From 5b38288fcd08a8fc5c1a1503577e20d37cfb10f7 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 27 Jan 2023 23:48:06 +0800
Subject: [PATCH 001/280] Generate the tier 2 interpreter
---
Python/bytecodes.c | 10 +-
Python/generated_cases.c.h | 30 +-
Python/generated_cases_tier2.c.h | 3713 +++++++++++++++++++++++
Python/opcode_metadata_tier2.h | 883 ++++++
Tools/cases_generator/generate_cases.py | 94 +-
Tools/cases_generator/parser.py | 28 +-
6 files changed, 4727 insertions(+), 31 deletions(-)
create mode 100644 Python/generated_cases_tier2.c.h
create mode 100644 Python/opcode_metadata_tier2.h
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index e5769f61fc28d0..d8532fc0d0c3b4 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -284,10 +284,13 @@ dummy_func(
ERROR_IF(sum == NULL, error);
}
- inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
+ u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (unused / 1, left, right -- left, right)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ }
+
+ u_inst(BINARY_OP_ADD_INST_REST, (unused / 1, left, right -- sum)) {
STAT_INC(BINARY_OP, hit);
sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
@@ -295,6 +298,11 @@ dummy_func(
ERROR_IF(sum == NULL, error);
}
+ macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
+ BINARY_OP_ADD_INT_TYPE_CHECK();
+ BINARY_OP_ADD_INST_REST();
+ }
+
family(binary_subscr, INLINE_CACHE_ENTRIES_BINARY_SUBSCR) = {
BINARY_SUBSCR,
BINARY_SUBSCR_DICT,
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 287a1f1f042089..c3b455177e33e3 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -1,7 +1,23 @@
-// This file is generated by Tools/cases_generator/generate_cases.py
-// from Python/bytecodes.c
+// This file is generated by Tools\cases_generator\generate_cases.py
+// from Python\bytecodes.c
// Do not edit!
+ #define BINARY_OP_ADD_INT_TYPE_CHECK() \
+ do { \
+ assert(cframe.use_tracing == 0);\
+ DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);\
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);\
+ } while (0)
+
+ #define BINARY_OP_ADD_INST_REST() \
+ do { \
+ STAT_INC(BINARY_OP, hit);\
+ sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);\
+ _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);\
+ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);\
+ if (sum == NULL) goto pop_2_error;\
+ } while (0)
+
TARGET(NOP) {
DISPATCH();
}
@@ -390,14 +406,8 @@
PyObject *right = PEEK(1);
PyObject *left = PEEK(2);
PyObject *sum;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
- STAT_INC(BINARY_OP, hit);
- sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
- if (sum == NULL) goto pop_2_error;
+ BINARY_OP_ADD_INT_TYPE_CHECK();
+ BINARY_OP_ADD_INST_REST();
STACK_SHRINK(1);
POKE(1, sum);
JUMPBY(1);
diff --git a/Python/generated_cases_tier2.c.h b/Python/generated_cases_tier2.c.h
new file mode 100644
index 00000000000000..91f4265c139aad
--- /dev/null
+++ b/Python/generated_cases_tier2.c.h
@@ -0,0 +1,3713 @@
+// This file is generated by Tools\cases_generator\generate_cases.py
+// from Python\bytecodes.c
+// Do not edit!
+
+ TARGET(NOP) {
+ DISPATCH();
+ }
+
+ TARGET(RESUME) {
+ assert(tstate->cframe == &cframe);
+ assert(frame == cframe.current_frame);
+ if (_Py_atomic_load_relaxed_int32(eval_breaker) && oparg < 2) {
+ goto handle_eval_breaker;
+ }
+ DISPATCH();
+ }
+
+ TARGET(LOAD_CLOSURE) {
+ PyObject *value;
+ /* We keep LOAD_CLOSURE so that the bytecode stays more readable. */
+ value = GETLOCAL(oparg);
+ if (value == NULL) goto unbound_local_error;
+ Py_INCREF(value);
+ STACK_GROW(1);
+ POKE(1, value);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_FAST_CHECK) {
+ PyObject *value;
+ value = GETLOCAL(oparg);
+ if (value == NULL) goto unbound_local_error;
+ Py_INCREF(value);
+ STACK_GROW(1);
+ POKE(1, value);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_FAST) {
+ PyObject *value;
+ value = GETLOCAL(oparg);
+ assert(value != NULL);
+ Py_INCREF(value);
+ STACK_GROW(1);
+ POKE(1, value);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_CONST) {
+ PREDICTED(LOAD_CONST);
+ PyObject *value;
+ value = GETITEM(consts, oparg);
+ Py_INCREF(value);
+ STACK_GROW(1);
+ POKE(1, value);
+ DISPATCH();
+ }
+
+ TARGET(STORE_FAST) {
+ PyObject *value = PEEK(1);
+ SETLOCAL(oparg, value);
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_FAST__LOAD_FAST) {
+ PyObject *_tmp_1;
+ PyObject *_tmp_2;
+ {
+ PyObject *value;
+ value = GETLOCAL(oparg);
+ assert(value != NULL);
+ Py_INCREF(value);
+ _tmp_2 = value;
+ }
+ NEXTOPARG();
+ JUMPBY(1);
+ {
+ PyObject *value;
+ value = GETLOCAL(oparg);
+ assert(value != NULL);
+ Py_INCREF(value);
+ _tmp_1 = value;
+ }
+ STACK_GROW(2);
+ POKE(1, _tmp_1);
+ POKE(2, _tmp_2);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_FAST__LOAD_CONST) {
+ PyObject *_tmp_1;
+ PyObject *_tmp_2;
+ {
+ PyObject *value;
+ value = GETLOCAL(oparg);
+ assert(value != NULL);
+ Py_INCREF(value);
+ _tmp_2 = value;
+ }
+ NEXTOPARG();
+ JUMPBY(1);
+ {
+ PyObject *value;
+ value = GETITEM(consts, oparg);
+ Py_INCREF(value);
+ _tmp_1 = value;
+ }
+ STACK_GROW(2);
+ POKE(1, _tmp_1);
+ POKE(2, _tmp_2);
+ DISPATCH();
+ }
+
+ TARGET(STORE_FAST__LOAD_FAST) {
+ PyObject *_tmp_1 = PEEK(1);
+ {
+ PyObject *value = _tmp_1;
+ SETLOCAL(oparg, value);
+ }
+ NEXTOPARG();
+ JUMPBY(1);
+ {
+ PyObject *value;
+ value = GETLOCAL(oparg);
+ assert(value != NULL);
+ Py_INCREF(value);
+ _tmp_1 = value;
+ }
+ POKE(1, _tmp_1);
+ DISPATCH();
+ }
+
+ TARGET(STORE_FAST__STORE_FAST) {
+ PyObject *_tmp_1 = PEEK(1);
+ PyObject *_tmp_2 = PEEK(2);
+ {
+ PyObject *value = _tmp_1;
+ SETLOCAL(oparg, value);
+ }
+ NEXTOPARG();
+ JUMPBY(1);
+ {
+ PyObject *value = _tmp_2;
+ SETLOCAL(oparg, value);
+ }
+ STACK_SHRINK(2);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_CONST__LOAD_FAST) {
+ PyObject *_tmp_1;
+ PyObject *_tmp_2;
+ {
+ PyObject *value;
+ value = GETITEM(consts, oparg);
+ Py_INCREF(value);
+ _tmp_2 = value;
+ }
+ NEXTOPARG();
+ JUMPBY(1);
+ {
+ PyObject *value;
+ value = GETLOCAL(oparg);
+ assert(value != NULL);
+ Py_INCREF(value);
+ _tmp_1 = value;
+ }
+ STACK_GROW(2);
+ POKE(1, _tmp_1);
+ POKE(2, _tmp_2);
+ DISPATCH();
+ }
+
+ TARGET(POP_TOP) {
+ PyObject *value = PEEK(1);
+ Py_DECREF(value);
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(PUSH_NULL) {
+ PyObject *res;
+ res = NULL;
+ STACK_GROW(1);
+ POKE(1, res);
+ DISPATCH();
+ }
+
+ TARGET(END_FOR) {
+ PyObject *_tmp_1 = PEEK(1);
+ PyObject *_tmp_2 = PEEK(2);
+ {
+ PyObject *value = _tmp_1;
+ Py_DECREF(value);
+ }
+ {
+ PyObject *value = _tmp_2;
+ Py_DECREF(value);
+ }
+ STACK_SHRINK(2);
+ DISPATCH();
+ }
+
+ TARGET(UNARY_NEGATIVE) {
+ PyObject *value = PEEK(1);
+ PyObject *res;
+ res = PyNumber_Negative(value);
+ Py_DECREF(value);
+ if (res == NULL) goto pop_1_error;
+ POKE(1, res);
+ DISPATCH();
+ }
+
+ TARGET(UNARY_NOT) {
+ PyObject *value = PEEK(1);
+ PyObject *res;
+ int err = PyObject_IsTrue(value);
+ Py_DECREF(value);
+ if (err < 0) goto pop_1_error;
+ if (err == 0) {
+ res = Py_True;
+ }
+ else {
+ res = Py_False;
+ }
+ Py_INCREF(res);
+ POKE(1, res);
+ DISPATCH();
+ }
+
+ TARGET(UNARY_INVERT) {
+ PyObject *value = PEEK(1);
+ PyObject *res;
+ res = PyNumber_Invert(value);
+ Py_DECREF(value);
+ if (res == NULL) goto pop_1_error;
+ POKE(1, res);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_MULTIPLY_INT) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *prod;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
+ DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
+ STAT_INC(BINARY_OP, hit);
+ prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right);
+ _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ if (prod == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, prod);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_MULTIPLY_FLOAT) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *prod;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
+ DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
+ STAT_INC(BINARY_OP, hit);
+ double dprod = ((PyFloatObject *)left)->ob_fval *
+ ((PyFloatObject *)right)->ob_fval;
+ prod = PyFloat_FromDouble(dprod);
+ _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
+ if (prod == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, prod);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_SUBTRACT_INT) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *sub;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
+ DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
+ STAT_INC(BINARY_OP, hit);
+ sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right);
+ _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ if (sub == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, sub);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_SUBTRACT_FLOAT) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *sub;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
+ DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
+ STAT_INC(BINARY_OP, hit);
+ double dsub = ((PyFloatObject *)left)->ob_fval - ((PyFloatObject *)right)->ob_fval;
+ sub = PyFloat_FromDouble(dsub);
+ _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
+ if (sub == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, sub);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_ADD_UNICODE) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *res;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP);
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ STAT_INC(BINARY_OP, hit);
+ res = PyUnicode_Concat(left, right);
+ _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
+ if (res == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, res);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_INPLACE_ADD_UNICODE) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP);
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ _Py_CODEUNIT true_next = next_instr[INLINE_CACHE_ENTRIES_BINARY_OP];
+ assert(_Py_OPCODE(true_next) == STORE_FAST ||
+ _Py_OPCODE(true_next) == STORE_FAST__LOAD_FAST);
+ PyObject **target_local = &GETLOCAL(_Py_OPARG(true_next));
+ DEOPT_IF(*target_local != left, BINARY_OP);
+ STAT_INC(BINARY_OP, hit);
+ /* Handle `left = left + right` or `left += right` for str.
+ *
+ * When possible, extend `left` in place rather than
+ * allocating a new PyUnicodeObject. This attempts to avoid
+ * quadratic behavior when one neglects to use str.join().
+ *
+ * If `left` has only two references remaining (one from
+ * the stack, one in the locals), DECREFing `left` leaves
+ * only the locals reference, so PyUnicode_Append knows
+ * that the string is safe to mutate.
+ */
+ assert(Py_REFCNT(left) >= 2);
+ _Py_DECREF_NO_DEALLOC(left);
+ PyUnicode_Append(target_local, right);
+ _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
+ if (*target_local == NULL) goto pop_2_error;
+ // The STORE_FAST is already done.
+ JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP + 1);
+ STACK_SHRINK(2);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_ADD_FLOAT) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *sum;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ STAT_INC(BINARY_OP, hit);
+ double dsum = ((PyFloatObject *)left)->ob_fval +
+ ((PyFloatObject *)right)->ob_fval;
+ sum = PyFloat_FromDouble(dsum);
+ _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
+ if (sum == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, sum);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_ADD_INT_TYPE_CHECK) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_ADD_INST_REST) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *sum;
+ STAT_INC(BINARY_OP, hit);
+ sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
+ _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ if (sum == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, sum);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_SUBSCR) {
+ PREDICTED(BINARY_SUBSCR);
+ static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 4, "incorrect cache size");
+ PyObject *sub = PEEK(1);
+ PyObject *container = PEEK(2);
+ PyObject *res;
+ #if ENABLE_SPECIALIZATION
+ _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ next_instr--;
+ _Py_Specialize_BinarySubscr(container, sub, next_instr);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(BINARY_SUBSCR, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
+ res = PyObject_GetItem(container, sub);
+ Py_DECREF(container);
+ Py_DECREF(sub);
+ if (res == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, res);
+ JUMPBY(4);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_SLICE) {
+ PyObject *stop = PEEK(1);
+ PyObject *start = PEEK(2);
+ PyObject *container = PEEK(3);
+ PyObject *res;
+ PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop);
+ // Can't use ERROR_IF() here, because we haven't
+ // DECREF'ed container yet, and we still own slice.
+ if (slice == NULL) {
+ res = NULL;
+ }
+ else {
+ res = PyObject_GetItem(container, slice);
+ Py_DECREF(slice);
+ }
+ Py_DECREF(container);
+ if (res == NULL) goto pop_3_error;
+ STACK_SHRINK(2);
+ POKE(1, res);
+ DISPATCH();
+ }
+
+ TARGET(STORE_SLICE) {
+ PyObject *stop = PEEK(1);
+ PyObject *start = PEEK(2);
+ PyObject *container = PEEK(3);
+ PyObject *v = PEEK(4);
+ PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop);
+ int err;
+ if (slice == NULL) {
+ err = 1;
+ }
+ else {
+ err = PyObject_SetItem(container, slice, v);
+ Py_DECREF(slice);
+ }
+ Py_DECREF(v);
+ Py_DECREF(container);
+ if (err) goto pop_4_error;
+ STACK_SHRINK(4);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_SUBSCR_LIST_INT) {
+ PyObject *sub = PEEK(1);
+ PyObject *list = PEEK(2);
+ PyObject *res;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
+ DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
+
+ // Deopt unless 0 <= sub < PyList_Size(list)
+ DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
+ assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0);
+ Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
+ DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
+ STAT_INC(BINARY_SUBSCR, hit);
+ res = PyList_GET_ITEM(list, index);
+ assert(res != NULL);
+ Py_INCREF(res);
+ _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
+ Py_DECREF(list);
+ STACK_SHRINK(1);
+ POKE(1, res);
+ JUMPBY(4);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_SUBSCR_TUPLE_INT) {
+ PyObject *sub = PEEK(1);
+ PyObject *tuple = PEEK(2);
+ PyObject *res;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
+ DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR);
+
+ // Deopt unless 0 <= sub < PyTuple_Size(list)
+ DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
+ assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0);
+ Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
+ DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR);
+ STAT_INC(BINARY_SUBSCR, hit);
+ res = PyTuple_GET_ITEM(tuple, index);
+ assert(res != NULL);
+ Py_INCREF(res);
+ _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
+ Py_DECREF(tuple);
+ STACK_SHRINK(1);
+ POKE(1, res);
+ JUMPBY(4);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_SUBSCR_DICT) {
+ PyObject *sub = PEEK(1);
+ PyObject *dict = PEEK(2);
+ PyObject *res;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR);
+ STAT_INC(BINARY_SUBSCR, hit);
+ res = PyDict_GetItemWithError(dict, sub);
+ if (res == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_SetKeyError(sub);
+ }
+ Py_DECREF(dict);
+ Py_DECREF(sub);
+ if (true) goto pop_2_error;
+ }
+ Py_INCREF(res); // Do this before DECREF'ing dict, sub
+ Py_DECREF(dict);
+ Py_DECREF(sub);
+ STACK_SHRINK(1);
+ POKE(1, res);
+ JUMPBY(4);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_SUBSCR_GETITEM) {
+ PyObject *sub = PEEK(1);
+ PyObject *container = PEEK(2);
+ uint32_t type_version = read_u32(&next_instr[1].cache);
+ uint16_t func_version = read_u16(&next_instr[3].cache);
+ PyTypeObject *tp = Py_TYPE(container);
+ DEOPT_IF(tp->tp_version_tag != type_version, BINARY_SUBSCR);
+ assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE);
+ PyObject *cached = ((PyHeapTypeObject *)tp)->_spec_cache.getitem;
+ assert(PyFunction_Check(cached));
+ PyFunctionObject *getitem = (PyFunctionObject *)cached;
+ DEOPT_IF(getitem->func_version != func_version, BINARY_SUBSCR);
+ PyCodeObject *code = (PyCodeObject *)getitem->func_code;
+ assert(code->co_argcount == 2);
+ DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR);
+ STAT_INC(BINARY_SUBSCR, hit);
+ Py_INCREF(getitem);
+ _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2);
+ STACK_SHRINK(2);
+ new_frame->localsplus[0] = container;
+ new_frame->localsplus[1] = sub;
+ JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR);
+ DISPATCH_INLINED(new_frame);
+ }
+
+ TARGET(LIST_APPEND) {
+ PyObject *v = PEEK(1);
+ PyObject *list = PEEK(2 + (oparg-1));
+ if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error;
+ STACK_SHRINK(1);
+ PREDICT(JUMP_BACKWARD);
+ DISPATCH();
+ }
+
+ TARGET(SET_ADD) {
+ PyObject *v = PEEK(1);
+ PyObject *set = PEEK(2 + (oparg-1));
+ int err = PySet_Add(set, v);
+ Py_DECREF(v);
+ if (err) goto pop_1_error;
+ STACK_SHRINK(1);
+ PREDICT(JUMP_BACKWARD);
+ DISPATCH();
+ }
+
+ TARGET(STORE_SUBSCR) {
+ PREDICTED(STORE_SUBSCR);
+ PyObject *sub = PEEK(1);
+ PyObject *container = PEEK(2);
+ PyObject *v = PEEK(3);
+ uint16_t counter = read_u16(&next_instr[0].cache);
+ #if ENABLE_SPECIALIZATION
+ if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
+ assert(cframe.use_tracing == 0);
+ next_instr--;
+ _Py_Specialize_StoreSubscr(container, sub, next_instr);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(STORE_SUBSCR, deferred);
+ _PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr;
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #else
+ (void)counter; // Unused.
+ #endif /* ENABLE_SPECIALIZATION */
+ /* container[sub] = v */
+ int err = PyObject_SetItem(container, sub, v);
+ Py_DECREF(v);
+ Py_DECREF(container);
+ Py_DECREF(sub);
+ if (err) goto pop_3_error;
+ STACK_SHRINK(3);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(STORE_SUBSCR_LIST_INT) {
+ PyObject *sub = PEEK(1);
+ PyObject *list = PEEK(2);
+ PyObject *value = PEEK(3);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR);
+ DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
+
+ // Ensure nonnegative, zero-or-one-digit ints.
+ DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR);
+ Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
+ // Ensure index < len(list)
+ DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR);
+ STAT_INC(STORE_SUBSCR, hit);
+
+ PyObject *old_value = PyList_GET_ITEM(list, index);
+ PyList_SET_ITEM(list, index, value);
+ assert(old_value != NULL);
+ Py_DECREF(old_value);
+ _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
+ Py_DECREF(list);
+ STACK_SHRINK(3);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(STORE_SUBSCR_DICT) {
+ PyObject *sub = PEEK(1);
+ PyObject *dict = PEEK(2);
+ PyObject *value = PEEK(3);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR);
+ STAT_INC(STORE_SUBSCR, hit);
+ int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value);
+ Py_DECREF(dict);
+ if (err) goto pop_3_error;
+ STACK_SHRINK(3);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(DELETE_SUBSCR) {
+ PyObject *sub = PEEK(1);
+ PyObject *container = PEEK(2);
+ /* del container[sub] */
+ int err = PyObject_DelItem(container, sub);
+ Py_DECREF(container);
+ Py_DECREF(sub);
+ if (err) goto pop_2_error;
+ STACK_SHRINK(2);
+ DISPATCH();
+ }
+
+ TARGET(CALL_INTRINSIC_1) {
+ PyObject *value = PEEK(1);
+ PyObject *res;
+ assert(oparg <= MAX_INTRINSIC_1);
+ res = _PyIntrinsics_UnaryFunctions[oparg](tstate, value);
+ Py_DECREF(value);
+ if (res == NULL) goto pop_1_error;
+ POKE(1, res);
+ DISPATCH();
+ }
+
+ TARGET(RAISE_VARARGS) {
+ PyObject **args = &PEEK(oparg);
+ PyObject *cause = NULL, *exc = NULL;
+ switch (oparg) {
+ case 2:
+ cause = args[1];
+ /* fall through */
+ case 1:
+ exc = args[0];
+ /* fall through */
+ case 0:
+ if (do_raise(tstate, exc, cause)) { STACK_SHRINK(oparg); goto exception_unwind; }
+ break;
+ default:
+ _PyErr_SetString(tstate, PyExc_SystemError,
+ "bad RAISE_VARARGS oparg");
+ break;
+ }
+ if (true) { STACK_SHRINK(oparg); goto error; }
+ }
+
+ TARGET(INTERPRETER_EXIT) {
+ PyObject *retval = PEEK(1);
+ assert(frame == &entry_frame);
+ assert(_PyFrame_IsIncomplete(frame));
+ STACK_SHRINK(1); // Since we're not going to DISPATCH()
+ assert(EMPTY());
+ /* Restore previous cframe and return. */
+ tstate->cframe = cframe.previous;
+ tstate->cframe->use_tracing = cframe.use_tracing;
+ assert(tstate->cframe->current_frame == frame->previous);
+ assert(!_PyErr_Occurred(tstate));
+ _Py_LeaveRecursiveCallTstate(tstate);
+ return retval;
+ }
+
+ TARGET(RETURN_VALUE) {
+ PyObject *retval = PEEK(1);
+ STACK_SHRINK(1);
+ assert(EMPTY());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ TRACE_FUNCTION_EXIT();
+ DTRACE_FUNCTION_EXIT();
+ _Py_LeaveRecursiveCallPy(tstate);
+ assert(frame != &entry_frame);
+ // GH-99729: We need to unlink the frame *before* clearing it:
+ _PyInterpreterFrame *dying = frame;
+ frame = cframe.current_frame = dying->previous;
+ _PyEvalFrameClearAndPop(tstate, dying);
+ _PyFrame_StackPush(frame, retval);
+ goto resume_frame;
+ }
+
+ TARGET(GET_AITER) {
+ PyObject *obj = PEEK(1);
+ PyObject *iter;
+ unaryfunc getter = NULL;
+ PyTypeObject *type = Py_TYPE(obj);
+
+ if (type->tp_as_async != NULL) {
+ getter = type->tp_as_async->am_aiter;
+ }
+
+ if (getter == NULL) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'async for' requires an object with "
+ "__aiter__ method, got %.100s",
+ type->tp_name);
+ Py_DECREF(obj);
+ if (true) goto pop_1_error;
+ }
+
+ iter = (*getter)(obj);
+ Py_DECREF(obj);
+ if (iter == NULL) goto pop_1_error;
+
+ if (Py_TYPE(iter)->tp_as_async == NULL ||
+ Py_TYPE(iter)->tp_as_async->am_anext == NULL) {
+
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'async for' received an object from __aiter__ "
+ "that does not implement __anext__: %.100s",
+ Py_TYPE(iter)->tp_name);
+ Py_DECREF(iter);
+ if (true) goto pop_1_error;
+ }
+ POKE(1, iter);
+ DISPATCH();
+ }
+
+ TARGET(GET_ANEXT) {
+ PyObject *aiter = PEEK(1);
+ PyObject *awaitable;
+ unaryfunc getter = NULL;
+ PyObject *next_iter = NULL;
+ PyTypeObject *type = Py_TYPE(aiter);
+
+ if (PyAsyncGen_CheckExact(aiter)) {
+ awaitable = type->tp_as_async->am_anext(aiter);
+ if (awaitable == NULL) {
+ goto error;
+ }
+ } else {
+ if (type->tp_as_async != NULL){
+ getter = type->tp_as_async->am_anext;
+ }
+
+ if (getter != NULL) {
+ next_iter = (*getter)(aiter);
+ if (next_iter == NULL) {
+ goto error;
+ }
+ }
+ else {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'async for' requires an iterator with "
+ "__anext__ method, got %.100s",
+ type->tp_name);
+ goto error;
+ }
+
+ awaitable = _PyCoro_GetAwaitableIter(next_iter);
+ if (awaitable == NULL) {
+ _PyErr_FormatFromCause(
+ PyExc_TypeError,
+ "'async for' received an invalid object "
+ "from __anext__: %.100s",
+ Py_TYPE(next_iter)->tp_name);
+
+ Py_DECREF(next_iter);
+ goto error;
+ } else {
+ Py_DECREF(next_iter);
+ }
+ }
+
+ STACK_GROW(1);
+ POKE(1, awaitable);
+ PREDICT(LOAD_CONST);
+ DISPATCH();
+ }
+
+ TARGET(GET_AWAITABLE) {
+ PREDICTED(GET_AWAITABLE);
+ PyObject *iterable = PEEK(1);
+ PyObject *iter;
+ iter = _PyCoro_GetAwaitableIter(iterable);
+
+ if (iter == NULL) {
+ format_awaitable_error(tstate, Py_TYPE(iterable), oparg);
+ }
+
+ Py_DECREF(iterable);
+
+ if (iter != NULL && PyCoro_CheckExact(iter)) {
+ PyObject *yf = _PyGen_yf((PyGenObject*)iter);
+ if (yf != NULL) {
+ /* `iter` is a coroutine object that is being
+ awaited, `yf` is a pointer to the current awaitable
+ being awaited on. */
+ Py_DECREF(yf);
+ Py_CLEAR(iter);
+ _PyErr_SetString(tstate, PyExc_RuntimeError,
+ "coroutine is being awaited already");
+ /* The code below jumps to `error` if `iter` is NULL. */
+ }
+ }
+
+ if (iter == NULL) goto pop_1_error;
+
+ POKE(1, iter);
+ PREDICT(LOAD_CONST);
+ DISPATCH();
+ }
+
+ TARGET(SEND) {
+ assert(frame != &entry_frame);
+ assert(STACK_LEVEL() >= 2);
+ PyObject *v = POP();
+ PyObject *receiver = TOP();
+ PySendResult gen_status;
+ PyObject *retval;
+ if (tstate->c_tracefunc == NULL) {
+ gen_status = PyIter_Send(receiver, v, &retval);
+ } else {
+ if (Py_IsNone(v) && PyIter_Check(receiver)) {
+ retval = Py_TYPE(receiver)->tp_iternext(receiver);
+ }
+ else {
+ retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v);
+ }
+ if (retval == NULL) {
+ if (tstate->c_tracefunc != NULL
+ && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
+ call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
+ if (_PyGen_FetchStopIterationValue(&retval) == 0) {
+ gen_status = PYGEN_RETURN;
+ }
+ else {
+ gen_status = PYGEN_ERROR;
+ }
+ }
+ else {
+ gen_status = PYGEN_NEXT;
+ }
+ }
+ Py_DECREF(v);
+ if (gen_status == PYGEN_ERROR) {
+ assert(retval == NULL);
+ goto error;
+ }
+ if (gen_status == PYGEN_RETURN) {
+ assert(retval != NULL);
+ Py_DECREF(receiver);
+ SET_TOP(retval);
+ JUMPBY(oparg);
+ }
+ else {
+ assert(gen_status == PYGEN_NEXT);
+ assert(retval != NULL);
+ PUSH(retval);
+ }
+ DISPATCH();
+ }
+
+ TARGET(YIELD_VALUE) {
+ PyObject *retval = PEEK(1);
+ // NOTE: It's important that YIELD_VALUE never raises an exception!
+ // The compiler treats any exception raised here as a failed close()
+ // or throw() call.
+ assert(frame != &entry_frame);
+ PyGenObject *gen = _PyFrame_GetGenerator(frame);
+ gen->gi_frame_state = FRAME_SUSPENDED;
+ _PyFrame_SetStackPointer(frame, stack_pointer - 1);
+ TRACE_FUNCTION_EXIT();
+ DTRACE_FUNCTION_EXIT();
+ tstate->exc_info = gen->gi_exc_state.previous_item;
+ gen->gi_exc_state.previous_item = NULL;
+ _Py_LeaveRecursiveCallPy(tstate);
+ _PyInterpreterFrame *gen_frame = frame;
+ frame = cframe.current_frame = frame->previous;
+ gen_frame->previous = NULL;
+ frame->prev_instr -= frame->yield_offset;
+ _PyFrame_StackPush(frame, retval);
+ goto resume_frame;
+ }
+
+ TARGET(POP_EXCEPT) {
+ PyObject *exc_value = PEEK(1);
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ Py_XSETREF(exc_info->exc_value, exc_value);
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(RERAISE) {
+ if (oparg) {
+ PyObject *lasti = PEEK(oparg + 1);
+ if (PyLong_Check(lasti)) {
+ frame->prev_instr = _PyCode_CODE(frame->f_code) + PyLong_AsLong(lasti);
+ assert(!_PyErr_Occurred(tstate));
+ }
+ else {
+ assert(PyLong_Check(lasti));
+ _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int");
+ goto error;
+ }
+ }
+ PyObject *val = POP();
+ assert(val && PyExceptionInstance_Check(val));
+ PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val));
+ PyObject *tb = PyException_GetTraceback(val);
+ _PyErr_Restore(tstate, exc, val, tb);
+ goto exception_unwind;
+ }
+
+ TARGET(PREP_RERAISE_STAR) {
+ PyObject *excs = PEEK(1);
+ PyObject *orig = PEEK(2);
+ PyObject *val;
+ assert(PyList_Check(excs));
+
+ val = _PyExc_PrepReraiseStar(orig, excs);
+ Py_DECREF(orig);
+ Py_DECREF(excs);
+
+ if (val == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, val);
+ DISPATCH();
+ }
+
+ TARGET(END_ASYNC_FOR) {
+ PyObject *val = POP();
+ assert(val && PyExceptionInstance_Check(val));
+ if (PyErr_GivenExceptionMatches(val, PyExc_StopAsyncIteration)) {
+ Py_DECREF(val);
+ Py_DECREF(POP());
+ }
+ else {
+ PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val));
+ PyObject *tb = PyException_GetTraceback(val);
+ _PyErr_Restore(tstate, exc, val, tb);
+ goto exception_unwind;
+ }
+ DISPATCH();
+ }
+
+ TARGET(CLEANUP_THROW) {
+ assert(throwflag);
+ PyObject *exc_value = TOP();
+ assert(exc_value && PyExceptionInstance_Check(exc_value));
+ if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) {
+ PyObject *value = ((PyStopIterationObject *)exc_value)->value;
+ Py_INCREF(value);
+ Py_DECREF(POP()); // The StopIteration.
+ Py_DECREF(POP()); // The last sent value.
+ Py_DECREF(POP()); // The delegated sub-iterator.
+ PUSH(value);
+ }
+ else {
+ PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value));
+ PyObject *exc_traceback = PyException_GetTraceback(exc_value);
+ _PyErr_Restore(tstate, exc_type, Py_NewRef(exc_value), exc_traceback);
+ goto exception_unwind;
+ }
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ASSERTION_ERROR) {
+ PyObject *value;
+ value = Py_NewRef(PyExc_AssertionError);
+ STACK_GROW(1);
+ POKE(1, value);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_BUILD_CLASS) {
+ PyObject *bc;
+ if (PyDict_CheckExact(BUILTINS())) {
+ bc = _PyDict_GetItemWithError(BUILTINS(),
+ &_Py_ID(__build_class__));
+ if (bc == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_SetString(tstate, PyExc_NameError,
+ "__build_class__ not found");
+ }
+ if (true) goto error;
+ }
+ Py_INCREF(bc);
+ }
+ else {
+ bc = PyObject_GetItem(BUILTINS(), &_Py_ID(__build_class__));
+ if (bc == NULL) {
+ if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError))
+ _PyErr_SetString(tstate, PyExc_NameError,
+ "__build_class__ not found");
+ if (true) goto error;
+ }
+ }
+ STACK_GROW(1);
+ POKE(1, bc);
+ DISPATCH();
+ }
+
+ TARGET(STORE_NAME) {
+ PyObject *v = PEEK(1);
+ PyObject *name = GETITEM(names, oparg);
+ PyObject *ns = LOCALS();
+ int err;
+ if (ns == NULL) {
+ _PyErr_Format(tstate, PyExc_SystemError,
+ "no locals found when storing %R", name);
+ Py_DECREF(v);
+ if (true) goto pop_1_error;
+ }
+ if (PyDict_CheckExact(ns))
+ err = PyDict_SetItem(ns, name, v);
+ else
+ err = PyObject_SetItem(ns, name, v);
+ Py_DECREF(v);
+ if (err) goto pop_1_error;
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(DELETE_NAME) {
+ PyObject *name = GETITEM(names, oparg);
+ PyObject *ns = LOCALS();
+ int err;
+ if (ns == NULL) {
+ _PyErr_Format(tstate, PyExc_SystemError,
+ "no locals when deleting %R", name);
+ goto error;
+ }
+ err = PyObject_DelItem(ns, name);
+ // Can't use ERROR_IF here.
+ if (err != 0) {
+ format_exc_check_arg(tstate, PyExc_NameError,
+ NAME_ERROR_MSG,
+ name);
+ goto error;
+ }
+ DISPATCH();
+ }
+
+ TARGET(UNPACK_SEQUENCE) {
+ PREDICTED(UNPACK_SEQUENCE);
+ #if ENABLE_SPECIALIZATION
+ _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ PyObject *seq = TOP();
+ next_instr--;
+ _Py_Specialize_UnpackSequence(seq, next_instr, oparg);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(UNPACK_SEQUENCE, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
+ PyObject *seq = POP();
+ PyObject **top = stack_pointer + oparg;
+ if (!unpack_iterable(tstate, seq, oparg, -1, top)) {
+ Py_DECREF(seq);
+ goto error;
+ }
+ STACK_GROW(oparg);
+ Py_DECREF(seq);
+ JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
+ DISPATCH();
+ }
+
+ TARGET(UNPACK_SEQUENCE_TWO_TUPLE) {
+ PyObject *seq = TOP();
+ DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
+ DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE);
+ STAT_INC(UNPACK_SEQUENCE, hit);
+ SET_TOP(Py_NewRef(PyTuple_GET_ITEM(seq, 1)));
+ PUSH(Py_NewRef(PyTuple_GET_ITEM(seq, 0)));
+ Py_DECREF(seq);
+ JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
+ DISPATCH();
+ }
+
+ TARGET(UNPACK_SEQUENCE_TUPLE) {
+ PyObject *seq = TOP();
+ DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
+ DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE);
+ STAT_INC(UNPACK_SEQUENCE, hit);
+ STACK_SHRINK(1);
+ PyObject **items = _PyTuple_ITEMS(seq);
+ while (oparg--) {
+ PUSH(Py_NewRef(items[oparg]));
+ }
+ Py_DECREF(seq);
+ JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
+ DISPATCH();
+ }
+
+ TARGET(UNPACK_SEQUENCE_LIST) {
+ PyObject *seq = TOP();
+ DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE);
+ DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE);
+ STAT_INC(UNPACK_SEQUENCE, hit);
+ STACK_SHRINK(1);
+ PyObject **items = _PyList_ITEMS(seq);
+ while (oparg--) {
+ PUSH(Py_NewRef(items[oparg]));
+ }
+ Py_DECREF(seq);
+ JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
+ DISPATCH();
+ }
+
+ TARGET(UNPACK_EX) {
+ int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
+ PyObject *seq = POP();
+ PyObject **top = stack_pointer + totalargs;
+ if (!unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top)) {
+ Py_DECREF(seq);
+ goto error;
+ }
+ STACK_GROW(totalargs);
+ Py_DECREF(seq);
+ DISPATCH();
+ }
+
+ TARGET(STORE_ATTR) {
+ PREDICTED(STORE_ATTR);
+ PyObject *owner = PEEK(1);
+ PyObject *v = PEEK(2);
+ uint16_t counter = read_u16(&next_instr[0].cache);
+ #if ENABLE_SPECIALIZATION
+ if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
+ assert(cframe.use_tracing == 0);
+ PyObject *name = GETITEM(names, oparg);
+ next_instr--;
+ _Py_Specialize_StoreAttr(owner, next_instr, name);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(STORE_ATTR, deferred);
+ _PyAttrCache *cache = (_PyAttrCache *)next_instr;
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #else
+ (void)counter; // Unused.
+ #endif /* ENABLE_SPECIALIZATION */
+ PyObject *name = GETITEM(names, oparg);
+ int err = PyObject_SetAttr(owner, name, v);
+ Py_DECREF(v);
+ Py_DECREF(owner);
+ if (err) goto pop_2_error;
+ STACK_SHRINK(2);
+ JUMPBY(4);
+ DISPATCH();
+ }
+
+ TARGET(DELETE_ATTR) {
+ PyObject *owner = PEEK(1);
+ PyObject *name = GETITEM(names, oparg);
+ int err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
+ Py_DECREF(owner);
+ if (err) goto pop_1_error;
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(STORE_GLOBAL) {
+ PyObject *v = PEEK(1);
+ PyObject *name = GETITEM(names, oparg);
+ int err = PyDict_SetItem(GLOBALS(), name, v);
+ Py_DECREF(v);
+ if (err) goto pop_1_error;
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(DELETE_GLOBAL) {
+ PyObject *name = GETITEM(names, oparg);
+ int err;
+ err = PyDict_DelItem(GLOBALS(), name);
+ // Can't use ERROR_IF here.
+ if (err != 0) {
+ if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
+ format_exc_check_arg(tstate, PyExc_NameError,
+ NAME_ERROR_MSG, name);
+ }
+ goto error;
+ }
+ DISPATCH();
+ }
+
+ TARGET(LOAD_NAME) {
+ PyObject *v;
+ PyObject *name = GETITEM(names, oparg);
+ PyObject *locals = LOCALS();
+ if (locals == NULL) {
+ _PyErr_Format(tstate, PyExc_SystemError,
+ "no locals when loading %R", name);
+ goto error;
+ }
+ if (PyDict_CheckExact(locals)) {
+ v = PyDict_GetItemWithError(locals, name);
+ if (v != NULL) {
+ Py_INCREF(v);
+ }
+ else if (_PyErr_Occurred(tstate)) {
+ goto error;
+ }
+ }
+ else {
+ v = PyObject_GetItem(locals, name);
+ if (v == NULL) {
+ if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError))
+ goto error;
+ _PyErr_Clear(tstate);
+ }
+ }
+ if (v == NULL) {
+ v = PyDict_GetItemWithError(GLOBALS(), name);
+ if (v != NULL) {
+ Py_INCREF(v);
+ }
+ else if (_PyErr_Occurred(tstate)) {
+ goto error;
+ }
+ else {
+ if (PyDict_CheckExact(BUILTINS())) {
+ v = PyDict_GetItemWithError(BUILTINS(), name);
+ if (v == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ format_exc_check_arg(
+ tstate, PyExc_NameError,
+ NAME_ERROR_MSG, name);
+ }
+ goto error;
+ }
+ Py_INCREF(v);
+ }
+ else {
+ v = PyObject_GetItem(BUILTINS(), name);
+ if (v == NULL) {
+ if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
+ format_exc_check_arg(
+ tstate, PyExc_NameError,
+ NAME_ERROR_MSG, name);
+ }
+ goto error;
+ }
+ }
+ }
+ }
+ STACK_GROW(1);
+ POKE(1, v);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_GLOBAL) {
+ PREDICTED(LOAD_GLOBAL);
+ #if ENABLE_SPECIALIZATION
+ _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ PyObject *name = GETITEM(names, oparg>>1);
+ next_instr--;
+ _Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(LOAD_GLOBAL, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
+ int push_null = oparg & 1;
+ PEEK(0) = NULL;
+ PyObject *name = GETITEM(names, oparg>>1);
+ PyObject *v;
+ if (PyDict_CheckExact(GLOBALS())
+ && PyDict_CheckExact(BUILTINS()))
+ {
+ v = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(),
+ (PyDictObject *)BUILTINS(),
+ name);
+ if (v == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ /* _PyDict_LoadGlobal() returns NULL without raising
+ * an exception if the key doesn't exist */
+ format_exc_check_arg(tstate, PyExc_NameError,
+ NAME_ERROR_MSG, name);
+ }
+ goto error;
+ }
+ Py_INCREF(v);
+ }
+ else {
+ /* Slow-path if globals or builtins is not a dict */
+
+ /* namespace 1: globals */
+ v = PyObject_GetItem(GLOBALS(), name);
+ if (v == NULL) {
+ if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
+ goto error;
+ }
+ _PyErr_Clear(tstate);
+
+ /* namespace 2: builtins */
+ v = PyObject_GetItem(BUILTINS(), name);
+ if (v == NULL) {
+ if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
+ format_exc_check_arg(
+ tstate, PyExc_NameError,
+ NAME_ERROR_MSG, name);
+ }
+ goto error;
+ }
+ }
+ }
+ /* Skip over inline cache */
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL);
+ STACK_GROW(push_null);
+ PUSH(v);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_GLOBAL_MODULE) {
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL);
+ PyDictObject *dict = (PyDictObject *)GLOBALS();
+ _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr;
+ uint32_t version = read_u32(cache->module_keys_version);
+ DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL);
+ assert(DK_IS_UNICODE(dict->ma_keys));
+ PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys);
+ PyObject *res = entries[cache->index].me_value;
+ DEOPT_IF(res == NULL, LOAD_GLOBAL);
+ int push_null = oparg & 1;
+ PEEK(0) = NULL;
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL);
+ STAT_INC(LOAD_GLOBAL, hit);
+ STACK_GROW(push_null+1);
+ SET_TOP(Py_NewRef(res));
+ DISPATCH();
+ }
+
+ TARGET(LOAD_GLOBAL_BUILTIN) {
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL);
+ DEOPT_IF(!PyDict_CheckExact(BUILTINS()), LOAD_GLOBAL);
+ PyDictObject *mdict = (PyDictObject *)GLOBALS();
+ PyDictObject *bdict = (PyDictObject *)BUILTINS();
+ _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr;
+ uint32_t mod_version = read_u32(cache->module_keys_version);
+ uint16_t bltn_version = cache->builtin_keys_version;
+ DEOPT_IF(mdict->ma_keys->dk_version != mod_version, LOAD_GLOBAL);
+ DEOPT_IF(bdict->ma_keys->dk_version != bltn_version, LOAD_GLOBAL);
+ assert(DK_IS_UNICODE(bdict->ma_keys));
+ PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys);
+ PyObject *res = entries[cache->index].me_value;
+ DEOPT_IF(res == NULL, LOAD_GLOBAL);
+ int push_null = oparg & 1;
+ PEEK(0) = NULL;
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL);
+ STAT_INC(LOAD_GLOBAL, hit);
+ STACK_GROW(push_null+1);
+ SET_TOP(Py_NewRef(res));
+ DISPATCH();
+ }
+
+ TARGET(DELETE_FAST) {
+ PyObject *v = GETLOCAL(oparg);
+ if (v == NULL) goto unbound_local_error;
+ SETLOCAL(oparg, NULL);
+ DISPATCH();
+ }
+
+ TARGET(MAKE_CELL) {
+ // "initial" is probably NULL but not if it's an arg (or set
+ // via PyFrame_LocalsToFast() before MAKE_CELL has run).
+ PyObject *initial = GETLOCAL(oparg);
+ PyObject *cell = PyCell_New(initial);
+ if (cell == NULL) {
+ goto resume_with_error;
+ }
+ SETLOCAL(oparg, cell);
+ DISPATCH();
+ }
+
+ TARGET(DELETE_DEREF) {
+ PyObject *cell = GETLOCAL(oparg);
+ PyObject *oldobj = PyCell_GET(cell);
+ // Can't use ERROR_IF here.
+ // Fortunately we don't need its superpower.
+ if (oldobj == NULL) {
+ format_exc_unbound(tstate, frame->f_code, oparg);
+ goto error;
+ }
+ PyCell_SET(cell, NULL);
+ Py_DECREF(oldobj);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_CLASSDEREF) {
+ PyObject *value;
+ PyObject *name, *locals = LOCALS();
+ assert(locals);
+ assert(oparg >= 0 && oparg < frame->f_code->co_nlocalsplus);
+ name = PyTuple_GET_ITEM(frame->f_code->co_localsplusnames, oparg);
+ if (PyDict_CheckExact(locals)) {
+ value = PyDict_GetItemWithError(locals, name);
+ if (value != NULL) {
+ Py_INCREF(value);
+ }
+ else if (_PyErr_Occurred(tstate)) {
+ goto error;
+ }
+ }
+ else {
+ value = PyObject_GetItem(locals, name);
+ if (value == NULL) {
+ if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
+ goto error;
+ }
+ _PyErr_Clear(tstate);
+ }
+ }
+ if (!value) {
+ PyObject *cell = GETLOCAL(oparg);
+ value = PyCell_GET(cell);
+ if (value == NULL) {
+ format_exc_unbound(tstate, frame->f_code, oparg);
+ goto error;
+ }
+ Py_INCREF(value);
+ }
+ STACK_GROW(1);
+ POKE(1, value);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_DEREF) {
+ PyObject *value;
+ PyObject *cell = GETLOCAL(oparg);
+ value = PyCell_GET(cell);
+ if (value == NULL) {
+ format_exc_unbound(tstate, frame->f_code, oparg);
+ if (true) goto error;
+ }
+ Py_INCREF(value);
+ STACK_GROW(1);
+ POKE(1, value);
+ DISPATCH();
+ }
+
+ TARGET(STORE_DEREF) {
+ PyObject *v = PEEK(1);
+ PyObject *cell = GETLOCAL(oparg);
+ PyObject *oldobj = PyCell_GET(cell);
+ PyCell_SET(cell, v);
+ Py_XDECREF(oldobj);
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(COPY_FREE_VARS) {
+ /* Copy closure variables to free variables */
+ PyCodeObject *co = frame->f_code;
+ assert(PyFunction_Check(frame->f_funcobj));
+ PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure;
+ assert(oparg == co->co_nfreevars);
+ int offset = co->co_nlocalsplus - oparg;
+ for (int i = 0; i < oparg; ++i) {
+ PyObject *o = PyTuple_GET_ITEM(closure, i);
+ frame->localsplus[offset + i] = Py_NewRef(o);
+ }
+ DISPATCH();
+ }
+
+ TARGET(BUILD_STRING) {
+ PyObject **pieces = &PEEK(oparg);
+ PyObject *str;
+ str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg);
+ for (int i = 0; i < oparg; i++) {
+ Py_DECREF(pieces[i]);
+ }
+ if (str == NULL) { STACK_SHRINK(oparg); goto error; }
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ POKE(1, str);
+ DISPATCH();
+ }
+
+ TARGET(BUILD_TUPLE) {
+ PyObject **values = &PEEK(oparg);
+ PyObject *tup;
+ tup = _PyTuple_FromArraySteal(values, oparg);
+ if (tup == NULL) { STACK_SHRINK(oparg); goto error; }
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ POKE(1, tup);
+ DISPATCH();
+ }
+
+ TARGET(BUILD_LIST) {
+ PyObject **values = &PEEK(oparg);
+ PyObject *list;
+ list = _PyList_FromArraySteal(values, oparg);
+ if (list == NULL) { STACK_SHRINK(oparg); goto error; }
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ POKE(1, list);
+ DISPATCH();
+ }
+
+ TARGET(LIST_EXTEND) {
+ PyObject *iterable = PEEK(1);
+ PyObject *list = PEEK(2 + (oparg-1));
+ PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable);
+ if (none_val == NULL) {
+ if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) &&
+ (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable)))
+ {
+ _PyErr_Clear(tstate);
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "Value after * must be an iterable, not %.200s",
+ Py_TYPE(iterable)->tp_name);
+ }
+ Py_DECREF(iterable);
+ if (true) goto pop_1_error;
+ }
+ Py_DECREF(none_val);
+ Py_DECREF(iterable);
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(SET_UPDATE) {
+ PyObject *iterable = PEEK(1);
+ PyObject *set = PEEK(2 + (oparg-1));
+ int err = _PySet_Update(set, iterable);
+ Py_DECREF(iterable);
+ if (err < 0) goto pop_1_error;
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(BUILD_SET) {
+ PyObject **values = &PEEK(oparg);
+ PyObject *set;
+ set = PySet_New(NULL);
+ int err = 0;
+ for (int i = 0; i < oparg; i++) {
+ PyObject *item = values[i];
+ if (err == 0)
+ err = PySet_Add(set, item);
+ Py_DECREF(item);
+ }
+ if (err != 0) {
+ Py_DECREF(set);
+ if (true) { STACK_SHRINK(oparg); goto error; }
+ }
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ POKE(1, set);
+ DISPATCH();
+ }
+
+ TARGET(BUILD_MAP) {
+ PyObject **values = &PEEK(oparg*2);
+ PyObject *map;
+ map = _PyDict_FromItems(
+ values, 2,
+ values+1, 2,
+ oparg);
+ if (map == NULL)
+ goto error;
+
+ for (int i = 0; i < oparg; i++) {
+ Py_DECREF(values[i*2]);
+ Py_DECREF(values[i*2+1]);
+ }
+ if (map == NULL) { STACK_SHRINK(oparg*2); goto error; }
+ STACK_SHRINK(oparg*2);
+ STACK_GROW(1);
+ POKE(1, map);
+ DISPATCH();
+ }
+
+ TARGET(SETUP_ANNOTATIONS) {
+ int err;
+ PyObject *ann_dict;
+ if (LOCALS() == NULL) {
+ _PyErr_Format(tstate, PyExc_SystemError,
+ "no locals found when setting up annotations");
+ if (true) goto error;
+ }
+ /* check if __annotations__ in locals()... */
+ if (PyDict_CheckExact(LOCALS())) {
+ ann_dict = _PyDict_GetItemWithError(LOCALS(),
+ &_Py_ID(__annotations__));
+ if (ann_dict == NULL) {
+ if (_PyErr_Occurred(tstate)) goto error;
+ /* ...if not, create a new one */
+ ann_dict = PyDict_New();
+ if (ann_dict == NULL) goto error;
+ err = PyDict_SetItem(LOCALS(), &_Py_ID(__annotations__),
+ ann_dict);
+ Py_DECREF(ann_dict);
+ if (err) goto error;
+ }
+ }
+ else {
+ /* do the same if locals() is not a dict */
+ ann_dict = PyObject_GetItem(LOCALS(), &_Py_ID(__annotations__));
+ if (ann_dict == NULL) {
+ if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) goto error;
+ _PyErr_Clear(tstate);
+ ann_dict = PyDict_New();
+ if (ann_dict == NULL) goto error;
+ err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__),
+ ann_dict);
+ Py_DECREF(ann_dict);
+ if (err) goto error;
+ }
+ else {
+ Py_DECREF(ann_dict);
+ }
+ }
+ DISPATCH();
+ }
+
+ TARGET(BUILD_CONST_KEY_MAP) {
+ PyObject *keys = PEEK(1);
+ PyObject **values = &PEEK(1 + oparg);
+ PyObject *map;
+ if (!PyTuple_CheckExact(keys) ||
+ PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) {
+ _PyErr_SetString(tstate, PyExc_SystemError,
+ "bad BUILD_CONST_KEY_MAP keys argument");
+ goto error; // Pop the keys and values.
+ }
+ map = _PyDict_FromItems(
+ &PyTuple_GET_ITEM(keys, 0), 1,
+ values, 1, oparg);
+ Py_DECREF(keys);
+ for (int i = 0; i < oparg; i++) {
+ Py_DECREF(values[i]);
+ }
+ if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; }
+ STACK_SHRINK(oparg);
+ POKE(1, map);
+ DISPATCH();
+ }
+
+ TARGET(DICT_UPDATE) {
+ PyObject *update = PEEK(1);
+ PyObject *dict = PEEK(oparg + 1); // update is still on the stack
+ if (PyDict_Update(dict, update) < 0) {
+ if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'%.200s' object is not a mapping",
+ Py_TYPE(update)->tp_name);
+ }
+ Py_DECREF(update);
+ if (true) goto pop_1_error;
+ }
+ Py_DECREF(update);
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(DICT_MERGE) {
+ PyObject *update = PEEK(1);
+ PyObject *dict = PEEK(oparg + 1); // update is still on the stack
+
+ if (_PyDict_MergeEx(dict, update, 2) < 0) {
+ format_kwargs_error(tstate, PEEK(3 + oparg), update);
+ Py_DECREF(update);
+ if (true) goto pop_1_error;
+ }
+ Py_DECREF(update);
+ STACK_SHRINK(1);
+ PREDICT(CALL_FUNCTION_EX);
+ DISPATCH();
+ }
+
+ TARGET(MAP_ADD) {
+ PyObject *value = PEEK(1);
+ PyObject *key = PEEK(2);
+ PyObject *dict = PEEK(oparg + 2); // key, value are still on the stack
+ assert(PyDict_CheckExact(dict));
+ /* dict[key] = value */
+ // Do not DECREF INPUTS because the function steals the references
+ if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error;
+ STACK_SHRINK(2);
+ PREDICT(JUMP_BACKWARD);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR) {
+ PREDICTED(LOAD_ATTR);
+ #if ENABLE_SPECIALIZATION
+ _PyAttrCache *cache = (_PyAttrCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ PyObject *owner = TOP();
+ PyObject *name = GETITEM(names, oparg>>1);
+ next_instr--;
+ _Py_Specialize_LoadAttr(owner, next_instr, name);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(LOAD_ATTR, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
+ PyObject *name = GETITEM(names, oparg >> 1);
+ PyObject *owner = TOP();
+ if (oparg & 1) {
+ /* Designed to work in tandem with CALL. */
+ PyObject* meth = NULL;
+
+ int meth_found = _PyObject_GetMethod(owner, name, &meth);
+
+ if (meth == NULL) {
+ /* Most likely attribute wasn't found. */
+ goto error;
+ }
+
+ if (meth_found) {
+ /* We can bypass temporary bound method object.
+ meth is unbound method and obj is self.
+
+ meth | self | arg1 | ... | argN
+ */
+ SET_TOP(meth);
+ PUSH(owner); // self
+ }
+ else {
+ /* meth is not an unbound method (but a regular attr, or
+ something was returned by a descriptor protocol). Set
+ the second element of the stack to NULL, to signal
+ CALL that it's not a method call.
+
+ NULL | meth | arg1 | ... | argN
+ */
+ SET_TOP(NULL);
+ Py_DECREF(owner);
+ PUSH(meth);
+ }
+ }
+ else {
+ PyObject *res = PyObject_GetAttr(owner, name);
+ if (res == NULL) {
+ goto error;
+ }
+ Py_DECREF(owner);
+ SET_TOP(res);
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR_INSTANCE_VALUE) {
+ assert(cframe.use_tracing == 0);
+ PyObject *owner = TOP();
+ PyObject *res;
+ PyTypeObject *tp = Py_TYPE(owner);
+ _PyAttrCache *cache = (_PyAttrCache *)next_instr;
+ uint32_t type_version = read_u32(cache->version);
+ assert(type_version != 0);
+ DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
+ assert(tp->tp_dictoffset < 0);
+ assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
+ PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ res = _PyDictOrValues_GetValues(dorv)->values[cache->index];
+ DEOPT_IF(res == NULL, LOAD_ATTR);
+ STAT_INC(LOAD_ATTR, hit);
+ Py_INCREF(res);
+ SET_TOP(NULL);
+ STACK_GROW((oparg & 1));
+ SET_TOP(res);
+ Py_DECREF(owner);
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR_MODULE) {
+ assert(cframe.use_tracing == 0);
+ PyObject *owner = TOP();
+ PyObject *res;
+ _PyAttrCache *cache = (_PyAttrCache *)next_instr;
+ DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR);
+ PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict;
+ assert(dict != NULL);
+ DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->version),
+ LOAD_ATTR);
+ assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
+ assert(cache->index < dict->ma_keys->dk_nentries);
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + cache->index;
+ res = ep->me_value;
+ DEOPT_IF(res == NULL, LOAD_ATTR);
+ STAT_INC(LOAD_ATTR, hit);
+ Py_INCREF(res);
+ SET_TOP(NULL);
+ STACK_GROW((oparg & 1));
+ SET_TOP(res);
+ Py_DECREF(owner);
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR_WITH_HINT) {
+ assert(cframe.use_tracing == 0);
+ PyObject *owner = TOP();
+ PyObject *res;
+ PyTypeObject *tp = Py_TYPE(owner);
+ _PyAttrCache *cache = (_PyAttrCache *)next_instr;
+ uint32_t type_version = read_u32(cache->version);
+ assert(type_version != 0);
+ DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
+ assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
+ PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
+ DEOPT_IF(dict == NULL, LOAD_ATTR);
+ assert(PyDict_CheckExact((PyObject *)dict));
+ PyObject *name = GETITEM(names, oparg>>1);
+ uint16_t hint = cache->index;
+ DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR);
+ if (DK_IS_UNICODE(dict->ma_keys)) {
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
+ DEOPT_IF(ep->me_key != name, LOAD_ATTR);
+ res = ep->me_value;
+ }
+ else {
+ PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint;
+ DEOPT_IF(ep->me_key != name, LOAD_ATTR);
+ res = ep->me_value;
+ }
+ DEOPT_IF(res == NULL, LOAD_ATTR);
+ STAT_INC(LOAD_ATTR, hit);
+ Py_INCREF(res);
+ SET_TOP(NULL);
+ STACK_GROW((oparg & 1));
+ SET_TOP(res);
+ Py_DECREF(owner);
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR_SLOT) {
+ assert(cframe.use_tracing == 0);
+ PyObject *owner = TOP();
+ PyObject *res;
+ PyTypeObject *tp = Py_TYPE(owner);
+ _PyAttrCache *cache = (_PyAttrCache *)next_instr;
+ uint32_t type_version = read_u32(cache->version);
+ assert(type_version != 0);
+ DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
+ char *addr = (char *)owner + cache->index;
+ res = *(PyObject **)addr;
+ DEOPT_IF(res == NULL, LOAD_ATTR);
+ STAT_INC(LOAD_ATTR, hit);
+ Py_INCREF(res);
+ SET_TOP(NULL);
+ STACK_GROW((oparg & 1));
+ SET_TOP(res);
+ Py_DECREF(owner);
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR_CLASS) {
+ assert(cframe.use_tracing == 0);
+ _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
+
+ PyObject *cls = TOP();
+ DEOPT_IF(!PyType_Check(cls), LOAD_ATTR);
+ uint32_t type_version = read_u32(cache->type_version);
+ DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != type_version,
+ LOAD_ATTR);
+ assert(type_version != 0);
+
+ STAT_INC(LOAD_ATTR, hit);
+ PyObject *res = read_obj(cache->descr);
+ assert(res != NULL);
+ Py_INCREF(res);
+ SET_TOP(NULL);
+ STACK_GROW((oparg & 1));
+ SET_TOP(res);
+ Py_DECREF(cls);
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR_PROPERTY) {
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
+ _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
+
+ PyObject *owner = TOP();
+ PyTypeObject *cls = Py_TYPE(owner);
+ uint32_t type_version = read_u32(cache->type_version);
+ DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
+ assert(type_version != 0);
+ PyObject *fget = read_obj(cache->descr);
+ assert(Py_IS_TYPE(fget, &PyFunction_Type));
+ PyFunctionObject *f = (PyFunctionObject *)fget;
+ uint32_t func_version = read_u32(cache->keys_version);
+ assert(func_version != 0);
+ DEOPT_IF(f->func_version != func_version, LOAD_ATTR);
+ PyCodeObject *code = (PyCodeObject *)f->func_code;
+ assert(code->co_argcount == 1);
+ DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR);
+ STAT_INC(LOAD_ATTR, hit);
+ Py_INCREF(fget);
+ _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
+ SET_TOP(NULL);
+ int shrink_stack = !(oparg & 1);
+ STACK_SHRINK(shrink_stack);
+ new_frame->localsplus[0] = owner;
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH_INLINED(new_frame);
+ }
+
+ TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) {
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
+ _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
+ PyObject *owner = TOP();
+ PyTypeObject *cls = Py_TYPE(owner);
+ uint32_t type_version = read_u32(cache->type_version);
+ DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
+ assert(type_version != 0);
+ PyObject *getattribute = read_obj(cache->descr);
+ assert(Py_IS_TYPE(getattribute, &PyFunction_Type));
+ PyFunctionObject *f = (PyFunctionObject *)getattribute;
+ uint32_t func_version = read_u32(cache->keys_version);
+ assert(func_version != 0);
+ DEOPT_IF(f->func_version != func_version, LOAD_ATTR);
+ PyCodeObject *code = (PyCodeObject *)f->func_code;
+ assert(code->co_argcount == 2);
+ DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR);
+ STAT_INC(LOAD_ATTR, hit);
+
+ PyObject *name = GETITEM(names, oparg >> 1);
+ Py_INCREF(f);
+ _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2);
+ SET_TOP(NULL);
+ int shrink_stack = !(oparg & 1);
+ STACK_SHRINK(shrink_stack);
+ new_frame->localsplus[0] = owner;
+ new_frame->localsplus[1] = Py_NewRef(name);
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH_INLINED(new_frame);
+ }
+
+ TARGET(STORE_ATTR_INSTANCE_VALUE) {
+ PyObject *owner = PEEK(1);
+ PyObject *value = PEEK(2);
+ uint32_t type_version = read_u32(&next_instr[1].cache);
+ uint16_t index = read_u16(&next_instr[3].cache);
+ assert(cframe.use_tracing == 0);
+ PyTypeObject *tp = Py_TYPE(owner);
+ assert(type_version != 0);
+ DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
+ assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
+ PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR);
+ STAT_INC(STORE_ATTR, hit);
+ PyDictValues *values = _PyDictOrValues_GetValues(dorv);
+ PyObject *old_value = values->values[index];
+ values->values[index] = value;
+ if (old_value == NULL) {
+ _PyDictValues_AddToInsertionOrder(values, index);
+ }
+ else {
+ Py_DECREF(old_value);
+ }
+ Py_DECREF(owner);
+ STACK_SHRINK(2);
+ JUMPBY(4);
+ DISPATCH();
+ }
+
+ TARGET(STORE_ATTR_WITH_HINT) {
+ PyObject *owner = PEEK(1);
+ PyObject *value = PEEK(2);
+ uint32_t type_version = read_u32(&next_instr[1].cache);
+ uint16_t hint = read_u16(&next_instr[3].cache);
+ assert(cframe.use_tracing == 0);
+ PyTypeObject *tp = Py_TYPE(owner);
+ assert(type_version != 0);
+ DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
+ assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
+ PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
+ DEOPT_IF(_PyDictOrValues_IsValues(dorv), STORE_ATTR);
+ PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
+ DEOPT_IF(dict == NULL, STORE_ATTR);
+ assert(PyDict_CheckExact((PyObject *)dict));
+ PyObject *name = GETITEM(names, oparg);
+ DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR);
+ PyObject *old_value;
+ uint64_t new_version;
+ if (DK_IS_UNICODE(dict->ma_keys)) {
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
+ DEOPT_IF(ep->me_key != name, STORE_ATTR);
+ old_value = ep->me_value;
+ DEOPT_IF(old_value == NULL, STORE_ATTR);
+ new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value);
+ ep->me_value = value;
+ }
+ else {
+ PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint;
+ DEOPT_IF(ep->me_key != name, STORE_ATTR);
+ old_value = ep->me_value;
+ DEOPT_IF(old_value == NULL, STORE_ATTR);
+ new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value);
+ ep->me_value = value;
+ }
+ Py_DECREF(old_value);
+ STAT_INC(STORE_ATTR, hit);
+ /* Ensure dict is GC tracked if it needs to be */
+ if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(value)) {
+ _PyObject_GC_TRACK(dict);
+ }
+ /* PEP 509 */
+ dict->ma_version_tag = new_version;
+ Py_DECREF(owner);
+ STACK_SHRINK(2);
+ JUMPBY(4);
+ DISPATCH();
+ }
+
+ TARGET(STORE_ATTR_SLOT) {
+ PyObject *owner = PEEK(1);
+ PyObject *value = PEEK(2);
+ uint32_t type_version = read_u32(&next_instr[1].cache);
+ uint16_t index = read_u16(&next_instr[3].cache);
+ assert(cframe.use_tracing == 0);
+ PyTypeObject *tp = Py_TYPE(owner);
+ assert(type_version != 0);
+ DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
+ char *addr = (char *)owner + index;
+ STAT_INC(STORE_ATTR, hit);
+ PyObject *old_value = *(PyObject **)addr;
+ *(PyObject **)addr = value;
+ Py_XDECREF(old_value);
+ Py_DECREF(owner);
+ STACK_SHRINK(2);
+ JUMPBY(4);
+ DISPATCH();
+ }
+
+ TARGET(COMPARE_OP) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *res;
+ STAT_INC(COMPARE_OP, deferred);
+ assert((oparg >> 4) <= Py_GE);
+ res = PyObject_RichCompare(left, right, oparg>>4);
+ Py_DECREF(left);
+ Py_DECREF(right);
+ if (res == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, res);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(COMPARE_AND_BRANCH) {
+ PREDICTED(COMPARE_AND_BRANCH);
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ #if ENABLE_SPECIALIZATION
+ _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ next_instr--;
+ _Py_Specialize_CompareAndBranch(left, right, next_instr, oparg);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(COMPARE_AND_BRANCH, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
+ assert((oparg >> 4) <= Py_GE);
+ PyObject *cond = PyObject_RichCompare(left, right, oparg>>4);
+ Py_DECREF(left);
+ Py_DECREF(right);
+ if (cond == NULL) goto pop_2_error;
+ assert(_Py_OPCODE(next_instr[1]) == POP_JUMP_IF_FALSE ||
+ _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE);
+ bool jump_on_true = _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE;
+ int offset = _Py_OPARG(next_instr[1]);
+ int err = PyObject_IsTrue(cond);
+ Py_DECREF(cond);
+ if (err < 0) {
+ goto error;
+ }
+ if (jump_on_true == (err != 0)) {
+ JUMPBY(offset);
+ }
+ STACK_SHRINK(2);
+ JUMPBY(2);
+ DISPATCH();
+ }
+
+ TARGET(COMPARE_AND_BRANCH_FLOAT) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_AND_BRANCH);
+ DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_AND_BRANCH);
+ STAT_INC(COMPARE_AND_BRANCH, hit);
+ double dleft = PyFloat_AS_DOUBLE(left);
+ double dright = PyFloat_AS_DOUBLE(right);
+ // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg
+ int sign_ish = COMPARISON_BIT(dleft, dright);
+ _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
+ if (sign_ish & oparg) {
+ int offset = _Py_OPARG(next_instr[1]);
+ JUMPBY(offset);
+ }
+ STACK_SHRINK(2);
+ JUMPBY(2);
+ DISPATCH();
+ }
+
+ TARGET(COMPARE_AND_BRANCH_INT) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH);
+ DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH);
+ DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_AND_BRANCH);
+ DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH);
+ STAT_INC(COMPARE_AND_BRANCH, hit);
+ assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
+ Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0];
+ Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0];
+ // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
+ int sign_ish = COMPARISON_BIT(ileft, iright);
+ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
+ if (sign_ish & oparg) {
+ int offset = _Py_OPARG(next_instr[1]);
+ JUMPBY(offset);
+ }
+ STACK_SHRINK(2);
+ JUMPBY(2);
+ DISPATCH();
+ }
+
+ TARGET(COMPARE_AND_BRANCH_STR) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_AND_BRANCH);
+ DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_AND_BRANCH);
+ STAT_INC(COMPARE_AND_BRANCH, hit);
+ int res = _PyUnicode_Equal(left, right);
+ assert((oparg >>4) == Py_EQ || (oparg >>4) == Py_NE);
+ _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc);
+ _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
+ assert(res == 0 || res == 1);
+ assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS);
+ assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS);
+ if ((res + COMPARISON_NOT_EQUALS) & oparg) {
+ int offset = _Py_OPARG(next_instr[1]);
+ JUMPBY(offset);
+ }
+ STACK_SHRINK(2);
+ JUMPBY(2);
+ DISPATCH();
+ }
+
+ TARGET(IS_OP) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *b;
+ int res = Py_Is(left, right) ^ oparg;
+ Py_DECREF(left);
+ Py_DECREF(right);
+ b = Py_NewRef(res ? Py_True : Py_False);
+ STACK_SHRINK(1);
+ POKE(1, b);
+ DISPATCH();
+ }
+
+ TARGET(CONTAINS_OP) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *b;
+ int res = PySequence_Contains(right, left);
+ Py_DECREF(left);
+ Py_DECREF(right);
+ if (res < 0) goto pop_2_error;
+ b = Py_NewRef((res^oparg) ? Py_True : Py_False);
+ STACK_SHRINK(1);
+ POKE(1, b);
+ DISPATCH();
+ }
+
+ TARGET(CHECK_EG_MATCH) {
+ PyObject *match_type = PEEK(1);
+ PyObject *exc_value = PEEK(2);
+ PyObject *rest;
+ PyObject *match;
+ if (check_except_star_type_valid(tstate, match_type) < 0) {
+ Py_DECREF(exc_value);
+ Py_DECREF(match_type);
+ if (true) goto pop_2_error;
+ }
+
+ match = NULL;
+ rest = NULL;
+ int res = exception_group_match(exc_value, match_type,
+ &match, &rest);
+ Py_DECREF(exc_value);
+ Py_DECREF(match_type);
+ if (res < 0) goto pop_2_error;
+
+ assert((match == NULL) == (rest == NULL));
+ if (match == NULL) goto pop_2_error;
+
+ if (!Py_IsNone(match)) {
+ PyErr_SetExcInfo(NULL, Py_NewRef(match), NULL);
+ }
+ POKE(1, match);
+ POKE(2, rest);
+ DISPATCH();
+ }
+
+ TARGET(CHECK_EXC_MATCH) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *b;
+ assert(PyExceptionInstance_Check(left));
+ if (check_except_type_valid(tstate, right) < 0) {
+ Py_DECREF(right);
+ if (true) goto pop_1_error;
+ }
+
+ int res = PyErr_GivenExceptionMatches(left, right);
+ Py_DECREF(right);
+ b = Py_NewRef(res ? Py_True : Py_False);
+ POKE(1, b);
+ DISPATCH();
+ }
+
+ TARGET(IMPORT_NAME) {
+ PyObject *fromlist = PEEK(1);
+ PyObject *level = PEEK(2);
+ PyObject *res;
+ PyObject *name = GETITEM(names, oparg);
+ res = import_name(tstate, frame, name, fromlist, level);
+ Py_DECREF(level);
+ Py_DECREF(fromlist);
+ if (res == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, res);
+ DISPATCH();
+ }
+
+ TARGET(IMPORT_FROM) {
+ PyObject *from = PEEK(1);
+ PyObject *res;
+ PyObject *name = GETITEM(names, oparg);
+ res = import_from(tstate, from, name);
+ if (res == NULL) goto error;
+ STACK_GROW(1);
+ POKE(1, res);
+ DISPATCH();
+ }
+
+ TARGET(JUMP_FORWARD) {
+ JUMPBY(oparg);
+ DISPATCH();
+ }
+
+ TARGET(JUMP_BACKWARD) {
+ PREDICTED(JUMP_BACKWARD);
+ assert(oparg < INSTR_OFFSET());
+ JUMPBY(-oparg);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(POP_JUMP_IF_FALSE) {
+ PREDICTED(POP_JUMP_IF_FALSE);
+ PyObject *cond = POP();
+ if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ }
+ else if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ JUMPBY(oparg);
+ }
+ else {
+ int err = PyObject_IsTrue(cond);
+ Py_DECREF(cond);
+ if (err > 0)
+ ;
+ else if (err == 0) {
+ JUMPBY(oparg);
+ }
+ else
+ goto error;
+ }
+ DISPATCH();
+ }
+
+ TARGET(POP_JUMP_IF_TRUE) {
+ PyObject *cond = POP();
+ if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ }
+ else if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ JUMPBY(oparg);
+ }
+ else {
+ int err = PyObject_IsTrue(cond);
+ Py_DECREF(cond);
+ if (err > 0) {
+ JUMPBY(oparg);
+ }
+ else if (err == 0)
+ ;
+ else
+ goto error;
+ }
+ DISPATCH();
+ }
+
+ TARGET(POP_JUMP_IF_NOT_NONE) {
+ PyObject *value = POP();
+ if (!Py_IsNone(value)) {
+ JUMPBY(oparg);
+ }
+ Py_DECREF(value);
+ DISPATCH();
+ }
+
+ TARGET(POP_JUMP_IF_NONE) {
+ PyObject *value = POP();
+ if (Py_IsNone(value)) {
+ _Py_DECREF_NO_DEALLOC(value);
+ JUMPBY(oparg);
+ }
+ else {
+ Py_DECREF(value);
+ }
+ DISPATCH();
+ }
+
+ TARGET(JUMP_IF_FALSE_OR_POP) {
+ PyObject *cond = TOP();
+ int err;
+ if (Py_IsTrue(cond)) {
+ STACK_SHRINK(1);
+ _Py_DECREF_NO_DEALLOC(cond);
+ }
+ else if (Py_IsFalse(cond)) {
+ JUMPBY(oparg);
+ }
+ else {
+ err = PyObject_IsTrue(cond);
+ if (err > 0) {
+ STACK_SHRINK(1);
+ Py_DECREF(cond);
+ }
+ else if (err == 0) {
+ JUMPBY(oparg);
+ }
+ else {
+ goto error;
+ }
+ }
+ DISPATCH();
+ }
+
+ TARGET(JUMP_IF_TRUE_OR_POP) {
+ PyObject *cond = TOP();
+ int err;
+ if (Py_IsFalse(cond)) {
+ STACK_SHRINK(1);
+ _Py_DECREF_NO_DEALLOC(cond);
+ }
+ else if (Py_IsTrue(cond)) {
+ JUMPBY(oparg);
+ }
+ else {
+ err = PyObject_IsTrue(cond);
+ if (err > 0) {
+ JUMPBY(oparg);
+ }
+ else if (err == 0) {
+ STACK_SHRINK(1);
+ Py_DECREF(cond);
+ }
+ else {
+ goto error;
+ }
+ }
+ DISPATCH();
+ }
+
+ TARGET(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).
+ */
+ JUMPBY(-oparg);
+ DISPATCH();
+ }
+
+ TARGET(GET_LEN) {
+ // PUSH(len(TOS))
+ Py_ssize_t len_i = PyObject_Length(TOP());
+ if (len_i < 0) {
+ goto error;
+ }
+ PyObject *len_o = PyLong_FromSsize_t(len_i);
+ if (len_o == NULL) {
+ goto error;
+ }
+ PUSH(len_o);
+ DISPATCH();
+ }
+
+ TARGET(MATCH_CLASS) {
+ PyObject *names = PEEK(1);
+ PyObject *type = PEEK(2);
+ PyObject *subject = PEEK(3);
+ PyObject *attrs;
+ // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or
+ // None on failure.
+ assert(PyTuple_CheckExact(names));
+ attrs = match_class(tstate, subject, type, oparg, names);
+ Py_DECREF(subject);
+ Py_DECREF(type);
+ Py_DECREF(names);
+ if (attrs) {
+ assert(PyTuple_CheckExact(attrs)); // Success!
+ }
+ else {
+ if (_PyErr_Occurred(tstate)) goto pop_3_error;
+ attrs = Py_NewRef(Py_None); // Failure!
+ }
+ STACK_SHRINK(2);
+ POKE(1, attrs);
+ DISPATCH();
+ }
+
+ TARGET(MATCH_MAPPING) {
+ PyObject *subject = PEEK(1);
+ PyObject *res;
+ int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING;
+ res = Py_NewRef(match ? Py_True : Py_False);
+ STACK_GROW(1);
+ POKE(1, res);
+ PREDICT(POP_JUMP_IF_FALSE);
+ DISPATCH();
+ }
+
+ TARGET(MATCH_SEQUENCE) {
+ PyObject *subject = PEEK(1);
+ PyObject *res;
+ int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE;
+ res = Py_NewRef(match ? Py_True : Py_False);
+ STACK_GROW(1);
+ POKE(1, res);
+ PREDICT(POP_JUMP_IF_FALSE);
+ DISPATCH();
+ }
+
+ TARGET(MATCH_KEYS) {
+ PyObject *keys = PEEK(1);
+ PyObject *subject = PEEK(2);
+ PyObject *values_or_none;
+ // On successful match, PUSH(values). Otherwise, PUSH(None).
+ values_or_none = match_keys(tstate, subject, keys);
+ if (values_or_none == NULL) goto error;
+ STACK_GROW(1);
+ POKE(1, values_or_none);
+ DISPATCH();
+ }
+
+ TARGET(GET_ITER) {
+ /* before: [obj]; after [getiter(obj)] */
+ PyObject *iterable = TOP();
+ PyObject *iter = PyObject_GetIter(iterable);
+ Py_DECREF(iterable);
+ SET_TOP(iter);
+ if (iter == NULL)
+ goto error;
+ DISPATCH();
+ }
+
+ TARGET(GET_YIELD_FROM_ITER) {
+ /* before: [obj]; after [getiter(obj)] */
+ PyObject *iterable = TOP();
+ PyObject *iter;
+ if (PyCoro_CheckExact(iterable)) {
+ /* `iterable` is a coroutine */
+ if (!(frame->f_code->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
+ /* and it is used in a 'yield from' expression of a
+ regular generator. */
+ Py_DECREF(iterable);
+ SET_TOP(NULL);
+ _PyErr_SetString(tstate, PyExc_TypeError,
+ "cannot 'yield from' a coroutine object "
+ "in a non-coroutine generator");
+ goto error;
+ }
+ }
+ else if (!PyGen_CheckExact(iterable)) {
+ /* `iterable` is not a generator. */
+ iter = PyObject_GetIter(iterable);
+ Py_DECREF(iterable);
+ SET_TOP(iter);
+ if (iter == NULL)
+ goto error;
+ }
+ PREDICT(LOAD_CONST);
+ DISPATCH();
+ }
+
+ TARGET(FOR_ITER) {
+ PREDICTED(FOR_ITER);
+ #if ENABLE_SPECIALIZATION
+ _PyForIterCache *cache = (_PyForIterCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ next_instr--;
+ _Py_Specialize_ForIter(TOP(), next_instr, oparg);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(FOR_ITER, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
+ /* before: [iter]; after: [iter, iter()] *or* [] */
+ PyObject *iter = TOP();
+ PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter);
+ if (next != NULL) {
+ PUSH(next);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ }
+ else {
+ if (_PyErr_Occurred(tstate)) {
+ if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
+ goto error;
+ }
+ else if (tstate->c_tracefunc != NULL) {
+ call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
+ }
+ _PyErr_Clear(tstate);
+ }
+ /* iterator ended normally */
+ assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR);
+ STACK_SHRINK(1);
+ Py_DECREF(iter);
+ /* Skip END_FOR */
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
+ }
+ DISPATCH();
+ }
+
+ TARGET(FOR_ITER_LIST) {
+ assert(cframe.use_tracing == 0);
+ _PyListIterObject *it = (_PyListIterObject *)TOP();
+ DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER);
+ STAT_INC(FOR_ITER, hit);
+ PyListObject *seq = it->it_seq;
+ if (seq) {
+ if (it->it_index < PyList_GET_SIZE(seq)) {
+ PyObject *next = PyList_GET_ITEM(seq, it->it_index++);
+ PUSH(Py_NewRef(next));
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ goto end_for_iter_list; // End of this instruction
+ }
+ it->it_seq = NULL;
+ Py_DECREF(seq);
+ }
+ STACK_SHRINK(1);
+ Py_DECREF(it);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
+ end_for_iter_list:
+ DISPATCH();
+ }
+
+ TARGET(FOR_ITER_TUPLE) {
+ assert(cframe.use_tracing == 0);
+ _PyTupleIterObject *it = (_PyTupleIterObject *)TOP();
+ DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER);
+ STAT_INC(FOR_ITER, hit);
+ PyTupleObject *seq = it->it_seq;
+ if (seq) {
+ if (it->it_index < PyTuple_GET_SIZE(seq)) {
+ PyObject *next = PyTuple_GET_ITEM(seq, it->it_index++);
+ PUSH(Py_NewRef(next));
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ goto end_for_iter_tuple; // End of this instruction
+ }
+ it->it_seq = NULL;
+ Py_DECREF(seq);
+ }
+ STACK_SHRINK(1);
+ Py_DECREF(it);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
+ end_for_iter_tuple:
+ DISPATCH();
+ }
+
+ TARGET(FOR_ITER_RANGE) {
+ assert(cframe.use_tracing == 0);
+ _PyRangeIterObject *r = (_PyRangeIterObject *)TOP();
+ DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER);
+ STAT_INC(FOR_ITER, hit);
+ _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER];
+ assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST);
+ if (r->len <= 0) {
+ STACK_SHRINK(1);
+ Py_DECREF(r);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
+ }
+ else {
+ long value = r->start;
+ r->start = value + r->step;
+ r->len--;
+ if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) {
+ goto error;
+ }
+ // The STORE_FAST is already done.
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + 1);
+ }
+ DISPATCH();
+ }
+
+ TARGET(FOR_ITER_GEN) {
+ assert(cframe.use_tracing == 0);
+ PyGenObject *gen = (PyGenObject *)TOP();
+ DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER);
+ DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER);
+ STAT_INC(FOR_ITER, hit);
+ _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
+ frame->yield_offset = oparg;
+ _PyFrame_StackPush(gen_frame, Py_NewRef(Py_None));
+ gen->gi_frame_state = FRAME_EXECUTING;
+ gen->gi_exc_state.previous_item = tstate->exc_info;
+ tstate->exc_info = &gen->gi_exc_state;
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg);
+ assert(_Py_OPCODE(*next_instr) == END_FOR);
+ DISPATCH_INLINED(gen_frame);
+ }
+
+ TARGET(BEFORE_ASYNC_WITH) {
+ PyObject *mgr = TOP();
+ PyObject *res;
+ PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__));
+ if (enter == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'%.200s' object does not support the "
+ "asynchronous context manager protocol",
+ Py_TYPE(mgr)->tp_name);
+ }
+ goto error;
+ }
+ PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__));
+ if (exit == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'%.200s' object does not support the "
+ "asynchronous context manager protocol "
+ "(missed __aexit__ method)",
+ Py_TYPE(mgr)->tp_name);
+ }
+ Py_DECREF(enter);
+ goto error;
+ }
+ SET_TOP(exit);
+ Py_DECREF(mgr);
+ res = _PyObject_CallNoArgs(enter);
+ Py_DECREF(enter);
+ if (res == NULL)
+ goto error;
+ PUSH(res);
+ PREDICT(GET_AWAITABLE);
+ DISPATCH();
+ }
+
+ TARGET(BEFORE_WITH) {
+ PyObject *mgr = TOP();
+ PyObject *res;
+ PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__));
+ if (enter == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'%.200s' object does not support the "
+ "context manager protocol",
+ Py_TYPE(mgr)->tp_name);
+ }
+ goto error;
+ }
+ PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__));
+ if (exit == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ "'%.200s' object does not support the "
+ "context manager protocol "
+ "(missed __exit__ method)",
+ Py_TYPE(mgr)->tp_name);
+ }
+ Py_DECREF(enter);
+ goto error;
+ }
+ SET_TOP(exit);
+ Py_DECREF(mgr);
+ res = _PyObject_CallNoArgs(enter);
+ Py_DECREF(enter);
+ if (res == NULL) {
+ goto error;
+ }
+ PUSH(res);
+ DISPATCH();
+ }
+
+ TARGET(WITH_EXCEPT_START) {
+ PyObject *val = PEEK(1);
+ PyObject *lasti = PEEK(3);
+ PyObject *exit_func = PEEK(4);
+ PyObject *res;
+ /* At the top of the stack are 4 values:
+ - val: TOP = exc_info()
+ - unused: SECOND = previous exception
+ - lasti: THIRD = lasti of exception in exc_info()
+ - exit_func: FOURTH = the context.__exit__ bound method
+ We call FOURTH(type(TOP), TOP, GetTraceback(TOP)).
+ Then we push the __exit__ return value.
+ */
+ PyObject *exc, *tb;
+
+ assert(val && PyExceptionInstance_Check(val));
+ exc = PyExceptionInstance_Class(val);
+ tb = PyException_GetTraceback(val);
+ Py_XDECREF(tb);
+ assert(PyLong_Check(lasti));
+ (void)lasti; // Shut up compiler warning if asserts are off
+ PyObject *stack[4] = {NULL, exc, val, tb};
+ res = PyObject_Vectorcall(exit_func, stack + 1,
+ 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+ if (res == NULL) goto error;
+ STACK_GROW(1);
+ POKE(1, res);
+ DISPATCH();
+ }
+
+ TARGET(PUSH_EXC_INFO) {
+ PyObject *value = TOP();
+
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ if (exc_info->exc_value != NULL) {
+ SET_TOP(exc_info->exc_value);
+ }
+ else {
+ SET_TOP(Py_NewRef(Py_None));
+ }
+
+ PUSH(Py_NewRef(value));
+ assert(PyExceptionInstance_Check(value));
+ exc_info->exc_value = value;
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
+ /* Cached method object */
+ assert(cframe.use_tracing == 0);
+ PyObject *self = TOP();
+ PyTypeObject *self_cls = Py_TYPE(self);
+ _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
+ uint32_t type_version = read_u32(cache->type_version);
+ assert(type_version != 0);
+ DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
+ assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT);
+ PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(self);
+ DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
+ PyHeapTypeObject *self_heap_type = (PyHeapTypeObject *)self_cls;
+ DEOPT_IF(self_heap_type->ht_cached_keys->dk_version !=
+ read_u32(cache->keys_version), LOAD_ATTR);
+ STAT_INC(LOAD_ATTR, hit);
+ PyObject *res = read_obj(cache->descr);
+ assert(res != NULL);
+ assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
+ SET_TOP(Py_NewRef(res));
+ PUSH(self);
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR_METHOD_NO_DICT) {
+ assert(cframe.use_tracing == 0);
+ PyObject *self = TOP();
+ PyTypeObject *self_cls = Py_TYPE(self);
+ _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
+ uint32_t type_version = read_u32(cache->type_version);
+ DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
+ assert(self_cls->tp_dictoffset == 0);
+ STAT_INC(LOAD_ATTR, hit);
+ PyObject *res = read_obj(cache->descr);
+ assert(res != NULL);
+ assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
+ SET_TOP(Py_NewRef(res));
+ PUSH(self);
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH();
+ }
+
+ TARGET(LOAD_ATTR_METHOD_LAZY_DICT) {
+ assert(cframe.use_tracing == 0);
+ PyObject *self = TOP();
+ PyTypeObject *self_cls = Py_TYPE(self);
+ _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
+ uint32_t type_version = read_u32(cache->type_version);
+ DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
+ Py_ssize_t dictoffset = self_cls->tp_dictoffset;
+ assert(dictoffset > 0);
+ PyObject *dict = *(PyObject **)((char *)self + dictoffset);
+ /* This object has a __dict__, just not yet created */
+ DEOPT_IF(dict != NULL, LOAD_ATTR);
+ STAT_INC(LOAD_ATTR, hit);
+ PyObject *res = read_obj(cache->descr);
+ assert(res != NULL);
+ assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
+ SET_TOP(Py_NewRef(res));
+ PUSH(self);
+ JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
+ DISPATCH();
+ }
+
+ TARGET(CALL_BOUND_METHOD_EXACT_ARGS) {
+ DEOPT_IF(is_method(stack_pointer, oparg), CALL);
+ PyObject *function = PEEK(oparg + 1);
+ DEOPT_IF(Py_TYPE(function) != &PyMethod_Type, CALL);
+ STAT_INC(CALL, hit);
+ PyObject *self = ((PyMethodObject *)function)->im_self;
+ PEEK(oparg + 1) = Py_NewRef(self);
+ PyObject *meth = ((PyMethodObject *)function)->im_func;
+ PEEK(oparg + 2) = Py_NewRef(meth);
+ Py_DECREF(function);
+ GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS);
+ }
+
+ TARGET(KW_NAMES) {
+ assert(kwnames == NULL);
+ assert(oparg < PyTuple_GET_SIZE(consts));
+ kwnames = GETITEM(consts, oparg);
+ DISPATCH();
+ }
+
+ TARGET(CALL) {
+ PREDICTED(CALL);
+ #if ENABLE_SPECIALIZATION
+ _PyCallCache *cache = (_PyCallCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ int is_meth = is_method(stack_pointer, oparg);
+ int nargs = oparg + is_meth;
+ PyObject *callable = PEEK(nargs + 1);
+ next_instr--;
+ _Py_Specialize_Call(callable, next_instr, nargs, kwnames);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(CALL, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
+ int total_args, is_meth;
+ is_meth = is_method(stack_pointer, oparg);
+ PyObject *function = PEEK(oparg + 1);
+ if (!is_meth && Py_TYPE(function) == &PyMethod_Type) {
+ PyObject *self = ((PyMethodObject *)function)->im_self;
+ PEEK(oparg+1) = Py_NewRef(self);
+ PyObject *meth = ((PyMethodObject *)function)->im_func;
+ PEEK(oparg+2) = Py_NewRef(meth);
+ Py_DECREF(function);
+ is_meth = 1;
+ }
+ total_args = oparg + is_meth;
+ function = PEEK(total_args + 1);
+ int positional_args = total_args - KWNAMES_LEN();
+ // Check if the call can be inlined or not
+ if (Py_TYPE(function) == &PyFunction_Type &&
+ tstate->interp->eval_frame == NULL &&
+ ((PyFunctionObject *)function)->vectorcall == _PyFunction_Vectorcall)
+ {
+ int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags;
+ PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(function));
+ STACK_SHRINK(total_args);
+ _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit(
+ tstate, (PyFunctionObject *)function, locals,
+ stack_pointer, positional_args, kwnames
+ );
+ kwnames = NULL;
+ STACK_SHRINK(2-is_meth);
+ // The frame has stolen all the arguments from the stack,
+ // so there is no need to clean them up.
+ if (new_frame == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ DISPATCH_INLINED(new_frame);
+ }
+ /* Callable is not a normal Python function */
+ PyObject *res;
+ if (cframe.use_tracing) {
+ res = trace_call_function(
+ tstate, function, stack_pointer-total_args,
+ positional_args, kwnames);
+ }
+ else {
+ res = PyObject_Vectorcall(
+ function, stack_pointer-total_args,
+ positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET,
+ kwnames);
+ }
+ kwnames = NULL;
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+ Py_DECREF(function);
+ /* Clear the stack */
+ STACK_SHRINK(total_args);
+ for (int i = 0; i < total_args; i++) {
+ Py_DECREF(stack_pointer[i]);
+ }
+ STACK_SHRINK(2-is_meth);
+ PUSH(res);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_PY_EXACT_ARGS) {
+ PREDICTED(CALL_PY_EXACT_ARGS);
+ assert(kwnames == NULL);
+ DEOPT_IF(tstate->interp->eval_frame, CALL);
+ _PyCallCache *cache = (_PyCallCache *)next_instr;
+ int is_meth = is_method(stack_pointer, oparg);
+ int argcount = oparg + is_meth;
+ PyObject *callable = PEEK(argcount + 1);
+ DEOPT_IF(!PyFunction_Check(callable), CALL);
+ PyFunctionObject *func = (PyFunctionObject *)callable;
+ DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL);
+ PyCodeObject *code = (PyCodeObject *)func->func_code;
+ DEOPT_IF(code->co_argcount != argcount, CALL);
+ DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL);
+ STAT_INC(CALL, hit);
+ _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, argcount);
+ STACK_SHRINK(argcount);
+ for (int i = 0; i < argcount; i++) {
+ new_frame->localsplus[i] = stack_pointer[i];
+ }
+ STACK_SHRINK(2-is_meth);
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ DISPATCH_INLINED(new_frame);
+ }
+
+ TARGET(CALL_PY_WITH_DEFAULTS) {
+ assert(kwnames == NULL);
+ DEOPT_IF(tstate->interp->eval_frame, CALL);
+ _PyCallCache *cache = (_PyCallCache *)next_instr;
+ int is_meth = is_method(stack_pointer, oparg);
+ int argcount = oparg + is_meth;
+ PyObject *callable = PEEK(argcount + 1);
+ DEOPT_IF(!PyFunction_Check(callable), CALL);
+ PyFunctionObject *func = (PyFunctionObject *)callable;
+ DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL);
+ PyCodeObject *code = (PyCodeObject *)func->func_code;
+ DEOPT_IF(argcount > code->co_argcount, CALL);
+ int minargs = cache->min_args;
+ DEOPT_IF(argcount < minargs, CALL);
+ DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL);
+ STAT_INC(CALL, hit);
+ _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount);
+ STACK_SHRINK(argcount);
+ for (int i = 0; i < argcount; i++) {
+ new_frame->localsplus[i] = stack_pointer[i];
+ }
+ for (int i = argcount; i < code->co_argcount; i++) {
+ PyObject *def = PyTuple_GET_ITEM(func->func_defaults,
+ i - minargs);
+ new_frame->localsplus[i] = Py_NewRef(def);
+ }
+ STACK_SHRINK(2-is_meth);
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ DISPATCH_INLINED(new_frame);
+ }
+
+ TARGET(CALL_NO_KW_TYPE_1) {
+ assert(kwnames == NULL);
+ assert(cframe.use_tracing == 0);
+ assert(oparg == 1);
+ DEOPT_IF(is_method(stack_pointer, 1), CALL);
+ PyObject *obj = TOP();
+ PyObject *callable = SECOND();
+ DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL);
+ STAT_INC(CALL, hit);
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ PyObject *res = Py_NewRef(Py_TYPE(obj));
+ Py_DECREF(callable);
+ Py_DECREF(obj);
+ STACK_SHRINK(2);
+ SET_TOP(res);
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_STR_1) {
+ assert(kwnames == NULL);
+ assert(cframe.use_tracing == 0);
+ assert(oparg == 1);
+ DEOPT_IF(is_method(stack_pointer, 1), CALL);
+ PyObject *callable = PEEK(2);
+ DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL);
+ STAT_INC(CALL, hit);
+ PyObject *arg = TOP();
+ PyObject *res = PyObject_Str(arg);
+ Py_DECREF(arg);
+ Py_DECREF(&PyUnicode_Type);
+ STACK_SHRINK(2);
+ SET_TOP(res);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_TUPLE_1) {
+ assert(kwnames == NULL);
+ assert(oparg == 1);
+ DEOPT_IF(is_method(stack_pointer, 1), CALL);
+ PyObject *callable = PEEK(2);
+ DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL);
+ STAT_INC(CALL, hit);
+ PyObject *arg = TOP();
+ PyObject *res = PySequence_Tuple(arg);
+ Py_DECREF(arg);
+ Py_DECREF(&PyTuple_Type);
+ STACK_SHRINK(2);
+ SET_TOP(res);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_BUILTIN_CLASS) {
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ int kwnames_len = KWNAMES_LEN();
+ PyObject *callable = PEEK(total_args + 1);
+ DEOPT_IF(!PyType_Check(callable), CALL);
+ PyTypeObject *tp = (PyTypeObject *)callable;
+ DEOPT_IF(tp->tp_vectorcall == NULL, CALL);
+ STAT_INC(CALL, hit);
+ STACK_SHRINK(total_args);
+ PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer,
+ total_args-kwnames_len, kwnames);
+ kwnames = NULL;
+ /* Free the arguments. */
+ for (int i = 0; i < total_args; i++) {
+ Py_DECREF(stack_pointer[i]);
+ }
+ Py_DECREF(tp);
+ STACK_SHRINK(1-is_meth);
+ SET_TOP(res);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_BUILTIN_O) {
+ assert(cframe.use_tracing == 0);
+ /* Builtin METH_O functions */
+ assert(kwnames == NULL);
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ DEOPT_IF(total_args != 1, CALL);
+ PyObject *callable = PEEK(total_args + 1);
+ DEOPT_IF(!PyCFunction_CheckExact(callable), CALL);
+ DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL);
+ STAT_INC(CALL, hit);
+ PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable);
+ // This is slower but CPython promises to check all non-vectorcall
+ // function calls.
+ if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) {
+ goto error;
+ }
+ PyObject *arg = TOP();
+ PyObject *res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg);
+ _Py_LeaveRecursiveCallTstate(tstate);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+
+ Py_DECREF(arg);
+ Py_DECREF(callable);
+ STACK_SHRINK(2-is_meth);
+ SET_TOP(res);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_BUILTIN_FAST) {
+ assert(cframe.use_tracing == 0);
+ /* Builtin METH_FASTCALL functions, without keywords */
+ assert(kwnames == NULL);
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ PyObject *callable = PEEK(total_args + 1);
+ DEOPT_IF(!PyCFunction_CheckExact(callable), CALL);
+ DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL,
+ CALL);
+ STAT_INC(CALL, hit);
+ PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable);
+ STACK_SHRINK(total_args);
+ /* res = func(self, args, nargs) */
+ PyObject *res = ((_PyCFunctionFast)(void(*)(void))cfunc)(
+ PyCFunction_GET_SELF(callable),
+ stack_pointer,
+ total_args);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+
+ /* Free the arguments. */
+ for (int i = 0; i < total_args; i++) {
+ Py_DECREF(stack_pointer[i]);
+ }
+ STACK_SHRINK(2-is_meth);
+ PUSH(res);
+ Py_DECREF(callable);
+ if (res == NULL) {
+ /* Not deopting because this doesn't mean our optimization was
+ wrong. `res` can be NULL for valid reasons. Eg. getattr(x,
+ 'invalid'). In those cases an exception is set, so we must
+ handle it.
+ */
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) {
+ assert(cframe.use_tracing == 0);
+ /* Builtin METH_FASTCALL | METH_KEYWORDS functions */
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ PyObject *callable = PEEK(total_args + 1);
+ DEOPT_IF(!PyCFunction_CheckExact(callable), CALL);
+ DEOPT_IF(PyCFunction_GET_FLAGS(callable) !=
+ (METH_FASTCALL | METH_KEYWORDS), CALL);
+ STAT_INC(CALL, hit);
+ STACK_SHRINK(total_args);
+ /* res = func(self, args, nargs, kwnames) */
+ _PyCFunctionFastWithKeywords cfunc =
+ (_PyCFunctionFastWithKeywords)(void(*)(void))
+ PyCFunction_GET_FUNCTION(callable);
+ PyObject *res = cfunc(
+ PyCFunction_GET_SELF(callable),
+ stack_pointer,
+ total_args - KWNAMES_LEN(),
+ kwnames
+ );
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+ kwnames = NULL;
+
+ /* Free the arguments. */
+ for (int i = 0; i < total_args; i++) {
+ Py_DECREF(stack_pointer[i]);
+ }
+ STACK_SHRINK(2-is_meth);
+ PUSH(res);
+ Py_DECREF(callable);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_LEN) {
+ assert(cframe.use_tracing == 0);
+ assert(kwnames == NULL);
+ /* len(o) */
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ DEOPT_IF(total_args != 1, CALL);
+ PyObject *callable = PEEK(total_args + 1);
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ DEOPT_IF(callable != interp->callable_cache.len, CALL);
+ STAT_INC(CALL, hit);
+ PyObject *arg = TOP();
+ Py_ssize_t len_i = PyObject_Length(arg);
+ if (len_i < 0) {
+ goto error;
+ }
+ PyObject *res = PyLong_FromSsize_t(len_i);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+
+ STACK_SHRINK(2-is_meth);
+ SET_TOP(res);
+ Py_DECREF(callable);
+ Py_DECREF(arg);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_ISINSTANCE) {
+ assert(cframe.use_tracing == 0);
+ assert(kwnames == NULL);
+ /* isinstance(o, o2) */
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ PyObject *callable = PEEK(total_args + 1);
+ DEOPT_IF(total_args != 2, CALL);
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ DEOPT_IF(callable != interp->callable_cache.isinstance, CALL);
+ STAT_INC(CALL, hit);
+ PyObject *cls = POP();
+ PyObject *inst = TOP();
+ int retval = PyObject_IsInstance(inst, cls);
+ if (retval < 0) {
+ Py_DECREF(cls);
+ goto error;
+ }
+ PyObject *res = PyBool_FromLong(retval);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+
+ STACK_SHRINK(2-is_meth);
+ SET_TOP(res);
+ Py_DECREF(inst);
+ Py_DECREF(cls);
+ Py_DECREF(callable);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_LIST_APPEND) {
+ assert(cframe.use_tracing == 0);
+ assert(kwnames == NULL);
+ assert(oparg == 1);
+ PyObject *callable = PEEK(3);
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ DEOPT_IF(callable != interp->callable_cache.list_append, CALL);
+ PyObject *list = SECOND();
+ DEOPT_IF(!PyList_Check(list), CALL);
+ STAT_INC(CALL, hit);
+ PyObject *arg = POP();
+ if (_PyList_AppendTakeRef((PyListObject *)list, arg) < 0) {
+ goto error;
+ }
+ STACK_SHRINK(2);
+ Py_DECREF(list);
+ Py_DECREF(callable);
+ // CALL + POP_TOP
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1);
+ assert(_Py_OPCODE(next_instr[-1]) == POP_TOP);
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) {
+ assert(kwnames == NULL);
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ PyMethodDescrObject *callable =
+ (PyMethodDescrObject *)PEEK(total_args + 1);
+ DEOPT_IF(total_args != 2, CALL);
+ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
+ PyMethodDef *meth = callable->d_method;
+ DEOPT_IF(meth->ml_flags != METH_O, CALL);
+ PyObject *arg = TOP();
+ PyObject *self = SECOND();
+ DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL);
+ STAT_INC(CALL, hit);
+ PyCFunction cfunc = meth->ml_meth;
+ // This is slower but CPython promises to check all non-vectorcall
+ // function calls.
+ if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) {
+ goto error;
+ }
+ PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, arg);
+ _Py_LeaveRecursiveCallTstate(tstate);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+ Py_DECREF(self);
+ Py_DECREF(arg);
+ STACK_SHRINK(oparg + 1);
+ SET_TOP(res);
+ Py_DECREF(callable);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ PyMethodDescrObject *callable =
+ (PyMethodDescrObject *)PEEK(total_args + 1);
+ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
+ PyMethodDef *meth = callable->d_method;
+ DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL);
+ PyTypeObject *d_type = callable->d_common.d_type;
+ PyObject *self = PEEK(total_args);
+ DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL);
+ STAT_INC(CALL, hit);
+ int nargs = total_args-1;
+ STACK_SHRINK(nargs);
+ _PyCFunctionFastWithKeywords cfunc =
+ (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
+ PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(),
+ kwnames);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+ kwnames = NULL;
+
+ /* Free the arguments. */
+ for (int i = 0; i < nargs; i++) {
+ Py_DECREF(stack_pointer[i]);
+ }
+ Py_DECREF(self);
+ STACK_SHRINK(2-is_meth);
+ SET_TOP(res);
+ Py_DECREF(callable);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) {
+ assert(kwnames == NULL);
+ assert(oparg == 0 || oparg == 1);
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ DEOPT_IF(total_args != 1, CALL);
+ PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND();
+ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
+ PyMethodDef *meth = callable->d_method;
+ PyObject *self = TOP();
+ DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL);
+ DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL);
+ STAT_INC(CALL, hit);
+ PyCFunction cfunc = meth->ml_meth;
+ // This is slower but CPython promises to check all non-vectorcall
+ // function calls.
+ if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) {
+ goto error;
+ }
+ PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, NULL);
+ _Py_LeaveRecursiveCallTstate(tstate);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+ Py_DECREF(self);
+ STACK_SHRINK(oparg + 1);
+ SET_TOP(res);
+ Py_DECREF(callable);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) {
+ assert(kwnames == NULL);
+ int is_meth = is_method(stack_pointer, oparg);
+ int total_args = oparg + is_meth;
+ PyMethodDescrObject *callable =
+ (PyMethodDescrObject *)PEEK(total_args + 1);
+ /* Builtin METH_FASTCALL methods, without keywords */
+ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
+ PyMethodDef *meth = callable->d_method;
+ DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL);
+ PyObject *self = PEEK(total_args);
+ DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL);
+ STAT_INC(CALL, hit);
+ _PyCFunctionFast cfunc =
+ (_PyCFunctionFast)(void(*)(void))meth->ml_meth;
+ int nargs = total_args-1;
+ STACK_SHRINK(nargs);
+ PyObject *res = cfunc(self, stack_pointer, nargs);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+ /* Clear the stack of the arguments. */
+ for (int i = 0; i < nargs; i++) {
+ Py_DECREF(stack_pointer[i]);
+ }
+ Py_DECREF(self);
+ STACK_SHRINK(2-is_meth);
+ SET_TOP(res);
+ Py_DECREF(callable);
+ if (res == NULL) {
+ goto error;
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_CALL);
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(CALL_FUNCTION_EX) {
+ PREDICTED(CALL_FUNCTION_EX);
+ PyObject *func, *callargs, *kwargs = NULL, *result;
+ if (oparg & 0x01) {
+ kwargs = POP();
+ // DICT_MERGE is called before this opcode if there are kwargs.
+ // It converts all dict subtypes in kwargs into regular dicts.
+ assert(PyDict_CheckExact(kwargs));
+ }
+ callargs = POP();
+ func = TOP();
+ if (!PyTuple_CheckExact(callargs)) {
+ if (check_args_iterable(tstate, func, callargs) < 0) {
+ Py_DECREF(callargs);
+ goto error;
+ }
+ Py_SETREF(callargs, PySequence_Tuple(callargs));
+ if (callargs == NULL) {
+ goto error;
+ }
+ }
+ assert(PyTuple_CheckExact(callargs));
+
+ result = do_call_core(tstate, func, callargs, kwargs, cframe.use_tracing);
+ Py_DECREF(func);
+ Py_DECREF(callargs);
+ Py_XDECREF(kwargs);
+
+ STACK_SHRINK(1);
+ assert(TOP() == NULL);
+ SET_TOP(result);
+ if (result == NULL) {
+ goto error;
+ }
+ CHECK_EVAL_BREAKER();
+ DISPATCH();
+ }
+
+ TARGET(MAKE_FUNCTION) {
+ PyObject *codeobj = POP();
+ PyFunctionObject *func = (PyFunctionObject *)
+ PyFunction_New(codeobj, GLOBALS());
+
+ Py_DECREF(codeobj);
+ if (func == NULL) {
+ goto error;
+ }
+
+ if (oparg & 0x08) {
+ assert(PyTuple_CheckExact(TOP()));
+ func->func_closure = POP();
+ }
+ if (oparg & 0x04) {
+ assert(PyTuple_CheckExact(TOP()));
+ func->func_annotations = POP();
+ }
+ if (oparg & 0x02) {
+ assert(PyDict_CheckExact(TOP()));
+ func->func_kwdefaults = POP();
+ }
+ if (oparg & 0x01) {
+ assert(PyTuple_CheckExact(TOP()));
+ func->func_defaults = POP();
+ }
+
+ func->func_version = ((PyCodeObject *)codeobj)->co_version;
+ PUSH((PyObject *)func);
+ DISPATCH();
+ }
+
+ TARGET(RETURN_GENERATOR) {
+ assert(PyFunction_Check(frame->f_funcobj));
+ PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj;
+ PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func);
+ if (gen == NULL) {
+ goto error;
+ }
+ assert(EMPTY());
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
+ _PyFrame_Copy(frame, gen_frame);
+ assert(frame->frame_obj == NULL);
+ gen->gi_frame_state = FRAME_CREATED;
+ gen_frame->owner = FRAME_OWNED_BY_GENERATOR;
+ _Py_LeaveRecursiveCallPy(tstate);
+ assert(frame != &entry_frame);
+ _PyInterpreterFrame *prev = frame->previous;
+ _PyThreadState_PopFrame(tstate, frame);
+ frame = cframe.current_frame = prev;
+ _PyFrame_StackPush(frame, (PyObject *)gen);
+ goto resume_frame;
+ }
+
+ TARGET(BUILD_SLICE) {
+ PyObject *start, *stop, *step, *slice;
+ if (oparg == 3)
+ step = POP();
+ else
+ step = NULL;
+ stop = POP();
+ start = TOP();
+ slice = PySlice_New(start, stop, step);
+ Py_DECREF(start);
+ Py_DECREF(stop);
+ Py_XDECREF(step);
+ SET_TOP(slice);
+ if (slice == NULL)
+ goto error;
+ DISPATCH();
+ }
+
+ TARGET(FORMAT_VALUE) {
+ /* Handles f-string value formatting. */
+ PyObject *result;
+ PyObject *fmt_spec;
+ PyObject *value;
+ PyObject *(*conv_fn)(PyObject *);
+ int which_conversion = oparg & FVC_MASK;
+ int have_fmt_spec = (oparg & FVS_MASK) == FVS_HAVE_SPEC;
+
+ fmt_spec = have_fmt_spec ? POP() : NULL;
+ value = POP();
+
+ /* See if any conversion is specified. */
+ switch (which_conversion) {
+ case FVC_NONE: conv_fn = NULL; break;
+ case FVC_STR: conv_fn = PyObject_Str; break;
+ case FVC_REPR: conv_fn = PyObject_Repr; break;
+ case FVC_ASCII: conv_fn = PyObject_ASCII; break;
+ default:
+ _PyErr_Format(tstate, PyExc_SystemError,
+ "unexpected conversion flag %d",
+ which_conversion);
+ goto error;
+ }
+
+ /* If there's a conversion function, call it and replace
+ value with that result. Otherwise, just use value,
+ without conversion. */
+ if (conv_fn != NULL) {
+ result = conv_fn(value);
+ Py_DECREF(value);
+ if (result == NULL) {
+ Py_XDECREF(fmt_spec);
+ goto error;
+ }
+ value = result;
+ }
+
+ /* If value is a unicode object, and there's no fmt_spec,
+ then we know the result of format(value) is value
+ itself. In that case, skip calling format(). I plan to
+ move this optimization in to PyObject_Format()
+ itself. */
+ if (PyUnicode_CheckExact(value) && fmt_spec == NULL) {
+ /* Do nothing, just transfer ownership to result. */
+ result = value;
+ } else {
+ /* Actually call format(). */
+ result = PyObject_Format(value, fmt_spec);
+ Py_DECREF(value);
+ Py_XDECREF(fmt_spec);
+ if (result == NULL) {
+ goto error;
+ }
+ }
+
+ PUSH(result);
+ DISPATCH();
+ }
+
+ TARGET(COPY) {
+ assert(oparg != 0);
+ PyObject *peek = PEEK(oparg);
+ PUSH(Py_NewRef(peek));
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP) {
+ PREDICTED(BINARY_OP);
+ static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size");
+ PyObject *rhs = PEEK(1);
+ PyObject *lhs = PEEK(2);
+ PyObject *res;
+ #if ENABLE_SPECIALIZATION
+ _PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ next_instr--;
+ _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, &GETLOCAL(0));
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(BINARY_OP, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
+ assert(0 <= oparg);
+ assert((unsigned)oparg < Py_ARRAY_LENGTH(binary_ops));
+ assert(binary_ops[oparg]);
+ res = binary_ops[oparg](lhs, rhs);
+ Py_DECREF(lhs);
+ Py_DECREF(rhs);
+ if (res == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, res);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(SWAP) {
+ assert(oparg != 0);
+ PyObject *top = TOP();
+ SET_TOP(PEEK(oparg));
+ PEEK(oparg) = top;
+ DISPATCH();
+ }
+
+ TARGET(EXTENDED_ARG) {
+ assert(oparg);
+ assert(cframe.use_tracing == 0);
+ opcode = _Py_OPCODE(*next_instr);
+ oparg = oparg << 8 | _Py_OPARG(*next_instr);
+ PRE_DISPATCH_GOTO();
+ DISPATCH_GOTO();
+ }
+
+ TARGET(CACHE) {
+ Py_UNREACHABLE();
+ }
diff --git a/Python/opcode_metadata_tier2.h b/Python/opcode_metadata_tier2.h
new file mode 100644
index 00000000000000..d7f86246e8f571
--- /dev/null
+++ b/Python/opcode_metadata_tier2.h
@@ -0,0 +1,883 @@
+// This file is generated by Tools\cases_generator\generate_cases.py --metadata
+// from Python\bytecodes.c
+// Do not edit!
+
+#ifndef NDEBUG
+static int
+_PyOpcode_num_popped(int opcode, int oparg) {
+ switch(opcode) {
+ case NOP:
+ return 0;
+ case RESUME:
+ return 0;
+ case LOAD_CLOSURE:
+ return 0;
+ case LOAD_FAST_CHECK:
+ return 0;
+ case LOAD_FAST:
+ return 0;
+ case LOAD_CONST:
+ return 0;
+ case STORE_FAST:
+ return 1;
+ case LOAD_FAST__LOAD_FAST:
+ return 0+0;
+ case LOAD_FAST__LOAD_CONST:
+ return 0+0;
+ case STORE_FAST__LOAD_FAST:
+ return 1+0;
+ case STORE_FAST__STORE_FAST:
+ return 1+1;
+ case LOAD_CONST__LOAD_FAST:
+ return 0+0;
+ case POP_TOP:
+ return 1;
+ case PUSH_NULL:
+ return 0;
+ case END_FOR:
+ return 1+1;
+ case UNARY_NEGATIVE:
+ return 1;
+ case UNARY_NOT:
+ return 1;
+ case UNARY_INVERT:
+ return 1;
+ case BINARY_OP_MULTIPLY_INT:
+ return 2;
+ case BINARY_OP_MULTIPLY_FLOAT:
+ return 2;
+ case BINARY_OP_SUBTRACT_INT:
+ return 2;
+ case BINARY_OP_SUBTRACT_FLOAT:
+ return 2;
+ case BINARY_OP_ADD_UNICODE:
+ return 2;
+ case BINARY_OP_INPLACE_ADD_UNICODE:
+ return 2;
+ case BINARY_OP_ADD_FLOAT:
+ return 2;
+ case BINARY_OP_ADD_INT_TYPE_CHECK:
+ return 2;
+ case BINARY_OP_ADD_INST_REST:
+ return 2;
+ case BINARY_OP_ADD_INT:
+ return 2;
+ case BINARY_SUBSCR:
+ return 2;
+ case BINARY_SLICE:
+ return 3;
+ case STORE_SLICE:
+ return 4;
+ case BINARY_SUBSCR_LIST_INT:
+ return 2;
+ case BINARY_SUBSCR_TUPLE_INT:
+ return 2;
+ case BINARY_SUBSCR_DICT:
+ return 2;
+ case BINARY_SUBSCR_GETITEM:
+ return 2;
+ case LIST_APPEND:
+ return (oparg-1) + 2;
+ case SET_ADD:
+ return (oparg-1) + 2;
+ case STORE_SUBSCR:
+ return 3;
+ case STORE_SUBSCR_LIST_INT:
+ return 3;
+ case STORE_SUBSCR_DICT:
+ return 3;
+ case DELETE_SUBSCR:
+ return 2;
+ case CALL_INTRINSIC_1:
+ return 1;
+ case RAISE_VARARGS:
+ return oparg;
+ case INTERPRETER_EXIT:
+ return 1;
+ case RETURN_VALUE:
+ return 1;
+ case GET_AITER:
+ return 1;
+ case GET_ANEXT:
+ return 1;
+ case GET_AWAITABLE:
+ return 1;
+ case SEND:
+ return -1;
+ case YIELD_VALUE:
+ return 1;
+ case POP_EXCEPT:
+ return 1;
+ case RERAISE:
+ return -1;
+ case PREP_RERAISE_STAR:
+ return 2;
+ case END_ASYNC_FOR:
+ return -1;
+ case CLEANUP_THROW:
+ return -1;
+ case LOAD_ASSERTION_ERROR:
+ return 0;
+ case LOAD_BUILD_CLASS:
+ return 0;
+ case STORE_NAME:
+ return 1;
+ case DELETE_NAME:
+ return 0;
+ case UNPACK_SEQUENCE:
+ return -1;
+ case UNPACK_SEQUENCE_TWO_TUPLE:
+ return -1;
+ case UNPACK_SEQUENCE_TUPLE:
+ return -1;
+ case UNPACK_SEQUENCE_LIST:
+ return -1;
+ case UNPACK_EX:
+ return -1;
+ case STORE_ATTR:
+ return 2;
+ case DELETE_ATTR:
+ return 1;
+ case STORE_GLOBAL:
+ return 1;
+ case DELETE_GLOBAL:
+ return 0;
+ case LOAD_NAME:
+ return 0;
+ case LOAD_GLOBAL:
+ return -1;
+ case LOAD_GLOBAL_MODULE:
+ return -1;
+ case LOAD_GLOBAL_BUILTIN:
+ return -1;
+ case DELETE_FAST:
+ return 0;
+ case MAKE_CELL:
+ return 0;
+ case DELETE_DEREF:
+ return 0;
+ case LOAD_CLASSDEREF:
+ return 0;
+ case LOAD_DEREF:
+ return 0;
+ case STORE_DEREF:
+ return 1;
+ case COPY_FREE_VARS:
+ return 0;
+ case BUILD_STRING:
+ return oparg;
+ case BUILD_TUPLE:
+ return oparg;
+ case BUILD_LIST:
+ return oparg;
+ case LIST_EXTEND:
+ return (oparg-1) + 2;
+ case SET_UPDATE:
+ return (oparg-1) + 2;
+ case BUILD_SET:
+ return oparg;
+ case BUILD_MAP:
+ return oparg*2;
+ case SETUP_ANNOTATIONS:
+ return 0;
+ case BUILD_CONST_KEY_MAP:
+ return oparg + 1;
+ case DICT_UPDATE:
+ return 1;
+ case DICT_MERGE:
+ return 1;
+ case MAP_ADD:
+ return 2;
+ case LOAD_ATTR:
+ return -1;
+ case LOAD_ATTR_INSTANCE_VALUE:
+ return -1;
+ case LOAD_ATTR_MODULE:
+ return -1;
+ case LOAD_ATTR_WITH_HINT:
+ return -1;
+ case LOAD_ATTR_SLOT:
+ return -1;
+ case LOAD_ATTR_CLASS:
+ return -1;
+ case LOAD_ATTR_PROPERTY:
+ return -1;
+ case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN:
+ return -1;
+ case STORE_ATTR_INSTANCE_VALUE:
+ return 2;
+ case STORE_ATTR_WITH_HINT:
+ return 2;
+ case STORE_ATTR_SLOT:
+ return 2;
+ case COMPARE_OP:
+ return 2;
+ case COMPARE_AND_BRANCH:
+ return 2;
+ case COMPARE_AND_BRANCH_FLOAT:
+ return 2;
+ case COMPARE_AND_BRANCH_INT:
+ return 2;
+ case COMPARE_AND_BRANCH_STR:
+ return 2;
+ case IS_OP:
+ return 2;
+ case CONTAINS_OP:
+ return 2;
+ case CHECK_EG_MATCH:
+ return 2;
+ case CHECK_EXC_MATCH:
+ return 2;
+ case IMPORT_NAME:
+ return 2;
+ case IMPORT_FROM:
+ return 1;
+ case JUMP_FORWARD:
+ return 0;
+ case JUMP_BACKWARD:
+ return 0;
+ case POP_JUMP_IF_FALSE:
+ return -1;
+ case POP_JUMP_IF_TRUE:
+ return -1;
+ case POP_JUMP_IF_NOT_NONE:
+ return -1;
+ case POP_JUMP_IF_NONE:
+ return -1;
+ case JUMP_IF_FALSE_OR_POP:
+ return -1;
+ case JUMP_IF_TRUE_OR_POP:
+ return -1;
+ case JUMP_BACKWARD_NO_INTERRUPT:
+ return -1;
+ case GET_LEN:
+ return -1;
+ case MATCH_CLASS:
+ return 3;
+ case MATCH_MAPPING:
+ return 1;
+ case MATCH_SEQUENCE:
+ return 1;
+ case MATCH_KEYS:
+ return 2;
+ case GET_ITER:
+ return -1;
+ case GET_YIELD_FROM_ITER:
+ return -1;
+ case FOR_ITER:
+ return -1;
+ case FOR_ITER_LIST:
+ return -1;
+ case FOR_ITER_TUPLE:
+ return -1;
+ case FOR_ITER_RANGE:
+ return -1;
+ case FOR_ITER_GEN:
+ return -1;
+ case BEFORE_ASYNC_WITH:
+ return -1;
+ case BEFORE_WITH:
+ return -1;
+ case WITH_EXCEPT_START:
+ return 4;
+ case PUSH_EXC_INFO:
+ return -1;
+ case LOAD_ATTR_METHOD_WITH_VALUES:
+ return -1;
+ case LOAD_ATTR_METHOD_NO_DICT:
+ return -1;
+ case LOAD_ATTR_METHOD_LAZY_DICT:
+ return -1;
+ case CALL_BOUND_METHOD_EXACT_ARGS:
+ return -1;
+ case KW_NAMES:
+ return -1;
+ case CALL:
+ return -1;
+ case CALL_PY_EXACT_ARGS:
+ return -1;
+ case CALL_PY_WITH_DEFAULTS:
+ return -1;
+ case CALL_NO_KW_TYPE_1:
+ return -1;
+ case CALL_NO_KW_STR_1:
+ return -1;
+ case CALL_NO_KW_TUPLE_1:
+ return -1;
+ case CALL_BUILTIN_CLASS:
+ return -1;
+ case CALL_NO_KW_BUILTIN_O:
+ return -1;
+ case CALL_NO_KW_BUILTIN_FAST:
+ return -1;
+ case CALL_BUILTIN_FAST_WITH_KEYWORDS:
+ return -1;
+ case CALL_NO_KW_LEN:
+ return -1;
+ case CALL_NO_KW_ISINSTANCE:
+ return -1;
+ case CALL_NO_KW_LIST_APPEND:
+ return -1;
+ case CALL_NO_KW_METHOD_DESCRIPTOR_O:
+ return -1;
+ case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS:
+ return -1;
+ case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS:
+ return -1;
+ case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
+ return -1;
+ case CALL_FUNCTION_EX:
+ return -1;
+ case MAKE_FUNCTION:
+ return -1;
+ case RETURN_GENERATOR:
+ return -1;
+ case BUILD_SLICE:
+ return -1;
+ case FORMAT_VALUE:
+ return -1;
+ case COPY:
+ return -1;
+ case BINARY_OP:
+ return 2;
+ case SWAP:
+ return -1;
+ case EXTENDED_ARG:
+ return -1;
+ case CACHE:
+ return -1;
+ default:
+ Py_UNREACHABLE();
+ }
+}
+#endif
+
+#ifndef NDEBUG
+static int
+_PyOpcode_num_pushed(int opcode, int oparg) {
+ switch(opcode) {
+ case NOP:
+ return 0;
+ case RESUME:
+ return 0;
+ case LOAD_CLOSURE:
+ return 1;
+ case LOAD_FAST_CHECK:
+ return 1;
+ case LOAD_FAST:
+ return 1;
+ case LOAD_CONST:
+ return 1;
+ case STORE_FAST:
+ return 0;
+ case LOAD_FAST__LOAD_FAST:
+ return 1+1;
+ case LOAD_FAST__LOAD_CONST:
+ return 1+1;
+ case STORE_FAST__LOAD_FAST:
+ return 0+1;
+ case STORE_FAST__STORE_FAST:
+ return 0+0;
+ case LOAD_CONST__LOAD_FAST:
+ return 1+1;
+ case POP_TOP:
+ return 0;
+ case PUSH_NULL:
+ return 1;
+ case END_FOR:
+ return 0+0;
+ case UNARY_NEGATIVE:
+ return 1;
+ case UNARY_NOT:
+ return 1;
+ case UNARY_INVERT:
+ return 1;
+ case BINARY_OP_MULTIPLY_INT:
+ return 1;
+ case BINARY_OP_MULTIPLY_FLOAT:
+ return 1;
+ case BINARY_OP_SUBTRACT_INT:
+ return 1;
+ case BINARY_OP_SUBTRACT_FLOAT:
+ return 1;
+ case BINARY_OP_ADD_UNICODE:
+ return 1;
+ case BINARY_OP_INPLACE_ADD_UNICODE:
+ return 0;
+ case BINARY_OP_ADD_FLOAT:
+ return 1;
+ case BINARY_OP_ADD_INT_TYPE_CHECK:
+ return 2;
+ case BINARY_OP_ADD_INST_REST:
+ return 1;
+ case BINARY_OP_ADD_INT:
+ return 1;
+ case BINARY_SUBSCR:
+ return 1;
+ case BINARY_SLICE:
+ return 1;
+ case STORE_SLICE:
+ return 0;
+ case BINARY_SUBSCR_LIST_INT:
+ return 1;
+ case BINARY_SUBSCR_TUPLE_INT:
+ return 1;
+ case BINARY_SUBSCR_DICT:
+ return 1;
+ case BINARY_SUBSCR_GETITEM:
+ return 1;
+ case LIST_APPEND:
+ return (oparg-1) + 1;
+ case SET_ADD:
+ return (oparg-1) + 1;
+ case STORE_SUBSCR:
+ return 0;
+ case STORE_SUBSCR_LIST_INT:
+ return 0;
+ case STORE_SUBSCR_DICT:
+ return 0;
+ case DELETE_SUBSCR:
+ return 0;
+ case CALL_INTRINSIC_1:
+ return 1;
+ case RAISE_VARARGS:
+ return 0;
+ case INTERPRETER_EXIT:
+ return 0;
+ case RETURN_VALUE:
+ return 0;
+ case GET_AITER:
+ return 1;
+ case GET_ANEXT:
+ return 2;
+ case GET_AWAITABLE:
+ return 1;
+ case SEND:
+ return -1;
+ case YIELD_VALUE:
+ return 1;
+ case POP_EXCEPT:
+ return 0;
+ case RERAISE:
+ return -1;
+ case PREP_RERAISE_STAR:
+ return 1;
+ case END_ASYNC_FOR:
+ return -1;
+ case CLEANUP_THROW:
+ return -1;
+ case LOAD_ASSERTION_ERROR:
+ return 1;
+ case LOAD_BUILD_CLASS:
+ return 1;
+ case STORE_NAME:
+ return 0;
+ case DELETE_NAME:
+ return 0;
+ case UNPACK_SEQUENCE:
+ return -1;
+ case UNPACK_SEQUENCE_TWO_TUPLE:
+ return -1;
+ case UNPACK_SEQUENCE_TUPLE:
+ return -1;
+ case UNPACK_SEQUENCE_LIST:
+ return -1;
+ case UNPACK_EX:
+ return -1;
+ case STORE_ATTR:
+ return 0;
+ case DELETE_ATTR:
+ return 0;
+ case STORE_GLOBAL:
+ return 0;
+ case DELETE_GLOBAL:
+ return 0;
+ case LOAD_NAME:
+ return 1;
+ case LOAD_GLOBAL:
+ return -1;
+ case LOAD_GLOBAL_MODULE:
+ return -1;
+ case LOAD_GLOBAL_BUILTIN:
+ return -1;
+ case DELETE_FAST:
+ return 0;
+ case MAKE_CELL:
+ return 0;
+ case DELETE_DEREF:
+ return 0;
+ case LOAD_CLASSDEREF:
+ return 1;
+ case LOAD_DEREF:
+ return 1;
+ case STORE_DEREF:
+ return 0;
+ case COPY_FREE_VARS:
+ return 0;
+ case BUILD_STRING:
+ return 1;
+ case BUILD_TUPLE:
+ return 1;
+ case BUILD_LIST:
+ return 1;
+ case LIST_EXTEND:
+ return (oparg-1) + 1;
+ case SET_UPDATE:
+ return (oparg-1) + 1;
+ case BUILD_SET:
+ return 1;
+ case BUILD_MAP:
+ return 1;
+ case SETUP_ANNOTATIONS:
+ return 0;
+ case BUILD_CONST_KEY_MAP:
+ return 1;
+ case DICT_UPDATE:
+ return 0;
+ case DICT_MERGE:
+ return 0;
+ case MAP_ADD:
+ return 0;
+ case LOAD_ATTR:
+ return -1;
+ case LOAD_ATTR_INSTANCE_VALUE:
+ return -1;
+ case LOAD_ATTR_MODULE:
+ return -1;
+ case LOAD_ATTR_WITH_HINT:
+ return -1;
+ case LOAD_ATTR_SLOT:
+ return -1;
+ case LOAD_ATTR_CLASS:
+ return -1;
+ case LOAD_ATTR_PROPERTY:
+ return -1;
+ case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN:
+ return -1;
+ case STORE_ATTR_INSTANCE_VALUE:
+ return 0;
+ case STORE_ATTR_WITH_HINT:
+ return 0;
+ case STORE_ATTR_SLOT:
+ return 0;
+ case COMPARE_OP:
+ return 1;
+ case COMPARE_AND_BRANCH:
+ return 0;
+ case COMPARE_AND_BRANCH_FLOAT:
+ return 0;
+ case COMPARE_AND_BRANCH_INT:
+ return 0;
+ case COMPARE_AND_BRANCH_STR:
+ return 0;
+ case IS_OP:
+ return 1;
+ case CONTAINS_OP:
+ return 1;
+ case CHECK_EG_MATCH:
+ return 2;
+ case CHECK_EXC_MATCH:
+ return 2;
+ case IMPORT_NAME:
+ return 1;
+ case IMPORT_FROM:
+ return 2;
+ case JUMP_FORWARD:
+ return 0;
+ case JUMP_BACKWARD:
+ return 0;
+ case POP_JUMP_IF_FALSE:
+ return -1;
+ case POP_JUMP_IF_TRUE:
+ return -1;
+ case POP_JUMP_IF_NOT_NONE:
+ return -1;
+ case POP_JUMP_IF_NONE:
+ return -1;
+ case JUMP_IF_FALSE_OR_POP:
+ return -1;
+ case JUMP_IF_TRUE_OR_POP:
+ return -1;
+ case JUMP_BACKWARD_NO_INTERRUPT:
+ return -1;
+ case GET_LEN:
+ return -1;
+ case MATCH_CLASS:
+ return 1;
+ case MATCH_MAPPING:
+ return 2;
+ case MATCH_SEQUENCE:
+ return 2;
+ case MATCH_KEYS:
+ return 3;
+ case GET_ITER:
+ return -1;
+ case GET_YIELD_FROM_ITER:
+ return -1;
+ case FOR_ITER:
+ return -1;
+ case FOR_ITER_LIST:
+ return -1;
+ case FOR_ITER_TUPLE:
+ return -1;
+ case FOR_ITER_RANGE:
+ return -1;
+ case FOR_ITER_GEN:
+ return -1;
+ case BEFORE_ASYNC_WITH:
+ return -1;
+ case BEFORE_WITH:
+ return -1;
+ case WITH_EXCEPT_START:
+ return 5;
+ case PUSH_EXC_INFO:
+ return -1;
+ case LOAD_ATTR_METHOD_WITH_VALUES:
+ return -1;
+ case LOAD_ATTR_METHOD_NO_DICT:
+ return -1;
+ case LOAD_ATTR_METHOD_LAZY_DICT:
+ return -1;
+ case CALL_BOUND_METHOD_EXACT_ARGS:
+ return -1;
+ case KW_NAMES:
+ return -1;
+ case CALL:
+ return -1;
+ case CALL_PY_EXACT_ARGS:
+ return -1;
+ case CALL_PY_WITH_DEFAULTS:
+ return -1;
+ case CALL_NO_KW_TYPE_1:
+ return -1;
+ case CALL_NO_KW_STR_1:
+ return -1;
+ case CALL_NO_KW_TUPLE_1:
+ return -1;
+ case CALL_BUILTIN_CLASS:
+ return -1;
+ case CALL_NO_KW_BUILTIN_O:
+ return -1;
+ case CALL_NO_KW_BUILTIN_FAST:
+ return -1;
+ case CALL_BUILTIN_FAST_WITH_KEYWORDS:
+ return -1;
+ case CALL_NO_KW_LEN:
+ return -1;
+ case CALL_NO_KW_ISINSTANCE:
+ return -1;
+ case CALL_NO_KW_LIST_APPEND:
+ return -1;
+ case CALL_NO_KW_METHOD_DESCRIPTOR_O:
+ return -1;
+ case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS:
+ return -1;
+ case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS:
+ return -1;
+ case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
+ return -1;
+ case CALL_FUNCTION_EX:
+ return -1;
+ case MAKE_FUNCTION:
+ return -1;
+ case RETURN_GENERATOR:
+ return -1;
+ case BUILD_SLICE:
+ return -1;
+ case FORMAT_VALUE:
+ return -1;
+ case COPY:
+ return -1;
+ case BINARY_OP:
+ return 1;
+ case SWAP:
+ return -1;
+ case EXTENDED_ARG:
+ return -1;
+ case CACHE:
+ return -1;
+ default:
+ Py_UNREACHABLE();
+ }
+}
+#endif
+enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };
+enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 };
+struct opcode_metadata {
+ enum Direction dir_op1;
+ enum Direction dir_op2;
+ enum Direction dir_op3;
+ bool valid_entry;
+ enum InstructionFormat instr_format;
+} _PyOpcode_opcode_metadata[256] = {
+ [NOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [RESUME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_FAST_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
+ [LOAD_FAST__LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
+ [STORE_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
+ [STORE_FAST__STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
+ [LOAD_CONST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
+ [POP_TOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [PUSH_NULL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [END_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [UNARY_NEGATIVE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [UNARY_NOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [UNARY_INVERT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BINARY_OP_MULTIPLY_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_MULTIPLY_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_SUBTRACT_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_SUBTRACT_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_ADD_INT_TYPE_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_ADD_INST_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_ADD_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
+ [BINARY_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [STORE_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BINARY_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
+ [BINARY_SUBSCR_TUPLE_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
+ [BINARY_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
+ [BINARY_SUBSCR_GETITEM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
+ [LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [SET_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [STORE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [STORE_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [STORE_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [DELETE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [CALL_INTRINSIC_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [RAISE_VARARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [INTERPRETER_EXIT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [RETURN_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [PREP_RERAISE_STAR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [END_ASYNC_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [CLEANUP_THROW] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [LOAD_ASSERTION_ERROR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [LOAD_BUILD_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [STORE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
+ [DELETE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [STORE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [DELETE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_GLOBAL_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_GLOBAL_BUILTIN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [DELETE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [MAKE_CELL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [DELETE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_CLASSDEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [STORE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [COPY_FREE_VARS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BUILD_STRING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BUILD_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BUILD_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LIST_EXTEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [SET_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BUILD_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BUILD_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [SETUP_ANNOTATIONS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BUILD_CONST_KEY_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [DICT_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [DICT_MERGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [MAP_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_ATTR_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_ATTR_PROPERTY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [STORE_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
+ [STORE_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
+ [STORE_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
+ [COMPARE_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
+ [COMPARE_AND_BRANCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
+ [COMPARE_AND_BRANCH_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
+ [COMPARE_AND_BRANCH_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
+ [COMPARE_AND_BRANCH_STR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
+ [IS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CONTAINS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CHECK_EG_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [CHECK_EXC_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [IMPORT_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [IMPORT_FROM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [JUMP_FORWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [JUMP_BACKWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [POP_JUMP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [POP_JUMP_IF_TRUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [POP_JUMP_IF_NOT_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [POP_JUMP_IF_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [JUMP_IF_FALSE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [JUMP_IF_TRUE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [JUMP_BACKWARD_NO_INTERRUPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [GET_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [MATCH_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [MATCH_MAPPING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [MATCH_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [MATCH_KEYS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [WITH_EXCEPT_START] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [PUSH_EXC_INFO] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [LOAD_ATTR_METHOD_WITH_VALUES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [LOAD_ATTR_METHOD_NO_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [LOAD_ATTR_METHOD_LAZY_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [KW_NAMES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_PY_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_PY_WITH_DEFAULTS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_TYPE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_STR_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_TUPLE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_BUILTIN_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_BUILTIN_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_BUILTIN_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_ISINSTANCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CALL_FUNCTION_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [MAKE_FUNCTION] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [RETURN_GENERATOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BUILD_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [FORMAT_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [COPY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BINARY_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
+ [SWAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [EXTENDED_ARG] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [CACHE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+};
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 9d894d2ff57455..0487c6ac02839d 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -12,6 +12,8 @@
import sys
import typing
+from enum import Enum, auto
+
import parser
from parser import StackEffect
@@ -20,10 +22,19 @@
THIS = os.path.relpath(__file__, ROOT)
DEFAULT_INPUT = os.path.relpath(os.path.join(ROOT, "Python/bytecodes.c"))
+
+# Tier 1 interpreter
DEFAULT_OUTPUT = os.path.relpath(os.path.join(ROOT, "Python/generated_cases.c.h"))
DEFAULT_METADATA_OUTPUT = os.path.relpath(
os.path.join(ROOT, "Python/opcode_metadata.h")
)
+
+# Tier 2 interpreter
+TIER2_OUTPUT = os.path.relpath(os.path.join(ROOT, "Python/generated_cases_tier2.c.h"))
+TIER2_METADATA_OUTPUT = os.path.relpath(
+ os.path.join(ROOT, "Python/opcode_metadata_tier2.h")
+)
+
BEGIN_MARKER = "// BEGIN BYTECODES //"
END_MARKER = "// END BYTECODES //"
RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*(?://.*)?$"
@@ -48,6 +59,11 @@
)
+class InterpreterTier(Enum):
+ TIER1 = auto()
+ TIER2 = auto()
+
+
def effect_size(effect: StackEffect) -> tuple[int, str]:
"""Return the 'size' impact of a stack effect.
@@ -102,17 +118,19 @@ class Formatter:
stream: typing.TextIO
prefix: str
+ postfix: str
def __init__(self, stream: typing.TextIO, indent: int) -> None:
self.stream = stream
self.prefix = " " * indent
+ self.postfix = ""
def write_raw(self, s: str) -> None:
self.stream.write(s)
def emit(self, arg: str) -> None:
if arg:
- self.write_raw(f"{self.prefix}{arg}\n")
+ self.write_raw(f"{self.prefix}{arg}{self.postfix}\n")
else:
self.write_raw("\n")
@@ -185,7 +203,7 @@ class Instruction:
# Parts of the underlying instruction definition
inst: parser.InstDef
register: bool
- kind: typing.Literal["inst", "op", "legacy"] # Legacy means no (input -- output)
+ kind: parser.INST_KINDS
name: str
block: parser.Block
block_text: list[str] # Block.text, less curlies, less PREDICT() calls
@@ -377,7 +395,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
f"{extra}{space}if ({cond}) {{ STACK_SHRINK({symbolic}); goto {label}; }}\n"
)
else:
- out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n")
+ out.write_raw(f"{extra}{space}if ({cond}) goto {label};{out.postfix}\n")
elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*(?://.*)?$", line):
if not self.register:
space = m.group(1)
@@ -385,7 +403,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
if ieff.name not in self.unmoved_names:
out.write_raw(f"{extra}{space}Py_DECREF({ieff.name});\n")
else:
- out.write_raw(extra + line)
+ out.write_raw(extra + line.rstrip("\n") + out.postfix + "\n")
InstructionOrCacheEffect = Instruction | parser.CacheEffect
@@ -446,13 +464,15 @@ class Analyzer:
filename: str
output_filename: str
+ tier: InterpreterTier
src: str
errors: int = 0
- def __init__(self, filename: str, output_filename: str):
+ def __init__(self, filename: str, output_filename: str, tier: InterpreterTier):
"""Read the input file."""
self.filename = filename
self.output_filename = output_filename
+ self.tier = tier
with open(filename) as f:
self.src = f.read()
@@ -897,12 +917,32 @@ def write_instructions(self) -> None:
n_instrs = 0
n_supers = 0
n_macros = 0
+
+ # Single pass to hoist all the u_instructions to the top.
+ if self.tier == InterpreterTier.TIER1:
+ for thing in self.everything:
+ match thing:
+ case parser.InstDef():
+ if thing.kind == "u_inst":
+ self.write_u_inst_as_c_macro(
+ self.instrs[thing.name])
+ case _:
+ pass
+
+ # Everything else
for thing in self.everything:
match thing:
case parser.InstDef():
- if thing.kind != "op":
- n_instrs += 1
- self.write_instr(self.instrs[thing.name])
+ match thing.kind:
+ case "op":
+ pass
+ case "u_inst" if self.tier != InterpreterTier.TIER2:
+ pass
+ case "macro_inst" if self.tier != InterpreterTier.TIER1:
+ pass
+ case _:
+ n_instrs += 1
+ self.write_instr(self.instrs[thing.name])
case parser.Super():
n_supers += 1
self.write_super(self.super_instrs[thing.name])
@@ -930,6 +970,16 @@ def write_instr(self, instr: Instruction) -> None:
self.out.emit(f"PREDICT({prediction});")
self.out.emit(f"DISPATCH();")
+ def write_u_inst_as_c_macro(self, instr: Instruction) -> None:
+ name = instr.name
+ self.out.emit("")
+ self.out.emit(f"#define {name}() \\")
+ self.out.emit("do { \\")
+ self.out.postfix = "\\"
+ instr.write_body(self.out, 0)
+ self.out.postfix = ""
+ self.out.emit("} while (0)")
+
def write_super(self, sup: SuperInstruction) -> None:
"""Write code for a super-instruction."""
with self.wrap_super_or_macro(sup):
@@ -1038,22 +1088,34 @@ def variable_used(node: parser.Node, name: str) -> bool:
return any(token.kind == "IDENTIFIER" and token.text == name for token in node.tokens)
-def main():
- """Parse command line, parse input, analyze, write output."""
- args = arg_parser.parse_args() # Prints message and sys.exit(2) on error
- if args.metadata:
- if args.output == DEFAULT_OUTPUT:
- args.output = DEFAULT_METADATA_OUTPUT
- a = Analyzer(args.input, args.output) # Raises OSError if input unreadable
+def generate_interpreter(input_: str, output: str, metadata: bool,
+ tier: InterpreterTier) -> None:
+ if metadata:
+ if output == DEFAULT_OUTPUT:
+ output = DEFAULT_METADATA_OUTPUT
+ elif output == TIER2_OUTPUT:
+ output = TIER2_METADATA_OUTPUT
+ a = Analyzer(input_, output, tier) # Raises OSError if input unreadable
a.parse() # Raises SyntaxError on failure
a.analyze() # Prints messages and sets a.errors on failure
if a.errors:
sys.exit(f"Found {a.errors} errors")
- if args.metadata:
+ if metadata:
a.write_metadata()
else:
a.write_instructions() # Raises OSError if output can't be written
+def main():
+ """Parse command line, parse input, analyze, write output."""
+ args = arg_parser.parse_args() # Prints message and sys.exit(2) on error
+
+ # Tier 1 interpreter
+ generate_interpreter(args.input, args.output, args.metadata,
+ InterpreterTier.TIER1)
+ # Tier 2 interpreter
+ generate_interpreter(args.input, TIER2_OUTPUT, args.metadata,
+ InterpreterTier.TIER2)
+
if __name__ == "__main__":
main()
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index c2cebe96ccd6be..b94ef9f24a9eb8 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -1,7 +1,7 @@
"""Parser for bytecodes.inst."""
from dataclasses import dataclass, field
-from typing import NamedTuple, Callable, TypeVar, Literal
+from typing import NamedTuple, Callable, TypeVar, Literal, get_args, TypeAlias
import lexer as lx
from plexer import PLexer
@@ -100,10 +100,30 @@ class OpName(Node):
UOp = OpName | CacheEffect
+# Note: A mapping of macro_inst -> u_inst+ is created later.
+INST_KINDS: TypeAlias = Literal[
+ # Legacy means no (inputs -- outputs)
+ "legacy",
+ # This generates an instruction definition in the tier 1 and 2 interpreter.
+ "inst",
+ # This is a pseudo instruction used only internally by the cases generator.
+ "op",
+ # This generates an instruction definition strictly only in the
+ # tier 1 interpreter.
+ "macro_inst",
+ # This generates an instruction definition strictly only in the
+ # tier 2 interpreter.
+ "u_inst",
+]
+
+# Remove legacy
+INST_LABELS: tuple[INST_KINDS] = get_args(INST_KINDS)[1:]
+
+
@dataclass
class InstHeader(Node):
register: bool
- kind: Literal["inst", "op", "legacy"] # Legacy means no (inputs -- outputs)
+ kind: INST_KINDS
name: str
inputs: list[InputEffect]
outputs: list[OutputEffect]
@@ -112,7 +132,7 @@ class InstHeader(Node):
@dataclass
class InstDef(Node):
register: bool
- kind: Literal["inst", "op", "legacy"]
+ kind: INST_KINDS
name: str
inputs: list[InputEffect]
outputs: list[OutputEffect]
@@ -167,7 +187,7 @@ def inst_header(self) -> InstHeader | None:
# | [register] op(NAME, (inputs -- outputs))
# TODO: Make INST a keyword in the lexer.
register = bool(self.expect(lx.REGISTER))
- if (tkn := self.expect(lx.IDENTIFIER)) and (kind := tkn.text) in ("inst", "op"):
+ if (tkn := self.expect(lx.IDENTIFIER)) and (kind := tkn.text) in INST_LABELS:
if self.expect(lx.LPAREN) and (tkn := self.expect(lx.IDENTIFIER)):
name = tkn.text
if self.expect(lx.COMMA):
From 749dfc491600c69dcd8576c4b7047b5e8fc55335 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 28 Jan 2023 00:47:38 +0800
Subject: [PATCH 002/280] fix typo and formatting
---
Python/bytecodes.c | 4 ++--
Python/generated_cases.c.h | 2 +-
Python/generated_cases_tier2.c.h | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index d8532fc0d0c3b4..eb1faa80514e18 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -284,13 +284,13 @@ dummy_func(
ERROR_IF(sum == NULL, error);
}
- u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (unused / 1, left, right -- left, right)) {
+ u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (unused/1, left, right -- left, right)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
}
- u_inst(BINARY_OP_ADD_INST_REST, (unused / 1, left, right -- sum)) {
+ u_inst(BINARY_OP_ADD_INT_REST, (unused/1, left, right -- sum)) {
STAT_INC(BINARY_OP, hit);
sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index c3b455177e33e3..de49b157b57d6f 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -9,7 +9,7 @@
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);\
} while (0)
- #define BINARY_OP_ADD_INST_REST() \
+ #define BINARY_OP_ADD_INT_REST() \
do { \
STAT_INC(BINARY_OP, hit);\
sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);\
diff --git a/Python/generated_cases_tier2.c.h b/Python/generated_cases_tier2.c.h
index 91f4265c139aad..3a7b35f2c89f4b 100644
--- a/Python/generated_cases_tier2.c.h
+++ b/Python/generated_cases_tier2.c.h
@@ -396,7 +396,7 @@
DISPATCH();
}
- TARGET(BINARY_OP_ADD_INST_REST) {
+ TARGET(BINARY_OP_ADD_INT_REST) {
PyObject *right = PEEK(1);
PyObject *left = PEEK(2);
PyObject *sum;
From 069f0f53941dbe72439ab3cef6845d9b885929c1 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 28 Jan 2023 02:23:55 +0800
Subject: [PATCH 003/280] Create a mapping from macroop to uop
---
Python/bytecodes.c | 2 +
Python/opcode_macro_to_micro.h | 6 +++
Python/opcode_metadata.h | 12 ++++-
Python/opcode_metadata_tier2.h | 7 ++-
Tools/cases_generator/generate_cases.py | 65 +++++++++++++++++++++++--
Tools/cases_generator/parser.py | 31 +++++++++++-
6 files changed, 111 insertions(+), 12 deletions(-)
create mode 100644 Python/opcode_macro_to_micro.h
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index eb1faa80514e18..d187e0f24c3416 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -303,6 +303,8 @@ dummy_func(
BINARY_OP_ADD_INST_REST();
}
+ uop_map(BINARY_OP_ADD_INT) = BINARY_OP_ADD_INT_TYPE_CHECK + BINARY_OP_ADD_INT_REST;
+
family(binary_subscr, INLINE_CACHE_ENTRIES_BINARY_SUBSCR) = {
BINARY_SUBSCR,
BINARY_SUBSCR_DICT,
diff --git a/Python/opcode_macro_to_micro.h b/Python/opcode_macro_to_micro.h
new file mode 100644
index 00000000000000..22134e6ab7b91c
--- /dev/null
+++ b/Python/opcode_macro_to_micro.h
@@ -0,0 +1,6 @@
+// This file is generated by Tools\cases_generator\generate_cases.py --uopmap
+// from Python\bytecodes.c
+// Do not edit!
+static int _Py_MacroOp_To_UOp[][2] = {
+[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_TYPE_CHECK, BINARY_OP_ADD_INT_REST},
+}
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index cca86629e48d16..3fc45b25de927e 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -1,5 +1,5 @@
-// This file is generated by Tools/cases_generator/generate_cases.py --metadata
-// from Python/bytecodes.c
+// This file is generated by Tools\cases_generator\generate_cases.py --metadata
+// from Python\bytecodes.c
// Do not edit!
#ifndef NDEBUG
@@ -56,6 +56,10 @@ _PyOpcode_num_popped(int opcode, int oparg) {
return 2;
case BINARY_OP_ADD_FLOAT:
return 2;
+ case BINARY_OP_ADD_INT_TYPE_CHECK:
+ return 2;
+ case BINARY_OP_ADD_INT_REST:
+ return 2;
case BINARY_OP_ADD_INT:
return 2;
case BINARY_SUBSCR:
@@ -402,6 +406,10 @@ _PyOpcode_num_pushed(int opcode, int oparg) {
return 0;
case BINARY_OP_ADD_FLOAT:
return 1;
+ case BINARY_OP_ADD_INT_TYPE_CHECK:
+ return 2;
+ case BINARY_OP_ADD_INT_REST:
+ return 1;
case BINARY_OP_ADD_INT:
return 1;
case BINARY_SUBSCR:
diff --git a/Python/opcode_metadata_tier2.h b/Python/opcode_metadata_tier2.h
index d7f86246e8f571..bf9170e99cf754 100644
--- a/Python/opcode_metadata_tier2.h
+++ b/Python/opcode_metadata_tier2.h
@@ -58,7 +58,7 @@ _PyOpcode_num_popped(int opcode, int oparg) {
return 2;
case BINARY_OP_ADD_INT_TYPE_CHECK:
return 2;
- case BINARY_OP_ADD_INST_REST:
+ case BINARY_OP_ADD_INT_REST:
return 2;
case BINARY_OP_ADD_INT:
return 2;
@@ -408,7 +408,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) {
return 1;
case BINARY_OP_ADD_INT_TYPE_CHECK:
return 2;
- case BINARY_OP_ADD_INST_REST:
+ case BINARY_OP_ADD_INT_REST:
return 1;
case BINARY_OP_ADD_INT:
return 1;
@@ -736,8 +736,7 @@ struct opcode_metadata {
[BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[BINARY_OP_ADD_INT_TYPE_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_ADD_INST_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_ADD_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_ADD_INT_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[BINARY_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
[BINARY_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[STORE_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 0487c6ac02839d..d2187fa52105e2 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -34,6 +34,9 @@
TIER2_METADATA_OUTPUT = os.path.relpath(
os.path.join(ROOT, "Python/opcode_metadata_tier2.h")
)
+TIER2_MACRO_TO_MICRO_MAP_OUTPUT = os.path.relpath(
+ os.path.join(ROOT, "Python/opcode_macro_to_micro.h")
+)
BEGIN_MARKER = "// BEGIN BYTECODES //"
END_MARKER = "// END BYTECODES //"
@@ -58,6 +61,14 @@
help=f"Generate metadata instead, changes output default to {DEFAULT_METADATA_OUTPUT}",
)
+arg_parser.add_argument(
+ "-u",
+ "--uopmap",
+ action="store_true",
+ help=f"Generate macro to micro instruction map instead,"
+ f"changes output default to {TIER2_MACRO_TO_MICRO_MAP_OUTPUT}",
+)
+
class InterpreterTier(Enum):
TIER1 = auto()
@@ -493,6 +504,7 @@ def error(self, msg: str, node: parser.Node) -> None:
super_instrs: dict[str, SuperInstruction]
macros: dict[str, parser.Macro]
macro_instrs: dict[str, MacroInstruction]
+ macro_map: dict[str, parser.UOpMap]
families: dict[str, parser.Family]
def parse(self) -> None:
@@ -525,6 +537,7 @@ def parse(self) -> None:
self.instrs = {}
self.supers = {}
self.macros = {}
+ self.macro_map = {}
self.families = {}
while thing := psr.definition():
match thing:
@@ -537,6 +550,8 @@ def parse(self) -> None:
case parser.Macro(name):
self.macros[name] = thing
self.everything.append(thing)
+ case parser.UOpMap(name):
+ self.macro_map[name] = thing
case parser.Family(name):
self.families[name] = thing
case _:
@@ -639,6 +654,8 @@ def analyze_supers_and_macros(self) -> None:
self.super_instrs[name] = self.analyze_super(super)
for name, macro in self.macros.items():
self.macro_instrs[name] = self.analyze_macro(macro)
+ for name, uopmap in self.macro_map.items():
+ self.analyze_uopmap(uopmap)
def analyze_super(self, super: parser.Super) -> SuperInstruction:
components = self.check_super_components(super)
@@ -679,6 +696,15 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction:
final_sp = sp
return MacroInstruction(macro.name, stack, initial_sp, final_sp, format, macro, parts)
+ def analyze_uopmap(self, uopmap: parser.UOpMap):
+ self.check_uopmap_components(uopmap)
+
+ def check_uopmap_components(self, uopmap: parser.UOpMap) -> None:
+ components: list[InstructionOrCacheEffect] = []
+ for uop in uopmap.u_insts:
+ if uop not in self.instrs:
+ self.error(f"Unknown instruction {uop!r}", uop)
+
def analyze_instruction(
self, instr: Instruction, stack: list[StackEffect], sp: int
) -> tuple[Component, int]:
@@ -814,6 +840,22 @@ def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None:
write_function('popped', popped_data)
write_function('pushed', pushed_data)
+ def write_uopmap(self):
+ """Write the macro instruction to uop mapping to output file."""
+ with open(self.output_filename, "w") as f:
+ # Write provenance header
+ f.write(f"// This file is generated by {THIS} --uopmap\n")
+ f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n")
+ f.write(f"// Do not edit!\n")
+
+ # Create formatter; the rest of the code uses this
+ self.out = Formatter(f, 0)
+ self.out.emit("static int _Py_MacroOp_To_UOp[][2] = {")
+ for name, uopmap in self.macro_map.items():
+ u_insts = uopmap.u_insts
+ self.out.emit(f"[{name}] = {{{u_insts[0]}, {u_insts[1]}}},")
+ self.out.emit("}")
+
def write_metadata(self) -> None:
"""Write instruction metadata to output file."""
@@ -827,6 +869,8 @@ def write_metadata(self) -> None:
format = self.super_instrs[thing.name].instr_fmt
case parser.Macro():
format = self.macro_instrs[thing.name].instr_fmt
+ case parser.UOpMap():
+ pass
case _:
typing.assert_never(thing)
all_formats.add(format)
@@ -860,8 +904,12 @@ def write_metadata(self) -> None:
for thing in self.everything:
match thing:
case parser.InstDef():
- if thing.kind != "op":
- self.write_metadata_for_inst(self.instrs[thing.name])
+ if thing.kind == "op":
+ continue
+ if ((thing.kind == "macro_inst" and self.tier != InterpreterTier.TIER1)
+ or (thing.kind == "u_inst" and self.tier != InterpreterTier.TIER2)):
+ continue
+ self.write_metadata_for_inst(self.instrs[thing.name])
case parser.Super():
self.write_metadata_for_super(self.super_instrs[thing.name])
case parser.Macro():
@@ -949,6 +997,8 @@ def write_instructions(self) -> None:
case parser.Macro():
n_macros += 1
self.write_macro(self.macro_instrs[thing.name])
+ case parser.UOpMap():
+ pass
case _:
typing.assert_never(thing)
@@ -1088,8 +1138,10 @@ def variable_used(node: parser.Node, name: str) -> bool:
return any(token.kind == "IDENTIFIER" and token.text == name for token in node.tokens)
-def generate_interpreter(input_: str, output: str, metadata: bool,
+def generate_interpreter(input_: str, output: str, metadata: bool, uopmap: bool,
tier: InterpreterTier) -> None:
+ if tier == InterpreterTier.TIER2 and uopmap:
+ output = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
if metadata:
if output == DEFAULT_OUTPUT:
output = DEFAULT_METADATA_OUTPUT
@@ -1100,6 +1152,9 @@ def generate_interpreter(input_: str, output: str, metadata: bool,
a.analyze() # Prints messages and sets a.errors on failure
if a.errors:
sys.exit(f"Found {a.errors} errors")
+ if uopmap:
+ a.write_uopmap()
+ return
if metadata:
a.write_metadata()
else:
@@ -1110,10 +1165,10 @@ def main():
args = arg_parser.parse_args() # Prints message and sys.exit(2) on error
# Tier 1 interpreter
- generate_interpreter(args.input, args.output, args.metadata,
+ generate_interpreter(args.input, args.output, args.metadata, False,
InterpreterTier.TIER1)
# Tier 2 interpreter
- generate_interpreter(args.input, TIER2_OUTPUT, args.metadata,
+ generate_interpreter(args.input, TIER2_OUTPUT, args.metadata, args.uopmap,
InterpreterTier.TIER2)
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index b94ef9f24a9eb8..f4524095075b75 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -150,6 +150,11 @@ class Macro(Node):
name: str
uops: list[UOp]
+@dataclass
+class UOpMap(Node):
+ name: str
+ u_insts: list[str]
+
@dataclass
class Family(Node):
@@ -160,13 +165,15 @@ class Family(Node):
class Parser(PLexer):
@contextual
- def definition(self) -> InstDef | Super | Macro | Family | None:
+ def definition(self) -> InstDef | Super | Macro | UOpMap | Family | None:
if inst := self.inst_def():
return inst
if super := self.super_def():
return super
if macro := self.macro_def():
return macro
+ if uopmap := self.uopmap_def():
+ return uopmap
if family := self.family_def():
return family
@@ -349,6 +356,28 @@ def uop(self) -> UOp | None:
else:
return OpName(tkn.text)
+ @contextual
+ def uopmap_def(self) -> UOpMap | None:
+ if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text == "uop_map":
+ if self.expect(lx.LPAREN):
+ if tkn := self.expect(lx.IDENTIFIER):
+ if self.expect(lx.RPAREN):
+ if self.expect(lx.EQUALS):
+ if u_insts := self.u_insts():
+ self.require(lx.SEMI)
+ res = UOpMap(tkn.text, u_insts)
+ return res
+
+ def u_insts(self) -> list[str] | None:
+ if tkn := self.expect(lx.IDENTIFIER):
+ u_insts = [tkn.text]
+ while self.expect(lx.PLUS):
+ if tkn := self.expect(lx.IDENTIFIER):
+ u_insts.append(tkn.text)
+ else:
+ raise self.make_syntax_error("Expected op name")
+ return u_insts
+
@contextual
def family_def(self) -> Family | None:
if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text == "family":
From 688cc5752d0a03f5b8905bc7908905fe0af55db5 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 28 Jan 2023 15:10:33 +0800
Subject: [PATCH 004/280] Auto detect micros without manual definition
---
Python/bytecodes.c | 6 +--
Python/generated_cases.c.h | 2 +-
Python/opcode_macro_to_micro.h | 4 +-
Tools/cases_generator/generate_cases.py | 56 ++++++++++++-------------
Tools/cases_generator/parser.py | 32 +++++---------
5 files changed, 41 insertions(+), 59 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index d187e0f24c3416..07625a5bb17bc6 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -299,12 +299,10 @@ dummy_func(
}
macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
- BINARY_OP_ADD_INT_TYPE_CHECK();
- BINARY_OP_ADD_INST_REST();
+ U_INST(BINARY_OP_ADD_INT_TYPE_CHECK);
+ U_INST(BINARY_OP_ADD_INT_REST);
}
- uop_map(BINARY_OP_ADD_INT) = BINARY_OP_ADD_INT_TYPE_CHECK + BINARY_OP_ADD_INT_REST;
-
family(binary_subscr, INLINE_CACHE_ENTRIES_BINARY_SUBSCR) = {
BINARY_SUBSCR,
BINARY_SUBSCR_DICT,
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index de49b157b57d6f..85d44e9dc1b9ca 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -407,7 +407,7 @@
PyObject *left = PEEK(2);
PyObject *sum;
BINARY_OP_ADD_INT_TYPE_CHECK();
- BINARY_OP_ADD_INST_REST();
+ BINARY_OP_ADD_INT_REST();
STACK_SHRINK(1);
POKE(1, sum);
JUMPBY(1);
diff --git a/Python/opcode_macro_to_micro.h b/Python/opcode_macro_to_micro.h
index 22134e6ab7b91c..4029b5d261b129 100644
--- a/Python/opcode_macro_to_micro.h
+++ b/Python/opcode_macro_to_micro.h
@@ -1,6 +1,6 @@
-// This file is generated by Tools\cases_generator\generate_cases.py --uopmap
+// This file is generated by Tools\cases_generator\generate_cases.py --macromap
// from Python\bytecodes.c
// Do not edit!
-static int _Py_MacroOp_To_UOp[][2] = {
+static int _Py_MacroOpToUOp[][2] = {
[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_TYPE_CHECK, BINARY_OP_ADD_INT_REST},
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index d2187fa52105e2..aa0a7ddb3b5fdf 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -63,7 +63,7 @@
arg_parser.add_argument(
"-u",
- "--uopmap",
+ "--macromap",
action="store_true",
help=f"Generate macro to micro instruction map instead,"
f"changes output default to {TIER2_MACRO_TO_MICRO_MAP_OUTPUT}",
@@ -383,7 +383,10 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
assert dedent <= 0
extra = " " * -dedent
for line in self.block_text:
- if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*(?://.*)?$", line):
+ if m := re.match(r"(\s*)U_INST\((.+)\);\s*$", line):
+ space, label = m.groups()
+ out.emit(f"{label}();")
+ elif m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*(?://.*)?$", line):
space, cond, label = m.groups()
# ERROR_IF() must pop the inputs from the stack.
# The code block is responsible for DECREF()ing them.
@@ -504,7 +507,7 @@ def error(self, msg: str, node: parser.Node) -> None:
super_instrs: dict[str, SuperInstruction]
macros: dict[str, parser.Macro]
macro_instrs: dict[str, MacroInstruction]
- macro_map: dict[str, parser.UOpMap]
+ macro_instdefs: list[parser.InstDef]
families: dict[str, parser.Family]
def parse(self) -> None:
@@ -537,21 +540,22 @@ def parse(self) -> None:
self.instrs = {}
self.supers = {}
self.macros = {}
- self.macro_map = {}
+ self.macro_instrs = {}
+ self.macro_instdefs = []
self.families = {}
while thing := psr.definition():
match thing:
case parser.InstDef(name=name):
self.instrs[name] = Instruction(thing)
self.everything.append(thing)
+ if thing.kind == "macro_inst":
+ self.macro_instdefs.append(thing)
case parser.Super(name):
self.supers[name] = thing
self.everything.append(thing)
case parser.Macro(name):
self.macros[name] = thing
self.everything.append(thing)
- case parser.UOpMap(name):
- self.macro_map[name] = thing
case parser.Family(name):
self.families[name] = thing
case _:
@@ -654,8 +658,8 @@ def analyze_supers_and_macros(self) -> None:
self.super_instrs[name] = self.analyze_super(super)
for name, macro in self.macros.items():
self.macro_instrs[name] = self.analyze_macro(macro)
- for name, uopmap in self.macro_map.items():
- self.analyze_uopmap(uopmap)
+ for macro_instdef in self.macro_instdefs:
+ self.analyze_macro_instdefs(macro_instdef)
def analyze_super(self, super: parser.Super) -> SuperInstruction:
components = self.check_super_components(super)
@@ -696,14 +700,10 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction:
final_sp = sp
return MacroInstruction(macro.name, stack, initial_sp, final_sp, format, macro, parts)
- def analyze_uopmap(self, uopmap: parser.UOpMap):
- self.check_uopmap_components(uopmap)
-
- def check_uopmap_components(self, uopmap: parser.UOpMap) -> None:
- components: list[InstructionOrCacheEffect] = []
- for uop in uopmap.u_insts:
+ def analyze_macro_instdefs(self, macro_def: parser.InstDef):
+ for uop in macro_def.u_insts:
if uop not in self.instrs:
- self.error(f"Unknown instruction {uop!r}", uop)
+ self.error(f"Unknown instruction {uop} in {macro_def!r}", macro_def)
def analyze_instruction(
self, instr: Instruction, stack: list[StackEffect], sp: int
@@ -840,20 +840,20 @@ def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None:
write_function('popped', popped_data)
write_function('pushed', pushed_data)
- def write_uopmap(self):
+ def write_macromap(self):
"""Write the macro instruction to uop mapping to output file."""
with open(self.output_filename, "w") as f:
# Write provenance header
- f.write(f"// This file is generated by {THIS} --uopmap\n")
+ f.write(f"// This file is generated by {THIS} --macromap\n")
f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n")
f.write(f"// Do not edit!\n")
# Create formatter; the rest of the code uses this
self.out = Formatter(f, 0)
- self.out.emit("static int _Py_MacroOp_To_UOp[][2] = {")
- for name, uopmap in self.macro_map.items():
- u_insts = uopmap.u_insts
- self.out.emit(f"[{name}] = {{{u_insts[0]}, {u_insts[1]}}},")
+ self.out.emit("static int _Py_MacroOpToUOp[][2] = {")
+ for macro_def in self.macro_instdefs:
+ u_insts = macro_def.u_insts
+ self.out.emit(f"[{macro_def.name}] = {{{u_insts[0]}, {u_insts[1]}}},")
self.out.emit("}")
def write_metadata(self) -> None:
@@ -869,8 +869,6 @@ def write_metadata(self) -> None:
format = self.super_instrs[thing.name].instr_fmt
case parser.Macro():
format = self.macro_instrs[thing.name].instr_fmt
- case parser.UOpMap():
- pass
case _:
typing.assert_never(thing)
all_formats.add(format)
@@ -997,8 +995,6 @@ def write_instructions(self) -> None:
case parser.Macro():
n_macros += 1
self.write_macro(self.macro_instrs[thing.name])
- case parser.UOpMap():
- pass
case _:
typing.assert_never(thing)
@@ -1138,9 +1134,9 @@ def variable_used(node: parser.Node, name: str) -> bool:
return any(token.kind == "IDENTIFIER" and token.text == name for token in node.tokens)
-def generate_interpreter(input_: str, output: str, metadata: bool, uopmap: bool,
+def generate_interpreter(input_: str, output: str, metadata: bool, macromap: bool,
tier: InterpreterTier) -> None:
- if tier == InterpreterTier.TIER2 and uopmap:
+ if tier == InterpreterTier.TIER2 and macromap:
output = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
if metadata:
if output == DEFAULT_OUTPUT:
@@ -1152,8 +1148,8 @@ def generate_interpreter(input_: str, output: str, metadata: bool, uopmap: bool,
a.analyze() # Prints messages and sets a.errors on failure
if a.errors:
sys.exit(f"Found {a.errors} errors")
- if uopmap:
- a.write_uopmap()
+ if macromap:
+ a.write_macromap()
return
if metadata:
a.write_metadata()
@@ -1168,7 +1164,7 @@ def main():
generate_interpreter(args.input, args.output, args.metadata, False,
InterpreterTier.TIER1)
# Tier 2 interpreter
- generate_interpreter(args.input, TIER2_OUTPUT, args.metadata, args.uopmap,
+ generate_interpreter(args.input, TIER2_OUTPUT, args.metadata, args.macromap,
InterpreterTier.TIER2)
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index f4524095075b75..fd0c0e4c2527db 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -3,6 +3,7 @@
from dataclasses import dataclass, field
from typing import NamedTuple, Callable, TypeVar, Literal, get_args, TypeAlias
+import re
import lexer as lx
from plexer import PLexer
@@ -137,6 +138,7 @@ class InstDef(Node):
inputs: list[InputEffect]
outputs: list[OutputEffect]
block: Block
+ u_insts: list[str]
@dataclass
@@ -150,12 +152,6 @@ class Macro(Node):
name: str
uops: list[UOp]
-@dataclass
-class UOpMap(Node):
- name: str
- u_insts: list[str]
-
-
@dataclass
class Family(Node):
name: str
@@ -165,15 +161,13 @@ class Family(Node):
class Parser(PLexer):
@contextual
- def definition(self) -> InstDef | Super | Macro | UOpMap | Family | None:
+ def definition(self) -> InstDef | Super | Macro | Family | None:
if inst := self.inst_def():
return inst
if super := self.super_def():
return super
if macro := self.macro_def():
return macro
- if uopmap := self.uopmap_def():
- return uopmap
if family := self.family_def():
return family
@@ -181,8 +175,14 @@ def definition(self) -> InstDef | Super | Macro | UOpMap | Family | None:
def inst_def(self) -> InstDef | None:
if hdr := self.inst_header():
if block := self.block():
+ u_insts = []
+ if hdr.kind == "macro_inst":
+ for line in block.text.splitlines():
+ if m := re.match(r"(\s*)U_INST\((.+)\);\s*$", line):
+ space, label = m.groups()
+ u_insts.append(label)
return InstDef(
- hdr.register, hdr.kind, hdr.name, hdr.inputs, hdr.outputs, block
+ hdr.register, hdr.kind, hdr.name, hdr.inputs, hdr.outputs, block, u_insts
)
raise self.make_syntax_error("Expected block")
return None
@@ -356,18 +356,6 @@ def uop(self) -> UOp | None:
else:
return OpName(tkn.text)
- @contextual
- def uopmap_def(self) -> UOpMap | None:
- if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text == "uop_map":
- if self.expect(lx.LPAREN):
- if tkn := self.expect(lx.IDENTIFIER):
- if self.expect(lx.RPAREN):
- if self.expect(lx.EQUALS):
- if u_insts := self.u_insts():
- self.require(lx.SEMI)
- res = UOpMap(tkn.text, u_insts)
- return res
-
def u_insts(self) -> list[str] | None:
if tkn := self.expect(lx.IDENTIFIER):
u_insts = [tkn.text]
From cc8e56a3adad64d077d06bff469079277d6c8095 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 28 Jan 2023 16:50:42 +0800
Subject: [PATCH 005/280] Move the macro to micro map to an internal header
---
.../internal/pycore_opcode_macro_to_micro.h | 188 ++++++++++++++++++
Python/bytecodes.c | 10 +-
Python/opcode_macro_to_micro.h | 6 -
Tools/cases_generator/generate_cases.py | 40 +++-
4 files changed, 230 insertions(+), 14 deletions(-)
create mode 100644 Include/internal/pycore_opcode_macro_to_micro.h
delete mode 100644 Python/opcode_macro_to_micro.h
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
new file mode 100644
index 00000000000000..def9dd5781e5a0
--- /dev/null
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -0,0 +1,188 @@
+// This file is generated by Tools\cases_generator\generate_cases.py --macromap
+// from Python\bytecodes.c
+// Do not edit!
+#ifndef Py_INTERNAL_OPCODE_MACRO_TO_MICRO
+#define Py_INTERNAL_OPCODE_MACRO_TO_MICRO
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+# error "this header requires Py_BUILD_CORE define"
+#endif
+#include "opcode.h"
+
+static int _Py_MacroOpUOpCount[] = {
+[NOP] = 1,
+[RESUME] = 1,
+[LOAD_CLOSURE] = 1,
+[LOAD_FAST_CHECK] = 1,
+[LOAD_FAST] = 1,
+[LOAD_CONST] = 1,
+[STORE_FAST] = 1,
+[POP_TOP] = 1,
+[PUSH_NULL] = 1,
+[UNARY_NEGATIVE] = 1,
+[UNARY_NOT] = 1,
+[UNARY_INVERT] = 1,
+[BINARY_OP_MULTIPLY_INT] = 1,
+[BINARY_OP_MULTIPLY_FLOAT] = 1,
+[BINARY_OP_SUBTRACT_INT] = 1,
+[BINARY_OP_SUBTRACT_FLOAT] = 1,
+[BINARY_OP_ADD_UNICODE] = 1,
+[BINARY_OP_INPLACE_ADD_UNICODE] = 1,
+[BINARY_OP_ADD_FLOAT] = 1,
+[BINARY_OP_ADD_INT] = 2,
+[BINARY_OP_ADD_INT_TYPE_CHECK] = 1,
+[BINARY_OP_ADD_INT_REST] = 1,
+[BINARY_SUBSCR] = 1,
+[BINARY_SLICE] = 1,
+[STORE_SLICE] = 1,
+[BINARY_SUBSCR_LIST_INT] = 1,
+[BINARY_SUBSCR_TUPLE_INT] = 1,
+[BINARY_SUBSCR_DICT] = 1,
+[BINARY_SUBSCR_GETITEM] = 1,
+[LIST_APPEND] = 1,
+[SET_ADD] = 1,
+[STORE_SUBSCR] = 1,
+[STORE_SUBSCR_LIST_INT] = 1,
+[STORE_SUBSCR_DICT] = 1,
+[DELETE_SUBSCR] = 1,
+[CALL_INTRINSIC_1] = 1,
+[RAISE_VARARGS] = 1,
+[INTERPRETER_EXIT] = 1,
+[RETURN_VALUE] = 1,
+[GET_AITER] = 1,
+[GET_ANEXT] = 1,
+[GET_AWAITABLE] = 1,
+[SEND] = 1,
+[YIELD_VALUE] = 1,
+[POP_EXCEPT] = 1,
+[RERAISE] = 1,
+[PREP_RERAISE_STAR] = 1,
+[END_ASYNC_FOR] = 1,
+[CLEANUP_THROW] = 1,
+[LOAD_ASSERTION_ERROR] = 1,
+[LOAD_BUILD_CLASS] = 1,
+[STORE_NAME] = 1,
+[DELETE_NAME] = 1,
+[UNPACK_SEQUENCE] = 1,
+[UNPACK_SEQUENCE_TWO_TUPLE] = 1,
+[UNPACK_SEQUENCE_TUPLE] = 1,
+[UNPACK_SEQUENCE_LIST] = 1,
+[UNPACK_EX] = 1,
+[STORE_ATTR] = 1,
+[DELETE_ATTR] = 1,
+[STORE_GLOBAL] = 1,
+[DELETE_GLOBAL] = 1,
+[LOAD_NAME] = 1,
+[LOAD_GLOBAL] = 1,
+[LOAD_GLOBAL_MODULE] = 1,
+[LOAD_GLOBAL_BUILTIN] = 1,
+[DELETE_FAST] = 1,
+[MAKE_CELL] = 1,
+[DELETE_DEREF] = 1,
+[LOAD_CLASSDEREF] = 1,
+[LOAD_DEREF] = 1,
+[STORE_DEREF] = 1,
+[COPY_FREE_VARS] = 1,
+[BUILD_STRING] = 1,
+[BUILD_TUPLE] = 1,
+[BUILD_LIST] = 1,
+[LIST_EXTEND] = 1,
+[SET_UPDATE] = 1,
+[BUILD_SET] = 1,
+[BUILD_MAP] = 1,
+[SETUP_ANNOTATIONS] = 1,
+[BUILD_CONST_KEY_MAP] = 1,
+[DICT_UPDATE] = 1,
+[DICT_MERGE] = 1,
+[MAP_ADD] = 1,
+[LOAD_ATTR] = 1,
+[LOAD_ATTR_INSTANCE_VALUE] = 1,
+[LOAD_ATTR_MODULE] = 1,
+[LOAD_ATTR_WITH_HINT] = 1,
+[LOAD_ATTR_SLOT] = 1,
+[LOAD_ATTR_CLASS] = 1,
+[LOAD_ATTR_PROPERTY] = 1,
+[LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = 1,
+[STORE_ATTR_INSTANCE_VALUE] = 1,
+[STORE_ATTR_WITH_HINT] = 1,
+[STORE_ATTR_SLOT] = 1,
+[COMPARE_OP] = 1,
+[COMPARE_AND_BRANCH] = 1,
+[COMPARE_AND_BRANCH_FLOAT] = 1,
+[COMPARE_AND_BRANCH_INT] = 1,
+[COMPARE_AND_BRANCH_STR] = 1,
+[IS_OP] = 1,
+[CONTAINS_OP] = 1,
+[CHECK_EG_MATCH] = 1,
+[CHECK_EXC_MATCH] = 1,
+[IMPORT_NAME] = 1,
+[IMPORT_FROM] = 1,
+[JUMP_FORWARD] = 1,
+[JUMP_BACKWARD] = 1,
+[POP_JUMP_IF_FALSE] = 1,
+[POP_JUMP_IF_TRUE] = 1,
+[POP_JUMP_IF_NOT_NONE] = 1,
+[POP_JUMP_IF_NONE] = 1,
+[JUMP_IF_FALSE_OR_POP] = 1,
+[JUMP_IF_TRUE_OR_POP] = 1,
+[JUMP_BACKWARD_NO_INTERRUPT] = 1,
+[GET_LEN] = 1,
+[MATCH_CLASS] = 1,
+[MATCH_MAPPING] = 1,
+[MATCH_SEQUENCE] = 1,
+[MATCH_KEYS] = 1,
+[GET_ITER] = 1,
+[GET_YIELD_FROM_ITER] = 1,
+[FOR_ITER] = 1,
+[FOR_ITER_LIST] = 1,
+[FOR_ITER_TUPLE] = 1,
+[FOR_ITER_RANGE] = 1,
+[FOR_ITER_GEN] = 1,
+[BEFORE_ASYNC_WITH] = 1,
+[BEFORE_WITH] = 1,
+[WITH_EXCEPT_START] = 1,
+[PUSH_EXC_INFO] = 1,
+[LOAD_ATTR_METHOD_WITH_VALUES] = 1,
+[LOAD_ATTR_METHOD_NO_DICT] = 1,
+[LOAD_ATTR_METHOD_LAZY_DICT] = 1,
+[CALL_BOUND_METHOD_EXACT_ARGS] = 1,
+[KW_NAMES] = 1,
+[CALL] = 1,
+[CALL_PY_EXACT_ARGS] = 1,
+[CALL_PY_WITH_DEFAULTS] = 1,
+[CALL_NO_KW_TYPE_1] = 1,
+[CALL_NO_KW_STR_1] = 1,
+[CALL_NO_KW_TUPLE_1] = 1,
+[CALL_BUILTIN_CLASS] = 1,
+[CALL_NO_KW_BUILTIN_O] = 1,
+[CALL_NO_KW_BUILTIN_FAST] = 1,
+[CALL_BUILTIN_FAST_WITH_KEYWORDS] = 1,
+[CALL_NO_KW_LEN] = 1,
+[CALL_NO_KW_ISINSTANCE] = 1,
+[CALL_NO_KW_LIST_APPEND] = 1,
+[CALL_NO_KW_METHOD_DESCRIPTOR_O] = 1,
+[CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = 1,
+[CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = 1,
+[CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = 1,
+[CALL_FUNCTION_EX] = 1,
+[MAKE_FUNCTION] = 1,
+[RETURN_GENERATOR] = 1,
+[BUILD_SLICE] = 1,
+[FORMAT_VALUE] = 1,
+[COPY] = 1,
+[BINARY_OP] = 1,
+[SWAP] = 1,
+[EXTENDED_ARG] = 1,
+[CACHE] = 1,
+}
+
+static int _Py_MacroOpToUOp[][2] = {
+[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_TYPE_CHECK, BINARY_OP_ADD_INT_REST},
+}
+#ifdef __cplusplus
+}
+#endif
+#endif // Py_INTERNAL_OPCODE_MACRO_TO_MICRO
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 07625a5bb17bc6..1d3735d61d0430 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -284,6 +284,11 @@ dummy_func(
ERROR_IF(sum == NULL, error);
}
+ macro_inst(BINARY_OP_ADD_INT, (unused / 1, left, right -- sum)) {
+ U_INST(BINARY_OP_ADD_INT_TYPE_CHECK);
+ U_INST(BINARY_OP_ADD_INT_REST);
+ }
+
u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (unused/1, left, right -- left, right)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
@@ -298,11 +303,6 @@ dummy_func(
ERROR_IF(sum == NULL, error);
}
- macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
- U_INST(BINARY_OP_ADD_INT_TYPE_CHECK);
- U_INST(BINARY_OP_ADD_INT_REST);
- }
-
family(binary_subscr, INLINE_CACHE_ENTRIES_BINARY_SUBSCR) = {
BINARY_SUBSCR,
BINARY_SUBSCR_DICT,
diff --git a/Python/opcode_macro_to_micro.h b/Python/opcode_macro_to_micro.h
deleted file mode 100644
index 4029b5d261b129..00000000000000
--- a/Python/opcode_macro_to_micro.h
+++ /dev/null
@@ -1,6 +0,0 @@
-// This file is generated by Tools\cases_generator\generate_cases.py --macromap
-// from Python\bytecodes.c
-// Do not edit!
-static int _Py_MacroOpToUOp[][2] = {
-[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_TYPE_CHECK, BINARY_OP_ADD_INT_REST},
-}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index aa0a7ddb3b5fdf..890d304def8098 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -35,7 +35,7 @@
os.path.join(ROOT, "Python/opcode_metadata_tier2.h")
)
TIER2_MACRO_TO_MICRO_MAP_OUTPUT = os.path.relpath(
- os.path.join(ROOT, "Python/opcode_macro_to_micro.h")
+ os.path.join(ROOT, "Include/internal/pycore_opcode_macro_to_micro.h")
)
BEGIN_MARKER = "// BEGIN BYTECODES //"
@@ -850,11 +850,45 @@ def write_macromap(self):
# Create formatter; the rest of the code uses this
self.out = Formatter(f, 0)
- self.out.emit("static int _Py_MacroOpToUOp[][2] = {")
+ # Header guard
+ self.out.emit("")
+ self.out.emit("#ifndef Py_INTERNAL_OPCODE_MACRO_TO_MICRO")
+ self.out.emit("#define Py_INTERNAL_OPCODE_MACRO_TO_MICRO")
+ self.out.emit("#ifdef __cplusplus")
+ self.out.emit('extern "C" {')
+ self.out.emit("#endif")
+ self.out.emit("")
+ self.out.emit("#ifndef Py_BUILD_CORE")
+ self.out.emit('# error "this header requires Py_BUILD_CORE define"')
+ self.out.emit("#endif")
+ self.out.emit('#include "opcode.h"')
+ self.out.emit("")
+
+ self.out.emit("extern const int _Py_MacroOpUOpCount[] = {")
+ macro_instrdef_names = set()
+ max_instr_len = 0
+ for name, instr_def in self.instrs.items():
+ if (macro_def := instr_def.inst) in self.macro_instdefs:
+ u_insts = macro_def.u_insts
+ instr_len = len(u_insts)
+ max_instr_len = max(instr_len, max_instr_len)
+ self.out.emit(f"[{macro_def.name}] = {instr_len},")
+ macro_instrdef_names.add(macro_def.name)
+ else:
+ self.out.emit(f"[{name}] = 1,")
+ self.out.emit("}")
+ self.out.emit("")
+ self.out.emit(f"extern const int _Py_MacroOpToUOp[][{max_instr_len}] = {{")
for macro_def in self.macro_instdefs:
u_insts = macro_def.u_insts
- self.out.emit(f"[{macro_def.name}] = {{{u_insts[0]}, {u_insts[1]}}},")
+ self.out.emit(f"[{macro_def.name}] = {{{', '.join(u_insts)}}},")
+ self.out.emit("}")
+
+ # Header guard end
+ self.out.emit("#ifdef __cplusplus")
self.out.emit("}")
+ self.out.emit("#endif")
+ self.out.emit("#endif // Py_INTERNAL_OPCODE_MACRO_TO_MICRO")
def write_metadata(self) -> None:
"""Write instruction metadata to output file."""
From 081eb3247675b6debded07569c085c578509307f Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 28 Jan 2023 18:13:14 +0800
Subject: [PATCH 006/280] Generate tier2 instructions separate from tier1
---
Include/internal/pycore_opcode.h | 270 ++++++++++++++++++++++++++++++-
Include/opcode.h | 70 ++++++++
Lib/opcode.py | 8 +
Tools/build/generate_opcode_h.py | 47 +++++-
4 files changed, 391 insertions(+), 4 deletions(-)
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 05c0485b0641d8..7a6c6f6158a3c4 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -226,7 +226,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
};
#endif // NEED_OPCODE_TABLES
-#ifdef Py_DEBUG
+#if defined(Py_DEBUG) && !defined(Py_TIER2_INTERPRETER)
static const char *const _PyOpcode_OpName[263] = {
[CACHE] = "CACHE",
[POP_TOP] = "POP_TOP",
@@ -494,8 +494,274 @@ static const char *const _PyOpcode_OpName[263] = {
};
#endif
+#if defined(Py_DEBUG) && defined(Py_TIER2_INTERPRETER)
+static const char *const _PyOpcode_OpName[263] = {
+ [CACHE] = "CACHE",
+ [POP_TOP] = "POP_TOP",
+ [PUSH_NULL] = "PUSH_NULL",
+ [INTERPRETER_EXIT] = "INTERPRETER_EXIT",
+ [END_FOR] = "END_FOR",
+ [BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
+ [BINARY_OP_ADD_INT_TYPE_CHECK] = "BINARY_OP_ADD_INT_TYPE_CHECK",
+ [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE",
+ [NOP] = "NOP",
+ [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT",
+ [UNARY_NEGATIVE] = "UNARY_NEGATIVE",
+ [UNARY_NOT] = "UNARY_NOT",
+ [BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT",
+ [BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT",
+ [UNARY_INVERT] = "UNARY_INVERT",
+ [BINARY_OP_SUBTRACT_INT] = "BINARY_OP_SUBTRACT_INT",
+ [BINARY_SUBSCR_DICT] = "BINARY_SUBSCR_DICT",
+ [BINARY_SUBSCR_GETITEM] = "BINARY_SUBSCR_GETITEM",
+ [BINARY_SUBSCR_LIST_INT] = "BINARY_SUBSCR_LIST_INT",
+ [BINARY_SUBSCR_TUPLE_INT] = "BINARY_SUBSCR_TUPLE_INT",
+ [CALL_PY_EXACT_ARGS] = "CALL_PY_EXACT_ARGS",
+ [CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS",
+ [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS",
+ [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS",
+ [BINARY_SUBSCR] = "BINARY_SUBSCR",
+ [BINARY_SLICE] = "BINARY_SLICE",
+ [STORE_SLICE] = "STORE_SLICE",
+ [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS",
+ [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
+ [GET_LEN] = "GET_LEN",
+ [MATCH_MAPPING] = "MATCH_MAPPING",
+ [MATCH_SEQUENCE] = "MATCH_SEQUENCE",
+ [MATCH_KEYS] = "MATCH_KEYS",
+ [CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST",
+ [PUSH_EXC_INFO] = "PUSH_EXC_INFO",
+ [CHECK_EXC_MATCH] = "CHECK_EXC_MATCH",
+ [CHECK_EG_MATCH] = "CHECK_EG_MATCH",
+ [CALL_NO_KW_BUILTIN_O] = "CALL_NO_KW_BUILTIN_O",
+ [CALL_NO_KW_ISINSTANCE] = "CALL_NO_KW_ISINSTANCE",
+ [CALL_NO_KW_LEN] = "CALL_NO_KW_LEN",
+ [CALL_NO_KW_LIST_APPEND] = "CALL_NO_KW_LIST_APPEND",
+ [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = "CALL_NO_KW_METHOD_DESCRIPTOR_FAST",
+ [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = "CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS",
+ [CALL_NO_KW_METHOD_DESCRIPTOR_O] = "CALL_NO_KW_METHOD_DESCRIPTOR_O",
+ [CALL_NO_KW_STR_1] = "CALL_NO_KW_STR_1",
+ [CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1",
+ [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1",
+ [COMPARE_AND_BRANCH_FLOAT] = "COMPARE_AND_BRANCH_FLOAT",
+ [WITH_EXCEPT_START] = "WITH_EXCEPT_START",
+ [GET_AITER] = "GET_AITER",
+ [GET_ANEXT] = "GET_ANEXT",
+ [BEFORE_ASYNC_WITH] = "BEFORE_ASYNC_WITH",
+ [BEFORE_WITH] = "BEFORE_WITH",
+ [END_ASYNC_FOR] = "END_ASYNC_FOR",
+ [CLEANUP_THROW] = "CLEANUP_THROW",
+ [COMPARE_AND_BRANCH_INT] = "COMPARE_AND_BRANCH_INT",
+ [COMPARE_AND_BRANCH_STR] = "COMPARE_AND_BRANCH_STR",
+ [FOR_ITER_LIST] = "FOR_ITER_LIST",
+ [FOR_ITER_TUPLE] = "FOR_ITER_TUPLE",
+ [STORE_SUBSCR] = "STORE_SUBSCR",
+ [DELETE_SUBSCR] = "DELETE_SUBSCR",
+ [FOR_ITER_RANGE] = "FOR_ITER_RANGE",
+ [FOR_ITER_GEN] = "FOR_ITER_GEN",
+ [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS",
+ [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
+ [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
+ [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
+ [GET_ITER] = "GET_ITER",
+ [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER",
+ [LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
+ [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
+ [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
+ [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
+ [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
+ [RETURN_GENERATOR] = "RETURN_GENERATOR",
+ [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT",
+ [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
+ [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
+ [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
+ [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
+ [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
+ [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
+ [RETURN_VALUE] = "RETURN_VALUE",
+ [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
+ [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
+ [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
+ [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
+ [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR",
+ [POP_EXCEPT] = "POP_EXCEPT",
+ [STORE_NAME] = "STORE_NAME",
+ [DELETE_NAME] = "DELETE_NAME",
+ [UNPACK_SEQUENCE] = "UNPACK_SEQUENCE",
+ [FOR_ITER] = "FOR_ITER",
+ [UNPACK_EX] = "UNPACK_EX",
+ [STORE_ATTR] = "STORE_ATTR",
+ [DELETE_ATTR] = "DELETE_ATTR",
+ [STORE_GLOBAL] = "STORE_GLOBAL",
+ [DELETE_GLOBAL] = "DELETE_GLOBAL",
+ [SWAP] = "SWAP",
+ [LOAD_CONST] = "LOAD_CONST",
+ [LOAD_NAME] = "LOAD_NAME",
+ [BUILD_TUPLE] = "BUILD_TUPLE",
+ [BUILD_LIST] = "BUILD_LIST",
+ [BUILD_SET] = "BUILD_SET",
+ [BUILD_MAP] = "BUILD_MAP",
+ [LOAD_ATTR] = "LOAD_ATTR",
+ [COMPARE_OP] = "COMPARE_OP",
+ [IMPORT_NAME] = "IMPORT_NAME",
+ [IMPORT_FROM] = "IMPORT_FROM",
+ [JUMP_FORWARD] = "JUMP_FORWARD",
+ [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP",
+ [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP",
+ [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
+ [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
+ [POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
+ [LOAD_GLOBAL] = "LOAD_GLOBAL",
+ [IS_OP] = "IS_OP",
+ [CONTAINS_OP] = "CONTAINS_OP",
+ [RERAISE] = "RERAISE",
+ [COPY] = "COPY",
+ [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
+ [BINARY_OP] = "BINARY_OP",
+ [SEND] = "SEND",
+ [LOAD_FAST] = "LOAD_FAST",
+ [STORE_FAST] = "STORE_FAST",
+ [DELETE_FAST] = "DELETE_FAST",
+ [LOAD_FAST_CHECK] = "LOAD_FAST_CHECK",
+ [POP_JUMP_IF_NOT_NONE] = "POP_JUMP_IF_NOT_NONE",
+ [POP_JUMP_IF_NONE] = "POP_JUMP_IF_NONE",
+ [RAISE_VARARGS] = "RAISE_VARARGS",
+ [GET_AWAITABLE] = "GET_AWAITABLE",
+ [MAKE_FUNCTION] = "MAKE_FUNCTION",
+ [BUILD_SLICE] = "BUILD_SLICE",
+ [JUMP_BACKWARD_NO_INTERRUPT] = "JUMP_BACKWARD_NO_INTERRUPT",
+ [MAKE_CELL] = "MAKE_CELL",
+ [LOAD_CLOSURE] = "LOAD_CLOSURE",
+ [LOAD_DEREF] = "LOAD_DEREF",
+ [STORE_DEREF] = "STORE_DEREF",
+ [DELETE_DEREF] = "DELETE_DEREF",
+ [JUMP_BACKWARD] = "JUMP_BACKWARD",
+ [COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH",
+ [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
+ [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
+ [EXTENDED_ARG] = "EXTENDED_ARG",
+ [LIST_APPEND] = "LIST_APPEND",
+ [SET_ADD] = "SET_ADD",
+ [MAP_ADD] = "MAP_ADD",
+ [LOAD_CLASSDEREF] = "LOAD_CLASSDEREF",
+ [COPY_FREE_VARS] = "COPY_FREE_VARS",
+ [YIELD_VALUE] = "YIELD_VALUE",
+ [RESUME] = "RESUME",
+ [MATCH_CLASS] = "MATCH_CLASS",
+ [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
+ [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
+ [FORMAT_VALUE] = "FORMAT_VALUE",
+ [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
+ [BUILD_STRING] = "BUILD_STRING",
+ [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
+ [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
+ [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
+ [160] = "<161>",
+ [BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
+ [SET_UPDATE] = "SET_UPDATE",
+ [DICT_MERGE] = "DICT_MERGE",
+ [DICT_UPDATE] = "DICT_UPDATE",
+ [165] = "<166>",
+ [166] = "<167>",
+ [167] = "<168>",
+ [168] = "<169>",
+ [169] = "<170>",
+ [CALL] = "CALL",
+ [KW_NAMES] = "KW_NAMES",
+ [CALL_INTRINSIC_1] = "CALL_INTRINSIC_1",
+ [173] = "<174>",
+ [174] = "<175>",
+ [175] = "<176>",
+ [176] = "<177>",
+ [177] = "<178>",
+ [178] = "<179>",
+ [179] = "<180>",
+ [180] = "<181>",
+ [181] = "<182>",
+ [182] = "<183>",
+ [183] = "<184>",
+ [184] = "<185>",
+ [185] = "<186>",
+ [186] = "<187>",
+ [187] = "<188>",
+ [188] = "<189>",
+ [189] = "<190>",
+ [190] = "<191>",
+ [191] = "<192>",
+ [192] = "<193>",
+ [193] = "<194>",
+ [194] = "<195>",
+ [195] = "<196>",
+ [196] = "<197>",
+ [197] = "<198>",
+ [198] = "<199>",
+ [199] = "<200>",
+ [200] = "<201>",
+ [201] = "<202>",
+ [202] = "<203>",
+ [203] = "<204>",
+ [204] = "<205>",
+ [205] = "<206>",
+ [206] = "<207>",
+ [207] = "<208>",
+ [208] = "<209>",
+ [209] = "<210>",
+ [210] = "<211>",
+ [211] = "<212>",
+ [212] = "<213>",
+ [213] = "<214>",
+ [214] = "<215>",
+ [215] = "<216>",
+ [216] = "<217>",
+ [217] = "<218>",
+ [218] = "<219>",
+ [219] = "<220>",
+ [220] = "<221>",
+ [221] = "<222>",
+ [222] = "<223>",
+ [223] = "<224>",
+ [224] = "<225>",
+ [225] = "<226>",
+ [226] = "<227>",
+ [227] = "<228>",
+ [228] = "<229>",
+ [229] = "<230>",
+ [230] = "<231>",
+ [231] = "<232>",
+ [232] = "<233>",
+ [233] = "<234>",
+ [234] = "<235>",
+ [235] = "<236>",
+ [236] = "<237>",
+ [237] = "<238>",
+ [238] = "<239>",
+ [239] = "<240>",
+ [240] = "<241>",
+ [241] = "<242>",
+ [242] = "<243>",
+ [243] = "<244>",
+ [244] = "<245>",
+ [245] = "<246>",
+ [246] = "<247>",
+ [247] = "<248>",
+ [248] = "<249>",
+ [249] = "<250>",
+ [250] = "<251>",
+ [251] = "<252>",
+ [252] = "<253>",
+ [253] = "<254>",
+ [DO_TRACING] = "DO_TRACING",
+ [SETUP_FINALLY] = "SETUP_FINALLY",
+ [SETUP_CLEANUP] = "SETUP_CLEANUP",
+ [SETUP_WITH] = "SETUP_WITH",
+ [POP_BLOCK] = "POP_BLOCK",
+ [JUMP] = "JUMP",
+ [JUMP_NO_INTERRUPT] = "JUMP_NO_INTERRUPT",
+ [LOAD_METHOD] = "LOAD_METHOD",
+};
+#endif
+
#define EXTRA_CASES \
- case 161: \
case 166: \
case 167: \
case 168: \
diff --git a/Include/opcode.h b/Include/opcode.h
index 827f9931beb3e6..63e5a4329b27db 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -125,6 +125,8 @@ extern "C" {
#define JUMP_NO_INTERRUPT 261
#define LOAD_METHOD 262
#define MAX_PSEUDO_OPCODE 262
+
+#ifndef Py_TIER2_INTERPRETER
#define BINARY_OP_ADD_FLOAT 5
#define BINARY_OP_ADD_INT 6
#define BINARY_OP_ADD_UNICODE 7
@@ -187,6 +189,74 @@ extern "C" {
#define UNPACK_SEQUENCE_TUPLE 159
#define UNPACK_SEQUENCE_TWO_TUPLE 160
#define DO_TRACING 255
+#endif
+
+#ifdef Py_TIER2_INTERPRETER
+#define BINARY_OP_ADD_FLOAT 5
+#define BINARY_OP_ADD_UNICODE 7
+#define BINARY_OP_INPLACE_ADD_UNICODE 8
+#define BINARY_OP_MULTIPLY_FLOAT 10
+#define BINARY_OP_MULTIPLY_INT 13
+#define BINARY_OP_SUBTRACT_FLOAT 14
+#define BINARY_OP_SUBTRACT_INT 16
+#define BINARY_SUBSCR_DICT 17
+#define BINARY_SUBSCR_GETITEM 18
+#define BINARY_SUBSCR_LIST_INT 19
+#define BINARY_SUBSCR_TUPLE_INT 20
+#define CALL_PY_EXACT_ARGS 21
+#define CALL_PY_WITH_DEFAULTS 22
+#define CALL_BOUND_METHOD_EXACT_ARGS 23
+#define CALL_BUILTIN_CLASS 24
+#define CALL_BUILTIN_FAST_WITH_KEYWORDS 28
+#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 29
+#define CALL_NO_KW_BUILTIN_FAST 34
+#define CALL_NO_KW_BUILTIN_O 38
+#define CALL_NO_KW_ISINSTANCE 39
+#define CALL_NO_KW_LEN 40
+#define CALL_NO_KW_LIST_APPEND 41
+#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 42
+#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 43
+#define CALL_NO_KW_METHOD_DESCRIPTOR_O 44
+#define CALL_NO_KW_STR_1 45
+#define CALL_NO_KW_TUPLE_1 46
+#define CALL_NO_KW_TYPE_1 47
+#define COMPARE_AND_BRANCH_FLOAT 48
+#define COMPARE_AND_BRANCH_INT 56
+#define COMPARE_AND_BRANCH_STR 57
+#define FOR_ITER_LIST 58
+#define FOR_ITER_TUPLE 59
+#define FOR_ITER_RANGE 62
+#define FOR_ITER_GEN 63
+#define LOAD_ATTR_CLASS 64
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 65
+#define LOAD_ATTR_INSTANCE_VALUE 66
+#define LOAD_ATTR_MODULE 67
+#define LOAD_ATTR_PROPERTY 70
+#define LOAD_ATTR_SLOT 72
+#define LOAD_ATTR_WITH_HINT 73
+#define LOAD_ATTR_METHOD_LAZY_DICT 76
+#define LOAD_ATTR_METHOD_NO_DICT 77
+#define LOAD_ATTR_METHOD_WITH_VALUES 78
+#define LOAD_CONST__LOAD_FAST 79
+#define LOAD_FAST__LOAD_CONST 80
+#define LOAD_FAST__LOAD_FAST 81
+#define LOAD_GLOBAL_BUILTIN 82
+#define LOAD_GLOBAL_MODULE 84
+#define STORE_ATTR_INSTANCE_VALUE 86
+#define STORE_ATTR_SLOT 87
+#define STORE_ATTR_WITH_HINT 113
+#define STORE_FAST__LOAD_FAST 121
+#define STORE_FAST__STORE_FAST 143
+#define STORE_SUBSCR_DICT 153
+#define STORE_SUBSCR_LIST_INT 154
+#define UNPACK_SEQUENCE_LIST 158
+#define UNPACK_SEQUENCE_TUPLE 159
+#define UNPACK_SEQUENCE_TWO_TUPLE 160
+#define DO_TRACING 255
+#define BINARY_OP_ADD_INT_TYPE_CHECK 6
+#define BINARY_OP_ADD_INT_REST 161
+#endif
+
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index c317e23beae62b..9c1402e316d395 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -432,3 +432,11 @@ def pseudo_op(name, op, real_ops):
_inline_cache_entries = [
sum(_cache_format.get(opname[opcode], {}).values()) for opcode in range(256)
]
+
+_macro_ops = [
+ 'BINARY_OP_ADD_INT',
+]
+_uops = [
+ 'BINARY_OP_ADD_INT_TYPE_CHECK',
+ 'BINARY_OP_ADD_INT_REST',
+]
diff --git a/Tools/build/generate_opcode_h.py b/Tools/build/generate_opcode_h.py
index 9b2112f7f5f31d..3ff94a324a0803 100644
--- a/Tools/build/generate_opcode_h.py
+++ b/Tools/build/generate_opcode_h.py
@@ -105,14 +105,35 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
specialized_opmap[name] = next_op
opname_including_specialized[next_op] = name
used[next_op] = True
+
specialized_opmap['DO_TRACING'] = 255
opname_including_specialized[255] = 'DO_TRACING'
used[255] = True
+ # The Tier 2 ops
+ next_op = 1
+ uop_opmap = specialized_opmap.copy()
+ uop_opname = opname_including_specialized.copy()
+ # Remove macroops
+ for name in opcode['_macro_ops']:
+ op = uop_opmap[name]
+ del uop_opmap[name]
+ del uop_opname[op]
+ used[op] = False
+ # Add microops
+ for name in opcode['_uops']:
+ while used[next_op]:
+ next_op += 1
+ uop_opmap[name] = next_op
+ uop_opname[next_op] = name
+ used[next_op] = True
+
with open(outfile, 'w') as fobj, open(internaloutfile, 'w') as iobj:
fobj.write(header)
iobj.write(internal_header)
+ # Tier 1 opcodes
+
for name in opname:
if name in opmap:
op = opmap[name]
@@ -126,9 +147,19 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
if op == MAX_PSEUDO_OPCODE:
fobj.write(DEFINE.format("MAX_PSEUDO_OPCODE", MAX_PSEUDO_OPCODE))
-
+ fobj.write("\n")
+ fobj.write("#ifndef Py_TIER2_INTERPRETER\n")
for name, op in specialized_opmap.items():
fobj.write(DEFINE.format(name, op))
+ fobj.write("#endif\n")
+ fobj.write("\n")
+
+ # Tier 2 opcodes
+ fobj.write("#ifdef Py_TIER2_INTERPRETER\n")
+ for name, op in uop_opmap.items():
+ fobj.write(DEFINE.format(name, op))
+ fobj.write("#endif\n")
+ fobj.write("\n")
iobj.write("\nextern const uint8_t _PyOpcode_Caches[256];\n")
iobj.write("\nextern const uint8_t _PyOpcode_Deopt[256];\n")
@@ -177,7 +208,8 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
fobj.write(f"#define ENABLE_SPECIALIZATION {int(ENABLE_SPECIALIZATION)}")
iobj.write("\n")
- iobj.write("#ifdef Py_DEBUG\n")
+ # Tier 1 opnames
+ iobj.write("#if defined(Py_DEBUG) && !defined(Py_TIER2_INTERPRETER)\n")
iobj.write(f"static const char *const _PyOpcode_OpName[{NUM_OPCODES}] = {{\n")
for op, name in enumerate(opname_including_specialized):
if name[0] != "<":
@@ -186,6 +218,17 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
iobj.write("};\n")
iobj.write("#endif\n")
+ iobj.write("\n")
+ # Tier 2 opnames
+ iobj.write("#if defined(Py_DEBUG) && defined(Py_TIER2_INTERPRETER)\n")
+ iobj.write(f"static const char *const _PyOpcode_OpName[{NUM_OPCODES}] = {{\n")
+ for op, name in enumerate(uop_opname):
+ if name[0] != "<":
+ op = name
+ iobj.write(f''' [{op}] = "{name}",\n''')
+ iobj.write("};\n")
+ iobj.write("#endif\n")
+
iobj.write("\n")
iobj.write("#define EXTRA_CASES \\\n")
for i, flag in enumerate(used):
From 056dc12c20d6d9d18d1474afd565bafce771d45e Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sun, 29 Jan 2023 01:16:37 +0800
Subject: [PATCH 007/280] Combine the interpreters
---
Include/internal/pycore_opcode.h | 273 +-
.../internal/pycore_opcode_macro_to_micro.h | 5 +-
Include/opcode.h | 13 +-
Python/bytecodes.c | 6 +-
Python/generated_cases.c.h | 31 +-
Python/generated_cases_tier2.c.h | 3713 -----------------
Python/opcode_metadata_tier2.h | 882 ----
Tools/build/generate_opcode_h.py | 28 +-
Tools/cases_generator/generate_cases.py | 74 +-
9 files changed, 65 insertions(+), 4960 deletions(-)
delete mode 100644 Python/generated_cases_tier2.c.h
delete mode 100644 Python/opcode_metadata_tier2.h
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 7a6c6f6158a3c4..92ce888e0c322f 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -226,7 +226,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
};
#endif // NEED_OPCODE_TABLES
-#if defined(Py_DEBUG) && !defined(Py_TIER2_INTERPRETER)
+#ifdef Py_DEBUG
static const char *const _PyOpcode_OpName[263] = {
[CACHE] = "CACHE",
[POP_TOP] = "POP_TOP",
@@ -389,12 +389,12 @@ static const char *const _PyOpcode_OpName[263] = {
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
- [161] = "<161>",
+ [BINARY_OP_ADD_INT_TYPE_CHECK] = "BINARY_OP_ADD_INT_TYPE_CHECK",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
- [166] = "<166>",
+ [BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
[167] = "<167>",
[168] = "<168>",
[169] = "<169>",
@@ -494,275 +494,8 @@ static const char *const _PyOpcode_OpName[263] = {
};
#endif
-#if defined(Py_DEBUG) && defined(Py_TIER2_INTERPRETER)
-static const char *const _PyOpcode_OpName[263] = {
- [CACHE] = "CACHE",
- [POP_TOP] = "POP_TOP",
- [PUSH_NULL] = "PUSH_NULL",
- [INTERPRETER_EXIT] = "INTERPRETER_EXIT",
- [END_FOR] = "END_FOR",
- [BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
- [BINARY_OP_ADD_INT_TYPE_CHECK] = "BINARY_OP_ADD_INT_TYPE_CHECK",
- [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE",
- [NOP] = "NOP",
- [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT",
- [UNARY_NEGATIVE] = "UNARY_NEGATIVE",
- [UNARY_NOT] = "UNARY_NOT",
- [BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT",
- [BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT",
- [UNARY_INVERT] = "UNARY_INVERT",
- [BINARY_OP_SUBTRACT_INT] = "BINARY_OP_SUBTRACT_INT",
- [BINARY_SUBSCR_DICT] = "BINARY_SUBSCR_DICT",
- [BINARY_SUBSCR_GETITEM] = "BINARY_SUBSCR_GETITEM",
- [BINARY_SUBSCR_LIST_INT] = "BINARY_SUBSCR_LIST_INT",
- [BINARY_SUBSCR_TUPLE_INT] = "BINARY_SUBSCR_TUPLE_INT",
- [CALL_PY_EXACT_ARGS] = "CALL_PY_EXACT_ARGS",
- [CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS",
- [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS",
- [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS",
- [BINARY_SUBSCR] = "BINARY_SUBSCR",
- [BINARY_SLICE] = "BINARY_SLICE",
- [STORE_SLICE] = "STORE_SLICE",
- [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS",
- [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
- [GET_LEN] = "GET_LEN",
- [MATCH_MAPPING] = "MATCH_MAPPING",
- [MATCH_SEQUENCE] = "MATCH_SEQUENCE",
- [MATCH_KEYS] = "MATCH_KEYS",
- [CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST",
- [PUSH_EXC_INFO] = "PUSH_EXC_INFO",
- [CHECK_EXC_MATCH] = "CHECK_EXC_MATCH",
- [CHECK_EG_MATCH] = "CHECK_EG_MATCH",
- [CALL_NO_KW_BUILTIN_O] = "CALL_NO_KW_BUILTIN_O",
- [CALL_NO_KW_ISINSTANCE] = "CALL_NO_KW_ISINSTANCE",
- [CALL_NO_KW_LEN] = "CALL_NO_KW_LEN",
- [CALL_NO_KW_LIST_APPEND] = "CALL_NO_KW_LIST_APPEND",
- [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = "CALL_NO_KW_METHOD_DESCRIPTOR_FAST",
- [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = "CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS",
- [CALL_NO_KW_METHOD_DESCRIPTOR_O] = "CALL_NO_KW_METHOD_DESCRIPTOR_O",
- [CALL_NO_KW_STR_1] = "CALL_NO_KW_STR_1",
- [CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1",
- [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1",
- [COMPARE_AND_BRANCH_FLOAT] = "COMPARE_AND_BRANCH_FLOAT",
- [WITH_EXCEPT_START] = "WITH_EXCEPT_START",
- [GET_AITER] = "GET_AITER",
- [GET_ANEXT] = "GET_ANEXT",
- [BEFORE_ASYNC_WITH] = "BEFORE_ASYNC_WITH",
- [BEFORE_WITH] = "BEFORE_WITH",
- [END_ASYNC_FOR] = "END_ASYNC_FOR",
- [CLEANUP_THROW] = "CLEANUP_THROW",
- [COMPARE_AND_BRANCH_INT] = "COMPARE_AND_BRANCH_INT",
- [COMPARE_AND_BRANCH_STR] = "COMPARE_AND_BRANCH_STR",
- [FOR_ITER_LIST] = "FOR_ITER_LIST",
- [FOR_ITER_TUPLE] = "FOR_ITER_TUPLE",
- [STORE_SUBSCR] = "STORE_SUBSCR",
- [DELETE_SUBSCR] = "DELETE_SUBSCR",
- [FOR_ITER_RANGE] = "FOR_ITER_RANGE",
- [FOR_ITER_GEN] = "FOR_ITER_GEN",
- [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS",
- [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
- [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
- [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
- [GET_ITER] = "GET_ITER",
- [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER",
- [LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
- [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
- [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
- [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
- [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
- [RETURN_GENERATOR] = "RETURN_GENERATOR",
- [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT",
- [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
- [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
- [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
- [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
- [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
- [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
- [RETURN_VALUE] = "RETURN_VALUE",
- [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
- [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
- [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
- [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
- [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR",
- [POP_EXCEPT] = "POP_EXCEPT",
- [STORE_NAME] = "STORE_NAME",
- [DELETE_NAME] = "DELETE_NAME",
- [UNPACK_SEQUENCE] = "UNPACK_SEQUENCE",
- [FOR_ITER] = "FOR_ITER",
- [UNPACK_EX] = "UNPACK_EX",
- [STORE_ATTR] = "STORE_ATTR",
- [DELETE_ATTR] = "DELETE_ATTR",
- [STORE_GLOBAL] = "STORE_GLOBAL",
- [DELETE_GLOBAL] = "DELETE_GLOBAL",
- [SWAP] = "SWAP",
- [LOAD_CONST] = "LOAD_CONST",
- [LOAD_NAME] = "LOAD_NAME",
- [BUILD_TUPLE] = "BUILD_TUPLE",
- [BUILD_LIST] = "BUILD_LIST",
- [BUILD_SET] = "BUILD_SET",
- [BUILD_MAP] = "BUILD_MAP",
- [LOAD_ATTR] = "LOAD_ATTR",
- [COMPARE_OP] = "COMPARE_OP",
- [IMPORT_NAME] = "IMPORT_NAME",
- [IMPORT_FROM] = "IMPORT_FROM",
- [JUMP_FORWARD] = "JUMP_FORWARD",
- [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP",
- [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP",
- [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
- [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
- [POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
- [LOAD_GLOBAL] = "LOAD_GLOBAL",
- [IS_OP] = "IS_OP",
- [CONTAINS_OP] = "CONTAINS_OP",
- [RERAISE] = "RERAISE",
- [COPY] = "COPY",
- [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
- [BINARY_OP] = "BINARY_OP",
- [SEND] = "SEND",
- [LOAD_FAST] = "LOAD_FAST",
- [STORE_FAST] = "STORE_FAST",
- [DELETE_FAST] = "DELETE_FAST",
- [LOAD_FAST_CHECK] = "LOAD_FAST_CHECK",
- [POP_JUMP_IF_NOT_NONE] = "POP_JUMP_IF_NOT_NONE",
- [POP_JUMP_IF_NONE] = "POP_JUMP_IF_NONE",
- [RAISE_VARARGS] = "RAISE_VARARGS",
- [GET_AWAITABLE] = "GET_AWAITABLE",
- [MAKE_FUNCTION] = "MAKE_FUNCTION",
- [BUILD_SLICE] = "BUILD_SLICE",
- [JUMP_BACKWARD_NO_INTERRUPT] = "JUMP_BACKWARD_NO_INTERRUPT",
- [MAKE_CELL] = "MAKE_CELL",
- [LOAD_CLOSURE] = "LOAD_CLOSURE",
- [LOAD_DEREF] = "LOAD_DEREF",
- [STORE_DEREF] = "STORE_DEREF",
- [DELETE_DEREF] = "DELETE_DEREF",
- [JUMP_BACKWARD] = "JUMP_BACKWARD",
- [COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH",
- [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
- [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
- [EXTENDED_ARG] = "EXTENDED_ARG",
- [LIST_APPEND] = "LIST_APPEND",
- [SET_ADD] = "SET_ADD",
- [MAP_ADD] = "MAP_ADD",
- [LOAD_CLASSDEREF] = "LOAD_CLASSDEREF",
- [COPY_FREE_VARS] = "COPY_FREE_VARS",
- [YIELD_VALUE] = "YIELD_VALUE",
- [RESUME] = "RESUME",
- [MATCH_CLASS] = "MATCH_CLASS",
- [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
- [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
- [FORMAT_VALUE] = "FORMAT_VALUE",
- [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
- [BUILD_STRING] = "BUILD_STRING",
- [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
- [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
- [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
- [160] = "<161>",
- [BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
- [SET_UPDATE] = "SET_UPDATE",
- [DICT_MERGE] = "DICT_MERGE",
- [DICT_UPDATE] = "DICT_UPDATE",
- [165] = "<166>",
- [166] = "<167>",
- [167] = "<168>",
- [168] = "<169>",
- [169] = "<170>",
- [CALL] = "CALL",
- [KW_NAMES] = "KW_NAMES",
- [CALL_INTRINSIC_1] = "CALL_INTRINSIC_1",
- [173] = "<174>",
- [174] = "<175>",
- [175] = "<176>",
- [176] = "<177>",
- [177] = "<178>",
- [178] = "<179>",
- [179] = "<180>",
- [180] = "<181>",
- [181] = "<182>",
- [182] = "<183>",
- [183] = "<184>",
- [184] = "<185>",
- [185] = "<186>",
- [186] = "<187>",
- [187] = "<188>",
- [188] = "<189>",
- [189] = "<190>",
- [190] = "<191>",
- [191] = "<192>",
- [192] = "<193>",
- [193] = "<194>",
- [194] = "<195>",
- [195] = "<196>",
- [196] = "<197>",
- [197] = "<198>",
- [198] = "<199>",
- [199] = "<200>",
- [200] = "<201>",
- [201] = "<202>",
- [202] = "<203>",
- [203] = "<204>",
- [204] = "<205>",
- [205] = "<206>",
- [206] = "<207>",
- [207] = "<208>",
- [208] = "<209>",
- [209] = "<210>",
- [210] = "<211>",
- [211] = "<212>",
- [212] = "<213>",
- [213] = "<214>",
- [214] = "<215>",
- [215] = "<216>",
- [216] = "<217>",
- [217] = "<218>",
- [218] = "<219>",
- [219] = "<220>",
- [220] = "<221>",
- [221] = "<222>",
- [222] = "<223>",
- [223] = "<224>",
- [224] = "<225>",
- [225] = "<226>",
- [226] = "<227>",
- [227] = "<228>",
- [228] = "<229>",
- [229] = "<230>",
- [230] = "<231>",
- [231] = "<232>",
- [232] = "<233>",
- [233] = "<234>",
- [234] = "<235>",
- [235] = "<236>",
- [236] = "<237>",
- [237] = "<238>",
- [238] = "<239>",
- [239] = "<240>",
- [240] = "<241>",
- [241] = "<242>",
- [242] = "<243>",
- [243] = "<244>",
- [244] = "<245>",
- [245] = "<246>",
- [246] = "<247>",
- [247] = "<248>",
- [248] = "<249>",
- [249] = "<250>",
- [250] = "<251>",
- [251] = "<252>",
- [252] = "<253>",
- [253] = "<254>",
- [DO_TRACING] = "DO_TRACING",
- [SETUP_FINALLY] = "SETUP_FINALLY",
- [SETUP_CLEANUP] = "SETUP_CLEANUP",
- [SETUP_WITH] = "SETUP_WITH",
- [POP_BLOCK] = "POP_BLOCK",
- [JUMP] = "JUMP",
- [JUMP_NO_INTERRUPT] = "JUMP_NO_INTERRUPT",
- [LOAD_METHOD] = "LOAD_METHOD",
-};
-#endif
#define EXTRA_CASES \
- case 166: \
case 167: \
case 168: \
case 169: \
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index def9dd5781e5a0..aa9049ee5b9518 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -1,6 +1,7 @@
// This file is generated by Tools\cases_generator\generate_cases.py --macromap
// from Python\bytecodes.c
// Do not edit!
+
#ifndef Py_INTERNAL_OPCODE_MACRO_TO_MICRO
#define Py_INTERNAL_OPCODE_MACRO_TO_MICRO
#ifdef __cplusplus
@@ -12,7 +13,7 @@ extern "C" {
#endif
#include "opcode.h"
-static int _Py_MacroOpUOpCount[] = {
+extern const int _Py_MacroOpUOpCount[] = {
[NOP] = 1,
[RESUME] = 1,
[LOAD_CLOSURE] = 1,
@@ -179,7 +180,7 @@ static int _Py_MacroOpUOpCount[] = {
[CACHE] = 1,
}
-static int _Py_MacroOpToUOp[][2] = {
+extern const int _Py_MacroOpToUOp[][2] = {
[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_TYPE_CHECK, BINARY_OP_ADD_INT_REST},
}
#ifdef __cplusplus
diff --git a/Include/opcode.h b/Include/opcode.h
index 63e5a4329b27db..93635c89da2626 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -125,8 +125,6 @@ extern "C" {
#define JUMP_NO_INTERRUPT 261
#define LOAD_METHOD 262
#define MAX_PSEUDO_OPCODE 262
-
-#ifndef Py_TIER2_INTERPRETER
#define BINARY_OP_ADD_FLOAT 5
#define BINARY_OP_ADD_INT 6
#define BINARY_OP_ADD_UNICODE 7
@@ -189,10 +187,9 @@ extern "C" {
#define UNPACK_SEQUENCE_TUPLE 159
#define UNPACK_SEQUENCE_TWO_TUPLE 160
#define DO_TRACING 255
-#endif
-
-#ifdef Py_TIER2_INTERPRETER
+// Tier 2 interpreter ops
#define BINARY_OP_ADD_FLOAT 5
+#define BINARY_OP_ADD_INT 6
#define BINARY_OP_ADD_UNICODE 7
#define BINARY_OP_INPLACE_ADD_UNICODE 8
#define BINARY_OP_MULTIPLY_FLOAT 10
@@ -253,10 +250,8 @@ extern "C" {
#define UNPACK_SEQUENCE_TUPLE 159
#define UNPACK_SEQUENCE_TWO_TUPLE 160
#define DO_TRACING 255
-#define BINARY_OP_ADD_INT_TYPE_CHECK 6
-#define BINARY_OP_ADD_INT_REST 161
-#endif
-
+#define BINARY_OP_ADD_INT_TYPE_CHECK 161
+#define BINARY_OP_ADD_INT_REST 166
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 1d3735d61d0430..94f4b7ec15567c 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -284,18 +284,18 @@ dummy_func(
ERROR_IF(sum == NULL, error);
}
- macro_inst(BINARY_OP_ADD_INT, (unused / 1, left, right -- sum)) {
+ macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
U_INST(BINARY_OP_ADD_INT_TYPE_CHECK);
U_INST(BINARY_OP_ADD_INT_REST);
}
- u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (unused/1, left, right -- left, right)) {
+ u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (left, right -- left, right)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
}
- u_inst(BINARY_OP_ADD_INT_REST, (unused/1, left, right -- sum)) {
+ u_inst(BINARY_OP_ADD_INT_REST, (left, right -- sum)) {
STAT_INC(BINARY_OP, hit);
sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 85d44e9dc1b9ca..da28e41dbc7f70 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2,14 +2,14 @@
// from Python\bytecodes.c
// Do not edit!
- #define BINARY_OP_ADD_INT_TYPE_CHECK() \
+ #define UOP_BINARY_OP_ADD_INT_TYPE_CHECK() \
do { \
assert(cframe.use_tracing == 0);\
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);\
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);\
} while (0)
- #define BINARY_OP_ADD_INT_REST() \
+ #define UOP_BINARY_OP_ADD_INT_REST() \
do { \
STAT_INC(BINARY_OP, hit);\
sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);\
@@ -406,14 +406,37 @@
PyObject *right = PEEK(1);
PyObject *left = PEEK(2);
PyObject *sum;
- BINARY_OP_ADD_INT_TYPE_CHECK();
- BINARY_OP_ADD_INT_REST();
+ UOP_BINARY_OP_ADD_INT_TYPE_CHECK();
+ UOP_BINARY_OP_ADD_INT_REST();
STACK_SHRINK(1);
POKE(1, sum);
JUMPBY(1);
DISPATCH();
}
+ TARGET(BINARY_OP_ADD_INT_TYPE_CHECK) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_ADD_INT_REST) {
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *sum;
+ STAT_INC(BINARY_OP, hit);
+ sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
+ _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
+ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
+ if (sum == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, sum);
+ DISPATCH();
+ }
+
TARGET(BINARY_SUBSCR) {
PREDICTED(BINARY_SUBSCR);
static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 4, "incorrect cache size");
diff --git a/Python/generated_cases_tier2.c.h b/Python/generated_cases_tier2.c.h
deleted file mode 100644
index 3a7b35f2c89f4b..00000000000000
--- a/Python/generated_cases_tier2.c.h
+++ /dev/null
@@ -1,3713 +0,0 @@
-// This file is generated by Tools\cases_generator\generate_cases.py
-// from Python\bytecodes.c
-// Do not edit!
-
- TARGET(NOP) {
- DISPATCH();
- }
-
- TARGET(RESUME) {
- assert(tstate->cframe == &cframe);
- assert(frame == cframe.current_frame);
- if (_Py_atomic_load_relaxed_int32(eval_breaker) && oparg < 2) {
- goto handle_eval_breaker;
- }
- DISPATCH();
- }
-
- TARGET(LOAD_CLOSURE) {
- PyObject *value;
- /* We keep LOAD_CLOSURE so that the bytecode stays more readable. */
- value = GETLOCAL(oparg);
- if (value == NULL) goto unbound_local_error;
- Py_INCREF(value);
- STACK_GROW(1);
- POKE(1, value);
- DISPATCH();
- }
-
- TARGET(LOAD_FAST_CHECK) {
- PyObject *value;
- value = GETLOCAL(oparg);
- if (value == NULL) goto unbound_local_error;
- Py_INCREF(value);
- STACK_GROW(1);
- POKE(1, value);
- DISPATCH();
- }
-
- TARGET(LOAD_FAST) {
- PyObject *value;
- value = GETLOCAL(oparg);
- assert(value != NULL);
- Py_INCREF(value);
- STACK_GROW(1);
- POKE(1, value);
- DISPATCH();
- }
-
- TARGET(LOAD_CONST) {
- PREDICTED(LOAD_CONST);
- PyObject *value;
- value = GETITEM(consts, oparg);
- Py_INCREF(value);
- STACK_GROW(1);
- POKE(1, value);
- DISPATCH();
- }
-
- TARGET(STORE_FAST) {
- PyObject *value = PEEK(1);
- SETLOCAL(oparg, value);
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(LOAD_FAST__LOAD_FAST) {
- PyObject *_tmp_1;
- PyObject *_tmp_2;
- {
- PyObject *value;
- value = GETLOCAL(oparg);
- assert(value != NULL);
- Py_INCREF(value);
- _tmp_2 = value;
- }
- NEXTOPARG();
- JUMPBY(1);
- {
- PyObject *value;
- value = GETLOCAL(oparg);
- assert(value != NULL);
- Py_INCREF(value);
- _tmp_1 = value;
- }
- STACK_GROW(2);
- POKE(1, _tmp_1);
- POKE(2, _tmp_2);
- DISPATCH();
- }
-
- TARGET(LOAD_FAST__LOAD_CONST) {
- PyObject *_tmp_1;
- PyObject *_tmp_2;
- {
- PyObject *value;
- value = GETLOCAL(oparg);
- assert(value != NULL);
- Py_INCREF(value);
- _tmp_2 = value;
- }
- NEXTOPARG();
- JUMPBY(1);
- {
- PyObject *value;
- value = GETITEM(consts, oparg);
- Py_INCREF(value);
- _tmp_1 = value;
- }
- STACK_GROW(2);
- POKE(1, _tmp_1);
- POKE(2, _tmp_2);
- DISPATCH();
- }
-
- TARGET(STORE_FAST__LOAD_FAST) {
- PyObject *_tmp_1 = PEEK(1);
- {
- PyObject *value = _tmp_1;
- SETLOCAL(oparg, value);
- }
- NEXTOPARG();
- JUMPBY(1);
- {
- PyObject *value;
- value = GETLOCAL(oparg);
- assert(value != NULL);
- Py_INCREF(value);
- _tmp_1 = value;
- }
- POKE(1, _tmp_1);
- DISPATCH();
- }
-
- TARGET(STORE_FAST__STORE_FAST) {
- PyObject *_tmp_1 = PEEK(1);
- PyObject *_tmp_2 = PEEK(2);
- {
- PyObject *value = _tmp_1;
- SETLOCAL(oparg, value);
- }
- NEXTOPARG();
- JUMPBY(1);
- {
- PyObject *value = _tmp_2;
- SETLOCAL(oparg, value);
- }
- STACK_SHRINK(2);
- DISPATCH();
- }
-
- TARGET(LOAD_CONST__LOAD_FAST) {
- PyObject *_tmp_1;
- PyObject *_tmp_2;
- {
- PyObject *value;
- value = GETITEM(consts, oparg);
- Py_INCREF(value);
- _tmp_2 = value;
- }
- NEXTOPARG();
- JUMPBY(1);
- {
- PyObject *value;
- value = GETLOCAL(oparg);
- assert(value != NULL);
- Py_INCREF(value);
- _tmp_1 = value;
- }
- STACK_GROW(2);
- POKE(1, _tmp_1);
- POKE(2, _tmp_2);
- DISPATCH();
- }
-
- TARGET(POP_TOP) {
- PyObject *value = PEEK(1);
- Py_DECREF(value);
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(PUSH_NULL) {
- PyObject *res;
- res = NULL;
- STACK_GROW(1);
- POKE(1, res);
- DISPATCH();
- }
-
- TARGET(END_FOR) {
- PyObject *_tmp_1 = PEEK(1);
- PyObject *_tmp_2 = PEEK(2);
- {
- PyObject *value = _tmp_1;
- Py_DECREF(value);
- }
- {
- PyObject *value = _tmp_2;
- Py_DECREF(value);
- }
- STACK_SHRINK(2);
- DISPATCH();
- }
-
- TARGET(UNARY_NEGATIVE) {
- PyObject *value = PEEK(1);
- PyObject *res;
- res = PyNumber_Negative(value);
- Py_DECREF(value);
- if (res == NULL) goto pop_1_error;
- POKE(1, res);
- DISPATCH();
- }
-
- TARGET(UNARY_NOT) {
- PyObject *value = PEEK(1);
- PyObject *res;
- int err = PyObject_IsTrue(value);
- Py_DECREF(value);
- if (err < 0) goto pop_1_error;
- if (err == 0) {
- res = Py_True;
- }
- else {
- res = Py_False;
- }
- Py_INCREF(res);
- POKE(1, res);
- DISPATCH();
- }
-
- TARGET(UNARY_INVERT) {
- PyObject *value = PEEK(1);
- PyObject *res;
- res = PyNumber_Invert(value);
- Py_DECREF(value);
- if (res == NULL) goto pop_1_error;
- POKE(1, res);
- DISPATCH();
- }
-
- TARGET(BINARY_OP_MULTIPLY_INT) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *prod;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
- DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
- STAT_INC(BINARY_OP, hit);
- prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
- if (prod == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, prod);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(BINARY_OP_MULTIPLY_FLOAT) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *prod;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
- DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
- STAT_INC(BINARY_OP, hit);
- double dprod = ((PyFloatObject *)left)->ob_fval *
- ((PyFloatObject *)right)->ob_fval;
- prod = PyFloat_FromDouble(dprod);
- _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
- _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
- if (prod == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, prod);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(BINARY_OP_SUBTRACT_INT) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *sub;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
- DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
- STAT_INC(BINARY_OP, hit);
- sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
- if (sub == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, sub);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(BINARY_OP_SUBTRACT_FLOAT) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *sub;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
- DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
- STAT_INC(BINARY_OP, hit);
- double dsub = ((PyFloatObject *)left)->ob_fval - ((PyFloatObject *)right)->ob_fval;
- sub = PyFloat_FromDouble(dsub);
- _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
- _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
- if (sub == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, sub);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(BINARY_OP_ADD_UNICODE) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *res;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP);
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
- STAT_INC(BINARY_OP, hit);
- res = PyUnicode_Concat(left, right);
- _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc);
- _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
- if (res == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, res);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(BINARY_OP_INPLACE_ADD_UNICODE) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP);
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
- _Py_CODEUNIT true_next = next_instr[INLINE_CACHE_ENTRIES_BINARY_OP];
- assert(_Py_OPCODE(true_next) == STORE_FAST ||
- _Py_OPCODE(true_next) == STORE_FAST__LOAD_FAST);
- PyObject **target_local = &GETLOCAL(_Py_OPARG(true_next));
- DEOPT_IF(*target_local != left, BINARY_OP);
- STAT_INC(BINARY_OP, hit);
- /* Handle `left = left + right` or `left += right` for str.
- *
- * When possible, extend `left` in place rather than
- * allocating a new PyUnicodeObject. This attempts to avoid
- * quadratic behavior when one neglects to use str.join().
- *
- * If `left` has only two references remaining (one from
- * the stack, one in the locals), DECREFing `left` leaves
- * only the locals reference, so PyUnicode_Append knows
- * that the string is safe to mutate.
- */
- assert(Py_REFCNT(left) >= 2);
- _Py_DECREF_NO_DEALLOC(left);
- PyUnicode_Append(target_local, right);
- _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
- if (*target_local == NULL) goto pop_2_error;
- // The STORE_FAST is already done.
- JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP + 1);
- STACK_SHRINK(2);
- DISPATCH();
- }
-
- TARGET(BINARY_OP_ADD_FLOAT) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *sum;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
- STAT_INC(BINARY_OP, hit);
- double dsum = ((PyFloatObject *)left)->ob_fval +
- ((PyFloatObject *)right)->ob_fval;
- sum = PyFloat_FromDouble(dsum);
- _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
- _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
- if (sum == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, sum);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(BINARY_OP_ADD_INT_TYPE_CHECK) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(BINARY_OP_ADD_INT_REST) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *sum;
- STAT_INC(BINARY_OP, hit);
- sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
- if (sum == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, sum);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(BINARY_SUBSCR) {
- PREDICTED(BINARY_SUBSCR);
- static_assert(INLINE_CACHE_ENTRIES_BINARY_SUBSCR == 4, "incorrect cache size");
- PyObject *sub = PEEK(1);
- PyObject *container = PEEK(2);
- PyObject *res;
- #if ENABLE_SPECIALIZATION
- _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr;
- if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
- assert(cframe.use_tracing == 0);
- next_instr--;
- _Py_Specialize_BinarySubscr(container, sub, next_instr);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(BINARY_SUBSCR, deferred);
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #endif /* ENABLE_SPECIALIZATION */
- res = PyObject_GetItem(container, sub);
- Py_DECREF(container);
- Py_DECREF(sub);
- if (res == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, res);
- JUMPBY(4);
- DISPATCH();
- }
-
- TARGET(BINARY_SLICE) {
- PyObject *stop = PEEK(1);
- PyObject *start = PEEK(2);
- PyObject *container = PEEK(3);
- PyObject *res;
- PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop);
- // Can't use ERROR_IF() here, because we haven't
- // DECREF'ed container yet, and we still own slice.
- if (slice == NULL) {
- res = NULL;
- }
- else {
- res = PyObject_GetItem(container, slice);
- Py_DECREF(slice);
- }
- Py_DECREF(container);
- if (res == NULL) goto pop_3_error;
- STACK_SHRINK(2);
- POKE(1, res);
- DISPATCH();
- }
-
- TARGET(STORE_SLICE) {
- PyObject *stop = PEEK(1);
- PyObject *start = PEEK(2);
- PyObject *container = PEEK(3);
- PyObject *v = PEEK(4);
- PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop);
- int err;
- if (slice == NULL) {
- err = 1;
- }
- else {
- err = PyObject_SetItem(container, slice, v);
- Py_DECREF(slice);
- }
- Py_DECREF(v);
- Py_DECREF(container);
- if (err) goto pop_4_error;
- STACK_SHRINK(4);
- DISPATCH();
- }
-
- TARGET(BINARY_SUBSCR_LIST_INT) {
- PyObject *sub = PEEK(1);
- PyObject *list = PEEK(2);
- PyObject *res;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
- DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
-
- // Deopt unless 0 <= sub < PyList_Size(list)
- DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
- assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0);
- Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
- DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
- STAT_INC(BINARY_SUBSCR, hit);
- res = PyList_GET_ITEM(list, index);
- assert(res != NULL);
- Py_INCREF(res);
- _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
- Py_DECREF(list);
- STACK_SHRINK(1);
- POKE(1, res);
- JUMPBY(4);
- DISPATCH();
- }
-
- TARGET(BINARY_SUBSCR_TUPLE_INT) {
- PyObject *sub = PEEK(1);
- PyObject *tuple = PEEK(2);
- PyObject *res;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
- DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR);
-
- // Deopt unless 0 <= sub < PyTuple_Size(list)
- DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
- assert(((PyLongObject *)_PyLong_GetZero())->ob_digit[0] == 0);
- Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
- DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR);
- STAT_INC(BINARY_SUBSCR, hit);
- res = PyTuple_GET_ITEM(tuple, index);
- assert(res != NULL);
- Py_INCREF(res);
- _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
- Py_DECREF(tuple);
- STACK_SHRINK(1);
- POKE(1, res);
- JUMPBY(4);
- DISPATCH();
- }
-
- TARGET(BINARY_SUBSCR_DICT) {
- PyObject *sub = PEEK(1);
- PyObject *dict = PEEK(2);
- PyObject *res;
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR);
- STAT_INC(BINARY_SUBSCR, hit);
- res = PyDict_GetItemWithError(dict, sub);
- if (res == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_SetKeyError(sub);
- }
- Py_DECREF(dict);
- Py_DECREF(sub);
- if (true) goto pop_2_error;
- }
- Py_INCREF(res); // Do this before DECREF'ing dict, sub
- Py_DECREF(dict);
- Py_DECREF(sub);
- STACK_SHRINK(1);
- POKE(1, res);
- JUMPBY(4);
- DISPATCH();
- }
-
- TARGET(BINARY_SUBSCR_GETITEM) {
- PyObject *sub = PEEK(1);
- PyObject *container = PEEK(2);
- uint32_t type_version = read_u32(&next_instr[1].cache);
- uint16_t func_version = read_u16(&next_instr[3].cache);
- PyTypeObject *tp = Py_TYPE(container);
- DEOPT_IF(tp->tp_version_tag != type_version, BINARY_SUBSCR);
- assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE);
- PyObject *cached = ((PyHeapTypeObject *)tp)->_spec_cache.getitem;
- assert(PyFunction_Check(cached));
- PyFunctionObject *getitem = (PyFunctionObject *)cached;
- DEOPT_IF(getitem->func_version != func_version, BINARY_SUBSCR);
- PyCodeObject *code = (PyCodeObject *)getitem->func_code;
- assert(code->co_argcount == 2);
- DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), BINARY_SUBSCR);
- STAT_INC(BINARY_SUBSCR, hit);
- Py_INCREF(getitem);
- _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, getitem, 2);
- STACK_SHRINK(2);
- new_frame->localsplus[0] = container;
- new_frame->localsplus[1] = sub;
- JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR);
- DISPATCH_INLINED(new_frame);
- }
-
- TARGET(LIST_APPEND) {
- PyObject *v = PEEK(1);
- PyObject *list = PEEK(2 + (oparg-1));
- if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error;
- STACK_SHRINK(1);
- PREDICT(JUMP_BACKWARD);
- DISPATCH();
- }
-
- TARGET(SET_ADD) {
- PyObject *v = PEEK(1);
- PyObject *set = PEEK(2 + (oparg-1));
- int err = PySet_Add(set, v);
- Py_DECREF(v);
- if (err) goto pop_1_error;
- STACK_SHRINK(1);
- PREDICT(JUMP_BACKWARD);
- DISPATCH();
- }
-
- TARGET(STORE_SUBSCR) {
- PREDICTED(STORE_SUBSCR);
- PyObject *sub = PEEK(1);
- PyObject *container = PEEK(2);
- PyObject *v = PEEK(3);
- uint16_t counter = read_u16(&next_instr[0].cache);
- #if ENABLE_SPECIALIZATION
- if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
- assert(cframe.use_tracing == 0);
- next_instr--;
- _Py_Specialize_StoreSubscr(container, sub, next_instr);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(STORE_SUBSCR, deferred);
- _PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr;
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #else
- (void)counter; // Unused.
- #endif /* ENABLE_SPECIALIZATION */
- /* container[sub] = v */
- int err = PyObject_SetItem(container, sub, v);
- Py_DECREF(v);
- Py_DECREF(container);
- Py_DECREF(sub);
- if (err) goto pop_3_error;
- STACK_SHRINK(3);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(STORE_SUBSCR_LIST_INT) {
- PyObject *sub = PEEK(1);
- PyObject *list = PEEK(2);
- PyObject *value = PEEK(3);
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR);
- DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
-
- // Ensure nonnegative, zero-or-one-digit ints.
- DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR);
- Py_ssize_t index = ((PyLongObject*)sub)->ob_digit[0];
- // Ensure index < len(list)
- DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR);
- STAT_INC(STORE_SUBSCR, hit);
-
- PyObject *old_value = PyList_GET_ITEM(list, index);
- PyList_SET_ITEM(list, index, value);
- assert(old_value != NULL);
- Py_DECREF(old_value);
- _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
- Py_DECREF(list);
- STACK_SHRINK(3);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(STORE_SUBSCR_DICT) {
- PyObject *sub = PEEK(1);
- PyObject *dict = PEEK(2);
- PyObject *value = PEEK(3);
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR);
- STAT_INC(STORE_SUBSCR, hit);
- int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value);
- Py_DECREF(dict);
- if (err) goto pop_3_error;
- STACK_SHRINK(3);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(DELETE_SUBSCR) {
- PyObject *sub = PEEK(1);
- PyObject *container = PEEK(2);
- /* del container[sub] */
- int err = PyObject_DelItem(container, sub);
- Py_DECREF(container);
- Py_DECREF(sub);
- if (err) goto pop_2_error;
- STACK_SHRINK(2);
- DISPATCH();
- }
-
- TARGET(CALL_INTRINSIC_1) {
- PyObject *value = PEEK(1);
- PyObject *res;
- assert(oparg <= MAX_INTRINSIC_1);
- res = _PyIntrinsics_UnaryFunctions[oparg](tstate, value);
- Py_DECREF(value);
- if (res == NULL) goto pop_1_error;
- POKE(1, res);
- DISPATCH();
- }
-
- TARGET(RAISE_VARARGS) {
- PyObject **args = &PEEK(oparg);
- PyObject *cause = NULL, *exc = NULL;
- switch (oparg) {
- case 2:
- cause = args[1];
- /* fall through */
- case 1:
- exc = args[0];
- /* fall through */
- case 0:
- if (do_raise(tstate, exc, cause)) { STACK_SHRINK(oparg); goto exception_unwind; }
- break;
- default:
- _PyErr_SetString(tstate, PyExc_SystemError,
- "bad RAISE_VARARGS oparg");
- break;
- }
- if (true) { STACK_SHRINK(oparg); goto error; }
- }
-
- TARGET(INTERPRETER_EXIT) {
- PyObject *retval = PEEK(1);
- assert(frame == &entry_frame);
- assert(_PyFrame_IsIncomplete(frame));
- STACK_SHRINK(1); // Since we're not going to DISPATCH()
- assert(EMPTY());
- /* Restore previous cframe and return. */
- tstate->cframe = cframe.previous;
- tstate->cframe->use_tracing = cframe.use_tracing;
- assert(tstate->cframe->current_frame == frame->previous);
- assert(!_PyErr_Occurred(tstate));
- _Py_LeaveRecursiveCallTstate(tstate);
- return retval;
- }
-
- TARGET(RETURN_VALUE) {
- PyObject *retval = PEEK(1);
- STACK_SHRINK(1);
- assert(EMPTY());
- _PyFrame_SetStackPointer(frame, stack_pointer);
- TRACE_FUNCTION_EXIT();
- DTRACE_FUNCTION_EXIT();
- _Py_LeaveRecursiveCallPy(tstate);
- assert(frame != &entry_frame);
- // GH-99729: We need to unlink the frame *before* clearing it:
- _PyInterpreterFrame *dying = frame;
- frame = cframe.current_frame = dying->previous;
- _PyEvalFrameClearAndPop(tstate, dying);
- _PyFrame_StackPush(frame, retval);
- goto resume_frame;
- }
-
- TARGET(GET_AITER) {
- PyObject *obj = PEEK(1);
- PyObject *iter;
- unaryfunc getter = NULL;
- PyTypeObject *type = Py_TYPE(obj);
-
- if (type->tp_as_async != NULL) {
- getter = type->tp_as_async->am_aiter;
- }
-
- if (getter == NULL) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'async for' requires an object with "
- "__aiter__ method, got %.100s",
- type->tp_name);
- Py_DECREF(obj);
- if (true) goto pop_1_error;
- }
-
- iter = (*getter)(obj);
- Py_DECREF(obj);
- if (iter == NULL) goto pop_1_error;
-
- if (Py_TYPE(iter)->tp_as_async == NULL ||
- Py_TYPE(iter)->tp_as_async->am_anext == NULL) {
-
- _PyErr_Format(tstate, PyExc_TypeError,
- "'async for' received an object from __aiter__ "
- "that does not implement __anext__: %.100s",
- Py_TYPE(iter)->tp_name);
- Py_DECREF(iter);
- if (true) goto pop_1_error;
- }
- POKE(1, iter);
- DISPATCH();
- }
-
- TARGET(GET_ANEXT) {
- PyObject *aiter = PEEK(1);
- PyObject *awaitable;
- unaryfunc getter = NULL;
- PyObject *next_iter = NULL;
- PyTypeObject *type = Py_TYPE(aiter);
-
- if (PyAsyncGen_CheckExact(aiter)) {
- awaitable = type->tp_as_async->am_anext(aiter);
- if (awaitable == NULL) {
- goto error;
- }
- } else {
- if (type->tp_as_async != NULL){
- getter = type->tp_as_async->am_anext;
- }
-
- if (getter != NULL) {
- next_iter = (*getter)(aiter);
- if (next_iter == NULL) {
- goto error;
- }
- }
- else {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'async for' requires an iterator with "
- "__anext__ method, got %.100s",
- type->tp_name);
- goto error;
- }
-
- awaitable = _PyCoro_GetAwaitableIter(next_iter);
- if (awaitable == NULL) {
- _PyErr_FormatFromCause(
- PyExc_TypeError,
- "'async for' received an invalid object "
- "from __anext__: %.100s",
- Py_TYPE(next_iter)->tp_name);
-
- Py_DECREF(next_iter);
- goto error;
- } else {
- Py_DECREF(next_iter);
- }
- }
-
- STACK_GROW(1);
- POKE(1, awaitable);
- PREDICT(LOAD_CONST);
- DISPATCH();
- }
-
- TARGET(GET_AWAITABLE) {
- PREDICTED(GET_AWAITABLE);
- PyObject *iterable = PEEK(1);
- PyObject *iter;
- iter = _PyCoro_GetAwaitableIter(iterable);
-
- if (iter == NULL) {
- format_awaitable_error(tstate, Py_TYPE(iterable), oparg);
- }
-
- Py_DECREF(iterable);
-
- if (iter != NULL && PyCoro_CheckExact(iter)) {
- PyObject *yf = _PyGen_yf((PyGenObject*)iter);
- if (yf != NULL) {
- /* `iter` is a coroutine object that is being
- awaited, `yf` is a pointer to the current awaitable
- being awaited on. */
- Py_DECREF(yf);
- Py_CLEAR(iter);
- _PyErr_SetString(tstate, PyExc_RuntimeError,
- "coroutine is being awaited already");
- /* The code below jumps to `error` if `iter` is NULL. */
- }
- }
-
- if (iter == NULL) goto pop_1_error;
-
- POKE(1, iter);
- PREDICT(LOAD_CONST);
- DISPATCH();
- }
-
- TARGET(SEND) {
- assert(frame != &entry_frame);
- assert(STACK_LEVEL() >= 2);
- PyObject *v = POP();
- PyObject *receiver = TOP();
- PySendResult gen_status;
- PyObject *retval;
- if (tstate->c_tracefunc == NULL) {
- gen_status = PyIter_Send(receiver, v, &retval);
- } else {
- if (Py_IsNone(v) && PyIter_Check(receiver)) {
- retval = Py_TYPE(receiver)->tp_iternext(receiver);
- }
- else {
- retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v);
- }
- if (retval == NULL) {
- if (tstate->c_tracefunc != NULL
- && _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
- call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
- if (_PyGen_FetchStopIterationValue(&retval) == 0) {
- gen_status = PYGEN_RETURN;
- }
- else {
- gen_status = PYGEN_ERROR;
- }
- }
- else {
- gen_status = PYGEN_NEXT;
- }
- }
- Py_DECREF(v);
- if (gen_status == PYGEN_ERROR) {
- assert(retval == NULL);
- goto error;
- }
- if (gen_status == PYGEN_RETURN) {
- assert(retval != NULL);
- Py_DECREF(receiver);
- SET_TOP(retval);
- JUMPBY(oparg);
- }
- else {
- assert(gen_status == PYGEN_NEXT);
- assert(retval != NULL);
- PUSH(retval);
- }
- DISPATCH();
- }
-
- TARGET(YIELD_VALUE) {
- PyObject *retval = PEEK(1);
- // NOTE: It's important that YIELD_VALUE never raises an exception!
- // The compiler treats any exception raised here as a failed close()
- // or throw() call.
- assert(frame != &entry_frame);
- PyGenObject *gen = _PyFrame_GetGenerator(frame);
- gen->gi_frame_state = FRAME_SUSPENDED;
- _PyFrame_SetStackPointer(frame, stack_pointer - 1);
- TRACE_FUNCTION_EXIT();
- DTRACE_FUNCTION_EXIT();
- tstate->exc_info = gen->gi_exc_state.previous_item;
- gen->gi_exc_state.previous_item = NULL;
- _Py_LeaveRecursiveCallPy(tstate);
- _PyInterpreterFrame *gen_frame = frame;
- frame = cframe.current_frame = frame->previous;
- gen_frame->previous = NULL;
- frame->prev_instr -= frame->yield_offset;
- _PyFrame_StackPush(frame, retval);
- goto resume_frame;
- }
-
- TARGET(POP_EXCEPT) {
- PyObject *exc_value = PEEK(1);
- _PyErr_StackItem *exc_info = tstate->exc_info;
- Py_XSETREF(exc_info->exc_value, exc_value);
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(RERAISE) {
- if (oparg) {
- PyObject *lasti = PEEK(oparg + 1);
- if (PyLong_Check(lasti)) {
- frame->prev_instr = _PyCode_CODE(frame->f_code) + PyLong_AsLong(lasti);
- assert(!_PyErr_Occurred(tstate));
- }
- else {
- assert(PyLong_Check(lasti));
- _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int");
- goto error;
- }
- }
- PyObject *val = POP();
- assert(val && PyExceptionInstance_Check(val));
- PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val));
- PyObject *tb = PyException_GetTraceback(val);
- _PyErr_Restore(tstate, exc, val, tb);
- goto exception_unwind;
- }
-
- TARGET(PREP_RERAISE_STAR) {
- PyObject *excs = PEEK(1);
- PyObject *orig = PEEK(2);
- PyObject *val;
- assert(PyList_Check(excs));
-
- val = _PyExc_PrepReraiseStar(orig, excs);
- Py_DECREF(orig);
- Py_DECREF(excs);
-
- if (val == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, val);
- DISPATCH();
- }
-
- TARGET(END_ASYNC_FOR) {
- PyObject *val = POP();
- assert(val && PyExceptionInstance_Check(val));
- if (PyErr_GivenExceptionMatches(val, PyExc_StopAsyncIteration)) {
- Py_DECREF(val);
- Py_DECREF(POP());
- }
- else {
- PyObject *exc = Py_NewRef(PyExceptionInstance_Class(val));
- PyObject *tb = PyException_GetTraceback(val);
- _PyErr_Restore(tstate, exc, val, tb);
- goto exception_unwind;
- }
- DISPATCH();
- }
-
- TARGET(CLEANUP_THROW) {
- assert(throwflag);
- PyObject *exc_value = TOP();
- assert(exc_value && PyExceptionInstance_Check(exc_value));
- if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) {
- PyObject *value = ((PyStopIterationObject *)exc_value)->value;
- Py_INCREF(value);
- Py_DECREF(POP()); // The StopIteration.
- Py_DECREF(POP()); // The last sent value.
- Py_DECREF(POP()); // The delegated sub-iterator.
- PUSH(value);
- }
- else {
- PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value));
- PyObject *exc_traceback = PyException_GetTraceback(exc_value);
- _PyErr_Restore(tstate, exc_type, Py_NewRef(exc_value), exc_traceback);
- goto exception_unwind;
- }
- DISPATCH();
- }
-
- TARGET(LOAD_ASSERTION_ERROR) {
- PyObject *value;
- value = Py_NewRef(PyExc_AssertionError);
- STACK_GROW(1);
- POKE(1, value);
- DISPATCH();
- }
-
- TARGET(LOAD_BUILD_CLASS) {
- PyObject *bc;
- if (PyDict_CheckExact(BUILTINS())) {
- bc = _PyDict_GetItemWithError(BUILTINS(),
- &_Py_ID(__build_class__));
- if (bc == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_SetString(tstate, PyExc_NameError,
- "__build_class__ not found");
- }
- if (true) goto error;
- }
- Py_INCREF(bc);
- }
- else {
- bc = PyObject_GetItem(BUILTINS(), &_Py_ID(__build_class__));
- if (bc == NULL) {
- if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError))
- _PyErr_SetString(tstate, PyExc_NameError,
- "__build_class__ not found");
- if (true) goto error;
- }
- }
- STACK_GROW(1);
- POKE(1, bc);
- DISPATCH();
- }
-
- TARGET(STORE_NAME) {
- PyObject *v = PEEK(1);
- PyObject *name = GETITEM(names, oparg);
- PyObject *ns = LOCALS();
- int err;
- if (ns == NULL) {
- _PyErr_Format(tstate, PyExc_SystemError,
- "no locals found when storing %R", name);
- Py_DECREF(v);
- if (true) goto pop_1_error;
- }
- if (PyDict_CheckExact(ns))
- err = PyDict_SetItem(ns, name, v);
- else
- err = PyObject_SetItem(ns, name, v);
- Py_DECREF(v);
- if (err) goto pop_1_error;
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(DELETE_NAME) {
- PyObject *name = GETITEM(names, oparg);
- PyObject *ns = LOCALS();
- int err;
- if (ns == NULL) {
- _PyErr_Format(tstate, PyExc_SystemError,
- "no locals when deleting %R", name);
- goto error;
- }
- err = PyObject_DelItem(ns, name);
- // Can't use ERROR_IF here.
- if (err != 0) {
- format_exc_check_arg(tstate, PyExc_NameError,
- NAME_ERROR_MSG,
- name);
- goto error;
- }
- DISPATCH();
- }
-
- TARGET(UNPACK_SEQUENCE) {
- PREDICTED(UNPACK_SEQUENCE);
- #if ENABLE_SPECIALIZATION
- _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr;
- if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
- assert(cframe.use_tracing == 0);
- PyObject *seq = TOP();
- next_instr--;
- _Py_Specialize_UnpackSequence(seq, next_instr, oparg);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(UNPACK_SEQUENCE, deferred);
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #endif /* ENABLE_SPECIALIZATION */
- PyObject *seq = POP();
- PyObject **top = stack_pointer + oparg;
- if (!unpack_iterable(tstate, seq, oparg, -1, top)) {
- Py_DECREF(seq);
- goto error;
- }
- STACK_GROW(oparg);
- Py_DECREF(seq);
- JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
- DISPATCH();
- }
-
- TARGET(UNPACK_SEQUENCE_TWO_TUPLE) {
- PyObject *seq = TOP();
- DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
- DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE);
- STAT_INC(UNPACK_SEQUENCE, hit);
- SET_TOP(Py_NewRef(PyTuple_GET_ITEM(seq, 1)));
- PUSH(Py_NewRef(PyTuple_GET_ITEM(seq, 0)));
- Py_DECREF(seq);
- JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
- DISPATCH();
- }
-
- TARGET(UNPACK_SEQUENCE_TUPLE) {
- PyObject *seq = TOP();
- DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
- DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE);
- STAT_INC(UNPACK_SEQUENCE, hit);
- STACK_SHRINK(1);
- PyObject **items = _PyTuple_ITEMS(seq);
- while (oparg--) {
- PUSH(Py_NewRef(items[oparg]));
- }
- Py_DECREF(seq);
- JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
- DISPATCH();
- }
-
- TARGET(UNPACK_SEQUENCE_LIST) {
- PyObject *seq = TOP();
- DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE);
- DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE);
- STAT_INC(UNPACK_SEQUENCE, hit);
- STACK_SHRINK(1);
- PyObject **items = _PyList_ITEMS(seq);
- while (oparg--) {
- PUSH(Py_NewRef(items[oparg]));
- }
- Py_DECREF(seq);
- JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
- DISPATCH();
- }
-
- TARGET(UNPACK_EX) {
- int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
- PyObject *seq = POP();
- PyObject **top = stack_pointer + totalargs;
- if (!unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top)) {
- Py_DECREF(seq);
- goto error;
- }
- STACK_GROW(totalargs);
- Py_DECREF(seq);
- DISPATCH();
- }
-
- TARGET(STORE_ATTR) {
- PREDICTED(STORE_ATTR);
- PyObject *owner = PEEK(1);
- PyObject *v = PEEK(2);
- uint16_t counter = read_u16(&next_instr[0].cache);
- #if ENABLE_SPECIALIZATION
- if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
- assert(cframe.use_tracing == 0);
- PyObject *name = GETITEM(names, oparg);
- next_instr--;
- _Py_Specialize_StoreAttr(owner, next_instr, name);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(STORE_ATTR, deferred);
- _PyAttrCache *cache = (_PyAttrCache *)next_instr;
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #else
- (void)counter; // Unused.
- #endif /* ENABLE_SPECIALIZATION */
- PyObject *name = GETITEM(names, oparg);
- int err = PyObject_SetAttr(owner, name, v);
- Py_DECREF(v);
- Py_DECREF(owner);
- if (err) goto pop_2_error;
- STACK_SHRINK(2);
- JUMPBY(4);
- DISPATCH();
- }
-
- TARGET(DELETE_ATTR) {
- PyObject *owner = PEEK(1);
- PyObject *name = GETITEM(names, oparg);
- int err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
- Py_DECREF(owner);
- if (err) goto pop_1_error;
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(STORE_GLOBAL) {
- PyObject *v = PEEK(1);
- PyObject *name = GETITEM(names, oparg);
- int err = PyDict_SetItem(GLOBALS(), name, v);
- Py_DECREF(v);
- if (err) goto pop_1_error;
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(DELETE_GLOBAL) {
- PyObject *name = GETITEM(names, oparg);
- int err;
- err = PyDict_DelItem(GLOBALS(), name);
- // Can't use ERROR_IF here.
- if (err != 0) {
- if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
- format_exc_check_arg(tstate, PyExc_NameError,
- NAME_ERROR_MSG, name);
- }
- goto error;
- }
- DISPATCH();
- }
-
- TARGET(LOAD_NAME) {
- PyObject *v;
- PyObject *name = GETITEM(names, oparg);
- PyObject *locals = LOCALS();
- if (locals == NULL) {
- _PyErr_Format(tstate, PyExc_SystemError,
- "no locals when loading %R", name);
- goto error;
- }
- if (PyDict_CheckExact(locals)) {
- v = PyDict_GetItemWithError(locals, name);
- if (v != NULL) {
- Py_INCREF(v);
- }
- else if (_PyErr_Occurred(tstate)) {
- goto error;
- }
- }
- else {
- v = PyObject_GetItem(locals, name);
- if (v == NULL) {
- if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError))
- goto error;
- _PyErr_Clear(tstate);
- }
- }
- if (v == NULL) {
- v = PyDict_GetItemWithError(GLOBALS(), name);
- if (v != NULL) {
- Py_INCREF(v);
- }
- else if (_PyErr_Occurred(tstate)) {
- goto error;
- }
- else {
- if (PyDict_CheckExact(BUILTINS())) {
- v = PyDict_GetItemWithError(BUILTINS(), name);
- if (v == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- format_exc_check_arg(
- tstate, PyExc_NameError,
- NAME_ERROR_MSG, name);
- }
- goto error;
- }
- Py_INCREF(v);
- }
- else {
- v = PyObject_GetItem(BUILTINS(), name);
- if (v == NULL) {
- if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
- format_exc_check_arg(
- tstate, PyExc_NameError,
- NAME_ERROR_MSG, name);
- }
- goto error;
- }
- }
- }
- }
- STACK_GROW(1);
- POKE(1, v);
- DISPATCH();
- }
-
- TARGET(LOAD_GLOBAL) {
- PREDICTED(LOAD_GLOBAL);
- #if ENABLE_SPECIALIZATION
- _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr;
- if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
- assert(cframe.use_tracing == 0);
- PyObject *name = GETITEM(names, oparg>>1);
- next_instr--;
- _Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(LOAD_GLOBAL, deferred);
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #endif /* ENABLE_SPECIALIZATION */
- int push_null = oparg & 1;
- PEEK(0) = NULL;
- PyObject *name = GETITEM(names, oparg>>1);
- PyObject *v;
- if (PyDict_CheckExact(GLOBALS())
- && PyDict_CheckExact(BUILTINS()))
- {
- v = _PyDict_LoadGlobal((PyDictObject *)GLOBALS(),
- (PyDictObject *)BUILTINS(),
- name);
- if (v == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- /* _PyDict_LoadGlobal() returns NULL without raising
- * an exception if the key doesn't exist */
- format_exc_check_arg(tstate, PyExc_NameError,
- NAME_ERROR_MSG, name);
- }
- goto error;
- }
- Py_INCREF(v);
- }
- else {
- /* Slow-path if globals or builtins is not a dict */
-
- /* namespace 1: globals */
- v = PyObject_GetItem(GLOBALS(), name);
- if (v == NULL) {
- if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
- goto error;
- }
- _PyErr_Clear(tstate);
-
- /* namespace 2: builtins */
- v = PyObject_GetItem(BUILTINS(), name);
- if (v == NULL) {
- if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
- format_exc_check_arg(
- tstate, PyExc_NameError,
- NAME_ERROR_MSG, name);
- }
- goto error;
- }
- }
- }
- /* Skip over inline cache */
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL);
- STACK_GROW(push_null);
- PUSH(v);
- DISPATCH();
- }
-
- TARGET(LOAD_GLOBAL_MODULE) {
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL);
- PyDictObject *dict = (PyDictObject *)GLOBALS();
- _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr;
- uint32_t version = read_u32(cache->module_keys_version);
- DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL);
- assert(DK_IS_UNICODE(dict->ma_keys));
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys);
- PyObject *res = entries[cache->index].me_value;
- DEOPT_IF(res == NULL, LOAD_GLOBAL);
- int push_null = oparg & 1;
- PEEK(0) = NULL;
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL);
- STAT_INC(LOAD_GLOBAL, hit);
- STACK_GROW(push_null+1);
- SET_TOP(Py_NewRef(res));
- DISPATCH();
- }
-
- TARGET(LOAD_GLOBAL_BUILTIN) {
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL);
- DEOPT_IF(!PyDict_CheckExact(BUILTINS()), LOAD_GLOBAL);
- PyDictObject *mdict = (PyDictObject *)GLOBALS();
- PyDictObject *bdict = (PyDictObject *)BUILTINS();
- _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr;
- uint32_t mod_version = read_u32(cache->module_keys_version);
- uint16_t bltn_version = cache->builtin_keys_version;
- DEOPT_IF(mdict->ma_keys->dk_version != mod_version, LOAD_GLOBAL);
- DEOPT_IF(bdict->ma_keys->dk_version != bltn_version, LOAD_GLOBAL);
- assert(DK_IS_UNICODE(bdict->ma_keys));
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys);
- PyObject *res = entries[cache->index].me_value;
- DEOPT_IF(res == NULL, LOAD_GLOBAL);
- int push_null = oparg & 1;
- PEEK(0) = NULL;
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_GLOBAL);
- STAT_INC(LOAD_GLOBAL, hit);
- STACK_GROW(push_null+1);
- SET_TOP(Py_NewRef(res));
- DISPATCH();
- }
-
- TARGET(DELETE_FAST) {
- PyObject *v = GETLOCAL(oparg);
- if (v == NULL) goto unbound_local_error;
- SETLOCAL(oparg, NULL);
- DISPATCH();
- }
-
- TARGET(MAKE_CELL) {
- // "initial" is probably NULL but not if it's an arg (or set
- // via PyFrame_LocalsToFast() before MAKE_CELL has run).
- PyObject *initial = GETLOCAL(oparg);
- PyObject *cell = PyCell_New(initial);
- if (cell == NULL) {
- goto resume_with_error;
- }
- SETLOCAL(oparg, cell);
- DISPATCH();
- }
-
- TARGET(DELETE_DEREF) {
- PyObject *cell = GETLOCAL(oparg);
- PyObject *oldobj = PyCell_GET(cell);
- // Can't use ERROR_IF here.
- // Fortunately we don't need its superpower.
- if (oldobj == NULL) {
- format_exc_unbound(tstate, frame->f_code, oparg);
- goto error;
- }
- PyCell_SET(cell, NULL);
- Py_DECREF(oldobj);
- DISPATCH();
- }
-
- TARGET(LOAD_CLASSDEREF) {
- PyObject *value;
- PyObject *name, *locals = LOCALS();
- assert(locals);
- assert(oparg >= 0 && oparg < frame->f_code->co_nlocalsplus);
- name = PyTuple_GET_ITEM(frame->f_code->co_localsplusnames, oparg);
- if (PyDict_CheckExact(locals)) {
- value = PyDict_GetItemWithError(locals, name);
- if (value != NULL) {
- Py_INCREF(value);
- }
- else if (_PyErr_Occurred(tstate)) {
- goto error;
- }
- }
- else {
- value = PyObject_GetItem(locals, name);
- if (value == NULL) {
- if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
- goto error;
- }
- _PyErr_Clear(tstate);
- }
- }
- if (!value) {
- PyObject *cell = GETLOCAL(oparg);
- value = PyCell_GET(cell);
- if (value == NULL) {
- format_exc_unbound(tstate, frame->f_code, oparg);
- goto error;
- }
- Py_INCREF(value);
- }
- STACK_GROW(1);
- POKE(1, value);
- DISPATCH();
- }
-
- TARGET(LOAD_DEREF) {
- PyObject *value;
- PyObject *cell = GETLOCAL(oparg);
- value = PyCell_GET(cell);
- if (value == NULL) {
- format_exc_unbound(tstate, frame->f_code, oparg);
- if (true) goto error;
- }
- Py_INCREF(value);
- STACK_GROW(1);
- POKE(1, value);
- DISPATCH();
- }
-
- TARGET(STORE_DEREF) {
- PyObject *v = PEEK(1);
- PyObject *cell = GETLOCAL(oparg);
- PyObject *oldobj = PyCell_GET(cell);
- PyCell_SET(cell, v);
- Py_XDECREF(oldobj);
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(COPY_FREE_VARS) {
- /* Copy closure variables to free variables */
- PyCodeObject *co = frame->f_code;
- assert(PyFunction_Check(frame->f_funcobj));
- PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure;
- assert(oparg == co->co_nfreevars);
- int offset = co->co_nlocalsplus - oparg;
- for (int i = 0; i < oparg; ++i) {
- PyObject *o = PyTuple_GET_ITEM(closure, i);
- frame->localsplus[offset + i] = Py_NewRef(o);
- }
- DISPATCH();
- }
-
- TARGET(BUILD_STRING) {
- PyObject **pieces = &PEEK(oparg);
- PyObject *str;
- str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg);
- for (int i = 0; i < oparg; i++) {
- Py_DECREF(pieces[i]);
- }
- if (str == NULL) { STACK_SHRINK(oparg); goto error; }
- STACK_SHRINK(oparg);
- STACK_GROW(1);
- POKE(1, str);
- DISPATCH();
- }
-
- TARGET(BUILD_TUPLE) {
- PyObject **values = &PEEK(oparg);
- PyObject *tup;
- tup = _PyTuple_FromArraySteal(values, oparg);
- if (tup == NULL) { STACK_SHRINK(oparg); goto error; }
- STACK_SHRINK(oparg);
- STACK_GROW(1);
- POKE(1, tup);
- DISPATCH();
- }
-
- TARGET(BUILD_LIST) {
- PyObject **values = &PEEK(oparg);
- PyObject *list;
- list = _PyList_FromArraySteal(values, oparg);
- if (list == NULL) { STACK_SHRINK(oparg); goto error; }
- STACK_SHRINK(oparg);
- STACK_GROW(1);
- POKE(1, list);
- DISPATCH();
- }
-
- TARGET(LIST_EXTEND) {
- PyObject *iterable = PEEK(1);
- PyObject *list = PEEK(2 + (oparg-1));
- PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable);
- if (none_val == NULL) {
- if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) &&
- (Py_TYPE(iterable)->tp_iter == NULL && !PySequence_Check(iterable)))
- {
- _PyErr_Clear(tstate);
- _PyErr_Format(tstate, PyExc_TypeError,
- "Value after * must be an iterable, not %.200s",
- Py_TYPE(iterable)->tp_name);
- }
- Py_DECREF(iterable);
- if (true) goto pop_1_error;
- }
- Py_DECREF(none_val);
- Py_DECREF(iterable);
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(SET_UPDATE) {
- PyObject *iterable = PEEK(1);
- PyObject *set = PEEK(2 + (oparg-1));
- int err = _PySet_Update(set, iterable);
- Py_DECREF(iterable);
- if (err < 0) goto pop_1_error;
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(BUILD_SET) {
- PyObject **values = &PEEK(oparg);
- PyObject *set;
- set = PySet_New(NULL);
- int err = 0;
- for (int i = 0; i < oparg; i++) {
- PyObject *item = values[i];
- if (err == 0)
- err = PySet_Add(set, item);
- Py_DECREF(item);
- }
- if (err != 0) {
- Py_DECREF(set);
- if (true) { STACK_SHRINK(oparg); goto error; }
- }
- STACK_SHRINK(oparg);
- STACK_GROW(1);
- POKE(1, set);
- DISPATCH();
- }
-
- TARGET(BUILD_MAP) {
- PyObject **values = &PEEK(oparg*2);
- PyObject *map;
- map = _PyDict_FromItems(
- values, 2,
- values+1, 2,
- oparg);
- if (map == NULL)
- goto error;
-
- for (int i = 0; i < oparg; i++) {
- Py_DECREF(values[i*2]);
- Py_DECREF(values[i*2+1]);
- }
- if (map == NULL) { STACK_SHRINK(oparg*2); goto error; }
- STACK_SHRINK(oparg*2);
- STACK_GROW(1);
- POKE(1, map);
- DISPATCH();
- }
-
- TARGET(SETUP_ANNOTATIONS) {
- int err;
- PyObject *ann_dict;
- if (LOCALS() == NULL) {
- _PyErr_Format(tstate, PyExc_SystemError,
- "no locals found when setting up annotations");
- if (true) goto error;
- }
- /* check if __annotations__ in locals()... */
- if (PyDict_CheckExact(LOCALS())) {
- ann_dict = _PyDict_GetItemWithError(LOCALS(),
- &_Py_ID(__annotations__));
- if (ann_dict == NULL) {
- if (_PyErr_Occurred(tstate)) goto error;
- /* ...if not, create a new one */
- ann_dict = PyDict_New();
- if (ann_dict == NULL) goto error;
- err = PyDict_SetItem(LOCALS(), &_Py_ID(__annotations__),
- ann_dict);
- Py_DECREF(ann_dict);
- if (err) goto error;
- }
- }
- else {
- /* do the same if locals() is not a dict */
- ann_dict = PyObject_GetItem(LOCALS(), &_Py_ID(__annotations__));
- if (ann_dict == NULL) {
- if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) goto error;
- _PyErr_Clear(tstate);
- ann_dict = PyDict_New();
- if (ann_dict == NULL) goto error;
- err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__),
- ann_dict);
- Py_DECREF(ann_dict);
- if (err) goto error;
- }
- else {
- Py_DECREF(ann_dict);
- }
- }
- DISPATCH();
- }
-
- TARGET(BUILD_CONST_KEY_MAP) {
- PyObject *keys = PEEK(1);
- PyObject **values = &PEEK(1 + oparg);
- PyObject *map;
- if (!PyTuple_CheckExact(keys) ||
- PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) {
- _PyErr_SetString(tstate, PyExc_SystemError,
- "bad BUILD_CONST_KEY_MAP keys argument");
- goto error; // Pop the keys and values.
- }
- map = _PyDict_FromItems(
- &PyTuple_GET_ITEM(keys, 0), 1,
- values, 1, oparg);
- Py_DECREF(keys);
- for (int i = 0; i < oparg; i++) {
- Py_DECREF(values[i]);
- }
- if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; }
- STACK_SHRINK(oparg);
- POKE(1, map);
- DISPATCH();
- }
-
- TARGET(DICT_UPDATE) {
- PyObject *update = PEEK(1);
- PyObject *dict = PEEK(oparg + 1); // update is still on the stack
- if (PyDict_Update(dict, update) < 0) {
- if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object is not a mapping",
- Py_TYPE(update)->tp_name);
- }
- Py_DECREF(update);
- if (true) goto pop_1_error;
- }
- Py_DECREF(update);
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(DICT_MERGE) {
- PyObject *update = PEEK(1);
- PyObject *dict = PEEK(oparg + 1); // update is still on the stack
-
- if (_PyDict_MergeEx(dict, update, 2) < 0) {
- format_kwargs_error(tstate, PEEK(3 + oparg), update);
- Py_DECREF(update);
- if (true) goto pop_1_error;
- }
- Py_DECREF(update);
- STACK_SHRINK(1);
- PREDICT(CALL_FUNCTION_EX);
- DISPATCH();
- }
-
- TARGET(MAP_ADD) {
- PyObject *value = PEEK(1);
- PyObject *key = PEEK(2);
- PyObject *dict = PEEK(oparg + 2); // key, value are still on the stack
- assert(PyDict_CheckExact(dict));
- /* dict[key] = value */
- // Do not DECREF INPUTS because the function steals the references
- if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error;
- STACK_SHRINK(2);
- PREDICT(JUMP_BACKWARD);
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR) {
- PREDICTED(LOAD_ATTR);
- #if ENABLE_SPECIALIZATION
- _PyAttrCache *cache = (_PyAttrCache *)next_instr;
- if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
- assert(cframe.use_tracing == 0);
- PyObject *owner = TOP();
- PyObject *name = GETITEM(names, oparg>>1);
- next_instr--;
- _Py_Specialize_LoadAttr(owner, next_instr, name);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(LOAD_ATTR, deferred);
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #endif /* ENABLE_SPECIALIZATION */
- PyObject *name = GETITEM(names, oparg >> 1);
- PyObject *owner = TOP();
- if (oparg & 1) {
- /* Designed to work in tandem with CALL. */
- PyObject* meth = NULL;
-
- int meth_found = _PyObject_GetMethod(owner, name, &meth);
-
- if (meth == NULL) {
- /* Most likely attribute wasn't found. */
- goto error;
- }
-
- if (meth_found) {
- /* We can bypass temporary bound method object.
- meth is unbound method and obj is self.
-
- meth | self | arg1 | ... | argN
- */
- SET_TOP(meth);
- PUSH(owner); // self
- }
- else {
- /* meth is not an unbound method (but a regular attr, or
- something was returned by a descriptor protocol). Set
- the second element of the stack to NULL, to signal
- CALL that it's not a method call.
-
- NULL | meth | arg1 | ... | argN
- */
- SET_TOP(NULL);
- Py_DECREF(owner);
- PUSH(meth);
- }
- }
- else {
- PyObject *res = PyObject_GetAttr(owner, name);
- if (res == NULL) {
- goto error;
- }
- Py_DECREF(owner);
- SET_TOP(res);
- }
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR_INSTANCE_VALUE) {
- assert(cframe.use_tracing == 0);
- PyObject *owner = TOP();
- PyObject *res;
- PyTypeObject *tp = Py_TYPE(owner);
- _PyAttrCache *cache = (_PyAttrCache *)next_instr;
- uint32_t type_version = read_u32(cache->version);
- assert(type_version != 0);
- DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
- assert(tp->tp_dictoffset < 0);
- assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
- res = _PyDictOrValues_GetValues(dorv)->values[cache->index];
- DEOPT_IF(res == NULL, LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
- Py_INCREF(res);
- SET_TOP(NULL);
- STACK_GROW((oparg & 1));
- SET_TOP(res);
- Py_DECREF(owner);
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR_MODULE) {
- assert(cframe.use_tracing == 0);
- PyObject *owner = TOP();
- PyObject *res;
- _PyAttrCache *cache = (_PyAttrCache *)next_instr;
- DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR);
- PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict;
- assert(dict != NULL);
- DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->version),
- LOAD_ATTR);
- assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
- assert(cache->index < dict->ma_keys->dk_nentries);
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + cache->index;
- res = ep->me_value;
- DEOPT_IF(res == NULL, LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
- Py_INCREF(res);
- SET_TOP(NULL);
- STACK_GROW((oparg & 1));
- SET_TOP(res);
- Py_DECREF(owner);
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR_WITH_HINT) {
- assert(cframe.use_tracing == 0);
- PyObject *owner = TOP();
- PyObject *res;
- PyTypeObject *tp = Py_TYPE(owner);
- _PyAttrCache *cache = (_PyAttrCache *)next_instr;
- uint32_t type_version = read_u32(cache->version);
- assert(type_version != 0);
- DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
- assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
- PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
- DEOPT_IF(dict == NULL, LOAD_ATTR);
- assert(PyDict_CheckExact((PyObject *)dict));
- PyObject *name = GETITEM(names, oparg>>1);
- uint16_t hint = cache->index;
- DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR);
- if (DK_IS_UNICODE(dict->ma_keys)) {
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
- DEOPT_IF(ep->me_key != name, LOAD_ATTR);
- res = ep->me_value;
- }
- else {
- PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint;
- DEOPT_IF(ep->me_key != name, LOAD_ATTR);
- res = ep->me_value;
- }
- DEOPT_IF(res == NULL, LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
- Py_INCREF(res);
- SET_TOP(NULL);
- STACK_GROW((oparg & 1));
- SET_TOP(res);
- Py_DECREF(owner);
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR_SLOT) {
- assert(cframe.use_tracing == 0);
- PyObject *owner = TOP();
- PyObject *res;
- PyTypeObject *tp = Py_TYPE(owner);
- _PyAttrCache *cache = (_PyAttrCache *)next_instr;
- uint32_t type_version = read_u32(cache->version);
- assert(type_version != 0);
- DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
- char *addr = (char *)owner + cache->index;
- res = *(PyObject **)addr;
- DEOPT_IF(res == NULL, LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
- Py_INCREF(res);
- SET_TOP(NULL);
- STACK_GROW((oparg & 1));
- SET_TOP(res);
- Py_DECREF(owner);
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR_CLASS) {
- assert(cframe.use_tracing == 0);
- _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
-
- PyObject *cls = TOP();
- DEOPT_IF(!PyType_Check(cls), LOAD_ATTR);
- uint32_t type_version = read_u32(cache->type_version);
- DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != type_version,
- LOAD_ATTR);
- assert(type_version != 0);
-
- STAT_INC(LOAD_ATTR, hit);
- PyObject *res = read_obj(cache->descr);
- assert(res != NULL);
- Py_INCREF(res);
- SET_TOP(NULL);
- STACK_GROW((oparg & 1));
- SET_TOP(res);
- Py_DECREF(cls);
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR_PROPERTY) {
- assert(cframe.use_tracing == 0);
- DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
- _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
-
- PyObject *owner = TOP();
- PyTypeObject *cls = Py_TYPE(owner);
- uint32_t type_version = read_u32(cache->type_version);
- DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
- assert(type_version != 0);
- PyObject *fget = read_obj(cache->descr);
- assert(Py_IS_TYPE(fget, &PyFunction_Type));
- PyFunctionObject *f = (PyFunctionObject *)fget;
- uint32_t func_version = read_u32(cache->keys_version);
- assert(func_version != 0);
- DEOPT_IF(f->func_version != func_version, LOAD_ATTR);
- PyCodeObject *code = (PyCodeObject *)f->func_code;
- assert(code->co_argcount == 1);
- DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
- Py_INCREF(fget);
- _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
- SET_TOP(NULL);
- int shrink_stack = !(oparg & 1);
- STACK_SHRINK(shrink_stack);
- new_frame->localsplus[0] = owner;
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH_INLINED(new_frame);
- }
-
- TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) {
- assert(cframe.use_tracing == 0);
- DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
- _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
- PyObject *owner = TOP();
- PyTypeObject *cls = Py_TYPE(owner);
- uint32_t type_version = read_u32(cache->type_version);
- DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
- assert(type_version != 0);
- PyObject *getattribute = read_obj(cache->descr);
- assert(Py_IS_TYPE(getattribute, &PyFunction_Type));
- PyFunctionObject *f = (PyFunctionObject *)getattribute;
- uint32_t func_version = read_u32(cache->keys_version);
- assert(func_version != 0);
- DEOPT_IF(f->func_version != func_version, LOAD_ATTR);
- PyCodeObject *code = (PyCodeObject *)f->func_code;
- assert(code->co_argcount == 2);
- DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
-
- PyObject *name = GETITEM(names, oparg >> 1);
- Py_INCREF(f);
- _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2);
- SET_TOP(NULL);
- int shrink_stack = !(oparg & 1);
- STACK_SHRINK(shrink_stack);
- new_frame->localsplus[0] = owner;
- new_frame->localsplus[1] = Py_NewRef(name);
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH_INLINED(new_frame);
- }
-
- TARGET(STORE_ATTR_INSTANCE_VALUE) {
- PyObject *owner = PEEK(1);
- PyObject *value = PEEK(2);
- uint32_t type_version = read_u32(&next_instr[1].cache);
- uint16_t index = read_u16(&next_instr[3].cache);
- assert(cframe.use_tracing == 0);
- PyTypeObject *tp = Py_TYPE(owner);
- assert(type_version != 0);
- DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
- assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR);
- STAT_INC(STORE_ATTR, hit);
- PyDictValues *values = _PyDictOrValues_GetValues(dorv);
- PyObject *old_value = values->values[index];
- values->values[index] = value;
- if (old_value == NULL) {
- _PyDictValues_AddToInsertionOrder(values, index);
- }
- else {
- Py_DECREF(old_value);
- }
- Py_DECREF(owner);
- STACK_SHRINK(2);
- JUMPBY(4);
- DISPATCH();
- }
-
- TARGET(STORE_ATTR_WITH_HINT) {
- PyObject *owner = PEEK(1);
- PyObject *value = PEEK(2);
- uint32_t type_version = read_u32(&next_instr[1].cache);
- uint16_t hint = read_u16(&next_instr[3].cache);
- assert(cframe.use_tracing == 0);
- PyTypeObject *tp = Py_TYPE(owner);
- assert(type_version != 0);
- DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
- assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
- DEOPT_IF(_PyDictOrValues_IsValues(dorv), STORE_ATTR);
- PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
- DEOPT_IF(dict == NULL, STORE_ATTR);
- assert(PyDict_CheckExact((PyObject *)dict));
- PyObject *name = GETITEM(names, oparg);
- DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR);
- PyObject *old_value;
- uint64_t new_version;
- if (DK_IS_UNICODE(dict->ma_keys)) {
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
- DEOPT_IF(ep->me_key != name, STORE_ATTR);
- old_value = ep->me_value;
- DEOPT_IF(old_value == NULL, STORE_ATTR);
- new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value);
- ep->me_value = value;
- }
- else {
- PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint;
- DEOPT_IF(ep->me_key != name, STORE_ATTR);
- old_value = ep->me_value;
- DEOPT_IF(old_value == NULL, STORE_ATTR);
- new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value);
- ep->me_value = value;
- }
- Py_DECREF(old_value);
- STAT_INC(STORE_ATTR, hit);
- /* Ensure dict is GC tracked if it needs to be */
- if (!_PyObject_GC_IS_TRACKED(dict) && _PyObject_GC_MAY_BE_TRACKED(value)) {
- _PyObject_GC_TRACK(dict);
- }
- /* PEP 509 */
- dict->ma_version_tag = new_version;
- Py_DECREF(owner);
- STACK_SHRINK(2);
- JUMPBY(4);
- DISPATCH();
- }
-
- TARGET(STORE_ATTR_SLOT) {
- PyObject *owner = PEEK(1);
- PyObject *value = PEEK(2);
- uint32_t type_version = read_u32(&next_instr[1].cache);
- uint16_t index = read_u16(&next_instr[3].cache);
- assert(cframe.use_tracing == 0);
- PyTypeObject *tp = Py_TYPE(owner);
- assert(type_version != 0);
- DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
- char *addr = (char *)owner + index;
- STAT_INC(STORE_ATTR, hit);
- PyObject *old_value = *(PyObject **)addr;
- *(PyObject **)addr = value;
- Py_XDECREF(old_value);
- Py_DECREF(owner);
- STACK_SHRINK(2);
- JUMPBY(4);
- DISPATCH();
- }
-
- TARGET(COMPARE_OP) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *res;
- STAT_INC(COMPARE_OP, deferred);
- assert((oparg >> 4) <= Py_GE);
- res = PyObject_RichCompare(left, right, oparg>>4);
- Py_DECREF(left);
- Py_DECREF(right);
- if (res == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, res);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(COMPARE_AND_BRANCH) {
- PREDICTED(COMPARE_AND_BRANCH);
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- #if ENABLE_SPECIALIZATION
- _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
- if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
- assert(cframe.use_tracing == 0);
- next_instr--;
- _Py_Specialize_CompareAndBranch(left, right, next_instr, oparg);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(COMPARE_AND_BRANCH, deferred);
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #endif /* ENABLE_SPECIALIZATION */
- assert((oparg >> 4) <= Py_GE);
- PyObject *cond = PyObject_RichCompare(left, right, oparg>>4);
- Py_DECREF(left);
- Py_DECREF(right);
- if (cond == NULL) goto pop_2_error;
- assert(_Py_OPCODE(next_instr[1]) == POP_JUMP_IF_FALSE ||
- _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE);
- bool jump_on_true = _Py_OPCODE(next_instr[1]) == POP_JUMP_IF_TRUE;
- int offset = _Py_OPARG(next_instr[1]);
- int err = PyObject_IsTrue(cond);
- Py_DECREF(cond);
- if (err < 0) {
- goto error;
- }
- if (jump_on_true == (err != 0)) {
- JUMPBY(offset);
- }
- STACK_SHRINK(2);
- JUMPBY(2);
- DISPATCH();
- }
-
- TARGET(COMPARE_AND_BRANCH_FLOAT) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_AND_BRANCH);
- DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_AND_BRANCH);
- STAT_INC(COMPARE_AND_BRANCH, hit);
- double dleft = PyFloat_AS_DOUBLE(left);
- double dright = PyFloat_AS_DOUBLE(right);
- // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg
- int sign_ish = COMPARISON_BIT(dleft, dright);
- _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
- _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
- if (sign_ish & oparg) {
- int offset = _Py_OPARG(next_instr[1]);
- JUMPBY(offset);
- }
- STACK_SHRINK(2);
- JUMPBY(2);
- DISPATCH();
- }
-
- TARGET(COMPARE_AND_BRANCH_INT) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH);
- DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH);
- DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_AND_BRANCH);
- DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH);
- STAT_INC(COMPARE_AND_BRANCH, hit);
- assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
- Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0];
- Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0];
- // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
- int sign_ish = COMPARISON_BIT(ileft, iright);
- _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
- _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- if (sign_ish & oparg) {
- int offset = _Py_OPARG(next_instr[1]);
- JUMPBY(offset);
- }
- STACK_SHRINK(2);
- JUMPBY(2);
- DISPATCH();
- }
-
- TARGET(COMPARE_AND_BRANCH_STR) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_AND_BRANCH);
- DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_AND_BRANCH);
- STAT_INC(COMPARE_AND_BRANCH, hit);
- int res = _PyUnicode_Equal(left, right);
- assert((oparg >>4) == Py_EQ || (oparg >>4) == Py_NE);
- _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc);
- _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
- assert(res == 0 || res == 1);
- assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS);
- assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS);
- if ((res + COMPARISON_NOT_EQUALS) & oparg) {
- int offset = _Py_OPARG(next_instr[1]);
- JUMPBY(offset);
- }
- STACK_SHRINK(2);
- JUMPBY(2);
- DISPATCH();
- }
-
- TARGET(IS_OP) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *b;
- int res = Py_Is(left, right) ^ oparg;
- Py_DECREF(left);
- Py_DECREF(right);
- b = Py_NewRef(res ? Py_True : Py_False);
- STACK_SHRINK(1);
- POKE(1, b);
- DISPATCH();
- }
-
- TARGET(CONTAINS_OP) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *b;
- int res = PySequence_Contains(right, left);
- Py_DECREF(left);
- Py_DECREF(right);
- if (res < 0) goto pop_2_error;
- b = Py_NewRef((res^oparg) ? Py_True : Py_False);
- STACK_SHRINK(1);
- POKE(1, b);
- DISPATCH();
- }
-
- TARGET(CHECK_EG_MATCH) {
- PyObject *match_type = PEEK(1);
- PyObject *exc_value = PEEK(2);
- PyObject *rest;
- PyObject *match;
- if (check_except_star_type_valid(tstate, match_type) < 0) {
- Py_DECREF(exc_value);
- Py_DECREF(match_type);
- if (true) goto pop_2_error;
- }
-
- match = NULL;
- rest = NULL;
- int res = exception_group_match(exc_value, match_type,
- &match, &rest);
- Py_DECREF(exc_value);
- Py_DECREF(match_type);
- if (res < 0) goto pop_2_error;
-
- assert((match == NULL) == (rest == NULL));
- if (match == NULL) goto pop_2_error;
-
- if (!Py_IsNone(match)) {
- PyErr_SetExcInfo(NULL, Py_NewRef(match), NULL);
- }
- POKE(1, match);
- POKE(2, rest);
- DISPATCH();
- }
-
- TARGET(CHECK_EXC_MATCH) {
- PyObject *right = PEEK(1);
- PyObject *left = PEEK(2);
- PyObject *b;
- assert(PyExceptionInstance_Check(left));
- if (check_except_type_valid(tstate, right) < 0) {
- Py_DECREF(right);
- if (true) goto pop_1_error;
- }
-
- int res = PyErr_GivenExceptionMatches(left, right);
- Py_DECREF(right);
- b = Py_NewRef(res ? Py_True : Py_False);
- POKE(1, b);
- DISPATCH();
- }
-
- TARGET(IMPORT_NAME) {
- PyObject *fromlist = PEEK(1);
- PyObject *level = PEEK(2);
- PyObject *res;
- PyObject *name = GETITEM(names, oparg);
- res = import_name(tstate, frame, name, fromlist, level);
- Py_DECREF(level);
- Py_DECREF(fromlist);
- if (res == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, res);
- DISPATCH();
- }
-
- TARGET(IMPORT_FROM) {
- PyObject *from = PEEK(1);
- PyObject *res;
- PyObject *name = GETITEM(names, oparg);
- res = import_from(tstate, from, name);
- if (res == NULL) goto error;
- STACK_GROW(1);
- POKE(1, res);
- DISPATCH();
- }
-
- TARGET(JUMP_FORWARD) {
- JUMPBY(oparg);
- DISPATCH();
- }
-
- TARGET(JUMP_BACKWARD) {
- PREDICTED(JUMP_BACKWARD);
- assert(oparg < INSTR_OFFSET());
- JUMPBY(-oparg);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(POP_JUMP_IF_FALSE) {
- PREDICTED(POP_JUMP_IF_FALSE);
- PyObject *cond = POP();
- if (Py_IsTrue(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- }
- else if (Py_IsFalse(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- JUMPBY(oparg);
- }
- else {
- int err = PyObject_IsTrue(cond);
- Py_DECREF(cond);
- if (err > 0)
- ;
- else if (err == 0) {
- JUMPBY(oparg);
- }
- else
- goto error;
- }
- DISPATCH();
- }
-
- TARGET(POP_JUMP_IF_TRUE) {
- PyObject *cond = POP();
- if (Py_IsFalse(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- }
- else if (Py_IsTrue(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- JUMPBY(oparg);
- }
- else {
- int err = PyObject_IsTrue(cond);
- Py_DECREF(cond);
- if (err > 0) {
- JUMPBY(oparg);
- }
- else if (err == 0)
- ;
- else
- goto error;
- }
- DISPATCH();
- }
-
- TARGET(POP_JUMP_IF_NOT_NONE) {
- PyObject *value = POP();
- if (!Py_IsNone(value)) {
- JUMPBY(oparg);
- }
- Py_DECREF(value);
- DISPATCH();
- }
-
- TARGET(POP_JUMP_IF_NONE) {
- PyObject *value = POP();
- if (Py_IsNone(value)) {
- _Py_DECREF_NO_DEALLOC(value);
- JUMPBY(oparg);
- }
- else {
- Py_DECREF(value);
- }
- DISPATCH();
- }
-
- TARGET(JUMP_IF_FALSE_OR_POP) {
- PyObject *cond = TOP();
- int err;
- if (Py_IsTrue(cond)) {
- STACK_SHRINK(1);
- _Py_DECREF_NO_DEALLOC(cond);
- }
- else if (Py_IsFalse(cond)) {
- JUMPBY(oparg);
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- STACK_SHRINK(1);
- Py_DECREF(cond);
- }
- else if (err == 0) {
- JUMPBY(oparg);
- }
- else {
- goto error;
- }
- }
- DISPATCH();
- }
-
- TARGET(JUMP_IF_TRUE_OR_POP) {
- PyObject *cond = TOP();
- int err;
- if (Py_IsFalse(cond)) {
- STACK_SHRINK(1);
- _Py_DECREF_NO_DEALLOC(cond);
- }
- else if (Py_IsTrue(cond)) {
- JUMPBY(oparg);
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- JUMPBY(oparg);
- }
- else if (err == 0) {
- STACK_SHRINK(1);
- Py_DECREF(cond);
- }
- else {
- goto error;
- }
- }
- DISPATCH();
- }
-
- TARGET(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).
- */
- JUMPBY(-oparg);
- DISPATCH();
- }
-
- TARGET(GET_LEN) {
- // PUSH(len(TOS))
- Py_ssize_t len_i = PyObject_Length(TOP());
- if (len_i < 0) {
- goto error;
- }
- PyObject *len_o = PyLong_FromSsize_t(len_i);
- if (len_o == NULL) {
- goto error;
- }
- PUSH(len_o);
- DISPATCH();
- }
-
- TARGET(MATCH_CLASS) {
- PyObject *names = PEEK(1);
- PyObject *type = PEEK(2);
- PyObject *subject = PEEK(3);
- PyObject *attrs;
- // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or
- // None on failure.
- assert(PyTuple_CheckExact(names));
- attrs = match_class(tstate, subject, type, oparg, names);
- Py_DECREF(subject);
- Py_DECREF(type);
- Py_DECREF(names);
- if (attrs) {
- assert(PyTuple_CheckExact(attrs)); // Success!
- }
- else {
- if (_PyErr_Occurred(tstate)) goto pop_3_error;
- attrs = Py_NewRef(Py_None); // Failure!
- }
- STACK_SHRINK(2);
- POKE(1, attrs);
- DISPATCH();
- }
-
- TARGET(MATCH_MAPPING) {
- PyObject *subject = PEEK(1);
- PyObject *res;
- int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING;
- res = Py_NewRef(match ? Py_True : Py_False);
- STACK_GROW(1);
- POKE(1, res);
- PREDICT(POP_JUMP_IF_FALSE);
- DISPATCH();
- }
-
- TARGET(MATCH_SEQUENCE) {
- PyObject *subject = PEEK(1);
- PyObject *res;
- int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE;
- res = Py_NewRef(match ? Py_True : Py_False);
- STACK_GROW(1);
- POKE(1, res);
- PREDICT(POP_JUMP_IF_FALSE);
- DISPATCH();
- }
-
- TARGET(MATCH_KEYS) {
- PyObject *keys = PEEK(1);
- PyObject *subject = PEEK(2);
- PyObject *values_or_none;
- // On successful match, PUSH(values). Otherwise, PUSH(None).
- values_or_none = match_keys(tstate, subject, keys);
- if (values_or_none == NULL) goto error;
- STACK_GROW(1);
- POKE(1, values_or_none);
- DISPATCH();
- }
-
- TARGET(GET_ITER) {
- /* before: [obj]; after [getiter(obj)] */
- PyObject *iterable = TOP();
- PyObject *iter = PyObject_GetIter(iterable);
- Py_DECREF(iterable);
- SET_TOP(iter);
- if (iter == NULL)
- goto error;
- DISPATCH();
- }
-
- TARGET(GET_YIELD_FROM_ITER) {
- /* before: [obj]; after [getiter(obj)] */
- PyObject *iterable = TOP();
- PyObject *iter;
- if (PyCoro_CheckExact(iterable)) {
- /* `iterable` is a coroutine */
- if (!(frame->f_code->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
- /* and it is used in a 'yield from' expression of a
- regular generator. */
- Py_DECREF(iterable);
- SET_TOP(NULL);
- _PyErr_SetString(tstate, PyExc_TypeError,
- "cannot 'yield from' a coroutine object "
- "in a non-coroutine generator");
- goto error;
- }
- }
- else if (!PyGen_CheckExact(iterable)) {
- /* `iterable` is not a generator. */
- iter = PyObject_GetIter(iterable);
- Py_DECREF(iterable);
- SET_TOP(iter);
- if (iter == NULL)
- goto error;
- }
- PREDICT(LOAD_CONST);
- DISPATCH();
- }
-
- TARGET(FOR_ITER) {
- PREDICTED(FOR_ITER);
- #if ENABLE_SPECIALIZATION
- _PyForIterCache *cache = (_PyForIterCache *)next_instr;
- if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
- assert(cframe.use_tracing == 0);
- next_instr--;
- _Py_Specialize_ForIter(TOP(), next_instr, oparg);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(FOR_ITER, deferred);
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #endif /* ENABLE_SPECIALIZATION */
- /* before: [iter]; after: [iter, iter()] *or* [] */
- PyObject *iter = TOP();
- PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter);
- if (next != NULL) {
- PUSH(next);
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
- }
- else {
- if (_PyErr_Occurred(tstate)) {
- if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
- goto error;
- }
- else if (tstate->c_tracefunc != NULL) {
- call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
- }
- _PyErr_Clear(tstate);
- }
- /* iterator ended normally */
- assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR);
- STACK_SHRINK(1);
- Py_DECREF(iter);
- /* Skip END_FOR */
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
- }
- DISPATCH();
- }
-
- TARGET(FOR_ITER_LIST) {
- assert(cframe.use_tracing == 0);
- _PyListIterObject *it = (_PyListIterObject *)TOP();
- DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER);
- STAT_INC(FOR_ITER, hit);
- PyListObject *seq = it->it_seq;
- if (seq) {
- if (it->it_index < PyList_GET_SIZE(seq)) {
- PyObject *next = PyList_GET_ITEM(seq, it->it_index++);
- PUSH(Py_NewRef(next));
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
- goto end_for_iter_list; // End of this instruction
- }
- it->it_seq = NULL;
- Py_DECREF(seq);
- }
- STACK_SHRINK(1);
- Py_DECREF(it);
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
- end_for_iter_list:
- DISPATCH();
- }
-
- TARGET(FOR_ITER_TUPLE) {
- assert(cframe.use_tracing == 0);
- _PyTupleIterObject *it = (_PyTupleIterObject *)TOP();
- DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER);
- STAT_INC(FOR_ITER, hit);
- PyTupleObject *seq = it->it_seq;
- if (seq) {
- if (it->it_index < PyTuple_GET_SIZE(seq)) {
- PyObject *next = PyTuple_GET_ITEM(seq, it->it_index++);
- PUSH(Py_NewRef(next));
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
- goto end_for_iter_tuple; // End of this instruction
- }
- it->it_seq = NULL;
- Py_DECREF(seq);
- }
- STACK_SHRINK(1);
- Py_DECREF(it);
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
- end_for_iter_tuple:
- DISPATCH();
- }
-
- TARGET(FOR_ITER_RANGE) {
- assert(cframe.use_tracing == 0);
- _PyRangeIterObject *r = (_PyRangeIterObject *)TOP();
- DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER);
- STAT_INC(FOR_ITER, hit);
- _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER];
- assert(_PyOpcode_Deopt[_Py_OPCODE(next)] == STORE_FAST);
- if (r->len <= 0) {
- STACK_SHRINK(1);
- Py_DECREF(r);
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1);
- }
- else {
- long value = r->start;
- r->start = value + r->step;
- r->len--;
- if (_PyLong_AssignValue(&GETLOCAL(_Py_OPARG(next)), value) < 0) {
- goto error;
- }
- // The STORE_FAST is already done.
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + 1);
- }
- DISPATCH();
- }
-
- TARGET(FOR_ITER_GEN) {
- assert(cframe.use_tracing == 0);
- PyGenObject *gen = (PyGenObject *)TOP();
- DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER);
- DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER);
- STAT_INC(FOR_ITER, hit);
- _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
- frame->yield_offset = oparg;
- _PyFrame_StackPush(gen_frame, Py_NewRef(Py_None));
- gen->gi_frame_state = FRAME_EXECUTING;
- gen->gi_exc_state.previous_item = tstate->exc_info;
- tstate->exc_info = &gen->gi_exc_state;
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg);
- assert(_Py_OPCODE(*next_instr) == END_FOR);
- DISPATCH_INLINED(gen_frame);
- }
-
- TARGET(BEFORE_ASYNC_WITH) {
- PyObject *mgr = TOP();
- PyObject *res;
- PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__));
- if (enter == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "asynchronous context manager protocol",
- Py_TYPE(mgr)->tp_name);
- }
- goto error;
- }
- PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__));
- if (exit == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "asynchronous context manager protocol "
- "(missed __aexit__ method)",
- Py_TYPE(mgr)->tp_name);
- }
- Py_DECREF(enter);
- goto error;
- }
- SET_TOP(exit);
- Py_DECREF(mgr);
- res = _PyObject_CallNoArgs(enter);
- Py_DECREF(enter);
- if (res == NULL)
- goto error;
- PUSH(res);
- PREDICT(GET_AWAITABLE);
- DISPATCH();
- }
-
- TARGET(BEFORE_WITH) {
- PyObject *mgr = TOP();
- PyObject *res;
- PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__));
- if (enter == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "context manager protocol",
- Py_TYPE(mgr)->tp_name);
- }
- goto error;
- }
- PyObject *exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__));
- if (exit == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "context manager protocol "
- "(missed __exit__ method)",
- Py_TYPE(mgr)->tp_name);
- }
- Py_DECREF(enter);
- goto error;
- }
- SET_TOP(exit);
- Py_DECREF(mgr);
- res = _PyObject_CallNoArgs(enter);
- Py_DECREF(enter);
- if (res == NULL) {
- goto error;
- }
- PUSH(res);
- DISPATCH();
- }
-
- TARGET(WITH_EXCEPT_START) {
- PyObject *val = PEEK(1);
- PyObject *lasti = PEEK(3);
- PyObject *exit_func = PEEK(4);
- PyObject *res;
- /* At the top of the stack are 4 values:
- - val: TOP = exc_info()
- - unused: SECOND = previous exception
- - lasti: THIRD = lasti of exception in exc_info()
- - exit_func: FOURTH = the context.__exit__ bound method
- We call FOURTH(type(TOP), TOP, GetTraceback(TOP)).
- Then we push the __exit__ return value.
- */
- PyObject *exc, *tb;
-
- assert(val && PyExceptionInstance_Check(val));
- exc = PyExceptionInstance_Class(val);
- tb = PyException_GetTraceback(val);
- Py_XDECREF(tb);
- assert(PyLong_Check(lasti));
- (void)lasti; // Shut up compiler warning if asserts are off
- PyObject *stack[4] = {NULL, exc, val, tb};
- res = PyObject_Vectorcall(exit_func, stack + 1,
- 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
- if (res == NULL) goto error;
- STACK_GROW(1);
- POKE(1, res);
- DISPATCH();
- }
-
- TARGET(PUSH_EXC_INFO) {
- PyObject *value = TOP();
-
- _PyErr_StackItem *exc_info = tstate->exc_info;
- if (exc_info->exc_value != NULL) {
- SET_TOP(exc_info->exc_value);
- }
- else {
- SET_TOP(Py_NewRef(Py_None));
- }
-
- PUSH(Py_NewRef(value));
- assert(PyExceptionInstance_Check(value));
- exc_info->exc_value = value;
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
- /* Cached method object */
- assert(cframe.use_tracing == 0);
- PyObject *self = TOP();
- PyTypeObject *self_cls = Py_TYPE(self);
- _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
- uint32_t type_version = read_u32(cache->type_version);
- assert(type_version != 0);
- DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
- assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(self);
- DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
- PyHeapTypeObject *self_heap_type = (PyHeapTypeObject *)self_cls;
- DEOPT_IF(self_heap_type->ht_cached_keys->dk_version !=
- read_u32(cache->keys_version), LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
- PyObject *res = read_obj(cache->descr);
- assert(res != NULL);
- assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
- SET_TOP(Py_NewRef(res));
- PUSH(self);
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR_METHOD_NO_DICT) {
- assert(cframe.use_tracing == 0);
- PyObject *self = TOP();
- PyTypeObject *self_cls = Py_TYPE(self);
- _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
- uint32_t type_version = read_u32(cache->type_version);
- DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
- assert(self_cls->tp_dictoffset == 0);
- STAT_INC(LOAD_ATTR, hit);
- PyObject *res = read_obj(cache->descr);
- assert(res != NULL);
- assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
- SET_TOP(Py_NewRef(res));
- PUSH(self);
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH();
- }
-
- TARGET(LOAD_ATTR_METHOD_LAZY_DICT) {
- assert(cframe.use_tracing == 0);
- PyObject *self = TOP();
- PyTypeObject *self_cls = Py_TYPE(self);
- _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
- uint32_t type_version = read_u32(cache->type_version);
- DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
- Py_ssize_t dictoffset = self_cls->tp_dictoffset;
- assert(dictoffset > 0);
- PyObject *dict = *(PyObject **)((char *)self + dictoffset);
- /* This object has a __dict__, just not yet created */
- DEOPT_IF(dict != NULL, LOAD_ATTR);
- STAT_INC(LOAD_ATTR, hit);
- PyObject *res = read_obj(cache->descr);
- assert(res != NULL);
- assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
- SET_TOP(Py_NewRef(res));
- PUSH(self);
- JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
- DISPATCH();
- }
-
- TARGET(CALL_BOUND_METHOD_EXACT_ARGS) {
- DEOPT_IF(is_method(stack_pointer, oparg), CALL);
- PyObject *function = PEEK(oparg + 1);
- DEOPT_IF(Py_TYPE(function) != &PyMethod_Type, CALL);
- STAT_INC(CALL, hit);
- PyObject *self = ((PyMethodObject *)function)->im_self;
- PEEK(oparg + 1) = Py_NewRef(self);
- PyObject *meth = ((PyMethodObject *)function)->im_func;
- PEEK(oparg + 2) = Py_NewRef(meth);
- Py_DECREF(function);
- GO_TO_INSTRUCTION(CALL_PY_EXACT_ARGS);
- }
-
- TARGET(KW_NAMES) {
- assert(kwnames == NULL);
- assert(oparg < PyTuple_GET_SIZE(consts));
- kwnames = GETITEM(consts, oparg);
- DISPATCH();
- }
-
- TARGET(CALL) {
- PREDICTED(CALL);
- #if ENABLE_SPECIALIZATION
- _PyCallCache *cache = (_PyCallCache *)next_instr;
- if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
- assert(cframe.use_tracing == 0);
- int is_meth = is_method(stack_pointer, oparg);
- int nargs = oparg + is_meth;
- PyObject *callable = PEEK(nargs + 1);
- next_instr--;
- _Py_Specialize_Call(callable, next_instr, nargs, kwnames);
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(CALL, deferred);
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #endif /* ENABLE_SPECIALIZATION */
- int total_args, is_meth;
- is_meth = is_method(stack_pointer, oparg);
- PyObject *function = PEEK(oparg + 1);
- if (!is_meth && Py_TYPE(function) == &PyMethod_Type) {
- PyObject *self = ((PyMethodObject *)function)->im_self;
- PEEK(oparg+1) = Py_NewRef(self);
- PyObject *meth = ((PyMethodObject *)function)->im_func;
- PEEK(oparg+2) = Py_NewRef(meth);
- Py_DECREF(function);
- is_meth = 1;
- }
- total_args = oparg + is_meth;
- function = PEEK(total_args + 1);
- int positional_args = total_args - KWNAMES_LEN();
- // Check if the call can be inlined or not
- if (Py_TYPE(function) == &PyFunction_Type &&
- tstate->interp->eval_frame == NULL &&
- ((PyFunctionObject *)function)->vectorcall == _PyFunction_Vectorcall)
- {
- int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags;
- PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(function));
- STACK_SHRINK(total_args);
- _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit(
- tstate, (PyFunctionObject *)function, locals,
- stack_pointer, positional_args, kwnames
- );
- kwnames = NULL;
- STACK_SHRINK(2-is_meth);
- // The frame has stolen all the arguments from the stack,
- // so there is no need to clean them up.
- if (new_frame == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- DISPATCH_INLINED(new_frame);
- }
- /* Callable is not a normal Python function */
- PyObject *res;
- if (cframe.use_tracing) {
- res = trace_call_function(
- tstate, function, stack_pointer-total_args,
- positional_args, kwnames);
- }
- else {
- res = PyObject_Vectorcall(
- function, stack_pointer-total_args,
- positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET,
- kwnames);
- }
- kwnames = NULL;
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
- Py_DECREF(function);
- /* Clear the stack */
- STACK_SHRINK(total_args);
- for (int i = 0; i < total_args; i++) {
- Py_DECREF(stack_pointer[i]);
- }
- STACK_SHRINK(2-is_meth);
- PUSH(res);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_PY_EXACT_ARGS) {
- PREDICTED(CALL_PY_EXACT_ARGS);
- assert(kwnames == NULL);
- DEOPT_IF(tstate->interp->eval_frame, CALL);
- _PyCallCache *cache = (_PyCallCache *)next_instr;
- int is_meth = is_method(stack_pointer, oparg);
- int argcount = oparg + is_meth;
- PyObject *callable = PEEK(argcount + 1);
- DEOPT_IF(!PyFunction_Check(callable), CALL);
- PyFunctionObject *func = (PyFunctionObject *)callable;
- DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL);
- PyCodeObject *code = (PyCodeObject *)func->func_code;
- DEOPT_IF(code->co_argcount != argcount, CALL);
- DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL);
- STAT_INC(CALL, hit);
- _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, argcount);
- STACK_SHRINK(argcount);
- for (int i = 0; i < argcount; i++) {
- new_frame->localsplus[i] = stack_pointer[i];
- }
- STACK_SHRINK(2-is_meth);
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- DISPATCH_INLINED(new_frame);
- }
-
- TARGET(CALL_PY_WITH_DEFAULTS) {
- assert(kwnames == NULL);
- DEOPT_IF(tstate->interp->eval_frame, CALL);
- _PyCallCache *cache = (_PyCallCache *)next_instr;
- int is_meth = is_method(stack_pointer, oparg);
- int argcount = oparg + is_meth;
- PyObject *callable = PEEK(argcount + 1);
- DEOPT_IF(!PyFunction_Check(callable), CALL);
- PyFunctionObject *func = (PyFunctionObject *)callable;
- DEOPT_IF(func->func_version != read_u32(cache->func_version), CALL);
- PyCodeObject *code = (PyCodeObject *)func->func_code;
- DEOPT_IF(argcount > code->co_argcount, CALL);
- int minargs = cache->min_args;
- DEOPT_IF(argcount < minargs, CALL);
- DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL);
- STAT_INC(CALL, hit);
- _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, code->co_argcount);
- STACK_SHRINK(argcount);
- for (int i = 0; i < argcount; i++) {
- new_frame->localsplus[i] = stack_pointer[i];
- }
- for (int i = argcount; i < code->co_argcount; i++) {
- PyObject *def = PyTuple_GET_ITEM(func->func_defaults,
- i - minargs);
- new_frame->localsplus[i] = Py_NewRef(def);
- }
- STACK_SHRINK(2-is_meth);
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- DISPATCH_INLINED(new_frame);
- }
-
- TARGET(CALL_NO_KW_TYPE_1) {
- assert(kwnames == NULL);
- assert(cframe.use_tracing == 0);
- assert(oparg == 1);
- DEOPT_IF(is_method(stack_pointer, 1), CALL);
- PyObject *obj = TOP();
- PyObject *callable = SECOND();
- DEOPT_IF(callable != (PyObject *)&PyType_Type, CALL);
- STAT_INC(CALL, hit);
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- PyObject *res = Py_NewRef(Py_TYPE(obj));
- Py_DECREF(callable);
- Py_DECREF(obj);
- STACK_SHRINK(2);
- SET_TOP(res);
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_STR_1) {
- assert(kwnames == NULL);
- assert(cframe.use_tracing == 0);
- assert(oparg == 1);
- DEOPT_IF(is_method(stack_pointer, 1), CALL);
- PyObject *callable = PEEK(2);
- DEOPT_IF(callable != (PyObject *)&PyUnicode_Type, CALL);
- STAT_INC(CALL, hit);
- PyObject *arg = TOP();
- PyObject *res = PyObject_Str(arg);
- Py_DECREF(arg);
- Py_DECREF(&PyUnicode_Type);
- STACK_SHRINK(2);
- SET_TOP(res);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_TUPLE_1) {
- assert(kwnames == NULL);
- assert(oparg == 1);
- DEOPT_IF(is_method(stack_pointer, 1), CALL);
- PyObject *callable = PEEK(2);
- DEOPT_IF(callable != (PyObject *)&PyTuple_Type, CALL);
- STAT_INC(CALL, hit);
- PyObject *arg = TOP();
- PyObject *res = PySequence_Tuple(arg);
- Py_DECREF(arg);
- Py_DECREF(&PyTuple_Type);
- STACK_SHRINK(2);
- SET_TOP(res);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_BUILTIN_CLASS) {
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- int kwnames_len = KWNAMES_LEN();
- PyObject *callable = PEEK(total_args + 1);
- DEOPT_IF(!PyType_Check(callable), CALL);
- PyTypeObject *tp = (PyTypeObject *)callable;
- DEOPT_IF(tp->tp_vectorcall == NULL, CALL);
- STAT_INC(CALL, hit);
- STACK_SHRINK(total_args);
- PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer,
- total_args-kwnames_len, kwnames);
- kwnames = NULL;
- /* Free the arguments. */
- for (int i = 0; i < total_args; i++) {
- Py_DECREF(stack_pointer[i]);
- }
- Py_DECREF(tp);
- STACK_SHRINK(1-is_meth);
- SET_TOP(res);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_BUILTIN_O) {
- assert(cframe.use_tracing == 0);
- /* Builtin METH_O functions */
- assert(kwnames == NULL);
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- DEOPT_IF(total_args != 1, CALL);
- PyObject *callable = PEEK(total_args + 1);
- DEOPT_IF(!PyCFunction_CheckExact(callable), CALL);
- DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL);
- STAT_INC(CALL, hit);
- PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable);
- // This is slower but CPython promises to check all non-vectorcall
- // function calls.
- if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) {
- goto error;
- }
- PyObject *arg = TOP();
- PyObject *res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg);
- _Py_LeaveRecursiveCallTstate(tstate);
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
-
- Py_DECREF(arg);
- Py_DECREF(callable);
- STACK_SHRINK(2-is_meth);
- SET_TOP(res);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_BUILTIN_FAST) {
- assert(cframe.use_tracing == 0);
- /* Builtin METH_FASTCALL functions, without keywords */
- assert(kwnames == NULL);
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- PyObject *callable = PEEK(total_args + 1);
- DEOPT_IF(!PyCFunction_CheckExact(callable), CALL);
- DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL,
- CALL);
- STAT_INC(CALL, hit);
- PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable);
- STACK_SHRINK(total_args);
- /* res = func(self, args, nargs) */
- PyObject *res = ((_PyCFunctionFast)(void(*)(void))cfunc)(
- PyCFunction_GET_SELF(callable),
- stack_pointer,
- total_args);
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
-
- /* Free the arguments. */
- for (int i = 0; i < total_args; i++) {
- Py_DECREF(stack_pointer[i]);
- }
- STACK_SHRINK(2-is_meth);
- PUSH(res);
- Py_DECREF(callable);
- if (res == NULL) {
- /* Not deopting because this doesn't mean our optimization was
- wrong. `res` can be NULL for valid reasons. Eg. getattr(x,
- 'invalid'). In those cases an exception is set, so we must
- handle it.
- */
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) {
- assert(cframe.use_tracing == 0);
- /* Builtin METH_FASTCALL | METH_KEYWORDS functions */
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- PyObject *callable = PEEK(total_args + 1);
- DEOPT_IF(!PyCFunction_CheckExact(callable), CALL);
- DEOPT_IF(PyCFunction_GET_FLAGS(callable) !=
- (METH_FASTCALL | METH_KEYWORDS), CALL);
- STAT_INC(CALL, hit);
- STACK_SHRINK(total_args);
- /* res = func(self, args, nargs, kwnames) */
- _PyCFunctionFastWithKeywords cfunc =
- (_PyCFunctionFastWithKeywords)(void(*)(void))
- PyCFunction_GET_FUNCTION(callable);
- PyObject *res = cfunc(
- PyCFunction_GET_SELF(callable),
- stack_pointer,
- total_args - KWNAMES_LEN(),
- kwnames
- );
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
- kwnames = NULL;
-
- /* Free the arguments. */
- for (int i = 0; i < total_args; i++) {
- Py_DECREF(stack_pointer[i]);
- }
- STACK_SHRINK(2-is_meth);
- PUSH(res);
- Py_DECREF(callable);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_LEN) {
- assert(cframe.use_tracing == 0);
- assert(kwnames == NULL);
- /* len(o) */
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- DEOPT_IF(total_args != 1, CALL);
- PyObject *callable = PEEK(total_args + 1);
- PyInterpreterState *interp = _PyInterpreterState_GET();
- DEOPT_IF(callable != interp->callable_cache.len, CALL);
- STAT_INC(CALL, hit);
- PyObject *arg = TOP();
- Py_ssize_t len_i = PyObject_Length(arg);
- if (len_i < 0) {
- goto error;
- }
- PyObject *res = PyLong_FromSsize_t(len_i);
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
-
- STACK_SHRINK(2-is_meth);
- SET_TOP(res);
- Py_DECREF(callable);
- Py_DECREF(arg);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_ISINSTANCE) {
- assert(cframe.use_tracing == 0);
- assert(kwnames == NULL);
- /* isinstance(o, o2) */
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- PyObject *callable = PEEK(total_args + 1);
- DEOPT_IF(total_args != 2, CALL);
- PyInterpreterState *interp = _PyInterpreterState_GET();
- DEOPT_IF(callable != interp->callable_cache.isinstance, CALL);
- STAT_INC(CALL, hit);
- PyObject *cls = POP();
- PyObject *inst = TOP();
- int retval = PyObject_IsInstance(inst, cls);
- if (retval < 0) {
- Py_DECREF(cls);
- goto error;
- }
- PyObject *res = PyBool_FromLong(retval);
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
-
- STACK_SHRINK(2-is_meth);
- SET_TOP(res);
- Py_DECREF(inst);
- Py_DECREF(cls);
- Py_DECREF(callable);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_LIST_APPEND) {
- assert(cframe.use_tracing == 0);
- assert(kwnames == NULL);
- assert(oparg == 1);
- PyObject *callable = PEEK(3);
- PyInterpreterState *interp = _PyInterpreterState_GET();
- DEOPT_IF(callable != interp->callable_cache.list_append, CALL);
- PyObject *list = SECOND();
- DEOPT_IF(!PyList_Check(list), CALL);
- STAT_INC(CALL, hit);
- PyObject *arg = POP();
- if (_PyList_AppendTakeRef((PyListObject *)list, arg) < 0) {
- goto error;
- }
- STACK_SHRINK(2);
- Py_DECREF(list);
- Py_DECREF(callable);
- // CALL + POP_TOP
- JUMPBY(INLINE_CACHE_ENTRIES_CALL + 1);
- assert(_Py_OPCODE(next_instr[-1]) == POP_TOP);
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) {
- assert(kwnames == NULL);
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- PyMethodDescrObject *callable =
- (PyMethodDescrObject *)PEEK(total_args + 1);
- DEOPT_IF(total_args != 2, CALL);
- DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
- PyMethodDef *meth = callable->d_method;
- DEOPT_IF(meth->ml_flags != METH_O, CALL);
- PyObject *arg = TOP();
- PyObject *self = SECOND();
- DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL);
- STAT_INC(CALL, hit);
- PyCFunction cfunc = meth->ml_meth;
- // This is slower but CPython promises to check all non-vectorcall
- // function calls.
- if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) {
- goto error;
- }
- PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, arg);
- _Py_LeaveRecursiveCallTstate(tstate);
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
- Py_DECREF(self);
- Py_DECREF(arg);
- STACK_SHRINK(oparg + 1);
- SET_TOP(res);
- Py_DECREF(callable);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- PyMethodDescrObject *callable =
- (PyMethodDescrObject *)PEEK(total_args + 1);
- DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
- PyMethodDef *meth = callable->d_method;
- DEOPT_IF(meth->ml_flags != (METH_FASTCALL|METH_KEYWORDS), CALL);
- PyTypeObject *d_type = callable->d_common.d_type;
- PyObject *self = PEEK(total_args);
- DEOPT_IF(!Py_IS_TYPE(self, d_type), CALL);
- STAT_INC(CALL, hit);
- int nargs = total_args-1;
- STACK_SHRINK(nargs);
- _PyCFunctionFastWithKeywords cfunc =
- (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
- PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(),
- kwnames);
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
- kwnames = NULL;
-
- /* Free the arguments. */
- for (int i = 0; i < nargs; i++) {
- Py_DECREF(stack_pointer[i]);
- }
- Py_DECREF(self);
- STACK_SHRINK(2-is_meth);
- SET_TOP(res);
- Py_DECREF(callable);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) {
- assert(kwnames == NULL);
- assert(oparg == 0 || oparg == 1);
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- DEOPT_IF(total_args != 1, CALL);
- PyMethodDescrObject *callable = (PyMethodDescrObject *)SECOND();
- DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
- PyMethodDef *meth = callable->d_method;
- PyObject *self = TOP();
- DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL);
- DEOPT_IF(meth->ml_flags != METH_NOARGS, CALL);
- STAT_INC(CALL, hit);
- PyCFunction cfunc = meth->ml_meth;
- // This is slower but CPython promises to check all non-vectorcall
- // function calls.
- if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) {
- goto error;
- }
- PyObject *res = _PyCFunction_TrampolineCall(cfunc, self, NULL);
- _Py_LeaveRecursiveCallTstate(tstate);
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
- Py_DECREF(self);
- STACK_SHRINK(oparg + 1);
- SET_TOP(res);
- Py_DECREF(callable);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) {
- assert(kwnames == NULL);
- int is_meth = is_method(stack_pointer, oparg);
- int total_args = oparg + is_meth;
- PyMethodDescrObject *callable =
- (PyMethodDescrObject *)PEEK(total_args + 1);
- /* Builtin METH_FASTCALL methods, without keywords */
- DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL);
- PyMethodDef *meth = callable->d_method;
- DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL);
- PyObject *self = PEEK(total_args);
- DEOPT_IF(!Py_IS_TYPE(self, callable->d_common.d_type), CALL);
- STAT_INC(CALL, hit);
- _PyCFunctionFast cfunc =
- (_PyCFunctionFast)(void(*)(void))meth->ml_meth;
- int nargs = total_args-1;
- STACK_SHRINK(nargs);
- PyObject *res = cfunc(self, stack_pointer, nargs);
- assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
- /* Clear the stack of the arguments. */
- for (int i = 0; i < nargs; i++) {
- Py_DECREF(stack_pointer[i]);
- }
- Py_DECREF(self);
- STACK_SHRINK(2-is_meth);
- SET_TOP(res);
- Py_DECREF(callable);
- if (res == NULL) {
- goto error;
- }
- JUMPBY(INLINE_CACHE_ENTRIES_CALL);
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(CALL_FUNCTION_EX) {
- PREDICTED(CALL_FUNCTION_EX);
- PyObject *func, *callargs, *kwargs = NULL, *result;
- if (oparg & 0x01) {
- kwargs = POP();
- // DICT_MERGE is called before this opcode if there are kwargs.
- // It converts all dict subtypes in kwargs into regular dicts.
- assert(PyDict_CheckExact(kwargs));
- }
- callargs = POP();
- func = TOP();
- if (!PyTuple_CheckExact(callargs)) {
- if (check_args_iterable(tstate, func, callargs) < 0) {
- Py_DECREF(callargs);
- goto error;
- }
- Py_SETREF(callargs, PySequence_Tuple(callargs));
- if (callargs == NULL) {
- goto error;
- }
- }
- assert(PyTuple_CheckExact(callargs));
-
- result = do_call_core(tstate, func, callargs, kwargs, cframe.use_tracing);
- Py_DECREF(func);
- Py_DECREF(callargs);
- Py_XDECREF(kwargs);
-
- STACK_SHRINK(1);
- assert(TOP() == NULL);
- SET_TOP(result);
- if (result == NULL) {
- goto error;
- }
- CHECK_EVAL_BREAKER();
- DISPATCH();
- }
-
- TARGET(MAKE_FUNCTION) {
- PyObject *codeobj = POP();
- PyFunctionObject *func = (PyFunctionObject *)
- PyFunction_New(codeobj, GLOBALS());
-
- Py_DECREF(codeobj);
- if (func == NULL) {
- goto error;
- }
-
- if (oparg & 0x08) {
- assert(PyTuple_CheckExact(TOP()));
- func->func_closure = POP();
- }
- if (oparg & 0x04) {
- assert(PyTuple_CheckExact(TOP()));
- func->func_annotations = POP();
- }
- if (oparg & 0x02) {
- assert(PyDict_CheckExact(TOP()));
- func->func_kwdefaults = POP();
- }
- if (oparg & 0x01) {
- assert(PyTuple_CheckExact(TOP()));
- func->func_defaults = POP();
- }
-
- func->func_version = ((PyCodeObject *)codeobj)->co_version;
- PUSH((PyObject *)func);
- DISPATCH();
- }
-
- TARGET(RETURN_GENERATOR) {
- assert(PyFunction_Check(frame->f_funcobj));
- PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj;
- PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func);
- if (gen == NULL) {
- goto error;
- }
- assert(EMPTY());
- _PyFrame_SetStackPointer(frame, stack_pointer);
- _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
- _PyFrame_Copy(frame, gen_frame);
- assert(frame->frame_obj == NULL);
- gen->gi_frame_state = FRAME_CREATED;
- gen_frame->owner = FRAME_OWNED_BY_GENERATOR;
- _Py_LeaveRecursiveCallPy(tstate);
- assert(frame != &entry_frame);
- _PyInterpreterFrame *prev = frame->previous;
- _PyThreadState_PopFrame(tstate, frame);
- frame = cframe.current_frame = prev;
- _PyFrame_StackPush(frame, (PyObject *)gen);
- goto resume_frame;
- }
-
- TARGET(BUILD_SLICE) {
- PyObject *start, *stop, *step, *slice;
- if (oparg == 3)
- step = POP();
- else
- step = NULL;
- stop = POP();
- start = TOP();
- slice = PySlice_New(start, stop, step);
- Py_DECREF(start);
- Py_DECREF(stop);
- Py_XDECREF(step);
- SET_TOP(slice);
- if (slice == NULL)
- goto error;
- DISPATCH();
- }
-
- TARGET(FORMAT_VALUE) {
- /* Handles f-string value formatting. */
- PyObject *result;
- PyObject *fmt_spec;
- PyObject *value;
- PyObject *(*conv_fn)(PyObject *);
- int which_conversion = oparg & FVC_MASK;
- int have_fmt_spec = (oparg & FVS_MASK) == FVS_HAVE_SPEC;
-
- fmt_spec = have_fmt_spec ? POP() : NULL;
- value = POP();
-
- /* See if any conversion is specified. */
- switch (which_conversion) {
- case FVC_NONE: conv_fn = NULL; break;
- case FVC_STR: conv_fn = PyObject_Str; break;
- case FVC_REPR: conv_fn = PyObject_Repr; break;
- case FVC_ASCII: conv_fn = PyObject_ASCII; break;
- default:
- _PyErr_Format(tstate, PyExc_SystemError,
- "unexpected conversion flag %d",
- which_conversion);
- goto error;
- }
-
- /* If there's a conversion function, call it and replace
- value with that result. Otherwise, just use value,
- without conversion. */
- if (conv_fn != NULL) {
- result = conv_fn(value);
- Py_DECREF(value);
- if (result == NULL) {
- Py_XDECREF(fmt_spec);
- goto error;
- }
- value = result;
- }
-
- /* If value is a unicode object, and there's no fmt_spec,
- then we know the result of format(value) is value
- itself. In that case, skip calling format(). I plan to
- move this optimization in to PyObject_Format()
- itself. */
- if (PyUnicode_CheckExact(value) && fmt_spec == NULL) {
- /* Do nothing, just transfer ownership to result. */
- result = value;
- } else {
- /* Actually call format(). */
- result = PyObject_Format(value, fmt_spec);
- Py_DECREF(value);
- Py_XDECREF(fmt_spec);
- if (result == NULL) {
- goto error;
- }
- }
-
- PUSH(result);
- DISPATCH();
- }
-
- TARGET(COPY) {
- assert(oparg != 0);
- PyObject *peek = PEEK(oparg);
- PUSH(Py_NewRef(peek));
- DISPATCH();
- }
-
- TARGET(BINARY_OP) {
- PREDICTED(BINARY_OP);
- static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size");
- PyObject *rhs = PEEK(1);
- PyObject *lhs = PEEK(2);
- PyObject *res;
- #if ENABLE_SPECIALIZATION
- _PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr;
- if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
- assert(cframe.use_tracing == 0);
- next_instr--;
- _Py_Specialize_BinaryOp(lhs, rhs, next_instr, oparg, &GETLOCAL(0));
- DISPATCH_SAME_OPARG();
- }
- STAT_INC(BINARY_OP, deferred);
- DECREMENT_ADAPTIVE_COUNTER(cache->counter);
- #endif /* ENABLE_SPECIALIZATION */
- assert(0 <= oparg);
- assert((unsigned)oparg < Py_ARRAY_LENGTH(binary_ops));
- assert(binary_ops[oparg]);
- res = binary_ops[oparg](lhs, rhs);
- Py_DECREF(lhs);
- Py_DECREF(rhs);
- if (res == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- POKE(1, res);
- JUMPBY(1);
- DISPATCH();
- }
-
- TARGET(SWAP) {
- assert(oparg != 0);
- PyObject *top = TOP();
- SET_TOP(PEEK(oparg));
- PEEK(oparg) = top;
- DISPATCH();
- }
-
- TARGET(EXTENDED_ARG) {
- assert(oparg);
- assert(cframe.use_tracing == 0);
- opcode = _Py_OPCODE(*next_instr);
- oparg = oparg << 8 | _Py_OPARG(*next_instr);
- PRE_DISPATCH_GOTO();
- DISPATCH_GOTO();
- }
-
- TARGET(CACHE) {
- Py_UNREACHABLE();
- }
diff --git a/Python/opcode_metadata_tier2.h b/Python/opcode_metadata_tier2.h
deleted file mode 100644
index bf9170e99cf754..00000000000000
--- a/Python/opcode_metadata_tier2.h
+++ /dev/null
@@ -1,882 +0,0 @@
-// This file is generated by Tools\cases_generator\generate_cases.py --metadata
-// from Python\bytecodes.c
-// Do not edit!
-
-#ifndef NDEBUG
-static int
-_PyOpcode_num_popped(int opcode, int oparg) {
- switch(opcode) {
- case NOP:
- return 0;
- case RESUME:
- return 0;
- case LOAD_CLOSURE:
- return 0;
- case LOAD_FAST_CHECK:
- return 0;
- case LOAD_FAST:
- return 0;
- case LOAD_CONST:
- return 0;
- case STORE_FAST:
- return 1;
- case LOAD_FAST__LOAD_FAST:
- return 0+0;
- case LOAD_FAST__LOAD_CONST:
- return 0+0;
- case STORE_FAST__LOAD_FAST:
- return 1+0;
- case STORE_FAST__STORE_FAST:
- return 1+1;
- case LOAD_CONST__LOAD_FAST:
- return 0+0;
- case POP_TOP:
- return 1;
- case PUSH_NULL:
- return 0;
- case END_FOR:
- return 1+1;
- case UNARY_NEGATIVE:
- return 1;
- case UNARY_NOT:
- return 1;
- case UNARY_INVERT:
- return 1;
- case BINARY_OP_MULTIPLY_INT:
- return 2;
- case BINARY_OP_MULTIPLY_FLOAT:
- return 2;
- case BINARY_OP_SUBTRACT_INT:
- return 2;
- case BINARY_OP_SUBTRACT_FLOAT:
- return 2;
- case BINARY_OP_ADD_UNICODE:
- return 2;
- case BINARY_OP_INPLACE_ADD_UNICODE:
- return 2;
- case BINARY_OP_ADD_FLOAT:
- return 2;
- case BINARY_OP_ADD_INT_TYPE_CHECK:
- return 2;
- case BINARY_OP_ADD_INT_REST:
- return 2;
- case BINARY_OP_ADD_INT:
- return 2;
- case BINARY_SUBSCR:
- return 2;
- case BINARY_SLICE:
- return 3;
- case STORE_SLICE:
- return 4;
- case BINARY_SUBSCR_LIST_INT:
- return 2;
- case BINARY_SUBSCR_TUPLE_INT:
- return 2;
- case BINARY_SUBSCR_DICT:
- return 2;
- case BINARY_SUBSCR_GETITEM:
- return 2;
- case LIST_APPEND:
- return (oparg-1) + 2;
- case SET_ADD:
- return (oparg-1) + 2;
- case STORE_SUBSCR:
- return 3;
- case STORE_SUBSCR_LIST_INT:
- return 3;
- case STORE_SUBSCR_DICT:
- return 3;
- case DELETE_SUBSCR:
- return 2;
- case CALL_INTRINSIC_1:
- return 1;
- case RAISE_VARARGS:
- return oparg;
- case INTERPRETER_EXIT:
- return 1;
- case RETURN_VALUE:
- return 1;
- case GET_AITER:
- return 1;
- case GET_ANEXT:
- return 1;
- case GET_AWAITABLE:
- return 1;
- case SEND:
- return -1;
- case YIELD_VALUE:
- return 1;
- case POP_EXCEPT:
- return 1;
- case RERAISE:
- return -1;
- case PREP_RERAISE_STAR:
- return 2;
- case END_ASYNC_FOR:
- return -1;
- case CLEANUP_THROW:
- return -1;
- case LOAD_ASSERTION_ERROR:
- return 0;
- case LOAD_BUILD_CLASS:
- return 0;
- case STORE_NAME:
- return 1;
- case DELETE_NAME:
- return 0;
- case UNPACK_SEQUENCE:
- return -1;
- case UNPACK_SEQUENCE_TWO_TUPLE:
- return -1;
- case UNPACK_SEQUENCE_TUPLE:
- return -1;
- case UNPACK_SEQUENCE_LIST:
- return -1;
- case UNPACK_EX:
- return -1;
- case STORE_ATTR:
- return 2;
- case DELETE_ATTR:
- return 1;
- case STORE_GLOBAL:
- return 1;
- case DELETE_GLOBAL:
- return 0;
- case LOAD_NAME:
- return 0;
- case LOAD_GLOBAL:
- return -1;
- case LOAD_GLOBAL_MODULE:
- return -1;
- case LOAD_GLOBAL_BUILTIN:
- return -1;
- case DELETE_FAST:
- return 0;
- case MAKE_CELL:
- return 0;
- case DELETE_DEREF:
- return 0;
- case LOAD_CLASSDEREF:
- return 0;
- case LOAD_DEREF:
- return 0;
- case STORE_DEREF:
- return 1;
- case COPY_FREE_VARS:
- return 0;
- case BUILD_STRING:
- return oparg;
- case BUILD_TUPLE:
- return oparg;
- case BUILD_LIST:
- return oparg;
- case LIST_EXTEND:
- return (oparg-1) + 2;
- case SET_UPDATE:
- return (oparg-1) + 2;
- case BUILD_SET:
- return oparg;
- case BUILD_MAP:
- return oparg*2;
- case SETUP_ANNOTATIONS:
- return 0;
- case BUILD_CONST_KEY_MAP:
- return oparg + 1;
- case DICT_UPDATE:
- return 1;
- case DICT_MERGE:
- return 1;
- case MAP_ADD:
- return 2;
- case LOAD_ATTR:
- return -1;
- case LOAD_ATTR_INSTANCE_VALUE:
- return -1;
- case LOAD_ATTR_MODULE:
- return -1;
- case LOAD_ATTR_WITH_HINT:
- return -1;
- case LOAD_ATTR_SLOT:
- return -1;
- case LOAD_ATTR_CLASS:
- return -1;
- case LOAD_ATTR_PROPERTY:
- return -1;
- case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN:
- return -1;
- case STORE_ATTR_INSTANCE_VALUE:
- return 2;
- case STORE_ATTR_WITH_HINT:
- return 2;
- case STORE_ATTR_SLOT:
- return 2;
- case COMPARE_OP:
- return 2;
- case COMPARE_AND_BRANCH:
- return 2;
- case COMPARE_AND_BRANCH_FLOAT:
- return 2;
- case COMPARE_AND_BRANCH_INT:
- return 2;
- case COMPARE_AND_BRANCH_STR:
- return 2;
- case IS_OP:
- return 2;
- case CONTAINS_OP:
- return 2;
- case CHECK_EG_MATCH:
- return 2;
- case CHECK_EXC_MATCH:
- return 2;
- case IMPORT_NAME:
- return 2;
- case IMPORT_FROM:
- return 1;
- case JUMP_FORWARD:
- return 0;
- case JUMP_BACKWARD:
- return 0;
- case POP_JUMP_IF_FALSE:
- return -1;
- case POP_JUMP_IF_TRUE:
- return -1;
- case POP_JUMP_IF_NOT_NONE:
- return -1;
- case POP_JUMP_IF_NONE:
- return -1;
- case JUMP_IF_FALSE_OR_POP:
- return -1;
- case JUMP_IF_TRUE_OR_POP:
- return -1;
- case JUMP_BACKWARD_NO_INTERRUPT:
- return -1;
- case GET_LEN:
- return -1;
- case MATCH_CLASS:
- return 3;
- case MATCH_MAPPING:
- return 1;
- case MATCH_SEQUENCE:
- return 1;
- case MATCH_KEYS:
- return 2;
- case GET_ITER:
- return -1;
- case GET_YIELD_FROM_ITER:
- return -1;
- case FOR_ITER:
- return -1;
- case FOR_ITER_LIST:
- return -1;
- case FOR_ITER_TUPLE:
- return -1;
- case FOR_ITER_RANGE:
- return -1;
- case FOR_ITER_GEN:
- return -1;
- case BEFORE_ASYNC_WITH:
- return -1;
- case BEFORE_WITH:
- return -1;
- case WITH_EXCEPT_START:
- return 4;
- case PUSH_EXC_INFO:
- return -1;
- case LOAD_ATTR_METHOD_WITH_VALUES:
- return -1;
- case LOAD_ATTR_METHOD_NO_DICT:
- return -1;
- case LOAD_ATTR_METHOD_LAZY_DICT:
- return -1;
- case CALL_BOUND_METHOD_EXACT_ARGS:
- return -1;
- case KW_NAMES:
- return -1;
- case CALL:
- return -1;
- case CALL_PY_EXACT_ARGS:
- return -1;
- case CALL_PY_WITH_DEFAULTS:
- return -1;
- case CALL_NO_KW_TYPE_1:
- return -1;
- case CALL_NO_KW_STR_1:
- return -1;
- case CALL_NO_KW_TUPLE_1:
- return -1;
- case CALL_BUILTIN_CLASS:
- return -1;
- case CALL_NO_KW_BUILTIN_O:
- return -1;
- case CALL_NO_KW_BUILTIN_FAST:
- return -1;
- case CALL_BUILTIN_FAST_WITH_KEYWORDS:
- return -1;
- case CALL_NO_KW_LEN:
- return -1;
- case CALL_NO_KW_ISINSTANCE:
- return -1;
- case CALL_NO_KW_LIST_APPEND:
- return -1;
- case CALL_NO_KW_METHOD_DESCRIPTOR_O:
- return -1;
- case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS:
- return -1;
- case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS:
- return -1;
- case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
- return -1;
- case CALL_FUNCTION_EX:
- return -1;
- case MAKE_FUNCTION:
- return -1;
- case RETURN_GENERATOR:
- return -1;
- case BUILD_SLICE:
- return -1;
- case FORMAT_VALUE:
- return -1;
- case COPY:
- return -1;
- case BINARY_OP:
- return 2;
- case SWAP:
- return -1;
- case EXTENDED_ARG:
- return -1;
- case CACHE:
- return -1;
- default:
- Py_UNREACHABLE();
- }
-}
-#endif
-
-#ifndef NDEBUG
-static int
-_PyOpcode_num_pushed(int opcode, int oparg) {
- switch(opcode) {
- case NOP:
- return 0;
- case RESUME:
- return 0;
- case LOAD_CLOSURE:
- return 1;
- case LOAD_FAST_CHECK:
- return 1;
- case LOAD_FAST:
- return 1;
- case LOAD_CONST:
- return 1;
- case STORE_FAST:
- return 0;
- case LOAD_FAST__LOAD_FAST:
- return 1+1;
- case LOAD_FAST__LOAD_CONST:
- return 1+1;
- case STORE_FAST__LOAD_FAST:
- return 0+1;
- case STORE_FAST__STORE_FAST:
- return 0+0;
- case LOAD_CONST__LOAD_FAST:
- return 1+1;
- case POP_TOP:
- return 0;
- case PUSH_NULL:
- return 1;
- case END_FOR:
- return 0+0;
- case UNARY_NEGATIVE:
- return 1;
- case UNARY_NOT:
- return 1;
- case UNARY_INVERT:
- return 1;
- case BINARY_OP_MULTIPLY_INT:
- return 1;
- case BINARY_OP_MULTIPLY_FLOAT:
- return 1;
- case BINARY_OP_SUBTRACT_INT:
- return 1;
- case BINARY_OP_SUBTRACT_FLOAT:
- return 1;
- case BINARY_OP_ADD_UNICODE:
- return 1;
- case BINARY_OP_INPLACE_ADD_UNICODE:
- return 0;
- case BINARY_OP_ADD_FLOAT:
- return 1;
- case BINARY_OP_ADD_INT_TYPE_CHECK:
- return 2;
- case BINARY_OP_ADD_INT_REST:
- return 1;
- case BINARY_OP_ADD_INT:
- return 1;
- case BINARY_SUBSCR:
- return 1;
- case BINARY_SLICE:
- return 1;
- case STORE_SLICE:
- return 0;
- case BINARY_SUBSCR_LIST_INT:
- return 1;
- case BINARY_SUBSCR_TUPLE_INT:
- return 1;
- case BINARY_SUBSCR_DICT:
- return 1;
- case BINARY_SUBSCR_GETITEM:
- return 1;
- case LIST_APPEND:
- return (oparg-1) + 1;
- case SET_ADD:
- return (oparg-1) + 1;
- case STORE_SUBSCR:
- return 0;
- case STORE_SUBSCR_LIST_INT:
- return 0;
- case STORE_SUBSCR_DICT:
- return 0;
- case DELETE_SUBSCR:
- return 0;
- case CALL_INTRINSIC_1:
- return 1;
- case RAISE_VARARGS:
- return 0;
- case INTERPRETER_EXIT:
- return 0;
- case RETURN_VALUE:
- return 0;
- case GET_AITER:
- return 1;
- case GET_ANEXT:
- return 2;
- case GET_AWAITABLE:
- return 1;
- case SEND:
- return -1;
- case YIELD_VALUE:
- return 1;
- case POP_EXCEPT:
- return 0;
- case RERAISE:
- return -1;
- case PREP_RERAISE_STAR:
- return 1;
- case END_ASYNC_FOR:
- return -1;
- case CLEANUP_THROW:
- return -1;
- case LOAD_ASSERTION_ERROR:
- return 1;
- case LOAD_BUILD_CLASS:
- return 1;
- case STORE_NAME:
- return 0;
- case DELETE_NAME:
- return 0;
- case UNPACK_SEQUENCE:
- return -1;
- case UNPACK_SEQUENCE_TWO_TUPLE:
- return -1;
- case UNPACK_SEQUENCE_TUPLE:
- return -1;
- case UNPACK_SEQUENCE_LIST:
- return -1;
- case UNPACK_EX:
- return -1;
- case STORE_ATTR:
- return 0;
- case DELETE_ATTR:
- return 0;
- case STORE_GLOBAL:
- return 0;
- case DELETE_GLOBAL:
- return 0;
- case LOAD_NAME:
- return 1;
- case LOAD_GLOBAL:
- return -1;
- case LOAD_GLOBAL_MODULE:
- return -1;
- case LOAD_GLOBAL_BUILTIN:
- return -1;
- case DELETE_FAST:
- return 0;
- case MAKE_CELL:
- return 0;
- case DELETE_DEREF:
- return 0;
- case LOAD_CLASSDEREF:
- return 1;
- case LOAD_DEREF:
- return 1;
- case STORE_DEREF:
- return 0;
- case COPY_FREE_VARS:
- return 0;
- case BUILD_STRING:
- return 1;
- case BUILD_TUPLE:
- return 1;
- case BUILD_LIST:
- return 1;
- case LIST_EXTEND:
- return (oparg-1) + 1;
- case SET_UPDATE:
- return (oparg-1) + 1;
- case BUILD_SET:
- return 1;
- case BUILD_MAP:
- return 1;
- case SETUP_ANNOTATIONS:
- return 0;
- case BUILD_CONST_KEY_MAP:
- return 1;
- case DICT_UPDATE:
- return 0;
- case DICT_MERGE:
- return 0;
- case MAP_ADD:
- return 0;
- case LOAD_ATTR:
- return -1;
- case LOAD_ATTR_INSTANCE_VALUE:
- return -1;
- case LOAD_ATTR_MODULE:
- return -1;
- case LOAD_ATTR_WITH_HINT:
- return -1;
- case LOAD_ATTR_SLOT:
- return -1;
- case LOAD_ATTR_CLASS:
- return -1;
- case LOAD_ATTR_PROPERTY:
- return -1;
- case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN:
- return -1;
- case STORE_ATTR_INSTANCE_VALUE:
- return 0;
- case STORE_ATTR_WITH_HINT:
- return 0;
- case STORE_ATTR_SLOT:
- return 0;
- case COMPARE_OP:
- return 1;
- case COMPARE_AND_BRANCH:
- return 0;
- case COMPARE_AND_BRANCH_FLOAT:
- return 0;
- case COMPARE_AND_BRANCH_INT:
- return 0;
- case COMPARE_AND_BRANCH_STR:
- return 0;
- case IS_OP:
- return 1;
- case CONTAINS_OP:
- return 1;
- case CHECK_EG_MATCH:
- return 2;
- case CHECK_EXC_MATCH:
- return 2;
- case IMPORT_NAME:
- return 1;
- case IMPORT_FROM:
- return 2;
- case JUMP_FORWARD:
- return 0;
- case JUMP_BACKWARD:
- return 0;
- case POP_JUMP_IF_FALSE:
- return -1;
- case POP_JUMP_IF_TRUE:
- return -1;
- case POP_JUMP_IF_NOT_NONE:
- return -1;
- case POP_JUMP_IF_NONE:
- return -1;
- case JUMP_IF_FALSE_OR_POP:
- return -1;
- case JUMP_IF_TRUE_OR_POP:
- return -1;
- case JUMP_BACKWARD_NO_INTERRUPT:
- return -1;
- case GET_LEN:
- return -1;
- case MATCH_CLASS:
- return 1;
- case MATCH_MAPPING:
- return 2;
- case MATCH_SEQUENCE:
- return 2;
- case MATCH_KEYS:
- return 3;
- case GET_ITER:
- return -1;
- case GET_YIELD_FROM_ITER:
- return -1;
- case FOR_ITER:
- return -1;
- case FOR_ITER_LIST:
- return -1;
- case FOR_ITER_TUPLE:
- return -1;
- case FOR_ITER_RANGE:
- return -1;
- case FOR_ITER_GEN:
- return -1;
- case BEFORE_ASYNC_WITH:
- return -1;
- case BEFORE_WITH:
- return -1;
- case WITH_EXCEPT_START:
- return 5;
- case PUSH_EXC_INFO:
- return -1;
- case LOAD_ATTR_METHOD_WITH_VALUES:
- return -1;
- case LOAD_ATTR_METHOD_NO_DICT:
- return -1;
- case LOAD_ATTR_METHOD_LAZY_DICT:
- return -1;
- case CALL_BOUND_METHOD_EXACT_ARGS:
- return -1;
- case KW_NAMES:
- return -1;
- case CALL:
- return -1;
- case CALL_PY_EXACT_ARGS:
- return -1;
- case CALL_PY_WITH_DEFAULTS:
- return -1;
- case CALL_NO_KW_TYPE_1:
- return -1;
- case CALL_NO_KW_STR_1:
- return -1;
- case CALL_NO_KW_TUPLE_1:
- return -1;
- case CALL_BUILTIN_CLASS:
- return -1;
- case CALL_NO_KW_BUILTIN_O:
- return -1;
- case CALL_NO_KW_BUILTIN_FAST:
- return -1;
- case CALL_BUILTIN_FAST_WITH_KEYWORDS:
- return -1;
- case CALL_NO_KW_LEN:
- return -1;
- case CALL_NO_KW_ISINSTANCE:
- return -1;
- case CALL_NO_KW_LIST_APPEND:
- return -1;
- case CALL_NO_KW_METHOD_DESCRIPTOR_O:
- return -1;
- case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS:
- return -1;
- case CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS:
- return -1;
- case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
- return -1;
- case CALL_FUNCTION_EX:
- return -1;
- case MAKE_FUNCTION:
- return -1;
- case RETURN_GENERATOR:
- return -1;
- case BUILD_SLICE:
- return -1;
- case FORMAT_VALUE:
- return -1;
- case COPY:
- return -1;
- case BINARY_OP:
- return 1;
- case SWAP:
- return -1;
- case EXTENDED_ARG:
- return -1;
- case CACHE:
- return -1;
- default:
- Py_UNREACHABLE();
- }
-}
-#endif
-enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };
-enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 };
-struct opcode_metadata {
- enum Direction dir_op1;
- enum Direction dir_op2;
- enum Direction dir_op3;
- bool valid_entry;
- enum InstructionFormat instr_format;
-} _PyOpcode_opcode_metadata[256] = {
- [NOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [RESUME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_FAST_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [LOAD_FAST__LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [STORE_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [STORE_FAST__STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [LOAD_CONST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [POP_TOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [PUSH_NULL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [END_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [UNARY_NEGATIVE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [UNARY_NOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [UNARY_INVERT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BINARY_OP_MULTIPLY_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_MULTIPLY_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_SUBTRACT_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_SUBTRACT_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_ADD_INT_TYPE_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_ADD_INT_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [BINARY_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [STORE_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BINARY_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [BINARY_SUBSCR_TUPLE_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [BINARY_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [BINARY_SUBSCR_GETITEM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [SET_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [STORE_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [STORE_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [DELETE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [CALL_INTRINSIC_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [RAISE_VARARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [INTERPRETER_EXIT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [RETURN_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [PREP_RERAISE_STAR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [END_ASYNC_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [CLEANUP_THROW] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [LOAD_ASSERTION_ERROR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [LOAD_BUILD_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [DELETE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DELETE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_GLOBAL_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_GLOBAL_BUILTIN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DELETE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [MAKE_CELL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DELETE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_CLASSDEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [COPY_FREE_VARS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_STRING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LIST_EXTEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [SET_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [SETUP_ANNOTATIONS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BUILD_CONST_KEY_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DICT_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DICT_MERGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [MAP_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_ATTR_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_ATTR_PROPERTY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [STORE_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [STORE_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [COMPARE_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [COMPARE_AND_BRANCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
- [COMPARE_AND_BRANCH_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
- [COMPARE_AND_BRANCH_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
- [COMPARE_AND_BRANCH_STR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
- [IS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CONTAINS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CHECK_EG_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [CHECK_EXC_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [IMPORT_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [IMPORT_FROM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [JUMP_FORWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [JUMP_BACKWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [POP_JUMP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [POP_JUMP_IF_TRUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [POP_JUMP_IF_NOT_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [POP_JUMP_IF_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [JUMP_IF_FALSE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [JUMP_IF_TRUE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [JUMP_BACKWARD_NO_INTERRUPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [GET_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [MATCH_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [MATCH_MAPPING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [MATCH_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [MATCH_KEYS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [WITH_EXCEPT_START] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [PUSH_EXC_INFO] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [LOAD_ATTR_METHOD_WITH_VALUES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [LOAD_ATTR_METHOD_NO_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [LOAD_ATTR_METHOD_LAZY_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [KW_NAMES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_PY_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_PY_WITH_DEFAULTS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_TYPE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_STR_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_TUPLE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_BUILTIN_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_BUILTIN_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_BUILTIN_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_ISINSTANCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_FUNCTION_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [MAKE_FUNCTION] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [RETURN_GENERATOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BUILD_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [FORMAT_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [COPY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BINARY_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [SWAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [EXTENDED_ARG] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CACHE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
-};
diff --git a/Tools/build/generate_opcode_h.py b/Tools/build/generate_opcode_h.py
index 3ff94a324a0803..d3889f5b67fd7f 100644
--- a/Tools/build/generate_opcode_h.py
+++ b/Tools/build/generate_opcode_h.py
@@ -113,19 +113,12 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
# The Tier 2 ops
next_op = 1
uop_opmap = specialized_opmap.copy()
- uop_opname = opname_including_specialized.copy()
- # Remove macroops
- for name in opcode['_macro_ops']:
- op = uop_opmap[name]
- del uop_opmap[name]
- del uop_opname[op]
- used[op] = False
# Add microops
for name in opcode['_uops']:
while used[next_op]:
next_op += 1
uop_opmap[name] = next_op
- uop_opname[next_op] = name
+ opname_including_specialized[next_op] = name
used[next_op] = True
with open(outfile, 'w') as fobj, open(internaloutfile, 'w') as iobj:
@@ -147,19 +140,13 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
if op == MAX_PSEUDO_OPCODE:
fobj.write(DEFINE.format("MAX_PSEUDO_OPCODE", MAX_PSEUDO_OPCODE))
- fobj.write("\n")
- fobj.write("#ifndef Py_TIER2_INTERPRETER\n")
for name, op in specialized_opmap.items():
fobj.write(DEFINE.format(name, op))
- fobj.write("#endif\n")
- fobj.write("\n")
# Tier 2 opcodes
- fobj.write("#ifdef Py_TIER2_INTERPRETER\n")
+ fobj.write("// Tier 2 interpreter ops\n")
for name, op in uop_opmap.items():
fobj.write(DEFINE.format(name, op))
- fobj.write("#endif\n")
- fobj.write("\n")
iobj.write("\nextern const uint8_t _PyOpcode_Caches[256];\n")
iobj.write("\nextern const uint8_t _PyOpcode_Deopt[256];\n")
@@ -209,7 +196,7 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
iobj.write("\n")
# Tier 1 opnames
- iobj.write("#if defined(Py_DEBUG) && !defined(Py_TIER2_INTERPRETER)\n")
+ iobj.write("#ifdef Py_DEBUG\n")
iobj.write(f"static const char *const _PyOpcode_OpName[{NUM_OPCODES}] = {{\n")
for op, name in enumerate(opname_including_specialized):
if name[0] != "<":
@@ -219,15 +206,6 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
iobj.write("#endif\n")
iobj.write("\n")
- # Tier 2 opnames
- iobj.write("#if defined(Py_DEBUG) && defined(Py_TIER2_INTERPRETER)\n")
- iobj.write(f"static const char *const _PyOpcode_OpName[{NUM_OPCODES}] = {{\n")
- for op, name in enumerate(uop_opname):
- if name[0] != "<":
- op = name
- iobj.write(f''' [{op}] = "{name}",\n''')
- iobj.write("};\n")
- iobj.write("#endif\n")
iobj.write("\n")
iobj.write("#define EXTRA_CASES \\\n")
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 890d304def8098..39d50c43a59cd6 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -30,10 +30,6 @@
)
# Tier 2 interpreter
-TIER2_OUTPUT = os.path.relpath(os.path.join(ROOT, "Python/generated_cases_tier2.c.h"))
-TIER2_METADATA_OUTPUT = os.path.relpath(
- os.path.join(ROOT, "Python/opcode_metadata_tier2.h")
-)
TIER2_MACRO_TO_MICRO_MAP_OUTPUT = os.path.relpath(
os.path.join(ROOT, "Include/internal/pycore_opcode_macro_to_micro.h")
)
@@ -70,11 +66,6 @@
)
-class InterpreterTier(Enum):
- TIER1 = auto()
- TIER2 = auto()
-
-
def effect_size(effect: StackEffect) -> tuple[int, str]:
"""Return the 'size' impact of a stack effect.
@@ -385,7 +376,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
for line in self.block_text:
if m := re.match(r"(\s*)U_INST\((.+)\);\s*$", line):
space, label = m.groups()
- out.emit(f"{label}();")
+ out.emit(f"UOP_{label}();")
elif m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*(?://.*)?$", line):
space, cond, label = m.groups()
# ERROR_IF() must pop the inputs from the stack.
@@ -478,15 +469,13 @@ class Analyzer:
filename: str
output_filename: str
- tier: InterpreterTier
src: str
errors: int = 0
- def __init__(self, filename: str, output_filename: str, tier: InterpreterTier):
+ def __init__(self, filename: str, output_filename: str):
"""Read the input file."""
self.filename = filename
self.output_filename = output_filename
- self.tier = tier
with open(filename) as f:
self.src = f.read()
@@ -938,9 +927,6 @@ def write_metadata(self) -> None:
case parser.InstDef():
if thing.kind == "op":
continue
- if ((thing.kind == "macro_inst" and self.tier != InterpreterTier.TIER1)
- or (thing.kind == "u_inst" and self.tier != InterpreterTier.TIER2)):
- continue
self.write_metadata_for_inst(self.instrs[thing.name])
case parser.Super():
self.write_metadata_for_super(self.super_instrs[thing.name])
@@ -999,15 +985,14 @@ def write_instructions(self) -> None:
n_macros = 0
# Single pass to hoist all the u_instructions to the top.
- if self.tier == InterpreterTier.TIER1:
- for thing in self.everything:
- match thing:
- case parser.InstDef():
- if thing.kind == "u_inst":
- self.write_u_inst_as_c_macro(
- self.instrs[thing.name])
- case _:
- pass
+ for thing in self.everything:
+ match thing:
+ case parser.InstDef():
+ if thing.kind == "u_inst":
+ self.write_u_inst_as_c_macro(
+ self.instrs[thing.name])
+ case _:
+ pass
# Everything else
for thing in self.everything:
@@ -1016,10 +1001,6 @@ def write_instructions(self) -> None:
match thing.kind:
case "op":
pass
- case "u_inst" if self.tier != InterpreterTier.TIER2:
- pass
- case "macro_inst" if self.tier != InterpreterTier.TIER1:
- pass
case _:
n_instrs += 1
self.write_instr(self.instrs[thing.name])
@@ -1053,7 +1034,7 @@ def write_instr(self, instr: Instruction) -> None:
def write_u_inst_as_c_macro(self, instr: Instruction) -> None:
name = instr.name
self.out.emit("")
- self.out.emit(f"#define {name}() \\")
+ self.out.emit(f"#define UOP_{name}() \\")
self.out.emit("do { \\")
self.out.postfix = "\\"
instr.write_body(self.out, 0)
@@ -1167,40 +1148,29 @@ def variable_used(node: parser.Node, name: str) -> bool:
"""Determine whether a variable with a given name is used in a node."""
return any(token.kind == "IDENTIFIER" and token.text == name for token in node.tokens)
+def main():
+ """Parse command line, parse input, analyze, write output."""
+ args = arg_parser.parse_args() # Prints message and sys.exit(2) on error
-def generate_interpreter(input_: str, output: str, metadata: bool, macromap: bool,
- tier: InterpreterTier) -> None:
- if tier == InterpreterTier.TIER2 and macromap:
- output = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
- if metadata:
- if output == DEFAULT_OUTPUT:
+ if args.macromap:
+ if args.output == DEFAULT_OUTPUT:
+ output = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
+ elif args.metadata:
+ if args.output == DEFAULT_OUTPUT:
output = DEFAULT_METADATA_OUTPUT
- elif output == TIER2_OUTPUT:
- output = TIER2_METADATA_OUTPUT
- a = Analyzer(input_, output, tier) # Raises OSError if input unreadable
+ a = Analyzer(args.input, args.output) # Raises OSError if input unreadable
a.parse() # Raises SyntaxError on failure
a.analyze() # Prints messages and sets a.errors on failure
if a.errors:
sys.exit(f"Found {a.errors} errors")
- if macromap:
+ if args.macromap:
a.write_macromap()
return
- if metadata:
+ elif args.metadata:
a.write_metadata()
else:
a.write_instructions() # Raises OSError if output can't be written
-def main():
- """Parse command line, parse input, analyze, write output."""
- args = arg_parser.parse_args() # Prints message and sys.exit(2) on error
-
- # Tier 1 interpreter
- generate_interpreter(args.input, args.output, args.metadata, False,
- InterpreterTier.TIER1)
- # Tier 2 interpreter
- generate_interpreter(args.input, TIER2_OUTPUT, args.metadata, args.macromap,
- InterpreterTier.TIER2)
-
if __name__ == "__main__":
main()
From d7566dd9639d892fa72450bc3c5007c1d9994e9c Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sun, 29 Jan 2023 02:22:00 +0800
Subject: [PATCH 008/280] Generate type information mapping
---
.../internal/pycore_opcode_macro_to_micro.h | 11 ++-
PCbuild/pythoncore.vcxproj | 5 +-
PCbuild/pythoncore.vcxproj.filters | 3 +
Python/bytecodes.c | 2 +-
Python/specialize.c | 1 +
Tools/cases_generator/generate_cases.py | 82 ++++++++++++-------
6 files changed, 68 insertions(+), 36 deletions(-)
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index aa9049ee5b9518..ab2cd8b54eb368 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -2,8 +2,8 @@
// from Python\bytecodes.c
// Do not edit!
-#ifndef Py_INTERNAL_OPCODE_MACRO_TO_MICRO
-#define Py_INTERNAL_OPCODE_MACRO_TO_MICRO
+#ifndef Py_INTERNAL_OPCODE_MACRO_TO_MICRO_H
+#define Py_INTERNAL_OPCODE_MACRO_TO_MICRO_H
#ifdef __cplusplus
extern "C" {
#endif
@@ -178,11 +178,14 @@ extern const int _Py_MacroOpUOpCount[] = {
[SWAP] = 1,
[EXTENDED_ARG] = 1,
[CACHE] = 1,
-}
+};
extern const int _Py_MacroOpToUOp[][2] = {
[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_TYPE_CHECK, BINARY_OP_ADD_INT_REST},
-}
+};
+extern const PyTypeObject *_Py_UOpGuardTypes[][2] = {
+[BINARY_OP_ADD_INT_TYPE_CHECK] = {&PyLong_Type, &PyLong_Type},
+};
#ifdef __cplusplus
}
#endif
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index d3bd5b378e0dd0..791562710efc54 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -123,6 +123,7 @@
+
@@ -615,7 +616,6 @@
-
@@ -624,8 +624,7 @@
-
-
+
\ No newline at end of file
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index c1b531fd818a97..fe5bc990fec207 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -738,6 +738,9 @@
Include\internal
+
+ Include\internal
+
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 94f4b7ec15567c..8d9d4aee23b6a4 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -289,7 +289,7 @@ dummy_func(
U_INST(BINARY_OP_ADD_INT_REST);
}
- u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (left, right -- left, right)) {
+ u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (left : PyLong_Type, right : PyLong_Type -- left, right)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
diff --git a/Python/specialize.c b/Python/specialize.c
index 84784b2d149e82..77c5f2a30286bd 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -9,6 +9,7 @@
#include "pycore_opcode.h" // _PyOpcode_Caches
#include "structmember.h" // struct PyMemberDef, T_OFFSET_EX
#include "pycore_descrobject.h"
+#include "pycore_opcode_macro_to_micro.h"
#include // rand()
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 39d50c43a59cd6..8e6d9beb147511 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -62,6 +62,7 @@
"--macromap",
action="store_true",
help=f"Generate macro to micro instruction map instead,"
+ f" along with the type for uop type guards"
f"changes output default to {TIER2_MACRO_TO_MICRO_MAP_OUTPUT}",
)
@@ -497,6 +498,7 @@ def error(self, msg: str, node: parser.Node) -> None:
macros: dict[str, parser.Macro]
macro_instrs: dict[str, MacroInstruction]
macro_instdefs: list[parser.InstDef]
+ u_insts: list[parser.InstDef]
families: dict[str, parser.Family]
def parse(self) -> None:
@@ -531,6 +533,7 @@ def parse(self) -> None:
self.macros = {}
self.macro_instrs = {}
self.macro_instdefs = []
+ self.u_insts = []
self.families = {}
while thing := psr.definition():
match thing:
@@ -539,6 +542,8 @@ def parse(self) -> None:
self.everything.append(thing)
if thing.kind == "macro_inst":
self.macro_instdefs.append(thing)
+ elif thing.kind == "u_inst":
+ self.u_insts.append(thing)
case parser.Super(name):
self.supers[name] = thing
self.everything.append(thing)
@@ -829,8 +834,7 @@ def write_function(direction: str, data: list[tuple[Instruction, str]]) -> None:
write_function('popped', popped_data)
write_function('pushed', pushed_data)
- def write_macromap(self):
- """Write the macro instruction to uop mapping to output file."""
+ def write_macromap_and_typedata(self):
with open(self.output_filename, "w") as f:
# Write provenance header
f.write(f"// This file is generated by {THIS} --macromap\n")
@@ -841,8 +845,8 @@ def write_macromap(self):
self.out = Formatter(f, 0)
# Header guard
self.out.emit("")
- self.out.emit("#ifndef Py_INTERNAL_OPCODE_MACRO_TO_MICRO")
- self.out.emit("#define Py_INTERNAL_OPCODE_MACRO_TO_MICRO")
+ self.out.emit("#ifndef Py_INTERNAL_OPCODE_MACRO_TO_MICRO_H")
+ self.out.emit("#define Py_INTERNAL_OPCODE_MACRO_TO_MICRO_H")
self.out.emit("#ifdef __cplusplus")
self.out.emit('extern "C" {')
self.out.emit("#endif")
@@ -852,33 +856,55 @@ def write_macromap(self):
self.out.emit("#endif")
self.out.emit('#include "opcode.h"')
self.out.emit("")
-
- self.out.emit("extern const int _Py_MacroOpUOpCount[] = {")
- macro_instrdef_names = set()
- max_instr_len = 0
- for name, instr_def in self.instrs.items():
- if (macro_def := instr_def.inst) in self.macro_instdefs:
- u_insts = macro_def.u_insts
- instr_len = len(u_insts)
- max_instr_len = max(instr_len, max_instr_len)
- self.out.emit(f"[{macro_def.name}] = {instr_len},")
- macro_instrdef_names.add(macro_def.name)
- else:
- self.out.emit(f"[{name}] = 1,")
- self.out.emit("}")
- self.out.emit("")
- self.out.emit(f"extern const int _Py_MacroOpToUOp[][{max_instr_len}] = {{")
- for macro_def in self.macro_instdefs:
- u_insts = macro_def.u_insts
- self.out.emit(f"[{macro_def.name}] = {{{', '.join(u_insts)}}},")
- self.out.emit("}")
-
+ self.write_macromap()
+ self.write_uopguard_typedata()
# Header guard end
self.out.emit("#ifdef __cplusplus")
self.out.emit("}")
self.out.emit("#endif")
self.out.emit("#endif // Py_INTERNAL_OPCODE_MACRO_TO_MICRO")
+ def write_macromap(self):
+ """Write the macro instruction to uop mapping to output file."""
+ self.out.emit("extern const int _Py_MacroOpUOpCount[] = {")
+ macro_instrdef_names = set()
+ max_instr_len = 0
+ for name, instr_def in self.instrs.items():
+ if (macro_def := instr_def.inst) in self.macro_instdefs:
+ u_insts = macro_def.u_insts
+ instr_len = len(u_insts)
+ max_instr_len = max(instr_len, max_instr_len)
+ self.out.emit(f"[{macro_def.name}] = {instr_len},")
+ macro_instrdef_names.add(macro_def.name)
+ else:
+ self.out.emit(f"[{name}] = 1,")
+ self.out.emit("};")
+ self.out.emit("")
+ self.out.emit(f"extern const int _Py_MacroOpToUOp[][{max_instr_len}] = {{")
+ for macro_def in self.macro_instdefs:
+ u_insts = macro_def.u_insts
+ self.out.emit(f"[{macro_def.name}] = {{{', '.join(u_insts)}}},")
+ self.out.emit("};")
+
+ def write_uopguard_typedata(self):
+ """Write the type information expected of each uop typeguard."""
+ uop_to_type_input = {}
+ max_types = 0
+ for instr_def in self.u_insts:
+ types = []
+ for input_ in instr_def.inputs:
+ if isinstance(input_, StackEffect) and input_.type:
+ types.append(input_.type)
+ if types:
+ max_types = max(max_types, len(types))
+ uop_to_type_input[instr_def.name] = types
+
+ self.out.emit(f"extern const PyTypeObject *_Py_UOpGuardTypes[][{max_types}] = {{")
+ for name, types in uop_to_type_input.items():
+ self.out.emit(f"[{name}] = {{{', '.join(['&' + type_ for type_ in types])}}},")
+ self.out.emit("};")
+
+
def write_metadata(self) -> None:
"""Write instruction metadata to output file."""
@@ -1154,17 +1180,17 @@ def main():
if args.macromap:
if args.output == DEFAULT_OUTPUT:
- output = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
+ args.output = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
elif args.metadata:
if args.output == DEFAULT_OUTPUT:
- output = DEFAULT_METADATA_OUTPUT
+ args.output = DEFAULT_METADATA_OUTPUT
a = Analyzer(args.input, args.output) # Raises OSError if input unreadable
a.parse() # Raises SyntaxError on failure
a.analyze() # Prints messages and sets a.errors on failure
if a.errors:
sys.exit(f"Found {a.errors} errors")
if args.macromap:
- a.write_macromap()
+ a.write_macromap_and_typedata()
return
elif args.metadata:
a.write_metadata()
From ef6f491955f38c7b15969ac41a195a8a078bf7f2 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sun, 29 Jan 2023 15:29:17 +0800
Subject: [PATCH 009/280] Generate type data using output of guards
---
Include/internal/pycore_opcode.h | 5 ++---
Include/opcode.h | 5 +++--
Lib/opcode.py | 1 +
Python/bytecodes.c | 2 +-
Python/compile.c | 1 +
Tools/cases_generator/generate_cases.py | 12 ++++++------
6 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 92ce888e0c322f..457a1a4d87ec23 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -389,13 +389,13 @@ static const char *const _PyOpcode_OpName[263] = {
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
- [BINARY_OP_ADD_INT_TYPE_CHECK] = "BINARY_OP_ADD_INT_TYPE_CHECK",
+ [BB_NEXT] = "BB_NEXT",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
+ [BINARY_OP_ADD_INT_TYPE_CHECK] = "BINARY_OP_ADD_INT_TYPE_CHECK",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
- [167] = "<167>",
[168] = "<168>",
[169] = "<169>",
[170] = "<170>",
@@ -496,7 +496,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 167: \
case 168: \
case 169: \
case 170: \
diff --git a/Include/opcode.h b/Include/opcode.h
index 93635c89da2626..2b0c652e865331 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -250,8 +250,9 @@ extern "C" {
#define UNPACK_SEQUENCE_TUPLE 159
#define UNPACK_SEQUENCE_TWO_TUPLE 160
#define DO_TRACING 255
-#define BINARY_OP_ADD_INT_TYPE_CHECK 161
-#define BINARY_OP_ADD_INT_REST 166
+#define BB_NEXT 161
+#define BINARY_OP_ADD_INT_TYPE_CHECK 166
+#define BINARY_OP_ADD_INT_REST 167
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 9c1402e316d395..292eae330c2095 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -437,6 +437,7 @@ def pseudo_op(name, op, real_ops):
'BINARY_OP_ADD_INT',
]
_uops = [
+ 'BB_NEXT',
'BINARY_OP_ADD_INT_TYPE_CHECK',
'BINARY_OP_ADD_INT_REST',
]
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 8d9d4aee23b6a4..5fdf0b137e638a 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -289,7 +289,7 @@ dummy_func(
U_INST(BINARY_OP_ADD_INT_REST);
}
- u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (left : PyLong_Type, right : PyLong_Type -- left, right)) {
+ u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (left, right-- left : PyLong_Type, right: PyLong_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
diff --git a/Python/compile.c b/Python/compile.c
index c31f08c0a1797b..18ef7646117979 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1079,6 +1079,7 @@ stack_effect(int opcode, int oparg, int jump)
case EXTENDED_ARG:
case RESUME:
case CACHE:
+ case BB_NEXT:
return 0;
/* Stack manipulation */
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 8e6d9beb147511..0d173619a9b807 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -888,19 +888,19 @@ def write_macromap(self):
def write_uopguard_typedata(self):
"""Write the type information expected of each uop typeguard."""
- uop_to_type_input = {}
+ uop_to_type_output = {}
max_types = 0
for instr_def in self.u_insts:
types = []
- for input_ in instr_def.inputs:
- if isinstance(input_, StackEffect) and input_.type:
- types.append(input_.type)
+ for output in instr_def.outputs:
+ if isinstance(output, StackEffect) and output.type:
+ types.append(output.type)
if types:
max_types = max(max_types, len(types))
- uop_to_type_input[instr_def.name] = types
+ uop_to_type_output[instr_def.name] = types
self.out.emit(f"extern const PyTypeObject *_Py_UOpGuardTypes[][{max_types}] = {{")
- for name, types in uop_to_type_input.items():
+ for name, types in uop_to_type_output.items():
self.out.emit(f"[{name}] = {{{', '.join(['&' + type_ for type_ in types])}}},")
self.out.emit("};")
From 9c83802ddc8aa9f563771cc4dc27b78e169f88fe Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sun, 29 Jan 2023 20:42:33 +0800
Subject: [PATCH 010/280] [NOT WORKING] Allocate basic blocks
---
Include/cpython/code.h | 19 ++
Include/internal/pycore_code.h | 3 +
Include/internal/pycore_frame.h | 5 +-
Include/internal/pycore_opcode.h | 66 +++----
Include/opcode.h | 254 +++++++++++++------------
Lib/opcode.py | 6 +
Objects/codeobject.c | 23 +++
Objects/genobject.c | 5 +-
PCbuild/_freeze_module.vcxproj | 1 +
PCbuild/_freeze_module.vcxproj.filters | 3 +
PCbuild/pythoncore.vcxproj | 1 +
PCbuild/pythoncore.vcxproj.filters | 3 +
Python/bytecodes.c | 10 +
Python/ceval_macros.h | 4 +-
Python/generated_cases.c.h | 17 +-
Python/opcode_targets.h | 60 +++---
Python/tier2.c | 62 ++++++
Tools/build/deepfreeze.py | 3 +
18 files changed, 351 insertions(+), 194 deletions(-)
create mode 100644 Python/tier2.c
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 0cf49f06c87732..e0c173411594d4 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -42,6 +42,22 @@ typedef struct {
PyObject *_co_freevars;
} _PyCoCached;
+
+// What the tier 2 interpreter executes
+typedef struct _PyTier2BB {
+ // Stores the start pointer in the tier 1 bytecode.
+ // So that when we exit the trace we can calculate where to return.
+ _Py_CODEUNIT *tier1_start;
+ _Py_CODEUNIT u_code[1];
+} _PyTier2BB;
+
+// Bump allocator for basic blocks (overallocated)
+typedef struct _PyTier2BBSpace {
+ struct _PyTier2BBSpace *next;
+ void *water_level;
+ _PyTier2BB bbs[1];
+} _PyTier2BBSpace;
+
// To avoid repeating ourselves in deepfreeze.py, all PyCodeObject members are
// defined in this macro:
#define _PyCode_DEF(SIZE) { \
@@ -102,6 +118,9 @@ typedef struct {
_PyCoCached *_co_cached; /* cached co_* attributes */ \
int _co_firsttraceable; /* index of first traceable instruction */ \
char *_co_linearray; /* array of line offsets */ \
+ int _tier2_warmup; /* warmup counter for tier 2 */ \
+ _PyTier2BB *_bb_next; /* the tier 2 basic block to execute (if any) */ \
+ _PyTier2BBSpace *_bb_space; /* linked list storing basic blocks */ \
/* Scratch space for extra data relating to the code object. \
Type is a void* to keep the format private in codeobject.c to force \
people to go through the proper APIs. */ \
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index a287250acc1912..090353cac17d6a 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -239,6 +239,9 @@ extern void _PyStaticCode_Fini(PyCodeObject *co);
/* Function to intern strings of codeobjects and quicken the bytecode */
extern int _PyStaticCode_Init(PyCodeObject *co);
+/* Tier 2 interpreter */
+extern void _PyCode_Tier2Warmup(struct _PyInterpreterFrame *, _Py_CODEUNIT **);
+
#ifdef Py_STATS
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index f12b225ebfccf2..695fc94cc956a9 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -69,7 +69,8 @@ typedef struct _PyInterpreterFrame {
} _PyInterpreterFrame;
#define _PyInterpreterFrame_LASTI(IF) \
- ((int)((IF)->prev_instr - _PyCode_CODE((IF)->f_code)))
+ ((int)((IF)->prev_instr - \
+ ((IF)->f_code->_bb_next == NULL ? _PyCode_CODE((IF)->f_code) : (IF)->f_code->_bb_next->u_code)))
static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
return f->localsplus + f->f_code->co_nlocalsplus;
@@ -121,7 +122,7 @@ _PyFrame_Initialize(
frame->f_locals = locals;
frame->stacktop = code->co_nlocalsplus;
frame->frame_obj = NULL;
- frame->prev_instr = _PyCode_CODE(code) - 1;
+ frame->prev_instr = (code->_bb_next == NULL ? _PyCode_CODE(code) - 1 : code->_bb_next->u_code - 1);
frame->yield_offset = 0;
frame->owner = FRAME_OWNED_BY_THREAD;
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 457a1a4d87ec23..b7f27863ba5e24 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -140,6 +140,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
[IS_OP] = IS_OP,
[JUMP_BACKWARD] = JUMP_BACKWARD,
[JUMP_BACKWARD_NO_INTERRUPT] = JUMP_BACKWARD_NO_INTERRUPT,
+ [JUMP_BACKWARD_QUICK] = JUMP_BACKWARD,
[JUMP_FORWARD] = JUMP_FORWARD,
[JUMP_IF_FALSE_OR_POP] = JUMP_IF_FALSE_OR_POP,
[JUMP_IF_TRUE_OR_POP] = JUMP_IF_TRUE_OR_POP,
@@ -192,6 +193,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
[RAISE_VARARGS] = RAISE_VARARGS,
[RERAISE] = RERAISE,
[RESUME] = RESUME,
+ [RESUME_QUICK] = RESUME,
[RETURN_GENERATOR] = RETURN_GENERATOR,
[RETURN_VALUE] = RETURN_VALUE,
[SEND] = SEND,
@@ -233,17 +235,19 @@ static const char *const _PyOpcode_OpName[263] = {
[PUSH_NULL] = "PUSH_NULL",
[INTERPRETER_EXIT] = "INTERPRETER_EXIT",
[END_FOR] = "END_FOR",
+ [RESUME_QUICK] = "RESUME_QUICK",
+ [JUMP_BACKWARD_QUICK] = "JUMP_BACKWARD_QUICK",
[BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
[BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT",
- [BINARY_OP_ADD_UNICODE] = "BINARY_OP_ADD_UNICODE",
- [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE",
[NOP] = "NOP",
- [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT",
+ [BINARY_OP_ADD_UNICODE] = "BINARY_OP_ADD_UNICODE",
[UNARY_NEGATIVE] = "UNARY_NEGATIVE",
[UNARY_NOT] = "UNARY_NOT",
+ [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE",
+ [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT",
+ [UNARY_INVERT] = "UNARY_INVERT",
[BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT",
[BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT",
- [UNARY_INVERT] = "UNARY_INVERT",
[BINARY_OP_SUBTRACT_INT] = "BINARY_OP_SUBTRACT_INT",
[BINARY_SUBSCR_DICT] = "BINARY_SUBSCR_DICT",
[BINARY_SUBSCR_GETITEM] = "BINARY_SUBSCR_GETITEM",
@@ -251,21 +255,21 @@ static const char *const _PyOpcode_OpName[263] = {
[BINARY_SUBSCR_TUPLE_INT] = "BINARY_SUBSCR_TUPLE_INT",
[CALL_PY_EXACT_ARGS] = "CALL_PY_EXACT_ARGS",
[CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS",
- [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS",
- [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS",
[BINARY_SUBSCR] = "BINARY_SUBSCR",
[BINARY_SLICE] = "BINARY_SLICE",
[STORE_SLICE] = "STORE_SLICE",
- [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS",
- [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
+ [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS",
+ [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS",
[GET_LEN] = "GET_LEN",
[MATCH_MAPPING] = "MATCH_MAPPING",
[MATCH_SEQUENCE] = "MATCH_SEQUENCE",
[MATCH_KEYS] = "MATCH_KEYS",
- [CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST",
+ [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS",
[PUSH_EXC_INFO] = "PUSH_EXC_INFO",
[CHECK_EXC_MATCH] = "CHECK_EXC_MATCH",
[CHECK_EG_MATCH] = "CHECK_EG_MATCH",
+ [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
+ [CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST",
[CALL_NO_KW_BUILTIN_O] = "CALL_NO_KW_BUILTIN_O",
[CALL_NO_KW_ISINSTANCE] = "CALL_NO_KW_ISINSTANCE",
[CALL_NO_KW_LEN] = "CALL_NO_KW_LEN",
@@ -275,8 +279,6 @@ static const char *const _PyOpcode_OpName[263] = {
[CALL_NO_KW_METHOD_DESCRIPTOR_O] = "CALL_NO_KW_METHOD_DESCRIPTOR_O",
[CALL_NO_KW_STR_1] = "CALL_NO_KW_STR_1",
[CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1",
- [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1",
- [COMPARE_AND_BRANCH_FLOAT] = "COMPARE_AND_BRANCH_FLOAT",
[WITH_EXCEPT_START] = "WITH_EXCEPT_START",
[GET_AITER] = "GET_AITER",
[GET_ANEXT] = "GET_ANEXT",
@@ -284,38 +286,38 @@ static const char *const _PyOpcode_OpName[263] = {
[BEFORE_WITH] = "BEFORE_WITH",
[END_ASYNC_FOR] = "END_ASYNC_FOR",
[CLEANUP_THROW] = "CLEANUP_THROW",
+ [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1",
+ [COMPARE_AND_BRANCH_FLOAT] = "COMPARE_AND_BRANCH_FLOAT",
[COMPARE_AND_BRANCH_INT] = "COMPARE_AND_BRANCH_INT",
[COMPARE_AND_BRANCH_STR] = "COMPARE_AND_BRANCH_STR",
- [FOR_ITER_LIST] = "FOR_ITER_LIST",
- [FOR_ITER_TUPLE] = "FOR_ITER_TUPLE",
[STORE_SUBSCR] = "STORE_SUBSCR",
[DELETE_SUBSCR] = "DELETE_SUBSCR",
+ [FOR_ITER_LIST] = "FOR_ITER_LIST",
+ [FOR_ITER_TUPLE] = "FOR_ITER_TUPLE",
[FOR_ITER_RANGE] = "FOR_ITER_RANGE",
[FOR_ITER_GEN] = "FOR_ITER_GEN",
[LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS",
[LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
- [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
- [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
[GET_ITER] = "GET_ITER",
[GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER",
- [LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
+ [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
[LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
- [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
- [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
+ [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
+ [LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
[LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
[RETURN_GENERATOR] = "RETURN_GENERATOR",
+ [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
+ [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
[LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT",
[LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
[LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
[LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
[LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
+ [RETURN_VALUE] = "RETURN_VALUE",
[LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
+ [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
[LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
- [RETURN_VALUE] = "RETURN_VALUE",
[LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
- [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
- [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
- [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[PREP_RERAISE_STAR] = "PREP_RERAISE_STAR",
[POP_EXCEPT] = "POP_EXCEPT",
[STORE_NAME] = "STORE_NAME",
@@ -341,7 +343,7 @@ static const char *const _PyOpcode_OpName[263] = {
[JUMP_FORWARD] = "JUMP_FORWARD",
[JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP",
[JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP",
- [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
+ [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
[POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
[POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
[LOAD_GLOBAL] = "LOAD_GLOBAL",
@@ -349,7 +351,7 @@ static const char *const _PyOpcode_OpName[263] = {
[CONTAINS_OP] = "CONTAINS_OP",
[RERAISE] = "RERAISE",
[COPY] = "COPY",
- [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
+ [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[BINARY_OP] = "BINARY_OP",
[SEND] = "SEND",
[LOAD_FAST] = "LOAD_FAST",
@@ -371,7 +373,7 @@ static const char *const _PyOpcode_OpName[263] = {
[JUMP_BACKWARD] = "JUMP_BACKWARD",
[COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH",
[CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
- [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
+ [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD",
@@ -381,23 +383,23 @@ static const char *const _PyOpcode_OpName[263] = {
[YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS",
- [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
- [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
+ [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
+ [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING",
+ [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
+ [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
- [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
- [BB_NEXT] = "BB_NEXT",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
+ [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
+ [BB_NEXT] = "BB_NEXT",
[BINARY_OP_ADD_INT_TYPE_CHECK] = "BINARY_OP_ADD_INT_TYPE_CHECK",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
- [168] = "<168>",
- [169] = "<169>",
[170] = "<170>",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
@@ -496,8 +498,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 168: \
- case 169: \
case 170: \
case 174: \
case 175: \
diff --git a/Include/opcode.h b/Include/opcode.h
index 2b0c652e865331..bdd4f8b8e44d46 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -125,134 +125,138 @@ extern "C" {
#define JUMP_NO_INTERRUPT 261
#define LOAD_METHOD 262
#define MAX_PSEUDO_OPCODE 262
-#define BINARY_OP_ADD_FLOAT 5
-#define BINARY_OP_ADD_INT 6
-#define BINARY_OP_ADD_UNICODE 7
-#define BINARY_OP_INPLACE_ADD_UNICODE 8
-#define BINARY_OP_MULTIPLY_FLOAT 10
-#define BINARY_OP_MULTIPLY_INT 13
-#define BINARY_OP_SUBTRACT_FLOAT 14
-#define BINARY_OP_SUBTRACT_INT 16
-#define BINARY_SUBSCR_DICT 17
-#define BINARY_SUBSCR_GETITEM 18
-#define BINARY_SUBSCR_LIST_INT 19
-#define BINARY_SUBSCR_TUPLE_INT 20
-#define CALL_PY_EXACT_ARGS 21
-#define CALL_PY_WITH_DEFAULTS 22
-#define CALL_BOUND_METHOD_EXACT_ARGS 23
-#define CALL_BUILTIN_CLASS 24
-#define CALL_BUILTIN_FAST_WITH_KEYWORDS 28
-#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 29
-#define CALL_NO_KW_BUILTIN_FAST 34
-#define CALL_NO_KW_BUILTIN_O 38
-#define CALL_NO_KW_ISINSTANCE 39
-#define CALL_NO_KW_LEN 40
-#define CALL_NO_KW_LIST_APPEND 41
-#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 42
-#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 43
-#define CALL_NO_KW_METHOD_DESCRIPTOR_O 44
-#define CALL_NO_KW_STR_1 45
-#define CALL_NO_KW_TUPLE_1 46
-#define CALL_NO_KW_TYPE_1 47
-#define COMPARE_AND_BRANCH_FLOAT 48
-#define COMPARE_AND_BRANCH_INT 56
-#define COMPARE_AND_BRANCH_STR 57
-#define FOR_ITER_LIST 58
-#define FOR_ITER_TUPLE 59
-#define FOR_ITER_RANGE 62
-#define FOR_ITER_GEN 63
-#define LOAD_ATTR_CLASS 64
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 65
-#define LOAD_ATTR_INSTANCE_VALUE 66
-#define LOAD_ATTR_MODULE 67
-#define LOAD_ATTR_PROPERTY 70
-#define LOAD_ATTR_SLOT 72
-#define LOAD_ATTR_WITH_HINT 73
-#define LOAD_ATTR_METHOD_LAZY_DICT 76
-#define LOAD_ATTR_METHOD_NO_DICT 77
-#define LOAD_ATTR_METHOD_WITH_VALUES 78
-#define LOAD_CONST__LOAD_FAST 79
-#define LOAD_FAST__LOAD_CONST 80
-#define LOAD_FAST__LOAD_FAST 81
-#define LOAD_GLOBAL_BUILTIN 82
-#define LOAD_GLOBAL_MODULE 84
-#define STORE_ATTR_INSTANCE_VALUE 86
-#define STORE_ATTR_SLOT 87
-#define STORE_ATTR_WITH_HINT 113
-#define STORE_FAST__LOAD_FAST 121
-#define STORE_FAST__STORE_FAST 143
-#define STORE_SUBSCR_DICT 153
-#define STORE_SUBSCR_LIST_INT 154
-#define UNPACK_SEQUENCE_LIST 158
-#define UNPACK_SEQUENCE_TUPLE 159
-#define UNPACK_SEQUENCE_TWO_TUPLE 160
+#define RESUME_QUICK 5
+#define JUMP_BACKWARD_QUICK 6
+#define BINARY_OP_ADD_FLOAT 7
+#define BINARY_OP_ADD_INT 8
+#define BINARY_OP_ADD_UNICODE 10
+#define BINARY_OP_INPLACE_ADD_UNICODE 13
+#define BINARY_OP_MULTIPLY_FLOAT 14
+#define BINARY_OP_MULTIPLY_INT 16
+#define BINARY_OP_SUBTRACT_FLOAT 17
+#define BINARY_OP_SUBTRACT_INT 18
+#define BINARY_SUBSCR_DICT 19
+#define BINARY_SUBSCR_GETITEM 20
+#define BINARY_SUBSCR_LIST_INT 21
+#define BINARY_SUBSCR_TUPLE_INT 22
+#define CALL_PY_EXACT_ARGS 23
+#define CALL_PY_WITH_DEFAULTS 24
+#define CALL_BOUND_METHOD_EXACT_ARGS 28
+#define CALL_BUILTIN_CLASS 29
+#define CALL_BUILTIN_FAST_WITH_KEYWORDS 34
+#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 38
+#define CALL_NO_KW_BUILTIN_FAST 39
+#define CALL_NO_KW_BUILTIN_O 40
+#define CALL_NO_KW_ISINSTANCE 41
+#define CALL_NO_KW_LEN 42
+#define CALL_NO_KW_LIST_APPEND 43
+#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44
+#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45
+#define CALL_NO_KW_METHOD_DESCRIPTOR_O 46
+#define CALL_NO_KW_STR_1 47
+#define CALL_NO_KW_TUPLE_1 48
+#define CALL_NO_KW_TYPE_1 56
+#define COMPARE_AND_BRANCH_FLOAT 57
+#define COMPARE_AND_BRANCH_INT 58
+#define COMPARE_AND_BRANCH_STR 59
+#define FOR_ITER_LIST 62
+#define FOR_ITER_TUPLE 63
+#define FOR_ITER_RANGE 64
+#define FOR_ITER_GEN 65
+#define LOAD_ATTR_CLASS 66
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 67
+#define LOAD_ATTR_INSTANCE_VALUE 70
+#define LOAD_ATTR_MODULE 72
+#define LOAD_ATTR_PROPERTY 73
+#define LOAD_ATTR_SLOT 76
+#define LOAD_ATTR_WITH_HINT 77
+#define LOAD_ATTR_METHOD_LAZY_DICT 78
+#define LOAD_ATTR_METHOD_NO_DICT 79
+#define LOAD_ATTR_METHOD_WITH_VALUES 80
+#define LOAD_CONST__LOAD_FAST 81
+#define LOAD_FAST__LOAD_CONST 82
+#define LOAD_FAST__LOAD_FAST 84
+#define LOAD_GLOBAL_BUILTIN 86
+#define LOAD_GLOBAL_MODULE 87
+#define STORE_ATTR_INSTANCE_VALUE 113
+#define STORE_ATTR_SLOT 121
+#define STORE_ATTR_WITH_HINT 143
+#define STORE_FAST__LOAD_FAST 153
+#define STORE_FAST__STORE_FAST 154
+#define STORE_SUBSCR_DICT 158
+#define STORE_SUBSCR_LIST_INT 159
+#define UNPACK_SEQUENCE_LIST 160
+#define UNPACK_SEQUENCE_TUPLE 161
+#define UNPACK_SEQUENCE_TWO_TUPLE 166
#define DO_TRACING 255
// Tier 2 interpreter ops
-#define BINARY_OP_ADD_FLOAT 5
-#define BINARY_OP_ADD_INT 6
-#define BINARY_OP_ADD_UNICODE 7
-#define BINARY_OP_INPLACE_ADD_UNICODE 8
-#define BINARY_OP_MULTIPLY_FLOAT 10
-#define BINARY_OP_MULTIPLY_INT 13
-#define BINARY_OP_SUBTRACT_FLOAT 14
-#define BINARY_OP_SUBTRACT_INT 16
-#define BINARY_SUBSCR_DICT 17
-#define BINARY_SUBSCR_GETITEM 18
-#define BINARY_SUBSCR_LIST_INT 19
-#define BINARY_SUBSCR_TUPLE_INT 20
-#define CALL_PY_EXACT_ARGS 21
-#define CALL_PY_WITH_DEFAULTS 22
-#define CALL_BOUND_METHOD_EXACT_ARGS 23
-#define CALL_BUILTIN_CLASS 24
-#define CALL_BUILTIN_FAST_WITH_KEYWORDS 28
-#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 29
-#define CALL_NO_KW_BUILTIN_FAST 34
-#define CALL_NO_KW_BUILTIN_O 38
-#define CALL_NO_KW_ISINSTANCE 39
-#define CALL_NO_KW_LEN 40
-#define CALL_NO_KW_LIST_APPEND 41
-#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 42
-#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 43
-#define CALL_NO_KW_METHOD_DESCRIPTOR_O 44
-#define CALL_NO_KW_STR_1 45
-#define CALL_NO_KW_TUPLE_1 46
-#define CALL_NO_KW_TYPE_1 47
-#define COMPARE_AND_BRANCH_FLOAT 48
-#define COMPARE_AND_BRANCH_INT 56
-#define COMPARE_AND_BRANCH_STR 57
-#define FOR_ITER_LIST 58
-#define FOR_ITER_TUPLE 59
-#define FOR_ITER_RANGE 62
-#define FOR_ITER_GEN 63
-#define LOAD_ATTR_CLASS 64
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 65
-#define LOAD_ATTR_INSTANCE_VALUE 66
-#define LOAD_ATTR_MODULE 67
-#define LOAD_ATTR_PROPERTY 70
-#define LOAD_ATTR_SLOT 72
-#define LOAD_ATTR_WITH_HINT 73
-#define LOAD_ATTR_METHOD_LAZY_DICT 76
-#define LOAD_ATTR_METHOD_NO_DICT 77
-#define LOAD_ATTR_METHOD_WITH_VALUES 78
-#define LOAD_CONST__LOAD_FAST 79
-#define LOAD_FAST__LOAD_CONST 80
-#define LOAD_FAST__LOAD_FAST 81
-#define LOAD_GLOBAL_BUILTIN 82
-#define LOAD_GLOBAL_MODULE 84
-#define STORE_ATTR_INSTANCE_VALUE 86
-#define STORE_ATTR_SLOT 87
-#define STORE_ATTR_WITH_HINT 113
-#define STORE_FAST__LOAD_FAST 121
-#define STORE_FAST__STORE_FAST 143
-#define STORE_SUBSCR_DICT 153
-#define STORE_SUBSCR_LIST_INT 154
-#define UNPACK_SEQUENCE_LIST 158
-#define UNPACK_SEQUENCE_TUPLE 159
-#define UNPACK_SEQUENCE_TWO_TUPLE 160
+#define RESUME_QUICK 5
+#define JUMP_BACKWARD_QUICK 6
+#define BINARY_OP_ADD_FLOAT 7
+#define BINARY_OP_ADD_INT 8
+#define BINARY_OP_ADD_UNICODE 10
+#define BINARY_OP_INPLACE_ADD_UNICODE 13
+#define BINARY_OP_MULTIPLY_FLOAT 14
+#define BINARY_OP_MULTIPLY_INT 16
+#define BINARY_OP_SUBTRACT_FLOAT 17
+#define BINARY_OP_SUBTRACT_INT 18
+#define BINARY_SUBSCR_DICT 19
+#define BINARY_SUBSCR_GETITEM 20
+#define BINARY_SUBSCR_LIST_INT 21
+#define BINARY_SUBSCR_TUPLE_INT 22
+#define CALL_PY_EXACT_ARGS 23
+#define CALL_PY_WITH_DEFAULTS 24
+#define CALL_BOUND_METHOD_EXACT_ARGS 28
+#define CALL_BUILTIN_CLASS 29
+#define CALL_BUILTIN_FAST_WITH_KEYWORDS 34
+#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 38
+#define CALL_NO_KW_BUILTIN_FAST 39
+#define CALL_NO_KW_BUILTIN_O 40
+#define CALL_NO_KW_ISINSTANCE 41
+#define CALL_NO_KW_LEN 42
+#define CALL_NO_KW_LIST_APPEND 43
+#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44
+#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45
+#define CALL_NO_KW_METHOD_DESCRIPTOR_O 46
+#define CALL_NO_KW_STR_1 47
+#define CALL_NO_KW_TUPLE_1 48
+#define CALL_NO_KW_TYPE_1 56
+#define COMPARE_AND_BRANCH_FLOAT 57
+#define COMPARE_AND_BRANCH_INT 58
+#define COMPARE_AND_BRANCH_STR 59
+#define FOR_ITER_LIST 62
+#define FOR_ITER_TUPLE 63
+#define FOR_ITER_RANGE 64
+#define FOR_ITER_GEN 65
+#define LOAD_ATTR_CLASS 66
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 67
+#define LOAD_ATTR_INSTANCE_VALUE 70
+#define LOAD_ATTR_MODULE 72
+#define LOAD_ATTR_PROPERTY 73
+#define LOAD_ATTR_SLOT 76
+#define LOAD_ATTR_WITH_HINT 77
+#define LOAD_ATTR_METHOD_LAZY_DICT 78
+#define LOAD_ATTR_METHOD_NO_DICT 79
+#define LOAD_ATTR_METHOD_WITH_VALUES 80
+#define LOAD_CONST__LOAD_FAST 81
+#define LOAD_FAST__LOAD_CONST 82
+#define LOAD_FAST__LOAD_FAST 84
+#define LOAD_GLOBAL_BUILTIN 86
+#define LOAD_GLOBAL_MODULE 87
+#define STORE_ATTR_INSTANCE_VALUE 113
+#define STORE_ATTR_SLOT 121
+#define STORE_ATTR_WITH_HINT 143
+#define STORE_FAST__LOAD_FAST 153
+#define STORE_FAST__STORE_FAST 154
+#define STORE_SUBSCR_DICT 158
+#define STORE_SUBSCR_LIST_INT 159
+#define UNPACK_SEQUENCE_LIST 160
+#define UNPACK_SEQUENCE_TUPLE 161
+#define UNPACK_SEQUENCE_TWO_TUPLE 166
#define DO_TRACING 255
-#define BB_NEXT 161
-#define BINARY_OP_ADD_INT_TYPE_CHECK 166
-#define BINARY_OP_ADD_INT_REST 167
+#define BB_NEXT 167
+#define BINARY_OP_ADD_INT_TYPE_CHECK 168
+#define BINARY_OP_ADD_INT_REST 169
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 292eae330c2095..08c73abd28848f 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -279,6 +279,12 @@ def pseudo_op(name, op, real_ops):
]
_specializations = {
+ "RESUME": [
+ "RESUME_QUICK",
+ ],
+ "JUMP_BACKWARD": [
+ "JUMP_BACKWARD_QUICK",
+ ],
"BINARY_OP": [
"BINARY_OP_ADD_FLOAT",
"BINARY_OP_ADD_INT",
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index ab31b6582cdaae..ba3c7bdce5a772 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -409,6 +409,9 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
co->_co_linearray_entry_size = 0;
co->_co_linearray = NULL;
+ co->_tier2_warmup = -64;
+ co->_bb_next = NULL;
+ co->_bb_space = NULL;
memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code),
PyBytes_GET_SIZE(con->code));
int entry_point = 0;
@@ -1705,6 +1708,16 @@ code_dealloc(PyCodeObject *co)
if (co->_co_linearray) {
PyMem_Free(co->_co_linearray);
}
+ co->_bb_next = NULL;
+ if (co->_bb_space != NULL) {
+ // Traverse the linked list
+ for (_PyTier2BBSpace *curr = co->_bb_space; curr != NULL;) {
+ _PyTier2BBSpace *prev = curr;
+ curr = curr->next;
+ PyMem_Free(prev);
+ }
+ co->_bb_space = NULL;
+ }
PyObject_Free(co);
}
@@ -2282,6 +2295,16 @@ _PyStaticCode_Fini(PyCodeObject *co)
PyMem_Free(co->_co_cached);
co->_co_cached = NULL;
}
+ co->_bb_next = NULL;
+ if (co->_bb_space != NULL) {
+ // Traverse the linked list
+ for (_PyTier2BBSpace *curr = co->_bb_space; curr != NULL;) {
+ _PyTier2BBSpace *prev = curr;
+ curr = curr->next;
+ PyMem_Free(prev);
+ }
+ co->_bb_space = NULL;
+ }
co->co_extra = NULL;
if (co->co_weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject *)co);
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 35246653c45348..b74073c39314ef 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -336,7 +336,8 @@ _PyGen_yf(PyGenObject *gen)
return NULL;
}
_Py_CODEUNIT next = frame->prev_instr[1];
- if (_Py_OPCODE(next) != RESUME || _Py_OPARG(next) < 2)
+ if ((_Py_OPCODE(next) != RESUME && _Py_OPCODE(next) != RESUME_QUICK) ||
+ _Py_OPARG(next) < 2)
{
/* Not in a yield from */
return NULL;
@@ -372,7 +373,7 @@ gen_close(PyGenObject *gen, PyObject *args)
/* It is possible for the previous instruction to not be a
* YIELD_VALUE if the debugger has changed the lineno. */
if (err == 0 && frame->prev_instr->opcode == YIELD_VALUE) {
- assert(frame->prev_instr[1].opcode == RESUME);
+ assert(frame->prev_instr[1].opcode == RESUME || frame->prev_instr[1].opcode == RESUME_QUICK);
int exception_handler_depth = frame->prev_instr->oparg;
assert(exception_handler_depth > 0);
/* We can safely ignore the outermost try block
diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj
index 4f39756019e692..5271008885aa34 100644
--- a/PCbuild/_freeze_module.vcxproj
+++ b/PCbuild/_freeze_module.vcxproj
@@ -229,6 +229,7 @@
+
diff --git a/PCbuild/_freeze_module.vcxproj.filters b/PCbuild/_freeze_module.vcxproj.filters
index 7d7c4587b9a3f3..1f2ae059d6fd1a 100644
--- a/PCbuild/_freeze_module.vcxproj.filters
+++ b/PCbuild/_freeze_module.vcxproj.filters
@@ -343,6 +343,9 @@
Source Files
+
+ Source Files
+
Source Files
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index 791562710efc54..5a3723f8043e33 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -546,6 +546,7 @@
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index fe5bc990fec207..afa37380aebda4 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -1223,6 +1223,9 @@
Python
+
+ Python
+
Python
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 5fdf0b137e638a..491becdad860fd 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -88,6 +88,11 @@ dummy_func(
}
inst(RESUME, (--)) {
+ _PyCode_Tier2Warmup(frame, &next_instr);
+ GO_TO_INSTRUCTION(RESUME_QUICK);
+ }
+
+ inst(RESUME_QUICK, (--)) {
assert(tstate->cframe == &cframe);
assert(frame == cframe.current_frame);
if (_Py_atomic_load_relaxed_int32(eval_breaker) && oparg < 2) {
@@ -1949,6 +1954,11 @@ dummy_func(
}
inst(JUMP_BACKWARD, (--)) {
+ _PyCode_Tier2Warmup(frame, &next_instr);
+ GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
+ }
+
+ inst(JUMP_BACKWARD_QUICK, (--)) {
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index d7a8f0beeec872..0bac6c08a4f73d 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -140,7 +140,9 @@ GETITEM(PyObject *v, Py_ssize_t i) {
/* Code access macros */
/* The integer overflow is checked by an assertion below. */
-#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code)))
+// TODO change this calculation when interpreter is bb aware.
+#define INSTR_OFFSET() ((int)(next_instr - \
+ (frame->f_code->_bb_next == NULL ? _PyCode_CODE(frame->f_code) : frame->f_code->_bb_next->u_code)))
#define NEXTOPARG() do { \
_Py_CODEUNIT word = *next_instr; \
opcode = _Py_OPCODE(word); \
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index da28e41dbc7f70..e269f3bb35feb7 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -23,6 +23,12 @@
}
TARGET(RESUME) {
+ _PyCode_Tier2Warmup(frame, &next_instr);
+ GO_TO_INSTRUCTION(RESUME_QUICK);
+ }
+
+ TARGET(RESUME_QUICK) {
+ PREDICTED(RESUME_QUICK);
assert(tstate->cframe == &cframe);
assert(frame == cframe.current_frame);
if (_Py_atomic_load_relaxed_int32(eval_breaker) && oparg < 2) {
@@ -2360,7 +2366,16 @@
TARGET(JUMP_BACKWARD) {
PREDICTED(JUMP_BACKWARD);
- assert(oparg < INSTR_OFFSET());
+ _PyCode_Tier2Warmup(frame, &next_instr);
+ GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
+ }
+
+ TARGET(JUMP_BACKWARD_QUICK) {
+ PREDICTED(JUMP_BACKWARD_QUICK);
+ if (oparg >= INSTR_OFFSET()) {
+ fprintf(stderr, "%ld, %ld\n", oparg, INSTR_OFFSET());
+ }
+ //assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
DISPATCH();
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index f1c3f3e0c4ee17..98c27d3b27e25c 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -4,17 +4,19 @@ static void *opcode_targets[256] = {
&&TARGET_PUSH_NULL,
&&TARGET_INTERPRETER_EXIT,
&&TARGET_END_FOR,
+ &&TARGET_RESUME_QUICK,
+ &&TARGET_JUMP_BACKWARD_QUICK,
&&TARGET_BINARY_OP_ADD_FLOAT,
&&TARGET_BINARY_OP_ADD_INT,
- &&TARGET_BINARY_OP_ADD_UNICODE,
- &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE,
&&TARGET_NOP,
- &&TARGET_BINARY_OP_MULTIPLY_FLOAT,
+ &&TARGET_BINARY_OP_ADD_UNICODE,
&&TARGET_UNARY_NEGATIVE,
&&TARGET_UNARY_NOT,
+ &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE,
+ &&TARGET_BINARY_OP_MULTIPLY_FLOAT,
+ &&TARGET_UNARY_INVERT,
&&TARGET_BINARY_OP_MULTIPLY_INT,
&&TARGET_BINARY_OP_SUBTRACT_FLOAT,
- &&TARGET_UNARY_INVERT,
&&TARGET_BINARY_OP_SUBTRACT_INT,
&&TARGET_BINARY_SUBSCR_DICT,
&&TARGET_BINARY_SUBSCR_GETITEM,
@@ -22,21 +24,21 @@ static void *opcode_targets[256] = {
&&TARGET_BINARY_SUBSCR_TUPLE_INT,
&&TARGET_CALL_PY_EXACT_ARGS,
&&TARGET_CALL_PY_WITH_DEFAULTS,
- &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS,
- &&TARGET_CALL_BUILTIN_CLASS,
&&TARGET_BINARY_SUBSCR,
&&TARGET_BINARY_SLICE,
&&TARGET_STORE_SLICE,
- &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS,
- &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS,
+ &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS,
+ &&TARGET_CALL_BUILTIN_CLASS,
&&TARGET_GET_LEN,
&&TARGET_MATCH_MAPPING,
&&TARGET_MATCH_SEQUENCE,
&&TARGET_MATCH_KEYS,
- &&TARGET_CALL_NO_KW_BUILTIN_FAST,
+ &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS,
&&TARGET_PUSH_EXC_INFO,
&&TARGET_CHECK_EXC_MATCH,
&&TARGET_CHECK_EG_MATCH,
+ &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS,
+ &&TARGET_CALL_NO_KW_BUILTIN_FAST,
&&TARGET_CALL_NO_KW_BUILTIN_O,
&&TARGET_CALL_NO_KW_ISINSTANCE,
&&TARGET_CALL_NO_KW_LEN,
@@ -46,8 +48,6 @@ static void *opcode_targets[256] = {
&&TARGET_CALL_NO_KW_METHOD_DESCRIPTOR_O,
&&TARGET_CALL_NO_KW_STR_1,
&&TARGET_CALL_NO_KW_TUPLE_1,
- &&TARGET_CALL_NO_KW_TYPE_1,
- &&TARGET_COMPARE_AND_BRANCH_FLOAT,
&&TARGET_WITH_EXCEPT_START,
&&TARGET_GET_AITER,
&&TARGET_GET_ANEXT,
@@ -55,38 +55,38 @@ static void *opcode_targets[256] = {
&&TARGET_BEFORE_WITH,
&&TARGET_END_ASYNC_FOR,
&&TARGET_CLEANUP_THROW,
+ &&TARGET_CALL_NO_KW_TYPE_1,
+ &&TARGET_COMPARE_AND_BRANCH_FLOAT,
&&TARGET_COMPARE_AND_BRANCH_INT,
&&TARGET_COMPARE_AND_BRANCH_STR,
- &&TARGET_FOR_ITER_LIST,
- &&TARGET_FOR_ITER_TUPLE,
&&TARGET_STORE_SUBSCR,
&&TARGET_DELETE_SUBSCR,
+ &&TARGET_FOR_ITER_LIST,
+ &&TARGET_FOR_ITER_TUPLE,
&&TARGET_FOR_ITER_RANGE,
&&TARGET_FOR_ITER_GEN,
&&TARGET_LOAD_ATTR_CLASS,
&&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
- &&TARGET_LOAD_ATTR_INSTANCE_VALUE,
- &&TARGET_LOAD_ATTR_MODULE,
&&TARGET_GET_ITER,
&&TARGET_GET_YIELD_FROM_ITER,
- &&TARGET_LOAD_ATTR_PROPERTY,
+ &&TARGET_LOAD_ATTR_INSTANCE_VALUE,
&&TARGET_LOAD_BUILD_CLASS,
- &&TARGET_LOAD_ATTR_SLOT,
- &&TARGET_LOAD_ATTR_WITH_HINT,
+ &&TARGET_LOAD_ATTR_MODULE,
+ &&TARGET_LOAD_ATTR_PROPERTY,
&&TARGET_LOAD_ASSERTION_ERROR,
&&TARGET_RETURN_GENERATOR,
+ &&TARGET_LOAD_ATTR_SLOT,
+ &&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LOAD_ATTR_METHOD_LAZY_DICT,
&&TARGET_LOAD_ATTR_METHOD_NO_DICT,
&&TARGET_LOAD_ATTR_METHOD_WITH_VALUES,
&&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_LOAD_FAST__LOAD_CONST,
+ &&TARGET_RETURN_VALUE,
&&TARGET_LOAD_FAST__LOAD_FAST,
+ &&TARGET_SETUP_ANNOTATIONS,
&&TARGET_LOAD_GLOBAL_BUILTIN,
- &&TARGET_RETURN_VALUE,
&&TARGET_LOAD_GLOBAL_MODULE,
- &&TARGET_SETUP_ANNOTATIONS,
- &&TARGET_STORE_ATTR_INSTANCE_VALUE,
- &&TARGET_STORE_ATTR_SLOT,
&&TARGET_PREP_RERAISE_STAR,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
@@ -112,7 +112,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_FORWARD,
&&TARGET_JUMP_IF_FALSE_OR_POP,
&&TARGET_JUMP_IF_TRUE_OR_POP,
- &&TARGET_STORE_ATTR_WITH_HINT,
+ &&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_POP_JUMP_IF_FALSE,
&&TARGET_POP_JUMP_IF_TRUE,
&&TARGET_LOAD_GLOBAL,
@@ -120,7 +120,7 @@ static void *opcode_targets[256] = {
&&TARGET_CONTAINS_OP,
&&TARGET_RERAISE,
&&TARGET_COPY,
- &&TARGET_STORE_FAST__LOAD_FAST,
+ &&TARGET_STORE_ATTR_SLOT,
&&TARGET_BINARY_OP,
&&TARGET_SEND,
&&TARGET_LOAD_FAST,
@@ -142,7 +142,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_BACKWARD,
&&TARGET_COMPARE_AND_BRANCH,
&&TARGET_CALL_FUNCTION_EX,
- &&TARGET_STORE_FAST__STORE_FAST,
+ &&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
&&TARGET_SET_ADD,
@@ -152,20 +152,20 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE,
&&TARGET_RESUME,
&&TARGET_MATCH_CLASS,
- &&TARGET_STORE_SUBSCR_DICT,
- &&TARGET_STORE_SUBSCR_LIST_INT,
+ &&TARGET_STORE_FAST__LOAD_FAST,
+ &&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING,
+ &&TARGET_STORE_SUBSCR_DICT,
+ &&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_UNPACK_SEQUENCE_LIST,
&&TARGET_UNPACK_SEQUENCE_TUPLE,
- &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
- &&_unknown_opcode,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
- &&_unknown_opcode,
+ &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
diff --git a/Python/tier2.c b/Python/tier2.c
new file mode 100644
index 00000000000000..f3ece7f851a262
--- /dev/null
+++ b/Python/tier2.c
@@ -0,0 +1,62 @@
+#include "Python.h"
+#include "pycore_code.h"
+#include "pycore_frame.h"
+
+#include "opcode.h"
+
+static int
+_PyCode_Tier2Initialize(_PyInterpreterFrame *, _Py_CODEUNIT **);
+
+// Tier 2 warmup counter
+void
+_PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT **next_instr)
+{
+ PyCodeObject *code = frame->f_code;
+ if (code->_tier2_warmup != 0) {
+ code->_tier2_warmup++;
+ if (code->_tier2_warmup == 0) {
+ // If it fails, due to lack of memory or whatever,
+ // just fall back to the tier 1 interpreter.
+ if (_PyCode_Tier2Initialize(frame, next_instr) < 0) {
+ PyErr_Clear();
+ }
+ }
+ }
+}
+
+static int
+_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT **next_instr)
+{
+ PyCodeObject *co = frame->f_code;
+ if (co->_bb_space != NULL) {
+ return 0;
+ }
+ // 1. Initialize basic blocks space.
+ // 2. Copy over instructions to basic blocks.
+ // (For now, just have one basic block = on code object)
+ // @TODO split up code object into basic blocks.
+ // 3. Set the instruction pointer to correct one.
+ fprintf(stderr, "INITIALIZING %ld\n", Py_SIZE(co));
+ _PyTier2BBSpace *bb_space = PyMem_Malloc(Py_SIZE(co) * sizeof(_Py_CODEUNIT) * 2);
+ if (bb_space == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ bb_space->next = NULL;
+ bb_space->water_level = 0;
+ co->_bb_space = bb_space;
+
+ _PyTier2BB *bb_ptr = (_PyTier2BB *)bb_space->bbs;
+ bb_ptr->tier1_start = _PyCode_CODE(co);
+ memcpy(bb_ptr->u_code, _PyCode_CODE(co), Py_SIZE(co) * sizeof(_Py_CODEUNIT));
+
+ co->_bb_next = bb_ptr;
+ // test to see we are working
+ _py_set_opcode(bb_ptr->u_code, CACHE);
+
+ // Set the instruction pointer to the next one in the bb
+ frame->prev_instr = bb_ptr->u_code + (frame->prev_instr - _PyCode_CODE(co));
+ *next_instr = frame->prev_instr + 1;
+
+ return 0;
+}
diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py
index 511b26a5ce3dc7..7718bc95722138 100644
--- a/Tools/build/deepfreeze.py
+++ b/Tools/build/deepfreeze.py
@@ -277,6 +277,9 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
self.write(f".co_linetable = {co_linetable},")
self.write(f"._co_cached = NULL,")
self.write("._co_linearray = NULL,")
+ self.write("._tier2_warmup = -64,")
+ self.write("._bb_next = NULL,")
+ self.write("._bb_space = NULL,")
self.write(f".co_code_adaptive = {co_code_adaptive},")
for i, op in enumerate(code.co_code[::2]):
if op == RESUME:
From 9b2e69a73c6d5db67c0e8a55efbaa79b72e9d233 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sun, 29 Jan 2023 21:56:16 +0800
Subject: [PATCH 011/280] [partially not working] rewrite resumes and jumps
---
Include/cpython/code.h | 1 +
Include/internal/pycore_code.h | 2 +-
Python/bytecodes.c | 7 +++--
Python/generated_cases.c.h | 8 ++---
Python/tier2.c | 56 +++++++++++++++++++++++-----------
5 files changed, 49 insertions(+), 25 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index e0c173411594d4..01eed3380a4929 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -47,6 +47,7 @@ typedef struct {
typedef struct _PyTier2BB {
// Stores the start pointer in the tier 1 bytecode.
// So that when we exit the trace we can calculate where to return.
+ struct _PyTier2BB *bb_next;
_Py_CODEUNIT *tier1_start;
_Py_CODEUNIT u_code[1];
} _PyTier2BB;
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 090353cac17d6a..392ea9cf83055a 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -240,7 +240,7 @@ extern void _PyStaticCode_Fini(PyCodeObject *co);
extern int _PyStaticCode_Init(PyCodeObject *co);
/* Tier 2 interpreter */
-extern void _PyCode_Tier2Warmup(struct _PyInterpreterFrame *, _Py_CODEUNIT **);
+extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *, _Py_CODEUNIT *);
#ifdef Py_STATS
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 491becdad860fd..473c9a6f8c814a 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -88,7 +88,7 @@ dummy_func(
}
inst(RESUME, (--)) {
- _PyCode_Tier2Warmup(frame, &next_instr);
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
GO_TO_INSTRUCTION(RESUME_QUICK);
}
@@ -1954,11 +1954,14 @@ dummy_func(
}
inst(JUMP_BACKWARD, (--)) {
- _PyCode_Tier2Warmup(frame, &next_instr);
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
}
inst(JUMP_BACKWARD_QUICK, (--)) {
+ if (oparg >= INSTR_OFFSET()) {
+ fprintf(stderr, "%ld, %p, %p, %p\n", oparg, next_instr, _PyCode_CODE(frame->f_code), frame->f_code->_bb_next->u_code);
+ }
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index e269f3bb35feb7..1a4aadb96b40d6 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -23,7 +23,7 @@
}
TARGET(RESUME) {
- _PyCode_Tier2Warmup(frame, &next_instr);
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
GO_TO_INSTRUCTION(RESUME_QUICK);
}
@@ -2366,16 +2366,16 @@
TARGET(JUMP_BACKWARD) {
PREDICTED(JUMP_BACKWARD);
- _PyCode_Tier2Warmup(frame, &next_instr);
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
}
TARGET(JUMP_BACKWARD_QUICK) {
PREDICTED(JUMP_BACKWARD_QUICK);
if (oparg >= INSTR_OFFSET()) {
- fprintf(stderr, "%ld, %ld\n", oparg, INSTR_OFFSET());
+ fprintf(stderr, "%ld, %p, %p, %p IS_NULL %d\n", oparg, next_instr, _PyCode_CODE(frame->f_code), frame->f_code->_bb_next->u_code, frame->f_code->_bb_next == NULL);
}
- //assert(oparg < INSTR_OFFSET());
+ assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
DISPATCH();
diff --git a/Python/tier2.c b/Python/tier2.c
index f3ece7f851a262..25c5afe855f922 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -4,12 +4,12 @@
#include "opcode.h"
-static int
-_PyCode_Tier2Initialize(_PyInterpreterFrame *, _Py_CODEUNIT **);
+static _Py_CODEUNIT *
+_PyCode_Tier2Initialize(_PyInterpreterFrame *, _Py_CODEUNIT *);
// Tier 2 warmup counter
-void
-_PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT **next_instr)
+_Py_CODEUNIT *
+_PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
PyCodeObject *code = frame->f_code;
if (code->_tier2_warmup != 0) {
@@ -17,30 +17,35 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT **next_instr)
if (code->_tier2_warmup == 0) {
// If it fails, due to lack of memory or whatever,
// just fall back to the tier 1 interpreter.
- if (_PyCode_Tier2Initialize(frame, next_instr) < 0) {
+ _Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
+ if (next == NULL) {
PyErr_Clear();
}
+ else {
+ return next;
+ }
}
}
+ return next_instr;
}
-static int
-_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT **next_instr)
+static _Py_CODEUNIT *
+_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
+ int curr = _Py_OPCODE(*(next_instr - 1));
+ assert(curr == RESUME || curr == JUMP_BACKWARD);
PyCodeObject *co = frame->f_code;
- if (co->_bb_space != NULL) {
- return 0;
- }
+ assert(co->_bb_space == NULL);
// 1. Initialize basic blocks space.
// 2. Copy over instructions to basic blocks.
// (For now, just have one basic block = on code object)
// @TODO split up code object into basic blocks.
// 3. Set the instruction pointer to correct one.
fprintf(stderr, "INITIALIZING %ld\n", Py_SIZE(co));
- _PyTier2BBSpace *bb_space = PyMem_Malloc(Py_SIZE(co) * sizeof(_Py_CODEUNIT) * 2);
+ _PyTier2BBSpace *bb_space = PyMem_Malloc((sizeof(_PyTier2BB) + Py_SIZE(co) * sizeof(_Py_CODEUNIT)) * 2);
if (bb_space == NULL) {
PyErr_NoMemory();
- return -1;
+ return NULL;
}
bb_space->next = NULL;
bb_space->water_level = 0;
@@ -49,14 +54,29 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT **next_instr)
_PyTier2BB *bb_ptr = (_PyTier2BB *)bb_space->bbs;
bb_ptr->tier1_start = _PyCode_CODE(co);
memcpy(bb_ptr->u_code, _PyCode_CODE(co), Py_SIZE(co) * sizeof(_Py_CODEUNIT));
+ // Remove all the RESUME and JUMP_BACKWARDS instructions
+ for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
+ _Py_CODEUNIT instr = bb_ptr->u_code[i];
+ switch (_Py_OPCODE(instr)) {
+ case RESUME:
+ _py_set_opcode(&(bb_ptr->u_code[i]), RESUME_QUICK);
+ break;
+ case JUMP_BACKWARD:
+ _py_set_opcode(&(bb_ptr->u_code[i]), JUMP_BACKWARD);
+ break;
+ }
+
+ }
+
co->_bb_next = bb_ptr;
- // test to see we are working
- _py_set_opcode(bb_ptr->u_code, CACHE);
- // Set the instruction pointer to the next one in the bb
- frame->prev_instr = bb_ptr->u_code + (frame->prev_instr - _PyCode_CODE(co));
- *next_instr = frame->prev_instr + 1;
- return 0;
+ // Set the instruction pointer to the next one in the bb
+ Py_ssize_t offset_from_start = (frame->prev_instr - _PyCode_CODE(co));
+ assert(offset_from_start >= -1);
+ frame->prev_instr = bb_ptr->u_code + offset_from_start;
+ // test to see we are working
+ // _py_set_opcode(next_instr, CACHE);
+ return bb_ptr->u_code + (next_instr - _PyCode_CODE(co));
}
From 319c18938a70bfb15d9aa4f69b25fd0e153bc6c4 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 30 Jan 2023 02:41:55 +0800
Subject: [PATCH 012/280] Use first_instr
---
Include/cpython/code.h | 1 +
Include/internal/pycore_frame.h | 3 +--
Include/internal/pycore_opcode_macro_to_micro.h | 2 ++
Lib/importlib/_bootstrap_external.py | 2 +-
Objects/codeobject.c | 4 ++++
Python/bytecodes.c | 10 +++++++---
Python/ceval_macros.h | 3 +--
Python/generated_cases.c.h | 11 ++++++++---
Python/tier2.c | 14 +++++---------
Tools/build/deepfreeze.py | 1 +
10 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 01eed3380a4929..594dfbc7eb7213 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -119,6 +119,7 @@ typedef struct _PyTier2BBSpace {
_PyCoCached *_co_cached; /* cached co_* attributes */ \
int _co_firsttraceable; /* index of first traceable instruction */ \
char *_co_linearray; /* array of line offsets */ \
+ _Py_CODEUNIT *_first_instr; /* points to first tier 1/tier 2 instruction */ \
int _tier2_warmup; /* warmup counter for tier 2 */ \
_PyTier2BB *_bb_next; /* the tier 2 basic block to execute (if any) */ \
_PyTier2BBSpace *_bb_space; /* linked list storing basic blocks */ \
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index 695fc94cc956a9..63fb9b2d26fca5 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -69,8 +69,7 @@ typedef struct _PyInterpreterFrame {
} _PyInterpreterFrame;
#define _PyInterpreterFrame_LASTI(IF) \
- ((int)((IF)->prev_instr - \
- ((IF)->f_code->_bb_next == NULL ? _PyCode_CODE((IF)->f_code) : (IF)->f_code->_bb_next->u_code)))
+ ((int)((IF)->prev_instr - (IF)->f_code->_first_instr))
static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
return f->localsplus + f->f_code->co_nlocalsplus;
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index ab2cd8b54eb368..13d681be4afa83 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -16,6 +16,7 @@ extern "C" {
extern const int _Py_MacroOpUOpCount[] = {
[NOP] = 1,
[RESUME] = 1,
+[RESUME_QUICK] = 1,
[LOAD_CLOSURE] = 1,
[LOAD_FAST_CHECK] = 1,
[LOAD_FAST] = 1,
@@ -123,6 +124,7 @@ extern const int _Py_MacroOpUOpCount[] = {
[IMPORT_FROM] = 1,
[JUMP_FORWARD] = 1,
[JUMP_BACKWARD] = 1,
+[JUMP_BACKWARD_QUICK] = 1,
[POP_JUMP_IF_FALSE] = 1,
[POP_JUMP_IF_TRUE] = 1,
[POP_JUMP_IF_NOT_NONE] = 1,
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index e760fbb15759d4..ebe1ef832289ac 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -443,7 +443,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3517).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3519).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index ba3c7bdce5a772..cfc07fc98b8ba4 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -409,6 +409,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
co->_co_linearray_entry_size = 0;
co->_co_linearray = NULL;
+ co->_first_instr = _PyCode_CODE(co);
co->_tier2_warmup = -64;
co->_bb_next = NULL;
co->_bb_space = NULL;
@@ -1708,6 +1709,7 @@ code_dealloc(PyCodeObject *co)
if (co->_co_linearray) {
PyMem_Free(co->_co_linearray);
}
+ co->_first_instr = NULL;
co->_bb_next = NULL;
if (co->_bb_space != NULL) {
// Traverse the linked list
@@ -2295,6 +2297,7 @@ _PyStaticCode_Fini(PyCodeObject *co)
PyMem_Free(co->_co_cached);
co->_co_cached = NULL;
}
+ co->_first_instr = NULL;
co->_bb_next = NULL;
if (co->_bb_space != NULL) {
// Traverse the linked list
@@ -2319,6 +2322,7 @@ _PyStaticCode_Fini(PyCodeObject *co)
int
_PyStaticCode_Init(PyCodeObject *co)
{
+ co->_first_instr = _PyCode_CODE(co);
int res = intern_strings(co->co_names);
if (res < 0) {
return -1;
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 473c9a6f8c814a..e0f18670358cc8 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -88,7 +88,9 @@ dummy_func(
}
inst(RESUME, (--)) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ if (cframe.use_tracing == 0) {
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ }
GO_TO_INSTRUCTION(RESUME_QUICK);
}
@@ -1954,13 +1956,15 @@ dummy_func(
}
inst(JUMP_BACKWARD, (--)) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ if (cframe.use_tracing == 0) {
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ }
GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
}
inst(JUMP_BACKWARD_QUICK, (--)) {
if (oparg >= INSTR_OFFSET()) {
- fprintf(stderr, "%ld, %p, %p, %p\n", oparg, next_instr, _PyCode_CODE(frame->f_code), frame->f_code->_bb_next->u_code);
+ fprintf(stderr, "%ld, %p, %p, %p, %p, %p\n", oparg, next_instr, _PyCode_CODE(frame->f_code), frame->f_code->_bb_next->u_code, frame->f_code->_first_instr, frame->prev_instr);
}
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 0bac6c08a4f73d..255409348ee74a 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -141,8 +141,7 @@ GETITEM(PyObject *v, Py_ssize_t i) {
/* The integer overflow is checked by an assertion below. */
// TODO change this calculation when interpreter is bb aware.
-#define INSTR_OFFSET() ((int)(next_instr - \
- (frame->f_code->_bb_next == NULL ? _PyCode_CODE(frame->f_code) : frame->f_code->_bb_next->u_code)))
+#define INSTR_OFFSET() ((int)(next_instr - frame->f_code->_first_instr))
#define NEXTOPARG() do { \
_Py_CODEUNIT word = *next_instr; \
opcode = _Py_OPCODE(word); \
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 1a4aadb96b40d6..c9e79c5f846e64 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -23,7 +23,9 @@
}
TARGET(RESUME) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ if (cframe.use_tracing == 0) {
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ }
GO_TO_INSTRUCTION(RESUME_QUICK);
}
@@ -2366,14 +2368,17 @@
TARGET(JUMP_BACKWARD) {
PREDICTED(JUMP_BACKWARD);
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ if (cframe.use_tracing == 0) {
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ }
GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
}
TARGET(JUMP_BACKWARD_QUICK) {
PREDICTED(JUMP_BACKWARD_QUICK);
if (oparg >= INSTR_OFFSET()) {
- fprintf(stderr, "%ld, %p, %p, %p IS_NULL %d\n", oparg, next_instr, _PyCode_CODE(frame->f_code), frame->f_code->_bb_next->u_code, frame->f_code->_bb_next == NULL);
+ fprintf(stderr, "%ld, %p, %p, %p, %p, %p\n", oparg, next_instr, _PyCode_CODE(frame->f_code), frame->f_code->_bb_next->u_code, frame->f_code->_first_instr, frame->prev_instr);
+
}
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
diff --git a/Python/tier2.c b/Python/tier2.c
index 25c5afe855f922..ce1dbad39c6866 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -18,10 +18,7 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// If it fails, due to lack of memory or whatever,
// just fall back to the tier 1 interpreter.
_Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
- if (next == NULL) {
- PyErr_Clear();
- }
- else {
+ if (next != NULL) {
return next;
}
}
@@ -42,18 +39,17 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// @TODO split up code object into basic blocks.
// 3. Set the instruction pointer to correct one.
fprintf(stderr, "INITIALIZING %ld\n", Py_SIZE(co));
- _PyTier2BBSpace *bb_space = PyMem_Malloc((sizeof(_PyTier2BB) + Py_SIZE(co) * sizeof(_Py_CODEUNIT)) * 2);
+ _PyTier2BBSpace *bb_space = PyMem_Malloc((sizeof(_PyTier2BB) + _PyCode_NBYTES(co)) * 2);
if (bb_space == NULL) {
- PyErr_NoMemory();
return NULL;
}
bb_space->next = NULL;
bb_space->water_level = 0;
co->_bb_space = bb_space;
- _PyTier2BB *bb_ptr = (_PyTier2BB *)bb_space->bbs;
+ _PyTier2BB *bb_ptr = bb_space->bbs;
bb_ptr->tier1_start = _PyCode_CODE(co);
- memcpy(bb_ptr->u_code, _PyCode_CODE(co), Py_SIZE(co) * sizeof(_Py_CODEUNIT));
+ memcpy(bb_ptr->u_code, _PyCode_CODE(co), _PyCode_NBYTES(co));
// Remove all the RESUME and JUMP_BACKWARDS instructions
for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
_Py_CODEUNIT instr = bb_ptr->u_code[i];
@@ -70,7 +66,7 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
co->_bb_next = bb_ptr;
-
+ co->_first_instr = bb_ptr->u_code;
// Set the instruction pointer to the next one in the bb
Py_ssize_t offset_from_start = (frame->prev_instr - _PyCode_CODE(co));
diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py
index 7718bc95722138..81e64516ecff3a 100644
--- a/Tools/build/deepfreeze.py
+++ b/Tools/build/deepfreeze.py
@@ -277,6 +277,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
self.write(f".co_linetable = {co_linetable},")
self.write(f"._co_cached = NULL,")
self.write("._co_linearray = NULL,")
+ self.write(f"._first_instr = NULL,")
self.write("._tier2_warmup = -64,")
self.write("._bb_next = NULL,")
self.write("._bb_space = NULL,")
From d749e58e33e3b23406caf6dbd680c8797f2c9a69 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 30 Jan 2023 16:33:40 +0800
Subject: [PATCH 013/280] Remove first_instr
We don't need to calculate the first_instr because all basic blocks won't have JUMP instructions
---
Include/cpython/code.h | 1 -
Include/internal/pycore_frame.h | 2 +-
Objects/codeobject.c | 4 ----
Python/bytecodes.c | 3 ---
Python/ceval_macros.h | 2 +-
Python/generated_cases.c.h | 4 ----
Tools/build/deepfreeze.py | 1 -
7 files changed, 2 insertions(+), 15 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 594dfbc7eb7213..01eed3380a4929 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -119,7 +119,6 @@ typedef struct _PyTier2BBSpace {
_PyCoCached *_co_cached; /* cached co_* attributes */ \
int _co_firsttraceable; /* index of first traceable instruction */ \
char *_co_linearray; /* array of line offsets */ \
- _Py_CODEUNIT *_first_instr; /* points to first tier 1/tier 2 instruction */ \
int _tier2_warmup; /* warmup counter for tier 2 */ \
_PyTier2BB *_bb_next; /* the tier 2 basic block to execute (if any) */ \
_PyTier2BBSpace *_bb_space; /* linked list storing basic blocks */ \
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index 63fb9b2d26fca5..1aff2dc6a6d065 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -69,7 +69,7 @@ typedef struct _PyInterpreterFrame {
} _PyInterpreterFrame;
#define _PyInterpreterFrame_LASTI(IF) \
- ((int)((IF)->prev_instr - (IF)->f_code->_first_instr))
+ ((int)((IF)->prev_instr - _PyCode_CODE((IF)->f_code)))
static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
return f->localsplus + f->f_code->co_nlocalsplus;
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index cfc07fc98b8ba4..ba3c7bdce5a772 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -409,7 +409,6 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
co->_co_linearray_entry_size = 0;
co->_co_linearray = NULL;
- co->_first_instr = _PyCode_CODE(co);
co->_tier2_warmup = -64;
co->_bb_next = NULL;
co->_bb_space = NULL;
@@ -1709,7 +1708,6 @@ code_dealloc(PyCodeObject *co)
if (co->_co_linearray) {
PyMem_Free(co->_co_linearray);
}
- co->_first_instr = NULL;
co->_bb_next = NULL;
if (co->_bb_space != NULL) {
// Traverse the linked list
@@ -2297,7 +2295,6 @@ _PyStaticCode_Fini(PyCodeObject *co)
PyMem_Free(co->_co_cached);
co->_co_cached = NULL;
}
- co->_first_instr = NULL;
co->_bb_next = NULL;
if (co->_bb_space != NULL) {
// Traverse the linked list
@@ -2322,7 +2319,6 @@ _PyStaticCode_Fini(PyCodeObject *co)
int
_PyStaticCode_Init(PyCodeObject *co)
{
- co->_first_instr = _PyCode_CODE(co);
int res = intern_strings(co->co_names);
if (res < 0) {
return -1;
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index e0f18670358cc8..ad16ad8f203158 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1963,9 +1963,6 @@ dummy_func(
}
inst(JUMP_BACKWARD_QUICK, (--)) {
- if (oparg >= INSTR_OFFSET()) {
- fprintf(stderr, "%ld, %p, %p, %p, %p, %p\n", oparg, next_instr, _PyCode_CODE(frame->f_code), frame->f_code->_bb_next->u_code, frame->f_code->_first_instr, frame->prev_instr);
- }
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 255409348ee74a..7d601c71fde43b 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -141,7 +141,7 @@ GETITEM(PyObject *v, Py_ssize_t i) {
/* The integer overflow is checked by an assertion below. */
// TODO change this calculation when interpreter is bb aware.
-#define INSTR_OFFSET() ((int)(next_instr - frame->f_code->_first_instr))
+#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code)))
#define NEXTOPARG() do { \
_Py_CODEUNIT word = *next_instr; \
opcode = _Py_OPCODE(word); \
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index c9e79c5f846e64..f8766a4ccd1bb1 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2376,10 +2376,6 @@
TARGET(JUMP_BACKWARD_QUICK) {
PREDICTED(JUMP_BACKWARD_QUICK);
- if (oparg >= INSTR_OFFSET()) {
- fprintf(stderr, "%ld, %p, %p, %p, %p, %p\n", oparg, next_instr, _PyCode_CODE(frame->f_code), frame->f_code->_bb_next->u_code, frame->f_code->_first_instr, frame->prev_instr);
-
- }
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py
index 81e64516ecff3a..7718bc95722138 100644
--- a/Tools/build/deepfreeze.py
+++ b/Tools/build/deepfreeze.py
@@ -277,7 +277,6 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
self.write(f".co_linetable = {co_linetable},")
self.write(f"._co_cached = NULL,")
self.write("._co_linearray = NULL,")
- self.write(f"._first_instr = NULL,")
self.write("._tier2_warmup = -64,")
self.write("._bb_next = NULL,")
self.write("._bb_space = NULL,")
From 90cd348cc17ecedefa4c9edd15ad3e02c0a084f4 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 30 Jan 2023 16:34:21 +0800
Subject: [PATCH 014/280] Remove first_instr for real this time
---
Python/tier2.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index ce1dbad39c6866..4225a25f2dde7d 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -66,7 +66,6 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
co->_bb_next = bb_ptr;
- co->_first_instr = bb_ptr->u_code;
// Set the instruction pointer to the next one in the bb
Py_ssize_t offset_from_start = (frame->prev_instr - _PyCode_CODE(co));
From 266f8fbe4f0329e4b46ae2de150a48c2971acb5d Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 30 Jan 2023 19:48:46 +0800
Subject: [PATCH 015/280] try (and fail) to add CACHE after each jump
---
Include/internal/pycore_code.h | 7 +++++++
Include/internal/pycore_opcode.h | 8 +++++++-
Lib/importlib/_bootstrap_external.py | 2 +-
Lib/opcode.py | 20 ++++++++++++++++++++
Python/specialize.c | 26 +++++++++++++-------------
Python/tier2.c | 12 ++++++------
6 files changed, 54 insertions(+), 21 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 392ea9cf83055a..991278ac1d80d3 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -41,10 +41,17 @@ typedef struct {
typedef struct {
uint16_t counter;
+ uint16_t branch_counter;
} _PyCompareOpCache;
#define INLINE_CACHE_ENTRIES_COMPARE_OP CACHE_ENTRIES(_PyCompareOpCache)
+typedef struct {
+ uint16_t branch_counter;
+} _PyConditionalJumpCache;
+
+#define INLINE_CACHE_ENTRIES_CONDITIONAL_JUMP CACHE_ENTRIES(_PyConditionalJumpCache)
+
typedef struct {
uint16_t counter;
uint16_t type_version[2];
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index b7f27863ba5e24..3d6fe300c0b584 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -48,9 +48,15 @@ const uint8_t _PyOpcode_Caches[256] = {
[STORE_ATTR] = 4,
[LOAD_ATTR] = 9,
[COMPARE_OP] = 1,
+ [JUMP_IF_FALSE_OR_POP] = 1,
+ [JUMP_IF_TRUE_OR_POP] = 1,
+ [POP_JUMP_IF_FALSE] = 1,
+ [POP_JUMP_IF_TRUE] = 1,
[LOAD_GLOBAL] = 5,
[BINARY_OP] = 1,
- [COMPARE_AND_BRANCH] = 1,
+ [POP_JUMP_IF_NOT_NONE] = 1,
+ [POP_JUMP_IF_NONE] = 1,
+ [COMPARE_AND_BRANCH] = 2,
[CALL] = 4,
};
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index ebe1ef832289ac..4decba46906772 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -443,7 +443,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3519).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3529).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 08c73abd28848f..8b4fb2f60c10a1 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -403,8 +403,28 @@ def pseudo_op(name, op, real_ops):
"COMPARE_OP": {
"counter": 1,
},
+ # Conditional branch instructions all need a branch counter.
"COMPARE_AND_BRANCH": {
"counter": 1,
+ "branch_counter": 1,
+ },
+ "JUMP_IF_FALSE_OR_POP": {
+ "branch_counter": 1,
+ },
+ "JUMP_IF_TRUE_OR_POP": {
+ "branch_counter": 1,
+ },
+ "POP_JUMP_IF_FALSE": {
+ "branch_counter": 1,
+ },
+ "POP_JUMP_IF_TRUE": {
+ "branch_counter": 1,
+ },
+ "POP_JUMP_IF_NOT_NONE": {
+ "branch_counter": 1,
+ },
+ "POP_JUMP_IF_NONE": {
+ "branch_counter": 1,
},
"BINARY_SUBSCR": {
"counter": 1,
diff --git a/Python/specialize.c b/Python/specialize.c
index 77c5f2a30286bd..debad90f550695 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -305,19 +305,19 @@ _PyCode_Quicken(PyCodeObject *code)
case STORE_FAST << 8 | STORE_FAST:
instructions[i - 1].opcode = STORE_FAST__STORE_FAST;
break;
- case COMPARE_OP << 8 | POP_JUMP_IF_TRUE:
- case COMPARE_OP << 8 | POP_JUMP_IF_FALSE:
- {
- int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg;
- assert((oparg >> 4) <= Py_GE);
- int mask = compare_masks[oparg >> 4];
- if (opcode == POP_JUMP_IF_FALSE) {
- mask = mask ^ 0xf;
- }
- instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].opcode = COMPARE_AND_BRANCH;
- instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg = (oparg & 0xf0) | mask;
- break;
- }
+ //case COMPARE_OP << 8 | POP_JUMP_IF_TRUE:
+ //case COMPARE_OP << 8 | POP_JUMP_IF_FALSE:
+ //{
+ // int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg;
+ // assert((oparg >> 4) <= Py_GE);
+ // int mask = compare_masks[oparg >> 4];
+ // if (opcode == POP_JUMP_IF_FALSE) {
+ // mask = mask ^ 0xf;
+ // }
+ // instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].opcode = COMPARE_AND_BRANCH;
+ // instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg = (oparg & 0xf0) | mask;
+ // break;
+ //}
}
}
#endif /* ENABLE_SPECIALIZATION */
diff --git a/Python/tier2.c b/Python/tier2.c
index 4225a25f2dde7d..8ba91ce4f2d3a5 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -15,12 +15,12 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
if (code->_tier2_warmup != 0) {
code->_tier2_warmup++;
if (code->_tier2_warmup == 0) {
- // If it fails, due to lack of memory or whatever,
- // just fall back to the tier 1 interpreter.
- _Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
- if (next != NULL) {
- return next;
- }
+ //// If it fails, due to lack of memory or whatever,
+ //// just fall back to the tier 1 interpreter.
+ //_Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
+ //if (next != NULL) {
+ // return next;
+ //}
}
}
return next_instr;
From aec0d6f7689d8d1596b48c1ad279015dd6482aa4 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 1 Feb 2023 20:22:13 +0800
Subject: [PATCH 016/280] Revert "try (and fail) to add CACHE after each jump"
This reverts commit 266f8fbe4f0329e4b46ae2de150a48c2971acb5d.
---
Include/internal/pycore_code.h | 7 -------
Include/internal/pycore_opcode.h | 8 +-------
Lib/importlib/_bootstrap_external.py | 2 +-
Lib/opcode.py | 20 --------------------
Python/specialize.c | 26 +++++++++++++-------------
Python/tier2.c | 12 ++++++------
6 files changed, 21 insertions(+), 54 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 991278ac1d80d3..392ea9cf83055a 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -41,17 +41,10 @@ typedef struct {
typedef struct {
uint16_t counter;
- uint16_t branch_counter;
} _PyCompareOpCache;
#define INLINE_CACHE_ENTRIES_COMPARE_OP CACHE_ENTRIES(_PyCompareOpCache)
-typedef struct {
- uint16_t branch_counter;
-} _PyConditionalJumpCache;
-
-#define INLINE_CACHE_ENTRIES_CONDITIONAL_JUMP CACHE_ENTRIES(_PyConditionalJumpCache)
-
typedef struct {
uint16_t counter;
uint16_t type_version[2];
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 3d6fe300c0b584..b7f27863ba5e24 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -48,15 +48,9 @@ const uint8_t _PyOpcode_Caches[256] = {
[STORE_ATTR] = 4,
[LOAD_ATTR] = 9,
[COMPARE_OP] = 1,
- [JUMP_IF_FALSE_OR_POP] = 1,
- [JUMP_IF_TRUE_OR_POP] = 1,
- [POP_JUMP_IF_FALSE] = 1,
- [POP_JUMP_IF_TRUE] = 1,
[LOAD_GLOBAL] = 5,
[BINARY_OP] = 1,
- [POP_JUMP_IF_NOT_NONE] = 1,
- [POP_JUMP_IF_NONE] = 1,
- [COMPARE_AND_BRANCH] = 2,
+ [COMPARE_AND_BRANCH] = 1,
[CALL] = 4,
};
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index 4decba46906772..ebe1ef832289ac 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -443,7 +443,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3529).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3519).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 8b4fb2f60c10a1..08c73abd28848f 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -403,28 +403,8 @@ def pseudo_op(name, op, real_ops):
"COMPARE_OP": {
"counter": 1,
},
- # Conditional branch instructions all need a branch counter.
"COMPARE_AND_BRANCH": {
"counter": 1,
- "branch_counter": 1,
- },
- "JUMP_IF_FALSE_OR_POP": {
- "branch_counter": 1,
- },
- "JUMP_IF_TRUE_OR_POP": {
- "branch_counter": 1,
- },
- "POP_JUMP_IF_FALSE": {
- "branch_counter": 1,
- },
- "POP_JUMP_IF_TRUE": {
- "branch_counter": 1,
- },
- "POP_JUMP_IF_NOT_NONE": {
- "branch_counter": 1,
- },
- "POP_JUMP_IF_NONE": {
- "branch_counter": 1,
},
"BINARY_SUBSCR": {
"counter": 1,
diff --git a/Python/specialize.c b/Python/specialize.c
index debad90f550695..77c5f2a30286bd 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -305,19 +305,19 @@ _PyCode_Quicken(PyCodeObject *code)
case STORE_FAST << 8 | STORE_FAST:
instructions[i - 1].opcode = STORE_FAST__STORE_FAST;
break;
- //case COMPARE_OP << 8 | POP_JUMP_IF_TRUE:
- //case COMPARE_OP << 8 | POP_JUMP_IF_FALSE:
- //{
- // int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg;
- // assert((oparg >> 4) <= Py_GE);
- // int mask = compare_masks[oparg >> 4];
- // if (opcode == POP_JUMP_IF_FALSE) {
- // mask = mask ^ 0xf;
- // }
- // instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].opcode = COMPARE_AND_BRANCH;
- // instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg = (oparg & 0xf0) | mask;
- // break;
- //}
+ case COMPARE_OP << 8 | POP_JUMP_IF_TRUE:
+ case COMPARE_OP << 8 | POP_JUMP_IF_FALSE:
+ {
+ int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg;
+ assert((oparg >> 4) <= Py_GE);
+ int mask = compare_masks[oparg >> 4];
+ if (opcode == POP_JUMP_IF_FALSE) {
+ mask = mask ^ 0xf;
+ }
+ instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].opcode = COMPARE_AND_BRANCH;
+ instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].oparg = (oparg & 0xf0) | mask;
+ break;
+ }
}
}
#endif /* ENABLE_SPECIALIZATION */
diff --git a/Python/tier2.c b/Python/tier2.c
index 8ba91ce4f2d3a5..4225a25f2dde7d 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -15,12 +15,12 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
if (code->_tier2_warmup != 0) {
code->_tier2_warmup++;
if (code->_tier2_warmup == 0) {
- //// If it fails, due to lack of memory or whatever,
- //// just fall back to the tier 1 interpreter.
- //_Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
- //if (next != NULL) {
- // return next;
- //}
+ // If it fails, due to lack of memory or whatever,
+ // just fall back to the tier 1 interpreter.
+ _Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
+ if (next != NULL) {
+ return next;
+ }
}
}
return next_instr;
From 146bf1369fbd3968551af46d15e73420d5434d7a Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 8 Feb 2023 23:50:57 +0800
Subject: [PATCH 017/280] Write functions to allocate execution bbs
---
Include/cpython/code.h | 4 +-
Include/internal/pycore_opcode.h | 3 +-
Include/opcode.h | 5 +-
Lib/importlib/_bootstrap_external.py | 2 +-
Lib/opcode.py | 1 -
Python/tier2.c | 68 +++++++++++++++++++++++++---
6 files changed, 70 insertions(+), 13 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 01eed3380a4929..f71a4a679e9098 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -55,7 +55,9 @@ typedef struct _PyTier2BB {
// Bump allocator for basic blocks (overallocated)
typedef struct _PyTier2BBSpace {
struct _PyTier2BBSpace *next;
- void *water_level;
+ int max_capacity;
+ // How much space has been consumed in bbs.
+ int water_level;
_PyTier2BB bbs[1];
} _PyTier2BBSpace;
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index b7f27863ba5e24..36623f101e82ef 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -397,9 +397,9 @@ static const char *const _PyOpcode_OpName[263] = {
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
- [BB_NEXT] = "BB_NEXT",
[BINARY_OP_ADD_INT_TYPE_CHECK] = "BINARY_OP_ADD_INT_TYPE_CHECK",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
+ [169] = "<169>",
[170] = "<170>",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
@@ -498,6 +498,7 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
+ case 169: \
case 170: \
case 174: \
case 175: \
diff --git a/Include/opcode.h b/Include/opcode.h
index bdd4f8b8e44d46..e81d95e0f8fcc5 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -254,9 +254,8 @@ extern "C" {
#define UNPACK_SEQUENCE_TUPLE 161
#define UNPACK_SEQUENCE_TWO_TUPLE 166
#define DO_TRACING 255
-#define BB_NEXT 167
-#define BINARY_OP_ADD_INT_TYPE_CHECK 168
-#define BINARY_OP_ADD_INT_REST 169
+#define BINARY_OP_ADD_INT_TYPE_CHECK 167
+#define BINARY_OP_ADD_INT_REST 168
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index ebe1ef832289ac..e760fbb15759d4 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -443,7 +443,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3519).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3517).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 08c73abd28848f..47abe8e508aa3b 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -443,7 +443,6 @@ def pseudo_op(name, op, real_ops):
'BINARY_OP_ADD_INT',
]
_uops = [
- 'BB_NEXT',
'BINARY_OP_ADD_INT_TYPE_CHECK',
'BINARY_OP_ADD_INT_REST',
]
diff --git a/Python/tier2.c b/Python/tier2.c
index 4225a25f2dde7d..a5a2ebc569eb0d 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -26,6 +26,33 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
return next_instr;
}
+
+static _PyTier2BBSpace *
+_PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
+{
+ _PyTier2BBSpace *bb_space = PyMem_Malloc(space_to_alloc);
+ if (bb_space == NULL) {
+ return NULL;
+ }
+ bb_space->next = NULL;
+ bb_space->water_level = 0;
+ bb_space->max_capacity = space_to_alloc - sizeof(_PyTier2BB);
+ return bb_space;
+}
+
+/* Init a BB in BB space without any checks for waterlevel. */
+static _PyTier2BB *
+_PyTier2_InitBBNoCheck(_PyTier2BBSpace *bb_space, _Py_CODEUNIT *tier1_start,
+ const void *instr_bytes_src, Py_ssize_t instr_nbytes)
+{
+ _PyTier2BB *bb_ptr = &bb_space->bbs[bb_space->water_level];
+ bb_ptr->tier1_start = tier1_start;
+ memcpy(bb_ptr->u_code, instr_bytes_src, instr_nbytes);
+ assert(bb_space->water_level + instr_nbytes == (int)(bb_space->water_level + instr_nbytes));
+ bb_space->water_level += instr_nbytes;
+ return bb_ptr;
+}
+
static _Py_CODEUNIT *
_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
@@ -39,17 +66,18 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// @TODO split up code object into basic blocks.
// 3. Set the instruction pointer to correct one.
fprintf(stderr, "INITIALIZING %ld\n", Py_SIZE(co));
- _PyTier2BBSpace *bb_space = PyMem_Malloc((sizeof(_PyTier2BB) + _PyCode_NBYTES(co)) * 2);
+ Py_ssize_t space_to_alloc = (sizeof(_PyTier2BB) + _PyCode_NBYTES(co)) * 2;
+
+ _PyTier2BBSpace *bb_space = _PyTier2_CreateBBSpace(space_to_alloc);
if (bb_space == NULL) {
return NULL;
}
- bb_space->next = NULL;
- bb_space->water_level = 0;
+
co->_bb_space = bb_space;
- _PyTier2BB *bb_ptr = bb_space->bbs;
- bb_ptr->tier1_start = _PyCode_CODE(co);
- memcpy(bb_ptr->u_code, _PyCode_CODE(co), _PyCode_NBYTES(co));
+ _PyTier2BB *bb_ptr = _PyTier2_InitBBNoCheck(bb_space, _PyCode_CODE(co),
+ _PyCode_CODE(co), _PyCode_NBYTES(co));
+
// Remove all the RESUME and JUMP_BACKWARDS instructions
for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
_Py_CODEUNIT instr = bb_ptr->u_code[i];
@@ -75,3 +103,31 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// _py_set_opcode(next_instr, CACHE);
return bb_ptr->u_code + (next_instr - _PyCode_CODE(co));
}
+
+/* Allocates and initializes a new basic block. If not enough space in
+ the overallocated array, create a new array.
+
+ Make sure to call _PyCode_Tier2Initialize before this!
+*/
+static _PyTier2BB *
+_PyCode_Tier2BBNew(PyCodeObject *co, _Py_CODEUNIT *tier1_start, _Py_CODEUNIT *instr, Py_ssize_t code_size)
+{
+ assert(co->_bb_space != NULL);
+
+ _PyTier2BBSpace *bb_space = co->_bb_space;
+ Py_ssize_t amount_to_alloc = code_size + sizeof(_PyTier2BB);
+ assert(bb_space->water_level + amount_to_alloc == (int)(bb_space->water_level + amount_to_alloc));
+
+ // Need to allocate a new array.
+ if (bb_space->water_level + amount_to_alloc > bb_space->max_capacity) {
+ _PyTier2BBSpace *next_bb_space = _PyTier2_CreateBBSpace(bb_space->max_capacity + amount_to_alloc);
+ if (next_bb_space == NULL) {
+ return NULL;
+ }
+ next_bb_space->next = bb_space;
+ // We want to make our bb_space point to the most recent one to get O(1) BB allocations.
+ co->_bb_space = next_bb_space;
+ bb_space = next_bb_space;
+ }
+ return _PyTier2_InitBBNoCheck(bb_space, tier1_start, instr, code_size * sizeof(_Py_CODEUNIT));
+}
From fd10c2cf46fa28d23127f6f98a208cd1fc069911 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 9 Feb 2023 12:25:09 +0800
Subject: [PATCH 018/280] tracer/profiler kind of thing
---
Include/internal/pycore_code.h | 2 +-
Include/internal/pycore_interp.h | 1 +
Python/bytecodes.c | 13 +-
Python/ceval.c | 397 +++++++++++++++++++++++++++++++
Python/ceval_macros.h | 117 +++++++--
Python/compile.c | 1 -
Python/generated_cases.c.h | 15 +-
Python/tier2.c | 26 +-
8 files changed, 541 insertions(+), 31 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 392ea9cf83055a..b1520b2923a650 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -240,7 +240,7 @@ extern void _PyStaticCode_Fini(PyCodeObject *co);
extern int _PyStaticCode_Init(PyCodeObject *co);
/* Tier 2 interpreter */
-extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *, _Py_CODEUNIT *);
+extern int _PyCode_Tier2Warmup(PyThreadState *, struct _PyInterpreterFrame *, int, _Py_CODEUNIT *, PyObject **, PyObject **);
#ifdef Py_STATS
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 0e3d46852f2e6d..c4f11d8e0c4e31 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -230,6 +230,7 @@ PyAPI_FUNC(int) _PyInterpreterState_IDInitref(PyInterpreterState *);
PyAPI_FUNC(int) _PyInterpreterState_IDIncref(PyInterpreterState *);
PyAPI_FUNC(void) _PyInterpreterState_IDDecref(PyInterpreterState *);
+PyObject *_PyEval_EvalFrameTier2Profile(PyThreadState *tstate, struct _PyInterpreterFrame *frame, int throwflag);
#ifdef __cplusplus
}
#endif
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index c310802d577507..47e270c663b0ab 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -89,7 +89,10 @@ dummy_func(
inst(RESUME, (--)) {
if (cframe.use_tracing == 0) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ PyObject *retval = NULL;
+ if (_PyCode_Tier2Warmup(tstate, frame, throwflag, next_instr, stack_pointer, &retval)) {
+ return retval;
+ }
}
GO_TO_INSTRUCTION(RESUME_QUICK);
}
@@ -1881,10 +1884,14 @@ dummy_func(
}
inst(JUMP_BACKWARD, (--)) {
+ JUMPBY(-oparg);
+ CHECK_EVAL_BREAKER();
if (cframe.use_tracing == 0) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ PyObject *retval = NULL;
+ if (_PyCode_Tier2Warmup(tstate, frame, throwflag, next_instr, stack_pointer, &retval)) {
+ return retval;
+ }
}
- GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
}
inst(JUMP_BACKWARD_QUICK, (--)) {
diff --git a/Python/ceval.c b/Python/ceval.c
index a91f5baca8853e..6102f5d025edc9 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -706,6 +706,7 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) {
# pragma warning(disable:4102)
#endif
+#undef TIER2_PROFILING
PyObject* _Py_HOT_FUNCTION
_PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)
{
@@ -1094,6 +1095,402 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
# pragma warning(pop)
#endif
+// CARBON-COPY of _PyEval_EvalFrameDefault. KEEP IN SYNC WITH THAT FUNCTION.
+#define TIER2_PROFILING 1
+#undef INSTRUCTION_START
+#undef TARGET
+#undef GETITEM
+#include "ceval_macros.h"
+PyObject *
+_PyEval_EvalFrameTier2Profile(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag, PyObject **old_stack_pointer)
+{
+ fprintf(stderr, "TIER2_PROFILING\n");
+ _Py_EnsureTstateNotNULL(tstate);
+ CALL_STAT_INC(pyeval_calls);
+
+#if USE_COMPUTED_GOTOS
+ /* Import the static jump table */
+#include "opcode_targets.h"
+#endif
+
+#ifdef Py_STATS
+ int lastopcode = 0;
+#endif
+ // opcode is an 8-bit value to improve the code generated by MSVC
+ // for the big switch below (in combination with the EXTRA_CASES macro).
+ uint8_t opcode; /* Current opcode */
+ int oparg; /* Current opcode argument, if any */
+ _Py_atomic_int *const eval_breaker = &tstate->interp->ceval.eval_breaker;
+#ifdef LLTRACE
+ int lltrace = 0;
+#endif
+
+ _PyCFrame cframe;
+ _PyInterpreterFrame entry_frame;
+ PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions.
+
+ /* WARNING: Because the _PyCFrame lives on the C stack,
+ * but can be accessed from a heap allocated object (tstate)
+ * strict stack discipline must be maintained.
+ */
+ _PyCFrame *prev_cframe = tstate->cframe;
+ cframe.use_tracing = prev_cframe->use_tracing;
+ cframe.previous = prev_cframe;
+ tstate->cframe = &cframe;
+
+ assert(tstate->interp->interpreter_trampoline != NULL);
+#ifdef Py_DEBUG
+ /* Set these to invalid but identifiable values for debugging. */
+ entry_frame.f_funcobj = (PyObject *)0xaaa0;
+ entry_frame.f_locals = (PyObject *)0xaaa1;
+ entry_frame.frame_obj = (PyFrameObject *)0xaaa2;
+ entry_frame.f_globals = (PyObject *)0xaaa3;
+ entry_frame.f_builtins = (PyObject *)0xaaa4;
+#endif
+ entry_frame.f_code = tstate->interp->interpreter_trampoline;
+ entry_frame.prev_instr =
+ _PyCode_CODE(tstate->interp->interpreter_trampoline);
+ entry_frame.stacktop = 0;
+ entry_frame.owner = FRAME_OWNED_BY_CSTACK;
+ entry_frame.yield_offset = 0;
+ /* Push frame */
+ entry_frame.previous = prev_cframe->current_frame;
+ frame->previous = &entry_frame;
+ cframe.current_frame = frame;
+
+ if (_Py_EnterRecursiveCallTstate(tstate, "")) {
+ tstate->c_recursion_remaining--;
+ tstate->py_recursion_remaining--;
+ goto exit_unwind;
+ }
+
+ /* support for generator.throw() */
+ if (throwflag) {
+ if (_Py_EnterRecursivePy(tstate)) {
+ goto exit_unwind;
+ }
+ TRACE_FUNCTION_THROW_ENTRY();
+ DTRACE_FUNCTION_ENTRY();
+ goto resume_with_error;
+ }
+
+ /* Local "register" variables.
+ * These are cached values from the frame and code object. */
+
+ PyObject *names;
+ PyObject *consts;
+ _Py_CODEUNIT *next_instr;
+ PyObject **stack_pointer;
+
+ /* Sets the above local variables from the frame */
+#define SET_LOCALS_FROM_FRAME() \
+ { \
+ PyCodeObject *co = frame->f_code; \
+ names = co->co_names; \
+ consts = co->co_consts; \
+ } \
+ assert(_PyInterpreterFrame_LASTI(frame) >= -1); \
+ /* Jump back to the last instruction executed... */ \
+ next_instr = frame->prev_instr + 1; \
+ stack_pointer = _PyFrame_GetStackPointer(frame); \
+ /* Set stackdepth to -1. \
+ Update when returning or calling trace function. \
+ Having stackdepth <= 0 ensures that invalid \
+ values are not visible to the cycle GC. \
+ We choose -1 rather than 0 to assist debugging. \
+ */ \
+ frame->stacktop = -1;
+
+
+start_frame:
+ if (_Py_EnterRecursivePy(tstate)) {
+ goto exit_unwind;
+ }
+
+resume_frame:
+ SET_LOCALS_FROM_FRAME();
+
+#ifdef LLTRACE
+ {
+ if (frame != &entry_frame) {
+ int r = PyDict_Contains(GLOBALS(), &_Py_ID(__lltrace__));
+ if (r < 0) {
+ goto exit_unwind;
+ }
+ lltrace = r;
+ }
+ if (lltrace) {
+ lltrace_resume_frame(frame);
+ }
+ }
+#endif
+
+#ifdef Py_DEBUG
+ /* _PyEval_EvalFrameDefault() must not be called with an exception set,
+ because it can clear it (directly or indirectly) and so the
+ caller loses its exception */
+ assert(!_PyErr_Occurred(tstate));
+#endif
+
+ DISPATCH();
+
+handle_eval_breaker:
+
+ /* Do periodic things, like check for signals and async I/0.
+ * We need to do reasonably frequently, but not too frequently.
+ * All loops should include a check of the eval breaker.
+ * We also check on return from any builtin function.
+ */
+ if (_Py_HandlePending(tstate) != 0) {
+ goto error;
+ }
+ DISPATCH();
+
+ {
+ /* Start instructions */
+#if !USE_COMPUTED_GOTOS
+ dispatch_opcode :
+ switch (opcode)
+#endif
+ {
+
+#include "generated_cases.c.h"
+
+#if USE_COMPUTED_GOTOS
+ TARGET_DO_TRACING :
+#else
+ case DO_TRACING:
+#endif
+ {
+ assert(cframe.use_tracing);
+ assert(tstate->tracing == 0);
+ if (INSTR_OFFSET() >= frame->f_code->_co_firsttraceable) {
+ int instr_prev = _PyInterpreterFrame_LASTI(frame);
+ frame->prev_instr = next_instr;
+ NEXTOPARG();
+ // No _PyOpcode_Deopt here, since RESUME has no optimized forms:
+ if (opcode == RESUME) {
+ if (oparg < 2) {
+ CHECK_EVAL_BREAKER();
+ }
+ /* Call tracing */
+ TRACE_FUNCTION_ENTRY();
+ DTRACE_FUNCTION_ENTRY();
+ }
+ else {
+ /* line-by-line tracing support */
+ if (PyDTrace_LINE_ENABLED()) {
+ maybe_dtrace_line(frame, &tstate->trace_info, instr_prev);
+ }
+
+ if (cframe.use_tracing &&
+ tstate->c_tracefunc != NULL && !tstate->tracing) {
+ int err;
+ /* see maybe_call_line_trace()
+ for expository comments */
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+
+ err = maybe_call_line_trace(tstate->c_tracefunc,
+ tstate->c_traceobj,
+ tstate, frame, instr_prev);
+ // Reload possibly changed frame fields:
+ stack_pointer = _PyFrame_GetStackPointer(frame);
+ frame->stacktop = -1;
+ // next_instr is only reloaded if tracing *does not* raise.
+ // This is consistent with the behavior of older Python
+ // versions. If a trace function sets a new f_lineno and
+ // *then* raises, we use the *old* location when searching
+ // for an exception handler, displaying the traceback, and
+ // so on:
+ if (err) {
+ // next_instr wasn't incremented at the start of this
+ // instruction. Increment it before handling the error,
+ // so that it looks the same as a "normal" instruction:
+ next_instr++;
+ goto error;
+ }
+ // Reload next_instr. Don't increment it, though, since
+ // we're going to re-dispatch to the "true" instruction now:
+ next_instr = frame->prev_instr;
+ }
+ }
+ }
+ NEXTOPARG();
+ PRE_DISPATCH_GOTO();
+ // No _PyOpcode_Deopt here, since EXTENDED_ARG has no optimized forms:
+ while (opcode == EXTENDED_ARG) {
+ // CPython hasn't ever traced the instruction after an EXTENDED_ARG.
+ // Inline the EXTENDED_ARG here, so we can avoid branching there:
+ INSTRUCTION_START(EXTENDED_ARG);
+ opcode = _Py_OPCODE(*next_instr);
+ oparg = oparg << 8 | _Py_OPARG(*next_instr);
+ // Make sure the next instruction isn't a RESUME, since that needs
+ // to trace properly (and shouldn't have an EXTENDED_ARG, anyways):
+ assert(opcode != RESUME);
+ PRE_DISPATCH_GOTO();
+ }
+ opcode = _PyOpcode_Deopt[opcode];
+ if (_PyOpcode_Caches[opcode]) {
+ uint16_t *counter = &next_instr[1].cache;
+ // The instruction is going to decrement the counter, so we need to
+ // increment it here to make sure it doesn't try to specialize:
+ if (!ADAPTIVE_COUNTER_IS_MAX(*counter)) {
+ INCREMENT_ADAPTIVE_COUNTER(*counter);
+ }
+ }
+ DISPATCH_GOTO();
+ }
+
+#if USE_COMPUTED_GOTOS
+ _unknown_opcode :
+#else
+ EXTRA_CASES // From opcode.h, a 'case' for each unused opcode
+#endif
+ /* Tell C compilers not to hold the opcode variable in the loop.
+ next_instr points the current instruction without TARGET(). */
+ opcode = _Py_OPCODE(*next_instr);
+ _PyErr_Format(tstate, PyExc_SystemError,
+ "%U:%d: unknown opcode %d",
+ frame->f_code->co_filename,
+ _PyInterpreterFrame_GetLine(frame),
+ opcode);
+ goto error;
+
+ } /* End instructions */
+
+ /* This should never be reached. Every opcode should end with DISPATCH()
+ or goto error. */
+ Py_UNREACHABLE();
+
+ unbound_local_error:
+ {
+ format_exc_check_arg(tstate, PyExc_UnboundLocalError,
+ UNBOUNDLOCAL_ERROR_MSG,
+ PyTuple_GetItem(frame->f_code->co_localsplusnames, oparg)
+ );
+ goto error;
+ }
+
+ pop_4_error:
+ STACK_SHRINK(1);
+ pop_3_error:
+ STACK_SHRINK(1);
+ pop_2_error:
+ STACK_SHRINK(1);
+ pop_1_error:
+ STACK_SHRINK(1);
+ error:
+ kwnames = NULL;
+ /* Double-check exception status. */
+#ifdef NDEBUG
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_SetString(tstate, PyExc_SystemError,
+ "error return without exception set");
+ }
+#else
+ assert(_PyErr_Occurred(tstate));
+#endif
+
+ /* Log traceback info. */
+ assert(frame != &entry_frame);
+ if (!_PyFrame_IsIncomplete(frame)) {
+ PyFrameObject *f = _PyFrame_GetFrameObject(frame);
+ if (f != NULL) {
+ PyTraceBack_Here(f);
+ }
+ }
+
+ if (tstate->c_tracefunc != NULL) {
+ /* Make sure state is set to FRAME_UNWINDING for tracing */
+ call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
+ tstate, frame);
+ }
+
+ exception_unwind:
+ {
+ /* We can't use frame->f_lasti here, as RERAISE may have set it */
+ int offset = INSTR_OFFSET() - 1;
+ int level, handler, lasti;
+ if (get_exception_handler(frame->f_code, offset, &level, &handler, &lasti) == 0) {
+ // No handlers, so exit.
+ assert(_PyErr_Occurred(tstate));
+
+ /* Pop remaining stack entries. */
+ PyObject **stackbase = _PyFrame_Stackbase(frame);
+ while (stack_pointer > stackbase) {
+ PyObject *o = POP();
+ Py_XDECREF(o);
+ }
+ assert(STACK_LEVEL() == 0);
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ TRACE_FUNCTION_UNWIND();
+ DTRACE_FUNCTION_EXIT();
+ goto exit_unwind;
+ }
+
+ assert(STACK_LEVEL() >= level);
+ PyObject **new_top = _PyFrame_Stackbase(frame) + level;
+ while (stack_pointer > new_top) {
+ PyObject *v = POP();
+ Py_XDECREF(v);
+ }
+ PyObject *exc, *val, *tb;
+ if (lasti) {
+ int frame_lasti = _PyInterpreterFrame_LASTI(frame);
+ PyObject *lasti = PyLong_FromLong(frame_lasti);
+ if (lasti == NULL) {
+ goto exception_unwind;
+ }
+ PUSH(lasti);
+ }
+ _PyErr_Fetch(tstate, &exc, &val, &tb);
+ /* Make the raw exception data
+ available to the handler,
+ so a program can emulate the
+ Python main loop. */
+ _PyErr_NormalizeException(tstate, &exc, &val, &tb);
+ if (tb != NULL)
+ PyException_SetTraceback(val, tb);
+ else
+ PyException_SetTraceback(val, Py_None);
+ Py_XDECREF(tb);
+ Py_XDECREF(exc);
+ PUSH(val);
+ JUMPTO(handler);
+ /* Resume normal execution */
+ DISPATCH();
+ }
+ }
+
+exit_unwind:
+ assert(_PyErr_Occurred(tstate));
+ _Py_LeaveRecursiveCallPy(tstate);
+ assert(frame != &entry_frame);
+ // GH-99729: We need to unlink the frame *before* clearing it:
+ _PyInterpreterFrame *dying = frame;
+ frame = cframe.current_frame = dying->previous;
+ _PyEvalFrameClearAndPop(tstate, dying);
+ if (frame == &entry_frame) {
+ /* Restore previous cframe and exit */
+ tstate->cframe = cframe.previous;
+ tstate->cframe->use_tracing = cframe.use_tracing;
+ assert(tstate->cframe->current_frame == frame->previous);
+ _Py_LeaveRecursiveCallTstate(tstate);
+ return NULL;
+ }
+
+resume_with_error:
+ SET_LOCALS_FROM_FRAME();
+ goto error;
+
+}
+
+#undef TIER2_PROFILING
+#undef INSTRUCTION_START
+#undef TARGET
+#undef GETITEM
+#include "ceval_macros.h"
+
static void
format_missing(PyThreadState *tstate, const char *kind,
PyCodeObject *co, PyObject *names, PyObject *qualname)
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 7d601c71fde43b..1aa12383b748c6 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -47,6 +47,10 @@
#define OR_DTRACE_LINE
#endif
+#ifdef USE_COMPUTED_GOTOS
+ #undef USE_COMPUTED_GOTOS
+#endif
+
#ifdef HAVE_COMPUTED_GOTOS
#ifndef USE_COMPUTED_GOTOS
#define USE_COMPUTED_GOTOS 1
@@ -59,26 +63,57 @@
#define USE_COMPUTED_GOTOS 0
#endif
-#ifdef Py_STATS
-#define INSTRUCTION_START(op) \
- do { \
- frame->prev_instr = next_instr++; \
- OPCODE_EXE_INC(op); \
- if (_py_stats) _py_stats->opcode_stats[lastopcode].pair_count[op]++; \
- lastopcode = op; \
- } while (0)
-#else
-#define INSTRUCTION_START(op) (frame->prev_instr = next_instr++)
+#ifdef INSTRUCTION_START
+ #undef INSTRUCTION_START
#endif
-#if USE_COMPUTED_GOTOS
-# define TARGET(op) TARGET_##op: INSTRUCTION_START(op);
-# define DISPATCH_GOTO() goto *opcode_targets[opcode]
+#ifndef TIER2_PROFILING
+ #ifdef Py_STATS
+ #define INSTRUCTION_START(op) \
+ do { \
+ frame->prev_instr = next_instr++; \
+ OPCODE_EXE_INC(op); \
+ if (_py_stats) _py_stats->opcode_stats[lastopcode].pair_count[op]++; \
+ lastopcode = op; \
+ } while (0)
+ #else
+ #define INSTRUCTION_START(op) (frame->prev_instr = next_instr++)
+ #endif
#else
-# define TARGET(op) case op: TARGET_##op: INSTRUCTION_START(op);
-# define DISPATCH_GOTO() goto dispatch_opcode
+ #define INSTRUCTION_START(op) \
+ do { \
+ frame->prev_instr = next_instr++; \
+ fprintf(stderr, "%d\n", op); \
+ } while (0)
+#endif /* TIER2_PROFILING */
+
+#ifdef TARGET
+#undef TARGET
+#endif
+
+#ifdef DISPATCH_GOTO
+#undef DISPATCH_GOTO
#endif
+#ifndef TIER2_PROFILING
+ #if USE_COMPUTED_GOTOS
+ # define TARGET(op) TARGET_##op: INSTRUCTION_START(op);
+ # define DISPATCH_GOTO() goto *opcode_targets[opcode]
+ #else
+ # define TARGET(op) case op: TARGET_##op: INSTRUCTION_START(op);
+ # define DISPATCH_GOTO() goto dispatch_opcode
+ #endif
+#else
+ #if USE_COMPUTED_GOTOS
+ # define TARGET(op) TARGET_TIER2_PROFILE_##op: INSTRUCTION_START(op);
+ # define DISPATCH_GOTO() goto *opcode_targets[opcode]
+ #else
+ # define TARGET(op) case op: TARGET_TIER2_PROFILE_##op: INSTRUCTION_START(op);
+ # define DISPATCH_GOTO() goto dispatch_opcode
+ #endif
+#endif /* TIER2_PROFILING */
+
+
/* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */
#ifdef LLTRACE
#define PRE_DISPATCH_GOTO() if (lltrace) { \
@@ -87,6 +122,9 @@
#define PRE_DISPATCH_GOTO() ((void)0)
#endif
+#ifdef DISPATCH
+#undef DISPATCH
+#endif
/* Do interpreter dispatch accounting for tracing and instrumentation */
#define DISPATCH() \
@@ -98,6 +136,10 @@
DISPATCH_GOTO(); \
}
+#ifdef DISPATCH_SAME_OPARG
+#undef DISPATCH_SAME_OPARG
+#endif
+
#define DISPATCH_SAME_OPARG() \
{ \
opcode = _Py_OPCODE(*next_instr); \
@@ -106,6 +148,10 @@
DISPATCH_GOTO(); \
}
+#ifdef DISPATCH_INLINED
+#undef DISPATCH_INLINED
+#endif
+
#define DISPATCH_INLINED(NEW_FRAME) \
do { \
_PyFrame_SetStackPointer(frame, stack_pointer); \
@@ -116,6 +162,10 @@
goto start_frame; \
} while (0)
+#ifdef CHECK_EVAL_BREAKER
+#undef CHECK_EVAL_BREAKER
+#endif
+
#define CHECK_EVAL_BREAKER() \
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); \
if (_Py_atomic_load_relaxed_int32(eval_breaker)) { \
@@ -125,9 +175,12 @@
/* Tuple access macros */
+
#ifndef Py_DEBUG
#define GETITEM(v, i) PyTuple_GET_ITEM((v), (i))
#else
+#ifndef _GETITEM_DEF_H
+#define _GETITEM_DEF_H
static inline PyObject *
GETITEM(PyObject *v, Py_ssize_t i) {
assert(PyTuple_Check(v));
@@ -135,10 +188,27 @@ GETITEM(PyObject *v, Py_ssize_t i) {
assert(i < PyTuple_GET_SIZE(v));
return PyTuple_GET_ITEM(v, i);
}
+#endif /* _GETITEM_DEF_H */
#endif
/* Code access macros */
+#ifdef INSTR_OFFSET
+#undef INSTR_OFFSET
+#endif
+
+#ifdef NEXTOPARG
+#undef NEXTOPARG
+#endif
+
+#ifdef JUMPTO
+#undef JUMPTO
+#endif
+
+#ifdef JUMPBY
+#undef JUMPBY
+#endif
+
/* The integer overflow is checked by an assertion below. */
// TODO change this calculation when interpreter is bb aware.
#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code)))
@@ -176,7 +246,23 @@ GETITEM(PyObject *v, Py_ssize_t i) {
*/
+#ifdef PREDICT_ID
+#undef PREDICT_ID
+#endif
+
+#ifndef TIER2_PROFILING
#define PREDICT_ID(op) PRED_##op
+#else
+#define PREDICT_ID(op) PRED_TIER2_PROFILING_##op
+#endif
+
+#ifdef PREDICT
+#undef PREDICT
+#endif
+
+#ifdef PREDICTED
+#undef PREDICTED
+#endif
#if USE_COMPUTED_GOTOS
#define PREDICT(op) if (0) goto PREDICT_ID(op)
@@ -251,6 +337,7 @@ GETITEM(PyObject *v, Py_ssize_t i) {
GETLOCAL(i) = value; \
Py_XDECREF(tmp); } while (0)
+
#define GO_TO_INSTRUCTION(op) goto PREDICT_ID(op)
#ifdef Py_STATS
diff --git a/Python/compile.c b/Python/compile.c
index bdc25ba9df9b4a..df2dffb95bbd7e 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1079,7 +1079,6 @@ stack_effect(int opcode, int oparg, int jump)
case EXTENDED_ARG:
case RESUME:
case CACHE:
- case BB_NEXT:
return 0;
/* Stack manipulation */
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 605b9767417d3c..ca6216218f03c2 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -24,7 +24,10 @@
TARGET(RESUME) {
if (cframe.use_tracing == 0) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ PyObject *retval = NULL;
+ if (_PyCode_Tier2Warmup(tstate, frame, throwflag, next_instr, stack_pointer, &retval)) {
+ return retval;
+ }
}
GO_TO_INSTRUCTION(RESUME_QUICK);
}
@@ -2417,14 +2420,18 @@
TARGET(JUMP_BACKWARD) {
PREDICTED(JUMP_BACKWARD);
+ JUMPBY(-oparg);
+ CHECK_EVAL_BREAKER();
if (cframe.use_tracing == 0) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ PyObject *retval = NULL;
+ if (_PyCode_Tier2Warmup(tstate, frame, throwflag, next_instr, stack_pointer, &retval)) {
+ return retval;
+ }
}
- GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
+ DISPATCH();
}
TARGET(JUMP_BACKWARD_QUICK) {
- PREDICTED(JUMP_BACKWARD_QUICK);
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Python/tier2.c b/Python/tier2.c
index a5a2ebc569eb0d..ddb82526d7e719 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1,16 +1,22 @@
#include "Python.h"
#include "pycore_code.h"
#include "pycore_frame.h"
+#include "pycore_interp.h"
#include "opcode.h"
+#include "pystate.h"
+#include "pytypedefs.h"
static _Py_CODEUNIT *
_PyCode_Tier2Initialize(_PyInterpreterFrame *, _Py_CODEUNIT *);
// Tier 2 warmup counter
-_Py_CODEUNIT *
-_PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
+int
+_PyCode_Tier2Warmup(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag, _Py_CODEUNIT *next_instr, PyObject **stack_pointer, PyObject **retval)
{
+ if (tstate->interp->eval_frame != NULL) {
+ return 0;
+ }
PyCodeObject *code = frame->f_code;
if (code->_tier2_warmup != 0) {
code->_tier2_warmup++;
@@ -18,15 +24,22 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// If it fails, due to lack of memory or whatever,
// just fall back to the tier 1 interpreter.
_Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
- if (next != NULL) {
- return next;
- }
+ // Swap out the profiler to use the profiling eval loop.
+ frame->prev_instr = next_instr - 1;
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ // Do something with entry_frame here, maybe set the current frame to an entry
+ // frame and check for that in RETURN_VALUE?
+ *retval = _PyEval_EvalFrameTier2Profile(tstate, frame, throwflag);
+ PyObject_Print(*retval, stderr, Py_PRINT_RAW);
+ return 1;
+
}
}
- return next_instr;
+ return 0;
}
+
static _PyTier2BBSpace *
_PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
{
@@ -57,7 +70,6 @@ static _Py_CODEUNIT *
_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
int curr = _Py_OPCODE(*(next_instr - 1));
- assert(curr == RESUME || curr == JUMP_BACKWARD);
PyCodeObject *co = frame->f_code;
assert(co->_bb_space == NULL);
// 1. Initialize basic blocks space.
From f87f8f4251a7bf58c780776eee2af9446ebda70d Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 9 Feb 2023 12:25:43 +0800
Subject: [PATCH 019/280] Revert "tracer/profiler kind of thing"
This reverts commit fd10c2cf46fa28d23127f6f98a208cd1fc069911.
---
Include/internal/pycore_code.h | 2 +-
Include/internal/pycore_interp.h | 1 -
Python/bytecodes.c | 13 +-
Python/ceval.c | 397 -------------------------------
Python/ceval_macros.h | 117 ++-------
Python/compile.c | 1 +
Python/generated_cases.c.h | 15 +-
Python/tier2.c | 26 +-
8 files changed, 31 insertions(+), 541 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index b1520b2923a650..392ea9cf83055a 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -240,7 +240,7 @@ extern void _PyStaticCode_Fini(PyCodeObject *co);
extern int _PyStaticCode_Init(PyCodeObject *co);
/* Tier 2 interpreter */
-extern int _PyCode_Tier2Warmup(PyThreadState *, struct _PyInterpreterFrame *, int, _Py_CODEUNIT *, PyObject **, PyObject **);
+extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *, _Py_CODEUNIT *);
#ifdef Py_STATS
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index c4f11d8e0c4e31..0e3d46852f2e6d 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -230,7 +230,6 @@ PyAPI_FUNC(int) _PyInterpreterState_IDInitref(PyInterpreterState *);
PyAPI_FUNC(int) _PyInterpreterState_IDIncref(PyInterpreterState *);
PyAPI_FUNC(void) _PyInterpreterState_IDDecref(PyInterpreterState *);
-PyObject *_PyEval_EvalFrameTier2Profile(PyThreadState *tstate, struct _PyInterpreterFrame *frame, int throwflag);
#ifdef __cplusplus
}
#endif
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 47e270c663b0ab..c310802d577507 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -89,10 +89,7 @@ dummy_func(
inst(RESUME, (--)) {
if (cframe.use_tracing == 0) {
- PyObject *retval = NULL;
- if (_PyCode_Tier2Warmup(tstate, frame, throwflag, next_instr, stack_pointer, &retval)) {
- return retval;
- }
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
}
GO_TO_INSTRUCTION(RESUME_QUICK);
}
@@ -1884,14 +1881,10 @@ dummy_func(
}
inst(JUMP_BACKWARD, (--)) {
- JUMPBY(-oparg);
- CHECK_EVAL_BREAKER();
if (cframe.use_tracing == 0) {
- PyObject *retval = NULL;
- if (_PyCode_Tier2Warmup(tstate, frame, throwflag, next_instr, stack_pointer, &retval)) {
- return retval;
- }
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
}
+ GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
}
inst(JUMP_BACKWARD_QUICK, (--)) {
diff --git a/Python/ceval.c b/Python/ceval.c
index 6102f5d025edc9..a91f5baca8853e 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -706,7 +706,6 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) {
# pragma warning(disable:4102)
#endif
-#undef TIER2_PROFILING
PyObject* _Py_HOT_FUNCTION
_PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)
{
@@ -1095,402 +1094,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
# pragma warning(pop)
#endif
-// CARBON-COPY of _PyEval_EvalFrameDefault. KEEP IN SYNC WITH THAT FUNCTION.
-#define TIER2_PROFILING 1
-#undef INSTRUCTION_START
-#undef TARGET
-#undef GETITEM
-#include "ceval_macros.h"
-PyObject *
-_PyEval_EvalFrameTier2Profile(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag, PyObject **old_stack_pointer)
-{
- fprintf(stderr, "TIER2_PROFILING\n");
- _Py_EnsureTstateNotNULL(tstate);
- CALL_STAT_INC(pyeval_calls);
-
-#if USE_COMPUTED_GOTOS
- /* Import the static jump table */
-#include "opcode_targets.h"
-#endif
-
-#ifdef Py_STATS
- int lastopcode = 0;
-#endif
- // opcode is an 8-bit value to improve the code generated by MSVC
- // for the big switch below (in combination with the EXTRA_CASES macro).
- uint8_t opcode; /* Current opcode */
- int oparg; /* Current opcode argument, if any */
- _Py_atomic_int *const eval_breaker = &tstate->interp->ceval.eval_breaker;
-#ifdef LLTRACE
- int lltrace = 0;
-#endif
-
- _PyCFrame cframe;
- _PyInterpreterFrame entry_frame;
- PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions.
-
- /* WARNING: Because the _PyCFrame lives on the C stack,
- * but can be accessed from a heap allocated object (tstate)
- * strict stack discipline must be maintained.
- */
- _PyCFrame *prev_cframe = tstate->cframe;
- cframe.use_tracing = prev_cframe->use_tracing;
- cframe.previous = prev_cframe;
- tstate->cframe = &cframe;
-
- assert(tstate->interp->interpreter_trampoline != NULL);
-#ifdef Py_DEBUG
- /* Set these to invalid but identifiable values for debugging. */
- entry_frame.f_funcobj = (PyObject *)0xaaa0;
- entry_frame.f_locals = (PyObject *)0xaaa1;
- entry_frame.frame_obj = (PyFrameObject *)0xaaa2;
- entry_frame.f_globals = (PyObject *)0xaaa3;
- entry_frame.f_builtins = (PyObject *)0xaaa4;
-#endif
- entry_frame.f_code = tstate->interp->interpreter_trampoline;
- entry_frame.prev_instr =
- _PyCode_CODE(tstate->interp->interpreter_trampoline);
- entry_frame.stacktop = 0;
- entry_frame.owner = FRAME_OWNED_BY_CSTACK;
- entry_frame.yield_offset = 0;
- /* Push frame */
- entry_frame.previous = prev_cframe->current_frame;
- frame->previous = &entry_frame;
- cframe.current_frame = frame;
-
- if (_Py_EnterRecursiveCallTstate(tstate, "")) {
- tstate->c_recursion_remaining--;
- tstate->py_recursion_remaining--;
- goto exit_unwind;
- }
-
- /* support for generator.throw() */
- if (throwflag) {
- if (_Py_EnterRecursivePy(tstate)) {
- goto exit_unwind;
- }
- TRACE_FUNCTION_THROW_ENTRY();
- DTRACE_FUNCTION_ENTRY();
- goto resume_with_error;
- }
-
- /* Local "register" variables.
- * These are cached values from the frame and code object. */
-
- PyObject *names;
- PyObject *consts;
- _Py_CODEUNIT *next_instr;
- PyObject **stack_pointer;
-
- /* Sets the above local variables from the frame */
-#define SET_LOCALS_FROM_FRAME() \
- { \
- PyCodeObject *co = frame->f_code; \
- names = co->co_names; \
- consts = co->co_consts; \
- } \
- assert(_PyInterpreterFrame_LASTI(frame) >= -1); \
- /* Jump back to the last instruction executed... */ \
- next_instr = frame->prev_instr + 1; \
- stack_pointer = _PyFrame_GetStackPointer(frame); \
- /* Set stackdepth to -1. \
- Update when returning or calling trace function. \
- Having stackdepth <= 0 ensures that invalid \
- values are not visible to the cycle GC. \
- We choose -1 rather than 0 to assist debugging. \
- */ \
- frame->stacktop = -1;
-
-
-start_frame:
- if (_Py_EnterRecursivePy(tstate)) {
- goto exit_unwind;
- }
-
-resume_frame:
- SET_LOCALS_FROM_FRAME();
-
-#ifdef LLTRACE
- {
- if (frame != &entry_frame) {
- int r = PyDict_Contains(GLOBALS(), &_Py_ID(__lltrace__));
- if (r < 0) {
- goto exit_unwind;
- }
- lltrace = r;
- }
- if (lltrace) {
- lltrace_resume_frame(frame);
- }
- }
-#endif
-
-#ifdef Py_DEBUG
- /* _PyEval_EvalFrameDefault() must not be called with an exception set,
- because it can clear it (directly or indirectly) and so the
- caller loses its exception */
- assert(!_PyErr_Occurred(tstate));
-#endif
-
- DISPATCH();
-
-handle_eval_breaker:
-
- /* Do periodic things, like check for signals and async I/0.
- * We need to do reasonably frequently, but not too frequently.
- * All loops should include a check of the eval breaker.
- * We also check on return from any builtin function.
- */
- if (_Py_HandlePending(tstate) != 0) {
- goto error;
- }
- DISPATCH();
-
- {
- /* Start instructions */
-#if !USE_COMPUTED_GOTOS
- dispatch_opcode :
- switch (opcode)
-#endif
- {
-
-#include "generated_cases.c.h"
-
-#if USE_COMPUTED_GOTOS
- TARGET_DO_TRACING :
-#else
- case DO_TRACING:
-#endif
- {
- assert(cframe.use_tracing);
- assert(tstate->tracing == 0);
- if (INSTR_OFFSET() >= frame->f_code->_co_firsttraceable) {
- int instr_prev = _PyInterpreterFrame_LASTI(frame);
- frame->prev_instr = next_instr;
- NEXTOPARG();
- // No _PyOpcode_Deopt here, since RESUME has no optimized forms:
- if (opcode == RESUME) {
- if (oparg < 2) {
- CHECK_EVAL_BREAKER();
- }
- /* Call tracing */
- TRACE_FUNCTION_ENTRY();
- DTRACE_FUNCTION_ENTRY();
- }
- else {
- /* line-by-line tracing support */
- if (PyDTrace_LINE_ENABLED()) {
- maybe_dtrace_line(frame, &tstate->trace_info, instr_prev);
- }
-
- if (cframe.use_tracing &&
- tstate->c_tracefunc != NULL && !tstate->tracing) {
- int err;
- /* see maybe_call_line_trace()
- for expository comments */
- _PyFrame_SetStackPointer(frame, stack_pointer);
-
- err = maybe_call_line_trace(tstate->c_tracefunc,
- tstate->c_traceobj,
- tstate, frame, instr_prev);
- // Reload possibly changed frame fields:
- stack_pointer = _PyFrame_GetStackPointer(frame);
- frame->stacktop = -1;
- // next_instr is only reloaded if tracing *does not* raise.
- // This is consistent with the behavior of older Python
- // versions. If a trace function sets a new f_lineno and
- // *then* raises, we use the *old* location when searching
- // for an exception handler, displaying the traceback, and
- // so on:
- if (err) {
- // next_instr wasn't incremented at the start of this
- // instruction. Increment it before handling the error,
- // so that it looks the same as a "normal" instruction:
- next_instr++;
- goto error;
- }
- // Reload next_instr. Don't increment it, though, since
- // we're going to re-dispatch to the "true" instruction now:
- next_instr = frame->prev_instr;
- }
- }
- }
- NEXTOPARG();
- PRE_DISPATCH_GOTO();
- // No _PyOpcode_Deopt here, since EXTENDED_ARG has no optimized forms:
- while (opcode == EXTENDED_ARG) {
- // CPython hasn't ever traced the instruction after an EXTENDED_ARG.
- // Inline the EXTENDED_ARG here, so we can avoid branching there:
- INSTRUCTION_START(EXTENDED_ARG);
- opcode = _Py_OPCODE(*next_instr);
- oparg = oparg << 8 | _Py_OPARG(*next_instr);
- // Make sure the next instruction isn't a RESUME, since that needs
- // to trace properly (and shouldn't have an EXTENDED_ARG, anyways):
- assert(opcode != RESUME);
- PRE_DISPATCH_GOTO();
- }
- opcode = _PyOpcode_Deopt[opcode];
- if (_PyOpcode_Caches[opcode]) {
- uint16_t *counter = &next_instr[1].cache;
- // The instruction is going to decrement the counter, so we need to
- // increment it here to make sure it doesn't try to specialize:
- if (!ADAPTIVE_COUNTER_IS_MAX(*counter)) {
- INCREMENT_ADAPTIVE_COUNTER(*counter);
- }
- }
- DISPATCH_GOTO();
- }
-
-#if USE_COMPUTED_GOTOS
- _unknown_opcode :
-#else
- EXTRA_CASES // From opcode.h, a 'case' for each unused opcode
-#endif
- /* Tell C compilers not to hold the opcode variable in the loop.
- next_instr points the current instruction without TARGET(). */
- opcode = _Py_OPCODE(*next_instr);
- _PyErr_Format(tstate, PyExc_SystemError,
- "%U:%d: unknown opcode %d",
- frame->f_code->co_filename,
- _PyInterpreterFrame_GetLine(frame),
- opcode);
- goto error;
-
- } /* End instructions */
-
- /* This should never be reached. Every opcode should end with DISPATCH()
- or goto error. */
- Py_UNREACHABLE();
-
- unbound_local_error:
- {
- format_exc_check_arg(tstate, PyExc_UnboundLocalError,
- UNBOUNDLOCAL_ERROR_MSG,
- PyTuple_GetItem(frame->f_code->co_localsplusnames, oparg)
- );
- goto error;
- }
-
- pop_4_error:
- STACK_SHRINK(1);
- pop_3_error:
- STACK_SHRINK(1);
- pop_2_error:
- STACK_SHRINK(1);
- pop_1_error:
- STACK_SHRINK(1);
- error:
- kwnames = NULL;
- /* Double-check exception status. */
-#ifdef NDEBUG
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_SetString(tstate, PyExc_SystemError,
- "error return without exception set");
- }
-#else
- assert(_PyErr_Occurred(tstate));
-#endif
-
- /* Log traceback info. */
- assert(frame != &entry_frame);
- if (!_PyFrame_IsIncomplete(frame)) {
- PyFrameObject *f = _PyFrame_GetFrameObject(frame);
- if (f != NULL) {
- PyTraceBack_Here(f);
- }
- }
-
- if (tstate->c_tracefunc != NULL) {
- /* Make sure state is set to FRAME_UNWINDING for tracing */
- call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
- tstate, frame);
- }
-
- exception_unwind:
- {
- /* We can't use frame->f_lasti here, as RERAISE may have set it */
- int offset = INSTR_OFFSET() - 1;
- int level, handler, lasti;
- if (get_exception_handler(frame->f_code, offset, &level, &handler, &lasti) == 0) {
- // No handlers, so exit.
- assert(_PyErr_Occurred(tstate));
-
- /* Pop remaining stack entries. */
- PyObject **stackbase = _PyFrame_Stackbase(frame);
- while (stack_pointer > stackbase) {
- PyObject *o = POP();
- Py_XDECREF(o);
- }
- assert(STACK_LEVEL() == 0);
- _PyFrame_SetStackPointer(frame, stack_pointer);
- TRACE_FUNCTION_UNWIND();
- DTRACE_FUNCTION_EXIT();
- goto exit_unwind;
- }
-
- assert(STACK_LEVEL() >= level);
- PyObject **new_top = _PyFrame_Stackbase(frame) + level;
- while (stack_pointer > new_top) {
- PyObject *v = POP();
- Py_XDECREF(v);
- }
- PyObject *exc, *val, *tb;
- if (lasti) {
- int frame_lasti = _PyInterpreterFrame_LASTI(frame);
- PyObject *lasti = PyLong_FromLong(frame_lasti);
- if (lasti == NULL) {
- goto exception_unwind;
- }
- PUSH(lasti);
- }
- _PyErr_Fetch(tstate, &exc, &val, &tb);
- /* Make the raw exception data
- available to the handler,
- so a program can emulate the
- Python main loop. */
- _PyErr_NormalizeException(tstate, &exc, &val, &tb);
- if (tb != NULL)
- PyException_SetTraceback(val, tb);
- else
- PyException_SetTraceback(val, Py_None);
- Py_XDECREF(tb);
- Py_XDECREF(exc);
- PUSH(val);
- JUMPTO(handler);
- /* Resume normal execution */
- DISPATCH();
- }
- }
-
-exit_unwind:
- assert(_PyErr_Occurred(tstate));
- _Py_LeaveRecursiveCallPy(tstate);
- assert(frame != &entry_frame);
- // GH-99729: We need to unlink the frame *before* clearing it:
- _PyInterpreterFrame *dying = frame;
- frame = cframe.current_frame = dying->previous;
- _PyEvalFrameClearAndPop(tstate, dying);
- if (frame == &entry_frame) {
- /* Restore previous cframe and exit */
- tstate->cframe = cframe.previous;
- tstate->cframe->use_tracing = cframe.use_tracing;
- assert(tstate->cframe->current_frame == frame->previous);
- _Py_LeaveRecursiveCallTstate(tstate);
- return NULL;
- }
-
-resume_with_error:
- SET_LOCALS_FROM_FRAME();
- goto error;
-
-}
-
-#undef TIER2_PROFILING
-#undef INSTRUCTION_START
-#undef TARGET
-#undef GETITEM
-#include "ceval_macros.h"
-
static void
format_missing(PyThreadState *tstate, const char *kind,
PyCodeObject *co, PyObject *names, PyObject *qualname)
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 1aa12383b748c6..7d601c71fde43b 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -47,10 +47,6 @@
#define OR_DTRACE_LINE
#endif
-#ifdef USE_COMPUTED_GOTOS
- #undef USE_COMPUTED_GOTOS
-#endif
-
#ifdef HAVE_COMPUTED_GOTOS
#ifndef USE_COMPUTED_GOTOS
#define USE_COMPUTED_GOTOS 1
@@ -63,56 +59,25 @@
#define USE_COMPUTED_GOTOS 0
#endif
-#ifdef INSTRUCTION_START
- #undef INSTRUCTION_START
-#endif
-
-#ifndef TIER2_PROFILING
- #ifdef Py_STATS
- #define INSTRUCTION_START(op) \
- do { \
- frame->prev_instr = next_instr++; \
- OPCODE_EXE_INC(op); \
- if (_py_stats) _py_stats->opcode_stats[lastopcode].pair_count[op]++; \
- lastopcode = op; \
- } while (0)
- #else
- #define INSTRUCTION_START(op) (frame->prev_instr = next_instr++)
- #endif
+#ifdef Py_STATS
+#define INSTRUCTION_START(op) \
+ do { \
+ frame->prev_instr = next_instr++; \
+ OPCODE_EXE_INC(op); \
+ if (_py_stats) _py_stats->opcode_stats[lastopcode].pair_count[op]++; \
+ lastopcode = op; \
+ } while (0)
#else
- #define INSTRUCTION_START(op) \
- do { \
- frame->prev_instr = next_instr++; \
- fprintf(stderr, "%d\n", op); \
- } while (0)
-#endif /* TIER2_PROFILING */
-
-#ifdef TARGET
-#undef TARGET
-#endif
-
-#ifdef DISPATCH_GOTO
-#undef DISPATCH_GOTO
+#define INSTRUCTION_START(op) (frame->prev_instr = next_instr++)
#endif
-#ifndef TIER2_PROFILING
- #if USE_COMPUTED_GOTOS
- # define TARGET(op) TARGET_##op: INSTRUCTION_START(op);
- # define DISPATCH_GOTO() goto *opcode_targets[opcode]
- #else
- # define TARGET(op) case op: TARGET_##op: INSTRUCTION_START(op);
- # define DISPATCH_GOTO() goto dispatch_opcode
- #endif
+#if USE_COMPUTED_GOTOS
+# define TARGET(op) TARGET_##op: INSTRUCTION_START(op);
+# define DISPATCH_GOTO() goto *opcode_targets[opcode]
#else
- #if USE_COMPUTED_GOTOS
- # define TARGET(op) TARGET_TIER2_PROFILE_##op: INSTRUCTION_START(op);
- # define DISPATCH_GOTO() goto *opcode_targets[opcode]
- #else
- # define TARGET(op) case op: TARGET_TIER2_PROFILE_##op: INSTRUCTION_START(op);
- # define DISPATCH_GOTO() goto dispatch_opcode
- #endif
-#endif /* TIER2_PROFILING */
-
+# define TARGET(op) case op: TARGET_##op: INSTRUCTION_START(op);
+# define DISPATCH_GOTO() goto dispatch_opcode
+#endif
/* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */
#ifdef LLTRACE
@@ -122,9 +87,6 @@
#define PRE_DISPATCH_GOTO() ((void)0)
#endif
-#ifdef DISPATCH
-#undef DISPATCH
-#endif
/* Do interpreter dispatch accounting for tracing and instrumentation */
#define DISPATCH() \
@@ -136,10 +98,6 @@
DISPATCH_GOTO(); \
}
-#ifdef DISPATCH_SAME_OPARG
-#undef DISPATCH_SAME_OPARG
-#endif
-
#define DISPATCH_SAME_OPARG() \
{ \
opcode = _Py_OPCODE(*next_instr); \
@@ -148,10 +106,6 @@
DISPATCH_GOTO(); \
}
-#ifdef DISPATCH_INLINED
-#undef DISPATCH_INLINED
-#endif
-
#define DISPATCH_INLINED(NEW_FRAME) \
do { \
_PyFrame_SetStackPointer(frame, stack_pointer); \
@@ -162,10 +116,6 @@
goto start_frame; \
} while (0)
-#ifdef CHECK_EVAL_BREAKER
-#undef CHECK_EVAL_BREAKER
-#endif
-
#define CHECK_EVAL_BREAKER() \
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); \
if (_Py_atomic_load_relaxed_int32(eval_breaker)) { \
@@ -175,12 +125,9 @@
/* Tuple access macros */
-
#ifndef Py_DEBUG
#define GETITEM(v, i) PyTuple_GET_ITEM((v), (i))
#else
-#ifndef _GETITEM_DEF_H
-#define _GETITEM_DEF_H
static inline PyObject *
GETITEM(PyObject *v, Py_ssize_t i) {
assert(PyTuple_Check(v));
@@ -188,27 +135,10 @@ GETITEM(PyObject *v, Py_ssize_t i) {
assert(i < PyTuple_GET_SIZE(v));
return PyTuple_GET_ITEM(v, i);
}
-#endif /* _GETITEM_DEF_H */
#endif
/* Code access macros */
-#ifdef INSTR_OFFSET
-#undef INSTR_OFFSET
-#endif
-
-#ifdef NEXTOPARG
-#undef NEXTOPARG
-#endif
-
-#ifdef JUMPTO
-#undef JUMPTO
-#endif
-
-#ifdef JUMPBY
-#undef JUMPBY
-#endif
-
/* The integer overflow is checked by an assertion below. */
// TODO change this calculation when interpreter is bb aware.
#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code)))
@@ -246,23 +176,7 @@ GETITEM(PyObject *v, Py_ssize_t i) {
*/
-#ifdef PREDICT_ID
-#undef PREDICT_ID
-#endif
-
-#ifndef TIER2_PROFILING
#define PREDICT_ID(op) PRED_##op
-#else
-#define PREDICT_ID(op) PRED_TIER2_PROFILING_##op
-#endif
-
-#ifdef PREDICT
-#undef PREDICT
-#endif
-
-#ifdef PREDICTED
-#undef PREDICTED
-#endif
#if USE_COMPUTED_GOTOS
#define PREDICT(op) if (0) goto PREDICT_ID(op)
@@ -337,7 +251,6 @@ GETITEM(PyObject *v, Py_ssize_t i) {
GETLOCAL(i) = value; \
Py_XDECREF(tmp); } while (0)
-
#define GO_TO_INSTRUCTION(op) goto PREDICT_ID(op)
#ifdef Py_STATS
diff --git a/Python/compile.c b/Python/compile.c
index df2dffb95bbd7e..bdc25ba9df9b4a 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1079,6 +1079,7 @@ stack_effect(int opcode, int oparg, int jump)
case EXTENDED_ARG:
case RESUME:
case CACHE:
+ case BB_NEXT:
return 0;
/* Stack manipulation */
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index ca6216218f03c2..605b9767417d3c 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -24,10 +24,7 @@
TARGET(RESUME) {
if (cframe.use_tracing == 0) {
- PyObject *retval = NULL;
- if (_PyCode_Tier2Warmup(tstate, frame, throwflag, next_instr, stack_pointer, &retval)) {
- return retval;
- }
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
}
GO_TO_INSTRUCTION(RESUME_QUICK);
}
@@ -2420,18 +2417,14 @@
TARGET(JUMP_BACKWARD) {
PREDICTED(JUMP_BACKWARD);
- JUMPBY(-oparg);
- CHECK_EVAL_BREAKER();
if (cframe.use_tracing == 0) {
- PyObject *retval = NULL;
- if (_PyCode_Tier2Warmup(tstate, frame, throwflag, next_instr, stack_pointer, &retval)) {
- return retval;
- }
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
}
- DISPATCH();
+ GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
}
TARGET(JUMP_BACKWARD_QUICK) {
+ PREDICTED(JUMP_BACKWARD_QUICK);
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Python/tier2.c b/Python/tier2.c
index ddb82526d7e719..a5a2ebc569eb0d 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1,22 +1,16 @@
#include "Python.h"
#include "pycore_code.h"
#include "pycore_frame.h"
-#include "pycore_interp.h"
#include "opcode.h"
-#include "pystate.h"
-#include "pytypedefs.h"
static _Py_CODEUNIT *
_PyCode_Tier2Initialize(_PyInterpreterFrame *, _Py_CODEUNIT *);
// Tier 2 warmup counter
-int
-_PyCode_Tier2Warmup(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag, _Py_CODEUNIT *next_instr, PyObject **stack_pointer, PyObject **retval)
+_Py_CODEUNIT *
+_PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
- if (tstate->interp->eval_frame != NULL) {
- return 0;
- }
PyCodeObject *code = frame->f_code;
if (code->_tier2_warmup != 0) {
code->_tier2_warmup++;
@@ -24,22 +18,15 @@ _PyCode_Tier2Warmup(PyThreadState *tstate, _PyInterpreterFrame *frame, int throw
// If it fails, due to lack of memory or whatever,
// just fall back to the tier 1 interpreter.
_Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
- // Swap out the profiler to use the profiling eval loop.
- frame->prev_instr = next_instr - 1;
- _PyFrame_SetStackPointer(frame, stack_pointer);
- // Do something with entry_frame here, maybe set the current frame to an entry
- // frame and check for that in RETURN_VALUE?
- *retval = _PyEval_EvalFrameTier2Profile(tstate, frame, throwflag);
- PyObject_Print(*retval, stderr, Py_PRINT_RAW);
- return 1;
-
+ if (next != NULL) {
+ return next;
+ }
}
}
- return 0;
+ return next_instr;
}
-
static _PyTier2BBSpace *
_PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
{
@@ -70,6 +57,7 @@ static _Py_CODEUNIT *
_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
int curr = _Py_OPCODE(*(next_instr - 1));
+ assert(curr == RESUME || curr == JUMP_BACKWARD);
PyCodeObject *co = frame->f_code;
assert(co->_bb_space == NULL);
// 1. Initialize basic blocks space.
From 46ee564fa9cfe4ff5d0eb97377fa0d1885562bcb Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 10 Feb 2023 21:18:28 +0800
Subject: [PATCH 020/280] Add functionality to detect BB from code objects
---
Include/cpython/code.h | 13 +-
Include/internal/pycore_frame.h | 3 +-
Include/internal/pycore_opcode.h | 40 +--
.../internal/pycore_opcode_macro_to_micro.h | 2 +-
Include/opcode.h | 250 +++++++++---------
Lib/opcode.py | 3 -
Objects/codeobject.c | 16 +-
Python/bytecodes.c | 7 -
Python/compile.c | 1 -
Python/generated_cases.c.h | 8 -
Python/opcode_metadata.h | 17 +-
Python/opcode_targets.h | 38 +--
Python/tier2.c | 225 +++++++++++++---
Tools/build/deepfreeze.py | 4 +-
14 files changed, 395 insertions(+), 232 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index f71a4a679e9098..9d6c29eee27a7e 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -45,10 +45,10 @@ typedef struct {
// What the tier 2 interpreter executes
typedef struct _PyTier2BB {
- // Stores the start pointer in the tier 1 bytecode.
- // So that when we exit the trace we can calculate where to return.
- struct _PyTier2BB *bb_next;
- _Py_CODEUNIT *tier1_start;
+ struct _PyTier2BB *successor_bb;
+ // Stores the end pointer in the tier 1 bytecode.
+ // So that when we exit the BB we can calculate where to return.
+ _Py_CODEUNIT *tier1_end;
_Py_CODEUNIT u_code[1];
} _PyTier2BB;
@@ -122,8 +122,11 @@ typedef struct _PyTier2BBSpace {
int _co_firsttraceable; /* index of first traceable instruction */ \
char *_co_linearray; /* array of line offsets */ \
int _tier2_warmup; /* warmup counter for tier 2 */ \
- _PyTier2BB *_bb_next; /* the tier 2 basic block to execute (if any) */ \
+ _PyTier2BB *_entry_bb; /* the tier 2 basic block to execute (if any) */ \
_PyTier2BBSpace *_bb_space; /* linked list storing basic blocks */ \
+ int _jump_target_count; /* Number of entries in _jump_targets */ \
+ /* sorted ascending offsets (from start of co_code_adaptive) for jump targets */ \
+ int *_jump_targets; \
/* Scratch space for extra data relating to the code object. \
Type is a void* to keep the format private in codeobject.c to force \
people to go through the proper APIs. */ \
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index 1aff2dc6a6d065..ce87e2a428d8f0 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -121,7 +121,8 @@ _PyFrame_Initialize(
frame->f_locals = locals;
frame->stacktop = code->co_nlocalsplus;
frame->frame_obj = NULL;
- frame->prev_instr = (code->_bb_next == NULL ? _PyCode_CODE(code) - 1 : code->_bb_next->u_code - 1);
+ // @TODO CHANGE ME
+ frame->prev_instr = _PyCode_CODE(code);
frame->yield_offset = 0;
frame->owner = FRAME_OWNED_BY_THREAD;
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index a2a87853c6e128..eee13ed8962825 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -140,7 +140,6 @@ const uint8_t _PyOpcode_Deopt[256] = {
[IS_OP] = IS_OP,
[JUMP_BACKWARD] = JUMP_BACKWARD,
[JUMP_BACKWARD_NO_INTERRUPT] = JUMP_BACKWARD_NO_INTERRUPT,
- [JUMP_BACKWARD_QUICK] = JUMP_BACKWARD,
[JUMP_FORWARD] = JUMP_FORWARD,
[JUMP_IF_FALSE_OR_POP] = JUMP_IF_FALSE_OR_POP,
[JUMP_IF_TRUE_OR_POP] = JUMP_IF_TRUE_OR_POP,
@@ -237,17 +236,16 @@ static const char *const _PyOpcode_OpName[263] = {
[INTERPRETER_EXIT] = "INTERPRETER_EXIT",
[END_FOR] = "END_FOR",
[RESUME_QUICK] = "RESUME_QUICK",
- [JUMP_BACKWARD_QUICK] = "JUMP_BACKWARD_QUICK",
[BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
[BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT",
- [NOP] = "NOP",
[BINARY_OP_ADD_UNICODE] = "BINARY_OP_ADD_UNICODE",
+ [NOP] = "NOP",
+ [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE",
[UNARY_NEGATIVE] = "UNARY_NEGATIVE",
[UNARY_NOT] = "UNARY_NOT",
- [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE",
[BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT",
- [UNARY_INVERT] = "UNARY_INVERT",
[BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT",
+ [UNARY_INVERT] = "UNARY_INVERT",
[BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT",
[BINARY_OP_SUBTRACT_INT] = "BINARY_OP_SUBTRACT_INT",
[BINARY_SUBSCR_DICT] = "BINARY_SUBSCR_DICT",
@@ -256,20 +254,20 @@ static const char *const _PyOpcode_OpName[263] = {
[BINARY_SUBSCR_TUPLE_INT] = "BINARY_SUBSCR_TUPLE_INT",
[CALL_PY_EXACT_ARGS] = "CALL_PY_EXACT_ARGS",
[CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS",
+ [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS",
[BINARY_SUBSCR] = "BINARY_SUBSCR",
[BINARY_SLICE] = "BINARY_SLICE",
[STORE_SLICE] = "STORE_SLICE",
- [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS",
[CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS",
+ [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS",
[GET_LEN] = "GET_LEN",
[MATCH_MAPPING] = "MATCH_MAPPING",
[MATCH_SEQUENCE] = "MATCH_SEQUENCE",
[MATCH_KEYS] = "MATCH_KEYS",
- [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS",
+ [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
[PUSH_EXC_INFO] = "PUSH_EXC_INFO",
[CHECK_EXC_MATCH] = "CHECK_EXC_MATCH",
[CHECK_EG_MATCH] = "CHECK_EG_MATCH",
- [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
[CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST",
[CALL_NO_KW_BUILTIN_O] = "CALL_NO_KW_BUILTIN_O",
[CALL_NO_KW_ISINSTANCE] = "CALL_NO_KW_ISINSTANCE",
@@ -280,6 +278,7 @@ static const char *const _PyOpcode_OpName[263] = {
[CALL_NO_KW_METHOD_DESCRIPTOR_O] = "CALL_NO_KW_METHOD_DESCRIPTOR_O",
[CALL_NO_KW_STR_1] = "CALL_NO_KW_STR_1",
[CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1",
+ [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1",
[WITH_EXCEPT_START] = "WITH_EXCEPT_START",
[GET_AITER] = "GET_AITER",
[GET_ANEXT] = "GET_ANEXT",
@@ -287,38 +286,38 @@ static const char *const _PyOpcode_OpName[263] = {
[BEFORE_WITH] = "BEFORE_WITH",
[END_ASYNC_FOR] = "END_ASYNC_FOR",
[CLEANUP_THROW] = "CLEANUP_THROW",
- [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1",
[COMPARE_AND_BRANCH_FLOAT] = "COMPARE_AND_BRANCH_FLOAT",
[COMPARE_AND_BRANCH_INT] = "COMPARE_AND_BRANCH_INT",
[COMPARE_AND_BRANCH_STR] = "COMPARE_AND_BRANCH_STR",
+ [FOR_ITER_LIST] = "FOR_ITER_LIST",
[STORE_SUBSCR] = "STORE_SUBSCR",
[DELETE_SUBSCR] = "DELETE_SUBSCR",
- [FOR_ITER_LIST] = "FOR_ITER_LIST",
[FOR_ITER_TUPLE] = "FOR_ITER_TUPLE",
[FOR_ITER_RANGE] = "FOR_ITER_RANGE",
[FOR_ITER_GEN] = "FOR_ITER_GEN",
[LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS",
[LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
+ [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
[GET_ITER] = "GET_ITER",
[GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER",
- [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
- [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
[LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
+ [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
[LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
+ [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
[LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
[RETURN_GENERATOR] = "RETURN_GENERATOR",
- [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
[LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
[LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT",
[LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
[LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
[LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
[LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
- [RETURN_VALUE] = "RETURN_VALUE",
[LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
- [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
+ [RETURN_VALUE] = "RETURN_VALUE",
[LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
+ [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
[LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
+ [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
[PREP_RERAISE_STAR] = "PREP_RERAISE_STAR",
[POP_EXCEPT] = "POP_EXCEPT",
[STORE_NAME] = "STORE_NAME",
@@ -344,7 +343,7 @@ static const char *const _PyOpcode_OpName[263] = {
[JUMP_FORWARD] = "JUMP_FORWARD",
[JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP",
[JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP",
- [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
+ [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
[POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
[LOAD_GLOBAL] = "LOAD_GLOBAL",
@@ -374,7 +373,7 @@ static const char *const _PyOpcode_OpName[263] = {
[JUMP_BACKWARD] = "JUMP_BACKWARD",
[COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH",
[CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
- [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
+ [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD",
@@ -384,23 +383,23 @@ static const char *const _PyOpcode_OpName[263] = {
[YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS",
- [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
+ [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING",
- [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
[STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
+ [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
- [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
[BINARY_OP_ADD_INT_TYPE_CHECK] = "BINARY_OP_ADD_INT_TYPE_CHECK",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
+ [169] = "<169>",
[170] = "<170>",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
@@ -499,6 +498,7 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
+ case 169: \
case 170: \
case 174: \
case 175: \
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index 13d681be4afa83..bffa49d3ceefcb 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -54,6 +54,7 @@ extern const int _Py_MacroOpUOpCount[] = {
[RAISE_VARARGS] = 1,
[INTERPRETER_EXIT] = 1,
[RETURN_VALUE] = 1,
+[RETURN_CONST] = 1,
[GET_AITER] = 1,
[GET_ANEXT] = 1,
[GET_AWAITABLE] = 1,
@@ -124,7 +125,6 @@ extern const int _Py_MacroOpUOpCount[] = {
[IMPORT_FROM] = 1,
[JUMP_FORWARD] = 1,
[JUMP_BACKWARD] = 1,
-[JUMP_BACKWARD_QUICK] = 1,
[POP_JUMP_IF_FALSE] = 1,
[POP_JUMP_IF_TRUE] = 1,
[POP_JUMP_IF_NOT_NONE] = 1,
diff --git a/Include/opcode.h b/Include/opcode.h
index 91662127552c56..b89441dc98e835 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -127,136 +127,134 @@ extern "C" {
#define LOAD_METHOD 262
#define MAX_PSEUDO_OPCODE 262
#define RESUME_QUICK 5
-#define JUMP_BACKWARD_QUICK 6
-#define BINARY_OP_ADD_FLOAT 7
-#define BINARY_OP_ADD_INT 8
-#define BINARY_OP_ADD_UNICODE 10
-#define BINARY_OP_INPLACE_ADD_UNICODE 13
-#define BINARY_OP_MULTIPLY_FLOAT 14
-#define BINARY_OP_MULTIPLY_INT 16
-#define BINARY_OP_SUBTRACT_FLOAT 17
-#define BINARY_OP_SUBTRACT_INT 18
-#define BINARY_SUBSCR_DICT 19
-#define BINARY_SUBSCR_GETITEM 20
-#define BINARY_SUBSCR_LIST_INT 21
-#define BINARY_SUBSCR_TUPLE_INT 22
-#define CALL_PY_EXACT_ARGS 23
-#define CALL_PY_WITH_DEFAULTS 24
-#define CALL_BOUND_METHOD_EXACT_ARGS 28
-#define CALL_BUILTIN_CLASS 29
-#define CALL_BUILTIN_FAST_WITH_KEYWORDS 34
-#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 38
-#define CALL_NO_KW_BUILTIN_FAST 39
-#define CALL_NO_KW_BUILTIN_O 40
-#define CALL_NO_KW_ISINSTANCE 41
-#define CALL_NO_KW_LEN 42
-#define CALL_NO_KW_LIST_APPEND 43
-#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44
-#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45
-#define CALL_NO_KW_METHOD_DESCRIPTOR_O 46
-#define CALL_NO_KW_STR_1 47
-#define CALL_NO_KW_TUPLE_1 48
-#define CALL_NO_KW_TYPE_1 56
-#define COMPARE_AND_BRANCH_FLOAT 57
-#define COMPARE_AND_BRANCH_INT 58
-#define COMPARE_AND_BRANCH_STR 59
-#define FOR_ITER_LIST 62
-#define FOR_ITER_TUPLE 63
-#define FOR_ITER_RANGE 64
-#define FOR_ITER_GEN 65
-#define LOAD_ATTR_CLASS 66
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 67
-#define LOAD_ATTR_INSTANCE_VALUE 70
-#define LOAD_ATTR_MODULE 72
-#define LOAD_ATTR_PROPERTY 73
-#define LOAD_ATTR_SLOT 76
-#define LOAD_ATTR_WITH_HINT 77
-#define LOAD_ATTR_METHOD_LAZY_DICT 78
-#define LOAD_ATTR_METHOD_NO_DICT 79
-#define LOAD_ATTR_METHOD_WITH_VALUES 80
-#define LOAD_CONST__LOAD_FAST 81
-#define LOAD_FAST__LOAD_CONST 82
-#define LOAD_FAST__LOAD_FAST 84
-#define LOAD_GLOBAL_BUILTIN 86
-#define LOAD_GLOBAL_MODULE 87
-#define STORE_ATTR_INSTANCE_VALUE 113
-#define STORE_ATTR_SLOT 143
-#define STORE_ATTR_WITH_HINT 153
-#define STORE_FAST__LOAD_FAST 154
-#define STORE_FAST__STORE_FAST 158
-#define STORE_SUBSCR_DICT 159
-#define STORE_SUBSCR_LIST_INT 160
-#define UNPACK_SEQUENCE_LIST 161
-#define UNPACK_SEQUENCE_TUPLE 166
-#define UNPACK_SEQUENCE_TWO_TUPLE 167
+#define BINARY_OP_ADD_FLOAT 6
+#define BINARY_OP_ADD_INT 7
+#define BINARY_OP_ADD_UNICODE 8
+#define BINARY_OP_INPLACE_ADD_UNICODE 10
+#define BINARY_OP_MULTIPLY_FLOAT 13
+#define BINARY_OP_MULTIPLY_INT 14
+#define BINARY_OP_SUBTRACT_FLOAT 16
+#define BINARY_OP_SUBTRACT_INT 17
+#define BINARY_SUBSCR_DICT 18
+#define BINARY_SUBSCR_GETITEM 19
+#define BINARY_SUBSCR_LIST_INT 20
+#define BINARY_SUBSCR_TUPLE_INT 21
+#define CALL_PY_EXACT_ARGS 22
+#define CALL_PY_WITH_DEFAULTS 23
+#define CALL_BOUND_METHOD_EXACT_ARGS 24
+#define CALL_BUILTIN_CLASS 28
+#define CALL_BUILTIN_FAST_WITH_KEYWORDS 29
+#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 34
+#define CALL_NO_KW_BUILTIN_FAST 38
+#define CALL_NO_KW_BUILTIN_O 39
+#define CALL_NO_KW_ISINSTANCE 40
+#define CALL_NO_KW_LEN 41
+#define CALL_NO_KW_LIST_APPEND 42
+#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 43
+#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 44
+#define CALL_NO_KW_METHOD_DESCRIPTOR_O 45
+#define CALL_NO_KW_STR_1 46
+#define CALL_NO_KW_TUPLE_1 47
+#define CALL_NO_KW_TYPE_1 48
+#define COMPARE_AND_BRANCH_FLOAT 56
+#define COMPARE_AND_BRANCH_INT 57
+#define COMPARE_AND_BRANCH_STR 58
+#define FOR_ITER_LIST 59
+#define FOR_ITER_TUPLE 62
+#define FOR_ITER_RANGE 63
+#define FOR_ITER_GEN 64
+#define LOAD_ATTR_CLASS 65
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 66
+#define LOAD_ATTR_INSTANCE_VALUE 67
+#define LOAD_ATTR_MODULE 70
+#define LOAD_ATTR_PROPERTY 72
+#define LOAD_ATTR_SLOT 73
+#define LOAD_ATTR_WITH_HINT 76
+#define LOAD_ATTR_METHOD_LAZY_DICT 77
+#define LOAD_ATTR_METHOD_NO_DICT 78
+#define LOAD_ATTR_METHOD_WITH_VALUES 79
+#define LOAD_CONST__LOAD_FAST 80
+#define LOAD_FAST__LOAD_CONST 81
+#define LOAD_FAST__LOAD_FAST 82
+#define LOAD_GLOBAL_BUILTIN 84
+#define LOAD_GLOBAL_MODULE 86
+#define STORE_ATTR_INSTANCE_VALUE 87
+#define STORE_ATTR_SLOT 113
+#define STORE_ATTR_WITH_HINT 143
+#define STORE_FAST__LOAD_FAST 153
+#define STORE_FAST__STORE_FAST 154
+#define STORE_SUBSCR_DICT 158
+#define STORE_SUBSCR_LIST_INT 159
+#define UNPACK_SEQUENCE_LIST 160
+#define UNPACK_SEQUENCE_TUPLE 161
+#define UNPACK_SEQUENCE_TWO_TUPLE 166
#define DO_TRACING 255
// Tier 2 interpreter ops
#define RESUME_QUICK 5
-#define JUMP_BACKWARD_QUICK 6
-#define BINARY_OP_ADD_FLOAT 7
-#define BINARY_OP_ADD_INT 8
-#define BINARY_OP_ADD_UNICODE 10
-#define BINARY_OP_INPLACE_ADD_UNICODE 13
-#define BINARY_OP_MULTIPLY_FLOAT 14
-#define BINARY_OP_MULTIPLY_INT 16
-#define BINARY_OP_SUBTRACT_FLOAT 17
-#define BINARY_OP_SUBTRACT_INT 18
-#define BINARY_SUBSCR_DICT 19
-#define BINARY_SUBSCR_GETITEM 20
-#define BINARY_SUBSCR_LIST_INT 21
-#define BINARY_SUBSCR_TUPLE_INT 22
-#define CALL_PY_EXACT_ARGS 23
-#define CALL_PY_WITH_DEFAULTS 24
-#define CALL_BOUND_METHOD_EXACT_ARGS 28
-#define CALL_BUILTIN_CLASS 29
-#define CALL_BUILTIN_FAST_WITH_KEYWORDS 34
-#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 38
-#define CALL_NO_KW_BUILTIN_FAST 39
-#define CALL_NO_KW_BUILTIN_O 40
-#define CALL_NO_KW_ISINSTANCE 41
-#define CALL_NO_KW_LEN 42
-#define CALL_NO_KW_LIST_APPEND 43
-#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44
-#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45
-#define CALL_NO_KW_METHOD_DESCRIPTOR_O 46
-#define CALL_NO_KW_STR_1 47
-#define CALL_NO_KW_TUPLE_1 48
-#define CALL_NO_KW_TYPE_1 56
-#define COMPARE_AND_BRANCH_FLOAT 57
-#define COMPARE_AND_BRANCH_INT 58
-#define COMPARE_AND_BRANCH_STR 59
-#define FOR_ITER_LIST 62
-#define FOR_ITER_TUPLE 63
-#define FOR_ITER_RANGE 64
-#define FOR_ITER_GEN 65
-#define LOAD_ATTR_CLASS 66
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 67
-#define LOAD_ATTR_INSTANCE_VALUE 70
-#define LOAD_ATTR_MODULE 72
-#define LOAD_ATTR_PROPERTY 73
-#define LOAD_ATTR_SLOT 76
-#define LOAD_ATTR_WITH_HINT 77
-#define LOAD_ATTR_METHOD_LAZY_DICT 78
-#define LOAD_ATTR_METHOD_NO_DICT 79
-#define LOAD_ATTR_METHOD_WITH_VALUES 80
-#define LOAD_CONST__LOAD_FAST 81
-#define LOAD_FAST__LOAD_CONST 82
-#define LOAD_FAST__LOAD_FAST 84
-#define LOAD_GLOBAL_BUILTIN 86
-#define LOAD_GLOBAL_MODULE 87
-#define STORE_ATTR_INSTANCE_VALUE 113
-#define STORE_ATTR_SLOT 143
-#define STORE_ATTR_WITH_HINT 153
-#define STORE_FAST__LOAD_FAST 154
-#define STORE_FAST__STORE_FAST 158
-#define STORE_SUBSCR_DICT 159
-#define STORE_SUBSCR_LIST_INT 160
-#define UNPACK_SEQUENCE_LIST 161
-#define UNPACK_SEQUENCE_TUPLE 166
-#define UNPACK_SEQUENCE_TWO_TUPLE 167
+#define BINARY_OP_ADD_FLOAT 6
+#define BINARY_OP_ADD_INT 7
+#define BINARY_OP_ADD_UNICODE 8
+#define BINARY_OP_INPLACE_ADD_UNICODE 10
+#define BINARY_OP_MULTIPLY_FLOAT 13
+#define BINARY_OP_MULTIPLY_INT 14
+#define BINARY_OP_SUBTRACT_FLOAT 16
+#define BINARY_OP_SUBTRACT_INT 17
+#define BINARY_SUBSCR_DICT 18
+#define BINARY_SUBSCR_GETITEM 19
+#define BINARY_SUBSCR_LIST_INT 20
+#define BINARY_SUBSCR_TUPLE_INT 21
+#define CALL_PY_EXACT_ARGS 22
+#define CALL_PY_WITH_DEFAULTS 23
+#define CALL_BOUND_METHOD_EXACT_ARGS 24
+#define CALL_BUILTIN_CLASS 28
+#define CALL_BUILTIN_FAST_WITH_KEYWORDS 29
+#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 34
+#define CALL_NO_KW_BUILTIN_FAST 38
+#define CALL_NO_KW_BUILTIN_O 39
+#define CALL_NO_KW_ISINSTANCE 40
+#define CALL_NO_KW_LEN 41
+#define CALL_NO_KW_LIST_APPEND 42
+#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 43
+#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 44
+#define CALL_NO_KW_METHOD_DESCRIPTOR_O 45
+#define CALL_NO_KW_STR_1 46
+#define CALL_NO_KW_TUPLE_1 47
+#define CALL_NO_KW_TYPE_1 48
+#define COMPARE_AND_BRANCH_FLOAT 56
+#define COMPARE_AND_BRANCH_INT 57
+#define COMPARE_AND_BRANCH_STR 58
+#define FOR_ITER_LIST 59
+#define FOR_ITER_TUPLE 62
+#define FOR_ITER_RANGE 63
+#define FOR_ITER_GEN 64
+#define LOAD_ATTR_CLASS 65
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 66
+#define LOAD_ATTR_INSTANCE_VALUE 67
+#define LOAD_ATTR_MODULE 70
+#define LOAD_ATTR_PROPERTY 72
+#define LOAD_ATTR_SLOT 73
+#define LOAD_ATTR_WITH_HINT 76
+#define LOAD_ATTR_METHOD_LAZY_DICT 77
+#define LOAD_ATTR_METHOD_NO_DICT 78
+#define LOAD_ATTR_METHOD_WITH_VALUES 79
+#define LOAD_CONST__LOAD_FAST 80
+#define LOAD_FAST__LOAD_CONST 81
+#define LOAD_FAST__LOAD_FAST 82
+#define LOAD_GLOBAL_BUILTIN 84
+#define LOAD_GLOBAL_MODULE 86
+#define STORE_ATTR_INSTANCE_VALUE 87
+#define STORE_ATTR_SLOT 113
+#define STORE_ATTR_WITH_HINT 143
+#define STORE_FAST__LOAD_FAST 153
+#define STORE_FAST__STORE_FAST 154
+#define STORE_SUBSCR_DICT 158
+#define STORE_SUBSCR_LIST_INT 159
+#define UNPACK_SEQUENCE_LIST 160
+#define UNPACK_SEQUENCE_TUPLE 161
+#define UNPACK_SEQUENCE_TWO_TUPLE 166
#define DO_TRACING 255
-#define BINARY_OP_ADD_INT_TYPE_CHECK 168
-#define BINARY_OP_ADD_INT_REST 169
+#define BINARY_OP_ADD_INT_TYPE_CHECK 167
+#define BINARY_OP_ADD_INT_REST 168
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index afb1e4713a2718..a12b6568dace4d 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -284,9 +284,6 @@ def pseudo_op(name, op, real_ops):
"RESUME": [
"RESUME_QUICK",
],
- "JUMP_BACKWARD": [
- "JUMP_BACKWARD_QUICK",
- ],
"BINARY_OP": [
"BINARY_OP_ADD_FLOAT",
"BINARY_OP_ADD_INT",
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index ba3c7bdce5a772..db3a7b6970bd31 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -410,8 +410,10 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
co->_co_linearray_entry_size = 0;
co->_co_linearray = NULL;
co->_tier2_warmup = -64;
- co->_bb_next = NULL;
+ co->_entry_bb = NULL;
co->_bb_space = NULL;
+ co->_jump_target_count = 0;
+ co->_jump_targets = NULL;
memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code),
PyBytes_GET_SIZE(con->code));
int entry_point = 0;
@@ -1708,7 +1710,8 @@ code_dealloc(PyCodeObject *co)
if (co->_co_linearray) {
PyMem_Free(co->_co_linearray);
}
- co->_bb_next = NULL;
+ co->_entry_bb = NULL;
+ co->_jump_target_count = 0;
if (co->_bb_space != NULL) {
// Traverse the linked list
for (_PyTier2BBSpace *curr = co->_bb_space; curr != NULL;) {
@@ -1718,6 +1721,9 @@ code_dealloc(PyCodeObject *co)
}
co->_bb_space = NULL;
}
+ if (co->_jump_targets != NULL) {
+ PyMem_Free(co->_jump_targets);
+ }
PyObject_Free(co);
}
@@ -2295,7 +2301,8 @@ _PyStaticCode_Fini(PyCodeObject *co)
PyMem_Free(co->_co_cached);
co->_co_cached = NULL;
}
- co->_bb_next = NULL;
+ co->_entry_bb = NULL;
+ co->_jump_target_count = 0;
if (co->_bb_space != NULL) {
// Traverse the linked list
for (_PyTier2BBSpace *curr = co->_bb_space; curr != NULL;) {
@@ -2305,6 +2312,9 @@ _PyStaticCode_Fini(PyCodeObject *co)
}
co->_bb_space = NULL;
}
+ if (co->_jump_targets != NULL) {
+ PyMem_Free(co->_jump_targets);
+ }
co->co_extra = NULL;
if (co->co_weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject *)co);
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index c310802d577507..200708b67a46ac 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1881,13 +1881,6 @@ dummy_func(
}
inst(JUMP_BACKWARD, (--)) {
- if (cframe.use_tracing == 0) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
- }
- GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
- }
-
- inst(JUMP_BACKWARD_QUICK, (--)) {
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Python/compile.c b/Python/compile.c
index bdc25ba9df9b4a..df2dffb95bbd7e 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1079,7 +1079,6 @@ stack_effect(int opcode, int oparg, int jump)
case EXTENDED_ARG:
case RESUME:
case CACHE:
- case BB_NEXT:
return 0;
/* Stack manipulation */
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 605b9767417d3c..a93b444476b450 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2417,14 +2417,6 @@
TARGET(JUMP_BACKWARD) {
PREDICTED(JUMP_BACKWARD);
- if (cframe.use_tracing == 0) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
- }
- GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
- }
-
- TARGET(JUMP_BACKWARD_QUICK) {
- PREDICTED(JUMP_BACKWARD_QUICK);
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index de7be63ac6aeac..6551cd7807f128 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -10,6 +10,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 0;
case RESUME:
return 0;
+ case RESUME_QUICK:
+ return 0;
case LOAD_CLOSURE:
return 0;
case LOAD_FAST_CHECK:
@@ -56,12 +58,12 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 2;
case BINARY_OP_ADD_FLOAT:
return 2;
+ case BINARY_OP_ADD_INT:
+ return 2;
case BINARY_OP_ADD_INT_TYPE_CHECK:
return 2;
case BINARY_OP_ADD_INT_REST:
return 2;
- case BINARY_OP_ADD_INT:
- return 2;
case BINARY_SUBSCR:
return 2;
case BINARY_SLICE:
@@ -362,6 +364,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case RESUME:
return 0;
+ case RESUME_QUICK:
+ return 0;
case LOAD_CLOSURE:
return 1;
case LOAD_FAST_CHECK:
@@ -408,12 +412,12 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case BINARY_OP_ADD_FLOAT:
return 1;
+ case BINARY_OP_ADD_INT:
+ return 1;
case BINARY_OP_ADD_INT_TYPE_CHECK:
return 2;
case BINARY_OP_ADD_INT_REST:
return 1;
- case BINARY_OP_ADD_INT:
- return 1;
case BINARY_SUBSCR:
return 1;
case BINARY_SLICE:
@@ -715,7 +719,8 @@ struct opcode_metadata {
enum InstructionFormat instr_format;
} _PyOpcode_opcode_metadata[256] = {
[NOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [RESUME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [RESUME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [RESUME_QUICK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_FAST_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
@@ -740,6 +745,8 @@ struct opcode_metadata {
[BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[BINARY_OP_ADD_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_OP_ADD_INT_TYPE_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BINARY_OP_ADD_INT_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
[BINARY_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[STORE_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 839778d38beca9..1ec5cbbb9a0e4b 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -5,17 +5,16 @@ static void *opcode_targets[256] = {
&&TARGET_INTERPRETER_EXIT,
&&TARGET_END_FOR,
&&TARGET_RESUME_QUICK,
- &&TARGET_JUMP_BACKWARD_QUICK,
&&TARGET_BINARY_OP_ADD_FLOAT,
&&TARGET_BINARY_OP_ADD_INT,
- &&TARGET_NOP,
&&TARGET_BINARY_OP_ADD_UNICODE,
+ &&TARGET_NOP,
+ &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE,
&&TARGET_UNARY_NEGATIVE,
&&TARGET_UNARY_NOT,
- &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE,
&&TARGET_BINARY_OP_MULTIPLY_FLOAT,
- &&TARGET_UNARY_INVERT,
&&TARGET_BINARY_OP_MULTIPLY_INT,
+ &&TARGET_UNARY_INVERT,
&&TARGET_BINARY_OP_SUBTRACT_FLOAT,
&&TARGET_BINARY_OP_SUBTRACT_INT,
&&TARGET_BINARY_SUBSCR_DICT,
@@ -24,20 +23,20 @@ static void *opcode_targets[256] = {
&&TARGET_BINARY_SUBSCR_TUPLE_INT,
&&TARGET_CALL_PY_EXACT_ARGS,
&&TARGET_CALL_PY_WITH_DEFAULTS,
+ &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS,
&&TARGET_BINARY_SUBSCR,
&&TARGET_BINARY_SLICE,
&&TARGET_STORE_SLICE,
- &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS,
&&TARGET_CALL_BUILTIN_CLASS,
+ &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS,
&&TARGET_GET_LEN,
&&TARGET_MATCH_MAPPING,
&&TARGET_MATCH_SEQUENCE,
&&TARGET_MATCH_KEYS,
- &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS,
+ &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS,
&&TARGET_PUSH_EXC_INFO,
&&TARGET_CHECK_EXC_MATCH,
&&TARGET_CHECK_EG_MATCH,
- &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS,
&&TARGET_CALL_NO_KW_BUILTIN_FAST,
&&TARGET_CALL_NO_KW_BUILTIN_O,
&&TARGET_CALL_NO_KW_ISINSTANCE,
@@ -48,6 +47,7 @@ static void *opcode_targets[256] = {
&&TARGET_CALL_NO_KW_METHOD_DESCRIPTOR_O,
&&TARGET_CALL_NO_KW_STR_1,
&&TARGET_CALL_NO_KW_TUPLE_1,
+ &&TARGET_CALL_NO_KW_TYPE_1,
&&TARGET_WITH_EXCEPT_START,
&&TARGET_GET_AITER,
&&TARGET_GET_ANEXT,
@@ -55,38 +55,38 @@ static void *opcode_targets[256] = {
&&TARGET_BEFORE_WITH,
&&TARGET_END_ASYNC_FOR,
&&TARGET_CLEANUP_THROW,
- &&TARGET_CALL_NO_KW_TYPE_1,
&&TARGET_COMPARE_AND_BRANCH_FLOAT,
&&TARGET_COMPARE_AND_BRANCH_INT,
&&TARGET_COMPARE_AND_BRANCH_STR,
+ &&TARGET_FOR_ITER_LIST,
&&TARGET_STORE_SUBSCR,
&&TARGET_DELETE_SUBSCR,
- &&TARGET_FOR_ITER_LIST,
&&TARGET_FOR_ITER_TUPLE,
&&TARGET_FOR_ITER_RANGE,
&&TARGET_FOR_ITER_GEN,
&&TARGET_LOAD_ATTR_CLASS,
&&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
+ &&TARGET_LOAD_ATTR_INSTANCE_VALUE,
&&TARGET_GET_ITER,
&&TARGET_GET_YIELD_FROM_ITER,
- &&TARGET_LOAD_ATTR_INSTANCE_VALUE,
- &&TARGET_LOAD_BUILD_CLASS,
&&TARGET_LOAD_ATTR_MODULE,
+ &&TARGET_LOAD_BUILD_CLASS,
&&TARGET_LOAD_ATTR_PROPERTY,
+ &&TARGET_LOAD_ATTR_SLOT,
&&TARGET_LOAD_ASSERTION_ERROR,
&&TARGET_RETURN_GENERATOR,
- &&TARGET_LOAD_ATTR_SLOT,
&&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LOAD_ATTR_METHOD_LAZY_DICT,
&&TARGET_LOAD_ATTR_METHOD_NO_DICT,
&&TARGET_LOAD_ATTR_METHOD_WITH_VALUES,
&&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_LOAD_FAST__LOAD_CONST,
- &&TARGET_RETURN_VALUE,
&&TARGET_LOAD_FAST__LOAD_FAST,
- &&TARGET_SETUP_ANNOTATIONS,
+ &&TARGET_RETURN_VALUE,
&&TARGET_LOAD_GLOBAL_BUILTIN,
+ &&TARGET_SETUP_ANNOTATIONS,
&&TARGET_LOAD_GLOBAL_MODULE,
+ &&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_PREP_RERAISE_STAR,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
@@ -112,7 +112,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_FORWARD,
&&TARGET_JUMP_IF_FALSE_OR_POP,
&&TARGET_JUMP_IF_TRUE_OR_POP,
- &&TARGET_STORE_ATTR_INSTANCE_VALUE,
+ &&TARGET_STORE_ATTR_SLOT,
&&TARGET_POP_JUMP_IF_FALSE,
&&TARGET_POP_JUMP_IF_TRUE,
&&TARGET_LOAD_GLOBAL,
@@ -142,7 +142,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_BACKWARD,
&&TARGET_COMPARE_AND_BRANCH,
&&TARGET_CALL_FUNCTION_EX,
- &&TARGET_STORE_ATTR_SLOT,
+ &&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
&&TARGET_SET_ADD,
@@ -152,24 +152,24 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE,
&&TARGET_RESUME,
&&TARGET_MATCH_CLASS,
- &&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_STORE_FAST__LOAD_FAST,
+ &&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING,
- &&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_STORE_SUBSCR_DICT,
&&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_UNPACK_SEQUENCE_LIST,
+ &&TARGET_UNPACK_SEQUENCE_TUPLE,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
- &&TARGET_UNPACK_SEQUENCE_TUPLE,
&&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
+ &&_unknown_opcode,
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_CALL_INTRINSIC_1,
diff --git a/Python/tier2.c b/Python/tier2.c
index a5a2ebc569eb0d..80a51c6db7d193 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1,4 +1,5 @@
#include "Python.h"
+#include "stdlib.h"
#include "pycore_code.h"
#include "pycore_frame.h"
@@ -18,6 +19,7 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// If it fails, due to lack of memory or whatever,
// just fall back to the tier 1 interpreter.
_Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
+ return next_instr;
if (next != NULL) {
return next;
}
@@ -26,7 +28,14 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
return next_instr;
}
+// Gets end of the bytecode for a code object.
+_Py_CODEUNIT *
+_PyCode_GetEnd(PyCodeObject *co)
+{
+ return (_Py_CODEUNIT *)(co->co_code_adaptive + _PyCode_NBYTES(co));
+}
+// Creates the overallocated array for the BBs.
static _PyTier2BBSpace *
_PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
{
@@ -36,36 +45,190 @@ _PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
}
bb_space->next = NULL;
bb_space->water_level = 0;
- bb_space->max_capacity = space_to_alloc - sizeof(_PyTier2BB);
+ assert((int)(space_to_alloc - sizeof(_PyTier2BB)) == (space_to_alloc - sizeof(_PyTier2BB)));
+ bb_space->max_capacity = (int)(space_to_alloc - sizeof(_PyTier2BB));
return bb_space;
}
/* Init a BB in BB space without any checks for waterlevel. */
static _PyTier2BB *
-_PyTier2_InitBBNoCheck(_PyTier2BBSpace *bb_space, _Py_CODEUNIT *tier1_start,
- const void *instr_bytes_src, Py_ssize_t instr_nbytes)
+_PyTier2_InitBBNoCheck(_PyTier2BBSpace *bb_space, _Py_CODEUNIT *tier1_end,
+ _Py_CODEUNIT *instr_start, _Py_CODEUNIT *instr_end)
{
+ Py_ssize_t nbytes = (instr_end - instr_start) * sizeof(_Py_CODEUNIT);
_PyTier2BB *bb_ptr = &bb_space->bbs[bb_space->water_level];
- bb_ptr->tier1_start = tier1_start;
- memcpy(bb_ptr->u_code, instr_bytes_src, instr_nbytes);
- assert(bb_space->water_level + instr_nbytes == (int)(bb_space->water_level + instr_nbytes));
- bb_space->water_level += instr_nbytes;
+ bb_ptr->tier1_end = tier1_end;
+ memcpy(bb_ptr->u_code, (const void *)instr_start, nbytes);
+ assert(bb_space->water_level + nbytes == (int)nbytes);
+ bb_space->water_level += (int)nbytes;
return bb_ptr;
}
+
+/* Opcode detection functions. Keep in sync with compile.c and dis! */
+
+// dis.hasjabs
+static inline int
+IS_JABS_OPCODE(int opcode)
+{
+ return 0;
+}
+
+// dis.hasjrel
+static inline int
+IS_JREL_OPCODE(int opcode)
+{
+ switch (opcode) {
+ case FOR_ITER:
+ case JUMP_FORWARD:
+ case JUMP_IF_FALSE_OR_POP:
+ case JUMP_IF_TRUE_OR_POP:
+ case POP_JUMP_IF_FALSE:
+ case POP_JUMP_IF_TRUE:
+ case SEND:
+ case POP_JUMP_IF_NOT_NONE:
+ case POP_JUMP_IF_NONE:
+ case JUMP_BACKWARD_NO_INTERRUPT:
+ case JUMP_BACKWARD:
+ return 1;
+ default:
+ return 0;
+
+ }
+}
+
+// dis.hasjrel || dis.hasjabs
+static inline int
+IS_JUMP_OPCODE(int opcode)
+{
+ return IS_JREL_OPCODE(opcode) || IS_JABS_OPCODE(opcode);
+}
+
+
+static inline int
+IS_SCOPE_EXIT_OPCODE(int opcode)
+{
+ switch (opcode) {
+ case RETURN_VALUE:
+ case RAISE_VARARGS:
+ case RERAISE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+// KEEP IN SYNC WITH COMPILE.c!!!!
+static int
+IS_TERMINATOR_OPCODE(int opcode)
+{
+ return IS_JUMP_OPCODE(opcode) || IS_SCOPE_EXIT_OPCODE(opcode);
+}
+
+static int
+compare_ints(const void *a, const void *b)
+{
+ const int a_num = *(int *)a;
+ const int b_num = *(int *)b;
+ return a_num - b_num;
+}
+
+// Returns 1 on error, 0 on success. Populates the jump target offset
+// array for a code object.
+static int
+_PyCode_Tier2FillJumpTargets(PyCodeObject *co)
+{
+ // Remove all the RESUME instructions.
+ // Count all the jump targets.
+ Py_ssize_t jump_target_count = 0;
+ for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
+ _Py_CODEUNIT *instr_ptr = _PyCode_CODE(co) + i;
+ _Py_CODEUNIT instr = *instr_ptr;
+ switch (_Py_OPCODE(instr)) {
+ case RESUME:
+ _py_set_opcode(instr_ptr, RESUME_QUICK);
+ break;
+ default:
+ jump_target_count += IS_JUMP_OPCODE(_Py_OPCODE(instr));
+ }
+ }
+
+ // Find all the jump target instructions
+ _Py_CODEUNIT *end = _PyCode_GetEnd(co);
+ _Py_CODEUNIT *start = _PyCode_CODE(co);
+ _Py_CODEUNIT *curr = start;
+
+ // Impossibly big.
+ if (jump_target_count != (int)jump_target_count) {
+ return 1;
+ }
+ // Impossibly big
+ if (end - start != (int)(end - start)) {
+ return 1;
+ }
+ co->_jump_target_count = (int)jump_target_count;
+ int *jump_targets = PyMem_Malloc(jump_target_count * sizeof(int));
+ if (jump_targets == NULL) {
+ return 1;
+ }
+ int curr_i = 0;
+ while (curr <= end) {
+ _Py_CODEUNIT instr = *curr;
+ if (IS_JUMP_OPCODE(_Py_OPCODE(instr))) {
+ _Py_CODEUNIT *target = curr + _Py_OPARG(instr);
+ // (in terms of offset from start of co_code_adaptive)
+ jump_targets[curr_i] = (int)(target - start);
+ curr_i++;
+ }
+ curr++;
+ }
+ qsort(jump_targets, jump_target_count, sizeof(int), compare_ints);
+ co->_jump_targets = jump_targets;
+ return 0;
+}
+
+// Detects a BB from the current instruction start to the end of the code object.
+_Py_CODEUNIT *
+_PyTier2_Code_DetectBB(PyCodeObject *co, _Py_CODEUNIT *start)
+{
+ // There are only two cases that a BB ends.
+ // 1. If there's a branch instruction / scope exit.
+ // 2. If the instruction is a jump target.
+ _Py_CODEUNIT *instr_end = _PyCode_GetEnd(co);
+ _Py_CODEUNIT *curr = start;
+ int *jump_target_offsets = co->_jump_targets;
+ int jump_target_count = co->_jump_target_count;
+ int curr_jump = 0;
+ while (curr < instr_end) {
+ _Py_CODEUNIT instr = *curr;
+ int opcode = _Py_OPCODE(instr);
+ if (IS_JUMP_OPCODE(opcode) || IS_SCOPE_EXIT_OPCODE(opcode)) {
+ return curr;
+ }
+ while (_PyCode_CODE(co) + jump_target_offsets[curr_jump] < curr) {
+ curr_jump++;
+ }
+ if (_PyCode_CODE(co) + jump_target_offsets[curr_jump] == curr) {
+ return curr;
+ }
+ curr++;
+ }
+ return instr_end;
+}
+
static _Py_CODEUNIT *
_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
int curr = _Py_OPCODE(*(next_instr - 1));
- assert(curr == RESUME || curr == JUMP_BACKWARD);
+ assert(curr == RESUME);
PyCodeObject *co = frame->f_code;
assert(co->_bb_space == NULL);
+ // 0. Single pass: mark jump targets
// 1. Initialize basic blocks space.
- // 2. Copy over instructions to basic blocks.
- // (For now, just have one basic block = on code object)
- // @TODO split up code object into basic blocks.
- // 3. Set the instruction pointer to correct one.
+ // 2. Create the entry BB (if it is, else the current BB).
+ // 3. Jump into that BB.
fprintf(stderr, "INITIALIZING %ld\n", Py_SIZE(co));
+
Py_ssize_t space_to_alloc = (sizeof(_PyTier2BB) + _PyCode_NBYTES(co)) * 2;
_PyTier2BBSpace *bb_space = _PyTier2_CreateBBSpace(space_to_alloc);
@@ -75,47 +238,45 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
co->_bb_space = bb_space;
- _PyTier2BB *bb_ptr = _PyTier2_InitBBNoCheck(bb_space, _PyCode_CODE(co),
- _PyCode_CODE(co), _PyCode_NBYTES(co));
+ //_Py_CODEUNIT *entry_bb_end = _PyTier2_Code_DetectBB(co, _PyCode_CODE(co));
+ _Py_CODEUNIT *entry_bb_end = _PyCode_GetEnd(co);
- // Remove all the RESUME and JUMP_BACKWARDS instructions
- for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
- _Py_CODEUNIT instr = bb_ptr->u_code[i];
- switch (_Py_OPCODE(instr)) {
- case RESUME:
- _py_set_opcode(&(bb_ptr->u_code[i]), RESUME_QUICK);
- break;
- case JUMP_BACKWARD:
- _py_set_opcode(&(bb_ptr->u_code[i]), JUMP_BACKWARD);
- break;
- }
+ _PyTier2BB *bb_ptr = _PyTier2_InitBBNoCheck(bb_space,
+ entry_bb_end,
+ _PyCode_CODE(co),
+ entry_bb_end);
+ if (_PyCode_Tier2FillJumpTargets(co)) {
+ goto cleanup;
}
-
- co->_bb_next = bb_ptr;
+
+ co->_entry_bb = bb_ptr;
// Set the instruction pointer to the next one in the bb
Py_ssize_t offset_from_start = (frame->prev_instr - _PyCode_CODE(co));
assert(offset_from_start >= -1);
frame->prev_instr = bb_ptr->u_code + offset_from_start;
- // test to see we are working
// _py_set_opcode(next_instr, CACHE);
return bb_ptr->u_code + (next_instr - _PyCode_CODE(co));
+
+cleanup:
+ PyMem_Free(bb_space);
+ return NULL;
}
-/* Allocates and initializes a new basic block. If not enough space in
+/* Allocates and initializes a new basic block. If there's not enough space in
the overallocated array, create a new array.
Make sure to call _PyCode_Tier2Initialize before this!
*/
static _PyTier2BB *
-_PyCode_Tier2BBNew(PyCodeObject *co, _Py_CODEUNIT *tier1_start, _Py_CODEUNIT *instr, Py_ssize_t code_size)
+_PyCode_Tier2BBNew(PyCodeObject *co, _Py_CODEUNIT *instr_start, _Py_CODEUNIT *instr_end)
{
assert(co->_bb_space != NULL);
_PyTier2BBSpace *bb_space = co->_bb_space;
- Py_ssize_t amount_to_alloc = code_size + sizeof(_PyTier2BB);
+ Py_ssize_t amount_to_alloc = (instr_start - instr_end) * sizeof(_Py_CODEUNIT *) + sizeof(_PyTier2BB);
assert(bb_space->water_level + amount_to_alloc == (int)(bb_space->water_level + amount_to_alloc));
// Need to allocate a new array.
@@ -129,5 +290,5 @@ _PyCode_Tier2BBNew(PyCodeObject *co, _Py_CODEUNIT *tier1_start, _Py_CODEUNIT *in
co->_bb_space = next_bb_space;
bb_space = next_bb_space;
}
- return _PyTier2_InitBBNoCheck(bb_space, tier1_start, instr, code_size * sizeof(_Py_CODEUNIT));
+ return _PyTier2_InitBBNoCheck(bb_space, _PyCode_GetEnd(co), instr_start, instr_end);
}
diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py
index 7718bc95722138..88682395abf8c1 100644
--- a/Tools/build/deepfreeze.py
+++ b/Tools/build/deepfreeze.py
@@ -278,8 +278,10 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
self.write(f"._co_cached = NULL,")
self.write("._co_linearray = NULL,")
self.write("._tier2_warmup = -64,")
- self.write("._bb_next = NULL,")
+ self.write("._entry_bb = NULL,")
self.write("._bb_space = NULL,")
+ self.write("._jump_target_count = 0,")
+ self.write("._jump_targets = NULL,")
self.write(f".co_code_adaptive = {co_code_adaptive},")
for i, op in enumerate(code.co_code[::2]):
if op == RESUME:
From 3a796b5dd88b758a628c50a85c0e6ae749559a62 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 10 Feb 2023 21:58:30 +0800
Subject: [PATCH 021/280] Lazily allocate tier 2 information
---
Include/cpython/code.h | 14 ++++---
Lib/importlib/_bootstrap_external.py | 2 +-
Objects/codeobject.c | 61 +++++++++++++---------------
Python/bytecodes.c | 2 +-
Python/generated_cases.c.h | 6 +--
Python/tier2.c | 29 ++++++++-----
Tools/build/deepfreeze.py | 6 +--
7 files changed, 63 insertions(+), 57 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 9d6c29eee27a7e..73d529b6f3dca0 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -61,6 +61,14 @@ typedef struct _PyTier2BBSpace {
_PyTier2BB bbs[1];
} _PyTier2BBSpace;
+typedef struct _PyTier2Info {
+ _PyTier2BB *_entry_bb; /* the tier 2 basic block to execute (if any) */
+ _PyTier2BBSpace *_bb_space; /* linked list storing basic blocks */
+ int _jump_target_count; /* Number of entries in _jump_targets */
+ /* sorted ascending offsets (from start of co_code_adaptive) for jump targets */
+ int *_jump_targets;
+} _PyTier2Info;
+
// To avoid repeating ourselves in deepfreeze.py, all PyCodeObject members are
// defined in this macro:
#define _PyCode_DEF(SIZE) { \
@@ -122,11 +130,7 @@ typedef struct _PyTier2BBSpace {
int _co_firsttraceable; /* index of first traceable instruction */ \
char *_co_linearray; /* array of line offsets */ \
int _tier2_warmup; /* warmup counter for tier 2 */ \
- _PyTier2BB *_entry_bb; /* the tier 2 basic block to execute (if any) */ \
- _PyTier2BBSpace *_bb_space; /* linked list storing basic blocks */ \
- int _jump_target_count; /* Number of entries in _jump_targets */ \
- /* sorted ascending offsets (from start of co_code_adaptive) for jump targets */ \
- int *_jump_targets; \
+ _PyTier2Info *_tier2_info; /* info required for tier 2, lazily alloc */ \
/* Scratch space for extra data relating to the code object. \
Type is a void* to keep the format private in codeobject.c to force \
people to go through the proper APIs. */ \
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index 933c8c7d7e0590..33fad0ad9a1cab 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -444,7 +444,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3518).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3519).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index db3a7b6970bd31..6c4ae5b0b190db 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -410,10 +410,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
co->_co_linearray_entry_size = 0;
co->_co_linearray = NULL;
co->_tier2_warmup = -64;
- co->_entry_bb = NULL;
- co->_bb_space = NULL;
- co->_jump_target_count = 0;
- co->_jump_targets = NULL;
+ co->_tier2_info = NULL;
memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code),
PyBytes_GET_SIZE(con->code));
int entry_point = 0;
@@ -1668,6 +1665,30 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount,
return co;
}
+static void
+code_tier2_fini(PyCodeObject *co)
+{
+ if (co->_tier2_info == NULL) {
+ return;
+ }
+ _PyTier2Info *t2_info = co->_tier2_info;
+ t2_info->_entry_bb = NULL;
+ t2_info->_jump_target_count = 0;
+ if (t2_info->_bb_space != NULL) {
+ // Traverse the linked list
+ for (_PyTier2BBSpace *curr = t2_info->_bb_space; curr != NULL;) {
+ _PyTier2BBSpace *prev = curr;
+ curr = curr->next;
+ PyMem_Free(prev);
+ }
+ t2_info->_bb_space = NULL;
+ }
+ if (t2_info->_jump_targets != NULL) {
+ PyMem_Free(t2_info->_jump_targets);
+ t2_info->_jump_targets = NULL;
+ }
+}
+
static void
code_dealloc(PyCodeObject *co)
{
@@ -1710,20 +1731,8 @@ code_dealloc(PyCodeObject *co)
if (co->_co_linearray) {
PyMem_Free(co->_co_linearray);
}
- co->_entry_bb = NULL;
- co->_jump_target_count = 0;
- if (co->_bb_space != NULL) {
- // Traverse the linked list
- for (_PyTier2BBSpace *curr = co->_bb_space; curr != NULL;) {
- _PyTier2BBSpace *prev = curr;
- curr = curr->next;
- PyMem_Free(prev);
- }
- co->_bb_space = NULL;
- }
- if (co->_jump_targets != NULL) {
- PyMem_Free(co->_jump_targets);
- }
+ code_tier2_fini(co);
+ co->_tier2_info = NULL;
PyObject_Free(co);
}
@@ -2301,20 +2310,8 @@ _PyStaticCode_Fini(PyCodeObject *co)
PyMem_Free(co->_co_cached);
co->_co_cached = NULL;
}
- co->_entry_bb = NULL;
- co->_jump_target_count = 0;
- if (co->_bb_space != NULL) {
- // Traverse the linked list
- for (_PyTier2BBSpace *curr = co->_bb_space; curr != NULL;) {
- _PyTier2BBSpace *prev = curr;
- curr = curr->next;
- PyMem_Free(prev);
- }
- co->_bb_space = NULL;
- }
- if (co->_jump_targets != NULL) {
- PyMem_Free(co->_jump_targets);
- }
+ code_tier2_fini(co);
+ co->_tier2_info = NULL;
co->co_extra = NULL;
if (co->co_weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject *)co);
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 200708b67a46ac..3bf1bf3f18dbf4 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -296,7 +296,7 @@ dummy_func(
U_INST(BINARY_OP_ADD_INT_REST);
}
- u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (left, right-- left : PyLong_Type, right: PyLong_Type)) {
+ u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (left, right -- left : PyLong_Type, right: PyLong_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index a93b444476b450..d0bde190576225 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -23,9 +23,9 @@
}
TARGET(RESUME) {
- if (cframe.use_tracing == 0) {
- next_instr = _PyCode_Tier2Warmup(frame, next_instr);
- }
+ //if (cframe.use_tracing == 0) {
+ // next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ //}
GO_TO_INSTRUCTION(RESUME_QUICK);
}
diff --git a/Python/tier2.c b/Python/tier2.c
index 80a51c6db7d193..968db9c90f84fd 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -138,6 +138,7 @@ compare_ints(const void *a, const void *b)
static int
_PyCode_Tier2FillJumpTargets(PyCodeObject *co)
{
+ assert(co->_tier2_info != NULL);
// Remove all the RESUME instructions.
// Count all the jump targets.
Py_ssize_t jump_target_count = 0;
@@ -166,7 +167,7 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
if (end - start != (int)(end - start)) {
return 1;
}
- co->_jump_target_count = (int)jump_target_count;
+ co->_tier2_info->_jump_target_count = (int)jump_target_count;
int *jump_targets = PyMem_Malloc(jump_target_count * sizeof(int));
if (jump_targets == NULL) {
return 1;
@@ -183,7 +184,7 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
curr++;
}
qsort(jump_targets, jump_target_count, sizeof(int), compare_ints);
- co->_jump_targets = jump_targets;
+ co->_tier2_info->_jump_targets = jump_targets;
return 0;
}
@@ -191,13 +192,14 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
_Py_CODEUNIT *
_PyTier2_Code_DetectBB(PyCodeObject *co, _Py_CODEUNIT *start)
{
+ assert(co->_tier2_info != NULL);
// There are only two cases that a BB ends.
// 1. If there's a branch instruction / scope exit.
// 2. If the instruction is a jump target.
_Py_CODEUNIT *instr_end = _PyCode_GetEnd(co);
_Py_CODEUNIT *curr = start;
- int *jump_target_offsets = co->_jump_targets;
- int jump_target_count = co->_jump_target_count;
+ int *jump_target_offsets = co->_tier2_info->_jump_targets;
+ int jump_target_count = co->_tier2_info->_jump_target_count;
int curr_jump = 0;
while (curr < instr_end) {
_Py_CODEUNIT instr = *curr;
@@ -222,7 +224,12 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
int curr = _Py_OPCODE(*(next_instr - 1));
assert(curr == RESUME);
PyCodeObject *co = frame->f_code;
- assert(co->_bb_space == NULL);
+ assert(co->_tier2_info == NULL);
+ _PyTier2Info *t2_info = PyMem_Malloc(sizeof(_PyTier2Info));
+ if (t2_info == NULL) {
+ return NULL;
+ }
+ co->_tier2_info = t2_info;
// 0. Single pass: mark jump targets
// 1. Initialize basic blocks space.
// 2. Create the entry BB (if it is, else the current BB).
@@ -233,10 +240,11 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
_PyTier2BBSpace *bb_space = _PyTier2_CreateBBSpace(space_to_alloc);
if (bb_space == NULL) {
+ PyMem_Free(t2_info);
return NULL;
}
- co->_bb_space = bb_space;
+ t2_info->_bb_space = bb_space;
//_Py_CODEUNIT *entry_bb_end = _PyTier2_Code_DetectBB(co, _PyCode_CODE(co));
_Py_CODEUNIT *entry_bb_end = _PyCode_GetEnd(co);
@@ -251,7 +259,7 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
}
- co->_entry_bb = bb_ptr;
+ t2_info->_entry_bb = bb_ptr;
// Set the instruction pointer to the next one in the bb
Py_ssize_t offset_from_start = (frame->prev_instr - _PyCode_CODE(co));
@@ -273,9 +281,10 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
static _PyTier2BB *
_PyCode_Tier2BBNew(PyCodeObject *co, _Py_CODEUNIT *instr_start, _Py_CODEUNIT *instr_end)
{
- assert(co->_bb_space != NULL);
+ assert(co->_tier2_info != NULL);
+ assert(co->_tier2_info->_bb_space != NULL);
- _PyTier2BBSpace *bb_space = co->_bb_space;
+ _PyTier2BBSpace *bb_space = co->_tier2_info->_bb_space;
Py_ssize_t amount_to_alloc = (instr_start - instr_end) * sizeof(_Py_CODEUNIT *) + sizeof(_PyTier2BB);
assert(bb_space->water_level + amount_to_alloc == (int)(bb_space->water_level + amount_to_alloc));
@@ -287,7 +296,7 @@ _PyCode_Tier2BBNew(PyCodeObject *co, _Py_CODEUNIT *instr_start, _Py_CODEUNIT *in
}
next_bb_space->next = bb_space;
// We want to make our bb_space point to the most recent one to get O(1) BB allocations.
- co->_bb_space = next_bb_space;
+ co->_tier2_info->_bb_space = next_bb_space;
bb_space = next_bb_space;
}
return _PyTier2_InitBBNoCheck(bb_space, _PyCode_GetEnd(co), instr_start, instr_end);
diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py
index 88682395abf8c1..57e0fdb05bfd64 100644
--- a/Tools/build/deepfreeze.py
+++ b/Tools/build/deepfreeze.py
@@ -278,11 +278,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
self.write(f"._co_cached = NULL,")
self.write("._co_linearray = NULL,")
self.write("._tier2_warmup = -64,")
- self.write("._entry_bb = NULL,")
- self.write("._bb_space = NULL,")
- self.write("._jump_target_count = 0,")
- self.write("._jump_targets = NULL,")
- self.write(f".co_code_adaptive = {co_code_adaptive},")
+ self.write("._tier2_info = NULL,")
for i, op in enumerate(code.co_code[::2]):
if op == RESUME:
self.write(f"._co_firsttraceable = {i},")
From 1aab16d4805ca56d22a9a3eae9494290376f8ccf Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 10 Feb 2023 22:00:29 +0800
Subject: [PATCH 022/280] try fix bug
---
Include/internal/pycore_frame.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index ce87e2a428d8f0..238eab5915ac4d 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -122,7 +122,7 @@ _PyFrame_Initialize(
frame->stacktop = code->co_nlocalsplus;
frame->frame_obj = NULL;
// @TODO CHANGE ME
- frame->prev_instr = _PyCode_CODE(code);
+ frame->prev_instr = _PyCode_CODE(code) - 1;
frame->yield_offset = 0;
frame->owner = FRAME_OWNED_BY_THREAD;
From b0466bd47e0a055e4363ffba67edeb14d71ef5a2 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 10 Feb 2023 22:04:38 +0800
Subject: [PATCH 023/280] fix a bug for real this time
---
Tools/build/deepfreeze.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py
index 57e0fdb05bfd64..6718973702cdf0 100644
--- a/Tools/build/deepfreeze.py
+++ b/Tools/build/deepfreeze.py
@@ -279,6 +279,7 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
self.write("._co_linearray = NULL,")
self.write("._tier2_warmup = -64,")
self.write("._tier2_info = NULL,")
+ self.write(f".co_code_adaptive = {co_code_adaptive},")
for i, op in enumerate(code.co_code[::2]):
if op == RESUME:
self.write(f"._co_firsttraceable = {i},")
From 59a07ddca90bdce946f77af4cb8cd300c967558b Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sun, 12 Feb 2023 23:28:23 +0800
Subject: [PATCH 024/280] Part 1: Drive execution of BBs lazily
---
Include/cpython/code.h | 43 +-
Include/internal/pycore_interp.h | 6 +
Include/internal/pycore_opcode.h | 25 +-
.../internal/pycore_opcode_macro_to_micro.h | 6 +-
Include/opcode.h | 11 +-
Lib/opcode.py | 25 +-
Objects/codeobject.c | 25 +-
Python/bytecodes.c | 4 +-
Python/generated_cases.c.h | 12 +-
Python/opcode_metadata.h | 6 +-
Python/pystate.c | 4 +
Python/tier2.c | 708 +++++++++++++++---
12 files changed, 729 insertions(+), 146 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 73d529b6f3dca0..4fd9705b9dff0f 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -43,30 +43,61 @@ typedef struct {
} _PyCoCached;
+//// Used to store intermediate code information for the tier 2 "translator".
+//// This is eventually finally converted to _PyTier2BB s.
+//// Code Object -> _PyTier2IntermediateCode -> _PyTier2BB s.
+//typedef struct _PyTier2IntermediateCode {
+// /* Number of entries in _jump_targets */
+// int _jump_target_count;
+// /* sorted ascending offsets (from start of field code) for jump targets */
+// // The offsets are in number of _Py_CODEUNITs
+// int *_jump_targets;
+// int n_instrs;
+// _Py_CODEUNIT code[1];
+//} _PyTier2IntermediateCode;
+
+
// What the tier 2 interpreter executes
typedef struct _PyTier2BB {
- struct _PyTier2BB *successor_bb;
- // Stores the end pointer in the tier 1 bytecode.
- // So that when we exit the BB we can calculate where to return.
+ _Py_CODEUNIT *successor_bb;
+ // The other BB to go should BB_BRANCH fail.
+ _Py_CODEUNIT *alternate_bb;
+ // Array of types. This corresponds to the fast locals array.
+ int type_context_len;
+ PyTypeObject **type_context;
+ //// Stores the end pointer in the intermediate bytecode.
+ //// So that when we hit BB_BRANCH, we know what's the next
+ //// thing to generate.
+ //_Py_CODEUNIT *intermediate_code_end;
_Py_CODEUNIT *tier1_end;
+ // There's extra memory at the end of this.
+ int n_instrs;
_Py_CODEUNIT u_code[1];
} _PyTier2BB;
+// Find the start of the BB from u_code.
+#define _PyTier2BB_FROM_UCODE(code) (_PyTier2BB *)(((char *)code) - offsetof(_PyTier2BB, u_code))
+
// Bump allocator for basic blocks (overallocated)
typedef struct _PyTier2BBSpace {
struct _PyTier2BBSpace *next;
int max_capacity;
// How much space has been consumed in bbs.
int water_level;
+ // There's extra memory at the end of this.
_PyTier2BB bbs[1];
} _PyTier2BBSpace;
+// Tier 2 info stored in the code object. Lazily allocated.
typedef struct _PyTier2Info {
_PyTier2BB *_entry_bb; /* the tier 2 basic block to execute (if any) */
_PyTier2BBSpace *_bb_space; /* linked list storing basic blocks */
- int _jump_target_count; /* Number of entries in _jump_targets */
- /* sorted ascending offsets (from start of co_code_adaptive) for jump targets */
- int *_jump_targets;
+ //_PyTier2IntermediateCode *i_code; /* intermediate bytecode to generate tier 2 basic blocks */
+ // Keeps track of offset of jump targets (in number of codeunits)
+ // from co_code_adaptive.
+ int jump_target_count;
+ int *jump_targets;
+ PyTypeObject **types_stack;
} _PyTier2Info;
// To avoid repeating ourselves in deepfreeze.py, all PyCodeObject members are
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 0e3d46852f2e6d..371e9071de67af 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -186,6 +186,12 @@ struct _is {
struct _Py_interp_cached_objects cached_objects;
struct _Py_interp_static_objects static_objects;
+ // TIER 2 INTERPRETER INFORMATION
+
+ // Bytecode scratchspace when emitting tier 2 bytecode.
+ int tier2_bytecode_scratchsize;
+ _Py_CODEUNIT *tier2_bytecode_scratch;
+
/* The following fields are here to avoid allocation during init.
The data is exposed through PyInterpreterState pointer fields.
These fields should not be accessed directly outside of init.
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index eee13ed8962825..7e7908d543683b 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -397,18 +397,18 @@ static const char *const _PyOpcode_OpName[263] = {
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
- [BINARY_OP_ADD_INT_TYPE_CHECK] = "BINARY_OP_ADD_INT_TYPE_CHECK",
- [BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
- [169] = "<169>",
- [170] = "<170>",
+ [BB_ENTER_FRAME] = "BB_ENTER_FRAME",
+ [BB_EXIT_FRAME] = "BB_EXIT_FRAME",
+ [BB_TYPE_BRANCH] = "BB_TYPE_BRANCH",
+ [BB_ITER] = "BB_ITER",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
[CALL_INTRINSIC_1] = "CALL_INTRINSIC_1",
- [174] = "<174>",
- [175] = "<175>",
- [176] = "<176>",
- [177] = "<177>",
- [178] = "<178>",
+ [BB_BRANCH_OR_POP] = "BB_BRANCH_OR_POP",
+ [BB_POP_THEN_BRANCH] = "BB_POP_THEN_BRANCH",
+ [BB_POP_BRANCH] = "BB_POP_BRANCH",
+ [BINARY_CHECK_INT] = "BINARY_CHECK_INT",
+ [BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
[179] = "<179>",
[180] = "<180>",
[181] = "<181>",
@@ -498,13 +498,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 169: \
- case 170: \
- case 174: \
- case 175: \
- case 176: \
- case 177: \
- case 178: \
case 179: \
case 180: \
case 181: \
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index bffa49d3ceefcb..ae2cc505738362 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -35,7 +35,7 @@ extern const int _Py_MacroOpUOpCount[] = {
[BINARY_OP_INPLACE_ADD_UNICODE] = 1,
[BINARY_OP_ADD_FLOAT] = 1,
[BINARY_OP_ADD_INT] = 2,
-[BINARY_OP_ADD_INT_TYPE_CHECK] = 1,
+[BINARY_CHECK_INT] = 1,
[BINARY_OP_ADD_INT_REST] = 1,
[BINARY_SUBSCR] = 1,
[BINARY_SLICE] = 1,
@@ -183,10 +183,10 @@ extern const int _Py_MacroOpUOpCount[] = {
};
extern const int _Py_MacroOpToUOp[][2] = {
-[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_TYPE_CHECK, BINARY_OP_ADD_INT_REST},
+[BINARY_OP_ADD_INT] = {BINARY_CHECK_INT, BINARY_OP_ADD_INT_REST},
};
extern const PyTypeObject *_Py_UOpGuardTypes[][2] = {
-[BINARY_OP_ADD_INT_TYPE_CHECK] = {&PyLong_Type, &PyLong_Type},
+[BINARY_CHECK_INT] = {&PyLong_Type, &PyLong_Type},
};
#ifdef __cplusplus
}
diff --git a/Include/opcode.h b/Include/opcode.h
index b89441dc98e835..d8569fe97b31b9 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -253,8 +253,15 @@ extern "C" {
#define UNPACK_SEQUENCE_TUPLE 161
#define UNPACK_SEQUENCE_TWO_TUPLE 166
#define DO_TRACING 255
-#define BINARY_OP_ADD_INT_TYPE_CHECK 167
-#define BINARY_OP_ADD_INT_REST 168
+#define BB_ENTER_FRAME 167
+#define BB_EXIT_FRAME 168
+#define BB_TYPE_BRANCH 169
+#define BB_ITER 170
+#define BB_BRANCH_OR_POP 174
+#define BB_POP_THEN_BRANCH 175
+#define BB_POP_BRANCH 176
+#define BINARY_CHECK_INT 177
+#define BINARY_OP_ADD_INT_REST 178
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index a12b6568dace4d..be1c0dbf8fafe9 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -442,6 +442,29 @@ def pseudo_op(name, op, real_ops):
'BINARY_OP_ADD_INT',
]
_uops = [
- 'BINARY_OP_ADD_INT_TYPE_CHECK',
+ # Tier 2 BB opcodes
+ 'BB_ENTER_FRAME',
+ 'BB_EXIT_FRAME',
+ ## These branches correspond to the jump instructions
+ 'BB_TYPE_BRANCH',
+ 'BB_ITER', # FOR_ITER
+ 'BB_BRANCH_OR_POP', # JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP
+ 'BB_POP_THEN_BRANCH', # POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE
+ 'BB_POP_BRANCH', # POP_JUMP_IF_NOT_NONE, POP_JUMP_IF_NONE
+
+ # Common type checks
+ # These instructions check that one operand is a certain type.
+ # Their oparg is the offset from TOS to read.
+ # 'UNARY_CHECK_INT',
+ # 'UNARY_CHECK_FLOAT',
+ # 'UNARY_CHECK_STR',
+
+ # These instructions check that both operands are a certain type.
+ # The benefit is that they save some dispatch overhead versus the
+ # single operand forms.
+ 'BINARY_CHECK_INT',
+ # 'BINARY_CHECK_FLOAT',
+ # 'BINARY_CHECK_STR',
+ # BINARY_OP_ADD_INT,
'BINARY_OP_ADD_INT_REST',
]
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 6c4ae5b0b190db..bdc31b6216304f 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -1673,7 +1673,6 @@ code_tier2_fini(PyCodeObject *co)
}
_PyTier2Info *t2_info = co->_tier2_info;
t2_info->_entry_bb = NULL;
- t2_info->_jump_target_count = 0;
if (t2_info->_bb_space != NULL) {
// Traverse the linked list
for (_PyTier2BBSpace *curr = t2_info->_bb_space; curr != NULL;) {
@@ -1683,10 +1682,26 @@ code_tier2_fini(PyCodeObject *co)
}
t2_info->_bb_space = NULL;
}
- if (t2_info->_jump_targets != NULL) {
- PyMem_Free(t2_info->_jump_targets);
- t2_info->_jump_targets = NULL;
- }
+
+ if (t2_info->jump_target_count > 0 &&
+ t2_info->jump_targets != NULL) {
+ PyMem_Free(t2_info->jump_targets);
+ t2_info->jump_targets = NULL;
+ }
+ if (t2_info->types_stack != NULL) {
+ PyMem_Free(t2_info->types_stack);
+ t2_info->types_stack = NULL;
+ }
+ t2_info->jump_target_count = 0;
+ PyMem_Free(t2_info);
+ //if (t2_info->i_code != NULL) {
+ // if (t2_info->i_code->_jump_targets != NULL) {
+ // PyMem_Free(t2_info->i_code->_jump_targets);
+ // t2_info->i_code->_jump_targets = NULL;
+ // }
+ // PyMem_Free(t2_info->i_code);
+ // t2_info->i_code = NULL;
+ //}
}
static void
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 3bf1bf3f18dbf4..9900ad02bd24b4 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -292,11 +292,11 @@ dummy_func(
}
macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
- U_INST(BINARY_OP_ADD_INT_TYPE_CHECK);
+ U_INST(BINARY_CHECK_INT);
U_INST(BINARY_OP_ADD_INT_REST);
}
- u_inst(BINARY_OP_ADD_INT_TYPE_CHECK, (left, right -- left : PyLong_Type, right: PyLong_Type)) {
+ u_inst(BINARY_CHECK_INT, (left, right -- left : PyLong_Type, right: PyLong_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index d0bde190576225..86125e4a5c3347 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2,7 +2,7 @@
// from Python\bytecodes.c
// Do not edit!
- #define UOP_BINARY_OP_ADD_INT_TYPE_CHECK() \
+ #define UOP_BINARY_CHECK_INT() \
do { \
assert(cframe.use_tracing == 0);\
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);\
@@ -23,9 +23,9 @@
}
TARGET(RESUME) {
- //if (cframe.use_tracing == 0) {
- // next_instr = _PyCode_Tier2Warmup(frame, next_instr);
- //}
+ if (cframe.use_tracing == 0) {
+ next_instr = _PyCode_Tier2Warmup(frame, next_instr);
+ }
GO_TO_INSTRUCTION(RESUME_QUICK);
}
@@ -414,7 +414,7 @@
PyObject *right = PEEK(1);
PyObject *left = PEEK(2);
PyObject *sum;
- UOP_BINARY_OP_ADD_INT_TYPE_CHECK();
+ UOP_BINARY_CHECK_INT();
UOP_BINARY_OP_ADD_INT_REST();
STACK_SHRINK(1);
POKE(1, sum);
@@ -422,7 +422,7 @@
DISPATCH();
}
- TARGET(BINARY_OP_ADD_INT_TYPE_CHECK) {
+ TARGET(BINARY_CHECK_INT) {
PyObject *right = PEEK(1);
PyObject *left = PEEK(2);
assert(cframe.use_tracing == 0);
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 6551cd7807f128..d4ac2d2b43ecd2 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -60,7 +60,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 2;
case BINARY_OP_ADD_INT:
return 2;
- case BINARY_OP_ADD_INT_TYPE_CHECK:
+ case BINARY_CHECK_INT:
return 2;
case BINARY_OP_ADD_INT_REST:
return 2;
@@ -414,7 +414,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 1;
case BINARY_OP_ADD_INT:
return 1;
- case BINARY_OP_ADD_INT_TYPE_CHECK:
+ case BINARY_CHECK_INT:
return 2;
case BINARY_OP_ADD_INT_REST:
return 1;
@@ -745,7 +745,7 @@ struct opcode_metadata {
[BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[BINARY_OP_ADD_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_ADD_INT_TYPE_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BINARY_CHECK_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_OP_ADD_INT_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
[BINARY_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
diff --git a/Python/pystate.c b/Python/pystate.c
index 1261092d1435fa..d79ba19bf09bff 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -643,6 +643,10 @@ init_interpreter(PyInterpreterState *interp,
PyConfig_InitPythonConfig(&interp->config);
_PyType_InitCache(interp);
+ // Tier 2 interpreter information
+ interp->tier2_bytecode_scratchsize = 0;
+ interp->tier2_bytecode_scratch = NULL;
+
interp->_initialized = 1;
}
diff --git a/Python/tier2.c b/Python/tier2.c
index 968db9c90f84fd..d84582d8027db6 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -2,11 +2,22 @@
#include "stdlib.h"
#include "pycore_code.h"
#include "pycore_frame.h"
+#include "pycore_opcode.h"
+#include "pycore_pystate.h"
#include "opcode.h"
-static _Py_CODEUNIT *
-_PyCode_Tier2Initialize(_PyInterpreterFrame *, _Py_CODEUNIT *);
+#define BB_DEBUG 1
+
+// Number of potential extra instructions at end of a BB, for branch or cleanup purposes.
+// BB_BRANCH instruction: 1
+#define BB_EPILOG 1
+
+static _Py_CODEUNIT *_PyCode_Tier2Initialize(_PyInterpreterFrame *, _Py_CODEUNIT *);
+
+static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
+
+////////// CEVAL functions
// Tier 2 warmup counter
_Py_CODEUNIT *
@@ -28,13 +39,71 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
return next_instr;
}
+////////// Utility functions
+
+// Checks that we have enough scratch space for the current code object. Else allocate more.
+static _Py_CODEUNIT *
+_PyInterpreter_GetScratchSpace(PyCodeObject *co)
+{
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ int n_instrs = (int)Py_SIZE(co);
+ Py_ssize_t space_to_alloc = _PyCode_NBYTES(co);
+ int overalloc = 2;
+ if (interp->tier2_bytecode_scratchsize == 0 ||
+ interp->tier2_bytecode_scratch == NULL) {
+ interp->tier2_bytecode_scratch = PyMem_Malloc(space_to_alloc * overalloc);
+ if (interp->tier2_bytecode_scratch == NULL) {
+ return NULL;
+ }
+ interp->tier2_bytecode_scratchsize = Py_SIZE(co) * overalloc;
+ return interp->tier2_bytecode_scratch;
+ }
+ if (interp->tier2_bytecode_scratchsize < n_instrs) {
+ PyMem_Free(interp->tier2_bytecode_scratch);
+ interp->tier2_bytecode_scratch = PyMem_Malloc(space_to_alloc * overalloc);
+ if (interp->tier2_bytecode_scratch == NULL) {
+ return NULL;
+ }
+ interp->tier2_bytecode_scratchsize = Py_SIZE(co) * overalloc;
+ }
+ return interp->tier2_bytecode_scratch;
+}
+
// Gets end of the bytecode for a code object.
_Py_CODEUNIT *
_PyCode_GetEnd(PyCodeObject *co)
{
- return (_Py_CODEUNIT *)(co->co_code_adaptive + _PyCode_NBYTES(co));
+ _Py_CODEUNIT *end = (_Py_CODEUNIT *)(co->co_code_adaptive + _PyCode_NBYTES(co));
+ return end;
+}
+
+// Gets end of actual bytecode executed. _PyCode_GetEnd might return a CACHE instruction.
+_Py_CODEUNIT *
+_PyCode_GetLogicalEnd(PyCodeObject *co)
+{
+ _Py_CODEUNIT *end = _PyCode_GetEnd(co);
+ while (_Py_OPCODE(*end) == CACHE) {
+ end--;
+ }
+#if BB_DEBUG
+ if (!IS_SCOPE_EXIT_OPCODE(_Py_OPCODE(*end))) {
+ fprintf(stderr, "WRONG EXIT OPCODE: %d\n", _Py_OPCODE(*end));
+ assert(0);
+ }
+#endif
+ assert(IS_SCOPE_EXIT_OPCODE(_Py_OPCODE(*end)));
+ return end;
+}
+
+// Gets end of the bytecode for a tier 2 BB.
+_Py_CODEUNIT *
+_PyTier2BB_UCodeEnd(_PyTier2BB *bb)
+{
+ return (_Py_CODEUNIT *)(bb->u_code + bb->n_instrs);
}
+////////// BB SPACE FUNCTIONS
+
// Creates the overallocated array for the BBs.
static _PyTier2BBSpace *
_PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
@@ -50,20 +119,57 @@ _PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
return bb_space;
}
+////////// TIER 2 BB FUNCTIONS
+
/* Init a BB in BB space without any checks for waterlevel. */
static _PyTier2BB *
_PyTier2_InitBBNoCheck(_PyTier2BBSpace *bb_space, _Py_CODEUNIT *tier1_end,
_Py_CODEUNIT *instr_start, _Py_CODEUNIT *instr_end)
{
- Py_ssize_t nbytes = (instr_end - instr_start) * sizeof(_Py_CODEUNIT);
+ Py_ssize_t ninstrs = (instr_end - instr_start) + BB_EPILOG;
+ assert(ninstrs == (int)ninstrs);
+ Py_ssize_t nbytes = ninstrs * sizeof(_Py_CODEUNIT);
_PyTier2BB *bb_ptr = &bb_space->bbs[bb_space->water_level];
bb_ptr->tier1_end = tier1_end;
+ bb_ptr->n_instrs = (int)ninstrs;
+ bb_ptr->successor_bb = NULL;
+ bb_ptr->alternate_bb = NULL;
+ // @TODO, add type context.
memcpy(bb_ptr->u_code, (const void *)instr_start, nbytes);
assert(bb_space->water_level + nbytes == (int)nbytes);
bb_space->water_level += (int)nbytes;
return bb_ptr;
}
+/* Allocates and initializes a new basic block. If there's not enough space in
+ the overallocated array, create a new array.
+
+ Make sure to call _PyCode_Tier2Initialize before this!
+*/
+static _PyTier2BB *
+_PyCode_Tier2BBNew(PyCodeObject *co, _Py_CODEUNIT *tier1_end, _Py_CODEUNIT *instr_start, _Py_CODEUNIT *instr_end)
+{
+ assert(co->_tier2_info != NULL);
+ assert(co->_tier2_info->_bb_space != NULL);
+
+ _PyTier2BBSpace *bb_space = co->_tier2_info->_bb_space;
+ Py_ssize_t amount_to_alloc = (instr_start - instr_end) * sizeof(_Py_CODEUNIT *) + sizeof(_PyTier2BB);
+ assert(bb_space->water_level + amount_to_alloc == (int)(bb_space->water_level + amount_to_alloc));
+
+ // Need to allocate a new array.
+ if (bb_space->water_level + amount_to_alloc > bb_space->max_capacity) {
+ _PyTier2BBSpace *next_bb_space = _PyTier2_CreateBBSpace(bb_space->max_capacity + amount_to_alloc);
+ if (next_bb_space == NULL) {
+ return NULL;
+ }
+ next_bb_space->next = bb_space;
+ // We want to make our bb_space point to the most recent one to get O(1) BB allocations.
+ co->_tier2_info->_bb_space = next_bb_space;
+ bb_space = next_bb_space;
+ }
+ return _PyTier2_InitBBNoCheck(bb_space, tier1_end, instr_start, instr_end);
+}
+
/* Opcode detection functions. Keep in sync with compile.c and dis! */
@@ -83,6 +189,7 @@ IS_JREL_OPCODE(int opcode)
case JUMP_FORWARD:
case JUMP_IF_FALSE_OR_POP:
case JUMP_IF_TRUE_OR_POP:
+ // These two tend to be after a COMPARE_AND_BRANCH.
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
case SEND:
@@ -104,14 +211,22 @@ IS_JUMP_OPCODE(int opcode)
return IS_JREL_OPCODE(opcode) || IS_JABS_OPCODE(opcode);
}
+// dis.hascompare
+static inline int
+IS_COMPARE_OPCODE(int opcode)
+{
+ return opcode == COMPARE_OP || opcode == COMPARE_AND_BRANCH;
+}
static inline int
IS_SCOPE_EXIT_OPCODE(int opcode)
{
switch (opcode) {
case RETURN_VALUE:
+ case RETURN_CONST:
case RAISE_VARARGS:
case RERAISE:
+ case INTERPRETER_EXIT:
return 1;
default:
return 0;
@@ -125,16 +240,357 @@ IS_TERMINATOR_OPCODE(int opcode)
return IS_JUMP_OPCODE(opcode) || IS_SCOPE_EXIT_OPCODE(opcode);
}
+// Opcodes that we can't handle at the moment. If we see them,
+// ditch tier 2 attempts.
+static inline int
+IS_FORBIDDEN_OPCODE(int opcode)
+{
+ switch (opcode) {
+ // Generators and coroutines
+ case SEND:
+ case YIELD_VALUE:
+ // Raise keyword
+ case RAISE_VARARGS:
+ // Exceptions, we could support these theoretically.
+ // Just too much work for now
+ case PUSH_EXC_INFO:
+ case RERAISE:
+ case POP_EXCEPT:
+ // Closures
+ case LOAD_DEREF:
+ case MAKE_CELL:
+ // @TODO backward jumps should be supported!
+ case JUMP_BACKWARD:
+ case JUMP_BACKWARD_NO_INTERRUPT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline PyTypeObject *
+INSTR_LOCAL_READ_TYPE(PyCodeObject *co, _Py_CODEUNIT instr, PyTypeObject **type_context)
+{
+ int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
+ int oparg = _Py_OPARG(instr);
+ switch (opcode) {
+ case LOAD_CONST:
+ return Py_TYPE(PyTuple_GET_ITEM(co->co_consts, oparg));
+ case LOAD_FAST:
+ return type_context[oparg];
+ // Note: Don't bother with LOAD_NAME, because those exist only in the global
+ // scope.
+ default:
+ return NULL;
+ }
+}
+
+/*
+* This does multiple things:
+* 1. Gets the result type of an instruction, if it can figure it out.
+ 2. Determines whether thet instruction has a corresponding macro/micro instruction
+ that acts a guard, and whether it needs it.
+ 3. How many guards it needs, and what are the guards.
+ 4. Finally, the corresponding specialised micro-op to operate on this type.
+
+ Returns the inferred type of an operation. NULL if unknown.
+
+ how_many_guards returns the number of guards (0, 1)
+ guards should be a opcode corresponding to the guard instruction to use.
+*/
+static PyTypeObject *
+BINARY_OP_RESULT_TYPE(PyCodeObject *co, _Py_CODEUNIT *instr, int n_typecontext,
+ PyTypeObject **type_context, int *how_many_guards, _Py_CODEUNIT *guard, _Py_CODEUNIT *action)
+{
+ int opcode = _PyOpcode_Deopt[_Py_OPCODE(*instr)];
+ int oparg = _Py_OPARG(*instr);
+ switch (opcode) {
+ case BINARY_OP:
+ if (oparg == NB_ADD) {
+ // For BINARY OP, read the previous two load instructions
+ // to see what variables we need to type check.
+ PyTypeObject *lhs_type = INSTR_LOCAL_READ_TYPE(co, *(instr - 2), type_context);
+ PyTypeObject *rhs_type = INSTR_LOCAL_READ_TYPE(co, *(instr - 1), type_context);
+ // The two instruction types are known.
+ if (lhs_type == &PyLong_Type) {
+ if (rhs_type == &PyLong_Type) {
+ *how_many_guards = 0;
+ action->opcode = BINARY_OP_ADD_INT_REST;
+ return &PyLong_Type;
+ }
+ //// Right side unknown. Emit single check.
+ //else if (rhs_type == NULL) {
+ // *how_many_guards = 1;
+ // guard->opcode =
+ // return NULL;
+ //}
+ }
+ // We don't know anything, need to emit guard.
+ // @TODO
+ }
+ break;
+ }
+ return NULL;
+}
+
+static inline _Py_CODEUNIT *
+emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard)
+{
+ *write_curr = guard;
+ write_curr++;
+ _py_set_opcode(write_curr, BB_TYPE_BRANCH);
+ write_curr++;
+ return write_curr;
+}
+
+static inline _Py_CODEUNIT *
+emit_logical_branch(_Py_CODEUNIT *write_curr, int branch)
+{
+ int opcode;
+ int oparg = 0;
+ // @TODO handle JUMP_BACKWARDS and JUMP_BACKWARDS_NO_INTERRUPT
+ switch (branch) {
+ case FOR_ITER:
+ opcode = BB_ITER;
+ break;
+ case JUMP_IF_FALSE_OR_POP:
+ oparg = 1;
+ case JUMP_IF_TRUE_OR_POP:
+ opcode = BB_BRANCH_OR_POP;
+ break;
+ case POP_JUMP_IF_FALSE:
+ oparg = 1;
+ case POP_JUMP_IF_TRUE:
+ opcode = BB_POP_THEN_BRANCH;
+ break;
+ case POP_JUMP_IF_NOT_NONE:
+ oparg = 1;
+ case POP_JUMP_IF_NONE:
+ opcode = BB_POP_BRANCH;
+ break;
+ default:
+ // Honestly shouldn't happen because branches that
+ // we can't handle are in IS_FORBIDDEN_OPCODE
+#if BB_DEBUG
+ fprintf(stderr, "emit_logical_branch unreachable\n");
+#endif
+ Py_UNREACHABLE();
+ }
+#if BB_DEBUG
+ fprintf(stderr, "emitted logical branch\n");
+#endif
+ _py_set_opcode(write_curr, opcode);
+ write_curr->oparg = oparg;
+ write_curr++;
+ return write_curr;
+}
+
+static inline _Py_CODEUNIT *
+emit_scope_exit(_Py_CODEUNIT *write_curr, int exit)
+{
+ switch (exit) {
+ case RETURN_VALUE:
+ case RETURN_CONST:
+ case INTERPRETER_EXIT:
+#if BB_DEBUG
+ fprintf(stderr, "emitted scope exit\n");
+#endif
+ // @TODO we can propogate and chain BBs across call boundaries
+ // Thanks to CPython's inlined call frames.
+ _py_set_opcode(write_curr, BB_EXIT_FRAME);
+ return write_curr + 1;
+ default:
+ // The rest are forbidden.
+#if BB_DEBUG
+ fprintf(stderr, "emit_scope_exit unreachable %d\n", exit);
+#endif
+ Py_UNREACHABLE();
+ }
+}
+
+static inline _Py_CODEUNIT *
+emit_i(_Py_CODEUNIT *write_curr, int opcode, int oparg)
+{
+ _py_set_opcode(write_curr, opcode);
+ write_curr->oparg = oparg;
+ write_curr++;
+ return write_curr;
+}
+
+static inline _Py_CODEUNIT *
+copy_cache_entries(_Py_CODEUNIT *write_curr, _Py_CODEUNIT *cache, int n_entries)
+{
+ for (int i = 0; i < n_entries; i++) {
+ *write_curr = *cache;
+ cache++;
+ write_curr++;
+ }
+ return write_curr;
+}
+
+// Detects a BB from the current instruction start to the end of the first basic block it sees.
+// Then emits the instructions for a _PyTier2BB.
+//
+// Instructions emitted depend on the type_context.
+// For example, if it sees a BINARY_ADD instruction, but it knows the two operands are already of
+// type PyLongObject, a BINARY_ADD_INT_REST will be emitted without an type checks.
+//
+// However, if one of the operands are unknown, a logical chain of CHECK instructions will be emitted,
+// and the basic block will end at the first of the chain.
+//
+// Note: a BB end also includes a type guard.
+_PyTier2BB *
+_PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
+ int n_typecontext, PyTypeObject **type_context)
+{
+#define END() goto end;
+#define JUMPBY(x) i += x + 1; continue;
+#define BASIC_PUSH(v) (*stack_pointer++ = (v))
+#define BASIC_POP() (*--stack_pointer)
+#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); write_i = copy_cache_entries(write_i, curr+1, caches); break;
+ assert(co->_tier2_info != NULL);
+ // There are only two cases that a BB ends.
+ // 1. If there's a branch instruction / scope exit.
+ // 2. If the instruction is a jump target.
+
+ // Make a copy of the type context
+ PyTypeObject **type_context_copy = PyMem_Malloc(n_typecontext * sizeof(PyTypeObject *));
+ if (type_context_copy == NULL) {
+ return NULL;
+ }
+ memcpy(type_context_copy, type_context, n_typecontext * sizeof(PyTypeObject *));
+
+ _Py_CODEUNIT *start_i = _PyInterpreter_GetScratchSpace(co);
+ _Py_CODEUNIT *write_i = start_i;
+ PyTypeObject **stack_pointer = co->_tier2_info->types_stack;
+ int tos = -1;
+
+ // A meta-interpreter for types.
+ Py_ssize_t i = 0;
+ for (; i < Py_SIZE(co); i++) {
+ _Py_CODEUNIT *curr = _PyCode_CODE(co) + i;
+ _Py_CODEUNIT instr = *curr;
+ int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
+ int oparg = _Py_OPARG(instr);
+ int caches = _PyOpcode_Caches[opcode];
+
+ // Just because an instruction requires a guard doesn't mean it's the end of a BB.
+ // We need to check whether we can eliminate the guard based on the current type context.
+ int how_many_guards = 0;
+ _Py_CODEUNIT guard_instr;
+ _Py_CODEUNIT action;
+
+ switch (opcode) {
+ //case LOAD_FAST:
+ // BASIC_PUSH(type_context[oparg]);
+ // DISPATCH();
+ //case LOAD_NAME:
+ // BASIC_PUSH(NULL);
+ // DISPATCH();
+ //case LOAD_CONST: {
+ // PyTypeObject *cont = Py_TYPE(PyTuple_GET_ITEM(co->co_consts, oparg));
+ // BASIC_PUSH(cont);
+ // DISPATCH();
+ //}
+ //case STORE_FAST: {
+ // type_context[oparg] = BASIC_POP();
+ // DISPATCH();
+ //}
+ //case STORE_NAME:
+ // BASIC_POP();
+ // DISPATCH();
+ //case BINARY_OP: {
+ // PyTypeObject *res = BINARY_OP_RESULT_TYPE(co, curr, n_typecontext, type_context,
+ // &how_many_guards, &guard_instr, &action);
+ // // We need a guard. So this is the end of the basic block.
+ // // @TODO in the future, support multiple guards.
+ // if (how_many_guards > 0) {
+ // // Emit the guard
+ // emit_type_guard(&write_i, guard_instr);
+ // END();
+ // }
+ // else {
+ // BASIC_PUSH(res);
+ // // Don't need a guard, either is a micro op, or is a generic instruction
+ // // No type information known. Use a generic instruction.
+ // if (res == NULL) {
+ // DISPATCH();
+ // }
+ // else {
+ // emit_i(&write_i, _Py_OPCODE(action), 0);
+ // break;
+ // }
+ // }
+ //}
+ default:
+ // These are definitely the end of a basic block.
+ if (IS_SCOPE_EXIT_OPCODE(opcode)) {
+ // Emit the scope exit instruction.
+ write_i = emit_scope_exit(write_i, opcode);
+ END();
+ }
+
+ // Jumps may be the end of a basic block if they are conditional (a branch).
+ if (IS_JUMP_OPCODE(opcode)) {
+ // Unconditional forward jump... continue with the BB without writing the jump.
+ if (opcode == JUMP_FORWARD) {
+ // JUMP offset (oparg) + current instruction + cache entries
+ JUMPBY(oparg);
+ }
+ write_i = emit_logical_branch(write_i, opcode);
+ END();
+ }
+ DISPATCH();
+ }
+
+ i += caches;
+
+ }
+end:
+//#if BB_DEBUG
+// fprintf(stderr, "i is %Id\n", i);
+//#endif
+ // Create the tier 2 BB
+ return _PyCode_Tier2BBNew(co, _PyCode_CODE(co) + i, start_i, write_i);
+}
+
+
+//// The exits of BBs need to use BB_BRANCH instruction.
+//static void
+//_PyTier2BB_RewriteExitBranch(_PyTier2BB *bb)
+//{
+// _Py_CODEUNIT *last_instr = _PyTier2BB_UCodeEnd(bb) - BB_EPILOG;
+// int op = _PyOpcode_Deopt[_Py_OPCODE(*last_instr)];
+// assert(IS_TERMINATOR_OPCODE(op));
+// if (IS_JUMP_OPCODE(op)) {
+// _Py_CODEUNIT lasti = *last_instr;
+// _py_set_opcode(last_instr, BB_BRANCH);
+// // Write the original jump instruction after to know where
+// // to start generating the next BB from.
+// _Py_SET_OPCODE
+// return;
+// }
+// assert(IS_SCOPE_EXIT_OPCODE(op));
+// assert(op == RETURN_VALUE || op == RETURN_CONST);
+// // Need to FRAME_EXIT.
+//
+//}
+
+
+////////// _PyTier2Info FUNCTIONS
+
static int
compare_ints(const void *a, const void *b)
{
- const int a_num = *(int *)a;
- const int b_num = *(int *)b;
- return a_num - b_num;
+ return *(int *)a - *(int *)b;
}
// Returns 1 on error, 0 on success. Populates the jump target offset
// array for a code object.
+// NOTE TO SELF: WE MIGHT NOT EVEN NEED TO FILL UP JUMP TARGETS.
+// JUMP TARGETS CONTINUE BEING PART OF THE BASIC BLOCK AS LONG
+// AS NO BRANCH IS DETECTED.
+// REMOVE IN THE FUTURE IF UNECESSARY.
static int
_PyCode_Tier2FillJumpTargets(PyCodeObject *co)
{
@@ -145,96 +601,172 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
_Py_CODEUNIT *instr_ptr = _PyCode_CODE(co) + i;
_Py_CODEUNIT instr = *instr_ptr;
- switch (_Py_OPCODE(instr)) {
+ int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
+ int oparg = _Py_OPARG(instr);
+ switch (opcode) {
case RESUME:
_py_set_opcode(instr_ptr, RESUME_QUICK);
break;
default:
- jump_target_count += IS_JUMP_OPCODE(_Py_OPCODE(instr));
+ // We want to track all guard instructions as
+ // jumps too.
+ jump_target_count += IS_JUMP_OPCODE(opcode); // + INSTR_HAS_GUARD(instr);
}
+ i += _PyOpcode_Caches[opcode];
}
- // Find all the jump target instructions
- _Py_CODEUNIT *end = _PyCode_GetEnd(co);
- _Py_CODEUNIT *start = _PyCode_CODE(co);
- _Py_CODEUNIT *curr = start;
-
// Impossibly big.
if (jump_target_count != (int)jump_target_count) {
return 1;
}
- // Impossibly big
- if (end - start != (int)(end - start)) {
- return 1;
+
+ // Find all the jump target instructions
+ // Don't allocate a zero byte space as this may be undefined behavior.
+ if (jump_target_count == 0) {
+ co->_tier2_info->jump_targets = NULL;
+ // Successful (no jump targets)!
+ co->_tier2_info->jump_target_count = (int)jump_target_count;
+ return 0;
}
- co->_tier2_info->_jump_target_count = (int)jump_target_count;
int *jump_targets = PyMem_Malloc(jump_target_count * sizeof(int));
if (jump_targets == NULL) {
return 1;
}
+ _Py_CODEUNIT *start = _PyCode_CODE(co);
int curr_i = 0;
- while (curr <= end) {
+ for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
+ _Py_CODEUNIT *curr = start + i;
_Py_CODEUNIT instr = *curr;
- if (IS_JUMP_OPCODE(_Py_OPCODE(instr))) {
+ int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
+ int oparg = _Py_OPARG(instr);
+ //switch (opcode) {
+ //// KEEP IN SYNC WITH INSTR_HAS_GUARD
+ //case BINARY_OP:
+ // if (oparg == NB_ADD) {
+ // jump_targets[curr_i] = (int)(curr - start) + INLINE_CACHE_ENTRIES_BINARY_OP;
+ // curr_i++;
+ // }
+ // break;
+ //default:
+ if (IS_JUMP_OPCODE(opcode)) {
_Py_CODEUNIT *target = curr + _Py_OPARG(instr);
// (in terms of offset from start of co_code_adaptive)
jump_targets[curr_i] = (int)(target - start);
curr_i++;
}
- curr++;
+ //}
+ i += _PyOpcode_Caches[opcode];
}
+ assert(curr_i == jump_target_count);
qsort(jump_targets, jump_target_count, sizeof(int), compare_ints);
- co->_tier2_info->_jump_targets = jump_targets;
+#if BB_DEBUG
+ fprintf(stderr, "JUMP TARGET COUNT: %lld\n", jump_target_count);
+ fprintf(stderr, "JUMP TARGET OFFSETS (FROM START OF CODE): ");
+ for (Py_ssize_t i = 0; i < jump_target_count; i++) {
+ fprintf(stderr, "%d ,", jump_targets[i]);
+ }
+ fprintf(stderr, "\n");
+#endif
+ co->_tier2_info->jump_target_count = (int)jump_target_count;
+ co->_tier2_info->jump_targets = jump_targets;
return 0;
}
-// Detects a BB from the current instruction start to the end of the code object.
-_Py_CODEUNIT *
-_PyTier2_Code_DetectBB(PyCodeObject *co, _Py_CODEUNIT *start)
+//static _PyTier2IntermediateCode *
+//_PyIntermediateCode_Initialize(PyCodeObject *co)
+//{
+// assert(co->_tier2_info != NULL);
+// Py_ssize_t space_to_alloc = (sizeof(PyCodeObject) + _PyCode_NBYTES(co)) + BB_EPILOG;
+// _PyTier2IntermediateCode *i_code = PyMem_Malloc(space_to_alloc);
+// if (i_code == NULL) {
+// return NULL;
+// }
+//
+// // Copy over bytecode
+// for (Py_ssize_t curr = 0; curr < Py_SIZE(co); curr++) {
+// _Py_CODEUNIT *curr_instr = _PyCode_CODE(co) + curr;
+// // NEED TO TRANSFORM BINARY OPS TO
+// i_code->code[curr] = *curr_instr;
+// }
+// return i_code;
+//}
+
+
+static _PyTier2Info *
+_PyTier2Info_Initialize(PyCodeObject *co)
{
- assert(co->_tier2_info != NULL);
- // There are only two cases that a BB ends.
- // 1. If there's a branch instruction / scope exit.
- // 2. If the instruction is a jump target.
- _Py_CODEUNIT *instr_end = _PyCode_GetEnd(co);
- _Py_CODEUNIT *curr = start;
- int *jump_target_offsets = co->_tier2_info->_jump_targets;
- int jump_target_count = co->_tier2_info->_jump_target_count;
- int curr_jump = 0;
- while (curr < instr_end) {
- _Py_CODEUNIT instr = *curr;
- int opcode = _Py_OPCODE(instr);
- if (IS_JUMP_OPCODE(opcode) || IS_SCOPE_EXIT_OPCODE(opcode)) {
- return curr;
- }
- while (_PyCode_CODE(co) + jump_target_offsets[curr_jump] < curr) {
- curr_jump++;
- }
- if (_PyCode_CODE(co) + jump_target_offsets[curr_jump] == curr) {
- return curr;
- }
- curr++;
+ assert(co->_tier2_info == NULL);
+ _PyTier2Info *t2_info = PyMem_Malloc(sizeof(_PyTier2Info));
+ if (t2_info == NULL) {
+ return NULL;
+ }
+ co->_tier2_info = t2_info;
+
+ //if (_PyIntermediateCode_Initialize(co) == NULL) {
+ // PyMem_FREE(t2_info);
+ // return NULL;
+ //}
+
+ // Next is to intitialize stack space for the tier 2 types meta-interpretr.
+ t2_info->types_stack = PyMem_Malloc(co->co_stacksize * sizeof(PyObject *));
+ if (t2_info->types_stack == NULL) {
+ PyMem_Free(t2_info);
+ }
+ return t2_info;
+}
+
+////////// TYPE CONTEXT FUNCTIONS
+
+static PyTypeObject **
+initialize_type_context(PyCodeObject *co) {
+ int nlocals = co->co_nlocals;
+ PyTypeObject **type_context = PyMem_Malloc(nlocals * sizeof(PyTypeObject *));
+ if (type_context == NULL) {
+ return NULL;
}
- return instr_end;
+ // Initialize to uknown type.
+ for (int i = 0; i < nlocals; i++) {
+ type_context[i] = NULL;
+ }
+ return type_context;
}
+////////// OVERALL TIER2 FUNCTIONS
+
+
+// 1. Initialize whatever we need.
+// 2. Create the entry BB.
+// 3. Jump into that BB.
static _Py_CODEUNIT *
_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
- int curr = _Py_OPCODE(*(next_instr - 1));
- assert(curr == RESUME);
+ assert(_Py_OPCODE(*(next_instr - 1)) == RESUME);
+
+ // First check for forbidden opcodes that we currently can't handle.
PyCodeObject *co = frame->f_code;
- assert(co->_tier2_info == NULL);
- _PyTier2Info *t2_info = PyMem_Malloc(sizeof(_PyTier2Info));
+ // Impossibly big.
+ if ((int)Py_SIZE(co) != Py_SIZE(co)) {
+ return NULL;
+ }
+ for (Py_ssize_t curr = 0; curr < Py_SIZE(co); curr++) {
+ _Py_CODEUNIT *curr_instr = _PyCode_CODE(co) + curr;
+ if (IS_FORBIDDEN_OPCODE(_PyOpcode_Deopt[_Py_OPCODE(*curr_instr)])) {
+ return NULL;
+ }
+ }
+
+ if (_PyInterpreter_GetScratchSpace(co) == NULL) {
+ return NULL;
+ }
+
+ _PyTier2Info *t2_info = _PyTier2Info_Initialize(co);
if (t2_info == NULL) {
return NULL;
}
- co->_tier2_info = t2_info;
- // 0. Single pass: mark jump targets
- // 1. Initialize basic blocks space.
- // 2. Create the entry BB (if it is, else the current BB).
- // 3. Jump into that BB.
- fprintf(stderr, "INITIALIZING %ld\n", Py_SIZE(co));
+
+#if BB_DEBUG
+ fprintf(stderr, "INITIALIZING\n");
+#endif
Py_ssize_t space_to_alloc = (sizeof(_PyTier2BB) + _PyCode_NBYTES(co)) * 2;
@@ -243,61 +775,33 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
PyMem_Free(t2_info);
return NULL;
}
+ if (_PyCode_Tier2FillJumpTargets(co)) {
+ goto cleanup;
+ }
t2_info->_bb_space = bb_space;
- //_Py_CODEUNIT *entry_bb_end = _PyTier2_Code_DetectBB(co, _PyCode_CODE(co));
- _Py_CODEUNIT *entry_bb_end = _PyCode_GetEnd(co);
-
- _PyTier2BB *bb_ptr = _PyTier2_InitBBNoCheck(bb_space,
- entry_bb_end,
- _PyCode_CODE(co),
- entry_bb_end);
-
- if (_PyCode_Tier2FillJumpTargets(co)) {
+ PyTypeObject **type_context = initialize_type_context(co);
+ if (type_context == NULL) {
goto cleanup;
}
+ _PyTier2BB *bb_ptr = _PyTier2_Code_DetectAndEmitBB(co, _PyCode_CODE(co), co->co_nlocals, type_context);
+ if (bb_ptr == NULL) {
+ goto cleanup;
+ }
+#if BB_DEBUG
+ fprintf(stderr, "ENTRY BB END IS: %d\n", (int)(bb_ptr->tier1_end - _PyCode_CODE(co)));
+#endif
t2_info->_entry_bb = bb_ptr;
- // Set the instruction pointer to the next one in the bb
- Py_ssize_t offset_from_start = (frame->prev_instr - _PyCode_CODE(co));
- assert(offset_from_start >= -1);
- frame->prev_instr = bb_ptr->u_code + offset_from_start;
- // _py_set_opcode(next_instr, CACHE);
- return bb_ptr->u_code + (next_instr - _PyCode_CODE(co));
+ // Set the starting instruction to the entry BB.
+ // frame->prev_instr = bb_ptr->u_code - 1;
+ return bb_ptr->u_code;
cleanup:
+ PyMem_Free(t2_info);
PyMem_Free(bb_space);
return NULL;
}
-
-/* Allocates and initializes a new basic block. If there's not enough space in
- the overallocated array, create a new array.
-
- Make sure to call _PyCode_Tier2Initialize before this!
-*/
-static _PyTier2BB *
-_PyCode_Tier2BBNew(PyCodeObject *co, _Py_CODEUNIT *instr_start, _Py_CODEUNIT *instr_end)
-{
- assert(co->_tier2_info != NULL);
- assert(co->_tier2_info->_bb_space != NULL);
-
- _PyTier2BBSpace *bb_space = co->_tier2_info->_bb_space;
- Py_ssize_t amount_to_alloc = (instr_start - instr_end) * sizeof(_Py_CODEUNIT *) + sizeof(_PyTier2BB);
- assert(bb_space->water_level + amount_to_alloc == (int)(bb_space->water_level + amount_to_alloc));
-
- // Need to allocate a new array.
- if (bb_space->water_level + amount_to_alloc > bb_space->max_capacity) {
- _PyTier2BBSpace *next_bb_space = _PyTier2_CreateBBSpace(bb_space->max_capacity + amount_to_alloc);
- if (next_bb_space == NULL) {
- return NULL;
- }
- next_bb_space->next = bb_space;
- // We want to make our bb_space point to the most recent one to get O(1) BB allocations.
- co->_tier2_info->_bb_space = next_bb_space;
- bb_space = next_bb_space;
- }
- return _PyTier2_InitBBNoCheck(bb_space, _PyCode_GetEnd(co), instr_start, instr_end);
-}
From 2aaea4f81e41309b685290d5f1ffb0a019a67ee7 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 13 Feb 2023 16:04:57 +0800
Subject: [PATCH 025/280] Part 2: Drive execution of BBs lazily
---
Include/cpython/code.h | 4 +-
Include/internal/pycore_code.h | 4 +
Lib/opcode.py | 2 +-
Python/bytecodes.c | 146 +++++++++++++++++++++++++++++++++
Python/ceval.c | 1 +
Python/tier2.c | 90 ++++++++++++++------
6 files changed, 219 insertions(+), 28 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 4fd9705b9dff0f..592843eedc724b 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -59,9 +59,9 @@ typedef struct {
// What the tier 2 interpreter executes
typedef struct _PyTier2BB {
- _Py_CODEUNIT *successor_bb;
+ struct _PyTier2BB *successor_bb;
// The other BB to go should BB_BRANCH fail.
- _Py_CODEUNIT *alternate_bb;
+ struct _PyTier2BB *alternate_bb;
// Array of types. This corresponds to the fast locals array.
int type_context_len;
PyTypeObject **type_context;
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 392ea9cf83055a..35c958613cdaba 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -241,6 +241,7 @@ extern int _PyStaticCode_Init(PyCodeObject *co);
/* Tier 2 interpreter */
extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *, _Py_CODEUNIT *);
+extern _Py_CODEUNIT *_PyTier2BB_ExecuteNextBB(struct _PyInterpreterFrame *frame, _PyTier2BB **curr_bb, int direction);
#ifdef Py_STATS
@@ -500,6 +501,9 @@ extern uint32_t _Py_next_func_version;
#define COMPARISON_NOT_EQUALS (COMPARISON_UNORDERED | COMPARISON_LESS_THAN | COMPARISON_GREATER_THAN)
+#define BB_SUC 1
+#define BB_ALT 0
+
#ifdef __cplusplus
}
#endif
diff --git a/Lib/opcode.py b/Lib/opcode.py
index be1c0dbf8fafe9..02384a86a8132c 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -447,7 +447,7 @@ def pseudo_op(name, op, real_ops):
'BB_EXIT_FRAME',
## These branches correspond to the jump instructions
'BB_TYPE_BRANCH',
- 'BB_ITER', # FOR_ITER
+ 'BB_ITER', # FOR_ITER's null (iterator) check
'BB_BRANCH_OR_POP', # JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP
'BB_POP_THEN_BRANCH', # POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE
'BB_POP_BRANCH', # POP_JUMP_IF_NOT_NONE, POP_JUMP_IF_NONE
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 9900ad02bd24b4..8216d4fc636915 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1926,6 +1926,51 @@ dummy_func(
}
}
+ inst(BB_POP_THEN_BRANCH, (cond -- )) {
+ // POP_JUMP_IF_FALSE
+ if (oparg) {
+ if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
+ }
+ else if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ }
+ else {
+ int err = PyObject_IsTrue(cond);
+ Py_DECREF(cond);
+ if (err == 0) {
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ }
+ else {
+ ERROR_IF(err < 0, error);
+ }
+ }
+ }
+ // POP_JUMP_IF_TRUE
+ else {
+ if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
+ }
+ else if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ }
+ else {
+ int err = PyObject_IsTrue(cond);
+ Py_DECREF(cond);
+ if (err > 0) {
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ }
+ else {
+ ERROR_IF(err < 0, error);
+ }
+ }
+ }
+ }
+
inst(POP_JUMP_IF_NOT_NONE, (value -- )) {
if (!Py_IsNone(value)) {
Py_DECREF(value);
@@ -1946,6 +1991,20 @@ dummy_func(
}
}
+ inst(BB_POP_BRANCH, (value--)) {
+ // oparg 1: POP_JUMP_IF_NOT_NONE
+ // oparg 0: POP_JUMP_IF_NONE
+ if (oparg - Py_IsNone(value)) {
+ Py_DECREF(value);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ }
+ else {
+ Py_DECREF(value);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
+ }
+
+ }
+
inst(JUMP_IF_FALSE_OR_POP, (cond -- cond if (jump))) {
bool jump = false;
int err;
@@ -1996,6 +2055,63 @@ dummy_func(
}
}
+ inst(BB_BRANCH_OR_POP, cond -- cond if (jump))) {
+ bool jump = false;
+ int err;
+ // JUMP_IF_FALSE_OR_POP
+ if (oparg) {
+ if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
+ }
+ else if (Py_IsFalse(cond)) {
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ jump = true;
+ }
+ else {
+ err = PyObject_IsTrue(cond);
+ if (err > 0) {
+ Py_DECREF(cond);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
+ }
+ else if (err == 0) {
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ jump = true;
+ }
+ else {
+ goto error;
+ }
+ }
+
+ }
+ else {
+ // JUMP_IF_TRUE_OR_POP
+ if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
+ }
+ else if (Py_IsTrue(cond)) {
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ jump = true;
+ }
+ else {
+ err = PyObject_IsTrue(cond);
+ if (err > 0) {
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ jump = true;
+ }
+ else if (err == 0) {
+ Py_DECREF(cond);
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
+ }
+ else {
+ goto error;
+ }
+ }
+ }
+
+ }
+
inst(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
@@ -2130,6 +2246,29 @@ dummy_func(
// Common case: no jump, leave it to the code generator
}
+ // FOR_ITER
+ inst(BB_ITER, (unused / 1, iter -- iter, next)) {
+ next = (*Py_TYPE(iter)->tp_iternext)(iter);
+ if (next == NULL) {
+ if (_PyErr_Occurred(tstate)) {
+ if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
+ goto error;
+ }
+ else if (tstate->c_tracefunc != NULL) {
+ call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
+ }
+ _PyErr_Clear(tstate);
+ }
+ /* iterator ended normally */
+ assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR);
+ Py_DECREF(iter);
+ STACK_SHRINK(1);
+ /* Jump forward oparg, then skip following END_FOR instruction */
+ next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ DISPATCH();
+ }
+ }
+
inst(FOR_ITER_LIST, (unused/1, iter -- iter, next)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER);
@@ -3150,6 +3289,13 @@ dummy_func(
Py_UNREACHABLE();
}
+ //// TIER 2 BASIC BLOCK INSTRUCTIONS
+ //inst(BB_TYPE_BRANCH, (--)) {
+ // if (should_bb_branch) {
+
+ // }
+ //}
+
// END BYTECODES //
diff --git a/Python/ceval.c b/Python/ceval.c
index a91f5baca8853e..adfc85a80c459b 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -732,6 +732,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_PyCFrame cframe;
_PyInterpreterFrame entry_frame;
PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions.
+ bool should_bb_branch = false;
/* WARNING: Because the _PyCFrame lives on the C stack,
* but can be accessed from a heap allocated object (tstate)
diff --git a/Python/tier2.c b/Python/tier2.c
index d84582d8027db6..5407298fa79b0d 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -13,32 +13,10 @@
// BB_BRANCH instruction: 1
#define BB_EPILOG 1
-static _Py_CODEUNIT *_PyCode_Tier2Initialize(_PyInterpreterFrame *, _Py_CODEUNIT *);
-
static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
////////// CEVAL functions
-// Tier 2 warmup counter
-_Py_CODEUNIT *
-_PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
-{
- PyCodeObject *code = frame->f_code;
- if (code->_tier2_warmup != 0) {
- code->_tier2_warmup++;
- if (code->_tier2_warmup == 0) {
- // If it fails, due to lack of memory or whatever,
- // just fall back to the tier 1 interpreter.
- _Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
- return next_instr;
- if (next != NULL) {
- return next;
- }
- }
- }
- return next_instr;
-}
-
////////// Utility functions
// Checks that we have enough scratch space for the current code object. Else allocate more.
@@ -417,6 +395,9 @@ emit_i(_Py_CODEUNIT *write_curr, int opcode, int oparg)
return write_curr;
}
+// Note: we're copying over the actual caches to preserve information!
+// This way instructions that we can't type propagate over still stay
+// optimized.
static inline _Py_CODEUNIT *
copy_cache_entries(_Py_CODEUNIT *write_curr, _Py_CODEUNIT *cache, int n_entries)
{
@@ -447,7 +428,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
#define JUMPBY(x) i += x + 1; continue;
#define BASIC_PUSH(v) (*stack_pointer++ = (v))
#define BASIC_POP() (*--stack_pointer)
-#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); write_i = copy_cache_entries(write_i, curr+1, caches); break;
+#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); write_i = copy_cache_entries(write_i, curr+1, caches); i += caches; continue;
assert(co->_tier2_info != NULL);
// There are only two cases that a BB ends.
// 1. If there's a branch instruction / scope exit.
@@ -481,6 +462,9 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
_Py_CODEUNIT action;
switch (opcode) {
+ case COMPARE_AND_BRANCH:
+ opcode = COMPARE_OP;
+ DISPATCH();
//case LOAD_FAST:
// BASIC_PUSH(type_context[oparg]);
// DISPATCH();
@@ -543,8 +527,6 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
DISPATCH();
}
- i += caches;
-
}
end:
//#if BB_DEBUG
@@ -805,3 +787,61 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
PyMem_Free(bb_space);
return NULL;
}
+
+////////// CEVAL FUNCTIONS
+
+// Tier 2 warmup counter
+_Py_CODEUNIT *
+_PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
+{
+ PyCodeObject *code = frame->f_code;
+ if (code->_tier2_warmup != 0) {
+ code->_tier2_warmup++;
+ if (code->_tier2_warmup == 0) {
+ // If it fails, due to lack of memory or whatever,
+ // just fall back to the tier 1 interpreter.
+ _Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
+ return next_instr;
+ if (next != NULL) {
+ return next;
+ }
+ }
+ }
+ return next_instr;
+}
+
+// Executes successor/alternate BB, depending on direction
+// Lazily generates successive BBs when required.
+// direction: 1 for successor, 0 for alternative.
+_Py_CODEUNIT *
+_PyTier2BB_ExecuteNextBB(_PyInterpreterFrame *frame, _PyTier2BB **curr_bb, int direction)
+{
+ PyCodeObject *co = frame->f_code;
+ _PyTier2BB *curr = *curr_bb;
+ if (BB_SUC) {
+ if (curr->successor_bb == NULL) {
+ _PyTier2BB *succ = _PyTier2_Code_DetectAndEmitBB(co,
+ curr->tier1_end, curr->type_context_len, curr->type_context);
+ curr->successor_bb = succ;
+ *curr_bb = succ;
+ return succ->u_code;
+ }
+ else {
+ *curr_bb = curr->successor_bb;
+ return curr->successor_bb->u_code;
+ }
+ }
+ else {
+ if (curr->alternate_bb == NULL) {
+ _PyTier2BB *alt = _PyTier2_Code_DetectAndEmitBB(co,
+ curr->tier1_end, curr->type_context_len, curr->type_context);
+ curr->alternate_bb = alt;
+ *curr_bb = alt;
+ return alt->u_code;
+ }
+ else {
+ *curr_bb = curr->alternate_bb;
+ return curr->alternate_bb->u_code;
+ }
+ }
+}
From c4399022930c203e27ec2041772eaef51081d531 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 15 Feb 2023 00:14:17 +0800
Subject: [PATCH 026/280] Part 2: Drive execution of the BBs
---
Include/cpython/code.h | 36 +--
Include/internal/pycore_code.h | 7 +-
Include/internal/pycore_interp.h | 5 -
Include/internal/pycore_opcode.h | 29 +--
Include/opcode.h | 23 +-
Lib/opcode.py | 35 ++-
Objects/codeobject.c | 10 +-
Python/bytecodes.c | 198 ++++++++--------
Python/ceval.c | 4 +-
Python/generated_cases.c.h | 165 +++++++++++++
Python/opcode_metadata.h | 35 +++
Python/pystate.c | 4 -
Python/tier2.c | 391 ++++++++++++++-----------------
13 files changed, 555 insertions(+), 387 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 592843eedc724b..ddae5db731a31a 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -57,46 +57,36 @@ typedef struct {
//} _PyTier2IntermediateCode;
-// What the tier 2 interpreter executes
-typedef struct _PyTier2BB {
- struct _PyTier2BB *successor_bb;
- // The other BB to go should BB_BRANCH fail.
- struct _PyTier2BB *alternate_bb;
+// Tier 2 interpreter information
+typedef struct _PyTier2BBMetadata {
// Array of types. This corresponds to the fast locals array.
int type_context_len;
PyTypeObject **type_context;
- //// Stores the end pointer in the intermediate bytecode.
- //// So that when we hit BB_BRANCH, we know what's the next
- //// thing to generate.
- //_Py_CODEUNIT *intermediate_code_end;
_Py_CODEUNIT *tier1_end;
- // There's extra memory at the end of this.
- int n_instrs;
- _Py_CODEUNIT u_code[1];
-} _PyTier2BB;
-
-// Find the start of the BB from u_code.
-#define _PyTier2BB_FROM_UCODE(code) (_PyTier2BB *)(((char *)code) - offsetof(_PyTier2BB, u_code))
+} _PyTier2BBMetadata;
// Bump allocator for basic blocks (overallocated)
typedef struct _PyTier2BBSpace {
struct _PyTier2BBSpace *next;
- int max_capacity;
- // How much space has been consumed in bbs.
- int water_level;
+ // (in bytes)
+ Py_ssize_t max_capacity;
+ // How much space has been consumed in bbs. (in bytes)
+ Py_ssize_t water_level;
// There's extra memory at the end of this.
- _PyTier2BB bbs[1];
+ _Py_CODEUNIT u_code[1];
} _PyTier2BBSpace;
+#define _PyTier2BBSpace_NBYTES_USED(space) sizeof(_PyTier2BBSpace) + space->max_capacity
+
// Tier 2 info stored in the code object. Lazily allocated.
typedef struct _PyTier2Info {
- _PyTier2BB *_entry_bb; /* the tier 2 basic block to execute (if any) */
+ _Py_CODEUNIT *_entry_bb; /* the tier 2 basic block to execute (if any) */
_PyTier2BBSpace *_bb_space; /* linked list storing basic blocks */
//_PyTier2IntermediateCode *i_code; /* intermediate bytecode to generate tier 2 basic blocks */
// Keeps track of offset of jump targets (in number of codeunits)
// from co_code_adaptive.
- int jump_target_count;
- int *jump_targets;
+ int backward_jump_count;
+ int *backward_jump_offsets;
PyTypeObject **types_stack;
} _PyTier2Info;
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 35c958613cdaba..37cbdaa65bc862 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -92,6 +92,12 @@ typedef struct {
#define INLINE_CACHE_ENTRIES_FOR_ITER CACHE_ENTRIES(_PyForIterCache)
+typedef struct {
+ uint16_t offset[2];
+} _PyBBBranchCache;
+
+#define INLINE_CACHE_ENTRIES_BB_BRANCH CACHE_ENTRIES(_PyBBBranchCache)
+
// Borrowed references to common callables:
struct callable_cache {
PyObject *isinstance;
@@ -241,7 +247,6 @@ extern int _PyStaticCode_Init(PyCodeObject *co);
/* Tier 2 interpreter */
extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *, _Py_CODEUNIT *);
-extern _Py_CODEUNIT *_PyTier2BB_ExecuteNextBB(struct _PyInterpreterFrame *frame, _PyTier2BB **curr_bb, int direction);
#ifdef Py_STATS
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index 371e9071de67af..2c66da0f95d4bc 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -186,11 +186,6 @@ struct _is {
struct _Py_interp_cached_objects cached_objects;
struct _Py_interp_static_objects static_objects;
- // TIER 2 INTERPRETER INFORMATION
-
- // Bytecode scratchspace when emitting tier 2 bytecode.
- int tier2_bytecode_scratchsize;
- _Py_CODEUNIT *tier2_bytecode_scratch;
/* The following fields are here to avoid allocation during init.
The data is exposed through PyInterpreterState pointer fields.
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 7e7908d543683b..e35db6f12e41e1 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -397,23 +397,23 @@ static const char *const _PyOpcode_OpName[263] = {
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
- [BB_ENTER_FRAME] = "BB_ENTER_FRAME",
- [BB_EXIT_FRAME] = "BB_EXIT_FRAME",
- [BB_TYPE_BRANCH] = "BB_TYPE_BRANCH",
- [BB_ITER] = "BB_ITER",
+ [BB_BRANCH] = "BB_BRANCH",
+ [BB_BRANCH_IF_FLAG_UNSET] = "BB_BRANCH_IF_FLAG_UNSET",
+ [BB_BRANCH_IF_FLAG_SET] = "BB_BRANCH_IF_FLAG_SET",
+ [BB_TEST_ITER] = "BB_TEST_ITER",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
[CALL_INTRINSIC_1] = "CALL_INTRINSIC_1",
- [BB_BRANCH_OR_POP] = "BB_BRANCH_OR_POP",
- [BB_POP_THEN_BRANCH] = "BB_POP_THEN_BRANCH",
- [BB_POP_BRANCH] = "BB_POP_BRANCH",
+ [BB_TEST_IF_FALSE_OR_POP] = "BB_TEST_IF_FALSE_OR_POP",
+ [BB_TEST_IF_TRUE_OR_POP] = "BB_TEST_IF_TRUE_OR_POP",
+ [BB_TEST_POP_IF_FALSE] = "BB_TEST_POP_IF_FALSE",
+ [BB_TEST_POP_IF_TRUE] = "BB_TEST_POP_IF_TRUE",
+ [BB_TEST_POP_IF_NOT_NONE] = "BB_TEST_POP_IF_NOT_NONE",
+ [BB_TEST_POP_IF_NONE] = "BB_TEST_POP_IF_NONE",
+ [BB_JUMP_BACKWARD] = "BB_JUMP_BACKWARD",
+ [BB_JUMP_BACKWARD_LAZY] = "BB_JUMP_BACKWARD_LAZY",
[BINARY_CHECK_INT] = "BINARY_CHECK_INT",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
- [179] = "<179>",
- [180] = "<180>",
- [181] = "<181>",
- [182] = "<182>",
- [183] = "<183>",
[184] = "<184>",
[185] = "<185>",
[186] = "<186>",
@@ -498,11 +498,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 179: \
- case 180: \
- case 181: \
- case 182: \
- case 183: \
case 184: \
case 185: \
case 186: \
diff --git a/Include/opcode.h b/Include/opcode.h
index d8569fe97b31b9..45efe80f66bae6 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -253,15 +253,20 @@ extern "C" {
#define UNPACK_SEQUENCE_TUPLE 161
#define UNPACK_SEQUENCE_TWO_TUPLE 166
#define DO_TRACING 255
-#define BB_ENTER_FRAME 167
-#define BB_EXIT_FRAME 168
-#define BB_TYPE_BRANCH 169
-#define BB_ITER 170
-#define BB_BRANCH_OR_POP 174
-#define BB_POP_THEN_BRANCH 175
-#define BB_POP_BRANCH 176
-#define BINARY_CHECK_INT 177
-#define BINARY_OP_ADD_INT_REST 178
+#define BB_BRANCH 167
+#define BB_BRANCH_IF_FLAG_UNSET 168
+#define BB_BRANCH_IF_FLAG_SET 169
+#define BB_TEST_ITER 170
+#define BB_TEST_IF_FALSE_OR_POP 174
+#define BB_TEST_IF_TRUE_OR_POP 175
+#define BB_TEST_POP_IF_FALSE 176
+#define BB_TEST_POP_IF_TRUE 177
+#define BB_TEST_POP_IF_NOT_NONE 178
+#define BB_TEST_POP_IF_NONE 179
+#define BB_JUMP_BACKWARD 180
+#define BB_JUMP_BACKWARD_LAZY 181
+#define BINARY_CHECK_INT 182
+#define BINARY_OP_ADD_INT_REST 183
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 02384a86a8132c..a04a3b0070d77a 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -443,14 +443,33 @@ def pseudo_op(name, op, real_ops):
]
_uops = [
# Tier 2 BB opcodes
- 'BB_ENTER_FRAME',
- 'BB_EXIT_FRAME',
- ## These branches correspond to the jump instructions
- 'BB_TYPE_BRANCH',
- 'BB_ITER', # FOR_ITER's null (iterator) check
- 'BB_BRANCH_OR_POP', # JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP
- 'BB_POP_THEN_BRANCH', # POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE
- 'BB_POP_BRANCH', # POP_JUMP_IF_NOT_NONE, POP_JUMP_IF_NONE
+ # Frame creation
+ # 'BB_ENTER_FRAME',
+ # 'BB_EXIT_FRAME',
+ # Initial generic branching instruction.
+ 'BB_BRANCH',
+ # The BB_BRANCH transitions to one of these two.
+ # This happens when the fall through is generated, but not the other branch.
+ 'BB_BRANCH_IF_FLAG_UNSET',
+ 'BB_BRANCH_IF_FLAG_SET',
+ # The final form is that once both branches are generated, we can just
+ # override these instructions with a generic JUMP.
+
+ # These tests correspond to the jump instructions
+ # FOR_ITER's null (iterator) check
+ 'BB_TEST_ITER',
+ # JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP
+ 'BB_TEST_IF_FALSE_OR_POP',
+ 'BB_TEST_IF_TRUE_OR_POP',
+ # POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE
+ 'BB_TEST_POP_IF_FALSE',
+ 'BB_TEST_POP_IF_TRUE',
+ # POP_JUMP_IF_NOT_NONE, POP_JUMP_IF_NONE
+ 'BB_TEST_POP_IF_NOT_NONE',
+ 'BB_TEST_POP_IF_NONE',
+ # JUMP_BACKWARD
+ 'BB_JUMP_BACKWARD',
+ 'BB_JUMP_BACKWARD_LAZY',
# Common type checks
# These instructions check that one operand is a certain type.
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index bdc31b6216304f..8e2b20e2dd7da4 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -1683,16 +1683,16 @@ code_tier2_fini(PyCodeObject *co)
t2_info->_bb_space = NULL;
}
- if (t2_info->jump_target_count > 0 &&
- t2_info->jump_targets != NULL) {
- PyMem_Free(t2_info->jump_targets);
- t2_info->jump_targets = NULL;
+ if (t2_info->backward_jump_count > 0 &&
+ t2_info->backward_jump_offsets != NULL) {
+ PyMem_Free(t2_info->backward_jump_offsets);
+ t2_info->backward_jump_offsets = NULL;
}
if (t2_info->types_stack != NULL) {
PyMem_Free(t2_info->types_stack);
t2_info->types_stack = NULL;
}
- t2_info->jump_target_count = 0;
+ t2_info->backward_jump_count = 0;
PyMem_Free(t2_info);
//if (t2_info->i_code != NULL) {
// if (t2_info->i_code->_jump_targets != NULL) {
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 8216d4fc636915..74cf30ba8b3cab 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1906,6 +1906,27 @@ dummy_func(
}
}
+ inst(BB_TEST_POP_IF_FALSE, (cond -- )) {
+ if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = true;
+ }
+ else if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = false;
+ }
+ else {
+ int err = PyObject_IsTrue(cond);
+ Py_DECREF(cond);
+ if (err == 0) {
+ bb_test = false;
+ }
+ else {
+ ERROR_IF(err < 0, error);
+ }
+ }
+ }
+
inst(POP_JUMP_IF_TRUE, (cond -- )) {
if (Py_IsFalse(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
@@ -1926,51 +1947,28 @@ dummy_func(
}
}
- inst(BB_POP_THEN_BRANCH, (cond -- )) {
- // POP_JUMP_IF_FALSE
- if (oparg) {
- if (Py_IsTrue(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
- }
- else if (Py_IsFalse(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
- }
- else {
- int err = PyObject_IsTrue(cond);
- Py_DECREF(cond);
- if (err == 0) {
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
- }
- else {
- ERROR_IF(err < 0, error);
- }
- }
+ inst(BB_TEST_POP_IF_TRUE, (cond -- )) {
+ if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = true;
+ }
+ else if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = false;
}
- // POP_JUMP_IF_TRUE
else {
- if (Py_IsFalse(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
- }
- else if (Py_IsTrue(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ int err = PyObject_IsTrue(cond);
+ Py_DECREF(cond);
+ if (err > 0) {
+ bb_test = false;
}
else {
- int err = PyObject_IsTrue(cond);
- Py_DECREF(cond);
- if (err > 0) {
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
- }
- else {
- ERROR_IF(err < 0, error);
- }
+ ERROR_IF(err < 0, error);
}
}
}
+
inst(POP_JUMP_IF_NOT_NONE, (value -- )) {
if (!Py_IsNone(value)) {
Py_DECREF(value);
@@ -1981,6 +1979,17 @@ dummy_func(
}
}
+ inst(BB_TEST_POP_IF_NOT_NONE, (value -- )) {
+ if (!Py_IsNone(value)) {
+ Py_DECREF(value);
+ bb_test = false;
+ }
+ else {
+ _Py_DECREF_NO_DEALLOC(value);
+ bb_test = true;
+ }
+ }
+
inst(POP_JUMP_IF_NONE, (value -- )) {
if (Py_IsNone(value)) {
_Py_DECREF_NO_DEALLOC(value);
@@ -1991,18 +2000,15 @@ dummy_func(
}
}
- inst(BB_POP_BRANCH, (value--)) {
- // oparg 1: POP_JUMP_IF_NOT_NONE
- // oparg 0: POP_JUMP_IF_NONE
- if (oparg - Py_IsNone(value)) {
- Py_DECREF(value);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ inst(BB_TEST_POP_IF_NONE, (value -- )) {
+ if (Py_IsNone(value)) {
+ _Py_DECREF_NO_DEALLOC(value);
+ bb_test = false;
}
else {
Py_DECREF(value);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
+ bb_test = true;
}
-
}
inst(JUMP_IF_FALSE_OR_POP, (cond -- cond if (jump))) {
@@ -2030,6 +2036,33 @@ dummy_func(
}
}
+ inst(BB_TEST_IF_FALSE_OR_POP, (cond -- cond if (jump))) {
+ bool jump = false;
+ int err;
+ if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = true;
+ }
+ else if (Py_IsFalse(cond)) {
+ bb_test = false;
+ jump = true;
+ }
+ else {
+ err = PyObject_IsTrue(cond);
+ if (err > 0) {
+ bb_test = true;
+ Py_DECREF(cond);
+ }
+ else if (err == 0) {
+ bb_test = false;
+ jump = true;
+ }
+ else {
+ goto error;
+ }
+ }
+ }
+
inst(JUMP_IF_TRUE_OR_POP, (cond -- cond if (jump))) {
bool jump = false;
int err;
@@ -2055,61 +2088,31 @@ dummy_func(
}
}
- inst(BB_BRANCH_OR_POP, cond -- cond if (jump))) {
+ inst(BB_TEST_IF_TRUE_OR_POP, (cond -- cond if (jump))) {
bool jump = false;
int err;
- // JUMP_IF_FALSE_OR_POP
- if (oparg) {
- if (Py_IsTrue(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
- }
- else if (Py_IsFalse(cond)) {
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
- jump = true;
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- Py_DECREF(cond);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
- }
- else if (err == 0) {
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
- jump = true;
- }
- else {
- goto error;
- }
- }
-
+ if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = true;
+ }
+ else if (Py_IsTrue(cond)) {
+ bb_test = false;
+ jump = true;
}
else {
- // JUMP_IF_TRUE_OR_POP
- if (Py_IsFalse(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
- }
- else if (Py_IsTrue(cond)) {
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
+ err = PyObject_IsTrue(cond);
+ if (err > 0) {
+ bb_test = false;
jump = true;
}
+ else if (err == 0) {
+ bb_test = true;
+ Py_DECREF(cond);
+ }
else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
- jump = true;
- }
- else if (err == 0) {
- Py_DECREF(cond);
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_SUC);
- }
- else {
- goto error;
- }
+ goto error;
}
}
-
}
inst(JUMP_BACKWARD_NO_INTERRUPT, (--)) {
@@ -2247,7 +2250,7 @@ dummy_func(
}
// FOR_ITER
- inst(BB_ITER, (unused / 1, iter -- iter, next)) {
+ inst(BB_TEST_ITER, (unused / 1, iter -- iter, next)) {
next = (*Py_TYPE(iter)->tp_iternext)(iter);
if (next == NULL) {
if (_PyErr_Occurred(tstate)) {
@@ -2263,10 +2266,9 @@ dummy_func(
assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR);
Py_DECREF(iter);
STACK_SHRINK(1);
- /* Jump forward oparg, then skip following END_FOR instruction */
- next_instr = _PyTier2BB_ExecuteNextBB(frame, &curr_bb, BB_ALT);
- DISPATCH();
+ bb_test = false;
}
+ bb_test = true;
}
inst(FOR_ITER_LIST, (unused/1, iter -- iter, next)) {
@@ -3289,12 +3291,6 @@ dummy_func(
Py_UNREACHABLE();
}
- //// TIER 2 BASIC BLOCK INSTRUCTIONS
- //inst(BB_TYPE_BRANCH, (--)) {
- // if (should_bb_branch) {
-
- // }
- //}
// END BYTECODES //
diff --git a/Python/ceval.c b/Python/ceval.c
index adfc85a80c459b..ca8f2705f688f9 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -732,7 +732,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_PyCFrame cframe;
_PyInterpreterFrame entry_frame;
PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions.
- bool should_bb_branch = false;
+ // true = successor
+ // false = alternate
+ bool bb_test = true;
/* WARNING: Because the _PyCFrame lives on the C stack,
* but can be accessed from a heap allocated object (tstate)
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 86125e4a5c3347..0fe44f7c663d40 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2447,6 +2447,30 @@
DISPATCH();
}
+ TARGET(BB_TEST_POP_IF_FALSE) {
+ PyObject *cond = PEEK(1);
+ if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = true;
+ }
+ else if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = false;
+ }
+ else {
+ int err = PyObject_IsTrue(cond);
+ Py_DECREF(cond);
+ if (err == 0) {
+ bb_test = false;
+ }
+ else {
+ if (err < 0) goto pop_1_error;
+ }
+ }
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
TARGET(POP_JUMP_IF_TRUE) {
PyObject *cond = PEEK(1);
if (Py_IsFalse(cond)) {
@@ -2470,6 +2494,30 @@
DISPATCH();
}
+ TARGET(BB_TEST_POP_IF_TRUE) {
+ PyObject *cond = PEEK(1);
+ if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = true;
+ }
+ else if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = false;
+ }
+ else {
+ int err = PyObject_IsTrue(cond);
+ Py_DECREF(cond);
+ if (err > 0) {
+ bb_test = false;
+ }
+ else {
+ if (err < 0) goto pop_1_error;
+ }
+ }
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
TARGET(POP_JUMP_IF_NOT_NONE) {
PyObject *value = PEEK(1);
if (!Py_IsNone(value)) {
@@ -2483,6 +2531,20 @@
DISPATCH();
}
+ TARGET(BB_TEST_POP_IF_NOT_NONE) {
+ PyObject *value = PEEK(1);
+ if (!Py_IsNone(value)) {
+ Py_DECREF(value);
+ bb_test = false;
+ }
+ else {
+ _Py_DECREF_NO_DEALLOC(value);
+ bb_test = true;
+ }
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
TARGET(POP_JUMP_IF_NONE) {
PyObject *value = PEEK(1);
if (Py_IsNone(value)) {
@@ -2496,6 +2558,20 @@
DISPATCH();
}
+ TARGET(BB_TEST_POP_IF_NONE) {
+ PyObject *value = PEEK(1);
+ if (Py_IsNone(value)) {
+ _Py_DECREF_NO_DEALLOC(value);
+ bb_test = false;
+ }
+ else {
+ Py_DECREF(value);
+ bb_test = true;
+ }
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
TARGET(JUMP_IF_FALSE_OR_POP) {
PyObject *cond = PEEK(1);
bool jump = false;
@@ -2525,6 +2601,37 @@
DISPATCH();
}
+ TARGET(BB_TEST_IF_FALSE_OR_POP) {
+ PyObject *cond = PEEK(1);
+ bool jump = false;
+ int err;
+ if (Py_IsTrue(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = true;
+ }
+ else if (Py_IsFalse(cond)) {
+ bb_test = false;
+ jump = true;
+ }
+ else {
+ err = PyObject_IsTrue(cond);
+ if (err > 0) {
+ bb_test = true;
+ Py_DECREF(cond);
+ }
+ else if (err == 0) {
+ bb_test = false;
+ jump = true;
+ }
+ else {
+ goto error;
+ }
+ }
+ STACK_SHRINK(1);
+ STACK_GROW((jump ? 1 : 0));
+ DISPATCH();
+ }
+
TARGET(JUMP_IF_TRUE_OR_POP) {
PyObject *cond = PEEK(1);
bool jump = false;
@@ -2554,6 +2661,37 @@
DISPATCH();
}
+ TARGET(BB_TEST_IF_TRUE_OR_POP) {
+ PyObject *cond = PEEK(1);
+ bool jump = false;
+ int err;
+ if (Py_IsFalse(cond)) {
+ _Py_DECREF_NO_DEALLOC(cond);
+ bb_test = true;
+ }
+ else if (Py_IsTrue(cond)) {
+ bb_test = false;
+ jump = true;
+ }
+ else {
+ err = PyObject_IsTrue(cond);
+ if (err > 0) {
+ bb_test = false;
+ jump = true;
+ }
+ else if (err == 0) {
+ bb_test = true;
+ Py_DECREF(cond);
+ }
+ else {
+ goto error;
+ }
+ }
+ STACK_SHRINK(1);
+ STACK_GROW((jump ? 1 : 0));
+ DISPATCH();
+ }
+
TARGET(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
@@ -2721,6 +2859,33 @@
DISPATCH();
}
+ TARGET(BB_TEST_ITER) {
+ PyObject *iter = PEEK(1);
+ PyObject *next;
+ next = (*Py_TYPE(iter)->tp_iternext)(iter);
+ if (next == NULL) {
+ if (_PyErr_Occurred(tstate)) {
+ if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
+ goto error;
+ }
+ else if (tstate->c_tracefunc != NULL) {
+ call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
+ }
+ _PyErr_Clear(tstate);
+ }
+ /* iterator ended normally */
+ assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR);
+ Py_DECREF(iter);
+ STACK_SHRINK(1);
+ bb_test = false;
+ }
+ bb_test = true;
+ STACK_GROW(1);
+ POKE(1, next);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
TARGET(FOR_ITER_LIST) {
PyObject *iter = PEEK(1);
PyObject *next;
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index d4ac2d2b43ecd2..424f78f657bdaa 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -242,16 +242,28 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 0;
case POP_JUMP_IF_FALSE:
return 1;
+ case BB_TEST_POP_IF_FALSE:
+ return 1;
case POP_JUMP_IF_TRUE:
return 1;
+ case BB_TEST_POP_IF_TRUE:
+ return 1;
case POP_JUMP_IF_NOT_NONE:
return 1;
+ case BB_TEST_POP_IF_NOT_NONE:
+ return 1;
case POP_JUMP_IF_NONE:
return 1;
+ case BB_TEST_POP_IF_NONE:
+ return 1;
case JUMP_IF_FALSE_OR_POP:
return 1;
+ case BB_TEST_IF_FALSE_OR_POP:
+ return 1;
case JUMP_IF_TRUE_OR_POP:
return 1;
+ case BB_TEST_IF_TRUE_OR_POP:
+ return 1;
case JUMP_BACKWARD_NO_INTERRUPT:
return 0;
case GET_LEN:
@@ -270,6 +282,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 1;
case FOR_ITER:
return 1;
+ case BB_TEST_ITER:
+ return 1;
case FOR_ITER_LIST:
return 1;
case FOR_ITER_TUPLE:
@@ -596,16 +610,28 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case POP_JUMP_IF_FALSE:
return 0;
+ case BB_TEST_POP_IF_FALSE:
+ return 0;
case POP_JUMP_IF_TRUE:
return 0;
+ case BB_TEST_POP_IF_TRUE:
+ return 0;
case POP_JUMP_IF_NOT_NONE:
return 0;
+ case BB_TEST_POP_IF_NOT_NONE:
+ return 0;
case POP_JUMP_IF_NONE:
return 0;
+ case BB_TEST_POP_IF_NONE:
+ return 0;
case JUMP_IF_FALSE_OR_POP:
return (jump ? 1 : 0);
+ case BB_TEST_IF_FALSE_OR_POP:
+ return (jump ? 1 : 0);
case JUMP_IF_TRUE_OR_POP:
return (jump ? 1 : 0);
+ case BB_TEST_IF_TRUE_OR_POP:
+ return (jump ? 1 : 0);
case JUMP_BACKWARD_NO_INTERRUPT:
return 0;
case GET_LEN:
@@ -624,6 +650,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 1;
case FOR_ITER:
return 2;
+ case BB_TEST_ITER:
+ return 2;
case FOR_ITER_LIST:
return 2;
case FOR_ITER_TUPLE:
@@ -836,11 +864,17 @@ struct opcode_metadata {
[JUMP_FORWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[JUMP_BACKWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[POP_JUMP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_TEST_POP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[POP_JUMP_IF_TRUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_TEST_POP_IF_TRUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[POP_JUMP_IF_NOT_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_TEST_POP_IF_NOT_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[POP_JUMP_IF_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_TEST_POP_IF_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[JUMP_IF_FALSE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_TEST_IF_FALSE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[JUMP_IF_TRUE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_TEST_IF_TRUE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[JUMP_BACKWARD_NO_INTERRUPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[GET_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[MATCH_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
@@ -850,6 +884,7 @@ struct opcode_metadata {
[GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
+ [BB_TEST_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
diff --git a/Python/pystate.c b/Python/pystate.c
index d79ba19bf09bff..1261092d1435fa 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -643,10 +643,6 @@ init_interpreter(PyInterpreterState *interp,
PyConfig_InitPythonConfig(&interp->config);
_PyType_InitCache(interp);
- // Tier 2 interpreter information
- interp->tier2_bytecode_scratchsize = 0;
- interp->tier2_bytecode_scratch = NULL;
-
interp->_initialized = 1;
}
diff --git a/Python/tier2.c b/Python/tier2.c
index 5407298fa79b0d..406cf340f10cd5 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -10,43 +10,29 @@
#define BB_DEBUG 1
// Number of potential extra instructions at end of a BB, for branch or cleanup purposes.
-// BB_BRANCH instruction: 1
-#define BB_EPILOG 1
+#define BB_EPILOG 0
static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
-////////// CEVAL functions
-
-////////// Utility functions
+////////// TYPE CONTEXT FUNCTIONS
-// Checks that we have enough scratch space for the current code object. Else allocate more.
-static _Py_CODEUNIT *
-_PyInterpreter_GetScratchSpace(PyCodeObject *co)
-{
- PyInterpreterState *interp = _PyInterpreterState_GET();
- int n_instrs = (int)Py_SIZE(co);
- Py_ssize_t space_to_alloc = _PyCode_NBYTES(co);
- int overalloc = 2;
- if (interp->tier2_bytecode_scratchsize == 0 ||
- interp->tier2_bytecode_scratch == NULL) {
- interp->tier2_bytecode_scratch = PyMem_Malloc(space_to_alloc * overalloc);
- if (interp->tier2_bytecode_scratch == NULL) {
- return NULL;
- }
- interp->tier2_bytecode_scratchsize = Py_SIZE(co) * overalloc;
- return interp->tier2_bytecode_scratch;
+static PyTypeObject **
+initialize_type_context(PyCodeObject *co, int *type_context_len) {
+ int nlocals = co->co_nlocals;
+ PyTypeObject **type_context = PyMem_Malloc(nlocals * sizeof(PyTypeObject *));
+ if (type_context == NULL) {
+ return NULL;
}
- if (interp->tier2_bytecode_scratchsize < n_instrs) {
- PyMem_Free(interp->tier2_bytecode_scratch);
- interp->tier2_bytecode_scratch = PyMem_Malloc(space_to_alloc * overalloc);
- if (interp->tier2_bytecode_scratch == NULL) {
- return NULL;
- }
- interp->tier2_bytecode_scratchsize = Py_SIZE(co) * overalloc;
+ // Initialize to uknown type.
+ for (int i = 0; i < nlocals; i++) {
+ type_context[i] = NULL;
}
- return interp->tier2_bytecode_scratch;
+ *type_context_len = nlocals;
+ return type_context;
}
+////////// Utility functions
+
// Gets end of the bytecode for a code object.
_Py_CODEUNIT *
_PyCode_GetEnd(PyCodeObject *co)
@@ -73,12 +59,12 @@ _PyCode_GetLogicalEnd(PyCodeObject *co)
return end;
}
-// Gets end of the bytecode for a tier 2 BB.
-_Py_CODEUNIT *
-_PyTier2BB_UCodeEnd(_PyTier2BB *bb)
-{
- return (_Py_CODEUNIT *)(bb->u_code + bb->n_instrs);
-}
+//// Gets end of the bytecode for a tier 2 BB.
+//_Py_CODEUNIT *
+//_PyTier2BB_UCodeEnd(_PyTier2BB *bb)
+//{
+// return (_Py_CODEUNIT *)(bb->u_code + bb->n_instrs);
+//}
////////// BB SPACE FUNCTIONS
@@ -92,63 +78,60 @@ _PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
}
bb_space->next = NULL;
bb_space->water_level = 0;
- assert((int)(space_to_alloc - sizeof(_PyTier2BB)) == (space_to_alloc - sizeof(_PyTier2BB)));
- bb_space->max_capacity = (int)(space_to_alloc - sizeof(_PyTier2BB));
+ bb_space->max_capacity = (space_to_alloc - sizeof(_PyTier2BBSpace));
return bb_space;
}
-////////// TIER 2 BB FUNCTIONS
-
-/* Init a BB in BB space without any checks for waterlevel. */
-static _PyTier2BB *
-_PyTier2_InitBBNoCheck(_PyTier2BBSpace *bb_space, _Py_CODEUNIT *tier1_end,
- _Py_CODEUNIT *instr_start, _Py_CODEUNIT *instr_end)
-{
- Py_ssize_t ninstrs = (instr_end - instr_start) + BB_EPILOG;
- assert(ninstrs == (int)ninstrs);
- Py_ssize_t nbytes = ninstrs * sizeof(_Py_CODEUNIT);
- _PyTier2BB *bb_ptr = &bb_space->bbs[bb_space->water_level];
- bb_ptr->tier1_end = tier1_end;
- bb_ptr->n_instrs = (int)ninstrs;
- bb_ptr->successor_bb = NULL;
- bb_ptr->alternate_bb = NULL;
- // @TODO, add type context.
- memcpy(bb_ptr->u_code, (const void *)instr_start, nbytes);
- assert(bb_space->water_level + nbytes == (int)nbytes);
- bb_space->water_level += (int)nbytes;
- return bb_ptr;
-}
-
-/* Allocates and initializes a new basic block. If there's not enough space in
- the overallocated array, create a new array.
-
- Make sure to call _PyCode_Tier2Initialize before this!
-*/
-static _PyTier2BB *
-_PyCode_Tier2BBNew(PyCodeObject *co, _Py_CODEUNIT *tier1_end, _Py_CODEUNIT *instr_start, _Py_CODEUNIT *instr_end)
+static _PyTier2BBSpace *
+_PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_requested)
{
assert(co->_tier2_info != NULL);
assert(co->_tier2_info->_bb_space != NULL);
-
- _PyTier2BBSpace *bb_space = co->_tier2_info->_bb_space;
- Py_ssize_t amount_to_alloc = (instr_start - instr_end) * sizeof(_Py_CODEUNIT *) + sizeof(_PyTier2BB);
- assert(bb_space->water_level + amount_to_alloc == (int)(bb_space->water_level + amount_to_alloc));
-
- // Need to allocate a new array.
- if (bb_space->water_level + amount_to_alloc > bb_space->max_capacity) {
- _PyTier2BBSpace *next_bb_space = _PyTier2_CreateBBSpace(bb_space->max_capacity + amount_to_alloc);
- if (next_bb_space == NULL) {
+ _PyTier2BBSpace *curr = co->_tier2_info->_bb_space;
+ // Note: overallocate
+ Py_ssize_t new_size = sizeof(_PyTier2BBSpace) + (curr->water_level + space_requested) * 2;
+ // Overflow or over max capacity
+ if (new_size < 0 || curr->water_level + space_requested > curr->max_capacity) {
+ // Try realloc first
+ if (PyMem_Realloc(curr, new_size) == NULL) {
+ // Too much trouble. just fall back to tier 1
+ //// Try malloc then memcpy if realloc fails (which it might for a large reallocation)
+ //_PyTier2BBSpace *new_space = PyMem_Malloc(new_size);
+ //if (new_space == NULL) {
+ // return NULL;
+ //}
+ //memcpy(new_space, curr, _PyTier2BBSpace_NBYTES_USED(curr));
+ //PyMem_Free(curr);
+ //co->_tier2_info->_bb_space = new_space;
+ //return new_space;
return NULL;
}
- next_bb_space->next = bb_space;
- // We want to make our bb_space point to the most recent one to get O(1) BB allocations.
- co->_tier2_info->_bb_space = next_bb_space;
- bb_space = next_bb_space;
}
- return _PyTier2_InitBBNoCheck(bb_space, tier1_end, instr_start, instr_end);
+ return curr;
}
+static _PyTier2BBMetadata *
+_PyTier2_AllocateBBMetaData(PyCodeObject *co, _Py_CODEUNIT *tier1_end)
+{
+ int type_context_len = 0;
+ PyTypeObject **type_context = initialize_type_context(co, &type_context_len);
+ if (type_context == NULL) {
+ return NULL;
+ }
+
+ _PyTier2BBMetadata *metadata = PyMem_Malloc(sizeof(_PyTier2BBMetadata));
+ if (metadata == NULL) {
+ PyMem_Free(type_context);
+ return NULL;
+ }
+
+ metadata->tier1_end = tier1_end;
+ metadata->type_context = type_context;
+ metadata->type_context_len = type_context_len;
+ return metadata;
+}
+
/* Opcode detection functions. Keep in sync with compile.c and dis! */
// dis.hasjabs
@@ -182,6 +165,13 @@ IS_JREL_OPCODE(int opcode)
}
}
+static inline int
+IS_JUMP_BACKWARDS_OPCODE(int opcode)
+{
+ return opcode == JUMP_BACKWARD_NO_INTERRUPT || opcode == JUMP_BACKWARD;
+}
+
+
// dis.hasjrel || dis.hasjabs
static inline int
IS_JUMP_OPCODE(int opcode)
@@ -316,35 +306,44 @@ emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard)
{
*write_curr = guard;
write_curr++;
- _py_set_opcode(write_curr, BB_TYPE_BRANCH);
+ _py_set_opcode(write_curr, BB_BRANCH);
write_curr++;
return write_curr;
}
static inline _Py_CODEUNIT *
-emit_logical_branch(_Py_CODEUNIT *write_curr, int branch)
+emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int code_offset)
{
int opcode;
- int oparg = 0;
+ int oparg = _Py_OPARG(branch);
// @TODO handle JUMP_BACKWARDS and JUMP_BACKWARDS_NO_INTERRUPT
- switch (branch) {
+ switch (_Py_OPCODE(branch)) {
+ case JUMP_BACKWARD:
+ // The initial backwards jump needs to find the right basic block.
+ // Subsequent jumps don't need to check this anymore. They can just
+ // jump directly with BB_JUMP_BACKWARD.
+ opcode = BB_JUMP_BACKWARD_LAZY;
+ break;
case FOR_ITER:
- opcode = BB_ITER;
+ opcode = BB_TEST_ITER;
break;
case JUMP_IF_FALSE_OR_POP:
- oparg = 1;
+ opcode = BB_TEST_IF_FALSE_OR_POP;
+ break;
case JUMP_IF_TRUE_OR_POP:
- opcode = BB_BRANCH_OR_POP;
+ opcode = BB_TEST_IF_TRUE_OR_POP;
break;
case POP_JUMP_IF_FALSE:
- oparg = 1;
+ opcode = BB_TEST_POP_IF_FALSE;
+ break;
case POP_JUMP_IF_TRUE:
- opcode = BB_POP_THEN_BRANCH;
+ opcode = BB_TEST_POP_IF_TRUE;
break;
case POP_JUMP_IF_NOT_NONE:
- oparg = 1;
+ opcode = BB_TEST_POP_IF_NOT_NONE;
+ break;
case POP_JUMP_IF_NONE:
- opcode = BB_POP_BRANCH;
+ opcode = BB_TEST_POP_IF_NONE;
break;
default:
// Honestly shouldn't happen because branches that
@@ -357,30 +356,46 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, int branch)
#if BB_DEBUG
fprintf(stderr, "emitted logical branch\n");
#endif
+ // We prefix with an empty EXTENDED_ARG, just in case the future jumps
+ // are not large enough to handle the bytecode format.
+ _py_set_opcode(write_curr, EXTENDED_ARG);
+ write_curr->oparg = 0;
+ write_curr++;
_py_set_opcode(write_curr, opcode);
write_curr->oparg = oparg;
write_curr++;
+ // Each guard also holds 2 CACHE entries. This stores an int32 of the
+ // offset from start of the code object (in _Py_CODEUNITs) that the current guard
+ // can generate the basic block from.
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
+ _py_set_opcode(write_curr, CACHE);
+ write_curr++;
+ _py_set_opcode(write_curr, CACHE);
+ write_curr++;
+ write_u32(cache->offset, (uint32_t)code_offset);
return write_curr;
}
static inline _Py_CODEUNIT *
-emit_scope_exit(_Py_CODEUNIT *write_curr, int exit)
+emit_scope_exit(_Py_CODEUNIT *write_curr, _Py_CODEUNIT exit)
{
- switch (exit) {
+ switch (_Py_OPCODE(exit)) {
case RETURN_VALUE:
case RETURN_CONST:
case INTERPRETER_EXIT:
#if BB_DEBUG
fprintf(stderr, "emitted scope exit\n");
#endif
- // @TODO we can propogate and chain BBs across call boundaries
- // Thanks to CPython's inlined call frames.
- _py_set_opcode(write_curr, BB_EXIT_FRAME);
- return write_curr + 1;
+ //// @TODO we can propogate and chain BBs across call boundaries
+ //// Thanks to CPython's inlined call frames.
+ //_py_set_opcode(write_curr, BB_EXIT_FRAME);
+ *write_curr = exit;
+ write_curr++;
+ return write_curr;
default:
// The rest are forbidden.
#if BB_DEBUG
- fprintf(stderr, "emit_scope_exit unreachable %d\n", exit);
+ fprintf(stderr, "emit_scope_exit unreachable %d\n", _Py_OPCODE(exit));
#endif
Py_UNREACHABLE();
}
@@ -410,7 +425,7 @@ copy_cache_entries(_Py_CODEUNIT *write_curr, _Py_CODEUNIT *cache, int n_entries)
}
// Detects a BB from the current instruction start to the end of the first basic block it sees.
-// Then emits the instructions for a _PyTier2BB.
+// Then emits the instructions into the bb space.
//
// Instructions emitted depend on the type_context.
// For example, if it sees a BINARY_ADD instruction, but it knows the two operands are already of
@@ -420,9 +435,9 @@ copy_cache_entries(_Py_CODEUNIT *write_curr, _Py_CODEUNIT *cache, int n_entries)
// and the basic block will end at the first of the chain.
//
// Note: a BB end also includes a type guard.
-_PyTier2BB *
+_PyTier2BBMetadata *
_PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
- int n_typecontext, PyTypeObject **type_context)
+ int n_typecontext, PyTypeObject **type_context, _Py_CODEUNIT *t2_start)
{
#define END() goto end;
#define JUMPBY(x) i += x + 1; continue;
@@ -439,9 +454,10 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
if (type_context_copy == NULL) {
return NULL;
}
+
memcpy(type_context_copy, type_context, n_typecontext * sizeof(PyTypeObject *));
- _Py_CODEUNIT *start_i = _PyInterpreter_GetScratchSpace(co);
+ _Py_CODEUNIT *start_i = t2_start;
_Py_CODEUNIT *write_i = start_i;
PyTypeObject **stack_pointer = co->_tier2_info->types_stack;
int tos = -1;
@@ -462,9 +478,9 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
_Py_CODEUNIT action;
switch (opcode) {
- case COMPARE_AND_BRANCH:
- opcode = COMPARE_OP;
- DISPATCH();
+ //case COMPARE_AND_BRANCH:
+ // opcode = COMPARE_OP;
+ // DISPATCH();
//case LOAD_FAST:
// BASIC_PUSH(type_context[oparg]);
// DISPATCH();
@@ -510,7 +526,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
// These are definitely the end of a basic block.
if (IS_SCOPE_EXIT_OPCODE(opcode)) {
// Emit the scope exit instruction.
- write_i = emit_scope_exit(write_i, opcode);
+ write_i = emit_scope_exit(write_i, instr);
END();
}
@@ -521,7 +537,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
// JUMP offset (oparg) + current instruction + cache entries
JUMPBY(oparg);
}
- write_i = emit_logical_branch(write_i, opcode);
+ write_i = emit_logical_branch(write_i, instr, i);
END();
}
DISPATCH();
@@ -533,32 +549,10 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
// fprintf(stderr, "i is %Id\n", i);
//#endif
// Create the tier 2 BB
- return _PyCode_Tier2BBNew(co, _PyCode_CODE(co) + i, start_i, write_i);
+ return _PyTier2_AllocateBBMetaData(co, _PyCode_CODE(co) + i);
}
-//// The exits of BBs need to use BB_BRANCH instruction.
-//static void
-//_PyTier2BB_RewriteExitBranch(_PyTier2BB *bb)
-//{
-// _Py_CODEUNIT *last_instr = _PyTier2BB_UCodeEnd(bb) - BB_EPILOG;
-// int op = _PyOpcode_Deopt[_Py_OPCODE(*last_instr)];
-// assert(IS_TERMINATOR_OPCODE(op));
-// if (IS_JUMP_OPCODE(op)) {
-// _Py_CODEUNIT lasti = *last_instr;
-// _py_set_opcode(last_instr, BB_BRANCH);
-// // Write the original jump instruction after to know where
-// // to start generating the next BB from.
-// _Py_SET_OPCODE
-// return;
-// }
-// assert(IS_SCOPE_EXIT_OPCODE(op));
-// assert(op == RETURN_VALUE || op == RETURN_CONST);
-// // Need to FRAME_EXIT.
-//
-//}
-
-
////////// _PyTier2Info FUNCTIONS
static int
@@ -579,7 +573,7 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
assert(co->_tier2_info != NULL);
// Remove all the RESUME instructions.
// Count all the jump targets.
- Py_ssize_t jump_target_count = 0;
+ Py_ssize_t backwards_jump_count = 0;
for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
_Py_CODEUNIT *instr_ptr = _PyCode_CODE(co) + i;
_Py_CODEUNIT instr = *instr_ptr;
@@ -592,26 +586,26 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
default:
// We want to track all guard instructions as
// jumps too.
- jump_target_count += IS_JUMP_OPCODE(opcode); // + INSTR_HAS_GUARD(instr);
+ backwards_jump_count += IS_JUMP_BACKWARDS_OPCODE(opcode); // + INSTR_HAS_GUARD(instr);
}
i += _PyOpcode_Caches[opcode];
}
// Impossibly big.
- if (jump_target_count != (int)jump_target_count) {
+ if (backwards_jump_count != (int)backwards_jump_count) {
return 1;
}
// Find all the jump target instructions
// Don't allocate a zero byte space as this may be undefined behavior.
- if (jump_target_count == 0) {
- co->_tier2_info->jump_targets = NULL;
+ if (backwards_jump_count == 0) {
+ co->_tier2_info->backward_jump_offsets = NULL;
// Successful (no jump targets)!
- co->_tier2_info->jump_target_count = (int)jump_target_count;
+ co->_tier2_info->backward_jump_count = (int)backwards_jump_count;
return 0;
}
- int *jump_targets = PyMem_Malloc(jump_target_count * sizeof(int));
- if (jump_targets == NULL) {
+ int *backward_jump_offsets = PyMem_Malloc(backwards_jump_count * sizeof(int));
+ if (backward_jump_offsets == NULL) {
return 1;
}
_Py_CODEUNIT *start = _PyCode_CODE(co);
@@ -621,36 +615,27 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
_Py_CODEUNIT instr = *curr;
int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
int oparg = _Py_OPARG(instr);
- //switch (opcode) {
- //// KEEP IN SYNC WITH INSTR_HAS_GUARD
- //case BINARY_OP:
- // if (oparg == NB_ADD) {
- // jump_targets[curr_i] = (int)(curr - start) + INLINE_CACHE_ENTRIES_BINARY_OP;
- // curr_i++;
- // }
- // break;
- //default:
- if (IS_JUMP_OPCODE(opcode)) {
+ if (IS_JUMP_BACKWARDS_OPCODE(opcode)) {
_Py_CODEUNIT *target = curr + _Py_OPARG(instr);
// (in terms of offset from start of co_code_adaptive)
- jump_targets[curr_i] = (int)(target - start);
+ backward_jump_offsets[curr_i] = (int)(target - start);
curr_i++;
}
//}
i += _PyOpcode_Caches[opcode];
}
- assert(curr_i == jump_target_count);
- qsort(jump_targets, jump_target_count, sizeof(int), compare_ints);
+ assert(curr_i == backwards_jump_count);
+ qsort(backward_jump_offsets, backwards_jump_count, sizeof(int), compare_ints);
#if BB_DEBUG
- fprintf(stderr, "JUMP TARGET COUNT: %lld\n", jump_target_count);
- fprintf(stderr, "JUMP TARGET OFFSETS (FROM START OF CODE): ");
- for (Py_ssize_t i = 0; i < jump_target_count; i++) {
- fprintf(stderr, "%d ,", jump_targets[i]);
+ fprintf(stderr, "BACKWARD JUMP COUNNT : %Id\n", backwards_jump_count);
+ fprintf(stderr, "BACKWARD JUMP TARGET OFFSETS (FROM START OF CODE): ");
+ for (Py_ssize_t i = 0; i < backwards_jump_count; i++) {
+ fprintf(stderr, "%d ,", backward_jump_offsets[i]);
}
fprintf(stderr, "\n");
#endif
- co->_tier2_info->jump_target_count = (int)jump_target_count;
- co->_tier2_info->jump_targets = jump_targets;
+ co->_tier2_info->backward_jump_count = (int)backwards_jump_count;
+ co->_tier2_info->backward_jump_offsets = backward_jump_offsets;
return 0;
}
@@ -697,21 +682,6 @@ _PyTier2Info_Initialize(PyCodeObject *co)
return t2_info;
}
-////////// TYPE CONTEXT FUNCTIONS
-
-static PyTypeObject **
-initialize_type_context(PyCodeObject *co) {
- int nlocals = co->co_nlocals;
- PyTypeObject **type_context = PyMem_Malloc(nlocals * sizeof(PyTypeObject *));
- if (type_context == NULL) {
- return NULL;
- }
- // Initialize to uknown type.
- for (int i = 0; i < nlocals; i++) {
- type_context[i] = NULL;
- }
- return type_context;
-}
////////// OVERALL TIER2 FUNCTIONS
@@ -737,10 +707,6 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
}
}
- if (_PyInterpreter_GetScratchSpace(co) == NULL) {
- return NULL;
- }
-
_PyTier2Info *t2_info = _PyTier2Info_Initialize(co);
if (t2_info == NULL) {
return NULL;
@@ -750,7 +716,7 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
fprintf(stderr, "INITIALIZING\n");
#endif
- Py_ssize_t space_to_alloc = (sizeof(_PyTier2BB) + _PyCode_NBYTES(co)) * 2;
+ Py_ssize_t space_to_alloc = (_PyCode_NBYTES(co)) * 3;
_PyTier2BBSpace *bb_space = _PyTier2_CreateBBSpace(space_to_alloc);
if (bb_space == NULL) {
@@ -763,24 +729,27 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
t2_info->_bb_space = bb_space;
- PyTypeObject **type_context = initialize_type_context(co);
+ int type_context_len = 0;
+ PyTypeObject **type_context = initialize_type_context(co, &type_context_len);
if (type_context == NULL) {
goto cleanup;
}
- _PyTier2BB *bb_ptr = _PyTier2_Code_DetectAndEmitBB(co, _PyCode_CODE(co), co->co_nlocals, type_context);
- if (bb_ptr == NULL) {
+ _Py_CODEUNIT *t2_code = bb_space->u_code;
+ _PyTier2BBMetadata *meta = _PyTier2_Code_DetectAndEmitBB(
+ co, _PyCode_CODE(co), type_context_len, type_context, t2_code);
+ if (meta == NULL) {
goto cleanup;
}
#if BB_DEBUG
- fprintf(stderr, "ENTRY BB END IS: %d\n", (int)(bb_ptr->tier1_end - _PyCode_CODE(co)));
+ fprintf(stderr, "ENTRY BB END IS: %d\n", (int)(meta->tier1_end - _PyCode_CODE(co)));
#endif
- t2_info->_entry_bb = bb_ptr;
+ t2_info->_entry_bb = t2_code;
// Set the starting instruction to the entry BB.
// frame->prev_instr = bb_ptr->u_code - 1;
- return bb_ptr->u_code;
+ return t2_code;
cleanup:
PyMem_Free(t2_info);
@@ -810,38 +779,34 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
return next_instr;
}
-// Executes successor/alternate BB, depending on direction
-// Lazily generates successive BBs when required.
-// direction: 1 for successor, 0 for alternative.
+//// Executes successor/alternate BB
+//// Lazily generates successive BBs when required.
+
_Py_CODEUNIT *
-_PyTier2BB_ExecuteNextBB(_PyInterpreterFrame *frame, _PyTier2BB **curr_bb, int direction)
+_PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, _Py_CODEUNIT *curr_tier1,
+ _Py_CODEUNIT *curr_tier2)
{
- PyCodeObject *co = frame->f_code;
- _PyTier2BB *curr = *curr_bb;
- if (BB_SUC) {
- if (curr->successor_bb == NULL) {
- _PyTier2BB *succ = _PyTier2_Code_DetectAndEmitBB(co,
- curr->tier1_end, curr->type_context_len, curr->type_context);
- curr->successor_bb = succ;
- *curr_bb = succ;
- return succ->u_code;
- }
- else {
- *curr_bb = curr->successor_bb;
- return curr->successor_bb->u_code;
- }
+ // Be a pessimist and assume we need to write the entire code into the BB.
+ _PyTier2BBSpace *space = _PyTier2_BBSpaceCheckAndReallocIfNeeded(
+ frame->f_code, _PyCode_NBYTES(frame->f_code));
+ if (space == NULL) {
+ // DEOPTIMIZE TO TIER 1?
+ return NULL;
}
- else {
- if (curr->alternate_bb == NULL) {
- _PyTier2BB *alt = _PyTier2_Code_DetectAndEmitBB(co,
- curr->tier1_end, curr->type_context_len, curr->type_context);
- curr->alternate_bb = alt;
- *curr_bb = alt;
- return alt->u_code;
- }
- else {
- *curr_bb = curr->alternate_bb;
- return curr->alternate_bb->u_code;
- }
+ // Write to the top of the space. That is automatically where the next instruction
+ // should execute.
+ // start writing at curr_tier2
+ int type_context_len = 0;
+ PyTypeObject **type_context = initialize_type_context(frame->f_code, &type_context_len);
+ if (type_context == NULL) {
+ return NULL;
+ }
+ _PyTier2BBMetadata *metadata = _PyTier2_Code_DetectAndEmitBB(
+ frame->f_code, curr_tier1, type_context_len,
+ type_context, curr_tier2);
+ if (metadata == NULL) {
+ PyMem_Free(type_context);
+ return NULL;
}
+ return curr_tier2;
}
From 94d89252bbe4a682de121b56dc7ea6cc6be6b52a Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 15 Feb 2023 22:49:21 +0800
Subject: [PATCH 027/280] Part 3: Add BB IDs
---
Include/cpython/code.h | 23 +++-
Include/internal/pycore_code.h | 4 +-
Objects/codeobject.c | 5 +
Python/tier2.c | 200 +++++++++++++++++++++++++--------
4 files changed, 184 insertions(+), 48 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index ddae5db731a31a..7cbadba66b1dd1 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -59,10 +59,15 @@ typedef struct {
// Tier 2 interpreter information
typedef struct _PyTier2BBMetadata {
+ // Index into _PyTier2Info->bb_data
+ int id;
// Array of types. This corresponds to the fast locals array.
int type_context_len;
PyTypeObject **type_context;
+ _Py_CODEUNIT *tier2_start;
_Py_CODEUNIT *tier1_end;
+ // Type stack state
+ PyTypeObject **types_stack;
} _PyTier2BBMetadata;
// Bump allocator for basic blocks (overallocated)
@@ -80,14 +85,26 @@ typedef struct _PyTier2BBSpace {
// Tier 2 info stored in the code object. Lazily allocated.
typedef struct _PyTier2Info {
- _Py_CODEUNIT *_entry_bb; /* the tier 2 basic block to execute (if any) */
- _PyTier2BBSpace *_bb_space; /* linked list storing basic blocks */
- //_PyTier2IntermediateCode *i_code; /* intermediate bytecode to generate tier 2 basic blocks */
+ /* the tier 2 basic block to execute (if any) */
+ _Py_CODEUNIT *_entry_bb;
+ _PyTier2BBSpace *_bb_space;
// Keeps track of offset of jump targets (in number of codeunits)
// from co_code_adaptive.
int backward_jump_count;
int *backward_jump_offsets;
+ // Each backward jump offset will have a corresponding array of _PyTier2BBMetadata *
+ // This allows us to find a suitable BB on a backward jump.
+ // So backward jump offset [1, 2, 3 ,4]
+ // will have [[BB_ID1, BB_ID2], [BB_ID3,], [], []]
+ // etc.
+ int *backward_jump_target_bb_ids;
PyTypeObject **types_stack;
+ // Max len of bb_data
+ int bb_data_len;
+ // Current index to write into in bb_data. Incremented after each write.
+ // This also assigns the BB ID.
+ int bb_data_curr;
+ _PyTier2BBMetadata **bb_data;
} _PyTier2Info;
// To avoid repeating ourselves in deepfreeze.py, all PyCodeObject members are
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 37cbdaa65bc862..87b1ae5b7ef72f 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -93,7 +93,9 @@ typedef struct {
#define INLINE_CACHE_ENTRIES_FOR_ITER CACHE_ENTRIES(_PyForIterCache)
typedef struct {
- uint16_t offset[2];
+ // Unique ID (for this code object) for this basic block. This indexes into
+ // the PyTier2Info bb_data field.
+ uint16_t bb_id;
} _PyBBBranchCache;
#define INLINE_CACHE_ENTRIES_BB_BRANCH CACHE_ENTRIES(_PyBBBranchCache)
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 8e2b20e2dd7da4..0a03fc44daff1a 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -418,6 +418,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
_Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME) {
entry_point++;
}
+
co->_co_firsttraceable = entry_point;
_PyCode_Quicken(co);
notify_code_watchers(PY_CODE_EVENT_CREATE, co);
@@ -1693,6 +1694,10 @@ code_tier2_fini(PyCodeObject *co)
t2_info->types_stack = NULL;
}
t2_info->backward_jump_count = 0;
+ if (t2_info->bb_data != NULL && t2_info->bb_data_len > 0) {
+ PyMem_Free(t2_info->bb_data);
+ }
+ t2_info->bb_data_len = 0;
PyMem_Free(t2_info);
//if (t2_info->i_code != NULL) {
// if (t2_info->i_code->_jump_targets != NULL) {
diff --git a/Python/tier2.c b/Python/tier2.c
index 406cf340f10cd5..4de86144270312 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -14,6 +14,7 @@
static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
+
////////// TYPE CONTEXT FUNCTIONS
static PyTypeObject **
@@ -90,48 +91,90 @@ _PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_reque
_PyTier2BBSpace *curr = co->_tier2_info->_bb_space;
// Note: overallocate
Py_ssize_t new_size = sizeof(_PyTier2BBSpace) + (curr->water_level + space_requested) * 2;
- // Overflow or over max capacity
- if (new_size < 0 || curr->water_level + space_requested > curr->max_capacity) {
- // Try realloc first
- if (PyMem_Realloc(curr, new_size) == NULL) {
- // Too much trouble. just fall back to tier 1
- //// Try malloc then memcpy if realloc fails (which it might for a large reallocation)
- //_PyTier2BBSpace *new_space = PyMem_Malloc(new_size);
- //if (new_space == NULL) {
- // return NULL;
- //}
- //memcpy(new_space, curr, _PyTier2BBSpace_NBYTES_USED(curr));
- //PyMem_Free(curr);
- //co->_tier2_info->_bb_space = new_space;
- //return new_space;
+ // Over max capacity
+ if (curr->water_level + space_requested > curr->max_capacity) {
+ _PyTier2BBSpace *new_space = PyMem_Realloc(curr, new_size);
+ if (new_space == NULL) {
return NULL;
}
}
return curr;
}
+// BB METADATA FUNCTIONS
static _PyTier2BBMetadata *
-_PyTier2_AllocateBBMetaData(PyCodeObject *co, _Py_CODEUNIT *tier1_end)
+allocate_bb_metadata(PyCodeObject *co, _Py_CODEUNIT *tier2_start,
+ _Py_CODEUNIT *tier1_end, int type_context_len,
+ PyTypeObject **type_context)
{
- int type_context_len = 0;
- PyTypeObject **type_context = initialize_type_context(co, &type_context_len);
- if (type_context == NULL) {
- return NULL;
- }
-
_PyTier2BBMetadata *metadata = PyMem_Malloc(sizeof(_PyTier2BBMetadata));
if (metadata == NULL) {
- PyMem_Free(type_context);
return NULL;
}
+ metadata->tier2_start = tier2_start;
metadata->tier1_end = tier1_end;
metadata->type_context = type_context;
metadata->type_context_len = type_context_len;
return metadata;
}
+static int
+update_backward_jump_target_bb_ids(PyCodeObject *co, _PyTier2BBMetadata *metadata)
+{
+ assert(co->_tier2_info != NULL);
+
+}
+
+// Writes BB metadata to code object's tier2info bb_data field.
+// 0 on success, 1 on error.
+static int
+write_bb_metadata(PyCodeObject *co, _PyTier2BBMetadata *metadata)
+{
+ assert(co->_tier2_info != NULL);
+ // Not enough space left in bb_data, allocate new one.
+ if (co->_tier2_info->bb_data == NULL ||
+ co->_tier2_info->bb_data_curr >= co->_tier2_info->bb_data_len) {
+ int new_len = (co->_tier2_info->bb_data_len + 1) * 2;
+ Py_ssize_t new_space = new_len * sizeof(_PyTier2BBMetadata *);
+ // Overflow
+ if (new_len < 0) {
+ return 1;
+ }
+ _PyTier2BBMetadata **new_data = PyMem_Realloc(co->_tier2_info->bb_data, new_space);
+ if (new_data == NULL) {
+ return 1;
+ }
+ co->_tier2_info->bb_data = new_data;
+ co->_tier2_info->bb_data_len = new_len;
+ }
+ int id = co->_tier2_info->bb_data_curr;
+ co->_tier2_info->bb_data[id] = metadata;
+ metadata->id = id;
+ co->_tier2_info->bb_data_curr++;
+ return 0;
+}
+
+static _PyTier2BBMetadata *
+_PyTier2_AllocateBBMetaData(PyCodeObject *co, _Py_CODEUNIT *tier2_start,
+ _Py_CODEUNIT *tier1_end, int type_context_len,
+ PyTypeObject **type_context)
+{
+ _PyTier2BBMetadata *meta = allocate_bb_metadata(co,
+ tier2_start, tier1_end, type_context_len, type_context);
+ if (meta == NULL) {
+ return NULL;
+ }
+ if (write_bb_metadata(co, meta)) {
+ PyMem_Free(meta);
+ return NULL;
+ }
+
+ return meta;
+
+}
+
/* Opcode detection functions. Keep in sync with compile.c and dis! */
// dis.hasjabs
@@ -312,7 +355,7 @@ emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard)
}
static inline _Py_CODEUNIT *
-emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int code_offset)
+emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
{
int opcode;
int oparg = _Py_OPARG(branch);
@@ -368,11 +411,12 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int code_offs
// offset from start of the code object (in _Py_CODEUNITs) that the current guard
// can generate the basic block from.
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
- _py_set_opcode(write_curr, CACHE);
- write_curr++;
- _py_set_opcode(write_curr, CACHE);
- write_curr++;
- write_u32(cache->offset, (uint32_t)code_offset);
+ for (int i = 0; i < INLINE_CACHE_ENTRIES_BB_BRANCH; i++) {
+ _py_set_opcode(write_curr, CACHE);
+ write_curr++;
+ }
+ assert((uint16_t)(bb_id) == bb_id);
+ cache->bb_id = (uint16_t)(bb_id);
return write_curr;
}
@@ -424,6 +468,24 @@ copy_cache_entries(_Py_CODEUNIT *write_curr, _Py_CODEUNIT *cache, int n_entries)
return write_curr;
}
+
+
+static int
+IS_BACKWARDS_JUMP_TARGET(PyCodeObject *co, _Py_CODEUNIT *curr)
+{
+ assert(co->_tier2_info != NULL);
+ int backward_jump_count = co->_tier2_info->backward_jump_count;
+ int *backward_jump_offsets = co->_tier2_info->backward_jump_offsets;
+ _Py_CODEUNIT *start = _PyCode_CODE(co);
+ // TODO: CHANGE TO BINARY SEARCH WHEN i > 40. For smaller values, linear search is quicker.
+ for (int i = 0; i < backward_jump_count; i++) {
+ if (curr == start + backward_jump_offsets[i]) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
// Detects a BB from the current instruction start to the end of the first basic block it sees.
// Then emits the instructions into the bb space.
//
@@ -455,10 +517,12 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
return NULL;
}
+ _PyTier2BBMetadata *meta = NULL;
+
+
memcpy(type_context_copy, type_context, n_typecontext * sizeof(PyTypeObject *));
- _Py_CODEUNIT *start_i = t2_start;
- _Py_CODEUNIT *write_i = start_i;
+ _Py_CODEUNIT *write_i = t2_start;
PyTypeObject **stack_pointer = co->_tier2_info->types_stack;
int tos = -1;
@@ -478,9 +542,10 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
_Py_CODEUNIT action;
switch (opcode) {
- //case COMPARE_AND_BRANCH:
- // opcode = COMPARE_OP;
- // DISPATCH();
+ // We need to rewrite the pseudo-branch instruction.
+ case COMPARE_AND_BRANCH:
+ opcode = COMPARE_OP;
+ DISPATCH();
//case LOAD_FAST:
// BASIC_PUSH(type_context[oparg]);
// DISPATCH();
@@ -523,6 +588,24 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
// }
//}
default:
+ if (IS_BACKWARDS_JUMP_TARGET(co, curr)) {
+ // Indicates it's the start a new basic block. That's fine. Just continue.
+ if (write_i == t2_start) {
+ DISPATCH();
+ }
+ // Not a start of a new basic block, we need to log this as a basic block
+ // and start a new BB for the sake of JUMP_BACKWARD.
+ i--;
+ meta = _PyTier2_AllocateBBMetaData(co, t2_start, _PyCode_CODE(co) + i, n_typecontext, type_context_copy);
+ if (meta == NULL) {
+ PyMem_Free(type_context_copy);
+ return NULL;
+ }
+ // Set the new start
+ t2_start = write_i;
+ // Don't increment, just fall through and let the new BB start with this
+ // instruction.
+ }
// These are definitely the end of a basic block.
if (IS_SCOPE_EXIT_OPCODE(opcode)) {
// Emit the scope exit instruction.
@@ -530,6 +613,8 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
END();
}
+
+
// Jumps may be the end of a basic block if they are conditional (a branch).
if (IS_JUMP_OPCODE(opcode)) {
// Unconditional forward jump... continue with the BB without writing the jump.
@@ -537,7 +622,9 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
// JUMP offset (oparg) + current instruction + cache entries
JUMPBY(oparg);
}
- write_i = emit_logical_branch(write_i, instr, i);
+ // Get the BB ID without incrementing it.
+ // AllocateBBMetaData will increment.
+ write_i = emit_logical_branch(write_i, instr, co->_tier2_info->bb_data_curr);
END();
}
DISPATCH();
@@ -549,7 +636,9 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
// fprintf(stderr, "i is %Id\n", i);
//#endif
// Create the tier 2 BB
- return _PyTier2_AllocateBBMetaData(co, _PyCode_CODE(co) + i);
+ meta = _PyTier2_AllocateBBMetaData(co, t2_start, _PyCode_CODE(co) + i, n_typecontext, type_context_copy);
+ return meta;
+
}
@@ -608,6 +697,14 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
if (backward_jump_offsets == NULL) {
return 1;
}
+ int *backward_jump_target_bb_ids = PyMem_Malloc(backwards_jump_count * sizeof(int));
+ if (backward_jump_target_bb_ids == NULL) {
+ PyMem_Free(backward_jump_offsets);
+ return 1;
+ }
+ for (int i = 0; i < backwards_jump_count; i++) {
+ backward_jump_target_bb_ids[i] = -1;
+ }
_Py_CODEUNIT *start = _PyCode_CODE(co);
int curr_i = 0;
for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
@@ -627,7 +724,7 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
assert(curr_i == backwards_jump_count);
qsort(backward_jump_offsets, backwards_jump_count, sizeof(int), compare_ints);
#if BB_DEBUG
- fprintf(stderr, "BACKWARD JUMP COUNNT : %Id\n", backwards_jump_count);
+ fprintf(stderr, "BACKWARD JUMP COUNT : %Id\n", backwards_jump_count);
fprintf(stderr, "BACKWARD JUMP TARGET OFFSETS (FROM START OF CODE): ");
for (Py_ssize_t i = 0; i < backwards_jump_count; i++) {
fprintf(stderr, "%d ,", backward_jump_offsets[i]);
@@ -636,6 +733,7 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
#endif
co->_tier2_info->backward_jump_count = (int)backwards_jump_count;
co->_tier2_info->backward_jump_offsets = backward_jump_offsets;
+ co->_tier2_info->backward_jump_target_bb_ids = backward_jump_target_bb_ids;
return 0;
}
@@ -667,18 +765,32 @@ _PyTier2Info_Initialize(PyCodeObject *co)
if (t2_info == NULL) {
return NULL;
}
- co->_tier2_info = t2_info;
-
- //if (_PyIntermediateCode_Initialize(co) == NULL) {
- // PyMem_FREE(t2_info);
- // return NULL;
- //}
// Next is to intitialize stack space for the tier 2 types meta-interpretr.
- t2_info->types_stack = PyMem_Malloc(co->co_stacksize * sizeof(PyObject *));
- if (t2_info->types_stack == NULL) {
+ PyTypeObject **types_stack = PyMem_Malloc(co->co_stacksize * sizeof(PyObject *));
+ if (types_stack == NULL) {
+ PyMem_Free(t2_info);
+ return NULL;
+ }
+ t2_info->types_stack = types_stack;
+ t2_info->backward_jump_count = 0;
+ t2_info->backward_jump_offsets = NULL;
+
+ // Initialize BB data array
+ t2_info->bb_data_len = 0;
+ t2_info->bb_data = NULL;
+ t2_info->bb_data_curr = 0;
+ int bb_data_len = (Py_SIZE(co) / 5 + 1);
+ _PyTier2Info **bb_data = PyMem_Malloc(bb_data_len * sizeof(_PyTier2Info *));
+ if (bb_data == NULL) {
PyMem_Free(t2_info);
+ PyMem_Free(types_stack);
+ return NULL;
}
+ t2_info->bb_data_len = bb_data_len;
+ t2_info->bb_data = bb_data;
+ co->_tier2_info = t2_info;
+
return t2_info;
}
From c7324b536984c3e688bbe5c0dd478ecad5da7d92 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 16 Feb 2023 01:39:39 +0800
Subject: [PATCH 028/280] Part 3.5: Cleanup and bugfixes
---
Include/cpython/code.h | 1 -
Include/internal/pycore_opcode.h | 3 +-
Include/opcode.h | 7 ++-
Lib/opcode.py | 1 -
Objects/codeobject.c | 8 ----
Python/tier2.c | 82 +++++++++++++-------------------
6 files changed, 39 insertions(+), 63 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 7cbadba66b1dd1..096552524d3089 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -72,7 +72,6 @@ typedef struct _PyTier2BBMetadata {
// Bump allocator for basic blocks (overallocated)
typedef struct _PyTier2BBSpace {
- struct _PyTier2BBSpace *next;
// (in bytes)
Py_ssize_t max_capacity;
// How much space has been consumed in bbs. (in bytes)
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index e35db6f12e41e1..64b9c8ad6a3139 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -410,10 +410,10 @@ static const char *const _PyOpcode_OpName[263] = {
[BB_TEST_POP_IF_TRUE] = "BB_TEST_POP_IF_TRUE",
[BB_TEST_POP_IF_NOT_NONE] = "BB_TEST_POP_IF_NOT_NONE",
[BB_TEST_POP_IF_NONE] = "BB_TEST_POP_IF_NONE",
- [BB_JUMP_BACKWARD] = "BB_JUMP_BACKWARD",
[BB_JUMP_BACKWARD_LAZY] = "BB_JUMP_BACKWARD_LAZY",
[BINARY_CHECK_INT] = "BINARY_CHECK_INT",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
+ [183] = "<183>",
[184] = "<184>",
[185] = "<185>",
[186] = "<186>",
@@ -498,6 +498,7 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
+ case 183: \
case 184: \
case 185: \
case 186: \
diff --git a/Include/opcode.h b/Include/opcode.h
index 45efe80f66bae6..bc02847eb1b460 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -263,10 +263,9 @@ extern "C" {
#define BB_TEST_POP_IF_TRUE 177
#define BB_TEST_POP_IF_NOT_NONE 178
#define BB_TEST_POP_IF_NONE 179
-#define BB_JUMP_BACKWARD 180
-#define BB_JUMP_BACKWARD_LAZY 181
-#define BINARY_CHECK_INT 182
-#define BINARY_OP_ADD_INT_REST 183
+#define BB_JUMP_BACKWARD_LAZY 180
+#define BINARY_CHECK_INT 181
+#define BINARY_OP_ADD_INT_REST 182
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index a04a3b0070d77a..f1b27532e3c4dc 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -468,7 +468,6 @@ def pseudo_op(name, op, real_ops):
'BB_TEST_POP_IF_NOT_NONE',
'BB_TEST_POP_IF_NONE',
# JUMP_BACKWARD
- 'BB_JUMP_BACKWARD',
'BB_JUMP_BACKWARD_LAZY',
# Common type checks
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 0a03fc44daff1a..5e3d1cd8e1c75b 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -1699,14 +1699,6 @@ code_tier2_fini(PyCodeObject *co)
}
t2_info->bb_data_len = 0;
PyMem_Free(t2_info);
- //if (t2_info->i_code != NULL) {
- // if (t2_info->i_code->_jump_targets != NULL) {
- // PyMem_Free(t2_info->i_code->_jump_targets);
- // t2_info->i_code->_jump_targets = NULL;
- // }
- // PyMem_Free(t2_info->i_code);
- // t2_info->i_code = NULL;
- //}
}
static void
diff --git a/Python/tier2.c b/Python/tier2.c
index 4de86144270312..1674de666a0aeb 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -60,12 +60,6 @@ _PyCode_GetLogicalEnd(PyCodeObject *co)
return end;
}
-//// Gets end of the bytecode for a tier 2 BB.
-//_Py_CODEUNIT *
-//_PyTier2BB_UCodeEnd(_PyTier2BB *bb)
-//{
-// return (_Py_CODEUNIT *)(bb->u_code + bb->n_instrs);
-//}
////////// BB SPACE FUNCTIONS
@@ -77,27 +71,34 @@ _PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
if (bb_space == NULL) {
return NULL;
}
- bb_space->next = NULL;
bb_space->water_level = 0;
bb_space->max_capacity = (space_to_alloc - sizeof(_PyTier2BBSpace));
return bb_space;
}
+// Checks if there's enough space in the BBSpace for space_requested.
+// Reallocates if neccessary.
+// DOES NOT ADJUST THE WATER LEVEL AS THIS IS JUST A CHECK. ONLY ADJUSTS THE MAX SPACE.
static _PyTier2BBSpace *
_PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_requested)
{
assert(co->_tier2_info != NULL);
assert(co->_tier2_info->_bb_space != NULL);
_PyTier2BBSpace *curr = co->_tier2_info->_bb_space;
- // Note: overallocate
- Py_ssize_t new_size = sizeof(_PyTier2BBSpace) + (curr->water_level + space_requested) * 2;
// Over max capacity
if (curr->water_level + space_requested > curr->max_capacity) {
+ // Note: overallocate
+ Py_ssize_t new_size = sizeof(_PyTier2BBSpace) + (curr->water_level + space_requested) * 2;
_PyTier2BBSpace *new_space = PyMem_Realloc(curr, new_size);
if (new_space == NULL) {
return NULL;
}
+ co->_tier2_info->_bb_space = new_space;
+ new_space->max_capacity = new_size;
+ PyMem_Free(curr);
+ return new_space;
}
+ // We have enouogh space. Don't do anything, j
return curr;
}
@@ -354,6 +355,7 @@ emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard)
return write_curr;
}
+// Converts the tier 1 branch bytecode to tier 2 branch bytecode.
static inline _Py_CODEUNIT *
emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
{
@@ -364,7 +366,7 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
case JUMP_BACKWARD:
// The initial backwards jump needs to find the right basic block.
// Subsequent jumps don't need to check this anymore. They can just
- // jump directly with BB_JUMP_BACKWARD.
+ // jump directly with JUMP_BACKWARD.
opcode = BB_JUMP_BACKWARD_LAZY;
break;
case FOR_ITER:
@@ -498,8 +500,9 @@ IS_BACKWARDS_JUMP_TARGET(PyCodeObject *co, _Py_CODEUNIT *curr)
//
// Note: a BB end also includes a type guard.
_PyTier2BBMetadata *
-_PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
- int n_typecontext, PyTypeObject **type_context, _Py_CODEUNIT *t2_start)
+_PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
+ _Py_CODEUNIT *start,
+ int n_typecontext, PyTypeObject **type_context)
{
#define END() goto end;
#define JUMPBY(x) i += x + 1; continue;
@@ -522,6 +525,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
memcpy(type_context_copy, type_context, n_typecontext * sizeof(PyTypeObject *));
+ _Py_CODEUNIT *t2_start = (_Py_CODEUNIT *)(((char *)bb_space->u_code) + bb_space->water_level);
_Py_CODEUNIT *write_i = t2_start;
PyTypeObject **stack_pointer = co->_tier2_info->types_stack;
int tos = -1;
@@ -637,6 +641,8 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _Py_CODEUNIT *start,
//#endif
// Create the tier 2 BB
meta = _PyTier2_AllocateBBMetaData(co, t2_start, _PyCode_CODE(co) + i, n_typecontext, type_context_copy);
+ // Tell BB space the number of bytes we wrote.
+ bb_space->water_level += (write_i - t2_start) * sizeof(_Py_CODEUNIT);
return meta;
}
@@ -650,12 +656,8 @@ compare_ints(const void *a, const void *b)
return *(int *)a - *(int *)b;
}
-// Returns 1 on error, 0 on success. Populates the jump target offset
-// array for a code object.
-// NOTE TO SELF: WE MIGHT NOT EVEN NEED TO FILL UP JUMP TARGETS.
-// JUMP TARGETS CONTINUE BEING PART OF THE BASIC BLOCK AS LONG
-// AS NO BRANCH IS DETECTED.
-// REMOVE IN THE FUTURE IF UNECESSARY.
+// Returns 1 on error, 0 on success. Populates the backwards jump target offset
+// array for a code object..
static int
_PyCode_Tier2FillJumpTargets(PyCodeObject *co)
{
@@ -718,11 +720,11 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
backward_jump_offsets[curr_i] = (int)(target - start);
curr_i++;
}
- //}
i += _PyOpcode_Caches[opcode];
}
assert(curr_i == backwards_jump_count);
- qsort(backward_jump_offsets, backwards_jump_count, sizeof(int), compare_ints);
+ qsort(backward_jump_offsets,backwards_jump_count,
+ sizeof(int), compare_ints);
#if BB_DEBUG
fprintf(stderr, "BACKWARD JUMP COUNT : %Id\n", backwards_jump_count);
fprintf(stderr, "BACKWARD JUMP TARGET OFFSETS (FROM START OF CODE): ");
@@ -737,24 +739,6 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
return 0;
}
-//static _PyTier2IntermediateCode *
-//_PyIntermediateCode_Initialize(PyCodeObject *co)
-//{
-// assert(co->_tier2_info != NULL);
-// Py_ssize_t space_to_alloc = (sizeof(PyCodeObject) + _PyCode_NBYTES(co)) + BB_EPILOG;
-// _PyTier2IntermediateCode *i_code = PyMem_Malloc(space_to_alloc);
-// if (i_code == NULL) {
-// return NULL;
-// }
-//
-// // Copy over bytecode
-// for (Py_ssize_t curr = 0; curr < Py_SIZE(co); curr++) {
-// _Py_CODEUNIT *curr_instr = _PyCode_CODE(co) + curr;
-// // NEED TO TRANSFORM BINARY OPS TO
-// i_code->code[curr] = *curr_instr;
-// }
-// return i_code;
-//}
static _PyTier2Info *
@@ -846,10 +830,11 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
if (type_context == NULL) {
goto cleanup;
}
- _Py_CODEUNIT *t2_code = bb_space->u_code;
_PyTier2BBMetadata *meta = _PyTier2_Code_DetectAndEmitBB(
- co, _PyCode_CODE(co), type_context_len, type_context, t2_code);
+ co, bb_space,
+ _PyCode_CODE(co), type_context_len, type_context);
if (meta == NULL) {
+ PyMem_Free(type_context);
goto cleanup;
}
#if BB_DEBUG
@@ -857,11 +842,11 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
#endif
- t2_info->_entry_bb = t2_code;
+ t2_info->_entry_bb = meta->tier2_start;
// Set the starting instruction to the entry BB.
// frame->prev_instr = bb_ptr->u_code - 1;
- return t2_code;
+ return meta->tier2_start;
cleanup:
PyMem_Free(t2_info);
@@ -891,14 +876,15 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
return next_instr;
}
-//// Executes successor/alternate BB
-//// Lazily generates successive BBs when required.
-
+// Lazily generates successive BBs when required.
+// The first basic block created will always be directly after the current tier 2 code.
+// The second basic block created will always require a jump.
_Py_CODEUNIT *
_PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, _Py_CODEUNIT *curr_tier1,
_Py_CODEUNIT *curr_tier2)
{
// Be a pessimist and assume we need to write the entire code into the BB.
+ // The size of the BB generated will definitely be equal to or smaller than this.
_PyTier2BBSpace *space = _PyTier2_BBSpaceCheckAndReallocIfNeeded(
frame->f_code, _PyCode_NBYTES(frame->f_code));
if (space == NULL) {
@@ -914,11 +900,11 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, _Py_CODEUNIT *curr_tier1,
return NULL;
}
_PyTier2BBMetadata *metadata = _PyTier2_Code_DetectAndEmitBB(
- frame->f_code, curr_tier1, type_context_len,
- type_context, curr_tier2);
+ frame->f_code, space, curr_tier1, type_context_len,
+ type_context);
if (metadata == NULL) {
PyMem_Free(type_context);
return NULL;
}
- return curr_tier2;
+ return metadata->tier2_start;
}
From 31504ad5262f24e0964dc0dfd6a1d62b4cfa03d2 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 17 Feb 2023 13:53:36 +0800
Subject: [PATCH 029/280] Fix bugs from upstream merge
---
Include/internal/pycore_opcode_macro_to_micro.h | 12 ++++++++++--
Tools/cases_generator/generate_cases.py | 1 +
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index ae2cc505738362..8131e548a5c578 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -51,6 +51,7 @@ extern const int _Py_MacroOpUOpCount[] = {
[STORE_SUBSCR_DICT] = 1,
[DELETE_SUBSCR] = 1,
[CALL_INTRINSIC_1] = 1,
+[CALL_INTRINSIC_2] = 1,
[RAISE_VARARGS] = 1,
[INTERPRETER_EXIT] = 1,
[RETURN_VALUE] = 1,
@@ -59,10 +60,10 @@ extern const int _Py_MacroOpUOpCount[] = {
[GET_ANEXT] = 1,
[GET_AWAITABLE] = 1,
[SEND] = 1,
+[SEND_GEN] = 1,
[YIELD_VALUE] = 1,
[POP_EXCEPT] = 1,
[RERAISE] = 1,
-[PREP_RERAISE_STAR] = 1,
[END_ASYNC_FOR] = 1,
[CLEANUP_THROW] = 1,
[LOAD_ASSERTION_ERROR] = 1,
@@ -126,11 +127,17 @@ extern const int _Py_MacroOpUOpCount[] = {
[JUMP_FORWARD] = 1,
[JUMP_BACKWARD] = 1,
[POP_JUMP_IF_FALSE] = 1,
+[BB_TEST_POP_IF_FALSE] = 1,
[POP_JUMP_IF_TRUE] = 1,
+[BB_TEST_POP_IF_TRUE] = 1,
[POP_JUMP_IF_NOT_NONE] = 1,
+[BB_TEST_POP_IF_NOT_NONE] = 1,
[POP_JUMP_IF_NONE] = 1,
+[BB_TEST_POP_IF_NONE] = 1,
[JUMP_IF_FALSE_OR_POP] = 1,
+[BB_TEST_IF_FALSE_OR_POP] = 1,
[JUMP_IF_TRUE_OR_POP] = 1,
+[BB_TEST_IF_TRUE_OR_POP] = 1,
[JUMP_BACKWARD_NO_INTERRUPT] = 1,
[GET_LEN] = 1,
[MATCH_CLASS] = 1,
@@ -140,6 +147,7 @@ extern const int _Py_MacroOpUOpCount[] = {
[GET_ITER] = 1,
[GET_YIELD_FROM_ITER] = 1,
[FOR_ITER] = 1,
+[BB_TEST_ITER] = 1,
[FOR_ITER_LIST] = 1,
[FOR_ITER_TUPLE] = 1,
[FOR_ITER_RANGE] = 1,
@@ -151,9 +159,9 @@ extern const int _Py_MacroOpUOpCount[] = {
[LOAD_ATTR_METHOD_WITH_VALUES] = 1,
[LOAD_ATTR_METHOD_NO_DICT] = 1,
[LOAD_ATTR_METHOD_LAZY_DICT] = 1,
-[CALL_BOUND_METHOD_EXACT_ARGS] = 1,
[KW_NAMES] = 1,
[CALL] = 1,
+[CALL_BOUND_METHOD_EXACT_ARGS] = 1,
[CALL_PY_EXACT_ARGS] = 1,
[CALL_PY_WITH_DEFAULTS] = 1,
[CALL_NO_KW_TYPE_1] = 1,
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 4aee9ac9580a2c..71bfa55e38aa6f 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -1338,6 +1338,7 @@ def main():
sys.exit(f"Found {a.errors} errors")
a.write_instructions() # Raises OSError if output can't be written
a.write_metadata()
+ a.output_filename = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
a.write_macromap_and_typedata()
From 0b29290922b6346d7d446cd43770bf9a9c603cb4 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 17 Feb 2023 13:53:53 +0800
Subject: [PATCH 030/280] Handle EXTENDED_ARG
---
Lib/opcode.py | 6 +++---
Python/tier2.c | 42 +++++++++++++++++++++++++++++-------------
2 files changed, 32 insertions(+), 16 deletions(-)
diff --git a/Lib/opcode.py b/Lib/opcode.py
index c3672e09a5b040..7eb00d7f9e2e04 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -453,11 +453,11 @@ def pseudo_op(name, op, real_ops):
# 'BB_ENTER_FRAME',
# 'BB_EXIT_FRAME',
# Initial generic branching instruction.
- 'BB_BRANCH',
+ 'BB_BRANCH', # When both exits have not been generated.
# The BB_BRANCH transitions to one of these two.
# This happens when the fall through is generated, but not the other branch.
- 'BB_BRANCH_IF_FLAG_UNSET',
- 'BB_BRANCH_IF_FLAG_SET',
+ 'BB_BRANCH_IF_FLAG_UNSET', # When alternate exit is not yet generated.
+ 'BB_BRANCH_IF_FLAG_SET', # When successor exit is not yet generated.
# The final form is that once both branches are generated, we can just
# override these instructions with a generic JUMP.
diff --git a/Python/tier2.c b/Python/tier2.c
index 1674de666a0aeb..6b0e7b7b980bdc 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -271,10 +271,11 @@ IS_FORBIDDEN_OPCODE(int opcode)
// Closures
case LOAD_DEREF:
case MAKE_CELL:
- // @TODO backward jumps should be supported!
- case JUMP_BACKWARD:
- case JUMP_BACKWARD_NO_INTERRUPT:
+ // DELETE_FAST
+ case DELETE_FAST:
+ // TODO: Pattern matching add here
return 1;
+
default:
return 0;
}
@@ -402,16 +403,14 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
fprintf(stderr, "emitted logical branch\n");
#endif
// We prefix with an empty EXTENDED_ARG, just in case the future jumps
- // are not large enough to handle the bytecode format.
+ // are not large enough to handle the bytecode format when jumping to
+ // the 2nd bb.
_py_set_opcode(write_curr, EXTENDED_ARG);
write_curr->oparg = 0;
write_curr++;
_py_set_opcode(write_curr, opcode);
write_curr->oparg = oparg;
write_curr++;
- // Each guard also holds 2 CACHE entries. This stores an int32 of the
- // offset from start of the code object (in _Py_CODEUNITs) that the current guard
- // can generate the basic block from.
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
for (int i = 0; i < INLINE_CACHE_ENTRIES_BB_BRANCH; i++) {
_py_set_opcode(write_curr, CACHE);
@@ -508,7 +507,13 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
#define JUMPBY(x) i += x + 1; continue;
#define BASIC_PUSH(v) (*stack_pointer++ = (v))
#define BASIC_POP() (*--stack_pointer)
-#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); write_i = copy_cache_entries(write_i, curr+1, caches); i += caches; continue;
+#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); \
+ write_i = copy_cache_entries(write_i, curr+1, caches); \
+ i += caches; \
+ continue;
+#define DISPATCH_GOTO() goto dispatch_opcode;
+
+
assert(co->_tier2_info != NULL);
// There are only two cases that a BB ends.
// 1. If there's a branch instruction / scope exit.
@@ -530,13 +535,14 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
PyTypeObject **stack_pointer = co->_tier2_info->types_stack;
int tos = -1;
+
// A meta-interpreter for types.
Py_ssize_t i = 0;
for (; i < Py_SIZE(co); i++) {
_Py_CODEUNIT *curr = _PyCode_CODE(co) + i;
- _Py_CODEUNIT instr = *curr;
- int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
- int oparg = _Py_OPARG(instr);
+ _Py_CODEUNIT *next_instr = curr + 1;
+ int opcode = _PyOpcode_Deopt[_Py_OPCODE(*curr)];
+ int oparg = _Py_OPARG(*curr);
int caches = _PyOpcode_Caches[opcode];
// Just because an instruction requires a guard doesn't mean it's the end of a BB.
@@ -545,7 +551,16 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
_Py_CODEUNIT guard_instr;
_Py_CODEUNIT action;
+ dispatch_opcode:
switch (opcode) {
+ case EXTENDED_ARG:
+ write_i = emit_i(write_i, EXTENDED_ARG, _Py_OPARG(*curr));
+ curr++;
+ next_instr++;
+ oparg = oparg << 8 | _Py_OPARG(*curr);
+ opcode = _Py_OPCODE(*curr);
+ caches = _PyOpcode_Caches[opcode];
+ DISPATCH_GOTO();
// We need to rewrite the pseudo-branch instruction.
case COMPARE_AND_BRANCH:
opcode = COMPARE_OP;
@@ -613,7 +628,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// These are definitely the end of a basic block.
if (IS_SCOPE_EXIT_OPCODE(opcode)) {
// Emit the scope exit instruction.
- write_i = emit_scope_exit(write_i, instr);
+ write_i = emit_scope_exit(write_i, *curr);
END();
}
@@ -628,7 +643,8 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
}
// Get the BB ID without incrementing it.
// AllocateBBMetaData will increment.
- write_i = emit_logical_branch(write_i, instr, co->_tier2_info->bb_data_curr);
+ write_i = emit_logical_branch(write_i, *curr,
+ co->_tier2_info->bb_data_curr);
END();
}
DISPATCH();
From cc8bf6b0a60929aa0517bed6bd8046da729c05ca Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 21 Feb 2023 15:31:47 +0800
Subject: [PATCH 031/280] Part 4: handle backwards jumps
---
Include/cpython/code.h | 3 +-
Include/internal/pycore_code.h | 10 +-
Include/internal/pycore_frame.h | 7 +-
.../internal/pycore_opcode_macro_to_micro.h | 4 +
Objects/codeobject.c | 13 +-
Python/bytecodes.c | 103 ++++++++-
Python/generated_cases.c.h | 107 ++++++++-
Python/opcode_metadata.h | 20 ++
Python/tier2.c | 207 ++++++++++++++----
9 files changed, 425 insertions(+), 49 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 096552524d3089..b0683b86f8d33e 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -65,6 +65,7 @@ typedef struct _PyTier2BBMetadata {
int type_context_len;
PyTypeObject **type_context;
_Py_CODEUNIT *tier2_start;
+ // Note, this is the first tier 1 instruction to execute AFTER the BB ends.
_Py_CODEUNIT *tier1_end;
// Type stack state
PyTypeObject **types_stack;
@@ -85,7 +86,7 @@ typedef struct _PyTier2BBSpace {
// Tier 2 info stored in the code object. Lazily allocated.
typedef struct _PyTier2Info {
/* the tier 2 basic block to execute (if any) */
- _Py_CODEUNIT *_entry_bb;
+ _PyTier2BBMetadata *_entry_bb;
_PyTier2BBSpace *_bb_space;
// Keeps track of offset of jump targets (in number of codeunits)
// from co_code_adaptive.
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 37f309f9b15643..d5612e5c5be40b 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -255,7 +255,15 @@ extern void _PyStaticCode_Fini(PyCodeObject *co);
extern int _PyStaticCode_Init(PyCodeObject *co);
/* Tier 2 interpreter */
-extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *, _Py_CODEUNIT *);
+extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *,
+ _Py_CODEUNIT *);
+extern _Py_CODEUNIT *_PyTier2_GenerateNextBB(
+ struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
+ _Py_CODEUNIT **tier1_fallback);
+extern _Py_CODEUNIT *_PyTier2_LocateJumpBackwardsBB(
+ struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
+ _Py_CODEUNIT **tier1_fallback);
+
#ifdef Py_STATS
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index 609cee8ef54fd5..d9d4f7c4e1322e 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -120,7 +120,12 @@ _PyFrame_Initialize(
frame->stacktop = code->co_nlocalsplus;
frame->frame_obj = NULL;
// @TODO CHANGE ME
- frame->prev_instr = _PyCode_CODE(code) - 1;
+ if (code->_tier2_info != NULL) {
+ frame->prev_instr = code->_tier2_info->_entry_bb->tier2_start - 1;
+ }
+ else {
+ frame->prev_instr = _PyCode_CODE(code) - 1;
+ }
frame->yield_offset = 0;
frame->owner = FRAME_OWNED_BY_THREAD;
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index 8131e548a5c578..9b9fe6117725b3 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -188,6 +188,10 @@ extern const int _Py_MacroOpUOpCount[] = {
[SWAP] = 1,
[EXTENDED_ARG] = 1,
[CACHE] = 1,
+[BB_BRANCH] = 1,
+[BB_BRANCH_IF_FLAG_UNSET] = 1,
+[BB_BRANCH_IF_FLAG_SET] = 1,
+[BB_JUMP_BACKWARD_LAZY] = 1,
};
extern const int _Py_MacroOpToUOp[][2] = {
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 5e3d1cd8e1c75b..d9259cff23a2d9 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -843,7 +843,7 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq)
if (addrq < 0) {
return co->co_firstlineno;
}
- assert(addrq >= 0 && addrq < _PyCode_NBYTES(co));
+ // assert(addrq >= 0 && addrq < _PyCode_NBYTES(co));
if (co->_co_linearray) {
return _PyCode_LineNumberFromArray(co, addrq / sizeof(_Py_CODEUNIT));
}
@@ -1676,11 +1676,12 @@ code_tier2_fini(PyCodeObject *co)
t2_info->_entry_bb = NULL;
if (t2_info->_bb_space != NULL) {
// Traverse the linked list
- for (_PyTier2BBSpace *curr = t2_info->_bb_space; curr != NULL;) {
- _PyTier2BBSpace *prev = curr;
- curr = curr->next;
- PyMem_Free(prev);
- }
+ //for (_PyTier2BBSpace *curr = t2_info->_bb_space; curr != NULL;) {
+ // _PyTier2BBSpace *prev = curr;
+ // curr = curr->next;
+ // PyMem_Free(prev);
+ //}
+ PyMem_Free(t2_info->_bb_space);
t2_info->_bb_space = NULL;
}
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 1c79e5d20dbc91..44947aba44660e 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3272,7 +3272,7 @@ dummy_func(
}
inst(EXTENDED_ARG, (--)) {
- assert(oparg);
+ // assert(oparg);
assert(cframe.use_tracing == 0);
opcode = _Py_OPCODE(*next_instr);
oparg = oparg << 8 | _Py_OPARG(*next_instr);
@@ -3284,6 +3284,107 @@ dummy_func(
Py_UNREACHABLE();
}
+ // Tier 2 instructions
+ inst(BB_BRANCH, (--)) {
+ _Py_CODEUNIT *t2_nextinstr = NULL;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
+ _Py_CODEUNIT *tier1_fallback = NULL;
+ if (bb_test) {
+ // Rewrite self
+ _py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_UNSET);
+ // Generate consequent.
+ t2_nextinstr = _PyTier2_GenerateNextBB(
+ frame, cache->bb_id, 0, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback;
+ }
+ }
+ else {
+ // Rewrite self
+ _py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_SET);
+ // Generate predicate.
+ t2_nextinstr = _PyTier2_GenerateNextBB(
+ frame, cache->bb_id, oparg, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback + oparg;
+ }
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ // Their addresses should be the same. Because
+ // The first BB should be generated right after the previous one.
+ if (next_instr != t2_nextinstr) {
+ fprintf(stderr, "next: %p, t2 next: %p\n", next_instr, t2_nextinstr);
+ }
+ assert(next_instr == t2_nextinstr);
+ next_instr = t2_nextinstr;
+ }
+
+ inst(BB_BRANCH_IF_FLAG_UNSET, (--)) {
+ if (!bb_test) {
+ _Py_CODEUNIT *t2_nextinstr = NULL;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
+ _Py_CODEUNIT *tier1_fallback = NULL;
+
+ // @TODO: Rewrite TEST Instruction to a JUMP above.
+
+ t2_nextinstr = _PyTier2_GenerateNextBB(
+ frame, cache->bb_id, oparg, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback;
+ }
+ next_instr = t2_nextinstr;
+ }
+ else {
+ JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ }
+ }
+
+ inst(BB_BRANCH_IF_FLAG_SET, (--)) {
+ if (bb_test) {
+ _Py_CODEUNIT *t2_nextinstr = NULL;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
+ _Py_CODEUNIT *tier1_fallback = NULL;
+
+ // @TODO: Rewrite TEST Instruction to a JUMP above.
+
+ t2_nextinstr = _PyTier2_GenerateNextBB(
+ frame, cache->bb_id, oparg, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback;
+ }
+ next_instr = t2_nextinstr;
+ }
+ else {
+ JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ }
+ }
+
+ inst(BB_JUMP_BACKWARD_LAZY, (--)) {
+ _Py_CODEUNIT *t2_nextinstr = NULL;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
+ _Py_CODEUNIT *tier1_fallback = NULL;
+
+ t2_nextinstr = _PyTier2_LocateJumpBackwardsBB(
+ frame, cache->bb_id, oparg, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback;
+ }
+ // Rewrite self
+ _Py_CODEUNIT *curr = next_instr - 1;
+ _Py_CODEUNIT *prev = curr - 1;
+ assert(_Py_OPCODE(*prev) == EXTENDED_ARG);
+ int op_arg = (int)(t2_nextinstr - next_instr);
+ assert(op_arg <= INT16_MAX);
+ _py_set_opcode(curr, JUMP_BACKWARD);
+ prev->oparg = (op_arg >> 8) & 0xFF;
+ curr->oparg = op_arg & 0xFF;
+ next_instr = t2_nextinstr;
+ }
// END BYTECODES //
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index b4de8d1feffb65..4e12ce60e89f7e 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4107,7 +4107,7 @@
}
TARGET(EXTENDED_ARG) {
- assert(oparg);
+ // assert(oparg);
assert(cframe.use_tracing == 0);
opcode = _Py_OPCODE(*next_instr);
oparg = oparg << 8 | _Py_OPARG(*next_instr);
@@ -4118,3 +4118,108 @@
TARGET(CACHE) {
Py_UNREACHABLE();
}
+
+ TARGET(BB_BRANCH) {
+ _Py_CODEUNIT *t2_nextinstr = NULL;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
+ _Py_CODEUNIT *tier1_fallback = NULL;
+ if (bb_test) {
+ // Rewrite self
+ _py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_UNSET);
+ // Generate consequent.
+ t2_nextinstr = _PyTier2_GenerateNextBB(
+ frame, cache->bb_id, 0, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback;
+ }
+ }
+ else {
+ // Rewrite self
+ _py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_SET);
+ // Generate predicate.
+ t2_nextinstr = _PyTier2_GenerateNextBB(
+ frame, cache->bb_id, oparg, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback + oparg;
+ }
+ }
+ JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ // Their addresses should be the same. Because
+ // The first BB should be generated right after the previous one.
+ if (next_instr != t2_nextinstr) {
+ fprintf(stderr, "next: %p, t2 next: %p\n", next_instr, t2_nextinstr);
+ }
+ assert(next_instr == t2_nextinstr);
+ next_instr = t2_nextinstr;
+ DISPATCH();
+ }
+
+ TARGET(BB_BRANCH_IF_FLAG_UNSET) {
+ if (!bb_test) {
+ _Py_CODEUNIT *t2_nextinstr = NULL;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
+ _Py_CODEUNIT *tier1_fallback = NULL;
+
+ // @TODO: Rewrite TEST Instruction to a JUMP above.
+
+ t2_nextinstr = _PyTier2_GenerateNextBB(
+ frame, cache->bb_id, oparg, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback;
+ }
+ next_instr = t2_nextinstr;
+ }
+ else {
+ JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ }
+ DISPATCH();
+ }
+
+ TARGET(BB_BRANCH_IF_FLAG_SET) {
+ if (bb_test) {
+ _Py_CODEUNIT *t2_nextinstr = NULL;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
+ _Py_CODEUNIT *tier1_fallback = NULL;
+
+ // @TODO: Rewrite TEST Instruction to a JUMP above.
+
+ t2_nextinstr = _PyTier2_GenerateNextBB(
+ frame, cache->bb_id, oparg, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback;
+ }
+ next_instr = t2_nextinstr;
+ }
+ else {
+ JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ }
+ DISPATCH();
+ }
+
+ TARGET(BB_JUMP_BACKWARD_LAZY) {
+ _Py_CODEUNIT *t2_nextinstr = NULL;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
+ _Py_CODEUNIT *tier1_fallback = NULL;
+
+ t2_nextinstr = _PyTier2_GenerateNextBB(
+ frame, cache->bb_id, oparg, &tier1_fallback);
+ if (t2_nextinstr == NULL) {
+ // Fall back to tier 1.
+ next_instr = tier1_fallback;
+ }
+ // Rewrite self
+ _Py_CODEUNIT *curr = next_instr - 1;
+ _Py_CODEUNIT *prev = curr - 1;
+ assert(_Py_OPCODE(*prev) == EXTENDED_ARG);
+ int op_arg = (int)(t2_nextinstr - next_instr);
+ assert(op_arg <= INT16_MAX);
+ _py_set_opcode(curr, JUMP_BACKWARD);
+ prev->oparg = (op_arg >> 8) & 0xFF;
+ curr->oparg = op_arg & 0xFF;
+ next_instr = t2_nextinstr;
+ DISPATCH();
+ }
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 579851dcf09ab4..45de8827a0398e 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -368,6 +368,14 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 0;
case CACHE:
return 0;
+ case BB_BRANCH:
+ return 0;
+ case BB_BRANCH_IF_FLAG_UNSET:
+ return 0;
+ case BB_BRANCH_IF_FLAG_SET:
+ return 0;
+ case BB_JUMP_BACKWARD_LAZY:
+ return 0;
default:
return -1;
}
@@ -740,6 +748,14 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case CACHE:
return 0;
+ case BB_BRANCH:
+ return 0;
+ case BB_BRANCH_IF_FLAG_UNSET:
+ return 0;
+ case BB_BRANCH_IF_FLAG_SET:
+ return 0;
+ case BB_JUMP_BACKWARD_LAZY:
+ return 0;
default:
return -1;
}
@@ -940,5 +956,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[SWAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[EXTENDED_ARG] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[CACHE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BB_BRANCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_BRANCH_IF_FLAG_UNSET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_BRANCH_IF_FLAG_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_JUMP_BACKWARD_LAZY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
};
#endif
diff --git a/Python/tier2.c b/Python/tier2.c
index 6b0e7b7b980bdc..c99ef86cec6245 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -273,7 +273,13 @@ IS_FORBIDDEN_OPCODE(int opcode)
case MAKE_CELL:
// DELETE_FAST
case DELETE_FAST:
- // TODO: Pattern matching add here
+ // Pattern matching
+ case MATCH_MAPPING:
+ case MATCH_SEQUENCE:
+ case MATCH_KEYS:
+ // Too large arguments, we can handle this, just
+ // increases complexity
+ case EXTENDED_ARG:
return 1;
default:
@@ -347,12 +353,30 @@ BINARY_OP_RESULT_TYPE(PyCodeObject *co, _Py_CODEUNIT *instr, int n_typecontext,
}
static inline _Py_CODEUNIT *
-emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard)
+emit_cache_entries(_Py_CODEUNIT *write_curr, int cache_entries)
+{
+ for (int i = 0; i < cache_entries; i++) {
+ _py_set_opcode(write_curr, CACHE);
+ write_curr++;
+ }
+ return write_curr;
+}
+
+static inline _Py_CODEUNIT *
+emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard, int bb_id)
{
*write_curr = guard;
write_curr++;
+ _py_set_opcode(write_curr, EXTENDED_ARG);
+ write_curr->oparg = 0;
+ write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
+ write_curr->oparg = 0;
write_curr++;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
+ write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
+ assert((uint16_t)(bb_id) == bb_id);
+ cache->bb_id = (uint16_t)(bb_id);
return write_curr;
}
@@ -363,7 +387,7 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
int opcode;
int oparg = _Py_OPARG(branch);
// @TODO handle JUMP_BACKWARDS and JUMP_BACKWARDS_NO_INTERRUPT
- switch (_Py_OPCODE(branch)) {
+ switch (_PyOpcode_Deopt[_Py_OPCODE(branch)]) {
case JUMP_BACKWARD:
// The initial backwards jump needs to find the right basic block.
// Subsequent jumps don't need to check this anymore. They can just
@@ -395,30 +419,51 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
// Honestly shouldn't happen because branches that
// we can't handle are in IS_FORBIDDEN_OPCODE
#if BB_DEBUG
- fprintf(stderr, "emit_logical_branch unreachable\n");
+ fprintf(stderr,
+ "emit_logical_branch unreachable opcode %d\n", _Py_OPCODE(branch));
#endif
Py_UNREACHABLE();
}
#if BB_DEBUG
- fprintf(stderr, "emitted logical branch\n");
+ fprintf(stderr, "emitted logical branch %p %d\n", write_curr,
+ _Py_OPCODE(branch));
#endif
- // We prefix with an empty EXTENDED_ARG, just in case the future jumps
- // are not large enough to handle the bytecode format when jumping to
- // the 2nd bb.
- _py_set_opcode(write_curr, EXTENDED_ARG);
- write_curr->oparg = 0;
- write_curr++;
- _py_set_opcode(write_curr, opcode);
- write_curr->oparg = oparg;
- write_curr++;
- _PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
- for (int i = 0; i < INLINE_CACHE_ENTRIES_BB_BRANCH; i++) {
- _py_set_opcode(write_curr, CACHE);
+ // Backwards jumps should be handled specially.
+ if (opcode == BB_JUMP_BACKWARD_LAZY) {
+ _py_set_opcode(write_curr, EXTENDED_ARG);
+ write_curr->oparg = (oparg >> 8) & 0xFF;
write_curr++;
+ // We don't need to recalculate the backward jump, because that only needs to be done
+ // when it locates the next BB in JUMP_BACKWARD_LAZY.
+ _py_set_opcode(write_curr, BB_JUMP_BACKWARD_LAZY);
+ write_curr->oparg = oparg & 0xFF;
+ write_curr++;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
+ write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
+ assert((uint16_t)(bb_id) == bb_id);
+ cache->bb_id = (uint16_t)(bb_id);
+ return write_curr;
+ }
+ else {
+ //_Py_CODEUNIT *start = write_curr;
+ _py_set_opcode(write_curr, opcode);
+ write_curr->oparg = oparg;
+ write_curr++;
+ // We prefix with an empty EXTENDED_ARG, just in case the future jumps
+ // are not large enough to handle the bytecode format when jumping to
+ // the 2nd bb.
+ _py_set_opcode(write_curr, EXTENDED_ARG);
+ write_curr->oparg = (oparg >> 8) & 0xFF;
+ write_curr++;
+ _py_set_opcode(write_curr, BB_BRANCH);
+ write_curr->oparg = oparg & 0xFF;
+ write_curr++;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
+ write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
+ assert((uint16_t)(bb_id) == bb_id);
+ cache->bb_id = (uint16_t)(bb_id);
+ return write_curr;
}
- assert((uint16_t)(bb_id) == bb_id);
- cache->bb_id = (uint16_t)(bb_id);
- return write_curr;
}
static inline _Py_CODEUNIT *
@@ -500,7 +545,7 @@ IS_BACKWARDS_JUMP_TARGET(PyCodeObject *co, _Py_CODEUNIT *curr)
// Note: a BB end also includes a type guard.
_PyTier2BBMetadata *
_PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
- _Py_CODEUNIT *start,
+ _Py_CODEUNIT *tier1_start,
int n_typecontext, PyTypeObject **type_context)
{
#define END() goto end;
@@ -537,7 +582,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// A meta-interpreter for types.
- Py_ssize_t i = 0;
+ Py_ssize_t i = (tier1_start - _PyCode_CODE(co));
for (; i < Py_SIZE(co); i++) {
_Py_CODEUNIT *curr = _PyCode_CODE(co) + i;
_Py_CODEUNIT *next_instr = curr + 1;
@@ -550,7 +595,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
int how_many_guards = 0;
_Py_CODEUNIT guard_instr;
_Py_CODEUNIT action;
-
+
dispatch_opcode:
switch (opcode) {
case EXTENDED_ARG:
@@ -565,6 +610,8 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
case COMPARE_AND_BRANCH:
opcode = COMPARE_OP;
DISPATCH();
+ // FOR_ITER must be handled separately from other opcodes as it has
+ // CACHE entries following it.
//case LOAD_FAST:
// BASIC_PUSH(type_context[oparg]);
// DISPATCH();
@@ -645,6 +692,9 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// AllocateBBMetaData will increment.
write_i = emit_logical_branch(write_i, *curr,
co->_tier2_info->bb_data_curr);
+ // Mainly for FOR_ITER
+ write_i = emit_cache_entries(write_i, caches);
+ i += caches;
END();
}
DISPATCH();
@@ -656,9 +706,15 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// fprintf(stderr, "i is %Id\n", i);
//#endif
// Create the tier 2 BB
- meta = _PyTier2_AllocateBBMetaData(co, t2_start, _PyCode_CODE(co) + i, n_typecontext, type_context_copy);
+ meta = _PyTier2_AllocateBBMetaData(co, t2_start,
+ // + 1 because we want to start with the NEXT instruction for the scan
+ _PyCode_CODE(co) + i + 1, n_typecontext, type_context_copy);
// Tell BB space the number of bytes we wrote.
- bb_space->water_level += (write_i - t2_start) * sizeof(_Py_CODEUNIT);
+ // -1 becaues write_i points to the instruction AFTER the end
+ bb_space->water_level += (write_i - t2_start - 1) * sizeof(_Py_CODEUNIT);
+#if BB_DEBUG
+ fprintf(stderr, "Generated BB T2 Start: %p\n", meta->tier2_start);
+#endif
return meta;
}
@@ -780,14 +836,15 @@ _PyTier2Info_Initialize(PyCodeObject *co)
t2_info->bb_data_len = 0;
t2_info->bb_data = NULL;
t2_info->bb_data_curr = 0;
- int bb_data_len = (Py_SIZE(co) / 5 + 1);
- _PyTier2Info **bb_data = PyMem_Malloc(bb_data_len * sizeof(_PyTier2Info *));
+ Py_ssize_t bb_data_len = (Py_SIZE(co) / 5 + 1);
+ assert((int)bb_data_len == bb_data_len);
+ _PyTier2BBMetadata **bb_data = PyMem_Malloc(bb_data_len * sizeof(_PyTier2Info *));
if (bb_data == NULL) {
PyMem_Free(t2_info);
PyMem_Free(types_stack);
return NULL;
}
- t2_info->bb_data_len = bb_data_len;
+ t2_info->bb_data_len = (int)bb_data_len;
t2_info->bb_data = bb_data;
co->_tier2_info = t2_info;
@@ -798,6 +855,29 @@ _PyTier2Info_Initialize(PyCodeObject *co)
////////// OVERALL TIER2 FUNCTIONS
+// We use simple heuristics to determine if there are operations
+// we can optimize in this.
+// Specifically, we are looking for the presence of PEP 659
+// specialized forms of bytecode, because this indicates
+// that it's a known form.
+// ADD MORE HERE AS WE GO ALONG.
+static inline int
+IS_OPTIMIZABLE_OPCODE(int opcode, int oparg)
+{
+ switch (_PyOpcode_Deopt[opcode]) {
+ case BINARY_OP:
+ switch (oparg) {
+ case NB_ADD:
+ // We want a specialised form, not the generic BINARY_OP.
+ return opcode != _PyOpcode_Deopt[opcode];
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
// 1. Initialize whatever we need.
// 2. Create the entry BB.
// 3. Jump into that BB.
@@ -812,11 +892,17 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
if ((int)Py_SIZE(co) != Py_SIZE(co)) {
return NULL;
}
+ int optimizable = 0;
for (Py_ssize_t curr = 0; curr < Py_SIZE(co); curr++) {
_Py_CODEUNIT *curr_instr = _PyCode_CODE(co) + curr;
if (IS_FORBIDDEN_OPCODE(_PyOpcode_Deopt[_Py_OPCODE(*curr_instr)])) {
return NULL;
}
+ optimizable |= IS_OPTIMIZABLE_OPCODE(_Py_OPCODE(*curr_instr), _Py_OPARG(*curr_instr));
+ }
+
+ if (!optimizable) {
+ return NULL;
}
_PyTier2Info *t2_info = _PyTier2Info_Initialize(co);
@@ -858,8 +944,10 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
#endif
- t2_info->_entry_bb = meta->tier2_start;
+ t2_info->_entry_bb = meta;
+ // SET THE FRAME INFO
+ frame->prev_instr = meta->tier2_start - 1;
// Set the starting instruction to the entry BB.
// frame->prev_instr = bb_ptr->u_code - 1;
return meta->tier2_start;
@@ -883,7 +971,6 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// If it fails, due to lack of memory or whatever,
// just fall back to the tier 1 interpreter.
_Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
- return next_instr;
if (next != NULL) {
return next;
}
@@ -896,27 +983,31 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// The first basic block created will always be directly after the current tier 2 code.
// The second basic block created will always require a jump.
_Py_CODEUNIT *
-_PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, _Py_CODEUNIT *curr_tier1,
- _Py_CODEUNIT *curr_tier2)
+_PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
+ _Py_CODEUNIT **tier1_fallback)
{
- // Be a pessimist and assume we need to write the entire code into the BB.
+ PyCodeObject *co = frame->f_code;
+ assert(co->_tier2_info != NULL);
+ assert(bb_id <= co->_tier2_info->bb_data_curr);
+ _PyTier2BBMetadata *meta = co->_tier2_info->bb_data[bb_id];
+ _Py_CODEUNIT *tier1_end = meta->tier1_end + jumpby;
+ *tier1_fallback = tier1_end;
+ // Be a pessimist and assume we need to write the entire rest of code into the BB.
// The size of the BB generated will definitely be equal to or smaller than this.
_PyTier2BBSpace *space = _PyTier2_BBSpaceCheckAndReallocIfNeeded(
- frame->f_code, _PyCode_NBYTES(frame->f_code));
+ frame->f_code,
+ _PyCode_NBYTES(co) - (tier1_end - _PyCode_CODE(co)) * sizeof(_Py_CODEUNIT));
if (space == NULL) {
// DEOPTIMIZE TO TIER 1?
return NULL;
}
- // Write to the top of the space. That is automatically where the next instruction
- // should execute.
- // start writing at curr_tier2
int type_context_len = 0;
PyTypeObject **type_context = initialize_type_context(frame->f_code, &type_context_len);
if (type_context == NULL) {
return NULL;
}
_PyTier2BBMetadata *metadata = _PyTier2_Code_DetectAndEmitBB(
- frame->f_code, space, curr_tier1, type_context_len,
+ frame->f_code, space, tier1_end, type_context_len,
type_context);
if (metadata == NULL) {
PyMem_Free(type_context);
@@ -924,3 +1015,43 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, _Py_CODEUNIT *curr_tier1,
}
return metadata->tier2_start;
}
+
+_Py_CODEUNIT *
+_PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
+ _Py_CODEUNIT **tier1_fallback)
+{
+ PyCodeObject *co = frame->f_code;
+ assert(co->_tier2_info != NULL);
+ assert(bb_id <= co->_tier2_info->bb_data_curr);
+ _PyTier2BBMetadata *meta = co->_tier2_info->bb_data[bb_id];
+ // The jump target
+ _Py_CODEUNIT *tier1_jump_target = meta->tier1_end + jumpby;
+ *tier1_fallback = tier1_jump_target;
+ // Be a pessimist and assume we need to write the entire rest of code into the BB.
+ // The size of the BB generated will definitely be equal to or smaller than this.
+ _PyTier2BBSpace *space = _PyTier2_BBSpaceCheckAndReallocIfNeeded(
+ frame->f_code,
+ _PyCode_NBYTES(co) - (tier1_jump_target - _PyCode_CODE(co)) * sizeof(_Py_CODEUNIT));
+ if (space == NULL) {
+ // DEOPTIMIZE TO TIER 1?
+ return NULL;
+ }
+ int type_context_len = 0;
+ PyTypeObject **type_context = initialize_type_context(frame->f_code, &type_context_len);
+ if (type_context == NULL) {
+ return NULL;
+ }
+ // Now, find the matching BB
+ _PyTier2Info *t2_info = co->_tier2_info;
+ int jump_offset = (int)(tier1_jump_target - _PyCode_CODE(co));
+ int matching_bb_id = -1;
+ for (int i = 0; i < t2_info->backward_jump_count; i++) {
+ if (t2_info->backward_jump_offsets[i] == jump_offset) {
+ matching_bb_id = t2_info->backward_jump_target_bb_ids[i];
+ }
+ }
+ assert(matching_bb_id >= 0);
+ assert(matching_bb_id <= t2_info->bb_data_curr);
+ _PyTier2BBMetadata *target_metadata = t2_info->bb_data[matching_bb_id];
+ return target_metadata->tier2_start;
+}
From e26f746f3f552ebed5005f77f5e27de8aba8e929 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 21 Feb 2023 21:15:45 +0800
Subject: [PATCH 032/280] Part 4.1: Find backward jump targets
TODO: Handle transformations to original jumps
---
Include/cpython/code.h | 2 +-
Include/internal/pycore_frame.h | 11 +-
Python/bytecodes.c | 15 ++-
Python/ceval_macros.h | 3 +-
Python/generated_cases.c.h | 13 +-
Python/opcode_metadata.h | 2 +-
Python/tier2.c | 216 ++++++++++++++++++++++++++------
7 files changed, 211 insertions(+), 51 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index b0683b86f8d33e..9675a6017e555a 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -97,7 +97,7 @@ typedef struct _PyTier2Info {
// So backward jump offset [1, 2, 3 ,4]
// will have [[BB_ID1, BB_ID2], [BB_ID3,], [], []]
// etc.
- int *backward_jump_target_bb_ids;
+ int **backward_jump_target_bb_ids;
PyTypeObject **types_stack;
// Max len of bb_data
int bb_data_len;
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index d9d4f7c4e1322e..d9f60179a02a0d 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -66,8 +66,15 @@ typedef struct _PyInterpreterFrame {
PyObject *localsplus[1];
} _PyInterpreterFrame;
-#define _PyInterpreterFrame_LASTI(IF) \
- ((int)((IF)->prev_instr - _PyCode_CODE((IF)->f_code)))
+static inline int
+_PyInterpreterFrame_LASTI(_PyInterpreterFrame *f) {
+ if (f->f_code->_tier2_info != NULL) {
+ return ((int)((f)->prev_instr - f->f_code->_tier2_info->_bb_space->u_code));
+ }
+ return ((int)((f)->prev_instr - _PyCode_CODE((f)->f_code)));
+}
+//#define _PyInterpreterFrame_LASTI(IF) \
+// ((int)((IF)->prev_instr - _PyCode_CODE((IF)->f_code)))
static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
return f->localsplus + f->f_code->co_nlocalsplus;
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 44947aba44660e..974ba65866b26e 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2280,10 +2280,11 @@ dummy_func(
_PyErr_Clear(tstate);
}
/* iterator ended normally */
- assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR);
Py_DECREF(iter);
STACK_SHRINK(1);
bb_test = false;
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ DISPATCH();
}
bb_test = true;
}
@@ -3298,6 +3299,7 @@ dummy_func(
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
+ DISPATCH();
}
}
else {
@@ -3309,6 +3311,7 @@ dummy_func(
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback + oparg;
+ DISPATCH();
}
}
JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -3327,7 +3330,7 @@ dummy_func(
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- // @TODO: Rewrite TEST Instruction to a JUMP above.
+ // @TODO: Rewrite TEST intruction above to a JUMP above.
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id, oparg, &tier1_fallback);
@@ -3348,7 +3351,7 @@ dummy_func(
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- // @TODO: Rewrite TEST Instruction to a JUMP above.
+ // @TODO: Rewrite TEST intruction above to a JUMP above..
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id, oparg, &tier1_fallback);
@@ -3369,7 +3372,7 @@ dummy_func(
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_LocateJumpBackwardsBB(
- frame, cache->bb_id, oparg, &tier1_fallback);
+ frame, cache->bb_id, -oparg, &tier1_fallback);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -3378,11 +3381,13 @@ dummy_func(
_Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *prev = curr - 1;
assert(_Py_OPCODE(*prev) == EXTENDED_ARG);
- int op_arg = (int)(t2_nextinstr - next_instr);
+ int op_arg = -(int)(t2_nextinstr - next_instr);
+ // fprintf(stderr, "JUMP_BACKWARD oparg is %d\n", op_arg);
assert(op_arg <= INT16_MAX);
_py_set_opcode(curr, JUMP_BACKWARD);
prev->oparg = (op_arg >> 8) & 0xFF;
curr->oparg = op_arg & 0xFF;
+ _py_set_opcode(next_instr, END_FOR);
next_instr = t2_nextinstr;
}
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 68069b8803332e..afbb56aa429cc8 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -141,7 +141,8 @@ GETITEM(PyObject *v, Py_ssize_t i) {
/* The integer overflow is checked by an assertion below. */
// TODO change this calculation when interpreter is bb aware.
-#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code)))
+#define INSTR_OFFSET() ((int)(next_instr - \
+ (frame->f_code->_tier2_info == NULL ? _PyCode_CODE(frame->f_code) : frame->f_code->_tier2_info->_bb_space->u_code)))
#define NEXTOPARG() do { \
_Py_CODEUNIT word = *next_instr; \
opcode = _Py_OPCODE(word); \
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 4e12ce60e89f7e..aee1db10f3ca5d 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2890,10 +2890,11 @@
_PyErr_Clear(tstate);
}
/* iterator ended normally */
- assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR);
Py_DECREF(iter);
STACK_SHRINK(1);
bb_test = false;
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ DISPATCH();
}
bb_test = true;
STACK_GROW(1);
@@ -4132,6 +4133,7 @@
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
+ DISPATCH();
}
}
else {
@@ -4143,6 +4145,7 @@
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback + oparg;
+ DISPATCH();
}
}
JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -4205,8 +4208,8 @@
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback);
+ t2_nextinstr = _PyTier2_LocateJumpBackwardsBB(
+ frame, cache->bb_id, -oparg, &tier1_fallback);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -4215,11 +4218,13 @@
_Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *prev = curr - 1;
assert(_Py_OPCODE(*prev) == EXTENDED_ARG);
- int op_arg = (int)(t2_nextinstr - next_instr);
+ int op_arg = -(int)(t2_nextinstr - next_instr);
+ // fprintf(stderr, "JUMP_BACKWARD oparg is %d\n", op_arg);
assert(op_arg <= INT16_MAX);
_py_set_opcode(curr, JUMP_BACKWARD);
prev->oparg = (op_arg >> 8) & 0xFF;
curr->oparg = op_arg & 0xFF;
+ _py_set_opcode(next_instr, END_FOR);
next_instr = t2_nextinstr;
DISPATCH();
}
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 45de8827a0398e..296d36d66163c9 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -915,7 +915,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BB_TEST_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
+ [BB_TEST_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
diff --git a/Python/tier2.c b/Python/tier2.c
index c99ef86cec6245..e81987598455de 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -4,11 +4,13 @@
#include "pycore_frame.h"
#include "pycore_opcode.h"
#include "pycore_pystate.h"
+#include "stdbool.h"
#include "opcode.h"
#define BB_DEBUG 1
-
+// Max typed version basic blocks per basic block
+#define MAX_BB_VERSIONS 5
// Number of potential extra instructions at end of a BB, for branch or cleanup purposes.
#define BB_EPILOG 0
@@ -424,12 +426,12 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
#endif
Py_UNREACHABLE();
}
-#if BB_DEBUG
- fprintf(stderr, "emitted logical branch %p %d\n", write_curr,
- _Py_OPCODE(branch));
-#endif
// Backwards jumps should be handled specially.
if (opcode == BB_JUMP_BACKWARD_LAZY) {
+#if BB_DEBUG
+ fprintf(stderr, "emitted backwards jump %p %d\n", write_curr,
+ _Py_OPCODE(branch));
+#endif
_py_set_opcode(write_curr, EXTENDED_ARG);
write_curr->oparg = (oparg >> 8) & 0xFF;
write_curr++;
@@ -444,7 +446,37 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
cache->bb_id = (uint16_t)(bb_id);
return write_curr;
}
+ // FOR_ITER is also a special jump
+ else if (opcode == BB_TEST_ITER) {
+#if BB_DEBUG
+ fprintf(stderr, "emitted logical branch %p %d\n", write_curr,
+ _Py_OPCODE(branch));
+#endif
+ // The oparg of FOR_ITER is a little special, the actual jump has to jump over
+ // its own cache entries, the oparg, AND the END_FOR (+1).
+ oparg = INLINE_CACHE_ENTRIES_FOR_ITER + oparg;
+ //_Py_CODEUNIT *start = write_curr;
+ _py_set_opcode(write_curr, BB_TEST_ITER);
+ write_curr->oparg = oparg;
+ write_curr++;
+ write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_FOR_ITER);
+ _py_set_opcode(write_curr, EXTENDED_ARG);
+ write_curr->oparg = (oparg >> 8) & 0xFF;
+ write_curr++;
+ _py_set_opcode(write_curr, BB_BRANCH);
+ write_curr->oparg = oparg & 0xFF;
+ write_curr++;
+ _PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
+ write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
+ assert((uint16_t)(bb_id) == bb_id);
+ cache->bb_id = (uint16_t)(bb_id);
+ return write_curr;
+ }
else {
+#if BB_DEBUG
+ fprintf(stderr, "emitted logical branch %p %d\n", write_curr,
+ _Py_OPCODE(branch));
+#endif
//_Py_CODEUNIT *start = write_curr;
_py_set_opcode(write_curr, opcode);
write_curr->oparg = oparg;
@@ -476,7 +508,7 @@ emit_scope_exit(_Py_CODEUNIT *write_curr, _Py_CODEUNIT exit)
#if BB_DEBUG
fprintf(stderr, "emitted scope exit\n");
#endif
- //// @TODO we can propogate and chain BBs across call boundaries
+ //// @TODO we can propagate and chain BBs across call boundaries
//// Thanks to CPython's inlined call frames.
//_py_set_opcode(write_curr, BB_EXIT_FRAME);
*write_curr = exit;
@@ -532,6 +564,40 @@ IS_BACKWARDS_JUMP_TARGET(PyCodeObject *co, _Py_CODEUNIT *curr)
return 0;
}
+// 1 for error, 0 for success.
+static inline int
+add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
+ int backwards_jump_target)
+{
+ // Locate where to insert the BB ID
+ int backward_jump_offset_index = 0;
+ bool found = false;
+ for (; backward_jump_offset_index < t2_info->backward_jump_count;
+ backward_jump_offset_index++) {
+ if (t2_info->backward_jump_offsets[backward_jump_offset_index] ==
+ backwards_jump_target) {
+ found = true;
+ break;
+ }
+ }
+ assert(found);
+ int jump_i = 0;
+ found = false;
+ for (; jump_i < MAX_BB_VERSIONS; jump_i++) {
+ if (t2_info->backward_jump_target_bb_ids[backward_jump_offset_index][jump_i] == -1) {
+ t2_info->backward_jump_target_bb_ids[backward_jump_offset_index][jump_i] = meta->id;
+ found = true;
+ break;
+ }
+ }
+ // Out of basic blocks versions.
+ if (!found) {
+ return 1;
+ }
+ assert(found);
+ return 0;
+}
+
// Detects a BB from the current instruction start to the end of the first basic block it sees.
// Then emits the instructions into the bb space.
//
@@ -562,24 +628,28 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
assert(co->_tier2_info != NULL);
// There are only two cases that a BB ends.
// 1. If there's a branch instruction / scope exit.
- // 2. If the instruction is a jump target.
+ // 2. If there's a type guard.
// Make a copy of the type context
PyTypeObject **type_context_copy = PyMem_Malloc(n_typecontext * sizeof(PyTypeObject *));
if (type_context_copy == NULL) {
return NULL;
}
+ memcpy(type_context_copy, type_context, n_typecontext * sizeof(PyTypeObject *));
_PyTier2BBMetadata *meta = NULL;
+ _PyTier2BBMetadata *temp_meta = NULL;
-
- memcpy(type_context_copy, type_context, n_typecontext * sizeof(PyTypeObject *));
-
+ _PyTier2Info *t2_info = co->_tier2_info;
_Py_CODEUNIT *t2_start = (_Py_CODEUNIT *)(((char *)bb_space->u_code) + bb_space->water_level);
_Py_CODEUNIT *write_i = t2_start;
PyTypeObject **stack_pointer = co->_tier2_info->types_stack;
int tos = -1;
+ // For handling of backwards jumps
+ bool starts_with_backwards_jump_target = false;
+ int backwards_jump_target_offset = -1;
+ bool virtual_start = false;
// A meta-interpreter for types.
Py_ssize_t i = (tier1_start - _PyCode_CODE(co));
@@ -598,10 +668,14 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
dispatch_opcode:
switch (opcode) {
+ case RESUME:
+ opcode = RESUME_QUICK;
+ DISPATCH();
case EXTENDED_ARG:
write_i = emit_i(write_i, EXTENDED_ARG, _Py_OPARG(*curr));
curr++;
next_instr++;
+ i++;
oparg = oparg << 8 | _Py_OPARG(*curr);
opcode = _Py_OPCODE(*curr);
caches = _PyOpcode_Caches[opcode];
@@ -654,24 +728,36 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// }
//}
default:
+ fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
if (IS_BACKWARDS_JUMP_TARGET(co, curr)) {
- // Indicates it's the start a new basic block. That's fine. Just continue.
- if (write_i == t2_start) {
- DISPATCH();
+ fprintf(stderr, "Encountered a backward jump target\n");
+ // This should be the end of another basic block, or the start of a new.
+ // Start of a new basic block, just ignore and continue.
+ if (virtual_start) {
+ fprintf(stderr, "Emitted virtual start of basic block\n");
+ starts_with_backwards_jump_target = true;
+ backwards_jump_target_offset = curr - _PyCode_CODE(co);
+ virtual_start = false;
+ goto fall_through;
}
- // Not a start of a new basic block, we need to log this as a basic block
- // and start a new BB for the sake of JUMP_BACKWARD.
+ // Else, create a virtual end to the basic block.
+ // But generate the block after that so it can fall through.
i--;
- meta = _PyTier2_AllocateBBMetaData(co, t2_start, _PyCode_CODE(co) + i, n_typecontext, type_context_copy);
+ meta = _PyTier2_AllocateBBMetaData(co,
+ t2_start, _PyCode_CODE(co) + i, n_typecontext, type_context_copy);
if (meta == NULL) {
PyMem_Free(type_context_copy);
return NULL;
}
- // Set the new start
+ bb_space->water_level += (write_i - t2_start) * sizeof(_Py_CODEUNIT);
+ // Reset the start
t2_start = write_i;
- // Don't increment, just fall through and let the new BB start with this
- // instruction.
+ i++;
+ virtual_start = true;
+ // Don't change opcode or oparg, let us handle it again.
+ DISPATCH_GOTO();
}
+ fall_through:
// These are definitely the end of a basic block.
if (IS_SCOPE_EXIT_OPCODE(opcode)) {
// Emit the scope exit instruction.
@@ -679,8 +765,6 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
END();
}
-
-
// Jumps may be the end of a basic block if they are conditional (a branch).
if (IS_JUMP_OPCODE(opcode)) {
// Unconditional forward jump... continue with the BB without writing the jump.
@@ -692,8 +776,6 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// AllocateBBMetaData will increment.
write_i = emit_logical_branch(write_i, *curr,
co->_tier2_info->bb_data_curr);
- // Mainly for FOR_ITER
- write_i = emit_cache_entries(write_i, caches);
i += caches;
END();
}
@@ -702,20 +784,32 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
}
end:
-//#if BB_DEBUG
-// fprintf(stderr, "i is %Id\n", i);
-//#endif
// Create the tier 2 BB
- meta = _PyTier2_AllocateBBMetaData(co, t2_start,
+ temp_meta = _PyTier2_AllocateBBMetaData(co, t2_start,
// + 1 because we want to start with the NEXT instruction for the scan
_PyCode_CODE(co) + i + 1, n_typecontext, type_context_copy);
- // Tell BB space the number of bytes we wrote.
- // -1 becaues write_i points to the instruction AFTER the end
- bb_space->water_level += (write_i - t2_start - 1) * sizeof(_Py_CODEUNIT);
+ // We need to return the first block to enter into. If there is already a block generated
+ // before us, then we use that instead of the most recent block.
+ if (meta == NULL) {
+ meta = temp_meta;
+ }
+ if (starts_with_backwards_jump_target) {
+ // Add the basic block to the jump ids
+ if (add_metadata_to_jump_2d_array(t2_info, temp_meta, backwards_jump_target_offset) < 0) {
+ PyMem_Free(meta);
+ PyMem_Free(temp_meta);
+ PyMem_Free(type_context_copy);
+ return NULL;
+ }
+ }
+ // Tell BB space the number of bytes we wrote.
+ // -1 becaues write_i points to the instruction AFTER the end
+ bb_space->water_level += (write_i - t2_start) * sizeof(_Py_CODEUNIT);
#if BB_DEBUG
- fprintf(stderr, "Generated BB T2 Start: %p\n", meta->tier2_start);
+ fprintf(stderr, "Generated BB T2 Start: %p, T1 offset: %d\n", meta->tier2_start,
+ meta->tier1_end - _PyCode_CODE(co));
#endif
- return meta;
+ return meta;
}
@@ -728,6 +822,29 @@ compare_ints(const void *a, const void *b)
return *(int *)a - *(int *)b;
}
+static int
+allocate_jump_offset_2d_array(int backwards_jump_count, int **backward_jump_target_bb_ids)
+{
+ int done = 0;
+ for (int i = 0; i < backwards_jump_count; i++) {
+ int *jump_offsets = PyMem_Malloc(sizeof(int) * MAX_BB_VERSIONS);
+ if (jump_offsets == NULL) {
+ goto error;
+ }
+ for (int i = 0; i < MAX_BB_VERSIONS; i++) {
+ jump_offsets[i] = -1;
+ }
+ done++;
+ backward_jump_target_bb_ids[i] = jump_offsets;
+ }
+ return 0;
+error:
+ for (int i = 0; i < done; i++) {
+ PyMem_Free(backward_jump_target_bb_ids[i]);
+ }
+ return 1;
+}
+
// Returns 1 on error, 0 on success. Populates the backwards jump target offset
// array for a code object..
static int
@@ -771,14 +888,16 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
if (backward_jump_offsets == NULL) {
return 1;
}
- int *backward_jump_target_bb_ids = PyMem_Malloc(backwards_jump_count * sizeof(int));
+ int **backward_jump_target_bb_ids = PyMem_Malloc(backwards_jump_count * sizeof(int *));
if (backward_jump_target_bb_ids == NULL) {
PyMem_Free(backward_jump_offsets);
return 1;
}
- for (int i = 0; i < backwards_jump_count; i++) {
- backward_jump_target_bb_ids[i] = -1;
+ if (allocate_jump_offset_2d_array(backwards_jump_count, backward_jump_target_bb_ids)) {
+ PyMem_Free(backward_jump_offsets);
+ return 1;
}
+
_Py_CODEUNIT *start = _PyCode_CODE(co);
int curr_i = 0;
for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
@@ -787,7 +906,9 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
int oparg = _Py_OPARG(instr);
if (IS_JUMP_BACKWARDS_OPCODE(opcode)) {
- _Py_CODEUNIT *target = curr + _Py_OPARG(instr);
+ // + 1 because it's calculated from nextinstr (see JUMPBY in ceval.c)
+ _Py_CODEUNIT *target = curr + 1 - oparg;
+ fprintf(stderr, "jump target opcode is %d\n", _Py_OPCODE(*target));
// (in terms of offset from start of co_code_adaptive)
backward_jump_offsets[curr_i] = (int)(target - start);
curr_i++;
@@ -896,12 +1017,18 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
for (Py_ssize_t curr = 0; curr < Py_SIZE(co); curr++) {
_Py_CODEUNIT *curr_instr = _PyCode_CODE(co) + curr;
if (IS_FORBIDDEN_OPCODE(_PyOpcode_Deopt[_Py_OPCODE(*curr_instr)])) {
+#if BB_DEBUG
+ fprintf(stderr, "FORBIDDEN OPCODE %d\n", _Py_OPCODE(*curr_instr));
+#endif
return NULL;
}
optimizable |= IS_OPTIMIZABLE_OPCODE(_Py_OPCODE(*curr_instr), _Py_OPARG(*curr_instr));
}
if (!optimizable) {
+#if BB_DEBUG
+ fprintf(stderr, "NOT OPTIMIZABLE\n");
+#endif
return NULL;
}
@@ -1045,13 +1172,28 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int j
_PyTier2Info *t2_info = co->_tier2_info;
int jump_offset = (int)(tier1_jump_target - _PyCode_CODE(co));
int matching_bb_id = -1;
+
+ fprintf(stderr, "finding jump target: %d\n", jump_offset);
for (int i = 0; i < t2_info->backward_jump_count; i++) {
+ fprintf(stderr, "jump offset checked: %d\n", t2_info->backward_jump_offsets[i]);
if (t2_info->backward_jump_offsets[i] == jump_offset) {
- matching_bb_id = t2_info->backward_jump_target_bb_ids[i];
+ for (int x = 0; x < MAX_BB_VERSIONS; x++) {
+ fprintf(stderr, "jump target BB ID: %d\n",
+ t2_info->backward_jump_target_bb_ids[i][x]);
+ // @TODO, this is where the diff function is supposed to be
+ // it will calculate the closest type context BB
+ // For now just any valid BB (>= 0) is used.
+ if (t2_info->backward_jump_target_bb_ids[i][x] >= 0) {
+ matching_bb_id = t2_info->backward_jump_target_bb_ids[i][x];
+ break;
+ }
+ }
+ break;
}
}
assert(matching_bb_id >= 0);
assert(matching_bb_id <= t2_info->bb_data_curr);
+ fprintf(stderr, "Found jump target BB ID: %d\n", matching_bb_id);
_PyTier2BBMetadata *target_metadata = t2_info->bb_data[matching_bb_id];
return target_metadata->tier2_start;
}
From c5818876016d7e8b392d42438e06930c41f8fcc1 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 21 Feb 2023 22:19:17 +0800
Subject: [PATCH 033/280] Part 4.2: Properly handle END_FOR
---
Python/bytecodes.c | 3 +--
Python/generated_cases.c.h | 6 ++----
Python/opcode_metadata.h | 2 +-
Python/tier2.c | 15 ++++++++++++---
4 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 974ba65866b26e..9602fea53d28b4 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2267,7 +2267,7 @@ dummy_func(
}
// FOR_ITER
- inst(BB_TEST_ITER, (unused / 1, iter -- iter, next)) {
+ inst(BB_TEST_ITER, (iter -- iter, next)) {
next = (*Py_TYPE(iter)->tp_iternext)(iter);
if (next == NULL) {
if (_PyErr_Occurred(tstate)) {
@@ -2283,7 +2283,6 @@ dummy_func(
Py_DECREF(iter);
STACK_SHRINK(1);
bb_test = false;
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
DISPATCH();
}
bb_test = true;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index aee1db10f3ca5d..42bfd68f9650f8 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2893,13 +2893,11 @@
Py_DECREF(iter);
STACK_SHRINK(1);
bb_test = false;
- JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
DISPATCH();
}
bb_test = true;
STACK_GROW(1);
POKE(1, next);
- JUMPBY(1);
DISPATCH();
}
@@ -4165,7 +4163,7 @@
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- // @TODO: Rewrite TEST Instruction to a JUMP above.
+ // @TODO: Rewrite TEST intruction above to a JUMP above.
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id, oparg, &tier1_fallback);
@@ -4187,7 +4185,7 @@
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- // @TODO: Rewrite TEST Instruction to a JUMP above.
+ // @TODO: Rewrite TEST intruction above to a JUMP above..
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id, oparg, &tier1_fallback);
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 296d36d66163c9..aacdfe3ced34d3 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -915,7 +915,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BB_TEST_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BB_TEST_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
diff --git a/Python/tier2.c b/Python/tier2.c
index e81987598455de..c8022ac6048107 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -453,13 +453,15 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
_Py_OPCODE(branch));
#endif
// The oparg of FOR_ITER is a little special, the actual jump has to jump over
- // its own cache entries, the oparg, AND the END_FOR (+1).
- oparg = INLINE_CACHE_ENTRIES_FOR_ITER + oparg;
+ // its own cache entries, the oparg, -1 to tell it to start generating from the
+ // END_FOR. However, at runtime, we will skip this END_FOR.
+ oparg = INLINE_CACHE_ENTRIES_FOR_ITER + oparg - 1;
//_Py_CODEUNIT *start = write_curr;
_py_set_opcode(write_curr, BB_TEST_ITER);
write_curr->oparg = oparg;
write_curr++;
- write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_FOR_ITER);
+ // We don't need to emit inline cache entries, because when this converts,
+ // we can just make use of the EXTENDED_ARG and BB_BRANCH below.
_py_set_opcode(write_curr, EXTENDED_ARG);
write_curr->oparg = (oparg >> 8) & 0xFF;
write_curr++;
@@ -684,6 +686,13 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
case COMPARE_AND_BRANCH:
opcode = COMPARE_OP;
DISPATCH();
+ case END_FOR:
+ // Assert that we are the start of a BB
+ assert(t2_start == write_i);
+ // Though we want to emit this, we don't want to start execution from END_FOR.
+ // So we tell the BB to skip over it.
+ t2_start++;
+ DISPATCH();
// FOR_ITER must be handled separately from other opcodes as it has
// CACHE entries following it.
//case LOAD_FAST:
From 33be113c3951f6583ac8dcb7f45c37eca103649d Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 22 Feb 2023 12:07:01 +0800
Subject: [PATCH 034/280] Part 5: Complete the lazy generation of basic blocks
---
Include/cpython/code.h | 15 --
Include/internal/pycore_code.h | 4 +-
Include/internal/pycore_opcode.h | 68 +++++----
.../internal/pycore_opcode_macro_to_micro.h | 2 +
Include/opcode.h | 140 ++++++++++--------
Lib/opcode.py | 12 ++
Python/bytecodes.c | 61 ++++----
Python/generated_cases.c.h | 59 +++++---
Python/opcode_metadata.h | 18 ++-
Python/opcode_targets.h | 46 +++---
Python/tier2.c | 128 +++++++++++++---
11 files changed, 342 insertions(+), 211 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 9675a6017e555a..3e413c618d5eb0 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -42,21 +42,6 @@ typedef struct {
PyObject *_co_freevars;
} _PyCoCached;
-
-//// Used to store intermediate code information for the tier 2 "translator".
-//// This is eventually finally converted to _PyTier2BB s.
-//// Code Object -> _PyTier2IntermediateCode -> _PyTier2BB s.
-//typedef struct _PyTier2IntermediateCode {
-// /* Number of entries in _jump_targets */
-// int _jump_target_count;
-// /* sorted ascending offsets (from start of field code) for jump targets */
-// // The offsets are in number of _Py_CODEUNITs
-// int *_jump_targets;
-// int n_instrs;
-// _Py_CODEUNIT code[1];
-//} _PyTier2IntermediateCode;
-
-
// Tier 2 interpreter information
typedef struct _PyTier2BBMetadata {
// Index into _PyTier2Info->bb_data
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index d5612e5c5be40b..914c0c03e95c9b 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -263,8 +263,8 @@ extern _Py_CODEUNIT *_PyTier2_GenerateNextBB(
extern _Py_CODEUNIT *_PyTier2_LocateJumpBackwardsBB(
struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
_Py_CODEUNIT **tier1_fallback);
-
-
+extern void _PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target);
+extern void _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *target);
#ifdef Py_STATS
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 689564a071c483..16e0f7f2f65033 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -56,6 +56,10 @@ const uint8_t _PyOpcode_Caches[256] = {
};
const uint8_t _PyOpcode_Deopt[256] = {
+ [BB_TEST_ITER_GEN] = BB_TEST_ITER,
+ [BB_TEST_ITER_LIST] = BB_TEST_ITER,
+ [BB_TEST_ITER_RANGE] = BB_TEST_ITER,
+ [BB_TEST_ITER_TUPLE] = BB_TEST_ITER,
[BEFORE_ASYNC_WITH] = BEFORE_ASYNC_WITH,
[BEFORE_WITH] = BEFORE_WITH,
[BINARY_OP] = BINARY_OP,
@@ -297,30 +301,30 @@ static const char *const _PyOpcode_OpName[263] = {
[FOR_ITER_TUPLE] = "FOR_ITER_TUPLE",
[FOR_ITER_RANGE] = "FOR_ITER_RANGE",
[FOR_ITER_GEN] = "FOR_ITER_GEN",
+ [BB_TEST_ITER_LIST] = "BB_TEST_ITER_LIST",
+ [BB_TEST_ITER_TUPLE] = "BB_TEST_ITER_TUPLE",
+ [BB_TEST_ITER_RANGE] = "BB_TEST_ITER_RANGE",
+ [GET_ITER] = "GET_ITER",
+ [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER",
+ [BB_TEST_ITER_GEN] = "BB_TEST_ITER_GEN",
+ [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
[LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS",
[LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
+ [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
+ [RETURN_GENERATOR] = "RETURN_GENERATOR",
[LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
- [GET_ITER] = "GET_ITER",
- [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER",
[LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
- [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
[LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
[LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
- [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
- [RETURN_GENERATOR] = "RETURN_GENERATOR",
[LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
[LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT",
[LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
+ [RETURN_VALUE] = "RETURN_VALUE",
[LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
+ [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
[LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
[LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
[LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
- [RETURN_VALUE] = "RETURN_VALUE",
- [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
- [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
- [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
- [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
- [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[POP_EXCEPT] = "POP_EXCEPT",
[STORE_NAME] = "STORE_NAME",
[DELETE_NAME] = "DELETE_NAME",
@@ -345,7 +349,7 @@ static const char *const _PyOpcode_OpName[263] = {
[JUMP_FORWARD] = "JUMP_FORWARD",
[JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP",
[JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP",
- [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
+ [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
[POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
[POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
[LOAD_GLOBAL] = "LOAD_GLOBAL",
@@ -375,7 +379,7 @@ static const char *const _PyOpcode_OpName[263] = {
[JUMP_BACKWARD] = "JUMP_BACKWARD",
[COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH",
[CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
- [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
+ [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
[EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD",
@@ -385,28 +389,34 @@ static const char *const _PyOpcode_OpName[263] = {
[YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS",
- [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
- [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
+ [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
+ [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING",
- [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
- [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
- [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
- [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
+ [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
+ [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
+ [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
+ [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
+ [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
+ [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
+ [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
+ [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
[SEND_GEN] = "SEND_GEN",
- [BB_BRANCH] = "BB_BRANCH",
- [BB_BRANCH_IF_FLAG_UNSET] = "BB_BRANCH_IF_FLAG_UNSET",
- [BB_BRANCH_IF_FLAG_SET] = "BB_BRANCH_IF_FLAG_SET",
- [BB_TEST_ITER] = "BB_TEST_ITER",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
[CALL_INTRINSIC_1] = "CALL_INTRINSIC_1",
[CALL_INTRINSIC_2] = "CALL_INTRINSIC_2",
+ [BB_BRANCH] = "BB_BRANCH",
+ [BB_BRANCH_IF_FLAG_UNSET] = "BB_BRANCH_IF_FLAG_UNSET",
+ [BB_BRANCH_IF_FLAG_SET] = "BB_BRANCH_IF_FLAG_SET",
+ [BB_JUMP_IF_FLAG_UNSET] = "BB_JUMP_IF_FLAG_UNSET",
+ [BB_JUMP_IF_FLAG_SET] = "BB_JUMP_IF_FLAG_SET",
+ [BB_TEST_ITER] = "BB_TEST_ITER",
[BB_TEST_IF_FALSE_OR_POP] = "BB_TEST_IF_FALSE_OR_POP",
[BB_TEST_IF_TRUE_OR_POP] = "BB_TEST_IF_TRUE_OR_POP",
[BB_TEST_POP_IF_FALSE] = "BB_TEST_POP_IF_FALSE",
@@ -416,12 +426,6 @@ static const char *const _PyOpcode_OpName[263] = {
[BB_JUMP_BACKWARD_LAZY] = "BB_JUMP_BACKWARD_LAZY",
[BINARY_CHECK_INT] = "BINARY_CHECK_INT",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
- [184] = "<184>",
- [185] = "<185>",
- [186] = "<186>",
- [187] = "<187>",
- [188] = "<188>",
- [189] = "<189>",
[190] = "<190>",
[191] = "<191>",
[192] = "<192>",
@@ -500,12 +504,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 184: \
- case 185: \
- case 186: \
- case 187: \
- case 188: \
- case 189: \
case 190: \
case 191: \
case 192: \
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index 9b9fe6117725b3..0d107771b428ea 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -190,7 +190,9 @@ extern const int _Py_MacroOpUOpCount[] = {
[CACHE] = 1,
[BB_BRANCH] = 1,
[BB_BRANCH_IF_FLAG_UNSET] = 1,
+[BB_JUMP_IF_FLAG_UNSET] = 1,
[BB_BRANCH_IF_FLAG_SET] = 1,
+[BB_JUMP_IF_FLAG_SET] = 1,
[BB_JUMP_BACKWARD_LAZY] = 1,
};
diff --git a/Include/opcode.h b/Include/opcode.h
index 5cdac760821523..ec2989e8abaed5 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -163,32 +163,36 @@ extern "C" {
#define FOR_ITER_TUPLE 62
#define FOR_ITER_RANGE 63
#define FOR_ITER_GEN 64
-#define LOAD_ATTR_CLASS 65
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 66
-#define LOAD_ATTR_INSTANCE_VALUE 67
-#define LOAD_ATTR_MODULE 70
-#define LOAD_ATTR_PROPERTY 72
-#define LOAD_ATTR_SLOT 73
-#define LOAD_ATTR_WITH_HINT 76
-#define LOAD_ATTR_METHOD_LAZY_DICT 77
-#define LOAD_ATTR_METHOD_NO_DICT 78
-#define LOAD_ATTR_METHOD_WITH_VALUES 79
-#define LOAD_CONST__LOAD_FAST 80
-#define LOAD_FAST__LOAD_CONST 81
-#define LOAD_FAST__LOAD_FAST 82
-#define LOAD_GLOBAL_BUILTIN 84
-#define LOAD_GLOBAL_MODULE 86
-#define STORE_ATTR_INSTANCE_VALUE 87
-#define STORE_ATTR_SLOT 88
-#define STORE_ATTR_WITH_HINT 113
-#define STORE_FAST__LOAD_FAST 143
-#define STORE_FAST__STORE_FAST 153
-#define STORE_SUBSCR_DICT 154
-#define STORE_SUBSCR_LIST_INT 158
-#define UNPACK_SEQUENCE_LIST 159
-#define UNPACK_SEQUENCE_TUPLE 160
-#define UNPACK_SEQUENCE_TWO_TUPLE 161
-#define SEND_GEN 166
+#define BB_TEST_ITER_LIST 65
+#define BB_TEST_ITER_TUPLE 66
+#define BB_TEST_ITER_RANGE 67
+#define BB_TEST_ITER_GEN 70
+#define LOAD_ATTR_CLASS 72
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 73
+#define LOAD_ATTR_INSTANCE_VALUE 76
+#define LOAD_ATTR_MODULE 77
+#define LOAD_ATTR_PROPERTY 78
+#define LOAD_ATTR_SLOT 79
+#define LOAD_ATTR_WITH_HINT 80
+#define LOAD_ATTR_METHOD_LAZY_DICT 81
+#define LOAD_ATTR_METHOD_NO_DICT 82
+#define LOAD_ATTR_METHOD_WITH_VALUES 84
+#define LOAD_CONST__LOAD_FAST 86
+#define LOAD_FAST__LOAD_CONST 87
+#define LOAD_FAST__LOAD_FAST 88
+#define LOAD_GLOBAL_BUILTIN 113
+#define LOAD_GLOBAL_MODULE 143
+#define STORE_ATTR_INSTANCE_VALUE 153
+#define STORE_ATTR_SLOT 154
+#define STORE_ATTR_WITH_HINT 158
+#define STORE_FAST__LOAD_FAST 159
+#define STORE_FAST__STORE_FAST 160
+#define STORE_SUBSCR_DICT 161
+#define STORE_SUBSCR_LIST_INT 166
+#define UNPACK_SEQUENCE_LIST 167
+#define UNPACK_SEQUENCE_TUPLE 168
+#define UNPACK_SEQUENCE_TWO_TUPLE 169
+#define SEND_GEN 170
#define DO_TRACING 255
// Tier 2 interpreter ops
#define RESUME_QUICK 5
@@ -228,46 +232,52 @@ extern "C" {
#define FOR_ITER_TUPLE 62
#define FOR_ITER_RANGE 63
#define FOR_ITER_GEN 64
-#define LOAD_ATTR_CLASS 65
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 66
-#define LOAD_ATTR_INSTANCE_VALUE 67
-#define LOAD_ATTR_MODULE 70
-#define LOAD_ATTR_PROPERTY 72
-#define LOAD_ATTR_SLOT 73
-#define LOAD_ATTR_WITH_HINT 76
-#define LOAD_ATTR_METHOD_LAZY_DICT 77
-#define LOAD_ATTR_METHOD_NO_DICT 78
-#define LOAD_ATTR_METHOD_WITH_VALUES 79
-#define LOAD_CONST__LOAD_FAST 80
-#define LOAD_FAST__LOAD_CONST 81
-#define LOAD_FAST__LOAD_FAST 82
-#define LOAD_GLOBAL_BUILTIN 84
-#define LOAD_GLOBAL_MODULE 86
-#define STORE_ATTR_INSTANCE_VALUE 87
-#define STORE_ATTR_SLOT 88
-#define STORE_ATTR_WITH_HINT 113
-#define STORE_FAST__LOAD_FAST 143
-#define STORE_FAST__STORE_FAST 153
-#define STORE_SUBSCR_DICT 154
-#define STORE_SUBSCR_LIST_INT 158
-#define UNPACK_SEQUENCE_LIST 159
-#define UNPACK_SEQUENCE_TUPLE 160
-#define UNPACK_SEQUENCE_TWO_TUPLE 161
-#define SEND_GEN 166
+#define BB_TEST_ITER_LIST 65
+#define BB_TEST_ITER_TUPLE 66
+#define BB_TEST_ITER_RANGE 67
+#define BB_TEST_ITER_GEN 70
+#define LOAD_ATTR_CLASS 72
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 73
+#define LOAD_ATTR_INSTANCE_VALUE 76
+#define LOAD_ATTR_MODULE 77
+#define LOAD_ATTR_PROPERTY 78
+#define LOAD_ATTR_SLOT 79
+#define LOAD_ATTR_WITH_HINT 80
+#define LOAD_ATTR_METHOD_LAZY_DICT 81
+#define LOAD_ATTR_METHOD_NO_DICT 82
+#define LOAD_ATTR_METHOD_WITH_VALUES 84
+#define LOAD_CONST__LOAD_FAST 86
+#define LOAD_FAST__LOAD_CONST 87
+#define LOAD_FAST__LOAD_FAST 88
+#define LOAD_GLOBAL_BUILTIN 113
+#define LOAD_GLOBAL_MODULE 143
+#define STORE_ATTR_INSTANCE_VALUE 153
+#define STORE_ATTR_SLOT 154
+#define STORE_ATTR_WITH_HINT 158
+#define STORE_FAST__LOAD_FAST 159
+#define STORE_FAST__STORE_FAST 160
+#define STORE_SUBSCR_DICT 161
+#define STORE_SUBSCR_LIST_INT 166
+#define UNPACK_SEQUENCE_LIST 167
+#define UNPACK_SEQUENCE_TUPLE 168
+#define UNPACK_SEQUENCE_TWO_TUPLE 169
+#define SEND_GEN 170
#define DO_TRACING 255
-#define BB_BRANCH 167
-#define BB_BRANCH_IF_FLAG_UNSET 168
-#define BB_BRANCH_IF_FLAG_SET 169
-#define BB_TEST_ITER 170
-#define BB_TEST_IF_FALSE_OR_POP 175
-#define BB_TEST_IF_TRUE_OR_POP 176
-#define BB_TEST_POP_IF_FALSE 177
-#define BB_TEST_POP_IF_TRUE 178
-#define BB_TEST_POP_IF_NOT_NONE 179
-#define BB_TEST_POP_IF_NONE 180
-#define BB_JUMP_BACKWARD_LAZY 181
-#define BINARY_CHECK_INT 182
-#define BINARY_OP_ADD_INT_REST 183
+#define BB_BRANCH 175
+#define BB_BRANCH_IF_FLAG_UNSET 176
+#define BB_BRANCH_IF_FLAG_SET 177
+#define BB_JUMP_IF_FLAG_UNSET 178
+#define BB_JUMP_IF_FLAG_SET 179
+#define BB_TEST_ITER 180
+#define BB_TEST_IF_FALSE_OR_POP 181
+#define BB_TEST_IF_TRUE_OR_POP 182
+#define BB_TEST_POP_IF_FALSE 183
+#define BB_TEST_POP_IF_TRUE 184
+#define BB_TEST_POP_IF_NOT_NONE 185
+#define BB_TEST_POP_IF_NONE 186
+#define BB_JUMP_BACKWARD_LAZY 187
+#define BINARY_CHECK_INT 188
+#define BINARY_OP_ADD_INT_REST 189
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 7eb00d7f9e2e04..fb4d55637fdfa1 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -330,6 +330,12 @@ def pseudo_op(name, op, real_ops):
"FOR_ITER_RANGE",
"FOR_ITER_GEN",
],
+ "BB_TEST_ITER": [
+ "BB_TEST_ITER_LIST",
+ "BB_TEST_ITER_TUPLE",
+ "BB_TEST_ITER_RANGE",
+ "BB_TEST_ITER_GEN",
+ ],
"LOAD_ATTR": [
# These potentially push [NULL, bound method] onto the stack.
"LOAD_ATTR_CLASS",
@@ -416,6 +422,9 @@ def pseudo_op(name, op, real_ops):
"FOR_ITER": {
"counter": 1,
},
+ "BB_TEST_ITER": {
+ "counter": 1,
+ },
"LOAD_ATTR": {
"counter": 1,
"version": 2,
@@ -458,6 +467,9 @@ def pseudo_op(name, op, real_ops):
# This happens when the fall through is generated, but not the other branch.
'BB_BRANCH_IF_FLAG_UNSET', # When alternate exit is not yet generated.
'BB_BRANCH_IF_FLAG_SET', # When successor exit is not yet generated.
+ # When both edges are generated
+ 'BB_JUMP_IF_FLAG_UNSET',
+ 'BB_JUMP_IF_FLAG_SET',
# The final form is that once both branches are generated, we can just
# override these instructions with a generic JUMP.
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 9602fea53d28b4..caffa4ad6cba86 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2267,7 +2267,7 @@ dummy_func(
}
// FOR_ITER
- inst(BB_TEST_ITER, (iter -- iter, next)) {
+ inst(BB_TEST_ITER, (unused/1, iter -- iter, next)) {
next = (*Py_TYPE(iter)->tp_iternext)(iter);
if (next == NULL) {
if (_PyErr_Occurred(tstate)) {
@@ -2283,6 +2283,7 @@ dummy_func(
Py_DECREF(iter);
STACK_SHRINK(1);
bb_test = false;
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
DISPATCH();
}
bb_test = true;
@@ -3285,7 +3286,7 @@ dummy_func(
}
// Tier 2 instructions
- inst(BB_BRANCH, (--)) {
+ inst(BB_BRANCH, (unused/1 --)) {
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
@@ -3313,24 +3314,20 @@ dummy_func(
DISPATCH();
}
}
- JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
// Their addresses should be the same. Because
// The first BB should be generated right after the previous one.
- if (next_instr != t2_nextinstr) {
- fprintf(stderr, "next: %p, t2 next: %p\n", next_instr, t2_nextinstr);
- }
- assert(next_instr == t2_nextinstr);
+ assert(next_instr + INLINE_CACHE_ENTRIES_BB_BRANCH == t2_nextinstr);
next_instr = t2_nextinstr;
+ DISPATCH();
}
- inst(BB_BRANCH_IF_FLAG_UNSET, (--)) {
+ inst(BB_BRANCH_IF_FLAG_UNSET, (unused/1 --)) {
if (!bb_test) {
+ _Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- // @TODO: Rewrite TEST intruction above to a JUMP above.
-
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id, oparg, &tier1_fallback);
if (t2_nextinstr == NULL) {
@@ -3338,14 +3335,24 @@ dummy_func(
next_instr = tier1_fallback;
}
next_instr = t2_nextinstr;
+
+ // Rewrite self
+ _PyTier2_RewriteForwardJump(curr, next_instr);
+ DISPATCH();
}
- else {
- JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ }
+
+ inst(BB_JUMP_IF_FLAG_UNSET, (unused/1 --)) {
+ if (!bb_test) {
+ JUMPBY(oparg);
+ DISPATCH();
}
+ // Fall through to next instruction.
}
- inst(BB_BRANCH_IF_FLAG_SET, (--)) {
+ inst(BB_BRANCH_IF_FLAG_SET, (unused/1 --)) {
if (bb_test) {
+ _Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
@@ -3359,13 +3366,23 @@ dummy_func(
next_instr = tier1_fallback;
}
next_instr = t2_nextinstr;
+
+ // Rewrite self
+ _PyTier2_RewriteForwardJump(curr, next_instr);
+ DISPATCH();
}
- else {
- JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ }
+
+ inst(BB_JUMP_IF_FLAG_SET, (unused/1 --)) {
+ if (bb_test) {
+ JUMPBY(oparg);
+ DISPATCH();
}
+ // Fall through to next instruction.
}
inst(BB_JUMP_BACKWARD_LAZY, (--)) {
+ _Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
@@ -3376,18 +3393,10 @@ dummy_func(
// Fall back to tier 1.
next_instr = tier1_fallback;
}
- // Rewrite self
- _Py_CODEUNIT *curr = next_instr - 1;
- _Py_CODEUNIT *prev = curr - 1;
- assert(_Py_OPCODE(*prev) == EXTENDED_ARG);
- int op_arg = -(int)(t2_nextinstr - next_instr);
- // fprintf(stderr, "JUMP_BACKWARD oparg is %d\n", op_arg);
- assert(op_arg <= INT16_MAX);
- _py_set_opcode(curr, JUMP_BACKWARD);
- prev->oparg = (op_arg >> 8) & 0xFF;
- curr->oparg = op_arg & 0xFF;
- _py_set_opcode(next_instr, END_FOR);
next_instr = t2_nextinstr;
+
+ // Rewrite self
+ _PyTier2_RewriteBackwardJump(curr, next_instr);
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 42bfd68f9650f8..5b6e1bd9c1e1cb 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2893,11 +2893,13 @@
Py_DECREF(iter);
STACK_SHRINK(1);
bb_test = false;
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
DISPATCH();
}
bb_test = true;
STACK_GROW(1);
POKE(1, next);
+ JUMPBY(1);
DISPATCH();
}
@@ -4146,25 +4148,20 @@
DISPATCH();
}
}
- JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
// Their addresses should be the same. Because
// The first BB should be generated right after the previous one.
- if (next_instr != t2_nextinstr) {
- fprintf(stderr, "next: %p, t2 next: %p\n", next_instr, t2_nextinstr);
- }
- assert(next_instr == t2_nextinstr);
+ assert(next_instr + INLINE_CACHE_ENTRIES_BB_BRANCH == t2_nextinstr);
next_instr = t2_nextinstr;
DISPATCH();
}
TARGET(BB_BRANCH_IF_FLAG_UNSET) {
if (!bb_test) {
+ _Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- // @TODO: Rewrite TEST intruction above to a JUMP above.
-
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id, oparg, &tier1_fallback);
if (t2_nextinstr == NULL) {
@@ -4172,15 +4169,28 @@
next_instr = tier1_fallback;
}
next_instr = t2_nextinstr;
+
+ // Rewrite self
+ _PyTier2_RewriteForwardJump(curr, next_instr);
+ DISPATCH();
}
- else {
- JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BB_JUMP_IF_FLAG_UNSET) {
+ if (!bb_test) {
+ JUMPBY(oparg);
+ DISPATCH();
}
+ // Fall through to next instruction.
+ JUMPBY(1);
DISPATCH();
}
TARGET(BB_BRANCH_IF_FLAG_SET) {
if (bb_test) {
+ _Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
@@ -4194,14 +4204,27 @@
next_instr = tier1_fallback;
}
next_instr = t2_nextinstr;
+
+ // Rewrite self
+ _PyTier2_RewriteForwardJump(curr, next_instr);
+ DISPATCH();
}
- else {
- JUMPBY(INLINE_CACHE_ENTRIES_BB_BRANCH);
+ JUMPBY(1);
+ DISPATCH();
+ }
+
+ TARGET(BB_JUMP_IF_FLAG_SET) {
+ if (bb_test) {
+ JUMPBY(oparg);
+ DISPATCH();
}
+ // Fall through to next instruction.
+ JUMPBY(1);
DISPATCH();
}
TARGET(BB_JUMP_BACKWARD_LAZY) {
+ _Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
@@ -4212,17 +4235,9 @@
// Fall back to tier 1.
next_instr = tier1_fallback;
}
- // Rewrite self
- _Py_CODEUNIT *curr = next_instr - 1;
- _Py_CODEUNIT *prev = curr - 1;
- assert(_Py_OPCODE(*prev) == EXTENDED_ARG);
- int op_arg = -(int)(t2_nextinstr - next_instr);
- // fprintf(stderr, "JUMP_BACKWARD oparg is %d\n", op_arg);
- assert(op_arg <= INT16_MAX);
- _py_set_opcode(curr, JUMP_BACKWARD);
- prev->oparg = (op_arg >> 8) & 0xFF;
- curr->oparg = op_arg & 0xFF;
- _py_set_opcode(next_instr, END_FOR);
next_instr = t2_nextinstr;
+
+ // Rewrite self
+ _PyTier2_RewriteBackwardJump(curr, next_instr);
DISPATCH();
}
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index aacdfe3ced34d3..2b2148977635b8 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -372,8 +372,12 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 0;
case BB_BRANCH_IF_FLAG_UNSET:
return 0;
+ case BB_JUMP_IF_FLAG_UNSET:
+ return 0;
case BB_BRANCH_IF_FLAG_SET:
return 0;
+ case BB_JUMP_IF_FLAG_SET:
+ return 0;
case BB_JUMP_BACKWARD_LAZY:
return 0;
default:
@@ -752,8 +756,12 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case BB_BRANCH_IF_FLAG_UNSET:
return 0;
+ case BB_JUMP_IF_FLAG_UNSET:
+ return 0;
case BB_BRANCH_IF_FLAG_SET:
return 0;
+ case BB_JUMP_IF_FLAG_SET:
+ return 0;
case BB_JUMP_BACKWARD_LAZY:
return 0;
default:
@@ -915,7 +923,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BB_TEST_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BB_TEST_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
@@ -956,9 +964,11 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[SWAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[EXTENDED_ARG] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[CACHE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BB_BRANCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BB_BRANCH_IF_FLAG_UNSET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BB_BRANCH_IF_FLAG_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BB_BRANCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
+ [BB_BRANCH_IF_FLAG_UNSET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
+ [BB_JUMP_IF_FLAG_UNSET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
+ [BB_BRANCH_IF_FLAG_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
+ [BB_JUMP_IF_FLAG_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[BB_JUMP_BACKWARD_LAZY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
};
#endif
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index e3bb0c3df2838f..c377c673f85cb2 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -64,30 +64,30 @@ static void *opcode_targets[256] = {
&&TARGET_FOR_ITER_TUPLE,
&&TARGET_FOR_ITER_RANGE,
&&TARGET_FOR_ITER_GEN,
+ &&TARGET_BB_TEST_ITER_LIST,
+ &&TARGET_BB_TEST_ITER_TUPLE,
+ &&TARGET_BB_TEST_ITER_RANGE,
+ &&TARGET_GET_ITER,
+ &&TARGET_GET_YIELD_FROM_ITER,
+ &&TARGET_BB_TEST_ITER_GEN,
+ &&TARGET_LOAD_BUILD_CLASS,
&&TARGET_LOAD_ATTR_CLASS,
&&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
+ &&TARGET_LOAD_ASSERTION_ERROR,
+ &&TARGET_RETURN_GENERATOR,
&&TARGET_LOAD_ATTR_INSTANCE_VALUE,
- &&TARGET_GET_ITER,
- &&TARGET_GET_YIELD_FROM_ITER,
&&TARGET_LOAD_ATTR_MODULE,
- &&TARGET_LOAD_BUILD_CLASS,
&&TARGET_LOAD_ATTR_PROPERTY,
&&TARGET_LOAD_ATTR_SLOT,
- &&TARGET_LOAD_ASSERTION_ERROR,
- &&TARGET_RETURN_GENERATOR,
&&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LOAD_ATTR_METHOD_LAZY_DICT,
&&TARGET_LOAD_ATTR_METHOD_NO_DICT,
+ &&TARGET_RETURN_VALUE,
&&TARGET_LOAD_ATTR_METHOD_WITH_VALUES,
+ &&TARGET_SETUP_ANNOTATIONS,
&&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_LOAD_FAST__LOAD_CONST,
&&TARGET_LOAD_FAST__LOAD_FAST,
- &&TARGET_RETURN_VALUE,
- &&TARGET_LOAD_GLOBAL_BUILTIN,
- &&TARGET_SETUP_ANNOTATIONS,
- &&TARGET_LOAD_GLOBAL_MODULE,
- &&TARGET_STORE_ATTR_INSTANCE_VALUE,
- &&TARGET_STORE_ATTR_SLOT,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
&&TARGET_DELETE_NAME,
@@ -112,7 +112,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_FORWARD,
&&TARGET_JUMP_IF_FALSE_OR_POP,
&&TARGET_JUMP_IF_TRUE_OR_POP,
- &&TARGET_STORE_ATTR_WITH_HINT,
+ &&TARGET_LOAD_GLOBAL_BUILTIN,
&&TARGET_POP_JUMP_IF_FALSE,
&&TARGET_POP_JUMP_IF_TRUE,
&&TARGET_LOAD_GLOBAL,
@@ -142,7 +142,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_BACKWARD,
&&TARGET_COMPARE_AND_BRANCH,
&&TARGET_CALL_FUNCTION_EX,
- &&TARGET_STORE_FAST__LOAD_FAST,
+ &&TARGET_LOAD_GLOBAL_MODULE,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
&&TARGET_SET_ADD,
@@ -152,24 +152,24 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE,
&&TARGET_RESUME,
&&TARGET_MATCH_CLASS,
- &&TARGET_STORE_FAST__STORE_FAST,
- &&TARGET_STORE_SUBSCR_DICT,
+ &&TARGET_STORE_ATTR_INSTANCE_VALUE,
+ &&TARGET_STORE_ATTR_SLOT,
&&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING,
- &&TARGET_STORE_SUBSCR_LIST_INT,
- &&TARGET_UNPACK_SEQUENCE_LIST,
- &&TARGET_UNPACK_SEQUENCE_TUPLE,
- &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
+ &&TARGET_STORE_ATTR_WITH_HINT,
+ &&TARGET_STORE_FAST__LOAD_FAST,
+ &&TARGET_STORE_FAST__STORE_FAST,
+ &&TARGET_STORE_SUBSCR_DICT,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
+ &&TARGET_STORE_SUBSCR_LIST_INT,
+ &&TARGET_UNPACK_SEQUENCE_LIST,
+ &&TARGET_UNPACK_SEQUENCE_TUPLE,
+ &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
&&TARGET_SEND_GEN,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_CALL_INTRINSIC_1,
diff --git a/Python/tier2.c b/Python/tier2.c
index c8022ac6048107..44f6cb858698d6 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -369,9 +369,9 @@ emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard, int bb_id)
{
*write_curr = guard;
write_curr++;
- _py_set_opcode(write_curr, EXTENDED_ARG);
- write_curr->oparg = 0;
- write_curr++;
+ //_py_set_opcode(write_curr, EXTENDED_ARG);
+ //write_curr->oparg = 0;
+ //write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
write_curr->oparg = 0;
write_curr++;
@@ -426,19 +426,21 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
#endif
Py_UNREACHABLE();
}
+ assert(oparg <= 0XFF);
// Backwards jumps should be handled specially.
if (opcode == BB_JUMP_BACKWARD_LAZY) {
#if BB_DEBUG
fprintf(stderr, "emitted backwards jump %p %d\n", write_curr,
_Py_OPCODE(branch));
#endif
+ // Just in case
_py_set_opcode(write_curr, EXTENDED_ARG);
- write_curr->oparg = (oparg >> 8) & 0xFF;
+ write_curr->oparg = 0;
write_curr++;
// We don't need to recalculate the backward jump, because that only needs to be done
// when it locates the next BB in JUMP_BACKWARD_LAZY.
_py_set_opcode(write_curr, BB_JUMP_BACKWARD_LAZY);
- write_curr->oparg = oparg & 0xFF;
+ write_curr->oparg = oparg;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -449,24 +451,19 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
// FOR_ITER is also a special jump
else if (opcode == BB_TEST_ITER) {
#if BB_DEBUG
- fprintf(stderr, "emitted logical branch %p %d\n", write_curr,
+ fprintf(stderr, "emitted iter branch %p %d\n", write_curr,
_Py_OPCODE(branch));
#endif
// The oparg of FOR_ITER is a little special, the actual jump has to jump over
// its own cache entries, the oparg, -1 to tell it to start generating from the
// END_FOR. However, at runtime, we will skip this END_FOR.
oparg = INLINE_CACHE_ENTRIES_FOR_ITER + oparg - 1;
- //_Py_CODEUNIT *start = write_curr;
_py_set_opcode(write_curr, BB_TEST_ITER);
write_curr->oparg = oparg;
write_curr++;
- // We don't need to emit inline cache entries, because when this converts,
- // we can just make use of the EXTENDED_ARG and BB_BRANCH below.
- _py_set_opcode(write_curr, EXTENDED_ARG);
- write_curr->oparg = (oparg >> 8) & 0xFF;
- write_curr++;
+ write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_FOR_ITER);
_py_set_opcode(write_curr, BB_BRANCH);
- write_curr->oparg = oparg & 0xFF;
+ write_curr->oparg = oparg;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -483,12 +480,6 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
_py_set_opcode(write_curr, opcode);
write_curr->oparg = oparg;
write_curr++;
- // We prefix with an empty EXTENDED_ARG, just in case the future jumps
- // are not large enough to handle the bytecode format when jumping to
- // the 2nd bb.
- _py_set_opcode(write_curr, EXTENDED_ARG);
- write_curr->oparg = (oparg >> 8) & 0xFF;
- write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
write_curr->oparg = oparg & 0xFF;
write_curr++;
@@ -1206,3 +1197,102 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int j
_PyTier2BBMetadata *target_metadata = t2_info->bb_data[matching_bb_id];
return target_metadata->tier2_start;
}
+
+/*
+At generation of the second outgoing edge (basic block), the instructions look like this:
+
+BB_TEST_POP_IF_TRUE
+BB_BRANCH_IF_FLAG_SET
+CACHE
+
+Since both edges are now generated, we want to rewrite it to:
+
+BB_TEST_POP_IF_TRUE
+BB_JUMP_IF_FLAG_SET
+CACHE (will be converted to EXTENDED_ARGS if we need a bigger jump)
+
+Some instructions will be special since they need CACHE entries. E.g. FOR_ITER
+
+BB_TEST_ITER
+CACHE
+BB_BRANCH_IF_FLAG_SET
+CACHE
+
+Backwards jumps are handled by another function.
+*/
+
+void
+_PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target)
+{
+ _Py_CODEUNIT *write_curr = bb_branch;
+ // -1 because the PC is auto incremented
+ int oparg = (int)(target - bb_branch - 1);
+ int branch = _Py_OPCODE(*bb_branch);
+ assert(branch == BB_BRANCH_IF_FLAG_SET || branch == BB_BRANCH_IF_FLAG_UNSET);
+ bool requires_extended = oparg > 0xFF;
+ assert(oparg <= 0xFFFF);
+ if (requires_extended) {
+ _py_set_opcode(write_curr, EXTENDED_ARG);
+ write_curr->oparg = (oparg >> 8) & 0xFF;
+ write_curr++;
+ // -1 to oparg because now the jump instruction moves one unit forward.
+ oparg--;
+ }
+ _py_set_opcode(write_curr,
+ branch == BB_BRANCH_IF_FLAG_SET ? BB_JUMP_IF_FLAG_SET : BB_JUMP_IF_FLAG_UNSET);
+ write_curr->oparg = oparg & 0xFF;
+ write_curr++;
+ if (!requires_extended) {
+ _py_set_opcode(write_curr, NOP);
+ write_curr++;
+ }
+
+}
+
+
+/*
+Before:
+
+EXTENDED_ARG/NOP
+JUMP_BACKWARD_LAZY
+CACHE
+
+
+After:
+
+EXTENDED_ARG (if needed, else NOP)
+JUMP_BACKWARD_LAZY
+END_FOR
+*/
+
+void
+_PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *target)
+{
+ _Py_CODEUNIT *write_curr = jump_backward_lazy - 1;
+ _Py_CODEUNIT *prev = jump_backward_lazy - 1;
+ assert(_Py_OPCODE(*jump_backward_lazy) == BB_JUMP_BACKWARD_LAZY);
+ assert(_Py_OPCODE(*prev) == EXTENDED_ARG);
+
+ // +1 because we increment the PC before JUMPBY
+ int oparg = (int)(target - (jump_backward_lazy + 1));
+ assert(oparg < 0);
+ oparg = -oparg;
+ assert(oparg > 0);
+ assert(oparg <= 0xFFFF);
+
+ bool requires_extended = oparg > 0xFF;
+ if (requires_extended) {
+ _py_set_opcode(write_curr, EXTENDED_ARG);
+ write_curr->oparg = (oparg >> 8) & 0xFF;
+ write_curr++;
+ }
+ else {
+ _py_set_opcode(write_curr, NOP);
+ write_curr++;
+ }
+ _py_set_opcode(write_curr, JUMP_BACKWARD);
+ write_curr->oparg = oparg & 0xFF;
+ write_curr++;
+ _py_set_opcode(write_curr, END_FOR);
+ write_curr++;
+}
From 78810991b37e3a9d3ad856fb63f85e41ba4784e9 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 24 Feb 2023 15:04:18 +0800
Subject: [PATCH 035/280] Fix definition of BINARY_CHECK_INT
---
Include/internal/pycore_opcode_macro_to_micro.h | 9 ++++-----
Python/bytecodes.c | 9 +++++----
Python/generated_cases.c.h | 14 ++++----------
3 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index 0d107771b428ea..b33b88b838099b 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -34,7 +34,7 @@ extern const int _Py_MacroOpUOpCount[] = {
[BINARY_OP_ADD_UNICODE] = 1,
[BINARY_OP_INPLACE_ADD_UNICODE] = 1,
[BINARY_OP_ADD_FLOAT] = 1,
-[BINARY_OP_ADD_INT] = 2,
+[BINARY_OP_ADD_INT] = 1,
[BINARY_CHECK_INT] = 1,
[BINARY_OP_ADD_INT_REST] = 1,
[BINARY_SUBSCR] = 1,
@@ -196,11 +196,10 @@ extern const int _Py_MacroOpUOpCount[] = {
[BB_JUMP_BACKWARD_LAZY] = 1,
};
-extern const int _Py_MacroOpToUOp[][2] = {
-[BINARY_OP_ADD_INT] = {BINARY_CHECK_INT, BINARY_OP_ADD_INT_REST},
+extern const int _Py_MacroOpToUOp[][1] = {
+[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_REST},
};
-extern const PyTypeObject *_Py_UOpGuardTypes[][2] = {
-[BINARY_CHECK_INT] = {&PyLong_Type, &PyLong_Type},
+extern const PyTypeObject *_Py_UOpGuardTypes[][0] = {
};
#ifdef __cplusplus
}
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index caffa4ad6cba86..316171a08f45f7 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -292,14 +292,15 @@ dummy_func(
}
macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
- U_INST(BINARY_CHECK_INT);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
U_INST(BINARY_OP_ADD_INT_REST);
}
- u_inst(BINARY_CHECK_INT, (left, right -- left : PyLong_Type, right: PyLong_Type)) {
+ inst(BINARY_CHECK_INT, (left, right -- left : PyLong_Type, right : PyLong_Type)) {
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ bb_test = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
}
u_inst(BINARY_OP_ADD_INT_REST, (left, right -- sum)) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 5b6e1bd9c1e1cb..9ff9a718eef8ca 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2,13 +2,6 @@
// from Python\bytecodes.c
// Do not edit!
- #define UOP_BINARY_CHECK_INT() \
- do { \
- assert(cframe.use_tracing == 0);\
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);\
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);\
- } while (0)
-
#define UOP_BINARY_OP_ADD_INT_REST() \
do { \
STAT_INC(BINARY_OP, hit);\
@@ -414,7 +407,9 @@
PyObject *right = PEEK(1);
PyObject *left = PEEK(2);
PyObject *sum;
- UOP_BINARY_CHECK_INT();
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
UOP_BINARY_OP_ADD_INT_REST();
STACK_SHRINK(1);
POKE(1, sum);
@@ -426,8 +421,7 @@
PyObject *right = PEEK(1);
PyObject *left = PEEK(2);
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ bb_test = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
DISPATCH();
}
From 280e961838297246e92231180c8f0c59576d895b Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 24 Feb 2023 15:04:18 +0800
Subject: [PATCH 036/280] Fix definition of BINARY_CHECK_INT
---
Include/internal/pycore_opcode_macro_to_micro.h | 9 ++++-----
Python/bytecodes.c | 9 +++++----
Python/generated_cases.c.h | 14 ++++----------
3 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index 0d107771b428ea..b33b88b838099b 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -34,7 +34,7 @@ extern const int _Py_MacroOpUOpCount[] = {
[BINARY_OP_ADD_UNICODE] = 1,
[BINARY_OP_INPLACE_ADD_UNICODE] = 1,
[BINARY_OP_ADD_FLOAT] = 1,
-[BINARY_OP_ADD_INT] = 2,
+[BINARY_OP_ADD_INT] = 1,
[BINARY_CHECK_INT] = 1,
[BINARY_OP_ADD_INT_REST] = 1,
[BINARY_SUBSCR] = 1,
@@ -196,11 +196,10 @@ extern const int _Py_MacroOpUOpCount[] = {
[BB_JUMP_BACKWARD_LAZY] = 1,
};
-extern const int _Py_MacroOpToUOp[][2] = {
-[BINARY_OP_ADD_INT] = {BINARY_CHECK_INT, BINARY_OP_ADD_INT_REST},
+extern const int _Py_MacroOpToUOp[][1] = {
+[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_REST},
};
-extern const PyTypeObject *_Py_UOpGuardTypes[][2] = {
-[BINARY_CHECK_INT] = {&PyLong_Type, &PyLong_Type},
+extern const PyTypeObject *_Py_UOpGuardTypes[][0] = {
};
#ifdef __cplusplus
}
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index caffa4ad6cba86..316171a08f45f7 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -292,14 +292,15 @@ dummy_func(
}
macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
- U_INST(BINARY_CHECK_INT);
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
U_INST(BINARY_OP_ADD_INT_REST);
}
- u_inst(BINARY_CHECK_INT, (left, right -- left : PyLong_Type, right: PyLong_Type)) {
+ inst(BINARY_CHECK_INT, (left, right -- left : PyLong_Type, right : PyLong_Type)) {
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ bb_test = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
}
u_inst(BINARY_OP_ADD_INT_REST, (left, right -- sum)) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 5b6e1bd9c1e1cb..9ff9a718eef8ca 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2,13 +2,6 @@
// from Python\bytecodes.c
// Do not edit!
- #define UOP_BINARY_CHECK_INT() \
- do { \
- assert(cframe.use_tracing == 0);\
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);\
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);\
- } while (0)
-
#define UOP_BINARY_OP_ADD_INT_REST() \
do { \
STAT_INC(BINARY_OP, hit);\
@@ -414,7 +407,9 @@
PyObject *right = PEEK(1);
PyObject *left = PEEK(2);
PyObject *sum;
- UOP_BINARY_CHECK_INT();
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
+ DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
UOP_BINARY_OP_ADD_INT_REST();
STACK_SHRINK(1);
POKE(1, sum);
@@ -426,8 +421,7 @@
PyObject *right = PEEK(1);
PyObject *left = PEEK(2);
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
- DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ bb_test = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
DISPATCH();
}
From 89ca5de784dcaea76fb813042d005ae724f3dd8a Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 24 Feb 2023 23:35:34 +0800
Subject: [PATCH 037/280] Add JUMP_BACKWARD_QUICK
---
Include/internal/pycore_opcode.h | 42 +--
.../internal/pycore_opcode_macro_to_micro.h | 207 ------------
Include/opcode.h | 296 +++++++++---------
Lib/opcode.py | 3 +
PCbuild/pythoncore.vcxproj | 5 +-
PCbuild/pythoncore.vcxproj.filters | 3 -
Python/bytecodes.c | 5 +
Python/generated_cases.c.h | 6 +
Python/opcode_metadata.h | 7 +-
Python/opcode_targets.h | 40 +--
Python/specialize.c | 1 -
Python/tier2.c | 126 ++++----
Tools/cases_generator/generate_cases.py | 4 +-
13 files changed, 271 insertions(+), 474 deletions(-)
delete mode 100644 Include/internal/pycore_opcode_macro_to_micro.h
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 16e0f7f2f65033..4c46a2cb3eb5a8 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -146,6 +146,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
[IS_OP] = IS_OP,
[JUMP_BACKWARD] = JUMP_BACKWARD,
[JUMP_BACKWARD_NO_INTERRUPT] = JUMP_BACKWARD_NO_INTERRUPT,
+ [JUMP_BACKWARD_QUICK] = JUMP_BACKWARD,
[JUMP_FORWARD] = JUMP_FORWARD,
[JUMP_IF_FALSE_OR_POP] = JUMP_IF_FALSE_OR_POP,
[JUMP_IF_TRUE_OR_POP] = JUMP_IF_TRUE_OR_POP,
@@ -242,16 +243,17 @@ static const char *const _PyOpcode_OpName[263] = {
[INTERPRETER_EXIT] = "INTERPRETER_EXIT",
[END_FOR] = "END_FOR",
[RESUME_QUICK] = "RESUME_QUICK",
+ [JUMP_BACKWARD_QUICK] = "JUMP_BACKWARD_QUICK",
[BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
[BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT",
- [BINARY_OP_ADD_UNICODE] = "BINARY_OP_ADD_UNICODE",
[NOP] = "NOP",
- [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE",
+ [BINARY_OP_ADD_UNICODE] = "BINARY_OP_ADD_UNICODE",
[UNARY_NEGATIVE] = "UNARY_NEGATIVE",
[UNARY_NOT] = "UNARY_NOT",
+ [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE",
[BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT",
- [BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT",
[UNARY_INVERT] = "UNARY_INVERT",
+ [BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT",
[BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT",
[BINARY_OP_SUBTRACT_INT] = "BINARY_OP_SUBTRACT_INT",
[BINARY_SUBSCR_DICT] = "BINARY_SUBSCR_DICT",
@@ -260,20 +262,20 @@ static const char *const _PyOpcode_OpName[263] = {
[BINARY_SUBSCR_TUPLE_INT] = "BINARY_SUBSCR_TUPLE_INT",
[CALL_PY_EXACT_ARGS] = "CALL_PY_EXACT_ARGS",
[CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS",
- [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS",
[BINARY_SUBSCR] = "BINARY_SUBSCR",
[BINARY_SLICE] = "BINARY_SLICE",
[STORE_SLICE] = "STORE_SLICE",
+ [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS",
[CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS",
- [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS",
[GET_LEN] = "GET_LEN",
[MATCH_MAPPING] = "MATCH_MAPPING",
[MATCH_SEQUENCE] = "MATCH_SEQUENCE",
[MATCH_KEYS] = "MATCH_KEYS",
- [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
+ [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS",
[PUSH_EXC_INFO] = "PUSH_EXC_INFO",
[CHECK_EXC_MATCH] = "CHECK_EXC_MATCH",
[CHECK_EG_MATCH] = "CHECK_EG_MATCH",
+ [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
[CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST",
[CALL_NO_KW_BUILTIN_O] = "CALL_NO_KW_BUILTIN_O",
[CALL_NO_KW_ISINSTANCE] = "CALL_NO_KW_ISINSTANCE",
@@ -284,7 +286,6 @@ static const char *const _PyOpcode_OpName[263] = {
[CALL_NO_KW_METHOD_DESCRIPTOR_O] = "CALL_NO_KW_METHOD_DESCRIPTOR_O",
[CALL_NO_KW_STR_1] = "CALL_NO_KW_STR_1",
[CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1",
- [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1",
[WITH_EXCEPT_START] = "WITH_EXCEPT_START",
[GET_AITER] = "GET_AITER",
[GET_ANEXT] = "GET_ANEXT",
@@ -292,39 +293,39 @@ static const char *const _PyOpcode_OpName[263] = {
[BEFORE_WITH] = "BEFORE_WITH",
[END_ASYNC_FOR] = "END_ASYNC_FOR",
[CLEANUP_THROW] = "CLEANUP_THROW",
+ [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1",
[COMPARE_AND_BRANCH_FLOAT] = "COMPARE_AND_BRANCH_FLOAT",
[COMPARE_AND_BRANCH_INT] = "COMPARE_AND_BRANCH_INT",
[COMPARE_AND_BRANCH_STR] = "COMPARE_AND_BRANCH_STR",
- [FOR_ITER_LIST] = "FOR_ITER_LIST",
[STORE_SUBSCR] = "STORE_SUBSCR",
[DELETE_SUBSCR] = "DELETE_SUBSCR",
+ [FOR_ITER_LIST] = "FOR_ITER_LIST",
[FOR_ITER_TUPLE] = "FOR_ITER_TUPLE",
[FOR_ITER_RANGE] = "FOR_ITER_RANGE",
[FOR_ITER_GEN] = "FOR_ITER_GEN",
[BB_TEST_ITER_LIST] = "BB_TEST_ITER_LIST",
[BB_TEST_ITER_TUPLE] = "BB_TEST_ITER_TUPLE",
- [BB_TEST_ITER_RANGE] = "BB_TEST_ITER_RANGE",
[GET_ITER] = "GET_ITER",
[GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER",
- [BB_TEST_ITER_GEN] = "BB_TEST_ITER_GEN",
+ [BB_TEST_ITER_RANGE] = "BB_TEST_ITER_RANGE",
[LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
+ [BB_TEST_ITER_GEN] = "BB_TEST_ITER_GEN",
[LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS",
- [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
[LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
[RETURN_GENERATOR] = "RETURN_GENERATOR",
+ [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
[LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
[LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
[LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
[LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
[LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
[LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT",
- [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
[RETURN_VALUE] = "RETURN_VALUE",
- [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
+ [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
[SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
+ [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
[LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
[LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
- [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
[POP_EXCEPT] = "POP_EXCEPT",
[STORE_NAME] = "STORE_NAME",
[DELETE_NAME] = "DELETE_NAME",
@@ -349,7 +350,7 @@ static const char *const _PyOpcode_OpName[263] = {
[JUMP_FORWARD] = "JUMP_FORWARD",
[JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP",
[JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP",
- [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
+ [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
[POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
[POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
[LOAD_GLOBAL] = "LOAD_GLOBAL",
@@ -379,7 +380,7 @@ static const char *const _PyOpcode_OpName[263] = {
[JUMP_BACKWARD] = "JUMP_BACKWARD",
[COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH",
[CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
- [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
+ [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
[EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD",
@@ -389,28 +390,29 @@ static const char *const _PyOpcode_OpName[263] = {
[YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS",
+ [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
[STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
- [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING",
+ [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
- [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
+ [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
[STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
- [SEND_GEN] = "SEND_GEN",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
[CALL_INTRINSIC_1] = "CALL_INTRINSIC_1",
[CALL_INTRINSIC_2] = "CALL_INTRINSIC_2",
+ [SEND_GEN] = "SEND_GEN",
[BB_BRANCH] = "BB_BRANCH",
[BB_BRANCH_IF_FLAG_UNSET] = "BB_BRANCH_IF_FLAG_UNSET",
[BB_BRANCH_IF_FLAG_SET] = "BB_BRANCH_IF_FLAG_SET",
@@ -426,7 +428,6 @@ static const char *const _PyOpcode_OpName[263] = {
[BB_JUMP_BACKWARD_LAZY] = "BB_JUMP_BACKWARD_LAZY",
[BINARY_CHECK_INT] = "BINARY_CHECK_INT",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
- [190] = "<190>",
[191] = "<191>",
[192] = "<192>",
[193] = "<193>",
@@ -504,7 +505,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 190: \
case 191: \
case 192: \
case 193: \
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
deleted file mode 100644
index b33b88b838099b..00000000000000
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ /dev/null
@@ -1,207 +0,0 @@
-// This file is generated by Tools\cases_generator\generate_cases.py --macromap
-// from Python\bytecodes.c
-// Do not edit!
-
-#ifndef Py_INTERNAL_OPCODE_MACRO_TO_MICRO_H
-#define Py_INTERNAL_OPCODE_MACRO_TO_MICRO_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef Py_BUILD_CORE
-# error "this header requires Py_BUILD_CORE define"
-#endif
-#include "opcode.h"
-
-extern const int _Py_MacroOpUOpCount[] = {
-[NOP] = 1,
-[RESUME] = 1,
-[RESUME_QUICK] = 1,
-[LOAD_CLOSURE] = 1,
-[LOAD_FAST_CHECK] = 1,
-[LOAD_FAST] = 1,
-[LOAD_CONST] = 1,
-[STORE_FAST] = 1,
-[POP_TOP] = 1,
-[PUSH_NULL] = 1,
-[UNARY_NEGATIVE] = 1,
-[UNARY_NOT] = 1,
-[UNARY_INVERT] = 1,
-[BINARY_OP_MULTIPLY_INT] = 1,
-[BINARY_OP_MULTIPLY_FLOAT] = 1,
-[BINARY_OP_SUBTRACT_INT] = 1,
-[BINARY_OP_SUBTRACT_FLOAT] = 1,
-[BINARY_OP_ADD_UNICODE] = 1,
-[BINARY_OP_INPLACE_ADD_UNICODE] = 1,
-[BINARY_OP_ADD_FLOAT] = 1,
-[BINARY_OP_ADD_INT] = 1,
-[BINARY_CHECK_INT] = 1,
-[BINARY_OP_ADD_INT_REST] = 1,
-[BINARY_SUBSCR] = 1,
-[BINARY_SLICE] = 1,
-[STORE_SLICE] = 1,
-[BINARY_SUBSCR_LIST_INT] = 1,
-[BINARY_SUBSCR_TUPLE_INT] = 1,
-[BINARY_SUBSCR_DICT] = 1,
-[BINARY_SUBSCR_GETITEM] = 1,
-[LIST_APPEND] = 1,
-[SET_ADD] = 1,
-[STORE_SUBSCR] = 1,
-[STORE_SUBSCR_LIST_INT] = 1,
-[STORE_SUBSCR_DICT] = 1,
-[DELETE_SUBSCR] = 1,
-[CALL_INTRINSIC_1] = 1,
-[CALL_INTRINSIC_2] = 1,
-[RAISE_VARARGS] = 1,
-[INTERPRETER_EXIT] = 1,
-[RETURN_VALUE] = 1,
-[RETURN_CONST] = 1,
-[GET_AITER] = 1,
-[GET_ANEXT] = 1,
-[GET_AWAITABLE] = 1,
-[SEND] = 1,
-[SEND_GEN] = 1,
-[YIELD_VALUE] = 1,
-[POP_EXCEPT] = 1,
-[RERAISE] = 1,
-[END_ASYNC_FOR] = 1,
-[CLEANUP_THROW] = 1,
-[LOAD_ASSERTION_ERROR] = 1,
-[LOAD_BUILD_CLASS] = 1,
-[STORE_NAME] = 1,
-[DELETE_NAME] = 1,
-[UNPACK_SEQUENCE] = 1,
-[UNPACK_SEQUENCE_TWO_TUPLE] = 1,
-[UNPACK_SEQUENCE_TUPLE] = 1,
-[UNPACK_SEQUENCE_LIST] = 1,
-[UNPACK_EX] = 1,
-[STORE_ATTR] = 1,
-[DELETE_ATTR] = 1,
-[STORE_GLOBAL] = 1,
-[DELETE_GLOBAL] = 1,
-[LOAD_NAME] = 1,
-[LOAD_GLOBAL] = 1,
-[LOAD_GLOBAL_MODULE] = 1,
-[LOAD_GLOBAL_BUILTIN] = 1,
-[DELETE_FAST] = 1,
-[MAKE_CELL] = 1,
-[DELETE_DEREF] = 1,
-[LOAD_CLASSDEREF] = 1,
-[LOAD_DEREF] = 1,
-[STORE_DEREF] = 1,
-[COPY_FREE_VARS] = 1,
-[BUILD_STRING] = 1,
-[BUILD_TUPLE] = 1,
-[BUILD_LIST] = 1,
-[LIST_EXTEND] = 1,
-[SET_UPDATE] = 1,
-[BUILD_SET] = 1,
-[BUILD_MAP] = 1,
-[SETUP_ANNOTATIONS] = 1,
-[BUILD_CONST_KEY_MAP] = 1,
-[DICT_UPDATE] = 1,
-[DICT_MERGE] = 1,
-[MAP_ADD] = 1,
-[LOAD_ATTR] = 1,
-[LOAD_ATTR_INSTANCE_VALUE] = 1,
-[LOAD_ATTR_MODULE] = 1,
-[LOAD_ATTR_WITH_HINT] = 1,
-[LOAD_ATTR_SLOT] = 1,
-[LOAD_ATTR_CLASS] = 1,
-[LOAD_ATTR_PROPERTY] = 1,
-[LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = 1,
-[STORE_ATTR_INSTANCE_VALUE] = 1,
-[STORE_ATTR_WITH_HINT] = 1,
-[STORE_ATTR_SLOT] = 1,
-[COMPARE_OP] = 1,
-[COMPARE_AND_BRANCH] = 1,
-[COMPARE_AND_BRANCH_FLOAT] = 1,
-[COMPARE_AND_BRANCH_INT] = 1,
-[COMPARE_AND_BRANCH_STR] = 1,
-[IS_OP] = 1,
-[CONTAINS_OP] = 1,
-[CHECK_EG_MATCH] = 1,
-[CHECK_EXC_MATCH] = 1,
-[IMPORT_NAME] = 1,
-[IMPORT_FROM] = 1,
-[JUMP_FORWARD] = 1,
-[JUMP_BACKWARD] = 1,
-[POP_JUMP_IF_FALSE] = 1,
-[BB_TEST_POP_IF_FALSE] = 1,
-[POP_JUMP_IF_TRUE] = 1,
-[BB_TEST_POP_IF_TRUE] = 1,
-[POP_JUMP_IF_NOT_NONE] = 1,
-[BB_TEST_POP_IF_NOT_NONE] = 1,
-[POP_JUMP_IF_NONE] = 1,
-[BB_TEST_POP_IF_NONE] = 1,
-[JUMP_IF_FALSE_OR_POP] = 1,
-[BB_TEST_IF_FALSE_OR_POP] = 1,
-[JUMP_IF_TRUE_OR_POP] = 1,
-[BB_TEST_IF_TRUE_OR_POP] = 1,
-[JUMP_BACKWARD_NO_INTERRUPT] = 1,
-[GET_LEN] = 1,
-[MATCH_CLASS] = 1,
-[MATCH_MAPPING] = 1,
-[MATCH_SEQUENCE] = 1,
-[MATCH_KEYS] = 1,
-[GET_ITER] = 1,
-[GET_YIELD_FROM_ITER] = 1,
-[FOR_ITER] = 1,
-[BB_TEST_ITER] = 1,
-[FOR_ITER_LIST] = 1,
-[FOR_ITER_TUPLE] = 1,
-[FOR_ITER_RANGE] = 1,
-[FOR_ITER_GEN] = 1,
-[BEFORE_ASYNC_WITH] = 1,
-[BEFORE_WITH] = 1,
-[WITH_EXCEPT_START] = 1,
-[PUSH_EXC_INFO] = 1,
-[LOAD_ATTR_METHOD_WITH_VALUES] = 1,
-[LOAD_ATTR_METHOD_NO_DICT] = 1,
-[LOAD_ATTR_METHOD_LAZY_DICT] = 1,
-[KW_NAMES] = 1,
-[CALL] = 1,
-[CALL_BOUND_METHOD_EXACT_ARGS] = 1,
-[CALL_PY_EXACT_ARGS] = 1,
-[CALL_PY_WITH_DEFAULTS] = 1,
-[CALL_NO_KW_TYPE_1] = 1,
-[CALL_NO_KW_STR_1] = 1,
-[CALL_NO_KW_TUPLE_1] = 1,
-[CALL_BUILTIN_CLASS] = 1,
-[CALL_NO_KW_BUILTIN_O] = 1,
-[CALL_NO_KW_BUILTIN_FAST] = 1,
-[CALL_BUILTIN_FAST_WITH_KEYWORDS] = 1,
-[CALL_NO_KW_LEN] = 1,
-[CALL_NO_KW_ISINSTANCE] = 1,
-[CALL_NO_KW_LIST_APPEND] = 1,
-[CALL_NO_KW_METHOD_DESCRIPTOR_O] = 1,
-[CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = 1,
-[CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = 1,
-[CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = 1,
-[CALL_FUNCTION_EX] = 1,
-[MAKE_FUNCTION] = 1,
-[RETURN_GENERATOR] = 1,
-[BUILD_SLICE] = 1,
-[FORMAT_VALUE] = 1,
-[COPY] = 1,
-[BINARY_OP] = 1,
-[SWAP] = 1,
-[EXTENDED_ARG] = 1,
-[CACHE] = 1,
-[BB_BRANCH] = 1,
-[BB_BRANCH_IF_FLAG_UNSET] = 1,
-[BB_JUMP_IF_FLAG_UNSET] = 1,
-[BB_BRANCH_IF_FLAG_SET] = 1,
-[BB_JUMP_IF_FLAG_SET] = 1,
-[BB_JUMP_BACKWARD_LAZY] = 1,
-};
-
-extern const int _Py_MacroOpToUOp[][1] = {
-[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_REST},
-};
-extern const PyTypeObject *_Py_UOpGuardTypes[][0] = {
-};
-#ifdef __cplusplus
-}
-#endif
-#endif // Py_INTERNAL_OPCODE_MACRO_TO_MICRO
diff --git a/Include/opcode.h b/Include/opcode.h
index ec2989e8abaed5..7f0b02ab7687e2 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -127,157 +127,159 @@ extern "C" {
#define LOAD_METHOD 262
#define MAX_PSEUDO_OPCODE 262
#define RESUME_QUICK 5
-#define BINARY_OP_ADD_FLOAT 6
-#define BINARY_OP_ADD_INT 7
-#define BINARY_OP_ADD_UNICODE 8
-#define BINARY_OP_INPLACE_ADD_UNICODE 10
-#define BINARY_OP_MULTIPLY_FLOAT 13
-#define BINARY_OP_MULTIPLY_INT 14
-#define BINARY_OP_SUBTRACT_FLOAT 16
-#define BINARY_OP_SUBTRACT_INT 17
-#define BINARY_SUBSCR_DICT 18
-#define BINARY_SUBSCR_GETITEM 19
-#define BINARY_SUBSCR_LIST_INT 20
-#define BINARY_SUBSCR_TUPLE_INT 21
-#define CALL_PY_EXACT_ARGS 22
-#define CALL_PY_WITH_DEFAULTS 23
-#define CALL_BOUND_METHOD_EXACT_ARGS 24
-#define CALL_BUILTIN_CLASS 28
-#define CALL_BUILTIN_FAST_WITH_KEYWORDS 29
-#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 34
-#define CALL_NO_KW_BUILTIN_FAST 38
-#define CALL_NO_KW_BUILTIN_O 39
-#define CALL_NO_KW_ISINSTANCE 40
-#define CALL_NO_KW_LEN 41
-#define CALL_NO_KW_LIST_APPEND 42
-#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 43
-#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 44
-#define CALL_NO_KW_METHOD_DESCRIPTOR_O 45
-#define CALL_NO_KW_STR_1 46
-#define CALL_NO_KW_TUPLE_1 47
-#define CALL_NO_KW_TYPE_1 48
-#define COMPARE_AND_BRANCH_FLOAT 56
-#define COMPARE_AND_BRANCH_INT 57
-#define COMPARE_AND_BRANCH_STR 58
-#define FOR_ITER_LIST 59
-#define FOR_ITER_TUPLE 62
-#define FOR_ITER_RANGE 63
-#define FOR_ITER_GEN 64
-#define BB_TEST_ITER_LIST 65
-#define BB_TEST_ITER_TUPLE 66
-#define BB_TEST_ITER_RANGE 67
-#define BB_TEST_ITER_GEN 70
-#define LOAD_ATTR_CLASS 72
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 73
-#define LOAD_ATTR_INSTANCE_VALUE 76
-#define LOAD_ATTR_MODULE 77
-#define LOAD_ATTR_PROPERTY 78
-#define LOAD_ATTR_SLOT 79
-#define LOAD_ATTR_WITH_HINT 80
-#define LOAD_ATTR_METHOD_LAZY_DICT 81
-#define LOAD_ATTR_METHOD_NO_DICT 82
-#define LOAD_ATTR_METHOD_WITH_VALUES 84
-#define LOAD_CONST__LOAD_FAST 86
-#define LOAD_FAST__LOAD_CONST 87
-#define LOAD_FAST__LOAD_FAST 88
-#define LOAD_GLOBAL_BUILTIN 113
-#define LOAD_GLOBAL_MODULE 143
-#define STORE_ATTR_INSTANCE_VALUE 153
-#define STORE_ATTR_SLOT 154
-#define STORE_ATTR_WITH_HINT 158
-#define STORE_FAST__LOAD_FAST 159
-#define STORE_FAST__STORE_FAST 160
-#define STORE_SUBSCR_DICT 161
-#define STORE_SUBSCR_LIST_INT 166
-#define UNPACK_SEQUENCE_LIST 167
-#define UNPACK_SEQUENCE_TUPLE 168
-#define UNPACK_SEQUENCE_TWO_TUPLE 169
-#define SEND_GEN 170
+#define JUMP_BACKWARD_QUICK 6
+#define BINARY_OP_ADD_FLOAT 7
+#define BINARY_OP_ADD_INT 8
+#define BINARY_OP_ADD_UNICODE 10
+#define BINARY_OP_INPLACE_ADD_UNICODE 13
+#define BINARY_OP_MULTIPLY_FLOAT 14
+#define BINARY_OP_MULTIPLY_INT 16
+#define BINARY_OP_SUBTRACT_FLOAT 17
+#define BINARY_OP_SUBTRACT_INT 18
+#define BINARY_SUBSCR_DICT 19
+#define BINARY_SUBSCR_GETITEM 20
+#define BINARY_SUBSCR_LIST_INT 21
+#define BINARY_SUBSCR_TUPLE_INT 22
+#define CALL_PY_EXACT_ARGS 23
+#define CALL_PY_WITH_DEFAULTS 24
+#define CALL_BOUND_METHOD_EXACT_ARGS 28
+#define CALL_BUILTIN_CLASS 29
+#define CALL_BUILTIN_FAST_WITH_KEYWORDS 34
+#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 38
+#define CALL_NO_KW_BUILTIN_FAST 39
+#define CALL_NO_KW_BUILTIN_O 40
+#define CALL_NO_KW_ISINSTANCE 41
+#define CALL_NO_KW_LEN 42
+#define CALL_NO_KW_LIST_APPEND 43
+#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44
+#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45
+#define CALL_NO_KW_METHOD_DESCRIPTOR_O 46
+#define CALL_NO_KW_STR_1 47
+#define CALL_NO_KW_TUPLE_1 48
+#define CALL_NO_KW_TYPE_1 56
+#define COMPARE_AND_BRANCH_FLOAT 57
+#define COMPARE_AND_BRANCH_INT 58
+#define COMPARE_AND_BRANCH_STR 59
+#define FOR_ITER_LIST 62
+#define FOR_ITER_TUPLE 63
+#define FOR_ITER_RANGE 64
+#define FOR_ITER_GEN 65
+#define BB_TEST_ITER_LIST 66
+#define BB_TEST_ITER_TUPLE 67
+#define BB_TEST_ITER_RANGE 70
+#define BB_TEST_ITER_GEN 72
+#define LOAD_ATTR_CLASS 73
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 76
+#define LOAD_ATTR_INSTANCE_VALUE 77
+#define LOAD_ATTR_MODULE 78
+#define LOAD_ATTR_PROPERTY 79
+#define LOAD_ATTR_SLOT 80
+#define LOAD_ATTR_WITH_HINT 81
+#define LOAD_ATTR_METHOD_LAZY_DICT 82
+#define LOAD_ATTR_METHOD_NO_DICT 84
+#define LOAD_ATTR_METHOD_WITH_VALUES 86
+#define LOAD_CONST__LOAD_FAST 87
+#define LOAD_FAST__LOAD_CONST 88
+#define LOAD_FAST__LOAD_FAST 113
+#define LOAD_GLOBAL_BUILTIN 143
+#define LOAD_GLOBAL_MODULE 153
+#define STORE_ATTR_INSTANCE_VALUE 154
+#define STORE_ATTR_SLOT 158
+#define STORE_ATTR_WITH_HINT 159
+#define STORE_FAST__LOAD_FAST 160
+#define STORE_FAST__STORE_FAST 161
+#define STORE_SUBSCR_DICT 166
+#define STORE_SUBSCR_LIST_INT 167
+#define UNPACK_SEQUENCE_LIST 168
+#define UNPACK_SEQUENCE_TUPLE 169
+#define UNPACK_SEQUENCE_TWO_TUPLE 170
+#define SEND_GEN 175
#define DO_TRACING 255
// Tier 2 interpreter ops
#define RESUME_QUICK 5
-#define BINARY_OP_ADD_FLOAT 6
-#define BINARY_OP_ADD_INT 7
-#define BINARY_OP_ADD_UNICODE 8
-#define BINARY_OP_INPLACE_ADD_UNICODE 10
-#define BINARY_OP_MULTIPLY_FLOAT 13
-#define BINARY_OP_MULTIPLY_INT 14
-#define BINARY_OP_SUBTRACT_FLOAT 16
-#define BINARY_OP_SUBTRACT_INT 17
-#define BINARY_SUBSCR_DICT 18
-#define BINARY_SUBSCR_GETITEM 19
-#define BINARY_SUBSCR_LIST_INT 20
-#define BINARY_SUBSCR_TUPLE_INT 21
-#define CALL_PY_EXACT_ARGS 22
-#define CALL_PY_WITH_DEFAULTS 23
-#define CALL_BOUND_METHOD_EXACT_ARGS 24
-#define CALL_BUILTIN_CLASS 28
-#define CALL_BUILTIN_FAST_WITH_KEYWORDS 29
-#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 34
-#define CALL_NO_KW_BUILTIN_FAST 38
-#define CALL_NO_KW_BUILTIN_O 39
-#define CALL_NO_KW_ISINSTANCE 40
-#define CALL_NO_KW_LEN 41
-#define CALL_NO_KW_LIST_APPEND 42
-#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 43
-#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 44
-#define CALL_NO_KW_METHOD_DESCRIPTOR_O 45
-#define CALL_NO_KW_STR_1 46
-#define CALL_NO_KW_TUPLE_1 47
-#define CALL_NO_KW_TYPE_1 48
-#define COMPARE_AND_BRANCH_FLOAT 56
-#define COMPARE_AND_BRANCH_INT 57
-#define COMPARE_AND_BRANCH_STR 58
-#define FOR_ITER_LIST 59
-#define FOR_ITER_TUPLE 62
-#define FOR_ITER_RANGE 63
-#define FOR_ITER_GEN 64
-#define BB_TEST_ITER_LIST 65
-#define BB_TEST_ITER_TUPLE 66
-#define BB_TEST_ITER_RANGE 67
-#define BB_TEST_ITER_GEN 70
-#define LOAD_ATTR_CLASS 72
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 73
-#define LOAD_ATTR_INSTANCE_VALUE 76
-#define LOAD_ATTR_MODULE 77
-#define LOAD_ATTR_PROPERTY 78
-#define LOAD_ATTR_SLOT 79
-#define LOAD_ATTR_WITH_HINT 80
-#define LOAD_ATTR_METHOD_LAZY_DICT 81
-#define LOAD_ATTR_METHOD_NO_DICT 82
-#define LOAD_ATTR_METHOD_WITH_VALUES 84
-#define LOAD_CONST__LOAD_FAST 86
-#define LOAD_FAST__LOAD_CONST 87
-#define LOAD_FAST__LOAD_FAST 88
-#define LOAD_GLOBAL_BUILTIN 113
-#define LOAD_GLOBAL_MODULE 143
-#define STORE_ATTR_INSTANCE_VALUE 153
-#define STORE_ATTR_SLOT 154
-#define STORE_ATTR_WITH_HINT 158
-#define STORE_FAST__LOAD_FAST 159
-#define STORE_FAST__STORE_FAST 160
-#define STORE_SUBSCR_DICT 161
-#define STORE_SUBSCR_LIST_INT 166
-#define UNPACK_SEQUENCE_LIST 167
-#define UNPACK_SEQUENCE_TUPLE 168
-#define UNPACK_SEQUENCE_TWO_TUPLE 169
-#define SEND_GEN 170
+#define JUMP_BACKWARD_QUICK 6
+#define BINARY_OP_ADD_FLOAT 7
+#define BINARY_OP_ADD_INT 8
+#define BINARY_OP_ADD_UNICODE 10
+#define BINARY_OP_INPLACE_ADD_UNICODE 13
+#define BINARY_OP_MULTIPLY_FLOAT 14
+#define BINARY_OP_MULTIPLY_INT 16
+#define BINARY_OP_SUBTRACT_FLOAT 17
+#define BINARY_OP_SUBTRACT_INT 18
+#define BINARY_SUBSCR_DICT 19
+#define BINARY_SUBSCR_GETITEM 20
+#define BINARY_SUBSCR_LIST_INT 21
+#define BINARY_SUBSCR_TUPLE_INT 22
+#define CALL_PY_EXACT_ARGS 23
+#define CALL_PY_WITH_DEFAULTS 24
+#define CALL_BOUND_METHOD_EXACT_ARGS 28
+#define CALL_BUILTIN_CLASS 29
+#define CALL_BUILTIN_FAST_WITH_KEYWORDS 34
+#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 38
+#define CALL_NO_KW_BUILTIN_FAST 39
+#define CALL_NO_KW_BUILTIN_O 40
+#define CALL_NO_KW_ISINSTANCE 41
+#define CALL_NO_KW_LEN 42
+#define CALL_NO_KW_LIST_APPEND 43
+#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44
+#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45
+#define CALL_NO_KW_METHOD_DESCRIPTOR_O 46
+#define CALL_NO_KW_STR_1 47
+#define CALL_NO_KW_TUPLE_1 48
+#define CALL_NO_KW_TYPE_1 56
+#define COMPARE_AND_BRANCH_FLOAT 57
+#define COMPARE_AND_BRANCH_INT 58
+#define COMPARE_AND_BRANCH_STR 59
+#define FOR_ITER_LIST 62
+#define FOR_ITER_TUPLE 63
+#define FOR_ITER_RANGE 64
+#define FOR_ITER_GEN 65
+#define BB_TEST_ITER_LIST 66
+#define BB_TEST_ITER_TUPLE 67
+#define BB_TEST_ITER_RANGE 70
+#define BB_TEST_ITER_GEN 72
+#define LOAD_ATTR_CLASS 73
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 76
+#define LOAD_ATTR_INSTANCE_VALUE 77
+#define LOAD_ATTR_MODULE 78
+#define LOAD_ATTR_PROPERTY 79
+#define LOAD_ATTR_SLOT 80
+#define LOAD_ATTR_WITH_HINT 81
+#define LOAD_ATTR_METHOD_LAZY_DICT 82
+#define LOAD_ATTR_METHOD_NO_DICT 84
+#define LOAD_ATTR_METHOD_WITH_VALUES 86
+#define LOAD_CONST__LOAD_FAST 87
+#define LOAD_FAST__LOAD_CONST 88
+#define LOAD_FAST__LOAD_FAST 113
+#define LOAD_GLOBAL_BUILTIN 143
+#define LOAD_GLOBAL_MODULE 153
+#define STORE_ATTR_INSTANCE_VALUE 154
+#define STORE_ATTR_SLOT 158
+#define STORE_ATTR_WITH_HINT 159
+#define STORE_FAST__LOAD_FAST 160
+#define STORE_FAST__STORE_FAST 161
+#define STORE_SUBSCR_DICT 166
+#define STORE_SUBSCR_LIST_INT 167
+#define UNPACK_SEQUENCE_LIST 168
+#define UNPACK_SEQUENCE_TUPLE 169
+#define UNPACK_SEQUENCE_TWO_TUPLE 170
+#define SEND_GEN 175
#define DO_TRACING 255
-#define BB_BRANCH 175
-#define BB_BRANCH_IF_FLAG_UNSET 176
-#define BB_BRANCH_IF_FLAG_SET 177
-#define BB_JUMP_IF_FLAG_UNSET 178
-#define BB_JUMP_IF_FLAG_SET 179
-#define BB_TEST_ITER 180
-#define BB_TEST_IF_FALSE_OR_POP 181
-#define BB_TEST_IF_TRUE_OR_POP 182
-#define BB_TEST_POP_IF_FALSE 183
-#define BB_TEST_POP_IF_TRUE 184
-#define BB_TEST_POP_IF_NOT_NONE 185
-#define BB_TEST_POP_IF_NONE 186
-#define BB_JUMP_BACKWARD_LAZY 187
-#define BINARY_CHECK_INT 188
-#define BINARY_OP_ADD_INT_REST 189
+#define BB_BRANCH 176
+#define BB_BRANCH_IF_FLAG_UNSET 177
+#define BB_BRANCH_IF_FLAG_SET 178
+#define BB_JUMP_IF_FLAG_UNSET 179
+#define BB_JUMP_IF_FLAG_SET 180
+#define BB_TEST_ITER 181
+#define BB_TEST_IF_FALSE_OR_POP 182
+#define BB_TEST_IF_TRUE_OR_POP 183
+#define BB_TEST_POP_IF_FALSE 184
+#define BB_TEST_POP_IF_TRUE 185
+#define BB_TEST_POP_IF_NOT_NONE 186
+#define BB_TEST_POP_IF_NONE 187
+#define BB_JUMP_BACKWARD_LAZY 188
+#define BINARY_CHECK_INT 189
+#define BINARY_OP_ADD_INT_REST 190
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index fb4d55637fdfa1..02e488179f2ff6 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -284,6 +284,9 @@ def pseudo_op(name, op, real_ops):
"RESUME": [
"RESUME_QUICK",
],
+ "JUMP_BACKWARD": [
+ "JUMP_BACKWARD_QUICK",
+ ],
"BINARY_OP": [
"BINARY_OP_ADD_FLOAT",
"BINARY_OP_ADD_INT",
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index b6f80cfcf910f4..41eaa5030228f9 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -123,7 +123,6 @@
-
@@ -618,6 +617,7 @@
+
@@ -626,7 +626,8 @@
+
-
\ No newline at end of file
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index 953c269aca1d79..4824510f6ac7a6 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -738,9 +738,6 @@
Include\internal
-
- Include\internal
-
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 316171a08f45f7..7816feb9cb3102 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1899,6 +1899,11 @@ dummy_func(
}
inst(JUMP_BACKWARD, (--)) {
+ frame->f_code->_tier2_warmup++;
+ GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
+ }
+
+ inst(JUMP_BACKWARD_QUICK, (--)) {
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 9ff9a718eef8ca..2892b77344b5bc 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2427,6 +2427,12 @@
TARGET(JUMP_BACKWARD) {
PREDICTED(JUMP_BACKWARD);
+ frame->f_code->_tier2_warmup++;
+ GO_TO_INSTRUCTION(JUMP_BACKWARD_QUICK);
+ }
+
+ TARGET(JUMP_BACKWARD_QUICK) {
+ PREDICTED(JUMP_BACKWARD_QUICK);
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 2b2148977635b8..97ad6a9dfe547b 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -244,6 +244,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 0;
case JUMP_BACKWARD:
return 0;
+ case JUMP_BACKWARD_QUICK:
+ return 0;
case POP_JUMP_IF_FALSE:
return 1;
case BB_TEST_POP_IF_FALSE:
@@ -628,6 +630,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case JUMP_BACKWARD:
return 0;
+ case JUMP_BACKWARD_QUICK:
+ return 0;
case POP_JUMP_IF_FALSE:
return 0;
case BB_TEST_POP_IF_FALSE:
@@ -901,7 +905,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[IMPORT_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[IMPORT_FROM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[JUMP_FORWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [JUMP_BACKWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [JUMP_BACKWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [JUMP_BACKWARD_QUICK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[POP_JUMP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[BB_TEST_POP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[POP_JUMP_IF_TRUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index c377c673f85cb2..4a815c73acb7db 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -5,16 +5,17 @@ static void *opcode_targets[256] = {
&&TARGET_INTERPRETER_EXIT,
&&TARGET_END_FOR,
&&TARGET_RESUME_QUICK,
+ &&TARGET_JUMP_BACKWARD_QUICK,
&&TARGET_BINARY_OP_ADD_FLOAT,
&&TARGET_BINARY_OP_ADD_INT,
- &&TARGET_BINARY_OP_ADD_UNICODE,
&&TARGET_NOP,
- &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE,
+ &&TARGET_BINARY_OP_ADD_UNICODE,
&&TARGET_UNARY_NEGATIVE,
&&TARGET_UNARY_NOT,
+ &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE,
&&TARGET_BINARY_OP_MULTIPLY_FLOAT,
- &&TARGET_BINARY_OP_MULTIPLY_INT,
&&TARGET_UNARY_INVERT,
+ &&TARGET_BINARY_OP_MULTIPLY_INT,
&&TARGET_BINARY_OP_SUBTRACT_FLOAT,
&&TARGET_BINARY_OP_SUBTRACT_INT,
&&TARGET_BINARY_SUBSCR_DICT,
@@ -23,20 +24,20 @@ static void *opcode_targets[256] = {
&&TARGET_BINARY_SUBSCR_TUPLE_INT,
&&TARGET_CALL_PY_EXACT_ARGS,
&&TARGET_CALL_PY_WITH_DEFAULTS,
- &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS,
&&TARGET_BINARY_SUBSCR,
&&TARGET_BINARY_SLICE,
&&TARGET_STORE_SLICE,
+ &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS,
&&TARGET_CALL_BUILTIN_CLASS,
- &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS,
&&TARGET_GET_LEN,
&&TARGET_MATCH_MAPPING,
&&TARGET_MATCH_SEQUENCE,
&&TARGET_MATCH_KEYS,
- &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS,
+ &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS,
&&TARGET_PUSH_EXC_INFO,
&&TARGET_CHECK_EXC_MATCH,
&&TARGET_CHECK_EG_MATCH,
+ &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS,
&&TARGET_CALL_NO_KW_BUILTIN_FAST,
&&TARGET_CALL_NO_KW_BUILTIN_O,
&&TARGET_CALL_NO_KW_ISINSTANCE,
@@ -47,7 +48,6 @@ static void *opcode_targets[256] = {
&&TARGET_CALL_NO_KW_METHOD_DESCRIPTOR_O,
&&TARGET_CALL_NO_KW_STR_1,
&&TARGET_CALL_NO_KW_TUPLE_1,
- &&TARGET_CALL_NO_KW_TYPE_1,
&&TARGET_WITH_EXCEPT_START,
&&TARGET_GET_AITER,
&&TARGET_GET_ANEXT,
@@ -55,39 +55,39 @@ static void *opcode_targets[256] = {
&&TARGET_BEFORE_WITH,
&&TARGET_END_ASYNC_FOR,
&&TARGET_CLEANUP_THROW,
+ &&TARGET_CALL_NO_KW_TYPE_1,
&&TARGET_COMPARE_AND_BRANCH_FLOAT,
&&TARGET_COMPARE_AND_BRANCH_INT,
&&TARGET_COMPARE_AND_BRANCH_STR,
- &&TARGET_FOR_ITER_LIST,
&&TARGET_STORE_SUBSCR,
&&TARGET_DELETE_SUBSCR,
+ &&TARGET_FOR_ITER_LIST,
&&TARGET_FOR_ITER_TUPLE,
&&TARGET_FOR_ITER_RANGE,
&&TARGET_FOR_ITER_GEN,
&&TARGET_BB_TEST_ITER_LIST,
&&TARGET_BB_TEST_ITER_TUPLE,
- &&TARGET_BB_TEST_ITER_RANGE,
&&TARGET_GET_ITER,
&&TARGET_GET_YIELD_FROM_ITER,
- &&TARGET_BB_TEST_ITER_GEN,
+ &&TARGET_BB_TEST_ITER_RANGE,
&&TARGET_LOAD_BUILD_CLASS,
+ &&TARGET_BB_TEST_ITER_GEN,
&&TARGET_LOAD_ATTR_CLASS,
- &&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
&&TARGET_LOAD_ASSERTION_ERROR,
&&TARGET_RETURN_GENERATOR,
+ &&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
&&TARGET_LOAD_ATTR_INSTANCE_VALUE,
&&TARGET_LOAD_ATTR_MODULE,
&&TARGET_LOAD_ATTR_PROPERTY,
&&TARGET_LOAD_ATTR_SLOT,
&&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LOAD_ATTR_METHOD_LAZY_DICT,
- &&TARGET_LOAD_ATTR_METHOD_NO_DICT,
&&TARGET_RETURN_VALUE,
- &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES,
+ &&TARGET_LOAD_ATTR_METHOD_NO_DICT,
&&TARGET_SETUP_ANNOTATIONS,
+ &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES,
&&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_LOAD_FAST__LOAD_CONST,
- &&TARGET_LOAD_FAST__LOAD_FAST,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
&&TARGET_DELETE_NAME,
@@ -112,7 +112,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_FORWARD,
&&TARGET_JUMP_IF_FALSE_OR_POP,
&&TARGET_JUMP_IF_TRUE_OR_POP,
- &&TARGET_LOAD_GLOBAL_BUILTIN,
+ &&TARGET_LOAD_FAST__LOAD_FAST,
&&TARGET_POP_JUMP_IF_FALSE,
&&TARGET_POP_JUMP_IF_TRUE,
&&TARGET_LOAD_GLOBAL,
@@ -142,7 +142,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_BACKWARD,
&&TARGET_COMPARE_AND_BRANCH,
&&TARGET_CALL_FUNCTION_EX,
- &&TARGET_LOAD_GLOBAL_MODULE,
+ &&TARGET_LOAD_GLOBAL_BUILTIN,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
&&TARGET_SET_ADD,
@@ -152,29 +152,29 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE,
&&TARGET_RESUME,
&&TARGET_MATCH_CLASS,
+ &&TARGET_LOAD_GLOBAL_MODULE,
&&TARGET_STORE_ATTR_INSTANCE_VALUE,
- &&TARGET_STORE_ATTR_SLOT,
&&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING,
+ &&TARGET_STORE_ATTR_SLOT,
&&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_STORE_FAST__STORE_FAST,
- &&TARGET_STORE_SUBSCR_DICT,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
+ &&TARGET_STORE_SUBSCR_DICT,
&&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_UNPACK_SEQUENCE_LIST,
&&TARGET_UNPACK_SEQUENCE_TUPLE,
&&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
- &&TARGET_SEND_GEN,
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_CALL_INTRINSIC_1,
&&TARGET_CALL_INTRINSIC_2,
- &&_unknown_opcode,
+ &&TARGET_SEND_GEN,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
diff --git a/Python/specialize.c b/Python/specialize.c
index b236a06af6bc64..4ede3122d38046 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -9,7 +9,6 @@
#include "pycore_opcode.h" // _PyOpcode_Caches
#include "structmember.h" // struct PyMemberDef, T_OFFSET_EX
#include "pycore_descrobject.h"
-#include "pycore_opcode_macro_to_micro.h"
#include // rand()
diff --git a/Python/tier2.c b/Python/tier2.c
index 44f6cb858698d6..767d7e5bb98263 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -202,6 +202,7 @@ IS_JREL_OPCODE(int opcode)
case SEND:
case POP_JUMP_IF_NOT_NONE:
case POP_JUMP_IF_NONE:
+ case JUMP_BACKWARD_QUICK:
case JUMP_BACKWARD_NO_INTERRUPT:
case JUMP_BACKWARD:
return 1;
@@ -214,7 +215,9 @@ IS_JREL_OPCODE(int opcode)
static inline int
IS_JUMP_BACKWARDS_OPCODE(int opcode)
{
- return opcode == JUMP_BACKWARD_NO_INTERRUPT || opcode == JUMP_BACKWARD;
+ return opcode == JUMP_BACKWARD_NO_INTERRUPT ||
+ opcode == JUMP_BACKWARD ||
+ opcode == JUMP_BACKWARD_QUICK;
}
@@ -390,6 +393,7 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
int oparg = _Py_OPARG(branch);
// @TODO handle JUMP_BACKWARDS and JUMP_BACKWARDS_NO_INTERRUPT
switch (_PyOpcode_Deopt[_Py_OPCODE(branch)]) {
+ case JUMP_BACKWARD_QUICK:
case JUMP_BACKWARD:
// The initial backwards jump needs to find the right basic block.
// Subsequent jumps don't need to check this anymore. They can just
@@ -608,7 +612,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
int n_typecontext, PyTypeObject **type_context)
{
#define END() goto end;
-#define JUMPBY(x) i += x + 1; continue;
+#define JUMPBY(x) i += x + 1;
#define BASIC_PUSH(v) (*stack_pointer++ = (v))
#define BASIC_POP() (*--stack_pointer)
#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); \
@@ -664,15 +668,15 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
case RESUME:
opcode = RESUME_QUICK;
DISPATCH();
- case EXTENDED_ARG:
- write_i = emit_i(write_i, EXTENDED_ARG, _Py_OPARG(*curr));
- curr++;
- next_instr++;
- i++;
- oparg = oparg << 8 | _Py_OPARG(*curr);
- opcode = _Py_OPCODE(*curr);
- caches = _PyOpcode_Caches[opcode];
- DISPATCH_GOTO();
+ //case EXTENDED_ARG:
+ // write_i = emit_i(write_i, EXTENDED_ARG, _Py_OPARG(*curr));
+ // curr++;
+ // next_instr++;
+ // i++;
+ // oparg = oparg << 8 | _Py_OPARG(*curr);
+ // opcode = _Py_OPCODE(*curr);
+ // caches = _PyOpcode_Caches[opcode];
+ // DISPATCH_GOTO();
// We need to rewrite the pseudo-branch instruction.
case COMPARE_AND_BRANCH:
opcode = COMPARE_OP;
@@ -684,57 +688,18 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// So we tell the BB to skip over it.
t2_start++;
DISPATCH();
- // FOR_ITER must be handled separately from other opcodes as it has
- // CACHE entries following it.
- //case LOAD_FAST:
- // BASIC_PUSH(type_context[oparg]);
- // DISPATCH();
- //case LOAD_NAME:
- // BASIC_PUSH(NULL);
- // DISPATCH();
- //case LOAD_CONST: {
- // PyTypeObject *cont = Py_TYPE(PyTuple_GET_ITEM(co->co_consts, oparg));
- // BASIC_PUSH(cont);
- // DISPATCH();
- //}
- //case STORE_FAST: {
- // type_context[oparg] = BASIC_POP();
- // DISPATCH();
- //}
- //case STORE_NAME:
- // BASIC_POP();
- // DISPATCH();
- //case BINARY_OP: {
- // PyTypeObject *res = BINARY_OP_RESULT_TYPE(co, curr, n_typecontext, type_context,
- // &how_many_guards, &guard_instr, &action);
- // // We need a guard. So this is the end of the basic block.
- // // @TODO in the future, support multiple guards.
- // if (how_many_guards > 0) {
- // // Emit the guard
- // emit_type_guard(&write_i, guard_instr);
- // END();
- // }
- // else {
- // BASIC_PUSH(res);
- // // Don't need a guard, either is a micro op, or is a generic instruction
- // // No type information known. Use a generic instruction.
- // if (res == NULL) {
- // DISPATCH();
- // }
- // else {
- // emit_i(&write_i, _Py_OPCODE(action), 0);
- // break;
- // }
- // }
- //}
default:
fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
if (IS_BACKWARDS_JUMP_TARGET(co, curr)) {
+#if BB_DEBUG
fprintf(stderr, "Encountered a backward jump target\n");
+#endif
// This should be the end of another basic block, or the start of a new.
// Start of a new basic block, just ignore and continue.
if (virtual_start) {
+#if BB_DEBUG
fprintf(stderr, "Emitted virtual start of basic block\n");
+#endif
starts_with_backwards_jump_target = true;
backwards_jump_target_offset = curr - _PyCode_CODE(co);
virtual_start = false;
@@ -771,6 +736,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
if (opcode == JUMP_FORWARD) {
// JUMP offset (oparg) + current instruction + cache entries
JUMPBY(oparg);
+ continue;
}
// Get the BB ID without incrementing it.
// AllocateBBMetaData will increment.
@@ -851,23 +817,13 @@ static int
_PyCode_Tier2FillJumpTargets(PyCodeObject *co)
{
assert(co->_tier2_info != NULL);
- // Remove all the RESUME instructions.
- // Count all the jump targets.
+ // Count all the backwards jump targets.
Py_ssize_t backwards_jump_count = 0;
for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
_Py_CODEUNIT *instr_ptr = _PyCode_CODE(co) + i;
_Py_CODEUNIT instr = *instr_ptr;
int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
- int oparg = _Py_OPARG(instr);
- switch (opcode) {
- case RESUME:
- _py_set_opcode(instr_ptr, RESUME_QUICK);
- break;
- default:
- // We want to track all guard instructions as
- // jumps too.
- backwards_jump_count += IS_JUMP_BACKWARDS_OPCODE(opcode); // + INSTR_HAS_GUARD(instr);
- }
+ backwards_jump_count += IS_JUMP_BACKWARDS_OPCODE(opcode);
i += _PyOpcode_Caches[opcode];
}
@@ -908,7 +864,9 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
if (IS_JUMP_BACKWARDS_OPCODE(opcode)) {
// + 1 because it's calculated from nextinstr (see JUMPBY in ceval.c)
_Py_CODEUNIT *target = curr + 1 - oparg;
+#if BB_DEBUG
fprintf(stderr, "jump target opcode is %d\n", _Py_OPCODE(*target));
+#endif
// (in terms of offset from start of co_code_adaptive)
backward_jump_offsets[curr_i] = (int)(target - start);
curr_i++;
@@ -999,6 +957,26 @@ IS_OPTIMIZABLE_OPCODE(int opcode, int oparg)
}
}
+static inline void
+replace_resume_and_jump_backwards(PyCodeObject *co)
+{
+ for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
+ _Py_CODEUNIT *instr_ptr = _PyCode_CODE(co) + i;
+ _Py_CODEUNIT instr = *instr_ptr;
+ int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
+ int oparg = _Py_OPARG(instr);
+ switch (opcode) {
+ case RESUME:
+ _py_set_opcode(instr_ptr, RESUME_QUICK);
+ break;
+ case JUMP_BACKWARD:
+ _py_set_opcode(instr_ptr, JUMP_BACKWARD_QUICK);
+ break;
+ }
+ i += _PyOpcode_Caches[opcode];
+ }
+}
+
// 1. Initialize whatever we need.
// 2. Create the entry BB.
// 3. Jump into that BB.
@@ -1006,13 +984,14 @@ static _Py_CODEUNIT *
_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
assert(_Py_OPCODE(*(next_instr - 1)) == RESUME);
-
- // First check for forbidden opcodes that we currently can't handle.
PyCodeObject *co = frame->f_code;
+ // Replace all the RESUME and JUMP_BACKWARDS so that it doesn't waste time again.
+ replace_resume_and_jump_backwards(co);
// Impossibly big.
if ((int)Py_SIZE(co) != Py_SIZE(co)) {
return NULL;
}
+ // First check for forbidden opcodes that we currently can't handle.
int optimizable = 0;
for (Py_ssize_t curr = 0; curr < Py_SIZE(co); curr++) {
_Py_CODEUNIT *curr_instr = _PyCode_CODE(co) + curr;
@@ -1094,7 +1073,8 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
PyCodeObject *code = frame->f_code;
if (code->_tier2_warmup != 0) {
code->_tier2_warmup++;
- if (code->_tier2_warmup == 0) {
+ if (code->_tier2_warmup >= 0) {
+ assert(code->_tier2_info == NULL);
// If it fails, due to lack of memory or whatever,
// just fall back to the tier 1 interpreter.
_Py_CODEUNIT *next = _PyCode_Tier2Initialize(frame, next_instr);
@@ -1173,12 +1153,18 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int j
int jump_offset = (int)(tier1_jump_target - _PyCode_CODE(co));
int matching_bb_id = -1;
+#if BB_DEBUG
fprintf(stderr, "finding jump target: %d\n", jump_offset);
+#endif
for (int i = 0; i < t2_info->backward_jump_count; i++) {
+#if BB_DEBUG
fprintf(stderr, "jump offset checked: %d\n", t2_info->backward_jump_offsets[i]);
+#endif
if (t2_info->backward_jump_offsets[i] == jump_offset) {
for (int x = 0; x < MAX_BB_VERSIONS; x++) {
+#if BB_DEBUG
fprintf(stderr, "jump target BB ID: %d\n",
+#endif
t2_info->backward_jump_target_bb_ids[i][x]);
// @TODO, this is where the diff function is supposed to be
// it will calculate the closest type context BB
@@ -1290,7 +1276,7 @@ _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *tar
_py_set_opcode(write_curr, NOP);
write_curr++;
}
- _py_set_opcode(write_curr, JUMP_BACKWARD);
+ _py_set_opcode(write_curr, JUMP_BACKWARD_QUICK);
write_curr->oparg = oparg & 0xFF;
write_curr++;
_py_set_opcode(write_curr, END_FOR);
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 71bfa55e38aa6f..8703d7a0ea1ae5 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -1338,8 +1338,8 @@ def main():
sys.exit(f"Found {a.errors} errors")
a.write_instructions() # Raises OSError if output can't be written
a.write_metadata()
- a.output_filename = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
- a.write_macromap_and_typedata()
+ # a.output_filename = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
+ # a.write_macromap_and_typedata()
if __name__ == "__main__":
From 10e8eb2983dd47351723d32c8d7780f853d04ed0 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 24 Feb 2023 23:42:11 +0800
Subject: [PATCH 038/280] Fix jump calculation for FOR_ITER
---
Python/tier2.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 767d7e5bb98263..7bc75e1f38ad79 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -461,7 +461,9 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
// The oparg of FOR_ITER is a little special, the actual jump has to jump over
// its own cache entries, the oparg, -1 to tell it to start generating from the
// END_FOR. However, at runtime, we will skip this END_FOR.
- oparg = INLINE_CACHE_ENTRIES_FOR_ITER + oparg - 1;
+ // NOTE: IF YOU CHANGE ANY OF THE INSTRUCTIONS BELOW, MAKE SURE
+ // TO UPDATE THE CALCULATION OF OPARG. THIS IS EXTREMELY IMPORTANT.
+ oparg = INLINE_CACHE_ENTRIES_FOR_ITER + oparg;
_py_set_opcode(write_curr, BB_TEST_ITER);
write_curr->oparg = oparg;
write_curr++;
From e1a5b61d1563b9449786380475111418b85b9491 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 25 Feb 2023 00:04:31 +0800
Subject: [PATCH 039/280] Make compatible with main
---
Python/tier2.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 7bc75e1f38ad79..0da0a7f6be37ef 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -339,7 +339,7 @@ BINARY_OP_RESULT_TYPE(PyCodeObject *co, _Py_CODEUNIT *instr, int n_typecontext,
if (lhs_type == &PyLong_Type) {
if (rhs_type == &PyLong_Type) {
*how_many_guards = 0;
- action->opcode = BINARY_OP_ADD_INT_REST;
+ action->op.code = BINARY_OP_ADD_INT_REST;
return &PyLong_Type;
}
//// Right side unknown. Emit single check.
@@ -376,7 +376,7 @@ emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard, int bb_id)
//write_curr->oparg = 0;
//write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
- write_curr->oparg = 0;
+ write_curr->op.arg = 0;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -439,12 +439,12 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
#endif
// Just in case
_py_set_opcode(write_curr, EXTENDED_ARG);
- write_curr->oparg = 0;
+ write_curr->op.arg = 0;
write_curr++;
// We don't need to recalculate the backward jump, because that only needs to be done
// when it locates the next BB in JUMP_BACKWARD_LAZY.
_py_set_opcode(write_curr, BB_JUMP_BACKWARD_LAZY);
- write_curr->oparg = oparg;
+ write_curr->op.arg = oparg;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -465,11 +465,11 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
// TO UPDATE THE CALCULATION OF OPARG. THIS IS EXTREMELY IMPORTANT.
oparg = INLINE_CACHE_ENTRIES_FOR_ITER + oparg;
_py_set_opcode(write_curr, BB_TEST_ITER);
- write_curr->oparg = oparg;
+ write_curr->op.arg = oparg;
write_curr++;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_FOR_ITER);
_py_set_opcode(write_curr, BB_BRANCH);
- write_curr->oparg = oparg;
+ write_curr->op.arg = oparg;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -484,10 +484,10 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
#endif
//_Py_CODEUNIT *start = write_curr;
_py_set_opcode(write_curr, opcode);
- write_curr->oparg = oparg;
+ write_curr->op.arg = oparg;
write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
- write_curr->oparg = oparg & 0xFF;
+ write_curr->op.arg = oparg & 0xFF;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -526,7 +526,7 @@ static inline _Py_CODEUNIT *
emit_i(_Py_CODEUNIT *write_curr, int opcode, int oparg)
{
_py_set_opcode(write_curr, opcode);
- write_curr->oparg = oparg;
+ write_curr->op.arg = oparg;
write_curr++;
return write_curr;
}
@@ -1221,14 +1221,14 @@ _PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target)
assert(oparg <= 0xFFFF);
if (requires_extended) {
_py_set_opcode(write_curr, EXTENDED_ARG);
- write_curr->oparg = (oparg >> 8) & 0xFF;
+ write_curr->op.arg = (oparg >> 8) & 0xFF;
write_curr++;
// -1 to oparg because now the jump instruction moves one unit forward.
oparg--;
}
_py_set_opcode(write_curr,
branch == BB_BRANCH_IF_FLAG_SET ? BB_JUMP_IF_FLAG_SET : BB_JUMP_IF_FLAG_UNSET);
- write_curr->oparg = oparg & 0xFF;
+ write_curr->op.arg = oparg & 0xFF;
write_curr++;
if (!requires_extended) {
_py_set_opcode(write_curr, NOP);
@@ -1271,7 +1271,7 @@ _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *tar
bool requires_extended = oparg > 0xFF;
if (requires_extended) {
_py_set_opcode(write_curr, EXTENDED_ARG);
- write_curr->oparg = (oparg >> 8) & 0xFF;
+ write_curr->op.arg = (oparg >> 8) & 0xFF;
write_curr++;
}
else {
@@ -1279,7 +1279,7 @@ _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *tar
write_curr++;
}
_py_set_opcode(write_curr, JUMP_BACKWARD_QUICK);
- write_curr->oparg = oparg & 0xFF;
+ write_curr->op.arg = oparg & 0xFF;
write_curr++;
_py_set_opcode(write_curr, END_FOR);
write_curr++;
From 28158ff4cc5c6900d17f4c0ad7efbaa71375fe3c Mon Sep 17 00:00:00 2001
From: Julia
Date: Sat, 25 Feb 2023 13:31:52 +0800
Subject: [PATCH 040/280] refactor: generate_cases.py don't emit 0-length array
---
Include/internal/pycore_opcode_macro_to_micro.h | 2 --
Tools/cases_generator/generate_cases.py | 10 +++++-----
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/Include/internal/pycore_opcode_macro_to_micro.h b/Include/internal/pycore_opcode_macro_to_micro.h
index b33b88b838099b..4f120781d01037 100644
--- a/Include/internal/pycore_opcode_macro_to_micro.h
+++ b/Include/internal/pycore_opcode_macro_to_micro.h
@@ -199,8 +199,6 @@ extern const int _Py_MacroOpUOpCount[] = {
extern const int _Py_MacroOpToUOp[][1] = {
[BINARY_OP_ADD_INT] = {BINARY_OP_ADD_INT_REST},
};
-extern const PyTypeObject *_Py_UOpGuardTypes[][0] = {
-};
#ifdef __cplusplus
}
#endif
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 71bfa55e38aa6f..ac806a2d77a5e0 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -1012,11 +1012,11 @@ def write_uopguard_typedata(self):
if types:
max_types = max(max_types, len(types))
uop_to_type_output[instr_def.name] = types
-
- self.out.emit(f"extern const PyTypeObject *_Py_UOpGuardTypes[][{max_types}] = {{")
- for name, types in uop_to_type_output.items():
- self.out.emit(f"[{name}] = {{{', '.join(['&' + type_ for type_ in types])}}},")
- self.out.emit("};")
+ if max_types > 0:
+ self.out.emit(f"extern const PyTypeObject *_Py_UOpGuardTypes[][{max_types}] = {{")
+ for name, types in uop_to_type_output.items():
+ self.out.emit(f"[{name}] = {{{', '.join(['&' + type_ for type_ in types])}}},")
+ self.out.emit("};")
def write_metadata(self) -> None:
From c5bd72135165bf4ed87e5af6755e24c9df4bac1a Mon Sep 17 00:00:00 2001
From: Julia
Date: Sat, 25 Feb 2023 13:32:53 +0800
Subject: [PATCH 041/280] refactor: make type_context explicit as a struct in
preparation for implementing the types meta-interpreter
---
Include/cpython/code.h | 22 +++++--
Objects/codeobject.c | 5 +-
Python/tier2.c | 133 +++++++++++++++++++++++++++--------------
3 files changed, 105 insertions(+), 55 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 3e413c618d5eb0..1b91a8b514124b 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -42,18 +42,22 @@ typedef struct {
PyObject *_co_freevars;
} _PyCoCached;
+// Tier 2 types meta interpreter
+typedef struct _PyTier2TypeContext {
+ int type_locals_len;
+ int type_stack_len;
+ PyTypeObject** type_stack;
+ PyTypeObject** type_locals;
+} _PyTier2TypeContext;
+
// Tier 2 interpreter information
typedef struct _PyTier2BBMetadata {
// Index into _PyTier2Info->bb_data
int id;
- // Array of types. This corresponds to the fast locals array.
- int type_context_len;
- PyTypeObject **type_context;
+ _PyTier2TypeContext *type_context;
_Py_CODEUNIT *tier2_start;
// Note, this is the first tier 1 instruction to execute AFTER the BB ends.
_Py_CODEUNIT *tier1_end;
- // Type stack state
- PyTypeObject **types_stack;
} _PyTier2BBMetadata;
// Bump allocator for basic blocks (overallocated)
@@ -83,13 +87,19 @@ typedef struct _PyTier2Info {
// will have [[BB_ID1, BB_ID2], [BB_ID3,], [], []]
// etc.
int **backward_jump_target_bb_ids;
- PyTypeObject **types_stack;
// Max len of bb_data
int bb_data_len;
// Current index to write into in bb_data. Incremented after each write.
// This also assigns the BB ID.
int bb_data_curr;
_PyTier2BBMetadata **bb_data;
+
+ // @TODO:
+ // Potentially optimise _PyTier2TypeContext by allocating the stacksize
+ // to the size needed for the snapshot, and the type propagation is performed
+ // on type_metainterpreter_stack_scratch which is allocated only once per
+ // code object.
+ // PyTypeObject** type_metainterpreter_stack_scratch;
} _PyTier2Info;
// To avoid repeating ourselves in deepfreeze.py, all PyCodeObject members are
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index d9259cff23a2d9..22705442b43abc 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -1690,10 +1690,7 @@ code_tier2_fini(PyCodeObject *co)
PyMem_Free(t2_info->backward_jump_offsets);
t2_info->backward_jump_offsets = NULL;
}
- if (t2_info->types_stack != NULL) {
- PyMem_Free(t2_info->types_stack);
- t2_info->types_stack = NULL;
- }
+
t2_info->backward_jump_count = 0;
if (t2_info->bb_data != NULL && t2_info->bb_data_len > 0) {
PyMem_Free(t2_info->bb_data);
diff --git a/Python/tier2.c b/Python/tier2.c
index 44f6cb858698d6..a6ce08ae26f52a 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -19,21 +19,78 @@ static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
////////// TYPE CONTEXT FUNCTIONS
-static PyTypeObject **
-initialize_type_context(PyCodeObject *co, int *type_context_len) {
+static _PyTier2TypeContext *
+initialize_type_context(const PyCodeObject *co) {
+
int nlocals = co->co_nlocals;
- PyTypeObject **type_context = PyMem_Malloc(nlocals * sizeof(PyTypeObject *));
- if (type_context == NULL) {
+ int nstack = co->co_stacksize;
+
+ PyTypeObject **type_locals = PyMem_Malloc(nlocals * sizeof(PyTypeObject *));
+ if (type_locals == NULL) {
+ return NULL;
+ }
+ PyTypeObject **type_stack = PyMem_Malloc(nstack * sizeof(PyTypeObject *));
+ if (type_stack == NULL) {
+ PyMem_Free(type_locals);
return NULL;
}
- // Initialize to uknown type.
- for (int i = 0; i < nlocals; i++) {
- type_context[i] = NULL;
+
+ // Initialize to unknown type.
+ for (int i = 0; i < nlocals; i++) type_locals[i] = NULL;
+ for (int i = 0; i < nstack; i++) type_stack[i] = NULL;
+
+ _PyTier2TypeContext *type_context = PyMem_Malloc(sizeof(_PyTier2TypeContext));
+ if (type_context == NULL) {
+ PyMem_Free(type_locals);
+ PyMem_Free(type_stack);
+ return NULL;
}
- *type_context_len = nlocals;
+ type_context->type_locals_len = nlocals;
+ type_context->type_stack_len = nstack;
+ type_context->type_locals = type_locals;
+ type_context->type_stack = type_stack;
return type_context;
}
+static _PyTier2TypeContext *
+_PyTier2TypeContext_copy(const _PyTier2TypeContext* type_context) {
+
+ int nlocals = type_context->type_locals_len;
+ int nstack = type_context->type_stack_len;
+
+ PyTypeObject** type_locals = PyMem_Malloc(nlocals * sizeof(PyTypeObject*));
+ if (type_locals == NULL) {
+ return NULL;
+ }
+ PyTypeObject** type_stack = PyMem_Malloc(nstack * sizeof(PyTypeObject*));
+ if (type_stack == NULL) {
+ PyMem_Free(type_locals);
+ return NULL;
+ }
+
+ memcpy(type_locals, type_context->type_locals, nlocals * sizeof(PyTypeObject*));
+ memcpy(type_stack, type_context->type_stack, nstack * sizeof(PyTypeObject*));
+
+ _PyTier2TypeContext* new_type_context = PyMem_Malloc(sizeof(_PyTier2TypeContext));
+ if (new_type_context == NULL) {
+ PyMem_Free(type_locals);
+ PyMem_Free(type_stack);
+ return NULL;
+ }
+ new_type_context->type_locals_len = nlocals;
+ new_type_context->type_stack_len = nstack;
+ new_type_context->type_locals = type_locals;
+ new_type_context->type_stack = type_stack;
+ return new_type_context;
+}
+
+static void
+_PyTier2TypeContext_free(_PyTier2TypeContext* type_context) {
+ PyMem_Free(type_context->type_locals);
+ PyMem_Free(type_context->type_stack);
+ PyMem_Free(type_context);
+}
+
////////// Utility functions
// Gets end of the bytecode for a code object.
@@ -108,18 +165,18 @@ _PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_reque
static _PyTier2BBMetadata *
allocate_bb_metadata(PyCodeObject *co, _Py_CODEUNIT *tier2_start,
- _Py_CODEUNIT *tier1_end, int type_context_len,
- PyTypeObject **type_context)
+ _Py_CODEUNIT *tier1_end,
+ _PyTier2TypeContext *type_context)
{
_PyTier2BBMetadata *metadata = PyMem_Malloc(sizeof(_PyTier2BBMetadata));
if (metadata == NULL) {
return NULL;
+
}
metadata->tier2_start = tier2_start;
metadata->tier1_end = tier1_end;
metadata->type_context = type_context;
- metadata->type_context_len = type_context_len;
return metadata;
}
@@ -161,11 +218,11 @@ write_bb_metadata(PyCodeObject *co, _PyTier2BBMetadata *metadata)
static _PyTier2BBMetadata *
_PyTier2_AllocateBBMetaData(PyCodeObject *co, _Py_CODEUNIT *tier2_start,
- _Py_CODEUNIT *tier1_end, int type_context_len,
- PyTypeObject **type_context)
+ _Py_CODEUNIT *tier1_end,
+ _PyTier2TypeContext *type_context)
{
_PyTier2BBMetadata *meta = allocate_bb_metadata(co,
- tier2_start, tier1_end, type_context_len, type_context);
+ tier2_start, tier1_end, type_context);
if (meta == NULL) {
return NULL;
}
@@ -605,7 +662,8 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
_PyTier2BBMetadata *
_PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
_Py_CODEUNIT *tier1_start,
- int n_typecontext, PyTypeObject **type_context)
+ // Const qualifier as this function makes a copy of the type_context
+ const _PyTier2TypeContext *type_context)
{
#define END() goto end;
#define JUMPBY(x) i += x + 1; continue;
@@ -624,11 +682,10 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// 2. If there's a type guard.
// Make a copy of the type context
- PyTypeObject **type_context_copy = PyMem_Malloc(n_typecontext * sizeof(PyTypeObject *));
+ _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_copy(type_context);
if (type_context_copy == NULL) {
return NULL;
}
- memcpy(type_context_copy, type_context, n_typecontext * sizeof(PyTypeObject *));
_PyTier2BBMetadata *meta = NULL;
_PyTier2BBMetadata *temp_meta = NULL;
@@ -636,7 +693,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
_PyTier2Info *t2_info = co->_tier2_info;
_Py_CODEUNIT *t2_start = (_Py_CODEUNIT *)(((char *)bb_space->u_code) + bb_space->water_level);
_Py_CODEUNIT *write_i = t2_start;
- PyTypeObject **stack_pointer = co->_tier2_info->types_stack;
+ PyTypeObject **stack_pointer = type_context_copy->type_stack;
int tos = -1;
// For handling of backwards jumps
@@ -655,9 +712,9 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// Just because an instruction requires a guard doesn't mean it's the end of a BB.
// We need to check whether we can eliminate the guard based on the current type context.
- int how_many_guards = 0;
- _Py_CODEUNIT guard_instr;
- _Py_CODEUNIT action;
+ // int how_many_guards = 0;
+ // _Py_CODEUNIT guard_instr;
+ // _Py_CODEUNIT action;
dispatch_opcode:
switch (opcode) {
@@ -744,7 +801,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// But generate the block after that so it can fall through.
i--;
meta = _PyTier2_AllocateBBMetaData(co,
- t2_start, _PyCode_CODE(co) + i, n_typecontext, type_context_copy);
+ t2_start, _PyCode_CODE(co) + i, type_context_copy);
if (meta == NULL) {
PyMem_Free(type_context_copy);
return NULL;
@@ -787,7 +844,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// Create the tier 2 BB
temp_meta = _PyTier2_AllocateBBMetaData(co, t2_start,
// + 1 because we want to start with the NEXT instruction for the scan
- _PyCode_CODE(co) + i + 1, n_typecontext, type_context_copy);
+ _PyCode_CODE(co) + i + 1, type_context_copy);
// We need to return the first block to enter into. If there is already a block generated
// before us, then we use that instead of the most recent block.
if (meta == NULL) {
@@ -943,13 +1000,6 @@ _PyTier2Info_Initialize(PyCodeObject *co)
return NULL;
}
- // Next is to intitialize stack space for the tier 2 types meta-interpretr.
- PyTypeObject **types_stack = PyMem_Malloc(co->co_stacksize * sizeof(PyObject *));
- if (types_stack == NULL) {
- PyMem_Free(t2_info);
- return NULL;
- }
- t2_info->types_stack = types_stack;
t2_info->backward_jump_count = 0;
t2_info->backward_jump_offsets = NULL;
@@ -962,7 +1012,6 @@ _PyTier2Info_Initialize(PyCodeObject *co)
_PyTier2BBMetadata **bb_data = PyMem_Malloc(bb_data_len * sizeof(_PyTier2Info *));
if (bb_data == NULL) {
PyMem_Free(t2_info);
- PyMem_Free(types_stack);
return NULL;
}
t2_info->bb_data_len = (int)bb_data_len;
@@ -1054,14 +1103,13 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
t2_info->_bb_space = bb_space;
- int type_context_len = 0;
- PyTypeObject **type_context = initialize_type_context(co, &type_context_len);
+ _PyTier2TypeContext *type_context = initialize_type_context(co);
if (type_context == NULL) {
goto cleanup;
}
_PyTier2BBMetadata *meta = _PyTier2_Code_DetectAndEmitBB(
co, bb_space,
- _PyCode_CODE(co), type_context_len, type_context);
+ _PyCode_CODE(co), type_context);
if (meta == NULL) {
PyMem_Free(type_context);
goto cleanup;
@@ -1128,13 +1176,10 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
// DEOPTIMIZE TO TIER 1?
return NULL;
}
- int type_context_len = 0;
- PyTypeObject **type_context = initialize_type_context(frame->f_code, &type_context_len);
- if (type_context == NULL) {
- return NULL;
- }
+ // Get type_context of previous BB
+ _PyTier2TypeContext *type_context = meta->type_context;
_PyTier2BBMetadata *metadata = _PyTier2_Code_DetectAndEmitBB(
- frame->f_code, space, tier1_end, type_context_len,
+ frame->f_code, space, tier1_end,
type_context);
if (metadata == NULL) {
PyMem_Free(type_context);
@@ -1163,11 +1208,9 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int j
// DEOPTIMIZE TO TIER 1?
return NULL;
}
- int type_context_len = 0;
- PyTypeObject **type_context = initialize_type_context(frame->f_code, &type_context_len);
- if (type_context == NULL) {
- return NULL;
- }
+
+ // Get type_context of previous BB
+ _PyTier2TypeContext* type_context = meta->type_context;
// Now, find the matching BB
_PyTier2Info *t2_info = co->_tier2_info;
int jump_offset = (int)(tier1_jump_target - _PyCode_CODE(co));
From ce57e6abd0b9d8c9da44c9b8805e509432863fa1 Mon Sep 17 00:00:00 2001
From: Julia
Date: Sun, 26 Feb 2023 01:01:35 +0800
Subject: [PATCH 042/280] fix: memory handling of type_context
---
Objects/codeobject.c | 5 +++++
Python/tier2.c | 10 ++++------
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 19e6c8bdb576ba..089139073a4d7e 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -1672,6 +1672,11 @@ code_tier2_fini(PyCodeObject *co)
if (co->_tier2_info == NULL) {
return;
}
+ // @TODO:
+ // Write a proper destructor for _PyTier2Info
+ // and it's children structures.
+ // Current implementation e.g., doesn't clear
+ // bb_data
_PyTier2Info *t2_info = co->_tier2_info;
t2_info->_entry_bb = NULL;
if (t2_info->_bb_space != NULL) {
diff --git a/Python/tier2.c b/Python/tier2.c
index 3fc76e12bb98c4..7d47db12c8bbb9 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -770,7 +770,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
meta = _PyTier2_AllocateBBMetaData(co,
t2_start, _PyCode_CODE(co) + i, type_context_copy);
if (meta == NULL) {
- PyMem_Free(type_context_copy);
+ _PyTier2TypeContext_free(type_context_copy);
return NULL;
}
bb_space->water_level += (write_i - t2_start) * sizeof(_Py_CODEUNIT);
@@ -823,7 +823,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
if (add_metadata_to_jump_2d_array(t2_info, temp_meta, backwards_jump_target_offset) < 0) {
PyMem_Free(meta);
PyMem_Free(temp_meta);
- PyMem_Free(type_context_copy);
+ _PyTier2TypeContext_free(type_context_copy);
return NULL;
}
}
@@ -969,7 +969,7 @@ _PyTier2Info_Initialize(PyCodeObject *co)
t2_info->bb_data_curr = 0;
Py_ssize_t bb_data_len = (Py_SIZE(co) / 5 + 1);
assert((int)bb_data_len == bb_data_len);
- _PyTier2BBMetadata **bb_data = PyMem_Malloc(bb_data_len * sizeof(_PyTier2Info *));
+ _PyTier2BBMetadata **bb_data = PyMem_Calloc(bb_data_len, sizeof(_PyTier2BBMetadata*));
if (bb_data == NULL) {
PyMem_Free(t2_info);
return NULL;
@@ -981,7 +981,6 @@ _PyTier2Info_Initialize(PyCodeObject *co)
return t2_info;
}
-
////////// OVERALL TIER2 FUNCTIONS
@@ -1092,7 +1091,7 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
co, bb_space,
_PyCode_CODE(co), type_context);
if (meta == NULL) {
- PyMem_Free(type_context);
+ _PyTier2TypeContext_free(type_context);
goto cleanup;
}
#if BB_DEBUG
@@ -1164,7 +1163,6 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
frame->f_code, space, tier1_end,
type_context);
if (metadata == NULL) {
- PyMem_Free(type_context);
return NULL;
}
return metadata->tier2_start;
From 2f02babe7513c5f5c105ff0c50fc3a44abff0111 Mon Sep 17 00:00:00 2001
From: Julia
Date: Sun, 26 Feb 2023 15:26:09 +0800
Subject: [PATCH 043/280] Refactor: deadcode cleanup for type propagation code
---
Python/tier2.c | 65 --------------------------------------------------
1 file changed, 65 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 7d47db12c8bbb9..7aadfa8fb32939 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -349,71 +349,6 @@ IS_FORBIDDEN_OPCODE(int opcode)
}
}
-static inline PyTypeObject *
-INSTR_LOCAL_READ_TYPE(PyCodeObject *co, _Py_CODEUNIT instr, PyTypeObject **type_context)
-{
- int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
- int oparg = _Py_OPARG(instr);
- switch (opcode) {
- case LOAD_CONST:
- return Py_TYPE(PyTuple_GET_ITEM(co->co_consts, oparg));
- case LOAD_FAST:
- return type_context[oparg];
- // Note: Don't bother with LOAD_NAME, because those exist only in the global
- // scope.
- default:
- return NULL;
- }
-}
-
-/*
-* This does multiple things:
-* 1. Gets the result type of an instruction, if it can figure it out.
- 2. Determines whether thet instruction has a corresponding macro/micro instruction
- that acts a guard, and whether it needs it.
- 3. How many guards it needs, and what are the guards.
- 4. Finally, the corresponding specialised micro-op to operate on this type.
-
- Returns the inferred type of an operation. NULL if unknown.
-
- how_many_guards returns the number of guards (0, 1)
- guards should be a opcode corresponding to the guard instruction to use.
-*/
-static PyTypeObject *
-BINARY_OP_RESULT_TYPE(PyCodeObject *co, _Py_CODEUNIT *instr, int n_typecontext,
- PyTypeObject **type_context, int *how_many_guards, _Py_CODEUNIT *guard, _Py_CODEUNIT *action)
-{
- int opcode = _PyOpcode_Deopt[_Py_OPCODE(*instr)];
- int oparg = _Py_OPARG(*instr);
- switch (opcode) {
- case BINARY_OP:
- if (oparg == NB_ADD) {
- // For BINARY OP, read the previous two load instructions
- // to see what variables we need to type check.
- PyTypeObject *lhs_type = INSTR_LOCAL_READ_TYPE(co, *(instr - 2), type_context);
- PyTypeObject *rhs_type = INSTR_LOCAL_READ_TYPE(co, *(instr - 1), type_context);
- // The two instruction types are known.
- if (lhs_type == &PyLong_Type) {
- if (rhs_type == &PyLong_Type) {
- *how_many_guards = 0;
- action->op.code = BINARY_OP_ADD_INT_REST;
- return &PyLong_Type;
- }
- //// Right side unknown. Emit single check.
- //else if (rhs_type == NULL) {
- // *how_many_guards = 1;
- // guard->opcode =
- // return NULL;
- //}
- }
- // We don't know anything, need to emit guard.
- // @TODO
- }
- break;
- }
- return NULL;
-}
-
static inline _Py_CODEUNIT *
emit_cache_entries(_Py_CODEUNIT *write_curr, int cache_entries)
{
From f11c9af06c05cc70979c8f65628933667f9fe774 Mon Sep 17 00:00:00 2001
From: Julia
Date: Sun, 26 Feb 2023 18:11:23 +0800
Subject: [PATCH 044/280] working: types meta-interpreter in progress
Might require a refactor of the tier2 bytecode emitting code to progress.
When emitting from a codeobject (with tier1 bytecode):
1. deop inst back to tier0
2. emit tier2 inst based on current type context
3. type propagate through emitted inst
This allows the type propagator to work with tier2 bytecode (which contains more information)
and can probably be generated from the DSL
---
.gitattributes | 1 +
Include/cpython/code.h | 2 ++
Python/tier2.c | 13 ++++++++++---
Python/tier2_metainterpreter.c.h | 0
4 files changed, 13 insertions(+), 3 deletions(-)
create mode 100644 Python/tier2_metainterpreter.c.h
diff --git a/.gitattributes b/.gitattributes
index 13289182400109..c26944b3387c78 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -83,6 +83,7 @@ Parser/token.c generated
Programs/test_frozenmain.h generated
Python/Python-ast.c generated
Python/generated_cases.c.h generated
+Python/tier2_metainterpreter.c.h generated
Python/opcode_targets.h generated
Python/stdlib_module_names.h generated
Tools/peg_generator/pegen/grammar_parser.py generated
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 852a7965abec95..c1f8a0c27ccce3 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -58,6 +58,8 @@ typedef struct {
// Tier 2 types meta interpreter
typedef struct _PyTier2TypeContext {
+ // points into type_stack, points to one element after the stack
+ PyTypeObject** type_stack_ptr;
int type_locals_len;
int type_stack_len;
PyTypeObject** type_stack;
diff --git a/Python/tier2.c b/Python/tier2.c
index 7aadfa8fb32939..505903e017e93e 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -49,6 +49,7 @@ initialize_type_context(const PyCodeObject *co) {
type_context->type_stack_len = nstack;
type_context->type_locals = type_locals;
type_context->type_stack = type_stack;
+ type_context->type_stack_ptr = type_stack; // init ptr at start of stack
return type_context;
}
@@ -608,8 +609,11 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
{
#define END() goto end;
#define JUMPBY(x) i += x + 1;
-#define BASIC_PUSH(v) (*stack_pointer++ = (v))
-#define BASIC_POP() (*--stack_pointer)
+#define TYPESTACK_PUSH(v) (*type_stackptr++ = v)
+#define TYPESTACK_POP() (*--type_stackptr)
+#define TYPESTACK_PEEK(i) (*(type_stackptr - 1 - i))
+#define TYPELOCALS_SET(idx, v) type_locals[idx] = v;
+#define TYPELOCALS_UNSET(idx) type_locals[idx] = NULL;
#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); \
write_i = copy_cache_entries(write_i, curr+1, caches); \
i += caches; \
@@ -634,7 +638,9 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
_PyTier2Info *t2_info = co->_tier2_info;
_Py_CODEUNIT *t2_start = (_Py_CODEUNIT *)(((char *)bb_space->u_code) + bb_space->water_level);
_Py_CODEUNIT *write_i = t2_start;
- PyTypeObject **stack_pointer = type_context_copy->type_stack;
+ PyTypeObject **type_stack = type_context_copy->type_stack;
+ PyTypeObject **type_locals = type_context_copy->type_locals;
+ PyTypeObject** type_stackptr = &type_context_copy->type_stack_ptr;
int tos = -1;
// For handling of backwards jumps
@@ -672,6 +678,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// caches = _PyOpcode_Caches[opcode];
// DISPATCH_GOTO();
// We need to rewrite the pseudo-branch instruction.
+#include "tier2_metainterpreter.c.h"
case COMPARE_AND_BRANCH:
opcode = COMPARE_OP;
DISPATCH();
diff --git a/Python/tier2_metainterpreter.c.h b/Python/tier2_metainterpreter.c.h
new file mode 100644
index 00000000000000..e69de29bb2d1d6
From 1e0116df6f28af7bef0d52dd37cea5321806b443 Mon Sep 17 00:00:00 2001
From: Julia
Date: Mon, 27 Feb 2023 21:48:24 +0800
Subject: [PATCH 045/280] Feat: Added more type annotations and localarr effect
to DSL and updated bytecodes.c
---
Python/bytecodes.c | 16 +++---
Tools/cases_generator/parser.py | 90 ++++++++++++++++++++++++++++++---
2 files changed, 92 insertions(+), 14 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 6f707d2175a3e5..ea81270e9791f8 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -102,31 +102,31 @@ dummy_func(
}
}
- inst(LOAD_CLOSURE, (-- value)) {
+ inst(LOAD_CLOSURE, (-- value : locals[oparg])) {
/* We keep LOAD_CLOSURE so that the bytecode stays more readable. */
value = GETLOCAL(oparg);
ERROR_IF(value == NULL, unbound_local_error);
Py_INCREF(value);
}
- inst(LOAD_FAST_CHECK, (-- value)) {
+ inst(LOAD_FAST_CHECK, (-- value : locals[oparg])) {
value = GETLOCAL(oparg);
ERROR_IF(value == NULL, unbound_local_error);
Py_INCREF(value);
}
- inst(LOAD_FAST, (-- value)) {
+ inst(LOAD_FAST, (-- value : locals[oparg])) {
value = GETLOCAL(oparg);
assert(value != NULL);
Py_INCREF(value);
}
- inst(LOAD_CONST, (-- value)) {
+ inst(LOAD_CONST, (-- value : consts[oparg])) {
value = GETITEM(consts, oparg);
Py_INCREF(value);
}
- inst(STORE_FAST, (value --)) {
+ inst(STORE_FAST, (value --), locals[oparg] = *value) {
SETLOCAL(oparg, value);
}
@@ -303,7 +303,7 @@ dummy_func(
bb_test = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
}
- u_inst(BINARY_OP_ADD_INT_REST, (left, right -- sum)) {
+ u_inst(BINARY_OP_ADD_INT_REST, (left : PyLong_Type, right : PyLong_Type -- sum : PyLong_Type)) {
STAT_INC(BINARY_OP, hit);
sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
@@ -1180,13 +1180,13 @@ dummy_func(
null = NULL;
}
- inst(DELETE_FAST, (--)) {
+ inst(DELETE_FAST, (--), locals[oparg] = NULL) {
PyObject *v = GETLOCAL(oparg);
ERROR_IF(v == NULL, unbound_local_error);
SETLOCAL(oparg, NULL);
}
- inst(MAKE_CELL, (--)) {
+ inst(MAKE_CELL, (--), locals[oparg] = NULL) {
// "initial" is probably NULL but not if it's an arg (or set
// via PyFrame_LocalsToFast() before MAKE_CELL has run).
PyObject *initial = GETLOCAL(oparg);
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 4381abe5579462..2d9687a90e1646 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -68,10 +68,25 @@ class Block(Node):
pass
+@dataclass
+class StackVarTypeLiteral(Node):
+ literal: str
+
+
+@dataclass
+class StackVarTypeIndex(Node):
+ array: Literal["locals"] | Literal["consts"]
+ index: str
+
+
+StackVarType = StackVarTypeLiteral | StackVarTypeIndex
+
+
@dataclass
class StackEffect(Node):
name: str
type: str = "" # Optional `:type`
+ type_annotation: StackVarType | None = None # Default is None
cond: str = "" # Optional `if (cond)`
size: str = "" # Optional `[size]`
# Note: size cannot be combined with type or cond
@@ -88,6 +103,25 @@ class CacheEffect(Node):
size: int
+@dataclass
+class LocalEffectVarLiteral(Node):
+ name: str
+
+
+@dataclass
+class LocalEffectVarStack(Node):
+ name: str
+
+
+LocalEffectVar = LocalEffectVarLiteral | LocalEffectVarStack
+
+
+@dataclass
+class LocalEffect(Node):
+ index: str
+ value: LocalEffectVar
+
+
@dataclass
class OpName(Node):
name: str
@@ -125,6 +159,7 @@ class InstHeader(Node):
name: str
inputs: list[InputEffect]
outputs: list[OutputEffect]
+ localeffect: LocalEffect | None = None
@dataclass
@@ -136,6 +171,7 @@ class InstDef(Node):
outputs: list[OutputEffect]
block: Block
u_insts: list[str]
+ localeffect: LocalEffect | None = None
@dataclass
@@ -179,7 +215,7 @@ def inst_def(self) -> InstDef | None:
space, label = m.groups()
u_insts.append(label)
return InstDef(
- hdr.register, hdr.kind, hdr.name, hdr.inputs, hdr.outputs, block, u_insts
+ hdr.register, hdr.kind, hdr.name, hdr.inputs, hdr.outputs, block, u_insts, hdr.localeffect
)
raise self.make_syntax_error("Expected block")
return None
@@ -199,6 +235,11 @@ def inst_header(self) -> InstHeader | None:
if self.expect(lx.RPAREN):
if (tkn := self.peek()) and tkn.kind == lx.LBRACE:
return InstHeader(register, kind, name, inp, outp)
+ elif self.expect(lx.COMMA):
+ leffect = self.local_effect()
+ if self.expect(lx.RPAREN):
+ if (tkn := self.peek()) and tkn.kind == lx.LBRACE:
+ return InstHeader(register, kind, name, inp, outp, leffect)
elif self.expect(lx.RPAREN) and kind == "inst":
# No legacy stack effect if kind is "op".
return InstHeader(register, "legacy", name, [], [])
@@ -266,9 +307,12 @@ def stack_effect(self) -> StackEffect | None:
# IDENTIFIER [':' IDENTIFIER] ['if' '(' expression ')']
# | IDENTIFIER '[' expression ']'
if tkn := self.expect(lx.IDENTIFIER):
- type_text = ""
+ type = ""
+ has_type_annotation = False
+ type_annotation = None
if self.expect(lx.COLON):
- type_text = self.require(lx.IDENTIFIER).text.strip()
+ has_type_annotation = True
+ type_annotation = self.stackvar_type()
cond_text = ""
if self.expect(lx.IF):
self.require(lx.LPAREN)
@@ -278,14 +322,48 @@ def stack_effect(self) -> StackEffect | None:
cond_text = cond.text.strip()
size_text = ""
if self.expect(lx.LBRACKET):
- if type_text or cond_text:
+ if has_type_annotation or cond_text:
raise self.make_syntax_error("Unexpected [")
if not (size := self.expression()):
raise self.make_syntax_error("Expected expression")
self.require(lx.RBRACKET)
- type_text = "PyObject **"
+ type = "PyObject **"
size_text = size.text.strip()
- return StackEffect(tkn.text, type_text, cond_text, size_text)
+ return StackEffect(tkn.text, type, type_annotation, cond_text, size_text)
+
+ @contextual
+ def stackvar_type(self) -> StackVarType | None:
+ if id := self.expect(lx.IDENTIFIER):
+ idstr = id.text.strip()
+ if not self.expect(lx.LBRACKET):
+ return StackVarTypeLiteral(idstr + " *")
+ if idstr not in ["locals", "consts"]: return
+ if id := self.expect(lx.IDENTIFIER):
+ index = id.text.strip()
+ self.require(lx.RBRACKET)
+ return StackVarTypeIndex(
+ "locals" if idstr == "locals" else "consts",
+ index)
+
+ @contextual
+ def local_effect(self) -> LocalEffect | None:
+ if self.expect(lx.IDENTIFIER).text.strip() == "locals":
+ self.require(lx.LBRACKET)
+ if id := self.expect(lx.IDENTIFIER):
+ index = id.text.strip()
+ self.require(lx.RBRACKET)
+ self.require(lx.EQUALS)
+ if self.expect(lx.TIMES): # stackvar
+ value = self.require(lx.IDENTIFIER).text.strip()
+ return LocalEffect(
+ index,
+ LocalEffectVarStack(value)
+ )
+ value = self.require(lx.IDENTIFIER).text.strip()
+ return LocalEffect(
+ index,
+ LocalEffectVarLiteral(value)
+ )
@contextual
def expression(self) -> Expression | None:
From 4f4a3fd22663fa31280aedbe36f04ca3d713b59d Mon Sep 17 00:00:00 2001
From: Julia
Date: Wed, 1 Mar 2023 01:10:38 +0800
Subject: [PATCH 046/280] feat: Implemented type propagation except for jump
instructions with conditional stack effect
---
Python/tier2.c | 54 +-
Python/tier2_metainterpreter.c.h | 0
Python/tier2_typepropagator.c.h | 1282 +++++++++++++++++++++++
Tools/cases_generator/generate_cases.py | 159 ++-
Tools/cases_generator/parser.py | 2 +-
5 files changed, 1478 insertions(+), 19 deletions(-)
delete mode 100644 Python/tier2_metainterpreter.c.h
create mode 100644 Python/tier2_typepropagator.c.h
diff --git a/Python/tier2.c b/Python/tier2.c
index 505903e017e93e..3e4c5ab0922ab2 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -590,6 +590,40 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
return 0;
}
+static void
+type_propagate(
+ int opcode, int oparg,
+ PyTypeObject** type_stackptr, PyTypeObject** type_locals,
+ const PyObject* consts)
+{
+#define TARGET(op) case op:
+#define TYPESTACK_PEEK(idx) (type_stackptr[-(idx)])
+#define TYPESTACK_POKE(idx, v) type_stackptr[-(idx)] = (v)
+#define TYPELOCALS_SET(idx, v) type_locals[idx] = v;
+#define TYPELOCALS_GET(idx) (type_locals[idx])
+#define TYPECONST_GET(idx) Py_TYPE(_PyTuple_CAST(consts)->ob_item[(idx)])
+#define STACK_ADJUST(idx) type_stackptr += (idx)
+#define STACK_GROW(idx) STACK_ADJUST((idx))
+#define STACK_SHRINK(idx) STACK_ADJUST(-(idx))
+
+ switch (opcode) {
+#include "tier2_typepropagator.c.h"
+ default:
+ fprintf(stderr, "Unsupported opcode in type propagator: %d\n", opcode);
+ assert(opcode);
+ }
+
+#undef TARGET
+#undef TYPESTACK_PEEK
+#undef TYPESTACK_POKE
+#undef TYPELOCALS_SET
+#undef TYPELOCALS_GET
+#undef TYPECONST_GET
+#undef STACK_ADJUST
+#undef STACK_GROW
+#undef STACK_SHRINK
+}
+
// Detects a BB from the current instruction start to the end of the first basic block it sees.
// Then emits the instructions into the bb space.
//
@@ -609,14 +643,10 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
{
#define END() goto end;
#define JUMPBY(x) i += x + 1;
-#define TYPESTACK_PUSH(v) (*type_stackptr++ = v)
-#define TYPESTACK_POP() (*--type_stackptr)
-#define TYPESTACK_PEEK(i) (*(type_stackptr - 1 - i))
-#define TYPELOCALS_SET(idx, v) type_locals[idx] = v;
-#define TYPELOCALS_UNSET(idx) type_locals[idx] = NULL;
#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); \
write_i = copy_cache_entries(write_i, curr+1, caches); \
i += caches; \
+ type_propagate(opcode, oparg, type_stackptr, type_locals, consts); \
continue;
#define DISPATCH_GOTO() goto dispatch_opcode;
@@ -636,11 +666,12 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
_PyTier2BBMetadata *temp_meta = NULL;
_PyTier2Info *t2_info = co->_tier2_info;
+ PyObject *consts = co->co_consts;
_Py_CODEUNIT *t2_start = (_Py_CODEUNIT *)(((char *)bb_space->u_code) + bb_space->water_level);
_Py_CODEUNIT *write_i = t2_start;
PyTypeObject **type_stack = type_context_copy->type_stack;
PyTypeObject **type_locals = type_context_copy->type_locals;
- PyTypeObject** type_stackptr = &type_context_copy->type_stack_ptr;
+ PyTypeObject **type_stackptr = type_context_copy->type_stack_ptr;
int tos = -1;
// For handling of backwards jumps
@@ -668,17 +699,6 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
case RESUME:
opcode = RESUME_QUICK;
DISPATCH();
- //case EXTENDED_ARG:
- // write_i = emit_i(write_i, EXTENDED_ARG, _Py_OPARG(*curr));
- // curr++;
- // next_instr++;
- // i++;
- // oparg = oparg << 8 | _Py_OPARG(*curr);
- // opcode = _Py_OPCODE(*curr);
- // caches = _PyOpcode_Caches[opcode];
- // DISPATCH_GOTO();
- // We need to rewrite the pseudo-branch instruction.
-#include "tier2_metainterpreter.c.h"
case COMPARE_AND_BRANCH:
opcode = COMPARE_OP;
DISPATCH();
diff --git a/Python/tier2_metainterpreter.c.h b/Python/tier2_metainterpreter.c.h
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
new file mode 100644
index 00000000000000..fb7ee319f35d83
--- /dev/null
+++ b/Python/tier2_typepropagator.c.h
@@ -0,0 +1,1282 @@
+// This file is generated by Tools\cases_generator\generate_cases.py @TODO: make this a seperate argument
+// from Python\bytecodes.c
+// Do not edit!
+
+ TARGET(NOP) {
+ break;
+ }
+
+ TARGET(RESUME) {
+ break;
+ }
+
+ TARGET(RESUME_QUICK) {
+ break;
+ }
+
+ TARGET(LOAD_CLOSURE) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, TYPELOCALS_GET(oparg));
+ break;
+ }
+
+ TARGET(LOAD_FAST_CHECK) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, TYPELOCALS_GET(oparg));
+ break;
+ }
+
+ TARGET(LOAD_FAST) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, TYPELOCALS_GET(oparg));
+ break;
+ }
+
+ TARGET(LOAD_CONST) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, TYPECONST_GET(oparg));
+ break;
+ }
+
+ TARGET(STORE_FAST) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ TYPELOCALS_SET(oparg, value)
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(POP_TOP) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(PUSH_NULL) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(END_FOR) {
+ {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ }
+ {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ }
+ break;
+ }
+
+ TARGET(UNARY_NEGATIVE) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(UNARY_NOT) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(UNARY_INVERT) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_OP_MULTIPLY_INT) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_OP_MULTIPLY_FLOAT) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_OP_SUBTRACT_INT) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_OP_SUBTRACT_FLOAT) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_UNICODE) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_OP_INPLACE_ADD_UNICODE) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_FLOAT) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_INT) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_CHECK_INT) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ TYPESTACK_POKE(1, &PyLong_Type);
+ TYPESTACK_POKE(2, &PyLong_Type);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_INT_REST) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, &PyLong_Type);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR) {
+ PyTypeObject *sub = TYPESTACK_PEEK(1);
+ PyTypeObject *container = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_SLICE) {
+ PyTypeObject *stop = TYPESTACK_PEEK(1);
+ PyTypeObject *start = TYPESTACK_PEEK(2);
+ PyTypeObject *container = TYPESTACK_PEEK(3);
+ STACK_SHRINK(2);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(STORE_SLICE) {
+ PyTypeObject *stop = TYPESTACK_PEEK(1);
+ PyTypeObject *start = TYPESTACK_PEEK(2);
+ PyTypeObject *container = TYPESTACK_PEEK(3);
+ PyTypeObject *v = TYPESTACK_PEEK(4);
+ STACK_SHRINK(4);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR_LIST_INT) {
+ PyTypeObject *sub = TYPESTACK_PEEK(1);
+ PyTypeObject *list = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR_TUPLE_INT) {
+ PyTypeObject *sub = TYPESTACK_PEEK(1);
+ PyTypeObject *tuple = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR_DICT) {
+ PyTypeObject *sub = TYPESTACK_PEEK(1);
+ PyTypeObject *dict = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR_GETITEM) {
+ PyTypeObject *sub = TYPESTACK_PEEK(1);
+ PyTypeObject *container = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(LIST_APPEND) {
+ PyTypeObject *v = TYPESTACK_PEEK(1);
+ PyTypeObject *list = TYPESTACK_PEEK(2 + (oparg-1));
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(SET_ADD) {
+ PyTypeObject *v = TYPESTACK_PEEK(1);
+ PyTypeObject *set = TYPESTACK_PEEK(2 + (oparg-1));
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(STORE_SUBSCR) {
+ PyTypeObject *sub = TYPESTACK_PEEK(1);
+ PyTypeObject *container = TYPESTACK_PEEK(2);
+ PyTypeObject *v = TYPESTACK_PEEK(3);
+ STACK_SHRINK(3);
+ break;
+ }
+
+ TARGET(STORE_SUBSCR_LIST_INT) {
+ PyTypeObject *sub = TYPESTACK_PEEK(1);
+ PyTypeObject *list = TYPESTACK_PEEK(2);
+ PyTypeObject *value = TYPESTACK_PEEK(3);
+ STACK_SHRINK(3);
+ break;
+ }
+
+ TARGET(STORE_SUBSCR_DICT) {
+ PyTypeObject *sub = TYPESTACK_PEEK(1);
+ PyTypeObject *dict = TYPESTACK_PEEK(2);
+ PyTypeObject *value = TYPESTACK_PEEK(3);
+ STACK_SHRINK(3);
+ break;
+ }
+
+ TARGET(DELETE_SUBSCR) {
+ PyTypeObject *sub = TYPESTACK_PEEK(1);
+ PyTypeObject *container = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(CALL_INTRINSIC_1) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_INTRINSIC_2) {
+ PyTypeObject *value1 = TYPESTACK_PEEK(1);
+ PyTypeObject *value2 = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(RAISE_VARARGS) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ STACK_SHRINK(oparg);
+ break;
+ }
+
+ TARGET(INTERPRETER_EXIT) {
+ PyTypeObject *retval = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(RETURN_VALUE) {
+ PyTypeObject *retval = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(RETURN_CONST) {
+ break;
+ }
+
+ TARGET(GET_AITER) {
+ PyTypeObject *obj = TYPESTACK_PEEK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(GET_ANEXT) {
+ PyTypeObject *aiter = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(GET_AWAITABLE) {
+ PyTypeObject *iterable = TYPESTACK_PEEK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(SEND) {
+ PyTypeObject *v = TYPESTACK_PEEK(1);
+ PyTypeObject *receiver = TYPESTACK_PEEK(2);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(SEND_GEN) {
+ PyTypeObject *v = TYPESTACK_PEEK(1);
+ PyTypeObject *receiver = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(YIELD_VALUE) {
+ PyTypeObject *retval = TYPESTACK_PEEK(1);
+ break;
+ }
+
+ TARGET(POP_EXCEPT) {
+ PyTypeObject *exc_value = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(RERAISE) {
+ PyTypeObject *exc = TYPESTACK_PEEK(1);
+ PyTypeObject **values = &TYPESTACK_PEEK(1 + oparg);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(END_ASYNC_FOR) {
+ PyTypeObject *exc = TYPESTACK_PEEK(1);
+ PyTypeObject *awaitable = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(CLEANUP_THROW) {
+ PyTypeObject *exc_value = TYPESTACK_PEEK(1);
+ PyTypeObject *last_sent_val = TYPESTACK_PEEK(2);
+ PyTypeObject *sub_iter = TYPESTACK_PEEK(3);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(2, NULL);
+ break;
+ }
+
+ TARGET(LOAD_ASSERTION_ERROR) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(LOAD_BUILD_CLASS) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(STORE_NAME) {
+ PyTypeObject *v = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(DELETE_NAME) {
+ break;
+ }
+
+ TARGET(UNPACK_SEQUENCE) {
+ PyTypeObject *seq = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ STACK_GROW(oparg);
+ break;
+ }
+
+ TARGET(UNPACK_SEQUENCE_TWO_TUPLE) {
+ PyTypeObject *seq = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ STACK_GROW(oparg);
+ TYPESTACK_POKE(oparg, NULL);
+ break;
+ }
+
+ TARGET(UNPACK_SEQUENCE_TUPLE) {
+ PyTypeObject *seq = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ STACK_GROW(oparg);
+ TYPESTACK_POKE(oparg, NULL);
+ break;
+ }
+
+ TARGET(UNPACK_SEQUENCE_LIST) {
+ PyTypeObject *seq = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ STACK_GROW(oparg);
+ TYPESTACK_POKE(oparg, NULL);
+ break;
+ }
+
+ TARGET(UNPACK_EX) {
+ PyTypeObject *seq = TYPESTACK_PEEK(1);
+ STACK_GROW((oparg & 0xFF) + (oparg >> 8));
+ break;
+ }
+
+ TARGET(STORE_ATTR) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ PyTypeObject *v = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(DELETE_ATTR) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(STORE_GLOBAL) {
+ PyTypeObject *v = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(DELETE_GLOBAL) {
+ break;
+ }
+
+ TARGET(LOAD_NAME) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(LOAD_GLOBAL) {
+ STACK_GROW(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_GLOBAL_MODULE) {
+ STACK_GROW(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_GLOBAL_BUILTIN) {
+ STACK_GROW(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(DELETE_FAST) {
+ TYPELOCALS_SET(oparg, NULL)
+ break;
+ }
+
+ TARGET(MAKE_CELL) {
+ TYPELOCALS_SET(oparg, NULL)
+ break;
+ }
+
+ TARGET(DELETE_DEREF) {
+ break;
+ }
+
+ TARGET(LOAD_CLASSDEREF) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(LOAD_DEREF) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(STORE_DEREF) {
+ PyTypeObject *v = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(COPY_FREE_VARS) {
+ break;
+ }
+
+ TARGET(BUILD_STRING) {
+ PyTypeObject **pieces = &TYPESTACK_PEEK(oparg);
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BUILD_TUPLE) {
+ PyTypeObject **values = &TYPESTACK_PEEK(oparg);
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BUILD_LIST) {
+ PyTypeObject **values = &TYPESTACK_PEEK(oparg);
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(LIST_EXTEND) {
+ PyTypeObject *iterable = TYPESTACK_PEEK(1);
+ PyTypeObject *list = TYPESTACK_PEEK(2 + (oparg-1));
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(SET_UPDATE) {
+ PyTypeObject *iterable = TYPESTACK_PEEK(1);
+ PyTypeObject *set = TYPESTACK_PEEK(2 + (oparg-1));
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BUILD_SET) {
+ PyTypeObject **values = &TYPESTACK_PEEK(oparg);
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BUILD_MAP) {
+ PyTypeObject **values = &TYPESTACK_PEEK(oparg*2);
+ STACK_SHRINK(oparg*2);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(SETUP_ANNOTATIONS) {
+ break;
+ }
+
+ TARGET(BUILD_CONST_KEY_MAP) {
+ PyTypeObject *keys = TYPESTACK_PEEK(1);
+ PyTypeObject **values = &TYPESTACK_PEEK(1 + oparg);
+ STACK_SHRINK(oparg);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(DICT_UPDATE) {
+ PyTypeObject *update = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(DICT_MERGE) {
+ PyTypeObject *update = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(MAP_ADD) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ PyTypeObject *key = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(LOAD_ATTR) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_INSTANCE_VALUE) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_MODULE) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_WITH_HINT) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_SLOT) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_CLASS) {
+ PyTypeObject *cls = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_PROPERTY) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ break;
+ }
+
+ TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ break;
+ }
+
+ TARGET(STORE_ATTR_INSTANCE_VALUE) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ PyTypeObject *value = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(STORE_ATTR_WITH_HINT) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ PyTypeObject *value = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(STORE_ATTR_SLOT) {
+ PyTypeObject *owner = TYPESTACK_PEEK(1);
+ PyTypeObject *value = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(COMPARE_OP) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(COMPARE_AND_BRANCH) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(COMPARE_AND_BRANCH_FLOAT) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(COMPARE_AND_BRANCH_INT) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(COMPARE_AND_BRANCH_STR) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(IS_OP) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CONTAINS_OP) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CHECK_EG_MATCH) {
+ PyTypeObject *match_type = TYPESTACK_PEEK(1);
+ PyTypeObject *exc_value = TYPESTACK_PEEK(2);
+ TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(2, NULL);
+ break;
+ }
+
+ TARGET(CHECK_EXC_MATCH) {
+ PyTypeObject *right = TYPESTACK_PEEK(1);
+ PyTypeObject *left = TYPESTACK_PEEK(2);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(IMPORT_NAME) {
+ PyTypeObject *fromlist = TYPESTACK_PEEK(1);
+ PyTypeObject *level = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(IMPORT_FROM) {
+ PyTypeObject *from = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(JUMP_FORWARD) {
+ break;
+ }
+
+ TARGET(JUMP_BACKWARD) {
+ break;
+ }
+
+ TARGET(JUMP_BACKWARD_QUICK) {
+ break;
+ }
+
+ TARGET(POP_JUMP_IF_FALSE) {
+ PyTypeObject *cond = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BB_TEST_POP_IF_FALSE) {
+ PyTypeObject *cond = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(POP_JUMP_IF_TRUE) {
+ PyTypeObject *cond = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BB_TEST_POP_IF_TRUE) {
+ PyTypeObject *cond = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(POP_JUMP_IF_NOT_NONE) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BB_TEST_POP_IF_NOT_NONE) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(POP_JUMP_IF_NONE) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BB_TEST_POP_IF_NONE) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(JUMP_IF_FALSE_OR_POP) {
+ PyTypeObject *cond = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ STACK_GROW((jump ? 1 : 0));
+ break;
+ }
+
+ TARGET(BB_TEST_IF_FALSE_OR_POP) {
+ PyTypeObject *cond = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ STACK_GROW((jump ? 1 : 0));
+ break;
+ }
+
+ TARGET(JUMP_IF_TRUE_OR_POP) {
+ PyTypeObject *cond = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ STACK_GROW((jump ? 1 : 0));
+ break;
+ }
+
+ TARGET(BB_TEST_IF_TRUE_OR_POP) {
+ PyTypeObject *cond = TYPESTACK_PEEK(1);
+ STACK_SHRINK(1);
+ STACK_GROW((jump ? 1 : 0));
+ break;
+ }
+
+ TARGET(JUMP_BACKWARD_NO_INTERRUPT) {
+ break;
+ }
+
+ TARGET(GET_LEN) {
+ PyTypeObject *obj = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(MATCH_CLASS) {
+ PyTypeObject *names = TYPESTACK_PEEK(1);
+ PyTypeObject *type = TYPESTACK_PEEK(2);
+ PyTypeObject *subject = TYPESTACK_PEEK(3);
+ STACK_SHRINK(2);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(MATCH_MAPPING) {
+ PyTypeObject *subject = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(MATCH_SEQUENCE) {
+ PyTypeObject *subject = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(MATCH_KEYS) {
+ PyTypeObject *keys = TYPESTACK_PEEK(1);
+ PyTypeObject *subject = TYPESTACK_PEEK(2);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(GET_ITER) {
+ PyTypeObject *iterable = TYPESTACK_PEEK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(GET_YIELD_FROM_ITER) {
+ PyTypeObject *iterable = TYPESTACK_PEEK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(FOR_ITER) {
+ PyTypeObject *iter = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BB_TEST_ITER) {
+ PyTypeObject *iter = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(FOR_ITER_LIST) {
+ PyTypeObject *iter = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(FOR_ITER_TUPLE) {
+ PyTypeObject *iter = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(FOR_ITER_RANGE) {
+ PyTypeObject *iter = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(FOR_ITER_GEN) {
+ PyTypeObject *iter = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ break;
+ }
+
+ TARGET(BEFORE_ASYNC_WITH) {
+ PyTypeObject *mgr = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(2, NULL);
+ break;
+ }
+
+ TARGET(BEFORE_WITH) {
+ PyTypeObject *mgr = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(2, NULL);
+ break;
+ }
+
+ TARGET(WITH_EXCEPT_START) {
+ PyTypeObject *val = TYPESTACK_PEEK(1);
+ PyTypeObject *lasti = TYPESTACK_PEEK(3);
+ PyTypeObject *exit_func = TYPESTACK_PEEK(4);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(PUSH_EXC_INFO) {
+ PyTypeObject *new_exc = TYPESTACK_PEEK(1);
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, new_exc);
+ TYPESTACK_POKE(2, NULL);
+ break;
+ }
+
+ TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
+ PyTypeObject *self = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_METHOD_NO_DICT) {
+ PyTypeObject *self = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_METHOD_LAZY_DICT) {
+ PyTypeObject *self = TYPESTACK_PEEK(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ break;
+ }
+
+ TARGET(KW_NAMES) {
+ break;
+ }
+
+ TARGET(CALL) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_BOUND_METHOD_EXACT_ARGS) {
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(CALL_PY_EXACT_ARGS) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(CALL_PY_WITH_DEFAULTS) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_TYPE_1) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *null = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_STR_1) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *null = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_TUPLE_1) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *null = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_BUILTIN_CLASS) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_BUILTIN_O) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_BUILTIN_FAST) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_LEN) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_ISINSTANCE) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_LIST_APPEND) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *self = TYPESTACK_PEEK(1 + oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) {
+ PyTypeObject **args = &TYPESTACK_PEEK(oparg);
+ PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(CALL_FUNCTION_EX) {
+ PyTypeObject *kwargs = (oparg & 1) ? TYPESTACK_PEEK(((oparg & 1) ? 1 : 0)) : NULL;
+ PyTypeObject *callargs = TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0));
+ PyTypeObject *func = TYPESTACK_PEEK(2 + ((oparg & 1) ? 1 : 0));
+ STACK_SHRINK(((oparg & 1) ? 1 : 0));
+ STACK_SHRINK(2);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(MAKE_FUNCTION) {
+ PyTypeObject *codeobj = TYPESTACK_PEEK(1);
+ PyTypeObject *closure = (oparg & 0x08) ? TYPESTACK_PEEK(1 + ((oparg & 0x08) ? 1 : 0)) : NULL;
+ PyTypeObject *annotations = (oparg & 0x04) ? TYPESTACK_PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0)) : NULL;
+ PyTypeObject *kwdefaults = (oparg & 0x02) ? TYPESTACK_PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0)) : NULL;
+ PyTypeObject *defaults = (oparg & 0x01) ? TYPESTACK_PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x01) ? 1 : 0)) : NULL;
+ STACK_SHRINK(((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(RETURN_GENERATOR) {
+ break;
+ }
+
+ TARGET(BUILD_SLICE) {
+ PyTypeObject *step = (oparg == 3) ? TYPESTACK_PEEK(((oparg == 3) ? 1 : 0)) : NULL;
+ PyTypeObject *stop = TYPESTACK_PEEK(1 + ((oparg == 3) ? 1 : 0));
+ PyTypeObject *start = TYPESTACK_PEEK(2 + ((oparg == 3) ? 1 : 0));
+ STACK_SHRINK(((oparg == 3) ? 1 : 0));
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(FORMAT_VALUE) {
+ PyTypeObject *fmt_spec = ((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? TYPESTACK_PEEK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)) : NULL;
+ PyTypeObject *value = TYPESTACK_PEEK(1 + (((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0));
+ STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0));
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(COPY) {
+ PyTypeObject *bottom = TYPESTACK_PEEK(1 + (oparg-1));
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(BINARY_OP) {
+ PyTypeObject *rhs = TYPESTACK_PEEK(1);
+ PyTypeObject *lhs = TYPESTACK_PEEK(2);
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, NULL);
+ break;
+ }
+
+ TARGET(SWAP) {
+ PyTypeObject *top = TYPESTACK_PEEK(1);
+ PyTypeObject *bottom = TYPESTACK_PEEK(2 + (oparg-2));
+ TYPESTACK_POKE(1, bottom);
+ TYPESTACK_POKE(2 + (oparg-2), top);
+ break;
+ }
+
+ TARGET(EXTENDED_ARG) {
+ break;
+ }
+
+ TARGET(CACHE) {
+ break;
+ }
+
+ TARGET(BB_BRANCH) {
+ break;
+ }
+
+ TARGET(BB_BRANCH_IF_FLAG_UNSET) {
+ break;
+ }
+
+ TARGET(BB_JUMP_IF_FLAG_UNSET) {
+ break;
+ }
+
+ TARGET(BB_BRANCH_IF_FLAG_SET) {
+ break;
+ }
+
+ TARGET(BB_JUMP_IF_FLAG_SET) {
+ break;
+ }
+
+ TARGET(BB_JUMP_BACKWARD_LAZY) {
+ break;
+ }
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index ea240ee692adb7..479ee2a8977b3f 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -16,7 +16,8 @@
from enum import Enum, auto
import parser
-from parser import StackEffect
+from parser import StackEffect, StackVarTypeLiteral, StackVarTypeIndex
+from parser import LocalEffect, LocalEffectVarLiteral, LocalEffectVarStack
HERE = os.path.dirname(__file__)
ROOT = os.path.join(HERE, "../..")
@@ -35,6 +36,11 @@
os.path.join(ROOT, "Include/internal/pycore_opcode_macro_to_micro.h")
)
+# Tier 2 type propagator
+TIER2_TYPE_PROPAGATOR_OUTPUT = os.path.relpath(
+ os.path.join(ROOT, "Python/tier2_typepropagator.c.h")
+)
+
BEGIN_MARKER = "// BEGIN BYTECODES //"
END_MARKER = "// END BYTECODES //"
RE_PREDICTED = (
@@ -229,6 +235,7 @@ class Instruction:
cache_effects: list[parser.CacheEffect]
input_effects: list[StackEffect]
output_effects: list[StackEffect]
+ local_effects: LocalEffect | None
unmoved_names: frozenset[str]
instr_fmt: str
@@ -257,6 +264,7 @@ def __init__(self, inst: parser.InstDef):
effect for effect in inst.inputs if isinstance(effect, StackEffect)
]
self.output_effects = inst.outputs # For consistency/completeness
+ self.local_effects = inst.localeffect
unmoved_names: set[str] = set()
for ieffect, oeffect in zip(self.input_effects, self.output_effects):
if ieffect.name == oeffect.name:
@@ -294,6 +302,95 @@ def analyze_registers(self, a: "Analyzer") -> None:
f"Instruction {self.name} has too many register effects", node=self.inst
)
+ def write_typeprop(self, out: Formatter) -> None:
+ """Write one instruction's type propagation rules"""
+
+ # Write input stack effect variable declarations and initializations
+ ieffects = list(reversed(self.input_effects))
+ usable_for_local_effect = {}
+ all_input_effect_names = {}
+ for i, ieffect in enumerate(ieffects):
+ isize = string_effect_size(
+ list_effect_size([ieff for ieff in ieffects[: i + 1]])
+ )
+ all_input_effect_names[ieffect.name] = (ieffect, i)
+ dst = StackEffect(ieffect.name, "PyTypeObject *")
+ if ieffect.size:
+ src = StackEffect(f"&TYPESTACK_PEEK({isize})", "PyTypeObject **")
+ dst = StackEffect(ieffect.name, "PyTypeObject **")
+ elif ieffect.cond:
+ src = StackEffect(f"({ieffect.cond}) ? TYPESTACK_PEEK({isize}) : NULL", "PyTypeObject *")
+ else:
+ usable_for_local_effect[ieffect.name] = ieffect
+ src = StackEffect(f"TYPESTACK_PEEK({isize})", "PyTypeObject *")
+ out.declare(dst, src)
+
+ # Write localarr effect
+ if self.local_effects:
+ idx = self.local_effects.index
+ val = self.local_effects.value
+ match val:
+ case LocalEffectVarLiteral(name=valstr):
+ if valstr != "NULL": valstr = f"&{valstr}"
+ case LocalEffectVarStack(name=valstr):
+ assert valstr in usable_for_local_effect, \
+ "`cond` and `size` stackvar not supported for localeffect"
+ case _:
+ typing.assert_never(val)
+ out.emit(f"TYPELOCALS_SET({idx}, {valstr})")
+
+ # Update stack size
+ out.stack_adjust(
+ 0,
+ [ieff for ieff in self.input_effects],
+ [oeff for oeff in self.output_effects],
+ )
+
+ # Stack effect
+ oeffects = list(reversed(self.output_effects))
+ for i, oeffect in enumerate(oeffects):
+ osize = string_effect_size(
+ list_effect_size([oeff for oeff in oeffects[: i + 1]])
+ )
+
+ # Check if it's even used
+ if oeffect.name == UNUSED: continue
+
+ # Check if there's type info
+ if typ := oeffect.type_annotation:
+ match typ:
+ case StackVarTypeLiteral(literal=val):
+ if val != "NULL": val = f"&{val}"
+ case StackVarTypeIndex(array=arr, index=idx):
+ val = f"{'TYPELOCALS_GET' if arr == 'locals' else 'TYPECONST_GET'}({idx})"
+ case _:
+ typing.assert_never(typ)
+ if oeffect.cond:
+ out.emit(f"if ({oeffect.cond}) {{ TYPESTACK_POKE({osize}, {val}); }}")
+ else:
+ out.emit(f"TYPESTACK_POKE({osize}, {val});")
+ continue
+
+ # Check if it's part of input effect
+ # TODO: Can we assume that they have the same type?
+ if oeffect.name in all_input_effect_names:
+ ieffect, j = all_input_effect_names[oeffect.name]
+ assert not ieffect.cond, \
+ "`cond` stackvar not supported for type prop"
+ # The stack var stays at the same pos
+ if len(oeffects) - i == len(ieffects) - j: continue
+ if oeffect.cond:
+ out.emit(f"if ({oeffect.cond}) {{ TYPESTACK_POKE({osize}, {oeffect.name}); }}")
+ else:
+ out.emit(f"TYPESTACK_POKE({osize}, {oeffect.name});")
+ continue
+
+ # Just output null
+ if oeffect.cond:
+ out.emit(f"if ({oeffect.cond}) {{ TYPESTACK_POKE({osize}, NULL); }}")
+ else:
+ out.emit(f"TYPESTACK_POKE({osize}, NULL);")
+
def write(self, out: Formatter) -> None:
"""Write one instruction, sans prologue and epilogue."""
# Write a static assertion that a family's cache size is correct
@@ -483,6 +580,10 @@ def write_body(self, out: Formatter, cache_adjust: int) -> None:
for var, oeffect in self.output_mapping:
out.assign(var, oeffect)
+ def write_typeprop(self, out: Formatter) -> None:
+ with out.block(""):
+ self.instr.write_typeprop(out)
+
@dataclasses.dataclass
class SuperOrMacroInstruction:
@@ -1019,6 +1120,33 @@ def write_uopguard_typedata(self):
self.out.emit(f"[{name}] = {{{', '.join(['&' + type_ for type_ in types])}}},")
self.out.emit("};")
+
+ def write_typepropagator(self) -> None:
+ """Write the type propagator"""
+
+ with open(self.output_filename, "w") as f:
+ # Write provenance header
+ f.write(f"// This file is generated by {THIS} @TODO: make this a seperate argument\n")
+ f.write(f"// from {os.path.relpath(self.filename, ROOT).replace(os.path.sep, posixpath.sep)}\n")
+ f.write(f"// Do not edit!\n")
+
+ # Create formatter
+ self.out = Formatter(f, 8)
+
+ for thing in self.everything:
+ match thing:
+ case parser.InstDef(kind=kind, name=name):
+ match kind:
+ case "op": pass
+ case _:
+ self.write_instr_typeprop(self.instrs[name])
+ case parser.Super(name=name):
+ self.write_super_typeprop(self.super_instrs[name])
+ case parser.Macro(name=name):
+ self.write_macro_typeprop(self.macro_instrs[name])
+ case _:
+ typing.assert_never(thing)
+
def write_metadata(self) -> None:
"""Write instruction metadata to output file."""
@@ -1237,6 +1365,31 @@ def write_macro(self, mac: MacroInstruction) -> None:
f'{cache_adjust}, "incorrect cache size");'
)
+ def write_instr_typeprop(self, instr: Instruction) -> None:
+ name = instr.name
+ self.out.emit("")
+ with self.out.block(f"TARGET({name})"):
+ instr.write_typeprop(self.out)
+ self.out.emit("break;")
+
+ def write_super_typeprop(self, sup: SuperInstruction) -> None:
+ # TODO: Support super instructions
+ # Currently not support because of the need for NEXTOPARG
+ ...
+
+ def write_macro_typeprop(self, mac: MacroInstruction) -> None:
+ # TODO: Make the code emitted more efficient by
+ # combining stack effect
+ name = mac.name
+ self.out.emit("")
+ with self.out.block(f"TARGET({name})"):
+ for comp in mac.parts:
+ if not isinstance(comp, Component): continue
+ comp.write_typeprop(self.out)
+ self.out.emit("break;")
+
+
+
@contextlib.contextmanager
def wrap_super_or_macro(self, up: SuperOrMacroInstruction):
"""Shared boilerplate for super- and macro instructions."""
@@ -1342,6 +1495,10 @@ def main():
# a.output_filename = TIER2_MACRO_TO_MICRO_MAP_OUTPUT
# a.write_macromap_and_typedata()
+ # Quick hack. @TODO refactor
+ a.output_filename = TIER2_TYPE_PROPAGATOR_OUTPUT
+ a.write_typepropagator()
+
if __name__ == "__main__":
main()
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 2d9687a90e1646..14adf59640b1dd 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -336,7 +336,7 @@ def stackvar_type(self) -> StackVarType | None:
if id := self.expect(lx.IDENTIFIER):
idstr = id.text.strip()
if not self.expect(lx.LBRACKET):
- return StackVarTypeLiteral(idstr + " *")
+ return StackVarTypeLiteral(idstr)
if idstr not in ["locals", "consts"]: return
if id := self.expect(lx.IDENTIFIER):
index = id.text.strip()
From 19b80a81c0b04a8ba2b9376ace35eec9055d159f Mon Sep 17 00:00:00 2001
From: Julia
Date: Wed, 1 Mar 2023 12:42:43 +0800
Subject: [PATCH 047/280] Refactor: Moved type_propagate to be with the
_PyTier2TypeContext code
In preparation for type propagating within the tier2 branch handling code
---
Python/tier2.c | 69 +++++++++++++++++++++++++-------------------------
1 file changed, 35 insertions(+), 34 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 3e4c5ab0922ab2..e5d0d831c4fd12 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -92,6 +92,41 @@ _PyTier2TypeContext_free(_PyTier2TypeContext* type_context) {
PyMem_Free(type_context);
}
+// Type propagates across a single function.
+static void
+type_propagate(
+ int opcode, int oparg,
+ PyTypeObject** type_stackptr, PyTypeObject** type_locals,
+ const PyObject* consts)
+{
+#define TARGET(op) case op:
+#define TYPESTACK_PEEK(idx) (type_stackptr[-(idx)])
+#define TYPESTACK_POKE(idx, v) type_stackptr[-(idx)] = (v)
+#define TYPELOCALS_SET(idx, v) type_locals[idx] = v;
+#define TYPELOCALS_GET(idx) (type_locals[idx])
+#define TYPECONST_GET(idx) Py_TYPE(_PyTuple_CAST(consts)->ob_item[(idx)])
+#define STACK_ADJUST(idx) type_stackptr += (idx)
+#define STACK_GROW(idx) STACK_ADJUST((idx))
+#define STACK_SHRINK(idx) STACK_ADJUST(-(idx))
+
+ switch (opcode) {
+#include "tier2_typepropagator.c.h"
+ default:
+ fprintf(stderr, "Unsupported opcode in type propagator: %d\n", opcode);
+ assert(opcode);
+ }
+
+#undef TARGET
+#undef TYPESTACK_PEEK
+#undef TYPESTACK_POKE
+#undef TYPELOCALS_SET
+#undef TYPELOCALS_GET
+#undef TYPECONST_GET
+#undef STACK_ADJUST
+#undef STACK_GROW
+#undef STACK_SHRINK
+}
+
////////// Utility functions
// Gets end of the bytecode for a code object.
@@ -590,40 +625,6 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
return 0;
}
-static void
-type_propagate(
- int opcode, int oparg,
- PyTypeObject** type_stackptr, PyTypeObject** type_locals,
- const PyObject* consts)
-{
-#define TARGET(op) case op:
-#define TYPESTACK_PEEK(idx) (type_stackptr[-(idx)])
-#define TYPESTACK_POKE(idx, v) type_stackptr[-(idx)] = (v)
-#define TYPELOCALS_SET(idx, v) type_locals[idx] = v;
-#define TYPELOCALS_GET(idx) (type_locals[idx])
-#define TYPECONST_GET(idx) Py_TYPE(_PyTuple_CAST(consts)->ob_item[(idx)])
-#define STACK_ADJUST(idx) type_stackptr += (idx)
-#define STACK_GROW(idx) STACK_ADJUST((idx))
-#define STACK_SHRINK(idx) STACK_ADJUST(-(idx))
-
- switch (opcode) {
-#include "tier2_typepropagator.c.h"
- default:
- fprintf(stderr, "Unsupported opcode in type propagator: %d\n", opcode);
- assert(opcode);
- }
-
-#undef TARGET
-#undef TYPESTACK_PEEK
-#undef TYPESTACK_POKE
-#undef TYPELOCALS_SET
-#undef TYPELOCALS_GET
-#undef TYPECONST_GET
-#undef STACK_ADJUST
-#undef STACK_GROW
-#undef STACK_SHRINK
-}
-
// Detects a BB from the current instruction start to the end of the first basic block it sees.
// Then emits the instructions into the bb space.
//
From a349f1606716353bb5cf772c76ed99f5efc722d0 Mon Sep 17 00:00:00 2001
From: Julia
Date: Wed, 1 Mar 2023 23:53:12 +0800
Subject: [PATCH 048/280] Refactor: Added tier2_typepropagator.c.h as generated
in .gitattributes
---
.gitattributes | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.gitattributes b/.gitattributes
index c26944b3387c78..35d81d575bf1e1 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -83,7 +83,7 @@ Parser/token.c generated
Programs/test_frozenmain.h generated
Python/Python-ast.c generated
Python/generated_cases.c.h generated
-Python/tier2_metainterpreter.c.h generated
+Python/tier2_typepropagator.c.h generated
Python/opcode_targets.h generated
Python/stdlib_module_names.h generated
Tools/peg_generator/pegen/grammar_parser.py generated
From 1b2d4a428b421c4e6e93e8972321ec5582b480ac Mon Sep 17 00:00:00 2001
From: Julia
Date: Thu, 2 Mar 2023 00:02:11 +0800
Subject: [PATCH 049/280] Feat: Handled type propagation across the tier2
branching logic
---
Include/internal/pycore_code.h | 2 +-
Python/bytecodes.c | 22 ++++-
Python/ceval.c | 5 ++
Python/generated_cases.c.h | 20 ++++-
Python/tier2.c | 107 +++++++++++++++++++-----
Python/tier2_typepropagator.c.h | 20 ++---
Tools/cases_generator/generate_cases.py | 16 ++++
7 files changed, 151 insertions(+), 41 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 914c0c03e95c9b..5ca88116a31b42 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -259,7 +259,7 @@ extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *,
_Py_CODEUNIT *);
extern _Py_CODEUNIT *_PyTier2_GenerateNextBB(
struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
- _Py_CODEUNIT **tier1_fallback);
+ _Py_CODEUNIT **tier1_fallback, char gen_bb_requires_pop);
extern _Py_CODEUNIT *_PyTier2_LocateJumpBackwardsBB(
struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
_Py_CODEUNIT **tier1_fallback);
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index ea81270e9791f8..bdd5173dc68fb2 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2084,6 +2084,10 @@ dummy_func(
goto error;
}
}
+ // This gets set so BRANCH_BB knows whether to pop
+ // the type stack (type propagation) when generating the
+ // target BB
+ gen_bb_requires_pop = jump;
}
inst(JUMP_IF_TRUE_OR_POP, (cond -- cond if (jump))) {
@@ -2136,6 +2140,10 @@ dummy_func(
goto error;
}
}
+ // This gets set so BRANCH_BB knows whether to pop
+ // the type stack (type propagation) when generating the
+ // target BB
+ gen_bb_requires_pop = jump;
}
inst(JUMP_BACKWARD_NO_INTERRUPT, (--)) {
@@ -3284,6 +3292,7 @@ dummy_func(
}
// Tier 2 instructions
+ // Type propagator assumes this doesn't affect type context
inst(BB_BRANCH, (unused/1 --)) {
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
@@ -3293,7 +3302,8 @@ dummy_func(
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_UNSET);
// Generate consequent.
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, 0, &tier1_fallback);
+ frame, cache->bb_id, 0, &tier1_fallback, gen_bb_requires_pop);
+ gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -3305,7 +3315,8 @@ dummy_func(
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_SET);
// Generate predicate.
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback);
+ frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback + oparg;
@@ -3327,7 +3338,8 @@ dummy_func(
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback);
+ frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -3358,7 +3370,8 @@ dummy_func(
// @TODO: Rewrite TEST intruction above to a JUMP above..
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback);
+ frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -3379,6 +3392,7 @@ dummy_func(
// Fall through to next instruction.
}
+ // Type propagator assumes this doesn't affect type context
inst(BB_JUMP_BACKWARD_LAZY, (--)) {
_Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
diff --git a/Python/ceval.c b/Python/ceval.c
index 7d0f7a8813411f..9f30fbefb92cd8 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -729,6 +729,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
// true = successor
// false = alternate
bool bb_test = true;
+ // For tier2 type propagation, handling of jump instructions with
+ // runtime-dependent stack effect.
+ // This flag is used to determine if the type context of a new bb
+ // requires a stack element to be popped.
+ bool gen_bb_requires_pop = false;
/* WARNING: Because the _PyCFrame lives on the C stack,
* but can be accessed from a heap allocated object (tstate)
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 2f7d223618a6ac..ba0aa72c68f22d 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2643,6 +2643,10 @@
goto error;
}
}
+ // This gets set so BRANCH_BB knows whether to pop
+ // the type stack (type propagation) when generating the
+ // target BB
+ gen_bb_requires_pop = jump;
STACK_SHRINK(1);
STACK_GROW((jump ? 1 : 0));
DISPATCH();
@@ -2703,6 +2707,10 @@
goto error;
}
}
+ // This gets set so BRANCH_BB knows whether to pop
+ // the type stack (type propagation) when generating the
+ // target BB
+ gen_bb_requires_pop = jump;
STACK_SHRINK(1);
STACK_GROW((jump ? 1 : 0));
DISPATCH();
@@ -4129,7 +4137,8 @@
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_UNSET);
// Generate consequent.
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, 0, &tier1_fallback);
+ frame, cache->bb_id, 0, &tier1_fallback, gen_bb_requires_pop);
+ gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -4141,7 +4150,8 @@
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_SET);
// Generate predicate.
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback);
+ frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback + oparg;
@@ -4163,7 +4173,8 @@
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback);
+ frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -4198,7 +4209,8 @@
// @TODO: Rewrite TEST intruction above to a JUMP above..
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback);
+ frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
diff --git a/Python/tier2.c b/Python/tier2.c
index e5d0d831c4fd12..569ce794859388 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -9,11 +9,13 @@
#include "opcode.h"
#define BB_DEBUG 1
+#define TYPEPROP_DEBUG 1
// Max typed version basic blocks per basic block
#define MAX_BB_VERSIONS 5
// Number of potential extra instructions at end of a BB, for branch or cleanup purposes.
#define BB_EPILOG 0
+
static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
@@ -22,6 +24,10 @@ static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
static _PyTier2TypeContext *
initialize_type_context(const PyCodeObject *co) {
+#if TYPEPROP_DEBUG
+ fprintf(stderr, " [*] Initialize type context\n");
+#endif
+
int nlocals = co->co_nlocals;
int nstack = co->co_stacksize;
@@ -56,6 +62,10 @@ initialize_type_context(const PyCodeObject *co) {
static _PyTier2TypeContext *
_PyTier2TypeContext_copy(const _PyTier2TypeContext* type_context) {
+#if TYPEPROP_DEBUG
+ fprintf(stderr, " [*] Copying type context\n");
+#endif
+
int nlocals = type_context->type_locals_len;
int nstack = type_context->type_stack_len;
@@ -82,11 +92,17 @@ _PyTier2TypeContext_copy(const _PyTier2TypeContext* type_context) {
new_type_context->type_stack_len = nstack;
new_type_context->type_locals = type_locals;
new_type_context->type_stack = type_stack;
+ new_type_context->type_stack_ptr = type_stack - type_context->type_stack + type_context->type_stack_ptr;
return new_type_context;
}
static void
_PyTier2TypeContext_free(_PyTier2TypeContext* type_context) {
+
+#if TYPEPROP_DEBUG
+ fprintf(stderr, " [*] Freeing type context\n");
+#endif
+
PyMem_Free(type_context->type_locals);
PyMem_Free(type_context->type_stack);
PyMem_Free(type_context);
@@ -96,9 +112,13 @@ _PyTier2TypeContext_free(_PyTier2TypeContext* type_context) {
static void
type_propagate(
int opcode, int oparg,
- PyTypeObject** type_stackptr, PyTypeObject** type_locals,
+ _PyTier2TypeContext* type_context,
const PyObject* consts)
{
+ PyTypeObject** type_stack = type_context->type_stack;
+ PyTypeObject** type_locals = type_context->type_locals;
+ PyTypeObject** type_stackptr = type_context->type_stack_ptr;
+
#define TARGET(op) case op:
#define TYPESTACK_PEEK(idx) (type_stackptr[-(idx)])
#define TYPESTACK_POKE(idx, v) type_stackptr[-(idx)] = (v)
@@ -109,13 +129,26 @@ type_propagate(
#define STACK_GROW(idx) STACK_ADJUST((idx))
#define STACK_SHRINK(idx) STACK_ADJUST(-(idx))
+#ifdef TYPEPROP_DEBUG
+ fprintf(stderr, " [-] Type stack bef: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack)/sizeof(PyTypeObject*));
+#ifdef Py_DEBUG
+ fprintf(stderr, " [-] Type propagating across: %s : %d\n", _PyOpcode_OpName[opcode], oparg);
+#endif
+#endif
+
switch (opcode) {
#include "tier2_typepropagator.c.h"
default:
fprintf(stderr, "Unsupported opcode in type propagator: %d\n", opcode);
- assert(opcode);
+ Py_UNREACHABLE();
}
+#ifdef TYPEPROP_DEBUG
+ fprintf(stderr, " [-] Type stack aft: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject*));
+#endif
+
+ type_context->type_stack_ptr = type_stackptr;
+
#undef TARGET
#undef TYPESTACK_PEEK
#undef TYPESTACK_POKE
@@ -415,7 +448,7 @@ emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard, int bb_id)
// Converts the tier 1 branch bytecode to tier 2 branch bytecode.
static inline _Py_CODEUNIT *
-emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
+emit_logical_branch(_PyTier2TypeContext* type_context, _Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
{
int opcode;
int oparg = _Py_OPARG(branch);
@@ -427,27 +460,38 @@ emit_logical_branch(_Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
// Subsequent jumps don't need to check this anymore. They can just
// jump directly with JUMP_BACKWARD.
opcode = BB_JUMP_BACKWARD_LAZY;
+ // v BB_JUMP_BACKWARD_LAZY has nothing to propagate
+ // type_propagate(opcode, oparg, type_context, NULL);
break;
case FOR_ITER:
opcode = BB_TEST_ITER;
+ type_propagate(opcode, oparg, type_context, NULL);
break;
case JUMP_IF_FALSE_OR_POP:
opcode = BB_TEST_IF_FALSE_OR_POP;
+ // This inst has conditional stack effect according to whether the branch is taken.
+ // This inst sets the `gen_bb_requires_pop` flag to handle stack effect of this opcode in BB_BRANCH
break;
case JUMP_IF_TRUE_OR_POP:
opcode = BB_TEST_IF_TRUE_OR_POP;
+ // This inst has conditional stack effect according to whether the branch is taken.
+ // This inst sets the `gen_bb_requires_pop` flag to handle stack effect of this opcode in BB_BRANCH
break;
case POP_JUMP_IF_FALSE:
opcode = BB_TEST_POP_IF_FALSE;
+ type_propagate(opcode, oparg, type_context, NULL);
break;
case POP_JUMP_IF_TRUE:
opcode = BB_TEST_POP_IF_TRUE;
+ type_propagate(opcode, oparg, type_context, NULL);
break;
case POP_JUMP_IF_NOT_NONE:
opcode = BB_TEST_POP_IF_NOT_NONE;
+ type_propagate(opcode, oparg, type_context, NULL);
break;
case POP_JUMP_IF_NONE:
opcode = BB_TEST_POP_IF_NONE;
+ type_propagate(opcode, oparg, type_context, NULL);
break;
default:
// Honestly shouldn't happen because branches that
@@ -639,15 +683,16 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
_PyTier2BBMetadata *
_PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
_Py_CODEUNIT *tier1_start,
- // Const qualifier as this function makes a copy of the type_context
- const _PyTier2TypeContext *type_context)
+ // starting_type_context will be modified in this function,
+ // do make a copy if needed before calling this function
+ _PyTier2TypeContext *starting_type_context)
{
#define END() goto end;
#define JUMPBY(x) i += x + 1;
#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); \
write_i = copy_cache_entries(write_i, curr+1, caches); \
i += caches; \
- type_propagate(opcode, oparg, type_stackptr, type_locals, consts); \
+ type_propagate(opcode, oparg, starting_type_context, consts); \
continue;
#define DISPATCH_GOTO() goto dispatch_opcode;
@@ -657,12 +702,6 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// 1. If there's a branch instruction / scope exit.
// 2. If there's a type guard.
- // Make a copy of the type context
- _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_copy(type_context);
- if (type_context_copy == NULL) {
- return NULL;
- }
-
_PyTier2BBMetadata *meta = NULL;
_PyTier2BBMetadata *temp_meta = NULL;
@@ -670,9 +709,6 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
PyObject *consts = co->co_consts;
_Py_CODEUNIT *t2_start = (_Py_CODEUNIT *)(((char *)bb_space->u_code) + bb_space->water_level);
_Py_CODEUNIT *write_i = t2_start;
- PyTypeObject **type_stack = type_context_copy->type_stack;
- PyTypeObject **type_locals = type_context_copy->type_locals;
- PyTypeObject **type_stackptr = type_context_copy->type_stack_ptr;
int tos = -1;
// For handling of backwards jumps
@@ -711,7 +747,9 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
t2_start++;
DISPATCH();
default:
+#if BB_DEBUG || TYPEPROP_DEBUG
fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
+#endif
if (IS_BACKWARDS_JUMP_TARGET(co, curr)) {
#if BB_DEBUG
fprintf(stderr, "Encountered a backward jump target\n");
@@ -730,6 +768,11 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// Else, create a virtual end to the basic block.
// But generate the block after that so it can fall through.
i--;
+ // Make a copy of the type context
+ _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_copy(starting_type_context);
+ if (type_context_copy == NULL) {
+ return NULL;
+ }
meta = _PyTier2_AllocateBBMetaData(co,
t2_start, _PyCode_CODE(co) + i, type_context_copy);
if (meta == NULL) {
@@ -762,7 +805,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
}
// Get the BB ID without incrementing it.
// AllocateBBMetaData will increment.
- write_i = emit_logical_branch(write_i, *curr,
+ write_i = emit_logical_branch(starting_type_context, write_i, *curr,
co->_tier2_info->bb_data_curr);
i += caches;
END();
@@ -773,9 +816,19 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
}
end:
// Create the tier 2 BB
+
+ // Make a copy of the type context
+ _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_copy(starting_type_context);
+ if (type_context_copy == NULL) {
+ return NULL;
+ }
temp_meta = _PyTier2_AllocateBBMetaData(co, t2_start,
// + 1 because we want to start with the NEXT instruction for the scan
_PyCode_CODE(co) + i + 1, type_context_copy);
+ if (temp_meta == NULL) {
+ _PyTier2TypeContext_free(type_context_copy);
+ return NULL;
+ }
// We need to return the first block to enter into. If there is already a block generated
// before us, then we use that instead of the most recent block.
if (meta == NULL) {
@@ -785,7 +838,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// Add the basic block to the jump ids
if (add_metadata_to_jump_2d_array(t2_info, temp_meta, backwards_jump_target_offset) < 0) {
PyMem_Free(meta);
- PyMem_Free(temp_meta);
+ if (meta != temp_meta) PyMem_Free(temp_meta);
_PyTier2TypeContext_free(type_context_copy);
return NULL;
}
@@ -1103,7 +1156,7 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// The second basic block created will always require a jump.
_Py_CODEUNIT *
_PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
- _Py_CODEUNIT **tier1_fallback)
+ _Py_CODEUNIT **tier1_fallback, char gen_bb_requires_pop)
{
PyCodeObject *co = frame->f_code;
assert(co->_tier2_info != NULL);
@@ -1122,10 +1175,22 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
}
// Get type_context of previous BB
_PyTier2TypeContext *type_context = meta->type_context;
+ // Make a copy of the type context
+ _PyTier2TypeContext* type_context_copy = _PyTier2TypeContext_copy(type_context);
+ if (type_context_copy == NULL) {
+ return NULL;
+ }
+ // If this flag is set, it means that either BB_TEST_IF_FALSE_OR_POP or
+ // BB_TEST_IF_TRUE_OR_POP was ran and the conditional stack effect was performed
+ // This means we have to pop an element from the type stack.
+ if (gen_bb_requires_pop) {
+ type_context_copy->type_stack_ptr--;
+ }
_PyTier2BBMetadata *metadata = _PyTier2_Code_DetectAndEmitBB(
frame->f_code, space, tier1_end,
- type_context);
+ type_context_copy);
if (metadata == NULL) {
+ _PyTier2TypeContext_free(type_context_copy);
return NULL;
}
return metadata->tier2_start;
@@ -1170,8 +1235,8 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int j
for (int x = 0; x < MAX_BB_VERSIONS; x++) {
#if BB_DEBUG
fprintf(stderr, "jump target BB ID: %d\n",
-#endif
t2_info->backward_jump_target_bb_ids[i][x]);
+#endif
// @TODO, this is where the diff function is supposed to be
// it will calculate the closest type context BB
// For now just any valid BB (>= 0) is used.
@@ -1185,7 +1250,9 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int j
}
assert(matching_bb_id >= 0);
assert(matching_bb_id <= t2_info->bb_data_curr);
+#if BB_DEBUG
fprintf(stderr, "Found jump target BB ID: %d\n", matching_bb_id);
+#endif
_PyTier2BBMetadata *target_metadata = t2_info->bb_data[matching_bb_id];
return target_metadata->tier2_start;
}
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index fb7ee319f35d83..786eff5afbcf23 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -829,30 +829,26 @@
}
TARGET(JUMP_IF_FALSE_OR_POP) {
- PyTypeObject *cond = TYPESTACK_PEEK(1);
- STACK_SHRINK(1);
- STACK_GROW((jump ? 1 : 0));
+ fprintf(stderr, "Type propagation across `{self.name}` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(BB_TEST_IF_FALSE_OR_POP) {
- PyTypeObject *cond = TYPESTACK_PEEK(1);
- STACK_SHRINK(1);
- STACK_GROW((jump ? 1 : 0));
+ fprintf(stderr, "Type propagation across `{self.name}` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(JUMP_IF_TRUE_OR_POP) {
- PyTypeObject *cond = TYPESTACK_PEEK(1);
- STACK_SHRINK(1);
- STACK_GROW((jump ? 1 : 0));
+ fprintf(stderr, "Type propagation across `{self.name}` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(BB_TEST_IF_TRUE_OR_POP) {
- PyTypeObject *cond = TYPESTACK_PEEK(1);
- STACK_SHRINK(1);
- STACK_GROW((jump ? 1 : 0));
+ fprintf(stderr, "Type propagation across `{self.name}` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 479ee2a8977b3f..3c37a47d21c2cf 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -49,6 +49,17 @@
UNUSED = "unused"
BITS_PER_CODE_UNIT = 16
+# Type propagation across these instructions are forbidden
+# due to conditional effects that can't be determined statically
+# The handling of type propagation across these opcodes are handled elsewhere
+# within tier2.
+TYPE_PROPAGATOR_FORBIDDEN = [
+ "JUMP_IF_FALSE_OR_POP",
+ "JUMP_IF_TRUE_OR_POP", # Type propagator shouldn't see these
+ "BB_TEST_IF_FALSE_OR_POP",
+ "BB_TEST_IF_TRUE_OR_POP" # Type propagator handles this in BB_BRANCH
+]
+
arg_parser = argparse.ArgumentParser(
description="Generate the code for the interpreter switch.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
@@ -305,6 +316,11 @@ def analyze_registers(self, a: "Analyzer") -> None:
def write_typeprop(self, out: Formatter) -> None:
"""Write one instruction's type propagation rules"""
+ if self.name in TYPE_PROPAGATOR_FORBIDDEN:
+ out.emit('fprintf(stderr, "Type propagation across `{self.name}` shouldn\'t be handled statically!\\n");')
+ out.emit("Py_UNREACHABLE();")
+ return
+
# Write input stack effect variable declarations and initializations
ieffects = list(reversed(self.input_effects))
usable_for_local_effect = {}
From 337720b095dcde838f9acdc637c9826013c0aa55 Mon Sep 17 00:00:00 2001
From: Julia
Date: Thu, 2 Mar 2023 14:36:15 +0800
Subject: [PATCH 050/280] Fix: Wrong behaviour of gen_bb_requires_pop
---
Python/bytecodes.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index bdd5173dc68fb2..59f3db48f80283 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2087,7 +2087,7 @@ dummy_func(
// This gets set so BRANCH_BB knows whether to pop
// the type stack (type propagation) when generating the
// target BB
- gen_bb_requires_pop = jump;
+ gen_bb_requires_pop = !jump;
}
inst(JUMP_IF_TRUE_OR_POP, (cond -- cond if (jump))) {
@@ -2143,7 +2143,7 @@ dummy_func(
// This gets set so BRANCH_BB knows whether to pop
// the type stack (type propagation) when generating the
// target BB
- gen_bb_requires_pop = jump;
+ gen_bb_requires_pop = !jump;
}
inst(JUMP_BACKWARD_NO_INTERRUPT, (--)) {
From 17b09caf86aec0b1c00f82595339833f57a5dc7e Mon Sep 17 00:00:00 2001
From: Julia
Date: Thu, 2 Mar 2023 15:35:11 +0800
Subject: [PATCH 051/280] Style: Reformatted to follow CPython's style guide
---
Python/tier2.c | 89 +++++++++++++++++++++++++++-----------------------
1 file changed, 49 insertions(+), 40 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 569ce794859388..62384f90e5c419 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -22,7 +22,8 @@ static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
////////// TYPE CONTEXT FUNCTIONS
static _PyTier2TypeContext *
-initialize_type_context(const PyCodeObject *co) {
+initialize_type_context(const PyCodeObject *co)
+{
#if TYPEPROP_DEBUG
fprintf(stderr, " [*] Initialize type context\n");
@@ -42,8 +43,12 @@ initialize_type_context(const PyCodeObject *co) {
}
// Initialize to unknown type.
- for (int i = 0; i < nlocals; i++) type_locals[i] = NULL;
- for (int i = 0; i < nstack; i++) type_stack[i] = NULL;
+ for (int i = 0; i < nlocals; i++) {
+ type_locals[i] = NULL;
+ }
+ for (int i = 0; i < nstack; i++) {
+ type_stack[i] = NULL;
+ }
_PyTier2TypeContext *type_context = PyMem_Malloc(sizeof(_PyTier2TypeContext));
if (type_context == NULL) {
@@ -60,7 +65,8 @@ initialize_type_context(const PyCodeObject *co) {
}
static _PyTier2TypeContext *
-_PyTier2TypeContext_copy(const _PyTier2TypeContext* type_context) {
+_PyTier2TypeContext_copy(const _PyTier2TypeContext *type_context)
+{
#if TYPEPROP_DEBUG
fprintf(stderr, " [*] Copying type context\n");
@@ -69,20 +75,20 @@ _PyTier2TypeContext_copy(const _PyTier2TypeContext* type_context) {
int nlocals = type_context->type_locals_len;
int nstack = type_context->type_stack_len;
- PyTypeObject** type_locals = PyMem_Malloc(nlocals * sizeof(PyTypeObject*));
+ PyTypeObject **type_locals = PyMem_Malloc(nlocals * sizeof(PyTypeObject *));
if (type_locals == NULL) {
return NULL;
}
- PyTypeObject** type_stack = PyMem_Malloc(nstack * sizeof(PyTypeObject*));
+ PyTypeObject **type_stack = PyMem_Malloc(nstack * sizeof(PyTypeObject *));
if (type_stack == NULL) {
PyMem_Free(type_locals);
return NULL;
}
- memcpy(type_locals, type_context->type_locals, nlocals * sizeof(PyTypeObject*));
- memcpy(type_stack, type_context->type_stack, nstack * sizeof(PyTypeObject*));
+ memcpy(type_locals, type_context->type_locals, nlocals * sizeof(PyTypeObject *));
+ memcpy(type_stack, type_context->type_stack, nstack * sizeof(PyTypeObject *));
- _PyTier2TypeContext* new_type_context = PyMem_Malloc(sizeof(_PyTier2TypeContext));
+ _PyTier2TypeContext *new_type_context = PyMem_Malloc(sizeof(_PyTier2TypeContext));
if (new_type_context == NULL) {
PyMem_Free(type_locals);
PyMem_Free(type_stack);
@@ -97,7 +103,8 @@ _PyTier2TypeContext_copy(const _PyTier2TypeContext* type_context) {
}
static void
-_PyTier2TypeContext_free(_PyTier2TypeContext* type_context) {
+_PyTier2TypeContext_free(_PyTier2TypeContext *type_context)
+{
#if TYPEPROP_DEBUG
fprintf(stderr, " [*] Freeing type context\n");
@@ -112,12 +119,12 @@ _PyTier2TypeContext_free(_PyTier2TypeContext* type_context) {
static void
type_propagate(
int opcode, int oparg,
- _PyTier2TypeContext* type_context,
- const PyObject* consts)
+ _PyTier2TypeContext *type_context,
+ const PyObject *consts)
{
- PyTypeObject** type_stack = type_context->type_stack;
- PyTypeObject** type_locals = type_context->type_locals;
- PyTypeObject** type_stackptr = type_context->type_stack_ptr;
+ PyTypeObject **type_stack = type_context->type_stack;
+ PyTypeObject **type_locals = type_context->type_locals;
+ PyTypeObject **type_stackptr = type_context->type_stack_ptr;
#define TARGET(op) case op:
#define TYPESTACK_PEEK(idx) (type_stackptr[-(idx)])
@@ -130,7 +137,7 @@ type_propagate(
#define STACK_SHRINK(idx) STACK_ADJUST(-(idx))
#ifdef TYPEPROP_DEBUG
- fprintf(stderr, " [-] Type stack bef: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack)/sizeof(PyTypeObject*));
+ fprintf(stderr, " [-] Type stack bef: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject *));
#ifdef Py_DEBUG
fprintf(stderr, " [-] Type propagating across: %s : %d\n", _PyOpcode_OpName[opcode], oparg);
#endif
@@ -144,7 +151,7 @@ type_propagate(
}
#ifdef TYPEPROP_DEBUG
- fprintf(stderr, " [-] Type stack aft: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject*));
+ fprintf(stderr, " [-] Type stack aft: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject *));
#endif
type_context->type_stack_ptr = type_stackptr;
@@ -322,7 +329,7 @@ IS_JREL_OPCODE(int opcode)
case JUMP_FORWARD:
case JUMP_IF_FALSE_OR_POP:
case JUMP_IF_TRUE_OR_POP:
- // These two tend to be after a COMPARE_AND_BRANCH.
+ // These two tend to be after a COMPARE_AND_BRANCH.
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
case SEND:
@@ -389,27 +396,27 @@ static inline int
IS_FORBIDDEN_OPCODE(int opcode)
{
switch (opcode) {
- // Generators and coroutines
+ // Generators and coroutines
case SEND:
case YIELD_VALUE:
- // Raise keyword
+ // Raise keyword
case RAISE_VARARGS:
- // Exceptions, we could support these theoretically.
- // Just too much work for now
+ // Exceptions, we could support these theoretically.
+ // Just too much work for now
case PUSH_EXC_INFO:
case RERAISE:
case POP_EXCEPT:
- // Closures
+ // Closures
case LOAD_DEREF:
case MAKE_CELL:
- // DELETE_FAST
+ // DELETE_FAST
case DELETE_FAST:
- // Pattern matching
+ // Pattern matching
case MATCH_MAPPING:
case MATCH_SEQUENCE:
case MATCH_KEYS:
- // Too large arguments, we can handle this, just
- // increases complexity
+ // Too large arguments, we can handle this, just
+ // increases complexity
case EXTENDED_ARG:
return 1;
@@ -448,7 +455,7 @@ emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard, int bb_id)
// Converts the tier 1 branch bytecode to tier 2 branch bytecode.
static inline _Py_CODEUNIT *
-emit_logical_branch(_PyTier2TypeContext* type_context, _Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
+emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
{
int opcode;
int oparg = _Py_OPARG(branch);
@@ -696,7 +703,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
continue;
#define DISPATCH_GOTO() goto dispatch_opcode;
-
+
assert(co->_tier2_info != NULL);
// There are only two cases that a BB ends.
// 1. If there's a branch instruction / scope exit.
@@ -730,7 +737,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// int how_many_guards = 0;
// _Py_CODEUNIT guard_instr;
// _Py_CODEUNIT action;
-
+
dispatch_opcode:
switch (opcode) {
case RESUME:
@@ -823,8 +830,8 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
return NULL;
}
temp_meta = _PyTier2_AllocateBBMetaData(co, t2_start,
- // + 1 because we want to start with the NEXT instruction for the scan
- _PyCode_CODE(co) + i + 1, type_context_copy);
+ // + 1 because we want to start with the NEXT instruction for the scan
+ _PyCode_CODE(co) + i + 1, type_context_copy);
if (temp_meta == NULL) {
_PyTier2TypeContext_free(type_context_copy);
return NULL;
@@ -838,7 +845,9 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// Add the basic block to the jump ids
if (add_metadata_to_jump_2d_array(t2_info, temp_meta, backwards_jump_target_offset) < 0) {
PyMem_Free(meta);
- if (meta != temp_meta) PyMem_Free(temp_meta);
+ if (meta != temp_meta) {
+ PyMem_Free(temp_meta);
+ }
_PyTier2TypeContext_free(type_context_copy);
return NULL;
}
@@ -875,7 +884,7 @@ allocate_jump_offset_2d_array(int backwards_jump_count, int **backward_jump_targ
for (int i = 0; i < MAX_BB_VERSIONS; i++) {
jump_offsets[i] = -1;
}
- done++;
+ done++;
backward_jump_target_bb_ids[i] = jump_offsets;
}
return 0;
@@ -949,8 +958,8 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
i += _PyOpcode_Caches[opcode];
}
assert(curr_i == backwards_jump_count);
- qsort(backward_jump_offsets,backwards_jump_count,
- sizeof(int), compare_ints);
+ qsort(backward_jump_offsets, backwards_jump_count,
+ sizeof(int), compare_ints);
#if BB_DEBUG
fprintf(stderr, "BACKWARD JUMP COUNT : %Id\n", backwards_jump_count);
fprintf(stderr, "BACKWARD JUMP TARGET OFFSETS (FROM START OF CODE): ");
@@ -985,7 +994,7 @@ _PyTier2Info_Initialize(PyCodeObject *co)
t2_info->bb_data_curr = 0;
Py_ssize_t bb_data_len = (Py_SIZE(co) / 5 + 1);
assert((int)bb_data_len == bb_data_len);
- _PyTier2BBMetadata **bb_data = PyMem_Calloc(bb_data_len, sizeof(_PyTier2BBMetadata*));
+ _PyTier2BBMetadata **bb_data = PyMem_Calloc(bb_data_len, sizeof(_PyTier2BBMetadata *));
if (bb_data == NULL) {
PyMem_Free(t2_info);
return NULL;
@@ -1114,7 +1123,7 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
fprintf(stderr, "ENTRY BB END IS: %d\n", (int)(meta->tier1_end - _PyCode_CODE(co)));
#endif
-
+
t2_info->_entry_bb = meta;
// SET THE FRAME INFO
@@ -1176,7 +1185,7 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
// Get type_context of previous BB
_PyTier2TypeContext *type_context = meta->type_context;
// Make a copy of the type context
- _PyTier2TypeContext* type_context_copy = _PyTier2TypeContext_copy(type_context);
+ _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_copy(type_context);
if (type_context_copy == NULL) {
return NULL;
}
@@ -1218,7 +1227,7 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int j
}
// Get type_context of previous BB
- _PyTier2TypeContext* type_context = meta->type_context;
+ _PyTier2TypeContext *type_context = meta->type_context;
// Now, find the matching BB
_PyTier2Info *t2_info = co->_tier2_info;
int jump_offset = (int)(tier1_jump_target - _PyCode_CODE(co));
From 579c2d68c4c51a8a642101a039d9d4b8b53dce2a Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Thu, 2 Mar 2023 15:38:17 +0800
Subject: [PATCH 052/280] Update Tools/cases_generator/parser.py
Co-authored-by: Ken Jin
---
Tools/cases_generator/parser.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 14adf59640b1dd..49f563b0628416 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -75,7 +75,7 @@ class StackVarTypeLiteral(Node):
@dataclass
class StackVarTypeIndex(Node):
- array: Literal["locals"] | Literal["consts"]
+ array: Literal["locals", "consts"]
index: str
From 80b9cbe4b584fe349a366a9314af597c9bf1f773 Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Thu, 2 Mar 2023 15:38:42 +0800
Subject: [PATCH 053/280] Update Tools/cases_generator/parser.py
Co-authored-by: Ken Jin
---
Tools/cases_generator/parser.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 49f563b0628416..1e66bf71bb7a36 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -79,7 +79,7 @@ class StackVarTypeIndex(Node):
index: str
-StackVarType = StackVarTypeLiteral | StackVarTypeIndex
+StackVarType: TypeAlias = StackVarTypeLiteral | StackVarTypeIndex
@dataclass
From 5c5db523b0723f34bde90b69b7f22674644537f2 Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Thu, 2 Mar 2023 15:39:33 +0800
Subject: [PATCH 054/280] Update Tools/cases_generator/parser.py
Co-authored-by: Ken Jin
---
Tools/cases_generator/parser.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 1e66bf71bb7a36..d81a9eefd529c3 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -113,7 +113,7 @@ class LocalEffectVarStack(Node):
name: str
-LocalEffectVar = LocalEffectVarLiteral | LocalEffectVarStack
+LocalEffectVar: TypeAlias = LocalEffectVarLiteral | LocalEffectVarStack
@dataclass
From f17dd5f82f394d54f7596d6ae0001de880a0ebc9 Mon Sep 17 00:00:00 2001
From: Julia
Date: Thu, 2 Mar 2023 15:43:47 +0800
Subject: [PATCH 055/280] Refactor: Change shadowed builtins `type` in
parser.py
---
Tools/cases_generator/parser.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 14adf59640b1dd..5655dc0be76e9d 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -307,7 +307,7 @@ def stack_effect(self) -> StackEffect | None:
# IDENTIFIER [':' IDENTIFIER] ['if' '(' expression ')']
# | IDENTIFIER '[' expression ']'
if tkn := self.expect(lx.IDENTIFIER):
- type = ""
+ _type = ""
has_type_annotation = False
type_annotation = None
if self.expect(lx.COLON):
@@ -327,9 +327,9 @@ def stack_effect(self) -> StackEffect | None:
if not (size := self.expression()):
raise self.make_syntax_error("Expected expression")
self.require(lx.RBRACKET)
- type = "PyObject **"
+ _type = "PyObject **"
size_text = size.text.strip()
- return StackEffect(tkn.text, type, type_annotation, cond_text, size_text)
+ return StackEffect(tkn.text, _type, type_annotation, cond_text, size_text)
@contextual
def stackvar_type(self) -> StackVarType | None:
From 699a6a3fea2553e53b510a7d3a90e39a8a0db546 Mon Sep 17 00:00:00 2001
From: Julia
Date: Thu, 2 Mar 2023 15:45:43 +0800
Subject: [PATCH 056/280] Refactor: Renamed functions
---
Python/tier2.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 62384f90e5c419..c49ee8eea1dda8 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -65,7 +65,7 @@ initialize_type_context(const PyCodeObject *co)
}
static _PyTier2TypeContext *
-_PyTier2TypeContext_copy(const _PyTier2TypeContext *type_context)
+_PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
{
#if TYPEPROP_DEBUG
@@ -103,7 +103,7 @@ _PyTier2TypeContext_copy(const _PyTier2TypeContext *type_context)
}
static void
-_PyTier2TypeContext_free(_PyTier2TypeContext *type_context)
+_PyTier2TypeContext_Free(_PyTier2TypeContext *type_context)
{
#if TYPEPROP_DEBUG
@@ -776,14 +776,14 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// But generate the block after that so it can fall through.
i--;
// Make a copy of the type context
- _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_copy(starting_type_context);
+ _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_Copy(starting_type_context);
if (type_context_copy == NULL) {
return NULL;
}
meta = _PyTier2_AllocateBBMetaData(co,
t2_start, _PyCode_CODE(co) + i, type_context_copy);
if (meta == NULL) {
- _PyTier2TypeContext_free(type_context_copy);
+ _PyTier2TypeContext_Free(type_context_copy);
return NULL;
}
bb_space->water_level += (write_i - t2_start) * sizeof(_Py_CODEUNIT);
@@ -825,7 +825,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// Create the tier 2 BB
// Make a copy of the type context
- _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_copy(starting_type_context);
+ _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_Copy(starting_type_context);
if (type_context_copy == NULL) {
return NULL;
}
@@ -833,7 +833,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// + 1 because we want to start with the NEXT instruction for the scan
_PyCode_CODE(co) + i + 1, type_context_copy);
if (temp_meta == NULL) {
- _PyTier2TypeContext_free(type_context_copy);
+ _PyTier2TypeContext_Free(type_context_copy);
return NULL;
}
// We need to return the first block to enter into. If there is already a block generated
@@ -848,7 +848,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
if (meta != temp_meta) {
PyMem_Free(temp_meta);
}
- _PyTier2TypeContext_free(type_context_copy);
+ _PyTier2TypeContext_Free(type_context_copy);
return NULL;
}
}
@@ -1116,7 +1116,7 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
co, bb_space,
_PyCode_CODE(co), type_context);
if (meta == NULL) {
- _PyTier2TypeContext_free(type_context);
+ _PyTier2TypeContext_Free(type_context);
goto cleanup;
}
#if BB_DEBUG
@@ -1185,7 +1185,7 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
// Get type_context of previous BB
_PyTier2TypeContext *type_context = meta->type_context;
// Make a copy of the type context
- _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_copy(type_context);
+ _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_Copy(type_context);
if (type_context_copy == NULL) {
return NULL;
}
@@ -1199,7 +1199,7 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
frame->f_code, space, tier1_end,
type_context_copy);
if (metadata == NULL) {
- _PyTier2TypeContext_free(type_context_copy);
+ _PyTier2TypeContext_Free(type_context_copy);
return NULL;
}
return metadata->tier2_start;
From 99a4ade88a3d9fefd29ac1190241e81b5c04d9c8 Mon Sep 17 00:00:00 2001
From: Julia
Date: Fri, 3 Mar 2023 01:53:08 +0800
Subject: [PATCH 057/280] Feat: More type prop rules in the DSL and bytecodes.c
DSL now allows declaring the type of an output stack effect
with the type of an input stack effect.
---
Python/bytecodes.c | 74 +++++++--------
Python/generated_cases.c.h | 8 +-
Python/tier2_typepropagator.c.h | 115 +++++++++++-------------
Tools/cases_generator/generate_cases.py | 48 +++++-----
Tools/cases_generator/parser.py | 25 +++++-
5 files changed, 144 insertions(+), 126 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 59f3db48f80283..35cc2176615d58 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -140,7 +140,7 @@ dummy_func(
DECREF_INPUTS();
}
- inst(PUSH_NULL, (-- res)) {
+ inst(PUSH_NULL, (-- res: NULL)) {
res = NULL;
}
@@ -184,7 +184,7 @@ dummy_func(
};
- inst(BINARY_OP_MULTIPLY_INT, (unused/1, left, right -- prod)) {
+ inst(BINARY_OP_MULTIPLY_INT, (unused/1, left, right -- prod: PyLong_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
@@ -195,7 +195,7 @@ dummy_func(
ERROR_IF(prod == NULL, error);
}
- inst(BINARY_OP_MULTIPLY_FLOAT, (unused/1, left, right -- prod)) {
+ inst(BINARY_OP_MULTIPLY_FLOAT, (unused/1, left, right -- prod: PyFloat_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
@@ -208,7 +208,7 @@ dummy_func(
ERROR_IF(prod == NULL, error);
}
- inst(BINARY_OP_SUBTRACT_INT, (unused/1, left, right -- sub)) {
+ inst(BINARY_OP_SUBTRACT_INT, (unused/1, left, right -- sub: PyLong_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
@@ -219,7 +219,7 @@ dummy_func(
ERROR_IF(sub == NULL, error);
}
- inst(BINARY_OP_SUBTRACT_FLOAT, (unused/1, left, right -- sub)) {
+ inst(BINARY_OP_SUBTRACT_FLOAT, (unused/1, left, right -- sub: PyFloat_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
@@ -231,7 +231,7 @@ dummy_func(
ERROR_IF(sub == NULL, error);
}
- inst(BINARY_OP_ADD_UNICODE, (unused/1, left, right -- res)) {
+ inst(BINARY_OP_ADD_UNICODE, (unused/1, left, right -- res: PyUnicode_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
@@ -278,7 +278,7 @@ dummy_func(
JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP + 1);
}
- inst(BINARY_OP_ADD_FLOAT, (unused/1, left, right -- sum)) {
+ inst(BINARY_OP_ADD_FLOAT, (unused/1, left, right -- sum: PyFloat_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
@@ -291,7 +291,7 @@ dummy_func(
ERROR_IF(sum == NULL, error);
}
- macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
+ macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum: PyLong_Type)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
@@ -303,7 +303,7 @@ dummy_func(
bb_test = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
}
- u_inst(BINARY_OP_ADD_INT_REST, (left : PyLong_Type, right : PyLong_Type -- sum : PyLong_Type)) {
+ u_inst(BINARY_OP_ADD_INT_REST, (left, right -- sum : PyLong_Type)) {
STAT_INC(BINARY_OP, hit);
sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
@@ -440,12 +440,12 @@ dummy_func(
DISPATCH_INLINED(new_frame);
}
- inst(LIST_APPEND, (list, unused[oparg-1], v -- list, unused[oparg-1])) {
+ inst(LIST_APPEND, (list, unused[oparg-1], v -- list: *list, unused[oparg-1])) {
ERROR_IF(_PyList_AppendTakeRef((PyListObject *)list, v) < 0, error);
PREDICT(JUMP_BACKWARD);
}
- inst(SET_ADD, (set, unused[oparg-1], v -- set, unused[oparg-1])) {
+ inst(SET_ADD, (set, unused[oparg-1], v -- set: *set, unused[oparg-1])) {
int err = PySet_Add(set, v);
Py_DECREF(v);
ERROR_IF(err, error);
@@ -628,7 +628,7 @@ dummy_func(
}
}
- inst(GET_ANEXT, (aiter -- aiter, awaitable)) {
+ inst(GET_ANEXT, (aiter -- aiter: *aiter, awaitable)) {
unaryfunc getter = NULL;
PyObject *next_iter = NULL;
PyTypeObject *type = Py_TYPE(aiter);
@@ -1274,7 +1274,7 @@ dummy_func(
}
}
- inst(BUILD_STRING, (pieces[oparg] -- str)) {
+ inst(BUILD_STRING, (pieces[oparg] -- str: PyUnicode_Type)) {
str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg);
for (int i = 0; i < oparg; i++) {
Py_DECREF(pieces[i]);
@@ -1282,17 +1282,17 @@ dummy_func(
ERROR_IF(str == NULL, error);
}
- inst(BUILD_TUPLE, (values[oparg] -- tup)) {
+ inst(BUILD_TUPLE, (values[oparg] -- tup: PyTuple_Type)) {
tup = _PyTuple_FromArraySteal(values, oparg);
ERROR_IF(tup == NULL, error);
}
- inst(BUILD_LIST, (values[oparg] -- list)) {
+ inst(BUILD_LIST, (values[oparg] -- list: PyList_Type)) {
list = _PyList_FromArraySteal(values, oparg);
ERROR_IF(list == NULL, error);
}
- inst(LIST_EXTEND, (list, unused[oparg-1], iterable -- list, unused[oparg-1])) {
+ inst(LIST_EXTEND, (list, unused[oparg-1], iterable -- list: *list, unused[oparg-1])) {
PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable);
if (none_val == NULL) {
if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) &&
@@ -1310,13 +1310,13 @@ dummy_func(
DECREF_INPUTS();
}
- inst(SET_UPDATE, (set, unused[oparg-1], iterable -- set, unused[oparg-1])) {
+ inst(SET_UPDATE, (set, unused[oparg-1], iterable -- set: *set, unused[oparg-1])) {
int err = _PySet_Update(set, iterable);
DECREF_INPUTS();
ERROR_IF(err < 0, error);
}
- inst(BUILD_SET, (values[oparg] -- set)) {
+ inst(BUILD_SET, (values[oparg] -- set: PySet_Type)) {
set = PySet_New(NULL);
if (set == NULL)
goto error;
@@ -1333,7 +1333,7 @@ dummy_func(
}
}
- inst(BUILD_MAP, (values[oparg*2] -- map)) {
+ inst(BUILD_MAP, (values[oparg*2] -- map: PyDict_Type)) {
map = _PyDict_FromItems(
values, 2,
values+1, 2,
@@ -1390,7 +1390,7 @@ dummy_func(
}
}
- inst(BUILD_CONST_KEY_MAP, (values[oparg], keys -- map)) {
+ inst(BUILD_CONST_KEY_MAP, (values[oparg], keys -- map: PyDict_Type)) {
if (!PyTuple_CheckExact(keys) ||
PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) {
_PyErr_SetString(tstate, PyExc_SystemError,
@@ -1835,13 +1835,13 @@ dummy_func(
}
}
- inst(IS_OP, (left, right -- b)) {
+ inst(IS_OP, (left, right -- b: PyBool_Type)) {
int res = Py_Is(left, right) ^ oparg;
DECREF_INPUTS();
b = Py_NewRef(res ? Py_True : Py_False);
}
- inst(CONTAINS_OP, (left, right -- b)) {
+ inst(CONTAINS_OP, (left, right -- b: PyBool_Type)) {
int res = PySequence_Contains(right, left);
DECREF_INPUTS();
ERROR_IF(res < 0, error);
@@ -1869,7 +1869,7 @@ dummy_func(
}
}
- inst(CHECK_EXC_MATCH, (left, right -- left, b)) {
+ inst(CHECK_EXC_MATCH, (left, right -- left: *left, b: PyBool_Type)) {
assert(PyExceptionInstance_Check(left));
if (check_except_type_valid(tstate, right) < 0) {
DECREF_INPUTS();
@@ -1888,7 +1888,7 @@ dummy_func(
ERROR_IF(res == NULL, error);
}
- inst(IMPORT_FROM, (from -- from, res)) {
+ inst(IMPORT_FROM, (from -- from: *from, res)) {
PyObject *name = GETITEM(names, oparg);
res = import_from(tstate, from, name);
ERROR_IF(res == NULL, error);
@@ -2155,7 +2155,7 @@ dummy_func(
JUMPBY(-oparg);
}
- inst(GET_LEN, (obj -- obj, len_o)) {
+ inst(GET_LEN, (obj -- obj: *obj, len_o: PyLong_Type)) {
// PUSH(len(TOS))
Py_ssize_t len_i = PyObject_Length(obj);
ERROR_IF(len_i < 0, error);
@@ -2178,19 +2178,19 @@ dummy_func(
}
}
- inst(MATCH_MAPPING, (subject -- subject, res)) {
+ inst(MATCH_MAPPING, (subject -- subject: *subject, res: PyBool_Type)) {
int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING;
res = Py_NewRef(match ? Py_True : Py_False);
PREDICT(POP_JUMP_IF_FALSE);
}
- inst(MATCH_SEQUENCE, (subject -- subject, res)) {
+ inst(MATCH_SEQUENCE, (subject -- subject: *subject, res: PyBool_Type)) {
int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE;
res = Py_NewRef(match ? Py_True : Py_False);
PREDICT(POP_JUMP_IF_FALSE);
}
- inst(MATCH_KEYS, (subject, keys -- subject, keys, values_or_none)) {
+ inst(MATCH_KEYS, (subject, keys -- subject: *subject, keys: *keys, values_or_none)) {
// On successful match, PUSH(values). Otherwise, PUSH(None).
values_or_none = match_keys(tstate, subject, keys);
ERROR_IF(values_or_none == NULL, error);
@@ -2245,7 +2245,7 @@ dummy_func(
FOR_ITER_GEN,
};
- inst(FOR_ITER, (unused/1, iter -- iter, next)) {
+ inst(FOR_ITER, (unused/1, iter -- iter: *iter, next)) {
#if ENABLE_SPECIALIZATION
_PyForIterCache *cache = (_PyForIterCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
@@ -2281,7 +2281,7 @@ dummy_func(
}
// FOR_ITER
- inst(BB_TEST_ITER, (unused/1, iter -- iter, next)) {
+ inst(BB_TEST_ITER, (unused/1, iter -- iter: *iter, next)) {
next = (*Py_TYPE(iter)->tp_iternext)(iter);
if (next == NULL) {
if (_PyErr_Occurred(tstate)) {
@@ -2303,7 +2303,7 @@ dummy_func(
bb_test = true;
}
- inst(FOR_ITER_LIST, (unused/1, iter -- iter, next)) {
+ inst(FOR_ITER_LIST, (unused/1, iter -- iter: *iter, next)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER);
_PyListIterObject *it = (_PyListIterObject *)iter;
@@ -2326,7 +2326,7 @@ dummy_func(
// Common case: no jump, leave it to the code generator
}
- inst(FOR_ITER_TUPLE, (unused/1, iter -- iter, next)) {
+ inst(FOR_ITER_TUPLE, (unused/1, iter -- iter: *iter, next)) {
assert(cframe.use_tracing == 0);
_PyTupleIterObject *it = (_PyTupleIterObject *)iter;
DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER);
@@ -2349,7 +2349,7 @@ dummy_func(
// Common case: no jump, leave it to the code generator
}
- inst(FOR_ITER_RANGE, (unused/1, iter -- iter, next)) {
+ inst(FOR_ITER_RANGE, (unused/1, iter -- iter: *iter, next)) {
assert(cframe.use_tracing == 0);
_PyRangeIterObject *r = (_PyRangeIterObject *)iter;
DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER);
@@ -2370,7 +2370,7 @@ dummy_func(
}
}
- inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) {
+ inst(FOR_ITER_GEN, (unused/1, iter -- iter: *iter, unused)) {
assert(cframe.use_tracing == 0);
PyGenObject *gen = (PyGenObject *)iter;
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER);
@@ -2455,7 +2455,7 @@ dummy_func(
}
}
- inst(WITH_EXCEPT_START, (exit_func, lasti, unused, val -- exit_func, lasti, unused, val, res)) {
+ inst(WITH_EXCEPT_START, (exit_func, lasti, unused, val -- exit_func, lasti: PyLong_Type, unused, val, res)) {
/* At the top of the stack are 4 values:
- val: TOP = exc_info()
- unused: SECOND = previous exception
@@ -3247,7 +3247,7 @@ dummy_func(
}
}
- inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) {
+ inst(COPY, (bottom, unused[oparg-1] -- bottom: *bottom, unused[oparg-1], top: *bottom)) {
assert(oparg > 0);
top = Py_NewRef(bottom);
}
@@ -3274,7 +3274,7 @@ dummy_func(
}
inst(SWAP, (bottom, unused[oparg-2], top --
- top, unused[oparg-2], bottom)) {
+ top : *top, unused[oparg-2], bottom : *bottom)) {
assert(oparg >= 2);
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index ba0aa72c68f22d..8aac7f5e2fa8f9 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -1,5 +1,5 @@
-// This file is generated by Tools\cases_generator\generate_cases.py
-// from Python\bytecodes.c
+// This file is generated by Tools/cases_generator/generate_cases.py
+// from Python/bytecodes.c
// Do not edit!
#define UOP_BINARY_OP_ADD_INT_REST() \
@@ -2646,7 +2646,7 @@
// This gets set so BRANCH_BB knows whether to pop
// the type stack (type propagation) when generating the
// target BB
- gen_bb_requires_pop = jump;
+ gen_bb_requires_pop = !jump;
STACK_SHRINK(1);
STACK_GROW((jump ? 1 : 0));
DISPATCH();
@@ -2710,7 +2710,7 @@
// This gets set so BRANCH_BB knows whether to pop
// the type stack (type propagation) when generating the
// target BB
- gen_bb_requires_pop = jump;
+ gen_bb_requires_pop = !jump;
STACK_SHRINK(1);
STACK_GROW((jump ? 1 : 0));
DISPATCH();
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 786eff5afbcf23..17ffa81e673dd8 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -1,5 +1,5 @@
-// This file is generated by Tools\cases_generator\generate_cases.py @TODO: make this a seperate argument
-// from Python\bytecodes.c
+// This file is generated by Tools/cases_generator/generate_cases.py @TODO: make this a seperate argument
+// from Python/bytecodes.c
// Do not edit!
TARGET(NOP) {
@@ -91,7 +91,7 @@
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyLong_Type);
break;
}
@@ -99,7 +99,7 @@
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyFloat_Type);
break;
}
@@ -107,7 +107,7 @@
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyLong_Type);
break;
}
@@ -115,7 +115,7 @@
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyFloat_Type);
break;
}
@@ -123,7 +123,7 @@
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyUnicode_Type);
break;
}
@@ -138,7 +138,7 @@
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyFloat_Type);
break;
}
@@ -146,7 +146,7 @@
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyLong_Type);
break;
}
@@ -283,8 +283,8 @@
}
TARGET(RAISE_VARARGS) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- STACK_SHRINK(oparg);
+ fprintf(stderr, "Type propagation across `RAISE_VARARGS` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
@@ -324,34 +324,32 @@
}
TARGET(SEND) {
- PyTypeObject *v = TYPESTACK_PEEK(1);
- PyTypeObject *receiver = TYPESTACK_PEEK(2);
- TYPESTACK_POKE(1, NULL);
+ fprintf(stderr, "Type propagation across `SEND` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(SEND_GEN) {
- PyTypeObject *v = TYPESTACK_PEEK(1);
- PyTypeObject *receiver = TYPESTACK_PEEK(2);
- STACK_SHRINK(1);
+ fprintf(stderr, "Type propagation across `SEND_GEN` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(YIELD_VALUE) {
- PyTypeObject *retval = TYPESTACK_PEEK(1);
+ fprintf(stderr, "Type propagation across `YIELD_VALUE` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(POP_EXCEPT) {
- PyTypeObject *exc_value = TYPESTACK_PEEK(1);
- STACK_SHRINK(1);
+ fprintf(stderr, "Type propagation across `POP_EXCEPT` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(RERAISE) {
- PyTypeObject *exc = TYPESTACK_PEEK(1);
- PyTypeObject **values = &TYPESTACK_PEEK(1 + oparg);
- STACK_SHRINK(1);
+ fprintf(stderr, "Type propagation across `RERAISE` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
@@ -485,12 +483,14 @@
}
TARGET(DELETE_FAST) {
- TYPELOCALS_SET(oparg, NULL)
+ fprintf(stderr, "Type propagation across `DELETE_FAST` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(MAKE_CELL) {
- TYPELOCALS_SET(oparg, NULL)
+ fprintf(stderr, "Type propagation across `MAKE_CELL` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
@@ -505,8 +505,8 @@
}
TARGET(LOAD_DEREF) {
- STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ fprintf(stderr, "Type propagation across `LOAD_DEREF` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
@@ -524,7 +524,7 @@
PyTypeObject **pieces = &TYPESTACK_PEEK(oparg);
STACK_SHRINK(oparg);
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyUnicode_Type);
break;
}
@@ -532,7 +532,7 @@
PyTypeObject **values = &TYPESTACK_PEEK(oparg);
STACK_SHRINK(oparg);
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyTuple_Type);
break;
}
@@ -540,7 +540,7 @@
PyTypeObject **values = &TYPESTACK_PEEK(oparg);
STACK_SHRINK(oparg);
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyList_Type);
break;
}
@@ -562,7 +562,7 @@
PyTypeObject **values = &TYPESTACK_PEEK(oparg);
STACK_SHRINK(oparg);
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PySet_Type);
break;
}
@@ -570,7 +570,7 @@
PyTypeObject **values = &TYPESTACK_PEEK(oparg*2);
STACK_SHRINK(oparg*2);
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyDict_Type);
break;
}
@@ -582,7 +582,7 @@
PyTypeObject *keys = TYPESTACK_PEEK(1);
PyTypeObject **values = &TYPESTACK_PEEK(1 + oparg);
STACK_SHRINK(oparg);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyDict_Type);
break;
}
@@ -726,7 +726,7 @@
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyBool_Type);
break;
}
@@ -734,7 +734,7 @@
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyBool_Type);
break;
}
@@ -749,7 +749,7 @@
TARGET(CHECK_EXC_MATCH) {
PyTypeObject *right = TYPESTACK_PEEK(1);
PyTypeObject *left = TYPESTACK_PEEK(2);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyBool_Type);
break;
}
@@ -829,25 +829,25 @@
}
TARGET(JUMP_IF_FALSE_OR_POP) {
- fprintf(stderr, "Type propagation across `{self.name}` shouldn't be handled statically!\n");
+ fprintf(stderr, "Type propagation across `JUMP_IF_FALSE_OR_POP` shouldn't be handled statically!\n");
Py_UNREACHABLE();
break;
}
TARGET(BB_TEST_IF_FALSE_OR_POP) {
- fprintf(stderr, "Type propagation across `{self.name}` shouldn't be handled statically!\n");
+ fprintf(stderr, "Type propagation across `BB_TEST_IF_FALSE_OR_POP` shouldn't be handled statically!\n");
Py_UNREACHABLE();
break;
}
TARGET(JUMP_IF_TRUE_OR_POP) {
- fprintf(stderr, "Type propagation across `{self.name}` shouldn't be handled statically!\n");
+ fprintf(stderr, "Type propagation across `JUMP_IF_TRUE_OR_POP` shouldn't be handled statically!\n");
Py_UNREACHABLE();
break;
}
TARGET(BB_TEST_IF_TRUE_OR_POP) {
- fprintf(stderr, "Type propagation across `{self.name}` shouldn't be handled statically!\n");
+ fprintf(stderr, "Type propagation across `BB_TEST_IF_TRUE_OR_POP` shouldn't be handled statically!\n");
Py_UNREACHABLE();
break;
}
@@ -859,7 +859,7 @@
TARGET(GET_LEN) {
PyTypeObject *obj = TYPESTACK_PEEK(1);
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, &PyLong_Type);
break;
}
@@ -873,24 +873,20 @@
}
TARGET(MATCH_MAPPING) {
- PyTypeObject *subject = TYPESTACK_PEEK(1);
- STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ fprintf(stderr, "Type propagation across `MATCH_MAPPING` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(MATCH_SEQUENCE) {
- PyTypeObject *subject = TYPESTACK_PEEK(1);
- STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ fprintf(stderr, "Type propagation across `MATCH_SEQUENCE` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(MATCH_KEYS) {
- PyTypeObject *keys = TYPESTACK_PEEK(1);
- PyTypeObject *subject = TYPESTACK_PEEK(2);
- STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ fprintf(stderr, "Type propagation across `MATCH_KEYS` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
@@ -964,19 +960,14 @@
}
TARGET(WITH_EXCEPT_START) {
- PyTypeObject *val = TYPESTACK_PEEK(1);
- PyTypeObject *lasti = TYPESTACK_PEEK(3);
- PyTypeObject *exit_func = TYPESTACK_PEEK(4);
- STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ fprintf(stderr, "Type propagation across `WITH_EXCEPT_START` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(PUSH_EXC_INFO) {
- PyTypeObject *new_exc = TYPESTACK_PEEK(1);
- STACK_GROW(1);
- TYPESTACK_POKE(1, new_exc);
- TYPESTACK_POKE(2, NULL);
+ fprintf(stderr, "Type propagation across `PUSH_EXC_INFO` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
@@ -1225,7 +1216,7 @@
TARGET(COPY) {
PyTypeObject *bottom = TYPESTACK_PEEK(1 + (oparg-1));
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPESTACK_POKE(1, bottom);
break;
}
@@ -1246,6 +1237,8 @@
}
TARGET(EXTENDED_ARG) {
+ fprintf(stderr, "Type propagation across `EXTENDED_ARG` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 3c37a47d21c2cf..58ed38b65221e5 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -16,7 +16,7 @@
from enum import Enum, auto
import parser
-from parser import StackEffect, StackVarTypeLiteral, StackVarTypeIndex
+from parser import StackEffect, StackVarTypeLiteral, StackVarTypeIndex, StackVarInputVar
from parser import LocalEffect, LocalEffectVarLiteral, LocalEffectVarStack
HERE = os.path.dirname(__file__)
@@ -49,13 +49,29 @@
UNUSED = "unused"
BITS_PER_CODE_UNIT = 16
-# Type propagation across these instructions are forbidden
-# due to conditional effects that can't be determined statically
-# The handling of type propagation across these opcodes are handled elsewhere
-# within tier2.
TYPE_PROPAGATOR_FORBIDDEN = [
+ # Type propagator shouldn't see these
"JUMP_IF_FALSE_OR_POP",
- "JUMP_IF_TRUE_OR_POP", # Type propagator shouldn't see these
+ "JUMP_IF_TRUE_OR_POP",
+ "SEND",
+ "SEND_GEN",
+ "YIELD_VALUE",
+ "RAISE_VARARGS",
+ "PUSH_EXC_INFO",
+ "RERAISE",
+ "POP_EXCEPT",
+ "LOAD_DEREF",
+ "MAKE_CELL",
+ "DELETE_FAST",
+ "MATCH_MAPPING",
+ "MATCH_SEQUENCE",
+ "MATCH_KEYS",
+ "EXTENDED_ARG",
+ "WITH_EXCEPT_START",
+ # Type propagation across these instructions are forbidden
+ # due to conditional effects that can't be determined statically
+ # The handling of type propagation across these opcodes are handled elsewhere
+ # within tier2.
"BB_TEST_IF_FALSE_OR_POP",
"BB_TEST_IF_TRUE_OR_POP" # Type propagator handles this in BB_BRANCH
]
@@ -317,7 +333,7 @@ def write_typeprop(self, out: Formatter) -> None:
"""Write one instruction's type propagation rules"""
if self.name in TYPE_PROPAGATOR_FORBIDDEN:
- out.emit('fprintf(stderr, "Type propagation across `{self.name}` shouldn\'t be handled statically!\\n");')
+ out.emit(f'fprintf(stderr, "Type propagation across `{self.name}` shouldn\'t be handled statically!\\n");')
out.emit("Py_UNREACHABLE();")
return
@@ -379,6 +395,10 @@ def write_typeprop(self, out: Formatter) -> None:
if val != "NULL": val = f"&{val}"
case StackVarTypeIndex(array=arr, index=idx):
val = f"{'TYPELOCALS_GET' if arr == 'locals' else 'TYPECONST_GET'}({idx})"
+ case StackVarInputVar(name=val):
+ ieffect, j = all_input_effect_names[val]
+ if len(oeffects) - i == len(ieffects) - j:
+ continue
case _:
typing.assert_never(typ)
if oeffect.cond:
@@ -386,20 +406,6 @@ def write_typeprop(self, out: Formatter) -> None:
else:
out.emit(f"TYPESTACK_POKE({osize}, {val});")
continue
-
- # Check if it's part of input effect
- # TODO: Can we assume that they have the same type?
- if oeffect.name in all_input_effect_names:
- ieffect, j = all_input_effect_names[oeffect.name]
- assert not ieffect.cond, \
- "`cond` stackvar not supported for type prop"
- # The stack var stays at the same pos
- if len(oeffects) - i == len(ieffects) - j: continue
- if oeffect.cond:
- out.emit(f"if ({oeffect.cond}) {{ TYPESTACK_POKE({osize}, {oeffect.name}); }}")
- else:
- out.emit(f"TYPESTACK_POKE({osize}, {oeffect.name});")
- continue
# Just output null
if oeffect.cond:
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 1e84119854edf2..d4feee6599fe7c 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -78,8 +78,12 @@ class StackVarTypeIndex(Node):
array: Literal["locals", "consts"]
index: str
+@dataclass
+class StackVarInputVar(Node):
+ name: str
+
-StackVarType: TypeAlias = StackVarTypeLiteral | StackVarTypeIndex
+StackVarType: TypeAlias = StackVarTypeLiteral | StackVarTypeIndex | StackVarInputVar
@dataclass
@@ -91,6 +95,9 @@ class StackEffect(Node):
size: str = "" # Optional `[size]`
# Note: size cannot be combined with type or cond
+ def __eq__(self, other: 'StackEffect') -> bool:
+ return self.name == other.name
+
@dataclass
class Expression(Node):
@@ -270,7 +277,13 @@ def inputs(self) -> list[InputEffect] | None:
@contextual
def input(self) -> InputEffect | None:
- return self.cache_effect() or self.stack_effect()
+ if r := self.cache_effect():
+ return r
+ r = self.stack_effect()
+ if r is None: return r
+ assert r.type_annotation is None, \
+ "Type annotations aren't allowed in input stack effect."
+ return r
def outputs(self) -> list[OutputEffect] | None:
# output (, output)*
@@ -344,10 +357,16 @@ def stackvar_type(self) -> StackVarType | None:
return StackVarTypeIndex(
"locals" if idstr == "locals" else "consts",
index)
+ elif self.expect(lx.TIMES):
+ id = self.require(lx.IDENTIFIER)
+ return StackVarInputVar(id.text.strip())
+
@contextual
def local_effect(self) -> LocalEffect | None:
- if self.expect(lx.IDENTIFIER).text.strip() == "locals":
+ if tok := self.expect(lx.IDENTIFIER):
+ if tok.text.strip() != "locals":
+ return
self.require(lx.LBRACKET)
if id := self.expect(lx.IDENTIFIER):
index = id.text.strip()
From d8c6edce966b2c136a8ea31f653185730f823b54 Mon Sep 17 00:00:00 2001
From: Julia
Date: Fri, 3 Mar 2023 12:14:17 +0800
Subject: [PATCH 058/280] Perf: Removed unneccessary declarations in type
propagator
Updated DSL to not require type annotations for unmoved stack variables
---
Python/bytecodes.c | 30 ++--
Python/tier2_typepropagator.c.h | 221 ------------------------
Tools/cases_generator/generate_cases.py | 30 +++-
3 files changed, 43 insertions(+), 238 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 35cc2176615d58..05436c842ddb68 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -440,12 +440,12 @@ dummy_func(
DISPATCH_INLINED(new_frame);
}
- inst(LIST_APPEND, (list, unused[oparg-1], v -- list: *list, unused[oparg-1])) {
+ inst(LIST_APPEND, (list, unused[oparg-1], v -- list, unused[oparg-1])) {
ERROR_IF(_PyList_AppendTakeRef((PyListObject *)list, v) < 0, error);
PREDICT(JUMP_BACKWARD);
}
- inst(SET_ADD, (set, unused[oparg-1], v -- set: *set, unused[oparg-1])) {
+ inst(SET_ADD, (set, unused[oparg-1], v -- set, unused[oparg-1])) {
int err = PySet_Add(set, v);
Py_DECREF(v);
ERROR_IF(err, error);
@@ -628,7 +628,7 @@ dummy_func(
}
}
- inst(GET_ANEXT, (aiter -- aiter: *aiter, awaitable)) {
+ inst(GET_ANEXT, (aiter -- aiter, awaitable)) {
unaryfunc getter = NULL;
PyObject *next_iter = NULL;
PyTypeObject *type = Py_TYPE(aiter);
@@ -1292,7 +1292,7 @@ dummy_func(
ERROR_IF(list == NULL, error);
}
- inst(LIST_EXTEND, (list, unused[oparg-1], iterable -- list: *list, unused[oparg-1])) {
+ inst(LIST_EXTEND, (list, unused[oparg-1], iterable -- list, unused[oparg-1])) {
PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable);
if (none_val == NULL) {
if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) &&
@@ -1310,7 +1310,7 @@ dummy_func(
DECREF_INPUTS();
}
- inst(SET_UPDATE, (set, unused[oparg-1], iterable -- set: *set, unused[oparg-1])) {
+ inst(SET_UPDATE, (set, unused[oparg-1], iterable -- set, unused[oparg-1])) {
int err = _PySet_Update(set, iterable);
DECREF_INPUTS();
ERROR_IF(err < 0, error);
@@ -1869,7 +1869,7 @@ dummy_func(
}
}
- inst(CHECK_EXC_MATCH, (left, right -- left: *left, b: PyBool_Type)) {
+ inst(CHECK_EXC_MATCH, (left, right -- left, b: PyBool_Type)) {
assert(PyExceptionInstance_Check(left));
if (check_except_type_valid(tstate, right) < 0) {
DECREF_INPUTS();
@@ -1888,7 +1888,7 @@ dummy_func(
ERROR_IF(res == NULL, error);
}
- inst(IMPORT_FROM, (from -- from: *from, res)) {
+ inst(IMPORT_FROM, (from -- from, res)) {
PyObject *name = GETITEM(names, oparg);
res = import_from(tstate, from, name);
ERROR_IF(res == NULL, error);
@@ -2155,7 +2155,7 @@ dummy_func(
JUMPBY(-oparg);
}
- inst(GET_LEN, (obj -- obj: *obj, len_o: PyLong_Type)) {
+ inst(GET_LEN, (obj -- obj, len_o: PyLong_Type)) {
// PUSH(len(TOS))
Py_ssize_t len_i = PyObject_Length(obj);
ERROR_IF(len_i < 0, error);
@@ -2245,7 +2245,7 @@ dummy_func(
FOR_ITER_GEN,
};
- inst(FOR_ITER, (unused/1, iter -- iter: *iter, next)) {
+ inst(FOR_ITER, (unused/1, iter -- iter, next)) {
#if ENABLE_SPECIALIZATION
_PyForIterCache *cache = (_PyForIterCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
@@ -2281,7 +2281,7 @@ dummy_func(
}
// FOR_ITER
- inst(BB_TEST_ITER, (unused/1, iter -- iter: *iter, next)) {
+ inst(BB_TEST_ITER, (unused/1, iter -- iter, next)) {
next = (*Py_TYPE(iter)->tp_iternext)(iter);
if (next == NULL) {
if (_PyErr_Occurred(tstate)) {
@@ -2303,7 +2303,7 @@ dummy_func(
bb_test = true;
}
- inst(FOR_ITER_LIST, (unused/1, iter -- iter: *iter, next)) {
+ inst(FOR_ITER_LIST, (unused/1, iter -- iter, next)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER);
_PyListIterObject *it = (_PyListIterObject *)iter;
@@ -2326,7 +2326,7 @@ dummy_func(
// Common case: no jump, leave it to the code generator
}
- inst(FOR_ITER_TUPLE, (unused/1, iter -- iter: *iter, next)) {
+ inst(FOR_ITER_TUPLE, (unused/1, iter -- iter, next)) {
assert(cframe.use_tracing == 0);
_PyTupleIterObject *it = (_PyTupleIterObject *)iter;
DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER);
@@ -2349,7 +2349,7 @@ dummy_func(
// Common case: no jump, leave it to the code generator
}
- inst(FOR_ITER_RANGE, (unused/1, iter -- iter: *iter, next)) {
+ inst(FOR_ITER_RANGE, (unused/1, iter -- iter, next)) {
assert(cframe.use_tracing == 0);
_PyRangeIterObject *r = (_PyRangeIterObject *)iter;
DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER);
@@ -2370,7 +2370,7 @@ dummy_func(
}
}
- inst(FOR_ITER_GEN, (unused/1, iter -- iter: *iter, unused)) {
+ inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) {
assert(cframe.use_tracing == 0);
PyGenObject *gen = (PyGenObject *)iter;
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER);
@@ -3247,7 +3247,7 @@ dummy_func(
}
}
- inst(COPY, (bottom, unused[oparg-1] -- bottom: *bottom, unused[oparg-1], top: *bottom)) {
+ inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top: *bottom)) {
assert(oparg > 0);
top = Py_NewRef(bottom);
}
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 17ffa81e673dd8..787d956eab0e72 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -46,7 +46,6 @@
}
TARGET(POP_TOP) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
@@ -59,224 +58,164 @@
TARGET(END_FOR) {
{
- PyTypeObject *value = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
}
{
- PyTypeObject *value = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
}
break;
}
TARGET(UNARY_NEGATIVE) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(UNARY_NOT) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(UNARY_INVERT) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(BINARY_OP_MULTIPLY_INT) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyLong_Type);
break;
}
TARGET(BINARY_OP_MULTIPLY_FLOAT) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyFloat_Type);
break;
}
TARGET(BINARY_OP_SUBTRACT_INT) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyLong_Type);
break;
}
TARGET(BINARY_OP_SUBTRACT_FLOAT) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyFloat_Type);
break;
}
TARGET(BINARY_OP_ADD_UNICODE) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyUnicode_Type);
break;
}
TARGET(BINARY_OP_INPLACE_ADD_UNICODE) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(BINARY_OP_ADD_FLOAT) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyFloat_Type);
break;
}
TARGET(BINARY_OP_ADD_INT) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyLong_Type);
break;
}
TARGET(BINARY_CHECK_INT) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
TYPESTACK_POKE(1, &PyLong_Type);
TYPESTACK_POKE(2, &PyLong_Type);
break;
}
TARGET(BINARY_OP_ADD_INT_REST) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyLong_Type);
break;
}
TARGET(BINARY_SUBSCR) {
- PyTypeObject *sub = TYPESTACK_PEEK(1);
- PyTypeObject *container = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(BINARY_SLICE) {
- PyTypeObject *stop = TYPESTACK_PEEK(1);
- PyTypeObject *start = TYPESTACK_PEEK(2);
- PyTypeObject *container = TYPESTACK_PEEK(3);
STACK_SHRINK(2);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(STORE_SLICE) {
- PyTypeObject *stop = TYPESTACK_PEEK(1);
- PyTypeObject *start = TYPESTACK_PEEK(2);
- PyTypeObject *container = TYPESTACK_PEEK(3);
- PyTypeObject *v = TYPESTACK_PEEK(4);
STACK_SHRINK(4);
break;
}
TARGET(BINARY_SUBSCR_LIST_INT) {
- PyTypeObject *sub = TYPESTACK_PEEK(1);
- PyTypeObject *list = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(BINARY_SUBSCR_TUPLE_INT) {
- PyTypeObject *sub = TYPESTACK_PEEK(1);
- PyTypeObject *tuple = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(BINARY_SUBSCR_DICT) {
- PyTypeObject *sub = TYPESTACK_PEEK(1);
- PyTypeObject *dict = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(BINARY_SUBSCR_GETITEM) {
- PyTypeObject *sub = TYPESTACK_PEEK(1);
- PyTypeObject *container = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
break;
}
TARGET(LIST_APPEND) {
- PyTypeObject *v = TYPESTACK_PEEK(1);
- PyTypeObject *list = TYPESTACK_PEEK(2 + (oparg-1));
STACK_SHRINK(1);
break;
}
TARGET(SET_ADD) {
- PyTypeObject *v = TYPESTACK_PEEK(1);
- PyTypeObject *set = TYPESTACK_PEEK(2 + (oparg-1));
STACK_SHRINK(1);
break;
}
TARGET(STORE_SUBSCR) {
- PyTypeObject *sub = TYPESTACK_PEEK(1);
- PyTypeObject *container = TYPESTACK_PEEK(2);
- PyTypeObject *v = TYPESTACK_PEEK(3);
STACK_SHRINK(3);
break;
}
TARGET(STORE_SUBSCR_LIST_INT) {
- PyTypeObject *sub = TYPESTACK_PEEK(1);
- PyTypeObject *list = TYPESTACK_PEEK(2);
- PyTypeObject *value = TYPESTACK_PEEK(3);
STACK_SHRINK(3);
break;
}
TARGET(STORE_SUBSCR_DICT) {
- PyTypeObject *sub = TYPESTACK_PEEK(1);
- PyTypeObject *dict = TYPESTACK_PEEK(2);
- PyTypeObject *value = TYPESTACK_PEEK(3);
STACK_SHRINK(3);
break;
}
TARGET(DELETE_SUBSCR) {
- PyTypeObject *sub = TYPESTACK_PEEK(1);
- PyTypeObject *container = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(CALL_INTRINSIC_1) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(CALL_INTRINSIC_2) {
- PyTypeObject *value1 = TYPESTACK_PEEK(1);
- PyTypeObject *value2 = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
break;
@@ -289,13 +228,11 @@
}
TARGET(INTERPRETER_EXIT) {
- PyTypeObject *retval = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(RETURN_VALUE) {
- PyTypeObject *retval = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
@@ -305,20 +242,17 @@
}
TARGET(GET_AITER) {
- PyTypeObject *obj = TYPESTACK_PEEK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(GET_ANEXT) {
- PyTypeObject *aiter = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(GET_AWAITABLE) {
- PyTypeObject *iterable = TYPESTACK_PEEK(1);
TYPESTACK_POKE(1, NULL);
break;
}
@@ -354,16 +288,11 @@
}
TARGET(END_ASYNC_FOR) {
- PyTypeObject *exc = TYPESTACK_PEEK(1);
- PyTypeObject *awaitable = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(CLEANUP_THROW) {
- PyTypeObject *exc_value = TYPESTACK_PEEK(1);
- PyTypeObject *last_sent_val = TYPESTACK_PEEK(2);
- PyTypeObject *sub_iter = TYPESTACK_PEEK(3);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
TYPESTACK_POKE(2, NULL);
@@ -383,7 +312,6 @@
}
TARGET(STORE_NAME) {
- PyTypeObject *v = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
@@ -393,14 +321,12 @@
}
TARGET(UNPACK_SEQUENCE) {
- PyTypeObject *seq = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
STACK_GROW(oparg);
break;
}
TARGET(UNPACK_SEQUENCE_TWO_TUPLE) {
- PyTypeObject *seq = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
STACK_GROW(oparg);
TYPESTACK_POKE(oparg, NULL);
@@ -408,7 +334,6 @@
}
TARGET(UNPACK_SEQUENCE_TUPLE) {
- PyTypeObject *seq = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
STACK_GROW(oparg);
TYPESTACK_POKE(oparg, NULL);
@@ -416,7 +341,6 @@
}
TARGET(UNPACK_SEQUENCE_LIST) {
- PyTypeObject *seq = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
STACK_GROW(oparg);
TYPESTACK_POKE(oparg, NULL);
@@ -424,26 +348,21 @@
}
TARGET(UNPACK_EX) {
- PyTypeObject *seq = TYPESTACK_PEEK(1);
STACK_GROW((oparg & 0xFF) + (oparg >> 8));
break;
}
TARGET(STORE_ATTR) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
- PyTypeObject *v = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(DELETE_ATTR) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(STORE_GLOBAL) {
- PyTypeObject *v = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
@@ -511,7 +430,6 @@
}
TARGET(STORE_DEREF) {
- PyTypeObject *v = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
@@ -521,7 +439,6 @@
}
TARGET(BUILD_STRING) {
- PyTypeObject **pieces = &TYPESTACK_PEEK(oparg);
STACK_SHRINK(oparg);
STACK_GROW(1);
TYPESTACK_POKE(1, &PyUnicode_Type);
@@ -529,7 +446,6 @@
}
TARGET(BUILD_TUPLE) {
- PyTypeObject **values = &TYPESTACK_PEEK(oparg);
STACK_SHRINK(oparg);
STACK_GROW(1);
TYPESTACK_POKE(1, &PyTuple_Type);
@@ -537,7 +453,6 @@
}
TARGET(BUILD_LIST) {
- PyTypeObject **values = &TYPESTACK_PEEK(oparg);
STACK_SHRINK(oparg);
STACK_GROW(1);
TYPESTACK_POKE(1, &PyList_Type);
@@ -545,21 +460,16 @@
}
TARGET(LIST_EXTEND) {
- PyTypeObject *iterable = TYPESTACK_PEEK(1);
- PyTypeObject *list = TYPESTACK_PEEK(2 + (oparg-1));
STACK_SHRINK(1);
break;
}
TARGET(SET_UPDATE) {
- PyTypeObject *iterable = TYPESTACK_PEEK(1);
- PyTypeObject *set = TYPESTACK_PEEK(2 + (oparg-1));
STACK_SHRINK(1);
break;
}
TARGET(BUILD_SET) {
- PyTypeObject **values = &TYPESTACK_PEEK(oparg);
STACK_SHRINK(oparg);
STACK_GROW(1);
TYPESTACK_POKE(1, &PySet_Type);
@@ -567,7 +477,6 @@
}
TARGET(BUILD_MAP) {
- PyTypeObject **values = &TYPESTACK_PEEK(oparg*2);
STACK_SHRINK(oparg*2);
STACK_GROW(1);
TYPESTACK_POKE(1, &PyDict_Type);
@@ -579,34 +488,27 @@
}
TARGET(BUILD_CONST_KEY_MAP) {
- PyTypeObject *keys = TYPESTACK_PEEK(1);
- PyTypeObject **values = &TYPESTACK_PEEK(1 + oparg);
STACK_SHRINK(oparg);
TYPESTACK_POKE(1, &PyDict_Type);
break;
}
TARGET(DICT_UPDATE) {
- PyTypeObject *update = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(DICT_MERGE) {
- PyTypeObject *update = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(MAP_ADD) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
- PyTypeObject *key = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(LOAD_ATTR) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
@@ -614,7 +516,6 @@
}
TARGET(LOAD_ATTR_INSTANCE_VALUE) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
@@ -622,7 +523,6 @@
}
TARGET(LOAD_ATTR_MODULE) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
@@ -630,7 +530,6 @@
}
TARGET(LOAD_ATTR_WITH_HINT) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
@@ -638,7 +537,6 @@
}
TARGET(LOAD_ATTR_SLOT) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
@@ -646,7 +544,6 @@
}
TARGET(LOAD_ATTR_CLASS) {
- PyTypeObject *cls = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
@@ -654,115 +551,86 @@
}
TARGET(LOAD_ATTR_PROPERTY) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
break;
}
TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
break;
}
TARGET(STORE_ATTR_INSTANCE_VALUE) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
- PyTypeObject *value = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(STORE_ATTR_WITH_HINT) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
- PyTypeObject *value = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(STORE_ATTR_SLOT) {
- PyTypeObject *owner = TYPESTACK_PEEK(1);
- PyTypeObject *value = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(COMPARE_OP) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(COMPARE_AND_BRANCH) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(COMPARE_AND_BRANCH_FLOAT) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(COMPARE_AND_BRANCH_INT) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(COMPARE_AND_BRANCH_STR) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(2);
break;
}
TARGET(IS_OP) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyBool_Type);
break;
}
TARGET(CONTAINS_OP) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyBool_Type);
break;
}
TARGET(CHECK_EG_MATCH) {
- PyTypeObject *match_type = TYPESTACK_PEEK(1);
- PyTypeObject *exc_value = TYPESTACK_PEEK(2);
TYPESTACK_POKE(1, NULL);
TYPESTACK_POKE(2, NULL);
break;
}
TARGET(CHECK_EXC_MATCH) {
- PyTypeObject *right = TYPESTACK_PEEK(1);
- PyTypeObject *left = TYPESTACK_PEEK(2);
TYPESTACK_POKE(1, &PyBool_Type);
break;
}
TARGET(IMPORT_NAME) {
- PyTypeObject *fromlist = TYPESTACK_PEEK(1);
- PyTypeObject *level = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(IMPORT_FROM) {
- PyTypeObject *from = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, NULL);
break;
@@ -781,49 +649,41 @@
}
TARGET(POP_JUMP_IF_FALSE) {
- PyTypeObject *cond = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(BB_TEST_POP_IF_FALSE) {
- PyTypeObject *cond = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(POP_JUMP_IF_TRUE) {
- PyTypeObject *cond = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(BB_TEST_POP_IF_TRUE) {
- PyTypeObject *cond = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(POP_JUMP_IF_NOT_NONE) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(BB_TEST_POP_IF_NOT_NONE) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(POP_JUMP_IF_NONE) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
TARGET(BB_TEST_POP_IF_NONE) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
STACK_SHRINK(1);
break;
}
@@ -857,16 +717,12 @@
}
TARGET(GET_LEN) {
- PyTypeObject *obj = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, &PyLong_Type);
break;
}
TARGET(MATCH_CLASS) {
- PyTypeObject *names = TYPESTACK_PEEK(1);
- PyTypeObject *type = TYPESTACK_PEEK(2);
- PyTypeObject *subject = TYPESTACK_PEEK(3);
STACK_SHRINK(2);
TYPESTACK_POKE(1, NULL);
break;
@@ -891,60 +747,51 @@
}
TARGET(GET_ITER) {
- PyTypeObject *iterable = TYPESTACK_PEEK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(GET_YIELD_FROM_ITER) {
- PyTypeObject *iterable = TYPESTACK_PEEK(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(FOR_ITER) {
- PyTypeObject *iter = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(BB_TEST_ITER) {
- PyTypeObject *iter = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(FOR_ITER_LIST) {
- PyTypeObject *iter = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(FOR_ITER_TUPLE) {
- PyTypeObject *iter = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(FOR_ITER_RANGE) {
- PyTypeObject *iter = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, NULL);
break;
}
TARGET(FOR_ITER_GEN) {
- PyTypeObject *iter = TYPESTACK_PEEK(1);
STACK_GROW(1);
break;
}
TARGET(BEFORE_ASYNC_WITH) {
- PyTypeObject *mgr = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, NULL);
TYPESTACK_POKE(2, NULL);
@@ -952,7 +799,6 @@
}
TARGET(BEFORE_WITH) {
- PyTypeObject *mgr = TYPESTACK_PEEK(1);
STACK_GROW(1);
TYPESTACK_POKE(1, NULL);
TYPESTACK_POKE(2, NULL);
@@ -972,7 +818,6 @@
}
TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
- PyTypeObject *self = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
@@ -980,7 +825,6 @@
}
TARGET(LOAD_ATTR_METHOD_NO_DICT) {
- PyTypeObject *self = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
@@ -988,7 +832,6 @@
}
TARGET(LOAD_ATTR_METHOD_LAZY_DICT) {
- PyTypeObject *self = TYPESTACK_PEEK(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
@@ -1000,9 +843,6 @@
}
TARGET(CALL) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1010,35 +850,24 @@
}
TARGET(CALL_BOUND_METHOD_EXACT_ARGS) {
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
break;
}
TARGET(CALL_PY_EXACT_ARGS) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
break;
}
TARGET(CALL_PY_WITH_DEFAULTS) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
break;
}
TARGET(CALL_NO_KW_TYPE_1) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *null = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1046,9 +875,6 @@
}
TARGET(CALL_NO_KW_STR_1) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *null = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1056,9 +882,6 @@
}
TARGET(CALL_NO_KW_TUPLE_1) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *null = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1066,9 +889,6 @@
}
TARGET(CALL_BUILTIN_CLASS) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1076,9 +896,6 @@
}
TARGET(CALL_NO_KW_BUILTIN_O) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1086,9 +903,6 @@
}
TARGET(CALL_NO_KW_BUILTIN_FAST) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1096,9 +910,6 @@
}
TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1106,9 +917,6 @@
}
TARGET(CALL_NO_KW_LEN) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1116,9 +924,6 @@
}
TARGET(CALL_NO_KW_ISINSTANCE) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *callable = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1126,17 +931,12 @@
}
TARGET(CALL_NO_KW_LIST_APPEND) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *self = TYPESTACK_PEEK(1 + oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
break;
}
TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1144,8 +944,6 @@
}
TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1153,8 +951,6 @@
}
TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1162,8 +958,6 @@
}
TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) {
- PyTypeObject **args = &TYPESTACK_PEEK(oparg);
- PyTypeObject *method = TYPESTACK_PEEK(2 + oparg);
STACK_SHRINK(oparg);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1171,9 +965,6 @@
}
TARGET(CALL_FUNCTION_EX) {
- PyTypeObject *kwargs = (oparg & 1) ? TYPESTACK_PEEK(((oparg & 1) ? 1 : 0)) : NULL;
- PyTypeObject *callargs = TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0));
- PyTypeObject *func = TYPESTACK_PEEK(2 + ((oparg & 1) ? 1 : 0));
STACK_SHRINK(((oparg & 1) ? 1 : 0));
STACK_SHRINK(2);
TYPESTACK_POKE(1, NULL);
@@ -1181,11 +972,6 @@
}
TARGET(MAKE_FUNCTION) {
- PyTypeObject *codeobj = TYPESTACK_PEEK(1);
- PyTypeObject *closure = (oparg & 0x08) ? TYPESTACK_PEEK(1 + ((oparg & 0x08) ? 1 : 0)) : NULL;
- PyTypeObject *annotations = (oparg & 0x04) ? TYPESTACK_PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0)) : NULL;
- PyTypeObject *kwdefaults = (oparg & 0x02) ? TYPESTACK_PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0)) : NULL;
- PyTypeObject *defaults = (oparg & 0x01) ? TYPESTACK_PEEK(1 + ((oparg & 0x08) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x01) ? 1 : 0)) : NULL;
STACK_SHRINK(((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
break;
@@ -1196,9 +982,6 @@
}
TARGET(BUILD_SLICE) {
- PyTypeObject *step = (oparg == 3) ? TYPESTACK_PEEK(((oparg == 3) ? 1 : 0)) : NULL;
- PyTypeObject *stop = TYPESTACK_PEEK(1 + ((oparg == 3) ? 1 : 0));
- PyTypeObject *start = TYPESTACK_PEEK(2 + ((oparg == 3) ? 1 : 0));
STACK_SHRINK(((oparg == 3) ? 1 : 0));
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
@@ -1206,8 +989,6 @@
}
TARGET(FORMAT_VALUE) {
- PyTypeObject *fmt_spec = ((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? TYPESTACK_PEEK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)) : NULL;
- PyTypeObject *value = TYPESTACK_PEEK(1 + (((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0));
STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0));
TYPESTACK_POKE(1, NULL);
break;
@@ -1221,8 +1002,6 @@
}
TARGET(BINARY_OP) {
- PyTypeObject *rhs = TYPESTACK_PEEK(1);
- PyTypeObject *lhs = TYPESTACK_PEEK(2);
STACK_SHRINK(1);
TYPESTACK_POKE(1, NULL);
break;
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 58ed38b65221e5..ab83895f1fabd2 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -337,11 +337,30 @@ def write_typeprop(self, out: Formatter) -> None:
out.emit("Py_UNREACHABLE();")
return
+ need_to_declare = []
+ # Stack input is used in local effect
+ if self.local_effects and \
+ isinstance(val := self.local_effects.value, LocalEffectVarStack):
+ need_to_declare.append(val.name)
+ # Stack input is used in output effect
+ for oeffect in self.output_effects:
+ if not (typ := oeffect.type_annotation): continue
+ if not isinstance(typ, StackVarInputVar): continue
+ if oeffect.name in self.unmoved_names:
+ print(
+ f"Warn: {self.name} type annotation for {oeffect.name} will be ignored "
+ "as it is unmoved")
+ continue
+ need_to_declare.append(typ.name)
+
# Write input stack effect variable declarations and initializations
ieffects = list(reversed(self.input_effects))
usable_for_local_effect = {}
all_input_effect_names = {}
for i, ieffect in enumerate(ieffects):
+
+ if ieffect.name not in need_to_declare: continue
+
isize = string_effect_size(
list_effect_size([ieff for ieff in ieffects[: i + 1]])
)
@@ -396,8 +415,11 @@ def write_typeprop(self, out: Formatter) -> None:
case StackVarTypeIndex(array=arr, index=idx):
val = f"{'TYPELOCALS_GET' if arr == 'locals' else 'TYPECONST_GET'}({idx})"
case StackVarInputVar(name=val):
- ieffect, j = all_input_effect_names[val]
- if len(oeffects) - i == len(ieffects) - j:
+ # We determined above that we don't need to write this stack effect
+ if val not in need_to_declare:
+ continue
+ # Unmoved var, don't need to write
+ if oeffect.name in self.unmoved_names:
continue
case _:
typing.assert_never(typ)
@@ -406,6 +428,10 @@ def write_typeprop(self, out: Formatter) -> None:
else:
out.emit(f"TYPESTACK_POKE({osize}, {val});")
continue
+
+ # Don't touch unmoved stack vars
+ if oeffect.name in self.unmoved_names:
+ continue
# Just output null
if oeffect.cond:
From 24ded88163a2c0e003a8ea1ecc7c423bd3def077 Mon Sep 17 00:00:00 2001
From: Julia
Date: Fri, 3 Mar 2023 12:23:36 +0800
Subject: [PATCH 059/280] Perf: Removed unneccessary type annotations in MATCH
bytecode
---
Python/bytecodes.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 05436c842ddb68..ef42a10c4d572e 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2178,19 +2178,19 @@ dummy_func(
}
}
- inst(MATCH_MAPPING, (subject -- subject: *subject, res: PyBool_Type)) {
+ inst(MATCH_MAPPING, (subject -- subject, res: PyBool_Type)) {
int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING;
res = Py_NewRef(match ? Py_True : Py_False);
PREDICT(POP_JUMP_IF_FALSE);
}
- inst(MATCH_SEQUENCE, (subject -- subject: *subject, res: PyBool_Type)) {
+ inst(MATCH_SEQUENCE, (subject -- subject, res: PyBool_Type)) {
int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE;
res = Py_NewRef(match ? Py_True : Py_False);
PREDICT(POP_JUMP_IF_FALSE);
}
- inst(MATCH_KEYS, (subject, keys -- subject: *subject, keys: *keys, values_or_none)) {
+ inst(MATCH_KEYS, (subject, keys -- subject, keys, values_or_none)) {
// On successful match, PUSH(values). Otherwise, PUSH(None).
values_or_none = match_keys(tstate, subject, keys);
ERROR_IF(values_or_none == NULL, error);
From 223894439bcd10720e6c841a35cd599ceca62eec Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 4 Mar 2023 16:32:55 +0800
Subject: [PATCH 060/280] Support multi-file inputs
---
Python/opcode_metadata.h | 5 +-
Python/tier2_typepropagator.c.h | 3 +-
Tools/cases_generator/generate_cases.py | 77 ++-----------------------
3 files changed, 9 insertions(+), 76 deletions(-)
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 97ad6a9dfe547b..dc15965d034a9b 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -1,5 +1,6 @@
-// This file is generated by Tools\cases_generator\generate_cases.py --metadata
-// from Python\bytecodes.c
+// This file is generated by Tools/cases_generator/generate_cases.py
+// from:
+// Python/bytecodes.c
// Do not edit!
#ifndef NEED_OPCODE_TABLES
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 787d956eab0e72..ffbc68a62ae403 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -1,5 +1,6 @@
// This file is generated by Tools/cases_generator/generate_cases.py @TODO: make this a seperate argument
-// from Python/bytecodes.c
+// from:
+// Python/bytecodes.c
// Do not edit!
TARGET(NOP) {
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 18d7f11dab6afe..5495b3c9f08f94 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -92,7 +92,6 @@
arg_parser.add_argument(
"-u",
- "--macromap",
action="store_true",
help=f"Generate macro to micro instruction map instead,"
f" along with the type for uop type guards"
@@ -717,6 +716,9 @@ def parse(self) -> None:
self.instrs = {}
self.supers = {}
self.macros = {}
+ self.macro_instrs = {}
+ self.macro_instdefs = []
+ self.u_insts = []
self.families = {}
instrs_idx: dict[str, int] = dict()
@@ -1128,77 +1130,6 @@ def write_function(
write_function("popped", popped_data)
write_function("pushed", pushed_data)
self.out.emit("")
-
- def write_macromap_and_typedata(self):
- with open(self.output_filename, "w") as f:
- # Write provenance header
- f.write(f"// This file is generated by {THIS} --macromap\n")
- f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n")
- f.write(f"// Do not edit!\n")
-
- # Create formatter; the rest of the code uses this
- self.out = Formatter(f, 0)
- # Header guard
- self.out.emit("")
- self.out.emit("#ifndef Py_INTERNAL_OPCODE_MACRO_TO_MICRO_H")
- self.out.emit("#define Py_INTERNAL_OPCODE_MACRO_TO_MICRO_H")
- self.out.emit("#ifdef __cplusplus")
- self.out.emit('extern "C" {')
- self.out.emit("#endif")
- self.out.emit("")
- self.out.emit("#ifndef Py_BUILD_CORE")
- self.out.emit('# error "this header requires Py_BUILD_CORE define"')
- self.out.emit("#endif")
- self.out.emit('#include "opcode.h"')
- self.out.emit("")
- self.write_macromap()
- self.write_uopguard_typedata()
- # Header guard end
- self.out.emit("#ifdef __cplusplus")
- self.out.emit("}")
- self.out.emit("#endif")
- self.out.emit("#endif // Py_INTERNAL_OPCODE_MACRO_TO_MICRO")
-
- def write_macromap(self):
- """Write the macro instruction to uop mapping to output file."""
- self.out.emit("extern const int _Py_MacroOpUOpCount[] = {")
- macro_instrdef_names = set()
- max_instr_len = 0
- for name, instr_def in self.instrs.items():
- if (macro_def := instr_def.inst) in self.macro_instdefs:
- u_insts = macro_def.u_insts
- instr_len = len(u_insts)
- max_instr_len = max(instr_len, max_instr_len)
- self.out.emit(f"[{macro_def.name}] = {instr_len},")
- macro_instrdef_names.add(macro_def.name)
- else:
- self.out.emit(f"[{name}] = 1,")
- self.out.emit("};")
- self.out.emit("")
- self.out.emit(f"extern const int _Py_MacroOpToUOp[][{max_instr_len}] = {{")
- for macro_def in self.macro_instdefs:
- u_insts = macro_def.u_insts
- self.out.emit(f"[{macro_def.name}] = {{{', '.join(u_insts)}}},")
- self.out.emit("};")
-
- def write_uopguard_typedata(self):
- """Write the type information expected of each uop typeguard."""
- uop_to_type_output = {}
- max_types = 0
- for instr_def in self.u_insts:
- types = []
- for output in instr_def.outputs:
- if isinstance(output, StackEffect) and output.type:
- types.append(output.type)
- if types:
- max_types = max(max_types, len(types))
- uop_to_type_output[instr_def.name] = types
- if max_types > 0:
- self.out.emit(f"extern const PyTypeObject *_Py_UOpGuardTypes[][{max_types}] = {{")
- for name, types in uop_to_type_output.items():
- self.out.emit(f"[{name}] = {{{', '.join(['&' + type_ for type_ in types])}}},")
- self.out.emit("};")
-
def write_typepropagator(self) -> None:
"""Write the type propagator"""
@@ -1206,7 +1137,7 @@ def write_typepropagator(self) -> None:
with open(self.output_filename, "w") as f:
# Write provenance header
f.write(f"// This file is generated by {THIS} @TODO: make this a seperate argument\n")
- f.write(f"// from {os.path.relpath(self.filename, ROOT).replace(os.path.sep, posixpath.sep)}\n")
+ f.write(self.from_source_files())
f.write(f"// Do not edit!\n")
# Create formatter
From ed371385bb7cfc1ca50756a26235a5a4e6f7f4ea Mon Sep 17 00:00:00 2001
From: Julia
Date: Thu, 9 Mar 2023 12:30:47 +0800
Subject: [PATCH 061/280] Refactor: Setup parser.py and generate_cases.py in
preparation for the more general type prop
---
Python/bytecodes.c | 2 +-
Tools/cases_generator/generate_cases.py | 20 +++++++++++-----
Tools/cases_generator/parser.py | 32 ++++++++++++++++++-------
3 files changed, 38 insertions(+), 16 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 3f3593b49e51bd..f08711f527ca9c 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -298,7 +298,7 @@ dummy_func(
U_INST(BINARY_OP_ADD_INT_REST);
}
- inst(BINARY_CHECK_INT, (left, right -- left : PyLong_Type, right : PyLong_Type)) {
+ inst(BINARY_CHECK_INT, (left, right -- left : <<= PyLong_Type, right : <<= PyLong_Type)) {
assert(cframe.use_tracing == 0);
bb_test = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 5495b3c9f08f94..3064a68f40513c 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -16,7 +16,8 @@
from enum import Enum, auto
import parser
-from parser import StackEffect, StackVarTypeLiteral, StackVarTypeIndex, StackVarInputVar
+from parser import StackEffect
+from parser import TypeLiteralAnnotation, TypeIndexAnnotation, TypeInputAnnotation, TypeDerefAnnotation
from parser import LocalEffect, LocalEffectVarLiteral, LocalEffectVarStack
HERE = os.path.dirname(__file__)
@@ -342,8 +343,8 @@ def write_typeprop(self, out: Formatter) -> None:
# Stack input is used in output effect
for oeffect in self.output_effects:
if not (typ := oeffect.type_annotation): continue
- if not isinstance(typ, StackVarInputVar): continue
- if oeffect.name in self.unmoved_names:
+ if not isinstance(typ, TypeInputAnnotation): continue
+ if oeffect.name in self.unmoved_names and oeffect.name == typ.name:
print(
f"Warn: {self.name} type annotation for {oeffect.name} will be ignored "
"as it is unmoved")
@@ -407,17 +408,24 @@ def write_typeprop(self, out: Formatter) -> None:
# Check if there's type info
if typ := oeffect.type_annotation:
match typ:
- case StackVarTypeLiteral(literal=val):
+ case TypeLiteralAnnotation(literal=val):
if val != "NULL": val = f"&{val}"
- case StackVarTypeIndex(array=arr, index=idx):
+ case TypeIndexAnnotation(array=arr, index=idx):
val = f"{'TYPELOCALS_GET' if arr == 'locals' else 'TYPECONST_GET'}({idx})"
- case StackVarInputVar(name=val):
+ case TypeInputAnnotation(name=val):
# We determined above that we don't need to write this stack effect
if val not in need_to_declare:
continue
# Unmoved var, don't need to write
if oeffect.name in self.unmoved_names:
continue
+ case TypeDerefAnnotation(typeval=typeval):
+ match typeval:
+ case TypeLiteralAnnotation(literal=val):
+ # TODO
+ continue
+ case _:
+ typing.assert_never(typeval)
case _:
typing.assert_never(typ)
if oeffect.cond:
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 26b55869a0c191..372929f760ca43 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -69,28 +69,36 @@ class Block(Node):
@dataclass
-class StackVarTypeLiteral(Node):
+class TypeLiteralAnnotation(Node):
literal: str
@dataclass
-class StackVarTypeIndex(Node):
+class TypeIndexAnnotation(Node):
array: Literal["locals", "consts"]
index: str
@dataclass
-class StackVarInputVar(Node):
+class TypeInputAnnotation(Node):
name: str
+@dataclass
+class TypeDerefAnnotation(Node):
+ typeval: TypeLiteralAnnotation # Support more types as needed
-StackVarType: TypeAlias = StackVarTypeLiteral | StackVarTypeIndex | StackVarInputVar
+TypeAnnotation: TypeAlias = (
+ TypeLiteralAnnotation
+ | TypeIndexAnnotation
+ | TypeInputAnnotation
+ | TypeDerefAnnotation
+)
@dataclass
class StackEffect(Node):
name: str
type: str = "" # Optional `:type`
- type_annotation: StackVarType | None = None # Default is None
+ type_annotation: TypeAnnotation | None = None # Default is None
cond: str = "" # Optional `if (cond)`
size: str = "" # Optional `[size]`
# Note: size cannot be combined with type or cond
@@ -348,21 +356,27 @@ def stack_effect(self) -> StackEffect | None:
return StackEffect(tkn.text, _type, type_annotation, cond_text, size_text)
@contextual
- def stackvar_type(self) -> StackVarType | None:
+ def stackvar_type(self) -> TypeAnnotation | None:
if id := self.expect(lx.IDENTIFIER):
idstr = id.text.strip()
if not self.expect(lx.LBRACKET):
- return StackVarTypeLiteral(idstr)
+ return TypeLiteralAnnotation(idstr)
if idstr not in ["locals", "consts"]: return
if id := self.expect(lx.IDENTIFIER):
index = id.text.strip()
self.require(lx.RBRACKET)
- return StackVarTypeIndex(
+ return TypeIndexAnnotation(
"locals" if idstr == "locals" else "consts",
index)
elif self.expect(lx.TIMES):
id = self.require(lx.IDENTIFIER)
- return StackVarInputVar(id.text.strip())
+ return TypeInputAnnotation(id.text.strip())
+ elif self.expect(lx.LSHIFTEQUAL):
+ typeval = self.stackvar_type()
+ # TODO: Support other type annotations as needed
+ assert isinstance(typeval, TypeLiteralAnnotation), \
+ "TypeDerefAnnotation only supports Type Literals"
+ return TypeDerefAnnotation(typeval)
@contextual
From 066df400e2eec62f54a068a0ee3fcb841b1b8979 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 13 Mar 2023 00:25:56 +0800
Subject: [PATCH 062/280] Add dis support for tier 2 code objects
---
Include/cpython/code.h | 2 --
Lib/dis.py | 25 +++++++++++++++++--------
Objects/codeobject.c | 11 +++++++++++
Python/tier2.c | 3 ++-
4 files changed, 30 insertions(+), 11 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 69adfcefd82722..69ceeb8d1bddfb 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -86,8 +86,6 @@ typedef struct _PyTier2BBSpace {
_Py_CODEUNIT u_code[1];
} _PyTier2BBSpace;
-#define _PyTier2BBSpace_NBYTES_USED(space) sizeof(_PyTier2BBSpace) + space->max_capacity
-
// Tier 2 info stored in the code object. Lazily allocated.
typedef struct _PyTier2Info {
/* the tier 2 basic block to execute (if any) */
diff --git a/Lib/dis.py b/Lib/dis.py
index 9edde6ae8258da..8f7d3de20ec257 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -13,6 +13,7 @@
_nb_ops,
_specializations,
_specialized_instructions,
+ _uops,
)
__all__ = ["code_info", "dis", "disassemble", "distb", "disco",
@@ -52,6 +53,10 @@
_all_opname[spec_op] = specialized
_all_opmap[specialized] = spec_op
+_empty_slot = [slot for slot, name in enumerate(_all_opname) if name.startswith("<")]
+for uop_opcode, uop in zip(_empty_slot, _uops):
+ _all_opname[uop_opcode] = uop
+
deoptmap = {
specialized: base for base, family in _specializations.items() for specialized in family
}
@@ -69,7 +74,8 @@ def _try_compile(source, name):
c = compile(source, name, 'exec')
return c
-def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False):
+def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
+ tier2=False):
"""Disassemble classes, methods, functions, and other compiled objects.
With no argument, disassemble the last traceback.
@@ -105,7 +111,7 @@ def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False):
print("Sorry:", msg, file=file)
print(file=file)
elif hasattr(x, 'co_code'): # Code object
- _disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive)
+ _disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, tier2=tier2)
elif isinstance(x, (bytes, bytearray)): # Raw bytecode
_disassemble_bytes(x, file=file, show_caches=show_caches)
elif isinstance(x, str): # Source code
@@ -188,7 +194,9 @@ def _deoptop(op):
name = _all_opname[op]
return _all_opmap[deoptmap[name]] if name in deoptmap else op
-def _get_code_array(co, adaptive):
+def _get_code_array(co, adaptive, tier2=False):
+ if tier2:
+ return co._co_code_tier2
return co._co_code_adaptive if adaptive else co.co_code
def code_info(x):
@@ -525,18 +533,18 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
Positions(*next(co_positions, ()))
)
-def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False):
+def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False, tier2=False):
"""Disassemble a code object."""
linestarts = dict(findlinestarts(co))
exception_entries = _parse_exception_table(co)
- _disassemble_bytes(_get_code_array(co, adaptive),
+ _disassemble_bytes(_get_code_array(co, adaptive, tier2),
lasti, co._varname_from_oparg,
co.co_names, co.co_consts, linestarts, file=file,
exception_entries=exception_entries,
co_positions=co.co_positions(), show_caches=show_caches)
-def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False):
- disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive)
+def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, tier2=False):
+ disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, tier2=tier2)
if depth is None or depth > 0:
if depth is not None:
depth = depth - 1
@@ -545,7 +553,8 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adap
print(file=file)
print("Disassembly of %r:" % (x,), file=file)
_disassemble_recursive(
- x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive
+ x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive,
+ tier2=tier2
)
def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index f5f5f6c1db802d..18f294134e7084 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -1998,9 +1998,20 @@ code_getcode(PyCodeObject *code, void *closure)
return _PyCode_GetCode(code);
}
+static PyObject *
+code_getcodetier2(PyCodeObject *code, void *closure)
+{
+ if (code->_tier2_info == NULL) {
+ return PyBytes_FromStringAndSize("", 0);
+ }
+ return PyBytes_FromStringAndSize(code->_tier2_info->_bb_space->u_code,
+ code->_tier2_info->_bb_space->water_level);
+}
+
static PyGetSetDef code_getsetlist[] = {
{"co_lnotab", (getter)code_getlnotab, NULL, NULL},
{"_co_code_adaptive", (getter)code_getcodeadaptive, NULL, NULL},
+ {"_co_code_tier2", (getter)code_getcodetier2, NULL, NULL},
// The following old names are kept for backward compatibility.
{"co_varnames", (getter)code_getvarnames, NULL, NULL},
{"co_cellvars", (getter)code_getcellvars, NULL, NULL},
diff --git a/Python/tier2.c b/Python/tier2.c
index c49ee8eea1dda8..aa0be4b90489f9 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -821,11 +821,12 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
}
}
+ _PyTier2TypeContext *type_context_copy = NULL;
end:
// Create the tier 2 BB
// Make a copy of the type context
- _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_Copy(starting_type_context);
+ type_context_copy = _PyTier2TypeContext_Copy(starting_type_context);
if (type_context_copy == NULL) {
return NULL;
}
From abb95481611db188cf393c811f3a595bb2823be1 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 13 Mar 2023 02:31:15 +0800
Subject: [PATCH 063/280] Add Jump offsets to dis
---
Lib/dis.py | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/Lib/dis.py b/Lib/dis.py
index 8f7d3de20ec257..6648e477cbe565 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -53,9 +53,14 @@
_all_opname[spec_op] = specialized
_all_opmap[specialized] = spec_op
+_bb_jumps = []
+_uop_hasoparg = []
_empty_slot = [slot for slot, name in enumerate(_all_opname) if name.startswith("<")]
for uop_opcode, uop in zip(_empty_slot, _uops):
_all_opname[uop_opcode] = uop
+ if uop.startswith('BB_BRANCH') or uop.startswith('BB_JUMP'):
+ _bb_jumps.append(uop_opcode)
+ _uop_hasoparg.append(uop_opcode)
deoptmap = {
specialized: base for base, family in _specializations.items() for specialized in family
@@ -339,7 +344,8 @@ def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4):
return ' '.join(fields).rstrip()
-def get_instructions(x, *, first_line=None, show_caches=False, adaptive=False):
+def get_instructions(x, *, first_line=None, show_caches=False, adaptive=False,
+ tier2=False):
"""Iterator for the opcodes in methods, functions or code
Generates a series of Instruction named tuples giving the details of
@@ -356,7 +362,7 @@ def get_instructions(x, *, first_line=None, show_caches=False, adaptive=False):
line_offset = first_line - co.co_firstlineno
else:
line_offset = 0
- return _get_instructions_bytes(_get_code_array(co, adaptive),
+ return _get_instructions_bytes(_get_code_array(co, adaptive, tier2),
co._varname_from_oparg,
co.co_names, co.co_consts,
linestarts, line_offset,
@@ -485,6 +491,12 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
elif deop in hasjabs:
argval = arg*2
argrepr = "to " + repr(argval)
+ elif deop in _bb_jumps:
+ print("HI")
+ signed_arg = -arg if _is_backward_jump(deop) else arg
+ argval = offset + 2 + signed_arg*2
+ argval += 2 * caches
+ argrepr = "to " + repr(argval)
elif deop in hasjrel:
signed_arg = -arg if _is_backward_jump(deop) else arg
argval = offset + 2 + signed_arg*2
@@ -620,7 +632,7 @@ def _unpack_opargs(code):
op = code[i]
deop = _deoptop(op)
caches = _inline_cache_entries[deop]
- if deop in hasarg:
+ if deop in hasarg or deop in _uop_hasoparg:
arg = code[i+1] | extended_arg
extended_arg = (arg << 8) if deop == EXTENDED_ARG else 0
# The oparg is stored as a signed integer
@@ -715,7 +727,8 @@ class Bytecode:
Iterating over this yields the bytecode operations as Instruction instances.
"""
- def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False):
+ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False,
+ adaptive=False, tier2=False):
self.codeobj = co = _get_code_object(x)
if first_line is None:
self.first_line = co.co_firstlineno
@@ -729,10 +742,11 @@ def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False
self.exception_entries = _parse_exception_table(co)
self.show_caches = show_caches
self.adaptive = adaptive
+ self.tier2 = tier2
def __iter__(self):
co = self.codeobj
- return _get_instructions_bytes(_get_code_array(co, self.adaptive),
+ return _get_instructions_bytes(_get_code_array(co, self.adaptive, self.tier2),
co._varname_from_oparg,
co.co_names, co.co_consts,
self._linestarts,
@@ -766,7 +780,7 @@ def dis(self):
else:
offset = -1
with io.StringIO() as output:
- _disassemble_bytes(_get_code_array(co, self.adaptive),
+ _disassemble_bytes(_get_code_array(co, self.adaptive, self.tier2),
varname_from_oparg=co._varname_from_oparg,
names=co.co_names, co_consts=co.co_consts,
linestarts=self._linestarts,
From 52994f16a9d94739b13fcff2dc72639ee64bbb13 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 13 Mar 2023 18:51:48 +0800
Subject: [PATCH 064/280] Use type propagation to generate branchless ops
---
Include/internal/pycore_code.h | 13 ++-
Lib/dis.py | 3 +-
Python/bytecodes.c | 16 +--
Python/ceval.c | 4 +-
Python/generated_cases.c.h | 16 +--
Python/tier2.c | 189 ++++++++++++++++++++++++++++-----
6 files changed, 194 insertions(+), 47 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index f31a607d23f268..8d1b5599972de0 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -9,7 +9,9 @@ extern "C" {
typedef struct {
// Unique ID (for this code object) for this basic block. This indexes into
// the PyTier2Info bb_data field.
- uint16_t bb_id;
+ // The LSB indicates whether the bb branch is a type guard or not.
+ // To get the actual BB ID, do a right bit shift by one.
+ uint16_t bb_id_tagged;
} _PyBBBranchCache;
#define INLINE_CACHE_ENTRIES_BB_BRANCH CACHE_ENTRIES(_PyBBBranchCache)
@@ -258,8 +260,13 @@ extern int _PyStaticCode_Init(PyCodeObject *co);
extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *,
_Py_CODEUNIT *);
extern _Py_CODEUNIT *_PyTier2_GenerateNextBB(
- struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
- _Py_CODEUNIT **tier1_fallback, char gen_bb_requires_pop);
+ struct _PyInterpreterFrame *frame,
+ uint16_t bb_id_tagged,
+ _Py_CODEUNIT *curr_executing_instr,
+ int jumpby,
+ _Py_CODEUNIT **tier1_fallback,
+ char gen_bb_requires_pop,
+ char gen_bb_is_successor);
extern _Py_CODEUNIT *_PyTier2_LocateJumpBackwardsBB(
struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
_Py_CODEUNIT **tier1_fallback);
diff --git a/Lib/dis.py b/Lib/dis.py
index 6648e477cbe565..9295e58d35f103 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -59,7 +59,8 @@
for uop_opcode, uop in zip(_empty_slot, _uops):
_all_opname[uop_opcode] = uop
if uop.startswith('BB_BRANCH') or uop.startswith('BB_JUMP'):
- _bb_jumps.append(uop_opcode)
+ if uop.startswith('BB_JUMP'):
+ _bb_jumps.append(uop_opcode)
_uop_hasoparg.append(uop_opcode)
deoptmap = {
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index c1aa0d894f058a..3ceb2e622a73e1 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3300,7 +3300,8 @@ dummy_func(
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_UNSET);
// Generate consequent.
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, 0, &tier1_fallback, gen_bb_requires_pop);
+ frame, cache->bb_id_tagged, next_instr - 1,
+ 0, &tier1_fallback, gen_bb_requires_pop, bb_test);
gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
@@ -3311,9 +3312,10 @@ dummy_func(
else {
// Rewrite self
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_SET);
- // Generate predicate.
+ // Generate alternative.
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ frame, cache->bb_id_tagged, next_instr - 1,
+ oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
@@ -3336,7 +3338,8 @@ dummy_func(
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ frame, cache->bb_id_tagged, next_instr - 1,
+ oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
@@ -3368,7 +3371,8 @@ dummy_func(
// @TODO: Rewrite TEST intruction above to a JUMP above..
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ frame, cache->bb_id_tagged, next_instr - 1,
+ oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
@@ -3398,7 +3402,7 @@ dummy_func(
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_LocateJumpBackwardsBB(
- frame, cache->bb_id, -oparg, &tier1_fallback);
+ frame, cache->bb_id_tagged, -oparg, &tier1_fallback);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
diff --git a/Python/ceval.c b/Python/ceval.c
index 4031dbbe427702..017f5e9aeb977e 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -726,12 +726,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions.
// true = successor
// false = alternate
- bool bb_test = true;
+ char bb_test = true;
// For tier2 type propagation, handling of jump instructions with
// runtime-dependent stack effect.
// This flag is used to determine if the type context of a new bb
// requires a stack element to be popped.
- bool gen_bb_requires_pop = false;
+ char gen_bb_requires_pop = false;
/* WARNING: Because the _PyCFrame lives on the C stack,
* but can be accessed from a heap allocated object (tstate)
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index a0334ecee28441..e513ccd8963b54 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4131,7 +4131,8 @@
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_UNSET);
// Generate consequent.
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, 0, &tier1_fallback, gen_bb_requires_pop);
+ frame, cache->bb_id_tagged, next_instr - 1,
+ 0, &tier1_fallback, gen_bb_requires_pop, bb_test);
gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
@@ -4142,9 +4143,10 @@
else {
// Rewrite self
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_SET);
- // Generate predicate.
+ // Generate alternative.
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ frame, cache->bb_id_tagged, next_instr - 1,
+ oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
@@ -4167,7 +4169,8 @@
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ frame, cache->bb_id_tagged, next_instr - 1,
+ oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
@@ -4203,7 +4206,8 @@
// @TODO: Rewrite TEST intruction above to a JUMP above..
t2_nextinstr = _PyTier2_GenerateNextBB(
- frame, cache->bb_id, oparg, &tier1_fallback, gen_bb_requires_pop);
+ frame, cache->bb_id_tagged, next_instr - 1,
+ oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
gen_bb_requires_pop = false;
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
@@ -4236,7 +4240,7 @@
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_LocateJumpBackwardsBB(
- frame, cache->bb_id, -oparg, &tier1_fallback);
+ frame, cache->bb_id_tagged, -oparg, &tier1_fallback);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
diff --git a/Python/tier2.c b/Python/tier2.c
index aa0be4b90489f9..e808352c76a39a 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -435,25 +435,57 @@ emit_cache_entries(_Py_CODEUNIT *write_curr, int cache_entries)
return write_curr;
}
+static inline void
+write_bb_id(_PyBBBranchCache *cache, int bb_id, bool is_type_guard) {
+ assert((uint16_t)(bb_id) == bb_id);
+ uint16_t bb_id16 = (uint16_t)bb_id;
+ // Make sure MSB is unset, because we need to shift it.
+ assert((bb_id16 & 0x8000) == 0);
+ bb_id16 <<= 1;
+ bb_id16 |= is_type_guard;
+ cache->bb_id_tagged = bb_id16;
+}
+
+#define BB_ID(bb_id_raw) (bb_id_raw >> 1)
+#define BB_IS_TYPE_BRANCH(bb_id_raw) (bb_id_raw & 1)
+
+
+static int type_guard_ladder[256] = {
+ BINARY_CHECK_INT,
+ -1,
+};
+
+static int type_guard_to_index[256] = {
+ [BINARY_CHECK_INT] = 0,
+};
+
+
static inline _Py_CODEUNIT *
-emit_type_guard(_Py_CODEUNIT *write_curr, _Py_CODEUNIT guard, int bb_id)
+emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int bb_id)
{
- *write_curr = guard;
+#if BB_DEBUG
+ fprintf(stderr, "emitted type guard %p %s\n", write_curr,
+ _PyOpcode_OpName[guard_opcode]);
+#endif
+ write_curr->op.code = guard_opcode;
+ write_curr->op.arg = type_guard_to_index[BINARY_CHECK_INT];
write_curr++;
- //_py_set_opcode(write_curr, EXTENDED_ARG);
- //write_curr->oparg = 0;
- //write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
write_curr->op.arg = 0;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
- assert((uint16_t)(bb_id) == bb_id);
- cache->bb_id = (uint16_t)(bb_id);
+ write_bb_id(cache, bb_id, true);
return write_curr;
}
// Converts the tier 1 branch bytecode to tier 2 branch bytecode.
+// This converts sequence of instructions like
+// JUMP_IF_FALSE_OR_POP
+// to
+// BB_TEST_IF_FALSE_OR_POP
+// BB_BRANCH
+// CACHE (bb_id of the current BB << 1 | is_type_branch)
static inline _Py_CODEUNIT *
emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
{
@@ -527,8 +559,7 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
- assert((uint16_t)(bb_id) == bb_id);
- cache->bb_id = (uint16_t)(bb_id);
+ write_bb_id(cache, bb_id, false);
return write_curr;
}
// FOR_ITER is also a special jump
@@ -552,8 +583,7 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
- assert((uint16_t)(bb_id) == bb_id);
- cache->bb_id = (uint16_t)(bb_id);
+ write_bb_id(cache, bb_id, false);
return write_curr;
}
else {
@@ -570,8 +600,7 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
- assert((uint16_t)(bb_id) == bb_id);
- cache->bb_id = (uint16_t)(bb_id);
+ write_bb_id(cache, bb_id, false);
return write_curr;
}
}
@@ -676,6 +705,55 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
return 0;
}
+// This converts sequence of instructions like
+// BINARY_OP (ADD)
+// to
+// BINARY_CHECK_INT
+// BB_BRANCH
+// CACHE (bb_id of the current BB << 1 | is_type_branch)
+// // The BINARY_ADD then goes to the next BB
+static inline _Py_CODEUNIT *
+infer_BINARY_OP_ADD(
+ bool *needs_guard,
+ _Py_CODEUNIT *prev_type_guard,
+ _Py_CODEUNIT raw_op,
+ _Py_CODEUNIT *write_curr,
+ _PyTier2TypeContext *type_context,
+ int bb_id)
+{
+ *needs_guard = false;
+ PyTypeObject *right = type_context->type_stack_ptr[-1];
+ PyTypeObject *left = type_context->type_stack_ptr[-2];
+ if (left == &PyLong_Type) {
+ if (right == &PyLong_Type) {
+ write_curr->op.code = BINARY_OP_ADD_INT_REST;
+ write_curr++;
+ type_propagate(BINARY_OP_ADD_INT_REST, 0, type_context, NULL);
+ return write_curr;
+ }
+ }
+ // Unknown, time to emit the chain of guards.
+ // First, we guesstimate using the current specialised op
+ *needs_guard = true;
+ if (raw_op.op.code == BINARY_OP_ADD_INT) {
+ return emit_type_guard(write_curr, BINARY_CHECK_INT, bb_id);
+ }
+ // If there current op isn't specialised, we just do the general ladder.
+ if (prev_type_guard == NULL) {
+ return emit_type_guard(write_curr, BINARY_CHECK_INT, bb_id);
+ }
+ else {
+ int next_guard = type_guard_ladder[type_guard_to_index[prev_type_guard->op.code] + 1];
+ if (next_guard != -1) {
+ return emit_type_guard(write_curr, next_guard, bb_id);
+ }
+ // End of ladder, fall through
+ }
+ // Unknown, just emit the same opcode, don't bother emitting guard.
+ // Fall through and let the code generator handle.
+ return NULL;
+}
+
// Detects a BB from the current instruction start to the end of the first basic block it sees.
// Then emits the instructions into the bb space.
//
@@ -688,12 +766,17 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
//
// Note: a BB end also includes a type guard.
_PyTier2BBMetadata *
-_PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
+_PyTier2_Code_DetectAndEmitBB(
+ PyCodeObject *co,
+ _PyTier2BBSpace *bb_space,
+ _Py_CODEUNIT *prev_type_guard,
_Py_CODEUNIT *tier1_start,
// starting_type_context will be modified in this function,
// do make a copy if needed before calling this function
_PyTier2TypeContext *starting_type_context)
{
+ assert(prev_type_guard == NULL ||
+ prev_type_guard->op.code == BINARY_CHECK_INT);
#define END() goto end;
#define JUMPBY(x) i += x + 1;
#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); \
@@ -708,6 +791,7 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// There are only two cases that a BB ends.
// 1. If there's a branch instruction / scope exit.
// 2. If there's a type guard.
+ bool needs_guard = 0;
_PyTier2BBMetadata *meta = NULL;
_PyTier2BBMetadata *temp_meta = NULL;
@@ -734,9 +818,6 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// Just because an instruction requires a guard doesn't mean it's the end of a BB.
// We need to check whether we can eliminate the guard based on the current type context.
- // int how_many_guards = 0;
- // _Py_CODEUNIT guard_instr;
- // _Py_CODEUNIT action;
dispatch_opcode:
switch (opcode) {
@@ -753,6 +834,29 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
// So we tell the BB to skip over it.
t2_start++;
DISPATCH();
+ case BINARY_OP:
+ if (oparg == NB_ADD) {
+ // Add operation. Need to check if we can infer types.
+ _Py_CODEUNIT *possible_next = infer_BINARY_OP_ADD(&needs_guard,
+ prev_type_guard,
+ *curr,
+ write_i, starting_type_context,
+ co->_tier2_info->bb_data_curr);
+ if (possible_next == NULL) {
+ DISPATCH();
+ }
+ write_i = possible_next;
+ if (needs_guard) {
+ // Point to the same instruction, because in this BB we emit
+ // the guard.
+ // The next BB emits the instruction.
+ i--;
+ END();
+ }
+ i += caches;
+ continue;
+ }
+ DISPATCH()
default:
#if BB_DEBUG || TYPEPROP_DEBUG
fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
@@ -844,7 +948,8 @@ _PyTier2_Code_DetectAndEmitBB(PyCodeObject *co, _PyTier2BBSpace *bb_space,
}
if (starts_with_backwards_jump_target) {
// Add the basic block to the jump ids
- if (add_metadata_to_jump_2d_array(t2_info, temp_meta, backwards_jump_target_offset) < 0) {
+ if (add_metadata_to_jump_2d_array(t2_info, temp_meta,
+ backwards_jump_target_offset) < 0) {
PyMem_Free(meta);
if (meta != temp_meta) {
PyMem_Free(temp_meta);
@@ -1114,7 +1219,7 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
goto cleanup;
}
_PyTier2BBMetadata *meta = _PyTier2_Code_DetectAndEmitBB(
- co, bb_space,
+ co, bb_space, NULL,
_PyCode_CODE(co), type_context);
if (meta == NULL) {
_PyTier2TypeContext_Free(type_context);
@@ -1165,20 +1270,27 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
// The first basic block created will always be directly after the current tier 2 code.
// The second basic block created will always require a jump.
_Py_CODEUNIT *
-_PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
- _Py_CODEUNIT **tier1_fallback, char gen_bb_requires_pop)
+_PyTier2_GenerateNextBB(
+ _PyInterpreterFrame *frame,
+ uint16_t bb_id_tagged,
+ _Py_CODEUNIT *curr_executing_instr,
+ int jumpby,
+ _Py_CODEUNIT **tier1_fallback,
+ char gen_bb_requires_pop,
+ char gen_bb_is_successor)
{
PyCodeObject *co = frame->f_code;
assert(co->_tier2_info != NULL);
- assert(bb_id <= co->_tier2_info->bb_data_curr);
- _PyTier2BBMetadata *meta = co->_tier2_info->bb_data[bb_id];
+ assert(BB_ID(bb_id_tagged) <= co->_tier2_info->bb_data_curr);
+ _PyTier2BBMetadata *meta = co->_tier2_info->bb_data[BB_ID(bb_id_tagged)];
_Py_CODEUNIT *tier1_end = meta->tier1_end + jumpby;
*tier1_fallback = tier1_end;
// Be a pessimist and assume we need to write the entire rest of code into the BB.
// The size of the BB generated will definitely be equal to or smaller than this.
_PyTier2BBSpace *space = _PyTier2_BBSpaceCheckAndReallocIfNeeded(
frame->f_code,
- _PyCode_NBYTES(co) - (tier1_end - _PyCode_CODE(co)) * sizeof(_Py_CODEUNIT));
+ _PyCode_NBYTES(co) -
+ (tier1_end - _PyCode_CODE(co)) * sizeof(_Py_CODEUNIT));
if (space == NULL) {
// DEOPTIMIZE TO TIER 1?
return NULL;
@@ -1196,8 +1308,26 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
if (gen_bb_requires_pop) {
type_context_copy->type_stack_ptr--;
}
+ // For type branches, they directly precede the bb branch instruction
+ _Py_CODEUNIT *prev_type_guard = BB_IS_TYPE_BRANCH(bb_id_tagged)
+ ? curr_executing_instr - 1 : NULL;
+ if (gen_bb_is_successor && prev_type_guard != NULL) {
+ // Is a type branch, so the previous instruction shouldn't be
+ // one of those conditional pops.
+ assert(!gen_bb_requires_pop);
+ // Propagate the type guard information.
+#ifdef TYPEPROP_DEBUG
+ fprintf(stderr,
+ " [-] Previous predicate BB ended with a type guard: %s\n",
+ _PyOpcode_OpName[prev_type_guard->op.code]);
+#endif
+ type_propagate(prev_type_guard->op.code,
+ prev_type_guard->op.arg, type_context_copy, NULL);
+ }
_PyTier2BBMetadata *metadata = _PyTier2_Code_DetectAndEmitBB(
- frame->f_code, space, tier1_end,
+ frame->f_code, space,
+ prev_type_guard,
+ tier1_end,
type_context_copy);
if (metadata == NULL) {
_PyTier2TypeContext_Free(type_context_copy);
@@ -1207,13 +1337,13 @@ _PyTier2_GenerateNextBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
}
_Py_CODEUNIT *
-_PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
+_PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged, int jumpby,
_Py_CODEUNIT **tier1_fallback)
{
PyCodeObject *co = frame->f_code;
assert(co->_tier2_info != NULL);
- assert(bb_id <= co->_tier2_info->bb_data_curr);
- _PyTier2BBMetadata *meta = co->_tier2_info->bb_data[bb_id];
+ assert(BB_ID(bb_id_tagged) <= co->_tier2_info->bb_data_curr);
+ _PyTier2BBMetadata *meta = co->_tier2_info->bb_data[BB_ID(bb_id_tagged)];
// The jump target
_Py_CODEUNIT *tier1_jump_target = meta->tier1_end + jumpby;
*tier1_fallback = tier1_jump_target;
@@ -1221,7 +1351,8 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id, int j
// The size of the BB generated will definitely be equal to or smaller than this.
_PyTier2BBSpace *space = _PyTier2_BBSpaceCheckAndReallocIfNeeded(
frame->f_code,
- _PyCode_NBYTES(co) - (tier1_jump_target - _PyCode_CODE(co)) * sizeof(_Py_CODEUNIT));
+ _PyCode_NBYTES(co) -
+ (tier1_jump_target - _PyCode_CODE(co)) * sizeof(_Py_CODEUNIT));
if (space == NULL) {
// DEOPTIMIZE TO TIER 1?
return NULL;
From 1351aef5e225b0f10912157c797e9cef521186ca Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 13 Mar 2023 21:49:37 +0800
Subject: [PATCH 065/280] Copy specialised ops, ladder the type guards
---
Lib/dis.py | 2 ++
Python/tier2.c | 7 ++++---
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/Lib/dis.py b/Lib/dis.py
index 9295e58d35f103..9d3cc60e7f57f7 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -61,6 +61,8 @@
if uop.startswith('BB_BRANCH') or uop.startswith('BB_JUMP'):
if uop.startswith('BB_JUMP'):
_bb_jumps.append(uop_opcode)
+ if uop.startswith('BB_BRANCH'):
+ _inline_cache_entries[uop_opcode] = 1
_uop_hasoparg.append(uop_opcode)
deoptmap = {
diff --git a/Python/tier2.c b/Python/tier2.c
index e808352c76a39a..08f4896d84c30a 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -735,7 +735,7 @@ infer_BINARY_OP_ADD(
// Unknown, time to emit the chain of guards.
// First, we guesstimate using the current specialised op
*needs_guard = true;
- if (raw_op.op.code == BINARY_OP_ADD_INT) {
+ if (raw_op.op.code == BINARY_OP_ADD_INT && prev_type_guard == NULL) {
return emit_type_guard(write_curr, BINARY_CHECK_INT, bb_id);
}
// If there current op isn't specialised, we just do the general ladder.
@@ -779,7 +779,7 @@ _PyTier2_Code_DetectAndEmitBB(
prev_type_guard->op.code == BINARY_CHECK_INT);
#define END() goto end;
#define JUMPBY(x) i += x + 1;
-#define DISPATCH() write_i = emit_i(write_i, opcode, oparg); \
+#define DISPATCH() write_i = emit_i(write_i, specop, oparg); \
write_i = copy_cache_entries(write_i, curr+1, caches); \
i += caches; \
type_propagate(opcode, oparg, starting_type_context, consts); \
@@ -812,7 +812,8 @@ _PyTier2_Code_DetectAndEmitBB(
for (; i < Py_SIZE(co); i++) {
_Py_CODEUNIT *curr = _PyCode_CODE(co) + i;
_Py_CODEUNIT *next_instr = curr + 1;
- int opcode = _PyOpcode_Deopt[_Py_OPCODE(*curr)];
+ int specop = _Py_OPCODE(*curr);
+ int opcode = _PyOpcode_Deopt[specop];
int oparg = _Py_OPARG(*curr);
int caches = _PyOpcode_Caches[opcode];
From eccd7e1729ba957cdc0ba7daa7d3bb15d3efb059 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 13 Mar 2023 22:28:59 +0800
Subject: [PATCH 066/280] Add float support
---
Include/internal/pycore_opcode.h | 6 ++----
Include/opcode.h | 4 +++-
Lib/opcode.py | 5 +++--
Python/bytecodes.c | 9 +++++++++
Python/generated_cases.c.h | 28 +++++++++++++++++++++++++++-
Python/opcode_metadata.h | 10 ++++++++++
Python/tier2.c | 30 ++++++++++++++++++++----------
Python/tier2_typepropagator.c.h | 12 ++++++++++++
8 files changed, 86 insertions(+), 18 deletions(-)
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index ce742bad98f079..5be22719a3aec6 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -427,9 +427,9 @@ static const char *const _PyOpcode_OpName[263] = {
[BB_TEST_POP_IF_NONE] = "BB_TEST_POP_IF_NONE",
[BB_JUMP_BACKWARD_LAZY] = "BB_JUMP_BACKWARD_LAZY",
[BINARY_CHECK_INT] = "BINARY_CHECK_INT",
+ [BINARY_CHECK_FLOAT] = "BINARY_CHECK_FLOAT",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
- [191] = "<191>",
- [192] = "<192>",
+ [BINARY_OP_ADD_FLOAT_REST] = "BINARY_OP_ADD_FLOAT_REST",
[193] = "<193>",
[194] = "<194>",
[195] = "<195>",
@@ -505,8 +505,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 191: \
- case 192: \
case 193: \
case 194: \
case 195: \
diff --git a/Include/opcode.h b/Include/opcode.h
index 7f0b02ab7687e2..f5c6d7c5138c09 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -279,7 +279,9 @@ extern "C" {
#define BB_TEST_POP_IF_NONE 187
#define BB_JUMP_BACKWARD_LAZY 188
#define BINARY_CHECK_INT 189
-#define BINARY_OP_ADD_INT_REST 190
+#define BINARY_CHECK_FLOAT 190
+#define BINARY_OP_ADD_INT_REST 191
+#define BINARY_OP_ADD_FLOAT_REST 192
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 890982aaa85f67..2652223a50919d 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -458,6 +458,7 @@ def pseudo_op(name, op, real_ops):
_macro_ops = [
'BINARY_OP_ADD_INT',
+ 'BINARY_OP_ADD_FLOAT',
]
_uops = [
# Tier 2 BB opcodes
@@ -502,8 +503,8 @@ def pseudo_op(name, op, real_ops):
# The benefit is that they save some dispatch overhead versus the
# single operand forms.
'BINARY_CHECK_INT',
- # 'BINARY_CHECK_FLOAT',
+ 'BINARY_CHECK_FLOAT',
# 'BINARY_CHECK_STR',
- # BINARY_OP_ADD_INT,
'BINARY_OP_ADD_INT_REST',
+ 'BINARY_OP_ADD_FLOAT_REST',
]
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 806c15123e3958..7e93f7acfa29f9 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -276,6 +276,15 @@ dummy_func(
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ U_INST(BINARY_OP_ADD_FLOAT_REST);
+ }
+
+ inst(BINARY_CHECK_FLOAT, (left, right -- left : PyFloat_Type, right : PyFloat_Type)) {
+ assert(cframe.use_tracing == 0);
+ bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ }
+
+ u_inst(BINARY_OP_ADD_FLOAT_REST, (left, right -- sum : PyFloat_Type)) {
STAT_INC(BINARY_OP, hit);
double dsum = ((PyFloatObject *)left)->ob_fval +
((PyFloatObject *)right)->ob_fval;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 2281dad0534e1e..56a7fbb050b03e 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -3,6 +3,14 @@
// Python/bytecodes.c
// Do not edit!
+ #define UOP_BINARY_OP_ADD_FLOAT_REST() \
+ do { \
+ STAT_INC(BINARY_OP, hit);\
+ double dsum = ((PyFloatObject *)left)->ob_fval +\
+ ((PyFloatObject *)right)->ob_fval;\
+ DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dsum, sum);\
+ } while (0)
+
#define UOP_BINARY_OP_ADD_INT_REST() \
do { \
STAT_INC(BINARY_OP, hit);\
@@ -380,13 +388,31 @@
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
+ UOP_BINARY_OP_ADD_FLOAT_REST();
+ STACK_SHRINK(1);
+ stack_pointer[-1] = sum;
+ next_instr += 1;
+ DISPATCH();
+ }
+
+ TARGET(BINARY_CHECK_FLOAT) {
+ PyObject *right = stack_pointer[-1];
+ PyObject *left = stack_pointer[-2];
+ assert(cframe.use_tracing == 0);
+ bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_ADD_FLOAT_REST) {
+ PyObject *right = stack_pointer[-1];
+ PyObject *left = stack_pointer[-2];
+ PyObject *sum;
STAT_INC(BINARY_OP, hit);
double dsum = ((PyFloatObject *)left)->ob_fval +
((PyFloatObject *)right)->ob_fval;
DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dsum, sum);
STACK_SHRINK(1);
stack_pointer[-1] = sum;
- next_instr += 1;
DISPATCH();
}
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index c530134c2791cb..4a7209e7cf6c0d 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -61,6 +61,10 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 2;
case BINARY_OP_ADD_FLOAT:
return 2;
+ case BINARY_CHECK_FLOAT:
+ return 2;
+ case BINARY_OP_ADD_FLOAT_REST:
+ return 2;
case BINARY_OP_ADD_INT:
return 2;
case BINARY_CHECK_INT:
@@ -447,6 +451,10 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case BINARY_OP_ADD_FLOAT:
return 1;
+ case BINARY_CHECK_FLOAT:
+ return 2;
+ case BINARY_OP_ADD_FLOAT_REST:
+ return 1;
case BINARY_OP_ADD_INT:
return 1;
case BINARY_CHECK_INT:
@@ -815,6 +823,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[BINARY_OP_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
+ [BINARY_CHECK_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [BINARY_OP_ADD_FLOAT_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_OP_ADD_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[BINARY_CHECK_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_OP_ADD_INT_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
diff --git a/Python/tier2.c b/Python/tier2.c
index 08f4896d84c30a..7b1a6d29d45461 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -450,13 +450,20 @@ write_bb_id(_PyBBBranchCache *cache, int bb_id, bool is_type_guard) {
#define BB_IS_TYPE_BRANCH(bb_id_raw) (bb_id_raw & 1)
+// The order/hierarchy to emit type guards
+// NEED TO ADD TO THIS EVERY TIME WE ADD A NEW ONE.
static int type_guard_ladder[256] = {
+ -1,
BINARY_CHECK_INT,
+ BINARY_CHECK_FLOAT,
-1,
};
+// Type guard to index in the ladder.
+// KEEP IN SYNC WITH INDEX IN type_guard_ladder
static int type_guard_to_index[256] = {
- [BINARY_CHECK_INT] = 0,
+ [BINARY_CHECK_INT] = 1,
+ [BINARY_CHECK_FLOAT] = 2,
};
@@ -733,17 +740,13 @@ infer_BINARY_OP_ADD(
}
}
// Unknown, time to emit the chain of guards.
- // First, we guesstimate using the current specialised op
*needs_guard = true;
- if (raw_op.op.code == BINARY_OP_ADD_INT && prev_type_guard == NULL) {
- return emit_type_guard(write_curr, BINARY_CHECK_INT, bb_id);
- }
- // If there current op isn't specialised, we just do the general ladder.
if (prev_type_guard == NULL) {
return emit_type_guard(write_curr, BINARY_CHECK_INT, bb_id);
}
else {
- int next_guard = type_guard_ladder[type_guard_to_index[prev_type_guard->op.code] + 1];
+ int next_guard = type_guard_ladder[
+ type_guard_to_index[prev_type_guard->op.code] + 1];
if (next_guard != -1) {
return emit_type_guard(write_curr, next_guard, bb_id);
}
@@ -775,8 +778,11 @@ _PyTier2_Code_DetectAndEmitBB(
// do make a copy if needed before calling this function
_PyTier2TypeContext *starting_type_context)
{
- assert(prev_type_guard == NULL ||
- prev_type_guard->op.code == BINARY_CHECK_INT);
+ assert(
+ prev_type_guard == NULL ||
+ prev_type_guard->op.code == BINARY_CHECK_INT ||
+ prev_type_guard->op.code == BINARY_CHECK_FLOAT
+ );
#define END() goto end;
#define JUMPBY(x) i += x + 1;
#define DISPATCH() write_i = emit_i(write_i, specop, oparg); \
@@ -827,6 +833,7 @@ _PyTier2_Code_DetectAndEmitBB(
DISPATCH();
case COMPARE_AND_BRANCH:
opcode = COMPARE_OP;
+ specop = COMPARE_OP;
DISPATCH();
case END_FOR:
// Assert that we are the start of a BB
@@ -1177,13 +1184,16 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
int optimizable = 0;
for (Py_ssize_t curr = 0; curr < Py_SIZE(co); curr++) {
_Py_CODEUNIT *curr_instr = _PyCode_CODE(co) + curr;
- if (IS_FORBIDDEN_OPCODE(_PyOpcode_Deopt[_Py_OPCODE(*curr_instr)])) {
+ int deopt = _PyOpcode_Deopt[_Py_OPCODE(*curr_instr)];
+ if (IS_FORBIDDEN_OPCODE(deopt)) {
#if BB_DEBUG
fprintf(stderr, "FORBIDDEN OPCODE %d\n", _Py_OPCODE(*curr_instr));
#endif
return NULL;
}
optimizable |= IS_OPTIMIZABLE_OPCODE(_Py_OPCODE(*curr_instr), _Py_OPARG(*curr_instr));
+ // Skip the cache entries
+ curr += _PyOpcode_Caches[deopt];
}
if (!optimizable) {
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index ffbc68a62ae403..eb83b84f92b67c 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -123,6 +123,18 @@
break;
}
+ TARGET(BINARY_CHECK_FLOAT) {
+ TYPESTACK_POKE(1, &PyFloat_Type);
+ TYPESTACK_POKE(2, &PyFloat_Type);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_FLOAT_REST) {
+ STACK_SHRINK(1);
+ TYPESTACK_POKE(1, &PyFloat_Type);
+ break;
+ }
+
TARGET(BINARY_OP_ADD_INT) {
STACK_SHRINK(1);
TYPESTACK_POKE(1, &PyLong_Type);
From 448f82f4c876e46ac8cf90b983aa550972e34392 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 16 Mar 2023 15:45:33 +0800
Subject: [PATCH 067/280] Fix a bunch of compiler warnings
---
Objects/codeobject.c | 3 ++-
Python/tier2.c | 20 ++++++++++++--------
2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 18f294134e7084..ea72668a5f210c 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -2004,7 +2004,8 @@ code_getcodetier2(PyCodeObject *code, void *closure)
if (code->_tier2_info == NULL) {
return PyBytes_FromStringAndSize("", 0);
}
- return PyBytes_FromStringAndSize(code->_tier2_info->_bb_space->u_code,
+ return PyBytes_FromStringAndSize(
+ (const char *)code->_tier2_info->_bb_space->u_code,
code->_tier2_info->_bb_space->water_level);
}
diff --git a/Python/tier2.c b/Python/tier2.c
index 7b1a6d29d45461..607dd58f98880b 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -136,8 +136,9 @@ type_propagate(
#define STACK_GROW(idx) STACK_ADJUST((idx))
#define STACK_SHRINK(idx) STACK_ADJUST(-(idx))
-#ifdef TYPEPROP_DEBUG
- fprintf(stderr, " [-] Type stack bef: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject *));
+#if TYPEPROP_DEBUG
+ fprintf(stderr, " [-] Type stack bef: %llu\n",
+ ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject *));
#ifdef Py_DEBUG
fprintf(stderr, " [-] Type propagating across: %s : %d\n", _PyOpcode_OpName[opcode], oparg);
#endif
@@ -150,8 +151,9 @@ type_propagate(
Py_UNREACHABLE();
}
-#ifdef TYPEPROP_DEBUG
- fprintf(stderr, " [-] Type stack aft: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject *));
+#if TYPEPROP_DEBUG
+ fprintf(stderr, " [-] Type stack aft: %llu\n",
+ ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject *));
#endif
type_context->type_stack_ptr = type_stackptr;
@@ -880,7 +882,7 @@ _PyTier2_Code_DetectAndEmitBB(
fprintf(stderr, "Emitted virtual start of basic block\n");
#endif
starts_with_backwards_jump_target = true;
- backwards_jump_target_offset = curr - _PyCode_CODE(co);
+ backwards_jump_target_offset = (int)(curr - _PyCode_CODE(co));
virtual_start = false;
goto fall_through;
}
@@ -970,7 +972,7 @@ _PyTier2_Code_DetectAndEmitBB(
// -1 becaues write_i points to the instruction AFTER the end
bb_space->water_level += (write_i - t2_start) * sizeof(_Py_CODEUNIT);
#if BB_DEBUG
- fprintf(stderr, "Generated BB T2 Start: %p, T1 offset: %d\n", meta->tier2_start,
+ fprintf(stderr, "Generated BB T2 Start: %p, T1 offset: %zu\n", meta->tier2_start,
meta->tier1_end - _PyCode_CODE(co));
#endif
return meta;
@@ -1047,8 +1049,10 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
PyMem_Free(backward_jump_offsets);
return 1;
}
- if (allocate_jump_offset_2d_array(backwards_jump_count, backward_jump_target_bb_ids)) {
+ if (allocate_jump_offset_2d_array((int)backwards_jump_count,
+ backward_jump_target_bb_ids)) {
PyMem_Free(backward_jump_offsets);
+ PyMem_Free(backward_jump_target_bb_ids);
return 1;
}
@@ -1327,7 +1331,7 @@ _PyTier2_GenerateNextBB(
// one of those conditional pops.
assert(!gen_bb_requires_pop);
// Propagate the type guard information.
-#ifdef TYPEPROP_DEBUG
+#if TYPEPROP_DEBUG
fprintf(stderr,
" [-] Previous predicate BB ended with a type guard: %s\n",
_PyOpcode_OpName[prev_type_guard->op.code]);
From 417b725cd508481d99b8603c02fdb8282df28c53 Mon Sep 17 00:00:00 2001
From: Julia
Date: Fri, 17 Mar 2023 13:44:38 +0800
Subject: [PATCH 068/280] Refactor: Again
Setup parser.py and generate_cases.py in preparation for the more general type prop
---
Tools/cases_generator/generate_cases.py | 35 ++++++++--------
Tools/cases_generator/parser.py | 56 ++++++++++++++-----------
2 files changed, 49 insertions(+), 42 deletions(-)
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 3064a68f40513c..2fa61432f19234 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -17,7 +17,7 @@
import parser
from parser import StackEffect
-from parser import TypeLiteralAnnotation, TypeIndexAnnotation, TypeInputAnnotation, TypeDerefAnnotation
+from parser import TypeAnnotation, TypeSrcLiteral, TypeSrcConst, TypeSrcLocals, TypeSrcStackInput
from parser import LocalEffect, LocalEffectVarLiteral, LocalEffectVarStack
HERE = os.path.dirname(__file__)
@@ -343,13 +343,13 @@ def write_typeprop(self, out: Formatter) -> None:
# Stack input is used in output effect
for oeffect in self.output_effects:
if not (typ := oeffect.type_annotation): continue
- if not isinstance(typ, TypeInputAnnotation): continue
- if oeffect.name in self.unmoved_names and oeffect.name == typ.name:
+ if not isinstance(src := typ.src, TypeSrcStackInput): continue
+ if oeffect.name in self.unmoved_names and oeffect.name == src.name:
print(
f"Warn: {self.name} type annotation for {oeffect.name} will be ignored "
"as it is unmoved")
continue
- need_to_declare.append(typ.name)
+ need_to_declare.append(src.name)
# Write input stack effect variable declarations and initializations
ieffects = list(reversed(self.input_effects))
@@ -404,28 +404,29 @@ def write_typeprop(self, out: Formatter) -> None:
# Check if it's even used
if oeffect.name == UNUSED: continue
-
+
# Check if there's type info
if typ := oeffect.type_annotation:
- match typ:
- case TypeLiteralAnnotation(literal=val):
+
+ if typ.op == "TYPE_SET":
+ # TODO
+ continue
+
+ # typ.op == "TYPE_OVERWRITE"
+ match typ.src:
+ case TypeSrcLiteral(literal=val):
if val != "NULL": val = f"&{val}"
- case TypeIndexAnnotation(array=arr, index=idx):
- val = f"{'TYPELOCALS_GET' if arr == 'locals' else 'TYPECONST_GET'}({idx})"
- case TypeInputAnnotation(name=val):
+ case TypeSrcLocals(index=idx):
+ val = f"TYPELOCALS_GET({idx})"
+ case TypeSrcConst(index=idx):
+ val = f"TYPECONST_GET({idx})"
+ case TypeSrcStackInput(name=val):
# We determined above that we don't need to write this stack effect
if val not in need_to_declare:
continue
# Unmoved var, don't need to write
if oeffect.name in self.unmoved_names:
continue
- case TypeDerefAnnotation(typeval=typeval):
- match typeval:
- case TypeLiteralAnnotation(literal=val):
- # TODO
- continue
- case _:
- typing.assert_never(typeval)
case _:
typing.assert_never(typ)
if oeffect.cond:
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 372929f760ca43..f9b4f0fe4426b9 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -69,31 +69,34 @@ class Block(Node):
@dataclass
-class TypeLiteralAnnotation(Node):
+class TypeSrcLiteral(Node):
literal: str
@dataclass
-class TypeIndexAnnotation(Node):
- array: Literal["locals", "consts"]
+class TypeSrcConst(Node):
index: str
@dataclass
-class TypeInputAnnotation(Node):
- name: str
+class TypeSrcLocals(Node):
+ index: str
@dataclass
-class TypeDerefAnnotation(Node):
- typeval: TypeLiteralAnnotation # Support more types as needed
-
+class TypeSrcStackInput(Node):
+ name: str
-TypeAnnotation: TypeAlias = (
- TypeLiteralAnnotation
- | TypeIndexAnnotation
- | TypeInputAnnotation
- | TypeDerefAnnotation
+TypeSrc: TypeAlias = (
+ TypeSrcLiteral
+ | TypeSrcConst
+ | TypeSrcLocals
+ | TypeSrcStackInput
)
+@dataclass
+class TypeAnnotation(Node):
+ op: Literal["TYPE_SET", "TYPE_OVERWRITE"]
+ src: TypeSrc
+
@dataclass
class StackEffect(Node):
name: str
@@ -356,28 +359,31 @@ def stack_effect(self) -> StackEffect | None:
return StackEffect(tkn.text, _type, type_annotation, cond_text, size_text)
@contextual
- def stackvar_type(self) -> TypeAnnotation | None:
+ def stackvar_typesrc(self) -> TypeSrc | None:
if id := self.expect(lx.IDENTIFIER):
idstr = id.text.strip()
if not self.expect(lx.LBRACKET):
- return TypeLiteralAnnotation(idstr)
+ return TypeSrcLiteral(idstr)
if idstr not in ["locals", "consts"]: return
if id := self.expect(lx.IDENTIFIER):
index = id.text.strip()
self.require(lx.RBRACKET)
- return TypeIndexAnnotation(
- "locals" if idstr == "locals" else "consts",
- index)
+ if idstr == "locals":
+ return TypeSrcLocals(index)
+ return TypeSrcConst(index)
elif self.expect(lx.TIMES):
id = self.require(lx.IDENTIFIER)
- return TypeInputAnnotation(id.text.strip())
- elif self.expect(lx.LSHIFTEQUAL):
- typeval = self.stackvar_type()
- # TODO: Support other type annotations as needed
- assert isinstance(typeval, TypeLiteralAnnotation), \
- "TypeDerefAnnotation only supports Type Literals"
- return TypeDerefAnnotation(typeval)
+ return TypeSrcStackInput(id.text.strip())
+ @contextual
+ def stackvar_type(self) -> TypeAnnotation | None:
+ if self.expect(lx.LSHIFTEQUAL):
+ src = self.stackvar_typesrc()
+ if src is None: return None
+ return TypeAnnotation("TYPE_SET", src)
+ src = self.stackvar_typesrc()
+ if src is None: return None
+ return TypeAnnotation("TYPE_OVERWRITE", src)
@contextual
def local_effect(self) -> LocalEffect | None:
From 52513f9a896f2acfb9b7eb6dbe8af4de5ed4968b Mon Sep 17 00:00:00 2001
From: Julia
Date: Fri, 17 Mar 2023 13:46:01 +0800
Subject: [PATCH 069/280] Style: parser.py
---
Tools/cases_generator/parser.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index f9b4f0fe4426b9..0f082fd2742f0e 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -77,14 +77,17 @@ class TypeSrcLiteral(Node):
class TypeSrcConst(Node):
index: str
+
@dataclass
class TypeSrcLocals(Node):
index: str
+
@dataclass
class TypeSrcStackInput(Node):
name: str
+
TypeSrc: TypeAlias = (
TypeSrcLiteral
| TypeSrcConst
@@ -92,11 +95,13 @@ class TypeSrcStackInput(Node):
| TypeSrcStackInput
)
+
@dataclass
class TypeAnnotation(Node):
op: Literal["TYPE_SET", "TYPE_OVERWRITE"]
src: TypeSrc
+
@dataclass
class StackEffect(Node):
name: str
From 5d1a01e2a0ee5355417a9b516f0c575b427fdcb5 Mon Sep 17 00:00:00 2001
From: Julia
Date: Sat, 18 Mar 2023 15:41:33 +0800
Subject: [PATCH 070/280] Feat: Improved referenced-based type propagator
TODO: Add debugging mechanisms
---
Include/cpython/code.h | 27 ++-
Python/tier2.c | 251 +++++++++++++++++++++---
Python/tier2_typepropagator.c.h | 230 +++++++++++-----------
Tools/cases_generator/generate_cases.py | 92 +++++----
Tools/cases_generator/parser.py | 19 +-
5 files changed, 425 insertions(+), 194 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 3d97fbf7028913..ee02dc1192b534 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -56,14 +56,35 @@ typedef struct {
PyObject *_co_freevars;
} _PyCoCached;
+// TYPENODE is a tagged pointer that uses the last 2 LSB as the tag
+#define _Py_TYPENODE_t uintptr_t
+
+// TYPENODE Tags
+typedef enum _Py_TypeNodeTags {
+ // Node is unused
+ TYPE_NULL = 0,
+ // TYPE_ROOT can point to a PyTypeObject or be a NULL
+ TYPE_ROOT = 1,
+ // TYPE_REF points to a TYPE_ROOT or a TYPE_REF
+ TYPE_REF = 2
+} _Py_TypeNodeTags;
+
+#define _Py_TYPENODE_NULL 0
+#define _Py_TYPENODE_GET_TAG(typenode) ((typenode) & (0b11))
+#define _Py_TYPENODE_CLEAR_TAG(typenode) ((typenode) & (~(uintptr_t)(0b11)))
+
+#define _Py_TYPENODE_MAKE_NULL() _Py_TYPENODE_NULL
+#define _Py_TYPENODE_MAKE_ROOT(ptr) (_Py_TYPENODE_CLEAR_TAG(ptr) | TYPE_ROOT)
+#define _Py_TYPENODE_MAKE_REF(ptr) (_Py_TYPENODE_CLEAR_TAG(ptr) | TYPE_REF)
+
// Tier 2 types meta interpreter
typedef struct _PyTier2TypeContext {
// points into type_stack, points to one element after the stack
- PyTypeObject** type_stack_ptr;
+ _Py_TYPENODE_t *type_stack_ptr;
int type_locals_len;
int type_stack_len;
- PyTypeObject** type_stack;
- PyTypeObject** type_locals;
+ _Py_TYPENODE_t *type_stack;
+ _Py_TYPENODE_t *type_locals;
} _PyTier2TypeContext;
// Tier 2 interpreter information
diff --git a/Python/tier2.c b/Python/tier2.c
index c49ee8eea1dda8..369d13bc6a3d4f 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -32,11 +32,11 @@ initialize_type_context(const PyCodeObject *co)
int nlocals = co->co_nlocals;
int nstack = co->co_stacksize;
- PyTypeObject **type_locals = PyMem_Malloc(nlocals * sizeof(PyTypeObject *));
+ _Py_TYPENODE_t *type_locals = PyMem_Malloc(nlocals * sizeof(_Py_TYPENODE_t));
if (type_locals == NULL) {
return NULL;
}
- PyTypeObject **type_stack = PyMem_Malloc(nstack * sizeof(PyTypeObject *));
+ _Py_TYPENODE_t *type_stack = PyMem_Malloc(nstack * sizeof(_Py_TYPENODE_t));
if (type_stack == NULL) {
PyMem_Free(type_locals);
return NULL;
@@ -44,10 +44,10 @@ initialize_type_context(const PyCodeObject *co)
// Initialize to unknown type.
for (int i = 0; i < nlocals; i++) {
- type_locals[i] = NULL;
+ type_locals[i] = _Py_TYPENODE_NULL;
}
for (int i = 0; i < nstack; i++) {
- type_stack[i] = NULL;
+ type_stack[i] = _Py_TYPENODE_NULL;
}
_PyTier2TypeContext *type_context = PyMem_Malloc(sizeof(_PyTier2TypeContext));
@@ -75,18 +75,18 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
int nlocals = type_context->type_locals_len;
int nstack = type_context->type_stack_len;
- PyTypeObject **type_locals = PyMem_Malloc(nlocals * sizeof(PyTypeObject *));
+ _Py_TYPENODE_t *type_locals = PyMem_Malloc(nlocals * sizeof(_Py_TYPENODE_t));
if (type_locals == NULL) {
return NULL;
}
- PyTypeObject **type_stack = PyMem_Malloc(nstack * sizeof(PyTypeObject *));
+ _Py_TYPENODE_t *type_stack = PyMem_Malloc(nstack * sizeof(_Py_TYPENODE_t));
if (type_stack == NULL) {
PyMem_Free(type_locals);
return NULL;
}
- memcpy(type_locals, type_context->type_locals, nlocals * sizeof(PyTypeObject *));
- memcpy(type_stack, type_context->type_stack, nstack * sizeof(PyTypeObject *));
+ memcpy(type_locals, type_context->type_locals, nlocals * sizeof(_Py_TYPENODE_t));
+ memcpy(type_stack, type_context->type_stack, nstack * sizeof(_Py_TYPENODE_t));
_PyTier2TypeContext *new_type_context = PyMem_Malloc(sizeof(_PyTier2TypeContext));
if (new_type_context == NULL) {
@@ -115,6 +115,200 @@ _PyTier2TypeContext_Free(_PyTier2TypeContext *type_context)
PyMem_Free(type_context);
}
+static void
+__type_stack_shrink(_Py_TYPENODE_t **type_stackptr, int idx)
+{
+ while (idx--) {
+ **type_stackptr = _Py_TYPENODE_MAKE_NULL();
+ *type_stackptr -= 1;
+ }
+}
+
+static _Py_TYPENODE_t*
+__type_typenode_get_rootptr(_Py_TYPENODE_t ref)
+{
+ uintptr_t tag;
+ _Py_TYPENODE_t *ref_ptr;
+ do {
+ ref_ptr = (_Py_TYPENODE_t *)(ref);
+ ref = *ref_ptr;
+ tag = _Py_TYPENODE_GET_TAG(ref);
+ } while (tag != TYPE_ROOT);
+ return ref_ptr;
+}
+
+static void
+__type_propagate_TYPE_SET(
+ _Py_TYPENODE_t *src, _Py_TYPENODE_t *dst, bool src_is_new)
+{
+
+#ifdef Py_DEBUG
+ // If `src_is_new` is set:
+ // - `src` doesn't belong inside the type context yet.
+ // - `src` has to be a TYPE_ROOT
+ // - `src` is to be interpreted as a _Py_TYPENODE_t
+ if (src_is_new) {
+ assert(_Py_TYPENODE_GET_TAG((_Py_TYPENODE_t)src) == TYPE_ROOT);
+ }
+#endif
+
+ uintptr_t tag = _Py_TYPENODE_GET_TAG(*dst);
+ switch (tag) {
+ case TYPE_NULL:
+ case TYPE_ROOT: {
+ if (!src_is_new) {
+ // Make dst a reference to src
+ *dst = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ break;
+ }
+ // Make dst the src
+ *dst = (_Py_TYPENODE_t)src;
+ break;
+ }
+ case TYPE_REF: {
+ _Py_TYPENODE_t *rootref = __type_typenode_get_rootptr(*dst);
+ if (!src_is_new) {
+ // Traverse up to the root of dst, make root a reference to src
+ *rootref = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ break;
+ }
+ // Make root of dst the src
+ *rootref = (_Py_TYPENODE_t)src;
+ break;
+ }
+ }
+}
+
+static void
+__type_propagate_TYPE_OVERWRITE(
+ _PyTier2TypeContext *type_context,
+ _Py_TYPENODE_t* src, _Py_TYPENODE_t* dst, bool src_is_new)
+{
+
+#ifdef Py_DEBUG
+ // See: __type_propagate_TYPE_SET
+ if (src_is_new) {
+ assert(_Py_TYPENODE_GET_TAG((_Py_TYPENODE_t)src) == TYPE_ROOT);
+ }
+#endif
+
+ uintptr_t tag = _Py_TYPENODE_GET_TAG(*dst);
+ switch (tag) {
+ case TYPE_NULL: {
+ if (!src_is_new) {
+ // Make dst a reference to src
+ *dst = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ break;
+ }
+ // Make dst the src
+ *dst = (_Py_TYPENODE_t)src;
+ break;
+ }
+ case TYPE_ROOT: {
+
+ _Py_TYPENODE_t old_dst = *dst;
+ if (!src_is_new) {
+ // Make dst a reference to src
+ *dst = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ }
+ else {
+ // Make dst the src
+ *dst = (_Py_TYPENODE_t)src;
+ }
+
+ /* Pick one child of dst andmake that the new root of the dst tree */
+
+ // Children of dst will have this form
+ _Py_TYPENODE_t child_test = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)dst));
+ // Will be initialised to the first child we find (ptr to the new root)
+ _Py_TYPENODE_t *new_root_ptr = NULL;
+
+ // Search locals for children
+ int nlocals = type_context->type_locals_len;
+ for (int i = 0; i < nlocals; i++) {
+ _Py_TYPENODE_t *node_ptr = &(type_context->type_locals[i]);
+ if (*node_ptr == child_test) {
+ if (new_root_ptr == NULL) {
+ // First child encountered! initialise root
+ new_root_ptr = node_ptr;
+ *node_ptr = old_dst;
+ }
+ else {
+ // Not the first child encounted, point it to the new root
+ *node_ptr = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)new_root_ptr));
+ }
+ }
+ }
+
+ // Search stack for children
+ int nstack = (int)(type_context->type_stack_ptr - type_context->type_stack);
+ for (int i = 0; i < nstack; i++) {
+ _Py_TYPENODE_t *node_ptr = &(type_context->type_stack[i]);
+ if (*node_ptr == child_test) {
+ if (new_root_ptr == NULL) {
+ // First child encountered! initialise root
+ new_root_ptr = node_ptr;
+ *node_ptr = old_dst;
+ }
+ else {
+ // Not the first child encounted, point it to the new root
+ *node_ptr = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)new_root_ptr));
+ }
+ }
+ }
+ break;
+ }
+ case TYPE_REF: {
+
+ // Make dst a reference to src
+ _Py_TYPENODE_t old_dst = *dst;
+ if (!src_is_new) {
+ // Make dst a reference to src
+ *dst = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ }
+ else {
+ // Make dst the src
+ *dst = (_Py_TYPENODE_t)src;
+ }
+
+ /* Make all child of src be a reference to the parent of dst */
+
+ // Children of dst will have this form
+ _Py_TYPENODE_t child_test = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)dst));
+
+ // Search locals for children
+ int nlocals = type_context->type_locals_len;
+ for (int i = 0; i < nlocals; i++) {
+ _Py_TYPENODE_t *node_ptr = &(type_context->type_locals[i]);
+ if (*node_ptr == child_test) {
+ // Is a child of dst. Point it to the parent of dst
+ *node_ptr = old_dst;
+ }
+ }
+
+ // Search stack for children
+ int nstack = (int)(type_context->type_stack_ptr - type_context->type_stack);
+ for (int i = 0; i < nstack; i++) {
+ _Py_TYPENODE_t *node_ptr = &(type_context->type_stack[i]);
+ if (*node_ptr == child_test) {
+ // Is a child of dst. Point it to the parent of dst
+ *node_ptr = old_dst;
+ }
+ }
+ break;
+ }
+ }
+}
+
// Type propagates across a single function.
static void
type_propagate(
@@ -122,22 +316,29 @@ type_propagate(
_PyTier2TypeContext *type_context,
const PyObject *consts)
{
- PyTypeObject **type_stack = type_context->type_stack;
- PyTypeObject **type_locals = type_context->type_locals;
- PyTypeObject **type_stackptr = type_context->type_stack_ptr;
-
-#define TARGET(op) case op:
-#define TYPESTACK_PEEK(idx) (type_stackptr[-(idx)])
-#define TYPESTACK_POKE(idx, v) type_stackptr[-(idx)] = (v)
-#define TYPELOCALS_SET(idx, v) type_locals[idx] = v;
-#define TYPELOCALS_GET(idx) (type_locals[idx])
-#define TYPECONST_GET(idx) Py_TYPE(_PyTuple_CAST(consts)->ob_item[(idx)])
-#define STACK_ADJUST(idx) type_stackptr += (idx)
-#define STACK_GROW(idx) STACK_ADJUST((idx))
-#define STACK_SHRINK(idx) STACK_ADJUST(-(idx))
+ _Py_TYPENODE_t *type_stack = type_context->type_stack;
+ _Py_TYPENODE_t *type_locals = type_context->type_locals;
+ _Py_TYPENODE_t *type_stackptr = type_context->type_stack_ptr;
+
+#define TARGET(op) case op:
+#define TYPESTACK_PEEK(idx) (&(type_stackptr[-(idx)]))
+#define TYPELOCALS_GET(idx) (&(type_locals[idx]))
+
+// Get the type of the const and make into a TYPENODE ROOT
+#define TYPECONST_GET(idx) _Py_TYPENODE_MAKE_ROOT( \
+ (_Py_TYPENODE_t)Py_TYPE( \
+ _PyTuple_CAST(consts)->ob_item[(idx)]))
+
+#define TYPE_SET(src, dst, flag) __type_propagate_TYPE_SET((src), (dst), (flag))
+#define TYPE_OVERWRITE(src, dst, flag) __type_propagate_TYPE_OVERWRITE(type_context, (src), (dst), (flag))
+
+#define STACK_GROW(idx) type_stackptr += (idx)
+
+// Stack shrinking has to NULL the nodes
+#define STACK_SHRINK(idx) __type_stack_shrink(&type_stackptr, (idx))
#ifdef TYPEPROP_DEBUG
- fprintf(stderr, " [-] Type stack bef: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject *));
+ fprintf(stderr, " [-] Type stack bef: %llu\n", (uint64_t)(type_stackptr - type_stack));
#ifdef Py_DEBUG
fprintf(stderr, " [-] Type propagating across: %s : %d\n", _PyOpcode_OpName[opcode], oparg);
#endif
@@ -151,17 +352,17 @@ type_propagate(
}
#ifdef TYPEPROP_DEBUG
- fprintf(stderr, " [-] Type stack aft: %llu\n", ((uint64_t)type_stackptr - (uint64_t)type_stack) / sizeof(PyTypeObject *));
+ fprintf(stderr, " [-] Type stack aft: %llu\n", (uint64_t)(type_stackptr - type_stack));
#endif
type_context->type_stack_ptr = type_stackptr;
#undef TARGET
#undef TYPESTACK_PEEK
-#undef TYPESTACK_POKE
-#undef TYPELOCALS_SET
#undef TYPELOCALS_GET
#undef TYPECONST_GET
+#undef TYPE_SET
+#undef TYPE_OVERWRITE
#undef STACK_ADJUST
#undef STACK_GROW
#undef STACK_SHRINK
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index ffbc68a62ae403..7504adcd5dc829 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -17,31 +17,31 @@
TARGET(LOAD_CLOSURE) {
STACK_GROW(1);
- TYPESTACK_POKE(1, TYPELOCALS_GET(oparg));
+ TYPE_OVERWRITE(TYPELOCALS_GET(oparg), TYPESTACK_PEEK(1), false);
break;
}
TARGET(LOAD_FAST_CHECK) {
STACK_GROW(1);
- TYPESTACK_POKE(1, TYPELOCALS_GET(oparg));
+ TYPE_OVERWRITE(TYPELOCALS_GET(oparg), TYPESTACK_PEEK(1), false);
break;
}
TARGET(LOAD_FAST) {
STACK_GROW(1);
- TYPESTACK_POKE(1, TYPELOCALS_GET(oparg));
+ TYPE_OVERWRITE(TYPELOCALS_GET(oparg), TYPESTACK_PEEK(1), false);
break;
}
TARGET(LOAD_CONST) {
STACK_GROW(1);
- TYPESTACK_POKE(1, TYPECONST_GET(oparg));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)TYPECONST_GET(oparg), TYPESTACK_PEEK(1), true);
break;
}
TARGET(STORE_FAST) {
- PyTypeObject *value = TYPESTACK_PEEK(1);
- TYPELOCALS_SET(oparg, value)
+ _Py_TYPENODE_t *value = TYPESTACK_PEEK(1);
+ TYPE_OVERWRITE(value, TYPELOCALS_GET(oparg), false);
STACK_SHRINK(1);
break;
}
@@ -53,7 +53,7 @@
TARGET(PUSH_NULL) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -68,47 +68,47 @@
}
TARGET(UNARY_NEGATIVE) {
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(UNARY_NOT) {
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(UNARY_INVERT) {
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_MULTIPLY_INT) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyLong_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_MULTIPLY_FLOAT) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyFloat_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_SUBTRACT_INT) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyLong_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_SUBTRACT_FLOAT) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyFloat_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_ADD_UNICODE) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyUnicode_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyUnicode_Type), TYPESTACK_PEEK(1), true);
break;
}
@@ -119,37 +119,37 @@
TARGET(BINARY_OP_ADD_FLOAT) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyFloat_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_ADD_INT) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyLong_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_CHECK_INT) {
- TYPESTACK_POKE(1, &PyLong_Type);
- TYPESTACK_POKE(2, &PyLong_Type);
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(2), true);
break;
}
TARGET(BINARY_OP_ADD_INT_REST) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyLong_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_SUBSCR) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_SLICE) {
STACK_SHRINK(2);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -160,19 +160,19 @@
TARGET(BINARY_SUBSCR_LIST_INT) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_SUBSCR_TUPLE_INT) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_SUBSCR_DICT) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -212,13 +212,13 @@
}
TARGET(CALL_INTRINSIC_1) {
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_INTRINSIC_2) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -243,18 +243,18 @@
}
TARGET(GET_AITER) {
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(GET_ANEXT) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(GET_AWAITABLE) {
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -295,20 +295,20 @@
TARGET(CLEANUP_THROW) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
- TYPESTACK_POKE(2, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(2), true);
break;
}
TARGET(LOAD_ASSERTION_ERROR) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(LOAD_BUILD_CLASS) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -330,21 +330,21 @@
TARGET(UNPACK_SEQUENCE_TWO_TUPLE) {
STACK_SHRINK(1);
STACK_GROW(oparg);
- TYPESTACK_POKE(oparg, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(oparg), true);
break;
}
TARGET(UNPACK_SEQUENCE_TUPLE) {
STACK_SHRINK(1);
STACK_GROW(oparg);
- TYPESTACK_POKE(oparg, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(oparg), true);
break;
}
TARGET(UNPACK_SEQUENCE_LIST) {
STACK_SHRINK(1);
STACK_GROW(oparg);
- TYPESTACK_POKE(oparg, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(oparg), true);
break;
}
@@ -374,31 +374,31 @@
TARGET(LOAD_NAME) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(LOAD_GLOBAL) {
STACK_GROW(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_GLOBAL_MODULE) {
STACK_GROW(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_GLOBAL_BUILTIN) {
STACK_GROW(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
@@ -420,7 +420,7 @@
TARGET(LOAD_CLASSDEREF) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -442,21 +442,21 @@
TARGET(BUILD_STRING) {
STACK_SHRINK(oparg);
STACK_GROW(1);
- TYPESTACK_POKE(1, &PyUnicode_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyUnicode_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BUILD_TUPLE) {
STACK_SHRINK(oparg);
STACK_GROW(1);
- TYPESTACK_POKE(1, &PyTuple_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyTuple_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BUILD_LIST) {
STACK_SHRINK(oparg);
STACK_GROW(1);
- TYPESTACK_POKE(1, &PyList_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyList_Type), TYPESTACK_PEEK(1), true);
break;
}
@@ -473,14 +473,14 @@
TARGET(BUILD_SET) {
STACK_SHRINK(oparg);
STACK_GROW(1);
- TYPESTACK_POKE(1, &PySet_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PySet_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BUILD_MAP) {
STACK_SHRINK(oparg*2);
STACK_GROW(1);
- TYPESTACK_POKE(1, &PyDict_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyDict_Type), TYPESTACK_PEEK(1), true);
break;
}
@@ -490,7 +490,7 @@
TARGET(BUILD_CONST_KEY_MAP) {
STACK_SHRINK(oparg);
- TYPESTACK_POKE(1, &PyDict_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyDict_Type), TYPESTACK_PEEK(1), true);
break;
}
@@ -511,43 +511,43 @@
TARGET(LOAD_ATTR) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_INSTANCE_VALUE) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_MODULE) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_WITH_HINT) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_SLOT) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_CLASS) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
@@ -578,7 +578,7 @@
TARGET(COMPARE_OP) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -604,36 +604,36 @@
TARGET(IS_OP) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyBool_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyBool_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CONTAINS_OP) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyBool_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyBool_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CHECK_EG_MATCH) {
- TYPESTACK_POKE(1, NULL);
- TYPESTACK_POKE(2, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(2), true);
break;
}
TARGET(CHECK_EXC_MATCH) {
- TYPESTACK_POKE(1, &PyBool_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyBool_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(IMPORT_NAME) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(IMPORT_FROM) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -719,13 +719,13 @@
TARGET(GET_LEN) {
STACK_GROW(1);
- TYPESTACK_POKE(1, &PyLong_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
break;
}
TARGET(MATCH_CLASS) {
STACK_SHRINK(2);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -748,42 +748,42 @@
}
TARGET(GET_ITER) {
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(GET_YIELD_FROM_ITER) {
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(FOR_ITER) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(BB_TEST_ITER) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(FOR_ITER_LIST) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(FOR_ITER_TUPLE) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(FOR_ITER_RANGE) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -794,15 +794,15 @@
TARGET(BEFORE_ASYNC_WITH) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
- TYPESTACK_POKE(2, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(2), true);
break;
}
TARGET(BEFORE_WITH) {
STACK_GROW(1);
- TYPESTACK_POKE(1, NULL);
- TYPESTACK_POKE(2, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(2), true);
break;
}
@@ -820,22 +820,22 @@
TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_METHOD_NO_DICT) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_METHOD_LAZY_DICT) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
- if (oparg & 1) { TYPESTACK_POKE(1 + ((oparg & 1) ? 1 : 0), NULL); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
@@ -846,7 +846,7 @@
TARGET(CALL) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -871,63 +871,63 @@
TARGET(CALL_NO_KW_TYPE_1) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_STR_1) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_TUPLE_1) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_BUILTIN_CLASS) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_BUILTIN_O) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_BUILTIN_FAST) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_LEN) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_ISINSTANCE) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -940,41 +940,41 @@
TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_FUNCTION_EX) {
STACK_SHRINK(((oparg & 1) ? 1 : 0));
STACK_SHRINK(2);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(MAKE_FUNCTION) {
STACK_SHRINK(((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
@@ -985,34 +985,34 @@
TARGET(BUILD_SLICE) {
STACK_SHRINK(((oparg == 3) ? 1 : 0));
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(FORMAT_VALUE) {
STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0));
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(COPY) {
- PyTypeObject *bottom = TYPESTACK_PEEK(1 + (oparg-1));
+ _Py_TYPENODE_t *bottom = TYPESTACK_PEEK(1 + (oparg-1));
STACK_GROW(1);
- TYPESTACK_POKE(1, bottom);
+ TYPE_OVERWRITE(bottom, TYPESTACK_PEEK(1), false);
break;
}
TARGET(BINARY_OP) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, NULL);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
break;
}
TARGET(SWAP) {
- PyTypeObject *top = TYPESTACK_PEEK(1);
- PyTypeObject *bottom = TYPESTACK_PEEK(2 + (oparg-2));
- TYPESTACK_POKE(1, bottom);
- TYPESTACK_POKE(2 + (oparg-2), top);
+ _Py_TYPENODE_t *top = TYPESTACK_PEEK(1);
+ _Py_TYPENODE_t *bottom = TYPESTACK_PEEK(2 + (oparg-2));
+ TYPE_OVERWRITE(bottom, TYPESTACK_PEEK(1), false);
+ TYPE_OVERWRITE(top, TYPESTACK_PEEK(2 + (oparg-2)), false);
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 2fa61432f19234..14045e7ceabcb8 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -17,8 +17,8 @@
import parser
from parser import StackEffect
-from parser import TypeAnnotation, TypeSrcLiteral, TypeSrcConst, TypeSrcLocals, TypeSrcStackInput
-from parser import LocalEffect, LocalEffectVarLiteral, LocalEffectVarStack
+from parser import TypeSrcLiteral, TypeSrcConst, TypeSrcLocals, TypeSrcStackInput
+from parser import LocalEffect
HERE = os.path.dirname(__file__)
ROOT = os.path.join(HERE, "../..")
@@ -338,7 +338,7 @@ def write_typeprop(self, out: Formatter) -> None:
need_to_declare = []
# Stack input is used in local effect
if self.local_effects and \
- isinstance(val := self.local_effects.value, LocalEffectVarStack):
+ isinstance(val := self.local_effects.value, TypeSrcStackInput):
need_to_declare.append(val.name)
# Stack input is used in output effect
for oeffect in self.output_effects:
@@ -363,30 +363,46 @@ def write_typeprop(self, out: Formatter) -> None:
list_effect_size([ieff for ieff in ieffects[: i + 1]])
)
all_input_effect_names[ieffect.name] = (ieffect, i)
- dst = StackEffect(ieffect.name, "PyTypeObject *")
+ dst = StackEffect(ieffect.name, "_Py_TYPENODE_t *")
if ieffect.size:
- src = StackEffect(f"&TYPESTACK_PEEK({isize})", "PyTypeObject **")
- dst = StackEffect(ieffect.name, "PyTypeObject **")
+ # TODO: Support more cases as needed
+ raise Exception("Type propagation across sized input effect not implemented")
elif ieffect.cond:
- src = StackEffect(f"({ieffect.cond}) ? TYPESTACK_PEEK({isize}) : NULL", "PyTypeObject *")
+ src = StackEffect(f"({ieffect.cond}) ? TYPESTACK_PEEK({isize}) : NULL", "_Py_TYPENODE_t *")
else:
usable_for_local_effect[ieffect.name] = ieffect
- src = StackEffect(f"TYPESTACK_PEEK({isize})", "PyTypeObject *")
+ src = StackEffect(f"TYPESTACK_PEEK({isize})", "_Py_TYPENODE_t *")
out.declare(dst, src)
# Write localarr effect
if self.local_effects:
+
idx = self.local_effects.index
val = self.local_effects.value
+
+ typ_op = "TYPE_OVERWRITE"
+ dst = f"TYPELOCALS_GET({idx})"
match val:
- case LocalEffectVarLiteral(name=valstr):
- if valstr != "NULL": valstr = f"&{valstr}"
- case LocalEffectVarStack(name=valstr):
+ case TypeSrcLiteral(name=valstr):
+ if valstr == "NULL":
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL)"
+ flag = "true"
+ else:
+ src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
+ flag = "true"
+ case TypeSrcStackInput(name=valstr):
assert valstr in usable_for_local_effect, \
"`cond` and `size` stackvar not supported for localeffect"
+ src = valstr
+ flag = "false"
+ # TODO: Support more cases as needed
+ case TypeSrcConst():
+ raise Exception("Not implemented")
+ case TypeSrcLocals():
+ raise Exception("Not implemented")
case _:
typing.assert_never(val)
- out.emit(f"TYPELOCALS_SET({idx}, {valstr})")
+ out.emit(f"{typ_op}({src}, {dst}, {flag});")
# Update stack size
out.stack_adjust(
@@ -401,38 +417,40 @@ def write_typeprop(self, out: Formatter) -> None:
osize = string_effect_size(
list_effect_size([oeff for oeff in oeffects[: i + 1]])
)
+ dst = f"TYPESTACK_PEEK({osize})"
# Check if it's even used
if oeffect.name == UNUSED: continue
# Check if there's type info
if typ := oeffect.type_annotation:
-
- if typ.op == "TYPE_SET":
- # TODO
- continue
-
- # typ.op == "TYPE_OVERWRITE"
match typ.src:
- case TypeSrcLiteral(literal=val):
- if val != "NULL": val = f"&{val}"
- case TypeSrcLocals(index=idx):
- val = f"TYPELOCALS_GET({idx})"
+ case TypeSrcLiteral(literal=valstr):
+ if valstr == "NULL":
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL)"
+ flag = "true"
+ else:
+ src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
+ flag = "true"
+ case TypeSrcStackInput(name=valstr):
+ assert valstr in need_to_declare
+ assert oeffect.name not in self.unmoved_names
+ src = valstr
+ flag = "false"
case TypeSrcConst(index=idx):
- val = f"TYPECONST_GET({idx})"
- case TypeSrcStackInput(name=val):
- # We determined above that we don't need to write this stack effect
- if val not in need_to_declare:
- continue
- # Unmoved var, don't need to write
- if oeffect.name in self.unmoved_names:
- continue
+ src = f"(_Py_TYPENODE_t *)TYPECONST_GET({idx})"
+ flag = "true"
+ case TypeSrcLocals(index=idx):
+ src = f"TYPELOCALS_GET({idx})"
+ flag = "false"
case _:
- typing.assert_never(typ)
+ typing.assert_never(typ.src)
+
+ opstr = f"{typ.op}({src}, {dst}, {flag})"
if oeffect.cond:
- out.emit(f"if ({oeffect.cond}) {{ TYPESTACK_POKE({osize}, {val}); }}")
+ out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
else:
- out.emit(f"TYPESTACK_POKE({osize}, {val});")
+ out.emit(f"{opstr};")
continue
# Don't touch unmoved stack vars
@@ -440,10 +458,14 @@ def write_typeprop(self, out: Formatter) -> None:
continue
# Just output null
+ typ_op = "TYPE_OVERWRITE"
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL)"
+ flag = "true"
+ opstr = f"{typ_op}({src}, {dst}, {flag})"
if oeffect.cond:
- out.emit(f"if ({oeffect.cond}) {{ TYPESTACK_POKE({osize}, NULL); }}")
+ out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
else:
- out.emit(f"TYPESTACK_POKE({osize}, NULL);")
+ out.emit(f"{opstr};")
def write(self, out: Formatter) -> None:
"""Write one instruction, sans prologue and epilogue."""
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 0f082fd2742f0e..48ad3cefa0fef4 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -126,23 +126,10 @@ class CacheEffect(Node):
size: int
-@dataclass
-class LocalEffectVarLiteral(Node):
- name: str
-
-
-@dataclass
-class LocalEffectVarStack(Node):
- name: str
-
-
-LocalEffectVar: TypeAlias = LocalEffectVarLiteral | LocalEffectVarStack
-
-
@dataclass
class LocalEffect(Node):
index: str
- value: LocalEffectVar
+ value: TypeSrc
@dataclass
@@ -404,12 +391,12 @@ def local_effect(self) -> LocalEffect | None:
value = self.require(lx.IDENTIFIER).text.strip()
return LocalEffect(
index,
- LocalEffectVarStack(value)
+ TypeSrcStackInput(value)
)
value = self.require(lx.IDENTIFIER).text.strip()
return LocalEffect(
index,
- LocalEffectVarLiteral(value)
+ TypeSrcLiteral(value)
)
@contextual
From 49b09eb20abeddc878ba2628af9c4d3ac70b2650 Mon Sep 17 00:00:00 2001
From: Julia
Date: Sat, 18 Mar 2023 18:02:03 +0800
Subject: [PATCH 071/280] Fix+Debug: Fixed bugs and print type context
---
Include/cpython/code.h | 4 ++-
Python/tier2.c | 65 ++++++++++++++++++++++++++++++++++++------
2 files changed, 59 insertions(+), 10 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index ee02dc1192b534..d08bf48b9b4e8b 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -69,7 +69,6 @@ typedef enum _Py_TypeNodeTags {
TYPE_REF = 2
} _Py_TypeNodeTags;
-#define _Py_TYPENODE_NULL 0
#define _Py_TYPENODE_GET_TAG(typenode) ((typenode) & (0b11))
#define _Py_TYPENODE_CLEAR_TAG(typenode) ((typenode) & (~(uintptr_t)(0b11)))
@@ -77,6 +76,9 @@ typedef enum _Py_TypeNodeTags {
#define _Py_TYPENODE_MAKE_ROOT(ptr) (_Py_TYPENODE_CLEAR_TAG(ptr) | TYPE_ROOT)
#define _Py_TYPENODE_MAKE_REF(ptr) (_Py_TYPENODE_CLEAR_TAG(ptr) | TYPE_REF)
+#define _Py_TYPENODE_NULL 0
+#define _Py_TYPENODE_NULLROOT _Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL)
+
// Tier 2 types meta interpreter
typedef struct _PyTier2TypeContext {
// points into type_stack, points to one element after the stack
diff --git a/Python/tier2.c b/Python/tier2.c
index 369d13bc6a3d4f..fe7a9a2dc8577e 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -44,10 +44,10 @@ initialize_type_context(const PyCodeObject *co)
// Initialize to unknown type.
for (int i = 0; i < nlocals; i++) {
- type_locals[i] = _Py_TYPENODE_NULL;
+ type_locals[i] = _Py_TYPENODE_NULLROOT;
}
for (int i = 0; i < nstack; i++) {
- type_stack[i] = _Py_TYPENODE_NULL;
+ type_stack[i] = _Py_TYPENODE_NULLROOT;
}
_PyTier2TypeContext *type_context = PyMem_Malloc(sizeof(_PyTier2TypeContext));
@@ -125,18 +125,38 @@ __type_stack_shrink(_Py_TYPENODE_t **type_stackptr, int idx)
}
static _Py_TYPENODE_t*
-__type_typenode_get_rootptr(_Py_TYPENODE_t ref)
+__typenode_get_rootptr(_Py_TYPENODE_t ref)
{
uintptr_t tag;
_Py_TYPENODE_t *ref_ptr;
do {
- ref_ptr = (_Py_TYPENODE_t *)(ref);
+ ref_ptr = (_Py_TYPENODE_t *)(_Py_TYPENODE_CLEAR_TAG(ref));
ref = *ref_ptr;
tag = _Py_TYPENODE_GET_TAG(ref);
} while (tag != TYPE_ROOT);
return ref_ptr;
}
+static PyTypeObject*
+_typenode_get_type(_Py_TYPENODE_t node)
+{
+ uintptr_t tag = _Py_TYPENODE_GET_TAG(node);
+ switch (tag) {
+ case TYPE_NULL: return NULL;
+ case TYPE_ROOT: {
+ PyTypeObject *ret = (PyTypeObject *)_Py_TYPENODE_CLEAR_TAG(node);
+ return ret;
+ }
+ case TYPE_REF: {
+ _Py_TYPENODE_t *root_ref = __typenode_get_rootptr(node);
+ PyTypeObject *ret = (PyTypeObject *)_Py_TYPENODE_CLEAR_TAG(*root_ref);
+ return ret;
+ }
+ default:
+ Py_UNREACHABLE();
+ }
+}
+
static void
__type_propagate_TYPE_SET(
_Py_TYPENODE_t *src, _Py_TYPENODE_t *dst, bool src_is_new)
@@ -167,7 +187,7 @@ __type_propagate_TYPE_SET(
break;
}
case TYPE_REF: {
- _Py_TYPENODE_t *rootref = __type_typenode_get_rootptr(*dst);
+ _Py_TYPENODE_t *rootref = __typenode_get_rootptr(*dst);
if (!src_is_new) {
// Traverse up to the root of dst, make root a reference to src
*rootref = _Py_TYPENODE_MAKE_REF(
@@ -178,6 +198,8 @@ __type_propagate_TYPE_SET(
*rootref = (_Py_TYPENODE_t)src;
break;
}
+ default:
+ Py_UNREACHABLE();
}
}
@@ -306,8 +328,33 @@ __type_propagate_TYPE_OVERWRITE(
}
break;
}
+ default:
+ Py_UNREACHABLE();
+ }
+}
+
+#if TYPEPROP_DEBUG
+static void print_typestack(const _PyTier2TypeContext *type_context)
+{
+ _Py_TYPENODE_t *type_stack = type_context->type_stack;
+ _Py_TYPENODE_t *type_locals = type_context->type_locals;
+ _Py_TYPENODE_t *type_stackptr = type_context->type_stack_ptr;
+
+ int nstack = (int)(type_stackptr - type_stack);
+ fprintf(stderr, " Stack: [");
+ for (int i = 0; i < nstack; i++) {
+ PyTypeObject *type = _typenode_get_type(type_stack[i]);
+ fprintf(stderr, "%s, ", type == NULL ? "?" : type->tp_name);
+ }
+ fprintf(stderr, "]\n");
+ fprintf(stderr, " Locals: [");
+ for (int i = 0; i < type_context->type_locals_len; i++) {
+ PyTypeObject *type = _typenode_get_type(type_locals[i]);
+ fprintf(stderr, "%s, ", type == NULL ? "?" : type->tp_name);
}
+ fprintf(stderr, "]\n");
}
+#endif
// Type propagates across a single function.
static void
@@ -337,7 +384,7 @@ type_propagate(
// Stack shrinking has to NULL the nodes
#define STACK_SHRINK(idx) __type_stack_shrink(&type_stackptr, (idx))
-#ifdef TYPEPROP_DEBUG
+#if TYPEPROP_DEBUG
fprintf(stderr, " [-] Type stack bef: %llu\n", (uint64_t)(type_stackptr - type_stack));
#ifdef Py_DEBUG
fprintf(stderr, " [-] Type propagating across: %s : %d\n", _PyOpcode_OpName[opcode], oparg);
@@ -350,13 +397,13 @@ type_propagate(
fprintf(stderr, "Unsupported opcode in type propagator: %d\n", opcode);
Py_UNREACHABLE();
}
+ type_context->type_stack_ptr = type_stackptr;
-#ifdef TYPEPROP_DEBUG
+#if TYPEPROP_DEBUG
fprintf(stderr, " [-] Type stack aft: %llu\n", (uint64_t)(type_stackptr - type_stack));
+ print_typestack(type_context);
#endif
- type_context->type_stack_ptr = type_stackptr;
-
#undef TARGET
#undef TYPESTACK_PEEK
#undef TYPELOCALS_GET
From f0ed274088353c67346c90d8722d5a79bcf5bb7c Mon Sep 17 00:00:00 2001
From: Julia
Date: Mon, 20 Mar 2023 17:20:47 +0800
Subject: [PATCH 072/280] Fix: Bug fixes to the new type prop
---
Include/cpython/code.h | 1 -
Python/bytecodes.c | 2 +-
Python/tier2.c | 160 ++++++++++++++--------
Python/tier2_typepropagator.c.h | 172 ++++++++++++------------
Tools/cases_generator/generate_cases.py | 6 +-
5 files changed, 196 insertions(+), 145 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 5eeeef6d473d8e..4079cd565cfb74 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -72,7 +72,6 @@ typedef enum _Py_TypeNodeTags {
#define _Py_TYPENODE_GET_TAG(typenode) ((typenode) & (0b11))
#define _Py_TYPENODE_CLEAR_TAG(typenode) ((typenode) & (~(uintptr_t)(0b11)))
-#define _Py_TYPENODE_MAKE_NULL() _Py_TYPENODE_NULL
#define _Py_TYPENODE_MAKE_ROOT(ptr) (_Py_TYPENODE_CLEAR_TAG(ptr) | TYPE_ROOT)
#define _Py_TYPENODE_MAKE_REF(ptr) (_Py_TYPENODE_CLEAR_TAG(ptr) | TYPE_REF)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index fd19017a58891e..72ba844f017f41 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -279,7 +279,7 @@ dummy_func(
U_INST(BINARY_OP_ADD_FLOAT_REST);
}
- inst(BINARY_CHECK_FLOAT, (left, right -- left : PyFloat_Type, right : PyFloat_Type)) {
+ inst(BINARY_CHECK_FLOAT, (left, right -- left : <<= PyFloat_Type, right : <<= PyFloat_Type)) {
assert(cframe.use_tracing == 0);
bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
}
diff --git a/Python/tier2.c b/Python/tier2.c
index 6219cd6e036106..9a86c55ba1fc10 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -72,6 +72,8 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
fprintf(stderr, " [*] Copying type context\n");
#endif
+ _Py_TYPENODE_t *orig_type_locals = type_context->type_locals;
+ _Py_TYPENODE_t *orig_type_stack = type_context->type_stack;
int nlocals = type_context->type_locals_len;
int nstack = type_context->type_stack_len;
@@ -85,15 +87,65 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
return NULL;
}
- memcpy(type_locals, type_context->type_locals, nlocals * sizeof(_Py_TYPENODE_t));
- memcpy(type_stack, type_context->type_stack, nstack * sizeof(_Py_TYPENODE_t));
-
_PyTier2TypeContext *new_type_context = PyMem_Malloc(sizeof(_PyTier2TypeContext));
if (new_type_context == NULL) {
PyMem_Free(type_locals);
PyMem_Free(type_stack);
return NULL;
}
+
+ for (int i = 0; i < nlocals; i++) {
+ _Py_TYPENODE_t node = type_context->type_locals[i];
+ switch _Py_TYPENODE_GET_TAG(node)
+ {
+ case TYPE_ROOT:
+ type_locals[i] = node;
+ break;
+ case TYPE_REF: {
+ // Check if part of locals
+ _Py_TYPENODE_t *parent = (_Py_TYPENODE_t *)_Py_TYPENODE_CLEAR_TAG(node);
+
+ // Check if part of locals
+ if (parent - type_context->type_locals < nlocals) {
+ type_locals[i] = node - (uintptr_t)orig_type_locals + (uintptr_t)type_locals;
+ }
+ // Is part of stack
+ else {
+ type_locals[i] = node - (uintptr_t)orig_type_stack + (uintptr_t)type_stack;
+ }
+ break;
+ }
+ default:
+ Py_UNREACHABLE();
+ }
+ }
+
+ for (int i = 0; i < nstack; i++) {
+ _Py_TYPENODE_t node = type_context->type_stack[i];
+ switch _Py_TYPENODE_GET_TAG(node)
+ {
+ case TYPE_ROOT:
+ type_stack[i] = node;
+ break;
+ case TYPE_REF: {
+ // Check if part of locals
+ _Py_TYPENODE_t *parent = (_Py_TYPENODE_t *)_Py_TYPENODE_CLEAR_TAG(node);
+
+ // Check if part of locals
+ if (parent - type_context->type_locals < nlocals) {
+ type_stack[i] = node - (uintptr_t)orig_type_locals + (uintptr_t)type_locals;
+ }
+ // Is part of stack
+ else {
+ type_stack[i] = node - (uintptr_t)orig_type_stack + (uintptr_t)type_stack;
+ }
+ break;
+ }
+ default:
+ Py_UNREACHABLE();
+ }
+ }
+
new_type_context->type_locals_len = nlocals;
new_type_context->type_stack_len = nstack;
new_type_context->type_locals = type_locals;
@@ -115,15 +167,6 @@ _PyTier2TypeContext_Free(_PyTier2TypeContext *type_context)
PyMem_Free(type_context);
}
-static void
-__type_stack_shrink(_Py_TYPENODE_t **type_stackptr, int idx)
-{
- while (idx--) {
- **type_stackptr = _Py_TYPENODE_MAKE_NULL();
- *type_stackptr -= 1;
- }
-}
-
static _Py_TYPENODE_t*
__typenode_get_rootptr(_Py_TYPENODE_t ref)
{
@@ -142,7 +185,6 @@ _typenode_get_type(_Py_TYPENODE_t node)
{
uintptr_t tag = _Py_TYPENODE_GET_TAG(node);
switch (tag) {
- case TYPE_NULL: return NULL;
case TYPE_ROOT: {
PyTypeObject *ret = (PyTypeObject *)_Py_TYPENODE_CLEAR_TAG(node);
return ret;
@@ -174,12 +216,10 @@ __type_propagate_TYPE_SET(
uintptr_t tag = _Py_TYPENODE_GET_TAG(*dst);
switch (tag) {
- case TYPE_NULL:
case TYPE_ROOT: {
if (!src_is_new) {
// Make dst a reference to src
- *dst = _Py_TYPENODE_MAKE_REF(
- _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ *dst = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)src);
break;
}
// Make dst the src
@@ -190,8 +230,7 @@ __type_propagate_TYPE_SET(
_Py_TYPENODE_t *rootref = __typenode_get_rootptr(*dst);
if (!src_is_new) {
// Traverse up to the root of dst, make root a reference to src
- *rootref = _Py_TYPENODE_MAKE_REF(
- _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ *rootref = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)src);
break;
}
// Make root of dst the src
@@ -218,24 +257,12 @@ __type_propagate_TYPE_OVERWRITE(
uintptr_t tag = _Py_TYPENODE_GET_TAG(*dst);
switch (tag) {
- case TYPE_NULL: {
- if (!src_is_new) {
- // Make dst a reference to src
- *dst = _Py_TYPENODE_MAKE_REF(
- _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
- break;
- }
- // Make dst the src
- *dst = (_Py_TYPENODE_t)src;
- break;
- }
case TYPE_ROOT: {
_Py_TYPENODE_t old_dst = *dst;
if (!src_is_new) {
// Make dst a reference to src
- *dst = _Py_TYPENODE_MAKE_REF(
- _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ *dst = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)src);
}
else {
// Make dst the src
@@ -262,14 +289,13 @@ __type_propagate_TYPE_OVERWRITE(
}
else {
// Not the first child encounted, point it to the new root
- *node_ptr = _Py_TYPENODE_MAKE_REF(
- _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)new_root_ptr));
+ *node_ptr = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)new_root_ptr);
}
}
}
// Search stack for children
- int nstack = (int)(type_context->type_stack_ptr - type_context->type_stack);
+ int nstack = type_context->type_stack_len;
for (int i = 0; i < nstack; i++) {
_Py_TYPENODE_t *node_ptr = &(type_context->type_stack[i]);
if (*node_ptr == child_test) {
@@ -280,8 +306,7 @@ __type_propagate_TYPE_OVERWRITE(
}
else {
// Not the first child encounted, point it to the new root
- *node_ptr = _Py_TYPENODE_MAKE_REF(
- _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)new_root_ptr));
+ *node_ptr = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)new_root_ptr);
}
}
}
@@ -293,8 +318,7 @@ __type_propagate_TYPE_OVERWRITE(
_Py_TYPENODE_t old_dst = *dst;
if (!src_is_new) {
// Make dst a reference to src
- *dst = _Py_TYPENODE_MAKE_REF(
- _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ *dst = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)src);
}
else {
// Make dst the src
@@ -318,7 +342,7 @@ __type_propagate_TYPE_OVERWRITE(
}
// Search stack for children
- int nstack = (int)(type_context->type_stack_ptr - type_context->type_stack);
+ int nstack = type_context->type_stack_len;
for (int i = 0; i < nstack; i++) {
_Py_TYPENODE_t *node_ptr = &(type_context->type_stack[i]);
if (*node_ptr == child_test) {
@@ -333,24 +357,53 @@ __type_propagate_TYPE_OVERWRITE(
}
}
+static void
+__type_stack_shrink(_PyTier2TypeContext *type_context, _Py_TYPENODE_t **type_stackptr, int idx)
+{
+ while (idx--) {
+ // TODO:
+ // If we don't touch the stack elements
+ // when shrinking, we need to check for references
+ // on these elements.
+ // Otherwise, if we NULL these elements, we need to refactor
+ // the type propagator to perform shrinking last.
+ //
+ // __type_propagate_TYPE_OVERWRITE(
+ // type_context,
+ // (_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, *type_stackptr,
+ // true);
+ *type_stackptr -= 1;
+ }
+}
+
#if TYPEPROP_DEBUG
-static void print_typestack(const _PyTier2TypeContext *type_context)
+
+static void
+print_typestack(const _PyTier2TypeContext *type_context)
{
_Py_TYPENODE_t *type_stack = type_context->type_stack;
_Py_TYPENODE_t *type_locals = type_context->type_locals;
_Py_TYPENODE_t *type_stackptr = type_context->type_stack_ptr;
- int nstack = (int)(type_stackptr - type_stack);
- fprintf(stderr, " Stack: [");
+ int nstack_use = (int)(type_stackptr - type_stack);
+ int nstack = type_context->type_stack_len;
+ fprintf(stderr, " Stack: %p: [", type_stack);
for (int i = 0; i < nstack; i++) {
PyTypeObject *type = _typenode_get_type(type_stack[i]);
- fprintf(stderr, "%s, ", type == NULL ? "?" : type->tp_name);
+ _Py_TYPENODE_t tag = _Py_TYPENODE_GET_TAG(type_stack[i]);
+ fprintf(stderr, "%s%s%s",
+ i == nstack_use ? "." : " ",
+ type == NULL ? "?" : type->tp_name,
+ tag == TYPE_REF ? "*" : "");
}
fprintf(stderr, "]\n");
- fprintf(stderr, " Locals: [");
+ fprintf(stderr, " Locals %p: [", type_locals);
for (int i = 0; i < type_context->type_locals_len; i++) {
PyTypeObject *type = _typenode_get_type(type_locals[i]);
- fprintf(stderr, "%s, ", type == NULL ? "?" : type->tp_name);
+ _Py_TYPENODE_t tag = _Py_TYPENODE_GET_TAG(type_locals[i]);
+ fprintf(stderr, "%s%s ",
+ type == NULL ? "?" : type->tp_name,
+ tag == TYPE_REF ? "*" : "");
}
fprintf(stderr, "]\n");
}
@@ -365,10 +418,10 @@ type_propagate(
{
_Py_TYPENODE_t *type_stack = type_context->type_stack;
_Py_TYPENODE_t *type_locals = type_context->type_locals;
- _Py_TYPENODE_t *type_stackptr = type_context->type_stack_ptr;
+ _Py_TYPENODE_t **type_stackptr = &type_context->type_stack_ptr;
#define TARGET(op) case op:
-#define TYPESTACK_PEEK(idx) (&(type_stackptr[-(idx)]))
+#define TYPESTACK_PEEK(idx) (&((*type_stackptr)[-(idx)]))
#define TYPELOCALS_GET(idx) (&(type_locals[idx]))
// Get the type of the const and make into a TYPENODE ROOT
@@ -379,13 +432,13 @@ type_propagate(
#define TYPE_SET(src, dst, flag) __type_propagate_TYPE_SET((src), (dst), (flag))
#define TYPE_OVERWRITE(src, dst, flag) __type_propagate_TYPE_OVERWRITE(type_context, (src), (dst), (flag))
-#define STACK_GROW(idx) type_stackptr += (idx)
+#define STACK_GROW(idx) *type_stackptr += (idx)
// Stack shrinking has to NULL the nodes
-#define STACK_SHRINK(idx) __type_stack_shrink(&type_stackptr, (idx))
+#define STACK_SHRINK(idx) __type_stack_shrink(type_context, type_stackptr, (idx))
#if TYPEPROP_DEBUG
- fprintf(stderr, " [-] Type stack bef: %llu\n", (uint64_t)(type_stackptr - type_stack));
+ fprintf(stderr, " [-] Type stack bef: %llu\n", (uint64_t)(*type_stackptr - type_stack));
#ifdef Py_DEBUG
fprintf(stderr, " [-] Type propagating across: %s : %d\n", _PyOpcode_OpName[opcode], oparg);
#endif
@@ -397,10 +450,9 @@ type_propagate(
fprintf(stderr, "Unsupported opcode in type propagator: %d\n", opcode);
Py_UNREACHABLE();
}
- type_context->type_stack_ptr = type_stackptr;
#if TYPEPROP_DEBUG
- fprintf(stderr, " [-] Type stack aft: %llu\n", (uint64_t)(type_stackptr - type_stack));
+ fprintf(stderr, " [-] Type stack aft: %llu\n", (uint64_t)(*type_stackptr - type_stack));
print_typestack(type_context);
#endif
@@ -977,8 +1029,8 @@ infer_BINARY_OP_ADD(
int bb_id)
{
*needs_guard = false;
- PyTypeObject *right = type_context->type_stack_ptr[-1];
- PyTypeObject *left = type_context->type_stack_ptr[-2];
+ PyTypeObject *right = _typenode_get_type(type_context->type_stack_ptr[-1]);
+ PyTypeObject *left = _typenode_get_type(type_context->type_stack_ptr[-2]);
if (left == &PyLong_Type) {
if (right == &PyLong_Type) {
write_curr->op.code = BINARY_OP_ADD_INT_REST;
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 196eb912d9b281..72c7eb9cc4d60f 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -53,7 +53,7 @@
TARGET(PUSH_NULL) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -68,17 +68,17 @@
}
TARGET(UNARY_NEGATIVE) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(UNARY_NOT) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(UNARY_INVERT) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -124,14 +124,14 @@
}
TARGET(BINARY_CHECK_FLOAT) {
- TYPESTACK_POKE(1, &PyFloat_Type);
- TYPESTACK_POKE(2, &PyFloat_Type);
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(2), true);
break;
}
TARGET(BINARY_OP_ADD_FLOAT_REST) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyFloat_Type);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
break;
}
@@ -155,13 +155,13 @@
TARGET(BINARY_SUBSCR) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_SLICE) {
STACK_SHRINK(2);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -172,19 +172,19 @@
TARGET(BINARY_SUBSCR_LIST_INT) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_SUBSCR_TUPLE_INT) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_SUBSCR_DICT) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -224,13 +224,13 @@
}
TARGET(CALL_INTRINSIC_1) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_INTRINSIC_2) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -255,18 +255,18 @@
}
TARGET(GET_AITER) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(GET_ANEXT) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(GET_AWAITABLE) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -307,20 +307,20 @@
TARGET(CLEANUP_THROW) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(2), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(2), true);
break;
}
TARGET(LOAD_ASSERTION_ERROR) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(LOAD_BUILD_CLASS) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -342,21 +342,21 @@
TARGET(UNPACK_SEQUENCE_TWO_TUPLE) {
STACK_SHRINK(1);
STACK_GROW(oparg);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(oparg), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg), true);
break;
}
TARGET(UNPACK_SEQUENCE_TUPLE) {
STACK_SHRINK(1);
STACK_GROW(oparg);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(oparg), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg), true);
break;
}
TARGET(UNPACK_SEQUENCE_LIST) {
STACK_SHRINK(1);
STACK_GROW(oparg);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(oparg), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg), true);
break;
}
@@ -386,31 +386,31 @@
TARGET(LOAD_NAME) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(LOAD_GLOBAL) {
STACK_GROW(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_GLOBAL_MODULE) {
STACK_GROW(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_GLOBAL_BUILTIN) {
STACK_GROW(1);
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
@@ -432,7 +432,7 @@
TARGET(LOAD_CLASSDEREF) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -523,43 +523,43 @@
TARGET(LOAD_ATTR) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_INSTANCE_VALUE) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_MODULE) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_WITH_HINT) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_SLOT) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_CLASS) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
@@ -590,7 +590,7 @@
TARGET(COMPARE_OP) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -627,8 +627,8 @@
}
TARGET(CHECK_EG_MATCH) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(2), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(2), true);
break;
}
@@ -639,13 +639,13 @@
TARGET(IMPORT_NAME) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(IMPORT_FROM) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -737,7 +737,7 @@
TARGET(MATCH_CLASS) {
STACK_SHRINK(2);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -760,42 +760,42 @@
}
TARGET(GET_ITER) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(GET_YIELD_FROM_ITER) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(FOR_ITER) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(BB_TEST_ITER) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(FOR_ITER_LIST) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(FOR_ITER_TUPLE) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(FOR_ITER_RANGE) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -806,15 +806,15 @@
TARGET(BEFORE_ASYNC_WITH) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(2), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(2), true);
break;
}
TARGET(BEFORE_WITH) {
STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(2), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(2), true);
break;
}
@@ -832,22 +832,22 @@
TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_METHOD_NO_DICT) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
TARGET(LOAD_ATTR_METHOD_LAZY_DICT) {
STACK_GROW(((oparg & 1) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
- if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
break;
}
@@ -858,7 +858,7 @@
TARGET(CALL) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -883,63 +883,63 @@
TARGET(CALL_NO_KW_TYPE_1) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_STR_1) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_TUPLE_1) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_BUILTIN_CLASS) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_BUILTIN_O) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_BUILTIN_FAST) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_LEN) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_ISINSTANCE) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -952,41 +952,41 @@
TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) {
STACK_SHRINK(oparg);
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(CALL_FUNCTION_EX) {
STACK_SHRINK(((oparg & 1) ? 1 : 0));
STACK_SHRINK(2);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(MAKE_FUNCTION) {
STACK_SHRINK(((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -997,13 +997,13 @@
TARGET(BUILD_SLICE) {
STACK_SHRINK(((oparg == 3) ? 1 : 0));
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(FORMAT_VALUE) {
STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0));
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -1016,7 +1016,7 @@
TARGET(BINARY_OP) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 14045e7ceabcb8..e67608a83ffea3 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -385,7 +385,7 @@ def write_typeprop(self, out: Formatter) -> None:
match val:
case TypeSrcLiteral(name=valstr):
if valstr == "NULL":
- src = "(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL)"
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
flag = "true"
else:
src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
@@ -427,7 +427,7 @@ def write_typeprop(self, out: Formatter) -> None:
match typ.src:
case TypeSrcLiteral(literal=valstr):
if valstr == "NULL":
- src = "(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL)"
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
flag = "true"
else:
src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
@@ -459,7 +459,7 @@ def write_typeprop(self, out: Formatter) -> None:
# Just output null
typ_op = "TYPE_OVERWRITE"
- src = "(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT(_Py_TYPENODE_NULL)"
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
flag = "true"
opstr = f"{typ_op}({src}, {dst}, {flag})"
if oeffect.cond:
From 63fa5bbe3f32bfc46f64eb1b6c3d43775c3b7bf7 Mon Sep 17 00:00:00 2001
From: Julia
Date: Mon, 20 Mar 2023 17:44:02 +0800
Subject: [PATCH 073/280] Refactor: rename _typenode_get_type to
typenode_get_type
---
Python/tier2.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 9a86c55ba1fc10..325bd99728a556 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -181,7 +181,7 @@ __typenode_get_rootptr(_Py_TYPENODE_t ref)
}
static PyTypeObject*
-_typenode_get_type(_Py_TYPENODE_t node)
+typenode_get_type(_Py_TYPENODE_t node)
{
uintptr_t tag = _Py_TYPENODE_GET_TAG(node);
switch (tag) {
@@ -389,7 +389,7 @@ print_typestack(const _PyTier2TypeContext *type_context)
int nstack = type_context->type_stack_len;
fprintf(stderr, " Stack: %p: [", type_stack);
for (int i = 0; i < nstack; i++) {
- PyTypeObject *type = _typenode_get_type(type_stack[i]);
+ PyTypeObject *type = typenode_get_type(type_stack[i]);
_Py_TYPENODE_t tag = _Py_TYPENODE_GET_TAG(type_stack[i]);
fprintf(stderr, "%s%s%s",
i == nstack_use ? "." : " ",
@@ -399,7 +399,7 @@ print_typestack(const _PyTier2TypeContext *type_context)
fprintf(stderr, "]\n");
fprintf(stderr, " Locals %p: [", type_locals);
for (int i = 0; i < type_context->type_locals_len; i++) {
- PyTypeObject *type = _typenode_get_type(type_locals[i]);
+ PyTypeObject *type = typenode_get_type(type_locals[i]);
_Py_TYPENODE_t tag = _Py_TYPENODE_GET_TAG(type_locals[i]);
fprintf(stderr, "%s%s ",
type == NULL ? "?" : type->tp_name,
@@ -1029,8 +1029,8 @@ infer_BINARY_OP_ADD(
int bb_id)
{
*needs_guard = false;
- PyTypeObject *right = _typenode_get_type(type_context->type_stack_ptr[-1]);
- PyTypeObject *left = _typenode_get_type(type_context->type_stack_ptr[-2]);
+ PyTypeObject *right = typenode_get_type(type_context->type_stack_ptr[-1]);
+ PyTypeObject *left = typenode_get_type(type_context->type_stack_ptr[-2]);
if (left == &PyLong_Type) {
if (right == &PyLong_Type) {
write_curr->op.code = BINARY_OP_ADD_INT_REST;
From e7e69be8f795b849a4f2f18a09adf9350f168183 Mon Sep 17 00:00:00 2001
From: Julia
Date: Mon, 20 Mar 2023 18:26:40 +0800
Subject: [PATCH 074/280] Refactor: Simplify __type_stack_shrink
---
Python/tier2.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 325bd99728a556..a681dae6c31946 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -360,20 +360,20 @@ __type_propagate_TYPE_OVERWRITE(
static void
__type_stack_shrink(_PyTier2TypeContext *type_context, _Py_TYPENODE_t **type_stackptr, int idx)
{
- while (idx--) {
- // TODO:
- // If we don't touch the stack elements
- // when shrinking, we need to check for references
- // on these elements.
- // Otherwise, if we NULL these elements, we need to refactor
- // the type propagator to perform shrinking last.
- //
- // __type_propagate_TYPE_OVERWRITE(
- // type_context,
- // (_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, *type_stackptr,
- // true);
- *type_stackptr -= 1;
- }
+ // TODO:
+ // If we don't touch the stack elements
+ // when shrinking, we need to check for references
+ // on these elements.
+ // Otherwise, if we NULL these elements, we need to refactor
+ // the type propagator to perform shrinking last.
+ //while (idx--) {
+ // __type_propagate_TYPE_OVERWRITE(
+ // type_context,
+ // (_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, *type_stackptr,
+ // true);
+ // *type_stackptr -= 1;
+ //}
+ *type_stackptr -= idx;
}
#if TYPEPROP_DEBUG
From bed5b8aac0e5bc7fe6395eaf678ef2e27b820d80 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 20 Mar 2023 19:56:23 +0800
Subject: [PATCH 075/280] fix allocation issues
---
Python/tier2.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 607dd58f98880b..0b33b89e4e4490 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -204,12 +204,12 @@ _PyCode_GetLogicalEnd(PyCodeObject *co)
static _PyTier2BBSpace *
_PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
{
- _PyTier2BBSpace *bb_space = PyMem_Malloc(space_to_alloc);
+ _PyTier2BBSpace *bb_space = PyMem_Malloc(space_to_alloc + sizeof(_PyTier2BBSpace));
if (bb_space == NULL) {
return NULL;
}
bb_space->water_level = 0;
- bb_space->max_capacity = (space_to_alloc - sizeof(_PyTier2BBSpace));
+ bb_space->max_capacity = space_to_alloc;
return bb_space;
}
@@ -226,13 +226,15 @@ _PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_reque
if (curr->water_level + space_requested > curr->max_capacity) {
// Note: overallocate
Py_ssize_t new_size = sizeof(_PyTier2BBSpace) + (curr->water_level + space_requested) * 2;
+#if BB_DEBUG
+ fprintf(stderr, "Allocating new BB of size %lld\n", new_size);
+#endif
_PyTier2BBSpace *new_space = PyMem_Realloc(curr, new_size);
if (new_space == NULL) {
return NULL;
}
co->_tier2_info->_bb_space = new_space;
new_space->max_capacity = new_size;
- PyMem_Free(curr);
return new_space;
}
// We have enouogh space. Don't do anything, j
From d144e35f14573e2af89e374e4d4730beb92b1a21 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 20 Mar 2023 20:30:34 +0800
Subject: [PATCH 076/280] temp commit
---
Python/bytecodes.c | 8 +++++++-
Python/generated_cases.c.h | 10 ++++++++++
Python/tier2_typepropagator.c.h | 4 ++--
3 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 7e93f7acfa29f9..2af655962b46f3 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -279,9 +279,15 @@ dummy_func(
U_INST(BINARY_OP_ADD_FLOAT_REST);
}
- inst(BINARY_CHECK_FLOAT, (left, right -- left : PyFloat_Type, right : PyFloat_Type)) {
+ inst(BINARY_CHECK_FLOAT, (left, right -- left_unboxed : PyRawFloat_Type, left_unboxed : PyRawFloat_Type)) {
assert(cframe.use_tracing == 0);
bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ left_unboxed = (bb_test
+ ? *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)))
+ : left);
+ right_unboxed = (bb_test
+ ? *((PyObject **)(&(((PyFloatObject *)right)->ob_fval)))
+ : right);
}
u_inst(BINARY_OP_ADD_FLOAT_REST, (left, right -- sum : PyFloat_Type)) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 56a7fbb050b03e..e4565444a4500b 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -398,8 +398,18 @@
TARGET(BINARY_CHECK_FLOAT) {
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
+ PyObject *left_unboxed;
+ PyObject *left_unboxed;
assert(cframe.use_tracing == 0);
bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ left_unboxed = (bb_test
+ ? *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)))
+ : left);
+ right_unboxed = (bb_test
+ ? *((PyObject **)(&(((PyFloatObject *)right)->ob_fval)))
+ : right);
+ stack_pointer[-1] = left_unboxed;
+ stack_pointer[-2] = left_unboxed;
DISPATCH();
}
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index eb83b84f92b67c..6dcabba76bfd15 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -124,8 +124,8 @@
}
TARGET(BINARY_CHECK_FLOAT) {
- TYPESTACK_POKE(1, &PyFloat_Type);
- TYPESTACK_POKE(2, &PyFloat_Type);
+ TYPESTACK_POKE(1, &PyRawFloat_Type);
+ TYPESTACK_POKE(2, &PyRawFloat_Type);
break;
}
From 183d0094e0114bb66b7b35cf192cd8063b43f804 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 20 Mar 2023 21:06:57 +0800
Subject: [PATCH 077/280] Support unboxed floats
---
Include/internal/pycore_code.h | 4 -
Include/internal/pycore_frame.h | 17 +++-
Include/internal/pycore_opcode.h | 23 ++---
Include/opcode.h | 11 +-
Lib/opcode.py | 15 ++-
Objects/codeobject.c | 5 +-
Python/bytecodes.c | 46 +++++++--
Python/ceval_macros.h | 2 +
Python/frame.c | 4 +
Python/generated_cases.c.h | 79 ++++++++++++---
Python/opcode_metadata.h | 41 +++++++-
Python/tier2.c | 168 +++++++++++++++++++++++++++++--
Python/tier2_typepropagator.c.h | 46 ++++++++-
Tools/build/deepfreeze.py | 3 +-
14 files changed, 401 insertions(+), 63 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 8d1b5599972de0..bfda105c61d4dc 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -529,10 +529,6 @@ extern uint32_t _Py_next_func_version;
#define COMPARISON_NOT_EQUALS (COMPARISON_UNORDERED | COMPARISON_LESS_THAN | COMPARISON_GREATER_THAN)
-
-#define BB_SUC 1
-#define BB_ALT 0
-
#ifdef __cplusplus
}
#endif
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index b8710bd627e67c..b60bd5e25aa3d7 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -62,7 +62,7 @@ typedef struct _PyInterpreterFrame {
int stacktop; /* Offset of TOS from localsplus */
uint16_t yield_offset;
char owner;
- /* Locals and stack */
+ /* Locals and stack and unboxed bit mask */
PyObject *localsplus[1];
} _PyInterpreterFrame;
@@ -105,7 +105,10 @@ _PyFrame_NumSlotsForCodeObject(PyCodeObject *code)
/* This function needs to remain in sync with the calculation of
* co_framesize in Tools/build/deepfreeze.py */
assert(code->co_framesize >= FRAME_SPECIALS_SIZE);
- return code->co_framesize - FRAME_SPECIALS_SIZE;
+ int res = code->co_framesize - FRAME_SPECIALS_SIZE -
+ (code->co_nlocalsplus * sizeof(char) / sizeof(PyObject *) + 1);
+ assert(res > 0);
+ return res;
}
void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest);
@@ -126,7 +129,6 @@ _PyFrame_Initialize(
frame->f_locals = locals;
frame->stacktop = code->co_nlocalsplus;
frame->frame_obj = NULL;
- // @TODO CHANGE ME
if (code->_tier2_info != NULL) {
frame->prev_instr = code->_tier2_info->_entry_bb->tier2_start - 1;
}
@@ -141,6 +143,15 @@ _PyFrame_Initialize(
}
}
+// The unboxed bitmask. true indicates an unboxed value. false indicates a normal PyObject.
+static inline char*
+_PyFrame_GetUnboxedBitMask(_PyInterpreterFrame *frame)
+{
+ PyCodeObject *co = frame->f_code;
+ return (char *)(frame + co->co_framesize -
+ (co->co_nlocalsplus * sizeof(char) / sizeof(PyObject *) + 1));
+}
+
/* Gets the pointer to the locals array
* that precedes this frame.
*/
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 5be22719a3aec6..ab2ff150d4bede 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -428,15 +428,15 @@ static const char *const _PyOpcode_OpName[263] = {
[BB_JUMP_BACKWARD_LAZY] = "BB_JUMP_BACKWARD_LAZY",
[BINARY_CHECK_INT] = "BINARY_CHECK_INT",
[BINARY_CHECK_FLOAT] = "BINARY_CHECK_FLOAT",
+ [UNARY_CHECK_FLOAT] = "UNARY_CHECK_FLOAT",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
- [BINARY_OP_ADD_FLOAT_REST] = "BINARY_OP_ADD_FLOAT_REST",
- [193] = "<193>",
- [194] = "<194>",
- [195] = "<195>",
- [196] = "<196>",
- [197] = "<197>",
- [198] = "<198>",
- [199] = "<199>",
+ [BINARY_OP_ADD_FLOAT_UNBOXED] = "BINARY_OP_ADD_FLOAT_UNBOXED",
+ [UNBOX_FLOAT] = "UNBOX_FLOAT",
+ [BOX_FLOAT] = "BOX_FLOAT",
+ [LOAD_FAST_NO_INCREF] = "LOAD_FAST_NO_INCREF",
+ [STORE_FAST_BOXED_UNBOXED] = "STORE_FAST_BOXED_UNBOXED",
+ [STORE_FAST_UNBOXED_BOXED] = "STORE_FAST_UNBOXED_BOXED",
+ [STORE_FAST_UNBOXED_UNBOXED] = "STORE_FAST_UNBOXED_UNBOXED",
[200] = "<200>",
[201] = "<201>",
[202] = "<202>",
@@ -505,13 +505,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 193: \
- case 194: \
- case 195: \
- case 196: \
- case 197: \
- case 198: \
- case 199: \
case 200: \
case 201: \
case 202: \
diff --git a/Include/opcode.h b/Include/opcode.h
index f5c6d7c5138c09..a1570a55260b07 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -280,8 +280,15 @@ extern "C" {
#define BB_JUMP_BACKWARD_LAZY 188
#define BINARY_CHECK_INT 189
#define BINARY_CHECK_FLOAT 190
-#define BINARY_OP_ADD_INT_REST 191
-#define BINARY_OP_ADD_FLOAT_REST 192
+#define UNARY_CHECK_FLOAT 191
+#define BINARY_OP_ADD_INT_REST 192
+#define BINARY_OP_ADD_FLOAT_UNBOXED 193
+#define UNBOX_FLOAT 194
+#define BOX_FLOAT 195
+#define LOAD_FAST_NO_INCREF 196
+#define STORE_FAST_BOXED_UNBOXED 197
+#define STORE_FAST_UNBOXED_BOXED 198
+#define STORE_FAST_UNBOXED_UNBOXED 199
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 2652223a50919d..aa1b33a5187c72 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -504,7 +504,20 @@ def pseudo_op(name, op, real_ops):
# single operand forms.
'BINARY_CHECK_INT',
'BINARY_CHECK_FLOAT',
+ 'UNARY_CHECK_FLOAT',
# 'BINARY_CHECK_STR',
'BINARY_OP_ADD_INT_REST',
- 'BINARY_OP_ADD_FLOAT_REST',
+ 'BINARY_OP_ADD_FLOAT_UNBOXED',
+
+ # Boxing / unboxing ops
+ 'UNBOX_FLOAT',
+ 'BOX_FLOAT',
+ 'LOAD_FAST_NO_INCREF',
+ # Storing a boxed value, overwriting an unboxed local.
+ 'STORE_FAST_BOXED_UNBOXED',
+ # Storing an unboxed value, overwriting a boxed local.
+ 'STORE_FAST_UNBOXED_BOXED',
+ # Storing an unboxed value, overwriting an unboxed local.
+ 'STORE_FAST_UNBOXED_UNBOXED',
+ # The traditional STORE_FAST is storing a boxed value, overwriting a boxed local.
]
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index ea72668a5f210c..3b1edc08f54786 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -424,7 +424,10 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
/* derived values */
co->co_nlocalsplus = nlocalsplus;
co->co_nlocals = nlocals;
- co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE;
+ co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE +
+ // + this because at the end of the frame, we store the bit masks
+ // that indicate whether this value is unboxed or not
+ (nlocalsplus * sizeof(char) / sizeof(PyObject *) + 1);
co->co_ncellvars = ncellvars;
co->co_nfreevars = nfreevars;
co->co_version = _Py_next_func_version;
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 2af655962b46f3..9870ba98db1009 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -121,6 +121,10 @@ dummy_func(
Py_INCREF(value);
}
+ inst(LOAD_FAST_NO_INCREF, (-- value : locals[oparg])) {
+ value = GETLOCAL(oparg);
+ }
+
inst(LOAD_CONST, (-- value : consts[oparg])) {
value = GETITEM(consts, oparg);
Py_INCREF(value);
@@ -130,6 +134,20 @@ dummy_func(
SETLOCAL(oparg, value);
}
+ inst(STORE_FAST_BOXED_UNBOXED, (value --), locals[oparg] = *value) {
+ SETLOCAL_NO_DECREF(oparg, value);
+ _PyFrame_GetUnboxedBitMask(frame)[oparg] = false;
+ }
+
+ inst(STORE_FAST_UNBOXED_BOXED, (value--), locals[oparg] = *value) {
+ SETLOCAL(oparg, value);
+ _PyFrame_GetUnboxedBitMask(frame)[oparg] = true;
+ }
+
+ inst(STORE_FAST_UNBOXED_UNBOXED, (value--), locals[oparg] = *value) {
+ SETLOCAL_NO_DECREF(oparg, value);
+ }
+
super(LOAD_FAST__LOAD_FAST) = LOAD_FAST + LOAD_FAST;
super(LOAD_FAST__LOAD_CONST) = LOAD_FAST + LOAD_CONST;
super(STORE_FAST__LOAD_FAST) = STORE_FAST + LOAD_FAST;
@@ -276,10 +294,13 @@ dummy_func(
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
- U_INST(BINARY_OP_ADD_FLOAT_REST);
+ STAT_INC(BINARY_OP, hit);
+ double dsum = ((PyFloatObject *)left)->ob_fval +
+ ((PyFloatObject *)right)->ob_fval;
+ DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dsum, sum);
}
- inst(BINARY_CHECK_FLOAT, (left, right -- left_unboxed : PyRawFloat_Type, left_unboxed : PyRawFloat_Type)) {
+ inst(BINARY_CHECK_FLOAT, (left, right -- left_unboxed : PyRawFloat_Type, right_unboxed : PyRawFloat_Type)) {
assert(cframe.use_tracing == 0);
bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
left_unboxed = (bb_test
@@ -290,11 +311,24 @@ dummy_func(
: right);
}
- u_inst(BINARY_OP_ADD_FLOAT_REST, (left, right -- sum : PyFloat_Type)) {
+ inst(UNARY_CHECK_FLOAT, (arg, unused[oparg] -- arg : PyFloat_Type, unused[oparg])) {
+ assert(cframe.use_tracing == 0);
+ bb_test = PyFloat_CheckExact(arg);
+ }
+
+ inst(BINARY_OP_ADD_FLOAT_UNBOXED, (left, right -- sum : PyRawFloat_Type)) {
STAT_INC(BINARY_OP, hit);
- double dsum = ((PyFloatObject *)left)->ob_fval +
- ((PyFloatObject *)right)->ob_fval;
- DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dsum, sum);
+ double temp = *(double *)(&(left)) + *(double *)(&(right));
+ sum = *(PyObject **)(&temp);
+ }
+
+ inst(UNBOX_FLOAT, (boxed_float, unused[oparg] -- unboxed_float : PyRawFloat_Type, unused[oparg])) {
+ double temp = ((PyFloatObject *)boxed_float)->ob_fval;
+ unboxed_float = (*(PyObject **)(&temp));
+ }
+
+ inst(BOX_FLOAT, (raw_float, unused[oparg] -- boxed_float : PyFloat_Type, unused[oparg])) {
+ boxed_float = PyFloat_FromDouble(*(double *)(&(raw_float)));
}
macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum: PyLong_Type)) {
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 429d2b5c54fe1e..9fbec87988e4ae 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -251,6 +251,8 @@ GETITEM(PyObject *v, Py_ssize_t i) {
#define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); \
GETLOCAL(i) = value; \
Py_XDECREF(tmp); } while (0)
+#define SETLOCAL_NO_DECREF(i, value) do { PyObject *tmp = GETLOCAL(i); \
+ GETLOCAL(i) = value;} while (0)
#define GO_TO_INSTRUCTION(op) goto PREDICT_ID(op)
diff --git a/Python/frame.c b/Python/frame.c
index c2c0be30113912..1adc7a328792a5 100644
--- a/Python/frame.c
+++ b/Python/frame.c
@@ -136,7 +136,11 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
Py_DECREF(f);
}
assert(frame->stacktop >= 0);
+ char *unboxed_bitmask = _PyFrame_GetUnboxedBitMask(frame);
for (int i = 0; i < frame->stacktop; i++) {
+ if (unboxed_bitmask[i]) {
+ continue;
+ }
Py_XDECREF(frame->localsplus[i]);
}
Py_XDECREF(frame->frame_obj);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index e4565444a4500b..509c18a45ec8e3 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -3,14 +3,6 @@
// Python/bytecodes.c
// Do not edit!
- #define UOP_BINARY_OP_ADD_FLOAT_REST() \
- do { \
- STAT_INC(BINARY_OP, hit);\
- double dsum = ((PyFloatObject *)left)->ob_fval +\
- ((PyFloatObject *)right)->ob_fval;\
- DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dsum, sum);\
- } while (0)
-
#define UOP_BINARY_OP_ADD_INT_REST() \
do { \
STAT_INC(BINARY_OP, hit);\
@@ -72,6 +64,14 @@
DISPATCH();
}
+ TARGET(LOAD_FAST_NO_INCREF) {
+ PyObject *value;
+ value = GETLOCAL(oparg);
+ STACK_GROW(1);
+ stack_pointer[-1] = value;
+ DISPATCH();
+ }
+
TARGET(LOAD_CONST) {
PREDICTED(LOAD_CONST);
PyObject *value;
@@ -89,6 +89,29 @@
DISPATCH();
}
+ TARGET(STORE_FAST_BOXED_UNBOXED) {
+ PyObject *value = stack_pointer[-1];
+ SETLOCAL_NO_DECREF(oparg, value);
+ _PyFrame_GetUnboxedBitMask(frame)[oparg] = false;
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(STORE_FAST_UNBOXED_BOXED) {
+ PyObject *value = stack_pointer[-1];
+ SETLOCAL(oparg, value);
+ _PyFrame_GetUnboxedBitMask(frame)[oparg] = true;
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
+ TARGET(STORE_FAST_UNBOXED_UNBOXED) {
+ PyObject *value = stack_pointer[-1];
+ SETLOCAL_NO_DECREF(oparg, value);
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
TARGET(LOAD_FAST__LOAD_FAST) {
PyObject *_tmp_1;
PyObject *_tmp_2;
@@ -388,7 +411,10 @@
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
- UOP_BINARY_OP_ADD_FLOAT_REST();
+ STAT_INC(BINARY_OP, hit);
+ double dsum = ((PyFloatObject *)left)->ob_fval +
+ ((PyFloatObject *)right)->ob_fval;
+ DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dsum, sum);
STACK_SHRINK(1);
stack_pointer[-1] = sum;
next_instr += 1;
@@ -399,7 +425,7 @@
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
PyObject *left_unboxed;
- PyObject *left_unboxed;
+ PyObject *right_unboxed;
assert(cframe.use_tracing == 0);
bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
left_unboxed = (bb_test
@@ -408,24 +434,47 @@
right_unboxed = (bb_test
? *((PyObject **)(&(((PyFloatObject *)right)->ob_fval)))
: right);
- stack_pointer[-1] = left_unboxed;
+ stack_pointer[-1] = right_unboxed;
stack_pointer[-2] = left_unboxed;
DISPATCH();
}
- TARGET(BINARY_OP_ADD_FLOAT_REST) {
+ TARGET(UNARY_CHECK_FLOAT) {
+ PyObject *arg = stack_pointer[-(1 + oparg)];
+ assert(cframe.use_tracing == 0);
+ bb_test = PyFloat_CheckExact(arg);
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_ADD_FLOAT_UNBOXED) {
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
PyObject *sum;
STAT_INC(BINARY_OP, hit);
- double dsum = ((PyFloatObject *)left)->ob_fval +
- ((PyFloatObject *)right)->ob_fval;
- DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dsum, sum);
+ double temp = *(double *)(&(left)) + *(double *)(&(right));
+ sum = *(PyObject **)(&temp);
STACK_SHRINK(1);
stack_pointer[-1] = sum;
DISPATCH();
}
+ TARGET(UNBOX_FLOAT) {
+ PyObject *boxed_float = stack_pointer[-(1 + oparg)];
+ PyObject *unboxed_float;
+ double temp = ((PyFloatObject *)boxed_float)->ob_fval;
+ unboxed_float = (*(PyObject **)(&temp));
+ stack_pointer[-(1 + oparg)] = unboxed_float;
+ DISPATCH();
+ }
+
+ TARGET(BOX_FLOAT) {
+ PyObject *raw_float = stack_pointer[-(1 + oparg)];
+ PyObject *boxed_float;
+ boxed_float = PyFloat_FromDouble(*(double *)(&(raw_float)));
+ stack_pointer[-(1 + oparg)] = boxed_float;
+ DISPATCH();
+ }
+
TARGET(BINARY_OP_ADD_INT) {
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 4a7209e7cf6c0d..9a14cd4e009cb6 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -21,10 +21,18 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 0;
case LOAD_FAST:
return 0;
+ case LOAD_FAST_NO_INCREF:
+ return 0;
case LOAD_CONST:
return 0;
case STORE_FAST:
return 1;
+ case STORE_FAST_BOXED_UNBOXED:
+ return 1;
+ case STORE_FAST_UNBOXED_BOXED:
+ return 1;
+ case STORE_FAST_UNBOXED_UNBOXED:
+ return 1;
case LOAD_FAST__LOAD_FAST:
return 0+0;
case LOAD_FAST__LOAD_CONST:
@@ -63,8 +71,14 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 2;
case BINARY_CHECK_FLOAT:
return 2;
- case BINARY_OP_ADD_FLOAT_REST:
+ case UNARY_CHECK_FLOAT:
+ return oparg + 1;
+ case BINARY_OP_ADD_FLOAT_UNBOXED:
return 2;
+ case UNBOX_FLOAT:
+ return oparg + 1;
+ case BOX_FLOAT:
+ return oparg + 1;
case BINARY_OP_ADD_INT:
return 2;
case BINARY_CHECK_INT:
@@ -411,10 +425,18 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 1;
case LOAD_FAST:
return 1;
+ case LOAD_FAST_NO_INCREF:
+ return 1;
case LOAD_CONST:
return 1;
case STORE_FAST:
return 0;
+ case STORE_FAST_BOXED_UNBOXED:
+ return 0;
+ case STORE_FAST_UNBOXED_BOXED:
+ return 0;
+ case STORE_FAST_UNBOXED_UNBOXED:
+ return 0;
case LOAD_FAST__LOAD_FAST:
return 1+1;
case LOAD_FAST__LOAD_CONST:
@@ -453,8 +475,14 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 1;
case BINARY_CHECK_FLOAT:
return 2;
- case BINARY_OP_ADD_FLOAT_REST:
+ case UNARY_CHECK_FLOAT:
+ return oparg + 1;
+ case BINARY_OP_ADD_FLOAT_UNBOXED:
return 1;
+ case UNBOX_FLOAT:
+ return oparg + 1;
+ case BOX_FLOAT:
+ return oparg + 1;
case BINARY_OP_ADD_INT:
return 1;
case BINARY_CHECK_INT:
@@ -803,8 +831,12 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_FAST_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [LOAD_FAST_NO_INCREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [STORE_FAST_BOXED_UNBOXED] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [STORE_FAST_UNBOXED_BOXED] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [STORE_FAST_UNBOXED_UNBOXED] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
[LOAD_FAST__LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
[STORE_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
@@ -824,7 +856,10 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[BINARY_CHECK_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BINARY_OP_ADD_FLOAT_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [UNARY_CHECK_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BINARY_OP_ADD_FLOAT_UNBOXED] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [UNBOX_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [BOX_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[BINARY_OP_ADD_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[BINARY_CHECK_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[BINARY_OP_ADD_INT_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
diff --git a/Python/tier2.c b/Python/tier2.c
index 0b33b89e4e4490..06d94e0c9c73b2 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -16,6 +16,13 @@
#define BB_EPILOG 0
+/* Dummy types used by the types propagator */
+PyTypeObject PyRawFloat_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "rawfloat",
+ sizeof(PyFloatObject),
+};
+
static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
@@ -159,11 +166,6 @@ type_propagate(
type_context->type_stack_ptr = type_stackptr;
#undef TARGET
-#undef TYPESTACK_PEEK
-#undef TYPESTACK_POKE
-#undef TYPELOCALS_SET
-#undef TYPELOCALS_GET
-#undef TYPECONST_GET
#undef STACK_ADJUST
#undef STACK_GROW
#undef STACK_SHRINK
@@ -400,9 +402,23 @@ static inline int
IS_FORBIDDEN_OPCODE(int opcode)
{
switch (opcode) {
+ // Modifying containers
+ case LIST_EXTEND:
+ case SET_UPDATE:
+ case DICT_UPDATE:
+ // f-strings
+ case FORMAT_VALUE:
+ // Type hinting
+ case SETUP_ANNOTATIONS:
+ // Context manager
+ case BEFORE_WITH:
// Generators and coroutines
case SEND:
case YIELD_VALUE:
+ case GET_AITER:
+ case GET_ANEXT:
+ case BEFORE_ASYNC_WITH:
+ case END_ASYNC_FOR:
// Raise keyword
case RAISE_VARARGS:
// Exceptions, we could support these theoretically.
@@ -410,8 +426,11 @@ IS_FORBIDDEN_OPCODE(int opcode)
case PUSH_EXC_INFO:
case RERAISE:
case POP_EXCEPT:
+ case CHECK_EXC_MATCH:
+ case CLEANUP_THROW:
// Closures
case LOAD_DEREF:
+ case LOAD_CLASSDEREF:
case MAKE_CELL:
// DELETE_FAST
case DELETE_FAST:
@@ -429,6 +448,24 @@ IS_FORBIDDEN_OPCODE(int opcode)
}
}
+// Decides what values we need to rebox.
+// num_elements is how many stack entries and thus how far from the TOS we want to rebox.
+static inline _Py_CODEUNIT *
+rebox_stack(_Py_CODEUNIT *write_curr,
+ _PyTier2TypeContext *type_context, int num_elements)
+{
+ for (int i = 0; i < num_elements; i++) {
+ PyTypeObject **curr = type_context->type_stack_ptr - 1 - i;
+ if (*curr == &PyRawFloat_Type) {
+ write_curr->op.code = BOX_FLOAT;
+ write_curr->op.arg = i;
+ write_curr++;
+ type_propagate(BOX_FLOAT, i, type_context, NULL);
+ }
+ }
+ return write_curr;
+}
+
static inline _Py_CODEUNIT *
emit_cache_entries(_Py_CODEUNIT *write_curr, int cache_entries)
{
@@ -617,10 +654,15 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
}
static inline _Py_CODEUNIT *
-emit_scope_exit(_Py_CODEUNIT *write_curr, _Py_CODEUNIT exit)
+emit_scope_exit(_Py_CODEUNIT *write_curr, _Py_CODEUNIT exit,
+ _PyTier2TypeContext *type_context)
{
switch (_Py_OPCODE(exit)) {
case RETURN_VALUE:
+ write_curr = rebox_stack(write_curr, type_context, 1);
+ *write_curr = exit;
+ write_curr++;
+ return write_curr;
case RETURN_CONST:
case INTERPRETER_EXIT:
#if BB_DEBUG
@@ -743,7 +785,16 @@ infer_BINARY_OP_ADD(
return write_curr;
}
}
+ if (left == &PyRawFloat_Type) {
+ if (right == &PyRawFloat_Type) {
+ write_curr->op.code = BINARY_OP_ADD_FLOAT_UNBOXED;
+ write_curr++;
+ type_propagate(BINARY_OP_ADD_FLOAT_UNBOXED, 0, type_context, NULL);
+ return write_curr;
+ }
+ }
// Unknown, time to emit the chain of guards.
+ write_curr = rebox_stack(write_curr, type_context, 2);
*needs_guard = true;
if (prev_type_guard == NULL) {
return emit_type_guard(write_curr, BINARY_CHECK_INT, bb_id);
@@ -761,6 +812,12 @@ infer_BINARY_OP_ADD(
return NULL;
}
+static inline bool
+is_unboxed_type(PyTypeObject *t)
+{
+ return t == &PyRawFloat_Type;
+}
+
// Detects a BB from the current instruction start to the end of the first basic block it sees.
// Then emits the instructions into the bb space.
//
@@ -836,8 +893,7 @@ _PyTier2_Code_DetectAndEmitBB(
opcode = RESUME_QUICK;
DISPATCH();
case COMPARE_AND_BRANCH:
- opcode = COMPARE_OP;
- specop = COMPARE_OP;
+ opcode = specop = COMPARE_OP;
DISPATCH();
case END_FOR:
// Assert that we are the start of a BB
@@ -846,6 +902,73 @@ _PyTier2_Code_DetectAndEmitBB(
// So we tell the BB to skip over it.
t2_start++;
DISPATCH();
+ case LOAD_CONST:
+ if (TYPECONST_GET(oparg) == &PyFloat_Type) {
+ write_i->op.code = LOAD_CONST;
+ write_i->op.arg = oparg;
+ write_i++;
+ type_propagate(LOAD_CONST, oparg, starting_type_context, consts);
+ write_i->op.code = UNBOX_FLOAT;
+ write_i->op.arg = 0;
+ write_i++;
+ type_propagate(UNBOX_FLOAT, 0, starting_type_context, consts);
+ continue;
+ }
+ DISPATCH();
+ case LOAD_FAST: {
+ // Read-only, only for us to inspect the types. DO NOT MODIFY HERE.
+ // ONLY THE TYPES PROPAGATOR SHOULD MODIFY THEIR INTERNAL VALUES.
+ PyTypeObject **type_stack = starting_type_context->type_stack;
+ PyTypeObject **type_locals = starting_type_context->type_locals;
+ PyTypeObject **type_stackptr = starting_type_context->type_stack_ptr;
+ // Writing unboxed val to a boxed val.
+ if (is_unboxed_type(TYPELOCALS_GET(oparg))) {
+ opcode = specop = LOAD_FAST_NO_INCREF;
+ }
+ else {
+ opcode = specop = LOAD_FAST;
+ }
+ DISPATCH();
+ }
+ case STORE_FAST: {
+ // Read-only, only for us to inspect the types. DO NOT MODIFY HERE.
+ // ONLY THE TYPES PROPAGATOR SHOULD MODIFY THEIR INTERNAL VALUES.
+ PyTypeObject **type_stack = starting_type_context->type_stack;
+ PyTypeObject **type_locals = starting_type_context->type_locals;
+ PyTypeObject **type_stackptr = starting_type_context->type_stack_ptr;
+ // Writing unboxed val to a boxed val.
+ if (is_unboxed_type(TYPESTACK_PEEK(1))) {
+ if (!is_unboxed_type(TYPELOCALS_GET(oparg))) {
+ opcode = specop = STORE_FAST_UNBOXED_BOXED;
+ }
+ else {
+ opcode = specop = STORE_FAST_UNBOXED_UNBOXED;
+ }
+ }
+ else {
+ if (is_unboxed_type(TYPELOCALS_GET(oparg))) {
+ opcode = specop = STORE_FAST_BOXED_UNBOXED;
+ }
+ else {
+ opcode = specop = STORE_FAST;
+ }
+ }
+ DISPATCH();
+ }
+ // Need to handle reboxing at these boundaries.
+ case CALL:
+ write_i = rebox_stack(write_i, starting_type_context,
+ oparg + 2);
+ DISPATCH();
+ case BUILD_MAP:
+ write_i = rebox_stack(write_i, starting_type_context,
+ oparg * 2);
+ DISPATCH();
+ case BUILD_STRING:
+ case BUILD_LIST:
+ write_i = rebox_stack(write_i, starting_type_context,
+ oparg);
+ DISPATCH();
case BINARY_OP:
if (oparg == NB_ADD) {
// Add operation. Need to check if we can infer types.
@@ -868,7 +991,24 @@ _PyTier2_Code_DetectAndEmitBB(
i += caches;
continue;
}
+ write_i = rebox_stack(write_i, starting_type_context, 2);
DISPATCH()
+ case LOAD_ATTR:
+ case CALL_INTRINSIC_1:
+ case UNARY_NEGATIVE:
+ case UNARY_NOT:
+ case UNARY_INVERT:
+ case GET_LEN:
+ write_i = rebox_stack(write_i, starting_type_context, 1);
+ DISPATCH();
+ case CALL_INTRINSIC_2:
+ case BINARY_SUBSCR:
+ case BINARY_SLICE:
+ write_i = rebox_stack(write_i, starting_type_context, 2);
+ DISPATCH();
+ case STORE_SLICE:
+ write_i = rebox_stack(write_i, starting_type_context, 4);
+ DISPATCH();
default:
#if BB_DEBUG || TYPEPROP_DEBUG
fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
@@ -903,10 +1043,12 @@ _PyTier2_Code_DetectAndEmitBB(
return NULL;
}
bb_space->water_level += (write_i - t2_start) * sizeof(_Py_CODEUNIT);
- // Reset the start
+ // Reset all our values
t2_start = write_i;
i++;
virtual_start = true;
+ starting_type_context = type_context_copy;
+
// Don't change opcode or oparg, let us handle it again.
DISPATCH_GOTO();
}
@@ -914,7 +1056,7 @@ _PyTier2_Code_DetectAndEmitBB(
// These are definitely the end of a basic block.
if (IS_SCOPE_EXIT_OPCODE(opcode)) {
// Emit the scope exit instruction.
- write_i = emit_scope_exit(write_i, *curr);
+ write_i = emit_scope_exit(write_i, *curr, starting_type_context);
END();
}
@@ -1513,3 +1655,9 @@ _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *tar
_py_set_opcode(write_curr, END_FOR);
write_curr++;
}
+
+#undef TYPESTACK_PEEK
+#undef TYPESTACK_POKE
+#undef TYPELOCALS_SET
+#undef TYPELOCALS_GET
+#undef TYPECONST_GET
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 6dcabba76bfd15..94690bf372c7c4 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -33,6 +33,12 @@
break;
}
+ TARGET(LOAD_FAST_NO_INCREF) {
+ STACK_GROW(1);
+ TYPESTACK_POKE(1, TYPELOCALS_GET(oparg));
+ break;
+ }
+
TARGET(LOAD_CONST) {
STACK_GROW(1);
TYPESTACK_POKE(1, TYPECONST_GET(oparg));
@@ -46,6 +52,27 @@
break;
}
+ TARGET(STORE_FAST_BOXED_UNBOXED) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ TYPELOCALS_SET(oparg, value)
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(STORE_FAST_UNBOXED_BOXED) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ TYPELOCALS_SET(oparg, value)
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(STORE_FAST_UNBOXED_UNBOXED) {
+ PyTypeObject *value = TYPESTACK_PEEK(1);
+ TYPELOCALS_SET(oparg, value)
+ STACK_SHRINK(1);
+ break;
+ }
+
TARGET(POP_TOP) {
STACK_SHRINK(1);
break;
@@ -129,9 +156,24 @@
break;
}
- TARGET(BINARY_OP_ADD_FLOAT_REST) {
+ TARGET(UNARY_CHECK_FLOAT) {
+ TYPESTACK_POKE(1 + oparg, &PyFloat_Type);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_FLOAT_UNBOXED) {
STACK_SHRINK(1);
- TYPESTACK_POKE(1, &PyFloat_Type);
+ TYPESTACK_POKE(1, &PyRawFloat_Type);
+ break;
+ }
+
+ TARGET(UNBOX_FLOAT) {
+ TYPESTACK_POKE(1 + oparg, &PyRawFloat_Type);
+ break;
+ }
+
+ TARGET(BOX_FLOAT) {
+ TYPESTACK_POKE(1 + oparg, &PyFloat_Type);
break;
}
diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py
index 6718973702cdf0..26a1d3b99e1f7f 100644
--- a/Tools/build/deepfreeze.py
+++ b/Tools/build/deepfreeze.py
@@ -260,7 +260,8 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
self.field(code, "co_posonlyargcount")
self.field(code, "co_kwonlyargcount")
# The following should remain in sync with _PyFrame_NumSlotsForCodeObject
- self.write(f".co_framesize = {code.co_stacksize + len(localsplusnames)} + FRAME_SPECIALS_SIZE,")
+ self.write(f".co_framesize = {code.co_stacksize + len(localsplusnames)} + FRAME_SPECIALS_SIZE"
+ f" + ({len(localsplusnames)} * sizeof(char) / sizeof(PyObject *) + 1),")
self.field(code, "co_stacksize")
self.field(code, "co_firstlineno")
self.write(f".co_nlocalsplus = {len(localsplusnames)},")
From 34cca1e0073dcc2a4644f51546b044c05ff94215 Mon Sep 17 00:00:00 2001
From: Julia
Date: Mon, 20 Mar 2023 22:10:28 +0800
Subject: [PATCH 078/280] Feat: Allow multiple type operations per annotation
---
Python/bytecodes.c | 6 ++-
Python/tier2_typepropagator.c.h | 2 +
Tools/cases_generator/generate_cases.py | 69 +++++++++++++------------
Tools/cases_generator/parser.py | 31 ++++++++---
4 files changed, 68 insertions(+), 40 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 72ba844f017f41..face540aee1b1d 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -279,7 +279,11 @@ dummy_func(
U_INST(BINARY_OP_ADD_FLOAT_REST);
}
- inst(BINARY_CHECK_FLOAT, (left, right -- left : <<= PyFloat_Type, right : <<= PyFloat_Type)) {
+ inst(BINARY_CHECK_FLOAT, (
+ left, right
+ -- left : {<<= PyFloat_Type, PyRawFloat_Type},
+ right: {<<= PyFloat_Type, PyRawFloat_Type})
+ ) {
assert(cframe.use_tracing == 0);
bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
}
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 72c7eb9cc4d60f..cf123dd6b2d59d 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -125,7 +125,9 @@
TARGET(BINARY_CHECK_FLOAT) {
TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1), true);
TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(2), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(2), true);
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index e67608a83ffea3..16128dc85a7402 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -343,13 +343,15 @@ def write_typeprop(self, out: Formatter) -> None:
# Stack input is used in output effect
for oeffect in self.output_effects:
if not (typ := oeffect.type_annotation): continue
- if not isinstance(src := typ.src, TypeSrcStackInput): continue
- if oeffect.name in self.unmoved_names and oeffect.name == src.name:
- print(
- f"Warn: {self.name} type annotation for {oeffect.name} will be ignored "
- "as it is unmoved")
- continue
- need_to_declare.append(src.name)
+ ops = typ.ops
+ for op in ops:
+ if not isinstance(src := op.src, TypeSrcStackInput): continue
+ if oeffect.name in self.unmoved_names and oeffect.name == src.name:
+ print(
+ f"Warn: {self.name} type annotation for {oeffect.name} will be ignored "
+ "as it is unmoved")
+ continue
+ need_to_declare.append(src.name)
# Write input stack effect variable declarations and initializations
ieffects = list(reversed(self.input_effects))
@@ -424,33 +426,34 @@ def write_typeprop(self, out: Formatter) -> None:
# Check if there's type info
if typ := oeffect.type_annotation:
- match typ.src:
- case TypeSrcLiteral(literal=valstr):
- if valstr == "NULL":
- src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
- flag = "true"
- else:
- src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
+ for op in typ.ops:
+ match op.src:
+ case TypeSrcLiteral(literal=valstr):
+ if valstr == "NULL":
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
+ flag = "true"
+ else:
+ src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
+ flag = "true"
+ case TypeSrcStackInput(name=valstr):
+ assert valstr in need_to_declare
+ assert oeffect.name not in self.unmoved_names
+ src = valstr
+ flag = "false"
+ case TypeSrcConst(index=idx):
+ src = f"(_Py_TYPENODE_t *)TYPECONST_GET({idx})"
flag = "true"
- case TypeSrcStackInput(name=valstr):
- assert valstr in need_to_declare
- assert oeffect.name not in self.unmoved_names
- src = valstr
- flag = "false"
- case TypeSrcConst(index=idx):
- src = f"(_Py_TYPENODE_t *)TYPECONST_GET({idx})"
- flag = "true"
- case TypeSrcLocals(index=idx):
- src = f"TYPELOCALS_GET({idx})"
- flag = "false"
- case _:
- typing.assert_never(typ.src)
-
- opstr = f"{typ.op}({src}, {dst}, {flag})"
- if oeffect.cond:
- out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
- else:
- out.emit(f"{opstr};")
+ case TypeSrcLocals(index=idx):
+ src = f"TYPELOCALS_GET({idx})"
+ flag = "false"
+ case _:
+ typing.assert_never(op.src)
+
+ opstr = f"{op.op}({src}, {dst}, {flag})"
+ if oeffect.cond:
+ out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
+ else:
+ out.emit(f"{opstr};")
continue
# Don't touch unmoved stack vars
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 48ad3cefa0fef4..658b1b0d46c513 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -95,12 +95,14 @@ class TypeSrcStackInput(Node):
| TypeSrcStackInput
)
-
@dataclass
-class TypeAnnotation(Node):
+class TypeOperation(Node):
op: Literal["TYPE_SET", "TYPE_OVERWRITE"]
src: TypeSrc
+@dataclass
+class TypeAnnotation(Node):
+ ops: tuple[TypeOperation]
@dataclass
class StackEffect(Node):
@@ -331,7 +333,7 @@ def stack_effect(self) -> StackEffect | None:
type_annotation = None
if self.expect(lx.COLON):
has_type_annotation = True
- type_annotation = self.stackvar_type()
+ type_annotation = self.stackvar_typeannotation()
cond_text = ""
if self.expect(lx.IF):
self.require(lx.LPAREN)
@@ -368,14 +370,31 @@ def stackvar_typesrc(self) -> TypeSrc | None:
return TypeSrcStackInput(id.text.strip())
@contextual
- def stackvar_type(self) -> TypeAnnotation | None:
+ def stackvar_typeoperation(self) -> TypeOperation | None:
if self.expect(lx.LSHIFTEQUAL):
src = self.stackvar_typesrc()
if src is None: return None
- return TypeAnnotation("TYPE_SET", src)
+ return TypeOperation("TYPE_SET", src)
src = self.stackvar_typesrc()
if src is None: return None
- return TypeAnnotation("TYPE_OVERWRITE", src)
+ return TypeOperation("TYPE_OVERWRITE", src)
+
+ @contextual
+ def stackvar_typeannotation(self) -> TypeAnnotation | None:
+ ops = []
+ if self.expect(lx.LBRACE):
+ while True:
+ typ = self.stackvar_typeoperation()
+ ops.append(typ)
+ if typ is None: return None
+ if self.expect(lx.RBRACE):
+ break
+ self.require(lx.COMMA)
+ else:
+ typ = self.stackvar_typeoperation()
+ if typ is None: return None
+ ops.append(typ)
+ return TypeAnnotation(tuple(ops))
@contextual
def local_effect(self) -> LocalEffect | None:
From 79a204c453eb1354b38550fd9e27beb9ce31e6ca Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 20 Mar 2023 22:59:41 +0800
Subject: [PATCH 079/280] unbox when loading from fastlocals
---
Python/tier2.c | 22 +-
Python/tier2_typepropagator.c.h | 1107 +++++++++++++++++++++++++++++++
2 files changed, 1126 insertions(+), 3 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index f296c884121ac9..387d702b45ddee 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -529,6 +529,7 @@ _PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_reque
#if BB_DEBUG
fprintf(stderr, "Allocating new BB of size %lld\n", new_size);
#endif
+ // @TODO We can't Realloc, we actually need to do the linked list method.
_PyTier2BBSpace *new_space = PyMem_Realloc(curr, new_size);
if (new_space == NULL) {
return NULL;
@@ -809,7 +810,7 @@ static int type_guard_to_index[256] = {
static inline _Py_CODEUNIT *
emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int bb_id)
{
-#if BB_DEBUG
+#if BB_DEBUG && defined(Py_DEBUG)
fprintf(stderr, "emitted type guard %p %s\n", write_curr,
_PyOpcode_OpName[guard_opcode]);
#endif
@@ -1200,7 +1201,10 @@ _PyTier2_Code_DetectAndEmitBB(
// So we tell the BB to skip over it.
t2_start++;
DISPATCH();
- case LOAD_CONST:
+ case LOAD_CONST: {
+ _Py_TYPENODE_t *type_stack = starting_type_context->type_stack;
+ _Py_TYPENODE_t *type_locals = starting_type_context->type_locals;
+ _Py_TYPENODE_t **type_stackptr = &starting_type_context->type_stack_ptr;
if (TYPECONST_GET(oparg) == &PyFloat_Type) {
write_i->op.code = LOAD_CONST;
write_i->op.arg = oparg;
@@ -1213,6 +1217,7 @@ _PyTier2_Code_DetectAndEmitBB(
continue;
}
DISPATCH();
+ }
case LOAD_FAST: {
// Read-only, only for us to inspect the types. DO NOT MODIFY HERE.
// ONLY THE TYPES PROPAGATOR SHOULD MODIFY THEIR INTERNAL VALUES.
@@ -1224,6 +1229,17 @@ _PyTier2_Code_DetectAndEmitBB(
opcode = specop = LOAD_FAST_NO_INCREF;
}
else {
+ if (TYPELOCALS_GET(oparg) == &PyFloat_Type) {
+ write_i->op.code = LOAD_FAST;
+ write_i->op.arg = oparg;
+ write_i++;
+ type_propagate(LOAD_FAST, oparg, starting_type_context, consts);
+ write_i->op.code = UNBOX_FLOAT;
+ write_i->op.arg = 0;
+ write_i++;
+ type_propagate(UNBOX_FLOAT, oparg, starting_type_context, consts);
+ continue;
+ }
opcode = specop = LOAD_FAST;
}
DISPATCH();
@@ -1773,7 +1789,7 @@ _PyTier2_GenerateNextBB(
// one of those conditional pops.
assert(!gen_bb_requires_pop);
// Propagate the type guard information.
-#if TYPEPROP_DEBUG
+#if TYPEPROP_DEBUG && defined(Py_DEBUG)
fprintf(stderr,
" [-] Previous predicate BB ended with a type guard: %s\n",
_PyOpcode_OpName[prev_type_guard->op.code]);
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index e69de29bb2d1d6..11ecd8de10d44f 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -0,0 +1,1107 @@
+// This file is generated by Tools/cases_generator/generate_cases.py @TODO: make this a seperate argument
+// from:
+// Python/bytecodes.c
+// Do not edit!
+
+ TARGET(NOP) {
+ break;
+ }
+
+ TARGET(RESUME) {
+ break;
+ }
+
+ TARGET(RESUME_QUICK) {
+ break;
+ }
+
+ TARGET(LOAD_CLOSURE) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE(TYPELOCALS_GET(oparg), TYPESTACK_PEEK(1), false);
+ break;
+ }
+
+ TARGET(LOAD_FAST_CHECK) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE(TYPELOCALS_GET(oparg), TYPESTACK_PEEK(1), false);
+ break;
+ }
+
+ TARGET(LOAD_FAST) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE(TYPELOCALS_GET(oparg), TYPESTACK_PEEK(1), false);
+ break;
+ }
+
+ TARGET(LOAD_FAST_NO_INCREF) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE(TYPELOCALS_GET(oparg), TYPESTACK_PEEK(1), false);
+ break;
+ }
+
+ TARGET(LOAD_CONST) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)TYPECONST_GET(oparg), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(STORE_FAST) {
+ _Py_TYPENODE_t *value = TYPESTACK_PEEK(1);
+ TYPE_OVERWRITE(value, TYPELOCALS_GET(oparg), false);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(STORE_FAST_BOXED_UNBOXED) {
+ _Py_TYPENODE_t *value = TYPESTACK_PEEK(1);
+ TYPE_OVERWRITE(value, TYPELOCALS_GET(oparg), false);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(STORE_FAST_UNBOXED_BOXED) {
+ _Py_TYPENODE_t *value = TYPESTACK_PEEK(1);
+ TYPE_OVERWRITE(value, TYPELOCALS_GET(oparg), false);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(STORE_FAST_UNBOXED_UNBOXED) {
+ _Py_TYPENODE_t *value = TYPESTACK_PEEK(1);
+ TYPE_OVERWRITE(value, TYPELOCALS_GET(oparg), false);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(POP_TOP) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(PUSH_NULL) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(END_FOR) {
+ {
+ STACK_SHRINK(1);
+ }
+ {
+ STACK_SHRINK(1);
+ }
+ break;
+ }
+
+ TARGET(UNARY_NEGATIVE) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(UNARY_NOT) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(UNARY_INVERT) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_MULTIPLY_INT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_MULTIPLY_FLOAT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_SUBTRACT_INT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_SUBTRACT_FLOAT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_UNICODE) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyUnicode_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_INPLACE_ADD_UNICODE) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_FLOAT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_CHECK_FLOAT) {
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1), true);
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(2), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(2), true);
+ break;
+ }
+
+ TARGET(UNARY_CHECK_FLOAT) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1 + oparg), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_FLOAT_UNBOXED) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(UNBOX_FLOAT) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1 + oparg), true);
+ break;
+ }
+
+ TARGET(BOX_FLOAT) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1 + oparg), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_INT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_CHECK_INT) {
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(2), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_ADD_INT_REST) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_SLICE) {
+ STACK_SHRINK(2);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(STORE_SLICE) {
+ STACK_SHRINK(4);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR_LIST_INT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR_TUPLE_INT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR_DICT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_SUBSCR_GETITEM) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(LIST_APPEND) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(SET_ADD) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(STORE_SUBSCR) {
+ STACK_SHRINK(3);
+ break;
+ }
+
+ TARGET(STORE_SUBSCR_LIST_INT) {
+ STACK_SHRINK(3);
+ break;
+ }
+
+ TARGET(STORE_SUBSCR_DICT) {
+ STACK_SHRINK(3);
+ break;
+ }
+
+ TARGET(DELETE_SUBSCR) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(CALL_INTRINSIC_1) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_INTRINSIC_2) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(RAISE_VARARGS) {
+ fprintf(stderr, "Type propagation across `RAISE_VARARGS` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(INTERPRETER_EXIT) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(RETURN_VALUE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(RETURN_CONST) {
+ break;
+ }
+
+ TARGET(GET_AITER) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(GET_ANEXT) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(GET_AWAITABLE) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(SEND) {
+ fprintf(stderr, "Type propagation across `SEND` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(SEND_GEN) {
+ fprintf(stderr, "Type propagation across `SEND_GEN` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(YIELD_VALUE) {
+ fprintf(stderr, "Type propagation across `YIELD_VALUE` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(POP_EXCEPT) {
+ fprintf(stderr, "Type propagation across `POP_EXCEPT` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(RERAISE) {
+ fprintf(stderr, "Type propagation across `RERAISE` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(END_ASYNC_FOR) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(CLEANUP_THROW) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(2), true);
+ break;
+ }
+
+ TARGET(LOAD_ASSERTION_ERROR) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(LOAD_BUILD_CLASS) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(STORE_NAME) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(DELETE_NAME) {
+ break;
+ }
+
+ TARGET(UNPACK_SEQUENCE) {
+ STACK_SHRINK(1);
+ STACK_GROW(oparg);
+ break;
+ }
+
+ TARGET(UNPACK_SEQUENCE_TWO_TUPLE) {
+ STACK_SHRINK(1);
+ STACK_GROW(oparg);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg), true);
+ break;
+ }
+
+ TARGET(UNPACK_SEQUENCE_TUPLE) {
+ STACK_SHRINK(1);
+ STACK_GROW(oparg);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg), true);
+ break;
+ }
+
+ TARGET(UNPACK_SEQUENCE_LIST) {
+ STACK_SHRINK(1);
+ STACK_GROW(oparg);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg), true);
+ break;
+ }
+
+ TARGET(UNPACK_EX) {
+ STACK_GROW((oparg & 0xFF) + (oparg >> 8));
+ break;
+ }
+
+ TARGET(STORE_ATTR) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(DELETE_ATTR) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(STORE_GLOBAL) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(DELETE_GLOBAL) {
+ break;
+ }
+
+ TARGET(LOAD_NAME) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(LOAD_GLOBAL) {
+ STACK_GROW(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_GLOBAL_MODULE) {
+ STACK_GROW(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_GLOBAL_BUILTIN) {
+ STACK_GROW(1);
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(DELETE_FAST) {
+ fprintf(stderr, "Type propagation across `DELETE_FAST` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(MAKE_CELL) {
+ fprintf(stderr, "Type propagation across `MAKE_CELL` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(DELETE_DEREF) {
+ break;
+ }
+
+ TARGET(LOAD_CLASSDEREF) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(LOAD_DEREF) {
+ fprintf(stderr, "Type propagation across `LOAD_DEREF` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(STORE_DEREF) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(COPY_FREE_VARS) {
+ break;
+ }
+
+ TARGET(BUILD_STRING) {
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyUnicode_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BUILD_TUPLE) {
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyTuple_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BUILD_LIST) {
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyList_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(LIST_EXTEND) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(SET_UPDATE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BUILD_SET) {
+ STACK_SHRINK(oparg);
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PySet_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BUILD_MAP) {
+ STACK_SHRINK(oparg*2);
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyDict_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(SETUP_ANNOTATIONS) {
+ break;
+ }
+
+ TARGET(BUILD_CONST_KEY_MAP) {
+ STACK_SHRINK(oparg);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyDict_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(DICT_UPDATE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(DICT_MERGE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(MAP_ADD) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(LOAD_ATTR) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_INSTANCE_VALUE) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_MODULE) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_WITH_HINT) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_SLOT) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_CLASS) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_PROPERTY) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ break;
+ }
+
+ TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ break;
+ }
+
+ TARGET(STORE_ATTR_INSTANCE_VALUE) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(STORE_ATTR_WITH_HINT) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(STORE_ATTR_SLOT) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(COMPARE_OP) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(COMPARE_AND_BRANCH) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(COMPARE_AND_BRANCH_FLOAT) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(COMPARE_AND_BRANCH_INT) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(COMPARE_AND_BRANCH_STR) {
+ STACK_SHRINK(2);
+ break;
+ }
+
+ TARGET(IS_OP) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyBool_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CONTAINS_OP) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyBool_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CHECK_EG_MATCH) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(2), true);
+ break;
+ }
+
+ TARGET(CHECK_EXC_MATCH) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyBool_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(IMPORT_NAME) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(IMPORT_FROM) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(JUMP_FORWARD) {
+ break;
+ }
+
+ TARGET(JUMP_BACKWARD) {
+ break;
+ }
+
+ TARGET(JUMP_BACKWARD_QUICK) {
+ break;
+ }
+
+ TARGET(POP_JUMP_IF_FALSE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BB_TEST_POP_IF_FALSE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(POP_JUMP_IF_TRUE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BB_TEST_POP_IF_TRUE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(POP_JUMP_IF_NOT_NONE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BB_TEST_POP_IF_NOT_NONE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(POP_JUMP_IF_NONE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(BB_TEST_POP_IF_NONE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(JUMP_IF_FALSE_OR_POP) {
+ fprintf(stderr, "Type propagation across `JUMP_IF_FALSE_OR_POP` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(BB_TEST_IF_FALSE_OR_POP) {
+ fprintf(stderr, "Type propagation across `BB_TEST_IF_FALSE_OR_POP` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(JUMP_IF_TRUE_OR_POP) {
+ fprintf(stderr, "Type propagation across `JUMP_IF_TRUE_OR_POP` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(BB_TEST_IF_TRUE_OR_POP) {
+ fprintf(stderr, "Type propagation across `BB_TEST_IF_TRUE_OR_POP` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(JUMP_BACKWARD_NO_INTERRUPT) {
+ break;
+ }
+
+ TARGET(GET_LEN) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(MATCH_CLASS) {
+ STACK_SHRINK(2);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(MATCH_MAPPING) {
+ fprintf(stderr, "Type propagation across `MATCH_MAPPING` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(MATCH_SEQUENCE) {
+ fprintf(stderr, "Type propagation across `MATCH_SEQUENCE` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(MATCH_KEYS) {
+ fprintf(stderr, "Type propagation across `MATCH_KEYS` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(GET_ITER) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(GET_YIELD_FROM_ITER) {
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(FOR_ITER) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BB_TEST_ITER) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(FOR_ITER_LIST) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(FOR_ITER_TUPLE) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(FOR_ITER_RANGE) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(FOR_ITER_GEN) {
+ STACK_GROW(1);
+ break;
+ }
+
+ TARGET(BEFORE_ASYNC_WITH) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(2), true);
+ break;
+ }
+
+ TARGET(BEFORE_WITH) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(2), true);
+ break;
+ }
+
+ TARGET(WITH_EXCEPT_START) {
+ fprintf(stderr, "Type propagation across `WITH_EXCEPT_START` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(PUSH_EXC_INFO) {
+ fprintf(stderr, "Type propagation across `PUSH_EXC_INFO` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_METHOD_NO_DICT) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(LOAD_ATTR_METHOD_LAZY_DICT) {
+ STACK_GROW(((oparg & 1) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ if (oparg & 1) { TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1 + ((oparg & 1) ? 1 : 0)), true); }
+ break;
+ }
+
+ TARGET(KW_NAMES) {
+ break;
+ }
+
+ TARGET(CALL) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_BOUND_METHOD_EXACT_ARGS) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(CALL_PY_EXACT_ARGS) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(CALL_PY_WITH_DEFAULTS) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_TYPE_1) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_STR_1) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_TUPLE_1) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_BUILTIN_CLASS) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_BUILTIN_O) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_BUILTIN_FAST) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_LEN) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_ISINSTANCE) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_LIST_APPEND) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) {
+ STACK_SHRINK(oparg);
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CALL_FUNCTION_EX) {
+ STACK_SHRINK(((oparg & 1) ? 1 : 0));
+ STACK_SHRINK(2);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(MAKE_FUNCTION) {
+ STACK_SHRINK(((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(RETURN_GENERATOR) {
+ break;
+ }
+
+ TARGET(BUILD_SLICE) {
+ STACK_SHRINK(((oparg == 3) ? 1 : 0));
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(FORMAT_VALUE) {
+ STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0));
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(COPY) {
+ _Py_TYPENODE_t *bottom = TYPESTACK_PEEK(1 + (oparg-1));
+ STACK_GROW(1);
+ TYPE_OVERWRITE(bottom, TYPESTACK_PEEK(1), false);
+ break;
+ }
+
+ TARGET(BINARY_OP) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(SWAP) {
+ _Py_TYPENODE_t *top = TYPESTACK_PEEK(1);
+ _Py_TYPENODE_t *bottom = TYPESTACK_PEEK(2 + (oparg-2));
+ TYPE_OVERWRITE(bottom, TYPESTACK_PEEK(1), false);
+ TYPE_OVERWRITE(top, TYPESTACK_PEEK(2 + (oparg-2)), false);
+ break;
+ }
+
+ TARGET(EXTENDED_ARG) {
+ fprintf(stderr, "Type propagation across `EXTENDED_ARG` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
+ break;
+ }
+
+ TARGET(CACHE) {
+ break;
+ }
+
+ TARGET(BB_BRANCH) {
+ break;
+ }
+
+ TARGET(BB_BRANCH_IF_FLAG_UNSET) {
+ break;
+ }
+
+ TARGET(BB_JUMP_IF_FLAG_UNSET) {
+ break;
+ }
+
+ TARGET(BB_BRANCH_IF_FLAG_SET) {
+ break;
+ }
+
+ TARGET(BB_JUMP_IF_FLAG_SET) {
+ break;
+ }
+
+ TARGET(BB_JUMP_BACKWARD_LAZY) {
+ break;
+ }
From 0eacc5edebac8c2ca05cde5c23ee503fea5711b5 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 20 Mar 2023 23:25:59 +0800
Subject: [PATCH 080/280] Fix up merge problems
---
Python/tier2.c | 39 ++++++++++++++++++---------------------
1 file changed, 18 insertions(+), 21 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 387d702b45ddee..b601261907d552 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -434,7 +434,7 @@ type_propagate(
// Get the type of the const and make into a TYPENODE ROOT
#define TYPECONST_GET(idx) _Py_TYPENODE_MAKE_ROOT( \
(_Py_TYPENODE_t)Py_TYPE( \
- _PyTuple_CAST(consts)->ob_item[(idx)]))
+ PyTuple_GET_ITEM(consts, idx)))
#define TYPE_SET(src, dst, flag) __type_propagate_TYPE_SET((src), (dst), (flag))
#define TYPE_OVERWRITE(src, dst, flag) __type_propagate_TYPE_OVERWRITE(type_context, (src), (dst), (flag))
@@ -467,6 +467,7 @@ type_propagate(
#undef STACK_ADJUST
#undef STACK_GROW
#undef STACK_SHRINK
+#undef TYPECONST_GET
}
////////// Utility functions
@@ -754,8 +755,8 @@ rebox_stack(_Py_CODEUNIT *write_curr,
_PyTier2TypeContext *type_context, int num_elements)
{
for (int i = 0; i < num_elements; i++) {
- PyTypeObject **curr = type_context->type_stack_ptr - 1 - i;
- if (*curr == &PyRawFloat_Type) {
+ _Py_TYPENODE_t *curr = type_context->type_stack_ptr - 1 - i;
+ if (typenode_get_type(*curr) == &PyRawFloat_Type) {
write_curr->op.code = BOX_FLOAT;
write_curr->op.arg = i;
write_curr++;
@@ -1151,7 +1152,7 @@ _PyTier2_Code_DetectAndEmitBB(
type_propagate(opcode, oparg, starting_type_context, consts); \
continue;
#define DISPATCH_GOTO() goto dispatch_opcode;
-
+#define TYPECONST_GET_RAWTYPE(idx) Py_TYPE(PyTuple_GET_ITEM(consts, idx))
assert(co->_tier2_info != NULL);
// There are only two cases that a BB ends.
@@ -1202,10 +1203,7 @@ _PyTier2_Code_DetectAndEmitBB(
t2_start++;
DISPATCH();
case LOAD_CONST: {
- _Py_TYPENODE_t *type_stack = starting_type_context->type_stack;
- _Py_TYPENODE_t *type_locals = starting_type_context->type_locals;
- _Py_TYPENODE_t **type_stackptr = &starting_type_context->type_stack_ptr;
- if (TYPECONST_GET(oparg) == &PyFloat_Type) {
+ if (TYPECONST_GET_RAWTYPE(oparg) == &PyFloat_Type) {
write_i->op.code = LOAD_CONST;
write_i->op.arg = oparg;
write_i++;
@@ -1221,15 +1219,14 @@ _PyTier2_Code_DetectAndEmitBB(
case LOAD_FAST: {
// Read-only, only for us to inspect the types. DO NOT MODIFY HERE.
// ONLY THE TYPES PROPAGATOR SHOULD MODIFY THEIR INTERNAL VALUES.
- PyTypeObject **type_stack = starting_type_context->type_stack;
- PyTypeObject **type_locals = starting_type_context->type_locals;
- PyTypeObject **type_stackptr = starting_type_context->type_stack_ptr;
- // Writing unboxed val to a boxed val.
- if (is_unboxed_type(TYPELOCALS_GET(oparg))) {
+ _Py_TYPENODE_t *type_locals = starting_type_context->type_locals;
+ // Writing unboxed val to a boxed val.
+ PyTypeObject *local = typenode_get_type(*TYPELOCALS_GET(oparg));
+ if (is_unboxed_type(local)) {
opcode = specop = LOAD_FAST_NO_INCREF;
}
else {
- if (TYPELOCALS_GET(oparg) == &PyFloat_Type) {
+ if (local == &PyFloat_Type) {
write_i->op.code = LOAD_FAST;
write_i->op.arg = oparg;
write_i++;
@@ -1247,12 +1244,13 @@ _PyTier2_Code_DetectAndEmitBB(
case STORE_FAST: {
// Read-only, only for us to inspect the types. DO NOT MODIFY HERE.
// ONLY THE TYPES PROPAGATOR SHOULD MODIFY THEIR INTERNAL VALUES.
- PyTypeObject **type_stack = starting_type_context->type_stack;
- PyTypeObject **type_locals = starting_type_context->type_locals;
- PyTypeObject **type_stackptr = starting_type_context->type_stack_ptr;
+ _Py_TYPENODE_t *type_locals = starting_type_context->type_locals;
+ _Py_TYPENODE_t **type_stackptr = &starting_type_context->type_stack_ptr;
+ PyTypeObject *local = typenode_get_type(*TYPESTACK_PEEK(1));
+ PyTypeObject *store = typenode_get_type(*TYPELOCALS_GET(oparg));
// Writing unboxed val to a boxed val.
- if (is_unboxed_type(TYPESTACK_PEEK(1))) {
- if (!is_unboxed_type(TYPELOCALS_GET(oparg))) {
+ if (is_unboxed_type(local)) {
+ if (!is_unboxed_type(store)) {
opcode = specop = STORE_FAST_UNBOXED_BOXED;
}
else {
@@ -1260,7 +1258,7 @@ _PyTier2_Code_DetectAndEmitBB(
}
}
else {
- if (is_unboxed_type(TYPELOCALS_GET(oparg))) {
+ if (is_unboxed_type(store)) {
opcode = specop = STORE_FAST_BOXED_UNBOXED;
}
else {
@@ -1974,6 +1972,5 @@ _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *tar
#undef TYPESTACK_POKE
#undef TYPELOCALS_SET
#undef TYPELOCALS_GET
-#undef TYPECONST_GET
#undef TYPE_SET
#undef TYPE_OVERWRITE
From 438873eccaef8097f7cd9d2b17c6a0822ba7bff1 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 21 Mar 2023 00:15:40 +0800
Subject: [PATCH 081/280] Fix more segfaults, write back on LOAD_FAST unboxed
---
Include/internal/pycore_opcode.h | 3 +--
Include/opcode.h | 13 +++++++------
Lib/opcode.py | 1 +
Python/bytecodes.c | 3 +++
Python/generated_cases.c.h | 6 ++++++
Python/opcode_metadata.h | 5 +++++
Python/tier2.c | 26 ++++++++++++++++++++++++--
Python/tier2_typepropagator.c.h | 5 +++++
8 files changed, 52 insertions(+), 10 deletions(-)
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index ab2ff150d4bede..5dd2ed17adee51 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -431,13 +431,13 @@ static const char *const _PyOpcode_OpName[263] = {
[UNARY_CHECK_FLOAT] = "UNARY_CHECK_FLOAT",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
[BINARY_OP_ADD_FLOAT_UNBOXED] = "BINARY_OP_ADD_FLOAT_UNBOXED",
+ [POP_TOP_NO_DECREF] = "POP_TOP_NO_DECREF",
[UNBOX_FLOAT] = "UNBOX_FLOAT",
[BOX_FLOAT] = "BOX_FLOAT",
[LOAD_FAST_NO_INCREF] = "LOAD_FAST_NO_INCREF",
[STORE_FAST_BOXED_UNBOXED] = "STORE_FAST_BOXED_UNBOXED",
[STORE_FAST_UNBOXED_BOXED] = "STORE_FAST_UNBOXED_BOXED",
[STORE_FAST_UNBOXED_UNBOXED] = "STORE_FAST_UNBOXED_UNBOXED",
- [200] = "<200>",
[201] = "<201>",
[202] = "<202>",
[203] = "<203>",
@@ -505,7 +505,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 200: \
case 201: \
case 202: \
case 203: \
diff --git a/Include/opcode.h b/Include/opcode.h
index a1570a55260b07..1a883aafd96f1b 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -283,12 +283,13 @@ extern "C" {
#define UNARY_CHECK_FLOAT 191
#define BINARY_OP_ADD_INT_REST 192
#define BINARY_OP_ADD_FLOAT_UNBOXED 193
-#define UNBOX_FLOAT 194
-#define BOX_FLOAT 195
-#define LOAD_FAST_NO_INCREF 196
-#define STORE_FAST_BOXED_UNBOXED 197
-#define STORE_FAST_UNBOXED_BOXED 198
-#define STORE_FAST_UNBOXED_UNBOXED 199
+#define POP_TOP_NO_DECREF 194
+#define UNBOX_FLOAT 195
+#define BOX_FLOAT 196
+#define LOAD_FAST_NO_INCREF 197
+#define STORE_FAST_BOXED_UNBOXED 198
+#define STORE_FAST_UNBOXED_BOXED 199
+#define STORE_FAST_UNBOXED_UNBOXED 200
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index aa1b33a5187c72..e46327cfe680b7 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -510,6 +510,7 @@ def pseudo_op(name, op, real_ops):
'BINARY_OP_ADD_FLOAT_UNBOXED',
# Boxing / unboxing ops
+ 'POP_TOP_NO_DECREF',
'UNBOX_FLOAT',
'BOX_FLOAT',
'LOAD_FAST_NO_INCREF',
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index b065e5c22a0877..aedb50a0c3d6bc 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -158,6 +158,9 @@ dummy_func(
DECREF_INPUTS();
}
+ inst(POP_TOP_NO_DECREF, (value--)) {
+ }
+
inst(PUSH_NULL, (-- res: NULL)) {
res = NULL;
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 509c18a45ec8e3..60d7460c8d4d81 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -223,6 +223,12 @@
DISPATCH();
}
+ TARGET(POP_TOP_NO_DECREF) {
+ PyObject *value = stack_pointer[-1];
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
TARGET(PUSH_NULL) {
PyObject *res;
res = NULL;
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 9a14cd4e009cb6..24a861100bca47 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -45,6 +45,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 0+0;
case POP_TOP:
return 1;
+ case POP_TOP_NO_DECREF:
+ return 1;
case PUSH_NULL:
return 0;
case END_FOR:
@@ -449,6 +451,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 1+1;
case POP_TOP:
return 0;
+ case POP_TOP_NO_DECREF:
+ return 0;
case PUSH_NULL:
return 1;
case END_FOR:
@@ -843,6 +847,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[STORE_FAST__STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
[LOAD_CONST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
[POP_TOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
+ [POP_TOP_NO_DECREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[PUSH_NULL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[END_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[UNARY_NEGATIVE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
diff --git a/Python/tier2.c b/Python/tier2.c
index b601261907d552..10628e5720404b 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1202,6 +1202,17 @@ _PyTier2_Code_DetectAndEmitBB(
// So we tell the BB to skip over it.
t2_start++;
DISPATCH();
+ case POP_TOP: {
+ // Read-only, only for us to inspect the types. DO NOT MODIFY HERE.
+ // ONLY THE TYPES PROPAGATOR SHOULD MODIFY THEIR INTERNAL VALUES.
+ _Py_TYPENODE_t **type_stackptr = &starting_type_context->type_stack_ptr;
+ PyTypeObject *pop = typenode_get_type(*TYPESTACK_PEEK(1));
+ // Writing unboxed val to a boxed val.
+ if (is_unboxed_type(pop)) {
+ opcode = specop = POP_TOP_NO_DECREF;
+ }
+ DISPATCH();
+ }
case LOAD_CONST: {
if (TYPECONST_GET_RAWTYPE(oparg) == &PyFloat_Type) {
write_i->op.code = LOAD_CONST;
@@ -1230,11 +1241,22 @@ _PyTier2_Code_DetectAndEmitBB(
write_i->op.code = LOAD_FAST;
write_i->op.arg = oparg;
write_i++;
- type_propagate(LOAD_FAST, oparg, starting_type_context, consts);
+ type_propagate(LOAD_FAST,
+ oparg, starting_type_context, consts);
write_i->op.code = UNBOX_FLOAT;
write_i->op.arg = 0;
write_i++;
- type_propagate(UNBOX_FLOAT, oparg, starting_type_context, consts);
+ type_propagate(UNBOX_FLOAT, 0, starting_type_context, consts);
+ write_i->op.code = STORE_FAST_UNBOXED_BOXED;
+ write_i->op.arg = oparg;
+ write_i++;
+ type_propagate(STORE_FAST_UNBOXED_BOXED,
+ oparg, starting_type_context, consts);
+ write_i->op.code = LOAD_FAST_NO_INCREF;
+ write_i->op.arg = oparg;
+ write_i++;
+ type_propagate(LOAD_FAST_NO_INCREF,
+ oparg, starting_type_context, consts);
continue;
}
opcode = specop = LOAD_FAST;
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 11ecd8de10d44f..98ef0f8006d5b9 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -78,6 +78,11 @@
break;
}
+ TARGET(POP_TOP_NO_DECREF) {
+ STACK_SHRINK(1);
+ break;
+ }
+
TARGET(PUSH_NULL) {
STACK_GROW(1);
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
From 07a8d611f7c8c0f1095da8722807a54195a05e68 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 21 Mar 2023 16:15:22 +0800
Subject: [PATCH 082/280] Fix up some type annotations in DSL
---
Python/bytecodes.c | 18 +++++++++---------
Python/tier2_typepropagator.c.h | 16 +++++++++-------
2 files changed, 18 insertions(+), 16 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index aedb50a0c3d6bc..5adac55e7e8399 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -205,7 +205,7 @@ dummy_func(
};
- inst(BINARY_OP_MULTIPLY_INT, (unused/1, left, right -- prod: PyLong_Type)) {
+ inst(BINARY_OP_MULTIPLY_INT, (unused/1, left, right -- prod)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
@@ -216,7 +216,7 @@ dummy_func(
ERROR_IF(prod == NULL, error);
}
- inst(BINARY_OP_MULTIPLY_FLOAT, (unused/1, left, right -- prod: PyFloat_Type)) {
+ inst(BINARY_OP_MULTIPLY_FLOAT, (unused/1, left, right -- prod)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
@@ -226,7 +226,7 @@ dummy_func(
DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dprod, prod);
}
- inst(BINARY_OP_SUBTRACT_INT, (unused/1, left, right -- sub: PyLong_Type)) {
+ inst(BINARY_OP_SUBTRACT_INT, (unused/1, left, right -- sub)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
@@ -237,7 +237,7 @@ dummy_func(
ERROR_IF(sub == NULL, error);
}
- inst(BINARY_OP_SUBTRACT_FLOAT, (unused/1, left, right -- sub: PyFloat_Type)) {
+ inst(BINARY_OP_SUBTRACT_FLOAT, (unused/1, left, right -- sub)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
@@ -246,7 +246,7 @@ dummy_func(
DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dsub, sub);
}
- inst(BINARY_OP_ADD_UNICODE, (unused/1, left, right -- res: PyUnicode_Type)) {
+ inst(BINARY_OP_ADD_UNICODE, (unused/1, left, right -- res)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
@@ -293,7 +293,7 @@ dummy_func(
JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP + 1);
}
- inst(BINARY_OP_ADD_FLOAT, (unused/1, left, right -- sum: PyFloat_Type)) {
+ inst(BINARY_OP_ADD_FLOAT, (unused/1, left, right -- sum)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
@@ -338,7 +338,7 @@ dummy_func(
boxed_float = PyFloat_FromDouble(*(double *)(&(raw_float)));
}
- macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum: PyLong_Type)) {
+ macro_inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
@@ -487,7 +487,7 @@ dummy_func(
DISPATCH_INLINED(new_frame);
}
- inst(LIST_APPEND, (list, unused[oparg-1], v -- list, unused[oparg-1])) {
+ inst(LIST_APPEND, (list, unused[oparg-1], v -- list : PyList_Type, unused[oparg-1])) {
ERROR_IF(_PyList_AppendTakeRef((PyListObject *)list, v) < 0, error);
PREDICT(JUMP_BACKWARD);
}
@@ -1357,7 +1357,7 @@ dummy_func(
DECREF_INPUTS();
}
- inst(SET_UPDATE, (set, unused[oparg-1], iterable -- set, unused[oparg-1])) {
+ inst(SET_UPDATE, (set, unused[oparg-1], iterable -- set: PySet_Type, unused[oparg-1])) {
int err = _PySet_Update(set, iterable);
DECREF_INPUTS();
ERROR_IF(err < 0, error);
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 98ef0f8006d5b9..f3620b6c6a9b4b 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -116,31 +116,31 @@
TARGET(BINARY_OP_MULTIPLY_INT) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_MULTIPLY_FLOAT) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_SUBTRACT_INT) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_SUBTRACT_FLOAT) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
TARGET(BINARY_OP_ADD_UNICODE) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyUnicode_Type), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -151,7 +151,7 @@
TARGET(BINARY_OP_ADD_FLOAT) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -186,7 +186,7 @@
TARGET(BINARY_OP_ADD_INT) {
STACK_SHRINK(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
@@ -244,6 +244,7 @@
TARGET(LIST_APPEND) {
STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyList_Type), TYPESTACK_PEEK(1 + (oparg-1)), true);
break;
}
@@ -528,6 +529,7 @@
TARGET(SET_UPDATE) {
STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PySet_Type), TYPESTACK_PEEK(1 + (oparg-1)), true);
break;
}
From 3143a1003202c9786def41638a5a2dee2fef8a20 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 22 Mar 2023 13:07:43 +0800
Subject: [PATCH 083/280] fix refleak in UNBOX_FLOAT
---
Python/bytecodes.c | 1 +
Python/generated_cases.c.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 5adac55e7e8399..68a69d62bcc9fe 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -331,6 +331,7 @@ dummy_func(
inst(UNBOX_FLOAT, (boxed_float, unused[oparg] -- unboxed_float : PyRawFloat_Type, unused[oparg])) {
double temp = ((PyFloatObject *)boxed_float)->ob_fval;
+ Py_DECREF(boxed_float);
unboxed_float = (*(PyObject **)(&temp));
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 60d7460c8d4d81..30549b63597876 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -468,6 +468,7 @@
PyObject *boxed_float = stack_pointer[-(1 + oparg)];
PyObject *unboxed_float;
double temp = ((PyFloatObject *)boxed_float)->ob_fval;
+ Py_DECREF(boxed_float);
unboxed_float = (*(PyObject **)(&temp));
stack_pointer[-(1 + oparg)] = unboxed_float;
DISPATCH();
From 1359271d0ed6ca4965fb795dd14ea3934481299e Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Fri, 24 Mar 2023 00:26:27 +0800
Subject: [PATCH 084/280] Fix: Buggy pointer magic in copying type context
(#27)
* Fix: Buggy pointer magic in copying type context
Added better debugging for type context
* Refactor: Rename vars
---
Python/tier2.c | 87 ++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 70 insertions(+), 17 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 10628e5720404b..fb04660cea3f05 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -113,12 +113,17 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
_Py_TYPENODE_t *parent = (_Py_TYPENODE_t *)_Py_TYPENODE_CLEAR_TAG(node);
// Check if part of locals
- if (parent - type_context->type_locals < nlocals) {
- type_locals[i] = node - (uintptr_t)orig_type_locals + (uintptr_t)type_locals;
+ int offset_locals = (int)(parent - type_context->type_locals);
+ if (0 <= offset_locals && offset_locals < nlocals) {
+ type_locals[i] = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)(&type_locals[offset_locals]));
}
// Is part of stack
else {
- type_locals[i] = node - (uintptr_t)orig_type_stack + (uintptr_t)type_stack;
+#if TYPEPROP_DEBUG
+ int offset_stack = (int)(parent - type_context->type_stack);
+ assert(0 <= offset_stack && offset_stack < nstack);
+#endif
+ type_locals[i] = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)(&type_stack[offset_stack]));
}
break;
}
@@ -139,12 +144,17 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
_Py_TYPENODE_t *parent = (_Py_TYPENODE_t *)_Py_TYPENODE_CLEAR_TAG(node);
// Check if part of locals
- if (parent - type_context->type_locals < nlocals) {
- type_stack[i] = node - (uintptr_t)orig_type_locals + (uintptr_t)type_locals;
+ int plocals = (int)(parent - type_context->type_locals);
+ if (0 <= plocals && plocals < nlocals) {
+ type_stack[i] = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)(&type_locals[plocals]));
}
// Is part of stack
else {
- type_stack[i] = node - (uintptr_t)orig_type_stack + (uintptr_t)type_stack;
+#if TYPEPROP_DEBUG
+ int offset_stack = (int)(parent - type_context->type_stack);
+ assert(0 <= offset_stack && offset_stack < nstack);
+#endif
+ type_stack[i] = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)(&type_stack[offset_stack]));
}
break;
}
@@ -394,23 +404,62 @@ print_typestack(const _PyTier2TypeContext *type_context)
int nstack_use = (int)(type_stackptr - type_stack);
int nstack = type_context->type_stack_len;
+ int nlocals = type_context->type_locals_len;
+
+ int plocals = 0;
+ int pstack = 0;
+ bool is_local = false;
+
fprintf(stderr, " Stack: %p: [", type_stack);
for (int i = 0; i < nstack; i++) {
- PyTypeObject *type = typenode_get_type(type_stack[i]);
- _Py_TYPENODE_t tag = _Py_TYPENODE_GET_TAG(type_stack[i]);
- fprintf(stderr, "%s%s%s",
+ _Py_TYPENODE_t node = type_stack[i];
+ PyTypeObject *type = typenode_get_type(node);
+ _Py_TYPENODE_t tag = _Py_TYPENODE_GET_TAG(node);
+
+ if (tag == TYPE_REF) {
+ _Py_TYPENODE_t *parent = (_Py_TYPENODE_t *)(_Py_TYPENODE_CLEAR_TAG(node));
+ plocals = (int)(parent - type_context->type_locals);
+ pstack = (int)(parent - type_context->type_stack);
+ is_local = (0 <= plocals) && (plocals < nlocals);
+ if (!is_local) {
+ assert((0 <= pstack) && (pstack < nstack));
+ }
+ }
+
+ fprintf(stderr, "%s%s",
i == nstack_use ? "." : " ",
- type == NULL ? "?" : type->tp_name,
- tag == TYPE_REF ? "*" : "");
+ type == NULL ? "?" : type->tp_name);
+ if (tag == TYPE_REF) {
+ fprintf(stderr, "%s%d]",
+ is_local ? "->locals[" : "->stack[",
+ is_local ? plocals : pstack);
+ }
}
fprintf(stderr, "]\n");
+
fprintf(stderr, " Locals %p: [", type_locals);
- for (int i = 0; i < type_context->type_locals_len; i++) {
- PyTypeObject *type = typenode_get_type(type_locals[i]);
- _Py_TYPENODE_t tag = _Py_TYPENODE_GET_TAG(type_locals[i]);
- fprintf(stderr, "%s%s ",
- type == NULL ? "?" : type->tp_name,
- tag == TYPE_REF ? "*" : "");
+ for (int i = 0; i < nlocals; i++) {
+ _Py_TYPENODE_t node = type_locals[i];
+ PyTypeObject *type = typenode_get_type(node);
+ _Py_TYPENODE_t tag = _Py_TYPENODE_GET_TAG(node);
+
+ if (tag == TYPE_REF) {
+ _Py_TYPENODE_t *parent = (_Py_TYPENODE_t *)(_Py_TYPENODE_CLEAR_TAG(node));
+ plocals = (int)(parent - type_context->type_locals);
+ pstack = (int)(parent - type_context->type_stack);
+ is_local = (0 <= plocals) && (plocals < nlocals);
+ if (!is_local) {
+ assert((0 <= pstack) && (pstack < nstack));
+ }
+ }
+
+ fprintf(stderr, " %s",
+ type == NULL ? "?" : type->tp_name);
+ if (tag == TYPE_REF) {
+ fprintf(stderr, "%s%d]",
+ is_local ? "->locals[" : "->stack[",
+ is_local ? plocals : pstack);
+ }
}
fprintf(stderr, "]\n");
}
@@ -1669,7 +1718,11 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
int deopt = _PyOpcode_Deopt[_Py_OPCODE(*curr_instr)];
if (IS_FORBIDDEN_OPCODE(deopt)) {
#if BB_DEBUG
+#ifdef Py_DEBUG
+ fprintf(stderr, "FORBIDDEN OPCODE %s\n", _PyOpcode_OpName[_Py_OPCODE(*curr_instr)]);
+#else
fprintf(stderr, "FORBIDDEN OPCODE %d\n", _Py_OPCODE(*curr_instr));
+#endif
#endif
return NULL;
}
From 80fa4ed0e408e9fda9d4aa04e4b47fdc1db5db0f Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Mon, 13 Mar 2023 15:56:24 +0000
Subject: [PATCH 085/280] gh-102192: Replace PyErr_Fetch/Restore etc by more
efficient alternatives (#102631)
---
Python/ceval.c | 42 +++++++++++++++++++-----------------------
1 file changed, 19 insertions(+), 23 deletions(-)
diff --git a/Python/ceval.c b/Python/ceval.c
index 017f5e9aeb977e..e4b03e37c37805 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -13,7 +13,7 @@
#include "pycore_object.h" // _PyObject_GC_TRACK()
#include "pycore_moduleobject.h" // PyModuleObject
#include "pycore_opcode.h" // EXTRA_CASES
-#include "pycore_pyerrors.h" // _PyErr_Fetch(), _PyErr_GetRaisedException()
+#include "pycore_pyerrors.h" // _PyErr_GetRaisedException()
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_range.h" // _PyRangeIterObject
@@ -1791,18 +1791,15 @@ do_raise(PyThreadState *tstate, PyObject *exc, PyObject *cause)
if (exc == NULL) {
/* Reraise */
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
- value = exc_info->exc_value;
- if (Py_IsNone(value) || value == NULL) {
+ exc = exc_info->exc_value;
+ if (Py_IsNone(exc) || exc == NULL) {
_PyErr_SetString(tstate, PyExc_RuntimeError,
"No active exception to reraise");
return 0;
}
- assert(PyExceptionInstance_Check(value));
- type = PyExceptionInstance_Class(value);
- Py_XINCREF(type);
- Py_XINCREF(value);
- PyObject *tb = PyException_GetTraceback(value); /* new ref */
- _PyErr_Restore(tstate, type, value, tb);
+ Py_INCREF(exc);
+ assert(PyExceptionInstance_Check(exc));
+ _PyErr_SetRaisedException(tstate, exc);
return 1;
}
@@ -2043,28 +2040,27 @@ call_exc_trace(Py_tracefunc func, PyObject *self,
PyThreadState *tstate,
_PyInterpreterFrame *f)
{
- PyObject *type, *value, *traceback, *orig_traceback, *arg;
- int err;
- _PyErr_Fetch(tstate, &type, &value, &orig_traceback);
- if (value == NULL) {
- value = Py_NewRef(Py_None);
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
+ assert(exc && PyExceptionInstance_Check(exc));
+ PyObject *type = PyExceptionInstance_Class(exc);
+ PyObject *traceback = PyException_GetTraceback(exc);
+ if (traceback == NULL) {
+ traceback = Py_NewRef(Py_None);
}
- _PyErr_NormalizeException(tstate, &type, &value, &orig_traceback);
- traceback = (orig_traceback != NULL) ? orig_traceback : Py_None;
- arg = PyTuple_Pack(3, type, value, traceback);
+ PyObject *arg = PyTuple_Pack(3, type, exc, traceback);
+ Py_XDECREF(traceback);
+
if (arg == NULL) {
- _PyErr_Restore(tstate, type, value, orig_traceback);
+ _PyErr_SetRaisedException(tstate, exc);
return;
}
- err = call_trace(func, self, tstate, f, PyTrace_EXCEPTION, arg);
+ int err = call_trace(func, self, tstate, f, PyTrace_EXCEPTION, arg);
Py_DECREF(arg);
if (err == 0) {
- _PyErr_Restore(tstate, type, value, orig_traceback);
+ _PyErr_SetRaisedException(tstate, exc);
}
else {
- Py_XDECREF(type);
- Py_XDECREF(value);
- Py_XDECREF(orig_traceback);
+ Py_XDECREF(exc);
}
}
From aaca52a4fc4ad2954d886735f46ec8091e8971b1 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Mon, 13 Mar 2023 15:59:20 +0000
Subject: [PATCH 086/280] gh-87092: refactor assemble() to a number of separate
functions, which do not need the compiler struct (#102562)
---
Lib/test/support/bytecode_helper.py | 2 +-
Lib/test/test_peepholer.py | 43 ++-
Python/compile.c | 534 +++++++++++++++-------------
3 files changed, 318 insertions(+), 261 deletions(-)
diff --git a/Lib/test/support/bytecode_helper.py b/Lib/test/support/bytecode_helper.py
index 190fe8723b1fb5..1d9b889c920986 100644
--- a/Lib/test/support/bytecode_helper.py
+++ b/Lib/test/support/bytecode_helper.py
@@ -125,7 +125,7 @@ def complete_insts_info(self, insts):
assert isinstance(item, tuple)
inst = list(reversed(item))
opcode = dis.opmap[inst.pop()]
- oparg = inst.pop() if opcode in self.HAS_ARG_OR_TARGET else 0
+ oparg = inst.pop()
loc = inst + [-1] * (4 - len(inst))
res.append((opcode, oparg, *loc))
return res
diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py
index aea234e38705a8..9ff017da53c2b1 100644
--- a/Lib/test/test_peepholer.py
+++ b/Lib/test/test_peepholer.py
@@ -995,15 +995,19 @@ def test_conditional_jump_forward_non_const_condition(self):
('LOAD_CONST', 2, 13),
lbl,
('LOAD_CONST', 3, 14),
+ ('RETURN_VALUE', 14),
]
- expected = [
+ expected_insts = [
('LOAD_NAME', 1, 11),
('POP_JUMP_IF_TRUE', lbl := self.Label(), 12),
- ('LOAD_CONST', 2, 13),
+ ('LOAD_CONST', 1, 13),
lbl,
- ('LOAD_CONST', 3, 14)
+ ('RETURN_CONST', 2, 14),
]
- self.cfg_optimization_test(insts, expected, consts=list(range(5)))
+ self.cfg_optimization_test(insts,
+ expected_insts,
+ consts=[0, 1, 2, 3, 4],
+ expected_consts=[0, 2, 3])
def test_conditional_jump_forward_const_condition(self):
# The unreachable branch of the jump is removed, the jump
@@ -1015,26 +1019,32 @@ def test_conditional_jump_forward_const_condition(self):
('LOAD_CONST', 2, 13),
lbl,
('LOAD_CONST', 3, 14),
+ ('RETURN_VALUE', 14),
]
- expected = [
- ('NOP', None, 11),
- ('NOP', None, 12),
- ('LOAD_CONST', 3, 14)
+ expected_insts = [
+ ('NOP', 11),
+ ('NOP', 12),
+ ('RETURN_CONST', 1, 14),
]
- self.cfg_optimization_test(insts, expected, consts=list(range(5)))
+ self.cfg_optimization_test(insts,
+ expected_insts,
+ consts=[0, 1, 2, 3, 4],
+ expected_consts=[0, 3])
def test_conditional_jump_backward_non_const_condition(self):
insts = [
lbl1 := self.Label(),
('LOAD_NAME', 1, 11),
('POP_JUMP_IF_TRUE', lbl1, 12),
- ('LOAD_CONST', 2, 13),
+ ('LOAD_NAME', 2, 13),
+ ('RETURN_VALUE', 13),
]
expected = [
lbl := self.Label(),
('LOAD_NAME', 1, 11),
('POP_JUMP_IF_TRUE', lbl, 12),
- ('LOAD_CONST', 2, 13)
+ ('LOAD_NAME', 2, 13),
+ ('RETURN_VALUE', 13),
]
self.cfg_optimization_test(insts, expected, consts=list(range(5)))
@@ -1042,16 +1052,17 @@ def test_conditional_jump_backward_const_condition(self):
# The unreachable branch of the jump is removed
insts = [
lbl1 := self.Label(),
- ('LOAD_CONST', 1, 11),
+ ('LOAD_CONST', 3, 11),
('POP_JUMP_IF_TRUE', lbl1, 12),
('LOAD_CONST', 2, 13),
+ ('RETURN_VALUE', 13),
]
- expected = [
+ expected_insts = [
lbl := self.Label(),
- ('NOP', None, 11),
- ('JUMP', lbl, 12)
+ ('NOP', 11),
+ ('JUMP', lbl, 12),
]
- self.cfg_optimization_test(insts, expected, consts=list(range(5)))
+ self.cfg_optimization_test(insts, expected_insts, consts=list(range(5)))
if __name__ == "__main__":
diff --git a/Python/compile.c b/Python/compile.c
index 45c97b4f8ef0e6..29e55b8b30c56b 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -308,7 +308,7 @@ typedef struct basicblock_ {
block, not to be confused with b_next, which is next by control flow. */
struct basicblock_ *b_list;
/* The label of this block if it is a jump target, -1 otherwise */
- int b_label;
+ jump_target_label b_label;
/* Exception stack at start of block, used by assembler to create the exception handling table */
ExceptStack *b_exceptstack;
/* pointer to an array of instructions, initially NULL */
@@ -501,7 +501,7 @@ instr_sequence_next_inst(instr_sequence *seq) {
static jump_target_label
instr_sequence_new_label(instr_sequence *seq)
{
- jump_target_label lbl = {seq->s_next_free_label++};
+ jump_target_label lbl = {++seq->s_next_free_label};
return lbl;
}
@@ -606,6 +606,14 @@ instr_sequence_to_cfg(instr_sequence *seq, cfg_builder *g) {
instruction *instr = &seq->s_instrs[i];
RETURN_IF_ERROR(cfg_builder_addop(g, instr->i_opcode, instr->i_oparg, instr->i_loc));
}
+ int nblocks = 0;
+ for (basicblock *b = g->g_block_list; b != NULL; b = b->b_list) {
+ nblocks++;
+ }
+ if ((size_t)nblocks > SIZE_MAX / sizeof(basicblock *)) {
+ PyErr_NoMemory();
+ return ERROR;
+ }
return SUCCESS;
}
@@ -937,6 +945,7 @@ dictbytype(PyObject *src, int scope_type, int flag, Py_ssize_t offset)
static bool
cfg_builder_check(cfg_builder *g)
{
+ assert(g->g_entryblock->b_iused > 0);
for (basicblock *block = g->g_block_list; block != NULL; block = block->b_list) {
assert(!_PyMem_IsPtrFreed(block));
if (block->b_instr != NULL) {
@@ -1086,7 +1095,7 @@ cfg_builder_new_block(cfg_builder *g)
/* Extend the singly linked list of blocks with new block. */
b->b_list = g->g_block_list;
g->g_block_list = b;
- b->b_label = -1;
+ b->b_label = NO_LABEL;
return b;
}
@@ -1267,11 +1276,21 @@ basicblock_addop(basicblock *b, int opcode, int oparg, location loc)
static bool
cfg_builder_current_block_is_terminated(cfg_builder *g)
{
- if (IS_LABEL(g->g_current_label)) {
+ struct cfg_instr *last = basicblock_last_instr(g->g_curblock);
+ if (last && IS_TERMINATOR_OPCODE(last->i_opcode)) {
return true;
}
- struct cfg_instr *last = basicblock_last_instr(g->g_curblock);
- return last && IS_TERMINATOR_OPCODE(last->i_opcode);
+ if (IS_LABEL(g->g_current_label)) {
+ if (last || IS_LABEL(g->g_curblock->b_label)) {
+ return true;
+ }
+ else {
+ /* current block is empty, label it */
+ g->g_curblock->b_label = g->g_current_label;
+ g->g_current_label = NO_LABEL;
+ }
+ }
+ return false;
}
static int
@@ -1282,7 +1301,7 @@ cfg_builder_maybe_start_new_block(cfg_builder *g)
if (b == NULL) {
return ERROR;
}
- b->b_label = g->g_current_label.id;
+ b->b_label = g->g_current_label;
g->g_current_label = NO_LABEL;
cfg_builder_use_next_block(g, b);
}
@@ -1432,50 +1451,49 @@ merge_consts_recursive(PyObject *const_cache, PyObject *o)
}
static Py_ssize_t
-compiler_add_const(struct compiler *c, PyObject *o)
+compiler_add_const(PyObject *const_cache, struct compiler_unit *u, PyObject *o)
{
- PyObject *key = merge_consts_recursive(c->c_const_cache, o);
+ assert(PyDict_CheckExact(const_cache));
+ PyObject *key = merge_consts_recursive(const_cache, o);
if (key == NULL) {
return ERROR;
}
- Py_ssize_t arg = dict_add_o(c->u->u_consts, key);
+ Py_ssize_t arg = dict_add_o(u->u_consts, key);
Py_DECREF(key);
return arg;
}
static int
-compiler_addop_load_const(struct compiler *c, location loc, PyObject *o)
+compiler_addop_load_const(PyObject *const_cache, struct compiler_unit *u, location loc, PyObject *o)
{
- Py_ssize_t arg = compiler_add_const(c, o);
+ Py_ssize_t arg = compiler_add_const(const_cache, u, o);
if (arg < 0) {
return ERROR;
}
- return codegen_addop_i(INSTR_SEQUENCE(c), LOAD_CONST, arg, loc);
+ return codegen_addop_i(&u->u_instr_sequence, LOAD_CONST, arg, loc);
}
static int
-compiler_addop_o(struct compiler *c, location loc,
+compiler_addop_o(struct compiler_unit *u, location loc,
int opcode, PyObject *dict, PyObject *o)
{
Py_ssize_t arg = dict_add_o(dict, o);
if (arg < 0) {
return ERROR;
}
- return codegen_addop_i(INSTR_SEQUENCE(c), opcode, arg, loc);
+ return codegen_addop_i(&u->u_instr_sequence, opcode, arg, loc);
}
static int
-compiler_addop_name(struct compiler *c, location loc,
+compiler_addop_name(struct compiler_unit *u, location loc,
int opcode, PyObject *dict, PyObject *o)
{
- Py_ssize_t arg;
-
- PyObject *mangled = _Py_Mangle(c->u->u_private, o);
+ PyObject *mangled = _Py_Mangle(u->u_private, o);
if (!mangled) {
return ERROR;
}
- arg = dict_add_o(dict, mangled);
+ Py_ssize_t arg = dict_add_o(dict, mangled);
Py_DECREF(mangled);
if (arg < 0) {
return ERROR;
@@ -1488,7 +1506,7 @@ compiler_addop_name(struct compiler *c, location loc,
arg <<= 1;
arg |= 1;
}
- return codegen_addop_i(INSTR_SEQUENCE(c), opcode, arg, loc);
+ return codegen_addop_i(&u->u_instr_sequence, opcode, arg, loc);
}
/* Add an opcode with an integer argument */
@@ -1509,7 +1527,7 @@ codegen_addop_i(instr_sequence *seq, int opcode, Py_ssize_t oparg, location loc)
static int
codegen_addop_j(instr_sequence *seq, location loc,
- int opcode, jump_target_label target)
+ int opcode, jump_target_label target)
{
assert(IS_LABEL(target));
assert(IS_JUMP_OPCODE(opcode) || IS_BLOCK_PUSH_OPCODE(opcode));
@@ -1527,7 +1545,7 @@ codegen_addop_j(instr_sequence *seq, location loc,
}
#define ADDOP_LOAD_CONST(C, LOC, O) \
- RETURN_IF_ERROR(compiler_addop_load_const((C), (LOC), (O)))
+ RETURN_IF_ERROR(compiler_addop_load_const((C)->c_const_cache, (C)->u, (LOC), (O)))
/* Same as ADDOP_LOAD_CONST, but steals a reference. */
#define ADDOP_LOAD_CONST_NEW(C, LOC, O) { \
@@ -1535,7 +1553,7 @@ codegen_addop_j(instr_sequence *seq, location loc,
if (__new_const == NULL) { \
return ERROR; \
} \
- if (compiler_addop_load_const((C), (LOC), __new_const) < 0) { \
+ if (compiler_addop_load_const((C)->c_const_cache, (C)->u, (LOC), __new_const) < 0) { \
Py_DECREF(__new_const); \
return ERROR; \
} \
@@ -1544,7 +1562,7 @@ codegen_addop_j(instr_sequence *seq, location loc,
#define ADDOP_N(C, LOC, OP, O, TYPE) { \
assert(!HAS_CONST(OP)); /* use ADDOP_LOAD_CONST_NEW */ \
- if (compiler_addop_o((C), (LOC), (OP), (C)->u->u_ ## TYPE, (O)) < 0) { \
+ if (compiler_addop_o((C)->u, (LOC), (OP), (C)->u->u_ ## TYPE, (O)) < 0) { \
Py_DECREF((O)); \
return ERROR; \
} \
@@ -1552,7 +1570,7 @@ codegen_addop_j(instr_sequence *seq, location loc,
}
#define ADDOP_NAME(C, LOC, OP, O, TYPE) \
- RETURN_IF_ERROR(compiler_addop_name((C), (LOC), (OP), (C)->u->u_ ## TYPE, (O)))
+ RETURN_IF_ERROR(compiler_addop_name((C)->u, (LOC), (OP), (C)->u->u_ ## TYPE, (O)))
#define ADDOP_I(C, LOC, OP, O) \
RETURN_IF_ERROR(codegen_addop_i(INSTR_SEQUENCE(C), (OP), (O), (LOC)))
@@ -2556,7 +2574,7 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async)
if (c->c_optimize < 2) {
docstring = _PyAST_GetDocString(body);
}
- if (compiler_add_const(c, docstring ? docstring : Py_None) < 0) {
+ if (compiler_add_const(c->c_const_cache, c->u, docstring ? docstring : Py_None) < 0) {
compiler_exit_scope(c);
return ERROR;
}
@@ -2924,7 +2942,7 @@ compiler_lambda(struct compiler *c, expr_ty e)
/* Make None the first constant, so the lambda can't have a
docstring. */
- RETURN_IF_ERROR(compiler_add_const(c, Py_None));
+ RETURN_IF_ERROR(compiler_add_const(c->c_const_cache, c->u, Py_None));
c->u->u_argcount = asdl_seq_LEN(args->args);
c->u->u_posonlyargcount = asdl_seq_LEN(args->posonlyargs);
@@ -4915,7 +4933,7 @@ compiler_call_simple_kw_helper(struct compiler *c, location loc,
keyword_ty kw = asdl_seq_GET(keywords, i);
PyTuple_SET_ITEM(names, i, Py_NewRef(kw->arg));
}
- Py_ssize_t arg = compiler_add_const(c, names);
+ Py_ssize_t arg = compiler_add_const(c->c_const_cache, c->u, names);
if (arg < 0) {
return ERROR;
}
@@ -7372,7 +7390,7 @@ push_cold_blocks_to_end(cfg_builder *g, int code_flags) {
if (explicit_jump == NULL) {
return ERROR;
}
- basicblock_addop(explicit_jump, JUMP, b->b_next->b_label, NO_LOCATION);
+ basicblock_addop(explicit_jump, JUMP, b->b_next->b_label.id, NO_LOCATION);
explicit_jump->b_cold = 1;
explicit_jump->b_next = b->b_next;
b->b_next = explicit_jump;
@@ -7676,13 +7694,33 @@ assemble_emit_location(struct assembler* a, location loc, int isize)
return write_location_info_entry(a, loc, isize);
}
-/* assemble_emit()
+static int
+assemble_location_info(struct assembler *a, basicblock *entryblock, int firstlineno)
+{
+ a->a_lineno = firstlineno;
+ location loc = NO_LOCATION;
+ int size = 0;
+ for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
+ for (int j = 0; j < b->b_iused; j++) {
+ if (!same_location(loc, b->b_instr[j].i_loc)) {
+ RETURN_IF_ERROR(assemble_emit_location(a, loc, size));
+ loc = b->b_instr[j].i_loc;
+ size = 0;
+ }
+ size += instr_size(&b->b_instr[j]);
+ }
+ }
+ RETURN_IF_ERROR(assemble_emit_location(a, loc, size));
+ return SUCCESS;
+}
+
+/* assemble_emit_instr()
Extend the bytecode with a new instruction.
Update lnotab if necessary.
*/
static int
-assemble_emit(struct assembler *a, struct cfg_instr *i)
+assemble_emit_instr(struct assembler *a, struct cfg_instr *i)
{
Py_ssize_t len = PyBytes_GET_SIZE(a->a_bytecode);
_Py_CODEUNIT *code;
@@ -7700,6 +7738,35 @@ assemble_emit(struct assembler *a, struct cfg_instr *i)
return SUCCESS;
}
+static int merge_const_one(PyObject *const_cache, PyObject **obj);
+
+static int
+assemble_emit(struct assembler *a, basicblock *entryblock, int first_lineno,
+ PyObject *const_cache)
+{
+ RETURN_IF_ERROR(assemble_init(a, first_lineno));
+
+ for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
+ for (int j = 0; j < b->b_iused; j++) {
+ RETURN_IF_ERROR(assemble_emit_instr(a, &b->b_instr[j]));
+ }
+ }
+
+ RETURN_IF_ERROR(assemble_location_info(a, entryblock, a->a_lineno));
+
+ RETURN_IF_ERROR(assemble_exception_table(a, entryblock));
+
+ RETURN_IF_ERROR(_PyBytes_Resize(&a->a_except_table, a->a_except_table_off));
+ RETURN_IF_ERROR(merge_const_one(const_cache, &a->a_except_table));
+
+ RETURN_IF_ERROR(_PyBytes_Resize(&a->a_linetable, a->a_location_off));
+ RETURN_IF_ERROR(merge_const_one(const_cache, &a->a_linetable));
+
+ RETURN_IF_ERROR(_PyBytes_Resize(&a->a_bytecode, a->a_offset * sizeof(_Py_CODEUNIT)));
+ RETURN_IF_ERROR(merge_const_one(const_cache, &a->a_bytecode));
+ return SUCCESS;
+}
+
static int
normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
struct cfg_instr *last = basicblock_last_instr(b);
@@ -7760,7 +7827,7 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
if (backwards_jump == NULL) {
return ERROR;
}
- basicblock_addop(backwards_jump, JUMP, target->b_label, NO_LOCATION);
+ basicblock_addop(backwards_jump, JUMP, target->b_label.id, NO_LOCATION);
backwards_jump->b_instr[0].i_target = target;
last->i_opcode = reversed_opcode;
last->i_target = b->b_next;
@@ -7963,9 +8030,9 @@ fast_scan_many_locals(basicblock *entryblock, int nlocals)
static int
add_checks_for_loads_of_uninitialized_variables(basicblock *entryblock,
- struct compiler *c)
+ int nlocals,
+ int nparams)
{
- int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
if (nlocals == 0) {
return SUCCESS;
}
@@ -7986,7 +8053,6 @@ add_checks_for_loads_of_uninitialized_variables(basicblock *entryblock,
// First origin of being uninitialized:
// The non-parameter locals in the entry block.
- int nparams = (int)PyList_GET_SIZE(c->u->u_ste->ste_varnames);
uint64_t start_mask = 0;
for (int i = nparams; i < nlocals; i++) {
start_mask |= (uint64_t)1 << i;
@@ -8092,7 +8158,7 @@ compute_code_flags(struct compiler *c)
static int
merge_const_one(PyObject *const_cache, PyObject **obj)
{
- PyDict_CheckExact(const_cache);
+ assert(PyDict_CheckExact(const_cache));
PyObject *key = _PyCode_ConstantKey(*obj);
if (key == NULL) {
return ERROR;
@@ -8122,29 +8188,29 @@ extern void _Py_set_localsplus_info(int, PyObject *, unsigned char,
PyObject *, PyObject *);
static void
-compute_localsplus_info(struct compiler *c, int nlocalsplus,
+compute_localsplus_info(struct compiler_unit *u, int nlocalsplus,
PyObject *names, PyObject *kinds)
{
PyObject *k, *v;
Py_ssize_t pos = 0;
- while (PyDict_Next(c->u->u_varnames, &pos, &k, &v)) {
+ while (PyDict_Next(u->u_varnames, &pos, &k, &v)) {
int offset = (int)PyLong_AS_LONG(v);
assert(offset >= 0);
assert(offset < nlocalsplus);
// For now we do not distinguish arg kinds.
_PyLocals_Kind kind = CO_FAST_LOCAL;
- if (PyDict_GetItem(c->u->u_cellvars, k) != NULL) {
+ if (PyDict_GetItem(u->u_cellvars, k) != NULL) {
kind |= CO_FAST_CELL;
}
_Py_set_localsplus_info(offset, k, kind, names, kinds);
}
- int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
+ int nlocals = (int)PyDict_GET_SIZE(u->u_varnames);
// This counter mirrors the fix done in fix_cell_offsets().
int numdropped = 0;
pos = 0;
- while (PyDict_Next(c->u->u_cellvars, &pos, &k, &v)) {
- if (PyDict_GetItem(c->u->u_varnames, k) != NULL) {
+ while (PyDict_Next(u->u_cellvars, &pos, &k, &v)) {
+ if (PyDict_GetItem(u->u_varnames, k) != NULL) {
// Skip cells that are already covered by locals.
numdropped += 1;
continue;
@@ -8157,7 +8223,7 @@ compute_localsplus_info(struct compiler *c, int nlocalsplus,
}
pos = 0;
- while (PyDict_Next(c->u->u_freevars, &pos, &k, &v)) {
+ while (PyDict_Next(u->u_freevars, &pos, &k, &v)) {
int offset = (int)PyLong_AS_LONG(v);
assert(offset >= 0);
offset += nlocals - numdropped;
@@ -8167,19 +8233,20 @@ compute_localsplus_info(struct compiler *c, int nlocalsplus,
}
static PyCodeObject *
-makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
- int maxdepth, int nlocalsplus, int code_flags)
+makecode(struct compiler_unit *u, struct assembler *a, PyObject *const_cache,
+ PyObject *constslist, int maxdepth, int nlocalsplus, int code_flags,
+ PyObject *filename)
{
PyCodeObject *co = NULL;
PyObject *names = NULL;
PyObject *consts = NULL;
PyObject *localsplusnames = NULL;
PyObject *localspluskinds = NULL;
- names = dict_keys_inorder(c->u->u_names, 0);
+ names = dict_keys_inorder(u->u_names, 0);
if (!names) {
goto error;
}
- if (merge_const_one(c->c_const_cache, &names) < 0) {
+ if (merge_const_one(const_cache, &names) < 0) {
goto error;
}
@@ -8187,17 +8254,17 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
if (consts == NULL) {
goto error;
}
- if (merge_const_one(c->c_const_cache, &consts) < 0) {
+ if (merge_const_one(const_cache, &consts) < 0) {
goto error;
}
- assert(c->u->u_posonlyargcount < INT_MAX);
- assert(c->u->u_argcount < INT_MAX);
- assert(c->u->u_kwonlyargcount < INT_MAX);
- int posonlyargcount = (int)c->u->u_posonlyargcount;
- int posorkwargcount = (int)c->u->u_argcount;
+ assert(u->u_posonlyargcount < INT_MAX);
+ assert(u->u_argcount < INT_MAX);
+ assert(u->u_kwonlyargcount < INT_MAX);
+ int posonlyargcount = (int)u->u_posonlyargcount;
+ int posorkwargcount = (int)u->u_argcount;
assert(INT_MAX - posonlyargcount - posorkwargcount > 0);
- int kwonlyargcount = (int)c->u->u_kwonlyargcount;
+ int kwonlyargcount = (int)u->u_kwonlyargcount;
localsplusnames = PyTuple_New(nlocalsplus);
if (localsplusnames == NULL) {
@@ -8207,16 +8274,16 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
if (localspluskinds == NULL) {
goto error;
}
- compute_localsplus_info(c, nlocalsplus, localsplusnames, localspluskinds);
+ compute_localsplus_info(u, nlocalsplus, localsplusnames, localspluskinds);
struct _PyCodeConstructor con = {
- .filename = c->c_filename,
- .name = c->u->u_name,
- .qualname = c->u->u_qualname ? c->u->u_qualname : c->u->u_name,
+ .filename = filename,
+ .name = u->u_name,
+ .qualname = u->u_qualname ? u->u_qualname : u->u_name,
.flags = code_flags,
.code = a->a_bytecode,
- .firstlineno = c->u->u_firstlineno,
+ .firstlineno = u->u_firstlineno,
.linetable = a->a_linetable,
.consts = consts,
@@ -8238,7 +8305,7 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
goto error;
}
- if (merge_const_one(c->c_const_cache, &localsplusnames) < 0) {
+ if (merge_const_one(const_cache, &localsplusnames) < 0) {
goto error;
}
con.localsplusnames = localsplusnames;
@@ -8289,7 +8356,7 @@ dump_basicblock(const basicblock *b)
{
const char *b_return = basicblock_returns(b) ? "return " : "";
fprintf(stderr, "%d: [EH=%d CLD=%d WRM=%d NO_FT=%d %p] used: %d, depth: %d, offset: %d %s\n",
- b->b_label, b->b_except_handler, b->b_cold, b->b_warm, BB_NO_FALLTHROUGH(b), b, b->b_iused,
+ b->b_label.id, b->b_except_handler, b->b_cold, b->b_warm, BB_NO_FALLTHROUGH(b), b, b->b_iused,
b->b_startdepth, b->b_offset, b_return);
if (b->b_instr) {
int i;
@@ -8316,11 +8383,11 @@ static int
duplicate_exits_without_lineno(cfg_builder *g);
static int *
-build_cellfixedoffsets(struct compiler *c)
+build_cellfixedoffsets(struct compiler_unit *u)
{
- int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
- int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
- int nfreevars = (int)PyDict_GET_SIZE(c->u->u_freevars);
+ int nlocals = (int)PyDict_GET_SIZE(u->u_varnames);
+ int ncellvars = (int)PyDict_GET_SIZE(u->u_cellvars);
+ int nfreevars = (int)PyDict_GET_SIZE(u->u_freevars);
int noffsets = ncellvars + nfreevars;
int *fixed = PyMem_New(int, noffsets);
@@ -8334,8 +8401,8 @@ build_cellfixedoffsets(struct compiler *c)
PyObject *varname, *cellindex;
Py_ssize_t pos = 0;
- while (PyDict_Next(c->u->u_cellvars, &pos, &varname, &cellindex)) {
- PyObject *varindex = PyDict_GetItem(c->u->u_varnames, varname);
+ while (PyDict_Next(u->u_cellvars, &pos, &varname, &cellindex)) {
+ PyObject *varindex = PyDict_GetItem(u->u_varnames, varname);
if (varindex != NULL) {
assert(PyLong_AS_LONG(cellindex) < INT_MAX);
assert(PyLong_AS_LONG(varindex) < INT_MAX);
@@ -8349,17 +8416,17 @@ build_cellfixedoffsets(struct compiler *c)
}
static int
-insert_prefix_instructions(struct compiler *c, basicblock *entryblock,
+insert_prefix_instructions(struct compiler_unit *u, basicblock *entryblock,
int *fixed, int nfreevars, int code_flags)
{
- assert(c->u->u_firstlineno > 0);
+ assert(u->u_firstlineno > 0);
/* Add the generator prefix instructions. */
if (code_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
struct cfg_instr make_gen = {
.i_opcode = RETURN_GENERATOR,
.i_oparg = 0,
- .i_loc = LOCATION(c->u->u_firstlineno, c->u->u_firstlineno, -1, -1),
+ .i_loc = LOCATION(u->u_firstlineno, u->u_firstlineno, -1, -1),
.i_target = NULL,
};
RETURN_IF_ERROR(insert_instruction(entryblock, 0, &make_gen));
@@ -8373,12 +8440,12 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock,
}
/* Set up cells for any variable that escapes, to be put in a closure. */
- const int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
+ const int ncellvars = (int)PyDict_GET_SIZE(u->u_cellvars);
if (ncellvars) {
- // c->u->u_cellvars has the cells out of order so we sort them
+ // u->u_cellvars has the cells out of order so we sort them
// before adding the MAKE_CELL instructions. Note that we
// adjust for arg cells, which come first.
- const int nvars = ncellvars + (int)PyDict_GET_SIZE(c->u->u_varnames);
+ const int nvars = ncellvars + (int)PyDict_GET_SIZE(u->u_varnames);
int *sorted = PyMem_RawCalloc(nvars, sizeof(int));
if (sorted == NULL) {
PyErr_NoMemory();
@@ -8447,11 +8514,11 @@ guarantee_lineno_for_exits(basicblock *entryblock, int firstlineno) {
}
static int
-fix_cell_offsets(struct compiler *c, basicblock *entryblock, int *fixedmap)
+fix_cell_offsets(struct compiler_unit *u, basicblock *entryblock, int *fixedmap)
{
- int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
- int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
- int nfreevars = (int)PyDict_GET_SIZE(c->u->u_freevars);
+ int nlocals = (int)PyDict_GET_SIZE(u->u_varnames);
+ int ncellvars = (int)PyDict_GET_SIZE(u->u_cellvars);
+ int nfreevars = (int)PyDict_GET_SIZE(u->u_freevars);
int noffsets = ncellvars + nfreevars;
// First deal with duplicates (arg cells).
@@ -8491,8 +8558,6 @@ fix_cell_offsets(struct compiler *c, basicblock *entryblock, int *fixedmap)
return numdropped;
}
-static void
-propagate_line_numbers(basicblock *entryblock);
#ifndef NDEBUG
@@ -8589,30 +8654,30 @@ remove_redundant_jumps(cfg_builder *g) {
}
static int
-prepare_localsplus(struct compiler* c, cfg_builder *g, int code_flags)
+prepare_localsplus(struct compiler_unit* u, cfg_builder *g, int code_flags)
{
- assert(PyDict_GET_SIZE(c->u->u_varnames) < INT_MAX);
- assert(PyDict_GET_SIZE(c->u->u_cellvars) < INT_MAX);
- assert(PyDict_GET_SIZE(c->u->u_freevars) < INT_MAX);
- int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
- int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
- int nfreevars = (int)PyDict_GET_SIZE(c->u->u_freevars);
+ assert(PyDict_GET_SIZE(u->u_varnames) < INT_MAX);
+ assert(PyDict_GET_SIZE(u->u_cellvars) < INT_MAX);
+ assert(PyDict_GET_SIZE(u->u_freevars) < INT_MAX);
+ int nlocals = (int)PyDict_GET_SIZE(u->u_varnames);
+ int ncellvars = (int)PyDict_GET_SIZE(u->u_cellvars);
+ int nfreevars = (int)PyDict_GET_SIZE(u->u_freevars);
assert(INT_MAX - nlocals - ncellvars > 0);
assert(INT_MAX - nlocals - ncellvars - nfreevars > 0);
int nlocalsplus = nlocals + ncellvars + nfreevars;
- int* cellfixedoffsets = build_cellfixedoffsets(c);
+ int* cellfixedoffsets = build_cellfixedoffsets(u);
if (cellfixedoffsets == NULL) {
return ERROR;
}
// This must be called before fix_cell_offsets().
- if (insert_prefix_instructions(c, g->g_entryblock, cellfixedoffsets, nfreevars, code_flags)) {
+ if (insert_prefix_instructions(u, g->g_entryblock, cellfixedoffsets, nfreevars, code_flags)) {
PyMem_Free(cellfixedoffsets);
return ERROR;
}
- int numdropped = fix_cell_offsets(c, g->g_entryblock, cellfixedoffsets);
+ int numdropped = fix_cell_offsets(u, g->g_entryblock, cellfixedoffsets);
PyMem_Free(cellfixedoffsets); // At this point we're done with it.
cellfixedoffsets = NULL;
if (numdropped < 0) {
@@ -8636,176 +8701,127 @@ add_return_at_end(struct compiler *c, int addNone)
return SUCCESS;
}
-static PyCodeObject *
-assemble(struct compiler *c, int addNone)
-{
- PyCodeObject *co = NULL;
- PyObject *consts = NULL;
- cfg_builder g_;
- cfg_builder *g = &g_;
- struct assembler a;
- memset(&a, 0, sizeof(struct assembler));
-
- int code_flags = compute_code_flags(c);
- if (code_flags < 0) {
- return NULL;
- }
-
- if (add_return_at_end(c, addNone) < 0) {
- return NULL;
- }
-
- /** Preprocessing **/
- if (instr_sequence_to_cfg(INSTR_SEQUENCE(c), g) < 0) {
- goto error;
- }
- assert(cfg_builder_check(g));
-
- int nblocks = 0;
- for (basicblock *b = g->g_block_list; b != NULL; b = b->b_list) {
- nblocks++;
- }
- if ((size_t)nblocks > SIZE_MAX / sizeof(basicblock *)) {
- PyErr_NoMemory();
- goto error;
- }
+static void propagate_line_numbers(basicblock *entryblock);
+static int
+resolve_line_numbers(struct compiler_unit *u, cfg_builder *g)
+{
/* Set firstlineno if it wasn't explicitly set. */
- if (!c->u->u_firstlineno) {
+ if (!u->u_firstlineno) {
if (g->g_entryblock->b_instr && g->g_entryblock->b_instr->i_loc.lineno) {
- c->u->u_firstlineno = g->g_entryblock->b_instr->i_loc.lineno;
+ u->u_firstlineno = g->g_entryblock->b_instr->i_loc.lineno;
}
else {
- c->u->u_firstlineno = 1;
+ u->u_firstlineno = 1;
}
}
+ RETURN_IF_ERROR(duplicate_exits_without_lineno(g));
+ propagate_line_numbers(g->g_entryblock);
+ guarantee_lineno_for_exits(g->g_entryblock, u->u_firstlineno);
+ return SUCCESS;
+}
+static int
+optimize_code_unit(cfg_builder *g, PyObject *consts, PyObject *const_cache,
+ int code_flags, int nlocals, int nparams)
+{
+ assert(cfg_builder_check(g));
+ /** Preprocessing **/
/* Map labels to targets and mark exception handlers */
- if (translate_jump_labels_to_targets(g->g_entryblock) < 0) {
- goto error;
- }
- if (mark_except_handlers(g->g_entryblock) < 0) {
- goto error;
- }
- if (label_exception_targets(g->g_entryblock)) {
- goto error;
- }
+ RETURN_IF_ERROR(translate_jump_labels_to_targets(g->g_entryblock));
+ RETURN_IF_ERROR(mark_except_handlers(g->g_entryblock));
+ RETURN_IF_ERROR(label_exception_targets(g->g_entryblock));
/** Optimization **/
- consts = consts_dict_keys_inorder(c->u->u_consts);
+ RETURN_IF_ERROR(optimize_cfg(g, consts, const_cache));
+ RETURN_IF_ERROR(remove_unused_consts(g->g_entryblock, consts));
+ RETURN_IF_ERROR(
+ add_checks_for_loads_of_uninitialized_variables(
+ g->g_entryblock, nlocals, nparams));
+
+ RETURN_IF_ERROR(push_cold_blocks_to_end(g, code_flags));
+ return SUCCESS;
+}
+
+static PyCodeObject *
+assemble_code_unit(struct compiler_unit *u, PyObject *const_cache,
+ int code_flags, PyObject *filename)
+{
+ PyCodeObject *co = NULL;
+ PyObject *consts = consts_dict_keys_inorder(u->u_consts);
if (consts == NULL) {
goto error;
}
- if (optimize_cfg(g, consts, c->c_const_cache)) {
- goto error;
- }
- if (remove_unused_consts(g->g_entryblock, consts) < 0) {
+ cfg_builder g;
+ if (instr_sequence_to_cfg(&u->u_instr_sequence, &g) < 0) {
goto error;
}
- if (add_checks_for_loads_of_uninitialized_variables(g->g_entryblock, c) < 0) {
+ int nparams = (int)PyList_GET_SIZE(u->u_ste->ste_varnames);
+ int nlocals = (int)PyDict_GET_SIZE(u->u_varnames);
+ if (optimize_code_unit(&g, consts, const_cache, code_flags, nlocals, nparams) < 0) {
goto error;
}
- /** line numbers (TODO: move this before optimization stage) */
- if (duplicate_exits_without_lineno(g) < 0) {
- goto error;
- }
- propagate_line_numbers(g->g_entryblock);
- guarantee_lineno_for_exits(g->g_entryblock, c->u->u_firstlineno);
+ /** Assembly **/
- if (push_cold_blocks_to_end(g, code_flags) < 0) {
+ if (resolve_line_numbers(u, &g) < 0) {
goto error;
}
- /** Assembly **/
-
- int nlocalsplus = prepare_localsplus(c, g, code_flags);
+ int nlocalsplus = prepare_localsplus(u, &g, code_flags);
if (nlocalsplus < 0) {
goto error;
}
- int maxdepth = stackdepth(g->g_entryblock, code_flags);
+ int maxdepth = stackdepth(g.g_entryblock, code_flags);
if (maxdepth < 0) {
goto error;
}
/* TO DO -- For 3.12, make sure that `maxdepth <= MAX_ALLOWED_STACK_USE` */
- convert_exception_handlers_to_nops(g->g_entryblock);
+ convert_exception_handlers_to_nops(g.g_entryblock);
/* Order of basic blocks must have been determined by now */
- if (normalize_jumps(g) < 0) {
+ if (normalize_jumps(&g) < 0) {
goto error;
}
- assert(no_redundant_jumps(g));
- assert(opcode_metadata_is_sane(g));
+ assert(no_redundant_jumps(&g));
+ assert(opcode_metadata_is_sane(&g));
/* Can't modify the bytecode after computing jump offsets. */
- assemble_jump_offsets(g->g_entryblock);
+ assemble_jump_offsets(g.g_entryblock);
- /* Create assembler */
- if (assemble_init(&a, c->u->u_firstlineno) < 0) {
- goto error;
- }
-
- /* Emit code. */
- for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
- for (int j = 0; j < b->b_iused; j++) {
- if (assemble_emit(&a, &b->b_instr[j]) < 0) {
- goto error;
- }
- }
+ struct assembler a;
+ int res = assemble_emit(&a, g.g_entryblock, u->u_firstlineno, const_cache);
+ if (res == SUCCESS) {
+ co = makecode(u, &a, const_cache, consts, maxdepth, nlocalsplus,
+ code_flags, filename);
}
+ assemble_free(&a);
- /* Emit location info */
- a.a_lineno = c->u->u_firstlineno;
- location loc = NO_LOCATION;
- int size = 0;
- for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
- for (int j = 0; j < b->b_iused; j++) {
- if (!same_location(loc, b->b_instr[j].i_loc)) {
- if (assemble_emit_location(&a, loc, size)) {
- goto error;
- }
- loc = b->b_instr[j].i_loc;
- size = 0;
- }
- size += instr_size(&b->b_instr[j]);
- }
- }
- if (assemble_emit_location(&a, loc, size)) {
- goto error;
- }
+ error:
+ Py_XDECREF(consts);
+ cfg_builder_fini(&g);
+ return co;
+}
- if (assemble_exception_table(&a, g->g_entryblock) < 0) {
- goto error;
- }
- if (_PyBytes_Resize(&a.a_except_table, a.a_except_table_off) < 0) {
- goto error;
- }
- if (merge_const_one(c->c_const_cache, &a.a_except_table) < 0) {
- goto error;
- }
+static PyCodeObject *
+assemble(struct compiler *c, int addNone)
+{
+ struct compiler_unit *u = c->u;
+ PyObject *const_cache = c->c_const_cache;
+ PyObject *filename = c->c_filename;
- if (_PyBytes_Resize(&a.a_linetable, a.a_location_off) < 0) {
- goto error;
- }
- if (merge_const_one(c->c_const_cache, &a.a_linetable) < 0) {
- goto error;
+ int code_flags = compute_code_flags(c);
+ if (code_flags < 0) {
+ return NULL;
}
- if (_PyBytes_Resize(&a.a_bytecode, a.a_offset * sizeof(_Py_CODEUNIT)) < 0) {
- goto error;
- }
- if (merge_const_one(c->c_const_cache, &a.a_bytecode) < 0) {
- goto error;
+ if (add_return_at_end(c, addNone) < 0) {
+ return NULL;
}
- co = makecode(c, &a, consts, maxdepth, nlocalsplus, code_flags);
- error:
- Py_XDECREF(consts);
- cfg_builder_fini(g);
- assemble_free(&a);
- return co;
+ return assemble_code_unit(u, const_cache, code_flags, filename);
}
static PyObject*
@@ -9529,8 +9545,8 @@ translate_jump_labels_to_targets(basicblock *entryblock)
{
int max_label = -1;
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
- if (b->b_label > max_label) {
- max_label = b->b_label;
+ if (b->b_label.id > max_label) {
+ max_label = b->b_label.id;
}
}
size_t mapsize = sizeof(basicblock *) * (max_label + 1);
@@ -9541,8 +9557,8 @@ translate_jump_labels_to_targets(basicblock *entryblock)
}
memset(label2block, 0, mapsize);
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
- if (b->b_label >= 0) {
- label2block[b->b_label] = b;
+ if (b->b_label.id >= 0) {
+ label2block[b->b_label.id] = b;
}
}
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
@@ -9554,7 +9570,7 @@ translate_jump_labels_to_targets(basicblock *entryblock)
assert(lbl >= 0 && lbl <= max_label);
instr->i_target = label2block[lbl];
assert(instr->i_target != NULL);
- assert(instr->i_target->b_label == lbl);
+ assert(instr->i_target->b_label.id == lbl);
}
}
}
@@ -9828,9 +9844,15 @@ instructions_to_cfg(PyObject *instructions, cfg_builder *g)
if (PyErr_Occurred()) {
goto error;
}
- int oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1));
- if (PyErr_Occurred()) {
- goto error;
+ int oparg;
+ if (HAS_ARG(opcode)) {
+ oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1));
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+ }
+ else {
+ oparg = 0;
}
location loc;
loc.lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 2));
@@ -9851,7 +9873,10 @@ instructions_to_cfg(PyObject *instructions, cfg_builder *g)
}
RETURN_IF_ERROR(cfg_builder_addop(g, opcode, oparg, loc));
}
-
+ struct cfg_instr *last = basicblock_last_instr(g->g_curblock);
+ if (last && !IS_TERMINATOR_OPCODE(last->i_opcode)) {
+ RETURN_IF_ERROR(cfg_builder_addop(g, RETURN_VALUE, 0, NO_LOCATION));
+ }
PyMem_Free(is_target);
return SUCCESS;
error:
@@ -9859,6 +9884,38 @@ instructions_to_cfg(PyObject *instructions, cfg_builder *g)
return ERROR;
}
+static PyObject *
+instr_sequence_to_instructions(instr_sequence *seq) {
+ PyObject *instructions = PyList_New(0);
+ if (instructions == NULL) {
+ return NULL;
+ }
+ for (int i = 0; i < seq->s_used; i++) {
+ instruction *instr = &seq->s_instrs[i];
+ location loc = instr->i_loc;
+ int arg = HAS_TARGET(instr->i_opcode) ?
+ seq->s_labelmap[instr->i_oparg] : instr->i_oparg;
+
+ PyObject *inst_tuple = Py_BuildValue(
+ "(iiiiii)", instr->i_opcode, arg,
+ loc.lineno, loc.end_lineno,
+ loc.col_offset, loc.end_col_offset);
+ if (inst_tuple == NULL) {
+ goto error;
+ }
+
+ int res = PyList_Append(instructions, inst_tuple);
+ Py_DECREF(inst_tuple);
+ if (res != 0) {
+ goto error;
+ }
+ }
+ return instructions;
+error:
+ Py_XDECREF(instructions);
+ return NULL;
+}
+
static PyObject *
cfg_to_instructions(cfg_builder *g)
{
@@ -9868,7 +9925,7 @@ cfg_to_instructions(cfg_builder *g)
}
int lbl = 0;
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
- b->b_label = lbl;
+ b->b_label = (jump_target_label){lbl};
lbl += b->b_iused;
}
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
@@ -9876,7 +9933,7 @@ cfg_to_instructions(cfg_builder *g)
struct cfg_instr *instr = &b->b_instr[i];
location loc = instr->i_loc;
int arg = HAS_TARGET(instr->i_opcode) ?
- instr->i_target->b_label : instr->i_oparg;
+ instr->i_target->b_label.id : instr->i_oparg;
PyObject *inst_tuple = Py_BuildValue(
"(iiiiii)", instr->i_opcode, arg,
@@ -9932,19 +9989,10 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags,
goto finally;
}
- cfg_builder g;
- if (instr_sequence_to_cfg(INSTR_SEQUENCE(c), &g) < 0) {
- goto finally;
- }
- if (translate_jump_labels_to_targets(g.g_entryblock) < 0) {
- goto finally;
- }
-
- res = cfg_to_instructions(&g);
+ res = instr_sequence_to_instructions(INSTR_SEQUENCE(c));
finally:
compiler_exit_scope(c);
- cfg_builder_fini(&g);
compiler_free(c);
_PyArena_Free(arena);
return res;
@@ -9954,7 +10002,11 @@ PyObject *
_PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts)
{
PyObject *res = NULL;
- PyObject *const_cache = NULL;
+ PyObject *const_cache = PyDict_New();
+ if (const_cache == NULL) {
+ return NULL;
+ }
+
cfg_builder g;
memset(&g, 0, sizeof(cfg_builder));
if (cfg_builder_init(&g) < 0) {
@@ -9963,19 +10015,13 @@ _PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts)
if (instructions_to_cfg(instructions, &g) < 0) {
goto error;
}
- const_cache = PyDict_New();
- if (const_cache == NULL) {
- goto error;
- }
- if (translate_jump_labels_to_targets(g.g_entryblock) < 0) {
- goto error;
- }
- if (optimize_cfg(&g, consts, const_cache) < 0) {
+ int code_flags = 0, nlocals = 0, nparams = 0;
+ if (optimize_code_unit(&g, consts, const_cache, code_flags, nlocals, nparams) < 0) {
goto error;
}
res = cfg_to_instructions(&g);
error:
- Py_XDECREF(const_cache);
+ Py_DECREF(const_cache);
cfg_builder_fini(&g);
return res;
}
From 3985abda530a9f1527cf4a3910f5ce7001ada141 Mon Sep 17 00:00:00 2001
From: Mark Shannon
Date: Mon, 13 Mar 2023 18:35:37 +0000
Subject: [PATCH 087/280] GH-100987: Don't cache references to the names and
consts array in `_PyEval_EvalFrameDefault`. (#102640)
* Rename local variables, names and consts, from the interpeter loop. Will allow non-code objects in frames for better introspection of C builtins and extensions.
* Remove unused dummy variables.
---
Python/bytecodes.c | 42 +++++++++++++++++-----------------
Python/ceval.c | 7 ------
Python/generated_cases.c.h | 46 +++++++++++++++++++-------------------
3 files changed, 43 insertions(+), 52 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 68a69d62bcc9fe..204f702588fa5c 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -70,8 +70,6 @@ dummy_func(
unsigned int oparg,
_Py_atomic_int * const eval_breaker,
_PyCFrame cframe,
- PyObject *names,
- PyObject *consts,
_Py_CODEUNIT *next_instr,
PyObject **stack_pointer,
PyObject *kwnames,
@@ -627,7 +625,7 @@ dummy_func(
}
inst(RETURN_CONST, (--)) {
- PyObject *retval = GETITEM(consts, oparg);
+ PyObject *retval = GETITEM(frame->f_code->co_consts, oparg);
Py_INCREF(retval);
assert(EMPTY());
_PyFrame_SetStackPointer(frame, stack_pointer);
@@ -917,7 +915,7 @@ dummy_func(
}
inst(STORE_NAME, (v -- )) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
PyObject *ns = LOCALS();
int err;
if (ns == NULL) {
@@ -935,7 +933,7 @@ dummy_func(
}
inst(DELETE_NAME, (--)) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
PyObject *ns = LOCALS();
int err;
if (ns == NULL) {
@@ -1029,7 +1027,7 @@ dummy_func(
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
assert(cframe.use_tracing == 0);
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
next_instr--;
_Py_Specialize_StoreAttr(owner, next_instr, name);
DISPATCH_SAME_OPARG();
@@ -1040,7 +1038,7 @@ dummy_func(
#else
(void)counter; // Unused.
#endif /* ENABLE_SPECIALIZATION */
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err = PyObject_SetAttr(owner, name, v);
Py_DECREF(v);
Py_DECREF(owner);
@@ -1048,21 +1046,21 @@ dummy_func(
}
inst(DELETE_ATTR, (owner --)) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
Py_DECREF(owner);
ERROR_IF(err, error);
}
inst(STORE_GLOBAL, (v --)) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err = PyDict_SetItem(GLOBALS(), name, v);
Py_DECREF(v);
ERROR_IF(err, error);
}
inst(DELETE_GLOBAL, (--)) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err;
err = PyDict_DelItem(GLOBALS(), name);
// Can't use ERROR_IF here.
@@ -1076,7 +1074,7 @@ dummy_func(
}
inst(LOAD_NAME, ( -- v)) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
PyObject *locals = LOCALS();
if (locals == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
@@ -1147,7 +1145,7 @@ dummy_func(
_PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
- PyObject *name = GETITEM(names, oparg>>1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg>>1);
next_instr--;
_Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name);
DISPATCH_SAME_OPARG();
@@ -1155,7 +1153,7 @@ dummy_func(
STAT_INC(LOAD_GLOBAL, deferred);
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
#endif /* ENABLE_SPECIALIZATION */
- PyObject *name = GETITEM(names, oparg>>1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg>>1);
if (PyDict_CheckExact(GLOBALS())
&& PyDict_CheckExact(BUILTINS()))
{
@@ -1509,7 +1507,7 @@ dummy_func(
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
- PyObject *name = GETITEM(names, oparg>>1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg>>1);
next_instr--;
_Py_Specialize_LoadAttr(owner, next_instr, name);
DISPATCH_SAME_OPARG();
@@ -1517,7 +1515,7 @@ dummy_func(
STAT_INC(LOAD_ATTR, deferred);
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
#endif /* ENABLE_SPECIALIZATION */
- PyObject *name = GETITEM(names, oparg >> 1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 1);
if (oparg & 1) {
/* Designed to work in tandem with CALL, pushes two values. */
PyObject* meth = NULL;
@@ -1598,7 +1596,7 @@ dummy_func(
PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
DEOPT_IF(dict == NULL, LOAD_ATTR);
assert(PyDict_CheckExact((PyObject *)dict));
- PyObject *name = GETITEM(names, oparg>>1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg>>1);
uint16_t hint = index;
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR);
if (DK_IS_UNICODE(dict->ma_keys)) {
@@ -1689,7 +1687,7 @@ dummy_func(
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR);
STAT_INC(LOAD_ATTR, hit);
- PyObject *name = GETITEM(names, oparg >> 1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 1);
Py_INCREF(f);
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2);
// Manipulate stack directly because we exit with DISPATCH_INLINED().
@@ -1734,7 +1732,7 @@ dummy_func(
PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
DEOPT_IF(dict == NULL, STORE_ATTR);
assert(PyDict_CheckExact((PyObject *)dict));
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR);
PyObject *old_value;
uint64_t new_version;
@@ -1928,14 +1926,14 @@ dummy_func(
}
inst(IMPORT_NAME, (level, fromlist -- res)) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
res = import_name(tstate, frame, name, fromlist, level);
DECREF_INPUTS();
ERROR_IF(res == NULL, error);
}
inst(IMPORT_FROM, (from -- from, res)) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
res = import_from(tstate, from, name);
ERROR_IF(res == NULL, error);
}
@@ -2588,8 +2586,8 @@ dummy_func(
inst(KW_NAMES, (--)) {
assert(kwnames == NULL);
- assert(oparg < PyTuple_GET_SIZE(consts));
- kwnames = GETITEM(consts, oparg);
+ assert(oparg < PyTuple_GET_SIZE(frame->f_code->co_consts));
+ kwnames = GETITEM(frame->f_code->co_consts, oparg);
}
// Cache layout: counter/1, func_version/2, min_args/1
diff --git a/Python/ceval.c b/Python/ceval.c
index e4b03e37c37805..9d7c6e058cf8e5 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -781,18 +781,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
/* Local "register" variables.
* These are cached values from the frame and code object. */
- PyObject *names;
- PyObject *consts;
_Py_CODEUNIT *next_instr;
PyObject **stack_pointer;
/* Sets the above local variables from the frame */
#define SET_LOCALS_FROM_FRAME() \
- { \
- PyCodeObject *co = frame->f_code; \
- names = co->co_names; \
- consts = co->co_consts; \
- } \
assert(_PyInterpreterFrame_LASTI(frame) >= -1); \
/* Jump back to the last instruction executed... */ \
next_instr = frame->prev_instr + 1; \
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 30549b63597876..e9af7acafd9a47 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -75,7 +75,7 @@
TARGET(LOAD_CONST) {
PREDICTED(LOAD_CONST);
PyObject *value;
- value = GETITEM(consts, oparg);
+ value = GETITEM(frame->f_code->co_consts, oparg);
Py_INCREF(value);
STACK_GROW(1);
stack_pointer[-1] = value;
@@ -149,7 +149,7 @@
oparg = (next_instr++)->op.arg;
{
PyObject *value;
- value = GETITEM(consts, oparg);
+ value = GETITEM(frame->f_code->co_consts, oparg);
Py_INCREF(value);
_tmp_1 = value;
}
@@ -198,7 +198,7 @@
PyObject *_tmp_2;
{
PyObject *value;
- value = GETITEM(consts, oparg);
+ value = GETITEM(frame->f_code->co_consts, oparg);
Py_INCREF(value);
_tmp_2 = value;
}
@@ -871,7 +871,7 @@
}
TARGET(RETURN_CONST) {
- PyObject *retval = GETITEM(consts, oparg);
+ PyObject *retval = GETITEM(frame->f_code->co_consts, oparg);
Py_INCREF(retval);
assert(EMPTY());
_PyFrame_SetStackPointer(frame, stack_pointer);
@@ -1210,7 +1210,7 @@
TARGET(STORE_NAME) {
PyObject *v = stack_pointer[-1];
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
PyObject *ns = LOCALS();
int err;
if (ns == NULL) {
@@ -1230,7 +1230,7 @@
}
TARGET(DELETE_NAME) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
PyObject *ns = LOCALS();
int err;
if (ns == NULL) {
@@ -1344,7 +1344,7 @@
#if ENABLE_SPECIALIZATION
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
assert(cframe.use_tracing == 0);
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
next_instr--;
_Py_Specialize_StoreAttr(owner, next_instr, name);
DISPATCH_SAME_OPARG();
@@ -1355,7 +1355,7 @@
#else
(void)counter; // Unused.
#endif /* ENABLE_SPECIALIZATION */
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err = PyObject_SetAttr(owner, name, v);
Py_DECREF(v);
Py_DECREF(owner);
@@ -1367,7 +1367,7 @@
TARGET(DELETE_ATTR) {
PyObject *owner = stack_pointer[-1];
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
Py_DECREF(owner);
if (err) goto pop_1_error;
@@ -1377,7 +1377,7 @@
TARGET(STORE_GLOBAL) {
PyObject *v = stack_pointer[-1];
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err = PyDict_SetItem(GLOBALS(), name, v);
Py_DECREF(v);
if (err) goto pop_1_error;
@@ -1386,7 +1386,7 @@
}
TARGET(DELETE_GLOBAL) {
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err;
err = PyDict_DelItem(GLOBALS(), name);
// Can't use ERROR_IF here.
@@ -1402,7 +1402,7 @@
TARGET(LOAD_NAME) {
PyObject *v;
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
PyObject *locals = LOCALS();
if (locals == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
@@ -1474,7 +1474,7 @@
_PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
- PyObject *name = GETITEM(names, oparg>>1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg>>1);
next_instr--;
_Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name);
DISPATCH_SAME_OPARG();
@@ -1482,7 +1482,7 @@
STAT_INC(LOAD_GLOBAL, deferred);
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
#endif /* ENABLE_SPECIALIZATION */
- PyObject *name = GETITEM(names, oparg>>1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg>>1);
if (PyDict_CheckExact(GLOBALS())
&& PyDict_CheckExact(BUILTINS()))
{
@@ -1924,7 +1924,7 @@
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
- PyObject *name = GETITEM(names, oparg>>1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg>>1);
next_instr--;
_Py_Specialize_LoadAttr(owner, next_instr, name);
DISPATCH_SAME_OPARG();
@@ -1932,7 +1932,7 @@
STAT_INC(LOAD_ATTR, deferred);
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
#endif /* ENABLE_SPECIALIZATION */
- PyObject *name = GETITEM(names, oparg >> 1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 1);
if (oparg & 1) {
/* Designed to work in tandem with CALL, pushes two values. */
PyObject* meth = NULL;
@@ -2043,7 +2043,7 @@
PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
DEOPT_IF(dict == NULL, LOAD_ATTR);
assert(PyDict_CheckExact((PyObject *)dict));
- PyObject *name = GETITEM(names, oparg>>1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg>>1);
uint16_t hint = index;
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR);
if (DK_IS_UNICODE(dict->ma_keys)) {
@@ -2167,7 +2167,7 @@
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR);
STAT_INC(LOAD_ATTR, hit);
- PyObject *name = GETITEM(names, oparg >> 1);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 1);
Py_INCREF(f);
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2);
// Manipulate stack directly because we exit with DISPATCH_INLINED().
@@ -2223,7 +2223,7 @@
PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv);
DEOPT_IF(dict == NULL, STORE_ATTR);
assert(PyDict_CheckExact((PyObject *)dict));
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR);
PyObject *old_value;
uint64_t new_version;
@@ -2476,7 +2476,7 @@
PyObject *fromlist = stack_pointer[-1];
PyObject *level = stack_pointer[-2];
PyObject *res;
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
res = import_name(tstate, frame, name, fromlist, level);
Py_DECREF(level);
Py_DECREF(fromlist);
@@ -2489,7 +2489,7 @@
TARGET(IMPORT_FROM) {
PyObject *from = stack_pointer[-1];
PyObject *res;
- PyObject *name = GETITEM(names, oparg);
+ PyObject *name = GETITEM(frame->f_code->co_names, oparg);
res = import_from(tstate, from, name);
if (res == NULL) goto error;
STACK_GROW(1);
@@ -3304,8 +3304,8 @@
TARGET(KW_NAMES) {
assert(kwnames == NULL);
- assert(oparg < PyTuple_GET_SIZE(consts));
- kwnames = GETITEM(consts, oparg);
+ assert(oparg < PyTuple_GET_SIZE(frame->f_code->co_consts));
+ kwnames = GETITEM(frame->f_code->co_consts, oparg);
DISPATCH();
}
From 58140884b0e9c627196b199ae1c81e4316b1890e Mon Sep 17 00:00:00 2001
From: chgnrdv <52372310+chgnrdv@users.noreply.github.com>
Date: Mon, 13 Mar 2023 22:25:17 +0300
Subject: [PATCH 088/280] gh-102650: Remove duplicate include directives from
multiple source files (#102651)
Remove duplicate include directives from multiple source files
---
Mac/Tools/pythonw.c | 1 -
Modules/_hashopenssl.c | 3 +--
Modules/arraymodule.c | 1 -
Modules/signalmodule.c | 4 +---
Programs/_testembed.c | 1 -
5 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/Mac/Tools/pythonw.c b/Mac/Tools/pythonw.c
index 78813e818e7dac..9dfb77f6ff41c3 100644
--- a/Mac/Tools/pythonw.c
+++ b/Mac/Tools/pythonw.c
@@ -27,7 +27,6 @@
#include
#include
#include
-#include
#include
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index 82398547f9b372..ee8c588020118c 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -32,12 +32,11 @@
/* EVP is the preferred interface to hashing in OpenSSL */
#include
#include
-#include
+#include // FIPS_mode()
/* We use the object interface to discover what hashes OpenSSL supports. */
#include
#include
-#include // FIPS_mode()
#ifndef OPENSSL_THREADS
# error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL"
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c
index 114c69a033593c..798a7629257966 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -13,7 +13,6 @@
#include "pycore_bytesobject.h" // _PyBytes_Repeat
#include "structmember.h" // PyMemberDef
#include // offsetof()
-#include
/*[clinic input]
module array
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
index 0e472e1ee4f9dd..fdd1450050fa1b 100644
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -13,7 +13,7 @@
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_pyerrors.h" // _PyErr_SetString()
#include "pycore_pystate.h" // _PyThreadState_GET()
-#include "pycore_signal.h"
+#include "pycore_signal.h" // Py_NSIG
#ifndef MS_WINDOWS
# include "posixmodule.h"
@@ -28,8 +28,6 @@
# endif
#endif
-#include "pycore_signal.h" // Py_NSIG
-
#ifdef HAVE_SIGNAL_H
# include
#endif
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index a6ce3f7b200550..00717114b40286 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -9,7 +9,6 @@
#include "pycore_initconfig.h" // _PyConfig_InitCompatConfig()
#include "pycore_runtime.h" // _PyRuntime
#include "pycore_import.h" // _PyImport_FrozenBootstrap
-#include
#include
#include
#include // putenv()
From c54eeaf03633e168dcfbdb7a9e8f2424603e6e43 Mon Sep 17 00:00:00 2001
From: T
Date: Tue, 14 Mar 2023 04:46:35 +0800
Subject: [PATCH 089/280] gh-98169 dataclasses.astuple support DefaultDict
(#98170)
Co-authored-by: Pieter Eendebak
---
Lib/dataclasses.py | 25 ++++++++++++-------
Lib/test/test_dataclasses.py | 21 +++++++++++++---
...2-10-10-19-14-51.gh-issue-98169.DBWIxL.rst | 2 ++
3 files changed, 35 insertions(+), 13 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2022-10-10-19-14-51.gh-issue-98169.DBWIxL.rst
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index f4617b1dbdac66..7c3285cf440a39 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -1321,15 +1321,14 @@ def _asdict_inner(obj, dict_factory):
# generator (which is not true for namedtuples, handled
# above).
return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
- elif isinstance(obj, dict) and hasattr(type(obj), 'default_factory'):
- # obj is a defaultdict, which has a different constructor from
- # dict as it requires the default_factory as its first arg.
- # https://bugs.python.org/issue35540
- result = type(obj)(getattr(obj, 'default_factory'))
- for k, v in obj.items():
- result[_asdict_inner(k, dict_factory)] = _asdict_inner(v, dict_factory)
- return result
elif isinstance(obj, dict):
+ if hasattr(type(obj), 'default_factory'):
+ # obj is a defaultdict, which has a different constructor from
+ # dict as it requires the default_factory as its first arg.
+ result = type(obj)(getattr(obj, 'default_factory'))
+ for k, v in obj.items():
+ result[_asdict_inner(k, dict_factory)] = _asdict_inner(v, dict_factory)
+ return result
return type(obj)((_asdict_inner(k, dict_factory),
_asdict_inner(v, dict_factory))
for k, v in obj.items())
@@ -1382,7 +1381,15 @@ def _astuple_inner(obj, tuple_factory):
# above).
return type(obj)(_astuple_inner(v, tuple_factory) for v in obj)
elif isinstance(obj, dict):
- return type(obj)((_astuple_inner(k, tuple_factory), _astuple_inner(v, tuple_factory))
+ obj_type = type(obj)
+ if hasattr(obj_type, 'default_factory'):
+ # obj is a defaultdict, which has a different constructor from
+ # dict as it requires the default_factory as its first arg.
+ result = obj_type(getattr(obj, 'default_factory'))
+ for k, v in obj.items():
+ result[_astuple_inner(k, tuple_factory)] = _astuple_inner(v, tuple_factory)
+ return result
+ return obj_type((_astuple_inner(k, tuple_factory), _astuple_inner(v, tuple_factory))
for k, v in obj.items())
else:
return copy.deepcopy(obj)
diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py
index 76bed0c3314673..46d4e0fedad2f2 100644
--- a/Lib/test/test_dataclasses.py
+++ b/Lib/test/test_dataclasses.py
@@ -1706,19 +1706,17 @@ class C:
def test_helper_asdict_defaultdict(self):
# Ensure asdict() does not throw exceptions when a
# defaultdict is a member of a dataclass
-
@dataclass
class C:
mp: DefaultDict[str, List]
-
dd = defaultdict(list)
dd["x"].append(12)
c = C(mp=dd)
d = asdict(c)
- assert d == {"mp": {"x": [12]}}
- assert d["mp"] is not c.mp # make sure defaultdict is copied
+ self.assertEqual(d, {"mp": {"x": [12]}})
+ self.assertTrue(d["mp"] is not c.mp) # make sure defaultdict is copied
def test_helper_astuple(self):
# Basic tests for astuple(), it should return a new tuple.
@@ -1847,6 +1845,21 @@ class C:
t = astuple(c, tuple_factory=list)
self.assertEqual(t, ['outer', T(1, ['inner', T(11, 12, 13)], 2)])
+ def test_helper_astuple_defaultdict(self):
+ # Ensure astuple() does not throw exceptions when a
+ # defaultdict is a member of a dataclass
+ @dataclass
+ class C:
+ mp: DefaultDict[str, List]
+
+ dd = defaultdict(list)
+ dd["x"].append(12)
+ c = C(mp=dd)
+ t = astuple(c)
+
+ self.assertEqual(t, ({"x": [12]},))
+ self.assertTrue(t[0] is not dd) # make sure defaultdict is copied
+
def test_dynamic_class_creation(self):
cls_dict = {'__annotations__': {'x': int, 'y': int},
}
diff --git a/Misc/NEWS.d/next/Library/2022-10-10-19-14-51.gh-issue-98169.DBWIxL.rst b/Misc/NEWS.d/next/Library/2022-10-10-19-14-51.gh-issue-98169.DBWIxL.rst
new file mode 100644
index 00000000000000..24c3aeecc83f18
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-10-10-19-14-51.gh-issue-98169.DBWIxL.rst
@@ -0,0 +1,2 @@
+Fix :func:`dataclasses.astuple` crash when :class:`collections.defaultdict`
+is present in the attributes.
From 98b34ba69d383857943aa6fc57cd5cef05aa2590 Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Tue, 14 Mar 2023 00:42:05 +0300
Subject: [PATCH 090/280] gh-102069: Fix `__weakref__` descriptor generation
for custom dataclasses (#102075)
---
Lib/dataclasses.py | 3 +++
Lib/test/test_dataclasses.py | 15 +++++++++++----
...2023-02-20-16-47-56.gh-issue-102069.FS7f1j.rst | 1 +
3 files changed, 15 insertions(+), 4 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-02-20-16-47-56.gh-issue-102069.FS7f1j.rst
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 7c3285cf440a39..82b08fc017884f 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -1189,6 +1189,9 @@ def _add_slots(cls, is_frozen, weakref_slot):
# Remove __dict__ itself.
cls_dict.pop('__dict__', None)
+ # Clear existing `__weakref__` descriptor, it belongs to a previous type:
+ cls_dict.pop('__weakref__', None) # gh-102069
+
# And finally create the class.
qualname = getattr(cls, '__qualname__', None)
cls = type(cls)(cls.__name__, cls.__bases__, cls_dict)
diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py
index 46d4e0fedad2f2..46f33043c27071 100644
--- a/Lib/test/test_dataclasses.py
+++ b/Lib/test/test_dataclasses.py
@@ -3175,6 +3175,8 @@ class A:
with self.assertRaisesRegex(TypeError,
"cannot create weak reference"):
weakref.ref(a)
+ with self.assertRaises(AttributeError):
+ a.__weakref__
def test_slots_weakref(self):
@dataclass(slots=True, weakref_slot=True)
@@ -3183,7 +3185,9 @@ class A:
self.assertIn("__weakref__", A.__slots__)
a = A(1)
- weakref.ref(a)
+ a_ref = weakref.ref(a)
+
+ self.assertIs(a.__weakref__, a_ref)
def test_slots_weakref_base_str(self):
class Base:
@@ -3249,7 +3253,8 @@ class A(Base):
self.assertIn("__weakref__", Base.__slots__)
self.assertNotIn("__weakref__", A.__slots__)
a = A(1)
- weakref.ref(a)
+ a_ref = weakref.ref(a)
+ self.assertIs(a.__weakref__, a_ref)
def test_weakref_slot_subclass_no_weakref_slot(self):
@dataclass(slots=True, weakref_slot=True)
@@ -3265,7 +3270,8 @@ class A(Base):
self.assertIn("__weakref__", Base.__slots__)
self.assertNotIn("__weakref__", A.__slots__)
a = A(1)
- weakref.ref(a)
+ a_ref = weakref.ref(a)
+ self.assertIs(a.__weakref__, a_ref)
def test_weakref_slot_normal_base_weakref_slot(self):
class Base:
@@ -3280,7 +3286,8 @@ class A(Base):
self.assertIn("__weakref__", Base.__slots__)
self.assertNotIn("__weakref__", A.__slots__)
a = A(1)
- weakref.ref(a)
+ a_ref = weakref.ref(a)
+ self.assertIs(a.__weakref__, a_ref)
class TestDescriptors(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Library/2023-02-20-16-47-56.gh-issue-102069.FS7f1j.rst b/Misc/NEWS.d/next/Library/2023-02-20-16-47-56.gh-issue-102069.FS7f1j.rst
new file mode 100644
index 00000000000000..04c87e515cca93
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-02-20-16-47-56.gh-issue-102069.FS7f1j.rst
@@ -0,0 +1 @@
+Fix ``__weakref__`` descriptor generation for custom dataclasses.
From 762e0245162157452d5095451fad56a23d763163 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Mon, 13 Mar 2023 15:50:16 -0600
Subject: [PATCH 091/280] gh-101524: Fix the ChannelID tp_name (gh-102655)
https://github.com/python/cpython/issues/101524
---
Modules/_xxinterpchannelsmodule.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Modules/_xxinterpchannelsmodule.c b/Modules/_xxinterpchannelsmodule.c
index a0cd4a2363fb53..fead12c963da26 100644
--- a/Modules/_xxinterpchannelsmodule.c
+++ b/Modules/_xxinterpchannelsmodule.c
@@ -1806,7 +1806,7 @@ static PyType_Slot ChannelIDType_slots[] = {
};
static PyType_Spec ChannelIDType_spec = {
- .name = "_xxsubinterpreters.ChannelID",
+ .name = MODULE_NAME ".ChannelID",
.basicsize = sizeof(channelid),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_IMMUTABLETYPE),
From 75153c13d0cabbfa75e70ab594e66146ccad9b6e Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Mon, 13 Mar 2023 16:01:44 -0600
Subject: [PATCH 092/280] gh-101659: Avoid Allocation for Shared Exceptions in
the _xxsubinterpreters Module (gh-102659)
https://github.com/python/cpython/issues/101659
---
Modules/_xxsubinterpretersmodule.c | 123 ++++++++++++-----------------
1 file changed, 49 insertions(+), 74 deletions(-)
diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c
index 79dbe3474ba9e8..76fb87fa3a34e1 100644
--- a/Modules/_xxsubinterpretersmodule.c
+++ b/Modules/_xxsubinterpretersmodule.c
@@ -15,14 +15,14 @@
#define MODULE_NAME "_xxsubinterpreters"
-static char *
+static const char *
_copy_raw_string(PyObject *strobj)
{
const char *str = PyUnicode_AsUTF8(strobj);
if (str == NULL) {
return NULL;
}
- char *copied = PyMem_Malloc(strlen(str)+1);
+ char *copied = PyMem_RawMalloc(strlen(str)+1);
if (copied == NULL) {
PyErr_NoMemory();
return NULL;
@@ -128,7 +128,7 @@ clear_module_state(module_state *state)
/* data-sharing-specific code ***********************************************/
struct _sharednsitem {
- char *name;
+ const char *name;
_PyCrossInterpreterData data;
};
@@ -152,7 +152,7 @@ static void
_sharednsitem_clear(struct _sharednsitem *item)
{
if (item->name != NULL) {
- PyMem_Free(item->name);
+ PyMem_RawFree((void *)item->name);
item->name = NULL;
}
(void)_release_xid_data(&item->data, 1);
@@ -258,96 +258,74 @@ _sharedns_apply(_sharedns *shared, PyObject *ns)
// of the exception in the calling interpreter.
typedef struct _sharedexception {
- char *name;
- char *msg;
+ const char *name;
+ const char *msg;
} _sharedexception;
-static _sharedexception *
-_sharedexception_new(void)
-{
- _sharedexception *err = PyMem_NEW(_sharedexception, 1);
- if (err == NULL) {
- PyErr_NoMemory();
- return NULL;
- }
- err->name = NULL;
- err->msg = NULL;
- return err;
-}
+static const struct _sharedexception no_exception = {
+ .name = NULL,
+ .msg = NULL,
+};
static void
_sharedexception_clear(_sharedexception *exc)
{
if (exc->name != NULL) {
- PyMem_Free(exc->name);
+ PyMem_RawFree((void *)exc->name);
}
if (exc->msg != NULL) {
- PyMem_Free(exc->msg);
+ PyMem_RawFree((void *)exc->msg);
}
}
-static void
-_sharedexception_free(_sharedexception *exc)
-{
- _sharedexception_clear(exc);
- PyMem_Free(exc);
-}
-
-static _sharedexception *
-_sharedexception_bind(PyObject *exc)
+static const char *
+_sharedexception_bind(PyObject *exc, _sharedexception *sharedexc)
{
assert(exc != NULL);
- char *failure = NULL;
-
- _sharedexception *err = _sharedexception_new();
- if (err == NULL) {
- goto finally;
- }
+ const char *failure = NULL;
- PyObject *name = PyUnicode_FromFormat("%S", Py_TYPE(exc));
- if (name == NULL) {
+ PyObject *nameobj = PyUnicode_FromFormat("%S", Py_TYPE(exc));
+ if (nameobj == NULL) {
failure = "unable to format exception type name";
- goto finally;
+ goto error;
}
- err->name = _copy_raw_string(name);
- Py_DECREF(name);
- if (err->name == NULL) {
+ sharedexc->name = _copy_raw_string(nameobj);
+ Py_DECREF(nameobj);
+ if (sharedexc->name == NULL) {
if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
failure = "out of memory copying exception type name";
} else {
failure = "unable to encode and copy exception type name";
}
- goto finally;
+ goto error;
}
if (exc != NULL) {
- PyObject *msg = PyUnicode_FromFormat("%S", exc);
- if (msg == NULL) {
+ PyObject *msgobj = PyUnicode_FromFormat("%S", exc);
+ if (msgobj == NULL) {
failure = "unable to format exception message";
- goto finally;
+ goto error;
}
- err->msg = _copy_raw_string(msg);
- Py_DECREF(msg);
- if (err->msg == NULL) {
+ sharedexc->msg = _copy_raw_string(msgobj);
+ Py_DECREF(msgobj);
+ if (sharedexc->msg == NULL) {
if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
failure = "out of memory copying exception message";
} else {
failure = "unable to encode and copy exception message";
}
- goto finally;
+ goto error;
}
}
-finally:
- if (failure != NULL) {
- PyErr_Clear();
- if (err->name != NULL) {
- PyMem_Free(err->name);
- err->name = NULL;
- }
- err->msg = failure;
- }
- return err;
+ return NULL;
+
+error:
+ assert(failure != NULL);
+ PyErr_Clear();
+ _sharedexception_clear(sharedexc);
+ *sharedexc = no_exception;
+ return failure;
}
static void
@@ -430,7 +408,7 @@ _ensure_not_running(PyInterpreterState *interp)
static int
_run_script(PyInterpreterState *interp, const char *codestr,
- _sharedns *shared, _sharedexception **exc)
+ _sharedns *shared, _sharedexception *sharedexc)
{
PyObject *excval = NULL;
PyObject *main_mod = _PyInterpreterState_GetMainModule(interp);
@@ -462,22 +440,20 @@ _run_script(PyInterpreterState *interp, const char *codestr,
Py_DECREF(result); // We throw away the result.
}
- *exc = NULL;
+ *sharedexc = no_exception;
return 0;
error:
excval = PyErr_GetRaisedException();
- _sharedexception *sharedexc = _sharedexception_bind(excval);
- Py_XDECREF(excval);
- if (sharedexc == NULL) {
- fprintf(stderr, "RunFailedError: script raised an uncaught exception");
+ const char *failure = _sharedexception_bind(excval, sharedexc);
+ if (failure != NULL) {
+ fprintf(stderr,
+ "RunFailedError: script raised an uncaught exception (%s)",
+ failure);
PyErr_Clear();
- sharedexc = NULL;
- }
- else {
- assert(!PyErr_Occurred());
}
- *exc = sharedexc;
+ Py_XDECREF(excval);
+ assert(!PyErr_Occurred());
return -1;
}
@@ -505,7 +481,7 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp,
}
// Run the script.
- _sharedexception *exc = NULL;
+ _sharedexception exc;
int result = _run_script(interp, codestr, shared, &exc);
// Switch back.
@@ -514,10 +490,9 @@ _run_script_in_interpreter(PyObject *mod, PyInterpreterState *interp,
}
// Propagate any exception out to the caller.
- if (exc != NULL) {
+ if (exc.name != NULL) {
assert(state != NULL);
- _sharedexception_apply(exc, state->RunFailedError);
- _sharedexception_free(exc);
+ _sharedexception_apply(&exc, state->RunFailedError);
}
else if (result != 0) {
// We were unable to allocate a shared exception.
From b99132da13c2f7838877a1677b385838a0e86420 Mon Sep 17 00:00:00 2001
From: Guido van Rossum
Date: Mon, 13 Mar 2023 15:08:45 -0700
Subject: [PATCH 093/280] gh-98831: Use DECREF_INPUTS() more (#102409)
---
Python/bytecodes.c | 80 +++++++++++++++-----------------------
Python/generated_cases.c.h | 15 ++++---
2 files changed, 38 insertions(+), 57 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 204f702588fa5c..4cac5857a2cf42 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -457,8 +457,7 @@ dummy_func(
if (!_PyErr_Occurred(tstate)) {
_PyErr_SetKeyError(sub);
}
- Py_DECREF(dict);
- Py_DECREF(sub);
+ DECREF_INPUTS();
ERROR_IF(true, error);
}
Py_INCREF(res); // Do this before DECREF'ing dict, sub
@@ -493,7 +492,7 @@ dummy_func(
inst(SET_ADD, (set, unused[oparg-1], v -- set, unused[oparg-1])) {
int err = PySet_Add(set, v);
- Py_DECREF(v);
+ DECREF_INPUTS();
ERROR_IF(err, error);
PREDICT(JUMP_BACKWARD);
}
@@ -972,7 +971,7 @@ dummy_func(
#endif /* ENABLE_SPECIALIZATION */
PyObject **top = stack_pointer + oparg - 1;
int res = unpack_iterable(tstate, seq, oparg, -1, top);
- Py_DECREF(seq);
+ DECREF_INPUTS();
ERROR_IF(res == 0, error);
}
@@ -983,7 +982,7 @@ dummy_func(
STAT_INC(UNPACK_SEQUENCE, hit);
values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1));
values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0));
- Py_DECREF(seq);
+ DECREF_INPUTS();
}
inst(UNPACK_SEQUENCE_TUPLE, (unused/1, seq -- values[oparg])) {
@@ -994,7 +993,7 @@ dummy_func(
for (int i = oparg; --i >= 0; ) {
*values++ = Py_NewRef(items[i]);
}
- Py_DECREF(seq);
+ DECREF_INPUTS();
}
inst(UNPACK_SEQUENCE_LIST, (unused/1, seq -- values[oparg])) {
@@ -1005,14 +1004,14 @@ dummy_func(
for (int i = oparg; --i >= 0; ) {
*values++ = Py_NewRef(items[i]);
}
- Py_DECREF(seq);
+ DECREF_INPUTS();
}
inst(UNPACK_EX, (seq -- unused[oparg & 0xFF], unused, unused[oparg >> 8])) {
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
PyObject **top = stack_pointer + totalargs - 1;
int res = unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top);
- Py_DECREF(seq);
+ DECREF_INPUTS();
ERROR_IF(res == 0, error);
}
@@ -1040,22 +1039,21 @@ dummy_func(
#endif /* ENABLE_SPECIALIZATION */
PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err = PyObject_SetAttr(owner, name, v);
- Py_DECREF(v);
- Py_DECREF(owner);
+ DECREF_INPUTS();
ERROR_IF(err, error);
}
inst(DELETE_ATTR, (owner --)) {
PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
- Py_DECREF(owner);
+ DECREF_INPUTS();
ERROR_IF(err, error);
}
inst(STORE_GLOBAL, (v --)) {
PyObject *name = GETITEM(frame->f_code->co_names, oparg);
int err = PyDict_SetItem(GLOBALS(), name, v);
- Py_DECREF(v);
+ DECREF_INPUTS();
ERROR_IF(err, error);
}
@@ -1322,9 +1320,7 @@ dummy_func(
inst(BUILD_STRING, (pieces[oparg] -- str: PyUnicode_Type)) {
str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg);
- for (int i = 0; i < oparg; i++) {
- Py_DECREF(pieces[i]);
- }
+ DECREF_INPUTS();
ERROR_IF(str == NULL, error);
}
@@ -1387,10 +1383,7 @@ dummy_func(
if (map == NULL)
goto error;
- for (int i = 0; i < oparg; i++) {
- Py_DECREF(values[i*2]);
- Py_DECREF(values[i*2+1]);
- }
+ DECREF_INPUTS();
ERROR_IF(map == NULL, error);
}
@@ -1446,10 +1439,7 @@ dummy_func(
map = _PyDict_FromItems(
&PyTuple_GET_ITEM(keys, 0), 1,
values, 1, oparg);
- Py_DECREF(keys);
- for (int i = 0; i < oparg; i++) {
- Py_DECREF(values[i]);
- }
+ DECREF_INPUTS();
ERROR_IF(map == NULL, error);
}
@@ -1537,7 +1527,7 @@ dummy_func(
NULL | meth | arg1 | ... | argN
*/
- Py_DECREF(owner);
+ DECREF_INPUTS();
ERROR_IF(meth == NULL, error);
res2 = NULL;
res = meth;
@@ -1546,7 +1536,7 @@ dummy_func(
else {
/* Classic, pushes one value. */
res = PyObject_GetAttr(owner, name);
- Py_DECREF(owner);
+ DECREF_INPUTS();
ERROR_IF(res == NULL, error);
}
}
@@ -1565,7 +1555,7 @@ dummy_func(
STAT_INC(LOAD_ATTR, hit);
Py_INCREF(res);
res2 = NULL;
- Py_DECREF(owner);
+ DECREF_INPUTS();
}
inst(LOAD_ATTR_MODULE, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) {
@@ -1582,7 +1572,7 @@ dummy_func(
STAT_INC(LOAD_ATTR, hit);
Py_INCREF(res);
res2 = NULL;
- Py_DECREF(owner);
+ DECREF_INPUTS();
}
inst(LOAD_ATTR_WITH_HINT, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) {
@@ -1613,7 +1603,7 @@ dummy_func(
STAT_INC(LOAD_ATTR, hit);
Py_INCREF(res);
res2 = NULL;
- Py_DECREF(owner);
+ DECREF_INPUTS();
}
inst(LOAD_ATTR_SLOT, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) {
@@ -1627,7 +1617,7 @@ dummy_func(
STAT_INC(LOAD_ATTR, hit);
Py_INCREF(res);
res2 = NULL;
- Py_DECREF(owner);
+ DECREF_INPUTS();
}
inst(LOAD_ATTR_CLASS, (unused/1, type_version/2, unused/2, descr/4, cls -- res2 if (oparg & 1), res)) {
@@ -1643,7 +1633,7 @@ dummy_func(
res = descr;
assert(res != NULL);
Py_INCREF(res);
- Py_DECREF(cls);
+ DECREF_INPUTS();
}
inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused if (oparg & 1), unused)) {
@@ -1780,8 +1770,7 @@ dummy_func(
STAT_INC(COMPARE_OP, deferred);
assert((oparg >> 4) <= Py_GE);
res = PyObject_RichCompare(left, right, oparg>>4);
- Py_DECREF(left);
- Py_DECREF(right);
+ DECREF_INPUTS();
ERROR_IF(res == NULL, error);
}
@@ -1807,8 +1796,7 @@ dummy_func(
#endif /* ENABLE_SPECIALIZATION */
assert((oparg >> 4) <= Py_GE);
PyObject *cond = PyObject_RichCompare(left, right, oparg>>4);
- Py_DECREF(left);
- Py_DECREF(right);
+ DECREF_INPUTS();
ERROR_IF(cond == NULL, error);
assert(next_instr[1].op.code == POP_JUMP_IF_FALSE ||
next_instr[1].op.code == POP_JUMP_IF_TRUE);
@@ -1963,7 +1951,7 @@ dummy_func(
}
else {
int err = PyObject_IsTrue(cond);
- Py_DECREF(cond);
+ DECREF_INPUTS();
if (err == 0) {
JUMPBY(oparg);
}
@@ -2004,7 +1992,7 @@ dummy_func(
}
else {
int err = PyObject_IsTrue(cond);
- Py_DECREF(cond);
+ DECREF_INPUTS();
if (err > 0) {
JUMPBY(oparg);
}
@@ -2038,7 +2026,7 @@ dummy_func(
inst(POP_JUMP_IF_NOT_NONE, (value -- )) {
if (!Py_IsNone(value)) {
- Py_DECREF(value);
+ DECREF_INPUTS();
JUMPBY(oparg);
}
else {
@@ -2063,7 +2051,7 @@ dummy_func(
JUMPBY(oparg);
}
else {
- Py_DECREF(value);
+ DECREF_INPUTS();
}
}
@@ -2270,7 +2258,7 @@ dummy_func(
if (iter == NULL) {
goto error;
}
- Py_DECREF(iterable);
+ DECREF_INPUTS();
}
PREDICT(LOAD_CONST);
}
@@ -3168,9 +3156,7 @@ dummy_func(
assert(PyTuple_CheckExact(callargs));
result = do_call_core(tstate, func, callargs, kwargs, cframe.use_tracing);
- Py_DECREF(func);
- Py_DECREF(callargs);
- Py_XDECREF(kwargs);
+ DECREF_INPUTS();
assert(PEEK(3 + (oparg & 1)) == NULL);
ERROR_IF(result == NULL, error);
@@ -3237,9 +3223,7 @@ dummy_func(
inst(BUILD_SLICE, (start, stop, step if (oparg == 3) -- slice)) {
slice = PySlice_New(start, stop, step);
- Py_DECREF(start);
- Py_DECREF(stop);
- Py_XDECREF(step);
+ DECREF_INPUTS();
ERROR_IF(slice == NULL, error);
}
@@ -3285,8 +3269,7 @@ dummy_func(
} else {
/* Actually call format(). */
result = PyObject_Format(value, fmt_spec);
- Py_DECREF(value);
- Py_XDECREF(fmt_spec);
+ DECREF_INPUTS();
ERROR_IF(result == NULL, error);
}
}
@@ -3312,8 +3295,7 @@ dummy_func(
assert((unsigned)oparg < Py_ARRAY_LENGTH(binary_ops));
assert(binary_ops[oparg]);
res = binary_ops[oparg](lhs, rhs);
- Py_DECREF(lhs);
- Py_DECREF(rhs);
+ DECREF_INPUTS();
ERROR_IF(res == NULL, error);
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index e9af7acafd9a47..2f5726c317eb1b 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -1695,8 +1695,8 @@
PyObject **pieces = (stack_pointer - oparg);
PyObject *str;
str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg);
- for (int i = 0; i < oparg; i++) {
- Py_DECREF(pieces[i]);
+ for (int _i = oparg; --_i >= 0;) {
+ Py_DECREF(pieces[_i]);
}
if (str == NULL) { STACK_SHRINK(oparg); goto error; }
STACK_SHRINK(oparg);
@@ -1792,9 +1792,8 @@
if (map == NULL)
goto error;
- for (int i = 0; i < oparg; i++) {
- Py_DECREF(values[i*2]);
- Py_DECREF(values[i*2+1]);
+ for (int _i = oparg*2; --_i >= 0;) {
+ Py_DECREF(values[_i]);
}
if (map == NULL) { STACK_SHRINK(oparg*2); goto error; }
STACK_SHRINK(oparg*2);
@@ -1859,10 +1858,10 @@
map = _PyDict_FromItems(
&PyTuple_GET_ITEM(keys, 0), 1,
values, 1, oparg);
- Py_DECREF(keys);
- for (int i = 0; i < oparg; i++) {
- Py_DECREF(values[i]);
+ for (int _i = oparg; --_i >= 0;) {
+ Py_DECREF(values[_i]);
}
+ Py_DECREF(keys);
if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; }
STACK_SHRINK(oparg);
stack_pointer[-1] = map;
From e2be4e40f4fbea9948601d49b041ad83dbd62c75 Mon Sep 17 00:00:00 2001
From: Blind4Basics <32236948+Blind4Basics@users.noreply.github.com>
Date: Mon, 13 Mar 2023 23:35:37 +0100
Subject: [PATCH 094/280] gh-102627: Replace address pointing toward malicious
web page (#102630)
* Replace known bad address pointing toward a malicious web page.
Co-authored-by: C.A.M. Gerlach
Co-authored-by: Hugo van Kemenade
---
Doc/library/concurrent.futures.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst
index c543c849585b7f..09c9fc4e6e227a 100644
--- a/Doc/library/concurrent.futures.rst
+++ b/Doc/library/concurrent.futures.rst
@@ -202,7 +202,7 @@ ThreadPoolExecutor Example
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
- 'http://some-made-up-domain.com/']
+ 'http://nonexistant-subdomain.python.org/']
# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
From 5b8c3cbb5dfab98118112582a7a53f4c2eecc370 Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Mon, 13 Mar 2023 20:06:43 -0500
Subject: [PATCH 095/280] GH-102670: Use sumprod() to simplify, speed up, and
improve accuracy of statistics functions (GH-102649)
---
Lib/statistics.py | 26 ++++++++++---------
Lib/test/test_statistics.py | 12 ++++++++-
...-03-13-18-27-00.gh-issue-102670.GyoThv.rst | 2 ++
3 files changed, 27 insertions(+), 13 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-13-18-27-00.gh-issue-102670.GyoThv.rst
diff --git a/Lib/statistics.py b/Lib/statistics.py
index 7d5d750193a5ab..6bd214bbfe2ff5 100644
--- a/Lib/statistics.py
+++ b/Lib/statistics.py
@@ -1036,7 +1036,7 @@ def covariance(x, y, /):
raise StatisticsError('covariance requires at least two data points')
xbar = fsum(x) / n
ybar = fsum(y) / n
- sxy = fsum((xi - xbar) * (yi - ybar) for xi, yi in zip(x, y))
+ sxy = sumprod((xi - xbar for xi in x), (yi - ybar for yi in y))
return sxy / (n - 1)
@@ -1074,11 +1074,14 @@ def correlation(x, y, /, *, method='linear'):
start = (n - 1) / -2 # Center rankings around zero
x = _rank(x, start=start)
y = _rank(y, start=start)
- xbar = fsum(x) / n
- ybar = fsum(y) / n
- sxy = fsum((xi - xbar) * (yi - ybar) for xi, yi in zip(x, y))
- sxx = fsum((d := xi - xbar) * d for xi in x)
- syy = fsum((d := yi - ybar) * d for yi in y)
+ else:
+ xbar = fsum(x) / n
+ ybar = fsum(y) / n
+ x = [xi - xbar for xi in x]
+ y = [yi - ybar for yi in y]
+ sxy = sumprod(x, y)
+ sxx = sumprod(x, x)
+ syy = sumprod(y, y)
try:
return sxy / sqrt(sxx * syy)
except ZeroDivisionError:
@@ -1131,14 +1134,13 @@ def linear_regression(x, y, /, *, proportional=False):
raise StatisticsError('linear regression requires that both inputs have same number of data points')
if n < 2:
raise StatisticsError('linear regression requires at least two data points')
- if proportional:
- sxy = fsum(xi * yi for xi, yi in zip(x, y))
- sxx = fsum(xi * xi for xi in x)
- else:
+ if not proportional:
xbar = fsum(x) / n
ybar = fsum(y) / n
- sxy = fsum((xi - xbar) * (yi - ybar) for xi, yi in zip(x, y))
- sxx = fsum((d := xi - xbar) * d for xi in x)
+ x = [xi - xbar for xi in x] # List because used three times below
+ y = (yi - ybar for yi in y) # Generator because only used once below
+ sxy = sumprod(x, y) + 0.0 # Add zero to coerce result to a float
+ sxx = sumprod(x, x)
try:
slope = sxy / sxx # equivalent to: covariance(x, y) / variance(x)
except ZeroDivisionError:
diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py
index 31a3cb6b53a6f2..f0fa6454b1f91a 100644
--- a/Lib/test/test_statistics.py
+++ b/Lib/test/test_statistics.py
@@ -1,4 +1,4 @@
-"""Test suite for statistics module, including helper NumericTestCase and
+x = """Test suite for statistics module, including helper NumericTestCase and
approx_equal function.
"""
@@ -2610,6 +2610,16 @@ def test_proportional(self):
self.assertAlmostEqual(slope, 20 + 1/150)
self.assertEqual(intercept, 0.0)
+ def test_float_output(self):
+ x = [Fraction(2, 3), Fraction(3, 4)]
+ y = [Fraction(4, 5), Fraction(5, 6)]
+ slope, intercept = statistics.linear_regression(x, y)
+ self.assertTrue(isinstance(slope, float))
+ self.assertTrue(isinstance(intercept, float))
+ slope, intercept = statistics.linear_regression(x, y, proportional=True)
+ self.assertTrue(isinstance(slope, float))
+ self.assertTrue(isinstance(intercept, float))
+
class TestNormalDist:
# General note on precision: The pdf(), cdf(), and overlap() methods
diff --git a/Misc/NEWS.d/next/Library/2023-03-13-18-27-00.gh-issue-102670.GyoThv.rst b/Misc/NEWS.d/next/Library/2023-03-13-18-27-00.gh-issue-102670.GyoThv.rst
new file mode 100644
index 00000000000000..3de09f86754f3e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-13-18-27-00.gh-issue-102670.GyoThv.rst
@@ -0,0 +1,2 @@
+Optimized fmean(), correlation(), covariance(), and linear_regression()
+using the new math.sumprod() function.
From 91a5bd4c0eeec9c0fa2b4797764e1c2fd0b322fa Mon Sep 17 00:00:00 2001
From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com>
Date: Mon, 13 Mar 2023 20:35:54 -0500
Subject: [PATCH 096/280] gh-102013: Add PyUnstable_GC_VisitObjects (#102014)
---
Doc/c-api/gcsupport.rst | 33 +++++++++
Include/objimpl.h | 19 +++++
...-02-18-00-55-14.gh-issue-102013.83mrtI.rst | 1 +
Modules/_testcapimodule.c | 69 +++++++++++++++++++
Modules/gcmodule.c | 24 +++++++
5 files changed, 146 insertions(+)
create mode 100644 Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst
diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst
index 8c90d1e8991c10..cb5d64a50487fe 100644
--- a/Doc/c-api/gcsupport.rst
+++ b/Doc/c-api/gcsupport.rst
@@ -228,3 +228,36 @@ garbage collection runs.
Returns the current state, 0 for disabled and 1 for enabled.
.. versionadded:: 3.10
+
+
+Querying Garbage Collector State
+--------------------------------
+
+The C-API provides the following interface for querying information about
+the garbage collector.
+
+.. c:function:: void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)
+
+ Run supplied *callback* on all live GC-capable objects. *arg* is passed through to
+ all invocations of *callback*.
+
+ .. warning::
+ If new objects are (de)allocated by the callback it is undefined if they
+ will be visited.
+
+ Garbage collection is disabled during operation. Explicitly running a collection
+ in the callback may lead to undefined behaviour e.g. visiting the same objects
+ multiple times or not at all.
+
+ .. versionadded:: 3.12
+
+.. c:type:: int (*gcvisitobjects_t)(PyObject *object, void *arg)
+
+ Type of the visitor function to be passed to :c:func:`PyUnstable_GC_VisitObjects`.
+ *arg* is the same as the *arg* passed to ``PyUnstable_GC_VisitObjects``.
+ Return ``0`` to continue iteration, return ``1`` to stop iteration. Other return
+ values are reserved for now so behavior on returning anything else is undefined.
+
+ .. versionadded:: 3.12
+
+
diff --git a/Include/objimpl.h b/Include/objimpl.h
index dde8df34835328..ef871c5ea93ebe 100644
--- a/Include/objimpl.h
+++ b/Include/objimpl.h
@@ -157,6 +157,25 @@ PyAPI_FUNC(int) PyGC_Enable(void);
PyAPI_FUNC(int) PyGC_Disable(void);
PyAPI_FUNC(int) PyGC_IsEnabled(void);
+
+#if !defined(Py_LIMITED_API)
+/* Visit all live GC-capable objects, similar to gc.get_objects(None). The
+ * supplied callback is called on every such object with the void* arg set
+ * to the supplied arg. Returning 0 from the callback ends iteration, returning
+ * 1 allows iteration to continue. Returning any other value may result in
+ * undefined behaviour.
+ *
+ * If new objects are (de)allocated by the callback it is undefined if they
+ * will be visited.
+
+ * Garbage collection is disabled during operation. Explicitly running a
+ * collection in the callback may lead to undefined behaviour e.g. visiting the
+ * same objects multiple times or not at all.
+ */
+typedef int (*gcvisitobjects_t)(PyObject*, void*);
+PyAPI_FUNC(void) PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void* arg);
+#endif
+
/* Test if a type has a GC head */
#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
diff --git a/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst b/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst
new file mode 100644
index 00000000000000..0350237ebc7390
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-02-18-00-55-14.gh-issue-102013.83mrtI.rst
@@ -0,0 +1 @@
+Add a new (unstable) C-API function for iterating over GC'able objects using a callback: ``PyUnstable_VisitObjects``.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index ea67017a1ba3b1..f45d0312e94411 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -3310,6 +3310,73 @@ function_set_kw_defaults(PyObject *self, PyObject *args)
Py_RETURN_NONE;
}
+struct gc_visit_state_basic {
+ PyObject *target;
+ int found;
+};
+
+static int
+gc_visit_callback_basic(PyObject *obj, void *arg)
+{
+ struct gc_visit_state_basic *state = (struct gc_visit_state_basic *)arg;
+ if (obj == state->target) {
+ state->found = 1;
+ return 0;
+ }
+ return 1;
+}
+
+static PyObject *
+test_gc_visit_objects_basic(PyObject *Py_UNUSED(self),
+ PyObject *Py_UNUSED(ignored))
+{
+ PyObject *obj;
+ struct gc_visit_state_basic state;
+
+ obj = PyList_New(0);
+ if (obj == NULL) {
+ return NULL;
+ }
+ state.target = obj;
+ state.found = 0;
+
+ PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state);
+ Py_DECREF(obj);
+ if (!state.found) {
+ PyErr_SetString(
+ PyExc_AssertionError,
+ "test_gc_visit_objects_basic: Didn't find live list");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static int
+gc_visit_callback_exit_early(PyObject *obj, void *arg)
+ {
+ int *visited_i = (int *)arg;
+ (*visited_i)++;
+ if (*visited_i == 2) {
+ return 0;
+ }
+ return 1;
+}
+
+static PyObject *
+test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self),
+ PyObject *Py_UNUSED(ignored))
+{
+ int visited_i = 0;
+ PyUnstable_GC_VisitObjects(gc_visit_callback_exit_early, &visited_i);
+ if (visited_i != 2) {
+ PyErr_SetString(
+ PyExc_AssertionError,
+ "test_gc_visit_objects_exit_early: did not exit when expected");
+ }
+ Py_RETURN_NONE;
+}
+
+
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
static PyMethodDef TestMethods[] = {
@@ -3452,6 +3519,8 @@ static PyMethodDef TestMethods[] = {
{"function_set_defaults", function_set_defaults, METH_VARARGS, NULL},
{"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
+ {"test_gc_visit_objects_basic", test_gc_visit_objects_basic, METH_NOARGS, NULL},
+ {"test_gc_visit_objects_exit_early", test_gc_visit_objects_exit_early, METH_NOARGS, NULL},
{NULL, NULL} /* sentinel */
};
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 5879c5e11fe14a..4eaa5490b6134c 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -2401,3 +2401,27 @@ PyObject_GC_IsFinalized(PyObject *obj)
}
return 0;
}
+
+void
+PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)
+{
+ size_t i;
+ GCState *gcstate = get_gc_state();
+ int origenstate = gcstate->enabled;
+ gcstate->enabled = 0;
+ for (i = 0; i < NUM_GENERATIONS; i++) {
+ PyGC_Head *gc_list, *gc;
+ gc_list = GEN_HEAD(gcstate, i);
+ for (gc = GC_NEXT(gc_list); gc != gc_list; gc = GC_NEXT(gc)) {
+ PyObject *op = FROM_GC(gc);
+ Py_INCREF(op);
+ int res = callback(op, arg);
+ Py_DECREF(op);
+ if (!res) {
+ goto done;
+ }
+ }
+ }
+done:
+ gcstate->enabled = origenstate;
+}
From e65b6b835732f933a95738b0796b349dd02385b0 Mon Sep 17 00:00:00 2001
From: Joongi Kim
Date: Tue, 14 Mar 2023 11:07:59 +0900
Subject: [PATCH 097/280] doc: Remove a duplicate 'versionchanged' in
library/asyncio-task (gh-102677)
---
Doc/library/asyncio-task.rst | 3 ---
1 file changed, 3 deletions(-)
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index 9b984243282268..5f1449e1b599ef 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -814,9 +814,6 @@ Waiting Primitives
Raises :exc:`TimeoutError` if the timeout occurs before
all Futures are done.
- .. versionchanged:: 3.10
- Removed the *loop* parameter.
-
Example::
for coro in as_completed(aws):
From 4d7f5d0f8339aa2ff8b823483b8b5f1c9a09c629 Mon Sep 17 00:00:00 2001
From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Date: Tue, 14 Mar 2023 14:22:21 +0530
Subject: [PATCH 098/280] GH-100227: cleanup initialization of global interned
dict (#102682)
---
Objects/unicodeobject.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 2d50f9c340f2f3..b9fb53147b9b51 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -14533,6 +14533,15 @@ _PyUnicode_InitGlobalObjects(PyInterpreterState *interp)
return _PyStatus_OK();
}
+ // Initialize the global interned dict
+ PyObject *interned = PyDict_New();
+ if (interned == NULL) {
+ PyErr_Clear();
+ return _PyStatus_ERR("failed to create interned dict");
+ }
+
+ set_interned_dict(interned);
+
/* Intern statically allocated string identifiers and deepfreeze strings.
* This must be done before any module initialization so that statically
* allocated string identifiers are used instead of heap allocated strings.
@@ -14600,14 +14609,7 @@ PyUnicode_InternInPlace(PyObject **p)
}
PyObject *interned = get_interned_dict();
- if (interned == NULL) {
- interned = PyDict_New();
- if (interned == NULL) {
- PyErr_Clear(); /* Don't leave an exception */
- return;
- }
- set_interned_dict(interned);
- }
+ assert(interned != NULL);
PyObject *t = PyDict_SetDefault(interned, s, s);
if (t == NULL) {
From 9c6171822f0476e283045d7f6693a00ba145d1bb Mon Sep 17 00:00:00 2001
From: T
Date: Tue, 14 Mar 2023 17:23:52 +0800
Subject: [PATCH 099/280] gh-100315: clarification to `__slots__` docs.
(#102621)
refer to tp_itemsize in discussion on "variable-length" built-in types
---
Doc/reference/datamodel.rst | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index f447bbb1216d52..1865d09fcaa127 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -1944,8 +1944,10 @@ Notes on using *__slots__*
descriptor directly from the base class). This renders the meaning of the
program undefined. In the future, a check may be added to prevent this.
-* Nonempty *__slots__* does not work for classes derived from "variable-length"
- built-in types such as :class:`int`, :class:`bytes` and :class:`tuple`.
+* :exc:`TypeError` will be raised if nonempty *__slots__* are defined for a
+ class derived from a
+ :c:member:`"variable-length" built-in type ` such as
+ :class:`int`, :class:`bytes`, and :class:`tuple`.
* Any non-string :term:`iterable` may be assigned to *__slots__*.
From c92b445f4119da2f400067bec89177393bad680a Mon Sep 17 00:00:00 2001
From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Date: Tue, 14 Mar 2023 15:21:38 +0530
Subject: [PATCH 100/280] GH-94851: check unicode consistency of static strings
in debug mode (#102684)
---
.../internal/pycore_unicodeobject_generated.h | 664 ++++++++++++++++++
Tools/build/generate_global_objects.py | 1 +
2 files changed, 665 insertions(+)
diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h
index 52af37a8e60aa8..fea9b6dbb1a75f 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -13,1332 +13,1996 @@ static inline void
_PyUnicode_InitStaticStrings(void) {
PyObject *string;
string = &_Py_ID(CANCELLED);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(FINISHED);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(False);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(JSONDecodeError);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(PENDING);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(Py_Repr);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(TextIOWrapper);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(True);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(WarningMessage);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_WindowsConsoleIO);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__IOBase_closed);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__abc_tpflags__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__abs__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__abstractmethods__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__add__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__aenter__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__aexit__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__aiter__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__all__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__and__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__anext__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__annotations__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__args__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__asyncio_running_event_loop__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__await__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__bases__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__bool__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__build_class__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__builtins__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__bytes__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__call__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__cantrace__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__class__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__class_getitem__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__classcell__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__complex__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__contains__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__copy__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__ctypes_from_outparam__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__del__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__delattr__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__delete__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__delitem__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__dict__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__dictoffset__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__dir__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__divmod__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__doc__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__enter__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__eq__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__exit__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__file__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__float__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__floordiv__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__format__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__fspath__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__ge__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__get__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__getattr__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__getattribute__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__getinitargs__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__getitem__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__getnewargs__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__getnewargs_ex__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__getstate__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__gt__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__hash__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__iadd__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__iand__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__ifloordiv__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__ilshift__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__imatmul__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__imod__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__import__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__imul__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__index__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__init__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__init_subclass__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__instancecheck__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__int__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__invert__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__ior__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__ipow__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__irshift__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__isabstractmethod__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__isub__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__iter__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__itruediv__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__ixor__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__le__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__len__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__length_hint__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__lltrace__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__loader__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__lshift__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__lt__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__main__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__matmul__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__missing__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__mod__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__module__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__mro_entries__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__mul__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__name__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__ne__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__neg__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__new__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__newobj__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__newobj_ex__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__next__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__notes__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__or__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__orig_class__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__origin__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__package__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__parameters__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__path__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__pos__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__pow__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__prepare__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__qualname__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__radd__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rand__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rdivmod__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__reduce__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__reduce_ex__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__repr__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__reversed__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rfloordiv__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rlshift__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rmatmul__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rmod__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rmul__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__ror__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__round__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rpow__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rrshift__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rshift__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rsub__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rtruediv__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__rxor__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__set__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__set_name__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__setattr__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__setitem__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__setstate__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__sizeof__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__slotnames__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__slots__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__spec__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__str__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__sub__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__subclasscheck__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__subclasshook__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__truediv__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__trunc__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__typing_is_unpacked_typevartuple__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__typing_prepare_subst__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__typing_subst__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__typing_unpacked_tuple_args__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__warningregistry__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__weaklistoffset__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__weakref__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(__xor__);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_abc_impl);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_abstract_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_active);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_annotation);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_anonymous_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_argtypes_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_as_parameter_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_asyncio_future_blocking);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_blksize);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_bootstrap);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_check_retval_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_dealloc_warn);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_feature_version);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_fields_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_finalizing);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_find_and_load);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_fix_up_module);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_flags_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_get_sourcefile);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_handle_fromlist);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_initializing);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_io);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_is_text_encoding);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_length_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_limbo);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_lock_unlock_module);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_loop);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_needs_com_addref_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_pack_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_restype_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_showwarnmsg);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_shutdown);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_slotnames);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_strptime_datetime);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_swappedbytes_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_type_);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_uninitialized_submodules);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_warn_unawaited_coroutine);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(_xoptions);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(a);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(abs_tol);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(access);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(add);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(add_done_callback);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(after_in_child);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(after_in_parent);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(aggregate_class);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(append);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(argdefs);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(arguments);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(argv);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(as_integer_ratio);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(ast);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(attribute);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(authorizer_callback);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(autocommit);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(b);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(backtick);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(base);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(before);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(big);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(binary_form);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(block);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(buffer);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(buffer_callback);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(buffer_size);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(buffering);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(buffers);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(bufsize);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(builtins);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(byteorder);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(bytes);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(bytes_per_sep);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(c);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(c_call);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(c_exception);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(c_return);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(cached_statements);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(cadata);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(cafile);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(call);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(call_exception_handler);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(call_soon);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(cancel);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(capath);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(category);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(cb_type);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(certfile);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(check_same_thread);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(clear);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(close);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(closed);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(closefd);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(closure);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_argcount);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_cellvars);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_code);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_consts);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_exceptiontable);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_filename);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_firstlineno);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_flags);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_freevars);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_kwonlyargcount);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_linetable);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_name);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_names);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_nlocals);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_posonlyargcount);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_qualname);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_stacksize);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(co_varnames);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(code);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(command);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(comment_factory);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(consts);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(context);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(cookie);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(copy);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(copyreg);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(coro);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(count);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(cwd);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(d);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(data);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(database);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(decode);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(decoder);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(default);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(defaultaction);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(delete);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(depth);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(detect_types);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(deterministic);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(device);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(dict);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(dictcomp);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(difference_update);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(digest);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(digest_size);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(digestmod);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(dir_fd);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(discard);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(dispatch_table);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(displayhook);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(dklen);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(doc);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(dont_inherit);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(dst);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(dst_dir_fd);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(duration);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(e);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(effective_ids);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(element_factory);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(encode);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(encoding);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(end);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(end_lineno);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(end_offset);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(endpos);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(env);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(errors);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(event);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(eventmask);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(exc_type);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(exc_value);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(excepthook);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(exception);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(exp);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(extend);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(facility);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(factory);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(false);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(family);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fanout);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fd);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fd2);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fdel);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fget);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(file);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(file_actions);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(filename);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fileno);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(filepath);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fillvalue);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(filters);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(final);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(find_class);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fix_imports);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(flags);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(flush);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(follow_symlinks);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(format);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(frequency);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(from_param);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fromlist);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fromtimestamp);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fromutc);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(fset);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(func);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(future);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(generation);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(genexpr);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(get);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(get_debug);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(get_event_loop);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(get_loop);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(get_source);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(getattr);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(getstate);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(gid);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(globals);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(groupindex);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(groups);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(handle);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(hash_name);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(header);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(headers);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(hi);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(hook);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(id);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(ident);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(ignore);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(imag);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(importlib);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(in_fd);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(incoming);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(indexgroup);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(inf);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(inheritable);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(initial);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(initial_bytes);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(initial_value);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(initval);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(inner_size);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(input);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(insert_comments);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(insert_pis);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(instructions);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(intern);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(intersection);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(isatty);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(isinstance);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(isoformat);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(isolation_level);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(istext);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(item);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(items);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(iter);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(iterable);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(iterations);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(join);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(jump);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(keepends);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(key);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(keyfile);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(keys);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(kind);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(kw);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(kw1);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(kw2);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(lambda);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(last);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(last_node);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(last_traceback);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(last_type);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(last_value);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(latin1);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(leaf_size);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(len);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(length);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(level);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(limit);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(line);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(line_buffering);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(lineno);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(listcomp);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(little);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(lo);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(locale);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(locals);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(logoption);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(loop);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(mapping);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(match);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(max_length);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(maxdigits);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(maxevents);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(maxmem);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(maxsplit);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(maxvalue);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(memLevel);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(memlimit);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(message);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(metaclass);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(method);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(mod);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(mode);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(module);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(module_globals);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(modules);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(mro);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(msg);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(mycmp);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(n);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(n_arg);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(n_fields);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(n_sequence_fields);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(n_unnamed_fields);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(name);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(name_from);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(namespace_separator);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(namespaces);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(narg);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(ndigits);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(new_limit);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(newline);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(newlines);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(next);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(node_depth);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(node_offset);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(ns);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(nstype);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(nt);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(null);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(number);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(obj);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(object);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(offset);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(offset_dst);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(offset_src);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(on_type_read);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(onceregistry);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(only_keys);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(oparg);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(opcode);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(open);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(opener);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(operation);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(optimize);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(options);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(order);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(out_fd);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(outgoing);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(overlapped);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(owner);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(p);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(pages);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(parent);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(password);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(path);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(pattern);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(peek);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(persistent_id);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(persistent_load);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(person);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(pi_factory);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(pid);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(policy);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(pos);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(pos1);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(pos2);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(posix);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(print_file_and_line);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(priority);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(progress);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(progress_handler);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(proto);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(protocol);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(ps1);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(ps2);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(query);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(quotetabs);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(r);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(raw);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(read);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(read1);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(readable);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(readall);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(readinto);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(readinto1);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(readline);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(readonly);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(real);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(reducer_override);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(registry);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(rel_tol);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(reload);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(repl);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(replace);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(reserved);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(reset);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(resetids);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(return);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(reverse);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(reversed);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(s);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(salt);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(sched_priority);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(scheduler);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(seek);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(seekable);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(selectors);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(self);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(send);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(sep);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(sequence);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(server_hostname);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(server_side);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(session);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(setcomp);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(setpgroup);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(setsid);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(setsigdef);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(setsigmask);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(setstate);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(shape);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(show_cmd);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(signed);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(size);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(sizehint);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(skip_file_prefixes);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(sleep);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(sock);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(sort);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(sound);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(source);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(source_traceback);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(src);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(src_dir_fd);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(stacklevel);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(start);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(statement);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(status);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(stderr);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(stdin);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(stdout);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(step);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(store_name);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(strategy);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(strftime);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(strict);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(strict_mode);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(string);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(sub_key);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(symmetric_difference_update);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(tabsize);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(tag);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(target);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(target_is_directory);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(task);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(tb_frame);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(tb_lasti);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(tb_lineno);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(tb_next);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(tell);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(template);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(term);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(text);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(threading);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(throw);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(timeout);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(times);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(timetuple);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(top);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(trace_callback);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(traceback);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(trailers);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(translate);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(true);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(truncate);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(twice);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(txt);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(type);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(tz);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(tzname);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(uid);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(unlink);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(unraisablehook);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(uri);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(usedforsecurity);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(value);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(values);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(version);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(volume);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(warnings);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(warnoptions);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(wbits);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(week);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(weekday);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(which);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(who);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(withdata);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(writable);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(write);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(write_through);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(x);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(year);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
string = &_Py_ID(zdict);
+ assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
}
/* End auto-generated code */
diff --git a/Tools/build/generate_global_objects.py b/Tools/build/generate_global_objects.py
index 9ceae89878cda8..1f53f02d41ef39 100644
--- a/Tools/build/generate_global_objects.py
+++ b/Tools/build/generate_global_objects.py
@@ -360,6 +360,7 @@ def generate_static_strings_initializer(identifiers, strings):
# This use of _Py_ID() is ignored by iter_global_strings()
# since iter_files() ignores .h files.
printer.write(f'string = &_Py_ID({i});')
+ printer.write(f'assert(_PyUnicode_CheckConsistency(string, 1));')
printer.write(f'PyUnicode_InternInPlace(&string);')
# XXX What about "strings"?
printer.write(END)
From 038b67ce91dfad85b4972f3c65c8797a824fdf33 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Tue, 14 Mar 2023 10:05:54 -0600
Subject: [PATCH 101/280] gh-81057: Add a CI Check for New Unsupported C Global
Variables (gh-102506)
This will keep us from adding new unsupported (i.e. non-const) C global variables, which would break interpreter isolation.
FYI, historically it is very uncommon for new global variables to get added. Furthermore, it is rare for new code to break the c-analyzer. So the check should almost always pass unnoticed.
Note that I've removed test_check_c_globals. A test wasn't a great fit conceptually and was super slow on debug builds. A CI check is a better fit.
This also resolves gh-100237.
https://github.com/python/cpython/issues/81057
---
.github/workflows/build.yml | 3 +
Lib/test/test_check_c_globals.py | 34 ----------
Makefile.pre.in | 6 ++
.../c_parser/preprocessor/common.py | 6 +-
Tools/c-analyzer/c_parser/preprocessor/gcc.py | 23 ++++---
Tools/c-analyzer/cpython/__main__.py | 62 ++++++++++++++++---
Tools/c-analyzer/cpython/_parser.py | 9 ++-
Tools/c-analyzer/cpython/ignored.tsv | 1 +
8 files changed, 90 insertions(+), 54 deletions(-)
delete mode 100644 Lib/test/test_check_c_globals.py
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2241b0b8aa409e..4e5328282f1224 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -111,6 +111,9 @@ jobs:
run: make smelly
- name: Check limited ABI symbols
run: make check-limited-abi
+ - name: Check for unsupported C global variables
+ if: github.event_name == 'pull_request' # $GITHUB_EVENT_NAME
+ run: make check-c-globals
build_win32:
name: 'Windows (x86)'
diff --git a/Lib/test/test_check_c_globals.py b/Lib/test/test_check_c_globals.py
deleted file mode 100644
index 670be52422f799..00000000000000
--- a/Lib/test/test_check_c_globals.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import unittest
-import test.test_tools
-from test.support.warnings_helper import save_restore_warnings_filters
-
-
-# TODO: gh-92584: c-analyzer uses distutils which was removed in Python 3.12
-raise unittest.SkipTest("distutils has been removed in Python 3.12")
-
-
-test.test_tools.skip_if_missing('c-analyzer')
-with test.test_tools.imports_under_tool('c-analyzer'):
- # gh-95349: Save/restore warnings filters to leave them unchanged.
- # Importing the c-analyzer imports docutils which imports pkg_resources
- # which adds a warnings filter.
- with save_restore_warnings_filters():
- from cpython.__main__ import main
-
-
-class ActualChecks(unittest.TestCase):
-
- # XXX Also run the check in "make check".
- #@unittest.expectedFailure
- # Failing on one of the buildbots (see https://bugs.python.org/issue36876).
- @unittest.skip('activate this once all the globals have been resolved')
- def test_check_c_globals(self):
- try:
- main('check', {})
- except NotImplementedError:
- raise unittest.SkipTest('not supported on this host')
-
-
-if __name__ == '__main__':
- # Test needs to be a package, so we can do relative imports.
- unittest.main()
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 1a1853bf3d7871..59762165c10036 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -2560,6 +2560,12 @@ distclean: clobber docclean
smelly: all
$(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/build/smelly.py
+# Check if any unsupported C global variables have been added.
+check-c-globals:
+ $(PYTHON_FOR_REGEN) $(srcdir)/Tools/c-analyzer/check-c-globals.py \
+ --format summary \
+ --traceback
+
# Find files with funny names
funny:
find $(SUBDIRS) $(SUBDIRSTOO) \
diff --git a/Tools/c-analyzer/c_parser/preprocessor/common.py b/Tools/c-analyzer/c_parser/preprocessor/common.py
index 4291a066337545..dbe1edeef38527 100644
--- a/Tools/c-analyzer/c_parser/preprocessor/common.py
+++ b/Tools/c-analyzer/c_parser/preprocessor/common.py
@@ -115,15 +115,15 @@ def converted_error(tool, argv, filename):
def convert_error(tool, argv, filename, stderr, rc):
error = (stderr.splitlines()[0], rc)
if (_expected := is_os_mismatch(filename, stderr)):
- logger.debug(stderr.strip())
+ logger.info(stderr.strip())
raise OSMismatchError(filename, _expected, argv, error, tool)
elif (_missing := is_missing_dep(stderr)):
- logger.debug(stderr.strip())
+ logger.info(stderr.strip())
raise MissingDependenciesError(filename, (_missing,), argv, error, tool)
elif '#error' in stderr:
# XXX Ignore incompatible files.
error = (stderr.splitlines()[1], rc)
- logger.debug(stderr.strip())
+ logger.info(stderr.strip())
raise ErrorDirectiveError(filename, argv, error, tool)
else:
# Try one more time, with stderr written to the terminal.
diff --git a/Tools/c-analyzer/c_parser/preprocessor/gcc.py b/Tools/c-analyzer/c_parser/preprocessor/gcc.py
index 7ef1a8afc3b135..24c1b0e9b9d48c 100644
--- a/Tools/c-analyzer/c_parser/preprocessor/gcc.py
+++ b/Tools/c-analyzer/c_parser/preprocessor/gcc.py
@@ -6,6 +6,11 @@
TOOL = 'gcc'
+META_FILES = {
+ '',
+ '',
+}
+
# https://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html
# flags:
# 1 start of a new file
@@ -75,11 +80,15 @@ def _iter_lines(text, reqfile, samefiles, cwd, raw=False):
# The first line is special.
# The next two lines are consistent.
- for expected in [
- f'# 1 "{reqfile}"',
- '# 1 ""',
- '# 1 ""',
- ]:
+ firstlines = [
+ f'# 0 "{reqfile}"',
+ '# 0 ""',
+ '# 0 ""',
+ ]
+ if text.startswith('# 1 '):
+ # Some preprocessors emit a lineno of 1 for line-less entries.
+ firstlines = [l.replace('# 0 ', '# 1 ') for l in firstlines]
+ for expected in firstlines:
line = next(lines)
if line != expected:
raise NotImplementedError((line, expected))
@@ -121,7 +130,7 @@ def _iter_top_include_lines(lines, topfile, cwd,
# _parse_marker_line() that the preprocessor reported lno as 1.
lno = 1
for line in lines:
- if line == '# 1 "" 2':
+ if line == '# 0 "" 2' or line == '# 1 "" 2':
# We're done with this top-level include.
return
@@ -174,8 +183,8 @@ def _parse_marker_line(line, reqfile=None):
return None, None, None
lno, origfile, flags = m.groups()
lno = int(lno)
+ assert origfile not in META_FILES, (line,)
assert lno > 0, (line, lno)
- assert origfile not in ('', ''), (line,)
flags = set(int(f) for f in flags.split()) if flags else ()
if 1 in flags:
diff --git a/Tools/c-analyzer/cpython/__main__.py b/Tools/c-analyzer/cpython/__main__.py
index 2b9e4233b95ac4..fe7a16726f45a9 100644
--- a/Tools/c-analyzer/cpython/__main__.py
+++ b/Tools/c-analyzer/cpython/__main__.py
@@ -1,5 +1,6 @@
import logging
import sys
+import textwrap
from c_common.fsutil import expand_filenames, iter_files_by_suffix
from c_common.scriptutil import (
@@ -26,6 +27,39 @@
logger = logging.getLogger(__name__)
+CHECK_EXPLANATION = textwrap.dedent('''
+ -------------------------
+
+ Non-constant global variables are generally not supported
+ in the CPython repo. We use a tool to analyze the C code
+ and report if any unsupported globals are found. The tool
+ may be run manually with:
+
+ ./python Tools/c-analyzer/check-c-globals.py --format summary [FILE]
+
+ Occasionally the tool is unable to parse updated code.
+ If this happens then add the file to the "EXCLUDED" list
+ in Tools/c-analyzer/cpython/_parser.py and create a new
+ issue for fixing the tool (and CC ericsnowcurrently
+ on the issue).
+
+ If the tool reports an unsupported global variable and
+ it is actually const (and thus supported) then first try
+ fixing the declaration appropriately in the code. If that
+ doesn't work then add the variable to the "should be const"
+ section of Tools/c-analyzer/cpython/ignored.tsv.
+
+ If the tool otherwise reports an unsupported global variable
+ then first try to make it non-global, possibly adding to
+ PyInterpreterState (for core code) or module state (for
+ extension modules). In an emergency, you can add the
+ variable to Tools/c-analyzer/cpython/globals-to-fix.tsv
+ to get CI passing, but doing so should be avoided. If
+ this course it taken, be sure to create an issue for
+ eliminating the global (and CC ericsnowcurrently).
+''')
+
+
def _resolve_filenames(filenames):
if filenames:
resolved = (_files.resolve_filename(f) for f in filenames)
@@ -123,14 +157,26 @@ def _cli_check(parser, **kwargs):
def cmd_check(filenames=None, **kwargs):
filenames = _resolve_filenames(filenames)
kwargs['get_file_preprocessor'] = _parser.get_preprocessor(log_err=print)
- c_analyzer.cmd_check(
- filenames,
- relroot=REPO_ROOT,
- _analyze=_analyzer.analyze,
- _CHECKS=CHECKS,
- file_maxsizes=_parser.MAX_SIZES,
- **kwargs
- )
+ try:
+ c_analyzer.cmd_check(
+ filenames,
+ relroot=REPO_ROOT,
+ _analyze=_analyzer.analyze,
+ _CHECKS=CHECKS,
+ file_maxsizes=_parser.MAX_SIZES,
+ **kwargs
+ )
+ except SystemExit as exc:
+ num_failed = exc.args[0] if getattr(exc, 'args', None) else None
+ if isinstance(num_failed, int):
+ if num_failed > 0:
+ sys.stderr.flush()
+ print(CHECK_EXPLANATION, flush=True)
+ raise # re-raise
+ except Exception:
+ sys.stderr.flush()
+ print(CHECK_EXPLANATION, flush=True)
+ raise # re-raise
def cmd_analyze(filenames=None, **kwargs):
diff --git a/Tools/c-analyzer/cpython/_parser.py b/Tools/c-analyzer/cpython/_parser.py
index e7764165d36c4c..6da4fbbf4224f1 100644
--- a/Tools/c-analyzer/cpython/_parser.py
+++ b/Tools/c-analyzer/cpython/_parser.py
@@ -106,15 +106,20 @@ def clean_lines(text):
* ./Include/internal
Modules/_decimal/**/*.c Modules/_decimal/libmpdec
+Modules/_elementtree.c Modules/expat
Modules/_hacl/*.c Modules/_hacl/include
Modules/_hacl/*.h Modules/_hacl/include
-Modules/_tkinter.c /usr/include/tcl8.6
Modules/md5module.c Modules/_hacl/include
Modules/sha1module.c Modules/_hacl/include
Modules/sha2module.c Modules/_hacl/include
-Modules/tkappinit.c /usr/include/tcl
Objects/stringlib/*.h Objects
+# possible system-installed headers, just in case
+Modules/_tkinter.c /usr/include/tcl8.6
+Modules/_uuidmodule.c /usr/include/uuid
+Modules/nismodule.c /usr/include/tirpc
+Modules/tkappinit.c /usr/include/tcl
+
# @end=tsv@
''')[1:]
diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv
index 700ddf2851839e..048112dd992555 100644
--- a/Tools/c-analyzer/cpython/ignored.tsv
+++ b/Tools/c-analyzer/cpython/ignored.tsv
@@ -228,6 +228,7 @@ Modules/_xxinterpchannelsmodule.c - _channelid_end_recv -
Modules/_xxinterpchannelsmodule.c - _channelid_end_send -
Modules/_zoneinfo.c - DAYS_BEFORE_MONTH -
Modules/_zoneinfo.c - DAYS_IN_MONTH -
+Modules/_xxsubinterpretersmodule.c - no_exception -
Modules/arraymodule.c - descriptors -
Modules/arraymodule.c - emptybuf -
Modules/cjkcodecs/_codecs_cn.c - _mapping_list -
From 41426fe588e204baddc4800c79d92dec7dbb1fd6 Mon Sep 17 00:00:00 2001
From: Paul Watson
Date: Tue, 14 Mar 2023 13:40:12 -0500
Subject: [PATCH 102/280] gh-102354: change python3 to python in docs examples
(#102696)
---
Doc/library/__main__.rst | 2 +-
Doc/library/importlib.metadata.rst | 2 +-
Doc/library/pdb.rst | 4 ++--
Doc/tutorial/venv.rst | 2 +-
Doc/using/venv-create.inc | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/Doc/library/__main__.rst b/Doc/library/__main__.rst
index 6a2a7a7317f711..761c88710f9891 100644
--- a/Doc/library/__main__.rst
+++ b/Doc/library/__main__.rst
@@ -259,7 +259,7 @@ one mentioned below are preferred.
See :mod:`venv` for an example of a package with a minimal ``__main__.py``
in the standard library. It doesn't contain a ``if __name__ == '__main__'``
- block. You can invoke it with ``python3 -m venv [directory]``.
+ block. You can invoke it with ``python -m venv [directory]``.
See :mod:`runpy` for more details on the :option:`-m` flag to the
interpreter executable.
diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst
index 988d1a317f5960..6e084101995e25 100644
--- a/Doc/library/importlib.metadata.rst
+++ b/Doc/library/importlib.metadata.rst
@@ -73,7 +73,7 @@ something into it:
.. code-block:: shell-session
- $ python3 -m venv example
+ $ python -m venv example
$ source example/bin/activate
(example) $ python -m pip install wheel
diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst
index 4ae12a5d03a78d..21c6ca8622dceb 100644
--- a/Doc/library/pdb.rst
+++ b/Doc/library/pdb.rst
@@ -58,7 +58,7 @@ of the debugger is::
:file:`pdb.py` can also be invoked as a script to debug other scripts. For
example::
- python3 -m pdb myscript.py
+ python -m pdb myscript.py
When invoked as a script, pdb will automatically enter post-mortem debugging if
the program being debugged exits abnormally. After post-mortem debugging (or
@@ -72,7 +72,7 @@ useful than quitting the debugger upon program's exit.
.. versionadded:: 3.7
:file:`pdb.py` now accepts a ``-m`` option that execute modules similar to the way
- ``python3 -m`` does. As with a script, the debugger will pause execution just
+ ``python -m`` does. As with a script, the debugger will pause execution just
before the first line of the module.
diff --git a/Doc/tutorial/venv.rst b/Doc/tutorial/venv.rst
index 05f0e6bbcc1b04..d1bba098d7d23b 100644
--- a/Doc/tutorial/venv.rst
+++ b/Doc/tutorial/venv.rst
@@ -44,7 +44,7 @@ whichever version you want.
To create a virtual environment, decide upon a directory where you want to
place it, and run the :mod:`venv` module as a script with the directory path::
- python3 -m venv tutorial-env
+ python -m venv tutorial-env
This will create the ``tutorial-env`` directory if it doesn't exist,
and also create directories inside it containing a copy of the Python
diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc
index d535b254f05698..43ee6b7807d57e 100644
--- a/Doc/using/venv-create.inc
+++ b/Doc/using/venv-create.inc
@@ -1,7 +1,7 @@
Creation of :ref:`virtual environments ` is done by executing the
command ``venv``::
- python3 -m venv /path/to/new/virtual/environment
+ python -m venv /path/to/new/virtual/environment
Running this command creates the target directory (creating any parent
directories that don't exist already) and places a ``pyvenv.cfg`` file in it
From f4367d5b83413c699ed89a51cff15cc155fa5f8b Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Tue, 14 Mar 2023 14:01:35 -0600
Subject: [PATCH 103/280] gh-102660: Handle m_copy Specially for the sys and
builtins Modules (gh-102661)
It doesn't make sense to use multi-phase init for these modules. Using a per-interpreter "m_copy" (instead of PyModuleDef.m_base.m_copy) makes this work okay. (This came up while working on gh-101660.)
Note that we might instead end up disallowing re-load for sys/builtins since they are so special.
https://github.com/python/cpython/issues/102660
---
Include/internal/pycore_interp.h | 1 +
Python/bltinmodule.c | 3 +++
Python/import.c | 35 ++++++++++++++++++++++++++++----
Python/pystate.c | 1 +
Python/sysmodule.c | 8 ++++++++
5 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index a3a582522361f1..fd7b92e86aa120 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -111,6 +111,7 @@ struct _is {
PyObject *dict; /* Stores per-interpreter state */
+ PyObject *sysdict_copy;
PyObject *builtins_copy;
// Initialized to _PyEval_EvalFrameDefault().
_PyFrameEvalFunction eval_frame;
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 12ca0ba6c4873c..a8a34620b9bcdf 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -3098,6 +3098,9 @@ _PyBuiltin_Init(PyInterpreterState *interp)
}
Py_DECREF(debug);
+ /* m_copy of Py_None means it is copied some other way. */
+ builtinsmodule.m_base.m_copy = Py_NewRef(Py_None);
+
return mod;
#undef ADD_TO_ALL
#undef SETBUILTIN
diff --git a/Python/import.c b/Python/import.c
index 1bf4199e125aa5..612fee243bd9e7 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -978,6 +978,16 @@ _PyImport_CheckSubinterpIncompatibleExtensionAllowed(const char *name)
return 0;
}
+static inline int
+match_mod_name(PyObject *actual, const char *expected)
+{
+ if (PyUnicode_CompareWithASCIIString(actual, expected) == 0) {
+ return 1;
+ }
+ assert(!PyErr_Occurred());
+ return 0;
+}
+
static int
fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename)
{
@@ -1001,7 +1011,8 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename)
// when the extension module doesn't support sub-interpreters.
// XXX Why special-case the main interpreter?
if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) {
- if (def->m_size == -1) {
+ /* m_copy of Py_None means it is copied some other way. */
+ if (def->m_size == -1 && def->m_base.m_copy != Py_None) {
if (def->m_base.m_copy) {
/* Somebody already imported the module,
likely under a different name.
@@ -1055,18 +1066,34 @@ import_find_extension(PyThreadState *tstate, PyObject *name,
PyObject *modules = MODULES(tstate->interp);
if (def->m_size == -1) {
+ PyObject *m_copy = def->m_base.m_copy;
/* Module does not support repeated initialization */
- if (def->m_base.m_copy == NULL)
+ if (m_copy == NULL) {
return NULL;
+ }
+ else if (m_copy == Py_None) {
+ if (match_mod_name(name, "sys")) {
+ m_copy = tstate->interp->sysdict_copy;
+ }
+ else if (match_mod_name(name, "builtins")) {
+ m_copy = tstate->interp->builtins_copy;
+ }
+ else {
+ _PyErr_SetString(tstate, PyExc_ImportError, "missing m_copy");
+ return NULL;
+ }
+ }
+ /* m_copy of Py_None means it is copied some other way. */
mod = import_add_module(tstate, name);
- if (mod == NULL)
+ if (mod == NULL) {
return NULL;
+ }
mdict = PyModule_GetDict(mod);
if (mdict == NULL) {
Py_DECREF(mod);
return NULL;
}
- if (PyDict_Update(mdict, def->m_base.m_copy)) {
+ if (PyDict_Update(mdict, m_copy)) {
Py_DECREF(mod);
return NULL;
}
diff --git a/Python/pystate.c b/Python/pystate.c
index 28606e4f32f71c..3a2966c54a4c3b 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -805,6 +805,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
assert(interp->imports.importlib == NULL);
assert(interp->imports.import_func == NULL);
+ Py_CLEAR(interp->sysdict_copy);
Py_CLEAR(interp->builtins_copy);
Py_CLEAR(interp->dict);
#ifdef HAVE_FORK
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 764fb70bae6c38..fc0550266bf1af 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -3425,12 +3425,20 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p)
return _PyStatus_ERR("failed to create a module object");
}
+ /* m_copy of Py_None means it is copied some other way. */
+ sysmodule.m_base.m_copy = Py_NewRef(Py_None);
+
PyObject *sysdict = PyModule_GetDict(sysmod);
if (sysdict == NULL) {
goto error;
}
interp->sysdict = Py_NewRef(sysdict);
+ interp->sysdict_copy = PyDict_Copy(sysdict);
+ if (interp->sysdict_copy == NULL) {
+ goto error;
+ }
+
if (PyDict_SetItemString(sysdict, "modules", modules) < 0) {
goto error;
}
From b608a1920bb22a2ebeed90103d6fd9258ccbdbdd Mon Sep 17 00:00:00 2001
From: Dong-hee Na
Date: Wed, 15 Mar 2023 05:20:14 +0900
Subject: [PATCH 104/280] gh-102674: Remove _specialization_stats from
Lib/opcode.py (#102685)
It's not use except in a test, so move it there instead.
---
Lib/opcode.py | 8 --------
Lib/test/test__opcode.py | 3 +--
2 files changed, 1 insertion(+), 10 deletions(-)
diff --git a/Lib/opcode.py b/Lib/opcode.py
index e46327cfe680b7..01e574509d38d5 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -389,14 +389,6 @@ def pseudo_op(name, op, real_ops):
_specialized_instructions = [
opcode for family in _specializations.values() for opcode in family
]
-_specialization_stats = [
- "success",
- "failure",
- "hit",
- "deferred",
- "miss",
- "deopt",
-]
_cache_format = {
"LOAD_GLOBAL": {
diff --git a/Lib/test/test__opcode.py b/Lib/test/test__opcode.py
index db831069c7aeb8..fb4ab15f7041ed 100644
--- a/Lib/test/test__opcode.py
+++ b/Lib/test/test__opcode.py
@@ -69,8 +69,7 @@ def test_stack_effect_jump(self):
class SpecializationStatsTests(unittest.TestCase):
def test_specialization_stats(self):
- stat_names = opcode._specialization_stats
-
+ stat_names = ["success", "failure", "hit", "deferred", "miss", "deopt"]
specialized_opcodes = [
op.lower()
for op in opcode._specializations
From a5048304df5e6936076751bd2a5cdddcbe84da06 Mon Sep 17 00:00:00 2001
From: JosephSBoyle <48555120+JosephSBoyle@users.noreply.github.com>
Date: Tue, 14 Mar 2023 22:16:45 +0000
Subject: [PATCH 105/280] Rename redundant enum tests so that they run
(#102535)
---
Lib/test/test_enum.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index 2b14590b2c21af..a11bb441f06e8e 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -4579,15 +4579,14 @@ class Double(Enum):
TWO = 2
self.assertEqual(Double.__doc__, None)
-
- def test_doc_1(self):
+ def test_doc_3(self):
class Triple(Enum):
ONE = 1
TWO = 2
THREE = 3
self.assertEqual(Triple.__doc__, None)
- def test_doc_1(self):
+ def test_doc_4(self):
class Quadruple(Enum):
ONE = 1
TWO = 2
From fedee27a1f88fb7eac90ecd025c80d164753566b Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Tue, 14 Mar 2023 22:38:15 +0000
Subject: [PATCH 106/280] gh-101578: mention in what's new in 3.12 that
exceptions are now normalized before stored (#102702)
---
Doc/whatsnew/3.12.rst | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 9f33dbc808ddc0..217ffec1ee1007 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -982,6 +982,11 @@ Porting to Python 3.12
effects, these side effects are no longer duplicated.
(Contributed by Victor Stinner in :gh:`98724`.)
+* The interpreter's error indicator is now always normalized. This means
+ that :c:func:`PyErr_SetObject`, :c:func:`PyErr_SetString` and the other
+ functions that set the error indicator now normalize the exception
+ before storing it. (Contributed by Mark Shannon in :gh:`101578`.)
+
Deprecated
----------
From 2d5a20cd9e2ad95589a3db95bb52607737118797 Mon Sep 17 00:00:00 2001
From: "Robert Prater (B. Eng)"
Date: Tue, 14 Mar 2023 20:03:43 -0400
Subject: [PATCH 107/280] gh-102703: Fix typo in modules tutorial documentation
(GH-102707)
**Before**
This prevents directories with a common name, such as ``string``, unintentionally hiding ...
**After**
This prevents directories with a common name, such as ``string``, from unintentionally hiding ...
---
Doc/tutorial/modules.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst
index ad70d92994af49..4daafa49a34d2e 100644
--- a/Doc/tutorial/modules.rst
+++ b/Doc/tutorial/modules.rst
@@ -438,7 +438,7 @@ When importing the package, Python searches through the directories on
The :file:`__init__.py` files are required to make Python treat directories
containing the file as packages. This prevents directories with a common name,
-such as ``string``, unintentionally hiding valid modules that occur later
+such as ``string``, from unintentionally hiding valid modules that occur later
on the module search path. In the simplest case, :file:`__init__.py` can just be
an empty file, but it can also execute initialization code for the package or
set the ``__all__`` variable, described later.
From 9e47401b23e8dcfb3d19d5466499331e0aba4e5d Mon Sep 17 00:00:00 2001
From: Steve Dower
Date: Wed, 15 Mar 2023 00:07:30 +0000
Subject: [PATCH 108/280] gh-102519: Avoid failing tests due to inaccessible
volumes (GH-102706)
---
Lib/test/test_os.py | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 253e2a23238f12..42357fef80ec89 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -2683,12 +2683,17 @@ def test_listvolumes(self):
def test_listmounts(self):
for volume in os.listvolumes():
- mounts = os.listmounts(volume)
- self.assertIsInstance(mounts, list)
- self.assertSetEqual(
- set(mounts),
- self.known_mounts & set(mounts),
- )
+ try:
+ mounts = os.listmounts(volume)
+ except OSError as ex:
+ if support.verbose:
+ print("Skipping", volume, "because of", ex)
+ else:
+ self.assertIsInstance(mounts, list)
+ self.assertSetEqual(
+ set(mounts),
+ self.known_mounts & set(mounts),
+ )
@unittest.skipUnless(hasattr(os, 'readlink'), 'needs os.readlink()')
From b78ead8662d6a71cb170f0eec01ddb2d25e49097 Mon Sep 17 00:00:00 2001
From: JosephSBoyle <48555120+JosephSBoyle@users.noreply.github.com>
Date: Wed, 15 Mar 2023 00:33:19 +0000
Subject: [PATCH 109/280] gh-102560 Add docstrings to asyncio.TaskGroup
(#102565)
---
Lib/asyncio/taskgroups.py | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py
index 911419e1769c17..0fdea3697ece3d 100644
--- a/Lib/asyncio/taskgroups.py
+++ b/Lib/asyncio/taskgroups.py
@@ -10,7 +10,21 @@
class TaskGroup:
+ """Asynchronous context manager for managing groups of tasks.
+ Example use:
+
+ async with asyncio.TaskGroup() as group:
+ task1 = group.create_task(some_coroutine(...))
+ task2 = group.create_task(other_coroutine(...))
+ print("Both tasks have completed now.")
+
+ All tasks are awaited when the context manager exits.
+
+ Any exceptions other than `asyncio.CancelledError` raised within
+ a task will cancel all remaining tasks and wait for them to exit.
+ The exceptions are then combined and raised as an `ExceptionGroup`.
+ """
def __init__(self):
self._entered = False
self._exiting = False
@@ -135,6 +149,10 @@ async def __aexit__(self, et, exc, tb):
self._errors = None
def create_task(self, coro, *, name=None, context=None):
+ """Create a new task in this group and return it.
+
+ Similar to `asyncio.create_task`.
+ """
if not self._entered:
raise RuntimeError(f"TaskGroup {self!r} has not been entered")
if self._exiting and not self._tasks:
From 669c82ac5f4f970aef21f7deae3ab822a273a430 Mon Sep 17 00:00:00 2001
From: Andre Hora
Date: Tue, 14 Mar 2023 23:36:31 -0300
Subject: [PATCH 110/280] gh-101377: improving
test_locale_calendar_formatweekday of calendar (#101378)
---------
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Terry Jan Reedy
---
Lib/test/test_calendar.py | 8 ++++++--
.../Tests/2023-01-27-18-10-40.gh-issue-101377.IJGpqh.rst | 1 +
2 files changed, 7 insertions(+), 2 deletions(-)
create mode 100644 Misc/NEWS.d/next/Tests/2023-01-27-18-10-40.gh-issue-101377.IJGpqh.rst
diff --git a/Lib/test/test_calendar.py b/Lib/test/test_calendar.py
index 3d9dcf12f2dad8..ccfbeede0be949 100644
--- a/Lib/test/test_calendar.py
+++ b/Lib/test/test_calendar.py
@@ -568,11 +568,15 @@ def test_locale_calendar_formatweekday(self):
try:
# formatweekday uses different day names based on the available width.
cal = calendar.LocaleTextCalendar(locale='en_US')
+ # For really short widths, the abbreviated name is truncated.
+ self.assertEqual(cal.formatweekday(0, 1), "M")
+ self.assertEqual(cal.formatweekday(0, 2), "Mo")
# For short widths, a centered, abbreviated name is used.
+ self.assertEqual(cal.formatweekday(0, 3), "Mon")
self.assertEqual(cal.formatweekday(0, 5), " Mon ")
- # For really short widths, even the abbreviated name is truncated.
- self.assertEqual(cal.formatweekday(0, 2), "Mo")
+ self.assertEqual(cal.formatweekday(0, 8), " Mon ")
# For long widths, the full day name is used.
+ self.assertEqual(cal.formatweekday(0, 9), " Monday ")
self.assertEqual(cal.formatweekday(0, 10), " Monday ")
except locale.Error:
raise unittest.SkipTest('cannot set the en_US locale')
diff --git a/Misc/NEWS.d/next/Tests/2023-01-27-18-10-40.gh-issue-101377.IJGpqh.rst b/Misc/NEWS.d/next/Tests/2023-01-27-18-10-40.gh-issue-101377.IJGpqh.rst
new file mode 100644
index 00000000000000..a9c19ce060e3ab
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2023-01-27-18-10-40.gh-issue-101377.IJGpqh.rst
@@ -0,0 +1 @@
+Improved test_locale_calendar_formatweekday of calendar.
From ff52779c54552f94745df5e1b6075c306c980700 Mon Sep 17 00:00:00 2001
From: Tom Levy
Date: Wed, 15 Mar 2023 20:06:32 +1300
Subject: [PATCH 111/280] Remove misformatted exclamation marks in docs
(#102694)
Remove the exclamation mark from :program:`!foo` in .rst files because
it inadvertently shows up in the rendered HTML.
(Sphinx's cross-referencing roles use a '!' prefix to suppress
hyperlinking[1], but :program: is not a cross-referencing role so the
'!' is displayed verbatim.)
The exclamation marks in venv.rst were introduced in #98350. See
comments [2] and [3] for additional discussion.
[1]: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#cross-referencing-syntax
[2]: https://github.com/python/cpython/pull/98350#issuecomment-1285965759
[3]: https://github.com/python/cpython/pull/98350#issuecomment-1286394047
Reported-by: Vinay Sajip
---
Doc/library/venv.rst | 4 ++--
Misc/NEWS.d/3.12.0a2.rst | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst
index 8eb0b35eaa12df..240ab139838db9 100644
--- a/Doc/library/venv.rst
+++ b/Doc/library/venv.rst
@@ -61,7 +61,7 @@ running from a virtual environment.
A virtual environment may be "activated" using a script in its binary directory
(``bin`` on POSIX; ``Scripts`` on Windows).
This will prepend that directory to your :envvar:`!PATH`, so that running
-:program:`!python` will invoke the environment's Python interpreter
+:program:`python` will invoke the environment's Python interpreter
and you can run installed scripts without having to use their full path.
The invocation of the activation script is platform-specific
(:samp:`{}` must be replaced by the path to the directory
@@ -84,7 +84,7 @@ containing the virtual environment):
+-------------+------------+--------------------------------------------------+
.. versionadded:: 3.4
- :program:`!fish` and :program:`!csh` activation scripts.
+ :program:`fish` and :program:`csh` activation scripts.
.. versionadded:: 3.8
PowerShell activation scripts installed under POSIX for PowerShell Core
diff --git a/Misc/NEWS.d/3.12.0a2.rst b/Misc/NEWS.d/3.12.0a2.rst
index 117be21a3221b6..d871384903e7cd 100644
--- a/Misc/NEWS.d/3.12.0a2.rst
+++ b/Misc/NEWS.d/3.12.0a2.rst
@@ -959,7 +959,7 @@ Fix ``make regen-test-levenshtein`` for out-of-tree builds.
Don't use vendored ``libmpdec`` headers if :option:`--with-system-libmpdec`
is passed to :program:`configure`. Don't use vendored ``libexpat`` headers
-if :option:`--with-system-expat` is passed to :program:`!configure`.
+if :option:`--with-system-expat` is passed to :program:`configure`.
..
From 4de1e653e7c98fdf60c644ff82b95193672872d1 Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Wed, 15 Mar 2023 12:33:41 +0300
Subject: [PATCH 112/280] gh-102615: Use `list` instead of `tuple` in `repr` of
paramspec (#102637)
Co-authored-by: Alex Waygood
---
Lib/test/test_typing.py | 45 +++++++++++++++++++
Lib/typing.py | 7 +--
...-03-13-12-05-55.gh-issue-102615.NcA_ZL.rst | 3 ++
3 files changed, 52 insertions(+), 3 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-13-12-05-55.gh-issue-102615.NcA_ZL.rst
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index c17be6cd0bbc4a..89c725cda54f12 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -3809,6 +3809,51 @@ class Y(C[int]):
self.assertEqual(Y.__qualname__,
'GenericTests.test_repr_2..Y')
+ def test_repr_3(self):
+ T = TypeVar('T')
+ T1 = TypeVar('T1')
+ P = ParamSpec('P')
+ P2 = ParamSpec('P2')
+ Ts = TypeVarTuple('Ts')
+
+ class MyCallable(Generic[P, T]):
+ pass
+
+ class DoubleSpec(Generic[P, P2, T]):
+ pass
+
+ class TsP(Generic[*Ts, P]):
+ pass
+
+ object_to_expected_repr = {
+ MyCallable[P, T]: "MyCallable[~P, ~T]",
+ MyCallable[Concatenate[T1, P], T]: "MyCallable[typing.Concatenate[~T1, ~P], ~T]",
+ MyCallable[[], bool]: "MyCallable[[], bool]",
+ MyCallable[[int], bool]: "MyCallable[[int], bool]",
+ MyCallable[[int, str], bool]: "MyCallable[[int, str], bool]",
+ MyCallable[[int, list[int]], bool]: "MyCallable[[int, list[int]], bool]",
+ MyCallable[Concatenate[*Ts, P], T]: "MyCallable[typing.Concatenate[*Ts, ~P], ~T]",
+
+ DoubleSpec[P2, P, T]: "DoubleSpec[~P2, ~P, ~T]",
+ DoubleSpec[[int], [str], bool]: "DoubleSpec[[int], [str], bool]",
+ DoubleSpec[[int, int], [str, str], bool]: "DoubleSpec[[int, int], [str, str], bool]",
+
+ TsP[*Ts, P]: "TsP[*Ts, ~P]",
+ TsP[int, str, list[int], []]: "TsP[int, str, list[int], []]",
+ TsP[int, [str, list[int]]]: "TsP[int, [str, list[int]]]",
+
+ # These lines are just too long to fit:
+ MyCallable[Concatenate[*Ts, P], int][int, str, [bool, float]]:
+ "MyCallable[[int, str, bool, float], int]",
+ }
+
+ for obj, expected_repr in object_to_expected_repr.items():
+ with self.subTest(obj=obj, expected_repr=expected_repr):
+ self.assertRegex(
+ repr(obj),
+ fr"^{re.escape(MyCallable.__module__)}.*\.{re.escape(expected_repr)}$",
+ )
+
def test_eq_1(self):
self.assertEqual(Generic, Generic)
self.assertEqual(Generic[T], Generic[T])
diff --git a/Lib/typing.py b/Lib/typing.py
index 8d40e923bb1d08..ab334395676159 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -230,16 +230,17 @@ def _type_repr(obj):
typically enough to uniquely identify a type. For everything
else, we fall back on repr(obj).
"""
- if isinstance(obj, types.GenericAlias):
- return repr(obj)
if isinstance(obj, type):
if obj.__module__ == 'builtins':
return obj.__qualname__
return f'{obj.__module__}.{obj.__qualname__}'
if obj is ...:
- return('...')
+ return '...'
if isinstance(obj, types.FunctionType):
return obj.__name__
+ if isinstance(obj, tuple):
+ # Special case for `repr` of types with `ParamSpec`:
+ return '[' + ', '.join(_type_repr(t) for t in obj) + ']'
return repr(obj)
diff --git a/Misc/NEWS.d/next/Library/2023-03-13-12-05-55.gh-issue-102615.NcA_ZL.rst b/Misc/NEWS.d/next/Library/2023-03-13-12-05-55.gh-issue-102615.NcA_ZL.rst
new file mode 100644
index 00000000000000..333068369bc4f7
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-13-12-05-55.gh-issue-102615.NcA_ZL.rst
@@ -0,0 +1,3 @@
+Typing: Improve the ``repr`` of generic aliases for classes generic over a
+:class:`~typing.ParamSpec`. (Use square brackets to represent a parameter
+list.)
From 06915ed9a23809993ba26b9ad07634646e5e741b Mon Sep 17 00:00:00 2001
From: Max Bachmann
Date: Wed, 15 Mar 2023 13:58:43 +0100
Subject: [PATCH 113/280] gh-102281: Fix potential nullptr dereference + use of
uninitialized memory (gh-102282)
---
.../2023-03-02-13-49-21.gh-issue-102281.QCuu2N.rst | 1 +
Modules/getpath.c | 5 ++++-
Python/fileutils.c | 6 +++++-
3 files changed, 10 insertions(+), 2 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-02-13-49-21.gh-issue-102281.QCuu2N.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-02-13-49-21.gh-issue-102281.QCuu2N.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-02-13-49-21.gh-issue-102281.QCuu2N.rst
new file mode 100644
index 00000000000000..b0269dd3d92bd5
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-02-13-49-21.gh-issue-102281.QCuu2N.rst
@@ -0,0 +1 @@
+Fix potential nullptr dereference and use of uninitialized memory in fileutils. Patch by Max Bachmann.
diff --git a/Modules/getpath.c b/Modules/getpath.c
index 2f20521592ce2e..237fe8c0c2c221 100644
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -446,7 +446,10 @@ getpath_realpath(PyObject *Py_UNUSED(self) , PyObject *args)
if (s) {
*s = L'\0';
}
- path2 = _Py_normpath(_Py_join_relfile(path, resolved), -1);
+ path2 = _Py_join_relfile(path, resolved);
+ if (path2) {
+ path2 = _Py_normpath(path2, -1);
+ }
PyMem_RawFree((void *)path);
path = path2;
}
diff --git a/Python/fileutils.c b/Python/fileutils.c
index 4ac759c45a3a1e..f48b626b444016 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -2233,7 +2233,10 @@ _Py_join_relfile(const wchar_t *dirname, const wchar_t *relfile)
}
assert(wcslen(dirname) < MAXPATHLEN);
assert(wcslen(relfile) < MAXPATHLEN - wcslen(dirname));
- join_relfile(filename, bufsize, dirname, relfile);
+ if (join_relfile(filename, bufsize, dirname, relfile) < 0) {
+ PyMem_RawFree(filename);
+ return NULL;
+ }
return filename;
}
@@ -2271,6 +2274,7 @@ _Py_find_basename(const wchar_t *filename)
wchar_t *
_Py_normpath(wchar_t *path, Py_ssize_t size)
{
+ assert(path != NULL);
if (!path[0] || size == 0) {
return path;
}
From 939f5a6019e07c102b8247a06047c7f9e5d5838b Mon Sep 17 00:00:00 2001
From: Julien Palard
Date: Wed, 15 Mar 2023 16:10:03 +0100
Subject: [PATCH 114/280] gh-101100: Documenting --prefix and --exec-prefix.
(GH-102695)
Co-authored-by: Erlend E. Aasland
---
Doc/c-api/init.rst | 2 +-
Doc/c-api/intro.rst | 10 +++++-----
Doc/library/sys.rst | 2 +-
Doc/using/configure.rst | 16 ++++++++++++++++
Doc/using/unix.rst | 2 +-
5 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst
index b50ee3b3803e29..38e324fb6409bc 100644
--- a/Doc/c-api/init.rst
+++ b/Doc/c-api/init.rst
@@ -513,7 +513,7 @@ Process-wide parameters
program name is ``'/usr/local/bin/python'``, the prefix is ``'/usr/local'``. The
returned string points into static storage; the caller should not modify its
value. This corresponds to the :makevar:`prefix` variable in the top-level
- :file:`Makefile` and the ``--prefix`` argument to the :program:`configure`
+ :file:`Makefile` and the :option:`--prefix` argument to the :program:`configure`
script at build time. The value is available to Python code as ``sys.prefix``.
It is only useful on Unix. See also the next function.
diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst
index 85eb24a495b640..acd4e033dfbc4b 100644
--- a/Doc/c-api/intro.rst
+++ b/Doc/c-api/intro.rst
@@ -78,19 +78,19 @@ used by extension writers. Structure member names do not have a reserved prefix.
The header files are typically installed with Python. On Unix, these are
located in the directories :file:`{prefix}/include/pythonversion/` and
-:file:`{exec_prefix}/include/pythonversion/`, where :envvar:`prefix` and
-:envvar:`exec_prefix` are defined by the corresponding parameters to Python's
+:file:`{exec_prefix}/include/pythonversion/`, where :option:`prefix <--prefix>` and
+:option:`exec_prefix <--exec-prefix>` are defined by the corresponding parameters to Python's
:program:`configure` script and *version* is
``'%d.%d' % sys.version_info[:2]``. On Windows, the headers are installed
-in :file:`{prefix}/include`, where :envvar:`prefix` is the installation
+in :file:`{prefix}/include`, where ``prefix`` is the installation
directory specified to the installer.
To include the headers, place both directories (if different) on your compiler's
search path for includes. Do *not* place the parent directories on the search
path and then use ``#include ``; this will break on
multi-platform builds since the platform independent headers under
-:envvar:`prefix` include the platform specific headers from
-:envvar:`exec_prefix`.
+:option:`prefix <--prefix>` include the platform specific headers from
+:option:`exec_prefix <--exec-prefix>`.
C++ users should note that although the API is defined entirely using C, the
header files properly declare the entry points to be ``extern "C"``. As a result,
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index 23c5bbed0c6f9c..a53d4908783e15 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -1323,7 +1323,7 @@ always available.
A string giving the site-specific directory prefix where the platform
independent Python files are installed; on Unix, the default is
- ``'/usr/local'``. This can be set at build time with the ``--prefix``
+ :file:`/usr/local`. This can be set at build time with the :option:`--prefix`
argument to the :program:`configure` script. See
:ref:`installation_paths` for derived paths.
diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst
index 8936bd381c9d97..ce858ab2c8d79e 100644
--- a/Doc/using/configure.rst
+++ b/Doc/using/configure.rst
@@ -216,6 +216,22 @@ WebAssembly Options
Install Options
---------------
+.. cmdoption:: --prefix=PREFIX
+
+ Install architecture-independent files in PREFIX. On Unix, it
+ defaults to :file:`/usr/local`.
+
+ This value can be retrived at runtime using :data:`sys.prefix`.
+
+ As an example, one can use ``--prefix="$HOME/.local/"`` to install
+ a Python in its home directory.
+
+.. cmdoption:: --exec-prefix=EPREFIX
+
+ Install architecture-dependent files in EPREFIX, defaults to :option:`--prefix`.
+
+ This value can be retrived at runtime using :data:`sys.exec_prefix`.
+
.. cmdoption:: --disable-test-modules
Don't build nor install test modules, like the :mod:`test` package or the
diff --git a/Doc/using/unix.rst b/Doc/using/unix.rst
index 24c02c99f871d5..067ff4cce5e48d 100644
--- a/Doc/using/unix.rst
+++ b/Doc/using/unix.rst
@@ -93,7 +93,7 @@ Python-related paths and files
==============================
These are subject to difference depending on local installation conventions;
-:envvar:`prefix` (``${prefix}``) and :envvar:`exec_prefix` (``${exec_prefix}``)
+:option:`prefix <--prefix>` and :option:`exec_prefix <--exec-prefix>`
are installation-dependent and should be interpreted as for GNU software; they
may be the same.
From 71190acad1e4393c21e89da5c5f105203df2d84d Mon Sep 17 00:00:00 2001
From: Guido van Rossum
Date: Wed, 15 Mar 2023 08:37:36 -0700
Subject: [PATCH 115/280] gh-102654: Insert #line directives in
generated_cases.c.h (#102669)
This behavior is optional, because in some extreme cases it
may just make debugging harder. The tool defaults it to off,
but it is on in Makefile.pre.in.
Also note that this makes diffs to generated_cases.c.h noisier,
since whenever you insert or delete a line in bytecodes.c,
all subsequent #line directives will change.
---
Makefile.pre.in | 1 +
Python/generated_cases.c.h | 6 +-
Tools/cases_generator/generate_cases.py | 89 ++++++++++++++++++++-----
3 files changed, 77 insertions(+), 19 deletions(-)
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 59762165c10036..8f13198e7e34b3 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1485,6 +1485,7 @@ regen-cases:
PYTHONPATH=$(srcdir)/Tools/cases_generator \
$(PYTHON_FOR_REGEN) \
$(srcdir)/Tools/cases_generator/generate_cases.py \
+ --emit-line-directives \
-o $(srcdir)/Python/generated_cases.c.h.new \
-m $(srcdir)/Python/opcode_metadata.h.new \
$(srcdir)/Python/bytecodes.c
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 2f5726c317eb1b..9fb532209041ce 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -75,7 +75,7 @@
TARGET(LOAD_CONST) {
PREDICTED(LOAD_CONST);
PyObject *value;
- value = GETITEM(frame->f_code->co_consts, oparg);
+ value = GETITEM(consts, oparg);
Py_INCREF(value);
STACK_GROW(1);
stack_pointer[-1] = value;
@@ -149,7 +149,7 @@
oparg = (next_instr++)->op.arg;
{
PyObject *value;
- value = GETITEM(frame->f_code->co_consts, oparg);
+ value = GETITEM(consts, oparg);
Py_INCREF(value);
_tmp_1 = value;
}
@@ -198,7 +198,7 @@
PyObject *_tmp_2;
{
PyObject *value;
- value = GETITEM(frame->f_code->co_consts, oparg);
+ value = GETITEM(consts, oparg);
Py_INCREF(value);
_tmp_2 = value;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 16128dc85a7402..46223c9dee99be 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -15,6 +15,7 @@
from enum import Enum, auto
+import lexer as lx
import parser
from parser import StackEffect
from parser import TypeSrcLiteral, TypeSrcConst, TypeSrcLocals, TypeSrcStackInput
@@ -87,6 +88,9 @@
arg_parser.add_argument(
"-m", "--metadata", type=str, help="Generated metadata", default=DEFAULT_METADATA_OUTPUT
)
+arg_parser.add_argument(
+ "-l", "--emit-line-directives", help="Emit #line directives", action="store_true"
+)
arg_parser.add_argument(
"input", nargs=argparse.REMAINDER, help="Instruction definition file(s)"
)
@@ -158,14 +162,35 @@ class Formatter:
stream: typing.TextIO
prefix: str
postfix: str
-
- def __init__(self, stream: typing.TextIO, indent: int) -> None:
+ emit_line_directives: bool = False
+ lineno: int # Next line number, 1-based
+ filename: str # Slightly improved stream.filename
+ nominal_lineno: int
+ nominal_filename: str
+
+ def __init__(
+ self, stream: typing.TextIO, indent: int, emit_line_directives: bool = False
+ ) -> None:
self.stream = stream
self.prefix = " " * indent
self.postfix = ""
+ self.emit_line_directives = emit_line_directives
+ self.lineno = 1
+ # Make filename more user-friendly and less platform-specific
+ filename = self.stream.name.replace("\\", "/")
+ if filename.startswith("./"):
+ filename = filename[2:]
+ if filename.endswith(".new"):
+ filename = filename[:-4]
+ self.filename = filename
+ self.nominal_lineno = 1
+ self.nominal_filename = filename
def write_raw(self, s: str) -> None:
self.stream.write(s)
+ newlines = s.count("\n")
+ self.lineno += newlines
+ self.nominal_lineno += newlines
def emit(self, arg: str) -> None:
if arg:
@@ -173,6 +198,17 @@ def emit(self, arg: str) -> None:
else:
self.write_raw("\n")
+ def set_lineno(self, lineno: int, filename: str) -> None:
+ if self.emit_line_directives:
+ if lineno != self.nominal_lineno or filename != self.nominal_filename:
+ self.emit(f'#line {lineno} "{filename}"')
+ self.nominal_lineno = lineno
+ self.nominal_filename = filename
+
+ def reset_lineno(self) -> None:
+ if self.lineno != self.nominal_lineno or self.filename != self.nominal_filename:
+ self.set_lineno(self.lineno + 1, self.filename)
+
@contextlib.contextmanager
def indent(self):
self.prefix += " "
@@ -253,6 +289,7 @@ class Instruction:
block: parser.Block
block_text: list[str] # Block.text, less curlies, less PREDICT() calls
predictions: list[str] # Prediction targets (instruction names)
+ block_line: int # First line of block in original code
# Computed by constructor
always_exits: bool
@@ -278,7 +315,7 @@ def __init__(self, inst: parser.InstDef):
self.kind = inst.kind
self.name = inst.name
self.block = inst.block
- self.block_text, self.check_eval_breaker, self.predictions = \
+ self.block_text, self.check_eval_breaker, self.predictions, self.block_line = \
extract_block_text(self.block)
self.always_exits = always_exits(self.block_text)
self.cache_effects = [
@@ -587,7 +624,13 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
assert dedent <= 0
extra = " " * -dedent
names_to_skip = self.unmoved_names | frozenset({UNUSED, "null"})
+ offset = 0
+ context = self.block.context
+ assert context != None
+ filename = context.owner.filename
for line in self.block_text:
+ out.set_lineno(self.block_line + offset, filename)
+ offset += 1
if m := re.match(r"(\s*)U_INST\((.+)\);\s*$", line):
space, label = m.groups()
out.emit(f"UOP_{label}();")
@@ -618,6 +661,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
out.write_raw(f"{space}if ({cond}) goto {label};{out.postfix}\n")
elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*(?://.*)?$", line):
if not self.register:
+ out.reset_lineno()
space = extra + m.group(1)
for ieff in self.input_effects:
if ieff.name in names_to_skip:
@@ -633,6 +677,7 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
out.write_raw(f"{space}Py_{decref}({ieff.name});\n")
else:
out.write_raw(extra + line.rstrip("\n") + out.postfix + "\n")
+ out.reset_lineno()
InstructionOrCacheEffect = Instruction | parser.CacheEffect
@@ -707,6 +752,7 @@ class Analyzer:
output_filename: str
metadata_filename: str
errors: int = 0
+ emit_line_directives: bool = False
def __init__(self, input_filenames: list[str], output_filename: str, metadata_filename: str):
"""Read the input file."""
@@ -772,6 +818,10 @@ def parse_file(self, filename: str, instrs_idx: dict[str, int]) -> None:
with open(filename) as file:
src = file.read()
+ # Make filename more user-friendly and less platform-specific
+ filename = filename.replace("\\", "/")
+ if filename.startswith("./"):
+ filename = filename[2:]
psr = parser.Parser(src, filename=filename)
# Skip until begin marker
@@ -1220,13 +1270,14 @@ def write_metadata(self) -> None:
format_enums = [INSTR_FMT_PREFIX + format for format in sorted(all_formats)]
with open(self.metadata_filename, "w") as f:
+ # Create formatter
+ self.out = Formatter(f, 0)
+
# Write provenance header
- f.write(f"// This file is generated by {THIS}\n")
- f.write(self.from_source_files())
- f.write(f"// Do not edit!\n")
+ self.out.write_raw(f"// This file is generated by {THIS}\n")
+ self.out.write_raw(self.from_source_files())
+ self.out.write_raw(f"// Do not edit!\n")
- # Create formatter; the rest of the code uses this
- self.out = Formatter(f, 0)
self.write_stack_effect_functions()
@@ -1302,13 +1353,13 @@ def write_metadata_for_macro(self, mac: MacroInstruction) -> None:
def write_instructions(self) -> None:
"""Write instructions to output file."""
with open(self.output_filename, "w") as f:
- # Write provenance header
- f.write(f"// This file is generated by {THIS}\n")
- f.write(self.from_source_files())
- f.write(f"// Do not edit!\n")
+ # Create formatter
+ self.out = Formatter(f, 8, self.emit_line_directives)
- # Create formatter; the rest of the code uses this
- self.out = Formatter(f, 8)
+ # Write provenance header
+ self.out.write_raw(f"// This file is generated by {THIS}\n")
+ self.out.write_raw(self.from_source_files())
+ self.out.write_raw(f"// Do not edit!\n")
# Write and count instructions of all kinds
n_instrs = 0
@@ -1478,13 +1529,16 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction):
self.out.emit(f"DISPATCH();")
-def extract_block_text(block: parser.Block) -> tuple[list[str], bool, list[str]]:
+def extract_block_text(block: parser.Block) -> tuple[list[str], bool, list[str], int]:
# Get lines of text with proper dedent
blocklines = block.text.splitlines(True)
+ first_token: lx.Token = block.tokens[0] # IndexError means the context is broken
+ block_line = first_token.begin[0]
# Remove blank lines from both ends
while blocklines and not blocklines[0].strip():
blocklines.pop(0)
+ block_line += 1
while blocklines and not blocklines[-1].strip():
blocklines.pop()
@@ -1493,6 +1547,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], bool, list[str]]
assert blocklines and blocklines[-1].strip() == "}"
blocklines.pop()
blocklines.pop(0)
+ block_line += 1
# Remove trailing blank lines
while blocklines and not blocklines[-1].strip():
@@ -1512,7 +1567,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], bool, list[str]]
predictions.insert(0, m.group(1))
blocklines.pop()
- return blocklines, check_eval_breaker, predictions
+ return blocklines, check_eval_breaker, predictions, block_line
def always_exits(lines: list[str]) -> bool:
@@ -1548,6 +1603,8 @@ def main():
if len(args.input) == 0:
args.input.append(DEFAULT_INPUT)
a = Analyzer(args.input, args.output, args.metadata) # Raises OSError if input unreadable
+ if args.emit_line_directives:
+ a.emit_line_directives = True
a.parse() # Raises SyntaxError on failure
a.analyze() # Prints messages and sets a.errors on failure
if a.errors:
From 7204c41d7df1b5c1669d20df998e035ceadbb034 Mon Sep 17 00:00:00 2001
From: Alex Waygood
Date: Wed, 15 Mar 2023 18:19:07 +0000
Subject: [PATCH 116/280] Exclude `ceval.c` from the C-analyzer tool (#102735)
The "check if generated files are up to date" CI check appears to be currently failing on all PRs (but not on pushes to main)
See, for example:
- https://github.com/python/cpython/pull/94468
- https://github.com/python/cpython/pull/94468
- https://github.com/python/cpython/pull/102731
This appears to be because the C-analyzer tool doesn't like the `#line` directives introduced in https://github.com/python/cpython/commit/70185de1abfe428049a5c43d58fcb656b46db96c. I'm advised by the message printed to the terminal in https://github.com/python/cpython/actions/runs/4428706945/jobs/7768216988#step:14:84 that this is the appropriate short-term fix!
---
Tools/c-analyzer/cpython/_parser.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Tools/c-analyzer/cpython/_parser.py b/Tools/c-analyzer/cpython/_parser.py
index 6da4fbbf4224f1..a2911e030ffee1 100644
--- a/Tools/c-analyzer/cpython/_parser.py
+++ b/Tools/c-analyzer/cpython/_parser.py
@@ -91,10 +91,15 @@ def clean_lines(text):
# XXX Fix the parser.
EXCLUDED += clean_lines('''
# The tool should be able to parse these...
+
# The problem with xmlparse.c is that something
# has gone wrong where # we handle "maybe inline actual"
# in Tools/c-analyzer/c_parser/parser/_global.py.
Modules/expat/xmlparse.c
+
+# The parser doesn't like the #line directives
+# that originate from generated_cases.c.h
+Python/ceval.c
''')
INCL_DIRS = clean_lines('''
From b537248ce7e294e8adaf4c864695a2906f55e6f7 Mon Sep 17 00:00:00 2001
From: Martin Breuss
Date: Wed, 15 Mar 2023 20:18:18 +0100
Subject: [PATCH 117/280] Fix typo in code comment (#102726)
---
Lib/test/test_traceback.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 92c5a000585855..7ef93b3f0ac332 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -2987,7 +2987,7 @@ class MyClass:
def test_getattr_suggestions_do_not_trigger_for_big_dicts(self):
class A:
blech = None
- # A class with a very big __dict__ will not be consider
+ # A class with a very big __dict__ will not be considered
# for suggestions.
for index in range(2000):
setattr(A, f"index_{index}", None)
From 824309ed60032609140df2e6f00a32c93c6b1183 Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Wed, 15 Mar 2023 15:15:23 -0500
Subject: [PATCH 118/280] Simplify and speed-up math.hypot() and math.dist()
(GH-102734)
---
Modules/mathmodule.c | 293 ++++++++++++++++++++-----------------------
1 file changed, 139 insertions(+), 154 deletions(-)
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 544560e8322c72..ae9e3211c072d8 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -92,6 +92,113 @@ get_math_module_state(PyObject *module)
return (math_module_state *)state;
}
+/*
+Double and triple length extended precision algorithms from:
+
+ Accurate Sum and Dot Product
+ by Takeshi Ogita, Siegfried M. Rump, and Shin’Ichi Oishi
+ https://doi.org/10.1137/030601818
+ https://www.tuhh.de/ti3/paper/rump/OgRuOi05.pdf
+
+*/
+
+typedef struct{ double hi; double lo; } DoubleLength;
+
+static DoubleLength
+dl_fast_sum(double a, double b)
+{
+ /* Algorithm 1.1. Compensated summation of two floating point numbers. */
+ assert(fabs(a) >= fabs(b));
+ double x = a + b;
+ double y = (a - x) + b;
+ return (DoubleLength) {x, y};
+}
+
+static DoubleLength
+dl_sum(double a, double b)
+{
+ /* Algorithm 3.1 Error-free transformation of the sum */
+ double x = a + b;
+ double z = x - a;
+ double y = (a - (x - z)) + (b - z);
+ return (DoubleLength) {x, y};
+}
+
+#ifndef UNRELIABLE_FMA
+
+static DoubleLength
+dl_mul(double x, double y)
+{
+ /* Algorithm 3.5. Error-free transformation of a product */
+ double z = x * y;
+ double zz = fma(x, y, -z);
+ return (DoubleLength) {z, zz};
+}
+
+#else
+
+/*
+ The default implementation of dl_mul() depends on the C math library
+ having an accurate fma() function as required by § 7.12.13.1 of the
+ C99 standard.
+
+ The UNRELIABLE_FMA option is provided as a slower but accurate
+ alternative for builds where the fma() function is found wanting.
+ The speed penalty may be modest (17% slower on an Apple M1 Max),
+ so don't hesitate to enable this build option.
+
+ The algorithms are from the T. J. Dekker paper:
+ A Floating-Point Technique for Extending the Available Precision
+ https://csclub.uwaterloo.ca/~pbarfuss/dekker1971.pdf
+*/
+
+static DoubleLength
+dl_split(double x) {
+ // Dekker (5.5) and (5.6).
+ double t = x * 134217729.0; // Veltkamp constant = 2.0 ** 27 + 1
+ double hi = t - (t - x);
+ double lo = x - hi;
+ return (DoubleLength) {hi, lo};
+}
+
+static DoubleLength
+dl_mul(double x, double y)
+{
+ // Dekker (5.12) and mul12()
+ DoubleLength xx = dl_split(x);
+ DoubleLength yy = dl_split(y);
+ double p = xx.hi * yy.hi;
+ double q = xx.hi * yy.lo + xx.lo * yy.hi;
+ double z = p + q;
+ double zz = p - z + q + xx.lo * yy.lo;
+ return (DoubleLength) {z, zz};
+}
+
+#endif
+
+typedef struct { double hi; double lo; double tiny; } TripleLength;
+
+static const TripleLength tl_zero = {0.0, 0.0, 0.0};
+
+static TripleLength
+tl_fma(double x, double y, TripleLength total)
+{
+ /* Algorithm 5.10 with SumKVert for K=3 */
+ DoubleLength pr = dl_mul(x, y);
+ DoubleLength sm = dl_sum(total.hi, pr.hi);
+ DoubleLength r1 = dl_sum(total.lo, pr.lo);
+ DoubleLength r2 = dl_sum(r1.hi, sm.lo);
+ return (TripleLength) {sm.hi, r2.hi, total.tiny + r1.lo + r2.lo};
+}
+
+static double
+tl_to_d(TripleLength total)
+{
+ DoubleLength last = dl_sum(total.lo, total.hi);
+ return total.tiny + last.lo + last.hi;
+}
+
+
/*
sin(pi*x), giving accurate results for all finite x (especially x
integral or close to an integer). This is here for use in the
@@ -2301,6 +2408,7 @@ that are almost always correctly rounded, four techniques are used:
* lossless scaling using a power-of-two scaling factor
* accurate squaring using Veltkamp-Dekker splitting [1]
+ or an equivalent with an fma() call
* compensated summation using a variant of the Neumaier algorithm [2]
* differential correction of the square root [3]
@@ -2359,14 +2467,21 @@ algorithm, effectively doubling the number of accurate bits.
This technique is used in Dekker's SQRT2 algorithm and again in
Borges' ALGORITHM 4 and 5.
-Without proof for all cases, hypot() cannot claim to be always
-correctly rounded. However for n <= 1000, prior to the final addition
-that rounds the overall result, the internal accuracy of "h" together
-with its correction of "x / (2.0 * h)" is at least 100 bits. [6]
-Also, hypot() was tested against a Decimal implementation with
-prec=300. After 100 million trials, no incorrectly rounded examples
-were found. In addition, perfect commutativity (all permutations are
-exactly equal) was verified for 1 billion random inputs with n=5. [7]
+The hypot() function is faithfully rounded (less than 1 ulp error)
+and usually correctly rounded (within 1/2 ulp). The squaring
+step is exact. The Neumaier summation computes as if in doubled
+precision (106 bits) and has the advantage that its input squares
+are non-negative so that the condition number of the sum is one.
+The square root with a differential correction is likewise computed
+as if in double precision.
+
+For n <= 1000, prior to the final addition that rounds the overall
+result, the internal accuracy of "h" together with its correction of
+"x / (2.0 * h)" is at least 100 bits. [6] Also, hypot() was tested
+against a Decimal implementation with prec=300. After 100 million
+trials, no incorrectly rounded examples were found. In addition,
+perfect commutativity (all permutations are exactly equal) was
+verified for 1 billion random inputs with n=5. [7]
References:
@@ -2383,9 +2498,8 @@ exactly equal) was verified for 1 billion random inputs with n=5. [7]
static inline double
vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
{
- const double T27 = 134217729.0; /* ldexp(1.0, 27) + 1.0) */
- double x, scale, oldcsum, csum = 1.0, frac1 = 0.0, frac2 = 0.0, frac3 = 0.0;
- double t, hi, lo, h;
+ double x, h, scale, oldcsum, csum = 1.0, frac1 = 0.0, frac2 = 0.0;
+ DoubleLength pr, sm;
int max_e;
Py_ssize_t i;
@@ -2410,54 +2524,21 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
x *= scale;
assert(fabs(x) < 1.0);
- t = x * T27;
- hi = t - (t - x);
- lo = x - hi;
- assert(hi + lo == x);
-
- x = hi * hi;
- assert(x <= 1.0);
- assert(fabs(csum) >= fabs(x));
- oldcsum = csum;
- csum += x;
- frac1 += (oldcsum - csum) + x;
-
- x = 2.0 * hi * lo;
- assert(fabs(csum) >= fabs(x));
- oldcsum = csum;
- csum += x;
- frac2 += (oldcsum - csum) + x;
-
- assert(csum + lo * lo == csum);
- frac3 += lo * lo;
- }
- h = sqrt(csum - 1.0 + (frac1 + frac2 + frac3));
-
- x = h;
- t = x * T27;
- hi = t - (t - x);
- lo = x - hi;
- assert (hi + lo == x);
+ pr = dl_mul(x, x);
+ assert(pr.hi <= 1.0);
- x = -hi * hi;
- assert(fabs(csum) >= fabs(x));
- oldcsum = csum;
- csum += x;
- frac1 += (oldcsum - csum) + x;
-
- x = -2.0 * hi * lo;
- assert(fabs(csum) >= fabs(x));
- oldcsum = csum;
- csum += x;
- frac2 += (oldcsum - csum) + x;
-
- x = -lo * lo;
- assert(fabs(csum) >= fabs(x));
- oldcsum = csum;
- csum += x;
- frac3 += (oldcsum - csum) + x;
-
- x = csum - 1.0 + (frac1 + frac2 + frac3);
+ sm = dl_fast_sum(csum, pr.hi);
+ csum = sm.hi;
+ frac1 += pr.lo;
+ frac2 += sm.lo;
+ }
+ h = sqrt(csum - 1.0 + (frac1 + frac2));
+ pr = dl_mul(-h, h);
+ sm = dl_fast_sum(csum, pr.hi);
+ csum = sm.hi;
+ frac1 += pr.lo;
+ frac2 += sm.lo;
+ x = csum - 1.0 + (frac1 + frac2);
return (h + x / (2.0 * h)) / scale;
}
/* When max_e < -1023, ldexp(1.0, -max_e) overflows.
@@ -2646,102 +2727,6 @@ long_add_would_overflow(long a, long b)
return (a > 0) ? (b > LONG_MAX - a) : (b < LONG_MIN - a);
}
-/*
-Double and triple length extended precision algorithms from:
-
- Accurate Sum and Dot Product
- by Takeshi Ogita, Siegfried M. Rump, and Shin’Ichi Oishi
- https://doi.org/10.1137/030601818
- https://www.tuhh.de/ti3/paper/rump/OgRuOi05.pdf
-
-*/
-
-typedef struct{ double hi; double lo; } DoubleLength;
-
-static DoubleLength
-dl_sum(double a, double b)
-{
- /* Algorithm 3.1 Error-free transformation of the sum */
- double x = a + b;
- double z = x - a;
- double y = (a - (x - z)) + (b - z);
- return (DoubleLength) {x, y};
-}
-
-#ifndef UNRELIABLE_FMA
-
-static DoubleLength
-dl_mul(double x, double y)
-{
- /* Algorithm 3.5. Error-free transformation of a product */
- double z = x * y;
- double zz = fma(x, y, -z);
- return (DoubleLength) {z, zz};
-}
-
-#else
-
-/*
- The default implementation of dl_mul() depends on the C math library
- having an accurate fma() function as required by § 7.12.13.1 of the
- C99 standard.
-
- The UNRELIABLE_FMA option is provided as a slower but accurate
- alternative for builds where the fma() function is found wanting.
- The speed penalty may be modest (17% slower on an Apple M1 Max),
- so don't hesitate to enable this build option.
-
- The algorithms are from the T. J. Dekker paper:
- A Floating-Point Technique for Extending the Available Precision
- https://csclub.uwaterloo.ca/~pbarfuss/dekker1971.pdf
-*/
-
-static DoubleLength
-dl_split(double x) {
- // Dekker (5.5) and (5.6).
- double t = x * 134217729.0; // Veltkamp constant = 2.0 ** 27 + 1
- double hi = t - (t - x);
- double lo = x - hi;
- return (DoubleLength) {hi, lo};
-}
-
-static DoubleLength
-dl_mul(double x, double y)
-{
- // Dekker (5.12) and mul12()
- DoubleLength xx = dl_split(x);
- DoubleLength yy = dl_split(y);
- double p = xx.hi * yy.hi;
- double q = xx.hi * yy.lo + xx.lo * yy.hi;
- double z = p + q;
- double zz = p - z + q + xx.lo * yy.lo;
- return (DoubleLength) {z, zz};
-}
-
-#endif
-
-typedef struct { double hi; double lo; double tiny; } TripleLength;
-
-static const TripleLength tl_zero = {0.0, 0.0, 0.0};
-
-static TripleLength
-tl_fma(double x, double y, TripleLength total)
-{
- /* Algorithm 5.10 with SumKVert for K=3 */
- DoubleLength pr = dl_mul(x, y);
- DoubleLength sm = dl_sum(total.hi, pr.hi);
- DoubleLength r1 = dl_sum(total.lo, pr.lo);
- DoubleLength r2 = dl_sum(r1.hi, sm.lo);
- return (TripleLength) {sm.hi, r2.hi, total.tiny + r1.lo + r2.lo};
-}
-
-static double
-tl_to_d(TripleLength total)
-{
- DoubleLength last = dl_sum(total.lo, total.hi);
- return total.tiny + last.lo + last.hi;
-}
-
/*[clinic input]
math.sumprod
From bc1ff3a48934ddfca6772256195a69bb1143028d Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Wed, 15 Mar 2023 21:25:31 +0000
Subject: [PATCH 119/280] gh-102738: remove from cases generator the code
related to register instructions (#102739)
---
.../pycore_global_objects_fini_generated.h | 1 +
Include/internal/pycore_global_strings.h | 1 +
.../internal/pycore_runtime_init_generated.h | 1 +
.../internal/pycore_unicodeobject_generated.h | 3 +
Python/opcode_metadata.h | 398 +++++++++---------
Tools/cases_generator/generate_cases.py | 190 +++------
6 files changed, 267 insertions(+), 327 deletions(-)
diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h
index 4b12ae523c3260..14dfd9ea5823ed 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -995,6 +995,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(kw2));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(lambda));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_exc));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_node));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_traceback));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_type));
diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h
index 17fb9ffbbf9f11..6f430bb25eb8d3 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -481,6 +481,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(kw2)
STRUCT_FOR_ID(lambda)
STRUCT_FOR_ID(last)
+ STRUCT_FOR_ID(last_exc)
STRUCT_FOR_ID(last_node)
STRUCT_FOR_ID(last_traceback)
STRUCT_FOR_ID(last_type)
diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h
index b240be57369d9d..0452c4c61551de 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -987,6 +987,7 @@ extern "C" {
INIT_ID(kw2), \
INIT_ID(lambda), \
INIT_ID(last), \
+ INIT_ID(last_exc), \
INIT_ID(last_node), \
INIT_ID(last_traceback), \
INIT_ID(last_type), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h
index fea9b6dbb1a75f..0a8865942e6d5b 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -1296,6 +1296,9 @@ _PyUnicode_InitStaticStrings(void) {
string = &_Py_ID(last);
assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
+ string = &_Py_ID(last_exc);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ PyUnicode_InternInPlace(&string);
string = &_Py_ID(last_node);
assert(_PyUnicode_CheckConsistency(string, 1));
PyUnicode_InternInPlace(&string);
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 24a861100bca47..5a00fe70a7c0bf 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -815,12 +815,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
}
#endif
-enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };
enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 };
struct opcode_metadata {
- enum Direction dir_op1;
- enum Direction dir_op2;
- enum Direction dir_op3;
bool valid_entry;
enum InstructionFormat instr_format;
};
@@ -829,202 +825,202 @@ struct opcode_metadata {
extern const struct opcode_metadata _PyOpcode_opcode_metadata[256];
#else
const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
- [NOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [RESUME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [RESUME_QUICK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_CLOSURE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_FAST_CHECK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_FAST_NO_INCREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_FAST_BOXED_UNBOXED] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_FAST_UNBOXED_BOXED] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_FAST_UNBOXED_UNBOXED] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [LOAD_FAST__LOAD_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [STORE_FAST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [STORE_FAST__STORE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [LOAD_CONST__LOAD_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBIB },
- [POP_TOP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [POP_TOP_NO_DECREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [PUSH_NULL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [END_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [UNARY_NEGATIVE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [UNARY_NOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [UNARY_INVERT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BINARY_OP_MULTIPLY_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_MULTIPLY_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_SUBTRACT_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_SUBTRACT_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_OP_INPLACE_ADD_UNICODE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BINARY_OP_ADD_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_CHECK_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [UNARY_CHECK_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BINARY_OP_ADD_FLOAT_UNBOXED] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [UNBOX_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BOX_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BINARY_OP_ADD_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [BINARY_CHECK_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BINARY_OP_ADD_INT_REST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BINARY_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [BINARY_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [STORE_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BINARY_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [BINARY_SUBSCR_TUPLE_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [BINARY_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [BINARY_SUBSCR_GETITEM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [SET_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [STORE_SUBSCR_LIST_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [STORE_SUBSCR_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [DELETE_SUBSCR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [CALL_INTRINSIC_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL_INTRINSIC_2] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [RAISE_VARARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [INTERPRETER_EXIT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [RETURN_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [RETURN_CONST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [SEND_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [END_ASYNC_FOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [CLEANUP_THROW] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [LOAD_ASSERTION_ERROR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [LOAD_BUILD_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [DELETE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DELETE_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_GLOBAL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [LOAD_GLOBAL_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [LOAD_GLOBAL_BUILTIN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [DELETE_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [MAKE_CELL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DELETE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_CLASSDEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [STORE_DEREF] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [COPY_FREE_VARS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_STRING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LIST_EXTEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [SET_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BUILD_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [SETUP_ANNOTATIONS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BUILD_CONST_KEY_MAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DICT_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [DICT_MERGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [MAP_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [LOAD_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [LOAD_ATTR_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [LOAD_ATTR_PROPERTY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [STORE_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [STORE_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [STORE_ATTR_SLOT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC000 },
- [COMPARE_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [COMPARE_AND_BRANCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
- [COMPARE_AND_BRANCH_FLOAT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
- [COMPARE_AND_BRANCH_INT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
- [COMPARE_AND_BRANCH_STR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC0 },
- [IS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CONTAINS_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CHECK_EG_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [CHECK_EXC_MATCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [IMPORT_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [IMPORT_FROM] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [JUMP_FORWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [JUMP_BACKWARD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [JUMP_BACKWARD_QUICK] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [POP_JUMP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BB_TEST_POP_IF_FALSE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [POP_JUMP_IF_TRUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BB_TEST_POP_IF_TRUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [POP_JUMP_IF_NOT_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BB_TEST_POP_IF_NOT_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [POP_JUMP_IF_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BB_TEST_POP_IF_NONE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [JUMP_IF_FALSE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BB_TEST_IF_FALSE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [JUMP_IF_TRUE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BB_TEST_IF_TRUE_OR_POP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [JUMP_BACKWARD_NO_INTERRUPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [GET_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [MATCH_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [MATCH_MAPPING] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [MATCH_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [MATCH_KEYS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BB_TEST_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
- [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [WITH_EXCEPT_START] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [PUSH_EXC_INFO] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [LOAD_ATTR_METHOD_WITH_VALUES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [LOAD_ATTR_METHOD_NO_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [LOAD_ATTR_METHOD_LAZY_DICT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
- [KW_NAMES] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CALL] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_BOUND_METHOD_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_PY_EXACT_ARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_PY_WITH_DEFAULTS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_TYPE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_STR_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_TUPLE_1] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_BUILTIN_CLASS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_BUILTIN_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_BUILTIN_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_LEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_ISINSTANCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_LIST_APPEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
- [CALL_FUNCTION_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [MAKE_FUNCTION] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [RETURN_GENERATOR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BUILD_SLICE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [FORMAT_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [COPY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [BINARY_OP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [SWAP] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [EXTENDED_ARG] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
- [CACHE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
- [BB_BRANCH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BB_BRANCH_IF_FLAG_UNSET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BB_JUMP_IF_FLAG_UNSET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BB_BRANCH_IF_FLAG_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BB_JUMP_IF_FLAG_SET] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
- [BB_JUMP_BACKWARD_LAZY] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
+ [NOP] = { true, INSTR_FMT_IX },
+ [RESUME] = { true, INSTR_FMT_IX },
+ [RESUME_QUICK] = { true, INSTR_FMT_IB },
+ [LOAD_CLOSURE] = { true, INSTR_FMT_IB },
+ [LOAD_FAST_CHECK] = { true, INSTR_FMT_IB },
+ [LOAD_FAST] = { true, INSTR_FMT_IB },
+ [LOAD_FAST_NO_INCREF] = { true, INSTR_FMT_IB },
+ [LOAD_CONST] = { true, INSTR_FMT_IB },
+ [STORE_FAST] = { true, INSTR_FMT_IB },
+ [STORE_FAST_BOXED_UNBOXED] = { true, INSTR_FMT_IB },
+ [STORE_FAST_UNBOXED_BOXED] = { true, INSTR_FMT_IB },
+ [STORE_FAST_UNBOXED_UNBOXED] = { true, INSTR_FMT_IB },
+ [LOAD_FAST__LOAD_FAST] = { true, INSTR_FMT_IBIB },
+ [LOAD_FAST__LOAD_CONST] = { true, INSTR_FMT_IBIB },
+ [STORE_FAST__LOAD_FAST] = { true, INSTR_FMT_IBIB },
+ [STORE_FAST__STORE_FAST] = { true, INSTR_FMT_IBIB },
+ [LOAD_CONST__LOAD_FAST] = { true, INSTR_FMT_IBIB },
+ [POP_TOP] = { true, INSTR_FMT_IX },
+ [POP_TOP_NO_DECREF] = { true, INSTR_FMT_IX },
+ [PUSH_NULL] = { true, INSTR_FMT_IX },
+ [END_FOR] = { true, INSTR_FMT_IB },
+ [UNARY_NEGATIVE] = { true, INSTR_FMT_IX },
+ [UNARY_NOT] = { true, INSTR_FMT_IX },
+ [UNARY_INVERT] = { true, INSTR_FMT_IX },
+ [BINARY_OP_MULTIPLY_INT] = { true, INSTR_FMT_IXC },
+ [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC },
+ [BINARY_OP_SUBTRACT_INT] = { true, INSTR_FMT_IXC },
+ [BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC },
+ [BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC },
+ [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IX },
+ [BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC },
+ [BINARY_CHECK_FLOAT] = { true, INSTR_FMT_IX },
+ [UNARY_CHECK_FLOAT] = { true, INSTR_FMT_IB },
+ [BINARY_OP_ADD_FLOAT_UNBOXED] = { true, INSTR_FMT_IX },
+ [UNBOX_FLOAT] = { true, INSTR_FMT_IB },
+ [BOX_FLOAT] = { true, INSTR_FMT_IB },
+ [BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC },
+ [BINARY_CHECK_INT] = { true, INSTR_FMT_IX },
+ [BINARY_OP_ADD_INT_REST] = { true, INSTR_FMT_IX },
+ [BINARY_SUBSCR] = { true, INSTR_FMT_IXC000 },
+ [BINARY_SLICE] = { true, INSTR_FMT_IX },
+ [STORE_SLICE] = { true, INSTR_FMT_IX },
+ [BINARY_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC000 },
+ [BINARY_SUBSCR_TUPLE_INT] = { true, INSTR_FMT_IXC000 },
+ [BINARY_SUBSCR_DICT] = { true, INSTR_FMT_IXC000 },
+ [BINARY_SUBSCR_GETITEM] = { true, INSTR_FMT_IXC000 },
+ [LIST_APPEND] = { true, INSTR_FMT_IB },
+ [SET_ADD] = { true, INSTR_FMT_IB },
+ [STORE_SUBSCR] = { true, INSTR_FMT_IXC },
+ [STORE_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC },
+ [STORE_SUBSCR_DICT] = { true, INSTR_FMT_IXC },
+ [DELETE_SUBSCR] = { true, INSTR_FMT_IX },
+ [CALL_INTRINSIC_1] = { true, INSTR_FMT_IB },
+ [CALL_INTRINSIC_2] = { true, INSTR_FMT_IB },
+ [RAISE_VARARGS] = { true, INSTR_FMT_IB },
+ [INTERPRETER_EXIT] = { true, INSTR_FMT_IX },
+ [RETURN_VALUE] = { true, INSTR_FMT_IX },
+ [RETURN_CONST] = { true, INSTR_FMT_IB },
+ [GET_AITER] = { true, INSTR_FMT_IX },
+ [GET_ANEXT] = { true, INSTR_FMT_IX },
+ [GET_AWAITABLE] = { true, INSTR_FMT_IB },
+ [SEND] = { true, INSTR_FMT_IBC },
+ [SEND_GEN] = { true, INSTR_FMT_IBC },
+ [YIELD_VALUE] = { true, INSTR_FMT_IX },
+ [POP_EXCEPT] = { true, INSTR_FMT_IX },
+ [RERAISE] = { true, INSTR_FMT_IB },
+ [END_ASYNC_FOR] = { true, INSTR_FMT_IX },
+ [CLEANUP_THROW] = { true, INSTR_FMT_IX },
+ [LOAD_ASSERTION_ERROR] = { true, INSTR_FMT_IX },
+ [LOAD_BUILD_CLASS] = { true, INSTR_FMT_IX },
+ [STORE_NAME] = { true, INSTR_FMT_IB },
+ [DELETE_NAME] = { true, INSTR_FMT_IB },
+ [UNPACK_SEQUENCE] = { true, INSTR_FMT_IBC },
+ [UNPACK_SEQUENCE_TWO_TUPLE] = { true, INSTR_FMT_IBC },
+ [UNPACK_SEQUENCE_TUPLE] = { true, INSTR_FMT_IBC },
+ [UNPACK_SEQUENCE_LIST] = { true, INSTR_FMT_IBC },
+ [UNPACK_EX] = { true, INSTR_FMT_IB },
+ [STORE_ATTR] = { true, INSTR_FMT_IBC000 },
+ [DELETE_ATTR] = { true, INSTR_FMT_IB },
+ [STORE_GLOBAL] = { true, INSTR_FMT_IB },
+ [DELETE_GLOBAL] = { true, INSTR_FMT_IB },
+ [LOAD_NAME] = { true, INSTR_FMT_IB },
+ [LOAD_GLOBAL] = { true, INSTR_FMT_IBC000 },
+ [LOAD_GLOBAL_MODULE] = { true, INSTR_FMT_IBC000 },
+ [LOAD_GLOBAL_BUILTIN] = { true, INSTR_FMT_IBC000 },
+ [DELETE_FAST] = { true, INSTR_FMT_IB },
+ [MAKE_CELL] = { true, INSTR_FMT_IB },
+ [DELETE_DEREF] = { true, INSTR_FMT_IB },
+ [LOAD_CLASSDEREF] = { true, INSTR_FMT_IB },
+ [LOAD_DEREF] = { true, INSTR_FMT_IB },
+ [STORE_DEREF] = { true, INSTR_FMT_IB },
+ [COPY_FREE_VARS] = { true, INSTR_FMT_IB },
+ [BUILD_STRING] = { true, INSTR_FMT_IB },
+ [BUILD_TUPLE] = { true, INSTR_FMT_IB },
+ [BUILD_LIST] = { true, INSTR_FMT_IB },
+ [LIST_EXTEND] = { true, INSTR_FMT_IB },
+ [SET_UPDATE] = { true, INSTR_FMT_IB },
+ [BUILD_SET] = { true, INSTR_FMT_IB },
+ [BUILD_MAP] = { true, INSTR_FMT_IB },
+ [SETUP_ANNOTATIONS] = { true, INSTR_FMT_IX },
+ [BUILD_CONST_KEY_MAP] = { true, INSTR_FMT_IB },
+ [DICT_UPDATE] = { true, INSTR_FMT_IB },
+ [DICT_MERGE] = { true, INSTR_FMT_IB },
+ [MAP_ADD] = { true, INSTR_FMT_IB },
+ [LOAD_ATTR] = { true, INSTR_FMT_IBC00000000 },
+ [LOAD_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IBC00000000 },
+ [LOAD_ATTR_MODULE] = { true, INSTR_FMT_IBC00000000 },
+ [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000 },
+ [LOAD_ATTR_SLOT] = { true, INSTR_FMT_IBC00000000 },
+ [LOAD_ATTR_CLASS] = { true, INSTR_FMT_IBC00000000 },
+ [LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000 },
+ [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { true, INSTR_FMT_IBC00000000 },
+ [STORE_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IXC000 },
+ [STORE_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC000 },
+ [STORE_ATTR_SLOT] = { true, INSTR_FMT_IXC000 },
+ [COMPARE_OP] = { true, INSTR_FMT_IBC },
+ [COMPARE_AND_BRANCH] = { true, INSTR_FMT_IBC0 },
+ [COMPARE_AND_BRANCH_FLOAT] = { true, INSTR_FMT_IBC0 },
+ [COMPARE_AND_BRANCH_INT] = { true, INSTR_FMT_IBC0 },
+ [COMPARE_AND_BRANCH_STR] = { true, INSTR_FMT_IBC0 },
+ [IS_OP] = { true, INSTR_FMT_IB },
+ [CONTAINS_OP] = { true, INSTR_FMT_IB },
+ [CHECK_EG_MATCH] = { true, INSTR_FMT_IX },
+ [CHECK_EXC_MATCH] = { true, INSTR_FMT_IX },
+ [IMPORT_NAME] = { true, INSTR_FMT_IB },
+ [IMPORT_FROM] = { true, INSTR_FMT_IB },
+ [JUMP_FORWARD] = { true, INSTR_FMT_IB },
+ [JUMP_BACKWARD] = { true, INSTR_FMT_IX },
+ [JUMP_BACKWARD_QUICK] = { true, INSTR_FMT_IB },
+ [POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IB },
+ [BB_TEST_POP_IF_FALSE] = { true, INSTR_FMT_IX },
+ [POP_JUMP_IF_TRUE] = { true, INSTR_FMT_IB },
+ [BB_TEST_POP_IF_TRUE] = { true, INSTR_FMT_IX },
+ [POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IB },
+ [BB_TEST_POP_IF_NOT_NONE] = { true, INSTR_FMT_IX },
+ [POP_JUMP_IF_NONE] = { true, INSTR_FMT_IB },
+ [BB_TEST_POP_IF_NONE] = { true, INSTR_FMT_IX },
+ [JUMP_IF_FALSE_OR_POP] = { true, INSTR_FMT_IB },
+ [BB_TEST_IF_FALSE_OR_POP] = { true, INSTR_FMT_IX },
+ [JUMP_IF_TRUE_OR_POP] = { true, INSTR_FMT_IB },
+ [BB_TEST_IF_TRUE_OR_POP] = { true, INSTR_FMT_IX },
+ [JUMP_BACKWARD_NO_INTERRUPT] = { true, INSTR_FMT_IB },
+ [GET_LEN] = { true, INSTR_FMT_IX },
+ [MATCH_CLASS] = { true, INSTR_FMT_IB },
+ [MATCH_MAPPING] = { true, INSTR_FMT_IX },
+ [MATCH_SEQUENCE] = { true, INSTR_FMT_IX },
+ [MATCH_KEYS] = { true, INSTR_FMT_IX },
+ [GET_ITER] = { true, INSTR_FMT_IX },
+ [GET_YIELD_FROM_ITER] = { true, INSTR_FMT_IX },
+ [FOR_ITER] = { true, INSTR_FMT_IBC },
+ [BB_TEST_ITER] = { true, INSTR_FMT_IXC },
+ [FOR_ITER_LIST] = { true, INSTR_FMT_IBC },
+ [FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC },
+ [FOR_ITER_RANGE] = { true, INSTR_FMT_IBC },
+ [FOR_ITER_GEN] = { true, INSTR_FMT_IBC },
+ [BEFORE_ASYNC_WITH] = { true, INSTR_FMT_IX },
+ [BEFORE_WITH] = { true, INSTR_FMT_IX },
+ [WITH_EXCEPT_START] = { true, INSTR_FMT_IX },
+ [PUSH_EXC_INFO] = { true, INSTR_FMT_IX },
+ [LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC00000000 },
+ [LOAD_ATTR_METHOD_NO_DICT] = { true, INSTR_FMT_IBC00000000 },
+ [LOAD_ATTR_METHOD_LAZY_DICT] = { true, INSTR_FMT_IBC00000000 },
+ [KW_NAMES] = { true, INSTR_FMT_IB },
+ [CALL] = { true, INSTR_FMT_IBC000 },
+ [CALL_BOUND_METHOD_EXACT_ARGS] = { true, INSTR_FMT_IBC000 },
+ [CALL_PY_EXACT_ARGS] = { true, INSTR_FMT_IBC000 },
+ [CALL_PY_WITH_DEFAULTS] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_TYPE_1] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_STR_1] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_TUPLE_1] = { true, INSTR_FMT_IBC000 },
+ [CALL_BUILTIN_CLASS] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_BUILTIN_O] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_BUILTIN_FAST] = { true, INSTR_FMT_IBC000 },
+ [CALL_BUILTIN_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_LEN] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_ISINSTANCE] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_LIST_APPEND] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_METHOD_DESCRIPTOR_O] = { true, INSTR_FMT_IBC000 },
+ [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = { true, INSTR_FMT_IBC000 },
+ [CALL_NO_KW_METHOD_DESCRIPTOR_FAST] = { true, INSTR_FMT_IBC000 },
+ [CALL_FUNCTION_EX] = { true, INSTR_FMT_IB },
+ [MAKE_FUNCTION] = { true, INSTR_FMT_IB },
+ [RETURN_GENERATOR] = { true, INSTR_FMT_IX },
+ [BUILD_SLICE] = { true, INSTR_FMT_IB },
+ [FORMAT_VALUE] = { true, INSTR_FMT_IB },
+ [COPY] = { true, INSTR_FMT_IB },
+ [BINARY_OP] = { true, INSTR_FMT_IBC },
+ [SWAP] = { true, INSTR_FMT_IB },
+ [EXTENDED_ARG] = { true, INSTR_FMT_IB },
+ [CACHE] = { true, INSTR_FMT_IX },
+ [BB_BRANCH] = { true, INSTR_FMT_IBC },
+ [BB_BRANCH_IF_FLAG_UNSET] = { true, INSTR_FMT_IBC },
+ [BB_JUMP_IF_FLAG_UNSET] = { true, INSTR_FMT_IBC },
+ [BB_BRANCH_IF_FLAG_SET] = { true, INSTR_FMT_IBC },
+ [BB_JUMP_IF_FLAG_SET] = { true, INSTR_FMT_IBC },
+ [BB_JUMP_BACKWARD_LAZY] = { true, INSTR_FMT_IB },
};
#endif
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 46223c9dee99be..31b6926f805485 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -283,7 +283,6 @@ class Instruction:
# Parts of the underlying instruction definition
inst: parser.InstDef
- register: bool
kind: parser.INST_KINDS
name: str
block: parser.Block
@@ -301,17 +300,12 @@ class Instruction:
unmoved_names: frozenset[str]
instr_fmt: str
- # Parallel to input_effects; set later
- input_registers: list[str] = dataclasses.field(repr=False)
- output_registers: list[str] = dataclasses.field(repr=False)
-
# Set later
family: parser.Family | None = None
predicted: bool = False
def __init__(self, inst: parser.InstDef):
self.inst = inst
- self.register = inst.register
self.kind = inst.kind
self.name = inst.name
self.block = inst.block
@@ -334,15 +328,10 @@ def __init__(self, inst: parser.InstDef):
else:
break
self.unmoved_names = frozenset(unmoved_names)
- if self.register:
- num_regs = len(self.input_effects) + len(self.output_effects)
- num_dummies = (num_regs // 2) * 2 + 1 - num_regs
- fmt = "I" + "B" * num_regs + "X" * num_dummies
+ if variable_used(inst, "oparg"):
+ fmt = "IB"
else:
- if variable_used(inst, "oparg"):
- fmt = "IB"
- else:
- fmt = "IX"
+ fmt = "IX"
cache = "C"
for ce in self.cache_effects:
for _ in range(ce.size):
@@ -350,20 +339,6 @@ def __init__(self, inst: parser.InstDef):
cache = "0"
self.instr_fmt = fmt
- def analyze_registers(self, a: "Analyzer") -> None:
- regs = iter(("REG(oparg1)", "REG(oparg2)", "REG(oparg3)"))
- try:
- self.input_registers = [
- next(regs) for ieff in self.input_effects if ieff.name != UNUSED
- ]
- self.output_registers = [
- next(regs) for oeff in self.output_effects if oeff.name != UNUSED
- ]
- except StopIteration: # Running out of registers
- a.error(
- f"Instruction {self.name} has too many register effects", node=self.inst
- )
-
def write_typeprop(self, out: Formatter) -> None:
"""Write one instruction's type propagation rules"""
@@ -518,25 +493,19 @@ def write(self, out: Formatter) -> None:
f'{self.cache_offset}, "incorrect cache size");'
)
- if not self.register:
- # Write input stack effect variable declarations and initializations
- ieffects = list(reversed(self.input_effects))
- for i, ieffect in enumerate(ieffects):
- isize = string_effect_size(
- list_effect_size([ieff for ieff in ieffects[: i + 1]])
- )
- if ieffect.size:
- src = StackEffect(f"(stack_pointer - {maybe_parenthesize(isize)})", "PyObject **")
- elif ieffect.cond:
- src = StackEffect(f"({ieffect.cond}) ? stack_pointer[-{maybe_parenthesize(isize)}] : NULL", "")
- else:
- src = StackEffect(f"stack_pointer[-{maybe_parenthesize(isize)}]", "")
- out.declare(ieffect, src)
- else:
- # Write input register variable declarations and initializations
- for ieffect, reg in zip(self.input_effects, self.input_registers):
- src = StackEffect(reg, "")
- out.declare(ieffect, src)
+ # Write input stack effect variable declarations and initializations
+ ieffects = list(reversed(self.input_effects))
+ for i, ieffect in enumerate(ieffects):
+ isize = string_effect_size(
+ list_effect_size([ieff for ieff in ieffects[: i + 1]])
+ )
+ if ieffect.size:
+ src = StackEffect(f"(stack_pointer - {maybe_parenthesize(isize)})", "PyObject **")
+ elif ieffect.cond:
+ src = StackEffect(f"({ieffect.cond}) ? stack_pointer[-{maybe_parenthesize(isize)}] : NULL", "")
+ else:
+ src = StackEffect(f"stack_pointer[-{maybe_parenthesize(isize)}]", "")
+ out.declare(ieffect, src)
# Write output stack effect variable declarations
isize = string_effect_size(list_effect_size(self.input_effects))
@@ -566,32 +535,26 @@ def write(self, out: Formatter) -> None:
if self.always_exits:
return
- if not self.register:
- # Write net stack growth/shrinkage
- out.stack_adjust(
- 0,
- [ieff for ieff in self.input_effects],
- [oeff for oeff in self.output_effects],
- )
+ # Write net stack growth/shrinkage
+ out.stack_adjust(
+ 0,
+ [ieff for ieff in self.input_effects],
+ [oeff for oeff in self.output_effects],
+ )
- # Write output stack effect assignments
- oeffects = list(reversed(self.output_effects))
- for i, oeffect in enumerate(oeffects):
- if oeffect.name in self.unmoved_names:
- continue
- osize = string_effect_size(
- list_effect_size([oeff for oeff in oeffects[: i + 1]])
- )
- if oeffect.size:
- dst = StackEffect(f"stack_pointer - {maybe_parenthesize(osize)}", "PyObject **")
- else:
- dst = StackEffect(f"stack_pointer[-{maybe_parenthesize(osize)}]", "")
- out.assign(dst, oeffect)
- else:
- # Write output register assignments
- for oeffect, reg in zip(self.output_effects, self.output_registers):
- dst = StackEffect(reg, "")
- out.assign(dst, oeffect)
+ # Write output stack effect assignments
+ oeffects = list(reversed(self.output_effects))
+ for i, oeffect in enumerate(oeffects):
+ if oeffect.name in self.unmoved_names:
+ continue
+ osize = string_effect_size(
+ list_effect_size([oeff for oeff in oeffects[: i + 1]])
+ )
+ if oeffect.size:
+ dst = StackEffect(f"stack_pointer - {maybe_parenthesize(osize)}", "PyObject **")
+ else:
+ dst = StackEffect(f"stack_pointer[-{maybe_parenthesize(osize)}]", "")
+ out.assign(dst, oeffect)
# Write cache effect
if self.cache_offset:
@@ -640,19 +603,17 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
# ERROR_IF() must pop the inputs from the stack.
# The code block is responsible for DECREF()ing them.
# NOTE: If the label doesn't exist, just add it to ceval.c.
- if not self.register:
- # Don't pop common input/output effects at the bottom!
- # These aren't DECREF'ed so they can stay.
- ieffs = list(self.input_effects)
- oeffs = list(self.output_effects)
- while ieffs and oeffs and ieffs[0] == oeffs[0]:
- ieffs.pop(0)
- oeffs.pop(0)
- ninputs, symbolic = list_effect_size(ieffs)
- if ninputs:
- label = f"pop_{ninputs}_{label}"
- else:
- symbolic = ""
+
+ # Don't pop common input/output effects at the bottom!
+ # These aren't DECREF'ed so they can stay.
+ ieffs = list(self.input_effects)
+ oeffs = list(self.output_effects)
+ while ieffs and oeffs and ieffs[0] == oeffs[0]:
+ ieffs.pop(0)
+ oeffs.pop(0)
+ ninputs, symbolic = list_effect_size(ieffs)
+ if ninputs:
+ label = f"pop_{ninputs}_{label}"
if symbolic:
out.write_raw(
f"{space}if ({cond}) {{ STACK_SHRINK({symbolic}); goto {label}; }}\n"
@@ -660,21 +621,20 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
else:
out.write_raw(f"{space}if ({cond}) goto {label};{out.postfix}\n")
elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*(?://.*)?$", line):
- if not self.register:
- out.reset_lineno()
- space = extra + m.group(1)
- for ieff in self.input_effects:
- if ieff.name in names_to_skip:
- continue
- if ieff.size:
- out.write_raw(
- f"{space}for (int _i = {ieff.size}; --_i >= 0;) {{\n"
- )
- out.write_raw(f"{space} Py_DECREF({ieff.name}[_i]);\n")
- out.write_raw(f"{space}}}\n")
- else:
- decref = "XDECREF" if ieff.cond else "DECREF"
- out.write_raw(f"{space}Py_{decref}({ieff.name});\n")
+ out.reset_lineno()
+ space = extra + m.group(1)
+ for ieff in self.input_effects:
+ if ieff.name in names_to_skip:
+ continue
+ if ieff.size:
+ out.write_raw(
+ f"{space}for (int _i = {ieff.size}; --_i >= 0;) {{\n"
+ )
+ out.write_raw(f"{space} Py_DECREF({ieff.name}[_i]);\n")
+ out.write_raw(f"{space}}}\n")
+ else:
+ decref = "XDECREF" if ieff.cond else "DECREF"
+ out.write_raw(f"{space}Py_{decref}({ieff.name});\n")
else:
out.write_raw(extra + line.rstrip("\n") + out.postfix + "\n")
out.reset_lineno()
@@ -887,7 +847,6 @@ def analyze(self) -> None:
Raises SystemExit if there is an error.
"""
self.find_predictions()
- self.analyze_register_instrs()
self.analyze_supers_and_macros()
self.map_families()
self.check_families()
@@ -997,11 +956,6 @@ def effect_counts(self, name: str) -> tuple[int, int, int]:
assert False, f"Unknown instruction {name!r}"
return cache, input, output
- def analyze_register_instrs(self) -> None:
- for instr in self.instrs.values():
- if instr.register:
- instr.analyze_registers(self)
-
def analyze_supers_and_macros(self) -> None:
"""Analyze each super- and macro instruction."""
self.super_instrs = {}
@@ -1033,7 +987,7 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction:
stack, initial_sp = self.stack_analysis(components)
sp = initial_sp
parts: list[Component | parser.CacheEffect] = []
- format = "IB" # Macros don't support register instructions yet
+ format = "IB"
cache = "C"
for component in components:
match component:
@@ -1282,13 +1236,9 @@ def write_metadata(self) -> None:
self.write_stack_effect_functions()
# Write type definitions
- self.out.emit("enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };")
self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};")
self.out.emit("struct opcode_metadata {")
with self.out.indent():
- self.out.emit("enum Direction dir_op1;")
- self.out.emit("enum Direction dir_op2;")
- self.out.emit("enum Direction dir_op3;")
self.out.emit("bool valid_entry;")
self.out.emit("enum InstructionFormat instr_format;")
self.out.emit("};")
@@ -1322,32 +1272,20 @@ def write_metadata(self) -> None:
def write_metadata_for_inst(self, instr: Instruction) -> None:
"""Write metadata for a single instruction."""
- dir_op1 = dir_op2 = dir_op3 = "DIR_NONE"
- if instr.kind == "legacy":
- assert not instr.register
- else:
- if instr.register:
- directions: list[str] = []
- directions.extend("DIR_READ" for _ in instr.input_effects)
- directions.extend("DIR_WRITE" for _ in instr.output_effects)
- directions.extend("DIR_NONE" for _ in range(3))
- dir_op1, dir_op2, dir_op3 = directions[:3]
self.out.emit(
- f" [{instr.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},"
+ f" [{instr.name}] = {{ true, {INSTR_FMT_PREFIX}{instr.instr_fmt} }},"
)
def write_metadata_for_super(self, sup: SuperInstruction) -> None:
"""Write metadata for a super-instruction."""
- dir_op1 = dir_op2 = dir_op3 = "DIR_NONE"
self.out.emit(
- f" [{sup.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }},"
+ f" [{sup.name}] = {{ true, {INSTR_FMT_PREFIX}{sup.instr_fmt} }},"
)
def write_metadata_for_macro(self, mac: MacroInstruction) -> None:
"""Write metadata for a macro-instruction."""
- dir_op1 = dir_op2 = dir_op3 = "DIR_NONE"
self.out.emit(
- f" [{mac.name}] = {{ {dir_op1}, {dir_op2}, {dir_op3}, true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }},"
+ f" [{mac.name}] = {{ true, {INSTR_FMT_PREFIX}{mac.instr_fmt} }},"
)
def write_instructions(self) -> None:
From 93685570a9431a34142939e44403beca951739e4 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Wed, 15 Mar 2023 18:43:54 -0600
Subject: [PATCH 120/280] gh-102660: Fix Refleaks in import.c (#102744)
gh-102661 introduced some leaks. This fixes them.
https://github.com/python/cpython/issues/102660
---
Python/bltinmodule.c | 3 ---
Python/import.c | 50 +++++++++++++++++++++++++-------------------
Python/sysmodule.c | 3 ---
3 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index a8a34620b9bcdf..12ca0ba6c4873c 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -3098,9 +3098,6 @@ _PyBuiltin_Init(PyInterpreterState *interp)
}
Py_DECREF(debug);
- /* m_copy of Py_None means it is copied some other way. */
- builtinsmodule.m_base.m_copy = Py_NewRef(Py_None);
-
return mod;
#undef ADD_TO_ALL
#undef SETBUILTIN
diff --git a/Python/import.c b/Python/import.c
index 612fee243bd9e7..9f80c6d8dd49a8 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -978,14 +978,29 @@ _PyImport_CheckSubinterpIncompatibleExtensionAllowed(const char *name)
return 0;
}
-static inline int
-match_mod_name(PyObject *actual, const char *expected)
+static PyObject *
+get_core_module_dict(PyInterpreterState *interp,
+ PyObject *name, PyObject *filename)
{
- if (PyUnicode_CompareWithASCIIString(actual, expected) == 0) {
- return 1;
+ /* Only builtin modules are core. */
+ if (filename == name) {
+ assert(!PyErr_Occurred());
+ if (PyUnicode_CompareWithASCIIString(name, "sys") == 0) {
+ return interp->sysdict_copy;
+ }
+ assert(!PyErr_Occurred());
+ if (PyUnicode_CompareWithASCIIString(name, "builtins") == 0) {
+ return interp->builtins_copy;
+ }
+ assert(!PyErr_Occurred());
}
- assert(!PyErr_Occurred());
- return 0;
+ return NULL;
+}
+
+static inline int
+is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *filename)
+{
+ return get_core_module_dict(interp, name, filename) != NULL;
}
static int
@@ -1009,10 +1024,8 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename)
// bpo-44050: Extensions and def->m_base.m_copy can be updated
// when the extension module doesn't support sub-interpreters.
- // XXX Why special-case the main interpreter?
- if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) {
- /* m_copy of Py_None means it is copied some other way. */
- if (def->m_size == -1 && def->m_base.m_copy != Py_None) {
+ if (def->m_size == -1) {
+ if (!is_core_module(tstate->interp, name, filename)) {
if (def->m_base.m_copy) {
/* Somebody already imported the module,
likely under a different name.
@@ -1028,7 +1041,10 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *filename)
return -1;
}
}
+ }
+ // XXX Why special-case the main interpreter?
+ if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) {
if (_extensions_cache_set(filename, name, def) < 0) {
return -1;
}
@@ -1069,21 +1085,11 @@ import_find_extension(PyThreadState *tstate, PyObject *name,
PyObject *m_copy = def->m_base.m_copy;
/* Module does not support repeated initialization */
if (m_copy == NULL) {
- return NULL;
- }
- else if (m_copy == Py_None) {
- if (match_mod_name(name, "sys")) {
- m_copy = tstate->interp->sysdict_copy;
- }
- else if (match_mod_name(name, "builtins")) {
- m_copy = tstate->interp->builtins_copy;
- }
- else {
- _PyErr_SetString(tstate, PyExc_ImportError, "missing m_copy");
+ m_copy = get_core_module_dict(tstate->interp, name, filename);
+ if (m_copy == NULL) {
return NULL;
}
}
- /* m_copy of Py_None means it is copied some other way. */
mod = import_add_module(tstate, name);
if (mod == NULL) {
return NULL;
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index fc0550266bf1af..d282104dcad414 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -3425,9 +3425,6 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p)
return _PyStatus_ERR("failed to create a module object");
}
- /* m_copy of Py_None means it is copied some other way. */
- sysmodule.m_base.m_copy = Py_NewRef(Py_None);
-
PyObject *sysdict = PyModule_GetDict(sysmod);
if (sysdict == NULL) {
goto error;
From b05f294a1a5761e6a159fcb2f3d5f31f77825977 Mon Sep 17 00:00:00 2001
From: Jamoo721 <81095953+Jamoo721@users.noreply.github.com>
Date: Thu, 16 Mar 2023 13:52:11 +1100
Subject: [PATCH 121/280] gh-102690: Use Edge as fallback in webbrowser instead
of IE (#102691)
---
Lib/webbrowser.py | 12 ++++++++----
.../2023-03-14-10-52-43.gh-issue-102690.sbXtqk.rst | 1 +
2 files changed, 9 insertions(+), 4 deletions(-)
create mode 100644 Misc/NEWS.d/next/Windows/2023-03-14-10-52-43.gh-issue-102690.sbXtqk.rst
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
index 44974d433b4696..a56ff33dbbdc69 100755
--- a/Lib/webbrowser.py
+++ b/Lib/webbrowser.py
@@ -542,11 +542,15 @@ def register_standard_browsers():
# First try to use the default Windows browser
register("windows-default", WindowsDefault)
- # Detect some common Windows browsers, fallback to IE
- iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
- "Internet Explorer\\IEXPLORE.EXE")
+ # Detect some common Windows browsers, fallback to Microsoft Edge
+ # location in 64-bit Windows
+ edge64 = os.path.join(os.environ.get("PROGRAMFILES(x86)", "C:\\Program Files (x86)"),
+ "Microsoft\\Edge\\Application\\msedge.exe")
+ # location in 32-bit Windows
+ edge32 = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
+ "Microsoft\\Edge\\Application\\msedge.exe")
for browser in ("firefox", "firebird", "seamonkey", "mozilla",
- "netscape", "opera", iexplore):
+ "opera", edge64, edge32):
if shutil.which(browser):
register(browser, None, BackgroundBrowser(browser))
else:
diff --git a/Misc/NEWS.d/next/Windows/2023-03-14-10-52-43.gh-issue-102690.sbXtqk.rst b/Misc/NEWS.d/next/Windows/2023-03-14-10-52-43.gh-issue-102690.sbXtqk.rst
new file mode 100644
index 00000000000000..5669ebbb442c24
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2023-03-14-10-52-43.gh-issue-102690.sbXtqk.rst
@@ -0,0 +1 @@
+Update :mod:`webbrowser` to fall back to Microsoft Edge instead of Internet Explorer.
From edd10d063f28af15898b624c49a067c0957e136e Mon Sep 17 00:00:00 2001
From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Date: Thu, 16 Mar 2023 09:20:43 +0530
Subject: [PATCH 122/280] GH-100112: avoid using iterable coroutines in
asyncio internally (#100128)
---
Lib/asyncio/tasks.py | 22 +++++--------------
Lib/test/test_asyncio/test_tasks.py | 15 +++++++++++++
...-03-10-13-51-21.gh-issue-100112.VHh4mw.rst | 1 +
3 files changed, 22 insertions(+), 16 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-10-13-51-21.gh-issue-100112.VHh4mw.rst
diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
index 1c20754b839b69..c90d32c97add78 100644
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -25,7 +25,6 @@
from . import exceptions
from . import futures
from . import timeouts
-from .coroutines import _is_coroutine
# Helper to generate new task names
# This uses itertools.count() instead of a "+= 1" operation because the latter
@@ -635,11 +634,14 @@ def ensure_future(coro_or_future, *, loop=None):
raise ValueError('The future belongs to a different loop than '
'the one specified as the loop argument')
return coro_or_future
- called_wrap_awaitable = False
+ should_close = True
if not coroutines.iscoroutine(coro_or_future):
if inspect.isawaitable(coro_or_future):
+ async def _wrap_awaitable(awaitable):
+ return await awaitable
+
coro_or_future = _wrap_awaitable(coro_or_future)
- called_wrap_awaitable = True
+ should_close = False
else:
raise TypeError('An asyncio.Future, a coroutine or an awaitable '
'is required')
@@ -649,23 +651,11 @@ def ensure_future(coro_or_future, *, loop=None):
try:
return loop.create_task(coro_or_future)
except RuntimeError:
- if not called_wrap_awaitable:
+ if should_close:
coro_or_future.close()
raise
-@types.coroutine
-def _wrap_awaitable(awaitable):
- """Helper for asyncio.ensure_future().
-
- Wraps awaitable (an object with __await__) into a coroutine
- that will later be wrapped in a Task by ensure_future().
- """
- return (yield from awaitable.__await__())
-
-_wrap_awaitable._is_coroutine = _is_coroutine
-
-
class _GatheringFuture(futures.Future):
"""Helper for gather().
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index e533d5273e9f38..5b935b526541a1 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -8,6 +8,7 @@
import re
import sys
import traceback
+import types
import unittest
from unittest import mock
from types import GenericAlias
@@ -274,6 +275,20 @@ async def coro():
loop.run_until_complete(fut)
self.assertEqual(fut.result(), 'ok')
+ def test_ensure_future_task_awaitable(self):
+ class Aw:
+ def __await__(self):
+ return asyncio.sleep(0, result='ok').__await__()
+
+ loop = asyncio.new_event_loop()
+ self.set_event_loop(loop)
+ task = asyncio.ensure_future(Aw(), loop=loop)
+ loop.run_until_complete(task)
+ self.assertTrue(task.done())
+ self.assertEqual(task.result(), 'ok')
+ self.assertIsInstance(task.get_coro(), types.CoroutineType)
+ loop.close()
+
def test_ensure_future_neither(self):
with self.assertRaises(TypeError):
asyncio.ensure_future('ok')
diff --git a/Misc/NEWS.d/next/Library/2023-03-10-13-51-21.gh-issue-100112.VHh4mw.rst b/Misc/NEWS.d/next/Library/2023-03-10-13-51-21.gh-issue-100112.VHh4mw.rst
new file mode 100644
index 00000000000000..eff77c40e30c48
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-10-13-51-21.gh-issue-100112.VHh4mw.rst
@@ -0,0 +1 @@
+:meth:`asyncio.Task.get_coro` now always returns a coroutine when wrapping an awaitable object. Patch by Kumar Aditya.
From f9f542d7e3de3f5989a40328438b55641b6ea432 Mon Sep 17 00:00:00 2001
From: yonatanp
Date: Thu, 16 Mar 2023 00:44:52 -0400
Subject: [PATCH 123/280] gh-94440: Fix issue of ProcessPoolExecutor shutdown
hanging (#94468)
Fix an issue of concurrent.futures ProcessPoolExecutor shutdown hanging.
Co-authored-by: Alex Waygood
---
Lib/concurrent/futures/process.py | 5 ++++
Lib/test/test_concurrent_futures.py | 28 +++++++++++++++++++
Misc/ACKS | 1 +
...2-06-30-21-28-41.gh-issue-94440.LtgX0d.rst | 2 ++
4 files changed, 36 insertions(+)
create mode 100644 Misc/NEWS.d/next/Library/2022-06-30-21-28-41.gh-issue-94440.LtgX0d.rst
diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py
index 3a8637b6fa1b47..816edab99f63e3 100644
--- a/Lib/concurrent/futures/process.py
+++ b/Lib/concurrent/futures/process.py
@@ -366,6 +366,11 @@ def run(self):
if self.is_shutting_down():
self.flag_executor_shutting_down()
+ # When only canceled futures remain in pending_work_items, our
+ # next call to wait_result_broken_or_wakeup would hang forever.
+ # This makes sure we have some running futures or none at all.
+ self.add_call_item_to_queue()
+
# Since no new work items can be added, it is safe to shutdown
# this thread if there are no pending work items.
if not self.pending_work_items:
diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py
index b3520ae3994e03..a20cb844a293c9 100644
--- a/Lib/test/test_concurrent_futures.py
+++ b/Lib/test/test_concurrent_futures.py
@@ -14,6 +14,7 @@
from logging.handlers import QueueHandler
import os
import queue
+import signal
import sys
import threading
import time
@@ -397,6 +398,33 @@ def test_hang_gh83386(self):
self.assertFalse(err)
self.assertEqual(out.strip(), b"apple")
+ def test_hang_gh94440(self):
+ """shutdown(wait=True) doesn't hang when a future was submitted and
+ quickly canceled right before shutdown.
+
+ See https://github.com/python/cpython/issues/94440.
+ """
+ if not hasattr(signal, 'alarm'):
+ raise unittest.SkipTest(
+ "Tested platform does not support the alarm signal")
+
+ def timeout(_signum, _frame):
+ raise RuntimeError("timed out waiting for shutdown")
+
+ kwargs = {}
+ if getattr(self, 'ctx', None):
+ kwargs['mp_context'] = self.get_context()
+ executor = self.executor_type(max_workers=1, **kwargs)
+ executor.submit(int).result()
+ old_handler = signal.signal(signal.SIGALRM, timeout)
+ try:
+ signal.alarm(5)
+ executor.submit(int).cancel()
+ executor.shutdown(wait=True)
+ finally:
+ signal.alarm(0)
+ signal.signal(signal.SIGALRM, old_handler)
+
class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest, BaseTestCase):
def test_threads_terminate(self):
diff --git a/Misc/ACKS b/Misc/ACKS
index 7bbde3af99782b..8cf5166a2bb1f4 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1385,6 +1385,7 @@ Thomas Perl
Mathieu Perreault
Mark Perrego
Trevor Perrin
+Yonatan Perry
Gabriel de Perthuis
Tim Peters
Benjamin Peterson
diff --git a/Misc/NEWS.d/next/Library/2022-06-30-21-28-41.gh-issue-94440.LtgX0d.rst b/Misc/NEWS.d/next/Library/2022-06-30-21-28-41.gh-issue-94440.LtgX0d.rst
new file mode 100644
index 00000000000000..3eee82e59dfafb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-06-30-21-28-41.gh-issue-94440.LtgX0d.rst
@@ -0,0 +1,2 @@
+Fix a :mod:`concurrent.futures.process` bug where ``ProcessPoolExecutor`` shutdown
+could hang after a future has been quickly submitted and canceled.
From a965c5e755161e922f58e5a5b81f8ee5ea361004 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Thu, 16 Mar 2023 10:16:01 +0000
Subject: [PATCH 124/280] gh-102594: PyErr_SetObject adds note to exception
raised on normalization error (#102675)
---
Include/cpython/pyerrors.h | 4 ++
Lib/test/test_capi/test_exceptions.py | 20 ++++++++++
...-03-14-00-11-46.gh-issue-102594.BjU-m2.rst | 1 +
Modules/_testcapi/exceptions.c | 21 ++++++++++
Objects/exceptions.c | 15 +++++++
Python/errors.c | 40 ++++++++++++++++---
6 files changed, 96 insertions(+), 5 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-14-00-11-46.gh-issue-102594.BjU-m2.rst
diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h
index 0d9cc9922f7368..d0300f6ee56a25 100644
--- a/Include/cpython/pyerrors.h
+++ b/Include/cpython/pyerrors.h
@@ -112,6 +112,10 @@ PyAPI_FUNC(PyObject *) _PyErr_FormatFromCause(
/* In exceptions.c */
+PyAPI_FUNC(int) _PyException_AddNote(
+ PyObject *exc,
+ PyObject *note);
+
/* Helper that attempts to replace the current exception with one of the
* same type but with a prefix added to the exception text. The resulting
* exception description looks like:
diff --git a/Lib/test/test_capi/test_exceptions.py b/Lib/test/test_capi/test_exceptions.py
index 55f131699a2567..b1c1a61e20685e 100644
--- a/Lib/test/test_capi/test_exceptions.py
+++ b/Lib/test/test_capi/test_exceptions.py
@@ -169,5 +169,25 @@ class Broken(Exception, metaclass=Meta):
with self.assertRaises(ZeroDivisionError) as e:
_testcapi.exc_set_object(Broken, Broken())
+ def test_set_object_and_fetch(self):
+ class Broken(Exception):
+ def __init__(self, *arg):
+ raise ValueError("Broken __init__")
+
+ exc = _testcapi.exc_set_object_fetch(Broken, 'abcd')
+ self.assertIsInstance(exc, ValueError)
+ self.assertEqual(exc.__notes__[0],
+ "Normalization failed: type=Broken args='abcd'")
+
+ class BadArg:
+ def __repr__(self):
+ raise TypeError('Broken arg type')
+
+ exc = _testcapi.exc_set_object_fetch(Broken, BadArg())
+ self.assertIsInstance(exc, ValueError)
+ self.assertEqual(exc.__notes__[0],
+ 'Normalization failed: type=Broken args=')
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-14-00-11-46.gh-issue-102594.BjU-m2.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-14-00-11-46.gh-issue-102594.BjU-m2.rst
new file mode 100644
index 00000000000000..0b95b5ec98e811
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-14-00-11-46.gh-issue-102594.BjU-m2.rst
@@ -0,0 +1 @@
+Add note to exception raised in ``PyErr_SetObject`` when normalization fails.
diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c
index a0575213987ffc..c64b823663c32f 100644
--- a/Modules/_testcapi/exceptions.c
+++ b/Modules/_testcapi/exceptions.c
@@ -92,6 +92,26 @@ exc_set_object(PyObject *self, PyObject *args)
return NULL;
}
+static PyObject *
+exc_set_object_fetch(PyObject *self, PyObject *args)
+{
+ PyObject *exc;
+ PyObject *obj;
+ PyObject *type;
+ PyObject *value;
+ PyObject *tb;
+
+ if (!PyArg_ParseTuple(args, "OO:exc_set_object", &exc, &obj)) {
+ return NULL;
+ }
+
+ PyErr_SetObject(exc, obj);
+ PyErr_Fetch(&type, &value, &tb);
+ Py_XDECREF(type);
+ Py_XDECREF(tb);
+ return value;
+}
+
static PyObject *
raise_exception(PyObject *self, PyObject *args)
{
@@ -262,6 +282,7 @@ static PyMethodDef test_methods[] = {
{"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc),
METH_VARARGS | METH_KEYWORDS},
{"exc_set_object", exc_set_object, METH_VARARGS},
+ {"exc_set_object_fetch", exc_set_object_fetch, METH_VARARGS},
{"raise_exception", raise_exception, METH_VARARGS},
{"raise_memoryerror", raise_memoryerror, METH_NOARGS},
{"set_exc_info", test_set_exc_info, METH_VARARGS},
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index c6fb6a3f19b2d0..d69f7400ca6042 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -3749,6 +3749,21 @@ _PyExc_Fini(PyInterpreterState *interp)
_PyExc_FiniTypes(interp);
}
+int
+_PyException_AddNote(PyObject *exc, PyObject *note)
+{
+ if (!PyExceptionInstance_Check(exc)) {
+ PyErr_Format(PyExc_TypeError,
+ "exc must be an exception, not '%s'",
+ Py_TYPE(exc)->tp_name);
+ return -1;
+ }
+ PyObject *r = BaseException_add_note(exc, note);
+ int res = r == NULL ? -1 : 0;
+ Py_XDECREF(r);
+ return res;
+}
+
/* Helper to do the equivalent of "raise X from Y" in C, but always using
* the current exception rather than passing one in.
*
diff --git a/Python/errors.c b/Python/errors.c
index bbf6d397ce8097..bdcbac317eb9ee 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -135,6 +135,28 @@ _PyErr_GetTopmostException(PyThreadState *tstate)
return exc_info;
}
+static PyObject *
+get_normalization_failure_note(PyThreadState *tstate, PyObject *exception, PyObject *value)
+{
+ PyObject *args = PyObject_Repr(value);
+ if (args == NULL) {
+ _PyErr_Clear(tstate);
+ args = PyUnicode_FromFormat("");
+ }
+ PyObject *note;
+ const char *tpname = ((PyTypeObject*)exception)->tp_name;
+ if (args == NULL) {
+ _PyErr_Clear(tstate);
+ note = PyUnicode_FromFormat("Normalization failed: type=%s", tpname);
+ }
+ else {
+ note = PyUnicode_FromFormat("Normalization failed: type=%s args=%S",
+ tpname, args);
+ Py_DECREF(args);
+ }
+ return note;
+}
+
void
_PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
{
@@ -160,19 +182,27 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
Py_XINCREF(value);
if (!is_subclass) {
/* We must normalize the value right now */
- PyObject *fixed_value;
/* Issue #23571: functions must not be called with an
exception set */
_PyErr_Clear(tstate);
- fixed_value = _PyErr_CreateException(exception, value);
- Py_XDECREF(value);
+ PyObject *fixed_value = _PyErr_CreateException(exception, value);
if (fixed_value == NULL) {
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
+ assert(PyExceptionInstance_Check(exc));
+
+ PyObject *note = get_normalization_failure_note(tstate, exception, value);
+ Py_XDECREF(value);
+ if (note != NULL) {
+ /* ignore errors in _PyException_AddNote - they will be overwritten below */
+ _PyException_AddNote(exc, note);
+ Py_DECREF(note);
+ }
+ _PyErr_SetRaisedException(tstate, exc);
return;
}
-
- value = fixed_value;
+ Py_XSETREF(value, fixed_value);
}
exc_value = _PyErr_GetTopmostException(tstate)->exc_value;
From e3461ea352c74c341ad0b8c3d310e6f4683c38fc Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Thu, 16 Mar 2023 16:05:38 +0300
Subject: [PATCH 125/280] Add comments to
`{typing,_collections_abc}._type_repr` about each other (#102752)
Remove `if` condition in `_collections_abc._type_repr` that's no longer needed, bringing it in sync with `typing._type_repr`.
---
Lib/_collections_abc.py | 3 +--
Lib/typing.py | 3 +++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index c62233b81a5c95..f86b91a5e6fb0d 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -517,9 +517,8 @@ def _type_repr(obj):
Copied from :mod:`typing` since collections.abc
shouldn't depend on that module.
+ (Keep this roughly in sync with the typing version.)
"""
- if isinstance(obj, GenericAlias):
- return repr(obj)
if isinstance(obj, type):
if obj.__module__ == 'builtins':
return obj.__qualname__
diff --git a/Lib/typing.py b/Lib/typing.py
index ab334395676159..3ee9679e50c0c4 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -230,6 +230,9 @@ def _type_repr(obj):
typically enough to uniquely identify a type. For everything
else, we fall back on repr(obj).
"""
+ # When changing this function, don't forget about
+ # `_collections_abc._type_repr`, which does the same thing
+ # and must be consistent with this one.
if isinstance(obj, type):
if obj.__module__ == 'builtins':
return obj.__qualname__
From 97306821d42cc2b0f6f331a70b988371b548f3f0 Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Thu, 16 Mar 2023 09:32:18 -0500
Subject: [PATCH 126/280] GH-102653: Make recipe docstring show the correct
distribution (#102742)
---
Doc/library/random.rst | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Doc/library/random.rst b/Doc/library/random.rst
index 4522f5a8d26b9d..098684d7270ffa 100644
--- a/Doc/library/random.rst
+++ b/Doc/library/random.rst
@@ -610,7 +610,8 @@ from the combinatoric iterators in the :mod:`itertools` module:
return tuple(pool[i] for i in indices)
def random_combination_with_replacement(iterable, r):
- "Random selection from itertools.combinations_with_replacement(iterable, r)"
+ "Choose r elements with replacement. Order the result to match the iterable."
+ # Result will be in set(itertools.combinations_with_replacement(iterable, r)).
pool = tuple(iterable)
n = len(pool)
indices = sorted(random.choices(range(n), k=r))
From dd97b7551b788d7df53d1cec71f3c59245d5f48b Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Thu, 16 Mar 2023 17:47:30 +0300
Subject: [PATCH 127/280] gh-102721: Improve coverage of
`_collections_abc._CallableGenericAlias` (#102722)
Co-authored-by: Alex Waygood
---
Lib/_collections_abc.py | 7 -------
Lib/test/test_typing.py | 39 ++++++++++++++++++++++++++++++++-------
2 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index f86b91a5e6fb0d..9d7724c33474cc 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -481,15 +481,8 @@ def __getitem__(self, item):
# rather than the default types.GenericAlias object. Most of the
# code is copied from typing's _GenericAlias and the builtin
# types.GenericAlias.
-
if not isinstance(item, tuple):
item = (item,)
- # A special case in PEP 612 where if X = Callable[P, int],
- # then X[int, str] == X[[int, str]].
- if (len(self.__parameters__) == 1
- and _is_param_expr(self.__parameters__[0])
- and item and not _is_param_expr(item[0])):
- item = (item,)
new_args = super().__getitem__(item).__args__
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 89c725cda54f12..c9f55de95c548f 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -1921,14 +1921,29 @@ def test_weakref(self):
self.assertEqual(weakref.ref(alias)(), alias)
def test_pickle(self):
+ global T_pickle, P_pickle, TS_pickle # needed for pickling
Callable = self.Callable
- alias = Callable[[int, str], float]
- for proto in range(pickle.HIGHEST_PROTOCOL + 1):
- s = pickle.dumps(alias, proto)
- loaded = pickle.loads(s)
- self.assertEqual(alias.__origin__, loaded.__origin__)
- self.assertEqual(alias.__args__, loaded.__args__)
- self.assertEqual(alias.__parameters__, loaded.__parameters__)
+ T_pickle = TypeVar('T_pickle')
+ P_pickle = ParamSpec('P_pickle')
+ TS_pickle = TypeVarTuple('TS_pickle')
+
+ samples = [
+ Callable[[int, str], float],
+ Callable[P_pickle, int],
+ Callable[P_pickle, T_pickle],
+ Callable[Concatenate[int, P_pickle], int],
+ Callable[Concatenate[*TS_pickle, P_pickle], int],
+ ]
+ for alias in samples:
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(alias=alias, proto=proto):
+ s = pickle.dumps(alias, proto)
+ loaded = pickle.loads(s)
+ self.assertEqual(alias.__origin__, loaded.__origin__)
+ self.assertEqual(alias.__args__, loaded.__args__)
+ self.assertEqual(alias.__parameters__, loaded.__parameters__)
+
+ del T_pickle, P_pickle, TS_pickle # cleaning up global state
def test_var_substitution(self):
Callable = self.Callable
@@ -1954,6 +1969,16 @@ def test_var_substitution(self):
self.assertEqual(C5[int, str, float],
Callable[[typing.List[int], tuple[str, int], float], int])
+ def test_type_subst_error(self):
+ Callable = self.Callable
+ P = ParamSpec('P')
+ T = TypeVar('T')
+
+ pat = "Expected a list of types, an ellipsis, ParamSpec, or Concatenate."
+
+ with self.assertRaisesRegex(TypeError, pat):
+ Callable[P, T][0, int]
+
def test_type_erasure(self):
Callable = self.Callable
class C1(Callable):
From a123f260030a417bdeb0c3055d851efcfaacd72e Mon Sep 17 00:00:00 2001
From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Date: Thu, 16 Mar 2023 20:28:10 +0530
Subject: [PATCH 128/280] GH-102748: remove legacy support for generator based
coroutines from `asyncio.iscoroutine` (#102749)
Co-authored-by: Alex Waygood
---
Doc/whatsnew/3.12.rst | 4 ++++
Lib/asyncio/coroutines.py | 3 +--
Lib/test/test_asyncio/test_pep492.py | 6 ++++++
.../Library/2023-03-16-08-17-29.gh-issue-102748.WNACpI.rst | 3 +++
4 files changed, 14 insertions(+), 2 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-16-08-17-29.gh-issue-102748.WNACpI.rst
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 217ffec1ee1007..b9c668543e1b73 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -237,6 +237,10 @@ asyncio
* Add C implementation of :func:`asyncio.current_task` for 4x-6x speedup.
(Contributed by Itamar Ostricher and Pranav Thulasiram Bhat in :gh:`100344`.)
+* :func:`asyncio.iscoroutine` now returns ``False`` for generators as
+ :mod:`asyncio` does not support legacy generator-based coroutines.
+ (Contributed by Kumar Aditya in :gh:`102748`.)
+
inspect
-------
diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py
index 7fda0e449d500a..ab4f30eb51ba2c 100644
--- a/Lib/asyncio/coroutines.py
+++ b/Lib/asyncio/coroutines.py
@@ -25,8 +25,7 @@ def iscoroutinefunction(func):
# Prioritize native coroutine check to speed-up
# asyncio.iscoroutine.
-_COROUTINE_TYPES = (types.CoroutineType, types.GeneratorType,
- collections.abc.Coroutine)
+_COROUTINE_TYPES = (types.CoroutineType, collections.abc.Coroutine)
_iscoroutine_typecache = set()
diff --git a/Lib/test/test_asyncio/test_pep492.py b/Lib/test/test_asyncio/test_pep492.py
index f833f788dcb98f..dc25a46985e349 100644
--- a/Lib/test/test_asyncio/test_pep492.py
+++ b/Lib/test/test_asyncio/test_pep492.py
@@ -119,6 +119,12 @@ async def foo(): pass
self.assertTrue(asyncio.iscoroutine(FakeCoro()))
+ def test_iscoroutine_generator(self):
+ def foo(): yield
+
+ self.assertFalse(asyncio.iscoroutine(foo()))
+
+
def test_iscoroutinefunction(self):
async def foo(): pass
self.assertTrue(asyncio.iscoroutinefunction(foo))
diff --git a/Misc/NEWS.d/next/Library/2023-03-16-08-17-29.gh-issue-102748.WNACpI.rst b/Misc/NEWS.d/next/Library/2023-03-16-08-17-29.gh-issue-102748.WNACpI.rst
new file mode 100644
index 00000000000000..b1dc67f38fe85d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-16-08-17-29.gh-issue-102748.WNACpI.rst
@@ -0,0 +1,3 @@
+:func:`asyncio.iscoroutine` now returns ``False`` for generators as
+:mod:`asyncio` does not support legacy generator-based coroutines.
+Patch by Kumar Aditya.
From df9a32283e3a751b1b664975f4c294e522ccb121 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Thu, 16 Mar 2023 09:26:42 -0600
Subject: [PATCH 129/280] gh-102737: Un-ignore ceval.c in the CI globals check
(gh-102745)
The tool now allows user-added #LINE preprocessor directives.
https://github.com/python/cpython/issues/102737
---
Tools/c-analyzer/c_parser/preprocessor/gcc.py | 10 +++++++---
Tools/c-analyzer/cpython/_parser.py | 4 ----
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/Tools/c-analyzer/c_parser/preprocessor/gcc.py b/Tools/c-analyzer/c_parser/preprocessor/gcc.py
index 24c1b0e9b9d48c..c680f351f22416 100644
--- a/Tools/c-analyzer/c_parser/preprocessor/gcc.py
+++ b/Tools/c-analyzer/c_parser/preprocessor/gcc.py
@@ -153,9 +153,13 @@ def _iter_top_include_lines(lines, topfile, cwd,
# XXX How can a file return to line 1?
#assert lno > 1, (line, lno)
else:
- # It's the next line from the file.
- assert included == files[-1], (line, files)
- assert lno > 1, (line, lno)
+ if included == files[-1]:
+ # It's the next line from the file.
+ assert lno > 1, (line, lno)
+ else:
+ # We ran into a user-added #LINE directive,
+ # which we promptly ignore.
+ pass
elif not files:
raise NotImplementedError((line,))
elif filter_reqfile(files[-1]):
diff --git a/Tools/c-analyzer/cpython/_parser.py b/Tools/c-analyzer/cpython/_parser.py
index a2911e030ffee1..acf30e2c4020b3 100644
--- a/Tools/c-analyzer/cpython/_parser.py
+++ b/Tools/c-analyzer/cpython/_parser.py
@@ -96,10 +96,6 @@ def clean_lines(text):
# has gone wrong where # we handle "maybe inline actual"
# in Tools/c-analyzer/c_parser/parser/_global.py.
Modules/expat/xmlparse.c
-
-# The parser doesn't like the #line directives
-# that originate from generated_cases.c.h
-Python/ceval.c
''')
INCL_DIRS = clean_lines('''
From 6b9d4cbfb450cdc4ddec6daad6be6afebd4a21ea Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Thu, 16 Mar 2023 16:21:49 +0000
Subject: [PATCH 130/280] gh-102192: Replace PyErr_Fetch/Restore etc by more
efficient alternatives (#102743)
---
Python/pythonrun.c | 79 ++++++++++++++++++++--------------------------
1 file changed, 35 insertions(+), 44 deletions(-)
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 34a44dd92847ba..5381b105a39fed 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -698,30 +698,30 @@ _Py_HandleSystemExit(int *exitcode_p)
return 0;
}
- PyObject *exception, *value, *tb;
- PyErr_Fetch(&exception, &value, &tb);
-
fflush(stdout);
int exitcode = 0;
- if (value == NULL || value == Py_None) {
+
+ PyObject *exc = PyErr_GetRaisedException();
+ if (exc == NULL) {
goto done;
}
+ assert(PyExceptionInstance_Check(exc));
- if (PyExceptionInstance_Check(value)) {
- /* The error code should be in the `code' attribute. */
- PyObject *code = PyObject_GetAttr(value, &_Py_ID(code));
- if (code) {
- Py_SETREF(value, code);
- if (value == Py_None)
- goto done;
+ /* The error code should be in the `code' attribute. */
+ PyObject *code = PyObject_GetAttr(exc, &_Py_ID(code));
+ if (code) {
+ Py_SETREF(exc, code);
+ if (exc == Py_None) {
+ goto done;
}
- /* If we failed to dig out the 'code' attribute,
- just let the else clause below print the error. */
}
+ /* If we failed to dig out the 'code' attribute,
+ * just let the else clause below print the error.
+ */
- if (PyLong_Check(value)) {
- exitcode = (int)PyLong_AsLong(value);
+ if (PyLong_Check(exc)) {
+ exitcode = (int)PyLong_AsLong(exc);
}
else {
PyThreadState *tstate = _PyThreadState_GET();
@@ -732,20 +732,17 @@ _Py_HandleSystemExit(int *exitcode_p)
*/
PyErr_Clear();
if (sys_stderr != NULL && sys_stderr != Py_None) {
- PyFile_WriteObject(value, sys_stderr, Py_PRINT_RAW);
+ PyFile_WriteObject(exc, sys_stderr, Py_PRINT_RAW);
} else {
- PyObject_Print(value, stderr, Py_PRINT_RAW);
+ PyObject_Print(exc, stderr, Py_PRINT_RAW);
fflush(stderr);
}
PySys_WriteStderr("\n");
exitcode = 1;
}
- done:
- /* Cleanup the exception */
- Py_CLEAR(exception);
- Py_CLEAR(value);
- Py_CLEAR(tb);
+done:
+ Py_CLEAR(exc);
*exitcode_p = exitcode;
return 1;
}
@@ -1641,35 +1638,29 @@ PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals,
}
-
static void
-flush_io(void)
+flush_io_stream(PyThreadState *tstate, PyObject *name)
{
- PyObject *f, *r;
- PyObject *type, *value, *traceback;
-
- /* Save the current exception */
- PyErr_Fetch(&type, &value, &traceback);
-
- PyThreadState *tstate = _PyThreadState_GET();
- f = _PySys_GetAttr(tstate, &_Py_ID(stderr));
- if (f != NULL) {
- r = _PyObject_CallMethodNoArgs(f, &_Py_ID(flush));
- if (r)
- Py_DECREF(r);
- else
- PyErr_Clear();
- }
- f = _PySys_GetAttr(tstate, &_Py_ID(stdout));
+ PyObject *f = _PySys_GetAttr(tstate, name);
if (f != NULL) {
- r = _PyObject_CallMethodNoArgs(f, &_Py_ID(flush));
- if (r)
+ PyObject *r = _PyObject_CallMethodNoArgs(f, &_Py_ID(flush));
+ if (r) {
Py_DECREF(r);
- else
+ }
+ else {
PyErr_Clear();
+ }
}
+}
- PyErr_Restore(type, value, traceback);
+static void
+flush_io(void)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
+ flush_io_stream(tstate, &_Py_ID(stderr));
+ flush_io_stream(tstate, &_Py_ID(stdout));
+ _PyErr_SetRaisedException(tstate, exc);
}
static PyObject *
From ba65c48bafe0d6e12d3cc5504731fb54dcf54446 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Thu, 16 Mar 2023 16:41:10 +0000
Subject: [PATCH 131/280] gh-102192: remove redundant exception fields from ssl
module socket (#102466)
---
Modules/_ssl.c | 28 +++++++++-------------------
Modules/_ssl/debughelpers.c | 7 +++----
2 files changed, 12 insertions(+), 23 deletions(-)
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 28112317bc289e..121d18884d0a9f 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -318,9 +318,7 @@ typedef struct {
* store exception information on the socket. The handshake, read, write,
* and shutdown methods check for chained exceptions.
*/
- PyObject *exc_type;
- PyObject *exc_value;
- PyObject *exc_tb;
+ PyObject *exc;
} PySSLSocket;
typedef struct {
@@ -564,13 +562,11 @@ fill_and_set_sslerror(_sslmodulestate *state,
static int
PySSL_ChainExceptions(PySSLSocket *sslsock) {
- if (sslsock->exc_type == NULL)
+ if (sslsock->exc == NULL)
return 0;
- _PyErr_ChainExceptions(sslsock->exc_type, sslsock->exc_value, sslsock->exc_tb);
- sslsock->exc_type = NULL;
- sslsock->exc_value = NULL;
- sslsock->exc_tb = NULL;
+ _PyErr_ChainExceptions1(sslsock->exc);
+ sslsock->exc = NULL;
return -1;
}
@@ -807,9 +803,7 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
self->owner = NULL;
self->server_hostname = NULL;
self->err = err;
- self->exc_type = NULL;
- self->exc_value = NULL;
- self->exc_tb = NULL;
+ self->exc = NULL;
/* Make sure the SSL error state is initialized */
ERR_clear_error();
@@ -2179,9 +2173,7 @@ Passed as \"self\" in servername callback.");
static int
PySSL_traverse(PySSLSocket *self, visitproc visit, void *arg)
{
- Py_VISIT(self->exc_type);
- Py_VISIT(self->exc_value);
- Py_VISIT(self->exc_tb);
+ Py_VISIT(self->exc);
Py_VISIT(Py_TYPE(self));
return 0;
}
@@ -2189,9 +2181,7 @@ PySSL_traverse(PySSLSocket *self, visitproc visit, void *arg)
static int
PySSL_clear(PySSLSocket *self)
{
- Py_CLEAR(self->exc_type);
- Py_CLEAR(self->exc_value);
- Py_CLEAR(self->exc_tb);
+ Py_CLEAR(self->exc);
return 0;
}
@@ -2536,7 +2526,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
PySSL_SetError(self, retval, __FILE__, __LINE__);
goto error;
}
- if (self->exc_type != NULL)
+ if (self->exc != NULL)
goto error;
done:
@@ -2662,7 +2652,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
PySSL_SetError(self, ret, __FILE__, __LINE__);
return NULL;
}
- if (self->exc_type != NULL)
+ if (self->exc != NULL)
goto error;
if (sock)
/* It's already INCREF'ed */
diff --git a/Modules/_ssl/debughelpers.c b/Modules/_ssl/debughelpers.c
index 08f3457035b90c..217f224942556e 100644
--- a/Modules/_ssl/debughelpers.c
+++ b/Modules/_ssl/debughelpers.c
@@ -74,7 +74,7 @@ _PySSL_msg_callback(int write_p, int version, int content_type,
buf, len
);
if (res == NULL) {
- PyErr_Fetch(&ssl_obj->exc_type, &ssl_obj->exc_value, &ssl_obj->exc_tb);
+ ssl_obj->exc = PyErr_GetRaisedException();
} else {
Py_DECREF(res);
}
@@ -138,8 +138,7 @@ _PySSL_keylog_callback(const SSL *ssl, const char *line)
lock = PyThread_allocate_lock();
if (lock == NULL) {
PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock");
- PyErr_Fetch(&ssl_obj->exc_type, &ssl_obj->exc_value,
- &ssl_obj->exc_tb);
+ ssl_obj->exc = PyErr_GetRaisedException();
return;
}
}
@@ -156,7 +155,7 @@ _PySSL_keylog_callback(const SSL *ssl, const char *line)
errno = e;
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError,
ssl_obj->ctx->keylog_filename);
- PyErr_Fetch(&ssl_obj->exc_type, &ssl_obj->exc_value, &ssl_obj->exc_tb);
+ ssl_obj->exc = PyErr_GetRaisedException();
}
PyGILState_Release(threadstate);
}
From a10c1f221a5248cedf476736eea365e1dfc84910 Mon Sep 17 00:00:00 2001
From: Steve Dower
Date: Thu, 16 Mar 2023 17:27:21 +0000
Subject: [PATCH 132/280] gh-99726: Improves correctness of stat results for
Windows, and uses faster API when available (GH-102149)
This deprecates `st_ctime` fields on Windows, with the intent to change them to contain the correct value in 3.14. For now, they should keep returning the creation time as they always have.
---
Doc/library/os.rst | 88 +++++---
Doc/whatsnew/3.12.rst | 16 ++
Include/internal/pycore_fileutils.h | 5 +-
Include/internal/pycore_fileutils_windows.h | 80 ++++++++
Lib/test/test_os.py | 12 +-
...3-02-22-17-26-10.gh-issue-99726.76t957.rst | 2 +
Modules/posixmodule.c | 191 +++++++++++++-----
PCbuild/pythoncore.vcxproj | 1 +
PCbuild/pythoncore.vcxproj.filters | 3 +
Python/fileutils.c | 130 ++++++++++--
10 files changed, 446 insertions(+), 82 deletions(-)
create mode 100644 Include/internal/pycore_fileutils_windows.h
create mode 100644 Misc/NEWS.d/next/Windows/2023-02-22-17-26-10.gh-issue-99726.76t957.rst
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index 5b9f49be1fad55..3153f79e10ce1f 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -2858,6 +2858,12 @@ features:
Added support for the :class:`~os.PathLike` interface. Added support
for :class:`bytes` paths on Windows.
+ .. versionchanged:: 3.12
+ The ``st_ctime`` attribute of a stat result is deprecated on Windows.
+ The file creation time is properly available as ``st_birthtime``, and
+ in the future ``st_ctime`` may be changed to return zero or the
+ metadata change time, if available.
+
.. function:: stat(path, *, dir_fd=None, follow_symlinks=True)
@@ -2973,10 +2979,12 @@ features:
.. attribute:: st_ctime
- Platform dependent:
+ Time of most recent metadata change expressed in seconds.
- * the time of most recent metadata change on Unix,
- * the time of creation on Windows, expressed in seconds.
+ .. versionchanged:: 3.12
+ ``st_ctime`` is deprecated on Windows. Use ``st_birthtime`` for
+ the file creation time. In the future, ``st_ctime`` will contain
+ the time of the most recent metadata change, as for other platforms.
.. attribute:: st_atime_ns
@@ -2989,29 +2997,48 @@ features:
.. attribute:: st_ctime_ns
- Platform dependent:
+ Time of most recent metadata change expressed in nanoseconds as an
+ integer.
+
+ .. versionchanged:: 3.12
+ ``st_ctime_ns`` is deprecated on Windows. Use ``st_birthtime_ns``
+ for the file creation time. In the future, ``st_ctime`` will contain
+ the time of the most recent metadata change, as for other platforms.
+
+ .. attribute:: st_birthtime
+
+ Time of file creation expressed in seconds. This attribute is not
+ always available, and may raise :exc:`AttributeError`.
+
+ .. versionchanged:: 3.12
+ ``st_birthtime`` is now available on Windows.
+
+ .. attribute:: st_birthtime_ns
- * the time of most recent metadata change on Unix,
- * the time of creation on Windows, expressed in nanoseconds as an
- integer.
+ Time of file creation expressed in nanoseconds as an integer.
+ This attribute is not always available, and may raise
+ :exc:`AttributeError`.
+
+ .. versionadded:: 3.12
.. note::
The exact meaning and resolution of the :attr:`st_atime`,
- :attr:`st_mtime`, and :attr:`st_ctime` attributes depend on the operating
- system and the file system. For example, on Windows systems using the FAT
- or FAT32 file systems, :attr:`st_mtime` has 2-second resolution, and
- :attr:`st_atime` has only 1-day resolution. See your operating system
- documentation for details.
+ :attr:`st_mtime`, :attr:`st_ctime` and :attr:`st_birthtime` attributes
+ depend on the operating system and the file system. For example, on
+ Windows systems using the FAT32 file systems, :attr:`st_mtime` has
+ 2-second resolution, and :attr:`st_atime` has only 1-day resolution.
+ See your operating system documentation for details.
Similarly, although :attr:`st_atime_ns`, :attr:`st_mtime_ns`,
- and :attr:`st_ctime_ns` are always expressed in nanoseconds, many
- systems do not provide nanosecond precision. On systems that do
- provide nanosecond precision, the floating-point object used to
- store :attr:`st_atime`, :attr:`st_mtime`, and :attr:`st_ctime`
- cannot preserve all of it, and as such will be slightly inexact.
- If you need the exact timestamps you should always use
- :attr:`st_atime_ns`, :attr:`st_mtime_ns`, and :attr:`st_ctime_ns`.
+ :attr:`st_ctime_ns` and :attr:`st_birthtime_ns` are always expressed in
+ nanoseconds, many systems do not provide nanosecond precision. On
+ systems that do provide nanosecond precision, the floating-point object
+ used to store :attr:`st_atime`, :attr:`st_mtime`, :attr:`st_ctime` and
+ :attr:`st_birthtime` cannot preserve all of it, and as such will be
+ slightly inexact. If you need the exact timestamps you should always use
+ :attr:`st_atime_ns`, :attr:`st_mtime_ns`, :attr:`st_ctime_ns` and
+ :attr:`st_birthtime_ns`.
On some Unix systems (such as Linux), the following attributes may also be
available:
@@ -3041,10 +3068,6 @@ features:
File generation number.
- .. attribute:: st_birthtime
-
- Time of file creation.
-
On Solaris and derivatives, the following attributes may also be
available:
@@ -3117,6 +3140,25 @@ features:
files as :const:`S_IFCHR`, :const:`S_IFIFO` or :const:`S_IFBLK`
as appropriate.
+ .. versionchanged:: 3.12
+ On Windows, :attr:`st_ctime` is deprecated. Eventually, it will
+ contain the last metadata change time, for consistency with other
+ platforms, but for now still contains creation time.
+ Use :attr:`st_birthtime` for the creation time.
+
+ .. versionchanged:: 3.12
+ On Windows, :attr:`st_ino` may now be up to 128 bits, depending
+ on the file system. Previously it would not be above 64 bits, and
+ larger file identifiers would be arbitrarily packed.
+
+ .. versionchanged:: 3.12
+ On Windows, :attr:`st_rdev` no longer returns a value. Previously
+ it would contain the same as :attr:`st_dev`, which was incorrect.
+
+ .. versionadded:: 3.12
+ Added the :attr:`st_birthtime` member on Windows.
+
+
.. function:: statvfs(path)
Perform a :c:func:`statvfs` system call on the given path. The return value is
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index b9c668543e1b73..03b1f975746787 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -306,6 +306,16 @@ os
functions on Windows for enumerating drives, volumes and mount points.
(Contributed by Steve Dower in :gh:`102519`.)
+* :func:`os.stat` and :func:`os.lstat` are now more accurate on Windows.
+ The ``st_birthtime`` field will now be filled with the creation time
+ of the file, and ``st_ctime`` is deprecated but still contains the
+ creation time (but in the future will return the last metadata change,
+ for consistency with other platforms). ``st_dev`` may be up to 64 bits
+ and ``st_ino`` up to 128 bits depending on your file system, and
+ ``st_rdev`` is always set to zero rather than incorrect values.
+ Both functions may be significantly faster on newer releases of
+ Windows. (Contributed by Steve Dower in :gh:`99726`.)
+
os.path
-------
@@ -469,6 +479,12 @@ Deprecated
warning at compile time. This field will be removed in Python 3.14.
(Contributed by Ramvikrams and Kumar Aditya in :gh:`101193`. PEP by Ken Jin.)
+* The ``st_ctime`` fields return by :func:`os.stat` and :func:`os.lstat` on
+ Windows are deprecated. In a future release, they will contain the last
+ metadata change time, consistent with other platforms. For now, they still
+ contain the creation time, which is also available in the new ``st_birthtime``
+ field. (Contributed by Steve Dower in :gh:`99726`.)
+
Pending Removal in Python 3.13
------------------------------
diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h
index 445ac0a3d955d9..ef6642d00f1b54 100644
--- a/Include/internal/pycore_fileutils.h
+++ b/Include/internal/pycore_fileutils.h
@@ -66,7 +66,7 @@ PyAPI_FUNC(PyObject *) _Py_device_encoding(int);
#ifdef MS_WINDOWS
struct _Py_stat_struct {
- unsigned long st_dev;
+ uint64_t st_dev;
uint64_t st_ino;
unsigned short st_mode;
int st_nlink;
@@ -80,8 +80,11 @@ struct _Py_stat_struct {
int st_mtime_nsec;
time_t st_ctime;
int st_ctime_nsec;
+ time_t st_birthtime;
+ int st_birthtime_nsec;
unsigned long st_file_attributes;
unsigned long st_reparse_tag;
+ uint64_t st_ino_high;
};
#else
# define _Py_stat_struct stat
diff --git a/Include/internal/pycore_fileutils_windows.h b/Include/internal/pycore_fileutils_windows.h
new file mode 100644
index 00000000000000..44874903b092f3
--- /dev/null
+++ b/Include/internal/pycore_fileutils_windows.h
@@ -0,0 +1,80 @@
+#ifndef Py_INTERNAL_FILEUTILS_WINDOWS_H
+#define Py_INTERNAL_FILEUTILS_WINDOWS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+# error "Py_BUILD_CORE must be defined to include this header"
+#endif
+
+#ifdef MS_WINDOWS
+
+#if !defined(NTDDI_WIN10_NI) || !(NTDDI_VERSION >= NTDDI_WIN10_NI)
+typedef struct _FILE_STAT_BASIC_INFORMATION {
+ LARGE_INTEGER FileId;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER AllocationSize;
+ LARGE_INTEGER EndOfFile;
+ ULONG FileAttributes;
+ ULONG ReparseTag;
+ ULONG NumberOfLinks;
+ ULONG DeviceType;
+ ULONG DeviceCharacteristics;
+ ULONG Reserved;
+ FILE_ID_128 FileId128;
+ LARGE_INTEGER VolumeSerialNumber;
+} FILE_STAT_BASIC_INFORMATION;
+
+typedef enum _FILE_INFO_BY_NAME_CLASS {
+ FileStatByNameInfo,
+ FileStatLxByNameInfo,
+ FileCaseSensitiveByNameInfo,
+ FileStatBasicByNameInfo,
+ MaximumFileInfoByNameClass
+} FILE_INFO_BY_NAME_CLASS;
+#endif
+
+typedef BOOL (WINAPI *PGetFileInformationByName)(
+ PCWSTR FileName,
+ FILE_INFO_BY_NAME_CLASS FileInformationClass,
+ PVOID FileInfoBuffer,
+ ULONG FileInfoBufferSize
+);
+
+static inline BOOL _Py_GetFileInformationByName(
+ PCWSTR FileName,
+ FILE_INFO_BY_NAME_CLASS FileInformationClass,
+ PVOID FileInfoBuffer,
+ ULONG FileInfoBufferSize
+) {
+ static PGetFileInformationByName GetFileInformationByName = NULL;
+ static int GetFileInformationByName_init = -1;
+
+ if (GetFileInformationByName_init < 0) {
+ HMODULE hMod = LoadLibraryW(L"api-ms-win-core-file-l2-1-4");
+ GetFileInformationByName_init = 0;
+ if (hMod) {
+ GetFileInformationByName = (PGetFileInformationByName)GetProcAddress(
+ hMod, "GetFileInformationByName");
+ if (GetFileInformationByName) {
+ GetFileInformationByName_init = 1;
+ } else {
+ FreeLibrary(hMod);
+ }
+ }
+ }
+
+ if (GetFileInformationByName_init <= 0) {
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+ return GetFileInformationByName(FileName, FileInformationClass, FileInfoBuffer, FileInfoBufferSize);
+}
+
+#endif
+
+#endif
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 42357fef80ec89..74ece3ffb4ed17 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -556,6 +556,15 @@ def trunc(x): return x
nanosecondy = getattr(result, name + "_ns") // 10000
self.assertAlmostEqual(floaty, nanosecondy, delta=2)
+ # Ensure both birthtime and birthtime_ns roughly agree, if present
+ try:
+ floaty = int(result.st_birthtime * 100000)
+ nanosecondy = result.st_birthtime_ns // 10000
+ except AttributeError:
+ pass
+ else:
+ self.assertAlmostEqual(floaty, nanosecondy, delta=2)
+
try:
result[200]
self.fail("No exception raised")
@@ -4234,7 +4243,8 @@ def assert_stat_equal(self, stat1, stat2, skip_fields):
for attr in dir(stat1):
if not attr.startswith("st_"):
continue
- if attr in ("st_dev", "st_ino", "st_nlink"):
+ if attr in ("st_dev", "st_ino", "st_nlink", "st_ctime",
+ "st_ctime_ns"):
continue
self.assertEqual(getattr(stat1, attr),
getattr(stat2, attr),
diff --git a/Misc/NEWS.d/next/Windows/2023-02-22-17-26-10.gh-issue-99726.76t957.rst b/Misc/NEWS.d/next/Windows/2023-02-22-17-26-10.gh-issue-99726.76t957.rst
new file mode 100644
index 00000000000000..e2578620017894
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2023-02-22-17-26-10.gh-issue-99726.76t957.rst
@@ -0,0 +1,2 @@
+Improves correctness of stat results for Windows, and uses faster API when
+available
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 7d91f7e4bac76b..e38caf7cc0abee 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -41,6 +41,7 @@
#ifndef MS_WINDOWS
# include "posixmodule.h"
#else
+# include "pycore_fileutils_windows.h"
# include "winreparse.h"
#endif
@@ -668,8 +669,11 @@ PyOS_AfterFork(void)
#ifdef MS_WINDOWS
/* defined in fileutils.c */
void _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *);
-void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *,
- ULONG, struct _Py_stat_struct *);
+void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *, ULONG,
+ FILE_BASIC_INFO *, FILE_ID_INFO *,
+ struct _Py_stat_struct *);
+void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *,
+ struct _Py_stat_struct *);
#endif
@@ -1819,12 +1823,39 @@ attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *re
return TRUE;
}
+
+static void
+update_st_mode_from_path(const wchar_t *path, DWORD attr,
+ struct _Py_stat_struct *result)
+{
+ if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ /* Fix the file execute permissions. This hack sets S_IEXEC if
+ the filename has an extension that is commonly used by files
+ that CreateProcessW can execute. A real implementation calls
+ GetSecurityInfo, OpenThreadToken/OpenProcessToken, and
+ AccessCheck to check for generic read, write, and execute
+ access. */
+ const wchar_t *fileExtension = wcsrchr(path, '.');
+ if (fileExtension) {
+ if (_wcsicmp(fileExtension, L".exe") == 0 ||
+ _wcsicmp(fileExtension, L".bat") == 0 ||
+ _wcsicmp(fileExtension, L".cmd") == 0 ||
+ _wcsicmp(fileExtension, L".com") == 0) {
+ result->st_mode |= 0111;
+ }
+ }
+ }
+}
+
+
static int
-win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
- BOOL traverse)
+win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
+ BOOL traverse)
{
HANDLE hFile;
BY_HANDLE_FILE_INFORMATION fileInfo;
+ FILE_BASIC_INFO basicInfo;
+ FILE_ID_INFO idInfo;
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
DWORD fileType, error;
BOOL isUnhandledTag = FALSE;
@@ -1954,12 +1985,16 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
for an unhandled tag. */
} else if (!isUnhandledTag) {
CloseHandle(hFile);
- return win32_xstat_impl(path, result, TRUE);
+ return win32_xstat_slow_impl(path, result, TRUE);
}
}
}
- if (!GetFileInformationByHandle(hFile, &fileInfo)) {
+ if (!GetFileInformationByHandle(hFile, &fileInfo) ||
+ !GetFileInformationByHandleEx(hFile, FileBasicInfo,
+ &basicInfo, sizeof(basicInfo)) ||
+ !GetFileInformationByHandleEx(hFile, FileIdInfo,
+ &idInfo, sizeof(idInfo))) {
switch (GetLastError()) {
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_FUNCTION:
@@ -1975,25 +2010,8 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
}
}
- _Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, result);
-
- if (!(fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
- /* Fix the file execute permissions. This hack sets S_IEXEC if
- the filename has an extension that is commonly used by files
- that CreateProcessW can execute. A real implementation calls
- GetSecurityInfo, OpenThreadToken/OpenProcessToken, and
- AccessCheck to check for generic read, write, and execute
- access. */
- const wchar_t *fileExtension = wcsrchr(path, '.');
- if (fileExtension) {
- if (_wcsicmp(fileExtension, L".exe") == 0 ||
- _wcsicmp(fileExtension, L".bat") == 0 ||
- _wcsicmp(fileExtension, L".cmd") == 0 ||
- _wcsicmp(fileExtension, L".com") == 0) {
- result->st_mode |= 0111;
- }
- }
- }
+ _Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, &basicInfo, &idInfo, result);
+ update_st_mode_from_path(path, fileInfo.dwFileAttributes, result);
cleanup:
if (hFile != INVALID_HANDLE_VALUE) {
@@ -2010,6 +2028,39 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
return retval;
}
+static int
+win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
+ BOOL traverse)
+{
+ FILE_STAT_BASIC_INFORMATION statInfo;
+ if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo,
+ &statInfo, sizeof(statInfo))) {
+ if (// Cannot use fast path for reparse points ...
+ !(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ // ... unless it's a name surrogate (symlink) and we're not following
+ || (!traverse && IsReparseTagNameSurrogate(statInfo.ReparseTag))
+ ) {
+ _Py_stat_basic_info_to_stat(&statInfo, result);
+ update_st_mode_from_path(path, statInfo.FileAttributes, result);
+ return 0;
+ }
+ } else {
+ switch(GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_NOT_READY:
+ case ERROR_BAD_NET_NAME:
+ /* These errors aren't worth retrying with the slow path */
+ return -1;
+ case ERROR_NOT_SUPPORTED:
+ /* indicates the API couldn't be loaded */
+ break;
+ }
+ }
+
+ return win32_xstat_slow_impl(path, result, traverse);
+}
+
static int
win32_xstat(const wchar_t *path, struct _Py_stat_struct *result, BOOL traverse)
{
@@ -2017,6 +2068,10 @@ win32_xstat(const wchar_t *path, struct _Py_stat_struct *result, BOOL traverse)
setting it to a POSIX error. Callers should use GetLastError. */
int code = win32_xstat_impl(path, result, traverse);
errno = 0;
+
+ /* ctime is only deprecated from 3.12, so we copy birthtime across */
+ result->st_ctime = result->st_birthtime;
+ result->st_ctime_nsec = result->st_birthtime_nsec;
return code;
}
/* About the following functions: win32_lstat_w, win32_stat, win32_stat_w
@@ -2087,9 +2142,12 @@ static PyStructSequence_Field stat_result_fields[] = {
#ifdef HAVE_STRUCT_STAT_ST_GEN
{"st_gen", "generation number"},
#endif
-#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
+#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS)
{"st_birthtime", "time of creation"},
#endif
+#ifdef MS_WINDOWS
+ {"st_birthtime_ns", "time of creation in nanoseconds"},
+#endif
#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
{"st_file_attributes", "Windows file attribute bits"},
#endif
@@ -2132,16 +2190,22 @@ static PyStructSequence_Field stat_result_fields[] = {
#define ST_GEN_IDX ST_FLAGS_IDX
#endif
-#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
+#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS)
#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1)
#else
#define ST_BIRTHTIME_IDX ST_GEN_IDX
#endif
-#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES
-#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_IDX+1)
+#ifdef MS_WINDOWS
+#define ST_BIRTHTIME_NS_IDX (ST_BIRTHTIME_IDX+1)
#else
-#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_IDX
+#define ST_BIRTHTIME_NS_IDX ST_BIRTHTIME_IDX
+#endif
+
+#if defined(HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES) || defined(MS_WINDOWS)
+#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_NS_IDX+1)
+#else
+#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_NS_IDX
#endif
#ifdef HAVE_STRUCT_STAT_ST_FSTYPE
@@ -2310,7 +2374,7 @@ _posix_free(void *module)
}
static void
-fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long nsec)
+fill_time(PyObject *module, PyObject *v, int s_index, int f_index, int ns_index, time_t sec, unsigned long nsec)
{
PyObject *s = _PyLong_FromTime_t(sec);
PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec);
@@ -2334,12 +2398,18 @@ fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long ns
goto exit;
}
- PyStructSequence_SET_ITEM(v, index, s);
- PyStructSequence_SET_ITEM(v, index+3, float_s);
- PyStructSequence_SET_ITEM(v, index+6, ns_total);
- s = NULL;
- float_s = NULL;
- ns_total = NULL;
+ if (s_index >= 0) {
+ PyStructSequence_SET_ITEM(v, s_index, s);
+ s = NULL;
+ }
+ if (f_index >= 0) {
+ PyStructSequence_SET_ITEM(v, f_index, float_s);
+ float_s = NULL;
+ }
+ if (ns_index >= 0) {
+ PyStructSequence_SET_ITEM(v, ns_index, ns_total);
+ ns_total = NULL;
+ }
exit:
Py_XDECREF(s);
Py_XDECREF(ns_fractional);
@@ -2348,6 +2418,33 @@ fill_time(PyObject *module, PyObject *v, int index, time_t sec, unsigned long ns
Py_XDECREF(float_s);
}
+#ifdef MS_WINDOWS
+static PyObject*
+_pystat_l128_from_l64_l64(uint64_t low, uint64_t high)
+{
+ PyObject *o_low = PyLong_FromUnsignedLongLong(low);
+ if (!o_low || !high) {
+ return o_low;
+ }
+ PyObject *o_high = PyLong_FromUnsignedLongLong(high);
+ PyObject *l64 = o_high ? PyLong_FromLong(64) : NULL;
+ if (!l64) {
+ Py_XDECREF(o_high);
+ Py_DECREF(o_low);
+ return NULL;
+ }
+ Py_SETREF(o_high, PyNumber_Lshift(o_high, l64));
+ Py_DECREF(l64);
+ if (!o_high) {
+ Py_DECREF(o_low);
+ return NULL;
+ }
+ Py_SETREF(o_low, PyNumber_Add(o_low, o_high));
+ Py_DECREF(o_high);
+ return o_low;
+}
+#endif
+
/* pack a system stat C structure into the Python stat tuple
(used by posix_stat() and posix_fstat()) */
static PyObject*
@@ -2360,12 +2457,13 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
return NULL;
PyStructSequence_SET_ITEM(v, 0, PyLong_FromLong((long)st->st_mode));
+#ifdef MS_WINDOWS
+ PyStructSequence_SET_ITEM(v, 1, _pystat_l128_from_l64_l64(st->st_ino, st->st_ino_high));
+ PyStructSequence_SET_ITEM(v, 2, PyLong_FromUnsignedLongLong(st->st_dev));
+#else
static_assert(sizeof(unsigned long long) >= sizeof(st->st_ino),
"stat.st_ino is larger than unsigned long long");
PyStructSequence_SET_ITEM(v, 1, PyLong_FromUnsignedLongLong(st->st_ino));
-#ifdef MS_WINDOWS
- PyStructSequence_SET_ITEM(v, 2, PyLong_FromUnsignedLong(st->st_dev));
-#else
PyStructSequence_SET_ITEM(v, 2, _PyLong_FromDev(st->st_dev));
#endif
PyStructSequence_SET_ITEM(v, 3, PyLong_FromLong((long)st->st_nlink));
@@ -2395,9 +2493,9 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
#else
ansec = mnsec = cnsec = 0;
#endif
- fill_time(module, v, 7, st->st_atime, ansec);
- fill_time(module, v, 8, st->st_mtime, mnsec);
- fill_time(module, v, 9, st->st_ctime, cnsec);
+ fill_time(module, v, 7, 10, 13, st->st_atime, ansec);
+ fill_time(module, v, 8, 11, 14, st->st_mtime, mnsec);
+ fill_time(module, v, 9, 12, 15, st->st_ctime, cnsec);
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX,
@@ -2415,7 +2513,7 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
PyStructSequence_SET_ITEM(v, ST_GEN_IDX,
PyLong_FromLong((long)st->st_gen));
#endif
-#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
+#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME)
{
PyObject *val;
unsigned long bsec,bnsec;
@@ -2429,6 +2527,9 @@ _pystat_fromstructstat(PyObject *module, STRUCT_STAT *st)
PyStructSequence_SET_ITEM(v, ST_BIRTHTIME_IDX,
val);
}
+#elif defined(MS_WINDOWS)
+ fill_time(module, v, -1, ST_BIRTHTIME_IDX, ST_BIRTHTIME_NS_IDX,
+ st->st_birthtime, st->st_birthtime_nsec);
#endif
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
PyStructSequence_SET_ITEM(v, ST_FLAGS_IDX,
@@ -14639,7 +14740,7 @@ DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW)
}
find_data_to_file_info(dataW, &file_info, &reparse_tag);
- _Py_attribute_data_to_stat(&file_info, reparse_tag, &entry->win32_lstat);
+ _Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL, &entry->win32_lstat);
return (PyObject *)entry;
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index ed1823faf80bd8..447a6787de8af7 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -216,6 +216,7 @@
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index 77a2b923d70027..b29c2dc62667a6 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -555,6 +555,9 @@
Include\internal
+
+ Include\internal
+
Include\internal
diff --git a/Python/fileutils.c b/Python/fileutils.c
index f48b626b444016..969b7163b5ac18 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -8,6 +8,8 @@
#ifdef MS_WINDOWS
# include
# include
+# include // FILE_DEVICE_* constants
+# include "pycore_fileutils_windows.h" // FILE_STAT_BASIC_INFORMATION
# if defined(MS_WINDOWS_GAMES) && !defined(MS_WINDOWS_DESKTOP)
# define PATHCCH_ALLOW_LONG_PATHS 0x01
# else
@@ -1056,6 +1058,13 @@ FILE_TIME_to_time_t_nsec(FILETIME *in_ptr, time_t *time_out, int* nsec_out)
*time_out = Py_SAFE_DOWNCAST((in / 10000000) - secs_between_epochs, __int64, time_t);
}
+static void
+LARGE_INTEGER_to_time_t_nsec(LARGE_INTEGER *in_ptr, time_t *time_out, int* nsec_out)
+{
+ *nsec_out = (int)(in_ptr->QuadPart % 10000000) * 100; /* FILETIME is in units of 100 nsec. */
+ *time_out = Py_SAFE_DOWNCAST((in_ptr->QuadPart / 10000000) - secs_between_epochs, __int64, time_t);
+}
+
void
_Py_time_t_to_FILE_TIME(time_t time_in, int nsec_in, FILETIME *out_ptr)
{
@@ -1085,33 +1094,126 @@ attributes_to_mode(DWORD attr)
return m;
}
+
+typedef union {
+ FILE_ID_128 id;
+ struct {
+ uint64_t st_ino;
+ uint64_t st_ino_high;
+ };
+} id_128_to_ino;
+
+
void
_Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
+ FILE_BASIC_INFO *basic_info, FILE_ID_INFO *id_info,
struct _Py_stat_struct *result)
{
memset(result, 0, sizeof(*result));
result->st_mode = attributes_to_mode(info->dwFileAttributes);
result->st_size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
- result->st_dev = info->dwVolumeSerialNumber;
- result->st_rdev = result->st_dev;
- FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_ctime, &result->st_ctime_nsec);
- FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
- FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
+ result->st_dev = id_info ? id_info->VolumeSerialNumber : info->dwVolumeSerialNumber;
+ result->st_rdev = 0;
+ /* st_ctime is deprecated, but we preserve the legacy value in our caller, not here */
+ if (basic_info) {
+ LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
+ LARGE_INTEGER_to_time_t_nsec(&basic_info->ChangeTime, &result->st_ctime, &result->st_ctime_nsec);
+ LARGE_INTEGER_to_time_t_nsec(&basic_info->LastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
+ LARGE_INTEGER_to_time_t_nsec(&basic_info->LastAccessTime, &result->st_atime, &result->st_atime_nsec);
+ } else {
+ FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
+ FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
+ FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime, &result->st_atime_nsec);
+ }
result->st_nlink = info->nNumberOfLinks;
- result->st_ino = (((uint64_t)info->nFileIndexHigh) << 32) + info->nFileIndexLow;
+
+ if (id_info) {
+ id_128_to_ino file_id;
+ file_id.id = id_info->FileId;
+ result->st_ino = file_id.st_ino;
+ result->st_ino_high = file_id.st_ino_high;
+ } else {
+ /* should only occur for DirEntry_from_find_data, in which case the
+ index is likely to be zero anyway. */
+ result->st_ino = (((uint64_t)info->nFileIndexHigh) << 32) + info->nFileIndexLow;
+ }
+
/* bpo-37834: Only actual symlinks set the S_IFLNK flag. But lstat() will
open other name surrogate reparse points without traversing them. To
detect/handle these, check st_file_attributes and st_reparse_tag. */
result->st_reparse_tag = reparse_tag;
if (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
reparse_tag == IO_REPARSE_TAG_SYMLINK) {
- /* first clear the S_IFMT bits */
- result->st_mode ^= (result->st_mode & S_IFMT);
- /* now set the bits that make this a symlink */
- result->st_mode |= S_IFLNK;
+ /* set the bits that make this a symlink */
+ result->st_mode = (result->st_mode & ~S_IFMT) | S_IFLNK;
}
result->st_file_attributes = info->dwFileAttributes;
}
+
+void
+_Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *info,
+ struct _Py_stat_struct *result)
+{
+ memset(result, 0, sizeof(*result));
+ result->st_mode = attributes_to_mode(info->FileAttributes);
+ result->st_size = info->EndOfFile.QuadPart;
+ LARGE_INTEGER_to_time_t_nsec(&info->CreationTime, &result->st_birthtime, &result->st_birthtime_nsec);
+ LARGE_INTEGER_to_time_t_nsec(&info->ChangeTime, &result->st_ctime, &result->st_ctime_nsec);
+ LARGE_INTEGER_to_time_t_nsec(&info->LastWriteTime, &result->st_mtime, &result->st_mtime_nsec);
+ LARGE_INTEGER_to_time_t_nsec(&info->LastAccessTime, &result->st_atime, &result->st_atime_nsec);
+ result->st_nlink = info->NumberOfLinks;
+ result->st_dev = info->VolumeSerialNumber.QuadPart;
+ /* File systems with less than 128-bits zero pad into this field */
+ id_128_to_ino file_id;
+ file_id.id = info->FileId128;
+ result->st_ino = file_id.st_ino;
+ result->st_ino_high = file_id.st_ino_high;
+ /* bpo-37834: Only actual symlinks set the S_IFLNK flag. But lstat() will
+ open other name surrogate reparse points without traversing them. To
+ detect/handle these, check st_file_attributes and st_reparse_tag. */
+ result->st_reparse_tag = info->ReparseTag;
+ if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
+ info->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+ /* set the bits that make this a symlink */
+ result->st_mode = (result->st_mode & ~S_IFMT) | S_IFLNK;
+ }
+ result->st_file_attributes = info->FileAttributes;
+ switch (info->DeviceType) {
+ case FILE_DEVICE_DISK:
+ case FILE_DEVICE_VIRTUAL_DISK:
+ case FILE_DEVICE_DFS:
+ case FILE_DEVICE_CD_ROM:
+ case FILE_DEVICE_CONTROLLER:
+ case FILE_DEVICE_DATALINK:
+ break;
+ case FILE_DEVICE_DISK_FILE_SYSTEM:
+ case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
+ case FILE_DEVICE_NETWORK_FILE_SYSTEM:
+ result->st_mode = (result->st_mode & ~S_IFMT) | 0x6000; /* _S_IFBLK */
+ break;
+ case FILE_DEVICE_CONSOLE:
+ case FILE_DEVICE_NULL:
+ case FILE_DEVICE_KEYBOARD:
+ case FILE_DEVICE_MODEM:
+ case FILE_DEVICE_MOUSE:
+ case FILE_DEVICE_PARALLEL_PORT:
+ case FILE_DEVICE_PRINTER:
+ case FILE_DEVICE_SCREEN:
+ case FILE_DEVICE_SERIAL_PORT:
+ case FILE_DEVICE_SOUND:
+ result->st_mode = (result->st_mode & ~S_IFMT) | _S_IFCHR;
+ break;
+ case FILE_DEVICE_NAMED_PIPE:
+ result->st_mode = (result->st_mode & ~S_IFMT) | _S_IFIFO;
+ break;
+ default:
+ if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ result->st_mode = (result->st_mode & ~S_IFMT) | _S_IFDIR;
+ }
+ break;
+ }
+}
+
#endif
/* Return information about a file.
@@ -1131,6 +1233,8 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
{
#ifdef MS_WINDOWS
BY_HANDLE_FILE_INFORMATION info;
+ FILE_BASIC_INFO basicInfo;
+ FILE_ID_INFO idInfo;
HANDLE h;
int type;
@@ -1162,14 +1266,16 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
return 0;
}
- if (!GetFileInformationByHandle(h, &info)) {
+ if (!GetFileInformationByHandle(h, &info) ||
+ !GetFileInformationByHandleEx(h, FileBasicInfo, &basicInfo, sizeof(basicInfo)) ||
+ !GetFileInformationByHandleEx(h, FileIdInfo, &idInfo, sizeof(idInfo))) {
/* The Win32 error is already set, but we also set errno for
callers who expect it */
errno = winerror_to_errno(GetLastError());
return -1;
}
- _Py_attribute_data_to_stat(&info, 0, status);
+ _Py_attribute_data_to_stat(&info, 0, &basicInfo, &idInfo, status);
return 0;
#else
return fstat(fd, status);
From 93e32d6b9320088c844e39a311b3f4be32c9d011 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Thu, 16 Mar 2023 19:03:52 +0000
Subject: [PATCH 133/280] gh-102192: Replace PyErr_Fetch/Restore etc by more
efficient alternatives (#102760)
---
Python/traceback.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/Python/traceback.c b/Python/traceback.c
index 31b85e77575efa..097f69c76abfe1 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -11,7 +11,7 @@
#include "pycore_interp.h" // PyInterpreterState.gc
#include "pycore_parser.h" // _PyParser_ASTFromString
#include "pycore_pyarena.h" // _PyArena_Free()
-#include "pycore_pyerrors.h" // _PyErr_Fetch()
+#include "pycore_pyerrors.h" // _PyErr_GetRaisedException()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_traceback.h" // EXCEPTION_TB_HEADER
@@ -242,17 +242,18 @@ _PyTraceBack_FromFrame(PyObject *tb_next, PyFrameObject *frame)
int
PyTraceBack_Here(PyFrameObject *frame)
{
- PyObject *exc, *val, *tb, *newtb;
- PyErr_Fetch(&exc, &val, &tb);
- newtb = _PyTraceBack_FromFrame(tb, frame);
+ PyObject *exc = PyErr_GetRaisedException();
+ assert(PyExceptionInstance_Check(exc));
+ PyObject *tb = PyException_GetTraceback(exc);
+ PyObject *newtb = _PyTraceBack_FromFrame(tb, frame);
+ Py_XDECREF(tb);
if (newtb == NULL) {
- _PyErr_ChainExceptions(exc, val, tb);
+ _PyErr_ChainExceptions1(exc);
return -1;
}
- assert(PyExceptionInstance_Check(val));
- PyException_SetTraceback(val, newtb);
- PyErr_Restore(exc, val, newtb);
- Py_XDECREF(tb);
+ PyException_SetTraceback(exc, newtb);
+ Py_XDECREF(newtb);
+ PyErr_SetRaisedException(exc);
return 0;
}
From 2c96fc0064cd3095ceaa6fa0f210f274a7021eae Mon Sep 17 00:00:00 2001
From: Mark Dickinson
Date: Thu, 16 Mar 2023 20:34:42 +0000
Subject: [PATCH 134/280] Fix outdated note about 'int' rounding or truncating
(#102736)
---
Doc/library/stdtypes.rst | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 550f808a16dfaa..bcfc6e5cfce611 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -335,11 +335,10 @@ Notes:
single: ceil() (in module math)
single: trunc() (in module math)
pair: numeric; conversions
- pair: C; language
- Conversion from floating point to integer may round or truncate
- as in C; see functions :func:`math.floor` and :func:`math.ceil` for
- well-defined conversions.
+ Conversion from :class:`float` to :class:`int` truncates, discarding the
+ fractional part. See functions :func:`math.floor` and :func:`math.ceil` for
+ alternative conversions.
(4)
float also accepts the strings "nan" and "inf" with an optional prefix "+"
From dd639e70aa664251ce5d3803376f10c038ebb4b5 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Thu, 16 Mar 2023 22:18:04 +0000
Subject: [PATCH 135/280] gh-102755: Add PyErr_DisplayException(exc) (#102756)
---
Doc/c-api/exceptions.rst | 6 ++
Doc/data/stable_abi.dat | 1 +
Doc/whatsnew/3.12.rst | 7 ++
Include/internal/pycore_pylifecycle.h | 1 +
Include/pythonrun.h | 1 +
Lib/test/test_stable_abi_ctypes.py | 1 +
...-03-16-14-44-29.gh-issue-102755.j1GxlV.rst | 3 +
Misc/stable_abi.toml | 2 +
Modules/_testcapi/exceptions.c | 13 +--
PC/python3dll.c | 1 +
Python/pylifecycle.c | 29 ++-----
Python/pythonrun.c | 87 +++++++++----------
Python/sysmodule.c | 2 +-
13 files changed, 76 insertions(+), 78 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-16-14-44-29.gh-issue-102755.j1GxlV.rst
diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index 8836c973f1f579..ddf7dc780b2745 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -86,6 +86,12 @@ Printing and clearing
An exception must be set when calling this function.
+.. c:function: void PyErr_DisplayException(PyObject *exc)
+
+ Print the standard traceback display of ``exc`` to ``sys.stderr``, including
+ chained exceptions and notes.
+
+ .. versionadded:: 3.12
Raising exceptions
==================
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 68ab0b5061f434..4cc06d22baaa93 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -133,6 +133,7 @@ function,PyErr_BadInternalCall,3.2,,
function,PyErr_CheckSignals,3.2,,
function,PyErr_Clear,3.2,,
function,PyErr_Display,3.2,,
+function,PyErr_DisplayException,3.12,,
function,PyErr_ExceptionMatches,3.2,,
function,PyErr_Fetch,3.2,,
function,PyErr_Format,3.2,,
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 03b1f975746787..a398cd93f73120 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -944,6 +944,10 @@ New Features
the :attr:`~BaseException.args` passed to the exception's constructor.
(Contributed by Mark Shannon in :gh:`101578`.)
+* Add :c:func:`PyErr_DisplayException`, which takes an exception instance,
+ to replace the legacy-api :c:func:`PyErr_Display`. (Contributed by
+ Irit Katriel in :gh:`102755`).
+
Porting to Python 3.12
----------------------
@@ -1077,6 +1081,9 @@ Deprecated
:c:func:`PyErr_SetRaisedException` instead.
(Contributed by Mark Shannon in :gh:`101578`.)
+* :c:func:`PyErr_Display` is deprecated. Use :c:func:`PyErr_DisplayException`
+ instead. (Contributed by Irit Katriel in :gh:`102755`).
+
Removed
-------
diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h
index e7a31807205254..a899e848bb8b3c 100644
--- a/Include/internal/pycore_pylifecycle.h
+++ b/Include/internal/pycore_pylifecycle.h
@@ -87,6 +87,7 @@ PyAPI_FUNC(PyObject*) _PyErr_WriteUnraisableDefaultHook(PyObject *unraisable);
PyAPI_FUNC(void) _PyErr_Print(PyThreadState *tstate);
PyAPI_FUNC(void) _PyErr_Display(PyObject *file, PyObject *exception,
PyObject *value, PyObject *tb);
+PyAPI_FUNC(void) _PyErr_DisplayException(PyObject *file, PyObject *exc);
PyAPI_FUNC(void) _PyThreadState_DeleteCurrent(PyThreadState *tstate);
diff --git a/Include/pythonrun.h b/Include/pythonrun.h
index 1b208b734ab1bf..41d82e89f84876 100644
--- a/Include/pythonrun.h
+++ b/Include/pythonrun.h
@@ -12,6 +12,7 @@ PyAPI_FUNC(PyObject *) Py_CompileString(const char *, const char *, int);
PyAPI_FUNC(void) PyErr_Print(void);
PyAPI_FUNC(void) PyErr_PrintEx(int);
PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *);
+PyAPI_FUNC(void) PyErr_DisplayException(PyObject *);
/* Stuff with no proper home (yet) */
diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py
index e77c1c8409880d..2feaaf8603b831 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -166,6 +166,7 @@ def test_windows_feature_macros(self):
"PyErr_CheckSignals",
"PyErr_Clear",
"PyErr_Display",
+ "PyErr_DisplayException",
"PyErr_ExceptionMatches",
"PyErr_Fetch",
"PyErr_Format",
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-16-14-44-29.gh-issue-102755.j1GxlV.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-16-14-44-29.gh-issue-102755.j1GxlV.rst
new file mode 100644
index 00000000000000..d09af8d060d405
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-16-14-44-29.gh-issue-102755.j1GxlV.rst
@@ -0,0 +1,3 @@
+Add :c:func:`PyErr_DisplayException` which takes just an exception instance,
+to replace the legacy :c:func:`PyErr_Display` which takes the ``(typ, exc,
+tb)`` triplet.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index 21ff9616133445..23baeeeae79193 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -609,6 +609,8 @@
added = '3.2'
[function.PyErr_Display]
added = '3.2'
+[function.PyErr_DisplayException]
+ added = '3.12'
[function.PyErr_ExceptionMatches]
added = '3.2'
[function.PyErr_Fetch]
diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c
index c64b823663c32f..1922ca3beb7916 100644
--- a/Modules/_testcapi/exceptions.c
+++ b/Modules/_testcapi/exceptions.c
@@ -39,20 +39,13 @@ err_restore(PyObject *self, PyObject *args) {
static PyObject *
exception_print(PyObject *self, PyObject *args)
{
- PyObject *value;
- PyObject *tb = NULL;
+ PyObject *exc;
- if (!PyArg_ParseTuple(args, "O:exception_print", &value)) {
+ if (!PyArg_ParseTuple(args, "O:exception_print", &exc)) {
return NULL;
}
- if (PyExceptionInstance_Check(value)) {
- tb = PyException_GetTraceback(value);
- }
-
- PyErr_Display((PyObject *) Py_TYPE(value), value, tb);
- Py_XDECREF(tb);
-
+ PyErr_DisplayException(exc);
Py_RETURN_NONE;
}
diff --git a/PC/python3dll.c b/PC/python3dll.c
index e300819365756e..706affa18351b3 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -192,6 +192,7 @@ EXPORT_FUNC(PyErr_BadInternalCall)
EXPORT_FUNC(PyErr_CheckSignals)
EXPORT_FUNC(PyErr_Clear)
EXPORT_FUNC(PyErr_Display)
+EXPORT_FUNC(PyErr_DisplayException)
EXPORT_FUNC(PyErr_ExceptionMatches)
EXPORT_FUNC(PyErr_Fetch)
EXPORT_FUNC(PyErr_Format)
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 82e94090a6027a..d0c65cc1f7fd44 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -2537,41 +2537,28 @@ _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp,
static int
_Py_FatalError_PrintExc(PyThreadState *tstate)
{
- PyObject *ferr, *res;
- PyObject *exception, *v, *tb;
- int has_tb;
-
- _PyErr_Fetch(tstate, &exception, &v, &tb);
- if (exception == NULL) {
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
+ if (exc == NULL) {
/* No current exception */
return 0;
}
- ferr = _PySys_GetAttr(tstate, &_Py_ID(stderr));
+ PyObject *ferr = _PySys_GetAttr(tstate, &_Py_ID(stderr));
if (ferr == NULL || ferr == Py_None) {
/* sys.stderr is not set yet or set to None,
no need to try to display the exception */
return 0;
}
- _PyErr_NormalizeException(tstate, &exception, &v, &tb);
- if (tb == NULL) {
- tb = Py_NewRef(Py_None);
- }
- PyException_SetTraceback(v, tb);
- if (exception == NULL) {
- /* PyErr_NormalizeException() failed */
- return 0;
- }
+ PyErr_DisplayException(exc);
- has_tb = (tb != Py_None);
- PyErr_Display(exception, v, tb);
- Py_XDECREF(exception);
- Py_XDECREF(v);
+ PyObject *tb = PyException_GetTraceback(exc);
+ int has_tb = (tb != NULL) && (tb != Py_None);
Py_XDECREF(tb);
+ Py_XDECREF(exc);
/* sys.stderr may be buffered: call sys.stderr.flush() */
- res = PyObject_CallMethodNoArgs(ferr, &_Py_ID(flush));
+ PyObject *res = PyObject_CallMethodNoArgs(ferr, &_Py_ID(flush));
if (res == NULL) {
_PyErr_Clear(tstate);
}
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 5381b105a39fed..07d119a67847c6 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -761,39 +761,34 @@ handle_system_exit(void)
static void
_PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars)
{
- PyObject *exception, *v, *tb, *hook;
-
+ PyObject *typ = NULL, *tb = NULL;
handle_system_exit();
- _PyErr_Fetch(tstate, &exception, &v, &tb);
- if (exception == NULL) {
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
+ if (exc == NULL) {
goto done;
}
-
- _PyErr_NormalizeException(tstate, &exception, &v, &tb);
+ assert(PyExceptionInstance_Check(exc));
+ typ = Py_NewRef(Py_TYPE(exc));
+ tb = PyException_GetTraceback(exc);
if (tb == NULL) {
tb = Py_NewRef(Py_None);
}
- PyException_SetTraceback(v, tb);
- if (exception == NULL) {
- goto done;
- }
- /* Now we know v != NULL too */
if (set_sys_last_vars) {
- if (_PySys_SetAttr(&_Py_ID(last_type), exception) < 0) {
+ if (_PySys_SetAttr(&_Py_ID(last_type), typ) < 0) {
_PyErr_Clear(tstate);
}
- if (_PySys_SetAttr(&_Py_ID(last_value), v) < 0) {
+ if (_PySys_SetAttr(&_Py_ID(last_value), exc) < 0) {
_PyErr_Clear(tstate);
}
if (_PySys_SetAttr(&_Py_ID(last_traceback), tb) < 0) {
_PyErr_Clear(tstate);
}
}
- hook = _PySys_GetAttr(tstate, &_Py_ID(excepthook));
+ PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(excepthook));
if (_PySys_Audit(tstate, "sys.excepthook", "OOOO", hook ? hook : Py_None,
- exception, v, tb) < 0) {
+ typ, exc, tb) < 0) {
if (PyErr_ExceptionMatches(PyExc_RuntimeError)) {
PyErr_Clear();
goto done;
@@ -802,46 +797,34 @@ _PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars)
}
if (hook) {
PyObject* stack[3];
- PyObject *result;
-
- stack[0] = exception;
- stack[1] = v;
+ stack[0] = typ;
+ stack[1] = exc;
stack[2] = tb;
- result = _PyObject_FastCall(hook, stack, 3);
+ PyObject *result = _PyObject_FastCall(hook, stack, 3);
if (result == NULL) {
handle_system_exit();
- PyObject *exception2, *v2, *tb2;
- _PyErr_Fetch(tstate, &exception2, &v2, &tb2);
- _PyErr_NormalizeException(tstate, &exception2, &v2, &tb2);
- /* It should not be possible for exception2 or v2
- to be NULL. However PyErr_Display() can't
- tolerate NULLs, so just be safe. */
- if (exception2 == NULL) {
- exception2 = Py_NewRef(Py_None);
- }
- if (v2 == NULL) {
- v2 = Py_NewRef(Py_None);
- }
+ PyObject *exc2 = _PyErr_GetRaisedException(tstate);
+ assert(exc2 && PyExceptionInstance_Check(exc2));
fflush(stdout);
PySys_WriteStderr("Error in sys.excepthook:\n");
- PyErr_Display(exception2, v2, tb2);
+ PyErr_DisplayException(exc2);
PySys_WriteStderr("\nOriginal exception was:\n");
- PyErr_Display(exception, v, tb);
- Py_DECREF(exception2);
- Py_DECREF(v2);
- Py_XDECREF(tb2);
+ PyErr_DisplayException(exc);
+ Py_DECREF(exc2);
+ }
+ else {
+ Py_DECREF(result);
}
- Py_XDECREF(result);
}
else {
PySys_WriteStderr("sys.excepthook is missing\n");
- PyErr_Display(exception, v, tb);
+ PyErr_DisplayException(exc);
}
done:
- Py_XDECREF(exception);
- Py_XDECREF(v);
+ Py_XDECREF(typ);
+ Py_XDECREF(exc);
Py_XDECREF(tb);
}
@@ -1505,7 +1488,7 @@ print_exception_recursive(struct exception_print_context *ctx, PyObject *value)
#define PyErr_MAX_GROUP_DEPTH 10
void
-_PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *tb)
+_PyErr_Display(PyObject *file, PyObject *unused, PyObject *value, PyObject *tb)
{
assert(file != NULL && file != Py_None);
if (PyExceptionInstance_Check(value)
@@ -1513,10 +1496,12 @@ _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *t
/* Put the traceback on the exception, otherwise it won't get
displayed. See issue #18776. */
PyObject *cur_tb = PyException_GetTraceback(value);
- if (cur_tb == NULL)
+ if (cur_tb == NULL) {
PyException_SetTraceback(value, tb);
- else
+ }
+ else {
Py_DECREF(cur_tb);
+ }
}
struct exception_print_context ctx;
@@ -1552,7 +1537,7 @@ _PyErr_Display(PyObject *file, PyObject *exception, PyObject *value, PyObject *t
}
void
-PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
+PyErr_Display(PyObject *unused, PyObject *value, PyObject *tb)
{
PyThreadState *tstate = _PyThreadState_GET();
PyObject *file = _PySys_GetAttr(tstate, &_Py_ID(stderr));
@@ -1565,10 +1550,20 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
return;
}
Py_INCREF(file);
- _PyErr_Display(file, exception, value, tb);
+ _PyErr_Display(file, NULL, value, tb);
Py_DECREF(file);
}
+void _PyErr_DisplayException(PyObject *file, PyObject *exc)
+{
+ _PyErr_Display(file, NULL, exc, NULL);
+}
+
+void PyErr_DisplayException(PyObject *exc)
+{
+ PyErr_Display(NULL, exc, NULL);
+}
+
PyObject *
PyRun_StringFlags(const char *str, int start, PyObject *globals,
PyObject *locals, PyCompilerFlags *flags)
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index d282104dcad414..cc5b9a6d418bfa 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -742,7 +742,7 @@ sys_excepthook_impl(PyObject *module, PyObject *exctype, PyObject *value,
PyObject *traceback)
/*[clinic end generated code: output=18d99fdda21b6b5e input=ecf606fa826f19d9]*/
{
- PyErr_Display(exctype, value, traceback);
+ PyErr_Display(NULL, value, traceback);
Py_RETURN_NONE;
}
From 0405b4505acfa57f4da38f1c2eb13f9fdf0cb1e7 Mon Sep 17 00:00:00 2001
From: Steve Dower
Date: Fri, 17 Mar 2023 01:07:07 +0000
Subject: [PATCH 136/280] Increase stack reserve size for Windows debug builds
to avoid test crashes (GH-102764)
---
PCbuild/python.vcxproj | 2 +-
PCbuild/pythonw.vcxproj | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/PCbuild/python.vcxproj b/PCbuild/python.vcxproj
index d07db3a6815058..f4640454a73070 100644
--- a/PCbuild/python.vcxproj
+++ b/PCbuild/python.vcxproj
@@ -95,7 +95,7 @@
Console
2000000
- 4000000
+ 8000000
diff --git a/PCbuild/pythonw.vcxproj b/PCbuild/pythonw.vcxproj
index e7216dec3a1af0..e23635e5ea9411 100644
--- a/PCbuild/pythonw.vcxproj
+++ b/PCbuild/pythonw.vcxproj
@@ -89,7 +89,8 @@
- 2000000
+ 2000000
+ 8000000
From 8d2b6b563ad894dd5c92aad3535efb32e72ee0b7 Mon Sep 17 00:00:00 2001
From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
Date: Fri, 17 Mar 2023 06:58:43 +0530
Subject: [PATCH 137/280] GH-78530: add support for generators in
`asyncio.wait` (#102761)
---
Doc/library/asyncio-task.rst | 4 ++++
Doc/whatsnew/3.12.rst | 3 +++
Lib/test/test_asyncio/test_tasks.py | 16 ++++++++++++++++
...2023-03-16-16-43-04.gh-issue-78530.Lr8eq_.rst | 1 +
4 files changed, 24 insertions(+)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-16-16-43-04.gh-issue-78530.Lr8eq_.rst
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index 5f1449e1b599ef..a0900cd25a7731 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -804,6 +804,10 @@ Waiting Primitives
.. versionchanged:: 3.11
Passing coroutine objects to ``wait()`` directly is forbidden.
+ .. versionchanged:: 3.12
+ Added support for generators yielding tasks.
+
+
.. function:: as_completed(aws, *, timeout=None)
Run :ref:`awaitable objects ` in the *aws*
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index a398cd93f73120..b55b9619fac226 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -241,6 +241,9 @@ asyncio
:mod:`asyncio` does not support legacy generator-based coroutines.
(Contributed by Kumar Aditya in :gh:`102748`.)
+* :func:`asyncio.wait` now accepts generators yielding tasks.
+ (Contributed by Kumar Aditya in :gh:`78530`.)
+
inspect
-------
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index 5b935b526541a1..731fa0c5a60b9b 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -1373,6 +1373,22 @@ async def foo():
self.assertEqual(res, 42)
self.assertAlmostEqual(0.15, loop.time())
+
+ def test_wait_generator(self):
+ async def func(a):
+ return a
+
+ loop = self.new_test_loop()
+
+ async def main():
+ tasks = (self.new_task(loop, func(i)) for i in range(10))
+ done, pending = await asyncio.wait(tasks, return_when=asyncio.ALL_COMPLETED)
+ self.assertEqual(len(done), 10)
+ self.assertEqual(len(pending), 0)
+
+ loop.run_until_complete(main())
+
+
def test_as_completed(self):
def gen():
diff --git a/Misc/NEWS.d/next/Library/2023-03-16-16-43-04.gh-issue-78530.Lr8eq_.rst b/Misc/NEWS.d/next/Library/2023-03-16-16-43-04.gh-issue-78530.Lr8eq_.rst
new file mode 100644
index 00000000000000..bdb46d08c5c4af
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-16-16-43-04.gh-issue-78530.Lr8eq_.rst
@@ -0,0 +1 @@
+:func:`asyncio.wait` now accepts generators yielding tasks. Patch by Kumar Aditya.
From b538145f86a58ffdb365400eaa2c7b82c52aeec2 Mon Sep 17 00:00:00 2001
From: Inada Naoki
Date: Fri, 17 Mar 2023 22:39:09 +0900
Subject: [PATCH 138/280] gh-102701: Fix overflow in dictobject.c (GH-102750)
---
Lib/test/test_bigmem.py | 9 +++++++++
.../2023-03-16-17-24-44.gh-issue-102701.iNGVaS.rst | 1 +
Objects/dictobject.c | 2 +-
3 files changed, 11 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-16-17-24-44.gh-issue-102701.iNGVaS.rst
diff --git a/Lib/test/test_bigmem.py b/Lib/test/test_bigmem.py
index 859f1539e20b80..c9ab1c1de9e186 100644
--- a/Lib/test/test_bigmem.py
+++ b/Lib/test/test_bigmem.py
@@ -1248,6 +1248,15 @@ def test_sort(self, size):
self.assertEqual(l[-10:], [5] * 10)
+class DictTest(unittest.TestCase):
+
+ @bigmemtest(size=357913941, memuse=160)
+ def test_dict(self, size):
+ # https://github.com/python/cpython/issues/102701
+ d = dict.fromkeys(range(size))
+ d[size] = 1
+
+
if __name__ == '__main__':
if len(sys.argv) > 1:
support.set_memlimit(sys.argv[1])
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-16-17-24-44.gh-issue-102701.iNGVaS.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-16-17-24-44.gh-issue-102701.iNGVaS.rst
new file mode 100644
index 00000000000000..4e1f31893377ba
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-16-17-24-44.gh-issue-102701.iNGVaS.rst
@@ -0,0 +1 @@
+Fix overflow when creating very large dict.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 227e438a8dfffc..53f9a380346a0d 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -596,7 +596,7 @@ new_keys_object(PyInterpreterState *interp, uint8_t log2_size, bool unicode)
assert(log2_size >= PyDict_LOG_MINSIZE);
- usable = USABLE_FRACTION(1<
Date: Fri, 17 Mar 2023 11:01:10 -0600
Subject: [PATCH 139/280] gh-102781: fix cwd dependence in cases generator
(#102782)
---
Tools/cases_generator/generate_cases.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 31b6926f805485..f9369f1ede897e 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -176,8 +176,9 @@ def __init__(
self.postfix = ""
self.emit_line_directives = emit_line_directives
self.lineno = 1
+ filename = os.path.relpath(self.stream.name, ROOT)
# Make filename more user-friendly and less platform-specific
- filename = self.stream.name.replace("\\", "/")
+ filename = filename.replace("\\", "/")
if filename.startswith("./"):
filename = filename[2:]
if filename.endswith(".new"):
@@ -778,6 +779,7 @@ def parse_file(self, filename: str, instrs_idx: dict[str, int]) -> None:
with open(filename) as file:
src = file.read()
+ filename = os.path.relpath(filename, ROOT)
# Make filename more user-friendly and less platform-specific
filename = filename.replace("\\", "/")
if filename.startswith("./"):
From d451dd05e84894af4505902f0f36af5dd887544e Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Fri, 17 Mar 2023 14:06:52 -0500
Subject: [PATCH 140/280] Simplify and improve accuracy for subnormals in
hypot() (GH-102785)
---
Modules/mathmodule.c | 63 ++++++++++++++++++++------------------------
1 file changed, 28 insertions(+), 35 deletions(-)
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index ae9e3211c072d8..48cd9a642de150 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -2498,7 +2498,7 @@ verified for 1 billion random inputs with n=5. [7]
static inline double
vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
{
- double x, h, scale, oldcsum, csum = 1.0, frac1 = 0.0, frac2 = 0.0;
+ double x, h, scale, csum = 1.0, frac1 = 0.0, frac2 = 0.0;
DoubleLength pr, sm;
int max_e;
Py_ssize_t i;
@@ -2513,49 +2513,42 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
return max;
}
frexp(max, &max_e);
- if (max_e >= -1023) {
- scale = ldexp(1.0, -max_e);
- assert(max * scale >= 0.5);
- assert(max * scale < 1.0);
+ if (max_e < -1023) {
+ /* When max_e < -1023, ldexp(1.0, -max_e) would overflow.
+ So we first perform lossless scaling from subnormals back to normals,
+ then recurse back to vector_norm(), and then finally undo the scaling.
+ */
for (i=0 ; i < n ; i++) {
- x = vec[i];
- assert(Py_IS_FINITE(x) && fabs(x) <= max);
+ vec[i] /= DBL_MIN;
+ }
+ return DBL_MIN * vector_norm(n, vec, max / DBL_MIN, found_nan);
+ }
+ scale = ldexp(1.0, -max_e);
+ assert(max * scale >= 0.5);
+ assert(max * scale < 1.0);
+ for (i=0 ; i < n ; i++) {
+ x = vec[i];
+ assert(Py_IS_FINITE(x) && fabs(x) <= max);
- x *= scale;
- assert(fabs(x) < 1.0);
+ x *= scale;
+ assert(fabs(x) < 1.0);
- pr = dl_mul(x, x);
- assert(pr.hi <= 1.0);
+ pr = dl_mul(x, x);
+ assert(pr.hi <= 1.0);
- sm = dl_fast_sum(csum, pr.hi);
- csum = sm.hi;
- frac1 += pr.lo;
- frac2 += sm.lo;
- }
- h = sqrt(csum - 1.0 + (frac1 + frac2));
- pr = dl_mul(-h, h);
sm = dl_fast_sum(csum, pr.hi);
csum = sm.hi;
frac1 += pr.lo;
frac2 += sm.lo;
- x = csum - 1.0 + (frac1 + frac2);
- return (h + x / (2.0 * h)) / scale;
}
- /* When max_e < -1023, ldexp(1.0, -max_e) overflows.
- So instead of multiplying by a scale, we just divide by *max*.
- */
- for (i=0 ; i < n ; i++) {
- x = vec[i];
- assert(Py_IS_FINITE(x) && fabs(x) <= max);
- x /= max;
- x = x*x;
- assert(x <= 1.0);
- assert(fabs(csum) >= fabs(x));
- oldcsum = csum;
- csum += x;
- frac1 += (oldcsum - csum) + x;
- }
- return max * sqrt(csum - 1.0 + frac1);
+ h = sqrt(csum - 1.0 + (frac1 + frac2));
+ pr = dl_mul(-h, h);
+ sm = dl_fast_sum(csum, pr.hi);
+ csum = sm.hi;
+ frac1 += pr.lo;
+ frac2 += sm.lo;
+ x = csum - 1.0 + (frac1 + frac2);
+ return (h + x / (2.0 * h)) / scale;
}
#define NUM_STACK_ELEMS 16
From e2e5fa6e794d5fb4a7fda966698b8c21d049c8a5 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Sat, 18 Mar 2023 07:19:38 +0000
Subject: [PATCH 141/280] gh-102799: remove unnecessary calls to sys.exc_info()
in tests (#102800)
---
Lib/test/test_asyncio/test_unix_events.py | 4 +-
Lib/test/test_exceptions.py | 18 +-
Lib/test/test_socket.py | 4 +-
Lib/test/test_sys.py | 4 +-
Lib/test/test_traceback.py | 218 +++++++++++-----------
5 files changed, 124 insertions(+), 124 deletions(-)
diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py
index 33d0ea15c6de0e..96999470a7c69a 100644
--- a/Lib/test/test_asyncio/test_unix_events.py
+++ b/Lib/test/test_asyncio/test_unix_events.py
@@ -1889,8 +1889,8 @@ async def test_fork_not_share_event_loop(self):
os.write(w, b'LOOP:' + str(id(loop)).encode())
except RuntimeError:
os.write(w, b'NO LOOP')
- except:
- os.write(w, b'ERROR:' + ascii(sys.exc_info()).encode())
+ except BaseException as e:
+ os.write(w, b'ERROR:' + ascii(e).encode())
finally:
os._exit(0)
else:
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 4ae71e431c56dc..284f26be717794 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -599,8 +599,8 @@ def test_notes(self):
def testWithTraceback(self):
try:
raise IndexError(4)
- except:
- tb = sys.exc_info()[2]
+ except Exception as e:
+ tb = e.__traceback__
e = BaseException().with_traceback(tb)
self.assertIsInstance(e, BaseException)
@@ -653,8 +653,8 @@ def test_invalid_delattr(self):
def testNoneClearsTracebackAttr(self):
try:
raise IndexError(4)
- except:
- tb = sys.exc_info()[2]
+ except Exception as e:
+ tb = e.__traceback__
e = Exception()
e.__traceback__ = tb
@@ -1337,11 +1337,11 @@ class MyException(Exception, metaclass=Meta):
def g():
try:
return g()
- except RecursionError:
- return sys.exc_info()
- e, v, tb = g()
- self.assertIsInstance(v, RecursionError, type(v))
- self.assertIn("maximum recursion depth exceeded", str(v))
+ except RecursionError as e:
+ return e
+ exc = g()
+ self.assertIsInstance(exc, RecursionError, type(exc))
+ self.assertIn("maximum recursion depth exceeded", str(exc))
@cpython_only
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index a313da29b4a4fd..60f106f2d1a1c2 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -5492,10 +5492,10 @@ def alarm_handler(signal, frame):
self.fail("caught timeout instead of Alarm")
except Alarm:
pass
- except:
+ except BaseException as e:
self.fail("caught other exception instead of Alarm:"
" %s(%s):\n%s" %
- (sys.exc_info()[:2] + (traceback.format_exc(),)))
+ (type(e), e, traceback.format_exc()))
else:
self.fail("nothing caught")
finally:
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index b839985def9a12..fb578c5ae6e5d5 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1649,8 +1649,8 @@ def test_pythontypes(self):
check(_ast.AST(), size('P'))
try:
raise TypeError
- except TypeError:
- tb = sys.exc_info()[2]
+ except TypeError as e:
+ tb = e.__traceback__
# traceback
if tb is not None:
check(tb, size('2P2i'))
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 7ef93b3f0ac332..1c5d1ab82c8e9c 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -296,15 +296,15 @@ class PrintExceptionAtExit(object):
def __init__(self):
try:
x = 1 / 0
- except Exception:
- self.exc_info = sys.exc_info()
- # self.exc_info[1] (traceback) contains frames:
+ except Exception as e:
+ self.exc = e
+ # self.exc.__traceback__ contains frames:
# explicitly clear the reference to self in the current
# frame to break a reference cycle
self = None
def __del__(self):
- traceback.print_exception(*self.exc_info)
+ traceback.print_exception(self.exc)
# Keep a reference in the module namespace to call the destructor
# when the module is unloaded
@@ -923,8 +923,8 @@ def check_traceback_format(self, cleanup_func=None):
from _testcapi import traceback_print
try:
self.some_exception()
- except KeyError:
- type_, value, tb = sys.exc_info()
+ except KeyError as e:
+ tb = e.__traceback__
if cleanup_func is not None:
# Clear the inner frames, not this one
cleanup_func(tb.tb_next)
@@ -1228,8 +1228,8 @@ def __eq__(self, other):
except UnhashableException:
try:
raise ex1
- except UnhashableException:
- exc_type, exc_val, exc_tb = sys.exc_info()
+ except UnhashableException as e:
+ exc_val = e
with captured_output("stderr") as stderr_f:
exception_print(exc_val)
@@ -2133,8 +2133,8 @@ def assertEqualExcept(actual, expected, ignore):
def test_extract_tb(self):
try:
self.last_raises5()
- except Exception:
- exc_type, exc_value, tb = sys.exc_info()
+ except Exception as e:
+ tb = e.__traceback__
def extract(**kwargs):
return traceback.extract_tb(tb, **kwargs)
@@ -2160,12 +2160,12 @@ def extract(**kwargs):
def test_format_exception(self):
try:
self.last_raises5()
- except Exception:
- exc_type, exc_value, tb = sys.exc_info()
+ except Exception as e:
+ exc = e
# [1:-1] to exclude "Traceback (...)" header and
# exception type and value
def extract(**kwargs):
- return traceback.format_exception(exc_type, exc_value, tb, **kwargs)[1:-1]
+ return traceback.format_exception(exc, **kwargs)[1:-1]
with support.swap_attr(sys, 'tracebacklimit', 1000):
nolim = extract()
@@ -2203,8 +2203,8 @@ def inner():
try:
outer()
- except:
- type_, value, tb = sys.exc_info()
+ except BaseException as e:
+ tb = e.__traceback__
# Initial assertion: there's one local in the inner frame.
inner_frame = tb.tb_next.tb_next.tb_next.tb_frame
@@ -2282,8 +2282,8 @@ def deeper():
def test_walk_tb(self):
try:
1/0
- except Exception:
- _, _, tb = sys.exc_info()
+ except Exception as e:
+ tb = e.__traceback__
s = list(traceback.walk_tb(tb))
self.assertEqual(len(s), 1)
@@ -2386,10 +2386,10 @@ def f():
def g():
try:
f()
- except:
- return sys.exc_info()
+ except Exception as e:
+ return e.__traceback__
- exc_info = g()
+ tb = g()
class Skip_G(traceback.StackSummary):
def format_frame_summary(self, frame_summary):
@@ -2398,7 +2398,7 @@ def format_frame_summary(self, frame_summary):
return super().format_frame_summary(frame_summary)
stack = Skip_G.extract(
- traceback.walk_tb(exc_info[2])).format()
+ traceback.walk_tb(tb)).format()
self.assertEqual(len(stack), 1)
lno = f.__code__.co_firstlineno + 1
@@ -2416,17 +2416,17 @@ class TestTracebackException(unittest.TestCase):
def test_smoke(self):
try:
1/0
- except Exception:
- exc_info = sys.exc_info()
- exc = traceback.TracebackException(*exc_info)
+ except Exception as e:
+ exc_obj = e
+ exc = traceback.TracebackException.from_exception(e)
expected_stack = traceback.StackSummary.extract(
- traceback.walk_tb(exc_info[2]))
+ traceback.walk_tb(e.__traceback__))
self.assertEqual(None, exc.__cause__)
self.assertEqual(None, exc.__context__)
self.assertEqual(False, exc.__suppress_context__)
self.assertEqual(expected_stack, exc.stack)
- self.assertEqual(exc_info[0], exc.exc_type)
- self.assertEqual(str(exc_info[1]), str(exc))
+ self.assertEqual(type(exc_obj), exc.exc_type)
+ self.assertEqual(str(exc_obj), str(exc))
def test_from_exception(self):
# Check all the parameters are accepted.
@@ -2435,9 +2435,10 @@ def foo():
try:
foo()
except Exception as e:
- exc_info = sys.exc_info()
+ exc_obj = e
+ tb = e.__traceback__
self.expected_stack = traceback.StackSummary.extract(
- traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
+ traceback.walk_tb(tb), limit=1, lookup_lines=False,
capture_locals=True)
self.exc = traceback.TracebackException.from_exception(
e, limit=1, lookup_lines=False, capture_locals=True)
@@ -2447,8 +2448,8 @@ def foo():
self.assertEqual(None, exc.__context__)
self.assertEqual(False, exc.__suppress_context__)
self.assertEqual(expected_stack, exc.stack)
- self.assertEqual(exc_info[0], exc.exc_type)
- self.assertEqual(str(exc_info[1]), str(exc))
+ self.assertEqual(type(exc_obj), exc.exc_type)
+ self.assertEqual(str(exc_obj), str(exc))
def test_cause(self):
try:
@@ -2459,18 +2460,18 @@ def test_cause(self):
exc_context = traceback.TracebackException(*exc_info_context)
cause = Exception("cause")
raise Exception("uh oh") from cause
- except Exception:
- exc_info = sys.exc_info()
- exc = traceback.TracebackException(*exc_info)
+ except Exception as e:
+ exc_obj = e
+ exc = traceback.TracebackException.from_exception(e)
expected_stack = traceback.StackSummary.extract(
- traceback.walk_tb(exc_info[2]))
+ traceback.walk_tb(e.__traceback__))
exc_cause = traceback.TracebackException(Exception, cause, None)
self.assertEqual(exc_cause, exc.__cause__)
self.assertEqual(exc_context, exc.__context__)
self.assertEqual(True, exc.__suppress_context__)
self.assertEqual(expected_stack, exc.stack)
- self.assertEqual(exc_info[0], exc.exc_type)
- self.assertEqual(str(exc_info[1]), str(exc))
+ self.assertEqual(type(exc_obj), exc.exc_type)
+ self.assertEqual(str(exc_obj), str(exc))
def test_context(self):
try:
@@ -2480,17 +2481,17 @@ def test_context(self):
exc_info_context = sys.exc_info()
exc_context = traceback.TracebackException(*exc_info_context)
raise Exception("uh oh")
- except Exception:
- exc_info = sys.exc_info()
- exc = traceback.TracebackException(*exc_info)
+ except Exception as e:
+ exc_obj = e
+ exc = traceback.TracebackException.from_exception(e)
expected_stack = traceback.StackSummary.extract(
- traceback.walk_tb(exc_info[2]))
+ traceback.walk_tb(e.__traceback__))
self.assertEqual(None, exc.__cause__)
self.assertEqual(exc_context, exc.__context__)
self.assertEqual(False, exc.__suppress_context__)
self.assertEqual(expected_stack, exc.stack)
- self.assertEqual(exc_info[0], exc.exc_type)
- self.assertEqual(str(exc_info[1]), str(exc))
+ self.assertEqual(type(exc_obj), exc.exc_type)
+ self.assertEqual(str(exc_obj), str(exc))
def test_long_context_chain(self):
def f():
@@ -2501,12 +2502,12 @@ def f():
try:
f()
- except RecursionError:
- exc_info = sys.exc_info()
+ except RecursionError as e:
+ exc_obj = e
else:
self.fail("Exception not raised")
- te = traceback.TracebackException(*exc_info)
+ te = traceback.TracebackException.from_exception(exc_obj)
res = list(te.format())
# many ZeroDiv errors followed by the RecursionError
@@ -2524,18 +2525,18 @@ def test_compact_with_cause(self):
finally:
cause = Exception("cause")
raise Exception("uh oh") from cause
- except Exception:
- exc_info = sys.exc_info()
- exc = traceback.TracebackException(*exc_info, compact=True)
+ except Exception as e:
+ exc_obj = e
+ exc = traceback.TracebackException.from_exception(exc_obj, compact=True)
expected_stack = traceback.StackSummary.extract(
- traceback.walk_tb(exc_info[2]))
+ traceback.walk_tb(exc_obj.__traceback__))
exc_cause = traceback.TracebackException(Exception, cause, None)
self.assertEqual(exc_cause, exc.__cause__)
self.assertEqual(None, exc.__context__)
self.assertEqual(True, exc.__suppress_context__)
self.assertEqual(expected_stack, exc.stack)
- self.assertEqual(exc_info[0], exc.exc_type)
- self.assertEqual(str(exc_info[1]), str(exc))
+ self.assertEqual(type(exc_obj), exc.exc_type)
+ self.assertEqual(str(exc_obj), str(exc))
def test_compact_no_cause(self):
try:
@@ -2545,37 +2546,37 @@ def test_compact_no_cause(self):
exc_info_context = sys.exc_info()
exc_context = traceback.TracebackException(*exc_info_context)
raise Exception("uh oh")
- except Exception:
- exc_info = sys.exc_info()
- exc = traceback.TracebackException(*exc_info, compact=True)
+ except Exception as e:
+ exc_obj = e
+ exc = traceback.TracebackException.from_exception(e, compact=True)
expected_stack = traceback.StackSummary.extract(
- traceback.walk_tb(exc_info[2]))
+ traceback.walk_tb(exc_obj.__traceback__))
self.assertEqual(None, exc.__cause__)
self.assertEqual(exc_context, exc.__context__)
self.assertEqual(False, exc.__suppress_context__)
self.assertEqual(expected_stack, exc.stack)
- self.assertEqual(exc_info[0], exc.exc_type)
- self.assertEqual(str(exc_info[1]), str(exc))
+ self.assertEqual(type(exc_obj), exc.exc_type)
+ self.assertEqual(str(exc_obj), str(exc))
def test_no_refs_to_exception_and_traceback_objects(self):
try:
1/0
- except Exception:
- exc_info = sys.exc_info()
+ except Exception as e:
+ exc_obj = e
- refcnt1 = sys.getrefcount(exc_info[1])
- refcnt2 = sys.getrefcount(exc_info[2])
- exc = traceback.TracebackException(*exc_info)
- self.assertEqual(sys.getrefcount(exc_info[1]), refcnt1)
- self.assertEqual(sys.getrefcount(exc_info[2]), refcnt2)
+ refcnt1 = sys.getrefcount(exc_obj)
+ refcnt2 = sys.getrefcount(exc_obj.__traceback__)
+ exc = traceback.TracebackException.from_exception(exc_obj)
+ self.assertEqual(sys.getrefcount(exc_obj), refcnt1)
+ self.assertEqual(sys.getrefcount(exc_obj.__traceback__), refcnt2)
def test_comparison_basic(self):
try:
1/0
- except Exception:
- exc_info = sys.exc_info()
- exc = traceback.TracebackException(*exc_info)
- exc2 = traceback.TracebackException(*exc_info)
+ except Exception as e:
+ exc_obj = e
+ exc = traceback.TracebackException.from_exception(exc_obj)
+ exc2 = traceback.TracebackException.from_exception(exc_obj)
self.assertIsNot(exc, exc2)
self.assertEqual(exc, exc2)
self.assertNotEqual(exc, object())
@@ -2594,28 +2595,28 @@ def raise_with_locals():
try:
raise_with_locals()
- except Exception:
- exc_info = sys.exc_info()
+ except Exception as e:
+ exc_obj = e
- exc = traceback.TracebackException(*exc_info)
- exc1 = traceback.TracebackException(*exc_info, limit=10)
- exc2 = traceback.TracebackException(*exc_info, limit=2)
+ exc = traceback.TracebackException.from_exception(exc_obj)
+ exc1 = traceback.TracebackException.from_exception(exc_obj, limit=10)
+ exc2 = traceback.TracebackException.from_exception(exc_obj, limit=2)
self.assertEqual(exc, exc1) # limit=10 gets all frames
self.assertNotEqual(exc, exc2) # limit=2 truncates the output
# locals change the output
- exc3 = traceback.TracebackException(*exc_info, capture_locals=True)
+ exc3 = traceback.TracebackException.from_exception(exc_obj, capture_locals=True)
self.assertNotEqual(exc, exc3)
# there are no locals in the innermost frame
- exc4 = traceback.TracebackException(*exc_info, limit=-1)
- exc5 = traceback.TracebackException(*exc_info, limit=-1, capture_locals=True)
+ exc4 = traceback.TracebackException.from_exception(exc_obj, limit=-1)
+ exc5 = traceback.TracebackException.from_exception(exc_obj, limit=-1, capture_locals=True)
self.assertEqual(exc4, exc5)
# there are locals in the next-to-innermost frame
- exc6 = traceback.TracebackException(*exc_info, limit=-2)
- exc7 = traceback.TracebackException(*exc_info, limit=-2, capture_locals=True)
+ exc6 = traceback.TracebackException.from_exception(exc_obj, limit=-2)
+ exc7 = traceback.TracebackException.from_exception(exc_obj, limit=-2, capture_locals=True)
self.assertNotEqual(exc6, exc7)
def test_comparison_equivalent_exceptions_are_equal(self):
@@ -2623,8 +2624,8 @@ def test_comparison_equivalent_exceptions_are_equal(self):
for _ in range(2):
try:
1/0
- except:
- excs.append(traceback.TracebackException(*sys.exc_info()))
+ except Exception as e:
+ excs.append(traceback.TracebackException.from_exception(e))
self.assertEqual(excs[0], excs[1])
self.assertEqual(list(excs[0].format()), list(excs[1].format()))
@@ -2640,9 +2641,9 @@ def __eq__(self, other):
except UnhashableException:
try:
raise ex1
- except UnhashableException:
- exc_info = sys.exc_info()
- exc = traceback.TracebackException(*exc_info)
+ except UnhashableException as e:
+ exc_obj = e
+ exc = traceback.TracebackException.from_exception(exc_obj)
formatted = list(exc.format())
self.assertIn('UnhashableException: ex2\n', formatted[2])
self.assertIn('UnhashableException: ex1\n', formatted[6])
@@ -2655,11 +2656,10 @@ def recurse(n):
1/0
try:
recurse(10)
- except Exception:
- exc_info = sys.exc_info()
- exc = traceback.TracebackException(*exc_info, limit=5)
+ except Exception as e:
+ exc = traceback.TracebackException.from_exception(e, limit=5)
expected_stack = traceback.StackSummary.extract(
- traceback.walk_tb(exc_info[2]), limit=5)
+ traceback.walk_tb(e.__traceback__), limit=5)
self.assertEqual(expected_stack, exc.stack)
def test_lookup_lines(self):
@@ -2706,9 +2706,9 @@ def f():
x = 12
try:
x/0
- except Exception:
- return sys.exc_info()
- exc = traceback.TracebackException(*f(), capture_locals=True)
+ except Exception as e:
+ return e
+ exc = traceback.TracebackException.from_exception(f(), capture_locals=True)
output = StringIO()
exc.print(file=output)
self.assertEqual(
@@ -2723,7 +2723,7 @@ def f():
class TestTracebackException_ExceptionGroups(unittest.TestCase):
def setUp(self):
super().setUp()
- self.eg_info = self._get_exception_group()
+ self.eg = self._get_exception_group()
def _get_exception_group(self):
def f():
@@ -2753,26 +2753,26 @@ def g(v):
except Exception as e:
exc4 = e
raise ExceptionGroup("eg2", [exc3, exc4])
- except ExceptionGroup:
- return sys.exc_info()
+ except ExceptionGroup as eg:
+ return eg
self.fail('Exception Not Raised')
def test_exception_group_construction(self):
- eg_info = self.eg_info
- teg1 = traceback.TracebackException(*eg_info)
- teg2 = traceback.TracebackException.from_exception(eg_info[1])
+ eg = self.eg
+ teg1 = traceback.TracebackException(type(eg), eg, eg.__traceback__)
+ teg2 = traceback.TracebackException.from_exception(eg)
self.assertIsNot(teg1, teg2)
self.assertEqual(teg1, teg2)
def test_exception_group_format_exception_only(self):
- teg = traceback.TracebackException(*self.eg_info)
+ teg = traceback.TracebackException.from_exception(self.eg)
formatted = ''.join(teg.format_exception_only()).split('\n')
expected = "ExceptionGroup: eg2 (2 sub-exceptions)\n".split('\n')
self.assertEqual(formatted, expected)
def test_exception_group_format(self):
- teg = traceback.TracebackException(*self.eg_info)
+ teg = traceback.TracebackException.from_exception(self.eg)
formatted = ''.join(teg.format()).split('\n')
lno_f = self.lno_f
@@ -2884,18 +2884,18 @@ def test_max_group_depth(self):
def test_comparison(self):
try:
- raise self.eg_info[1]
- except ExceptionGroup:
- exc_info = sys.exc_info()
+ raise self.eg
+ except ExceptionGroup as e:
+ exc = e
for _ in range(5):
try:
- raise exc_info[1]
- except:
- exc_info = sys.exc_info()
- exc = traceback.TracebackException(*exc_info)
- exc2 = traceback.TracebackException(*exc_info)
- exc3 = traceback.TracebackException(*exc_info, limit=300)
- ne = traceback.TracebackException(*exc_info, limit=3)
+ raise exc
+ except Exception as e:
+ exc_obj = e
+ exc = traceback.TracebackException.from_exception(exc_obj)
+ exc2 = traceback.TracebackException.from_exception(exc_obj)
+ exc3 = traceback.TracebackException.from_exception(exc_obj, limit=300)
+ ne = traceback.TracebackException.from_exception(exc_obj, limit=3)
self.assertIsNot(exc, exc2)
self.assertEqual(exc, exc2)
self.assertEqual(exc, exc3)
From b019008c9ac5d9c97fdff6094dc979e03504143a Mon Sep 17 00:00:00 2001
From: gaogaotiantian
Date: Sat, 18 Mar 2023 03:59:21 -0700
Subject: [PATCH 142/280] gh-101975: Fixed a potential SegFault on garbage
collection (GH-102803)
---
.../2023-03-18-02-36-39.gh-issue-101975.HwMR1d.rst | 1 +
Python/ceval_macros.h | 1 +
2 files changed, 2 insertions(+)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-18-02-36-39.gh-issue-101975.HwMR1d.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-18-02-36-39.gh-issue-101975.HwMR1d.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-18-02-36-39.gh-issue-101975.HwMR1d.rst
new file mode 100644
index 00000000000000..28c9a8465180db
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-18-02-36-39.gh-issue-101975.HwMR1d.rst
@@ -0,0 +1 @@
+Fixed ``stacktop`` value on tracing entries to avoid corruption on garbage collection.
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 9fbec87988e4ae..2a7f35e8e43ece 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -314,6 +314,7 @@ GETITEM(PyObject *v, Py_ssize_t i) {
_PyFrame_SetStackPointer(frame, stack_pointer); \
int err = trace_function_entry(tstate, frame); \
stack_pointer = _PyFrame_GetStackPointer(frame); \
+ frame->stacktop = -1; \
if (err) { \
goto error; \
} \
From 0d8548871823ee94e781c9a671da667c8cddd031 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Sat, 18 Mar 2023 11:47:11 +0000
Subject: [PATCH 143/280] gh-102778: Add sys.last_exc, deprecate sys.last_type,
sys.last_value,sys.last_traceback (#102779)
---
Doc/library/sys.rst | 25 +++++++++++--------
Doc/whatsnew/3.12.rst | 10 ++++++++
Lib/code.py | 4 ++-
Lib/dis.py | 5 +++-
Lib/idlelib/idle_test/test_stackviewer.py | 3 ++-
Lib/idlelib/pyshell.py | 7 ++++--
Lib/idlelib/run.py | 2 ++
Lib/idlelib/stackviewer.py | 21 +++++++++++-----
Lib/pdb.py | 6 ++++-
Lib/pydoc_data/topics.py | 4 +--
Lib/test/test_dis.py | 10 +++++++-
Lib/test/test_ttk/test_extensions.py | 4 ++-
Lib/tkinter/__init__.py | 1 +
Lib/traceback.py | 16 +++++++-----
...-03-17-13-43-34.gh-issue-102778.ANDv8I.rst | 3 +++
Python/pylifecycle.c | 2 +-
Python/pythonrun.c | 4 +++
Python/sysmodule.c | 6 +++--
18 files changed, 97 insertions(+), 36 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-17-13-43-34.gh-issue-102778.ANDv8I.rst
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index a53d4908783e15..b3b9b5e74ac068 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -1102,22 +1102,25 @@ always available.
.. versionadded:: 3.5
+.. data:: last_exc
+
+ This variable is not always defined; it is set to the exception instance
+ when an exception is not handled and the interpreter prints an error message
+ and a stack traceback. Its intended use is to allow an interactive user to
+ import a debugger module and engage in post-mortem debugging without having
+ to re-execute the command that caused the error. (Typical use is
+ ``import pdb; pdb.pm()`` to enter the post-mortem debugger; see :mod:`pdb`
+ module for more information.)
+
+ .. versionadded:: 3.12
.. data:: last_type
last_value
last_traceback
- These three variables are not always defined; they are set when an exception is
- not handled and the interpreter prints an error message and a stack traceback.
- Their intended use is to allow an interactive user to import a debugger module
- and engage in post-mortem debugging without having to re-execute the command
- that caused the error. (Typical use is ``import pdb; pdb.pm()`` to enter the
- post-mortem debugger; see :mod:`pdb` module for
- more information.)
-
- The meaning of the variables is the same as that of the return values from
- :func:`exc_info` above.
-
+ These three variables are deprecated; use :data:`sys.last_exc` instead.
+ They hold the legacy representation of ``sys.last_exc``, as returned
+ from :func:`exc_info` above.
.. data:: maxsize
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index b55b9619fac226..32fec962560ae1 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -397,6 +397,12 @@ sys
with contributions from Gregory P. Smith [Google] and Mark Shannon
in :gh:`96123`.)
+* Add :data:`sys.last_exc` which holds the last unhandled exception that
+ was raised (for post-mortem debugging use cases). Deprecate the
+ three fields that have the same information in its legacy form:
+ :data:`sys.last_type`, :data:`sys.last_value` and :data:`sys.last_traceback`.
+ (Contributed by Irit Katriel in :gh:`102778`.)
+
Optimizations
=============
@@ -488,6 +494,10 @@ Deprecated
contain the creation time, which is also available in the new ``st_birthtime``
field. (Contributed by Steve Dower in :gh:`99726`.)
+* The :data:`sys.last_type`, :data:`sys.last_value` and :data:`sys.last_traceback`
+ fields are deprecated. Use :data:`sys.last_exc` instead.
+ (Contributed by Irit Katriel in :gh:`102778`.)
+
Pending Removal in Python 3.13
------------------------------
diff --git a/Lib/code.py b/Lib/code.py
index 76000f8c8b2c1e..2bd5fa3e795a61 100644
--- a/Lib/code.py
+++ b/Lib/code.py
@@ -106,6 +106,7 @@ def showsyntaxerror(self, filename=None):
"""
type, value, tb = sys.exc_info()
+ sys.last_exc = value
sys.last_type = type
sys.last_value = value
sys.last_traceback = tb
@@ -119,7 +120,7 @@ def showsyntaxerror(self, filename=None):
else:
# Stuff in the right filename
value = SyntaxError(msg, (filename, lineno, offset, line))
- sys.last_value = value
+ sys.last_exc = sys.last_value = value
if sys.excepthook is sys.__excepthook__:
lines = traceback.format_exception_only(type, value)
self.write(''.join(lines))
@@ -138,6 +139,7 @@ def showtraceback(self):
"""
sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
sys.last_traceback = last_tb
+ sys.last_exc = ei[1]
try:
lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next)
if sys.excepthook is sys.__excepthook__:
diff --git a/Lib/dis.py b/Lib/dis.py
index 9d3cc60e7f57f7..844acd56f84ff0 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -132,7 +132,10 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False):
"""Disassemble a traceback (default: last traceback)."""
if tb is None:
try:
- tb = sys.last_traceback
+ if hasattr(sys, 'last_exc'):
+ tb = sys.last_exc.__traceback__
+ else:
+ tb = sys.last_traceback
except AttributeError:
raise RuntimeError("no last traceback to disassemble") from None
while tb.tb_next: tb = tb.tb_next
diff --git a/Lib/idlelib/idle_test/test_stackviewer.py b/Lib/idlelib/idle_test/test_stackviewer.py
index 98f53f9537bb25..f4626bb1702a30 100644
--- a/Lib/idlelib/idle_test/test_stackviewer.py
+++ b/Lib/idlelib/idle_test/test_stackviewer.py
@@ -19,6 +19,7 @@ def setUpClass(cls):
except NameError:
svs.last_type, svs.last_value, svs.last_traceback = (
sys.exc_info())
+ svs.last_exc = svs.last_value
requires('gui')
cls.root = Tk()
@@ -27,7 +28,7 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
svs = stackviewer.sys
- del svs.last_traceback, svs.last_type, svs.last_value
+ del svs.last_exc, svs.last_traceback, svs.last_type, svs.last_value
cls.root.update_idletasks()
## for id in cls.root.tk.call('after', 'info'):
diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py
index e68233a5a4131e..edc77ff26f62f7 100755
--- a/Lib/idlelib/pyshell.py
+++ b/Lib/idlelib/pyshell.py
@@ -1367,11 +1367,14 @@ def open_stack_viewer(self, event=None):
if self.interp.rpcclt:
return self.interp.remote_stack_viewer()
try:
- sys.last_traceback
+ if hasattr(sys, 'last_exc'):
+ sys.last_exc.__traceback__
+ else:
+ sys.last_traceback
except:
messagebox.showerror("No stack trace",
"There is no stack trace yet.\n"
- "(sys.last_traceback is not defined)",
+ "(sys.last_exc and sys.last_traceback are not defined)",
parent=self.text)
return
from idlelib.stackviewer import StackBrowser
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index 577c49eb67b20d..6a7b50cf5cfb27 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -239,6 +239,7 @@ def print_exception():
efile = sys.stderr
typ, val, tb = excinfo = sys.exc_info()
sys.last_type, sys.last_value, sys.last_traceback = excinfo
+ sys.last_exc = val
seen = set()
def print_exc(typ, exc, tb):
@@ -629,6 +630,7 @@ def stackviewer(self, flist_oid=None):
flist = self.rpchandler.get_remote_proxy(flist_oid)
while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
tb = tb.tb_next
+ sys.last_exc = val
sys.last_type = typ
sys.last_value = val
item = stackviewer.StackTreeItem(flist, tb)
diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py
index 94ffb4eff4dd26..702fd32ca5d1bd 100644
--- a/Lib/idlelib/stackviewer.py
+++ b/Lib/idlelib/stackviewer.py
@@ -27,7 +27,10 @@ def __init__(self, flist=None, tb=None):
def get_stack(self, tb):
if tb is None:
- tb = sys.last_traceback
+ if hasattr(sys, 'last_exc'):
+ tb = sys.last_exc.__traceback__
+ else:
+ tb = sys.last_traceback
stack = []
if tb and tb.tb_frame is None:
tb = tb.tb_next
@@ -37,11 +40,15 @@ def get_stack(self, tb):
return stack
def get_exception(self):
- type = sys.last_type
- value = sys.last_value
- if hasattr(type, "__name__"):
- type = type.__name__
- s = str(type)
+ if hasattr(sys, 'last_exc'):
+ typ = type(sys.last_exc)
+ value = sys.last_exc
+ else:
+ typ = sys.last_type
+ value = sys.last_value
+ if hasattr(typ, "__name__"):
+ typ = typ.__name__
+ s = str(typ)
if value is not None:
s = s + ": " + str(value)
return s
@@ -136,6 +143,7 @@ def _stack_viewer(parent): # htest #
except NameError:
exc_type, exc_value, exc_tb = sys.exc_info()
# inject stack trace to sys
+ sys.last_exc = exc_value
sys.last_type = exc_type
sys.last_value = exc_value
sys.last_traceback = exc_tb
@@ -143,6 +151,7 @@ def _stack_viewer(parent): # htest #
StackBrowser(top, flist=flist, top=top, tb=exc_tb)
# restore sys to original state
+ del sys.last_exc
del sys.last_type
del sys.last_value
del sys.last_traceback
diff --git a/Lib/pdb.py b/Lib/pdb.py
index f11fc55536810f..3543f53282db15 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -1739,7 +1739,11 @@ def post_mortem(t=None):
def pm():
"""Enter post-mortem debugging of the traceback found in sys.last_traceback."""
- post_mortem(sys.last_traceback)
+ if hasattr(sys, 'last_exc'):
+ tb = sys.last_exc.__traceback__
+ else:
+ tb = sys.last_traceback
+ post_mortem(tb)
# Main program for testing
diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py
index 573065b4b714d9..ad1b6aca6b95bc 100644
--- a/Lib/pydoc_data/topics.py
+++ b/Lib/pydoc_data/topics.py
@@ -4799,7 +4799,7 @@
'pdb.pm()\n'
'\n'
' Enter post-mortem debugging of the traceback found in\n'
- ' "sys.last_traceback".\n'
+ ' "sys.last_exc".\n'
'\n'
'The "run*" functions and "set_trace()" are aliases for '
'instantiating\n'
@@ -13858,7 +13858,7 @@
'if\n'
' the interpreter is interactive, it is also made available to '
'the\n'
- ' user as "sys.last_traceback".\n'
+ ' user as "sys.last_exc".\n'
'\n'
' For explicitly created tracebacks, it is up to the creator '
'of\n'
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index b77e3b06d0c1f1..fa1de1c7ded1b3 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -1026,6 +1026,10 @@ def test_disassemble_try_finally(self):
self.do_disassembly_test(_tryfinallyconst, dis_tryfinallyconst)
def test_dis_none(self):
+ try:
+ del sys.last_exc
+ except AttributeError:
+ pass
try:
del sys.last_traceback
except AttributeError:
@@ -1043,7 +1047,7 @@ def test_dis_traceback(self):
1/0
except Exception as e:
tb = e.__traceback__
- sys.last_traceback = tb
+ sys.last_exc = e
tb_dis = self.get_disassemble_as_string(tb.tb_frame.f_code, tb.tb_lasti)
self.do_disassembly_test(None, tb_dis, True)
@@ -1900,6 +1904,10 @@ def test_findlabels(self):
class TestDisTraceback(DisTestBase):
def setUp(self) -> None:
+ try: # We need to clean up existing tracebacks
+ del sys.last_exc
+ except AttributeError:
+ pass
try: # We need to clean up existing tracebacks
del sys.last_traceback
except AttributeError:
diff --git a/Lib/test/test_ttk/test_extensions.py b/Lib/test/test_ttk/test_extensions.py
index 6135c49701f08e..d5e069716971fe 100644
--- a/Lib/test/test_ttk/test_extensions.py
+++ b/Lib/test/test_ttk/test_extensions.py
@@ -45,7 +45,9 @@ def test_widget_destroy(self):
# value which causes the tracing callback to be called and then
# it tries calling instance attributes not yet defined.
ttk.LabeledScale(self.root, variable=myvar)
- if hasattr(sys, 'last_type'):
+ if hasattr(sys, 'last_exc'):
+ self.assertNotEqual(type(sys.last_exc), tkinter.TclError)
+ elif hasattr(sys, 'last_type'):
self.assertNotEqual(sys.last_type, tkinter.TclError)
def test_initialization(self):
diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index 7565e0f7e46073..479daf0e5abfc3 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -2400,6 +2400,7 @@ def report_callback_exception(self, exc, val, tb):
should when sys.stderr is None."""
import traceback
print("Exception in Tkinter callback", file=sys.stderr)
+ sys.last_exc = val
sys.last_type = exc
sys.last_value = val
sys.last_traceback = tb
diff --git a/Lib/traceback.py b/Lib/traceback.py
index c43c4720ae5a15..9e720ac9948fce 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -179,7 +179,7 @@ def _safe_string(value, what, func=str):
# --
def print_exc(limit=None, file=None, chain=True):
- """Shorthand for 'print_exception(*sys.exc_info(), limit, file)'."""
+ """Shorthand for 'print_exception(*sys.exc_info(), limit, file, chain)'."""
print_exception(*sys.exc_info(), limit=limit, file=file, chain=chain)
def format_exc(limit=None, chain=True):
@@ -187,12 +187,16 @@ def format_exc(limit=None, chain=True):
return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain))
def print_last(limit=None, file=None, chain=True):
- """This is a shorthand for 'print_exception(sys.last_type,
- sys.last_value, sys.last_traceback, limit, file)'."""
- if not hasattr(sys, "last_type"):
+ """This is a shorthand for 'print_exception(sys.last_exc, limit, file, chain)'."""
+ if not hasattr(sys, "last_exc") and not hasattr(sys, "last_type"):
raise ValueError("no last exception")
- print_exception(sys.last_type, sys.last_value, sys.last_traceback,
- limit, file, chain)
+
+ if hasattr(sys, "last_exc"):
+ print_exception(sys.last_exc, limit, file, chain)
+ else:
+ print_exception(sys.last_type, sys.last_value, sys.last_traceback,
+ limit, file, chain)
+
#
# Printing and Extracting Stacks.
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-17-13-43-34.gh-issue-102778.ANDv8I.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-17-13-43-34.gh-issue-102778.ANDv8I.rst
new file mode 100644
index 00000000000000..b5da227afa5a69
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-17-13-43-34.gh-issue-102778.ANDv8I.rst
@@ -0,0 +1,3 @@
+Add :data:`sys.last_exc` and deprecate :data:`sys.last_type`, :data:`sys.last_value`
+and :data:`sys.last_traceback`,
+which hold the same information in its legacy form.
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index d0c65cc1f7fd44..7bf12271db2323 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1304,7 +1304,7 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose)
{
// List of names to clear in sys
static const char * const sys_deletes[] = {
- "path", "argv", "ps1", "ps2",
+ "path", "argv", "ps1", "ps2", "last_exc",
"last_type", "last_value", "last_traceback",
"__interactivehook__",
// path_hooks and path_importer_cache are cleared
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 07d119a67847c6..6ea185a1b75bc9 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -776,6 +776,10 @@ _PyErr_PrintEx(PyThreadState *tstate, int set_sys_last_vars)
}
if (set_sys_last_vars) {
+ if (_PySys_SetAttr(&_Py_ID(last_exc), exc) < 0) {
+ _PyErr_Clear(tstate);
+ }
+ /* Legacy version: */
if (_PySys_SetAttr(&_Py_ID(last_type), typ) < 0) {
_PyErr_Clear(tstate);
}
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index cc5b9a6d418bfa..126b7d422e0009 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -2670,11 +2670,13 @@ stderr -- standard error object; used for error messages\n\
By assigning other file objects (or objects that behave like files)\n\
to these, it is possible to redirect all of the interpreter's I/O.\n\
\n\
+last_exc - the last uncaught exception\n\
+ Only available in an interactive session after a\n\
+ traceback has been printed.\n\
last_type -- type of last uncaught exception\n\
last_value -- value of last uncaught exception\n\
last_traceback -- traceback of last uncaught exception\n\
- These three are only available in an interactive session after a\n\
- traceback has been printed.\n\
+ These three are the (deprecated) legacy representation of last_exc.\n\
"
)
/* concatenating string here */
From 863f705cf90f5f040bb9e8574210c920e4953a82 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Sat, 18 Mar 2023 13:44:47 +0000
Subject: [PATCH 144/280] gh-102192: Replace PyErr_Fetch/Restore etc by more
efficient alternatives (#102769)
---
Python/errors.c | 67 +++++++++++++++++++------------------------------
1 file changed, 26 insertions(+), 41 deletions(-)
diff --git a/Python/errors.c b/Python/errors.c
index bdcbac317eb9ee..ab14770c6e810c 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -666,17 +666,15 @@ _PyErr_ChainExceptions(PyObject *typ, PyObject *val, PyObject *tb)
}
if (_PyErr_Occurred(tstate)) {
- PyObject *typ2, *val2, *tb2;
- _PyErr_Fetch(tstate, &typ2, &val2, &tb2);
_PyErr_NormalizeException(tstate, &typ, &val, &tb);
if (tb != NULL) {
PyException_SetTraceback(val, tb);
Py_DECREF(tb);
}
Py_DECREF(typ);
- _PyErr_NormalizeException(tstate, &typ2, &val2, &tb2);
- PyException_SetContext(val2, val);
- _PyErr_Restore(tstate, typ2, val2, tb2);
+ PyObject *exc2 = _PyErr_GetRaisedException(tstate);
+ PyException_SetContext(exc2, val);
+ _PyErr_SetRaisedException(tstate, exc2);
}
else {
_PyErr_Restore(tstate, typ, val, tb);
@@ -757,27 +755,15 @@ static PyObject *
_PyErr_FormatVFromCause(PyThreadState *tstate, PyObject *exception,
const char *format, va_list vargs)
{
- PyObject *exc, *val, *val2, *tb;
-
assert(_PyErr_Occurred(tstate));
- _PyErr_Fetch(tstate, &exc, &val, &tb);
- _PyErr_NormalizeException(tstate, &exc, &val, &tb);
- if (tb != NULL) {
- PyException_SetTraceback(val, tb);
- Py_DECREF(tb);
- }
- Py_DECREF(exc);
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
assert(!_PyErr_Occurred(tstate));
-
_PyErr_FormatV(tstate, exception, format, vargs);
-
- _PyErr_Fetch(tstate, &exc, &val2, &tb);
- _PyErr_NormalizeException(tstate, &exc, &val2, &tb);
- PyException_SetCause(val2, Py_NewRef(val));
- PyException_SetContext(val2, Py_NewRef(val));
- Py_DECREF(val);
- _PyErr_Restore(tstate, exc, val2, tb);
-
+ PyObject *exc2 = _PyErr_GetRaisedException(tstate);
+ PyException_SetCause(exc2, Py_NewRef(exc));
+ PyException_SetContext(exc2, Py_NewRef(exc));
+ Py_DECREF(exc);
+ _PyErr_SetRaisedException(tstate, exc2);
return NULL;
}
@@ -1698,19 +1684,18 @@ static void
PyErr_SyntaxLocationObjectEx(PyObject *filename, int lineno, int col_offset,
int end_lineno, int end_col_offset)
{
- PyObject *exc, *v, *tb, *tmp;
PyThreadState *tstate = _PyThreadState_GET();
/* add attributes for the line number and filename for the error */
- _PyErr_Fetch(tstate, &exc, &v, &tb);
- _PyErr_NormalizeException(tstate, &exc, &v, &tb);
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
/* XXX check that it is, indeed, a syntax error. It might not
* be, though. */
- tmp = PyLong_FromLong(lineno);
- if (tmp == NULL)
+ PyObject *tmp = PyLong_FromLong(lineno);
+ if (tmp == NULL) {
_PyErr_Clear(tstate);
+ }
else {
- if (PyObject_SetAttr(v, &_Py_ID(lineno), tmp)) {
+ if (PyObject_SetAttr(exc, &_Py_ID(lineno), tmp)) {
_PyErr_Clear(tstate);
}
Py_DECREF(tmp);
@@ -1722,7 +1707,7 @@ PyErr_SyntaxLocationObjectEx(PyObject *filename, int lineno, int col_offset,
_PyErr_Clear(tstate);
}
}
- if (PyObject_SetAttr(v, &_Py_ID(offset), tmp ? tmp : Py_None)) {
+ if (PyObject_SetAttr(exc, &_Py_ID(offset), tmp ? tmp : Py_None)) {
_PyErr_Clear(tstate);
}
Py_XDECREF(tmp);
@@ -1734,7 +1719,7 @@ PyErr_SyntaxLocationObjectEx(PyObject *filename, int lineno, int col_offset,
_PyErr_Clear(tstate);
}
}
- if (PyObject_SetAttr(v, &_Py_ID(end_lineno), tmp ? tmp : Py_None)) {
+ if (PyObject_SetAttr(exc, &_Py_ID(end_lineno), tmp ? tmp : Py_None)) {
_PyErr_Clear(tstate);
}
Py_XDECREF(tmp);
@@ -1746,20 +1731,20 @@ PyErr_SyntaxLocationObjectEx(PyObject *filename, int lineno, int col_offset,
_PyErr_Clear(tstate);
}
}
- if (PyObject_SetAttr(v, &_Py_ID(end_offset), tmp ? tmp : Py_None)) {
+ if (PyObject_SetAttr(exc, &_Py_ID(end_offset), tmp ? tmp : Py_None)) {
_PyErr_Clear(tstate);
}
Py_XDECREF(tmp);
tmp = NULL;
if (filename != NULL) {
- if (PyObject_SetAttr(v, &_Py_ID(filename), filename)) {
+ if (PyObject_SetAttr(exc, &_Py_ID(filename), filename)) {
_PyErr_Clear(tstate);
}
tmp = PyErr_ProgramTextObject(filename, lineno);
if (tmp) {
- if (PyObject_SetAttr(v, &_Py_ID(text), tmp)) {
+ if (PyObject_SetAttr(exc, &_Py_ID(text), tmp)) {
_PyErr_Clear(tstate);
}
Py_DECREF(tmp);
@@ -1768,17 +1753,17 @@ PyErr_SyntaxLocationObjectEx(PyObject *filename, int lineno, int col_offset,
_PyErr_Clear(tstate);
}
}
- if (exc != PyExc_SyntaxError) {
- if (_PyObject_LookupAttr(v, &_Py_ID(msg), &tmp) < 0) {
+ if ((PyObject *)Py_TYPE(exc) != PyExc_SyntaxError) {
+ if (_PyObject_LookupAttr(exc, &_Py_ID(msg), &tmp) < 0) {
_PyErr_Clear(tstate);
}
else if (tmp) {
Py_DECREF(tmp);
}
else {
- tmp = PyObject_Str(v);
+ tmp = PyObject_Str(exc);
if (tmp) {
- if (PyObject_SetAttr(v, &_Py_ID(msg), tmp)) {
+ if (PyObject_SetAttr(exc, &_Py_ID(msg), tmp)) {
_PyErr_Clear(tstate);
}
Py_DECREF(tmp);
@@ -1788,19 +1773,19 @@ PyErr_SyntaxLocationObjectEx(PyObject *filename, int lineno, int col_offset,
}
}
- if (_PyObject_LookupAttr(v, &_Py_ID(print_file_and_line), &tmp) < 0) {
+ if (_PyObject_LookupAttr(exc, &_Py_ID(print_file_and_line), &tmp) < 0) {
_PyErr_Clear(tstate);
}
else if (tmp) {
Py_DECREF(tmp);
}
else {
- if (PyObject_SetAttr(v, &_Py_ID(print_file_and_line), Py_None)) {
+ if (PyObject_SetAttr(exc, &_Py_ID(print_file_and_line), Py_None)) {
_PyErr_Clear(tstate);
}
}
}
- _PyErr_Restore(tstate, exc, v, tb);
+ _PyErr_SetRaisedException(tstate, exc);
}
void
From 1820891bc4a9a95a7d80bfb30bfabef4c89e98b2 Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Sat, 18 Mar 2023 12:21:48 -0500
Subject: [PATCH 145/280] Add more comments to hypot() (GH-102817)
---
Modules/mathmodule.c | 30 ++++++++++++------------------
1 file changed, 12 insertions(+), 18 deletions(-)
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 48cd9a642de150..c9a2be66863993 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -2447,9 +2447,8 @@ Since lo**2 is less than 1/2 ulp(csum), we have csum+lo*lo == csum.
To minimize loss of information during the accumulation of fractional
values, each term has a separate accumulator. This also breaks up
sequential dependencies in the inner loop so the CPU can maximize
-floating point throughput. [4] On a 2.6 GHz Haswell, adding one
-dimension has an incremental cost of only 5ns -- for example when
-moving from hypot(x,y) to hypot(x,y,z).
+floating point throughput. [4] On an Apple M1 Max, hypot(*vec)
+takes only 3.33 µsec when len(vec) == 1000.
The square root differential correction is needed because a
correctly rounded square root of a correctly rounded sum of
@@ -2473,7 +2472,7 @@ step is exact. The Neumaier summation computes as if in doubled
precision (106 bits) and has the advantage that its input squares
are non-negative so that the condition number of the sum is one.
The square root with a differential correction is likewise computed
-as if in double precision.
+as if in doubled precision.
For n <= 1000, prior to the final addition that rounds the overall
result, the internal accuracy of "h" together with its correction of
@@ -2514,12 +2513,9 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
}
frexp(max, &max_e);
if (max_e < -1023) {
- /* When max_e < -1023, ldexp(1.0, -max_e) would overflow.
- So we first perform lossless scaling from subnormals back to normals,
- then recurse back to vector_norm(), and then finally undo the scaling.
- */
+ /* When max_e < -1023, ldexp(1.0, -max_e) would overflow. */
for (i=0 ; i < n ; i++) {
- vec[i] /= DBL_MIN;
+ vec[i] /= DBL_MIN; // convert subnormals to normals
}
return DBL_MIN * vector_norm(n, vec, max / DBL_MIN, found_nan);
}
@@ -2529,17 +2525,14 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
for (i=0 ; i < n ; i++) {
x = vec[i];
assert(Py_IS_FINITE(x) && fabs(x) <= max);
-
- x *= scale;
+ x *= scale; // lossless scaling
assert(fabs(x) < 1.0);
-
- pr = dl_mul(x, x);
+ pr = dl_mul(x, x); // lossless squaring
assert(pr.hi <= 1.0);
-
- sm = dl_fast_sum(csum, pr.hi);
+ sm = dl_fast_sum(csum, pr.hi); // lossless addition
csum = sm.hi;
- frac1 += pr.lo;
- frac2 += sm.lo;
+ frac1 += pr.lo; // lossy addition
+ frac2 += sm.lo; // lossy addition
}
h = sqrt(csum - 1.0 + (frac1 + frac2));
pr = dl_mul(-h, h);
@@ -2548,7 +2541,8 @@ vector_norm(Py_ssize_t n, double *vec, double max, int found_nan)
frac1 += pr.lo;
frac2 += sm.lo;
x = csum - 1.0 + (frac1 + frac2);
- return (h + x / (2.0 * h)) / scale;
+ h += x / (2.0 * h); // differential correction
+ return h / scale;
}
#define NUM_STACK_ELEMS 16
From ba86d352c032ef01743cf451edb56a70e346fbbe Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Sun, 19 Mar 2023 15:17:59 +0000
Subject: [PATCH 146/280] gh-102192: Replace PyErr_Fetch/Restore etc by more
efficient alternatives (#102816)
---
Python/bytecodes.c | 8 ++------
Python/generated_cases.c.h | 8 ++------
Python/import.c | 28 ++++++++++++++--------------
Python/pythonrun.c | 2 +-
4 files changed, 19 insertions(+), 27 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 4cac5857a2cf42..c8ffd89c51a22c 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -851,9 +851,7 @@ dummy_func(
}
assert(exc && PyExceptionInstance_Check(exc));
Py_INCREF(exc);
- PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc));
- PyObject *tb = PyException_GetTraceback(exc);
- _PyErr_Restore(tstate, typ, exc, tb);
+ _PyErr_SetRaisedException(tstate, exc);
goto exception_unwind;
}
@@ -864,9 +862,7 @@ dummy_func(
}
else {
Py_INCREF(exc);
- PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc));
- PyObject *tb = PyException_GetTraceback(exc);
- _PyErr_Restore(tstate, typ, exc, tb);
+ _PyErr_SetRaisedException(tstate, exc);
goto exception_unwind;
}
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 9fb532209041ce..2c6388390b51ad 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -1122,9 +1122,7 @@
}
assert(exc && PyExceptionInstance_Check(exc));
Py_INCREF(exc);
- PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc));
- PyObject *tb = PyException_GetTraceback(exc);
- _PyErr_Restore(tstate, typ, exc, tb);
+ _PyErr_SetRaisedException(tstate, exc);
goto exception_unwind;
}
@@ -1138,9 +1136,7 @@
}
else {
Py_INCREF(exc);
- PyObject *typ = Py_NewRef(PyExceptionInstance_Class(exc));
- PyObject *tb = PyException_GetTraceback(exc);
- _PyErr_Restore(tstate, typ, exc, tb);
+ _PyErr_SetRaisedException(tstate, exc);
goto exception_unwind;
}
STACK_SHRINK(2);
diff --git a/Python/import.c b/Python/import.c
index 9f80c6d8dd49a8..c68bd1f7db420d 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -389,8 +389,7 @@ PyImport_AddModule(const char *name)
static void
remove_module(PyThreadState *tstate, PyObject *name)
{
- PyObject *type, *value, *traceback;
- _PyErr_Fetch(tstate, &type, &value, &traceback);
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
PyObject *modules = MODULES(tstate->interp);
if (PyDict_CheckExact(modules)) {
@@ -403,7 +402,7 @@ remove_module(PyThreadState *tstate, PyObject *name)
}
}
- _PyErr_ChainExceptions(type, value, traceback);
+ _PyErr_ChainExceptions1(exc);
}
@@ -2324,32 +2323,34 @@ remove_importlib_frames(PyThreadState *tstate)
const char *remove_frames = "_call_with_frames_removed";
int always_trim = 0;
int in_importlib = 0;
- PyObject *exception, *value, *base_tb, *tb;
PyObject **prev_link, **outer_link = NULL;
+ PyObject *base_tb = NULL;
/* Synopsis: if it's an ImportError, we trim all importlib chunks
from the traceback. We always trim chunks
which end with a call to "_call_with_frames_removed". */
- _PyErr_Fetch(tstate, &exception, &value, &base_tb);
- if (!exception || _PyInterpreterState_GetConfig(tstate->interp)->verbose) {
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
+ if (exc == NULL || _PyInterpreterState_GetConfig(tstate->interp)->verbose) {
goto done;
}
- if (PyType_IsSubtype((PyTypeObject *) exception,
- (PyTypeObject *) PyExc_ImportError))
+ if (PyType_IsSubtype(Py_TYPE(exc), (PyTypeObject *) PyExc_ImportError)) {
always_trim = 1;
+ }
+ assert(PyExceptionInstance_Check(exc));
+ base_tb = PyException_GetTraceback(exc);
prev_link = &base_tb;
- tb = base_tb;
+ PyObject *tb = base_tb;
while (tb != NULL) {
+ assert(PyTraceBack_Check(tb));
PyTracebackObject *traceback = (PyTracebackObject *)tb;
PyObject *next = (PyObject *) traceback->tb_next;
PyFrameObject *frame = traceback->tb_frame;
PyCodeObject *code = PyFrame_GetCode(frame);
int now_in_importlib;
- assert(PyTraceBack_Check(tb));
now_in_importlib = _PyUnicode_EqualToASCIIString(code->co_filename, importlib_filename) ||
_PyUnicode_EqualToASCIIString(code->co_filename, external_filename);
if (now_in_importlib && !in_importlib) {
@@ -2370,15 +2371,14 @@ remove_importlib_frames(PyThreadState *tstate)
Py_DECREF(code);
tb = next;
}
- assert(PyExceptionInstance_Check(value));
- assert((PyObject *)Py_TYPE(value) == exception);
if (base_tb == NULL) {
base_tb = Py_None;
Py_INCREF(Py_None);
}
- PyException_SetTraceback(value, base_tb);
+ PyException_SetTraceback(exc, base_tb);
done:
- _PyErr_Restore(tstate, exception, value, base_tb);
+ Py_XDECREF(base_tb);
+ _PyErr_SetRaisedException(tstate, exc);
}
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 6ea185a1b75bc9..b16d3f53f89fb9 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -18,7 +18,7 @@
#include "pycore_interp.h" // PyInterpreterState.importlib
#include "pycore_object.h" // _PyDebug_PrintTotalRefs()
#include "pycore_parser.h" // _PyParser_ASTFromString()
-#include "pycore_pyerrors.h" // _PyErr_Fetch, _Py_Offer_Suggestions
+#include "pycore_pyerrors.h" // _PyErr_GetRaisedException, _Py_Offer_Suggestions
#include "pycore_pylifecycle.h" // _Py_UnhandledKeyboardInterrupt
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_sysmodule.h" // _PySys_Audit()
From 2cfaac1fb0747e8e8fe869c0656809eb5ef960de Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Sun, 19 Mar 2023 15:18:24 +0000
Subject: [PATCH 147/280] gh-102755: fix refleak (#102826)
---
Python/pylifecycle.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 7bf12271db2323..317d6966d034fa 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -2547,6 +2547,7 @@ _Py_FatalError_PrintExc(PyThreadState *tstate)
if (ferr == NULL || ferr == Py_None) {
/* sys.stderr is not set yet or set to None,
no need to try to display the exception */
+ Py_DECREF(exc);
return 0;
}
@@ -2555,7 +2556,7 @@ _Py_FatalError_PrintExc(PyThreadState *tstate)
PyObject *tb = PyException_GetTraceback(exc);
int has_tb = (tb != NULL) && (tb != Py_None);
Py_XDECREF(tb);
- Py_XDECREF(exc);
+ Py_DECREF(exc);
/* sys.stderr may be buffered: call sys.stderr.flush() */
PyObject *res = PyObject_CallMethodNoArgs(ferr, &_Py_ID(flush));
From 47460943fcb63abd140534b41d15313077ec3f1d Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Sun, 19 Mar 2023 16:19:59 +0000
Subject: [PATCH 148/280] gh-102778: revert changes to idlelib (#102825)
---
Lib/idlelib/idle_test/test_stackviewer.py | 3 +--
Lib/idlelib/pyshell.py | 7 ++-----
Lib/idlelib/run.py | 2 --
Lib/idlelib/stackviewer.py | 21 ++++++---------------
4 files changed, 9 insertions(+), 24 deletions(-)
diff --git a/Lib/idlelib/idle_test/test_stackviewer.py b/Lib/idlelib/idle_test/test_stackviewer.py
index f4626bb1702a30..98f53f9537bb25 100644
--- a/Lib/idlelib/idle_test/test_stackviewer.py
+++ b/Lib/idlelib/idle_test/test_stackviewer.py
@@ -19,7 +19,6 @@ def setUpClass(cls):
except NameError:
svs.last_type, svs.last_value, svs.last_traceback = (
sys.exc_info())
- svs.last_exc = svs.last_value
requires('gui')
cls.root = Tk()
@@ -28,7 +27,7 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
svs = stackviewer.sys
- del svs.last_exc, svs.last_traceback, svs.last_type, svs.last_value
+ del svs.last_traceback, svs.last_type, svs.last_value
cls.root.update_idletasks()
## for id in cls.root.tk.call('after', 'info'):
diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py
index edc77ff26f62f7..e68233a5a4131e 100755
--- a/Lib/idlelib/pyshell.py
+++ b/Lib/idlelib/pyshell.py
@@ -1367,14 +1367,11 @@ def open_stack_viewer(self, event=None):
if self.interp.rpcclt:
return self.interp.remote_stack_viewer()
try:
- if hasattr(sys, 'last_exc'):
- sys.last_exc.__traceback__
- else:
- sys.last_traceback
+ sys.last_traceback
except:
messagebox.showerror("No stack trace",
"There is no stack trace yet.\n"
- "(sys.last_exc and sys.last_traceback are not defined)",
+ "(sys.last_traceback is not defined)",
parent=self.text)
return
from idlelib.stackviewer import StackBrowser
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index 6a7b50cf5cfb27..577c49eb67b20d 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -239,7 +239,6 @@ def print_exception():
efile = sys.stderr
typ, val, tb = excinfo = sys.exc_info()
sys.last_type, sys.last_value, sys.last_traceback = excinfo
- sys.last_exc = val
seen = set()
def print_exc(typ, exc, tb):
@@ -630,7 +629,6 @@ def stackviewer(self, flist_oid=None):
flist = self.rpchandler.get_remote_proxy(flist_oid)
while tb and tb.tb_frame.f_globals["__name__"] in ["rpc", "run"]:
tb = tb.tb_next
- sys.last_exc = val
sys.last_type = typ
sys.last_value = val
item = stackviewer.StackTreeItem(flist, tb)
diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py
index 702fd32ca5d1bd..94ffb4eff4dd26 100644
--- a/Lib/idlelib/stackviewer.py
+++ b/Lib/idlelib/stackviewer.py
@@ -27,10 +27,7 @@ def __init__(self, flist=None, tb=None):
def get_stack(self, tb):
if tb is None:
- if hasattr(sys, 'last_exc'):
- tb = sys.last_exc.__traceback__
- else:
- tb = sys.last_traceback
+ tb = sys.last_traceback
stack = []
if tb and tb.tb_frame is None:
tb = tb.tb_next
@@ -40,15 +37,11 @@ def get_stack(self, tb):
return stack
def get_exception(self):
- if hasattr(sys, 'last_exc'):
- typ = type(sys.last_exc)
- value = sys.last_exc
- else:
- typ = sys.last_type
- value = sys.last_value
- if hasattr(typ, "__name__"):
- typ = typ.__name__
- s = str(typ)
+ type = sys.last_type
+ value = sys.last_value
+ if hasattr(type, "__name__"):
+ type = type.__name__
+ s = str(type)
if value is not None:
s = s + ": " + str(value)
return s
@@ -143,7 +136,6 @@ def _stack_viewer(parent): # htest #
except NameError:
exc_type, exc_value, exc_tb = sys.exc_info()
# inject stack trace to sys
- sys.last_exc = exc_value
sys.last_type = exc_type
sys.last_value = exc_value
sys.last_traceback = exc_tb
@@ -151,7 +143,6 @@ def _stack_viewer(parent): # htest #
StackBrowser(top, flist=flist, top=top, tb=exc_tb)
# restore sys to original state
- del sys.last_exc
del sys.last_type
del sys.last_value
del sys.last_traceback
From c8e62e71dadd9e76512f494096065dcdc2e34568 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Sun, 19 Mar 2023 18:33:51 +0000
Subject: [PATCH 149/280] gh-102828: add onexc arg to shutil.rmtree. Deprecate
onerror. (#102829)
---
Doc/library/shutil.rst | 24 ++-
Doc/whatsnew/3.12.rst | 9 +
Lib/shutil.py | 106 ++++++-----
Lib/test/test_shutil.py | 170 +++++++++++++++++-
...-03-19-15-30-59.gh-issue-102828.NKClXg.rst | 3 +
5 files changed, 256 insertions(+), 56 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-19-15-30-59.gh-issue-102828.NKClXg.rst
diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst
index b33dbe21b1fa19..acba66258fe8f0 100644
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -292,15 +292,15 @@ Directory and files operations
.. versionadded:: 3.8
The *dirs_exist_ok* parameter.
-.. function:: rmtree(path, ignore_errors=False, onerror=None, *, dir_fd=None)
+.. function:: rmtree(path, ignore_errors=False, onerror=None, *, onexc=None, dir_fd=None)
.. index:: single: directory; deleting
Delete an entire directory tree; *path* must point to a directory (but not a
symbolic link to a directory). If *ignore_errors* is true, errors resulting
from failed removals will be ignored; if false or omitted, such errors are
- handled by calling a handler specified by *onerror* or, if that is omitted,
- they raise an exception.
+ handled by calling a handler specified by *onexc* or *onerror* or, if both
+ are omitted, exceptions are propagated to the caller.
This function can support :ref:`paths relative to directory descriptors
`.
@@ -315,14 +315,17 @@ Directory and files operations
otherwise. Applications can use the :data:`rmtree.avoids_symlink_attacks`
function attribute to determine which case applies.
- If *onerror* is provided, it must be a callable that accepts three
- parameters: *function*, *path*, and *excinfo*.
+ If *onexc* is provided, it must be a callable that accepts three parameters:
+ *function*, *path*, and *excinfo*.
The first parameter, *function*, is the function which raised the exception;
it depends on the platform and implementation. The second parameter,
*path*, will be the path name passed to *function*. The third parameter,
- *excinfo*, will be the exception information returned by
- :func:`sys.exc_info`. Exceptions raised by *onerror* will not be caught.
+ *excinfo*, is the exception that was raised. Exceptions raised by *onexc*
+ will not be caught.
+
+ The deprecated *onerror* is similar to *onexc*, except that the third
+ parameter it receives is the tuple returned from :func:`sys.exc_info`.
.. audit-event:: shutil.rmtree path,dir_fd shutil.rmtree
@@ -337,6 +340,9 @@ Directory and files operations
.. versionchanged:: 3.11
The *dir_fd* parameter.
+ .. versionchanged:: 3.12
+ Added the *onexc* parameter, deprecated *onerror*.
+
.. attribute:: rmtree.avoids_symlink_attacks
Indicates whether the current platform and implementation provides a
@@ -509,7 +515,7 @@ rmtree example
~~~~~~~~~~~~~~
This example shows how to remove a directory tree on Windows where some
-of the files have their read-only bit set. It uses the onerror callback
+of the files have their read-only bit set. It uses the onexc callback
to clear the readonly bit and reattempt the remove. Any subsequent failure
will propagate. ::
@@ -521,7 +527,7 @@ will propagate. ::
os.chmod(path, stat.S_IWRITE)
func(path)
- shutil.rmtree(directory, onerror=remove_readonly)
+ shutil.rmtree(directory, onexc=remove_readonly)
.. _archiving-operations:
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 32fec962560ae1..a36e68528c4f74 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -337,6 +337,11 @@ shutil
of the process to *root_dir* to perform archiving.
(Contributed by Serhiy Storchaka in :gh:`74696`.)
+* :func:`shutil.rmtree` now accepts a new argument *onexc* which is an
+ error handler like *onerror* but which expects an exception instance
+ rather than a *(typ, val, tb)* triplet. *onerror* is deprecated.
+ (Contributed by Irit Katriel in :gh:`102828`.)
+
sqlite3
-------
@@ -498,6 +503,10 @@ Deprecated
fields are deprecated. Use :data:`sys.last_exc` instead.
(Contributed by Irit Katriel in :gh:`102778`.)
+* The *onerror* argument of :func:`shutil.rmtree` is deprecated. Use *onexc*
+ instead. (Contributed by Irit Katriel in :gh:`102828`.)
+
+
Pending Removal in Python 3.13
------------------------------
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 867925aa10cc04..89a7ac72d98357 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -575,12 +575,12 @@ def _rmtree_islink(path):
return os.path.islink(path)
# version vulnerable to race conditions
-def _rmtree_unsafe(path, onerror):
+def _rmtree_unsafe(path, onexc):
try:
with os.scandir(path) as scandir_it:
entries = list(scandir_it)
- except OSError:
- onerror(os.scandir, path, sys.exc_info())
+ except OSError as err:
+ onexc(os.scandir, path, err)
entries = []
for entry in entries:
fullname = entry.path
@@ -596,28 +596,28 @@ def _rmtree_unsafe(path, onerror):
# a directory with a symlink after the call to
# os.scandir or entry.is_dir above.
raise OSError("Cannot call rmtree on a symbolic link")
- except OSError:
- onerror(os.path.islink, fullname, sys.exc_info())
+ except OSError as err:
+ onexc(os.path.islink, fullname, err)
continue
- _rmtree_unsafe(fullname, onerror)
+ _rmtree_unsafe(fullname, onexc)
else:
try:
os.unlink(fullname)
- except OSError:
- onerror(os.unlink, fullname, sys.exc_info())
+ except OSError as err:
+ onexc(os.unlink, fullname, err)
try:
os.rmdir(path)
- except OSError:
- onerror(os.rmdir, path, sys.exc_info())
+ except OSError as err:
+ onexc(os.rmdir, path, err)
# Version using fd-based APIs to protect against races
-def _rmtree_safe_fd(topfd, path, onerror):
+def _rmtree_safe_fd(topfd, path, onexc):
try:
with os.scandir(topfd) as scandir_it:
entries = list(scandir_it)
except OSError as err:
err.filename = path
- onerror(os.scandir, path, sys.exc_info())
+ onexc(os.scandir, path, err)
return
for entry in entries:
fullname = os.path.join(path, entry.name)
@@ -630,25 +630,25 @@ def _rmtree_safe_fd(topfd, path, onerror):
try:
orig_st = entry.stat(follow_symlinks=False)
is_dir = stat.S_ISDIR(orig_st.st_mode)
- except OSError:
- onerror(os.lstat, fullname, sys.exc_info())
+ except OSError as err:
+ onexc(os.lstat, fullname, err)
continue
if is_dir:
try:
dirfd = os.open(entry.name, os.O_RDONLY, dir_fd=topfd)
dirfd_closed = False
- except OSError:
- onerror(os.open, fullname, sys.exc_info())
+ except OSError as err:
+ onexc(os.open, fullname, err)
else:
try:
if os.path.samestat(orig_st, os.fstat(dirfd)):
- _rmtree_safe_fd(dirfd, fullname, onerror)
+ _rmtree_safe_fd(dirfd, fullname, onexc)
try:
os.close(dirfd)
dirfd_closed = True
os.rmdir(entry.name, dir_fd=topfd)
- except OSError:
- onerror(os.rmdir, fullname, sys.exc_info())
+ except OSError as err:
+ onexc(os.rmdir, fullname, err)
else:
try:
# This can only happen if someone replaces
@@ -656,23 +656,23 @@ def _rmtree_safe_fd(topfd, path, onerror):
# os.scandir or stat.S_ISDIR above.
raise OSError("Cannot call rmtree on a symbolic "
"link")
- except OSError:
- onerror(os.path.islink, fullname, sys.exc_info())
+ except OSError as err:
+ onexc(os.path.islink, fullname, err)
finally:
if not dirfd_closed:
os.close(dirfd)
else:
try:
os.unlink(entry.name, dir_fd=topfd)
- except OSError:
- onerror(os.unlink, fullname, sys.exc_info())
+ except OSError as err:
+ onexc(os.unlink, fullname, err)
_use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <=
os.supports_dir_fd and
os.scandir in os.supports_fd and
os.stat in os.supports_follow_symlinks)
-def rmtree(path, ignore_errors=False, onerror=None, *, dir_fd=None):
+def rmtree(path, ignore_errors=False, onerror=None, *, onexc=None, dir_fd=None):
"""Recursively delete a directory tree.
If dir_fd is not None, it should be a file descriptor open to a directory;
@@ -680,21 +680,39 @@ def rmtree(path, ignore_errors=False, onerror=None, *, dir_fd=None):
dir_fd may not be implemented on your platform.
If it is unavailable, using it will raise a NotImplementedError.
- If ignore_errors is set, errors are ignored; otherwise, if onerror
- is set, it is called to handle the error with arguments (func,
+ If ignore_errors is set, errors are ignored; otherwise, if onexc or
+ onerror is set, it is called to handle the error with arguments (func,
path, exc_info) where func is platform and implementation dependent;
path is the argument to that function that caused it to fail; and
- exc_info is a tuple returned by sys.exc_info(). If ignore_errors
- is false and onerror is None, an exception is raised.
+ the value of exc_info describes the exception. For onexc it is the
+ exception instance, and for onerror it is a tuple as returned by
+ sys.exc_info(). If ignore_errors is false and both onexc and
+ onerror are None, the exception is reraised.
+ onerror is deprecated and only remains for backwards compatibility.
+ If both onerror and onexc are set, onerror is ignored and onexc is used.
"""
sys.audit("shutil.rmtree", path, dir_fd)
if ignore_errors:
- def onerror(*args):
+ def onexc(*args):
pass
- elif onerror is None:
- def onerror(*args):
+ elif onerror is None and onexc is None:
+ def onexc(*args):
raise
+ elif onexc is None:
+ if onerror is None:
+ def onexc(*args):
+ raise
+ else:
+ # delegate to onerror
+ def onexc(*args):
+ func, path, exc = args
+ if exc is None:
+ exc_info = None, None, None
+ else:
+ exc_info = type(exc), exc, exc.__traceback__
+ return onerror(func, path, exc_info)
+
if _use_fd_functions:
# While the unsafe rmtree works fine on bytes, the fd based does not.
if isinstance(path, bytes):
@@ -703,30 +721,30 @@ def onerror(*args):
# lstat()/open()/fstat() trick.
try:
orig_st = os.lstat(path, dir_fd=dir_fd)
- except Exception:
- onerror(os.lstat, path, sys.exc_info())
+ except Exception as err:
+ onexc(os.lstat, path, err)
return
try:
fd = os.open(path, os.O_RDONLY, dir_fd=dir_fd)
fd_closed = False
- except Exception:
- onerror(os.open, path, sys.exc_info())
+ except Exception as err:
+ onexc(os.open, path, err)
return
try:
if os.path.samestat(orig_st, os.fstat(fd)):
- _rmtree_safe_fd(fd, path, onerror)
+ _rmtree_safe_fd(fd, path, onexc)
try:
os.close(fd)
fd_closed = True
os.rmdir(path, dir_fd=dir_fd)
- except OSError:
- onerror(os.rmdir, path, sys.exc_info())
+ except OSError as err:
+ onexc(os.rmdir, path, err)
else:
try:
# symlinks to directories are forbidden, see bug #1669
raise OSError("Cannot call rmtree on a symbolic link")
- except OSError:
- onerror(os.path.islink, path, sys.exc_info())
+ except OSError as err:
+ onexc(os.path.islink, path, err)
finally:
if not fd_closed:
os.close(fd)
@@ -737,11 +755,11 @@ def onerror(*args):
if _rmtree_islink(path):
# symlinks to directories are forbidden, see bug #1669
raise OSError("Cannot call rmtree on a symbolic link")
- except OSError:
- onerror(os.path.islink, path, sys.exc_info())
- # can't continue even if onerror hook returns
+ except OSError as err:
+ onexc(os.path.islink, path, err)
+ # can't continue even if onexc hook returns
return
- return _rmtree_unsafe(path, onerror)
+ return _rmtree_unsafe(path, onexc)
# Allow introspection of whether or not the hardening against symlink
# attacks is supported on the current platform
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 8fe62216ecdca0..fee3e7f765639a 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -195,7 +195,7 @@ def test_rmtree_works_on_bytes(self):
shutil.rmtree(victim)
@os_helper.skip_unless_symlink
- def test_rmtree_fails_on_symlink(self):
+ def test_rmtree_fails_on_symlink_onerror(self):
tmp = self.mkdtemp()
dir_ = os.path.join(tmp, 'dir')
os.mkdir(dir_)
@@ -213,6 +213,25 @@ def onerror(*args):
self.assertEqual(errors[0][1], link)
self.assertIsInstance(errors[0][2][1], OSError)
+ @os_helper.skip_unless_symlink
+ def test_rmtree_fails_on_symlink_onexc(self):
+ tmp = self.mkdtemp()
+ dir_ = os.path.join(tmp, 'dir')
+ os.mkdir(dir_)
+ link = os.path.join(tmp, 'link')
+ os.symlink(dir_, link)
+ self.assertRaises(OSError, shutil.rmtree, link)
+ self.assertTrue(os.path.exists(dir_))
+ self.assertTrue(os.path.lexists(link))
+ errors = []
+ def onexc(*args):
+ errors.append(args)
+ shutil.rmtree(link, onexc=onexc)
+ self.assertEqual(len(errors), 1)
+ self.assertIs(errors[0][0], os.path.islink)
+ self.assertEqual(errors[0][1], link)
+ self.assertIsInstance(errors[0][2], OSError)
+
@os_helper.skip_unless_symlink
def test_rmtree_works_on_symlinks(self):
tmp = self.mkdtemp()
@@ -236,7 +255,7 @@ def test_rmtree_works_on_symlinks(self):
self.assertTrue(os.path.exists(file1))
@unittest.skipUnless(_winapi, 'only relevant on Windows')
- def test_rmtree_fails_on_junctions(self):
+ def test_rmtree_fails_on_junctions_onerror(self):
tmp = self.mkdtemp()
dir_ = os.path.join(tmp, 'dir')
os.mkdir(dir_)
@@ -255,6 +274,26 @@ def onerror(*args):
self.assertEqual(errors[0][1], link)
self.assertIsInstance(errors[0][2][1], OSError)
+ @unittest.skipUnless(_winapi, 'only relevant on Windows')
+ def test_rmtree_fails_on_junctions_onexc(self):
+ tmp = self.mkdtemp()
+ dir_ = os.path.join(tmp, 'dir')
+ os.mkdir(dir_)
+ link = os.path.join(tmp, 'link')
+ _winapi.CreateJunction(dir_, link)
+ self.addCleanup(os_helper.unlink, link)
+ self.assertRaises(OSError, shutil.rmtree, link)
+ self.assertTrue(os.path.exists(dir_))
+ self.assertTrue(os.path.lexists(link))
+ errors = []
+ def onexc(*args):
+ errors.append(args)
+ shutil.rmtree(link, onexc=onexc)
+ self.assertEqual(len(errors), 1)
+ self.assertIs(errors[0][0], os.path.islink)
+ self.assertEqual(errors[0][1], link)
+ self.assertIsInstance(errors[0][2], OSError)
+
@unittest.skipUnless(_winapi, 'only relevant on Windows')
def test_rmtree_works_on_junctions(self):
tmp = self.mkdtemp()
@@ -277,7 +316,7 @@ def test_rmtree_works_on_junctions(self):
self.assertTrue(os.path.exists(dir3))
self.assertTrue(os.path.exists(file1))
- def test_rmtree_errors(self):
+ def test_rmtree_errors_onerror(self):
# filename is guaranteed not to exist
filename = tempfile.mktemp(dir=self.mkdtemp())
self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
@@ -309,6 +348,37 @@ def onerror(*args):
self.assertIsInstance(errors[1][2][1], NotADirectoryError)
self.assertEqual(errors[1][2][1].filename, filename)
+ def test_rmtree_errors_onexc(self):
+ # filename is guaranteed not to exist
+ filename = tempfile.mktemp(dir=self.mkdtemp())
+ self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
+ # test that ignore_errors option is honored
+ shutil.rmtree(filename, ignore_errors=True)
+
+ # existing file
+ tmpdir = self.mkdtemp()
+ write_file((tmpdir, "tstfile"), "")
+ filename = os.path.join(tmpdir, "tstfile")
+ with self.assertRaises(NotADirectoryError) as cm:
+ shutil.rmtree(filename)
+ self.assertEqual(cm.exception.filename, filename)
+ self.assertTrue(os.path.exists(filename))
+ # test that ignore_errors option is honored
+ shutil.rmtree(filename, ignore_errors=True)
+ self.assertTrue(os.path.exists(filename))
+ errors = []
+ def onexc(*args):
+ errors.append(args)
+ shutil.rmtree(filename, onexc=onexc)
+ self.assertEqual(len(errors), 2)
+ self.assertIs(errors[0][0], os.scandir)
+ self.assertEqual(errors[0][1], filename)
+ self.assertIsInstance(errors[0][2], NotADirectoryError)
+ self.assertEqual(errors[0][2].filename, filename)
+ self.assertIs(errors[1][0], os.rmdir)
+ self.assertEqual(errors[1][1], filename)
+ self.assertIsInstance(errors[1][2], NotADirectoryError)
+ self.assertEqual(errors[1][2].filename, filename)
@unittest.skipIf(sys.platform[:6] == 'cygwin',
"This test can't be run on Cygwin (issue #1071513).")
@@ -368,6 +438,100 @@ def check_args_to_onerror(self, func, arg, exc):
self.assertTrue(issubclass(exc[0], OSError))
self.errorState = 3
+ @unittest.skipIf(sys.platform[:6] == 'cygwin',
+ "This test can't be run on Cygwin (issue #1071513).")
+ @os_helper.skip_if_dac_override
+ @os_helper.skip_unless_working_chmod
+ def test_on_exc(self):
+ self.errorState = 0
+ os.mkdir(TESTFN)
+ self.addCleanup(shutil.rmtree, TESTFN)
+
+ self.child_file_path = os.path.join(TESTFN, 'a')
+ self.child_dir_path = os.path.join(TESTFN, 'b')
+ os_helper.create_empty_file(self.child_file_path)
+ os.mkdir(self.child_dir_path)
+ old_dir_mode = os.stat(TESTFN).st_mode
+ old_child_file_mode = os.stat(self.child_file_path).st_mode
+ old_child_dir_mode = os.stat(self.child_dir_path).st_mode
+ # Make unwritable.
+ new_mode = stat.S_IREAD|stat.S_IEXEC
+ os.chmod(self.child_file_path, new_mode)
+ os.chmod(self.child_dir_path, new_mode)
+ os.chmod(TESTFN, new_mode)
+
+ self.addCleanup(os.chmod, TESTFN, old_dir_mode)
+ self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
+ self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
+
+ shutil.rmtree(TESTFN, onexc=self.check_args_to_onexc)
+ # Test whether onexc has actually been called.
+ self.assertEqual(self.errorState, 3,
+ "Expected call to onexc function did not happen.")
+
+ def check_args_to_onexc(self, func, arg, exc):
+ # test_rmtree_errors deliberately runs rmtree
+ # on a directory that is chmod 500, which will fail.
+ # This function is run when shutil.rmtree fails.
+ # 99.9% of the time it initially fails to remove
+ # a file in the directory, so the first time through
+ # func is os.remove.
+ # However, some Linux machines running ZFS on
+ # FUSE experienced a failure earlier in the process
+ # at os.listdir. The first failure may legally
+ # be either.
+ if self.errorState < 2:
+ if func is os.unlink:
+ self.assertEqual(arg, self.child_file_path)
+ elif func is os.rmdir:
+ self.assertEqual(arg, self.child_dir_path)
+ else:
+ self.assertIs(func, os.listdir)
+ self.assertIn(arg, [TESTFN, self.child_dir_path])
+ self.assertTrue(isinstance(exc, OSError))
+ self.errorState += 1
+ else:
+ self.assertEqual(func, os.rmdir)
+ self.assertEqual(arg, TESTFN)
+ self.assertTrue(isinstance(exc, OSError))
+ self.errorState = 3
+
+ def test_both_onerror_and_onexc(self):
+ onerror_called = False
+ onexc_called = False
+
+ def onerror(*args):
+ nonlocal onerror_called
+ onerror_called = True
+
+ def onexc(*args):
+ nonlocal onexc_called
+ onexc_called = True
+
+ os.mkdir(TESTFN)
+ self.addCleanup(shutil.rmtree, TESTFN)
+
+ self.child_file_path = os.path.join(TESTFN, 'a')
+ self.child_dir_path = os.path.join(TESTFN, 'b')
+ os_helper.create_empty_file(self.child_file_path)
+ os.mkdir(self.child_dir_path)
+ old_dir_mode = os.stat(TESTFN).st_mode
+ old_child_file_mode = os.stat(self.child_file_path).st_mode
+ old_child_dir_mode = os.stat(self.child_dir_path).st_mode
+ # Make unwritable.
+ new_mode = stat.S_IREAD|stat.S_IEXEC
+ os.chmod(self.child_file_path, new_mode)
+ os.chmod(self.child_dir_path, new_mode)
+ os.chmod(TESTFN, new_mode)
+
+ self.addCleanup(os.chmod, TESTFN, old_dir_mode)
+ self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
+ self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
+
+ shutil.rmtree(TESTFN, onerror=onerror, onexc=onexc)
+ self.assertTrue(onexc_called)
+ self.assertFalse(onerror_called)
+
def test_rmtree_does_not_choke_on_failing_lstat(self):
try:
orig_lstat = os.lstat
diff --git a/Misc/NEWS.d/next/Library/2023-03-19-15-30-59.gh-issue-102828.NKClXg.rst b/Misc/NEWS.d/next/Library/2023-03-19-15-30-59.gh-issue-102828.NKClXg.rst
new file mode 100644
index 00000000000000..be9b2bab24a381
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-19-15-30-59.gh-issue-102828.NKClXg.rst
@@ -0,0 +1,3 @@
+Add the ``onexc`` arg to :func:`shutil.rmtree`, which is like ``onerror``
+but expects an exception instance rather than an exc_info tuple. Deprecate
+``onerror``.
From d66b16bb86f446b5bc10703fa7a7129a702289cb Mon Sep 17 00:00:00 2001
From: JosephSBoyle <48555120+JosephSBoyle@users.noreply.github.com>
Date: Sun, 19 Mar 2023 20:06:09 +0000
Subject: [PATCH 150/280] gh-102810: Add docstrings to the public-facing
methods of `asyncio.Timeout` (#102811)
Co-authored-by: Guido van Rossum
Co-authored-by: Alex Waygood
---
Lib/asyncio/timeouts.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/Lib/asyncio/timeouts.py b/Lib/asyncio/timeouts.py
index 94d25535fbc059..d07b291005e8f9 100644
--- a/Lib/asyncio/timeouts.py
+++ b/Lib/asyncio/timeouts.py
@@ -25,8 +25,18 @@ class _State(enum.Enum):
@final
class Timeout:
+ """Asynchronous context manager for cancelling overdue coroutines.
+
+ Use `timeout()` or `timeout_at()` rather than instantiating this class directly.
+ """
def __init__(self, when: Optional[float]) -> None:
+ """Schedule a timeout that will trigger at a given loop time.
+
+ - If `when` is `None`, the timeout will never trigger.
+ - If `when < loop.time()`, the timeout will trigger on the next
+ iteration of the event loop.
+ """
self._state = _State.CREATED
self._timeout_handler: Optional[events.TimerHandle] = None
@@ -34,9 +44,11 @@ def __init__(self, when: Optional[float]) -> None:
self._when = when
def when(self) -> Optional[float]:
+ """Return the current deadline."""
return self._when
def reschedule(self, when: Optional[float]) -> None:
+ """Reschedule the timeout."""
assert self._state is not _State.CREATED
if self._state is not _State.ENTERED:
raise RuntimeError(
From 49de99b65b551ff302e270ea477658e18e0488eb Mon Sep 17 00:00:00 2001
From: Pieter Eendebak
Date: Sun, 19 Mar 2023 23:52:47 +0100
Subject: [PATCH 151/280] gh-102491: Remove IronPython version check in
sys_version (#102492)
---
Lib/platform.py | 35 +------------------
Lib/test/test_platform.py | 24 ++++---------
...-03-08-08-37-36.gh-issue-102491.SFvvsC.rst | 2 ++
3 files changed, 10 insertions(+), 51 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-08-08-37-36.gh-issue-102491.SFvvsC.rst
diff --git a/Lib/platform.py b/Lib/platform.py
index f2b0d1d1bd3f5d..790ef860bf106e 100755
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -1040,20 +1040,6 @@ def processor():
r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)"
r'\[([^\]]+)\]?', re.ASCII) # "[compiler]"
-_ironpython_sys_version_parser = re.compile(
- r'IronPython\s*'
- r'([\d\.]+)'
- r'(?: \(([\d\.]+)\))?'
- r' on (.NET [\d\.]+)', re.ASCII)
-
-# IronPython covering 2.6 and 2.7
-_ironpython26_sys_version_parser = re.compile(
- r'([\d.]+)\s*'
- r'\(IronPython\s*'
- r'[\d.]+\s*'
- r'\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)'
-)
-
_pypy_sys_version_parser = re.compile(
r'([\w.+]+)\s*'
r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
@@ -1090,25 +1076,7 @@ def _sys_version(sys_version=None):
if result is not None:
return result
- # Parse it
- if 'IronPython' in sys_version:
- # IronPython
- name = 'IronPython'
- if sys_version.startswith('IronPython'):
- match = _ironpython_sys_version_parser.match(sys_version)
- else:
- match = _ironpython26_sys_version_parser.match(sys_version)
-
- if match is None:
- raise ValueError(
- 'failed to parse IronPython sys.version: %s' %
- repr(sys_version))
-
- version, alt_version, compiler = match.groups()
- buildno = ''
- builddate = ''
-
- elif sys.platform.startswith('java'):
+ if sys.platform.startswith('java'):
# Jython
name = 'Jython'
match = _sys_version_parser.match(sys_version)
@@ -1171,7 +1139,6 @@ def python_implementation():
Currently, the following implementations are identified:
'CPython' (C implementation of Python),
- 'IronPython' (.NET implementation of Python),
'Jython' (Java implementation of Python),
'PyPy' (Python implementation of Python).
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 72942dda342418..216973350319fe 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -123,10 +123,6 @@ def test_sys_version(self):
for input, output in (
('2.4.3 (#1, Jun 21 2006, 13:54:21) \n[GCC 3.3.4 (pre 3.3.5 20040809)]',
('CPython', '2.4.3', '', '', '1', 'Jun 21 2006 13:54:21', 'GCC 3.3.4 (pre 3.3.5 20040809)')),
- ('IronPython 1.0.60816 on .NET 2.0.50727.42',
- ('IronPython', '1.0.60816', '', '', '', '', '.NET 2.0.50727.42')),
- ('IronPython 1.0 (1.0.61005.1977) on .NET 2.0.50727.42',
- ('IronPython', '1.0.0', '', '', '', '', '.NET 2.0.50727.42')),
('2.4.3 (truncation, date, t) \n[GCC]',
('CPython', '2.4.3', '', '', 'truncation', 'date t', 'GCC')),
('2.4.3 (truncation, date, ) \n[GCC]',
@@ -161,20 +157,11 @@ def test_sys_version(self):
('r261:67515', 'Dec 6 2008 15:26:00'),
'GCC 4.0.1 (Apple Computer, Inc. build 5370)'),
- ("IronPython 2.0 (2.0.0.0) on .NET 2.0.50727.3053", None, "cli")
+ ("3.10.8 (tags/v3.10.8:aaaf517424, Feb 14 2023, 16:28:12) [GCC 9.4.0]",
+ None, "linux")
:
- ("IronPython", "2.0.0", "", "", ("", ""),
- ".NET 2.0.50727.3053"),
-
- ("2.6.1 (IronPython 2.6.1 (2.6.10920.0) on .NET 2.0.50727.1433)", None, "cli")
- :
- ("IronPython", "2.6.1", "", "", ("", ""),
- ".NET 2.0.50727.1433"),
-
- ("2.7.4 (IronPython 2.7.4 (2.7.0.40) on Mono 4.0.30319.1 (32-bit))", None, "cli")
- :
- ("IronPython", "2.7.4", "", "", ("", ""),
- "Mono 4.0.30319.1 (32-bit)"),
+ ('CPython', '3.10.8', '', '',
+ ('tags/v3.10.8:aaaf517424', 'Feb 14 2023 16:28:12'), 'GCC 9.4.0'),
("2.5 (trunk:6107, Mar 26 2009, 13:02:18) \n[Java HotSpot(TM) Client VM (\"Apple Computer, Inc.\")]",
('Jython', 'trunk', '6107'), "java1.5.0_16")
@@ -205,6 +192,9 @@ def test_sys_version(self):
self.assertEqual(platform.python_build(), info[4])
self.assertEqual(platform.python_compiler(), info[5])
+ with self.assertRaises(ValueError):
+ platform._sys_version('2. 4.3 (truncation) \n[GCC]')
+
def test_system_alias(self):
res = platform.system_alias(
platform.system(),
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-08-08-37-36.gh-issue-102491.SFvvsC.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-08-08-37-36.gh-issue-102491.SFvvsC.rst
new file mode 100644
index 00000000000000..5bdc9ed2f37adc
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-08-08-37-36.gh-issue-102491.SFvvsC.rst
@@ -0,0 +1,2 @@
+Improve import time of ``platform`` by removing IronPython version parsing. The IronPython version parsing
+was not functional (see https://github.com/IronLanguages/ironpython3/issues/1667).
From 5699276ed0455e840dcf2fce7035ffd9daf553b4 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Mon, 20 Mar 2023 00:03:55 +0000
Subject: [PATCH 152/280] gh-102828: fix test failure (add missing skip
instructions) (#102835)
---
Lib/test/test_shutil.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index fee3e7f765639a..89d65af3bc5b2b 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -496,6 +496,10 @@ def check_args_to_onexc(self, func, arg, exc):
self.assertTrue(isinstance(exc, OSError))
self.errorState = 3
+ @unittest.skipIf(sys.platform[:6] == 'cygwin',
+ "This test can't be run on Cygwin (issue #1071513).")
+ @os_helper.skip_if_dac_override
+ @os_helper.skip_unless_working_chmod
def test_both_onerror_and_onexc(self):
onerror_called = False
onexc_called = False
From 7dab35937e5c97fa8f3713b023bbdea4671b572e Mon Sep 17 00:00:00 2001
From: Alan Williams
Date: Sun, 19 Mar 2023 19:20:20 -0500
Subject: [PATCH 153/280] gh-72346: Added isdst deprecation warning to
email.utils.localtime (GH-91450)
---
Doc/library/email.utils.rst | 22 +++++-----
Doc/whatsnew/3.12.rst | 3 ++
Lib/email/utils.py | 40 +++++--------------
Lib/test/test_email/test_utils.py | 13 ++++--
...2-04-11-18-34-33.gh-issue-72346.pC7gnM.rst | 1 +
5 files changed, 34 insertions(+), 45 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-72346.pC7gnM.rst
diff --git a/Doc/library/email.utils.rst b/Doc/library/email.utils.rst
index 0e266b6a45782a..345b64001c1ace 100644
--- a/Doc/library/email.utils.rst
+++ b/Doc/library/email.utils.rst
@@ -13,19 +13,17 @@ module:
.. function:: localtime(dt=None)
- Return local time as an aware datetime object. If called without
- arguments, return current time. Otherwise *dt* argument should be a
- :class:`~datetime.datetime` instance, and it is converted to the local time
- zone according to the system time zone database. If *dt* is naive (that
- is, ``dt.tzinfo`` is ``None``), it is assumed to be in local time. In this
- case, a positive or zero value for *isdst* causes ``localtime`` to presume
- initially that summer time (for example, Daylight Saving Time) is or is not
- (respectively) in effect for the specified time. A negative value for
- *isdst* causes the ``localtime`` to attempt to divine whether summer time
- is in effect for the specified time.
-
- .. versionadded:: 3.3
+ Return local time as an aware datetime object. If called without
+ arguments, return current time. Otherwise *dt* argument should be a
+ :class:`~datetime.datetime` instance, and it is converted to the local time
+ zone according to the system time zone database. If *dt* is naive (that
+ is, ``dt.tzinfo`` is ``None``), it is assumed to be in local time. The
+ *isdst* parameter is ignored.
+ .. versionadded:: 3.3
+
+ .. deprecated-removed:: 3.12 3.14
+ The *isdst* parameter.
.. function:: make_msgid(idstring=None, domain=None)
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index a36e68528c4f74..cdd26cd19e720b 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -565,6 +565,9 @@ Pending Removal in Python 3.14
* Creating :c:data:`immutable types ` with mutable
bases using the C API.
+* Deprecated the *isdst* parameter in :func:`email.utils.localtime`.
+ (Contributed by Alan Williams in :gh:`72346`.)
+
* ``__package__`` and ``__cached__`` will cease to be set or taken
into consideration by the import system (:gh:`97879`).
diff --git a/Lib/email/utils.py b/Lib/email/utils.py
index cfdfeb3f1a86e4..4d014bacd6182e 100644
--- a/Lib/email/utils.py
+++ b/Lib/email/utils.py
@@ -331,41 +331,23 @@ def collapse_rfc2231_value(value, errors='replace',
# better than not having it.
#
-def localtime(dt=None, isdst=-1):
+def localtime(dt=None, isdst=None):
"""Return local time as an aware datetime object.
If called without arguments, return current time. Otherwise *dt*
argument should be a datetime instance, and it is converted to the
local time zone according to the system time zone database. If *dt* is
naive (that is, dt.tzinfo is None), it is assumed to be in local time.
- In this case, a positive or zero value for *isdst* causes localtime to
- presume initially that summer time (for example, Daylight Saving Time)
- is or is not (respectively) in effect for the specified time. A
- negative value for *isdst* causes the localtime() function to attempt
- to divine whether summer time is in effect for the specified time.
+ The isdst parameter is ignored.
"""
+ if isdst is not None:
+ import warnings
+ warnings._deprecated(
+ "The 'isdst' parameter to 'localtime'",
+ message='{name} is deprecated and slated for removal in Python {remove}',
+ remove=(3, 14),
+ )
if dt is None:
- return datetime.datetime.now(datetime.timezone.utc).astimezone()
- if dt.tzinfo is not None:
- return dt.astimezone()
- # We have a naive datetime. Convert to a (localtime) timetuple and pass to
- # system mktime together with the isdst hint. System mktime will return
- # seconds since epoch.
- tm = dt.timetuple()[:-1] + (isdst,)
- seconds = time.mktime(tm)
- localtm = time.localtime(seconds)
- try:
- delta = datetime.timedelta(seconds=localtm.tm_gmtoff)
- tz = datetime.timezone(delta, localtm.tm_zone)
- except AttributeError:
- # Compute UTC offset and compare with the value implied by tm_isdst.
- # If the values match, use the zone name implied by tm_isdst.
- delta = dt - datetime.datetime(*time.gmtime(seconds)[:6])
- dst = time.daylight and localtm.tm_isdst > 0
- gmtoff = -(time.altzone if dst else time.timezone)
- if delta == datetime.timedelta(seconds=gmtoff):
- tz = datetime.timezone(delta, time.tzname[dst])
- else:
- tz = datetime.timezone(delta)
- return dt.replace(tzinfo=tz)
+ dt = datetime.datetime.now()
+ return dt.astimezone()
diff --git a/Lib/test/test_email/test_utils.py b/Lib/test/test_email/test_utils.py
index 78afb358035e81..25fa48c5ee217b 100644
--- a/Lib/test/test_email/test_utils.py
+++ b/Lib/test/test_email/test_utils.py
@@ -83,14 +83,14 @@ def test_localtime_is_tz_aware_daylight_false(self):
def test_localtime_daylight_true_dst_false(self):
test.support.patch(self, time, 'daylight', True)
t0 = datetime.datetime(2012, 3, 12, 1, 1)
- t1 = utils.localtime(t0, isdst=-1)
+ t1 = utils.localtime(t0)
t2 = utils.localtime(t1)
self.assertEqual(t1, t2)
def test_localtime_daylight_false_dst_false(self):
test.support.patch(self, time, 'daylight', False)
t0 = datetime.datetime(2012, 3, 12, 1, 1)
- t1 = utils.localtime(t0, isdst=-1)
+ t1 = utils.localtime(t0)
t2 = utils.localtime(t1)
self.assertEqual(t1, t2)
@@ -98,7 +98,7 @@ def test_localtime_daylight_false_dst_false(self):
def test_localtime_daylight_true_dst_true(self):
test.support.patch(self, time, 'daylight', True)
t0 = datetime.datetime(2012, 3, 12, 1, 1)
- t1 = utils.localtime(t0, isdst=1)
+ t1 = utils.localtime(t0)
t2 = utils.localtime(t1)
self.assertEqual(t1, t2)
@@ -106,7 +106,7 @@ def test_localtime_daylight_true_dst_true(self):
def test_localtime_daylight_false_dst_true(self):
test.support.patch(self, time, 'daylight', False)
t0 = datetime.datetime(2012, 3, 12, 1, 1)
- t1 = utils.localtime(t0, isdst=1)
+ t1 = utils.localtime(t0)
t2 = utils.localtime(t1)
self.assertEqual(t1, t2)
@@ -157,6 +157,11 @@ def test_variable_tzname(self):
t1 = utils.localtime(t0)
self.assertEqual(t1.tzname(), 'EET')
+ def test_isdst_deprecation(self):
+ with self.assertWarns(DeprecationWarning):
+ t0 = datetime.datetime(1990, 1, 1)
+ t1 = utils.localtime(t0, isdst=True)
+
# Issue #24836: The timezone files are out of date (pre 2011k)
# on Mac OS X Snow Leopard.
@test.support.requires_mac_ver(10, 7)
diff --git a/Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-72346.pC7gnM.rst b/Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-72346.pC7gnM.rst
new file mode 100644
index 00000000000000..149ddd706c358f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-72346.pC7gnM.rst
@@ -0,0 +1 @@
+Added deprecation warning to *isdst* parameter of :func:`email.utils.localtime`.
From 6e01113b05b88fef73d9b7c9c28892df3ca9e3b4 Mon Sep 17 00:00:00 2001
From: Max Bachmann
Date: Mon, 20 Mar 2023 16:47:17 +0100
Subject: [PATCH 154/280] gh-102255: Use GetVersionEx instead of GetVersionExW
to match argument type (GH-102583)
Since we pass a structure of type `OSVERSIONINFOEX`, we need to call
`GetVersionEx` instead of `GetVersionExW`.
---
Modules/socketmodule.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index b7927750e334b7..6a6f8cf7392e1c 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -353,7 +353,7 @@ remove_unusable_flags(PyObject *m)
}
#ifndef MS_WINDOWS_DESKTOP
info.dwOSVersionInfoSize = sizeof(info);
- if (!GetVersionExW((OSVERSIONINFOW*) &info)) {
+ if (!GetVersionEx((OSVERSIONINFO*) &info)) {
PyErr_SetFromWindowsErr(0);
return -1;
}
From 2b10a8d5ca94144cd4fd9f4242ebc4070a0553ab Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Mon, 20 Mar 2023 10:03:04 -0600
Subject: [PATCH 155/280] gh-102304: Move _Py_RefTotal to _PyRuntimeState
(gh-102543)
The essentially eliminates the global variable, with the associated benefits. This is also a precursor to isolating this bit of state to PyInterpreterState.
Folks that currently read _Py_RefTotal directly would have to start using _Py_GetGlobalRefTotal() instead.
https://github.com/python/cpython/issues/102304
---
Include/cpython/object.h | 5 +-
Include/internal/pycore_object.h | 4 +-
Include/internal/pycore_object_state.h | 23 ++++++
Include/internal/pycore_runtime.h | 2 +
Include/object.h | 9 +--
Makefile.pre.in | 1 +
Objects/object.c | 101 +++++++++++++++++++------
PCbuild/pythoncore.vcxproj | 1 +
PCbuild/pythoncore.vcxproj.filters | 3 +
Python/pylifecycle.c | 1 +
Python/pystate.c | 3 +
Python/sysmodule.c | 2 +-
Tools/c-analyzer/cpython/ignored.tsv | 2 +-
13 files changed, 121 insertions(+), 36 deletions(-)
create mode 100644 Include/internal/pycore_object_state.h
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index 7b687d311359c3..0438612edd1dfe 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -11,7 +11,10 @@ PyAPI_FUNC(void) _Py_ForgetReference(PyObject *);
#endif
#ifdef Py_REF_DEBUG
-PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void);
+/* These are useful as debugging aids when chasing down refleaks. */
+PyAPI_FUNC(Py_ssize_t) _Py_GetGlobalRefTotal(void);
+# define _Py_GetRefTotal() _Py_GetGlobalRefTotal()
+PyAPI_FUNC(Py_ssize_t) _Py_GetLegacyRefTotal(void);
#endif
diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index 318e6f3371c0c3..b985eff8a8a08b 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -46,7 +46,8 @@ PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
extern void _Py_AddRefTotal(Py_ssize_t);
extern void _Py_IncRefTotal(void);
extern void _Py_DecRefTotal(void);
-# define _Py_DEC_REFTOTAL() _Py_RefTotal--
+
+# define _Py_DEC_REFTOTAL() _PyRuntime.object_state.reftotal--
#endif
// Increment reference count by n
@@ -225,6 +226,7 @@ static inline void _PyObject_GC_UNTRACK(
#endif
#ifdef Py_REF_DEBUG
+extern void _Py_FinalizeRefTotal(_PyRuntimeState *);
extern void _PyDebug_PrintTotalRefs(void);
#endif
diff --git a/Include/internal/pycore_object_state.h b/Include/internal/pycore_object_state.h
new file mode 100644
index 00000000000000..4e5862a11eddc5
--- /dev/null
+++ b/Include/internal/pycore_object_state.h
@@ -0,0 +1,23 @@
+#ifndef Py_INTERNAL_OBJECT_STATE_H
+#define Py_INTERNAL_OBJECT_STATE_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+# error "this header requires Py_BUILD_CORE define"
+#endif
+
+struct _py_object_runtime_state {
+#ifdef Py_REF_DEBUG
+ Py_ssize_t reftotal;
+#else
+ int _not_used;
+#endif
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_INTERNAL_OBJECT_STATE_H */
diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h
index 520109ca440444..de757dfa93bc26 100644
--- a/Include/internal/pycore_runtime.h
+++ b/Include/internal/pycore_runtime.h
@@ -15,6 +15,7 @@ extern "C" {
#include "pycore_global_objects.h" // struct _Py_global_objects
#include "pycore_import.h" // struct _import_runtime_state
#include "pycore_interp.h" // PyInterpreterState
+#include "pycore_object_state.h" // struct _py_object_runtime_state
#include "pycore_parser.h" // struct _parser_runtime_state
#include "pycore_pymem.h" // struct _pymem_allocators
#include "pycore_pyhash.h" // struct pyhash_runtime_state
@@ -150,6 +151,7 @@ typedef struct pyruntimestate {
void *open_code_userdata;
_Py_AuditHookEntry *audit_hook_head;
+ struct _py_object_runtime_state object_state;
struct _Py_float_runtime_state float_state;
struct _Py_unicode_runtime_state unicode_state;
diff --git a/Include/object.h b/Include/object.h
index 844b9c4a51c3e4..fc577353c1cc13 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -494,14 +494,9 @@ you can count such references to the type object.)
extern Py_ssize_t _Py_RefTotal;
# define _Py_INC_REFTOTAL() _Py_RefTotal++
# define _Py_DEC_REFTOTAL() _Py_RefTotal--
-# elif defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-extern void _Py_IncRefTotal(void);
-extern void _Py_DecRefTotal(void);
-# define _Py_INC_REFTOTAL() _Py_IncRefTotal()
-# define _Py_DEC_REFTOTAL() _Py_DecRefTotal()
# elif !defined(Py_LIMITED_API) || Py_LIMITED_API+0 > 0x030C0000
-extern void _Py_IncRefTotal_DO_NOT_USE_THIS(void);
-extern void _Py_DecRefTotal_DO_NOT_USE_THIS(void);
+PyAPI_FUNC(void) _Py_IncRefTotal_DO_NOT_USE_THIS(void);
+PyAPI_FUNC(void) _Py_DecRefTotal_DO_NOT_USE_THIS(void);
# define _Py_INC_REFTOTAL() _Py_IncRefTotal_DO_NOT_USE_THIS()
# define _Py_DEC_REFTOTAL() _Py_DecRefTotal_DO_NOT_USE_THIS()
# endif
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 8f13198e7e34b3..74e4171b010d0f 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1699,6 +1699,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_moduleobject.h \
$(srcdir)/Include/internal/pycore_namespace.h \
$(srcdir)/Include/internal/pycore_object.h \
+ $(srcdir)/Include/internal/pycore_object_state.h \
$(srcdir)/Include/internal/pycore_obmalloc.h \
$(srcdir)/Include/internal/pycore_obmalloc_init.h \
$(srcdir)/Include/internal/pycore_pathconfig.h \
diff --git a/Objects/object.c b/Objects/object.c
index dff5e2afa16ab8..95f7c966a414de 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -54,37 +54,71 @@ _PyObject_CheckConsistency(PyObject *op, int check_content)
#ifdef Py_REF_DEBUG
+/* We keep the legacy symbol around for backward compatibility. */
Py_ssize_t _Py_RefTotal;
+static inline Py_ssize_t
+get_legacy_reftotal(void)
+{
+ return _Py_RefTotal;
+}
+#endif
+
+#ifdef Py_REF_DEBUG
+
+# define REFTOTAL(runtime) \
+ (runtime)->object_state.reftotal
+
+static inline void
+reftotal_increment(_PyRuntimeState *runtime)
+{
+ REFTOTAL(runtime)++;
+}
+
static inline void
-reftotal_increment(void)
+reftotal_decrement(_PyRuntimeState *runtime)
{
- _Py_RefTotal++;
+ REFTOTAL(runtime)--;
}
static inline void
-reftotal_decrement(void)
+reftotal_add(_PyRuntimeState *runtime, Py_ssize_t n)
{
- _Py_RefTotal--;
+ REFTOTAL(runtime) += n;
}
+static inline Py_ssize_t get_global_reftotal(_PyRuntimeState *);
+
+/* We preserve the number of refs leaked during runtime finalization,
+ so they can be reported if the runtime is initialized again. */
+// XXX We don't lose any information by dropping this,
+// so we should consider doing so.
+static Py_ssize_t last_final_reftotal = 0;
+
void
-_Py_AddRefTotal(Py_ssize_t n)
+_Py_FinalizeRefTotal(_PyRuntimeState *runtime)
{
- _Py_RefTotal += n;
+ last_final_reftotal = get_global_reftotal(runtime);
+ REFTOTAL(runtime) = 0;
}
-Py_ssize_t
-_Py_GetRefTotal(void)
+static inline Py_ssize_t
+get_global_reftotal(_PyRuntimeState *runtime)
{
- return _Py_RefTotal;
+ /* For an update from _Py_RefTotal first. */
+ Py_ssize_t legacy = get_legacy_reftotal();
+ return REFTOTAL(runtime) + legacy + last_final_reftotal;
}
+#undef REFTOTAL
+
void
_PyDebug_PrintTotalRefs(void) {
+ _PyRuntimeState *runtime = &_PyRuntime;
fprintf(stderr,
"[%zd refs, %zd blocks]\n",
- _Py_GetRefTotal(), _Py_GetAllocatedBlocks());
+ get_global_reftotal(runtime), _Py_GetAllocatedBlocks());
+ /* It may be helpful to also print the "legacy" reftotal separately. */
}
#endif /* Py_REF_DEBUG */
@@ -139,30 +173,50 @@ _Py_NegativeRefcount(const char *filename, int lineno, PyObject *op)
filename, lineno, __func__);
}
-/* This is exposed strictly for use in Py_INCREF(). */
-PyAPI_FUNC(void)
+/* This is used strictly by Py_INCREF(). */
+void
_Py_IncRefTotal_DO_NOT_USE_THIS(void)
{
- reftotal_increment();
+ reftotal_increment(&_PyRuntime);
}
-/* This is exposed strictly for use in Py_DECREF(). */
-PyAPI_FUNC(void)
+/* This is used strictly by Py_DECREF(). */
+void
_Py_DecRefTotal_DO_NOT_USE_THIS(void)
{
- reftotal_decrement();
+ reftotal_decrement(&_PyRuntime);
}
void
_Py_IncRefTotal(void)
{
- reftotal_increment();
+ reftotal_increment(&_PyRuntime);
}
void
_Py_DecRefTotal(void)
{
- reftotal_decrement();
+ reftotal_decrement(&_PyRuntime);
+}
+
+void
+_Py_AddRefTotal(Py_ssize_t n)
+{
+ reftotal_add(&_PyRuntime, n);
+}
+
+/* This includes the legacy total
+ and any carried over from the last runtime init/fini cycle. */
+Py_ssize_t
+_Py_GetGlobalRefTotal(void)
+{
+ return get_global_reftotal(&_PyRuntime);
+}
+
+Py_ssize_t
+_Py_GetLegacyRefTotal(void)
+{
+ return get_legacy_reftotal();
}
#endif /* Py_REF_DEBUG */
@@ -182,21 +236,18 @@ Py_DecRef(PyObject *o)
void
_Py_IncRef(PyObject *o)
{
-#ifdef Py_REF_DEBUG
- reftotal_increment();
-#endif
Py_INCREF(o);
}
void
_Py_DecRef(PyObject *o)
{
-#ifdef Py_REF_DEBUG
- reftotal_decrement();
-#endif
Py_DECREF(o);
}
+
+/**************************************/
+
PyObject *
PyObject_Init(PyObject *op, PyTypeObject *tp)
{
@@ -2077,7 +2128,7 @@ void
_Py_NewReference(PyObject *op)
{
#ifdef Py_REF_DEBUG
- reftotal_increment();
+ reftotal_increment(&_PyRuntime);
#endif
new_reference(op);
}
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index 447a6787de8af7..03d70e711c32bf 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -239,6 +239,7 @@
+
diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index b29c2dc62667a6..cf03edff4e10d1 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -621,6 +621,9 @@
Include\internal
+
+ Include\internal
+
Include\internal
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 317d6966d034fa..731f340001b4e0 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1930,6 +1930,7 @@ Py_FinalizeEx(void)
if (show_ref_count) {
_PyDebug_PrintTotalRefs();
}
+ _Py_FinalizeRefTotal(runtime);
#endif
#ifdef Py_TRACE_REFS
diff --git a/Python/pystate.c b/Python/pystate.c
index 3a2966c54a4c3b..4d4213551a8b1c 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -482,6 +482,9 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
void
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
{
+ /* The reftotal is cleared by _Py_FinalizeRefTotal(). */
+ assert(runtime->object_state.reftotal == 0);
+
if (gilstate_tss_initialized(runtime)) {
gilstate_tss_fini(runtime);
}
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 126b7d422e0009..20761738b527cb 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1854,7 +1854,7 @@ static Py_ssize_t
sys_gettotalrefcount_impl(PyObject *module)
/*[clinic end generated code: output=4103886cf17c25bc input=53b744faa5d2e4f6]*/
{
- return _Py_GetRefTotal();
+ return _Py_GetGlobalRefTotal();
}
#endif /* Py_REF_DEBUG */
diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv
index 048112dd992555..14fc32a07b4309 100644
--- a/Tools/c-analyzer/cpython/ignored.tsv
+++ b/Tools/c-analyzer/cpython/ignored.tsv
@@ -141,7 +141,6 @@ Modules/syslogmodule.c - S_log_open -
##-----------------------
## kept for stable ABI compatibility
-# XXX should be per-interpreter, without impacting stable ABI extensions
Objects/object.c - _Py_RefTotal -
##-----------------------
@@ -301,6 +300,7 @@ Objects/genobject.c - NON_INIT_CORO_MSG -
Objects/longobject.c - _PyLong_DigitValue -
Objects/object.c - _Py_SwappedOp -
Objects/object.c - _Py_abstract_hack -
+Objects/object.c - last_final_reftotal -
Objects/object.c - static_types -
Objects/obmalloc.c - _PyMem -
Objects/obmalloc.c - _PyMem_Debug -
From 457391d4753da3288fb382d312165c905d46310c Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Mon, 20 Mar 2023 10:35:49 -0600
Subject: [PATCH 156/280] gh-102304: Add a What's New Entry About _Py_RefTotal
(gh-102845)
https://github.com/python/cpython/issues/102304
---
Doc/whatsnew/3.12.rst | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index cdd26cd19e720b..af52c7cb670c61 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -1036,6 +1036,11 @@ Porting to Python 3.12
functions that set the error indicator now normalize the exception
before storing it. (Contributed by Mark Shannon in :gh:`101578`.)
+* ``_Py_RefTotal`` is no longer authoritative and only kept around
+ for ABI compabitility. Note that it is an internal global and only
+ available on debug builds. If you happen to be using it then you'll
+ need to start using ``_Py_GetGlobalRefTotal()``.
+
Deprecated
----------
From 76fb37b43b179cc8e11fe2b3c200f128c7ee1e8f Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Mon, 20 Mar 2023 11:28:13 -0600
Subject: [PATCH 157/280] gh-102304: Fix Non-Debug Builds (gh-102846)
Some debug-only code slipped in with gh-102543.
https://github.com/python/cpython/issues/102304
---
Python/pystate.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Python/pystate.c b/Python/pystate.c
index 4d4213551a8b1c..60adb54685ce68 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -482,8 +482,10 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
void
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
{
+#ifdef Py_REF_DEBUG
/* The reftotal is cleared by _Py_FinalizeRefTotal(). */
assert(runtime->object_state.reftotal == 0);
+#endif
if (gilstate_tss_initialized(runtime)) {
gilstate_tss_fini(runtime);
From a003e0ff5ee4d3020574e7a9c0418531bfa664d9 Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Mon, 20 Mar 2023 17:14:29 -0500
Subject: [PATCH 158/280] Add itertool recipe for polynomial evaluation.
(GH-102852)
---
Doc/library/itertools.rst | 40 +++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index d85a17effb04a2..9364f72ca45610 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -866,6 +866,15 @@ which incur interpreter overhead.
window.append(x)
yield math.sumprod(kernel, window)
+ def polynomial_eval(coefficients, x):
+ "Evaluate a polynomial at a specific value."
+ # polynomial_eval([1, -4, -17, 60], x=2.5) --> 8.125 x³ -4x² -17x + 60
+ n = len(coefficients)
+ if n == 0:
+ return x * 0 # coerce zero to the type of x
+ powers = list(accumulate(repeat(x, n - 1), operator.mul, initial=1))
+ return math.sumprod(coefficients, reversed(powers))
+
def polynomial_from_roots(roots):
"""Compute a polynomial's coefficients from its roots.
@@ -1245,6 +1254,37 @@ which incur interpreter overhead.
>>> list(convolve(data, [1, -2, 1]))
[20, 0, -36, 24, -20, 20, -20, -4, 16]
+ >>> from fractions import Fraction
+ >>> from decimal import Decimal
+ >>> polynomial_eval([1, -4, -17, 60], x=2)
+ 18
+ >>> x = 2; x**3 - 4*x**2 -17*x + 60
+ 18
+ >>> polynomial_eval([1, -4, -17, 60], x=2.5)
+ 8.125
+ >>> x = 2.5; x**3 - 4*x**2 -17*x + 60
+ 8.125
+ >>> polynomial_eval([1, -4, -17, 60], x=Fraction(2, 3))
+ Fraction(1274, 27)
+ >>> x = Fraction(2, 3); x**3 - 4*x**2 -17*x + 60
+ Fraction(1274, 27)
+ >>> polynomial_eval([1, -4, -17, 60], x=Decimal('1.75'))
+ Decimal('23.359375')
+ >>> x = Decimal('1.75'); x**3 - 4*x**2 -17*x + 60
+ Decimal('23.359375')
+ >>> polynomial_eval([], 2)
+ 0
+ >>> polynomial_eval([], 2.5)
+ 0.0
+ >>> polynomial_eval([], Fraction(2, 3))
+ Fraction(0, 1)
+ >>> polynomial_eval([], Decimal('1.75'))
+ Decimal('0.00')
+ >>> polynomial_eval([11], 7) == 11
+ True
+ >>> polynomial_eval([11, 2], 7) == 11 * 7 + 2
+ True
+
>>> polynomial_from_roots([5, -4, 3])
[1, -4, -17, 60]
>>> factored = lambda x: (x - 5) * (x + 4) * (x - 3)
From 1886084724a9f885cc448e2ef4f793c88630774f Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Tue, 21 Mar 2023 02:30:46 +0300
Subject: [PATCH 159/280] gh-102809: Remove `Misc/gdbinit` (#102854)
Looks like the consensus is that we don't need this file anymore.
Old version can be always found here: https://github.com/python/cpython/blob/094cf392f49d3c16fe798863717f6c8e0f3734bb/Misc/gdbinit
---
...-03-21-01-27-07.gh-issue-102809.2F1Byz.rst | 1 +
Misc/gdbinit | 176 ------------------
2 files changed, 1 insertion(+), 176 deletions(-)
create mode 100644 Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-102809.2F1Byz.rst
delete mode 100644 Misc/gdbinit
diff --git a/Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-102809.2F1Byz.rst b/Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-102809.2F1Byz.rst
new file mode 100644
index 00000000000000..5c282769878563
--- /dev/null
+++ b/Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-102809.2F1Byz.rst
@@ -0,0 +1 @@
+``Misc/gdbinit`` was removed.
diff --git a/Misc/gdbinit b/Misc/gdbinit
deleted file mode 100644
index e8f62ba6476423..00000000000000
--- a/Misc/gdbinit
+++ /dev/null
@@ -1,176 +0,0 @@
-# If you use the GNU debugger gdb to debug the Python C runtime, you
-# might find some of the following commands useful. Copy this to your
-# ~/.gdbinit file and it'll get loaded into gdb automatically when you
-# start it up. Then, at the gdb prompt you can do things like:
-#
-# (gdb) pyo apyobjectptr
-#
-# refcounts: 1
-# address : 84a7a2c
-# $1 = void
-# (gdb)
-#
-# NOTE: If you have gdb 7 or later, it supports debugging of Python directly
-# with embedded macros that you may find superior to what is in here.
-# See Tools/gdb/libpython.py and http://bugs.python.org/issue8032.
-
-define pyo
- # side effect of calling _PyObject_Dump is to dump the object's
- # info - assigning just prevents gdb from printing the
- # NULL return value
- set $_unused_void = _PyObject_Dump($arg0)
-end
-document pyo
- Prints a representation of the object to stderr, along with the
- number of reference counts it currently has and the hex address the
- object is allocated at. The argument must be a PyObject*
-end
-
-define pyg
- print _PyGC_Dump($arg0)
-end
-document pyg
- Prints a representation of the object to stderr, along with the
- number of reference counts it currently has and the hex address the
- object is allocated at. The argument must be a PyGC_Head*
-end
-
-define pylocals
- set $_i = 0
- while $_i < f->f_code->co_nlocals
- if f->f_localsplus + $_i != 0
- set $_names = f->f_code->co_varnames
- set $_name = PyUnicode_AsUTF8(PyTuple_GetItem($_names, $_i))
- printf "%s:\n", $_name
- pyo f->f_localsplus[$_i]
- end
- set $_i = $_i + 1
- end
-end
-document pylocals
- Print the local variables of the current frame.
-end
-
-# A rewrite of the Python interpreter's line number calculator in GDB's
-# command language
-define lineno
- set $__continue = 1
- set $__co = f->f_code
- set $__lasti = f->f_lasti
- set $__sz = ((PyVarObject *)$__co->co_lnotab)->ob_size/2
- set $__p = (unsigned char *)((PyBytesObject *)$__co->co_lnotab)->ob_sval
- set $__li = $__co->co_firstlineno
- set $__ad = 0
- while ($__sz-1 >= 0 && $__continue)
- set $__sz = $__sz - 1
- set $__ad = $__ad + *$__p
- set $__p = $__p + 1
- if ($__ad > $__lasti)
- set $__continue = 0
- else
- set $__li = $__li + *$__p
- set $__p = $__p + 1
- end
- end
- printf "%d", $__li
-end
-
-define pyframev
- pyframe
- pylocals
-end
-document pyframev
- Print the current frame - verbose
-end
-
-define pyframe
- set $__fn = PyUnicode_AsUTF8(f->f_code->co_filename)
- set $__n = PyUnicode_AsUTF8(f->f_code->co_name)
- printf "%s (", $__fn
- lineno
- printf "): %s\n", $__n
-### Uncomment these lines when using from within Emacs/XEmacs so it will
-### automatically track/display the current Python source line
-# printf "%c%c%s:", 032, 032, $__fn
-# lineno
-# printf ":1\n"
-end
-
-### Use these at your own risk. It appears that a bug in gdb causes it
-### to crash in certain circumstances.
-
-#define up
-# up-silently 1
-# printframe
-#end
-
-#define down
-# down-silently 1
-# printframe
-#end
-
-define printframe
- if $pc > PyEval_EvalFrameEx && $pc < _PyEval_EvalFrameDefault
- pyframe
- else
- frame
- end
-end
-
-# Here's a somewhat fragile way to print the entire Python stack from gdb.
-# It's fragile because the tests for the value of $pc depend on the layout
-# of specific functions in the C source code.
-
-# Explanation of while and if tests: We want to pop up the stack until we
-# land in Py_Main (this is probably an incorrect assumption in an embedded
-# interpreter, but the test can be extended by an interested party). If
-# Py_Main <= $pc <= Py_GetArgcArv is true, $pc is in Py_Main(), so the while
-# tests succeeds as long as it's not true. In a similar fashion the if
-# statement tests to see if we are in PyEval_EvalFrameEx().
-
-# Note: The name of the main interpreter function and the function which
-# follow it has changed over time. This version of pystack works with this
-# version of Python. If you try using it with older or newer versions of
-# the interpreter you may will have to change the functions you compare with
-# $pc.
-
-define pystack
- while $pc < Py_Main || $pc > Py_GetArgcArgv
- if $pc > PyEval_EvalFrameEx && $pc < _PyEval_EvalFrameDefault
- pyframe
- end
- up-silently 1
- end
- select-frame 0
-end
-document pystack
- Print the entire Python call stack
-end
-
-define pystackv
- while $pc < Py_Main || $pc > Py_GetArgcArgv
- if $pc > PyEval_EvalFrameEx && $pc < _PyEval_EvalFrameDefault
- pyframev
- end
- up-silently 1
- end
- select-frame 0
-end
-document pystackv
- Print the entire Python call stack - verbose mode
-end
-
-define pu
- set $uni = $arg0
- set $i = 0
- while (*$uni && $i++<100)
- if (*$uni < 0x80)
- print *(char*)$uni++
- else
- print /x *(short*)$uni++
- end
- end
-end
-document pu
- Generally useful macro to print a Unicode string
-end
From 5226688568cd476959b7c58375dff387058d6546 Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Mon, 20 Mar 2023 20:40:04 -0500
Subject: [PATCH 160/280] Remove itermediate list. Expand docstring.
(GH-102862)
---
Doc/library/itertools.rst | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 9364f72ca45610..2427a8d85f841c 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -867,13 +867,17 @@ which incur interpreter overhead.
yield math.sumprod(kernel, window)
def polynomial_eval(coefficients, x):
- "Evaluate a polynomial at a specific value."
- # polynomial_eval([1, -4, -17, 60], x=2.5) --> 8.125 x³ -4x² -17x + 60
+ """Evaluate a polynomial at a specific value.
+
+ Computes with better numeric stability than Horner's method.
+ """
+ # Evaluate x³ -4x² -17x + 60 at x = 2.5
+ # polynomial_eval([1, -4, -17, 60], x=2.5) --> 8.125
n = len(coefficients)
if n == 0:
return x * 0 # coerce zero to the type of x
- powers = list(accumulate(repeat(x, n - 1), operator.mul, initial=1))
- return math.sumprod(coefficients, reversed(powers))
+ powers = accumulate(repeat(x, n - 1), operator.mul, initial=1)
+ return math.sumprod(reversed(coefficients), powers)
def polynomial_from_roots(roots):
"""Compute a polynomial's coefficients from its roots.
From f357fc76caa2d9d781e57679907eea8fe7d32ccd Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Tue, 21 Mar 2023 00:02:14 -0500
Subject: [PATCH 161/280] The pow() variant further improves accuracy
(GH-102866)
---
Doc/library/itertools.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 2427a8d85f841c..78f64ea67e2542 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -876,7 +876,7 @@ which incur interpreter overhead.
n = len(coefficients)
if n == 0:
return x * 0 # coerce zero to the type of x
- powers = accumulate(repeat(x, n - 1), operator.mul, initial=1)
+ powers = map(pow, repeat(x), range(n))
return math.sumprod(reversed(coefficients), powers)
def polynomial_from_roots(roots):
From af512cc383bb32a6ec815ba43e8d9a63cdfd1641 Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Tue, 21 Mar 2023 10:47:15 +0300
Subject: [PATCH 162/280] gh-102598: Remove obsolete optimization from
`FORMAT_VALUE` opcode (#102599)
---
Python/bytecodes.c | 18 ++++--------------
Python/generated_cases.c.h | 19 ++++---------------
2 files changed, 8 insertions(+), 29 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index c8ffd89c51a22c..92fa7664e00452 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3254,20 +3254,10 @@ dummy_func(
value = result;
}
- /* If value is a unicode object, and there's no fmt_spec,
- then we know the result of format(value) is value
- itself. In that case, skip calling format(). I plan to
- move this optimization in to PyObject_Format()
- itself. */
- if (PyUnicode_CheckExact(value) && fmt_spec == NULL) {
- /* Do nothing, just transfer ownership to result. */
- result = value;
- } else {
- /* Actually call format(). */
- result = PyObject_Format(value, fmt_spec);
- DECREF_INPUTS();
- ERROR_IF(result == NULL, error);
- }
+ result = PyObject_Format(value, fmt_spec);
+ Py_DECREF(value);
+ Py_XDECREF(fmt_spec);
+ ERROR_IF(result == NULL, error);
}
inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top: *bottom)) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 2c6388390b51ad..9df173f843f806 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4118,21 +4118,10 @@
value = result;
}
- /* If value is a unicode object, and there's no fmt_spec,
- then we know the result of format(value) is value
- itself. In that case, skip calling format(). I plan to
- move this optimization in to PyObject_Format()
- itself. */
- if (PyUnicode_CheckExact(value) && fmt_spec == NULL) {
- /* Do nothing, just transfer ownership to result. */
- result = value;
- } else {
- /* Actually call format(). */
- result = PyObject_Format(value, fmt_spec);
- Py_DECREF(value);
- Py_XDECREF(fmt_spec);
- if (result == NULL) { STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); goto pop_1_error; }
- }
+ result = PyObject_Format(value, fmt_spec);
+ Py_DECREF(value);
+ Py_XDECREF(fmt_spec);
+ if (result == NULL) { STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0)); goto pop_1_error; }
STACK_SHRINK((((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0));
stack_pointer[-1] = result;
DISPATCH();
From 45e8c3e8b6401d498f8bf91503235c6184913c3d Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Tue, 21 Mar 2023 09:36:18 +0000
Subject: [PATCH 163/280] gh-102755: PyErr_DisplayException only in ABI >=
3.12. Tests cover PyErr_Display as well (GH-102849)
---
Include/pythonrun.h | 3 +++
Lib/test/test_traceback.py | 16 +++++++++++++++-
Modules/_testcapi/exceptions.c | 16 +++++++++++++---
3 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/Include/pythonrun.h b/Include/pythonrun.h
index 41d82e89f84876..154c7450cb934f 100644
--- a/Include/pythonrun.h
+++ b/Include/pythonrun.h
@@ -12,7 +12,10 @@ PyAPI_FUNC(PyObject *) Py_CompileString(const char *, const char *, int);
PyAPI_FUNC(void) PyErr_Print(void);
PyAPI_FUNC(void) PyErr_PrintEx(int);
PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *);
+
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000
PyAPI_FUNC(void) PyErr_DisplayException(PyObject *);
+#endif
/* Stuff with no proper home (yet) */
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 1c5d1ab82c8e9c..399c59f8780d8e 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -394,6 +394,8 @@ def get_exception(self, callable, slice_start=0, slice_end=-1):
class CAPIExceptionFormattingMixin:
+ LEGACY = 0
+
def get_exception(self, callable, slice_start=0, slice_end=-1):
from _testcapi import exception_print
try:
@@ -401,11 +403,13 @@ def get_exception(self, callable, slice_start=0, slice_end=-1):
self.fail("No exception thrown.")
except Exception as e:
with captured_output("stderr") as tbstderr:
- exception_print(e)
+ exception_print(e, self.LEGACY)
return tbstderr.getvalue().splitlines()[slice_start:slice_end]
callable_line = get_exception.__code__.co_firstlineno + 3
+class CAPIExceptionFormattingLegacyMixin(CAPIExceptionFormattingMixin):
+ LEGACY = 1
@requires_debug_ranges()
class TracebackErrorLocationCaretTestBase:
@@ -912,6 +916,16 @@ class CPythonTracebackErrorCaretTests(
Same set of tests as above but with Python's internal traceback printing.
"""
+@cpython_only
+@requires_debug_ranges()
+class CPythonTracebackErrorCaretTests(
+ CAPIExceptionFormattingLegacyMixin,
+ TracebackErrorLocationCaretTestBase,
+ unittest.TestCase,
+):
+ """
+ Same set of tests as above but with Python's legacy internal traceback printing.
+ """
class TracebackFormatTests(unittest.TestCase):
diff --git a/Modules/_testcapi/exceptions.c b/Modules/_testcapi/exceptions.c
index 1922ca3beb7916..6099f7d20eb56a 100644
--- a/Modules/_testcapi/exceptions.c
+++ b/Modules/_testcapi/exceptions.c
@@ -40,12 +40,22 @@ static PyObject *
exception_print(PyObject *self, PyObject *args)
{
PyObject *exc;
+ int legacy = 0;
- if (!PyArg_ParseTuple(args, "O:exception_print", &exc)) {
+ if (!PyArg_ParseTuple(args, "O|i:exception_print", &exc, &legacy)) {
return NULL;
}
-
- PyErr_DisplayException(exc);
+ if (legacy) {
+ PyObject *tb = NULL;
+ if (PyExceptionInstance_Check(exc)) {
+ tb = PyException_GetTraceback(exc);
+ }
+ PyErr_Display((PyObject *) Py_TYPE(exc), exc, tb);
+ Py_XDECREF(tb);
+ }
+ else {
+ PyErr_DisplayException(exc);
+ }
Py_RETURN_NONE;
}
From 52172d3f9b7ee8c2c2e6a4c58a5c39762d7e0212 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Tue, 21 Mar 2023 11:07:03 +0000
Subject: [PATCH 164/280] gh-102799: Let pydoc use the exception instead of
sys.exc_info (#102830)
---
Lib/pydoc.py | 36 ++++++++++++++++++++++--------------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index 0a693f45230c93..78d8fd5357f72a 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -389,8 +389,17 @@ def synopsis(filename, cache={}):
class ErrorDuringImport(Exception):
"""Errors that occurred while trying to import something to document it."""
def __init__(self, filename, exc_info):
+ if not isinstance(exc_info, tuple):
+ assert isinstance(exc_info, BaseException)
+ self.exc = type(exc_info)
+ self.value = exc_info
+ self.tb = exc_info.__traceback__
+ else:
+ warnings.warn("A tuple value for exc_info is deprecated, use an exception instance",
+ DeprecationWarning)
+
+ self.exc, self.value, self.tb = exc_info
self.filename = filename
- self.exc, self.value, self.tb = exc_info
def __str__(self):
exc = self.exc.__name__
@@ -411,8 +420,8 @@ def importfile(path):
spec = importlib.util.spec_from_file_location(name, path, loader=loader)
try:
return importlib._bootstrap._load(spec)
- except:
- raise ErrorDuringImport(path, sys.exc_info())
+ except BaseException as err:
+ raise ErrorDuringImport(path, err)
def safeimport(path, forceload=0, cache={}):
"""Import a module; handle errors; return None if the module isn't found.
@@ -440,21 +449,20 @@ def safeimport(path, forceload=0, cache={}):
cache[key] = sys.modules[key]
del sys.modules[key]
module = __import__(path)
- except:
+ except BaseException as err:
# Did the error occur before or after the module was found?
- (exc, value, tb) = info = sys.exc_info()
if path in sys.modules:
# An error occurred while executing the imported module.
- raise ErrorDuringImport(sys.modules[path].__file__, info)
- elif exc is SyntaxError:
+ raise ErrorDuringImport(sys.modules[path].__file__, err)
+ elif type(err) is SyntaxError:
# A SyntaxError occurred before we could execute the module.
- raise ErrorDuringImport(value.filename, info)
- elif issubclass(exc, ImportError) and value.name == path:
+ raise ErrorDuringImport(err.filename, err)
+ elif isinstance(err, ImportError) and err.name == path:
# No such module in the path.
return None
else:
# Some other error occurred during the importing process.
- raise ErrorDuringImport(path, sys.exc_info())
+ raise ErrorDuringImport(path, err)
for part in path.split('.')[1:]:
try: module = getattr(module, part)
except AttributeError: return None
@@ -1997,8 +2005,8 @@ def __call__(self, request=_GoInteractive):
if request is not self._GoInteractive:
try:
self.help(request)
- except ImportError as e:
- self.output.write(f'{e}\n')
+ except ImportError as err:
+ self.output.write(f'{err}\n')
else:
self.intro()
self.interact()
@@ -2405,8 +2413,8 @@ def run(self):
docsvr = DocServer(self.host, self.port, self.ready)
self.docserver = docsvr
docsvr.serve_until_quit()
- except Exception as e:
- self.error = e
+ except Exception as err:
+ self.error = err
def ready(self, server):
self.serving = True
From 6776fb14bf93cb7302b1a12e639d94cb17edd471 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Tue, 21 Mar 2023 11:08:46 +0000
Subject: [PATCH 165/280] gh-102828: emit deprecation warning for onerror arg
to shutil.rmtree (#102850)
---
Doc/whatsnew/3.12.rst | 10 +++++++---
Lib/shutil.py | 6 ++++++
Lib/tempfile.py | 8 ++++----
Lib/test/test_shutil.py | 16 +++++++++++-----
4 files changed, 28 insertions(+), 12 deletions(-)
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index af52c7cb670c61..b7c956d7f78456 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -339,7 +339,8 @@ shutil
* :func:`shutil.rmtree` now accepts a new argument *onexc* which is an
error handler like *onerror* but which expects an exception instance
- rather than a *(typ, val, tb)* triplet. *onerror* is deprecated.
+ rather than a *(typ, val, tb)* triplet. *onerror* is deprecated and
+ will be removed in Python 3.14.
(Contributed by Irit Katriel in :gh:`102828`.)
@@ -503,8 +504,8 @@ Deprecated
fields are deprecated. Use :data:`sys.last_exc` instead.
(Contributed by Irit Katriel in :gh:`102778`.)
-* The *onerror* argument of :func:`shutil.rmtree` is deprecated. Use *onexc*
- instead. (Contributed by Irit Katriel in :gh:`102828`.)
+* The *onerror* argument of :func:`shutil.rmtree` is deprecated as will be removed
+ in Python 3.14. Use *onexc* instead. (Contributed by Irit Katriel in :gh:`102828`.)
Pending Removal in Python 3.13
@@ -586,6 +587,9 @@ Pending Removal in Python 3.14
functions that have been deprecated since Python 2 but only gained a
proper :exc:`DeprecationWarning` in 3.12. Remove them in 3.14.
+* The *onerror* argument of :func:`shutil.rmtree` is deprecated in 3.12,
+ and will be removed in 3.14.
+
Pending Removal in Future Versions
----------------------------------
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 89a7ac72d98357..b0576407e02ffb 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -10,6 +10,7 @@
import fnmatch
import collections
import errno
+import warnings
try:
import zlib
@@ -692,6 +693,11 @@ def rmtree(path, ignore_errors=False, onerror=None, *, onexc=None, dir_fd=None):
onerror is deprecated and only remains for backwards compatibility.
If both onerror and onexc are set, onerror is ignored and onexc is used.
"""
+
+ if onerror is not None:
+ warnings.warn("onerror argument is deprecated, use onexc instead",
+ DeprecationWarning)
+
sys.audit("shutil.rmtree", path, dir_fd)
if ignore_errors:
def onexc(*args):
diff --git a/Lib/tempfile.py b/Lib/tempfile.py
index bb18d60db0d919..cf06092f826bcc 100644
--- a/Lib/tempfile.py
+++ b/Lib/tempfile.py
@@ -864,8 +864,8 @@ def __init__(self, suffix=None, prefix=None, dir=None,
@classmethod
def _rmtree(cls, name, ignore_errors=False):
- def onerror(func, path, exc_info):
- if issubclass(exc_info[0], PermissionError):
+ def onexc(func, path, exc):
+ if isinstance(exc, PermissionError):
def resetperms(path):
try:
_os.chflags(path, 0)
@@ -885,13 +885,13 @@ def resetperms(path):
cls._rmtree(path, ignore_errors=ignore_errors)
except FileNotFoundError:
pass
- elif issubclass(exc_info[0], FileNotFoundError):
+ elif isinstance(exc, FileNotFoundError):
pass
else:
if not ignore_errors:
raise
- _shutil.rmtree(name, onerror=onerror)
+ _shutil.rmtree(name, onexc=onexc)
@classmethod
def _cleanup(cls, name, warn_message, ignore_errors=False):
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 89d65af3bc5b2b..1c0589ced9ea89 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -23,6 +23,7 @@
unregister_unpack_format, get_unpack_formats,
SameFileError, _GiveupOnFastCopy)
import tarfile
+import warnings
import zipfile
try:
import posix
@@ -207,7 +208,8 @@ def test_rmtree_fails_on_symlink_onerror(self):
errors = []
def onerror(*args):
errors.append(args)
- shutil.rmtree(link, onerror=onerror)
+ with self.assertWarns(DeprecationWarning):
+ shutil.rmtree(link, onerror=onerror)
self.assertEqual(len(errors), 1)
self.assertIs(errors[0][0], os.path.islink)
self.assertEqual(errors[0][1], link)
@@ -268,7 +270,8 @@ def test_rmtree_fails_on_junctions_onerror(self):
errors = []
def onerror(*args):
errors.append(args)
- shutil.rmtree(link, onerror=onerror)
+ with self.assertWarns(DeprecationWarning):
+ shutil.rmtree(link, onerror=onerror)
self.assertEqual(len(errors), 1)
self.assertIs(errors[0][0], os.path.islink)
self.assertEqual(errors[0][1], link)
@@ -337,7 +340,8 @@ def test_rmtree_errors_onerror(self):
errors = []
def onerror(*args):
errors.append(args)
- shutil.rmtree(filename, onerror=onerror)
+ with self.assertWarns(DeprecationWarning):
+ shutil.rmtree(filename, onerror=onerror)
self.assertEqual(len(errors), 2)
self.assertIs(errors[0][0], os.scandir)
self.assertEqual(errors[0][1], filename)
@@ -406,7 +410,8 @@ def test_on_error(self):
self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
- shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
+ with self.assertWarns(DeprecationWarning):
+ shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror)
# Test whether onerror has actually been called.
self.assertEqual(self.errorState, 3,
"Expected call to onerror function did not happen.")
@@ -532,7 +537,8 @@ def onexc(*args):
self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode)
self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode)
- shutil.rmtree(TESTFN, onerror=onerror, onexc=onexc)
+ with self.assertWarns(DeprecationWarning):
+ shutil.rmtree(TESTFN, onerror=onerror, onexc=onexc)
self.assertTrue(onexc_called)
self.assertFalse(onerror_called)
From 05078fdbe35ce2f3e6a808a12919ed9688633be6 Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Tue, 21 Mar 2023 19:46:24 +0300
Subject: [PATCH 166/280] gh-102595: Document `PyObject_Format` c-api function
(GH-102596)
Def: https://github.com/python/cpython/blame/5ffdaf748d98da6065158534720f1996a45a0072/Include/abstract.h#L389
---
Doc/c-api/object.rst | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst
index 84c72e7e108b64..0a12bb9e8c54f0 100644
--- a/Doc/c-api/object.rst
+++ b/Doc/c-api/object.rst
@@ -179,6 +179,15 @@ Object Protocol
If *o1* and *o2* are the same object, :c:func:`PyObject_RichCompareBool`
will always return ``1`` for :const:`Py_EQ` and ``0`` for :const:`Py_NE`.
+.. c:function:: PyObject* PyObject_Format(PyObject *obj, PyObject *format_spec)
+
+ Format *obj* using *format_spec*. This is equivalent to the Python
+ expression ``format(obj, format_spec)``.
+
+ *format_spec* may be ``NULL``. In this case the call is equivalent
+ to ``format(obj)``.
+ Returns the formatted string on success, ``NULL`` on failure.
+
.. c:function:: PyObject* PyObject_Repr(PyObject *o)
.. index:: builtin: repr
From 8eb9d2ff6bb77f8455ca8585888d0e187fb66afd Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Tue, 21 Mar 2023 10:49:12 -0600
Subject: [PATCH 167/280] gh-98608: Stop Treating All Errors from
_Py_NewInterpreterFromConfig() as Fatal (gh-102657)
Prior to this change, errors in _Py_NewInterpreterFromConfig() were always fatal. Instead, callers should be able to handle such errors and keep going. That's what this change supports. (This was an oversight in the original implementation of _Py_NewInterpreterFromConfig().) Note that the existing [fatal] behavior of the public Py_NewInterpreter() is preserved.
https://github.com/python/cpython/issues/98608
---
Include/cpython/initconfig.h | 1 +
Include/cpython/pylifecycle.h | 5 +++--
Include/internal/pycore_initconfig.h | 2 --
Modules/_testcapimodule.c | 8 ++++++--
Modules/_xxsubinterpretersmodule.c | 9 +++++++--
Python/pylifecycle.c | 19 ++++++++++---------
6 files changed, 27 insertions(+), 17 deletions(-)
diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h
index a070fa9ff3a038..8bc681b1a93f5c 100644
--- a/Include/cpython/initconfig.h
+++ b/Include/cpython/initconfig.h
@@ -25,6 +25,7 @@ PyAPI_FUNC(PyStatus) PyStatus_Exit(int exitcode);
PyAPI_FUNC(int) PyStatus_IsError(PyStatus err);
PyAPI_FUNC(int) PyStatus_IsExit(PyStatus err);
PyAPI_FUNC(int) PyStatus_Exception(PyStatus err);
+PyAPI_FUNC(PyObject *) _PyErr_SetFromPyStatus(PyStatus status);
/* --- PyWideStringList ------------------------------------------------ */
diff --git a/Include/cpython/pylifecycle.h b/Include/cpython/pylifecycle.h
index e1f83acbffc360..821b169d7a1759 100644
--- a/Include/cpython/pylifecycle.h
+++ b/Include/cpython/pylifecycle.h
@@ -62,5 +62,6 @@ PyAPI_FUNC(int) _Py_CoerceLegacyLocale(int warn);
PyAPI_FUNC(int) _Py_LegacyLocaleDetected(int warn);
PyAPI_FUNC(char *) _Py_SetLocaleFromEnv(int category);
-PyAPI_FUNC(PyThreadState *) _Py_NewInterpreterFromConfig(
- const _PyInterpreterConfig *);
+PyAPI_FUNC(PyStatus) _Py_NewInterpreterFromConfig(
+ PyThreadState **tstate_p,
+ const _PyInterpreterConfig *config);
diff --git a/Include/internal/pycore_initconfig.h b/Include/internal/pycore_initconfig.h
index 69f88d7d1d46b8..4cbd14a61d4545 100644
--- a/Include/internal/pycore_initconfig.h
+++ b/Include/internal/pycore_initconfig.h
@@ -44,8 +44,6 @@ struct pyruntimestate;
#define _PyStatus_UPDATE_FUNC(err) \
do { (err).func = _PyStatus_GET_FUNC(); } while (0)
-PyObject* _PyErr_SetFromPyStatus(PyStatus status);
-
/* --- PyWideStringList ------------------------------------------------ */
#define _PyWideStringList_INIT (PyWideStringList){.length = 0, .items = NULL}
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index f45d0312e94411..e2ebab5c5b4849 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -1538,15 +1538,19 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
.allow_daemon_threads = allow_daemon_threads,
.check_multi_interp_extensions = check_multi_interp_extensions,
};
- substate = _Py_NewInterpreterFromConfig(&config);
- if (substate == NULL) {
+ PyStatus status = _Py_NewInterpreterFromConfig(&substate, &config);
+ if (PyStatus_Exception(status)) {
/* Since no new thread state was created, there is no exception to
propagate; raise a fresh one after swapping in the old thread
state. */
PyThreadState_Swap(mainstate);
+ _PyErr_SetFromPyStatus(status);
+ PyObject *exc = PyErr_GetRaisedException();
PyErr_SetString(PyExc_RuntimeError, "sub-interpreter creation failed");
+ _PyErr_ChainExceptions1(exc);
return NULL;
}
+ assert(substate != NULL);
r = PyRun_SimpleStringFlags(code, &cflags);
Py_EndInterpreter(substate);
diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c
index 76fb87fa3a34e1..9648f080cd756c 100644
--- a/Modules/_xxsubinterpretersmodule.c
+++ b/Modules/_xxsubinterpretersmodule.c
@@ -526,15 +526,20 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds)
? (_PyInterpreterConfig)_PyInterpreterConfig_INIT
: (_PyInterpreterConfig)_PyInterpreterConfig_LEGACY_INIT;
// XXX Possible GILState issues?
- PyThreadState *tstate = _Py_NewInterpreterFromConfig(&config);
+ PyThreadState *tstate = NULL;
+ PyStatus status = _Py_NewInterpreterFromConfig(&tstate, &config);
PyThreadState_Swap(save_tstate);
- if (tstate == NULL) {
+ if (PyStatus_Exception(status)) {
/* Since no new thread state was created, there is no exception to
propagate; raise a fresh one after swapping in the old thread
state. */
+ _PyErr_SetFromPyStatus(status);
+ PyObject *exc = PyErr_GetRaisedException();
PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed");
+ _PyErr_ChainExceptions1(exc);
return NULL;
}
+ assert(tstate != NULL);
PyInterpreterState *interp = PyThreadState_GetInterpreter(tstate);
PyObject *idobj = _PyInterpreterState_GetIDObject(interp);
if (idobj == NULL) {
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 731f340001b4e0..8b58a14c693f22 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -2065,22 +2065,23 @@ new_interpreter(PyThreadState **tstate_p, const _PyInterpreterConfig *config)
return status;
}
-PyThreadState *
-_Py_NewInterpreterFromConfig(const _PyInterpreterConfig *config)
+PyStatus
+_Py_NewInterpreterFromConfig(PyThreadState **tstate_p,
+ const _PyInterpreterConfig *config)
{
- PyThreadState *tstate = NULL;
- PyStatus status = new_interpreter(&tstate, config);
- if (_PyStatus_EXCEPTION(status)) {
- Py_ExitStatusException(status);
- }
- return tstate;
+ return new_interpreter(tstate_p, config);
}
PyThreadState *
Py_NewInterpreter(void)
{
+ PyThreadState *tstate = NULL;
const _PyInterpreterConfig config = _PyInterpreterConfig_LEGACY_INIT;
- return _Py_NewInterpreterFromConfig(&config);
+ PyStatus status = _Py_NewInterpreterFromConfig(&tstate, &config);
+ if (_PyStatus_EXCEPTION(status)) {
+ Py_ExitStatusException(status);
+ }
+ return tstate;
}
/* Delete an interpreter and its last thread. This requires that the
From 70e542b3f0136046e6e4bc31cf777555e85db858 Mon Sep 17 00:00:00 2001
From: wim glenn
Date: Tue, 21 Mar 2023 12:06:18 -0500
Subject: [PATCH 168/280] gh-102876: remove superfluous parens from
itertools.batched recipe (GH-102877)
remove superfluous parens from itertools.batched recipe
---
Doc/library/itertools.rst | 2 +-
Lib/test/test_itertools.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 78f64ea67e2542..38bc369d410dda 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -195,7 +195,7 @@ loops that truncate the stream.
if n < 1:
raise ValueError('n must be at least one')
it = iter(iterable)
- while (batch := tuple(islice(it, n))):
+ while batch := tuple(islice(it, n)):
yield batch
.. versionadded:: 3.12
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index 7014bc97100cb4..9fe559d4b7eed5 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -1846,7 +1846,7 @@ def batched_recipe(iterable, n):
if n < 1:
raise ValueError('n must be at least one')
it = iter(iterable)
- while (batch := tuple(islice(it, n))):
+ while batch := tuple(islice(it, n)):
yield batch
for iterable, n in product(
From 4ae6f699179082e632732a038a0e23a9122597d8 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Tue, 21 Mar 2023 11:46:09 -0600
Subject: [PATCH 169/280] gh-102304: Move the Total Refcount to
PyInterpreterState (gh-102545)
Moving it valuable with a per-interpreter GIL. However, it is also useful without one, since it allows us to identify refleaks within a single interpreter or where references are escaping an interpreter. This becomes more important as we move the obmalloc state to PyInterpreterState.
https://github.com/python/cpython/issues/102304
---
Include/cpython/object.h | 1 +
Include/internal/pycore_interp.h | 2 +
Include/internal/pycore_object.h | 16 +++---
Include/internal/pycore_object_state.h | 8 +++
Objects/bytesobject.c | 2 +-
Objects/dictobject.c | 10 ++--
Objects/object.c | 79 +++++++++++++++++++-------
Objects/structseq.c | 2 +-
Objects/tupleobject.c | 2 +-
Objects/typeobject.c | 18 +++++-
Python/pylifecycle.c | 5 ++
Python/pystate.c | 10 +++-
Python/sysmodule.c | 2 +
13 files changed, 117 insertions(+), 40 deletions(-)
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index 0438612edd1dfe..859ffb91e223dc 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -15,6 +15,7 @@ PyAPI_FUNC(void) _Py_ForgetReference(PyObject *);
PyAPI_FUNC(Py_ssize_t) _Py_GetGlobalRefTotal(void);
# define _Py_GetRefTotal() _Py_GetGlobalRefTotal()
PyAPI_FUNC(Py_ssize_t) _Py_GetLegacyRefTotal(void);
+PyAPI_FUNC(Py_ssize_t) _PyInterpreterState_GetRefTotal(PyInterpreterState *);
#endif
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index fd7b92e86aa120..fcdf8d2f2c8e15 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -25,6 +25,7 @@ extern "C" {
#include "pycore_import.h" // struct _import_state
#include "pycore_list.h" // struct _Py_list_state
#include "pycore_global_objects.h" // struct _Py_interp_static_objects
+#include "pycore_object_state.h" // struct _py_object_state
#include "pycore_tuple.h" // struct _Py_tuple_state
#include "pycore_typeobject.h" // struct type_cache
#include "pycore_unicodeobject.h" // struct _Py_unicode_state
@@ -138,6 +139,7 @@ struct _is {
// One bit is set for each non-NULL entry in code_watchers
uint8_t active_code_watchers;
+ struct _py_object_state object_state;
struct _Py_unicode_state unicode;
struct _Py_float_state float_state;
struct _Py_long_state long_state;
diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index b985eff8a8a08b..d6bbafd4b6cccc 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -43,18 +43,19 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
built against the pre-3.12 stable ABI. */
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
-extern void _Py_AddRefTotal(Py_ssize_t);
-extern void _Py_IncRefTotal(void);
-extern void _Py_DecRefTotal(void);
+extern void _Py_AddRefTotal(PyInterpreterState *, Py_ssize_t);
+extern void _Py_IncRefTotal(PyInterpreterState *);
+extern void _Py_DecRefTotal(PyInterpreterState *);
-# define _Py_DEC_REFTOTAL() _PyRuntime.object_state.reftotal--
+# define _Py_DEC_REFTOTAL(interp) \
+ interp->object_state.reftotal--
#endif
// Increment reference count by n
static inline void _Py_RefcntAdd(PyObject* op, Py_ssize_t n)
{
#ifdef Py_REF_DEBUG
- _Py_AddRefTotal(n);
+ _Py_AddRefTotal(_PyInterpreterState_GET(), n);
#endif
op->ob_refcnt += n;
}
@@ -65,7 +66,7 @@ _Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
{
_Py_DECREF_STAT_INC();
#ifdef Py_REF_DEBUG
- _Py_DEC_REFTOTAL();
+ _Py_DEC_REFTOTAL(_PyInterpreterState_GET());
#endif
if (--op->ob_refcnt != 0) {
assert(op->ob_refcnt > 0);
@@ -83,7 +84,7 @@ _Py_DECREF_NO_DEALLOC(PyObject *op)
{
_Py_DECREF_STAT_INC();
#ifdef Py_REF_DEBUG
- _Py_DEC_REFTOTAL();
+ _Py_DEC_REFTOTAL(_PyInterpreterState_GET());
#endif
op->ob_refcnt--;
#ifdef Py_DEBUG
@@ -226,6 +227,7 @@ static inline void _PyObject_GC_UNTRACK(
#endif
#ifdef Py_REF_DEBUG
+extern void _PyInterpreterState_FinalizeRefTotal(PyInterpreterState *);
extern void _Py_FinalizeRefTotal(_PyRuntimeState *);
extern void _PyDebug_PrintTotalRefs(void);
#endif
diff --git a/Include/internal/pycore_object_state.h b/Include/internal/pycore_object_state.h
index 4e5862a11eddc5..94005d77881432 100644
--- a/Include/internal/pycore_object_state.h
+++ b/Include/internal/pycore_object_state.h
@@ -9,6 +9,14 @@ extern "C" {
#endif
struct _py_object_runtime_state {
+#ifdef Py_REF_DEBUG
+ Py_ssize_t interpreter_leaks;
+#else
+ int _not_used;
+#endif
+};
+
+struct _py_object_state {
#ifdef Py_REF_DEBUG
Py_ssize_t reftotal;
#else
diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c
index 687a654bdae137..2d8dab6f378006 100644
--- a/Objects/bytesobject.c
+++ b/Objects/bytesobject.c
@@ -3067,7 +3067,7 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
PyObject_Realloc(v, PyBytesObject_SIZE + newsize);
if (*pv == NULL) {
#ifdef Py_REF_DEBUG
- _Py_DecRefTotal();
+ _Py_DecRefTotal(_PyInterpreterState_GET());
#endif
PyObject_Free(v);
PyErr_NoMemory();
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 53f9a380346a0d..2ef520044340ee 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -304,7 +304,7 @@ static inline void
dictkeys_incref(PyDictKeysObject *dk)
{
#ifdef Py_REF_DEBUG
- _Py_IncRefTotal();
+ _Py_IncRefTotal(_PyInterpreterState_GET());
#endif
dk->dk_refcnt++;
}
@@ -314,7 +314,7 @@ dictkeys_decref(PyInterpreterState *interp, PyDictKeysObject *dk)
{
assert(dk->dk_refcnt > 0);
#ifdef Py_REF_DEBUG
- _Py_DecRefTotal();
+ _Py_DecRefTotal(_PyInterpreterState_GET());
#endif
if (--dk->dk_refcnt == 0) {
free_keys_object(interp, dk);
@@ -634,7 +634,7 @@ new_keys_object(PyInterpreterState *interp, uint8_t log2_size, bool unicode)
}
}
#ifdef Py_REF_DEBUG
- _Py_IncRefTotal();
+ _Py_IncRefTotal(_PyInterpreterState_GET());
#endif
dk->dk_refcnt = 1;
dk->dk_log2_size = log2_size;
@@ -824,7 +824,7 @@ clone_combined_dict_keys(PyDictObject *orig)
we have it now; calling dictkeys_incref would be an error as
keys->dk_refcnt is already set to 1 (after memcpy). */
#ifdef Py_REF_DEBUG
- _Py_IncRefTotal();
+ _Py_IncRefTotal(_PyInterpreterState_GET());
#endif
return keys;
}
@@ -1530,7 +1530,7 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
// We can not use free_keys_object here because key's reference
// are moved already.
#ifdef Py_REF_DEBUG
- _Py_DecRefTotal();
+ _Py_DecRefTotal(_PyInterpreterState_GET());
#endif
if (oldkeys == Py_EMPTY_KEYS) {
oldkeys->dk_refcnt--;
diff --git a/Objects/object.c b/Objects/object.c
index 95f7c966a414de..9dd5eb998217f6 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -66,25 +66,25 @@ get_legacy_reftotal(void)
#ifdef Py_REF_DEBUG
-# define REFTOTAL(runtime) \
- (runtime)->object_state.reftotal
+# define REFTOTAL(interp) \
+ interp->object_state.reftotal
static inline void
-reftotal_increment(_PyRuntimeState *runtime)
+reftotal_increment(PyInterpreterState *interp)
{
- REFTOTAL(runtime)++;
+ REFTOTAL(interp)++;
}
static inline void
-reftotal_decrement(_PyRuntimeState *runtime)
+reftotal_decrement(PyInterpreterState *interp)
{
- REFTOTAL(runtime)--;
+ REFTOTAL(interp)--;
}
static inline void
-reftotal_add(_PyRuntimeState *runtime, Py_ssize_t n)
+reftotal_add(PyInterpreterState *interp, Py_ssize_t n)
{
- REFTOTAL(runtime) += n;
+ REFTOTAL(interp) += n;
}
static inline Py_ssize_t get_global_reftotal(_PyRuntimeState *);
@@ -99,15 +99,43 @@ void
_Py_FinalizeRefTotal(_PyRuntimeState *runtime)
{
last_final_reftotal = get_global_reftotal(runtime);
- REFTOTAL(runtime) = 0;
+ runtime->object_state.interpreter_leaks = 0;
+}
+
+void
+_PyInterpreterState_FinalizeRefTotal(PyInterpreterState *interp)
+{
+ interp->runtime->object_state.interpreter_leaks += REFTOTAL(interp);
+ REFTOTAL(interp) = 0;
+}
+
+static inline Py_ssize_t
+get_reftotal(PyInterpreterState *interp)
+{
+ /* For a single interpreter, we ignore the legacy _Py_RefTotal,
+ since we can't determine which interpreter updated it. */
+ return REFTOTAL(interp);
}
static inline Py_ssize_t
get_global_reftotal(_PyRuntimeState *runtime)
{
- /* For an update from _Py_RefTotal first. */
- Py_ssize_t legacy = get_legacy_reftotal();
- return REFTOTAL(runtime) + legacy + last_final_reftotal;
+ Py_ssize_t total = 0;
+
+ /* Add up the total from each interpreter. */
+ HEAD_LOCK(&_PyRuntime);
+ PyInterpreterState *interp = PyInterpreterState_Head();
+ for (; interp != NULL; interp = PyInterpreterState_Next(interp)) {
+ total += REFTOTAL(interp);
+ }
+ HEAD_UNLOCK(&_PyRuntime);
+
+ /* Add in the updated value from the legacy _Py_RefTotal. */
+ total += get_legacy_reftotal();
+ total += last_final_reftotal;
+ total += runtime->object_state.interpreter_leaks;
+
+ return total;
}
#undef REFTOTAL
@@ -118,7 +146,8 @@ _PyDebug_PrintTotalRefs(void) {
fprintf(stderr,
"[%zd refs, %zd blocks]\n",
get_global_reftotal(runtime), _Py_GetAllocatedBlocks());
- /* It may be helpful to also print the "legacy" reftotal separately. */
+ /* It may be helpful to also print the "legacy" reftotal separately.
+ Likewise for the total for each interpreter. */
}
#endif /* Py_REF_DEBUG */
@@ -177,32 +206,32 @@ _Py_NegativeRefcount(const char *filename, int lineno, PyObject *op)
void
_Py_IncRefTotal_DO_NOT_USE_THIS(void)
{
- reftotal_increment(&_PyRuntime);
+ reftotal_increment(_PyInterpreterState_GET());
}
/* This is used strictly by Py_DECREF(). */
void
_Py_DecRefTotal_DO_NOT_USE_THIS(void)
{
- reftotal_decrement(&_PyRuntime);
+ reftotal_decrement(_PyInterpreterState_GET());
}
void
-_Py_IncRefTotal(void)
+_Py_IncRefTotal(PyInterpreterState *interp)
{
- reftotal_increment(&_PyRuntime);
+ reftotal_increment(interp);
}
void
-_Py_DecRefTotal(void)
+_Py_DecRefTotal(PyInterpreterState *interp)
{
- reftotal_decrement(&_PyRuntime);
+ reftotal_decrement(interp);
}
void
-_Py_AddRefTotal(Py_ssize_t n)
+_Py_AddRefTotal(PyInterpreterState *interp, Py_ssize_t n)
{
- reftotal_add(&_PyRuntime, n);
+ reftotal_add(interp, n);
}
/* This includes the legacy total
@@ -219,6 +248,12 @@ _Py_GetLegacyRefTotal(void)
return get_legacy_reftotal();
}
+Py_ssize_t
+_PyInterpreterState_GetRefTotal(PyInterpreterState *interp)
+{
+ return get_reftotal(interp);
+}
+
#endif /* Py_REF_DEBUG */
void
@@ -2128,7 +2163,7 @@ void
_Py_NewReference(PyObject *op)
{
#ifdef Py_REF_DEBUG
- reftotal_increment(&_PyRuntime);
+ reftotal_increment(_PyInterpreterState_GET());
#endif
new_reference(op);
}
diff --git a/Objects/structseq.c b/Objects/structseq.c
index c20962ecd82563..2a5343815866d3 100644
--- a/Objects/structseq.c
+++ b/Objects/structseq.c
@@ -592,7 +592,7 @@ _PyStructSequence_FiniType(PyTypeObject *type)
// Don't use Py_DECREF(): static type must not be deallocated
Py_SET_REFCNT(type, 0);
#ifdef Py_REF_DEBUG
- _Py_DecRefTotal();
+ _Py_DecRefTotal(_PyInterpreterState_GET());
#endif
// Make sure that _PyStructSequence_InitType() will initialize
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index 59c0251639d3dd..61fab4078d66ba 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -944,7 +944,7 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
if (sv == NULL) {
*pv = NULL;
#ifdef Py_REF_DEBUG
- _Py_DecRefTotal();
+ _Py_DecRefTotal(_PyInterpreterState_GET());
#endif
PyObject_GC_Del(v);
return -1;
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index f0654c239f6635..a37f97c71ec763 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -317,11 +317,27 @@ _PyType_InitCache(PyInterpreterState *interp)
entry->version = 0;
// Set to None so _PyType_Lookup() can use Py_SETREF(),
// rather than using slower Py_XSETREF().
- entry->name = Py_NewRef(Py_None);
+ // (See _PyType_FixCacheRefcounts() about the refcount.)
+ entry->name = Py_None;
entry->value = NULL;
}
}
+// This is the temporary fix used by pycore_create_interpreter(),
+// in pylifecycle.c. _PyType_InitCache() is called before the GIL
+// has been created (for the main interpreter) and without the
+// "current" thread state set. This causes crashes when the
+// reftotal is updated, so we don't modify the refcount in
+// _PyType_InitCache(), and instead do it later by calling
+// _PyType_FixCacheRefcounts().
+// XXX This workaround should be removed once we have immortal
+// objects (PEP 683).
+void
+_PyType_FixCacheRefcounts(void)
+{
+ _Py_RefcntAdd(Py_None, (1 << MCACHE_SIZE_EXP));
+}
+
static unsigned int
_PyType_ClearCache(PyInterpreterState *interp)
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 8b58a14c693f22..0d546d52087e10 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -802,6 +802,11 @@ pycore_interp_init(PyThreadState *tstate)
PyStatus status;
PyObject *sysmod = NULL;
+ // This is a temporary fix until we have immortal objects.
+ // (See _PyType_InitCache() in typeobject.c.)
+ extern void _PyType_FixCacheRefcounts(void);
+ _PyType_FixCacheRefcounts();
+
// Create singletons before the first PyType_Ready() call, since
// PyType_Ready() uses singletons like the Unicode empty string (tp_doc)
// and the empty tuple singletons (tp_bases).
diff --git a/Python/pystate.c b/Python/pystate.c
index 60adb54685ce68..b17efdbefd124c 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -483,8 +483,8 @@ void
_PyRuntimeState_Fini(_PyRuntimeState *runtime)
{
#ifdef Py_REF_DEBUG
- /* The reftotal is cleared by _Py_FinalizeRefTotal(). */
- assert(runtime->object_state.reftotal == 0);
+ /* The count is cleared by _Py_FinalizeRefTotal(). */
+ assert(runtime->object_state.interpreter_leaks == 0);
#endif
if (gilstate_tss_initialized(runtime)) {
@@ -904,6 +904,12 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
_PyEval_FiniState(&interp->ceval);
+#ifdef Py_REF_DEBUG
+ // XXX This call should be done at the end of clear_interpreter(),
+ // but currently some objects get decref'ed after that.
+ _PyInterpreterState_FinalizeRefTotal(interp);
+#endif
+
HEAD_LOCK(runtime);
PyInterpreterState **p;
for (p = &interpreters->head; ; p = &(*p)->next) {
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 20761738b527cb..4afb0f1d0b5ed2 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1854,6 +1854,8 @@ static Py_ssize_t
sys_gettotalrefcount_impl(PyObject *module)
/*[clinic end generated code: output=4103886cf17c25bc input=53b744faa5d2e4f6]*/
{
+ /* It may make sense to return the total for the current interpreter
+ or have a second function that does so. */
return _Py_GetGlobalRefTotal();
}
From 711256c8be811f8d39c349fab88d8e809d1ae3b7 Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Tue, 21 Mar 2023 13:21:57 -0500
Subject: [PATCH 170/280] Tweak polynomial itertool recipes (GH-102880)
---
Doc/library/itertools.rst | 27 +++++++++++++--------------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 38bc369d410dda..5daadfd3759f4b 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -866,6 +866,17 @@ which incur interpreter overhead.
window.append(x)
yield math.sumprod(kernel, window)
+ def polynomial_from_roots(roots):
+ """Compute a polynomial's coefficients from its roots.
+
+ (x - 5) (x + 4) (x - 3) expands to: x³ -4x² -17x + 60
+ """
+ # polynomial_from_roots([5, -4, 3]) --> [1, -4, -17, 60]
+ expansion = [1]
+ for r in roots:
+ expansion = convolve(expansion, (1, -r))
+ return list(expansion)
+
def polynomial_eval(coefficients, x):
"""Evaluate a polynomial at a specific value.
@@ -876,20 +887,8 @@ which incur interpreter overhead.
n = len(coefficients)
if n == 0:
return x * 0 # coerce zero to the type of x
- powers = map(pow, repeat(x), range(n))
- return math.sumprod(reversed(coefficients), powers)
-
- def polynomial_from_roots(roots):
- """Compute a polynomial's coefficients from its roots.
-
- (x - 5) (x + 4) (x - 3) expands to: x³ -4x² -17x + 60
- """
- # polynomial_from_roots([5, -4, 3]) --> [1, -4, -17, 60]
- roots = list(map(operator.neg, roots))
- return [
- sum(map(math.prod, combinations(roots, k)))
- for k in range(len(roots) + 1)
- ]
+ powers = map(pow, repeat(x), reversed(range(n)))
+ return math.sumprod(coefficients, powers)
def iter_index(iterable, value, start=0):
"Return indices where a value occurs in a sequence or iterable."
From ac24ffadfd7cffcf4ee1062b020a0b09b6d16929 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Tue, 21 Mar 2023 12:47:55 -0600
Subject: [PATCH 171/280] gh-98608: Fix Failure-handling in new_interpreter()
(gh-102658)
The error-handling code in new_interpreter() has been broken for a while. We hadn't noticed because those code mostly doesn't fail. (I noticed while working on gh-101660.) The problem is that we try to clear/delete the newly-created thread/interpreter using itself, which just failed. The solution is to switch back to the calling thread state first.
https://github.com/python/cpython/issues/98608
---
Python/pylifecycle.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 0d546d52087e10..d0e85519d23464 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -2062,10 +2062,10 @@ new_interpreter(PyThreadState **tstate_p, const _PyInterpreterConfig *config)
/* Oops, it didn't work. Undo it all. */
PyErr_PrintEx(0);
+ PyThreadState_Swap(save_tstate);
PyThreadState_Clear(tstate);
PyThreadState_Delete(tstate);
PyInterpreterState_Delete(interp);
- PyThreadState_Swap(save_tstate);
return status;
}
From 67394631752571bfc9fc7695b8cef166d31ace26 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Tue, 21 Mar 2023 19:13:49 +0000
Subject: [PATCH 172/280] gh-102860: improve performance of compiler's
instr_sequence_to_cfg (#102861)
---
Python/compile.c | 50 ++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 44 insertions(+), 6 deletions(-)
diff --git a/Python/compile.c b/Python/compile.c
index 29e55b8b30c56b..99296050445f50 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -595,17 +595,52 @@ static int
instr_sequence_to_cfg(instr_sequence *seq, cfg_builder *g) {
memset(g, 0, sizeof(cfg_builder));
RETURN_IF_ERROR(cfg_builder_init(g));
- /* Note: there can be more than one label for the same offset */
+
+ /* There can be more than one label for the same offset. The
+ * offset2lbl maping selects one of them which we use consistently.
+ */
+
+ int *offset2lbl = PyMem_Malloc(seq->s_used * sizeof(int));
+ if (offset2lbl == NULL) {
+ PyErr_NoMemory();
+ return ERROR;
+ }
for (int i = 0; i < seq->s_used; i++) {
- for (int j=0; j < seq->s_labelmap_size; j++) {
- if (seq->s_labelmap[j] == i) {
- jump_target_label lbl = {j};
- RETURN_IF_ERROR(cfg_builder_use_label(g, lbl));
+ offset2lbl[i] = -1;
+ }
+ for (int lbl=0; lbl < seq->s_labelmap_size; lbl++) {
+ int offset = seq->s_labelmap[lbl];
+ if (offset >= 0) {
+ assert(offset < seq->s_used);
+ offset2lbl[offset] = lbl;
+ }
+ }
+
+ for (int i = 0; i < seq->s_used; i++) {
+ int lbl = offset2lbl[i];
+ if (lbl >= 0) {
+ assert (lbl < seq->s_labelmap_size);
+ jump_target_label lbl_ = {lbl};
+ if (cfg_builder_use_label(g, lbl_) < 0) {
+ goto error;
}
}
instruction *instr = &seq->s_instrs[i];
- RETURN_IF_ERROR(cfg_builder_addop(g, instr->i_opcode, instr->i_oparg, instr->i_loc));
+ int opcode = instr->i_opcode;
+ int oparg = instr->i_oparg;
+ if (HAS_TARGET(opcode)) {
+ int offset = seq->s_labelmap[oparg];
+ assert(offset >= 0 && offset < seq->s_used);
+ int lbl = offset2lbl[offset];
+ assert(lbl >= 0 && lbl < seq->s_labelmap_size);
+ oparg = lbl;
+ }
+ if (cfg_builder_addop(g, opcode, oparg, instr->i_loc) < 0) {
+ goto error;
+ }
}
+ PyMem_Free(offset2lbl);
+
int nblocks = 0;
for (basicblock *b = g->g_block_list; b != NULL; b = b->b_list) {
nblocks++;
@@ -615,6 +650,9 @@ instr_sequence_to_cfg(instr_sequence *seq, cfg_builder *g) {
return ERROR;
}
return SUCCESS;
+error:
+ PyMem_Free(offset2lbl);
+ return ERROR;
}
From 7de1d793b7567fadd8425c976300f3d1e8047145 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Tue, 21 Mar 2023 14:01:38 -0600
Subject: [PATCH 173/280] gh-94673: Isolate the _io module to Each Interpreter
(gh-102663)
Aside from sys and builtins, _io is the only core builtin module that hasn't been ported to multi-phase init. We may do so later (e.g. gh-101948), but in the meantime we must at least take care of the module's static types properly. (This came up while working on gh-101660.)
https://github.com/python/cpython/issues/94673
---
Modules/_io/_iomodule.c | 40 ++++++++++++++++++++++++++++++++--------
Python/pylifecycle.c | 12 ++++++++----
2 files changed, 40 insertions(+), 12 deletions(-)
diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c
index 1506755427fc0d..5644cc05c45800 100644
--- a/Modules/_io/_iomodule.c
+++ b/Modules/_io/_iomodule.c
@@ -11,6 +11,7 @@
#include "Python.h"
#include "_iomodule.h"
#include "pycore_pystate.h" // _PyInterpreterState_GET()
+#include "pycore_initconfig.h" // _PyStatus_OK()
#ifdef HAVE_SYS_TYPES_H
#include
@@ -666,12 +667,40 @@ static PyTypeObject* static_types[] = {
};
+PyStatus
+_PyIO_InitTypes(PyInterpreterState *interp)
+{
+ if (!_Py_IsMainInterpreter(interp)) {
+ return _PyStatus_OK();
+ }
+
+ // Set type base classes
+#ifdef HAVE_WINDOWS_CONSOLE_IO
+ PyWindowsConsoleIO_Type.tp_base = &PyRawIOBase_Type;
+#endif
+
+ for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
+ PyTypeObject *type = static_types[i];
+ if (_PyStaticType_InitBuiltin(type) < 0) {
+ return _PyStatus_ERR("Can't initialize builtin type");
+ }
+ }
+
+ return _PyStatus_OK();
+}
+
void
-_PyIO_Fini(void)
+_PyIO_FiniTypes(PyInterpreterState *interp)
{
+ if (!_Py_IsMainInterpreter(interp)) {
+ return;
+ }
+
+ // Deallocate types in the reverse order to deallocate subclasses before
+ // their base classes.
for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types) - 1; i >= 0; i--) {
- PyTypeObject *exc = static_types[i];
- _PyStaticType_Dealloc(exc);
+ PyTypeObject *type = static_types[i];
+ _PyStaticType_Dealloc(type);
}
}
@@ -717,11 +746,6 @@ PyInit__io(void)
goto fail;
}
- // Set type base classes
-#ifdef HAVE_WINDOWS_CONSOLE_IO
- PyWindowsConsoleIO_Type.tp_base = &PyRawIOBase_Type;
-#endif
-
// Add types
for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) {
PyTypeObject *type = static_types[i];
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index d0e85519d23464..8110d94ba17526 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -31,7 +31,8 @@
#include "pycore_unicodeobject.h" // _PyUnicode_InitTypes()
#include "opcode.h"
-extern void _PyIO_Fini(void);
+extern PyStatus _PyIO_InitTypes(PyInterpreterState *interp);
+extern void _PyIO_FiniTypes(PyInterpreterState *interp);
#include // setlocale()
#include // getenv()
@@ -697,6 +698,11 @@ pycore_init_types(PyInterpreterState *interp)
return _PyStatus_ERR("failed to initialize an exception type");
}
+ status = _PyIO_InitTypes(interp);
+ if (_PyStatus_EXCEPTION(status)) {
+ return status;
+ }
+
status = _PyExc_InitGlobalObjects(interp);
if (_PyStatus_EXCEPTION(status)) {
return status;
@@ -1700,9 +1706,7 @@ finalize_interp_clear(PyThreadState *tstate)
/* Clear interpreter state and all thread states */
_PyInterpreterState_Clear(tstate);
- if (is_main_interp) {
- _PyIO_Fini();
- }
+ _PyIO_FiniTypes(tstate->interp);
/* Clear all loghooks */
/* Both _PySys_Audit function and users still need PyObject, such as tuple.
From 43f13b998d4b6542a221b46cd273385be6384eea Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Tue, 21 Mar 2023 21:36:31 +0000
Subject: [PATCH 174/280] gh-102406: replace exception chaining by PEP-678
notes in codecs (#102407)
---
Include/cpython/pyerrors.h | 18 ---
Lib/test/test_codecs.py | 106 +++++++----------
...-03-03-23-21-16.gh-issue-102406.XLqYO3.rst | 1 +
Objects/exceptions.c | 110 ------------------
Python/codecs.c | 35 +++---
5 files changed, 64 insertions(+), 206 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-03-23-21-16.gh-issue-102406.XLqYO3.rst
diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h
index d0300f6ee56a25..65bdc942f5067a 100644
--- a/Include/cpython/pyerrors.h
+++ b/Include/cpython/pyerrors.h
@@ -116,24 +116,6 @@ PyAPI_FUNC(int) _PyException_AddNote(
PyObject *exc,
PyObject *note);
-/* Helper that attempts to replace the current exception with one of the
- * same type but with a prefix added to the exception text. The resulting
- * exception description looks like:
- *
- * prefix (exc_type: original_exc_str)
- *
- * Only some exceptions can be safely replaced. If the function determines
- * it isn't safe to perform the replacement, it will leave the original
- * unmodified exception in place.
- *
- * Returns a borrowed reference to the new exception (if any), NULL if the
- * existing exception was left in place.
- */
-PyAPI_FUNC(PyObject *) _PyErr_TrySetFromCause(
- const char *prefix_format, /* ASCII-encoded string */
- ...
- );
-
/* In signalmodule.c */
int PySignal_SetWakeupFd(int fd);
diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py
index e3add0c1ee926c..376175f90f63eb 100644
--- a/Lib/test/test_codecs.py
+++ b/Lib/test/test_codecs.py
@@ -2819,24 +2819,19 @@ def test_binary_to_text_denylists_text_transforms(self):
self.assertIsNone(failure.exception.__cause__)
@unittest.skipUnless(zlib, "Requires zlib support")
- def test_custom_zlib_error_is_wrapped(self):
+ def test_custom_zlib_error_is_noted(self):
# Check zlib codec gives a good error for malformed input
- msg = "^decoding with 'zlib_codec' codec failed"
- with self.assertRaisesRegex(Exception, msg) as failure:
+ msg = "decoding with 'zlib_codec' codec failed"
+ with self.assertRaises(Exception) as failure:
codecs.decode(b"hello", "zlib_codec")
- self.assertIsInstance(failure.exception.__cause__,
- type(failure.exception))
+ self.assertEqual(msg, failure.exception.__notes__[0])
- def test_custom_hex_error_is_wrapped(self):
+ def test_custom_hex_error_is_noted(self):
# Check hex codec gives a good error for malformed input
- msg = "^decoding with 'hex_codec' codec failed"
- with self.assertRaisesRegex(Exception, msg) as failure:
+ msg = "decoding with 'hex_codec' codec failed"
+ with self.assertRaises(Exception) as failure:
codecs.decode(b"hello", "hex_codec")
- self.assertIsInstance(failure.exception.__cause__,
- type(failure.exception))
-
- # Unfortunately, the bz2 module throws OSError, which the codec
- # machinery currently can't wrap :(
+ self.assertEqual(msg, failure.exception.__notes__[0])
# Ensure codec aliases from http://bugs.python.org/issue7475 work
def test_aliases(self):
@@ -2860,11 +2855,8 @@ def test_uu_invalid(self):
self.assertRaises(ValueError, codecs.decode, b"", "uu-codec")
-# The codec system tries to wrap exceptions in order to ensure the error
-# mentions the operation being performed and the codec involved. We
-# currently *only* want this to happen for relatively stateless
-# exceptions, where the only significant information they contain is their
-# type and a single str argument.
+# The codec system tries to add notes to exceptions in order to ensure
+# the error mentions the operation being performed and the codec involved.
# Use a local codec registry to avoid appearing to leak objects when
# registering multiple search functions
@@ -2874,10 +2866,10 @@ def _get_test_codec(codec_name):
return _TEST_CODECS.get(codec_name)
-class ExceptionChainingTest(unittest.TestCase):
+class ExceptionNotesTest(unittest.TestCase):
def setUp(self):
- self.codec_name = 'exception_chaining_test'
+ self.codec_name = 'exception_notes_test'
codecs.register(_get_test_codec)
self.addCleanup(codecs.unregister, _get_test_codec)
@@ -2901,91 +2893,77 @@ def set_codec(self, encode, decode):
_TEST_CODECS[self.codec_name] = codec_info
@contextlib.contextmanager
- def assertWrapped(self, operation, exc_type, msg):
- full_msg = r"{} with {!r} codec failed \({}: {}\)".format(
- operation, self.codec_name, exc_type.__name__, msg)
- with self.assertRaisesRegex(exc_type, full_msg) as caught:
+ def assertNoted(self, operation, exc_type, msg):
+ full_msg = r"{} with {!r} codec failed".format(
+ operation, self.codec_name)
+ with self.assertRaises(exc_type) as caught:
yield caught
- self.assertIsInstance(caught.exception.__cause__, exc_type)
- self.assertIsNotNone(caught.exception.__cause__.__traceback__)
+ self.assertIn(full_msg, caught.exception.__notes__[0])
+ caught.exception.__notes__.clear()
def raise_obj(self, *args, **kwds):
# Helper to dynamically change the object raised by a test codec
raise self.obj_to_raise
- def check_wrapped(self, obj_to_raise, msg, exc_type=RuntimeError):
+ def check_note(self, obj_to_raise, msg, exc_type=RuntimeError):
self.obj_to_raise = obj_to_raise
self.set_codec(self.raise_obj, self.raise_obj)
- with self.assertWrapped("encoding", exc_type, msg):
+ with self.assertNoted("encoding", exc_type, msg):
"str_input".encode(self.codec_name)
- with self.assertWrapped("encoding", exc_type, msg):
+ with self.assertNoted("encoding", exc_type, msg):
codecs.encode("str_input", self.codec_name)
- with self.assertWrapped("decoding", exc_type, msg):
+ with self.assertNoted("decoding", exc_type, msg):
b"bytes input".decode(self.codec_name)
- with self.assertWrapped("decoding", exc_type, msg):
+ with self.assertNoted("decoding", exc_type, msg):
codecs.decode(b"bytes input", self.codec_name)
def test_raise_by_type(self):
- self.check_wrapped(RuntimeError, "")
+ self.check_note(RuntimeError, "")
def test_raise_by_value(self):
- msg = "This should be wrapped"
- self.check_wrapped(RuntimeError(msg), msg)
+ msg = "This should be noted"
+ self.check_note(RuntimeError(msg), msg)
def test_raise_grandchild_subclass_exact_size(self):
- msg = "This should be wrapped"
+ msg = "This should be noted"
class MyRuntimeError(RuntimeError):
__slots__ = ()
- self.check_wrapped(MyRuntimeError(msg), msg, MyRuntimeError)
+ self.check_note(MyRuntimeError(msg), msg, MyRuntimeError)
def test_raise_subclass_with_weakref_support(self):
- msg = "This should be wrapped"
+ msg = "This should be noted"
class MyRuntimeError(RuntimeError):
pass
- self.check_wrapped(MyRuntimeError(msg), msg, MyRuntimeError)
-
- def check_not_wrapped(self, obj_to_raise, msg):
- def raise_obj(*args, **kwds):
- raise obj_to_raise
- self.set_codec(raise_obj, raise_obj)
- with self.assertRaisesRegex(RuntimeError, msg):
- "str input".encode(self.codec_name)
- with self.assertRaisesRegex(RuntimeError, msg):
- codecs.encode("str input", self.codec_name)
- with self.assertRaisesRegex(RuntimeError, msg):
- b"bytes input".decode(self.codec_name)
- with self.assertRaisesRegex(RuntimeError, msg):
- codecs.decode(b"bytes input", self.codec_name)
+ self.check_note(MyRuntimeError(msg), msg, MyRuntimeError)
- def test_init_override_is_not_wrapped(self):
+ def test_init_override(self):
class CustomInit(RuntimeError):
def __init__(self):
pass
- self.check_not_wrapped(CustomInit, "")
+ self.check_note(CustomInit, "")
- def test_new_override_is_not_wrapped(self):
+ def test_new_override(self):
class CustomNew(RuntimeError):
def __new__(cls):
return super().__new__(cls)
- self.check_not_wrapped(CustomNew, "")
+ self.check_note(CustomNew, "")
- def test_instance_attribute_is_not_wrapped(self):
- msg = "This should NOT be wrapped"
+ def test_instance_attribute(self):
+ msg = "This should be noted"
exc = RuntimeError(msg)
exc.attr = 1
- self.check_not_wrapped(exc, "^{}$".format(msg))
+ self.check_note(exc, "^{}$".format(msg))
- def test_non_str_arg_is_not_wrapped(self):
- self.check_not_wrapped(RuntimeError(1), "1")
+ def test_non_str_arg(self):
+ self.check_note(RuntimeError(1), "1")
- def test_multiple_args_is_not_wrapped(self):
+ def test_multiple_args(self):
msg_re = r"^\('a', 'b', 'c'\)$"
- self.check_not_wrapped(RuntimeError('a', 'b', 'c'), msg_re)
+ self.check_note(RuntimeError('a', 'b', 'c'), msg_re)
# http://bugs.python.org/issue19609
- def test_codec_lookup_failure_not_wrapped(self):
+ def test_codec_lookup_failure(self):
msg = "^unknown encoding: {}$".format(self.codec_name)
- # The initial codec lookup should not be wrapped
with self.assertRaisesRegex(LookupError, msg):
"str input".encode(self.codec_name)
with self.assertRaisesRegex(LookupError, msg):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-03-23-21-16.gh-issue-102406.XLqYO3.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-03-23-21-16.gh-issue-102406.XLqYO3.rst
new file mode 100644
index 00000000000000..e0d061c37299f2
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-03-23-21-16.gh-issue-102406.XLqYO3.rst
@@ -0,0 +1 @@
+:mod:`codecs` encoding/decoding errors now get the context information (which operation and which codecs) attached as :pep:`678` notes instead of through chaining a new instance of the exception.
diff --git a/Objects/exceptions.c b/Objects/exceptions.c
index d69f7400ca6042..a355244cf997e6 100644
--- a/Objects/exceptions.c
+++ b/Objects/exceptions.c
@@ -3764,113 +3764,3 @@ _PyException_AddNote(PyObject *exc, PyObject *note)
return res;
}
-/* Helper to do the equivalent of "raise X from Y" in C, but always using
- * the current exception rather than passing one in.
- *
- * We currently limit this to *only* exceptions that use the BaseException
- * tp_init and tp_new methods, since we can be reasonably sure we can wrap
- * those correctly without losing data and without losing backwards
- * compatibility.
- *
- * We also aim to rule out *all* exceptions that might be storing additional
- * state, whether by having a size difference relative to BaseException,
- * additional arguments passed in during construction or by having a
- * non-empty instance dict.
- *
- * We need to be very careful with what we wrap, since changing types to
- * a broader exception type would be backwards incompatible for
- * existing codecs, and with different init or new method implementations
- * may either not support instantiation with PyErr_Format or lose
- * information when instantiated that way.
- *
- * XXX (ncoghlan): This could be made more comprehensive by exploiting the
- * fact that exceptions are expected to support pickling. If more builtin
- * exceptions (e.g. AttributeError) start to be converted to rich
- * exceptions with additional attributes, that's probably a better approach
- * to pursue over adding special cases for particular stateful subclasses.
- *
- * Returns a borrowed reference to the new exception (if any), NULL if the
- * existing exception was left in place.
- */
-PyObject *
-_PyErr_TrySetFromCause(const char *format, ...)
-{
- PyObject* msg_prefix;
- PyObject *instance_args;
- Py_ssize_t num_args, caught_type_size, base_exc_size;
- va_list vargs;
- int same_basic_size;
-
- PyObject *exc = PyErr_GetRaisedException();
- PyTypeObject *caught_type = Py_TYPE(exc);
- /* Ensure type info indicates no extra state is stored at the C level
- * and that the type can be reinstantiated using PyErr_Format
- */
- caught_type_size = caught_type->tp_basicsize;
- base_exc_size = _PyExc_BaseException.tp_basicsize;
- same_basic_size = (
- caught_type_size == base_exc_size ||
- (_PyType_SUPPORTS_WEAKREFS(caught_type) &&
- (caught_type_size == base_exc_size + (Py_ssize_t)sizeof(PyObject *))
- )
- );
- if (caught_type->tp_init != (initproc)BaseException_init ||
- caught_type->tp_new != BaseException_new ||
- !same_basic_size ||
- caught_type->tp_itemsize != _PyExc_BaseException.tp_itemsize) {
- /* We can't be sure we can wrap this safely, since it may contain
- * more state than just the exception type. Accordingly, we just
- * leave it alone.
- */
- PyErr_SetRaisedException(exc);
- return NULL;
- }
-
- /* Check the args are empty or contain a single string */
- instance_args = ((PyBaseExceptionObject *)exc)->args;
- num_args = PyTuple_GET_SIZE(instance_args);
- if (num_args > 1 ||
- (num_args == 1 &&
- !PyUnicode_CheckExact(PyTuple_GET_ITEM(instance_args, 0)))) {
- /* More than 1 arg, or the one arg we do have isn't a string
- */
- PyErr_SetRaisedException(exc);
- return NULL;
- }
-
- /* Ensure the instance dict is also empty */
- if (!_PyObject_IsInstanceDictEmpty(exc)) {
- /* While we could potentially copy a non-empty instance dictionary
- * to the replacement exception, for now we take the more
- * conservative path of leaving exceptions with attributes set
- * alone.
- */
- PyErr_SetRaisedException(exc);
- return NULL;
- }
-
- /* For exceptions that we can wrap safely, we chain the original
- * exception to a new one of the exact same type with an
- * error message that mentions the additional details and the
- * original exception.
- *
- * It would be nice to wrap OSError and various other exception
- * types as well, but that's quite a bit trickier due to the extra
- * state potentially stored on OSError instances.
- */
- va_start(vargs, format);
- msg_prefix = PyUnicode_FromFormatV(format, vargs);
- va_end(vargs);
- if (msg_prefix == NULL) {
- Py_DECREF(exc);
- return NULL;
- }
-
- PyErr_Format((PyObject*)Py_TYPE(exc), "%U (%s: %S)",
- msg_prefix, Py_TYPE(exc)->tp_name, exc);
- Py_DECREF(msg_prefix);
- PyObject *new_exc = PyErr_GetRaisedException();
- PyException_SetCause(new_exc, exc);
- PyErr_SetRaisedException(new_exc);
- return new_exc;
-}
diff --git a/Python/codecs.c b/Python/codecs.c
index b2087b499dfdba..9d800f9856c2f7 100644
--- a/Python/codecs.c
+++ b/Python/codecs.c
@@ -382,20 +382,27 @@ PyObject *PyCodec_StreamWriter(const char *encoding,
return codec_getstreamcodec(encoding, stream, errors, 3);
}
-/* Helper that tries to ensure the reported exception chain indicates the
- * codec that was invoked to trigger the failure without changing the type
- * of the exception raised.
- */
static void
-wrap_codec_error(const char *operation,
- const char *encoding)
+add_note_to_codec_error(const char *operation,
+ const char *encoding)
{
- /* TrySetFromCause will replace the active exception with a suitably
- * updated clone if it can, otherwise it will leave the original
- * exception alone.
- */
- _PyErr_TrySetFromCause("%s with '%s' codec failed",
- operation, encoding);
+ PyObject *exc = PyErr_GetRaisedException();
+ if (exc == NULL) {
+ return;
+ }
+ PyObject *note = PyUnicode_FromFormat("%s with '%s' codec failed",
+ operation, encoding);
+ if (note == NULL) {
+ _PyErr_ChainExceptions1(exc);
+ return;
+ }
+ int res = _PyException_AddNote(exc, note);
+ Py_DECREF(note);
+ if (res < 0) {
+ _PyErr_ChainExceptions1(exc);
+ return;
+ }
+ PyErr_SetRaisedException(exc);
}
/* Encode an object (e.g. a Unicode object) using the given encoding
@@ -418,7 +425,7 @@ _PyCodec_EncodeInternal(PyObject *object,
result = PyObject_Call(encoder, args, NULL);
if (result == NULL) {
- wrap_codec_error("encoding", encoding);
+ add_note_to_codec_error("encoding", encoding);
goto onError;
}
@@ -463,7 +470,7 @@ _PyCodec_DecodeInternal(PyObject *object,
result = PyObject_Call(decoder, args, NULL);
if (result == NULL) {
- wrap_codec_error("decoding", encoding);
+ add_note_to_codec_error("decoding", encoding);
goto onError;
}
if (!PyTuple_Check(result) ||
From 7174c0cc98d57ef5b3d3b67626b7e75b26c85298 Mon Sep 17 00:00:00 2001
From: David Poirier <1152277+david-poirier@users.noreply.github.com>
Date: Wed, 22 Mar 2023 08:58:31 +1100
Subject: [PATCH 175/280] Add link to `sys.exit` function documentation
(#102805)
* Add link to `sys.exit` function documentation
* Update Doc/library/os.rst
Co-authored-by: Ezio Melotti
* Update Doc/library/os.rst
Co-authored-by: C.A.M. Gerlach
---------
Co-authored-by: Ezio Melotti
Co-authored-by: C.A.M. Gerlach
---
Doc/library/os.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index 3153f79e10ce1f..7bb501c5946817 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -3951,7 +3951,7 @@ to be ignored.
.. note::
- The standard way to exit is ``sys.exit(n)``. :func:`_exit` should
+ The standard way to exit is :func:`sys.exit(n) `. :func:`!_exit` should
normally only be used in the child process after a :func:`fork`.
The following exit codes are defined and can be used with :func:`_exit`,
From 4dc17094b6b362c3dd21c53d74e2f4211ad7d48f Mon Sep 17 00:00:00 2001
From: Sergey B Kirpichev
Date: Wed, 22 Mar 2023 02:48:19 +0300
Subject: [PATCH 176/280] gh-102839: remove AC for math.log (GH-102863)
---
...-03-20-12-21-19.gh-issue-102839.RjRi12.rst | 1 +
Modules/clinic/mathmodule.c.h | 45 +------------------
Modules/mathmodule.c | 35 ++++++---------
3 files changed, 16 insertions(+), 65 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-20-12-21-19.gh-issue-102839.RjRi12.rst
diff --git a/Misc/NEWS.d/next/Library/2023-03-20-12-21-19.gh-issue-102839.RjRi12.rst b/Misc/NEWS.d/next/Library/2023-03-20-12-21-19.gh-issue-102839.RjRi12.rst
new file mode 100644
index 00000000000000..673b38974e4d1b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-20-12-21-19.gh-issue-102839.RjRi12.rst
@@ -0,0 +1 @@
+Improve performance of :func:`math.log` arguments handling by removing the argument clinic.
diff --git a/Modules/clinic/mathmodule.c.h b/Modules/clinic/mathmodule.c.h
index 1f9725883b9820..bc5bbceb4c92b6 100644
--- a/Modules/clinic/mathmodule.c.h
+++ b/Modules/clinic/mathmodule.c.h
@@ -186,49 +186,6 @@ math_modf(PyObject *module, PyObject *arg)
return return_value;
}
-PyDoc_STRVAR(math_log__doc__,
-"log(x, [base=math.e])\n"
-"Return the logarithm of x to the given base.\n"
-"\n"
-"If the base not specified, returns the natural logarithm (base e) of x.");
-
-#define MATH_LOG_METHODDEF \
- {"log", (PyCFunction)math_log, METH_VARARGS, math_log__doc__},
-
-static PyObject *
-math_log_impl(PyObject *module, PyObject *x, int group_right_1,
- PyObject *base);
-
-static PyObject *
-math_log(PyObject *module, PyObject *args)
-{
- PyObject *return_value = NULL;
- PyObject *x;
- int group_right_1 = 0;
- PyObject *base = NULL;
-
- switch (PyTuple_GET_SIZE(args)) {
- case 1:
- if (!PyArg_ParseTuple(args, "O:log", &x)) {
- goto exit;
- }
- break;
- case 2:
- if (!PyArg_ParseTuple(args, "OO:log", &x, &base)) {
- goto exit;
- }
- group_right_1 = 1;
- break;
- default:
- PyErr_SetString(PyExc_TypeError, "math.log requires 1 to 2 arguments");
- goto exit;
- }
- return_value = math_log_impl(module, x, group_right_1, base);
-
-exit:
- return return_value;
-}
-
PyDoc_STRVAR(math_log2__doc__,
"log2($module, x, /)\n"
"--\n"
@@ -954,4 +911,4 @@ math_ulp(PyObject *module, PyObject *arg)
exit:
return return_value;
}
-/*[clinic end generated code: output=899211ec70e4506c input=a9049054013a1b77]*/
+/*[clinic end generated code: output=a6437a3ba18c486a input=a9049054013a1b77]*/
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index c9a2be66863993..473936edb3f5cd 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -2284,33 +2284,22 @@ loghelper(PyObject* arg, double (*func)(double))
}
-/*[clinic input]
-math.log
-
- x: object
- [
- base: object(c_default="NULL") = math.e
- ]
- /
-
-Return the logarithm of x to the given base.
-
-If the base not specified, returns the natural logarithm (base e) of x.
-[clinic start generated code]*/
-
+/* AC: cannot convert yet, see gh-102839 and gh-89381, waiting
+ for support of multiple signatures */
static PyObject *
-math_log_impl(PyObject *module, PyObject *x, int group_right_1,
- PyObject *base)
-/*[clinic end generated code: output=7b5a39e526b73fc9 input=0f62d5726cbfebbd]*/
+math_log(PyObject *module, PyObject * const *args, Py_ssize_t nargs)
{
PyObject *num, *den;
PyObject *ans;
- num = loghelper(x, m_log);
- if (num == NULL || base == NULL)
+ if (!_PyArg_CheckPositional("log", nargs, 1, 2))
+ return NULL;
+
+ num = loghelper(args[0], m_log);
+ if (num == NULL || nargs == 1)
return num;
- den = loghelper(base, m_log);
+ den = loghelper(args[1], m_log);
if (den == NULL) {
Py_DECREF(num);
return NULL;
@@ -2322,6 +2311,10 @@ math_log_impl(PyObject *module, PyObject *x, int group_right_1,
return ans;
}
+PyDoc_STRVAR(math_log_doc,
+"log(x, [base=math.e])\n\
+Return the logarithm of x to the given base.\n\n\
+If the base not specified, returns the natural logarithm (base e) of x.");
/*[clinic input]
math.log2
@@ -4045,7 +4038,7 @@ static PyMethodDef math_methods[] = {
{"lcm", _PyCFunction_CAST(math_lcm), METH_FASTCALL, math_lcm_doc},
MATH_LDEXP_METHODDEF
{"lgamma", math_lgamma, METH_O, math_lgamma_doc},
- MATH_LOG_METHODDEF
+ {"log", _PyCFunction_CAST(math_log), METH_FASTCALL, math_log_doc},
{"log1p", math_log1p, METH_O, math_log1p_doc},
MATH_LOG10_METHODDEF
MATH_LOG2_METHODDEF
From e030c1865ae73242441c631b3afd9248f6e07c3b Mon Sep 17 00:00:00 2001
From: gaogaotiantian
Date: Wed, 22 Mar 2023 04:34:52 -0700
Subject: [PATCH 177/280] Docs: improve accuracy of pdb alias example (#102892)
---
Doc/library/pdb.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst
index 21c6ca8622dceb..5988477af03abd 100644
--- a/Doc/library/pdb.rst
+++ b/Doc/library/pdb.rst
@@ -513,7 +513,7 @@ can be overridden by the local file.
:file:`.pdbrc` file)::
# Print instance variables (usage "pi classInst")
- alias pi for k in %1.__dict__.keys(): print("%1.",k,"=",%1.__dict__[k])
+ alias pi for k in %1.__dict__.keys(): print(f"%1.{k} = {%1.__dict__[k]}")
# Print instance variables in self
alias ps pi self
From e1394426931729a64e87b49ab94b6e9bd5508463 Mon Sep 17 00:00:00 2001
From: Artem Mukhin
Date: Wed, 22 Mar 2023 12:35:27 +0100
Subject: [PATCH 178/280] GH-94808: Cover `PyOS_mystrnicmp` and
`PyOS_mystricmp` (gh-102469)
---
Modules/Setup.stdlib.in | 2 +-
Modules/_testcapi/parts.h | 1 +
Modules/_testcapi/pyos.c | 60 +++++++++++++++++++++++++++++++
Modules/_testcapimodule.c | 3 ++
PCbuild/_testcapi.vcxproj | 1 +
PCbuild/_testcapi.vcxproj.filters | 3 ++
6 files changed, 69 insertions(+), 1 deletion(-)
create mode 100644 Modules/_testcapi/pyos.c
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index b12290d436cbeb..fe1b9f8f5380c1 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -169,7 +169,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
-@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c
+@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/pyos.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
# Some testing modules MUST be built as shared libraries.
diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h
index c8f31dc8e39fae..60ec81dad2ba9e 100644
--- a/Modules/_testcapi/parts.h
+++ b/Modules/_testcapi/parts.h
@@ -38,6 +38,7 @@ int _PyTestCapi_Init_Float(PyObject *module);
int _PyTestCapi_Init_Structmember(PyObject *module);
int _PyTestCapi_Init_Exceptions(PyObject *module);
int _PyTestCapi_Init_Code(PyObject *module);
+int _PyTestCapi_Init_PyOS(PyObject *module);
#ifdef LIMITED_API_AVAILABLE
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
diff --git a/Modules/_testcapi/pyos.c b/Modules/_testcapi/pyos.c
new file mode 100644
index 00000000000000..63140e914875db
--- /dev/null
+++ b/Modules/_testcapi/pyos.c
@@ -0,0 +1,60 @@
+#include "parts.h"
+
+
+static PyObject *
+test_PyOS_mystrnicmp(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ assert(PyOS_mystrnicmp("", "", 0) == 0);
+ assert(PyOS_mystrnicmp("", "", 1) == 0);
+
+ assert(PyOS_mystrnicmp("insert", "ins", 3) == 0);
+ assert(PyOS_mystrnicmp("ins", "insert", 3) == 0);
+ assert(PyOS_mystrnicmp("insect", "insert", 3) == 0);
+
+ assert(PyOS_mystrnicmp("insert", "insert", 6) == 0);
+ assert(PyOS_mystrnicmp("Insert", "insert", 6) == 0);
+ assert(PyOS_mystrnicmp("INSERT", "insert", 6) == 0);
+ assert(PyOS_mystrnicmp("insert", "insert", 10) == 0);
+
+ assert(PyOS_mystrnicmp("invert", "insert", 6) == ('v' - 's'));
+ assert(PyOS_mystrnicmp("insert", "invert", 6) == ('s' - 'v'));
+ assert(PyOS_mystrnicmp("insert", "ins\0rt", 6) == 'e');
+
+ // GH-21845
+ assert(PyOS_mystrnicmp("insert\0a", "insert\0b", 8) == 0);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+test_PyOS_mystricmp(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ assert(PyOS_mystricmp("", "") == 0);
+ assert(PyOS_mystricmp("insert", "insert") == 0);
+ assert(PyOS_mystricmp("Insert", "insert") == 0);
+ assert(PyOS_mystricmp("INSERT", "insert") == 0);
+ assert(PyOS_mystricmp("insert", "ins") == 'e');
+ assert(PyOS_mystricmp("ins", "insert") == -'e');
+
+ // GH-21845
+ assert(PyOS_mystricmp("insert", "ins\0rt") == 'e');
+ assert(PyOS_mystricmp("invert", "insert") == ('v' - 's'));
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef test_methods[] = {
+ {"test_PyOS_mystrnicmp", test_PyOS_mystrnicmp, METH_NOARGS, NULL},
+ {"test_PyOS_mystricmp", test_PyOS_mystricmp, METH_NOARGS, NULL},
+ {NULL},
+};
+
+int
+_PyTestCapi_Init_PyOS(PyObject *mod)
+{
+ if (PyModule_AddFunctions(mod, test_methods) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index e2ebab5c5b4849..3d9a2aeeb7cfd5 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -4154,6 +4154,9 @@ PyInit__testcapi(void)
if (_PyTestCapi_Init_Code(m) < 0) {
return NULL;
}
+ if (_PyTestCapi_Init_PyOS(m) < 0) {
+ return NULL;
+ }
#ifndef LIMITED_API_AVAILABLE
PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False);
diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj
index 4cc184bfc1ac82..439cd687fda61d 100644
--- a/PCbuild/_testcapi.vcxproj
+++ b/PCbuild/_testcapi.vcxproj
@@ -109,6 +109,7 @@
+
diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters
index fbdaf04ce37cb1..0e42e4982c21ff 100644
--- a/PCbuild/_testcapi.vcxproj.filters
+++ b/PCbuild/_testcapi.vcxproj.filters
@@ -57,6 +57,9 @@
Source Files
+
+ Source Files
+
From 563cf60b4c10a17c9c98459bf495a3b4bd39f15e Mon Sep 17 00:00:00 2001
From: "Erlend E. Aasland"
Date: Wed, 22 Mar 2023 12:37:13 +0100
Subject: [PATCH 179/280] Docs: improve the accuracy of the sqlite3.connect()
timeout param (#102900)
Co-authored-by: C.A.M. Gerlach
---
Doc/library/sqlite3.rst | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index ff036ad56acba8..a78f3eb7221746 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -272,9 +272,9 @@ Module functions
:param float timeout:
How many seconds the connection should wait before raising
- an exception, if the database is locked by another connection.
- If another connection opens a transaction to modify the database,
- it will be locked until that transaction is committed.
+ an :exc:`OperationalError` when a table is locked.
+ If another connection opens a transaction to modify a table,
+ that table will be locked until the transaction is committed.
Default five seconds.
:param int detect_types:
From 3e098602f61989f507e6b3ea5748e8a438ec7606 Mon Sep 17 00:00:00 2001
From: Timo Ludwig
Date: Wed, 22 Mar 2023 12:46:58 +0100
Subject: [PATCH 180/280] gh-100989: Improve the accuracy of collections.deque
docstrings (#100990)
Co-authored-by: C.A.M. Gerlach
---
Modules/_collectionsmodule.c | 37 +++++++++++++++++++++++-------------
1 file changed, 24 insertions(+), 13 deletions(-)
diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c
index 68131f3b54d2ea..ba4a9760f7b906 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -910,7 +910,9 @@ deque_rotate(dequeobject *deque, PyObject *const *args, Py_ssize_t nargs)
}
PyDoc_STRVAR(rotate_doc,
-"Rotate the deque n steps to the right (default n=1). If n is negative, rotates left.");
+"rotate(n)\n\n"
+"Rotate the deque *n* steps to the right (default ``n=1``). "
+"If *n* is negative, rotates left.");
static PyObject *
deque_reverse(dequeobject *deque, PyObject *unused)
@@ -951,7 +953,8 @@ deque_reverse(dequeobject *deque, PyObject *unused)
}
PyDoc_STRVAR(reverse_doc,
-"D.reverse() -- reverse *IN PLACE*");
+"reverse()\n\n"
+"Reverse the elements of the deque *IN PLACE*.");
static PyObject *
deque_count(dequeobject *deque, PyObject *v)
@@ -990,7 +993,8 @@ deque_count(dequeobject *deque, PyObject *v)
}
PyDoc_STRVAR(count_doc,
-"D.count(value) -> integer -- return number of occurrences of value");
+"count(x) -> int\n\n"
+"Count the number of deque elements equal to *x*.");
static int
deque_contains(dequeobject *deque, PyObject *v)
@@ -1098,8 +1102,10 @@ deque_index(dequeobject *deque, PyObject *const *args, Py_ssize_t nargs)
}
PyDoc_STRVAR(index_doc,
-"D.index(value, [start, [stop]]) -> integer -- return first index of value.\n"
-"Raises ValueError if the value is not present.");
+"index(x, [start, [stop]]) -> int\n\n"
+"Return the position of *x* in the deque "
+"(at or after index *start* and before index *stop*). "
+"Returns the first match or raises a ValueError if not found.");
/* insert(), remove(), and delitem() are implemented in terms of
rotate() for simplicity and reasonable performance near the end
@@ -1144,10 +1150,13 @@ deque_insert(dequeobject *deque, PyObject *const *args, Py_ssize_t nargs)
}
PyDoc_STRVAR(insert_doc,
-"D.insert(index, object) -- insert object before index");
+"insert(i, x)\n\n"
+"Insert *x* into the deque at position *i*.");
PyDoc_STRVAR(remove_doc,
-"D.remove(value) -- remove first occurrence of value.");
+"remove(x)\n\n"
+"Remove the first occurrence of *x*."
+"If not found, raises a ValueError.");
static int
valid_index(Py_ssize_t i, Py_ssize_t limit)
@@ -1518,7 +1527,8 @@ deque_sizeof(dequeobject *deque, void *unused)
}
PyDoc_STRVAR(sizeof_doc,
-"D.__sizeof__() -- size of D in memory, in bytes");
+"__sizeof__() -> int\n\n"
+"Size of the deque in memory, in bytes.");
static PyObject *
deque_get_maxlen(dequeobject *deque, void *Py_UNUSED(ignored))
@@ -1553,7 +1563,8 @@ static PySequenceMethods deque_as_sequence = {
static PyObject *deque_iter(dequeobject *deque);
static PyObject *deque_reviter(dequeobject *deque, PyObject *Py_UNUSED(ignored));
PyDoc_STRVAR(reversed_doc,
- "D.__reversed__() -- return a reverse iterator over the deque");
+"__reversed__()\n\n"
+"Return a reverse iterator over the deque.");
static PyMethodDef deque_methods[] = {
{"append", (PyCFunction)deque_append,
@@ -1598,9 +1609,8 @@ static PyMethodDef deque_methods[] = {
};
PyDoc_STRVAR(deque_doc,
-"deque([iterable[, maxlen]]) --> deque object\n\
-\n\
-A list-like sequence optimized for data accesses near its endpoints.");
+"deque([iterable[, maxlen]]) -> collections.deque\n\n"
+"A list-like sequence optimized for data accesses near its endpoints.");
static PyTypeObject deque_type = {
PyVarObject_HEAD_INIT(NULL, 0)
@@ -1979,7 +1989,8 @@ new_defdict(defdictobject *dd, PyObject *arg)
dd->default_factory ? dd->default_factory : Py_None, arg, NULL);
}
-PyDoc_STRVAR(defdict_copy_doc, "D.copy() -> a shallow copy of D.");
+PyDoc_STRVAR(defdict_copy_doc, "copy() -> collections.deque\n\n"
+"A shallow copy of the deque.");
static PyObject *
defdict_copy(defdictobject *dd, PyObject *Py_UNUSED(ignored))
From f59f943f907a06ffb41f540ce94659a8d3b14f92 Mon Sep 17 00:00:00 2001
From: "Erlend E. Aasland"
Date: Wed, 22 Mar 2023 12:50:00 +0100
Subject: [PATCH 181/280] Docs: improve accuracy of
sqlite3.Connection.interrupt() (#102904)
Co-authored-by: C.A.M. Gerlach
---
Doc/library/sqlite3.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index a78f3eb7221746..4b2d13ab3a8fcd 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -911,7 +911,7 @@ Connection objects
Call this method from a different thread to abort any queries that might
be executing on the connection.
- Aborted queries will raise an exception.
+ Aborted queries will raise an :exc:`OperationalError`.
.. method:: set_authorizer(authorizer_callback)
From 6a956105bc59951a1fa76d60508202826cea5fc7 Mon Sep 17 00:00:00 2001
From: David Benjamin
Date: Wed, 22 Mar 2023 08:16:26 -0400
Subject: [PATCH 182/280] GH-95494: Fix transport EOF handling in OpenSSL 3.0
(GH-95495)
GH-25309 enabled SSL_OP_IGNORE_UNEXPECTED_EOF by default, with a comment
that it restores OpenSSL 1.1.1 behavior, but this wasn't quite right.
That option causes OpenSSL to treat transport EOF as the same as
close_notify (i.e. SSL_ERROR_ZERO_RETURN), whereas Python actually has
distinct SSLEOFError and SSLZeroReturnError exceptions. (The latter is
usually mapped to a zero return from read.) In OpenSSL 1.1.1, the ssl
module would raise them for transport EOF and close_notify,
respectively. In OpenSSL 3.0, both act like close_notify.
Fix this by, instead, just detecting SSL_R_UNEXPECTED_EOF_WHILE_READING
and mapping that to the other exception type.
There doesn't seem to have been any unit test of this error, so fill in
the missing one. This had to be done with the BIO path because it's
actually slightly tricky to simulate a transport EOF with Python's fd
based APIs. (If you instruct the server to close the socket, it gets
confused, probably because the server's SSL object is still referencing
the now dead fd?)
---
Lib/test/test_ssl.py | 18 +++++++++++++++---
...22-07-30-23-01-43.gh-issue-95495.RA-q1d.rst | 7 +++++++
Modules/_ssl.c | 14 ++++++++++----
3 files changed, 32 insertions(+), 7 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2022-07-30-23-01-43.gh-issue-95495.RA-q1d.rst
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index d4eb2d2e81fe0f..76669bdac72f3f 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -151,7 +151,6 @@ def data_file(*name):
OP_SINGLE_ECDH_USE = getattr(ssl, "OP_SINGLE_ECDH_USE", 0)
OP_CIPHER_SERVER_PREFERENCE = getattr(ssl, "OP_CIPHER_SERVER_PREFERENCE", 0)
OP_ENABLE_MIDDLEBOX_COMPAT = getattr(ssl, "OP_ENABLE_MIDDLEBOX_COMPAT", 0)
-OP_IGNORE_UNEXPECTED_EOF = getattr(ssl, "OP_IGNORE_UNEXPECTED_EOF", 0)
# Ubuntu has patched OpenSSL and changed behavior of security level 2
# see https://bugs.python.org/issue41561#msg389003
@@ -958,8 +957,7 @@ def test_options(self):
# SSLContext also enables these by default
default |= (OP_NO_COMPRESSION | OP_CIPHER_SERVER_PREFERENCE |
OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE |
- OP_ENABLE_MIDDLEBOX_COMPAT |
- OP_IGNORE_UNEXPECTED_EOF)
+ OP_ENABLE_MIDDLEBOX_COMPAT)
self.assertEqual(default, ctx.options)
with warnings_helper.check_warnings():
ctx.options |= ssl.OP_NO_TLSv1
@@ -2120,6 +2118,20 @@ def test_bio_read_write_data(self):
self.assertEqual(buf, b'foo\n')
self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap)
+ def test_transport_eof(self):
+ client_context, server_context, hostname = testing_context()
+ with socket.socket(socket.AF_INET) as sock:
+ sock.connect(self.server_addr)
+ incoming = ssl.MemoryBIO()
+ outgoing = ssl.MemoryBIO()
+ sslobj = client_context.wrap_bio(incoming, outgoing,
+ server_hostname=hostname)
+ self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)
+
+ # Simulate EOF from the transport.
+ incoming.write_eof()
+ self.assertRaises(ssl.SSLEOFError, sslobj.read)
+
@support.requires_resource('network')
class NetworkedTests(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Library/2022-07-30-23-01-43.gh-issue-95495.RA-q1d.rst b/Misc/NEWS.d/next/Library/2022-07-30-23-01-43.gh-issue-95495.RA-q1d.rst
new file mode 100644
index 00000000000000..d0f4ccbdd3e39f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-07-30-23-01-43.gh-issue-95495.RA-q1d.rst
@@ -0,0 +1,7 @@
+When built against OpenSSL 3.0, the :mod:`ssl` module had a bug where it
+reported unauthenticated EOFs (i.e. without close_notify) as a clean TLS-level
+EOF. It now raises :exc:`~ssl.SSLEOFError`, matching the behavior in previous
+versions of OpenSSL. The :attr:`~ssl.SSLContext.options` attribute on
+:class:`~ssl.SSLContext` also no longer includes
+:data:`~ssl.OP_IGNORE_UNEXPECTED_EOF` by default. This option may be set to
+specify the previous OpenSSL 3.0 behavior.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 121d18884d0a9f..321b6ec52b7775 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -660,6 +660,16 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
ERR_GET_REASON(e) == SSL_R_CERTIFICATE_VERIFY_FAILED) {
type = state->PySSLCertVerificationErrorObject;
}
+#if defined(SSL_R_UNEXPECTED_EOF_WHILE_READING)
+ /* OpenSSL 3.0 changed transport EOF from SSL_ERROR_SYSCALL with
+ * zero return value to SSL_ERROR_SSL with a special error code. */
+ if (ERR_GET_LIB(e) == ERR_LIB_SSL &&
+ ERR_GET_REASON(e) == SSL_R_UNEXPECTED_EOF_WHILE_READING) {
+ p = PY_SSL_ERROR_EOF;
+ type = state->PySSLEOFErrorObject;
+ errstr = "EOF occurred in violation of protocol";
+ }
+#endif
break;
}
default:
@@ -3092,10 +3102,6 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
#endif
#ifdef SSL_OP_SINGLE_ECDH_USE
options |= SSL_OP_SINGLE_ECDH_USE;
-#endif
-#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
- /* Make OpenSSL 3.0.0 behave like 1.1.1 */
- options |= SSL_OP_IGNORE_UNEXPECTED_EOF;
#endif
SSL_CTX_set_options(self->ctx, options);
From f0e93195a08882b610a32ed54766dbcf340c0a73 Mon Sep 17 00:00:00 2001
From: Icelain
Date: Wed, 22 Mar 2023 17:49:52 +0530
Subject: [PATCH 183/280] gh-101313: Add -h and --help arguments to the
webbrowser module (gh-101374)
---
Lib/webbrowser.py | 10 +++++++---
.../2023-01-27-14-51-07.gh-issue-101313.10AEXh.rst | 1 +
2 files changed, 8 insertions(+), 3 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-01-27-14-51-07.gh-issue-101313.10AEXh.rst
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
index a56ff33dbbdc69..4336597e68f625 100755
--- a/Lib/webbrowser.py
+++ b/Lib/webbrowser.py
@@ -713,11 +713,12 @@ def open(self, url, new=0, autoraise=True):
def main():
import getopt
- usage = """Usage: %s [-n | -t] url
+ usage = """Usage: %s [-n | -t | -h] url
-n: open new window
- -t: open new tab""" % sys.argv[0]
+ -t: open new tab
+ -h, --help: show help""" % sys.argv[0]
try:
- opts, args = getopt.getopt(sys.argv[1:], 'ntd')
+ opts, args = getopt.getopt(sys.argv[1:], 'ntdh',['help'])
except getopt.error as msg:
print(msg, file=sys.stderr)
print(usage, file=sys.stderr)
@@ -726,6 +727,9 @@ def main():
for o, a in opts:
if o == '-n': new_win = 1
elif o == '-t': new_win = 2
+ elif o == '-h' or o == '--help':
+ print(usage, file=sys.stderr)
+ sys.exit()
if len(args) != 1:
print(usage, file=sys.stderr)
sys.exit(1)
diff --git a/Misc/NEWS.d/next/Library/2023-01-27-14-51-07.gh-issue-101313.10AEXh.rst b/Misc/NEWS.d/next/Library/2023-01-27-14-51-07.gh-issue-101313.10AEXh.rst
new file mode 100644
index 00000000000000..63d0a7286920a4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-01-27-14-51-07.gh-issue-101313.10AEXh.rst
@@ -0,0 +1 @@
+Added -h and --help arguments to the webbrowser CLI
From b6098c5a1f0633fd1cbb703adc027a47e4490a36 Mon Sep 17 00:00:00 2001
From: "Erlend E. Aasland"
Date: Wed, 22 Mar 2023 14:05:08 +0100
Subject: [PATCH 184/280] gh-101947: Remove size check from sqlite3 serialize
test (#102914)
The size of the returned data is too implementation specific.
---
Lib/test/test_sqlite3/test_dbapi.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py
index 695e213cdc7b75..3013abfa730ed5 100644
--- a/Lib/test/test_sqlite3/test_dbapi.py
+++ b/Lib/test/test_sqlite3/test_dbapi.py
@@ -606,7 +606,6 @@ def test_serialize_deserialize(self):
with cx:
cx.execute("create table t(t)")
data = cx.serialize()
- self.assertEqual(len(data), 8192)
# Remove test table, verify that it was removed.
with cx:
From f30f362ffc4092d8e0f9f026a79d91f1811e4065 Mon Sep 17 00:00:00 2001
From: Max Bachmann
Date: Wed, 22 Mar 2023 14:44:28 +0100
Subject: [PATCH 185/280] gh-102027: Fix macro name (#102124)
This fixes the ssse3 / sse2 detection when sse4 is available.
Co-authored-by: Oleg Iarygin
---
.../2023-02-21-23-42-39.gh-issue-102027.fQARG0.rst | 2 ++
Modules/_blake2/impl/blake2-config.h | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-02-21-23-42-39.gh-issue-102027.fQARG0.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-02-21-23-42-39.gh-issue-102027.fQARG0.rst b/Misc/NEWS.d/next/Core and Builtins/2023-02-21-23-42-39.gh-issue-102027.fQARG0.rst
new file mode 100644
index 00000000000000..42d96b54677e41
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-02-21-23-42-39.gh-issue-102027.fQARG0.rst
@@ -0,0 +1,2 @@
+Fix SSE2 and SSE3 detection in ``_blake2`` internal module. Patch by Max
+Bachmann.
diff --git a/Modules/_blake2/impl/blake2-config.h b/Modules/_blake2/impl/blake2-config.h
index f5dd6faa9e6867..c09cb4bcf06723 100644
--- a/Modules/_blake2/impl/blake2-config.h
+++ b/Modules/_blake2/impl/blake2-config.h
@@ -53,7 +53,7 @@
#endif
#endif
-#ifdef HAVE_SSE41
+#ifdef HAVE_SSE4_1
#ifndef HAVE_SSSE3
#define HAVE_SSSE3
#endif
From 233ef6d7bb72bf8c9e0ec0173f182ee0a2c41e98 Mon Sep 17 00:00:00 2001
From: Benjamin Fogle
Date: Wed, 22 Mar 2023 10:08:41 -0400
Subject: [PATCH 186/280] gh-96931: Fix incorrect results in
ssl.SSLSocket.shared_ciphers (#96932)
---
Doc/library/ssl.rst | 2 +-
Lib/test/test_ssl.py | 6 ++--
...2-09-19-08-12-58.gh-issue-96931.x0WQhh.rst | 1 +
Modules/_ssl.c | 36 ++++++++++++++-----
4 files changed, 33 insertions(+), 12 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2022-09-19-08-12-58.gh-issue-96931.x0WQhh.rst
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 30f2a0765cc955..4b60b7c643b62c 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -1218,7 +1218,7 @@ SSL sockets also have the following additional methods and attributes:
.. method:: SSLSocket.shared_ciphers()
- Return the list of ciphers shared by the client during the handshake. Each
+ Return the list of ciphers available in both the client and server. Each
entry of the returned list is a three-value tuple containing the name of the
cipher, the version of the SSL protocol that defines its use, and the number
of secret bits the cipher uses. :meth:`~SSLSocket.shared_ciphers` returns
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 76669bdac72f3f..1317efb33c2306 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -2082,13 +2082,13 @@ def test_bio_handshake(self):
self.assertIs(sslobj._sslobj.owner, sslobj)
self.assertIsNone(sslobj.cipher())
self.assertIsNone(sslobj.version())
- self.assertIsNotNone(sslobj.shared_ciphers())
+ self.assertIsNone(sslobj.shared_ciphers())
self.assertRaises(ValueError, sslobj.getpeercert)
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
self.assertIsNone(sslobj.get_channel_binding('tls-unique'))
self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)
self.assertTrue(sslobj.cipher())
- self.assertIsNotNone(sslobj.shared_ciphers())
+ self.assertIsNone(sslobj.shared_ciphers())
self.assertIsNotNone(sslobj.version())
self.assertTrue(sslobj.getpeercert())
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
@@ -4051,7 +4051,7 @@ def cb_wrong_return_type(ssl_sock, server_name, initial_context):
def test_shared_ciphers(self):
client_context, server_context, hostname = testing_context()
client_context.set_ciphers("AES128:AES256")
- server_context.set_ciphers("AES256")
+ server_context.set_ciphers("AES256:eNULL")
expected_algs = [
"AES256", "AES-256",
# TLS 1.3 ciphers are always enabled
diff --git a/Misc/NEWS.d/next/Library/2022-09-19-08-12-58.gh-issue-96931.x0WQhh.rst b/Misc/NEWS.d/next/Library/2022-09-19-08-12-58.gh-issue-96931.x0WQhh.rst
new file mode 100644
index 00000000000000..766b1d4d477b72
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-09-19-08-12-58.gh-issue-96931.x0WQhh.rst
@@ -0,0 +1 @@
+Fix incorrect results from :meth:`ssl.SSLSocket.shared_ciphers`
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 321b6ec52b7775..36b66cdb5310c6 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -1998,24 +1998,44 @@ static PyObject *
_ssl__SSLSocket_shared_ciphers_impl(PySSLSocket *self)
/*[clinic end generated code: output=3d174ead2e42c4fd input=0bfe149da8fe6306]*/
{
- STACK_OF(SSL_CIPHER) *ciphers;
- int i;
+ STACK_OF(SSL_CIPHER) *server_ciphers;
+ STACK_OF(SSL_CIPHER) *client_ciphers;
+ int i, len;
PyObject *res;
+ const SSL_CIPHER* cipher;
+
+ /* Rather than use SSL_get_shared_ciphers, we use an equivalent algorithm because:
+
+ 1) It returns a colon seperated list of strings, in an undefined
+ order, that we would have to post process back into tuples.
+ 2) It will return a truncated string with no indication that it has
+ done so, if the buffer is too small.
+ */
- ciphers = SSL_get_ciphers(self->ssl);
- if (!ciphers)
+ server_ciphers = SSL_get_ciphers(self->ssl);
+ if (!server_ciphers)
Py_RETURN_NONE;
- res = PyList_New(sk_SSL_CIPHER_num(ciphers));
+ client_ciphers = SSL_get_client_ciphers(self->ssl);
+ if (!client_ciphers)
+ Py_RETURN_NONE;
+
+ res = PyList_New(sk_SSL_CIPHER_num(server_ciphers));
if (!res)
return NULL;
- for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
- PyObject *tup = cipher_to_tuple(sk_SSL_CIPHER_value(ciphers, i));
+ len = 0;
+ for (i = 0; i < sk_SSL_CIPHER_num(server_ciphers); i++) {
+ cipher = sk_SSL_CIPHER_value(server_ciphers, i);
+ if (sk_SSL_CIPHER_find(client_ciphers, cipher) < 0)
+ continue;
+
+ PyObject *tup = cipher_to_tuple(cipher);
if (!tup) {
Py_DECREF(res);
return NULL;
}
- PyList_SET_ITEM(res, i, tup);
+ PyList_SET_ITEM(res, len++, tup);
}
+ Py_SET_SIZE(res, len);
return res;
}
From b645f1b925cfc3b869bb0ec1e4ea6243848f0ed7 Mon Sep 17 00:00:00 2001
From: Stanislav Zmiev
Date: Wed, 22 Mar 2023 18:45:25 +0400
Subject: [PATCH 187/280] GH-89727: Fix pathlib.Path.walk RecursionError on
deep trees (GH-100282)
Use a stack to implement `pathlib.Path.walk()` iteratively instead of recursively to avoid hitting recursion limits on deeply nested trees.
Co-authored-by: Barney Gale
Co-authored-by: Brett Cannon
---
Lib/pathlib.py | 78 ++++++++++---------
Lib/test/test_pathlib.py | 13 ++++
...2-12-16-10-27-58.gh-issue-89727.y64ZLM.rst | 1 +
3 files changed, 54 insertions(+), 38 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2022-12-16-10-27-58.gh-issue-89727.y64ZLM.rst
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
index 55c44f12e5a2fb..a126bf2fe5570a 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -1197,45 +1197,47 @@ def expanduser(self):
def walk(self, top_down=True, on_error=None, follow_symlinks=False):
"""Walk the directory tree from this directory, similar to os.walk()."""
sys.audit("pathlib.Path.walk", self, on_error, follow_symlinks)
- return self._walk(top_down, on_error, follow_symlinks)
-
- def _walk(self, top_down, on_error, follow_symlinks):
- # We may not have read permission for self, in which case we can't
- # get a list of the files the directory contains. os.walk
- # always suppressed the exception then, rather than blow up for a
- # minor reason when (say) a thousand readable directories are still
- # left to visit. That logic is copied here.
- try:
- scandir_it = self._scandir()
- except OSError as error:
- if on_error is not None:
- on_error(error)
- return
-
- with scandir_it:
- dirnames = []
- filenames = []
- for entry in scandir_it:
- try:
- is_dir = entry.is_dir(follow_symlinks=follow_symlinks)
- except OSError:
- # Carried over from os.path.isdir().
- is_dir = False
-
- if is_dir:
- dirnames.append(entry.name)
- else:
- filenames.append(entry.name)
-
- if top_down:
- yield self, dirnames, filenames
-
- for dirname in dirnames:
- dirpath = self._make_child_relpath(dirname)
- yield from dirpath._walk(top_down, on_error, follow_symlinks)
+ paths = [self]
+
+ while paths:
+ path = paths.pop()
+ if isinstance(path, tuple):
+ yield path
+ continue
+
+ # We may not have read permission for self, in which case we can't
+ # get a list of the files the directory contains. os.walk()
+ # always suppressed the exception in that instance, rather than
+ # blow up for a minor reason when (say) a thousand readable
+ # directories are still left to visit. That logic is copied here.
+ try:
+ scandir_it = path._scandir()
+ except OSError as error:
+ if on_error is not None:
+ on_error(error)
+ continue
+
+ with scandir_it:
+ dirnames = []
+ filenames = []
+ for entry in scandir_it:
+ try:
+ is_dir = entry.is_dir(follow_symlinks=follow_symlinks)
+ except OSError:
+ # Carried over from os.path.isdir().
+ is_dir = False
+
+ if is_dir:
+ dirnames.append(entry.name)
+ else:
+ filenames.append(entry.name)
+
+ if top_down:
+ yield path, dirnames, filenames
+ else:
+ paths.append((path, dirnames, filenames))
- if not top_down:
- yield self, dirnames, filenames
+ paths += [path._make_child_relpath(d) for d in reversed(dirnames)]
class PosixPath(Path, PurePosixPath):
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
index f05dead5886743..3041630da67899 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -13,6 +13,7 @@
from unittest import mock
from test.support import import_helper
+from test.support import set_recursion_limit
from test.support import is_emscripten, is_wasi
from test.support import os_helper
from test.support.os_helper import TESTFN, FakePath
@@ -2793,6 +2794,18 @@ def test_walk_many_open_files(self):
self.assertEqual(next(it), expected)
path = path / 'd'
+ def test_walk_above_recursion_limit(self):
+ recursion_limit = 40
+ # directory_depth > recursion_limit
+ directory_depth = recursion_limit + 10
+ base = pathlib.Path(os_helper.TESTFN, 'deep')
+ path = pathlib.Path(base, *(['d'] * directory_depth))
+ path.mkdir(parents=True)
+
+ with set_recursion_limit(recursion_limit):
+ list(base.walk())
+ list(base.walk(top_down=False))
+
class PathTest(_BasePathTest, unittest.TestCase):
cls = pathlib.Path
diff --git a/Misc/NEWS.d/next/Library/2022-12-16-10-27-58.gh-issue-89727.y64ZLM.rst b/Misc/NEWS.d/next/Library/2022-12-16-10-27-58.gh-issue-89727.y64ZLM.rst
new file mode 100644
index 00000000000000..f9ac1475dceb00
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-12-16-10-27-58.gh-issue-89727.y64ZLM.rst
@@ -0,0 +1 @@
+Fix pathlib.Path.walk RecursionError on deep directory trees by rewriting it using iteration instead of recursion.
From 32f8276e679d5a03fec35867f5691a31e8f136e8 Mon Sep 17 00:00:00 2001
From: Mark Shannon
Date: Wed, 22 Mar 2023 14:49:51 +0000
Subject: [PATCH 188/280] GH-101291: Rearrange the size bits in PyLongObject
(GH-102464)
* Eliminate all remaining uses of Py_SIZE and Py_SET_SIZE on PyLongObject, adding asserts.
* Change layout of size/sign bits in longobject to support future addition of immortal ints and tagged medium ints.
* Add functions to hide some internals of long object, and for setting sign and digit count.
* Replace uses of IS_MEDIUM_VALUE macro with _PyLong_IsCompact().
---
Include/cpython/longintrepr.h | 6 +-
Include/internal/pycore_long.h | 164 ++++-
Include/internal/pycore_object.h | 3 +-
Include/internal/pycore_runtime_init.h | 10 +-
Include/object.h | 8 +-
...-03-06-10-02-22.gh-issue-101291.0FT2QS.rst | 7 +
Modules/_decimal/_decimal.c | 43 +-
Modules/_testcapi/mem.c | 2 +-
Modules/_tkinter.c | 7 +-
Modules/mathmodule.c | 19 +-
Objects/abstract.c | 3 +-
Objects/boolobject.c | 9 +-
Objects/listobject.c | 20 +-
Objects/longobject.c | 694 ++++++++----------
Objects/rangeobject.c | 2 +-
Objects/sliceobject.c | 6 +-
Objects/typeobject.c | 3 +-
Python/ast_opt.c | 13 +-
Python/bltinmodule.c | 16 +-
Python/bytecodes.c | 19 +-
Python/generated_cases.c.h | 19 +-
Python/marshal.c | 9 +-
Python/specialize.c | 8 +-
Tools/build/deepfreeze.py | 10 +-
Tools/gdb/libpython.py | 26 +-
25 files changed, 605 insertions(+), 521 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-06-10-02-22.gh-issue-101291.0FT2QS.rst
diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h
index 810daa83165e71..c4cf820da5e4f2 100644
--- a/Include/cpython/longintrepr.h
+++ b/Include/cpython/longintrepr.h
@@ -80,7 +80,7 @@ typedef long stwodigits; /* signed variant of twodigits */
*/
typedef struct _PyLongValue {
- Py_ssize_t ob_size; /* Number of items in variable part */
+ uintptr_t lv_tag; /* Number of digits, sign and flags */
digit ob_digit[1];
} _PyLongValue;
@@ -94,6 +94,10 @@ PyAPI_FUNC(PyLongObject *) _PyLong_New(Py_ssize_t);
/* Return a copy of src. */
PyAPI_FUNC(PyObject *) _PyLong_Copy(PyLongObject *src);
+PyAPI_FUNC(PyLongObject *)
+_PyLong_FromDigits(int negative, Py_ssize_t digit_count, digit *digits);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h
index 8c1d017bb95e4e..137a0465d5ec60 100644
--- a/Include/internal/pycore_long.h
+++ b/Include/internal/pycore_long.h
@@ -82,8 +82,6 @@ PyObject *_PyLong_Add(PyLongObject *left, PyLongObject *right);
PyObject *_PyLong_Multiply(PyLongObject *left, PyLongObject *right);
PyObject *_PyLong_Subtract(PyLongObject *left, PyLongObject *right);
-int _PyLong_AssignValue(PyObject **target, Py_ssize_t value);
-
/* Used by Python/mystrtoul.c, _PyBytes_FromHex(),
_PyBytes_DecodeEscape(), etc. */
PyAPI_DATA(unsigned char) _PyLong_DigitValue[256];
@@ -110,25 +108,155 @@ PyAPI_FUNC(char*) _PyLong_FormatBytesWriter(
int base,
int alternate);
-/* Return 1 if the argument is positive single digit int */
+/* Long value tag bits:
+ * 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1.
+ * 2: Reserved for immortality bit
+ * 3+ Unsigned digit count
+ */
+#define SIGN_MASK 3
+#define SIGN_ZERO 1
+#define SIGN_NEGATIVE 2
+#define NON_SIZE_BITS 3
+
+/* All *compact" values are guaranteed to fit into
+ * a Py_ssize_t with at least one bit to spare.
+ * In other words, for 64 bit machines, compact
+ * will be signed 63 (or fewer) bit values
+ */
+
+/* Return 1 if the argument is compact int */
+static inline int
+_PyLong_IsNonNegativeCompact(const PyLongObject* op) {
+ assert(PyLong_Check(op));
+ return op->long_value.lv_tag <= (1 << NON_SIZE_BITS);
+}
+
+static inline int
+_PyLong_IsCompact(const PyLongObject* op) {
+ assert(PyLong_Check(op));
+ return op->long_value.lv_tag < (2 << NON_SIZE_BITS);
+}
+
static inline int
-_PyLong_IsPositiveSingleDigit(PyObject* sub) {
- /* For a positive single digit int, the value of Py_SIZE(sub) is 0 or 1.
-
- We perform a fast check using a single comparison by casting from int
- to uint which casts negative numbers to large positive numbers.
- For details see Section 14.2 "Bounds Checking" in the Agner Fog
- optimization manual found at:
- https://www.agner.org/optimize/optimizing_cpp.pdf
-
- The function is not affected by -fwrapv, -fno-wrapv and -ftrapv
- compiler options of GCC and clang
- */
- assert(PyLong_CheckExact(sub));
- Py_ssize_t signed_size = Py_SIZE(sub);
- return ((size_t)signed_size) <= 1;
+_PyLong_BothAreCompact(const PyLongObject* a, const PyLongObject* b) {
+ assert(PyLong_Check(a));
+ assert(PyLong_Check(b));
+ return (a->long_value.lv_tag | b->long_value.lv_tag) < (2 << NON_SIZE_BITS);
+}
+
+/* Returns a *compact* value, iff `_PyLong_IsCompact` is true for `op`.
+ *
+ * "Compact" values have at least one bit to spare,
+ * so that addition and subtraction can be performed on the values
+ * without risk of overflow.
+ */
+static inline Py_ssize_t
+_PyLong_CompactValue(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ assert(_PyLong_IsCompact(op));
+ Py_ssize_t sign = 1 - (op->long_value.lv_tag & SIGN_MASK);
+ return sign * (Py_ssize_t)op->long_value.ob_digit[0];
+}
+
+static inline bool
+_PyLong_IsZero(const PyLongObject *op)
+{
+ return (op->long_value.lv_tag & SIGN_MASK) == SIGN_ZERO;
+}
+
+static inline bool
+_PyLong_IsNegative(const PyLongObject *op)
+{
+ return (op->long_value.lv_tag & SIGN_MASK) == SIGN_NEGATIVE;
+}
+
+static inline bool
+_PyLong_IsPositive(const PyLongObject *op)
+{
+ return (op->long_value.lv_tag & SIGN_MASK) == 0;
+}
+
+static inline Py_ssize_t
+_PyLong_DigitCount(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ return op->long_value.lv_tag >> NON_SIZE_BITS;
}
+/* Equivalent to _PyLong_DigitCount(op) * _PyLong_NonCompactSign(op) */
+static inline Py_ssize_t
+_PyLong_SignedDigitCount(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ Py_ssize_t sign = 1 - (op->long_value.lv_tag & SIGN_MASK);
+ return sign * (Py_ssize_t)(op->long_value.lv_tag >> NON_SIZE_BITS);
+}
+
+static inline int
+_PyLong_CompactSign(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ assert(_PyLong_IsCompact(op));
+ return 1 - (op->long_value.lv_tag & SIGN_MASK);
+}
+
+static inline int
+_PyLong_NonCompactSign(const PyLongObject *op)
+{
+ assert(PyLong_Check(op));
+ assert(!_PyLong_IsCompact(op));
+ return 1 - (op->long_value.lv_tag & SIGN_MASK);
+}
+
+/* Do a and b have the same sign? */
+static inline int
+_PyLong_SameSign(const PyLongObject *a, const PyLongObject *b)
+{
+ return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK);
+}
+
+#define TAG_FROM_SIGN_AND_SIZE(sign, size) ((1 - (sign)) | ((size) << NON_SIZE_BITS))
+
+static inline void
+_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size)
+{
+ assert(size >= 0);
+ assert(-1 <= sign && sign <= 1);
+ assert(sign != 0 || size == 0);
+ op->long_value.lv_tag = TAG_FROM_SIGN_AND_SIZE(sign, (size_t)size);
+}
+
+static inline void
+_PyLong_SetDigitCount(PyLongObject *op, Py_ssize_t size)
+{
+ assert(size >= 0);
+ op->long_value.lv_tag = (((size_t)size) << NON_SIZE_BITS) | (op->long_value.lv_tag & SIGN_MASK);
+}
+
+#define NON_SIZE_MASK ~((1 << NON_SIZE_BITS) - 1)
+
+static inline void
+_PyLong_FlipSign(PyLongObject *op) {
+ unsigned int flipped_sign = 2 - (op->long_value.lv_tag & SIGN_MASK);
+ op->long_value.lv_tag &= NON_SIZE_MASK;
+ op->long_value.lv_tag |= flipped_sign;
+}
+
+#define _PyLong_DIGIT_INIT(val) \
+ { \
+ .ob_base = _PyObject_IMMORTAL_INIT(&PyLong_Type), \
+ .long_value = { \
+ .lv_tag = TAG_FROM_SIGN_AND_SIZE( \
+ (val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \
+ (val) == 0 ? 0 : 1), \
+ { ((val) >= 0 ? (val) : -(val)) }, \
+ } \
+ }
+
+#define _PyLong_FALSE_TAG TAG_FROM_SIGN_AND_SIZE(0, 0)
+#define _PyLong_TRUE_TAG TAG_FROM_SIGN_AND_SIZE(1, 1)
+
#ifdef __cplusplus
}
#endif
diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index d6bbafd4b6cccc..e18e787449c257 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -137,8 +137,9 @@ static inline void
_PyObject_InitVar(PyVarObject *op, PyTypeObject *typeobj, Py_ssize_t size)
{
assert(op != NULL);
- Py_SET_SIZE(op, size);
+ assert(typeobj != &PyLong_Type);
_PyObject_Init((PyObject *)op, typeobj);
+ Py_SET_SIZE(op, size);
}
diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h
index bdecac944dfd3a..7cfa7c0c02494a 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -8,6 +8,7 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define"
#endif
+#include "pycore_long.h"
#include "pycore_object.h"
#include "pycore_parser.h"
#include "pycore_pymem_init.h"
@@ -130,15 +131,6 @@ extern PyTypeObject _PyExc_MemoryError;
// global objects
-#define _PyLong_DIGIT_INIT(val) \
- { \
- .ob_base = _PyObject_IMMORTAL_INIT(&PyLong_Type), \
- .long_value = { \
- ((val) == 0 ? 0 : ((val) > 0 ? 1 : -1)), \
- { ((val) >= 0 ? (val) : -(val)) }, \
- } \
- }
-
#define _PyBytes_SIMPLE_INIT(CH, LEN) \
{ \
_PyVarObject_IMMORTAL_INIT(&PyBytes_Type, (LEN)), \
diff --git a/Include/object.h b/Include/object.h
index fc577353c1cc13..2943a6066818cd 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -138,8 +138,13 @@ static inline PyTypeObject* Py_TYPE(PyObject *ob) {
# define Py_TYPE(ob) Py_TYPE(_PyObject_CAST(ob))
#endif
+PyAPI_DATA(PyTypeObject) PyLong_Type;
+PyAPI_DATA(PyTypeObject) PyBool_Type;
+
// bpo-39573: The Py_SET_SIZE() function must be used to set an object size.
static inline Py_ssize_t Py_SIZE(PyObject *ob) {
+ assert(ob->ob_type != &PyLong_Type);
+ assert(ob->ob_type != &PyBool_Type);
PyVarObject *var_ob = _PyVarObject_CAST(ob);
return var_ob->ob_size;
}
@@ -171,8 +176,9 @@ static inline void Py_SET_TYPE(PyObject *ob, PyTypeObject *type) {
# define Py_SET_TYPE(ob, type) Py_SET_TYPE(_PyObject_CAST(ob), type)
#endif
-
static inline void Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) {
+ assert(ob->ob_base.ob_type != &PyLong_Type);
+ assert(ob->ob_base.ob_type != &PyBool_Type);
ob->ob_size = size;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-06-10-02-22.gh-issue-101291.0FT2QS.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-06-10-02-22.gh-issue-101291.0FT2QS.rst
new file mode 100644
index 00000000000000..46f0f325f91630
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-06-10-02-22.gh-issue-101291.0FT2QS.rst
@@ -0,0 +1,7 @@
+Rearrage bits in first field (after header) of PyLongObject.
+* Bits 0 and 1: 1 - sign. I.e. 0 for positive numbers, 1 for zero and 2 for negative numbers.
+* Bit 2 reserved (probably for the immortal bit)
+* Bits 3+ the unsigned size.
+
+This makes a few operations slightly more efficient, and will enable a more
+compact and faster 2s-complement representation of most ints in future.
diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c
index 5936fbaaf35eb0..0e11c879732ab6 100644
--- a/Modules/_decimal/_decimal.c
+++ b/Modules/_decimal/_decimal.c
@@ -30,6 +30,7 @@
#endif
#include
+#include "pycore_long.h" // _PyLong_IsZero()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "complexobject.h"
#include "mpdecimal.h"
@@ -2146,35 +2147,25 @@ dec_from_long(PyTypeObject *type, PyObject *v,
{
PyObject *dec;
PyLongObject *l = (PyLongObject *)v;
- Py_ssize_t ob_size;
- size_t len;
- uint8_t sign;
dec = PyDecType_New(type);
if (dec == NULL) {
return NULL;
}
- ob_size = Py_SIZE(l);
- if (ob_size == 0) {
+ if (_PyLong_IsZero(l)) {
_dec_settriple(dec, MPD_POS, 0, 0);
return dec;
}
- if (ob_size < 0) {
- len = -ob_size;
- sign = MPD_NEG;
- }
- else {
- len = ob_size;
- sign = MPD_POS;
- }
+ uint8_t sign = _PyLong_IsNegative(l) ? MPD_NEG : MPD_POS;
- if (len == 1) {
- _dec_settriple(dec, sign, *l->long_value.ob_digit, 0);
+ if (_PyLong_IsCompact(l)) {
+ _dec_settriple(dec, sign, l->long_value.ob_digit[0], 0);
mpd_qfinalize(MPD(dec), ctx, status);
return dec;
}
+ size_t len = _PyLong_DigitCount(l);
#if PYLONG_BITS_IN_DIGIT == 30
mpd_qimport_u32(MPD(dec), l->long_value.ob_digit, len, sign, PyLong_BASE,
@@ -3482,7 +3473,6 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
PyLongObject *pylong;
digit *ob_digit;
size_t n;
- Py_ssize_t i;
mpd_t *x;
mpd_context_t workctx;
uint32_t status = 0;
@@ -3536,26 +3526,9 @@ dec_as_long(PyObject *dec, PyObject *context, int round)
}
assert(n > 0);
- pylong = _PyLong_New(n);
- if (pylong == NULL) {
- mpd_free(ob_digit);
- mpd_del(x);
- return NULL;
- }
-
- memcpy(pylong->long_value.ob_digit, ob_digit, n * sizeof(digit));
+ assert(!mpd_iszero(x));
+ pylong = _PyLong_FromDigits(mpd_isnegative(x), n, ob_digit);
mpd_free(ob_digit);
-
- i = n;
- while ((i > 0) && (pylong->long_value.ob_digit[i-1] == 0)) {
- i--;
- }
-
- Py_SET_SIZE(pylong, i);
- if (mpd_isnegative(x) && !mpd_iszero(x)) {
- Py_SET_SIZE(pylong, -i);
- }
-
mpd_del(x);
return (PyObject *) pylong;
}
diff --git a/Modules/_testcapi/mem.c b/Modules/_testcapi/mem.c
index ae3f7a4372dcd8..af32e9668dda2d 100644
--- a/Modules/_testcapi/mem.c
+++ b/Modules/_testcapi/mem.c
@@ -347,7 +347,7 @@ test_pyobject_new(PyObject *self, PyObject *Py_UNUSED(ignored))
{
PyObject *obj;
PyTypeObject *type = &PyBaseObject_Type;
- PyTypeObject *var_type = &PyLong_Type;
+ PyTypeObject *var_type = &PyBytes_Type;
// PyObject_New()
obj = PyObject_New(PyObject, type);
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index 1608939766ffb6..606e578a1f3116 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -32,6 +32,8 @@ Copyright (C) 1994 Steen Lumholt.
# include "pycore_fileutils.h" // _Py_stat()
#endif
+#include "pycore_long.h"
+
#ifdef MS_WINDOWS
#include
#endif
@@ -886,7 +888,8 @@ asBignumObj(PyObject *value)
const char *hexchars;
mp_int bigValue;
- neg = Py_SIZE(value) < 0;
+ assert(PyLong_Check(value));
+ neg = _PyLong_IsNegative((PyLongObject *)value);
hexstr = _PyLong_Format(value, 16);
if (hexstr == NULL)
return NULL;
@@ -1950,7 +1953,7 @@ _tkinter_tkapp_getboolean(TkappObject *self, PyObject *arg)
int v;
if (PyLong_Check(arg)) { /* int or bool */
- return PyBool_FromLong(Py_SIZE(arg) != 0);
+ return PyBool_FromLong(!_PyLong_IsZero((PyLongObject *)arg));
}
if (PyTclObject_Check(arg)) {
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 473936edb3f5cd..eddc1a33a953e6 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -836,7 +836,7 @@ long_lcm(PyObject *a, PyObject *b)
{
PyObject *g, *m, *f, *ab;
- if (Py_SIZE(a) == 0 || Py_SIZE(b) == 0) {
+ if (_PyLong_IsZero((PyLongObject *)a) || _PyLong_IsZero((PyLongObject *)b)) {
return PyLong_FromLong(0);
}
g = _PyLong_GCD(a, b);
@@ -1726,13 +1726,13 @@ math_isqrt(PyObject *module, PyObject *n)
return NULL;
}
- if (_PyLong_Sign(n) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)n)) {
PyErr_SetString(
PyExc_ValueError,
"isqrt() argument must be nonnegative");
goto error;
}
- if (_PyLong_Sign(n) == 0) {
+ if (_PyLong_IsZero((PyLongObject *)n)) {
Py_DECREF(n);
return PyLong_FromLong(0);
}
@@ -2254,7 +2254,7 @@ loghelper(PyObject* arg, double (*func)(double))
Py_ssize_t e;
/* Negative or zero inputs give a ValueError. */
- if (Py_SIZE(arg) <= 0) {
+ if (!_PyLong_IsPositive((PyLongObject *)arg)) {
PyErr_SetString(PyExc_ValueError,
"math domain error");
return NULL;
@@ -3716,12 +3716,12 @@ math_perm_impl(PyObject *module, PyObject *n, PyObject *k)
}
assert(PyLong_CheckExact(n) && PyLong_CheckExact(k));
- if (Py_SIZE(n) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)n)) {
PyErr_SetString(PyExc_ValueError,
"n must be a non-negative integer");
goto error;
}
- if (Py_SIZE(k) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)k)) {
PyErr_SetString(PyExc_ValueError,
"k must be a non-negative integer");
goto error;
@@ -3808,12 +3808,12 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k)
}
assert(PyLong_CheckExact(n) && PyLong_CheckExact(k));
- if (Py_SIZE(n) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)n)) {
PyErr_SetString(PyExc_ValueError,
"n must be a non-negative integer");
goto error;
}
- if (Py_SIZE(k) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)k)) {
PyErr_SetString(PyExc_ValueError,
"k must be a non-negative integer");
goto error;
@@ -3845,7 +3845,8 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k)
if (temp == NULL) {
goto error;
}
- if (Py_SIZE(temp) < 0) {
+ assert(PyLong_Check(temp));
+ if (_PyLong_IsNegative((PyLongObject *)temp)) {
Py_DECREF(temp);
result = PyLong_FromLong(0);
goto done;
diff --git a/Objects/abstract.c b/Objects/abstract.c
index 9dc74fb9c2608c..e95785900c9c5f 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -5,6 +5,7 @@
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
#include "pycore_object.h" // _Py_CheckSlotResult()
+#include "pycore_long.h" // _Py_IsNegative
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_unionobject.h" // _PyUnion_Check()
@@ -1483,7 +1484,7 @@ PyNumber_AsSsize_t(PyObject *item, PyObject *err)
/* Whether or not it is less than or equal to
zero is determined by the sign of ob_size
*/
- if (_PyLong_Sign(value) < 0)
+ if (_PyLong_IsNegative((PyLongObject *)value))
result = PY_SSIZE_T_MIN;
else
result = PY_SSIZE_T_MAX;
diff --git a/Objects/boolobject.c b/Objects/boolobject.c
index a035f463323823..9d8e956e06f712 100644
--- a/Objects/boolobject.c
+++ b/Objects/boolobject.c
@@ -2,6 +2,7 @@
#include "Python.h"
#include "pycore_object.h" // _Py_FatalRefcountError()
+#include "pycore_long.h" // FALSE_TAG TRUE_TAG
#include "pycore_runtime.h" // _Py_ID()
#include
@@ -198,10 +199,14 @@ PyTypeObject PyBool_Type = {
struct _longobject _Py_FalseStruct = {
PyObject_HEAD_INIT(&PyBool_Type)
- { 0, { 0 } }
+ { .lv_tag = _PyLong_FALSE_TAG,
+ { 0 }
+ }
};
struct _longobject _Py_TrueStruct = {
PyObject_HEAD_INIT(&PyBool_Type)
- { 1, { 1 } }
+ { .lv_tag = _PyLong_TRUE_TAG,
+ { 1 }
+ }
};
diff --git a/Objects/listobject.c b/Objects/listobject.c
index 1a210e77d55c29..f1edfb3a9a039d 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -4,6 +4,7 @@
#include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_interp.h" // PyInterpreterState.list
#include "pycore_list.h" // struct _Py_list_state, _PyListIterObject
+#include "pycore_long.h" // _PyLong_DigitCount
#include "pycore_object.h" // _PyObject_GC_TRACK()
#include "pycore_tuple.h" // _PyTuple_FromArray()
#include
@@ -2144,24 +2145,21 @@ unsafe_latin_compare(PyObject *v, PyObject *w, MergeState *ms)
static int
unsafe_long_compare(PyObject *v, PyObject *w, MergeState *ms)
{
- PyLongObject *vl, *wl; sdigit v0, w0; int res;
+ PyLongObject *vl, *wl;
+ intptr_t v0, w0;
+ int res;
/* Modified from Objects/longobject.c:long_compare, assuming: */
assert(Py_IS_TYPE(v, &PyLong_Type));
assert(Py_IS_TYPE(w, &PyLong_Type));
- assert(Py_ABS(Py_SIZE(v)) <= 1);
- assert(Py_ABS(Py_SIZE(w)) <= 1);
+ assert(_PyLong_IsCompact((PyLongObject *)v));
+ assert(_PyLong_IsCompact((PyLongObject *)w));
vl = (PyLongObject*)v;
wl = (PyLongObject*)w;
- v0 = Py_SIZE(vl) == 0 ? 0 : (sdigit)vl->long_value.ob_digit[0];
- w0 = Py_SIZE(wl) == 0 ? 0 : (sdigit)wl->long_value.ob_digit[0];
-
- if (Py_SIZE(vl) < 0)
- v0 = -v0;
- if (Py_SIZE(wl) < 0)
- w0 = -w0;
+ v0 = _PyLong_CompactValue(vl);
+ w0 = _PyLong_CompactValue(wl);
res = v0 < w0;
assert(res == PyObject_RichCompareBool(v, w, Py_LT));
@@ -2359,7 +2357,7 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
if (keys_are_all_same_type) {
if (key_type == &PyLong_Type &&
ints_are_bounded &&
- Py_ABS(Py_SIZE(key)) > 1) {
+ !_PyLong_IsCompact((PyLongObject *)key)) {
ints_are_bounded = 0;
}
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 51655cd0bad9ec..bb4eac0d932bb8 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -6,7 +6,7 @@
#include "pycore_bitutils.h" // _Py_popcount32()
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_long.h" // _Py_SmallInts
-#include "pycore_object.h" // _PyObject_InitVar()
+#include "pycore_object.h" // _PyObject_Init()
#include "pycore_pystate.h" // _Py_IsMainInterpreter()
#include "pycore_runtime.h" // _PY_NSMALLPOSINTS
#include "pycore_structseq.h" // _PyStructSequence_FiniType()
@@ -22,16 +22,7 @@ class int "PyObject *" "&PyLong_Type"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ec0275e3422a36e3]*/
-/* Is this PyLong of size 1, 0 or -1? */
-#define IS_MEDIUM_VALUE(x) (((size_t)Py_SIZE(x)) + 1U < 3U)
-
-/* convert a PyLong of size 1, 0 or -1 to a C integer */
-static inline stwodigits
-medium_value(PyLongObject *x)
-{
- assert(IS_MEDIUM_VALUE(x));
- return ((stwodigits)Py_SIZE(x)) * x->long_value.ob_digit[0];
-}
+#define medium_value(x) ((stwodigits)_PyLong_CompactValue(x))
#define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS)
#define IS_SMALL_UINT(ival) ((ival) < _PY_NSMALLPOSINTS)
@@ -68,7 +59,7 @@ get_small_int(sdigit ival)
static PyLongObject *
maybe_small_long(PyLongObject *v)
{
- if (v && IS_MEDIUM_VALUE(v)) {
+ if (v && _PyLong_IsCompact(v)) {
stwodigits ival = medium_value(v);
if (IS_SMALL_INT(ival)) {
_Py_DECREF_INT(v);
@@ -126,13 +117,18 @@ maybe_small_long(PyLongObject *v)
static PyLongObject *
long_normalize(PyLongObject *v)
{
- Py_ssize_t j = Py_ABS(Py_SIZE(v));
+ Py_ssize_t j = _PyLong_DigitCount(v);
Py_ssize_t i = j;
while (i > 0 && v->long_value.ob_digit[i-1] == 0)
--i;
if (i != j) {
- Py_SET_SIZE(v, (Py_SIZE(v) < 0) ? -(i) : i);
+ if (i == 0) {
+ _PyLong_SetSignAndDigitCount(v, 0, 0);
+ }
+ else {
+ _PyLong_SetDigitCount(v, i);
+ }
}
return v;
}
@@ -146,6 +142,7 @@ long_normalize(PyLongObject *v)
PyLongObject *
_PyLong_New(Py_ssize_t size)
{
+ assert(size >= 0);
PyLongObject *result;
if (size > (Py_ssize_t)MAX_LONG_DIGITS) {
PyErr_SetString(PyExc_OverflowError,
@@ -157,8 +154,8 @@ _PyLong_New(Py_ssize_t size)
Py_ssize_t ndigits = size ? size : 1;
/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
sizeof(digit)*size. Previous incarnations of this code used
- sizeof(PyVarObject) instead of the offsetof, but this risks being
- incorrect in the presence of padding between the PyVarObject header
+ sizeof() instead of the offsetof, but this risks being
+ incorrect in the presence of padding between the header
and the digits. */
result = PyObject_Malloc(offsetof(PyLongObject, long_value.ob_digit) +
ndigits*sizeof(digit));
@@ -166,34 +163,41 @@ _PyLong_New(Py_ssize_t size)
PyErr_NoMemory();
return NULL;
}
- _PyObject_InitVar((PyVarObject*)result, &PyLong_Type, size);
+ _PyLong_SetSignAndDigitCount(result, size != 0, size);
+ _PyObject_Init((PyObject*)result, &PyLong_Type);
+ return result;
+}
+
+PyLongObject *
+_PyLong_FromDigits(int negative, Py_ssize_t digit_count, digit *digits)
+{
+ assert(digit_count >= 0);
+ if (digit_count == 0) {
+ return (PyLongObject *)Py_NewRef(_PyLong_GetZero());
+ }
+ PyLongObject *result = _PyLong_New(digit_count);
+ if (result == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ _PyLong_SetSignAndDigitCount(result, negative?-1:1, digit_count);
+ memcpy(result->long_value.ob_digit, digits, digit_count * sizeof(digit));
return result;
}
PyObject *
_PyLong_Copy(PyLongObject *src)
{
- PyLongObject *result;
- Py_ssize_t i;
-
assert(src != NULL);
- i = Py_SIZE(src);
- if (i < 0)
- i = -(i);
- if (i < 2) {
+
+ if (_PyLong_IsCompact(src)) {
stwodigits ival = medium_value(src);
if (IS_SMALL_INT(ival)) {
return get_small_int((sdigit)ival);
}
}
- result = _PyLong_New(i);
- if (result != NULL) {
- Py_SET_SIZE(result, Py_SIZE(src));
- while (--i >= 0) {
- result->long_value.ob_digit[i] = src->long_value.ob_digit[i];
- }
- }
- return (PyObject *)result;
+ Py_ssize_t size = _PyLong_DigitCount(src);
+ return (PyObject *)_PyLong_FromDigits(_PyLong_IsNegative(src), size, src->long_value.ob_digit);
}
static PyObject *
@@ -207,9 +211,9 @@ _PyLong_FromMedium(sdigit x)
PyErr_NoMemory();
return NULL;
}
- Py_ssize_t sign = x < 0 ? -1: 1;
digit abs_x = x < 0 ? -x : x;
- _PyObject_InitVar((PyVarObject*)v, &PyLong_Type, sign);
+ _PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1);
+ _PyObject_Init((PyObject*)v, &PyLong_Type);
v->long_value.ob_digit[0] = abs_x;
return (PyObject*)v;
}
@@ -242,7 +246,7 @@ _PyLong_FromLarge(stwodigits ival)
PyLongObject *v = _PyLong_New(ndigits);
if (v != NULL) {
digit *p = v->long_value.ob_digit;
- Py_SET_SIZE(v, ndigits * sign);
+ _PyLong_SetSignAndDigitCount(v, sign, ndigits);
t = abs_ival;
while (t) {
*p++ = Py_SAFE_DOWNCAST(
@@ -267,38 +271,6 @@ _PyLong_FromSTwoDigits(stwodigits x)
return _PyLong_FromLarge(x);
}
-int
-_PyLong_AssignValue(PyObject **target, Py_ssize_t value)
-{
- PyObject *old = *target;
- if (IS_SMALL_INT(value)) {
- *target = get_small_int(Py_SAFE_DOWNCAST(value, Py_ssize_t, sdigit));
- Py_XDECREF(old);
- return 0;
- }
- else if (old != NULL && PyLong_CheckExact(old) &&
- Py_REFCNT(old) == 1 && Py_SIZE(old) == 1 &&
- (size_t)value <= PyLong_MASK)
- {
- // Mutate in place if there are no other references the old
- // object. This avoids an allocation in a common case.
- // Since the primary use-case is iterating over ranges, which
- // are typically positive, only do this optimization
- // for positive integers (for now).
- ((PyLongObject *)old)->long_value.ob_digit[0] =
- Py_SAFE_DOWNCAST(value, Py_ssize_t, digit);
- return 0;
- }
- else {
- *target = PyLong_FromSsize_t(value);
- Py_XDECREF(old);
- if (*target == NULL) {
- return -1;
- }
- return 0;
- }
-}
-
/* If a freshly-allocated int is already shared, it must
be a small integer, so negating it must go to PyLong_FromLong */
Py_LOCAL_INLINE(void)
@@ -308,7 +280,7 @@ _PyLong_Negate(PyLongObject **x_p)
x = (PyLongObject *)*x_p;
if (Py_REFCNT(x) == 1) {
- Py_SET_SIZE(x, -Py_SIZE(x));
+ _PyLong_FlipSign(x);
return;
}
@@ -347,7 +319,7 @@ PyLong_FromLong(long ival)
v = _PyLong_New(ndigits);
if (v != NULL) {
digit *p = v->long_value.ob_digit;
- Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits);
+ _PyLong_SetSignAndDigitCount(v, ival < 0 ? -1 : 1, ndigits);
t = abs_ival;
while (t) {
*p++ = (digit)(t & PyLong_MASK);
@@ -457,7 +429,7 @@ PyLong_FromDouble(double dval)
frac = ldexp(frac, PyLong_SHIFT);
}
if (neg) {
- Py_SET_SIZE(v, -(Py_SIZE(v)));
+ _PyLong_FlipSign(v);
}
return (PyObject *)v;
}
@@ -510,27 +482,22 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
return -1;
do_decref = 1;
}
-
- res = -1;
- i = Py_SIZE(v);
-
- switch (i) {
- case -1:
- res = -(sdigit)v->long_value.ob_digit[0];
- break;
- case 0:
- res = 0;
- break;
- case 1:
- res = v->long_value.ob_digit[0];
- break;
- default:
- sign = 1;
- x = 0;
- if (i < 0) {
- sign = -1;
- i = -(i);
+ if (_PyLong_IsCompact(v)) {
+#if SIZEOF_LONG < SIZEOF_VOID_P
+ intptr_t tmp = _PyLong_CompactValue(v);
+ res = (long)tmp;
+ if (res != tmp) {
+ *overflow = tmp < 0 ? -1 : 1;
}
+#else
+ res = _PyLong_CompactValue(v);
+#endif
+ }
+ else {
+ res = -1;
+ i = _PyLong_DigitCount(v);
+ sign = _PyLong_NonCompactSign(v);
+ x = 0;
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
@@ -540,8 +507,8 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
}
}
/* Haven't lost any bits, but casting to long requires extra
- * care (see comment above).
- */
+ * care (see comment above).
+ */
if (x <= (unsigned long)LONG_MAX) {
res = (long)x * sign;
}
@@ -615,18 +582,12 @@ PyLong_AsSsize_t(PyObject *vv) {
}
v = (PyLongObject *)vv;
- i = Py_SIZE(v);
- switch (i) {
- case -1: return -(sdigit)v->long_value.ob_digit[0];
- case 0: return 0;
- case 1: return v->long_value.ob_digit[0];
+ if (_PyLong_IsCompact(v)) {
+ return _PyLong_CompactValue(v);
}
- sign = 1;
+ i = _PyLong_DigitCount(v);
+ sign = _PyLong_NonCompactSign(v);
x = 0;
- if (i < 0) {
- sign = -1;
- i = -(i);
- }
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
@@ -670,28 +631,37 @@ PyLong_AsUnsignedLong(PyObject *vv)
}
v = (PyLongObject *)vv;
- i = Py_SIZE(v);
- x = 0;
- if (i < 0) {
+ if (_PyLong_IsNonNegativeCompact(v)) {
+#if SIZEOF_LONG < SIZEOF_VOID_P
+ intptr_t tmp = _PyLong_CompactValue(v);
+ unsigned long res = (unsigned long)tmp;
+ if (res != tmp) {
+ goto overflow;
+ }
+#else
+ return _PyLong_CompactValue(v);
+#endif
+ }
+ if (_PyLong_IsNegative(v)) {
PyErr_SetString(PyExc_OverflowError,
"can't convert negative value to unsigned int");
return (unsigned long) -1;
}
- switch (i) {
- case 0: return 0;
- case 1: return v->long_value.ob_digit[0];
- }
+ i = _PyLong_DigitCount(v);
+ x = 0;
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
if ((x >> PyLong_SHIFT) != prev) {
- PyErr_SetString(PyExc_OverflowError,
- "Python int too large to convert "
- "to C unsigned long");
- return (unsigned long) -1;
+ goto overflow;
}
}
return x;
+overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "Python int too large to convert "
+ "to C unsigned long");
+ return (unsigned long) -1;
}
/* Get a C size_t from an int object. Returns (size_t)-1 and sets
@@ -714,17 +684,16 @@ PyLong_AsSize_t(PyObject *vv)
}
v = (PyLongObject *)vv;
- i = Py_SIZE(v);
- x = 0;
- if (i < 0) {
+ if (_PyLong_IsNonNegativeCompact(v)) {
+ return _PyLong_CompactValue(v);
+ }
+ if (_PyLong_IsNegative(v)) {
PyErr_SetString(PyExc_OverflowError,
"can't convert negative value to size_t");
return (size_t) -1;
}
- switch (i) {
- case 0: return 0;
- case 1: return v->long_value.ob_digit[0];
- }
+ i = _PyLong_DigitCount(v);
+ x = 0;
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
@@ -746,24 +715,18 @@ _PyLong_AsUnsignedLongMask(PyObject *vv)
PyLongObject *v;
unsigned long x;
Py_ssize_t i;
- int sign;
if (vv == NULL || !PyLong_Check(vv)) {
PyErr_BadInternalCall();
return (unsigned long) -1;
}
v = (PyLongObject *)vv;
- i = Py_SIZE(v);
- switch (i) {
- case 0: return 0;
- case 1: return v->long_value.ob_digit[0];
+ if (_PyLong_IsCompact(v)) {
+ return (unsigned long)_PyLong_CompactValue(v);
}
- sign = 1;
+ i = _PyLong_DigitCount(v);
+ int sign = _PyLong_NonCompactSign(v);
x = 0;
- if (i < 0) {
- sign = -1;
- i = -i;
- }
while (--i >= 0) {
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
}
@@ -801,8 +764,10 @@ _PyLong_Sign(PyObject *vv)
assert(v != NULL);
assert(PyLong_Check(v));
-
- return Py_SIZE(v) == 0 ? 0 : (Py_SIZE(v) < 0 ? -1 : 1);
+ if (_PyLong_IsCompact(v)) {
+ return _PyLong_CompactSign(v);
+ }
+ return _PyLong_NonCompactSign(v);
}
static int
@@ -825,7 +790,7 @@ _PyLong_NumBits(PyObject *vv)
assert(v != NULL);
assert(PyLong_Check(v));
- ndigits = Py_ABS(Py_SIZE(v));
+ ndigits = _PyLong_DigitCount(v);
assert(ndigits == 0 || v->long_value.ob_digit[ndigits - 1] != 0);
if (ndigits > 0) {
digit msd = v->long_value.ob_digit[ndigits - 1];
@@ -952,7 +917,11 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n,
}
}
- Py_SET_SIZE(v, is_signed ? -idigit : idigit);
+ int sign = is_signed ? -1: 1;
+ if (idigit == 0) {
+ sign = 0;
+ }
+ _PyLong_SetSignAndDigitCount(v, sign, idigit);
return (PyObject *)maybe_small_long(long_normalize(v));
}
@@ -962,7 +931,7 @@ _PyLong_AsByteArray(PyLongObject* v,
int little_endian, int is_signed)
{
Py_ssize_t i; /* index into v->long_value.ob_digit */
- Py_ssize_t ndigits; /* |v->ob_size| */
+ Py_ssize_t ndigits; /* number of digits */
twodigits accum; /* sliding register */
unsigned int accumbits; /* # bits in accum */
int do_twos_comp; /* store 2's-comp? is_signed and v < 0 */
@@ -973,8 +942,8 @@ _PyLong_AsByteArray(PyLongObject* v,
assert(v != NULL && PyLong_Check(v));
- if (Py_SIZE(v) < 0) {
- ndigits = -(Py_SIZE(v));
+ ndigits = _PyLong_DigitCount(v);
+ if (_PyLong_IsNegative(v)) {
if (!is_signed) {
PyErr_SetString(PyExc_OverflowError,
"can't convert negative int to unsigned");
@@ -983,7 +952,6 @@ _PyLong_AsByteArray(PyLongObject* v,
do_twos_comp = 1;
}
else {
- ndigits = Py_SIZE(v);
do_twos_comp = 0;
}
@@ -1114,10 +1082,12 @@ PyLong_AsVoidPtr(PyObject *vv)
#if SIZEOF_VOID_P <= SIZEOF_LONG
long x;
- if (PyLong_Check(vv) && _PyLong_Sign(vv) < 0)
+ if (PyLong_Check(vv) && _PyLong_IsNegative((PyLongObject *)vv)) {
x = PyLong_AsLong(vv);
- else
+ }
+ else {
x = PyLong_AsUnsignedLong(vv);
+ }
#else
#if SIZEOF_LONG_LONG < SIZEOF_VOID_P
@@ -1125,10 +1095,12 @@ PyLong_AsVoidPtr(PyObject *vv)
#endif
long long x;
- if (PyLong_Check(vv) && _PyLong_Sign(vv) < 0)
+ if (PyLong_Check(vv) && _PyLong_IsNegative((PyLongObject *)vv)) {
x = PyLong_AsLongLong(vv);
- else
+ }
+ else {
x = PyLong_AsUnsignedLongLong(vv);
+ }
#endif /* SIZEOF_VOID_P <= SIZEOF_LONG */
@@ -1174,7 +1146,7 @@ PyLong_FromLongLong(long long ival)
v = _PyLong_New(ndigits);
if (v != NULL) {
digit *p = v->long_value.ob_digit;
- Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits);
+ _PyLong_SetSignAndDigitCount(v, ival < 0 ? -1 : 1, ndigits);
t = abs_ival;
while (t) {
*p++ = (digit)(t & PyLong_MASK);
@@ -1217,7 +1189,7 @@ PyLong_FromSsize_t(Py_ssize_t ival)
v = _PyLong_New(ndigits);
if (v != NULL) {
digit *p = v->long_value.ob_digit;
- Py_SET_SIZE(v, negative ? -ndigits : ndigits);
+ _PyLong_SetSignAndDigitCount(v, negative ? -1 : 1, ndigits);
t = abs_ival;
while (t) {
*p++ = (digit)(t & PyLong_MASK);
@@ -1253,18 +1225,11 @@ PyLong_AsLongLong(PyObject *vv)
do_decref = 1;
}
- res = 0;
- switch(Py_SIZE(v)) {
- case -1:
- bytes = -(sdigit)v->long_value.ob_digit[0];
- break;
- case 0:
- bytes = 0;
- break;
- case 1:
- bytes = v->long_value.ob_digit[0];
- break;
- default:
+ if (_PyLong_IsCompact(v)) {
+ res = 0;
+ bytes = _PyLong_CompactValue(v);
+ }
+ else {
res = _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)&bytes,
SIZEOF_LONG_LONG, PY_LITTLE_ENDIAN, 1);
}
@@ -1299,13 +1264,14 @@ PyLong_AsUnsignedLongLong(PyObject *vv)
}
v = (PyLongObject*)vv;
- switch(Py_SIZE(v)) {
- case 0: return 0;
- case 1: return v->long_value.ob_digit[0];
+ if (_PyLong_IsNonNegativeCompact(v)) {
+ res = 0;
+ bytes = _PyLong_CompactValue(v);
}
-
- res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes,
+ else {
+ res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes,
SIZEOF_LONG_LONG, PY_LITTLE_ENDIAN, 0);
+ }
/* Plan 9 can't handle long long in ? : expressions */
if (res < 0)
@@ -1330,17 +1296,12 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv)
return (unsigned long long) -1;
}
v = (PyLongObject *)vv;
- switch(Py_SIZE(v)) {
- case 0: return 0;
- case 1: return v->long_value.ob_digit[0];
+ if (_PyLong_IsCompact(v)) {
+ return (unsigned long long)(signed long long)_PyLong_CompactValue(v);
}
- i = Py_SIZE(v);
- sign = 1;
+ i = _PyLong_DigitCount(v);
+ sign = _PyLong_NonCompactSign(v);
x = 0;
- if (i < 0) {
- sign = -1;
- i = -i;
- }
while (--i >= 0) {
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
}
@@ -1407,32 +1368,19 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow)
return -1;
do_decref = 1;
}
-
- res = -1;
- i = Py_SIZE(v);
-
- switch (i) {
- case -1:
- res = -(sdigit)v->long_value.ob_digit[0];
- break;
- case 0:
- res = 0;
- break;
- case 1:
- res = v->long_value.ob_digit[0];
- break;
- default:
- sign = 1;
+ if (_PyLong_IsCompact(v)) {
+ res = _PyLong_CompactValue(v);
+ }
+ else {
+ i = _PyLong_DigitCount(v);
+ sign = _PyLong_NonCompactSign(v);
x = 0;
- if (i < 0) {
- sign = -1;
- i = -(i);
- }
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) + v->long_value.ob_digit[i];
if ((x >> PyLong_SHIFT) != prev) {
*overflow = sign;
+ res = -1;
goto exit;
}
}
@@ -1447,7 +1395,7 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow)
}
else {
*overflow = sign;
- /* res is already set to -1 */
+ res = -1;
}
}
exit:
@@ -1462,7 +1410,7 @@ _PyLong_UnsignedShort_Converter(PyObject *obj, void *ptr)
{
unsigned long uval;
- if (PyLong_Check(obj) && _PyLong_Sign(obj) < 0) {
+ if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
@@ -1484,7 +1432,7 @@ _PyLong_UnsignedInt_Converter(PyObject *obj, void *ptr)
{
unsigned long uval;
- if (PyLong_Check(obj) && _PyLong_Sign(obj) < 0) {
+ if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
@@ -1506,7 +1454,7 @@ _PyLong_UnsignedLong_Converter(PyObject *obj, void *ptr)
{
unsigned long uval;
- if (PyLong_Check(obj) && _PyLong_Sign(obj) < 0) {
+ if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
@@ -1523,7 +1471,7 @@ _PyLong_UnsignedLongLong_Converter(PyObject *obj, void *ptr)
{
unsigned long long uval;
- if (PyLong_Check(obj) && _PyLong_Sign(obj) < 0) {
+ if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
@@ -1540,7 +1488,7 @@ _PyLong_Size_t_Converter(PyObject *obj, void *ptr)
{
size_t uval;
- if (PyLong_Check(obj) && _PyLong_Sign(obj) < 0) {
+ if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
@@ -1694,7 +1642,7 @@ inplace_divrem1(digit *pout, digit *pin, Py_ssize_t size, digit n)
static PyLongObject *
divrem1(PyLongObject *a, digit n, digit *prem)
{
- const Py_ssize_t size = Py_ABS(Py_SIZE(a));
+ const Py_ssize_t size = _PyLong_DigitCount(a);
PyLongObject *z;
assert(n > 0 && n <= PyLong_MASK);
@@ -1726,7 +1674,7 @@ inplace_rem1(digit *pin, Py_ssize_t size, digit n)
static PyLongObject *
rem1(PyLongObject *a, digit n)
{
- const Py_ssize_t size = Py_ABS(Py_SIZE(a));
+ const Py_ssize_t size = _PyLong_DigitCount(a);
assert(n > 0 && n <= PyLong_MASK);
return (PyLongObject *)PyLong_FromLong(
@@ -1824,8 +1772,8 @@ long_to_decimal_string_internal(PyObject *aa,
PyErr_BadInternalCall();
return -1;
}
- size_a = Py_ABS(Py_SIZE(a));
- negative = Py_SIZE(a) < 0;
+ size_a = _PyLong_DigitCount(a);
+ negative = _PyLong_IsNegative(a);
/* quick and dirty pre-check for overflowing the decimal digit limit,
based on the inequality 10/3 >= log2(10)
@@ -2055,8 +2003,8 @@ long_format_binary(PyObject *aa, int base, int alternate,
PyErr_BadInternalCall();
return -1;
}
- size_a = Py_ABS(Py_SIZE(a));
- negative = Py_SIZE(a) < 0;
+ size_a = _PyLong_DigitCount(a);
+ negative = _PyLong_IsNegative(a);
/* Compute a rough upper bound for the length of the string */
switch (base) {
@@ -2532,7 +2480,7 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits,
*res = NULL;
return 0;
}
- Py_SET_SIZE(z, 0);
+ _PyLong_SetSignAndDigitCount(z, 0, 0);
/* `convwidth` consecutive input digits are treated as a single
* digit in base `convmultmax`.
@@ -2572,7 +2520,7 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits,
/* Multiply z by convmult, and add c. */
pz = z->long_value.ob_digit;
- pzstop = pz + Py_SIZE(z);
+ pzstop = pz + _PyLong_DigitCount(z);
for (; pz < pzstop; ++pz) {
c += (twodigits)*pz * convmult;
*pz = (digit)(c & PyLong_MASK);
@@ -2581,14 +2529,15 @@ long_from_non_binary_base(const char *start, const char *end, Py_ssize_t digits,
/* carry off the current end? */
if (c) {
assert(c < PyLong_BASE);
- if (Py_SIZE(z) < size_z) {
+ if (_PyLong_DigitCount(z) < size_z) {
*pz = (digit)c;
- Py_SET_SIZE(z, Py_SIZE(z) + 1);
+ assert(!_PyLong_IsNegative(z));
+ _PyLong_SetSignAndDigitCount(z, 1, _PyLong_DigitCount(z) + 1);
}
else {
PyLongObject *tmp;
/* Extremely rare. Get more space. */
- assert(Py_SIZE(z) == size_z);
+ assert(_PyLong_DigitCount(z) == size_z);
tmp = _PyLong_New(size_z + 1);
if (tmp == NULL) {
Py_DECREF(z);
@@ -2790,7 +2739,7 @@ PyLong_FromString(const char *str, char **pend, int base)
/* reset the base to 0, else the exception message
doesn't make too much sense */
base = 0;
- if (Py_SIZE(z) != 0) {
+ if (!_PyLong_IsZero(z)) {
goto onError;
}
/* there might still be other problems, therefore base
@@ -2799,7 +2748,7 @@ PyLong_FromString(const char *str, char **pend, int base)
/* Set sign and normalize */
if (sign < 0) {
- Py_SET_SIZE(z, -(Py_SIZE(z)));
+ _PyLong_FlipSign(z);
}
long_normalize(z);
z = maybe_small_long(z);
@@ -2891,7 +2840,7 @@ static int
long_divrem(PyLongObject *a, PyLongObject *b,
PyLongObject **pdiv, PyLongObject **prem)
{
- Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b));
+ Py_ssize_t size_a = _PyLong_DigitCount(a), size_b = _PyLong_DigitCount(b);
PyLongObject *z;
if (size_b == 0) {
@@ -2932,14 +2881,14 @@ long_divrem(PyLongObject *a, PyLongObject *b,
The quotient z has the sign of a*b;
the remainder r has the sign of a,
so a = b*z + r. */
- if ((Py_SIZE(a) < 0) != (Py_SIZE(b) < 0)) {
+ if ((_PyLong_IsNegative(a)) != (_PyLong_IsNegative(b))) {
_PyLong_Negate(&z);
if (z == NULL) {
Py_CLEAR(*prem);
return -1;
}
}
- if (Py_SIZE(a) < 0 && Py_SIZE(*prem) != 0) {
+ if (_PyLong_IsNegative(a) && !_PyLong_IsZero(*prem)) {
_PyLong_Negate(prem);
if (*prem == NULL) {
Py_DECREF(z);
@@ -2956,7 +2905,7 @@ long_divrem(PyLongObject *a, PyLongObject *b,
static int
long_rem(PyLongObject *a, PyLongObject *b, PyLongObject **prem)
{
- Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b));
+ Py_ssize_t size_a = _PyLong_DigitCount(a), size_b = _PyLong_DigitCount(b);
if (size_b == 0) {
PyErr_SetString(PyExc_ZeroDivisionError,
@@ -2983,7 +2932,7 @@ long_rem(PyLongObject *a, PyLongObject *b, PyLongObject **prem)
return -1;
}
/* Set the sign. */
- if (Py_SIZE(a) < 0 && Py_SIZE(*prem) != 0) {
+ if (_PyLong_IsNegative(a) && !_PyLong_IsZero(*prem)) {
_PyLong_Negate(prem);
if (*prem == NULL) {
Py_CLEAR(*prem);
@@ -2994,7 +2943,7 @@ long_rem(PyLongObject *a, PyLongObject *b, PyLongObject **prem)
}
/* Unsigned int division with remainder -- the algorithm. The arguments v1
- and w1 should satisfy 2 <= Py_ABS(Py_SIZE(w1)) <= Py_ABS(Py_SIZE(v1)). */
+ and w1 should satisfy 2 <= _PyLong_DigitCount(w1) <= _PyLong_DigitCount(v1). */
static PyLongObject *
x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem)
@@ -3014,8 +2963,8 @@ x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem)
that won't overflow a digit. */
/* allocate space; w will also be used to hold the final remainder */
- size_v = Py_ABS(Py_SIZE(v1));
- size_w = Py_ABS(Py_SIZE(w1));
+ size_v = _PyLong_DigitCount(v1);
+ size_w = _PyLong_DigitCount(w1);
assert(size_v >= size_w && size_w >= 2); /* Assert checks by div() */
v = _PyLong_New(size_v+1);
if (v == NULL) {
@@ -3154,7 +3103,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e)
multiple of 4, rounding ties to a multiple of 8. */
static const int half_even_correction[8] = {0, -1, -2, 1, 0, -1, 2, 1};
- a_size = Py_ABS(Py_SIZE(a));
+ a_size = _PyLong_DigitCount(a);
if (a_size == 0) {
/* Special case for 0: significand 0.0, exponent 0. */
*e = 0;
@@ -3240,7 +3189,7 @@ _PyLong_Frexp(PyLongObject *a, Py_ssize_t *e)
}
*e = a_bits;
- return Py_SIZE(a) < 0 ? -dx : dx;
+ return _PyLong_IsNegative(a) ? -dx : dx;
overflow:
/* exponent > PY_SSIZE_T_MAX */
@@ -3267,7 +3216,7 @@ PyLong_AsDouble(PyObject *v)
PyErr_SetString(PyExc_TypeError, "an integer is required");
return -1.0;
}
- if (IS_MEDIUM_VALUE(v)) {
+ if (_PyLong_IsCompact((PyLongObject *)v)) {
/* Fast path; single digit long (31 bits) will cast safely
to double. This improves performance of FP/long operations
by 20%.
@@ -3292,9 +3241,12 @@ PyLong_AsDouble(PyObject *v)
static Py_ssize_t
long_compare(PyLongObject *a, PyLongObject *b)
{
- Py_ssize_t sign = Py_SIZE(a) - Py_SIZE(b);
+ if (_PyLong_BothAreCompact(a, b)) {
+ return _PyLong_CompactValue(a) - _PyLong_CompactValue(b);
+ }
+ Py_ssize_t sign = _PyLong_SignedDigitCount(a) - _PyLong_SignedDigitCount(b);
if (sign == 0) {
- Py_ssize_t i = Py_ABS(Py_SIZE(a));
+ Py_ssize_t i = _PyLong_DigitCount(a);
sdigit diff = 0;
while (--i >= 0) {
diff = (sdigit) a->long_value.ob_digit[i] - (sdigit) b->long_value.ob_digit[i];
@@ -3302,7 +3254,7 @@ long_compare(PyLongObject *a, PyLongObject *b)
break;
}
}
- sign = Py_SIZE(a) < 0 ? -diff : diff;
+ sign = _PyLong_IsNegative(a) ? -diff : diff;
}
return sign;
}
@@ -3326,18 +3278,16 @@ long_hash(PyLongObject *v)
Py_ssize_t i;
int sign;
- i = Py_SIZE(v);
- switch(i) {
- case -1: return v->long_value.ob_digit[0]==1 ? -2 : -(sdigit)v->long_value.ob_digit[0];
- case 0: return 0;
- case 1: return v->long_value.ob_digit[0];
+ if (_PyLong_IsCompact(v)) {
+ x = _PyLong_CompactValue(v);
+ if (x == (Py_uhash_t)-1) {
+ x = (Py_uhash_t)-2;
+ }
+ return x;
}
- sign = 1;
+ i = _PyLong_DigitCount(v);
+ sign = _PyLong_NonCompactSign(v);
x = 0;
- if (i < 0) {
- sign = -1;
- i = -(i);
- }
while (--i >= 0) {
/* Here x is a quantity in the range [0, _PyHASH_MODULUS); we
want to compute x * 2**PyLong_SHIFT + v->long_value.ob_digit[i] modulo
@@ -3382,7 +3332,7 @@ long_hash(PyLongObject *v)
static PyLongObject *
x_add(PyLongObject *a, PyLongObject *b)
{
- Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b));
+ Py_ssize_t size_a = _PyLong_DigitCount(a), size_b = _PyLong_DigitCount(b);
PyLongObject *z;
Py_ssize_t i;
digit carry = 0;
@@ -3416,7 +3366,7 @@ x_add(PyLongObject *a, PyLongObject *b)
static PyLongObject *
x_sub(PyLongObject *a, PyLongObject *b)
{
- Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b));
+ Py_ssize_t size_a = _PyLong_DigitCount(a), size_b = _PyLong_DigitCount(b);
PyLongObject *z;
Py_ssize_t i;
int sign = 1;
@@ -3462,7 +3412,7 @@ x_sub(PyLongObject *a, PyLongObject *b)
}
assert(borrow == 0);
if (sign < 0) {
- Py_SET_SIZE(z, -Py_SIZE(z));
+ _PyLong_FlipSign(z);
}
return maybe_small_long(long_normalize(z));
}
@@ -3470,13 +3420,13 @@ x_sub(PyLongObject *a, PyLongObject *b)
PyObject *
_PyLong_Add(PyLongObject *a, PyLongObject *b)
{
- if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b)) {
+ if (_PyLong_BothAreCompact(a, b)) {
return _PyLong_FromSTwoDigits(medium_value(a) + medium_value(b));
}
PyLongObject *z;
- if (Py_SIZE(a) < 0) {
- if (Py_SIZE(b) < 0) {
+ if (_PyLong_IsNegative(a)) {
+ if (_PyLong_IsNegative(b)) {
z = x_add(a, b);
if (z != NULL) {
/* x_add received at least one multiple-digit int,
@@ -3484,14 +3434,14 @@ _PyLong_Add(PyLongObject *a, PyLongObject *b)
That also means z is not an element of
small_ints, so negating it in-place is safe. */
assert(Py_REFCNT(z) == 1);
- Py_SET_SIZE(z, -(Py_SIZE(z)));
+ _PyLong_FlipSign(z);
}
}
else
z = x_sub(b, a);
}
else {
- if (Py_SIZE(b) < 0)
+ if (_PyLong_IsNegative(b))
z = x_sub(a, b);
else
z = x_add(a, b);
@@ -3511,23 +3461,23 @@ _PyLong_Subtract(PyLongObject *a, PyLongObject *b)
{
PyLongObject *z;
- if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b)) {
+ if (_PyLong_BothAreCompact(a, b)) {
return _PyLong_FromSTwoDigits(medium_value(a) - medium_value(b));
}
- if (Py_SIZE(a) < 0) {
- if (Py_SIZE(b) < 0) {
+ if (_PyLong_IsNegative(a)) {
+ if (_PyLong_IsNegative(b)) {
z = x_sub(b, a);
}
else {
z = x_add(a, b);
if (z != NULL) {
- assert(Py_SIZE(z) == 0 || Py_REFCNT(z) == 1);
- Py_SET_SIZE(z, -(Py_SIZE(z)));
+ assert(_PyLong_IsZero(z) || Py_REFCNT(z) == 1);
+ _PyLong_FlipSign(z);
}
}
}
else {
- if (Py_SIZE(b) < 0)
+ if (_PyLong_IsNegative(b))
z = x_add(a, b);
else
z = x_sub(a, b);
@@ -3549,15 +3499,15 @@ static PyLongObject *
x_mul(PyLongObject *a, PyLongObject *b)
{
PyLongObject *z;
- Py_ssize_t size_a = Py_ABS(Py_SIZE(a));
- Py_ssize_t size_b = Py_ABS(Py_SIZE(b));
+ Py_ssize_t size_a = _PyLong_DigitCount(a);
+ Py_ssize_t size_b = _PyLong_DigitCount(b);
Py_ssize_t i;
z = _PyLong_New(size_a + size_b);
if (z == NULL)
return NULL;
- memset(z->long_value.ob_digit, 0, Py_SIZE(z) * sizeof(digit));
+ memset(z->long_value.ob_digit, 0, _PyLong_DigitCount(z) * sizeof(digit));
if (a == b) {
/* Efficient squaring per HAC, Algorithm 14.16:
* http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
@@ -3658,7 +3608,7 @@ kmul_split(PyLongObject *n,
{
PyLongObject *hi, *lo;
Py_ssize_t size_lo, size_hi;
- const Py_ssize_t size_n = Py_ABS(Py_SIZE(n));
+ const Py_ssize_t size_n = _PyLong_DigitCount(n);
size_lo = Py_MIN(size_n, size);
size_hi = size_n - size_lo;
@@ -3687,8 +3637,8 @@ static PyLongObject *k_lopsided_mul(PyLongObject *a, PyLongObject *b);
static PyLongObject *
k_mul(PyLongObject *a, PyLongObject *b)
{
- Py_ssize_t asize = Py_ABS(Py_SIZE(a));
- Py_ssize_t bsize = Py_ABS(Py_SIZE(b));
+ Py_ssize_t asize = _PyLong_DigitCount(a);
+ Py_ssize_t bsize = _PyLong_DigitCount(b);
PyLongObject *ah = NULL;
PyLongObject *al = NULL;
PyLongObject *bh = NULL;
@@ -3731,7 +3681,7 @@ k_mul(PyLongObject *a, PyLongObject *b)
/* If a is small compared to b, splitting on b gives a degenerate
* case with ah==0, and Karatsuba may be (even much) less efficient
* than "grade school" then. However, we can still win, by viewing
- * b as a string of "big digits", each of width a->ob_size. That
+ * b as a string of "big digits", each of the same width as a. That
* leads to a sequence of balanced calls to k_mul.
*/
if (2 * asize <= bsize)
@@ -3740,7 +3690,7 @@ k_mul(PyLongObject *a, PyLongObject *b)
/* Split a & b into hi & lo pieces. */
shift = bsize >> 1;
if (kmul_split(a, shift, &ah, &al) < 0) goto fail;
- assert(Py_SIZE(ah) > 0); /* the split isn't degenerate */
+ assert(_PyLong_IsPositive(ah)); /* the split isn't degenerate */
if (a == b) {
bh = (PyLongObject*)Py_NewRef(ah);
@@ -3769,20 +3719,20 @@ k_mul(PyLongObject *a, PyLongObject *b)
if (ret == NULL) goto fail;
#ifdef Py_DEBUG
/* Fill with trash, to catch reference to uninitialized digits. */
- memset(ret->long_value.ob_digit, 0xDF, Py_SIZE(ret) * sizeof(digit));
+ memset(ret->long_value.ob_digit, 0xDF, _PyLong_DigitCount(ret) * sizeof(digit));
#endif
/* 2. t1 <- ah*bh, and copy into high digits of result. */
if ((t1 = k_mul(ah, bh)) == NULL) goto fail;
- assert(Py_SIZE(t1) >= 0);
- assert(2*shift + Py_SIZE(t1) <= Py_SIZE(ret));
+ assert(!_PyLong_IsNegative(t1));
+ assert(2*shift + _PyLong_DigitCount(t1) <= _PyLong_DigitCount(ret));
memcpy(ret->long_value.ob_digit + 2*shift, t1->long_value.ob_digit,
- Py_SIZE(t1) * sizeof(digit));
+ _PyLong_DigitCount(t1) * sizeof(digit));
/* Zero-out the digits higher than the ah*bh copy. */
- i = Py_SIZE(ret) - 2*shift - Py_SIZE(t1);
+ i = _PyLong_DigitCount(ret) - 2*shift - _PyLong_DigitCount(t1);
if (i)
- memset(ret->long_value.ob_digit + 2*shift + Py_SIZE(t1), 0,
+ memset(ret->long_value.ob_digit + 2*shift + _PyLong_DigitCount(t1), 0,
i * sizeof(digit));
/* 3. t2 <- al*bl, and copy into the low digits. */
@@ -3790,23 +3740,23 @@ k_mul(PyLongObject *a, PyLongObject *b)
Py_DECREF(t1);
goto fail;
}
- assert(Py_SIZE(t2) >= 0);
- assert(Py_SIZE(t2) <= 2*shift); /* no overlap with high digits */
- memcpy(ret->long_value.ob_digit, t2->long_value.ob_digit, Py_SIZE(t2) * sizeof(digit));
+ assert(!_PyLong_IsNegative(t2));
+ assert(_PyLong_DigitCount(t2) <= 2*shift); /* no overlap with high digits */
+ memcpy(ret->long_value.ob_digit, t2->long_value.ob_digit, _PyLong_DigitCount(t2) * sizeof(digit));
/* Zero out remaining digits. */
- i = 2*shift - Py_SIZE(t2); /* number of uninitialized digits */
+ i = 2*shift - _PyLong_DigitCount(t2); /* number of uninitialized digits */
if (i)
- memset(ret->long_value.ob_digit + Py_SIZE(t2), 0, i * sizeof(digit));
+ memset(ret->long_value.ob_digit + _PyLong_DigitCount(t2), 0, i * sizeof(digit));
/* 4 & 5. Subtract ah*bh (t1) and al*bl (t2). We do al*bl first
* because it's fresher in cache.
*/
- i = Py_SIZE(ret) - shift; /* # digits after shift */
- (void)v_isub(ret->long_value.ob_digit + shift, i, t2->long_value.ob_digit, Py_SIZE(t2));
+ i = _PyLong_DigitCount(ret) - shift; /* # digits after shift */
+ (void)v_isub(ret->long_value.ob_digit + shift, i, t2->long_value.ob_digit, _PyLong_DigitCount(t2));
_Py_DECREF_INT(t2);
- (void)v_isub(ret->long_value.ob_digit + shift, i, t1->long_value.ob_digit, Py_SIZE(t1));
+ (void)v_isub(ret->long_value.ob_digit + shift, i, t1->long_value.ob_digit, _PyLong_DigitCount(t1));
_Py_DECREF_INT(t1);
/* 6. t3 <- (ah+al)(bh+bl), and add into result. */
@@ -3830,12 +3780,12 @@ k_mul(PyLongObject *a, PyLongObject *b)
_Py_DECREF_INT(t1);
_Py_DECREF_INT(t2);
if (t3 == NULL) goto fail;
- assert(Py_SIZE(t3) >= 0);
+ assert(!_PyLong_IsNegative(t3));
/* Add t3. It's not obvious why we can't run out of room here.
* See the (*) comment after this function.
*/
- (void)v_iadd(ret->long_value.ob_digit + shift, i, t3->long_value.ob_digit, Py_SIZE(t3));
+ (void)v_iadd(ret->long_value.ob_digit + shift, i, t3->long_value.ob_digit, _PyLong_DigitCount(t3));
_Py_DECREF_INT(t3);
return long_normalize(ret);
@@ -3896,17 +3846,17 @@ ah*bh and al*bl too.
/* b has at least twice the digits of a, and a is big enough that Karatsuba
* would pay off *if* the inputs had balanced sizes. View b as a sequence
- * of slices, each with a->ob_size digits, and multiply the slices by a,
- * one at a time. This gives k_mul balanced inputs to work with, and is
- * also cache-friendly (we compute one double-width slice of the result
+ * of slices, each with the same number of digits as a, and multiply the
+ * slices by a, one at a time. This gives k_mul balanced inputs to work with,
+ * and is also cache-friendly (we compute one double-width slice of the result
* at a time, then move on, never backtracking except for the helpful
* single-width slice overlap between successive partial sums).
*/
static PyLongObject *
k_lopsided_mul(PyLongObject *a, PyLongObject *b)
{
- const Py_ssize_t asize = Py_ABS(Py_SIZE(a));
- Py_ssize_t bsize = Py_ABS(Py_SIZE(b));
+ const Py_ssize_t asize = _PyLong_DigitCount(a);
+ Py_ssize_t bsize = _PyLong_DigitCount(b);
Py_ssize_t nbdone; /* # of b digits already multiplied */
PyLongObject *ret;
PyLongObject *bslice = NULL;
@@ -3918,7 +3868,7 @@ k_lopsided_mul(PyLongObject *a, PyLongObject *b)
ret = _PyLong_New(asize + bsize);
if (ret == NULL)
return NULL;
- memset(ret->long_value.ob_digit, 0, Py_SIZE(ret) * sizeof(digit));
+ memset(ret->long_value.ob_digit, 0, _PyLong_DigitCount(ret) * sizeof(digit));
/* Successive slices of b are copied into bslice. */
bslice = _PyLong_New(asize);
@@ -3933,14 +3883,15 @@ k_lopsided_mul(PyLongObject *a, PyLongObject *b)
/* Multiply the next slice of b by a. */
memcpy(bslice->long_value.ob_digit, b->long_value.ob_digit + nbdone,
nbtouse * sizeof(digit));
- Py_SET_SIZE(bslice, nbtouse);
+ assert(nbtouse >= 0);
+ _PyLong_SetSignAndDigitCount(bslice, 1, nbtouse);
product = k_mul(a, bslice);
if (product == NULL)
goto fail;
/* Add into result. */
- (void)v_iadd(ret->long_value.ob_digit + nbdone, Py_SIZE(ret) - nbdone,
- product->long_value.ob_digit, Py_SIZE(product));
+ (void)v_iadd(ret->long_value.ob_digit + nbdone, _PyLong_DigitCount(ret) - nbdone,
+ product->long_value.ob_digit, _PyLong_DigitCount(product));
_Py_DECREF_INT(product);
bsize -= nbtouse;
@@ -3962,14 +3913,14 @@ _PyLong_Multiply(PyLongObject *a, PyLongObject *b)
PyLongObject *z;
/* fast path for single-digit multiplication */
- if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b)) {
+ if (_PyLong_BothAreCompact(a, b)) {
stwodigits v = medium_value(a) * medium_value(b);
return _PyLong_FromSTwoDigits(v);
}
z = k_mul(a, b);
/* Negate if exactly one of the inputs is negative. */
- if (((Py_SIZE(a) ^ Py_SIZE(b)) < 0) && z) {
+ if (!_PyLong_SameSign(a, b) && z) {
_PyLong_Negate(&z);
if (z == NULL)
return NULL;
@@ -3992,11 +3943,10 @@ fast_mod(PyLongObject *a, PyLongObject *b)
sdigit right = b->long_value.ob_digit[0];
sdigit mod;
- assert(Py_ABS(Py_SIZE(a)) == 1);
- assert(Py_ABS(Py_SIZE(b)) == 1);
-
- if (Py_SIZE(a) == Py_SIZE(b)) {
- /* 'a' and 'b' have the same sign. */
+ assert(_PyLong_DigitCount(a) == 1);
+ assert(_PyLong_DigitCount(b) == 1);
+ sdigit sign = _PyLong_CompactSign(b);
+ if (_PyLong_SameSign(a, b)) {
mod = left % right;
}
else {
@@ -4004,7 +3954,7 @@ fast_mod(PyLongObject *a, PyLongObject *b)
mod = right - 1 - (left - 1) % right;
}
- return PyLong_FromLong(mod * (sdigit)Py_SIZE(b));
+ return PyLong_FromLong(mod * sign);
}
/* Fast floor division for single-digit longs. */
@@ -4015,11 +3965,10 @@ fast_floor_div(PyLongObject *a, PyLongObject *b)
sdigit right = b->long_value.ob_digit[0];
sdigit div;
- assert(Py_ABS(Py_SIZE(a)) == 1);
- assert(Py_ABS(Py_SIZE(b)) == 1);
+ assert(_PyLong_DigitCount(a) == 1);
+ assert(_PyLong_DigitCount(b) == 1);
- if (Py_SIZE(a) == Py_SIZE(b)) {
- /* 'a' and 'b' have the same sign. */
+ if (_PyLong_SameSign(a, b)) {
div = left / right;
}
else {
@@ -4097,7 +4046,7 @@ l_divmod(PyLongObject *v, PyLongObject *w,
{
PyLongObject *div, *mod;
- if (Py_ABS(Py_SIZE(v)) == 1 && Py_ABS(Py_SIZE(w)) == 1) {
+ if (_PyLong_DigitCount(v) == 1 && _PyLong_DigitCount(w) == 1) {
/* Fast path for single-digit longs */
div = NULL;
if (pdiv != NULL) {
@@ -4122,8 +4071,8 @@ l_divmod(PyLongObject *v, PyLongObject *w,
return 0;
}
#if WITH_PYLONG_MODULE
- Py_ssize_t size_v = Py_ABS(Py_SIZE(v)); /* digits in numerator */
- Py_ssize_t size_w = Py_ABS(Py_SIZE(w)); /* digits in denominator */
+ Py_ssize_t size_v = _PyLong_DigitCount(v); /* digits in numerator */
+ Py_ssize_t size_w = _PyLong_DigitCount(w); /* digits in denominator */
if (size_w > 300 && (size_v - size_w) > 150) {
/* Switch to _pylong.int_divmod(). If the quotient is small then
"schoolbook" division is linear-time so don't use in that case.
@@ -4135,8 +4084,8 @@ l_divmod(PyLongObject *v, PyLongObject *w,
#endif
if (long_divrem(v, w, &div, &mod) < 0)
return -1;
- if ((Py_SIZE(mod) < 0 && Py_SIZE(w) > 0) ||
- (Py_SIZE(mod) > 0 && Py_SIZE(w) < 0)) {
+ if ((_PyLong_IsNegative(mod) && _PyLong_IsPositive(w)) ||
+ (_PyLong_IsPositive(mod) && _PyLong_IsNegative(w))) {
PyLongObject *temp;
temp = (PyLongObject *) long_add(mod, w);
Py_SETREF(mod, temp);
@@ -4175,15 +4124,15 @@ l_mod(PyLongObject *v, PyLongObject *w, PyLongObject **pmod)
PyLongObject *mod;
assert(pmod);
- if (Py_ABS(Py_SIZE(v)) == 1 && Py_ABS(Py_SIZE(w)) == 1) {
+ if (_PyLong_DigitCount(v) == 1 && _PyLong_DigitCount(w) == 1) {
/* Fast path for single-digit longs */
*pmod = (PyLongObject *)fast_mod(v, w);
return -(*pmod == NULL);
}
if (long_rem(v, w, &mod) < 0)
return -1;
- if ((Py_SIZE(mod) < 0 && Py_SIZE(w) > 0) ||
- (Py_SIZE(mod) > 0 && Py_SIZE(w) < 0)) {
+ if ((_PyLong_IsNegative(mod) && _PyLong_IsPositive(w)) ||
+ (_PyLong_IsPositive(mod) && _PyLong_IsNegative(w))) {
PyLongObject *temp;
temp = (PyLongObject *) long_add(mod, w);
Py_SETREF(mod, temp);
@@ -4202,7 +4151,7 @@ long_div(PyObject *a, PyObject *b)
CHECK_BINOP(a, b);
- if (Py_ABS(Py_SIZE(a)) == 1 && Py_ABS(Py_SIZE(b)) == 1) {
+ if (_PyLong_DigitCount((PyLongObject*)a) == 1 && _PyLong_DigitCount((PyLongObject*)b) == 1) {
return fast_floor_div((PyLongObject*)a, (PyLongObject*)b);
}
@@ -4317,9 +4266,9 @@ long_true_divide(PyObject *v, PyObject *w)
*/
/* Reduce to case where a and b are both positive. */
- a_size = Py_ABS(Py_SIZE(a));
- b_size = Py_ABS(Py_SIZE(b));
- negate = (Py_SIZE(a) < 0) ^ (Py_SIZE(b) < 0);
+ a_size = _PyLong_DigitCount(a);
+ b_size = _PyLong_DigitCount(b);
+ negate = (_PyLong_IsNegative(a)) != (_PyLong_IsNegative(b));
if (b_size == 0) {
PyErr_SetString(PyExc_ZeroDivisionError,
"division by zero");
@@ -4412,7 +4361,7 @@ long_true_divide(PyObject *v, PyObject *w)
inexact = 1;
}
long_normalize(x);
- x_size = Py_SIZE(x);
+ x_size = _PyLong_SignedDigitCount(x);
/* x //= b. If the remainder is nonzero, set inexact. We own the only
reference to x, so it's safe to modify it in-place. */
@@ -4429,11 +4378,11 @@ long_true_divide(PyObject *v, PyObject *w)
Py_SETREF(x, div);
if (x == NULL)
goto error;
- if (Py_SIZE(rem))
+ if (!_PyLong_IsZero(rem))
inexact = 1;
Py_DECREF(rem);
}
- x_size = Py_ABS(Py_SIZE(x));
+ x_size = _PyLong_DigitCount(x);
assert(x_size > 0); /* result of division is never zero */
x_bits = (x_size-1)*PyLong_SHIFT+bit_length_digit(x->long_value.ob_digit[x_size-1]);
@@ -4535,7 +4484,7 @@ long_invmod(PyLongObject *a, PyLongObject *n)
PyLongObject *b, *c;
/* Should only ever be called for positive n */
- assert(Py_SIZE(n) > 0);
+ assert(_PyLong_IsPositive(n));
b = (PyLongObject *)PyLong_FromLong(1L);
if (b == NULL) {
@@ -4550,7 +4499,7 @@ long_invmod(PyLongObject *a, PyLongObject *n)
Py_INCREF(n);
/* references now owned: a, b, c, n */
- while (Py_SIZE(n) != 0) {
+ while (!_PyLong_IsZero(n)) {
PyLongObject *q, *r, *s, *t;
if (l_divmod(a, n, &q, &r) == -1) {
@@ -4636,7 +4585,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
Py_RETURN_NOTIMPLEMENTED;
}
- if (Py_SIZE(b) < 0 && c == NULL) {
+ if (_PyLong_IsNegative(b) && c == NULL) {
/* if exponent is negative and there's no modulus:
return a float. This works because we know
that this calls float_pow() which converts its
@@ -4649,7 +4598,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
if (c) {
/* if modulus == 0:
raise ValueError() */
- if (Py_SIZE(c) == 0) {
+ if (_PyLong_IsZero(c)) {
PyErr_SetString(PyExc_ValueError,
"pow() 3rd argument cannot be 0");
goto Error;
@@ -4658,7 +4607,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
/* if modulus < 0:
negativeOutput = True
modulus = -modulus */
- if (Py_SIZE(c) < 0) {
+ if (_PyLong_IsNegative(c)) {
negativeOutput = 1;
temp = (PyLongObject *)_PyLong_Copy(c);
if (temp == NULL)
@@ -4672,14 +4621,14 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
/* if modulus == 1:
return 0 */
- if ((Py_SIZE(c) == 1) && (c->long_value.ob_digit[0] == 1)) {
+ if (_PyLong_IsNonNegativeCompact(c) && (c->long_value.ob_digit[0] == 1)) {
z = (PyLongObject *)PyLong_FromLong(0L);
goto Done;
}
/* if exponent is negative, negate the exponent and
replace the base with a modular inverse */
- if (Py_SIZE(b) < 0) {
+ if (_PyLong_IsNegative(b)) {
temp = (PyLongObject *)_PyLong_Copy(b);
if (temp == NULL)
goto Error;
@@ -4705,7 +4654,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
base % modulus instead.
We could _always_ do this reduction, but l_mod() isn't cheap,
so we only do it when it buys something. */
- if (Py_SIZE(a) < 0 || Py_SIZE(a) > Py_SIZE(c)) {
+ if (_PyLong_IsNegative(a) || _PyLong_DigitCount(a) > _PyLong_DigitCount(c)) {
if (l_mod(a, c, &temp) < 0)
goto Error;
Py_SETREF(a, temp);
@@ -4747,7 +4696,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
REDUCE(result); \
} while(0)
- i = Py_SIZE(b);
+ i = _PyLong_SignedDigitCount(b);
digit bi = i ? b->long_value.ob_digit[i-1] : 0;
digit bit;
if (i <= 1 && bi <= 3) {
@@ -4839,7 +4788,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
pending = 0; \
} while(0)
- for (i = Py_SIZE(b) - 1; i >= 0; --i) {
+ for (i = _PyLong_SignedDigitCount(b) - 1; i >= 0; --i) {
const digit bi = b->long_value.ob_digit[i];
for (j = PyLong_SHIFT - 1; j >= 0; --j) {
const int bit = (bi >> j) & 1;
@@ -4857,7 +4806,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
ABSORB_PENDING;
}
- if (negativeOutput && (Py_SIZE(z) != 0)) {
+ if (negativeOutput && !_PyLong_IsZero(z)) {
temp = (PyLongObject *)long_sub(z, c);
if (temp == NULL)
goto Error;
@@ -4885,14 +4834,14 @@ long_invert(PyLongObject *v)
{
/* Implement ~x as -(x+1) */
PyLongObject *x;
- if (IS_MEDIUM_VALUE(v))
+ if (_PyLong_IsCompact(v))
return _PyLong_FromSTwoDigits(~medium_value(v));
x = (PyLongObject *) long_add(v, (PyLongObject *)_PyLong_GetOne());
if (x == NULL)
return NULL;
_PyLong_Negate(&x);
- /* No need for maybe_small_long here, since any small
- longs will have been caught in the Py_SIZE <= 1 fast path. */
+ /* No need for maybe_small_long here, since any small longs
+ will have been caught in the _PyLong_IsCompact() fast path. */
return (PyObject *)x;
}
@@ -4900,18 +4849,18 @@ static PyObject *
long_neg(PyLongObject *v)
{
PyLongObject *z;
- if (IS_MEDIUM_VALUE(v))
+ if (_PyLong_IsCompact(v))
return _PyLong_FromSTwoDigits(-medium_value(v));
z = (PyLongObject *)_PyLong_Copy(v);
if (z != NULL)
- Py_SET_SIZE(z, -(Py_SIZE(v)));
+ _PyLong_FlipSign(z);
return (PyObject *)z;
}
static PyObject *
long_abs(PyLongObject *v)
{
- if (Py_SIZE(v) < 0)
+ if (_PyLong_IsNegative(v))
return long_neg(v);
else
return long_long((PyObject *)v);
@@ -4920,7 +4869,7 @@ long_abs(PyLongObject *v)
static int
long_bool(PyLongObject *v)
{
- return Py_SIZE(v) != 0;
+ return !_PyLong_IsZero(v);
}
/* wordshift, remshift = divmod(shiftby, PyLong_SHIFT) */
@@ -4928,14 +4877,14 @@ static int
divmod_shift(PyObject *shiftby, Py_ssize_t *wordshift, digit *remshift)
{
assert(PyLong_Check(shiftby));
- assert(Py_SIZE(shiftby) >= 0);
+ assert(!_PyLong_IsNegative((PyLongObject *)shiftby));
Py_ssize_t lshiftby = PyLong_AsSsize_t((PyObject *)shiftby);
if (lshiftby >= 0) {
*wordshift = lshiftby / PyLong_SHIFT;
*remshift = lshiftby % PyLong_SHIFT;
return 0;
}
- /* PyLong_Check(shiftby) is true and Py_SIZE(shiftby) >= 0, so it must
+ /* PyLong_Check(shiftby) is true and shiftby is not negative, so it must
be that PyLong_AsSsize_t raised an OverflowError. */
assert(PyErr_ExceptionMatches(PyExc_OverflowError));
PyErr_Clear();
@@ -4973,7 +4922,7 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift)
assert(remshift < PyLong_SHIFT);
/* Fast path for small a. */
- if (IS_MEDIUM_VALUE(a)) {
+ if (_PyLong_IsCompact(a)) {
stwodigits m, x;
digit shift;
m = medium_value(a);
@@ -4982,8 +4931,8 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift)
return _PyLong_FromSTwoDigits(x);
}
- a_negative = Py_SIZE(a) < 0;
- size_a = Py_ABS(Py_SIZE(a));
+ a_negative = _PyLong_IsNegative(a);
+ size_a = _PyLong_DigitCount(a);
if (a_negative) {
/* For negative 'a', adjust so that 0 < remshift <= PyLong_SHIFT,
@@ -5024,7 +4973,7 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift)
significant `wordshift` digits of `a` is nonzero. Digit `wordshift`
of `2**shift - 1` has value `PyLong_MASK >> hishift`.
*/
- Py_SET_SIZE(z, -newsize);
+ _PyLong_SetSignAndDigitCount(z, -1, newsize);
digit sticky = 0;
for (Py_ssize_t j = 0; j < wordshift; j++) {
@@ -5054,11 +5003,11 @@ long_rshift(PyObject *a, PyObject *b)
CHECK_BINOP(a, b);
- if (Py_SIZE(b) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)b)) {
PyErr_SetString(PyExc_ValueError, "negative shift count");
return NULL;
}
- if (Py_SIZE(a) == 0) {
+ if (_PyLong_IsZero((PyLongObject *)a)) {
return PyLong_FromLong(0);
}
if (divmod_shift(b, &wordshift, &remshift) < 0)
@@ -5074,7 +5023,7 @@ _PyLong_Rshift(PyObject *a, size_t shiftby)
digit remshift;
assert(PyLong_Check(a));
- if (Py_SIZE(a) == 0) {
+ if (_PyLong_IsZero((PyLongObject *)a)) {
return PyLong_FromLong(0);
}
wordshift = shiftby / PyLong_SHIFT;
@@ -5089,23 +5038,23 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift)
Py_ssize_t oldsize, newsize, i, j;
twodigits accum;
- if (wordshift == 0 && IS_MEDIUM_VALUE(a)) {
+ if (wordshift == 0 && _PyLong_IsCompact(a)) {
stwodigits m = medium_value(a);
// bypass undefined shift operator behavior
stwodigits x = m < 0 ? -(-m << remshift) : m << remshift;
return _PyLong_FromSTwoDigits(x);
}
- oldsize = Py_ABS(Py_SIZE(a));
+ oldsize = _PyLong_DigitCount(a);
newsize = oldsize + wordshift;
if (remshift)
++newsize;
z = _PyLong_New(newsize);
if (z == NULL)
return NULL;
- if (Py_SIZE(a) < 0) {
+ if (_PyLong_IsNegative(a)) {
assert(Py_REFCNT(z) == 1);
- Py_SET_SIZE(z, -Py_SIZE(z));
+ _PyLong_FlipSign(z);
}
for (i = 0; i < wordshift; i++)
z->long_value.ob_digit[i] = 0;
@@ -5131,11 +5080,11 @@ long_lshift(PyObject *a, PyObject *b)
CHECK_BINOP(a, b);
- if (Py_SIZE(b) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)b)) {
PyErr_SetString(PyExc_ValueError, "negative shift count");
return NULL;
}
- if (Py_SIZE(a) == 0) {
+ if (_PyLong_IsZero((PyLongObject *)a)) {
return PyLong_FromLong(0);
}
if (divmod_shift(b, &wordshift, &remshift) < 0)
@@ -5151,7 +5100,7 @@ _PyLong_Lshift(PyObject *a, size_t shiftby)
digit remshift;
assert(PyLong_Check(a));
- if (Py_SIZE(a) == 0) {
+ if (_PyLong_IsZero((PyLongObject *)a)) {
return PyLong_FromLong(0);
}
wordshift = shiftby / PyLong_SHIFT;
@@ -5193,8 +5142,8 @@ long_bitwise(PyLongObject *a,
result back to sign-magnitude at the end. */
/* If a is negative, replace it by its two's complement. */
- size_a = Py_ABS(Py_SIZE(a));
- nega = Py_SIZE(a) < 0;
+ size_a = _PyLong_DigitCount(a);
+ nega = _PyLong_IsNegative(a);
if (nega) {
z = _PyLong_New(size_a);
if (z == NULL)
@@ -5207,8 +5156,8 @@ long_bitwise(PyLongObject *a,
Py_INCREF(a);
/* Same for b. */
- size_b = Py_ABS(Py_SIZE(b));
- negb = Py_SIZE(b) < 0;
+ size_b = _PyLong_DigitCount(b);
+ negb = _PyLong_IsNegative(b);
if (negb) {
z = _PyLong_New(size_b);
if (z == NULL) {
@@ -5289,7 +5238,7 @@ long_bitwise(PyLongObject *a,
/* Complement result if negative. */
if (negz) {
- Py_SET_SIZE(z, -(Py_SIZE(z)));
+ _PyLong_FlipSign(z);
z->long_value.ob_digit[size_z] = PyLong_MASK;
v_complement(z->long_value.ob_digit, z->long_value.ob_digit, size_z+1);
}
@@ -5305,7 +5254,7 @@ long_and(PyObject *a, PyObject *b)
CHECK_BINOP(a, b);
PyLongObject *x = (PyLongObject*)a;
PyLongObject *y = (PyLongObject*)b;
- if (IS_MEDIUM_VALUE(x) && IS_MEDIUM_VALUE(y)) {
+ if (_PyLong_IsCompact(x) && _PyLong_IsCompact(y)) {
return _PyLong_FromSTwoDigits(medium_value(x) & medium_value(y));
}
return long_bitwise(x, '&', y);
@@ -5317,7 +5266,7 @@ long_xor(PyObject *a, PyObject *b)
CHECK_BINOP(a, b);
PyLongObject *x = (PyLongObject*)a;
PyLongObject *y = (PyLongObject*)b;
- if (IS_MEDIUM_VALUE(x) && IS_MEDIUM_VALUE(y)) {
+ if (_PyLong_IsCompact(x) && _PyLong_IsCompact(y)) {
return _PyLong_FromSTwoDigits(medium_value(x) ^ medium_value(y));
}
return long_bitwise(x, '^', y);
@@ -5329,7 +5278,7 @@ long_or(PyObject *a, PyObject *b)
CHECK_BINOP(a, b);
PyLongObject *x = (PyLongObject*)a;
PyLongObject *y = (PyLongObject*)b;
- if (IS_MEDIUM_VALUE(x) && IS_MEDIUM_VALUE(y)) {
+ if (_PyLong_IsCompact(x) && _PyLong_IsCompact(y)) {
return _PyLong_FromSTwoDigits(medium_value(x) | medium_value(y));
}
return long_bitwise(x, '|', y);
@@ -5353,14 +5302,11 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg)
stwodigits x, y, q, s, t, c_carry, d_carry;
stwodigits A, B, C, D, T;
int nbits, k;
- Py_ssize_t size_a, size_b, alloc_a, alloc_b;
digit *a_digit, *b_digit, *c_digit, *d_digit, *a_end, *b_end;
a = (PyLongObject *)aarg;
b = (PyLongObject *)barg;
- size_a = Py_SIZE(a);
- size_b = Py_SIZE(b);
- if (-2 <= size_a && size_a <= 2 && -2 <= size_b && size_b <= 2) {
+ if (_PyLong_DigitCount(a) <= 2 && _PyLong_DigitCount(b) <= 2) {
Py_INCREF(a);
Py_INCREF(b);
goto simple;
@@ -5382,14 +5328,15 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg)
}
/* We now own references to a and b */
- alloc_a = Py_SIZE(a);
- alloc_b = Py_SIZE(b);
+ Py_ssize_t size_a, size_b, alloc_a, alloc_b;
+ alloc_a = _PyLong_DigitCount(a);
+ alloc_b = _PyLong_DigitCount(b);
/* reduce until a fits into 2 digits */
- while ((size_a = Py_SIZE(a)) > 2) {
+ while ((size_a = _PyLong_DigitCount(a)) > 2) {
nbits = bit_length_digit(a->long_value.ob_digit[size_a-1]);
/* extract top 2*PyLong_SHIFT bits of a into x, along with
corresponding bits of b into y */
- size_b = Py_SIZE(b);
+ size_b = _PyLong_DigitCount(b);
assert(size_b <= size_a);
if (size_b == 0) {
if (size_a < alloc_a) {
@@ -5433,7 +5380,7 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg)
Py_SETREF(a, b);
b = r;
alloc_a = alloc_b;
- alloc_b = Py_SIZE(b);
+ alloc_b = _PyLong_DigitCount(b);
continue;
}
@@ -5446,7 +5393,8 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg)
T = -C; C = -D; D = T;
}
if (c != NULL) {
- Py_SET_SIZE(c, size_a);
+ assert(size_a >= 0);
+ _PyLong_SetSignAndDigitCount(c, 1, size_a);
}
else if (Py_REFCNT(a) == 1) {
c = (PyLongObject*)Py_NewRef(a);
@@ -5459,11 +5407,13 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg)
}
if (d != NULL) {
- Py_SET_SIZE(d, size_a);
+ assert(size_a >= 0);
+ _PyLong_SetSignAndDigitCount(d, 1, size_a);
}
else if (Py_REFCNT(b) == 1 && size_a <= alloc_b) {
d = (PyLongObject*)Py_NewRef(b);
- Py_SET_SIZE(d, size_a);
+ assert(size_a >= 0);
+ _PyLong_SetSignAndDigitCount(d, 1, size_a);
}
else {
alloc_b = size_a;
@@ -5635,9 +5585,7 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase)
if (tmp == NULL)
return NULL;
assert(PyLong_Check(tmp));
- n = Py_SIZE(tmp);
- if (n < 0)
- n = -n;
+ n = _PyLong_DigitCount(tmp);
/* Fast operations for single digit integers (including zero)
* assume that there is always at least one digit present. */
if (n == 0) {
@@ -5649,7 +5597,7 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase)
return NULL;
}
assert(PyLong_Check(newobj));
- Py_SET_SIZE(newobj, Py_SIZE(tmp));
+ newobj->long_value.lv_tag = tmp->long_value.lv_tag;
for (i = 0; i < n; i++) {
newobj->long_value.ob_digit[i] = tmp->long_value.ob_digit[i];
}
@@ -5743,7 +5691,7 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b)
}
/* Do a and b have different signs? If so, quotient is negative. */
- quo_is_neg = (Py_SIZE(a) < 0) != (Py_SIZE(b) < 0);
+ quo_is_neg = (_PyLong_IsNegative((PyLongObject *)a)) != (_PyLong_IsNegative((PyLongObject *)b));
if (long_divrem((PyLongObject*)a, (PyLongObject*)b, &quo, &rem) < 0)
goto error;
@@ -5763,8 +5711,8 @@ _PyLong_DivmodNear(PyObject *a, PyObject *b)
cmp = long_compare((PyLongObject *)twice_rem, (PyLongObject *)b);
Py_DECREF(twice_rem);
- quo_is_odd = Py_SIZE(quo) != 0 && ((quo->long_value.ob_digit[0] & 1) != 0);
- if ((Py_SIZE(b) < 0 ? cmp < 0 : cmp > 0) || (cmp == 0 && quo_is_odd)) {
+ quo_is_odd = (quo->long_value.ob_digit[0] & 1) != 0;
+ if ((_PyLong_IsNegative((PyLongObject *)b) ? cmp < 0 : cmp > 0) || (cmp == 0 && quo_is_odd)) {
/* fix up quotient */
if (quo_is_neg)
temp = long_sub(quo, (PyLongObject *)one);
@@ -5837,7 +5785,7 @@ int___round___impl(PyObject *self, PyObject *o_ndigits)
return NULL;
/* if ndigits >= 0 then no rounding is necessary; return self unchanged */
- if (Py_SIZE(ndigits) >= 0) {
+ if (!_PyLong_IsNegative((PyLongObject *)ndigits)) {
Py_DECREF(ndigits);
return long_long(self);
}
@@ -5883,8 +5831,8 @@ int___sizeof___impl(PyObject *self)
/*[clinic end generated code: output=3303f008eaa6a0a5 input=9b51620c76fc4507]*/
{
/* using Py_MAX(..., 1) because we always allocate space for at least
- one digit, even though the integer zero has a Py_SIZE of 0 */
- Py_ssize_t ndigits = Py_MAX(Py_ABS(Py_SIZE(self)), 1);
+ one digit, even though the integer zero has a digit count of 0 */
+ Py_ssize_t ndigits = Py_MAX(_PyLong_DigitCount((PyLongObject *)self), 1);
return Py_TYPE(self)->tp_basicsize + Py_TYPE(self)->tp_itemsize * ndigits;
}
@@ -5911,7 +5859,7 @@ int_bit_length_impl(PyObject *self)
assert(self != NULL);
assert(PyLong_Check(self));
- ndigits = Py_ABS(Py_SIZE(self));
+ ndigits = _PyLong_DigitCount((PyLongObject *)self);
if (ndigits == 0)
return PyLong_FromLong(0);
@@ -5980,7 +5928,7 @@ int_bit_count_impl(PyObject *self)
assert(PyLong_Check(self));
PyLongObject *z = (PyLongObject *)self;
- Py_ssize_t ndigits = Py_ABS(Py_SIZE(z));
+ Py_ssize_t ndigits = _PyLong_DigitCount(z);
Py_ssize_t bit_count = 0;
/* Each digit has up to PyLong_SHIFT ones, so the accumulated bit count
diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c
index b4d0bbf32c84c8..beb86b9623bdbc 100644
--- a/Objects/rangeobject.c
+++ b/Objects/rangeobject.c
@@ -33,7 +33,7 @@ validate_step(PyObject *step)
return PyLong_FromLong(1);
step = PyNumber_Index(step);
- if (step && _PyLong_Sign(step) == 0) {
+ if (step && _PyLong_IsZero((PyLongObject *)step)) {
PyErr_SetString(PyExc_ValueError,
"range() arg 3 must not be zero");
Py_CLEAR(step);
diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c
index 5d2e6ad522bcf2..584ebce721faed 100644
--- a/Objects/sliceobject.c
+++ b/Objects/sliceobject.c
@@ -445,7 +445,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
if (start == NULL)
goto error;
- if (_PyLong_Sign(start) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)start)) {
/* start += length */
PyObject *tmp = PyNumber_Add(start, length);
Py_SETREF(start, tmp);
@@ -478,7 +478,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
if (stop == NULL)
goto error;
- if (_PyLong_Sign(stop) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)stop)) {
/* stop += length */
PyObject *tmp = PyNumber_Add(stop, length);
Py_SETREF(stop, tmp);
@@ -533,7 +533,7 @@ slice_indices(PySliceObject* self, PyObject* len)
if (length == NULL)
return NULL;
- if (_PyLong_Sign(length) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)length)) {
PyErr_SetString(PyExc_ValueError,
"length should not be negative");
Py_DECREF(length);
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index a37f97c71ec763..69e84743f13aac 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -8,6 +8,7 @@
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h" // _PyType_HasFeature()
+#include "pycore_long.h" // _PyLong_IsNegative()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_typeobject.h" // struct type_cache
@@ -7865,7 +7866,7 @@ slot_sq_length(PyObject *self)
return -1;
assert(PyLong_Check(res));
- if (Py_SIZE(res) < 0) {
+ if (_PyLong_IsNegative((PyLongObject *)res)) {
Py_DECREF(res);
PyErr_SetString(PyExc_ValueError,
"__len__() should return >= 0");
diff --git a/Python/ast_opt.c b/Python/ast_opt.c
index 1a0b2a05b1c713..8270fa8e372d93 100644
--- a/Python/ast_opt.c
+++ b/Python/ast_opt.c
@@ -2,6 +2,7 @@
#include "Python.h"
#include "pycore_ast.h" // _PyAST_GetDocString()
#include "pycore_compile.h" // _PyASTOptimizeState
+#include "pycore_long.h" // _PyLong
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_format.h" // F_LJUST
@@ -152,7 +153,9 @@ check_complexity(PyObject *obj, Py_ssize_t limit)
static PyObject *
safe_multiply(PyObject *v, PyObject *w)
{
- if (PyLong_Check(v) && PyLong_Check(w) && Py_SIZE(v) && Py_SIZE(w)) {
+ if (PyLong_Check(v) && PyLong_Check(w) &&
+ !_PyLong_IsZero((PyLongObject *)v) && !_PyLong_IsZero((PyLongObject *)w)
+ ) {
size_t vbits = _PyLong_NumBits(v);
size_t wbits = _PyLong_NumBits(w);
if (vbits == (size_t)-1 || wbits == (size_t)-1) {
@@ -198,7 +201,9 @@ safe_multiply(PyObject *v, PyObject *w)
static PyObject *
safe_power(PyObject *v, PyObject *w)
{
- if (PyLong_Check(v) && PyLong_Check(w) && Py_SIZE(v) && Py_SIZE(w) > 0) {
+ if (PyLong_Check(v) && PyLong_Check(w) &&
+ !_PyLong_IsZero((PyLongObject *)v) && _PyLong_IsPositive((PyLongObject *)w)
+ ) {
size_t vbits = _PyLong_NumBits(v);
size_t wbits = PyLong_AsSize_t(w);
if (vbits == (size_t)-1 || wbits == (size_t)-1) {
@@ -215,7 +220,9 @@ safe_power(PyObject *v, PyObject *w)
static PyObject *
safe_lshift(PyObject *v, PyObject *w)
{
- if (PyLong_Check(v) && PyLong_Check(w) && Py_SIZE(v) && Py_SIZE(w)) {
+ if (PyLong_Check(v) && PyLong_Check(w) &&
+ !_PyLong_IsZero((PyLongObject *)v) && !_PyLong_IsZero((PyLongObject *)w)
+ ) {
size_t vbits = _PyLong_NumBits(v);
size_t wbits = PyLong_AsSize_t(w);
if (vbits == (size_t)-1 || wbits == (size_t)-1) {
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 12ca0ba6c4873c..55fd364d007972 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -5,6 +5,7 @@
#include "pycore_ast.h" // _PyAST_Validate()
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_compile.h" // _PyAST_Compile()
+#include "pycore_long.h" // _PyLong_CompactValue
#include "pycore_object.h" // _Py_AddToAllObjects()
#include "pycore_pyerrors.h" // _PyErr_NoMemory()
#include "pycore_pystate.h" // _PyThreadState_GET()
@@ -2491,7 +2492,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
*/
if (PyLong_CheckExact(result)) {
int overflow;
- long i_result = PyLong_AsLongAndOverflow(result, &overflow);
+ Py_ssize_t i_result = PyLong_AsLongAndOverflow(result, &overflow);
/* If this already overflowed, don't even enter the loop. */
if (overflow == 0) {
Py_SETREF(result, NULL);
@@ -2505,15 +2506,14 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
return PyLong_FromLong(i_result);
}
if (PyLong_CheckExact(item) || PyBool_Check(item)) {
- long b;
+ Py_ssize_t b;
overflow = 0;
/* Single digits are common, fast, and cannot overflow on unpacking. */
- switch (Py_SIZE(item)) {
- case -1: b = -(sdigit) ((PyLongObject*)item)->long_value.ob_digit[0]; break;
- // Note: the continue goes to the top of the "while" loop that iterates over the elements
- case 0: Py_DECREF(item); continue;
- case 1: b = ((PyLongObject*)item)->long_value.ob_digit[0]; break;
- default: b = PyLong_AsLongAndOverflow(item, &overflow); break;
+ if (_PyLong_IsCompact((PyLongObject *)item)) {
+ b = _PyLong_CompactValue((PyLongObject *)item);
+ }
+ else {
+ b = PyLong_AsLongAndOverflow(item, &overflow);
}
if (overflow == 0 &&
(i_result >= 0 ? (b <= LONG_MAX - i_result)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 92fa7664e00452..5038e1bb17fc3f 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -418,8 +418,7 @@ dummy_func(
DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
// Deopt unless 0 <= sub < PyList_Size(list)
- DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
- assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0);
+ DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
STAT_INC(BINARY_SUBSCR, hit);
@@ -436,8 +435,7 @@ dummy_func(
DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR);
// Deopt unless 0 <= sub < PyTuple_Size(list)
- DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
- assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0);
+ DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR);
STAT_INC(BINARY_SUBSCR, hit);
@@ -529,7 +527,7 @@ dummy_func(
DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
// Ensure nonnegative, zero-or-one-digit ints.
- DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR);
+ DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
// Ensure index < len(list)
DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR);
@@ -1828,12 +1826,13 @@ dummy_func(
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH);
DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH);
- DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_AND_BRANCH);
- DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH);
+ DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_AND_BRANCH);
+ DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_AND_BRANCH);
STAT_INC(COMPARE_AND_BRANCH, hit);
- assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
- Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->long_value.ob_digit[0];
- Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->long_value.ob_digit[0];
+ assert(_PyLong_DigitCount((PyLongObject *)left) <= 1 &&
+ _PyLong_DigitCount((PyLongObject *)right) <= 1);
+ Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left);
+ Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right);
// 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
int sign_ish = COMPARISON_BIT(ileft, iright);
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 9df173f843f806..d83901ce3e2158 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -597,8 +597,7 @@
DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
// Deopt unless 0 <= sub < PyList_Size(list)
- DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
- assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0);
+ DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
STAT_INC(BINARY_SUBSCR, hit);
@@ -622,8 +621,7 @@
DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR);
// Deopt unless 0 <= sub < PyTuple_Size(list)
- DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), BINARY_SUBSCR);
- assert(((PyLongObject *)_PyLong_GetZero())->long_value.ob_digit[0] == 0);
+ DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
DEOPT_IF(index >= PyTuple_GET_SIZE(tuple), BINARY_SUBSCR);
STAT_INC(BINARY_SUBSCR, hit);
@@ -748,7 +746,7 @@
DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
// Ensure nonnegative, zero-or-one-digit ints.
- DEOPT_IF(!_PyLong_IsPositiveSingleDigit(sub), STORE_SUBSCR);
+ DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
// Ensure index < len(list)
DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR);
@@ -2351,12 +2349,13 @@
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH);
DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH);
- DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_AND_BRANCH);
- DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_AND_BRANCH);
+ DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_AND_BRANCH);
+ DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_AND_BRANCH);
STAT_INC(COMPARE_AND_BRANCH, hit);
- assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
- Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->long_value.ob_digit[0];
- Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->long_value.ob_digit[0];
+ assert(_PyLong_DigitCount((PyLongObject *)left) <= 1 &&
+ _PyLong_DigitCount((PyLongObject *)right) <= 1);
+ Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left);
+ Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right);
// 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
int sign_ish = COMPARISON_BIT(ileft, iright);
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
diff --git a/Python/marshal.c b/Python/marshal.c
index 94e79d4392ae6d..2966139cec9ae9 100644
--- a/Python/marshal.c
+++ b/Python/marshal.c
@@ -11,6 +11,7 @@
#include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_code.h" // _PyCode_New()
+#include "pycore_long.h" // _PyLong_DigitCount
#include "pycore_hashtable.h" // _Py_hashtable_t
#include "marshal.h" // Py_MARSHAL_VERSION
@@ -232,13 +233,13 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p)
digit d;
W_TYPE(TYPE_LONG, p);
- if (Py_SIZE(ob) == 0) {
+ if (_PyLong_IsZero(ob)) {
w_long((long)0, p);
return;
}
/* set l to number of base PyLong_MARSHAL_BASE digits */
- n = Py_ABS(Py_SIZE(ob));
+ n = _PyLong_DigitCount(ob);
l = (n-1) * PyLong_MARSHAL_RATIO;
d = ob->long_value.ob_digit[n-1];
assert(d != 0); /* a PyLong is always normalized */
@@ -251,7 +252,7 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p)
p->error = WFERR_UNMARSHALLABLE;
return;
}
- w_long((long)(Py_SIZE(ob) > 0 ? l : -l), p);
+ w_long((long)(_PyLong_IsNegative(ob) ? -l : l), p);
for (i=0; i < n-1; i++) {
d = ob->long_value.ob_digit[i];
@@ -839,7 +840,7 @@ r_PyLong(RFILE *p)
if (ob == NULL)
return NULL;
- Py_SET_SIZE(ob, n > 0 ? size : -size);
+ _PyLong_SetSignAndDigitCount(ob, n < 0 ? -1 : 1, size);
for (i = 0; i < size-1; i++) {
d = 0;
diff --git a/Python/specialize.c b/Python/specialize.c
index 719bd5bda329ff..2e93ab193990a1 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -1308,7 +1308,7 @@ _Py_Specialize_BinarySubscr(
PyTypeObject *container_type = Py_TYPE(container);
if (container_type == &PyList_Type) {
if (PyLong_CheckExact(sub)) {
- if (Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) {
+ if (_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
instr->op.code = BINARY_SUBSCR_LIST_INT;
goto success;
}
@@ -1321,7 +1321,7 @@ _Py_Specialize_BinarySubscr(
}
if (container_type == &PyTuple_Type) {
if (PyLong_CheckExact(sub)) {
- if (Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1) {
+ if (_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
instr->op.code = BINARY_SUBSCR_TUPLE_INT;
goto success;
}
@@ -1389,7 +1389,7 @@ _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *ins
PyTypeObject *container_type = Py_TYPE(container);
if (container_type == &PyList_Type) {
if (PyLong_CheckExact(sub)) {
- if ((Py_SIZE(sub) == 0 || Py_SIZE(sub) == 1)
+ if (_PyLong_IsNonNegativeCompact((PyLongObject *)sub)
&& ((PyLongObject *)sub)->long_value.ob_digit[0] < (size_t)PyList_GET_SIZE(container))
{
instr->op.code = STORE_SUBSCR_LIST_INT;
@@ -2007,7 +2007,7 @@ _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *inst
goto success;
}
if (PyLong_CheckExact(lhs)) {
- if (Py_ABS(Py_SIZE(lhs)) <= 1 && Py_ABS(Py_SIZE(rhs)) <= 1) {
+ if (_PyLong_IsCompact((PyLongObject *)lhs) && _PyLong_IsCompact((PyLongObject *)rhs)) {
instr->op.code = COMPARE_AND_BRANCH_INT;
goto success;
}
diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py
index 26a1d3b99e1f7f..b22d5b75139d80 100644
--- a/Tools/build/deepfreeze.py
+++ b/Tools/build/deepfreeze.py
@@ -312,7 +312,7 @@ def generate_tuple(self, name: str, t: Tuple[object, ...]) -> str:
return f"& {name}._object.ob_base.ob_base"
def _generate_int_for_bits(self, name: str, i: int, digit: int) -> None:
- sign = -1 if i < 0 else 0 if i == 0 else +1
+ sign = (i > 0) - (i < 0)
i = abs(i)
digits: list[int] = []
while i:
@@ -321,10 +321,12 @@ def _generate_int_for_bits(self, name: str, i: int, digit: int) -> None:
self.write("static")
with self.indent():
with self.block("struct"):
- self.write("PyObject_VAR_HEAD")
+ self.write("PyObject ob_base;")
+ self.write("uintptr_t lv_tag;")
self.write(f"digit ob_digit[{max(1, len(digits))}];")
with self.block(f"{name} =", ";"):
- self.object_var_head("PyLong_Type", sign*len(digits))
+ self.object_head("PyLong_Type")
+ self.write(f".lv_tag = TAG_FROM_SIGN_AND_SIZE({sign}, {len(digits)}),")
if digits:
ds = ", ".join(map(str, digits))
self.write(f".ob_digit = {{ {ds} }},")
@@ -348,7 +350,7 @@ def generate_int(self, name: str, i: int) -> str:
self.write('#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"')
self.write("#endif")
# If neither clause applies, it won't compile
- return f"& {name}.ob_base.ob_base"
+ return f"& {name}.ob_base"
def generate_float(self, name: str, x: float) -> str:
with self.block(f"static PyFloatObject {name} =", ";"):
diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py
index 56d6970b29249c..e38bd59e20a305 100755
--- a/Tools/gdb/libpython.py
+++ b/Tools/gdb/libpython.py
@@ -882,10 +882,16 @@ class PyLongObjectPtr(PyObjectPtr):
def proxyval(self, visited):
'''
Python's Include/longobjrep.h has this declaration:
- struct _longobject {
- PyObject_VAR_HEAD
- digit ob_digit[1];
- };
+
+ typedef struct _PyLongValue {
+ uintptr_t lv_tag; /* Number of digits, sign and flags */
+ digit ob_digit[1];
+ } _PyLongValue;
+
+ struct _longobject {
+ PyObject_HEAD
+ _PyLongValue long_value;
+ };
with this description:
The absolute value of a number is equal to
@@ -897,11 +903,13 @@ def proxyval(self, visited):
#define PyLong_SHIFT 30
#define PyLong_SHIFT 15
'''
- ob_size = int(self.field('ob_size'))
- if ob_size == 0:
+ long_value = self.field('long_value')
+ lv_tag = int(long_value['lv_tag'])
+ size = lv_tag >> 3
+ if size == 0:
return 0
- ob_digit = self.field('long_value')['ob_digit']
+ ob_digit = long_value['ob_digit']
if gdb.lookup_type('digit').sizeof == 2:
SHIFT = 15
@@ -909,9 +917,9 @@ def proxyval(self, visited):
SHIFT = 30
digits = [int(ob_digit[i]) * 2**(SHIFT*i)
- for i in safe_range(abs(ob_size))]
+ for i in safe_range(size)]
result = sum(digits)
- if ob_size < 0:
+ if (lv_tag & 3) == 2:
result = -result
return result
From ae4c54b1cd2c0e2988d092c0e9a96ad30e1f29a1 Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Wed, 22 Mar 2023 17:59:32 +0300
Subject: [PATCH 189/280] gh-102033: Fix syntax error in `Tools/c-analyzer`
(GH-102066)
The easiest way to format strings with `{}` meaningful chars is via `%`.
---
Tools/c-analyzer/c_analyzer/__main__.py | 2 -
Tools/c-analyzer/c_analyzer/info.py | 4 -
Tools/c-analyzer/c_common/info.py | 0
Tools/c-analyzer/c_common/iterutil.py | 4 -
Tools/c-analyzer/c_common/show.py | 0
Tools/c-analyzer/c_parser/__main__.py | 4 -
Tools/c-analyzer/c_parser/_state_machine.py | 244 --------------------
Tools/c-analyzer/c_parser/info.py | 3 +-
Tools/c-analyzer/c_parser/parser/_alt.py | 6 -
Tools/c-analyzer/c_parser/parser/_delim.py | 54 -----
Tools/c-analyzer/c_parser/parser/_global.py | 1 -
Tools/c-analyzer/cpython/__main__.py | 2 -
Tools/c-analyzer/cpython/_analyzer.py | 4 -
Tools/c-analyzer/cpython/_capi.py | 10 +-
14 files changed, 3 insertions(+), 335 deletions(-)
delete mode 100644 Tools/c-analyzer/c_common/info.py
delete mode 100644 Tools/c-analyzer/c_common/show.py
delete mode 100644 Tools/c-analyzer/c_parser/_state_machine.py
delete mode 100644 Tools/c-analyzer/c_parser/parser/_alt.py
delete mode 100644 Tools/c-analyzer/c_parser/parser/_delim.py
diff --git a/Tools/c-analyzer/c_analyzer/__main__.py b/Tools/c-analyzer/c_analyzer/__main__.py
index 5d89b29adf899e..cde39bc4e649d9 100644
--- a/Tools/c-analyzer/c_analyzer/__main__.py
+++ b/Tools/c-analyzer/c_analyzer/__main__.py
@@ -18,10 +18,8 @@
configure_logger,
get_prog,
filter_filenames,
- iter_marks,
)
from c_parser.info import KIND
-from c_parser.match import is_type_decl
from .match import filter_forward
from . import (
analyze as _analyze,
diff --git a/Tools/c-analyzer/c_analyzer/info.py b/Tools/c-analyzer/c_analyzer/info.py
index 27c3a5a4ee76f2..d231e07a60dd8e 100644
--- a/Tools/c-analyzer/c_analyzer/info.py
+++ b/Tools/c-analyzer/c_analyzer/info.py
@@ -1,4 +1,3 @@
-from collections import namedtuple
import os.path
from c_common import fsutil
@@ -13,9 +12,6 @@
from c_parser.match import (
is_type_decl,
)
-from .match import (
- is_process_global,
-)
IGNORED = _misc.Labeled('IGNORED')
diff --git a/Tools/c-analyzer/c_common/info.py b/Tools/c-analyzer/c_common/info.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/Tools/c-analyzer/c_common/iterutil.py b/Tools/c-analyzer/c_common/iterutil.py
index 6ded105304e454..dda3dd57c1cf5c 100644
--- a/Tools/c-analyzer/c_common/iterutil.py
+++ b/Tools/c-analyzer/c_common/iterutil.py
@@ -1,7 +1,3 @@
-
-_NOT_SET = object()
-
-
def peek_and_iter(items):
if not items:
return None, None
diff --git a/Tools/c-analyzer/c_common/show.py b/Tools/c-analyzer/c_common/show.py
deleted file mode 100644
index e69de29bb2d1d6..00000000000000
diff --git a/Tools/c-analyzer/c_parser/__main__.py b/Tools/c-analyzer/c_parser/__main__.py
index 78f47a1808f50b..2454fcba814291 100644
--- a/Tools/c-analyzer/c_parser/__main__.py
+++ b/Tools/c-analyzer/c_parser/__main__.py
@@ -1,10 +1,7 @@
import logging
-import os.path
import sys
-from c_common import fsutil
from c_common.scriptutil import (
- CLIArgSpec as Arg,
add_verbosity_cli,
add_traceback_cli,
add_kind_filtering_cli,
@@ -15,7 +12,6 @@
get_prog,
main_for_filenames,
)
-from .preprocessor import get_preprocessor
from .preprocessor.__main__ import (
add_common_cli as add_preprocessor_cli,
)
diff --git a/Tools/c-analyzer/c_parser/_state_machine.py b/Tools/c-analyzer/c_parser/_state_machine.py
deleted file mode 100644
index 875323188aadfd..00000000000000
--- a/Tools/c-analyzer/c_parser/_state_machine.py
+++ /dev/null
@@ -1,244 +0,0 @@
-
-f'''
- struct {ANON_IDENTIFIER};
- struct {{ ... }}
- struct {IDENTIFIER} {{ ... }}
-
- union {ANON_IDENTIFIER};
- union {{ ... }}
- union {IDENTIFIER} {{ ... }}
-
- enum {ANON_IDENTIFIER};
- enum {{ ... }}
- enum {IDENTIFIER} {{ ... }}
-
- typedef {VARTYPE} {IDENTIFIER};
- typedef {IDENTIFIER};
- typedef {IDENTIFIER};
- typedef {IDENTIFIER};
-'''
-
-
-def parse(srclines):
- if isinstance(srclines, str): # a filename
- raise NotImplementedError
-
-
-
-# This only handles at most 10 nested levels.
-#MATCHED_PARENS = textwrap.dedent(rf'''
-# # matched parens
-# (?:
-# [(] # level 0
-# (?:
-# [^()]*
-# [(] # level 1
-# (?:
-# [^()]*
-# [(] # level 2
-# (?:
-# [^()]*
-# [(] # level 3
-# (?:
-# [^()]*
-# [(] # level 4
-# (?:
-# [^()]*
-# [(] # level 5
-# (?:
-# [^()]*
-# [(] # level 6
-# (?:
-# [^()]*
-# [(] # level 7
-# (?:
-# [^()]*
-# [(] # level 8
-# (?:
-# [^()]*
-# [(] # level 9
-# (?:
-# [^()]*
-# [(] # level 10
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )*
-# [^()]*
-# [)]
-# )
-# # end matched parens
-# ''')
-
-r'''
- # for loop
- (?:
- \s* \b for
- \s* [(]
- (
- [^;]* ;
- [^;]* ;
- .*?
- ) #
- [)]
- \s*
- (?:
- (?:
- (
- {_ind(SIMPLE_STMT, 6)}
- ) #
- ;
- )
- |
- ( {{ ) #
- )
- )
- |
-
-
-
- (
- (?:
- (?:
- (?:
- {_ind(SIMPLE_STMT, 6)}
- )?
- return \b \s*
- {_ind(INITIALIZER, 5)}
- )
- |
- (?:
- (?:
- {IDENTIFIER} \s*
- (?: . | -> ) \s*
- )*
- {IDENTIFIER}
- \s* = \s*
- {_ind(INITIALIZER, 5)}
- )
- |
- (?:
- {_ind(SIMPLE_STMT, 5)}
- )
- )
- |
- # cast compound literal
- (?:
- (?:
- [^'"{{}};]*
- {_ind(STRING_LITERAL, 5)}
- )*
- [^'"{{}};]*?
- [^'"{{}};=]
- =
- \s* [(] [^)]* [)]
- \s* {{ [^;]* }}
- )
- ) #
-
-
-
- # compound statement
- (?:
- (
- (?:
-
- # "for" statements are handled separately above.
- (?: (?: else \s+ )? if | switch | while ) \s*
- {_ind(COMPOUND_HEAD, 5)}
- )
- |
- (?: else | do )
- # We do not worry about compound statements for labels,
- # "case", or "default".
- )? #
- \s*
- ( {{ ) #
- )
-
-
-
- (
- (?:
- [^'"{{}};]*
- {_ind(STRING_LITERAL, 5)}
- )*
- [^'"{{}};]*
- # Presumably we will not see "== {{".
- [^\s='"{{}};]
- )? #
-
-
-
- (
- \b
- (?:
- # We don't worry about labels with a compound statement.
- (?:
- switch \s* [(] [^{{]* [)]
- )
- |
- (?:
- case \b \s* [^:]+ [:]
- )
- |
- (?:
- default \s* [:]
- )
- |
- (?:
- do
- )
- |
- (?:
- while \s* [(] [^{{]* [)]
- )
- |
- #(?:
- # for \s* [(] [^{{]* [)]
- # )
- #|
- (?:
- if \s* [(]
- (?: [^{{]* [^)] \s* {{ )* [^{{]*
- [)]
- )
- |
- (?:
- else
- (?:
- \s*
- if \s* [(]
- (?: [^{{]* [^)] \s* {{ )* [^{{]*
- [)]
- )?
- )
- )
- )? #
-'''
diff --git a/Tools/c-analyzer/c_parser/info.py b/Tools/c-analyzer/c_parser/info.py
index 3fa9fefbd5ec0b..799f9237877447 100644
--- a/Tools/c-analyzer/c_parser/info.py
+++ b/Tools/c-analyzer/c_parser/info.py
@@ -1,6 +1,5 @@
from collections import namedtuple
import enum
-import os.path
import re
from c_common import fsutil
@@ -8,7 +7,7 @@
import c_common.misc as _misc
import c_common.strutil as _strutil
import c_common.tables as _tables
-from .parser._regexes import SIMPLE_TYPE, _STORAGE
+from .parser._regexes import _STORAGE
FIXED_TYPE = _misc.Labeled('FIXED_TYPE')
diff --git a/Tools/c-analyzer/c_parser/parser/_alt.py b/Tools/c-analyzer/c_parser/parser/_alt.py
deleted file mode 100644
index 05a9101b4f529a..00000000000000
--- a/Tools/c-analyzer/c_parser/parser/_alt.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-def _parse(srclines, anon_name):
- text = ' '.join(l for _, l in srclines)
-
- from ._delim import parse
- yield from parse(text, anon_name)
diff --git a/Tools/c-analyzer/c_parser/parser/_delim.py b/Tools/c-analyzer/c_parser/parser/_delim.py
deleted file mode 100644
index 51433a629d3a35..00000000000000
--- a/Tools/c-analyzer/c_parser/parser/_delim.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import re
-import textwrap
-
-from ._regexes import _ind, STRING_LITERAL
-
-
-def parse(text, anon_name):
- context = None
- data = None
- for m in DELIMITER_RE.find_iter(text):
- before, opened, closed = m.groups()
- delim = opened or closed
-
- handle_segment = HANDLERS[context][delim]
- result, context, data = handle_segment(before, delim, data)
- if result:
- yield result
-
-
-DELIMITER = textwrap.dedent(rf'''
- (
- (?:
- [^'"()\[\]{};]*
- {_ind(STRING_LITERAL, 3)}
- }*
- [^'"()\[\]{};]+
- )? #
- (?:
- (
- [(\[{]
- ) #
- |
- (
- [)\]};]
- ) #
- )?
- ''')
-DELIMITER_RE = re.compile(DELIMITER, re.VERBOSE)
-
-_HANDLERS = {
- None: { # global
- # opened
- '{': ...,
- '[': None,
- '(': None,
- # closed
- '}': None,
- ']': None,
- ')': None,
- ';': ...,
- },
- '': {
- },
-}
diff --git a/Tools/c-analyzer/c_parser/parser/_global.py b/Tools/c-analyzer/c_parser/parser/_global.py
index 35947c12998135..b1ac9f5db034e1 100644
--- a/Tools/c-analyzer/c_parser/parser/_global.py
+++ b/Tools/c-analyzer/c_parser/parser/_global.py
@@ -9,7 +9,6 @@
set_capture_groups,
)
from ._compound_decl_body import DECL_BODY_PARSERS
-#from ._func_body import parse_function_body
from ._func_body import parse_function_statics as parse_function_body
diff --git a/Tools/c-analyzer/cpython/__main__.py b/Tools/c-analyzer/cpython/__main__.py
index fe7a16726f45a9..ec026c6932f1f4 100644
--- a/Tools/c-analyzer/cpython/__main__.py
+++ b/Tools/c-analyzer/cpython/__main__.py
@@ -2,7 +2,6 @@
import sys
import textwrap
-from c_common.fsutil import expand_filenames, iter_files_by_suffix
from c_common.scriptutil import (
VERBOSITY,
add_verbosity_cli,
@@ -11,7 +10,6 @@
add_kind_filtering_cli,
add_files_cli,
add_progress_cli,
- main_for_filenames,
process_args_by_key,
configure_logger,
get_prog,
diff --git a/Tools/c-analyzer/cpython/_analyzer.py b/Tools/c-analyzer/cpython/_analyzer.py
index cfe5e75f2f4df6..68d6b31cf2b6f0 100644
--- a/Tools/c-analyzer/cpython/_analyzer.py
+++ b/Tools/c-analyzer/cpython/_analyzer.py
@@ -4,16 +4,12 @@
from c_common.clsutil import classonly
from c_parser.info import (
KIND,
- DeclID,
Declaration,
TypeDeclaration,
- TypeDef,
- Struct,
Member,
FIXED_TYPE,
)
from c_parser.match import (
- is_type_decl,
is_pots,
is_funcptr,
)
diff --git a/Tools/c-analyzer/cpython/_capi.py b/Tools/c-analyzer/cpython/_capi.py
index df8159a8cc169f..4552f71479bd06 100644
--- a/Tools/c-analyzer/cpython/_capi.py
+++ b/Tools/c-analyzer/cpython/_capi.py
@@ -7,7 +7,7 @@
from c_common.tables import build_table, resolve_columns
from c_parser.parser._regexes import _ind
-from ._files import iter_header_files, resolve_filename
+from ._files import iter_header_files
from . import REPO_ROOT
@@ -610,8 +610,7 @@ def _render_item_full(item, groupby, verbose):
yield item.name
yield f' {"filename:":10} {item.relfile}'
for extra in ('kind', 'level'):
- #if groupby != extra:
- yield f' {extra+":":10} {getattr(item, extra)}'
+ yield f' {extra+":":10} {getattr(item, extra)}'
if verbose:
print(' ---------------------------------------')
for lno, line in enumerate(item.text, item.lno):
@@ -636,7 +635,6 @@ def render_summary(items, *,
subtotals = summary['totals']['subs']
bygroup = summary['totals']['bygroup']
- lastempty = False
for outer, subtotal in subtotals.items():
if bygroup:
subtotal = f'({subtotal})'
@@ -646,10 +644,6 @@ def render_summary(items, *,
if outer in bygroup:
for inner, count in bygroup[outer].items():
yield f' {inner + ":":9} {count}'
- lastempty = False
- else:
- lastempty = True
-
total = f'*{summary["totals"]["all"]}*'
label = '*total*:'
if bygroup:
From 4080fb759a013aaeebd8fe8ee9951b573a9d8771 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?=
Date: Wed, 22 Mar 2023 17:52:10 +0000
Subject: [PATCH 190/280] gh-102780: Fix uncancel() call in asyncio timeouts
(#102815)
Also use `raise TimeOut from ` so that the CancelledError is set
in the `__cause__` field rather than in the `__context__` field.
Co-authored-by: Guido van Rossum
Co-authored-by: Alex Waygood
---
Doc/library/asyncio-task.rst | 16 ++++++++--
Lib/asyncio/timeouts.py | 7 +++--
Lib/test/test_asyncio/test_timeouts.py | 30 +++++++++++++++++++
...-03-22-16-15-18.gh-issue-102780.NEcljy.rst | 3 ++
4 files changed, 50 insertions(+), 6 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-22-16-15-18.gh-issue-102780.NEcljy.rst
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index a0900cd25a7731..d908e45b3c8d8b 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -300,13 +300,17 @@ in the task at the next opportunity.
It is recommended that coroutines use ``try/finally`` blocks to robustly
perform clean-up logic. In case :exc:`asyncio.CancelledError`
is explicitly caught, it should generally be propagated when
-clean-up is complete. Most code can safely ignore :exc:`asyncio.CancelledError`.
+clean-up is complete. :exc:`asyncio.CancelledError` directly subclasses
+:exc:`BaseException` so most code will not need to be aware of it.
The asyncio components that enable structured concurrency, like
:class:`asyncio.TaskGroup` and :func:`asyncio.timeout`,
are implemented using cancellation internally and might misbehave if
a coroutine swallows :exc:`asyncio.CancelledError`. Similarly, user code
-should not call :meth:`uncancel `.
+should not generally call :meth:`uncancel `.
+However, in cases when suppressing :exc:`asyncio.CancelledError` is
+truly desired, it is necessary to also call ``uncancel()`` to completely
+remove the cancellation state.
.. _taskgroups:
@@ -1148,7 +1152,9 @@ Task Object
Therefore, unlike :meth:`Future.cancel`, :meth:`Task.cancel` does
not guarantee that the Task will be cancelled, although
suppressing cancellation completely is not common and is actively
- discouraged.
+ discouraged. Should the coroutine nevertheless decide to suppress
+ the cancellation, it needs to call :meth:`Task.uncancel` in addition
+ to catching the exception.
.. versionchanged:: 3.9
Added the *msg* parameter.
@@ -1238,6 +1244,10 @@ Task Object
with :meth:`uncancel`. :class:`TaskGroup` context managers use
:func:`uncancel` in a similar fashion.
+ If end-user code is, for some reason, suppresing cancellation by
+ catching :exc:`CancelledError`, it needs to call this method to remove
+ the cancellation state.
+
.. method:: cancelling()
Return the number of pending cancellation requests to this Task, i.e.,
diff --git a/Lib/asyncio/timeouts.py b/Lib/asyncio/timeouts.py
index d07b291005e8f9..029c468739bf2d 100644
--- a/Lib/asyncio/timeouts.py
+++ b/Lib/asyncio/timeouts.py
@@ -84,6 +84,7 @@ def __repr__(self) -> str:
async def __aenter__(self) -> "Timeout":
self._state = _State.ENTERED
self._task = tasks.current_task()
+ self._cancelling = self._task.cancelling()
if self._task is None:
raise RuntimeError("Timeout should be used inside a task")
self.reschedule(self._when)
@@ -104,10 +105,10 @@ async def __aexit__(
if self._state is _State.EXPIRING:
self._state = _State.EXPIRED
- if self._task.uncancel() == 0 and exc_type is exceptions.CancelledError:
- # Since there are no outstanding cancel requests, we're
+ if self._task.uncancel() <= self._cancelling and exc_type is exceptions.CancelledError:
+ # Since there are no new cancel requests, we're
# handling this.
- raise TimeoutError
+ raise TimeoutError from exc_val
elif self._state is _State.ENTERED:
self._state = _State.EXITED
diff --git a/Lib/test/test_asyncio/test_timeouts.py b/Lib/test/test_asyncio/test_timeouts.py
index b9bac6f783776b..8b6b9a1fea0be8 100644
--- a/Lib/test/test_asyncio/test_timeouts.py
+++ b/Lib/test/test_asyncio/test_timeouts.py
@@ -247,6 +247,36 @@ async def test_nested_timeout_in_finally(self):
async with asyncio.timeout(0.01):
await asyncio.sleep(10)
+ async def test_timeout_after_cancellation(self):
+ try:
+ asyncio.current_task().cancel()
+ await asyncio.sleep(1) # work which will be cancelled
+ except asyncio.CancelledError:
+ pass
+ finally:
+ with self.assertRaises(TimeoutError):
+ async with asyncio.timeout(0.0):
+ await asyncio.sleep(1) # some cleanup
+
+ async def test_cancel_in_timeout_after_cancellation(self):
+ try:
+ asyncio.current_task().cancel()
+ await asyncio.sleep(1) # work which will be cancelled
+ except asyncio.CancelledError:
+ pass
+ finally:
+ with self.assertRaises(asyncio.CancelledError):
+ async with asyncio.timeout(1.0):
+ asyncio.current_task().cancel()
+ await asyncio.sleep(2) # some cleanup
+
+ async def test_timeout_exception_cause (self):
+ with self.assertRaises(asyncio.TimeoutError) as exc:
+ async with asyncio.timeout(0):
+ await asyncio.sleep(1)
+ cause = exc.exception.__cause__
+ assert isinstance(cause, asyncio.CancelledError)
+
if __name__ == '__main__':
unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2023-03-22-16-15-18.gh-issue-102780.NEcljy.rst b/Misc/NEWS.d/next/Library/2023-03-22-16-15-18.gh-issue-102780.NEcljy.rst
new file mode 100644
index 00000000000000..2aaffe34b86414
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-22-16-15-18.gh-issue-102780.NEcljy.rst
@@ -0,0 +1,3 @@
+The :class:`asyncio.Timeout` context manager now works reliably even when performing cleanup due
+to task cancellation. Previously it could raise a
+:exc:`~asyncio.CancelledError` instead of an :exc:`~asyncio.TimeoutError` in such cases.
From 27f390bef395d57558c922b2c23be1c8be3d70f3 Mon Sep 17 00:00:00 2001
From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
Date: Wed, 22 Mar 2023 18:10:48 +0000
Subject: [PATCH 191/280] gh-102859: Remove JUMP_IF_FALSE_OR_POP and
JUMP_IF_TRUE_OR_POP (#102870)
---
Doc/library/dis.rst | 24 ---
Doc/whatsnew/3.12.rst | 3 +
Include/internal/pycore_opcode.h | 30 +--
Include/opcode.h | 108 +++++-----
Lib/importlib/_bootstrap_external.py | 3 +-
Lib/opcode.py | 2 -
Lib/test/test__opcode.py | 4 -
Lib/test/test_peepholer.py | 24 +--
...-03-21-00-46-36.gh-issue-102859.PRkGca.rst | 2 +
Objects/frameobject.c | 14 +-
Python/bytecodes.c | 123 -----------
Python/compile.c | 192 +++++++-----------
Python/generated_cases.c.h | 142 -------------
Python/opcode_metadata.h | 25 ---
Python/opcode_targets.h | 20 +-
Python/tier2_typepropagator.c.h | 29 ---
16 files changed, 173 insertions(+), 572 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-21-00-46-36.gh-issue-102859.PRkGca.rst
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index f4f47b3bf4846d..b06fe67a983aa1 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -1152,30 +1152,6 @@ iterations of the loop.
.. versionchanged:: 3.12
This is no longer a pseudo-instruction.
-
-.. opcode:: JUMP_IF_TRUE_OR_POP (delta)
-
- If ``STACK[-1]`` is true, increments the bytecode counter by *delta* and leaves
- ``STACK[-1]`` on the stack. Otherwise (``STACK[-1]`` is false), ``STACK[-1]``
- is popped.
-
- .. versionadded:: 3.1
-
- .. versionchanged:: 3.11
- The oparg is now a relative delta rather than an absolute target.
-
-.. opcode:: JUMP_IF_FALSE_OR_POP (delta)
-
- If ``STACK[-1]`` is false, increments the bytecode counter by *delta* and leaves
- ``STACK[-1]`` on the stack. Otherwise (``STACK[-1]`` is true), ``STACK[-1]`` is
- popped.
-
- .. versionadded:: 3.1
-
- .. versionchanged:: 3.11
- The oparg is now a relative delta rather than an absolute target.
-
-
.. opcode:: FOR_ITER (delta)
``STACK[-1]`` is an :term:`iterator`. Call its :meth:`~iterator.__next__` method.
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index b7c956d7f78456..06ea416d751354 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -435,6 +435,9 @@ CPython bytecode changes
:opcode:`LOAD_METHOD` instruction if the low bit of its oparg is set.
(Contributed by Ken Jin in :gh:`93429`.)
+* Removed the :opcode:`JUMP_IF_FALSE_OR_POP` and :opcode:`JUMP_IF_TRUE_OR_POP`
+ instructions. (Contributed by Irit Katriel in :gh:`102859`.)
+
Demos and Tools
===============
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 5dd2ed17adee51..4a3270dd11b2de 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -21,7 +21,7 @@ static const uint32_t _PyOpcode_RelativeJump[9] = {
0U,
0U,
536870912U,
- 135118848U,
+ 135020544U,
4163U,
0U,
0U,
@@ -32,7 +32,7 @@ static const uint32_t _PyOpcode_Jump[9] = {
0U,
0U,
536870912U,
- 135118848U,
+ 135020544U,
4163U,
0U,
0U,
@@ -148,8 +148,6 @@ const uint8_t _PyOpcode_Deopt[256] = {
[JUMP_BACKWARD_NO_INTERRUPT] = JUMP_BACKWARD_NO_INTERRUPT,
[JUMP_BACKWARD_QUICK] = JUMP_BACKWARD,
[JUMP_FORWARD] = JUMP_FORWARD,
- [JUMP_IF_FALSE_OR_POP] = JUMP_IF_FALSE_OR_POP,
- [JUMP_IF_TRUE_OR_POP] = JUMP_IF_TRUE_OR_POP,
[KW_NAMES] = KW_NAMES,
[LIST_APPEND] = LIST_APPEND,
[LIST_EXTEND] = LIST_EXTEND,
@@ -348,9 +346,9 @@ static const char *const _PyOpcode_OpName[263] = {
[IMPORT_NAME] = "IMPORT_NAME",
[IMPORT_FROM] = "IMPORT_FROM",
[JUMP_FORWARD] = "JUMP_FORWARD",
- [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP",
- [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP",
[LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
+ [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
+ [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
[POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
[POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
[LOAD_GLOBAL] = "LOAD_GLOBAL",
@@ -380,7 +378,7 @@ static const char *const _PyOpcode_OpName[263] = {
[JUMP_BACKWARD] = "JUMP_BACKWARD",
[COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH",
[CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
- [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
+ [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
[EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD",
@@ -390,30 +388,28 @@ static const char *const _PyOpcode_OpName[263] = {
[YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS",
- [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
- [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
+ [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
+ [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING",
- [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
- [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
+ [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
+ [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
- [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
- [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
+ [SEND_GEN] = "SEND_GEN",
+ [BB_BRANCH] = "BB_BRANCH",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
[CALL_INTRINSIC_1] = "CALL_INTRINSIC_1",
[CALL_INTRINSIC_2] = "CALL_INTRINSIC_2",
- [SEND_GEN] = "SEND_GEN",
- [BB_BRANCH] = "BB_BRANCH",
[BB_BRANCH_IF_FLAG_UNSET] = "BB_BRANCH_IF_FLAG_UNSET",
[BB_BRANCH_IF_FLAG_SET] = "BB_BRANCH_IF_FLAG_SET",
[BB_JUMP_IF_FLAG_UNSET] = "BB_JUMP_IF_FLAG_UNSET",
@@ -438,6 +434,8 @@ static const char *const _PyOpcode_OpName[263] = {
[STORE_FAST_BOXED_UNBOXED] = "STORE_FAST_BOXED_UNBOXED",
[STORE_FAST_UNBOXED_BOXED] = "STORE_FAST_UNBOXED_BOXED",
[STORE_FAST_UNBOXED_UNBOXED] = "STORE_FAST_UNBOXED_UNBOXED",
+ [199] = "<199>",
+ [200] = "<200>",
[201] = "<201>",
[202] = "<202>",
[203] = "<203>",
@@ -505,6 +503,8 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
+ case 199: \
+ case 200: \
case 201: \
case 202: \
case 203: \
diff --git a/Include/opcode.h b/Include/opcode.h
index 1a883aafd96f1b..6f02501b835718 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -66,8 +66,6 @@ extern "C" {
#define IMPORT_NAME 108
#define IMPORT_FROM 109
#define JUMP_FORWARD 110
-#define JUMP_IF_FALSE_OR_POP 111
-#define JUMP_IF_TRUE_OR_POP 112
#define POP_JUMP_IF_FALSE 114
#define POP_JUMP_IF_TRUE 115
#define LOAD_GLOBAL 116
@@ -180,20 +178,20 @@ extern "C" {
#define LOAD_ATTR_METHOD_WITH_VALUES 86
#define LOAD_CONST__LOAD_FAST 87
#define LOAD_FAST__LOAD_CONST 88
-#define LOAD_FAST__LOAD_FAST 113
-#define LOAD_GLOBAL_BUILTIN 143
-#define LOAD_GLOBAL_MODULE 153
-#define STORE_ATTR_INSTANCE_VALUE 154
-#define STORE_ATTR_SLOT 158
-#define STORE_ATTR_WITH_HINT 159
-#define STORE_FAST__LOAD_FAST 160
-#define STORE_FAST__STORE_FAST 161
-#define STORE_SUBSCR_DICT 166
-#define STORE_SUBSCR_LIST_INT 167
-#define UNPACK_SEQUENCE_LIST 168
-#define UNPACK_SEQUENCE_TUPLE 169
-#define UNPACK_SEQUENCE_TWO_TUPLE 170
-#define SEND_GEN 175
+#define LOAD_FAST__LOAD_FAST 111
+#define LOAD_GLOBAL_BUILTIN 112
+#define LOAD_GLOBAL_MODULE 113
+#define STORE_ATTR_INSTANCE_VALUE 143
+#define STORE_ATTR_SLOT 153
+#define STORE_ATTR_WITH_HINT 154
+#define STORE_FAST__LOAD_FAST 158
+#define STORE_FAST__STORE_FAST 159
+#define STORE_SUBSCR_DICT 160
+#define STORE_SUBSCR_LIST_INT 161
+#define UNPACK_SEQUENCE_LIST 166
+#define UNPACK_SEQUENCE_TUPLE 167
+#define UNPACK_SEQUENCE_TWO_TUPLE 168
+#define SEND_GEN 169
#define DO_TRACING 255
// Tier 2 interpreter ops
#define RESUME_QUICK 5
@@ -250,46 +248,46 @@ extern "C" {
#define LOAD_ATTR_METHOD_WITH_VALUES 86
#define LOAD_CONST__LOAD_FAST 87
#define LOAD_FAST__LOAD_CONST 88
-#define LOAD_FAST__LOAD_FAST 113
-#define LOAD_GLOBAL_BUILTIN 143
-#define LOAD_GLOBAL_MODULE 153
-#define STORE_ATTR_INSTANCE_VALUE 154
-#define STORE_ATTR_SLOT 158
-#define STORE_ATTR_WITH_HINT 159
-#define STORE_FAST__LOAD_FAST 160
-#define STORE_FAST__STORE_FAST 161
-#define STORE_SUBSCR_DICT 166
-#define STORE_SUBSCR_LIST_INT 167
-#define UNPACK_SEQUENCE_LIST 168
-#define UNPACK_SEQUENCE_TUPLE 169
-#define UNPACK_SEQUENCE_TWO_TUPLE 170
-#define SEND_GEN 175
+#define LOAD_FAST__LOAD_FAST 111
+#define LOAD_GLOBAL_BUILTIN 112
+#define LOAD_GLOBAL_MODULE 113
+#define STORE_ATTR_INSTANCE_VALUE 143
+#define STORE_ATTR_SLOT 153
+#define STORE_ATTR_WITH_HINT 154
+#define STORE_FAST__LOAD_FAST 158
+#define STORE_FAST__STORE_FAST 159
+#define STORE_SUBSCR_DICT 160
+#define STORE_SUBSCR_LIST_INT 161
+#define UNPACK_SEQUENCE_LIST 166
+#define UNPACK_SEQUENCE_TUPLE 167
+#define UNPACK_SEQUENCE_TWO_TUPLE 168
+#define SEND_GEN 169
#define DO_TRACING 255
-#define BB_BRANCH 176
-#define BB_BRANCH_IF_FLAG_UNSET 177
-#define BB_BRANCH_IF_FLAG_SET 178
-#define BB_JUMP_IF_FLAG_UNSET 179
-#define BB_JUMP_IF_FLAG_SET 180
-#define BB_TEST_ITER 181
-#define BB_TEST_IF_FALSE_OR_POP 182
-#define BB_TEST_IF_TRUE_OR_POP 183
-#define BB_TEST_POP_IF_FALSE 184
-#define BB_TEST_POP_IF_TRUE 185
-#define BB_TEST_POP_IF_NOT_NONE 186
-#define BB_TEST_POP_IF_NONE 187
-#define BB_JUMP_BACKWARD_LAZY 188
-#define BINARY_CHECK_INT 189
-#define BINARY_CHECK_FLOAT 190
-#define UNARY_CHECK_FLOAT 191
-#define BINARY_OP_ADD_INT_REST 192
-#define BINARY_OP_ADD_FLOAT_UNBOXED 193
-#define POP_TOP_NO_DECREF 194
-#define UNBOX_FLOAT 195
-#define BOX_FLOAT 196
-#define LOAD_FAST_NO_INCREF 197
-#define STORE_FAST_BOXED_UNBOXED 198
-#define STORE_FAST_UNBOXED_BOXED 199
-#define STORE_FAST_UNBOXED_UNBOXED 200
+#define BB_BRANCH 170
+#define BB_BRANCH_IF_FLAG_UNSET 175
+#define BB_BRANCH_IF_FLAG_SET 176
+#define BB_JUMP_IF_FLAG_UNSET 177
+#define BB_JUMP_IF_FLAG_SET 178
+#define BB_TEST_ITER 179
+#define BB_TEST_IF_FALSE_OR_POP 180
+#define BB_TEST_IF_TRUE_OR_POP 181
+#define BB_TEST_POP_IF_FALSE 182
+#define BB_TEST_POP_IF_TRUE 183
+#define BB_TEST_POP_IF_NOT_NONE 184
+#define BB_TEST_POP_IF_NONE 185
+#define BB_JUMP_BACKWARD_LAZY 186
+#define BINARY_CHECK_INT 187
+#define BINARY_CHECK_FLOAT 188
+#define UNARY_CHECK_FLOAT 189
+#define BINARY_OP_ADD_INT_REST 190
+#define BINARY_OP_ADD_FLOAT_UNBOXED 191
+#define POP_TOP_NO_DECREF 192
+#define UNBOX_FLOAT 193
+#define BOX_FLOAT 194
+#define LOAD_FAST_NO_INCREF 195
+#define STORE_FAST_BOXED_UNBOXED 196
+#define STORE_FAST_UNBOXED_BOXED 197
+#define STORE_FAST_UNBOXED_UNBOXED 198
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index a01a0955182de5..3f78300c226345 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -435,6 +435,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.12a6 3519 (Modify SEND instruction)
# Python 3.12a6 3520 (Remove PREP_RERAISE_STAR, add CALL_INTRINSIC_2)
# Python 3.12a7 3521 (Shrink the LOAD_GLOBAL caches)
+# Python 3.12a7 3522 (Removed JUMP_IF_FALSE_OR_POP/JUMP_IF_TRUE_OR_POP)
# Python 3.13 will start with 3550
@@ -451,7 +452,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3521).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3522).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 01e574509d38d5..cacbdaf575039f 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -154,8 +154,6 @@ def pseudo_op(name, op, real_ops):
name_op('IMPORT_NAME', 108) # Index in name list
name_op('IMPORT_FROM', 109) # Index in name list
jrel_op('JUMP_FORWARD', 110) # Number of words to skip
-jrel_op('JUMP_IF_FALSE_OR_POP', 111) # Number of words to skip
-jrel_op('JUMP_IF_TRUE_OR_POP', 112) # ""
jrel_op('POP_JUMP_IF_FALSE', 114)
jrel_op('POP_JUMP_IF_TRUE', 115)
name_op('LOAD_GLOBAL', 116) # Index in name list
diff --git a/Lib/test/test__opcode.py b/Lib/test/test__opcode.py
index fb4ab15f7041ed..31f3c53992db13 100644
--- a/Lib/test/test__opcode.py
+++ b/Lib/test/test__opcode.py
@@ -34,10 +34,6 @@ def test_stack_effect(self):
self.assertRaises(ValueError, stack_effect, code, 0)
def test_stack_effect_jump(self):
- JUMP_IF_TRUE_OR_POP = dis.opmap['JUMP_IF_TRUE_OR_POP']
- self.assertEqual(stack_effect(JUMP_IF_TRUE_OR_POP, 0), 0)
- self.assertEqual(stack_effect(JUMP_IF_TRUE_OR_POP, 0, jump=True), 0)
- self.assertEqual(stack_effect(JUMP_IF_TRUE_OR_POP, 0, jump=False), -1)
FOR_ITER = dis.opmap['FOR_ITER']
self.assertEqual(stack_effect(FOR_ITER, 0), 1)
self.assertEqual(stack_effect(FOR_ITER, 0, jump=True), 1)
diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py
index 9ff017da53c2b1..01eb04b53060e9 100644
--- a/Lib/test/test_peepholer.py
+++ b/Lib/test/test_peepholer.py
@@ -52,10 +52,6 @@ def check_jump_targets(self, code):
tgt.opname == 'RETURN_VALUE'):
self.fail(f'{instr.opname} at {instr.offset} '
f'jumps to {tgt.opname} at {tgt.offset}')
- # JUMP_IF_*_OR_POP jump to conditional jump
- if '_OR_POP' in instr.opname and 'JUMP_IF_' in tgt.opname:
- self.fail(f'{instr.opname} at {instr.offset} '
- f'jumps to {tgt.opname} at {tgt.offset}')
def check_lnotab(self, code):
"Check that the lnotab byte offsets are sensible."
@@ -384,38 +380,36 @@ def f():
def test_elim_jump_to_uncond_jump3(self):
# Intentionally use two-line expressions to test issue37213.
- # JUMP_IF_FALSE_OR_POP to JUMP_IF_FALSE_OR_POP --> JUMP_IF_FALSE_OR_POP to non-jump
+ # POP_JUMP_IF_FALSE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_FALSE to non-jump
def f(a, b, c):
return ((a and b)
and c)
self.check_jump_targets(f)
self.check_lnotab(f)
- self.assertEqual(count_instr_recursively(f, 'JUMP_IF_FALSE_OR_POP'), 2)
- # JUMP_IF_TRUE_OR_POP to JUMP_IF_TRUE_OR_POP --> JUMP_IF_TRUE_OR_POP to non-jump
+ self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 2)
+ # POP_JUMP_IF_TRUE to POP_JUMP_IF_TRUE --> POP_JUMP_IF_TRUE to non-jump
def f(a, b, c):
return ((a or b)
or c)
self.check_jump_targets(f)
self.check_lnotab(f)
- self.assertEqual(count_instr_recursively(f, 'JUMP_IF_TRUE_OR_POP'), 2)
+ self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 2)
# JUMP_IF_FALSE_OR_POP to JUMP_IF_TRUE_OR_POP --> POP_JUMP_IF_FALSE to non-jump
def f(a, b, c):
return ((a and b)
or c)
self.check_jump_targets(f)
self.check_lnotab(f)
- self.assertNotInBytecode(f, 'JUMP_IF_FALSE_OR_POP')
- self.assertInBytecode(f, 'JUMP_IF_TRUE_OR_POP')
- self.assertInBytecode(f, 'POP_JUMP_IF_FALSE')
- # JUMP_IF_TRUE_OR_POP to JUMP_IF_FALSE_OR_POP --> POP_JUMP_IF_TRUE to non-jump
+ self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1)
+ self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1)
+ # POP_JUMP_IF_TRUE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE to non-jump
def f(a, b, c):
return ((a or b)
and c)
self.check_jump_targets(f)
self.check_lnotab(f)
- self.assertNotInBytecode(f, 'JUMP_IF_TRUE_OR_POP')
- self.assertInBytecode(f, 'JUMP_IF_FALSE_OR_POP')
- self.assertInBytecode(f, 'POP_JUMP_IF_TRUE')
+ self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1)
+ self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1)
def test_elim_jump_to_uncond_jump4(self):
def f():
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-21-00-46-36.gh-issue-102859.PRkGca.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-21-00-46-36.gh-issue-102859.PRkGca.rst
new file mode 100644
index 00000000000000..d2e2232c33cc4d
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-21-00-46-36.gh-issue-102859.PRkGca.rst
@@ -0,0 +1,2 @@
+Removed :opcode:`JUMP_IF_FALSE_OR_POP` and :opcode:`JUMP_IF_TRUE_OR_POP`
+instructions.
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 133c991bf701c4..19bd4b10780b91 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -306,8 +306,6 @@ mark_stacks(PyCodeObject *code_obj, int len)
}
opcode = code[i].op.code;
switch (opcode) {
- case JUMP_IF_FALSE_OR_POP:
- case JUMP_IF_TRUE_OR_POP:
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
{
@@ -318,16 +316,8 @@ mark_stacks(PyCodeObject *code_obj, int len)
if (stacks[j] == UNINITIALIZED && j < i) {
todo = 1;
}
- if (opcode == JUMP_IF_FALSE_OR_POP ||
- opcode == JUMP_IF_TRUE_OR_POP)
- {
- target_stack = next_stack;
- next_stack = pop_value(next_stack);
- }
- else {
- next_stack = pop_value(next_stack);
- target_stack = next_stack;
- }
+ next_stack = pop_value(next_stack);
+ target_stack = next_stack;
assert(stacks[j] == UNINITIALIZED || stacks[j] == target_stack);
stacks[j] = target_stack;
stacks[i+1] = next_stack;
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 5038e1bb17fc3f..b9a6a9e2b61208 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2050,129 +2050,6 @@ dummy_func(
}
}
- inst(BB_TEST_POP_IF_NONE, (value -- )) {
- if (Py_IsNone(value)) {
- _Py_DECREF_NO_DEALLOC(value);
- bb_test = false;
- }
- else {
- Py_DECREF(value);
- bb_test = true;
- }
- }
-
- inst(JUMP_IF_FALSE_OR_POP, (cond -- cond if (jump))) {
- bool jump = false;
- int err;
- if (Py_IsTrue(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- }
- else if (Py_IsFalse(cond)) {
- JUMPBY(oparg);
- jump = true;
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- Py_DECREF(cond);
- }
- else if (err == 0) {
- JUMPBY(oparg);
- jump = true;
- }
- else {
- goto error;
- }
- }
- }
-
- inst(BB_TEST_IF_FALSE_OR_POP, (cond -- cond if (jump))) {
- bool jump = false;
- int err;
- if (Py_IsTrue(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- bb_test = true;
- }
- else if (Py_IsFalse(cond)) {
- bb_test = false;
- jump = true;
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- bb_test = true;
- Py_DECREF(cond);
- }
- else if (err == 0) {
- bb_test = false;
- jump = true;
- }
- else {
- goto error;
- }
- }
- // This gets set so BRANCH_BB knows whether to pop
- // the type stack (type propagation) when generating the
- // target BB
- gen_bb_requires_pop = !jump;
- }
-
- inst(JUMP_IF_TRUE_OR_POP, (cond -- cond if (jump))) {
- bool jump = false;
- int err;
- if (Py_IsFalse(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- }
- else if (Py_IsTrue(cond)) {
- JUMPBY(oparg);
- jump = true;
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- JUMPBY(oparg);
- jump = true;
- }
- else if (err == 0) {
- Py_DECREF(cond);
- }
- else {
- goto error;
- }
- }
- }
-
- inst(BB_TEST_IF_TRUE_OR_POP, (cond -- cond if (jump))) {
- bool jump = false;
- int err;
- if (Py_IsFalse(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- bb_test = true;
- }
- else if (Py_IsTrue(cond)) {
- bb_test = false;
- jump = true;
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- bb_test = false;
- jump = true;
- }
- else if (err == 0) {
- bb_test = true;
- Py_DECREF(cond);
- }
- else {
- goto error;
- }
- }
- // This gets set so BRANCH_BB knows whether to pop
- // the type stack (type propagation) when generating the
- // target BB
- gen_bb_requires_pop = !jump;
- }
-
inst(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
diff --git a/Python/compile.c b/Python/compile.c
index 99296050445f50..33cd6ca07d3bb6 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -4241,19 +4241,18 @@ compiler_boolop(struct compiler *c, expr_ty e)
location loc = LOC(e);
assert(e->kind == BoolOp_kind);
if (e->v.BoolOp.op == And)
- jumpi = JUMP_IF_FALSE_OR_POP;
+ jumpi = POP_JUMP_IF_FALSE;
else
- jumpi = JUMP_IF_TRUE_OR_POP;
+ jumpi = POP_JUMP_IF_TRUE;
NEW_JUMP_TARGET_LABEL(c, end);
s = e->v.BoolOp.values;
n = asdl_seq_LEN(s) - 1;
assert(n >= 0);
for (i = 0; i < n; ++i) {
VISIT(c, expr, (expr_ty)asdl_seq_GET(s, i));
+ ADDOP_I(c, loc, COPY, 1);
ADDOP_JUMP(c, loc, jumpi, end);
- NEW_JUMP_TARGET_LABEL(c, next);
-
- USE_LABEL(c, next);
+ ADDOP(c, loc, POP_TOP);
}
VISIT(c, expr, (expr_ty)asdl_seq_GET(s, n));
@@ -4558,7 +4557,9 @@ compiler_compare(struct compiler *c, expr_ty e)
ADDOP_I(c, loc, SWAP, 2);
ADDOP_I(c, loc, COPY, 2);
ADDOP_COMPARE(c, loc, asdl_seq_GET(e->v.Compare.ops, i));
- ADDOP_JUMP(c, loc, JUMP_IF_FALSE_OR_POP, cleanup);
+ ADDOP_I(c, loc, COPY, 1);
+ ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, cleanup);
+ ADDOP(c, loc, POP_TOP);
}
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
ADDOP_COMPARE(c, loc, asdl_seq_GET(e->v.Compare.ops, n));
@@ -7836,21 +7837,6 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
case POP_JUMP_IF_TRUE:
reversed_opcode = POP_JUMP_IF_FALSE;
break;
- case JUMP_IF_TRUE_OR_POP:
- case JUMP_IF_FALSE_OR_POP:
- if (!is_forward) {
- /* As far as we can tell, the compiler never emits
- * these jumps with a backwards target. If/when this
- * exception is raised, we have found a use case for
- * a backwards version of this jump (or to replace
- * it with the sequence (COPY 1, POP_JUMP_IF_T/F, POP)
- */
- PyErr_Format(PyExc_SystemError,
- "unexpected %s jumping backwards",
- last->i_opcode == JUMP_IF_TRUE_OR_POP ?
- "JUMP_IF_TRUE_OR_POP" : "JUMP_IF_FALSE_OR_POP");
- }
- return SUCCESS;
}
if (is_forward) {
return SUCCESS;
@@ -9143,21 +9129,30 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
assert(PyList_CheckExact(consts));
struct cfg_instr nop;
INSTR_SET_OP0(&nop, NOP);
- struct cfg_instr *target;
+ struct cfg_instr *target = &nop;
+ int opcode = 0;
+ int oparg = 0;
+ int nextop = 0;
for (int i = 0; i < bb->b_iused; i++) {
struct cfg_instr *inst = &bb->b_instr[i];
- int oparg = inst->i_oparg;
- int nextop = i+1 < bb->b_iused ? bb->b_instr[i+1].i_opcode : 0;
- if (HAS_TARGET(inst->i_opcode)) {
- assert(inst->i_target->b_iused > 0);
- target = &inst->i_target->b_instr[0];
- assert(!IS_ASSEMBLER_OPCODE(target->i_opcode));
- }
- else {
- target = &nop;
+ bool is_copy_of_load_const = (opcode == LOAD_CONST &&
+ inst->i_opcode == COPY &&
+ inst->i_oparg == 1);
+ if (! is_copy_of_load_const) {
+ opcode = inst->i_opcode;
+ oparg = inst->i_oparg;
+ if (HAS_TARGET(opcode)) {
+ assert(inst->i_target->b_iused > 0);
+ target = &inst->i_target->b_instr[0];
+ assert(!IS_ASSEMBLER_OPCODE(target->i_opcode));
+ }
+ else {
+ target = &nop;
+ }
}
- assert(!IS_ASSEMBLER_OPCODE(inst->i_opcode));
- switch (inst->i_opcode) {
+ nextop = i+1 < bb->b_iused ? bb->b_instr[i+1].i_opcode : 0;
+ assert(!IS_ASSEMBLER_OPCODE(opcode));
+ switch (opcode) {
/* Remove LOAD_CONST const; conditional jump */
case LOAD_CONST:
{
@@ -9167,7 +9162,7 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
switch(nextop) {
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
- cnt = get_const_value(inst->i_opcode, oparg, consts);
+ cnt = get_const_value(opcode, oparg, consts);
if (cnt == NULL) {
goto error;
}
@@ -9185,28 +9180,8 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
INSTR_SET_OP0(&bb->b_instr[i + 1], NOP);
}
break;
- case JUMP_IF_FALSE_OR_POP:
- case JUMP_IF_TRUE_OR_POP:
- cnt = get_const_value(inst->i_opcode, oparg, consts);
- if (cnt == NULL) {
- goto error;
- }
- is_true = PyObject_IsTrue(cnt);
- Py_DECREF(cnt);
- if (is_true == -1) {
- goto error;
- }
- jump_if_true = nextop == JUMP_IF_TRUE_OR_POP;
- if (is_true == jump_if_true) {
- bb->b_instr[i+1].i_opcode = JUMP;
- }
- else {
- INSTR_SET_OP0(inst, NOP);
- INSTR_SET_OP0(&bb->b_instr[i + 1], NOP);
- }
- break;
case IS_OP:
- cnt = get_const_value(inst->i_opcode, oparg, consts);
+ cnt = get_const_value(opcode, oparg, consts);
if (cnt == NULL) {
goto error;
}
@@ -9252,65 +9227,6 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
}
}
break;
-
- /* Simplify conditional jump to conditional jump where the
- result of the first test implies the success of a similar
- test or the failure of the opposite test.
- Arises in code like:
- "a and b or c"
- "(a and b) and c"
- "(a or b) or c"
- "(a or b) and c"
- x:JUMP_IF_FALSE_OR_POP y y:JUMP_IF_FALSE_OR_POP z
- --> x:JUMP_IF_FALSE_OR_POP z
- x:JUMP_IF_FALSE_OR_POP y y:JUMP_IF_TRUE_OR_POP z
- --> x:POP_JUMP_IF_FALSE y+1
- where y+1 is the instruction following the second test.
- */
- case JUMP_IF_FALSE_OR_POP:
- switch (target->i_opcode) {
- case POP_JUMP_IF_FALSE:
- i -= jump_thread(inst, target, POP_JUMP_IF_FALSE);
- break;
- case JUMP:
- case JUMP_IF_FALSE_OR_POP:
- i -= jump_thread(inst, target, JUMP_IF_FALSE_OR_POP);
- break;
- case JUMP_IF_TRUE_OR_POP:
- case POP_JUMP_IF_TRUE:
- if (inst->i_loc.lineno == target->i_loc.lineno) {
- // We don't need to bother checking for loops here,
- // since a block's b_next cannot point to itself:
- assert(inst->i_target != inst->i_target->b_next);
- inst->i_opcode = POP_JUMP_IF_FALSE;
- inst->i_target = inst->i_target->b_next;
- --i;
- }
- break;
- }
- break;
- case JUMP_IF_TRUE_OR_POP:
- switch (target->i_opcode) {
- case POP_JUMP_IF_TRUE:
- i -= jump_thread(inst, target, POP_JUMP_IF_TRUE);
- break;
- case JUMP:
- case JUMP_IF_TRUE_OR_POP:
- i -= jump_thread(inst, target, JUMP_IF_TRUE_OR_POP);
- break;
- case JUMP_IF_FALSE_OR_POP:
- case POP_JUMP_IF_FALSE:
- if (inst->i_loc.lineno == target->i_loc.lineno) {
- // We don't need to bother checking for loops here,
- // since a block's b_next cannot point to itself:
- assert(inst->i_target != inst->i_target->b_next);
- inst->i_opcode = POP_JUMP_IF_TRUE;
- inst->i_target = inst->i_target->b_next;
- --i;
- }
- break;
- }
- break;
case POP_JUMP_IF_NOT_NONE:
case POP_JUMP_IF_NONE:
switch (target->i_opcode) {
@@ -9398,6 +9314,52 @@ inline_small_exit_blocks(basicblock *bb) {
return 0;
}
+
+static int
+remove_redundant_nops_and_pairs(basicblock *entryblock)
+{
+ bool done = false;
+
+ while (! done) {
+ done = true;
+ struct cfg_instr *prev_instr = NULL;
+ struct cfg_instr *instr = NULL;
+ for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
+ remove_redundant_nops(b);
+ if (IS_LABEL(b->b_label)) {
+ /* this block is a jump target, forget instr */
+ instr = NULL;
+ }
+ for (int i = 0; i < b->b_iused; i++) {
+ prev_instr = instr;
+ instr = &b->b_instr[i];
+ int prev_opcode = prev_instr ? prev_instr->i_opcode : 0;
+ int prev_oparg = prev_instr ? prev_instr->i_oparg : 0;
+ int opcode = instr->i_opcode;
+ bool is_redundant_pair = false;
+ if (opcode == POP_TOP) {
+ if (prev_opcode == LOAD_CONST) {
+ is_redundant_pair = true;
+ }
+ else if (prev_opcode == COPY && prev_oparg == 1) {
+ is_redundant_pair = true;
+ }
+ }
+ if (is_redundant_pair) {
+ INSTR_SET_OP0(prev_instr, NOP);
+ INSTR_SET_OP0(instr, NOP);
+ done = false;
+ }
+ }
+ if ((instr && is_jump(instr)) || !BB_HAS_FALLTHROUGH(b)) {
+ instr = NULL;
+ }
+ }
+ }
+ return SUCCESS;
+}
+
+
static int
remove_redundant_nops(basicblock *bb) {
/* Remove NOPs when legal to do so. */
@@ -9636,9 +9598,9 @@ optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache)
assert(no_empty_basic_blocks(g));
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
RETURN_IF_ERROR(optimize_basic_block(const_cache, b, consts));
- remove_redundant_nops(b);
assert(b->b_predecessors == 0);
}
+ RETURN_IF_ERROR(remove_redundant_nops_and_pairs(g->g_entryblock));
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
RETURN_IF_ERROR(inline_small_exit_blocks(b));
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index d83901ce3e2158..e74ef78c3b0aeb 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2645,148 +2645,6 @@
DISPATCH();
}
- TARGET(BB_TEST_POP_IF_NONE) {
- PyObject *value = stack_pointer[-1];
- if (Py_IsNone(value)) {
- _Py_DECREF_NO_DEALLOC(value);
- bb_test = false;
- }
- else {
- Py_DECREF(value);
- bb_test = true;
- }
- STACK_SHRINK(1);
- DISPATCH();
- }
-
- TARGET(JUMP_IF_FALSE_OR_POP) {
- PyObject *cond = stack_pointer[-1];
- bool jump = false;
- int err;
- if (Py_IsTrue(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- }
- else if (Py_IsFalse(cond)) {
- JUMPBY(oparg);
- jump = true;
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- Py_DECREF(cond);
- }
- else if (err == 0) {
- JUMPBY(oparg);
- jump = true;
- }
- else {
- goto error;
- }
- }
- STACK_SHRINK(1);
- STACK_GROW((jump ? 1 : 0));
- DISPATCH();
- }
-
- TARGET(BB_TEST_IF_FALSE_OR_POP) {
- PyObject *cond = stack_pointer[-1];
- bool jump = false;
- int err;
- if (Py_IsTrue(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- bb_test = true;
- }
- else if (Py_IsFalse(cond)) {
- bb_test = false;
- jump = true;
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- bb_test = true;
- Py_DECREF(cond);
- }
- else if (err == 0) {
- bb_test = false;
- jump = true;
- }
- else {
- goto error;
- }
- }
- // This gets set so BRANCH_BB knows whether to pop
- // the type stack (type propagation) when generating the
- // target BB
- gen_bb_requires_pop = !jump;
- STACK_SHRINK(1);
- STACK_GROW((jump ? 1 : 0));
- DISPATCH();
- }
-
- TARGET(JUMP_IF_TRUE_OR_POP) {
- PyObject *cond = stack_pointer[-1];
- bool jump = false;
- int err;
- if (Py_IsFalse(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- }
- else if (Py_IsTrue(cond)) {
- JUMPBY(oparg);
- jump = true;
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- JUMPBY(oparg);
- jump = true;
- }
- else if (err == 0) {
- Py_DECREF(cond);
- }
- else {
- goto error;
- }
- }
- STACK_SHRINK(1);
- STACK_GROW((jump ? 1 : 0));
- DISPATCH();
- }
-
- TARGET(BB_TEST_IF_TRUE_OR_POP) {
- PyObject *cond = stack_pointer[-1];
- bool jump = false;
- int err;
- if (Py_IsFalse(cond)) {
- _Py_DECREF_NO_DEALLOC(cond);
- bb_test = true;
- }
- else if (Py_IsTrue(cond)) {
- bb_test = false;
- jump = true;
- }
- else {
- err = PyObject_IsTrue(cond);
- if (err > 0) {
- bb_test = false;
- jump = true;
- }
- else if (err == 0) {
- bb_test = true;
- Py_DECREF(cond);
- }
- else {
- goto error;
- }
- }
- // This gets set so BRANCH_BB knows whether to pop
- // the type stack (type propagation) when generating the
- // target BB
- gen_bb_requires_pop = !jump;
- STACK_SHRINK(1);
- STACK_GROW((jump ? 1 : 0));
- DISPATCH();
- }
-
TARGET(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
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 5a00fe70a7c0bf..25463ced32d5d4 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -281,16 +281,6 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 1;
case POP_JUMP_IF_NONE:
return 1;
- case BB_TEST_POP_IF_NONE:
- return 1;
- case JUMP_IF_FALSE_OR_POP:
- return 1;
- case BB_TEST_IF_FALSE_OR_POP:
- return 1;
- case JUMP_IF_TRUE_OR_POP:
- return 1;
- case BB_TEST_IF_TRUE_OR_POP:
- return 1;
case JUMP_BACKWARD_NO_INTERRUPT:
return 0;
case GET_LEN:
@@ -687,16 +677,6 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case POP_JUMP_IF_NONE:
return 0;
- case BB_TEST_POP_IF_NONE:
- return 0;
- case JUMP_IF_FALSE_OR_POP:
- return (jump ? 1 : 0);
- case BB_TEST_IF_FALSE_OR_POP:
- return (jump ? 1 : 0);
- case JUMP_IF_TRUE_OR_POP:
- return (jump ? 1 : 0);
- case BB_TEST_IF_TRUE_OR_POP:
- return (jump ? 1 : 0);
case JUMP_BACKWARD_NO_INTERRUPT:
return 0;
case GET_LEN:
@@ -961,11 +941,6 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IB },
[BB_TEST_POP_IF_NOT_NONE] = { true, INSTR_FMT_IX },
[POP_JUMP_IF_NONE] = { true, INSTR_FMT_IB },
- [BB_TEST_POP_IF_NONE] = { true, INSTR_FMT_IX },
- [JUMP_IF_FALSE_OR_POP] = { true, INSTR_FMT_IB },
- [BB_TEST_IF_FALSE_OR_POP] = { true, INSTR_FMT_IX },
- [JUMP_IF_TRUE_OR_POP] = { true, INSTR_FMT_IB },
- [BB_TEST_IF_TRUE_OR_POP] = { true, INSTR_FMT_IX },
[JUMP_BACKWARD_NO_INTERRUPT] = { true, INSTR_FMT_IB },
[GET_LEN] = { true, INSTR_FMT_IX },
[MATCH_CLASS] = { true, INSTR_FMT_IB },
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 4a815c73acb7db..38217d9e8f2ba9 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -110,9 +110,9 @@ static void *opcode_targets[256] = {
&&TARGET_IMPORT_NAME,
&&TARGET_IMPORT_FROM,
&&TARGET_JUMP_FORWARD,
- &&TARGET_JUMP_IF_FALSE_OR_POP,
- &&TARGET_JUMP_IF_TRUE_OR_POP,
&&TARGET_LOAD_FAST__LOAD_FAST,
+ &&TARGET_LOAD_GLOBAL_BUILTIN,
+ &&TARGET_LOAD_GLOBAL_MODULE,
&&TARGET_POP_JUMP_IF_FALSE,
&&TARGET_POP_JUMP_IF_TRUE,
&&TARGET_LOAD_GLOBAL,
@@ -142,7 +142,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_BACKWARD,
&&TARGET_COMPARE_AND_BRANCH,
&&TARGET_CALL_FUNCTION_EX,
- &&TARGET_LOAD_GLOBAL_BUILTIN,
+ &&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
&&TARGET_SET_ADD,
@@ -152,29 +152,29 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE,
&&TARGET_RESUME,
&&TARGET_MATCH_CLASS,
- &&TARGET_LOAD_GLOBAL_MODULE,
- &&TARGET_STORE_ATTR_INSTANCE_VALUE,
+ &&TARGET_STORE_ATTR_SLOT,
+ &&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING,
- &&TARGET_STORE_ATTR_SLOT,
- &&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_STORE_FAST__STORE_FAST,
+ &&TARGET_STORE_SUBSCR_DICT,
+ &&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
- &&TARGET_STORE_SUBSCR_DICT,
- &&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_UNPACK_SEQUENCE_LIST,
&&TARGET_UNPACK_SEQUENCE_TUPLE,
&&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
+ &&TARGET_SEND_GEN,
+ &&_unknown_opcode,
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_CALL_INTRINSIC_1,
&&TARGET_CALL_INTRINSIC_2,
- &&TARGET_SEND_GEN,
+ &&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index f3620b6c6a9b4b..c50ae42092bfd5 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -747,35 +747,6 @@
break;
}
- TARGET(BB_TEST_POP_IF_NONE) {
- STACK_SHRINK(1);
- break;
- }
-
- TARGET(JUMP_IF_FALSE_OR_POP) {
- fprintf(stderr, "Type propagation across `JUMP_IF_FALSE_OR_POP` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(BB_TEST_IF_FALSE_OR_POP) {
- fprintf(stderr, "Type propagation across `BB_TEST_IF_FALSE_OR_POP` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(JUMP_IF_TRUE_OR_POP) {
- fprintf(stderr, "Type propagation across `JUMP_IF_TRUE_OR_POP` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(BB_TEST_IF_TRUE_OR_POP) {
- fprintf(stderr, "Type propagation across `BB_TEST_IF_TRUE_OR_POP` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
TARGET(JUMP_BACKWARD_NO_INTERRUPT) {
break;
}
From 404a6c5b46c4f84de4d523f78a8cb4274cb039b1 Mon Sep 17 00:00:00 2001
From: Kevin Kirsche
Date: Wed, 22 Mar 2023 14:14:05 -0400
Subject: [PATCH 192/280] gh-102921: [doc] Clarify `exc` argument name in
`BaseExceptionGroup` is plural (#102922)
---
Doc/library/exceptions.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst
index 4a57e9c8799336..18c3f47dddc079 100644
--- a/Doc/library/exceptions.rst
+++ b/Doc/library/exceptions.rst
@@ -948,8 +948,8 @@ their subgroups based on the types of the contained exceptions.
these fields do not need to be updated by :meth:`derive`. ::
>>> class MyGroup(ExceptionGroup):
- ... def derive(self, exc):
- ... return MyGroup(self.message, exc)
+ ... def derive(self, excs):
+ ... return MyGroup(self.message, excs)
...
>>> e = MyGroup("eg", [ValueError(1), TypeError(2)])
>>> e.add_note("a note")
From f8f08c2701c735541832a9db99f64a310d62e615 Mon Sep 17 00:00:00 2001
From: Jens-Hilmar Bradt <17177271+jenshb@users.noreply.github.com>
Date: Wed, 22 Mar 2023 19:43:41 +0100
Subject: [PATCH 193/280] [doc] Fix error in tutorial example: type(exc) is the
type rather than the instance (#102751)
---
Doc/tutorial/errors.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst
index e09c829b8e9721..ca5dc3314c63b6 100644
--- a/Doc/tutorial/errors.rst
+++ b/Doc/tutorial/errors.rst
@@ -160,7 +160,7 @@ accessing ``.args``. ::
>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
- ... print(type(inst)) # the exception instance
+ ... print(type(inst)) # the exception type
... print(inst.args) # arguments stored in .args
... print(inst) # __str__ allows args to be printed directly,
... # but may be overridden in exception subclasses
From 828e8878382024e1c08ebeae2ab4a5d63f806bca Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Wed, 22 Mar 2023 18:30:04 -0600
Subject: [PATCH 194/280] gh-100227: Make the Global Interned Dict Safe for
Isolated Interpreters (gh-102925)
This is effectively two changes. The first (the bulk of the change) is where we add _Py_AddToGlobalDict() (and _PyRuntime.cached_objects.main_tstate, etc.). The second (much smaller) change is where we update PyUnicode_InternInPlace() to use _Py_AddToGlobalDict() instead of calling PyDict_SetDefault() directly.
Basically, _Py_AddToGlobalDict() is a wrapper around PyDict_SetDefault() that should be used whenever we need to add a value to a runtime-global dict object (in the few cases where we are leaving the container global rather than moving it to PyInterpreterState, e.g. the interned strings dict). _Py_AddToGlobalDict() does all the necessary work to make sure the target global dict is shared safely between isolated interpreters. This is especially important as we move the obmalloc state to each interpreter (gh-101660), as well as, potentially, the GIL (PEP 684).
https://github.com/python/cpython/issues/100227
---
Include/internal/pycore_global_objects.h | 4 +
Include/internal/pycore_pystate.h | 5 +
Include/internal/pycore_runtime_init.h | 3 +
Include/internal/pycore_unicodeobject.h | 1 +
Objects/unicodeobject.c | 13 +-
Python/pylifecycle.c | 4 +
Python/pystate.c | 204 ++++++++++++++++++++---
7 files changed, 204 insertions(+), 30 deletions(-)
diff --git a/Include/internal/pycore_global_objects.h b/Include/internal/pycore_global_objects.h
index 9957da1fc5f22a..858321d67df481 100644
--- a/Include/internal/pycore_global_objects.h
+++ b/Include/internal/pycore_global_objects.h
@@ -28,6 +28,10 @@ extern "C" {
struct _Py_cached_objects {
PyObject *interned_strings;
+ /* A thread state tied to the main interpreter,
+ used exclusively for when a global object (e.g. interned strings)
+ is resized (i.e. deallocated + allocated) from an arbitrary thread. */
+ PyThreadState main_tstate;
};
#define _Py_GLOBAL_OBJECT(NAME) \
diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h
index 7046ec8d9adaaf..f159b516e66b18 100644
--- a/Include/internal/pycore_pystate.h
+++ b/Include/internal/pycore_pystate.h
@@ -127,6 +127,11 @@ PyAPI_FUNC(void) _PyThreadState_Init(
PyThreadState *tstate);
PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate);
+extern void _PyThreadState_InitDetached(PyThreadState *, PyInterpreterState *);
+extern void _PyThreadState_ClearDetached(PyThreadState *);
+
+extern PyObject * _Py_AddToGlobalDict(PyObject *, PyObject *, PyObject *);
+
static inline void
_PyThreadState_UpdateTracingState(PyThreadState *tstate)
diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h
index 7cfa7c0c02494a..fd358b2da6ccff 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -59,6 +59,9 @@ extern PyTypeObject _PyExc_MemoryError;
.types = { \
.next_version_tag = 1, \
}, \
+ .cached_objects = { \
+ .main_tstate = _PyThreadState_INIT, \
+ }, \
.static_objects = { \
.singletons = { \
.small_ints = _Py_small_ints_INIT, \
diff --git a/Include/internal/pycore_unicodeobject.h b/Include/internal/pycore_unicodeobject.h
index 19faceebf1d8ee..ed4feb603d6f38 100644
--- a/Include/internal/pycore_unicodeobject.h
+++ b/Include/internal/pycore_unicodeobject.h
@@ -34,6 +34,7 @@ struct _Py_unicode_runtime_ids {
struct _Py_unicode_runtime_state {
struct _Py_unicode_runtime_ids ids;
+ /* The interned dict is at _PyRuntime.cached_objects.interned_strings. */
};
/* fs_codec.encoding is initialized to NULL.
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index b9fb53147b9b51..891a65576ee29b 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -14609,16 +14609,11 @@ PyUnicode_InternInPlace(PyObject **p)
}
PyObject *interned = get_interned_dict();
- assert(interned != NULL);
-
- PyObject *t = PyDict_SetDefault(interned, s, s);
- if (t == NULL) {
- PyErr_Clear();
- return;
- }
-
+ PyObject *t = _Py_AddToGlobalDict(interned, s, s);
if (t != s) {
- Py_SETREF(*p, Py_NewRef(t));
+ if (t != NULL) {
+ Py_SETREF(*p, Py_NewRef(t));
+ }
return;
}
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 8110d94ba17526..5d7f8621833040 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -636,6 +636,8 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
return status;
}
+ _PyThreadState_InitDetached(&runtime->cached_objects.main_tstate, interp);
+
*tstate_p = tstate;
return _PyStatus_OK();
}
@@ -1932,6 +1934,8 @@ Py_FinalizeEx(void)
// XXX Do this sooner during finalization.
// XXX Ensure finalizer errors are handled properly.
+ _PyThreadState_ClearDetached(&runtime->cached_objects.main_tstate);
+
finalize_interp_clear(tstate);
finalize_interp_delete(tstate->interp);
diff --git a/Python/pystate.c b/Python/pystate.c
index b17efdbefd124c..394b12d24065f2 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -565,6 +565,124 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
#endif
+//---------------
+// global objects
+//---------------
+
+/* The global objects thread state is meant to be used in a very limited
+ way and should not be used to actually run any Python code. */
+
+static PyThreadState *
+bind_global_objects_state(_PyRuntimeState *runtime)
+{
+ PyThreadState *main_tstate = &runtime->cached_objects.main_tstate;
+
+ bind_tstate(main_tstate);
+ /* Unlike _PyThreadState_Bind(), we do not modify gilstate TSS. */
+
+ return main_tstate;
+}
+
+static void
+unbind_global_objects_state(_PyRuntimeState *runtime)
+{
+ PyThreadState *main_tstate = &runtime->cached_objects.main_tstate;
+ assert(tstate_is_alive(main_tstate));
+ assert(!main_tstate->_status.active);
+ assert(gilstate_tss_get(runtime) != main_tstate);
+
+ unbind_tstate(main_tstate);
+
+ /* This thread state may be bound/unbound repeatedly,
+ so we must erase evidence that it was ever bound (or unbound). */
+ main_tstate->_status.bound = 0;
+ main_tstate->_status.unbound = 0;
+
+ /* We must fully unlink the thread state from any OS thread,
+ to allow it to be bound more than once. */
+ main_tstate->thread_id = 0;
+#ifdef PY_HAVE_THREAD_NATIVE_ID
+ main_tstate->native_thread_id = 0;
+#endif
+}
+
+static inline void
+acquire_global_objects_lock(_PyRuntimeState *runtime)
+{
+ /* For now we can rely on the GIL, so we don't actually
+ acquire a global lock here. */
+ assert(current_fast_get(runtime) != NULL);
+}
+
+static inline void
+release_global_objects_lock(_PyRuntimeState *runtime)
+{
+ /* For now we can rely on the GIL, so we don't actually
+ release a global lock here. */
+ assert(current_fast_get(runtime) != NULL);
+}
+
+PyObject *
+_Py_AddToGlobalDict(PyObject *dict, PyObject *key, PyObject *value)
+{
+ assert(dict != NULL);
+ assert(PyDict_CheckExact(dict));
+
+ /* All global objects are stored in _PyRuntime
+ and owned by the main interpreter. */
+ _PyRuntimeState *runtime = &_PyRuntime;
+ PyThreadState *curts = current_fast_get(runtime);
+ PyInterpreterState *interp = curts->interp;
+ assert(interp != NULL); // The GIL must be held.
+
+ /* Due to interpreter isolation we must hold a global lock,
+ starting at this point and ending before we return.
+ Note that the operations in this function are very fucused
+ and we should not expect any reentrancy. */
+ acquire_global_objects_lock(runtime);
+
+ /* Swap to the main interpreter, if necessary. */
+ PyThreadState *oldts = NULL;
+ if (!_Py_IsMainInterpreter(interp)) {
+ PyThreadState *main_tstate = bind_global_objects_state(runtime);
+
+ oldts = _PyThreadState_Swap(runtime, main_tstate);
+ assert(oldts != NULL);
+ assert(!_Py_IsMainInterpreter(oldts->interp));
+
+ /* The limitations of the global objects thread state apply
+ from this point to the point we swap back to oldts. */
+ }
+
+ /* This might trigger a resize, which is why we must "acquire"
+ the global object state. Also note that PyDict_SetDefault()
+ must be compatible with our reentrancy and global objects state
+ constraints. */
+ PyObject *actual = PyDict_SetDefault(dict, key, value);
+ if (actual == NULL) {
+ /* Raising an exception from one interpreter in another
+ is problematic, so we clear it and let the caller deal
+ with the returned NULL. */
+ assert(PyErr_ExceptionMatches(PyExc_MemoryError));
+ PyErr_Clear();
+ }
+
+ /* Swap back, it it wasn't in the main interpreter already. */
+ if (oldts != NULL) {
+ // The returned tstate should be _PyRuntime.cached_objects.main_tstate.
+ _PyThreadState_Swap(runtime, oldts);
+
+ unbind_global_objects_state(runtime);
+ }
+
+ release_global_objects_lock(runtime);
+
+ // XXX Immortalize the key and value.
+
+ return actual;
+}
+
+
/*************************************/
/* the per-interpreter runtime state */
/*************************************/
@@ -1217,8 +1335,7 @@ free_threadstate(PyThreadState *tstate)
static void
init_threadstate(PyThreadState *tstate,
- PyInterpreterState *interp, uint64_t id,
- PyThreadState *next)
+ PyInterpreterState *interp, uint64_t id)
{
if (tstate->_status.initialized) {
Py_FatalError("thread state already initialized");
@@ -1227,18 +1344,13 @@ init_threadstate(PyThreadState *tstate,
assert(interp != NULL);
tstate->interp = interp;
+ // next/prev are set in add_threadstate().
+ assert(tstate->next == NULL);
+ assert(tstate->prev == NULL);
+
assert(id > 0);
tstate->id = id;
- assert(interp->threads.head == tstate);
- assert((next != NULL && id != 1) || (next == NULL && id == 1));
- if (next != NULL) {
- assert(next->prev == NULL || next->prev == tstate);
- next->prev = tstate;
- }
- tstate->next = next;
- assert(tstate->prev == NULL);
-
// thread_id and native_thread_id are set in bind_tstate().
tstate->py_recursion_limit = interp->ceval.recursion_limit,
@@ -1259,6 +1371,22 @@ init_threadstate(PyThreadState *tstate,
tstate->_status.initialized = 1;
}
+static void
+add_threadstate(PyInterpreterState *interp, PyThreadState *tstate,
+ PyThreadState *next)
+{
+ assert(interp->threads.head != tstate);
+ assert((next != NULL && tstate->id != 1) ||
+ (next == NULL && tstate->id == 1));
+ if (next != NULL) {
+ assert(next->prev == NULL || next->prev == tstate);
+ next->prev = tstate;
+ }
+ tstate->next = next;
+ assert(tstate->prev == NULL);
+ interp->threads.head = tstate;
+}
+
static PyThreadState *
new_threadstate(PyInterpreterState *interp)
{
@@ -1298,9 +1426,9 @@ new_threadstate(PyInterpreterState *interp)
&initial._main_interpreter._initial_thread,
sizeof(*tstate));
}
- interp->threads.head = tstate;
- init_threadstate(tstate, interp, id, old_head);
+ init_threadstate(tstate, interp, id);
+ add_threadstate(interp, tstate, old_head);
HEAD_UNLOCK(runtime);
if (!used_newtstate) {
@@ -1347,6 +1475,33 @@ _PyThreadState_Init(PyThreadState *tstate)
Py_FatalError("_PyThreadState_Init() is for internal use only");
}
+void
+_PyThreadState_InitDetached(PyThreadState *tstate, PyInterpreterState *interp)
+{
+ _PyRuntimeState *runtime = interp->runtime;
+
+ HEAD_LOCK(runtime);
+ interp->threads.next_unique_id += 1;
+ uint64_t id = interp->threads.next_unique_id;
+ HEAD_UNLOCK(runtime);
+
+ init_threadstate(tstate, interp, id);
+ // We do not call add_threadstate().
+}
+
+
+static void
+clear_datastack(PyThreadState *tstate)
+{
+ _PyStackChunk *chunk = tstate->datastack_chunk;
+ tstate->datastack_chunk = NULL;
+ while (chunk != NULL) {
+ _PyStackChunk *prev = chunk->previous;
+ _PyObject_VirtualFree(chunk, chunk->size);
+ chunk = prev;
+ }
+}
+
void
PyThreadState_Clear(PyThreadState *tstate)
{
@@ -1421,7 +1576,6 @@ PyThreadState_Clear(PyThreadState *tstate)
// XXX Do it as early in the function as possible.
}
-
/* Common code for PyThreadState_Delete() and PyThreadState_DeleteCurrent() */
static void
tstate_delete_common(PyThreadState *tstate)
@@ -1454,17 +1608,25 @@ tstate_delete_common(PyThreadState *tstate)
unbind_tstate(tstate);
// XXX Move to PyThreadState_Clear()?
- _PyStackChunk *chunk = tstate->datastack_chunk;
- tstate->datastack_chunk = NULL;
- while (chunk != NULL) {
- _PyStackChunk *prev = chunk->previous;
- _PyObject_VirtualFree(chunk, chunk->size);
- chunk = prev;
- }
+ clear_datastack(tstate);
tstate->_status.finalized = 1;
}
+void
+_PyThreadState_ClearDetached(PyThreadState *tstate)
+{
+ assert(!tstate->_status.bound);
+ assert(!tstate->_status.bound_gilstate);
+ assert(tstate->datastack_chunk == NULL);
+ assert(tstate->thread_id == 0);
+ assert(tstate->native_thread_id == 0);
+ assert(tstate->next == NULL);
+ assert(tstate->prev == NULL);
+
+ PyThreadState_Clear(tstate);
+ clear_datastack(tstate);
+}
static void
zapthreads(PyInterpreterState *interp)
From c95d83cd648abaeff427a5f405780fdf195bc93a Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Thu, 23 Mar 2023 13:37:04 +0300
Subject: [PATCH 195/280] gh-102939: Fix "conversion from Py_ssize_t to long"
warning in builtins (GH-102940)
---
Python/bltinmodule.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 55fd364d007972..fcb4d7a9a975c6 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -2503,7 +2503,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
Py_DECREF(iter);
if (PyErr_Occurred())
return NULL;
- return PyLong_FromLong(i_result);
+ return PyLong_FromSsize_t(i_result);
}
if (PyLong_CheckExact(item) || PyBool_Check(item)) {
Py_ssize_t b;
@@ -2525,7 +2525,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
}
}
/* Either overflowed or is not an int. Restore real objects and process normally */
- result = PyLong_FromLong(i_result);
+ result = PyLong_FromSsize_t(i_result);
if (result == NULL) {
Py_DECREF(item);
Py_DECREF(iter);
From 24016a897f9c609315f1314e108d92495cb36e1b Mon Sep 17 00:00:00 2001
From: "Erlend E. Aasland"
Date: Thu, 23 Mar 2023 14:21:32 +0100
Subject: [PATCH 196/280] Docs: fixup incorrect escape char in sqlite3 docs
(#102945)
---
Doc/library/sqlite3.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 4b2d13ab3a8fcd..51146e00999659 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -259,7 +259,7 @@ Module functions
.. function:: connect(database, timeout=5.0, detect_types=0, \
isolation_level="DEFERRED", check_same_thread=True, \
factory=sqlite3.Connection, cached_statements=128, \
- uri=False, \*, \
+ uri=False, *, \
autocommit=sqlite3.LEGACY_TRANSACTION_CONTROL)
Open a connection to an SQLite database.
From 4ef8efaf9c52165d9d50500bc57791c83064213b Mon Sep 17 00:00:00 2001
From: Alex Waygood
Date: Thu, 23 Mar 2023 14:17:54 +0000
Subject: [PATCH 197/280] gh-102947: Improve traceback when calling `fields()`
on a non-dataclass (#102948)
---
Lib/dataclasses.py | 2 +-
Lib/test/test_dataclasses.py | 12 ++++++++++++
.../2023-03-23-13-34-33.gh-issue-102947.cTwcpU.rst | 2 ++
3 files changed, 15 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-23-13-34-33.gh-issue-102947.cTwcpU.rst
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 82b08fc017884f..e3fd0b3e380dd8 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -1248,7 +1248,7 @@ def fields(class_or_instance):
try:
fields = getattr(class_or_instance, _FIELDS)
except AttributeError:
- raise TypeError('must be called with a dataclass type or instance')
+ raise TypeError('must be called with a dataclass type or instance') from None
# Exclude pseudo-fields. Note that fields is sorted by insertion
# order, so the order of the tuple is as the fields were defined.
diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py
index 46f33043c27071..affd9cede19c99 100644
--- a/Lib/test/test_dataclasses.py
+++ b/Lib/test/test_dataclasses.py
@@ -5,11 +5,13 @@
from dataclasses import *
import abc
+import io
import pickle
import inspect
import builtins
import types
import weakref
+import traceback
import unittest
from unittest.mock import Mock
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol, DefaultDict
@@ -1526,6 +1528,16 @@ class C: pass
with self.assertRaisesRegex(TypeError, 'dataclass type or instance'):
fields(C())
+ def test_clean_traceback_from_fields_exception(self):
+ stdout = io.StringIO()
+ try:
+ fields(object)
+ except TypeError as exc:
+ traceback.print_exception(exc, file=stdout)
+ printed_traceback = stdout.getvalue()
+ self.assertNotIn("AttributeError", printed_traceback)
+ self.assertNotIn("__dataclass_fields__", printed_traceback)
+
def test_helper_asdict(self):
# Basic tests for asdict(), it should return a new dictionary.
@dataclass
diff --git a/Misc/NEWS.d/next/Library/2023-03-23-13-34-33.gh-issue-102947.cTwcpU.rst b/Misc/NEWS.d/next/Library/2023-03-23-13-34-33.gh-issue-102947.cTwcpU.rst
new file mode 100644
index 00000000000000..b59c9820356697
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-23-13-34-33.gh-issue-102947.cTwcpU.rst
@@ -0,0 +1,2 @@
+Improve traceback when :func:`dataclasses.fields` is called on a
+non-dataclass. Patch by Alex Waygood
From 67a48e51011cbd26c622761de578d281c462234f Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Thu, 23 Mar 2023 19:26:11 +0300
Subject: [PATCH 198/280] gh-88965: typing: fix type substitution of a list of
types after initial `ParamSpec` substitution (#102808)
Previously, this used to fail:
```py
from typing import *
T = TypeVar("T")
P = ParamSpec("P")
class X(Generic[P]):
f: Callable[P, int]
Y = X[[int, T]]
Z = Y[str]
```
Co-authored-by: Alex Waygood
---
Lib/test/test_typing.py | 121 ++++++++++++++++++
Lib/typing.py | 33 ++++-
...3-03-18-14-59-21.gh-issue-88965.kA70Km.rst | 7 +
3 files changed, 154 insertions(+), 7 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-18-14-59-21.gh-issue-88965.kA70Km.rst
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index c9f55de95c548f..f448b0ee60a92a 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -7679,6 +7679,127 @@ def test_bad_var_substitution(self):
with self.assertRaises(TypeError):
collections.abc.Callable[P, T][arg, str]
+ def test_type_var_subst_for_other_type_vars(self):
+ T = TypeVar('T')
+ T2 = TypeVar('T2')
+ P = ParamSpec('P')
+ P2 = ParamSpec('P2')
+ Ts = TypeVarTuple('Ts')
+
+ class Base(Generic[P]):
+ pass
+
+ A1 = Base[T]
+ self.assertEqual(A1.__parameters__, (T,))
+ self.assertEqual(A1.__args__, ((T,),))
+ self.assertEqual(A1[int], Base[int])
+
+ A2 = Base[[T]]
+ self.assertEqual(A2.__parameters__, (T,))
+ self.assertEqual(A2.__args__, ((T,),))
+ self.assertEqual(A2[int], Base[int])
+
+ A3 = Base[[int, T]]
+ self.assertEqual(A3.__parameters__, (T,))
+ self.assertEqual(A3.__args__, ((int, T),))
+ self.assertEqual(A3[str], Base[[int, str]])
+
+ A4 = Base[[T, int, T2]]
+ self.assertEqual(A4.__parameters__, (T, T2))
+ self.assertEqual(A4.__args__, ((T, int, T2),))
+ self.assertEqual(A4[str, bool], Base[[str, int, bool]])
+
+ A5 = Base[[*Ts, int]]
+ self.assertEqual(A5.__parameters__, (Ts,))
+ self.assertEqual(A5.__args__, ((*Ts, int),))
+ self.assertEqual(A5[str, bool], Base[[str, bool, int]])
+
+ A5_2 = Base[[int, *Ts]]
+ self.assertEqual(A5_2.__parameters__, (Ts,))
+ self.assertEqual(A5_2.__args__, ((int, *Ts),))
+ self.assertEqual(A5_2[str, bool], Base[[int, str, bool]])
+
+ A6 = Base[[T, *Ts]]
+ self.assertEqual(A6.__parameters__, (T, Ts))
+ self.assertEqual(A6.__args__, ((T, *Ts),))
+ self.assertEqual(A6[int, str, bool], Base[[int, str, bool]])
+
+ A7 = Base[[T, T]]
+ self.assertEqual(A7.__parameters__, (T,))
+ self.assertEqual(A7.__args__, ((T, T),))
+ self.assertEqual(A7[int], Base[[int, int]])
+
+ A8 = Base[[T, list[T]]]
+ self.assertEqual(A8.__parameters__, (T,))
+ self.assertEqual(A8.__args__, ((T, list[T]),))
+ self.assertEqual(A8[int], Base[[int, list[int]]])
+
+ A9 = Base[[Tuple[*Ts], *Ts]]
+ self.assertEqual(A9.__parameters__, (Ts,))
+ self.assertEqual(A9.__args__, ((Tuple[*Ts], *Ts),))
+ self.assertEqual(A9[int, str], Base[Tuple[int, str], int, str])
+
+ A10 = Base[P2]
+ self.assertEqual(A10.__parameters__, (P2,))
+ self.assertEqual(A10.__args__, (P2,))
+ self.assertEqual(A10[[int, str]], Base[[int, str]])
+
+ class DoubleP(Generic[P, P2]):
+ pass
+
+ B1 = DoubleP[P, P2]
+ self.assertEqual(B1.__parameters__, (P, P2))
+ self.assertEqual(B1.__args__, (P, P2))
+ self.assertEqual(B1[[int, str], [bool]], DoubleP[[int, str], [bool]])
+ self.assertEqual(B1[[], []], DoubleP[[], []])
+
+ B2 = DoubleP[[int, str], P2]
+ self.assertEqual(B2.__parameters__, (P2,))
+ self.assertEqual(B2.__args__, ((int, str), P2))
+ self.assertEqual(B2[[bool, bool]], DoubleP[[int, str], [bool, bool]])
+ self.assertEqual(B2[[]], DoubleP[[int, str], []])
+
+ B3 = DoubleP[P, [bool, bool]]
+ self.assertEqual(B3.__parameters__, (P,))
+ self.assertEqual(B3.__args__, (P, (bool, bool)))
+ self.assertEqual(B3[[int, str]], DoubleP[[int, str], [bool, bool]])
+ self.assertEqual(B3[[]], DoubleP[[], [bool, bool]])
+
+ B4 = DoubleP[[T, int], [bool, T2]]
+ self.assertEqual(B4.__parameters__, (T, T2))
+ self.assertEqual(B4.__args__, ((T, int), (bool, T2)))
+ self.assertEqual(B4[str, float], DoubleP[[str, int], [bool, float]])
+
+ B5 = DoubleP[[*Ts, int], [bool, T2]]
+ self.assertEqual(B5.__parameters__, (Ts, T2))
+ self.assertEqual(B5.__args__, ((*Ts, int), (bool, T2)))
+ self.assertEqual(B5[str, bytes, float],
+ DoubleP[[str, bytes, int], [bool, float]])
+
+ B6 = DoubleP[[T, int], [bool, *Ts]]
+ self.assertEqual(B6.__parameters__, (T, Ts))
+ self.assertEqual(B6.__args__, ((T, int), (bool, *Ts)))
+ self.assertEqual(B6[str, bytes, float],
+ DoubleP[[str, int], [bool, bytes, float]])
+
+ class PandT(Generic[P, T]):
+ pass
+
+ C1 = PandT[P, T]
+ self.assertEqual(C1.__parameters__, (P, T))
+ self.assertEqual(C1.__args__, (P, T))
+ self.assertEqual(C1[[int, str], bool], PandT[[int, str], bool])
+
+ C2 = PandT[[int, T], T]
+ self.assertEqual(C2.__parameters__, (T,))
+ self.assertEqual(C2.__args__, ((int, T), T))
+ self.assertEqual(C2[str], PandT[[int, str], str])
+
+ C3 = PandT[[int, *Ts], T]
+ self.assertEqual(C3.__parameters__, (Ts, T))
+ self.assertEqual(C3.__args__, ((int, *Ts), T))
+ self.assertEqual(C3[str, bool, bytes], PandT[[int, str, bool], bytes])
+
def test_paramspec_in_nested_generics(self):
# Although ParamSpec should not be found in __parameters__ of most
# generics, they probably should be found when nested in
diff --git a/Lib/typing.py b/Lib/typing.py
index 3ee9679e50c0c4..157a563bbecea8 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -255,10 +255,17 @@ def _collect_parameters(args):
"""
parameters = []
for t in args:
- # We don't want __parameters__ descriptor of a bare Python class.
if isinstance(t, type):
- continue
- if hasattr(t, '__typing_subst__'):
+ # We don't want __parameters__ descriptor of a bare Python class.
+ pass
+ elif isinstance(t, tuple):
+ # `t` might be a tuple, when `ParamSpec` is substituted with
+ # `[T, int]`, or `[int, *Ts]`, etc.
+ for x in t:
+ for collected in _collect_parameters([x]):
+ if collected not in parameters:
+ parameters.append(collected)
+ elif hasattr(t, '__typing_subst__'):
if t not in parameters:
parameters.append(t)
else:
@@ -1441,10 +1448,12 @@ def _determine_new_args(self, args):
raise TypeError(f"Too {'many' if alen > plen else 'few'} arguments for {self};"
f" actual {alen}, expected {plen}")
new_arg_by_param = dict(zip(params, args))
+ return tuple(self._make_substitution(self.__args__, new_arg_by_param))
+ def _make_substitution(self, args, new_arg_by_param):
+ """Create a list of new type arguments."""
new_args = []
- for old_arg in self.__args__:
-
+ for old_arg in args:
if isinstance(old_arg, type):
new_args.append(old_arg)
continue
@@ -1488,10 +1497,20 @@ def _determine_new_args(self, args):
# should join all these types together in a flat list
# `(float, int, str)` - so again, we should `extend`.
new_args.extend(new_arg)
+ elif isinstance(old_arg, tuple):
+ # Corner case:
+ # P = ParamSpec('P')
+ # T = TypeVar('T')
+ # class Base(Generic[P]): ...
+ # Can be substituted like this:
+ # X = Base[[int, T]]
+ # In this case, `old_arg` will be a tuple:
+ new_args.append(
+ tuple(self._make_substitution(old_arg, new_arg_by_param)),
+ )
else:
new_args.append(new_arg)
-
- return tuple(new_args)
+ return new_args
def copy_with(self, args):
return self.__class__(self.__origin__, args, name=self._name, inst=self._inst,
diff --git a/Misc/NEWS.d/next/Library/2023-03-18-14-59-21.gh-issue-88965.kA70Km.rst b/Misc/NEWS.d/next/Library/2023-03-18-14-59-21.gh-issue-88965.kA70Km.rst
new file mode 100644
index 00000000000000..6e9642100cd8cb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-18-14-59-21.gh-issue-88965.kA70Km.rst
@@ -0,0 +1,7 @@
+typing: Fix a bug relating to substitution in custom classes generic over a
+:class:`~typing.ParamSpec`. Previously, if the ``ParamSpec`` was substituted
+with a parameters list that itself contained a :class:`~typing.TypeVar`, the
+``TypeVar`` in the parameters list could not be subsequently substituted. This
+is now fixed.
+
+Patch by Nikita Sobolev.
From 49e03ce245d90a39c9497227264c88ceb51f0dff Mon Sep 17 00:00:00 2001
From: AN Long
Date: Fri, 24 Mar 2023 00:34:48 +0800
Subject: [PATCH 199/280] gh-102943: Stop checking localized error text in
socket tests on Windows (GH-102944)
---
Lib/test/test_socket.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 60f106f2d1a1c2..32252f7b741fda 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -2562,8 +2562,7 @@ def testHyperVConstants(self):
socket.HV_GUID_LOOPBACK
def testCreateHyperVSocketWithUnknownProtoFailure(self):
- expected = "A protocol was specified in the socket function call " \
- "that does not support the semantics of the socket type requested"
+ expected = r"\[WinError 10041\]"
with self.assertRaisesRegex(OSError, expected):
socket.socket(socket.AF_HYPERV, socket.SOCK_STREAM)
From 47e12e5876412e1b745f8ed4b16826955a049d74 Mon Sep 17 00:00:00 2001
From: JosephSBoyle <48555120+JosephSBoyle@users.noreply.github.com>
Date: Thu, 23 Mar 2023 16:43:13 +0000
Subject: [PATCH 200/280] gh-102810 Improve the sphinx docs for
`asyncio.Timeout` (#102934)
Co-authored-by: Alex Waygood
---
Doc/library/asyncio-task.rst | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index d908e45b3c8d8b..c5a480ba20190a 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -624,32 +624,26 @@ Timeouts
The context manager produced by :func:`asyncio.timeout` can be
rescheduled to a different deadline and inspected.
- .. class:: Timeout()
+ .. class:: Timeout(when)
An :ref:`asynchronous context manager `
- that limits time spent inside of it.
+ for cancelling overdue coroutines.
- .. versionadded:: 3.11
+ ``when`` should be an absolute time at which the context should time out,
+ as measured by the event loop's clock:
+
+ - If ``when`` is ``None``, the timeout will never trigger.
+ - If ``when < loop.time()``, the timeout will trigger on the next
+ iteration of the event loop.
.. method:: when() -> float | None
Return the current deadline, or ``None`` if the current
deadline is not set.
- The deadline is a float, consistent with the time returned by
- :meth:`loop.time`.
-
.. method:: reschedule(when: float | None)
- Change the time the timeout will trigger.
-
- If *when* is ``None``, any current deadline will be removed, and the
- context manager will wait indefinitely.
-
- If *when* is a float, it is set as the new deadline.
-
- if *when* is in the past, the timeout will trigger on the next
- iteration of the event loop.
+ Reschedule the timeout.
.. method:: expired() -> bool
From 1999027c56875faf35ab10b9dea303513b67420e Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Thu, 23 Mar 2023 12:10:12 -0500
Subject: [PATCH 201/280] Move binomialvariate() to a section for discrete
distributions (GH-102955)
---
Doc/library/random.rst | 6 +++---
Lib/random.py | 45 +++++++++++++++++++++++-------------------
2 files changed, 28 insertions(+), 23 deletions(-)
diff --git a/Doc/library/random.rst b/Doc/library/random.rst
index 098684d7270ffa..c192919ac62e54 100644
--- a/Doc/library/random.rst
+++ b/Doc/library/random.rst
@@ -404,8 +404,8 @@ Alternative Generator
Class that implements the default pseudo-random number generator used by the
:mod:`random` module.
- .. deprecated:: 3.9
- In the future, the *seed* must be one of the following types:
+ .. deprecated-removed:: 3.9 3.11
+ Formerly the *seed* could be any hashable object. Now it is limited to:
:class:`NoneType`, :class:`int`, :class:`float`, :class:`str`,
:class:`bytes`, or :class:`bytearray`.
@@ -423,7 +423,7 @@ Notes on Reproducibility
------------------------
Sometimes it is useful to be able to reproduce the sequences given by a
-pseudo-random number generator. By re-using a seed value, the same sequence should be
+pseudo-random number generator. By reusing a seed value, the same sequence should be
reproducible from run to run as long as multiple threads are not running.
Most of the random module's algorithms and seeding functions are subject to
diff --git a/Lib/random.py b/Lib/random.py
index 3c4291f6a652a0..586c3f7f9da938 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -24,7 +24,6 @@
negative exponential
gamma
beta
- binomial
pareto
Weibull
@@ -33,6 +32,11 @@
circular uniform
von Mises
+ discrete distributions
+ ----------------------
+ binomial
+
+
General notes on the underlying Mersenne Twister core generator:
* The period is 2**19937-1.
@@ -731,6 +735,26 @@ def betavariate(self, alpha, beta):
return y / (y + self.gammavariate(beta, 1.0))
return 0.0
+ def paretovariate(self, alpha):
+ """Pareto distribution. alpha is the shape parameter."""
+ # Jain, pg. 495
+
+ u = 1.0 - self.random()
+ return u ** (-1.0 / alpha)
+
+ def weibullvariate(self, alpha, beta):
+ """Weibull distribution.
+
+ alpha is the scale parameter and beta is the shape parameter.
+
+ """
+ # Jain, pg. 499; bug fix courtesy Bill Arms
+
+ u = 1.0 - self.random()
+ return alpha * (-_log(u)) ** (1.0 / beta)
+
+
+ ## -------------------- discrete distributions ---------------------
def binomialvariate(self, n=1, p=0.5):
"""Binomial random variable.
@@ -816,25 +840,6 @@ def binomialvariate(self, n=1, p=0.5):
return k
- def paretovariate(self, alpha):
- """Pareto distribution. alpha is the shape parameter."""
- # Jain, pg. 495
-
- u = 1.0 - self.random()
- return u ** (-1.0 / alpha)
-
- def weibullvariate(self, alpha, beta):
- """Weibull distribution.
-
- alpha is the scale parameter and beta is the shape parameter.
-
- """
- # Jain, pg. 499; bug fix courtesy Bill Arms
-
- u = 1.0 - self.random()
- return alpha * (-_log(u)) ** (1.0 / beta)
-
-
## ------------------------------------------------------------------
## --------------- Operating System Random Source ------------------
From 10b47a870acbaf8e553ff6a76c78543194491f99 Mon Sep 17 00:00:00 2001
From: Alex Waygood
Date: Thu, 23 Mar 2023 18:18:53 +0000
Subject: [PATCH 202/280] gh-102936: typing: document performance pitfalls of
protocols decorated with `@runtime_checkable` (#102937)
---
Doc/library/typing.rst | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index 80a969e6335abe..08ffa0310f0f23 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -1584,16 +1584,32 @@ These are not used in annotations. They are building blocks for creating generic
assert isinstance(open('/some/file'), Closable)
+ @runtime_checkable
+ class Named(Protocol):
+ name: str
+
+ import threading
+ assert isinstance(threading.Thread(name='Bob'), Named)
+
.. note::
- :func:`runtime_checkable` will check only the presence of the required
- methods, not their type signatures. For example, :class:`ssl.SSLObject`
+ :func:`!runtime_checkable` will check only the presence of the required
+ methods or attributes, not their type signatures or types.
+ For example, :class:`ssl.SSLObject`
is a class, therefore it passes an :func:`issubclass`
check against :data:`Callable`. However, the
``ssl.SSLObject.__init__`` method exists only to raise a
:exc:`TypeError` with a more informative message, therefore making
it impossible to call (instantiate) :class:`ssl.SSLObject`.
+ .. note::
+
+ An :func:`isinstance` check against a runtime-checkable protocol can be
+ surprisingly slow compared to an ``isinstance()`` check against
+ a non-protocol class. Consider using alternative idioms such as
+ :func:`hasattr` calls for structural checks in performance-sensitive
+ code.
+
.. versionadded:: 3.8
Other special directives
From c25402d6ee349ffdcc2e70f8459121c7a59a23ee Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Thu, 23 Mar 2023 22:35:02 +0300
Subject: [PATCH 203/280] gh-98239: Document that `inspect.getsource()` can
raise `TypeError` (#101689)
---
Doc/library/inspect.rst | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index ccf240193d36a9..88f843c03b1d5a 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -574,6 +574,8 @@ Retrieving source code
object and the line number indicates where in the original source file the first
line of code was found. An :exc:`OSError` is raised if the source code cannot
be retrieved.
+ A :exc:`TypeError` is raised if the object is a built-in module, class, or
+ function.
.. versionchanged:: 3.3
:exc:`OSError` is raised instead of :exc:`IOError`, now an alias of the
@@ -586,6 +588,8 @@ Retrieving source code
class, method, function, traceback, frame, or code object. The source code is
returned as a single string. An :exc:`OSError` is raised if the source code
cannot be retrieved.
+ A :exc:`TypeError` is raised if the object is a built-in module, class, or
+ function.
.. versionchanged:: 3.3
:exc:`OSError` is raised instead of :exc:`IOError`, now an alias of the
From 110c3dd66a1dd6e12228e4deeb659e41bbec7d5f Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Thu, 23 Mar 2023 14:46:15 -0500
Subject: [PATCH 204/280] Minor readability improvement to the factor() recipe
(GH-102971)
---
Doc/library/itertools.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 5daadfd3759f4b..70e5b7905f20a9 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -936,7 +936,7 @@ which incur interpreter overhead.
n = quotient
if n == 1:
return
- if n >= 2:
+ if n > 1:
yield n
def flatten(list_of_lists):
From 9227b982f2a1e43a7ebe87c66031d0e3cfb5fabd Mon Sep 17 00:00:00 2001
From: Dong-hee Na
Date: Fri, 24 Mar 2023 05:30:18 +0900
Subject: [PATCH 205/280] gh-102558: [Enum] fix AttributeError during member
repr() (GH-102601)
---
Lib/enum.py | 2 ++
Lib/test/test_enum.py | 9 ++++++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/Lib/enum.py b/Lib/enum.py
index d14e91a9b017d1..ba927662a43b13 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -1186,6 +1186,8 @@ def _missing_(cls, value):
return None
def __repr__(self):
+ if not isinstance(self, Enum):
+ return repr(self)
v_repr = self.__class__._value_repr_ or repr
return "<%s.%s: %s>" % (self.__class__.__name__, self._name_, v_repr(self._value_))
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index a11bb441f06e8e..bb163c46481a42 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -11,7 +11,7 @@
import builtins as bltns
from collections import OrderedDict
from datetime import date
-from enum import Enum, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto
+from enum import Enum, EnumMeta, IntEnum, StrEnum, EnumType, Flag, IntFlag, unique, auto
from enum import STRICT, CONFORM, EJECT, KEEP, _simple_enum, _test_simple_enum
from enum import verify, UNIQUE, CONTINUOUS, NAMED_FLAGS, ReprEnum
from enum import member, nonmember, _iter_bits_lsb
@@ -644,6 +644,13 @@ class MySubEnum(MyEnum):
theother = auto()
self.assertEqual(repr(MySubEnum.that), "My name is that.")
+ def test_multiple_superclasses_repr(self):
+ class _EnumSuperClass(metaclass=EnumMeta):
+ pass
+ class E(_EnumSuperClass, Enum):
+ A = 1
+ self.assertEqual(repr(E.A), "")
+
def test_reversed_iteration_order(self):
self.assertEqual(
list(reversed(self.MainEnum)),
From 6183cbdc92d6648fa9997cdc8fc44971b3fc4983 Mon Sep 17 00:00:00 2001
From: Brandt Bucher
Date: Thu, 23 Mar 2023 15:25:09 -0700
Subject: [PATCH 206/280] GH-100982: Break up COMPARE_AND_BRANCH (GH-102801)
---
Doc/library/dis.rst | 9 --
Include/internal/pycore_code.h | 2 +-
Include/internal/pycore_opcode.h | 27 +++--
Include/opcode.h | 107 +++++++++--------
Lib/importlib/_bootstrap_external.py | 4 +-
Lib/opcode.py | 13 +--
Lib/test/test_compile.py | 2 +-
Lib/test/test_dis.py | 16 +--
...-03-17-12-09-45.gh-issue-100982.Pf_BI6.rst | 1 +
Objects/frameobject.c | 8 --
Python/bytecodes.c | 93 ++++++---------
Python/compile.c | 11 +-
Python/generated_cases.c.h | 109 +++++++-----------
Python/opcode_metadata.h | 31 +++--
Python/opcode_targets.h | 16 +--
Python/specialize.c | 92 +++++----------
Python/tier2_typepropagator.c.h | 20 ++--
Tools/c-analyzer/cpython/ignored.tsv | 1 -
Tools/scripts/summarize_stats.py | 2 -
19 files changed, 232 insertions(+), 332 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-100982.Pf_BI6.rst
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index b06fe67a983aa1..8703cddb3448cc 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -1042,15 +1042,6 @@ iterations of the loop.
``cmp_op[opname]``.
-.. opcode:: COMPARE_AND_BRANCH (opname)
-
- Compares the top two values on the stack, popping them, then branches.
- The direction and offset of the jump is embedded as a ``POP_JUMP_IF_TRUE``
- or ``POP_JUMP_IF_FALSE`` instruction immediately following the cache.
-
- .. versionadded:: 3.12
-
-
.. opcode:: IS_OP (invert)
Performs ``is`` comparison, or ``is not`` if ``invert`` is 1.
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index bfda105c61d4dc..3586c292117459 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -244,7 +244,7 @@ extern void _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr,
int nargs, PyObject *kwnames);
extern void _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
int oparg, PyObject **locals);
-extern void _Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs,
+extern void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs,
_Py_CODEUNIT *instr, int oparg);
extern void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr,
int oparg);
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 4a3270dd11b2de..9884184df4d3d8 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -51,7 +51,6 @@ const uint8_t _PyOpcode_Caches[256] = {
[LOAD_GLOBAL] = 4,
[BINARY_OP] = 1,
[SEND] = 1,
- [COMPARE_AND_BRANCH] = 1,
[CALL] = 4,
};
@@ -109,11 +108,10 @@ const uint8_t _PyOpcode_Deopt[256] = {
[CHECK_EG_MATCH] = CHECK_EG_MATCH,
[CHECK_EXC_MATCH] = CHECK_EXC_MATCH,
[CLEANUP_THROW] = CLEANUP_THROW,
- [COMPARE_AND_BRANCH] = COMPARE_AND_BRANCH,
- [COMPARE_AND_BRANCH_FLOAT] = COMPARE_AND_BRANCH,
- [COMPARE_AND_BRANCH_INT] = COMPARE_AND_BRANCH,
- [COMPARE_AND_BRANCH_STR] = COMPARE_AND_BRANCH,
[COMPARE_OP] = COMPARE_OP,
+ [COMPARE_OP_FLOAT] = COMPARE_OP,
+ [COMPARE_OP_INT] = COMPARE_OP,
+ [COMPARE_OP_STR] = COMPARE_OP,
[CONTAINS_OP] = CONTAINS_OP,
[COPY] = COPY,
[COPY_FREE_VARS] = COPY_FREE_VARS,
@@ -292,9 +290,9 @@ static const char *const _PyOpcode_OpName[263] = {
[END_ASYNC_FOR] = "END_ASYNC_FOR",
[CLEANUP_THROW] = "CLEANUP_THROW",
[CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1",
- [COMPARE_AND_BRANCH_FLOAT] = "COMPARE_AND_BRANCH_FLOAT",
- [COMPARE_AND_BRANCH_INT] = "COMPARE_AND_BRANCH_INT",
- [COMPARE_AND_BRANCH_STR] = "COMPARE_AND_BRANCH_STR",
+ [COMPARE_OP_FLOAT] = "COMPARE_OP_FLOAT",
+ [COMPARE_OP_INT] = "COMPARE_OP_INT",
+ [COMPARE_OP_STR] = "COMPARE_OP_STR",
[STORE_SUBSCR] = "STORE_SUBSCR",
[DELETE_SUBSCR] = "DELETE_SUBSCR",
[FOR_ITER_LIST] = "FOR_ITER_LIST",
@@ -376,9 +374,9 @@ static const char *const _PyOpcode_OpName[263] = {
[STORE_DEREF] = "STORE_DEREF",
[DELETE_DEREF] = "DELETE_DEREF",
[JUMP_BACKWARD] = "JUMP_BACKWARD",
- [COMPARE_AND_BRANCH] = "COMPARE_AND_BRANCH",
- [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
[STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
+ [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
+ [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD",
@@ -388,29 +386,28 @@ static const char *const _PyOpcode_OpName[263] = {
[YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS",
- [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
+ [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
[FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING",
- [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
[STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
+ [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
- [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
[SEND_GEN] = "SEND_GEN",
[BB_BRANCH] = "BB_BRANCH",
+ [BB_BRANCH_IF_FLAG_UNSET] = "BB_BRANCH_IF_FLAG_UNSET",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
[CALL_INTRINSIC_1] = "CALL_INTRINSIC_1",
[CALL_INTRINSIC_2] = "CALL_INTRINSIC_2",
- [BB_BRANCH_IF_FLAG_UNSET] = "BB_BRANCH_IF_FLAG_UNSET",
[BB_BRANCH_IF_FLAG_SET] = "BB_BRANCH_IF_FLAG_SET",
[BB_JUMP_IF_FLAG_UNSET] = "BB_JUMP_IF_FLAG_UNSET",
[BB_JUMP_IF_FLAG_SET] = "BB_JUMP_IF_FLAG_SET",
@@ -434,6 +431,7 @@ static const char *const _PyOpcode_OpName[263] = {
[STORE_FAST_BOXED_UNBOXED] = "STORE_FAST_BOXED_UNBOXED",
[STORE_FAST_UNBOXED_BOXED] = "STORE_FAST_UNBOXED_BOXED",
[STORE_FAST_UNBOXED_UNBOXED] = "STORE_FAST_UNBOXED_UNBOXED",
+ [198] = "<198>",
[199] = "<199>",
[200] = "<200>",
[201] = "<201>",
@@ -503,6 +501,7 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
+ case 198: \
case 199: \
case 200: \
case 201: \
diff --git a/Include/opcode.h b/Include/opcode.h
index 6f02501b835718..ae70a273f41573 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -93,7 +93,6 @@ extern "C" {
#define STORE_DEREF 138
#define DELETE_DEREF 139
#define JUMP_BACKWARD 140
-#define COMPARE_AND_BRANCH 141
#define CALL_FUNCTION_EX 142
#define EXTENDED_ARG 144
#define LIST_APPEND 145
@@ -155,9 +154,9 @@ extern "C" {
#define CALL_NO_KW_STR_1 47
#define CALL_NO_KW_TUPLE_1 48
#define CALL_NO_KW_TYPE_1 56
-#define COMPARE_AND_BRANCH_FLOAT 57
-#define COMPARE_AND_BRANCH_INT 58
-#define COMPARE_AND_BRANCH_STR 59
+#define COMPARE_OP_FLOAT 57
+#define COMPARE_OP_INT 58
+#define COMPARE_OP_STR 59
#define FOR_ITER_LIST 62
#define FOR_ITER_TUPLE 63
#define FOR_ITER_RANGE 64
@@ -181,17 +180,17 @@ extern "C" {
#define LOAD_FAST__LOAD_FAST 111
#define LOAD_GLOBAL_BUILTIN 112
#define LOAD_GLOBAL_MODULE 113
-#define STORE_ATTR_INSTANCE_VALUE 143
-#define STORE_ATTR_SLOT 153
-#define STORE_ATTR_WITH_HINT 154
-#define STORE_FAST__LOAD_FAST 158
-#define STORE_FAST__STORE_FAST 159
-#define STORE_SUBSCR_DICT 160
-#define STORE_SUBSCR_LIST_INT 161
-#define UNPACK_SEQUENCE_LIST 166
-#define UNPACK_SEQUENCE_TUPLE 167
-#define UNPACK_SEQUENCE_TWO_TUPLE 168
-#define SEND_GEN 169
+#define STORE_ATTR_INSTANCE_VALUE 141
+#define STORE_ATTR_SLOT 143
+#define STORE_ATTR_WITH_HINT 153
+#define STORE_FAST__LOAD_FAST 154
+#define STORE_FAST__STORE_FAST 158
+#define STORE_SUBSCR_DICT 159
+#define STORE_SUBSCR_LIST_INT 160
+#define UNPACK_SEQUENCE_LIST 161
+#define UNPACK_SEQUENCE_TUPLE 166
+#define UNPACK_SEQUENCE_TWO_TUPLE 167
+#define SEND_GEN 168
#define DO_TRACING 255
// Tier 2 interpreter ops
#define RESUME_QUICK 5
@@ -225,9 +224,9 @@ extern "C" {
#define CALL_NO_KW_STR_1 47
#define CALL_NO_KW_TUPLE_1 48
#define CALL_NO_KW_TYPE_1 56
-#define COMPARE_AND_BRANCH_FLOAT 57
-#define COMPARE_AND_BRANCH_INT 58
-#define COMPARE_AND_BRANCH_STR 59
+#define COMPARE_OP_FLOAT 57
+#define COMPARE_OP_INT 58
+#define COMPARE_OP_STR 59
#define FOR_ITER_LIST 62
#define FOR_ITER_TUPLE 63
#define FOR_ITER_RANGE 64
@@ -251,43 +250,43 @@ extern "C" {
#define LOAD_FAST__LOAD_FAST 111
#define LOAD_GLOBAL_BUILTIN 112
#define LOAD_GLOBAL_MODULE 113
-#define STORE_ATTR_INSTANCE_VALUE 143
-#define STORE_ATTR_SLOT 153
-#define STORE_ATTR_WITH_HINT 154
-#define STORE_FAST__LOAD_FAST 158
-#define STORE_FAST__STORE_FAST 159
-#define STORE_SUBSCR_DICT 160
-#define STORE_SUBSCR_LIST_INT 161
-#define UNPACK_SEQUENCE_LIST 166
-#define UNPACK_SEQUENCE_TUPLE 167
-#define UNPACK_SEQUENCE_TWO_TUPLE 168
-#define SEND_GEN 169
+#define STORE_ATTR_INSTANCE_VALUE 141
+#define STORE_ATTR_SLOT 143
+#define STORE_ATTR_WITH_HINT 153
+#define STORE_FAST__LOAD_FAST 154
+#define STORE_FAST__STORE_FAST 158
+#define STORE_SUBSCR_DICT 159
+#define STORE_SUBSCR_LIST_INT 160
+#define UNPACK_SEQUENCE_LIST 161
+#define UNPACK_SEQUENCE_TUPLE 166
+#define UNPACK_SEQUENCE_TWO_TUPLE 167
+#define SEND_GEN 168
#define DO_TRACING 255
-#define BB_BRANCH 170
-#define BB_BRANCH_IF_FLAG_UNSET 175
-#define BB_BRANCH_IF_FLAG_SET 176
-#define BB_JUMP_IF_FLAG_UNSET 177
-#define BB_JUMP_IF_FLAG_SET 178
-#define BB_TEST_ITER 179
-#define BB_TEST_IF_FALSE_OR_POP 180
-#define BB_TEST_IF_TRUE_OR_POP 181
-#define BB_TEST_POP_IF_FALSE 182
-#define BB_TEST_POP_IF_TRUE 183
-#define BB_TEST_POP_IF_NOT_NONE 184
-#define BB_TEST_POP_IF_NONE 185
-#define BB_JUMP_BACKWARD_LAZY 186
-#define BINARY_CHECK_INT 187
-#define BINARY_CHECK_FLOAT 188
-#define UNARY_CHECK_FLOAT 189
-#define BINARY_OP_ADD_INT_REST 190
-#define BINARY_OP_ADD_FLOAT_UNBOXED 191
-#define POP_TOP_NO_DECREF 192
-#define UNBOX_FLOAT 193
-#define BOX_FLOAT 194
-#define LOAD_FAST_NO_INCREF 195
-#define STORE_FAST_BOXED_UNBOXED 196
-#define STORE_FAST_UNBOXED_BOXED 197
-#define STORE_FAST_UNBOXED_UNBOXED 198
+#define BB_BRANCH 169
+#define BB_BRANCH_IF_FLAG_UNSET 170
+#define BB_BRANCH_IF_FLAG_SET 175
+#define BB_JUMP_IF_FLAG_UNSET 176
+#define BB_JUMP_IF_FLAG_SET 177
+#define BB_TEST_ITER 178
+#define BB_TEST_IF_FALSE_OR_POP 179
+#define BB_TEST_IF_TRUE_OR_POP 180
+#define BB_TEST_POP_IF_FALSE 181
+#define BB_TEST_POP_IF_TRUE 182
+#define BB_TEST_POP_IF_NOT_NONE 183
+#define BB_TEST_POP_IF_NONE 184
+#define BB_JUMP_BACKWARD_LAZY 185
+#define BINARY_CHECK_INT 186
+#define BINARY_CHECK_FLOAT 187
+#define UNARY_CHECK_FLOAT 188
+#define BINARY_OP_ADD_INT_REST 189
+#define BINARY_OP_ADD_FLOAT_UNBOXED 190
+#define POP_TOP_NO_DECREF 191
+#define UNBOX_FLOAT 192
+#define BOX_FLOAT 193
+#define LOAD_FAST_NO_INCREF 194
+#define STORE_FAST_BOXED_UNBOXED 195
+#define STORE_FAST_UNBOXED_BOXED 196
+#define STORE_FAST_UNBOXED_UNBOXED 197
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index 3f78300c226345..28e55fdc8b7d10 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -435,7 +435,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.12a6 3519 (Modify SEND instruction)
# Python 3.12a6 3520 (Remove PREP_RERAISE_STAR, add CALL_INTRINSIC_2)
# Python 3.12a7 3521 (Shrink the LOAD_GLOBAL caches)
-# Python 3.12a7 3522 (Removed JUMP_IF_FALSE_OR_POP/JUMP_IF_TRUE_OR_POP)
+# Python 3.12a7 3523 (Convert COMPARE_AND_BRANCH back to COMPARE_OP)
# Python 3.13 will start with 3550
@@ -452,7 +452,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.
-MAGIC_NUMBER = (3522).to_bytes(2, 'little') + b'\r\n'
+MAGIC_NUMBER = (3523).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
diff --git a/Lib/opcode.py b/Lib/opcode.py
index cacbdaf575039f..fba7f76cc279b7 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -191,8 +191,6 @@ def pseudo_op(name, op, real_ops):
def_op('DELETE_DEREF', 139)
hasfree.append(139)
jrel_op('JUMP_BACKWARD', 140) # Number of words to skip (backwards)
-def_op('COMPARE_AND_BRANCH', 141) # Comparison and jump
-hascompare.append(141)
def_op('CALL_FUNCTION_EX', 142) # Flags
@@ -320,10 +318,10 @@ def pseudo_op(name, op, real_ops):
"CALL_NO_KW_TUPLE_1",
"CALL_NO_KW_TYPE_1",
],
- "COMPARE_AND_BRANCH": [
- "COMPARE_AND_BRANCH_FLOAT",
- "COMPARE_AND_BRANCH_INT",
- "COMPARE_AND_BRANCH_STR",
+ "COMPARE_OP": [
+ "COMPARE_OP_FLOAT",
+ "COMPARE_OP_INT",
+ "COMPARE_OP_STR",
],
"FOR_ITER": [
"FOR_ITER_LIST",
@@ -404,9 +402,6 @@ def pseudo_op(name, op, real_ops):
"COMPARE_OP": {
"counter": 1,
},
- "COMPARE_AND_BRANCH": {
- "counter": 1,
- },
"BINARY_SUBSCR": {
"counter": 1,
"type_version": 2,
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index fe775779c50f50..dca38418935b76 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -1283,7 +1283,7 @@ def test_multiline_boolean_expression(self):
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_FALSE',
line=2, end_line=2, column=15, end_column=16, occurrence=2)
# compare d and 0
- self.assertOpcodeSourcePositionIs(compiled_code, 'COMPARE_AND_BRANCH',
+ self.assertOpcodeSourcePositionIs(compiled_code, 'COMPARE_OP',
line=4, end_line=4, column=8, end_column=13, occurrence=1)
# jump if comparison it True
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE',
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index fa1de1c7ded1b3..ed66b362b08080 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -46,7 +46,7 @@ def cm(cls, x):
%3d LOAD_FAST 1 (x)
LOAD_CONST 1 (1)
- COMPARE_OP 32 (==)
+ COMPARE_OP 40 (==)
LOAD_FAST 0 (self)
STORE_ATTR 0 (x)
RETURN_CONST 0 (None)
@@ -56,7 +56,7 @@ def cm(cls, x):
RESUME 0
LOAD_FAST 1
LOAD_CONST 1
- COMPARE_OP 32 (==)
+ COMPARE_OP 40 (==)
LOAD_FAST 0
STORE_ATTR 0
RETURN_CONST 0
@@ -67,7 +67,7 @@ def cm(cls, x):
%3d LOAD_FAST 1 (x)
LOAD_CONST 1 (1)
- COMPARE_OP 32 (==)
+ COMPARE_OP 40 (==)
LOAD_FAST 0 (cls)
STORE_ATTR 0 (x)
RETURN_CONST 0 (None)
@@ -78,7 +78,7 @@ def cm(cls, x):
%3d LOAD_FAST 0 (x)
LOAD_CONST 1 (1)
- COMPARE_OP 32 (==)
+ COMPARE_OP 40 (==)
STORE_FAST 0 (x)
RETURN_CONST 0 (None)
""" % (_C.sm.__code__.co_firstlineno, _C.sm.__code__.co_firstlineno + 2,)
@@ -1554,12 +1554,12 @@ def _prepare_test_cases():
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=54, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=56, starts_line=5, is_jump_target=False, positions=None),
Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=58, starts_line=None, is_jump_target=False, positions=None),
- Instruction(opname='COMPARE_AND_BRANCH', opcode=141, arg=13, argval='<', argrepr='<', offset=60, starts_line=None, is_jump_target=False, positions=None),
+ Instruction(opname='COMPARE_OP', opcode=107, arg=2, argval='<', argrepr='<', offset=60, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=68, argrepr='to 68', offset=64, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='JUMP_BACKWARD', opcode=140, arg=21, argval=26, argrepr='to 26', offset=66, starts_line=6, is_jump_target=False, positions=None),
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=68, starts_line=7, is_jump_target=True, positions=None),
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=70, starts_line=None, is_jump_target=False, positions=None),
- Instruction(opname='COMPARE_AND_BRANCH', opcode=141, arg=68, argval='>', argrepr='>', offset=72, starts_line=None, is_jump_target=False, positions=None),
+ Instruction(opname='COMPARE_OP', opcode=107, arg=68, argval='>', argrepr='>', offset=72, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=80, argrepr='to 80', offset=76, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='JUMP_BACKWARD', opcode=140, arg=27, argval=26, argrepr='to 26', offset=78, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=80, starts_line=8, is_jump_target=True, positions=None),
@@ -1581,12 +1581,12 @@ def _prepare_test_cases():
Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=146, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=148, starts_line=14, is_jump_target=False, positions=None),
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=150, starts_line=None, is_jump_target=False, positions=None),
- Instruction(opname='COMPARE_AND_BRANCH', opcode=141, arg=75, argval='>', argrepr='>', offset=152, starts_line=None, is_jump_target=False, positions=None),
+ Instruction(opname='COMPARE_OP', opcode=107, arg=68, argval='>', argrepr='>', offset=152, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=160, argrepr='to 160', offset=156, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='JUMP_BACKWARD', opcode=140, arg=25, argval=110, argrepr='to 110', offset=158, starts_line=15, is_jump_target=False, positions=None),
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=160, starts_line=16, is_jump_target=True, positions=None),
Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=162, starts_line=None, is_jump_target=False, positions=None),
- Instruction(opname='COMPARE_AND_BRANCH', opcode=141, arg=13, argval='<', argrepr='<', offset=164, starts_line=None, is_jump_target=False, positions=None),
+ Instruction(opname='COMPARE_OP', opcode=107, arg=2, argval='<', argrepr='<', offset=164, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=172, argrepr='to 172', offset=168, starts_line=None, is_jump_target=False, positions=None),
Instruction(opname='JUMP_FORWARD', opcode=110, arg=15, argval=202, argrepr='to 202', offset=170, starts_line=17, is_jump_target=False, positions=None),
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=172, starts_line=11, is_jump_target=True, positions=None),
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-100982.Pf_BI6.rst b/Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-100982.Pf_BI6.rst
new file mode 100644
index 00000000000000..31a8660836c759
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-100982.Pf_BI6.rst
@@ -0,0 +1 @@
+Replace all occurrences of ``COMPARE_AND_BRANCH`` with :opcode:`COMPARE_OP`.
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 19bd4b10780b91..63590d58809e3e 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -347,14 +347,6 @@ mark_stacks(PyCodeObject *code_obj, int len)
assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack);
stacks[j] = next_stack;
break;
- case COMPARE_AND_BRANCH:
- next_stack = pop_value(pop_value(next_stack));
- i++;
- j = get_arg(code, i) + i + 1;
- assert(j < len);
- assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack);
- stacks[j] = next_stack;
- break;
case GET_ITER:
case GET_AITER:
next_stack = push_value(pop_value(next_stack), Iterator);
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index b9a6a9e2b61208..55ef034c9a9ff8 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1760,75 +1760,54 @@ dummy_func(
Py_DECREF(owner);
}
- inst(COMPARE_OP, (unused/1, left, right -- res)) {
- STAT_INC(COMPARE_OP, deferred);
- assert((oparg >> 4) <= Py_GE);
- res = PyObject_RichCompare(left, right, oparg>>4);
- DECREF_INPUTS();
- ERROR_IF(res == NULL, error);
- }
-
- // No cache size here, since this is a family of super-instructions.
- family(compare_and_branch) = {
- COMPARE_AND_BRANCH,
- COMPARE_AND_BRANCH_FLOAT,
- COMPARE_AND_BRANCH_INT,
- COMPARE_AND_BRANCH_STR,
+ family(compare_op, INLINE_CACHE_ENTRIES_COMPARE_OP) = {
+ COMPARE_OP,
+ COMPARE_OP_FLOAT,
+ COMPARE_OP_INT,
+ COMPARE_OP_STR,
};
- inst(COMPARE_AND_BRANCH, (unused/2, left, right -- )) {
+ inst(COMPARE_OP, (unused/1, left, right -- res)) {
#if ENABLE_SPECIALIZATION
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
next_instr--;
- _Py_Specialize_CompareAndBranch(left, right, next_instr, oparg);
+ _Py_Specialize_CompareOp(left, right, next_instr, oparg);
DISPATCH_SAME_OPARG();
}
- STAT_INC(COMPARE_AND_BRANCH, deferred);
+ STAT_INC(COMPARE_OP, deferred);
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
#endif /* ENABLE_SPECIALIZATION */
assert((oparg >> 4) <= Py_GE);
- PyObject *cond = PyObject_RichCompare(left, right, oparg>>4);
+ res = PyObject_RichCompare(left, right, oparg>>4);
DECREF_INPUTS();
- ERROR_IF(cond == NULL, error);
- assert(next_instr[1].op.code == POP_JUMP_IF_FALSE ||
- next_instr[1].op.code == POP_JUMP_IF_TRUE);
- bool jump_on_true = next_instr[1].op.code == POP_JUMP_IF_TRUE;
- int offset = next_instr[1].op.arg;
- int err = PyObject_IsTrue(cond);
- Py_DECREF(cond);
- ERROR_IF(err < 0, error);
- if (jump_on_true == (err != 0)) {
- JUMPBY(offset);
- }
+ ERROR_IF(res == NULL, error);
}
- inst(COMPARE_AND_BRANCH_FLOAT, (unused/2, left, right -- )) {
+ inst(COMPARE_OP_FLOAT, (unused/1, left, right -- res)) {
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_AND_BRANCH);
- DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_AND_BRANCH);
- STAT_INC(COMPARE_AND_BRANCH, hit);
+ DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP);
+ DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP);
+ STAT_INC(COMPARE_OP, hit);
double dleft = PyFloat_AS_DOUBLE(left);
double dright = PyFloat_AS_DOUBLE(right);
// 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg
int sign_ish = COMPARISON_BIT(dleft, dright);
_Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
_Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
- if (sign_ish & oparg) {
- int offset = next_instr[1].op.arg;
- JUMPBY(offset);
- }
+ res = (sign_ish & oparg) ? Py_True : Py_False;
+ Py_INCREF(res);
}
- // Similar to COMPARE_AND_BRANCH_FLOAT
- inst(COMPARE_AND_BRANCH_INT, (unused/2, left, right -- )) {
+ // Similar to COMPARE_OP_FLOAT
+ inst(COMPARE_OP_INT, (unused/1, left, right -- res)) {
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH);
- DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH);
- DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_AND_BRANCH);
- DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_AND_BRANCH);
- STAT_INC(COMPARE_AND_BRANCH, hit);
+ DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP);
+ DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP);
+ DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_OP);
+ DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_OP);
+ STAT_INC(COMPARE_OP, hit);
assert(_PyLong_DigitCount((PyLongObject *)left) <= 1 &&
_PyLong_DigitCount((PyLongObject *)right) <= 1);
Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left);
@@ -1837,29 +1816,25 @@ dummy_func(
int sign_ish = COMPARISON_BIT(ileft, iright);
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- if (sign_ish & oparg) {
- int offset = next_instr[1].op.arg;
- JUMPBY(offset);
- }
+ res = (sign_ish & oparg) ? Py_True : Py_False;
+ Py_INCREF(res);
}
- // Similar to COMPARE_AND_BRANCH_FLOAT, but for ==, != only
- inst(COMPARE_AND_BRANCH_STR, (unused/2, left, right -- )) {
+ // Similar to COMPARE_OP_FLOAT, but for ==, != only
+ inst(COMPARE_OP_STR, (unused/1, left, right -- res)) {
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_AND_BRANCH);
- DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_AND_BRANCH);
- STAT_INC(COMPARE_AND_BRANCH, hit);
- int res = _PyUnicode_Equal(left, right);
+ DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP);
+ DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP);
+ STAT_INC(COMPARE_OP, hit);
+ int eq = _PyUnicode_Equal(left, right);
assert((oparg >>4) == Py_EQ || (oparg >>4) == Py_NE);
_Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc);
_Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
- assert(res == 0 || res == 1);
+ assert(eq == 0 || eq == 1);
assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS);
assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS);
- if ((res + COMPARISON_NOT_EQUALS) & oparg) {
- int offset = next_instr[1].op.arg;
- JUMPBY(offset);
- }
+ res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? Py_True : Py_False;
+ Py_INCREF(res);
}
inst(IS_OP, (left, right -- b: PyBool_Type)) {
diff --git a/Python/compile.c b/Python/compile.c
index 33cd6ca07d3bb6..192deaa4b35f4d 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -2800,6 +2800,15 @@ check_compare(struct compiler *c, expr_ty e)
return SUCCESS;
}
+static const int compare_masks[] = {
+ [Py_LT] = COMPARISON_LESS_THAN,
+ [Py_LE] = COMPARISON_LESS_THAN | COMPARISON_EQUALS,
+ [Py_EQ] = COMPARISON_EQUALS,
+ [Py_NE] = COMPARISON_NOT_EQUALS,
+ [Py_GT] = COMPARISON_GREATER_THAN,
+ [Py_GE] = COMPARISON_GREATER_THAN | COMPARISON_EQUALS,
+};
+
static int compiler_addcompare(struct compiler *c, location loc,
cmpop_ty op)
{
@@ -2840,7 +2849,7 @@ static int compiler_addcompare(struct compiler *c, location loc,
}
/* cmp goes in top bits of the oparg, while the low bits are used by quickened
* versions of this opcode to store the comparison mask. */
- ADDOP_I(c, loc, COMPARE_OP, cmp << 4);
+ ADDOP_I(c, loc, COMPARE_OP, (cmp << 4) | compare_masks[cmp]);
return SUCCESS;
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index e74ef78c3b0aeb..158116daaaddc4 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2271,87 +2271,65 @@
}
TARGET(COMPARE_OP) {
+ PREDICTED(COMPARE_OP);
+ static_assert(INLINE_CACHE_ENTRIES_COMPARE_OP == 1, "incorrect cache size");
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
PyObject *res;
- STAT_INC(COMPARE_OP, deferred);
- assert((oparg >> 4) <= Py_GE);
- res = PyObject_RichCompare(left, right, oparg>>4);
- Py_DECREF(left);
- Py_DECREF(right);
- if (res == NULL) goto pop_2_error;
- STACK_SHRINK(1);
- stack_pointer[-1] = res;
- next_instr += 1;
- DISPATCH();
- }
-
- TARGET(COMPARE_AND_BRANCH) {
- PREDICTED(COMPARE_AND_BRANCH);
- PyObject *right = stack_pointer[-1];
- PyObject *left = stack_pointer[-2];
#if ENABLE_SPECIALIZATION
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
next_instr--;
- _Py_Specialize_CompareAndBranch(left, right, next_instr, oparg);
+ _Py_Specialize_CompareOp(left, right, next_instr, oparg);
DISPATCH_SAME_OPARG();
}
- STAT_INC(COMPARE_AND_BRANCH, deferred);
+ STAT_INC(COMPARE_OP, deferred);
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
#endif /* ENABLE_SPECIALIZATION */
assert((oparg >> 4) <= Py_GE);
- PyObject *cond = PyObject_RichCompare(left, right, oparg>>4);
+ res = PyObject_RichCompare(left, right, oparg>>4);
Py_DECREF(left);
Py_DECREF(right);
- if (cond == NULL) goto pop_2_error;
- assert(next_instr[1].op.code == POP_JUMP_IF_FALSE ||
- next_instr[1].op.code == POP_JUMP_IF_TRUE);
- bool jump_on_true = next_instr[1].op.code == POP_JUMP_IF_TRUE;
- int offset = next_instr[1].op.arg;
- int err = PyObject_IsTrue(cond);
- Py_DECREF(cond);
- if (err < 0) goto pop_2_error;
- if (jump_on_true == (err != 0)) {
- JUMPBY(offset);
- }
- STACK_SHRINK(2);
- next_instr += 2;
+ if (res == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ stack_pointer[-1] = res;
+ next_instr += 1;
DISPATCH();
}
- TARGET(COMPARE_AND_BRANCH_FLOAT) {
+ TARGET(COMPARE_OP_FLOAT) {
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
+ PyObject *res;
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_AND_BRANCH);
- DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_AND_BRANCH);
- STAT_INC(COMPARE_AND_BRANCH, hit);
+ DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP);
+ DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP);
+ STAT_INC(COMPARE_OP, hit);
double dleft = PyFloat_AS_DOUBLE(left);
double dright = PyFloat_AS_DOUBLE(right);
// 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg
int sign_ish = COMPARISON_BIT(dleft, dright);
_Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
_Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
- if (sign_ish & oparg) {
- int offset = next_instr[1].op.arg;
- JUMPBY(offset);
- }
- STACK_SHRINK(2);
- next_instr += 2;
+ res = (sign_ish & oparg) ? Py_True : Py_False;
+ Py_INCREF(res);
+ STACK_SHRINK(1);
+ stack_pointer[-1] = res;
+ next_instr += 1;
DISPATCH();
}
- TARGET(COMPARE_AND_BRANCH_INT) {
+ TARGET(COMPARE_OP_INT) {
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
+ PyObject *res;
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyLong_CheckExact(left), COMPARE_AND_BRANCH);
- DEOPT_IF(!PyLong_CheckExact(right), COMPARE_AND_BRANCH);
- DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_AND_BRANCH);
- DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_AND_BRANCH);
- STAT_INC(COMPARE_AND_BRANCH, hit);
+ DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP);
+ DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP);
+ DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)left), COMPARE_OP);
+ DEOPT_IF(!_PyLong_IsCompact((PyLongObject *)right), COMPARE_OP);
+ STAT_INC(COMPARE_OP, hit);
assert(_PyLong_DigitCount((PyLongObject *)left) <= 1 &&
_PyLong_DigitCount((PyLongObject *)right) <= 1);
Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left);
@@ -2360,35 +2338,34 @@
int sign_ish = COMPARISON_BIT(ileft, iright);
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
- if (sign_ish & oparg) {
- int offset = next_instr[1].op.arg;
- JUMPBY(offset);
- }
- STACK_SHRINK(2);
- next_instr += 2;
+ res = (sign_ish & oparg) ? Py_True : Py_False;
+ Py_INCREF(res);
+ STACK_SHRINK(1);
+ stack_pointer[-1] = res;
+ next_instr += 1;
DISPATCH();
}
- TARGET(COMPARE_AND_BRANCH_STR) {
+ TARGET(COMPARE_OP_STR) {
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
+ PyObject *res;
assert(cframe.use_tracing == 0);
- DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_AND_BRANCH);
- DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_AND_BRANCH);
- STAT_INC(COMPARE_AND_BRANCH, hit);
- int res = _PyUnicode_Equal(left, right);
+ DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP);
+ DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP);
+ STAT_INC(COMPARE_OP, hit);
+ int eq = _PyUnicode_Equal(left, right);
assert((oparg >>4) == Py_EQ || (oparg >>4) == Py_NE);
_Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc);
_Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
- assert(res == 0 || res == 1);
+ assert(eq == 0 || eq == 1);
assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS);
assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS);
- if ((res + COMPARISON_NOT_EQUALS) & oparg) {
- int offset = next_instr[1].op.arg;
- JUMPBY(offset);
- }
- STACK_SHRINK(2);
- next_instr += 2;
+ res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? Py_True : Py_False;
+ Py_INCREF(res);
+ STACK_SHRINK(1);
+ stack_pointer[-1] = res;
+ next_instr += 1;
DISPATCH();
}
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 25463ced32d5d4..bc3aeba527d13b 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -241,13 +241,11 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 2;
case COMPARE_OP:
return 2;
- case COMPARE_AND_BRANCH:
+ case COMPARE_OP_FLOAT:
return 2;
- case COMPARE_AND_BRANCH_FLOAT:
+ case COMPARE_OP_INT:
return 2;
- case COMPARE_AND_BRANCH_INT:
- return 2;
- case COMPARE_AND_BRANCH_STR:
+ case COMPARE_OP_STR:
return 2;
case IS_OP:
return 2;
@@ -637,14 +635,12 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case COMPARE_OP:
return 1;
- case COMPARE_AND_BRANCH:
- return 0;
- case COMPARE_AND_BRANCH_FLOAT:
- return 0;
- case COMPARE_AND_BRANCH_INT:
- return 0;
- case COMPARE_AND_BRANCH_STR:
- return 0;
+ case COMPARE_OP_FLOAT:
+ return 1;
+ case COMPARE_OP_INT:
+ return 1;
+ case COMPARE_OP_STR:
+ return 1;
case IS_OP:
return 1;
case CONTAINS_OP:
@@ -795,7 +791,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
}
#endif
-enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 };
+enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 };
struct opcode_metadata {
bool valid_entry;
enum InstructionFormat instr_format;
@@ -921,10 +917,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[STORE_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC000 },
[STORE_ATTR_SLOT] = { true, INSTR_FMT_IXC000 },
[COMPARE_OP] = { true, INSTR_FMT_IBC },
- [COMPARE_AND_BRANCH] = { true, INSTR_FMT_IBC0 },
- [COMPARE_AND_BRANCH_FLOAT] = { true, INSTR_FMT_IBC0 },
- [COMPARE_AND_BRANCH_INT] = { true, INSTR_FMT_IBC0 },
- [COMPARE_AND_BRANCH_STR] = { true, INSTR_FMT_IBC0 },
+ [COMPARE_OP_FLOAT] = { true, INSTR_FMT_IBC },
+ [COMPARE_OP_INT] = { true, INSTR_FMT_IBC },
+ [COMPARE_OP_STR] = { true, INSTR_FMT_IBC },
[IS_OP] = { true, INSTR_FMT_IB },
[CONTAINS_OP] = { true, INSTR_FMT_IB },
[CHECK_EG_MATCH] = { true, INSTR_FMT_IX },
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 38217d9e8f2ba9..48c858e0cd2d00 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -56,9 +56,9 @@ static void *opcode_targets[256] = {
&&TARGET_END_ASYNC_FOR,
&&TARGET_CLEANUP_THROW,
&&TARGET_CALL_NO_KW_TYPE_1,
- &&TARGET_COMPARE_AND_BRANCH_FLOAT,
- &&TARGET_COMPARE_AND_BRANCH_INT,
- &&TARGET_COMPARE_AND_BRANCH_STR,
+ &&TARGET_COMPARE_OP_FLOAT,
+ &&TARGET_COMPARE_OP_INT,
+ &&TARGET_COMPARE_OP_STR,
&&TARGET_STORE_SUBSCR,
&&TARGET_DELETE_SUBSCR,
&&TARGET_FOR_ITER_LIST,
@@ -140,9 +140,9 @@ static void *opcode_targets[256] = {
&&TARGET_STORE_DEREF,
&&TARGET_DELETE_DEREF,
&&TARGET_JUMP_BACKWARD,
- &&TARGET_COMPARE_AND_BRANCH,
- &&TARGET_CALL_FUNCTION_EX,
&&TARGET_STORE_ATTR_INSTANCE_VALUE,
+ &&TARGET_CALL_FUNCTION_EX,
+ &&TARGET_STORE_ATTR_SLOT,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
&&TARGET_SET_ADD,
@@ -152,24 +152,24 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE,
&&TARGET_RESUME,
&&TARGET_MATCH_CLASS,
- &&TARGET_STORE_ATTR_SLOT,
&&TARGET_STORE_ATTR_WITH_HINT,
+ &&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING,
- &&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_STORE_SUBSCR_DICT,
&&TARGET_STORE_SUBSCR_LIST_INT,
+ &&TARGET_UNPACK_SEQUENCE_LIST,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
- &&TARGET_UNPACK_SEQUENCE_LIST,
&&TARGET_UNPACK_SEQUENCE_TUPLE,
&&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
&&TARGET_SEND_GEN,
&&_unknown_opcode,
+ &&_unknown_opcode,
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_CALL_INTRINSIC_1,
diff --git a/Python/specialize.c b/Python/specialize.c
index 2e93ab193990a1..dd5b22dd2346c5 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -264,15 +264,6 @@ do { \
#define SPECIALIZATION_FAIL(opcode, kind) ((void)0)
#endif
-static int compare_masks[] = {
- [Py_LT] = COMPARISON_LESS_THAN,
- [Py_LE] = COMPARISON_LESS_THAN | COMPARISON_EQUALS,
- [Py_EQ] = COMPARISON_EQUALS,
- [Py_NE] = COMPARISON_NOT_EQUALS,
- [Py_GT] = COMPARISON_GREATER_THAN,
- [Py_GE] = COMPARISON_GREATER_THAN | COMPARISON_EQUALS,
-};
-
// Initialize warmup counters and insert superinstructions. This cannot fail.
void
_PyCode_Quicken(PyCodeObject *code)
@@ -305,19 +296,6 @@ _PyCode_Quicken(PyCodeObject *code)
case STORE_FAST << 8 | STORE_FAST:
instructions[i - 1].op.code = STORE_FAST__STORE_FAST;
break;
- case COMPARE_OP << 8 | POP_JUMP_IF_TRUE:
- case COMPARE_OP << 8 | POP_JUMP_IF_FALSE:
- {
- int oparg = instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].op.arg;
- assert((oparg >> 4) <= Py_GE);
- int mask = compare_masks[oparg >> 4];
- if (opcode == POP_JUMP_IF_FALSE) {
- mask = mask ^ 0xf;
- }
- instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].op.code = COMPARE_AND_BRANCH;
- instructions[i - 1 - INLINE_CACHE_ENTRIES_COMPARE_OP].op.arg = (oparg & 0xf0) | mask;
- break;
- }
}
}
#endif /* ENABLE_SPECIALIZATION */
@@ -436,19 +414,17 @@ _PyCode_Quicken(PyCodeObject *code)
#define SPEC_FAIL_CALL_OPERATOR_WRAPPER 29
/* COMPARE_OP */
-#define SPEC_FAIL_COMPARE_DIFFERENT_TYPES 12
-#define SPEC_FAIL_COMPARE_STRING 13
-#define SPEC_FAIL_COMPARE_NOT_FOLLOWED_BY_COND_JUMP 14
-#define SPEC_FAIL_COMPARE_BIG_INT 15
-#define SPEC_FAIL_COMPARE_BYTES 16
-#define SPEC_FAIL_COMPARE_TUPLE 17
-#define SPEC_FAIL_COMPARE_LIST 18
-#define SPEC_FAIL_COMPARE_SET 19
-#define SPEC_FAIL_COMPARE_BOOL 20
-#define SPEC_FAIL_COMPARE_BASEOBJECT 21
-#define SPEC_FAIL_COMPARE_FLOAT_LONG 22
-#define SPEC_FAIL_COMPARE_LONG_FLOAT 23
-#define SPEC_FAIL_COMPARE_EXTENDED_ARG 24
+#define SPEC_FAIL_COMPARE_OP_DIFFERENT_TYPES 12
+#define SPEC_FAIL_COMPARE_OP_STRING 13
+#define SPEC_FAIL_COMPARE_OP_BIG_INT 14
+#define SPEC_FAIL_COMPARE_OP_BYTES 15
+#define SPEC_FAIL_COMPARE_OP_TUPLE 16
+#define SPEC_FAIL_COMPARE_OP_LIST 17
+#define SPEC_FAIL_COMPARE_OP_SET 18
+#define SPEC_FAIL_COMPARE_OP_BOOL 19
+#define SPEC_FAIL_COMPARE_OP_BASEOBJECT 20
+#define SPEC_FAIL_COMPARE_OP_FLOAT_LONG 21
+#define SPEC_FAIL_COMPARE_OP_LONG_FLOAT 22
/* FOR_ITER */
#define SPEC_FAIL_FOR_ITER_GENERATOR 10
@@ -1958,83 +1934,79 @@ compare_op_fail_kind(PyObject *lhs, PyObject *rhs)
{
if (Py_TYPE(lhs) != Py_TYPE(rhs)) {
if (PyFloat_CheckExact(lhs) && PyLong_CheckExact(rhs)) {
- return SPEC_FAIL_COMPARE_FLOAT_LONG;
+ return SPEC_FAIL_COMPARE_OP_FLOAT_LONG;
}
if (PyLong_CheckExact(lhs) && PyFloat_CheckExact(rhs)) {
- return SPEC_FAIL_COMPARE_LONG_FLOAT;
+ return SPEC_FAIL_COMPARE_OP_LONG_FLOAT;
}
- return SPEC_FAIL_COMPARE_DIFFERENT_TYPES;
+ return SPEC_FAIL_COMPARE_OP_DIFFERENT_TYPES;
}
if (PyBytes_CheckExact(lhs)) {
- return SPEC_FAIL_COMPARE_BYTES;
+ return SPEC_FAIL_COMPARE_OP_BYTES;
}
if (PyTuple_CheckExact(lhs)) {
- return SPEC_FAIL_COMPARE_TUPLE;
+ return SPEC_FAIL_COMPARE_OP_TUPLE;
}
if (PyList_CheckExact(lhs)) {
- return SPEC_FAIL_COMPARE_LIST;
+ return SPEC_FAIL_COMPARE_OP_LIST;
}
if (PySet_CheckExact(lhs) || PyFrozenSet_CheckExact(lhs)) {
- return SPEC_FAIL_COMPARE_SET;
+ return SPEC_FAIL_COMPARE_OP_SET;
}
if (PyBool_Check(lhs)) {
- return SPEC_FAIL_COMPARE_BOOL;
+ return SPEC_FAIL_COMPARE_OP_BOOL;
}
if (Py_TYPE(lhs)->tp_richcompare == PyBaseObject_Type.tp_richcompare) {
- return SPEC_FAIL_COMPARE_BASEOBJECT;
+ return SPEC_FAIL_COMPARE_OP_BASEOBJECT;
}
return SPEC_FAIL_OTHER;
}
#endif
void
-_Py_Specialize_CompareAndBranch(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
+_Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
int oparg)
{
assert(ENABLE_SPECIALIZATION);
- assert(_PyOpcode_Caches[COMPARE_AND_BRANCH] == INLINE_CACHE_ENTRIES_COMPARE_OP);
+ assert(_PyOpcode_Caches[COMPARE_OP] == INLINE_CACHE_ENTRIES_COMPARE_OP);
_PyCompareOpCache *cache = (_PyCompareOpCache *)(instr + 1);
-#ifndef NDEBUG
- int next_opcode = instr[INLINE_CACHE_ENTRIES_COMPARE_OP + 1].op.code;
- assert(next_opcode == POP_JUMP_IF_FALSE || next_opcode == POP_JUMP_IF_TRUE);
-#endif
if (Py_TYPE(lhs) != Py_TYPE(rhs)) {
- SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, compare_op_fail_kind(lhs, rhs));
+ SPECIALIZATION_FAIL(COMPARE_OP, compare_op_fail_kind(lhs, rhs));
goto failure;
}
if (PyFloat_CheckExact(lhs)) {
- instr->op.code = COMPARE_AND_BRANCH_FLOAT;
+ instr->op.code = COMPARE_OP_FLOAT;
goto success;
}
if (PyLong_CheckExact(lhs)) {
if (_PyLong_IsCompact((PyLongObject *)lhs) && _PyLong_IsCompact((PyLongObject *)rhs)) {
- instr->op.code = COMPARE_AND_BRANCH_INT;
+ instr->op.code = COMPARE_OP_INT;
goto success;
}
else {
- SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, SPEC_FAIL_COMPARE_BIG_INT);
+ SPECIALIZATION_FAIL(COMPARE_OP, SPEC_FAIL_COMPARE_OP_BIG_INT);
goto failure;
}
}
if (PyUnicode_CheckExact(lhs)) {
int cmp = oparg >> 4;
if (cmp != Py_EQ && cmp != Py_NE) {
- SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, SPEC_FAIL_COMPARE_STRING);
+ SPECIALIZATION_FAIL(COMPARE_OP, SPEC_FAIL_COMPARE_OP_STRING);
goto failure;
}
else {
- instr->op.code = COMPARE_AND_BRANCH_STR;
+ instr->op.code = COMPARE_OP_STR;
goto success;
}
}
- SPECIALIZATION_FAIL(COMPARE_AND_BRANCH, compare_op_fail_kind(lhs, rhs));
+ SPECIALIZATION_FAIL(COMPARE_OP, compare_op_fail_kind(lhs, rhs));
failure:
- STAT_INC(COMPARE_AND_BRANCH, failure);
- instr->op.code = COMPARE_AND_BRANCH;
+ STAT_INC(COMPARE_OP, failure);
+ instr->op.code = COMPARE_OP;
cache->counter = adaptive_counter_backoff(cache->counter);
return;
success:
- STAT_INC(COMPARE_AND_BRANCH, success);
+ STAT_INC(COMPARE_OP, success);
cache->counter = adaptive_counter_cooldown();
}
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index c50ae42092bfd5..46987cc6a07ed2 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -645,23 +645,21 @@
break;
}
- TARGET(COMPARE_AND_BRANCH) {
- STACK_SHRINK(2);
- break;
- }
-
- TARGET(COMPARE_AND_BRANCH_FLOAT) {
- STACK_SHRINK(2);
+ TARGET(COMPARE_OP_FLOAT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
- TARGET(COMPARE_AND_BRANCH_INT) {
- STACK_SHRINK(2);
+ TARGET(COMPARE_OP_INT) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
- TARGET(COMPARE_AND_BRANCH_STR) {
- STACK_SHRINK(2);
+ TARGET(COMPARE_OP_STR) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv
index 14fc32a07b4309..9203c1888149fb 100644
--- a/Tools/c-analyzer/cpython/ignored.tsv
+++ b/Tools/c-analyzer/cpython/ignored.tsv
@@ -350,7 +350,6 @@ Python/pylifecycle.c - INTERPRETER_TRAMPOLINE_CODEDEF -
Python/pystate.c - initial -
Python/specialize.c - adaptive_opcodes -
Python/specialize.c - cache_requirements -
-Python/specialize.c - compare_masks -
Python/stdlib_module_names.h - _Py_stdlib_module_names -
Python/sysmodule.c - _PySys_ImplCacheTag -
Python/sysmodule.c - _PySys_ImplName -
diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py
index 7789c4d3a17d38..ce25374f3a9a52 100644
--- a/Tools/scripts/summarize_stats.py
+++ b/Tools/scripts/summarize_stats.py
@@ -228,8 +228,6 @@ def kind_to_text(kind, defines, opname):
return pretty(defines[kind][0])
if opname.endswith("ATTR"):
opname = "ATTR"
- if opname in ("COMPARE_OP", "COMPARE_AND_BRANCH"):
- opname = "COMPARE"
if opname.endswith("SUBSCR"):
opname = "SUBSCR"
for name in defines[kind]:
From 6f57d0a57c801e55a3de2275a1c58ceee12e07e3 Mon Sep 17 00:00:00 2001
From: Steve Dower
Date: Thu, 23 Mar 2023 23:27:46 +0000
Subject: [PATCH 207/280] gh-99726: Fix order of recently added fields for
FILE_STAT_BASIC_INFORMATION (GH-102976)
---
Include/internal/pycore_fileutils_windows.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Include/internal/pycore_fileutils_windows.h b/Include/internal/pycore_fileutils_windows.h
index 44874903b092f3..9bc7feb8cecd01 100644
--- a/Include/internal/pycore_fileutils_windows.h
+++ b/Include/internal/pycore_fileutils_windows.h
@@ -25,8 +25,8 @@ typedef struct _FILE_STAT_BASIC_INFORMATION {
ULONG DeviceType;
ULONG DeviceCharacteristics;
ULONG Reserved;
- FILE_ID_128 FileId128;
LARGE_INTEGER VolumeSerialNumber;
+ FILE_ID_128 FileId128;
} FILE_STAT_BASIC_INFORMATION;
typedef enum _FILE_INFO_BY_NAME_CLASS {
From a67d94ab040700a62d47ad6b9272e7f43a59e9fa Mon Sep 17 00:00:00 2001
From: MonadChains
Date: Fri, 24 Mar 2023 00:42:43 +0100
Subject: [PATCH 208/280] gh-94684: uuid: support bytes in the name argument to
uuid3/5 (#94709)
RFC 4122 does not specify that name should be a string, so for completness the functions should also support a name given as a raw byte sequence.
---
Doc/library/uuid.rst | 6 ++--
Lib/test/test_uuid.py | 34 +++++++++++++++++--
Lib/uuid.py | 8 +++--
...2-07-09-13-07-30.gh-issue-94684.nV5yno.rst | 1 +
4 files changed, 43 insertions(+), 6 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2022-07-09-13-07-30.gh-issue-94684.nV5yno.rst
diff --git a/Doc/library/uuid.rst b/Doc/library/uuid.rst
index 38b6434f467fd6..94b9a432372195 100644
--- a/Doc/library/uuid.rst
+++ b/Doc/library/uuid.rst
@@ -186,7 +186,8 @@ The :mod:`uuid` module defines the following functions:
.. function:: uuid3(namespace, name)
Generate a UUID based on the MD5 hash of a namespace identifier (which is a
- UUID) and a name (which is a string).
+ UUID) and a name (which is a :class:`bytes` object or a string
+ that will be encoded using UTF-8).
.. index:: single: uuid3
@@ -201,7 +202,8 @@ The :mod:`uuid` module defines the following functions:
.. function:: uuid5(namespace, name)
Generate a UUID based on the SHA-1 hash of a namespace identifier (which is a
- UUID) and a name (which is a string).
+ UUID) and a name (which is a :class:`bytes` object or a string
+ that will be encoded using UTF-8).
.. index:: single: uuid5
diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py
index b2c229cd634e31..a178e942ecda0f 100755
--- a/Lib/test/test_uuid.py
+++ b/Lib/test/test_uuid.py
@@ -600,7 +600,22 @@ def test_uuid1_time(self):
def test_uuid3(self):
equal = self.assertEqual
- # Test some known version-3 UUIDs.
+ # Test some known version-3 UUIDs with name passed as a byte object
+ for u, v in [(self.uuid.uuid3(self.uuid.NAMESPACE_DNS, b'python.org'),
+ '6fa459ea-ee8a-3ca4-894e-db77e160355e'),
+ (self.uuid.uuid3(self.uuid.NAMESPACE_URL, b'http://python.org/'),
+ '9fe8e8c4-aaa8-32a9-a55c-4535a88b748d'),
+ (self.uuid.uuid3(self.uuid.NAMESPACE_OID, b'1.3.6.1'),
+ 'dd1a1cef-13d5-368a-ad82-eca71acd4cd1'),
+ (self.uuid.uuid3(self.uuid.NAMESPACE_X500, b'c=ca'),
+ '658d3002-db6b-3040-a1d1-8ddd7d189a4d'),
+ ]:
+ equal(u.variant, self.uuid.RFC_4122)
+ equal(u.version, 3)
+ equal(u, self.uuid.UUID(v))
+ equal(str(u), v)
+
+ # Test some known version-3 UUIDs with name passed as a string
for u, v in [(self.uuid.uuid3(self.uuid.NAMESPACE_DNS, 'python.org'),
'6fa459ea-ee8a-3ca4-894e-db77e160355e'),
(self.uuid.uuid3(self.uuid.NAMESPACE_URL, 'http://python.org/'),
@@ -632,7 +647,22 @@ def test_uuid4(self):
def test_uuid5(self):
equal = self.assertEqual
- # Test some known version-5 UUIDs.
+ # Test some known version-5 UUIDs with names given as byte objects
+ for u, v in [(self.uuid.uuid5(self.uuid.NAMESPACE_DNS, b'python.org'),
+ '886313e1-3b8a-5372-9b90-0c9aee199e5d'),
+ (self.uuid.uuid5(self.uuid.NAMESPACE_URL, b'http://python.org/'),
+ '4c565f0d-3f5a-5890-b41b-20cf47701c5e'),
+ (self.uuid.uuid5(self.uuid.NAMESPACE_OID, b'1.3.6.1'),
+ '1447fa61-5277-5fef-a9b3-fbc6e44f4af3'),
+ (self.uuid.uuid5(self.uuid.NAMESPACE_X500, b'c=ca'),
+ 'cc957dd1-a972-5349-98cd-874190002798'),
+ ]:
+ equal(u.variant, self.uuid.RFC_4122)
+ equal(u.version, 5)
+ equal(u, self.uuid.UUID(v))
+ equal(str(u), v)
+
+ # Test some known version-5 UUIDs with names given as strings
for u, v in [(self.uuid.uuid5(self.uuid.NAMESPACE_DNS, 'python.org'),
'886313e1-3b8a-5372-9b90-0c9aee199e5d'),
(self.uuid.uuid5(self.uuid.NAMESPACE_URL, 'http://python.org/'),
diff --git a/Lib/uuid.py b/Lib/uuid.py
index 1c5578bf1f05c2..698be34873b9dc 100644
--- a/Lib/uuid.py
+++ b/Lib/uuid.py
@@ -711,9 +711,11 @@ def uuid1(node=None, clock_seq=None):
def uuid3(namespace, name):
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
+ if isinstance(name, str):
+ name = bytes(name, "utf-8")
from hashlib import md5
digest = md5(
- namespace.bytes + bytes(name, "utf-8"),
+ namespace.bytes + name,
usedforsecurity=False
).digest()
return UUID(bytes=digest[:16], version=3)
@@ -724,8 +726,10 @@ def uuid4():
def uuid5(namespace, name):
"""Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
+ if isinstance(name, str):
+ name = bytes(name, "utf-8")
from hashlib import sha1
- hash = sha1(namespace.bytes + bytes(name, "utf-8")).digest()
+ hash = sha1(namespace.bytes + name).digest()
return UUID(bytes=hash[:16], version=5)
diff --git a/Misc/NEWS.d/next/Library/2022-07-09-13-07-30.gh-issue-94684.nV5yno.rst b/Misc/NEWS.d/next/Library/2022-07-09-13-07-30.gh-issue-94684.nV5yno.rst
new file mode 100644
index 00000000000000..1fa38c0044d36f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-07-09-13-07-30.gh-issue-94684.nV5yno.rst
@@ -0,0 +1 @@
+Now :func:`uuid.uuid3` and :func:`uuid.uuid5` functions support :class:`bytes` objects as their *name* argument.
From c945040ee6f0bc9cc8a3f927028f4d6e1ddb2243 Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Thu, 23 Mar 2023 19:50:17 -0500
Subject: [PATCH 209/280] GH-100989: Revert Improve the accuracy of
collections.deque docstrings (GH-102979)
---
Modules/_collectionsmodule.c | 37 +++++++++++++-----------------------
1 file changed, 13 insertions(+), 24 deletions(-)
diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c
index ba4a9760f7b906..68131f3b54d2ea 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -910,9 +910,7 @@ deque_rotate(dequeobject *deque, PyObject *const *args, Py_ssize_t nargs)
}
PyDoc_STRVAR(rotate_doc,
-"rotate(n)\n\n"
-"Rotate the deque *n* steps to the right (default ``n=1``). "
-"If *n* is negative, rotates left.");
+"Rotate the deque n steps to the right (default n=1). If n is negative, rotates left.");
static PyObject *
deque_reverse(dequeobject *deque, PyObject *unused)
@@ -953,8 +951,7 @@ deque_reverse(dequeobject *deque, PyObject *unused)
}
PyDoc_STRVAR(reverse_doc,
-"reverse()\n\n"
-"Reverse the elements of the deque *IN PLACE*.");
+"D.reverse() -- reverse *IN PLACE*");
static PyObject *
deque_count(dequeobject *deque, PyObject *v)
@@ -993,8 +990,7 @@ deque_count(dequeobject *deque, PyObject *v)
}
PyDoc_STRVAR(count_doc,
-"count(x) -> int\n\n"
-"Count the number of deque elements equal to *x*.");
+"D.count(value) -> integer -- return number of occurrences of value");
static int
deque_contains(dequeobject *deque, PyObject *v)
@@ -1102,10 +1098,8 @@ deque_index(dequeobject *deque, PyObject *const *args, Py_ssize_t nargs)
}
PyDoc_STRVAR(index_doc,
-"index(x, [start, [stop]]) -> int\n\n"
-"Return the position of *x* in the deque "
-"(at or after index *start* and before index *stop*). "
-"Returns the first match or raises a ValueError if not found.");
+"D.index(value, [start, [stop]]) -> integer -- return first index of value.\n"
+"Raises ValueError if the value is not present.");
/* insert(), remove(), and delitem() are implemented in terms of
rotate() for simplicity and reasonable performance near the end
@@ -1150,13 +1144,10 @@ deque_insert(dequeobject *deque, PyObject *const *args, Py_ssize_t nargs)
}
PyDoc_STRVAR(insert_doc,
-"insert(i, x)\n\n"
-"Insert *x* into the deque at position *i*.");
+"D.insert(index, object) -- insert object before index");
PyDoc_STRVAR(remove_doc,
-"remove(x)\n\n"
-"Remove the first occurrence of *x*."
-"If not found, raises a ValueError.");
+"D.remove(value) -- remove first occurrence of value.");
static int
valid_index(Py_ssize_t i, Py_ssize_t limit)
@@ -1527,8 +1518,7 @@ deque_sizeof(dequeobject *deque, void *unused)
}
PyDoc_STRVAR(sizeof_doc,
-"__sizeof__() -> int\n\n"
-"Size of the deque in memory, in bytes.");
+"D.__sizeof__() -- size of D in memory, in bytes");
static PyObject *
deque_get_maxlen(dequeobject *deque, void *Py_UNUSED(ignored))
@@ -1563,8 +1553,7 @@ static PySequenceMethods deque_as_sequence = {
static PyObject *deque_iter(dequeobject *deque);
static PyObject *deque_reviter(dequeobject *deque, PyObject *Py_UNUSED(ignored));
PyDoc_STRVAR(reversed_doc,
-"__reversed__()\n\n"
-"Return a reverse iterator over the deque.");
+ "D.__reversed__() -- return a reverse iterator over the deque");
static PyMethodDef deque_methods[] = {
{"append", (PyCFunction)deque_append,
@@ -1609,8 +1598,9 @@ static PyMethodDef deque_methods[] = {
};
PyDoc_STRVAR(deque_doc,
-"deque([iterable[, maxlen]]) -> collections.deque\n\n"
-"A list-like sequence optimized for data accesses near its endpoints.");
+"deque([iterable[, maxlen]]) --> deque object\n\
+\n\
+A list-like sequence optimized for data accesses near its endpoints.");
static PyTypeObject deque_type = {
PyVarObject_HEAD_INIT(NULL, 0)
@@ -1989,8 +1979,7 @@ new_defdict(defdictobject *dd, PyObject *arg)
dd->default_factory ? dd->default_factory : Py_None, arg, NULL);
}
-PyDoc_STRVAR(defdict_copy_doc, "copy() -> collections.deque\n\n"
-"A shallow copy of the deque.");
+PyDoc_STRVAR(defdict_copy_doc, "D.copy() -> a shallow copy of D.");
static PyObject *
defdict_copy(defdictobject *dd, PyObject *Py_UNUSED(ignored))
From de24df7a9abdb03326e1bd7a31237c56e6bf1605 Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Fri, 24 Mar 2023 00:39:12 -0500
Subject: [PATCH 210/280] GH-100989: remove annotation from docstring
(GH-102991)
---
Modules/_collectionsmodule.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c
index 68131f3b54d2ea..9d8aef1e677157 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -990,7 +990,7 @@ deque_count(dequeobject *deque, PyObject *v)
}
PyDoc_STRVAR(count_doc,
-"D.count(value) -> integer -- return number of occurrences of value");
+"D.count(value) -- return number of occurrences of value");
static int
deque_contains(dequeobject *deque, PyObject *v)
@@ -1098,7 +1098,7 @@ deque_index(dequeobject *deque, PyObject *const *args, Py_ssize_t nargs)
}
PyDoc_STRVAR(index_doc,
-"D.index(value, [start, [stop]]) -> integer -- return first index of value.\n"
+"D.index(value, [start, [stop]]) -- return first index of value.\n"
"Raises ValueError if the value is not present.");
/* insert(), remove(), and delitem() are implemented in terms of
From bf3bfdb41a5949ad1923969244d73e539205ba69 Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade
Date: Fri, 24 Mar 2023 13:23:35 +0200
Subject: [PATCH 211/280] gh-101100: Test docs in nit-picky mode (#102513)
Co-authored-by: C.A.M. Gerlach
Co-authored-by: Petr Viktorin
---
.github/workflows/doc.yml | 22 ++++++++
Doc/c-api/exceptions.rst | 2 +-
Doc/c-api/weakref.rst | 10 ++++
Doc/library/asyncio-task.rst | 7 +++
Doc/library/gzip.rst | 6 ++
Doc/tools/warnings-to-gh-actions.py | 25 ++++++++
Doc/whatsnew/3.12.rst | 88 ++++++++++++++---------------
Misc/NEWS.d/3.10.0a2.rst | 2 +-
8 files changed, 116 insertions(+), 46 deletions(-)
create mode 100644 Doc/tools/warnings-to-gh-actions.py
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index 465da12fa1be80..9ec60c198eb3b6 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -53,6 +53,28 @@ jobs:
- name: 'Build HTML documentation'
run: make -C Doc/ SPHINXOPTS="-q" SPHINXERRORHANDLING="-W --keep-going" html
+ # Add pull request annotations for Sphinx nitpicks (missing references)
+ - name: 'Get list of changed files'
+ id: changed_files
+ uses: Ana06/get-changed-files@v2.2.0
+ - name: 'Build changed files in nit-picky mode'
+ continue-on-error: true
+ run: |
+ # Mark files the pull request modified
+ touch ${{ steps.changed_files.outputs.added_modified }}
+ # Build docs with the '-n' (nit-picky) option; convert warnings to annotations
+ make -C Doc/ PYTHON=../python SPHINXOPTS="-q -n --keep-going" html 2>&1 |
+ python Doc/tools/warnings-to-gh-actions.py
+
+ # Ensure some files always pass Sphinx nit-picky mode (no missing references)
+ - name: 'Build known-good files in nit-picky mode'
+ run: |
+ # Mark files that must pass nit-picky
+ touch Doc/whatsnew/3.12.rst
+ touch Doc/library/sqlite3.rst
+ # Build docs with the '-n' (nit-picky) option, convert warnings to errors (-W)
+ make -C Doc/ PYTHON=../python SPHINXOPTS="-q -n -W --keep-going" html 2>&1
+
# Run "doctest" on HEAD as new syntax doesn't exist in the latest stable release
doctest:
name: 'Doctest'
diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index ddf7dc780b2745..49d2f18d4573b0 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -86,7 +86,7 @@ Printing and clearing
An exception must be set when calling this function.
-.. c:function: void PyErr_DisplayException(PyObject *exc)
+.. c:function:: void PyErr_DisplayException(PyObject *exc)
Print the standard traceback display of ``exc`` to ``sys.stderr``, including
chained exceptions and notes.
diff --git a/Doc/c-api/weakref.rst b/Doc/c-api/weakref.rst
index ace743ba01c5f5..f27ec4411b4a26 100644
--- a/Doc/c-api/weakref.rst
+++ b/Doc/c-api/weakref.rst
@@ -67,3 +67,13 @@ as much as it can.
.. c:function:: PyObject* PyWeakref_GET_OBJECT(PyObject *ref)
Similar to :c:func:`PyWeakref_GetObject`, but does no error checking.
+
+
+.. c:function:: void PyObject_ClearWeakRefs(PyObject *object)
+
+ This function is called by the :c:member:`~PyTypeObject.tp_dealloc` handler
+ to clear weak references.
+
+ This iterates through the weak references for *object* and calls callbacks
+ for those references which have one. It returns when all callbacks have
+ been attempted.
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index c5a480ba20190a..41d09e1e79705c 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -960,6 +960,13 @@ Introspection
.. versionadded:: 3.7
+.. function:: iscoroutine(obj)
+
+ Return ``True`` if *obj* is a coroutine object.
+
+ .. versionadded:: 3.4
+
+
Task Object
===========
diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst
index 1a2582d6a904b2..06cbd2567a0bc6 100644
--- a/Doc/library/gzip.rst
+++ b/Doc/library/gzip.rst
@@ -143,6 +143,12 @@ The module defines the following items:
:func:`time.time` and the :attr:`~os.stat_result.st_mtime` attribute of
the object returned by :func:`os.stat`.
+ .. attribute:: name
+
+ The path to the gzip file on disk, as a :class:`str` or :class:`bytes`.
+ Equivalent to the output of :func:`os.fspath` on the original input path,
+ with no other normalization, resolution or expansion.
+
.. versionchanged:: 3.1
Support for the :keyword:`with` statement was added, along with the
*mtime* constructor argument and :attr:`mtime` attribute.
diff --git a/Doc/tools/warnings-to-gh-actions.py b/Doc/tools/warnings-to-gh-actions.py
new file mode 100644
index 00000000000000..da33a4ede07abc
--- /dev/null
+++ b/Doc/tools/warnings-to-gh-actions.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+
+"""
+Convert Sphinx warning messages to GitHub Actions.
+
+Converts lines like:
+ .../Doc/library/cgi.rst:98: WARNING: reference target not found
+to:
+ ::warning file=.../Doc/library/cgi.rst,line=98::reference target not found
+
+Non-matching lines are echoed unchanged.
+
+see: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-warning-message
+"""
+
+import re
+import sys
+
+pattern = re.compile(r'(?P[^:]+):(?P\d+): WARNING: (?P.+)')
+
+for line in sys.stdin:
+ if match := pattern.fullmatch(line.strip()):
+ print('::warning file={file},line={line}::{msg}'.format_map(match))
+ else:
+ print(line)
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 06ea416d751354..e5bcfdecd9a487 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -248,7 +248,7 @@ inspect
-------
* Add :func:`inspect.markcoroutinefunction` to mark sync functions that return
- a :term:`coroutine` for use with :func:`iscoroutinefunction`.
+ a :term:`coroutine` for use with :func:`inspect.iscoroutinefunction`.
(Contributed Carlton Gibson in :gh:`99247`.)
* Add :func:`inspect.getasyncgenstate` and :func:`inspect.getasyncgenlocals`
@@ -277,7 +277,7 @@ dis
* Pseudo instruction opcodes (which are used by the compiler but
do not appear in executable bytecode) are now exposed in the
:mod:`dis` module.
- :data:`~dis.HAVE_ARGUMENT` is still relevant to real opcodes,
+ :opcode:`HAVE_ARGUMENT` is still relevant to real opcodes,
but it is not useful for pseudo instructions. Use the new
:data:`~dis.hasarg` collection instead.
(Contributed by Irit Katriel in :gh:`94216`.)
@@ -422,7 +422,7 @@ Optimizations
(Contributed by Kevin Modzelewski in :gh:`90536`.)
* Speed up the regular expression substitution (functions :func:`re.sub` and
- :func:`re.subn` and corresponding :class:`re.Pattern` methods) for
+ :func:`re.subn` and corresponding :class:`!re.Pattern` methods) for
replacement strings containing group references by 2--3 times.
(Contributed by Serhiy Storchaka in :gh:`91524`.)
@@ -435,7 +435,7 @@ CPython bytecode changes
:opcode:`LOAD_METHOD` instruction if the low bit of its oparg is set.
(Contributed by Ken Jin in :gh:`93429`.)
-* Removed the :opcode:`JUMP_IF_FALSE_OR_POP` and :opcode:`JUMP_IF_TRUE_OR_POP`
+* Removed the :opcode:`!JUMP_IF_FALSE_OR_POP` and :opcode:`!JUMP_IF_TRUE_OR_POP`
instructions. (Contributed by Irit Katriel in :gh:`102859`.)
@@ -482,7 +482,7 @@ Deprecated
:exc:`ImportWarning`).
(Contributed by Brett Cannon in :gh:`65961`.)
-* The :meth:`~asyncio.DefaultEventLoopPolicy.get_event_loop` method of the
+* The :meth:`~asyncio.get_event_loop` method of the
default event loop policy now emits a :exc:`DeprecationWarning` if there
is no current event loop set and it decides to create one.
(Contributed by Serhiy Storchaka and Guido van Rossum in :gh:`100160`.)
@@ -541,13 +541,13 @@ Modules (see :pep:`594`):
APIs:
-* :class:`configparser.LegacyInterpolation` (:gh:`90765`)
+* :class:`!configparser.LegacyInterpolation` (:gh:`90765`)
* :func:`locale.getdefaultlocale` (:gh:`90817`)
-* :meth:`turtle.RawTurtle.settiltangle` (:gh:`50096`)
-* :func:`unittest.findTestCases` (:gh:`50096`)
-* :func:`unittest.makeSuite` (:gh:`50096`)
-* :func:`unittest.getTestCaseNames` (:gh:`50096`)
-* :class:`webbrowser.MacOSX` (:gh:`86421`)
+* :meth:`!turtle.RawTurtle.settiltangle` (:gh:`50096`)
+* :func:`!unittest.findTestCases` (:gh:`50096`)
+* :func:`!unittest.makeSuite` (:gh:`50096`)
+* :func:`!unittest.getTestCaseNames` (:gh:`50096`)
+* :class:`!webbrowser.MacOSX` (:gh:`86421`)
Pending Removal in Python 3.14
------------------------------
@@ -555,9 +555,9 @@ Pending Removal in Python 3.14
* Deprecated the following :mod:`importlib.abc` classes, scheduled for removal in
Python 3.14:
- * :class:`importlib.abc.ResourceReader`
- * :class:`importlib.abc.Traversable`
- * :class:`importlib.abc.TraversableResources`
+ * :class:`!importlib.abc.ResourceReader`
+ * :class:`!importlib.abc.Traversable`
+ * :class:`!importlib.abc.TraversableResources`
Use :mod:`importlib.resources.abc` classes instead:
@@ -566,7 +566,7 @@ Pending Removal in Python 3.14
(Contributed by Jason R. Coombs and Hugo van Kemenade in :gh:`93963`.)
-* Creating :c:data:`immutable types ` with mutable
+* Creating immutable types (:data:`Py_TPFLAGS_IMMUTABLETYPE`) with mutable
bases using the C API.
* Deprecated the *isdst* parameter in :func:`email.utils.localtime`.
@@ -701,11 +701,11 @@ Removed
* Remove ``io.OpenWrapper`` and ``_pyio.OpenWrapper``, deprecated in Python
3.10: just use :func:`open` instead. The :func:`open` (:func:`io.open`)
- function is a built-in function. Since Python 3.10, :func:`_pyio.open` is
+ function is a built-in function. Since Python 3.10, :func:`!_pyio.open` is
also a static method.
(Contributed by Victor Stinner in :gh:`94169`.)
-* Remove the :func:`ssl.RAND_pseudo_bytes` function, deprecated in Python 3.6:
+* Remove the :func:`!ssl.RAND_pseudo_bytes` function, deprecated in Python 3.6:
use :func:`os.urandom` or :func:`ssl.RAND_bytes` instead.
(Contributed by Victor Stinner in :gh:`94199`.)
@@ -715,13 +715,13 @@ Removed
extension if it was not present.
(Contributed by Victor Stinner in :gh:`94196`.)
-* Remove the :func:`ssl.match_hostname` function. The
- :func:`ssl.match_hostname` was deprecated in Python 3.7. OpenSSL performs
+* Remove the :func:`!ssl.match_hostname` function.
+ It was deprecated in Python 3.7. OpenSSL performs
hostname matching since Python 3.7, Python no longer uses the
- :func:`ssl.match_hostname` function.
+ :func:`!ssl.match_hostname` function.
(Contributed by Victor Stinner in :gh:`94199`.)
-* Remove the :func:`locale.format` function, deprecated in Python 3.7:
+* Remove the :func:`!locale.format` function, deprecated in Python 3.7:
use :func:`locale.format_string` instead.
(Contributed by Victor Stinner in :gh:`94226`.)
@@ -731,9 +731,9 @@ Removed
a C implementation of :func:`~hashlib.pbkdf2_hmac()` which is faster.
(Contributed by Victor Stinner in :gh:`94199`.)
-* :mod:`xml.etree`: Remove the ``ElementTree.Element.copy()`` method of the
+* :mod:`xml.etree.ElementTree`: Remove the ``ElementTree.Element.copy()`` method of the
pure Python implementation, deprecated in Python 3.10, use the
- :func:`copy.copy` function instead. The C implementation of :mod:`xml.etree`
+ :func:`copy.copy` function instead. The C implementation of :mod:`xml.etree.ElementTree`
has no ``copy()`` method, only a ``__copy__()`` method.
(Contributed by Victor Stinner in :gh:`94383`.)
@@ -742,10 +742,10 @@ Removed
:pep:`451` for the rationale.
(Contributed by Victor Stinner in :gh:`94379`.)
-* Remove the :func:`ssl.wrap_socket` function, deprecated in Python 3.7:
+* Remove the :func:`!ssl.wrap_socket` function, deprecated in Python 3.7:
instead, create a :class:`ssl.SSLContext` object and call its
:class:`ssl.SSLContext.wrap_socket` method. Any package that still uses
- :func:`ssl.wrap_socket` is broken and insecure. The function neither sends a
+ :func:`!ssl.wrap_socket` is broken and insecure. The function neither sends a
SNI TLS extension nor validates server hostname. Code is subject to `CWE-295
`_: Improper Certificate
Validation.
@@ -912,7 +912,7 @@ New Features
The :const:`Py_TPFLAGS_HAVE_VECTORCALL` flag is now removed from a class
when the class's :py:meth:`~object.__call__` method is reassigned.
This makes vectorcall safe to use with mutable types (i.e. heap types
- without the :const:`immutable ` flag).
+ without the immutable flag, :const:`Py_TPFLAGS_IMMUTABLETYPE`).
Mutable types that do not override :c:member:`~PyTypeObject.tp_call` now
inherit the ``Py_TPFLAGS_HAVE_VECTORCALL`` flag.
(Contributed by Petr Viktorin in :gh:`93274`.)
@@ -945,7 +945,7 @@ New Features
(Contributed by Andrew Frost in :gh:`92257`.)
* The C API now permits registering callbacks via :c:func:`PyDict_AddWatcher`,
- :c:func:`PyDict_AddWatch` and related APIs to be called whenever a dictionary
+ :c:func:`PyDict_Watch` and related APIs to be called whenever a dictionary
is modified. This is intended for use by optimizing interpreters, JIT
compilers, or debuggers.
(Contributed by Carl Meyer in :gh:`91052`.)
@@ -977,7 +977,7 @@ New Features
(Contributed by Mark Shannon in :gh:`101578`.)
* Add :c:func:`PyErr_DisplayException`, which takes an exception instance,
- to replace the legacy-api :c:func:`PyErr_Display`. (Contributed by
+ to replace the legacy-api :c:func:`!PyErr_Display`. (Contributed by
Irit Katriel in :gh:`102755`).
Porting to Python 3.12
@@ -1024,7 +1024,7 @@ Porting to Python 3.12
supported, but does not fully support multiple inheritance
(:gh:`95589`), and performance may be worse.
Classes declaring :const:`Py_TPFLAGS_MANAGED_DICT` should call
- :c:func:`_PyObject_VisitManagedDict` and :c:func:`_PyObject_ClearManagedDict`
+ :c:func:`!_PyObject_VisitManagedDict` and :c:func:`!_PyObject_ClearManagedDict`
to traverse and clear their instance's dictionaries.
To clear weakrefs, call :c:func:`PyObject_ClearWeakRefs`, as before.
@@ -1069,17 +1069,17 @@ Deprecated
* :c:var:`Py_HashRandomizationFlag`: use :c:member:`PyConfig.use_hash_seed`
and :c:member:`PyConfig.hash_seed`
* :c:var:`Py_IsolatedFlag`: use :c:member:`PyConfig.isolated`
- * :c:var:`Py_LegacyWindowsFSEncodingFlag`: use :c:member:`PyConfig.legacy_windows_fs_encoding`
+ * :c:var:`Py_LegacyWindowsFSEncodingFlag`: use :c:member:`PyPreConfig.legacy_windows_fs_encoding`
* :c:var:`Py_LegacyWindowsStdioFlag`: use :c:member:`PyConfig.legacy_windows_stdio`
- * :c:var:`Py_FileSystemDefaultEncoding`: use :c:member:`PyConfig.filesystem_encoding`
- * :c:var:`Py_FileSystemDefaultEncodeErrors`: use :c:member:`PyConfig.filesystem_errors`
- * :c:var:`Py_UTF8Mode`: use :c:member:`PyPreConfig.utf8_mode` (see :c:func:`Py_PreInitialize`)
+ * :c:var:`!Py_FileSystemDefaultEncoding`: use :c:member:`PyConfig.filesystem_encoding`
+ * :c:var:`!Py_FileSystemDefaultEncodeErrors`: use :c:member:`PyConfig.filesystem_errors`
+ * :c:var:`!Py_UTF8Mode`: use :c:member:`PyPreConfig.utf8_mode` (see :c:func:`Py_PreInitialize`)
The :c:func:`Py_InitializeFromConfig` API should be used with
:c:type:`PyConfig` instead.
(Contributed by Victor Stinner in :gh:`77782`.)
-* Creating :c:data:`immutable types ` with mutable
+* Creating immutable types (:const:`Py_TPFLAGS_IMMUTABLETYPE`) with mutable
bases is deprecated and will be disabled in Python 3.14.
* The ``structmember.h`` header is deprecated, though it continues to be
@@ -1118,7 +1118,7 @@ Deprecated
:c:func:`PyErr_SetRaisedException` instead.
(Contributed by Mark Shannon in :gh:`101578`.)
-* :c:func:`PyErr_Display` is deprecated. Use :c:func:`PyErr_DisplayException`
+* :c:func:`!PyErr_Display` is deprecated. Use :c:func:`PyErr_DisplayException`
instead. (Contributed by Irit Katriel in :gh:`102755`).
@@ -1132,15 +1132,15 @@ Removed
* Legacy Unicode APIs have been removed. See :pep:`623` for detail.
- * :c:macro:`PyUnicode_WCHAR_KIND`
- * :c:func:`PyUnicode_AS_UNICODE`
- * :c:func:`PyUnicode_AsUnicode`
- * :c:func:`PyUnicode_AsUnicodeAndSize`
- * :c:func:`PyUnicode_AS_DATA`
- * :c:func:`PyUnicode_FromUnicode`
- * :c:func:`PyUnicode_GET_SIZE`
- * :c:func:`PyUnicode_GetSize`
- * :c:func:`PyUnicode_GET_DATA_SIZE`
+ * :c:macro:`!PyUnicode_WCHAR_KIND`
+ * :c:func:`!PyUnicode_AS_UNICODE`
+ * :c:func:`!PyUnicode_AsUnicode`
+ * :c:func:`!PyUnicode_AsUnicodeAndSize`
+ * :c:func:`!PyUnicode_AS_DATA`
+ * :c:func:`!PyUnicode_FromUnicode`
+ * :c:func:`!PyUnicode_GET_SIZE`
+ * :c:func:`!PyUnicode_GetSize`
+ * :c:func:`!PyUnicode_GET_DATA_SIZE`
* Remove the ``PyUnicode_InternImmortal()`` function and the
``SSTATE_INTERNED_IMMORTAL`` macro.
diff --git a/Misc/NEWS.d/3.10.0a2.rst b/Misc/NEWS.d/3.10.0a2.rst
index 61a291914f9333..061a82e90afd6b 100644
--- a/Misc/NEWS.d/3.10.0a2.rst
+++ b/Misc/NEWS.d/3.10.0a2.rst
@@ -888,7 +888,7 @@ file descriptors.
.. nonce: JUPE59
.. section: C API
-:c:data:`Py_FileSystemDefaultEncodeErrors` and :c:data:`Py_UTF8Mode` are
+:c:data:`!Py_FileSystemDefaultEncodeErrors` and :c:data:`!Py_UTF8Mode` are
available again in limited API.
..
From 387cc117fe611bbe40353e0bad09f25f99265f05 Mon Sep 17 00:00:00 2001
From: David Benjamin
Date: Fri, 24 Mar 2023 09:04:30 -0400
Subject: [PATCH 212/280] gh-100372: Use BIO_eof to detect EOF for
SSL_FILETYPE_ASN1 (GH-100373)
In PEM, we need to parse until error and then suppress `PEM_R_NO_START_LINE`, because PEM allows arbitrary leading and trailing data. DER, however, does not. Parsing until error and suppressing `ASN1_R_HEADER_TOO_LONG` doesn't quite work because that error also covers some cases that should be rejected.
Instead, check `BIO_eof` early and stop the loop that way.
Automerge-Triggered-By: GH:Yhg1s
---
Lib/test/test_ssl.py | 2 ++
.../2022-12-20-10-55-14.gh-issue-100372.utfP65.rst | 2 ++
Modules/_ssl.c | 10 ++++++----
3 files changed, 10 insertions(+), 4 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 1317efb33c2306..abf024fb89d2f3 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -1289,6 +1289,8 @@ def test_load_verify_cadata(self):
"not enough data: cadata does not contain a certificate"
):
ctx.load_verify_locations(cadata=b"broken")
+ with self.assertRaises(ssl.SSLError):
+ ctx.load_verify_locations(cadata=cacert_der + b"A")
@unittest.skipIf(Py_DEBUG_WIN32, "Avoid mixing debug/release CRT on Windows")
def test_load_dh_params(self):
diff --git a/Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst b/Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst
new file mode 100644
index 00000000000000..ec37aff5092c3a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-12-20-10-55-14.gh-issue-100372.utfP65.rst
@@ -0,0 +1,2 @@
+:meth:`ssl.SSLContext.load_verify_locations` no longer incorrectly accepts
+some cases of trailing data when parsing DER.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 36b66cdb5310c6..3fbb37332f67d3 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -3930,7 +3930,7 @@ _add_ca_certs(PySSLContext *self, const void *data, Py_ssize_t len,
{
BIO *biobuf = NULL;
X509_STORE *store;
- int retval = -1, err, loaded = 0;
+ int retval = -1, err, loaded = 0, was_bio_eof = 0;
assert(filetype == SSL_FILETYPE_ASN1 || filetype == SSL_FILETYPE_PEM);
@@ -3958,6 +3958,10 @@ _add_ca_certs(PySSLContext *self, const void *data, Py_ssize_t len,
int r;
if (filetype == SSL_FILETYPE_ASN1) {
+ if (BIO_eof(biobuf)) {
+ was_bio_eof = 1;
+ break;
+ }
cert = d2i_X509_bio(biobuf, NULL);
} else {
cert = PEM_read_bio_X509(biobuf, NULL,
@@ -3993,9 +3997,7 @@ _add_ca_certs(PySSLContext *self, const void *data, Py_ssize_t len,
}
_setSSLError(get_state_ctx(self), msg, 0, __FILE__, __LINE__);
retval = -1;
- } else if ((filetype == SSL_FILETYPE_ASN1) &&
- (ERR_GET_LIB(err) == ERR_LIB_ASN1) &&
- (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG)) {
+ } else if ((filetype == SSL_FILETYPE_ASN1) && was_bio_eof) {
/* EOF ASN1 file, not an error */
ERR_clear_error();
retval = 0;
From a11fcbbdcda48eb1f32d50c16b10496b5102703c Mon Sep 17 00:00:00 2001
From: Amin Alaee
Date: Fri, 24 Mar 2023 16:00:32 +0100
Subject: [PATCH 213/280] gh-102873: logging.LogRecord docs: improve
description of `msg` parameter (#102875)
Co-authored-by: Alex Waygood
---
Doc/library/logging.rst | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index 34e98fc2577003..22412e1a2113bb 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -813,8 +813,9 @@ wire).
:type lineno: int
:param msg: The event description message,
- which can be a %-format string with placeholders for variable data.
- :type msg: str
+ which can be a %-format string with placeholders for variable data,
+ or an arbitrary object (see :ref:`arbitrary-object-messages`).
+ :type msg: typing.Any
:param args: Variable data to merge into the *msg* argument
to obtain the event description.
From 6b4ce012340ccc44395fa34450c56c0cb2009ea3 Mon Sep 17 00:00:00 2001
From: gaogaotiantian
Date: Fri, 24 Mar 2023 13:50:06 -0700
Subject: [PATCH 214/280] gh-102980: Add tests for pdf's display, alias and
where commands (#102981)
---
Lib/test/test_pdb.py | 150 ++++++++++++++++++
...-03-23-23-25-18.gh-issue-102980.Zps4QF.rst | 1 +
2 files changed, 151 insertions(+)
create mode 100644 Misc/NEWS.d/next/Tests/2023-03-23-23-25-18.gh-issue-102980.Zps4QF.rst
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index d91bd0b2f03a0f..e96dc7fa1cf6e7 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -574,6 +574,156 @@ def test_pdb_whatis_command():
(Pdb) continue
"""
+def test_pdb_display_command():
+ """Test display command
+
+ >>> def test_function():
+ ... a = 0
+ ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+ ... a = 1
+ ... a = 2
+ ... a = 3
+ ... a = 4
+
+ >>> with PdbTestInput([ # doctest: +ELLIPSIS
+ ... 'display a',
+ ... 'n',
+ ... 'display',
+ ... 'undisplay a',
+ ... 'n',
+ ... 'display a',
+ ... 'undisplay',
+ ... 'display a < 1',
+ ... 'n',
+ ... 'continue',
+ ... ]):
+ ... test_function()
+ > (4)test_function()
+ -> a = 1
+ (Pdb) display a
+ display a: 0
+ (Pdb) n
+ > (5)test_function()
+ -> a = 2
+ display a: 1 [old: 0]
+ (Pdb) display
+ Currently displaying:
+ a: 1
+ (Pdb) undisplay a
+ (Pdb) n
+ > (6)test_function()
+ -> a = 3
+ (Pdb) display a
+ display a: 2
+ (Pdb) undisplay
+ (Pdb) display a < 1
+ display a < 1: False
+ (Pdb) n
+ > (7)test_function()
+ -> a = 4
+ (Pdb) continue
+ """
+
+def test_pdb_alias_command():
+ """Test alias command
+
+ >>> class A:
+ ... def __init__(self):
+ ... self.attr1 = 10
+ ... self.attr2 = 'str'
+ ... def method(self):
+ ... pass
+
+ >>> def test_function():
+ ... o = A()
+ ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+ ... o.method()
+
+ >>> with PdbTestInput([ # doctest: +ELLIPSIS
+ ... 'alias pi for k in %1.__dict__.keys(): print(f"%1.{k} = {%1.__dict__[k]}")',
+ ... 'alias ps pi self',
+ ... 'pi o',
+ ... 's',
+ ... 'ps',
+ ... 'continue',
+ ... ]):
+ ... test_function()
+ > (4)test_function()
+ -> o.method()
+ (Pdb) alias pi for k in %1.__dict__.keys(): print(f"%1.{k} = {%1.__dict__[k]}")
+ (Pdb) alias ps pi self
+ (Pdb) pi o
+ o.attr1 = 10
+ o.attr2 = str
+ (Pdb) s
+ --Call--
+ > (5)method()
+ -> def method(self):
+ (Pdb) ps
+ self.attr1 = 10
+ self.attr2 = str
+ (Pdb) continue
+ """
+
+def test_pdb_where_command():
+ """Test where command
+
+ >>> def g():
+ ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+
+ >>> def f():
+ ... g();
+
+ >>> def test_function():
+ ... f()
+
+ >>> with PdbTestInput([ # doctest: +ELLIPSIS
+ ... 'w',
+ ... 'where',
+ ... 'u',
+ ... 'w',
+ ... 'continue',
+ ... ]):
+ ... test_function()
+ --Return--
+ > (2)g()->None
+ -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+ (Pdb) w
+ ...
+ (8)()
+ -> test_function()
+ (2)test_function()
+ -> f()
+ (2)f()
+ -> g();
+ > (2)g()->None
+ -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+ (Pdb) where
+ ...
+ (8)()
+ -> test_function()
+ (2)test_function()
+ -> f()
+ (2)f()
+ -> g();
+ > (2)g()->None
+ -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+ (Pdb) u
+ > (2)f()
+ -> g();
+ (Pdb) w
+ ...
+ (8)()
+ -> test_function()
+ (2)test_function()
+ -> f()
+ > (2)f()
+ -> g();
+ (2)g()->None
+ -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+ (Pdb) continue
+ """
+
def test_post_mortem():
"""Test post mortem traceback debugging.
diff --git a/Misc/NEWS.d/next/Tests/2023-03-23-23-25-18.gh-issue-102980.Zps4QF.rst b/Misc/NEWS.d/next/Tests/2023-03-23-23-25-18.gh-issue-102980.Zps4QF.rst
new file mode 100644
index 00000000000000..48277367fc8755
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2023-03-23-23-25-18.gh-issue-102980.Zps4QF.rst
@@ -0,0 +1 @@
+Improve test coverage on :mod:`pdb`.
From 28122123faa9a027972978037aba9fc2e9a879b8 Mon Sep 17 00:00:00 2001
From: JakobDev
Date: Fri, 24 Mar 2023 22:52:06 +0100
Subject: [PATCH 215/280] gh-100131: Add optional delete parameter to
tempfile.TemporaryDirectory() (#100132)
Add optional delete parameter to tempfile.TemporaryDirectory().
Co-authored-by: Gregory P. Smith
---
Doc/library/tempfile.rst | 11 +++++++-
Lib/tempfile.py | 25 +++++++++++++------
Lib/test/test_tempfile.py | 6 +++++
...-12-09-11-21-38.gh-issue-100131.v863yR.rst | 1 +
4 files changed, 35 insertions(+), 8 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2022-12-09-11-21-38.gh-issue-100131.v863yR.rst
diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst
index b6d4f5dd05bbfc..61358eb76925b2 100644
--- a/Doc/library/tempfile.rst
+++ b/Doc/library/tempfile.rst
@@ -173,7 +173,7 @@ The module defines the following user-callable items:
or text *mode* was specified).
-.. class:: TemporaryDirectory(suffix=None, prefix=None, dir=None, ignore_cleanup_errors=False)
+.. class:: TemporaryDirectory(suffix=None, prefix=None, dir=None, ignore_cleanup_errors=False, *, delete=True)
This class securely creates a temporary directory using the same rules as :func:`mkdtemp`.
The resulting object can be used as a context manager (see
@@ -195,6 +195,12 @@ The module defines the following user-callable items:
(the :func:`cleanup` call, exiting the context manager, when the object
is garbage-collected or during interpreter shutdown).
+ The *delete* parameter can be used to disable cleanup of the directory tree
+ upon exiting the context. While it may seem unusual for a context manager
+ to disable the action taken when exiting the context, it can be useful during
+ debugging or when you need your cleanup behavior to be conditional based on
+ other logic.
+
.. audit-event:: tempfile.mkdtemp fullpath tempfile.TemporaryDirectory
.. versionadded:: 3.2
@@ -202,6 +208,9 @@ The module defines the following user-callable items:
.. versionchanged:: 3.10
Added *ignore_cleanup_errors* parameter.
+ .. versionchanged:: 3.12
+ Added the *delete* parameter.
+
.. function:: mkstemp(suffix=None, prefix=None, dir=None, text=False)
diff --git a/Lib/tempfile.py b/Lib/tempfile.py
index cf06092f826bcc..4732eb0efe1f76 100644
--- a/Lib/tempfile.py
+++ b/Lib/tempfile.py
@@ -850,17 +850,26 @@ class TemporaryDirectory:
...
Upon exiting the context, the directory and everything contained
- in it are removed.
+ in it are removed (unless delete=False is passed or an exception
+ is raised during cleanup and ignore_cleanup_errors is not True).
+
+ Optional Arguments:
+ suffix - A str suffix for the directory name. (see mkdtemp)
+ prefix - A str prefix for the directory name. (see mkdtemp)
+ dir - A directory to create this temp dir in. (see mkdtemp)
+ ignore_cleanup_errors - False; ignore exceptions during cleanup?
+ delete - True; whether the directory is automatically deleted.
"""
def __init__(self, suffix=None, prefix=None, dir=None,
- ignore_cleanup_errors=False):
+ ignore_cleanup_errors=False, *, delete=True):
self.name = mkdtemp(suffix, prefix, dir)
self._ignore_cleanup_errors = ignore_cleanup_errors
+ self._delete = delete
self._finalizer = _weakref.finalize(
self, self._cleanup, self.name,
warn_message="Implicitly cleaning up {!r}".format(self),
- ignore_errors=self._ignore_cleanup_errors)
+ ignore_errors=self._ignore_cleanup_errors, delete=self._delete)
@classmethod
def _rmtree(cls, name, ignore_errors=False):
@@ -894,9 +903,10 @@ def resetperms(path):
_shutil.rmtree(name, onexc=onexc)
@classmethod
- def _cleanup(cls, name, warn_message, ignore_errors=False):
- cls._rmtree(name, ignore_errors=ignore_errors)
- _warnings.warn(warn_message, ResourceWarning)
+ def _cleanup(cls, name, warn_message, ignore_errors=False, delete=True):
+ if delete:
+ cls._rmtree(name, ignore_errors=ignore_errors)
+ _warnings.warn(warn_message, ResourceWarning)
def __repr__(self):
return "<{} {!r}>".format(self.__class__.__name__, self.name)
@@ -905,7 +915,8 @@ def __enter__(self):
return self.name
def __exit__(self, exc, value, tb):
- self.cleanup()
+ if self._delete:
+ self.cleanup()
def cleanup(self):
if self._finalizer.detach() or _os.path.exists(self.name):
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
index 7c2c8de7a2e6fc..90155487cff585 100644
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -12,6 +12,7 @@
import types
import weakref
import gc
+import shutil
from unittest import mock
import unittest
@@ -1837,6 +1838,11 @@ def test_flags(self):
d.cleanup()
self.assertFalse(os.path.exists(d.name))
+ def test_delete_false(self):
+ with tempfile.TemporaryDirectory(delete=False) as working_dir:
+ pass
+ self.assertTrue(os.path.exists(working_dir))
+ shutil.rmtree(working_dir)
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2022-12-09-11-21-38.gh-issue-100131.v863yR.rst b/Misc/NEWS.d/next/Library/2022-12-09-11-21-38.gh-issue-100131.v863yR.rst
new file mode 100644
index 00000000000000..07891f2c1e9eb6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-12-09-11-21-38.gh-issue-100131.v863yR.rst
@@ -0,0 +1 @@
+Added an optional ``delete`` keyword argument to :class:`tempfile.TemporaryDirectory`.
From 6f2c3f0e387a1ef0f4b8187f6d51f1b35490f29b Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade
Date: Sat, 25 Mar 2023 09:15:02 +0200
Subject: [PATCH 216/280] gh-101100: Test only Doc/ files in nit-picky mode
(#103019)
gh-101100: Filter only Doc/ files
---
.github/workflows/doc.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index 9ec60c198eb3b6..29387d30d5a22f 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -57,6 +57,8 @@ jobs:
- name: 'Get list of changed files'
id: changed_files
uses: Ana06/get-changed-files@v2.2.0
+ with:
+ filter: "Doc/**"
- name: 'Build changed files in nit-picky mode'
continue-on-error: true
run: |
From faaaabd6318504891b3294a96314233c1e7f15be Mon Sep 17 00:00:00 2001
From: Raymond Hettinger
Date: Sat, 25 Mar 2023 02:19:20 -0500
Subject: [PATCH 217/280] GH-102833: Mention the key function in the docstrings
(GH-103009)
---
Lib/bisect.py | 8 ++++++++
Modules/_bisectmodule.c | 16 ++++++++++++----
Modules/clinic/_bisectmodule.c.h | 18 +++++++++++++-----
3 files changed, 33 insertions(+), 9 deletions(-)
diff --git a/Lib/bisect.py b/Lib/bisect.py
index d37da74f7b4055..ca6ca7240840bb 100644
--- a/Lib/bisect.py
+++ b/Lib/bisect.py
@@ -8,6 +8,8 @@ def insort_right(a, x, lo=0, hi=None, *, key=None):
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
+
+ A custom key function can be supplied to customize the sort order.
"""
if key is None:
lo = bisect_right(a, x, lo, hi)
@@ -25,6 +27,8 @@ def bisect_right(a, x, lo=0, hi=None, *, key=None):
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
+
+ A custom key function can be supplied to customize the sort order.
"""
if lo < 0:
@@ -57,6 +61,8 @@ def insort_left(a, x, lo=0, hi=None, *, key=None):
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
+
+ A custom key function can be supplied to customize the sort order.
"""
if key is None:
@@ -74,6 +80,8 @@ def bisect_left(a, x, lo=0, hi=None, *, key=None):
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
+
+ A custom key function can be supplied to customize the sort order.
"""
if lo < 0:
diff --git a/Modules/_bisectmodule.c b/Modules/_bisectmodule.c
index d3bec535ee512d..30801c2f87eee7 100644
--- a/Modules/_bisectmodule.c
+++ b/Modules/_bisectmodule.c
@@ -162,12 +162,14 @@ insert just after the rightmost x already there.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
+
+A custom key function can be supplied to customize the sort order.
[clinic start generated code]*/
static Py_ssize_t
_bisect_bisect_right_impl(PyObject *module, PyObject *a, PyObject *x,
Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
-/*[clinic end generated code: output=3a4bc09cc7c8a73d input=40fcc5afa06ae593]*/
+/*[clinic end generated code: output=3a4bc09cc7c8a73d input=43071869772dd53a]*/
{
return internal_bisect_right(a, x, lo, hi, key);
}
@@ -188,12 +190,14 @@ If x is already in a, insert it to the right of the rightmost x.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
+
+A custom key function can be supplied to customize the sort order.
[clinic start generated code]*/
static PyObject *
_bisect_insort_right_impl(PyObject *module, PyObject *a, PyObject *x,
Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
-/*[clinic end generated code: output=ac3bf26d07aedda2 input=44e1708e26b7b802]*/
+/*[clinic end generated code: output=ac3bf26d07aedda2 input=f60777d2b6ddb239]*/
{
PyObject *result, *key_x;
Py_ssize_t index;
@@ -343,12 +347,14 @@ insert just before the leftmost x already there.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
+
+A custom key function can be supplied to customize the sort order.
[clinic start generated code]*/
static Py_ssize_t
_bisect_bisect_left_impl(PyObject *module, PyObject *a, PyObject *x,
Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
-/*[clinic end generated code: output=70749d6e5cae9284 input=90dd35b50ceb05e3]*/
+/*[clinic end generated code: output=70749d6e5cae9284 input=f29c4fe7f9b797c7]*/
{
return internal_bisect_left(a, x, lo, hi, key);
}
@@ -370,12 +376,14 @@ If x is already in a, insert it to the left of the leftmost x.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
+
+A custom key function can be supplied to customize the sort order.
[clinic start generated code]*/
static PyObject *
_bisect_insort_left_impl(PyObject *module, PyObject *a, PyObject *x,
Py_ssize_t lo, Py_ssize_t hi, PyObject *key)
-/*[clinic end generated code: output=b1d33e5e7ffff11e input=3ab65d8784f585b1]*/
+/*[clinic end generated code: output=b1d33e5e7ffff11e input=0a700a82edbd472c]*/
{
PyObject *result, *key_x;
Py_ssize_t index;
diff --git a/Modules/clinic/_bisectmodule.c.h b/Modules/clinic/_bisectmodule.c.h
index bbf456e4b0f411..7944f5219b02a3 100644
--- a/Modules/clinic/_bisectmodule.c.h
+++ b/Modules/clinic/_bisectmodule.c.h
@@ -19,7 +19,9 @@ PyDoc_STRVAR(_bisect_bisect_right__doc__,
"insert just after the rightmost x already there.\n"
"\n"
"Optional args lo (default 0) and hi (default len(a)) bound the\n"
-"slice of a to be searched.");
+"slice of a to be searched.\n"
+"\n"
+"A custom key function can be supplied to customize the sort order.");
#define _BISECT_BISECT_RIGHT_METHODDEF \
{"bisect_right", _PyCFunction_CAST(_bisect_bisect_right), METH_FASTCALL|METH_KEYWORDS, _bisect_bisect_right__doc__},
@@ -125,7 +127,9 @@ PyDoc_STRVAR(_bisect_insort_right__doc__,
"If x is already in a, insert it to the right of the rightmost x.\n"
"\n"
"Optional args lo (default 0) and hi (default len(a)) bound the\n"
-"slice of a to be searched.");
+"slice of a to be searched.\n"
+"\n"
+"A custom key function can be supplied to customize the sort order.");
#define _BISECT_INSORT_RIGHT_METHODDEF \
{"insort_right", _PyCFunction_CAST(_bisect_insort_right), METH_FASTCALL|METH_KEYWORDS, _bisect_insort_right__doc__},
@@ -228,7 +232,9 @@ PyDoc_STRVAR(_bisect_bisect_left__doc__,
"insert just before the leftmost x already there.\n"
"\n"
"Optional args lo (default 0) and hi (default len(a)) bound the\n"
-"slice of a to be searched.");
+"slice of a to be searched.\n"
+"\n"
+"A custom key function can be supplied to customize the sort order.");
#define _BISECT_BISECT_LEFT_METHODDEF \
{"bisect_left", _PyCFunction_CAST(_bisect_bisect_left), METH_FASTCALL|METH_KEYWORDS, _bisect_bisect_left__doc__},
@@ -334,7 +340,9 @@ PyDoc_STRVAR(_bisect_insort_left__doc__,
"If x is already in a, insert it to the left of the leftmost x.\n"
"\n"
"Optional args lo (default 0) and hi (default len(a)) bound the\n"
-"slice of a to be searched.");
+"slice of a to be searched.\n"
+"\n"
+"A custom key function can be supplied to customize the sort order.");
#define _BISECT_INSORT_LEFT_METHODDEF \
{"insort_left", _PyCFunction_CAST(_bisect_insort_left), METH_FASTCALL|METH_KEYWORDS, _bisect_insort_left__doc__},
@@ -425,4 +433,4 @@ _bisect_insort_left(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P
exit:
return return_value;
}
-/*[clinic end generated code: output=7dc87f7af75275a1 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=5a7fa64bf9b262f3 input=a9049054013a1b77]*/
From 537a413dbc28a3fb96ce3c923b52286145f32b5d Mon Sep 17 00:00:00 2001
From: Peter Jiping Xie
Date: Sat, 25 Mar 2023 17:12:00 +0800
Subject: [PATCH 218/280] gh-103025: fix two ctypes doc issues (#103026)
---
Doc/library/ctypes.rst | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
index 8fd681286b812d..d49d702e9e7999 100644
--- a/Doc/library/ctypes.rst
+++ b/Doc/library/ctypes.rst
@@ -375,8 +375,8 @@ that they can be converted to the required C data type::
.. _ctypes-calling-variadic-functions:
-Calling varadic functions
-^^^^^^^^^^^^^^^^^^^^^^^^^
+Calling variadic functions
+^^^^^^^^^^^^^^^^^^^^^^^^^^
On a lot of platforms calling variadic functions through ctypes is exactly the same
as calling functions with a fixed number of parameters. On some platforms, and in
@@ -508,7 +508,7 @@ a string pointer and a char, and returns a pointer to a string::
If you want to avoid the ``ord("x")`` calls above, you can set the
:attr:`argtypes` attribute, and the second argument will be converted from a
-single character Python bytes object into a C char::
+single character Python bytes object into a C char:
.. doctest::
From a61efbe930ac8317f14c42c242cf54b33eec2050 Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Sat, 25 Mar 2023 22:36:38 +0300
Subject: [PATCH 219/280] gh-103027: Update `dataclass.make_dataclass`
docstring (gh-103028)
* gh-103027: Update `dataclass.make_dataclass` docstring
---
Lib/dataclasses.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index e3fd0b3e380dd8..0e04469be3ca32 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -1421,8 +1421,11 @@ class C(Base):
For the bases and namespace parameters, see the builtin type() function.
- The parameters init, repr, eq, order, unsafe_hash, and frozen are passed to
- dataclass().
+ The parameters init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only,
+ slots, and weakref_slot are passed to dataclass().
+
+ If module parameter is defined, the '__module__' attribute of the dataclass is
+ set to that value.
"""
if namespace is None:
From 86bc7345edfdc7908d525abdbabe4c9982517212 Mon Sep 17 00:00:00 2001
From: Liyang Zhang
Date: Sat, 25 Mar 2023 16:27:02 -0500
Subject: [PATCH 220/280] Fix typos in faulthandler, testcapi error messages
(#103020)
---
Modules/_testcapi/heaptype.c | 4 ++--
Modules/faulthandler.c | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c
index df2a061ed82b06..209cc182c0698d 100644
--- a/Modules/_testcapi/heaptype.c
+++ b/Modules/_testcapi/heaptype.c
@@ -174,7 +174,7 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(
}
if (res == 0) {
PyErr_SetString(PyExc_AssertionError,
- "TypeError did not inlclude expected message.");
+ "TypeError did not include expected message.");
goto finally;
}
result = Py_NewRef(Py_None);
@@ -265,7 +265,7 @@ test_type_from_ephemeral_spec(PyObject *self, PyObject *Py_UNUSED(ignored))
/* deallocate the spec (and all contents) */
- // (Explicitly ovewrite memory before freeing,
+ // (Explicitly overwrite memory before freeing,
// so bugs show themselves even without the debug allocator's help.)
memset(spec, 0xdd, sizeof(PyType_Spec));
PyMem_Del(spec);
diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c
index bfe35fed7a450a..9b4e4199cdc20a 100644
--- a/Modules/faulthandler.c
+++ b/Modules/faulthandler.c
@@ -120,7 +120,7 @@ faulthandler_get_fileno(PyObject **file_ptr)
return -1;
if (fd < 0) {
PyErr_SetString(PyExc_ValueError,
- "file is not a valid file descripter");
+ "file is not a valid file descriptor");
return -1;
}
*file_ptr = NULL;
From 6f15ebd44e5b17a3b6de9533b853089c5657083e Mon Sep 17 00:00:00 2001
From: gaogaotiantian
Date: Sat, 25 Mar 2023 14:31:45 -0700
Subject: [PATCH 221/280] Update pdb docs for arguments (#102965)
---
Doc/library/pdb.rst | 42 +++++++++++++++++++++---------------------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst
index 5988477af03abd..d80c5eebbf27a7 100644
--- a/Doc/library/pdb.rst
+++ b/Doc/library/pdb.rst
@@ -125,7 +125,7 @@ slightly different way:
Evaluate the *expression* (given as a string or a code object) under debugger
control. When :func:`runeval` returns, it returns the value of the
- expression. Otherwise this function is similar to :func:`run`.
+ *expression*. Otherwise this function is similar to :func:`run`.
.. function:: runcall(function, *args, **kwds)
@@ -178,7 +178,7 @@ access further features, you have to do this yourself:
that matches one of these patterns. [1]_
By default, Pdb sets a handler for the SIGINT signal (which is sent when the
- user presses :kbd:`Ctrl-C` on the console) when you give a ``continue`` command.
+ user presses :kbd:`Ctrl-C` on the console) when you give a :pdbcmd:`continue` command.
This allows you to break into the debugger again by pressing :kbd:`Ctrl-C`. If you
want Pdb not to touch the SIGINT handler, set *nosigint* to true.
@@ -328,9 +328,9 @@ can be overridden by the local file.
.. pdbcommand:: ignore bpnumber [count]
- Set the ignore count for the given breakpoint number. If count is omitted,
+ Set the ignore count for the given breakpoint number. If *count* is omitted,
the ignore count is set to 0. A breakpoint becomes active when the ignore
- count is zero. When non-zero, the count is decremented each time the
+ count is zero. When non-zero, the *count* is decremented each time the
breakpoint is reached and the breakpoint is not disabled and any associated
condition evaluates to true.
@@ -369,7 +369,7 @@ can be overridden by the local file.
breakpoint—which could have its own command list, leading to ambiguities about
which list to execute.
- If you use the 'silent' command in the command list, the usual message about
+ If you use the ``silent`` command in the command list, the usual message about
stopping at a breakpoint is not printed. This may be desirable for breakpoints
that are to print a specific message and then continue. If none of the other
commands print anything, you see no sign that the breakpoint was reached.
@@ -392,8 +392,8 @@ can be overridden by the local file.
Without argument, continue execution until the line with a number greater
than the current one is reached.
- With a line number, continue execution until a line with a number greater or
- equal to that is reached. In both cases, also stop when the current frame
+ With *lineno*, continue execution until a line with a number greater or
+ equal to *lineno* is reached. In both cases, also stop when the current frame
returns.
.. versionchanged:: 3.2
@@ -446,7 +446,7 @@ can be overridden by the local file.
.. pdbcommand:: p expression
- Evaluate the *expression* in the current context and print its value.
+ Evaluate *expression* in the current context and print its value.
.. note::
@@ -456,32 +456,32 @@ can be overridden by the local file.
.. pdbcommand:: pp expression
- Like the :pdbcmd:`p` command, except the value of the expression is
+ Like the :pdbcmd:`p` command, except the value of *expression* is
pretty-printed using the :mod:`pprint` module.
.. pdbcommand:: whatis expression
- Print the type of the *expression*.
+ Print the type of *expression*.
.. pdbcommand:: source expression
- Try to get source code for the given object and display it.
+ Try to get source code of *expression* and display it.
.. versionadded:: 3.2
.. pdbcommand:: display [expression]
- Display the value of the expression if it changed, each time execution stops
+ Display the value of *expression* if it changed, each time execution stops
in the current frame.
- Without expression, list all display expressions for the current frame.
+ Without *expression*, list all display expressions for the current frame.
.. versionadded:: 3.2
.. pdbcommand:: undisplay [expression]
- Do not display the expression any more in the current frame. Without
- expression, clear all display expressions for the current frame.
+ Do not display *expression* anymore in the current frame. Without
+ *expression*, clear all display expressions for the current frame.
.. versionadded:: 3.2
@@ -497,10 +497,10 @@ can be overridden by the local file.
.. pdbcommand:: alias [name [command]]
- Create an alias called *name* that executes *command*. The command must
+ Create an alias called *name* that executes *command*. The *command* must
*not* be enclosed in quotes. Replaceable parameters can be indicated by
``%1``, ``%2``, and so on, while ``%*`` is replaced by all the parameters.
- If no command is given, the current alias for *name* is shown. If no
+ If *command* is omitted, the current alias for *name* is shown. If no
arguments are given, all aliases are listed.
Aliases may be nested and can contain anything that can be legally typed at
@@ -519,7 +519,7 @@ can be overridden by the local file.
.. pdbcommand:: unalias name
- Delete the specified alias.
+ Delete the specified alias *name*.
.. pdbcommand:: ! statement
@@ -535,7 +535,7 @@ can be overridden by the local file.
.. pdbcommand:: run [args ...]
restart [args ...]
- Restart the debugged Python program. If an argument is supplied, it is split
+ Restart the debugged Python program. If *args* is supplied, it is split
with :mod:`shlex` and the result is used as the new :data:`sys.argv`.
History, breakpoints, actions and debugger options are preserved.
:pdbcmd:`restart` is an alias for :pdbcmd:`run`.
@@ -546,8 +546,8 @@ can be overridden by the local file.
.. pdbcommand:: debug code
- Enter a recursive debugger that steps through the code
- argument (which is an arbitrary expression or statement to be
+ Enter a recursive debugger that steps through *code*
+ (which is an arbitrary expression or statement to be
executed in the current environment).
.. pdbcommand:: retval
From 8e4f648fed57233b86ba3b3878fd9c0cc1f82d2a Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Sat, 25 Mar 2023 14:40:11 -0700
Subject: [PATCH 222/280] gh-98886: Fix issues with dataclass fields with
special underscore names (#102032)
This commit prefixes `__dataclass` to several things in the locals dict:
- Names like `_dflt_` (which cause trouble, see first test)
- Names like `_type_` (not known to be able to cause trouble)
- `_return_type` (not known to able to cause trouble)
- `_HAS_DEFAULT_FACTORY` (which causes trouble, see second test)
In addition, this removes `MISSING` from the locals dict. As far as I can tell, this wasn't needed even in the initial implementation of dataclasses.py (and tests on that version passed with it removed). This makes me wary :-)
This is basically a continuation of #96151, where fixing this was welcomed in https://github.com/python/cpython/pull/98143#issuecomment-1280306360
---
Lib/dataclasses.py | 19 +++++++++----------
Lib/test/test_dataclasses.py | 17 +++++++++++++++++
...3-02-18-23-03-50.gh-issue-98886.LkKGWv.rst | 1 +
3 files changed, 27 insertions(+), 10 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-02-18-23-03-50.gh-issue-98886.LkKGWv.rst
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index 0e04469be3ca32..7558287bad449e 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -432,8 +432,8 @@ def _create_fn(name, args, body, *, globals=None, locals=None,
locals = {}
return_annotation = ''
if return_type is not MISSING:
- locals['_return_type'] = return_type
- return_annotation = '->_return_type'
+ locals['__dataclass_return_type__'] = return_type
+ return_annotation = '->__dataclass_return_type__'
args = ','.join(args)
body = '\n'.join(f' {b}' for b in body)
@@ -467,14 +467,14 @@ def _field_init(f, frozen, globals, self_name, slots):
# Return the text of the line in the body of __init__ that will
# initialize this field.
- default_name = f'_dflt_{f.name}'
+ default_name = f'__dataclass_dflt_{f.name}__'
if f.default_factory is not MISSING:
if f.init:
# This field has a default factory. If a parameter is
# given, use it. If not, call the factory.
globals[default_name] = f.default_factory
value = (f'{default_name}() '
- f'if {f.name} is _HAS_DEFAULT_FACTORY '
+ f'if {f.name} is __dataclass_HAS_DEFAULT_FACTORY__ '
f'else {f.name}')
else:
# This is a field that's not in the __init__ params, but
@@ -535,11 +535,11 @@ def _init_param(f):
elif f.default is not MISSING:
# There's a default, this will be the name that's used to look
# it up.
- default = f'=_dflt_{f.name}'
+ default = f'=__dataclass_dflt_{f.name}__'
elif f.default_factory is not MISSING:
# There's a factory function. Set a marker.
- default = '=_HAS_DEFAULT_FACTORY'
- return f'{f.name}:_type_{f.name}{default}'
+ default = '=__dataclass_HAS_DEFAULT_FACTORY__'
+ return f'{f.name}:__dataclass_type_{f.name}__{default}'
def _init_fn(fields, std_fields, kw_only_fields, frozen, has_post_init,
@@ -562,10 +562,9 @@ def _init_fn(fields, std_fields, kw_only_fields, frozen, has_post_init,
raise TypeError(f'non-default argument {f.name!r} '
'follows default argument')
- locals = {f'_type_{f.name}': f.type for f in fields}
+ locals = {f'__dataclass_type_{f.name}__': f.type for f in fields}
locals.update({
- 'MISSING': MISSING,
- '_HAS_DEFAULT_FACTORY': _HAS_DEFAULT_FACTORY,
+ '__dataclass_HAS_DEFAULT_FACTORY__': _HAS_DEFAULT_FACTORY,
'__dataclass_builtins_object__': object,
})
diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py
index affd9cede19c99..6888680105c4fd 100644
--- a/Lib/test/test_dataclasses.py
+++ b/Lib/test/test_dataclasses.py
@@ -285,6 +285,23 @@ class C:
c = C(5)
self.assertEqual(c.BUILTINS, 5)
+ def test_field_with_special_single_underscore_names(self):
+ # gh-98886
+
+ @dataclass
+ class X:
+ x: int = field(default_factory=lambda: 111)
+ _dflt_x: int = field(default_factory=lambda: 222)
+
+ X()
+
+ @dataclass
+ class Y:
+ y: int = field(default_factory=lambda: 111)
+ _HAS_DEFAULT_FACTORY: int = 222
+
+ assert Y(y=222).y == 222
+
def test_field_named_like_builtin(self):
# Attribute names can shadow built-in names
# since code generation is used.
diff --git a/Misc/NEWS.d/next/Library/2023-02-18-23-03-50.gh-issue-98886.LkKGWv.rst b/Misc/NEWS.d/next/Library/2023-02-18-23-03-50.gh-issue-98886.LkKGWv.rst
new file mode 100644
index 00000000000000..64e4d6eed2f62d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-02-18-23-03-50.gh-issue-98886.LkKGWv.rst
@@ -0,0 +1 @@
+Fix issues when defining dataclasses that have fields with specific underscore names that aren't clearly reserved by :mod:`dataclasses`.
From 0831f21f2717317d71b7d6a1079ddd4f72cff161 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20G=C3=B6rgens?=
Date: Sun, 26 Mar 2023 07:38:24 +0800
Subject: [PATCH 223/280] Fix typo in _swappedbytes_ in ctypes comment
(#102773)
It's a minor typo, but it makes for a misleading comment. Let's fix it.
---
Lib/ctypes/_endian.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Lib/ctypes/_endian.py b/Lib/ctypes/_endian.py
index 34dee64b1a65a6..b5446c049bc9dc 100644
--- a/Lib/ctypes/_endian.py
+++ b/Lib/ctypes/_endian.py
@@ -37,7 +37,7 @@ class _swapped_union_meta(_swapped_meta, type(Union)): pass
################################################################
# Note: The Structure metaclass checks for the *presence* (not the
-# value!) of a _swapped_bytes_ attribute to determine the bit order in
+# value!) of a _swappedbytes_ attribute to determine the bit order in
# structures containing bit fields.
if sys.byteorder == "little":
From 0f312ec8af4132d422081f91215043d2352af72a Mon Sep 17 00:00:00 2001
From: Nikita Sobolev
Date: Mon, 27 Mar 2023 03:05:06 +0300
Subject: [PATCH 224/280] =?UTF-8?q?gh-102941:=20Fix=20"=E2=80=98subobj?=
=?UTF-8?q?=E2=80=99=20may=20be=20used=20uninitialized=20in=20this=20funct?=
=?UTF-8?q?ion"=20warning=20in=20`bytes=5Fmethods.c`=20(#102942)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Objects/bytes_methods.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c
index 6b8166385d375b..ef9e65e566ece9 100644
--- a/Objects/bytes_methods.c
+++ b/Objects/bytes_methods.c
@@ -774,7 +774,7 @@ _Py_bytes_tailmatch(const char *str, Py_ssize_t len,
{
Py_ssize_t start = 0;
Py_ssize_t end = PY_SSIZE_T_MAX;
- PyObject *subobj;
+ PyObject *subobj = NULL;
int result;
if (!stringlib_parse_args_finds(function_name, args, &subobj, &start, &end))
From c4306ba7fd60506afb8cb007cc3c35d3f85f3af0 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 27 Mar 2023 20:25:29 +0800
Subject: [PATCH 225/280] Finish merge
---
Include/internal/pycore_code.h | 1 -
Include/internal/pycore_opcode.h | 6 +-
Include/opcode.h | 36 +++---
Lib/opcode.py | 3 -
Python/bytecodes.c | 12 +-
Python/ceval.c | 5 -
Python/generated_cases.c.h | 12 +-
Python/tier2.c | 33 +-----
Tools/cases_generator/generate_cases.py | 143 ++++++++++++++++++++++++
9 files changed, 176 insertions(+), 75 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 3586c292117459..143199a4a1bb5b 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -265,7 +265,6 @@ extern _Py_CODEUNIT *_PyTier2_GenerateNextBB(
_Py_CODEUNIT *curr_executing_instr,
int jumpby,
_Py_CODEUNIT **tier1_fallback,
- char gen_bb_requires_pop,
char gen_bb_is_successor);
extern _Py_CODEUNIT *_PyTier2_LocateJumpBackwardsBB(
struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 9884184df4d3d8..155f8461021686 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -412,8 +412,6 @@ static const char *const _PyOpcode_OpName[263] = {
[BB_JUMP_IF_FLAG_UNSET] = "BB_JUMP_IF_FLAG_UNSET",
[BB_JUMP_IF_FLAG_SET] = "BB_JUMP_IF_FLAG_SET",
[BB_TEST_ITER] = "BB_TEST_ITER",
- [BB_TEST_IF_FALSE_OR_POP] = "BB_TEST_IF_FALSE_OR_POP",
- [BB_TEST_IF_TRUE_OR_POP] = "BB_TEST_IF_TRUE_OR_POP",
[BB_TEST_POP_IF_FALSE] = "BB_TEST_POP_IF_FALSE",
[BB_TEST_POP_IF_TRUE] = "BB_TEST_POP_IF_TRUE",
[BB_TEST_POP_IF_NOT_NONE] = "BB_TEST_POP_IF_NOT_NONE",
@@ -431,6 +429,8 @@ static const char *const _PyOpcode_OpName[263] = {
[STORE_FAST_BOXED_UNBOXED] = "STORE_FAST_BOXED_UNBOXED",
[STORE_FAST_UNBOXED_BOXED] = "STORE_FAST_UNBOXED_BOXED",
[STORE_FAST_UNBOXED_UNBOXED] = "STORE_FAST_UNBOXED_UNBOXED",
+ [196] = "<196>",
+ [197] = "<197>",
[198] = "<198>",
[199] = "<199>",
[200] = "<200>",
@@ -501,6 +501,8 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
+ case 196: \
+ case 197: \
case 198: \
case 199: \
case 200: \
diff --git a/Include/opcode.h b/Include/opcode.h
index ae70a273f41573..6ae5375ed0dadc 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -268,25 +268,23 @@ extern "C" {
#define BB_JUMP_IF_FLAG_UNSET 176
#define BB_JUMP_IF_FLAG_SET 177
#define BB_TEST_ITER 178
-#define BB_TEST_IF_FALSE_OR_POP 179
-#define BB_TEST_IF_TRUE_OR_POP 180
-#define BB_TEST_POP_IF_FALSE 181
-#define BB_TEST_POP_IF_TRUE 182
-#define BB_TEST_POP_IF_NOT_NONE 183
-#define BB_TEST_POP_IF_NONE 184
-#define BB_JUMP_BACKWARD_LAZY 185
-#define BINARY_CHECK_INT 186
-#define BINARY_CHECK_FLOAT 187
-#define UNARY_CHECK_FLOAT 188
-#define BINARY_OP_ADD_INT_REST 189
-#define BINARY_OP_ADD_FLOAT_UNBOXED 190
-#define POP_TOP_NO_DECREF 191
-#define UNBOX_FLOAT 192
-#define BOX_FLOAT 193
-#define LOAD_FAST_NO_INCREF 194
-#define STORE_FAST_BOXED_UNBOXED 195
-#define STORE_FAST_UNBOXED_BOXED 196
-#define STORE_FAST_UNBOXED_UNBOXED 197
+#define BB_TEST_POP_IF_FALSE 179
+#define BB_TEST_POP_IF_TRUE 180
+#define BB_TEST_POP_IF_NOT_NONE 181
+#define BB_TEST_POP_IF_NONE 182
+#define BB_JUMP_BACKWARD_LAZY 183
+#define BINARY_CHECK_INT 184
+#define BINARY_CHECK_FLOAT 185
+#define UNARY_CHECK_FLOAT 186
+#define BINARY_OP_ADD_INT_REST 187
+#define BINARY_OP_ADD_FLOAT_UNBOXED 188
+#define POP_TOP_NO_DECREF 189
+#define UNBOX_FLOAT 190
+#define BOX_FLOAT 191
+#define LOAD_FAST_NO_INCREF 192
+#define STORE_FAST_BOXED_UNBOXED 193
+#define STORE_FAST_UNBOXED_BOXED 194
+#define STORE_FAST_UNBOXED_UNBOXED 195
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index fba7f76cc279b7..5277f47b457262 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -465,9 +465,6 @@ def pseudo_op(name, op, real_ops):
# These tests correspond to the jump instructions
# FOR_ITER's null (iterator) check
'BB_TEST_ITER',
- # JUMP_IF_FALSE_OR_POP, JUMP_IF_TRUE_OR_POP
- 'BB_TEST_IF_FALSE_OR_POP',
- 'BB_TEST_IF_TRUE_OR_POP',
# POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE
'BB_TEST_POP_IF_FALSE',
'BB_TEST_POP_IF_TRUE',
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 55ef034c9a9ff8..e974186853211e 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3166,8 +3166,7 @@ dummy_func(
// Generate consequent.
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
- 0, &tier1_fallback, gen_bb_requires_pop, bb_test);
- gen_bb_requires_pop = false;
+ 0, &tier1_fallback, bb_test);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -3180,8 +3179,7 @@ dummy_func(
// Generate alternative.
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
- oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
- gen_bb_requires_pop = false;
+ oparg, &tier1_fallback, bb_test);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback + oparg;
@@ -3204,8 +3202,7 @@ dummy_func(
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
- oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
- gen_bb_requires_pop = false;
+ oparg, &tier1_fallback, bb_test);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -3237,8 +3234,7 @@ dummy_func(
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
- oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
- gen_bb_requires_pop = false;
+ oparg, &tier1_fallback, bb_test);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
diff --git a/Python/ceval.c b/Python/ceval.c
index 9d7c6e058cf8e5..87d36f7cd14e16 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -727,11 +727,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
// true = successor
// false = alternate
char bb_test = true;
- // For tier2 type propagation, handling of jump instructions with
- // runtime-dependent stack effect.
- // This flag is used to determine if the type context of a new bb
- // requires a stack element to be popped.
- char gen_bb_requires_pop = false;
/* WARNING: Because the _PyCFrame lives on the C stack,
* but can be accessed from a heap allocated object (tstate)
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 158116daaaddc4..a7c708a1a374be 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4033,8 +4033,7 @@
// Generate consequent.
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
- 0, &tier1_fallback, gen_bb_requires_pop, bb_test);
- gen_bb_requires_pop = false;
+ 0, &tier1_fallback, bb_test);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -4047,8 +4046,7 @@
// Generate alternative.
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
- oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
- gen_bb_requires_pop = false;
+ oparg, &tier1_fallback, bb_test);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback + oparg;
@@ -4071,8 +4069,7 @@
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
- oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
- gen_bb_requires_pop = false;
+ oparg, &tier1_fallback, bb_test);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
@@ -4108,8 +4105,7 @@
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
- oparg, &tier1_fallback, gen_bb_requires_pop, bb_test);
- gen_bb_requires_pop = false;
+ oparg, &tier1_fallback, bb_test);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
diff --git a/Python/tier2.c b/Python/tier2.c
index fb04660cea3f05..a2d468b12b2ec0 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -682,9 +682,7 @@ IS_JREL_OPCODE(int opcode)
switch (opcode) {
case FOR_ITER:
case JUMP_FORWARD:
- case JUMP_IF_FALSE_OR_POP:
- case JUMP_IF_TRUE_OR_POP:
- // These two tend to be after a COMPARE_AND_BRANCH.
+ // These two tend to be after a COMPARE_OP
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
case SEND:
@@ -720,7 +718,7 @@ IS_JUMP_OPCODE(int opcode)
static inline int
IS_COMPARE_OPCODE(int opcode)
{
- return opcode == COMPARE_OP || opcode == COMPARE_AND_BRANCH;
+ return opcode == COMPARE_OP;
}
static inline int
@@ -878,9 +876,9 @@ emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int bb_id)
// Converts the tier 1 branch bytecode to tier 2 branch bytecode.
// This converts sequence of instructions like
-// JUMP_IF_FALSE_OR_POP
+// POP_JUMP_IF_FALSE
// to
-// BB_TEST_IF_FALSE_OR_POP
+// BB_TEST_POP_IF_FALSE
// BB_BRANCH
// CACHE (bb_id of the current BB << 1 | is_type_branch)
static inline _Py_CODEUNIT *
@@ -903,16 +901,6 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
opcode = BB_TEST_ITER;
type_propagate(opcode, oparg, type_context, NULL);
break;
- case JUMP_IF_FALSE_OR_POP:
- opcode = BB_TEST_IF_FALSE_OR_POP;
- // This inst has conditional stack effect according to whether the branch is taken.
- // This inst sets the `gen_bb_requires_pop` flag to handle stack effect of this opcode in BB_BRANCH
- break;
- case JUMP_IF_TRUE_OR_POP:
- opcode = BB_TEST_IF_TRUE_OR_POP;
- // This inst has conditional stack effect according to whether the branch is taken.
- // This inst sets the `gen_bb_requires_pop` flag to handle stack effect of this opcode in BB_BRANCH
- break;
case POP_JUMP_IF_FALSE:
opcode = BB_TEST_POP_IF_FALSE;
type_propagate(opcode, oparg, type_context, NULL);
@@ -1241,9 +1229,6 @@ _PyTier2_Code_DetectAndEmitBB(
case RESUME:
opcode = RESUME_QUICK;
DISPATCH();
- case COMPARE_AND_BRANCH:
- opcode = specop = COMPARE_OP;
- DISPATCH();
case END_FOR:
// Assert that we are the start of a BB
assert(t2_start == write_i);
@@ -1822,7 +1807,6 @@ _PyTier2_GenerateNextBB(
_Py_CODEUNIT *curr_executing_instr,
int jumpby,
_Py_CODEUNIT **tier1_fallback,
- char gen_bb_requires_pop,
char gen_bb_is_successor)
{
PyCodeObject *co = frame->f_code;
@@ -1848,19 +1832,10 @@ _PyTier2_GenerateNextBB(
if (type_context_copy == NULL) {
return NULL;
}
- // If this flag is set, it means that either BB_TEST_IF_FALSE_OR_POP or
- // BB_TEST_IF_TRUE_OR_POP was ran and the conditional stack effect was performed
- // This means we have to pop an element from the type stack.
- if (gen_bb_requires_pop) {
- type_context_copy->type_stack_ptr--;
- }
// For type branches, they directly precede the bb branch instruction
_Py_CODEUNIT *prev_type_guard = BB_IS_TYPE_BRANCH(bb_id_tagged)
? curr_executing_instr - 1 : NULL;
if (gen_bb_is_successor && prev_type_guard != NULL) {
- // Is a type branch, so the previous instruction shouldn't be
- // one of those conditional pops.
- assert(!gen_bb_requires_pop);
// Propagate the type guard information.
#if TYPEPROP_DEBUG && defined(Py_DEBUG)
fprintf(stderr,
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index f9369f1ede897e..de704c1a59185d 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -640,6 +640,149 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
out.write_raw(extra + line.rstrip("\n") + out.postfix + "\n")
out.reset_lineno()
+ def write_typeprop(self, out: Formatter) -> None:
+ """Write one instruction's type propagation rules"""
+
+ if self.name in TYPE_PROPAGATOR_FORBIDDEN:
+ out.emit(f'fprintf(stderr, "Type propagation across `{self.name}` shouldn\'t be handled statically!\\n");')
+ out.emit("Py_UNREACHABLE();")
+ return
+
+ need_to_declare = []
+ # Stack input is used in local effect
+ if self.local_effects and \
+ isinstance(val := self.local_effects.value, TypeSrcStackInput):
+ need_to_declare.append(val.name)
+ # Stack input is used in output effect
+ for oeffect in self.output_effects:
+ if not (typ := oeffect.type_annotation): continue
+ ops = typ.ops
+ for op in ops:
+ if not isinstance(src := op.src, TypeSrcStackInput): continue
+ if oeffect.name in self.unmoved_names and oeffect.name == src.name:
+ print(
+ f"Warn: {self.name} type annotation for {oeffect.name} will be ignored "
+ "as it is unmoved")
+ continue
+ need_to_declare.append(src.name)
+
+ # Write input stack effect variable declarations and initializations
+ ieffects = list(reversed(self.input_effects))
+ usable_for_local_effect = {}
+ all_input_effect_names = {}
+ for i, ieffect in enumerate(ieffects):
+
+ if ieffect.name not in need_to_declare: continue
+
+ isize = string_effect_size(
+ list_effect_size([ieff for ieff in ieffects[: i + 1]])
+ )
+ all_input_effect_names[ieffect.name] = (ieffect, i)
+ dst = StackEffect(ieffect.name, "_Py_TYPENODE_t *")
+ if ieffect.size:
+ # TODO: Support more cases as needed
+ raise Exception("Type propagation across sized input effect not implemented")
+ elif ieffect.cond:
+ src = StackEffect(f"({ieffect.cond}) ? TYPESTACK_PEEK({isize}) : NULL", "_Py_TYPENODE_t *")
+ else:
+ usable_for_local_effect[ieffect.name] = ieffect
+ src = StackEffect(f"TYPESTACK_PEEK({isize})", "_Py_TYPENODE_t *")
+ out.declare(dst, src)
+
+ # Write localarr effect
+ if self.local_effects:
+
+ idx = self.local_effects.index
+ val = self.local_effects.value
+
+ typ_op = "TYPE_OVERWRITE"
+ dst = f"TYPELOCALS_GET({idx})"
+ match val:
+ case TypeSrcLiteral(name=valstr):
+ if valstr == "NULL":
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
+ flag = "true"
+ else:
+ src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
+ flag = "true"
+ case TypeSrcStackInput(name=valstr):
+ assert valstr in usable_for_local_effect, \
+ "`cond` and `size` stackvar not supported for localeffect"
+ src = valstr
+ flag = "false"
+ # TODO: Support more cases as needed
+ case TypeSrcConst():
+ raise Exception("Not implemented")
+ case TypeSrcLocals():
+ raise Exception("Not implemented")
+ case _:
+ typing.assert_never(val)
+ out.emit(f"{typ_op}({src}, {dst}, {flag});")
+
+ # Update stack size
+ out.stack_adjust(
+ 0,
+ [ieff for ieff in self.input_effects],
+ [oeff for oeff in self.output_effects],
+ )
+
+ # Stack effect
+ oeffects = list(reversed(self.output_effects))
+ for i, oeffect in enumerate(oeffects):
+ osize = string_effect_size(
+ list_effect_size([oeff for oeff in oeffects[: i + 1]])
+ )
+ dst = f"TYPESTACK_PEEK({osize})"
+
+ # Check if it's even used
+ if oeffect.name == UNUSED: continue
+
+ # Check if there's type info
+ if typ := oeffect.type_annotation:
+ for op in typ.ops:
+ match op.src:
+ case TypeSrcLiteral(literal=valstr):
+ if valstr == "NULL":
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
+ flag = "true"
+ else:
+ src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
+ flag = "true"
+ case TypeSrcStackInput(name=valstr):
+ assert valstr in need_to_declare
+ assert oeffect.name not in self.unmoved_names
+ src = valstr
+ flag = "false"
+ case TypeSrcConst(index=idx):
+ src = f"(_Py_TYPENODE_t *)TYPECONST_GET({idx})"
+ flag = "true"
+ case TypeSrcLocals(index=idx):
+ src = f"TYPELOCALS_GET({idx})"
+ flag = "false"
+ case _:
+ typing.assert_never(op.src)
+
+ opstr = f"{op.op}({src}, {dst}, {flag})"
+ if oeffect.cond:
+ out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
+ else:
+ out.emit(f"{opstr};")
+ continue
+
+ # Don't touch unmoved stack vars
+ if oeffect.name in self.unmoved_names:
+ continue
+
+ # Just output null
+ typ_op = "TYPE_OVERWRITE"
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
+ flag = "true"
+ opstr = f"{typ_op}({src}, {dst}, {flag})"
+ if oeffect.cond:
+ out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
+ else:
+ out.emit(f"{opstr};")
+
InstructionOrCacheEffect = Instruction | parser.CacheEffect
StackEffectMapping = list[tuple[StackEffect, StackEffect]]
From 950cf29334c14a0d1d5a9932521aadffa323e0bd Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Mon, 27 Mar 2023 20:25:37 +0800
Subject: [PATCH 226/280] fix a bug
---
Python/tier2.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index a2d468b12b2ec0..a3218905706e7d 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -863,7 +863,7 @@ emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int bb_id)
_PyOpcode_OpName[guard_opcode]);
#endif
write_curr->op.code = guard_opcode;
- write_curr->op.arg = type_guard_to_index[BINARY_CHECK_INT];
+ write_curr->op.arg = type_guard_to_index[guard_opcode];
write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
write_curr->op.arg = 0;
From 522725886d9cf34b65b7b3ad95accfc7b9116939 Mon Sep 17 00:00:00 2001
From: Julia
Date: Tue, 28 Mar 2023 02:37:19 +0800
Subject: [PATCH 227/280] Fix: Improper handling of conditional stack effect
for BB_TEST_ITER
---
Include/internal/pycore_code.h | 16 +++++++-
Python/bytecodes.c | 46 ++++++++++++-----------
Python/ceval.c | 5 +--
Python/generated_cases.c.h | 50 +++++++++++++------------
Python/tier2.c | 22 +++++++----
Python/tier2_typepropagator.c.h | 8 ++--
Tools/cases_generator/generate_cases.py | 2 +
7 files changed, 90 insertions(+), 59 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 143199a4a1bb5b..6be4e9e7a66a48 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -257,6 +257,20 @@ extern void _PyStaticCode_Fini(PyCodeObject *co);
extern int _PyStaticCode_Init(PyCodeObject *co);
/* Tier 2 interpreter */
+
+// gen_bb_is_successor:
+// true = successor
+// false = alternate
+// gen_bb_requires_pop:
+// For tier2 type propagation, handling of jump instructions with
+// runtime-dependent stack effect.
+// This flag is used to determine if the type context of a new bb
+// requires a stack element to be popped.
+#define BB_TEST(gen_bb_is_successor, gen_bb_requires_pop) \
+ ((gen_bb_is_successor << 1) + gen_bb_requires_pop)
+#define BB_TEST_IS_SUCCESSOR(bb_test) (bb_test >> 1)
+#define BB_TEST_IS_REQUIRES_POP(bb_test) (bb_test & 1)
+
extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *,
_Py_CODEUNIT *);
extern _Py_CODEUNIT *_PyTier2_GenerateNextBB(
@@ -265,7 +279,7 @@ extern _Py_CODEUNIT *_PyTier2_GenerateNextBB(
_Py_CODEUNIT *curr_executing_instr,
int jumpby,
_Py_CODEUNIT **tier1_fallback,
- char gen_bb_is_successor);
+ char bb_flag);
extern _Py_CODEUNIT *_PyTier2_LocateJumpBackwardsBB(
struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
_Py_CODEUNIT **tier1_fallback);
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index e974186853211e..38703d6c7825b6 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -124,7 +124,7 @@ dummy_func(
}
inst(LOAD_CONST, (-- value : consts[oparg])) {
- value = GETITEM(consts, oparg);
+ value = GETITEM(frame->f_code->co_consts, oparg);
Py_INCREF(value);
}
@@ -307,18 +307,21 @@ dummy_func(
right_unboxed: {<<= PyFloat_Type, PyRawFloat_Type})
) {
assert(cframe.use_tracing == 0);
- bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
- left_unboxed = (bb_test
+ char is_successor = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ bb_test = BB_TEST(is_successor ? 1 : 0, 0);
+
+ left_unboxed = (is_successor
? *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)))
: left);
- right_unboxed = (bb_test
+ right_unboxed = (is_successor
? *((PyObject **)(&(((PyFloatObject *)right)->ob_fval)))
: right);
}
inst(UNARY_CHECK_FLOAT, (arg, unused[oparg] -- arg : PyFloat_Type, unused[oparg])) {
assert(cframe.use_tracing == 0);
- bb_test = PyFloat_CheckExact(arg);
+ char is_successor = PyFloat_CheckExact(arg);
+ bb_test = BB_TEST(is_successor ? 1 : 0, 0);
}
inst(BINARY_OP_ADD_FLOAT_UNBOXED, (left, right -- sum : PyRawFloat_Type)) {
@@ -346,7 +349,8 @@ dummy_func(
inst(BINARY_CHECK_INT, (left, right -- left : <<= PyLong_Type, right : <<= PyLong_Type)) {
assert(cframe.use_tracing == 0);
- bb_test = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ char is_successor = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ bb_test = BB_TEST(is_successor ? 1 : 0, 0);
}
u_inst(BINARY_OP_ADD_INT_REST, (left, right -- sum : PyLong_Type)) {
@@ -1934,17 +1938,17 @@ dummy_func(
inst(BB_TEST_POP_IF_FALSE, (cond -- )) {
if (Py_IsTrue(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = true;
+ bb_test = BB_TEST(1, 0);
}
else if (Py_IsFalse(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = false;
+ bb_test = BB_TEST(0, 0);
}
else {
int err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err == 0) {
- bb_test = false;
+ bb_test = BB_TEST(0, 0);
}
else {
ERROR_IF(err < 0, error);
@@ -1975,17 +1979,17 @@ dummy_func(
inst(BB_TEST_POP_IF_TRUE, (cond -- )) {
if (Py_IsFalse(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = true;
+ bb_test = BB_TEST(1, 0);;
}
else if (Py_IsTrue(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = false;
+ bb_test = BB_TEST(0, 0);;
}
else {
int err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err > 0) {
- bb_test = false;
+ bb_test = BB_TEST(0, 0);;
}
else {
ERROR_IF(err < 0, error);
@@ -2007,11 +2011,11 @@ dummy_func(
inst(BB_TEST_POP_IF_NOT_NONE, (value -- )) {
if (!Py_IsNone(value)) {
Py_DECREF(value);
- bb_test = false;
+ bb_test = BB_TEST(0, 0);;
}
else {
_Py_DECREF_NO_DEALLOC(value);
- bb_test = true;
+ bb_test = BB_TEST(1, 0);;
}
}
@@ -2175,11 +2179,11 @@ dummy_func(
/* iterator ended normally */
Py_DECREF(iter);
STACK_SHRINK(1);
- bb_test = false;
+ bb_test = BB_TEST(0, 1);
JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
DISPATCH();
}
- bb_test = true;
+ bb_test = BB_TEST(1, 0);
}
inst(FOR_ITER_LIST, (unused/1, iter -- iter, next)) {
@@ -3160,7 +3164,7 @@ dummy_func(
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- if (bb_test) {
+ if (BB_TEST_IS_SUCCESSOR(bb_test)) {
// Rewrite self
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_UNSET);
// Generate consequent.
@@ -3194,7 +3198,7 @@ dummy_func(
}
inst(BB_BRANCH_IF_FLAG_UNSET, (unused/1 --)) {
- if (!bb_test) {
+ if (!BB_TEST_IS_SUCCESSOR(bb_test)) {
_Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
@@ -3216,7 +3220,7 @@ dummy_func(
}
inst(BB_JUMP_IF_FLAG_UNSET, (unused/1 --)) {
- if (!bb_test) {
+ if (!BB_TEST_IS_SUCCESSOR(bb_test)) {
JUMPBY(oparg);
DISPATCH();
}
@@ -3224,7 +3228,7 @@ dummy_func(
}
inst(BB_BRANCH_IF_FLAG_SET, (unused/1 --)) {
- if (bb_test) {
+ if (BB_TEST_IS_SUCCESSOR(bb_test)) {
_Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
@@ -3248,7 +3252,7 @@ dummy_func(
}
inst(BB_JUMP_IF_FLAG_SET, (unused/1 --)) {
- if (bb_test) {
+ if (BB_TEST_IS_SUCCESSOR(bb_test)) {
JUMPBY(oparg);
DISPATCH();
}
diff --git a/Python/ceval.c b/Python/ceval.c
index 87d36f7cd14e16..e09966d449f23e 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -724,9 +724,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_PyCFrame cframe;
_PyInterpreterFrame entry_frame;
PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions.
- // true = successor
- // false = alternate
- char bb_test = true;
+
+ char bb_test = BB_TEST(0, 0);
/* WARNING: Because the _PyCFrame lives on the C stack,
* but can be accessed from a heap allocated object (tstate)
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index a7c708a1a374be..f9b0b8716cb20b 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -75,7 +75,7 @@
TARGET(LOAD_CONST) {
PREDICTED(LOAD_CONST);
PyObject *value;
- value = GETITEM(consts, oparg);
+ value = GETITEM(frame->f_code->co_consts, oparg);
Py_INCREF(value);
STACK_GROW(1);
stack_pointer[-1] = value;
@@ -149,7 +149,7 @@
oparg = (next_instr++)->op.arg;
{
PyObject *value;
- value = GETITEM(consts, oparg);
+ value = GETITEM(frame->f_code->co_consts, oparg);
Py_INCREF(value);
_tmp_1 = value;
}
@@ -198,7 +198,7 @@
PyObject *_tmp_2;
{
PyObject *value;
- value = GETITEM(consts, oparg);
+ value = GETITEM(frame->f_code->co_consts, oparg);
Py_INCREF(value);
_tmp_2 = value;
}
@@ -433,11 +433,13 @@
PyObject *left_unboxed;
PyObject *right_unboxed;
assert(cframe.use_tracing == 0);
- bb_test = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
- left_unboxed = (bb_test
+ char is_successor = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ bb_test = BB_TEST(is_successor ? 1 : 0, 0);
+
+ left_unboxed = (is_successor
? *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)))
: left);
- right_unboxed = (bb_test
+ right_unboxed = (is_successor
? *((PyObject **)(&(((PyFloatObject *)right)->ob_fval)))
: right);
stack_pointer[-1] = right_unboxed;
@@ -448,7 +450,8 @@
TARGET(UNARY_CHECK_FLOAT) {
PyObject *arg = stack_pointer[-(1 + oparg)];
assert(cframe.use_tracing == 0);
- bb_test = PyFloat_CheckExact(arg);
+ char is_successor = PyFloat_CheckExact(arg);
+ bb_test = BB_TEST(is_successor ? 1 : 0, 0);
DISPATCH();
}
@@ -500,7 +503,8 @@
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
assert(cframe.use_tracing == 0);
- bb_test = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ char is_successor = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
+ bb_test = BB_TEST(is_successor ? 1 : 0, 0);
DISPATCH();
}
@@ -2515,17 +2519,17 @@
PyObject *cond = stack_pointer[-1];
if (Py_IsTrue(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = true;
+ bb_test = BB_TEST(1, 0);
}
else if (Py_IsFalse(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = false;
+ bb_test = BB_TEST(0, 0);
}
else {
int err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err == 0) {
- bb_test = false;
+ bb_test = BB_TEST(0, 0);
}
else {
if (err < 0) goto pop_1_error;
@@ -2562,17 +2566,17 @@
PyObject *cond = stack_pointer[-1];
if (Py_IsFalse(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = true;
+ bb_test = BB_TEST(1, 0);;
}
else if (Py_IsTrue(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = false;
+ bb_test = BB_TEST(0, 0);;
}
else {
int err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err > 0) {
- bb_test = false;
+ bb_test = BB_TEST(0, 0);;
}
else {
if (err < 0) goto pop_1_error;
@@ -2599,11 +2603,11 @@
PyObject *value = stack_pointer[-1];
if (!Py_IsNone(value)) {
Py_DECREF(value);
- bb_test = false;
+ bb_test = BB_TEST(0, 0);;
}
else {
_Py_DECREF_NO_DEALLOC(value);
- bb_test = true;
+ bb_test = BB_TEST(1, 0);;
}
STACK_SHRINK(1);
DISPATCH();
@@ -2806,11 +2810,11 @@
/* iterator ended normally */
Py_DECREF(iter);
STACK_SHRINK(1);
- bb_test = false;
+ bb_test = BB_TEST(0, 1);
JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
DISPATCH();
}
- bb_test = true;
+ bb_test = BB_TEST(1, 0);
STACK_GROW(1);
stack_pointer[-1] = next;
next_instr += 1;
@@ -4027,7 +4031,7 @@
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- if (bb_test) {
+ if (BB_TEST_IS_SUCCESSOR(bb_test)) {
// Rewrite self
_py_set_opcode(next_instr - 1, BB_BRANCH_IF_FLAG_UNSET);
// Generate consequent.
@@ -4061,7 +4065,7 @@
}
TARGET(BB_BRANCH_IF_FLAG_UNSET) {
- if (!bb_test) {
+ if (!BB_TEST_IS_SUCCESSOR(bb_test)) {
_Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
@@ -4085,7 +4089,7 @@
}
TARGET(BB_JUMP_IF_FLAG_UNSET) {
- if (!bb_test) {
+ if (!BB_TEST_IS_SUCCESSOR(bb_test)) {
JUMPBY(oparg);
DISPATCH();
}
@@ -4095,7 +4099,7 @@
}
TARGET(BB_BRANCH_IF_FLAG_SET) {
- if (bb_test) {
+ if (BB_TEST_IS_SUCCESSOR(bb_test)) {
_Py_CODEUNIT *curr = next_instr - 1;
_Py_CODEUNIT *t2_nextinstr = NULL;
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
@@ -4121,7 +4125,7 @@
}
TARGET(BB_JUMP_IF_FLAG_SET) {
- if (bb_test) {
+ if (BB_TEST_IS_SUCCESSOR(bb_test)) {
JUMPBY(oparg);
DISPATCH();
}
diff --git a/Python/tier2.c b/Python/tier2.c
index a3218905706e7d..2180fa7981f266 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -375,7 +375,7 @@ __type_propagate_TYPE_OVERWRITE(
}
static void
-__type_stack_shrink(_PyTier2TypeContext *type_context, _Py_TYPENODE_t **type_stackptr, int idx)
+__type_stack_shrink(_Py_TYPENODE_t **type_stackptr, int idx)
{
// TODO:
// If we don't touch the stack elements
@@ -491,7 +491,7 @@ type_propagate(
#define STACK_GROW(idx) *type_stackptr += (idx)
// Stack shrinking has to NULL the nodes
-#define STACK_SHRINK(idx) __type_stack_shrink(type_context, type_stackptr, (idx))
+#define STACK_SHRINK(idx) __type_stack_shrink(type_stackptr, (idx))
#if TYPEPROP_DEBUG
fprintf(stderr, " [-] Type stack bef: %llu\n", (uint64_t)(*type_stackptr - type_stack));
@@ -577,7 +577,7 @@ _PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_reque
// Note: overallocate
Py_ssize_t new_size = sizeof(_PyTier2BBSpace) + (curr->water_level + space_requested) * 2;
#if BB_DEBUG
- fprintf(stderr, "Allocating new BB of size %lld\n", new_size);
+ fprintf(stderr, "Allocating new BB of size %lld\n", (int64_t)new_size);
#endif
// @TODO We can't Realloc, we actually need to do the linked list method.
_PyTier2BBSpace *new_space = PyMem_Realloc(curr, new_size);
@@ -899,7 +899,8 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
break;
case FOR_ITER:
opcode = BB_TEST_ITER;
- type_propagate(opcode, oparg, type_context, NULL);
+ // This inst has conditional stack effect according to whether the branch is taken.
+ // This inst sets the `gen_bb_requires_pop` flag to handle stack effect of this opcode in BB_BRANCH
break;
case POP_JUMP_IF_FALSE:
opcode = BB_TEST_POP_IF_FALSE;
@@ -1225,6 +1226,9 @@ _PyTier2_Code_DetectAndEmitBB(
// We need to check whether we can eliminate the guard based on the current type context.
dispatch_opcode:
+#if TYPEPROP_DEBUG
+ fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
+#endif
switch (opcode) {
case RESUME:
opcode = RESUME_QUICK;
@@ -1378,7 +1382,7 @@ _PyTier2_Code_DetectAndEmitBB(
write_i = rebox_stack(write_i, starting_type_context, 4);
DISPATCH();
default:
-#if BB_DEBUG || TYPEPROP_DEBUG
+#if BB_DEBUG && !TYPEPROP_DEBUG
fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
#endif
if (IS_BACKWARDS_JUMP_TARGET(co, curr)) {
@@ -1807,7 +1811,7 @@ _PyTier2_GenerateNextBB(
_Py_CODEUNIT *curr_executing_instr,
int jumpby,
_Py_CODEUNIT **tier1_fallback,
- char gen_bb_is_successor)
+ char bb_flag)
{
PyCodeObject *co = frame->f_code;
assert(co->_tier2_info != NULL);
@@ -1832,10 +1836,14 @@ _PyTier2_GenerateNextBB(
if (type_context_copy == NULL) {
return NULL;
}
+
+ if (BB_TEST_IS_REQUIRES_POP(bb_flag)) {
+ __type_stack_shrink(&(type_context_copy->type_stack_ptr), 1);
+ }
// For type branches, they directly precede the bb branch instruction
_Py_CODEUNIT *prev_type_guard = BB_IS_TYPE_BRANCH(bb_id_tagged)
? curr_executing_instr - 1 : NULL;
- if (gen_bb_is_successor && prev_type_guard != NULL) {
+ if (BB_TEST_IS_SUCCESSOR(bb_flag) && prev_type_guard != NULL) {
// Propagate the type guard information.
#if TYPEPROP_DEBUG && defined(Py_DEBUG)
fprintf(stderr,
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 46987cc6a07ed2..4e2b95a615d360 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -790,14 +790,14 @@
}
TARGET(FOR_ITER) {
- STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ fprintf(stderr, "Type propagation across `FOR_ITER` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
TARGET(BB_TEST_ITER) {
- STACK_GROW(1);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ fprintf(stderr, "Type propagation across `BB_TEST_ITER` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index de704c1a59185d..f299d234993e55 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -55,6 +55,8 @@
# Type propagator shouldn't see these
"JUMP_IF_FALSE_OR_POP",
"JUMP_IF_TRUE_OR_POP",
+ "BB_TEST_ITER",
+ "FOR_ITER",
"SEND",
"SEND_GEN",
"YIELD_VALUE",
From 11609bf4e8314be42afb793b024f12e79900d68b Mon Sep 17 00:00:00 2001
From: Julia
Date: Tue, 28 Mar 2023 02:54:36 +0800
Subject: [PATCH 228/280] Fix: Buggy macros
---
Include/internal/pycore_code.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 6be4e9e7a66a48..e5e0fb971a67b5 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -267,9 +267,9 @@ extern int _PyStaticCode_Init(PyCodeObject *co);
// This flag is used to determine if the type context of a new bb
// requires a stack element to be popped.
#define BB_TEST(gen_bb_is_successor, gen_bb_requires_pop) \
- ((gen_bb_is_successor << 1) + gen_bb_requires_pop)
-#define BB_TEST_IS_SUCCESSOR(bb_test) (bb_test >> 1)
-#define BB_TEST_IS_REQUIRES_POP(bb_test) (bb_test & 1)
+ (((gen_bb_is_successor) << 1) + (gen_bb_requires_pop))
+#define BB_TEST_IS_SUCCESSOR(bb_test) ((bb_test) >> 1)
+#define BB_TEST_IS_REQUIRES_POP(bb_test) ((bb_test) & 1)
extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *,
_Py_CODEUNIT *);
From 20f919ffbfb2a4368a10cff45c8a2d275cc11641 Mon Sep 17 00:00:00 2001
From: Julia
Date: Tue, 28 Mar 2023 03:10:48 +0800
Subject: [PATCH 229/280] Fix: Added missing opcode
---
Python/bytecodes.c | 21 ++++++++++++++++-----
Python/generated_cases.c.h | 24 +++++++++++++++++++-----
Python/opcode_metadata.h | 5 +++++
Python/tier2_typepropagator.c.h | 5 +++++
4 files changed, 45 insertions(+), 10 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 38703d6c7825b6..5d3d23532a401e 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -1979,17 +1979,17 @@ dummy_func(
inst(BB_TEST_POP_IF_TRUE, (cond -- )) {
if (Py_IsFalse(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = BB_TEST(1, 0);;
+ bb_test = BB_TEST(1, 0);
}
else if (Py_IsTrue(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = BB_TEST(0, 0);;
+ bb_test = BB_TEST(0, 0);
}
else {
int err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err > 0) {
- bb_test = BB_TEST(0, 0);;
+ bb_test = BB_TEST(0, 0);
}
else {
ERROR_IF(err < 0, error);
@@ -2011,11 +2011,11 @@ dummy_func(
inst(BB_TEST_POP_IF_NOT_NONE, (value -- )) {
if (!Py_IsNone(value)) {
Py_DECREF(value);
- bb_test = BB_TEST(0, 0);;
+ bb_test = BB_TEST(0, 0);
}
else {
_Py_DECREF_NO_DEALLOC(value);
- bb_test = BB_TEST(1, 0);;
+ bb_test = BB_TEST(1, 0);
}
}
@@ -2029,6 +2029,17 @@ dummy_func(
}
}
+ inst(BB_TEST_POP_IF_NONE, (value -- )) {
+ if (Py_IsNone(value)) {
+ Py_DECREF(value);
+ bb_test = BB_TEST(0, 0);
+ }
+ else {
+ _Py_DECREF_NO_DEALLOC(value);
+ bb_test = BB_TEST(1, 0);
+ }
+ }
+
inst(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
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index f9b0b8716cb20b..829ff126293b34 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2566,17 +2566,17 @@
PyObject *cond = stack_pointer[-1];
if (Py_IsFalse(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = BB_TEST(1, 0);;
+ bb_test = BB_TEST(1, 0);
}
else if (Py_IsTrue(cond)) {
_Py_DECREF_NO_DEALLOC(cond);
- bb_test = BB_TEST(0, 0);;
+ bb_test = BB_TEST(0, 0);
}
else {
int err = PyObject_IsTrue(cond);
Py_DECREF(cond);
if (err > 0) {
- bb_test = BB_TEST(0, 0);;
+ bb_test = BB_TEST(0, 0);
}
else {
if (err < 0) goto pop_1_error;
@@ -2603,11 +2603,11 @@
PyObject *value = stack_pointer[-1];
if (!Py_IsNone(value)) {
Py_DECREF(value);
- bb_test = BB_TEST(0, 0);;
+ bb_test = BB_TEST(0, 0);
}
else {
_Py_DECREF_NO_DEALLOC(value);
- bb_test = BB_TEST(1, 0);;
+ bb_test = BB_TEST(1, 0);
}
STACK_SHRINK(1);
DISPATCH();
@@ -2626,6 +2626,20 @@
DISPATCH();
}
+ TARGET(BB_TEST_POP_IF_NONE) {
+ PyObject *value = stack_pointer[-1];
+ if (Py_IsNone(value)) {
+ Py_DECREF(value);
+ bb_test = BB_TEST(0, 0);
+ }
+ else {
+ _Py_DECREF_NO_DEALLOC(value);
+ bb_test = BB_TEST(1, 0);
+ }
+ STACK_SHRINK(1);
+ DISPATCH();
+ }
+
TARGET(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
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index bc3aeba527d13b..6f77f7c5d4e67c 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -279,6 +279,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 1;
case POP_JUMP_IF_NONE:
return 1;
+ case BB_TEST_POP_IF_NONE:
+ return 1;
case JUMP_BACKWARD_NO_INTERRUPT:
return 0;
case GET_LEN:
@@ -673,6 +675,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case POP_JUMP_IF_NONE:
return 0;
+ case BB_TEST_POP_IF_NONE:
+ return 0;
case JUMP_BACKWARD_NO_INTERRUPT:
return 0;
case GET_LEN:
@@ -936,6 +940,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IB },
[BB_TEST_POP_IF_NOT_NONE] = { true, INSTR_FMT_IX },
[POP_JUMP_IF_NONE] = { true, INSTR_FMT_IB },
+ [BB_TEST_POP_IF_NONE] = { true, INSTR_FMT_IX },
[JUMP_BACKWARD_NO_INTERRUPT] = { true, INSTR_FMT_IB },
[GET_LEN] = { true, INSTR_FMT_IX },
[MATCH_CLASS] = { true, INSTR_FMT_IB },
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 4e2b95a615d360..96b514cd096b1e 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -745,6 +745,11 @@
break;
}
+ TARGET(BB_TEST_POP_IF_NONE) {
+ STACK_SHRINK(1);
+ break;
+ }
+
TARGET(JUMP_BACKWARD_NO_INTERRUPT) {
break;
}
From b57665e2734018ae76b222c75c5e655ba53cd997 Mon Sep 17 00:00:00 2001
From: Julia
Date: Tue, 28 Mar 2023 03:34:50 +0800
Subject: [PATCH 230/280] Fix: Bug in EXTENDED_ARG handling for
_PyTier2_RewriteForwardJump
---
Python/tier2.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 2180fa7981f266..5cb0edc9401836 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -77,6 +77,8 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
#if TYPEPROP_DEBUG
fprintf(stderr, " [*] Copying type context\n");
+ static void print_typestack(const _PyTier2TypeContext * type_context);
+ print_typestack(type_context);
#endif
_Py_TYPENODE_t *orig_type_locals = type_context->type_locals;
@@ -1962,10 +1964,10 @@ _PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target)
assert(oparg <= 0xFFFF);
if (requires_extended) {
_py_set_opcode(write_curr, EXTENDED_ARG);
- write_curr->op.arg = (oparg >> 8) & 0xFF;
- write_curr++;
// -1 to oparg because now the jump instruction moves one unit forward.
oparg--;
+ write_curr->op.arg = (oparg >> 8) & 0xFF;
+ write_curr++;
}
_py_set_opcode(write_curr,
branch == BB_BRANCH_IF_FLAG_SET ? BB_JUMP_IF_FLAG_SET : BB_JUMP_IF_FLAG_UNSET);
From 88c27eb4379e8f4187f7eeee0473bad29bb9d4e4 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 28 Mar 2023 10:45:25 +0800
Subject: [PATCH 231/280] Apply review comments
---
Include/internal/pycore_code.h | 2 +-
Python/bytecodes.c | 6 +++---
Python/generated_cases.c.h | 6 +++---
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index e5e0fb971a67b5..70c6be0038d6d7 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -267,7 +267,7 @@ extern int _PyStaticCode_Init(PyCodeObject *co);
// This flag is used to determine if the type context of a new bb
// requires a stack element to be popped.
#define BB_TEST(gen_bb_is_successor, gen_bb_requires_pop) \
- (((gen_bb_is_successor) << 1) + (gen_bb_requires_pop))
+ (((gen_bb_is_successor) << 1) | (gen_bb_requires_pop))
#define BB_TEST_IS_SUCCESSOR(bb_test) ((bb_test) >> 1)
#define BB_TEST_IS_REQUIRES_POP(bb_test) ((bb_test) & 1)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 5d3d23532a401e..b86e4c98adfa69 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -308,7 +308,7 @@ dummy_func(
) {
assert(cframe.use_tracing == 0);
char is_successor = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
- bb_test = BB_TEST(is_successor ? 1 : 0, 0);
+ bb_test = BB_TEST(is_successor, 0);
left_unboxed = (is_successor
? *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)))
@@ -321,7 +321,7 @@ dummy_func(
inst(UNARY_CHECK_FLOAT, (arg, unused[oparg] -- arg : PyFloat_Type, unused[oparg])) {
assert(cframe.use_tracing == 0);
char is_successor = PyFloat_CheckExact(arg);
- bb_test = BB_TEST(is_successor ? 1 : 0, 0);
+ bb_test = BB_TEST(is_successor, 0);
}
inst(BINARY_OP_ADD_FLOAT_UNBOXED, (left, right -- sum : PyRawFloat_Type)) {
@@ -350,7 +350,7 @@ dummy_func(
inst(BINARY_CHECK_INT, (left, right -- left : <<= PyLong_Type, right : <<= PyLong_Type)) {
assert(cframe.use_tracing == 0);
char is_successor = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
- bb_test = BB_TEST(is_successor ? 1 : 0, 0);
+ bb_test = BB_TEST(is_successor, 0);
}
u_inst(BINARY_OP_ADD_INT_REST, (left, right -- sum : PyLong_Type)) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 829ff126293b34..7a2121a20b2144 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -434,7 +434,7 @@
PyObject *right_unboxed;
assert(cframe.use_tracing == 0);
char is_successor = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
- bb_test = BB_TEST(is_successor ? 1 : 0, 0);
+ bb_test = BB_TEST(is_successor, 0);
left_unboxed = (is_successor
? *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)))
@@ -451,7 +451,7 @@
PyObject *arg = stack_pointer[-(1 + oparg)];
assert(cframe.use_tracing == 0);
char is_successor = PyFloat_CheckExact(arg);
- bb_test = BB_TEST(is_successor ? 1 : 0, 0);
+ bb_test = BB_TEST(is_successor, 0);
DISPATCH();
}
@@ -504,7 +504,7 @@
PyObject *left = stack_pointer[-2];
assert(cframe.use_tracing == 0);
char is_successor = PyLong_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
- bb_test = BB_TEST(is_successor ? 1 : 0, 0);
+ bb_test = BB_TEST(is_successor, 0);
DISPATCH();
}
From d6a7b9792d4e2839123b536039ac04b1f8653a32 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 28 Mar 2023 18:15:13 +0800
Subject: [PATCH 232/280] Some cleanup
---
Include/internal/pycore_code.h | 2 +-
Python/bytecodes.c | 2 --
Python/ceval_macros.h | 2 +-
Python/tier2.c | 12 +++---------
4 files changed, 5 insertions(+), 13 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 70c6be0038d6d7..55c8bd9cd4227a 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -269,7 +269,7 @@ extern int _PyStaticCode_Init(PyCodeObject *co);
#define BB_TEST(gen_bb_is_successor, gen_bb_requires_pop) \
(((gen_bb_is_successor) << 1) | (gen_bb_requires_pop))
#define BB_TEST_IS_SUCCESSOR(bb_test) ((bb_test) >> 1)
-#define BB_TEST_IS_REQUIRES_POP(bb_test) ((bb_test) & 1)
+#define BB_TEST_IS_REQUIRES_POP(bb_test) ((bb_test) & 1)
extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *,
_Py_CODEUNIT *);
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index b86e4c98adfa69..0aa87a4a93d16a 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3245,8 +3245,6 @@ dummy_func(
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- // @TODO: Rewrite TEST intruction above to a JUMP above..
-
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
oparg, &tier1_fallback, bb_test);
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 2a7f35e8e43ece..1d3f15735a031a 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -68,7 +68,7 @@
lastopcode = op; \
} while (0)
#else
-#define INSTRUCTION_START(op) (frame->prev_instr = next_instr++)
+#define INSTRUCTION_START(op) (frame->prev_instr = next_instr++); /* fprintf(stderr, "%d: %s\n", INSTR_OFFSET(), _PyOpcode_OpName[op]); */
#endif
#if USE_COMPUTED_GOTOS
diff --git a/Python/tier2.c b/Python/tier2.c
index 5cb0edc9401836..f285c1305b4138 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1942,13 +1942,6 @@ BB_TEST_POP_IF_TRUE
BB_JUMP_IF_FLAG_SET
CACHE (will be converted to EXTENDED_ARGS if we need a bigger jump)
-Some instructions will be special since they need CACHE entries. E.g. FOR_ITER
-
-BB_TEST_ITER
-CACHE
-BB_BRANCH_IF_FLAG_SET
-CACHE
-
Backwards jumps are handled by another function.
*/
@@ -1958,6 +1951,7 @@ _PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target)
_Py_CODEUNIT *write_curr = bb_branch;
// -1 because the PC is auto incremented
int oparg = (int)(target - bb_branch - 1);
+ assert(oparg > 0);
int branch = _Py_OPCODE(*bb_branch);
assert(branch == BB_BRANCH_IF_FLAG_SET || branch == BB_BRANCH_IF_FLAG_UNSET);
bool requires_extended = oparg > 0xFF;
@@ -1985,14 +1979,14 @@ _PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target)
Before:
EXTENDED_ARG/NOP
-JUMP_BACKWARD_LAZY
+BB_JUMP_BACKWARD_LAZY
CACHE
After:
EXTENDED_ARG (if needed, else NOP)
-JUMP_BACKWARD_LAZY
+JUMP_BACKWARD_QUICK
END_FOR
*/
From 4b7137c8242bb13b6ac83dedbb4c5ecdf2731555 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 28 Mar 2023 18:26:00 +0800
Subject: [PATCH 233/280] Revert "Merge remote-tracking branch 'upstream/main'
into tier2_interpreter_no_separate_eval_no_tracer_contiguous_bbs"
This reverts commit fae3104c8de52a13fced2e425e978cd0db5f5057, reversing
changes made to d6a7b9792d4e2839123b536039ac04b1f8653a32.
---
Lib/dis.py | 7 +----
Lib/enum.py | 15 +++------
Lib/pdb.py | 31 ++++++++-----------
Lib/tarfile.py | 6 ++--
Lib/test/test_dis.py | 29 -----------------
Lib/test/test_enum.py | 18 +----------
Lib/test/test_pdb.py | 6 ----
Lib/test/test_tarfile.py | 7 -----
...3-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst | 3 --
...-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst | 2 --
...-03-26-20-54-57.gh-issue-103046.xBlA2l.rst | 1 -
...-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst | 1 -
Objects/unicodeobject.c | 9 +-----
Python/bytecodes.c | 2 +-
Python/generated_cases.c.h | 4 ++-
15 files changed, 28 insertions(+), 113 deletions(-)
delete mode 100644 Misc/NEWS.d/next/Library/2023-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst
delete mode 100644 Misc/NEWS.d/next/Library/2023-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst
delete mode 100644 Misc/NEWS.d/next/Library/2023-03-26-20-54-57.gh-issue-103046.xBlA2l.rst
delete mode 100644 Misc/NEWS.d/next/Library/2023-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst
diff --git a/Lib/dis.py b/Lib/dis.py
index accace858fab99..844acd56f84ff0 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -605,12 +605,7 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
instr.offset > 0)
if new_source_line:
print(file=file)
- if show_caches:
- is_current_instr = instr.offset == lasti
- else:
- # Each CACHE takes 2 bytes
- is_current_instr = instr.offset <= lasti \
- <= instr.offset + 2 * _inline_cache_entries[_deoptop(instr.opcode)]
+ is_current_instr = instr.offset == lasti
print(instr._disassemble(lineno_width, is_current_instr, offset_width),
file=file)
if exception_entries:
diff --git a/Lib/enum.py b/Lib/enum.py
index a9fb50606290fb..ba927662a43b13 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -518,13 +518,8 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
#
# adjust the sunders
_order_ = classdict.pop('_order_', None)
- _gnv = classdict.get('_generate_next_value_')
- if _gnv is not None and type(_gnv) is not staticmethod:
- _gnv = staticmethod(_gnv)
# convert to normal dict
classdict = dict(classdict.items())
- if _gnv is not None:
- classdict['_generate_next_value_'] = _gnv
#
# data type of member and the controlling Enum class
member_type, first_enum = metacls._get_mixins_(cls, bases)
@@ -928,7 +923,7 @@ def _convert_(cls, name, module, filter, source=None, *, boundary=None, as_globa
def _check_for_existing_members_(mcls, class_name, bases):
for chain in bases:
for base in chain.__mro__:
- if isinstance(base, EnumType) and base._member_names_:
+ if issubclass(base, Enum) and base._member_names_:
raise TypeError(
" cannot extend %r"
% (class_name, base)
@@ -947,7 +942,7 @@ def _get_mixins_(mcls, class_name, bases):
# ensure final parent class is an Enum derivative, find any concrete
# data type, and check that Enum has no members
first_enum = bases[-1]
- if not isinstance(first_enum, EnumType):
+ if not issubclass(first_enum, Enum):
raise TypeError("new enumerations should be created as "
"`EnumName([mixin_type, ...] [data_type,] enum_type)`")
member_type = mcls._find_data_type_(class_name, bases) or object
@@ -959,7 +954,7 @@ def _find_data_repr_(mcls, class_name, bases):
for base in chain.__mro__:
if base is object:
continue
- elif isinstance(base, EnumType):
+ elif issubclass(base, Enum):
# if we hit an Enum, use it's _value_repr_
return base._value_repr_
elif '__repr__' in base.__dict__:
@@ -985,12 +980,12 @@ def _find_data_type_(mcls, class_name, bases):
base_chain.add(base)
if base is object:
continue
- elif isinstance(base, EnumType):
+ elif issubclass(base, Enum):
if base._member_type_ is not object:
data_types.add(base._member_type_)
break
elif '__new__' in base.__dict__ or '__init__' in base.__dict__:
- if isinstance(base, EnumType):
+ if issubclass(base, Enum):
continue
data_types.add(candidate or base)
break
diff --git a/Lib/pdb.py b/Lib/pdb.py
index d402de1192f9e2..3543f53282db15 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -399,7 +399,7 @@ def preloop(self):
displaying = self.displaying.get(self.curframe)
if displaying:
for expr, oldvalue in displaying.items():
- newvalue, _ = self._getval_except(expr)
+ newvalue = self._getval_except(expr)
# check for identity first; this prevents custom __eq__ to
# be called at every loop, and also prevents instances whose
# fields are changed to be displayed
@@ -1246,12 +1246,13 @@ def _getval(self, arg):
def _getval_except(self, arg, frame=None):
try:
if frame is None:
- return eval(arg, self.curframe.f_globals, self.curframe_locals), None
+ return eval(arg, self.curframe.f_globals, self.curframe_locals)
else:
- return eval(arg, frame.f_globals, frame.f_locals), None
- except BaseException as exc:
- err = traceback.format_exception_only(exc)[-1].strip()
- return _rstr('** raised %s **' % err), exc
+ return eval(arg, frame.f_globals, frame.f_locals)
+ except:
+ exc_info = sys.exc_info()[:2]
+ err = traceback.format_exception_only(*exc_info)[-1].strip()
+ return _rstr('** raised %s **' % err)
def _error_exc(self):
exc_info = sys.exc_info()[:2]
@@ -1436,19 +1437,13 @@ def do_display(self, arg):
Without expression, list all display expressions for the current frame.
"""
if not arg:
- if self.displaying:
- self.message('Currently displaying:')
- for item in self.displaying.get(self.curframe, {}).items():
- self.message('%s: %r' % item)
- else:
- self.message('No expression is being displayed')
+ self.message('Currently displaying:')
+ for item in self.displaying.get(self.curframe, {}).items():
+ self.message('%s: %r' % item)
else:
- val, exc = self._getval_except(arg)
- if isinstance(exc, SyntaxError):
- self.message('Unable to display %s: %r' % (arg, val))
- else:
- self.displaying.setdefault(self.curframe, {})[arg] = val
- self.message('display %s: %r' % (arg, val))
+ val = self._getval_except(arg)
+ self.displaying.setdefault(self.curframe, {})[arg] = val
+ self.message('display %s: %r' % (arg, val))
complete_display = _complete_expression
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index b733195c9c5636..d686435d90ad1b 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -601,12 +601,12 @@ class _FileInFile(object):
object.
"""
- def __init__(self, fileobj, offset, size, name, blockinfo=None):
+ def __init__(self, fileobj, offset, size, blockinfo=None):
self.fileobj = fileobj
self.offset = offset
self.size = size
self.position = 0
- self.name = name
+ self.name = getattr(fileobj, "name", None)
self.closed = False
if blockinfo is None:
@@ -703,7 +703,7 @@ class ExFileObject(io.BufferedReader):
def __init__(self, tarfile, tarinfo):
fileobj = _FileInFile(tarfile.fileobj, tarinfo.offset_data,
- tarinfo.size, tarinfo.name, tarinfo.sparse)
+ tarinfo.size, tarinfo.sparse)
super().__init__(fileobj)
#class ExFileObject
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index 7cad8d1bfe13ae..ed66b362b08080 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -1198,35 +1198,6 @@ def test_show_caches(self):
self.assertEqual(caches.count(""), empty_caches)
self.assertEqual(len(caches), total_caches)
- @cpython_only
- def test_show_currinstr_with_cache(self):
- """
- Make sure that with lasti pointing to CACHE, it still shows the current
- line correctly
- """
- def f():
- print(a)
- # The code above should generate a LOAD_GLOBAL which has CACHE instr after
- # However, this might change in the future. So we explicitly try to find
- # a CACHE entry in the instructions. If we can't do that, fail the test
-
- for inst in dis.get_instructions(f, show_caches=True):
- if inst.opname == "CACHE":
- op_offset = inst.offset - 2
- cache_offset = inst.offset
- break
- else:
- self.fail("Can't find a CACHE entry in the function provided to do the test")
-
- assem_op = self.get_disassembly(f.__code__, lasti=op_offset, wrapper=False)
- assem_cache = self.get_disassembly(f.__code__, lasti=cache_offset, wrapper=False)
-
- # Make sure --> exists and points to the correct offset
- self.assertRegex(assem_op, fr"-->\s+{op_offset}")
- # Make sure when lasti points to cache, it shows the same disassembly
- self.assertEqual(assem_op, assem_cache)
-
-
class DisWithFileTests(DisTests):
# Run the tests again, using the file arg instead of print
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index bea19542705dc4..bb163c46481a42 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -270,17 +270,6 @@ class NewSubEnum(NewBaseEnum):
first = auto()
self.NewSubEnum = NewSubEnum
#
- class LazyGNV(self.enum_type):
- def _generate_next_value_(name, start, last, values):
- pass
- self.LazyGNV = LazyGNV
- #
- class BusyGNV(self.enum_type):
- @staticmethod
- def _generate_next_value_(name, start, last, values):
- pass
- self.BusyGNV = BusyGNV
- #
self.is_flag = False
self.names = ['first', 'second', 'third']
if issubclass(MainEnum, StrEnum):
@@ -477,12 +466,6 @@ def test_enum_in_enum_out(self):
Main = self.MainEnum
self.assertIs(Main(Main.first), Main.first)
- def test_gnv_is_static(self):
- lazy = self.LazyGNV
- busy = self.BusyGNV
- self.assertTrue(type(lazy.__dict__['_generate_next_value_']) is staticmethod)
- self.assertTrue(type(busy.__dict__['_generate_next_value_']) is staticmethod)
-
def test_hash(self):
MainEnum = self.MainEnum
mapping = {}
@@ -1386,6 +1369,7 @@ def repr(self):
class Huh(MyStr, MyInt, Enum):
One = 1
+
def test_pickle_enum(self):
if isinstance(Stooges, Exception):
raise Stooges
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index ae9c5d73e2daa7..e96dc7fa1cf6e7 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -586,8 +586,6 @@ def test_pdb_display_command():
... a = 4
>>> with PdbTestInput([ # doctest: +ELLIPSIS
- ... 'display +',
- ... 'display',
... 'display a',
... 'n',
... 'display',
@@ -602,10 +600,6 @@ def test_pdb_display_command():
... test_function()
> (4)test_function()
-> a = 1
- (Pdb) display +
- Unable to display +: ** raised SyntaxError: invalid syntax **
- (Pdb) display
- No expression is being displayed
(Pdb) display a
display a: 0
(Pdb) n
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 39f6f499c818ef..75b60e9a50e78a 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -479,13 +479,6 @@ def test_length_zero_header(self):
with tarfile.open(support.findfile('recursion.tar')) as tar:
pass
- def test_extractfile_name(self):
- # gh-74468: TarFile.name must name a file, not a parent archive.
- file = self.tar.getmember('ustar/regtype')
- with self.tar.extractfile(file) as fobj:
- self.assertEqual(fobj.name, 'ustar/regtype')
-
-
class MiscReadTestBase(CommonReadTest):
def requires_name_attribute(self):
pass
diff --git a/Misc/NEWS.d/next/Library/2023-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst b/Misc/NEWS.d/next/Library/2023-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst
deleted file mode 100644
index 8fad551f3a4ece..00000000000000
--- a/Misc/NEWS.d/next/Library/2023-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-Attribute name of the extracted :mod:`tarfile` file object now holds
-filename of itself rather than of the archive it is contained in.
-Patch by Oleg Iarygin.
diff --git a/Misc/NEWS.d/next/Library/2023-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst b/Misc/NEWS.d/next/Library/2023-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst
deleted file mode 100644
index e7958f6f002055..00000000000000
--- a/Misc/NEWS.d/next/Library/2023-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-It's no longer possible to register expressions to display in
-:class:`~pdb.Pdb` that raise :exc:`SyntaxError`. Patch by Tian Gao.
diff --git a/Misc/NEWS.d/next/Library/2023-03-26-20-54-57.gh-issue-103046.xBlA2l.rst b/Misc/NEWS.d/next/Library/2023-03-26-20-54-57.gh-issue-103046.xBlA2l.rst
deleted file mode 100644
index f9bd0a10056ef1..00000000000000
--- a/Misc/NEWS.d/next/Library/2023-03-26-20-54-57.gh-issue-103046.xBlA2l.rst
+++ /dev/null
@@ -1 +0,0 @@
-Display current line label correctly in :mod:`dis` when ``show_caches`` is False and ``lasti`` points to a CACHE entry.
diff --git a/Misc/NEWS.d/next/Library/2023-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst b/Misc/NEWS.d/next/Library/2023-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst
deleted file mode 100644
index c892d8376503f8..00000000000000
--- a/Misc/NEWS.d/next/Library/2023-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst
+++ /dev/null
@@ -1 +0,0 @@
-Ensure final ``_generate_next_value_`` is a ``staticmethod``.
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 4cb035ce6f9641..891a65576ee29b 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -14609,14 +14609,7 @@ PyUnicode_InternInPlace(PyObject **p)
}
PyObject *interned = get_interned_dict();
- assert(interned != NULL);
-
- PyObject *t = PyDict_SetDefault(interned, s, s);
- if (t == NULL) {
- PyErr_Clear();
- return;
- }
-
+ PyObject *t = _Py_AddToGlobalDict(interned, s, s);
if (t != s) {
if (t != NULL) {
Py_SETREF(*p, Py_NewRef(t));
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index b3076ee9142239..0aa87a4a93d16a 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2035,7 +2035,7 @@ dummy_func(
bb_test = BB_TEST(0, 0);
}
else {
- DECREF_INPUTS();
+ _Py_DECREF_NO_DEALLOC(value);
bb_test = BB_TEST(1, 0);
}
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 6874b03ac9b8bc..7a2121a20b2144 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2633,7 +2633,7 @@
bb_test = BB_TEST(0, 0);
}
else {
- Py_DECREF(value);
+ _Py_DECREF_NO_DEALLOC(value);
bb_test = BB_TEST(1, 0);
}
STACK_SHRINK(1);
@@ -4119,6 +4119,8 @@
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
+ // @TODO: Rewrite TEST intruction above to a JUMP above..
+
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
oparg, &tier1_fallback, bb_test);
From 632305b0673d9ce943a8ce7beb11505513ae46fe Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 28 Mar 2023 18:31:40 +0800
Subject: [PATCH 234/280] Fix bad merge
---
Include/internal/pycore_global_objects.h | 4 -
Include/internal/pycore_interp.h | 1 -
Include/internal/pycore_pystate.h | 5 -
Include/internal/pycore_runtime_init.h | 3 -
Include/internal/pycore_unicodeobject.h | 1 -
Lib/enum.py | 17 +-
Lib/pdb.py | 31 +--
Lib/tarfile.py | 6 +-
Lib/test/test_dis.py | 29 +++
Lib/test/test_enum.py | 18 +-
Lib/test/test_pdb.py | 6 +
Lib/test/test_tarfile.py | 7 +
...3-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst | 3 +
...-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst | 2 +
...-03-26-20-54-57.gh-issue-103046.xBlA2l.rst | 1 +
...-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst | 1 +
Objects/unicodeobject.c | 13 +-
Python/pylifecycle.c | 4 -
Python/pystate.c | 204 ++----------------
19 files changed, 127 insertions(+), 229 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2023-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst
create mode 100644 Misc/NEWS.d/next/Library/2023-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst
create mode 100644 Misc/NEWS.d/next/Library/2023-03-26-20-54-57.gh-issue-103046.xBlA2l.rst
create mode 100644 Misc/NEWS.d/next/Library/2023-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst
diff --git a/Include/internal/pycore_global_objects.h b/Include/internal/pycore_global_objects.h
index 858321d67df481..9957da1fc5f22a 100644
--- a/Include/internal/pycore_global_objects.h
+++ b/Include/internal/pycore_global_objects.h
@@ -28,10 +28,6 @@ extern "C" {
struct _Py_cached_objects {
PyObject *interned_strings;
- /* A thread state tied to the main interpreter,
- used exclusively for when a global object (e.g. interned strings)
- is resized (i.e. deallocated + allocated) from an arbitrary thread. */
- PyThreadState main_tstate;
};
#define _Py_GLOBAL_OBJECT(NAME) \
diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h
index fcdf8d2f2c8e15..1f2c0db2eb5f27 100644
--- a/Include/internal/pycore_interp.h
+++ b/Include/internal/pycore_interp.h
@@ -164,7 +164,6 @@ struct _is {
struct _Py_interp_cached_objects cached_objects;
struct _Py_interp_static_objects static_objects;
-
/* The following fields are here to avoid allocation during init.
The data is exposed through PyInterpreterState pointer fields.
These fields should not be accessed directly outside of init.
diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h
index f159b516e66b18..7046ec8d9adaaf 100644
--- a/Include/internal/pycore_pystate.h
+++ b/Include/internal/pycore_pystate.h
@@ -127,11 +127,6 @@ PyAPI_FUNC(void) _PyThreadState_Init(
PyThreadState *tstate);
PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate);
-extern void _PyThreadState_InitDetached(PyThreadState *, PyInterpreterState *);
-extern void _PyThreadState_ClearDetached(PyThreadState *);
-
-extern PyObject * _Py_AddToGlobalDict(PyObject *, PyObject *, PyObject *);
-
static inline void
_PyThreadState_UpdateTracingState(PyThreadState *tstate)
diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h
index fd358b2da6ccff..7cfa7c0c02494a 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -59,9 +59,6 @@ extern PyTypeObject _PyExc_MemoryError;
.types = { \
.next_version_tag = 1, \
}, \
- .cached_objects = { \
- .main_tstate = _PyThreadState_INIT, \
- }, \
.static_objects = { \
.singletons = { \
.small_ints = _Py_small_ints_INIT, \
diff --git a/Include/internal/pycore_unicodeobject.h b/Include/internal/pycore_unicodeobject.h
index ed4feb603d6f38..19faceebf1d8ee 100644
--- a/Include/internal/pycore_unicodeobject.h
+++ b/Include/internal/pycore_unicodeobject.h
@@ -34,7 +34,6 @@ struct _Py_unicode_runtime_ids {
struct _Py_unicode_runtime_state {
struct _Py_unicode_runtime_ids ids;
- /* The interned dict is at _PyRuntime.cached_objects.interned_strings. */
};
/* fs_codec.encoding is initialized to NULL.
diff --git a/Lib/enum.py b/Lib/enum.py
index ba927662a43b13..8c77117ce6acb4 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -518,8 +518,13 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
#
# adjust the sunders
_order_ = classdict.pop('_order_', None)
+ _gnv = classdict.get('_generate_next_value_')
+ if _gnv is not None and type(_gnv) is not staticmethod:
+ _gnv = staticmethod(_gnv)
# convert to normal dict
classdict = dict(classdict.items())
+ if _gnv is not None:
+ classdict['_generate_next_value_'] = _gnv
#
# data type of member and the controlling Enum class
member_type, first_enum = metacls._get_mixins_(cls, bases)
@@ -923,7 +928,7 @@ def _convert_(cls, name, module, filter, source=None, *, boundary=None, as_globa
def _check_for_existing_members_(mcls, class_name, bases):
for chain in bases:
for base in chain.__mro__:
- if issubclass(base, Enum) and base._member_names_:
+ if isinstance(base, EnumType) and base._member_names_:
raise TypeError(
" cannot extend %r"
% (class_name, base)
@@ -942,7 +947,7 @@ def _get_mixins_(mcls, class_name, bases):
# ensure final parent class is an Enum derivative, find any concrete
# data type, and check that Enum has no members
first_enum = bases[-1]
- if not issubclass(first_enum, Enum):
+ if not isinstance(first_enum, EnumType):
raise TypeError("new enumerations should be created as "
"`EnumName([mixin_type, ...] [data_type,] enum_type)`")
member_type = mcls._find_data_type_(class_name, bases) or object
@@ -954,7 +959,7 @@ def _find_data_repr_(mcls, class_name, bases):
for base in chain.__mro__:
if base is object:
continue
- elif issubclass(base, Enum):
+ elif isinstance(base, EnumType):
# if we hit an Enum, use it's _value_repr_
return base._value_repr_
elif '__repr__' in base.__dict__:
@@ -980,12 +985,12 @@ def _find_data_type_(mcls, class_name, bases):
base_chain.add(base)
if base is object:
continue
- elif issubclass(base, Enum):
+ elif isinstance(base, EnumType):
if base._member_type_ is not object:
data_types.add(base._member_type_)
break
elif '__new__' in base.__dict__ or '__init__' in base.__dict__:
- if issubclass(base, Enum):
+ if isinstance(base, EnumType):
continue
data_types.add(candidate or base)
break
@@ -1186,8 +1191,6 @@ def _missing_(cls, value):
return None
def __repr__(self):
- if not isinstance(self, Enum):
- return repr(self)
v_repr = self.__class__._value_repr_ or repr
return "<%s.%s: %s>" % (self.__class__.__name__, self._name_, v_repr(self._value_))
diff --git a/Lib/pdb.py b/Lib/pdb.py
index 3543f53282db15..d402de1192f9e2 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -399,7 +399,7 @@ def preloop(self):
displaying = self.displaying.get(self.curframe)
if displaying:
for expr, oldvalue in displaying.items():
- newvalue = self._getval_except(expr)
+ newvalue, _ = self._getval_except(expr)
# check for identity first; this prevents custom __eq__ to
# be called at every loop, and also prevents instances whose
# fields are changed to be displayed
@@ -1246,13 +1246,12 @@ def _getval(self, arg):
def _getval_except(self, arg, frame=None):
try:
if frame is None:
- return eval(arg, self.curframe.f_globals, self.curframe_locals)
+ return eval(arg, self.curframe.f_globals, self.curframe_locals), None
else:
- return eval(arg, frame.f_globals, frame.f_locals)
- except:
- exc_info = sys.exc_info()[:2]
- err = traceback.format_exception_only(*exc_info)[-1].strip()
- return _rstr('** raised %s **' % err)
+ return eval(arg, frame.f_globals, frame.f_locals), None
+ except BaseException as exc:
+ err = traceback.format_exception_only(exc)[-1].strip()
+ return _rstr('** raised %s **' % err), exc
def _error_exc(self):
exc_info = sys.exc_info()[:2]
@@ -1437,13 +1436,19 @@ def do_display(self, arg):
Without expression, list all display expressions for the current frame.
"""
if not arg:
- self.message('Currently displaying:')
- for item in self.displaying.get(self.curframe, {}).items():
- self.message('%s: %r' % item)
+ if self.displaying:
+ self.message('Currently displaying:')
+ for item in self.displaying.get(self.curframe, {}).items():
+ self.message('%s: %r' % item)
+ else:
+ self.message('No expression is being displayed')
else:
- val = self._getval_except(arg)
- self.displaying.setdefault(self.curframe, {})[arg] = val
- self.message('display %s: %r' % (arg, val))
+ val, exc = self._getval_except(arg)
+ if isinstance(exc, SyntaxError):
+ self.message('Unable to display %s: %r' % (arg, val))
+ else:
+ self.displaying.setdefault(self.curframe, {})[arg] = val
+ self.message('display %s: %r' % (arg, val))
complete_display = _complete_expression
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index d686435d90ad1b..b733195c9c5636 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -601,12 +601,12 @@ class _FileInFile(object):
object.
"""
- def __init__(self, fileobj, offset, size, blockinfo=None):
+ def __init__(self, fileobj, offset, size, name, blockinfo=None):
self.fileobj = fileobj
self.offset = offset
self.size = size
self.position = 0
- self.name = getattr(fileobj, "name", None)
+ self.name = name
self.closed = False
if blockinfo is None:
@@ -703,7 +703,7 @@ class ExFileObject(io.BufferedReader):
def __init__(self, tarfile, tarinfo):
fileobj = _FileInFile(tarfile.fileobj, tarinfo.offset_data,
- tarinfo.size, tarinfo.sparse)
+ tarinfo.size, tarinfo.name, tarinfo.sparse)
super().__init__(fileobj)
#class ExFileObject
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index ed66b362b08080..7cad8d1bfe13ae 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -1198,6 +1198,35 @@ def test_show_caches(self):
self.assertEqual(caches.count(""), empty_caches)
self.assertEqual(len(caches), total_caches)
+ @cpython_only
+ def test_show_currinstr_with_cache(self):
+ """
+ Make sure that with lasti pointing to CACHE, it still shows the current
+ line correctly
+ """
+ def f():
+ print(a)
+ # The code above should generate a LOAD_GLOBAL which has CACHE instr after
+ # However, this might change in the future. So we explicitly try to find
+ # a CACHE entry in the instructions. If we can't do that, fail the test
+
+ for inst in dis.get_instructions(f, show_caches=True):
+ if inst.opname == "CACHE":
+ op_offset = inst.offset - 2
+ cache_offset = inst.offset
+ break
+ else:
+ self.fail("Can't find a CACHE entry in the function provided to do the test")
+
+ assem_op = self.get_disassembly(f.__code__, lasti=op_offset, wrapper=False)
+ assem_cache = self.get_disassembly(f.__code__, lasti=cache_offset, wrapper=False)
+
+ # Make sure --> exists and points to the correct offset
+ self.assertRegex(assem_op, fr"-->\s+{op_offset}")
+ # Make sure when lasti points to cache, it shows the same disassembly
+ self.assertEqual(assem_op, assem_cache)
+
+
class DisWithFileTests(DisTests):
# Run the tests again, using the file arg instead of print
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py
index bb163c46481a42..bea19542705dc4 100644
--- a/Lib/test/test_enum.py
+++ b/Lib/test/test_enum.py
@@ -270,6 +270,17 @@ class NewSubEnum(NewBaseEnum):
first = auto()
self.NewSubEnum = NewSubEnum
#
+ class LazyGNV(self.enum_type):
+ def _generate_next_value_(name, start, last, values):
+ pass
+ self.LazyGNV = LazyGNV
+ #
+ class BusyGNV(self.enum_type):
+ @staticmethod
+ def _generate_next_value_(name, start, last, values):
+ pass
+ self.BusyGNV = BusyGNV
+ #
self.is_flag = False
self.names = ['first', 'second', 'third']
if issubclass(MainEnum, StrEnum):
@@ -466,6 +477,12 @@ def test_enum_in_enum_out(self):
Main = self.MainEnum
self.assertIs(Main(Main.first), Main.first)
+ def test_gnv_is_static(self):
+ lazy = self.LazyGNV
+ busy = self.BusyGNV
+ self.assertTrue(type(lazy.__dict__['_generate_next_value_']) is staticmethod)
+ self.assertTrue(type(busy.__dict__['_generate_next_value_']) is staticmethod)
+
def test_hash(self):
MainEnum = self.MainEnum
mapping = {}
@@ -1369,7 +1386,6 @@ def repr(self):
class Huh(MyStr, MyInt, Enum):
One = 1
-
def test_pickle_enum(self):
if isinstance(Stooges, Exception):
raise Stooges
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index e96dc7fa1cf6e7..ae9c5d73e2daa7 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -586,6 +586,8 @@ def test_pdb_display_command():
... a = 4
>>> with PdbTestInput([ # doctest: +ELLIPSIS
+ ... 'display +',
+ ... 'display',
... 'display a',
... 'n',
... 'display',
@@ -600,6 +602,10 @@ def test_pdb_display_command():
... test_function()
> (4)test_function()
-> a = 1
+ (Pdb) display +
+ Unable to display +: ** raised SyntaxError: invalid syntax **
+ (Pdb) display
+ No expression is being displayed
(Pdb) display a
display a: 0
(Pdb) n
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 75b60e9a50e78a..39f6f499c818ef 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -479,6 +479,13 @@ def test_length_zero_header(self):
with tarfile.open(support.findfile('recursion.tar')) as tar:
pass
+ def test_extractfile_name(self):
+ # gh-74468: TarFile.name must name a file, not a parent archive.
+ file = self.tar.getmember('ustar/regtype')
+ with self.tar.extractfile(file) as fobj:
+ self.assertEqual(fobj.name, 'ustar/regtype')
+
+
class MiscReadTestBase(CommonReadTest):
def requires_name_attribute(self):
pass
diff --git a/Misc/NEWS.d/next/Library/2023-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst b/Misc/NEWS.d/next/Library/2023-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst
new file mode 100644
index 00000000000000..8fad551f3a4ece
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-04-20-58-29.gh-issue-74468.Ac5Ew_.rst
@@ -0,0 +1,3 @@
+Attribute name of the extracted :mod:`tarfile` file object now holds
+filename of itself rather than of the archive it is contained in.
+Patch by Oleg Iarygin.
diff --git a/Misc/NEWS.d/next/Library/2023-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst b/Misc/NEWS.d/next/Library/2023-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst
new file mode 100644
index 00000000000000..e7958f6f002055
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-25-02-08-05.gh-issue-103023.Qfn7Hl.rst
@@ -0,0 +1,2 @@
+It's no longer possible to register expressions to display in
+:class:`~pdb.Pdb` that raise :exc:`SyntaxError`. Patch by Tian Gao.
diff --git a/Misc/NEWS.d/next/Library/2023-03-26-20-54-57.gh-issue-103046.xBlA2l.rst b/Misc/NEWS.d/next/Library/2023-03-26-20-54-57.gh-issue-103046.xBlA2l.rst
new file mode 100644
index 00000000000000..f9bd0a10056ef1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-26-20-54-57.gh-issue-103046.xBlA2l.rst
@@ -0,0 +1 @@
+Display current line label correctly in :mod:`dis` when ``show_caches`` is False and ``lasti`` points to a CACHE entry.
diff --git a/Misc/NEWS.d/next/Library/2023-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst b/Misc/NEWS.d/next/Library/2023-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst
new file mode 100644
index 00000000000000..c892d8376503f8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-03-27-15-01-16.gh-issue-103056.-Efh5Q.rst
@@ -0,0 +1 @@
+Ensure final ``_generate_next_value_`` is a ``staticmethod``.
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 891a65576ee29b..b9fb53147b9b51 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -14609,11 +14609,16 @@ PyUnicode_InternInPlace(PyObject **p)
}
PyObject *interned = get_interned_dict();
- PyObject *t = _Py_AddToGlobalDict(interned, s, s);
+ assert(interned != NULL);
+
+ PyObject *t = PyDict_SetDefault(interned, s, s);
+ if (t == NULL) {
+ PyErr_Clear();
+ return;
+ }
+
if (t != s) {
- if (t != NULL) {
- Py_SETREF(*p, Py_NewRef(t));
- }
+ Py_SETREF(*p, Py_NewRef(t));
return;
}
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 5d7f8621833040..8110d94ba17526 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -636,8 +636,6 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
return status;
}
- _PyThreadState_InitDetached(&runtime->cached_objects.main_tstate, interp);
-
*tstate_p = tstate;
return _PyStatus_OK();
}
@@ -1934,8 +1932,6 @@ Py_FinalizeEx(void)
// XXX Do this sooner during finalization.
// XXX Ensure finalizer errors are handled properly.
- _PyThreadState_ClearDetached(&runtime->cached_objects.main_tstate);
-
finalize_interp_clear(tstate);
finalize_interp_delete(tstate->interp);
diff --git a/Python/pystate.c b/Python/pystate.c
index 394b12d24065f2..b17efdbefd124c 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -565,124 +565,6 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
#endif
-//---------------
-// global objects
-//---------------
-
-/* The global objects thread state is meant to be used in a very limited
- way and should not be used to actually run any Python code. */
-
-static PyThreadState *
-bind_global_objects_state(_PyRuntimeState *runtime)
-{
- PyThreadState *main_tstate = &runtime->cached_objects.main_tstate;
-
- bind_tstate(main_tstate);
- /* Unlike _PyThreadState_Bind(), we do not modify gilstate TSS. */
-
- return main_tstate;
-}
-
-static void
-unbind_global_objects_state(_PyRuntimeState *runtime)
-{
- PyThreadState *main_tstate = &runtime->cached_objects.main_tstate;
- assert(tstate_is_alive(main_tstate));
- assert(!main_tstate->_status.active);
- assert(gilstate_tss_get(runtime) != main_tstate);
-
- unbind_tstate(main_tstate);
-
- /* This thread state may be bound/unbound repeatedly,
- so we must erase evidence that it was ever bound (or unbound). */
- main_tstate->_status.bound = 0;
- main_tstate->_status.unbound = 0;
-
- /* We must fully unlink the thread state from any OS thread,
- to allow it to be bound more than once. */
- main_tstate->thread_id = 0;
-#ifdef PY_HAVE_THREAD_NATIVE_ID
- main_tstate->native_thread_id = 0;
-#endif
-}
-
-static inline void
-acquire_global_objects_lock(_PyRuntimeState *runtime)
-{
- /* For now we can rely on the GIL, so we don't actually
- acquire a global lock here. */
- assert(current_fast_get(runtime) != NULL);
-}
-
-static inline void
-release_global_objects_lock(_PyRuntimeState *runtime)
-{
- /* For now we can rely on the GIL, so we don't actually
- release a global lock here. */
- assert(current_fast_get(runtime) != NULL);
-}
-
-PyObject *
-_Py_AddToGlobalDict(PyObject *dict, PyObject *key, PyObject *value)
-{
- assert(dict != NULL);
- assert(PyDict_CheckExact(dict));
-
- /* All global objects are stored in _PyRuntime
- and owned by the main interpreter. */
- _PyRuntimeState *runtime = &_PyRuntime;
- PyThreadState *curts = current_fast_get(runtime);
- PyInterpreterState *interp = curts->interp;
- assert(interp != NULL); // The GIL must be held.
-
- /* Due to interpreter isolation we must hold a global lock,
- starting at this point and ending before we return.
- Note that the operations in this function are very fucused
- and we should not expect any reentrancy. */
- acquire_global_objects_lock(runtime);
-
- /* Swap to the main interpreter, if necessary. */
- PyThreadState *oldts = NULL;
- if (!_Py_IsMainInterpreter(interp)) {
- PyThreadState *main_tstate = bind_global_objects_state(runtime);
-
- oldts = _PyThreadState_Swap(runtime, main_tstate);
- assert(oldts != NULL);
- assert(!_Py_IsMainInterpreter(oldts->interp));
-
- /* The limitations of the global objects thread state apply
- from this point to the point we swap back to oldts. */
- }
-
- /* This might trigger a resize, which is why we must "acquire"
- the global object state. Also note that PyDict_SetDefault()
- must be compatible with our reentrancy and global objects state
- constraints. */
- PyObject *actual = PyDict_SetDefault(dict, key, value);
- if (actual == NULL) {
- /* Raising an exception from one interpreter in another
- is problematic, so we clear it and let the caller deal
- with the returned NULL. */
- assert(PyErr_ExceptionMatches(PyExc_MemoryError));
- PyErr_Clear();
- }
-
- /* Swap back, it it wasn't in the main interpreter already. */
- if (oldts != NULL) {
- // The returned tstate should be _PyRuntime.cached_objects.main_tstate.
- _PyThreadState_Swap(runtime, oldts);
-
- unbind_global_objects_state(runtime);
- }
-
- release_global_objects_lock(runtime);
-
- // XXX Immortalize the key and value.
-
- return actual;
-}
-
-
/*************************************/
/* the per-interpreter runtime state */
/*************************************/
@@ -1335,7 +1217,8 @@ free_threadstate(PyThreadState *tstate)
static void
init_threadstate(PyThreadState *tstate,
- PyInterpreterState *interp, uint64_t id)
+ PyInterpreterState *interp, uint64_t id,
+ PyThreadState *next)
{
if (tstate->_status.initialized) {
Py_FatalError("thread state already initialized");
@@ -1344,13 +1227,18 @@ init_threadstate(PyThreadState *tstate,
assert(interp != NULL);
tstate->interp = interp;
- // next/prev are set in add_threadstate().
- assert(tstate->next == NULL);
- assert(tstate->prev == NULL);
-
assert(id > 0);
tstate->id = id;
+ assert(interp->threads.head == tstate);
+ assert((next != NULL && id != 1) || (next == NULL && id == 1));
+ if (next != NULL) {
+ assert(next->prev == NULL || next->prev == tstate);
+ next->prev = tstate;
+ }
+ tstate->next = next;
+ assert(tstate->prev == NULL);
+
// thread_id and native_thread_id are set in bind_tstate().
tstate->py_recursion_limit = interp->ceval.recursion_limit,
@@ -1371,22 +1259,6 @@ init_threadstate(PyThreadState *tstate,
tstate->_status.initialized = 1;
}
-static void
-add_threadstate(PyInterpreterState *interp, PyThreadState *tstate,
- PyThreadState *next)
-{
- assert(interp->threads.head != tstate);
- assert((next != NULL && tstate->id != 1) ||
- (next == NULL && tstate->id == 1));
- if (next != NULL) {
- assert(next->prev == NULL || next->prev == tstate);
- next->prev = tstate;
- }
- tstate->next = next;
- assert(tstate->prev == NULL);
- interp->threads.head = tstate;
-}
-
static PyThreadState *
new_threadstate(PyInterpreterState *interp)
{
@@ -1426,9 +1298,9 @@ new_threadstate(PyInterpreterState *interp)
&initial._main_interpreter._initial_thread,
sizeof(*tstate));
}
+ interp->threads.head = tstate;
- init_threadstate(tstate, interp, id);
- add_threadstate(interp, tstate, old_head);
+ init_threadstate(tstate, interp, id, old_head);
HEAD_UNLOCK(runtime);
if (!used_newtstate) {
@@ -1475,33 +1347,6 @@ _PyThreadState_Init(PyThreadState *tstate)
Py_FatalError("_PyThreadState_Init() is for internal use only");
}
-void
-_PyThreadState_InitDetached(PyThreadState *tstate, PyInterpreterState *interp)
-{
- _PyRuntimeState *runtime = interp->runtime;
-
- HEAD_LOCK(runtime);
- interp->threads.next_unique_id += 1;
- uint64_t id = interp->threads.next_unique_id;
- HEAD_UNLOCK(runtime);
-
- init_threadstate(tstate, interp, id);
- // We do not call add_threadstate().
-}
-
-
-static void
-clear_datastack(PyThreadState *tstate)
-{
- _PyStackChunk *chunk = tstate->datastack_chunk;
- tstate->datastack_chunk = NULL;
- while (chunk != NULL) {
- _PyStackChunk *prev = chunk->previous;
- _PyObject_VirtualFree(chunk, chunk->size);
- chunk = prev;
- }
-}
-
void
PyThreadState_Clear(PyThreadState *tstate)
{
@@ -1576,6 +1421,7 @@ PyThreadState_Clear(PyThreadState *tstate)
// XXX Do it as early in the function as possible.
}
+
/* Common code for PyThreadState_Delete() and PyThreadState_DeleteCurrent() */
static void
tstate_delete_common(PyThreadState *tstate)
@@ -1608,25 +1454,17 @@ tstate_delete_common(PyThreadState *tstate)
unbind_tstate(tstate);
// XXX Move to PyThreadState_Clear()?
- clear_datastack(tstate);
+ _PyStackChunk *chunk = tstate->datastack_chunk;
+ tstate->datastack_chunk = NULL;
+ while (chunk != NULL) {
+ _PyStackChunk *prev = chunk->previous;
+ _PyObject_VirtualFree(chunk, chunk->size);
+ chunk = prev;
+ }
tstate->_status.finalized = 1;
}
-void
-_PyThreadState_ClearDetached(PyThreadState *tstate)
-{
- assert(!tstate->_status.bound);
- assert(!tstate->_status.bound_gilstate);
- assert(tstate->datastack_chunk == NULL);
- assert(tstate->thread_id == 0);
- assert(tstate->native_thread_id == 0);
- assert(tstate->next == NULL);
- assert(tstate->prev == NULL);
-
- PyThreadState_Clear(tstate);
- clear_datastack(tstate);
-}
static void
zapthreads(PyInterpreterState *interp)
From 98e96aff33f80c5f1dc29900f10c1a626b7e6814 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 28 Mar 2023 18:34:56 +0800
Subject: [PATCH 235/280] remove extraneous print
---
Lib/dis.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/Lib/dis.py b/Lib/dis.py
index 844acd56f84ff0..5475c404858991 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -498,7 +498,6 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
argval = arg*2
argrepr = "to " + repr(argval)
elif deop in _bb_jumps:
- print("HI")
signed_arg = -arg if _is_backward_jump(deop) else arg
argval = offset + 2 + signed_arg*2
argval += 2 * caches
From 6a8e23094b8ebd12d6026487a1aa6409cc782c37 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 28 Mar 2023 20:39:25 +0800
Subject: [PATCH 236/280] Fix bug in forward jump rewrites
---
Python/generated_cases.c.h | 2 --
Python/specialize.c | 10 ++++-----
Python/tier2.c | 46 +++++++++++++++++++++-----------------
3 files changed, 31 insertions(+), 27 deletions(-)
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 7a2121a20b2144..c2d46d5e4dcab6 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4119,8 +4119,6 @@
_PyBBBranchCache *cache = (_PyBBBranchCache *)next_instr;
_Py_CODEUNIT *tier1_fallback = NULL;
- // @TODO: Rewrite TEST intruction above to a JUMP above..
-
t2_nextinstr = _PyTier2_GenerateNextBB(
frame, cache->bb_id_tagged, next_instr - 1,
oparg, &tier1_fallback, bb_test);
diff --git a/Python/specialize.c b/Python/specialize.c
index dd5b22dd2346c5..f27951613bfc7a 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -2153,11 +2153,11 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg)
instr->op.code = FOR_ITER_RANGE;
goto success;
}
- else if (tp == &PyGen_Type && oparg <= SHRT_MAX) {
- assert(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == END_FOR);
- instr->op.code = FOR_ITER_GEN;
- goto success;
- }
+ //else if (tp == &PyGen_Type && oparg <= SHRT_MAX) {
+ // assert(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == END_FOR);
+ // instr->op.code = FOR_ITER_GEN;
+ // goto success;
+ //}
SPECIALIZATION_FAIL(FOR_ITER,
_PySpecialization_ClassifyIterator(iter));
STAT_INC(FOR_ITER, failure);
diff --git a/Python/tier2.c b/Python/tier2.c
index f285c1305b4138..d7759e36e23110 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -582,13 +582,14 @@ _PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_reque
fprintf(stderr, "Allocating new BB of size %lld\n", (int64_t)new_size);
#endif
// @TODO We can't Realloc, we actually need to do the linked list method.
- _PyTier2BBSpace *new_space = PyMem_Realloc(curr, new_size);
- if (new_space == NULL) {
- return NULL;
- }
- co->_tier2_info->_bb_space = new_space;
- new_space->max_capacity = new_size;
- return new_space;
+ Py_UNREACHABLE();
+ //_PyTier2BBSpace *new_space = PyMem_Realloc(curr, new_size);
+ //if (new_space == NULL) {
+ // return NULL;
+ //}
+ //co->_tier2_info->_bb_space = new_space;
+ //new_space->max_capacity = new_size;
+ //return new_space;
}
// We have enouogh space. Don't do anything, j
return curr;
@@ -936,8 +937,8 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
fprintf(stderr, "emitted backwards jump %p %d\n", write_curr,
_Py_OPCODE(branch));
#endif
- // Just in case
- _py_set_opcode(write_curr, EXTENDED_ARG);
+ // Just in case, can be swapped out with an EXTENDED_ARG
+ _py_set_opcode(write_curr, NOP);
write_curr->op.arg = 0;
write_curr++;
// We don't need to recalculate the backward jump, because that only needs to be done
@@ -966,6 +967,9 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
write_curr->op.arg = oparg;
write_curr++;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_FOR_ITER);
+ // Just in case, can be swapped out with an EXTENDED_ARG
+ _py_set_opcode(write_curr, NOP);
+ write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
write_curr->op.arg = oparg;
write_curr++;
@@ -983,6 +987,9 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
_py_set_opcode(write_curr, opcode);
write_curr->op.arg = oparg;
write_curr++;
+ _py_set_opcode(write_curr, NOP);
+ // Just in case, can be swapped out with an EXTENDED_ARG
+ write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
write_curr->op.arg = oparg & 0xFF;
write_curr++;
@@ -1948,30 +1955,28 @@ Backwards jumps are handled by another function.
void
_PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target)
{
- _Py_CODEUNIT *write_curr = bb_branch;
+ int branch = _Py_OPCODE(*bb_branch);
+ assert(branch == BB_BRANCH_IF_FLAG_SET ||
+ branch == BB_BRANCH_IF_FLAG_UNSET);
+ _Py_CODEUNIT *write_curr = bb_branch - 1;
// -1 because the PC is auto incremented
int oparg = (int)(target - bb_branch - 1);
assert(oparg > 0);
- int branch = _Py_OPCODE(*bb_branch);
- assert(branch == BB_BRANCH_IF_FLAG_SET || branch == BB_BRANCH_IF_FLAG_UNSET);
bool requires_extended = oparg > 0xFF;
assert(oparg <= 0xFFFF);
if (requires_extended) {
_py_set_opcode(write_curr, EXTENDED_ARG);
- // -1 to oparg because now the jump instruction moves one unit forward.
- oparg--;
write_curr->op.arg = (oparg >> 8) & 0xFF;
write_curr++;
}
+ else {
+ _py_set_opcode(write_curr, NOP);
+ write_curr++;
+ }
_py_set_opcode(write_curr,
branch == BB_BRANCH_IF_FLAG_SET ? BB_JUMP_IF_FLAG_SET : BB_JUMP_IF_FLAG_UNSET);
write_curr->op.arg = oparg & 0xFF;
write_curr++;
- if (!requires_extended) {
- _py_set_opcode(write_curr, NOP);
- write_curr++;
- }
-
}
@@ -1996,7 +2001,8 @@ _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *tar
_Py_CODEUNIT *write_curr = jump_backward_lazy - 1;
_Py_CODEUNIT *prev = jump_backward_lazy - 1;
assert(_Py_OPCODE(*jump_backward_lazy) == BB_JUMP_BACKWARD_LAZY);
- assert(_Py_OPCODE(*prev) == EXTENDED_ARG);
+ assert(_Py_OPCODE(*prev) == EXTENDED_ARG ||
+ _Py_OPCODE(*prev) == NOP);
// +1 because we increment the PC before JUMPBY
int oparg = (int)(target - (jump_backward_lazy + 1));
From 590e99a4bde98f00d82dcd6f0b4461a5f6164296 Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Wed, 29 Mar 2023 01:51:48 +0800
Subject: [PATCH 237/280] Added EXTENDED_ARG to type prop (#29)
---
Python/tier2_typepropagator.c.h | 2 --
Tools/cases_generator/generate_cases.py | 1 -
2 files changed, 3 deletions(-)
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 96b514cd096b1e..357cc74c4e46c2 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -1054,8 +1054,6 @@
}
TARGET(EXTENDED_ARG) {
- fprintf(stderr, "Type propagation across `EXTENDED_ARG` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index f299d234993e55..1ac6962e00cc20 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -70,7 +70,6 @@
"MATCH_MAPPING",
"MATCH_SEQUENCE",
"MATCH_KEYS",
- "EXTENDED_ARG",
"WITH_EXCEPT_START",
# Type propagation across these instructions are forbidden
# due to conditional effects that can't be determined statically
From 47d10293302878d2c06af7c89b2dcff7761d8c31 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 29 Mar 2023 01:52:47 +0800
Subject: [PATCH 238/280] Revert "Added EXTENDED_ARG to type prop (#29)"
This reverts commit 590e99a4bde98f00d82dcd6f0b4461a5f6164296.
---
Python/tier2_typepropagator.c.h | 2 ++
Tools/cases_generator/generate_cases.py | 1 +
2 files changed, 3 insertions(+)
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 357cc74c4e46c2..96b514cd096b1e 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -1054,6 +1054,8 @@
}
TARGET(EXTENDED_ARG) {
+ fprintf(stderr, "Type propagation across `EXTENDED_ARG` shouldn't be handled statically!\n");
+ Py_UNREACHABLE();
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 1ac6962e00cc20..f299d234993e55 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -70,6 +70,7 @@
"MATCH_MAPPING",
"MATCH_SEQUENCE",
"MATCH_KEYS",
+ "EXTENDED_ARG",
"WITH_EXCEPT_START",
# Type propagation across these instructions are forbidden
# due to conditional effects that can't be determined statically
From 1b8fb915408949c531a8dd1535ba902ec8966b6a Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 29 Mar 2023 02:47:42 +0800
Subject: [PATCH 239/280] Handle EXTENDED_ARG in tier 1
---
Python/tier2.c | 64 ++++++++++++++++++++++++++++++++------------------
1 file changed, 41 insertions(+), 23 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index d7759e36e23110..1d364590513b60 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -749,7 +749,7 @@ IS_TERMINATOR_OPCODE(int opcode)
// Opcodes that we can't handle at the moment. If we see them,
// ditch tier 2 attempts.
static inline int
-IS_FORBIDDEN_OPCODE(int opcode)
+IS_FORBIDDEN_OPCODE(int opcode, int nextop)
{
switch (opcode) {
// Modifying containers
@@ -788,11 +788,10 @@ IS_FORBIDDEN_OPCODE(int opcode)
case MATCH_MAPPING:
case MATCH_SEQUENCE:
case MATCH_KEYS:
- // Too large arguments, we can handle this, just
- // increases complexity
- case EXTENDED_ARG:
return 1;
-
+ // Two simultaneous EXTENDED_ARG
+ case EXTENDED_ARG:
+ return nextop == EXTENDED_ARG;
default:
return 0;
}
@@ -885,10 +884,10 @@ emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int bb_id)
// BB_BRANCH
// CACHE (bb_id of the current BB << 1 | is_type_branch)
static inline _Py_CODEUNIT *
-emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr, _Py_CODEUNIT branch, int bb_id)
+emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
+ _Py_CODEUNIT branch, int bb_id, int oparg)
{
int opcode;
- int oparg = _Py_OPARG(branch);
// @TODO handle JUMP_BACKWARDS and JUMP_BACKWARDS_NO_INTERRUPT
switch (_PyOpcode_Deopt[_Py_OPCODE(branch)]) {
case JUMP_BACKWARD_QUICK:
@@ -930,7 +929,8 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
#endif
Py_UNREACHABLE();
}
- assert(oparg <= 0XFF);
+ assert(oparg <= 0XFFFF);
+ bool requires_extended_arg = oparg > 0xFF;
// Backwards jumps should be handled specially.
if (opcode == BB_JUMP_BACKWARD_LAZY) {
#if BB_DEBUG
@@ -938,13 +938,13 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
_Py_OPCODE(branch));
#endif
// Just in case, can be swapped out with an EXTENDED_ARG
- _py_set_opcode(write_curr, NOP);
- write_curr->op.arg = 0;
+ _py_set_opcode(write_curr, requires_extended_arg ? EXTENDED_ARG : NOP);
+ write_curr->op.arg = (oparg >> 8) & 0xFF;
write_curr++;
// We don't need to recalculate the backward jump, because that only needs to be done
// when it locates the next BB in JUMP_BACKWARD_LAZY.
_py_set_opcode(write_curr, BB_JUMP_BACKWARD_LAZY);
- write_curr->op.arg = oparg;
+ write_curr->op.arg = oparg & 0xFF;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -963,15 +963,19 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
// NOTE: IF YOU CHANGE ANY OF THE INSTRUCTIONS BELOW, MAKE SURE
// TO UPDATE THE CALCULATION OF OPARG. THIS IS EXTREMELY IMPORTANT.
oparg = INLINE_CACHE_ENTRIES_FOR_ITER + oparg;
+ requires_extended_arg = oparg > 0xFF;
+ _py_set_opcode(write_curr, requires_extended_arg ? EXTENDED_ARG : NOP);
+ write_curr->op.arg = (oparg >> 8) & 0xFF;
+ write_curr++;
_py_set_opcode(write_curr, BB_TEST_ITER);
- write_curr->op.arg = oparg;
+ write_curr->op.arg = oparg & 0xFF;
write_curr++;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_FOR_ITER);
- // Just in case, can be swapped out with an EXTENDED_ARG
- _py_set_opcode(write_curr, NOP);
+ _py_set_opcode(write_curr, requires_extended_arg ? EXTENDED_ARG : NOP);
+ write_curr->op.arg = (oparg >> 8) & 0xFF;
write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
- write_curr->op.arg = oparg;
+ write_curr->op.arg = oparg & 0xFF;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_BB_BRANCH);
@@ -983,12 +987,14 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
fprintf(stderr, "emitted logical branch %p %d\n", write_curr,
_Py_OPCODE(branch));
#endif
- //_Py_CODEUNIT *start = write_curr;
+ _py_set_opcode(write_curr, requires_extended_arg ? EXTENDED_ARG : NOP);
+ write_curr->op.arg = (oparg >> 8) & 0xFF;
+ write_curr++;
_py_set_opcode(write_curr, opcode);
- write_curr->op.arg = oparg;
+ write_curr->op.arg = oparg & 0xFF;
write_curr++;
- _py_set_opcode(write_curr, NOP);
- // Just in case, can be swapped out with an EXTENDED_ARG
+ _py_set_opcode(write_curr, requires_extended_arg ? EXTENDED_ARG : NOP);
+ write_curr->op.arg = (oparg >> 8) & 0xFF;
write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
write_curr->op.arg = oparg & 0xFF;
@@ -1239,8 +1245,16 @@ _PyTier2_Code_DetectAndEmitBB(
fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
#endif
switch (opcode) {
+ case EXTENDED_ARG:
+ specop = next_instr->op.code;
+ opcode = _PyOpcode_Deopt[specop];
+ oparg = oparg << 8 | next_instr->op.arg;
+ curr++;
+ next_instr++;
+ i+=2;
+ DISPATCH_GOTO();
case RESUME:
- opcode = RESUME_QUICK;
+ opcode = specop = RESUME_QUICK;
DISPATCH();
case END_FOR:
// Assert that we are the start of a BB
@@ -1359,7 +1373,8 @@ _PyTier2_Code_DetectAndEmitBB(
write_i, starting_type_context,
co->_tier2_info->bb_data_curr);
if (possible_next == NULL) {
- DISPATCH();
+ write_i = rebox_stack(write_i, starting_type_context, 2);
+ DISPATCH()
}
write_i = possible_next;
if (needs_guard) {
@@ -1452,7 +1467,7 @@ _PyTier2_Code_DetectAndEmitBB(
// Get the BB ID without incrementing it.
// AllocateBBMetaData will increment.
write_i = emit_logical_branch(starting_type_context, write_i, *curr,
- co->_tier2_info->bb_data_curr);
+ co->_tier2_info->bb_data_curr, oparg);
i += caches;
END();
}
@@ -1714,7 +1729,10 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
for (Py_ssize_t curr = 0; curr < Py_SIZE(co); curr++) {
_Py_CODEUNIT *curr_instr = _PyCode_CODE(co) + curr;
int deopt = _PyOpcode_Deopt[_Py_OPCODE(*curr_instr)];
- if (IS_FORBIDDEN_OPCODE(deopt)) {
+ int next = curr < Py_SIZE(co) - 1
+ ? _PyOpcode_Deopt[(curr_instr + 1)->op.code]
+ : 255;
+ if (IS_FORBIDDEN_OPCODE(deopt, next)) {
#if BB_DEBUG
#ifdef Py_DEBUG
fprintf(stderr, "FORBIDDEN OPCODE %s\n", _PyOpcode_OpName[_Py_OPCODE(*curr_instr)]);
From e8f9f981e97fae7863383477f12fbcfe6413e0dd Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 29 Mar 2023 17:36:23 +0800
Subject: [PATCH 240/280] Diff function for backwards jump
Also supports "loop unrolling"
---
Python/tier2.c | 244 ++++++++++++++++++++----
Python/tier2_typepropagator.c.h | 4 +-
Tools/cases_generator/generate_cases.py | 25 ++-
3 files changed, 220 insertions(+), 53 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 1d364590513b60..2bd8d756539259 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -647,6 +647,9 @@ write_bb_metadata(PyCodeObject *co, _PyTier2BBMetadata *metadata)
co->_tier2_info->bb_data[id] = metadata;
metadata->id = id;
co->_tier2_info->bb_data_curr++;
+#if BB_DEBUG
+ fprintf(stderr, "Creating a BB Metadata with ID %d\n", id);
+#endif
return 0;
}
@@ -825,21 +828,18 @@ emit_cache_entries(_Py_CODEUNIT *write_curr, int cache_entries)
return write_curr;
}
+#define BB_ID(bb_id_raw) (bb_id_raw >> 1)
+#define BB_IS_TYPE_BRANCH(bb_id_raw) (bb_id_raw & 1)
+#define MAKE_TAGGED_BB_ID(bb_id, type_branch) (bb_id << 1 | type_branch)
+
static inline void
write_bb_id(_PyBBBranchCache *cache, int bb_id, bool is_type_guard) {
assert((uint16_t)(bb_id) == bb_id);
- uint16_t bb_id16 = (uint16_t)bb_id;
// Make sure MSB is unset, because we need to shift it.
- assert((bb_id16 & 0x8000) == 0);
- bb_id16 <<= 1;
- bb_id16 |= is_type_guard;
- cache->bb_id_tagged = bb_id16;
+ assert((bb_id & 0x8000) == 0);
+ cache->bb_id_tagged = MAKE_TAGGED_BB_ID((uint16_t)bb_id, is_type_guard);
}
-#define BB_ID(bb_id_raw) (bb_id_raw >> 1)
-#define BB_IS_TYPE_BRANCH(bb_id_raw) (bb_id_raw & 1)
-
-
// The order/hierarchy to emit type guards
// NEED TO ADD TO THIS EVERY TIME WE ADD A NEW ONE.
static int type_guard_ladder[256] = {
@@ -971,6 +971,7 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
write_curr->op.arg = oparg & 0xFF;
write_curr++;
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_FOR_ITER);
+ type_propagate(BB_TEST_ITER, oparg, type_context, NULL);
_py_set_opcode(write_curr, requires_extended_arg ? EXTENDED_ARG : NOP);
write_curr->op.arg = (oparg >> 8) & 0xFF;
write_curr++;
@@ -1215,6 +1216,7 @@ _PyTier2_Code_DetectAndEmitBB(
_PyTier2BBMetadata *meta = NULL;
_PyTier2BBMetadata *temp_meta = NULL;
+ _PyTier2BBMetadata *jump_end_meta = NULL;
_PyTier2Info *t2_info = co->_tier2_info;
PyObject *consts = co->co_consts;
@@ -1424,6 +1426,9 @@ _PyTier2_Code_DetectAndEmitBB(
virtual_start = false;
goto fall_through;
}
+#if TYPEPROP_DEBUG
+ print_typestack(starting_type_context);
+#endif
// Else, create a virtual end to the basic block.
// But generate the block after that so it can fall through.
i--;
@@ -1432,7 +1437,7 @@ _PyTier2_Code_DetectAndEmitBB(
if (type_context_copy == NULL) {
return NULL;
}
- meta = _PyTier2_AllocateBBMetaData(co,
+ jump_end_meta = meta = _PyTier2_AllocateBBMetaData(co,
t2_start, _PyCode_CODE(co) + i, type_context_copy);
if (meta == NULL) {
_PyTier2TypeContext_Free(type_context_copy);
@@ -1443,7 +1448,6 @@ _PyTier2_Code_DetectAndEmitBB(
t2_start = write_i;
i++;
virtual_start = true;
- starting_type_context = type_context_copy;
// Don't change opcode or oparg, let us handle it again.
DISPATCH_GOTO();
@@ -1498,7 +1502,8 @@ _PyTier2_Code_DetectAndEmitBB(
}
if (starts_with_backwards_jump_target) {
// Add the basic block to the jump ids
- if (add_metadata_to_jump_2d_array(t2_info, temp_meta,
+ assert(jump_end_meta != NULL);
+ if (add_metadata_to_jump_2d_array(t2_info, jump_end_meta,
backwards_jump_target_offset) < 0) {
PyMem_Free(meta);
if (meta != temp_meta) {
@@ -1828,17 +1833,15 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
return next_instr;
}
-// Lazily generates successive BBs when required.
-// The first basic block created will always be directly after the current tier 2 code.
-// The second basic block created will always require a jump.
-_Py_CODEUNIT *
-_PyTier2_GenerateNextBB(
+_PyTier2BBMetadata *
+_PyTier2_GenerateNextBBMetaWithTypeContext(
_PyInterpreterFrame *frame,
uint16_t bb_id_tagged,
_Py_CODEUNIT *curr_executing_instr,
int jumpby,
_Py_CODEUNIT **tier1_fallback,
- char bb_flag)
+ char bb_flag,
+ _PyTier2TypeContext *type_context_copy)
{
PyCodeObject *co = frame->f_code;
assert(co->_tier2_info != NULL);
@@ -1856,14 +1859,6 @@ _PyTier2_GenerateNextBB(
// DEOPTIMIZE TO TIER 1?
return NULL;
}
- // Get type_context of previous BB
- _PyTier2TypeContext *type_context = meta->type_context;
- // Make a copy of the type context
- _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_Copy(type_context);
- if (type_context_copy == NULL) {
- return NULL;
- }
-
if (BB_TEST_IS_REQUIRES_POP(bb_flag)) {
__type_stack_shrink(&(type_context_copy->type_stack_ptr), 1);
}
@@ -1889,9 +1884,132 @@ _PyTier2_GenerateNextBB(
_PyTier2TypeContext_Free(type_context_copy);
return NULL;
}
+ return metadata;
+}
+
+// Lazily generates successive BBs when required.
+// The first basic block created will always be directly after the current tier 2 code.
+// The second basic block created will always require a jump.
+_PyTier2BBMetadata *
+_PyTier2_GenerateNextBBMeta(
+ _PyInterpreterFrame *frame,
+ uint16_t bb_id_tagged,
+ _Py_CODEUNIT *curr_executing_instr,
+ int jumpby,
+ _Py_CODEUNIT **tier1_fallback,
+ char bb_flag)
+{
+ _PyTier2BBMetadata *meta = frame->f_code->_tier2_info->bb_data[BB_ID(bb_id_tagged)];
+
+ // Get type_context of previous BB
+ _PyTier2TypeContext *type_context = meta->type_context;
+ // Make a copy of the type context
+ _PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_Copy(type_context);
+ if (type_context_copy == NULL) {
+ return NULL;
+ }
+
+ _PyTier2BBMetadata *next = _PyTier2_GenerateNextBBMetaWithTypeContext(
+ frame,
+ bb_id_tagged,
+ curr_executing_instr,
+ jumpby,
+ tier1_fallback,
+ bb_flag,
+ type_context_copy
+ );
+
+ if (next == NULL) {
+ PyMem_Free(type_context_copy);
+ return NULL;
+ }
+ return next;
+}
+
+_Py_CODEUNIT *
+_PyTier2_GenerateNextBB(
+ _PyInterpreterFrame *frame,
+ uint16_t bb_id_tagged,
+ _Py_CODEUNIT *curr_executing_instr,
+ int jumpby,
+ _Py_CODEUNIT **tier1_fallback,
+ char bb_flag)
+{
+ _PyTier2BBMetadata *metadata = _PyTier2_GenerateNextBBMeta(
+ frame,
+ bb_id_tagged,
+ curr_executing_instr,
+ jumpby,
+ tier1_fallback,
+ bb_flag);
+ if (metadata == NULL) {
+ return NULL;
+ }
return metadata->tier2_start;
}
+// Calculates the difference between two type contexts.
+// A positive number indicating the distance is returned.
+// Incompatible type contexts return INT_MAX.
+static int
+diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
+{
+#if BB_DEBUG
+ fprintf(stderr, " [*] Diffing type contexts\n");
+ static void print_typestack(const _PyTier2TypeContext * type_context);
+ print_typestack(ctx1);
+ print_typestack(ctx2);
+#endif
+ assert(ctx1->type_locals_len == ctx2->type_locals_len);
+ assert(ctx1->type_stack_len == ctx2->type_stack_len);
+ int stack_elems1 = (int)(ctx1->type_stack_ptr - ctx1->type_stack);
+ int stack_elems2 = (int)(ctx2->type_stack_ptr - ctx2->type_stack);
+ assert(stack_elems1 == stack_elems2);
+
+ int diff = 0;
+ // Check the difference in the type locals
+ for (int i = 0; i < ctx1->type_locals_len; i++) {
+ PyTypeObject *a = typenode_get_type(ctx1->type_locals[i]);
+ PyTypeObject *b = typenode_get_type(ctx2->type_locals[i]);
+ // We allow type widening but not narrowing or conversion/casts.
+ // 1. Int -> Int (bueno, diff + 0)
+ // 2. Int -> Unknown/NULL (bueno, diff + 1)
+ // 3. Unknown -> Int (no bueno)
+ // 4. Int -> Float (no bueno)
+
+ // Case 3. Widening operation.
+ if (a == NULL && b != NULL) {
+ return INT_MAX;
+ }
+
+ // Case 4. Incompatible type conversion.
+ if (a != b && b != NULL) {
+ return INT_MAX;
+ }
+
+ // Case 1 and 2. Diff increases if 2.
+ diff += (a != b);
+ }
+
+ // Check the difference in the type stack.
+ for (int i = 0; i < stack_elems1; i++) {
+ // Exact same as above.
+ PyTypeObject *a = typenode_get_type(ctx1->type_locals[i]);
+ PyTypeObject *b = typenode_get_type(ctx2->type_locals[i]);
+
+ if (a == NULL && b != NULL) {
+ return INT_MAX;
+ }
+
+ if (a != b && b != NULL) {
+ return INT_MAX;
+ }
+
+ diff += (a != b);
+ }
+ return diff;
+}
+
_Py_CODEUNIT *
_PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged, int jumpby,
_Py_CODEUNIT **tier1_fallback)
@@ -1915,11 +2033,14 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
}
// Get type_context of previous BB
- _PyTier2TypeContext *type_context = meta->type_context;
+ _PyTier2TypeContext *curr_type_context = meta->type_context;
// Now, find the matching BB
_PyTier2Info *t2_info = co->_tier2_info;
int jump_offset = (int)(tier1_jump_target - _PyCode_CODE(co));
int matching_bb_id = -1;
+ int candidate_bb_id = -1;
+ int min_diff = INT_MAX;
+ int jump_offset_id = -1;
#if BB_DEBUG
fprintf(stderr, "finding jump target: %d\n", jump_offset);
@@ -1929,22 +2050,62 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
fprintf(stderr, "jump offset checked: %d\n", t2_info->backward_jump_offsets[i]);
#endif
if (t2_info->backward_jump_offsets[i] == jump_offset) {
+ jump_offset_id = i;
for (int x = 0; x < MAX_BB_VERSIONS; x++) {
+ int target_bb_id = t2_info->backward_jump_target_bb_ids[i][x];
+ if (target_bb_id >= 0) {
+ candidate_bb_id = target_bb_id;
#if BB_DEBUG
- fprintf(stderr, "jump target BB ID: %d\n",
- t2_info->backward_jump_target_bb_ids[i][x]);
+ fprintf(stderr, "candidate jump target BB ID: %d\n",
+ candidate_bb_id);
#endif
- // @TODO, this is where the diff function is supposed to be
- // it will calculate the closest type context BB
- // For now just any valid BB (>= 0) is used.
- if (t2_info->backward_jump_target_bb_ids[i][x] >= 0) {
- matching_bb_id = t2_info->backward_jump_target_bb_ids[i][x];
- break;
+ int diff = diff_typecontext(curr_type_context,
+ co->_tier2_info->bb_data[target_bb_id]->type_context);
+ if (diff < min_diff) {
+ min_diff = diff;
+ matching_bb_id = target_bb_id;
+ }
}
}
break;
}
}
+ assert(jump_offset_id >= 0);
+ assert(candidate_bb_id >= 0);
+ // We couldn't find a matching BB to jump to. Time to generate our own.
+ // This also requires rewriting our backwards jump to a forward jump later.
+ if (matching_bb_id == -1) {
+ // We should use the type context occuring at the end of the loop.
+ _PyTier2TypeContext *copied = _PyTier2TypeContext_Copy(curr_type_context);
+ if (copied == NULL) {
+ return NULL;
+ }
+ _PyTier2BBMetadata *meta = _PyTier2_GenerateNextBBMetaWithTypeContext(
+ frame, MAKE_TAGGED_BB_ID(candidate_bb_id, 0),
+ NULL,
+ 1,
+ tier1_fallback,
+ 0,
+ copied);
+ if (meta == NULL) {
+ PyMem_Free(copied);
+ return NULL;
+ }
+ // Store the metadata in the jump ids.
+ assert(t2_info->backward_jump_offsets[jump_offset_id] == jump_offset);
+ bool found = false;
+ for (int x = 0; x < MAX_BB_VERSIONS; x++) {
+ int target_bb_id = t2_info->backward_jump_target_bb_ids[jump_offset_id][x];
+ // Write to an available space
+ if (target_bb_id < 0) {
+ t2_info->backward_jump_target_bb_ids[jump_offset_id][x] = meta->id;
+ found = true;
+ break;
+ }
+ }
+ assert(found);
+ return meta->tier2_start;
+ }
assert(matching_bb_id >= 0);
assert(matching_bb_id <= t2_info->bb_data_curr);
#if BB_DEBUG
@@ -2024,8 +2185,12 @@ _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *tar
// +1 because we increment the PC before JUMPBY
int oparg = (int)(target - (jump_backward_lazy + 1));
- assert(oparg < 0);
- oparg = -oparg;
+ assert(oparg != 0);
+ // Is backwards jump.
+ bool is_backwards_jump = oparg < 0;
+ if (is_backwards_jump) {
+ oparg = -oparg;
+ }
assert(oparg > 0);
assert(oparg <= 0xFFFF);
@@ -2039,11 +2204,14 @@ _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *tar
_py_set_opcode(write_curr, NOP);
write_curr++;
}
- _py_set_opcode(write_curr, JUMP_BACKWARD_QUICK);
+ _py_set_opcode(write_curr, is_backwards_jump
+ ? JUMP_BACKWARD_QUICK
+ : JUMP_FORWARD);
write_curr->op.arg = oparg & 0xFF;
write_curr++;
_py_set_opcode(write_curr, END_FOR);
write_curr++;
+ return;
}
#undef TYPESTACK_PEEK
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 96b514cd096b1e..1f2cfddd0378b2 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -801,8 +801,8 @@
}
TARGET(BB_TEST_ITER) {
- fprintf(stderr, "Type propagation across `BB_TEST_ITER` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index f299d234993e55..35207912ab2abb 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -55,7 +55,6 @@
# Type propagator shouldn't see these
"JUMP_IF_FALSE_OR_POP",
"JUMP_IF_TRUE_OR_POP",
- "BB_TEST_ITER",
"FOR_ITER",
"SEND",
"SEND_GEN",
@@ -361,7 +360,7 @@ def write_typeprop(self, out: Formatter) -> None:
ops = typ.ops
for op in ops:
if not isinstance(src := op.src, TypeSrcStackInput): continue
- if oeffect.name in self.unmoved_names and oeffect.name == src.name:
+ if oeffect.name in self.unmoved_names and oeffect.name == src.name:
print(
f"Warn: {self.name} type annotation for {oeffect.name} will be ignored "
"as it is unmoved")
@@ -400,7 +399,7 @@ def write_typeprop(self, out: Formatter) -> None:
typ_op = "TYPE_OVERWRITE"
dst = f"TYPELOCALS_GET({idx})"
match val:
- case TypeSrcLiteral(name=valstr):
+ case TypeSrcLiteral(name=valstr):
if valstr == "NULL":
src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
flag = "true"
@@ -438,7 +437,7 @@ def write_typeprop(self, out: Formatter) -> None:
# Check if it's even used
if oeffect.name == UNUSED: continue
-
+
# Check if there's type info
if typ := oeffect.type_annotation:
for op in typ.ops:
@@ -463,7 +462,7 @@ def write_typeprop(self, out: Formatter) -> None:
flag = "false"
case _:
typing.assert_never(op.src)
-
+
opstr = f"{op.op}({src}, {dst}, {flag})"
if oeffect.cond:
out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
@@ -474,7 +473,7 @@ def write_typeprop(self, out: Formatter) -> None:
# Don't touch unmoved stack vars
if oeffect.name in self.unmoved_names:
continue
-
+
# Just output null
typ_op = "TYPE_OVERWRITE"
src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
@@ -661,7 +660,7 @@ def write_typeprop(self, out: Formatter) -> None:
ops = typ.ops
for op in ops:
if not isinstance(src := op.src, TypeSrcStackInput): continue
- if oeffect.name in self.unmoved_names and oeffect.name == src.name:
+ if oeffect.name in self.unmoved_names and oeffect.name == src.name:
print(
f"Warn: {self.name} type annotation for {oeffect.name} will be ignored "
"as it is unmoved")
@@ -700,7 +699,7 @@ def write_typeprop(self, out: Formatter) -> None:
typ_op = "TYPE_OVERWRITE"
dst = f"TYPELOCALS_GET({idx})"
match val:
- case TypeSrcLiteral(name=valstr):
+ case TypeSrcLiteral(name=valstr):
if valstr == "NULL":
src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
flag = "true"
@@ -738,7 +737,7 @@ def write_typeprop(self, out: Formatter) -> None:
# Check if it's even used
if oeffect.name == UNUSED: continue
-
+
# Check if there's type info
if typ := oeffect.type_annotation:
for op in typ.ops:
@@ -763,7 +762,7 @@ def write_typeprop(self, out: Formatter) -> None:
flag = "false"
case _:
typing.assert_never(op.src)
-
+
opstr = f"{op.op}({src}, {dst}, {flag})"
if oeffect.cond:
out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
@@ -774,7 +773,7 @@ def write_typeprop(self, out: Formatter) -> None:
# Don't touch unmoved stack vars
if oeffect.name in self.unmoved_names:
continue
-
+
# Just output null
typ_op = "TYPE_OVERWRITE"
src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
@@ -1315,7 +1314,7 @@ def write_function(
write_function("popped", popped_data)
write_function("pushed", pushed_data)
self.out.emit("")
-
+
def write_typepropagator(self) -> None:
"""Write the type propagator"""
@@ -1574,7 +1573,7 @@ def write_super_typeprop(self, sup: SuperInstruction) -> None:
...
def write_macro_typeprop(self, mac: MacroInstruction) -> None:
- # TODO: Make the code emitted more efficient by
+ # TODO: Make the code emitted more efficient by
# combining stack effect
name = mac.name
self.out.emit("")
From d95cdb6aebe875b4b6432137949be2e9fa8e13e9 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Wed, 29 Mar 2023 21:33:54 +0800
Subject: [PATCH 241/280] Fix backwards jump generation (again)
---
Include/cpython/code.h | 9 ++-
Python/tier2.c | 164 ++++++++++++++++++++++++++---------------
2 files changed, 114 insertions(+), 59 deletions(-)
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index 4079cd565cfb74..02898cec582f91 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -108,6 +108,13 @@ typedef struct _PyTier2BBSpace {
_Py_CODEUNIT u_code[1];
} _PyTier2BBSpace;
+typedef struct _PyTier2BBStartTypeContextTriplet {
+ int id;
+ _Py_CODEUNIT *tier1_start;
+ // This is a strong reference. So during cleanup we need to free this.
+ _PyTier2TypeContext *start_type_context;
+} _PyTier2BBStartTypeContextTriplet;
+
// Tier 2 info stored in the code object. Lazily allocated.
typedef struct _PyTier2Info {
/* the tier 2 basic block to execute (if any) */
@@ -122,7 +129,7 @@ typedef struct _PyTier2Info {
// So backward jump offset [1, 2, 3 ,4]
// will have [[BB_ID1, BB_ID2], [BB_ID3,], [], []]
// etc.
- int **backward_jump_target_bb_ids;
+ _PyTier2BBStartTypeContextTriplet **backward_jump_target_bb_pairs;
// Max len of bb_data
int bb_data_len;
// Current index to write into in bb_data. Incremented after each write.
diff --git a/Python/tier2.c b/Python/tier2.c
index 2bd8d756539259..58547035d9fea4 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -614,12 +614,6 @@ allocate_bb_metadata(PyCodeObject *co, _Py_CODEUNIT *tier2_start,
return metadata;
}
-static int
-update_backward_jump_target_bb_ids(PyCodeObject *co, _PyTier2BBMetadata *metadata)
-{
- assert(co->_tier2_info != NULL);
-
-}
// Writes BB metadata to code object's tier2info bb_data field.
// 0 on success, 1 on error.
@@ -1081,7 +1075,8 @@ IS_BACKWARDS_JUMP_TARGET(PyCodeObject *co, _Py_CODEUNIT *curr)
// 1 for error, 0 for success.
static inline int
add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
- int backwards_jump_target)
+ int backwards_jump_target, _PyTier2TypeContext *starting_context,
+ _Py_CODEUNIT *tier1_start)
{
// Locate where to insert the BB ID
int backward_jump_offset_index = 0;
@@ -1098,8 +1093,12 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
int jump_i = 0;
found = false;
for (; jump_i < MAX_BB_VERSIONS; jump_i++) {
- if (t2_info->backward_jump_target_bb_ids[backward_jump_offset_index][jump_i] == -1) {
- t2_info->backward_jump_target_bb_ids[backward_jump_offset_index][jump_i] = meta->id;
+ if (t2_info->backward_jump_target_bb_pairs[backward_jump_offset_index][jump_i].id ==
+ -1) {
+ t2_info->backward_jump_target_bb_pairs[backward_jump_offset_index][jump_i].id =
+ meta->id;
+ t2_info->backward_jump_target_bb_pairs[backward_jump_offset_index][jump_i].start_type_context = starting_context;
+ t2_info->backward_jump_target_bb_pairs[backward_jump_offset_index][jump_i].tier1_start = tier1_start;
found = true;
break;
}
@@ -1148,15 +1147,17 @@ infer_BINARY_OP_ADD(
}
}
// Unknown, time to emit the chain of guards.
- write_curr = rebox_stack(write_curr, type_context, 2);
- *needs_guard = true;
if (prev_type_guard == NULL) {
+ write_curr = rebox_stack(write_curr, type_context, 2);
+ *needs_guard = true;
return emit_type_guard(write_curr, BINARY_CHECK_INT, bb_id);
}
else {
int next_guard = type_guard_ladder[
type_guard_to_index[prev_type_guard->op.code] + 1];
if (next_guard != -1) {
+ write_curr = rebox_stack(write_curr, type_context, 2);
+ *needs_guard = true;
return emit_type_guard(write_curr, next_guard, bb_id);
}
// End of ladder, fall through
@@ -1205,6 +1206,13 @@ _PyTier2_Code_DetectAndEmitBB(
i += caches; \
type_propagate(opcode, oparg, starting_type_context, consts); \
continue;
+
+#define DISPATCH_REBOX(x) write_i = rebox_stack(write_i, starting_type_context, x); \
+ write_i = emit_i(write_i, specop, oparg); \
+ write_i = copy_cache_entries(write_i, curr+1, caches); \
+ i += caches; \
+ type_propagate(opcode, oparg, starting_type_context, consts); \
+ continue;
#define DISPATCH_GOTO() goto dispatch_opcode;
#define TYPECONST_GET_RAWTYPE(idx) Py_TYPE(PyTuple_GET_ITEM(consts, idx))
@@ -1228,6 +1236,8 @@ _PyTier2_Code_DetectAndEmitBB(
bool starts_with_backwards_jump_target = false;
int backwards_jump_target_offset = -1;
bool virtual_start = false;
+ _PyTier2TypeContext *start_type_context_copy = NULL;
+ _Py_CODEUNIT *virtual_tier1_start = NULL;
// A meta-interpreter for types.
Py_ssize_t i = (tier1_start - _PyCode_CODE(co));
@@ -1354,18 +1364,12 @@ _PyTier2_Code_DetectAndEmitBB(
}
// Need to handle reboxing at these boundaries.
case CALL:
- write_i = rebox_stack(write_i, starting_type_context,
- oparg + 2);
- DISPATCH();
+ DISPATCH_REBOX(oparg + 2);
case BUILD_MAP:
- write_i = rebox_stack(write_i, starting_type_context,
- oparg * 2);
- DISPATCH();
+ DISPATCH_REBOX(oparg * 2);
case BUILD_STRING:
case BUILD_LIST:
- write_i = rebox_stack(write_i, starting_type_context,
- oparg);
- DISPATCH();
+ DISPATCH_REBOX(oparg);
case BINARY_OP:
if (oparg == NB_ADD) {
// Add operation. Need to check if we can infer types.
@@ -1375,8 +1379,7 @@ _PyTier2_Code_DetectAndEmitBB(
write_i, starting_type_context,
co->_tier2_info->bb_data_curr);
if (possible_next == NULL) {
- write_i = rebox_stack(write_i, starting_type_context, 2);
- DISPATCH()
+ DISPATCH_REBOX(2);
}
write_i = possible_next;
if (needs_guard) {
@@ -1389,24 +1392,20 @@ _PyTier2_Code_DetectAndEmitBB(
i += caches;
continue;
}
- write_i = rebox_stack(write_i, starting_type_context, 2);
- DISPATCH()
+ DISPATCH_REBOX(2);
case LOAD_ATTR:
case CALL_INTRINSIC_1:
case UNARY_NEGATIVE:
case UNARY_NOT:
case UNARY_INVERT:
case GET_LEN:
- write_i = rebox_stack(write_i, starting_type_context, 1);
- DISPATCH();
+ DISPATCH_REBOX(1);
case CALL_INTRINSIC_2:
case BINARY_SUBSCR:
case BINARY_SLICE:
- write_i = rebox_stack(write_i, starting_type_context, 2);
- DISPATCH();
+ DISPATCH_REBOX(2);
case STORE_SLICE:
- write_i = rebox_stack(write_i, starting_type_context, 4);
- DISPATCH();
+ DISPATCH_REBOX(4);
default:
#if BB_DEBUG && !TYPEPROP_DEBUG
fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
@@ -1424,6 +1423,12 @@ _PyTier2_Code_DetectAndEmitBB(
starts_with_backwards_jump_target = true;
backwards_jump_target_offset = (int)(curr - _PyCode_CODE(co));
virtual_start = false;
+ start_type_context_copy = _PyTier2TypeContext_Copy(starting_type_context);
+ virtual_tier1_start = _PyCode_CODE(co) + i;
+ if (start_type_context_copy == NULL) {
+ _PyTier2TypeContext_Free(starting_type_context);
+ return NULL;
+ }
goto fall_through;
}
#if TYPEPROP_DEBUG
@@ -1432,12 +1437,11 @@ _PyTier2_Code_DetectAndEmitBB(
// Else, create a virtual end to the basic block.
// But generate the block after that so it can fall through.
i--;
- // Make a copy of the type context
_PyTier2TypeContext *type_context_copy = _PyTier2TypeContext_Copy(starting_type_context);
if (type_context_copy == NULL) {
return NULL;
}
- jump_end_meta = meta = _PyTier2_AllocateBBMetaData(co,
+ meta = _PyTier2_AllocateBBMetaData(co,
t2_start, _PyCode_CODE(co) + i, type_context_copy);
if (meta == NULL) {
_PyTier2TypeContext_Free(type_context_copy);
@@ -1502,9 +1506,11 @@ _PyTier2_Code_DetectAndEmitBB(
}
if (starts_with_backwards_jump_target) {
// Add the basic block to the jump ids
- assert(jump_end_meta != NULL);
- if (add_metadata_to_jump_2d_array(t2_info, jump_end_meta,
- backwards_jump_target_offset) < 0) {
+ assert(start_type_context_copy != NULL);
+ assert(virtual_tier1_start != NULL);
+ if (add_metadata_to_jump_2d_array(t2_info, temp_meta,
+ backwards_jump_target_offset, start_type_context_copy,
+ virtual_tier1_start) < 0) {
PyMem_Free(meta);
if (meta != temp_meta) {
PyMem_Free(temp_meta);
@@ -1534,24 +1540,26 @@ compare_ints(const void *a, const void *b)
}
static int
-allocate_jump_offset_2d_array(int backwards_jump_count, int **backward_jump_target_bb_ids)
+allocate_jump_offset_2d_array(int backwards_jump_count,
+ _PyTier2BBStartTypeContextTriplet **backward_jump_target_bb_pairs)
{
int done = 0;
for (int i = 0; i < backwards_jump_count; i++) {
- int *jump_offsets = PyMem_Malloc(sizeof(int) * MAX_BB_VERSIONS);
- if (jump_offsets == NULL) {
+ _PyTier2BBStartTypeContextTriplet *pair =
+ PyMem_Malloc(sizeof(_PyTier2BBStartTypeContextTriplet) * MAX_BB_VERSIONS);
+ if (pair == NULL) {
goto error;
}
for (int i = 0; i < MAX_BB_VERSIONS; i++) {
- jump_offsets[i] = -1;
+ pair[i].id = -1;
}
done++;
- backward_jump_target_bb_ids[i] = jump_offsets;
+ backward_jump_target_bb_pairs[i] = pair;
}
return 0;
error:
for (int i = 0; i < done; i++) {
- PyMem_Free(backward_jump_target_bb_ids[i]);
+ PyMem_Free(backward_jump_target_bb_pairs[i]);
}
return 1;
}
@@ -1589,15 +1597,16 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
if (backward_jump_offsets == NULL) {
return 1;
}
- int **backward_jump_target_bb_ids = PyMem_Malloc(backwards_jump_count * sizeof(int *));
- if (backward_jump_target_bb_ids == NULL) {
+ _PyTier2BBStartTypeContextTriplet **backward_jump_target_bb_pairs =
+ PyMem_Malloc(backwards_jump_count * sizeof(_PyTier2BBStartTypeContextTriplet *));
+ if (backward_jump_target_bb_pairs == NULL) {
PyMem_Free(backward_jump_offsets);
return 1;
}
if (allocate_jump_offset_2d_array((int)backwards_jump_count,
- backward_jump_target_bb_ids)) {
+ backward_jump_target_bb_pairs)) {
PyMem_Free(backward_jump_offsets);
- PyMem_Free(backward_jump_target_bb_ids);
+ PyMem_Free(backward_jump_target_bb_pairs);
return 1;
}
@@ -1623,6 +1632,18 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
assert(curr_i == backwards_jump_count);
qsort(backward_jump_offsets, backwards_jump_count,
sizeof(int), compare_ints);
+ // Deduplicate
+ int backwards_jump_count_new = backwards_jump_count;
+ for (int i = 0; i < backwards_jump_count - 1; i++) {
+ for (int x = i + 1; x < backwards_jump_count; x++) {
+ if (backward_jump_offsets[i] == backward_jump_offsets[x]) {
+ backward_jump_offsets[x] = -1;
+ backwards_jump_count_new--;
+ }
+ }
+ }
+ qsort(backward_jump_offsets, backwards_jump_count,
+ sizeof(int), compare_ints);
#if BB_DEBUG
fprintf(stderr, "BACKWARD JUMP COUNT : %Id\n", backwards_jump_count);
fprintf(stderr, "BACKWARD JUMP TARGET OFFSETS (FROM START OF CODE): ");
@@ -1633,7 +1654,7 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
#endif
co->_tier2_info->backward_jump_count = (int)backwards_jump_count;
co->_tier2_info->backward_jump_offsets = backward_jump_offsets;
- co->_tier2_info->backward_jump_target_bb_ids = backward_jump_target_bb_ids;
+ co->_tier2_info->backward_jump_target_bb_pairs = backward_jump_target_bb_pairs;
return 0;
}
@@ -1841,13 +1862,15 @@ _PyTier2_GenerateNextBBMetaWithTypeContext(
int jumpby,
_Py_CODEUNIT **tier1_fallback,
char bb_flag,
- _PyTier2TypeContext *type_context_copy)
+ _PyTier2TypeContext *type_context_copy,
+ _Py_CODEUNIT *custom_tier1_end)
{
PyCodeObject *co = frame->f_code;
assert(co->_tier2_info != NULL);
assert(BB_ID(bb_id_tagged) <= co->_tier2_info->bb_data_curr);
_PyTier2BBMetadata *meta = co->_tier2_info->bb_data[BB_ID(bb_id_tagged)];
- _Py_CODEUNIT *tier1_end = meta->tier1_end + jumpby;
+ _Py_CODEUNIT *tier1_end = custom_tier1_end == NULL
+ ? meta->tier1_end + jumpby : custom_tier1_end;
*tier1_fallback = tier1_end;
// Be a pessimist and assume we need to write the entire rest of code into the BB.
// The size of the BB generated will definitely be equal to or smaller than this.
@@ -1916,7 +1939,8 @@ _PyTier2_GenerateNextBBMeta(
jumpby,
tier1_fallback,
bb_flag,
- type_context_copy
+ type_context_copy,
+ NULL
);
if (next == NULL) {
@@ -1954,6 +1978,8 @@ _PyTier2_GenerateNextBB(
static int
diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
{
+ assert(ctx1 != NULL);
+ assert(ctx2 != NULL);
#if BB_DEBUG
fprintf(stderr, " [*] Diffing type contexts\n");
static void print_typestack(const _PyTier2TypeContext * type_context);
@@ -1976,6 +2002,7 @@ diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
// 2. Int -> Unknown/NULL (bueno, diff + 1)
// 3. Unknown -> Int (no bueno)
// 4. Int -> Float (no bueno)
+ // 5. Unboxed type -> Unknown/Boxed type (no bueno)
// Case 3. Widening operation.
if (a == NULL && b != NULL) {
@@ -1987,6 +2014,11 @@ diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
return INT_MAX;
}
+ // Case 5. Boxed to unboxed conversion.
+ if (is_unboxed_type(a) && a != b) {
+ return INT_MAX;
+ }
+
// Case 1 and 2. Diff increases if 2.
diff += (a != b);
}
@@ -1994,8 +2026,8 @@ diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
// Check the difference in the type stack.
for (int i = 0; i < stack_elems1; i++) {
// Exact same as above.
- PyTypeObject *a = typenode_get_type(ctx1->type_locals[i]);
- PyTypeObject *b = typenode_get_type(ctx2->type_locals[i]);
+ PyTypeObject *a = typenode_get_type(ctx1->type_stack[i]);
+ PyTypeObject *b = typenode_get_type(ctx2->type_stack[i]);
if (a == NULL && b != NULL) {
return INT_MAX;
@@ -2005,6 +2037,11 @@ diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
return INT_MAX;
}
+ // Case 5. Boxed to unboxed conversion.
+ if (is_unboxed_type(a) && a != b) {
+ return INT_MAX;
+ }
+
diff += (a != b);
}
return diff;
@@ -2041,6 +2078,7 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
int candidate_bb_id = -1;
int min_diff = INT_MAX;
int jump_offset_id = -1;
+ _Py_CODEUNIT *candidate_bb_tier1_start = NULL;
#if BB_DEBUG
fprintf(stderr, "finding jump target: %d\n", jump_offset);
@@ -2052,15 +2090,16 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
if (t2_info->backward_jump_offsets[i] == jump_offset) {
jump_offset_id = i;
for (int x = 0; x < MAX_BB_VERSIONS; x++) {
- int target_bb_id = t2_info->backward_jump_target_bb_ids[i][x];
+ int target_bb_id = t2_info->backward_jump_target_bb_pairs[i][x].id;
if (target_bb_id >= 0) {
candidate_bb_id = target_bb_id;
+ candidate_bb_tier1_start = t2_info->backward_jump_target_bb_pairs[i][x].tier1_start;
#if BB_DEBUG
fprintf(stderr, "candidate jump target BB ID: %d\n",
candidate_bb_id);
#endif
int diff = diff_typecontext(curr_type_context,
- co->_tier2_info->bb_data[target_bb_id]->type_context);
+ t2_info->backward_jump_target_bb_pairs[i][x].start_type_context);
if (diff < min_diff) {
min_diff = diff;
matching_bb_id = target_bb_id;
@@ -2072,6 +2111,7 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
}
assert(jump_offset_id >= 0);
assert(candidate_bb_id >= 0);
+ assert(candidate_bb_tier1_start != NULL);
// We couldn't find a matching BB to jump to. Time to generate our own.
// This also requires rewriting our backwards jump to a forward jump later.
if (matching_bb_id == -1) {
@@ -2080,25 +2120,33 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
if (copied == NULL) {
return NULL;
}
+ _PyTier2TypeContext *second_copy = _PyTier2TypeContext_Copy(curr_type_context);
+ if (second_copy == NULL) {
+ return NULL;
+ }
_PyTier2BBMetadata *meta = _PyTier2_GenerateNextBBMetaWithTypeContext(
frame, MAKE_TAGGED_BB_ID(candidate_bb_id, 0),
NULL,
- 1,
+ 0,
tier1_fallback,
0,
- copied);
+ copied,
+ candidate_bb_tier1_start);
if (meta == NULL) {
- PyMem_Free(copied);
+ _PyTier2TypeContext_Free(copied);
+ _PyTier2TypeContext_Free(second_copy);
return NULL;
}
// Store the metadata in the jump ids.
assert(t2_info->backward_jump_offsets[jump_offset_id] == jump_offset);
bool found = false;
for (int x = 0; x < MAX_BB_VERSIONS; x++) {
- int target_bb_id = t2_info->backward_jump_target_bb_ids[jump_offset_id][x];
+ int target_bb_id = t2_info->backward_jump_target_bb_pairs[jump_offset_id][x].id;
// Write to an available space
if (target_bb_id < 0) {
- t2_info->backward_jump_target_bb_ids[jump_offset_id][x] = meta->id;
+ t2_info->backward_jump_target_bb_pairs[jump_offset_id][x].id = meta->id;
+ t2_info->backward_jump_target_bb_pairs[jump_offset_id][x].start_type_context = second_copy;
+ t2_info->backward_jump_target_bb_pairs[jump_offset_id][x].tier1_start = candidate_bb_tier1_start;
found = true;
break;
}
From 3c2ac64f1669f53a1efee383b9cdc7631857275a Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Thu, 30 Mar 2023 01:06:09 +0800
Subject: [PATCH 242/280] Fix: Buggy type propagation across SWAP (#30)
---
Python/tier2.c | 79 ++++++++++++++++++
Python/tier2_typepropagator.c.h | 104 ------------------------
Tools/cases_generator/generate_cases.py | 22 ++---
3 files changed, 84 insertions(+), 121 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 58547035d9fea4..07adcdfdca1b12 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -376,6 +376,74 @@ __type_propagate_TYPE_OVERWRITE(
}
}
+// src and dst are assumed to already be within the type context
+static void
+__type_propagate_TYPE_SWAP(
+ _PyTier2TypeContext *type_context,
+ _Py_TYPENODE_t *src, _Py_TYPENODE_t *dst)
+{
+ // Check if they are the same tree
+ _Py_TYPENODE_t *srcrootref = src;
+ _Py_TYPENODE_t *dstrootref = dst;
+ uintptr_t dsttag = _Py_TYPENODE_GET_TAG(*dst);
+ uintptr_t srctag = _Py_TYPENODE_GET_TAG(*src);
+ switch (dsttag) {
+ case TYPE_REF: dstrootref = __typenode_get_rootptr(*dst);
+ case TYPE_ROOT:
+ switch (srctag) {
+ case TYPE_REF: srcrootref = __typenode_get_rootptr(*src);
+ case TYPE_ROOT:
+ if (srcrootref == dstrootref) {
+ // Same tree, no point swapping
+ return;
+ }
+ break;
+ default:
+ Py_UNREACHABLE();
+ }
+ break;
+ default:
+ Py_UNREACHABLE();
+ }
+
+ // src and dst are different tree,
+ // Make all children of src be children of dst and vice versa
+
+ _Py_TYPENODE_t src_child_test = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)src));
+ _Py_TYPENODE_t dst_child_test = _Py_TYPENODE_MAKE_REF(
+ _Py_TYPENODE_CLEAR_TAG((_Py_TYPENODE_t)dst));
+
+ // Search locals for children
+ int nlocals = type_context->type_locals_len;
+ for (int i = 0; i < nlocals; i++) {
+ _Py_TYPENODE_t *node_ptr = &(type_context->type_locals[i]);
+ if (*node_ptr == src_child_test) {
+ *node_ptr = dst_child_test;
+ }
+ else if (*node_ptr == dst_child_test) {
+ *node_ptr = src_child_test;
+ }
+ }
+
+ // Search stack for children
+ int nstack = type_context->type_stack_len;
+ for (int i = 0; i < nstack; i++) {
+ _Py_TYPENODE_t *node_ptr = &(type_context->type_stack[i]);
+ if (*node_ptr == src_child_test) {
+ *node_ptr = dst_child_test;
+ }
+ else if (*node_ptr == dst_child_test) {
+ *node_ptr = src_child_test;
+ }
+ }
+
+ // Finally, actually swap the nodes
+ *src ^= *dst;
+ *dst ^= *src;
+ *src ^= *dst;
+}
+
static void
__type_stack_shrink(_Py_TYPENODE_t **type_stackptr, int idx)
{
@@ -489,6 +557,7 @@ type_propagate(
#define TYPE_SET(src, dst, flag) __type_propagate_TYPE_SET((src), (dst), (flag))
#define TYPE_OVERWRITE(src, dst, flag) __type_propagate_TYPE_OVERWRITE(type_context, (src), (dst), (flag))
+#define TYPE_SWAP(src, dst) __type_propagate_TYPE_SWAP(type_context, (src), (dst))
#define STACK_GROW(idx) *type_stackptr += (idx)
@@ -504,8 +573,18 @@ type_propagate(
switch (opcode) {
#include "tier2_typepropagator.c.h"
+ TARGET(SWAP) {
+ _Py_TYPENODE_t *top = TYPESTACK_PEEK(1);
+ _Py_TYPENODE_t * bottom = TYPESTACK_PEEK(2 + (oparg - 2));
+ TYPE_SWAP(top, bottom);
+ break;
+ }
default:
+#ifdef Py_DEBUG
+ fprintf(stderr, "Unsupported opcode in type propagator: %s : %d\n", _PyOpcode_OpName[opcode], oparg);
+#else
fprintf(stderr, "Unsupported opcode in type propagator: %d\n", opcode);
+#endif
Py_UNREACHABLE();
}
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 1f2cfddd0378b2..9c645df9007b0b 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -284,12 +284,6 @@
break;
}
- TARGET(RAISE_VARARGS) {
- fprintf(stderr, "Type propagation across `RAISE_VARARGS` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
TARGET(INTERPRETER_EXIT) {
STACK_SHRINK(1);
break;
@@ -320,36 +314,6 @@
break;
}
- TARGET(SEND) {
- fprintf(stderr, "Type propagation across `SEND` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(SEND_GEN) {
- fprintf(stderr, "Type propagation across `SEND_GEN` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(YIELD_VALUE) {
- fprintf(stderr, "Type propagation across `YIELD_VALUE` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(POP_EXCEPT) {
- fprintf(stderr, "Type propagation across `POP_EXCEPT` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(RERAISE) {
- fprintf(stderr, "Type propagation across `RERAISE` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
TARGET(END_ASYNC_FOR) {
STACK_SHRINK(2);
break;
@@ -464,18 +428,6 @@
break;
}
- TARGET(DELETE_FAST) {
- fprintf(stderr, "Type propagation across `DELETE_FAST` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(MAKE_CELL) {
- fprintf(stderr, "Type propagation across `MAKE_CELL` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
TARGET(DELETE_DEREF) {
break;
}
@@ -486,12 +438,6 @@
break;
}
- TARGET(LOAD_DEREF) {
- fprintf(stderr, "Type propagation across `LOAD_DEREF` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
TARGET(STORE_DEREF) {
STACK_SHRINK(1);
break;
@@ -766,24 +712,6 @@
break;
}
- TARGET(MATCH_MAPPING) {
- fprintf(stderr, "Type propagation across `MATCH_MAPPING` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(MATCH_SEQUENCE) {
- fprintf(stderr, "Type propagation across `MATCH_SEQUENCE` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(MATCH_KEYS) {
- fprintf(stderr, "Type propagation across `MATCH_KEYS` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
TARGET(GET_ITER) {
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
@@ -794,12 +722,6 @@
break;
}
- TARGET(FOR_ITER) {
- fprintf(stderr, "Type propagation across `FOR_ITER` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
TARGET(BB_TEST_ITER) {
STACK_GROW(1);
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
@@ -843,18 +765,6 @@
break;
}
- TARGET(WITH_EXCEPT_START) {
- fprintf(stderr, "Type propagation across `WITH_EXCEPT_START` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
- TARGET(PUSH_EXC_INFO) {
- fprintf(stderr, "Type propagation across `PUSH_EXC_INFO` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
STACK_GROW(((oparg & 1) ? 1 : 0));
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
@@ -1045,20 +955,6 @@
break;
}
- TARGET(SWAP) {
- _Py_TYPENODE_t *top = TYPESTACK_PEEK(1);
- _Py_TYPENODE_t *bottom = TYPESTACK_PEEK(2 + (oparg-2));
- TYPE_OVERWRITE(bottom, TYPESTACK_PEEK(1), false);
- TYPE_OVERWRITE(top, TYPESTACK_PEEK(2 + (oparg-2)), false);
- break;
- }
-
- TARGET(EXTENDED_ARG) {
- fprintf(stderr, "Type propagation across `EXTENDED_ARG` shouldn't be handled statically!\n");
- Py_UNREACHABLE();
- break;
- }
-
TARGET(CACHE) {
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 35207912ab2abb..1255ba463f5a8b 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -53,9 +53,9 @@
TYPE_PROPAGATOR_FORBIDDEN = [
# Type propagator shouldn't see these
- "JUMP_IF_FALSE_OR_POP",
- "JUMP_IF_TRUE_OR_POP",
"FOR_ITER",
+ "SWAP",
+ # Not supported
"SEND",
"SEND_GEN",
"YIELD_VALUE",
@@ -71,12 +71,6 @@
"MATCH_KEYS",
"EXTENDED_ARG",
"WITH_EXCEPT_START",
- # Type propagation across these instructions are forbidden
- # due to conditional effects that can't be determined statically
- # The handling of type propagation across these opcodes are handled elsewhere
- # within tier2.
- "BB_TEST_IF_FALSE_OR_POP",
- "BB_TEST_IF_TRUE_OR_POP" # Type propagator handles this in BB_BRANCH
]
arg_parser = argparse.ArgumentParser(
@@ -344,10 +338,7 @@ def __init__(self, inst: parser.InstDef):
def write_typeprop(self, out: Formatter) -> None:
"""Write one instruction's type propagation rules"""
- if self.name in TYPE_PROPAGATOR_FORBIDDEN:
- out.emit(f'fprintf(stderr, "Type propagation across `{self.name}` shouldn\'t be handled statically!\\n");')
- out.emit("Py_UNREACHABLE();")
- return
+ # TODO: Detect loops like in SWAP
need_to_declare = []
# Stack input is used in local effect
@@ -644,11 +635,6 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
def write_typeprop(self, out: Formatter) -> None:
"""Write one instruction's type propagation rules"""
- if self.name in TYPE_PROPAGATOR_FORBIDDEN:
- out.emit(f'fprintf(stderr, "Type propagation across `{self.name}` shouldn\'t be handled statically!\\n");')
- out.emit("Py_UNREACHABLE();")
- return
-
need_to_declare = []
# Stack input is used in local effect
if self.local_effects and \
@@ -1328,6 +1314,8 @@ def write_typepropagator(self) -> None:
self.out = Formatter(f, 8)
for thing in self.everything:
+ if thing.name in TYPE_PROPAGATOR_FORBIDDEN:
+ continue
match thing:
case parser.InstDef(kind=kind, name=name):
match kind:
From 6bade7bcf3955da069216d5dc801d63be0449284 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 30 Mar 2023 01:30:01 +0800
Subject: [PATCH 243/280] More support for EXTENDED_ARGS
---
Include/internal/pycore_code.h | 2 +-
Lib/dis.py | 12 ++++-
Python/bytecodes.c | 2 +-
Python/generated_cases.c.h | 2 +-
Python/tier2.c | 89 +++++++++++++++++++++-------------
5 files changed, 70 insertions(+), 37 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 55c8bd9cd4227a..0f8d9f7808153f 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -282,7 +282,7 @@ extern _Py_CODEUNIT *_PyTier2_GenerateNextBB(
char bb_flag);
extern _Py_CODEUNIT *_PyTier2_LocateJumpBackwardsBB(
struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
- _Py_CODEUNIT **tier1_fallback);
+ _Py_CODEUNIT **tier1_fallback, _Py_CODEUNIT *curr);
extern void _PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target);
extern void _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *target);
#ifdef Py_STATS
diff --git a/Lib/dis.py b/Lib/dis.py
index 5475c404858991..65fd696012718f 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -58,6 +58,7 @@
_empty_slot = [slot for slot, name in enumerate(_all_opname) if name.startswith("<")]
for uop_opcode, uop in zip(_empty_slot, _uops):
_all_opname[uop_opcode] = uop
+ _all_opmap[uop] = uop_opcode
if uop.startswith('BB_BRANCH') or uop.startswith('BB_JUMP'):
if uop.startswith('BB_JUMP'):
_bb_jumps.append(uop_opcode)
@@ -69,6 +70,15 @@
specialized: base for base, family in _specializations.items() for specialized in family
}
+_TIER2_STORE_OPS = (
+ # 'LOAD_FAST_NO_INCREF',
+ # 'STORE_FAST_BOXED_UNBOXED',
+ # 'STORE_FAST_UNBOXED_BOXED',
+ # 'STORE_FAST_UNBOXED_UNBOXED',
+)
+for store_op in ([_all_opmap[op] for op in _TIER2_STORE_OPS]):
+ hasname.append(store_op)
+ hasarg.append(store_op)
def _try_compile(source, name):
"""Attempts to compile the given source, first as an expression and
then as a statement if the first approach fails.
@@ -442,7 +452,7 @@ def _parse_exception_table(code):
return entries
def _is_backward_jump(op):
- return 'JUMP_BACKWARD' in opname[op]
+ return 'JUMP_BACKWARD' in opname[op] or 'JUMP_BACKWARD_QUICK' in opname[op]
def _get_instructions_bytes(code, varname_from_oparg=None,
names=None, co_consts=None,
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 0aa87a4a93d16a..76bbb5e566567d 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3276,7 +3276,7 @@ dummy_func(
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_LocateJumpBackwardsBB(
- frame, cache->bb_id_tagged, -oparg, &tier1_fallback);
+ frame, cache->bb_id_tagged, -oparg, &tier1_fallback, curr);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index c2d46d5e4dcab6..00601c8800259c 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4153,7 +4153,7 @@
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_LocateJumpBackwardsBB(
- frame, cache->bb_id_tagged, -oparg, &tier1_fallback);
+ frame, cache->bb_id_tagged, -oparg, &tier1_fallback, curr);
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
diff --git a/Python/tier2.c b/Python/tier2.c
index 07adcdfdca1b12..32c75f8eb65de4 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -12,8 +12,8 @@
#define TYPEPROP_DEBUG 1
// Max typed version basic blocks per basic block
#define MAX_BB_VERSIONS 5
-// Number of potential extra instructions at end of a BB, for branch or cleanup purposes.
-#define BB_EPILOG 0
+
+#define OVERALLOCATE_FACTOR 4
/* Dummy types used by the types propagator */
@@ -1336,14 +1336,6 @@ _PyTier2_Code_DetectAndEmitBB(
fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
#endif
switch (opcode) {
- case EXTENDED_ARG:
- specop = next_instr->op.code;
- opcode = _PyOpcode_Deopt[specop];
- oparg = oparg << 8 | next_instr->op.arg;
- curr++;
- next_instr++;
- i+=2;
- DISPATCH_GOTO();
case RESUME:
opcode = specop = RESUME_QUICK;
DISPATCH();
@@ -1489,27 +1481,26 @@ _PyTier2_Code_DetectAndEmitBB(
#if BB_DEBUG && !TYPEPROP_DEBUG
fprintf(stderr, "offset: %Id\n", curr - _PyCode_CODE(co));
#endif
- if (IS_BACKWARDS_JUMP_TARGET(co, curr)) {
+ // This should be the end of another basic block, or the start of a new.
+ // Start of a new basic block, just ignore and continue.
+ if (virtual_start) {
#if BB_DEBUG
- fprintf(stderr, "Encountered a backward jump target\n");
+ fprintf(stderr, "Emitted virtual start of basic block\n");
#endif
- // This should be the end of another basic block, or the start of a new.
- // Start of a new basic block, just ignore and continue.
- if (virtual_start) {
+ starts_with_backwards_jump_target = true;
+ virtual_start = false;
+ start_type_context_copy = _PyTier2TypeContext_Copy(starting_type_context);
+ if (start_type_context_copy == NULL) {
+ _PyTier2TypeContext_Free(starting_type_context);
+ return NULL;
+ }
+ goto fall_through;
+ }
+ if (IS_BACKWARDS_JUMP_TARGET(co, curr)) {
+backward_jump_target:
#if BB_DEBUG
- fprintf(stderr, "Emitted virtual start of basic block\n");
+ fprintf(stderr, "Encountered a backward jump target\n");
#endif
- starts_with_backwards_jump_target = true;
- backwards_jump_target_offset = (int)(curr - _PyCode_CODE(co));
- virtual_start = false;
- start_type_context_copy = _PyTier2TypeContext_Copy(starting_type_context);
- virtual_tier1_start = _PyCode_CODE(co) + i;
- if (start_type_context_copy == NULL) {
- _PyTier2TypeContext_Free(starting_type_context);
- return NULL;
- }
- goto fall_through;
- }
#if TYPEPROP_DEBUG
print_typestack(starting_type_context);
#endif
@@ -1530,8 +1521,20 @@ _PyTier2_Code_DetectAndEmitBB(
// Reset all our values
t2_start = write_i;
i++;
+ virtual_tier1_start = _PyCode_CODE(co) + i;
+ backwards_jump_target_offset = (int)(curr - _PyCode_CODE(co));
virtual_start = true;
+ if (opcode == EXTENDED_ARG) {
+ // Note: EXTENDED_ARG could be a jump target!!!!!
+ caches = _PyOpcode_Caches[opcode];
+ specop = next_instr->op.code;
+ opcode = _PyOpcode_Deopt[specop];
+ oparg = oparg << 8 | next_instr->op.arg;
+ curr++;
+ next_instr++;
+ i += 2;
+ }
// Don't change opcode or oparg, let us handle it again.
DISPATCH_GOTO();
}
@@ -1558,6 +1561,17 @@ _PyTier2_Code_DetectAndEmitBB(
i += caches;
END();
}
+ if (opcode == EXTENDED_ARG) {
+ // Note: EXTENDED_ARG could be a jump target!!!!!
+ caches = _PyOpcode_Caches[opcode];
+ specop = next_instr->op.code;
+ opcode = _PyOpcode_Deopt[specop];
+ oparg = oparg << 8 | next_instr->op.arg;
+ curr++;
+ next_instr++;
+ i += 2;
+ DISPATCH_GOTO();
+ }
DISPATCH();
}
@@ -1691,11 +1705,12 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
_Py_CODEUNIT *start = _PyCode_CODE(co);
int curr_i = 0;
+ int oparg = 0;
for (Py_ssize_t i = 0; i < Py_SIZE(co); i++) {
_Py_CODEUNIT *curr = start + i;
- _Py_CODEUNIT instr = *curr;
- int opcode = _PyOpcode_Deopt[_Py_OPCODE(instr)];
- int oparg = _Py_OPARG(instr);
+ int opcode = _PyOpcode_Deopt[curr->op.code];
+ oparg = curr->op.arg;
+ dispatch_same_oparg:
if (IS_JUMP_BACKWARDS_OPCODE(opcode)) {
// + 1 because it's calculated from nextinstr (see JUMPBY in ceval.c)
_Py_CODEUNIT *target = curr + 1 - oparg;
@@ -1706,6 +1721,13 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
backward_jump_offsets[curr_i] = (int)(target - start);
curr_i++;
}
+ else if (opcode == EXTENDED_ARG) {
+ oparg = oparg << 8 | (curr+1)->op.arg;
+ opcode = _PyOpcode_Deopt[(curr+1)->op.code];
+ i++;
+ curr++;
+ goto dispatch_same_oparg;
+ }
i += _PyOpcode_Caches[opcode];
}
assert(curr_i == backwards_jump_count);
@@ -1868,7 +1890,7 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
fprintf(stderr, "INITIALIZING\n");
#endif
- Py_ssize_t space_to_alloc = (_PyCode_NBYTES(co)) * 3;
+ Py_ssize_t space_to_alloc = (_PyCode_NBYTES(co)) * OVERALLOCATE_FACTOR;
_PyTier2BBSpace *bb_space = _PyTier2_CreateBBSpace(space_to_alloc);
if (bb_space == NULL) {
@@ -2128,14 +2150,15 @@ diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
_Py_CODEUNIT *
_PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged, int jumpby,
- _Py_CODEUNIT **tier1_fallback)
+ _Py_CODEUNIT **tier1_fallback,
+ _Py_CODEUNIT *curr)
{
PyCodeObject *co = frame->f_code;
assert(co->_tier2_info != NULL);
assert(BB_ID(bb_id_tagged) <= co->_tier2_info->bb_data_curr);
_PyTier2BBMetadata *meta = co->_tier2_info->bb_data[BB_ID(bb_id_tagged)];
// The jump target
- _Py_CODEUNIT *tier1_jump_target = meta->tier1_end + jumpby;
+ _Py_CODEUNIT *tier1_jump_target = meta->tier1_end + jumpby - ((curr-1)->op.code == EXTENDED_ARG && (curr-1)->op.arg > 0);
*tier1_fallback = tier1_jump_target;
// Be a pessimist and assume we need to write the entire rest of code into the BB.
// The size of the BB generated will definitely be equal to or smaller than this.
From 10fd8ecd197382b8ab89b793f918af1803d26c06 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 30 Mar 2023 01:42:02 +0800
Subject: [PATCH 244/280] fix a small memory leak
---
Python/tier2.c | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 32c75f8eb65de4..188af5e6b88998 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1576,20 +1576,14 @@ _PyTier2_Code_DetectAndEmitBB(
}
}
- _PyTier2TypeContext *type_context_copy = NULL;
end:
// Create the tier 2 BB
- // Make a copy of the type context
- type_context_copy = _PyTier2TypeContext_Copy(starting_type_context);
- if (type_context_copy == NULL) {
- return NULL;
- }
temp_meta = _PyTier2_AllocateBBMetaData(co, t2_start,
// + 1 because we want to start with the NEXT instruction for the scan
- _PyCode_CODE(co) + i + 1, type_context_copy);
+ _PyCode_CODE(co) + i + 1, starting_type_context);
if (temp_meta == NULL) {
- _PyTier2TypeContext_Free(type_context_copy);
+ _PyTier2TypeContext_Free(starting_type_context);
return NULL;
}
// We need to return the first block to enter into. If there is already a block generated
@@ -1608,7 +1602,7 @@ _PyTier2_Code_DetectAndEmitBB(
if (meta != temp_meta) {
PyMem_Free(temp_meta);
}
- _PyTier2TypeContext_Free(type_context_copy);
+ _PyTier2TypeContext_Free(starting_type_context);
return NULL;
}
}
From c9d3b4981f61e28eb145030e8aa986e962e82184 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 30 Mar 2023 01:56:51 +0800
Subject: [PATCH 245/280] Add assert for type stacksize
---
Include/internal/pycore_code.h | 2 +-
Python/bytecodes.c | 3 ++-
Python/generated_cases.c.h | 3 ++-
Python/tier2.c | 6 ++++--
4 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 0f8d9f7808153f..863e6b383a4937 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -282,7 +282,7 @@ extern _Py_CODEUNIT *_PyTier2_GenerateNextBB(
char bb_flag);
extern _Py_CODEUNIT *_PyTier2_LocateJumpBackwardsBB(
struct _PyInterpreterFrame *frame, uint16_t bb_id, int jumpby,
- _Py_CODEUNIT **tier1_fallback, _Py_CODEUNIT *curr);
+ _Py_CODEUNIT **tier1_fallback, _Py_CODEUNIT *curr, int stacksize);
extern void _PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target);
extern void _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *target);
#ifdef Py_STATS
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 76bbb5e566567d..93ab5df1b3f171 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3276,7 +3276,8 @@ dummy_func(
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_LocateJumpBackwardsBB(
- frame, cache->bb_id_tagged, -oparg, &tier1_fallback, curr);
+ frame, cache->bb_id_tagged, -oparg, &tier1_fallback, curr,
+ STACK_LEVEL());
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 00601c8800259c..f3eca0b9832732 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -4153,7 +4153,8 @@
_Py_CODEUNIT *tier1_fallback = NULL;
t2_nextinstr = _PyTier2_LocateJumpBackwardsBB(
- frame, cache->bb_id_tagged, -oparg, &tier1_fallback, curr);
+ frame, cache->bb_id_tagged, -oparg, &tier1_fallback, curr,
+ STACK_LEVEL());
if (t2_nextinstr == NULL) {
// Fall back to tier 1.
next_instr = tier1_fallback;
diff --git a/Python/tier2.c b/Python/tier2.c
index 188af5e6b88998..20d40acc1b3d34 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1497,7 +1497,6 @@ _PyTier2_Code_DetectAndEmitBB(
goto fall_through;
}
if (IS_BACKWARDS_JUMP_TARGET(co, curr)) {
-backward_jump_target:
#if BB_DEBUG
fprintf(stderr, "Encountered a backward jump target\n");
#endif
@@ -2145,12 +2144,15 @@ diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
_Py_CODEUNIT *
_PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged, int jumpby,
_Py_CODEUNIT **tier1_fallback,
- _Py_CODEUNIT *curr)
+ _Py_CODEUNIT *curr, int stacklevel)
{
PyCodeObject *co = frame->f_code;
assert(co->_tier2_info != NULL);
assert(BB_ID(bb_id_tagged) <= co->_tier2_info->bb_data_curr);
_PyTier2BBMetadata *meta = co->_tier2_info->bb_data[BB_ID(bb_id_tagged)];
+ // We assert that there are as many items on the operand stack as there are in the
+ // saved type stack.
+ assert((meta->type_context->type_stack_ptr - meta->type_context->type_stack) == stacklevel);
// The jump target
_Py_CODEUNIT *tier1_jump_target = meta->tier1_end + jumpby - ((curr-1)->op.code == EXTENDED_ARG && (curr-1)->op.arg > 0);
*tier1_fallback = tier1_jump_target;
From a77cf568e1decd9bfc1222955a49da86e56f5099 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 30 Mar 2023 02:15:03 +0800
Subject: [PATCH 246/280] Fix an EXTENDED_ARG bug
---
Python/tier2.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 20d40acc1b3d34..cc4974e047e4e8 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1525,14 +1525,16 @@ _PyTier2_Code_DetectAndEmitBB(
virtual_start = true;
if (opcode == EXTENDED_ARG) {
+ write_i = emit_i(write_i, specop, oparg);
// Note: EXTENDED_ARG could be a jump target!!!!!
- caches = _PyOpcode_Caches[opcode];
specop = next_instr->op.code;
opcode = _PyOpcode_Deopt[specop];
+ caches = _PyOpcode_Caches[opcode];
oparg = oparg << 8 | next_instr->op.arg;
curr++;
next_instr++;
- i += 2;
+ i += 1;
+ DISPATCH_GOTO();
}
// Don't change opcode or oparg, let us handle it again.
DISPATCH_GOTO();
@@ -1561,14 +1563,15 @@ _PyTier2_Code_DetectAndEmitBB(
END();
}
if (opcode == EXTENDED_ARG) {
+ write_i = emit_i(write_i, specop, oparg);
// Note: EXTENDED_ARG could be a jump target!!!!!
- caches = _PyOpcode_Caches[opcode];
specop = next_instr->op.code;
opcode = _PyOpcode_Deopt[specop];
+ caches = _PyOpcode_Caches[opcode];
oparg = oparg << 8 | next_instr->op.arg;
curr++;
next_instr++;
- i += 2;
+ i += 1;
DISPATCH_GOTO();
}
DISPATCH();
@@ -2150,9 +2153,12 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
assert(co->_tier2_info != NULL);
assert(BB_ID(bb_id_tagged) <= co->_tier2_info->bb_data_curr);
_PyTier2BBMetadata *meta = co->_tier2_info->bb_data[BB_ID(bb_id_tagged)];
- // We assert that there are as many items on the operand stack as there are in the
+#ifdef Py_DEBUG
+ // We assert that there are as many items on the operand stack as there are on the
// saved type stack.
- assert((meta->type_context->type_stack_ptr - meta->type_context->type_stack) == stacklevel);
+ int typestack_level = meta->type_context->type_stack_ptr - meta->type_context->type_stack;
+ assert(typestack_level == stacklevel);
+#endif
// The jump target
_Py_CODEUNIT *tier1_jump_target = meta->tier1_end + jumpby - ((curr-1)->op.code == EXTENDED_ARG && (curr-1)->op.arg > 0);
*tier1_fallback = tier1_jump_target;
From 66143f9adfaca1a3880d64d391102f46280f2aa3 Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Thu, 30 Mar 2023 20:38:19 +0800
Subject: [PATCH 247/280] Fix: BB_TEST_ITER wrong type propagation (#31)
---
Include/internal/pycore_code.h | 8 ++++----
Python/bytecodes.c | 2 +-
Python/generated_cases.c.h | 2 +-
Python/tier2.c | 6 ++++--
4 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 863e6b383a4937..3bee8696ed5efb 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -261,15 +261,15 @@ extern int _PyStaticCode_Init(PyCodeObject *co);
// gen_bb_is_successor:
// true = successor
// false = alternate
-// gen_bb_requires_pop:
+// gen_bb_requires_pop (maximum 7):
// For tier2 type propagation, handling of jump instructions with
// runtime-dependent stack effect.
// This flag is used to determine if the type context of a new bb
// requires a stack element to be popped.
#define BB_TEST(gen_bb_is_successor, gen_bb_requires_pop) \
- (((gen_bb_is_successor) << 1) | (gen_bb_requires_pop))
-#define BB_TEST_IS_SUCCESSOR(bb_test) ((bb_test) >> 1)
-#define BB_TEST_IS_REQUIRES_POP(bb_test) ((bb_test) & 1)
+ (((gen_bb_is_successor) << 4) | (gen_bb_requires_pop))
+#define BB_TEST_IS_SUCCESSOR(bb_test) ((bb_test) >> 4)
+#define BB_TEST_GET_N_REQUIRES_POP(bb_test) ((bb_test) & 0b1111)
extern _Py_CODEUNIT *_PyCode_Tier2Warmup(struct _PyInterpreterFrame *,
_Py_CODEUNIT *);
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 93ab5df1b3f171..34ae3cbddd681c 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2190,7 +2190,7 @@ dummy_func(
/* iterator ended normally */
Py_DECREF(iter);
STACK_SHRINK(1);
- bb_test = BB_TEST(0, 1);
+ bb_test = BB_TEST(0, 2);
JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
DISPATCH();
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index f3eca0b9832732..0ef2014548b926 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2824,7 +2824,7 @@
/* iterator ended normally */
Py_DECREF(iter);
STACK_SHRINK(1);
- bb_test = BB_TEST(0, 1);
+ bb_test = BB_TEST(0, 2);
JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
DISPATCH();
}
diff --git a/Python/tier2.c b/Python/tier2.c
index cc4974e047e4e8..2bb36794c131d3 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1979,8 +1979,10 @@ _PyTier2_GenerateNextBBMetaWithTypeContext(
// DEOPTIMIZE TO TIER 1?
return NULL;
}
- if (BB_TEST_IS_REQUIRES_POP(bb_flag)) {
- __type_stack_shrink(&(type_context_copy->type_stack_ptr), 1);
+
+ int n_required_pop = BB_TEST_GET_N_REQUIRES_POP(bb_flag);
+ if (n_required_pop) {
+ __type_stack_shrink(&(type_context_copy->type_stack_ptr), n_required_pop);
}
// For type branches, they directly precede the bb branch instruction
_Py_CODEUNIT *prev_type_guard = BB_IS_TYPE_BRANCH(bb_id_tagged)
From f36e9d7a3711c1e9918e7f3b292cdabdcb051af3 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 30 Mar 2023 21:18:54 +0800
Subject: [PATCH 248/280] Fix more EXTENDED_ARG issues
---
Python/tier2.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 2bb36794c131d3..a5aa841bc40f4d 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -658,7 +658,7 @@ _PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_reque
// Note: overallocate
Py_ssize_t new_size = sizeof(_PyTier2BBSpace) + (curr->water_level + space_requested) * 2;
#if BB_DEBUG
- fprintf(stderr, "Allocating new BB of size %lld\n", (int64_t)new_size);
+ fprintf(stderr, "Space requested: %lld, Allocating new BB of size %lld\n", (int64_t)space_requested, (int64_t)new_size);
#endif
// @TODO We can't Realloc, we actually need to do the linked list method.
Py_UNREACHABLE();
@@ -1113,8 +1113,13 @@ emit_scope_exit(_Py_CODEUNIT *write_curr, _Py_CODEUNIT exit,
static inline _Py_CODEUNIT *
emit_i(_Py_CODEUNIT *write_curr, int opcode, int oparg)
{
+ if (oparg > 0xFF) {
+ _py_set_opcode(write_curr, EXTENDED_ARG);
+ write_curr->op.arg = (oparg >> 8) & 0xFF;
+ write_curr++;
+ }
_py_set_opcode(write_curr, opcode);
- write_curr->op.arg = oparg;
+ write_curr->op.arg = oparg & 0xFF;
write_curr++;
return write_curr;
}
@@ -1280,14 +1285,14 @@ _PyTier2_Code_DetectAndEmitBB(
);
#define END() goto end;
#define JUMPBY(x) i += x + 1;
-#define DISPATCH() write_i = emit_i(write_i, specop, oparg); \
+#define DISPATCH() write_i = emit_i(write_i, specop, curr->op.arg); \
write_i = copy_cache_entries(write_i, curr+1, caches); \
i += caches; \
type_propagate(opcode, oparg, starting_type_context, consts); \
continue;
#define DISPATCH_REBOX(x) write_i = rebox_stack(write_i, starting_type_context, x); \
- write_i = emit_i(write_i, specop, oparg); \
+ write_i = emit_i(write_i, specop, curr->op.arg); \
write_i = copy_cache_entries(write_i, curr+1, caches); \
i += caches; \
type_propagate(opcode, oparg, starting_type_context, consts); \
@@ -1525,7 +1530,6 @@ _PyTier2_Code_DetectAndEmitBB(
virtual_start = true;
if (opcode == EXTENDED_ARG) {
- write_i = emit_i(write_i, specop, oparg);
// Note: EXTENDED_ARG could be a jump target!!!!!
specop = next_instr->op.code;
opcode = _PyOpcode_Deopt[specop];
@@ -1563,7 +1567,6 @@ _PyTier2_Code_DetectAndEmitBB(
END();
}
if (opcode == EXTENDED_ARG) {
- write_i = emit_i(write_i, specop, oparg);
// Note: EXTENDED_ARG could be a jump target!!!!!
specop = next_instr->op.code;
opcode = _PyOpcode_Deopt[specop];
@@ -2162,7 +2165,7 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
assert(typestack_level == stacklevel);
#endif
// The jump target
- _Py_CODEUNIT *tier1_jump_target = meta->tier1_end + jumpby - ((curr-1)->op.code == EXTENDED_ARG && (curr-1)->op.arg > 0);
+ _Py_CODEUNIT *tier1_jump_target = meta->tier1_end + jumpby;
*tier1_fallback = tier1_jump_target;
// Be a pessimist and assume we need to write the entire rest of code into the BB.
// The size of the BB generated will definitely be equal to or smaller than this.
From 941fea048b53a874b7a3caa89a24790b321dabe0 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Thu, 30 Mar 2023 22:02:25 +0800
Subject: [PATCH 249/280] Add BB_TEST_ITER specialisations
---
Include/internal/pycore_code.h | 2 +-
Include/internal/pycore_opcode.h | 15 ++---
Include/opcode.h | 111 ++++++-------------------------
Lib/opcode.py | 5 ++
Python/bytecodes.c | 83 ++++++++++++++++++++++-
Python/generated_cases.c.h | 102 +++++++++++++++++++++++++++-
Python/opcode_metadata.h | 17 ++++-
Python/specialize.c | 16 ++---
Python/tier2.c | 2 +
Python/tier2_typepropagator.c.h | 18 +++++
Tools/build/generate_opcode_h.py | 5 +-
11 files changed, 263 insertions(+), 113 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index 3bee8696ed5efb..c62c39a6a080d6 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -248,7 +248,7 @@ extern void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs,
_Py_CODEUNIT *instr, int oparg);
extern void _Py_Specialize_UnpackSequence(PyObject *seq, _Py_CODEUNIT *instr,
int oparg);
-extern void _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg);
+extern void _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg, char is_bb);
extern void _Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr);
/* Finalizer function for static codeobjects used in deepfreeze.py */
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 155f8461021686..0a9dbd1623c4a1 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -412,6 +412,9 @@ static const char *const _PyOpcode_OpName[263] = {
[BB_JUMP_IF_FLAG_UNSET] = "BB_JUMP_IF_FLAG_UNSET",
[BB_JUMP_IF_FLAG_SET] = "BB_JUMP_IF_FLAG_SET",
[BB_TEST_ITER] = "BB_TEST_ITER",
+ [BB_TEST_ITER_RANGE] = "BB_TEST_ITER_RANGE",
+ [BB_TEST_ITER_LIST] = "BB_TEST_ITER_LIST",
+ [BB_TEST_ITER_TUPLE] = "BB_TEST_ITER_TUPLE",
[BB_TEST_POP_IF_FALSE] = "BB_TEST_POP_IF_FALSE",
[BB_TEST_POP_IF_TRUE] = "BB_TEST_POP_IF_TRUE",
[BB_TEST_POP_IF_NOT_NONE] = "BB_TEST_POP_IF_NOT_NONE",
@@ -422,6 +425,8 @@ static const char *const _PyOpcode_OpName[263] = {
[UNARY_CHECK_FLOAT] = "UNARY_CHECK_FLOAT",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
[BINARY_OP_ADD_FLOAT_UNBOXED] = "BINARY_OP_ADD_FLOAT_UNBOXED",
+ [BINARY_OP_SUBTRACT_FLOAT_UNBOXED] = "BINARY_OP_SUBTRACT_FLOAT_UNBOXED",
+ [BINARY_OP_MULTIPLY_FLOAT_UNBOXED] = "BINARY_OP_MULTIPLY_FLOAT_UNBOXED",
[POP_TOP_NO_DECREF] = "POP_TOP_NO_DECREF",
[UNBOX_FLOAT] = "UNBOX_FLOAT",
[BOX_FLOAT] = "BOX_FLOAT",
@@ -429,11 +434,6 @@ static const char *const _PyOpcode_OpName[263] = {
[STORE_FAST_BOXED_UNBOXED] = "STORE_FAST_BOXED_UNBOXED",
[STORE_FAST_UNBOXED_BOXED] = "STORE_FAST_UNBOXED_BOXED",
[STORE_FAST_UNBOXED_UNBOXED] = "STORE_FAST_UNBOXED_UNBOXED",
- [196] = "<196>",
- [197] = "<197>",
- [198] = "<198>",
- [199] = "<199>",
- [200] = "<200>",
[201] = "<201>",
[202] = "<202>",
[203] = "<203>",
@@ -501,11 +501,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 196: \
- case 197: \
- case 198: \
- case 199: \
- case 200: \
case 201: \
case 202: \
case 203: \
diff --git a/Include/opcode.h b/Include/opcode.h
index 6ae5375ed0dadc..eb4cb486cad590 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -161,9 +161,6 @@ extern "C" {
#define FOR_ITER_TUPLE 63
#define FOR_ITER_RANGE 64
#define FOR_ITER_GEN 65
-#define BB_TEST_ITER_LIST 66
-#define BB_TEST_ITER_TUPLE 67
-#define BB_TEST_ITER_RANGE 70
#define BB_TEST_ITER_GEN 72
#define LOAD_ATTR_CLASS 73
#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 76
@@ -193,98 +190,34 @@ extern "C" {
#define SEND_GEN 168
#define DO_TRACING 255
// Tier 2 interpreter ops
-#define RESUME_QUICK 5
-#define JUMP_BACKWARD_QUICK 6
-#define BINARY_OP_ADD_FLOAT 7
-#define BINARY_OP_ADD_INT 8
-#define BINARY_OP_ADD_UNICODE 10
-#define BINARY_OP_INPLACE_ADD_UNICODE 13
-#define BINARY_OP_MULTIPLY_FLOAT 14
-#define BINARY_OP_MULTIPLY_INT 16
-#define BINARY_OP_SUBTRACT_FLOAT 17
-#define BINARY_OP_SUBTRACT_INT 18
-#define BINARY_SUBSCR_DICT 19
-#define BINARY_SUBSCR_GETITEM 20
-#define BINARY_SUBSCR_LIST_INT 21
-#define BINARY_SUBSCR_TUPLE_INT 22
-#define CALL_PY_EXACT_ARGS 23
-#define CALL_PY_WITH_DEFAULTS 24
-#define CALL_BOUND_METHOD_EXACT_ARGS 28
-#define CALL_BUILTIN_CLASS 29
-#define CALL_BUILTIN_FAST_WITH_KEYWORDS 34
-#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 38
-#define CALL_NO_KW_BUILTIN_FAST 39
-#define CALL_NO_KW_BUILTIN_O 40
-#define CALL_NO_KW_ISINSTANCE 41
-#define CALL_NO_KW_LEN 42
-#define CALL_NO_KW_LIST_APPEND 43
-#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44
-#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45
-#define CALL_NO_KW_METHOD_DESCRIPTOR_O 46
-#define CALL_NO_KW_STR_1 47
-#define CALL_NO_KW_TUPLE_1 48
-#define CALL_NO_KW_TYPE_1 56
-#define COMPARE_OP_FLOAT 57
-#define COMPARE_OP_INT 58
-#define COMPARE_OP_STR 59
-#define FOR_ITER_LIST 62
-#define FOR_ITER_TUPLE 63
-#define FOR_ITER_RANGE 64
-#define FOR_ITER_GEN 65
-#define BB_TEST_ITER_LIST 66
-#define BB_TEST_ITER_TUPLE 67
-#define BB_TEST_ITER_RANGE 70
-#define BB_TEST_ITER_GEN 72
-#define LOAD_ATTR_CLASS 73
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 76
-#define LOAD_ATTR_INSTANCE_VALUE 77
-#define LOAD_ATTR_MODULE 78
-#define LOAD_ATTR_PROPERTY 79
-#define LOAD_ATTR_SLOT 80
-#define LOAD_ATTR_WITH_HINT 81
-#define LOAD_ATTR_METHOD_LAZY_DICT 82
-#define LOAD_ATTR_METHOD_NO_DICT 84
-#define LOAD_ATTR_METHOD_WITH_VALUES 86
-#define LOAD_CONST__LOAD_FAST 87
-#define LOAD_FAST__LOAD_CONST 88
-#define LOAD_FAST__LOAD_FAST 111
-#define LOAD_GLOBAL_BUILTIN 112
-#define LOAD_GLOBAL_MODULE 113
-#define STORE_ATTR_INSTANCE_VALUE 141
-#define STORE_ATTR_SLOT 143
-#define STORE_ATTR_WITH_HINT 153
-#define STORE_FAST__LOAD_FAST 154
-#define STORE_FAST__STORE_FAST 158
-#define STORE_SUBSCR_DICT 159
-#define STORE_SUBSCR_LIST_INT 160
-#define UNPACK_SEQUENCE_LIST 161
-#define UNPACK_SEQUENCE_TUPLE 166
-#define UNPACK_SEQUENCE_TWO_TUPLE 167
-#define SEND_GEN 168
-#define DO_TRACING 255
#define BB_BRANCH 169
#define BB_BRANCH_IF_FLAG_UNSET 170
#define BB_BRANCH_IF_FLAG_SET 175
#define BB_JUMP_IF_FLAG_UNSET 176
#define BB_JUMP_IF_FLAG_SET 177
#define BB_TEST_ITER 178
-#define BB_TEST_POP_IF_FALSE 179
-#define BB_TEST_POP_IF_TRUE 180
-#define BB_TEST_POP_IF_NOT_NONE 181
-#define BB_TEST_POP_IF_NONE 182
-#define BB_JUMP_BACKWARD_LAZY 183
-#define BINARY_CHECK_INT 184
-#define BINARY_CHECK_FLOAT 185
-#define UNARY_CHECK_FLOAT 186
-#define BINARY_OP_ADD_INT_REST 187
-#define BINARY_OP_ADD_FLOAT_UNBOXED 188
-#define POP_TOP_NO_DECREF 189
-#define UNBOX_FLOAT 190
-#define BOX_FLOAT 191
-#define LOAD_FAST_NO_INCREF 192
-#define STORE_FAST_BOXED_UNBOXED 193
-#define STORE_FAST_UNBOXED_BOXED 194
-#define STORE_FAST_UNBOXED_UNBOXED 195
+#define BB_TEST_ITER_RANGE 179
+#define BB_TEST_ITER_LIST 180
+#define BB_TEST_ITER_TUPLE 181
+#define BB_TEST_POP_IF_FALSE 182
+#define BB_TEST_POP_IF_TRUE 183
+#define BB_TEST_POP_IF_NOT_NONE 184
+#define BB_TEST_POP_IF_NONE 185
+#define BB_JUMP_BACKWARD_LAZY 186
+#define BINARY_CHECK_INT 187
+#define BINARY_CHECK_FLOAT 188
+#define UNARY_CHECK_FLOAT 189
+#define BINARY_OP_ADD_INT_REST 190
+#define BINARY_OP_ADD_FLOAT_UNBOXED 191
+#define BINARY_OP_SUBTRACT_FLOAT_UNBOXED 192
+#define BINARY_OP_MULTIPLY_FLOAT_UNBOXED 193
+#define POP_TOP_NO_DECREF 194
+#define UNBOX_FLOAT 195
+#define BOX_FLOAT 196
+#define LOAD_FAST_NO_INCREF 197
+#define STORE_FAST_BOXED_UNBOXED 198
+#define STORE_FAST_UNBOXED_BOXED 199
+#define STORE_FAST_UNBOXED_UNBOXED 200
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 5277f47b457262..54751c07d0145e 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -465,6 +465,9 @@ def pseudo_op(name, op, real_ops):
# These tests correspond to the jump instructions
# FOR_ITER's null (iterator) check
'BB_TEST_ITER',
+ 'BB_TEST_ITER_RANGE',
+ 'BB_TEST_ITER_LIST',
+ 'BB_TEST_ITER_TUPLE',
# POP_JUMP_IF_FALSE, POP_JUMP_IF_TRUE
'BB_TEST_POP_IF_FALSE',
'BB_TEST_POP_IF_TRUE',
@@ -490,6 +493,8 @@ def pseudo_op(name, op, real_ops):
# 'BINARY_CHECK_STR',
'BINARY_OP_ADD_INT_REST',
'BINARY_OP_ADD_FLOAT_UNBOXED',
+ 'BINARY_OP_SUBTRACT_FLOAT_UNBOXED',
+ 'BINARY_OP_MULTIPLY_FLOAT_UNBOXED',
# Boxing / unboxing ops
'POP_TOP_NO_DECREF',
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 34ae3cbddd681c..3c6901db7e10a6 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2145,7 +2145,7 @@ dummy_func(
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
next_instr--;
- _Py_Specialize_ForIter(iter, next_instr, oparg);
+ _Py_Specialize_ForIter(iter, next_instr, oparg, 0);
DISPATCH_SAME_OPARG();
}
STAT_INC(FOR_ITER, deferred);
@@ -2176,6 +2176,17 @@ dummy_func(
// FOR_ITER
inst(BB_TEST_ITER, (unused/1, iter -- iter, next)) {
+ #if ENABLE_SPECIALIZATION
+ _PyForIterCache *cache = (_PyForIterCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ next_instr--;
+ _Py_Specialize_ForIter(iter, next_instr, oparg, 1);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(BB_TEST_ITER, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
next = (*Py_TYPE(iter)->tp_iternext)(iter);
if (next == NULL) {
if (_PyErr_Occurred(tstate)) {
@@ -2220,6 +2231,30 @@ dummy_func(
// Common case: no jump, leave it to the code generator
}
+ inst(BB_TEST_ITER_LIST, (unused/1, iter -- iter, next)) {
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, BB_TEST_ITER);
+ _PyListIterObject *it = (_PyListIterObject *)iter;
+ STAT_INC(FOR_ITER, hit);
+ PyListObject *seq = it->it_seq;
+ if (seq) {
+ if (it->it_index < PyList_GET_SIZE(seq)) {
+ next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++));
+ goto end_bb_iter_list; // End of this instruction
+ }
+ it->it_seq = NULL;
+ Py_DECREF(seq);
+ }
+ Py_DECREF(iter);
+ STACK_SHRINK(1);
+ bb_test = BB_TEST(0, 2);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ DISPATCH();
+ end_bb_iter_list:
+ // Common case: no jump, leave it to the code generator
+ bb_test = BB_TEST(1, 0);
+ }
+
inst(FOR_ITER_TUPLE, (unused/1, iter -- iter, next)) {
assert(cframe.use_tracing == 0);
_PyTupleIterObject *it = (_PyTupleIterObject *)iter;
@@ -2243,6 +2278,30 @@ dummy_func(
// Common case: no jump, leave it to the code generator
}
+ inst(BB_TEST_ITER_TUPLE, (unused/1, iter -- iter, next)) {
+ assert(cframe.use_tracing == 0);
+ _PyTupleIterObject *it = (_PyTupleIterObject *)iter;
+ DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, BB_TEST_ITER);
+ STAT_INC(FOR_ITER, hit);
+ PyTupleObject *seq = it->it_seq;
+ if (seq) {
+ if (it->it_index < PyTuple_GET_SIZE(seq)) {
+ next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++));
+ goto end_test_iter_tuple; // End of this instruction
+ }
+ it->it_seq = NULL;
+ Py_DECREF(seq);
+ }
+ Py_DECREF(iter);
+ STACK_SHRINK(1);
+ bb_test = BB_TEST(0, 2);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ DISPATCH();
+ end_test_iter_tuple:
+ // Common case: no jump, leave it to the code generator
+ bb_test = BB_TEST(1, 0);
+ }
+
inst(FOR_ITER_RANGE, (unused/1, iter -- iter, next)) {
assert(cframe.use_tracing == 0);
_PyRangeIterObject *r = (_PyRangeIterObject *)iter;
@@ -2264,6 +2323,28 @@ dummy_func(
}
}
+ inst(BB_TEST_ITER_RANGE, (unused / 1, iter -- iter, next)) {
+ assert(cframe.use_tracing == 0);
+ _PyRangeIterObject *r = (_PyRangeIterObject *)iter;
+ DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, BB_TEST_ITER);
+ STAT_INC(FOR_ITER, hit);
+ if (r->len <= 0) {
+ STACK_SHRINK(1);
+ Py_DECREF(r);
+ bb_test = BB_TEST(0, 2);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ DISPATCH();
+ }
+ long value = r->start;
+ r->start = value + r->step;
+ r->len--;
+ next = PyLong_FromLong(value);
+ if (next == NULL) {
+ goto error;
+ }
+ bb_test = BB_TEST(1, 0);
+ }
+
inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) {
assert(cframe.use_tracing == 0);
PyGenObject *gen = (PyGenObject *)iter;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 0ef2014548b926..8190fe7a4a5403 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2774,7 +2774,7 @@
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
next_instr--;
- _Py_Specialize_ForIter(iter, next_instr, oparg);
+ _Py_Specialize_ForIter(iter, next_instr, oparg, 0);
DISPATCH_SAME_OPARG();
}
STAT_INC(FOR_ITER, deferred);
@@ -2808,8 +2808,20 @@
}
TARGET(BB_TEST_ITER) {
+ PREDICTED(BB_TEST_ITER);
PyObject *iter = stack_pointer[-1];
PyObject *next;
+ #if ENABLE_SPECIALIZATION
+ _PyForIterCache *cache = (_PyForIterCache *)next_instr;
+ if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
+ assert(cframe.use_tracing == 0);
+ next_instr--;
+ _Py_Specialize_ForIter(iter, next_instr, oparg, 1);
+ DISPATCH_SAME_OPARG();
+ }
+ STAT_INC(BB_TEST_ITER, deferred);
+ DECREMENT_ADAPTIVE_COUNTER(cache->counter);
+ #endif /* ENABLE_SPECIALIZATION */
next = (*Py_TYPE(iter)->tp_iternext)(iter);
if (next == NULL) {
if (_PyErr_Occurred(tstate)) {
@@ -2864,6 +2876,36 @@
DISPATCH();
}
+ TARGET(BB_TEST_ITER_LIST) {
+ PyObject *iter = stack_pointer[-1];
+ PyObject *next;
+ assert(cframe.use_tracing == 0);
+ DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, BB_TEST_ITER);
+ _PyListIterObject *it = (_PyListIterObject *)iter;
+ STAT_INC(FOR_ITER, hit);
+ PyListObject *seq = it->it_seq;
+ if (seq) {
+ if (it->it_index < PyList_GET_SIZE(seq)) {
+ next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++));
+ goto end_bb_iter_list; // End of this instruction
+ }
+ it->it_seq = NULL;
+ Py_DECREF(seq);
+ }
+ Py_DECREF(iter);
+ STACK_SHRINK(1);
+ bb_test = BB_TEST(0, 2);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ DISPATCH();
+ end_bb_iter_list:
+ // Common case: no jump, leave it to the code generator
+ bb_test = BB_TEST(1, 0);
+ STACK_GROW(1);
+ stack_pointer[-1] = next;
+ next_instr += 1;
+ DISPATCH();
+ }
+
TARGET(FOR_ITER_TUPLE) {
PyObject *iter = stack_pointer[-1];
PyObject *next;
@@ -2893,6 +2935,36 @@
DISPATCH();
}
+ TARGET(BB_TEST_ITER_TUPLE) {
+ PyObject *iter = stack_pointer[-1];
+ PyObject *next;
+ assert(cframe.use_tracing == 0);
+ _PyTupleIterObject *it = (_PyTupleIterObject *)iter;
+ DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, BB_TEST_ITER);
+ STAT_INC(FOR_ITER, hit);
+ PyTupleObject *seq = it->it_seq;
+ if (seq) {
+ if (it->it_index < PyTuple_GET_SIZE(seq)) {
+ next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++));
+ goto end_test_iter_tuple; // End of this instruction
+ }
+ it->it_seq = NULL;
+ Py_DECREF(seq);
+ }
+ Py_DECREF(iter);
+ STACK_SHRINK(1);
+ bb_test = BB_TEST(0, 2);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ DISPATCH();
+ end_test_iter_tuple:
+ // Common case: no jump, leave it to the code generator
+ bb_test = BB_TEST(1, 0);
+ STACK_GROW(1);
+ stack_pointer[-1] = next;
+ next_instr += 1;
+ DISPATCH();
+ }
+
TARGET(FOR_ITER_RANGE) {
PyObject *iter = stack_pointer[-1];
PyObject *next;
@@ -2920,6 +2992,34 @@
DISPATCH();
}
+ TARGET(BB_TEST_ITER_RANGE) {
+ PyObject *iter = stack_pointer[-1];
+ PyObject *next;
+ assert(cframe.use_tracing == 0);
+ _PyRangeIterObject *r = (_PyRangeIterObject *)iter;
+ DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, BB_TEST_ITER);
+ STAT_INC(FOR_ITER, hit);
+ if (r->len <= 0) {
+ STACK_SHRINK(1);
+ Py_DECREF(r);
+ bb_test = BB_TEST(0, 2);
+ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER);
+ DISPATCH();
+ }
+ long value = r->start;
+ r->start = value + r->step;
+ r->len--;
+ next = PyLong_FromLong(value);
+ if (next == NULL) {
+ goto error;
+ }
+ bb_test = BB_TEST(1, 0);
+ STACK_GROW(1);
+ stack_pointer[-1] = next;
+ next_instr += 1;
+ DISPATCH();
+ }
+
TARGET(FOR_ITER_GEN) {
PyObject *iter = stack_pointer[-1];
assert(cframe.use_tracing == 0);
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 6f77f7c5d4e67c..1bb9f7b2926a4b 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -303,10 +303,16 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 1;
case FOR_ITER_LIST:
return 1;
+ case BB_TEST_ITER_LIST:
+ return 1;
case FOR_ITER_TUPLE:
return 1;
+ case BB_TEST_ITER_TUPLE:
+ return 1;
case FOR_ITER_RANGE:
return 1;
+ case BB_TEST_ITER_RANGE:
+ return 1;
case FOR_ITER_GEN:
return 1;
case BEFORE_ASYNC_WITH:
@@ -699,10 +705,16 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 2;
case FOR_ITER_LIST:
return 2;
+ case BB_TEST_ITER_LIST:
+ return 2;
case FOR_ITER_TUPLE:
return 2;
+ case BB_TEST_ITER_TUPLE:
+ return 2;
case FOR_ITER_RANGE:
return 2;
+ case BB_TEST_ITER_RANGE:
+ return 2;
case FOR_ITER_GEN:
return 2;
case BEFORE_ASYNC_WITH:
@@ -950,10 +962,13 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[GET_ITER] = { true, INSTR_FMT_IX },
[GET_YIELD_FROM_ITER] = { true, INSTR_FMT_IX },
[FOR_ITER] = { true, INSTR_FMT_IBC },
- [BB_TEST_ITER] = { true, INSTR_FMT_IXC },
+ [BB_TEST_ITER] = { true, INSTR_FMT_IBC },
[FOR_ITER_LIST] = { true, INSTR_FMT_IBC },
+ [BB_TEST_ITER_LIST] = { true, INSTR_FMT_IXC },
[FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC },
+ [BB_TEST_ITER_TUPLE] = { true, INSTR_FMT_IXC },
[FOR_ITER_RANGE] = { true, INSTR_FMT_IBC },
+ [BB_TEST_ITER_RANGE] = { true, INSTR_FMT_IXC },
[FOR_ITER_GEN] = { true, INSTR_FMT_IBC },
[BEFORE_ASYNC_WITH] = { true, INSTR_FMT_IX },
[BEFORE_WITH] = { true, INSTR_FMT_IX },
diff --git a/Python/specialize.c b/Python/specialize.c
index f27951613bfc7a..b9e450c74efffd 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -2135,22 +2135,22 @@ int
#endif
void
-_Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg)
+_Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg, char is_bb)
{
assert(ENABLE_SPECIALIZATION);
assert(_PyOpcode_Caches[FOR_ITER] == INLINE_CACHE_ENTRIES_FOR_ITER);
_PyForIterCache *cache = (_PyForIterCache *)(instr + 1);
PyTypeObject *tp = Py_TYPE(iter);
if (tp == &PyListIter_Type) {
- instr->op.code = FOR_ITER_LIST;
+ instr->op.code = is_bb ? BB_TEST_ITER_LIST : FOR_ITER_LIST;
goto success;
}
else if (tp == &PyTupleIter_Type) {
- instr->op.code = FOR_ITER_TUPLE;
+ instr->op.code = is_bb ? BB_TEST_ITER_TUPLE : FOR_ITER_TUPLE;
goto success;
}
else if (tp == &PyRangeIter_Type) {
- instr->op.code = FOR_ITER_RANGE;
+ instr->op.code = is_bb ? BB_TEST_ITER_RANGE : FOR_ITER_RANGE;
goto success;
}
//else if (tp == &PyGen_Type && oparg <= SHRT_MAX) {
@@ -2158,14 +2158,14 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg)
// instr->op.code = FOR_ITER_GEN;
// goto success;
//}
- SPECIALIZATION_FAIL(FOR_ITER,
+ SPECIALIZATION_FAIL(is_bb ? BB_TEST_ITER : FOR_ITER,
_PySpecialization_ClassifyIterator(iter));
- STAT_INC(FOR_ITER, failure);
- instr->op.code = FOR_ITER;
+ STAT_INC(is_bb ? BB_TEST_ITER : FOR_ITER, failure);
+ instr->op.code = is_bb ? BB_TEST_ITER : FOR_ITER;
cache->counter = adaptive_counter_backoff(cache->counter);
return;
success:
- STAT_INC(FOR_ITER, success);
+ STAT_INC(is_bb ? BB_TEST_ITER : FOR_ITER, success);
cache->counter = adaptive_counter_cooldown();
}
diff --git a/Python/tier2.c b/Python/tier2.c
index a5aa841bc40f4d..7a1e11a241398a 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1043,6 +1043,8 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
_py_set_opcode(write_curr, BB_TEST_ITER);
write_curr->op.arg = oparg & 0xFF;
write_curr++;
+ // Initialize adaptive interpreter counter
+ write_curr->cache = adaptive_counter_warmup();
write_curr = emit_cache_entries(write_curr, INLINE_CACHE_ENTRIES_FOR_ITER);
type_propagate(BB_TEST_ITER, oparg, type_context, NULL);
_py_set_opcode(write_curr, requires_extended_arg ? EXTENDED_ARG : NOP);
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 9c645df9007b0b..13093092ba8a70 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -734,18 +734,36 @@
break;
}
+ TARGET(BB_TEST_ITER_LIST) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
TARGET(FOR_ITER_TUPLE) {
STACK_GROW(1);
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
+ TARGET(BB_TEST_ITER_TUPLE) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
TARGET(FOR_ITER_RANGE) {
STACK_GROW(1);
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
break;
}
+ TARGET(BB_TEST_ITER_RANGE) {
+ STACK_GROW(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
TARGET(FOR_ITER_GEN) {
STACK_GROW(1);
break;
diff --git a/Tools/build/generate_opcode_h.py b/Tools/build/generate_opcode_h.py
index d3889f5b67fd7f..a8f3d5460f11f7 100644
--- a/Tools/build/generate_opcode_h.py
+++ b/Tools/build/generate_opcode_h.py
@@ -112,7 +112,7 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
# The Tier 2 ops
next_op = 1
- uop_opmap = specialized_opmap.copy()
+ uop_opmap = {}
# Add microops
for name in opcode['_uops']:
while used[next_op]:
@@ -141,7 +141,8 @@ def main(opcode_py, outfile='Include/opcode.h', internaloutfile='Include/interna
fobj.write(DEFINE.format("MAX_PSEUDO_OPCODE", MAX_PSEUDO_OPCODE))
for name, op in specialized_opmap.items():
- fobj.write(DEFINE.format(name, op))
+ if name not in uop_opmap:
+ fobj.write(DEFINE.format(name, op))
# Tier 2 opcodes
fobj.write("// Tier 2 interpreter ops\n")
From 3608d6b7e00394247500665d7d93083183863121 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 31 Mar 2023 00:27:31 +0800
Subject: [PATCH 250/280] Add unboxed float ops for multiply and subtract
---
Include/internal/pycore_opcode.h | 9 +--
Include/opcode.h | 21 +++---
Lib/opcode.py | 5 ++
Python/bytecodes.c | 33 ++++++++-
Python/generated_cases.c.h | 80 ++++++++++++++++++++-
Python/opcode_metadata.h | 25 +++++++
Python/tier2.c | 117 ++++++++++++++++++++++++++-----
Python/tier2_typepropagator.c.h | 34 ++++++++-
8 files changed, 285 insertions(+), 39 deletions(-)
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 0a9dbd1623c4a1..71491b83e71a56 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -425,18 +425,18 @@ static const char *const _PyOpcode_OpName[263] = {
[UNARY_CHECK_FLOAT] = "UNARY_CHECK_FLOAT",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
[BINARY_OP_ADD_FLOAT_UNBOXED] = "BINARY_OP_ADD_FLOAT_UNBOXED",
+ [BINARY_OP_SUBTRACT_INT_REST] = "BINARY_OP_SUBTRACT_INT_REST",
[BINARY_OP_SUBTRACT_FLOAT_UNBOXED] = "BINARY_OP_SUBTRACT_FLOAT_UNBOXED",
+ [BINARY_OP_MULTIPLY_INT_REST] = "BINARY_OP_MULTIPLY_INT_REST",
[BINARY_OP_MULTIPLY_FLOAT_UNBOXED] = "BINARY_OP_MULTIPLY_FLOAT_UNBOXED",
[POP_TOP_NO_DECREF] = "POP_TOP_NO_DECREF",
[UNBOX_FLOAT] = "UNBOX_FLOAT",
[BOX_FLOAT] = "BOX_FLOAT",
+ [COPY_NO_INCREF] = "COPY_NO_INCREF",
[LOAD_FAST_NO_INCREF] = "LOAD_FAST_NO_INCREF",
[STORE_FAST_BOXED_UNBOXED] = "STORE_FAST_BOXED_UNBOXED",
[STORE_FAST_UNBOXED_BOXED] = "STORE_FAST_UNBOXED_BOXED",
[STORE_FAST_UNBOXED_UNBOXED] = "STORE_FAST_UNBOXED_UNBOXED",
- [201] = "<201>",
- [202] = "<202>",
- [203] = "<203>",
[204] = "<204>",
[205] = "<205>",
[206] = "<206>",
@@ -501,9 +501,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 201: \
- case 202: \
- case 203: \
case 204: \
case 205: \
case 206: \
diff --git a/Include/opcode.h b/Include/opcode.h
index eb4cb486cad590..c0c5a79fd9da38 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -209,15 +209,18 @@ extern "C" {
#define UNARY_CHECK_FLOAT 189
#define BINARY_OP_ADD_INT_REST 190
#define BINARY_OP_ADD_FLOAT_UNBOXED 191
-#define BINARY_OP_SUBTRACT_FLOAT_UNBOXED 192
-#define BINARY_OP_MULTIPLY_FLOAT_UNBOXED 193
-#define POP_TOP_NO_DECREF 194
-#define UNBOX_FLOAT 195
-#define BOX_FLOAT 196
-#define LOAD_FAST_NO_INCREF 197
-#define STORE_FAST_BOXED_UNBOXED 198
-#define STORE_FAST_UNBOXED_BOXED 199
-#define STORE_FAST_UNBOXED_UNBOXED 200
+#define BINARY_OP_SUBTRACT_INT_REST 192
+#define BINARY_OP_SUBTRACT_FLOAT_UNBOXED 193
+#define BINARY_OP_MULTIPLY_INT_REST 194
+#define BINARY_OP_MULTIPLY_FLOAT_UNBOXED 195
+#define POP_TOP_NO_DECREF 196
+#define UNBOX_FLOAT 197
+#define BOX_FLOAT 198
+#define COPY_NO_INCREF 199
+#define LOAD_FAST_NO_INCREF 200
+#define STORE_FAST_BOXED_UNBOXED 201
+#define STORE_FAST_UNBOXED_BOXED 202
+#define STORE_FAST_UNBOXED_UNBOXED 203
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 54751c07d0145e..a3d60584a877d8 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -443,6 +443,8 @@ def pseudo_op(name, op, real_ops):
_macro_ops = [
'BINARY_OP_ADD_INT',
+ 'BINARY_OP_SUBTRACT_INT',
+ 'BINARY_OP_MULTIPLY_INT',
'BINARY_OP_ADD_FLOAT',
]
_uops = [
@@ -493,13 +495,16 @@ def pseudo_op(name, op, real_ops):
# 'BINARY_CHECK_STR',
'BINARY_OP_ADD_INT_REST',
'BINARY_OP_ADD_FLOAT_UNBOXED',
+ 'BINARY_OP_SUBTRACT_INT_REST',
'BINARY_OP_SUBTRACT_FLOAT_UNBOXED',
+ 'BINARY_OP_MULTIPLY_INT_REST',
'BINARY_OP_MULTIPLY_FLOAT_UNBOXED',
# Boxing / unboxing ops
'POP_TOP_NO_DECREF',
'UNBOX_FLOAT',
'BOX_FLOAT',
+ 'COPY_NO_INCREF',
'LOAD_FAST_NO_INCREF',
# Storing a boxed value, overwriting an unboxed local.
'STORE_FAST_BOXED_UNBOXED',
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 3c6901db7e10a6..7b3e11a7799373 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -203,10 +203,14 @@ dummy_func(
};
- inst(BINARY_OP_MULTIPLY_INT, (unused/1, left, right -- prod)) {
+ macro_inst(BINARY_OP_MULTIPLY_INT, (unused/1, left, right -- prod)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
+ U_INST(BINARY_OP_MULTIPLY_INT_REST);
+ }
+
+ u_inst(BINARY_OP_MULTIPLY_INT_REST, (left, right -- prod : PyLong_Type)) {
STAT_INC(BINARY_OP, hit);
prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
@@ -224,10 +228,14 @@ dummy_func(
DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dprod, prod);
}
- inst(BINARY_OP_SUBTRACT_INT, (unused/1, left, right -- sub)) {
+ macro_inst(BINARY_OP_SUBTRACT_INT, (unused/1, left, right -- sub)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
+ U_INST(BINARY_OP_SUBTRACT_INT_REST);
+ }
+
+ u_inst(BINARY_OP_SUBTRACT_INT_REST, (left, right -- sub : PyLong_Type)) {
STAT_INC(BINARY_OP, hit);
sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
@@ -318,10 +326,13 @@ dummy_func(
: right);
}
- inst(UNARY_CHECK_FLOAT, (arg, unused[oparg] -- arg : PyFloat_Type, unused[oparg])) {
+ inst(UNARY_CHECK_FLOAT, (arg, unused[oparg] -- arg_unboxed : { <<= PyFloat_Type, PyRawFloat_Type}, unused[oparg])) {
assert(cframe.use_tracing == 0);
char is_successor = PyFloat_CheckExact(arg);
bb_test = BB_TEST(is_successor, 0);
+ arg_unboxed = (is_successor
+ ? *((PyObject **)(&(((PyFloatObject *)arg)->ob_fval)))
+ : arg);
}
inst(BINARY_OP_ADD_FLOAT_UNBOXED, (left, right -- sum : PyRawFloat_Type)) {
@@ -330,6 +341,18 @@ dummy_func(
sum = *(PyObject **)(&temp);
}
+ inst(BINARY_OP_SUBTRACT_FLOAT_UNBOXED, (left, right -- sum : PyRawFloat_Type)) {
+ STAT_INC(BINARY_OP, hit);
+ double temp = *(double *)(&(left)) - *(double *)(&(right));
+ sum = *(PyObject **)(&temp);
+ }
+
+ inst(BINARY_OP_MULTIPLY_FLOAT_UNBOXED, (left, right -- prod : PyRawFloat_Type)) {
+ STAT_INC(BINARY_OP, hit);
+ double temp = *(double *)(&(left)) * *(double *)(&(right));
+ prod = *(PyObject **)(&temp);
+ }
+
inst(UNBOX_FLOAT, (boxed_float, unused[oparg] -- unboxed_float : PyRawFloat_Type, unused[oparg])) {
double temp = ((PyFloatObject *)boxed_float)->ob_fval;
Py_DECREF(boxed_float);
@@ -3212,6 +3235,10 @@ dummy_func(
top = Py_NewRef(bottom);
}
+ inst(COPY_NO_INCREF, (bottom, unused[oparg - 1] -- bottom, unused[oparg - 1], top: *bottom)) {
+ assert(oparg > 0);
+ }
+
inst(BINARY_OP, (unused/1, lhs, rhs -- res)) {
#if ENABLE_SPECIALIZATION
_PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 8190fe7a4a5403..455fb6ee3a2520 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -3,6 +3,24 @@
// Python/bytecodes.c
// Do not edit!
+ #define UOP_BINARY_OP_MULTIPLY_INT_REST() \
+ do { \
+ STAT_INC(BINARY_OP, hit);\
+ prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right);\
+ _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);\
+ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);\
+ if (prod == NULL) goto pop_2_error;\
+ } while (0)
+
+ #define UOP_BINARY_OP_SUBTRACT_INT_REST() \
+ do { \
+ STAT_INC(BINARY_OP, hit);\
+ sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right);\
+ _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);\
+ _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);\
+ if (sub == NULL) goto pop_2_error;\
+ } while (0)
+
#define UOP_BINARY_OP_ADD_INT_REST() \
do { \
STAT_INC(BINARY_OP, hit);\
@@ -296,6 +314,17 @@
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
+ UOP_BINARY_OP_MULTIPLY_INT_REST();
+ STACK_SHRINK(1);
+ stack_pointer[-1] = prod;
+ next_instr += 1;
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_MULTIPLY_INT_REST) {
+ PyObject *right = stack_pointer[-1];
+ PyObject *left = stack_pointer[-2];
+ PyObject *prod;
STAT_INC(BINARY_OP, hit);
prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
@@ -303,7 +332,6 @@
if (prod == NULL) goto pop_2_error;
STACK_SHRINK(1);
stack_pointer[-1] = prod;
- next_instr += 1;
DISPATCH();
}
@@ -331,6 +359,17 @@
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
+ UOP_BINARY_OP_SUBTRACT_INT_REST();
+ STACK_SHRINK(1);
+ stack_pointer[-1] = sub;
+ next_instr += 1;
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_SUBTRACT_INT_REST) {
+ PyObject *right = stack_pointer[-1];
+ PyObject *left = stack_pointer[-2];
+ PyObject *sub;
STAT_INC(BINARY_OP, hit);
sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
@@ -338,7 +377,6 @@
if (sub == NULL) goto pop_2_error;
STACK_SHRINK(1);
stack_pointer[-1] = sub;
- next_instr += 1;
DISPATCH();
}
@@ -449,9 +487,14 @@
TARGET(UNARY_CHECK_FLOAT) {
PyObject *arg = stack_pointer[-(1 + oparg)];
+ PyObject *arg_unboxed;
assert(cframe.use_tracing == 0);
char is_successor = PyFloat_CheckExact(arg);
bb_test = BB_TEST(is_successor, 0);
+ arg_unboxed = (is_successor
+ ? *((PyObject **)(&(((PyFloatObject *)arg)->ob_fval)))
+ : arg);
+ stack_pointer[-(1 + oparg)] = arg_unboxed;
DISPATCH();
}
@@ -467,6 +510,30 @@
DISPATCH();
}
+ TARGET(BINARY_OP_SUBTRACT_FLOAT_UNBOXED) {
+ PyObject *right = stack_pointer[-1];
+ PyObject *left = stack_pointer[-2];
+ PyObject *sum;
+ STAT_INC(BINARY_OP, hit);
+ double temp = *(double *)(&(left)) - *(double *)(&(right));
+ sum = *(PyObject **)(&temp);
+ STACK_SHRINK(1);
+ stack_pointer[-1] = sum;
+ DISPATCH();
+ }
+
+ TARGET(BINARY_OP_MULTIPLY_FLOAT_UNBOXED) {
+ PyObject *right = stack_pointer[-1];
+ PyObject *left = stack_pointer[-2];
+ PyObject *prod;
+ STAT_INC(BINARY_OP, hit);
+ double temp = *(double *)(&(left)) * *(double *)(&(right));
+ prod = *(PyObject **)(&temp);
+ STACK_SHRINK(1);
+ stack_pointer[-1] = prod;
+ DISPATCH();
+ }
+
TARGET(UNBOX_FLOAT) {
PyObject *boxed_float = stack_pointer[-(1 + oparg)];
PyObject *unboxed_float;
@@ -4089,6 +4156,15 @@
DISPATCH();
}
+ TARGET(COPY_NO_INCREF) {
+ PyObject *bottom = stack_pointer[-(1 + (oparg - 1))];
+ PyObject *top;
+ assert(oparg > 0);
+ STACK_GROW(1);
+ stack_pointer[-1] = top;
+ DISPATCH();
+ }
+
TARGET(BINARY_OP) {
PREDICTED(BINARY_OP);
static_assert(INLINE_CACHE_ENTRIES_BINARY_OP == 1, "incorrect cache size");
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 1bb9f7b2926a4b..7f7200503960f6 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -59,10 +59,14 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 1;
case BINARY_OP_MULTIPLY_INT:
return 2;
+ case BINARY_OP_MULTIPLY_INT_REST:
+ return 2;
case BINARY_OP_MULTIPLY_FLOAT:
return 2;
case BINARY_OP_SUBTRACT_INT:
return 2;
+ case BINARY_OP_SUBTRACT_INT_REST:
+ return 2;
case BINARY_OP_SUBTRACT_FLOAT:
return 2;
case BINARY_OP_ADD_UNICODE:
@@ -77,6 +81,10 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return oparg + 1;
case BINARY_OP_ADD_FLOAT_UNBOXED:
return 2;
+ case BINARY_OP_SUBTRACT_FLOAT_UNBOXED:
+ return 2;
+ case BINARY_OP_MULTIPLY_FLOAT_UNBOXED:
+ return 2;
case UNBOX_FLOAT:
return oparg + 1;
case BOX_FLOAT:
@@ -379,6 +387,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return (((oparg & FVS_MASK) == FVS_HAVE_SPEC) ? 1 : 0) + 1;
case COPY:
return (oparg-1) + 1;
+ case COPY_NO_INCREF:
+ return (oparg - 1) + 1;
case BINARY_OP:
return 2;
case SWAP:
@@ -461,10 +471,14 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 1;
case BINARY_OP_MULTIPLY_INT:
return 1;
+ case BINARY_OP_MULTIPLY_INT_REST:
+ return 1;
case BINARY_OP_MULTIPLY_FLOAT:
return 1;
case BINARY_OP_SUBTRACT_INT:
return 1;
+ case BINARY_OP_SUBTRACT_INT_REST:
+ return 1;
case BINARY_OP_SUBTRACT_FLOAT:
return 1;
case BINARY_OP_ADD_UNICODE:
@@ -479,6 +493,10 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return oparg + 1;
case BINARY_OP_ADD_FLOAT_UNBOXED:
return 1;
+ case BINARY_OP_SUBTRACT_FLOAT_UNBOXED:
+ return 1;
+ case BINARY_OP_MULTIPLY_FLOAT_UNBOXED:
+ return 1;
case UNBOX_FLOAT:
return oparg + 1;
case BOX_FLOAT:
@@ -781,6 +799,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 1;
case COPY:
return (oparg-1) + 2;
+ case COPY_NO_INCREF:
+ return (oparg - 1) + 2;
case BINARY_OP:
return 1;
case SWAP:
@@ -842,8 +862,10 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[UNARY_NOT] = { true, INSTR_FMT_IX },
[UNARY_INVERT] = { true, INSTR_FMT_IX },
[BINARY_OP_MULTIPLY_INT] = { true, INSTR_FMT_IXC },
+ [BINARY_OP_MULTIPLY_INT_REST] = { true, INSTR_FMT_IX },
[BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC },
[BINARY_OP_SUBTRACT_INT] = { true, INSTR_FMT_IXC },
+ [BINARY_OP_SUBTRACT_INT_REST] = { true, INSTR_FMT_IX },
[BINARY_OP_SUBTRACT_FLOAT] = { true, INSTR_FMT_IXC },
[BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC },
[BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IX },
@@ -851,6 +873,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[BINARY_CHECK_FLOAT] = { true, INSTR_FMT_IX },
[UNARY_CHECK_FLOAT] = { true, INSTR_FMT_IB },
[BINARY_OP_ADD_FLOAT_UNBOXED] = { true, INSTR_FMT_IX },
+ [BINARY_OP_SUBTRACT_FLOAT_UNBOXED] = { true, INSTR_FMT_IX },
+ [BINARY_OP_MULTIPLY_FLOAT_UNBOXED] = { true, INSTR_FMT_IX },
[UNBOX_FLOAT] = { true, INSTR_FMT_IB },
[BOX_FLOAT] = { true, INSTR_FMT_IB },
[BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC },
@@ -1002,6 +1026,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[BUILD_SLICE] = { true, INSTR_FMT_IB },
[FORMAT_VALUE] = { true, INSTR_FMT_IB },
[COPY] = { true, INSTR_FMT_IB },
+ [COPY_NO_INCREF] = { true, INSTR_FMT_IB },
[BINARY_OP] = { true, INSTR_FMT_IBC },
[SWAP] = { true, INSTR_FMT_IB },
[EXTENDED_ARG] = { true, INSTR_FMT_IB },
diff --git a/Python/tier2.c b/Python/tier2.c
index 7a1e11a241398a..41d3521a606e3b 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -11,9 +11,9 @@
#define BB_DEBUG 1
#define TYPEPROP_DEBUG 1
// Max typed version basic blocks per basic block
-#define MAX_BB_VERSIONS 5
+#define MAX_BB_VERSIONS 10
-#define OVERALLOCATE_FACTOR 4
+#define OVERALLOCATE_FACTOR 6
/* Dummy types used by the types propagator */
@@ -917,16 +917,19 @@ write_bb_id(_PyBBBranchCache *cache, int bb_id, bool is_type_guard) {
// NEED TO ADD TO THIS EVERY TIME WE ADD A NEW ONE.
static int type_guard_ladder[256] = {
-1,
- BINARY_CHECK_INT,
BINARY_CHECK_FLOAT,
+ BINARY_CHECK_INT,
+ -1,
+ UNARY_CHECK_FLOAT,
-1,
};
// Type guard to index in the ladder.
// KEEP IN SYNC WITH INDEX IN type_guard_ladder
static int type_guard_to_index[256] = {
- [BINARY_CHECK_INT] = 1,
- [BINARY_CHECK_FLOAT] = 2,
+ [BINARY_CHECK_FLOAT] = 1,
+ [BINARY_CHECK_INT] = 2,
+ [UNARY_CHECK_FLOAT] = 4,
};
@@ -1205,7 +1208,8 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
// CACHE (bb_id of the current BB << 1 | is_type_branch)
// // The BINARY_ADD then goes to the next BB
static inline _Py_CODEUNIT *
-infer_BINARY_OP_ADD(
+infer_BINARY_OP(
+ int oparg,
bool *needs_guard,
_Py_CODEUNIT *prev_type_guard,
_Py_CODEUNIT raw_op,
@@ -1213,30 +1217,57 @@ infer_BINARY_OP_ADD(
_PyTier2TypeContext *type_context,
int bb_id)
{
+ assert(oparg == NB_ADD || oparg == NB_SUBTRACT || oparg == NB_MULTIPLY);
*needs_guard = false;
PyTypeObject *right = typenode_get_type(type_context->type_stack_ptr[-1]);
PyTypeObject *left = typenode_get_type(type_context->type_stack_ptr[-2]);
if (left == &PyLong_Type) {
if (right == &PyLong_Type) {
- write_curr->op.code = BINARY_OP_ADD_INT_REST;
+ int opcode = oparg == NB_ADD
+ ? BINARY_OP_ADD_INT_REST
+ : oparg == NB_SUBTRACT
+ ? BINARY_OP_SUBTRACT_INT_REST
+ : oparg == NB_MULTIPLY
+ ? BINARY_OP_MULTIPLY_INT_REST
+ : (Py_UNREACHABLE(), 1);
+ write_curr->op.code = opcode;
write_curr++;
- type_propagate(BINARY_OP_ADD_INT_REST, 0, type_context, NULL);
+ type_propagate(opcode, 0, type_context, NULL);
return write_curr;
}
}
- if (left == &PyRawFloat_Type) {
- if (right == &PyRawFloat_Type) {
- write_curr->op.code = BINARY_OP_ADD_FLOAT_UNBOXED;
+ if ((left == &PyRawFloat_Type || left == &PyFloat_Type) &&
+ (right == &PyRawFloat_Type || right == &PyFloat_Type)) {
+ int opcode = oparg == NB_ADD
+ ? BINARY_OP_ADD_FLOAT_UNBOXED
+ : oparg == NB_SUBTRACT
+ ? BINARY_OP_SUBTRACT_FLOAT_UNBOXED
+ : oparg == NB_MULTIPLY
+ ? BINARY_OP_MULTIPLY_FLOAT_UNBOXED
+ : (Py_UNREACHABLE(), 1);
+ if (right == &PyFloat_Type) {
+ write_curr->op.code = UNBOX_FLOAT;
+ write_curr->op.arg = 0;
write_curr++;
- type_propagate(BINARY_OP_ADD_FLOAT_UNBOXED, 0, type_context, NULL);
- return write_curr;
+ type_propagate(UNBOX_FLOAT, 0, type_context, NULL);
}
+ if (left == &PyFloat_Type) {
+ write_curr->op.code = UNBOX_FLOAT;
+ write_curr->op.arg = 1;
+ write_curr++;
+ type_propagate(UNBOX_FLOAT, 1, type_context, NULL);
+ }
+ write_curr->op.code = opcode;
+ write_curr++;
+ type_propagate(opcode, 0, type_context, NULL);
+ return write_curr;
}
+ // Both side unknown
// Unknown, time to emit the chain of guards.
if (prev_type_guard == NULL) {
write_curr = rebox_stack(write_curr, type_context, 2);
*needs_guard = true;
- return emit_type_guard(write_curr, BINARY_CHECK_INT, bb_id);
+ return emit_type_guard(write_curr, BINARY_CHECK_FLOAT, bb_id);
}
else {
int next_guard = type_guard_ladder[
@@ -1248,8 +1279,6 @@ infer_BINARY_OP_ADD(
}
// End of ladder, fall through
}
- // Unknown, just emit the same opcode, don't bother emitting guard.
- // Fall through and let the code generator handle.
return NULL;
}
@@ -1364,6 +1393,17 @@ _PyTier2_Code_DetectAndEmitBB(
}
DISPATCH();
}
+ case COPY: {
+ // Read-only, only for us to inspect the types. DO NOT MODIFY HERE.
+ // ONLY THE TYPES PROPAGATOR SHOULD MODIFY THEIR INTERNAL VALUES.
+ _Py_TYPENODE_t **type_stackptr = &starting_type_context->type_stack_ptr;
+ PyTypeObject *pop = typenode_get_type(*TYPESTACK_PEEK(1 + (oparg - 1)));
+ // Writing unboxed val to a boxed val.
+ if (is_unboxed_type(pop)) {
+ opcode = specop = COPY_NO_INCREF;
+ }
+ DISPATCH();
+ }
case LOAD_CONST: {
if (TYPECONST_GET_RAWTYPE(oparg) == &PyFloat_Type) {
write_i->op.code = LOAD_CONST;
@@ -1414,6 +1454,42 @@ _PyTier2_Code_DetectAndEmitBB(
}
DISPATCH();
}
+ case LOAD_FAST_CHECK: {
+ // Read-only, only for us to inspect the types. DO NOT MODIFY HERE.
+ // ONLY THE TYPES PROPAGATOR SHOULD MODIFY THEIR INTERNAL VALUES.
+ _Py_TYPENODE_t *type_locals = starting_type_context->type_locals;
+ // Writing unboxed val to a boxed val.
+ PyTypeObject *local = typenode_get_type(*TYPELOCALS_GET(oparg));
+ if (is_unboxed_type(local)) {
+ opcode = specop = LOAD_FAST_NO_INCREF;
+ }
+ else {
+ if (local == &PyFloat_Type) {
+ write_i->op.code = LOAD_FAST;
+ write_i->op.arg = oparg;
+ write_i++;
+ type_propagate(LOAD_FAST,
+ oparg, starting_type_context, consts);
+ write_i->op.code = UNBOX_FLOAT;
+ write_i->op.arg = 0;
+ write_i++;
+ type_propagate(UNBOX_FLOAT, 0, starting_type_context, consts);
+ write_i->op.code = STORE_FAST_UNBOXED_BOXED;
+ write_i->op.arg = oparg;
+ write_i++;
+ type_propagate(STORE_FAST_UNBOXED_BOXED,
+ oparg, starting_type_context, consts);
+ write_i->op.code = LOAD_FAST_NO_INCREF;
+ write_i->op.arg = oparg;
+ write_i++;
+ type_propagate(LOAD_FAST_NO_INCREF,
+ oparg, starting_type_context, consts);
+ continue;
+ }
+ opcode = specop = LOAD_FAST_CHECK;
+ }
+ DISPATCH();
+ }
case STORE_FAST: {
// Read-only, only for us to inspect the types. DO NOT MODIFY HERE.
// ONLY THE TYPES PROPAGATOR SHOULD MODIFY THEIR INTERNAL VALUES.
@@ -1449,9 +1525,9 @@ _PyTier2_Code_DetectAndEmitBB(
case BUILD_LIST:
DISPATCH_REBOX(oparg);
case BINARY_OP:
- if (oparg == NB_ADD) {
+ if (oparg == NB_ADD || oparg == NB_SUBTRACT || oparg == NB_MULTIPLY) {
// Add operation. Need to check if we can infer types.
- _Py_CODEUNIT *possible_next = infer_BINARY_OP_ADD(&needs_guard,
+ _Py_CODEUNIT *possible_next = infer_BINARY_OP(oparg, &needs_guard,
prev_type_guard,
*curr,
write_i, starting_type_context,
@@ -1477,11 +1553,14 @@ _PyTier2_Code_DetectAndEmitBB(
case UNARY_NOT:
case UNARY_INVERT:
case GET_LEN:
+ case UNPACK_SEQUENCE:
DISPATCH_REBOX(1);
case CALL_INTRINSIC_2:
case BINARY_SUBSCR:
case BINARY_SLICE:
DISPATCH_REBOX(2);
+ case STORE_SUBSCR:
+ DISPATCH_REBOX(4);
case STORE_SLICE:
DISPATCH_REBOX(4);
default:
@@ -1807,6 +1886,8 @@ IS_OPTIMIZABLE_OPCODE(int opcode, int oparg)
switch (_PyOpcode_Deopt[opcode]) {
case BINARY_OP:
switch (oparg) {
+ case NB_SUBTRACT:
+ case NB_MULTIPLY:
case NB_ADD:
// We want a specialised form, not the generic BINARY_OP.
return opcode != _PyOpcode_Deopt[opcode];
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 13093092ba8a70..9cdc9b062095c8 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -120,6 +120,12 @@
break;
}
+ TARGET(BINARY_OP_MULTIPLY_INT_REST) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
TARGET(BINARY_OP_MULTIPLY_FLOAT) {
STACK_SHRINK(1);
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
@@ -132,6 +138,12 @@
break;
}
+ TARGET(BINARY_OP_SUBTRACT_INT_REST) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyLong_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
TARGET(BINARY_OP_SUBTRACT_FLOAT) {
STACK_SHRINK(1);
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
@@ -164,7 +176,8 @@
}
TARGET(UNARY_CHECK_FLOAT) {
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1 + oparg), true);
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1 + oparg), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1 + oparg), true);
break;
}
@@ -174,6 +187,18 @@
break;
}
+ TARGET(BINARY_OP_SUBTRACT_FLOAT_UNBOXED) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(BINARY_OP_MULTIPLY_FLOAT_UNBOXED) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1), true);
+ break;
+ }
+
TARGET(UNBOX_FLOAT) {
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1 + oparg), true);
break;
@@ -967,6 +992,13 @@
break;
}
+ TARGET(COPY_NO_INCREF) {
+ _Py_TYPENODE_t *bottom = TYPESTACK_PEEK(1 + (oparg - 1));
+ STACK_GROW(1);
+ TYPE_OVERWRITE(bottom, TYPESTACK_PEEK(1), false);
+ break;
+ }
+
TARGET(BINARY_OP) {
STACK_SHRINK(1);
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
From cfe1e1a75e551806a875386390338bbb813974ae Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 31 Mar 2023 05:52:36 +0800
Subject: [PATCH 251/280] fix up type check ladder generation
---
Python/ceval.c | 3 ++-
Python/tier2.c | 12 +++++++++---
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/Python/ceval.c b/Python/ceval.c
index e09966d449f23e..ad458ea6194063 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1010,7 +1010,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
PyObject **stackbase = _PyFrame_Stackbase(frame);
while (stack_pointer > stackbase) {
PyObject *o = POP();
- Py_XDECREF(o);
+ // @TODO UNDO ME ONCE FINISHING DEBUGGING
+ // Py_XDECREF(o);
}
assert(STACK_LEVEL() == 0);
_PyFrame_SetStackPointer(frame, stack_pointer);
diff --git a/Python/tier2.c b/Python/tier2.c
index 41d3521a606e3b..dc66c756918de7 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1209,6 +1209,7 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
// // The BINARY_ADD then goes to the next BB
static inline _Py_CODEUNIT *
infer_BINARY_OP(
+ _Py_CODEUNIT *t2_start,
int oparg,
bool *needs_guard,
_Py_CODEUNIT *prev_type_guard,
@@ -1218,6 +1219,7 @@ infer_BINARY_OP(
int bb_id)
{
assert(oparg == NB_ADD || oparg == NB_SUBTRACT || oparg == NB_MULTIPLY);
+ bool is_first_instr = (write_curr == t2_start);
*needs_guard = false;
PyTypeObject *right = typenode_get_type(type_context->type_stack_ptr[-1]);
PyTypeObject *left = typenode_get_type(type_context->type_stack_ptr[-2]);
@@ -1262,9 +1264,12 @@ infer_BINARY_OP(
type_propagate(opcode, 0, type_context, NULL);
return write_curr;
}
- // Both side unknown
// Unknown, time to emit the chain of guards.
- if (prev_type_guard == NULL) {
+ // No type guard before this, or it's not the first in the new BB.
+ // First in new BB usually indicates it's already part of a pre-existing ladder.
+ if (prev_type_guard == NULL ||
+ (!is_first_instr && prev_type_guard != NULL &&
+ type_guard_ladder[type_guard_to_index[prev_type_guard->op.code] + 1] != -1)) {
write_curr = rebox_stack(write_curr, type_context, 2);
*needs_guard = true;
return emit_type_guard(write_curr, BINARY_CHECK_FLOAT, bb_id);
@@ -1527,7 +1532,8 @@ _PyTier2_Code_DetectAndEmitBB(
case BINARY_OP:
if (oparg == NB_ADD || oparg == NB_SUBTRACT || oparg == NB_MULTIPLY) {
// Add operation. Need to check if we can infer types.
- _Py_CODEUNIT *possible_next = infer_BINARY_OP(oparg, &needs_guard,
+ _Py_CODEUNIT *possible_next = infer_BINARY_OP(t2_start,
+ oparg, &needs_guard,
prev_type_guard,
*curr,
write_i, starting_type_context,
From 15aa9c10db4c5e382930757981c83d94f2071335 Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Fri, 31 Mar 2023 16:08:24 +0800
Subject: [PATCH 252/280] Fix: Bug in typeprop handling UNPACK_*, and bug in
COPY_NO_INCREF (#32)
---
Python/bytecodes.c | 5 +-
Python/generated_cases.c.h | 5 +-
Python/opcode_metadata.h | 2 +-
Python/tier2.c | 4 +-
Python/tier2_typepropagator.c.h | 10 +-
Tools/cases_generator/generate_cases.py | 156 +++---------------------
Tools/cases_generator/parser.py | 1 +
7 files changed, 33 insertions(+), 150 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 7b3e11a7799373..993fb1523ceb42 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -978,7 +978,7 @@ dummy_func(
UNPACK_SEQUENCE_LIST,
};
- inst(UNPACK_SEQUENCE, (unused/1, seq -- unused[oparg])) {
+ inst(UNPACK_SEQUENCE, (unused/1, seq -- values[oparg])) {
#if ENABLE_SPECIALIZATION
_PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
@@ -1028,7 +1028,7 @@ dummy_func(
DECREF_INPUTS();
}
- inst(UNPACK_EX, (seq -- unused[oparg & 0xFF], unused, unused[oparg >> 8])) {
+ inst(UNPACK_EX, (seq -- unused[oparg >> 8], unused, values[oparg & 0xFF])) {
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
PyObject **top = stack_pointer + totalargs - 1;
int res = unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top);
@@ -3237,6 +3237,7 @@ dummy_func(
inst(COPY_NO_INCREF, (bottom, unused[oparg - 1] -- bottom, unused[oparg - 1], top: *bottom)) {
assert(oparg > 0);
+ top = bottom;
}
inst(BINARY_OP, (unused/1, lhs, rhs -- res)) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 455fb6ee3a2520..8ac8cfcf530627 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -1318,6 +1318,7 @@
PREDICTED(UNPACK_SEQUENCE);
static_assert(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE == 1, "incorrect cache size");
PyObject *seq = stack_pointer[-1];
+ PyObject **values = stack_pointer - (1);
#if ENABLE_SPECIALIZATION
_PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
@@ -1391,12 +1392,13 @@
TARGET(UNPACK_EX) {
PyObject *seq = stack_pointer[-1];
+ PyObject **values = stack_pointer - (1) + 1 + (oparg >> 8);
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
PyObject **top = stack_pointer + totalargs - 1;
int res = unpack_iterable(tstate, seq, oparg & 0xFF, oparg >> 8, top);
Py_DECREF(seq);
if (res == 0) goto pop_1_error;
- STACK_GROW((oparg & 0xFF) + (oparg >> 8));
+ STACK_GROW((oparg >> 8) + (oparg & 0xFF));
DISPATCH();
}
@@ -4160,6 +4162,7 @@
PyObject *bottom = stack_pointer[-(1 + (oparg - 1))];
PyObject *top;
assert(oparg > 0);
+ top = bottom;
STACK_GROW(1);
stack_pointer[-1] = top;
DISPATCH();
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index 7f7200503960f6..be21be496104fb 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -582,7 +582,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
case UNPACK_SEQUENCE_LIST:
return oparg;
case UNPACK_EX:
- return (oparg & 0xFF) + (oparg >> 8) + 1;
+ return (oparg >> 8) + (oparg & 0xFF) + 1;
case STORE_ATTR:
return 0;
case DELETE_ATTR:
diff --git a/Python/tier2.c b/Python/tier2.c
index dc66c756918de7..0e869925135523 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -121,8 +121,8 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
}
// Is part of stack
else {
-#if TYPEPROP_DEBUG
int offset_stack = (int)(parent - type_context->type_stack);
+#if TYPEPROP_DEBUG
assert(0 <= offset_stack && offset_stack < nstack);
#endif
type_locals[i] = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)(&type_stack[offset_stack]));
@@ -152,8 +152,8 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
}
// Is part of stack
else {
-#if TYPEPROP_DEBUG
int offset_stack = (int)(parent - type_context->type_stack);
+#if TYPEPROP_DEBUG
assert(0 <= offset_stack && offset_stack < nstack);
#endif
type_stack[i] = _Py_TYPENODE_MAKE_REF((_Py_TYPENODE_t)(&type_stack[offset_stack]));
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index 9cdc9b062095c8..df008d7028e8ed 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -375,32 +375,34 @@
TARGET(UNPACK_SEQUENCE) {
STACK_SHRINK(1);
STACK_GROW(oparg);
+ for (int i = 0; i < (oparg); i++) {TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg - i), true);}
break;
}
TARGET(UNPACK_SEQUENCE_TWO_TUPLE) {
STACK_SHRINK(1);
STACK_GROW(oparg);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg), true);
+ for (int i = 0; i < (oparg); i++) {TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg - i), true);}
break;
}
TARGET(UNPACK_SEQUENCE_TUPLE) {
STACK_SHRINK(1);
STACK_GROW(oparg);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg), true);
+ for (int i = 0; i < (oparg); i++) {TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg - i), true);}
break;
}
TARGET(UNPACK_SEQUENCE_LIST) {
STACK_SHRINK(1);
STACK_GROW(oparg);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg), true);
+ for (int i = 0; i < (oparg); i++) {TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(oparg - i), true);}
break;
}
TARGET(UNPACK_EX) {
- STACK_GROW((oparg & 0xFF) + (oparg >> 8));
+ STACK_GROW((oparg >> 8) + (oparg & 0xFF));
+ for (int i = 0; i < (oparg & 0xFF); i++) {TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK((oparg & 0xFF) - i), true);}
break;
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 1255ba463f5a8b..caa224493ae5d9 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -335,146 +335,6 @@ def __init__(self, inst: parser.InstDef):
cache = "0"
self.instr_fmt = fmt
- def write_typeprop(self, out: Formatter) -> None:
- """Write one instruction's type propagation rules"""
-
- # TODO: Detect loops like in SWAP
-
- need_to_declare = []
- # Stack input is used in local effect
- if self.local_effects and \
- isinstance(val := self.local_effects.value, TypeSrcStackInput):
- need_to_declare.append(val.name)
- # Stack input is used in output effect
- for oeffect in self.output_effects:
- if not (typ := oeffect.type_annotation): continue
- ops = typ.ops
- for op in ops:
- if not isinstance(src := op.src, TypeSrcStackInput): continue
- if oeffect.name in self.unmoved_names and oeffect.name == src.name:
- print(
- f"Warn: {self.name} type annotation for {oeffect.name} will be ignored "
- "as it is unmoved")
- continue
- need_to_declare.append(src.name)
-
- # Write input stack effect variable declarations and initializations
- ieffects = list(reversed(self.input_effects))
- usable_for_local_effect = {}
- all_input_effect_names = {}
- for i, ieffect in enumerate(ieffects):
-
- if ieffect.name not in need_to_declare: continue
-
- isize = string_effect_size(
- list_effect_size([ieff for ieff in ieffects[: i + 1]])
- )
- all_input_effect_names[ieffect.name] = (ieffect, i)
- dst = StackEffect(ieffect.name, "_Py_TYPENODE_t *")
- if ieffect.size:
- # TODO: Support more cases as needed
- raise Exception("Type propagation across sized input effect not implemented")
- elif ieffect.cond:
- src = StackEffect(f"({ieffect.cond}) ? TYPESTACK_PEEK({isize}) : NULL", "_Py_TYPENODE_t *")
- else:
- usable_for_local_effect[ieffect.name] = ieffect
- src = StackEffect(f"TYPESTACK_PEEK({isize})", "_Py_TYPENODE_t *")
- out.declare(dst, src)
-
- # Write localarr effect
- if self.local_effects:
-
- idx = self.local_effects.index
- val = self.local_effects.value
-
- typ_op = "TYPE_OVERWRITE"
- dst = f"TYPELOCALS_GET({idx})"
- match val:
- case TypeSrcLiteral(name=valstr):
- if valstr == "NULL":
- src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
- flag = "true"
- else:
- src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
- flag = "true"
- case TypeSrcStackInput(name=valstr):
- assert valstr in usable_for_local_effect, \
- "`cond` and `size` stackvar not supported for localeffect"
- src = valstr
- flag = "false"
- # TODO: Support more cases as needed
- case TypeSrcConst():
- raise Exception("Not implemented")
- case TypeSrcLocals():
- raise Exception("Not implemented")
- case _:
- typing.assert_never(val)
- out.emit(f"{typ_op}({src}, {dst}, {flag});")
-
- # Update stack size
- out.stack_adjust(
- 0,
- [ieff for ieff in self.input_effects],
- [oeff for oeff in self.output_effects],
- )
-
- # Stack effect
- oeffects = list(reversed(self.output_effects))
- for i, oeffect in enumerate(oeffects):
- osize = string_effect_size(
- list_effect_size([oeff for oeff in oeffects[: i + 1]])
- )
- dst = f"TYPESTACK_PEEK({osize})"
-
- # Check if it's even used
- if oeffect.name == UNUSED: continue
-
- # Check if there's type info
- if typ := oeffect.type_annotation:
- for op in typ.ops:
- match op.src:
- case TypeSrcLiteral(literal=valstr):
- if valstr == "NULL":
- src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
- flag = "true"
- else:
- src = f"(_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&{valstr})"
- flag = "true"
- case TypeSrcStackInput(name=valstr):
- assert valstr in need_to_declare
- assert oeffect.name not in self.unmoved_names
- src = valstr
- flag = "false"
- case TypeSrcConst(index=idx):
- src = f"(_Py_TYPENODE_t *)TYPECONST_GET({idx})"
- flag = "true"
- case TypeSrcLocals(index=idx):
- src = f"TYPELOCALS_GET({idx})"
- flag = "false"
- case _:
- typing.assert_never(op.src)
-
- opstr = f"{op.op}({src}, {dst}, {flag})"
- if oeffect.cond:
- out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
- else:
- out.emit(f"{opstr};")
- continue
-
- # Don't touch unmoved stack vars
- if oeffect.name in self.unmoved_names:
- continue
-
- # Just output null
- typ_op = "TYPE_OVERWRITE"
- src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
- flag = "true"
- opstr = f"{typ_op}({src}, {dst}, {flag})"
- if oeffect.cond:
- out.emit(f"if ({oeffect.cond}) {{ {opstr}; }}")
- else:
- out.emit(f"{opstr};")
-
def write(self, out: Formatter) -> None:
"""Write one instruction, sans prologue and epilogue."""
# Write a static assertion that a family's cache size is correct
@@ -635,6 +495,8 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
def write_typeprop(self, out: Formatter) -> None:
"""Write one instruction's type propagation rules"""
+ # TODO: Add SWAP to DSL
+
need_to_declare = []
# Stack input is used in local effect
if self.local_effects and \
@@ -724,6 +586,20 @@ def write_typeprop(self, out: Formatter) -> None:
# Check if it's even used
if oeffect.name == UNUSED: continue
+ # For now assume OVERWRITE with NULL
+ if oeffect.size:
+ op = "TYPE_OVERWRITE"
+ src = "(_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT"
+ flag = "true"
+ dst = f"TYPESTACK_PEEK({osize} - i)"
+ opstr = "".join([
+ f"for (int i = 0; i < ({oeffect.size}); i++) {{"
+ f"{op}({src}, {dst}, {flag});"
+ f"}}"
+ ])
+ out.emit(opstr)
+ continue
+
# Check if there's type info
if typ := oeffect.type_annotation:
for op in typ.ops:
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 658b1b0d46c513..5c87a2b85a2dd6 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -343,6 +343,7 @@ def stack_effect(self) -> StackEffect | None:
cond_text = cond.text.strip()
size_text = ""
if self.expect(lx.LBRACKET):
+ # TODO: Support type annotation for size output
if has_type_annotation or cond_text:
raise self.make_syntax_error("Unexpected [")
if not (size := self.expression()):
From 48212848926a16666cc099a1de46216d59ed0380 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 31 Mar 2023 21:44:46 +0800
Subject: [PATCH 253/280] Add guardless container ops
---
Include/internal/pycore_code.h | 1 +
Include/internal/pycore_opcode.h | 8 +-
Include/opcode.h | 20 ++--
Lib/opcode.py | 11 ++-
Objects/codeobject.c | 22 +++--
Python/bytecodes.c | 31 ++++---
Python/ceval.c | 3 +-
Python/generated_cases.c.h | 75 +++++++++++----
Python/opcode_metadata.h | 20 +++-
Python/tier2.c | 152 ++++++++++++++++++++++++++-----
Python/tier2_typepropagator.c.h | 23 +++--
11 files changed, 280 insertions(+), 86 deletions(-)
diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h
index c62c39a6a080d6..add3dfc28a436c 100644
--- a/Include/internal/pycore_code.h
+++ b/Include/internal/pycore_code.h
@@ -285,6 +285,7 @@ extern _Py_CODEUNIT *_PyTier2_LocateJumpBackwardsBB(
_Py_CODEUNIT **tier1_fallback, _Py_CODEUNIT *curr, int stacksize);
extern void _PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target);
extern void _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *target);
+void _PyTier2TypeContext_Free(_PyTier2TypeContext *type_context);
#ifdef Py_STATS
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index 71491b83e71a56..ce1195d45819ea 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -422,13 +422,15 @@ static const char *const _PyOpcode_OpName[263] = {
[BB_JUMP_BACKWARD_LAZY] = "BB_JUMP_BACKWARD_LAZY",
[BINARY_CHECK_INT] = "BINARY_CHECK_INT",
[BINARY_CHECK_FLOAT] = "BINARY_CHECK_FLOAT",
- [UNARY_CHECK_FLOAT] = "UNARY_CHECK_FLOAT",
+ [CHECK_LIST] = "CHECK_LIST",
[BINARY_OP_ADD_INT_REST] = "BINARY_OP_ADD_INT_REST",
[BINARY_OP_ADD_FLOAT_UNBOXED] = "BINARY_OP_ADD_FLOAT_UNBOXED",
[BINARY_OP_SUBTRACT_INT_REST] = "BINARY_OP_SUBTRACT_INT_REST",
[BINARY_OP_SUBTRACT_FLOAT_UNBOXED] = "BINARY_OP_SUBTRACT_FLOAT_UNBOXED",
[BINARY_OP_MULTIPLY_INT_REST] = "BINARY_OP_MULTIPLY_INT_REST",
[BINARY_OP_MULTIPLY_FLOAT_UNBOXED] = "BINARY_OP_MULTIPLY_FLOAT_UNBOXED",
+ [BINARY_SUBSCR_LIST_INT_REST] = "BINARY_SUBSCR_LIST_INT_REST",
+ [STORE_SUBSCR_LIST_INT_REST] = "STORE_SUBSCR_LIST_INT_REST",
[POP_TOP_NO_DECREF] = "POP_TOP_NO_DECREF",
[UNBOX_FLOAT] = "UNBOX_FLOAT",
[BOX_FLOAT] = "BOX_FLOAT",
@@ -437,8 +439,6 @@ static const char *const _PyOpcode_OpName[263] = {
[STORE_FAST_BOXED_UNBOXED] = "STORE_FAST_BOXED_UNBOXED",
[STORE_FAST_UNBOXED_BOXED] = "STORE_FAST_UNBOXED_BOXED",
[STORE_FAST_UNBOXED_UNBOXED] = "STORE_FAST_UNBOXED_UNBOXED",
- [204] = "<204>",
- [205] = "<205>",
[206] = "<206>",
[207] = "<207>",
[208] = "<208>",
@@ -501,8 +501,6 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
- case 204: \
- case 205: \
case 206: \
case 207: \
case 208: \
diff --git a/Include/opcode.h b/Include/opcode.h
index c0c5a79fd9da38..3d99716fec63e6 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -206,21 +206,23 @@ extern "C" {
#define BB_JUMP_BACKWARD_LAZY 186
#define BINARY_CHECK_INT 187
#define BINARY_CHECK_FLOAT 188
-#define UNARY_CHECK_FLOAT 189
+#define CHECK_LIST 189
#define BINARY_OP_ADD_INT_REST 190
#define BINARY_OP_ADD_FLOAT_UNBOXED 191
#define BINARY_OP_SUBTRACT_INT_REST 192
#define BINARY_OP_SUBTRACT_FLOAT_UNBOXED 193
#define BINARY_OP_MULTIPLY_INT_REST 194
#define BINARY_OP_MULTIPLY_FLOAT_UNBOXED 195
-#define POP_TOP_NO_DECREF 196
-#define UNBOX_FLOAT 197
-#define BOX_FLOAT 198
-#define COPY_NO_INCREF 199
-#define LOAD_FAST_NO_INCREF 200
-#define STORE_FAST_BOXED_UNBOXED 201
-#define STORE_FAST_UNBOXED_BOXED 202
-#define STORE_FAST_UNBOXED_UNBOXED 203
+#define BINARY_SUBSCR_LIST_INT_REST 196
+#define STORE_SUBSCR_LIST_INT_REST 197
+#define POP_TOP_NO_DECREF 198
+#define UNBOX_FLOAT 199
+#define BOX_FLOAT 200
+#define COPY_NO_INCREF 201
+#define LOAD_FAST_NO_INCREF 202
+#define STORE_FAST_BOXED_UNBOXED 203
+#define STORE_FAST_UNBOXED_BOXED 204
+#define STORE_FAST_UNBOXED_UNBOXED 205
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index a3d60584a877d8..1e26407a16ab06 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -446,6 +446,7 @@ def pseudo_op(name, op, real_ops):
'BINARY_OP_SUBTRACT_INT',
'BINARY_OP_MULTIPLY_INT',
'BINARY_OP_ADD_FLOAT',
+ 'BINARY_SUBSCR_LIST_INT',
]
_uops = [
# Tier 2 BB opcodes
@@ -491,8 +492,10 @@ def pseudo_op(name, op, real_ops):
# single operand forms.
'BINARY_CHECK_INT',
'BINARY_CHECK_FLOAT',
- 'UNARY_CHECK_FLOAT',
- # 'BINARY_CHECK_STR',
+ 'CHECK_LIST',
+
+ # These are guardless instructions
+ ## Arithmetic
'BINARY_OP_ADD_INT_REST',
'BINARY_OP_ADD_FLOAT_UNBOXED',
'BINARY_OP_SUBTRACT_INT_REST',
@@ -500,6 +503,10 @@ def pseudo_op(name, op, real_ops):
'BINARY_OP_MULTIPLY_INT_REST',
'BINARY_OP_MULTIPLY_FLOAT_UNBOXED',
+ # Containers
+ 'BINARY_SUBSCR_LIST_INT_REST',
+ 'STORE_SUBSCR_LIST_INT_REST',
+
# Boxing / unboxing ops
'POP_TOP_NO_DECREF',
'UNBOX_FLOAT',
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 3b1edc08f54786..f60aea5e4d46a5 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -1713,12 +1713,6 @@ code_tier2_fini(PyCodeObject *co)
_PyTier2Info *t2_info = co->_tier2_info;
t2_info->_entry_bb = NULL;
if (t2_info->_bb_space != NULL) {
- // Traverse the linked list
- //for (_PyTier2BBSpace *curr = t2_info->_bb_space; curr != NULL;) {
- // _PyTier2BBSpace *prev = curr;
- // curr = curr->next;
- // PyMem_Free(prev);
- //}
PyMem_Free(t2_info->_bb_space);
t2_info->_bb_space = NULL;
}
@@ -1727,12 +1721,28 @@ code_tier2_fini(PyCodeObject *co)
t2_info->backward_jump_offsets != NULL) {
PyMem_Free(t2_info->backward_jump_offsets);
t2_info->backward_jump_offsets = NULL;
+ _PyTier2BBStartTypeContextTriplet **backward_jump_target_bb_pairs = t2_info->backward_jump_target_bb_pairs;
+ //int backwards_jump_count = t2_info->backward_jump_count;
+ //for (int i = 0; i < backwards_jump_count; i++) {
+ // PyMem_Free(backward_jump_target_bb_pairs[i]);
+ //}
+ PyMem_Free(backward_jump_target_bb_pairs);
}
t2_info->backward_jump_count = 0;
if (t2_info->bb_data != NULL && t2_info->bb_data_len > 0) {
PyMem_Free(t2_info->bb_data);
}
+ //if (t2_info->bb_data != NULL) {
+ // for (int i = 0; i < t2_info->bb_data_curr; i++) {
+ // if (t2_info->bb_data[i] != NULL) {
+ // _PyTier2BBMetadata *meta = t2_info->bb_data[i];
+ // //_PyTier2TypeContext_Free(meta->type_context);
+ // //PyMem_Free(meta);
+ // }
+ // }
+ // PyMem_Free(t2_info->bb_data);
+ //}
t2_info->bb_data_len = 0;
PyMem_Free(t2_info);
}
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 993fb1523ceb42..d0f2d04dfead0f 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -326,15 +326,6 @@ dummy_func(
: right);
}
- inst(UNARY_CHECK_FLOAT, (arg, unused[oparg] -- arg_unboxed : { <<= PyFloat_Type, PyRawFloat_Type}, unused[oparg])) {
- assert(cframe.use_tracing == 0);
- char is_successor = PyFloat_CheckExact(arg);
- bb_test = BB_TEST(is_successor, 0);
- arg_unboxed = (is_successor
- ? *((PyObject **)(&(((PyFloatObject *)arg)->ob_fval)))
- : arg);
- }
-
inst(BINARY_OP_ADD_FLOAT_UNBOXED, (left, right -- sum : PyRawFloat_Type)) {
STAT_INC(BINARY_OP, hit);
double temp = *(double *)(&(left)) + *(double *)(&(right));
@@ -439,14 +430,18 @@ dummy_func(
ERROR_IF(err, error);
}
- inst(BINARY_SUBSCR_LIST_INT, (unused/4, list, sub -- res)) {
+ macro_inst(BINARY_SUBSCR_LIST_INT, (unused/4, list, sub -- res)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
// Deopt unless 0 <= sub < PyList_Size(list)
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR);
- Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
+ U_INST(BINARY_SUBSCR_LIST_INT_REST);
+ }
+
+ u_inst(BINARY_SUBSCR_LIST_INT_REST, (list, sub -- res)) {
+ Py_ssize_t index = ((PyLongObject *)sub)->long_value.ob_digit[0];
DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
STAT_INC(BINARY_SUBSCR, hit);
res = PyList_GET_ITEM(list, index);
@@ -456,6 +451,11 @@ dummy_func(
Py_DECREF(list);
}
+ inst(CHECK_LIST, (container, unused[oparg] -- container : { <<= PyList_Type, PyList_Type}, unused[oparg])) {
+ char is_successor = PyList_CheckExact(container);
+ bb_test = BB_TEST(is_successor, 0);
+ }
+
inst(BINARY_SUBSCR_TUPLE_INT, (unused/4, tuple, sub -- res)) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
@@ -548,7 +548,7 @@ dummy_func(
ERROR_IF(err, error);
}
- inst(STORE_SUBSCR_LIST_INT, (unused/1, value, list, sub -- )) {
+ macro_inst(STORE_SUBSCR_LIST_INT, (unused/1, value, list, sub -- )) {
assert(cframe.use_tracing == 0);
DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR);
DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
@@ -556,7 +556,12 @@ dummy_func(
// Ensure nonnegative, zero-or-one-digit ints.
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
- // Ensure index < len(list)
+ U_INST(STORE_SUBSCR_LIST_INT_REST);
+ }
+
+ u_inst(STORE_SUBSCR_LIST_INT_REST, (value, list, sub -- )) {
+ Py_ssize_t index = ((PyLongObject *)sub)->long_value.ob_digit[0];
+ /* Ensure index < len(list) */
DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR);
STAT_INC(STORE_SUBSCR, hit);
diff --git a/Python/ceval.c b/Python/ceval.c
index ad458ea6194063..6cf504a91e0c35 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1010,8 +1010,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
PyObject **stackbase = _PyFrame_Stackbase(frame);
while (stack_pointer > stackbase) {
PyObject *o = POP();
- // @TODO UNDO ME ONCE FINISHING DEBUGGING
- // Py_XDECREF(o);
+ Py_XDECREF(o);
}
assert(STACK_LEVEL() == 0);
_PyFrame_SetStackPointer(frame, stack_pointer);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 8ac8cfcf530627..d0dde435a97081 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -30,6 +30,33 @@
if (sum == NULL) goto pop_2_error;\
} while (0)
+ #define UOP_BINARY_SUBSCR_LIST_INT_REST() \
+ do { \
+ Py_ssize_t index = ((PyLongObject *)sub)->long_value.ob_digit[0];\
+ DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);\
+ STAT_INC(BINARY_SUBSCR, hit);\
+ res = PyList_GET_ITEM(list, index);\
+ assert(res != NULL);\
+ Py_INCREF(res);\
+ _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);\
+ Py_DECREF(list);\
+ } while (0)
+
+ #define UOP_STORE_SUBSCR_LIST_INT_REST() \
+ do { \
+ Py_ssize_t index = ((PyLongObject *)sub)->long_value.ob_digit[0];\
+ /* Ensure index < len(list) */\
+ DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR);\
+ STAT_INC(STORE_SUBSCR, hit);\
+\
+ PyObject *old_value = PyList_GET_ITEM(list, index);\
+ PyList_SET_ITEM(list, index, value);\
+ assert(old_value != NULL);\
+ Py_DECREF(old_value);\
+ _Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);\
+ Py_DECREF(list);\
+ } while (0)
+
TARGET(NOP) {
DISPATCH();
}
@@ -485,19 +512,6 @@
DISPATCH();
}
- TARGET(UNARY_CHECK_FLOAT) {
- PyObject *arg = stack_pointer[-(1 + oparg)];
- PyObject *arg_unboxed;
- assert(cframe.use_tracing == 0);
- char is_successor = PyFloat_CheckExact(arg);
- bb_test = BB_TEST(is_successor, 0);
- arg_unboxed = (is_successor
- ? *((PyObject **)(&(((PyFloatObject *)arg)->ob_fval)))
- : arg);
- stack_pointer[-(1 + oparg)] = arg_unboxed;
- DISPATCH();
- }
-
TARGET(BINARY_OP_ADD_FLOAT_UNBOXED) {
PyObject *right = stack_pointer[-1];
PyObject *left = stack_pointer[-2];
@@ -669,7 +683,18 @@
// Deopt unless 0 <= sub < PyList_Size(list)
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), BINARY_SUBSCR);
- Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
+ UOP_BINARY_SUBSCR_LIST_INT_REST();
+ STACK_SHRINK(1);
+ stack_pointer[-1] = res;
+ next_instr += 4;
+ DISPATCH();
+ }
+
+ TARGET(BINARY_SUBSCR_LIST_INT_REST) {
+ PyObject *sub = stack_pointer[-1];
+ PyObject *list = stack_pointer[-2];
+ PyObject *res;
+ Py_ssize_t index = ((PyLongObject *)sub)->long_value.ob_digit[0];
DEOPT_IF(index >= PyList_GET_SIZE(list), BINARY_SUBSCR);
STAT_INC(BINARY_SUBSCR, hit);
res = PyList_GET_ITEM(list, index);
@@ -679,7 +704,13 @@
Py_DECREF(list);
STACK_SHRINK(1);
stack_pointer[-1] = res;
- next_instr += 4;
+ DISPATCH();
+ }
+
+ TARGET(CHECK_LIST) {
+ PyObject *container = stack_pointer[-(1 + oparg)];
+ char is_successor = PyList_CheckExact(container);
+ bb_test = BB_TEST(is_successor, 0);
DISPATCH();
}
@@ -819,7 +850,18 @@
// Ensure nonnegative, zero-or-one-digit ints.
DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub), STORE_SUBSCR);
Py_ssize_t index = ((PyLongObject*)sub)->long_value.ob_digit[0];
- // Ensure index < len(list)
+ UOP_STORE_SUBSCR_LIST_INT_REST();
+ STACK_SHRINK(3);
+ next_instr += 1;
+ DISPATCH();
+ }
+
+ TARGET(STORE_SUBSCR_LIST_INT_REST) {
+ PyObject *sub = stack_pointer[-1];
+ PyObject *list = stack_pointer[-2];
+ PyObject *value = stack_pointer[-3];
+ Py_ssize_t index = ((PyLongObject *)sub)->long_value.ob_digit[0];
+ /* Ensure index < len(list) */
DEOPT_IF(index >= PyList_GET_SIZE(list), STORE_SUBSCR);
STAT_INC(STORE_SUBSCR, hit);
@@ -830,7 +872,6 @@
_Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
Py_DECREF(list);
STACK_SHRINK(3);
- next_instr += 1;
DISPATCH();
}
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index be21be496104fb..c62dd4c48b7c03 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -77,8 +77,6 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 2;
case BINARY_CHECK_FLOAT:
return 2;
- case UNARY_CHECK_FLOAT:
- return oparg + 1;
case BINARY_OP_ADD_FLOAT_UNBOXED:
return 2;
case BINARY_OP_SUBTRACT_FLOAT_UNBOXED:
@@ -103,6 +101,10 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 4;
case BINARY_SUBSCR_LIST_INT:
return 2;
+ case BINARY_SUBSCR_LIST_INT_REST:
+ return 2;
+ case CHECK_LIST:
+ return oparg + 1;
case BINARY_SUBSCR_TUPLE_INT:
return 2;
case BINARY_SUBSCR_DICT:
@@ -117,6 +119,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 3;
case STORE_SUBSCR_LIST_INT:
return 3;
+ case STORE_SUBSCR_LIST_INT_REST:
+ return 3;
case STORE_SUBSCR_DICT:
return 3;
case DELETE_SUBSCR:
@@ -489,8 +493,6 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 1;
case BINARY_CHECK_FLOAT:
return 2;
- case UNARY_CHECK_FLOAT:
- return oparg + 1;
case BINARY_OP_ADD_FLOAT_UNBOXED:
return 1;
case BINARY_OP_SUBTRACT_FLOAT_UNBOXED:
@@ -515,6 +517,10 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case BINARY_SUBSCR_LIST_INT:
return 1;
+ case BINARY_SUBSCR_LIST_INT_REST:
+ return 1;
+ case CHECK_LIST:
+ return oparg + 1;
case BINARY_SUBSCR_TUPLE_INT:
return 1;
case BINARY_SUBSCR_DICT:
@@ -529,6 +535,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0;
case STORE_SUBSCR_LIST_INT:
return 0;
+ case STORE_SUBSCR_LIST_INT_REST:
+ return 0;
case STORE_SUBSCR_DICT:
return 0;
case DELETE_SUBSCR:
@@ -871,7 +879,6 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IX },
[BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC },
[BINARY_CHECK_FLOAT] = { true, INSTR_FMT_IX },
- [UNARY_CHECK_FLOAT] = { true, INSTR_FMT_IB },
[BINARY_OP_ADD_FLOAT_UNBOXED] = { true, INSTR_FMT_IX },
[BINARY_OP_SUBTRACT_FLOAT_UNBOXED] = { true, INSTR_FMT_IX },
[BINARY_OP_MULTIPLY_FLOAT_UNBOXED] = { true, INSTR_FMT_IX },
@@ -884,6 +891,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[BINARY_SLICE] = { true, INSTR_FMT_IX },
[STORE_SLICE] = { true, INSTR_FMT_IX },
[BINARY_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC000 },
+ [BINARY_SUBSCR_LIST_INT_REST] = { true, INSTR_FMT_IX },
+ [CHECK_LIST] = { true, INSTR_FMT_IB },
[BINARY_SUBSCR_TUPLE_INT] = { true, INSTR_FMT_IXC000 },
[BINARY_SUBSCR_DICT] = { true, INSTR_FMT_IXC000 },
[BINARY_SUBSCR_GETITEM] = { true, INSTR_FMT_IXC000 },
@@ -891,6 +900,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[SET_ADD] = { true, INSTR_FMT_IB },
[STORE_SUBSCR] = { true, INSTR_FMT_IXC },
[STORE_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC },
+ [STORE_SUBSCR_LIST_INT_REST] = { true, INSTR_FMT_IX },
[STORE_SUBSCR_DICT] = { true, INSTR_FMT_IXC },
[DELETE_SUBSCR] = { true, INSTR_FMT_IX },
[CALL_INTRINSIC_1] = { true, INSTR_FMT_IB },
diff --git a/Python/tier2.c b/Python/tier2.c
index 0e869925135523..98071e272e76f3 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -4,6 +4,7 @@
#include "pycore_frame.h"
#include "pycore_opcode.h"
#include "pycore_pystate.h"
+#include "pycore_long.h"
#include "stdbool.h"
#include "opcode.h"
@@ -17,12 +18,22 @@
/* Dummy types used by the types propagator */
+
+// Represents a 64-bit unboxed double
PyTypeObject PyRawFloat_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"rawfloat",
sizeof(PyFloatObject),
};
+// Represents a PyLong that fits in a 64-bit long.
+PyTypeObject PySmallInt_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "smallint",
+ sizeof(PyFloatObject),
+};
+
+
static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
@@ -173,7 +184,7 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
return new_type_context;
}
-static void
+void
_PyTier2TypeContext_Free(_PyTier2TypeContext *type_context)
{
@@ -920,7 +931,7 @@ static int type_guard_ladder[256] = {
BINARY_CHECK_FLOAT,
BINARY_CHECK_INT,
-1,
- UNARY_CHECK_FLOAT,
+ CHECK_LIST,
-1,
};
@@ -929,19 +940,19 @@ static int type_guard_ladder[256] = {
static int type_guard_to_index[256] = {
[BINARY_CHECK_FLOAT] = 1,
[BINARY_CHECK_INT] = 2,
- [UNARY_CHECK_FLOAT] = 4,
+ [CHECK_LIST] = 4,
};
static inline _Py_CODEUNIT *
-emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int bb_id)
+emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int guard_oparg, int bb_id)
{
#if BB_DEBUG && defined(Py_DEBUG)
fprintf(stderr, "emitted type guard %p %s\n", write_curr,
_PyOpcode_OpName[guard_opcode]);
#endif
write_curr->op.code = guard_opcode;
- write_curr->op.arg = type_guard_to_index[guard_opcode];
+ write_curr->op.arg = guard_oparg;
write_curr++;
_py_set_opcode(write_curr, BB_BRANCH);
write_curr->op.arg = 0;
@@ -1267,12 +1278,10 @@ infer_BINARY_OP(
// Unknown, time to emit the chain of guards.
// No type guard before this, or it's not the first in the new BB.
// First in new BB usually indicates it's already part of a pre-existing ladder.
- if (prev_type_guard == NULL ||
- (!is_first_instr && prev_type_guard != NULL &&
- type_guard_ladder[type_guard_to_index[prev_type_guard->op.code] + 1] != -1)) {
+ if (prev_type_guard == NULL || !is_first_instr) {
write_curr = rebox_stack(write_curr, type_context, 2);
*needs_guard = true;
- return emit_type_guard(write_curr, BINARY_CHECK_FLOAT, bb_id);
+ return emit_type_guard(write_curr, BINARY_CHECK_FLOAT, 0, bb_id);
}
else {
int next_guard = type_guard_ladder[
@@ -1280,7 +1289,55 @@ infer_BINARY_OP(
if (next_guard != -1) {
write_curr = rebox_stack(write_curr, type_context, 2);
*needs_guard = true;
- return emit_type_guard(write_curr, next_guard, bb_id);
+ return emit_type_guard(write_curr, next_guard, 0, bb_id);
+ }
+ // End of ladder, fall through
+ }
+ return NULL;
+}
+
+static inline _Py_CODEUNIT *
+infer_BINARY_SUBSCR(
+ _Py_CODEUNIT *t2_start,
+ int oparg,
+ bool *needs_guard,
+ _Py_CODEUNIT *prev_type_guard,
+ _Py_CODEUNIT raw_op,
+ _Py_CODEUNIT *write_curr,
+ _PyTier2TypeContext *type_context,
+ int bb_id,
+ bool store)
+{
+ assert(oparg == NB_ADD || oparg == NB_SUBTRACT || oparg == NB_MULTIPLY);
+ bool is_first_instr = (write_curr == t2_start);
+ *needs_guard = false;
+ PyTypeObject *sub = typenode_get_type(type_context->type_stack_ptr[-1]);
+ PyTypeObject *container = typenode_get_type(type_context->type_stack_ptr[-2]);
+ if (container == &PyList_Type) {
+ if (sub == &PySmallInt_Type) {
+ int opcode = store
+ ? STORE_SUBSCR_LIST_INT_REST : BINARY_SUBSCR_LIST_INT_REST;
+ write_curr->op.code = opcode;
+ write_curr++;
+ type_propagate(opcode, 0, type_context, NULL);
+ return write_curr;
+ }
+ }
+ // Unknown, time to emit the chain of guards.
+ // No type guard before this, or it's not the first in the new BB.
+ // First in new BB usually indicates it's already part of a pre-existing ladder.
+ if (prev_type_guard == NULL || !is_first_instr) {
+ write_curr = rebox_stack(write_curr, type_context, 2);
+ *needs_guard = true;
+ return emit_type_guard(write_curr, CHECK_LIST, 1, bb_id);
+ }
+ else {
+ int next_guard = type_guard_ladder[
+ type_guard_to_index[prev_type_guard->op.code] + 1];
+ if (next_guard != -1) {
+ write_curr = rebox_stack(write_curr, type_context, store ? 3 : 2);
+ *needs_guard = true;
+ return emit_type_guard(write_curr, next_guard, 1, bb_id);
}
// End of ladder, fall through
}
@@ -1317,7 +1374,8 @@ _PyTier2_Code_DetectAndEmitBB(
assert(
prev_type_guard == NULL ||
prev_type_guard->op.code == BINARY_CHECK_INT ||
- prev_type_guard->op.code == BINARY_CHECK_FLOAT
+ prev_type_guard->op.code == BINARY_CHECK_FLOAT ||
+ prev_type_guard->op.code == CHECK_LIST
);
#define END() goto end;
#define JUMPBY(x) i += x + 1;
@@ -1335,6 +1393,7 @@ _PyTier2_Code_DetectAndEmitBB(
continue;
#define DISPATCH_GOTO() goto dispatch_opcode;
#define TYPECONST_GET_RAWTYPE(idx) Py_TYPE(PyTuple_GET_ITEM(consts, idx))
+#define GET_CONST(idx) PyTuple_GET_ITEM(consts, idx)
assert(co->_tier2_info != NULL);
// There are only two cases that a BB ends.
@@ -1410,10 +1469,9 @@ _PyTier2_Code_DetectAndEmitBB(
DISPATCH();
}
case LOAD_CONST: {
- if (TYPECONST_GET_RAWTYPE(oparg) == &PyFloat_Type) {
- write_i->op.code = LOAD_CONST;
- write_i->op.arg = oparg;
- write_i++;
+ PyTypeObject *typ = TYPECONST_GET_RAWTYPE(oparg);
+ if (typ == &PyFloat_Type) {
+ write_i = emit_i(write_i, LOAD_CONST, curr->op.arg);
type_propagate(LOAD_CONST, oparg, starting_type_context, consts);
write_i->op.code = UNBOX_FLOAT;
write_i->op.arg = 0;
@@ -1421,6 +1479,20 @@ _PyTier2_Code_DetectAndEmitBB(
type_propagate(UNBOX_FLOAT, 0, starting_type_context, consts);
continue;
}
+ else if (typ == &PyLong_Type) {
+ // We break our own rules for more efficient code here.
+ // NOTE: THIS MODIFIES THE TYPE CONTEXT.
+ if (_PyLong_IsNonNegativeCompact((PyLongObject *)GET_CONST(oparg))) {
+ write_i = emit_i(write_i, LOAD_CONST, curr->op.arg);
+
+ // Type propagate
+ _PyTier2TypeContext *type_context = starting_type_context;
+ _Py_TYPENODE_t **type_stackptr = &type_context->type_stack_ptr;
+ *type_stackptr += 1;
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PySmallInt_Type), TYPESTACK_PEEK(1), true);
+ continue;
+ }
+ }
DISPATCH();
}
case LOAD_FAST: {
@@ -1553,6 +1625,48 @@ _PyTier2_Code_DetectAndEmitBB(
continue;
}
DISPATCH_REBOX(2);
+ case BINARY_SUBSCR: {
+ _Py_CODEUNIT *possible_next = infer_BINARY_SUBSCR(
+ t2_start, oparg, &needs_guard,
+ prev_type_guard,
+ *curr,
+ write_i, starting_type_context,
+ co->_tier2_info->bb_data_curr, false);
+ if (possible_next == NULL) {
+ DISPATCH_REBOX(2);
+ }
+ write_i = possible_next;
+ if (needs_guard) {
+ // Point to the same instruction, because in this BB we emit
+ // the guard.
+ // The next BB emits the instruction.
+ i--;
+ END();
+ }
+ i += caches;
+ continue;
+ }
+ case STORE_SUBSCR: {
+ _Py_CODEUNIT *possible_next = infer_BINARY_SUBSCR(
+ t2_start, oparg, &needs_guard,
+ prev_type_guard,
+ *curr,
+ write_i, starting_type_context,
+ co->_tier2_info->bb_data_curr, true);
+ if (possible_next == NULL) {
+ DISPATCH_REBOX(3);
+ }
+ write_i = possible_next;
+ if (needs_guard) {
+ // Point to the same instruction, because in this BB we emit
+ // the guard.
+ // The next BB emits the instruction.
+ i--;
+ END();
+ }
+ i += caches;
+ continue;
+ }
case LOAD_ATTR:
case CALL_INTRINSIC_1:
case UNARY_NEGATIVE:
@@ -1562,11 +1676,8 @@ _PyTier2_Code_DetectAndEmitBB(
case UNPACK_SEQUENCE:
DISPATCH_REBOX(1);
case CALL_INTRINSIC_2:
- case BINARY_SUBSCR:
case BINARY_SLICE:
DISPATCH_REBOX(2);
- case STORE_SUBSCR:
- DISPATCH_REBOX(4);
case STORE_SLICE:
DISPATCH_REBOX(4);
default:
@@ -1820,12 +1931,10 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
qsort(backward_jump_offsets, backwards_jump_count,
sizeof(int), compare_ints);
// Deduplicate
- int backwards_jump_count_new = backwards_jump_count;
for (int i = 0; i < backwards_jump_count - 1; i++) {
for (int x = i + 1; x < backwards_jump_count; x++) {
if (backward_jump_offsets[i] == backward_jump_offsets[x]) {
backward_jump_offsets[x] = -1;
- backwards_jump_count_new--;
}
}
}
@@ -2250,7 +2359,7 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
#ifdef Py_DEBUG
// We assert that there are as many items on the operand stack as there are on the
// saved type stack.
- int typestack_level = meta->type_context->type_stack_ptr - meta->type_context->type_stack;
+ Py_ssize_t typestack_level = meta->type_context->type_stack_ptr - meta->type_context->type_stack;
assert(typestack_level == stacklevel);
#endif
// The jump target
@@ -2466,3 +2575,4 @@ _PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *tar
#undef TYPELOCALS_GET
#undef TYPE_SET
#undef TYPE_OVERWRITE
+#undef GET_CONST
diff --git a/Python/tier2_typepropagator.c.h b/Python/tier2_typepropagator.c.h
index df008d7028e8ed..cdab65faf0c314 100644
--- a/Python/tier2_typepropagator.c.h
+++ b/Python/tier2_typepropagator.c.h
@@ -175,12 +175,6 @@
break;
}
- TARGET(UNARY_CHECK_FLOAT) {
- TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyFloat_Type), TYPESTACK_PEEK(1 + oparg), true);
- TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1 + oparg), true);
- break;
- }
-
TARGET(BINARY_OP_ADD_FLOAT_UNBOXED) {
STACK_SHRINK(1);
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyRawFloat_Type), TYPESTACK_PEEK(1), true);
@@ -250,6 +244,18 @@
break;
}
+ TARGET(BINARY_SUBSCR_LIST_INT_REST) {
+ STACK_SHRINK(1);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
+ break;
+ }
+
+ TARGET(CHECK_LIST) {
+ TYPE_SET((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyList_Type), TYPESTACK_PEEK(1 + oparg), true);
+ TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_MAKE_ROOT((_Py_TYPENODE_t)&PyList_Type), TYPESTACK_PEEK(1 + oparg), true);
+ break;
+ }
+
TARGET(BINARY_SUBSCR_TUPLE_INT) {
STACK_SHRINK(1);
TYPE_OVERWRITE((_Py_TYPENODE_t *)_Py_TYPENODE_NULLROOT, TYPESTACK_PEEK(1), true);
@@ -288,6 +294,11 @@
break;
}
+ TARGET(STORE_SUBSCR_LIST_INT_REST) {
+ STACK_SHRINK(3);
+ break;
+ }
+
TARGET(STORE_SUBSCR_DICT) {
STACK_SHRINK(3);
break;
From 7ec2dc0aed6540c33ae148f0c60a98e53a3e14d1 Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Fri, 31 Mar 2023 21:47:02 +0800
Subject: [PATCH 254/280] Fix: Ref leak in unboxing checks (#33)
* Added EXTENDED_ARG to type prop
* Revert "Added EXTENDED_ARG to type prop"
This reverts commit 7249cb7e968b186857029213490e077d155e7c8a.
* Fix: Buggy type propagation across SWP
* Fix: Forgot to tag pointers in TYPE_SET
* Fix: BB_TEST_ITER wrong type propagation
* Fix: Bug in typeprop handling UNPACK_*, and bug in COPY_NO_INCREF
* Fix: Misc macro fix
* Fix: Ref leak in unboxing checks
---------
Co-authored-by: Ken Jin
---
Python/bytecodes.c | 14 ++++++++------
Python/generated_cases.c.h | 15 +++++++++------
2 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index d0f2d04dfead0f..2bb42c2ad449e9 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -318,12 +318,14 @@ dummy_func(
char is_successor = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
bb_test = BB_TEST(is_successor, 0);
- left_unboxed = (is_successor
- ? *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)))
- : left);
- right_unboxed = (is_successor
- ? *((PyObject **)(&(((PyFloatObject *)right)->ob_fval)))
- : right);
+ if (is_successor) {
+ left_unboxed = *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)));
+ right_unboxed = *((PyObject **)(&(((PyFloatObject *)right)->ob_fval)));
+ DECREF_INPUTS();
+ } else {
+ left_unboxed = left;
+ right_unboxed = right;
+ }
}
inst(BINARY_OP_ADD_FLOAT_UNBOXED, (left, right -- sum : PyRawFloat_Type)) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index d0dde435a97081..9ae72b8d258798 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -501,12 +501,15 @@
char is_successor = PyFloat_CheckExact(left) && (Py_TYPE(left) == Py_TYPE(right));
bb_test = BB_TEST(is_successor, 0);
- left_unboxed = (is_successor
- ? *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)))
- : left);
- right_unboxed = (is_successor
- ? *((PyObject **)(&(((PyFloatObject *)right)->ob_fval)))
- : right);
+ if (is_successor) {
+ left_unboxed = *((PyObject **)(&(((PyFloatObject *)left)->ob_fval)));
+ right_unboxed = *((PyObject **)(&(((PyFloatObject *)right)->ob_fval)));
+ Py_DECREF(left);
+ Py_DECREF(right);
+ } else {
+ left_unboxed = left;
+ right_unboxed = right;
+ }
stack_pointer[-1] = right_unboxed;
stack_pointer[-2] = left_unboxed;
DISPATCH();
From 0a14be32e83a6b79ae0bc98b0df705467529707d Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 31 Mar 2023 22:32:12 +0800
Subject: [PATCH 255/280] Add our own nbody benchmark
---
bm_nbody.py | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 149 insertions(+)
create mode 100644 bm_nbody.py
diff --git a/bm_nbody.py b/bm_nbody.py
new file mode 100644
index 00000000000000..1b8aed05910c89
--- /dev/null
+++ b/bm_nbody.py
@@ -0,0 +1,149 @@
+"""
+N-body benchmark from the Computer Language Benchmarks Game.
+
+This is intended to support Unladen Swallow's pyperf.py. Accordingly, it has been
+modified from the Shootout version:
+- Accept standard Unladen Swallow benchmark options.
+- Run report_energy()/advance() in a loop.
+- Reimplement itertools.combinations() to work with older Python versions.
+
+Pulled from:
+http://benchmarksgame.alioth.debian.org/u64q/program.php?test=nbody&lang=python3&id=1
+
+Contributed by Kevin Carson.
+Modified by Tupteq, Fredrik Johansson, and Daniel Nanz.
+"""
+
+import time
+
+__contact__ = "collinwinter@google.com (Collin Winter)"
+DEFAULT_ITERATIONS = 100
+DEFAULT_REFERENCE = 'sun'
+
+
+def combinations(l):
+ """Pure-Python implementation of itertools.combinations(l, 2)."""
+ result = []
+ for x in range(len(l) - 1):
+ ls = l[x + 1:]
+ for y in ls:
+ result.append((l[x], y))
+ return result
+
+
+PI = 3.14159265358979323
+SOLAR_MASS = 4 * PI * PI
+DAYS_PER_YEAR = 365.24
+
+BODIES = {
+ 'sun': ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], SOLAR_MASS),
+
+ 'jupiter': ([4.84143144246472090e+00,
+ -1.16032004402742839e+00,
+ -1.03622044471123109e-01],
+ [1.66007664274403694e-03 * DAYS_PER_YEAR,
+ 7.69901118419740425e-03 * DAYS_PER_YEAR,
+ -6.90460016972063023e-05 * DAYS_PER_YEAR],
+ 9.54791938424326609e-04 * SOLAR_MASS),
+
+ 'saturn': ([8.34336671824457987e+00,
+ 4.12479856412430479e+00,
+ -4.03523417114321381e-01],
+ [-2.76742510726862411e-03 * DAYS_PER_YEAR,
+ 4.99852801234917238e-03 * DAYS_PER_YEAR,
+ 2.30417297573763929e-05 * DAYS_PER_YEAR],
+ 2.85885980666130812e-04 * SOLAR_MASS),
+
+ 'uranus': ([1.28943695621391310e+01,
+ -1.51111514016986312e+01,
+ -2.23307578892655734e-01],
+ [2.96460137564761618e-03 * DAYS_PER_YEAR,
+ 2.37847173959480950e-03 * DAYS_PER_YEAR,
+ -2.96589568540237556e-05 * DAYS_PER_YEAR],
+ 4.36624404335156298e-05 * SOLAR_MASS),
+
+ 'neptune': ([1.53796971148509165e+01,
+ -2.59193146099879641e+01,
+ 1.79258772950371181e-01],
+ [2.68067772490389322e-03 * DAYS_PER_YEAR,
+ 1.62824170038242295e-03 * DAYS_PER_YEAR,
+ -9.51592254519715870e-05 * DAYS_PER_YEAR],
+ 5.15138902046611451e-05 * SOLAR_MASS)}
+
+
+SYSTEM = list(BODIES.values())
+PAIRS = combinations(SYSTEM)
+
+
+def advance(dt, n, bodies=SYSTEM, pairs=PAIRS):
+ for i in range(n):
+ for (([x1, y1, z1], v1, m1),
+ ([x2, y2, z2], v2, m2)) in pairs:
+ dx = x1 - x2
+ dy = y1 - y2
+ dz = z1 - z2
+ mag = dt * ((dx * dx + dy * dy + dz * dz) ** (-1.5))
+ b1m = m1 * mag
+ b2m = m2 * mag
+ v1[0] -= dx * b2m
+ v1[1] -= dy * b2m
+ v1[2] -= dz * b2m
+ v2[0] += dx * b1m
+ v2[1] += dy * b1m
+ v2[2] += dz * b1m
+ for (r, [vx, vy, vz], m) in bodies:
+ r[0] += dt * vx
+ r[1] += dt * vy
+ r[2] += dt * vz
+
+
+def report_energy(bodies=SYSTEM, pairs=PAIRS, e=0.0):
+ for (((x1, y1, z1), v1, m1),
+ ((x2, y2, z2), v2, m2)) in pairs:
+ dx = x1 - x2
+ dy = y1 - y2
+ dz = z1 - z2
+ e -= (m1 * m2) / ((dx * dx + dy * dy + dz * dz) ** 0.5)
+ for (r, [vx, vy, vz], m) in bodies:
+ e += m * (vx * vx + vy * vy + vz * vz) / 2.
+ return e
+
+
+def offset_momentum(ref, bodies=SYSTEM, px=0.0, py=0.0, pz=0.0):
+ for (r, [vx, vy, vz], m) in bodies:
+ px -= vx * m
+ py -= vy * m
+ pz -= vz * m
+ (r, v, m) = ref
+ v[0] = px / m
+ v[1] = py / m
+ v[2] = pz / m
+
+
+def bench_nbody(loops, reference, iterations):
+ # Set up global state
+ offset_momentum(BODIES[reference])
+
+ range_it = range(loops)
+ t0 = time.perf_counter()
+
+ for _ in range_it:
+ report_energy()
+ advance(0.01, iterations)
+ report_energy()
+
+ return time.perf_counter() - t0
+
+
+def add_cmdline_args(cmd, args):
+ cmd.extend(("--iterations", str(args.iterations)))
+
+
+if __name__ == '__main__':
+ # Warmup
+ bench_nbody(128, DEFAULT_REFERENCE, 128)
+ # Showtime
+ print("It's showtime")
+ taken = bench_nbody(DEFAULT_ITERATIONS, DEFAULT_REFERENCE, 10_000)
+ print("Time taken is", taken, "s")
+
\ No newline at end of file
From be8fbf9d78dc53064e1f85c9f85dc785897b3bb8 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 31 Mar 2023 22:36:22 +0800
Subject: [PATCH 256/280] change iterations
---
bm_nbody.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bm_nbody.py b/bm_nbody.py
index 1b8aed05910c89..721bb3f3f76a1e 100644
--- a/bm_nbody.py
+++ b/bm_nbody.py
@@ -144,6 +144,6 @@ def add_cmdline_args(cmd, args):
bench_nbody(128, DEFAULT_REFERENCE, 128)
# Showtime
print("It's showtime")
- taken = bench_nbody(DEFAULT_ITERATIONS, DEFAULT_REFERENCE, 10_000)
+ taken = bench_nbody(DEFAULT_ITERATIONS, DEFAULT_REFERENCE, 50_000)
print("Time taken is", taken, "s")
\ No newline at end of file
From 2f12aadad6457f4a79146ac29c239b62b6ba6eb7 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 31 Mar 2023 23:44:33 +0800
Subject: [PATCH 257/280] Remove BB_TEST_ITER_GEN
---
Include/internal/pycore_opcode.h | 24 +++----
Include/opcode.h | 119 +++++++++++++++----------------
Lib/opcode.py | 1 -
Python/opcode_targets.h | 20 +++---
4 files changed, 81 insertions(+), 83 deletions(-)
diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h
index ce1195d45819ea..bef8db3d0ebbb1 100644
--- a/Include/internal/pycore_opcode.h
+++ b/Include/internal/pycore_opcode.h
@@ -55,7 +55,6 @@ const uint8_t _PyOpcode_Caches[256] = {
};
const uint8_t _PyOpcode_Deopt[256] = {
- [BB_TEST_ITER_GEN] = BB_TEST_ITER,
[BB_TEST_ITER_LIST] = BB_TEST_ITER,
[BB_TEST_ITER_RANGE] = BB_TEST_ITER,
[BB_TEST_ITER_TUPLE] = BB_TEST_ITER,
@@ -305,23 +304,23 @@ static const char *const _PyOpcode_OpName[263] = {
[GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER",
[BB_TEST_ITER_RANGE] = "BB_TEST_ITER_RANGE",
[LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
- [BB_TEST_ITER_GEN] = "BB_TEST_ITER_GEN",
[LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS",
+ [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
[LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
[RETURN_GENERATOR] = "RETURN_GENERATOR",
- [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
[LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
[LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
[LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
[LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
[LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
[LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT",
- [RETURN_VALUE] = "RETURN_VALUE",
[LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
- [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
+ [RETURN_VALUE] = "RETURN_VALUE",
[LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
+ [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
[LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
[LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
+ [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
[POP_EXCEPT] = "POP_EXCEPT",
[STORE_NAME] = "STORE_NAME",
[DELETE_NAME] = "DELETE_NAME",
@@ -344,9 +343,9 @@ static const char *const _PyOpcode_OpName[263] = {
[IMPORT_NAME] = "IMPORT_NAME",
[IMPORT_FROM] = "IMPORT_FROM",
[JUMP_FORWARD] = "JUMP_FORWARD",
- [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
[LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
[LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
+ [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
[POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
[POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
[LOAD_GLOBAL] = "LOAD_GLOBAL",
@@ -374,9 +373,9 @@ static const char *const _PyOpcode_OpName[263] = {
[STORE_DEREF] = "STORE_DEREF",
[DELETE_DEREF] = "DELETE_DEREF",
[JUMP_BACKWARD] = "JUMP_BACKWARD",
- [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
- [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
[STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
+ [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
+ [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD",
@@ -386,29 +385,28 @@ static const char *const _PyOpcode_OpName[263] = {
[YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS",
- [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
+ [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING",
- [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
[STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
+ [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
- [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
[SEND_GEN] = "SEND_GEN",
[BB_BRANCH] = "BB_BRANCH",
[BB_BRANCH_IF_FLAG_UNSET] = "BB_BRANCH_IF_FLAG_UNSET",
+ [BB_BRANCH_IF_FLAG_SET] = "BB_BRANCH_IF_FLAG_SET",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
[CALL_INTRINSIC_1] = "CALL_INTRINSIC_1",
[CALL_INTRINSIC_2] = "CALL_INTRINSIC_2",
- [BB_BRANCH_IF_FLAG_SET] = "BB_BRANCH_IF_FLAG_SET",
[BB_JUMP_IF_FLAG_UNSET] = "BB_JUMP_IF_FLAG_UNSET",
[BB_JUMP_IF_FLAG_SET] = "BB_JUMP_IF_FLAG_SET",
[BB_TEST_ITER] = "BB_TEST_ITER",
@@ -439,6 +437,7 @@ static const char *const _PyOpcode_OpName[263] = {
[STORE_FAST_BOXED_UNBOXED] = "STORE_FAST_BOXED_UNBOXED",
[STORE_FAST_UNBOXED_BOXED] = "STORE_FAST_UNBOXED_BOXED",
[STORE_FAST_UNBOXED_UNBOXED] = "STORE_FAST_UNBOXED_UNBOXED",
+ [205] = "<205>",
[206] = "<206>",
[207] = "<207>",
[208] = "<208>",
@@ -501,6 +500,7 @@ static const char *const _PyOpcode_OpName[263] = {
#define EXTRA_CASES \
+ case 205: \
case 206: \
case 207: \
case 208: \
diff --git a/Include/opcode.h b/Include/opcode.h
index 3d99716fec63e6..749e50e727fe37 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -161,68 +161,67 @@ extern "C" {
#define FOR_ITER_TUPLE 63
#define FOR_ITER_RANGE 64
#define FOR_ITER_GEN 65
-#define BB_TEST_ITER_GEN 72
-#define LOAD_ATTR_CLASS 73
-#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 76
-#define LOAD_ATTR_INSTANCE_VALUE 77
-#define LOAD_ATTR_MODULE 78
-#define LOAD_ATTR_PROPERTY 79
-#define LOAD_ATTR_SLOT 80
-#define LOAD_ATTR_WITH_HINT 81
-#define LOAD_ATTR_METHOD_LAZY_DICT 82
-#define LOAD_ATTR_METHOD_NO_DICT 84
-#define LOAD_ATTR_METHOD_WITH_VALUES 86
-#define LOAD_CONST__LOAD_FAST 87
-#define LOAD_FAST__LOAD_CONST 88
-#define LOAD_FAST__LOAD_FAST 111
-#define LOAD_GLOBAL_BUILTIN 112
-#define LOAD_GLOBAL_MODULE 113
-#define STORE_ATTR_INSTANCE_VALUE 141
-#define STORE_ATTR_SLOT 143
-#define STORE_ATTR_WITH_HINT 153
-#define STORE_FAST__LOAD_FAST 154
-#define STORE_FAST__STORE_FAST 158
-#define STORE_SUBSCR_DICT 159
-#define STORE_SUBSCR_LIST_INT 160
-#define UNPACK_SEQUENCE_LIST 161
-#define UNPACK_SEQUENCE_TUPLE 166
-#define UNPACK_SEQUENCE_TWO_TUPLE 167
-#define SEND_GEN 168
+#define LOAD_ATTR_CLASS 72
+#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 73
+#define LOAD_ATTR_INSTANCE_VALUE 76
+#define LOAD_ATTR_MODULE 77
+#define LOAD_ATTR_PROPERTY 78
+#define LOAD_ATTR_SLOT 79
+#define LOAD_ATTR_WITH_HINT 80
+#define LOAD_ATTR_METHOD_LAZY_DICT 81
+#define LOAD_ATTR_METHOD_NO_DICT 82
+#define LOAD_ATTR_METHOD_WITH_VALUES 84
+#define LOAD_CONST__LOAD_FAST 86
+#define LOAD_FAST__LOAD_CONST 87
+#define LOAD_FAST__LOAD_FAST 88
+#define LOAD_GLOBAL_BUILTIN 111
+#define LOAD_GLOBAL_MODULE 112
+#define STORE_ATTR_INSTANCE_VALUE 113
+#define STORE_ATTR_SLOT 141
+#define STORE_ATTR_WITH_HINT 143
+#define STORE_FAST__LOAD_FAST 153
+#define STORE_FAST__STORE_FAST 154
+#define STORE_SUBSCR_DICT 158
+#define STORE_SUBSCR_LIST_INT 159
+#define UNPACK_SEQUENCE_LIST 160
+#define UNPACK_SEQUENCE_TUPLE 161
+#define UNPACK_SEQUENCE_TWO_TUPLE 166
+#define SEND_GEN 167
#define DO_TRACING 255
// Tier 2 interpreter ops
-#define BB_BRANCH 169
-#define BB_BRANCH_IF_FLAG_UNSET 170
-#define BB_BRANCH_IF_FLAG_SET 175
-#define BB_JUMP_IF_FLAG_UNSET 176
-#define BB_JUMP_IF_FLAG_SET 177
-#define BB_TEST_ITER 178
-#define BB_TEST_ITER_RANGE 179
-#define BB_TEST_ITER_LIST 180
-#define BB_TEST_ITER_TUPLE 181
-#define BB_TEST_POP_IF_FALSE 182
-#define BB_TEST_POP_IF_TRUE 183
-#define BB_TEST_POP_IF_NOT_NONE 184
-#define BB_TEST_POP_IF_NONE 185
-#define BB_JUMP_BACKWARD_LAZY 186
-#define BINARY_CHECK_INT 187
-#define BINARY_CHECK_FLOAT 188
-#define CHECK_LIST 189
-#define BINARY_OP_ADD_INT_REST 190
-#define BINARY_OP_ADD_FLOAT_UNBOXED 191
-#define BINARY_OP_SUBTRACT_INT_REST 192
-#define BINARY_OP_SUBTRACT_FLOAT_UNBOXED 193
-#define BINARY_OP_MULTIPLY_INT_REST 194
-#define BINARY_OP_MULTIPLY_FLOAT_UNBOXED 195
-#define BINARY_SUBSCR_LIST_INT_REST 196
-#define STORE_SUBSCR_LIST_INT_REST 197
-#define POP_TOP_NO_DECREF 198
-#define UNBOX_FLOAT 199
-#define BOX_FLOAT 200
-#define COPY_NO_INCREF 201
-#define LOAD_FAST_NO_INCREF 202
-#define STORE_FAST_BOXED_UNBOXED 203
-#define STORE_FAST_UNBOXED_BOXED 204
-#define STORE_FAST_UNBOXED_UNBOXED 205
+#define BB_BRANCH 168
+#define BB_BRANCH_IF_FLAG_UNSET 169
+#define BB_BRANCH_IF_FLAG_SET 170
+#define BB_JUMP_IF_FLAG_UNSET 175
+#define BB_JUMP_IF_FLAG_SET 176
+#define BB_TEST_ITER 177
+#define BB_TEST_ITER_RANGE 178
+#define BB_TEST_ITER_LIST 179
+#define BB_TEST_ITER_TUPLE 180
+#define BB_TEST_POP_IF_FALSE 181
+#define BB_TEST_POP_IF_TRUE 182
+#define BB_TEST_POP_IF_NOT_NONE 183
+#define BB_TEST_POP_IF_NONE 184
+#define BB_JUMP_BACKWARD_LAZY 185
+#define BINARY_CHECK_INT 186
+#define BINARY_CHECK_FLOAT 187
+#define CHECK_LIST 188
+#define BINARY_OP_ADD_INT_REST 189
+#define BINARY_OP_ADD_FLOAT_UNBOXED 190
+#define BINARY_OP_SUBTRACT_INT_REST 191
+#define BINARY_OP_SUBTRACT_FLOAT_UNBOXED 192
+#define BINARY_OP_MULTIPLY_INT_REST 193
+#define BINARY_OP_MULTIPLY_FLOAT_UNBOXED 194
+#define BINARY_SUBSCR_LIST_INT_REST 195
+#define STORE_SUBSCR_LIST_INT_REST 196
+#define POP_TOP_NO_DECREF 197
+#define UNBOX_FLOAT 198
+#define BOX_FLOAT 199
+#define COPY_NO_INCREF 200
+#define LOAD_FAST_NO_INCREF 201
+#define STORE_FAST_BOXED_UNBOXED 202
+#define STORE_FAST_UNBOXED_BOXED 203
+#define STORE_FAST_UNBOXED_UNBOXED 204
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 1e26407a16ab06..297d0001793abf 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -333,7 +333,6 @@ def pseudo_op(name, op, real_ops):
"BB_TEST_ITER_LIST",
"BB_TEST_ITER_TUPLE",
"BB_TEST_ITER_RANGE",
- "BB_TEST_ITER_GEN",
],
"LOAD_ATTR": [
# These potentially push [NULL, bound method] onto the stack.
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 48c858e0cd2d00..bd0902f1a9f59d 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -71,23 +71,23 @@ static void *opcode_targets[256] = {
&&TARGET_GET_YIELD_FROM_ITER,
&&TARGET_BB_TEST_ITER_RANGE,
&&TARGET_LOAD_BUILD_CLASS,
- &&TARGET_BB_TEST_ITER_GEN,
&&TARGET_LOAD_ATTR_CLASS,
+ &&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
&&TARGET_LOAD_ASSERTION_ERROR,
&&TARGET_RETURN_GENERATOR,
- &&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
&&TARGET_LOAD_ATTR_INSTANCE_VALUE,
&&TARGET_LOAD_ATTR_MODULE,
&&TARGET_LOAD_ATTR_PROPERTY,
&&TARGET_LOAD_ATTR_SLOT,
&&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LOAD_ATTR_METHOD_LAZY_DICT,
- &&TARGET_RETURN_VALUE,
&&TARGET_LOAD_ATTR_METHOD_NO_DICT,
- &&TARGET_SETUP_ANNOTATIONS,
+ &&TARGET_RETURN_VALUE,
&&TARGET_LOAD_ATTR_METHOD_WITH_VALUES,
+ &&TARGET_SETUP_ANNOTATIONS,
&&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_LOAD_FAST__LOAD_CONST,
+ &&TARGET_LOAD_FAST__LOAD_FAST,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
&&TARGET_DELETE_NAME,
@@ -110,9 +110,9 @@ static void *opcode_targets[256] = {
&&TARGET_IMPORT_NAME,
&&TARGET_IMPORT_FROM,
&&TARGET_JUMP_FORWARD,
- &&TARGET_LOAD_FAST__LOAD_FAST,
&&TARGET_LOAD_GLOBAL_BUILTIN,
&&TARGET_LOAD_GLOBAL_MODULE,
+ &&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_POP_JUMP_IF_FALSE,
&&TARGET_POP_JUMP_IF_TRUE,
&&TARGET_LOAD_GLOBAL,
@@ -140,9 +140,9 @@ static void *opcode_targets[256] = {
&&TARGET_STORE_DEREF,
&&TARGET_DELETE_DEREF,
&&TARGET_JUMP_BACKWARD,
- &&TARGET_STORE_ATTR_INSTANCE_VALUE,
- &&TARGET_CALL_FUNCTION_EX,
&&TARGET_STORE_ATTR_SLOT,
+ &&TARGET_CALL_FUNCTION_EX,
+ &&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
&&TARGET_SET_ADD,
@@ -152,24 +152,24 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE,
&&TARGET_RESUME,
&&TARGET_MATCH_CLASS,
- &&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_STORE_FAST__LOAD_FAST,
+ &&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING,
- &&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_STORE_SUBSCR_DICT,
&&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_UNPACK_SEQUENCE_LIST,
+ &&TARGET_UNPACK_SEQUENCE_TUPLE,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
- &&TARGET_UNPACK_SEQUENCE_TUPLE,
&&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
&&TARGET_SEND_GEN,
&&_unknown_opcode,
&&_unknown_opcode,
+ &&_unknown_opcode,
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_CALL_INTRINSIC_1,
From 74b20cbe479bbf1f99499442a9c11d415eeaa7ce Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Fri, 31 Mar 2023 23:57:09 +0800
Subject: [PATCH 258/280] make work on gcc
---
Makefile.pre.in | 1 +
1 file changed, 1 insertion(+)
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 74e4171b010d0f..1a6b1cfa1d074b 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -411,6 +411,7 @@ PYTHON_OBJS= \
Python/pytime.o \
Python/bootstrap_hash.o \
Python/specialize.o \
+ Python/tier2.o \
Python/structmember.o \
Python/symtable.o \
Python/sysmodule.o \
From 309715c006720275d315a76f352d22944993a0e3 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 1 Apr 2023 00:03:58 +0800
Subject: [PATCH 259/280] regen opcode targets
---
Python/makeopcodetargets.py | 4 +++
Python/opcode_targets.h | 66 ++++++++++++++++++-------------------
2 files changed, 37 insertions(+), 33 deletions(-)
diff --git a/Python/makeopcodetargets.py b/Python/makeopcodetargets.py
index 33a4b4a76a1253..c5043542fb592d 100755
--- a/Python/makeopcodetargets.py
+++ b/Python/makeopcodetargets.py
@@ -41,6 +41,10 @@ def write_contents(f):
while targets[next_op] != '_unknown_opcode':
next_op += 1
targets[next_op] = "TARGET_%s" % opname
+ for opname in opcode._uops:
+ while targets[next_op] != '_unknown_opcode':
+ next_op += 1
+ targets[next_op] = "TARGET_%s" % opname
f.write("static void *opcode_targets[256] = {\n")
f.write(",\n".join([" &&%s" % s for s in targets]))
f.write("\n};\n")
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index bd0902f1a9f59d..55e0cd2a690948 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -167,43 +167,43 @@ static void *opcode_targets[256] = {
&&TARGET_DICT_UPDATE,
&&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
&&TARGET_SEND_GEN,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
+ &&TARGET_BB_BRANCH,
+ &&TARGET_BB_BRANCH_IF_FLAG_UNSET,
+ &&TARGET_BB_BRANCH_IF_FLAG_SET,
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_CALL_INTRINSIC_1,
&&TARGET_CALL_INTRINSIC_2,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
- &&_unknown_opcode,
+ &&TARGET_BB_JUMP_IF_FLAG_UNSET,
+ &&TARGET_BB_JUMP_IF_FLAG_SET,
+ &&TARGET_BB_TEST_ITER,
+ &&TARGET_BB_TEST_ITER_RANGE,
+ &&TARGET_BB_TEST_ITER_LIST,
+ &&TARGET_BB_TEST_ITER_TUPLE,
+ &&TARGET_BB_TEST_POP_IF_FALSE,
+ &&TARGET_BB_TEST_POP_IF_TRUE,
+ &&TARGET_BB_TEST_POP_IF_NOT_NONE,
+ &&TARGET_BB_TEST_POP_IF_NONE,
+ &&TARGET_BB_JUMP_BACKWARD_LAZY,
+ &&TARGET_BINARY_CHECK_INT,
+ &&TARGET_BINARY_CHECK_FLOAT,
+ &&TARGET_CHECK_LIST,
+ &&TARGET_BINARY_OP_ADD_INT_REST,
+ &&TARGET_BINARY_OP_ADD_FLOAT_UNBOXED,
+ &&TARGET_BINARY_OP_SUBTRACT_INT_REST,
+ &&TARGET_BINARY_OP_SUBTRACT_FLOAT_UNBOXED,
+ &&TARGET_BINARY_OP_MULTIPLY_INT_REST,
+ &&TARGET_BINARY_OP_MULTIPLY_FLOAT_UNBOXED,
+ &&TARGET_BINARY_SUBSCR_LIST_INT_REST,
+ &&TARGET_STORE_SUBSCR_LIST_INT_REST,
+ &&TARGET_POP_TOP_NO_DECREF,
+ &&TARGET_UNBOX_FLOAT,
+ &&TARGET_BOX_FLOAT,
+ &&TARGET_COPY_NO_INCREF,
+ &&TARGET_LOAD_FAST_NO_INCREF,
+ &&TARGET_STORE_FAST_BOXED_UNBOXED,
+ &&TARGET_STORE_FAST_UNBOXED_BOXED,
+ &&TARGET_STORE_FAST_UNBOXED_UNBOXED,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
From 1ed4c15b17624025c6d843779815690fccb5748c Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sat, 1 Apr 2023 00:54:22 +0800
Subject: [PATCH 260/280] fix for makefile
---
Makefile.pre.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 1a6b1cfa1d074b..1818cf1362e24b 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -1198,7 +1198,7 @@ DEEPFREEZE_DEPS=$(srcdir)/Tools/build/deepfreeze.py $(FREEZE_MODULE_DEPS) $(FROZ
# BEGIN: deepfreeze modules
Python/deepfreeze/deepfreeze.c: $(DEEPFREEZE_DEPS)
- $(PYTHON_FOR_FREEZE) $(srcdir)/Tools/build/deepfreeze.py \
+ python3 $(srcdir)/Tools/build/deepfreeze.py \
Python/frozen_modules/importlib._bootstrap.h:importlib._bootstrap \
Python/frozen_modules/importlib._bootstrap_external.h:importlib._bootstrap_external \
Python/frozen_modules/zipimport.h:zipimport \
From 552949599863d4b8dbb48f1e43b50643bbc9d5a9 Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sat, 1 Apr 2023 00:58:29 +0800
Subject: [PATCH 261/280] Update bm_nbody.py
---
bm_nbody.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/bm_nbody.py b/bm_nbody.py
index 721bb3f3f76a1e..51c2d383a47c4c 100644
--- a/bm_nbody.py
+++ b/bm_nbody.py
@@ -128,9 +128,9 @@ def bench_nbody(loops, reference, iterations):
t0 = time.perf_counter()
for _ in range_it:
- report_energy()
+ # report_energy()
advance(0.01, iterations)
- report_energy()
+ # report_energy()
return time.perf_counter() - t0
From b758a31d2a8fbfb93818b91fa002c792f1bf0168 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 8 Apr 2023 02:23:14 +0800
Subject: [PATCH 262/280] Add documentation
---
Python/tier2.c | 595 +++++++++++++++++++++++++++++++++++++------------
1 file changed, 456 insertions(+), 139 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 98071e272e76f3..4dc65af081e0b2 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -39,6 +39,11 @@ static inline int IS_SCOPE_EXIT_OPCODE(int opcode);
////////// TYPE CONTEXT FUNCTIONS
+/**
+ * @brief Allocates and initializes the type context for a code object.
+ * @param co The code object the type context belongs to.
+ * @return The newly-created type context.
+*/
static _PyTier2TypeContext *
initialize_type_context(const PyCodeObject *co)
{
@@ -82,6 +87,11 @@ initialize_type_context(const PyCodeObject *co)
return type_context;
}
+/**
+ * @brief Does a deepcopy of a type context and all its nodes.
+ * @param type_context The type context to copy.
+ * @return Newly copied type context.
+*/
static _PyTier2TypeContext *
_PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
{
@@ -184,6 +194,10 @@ _PyTier2TypeContext_Copy(const _PyTier2TypeContext *type_context)
return new_type_context;
}
+/**
+ * @brief Destructor for a type context.
+ * @param type_context The type context to destroy/free.
+*/
void
_PyTier2TypeContext_Free(_PyTier2TypeContext *type_context)
{
@@ -210,6 +224,11 @@ __typenode_get_rootptr(_Py_TYPENODE_t ref)
return ref_ptr;
}
+/**
+ * @brief Gets the actual PyTypeObject* that a type node points to.
+ * @param node The type propagator node to look up.
+ * @return The referenced PyTypeObject*.
+*/
static PyTypeObject*
typenode_get_type(_Py_TYPENODE_t node)
{
@@ -229,6 +248,13 @@ typenode_get_type(_Py_TYPENODE_t node)
}
}
+// @TODO @JULES
+/**
+ * @brief
+ * @param src
+ * @param dst
+ * @param src_is_new
+*/
static void
__type_propagate_TYPE_SET(
_Py_TYPENODE_t *src, _Py_TYPENODE_t *dst, bool src_is_new)
@@ -272,6 +298,14 @@ __type_propagate_TYPE_SET(
}
}
+// @TODO @JULES
+/**
+ * @brief
+ * @param type_context
+ * @param src
+ * @param dst
+ * @param src_is_new
+*/
static void
__type_propagate_TYPE_OVERWRITE(
_PyTier2TypeContext *type_context,
@@ -387,7 +421,16 @@ __type_propagate_TYPE_OVERWRITE(
}
}
-// src and dst are assumed to already be within the type context
+
+// @TODO @JULES
+/**
+ * @brief
+ * @param type_context
+ * @param src
+ * @param dst
+ *
+ * src and dst are assumed to already be within the type context
+*/
static void
__type_propagate_TYPE_SWAP(
_PyTier2TypeContext *type_context,
@@ -455,6 +498,11 @@ __type_propagate_TYPE_SWAP(
*src ^= *dst;
}
+/**
+ * @brief Shrink a type stack by `idx` entries.
+ * @param type_stackptr The pointer to one after the top of type stack.
+ * @param idx The number of entries to shrink the stack by.
+*/
static void
__type_stack_shrink(_Py_TYPENODE_t **type_stackptr, int idx)
{
@@ -476,6 +524,10 @@ __type_stack_shrink(_Py_TYPENODE_t **type_stackptr, int idx)
#if TYPEPROP_DEBUG
+/**
+ * @brief Print the entries in a type context (along with locals).
+ * @param type_context The type context to display.
+*/
static void
print_typestack(const _PyTier2TypeContext *type_context)
{
@@ -546,7 +598,14 @@ print_typestack(const _PyTier2TypeContext *type_context)
}
#endif
-// Type propagates across a single function.
+
+/**
+ * @brief Type propagate across a single instruction
+ * @param opcode The instruction opcode.
+ * @param oparg The instruction oparg.
+ * @param type_context The current type context in the basic block.
+ * @param consts The co_consts array of the code object that holds the constants.
+*/
static void
type_propagate(
int opcode, int oparg,
@@ -611,38 +670,14 @@ type_propagate(
#undef TYPECONST_GET
}
-////////// Utility functions
-
-// Gets end of the bytecode for a code object.
-_Py_CODEUNIT *
-_PyCode_GetEnd(PyCodeObject *co)
-{
- _Py_CODEUNIT *end = (_Py_CODEUNIT *)(co->co_code_adaptive + _PyCode_NBYTES(co));
- return end;
-}
-
-// Gets end of actual bytecode executed. _PyCode_GetEnd might return a CACHE instruction.
-_Py_CODEUNIT *
-_PyCode_GetLogicalEnd(PyCodeObject *co)
-{
- _Py_CODEUNIT *end = _PyCode_GetEnd(co);
- while (_Py_OPCODE(*end) == CACHE) {
- end--;
- }
-#if BB_DEBUG
- if (!IS_SCOPE_EXIT_OPCODE(_Py_OPCODE(*end))) {
- fprintf(stderr, "WRONG EXIT OPCODE: %d\n", _Py_OPCODE(*end));
- assert(0);
- }
-#endif
- assert(IS_SCOPE_EXIT_OPCODE(_Py_OPCODE(*end)));
- return end;
-}
-
////////// BB SPACE FUNCTIONS
-// Creates the overallocated array for the BBs.
+/**
+ * @brief Creates the overallocated array for the BBs.
+ * @param space_to_alloc How much space to allocate.
+ * @return A new space that we can write basic block instructions to.
+*/
static _PyTier2BBSpace *
_PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
{
@@ -655,9 +690,12 @@ _PyTier2_CreateBBSpace(Py_ssize_t space_to_alloc)
return bb_space;
}
-// Checks if there's enough space in the BBSpace for space_requested.
-// Reallocates if neccessary.
-// DOES NOT ADJUST THE WATER LEVEL AS THIS IS JUST A CHECK. ONLY ADJUSTS THE MAX SPACE.
+/**
+ * @brief Checks if there's enough space in the basic block space for space_requested.
+ * @param co The code object's tier2 basic block space to check
+ * @param space_requested The amount of extra space you need.
+ * @return The space of the code object after checks.
+*/
static _PyTier2BBSpace *
_PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_requested)
{
@@ -673,20 +711,24 @@ _PyTier2_BBSpaceCheckAndReallocIfNeeded(PyCodeObject *co, Py_ssize_t space_reque
#endif
// @TODO We can't Realloc, we actually need to do the linked list method.
Py_UNREACHABLE();
- //_PyTier2BBSpace *new_space = PyMem_Realloc(curr, new_size);
- //if (new_space == NULL) {
- // return NULL;
- //}
- //co->_tier2_info->_bb_space = new_space;
- //new_space->max_capacity = new_size;
- //return new_space;
}
// We have enouogh space. Don't do anything, j
return curr;
}
-// BB METADATA FUNCTIONS
-
+//// BB METADATA FUNCTIONS
+
+/**
+ * @brief Allocate the metadata associate with a basic block.
+ * The metadata contains things like the type context at the end of the basic block.*
+ *
+ * @param co The code object this basic block belongs to.
+ * @param tier2_start The start of the tier 2 code (start of the basic block).
+ * @param tier1_end The end of the tie 1 code this basic block points to.
+ * @param type_context The type context associated with this basic block.
+ * @return Newly allocated metadata for this basic block.
+ *
+*/
static _PyTier2BBMetadata *
allocate_bb_metadata(PyCodeObject *co, _Py_CODEUNIT *tier2_start,
_Py_CODEUNIT *tier1_end,
@@ -705,8 +747,12 @@ allocate_bb_metadata(PyCodeObject *co, _Py_CODEUNIT *tier2_start,
}
-// Writes BB metadata to code object's tier2info bb_data field.
-// 0 on success, 1 on error.
+/**
+ * @brief Writes BB metadata to code object's tier2info bb_data field.
+ * @param co The code object whose metadata we should write to.
+ * @param metadata The metadata to write.
+ * @return 0 on success, 1 on error.
+*/
static int
write_bb_metadata(PyCodeObject *co, _PyTier2BBMetadata *metadata)
{
@@ -737,6 +783,17 @@ write_bb_metadata(PyCodeObject *co, _PyTier2BBMetadata *metadata)
return 0;
}
+/**
+ * @brief Allocate BB metadata, then write it.
+ * Consume this instead of `allocate_bb_metadata`.
+ *
+ * @param co The code object the metadat belongs to.
+ * @param tier2_start The start of the tier 2 code (start of the basic block).
+ * @param tier1_end The end of the tie 1 code this basic block points to.
+ * @param type_context The type context associated with this basic block.
+ * @return Newly allocated metadata for this basic block.
+ *
+*/
static _PyTier2BBMetadata *
_PyTier2_AllocateBBMetaData(PyCodeObject *co, _Py_CODEUNIT *tier2_start,
_Py_CODEUNIT *tier1_end,
@@ -758,14 +815,22 @@ _PyTier2_AllocateBBMetaData(PyCodeObject *co, _Py_CODEUNIT *tier2_start,
/* Opcode detection functions. Keep in sync with compile.c and dis! */
-// dis.hasjabs
+/**
+ * @brief C equivalent of dis.hasjabs
+ * @param opcode Opcode of the instruction.
+ * @return Whether this is an absolute jump.
+*/
static inline int
IS_JABS_OPCODE(int opcode)
{
return 0;
}
-// dis.hasjrel
+/**
+ * @brief C equivalent of dis.hasjrel
+ * @param opcode Opcode of the instruction.
+ * @return Whether this is a relative jump.
+*/
static inline int
IS_JREL_OPCODE(int opcode)
{
@@ -788,6 +853,11 @@ IS_JREL_OPCODE(int opcode)
}
}
+/**
+ * @brief Checks if this is a backwards jump instruction.
+ * @param opcode Opcode of the instruction.
+ * @return Whether this is a backwards jump.
+*/
static inline int
IS_JUMP_BACKWARDS_OPCODE(int opcode)
{
@@ -797,20 +867,23 @@ IS_JUMP_BACKWARDS_OPCODE(int opcode)
}
-// dis.hasjrel || dis.hasjabs
+/**
+ * @brief C equivalent of dis.hasjrel || dis.hasjabs
+ * @param opcode Opcode of the instruction.
+ * @return Whether this is a jump instruction.
+*/
static inline int
IS_JUMP_OPCODE(int opcode)
{
return IS_JREL_OPCODE(opcode) || IS_JABS_OPCODE(opcode);
}
-// dis.hascompare
-static inline int
-IS_COMPARE_OPCODE(int opcode)
-{
- return opcode == COMPARE_OP;
-}
+/**
+ * @brief Checks whether the opcode is a scope exit.
+ * @param opcode Opcode of the instruction.
+ * @return Whether this is a scope exit.
+*/
static inline int
IS_SCOPE_EXIT_OPCODE(int opcode)
{
@@ -827,14 +900,23 @@ IS_SCOPE_EXIT_OPCODE(int opcode)
}
// KEEP IN SYNC WITH COMPILE.c!!!!
+/**
+ * @brief Checks whether the opcode terminates a basic block.
+ * @param opcode Opcode of the instruction.
+ * @return Whether this is the end of a basic block.
+*/
static int
IS_TERMINATOR_OPCODE(int opcode)
{
return IS_JUMP_OPCODE(opcode) || IS_SCOPE_EXIT_OPCODE(opcode);
}
-// Opcodes that we can't handle at the moment. If we see them,
-// ditch tier 2 attempts.
+/**
+ * @brief Opcodes that we can't handle at the moment. If we see them, ditch tier 2 attempts.
+ * @param opcode Opcode of the instruction.
+ * @param nextop The opcode of the following instruction.
+ * @return Whether this opcode is forbidden.
+*/
static inline int
IS_FORBIDDEN_OPCODE(int opcode, int nextop)
{
@@ -884,8 +966,17 @@ IS_FORBIDDEN_OPCODE(int opcode, int nextop)
}
}
-// Decides what values we need to rebox.
-// num_elements is how many stack entries and thus how far from the TOS we want to rebox.
+/**
+ * @brief Decides what values we need to rebox.
+ *
+ * This function automatically emits rebox instructions if needed.
+ *
+ * @param write_curr Instruction write buffer.
+ * @param type_context The type context to base our decisions on.
+ * @param num_elements How many stack entries and thus how far from the TOS we want to rebox.
+ * @return Pointer to new instruction write buffer end.
+ *
+*/
static inline _Py_CODEUNIT *
rebox_stack(_Py_CODEUNIT *write_curr,
_PyTier2TypeContext *type_context, int num_elements)
@@ -902,6 +993,16 @@ rebox_stack(_Py_CODEUNIT *write_curr,
return write_curr;
}
+/**
+ * @brief Emit CACHE entries for an instruction.
+ * NOTE: THIS DOES NOT PRESERVE PREVIOUS CACHE INFORMATION.
+ * THUS THIS INITIALIZES A CLEAN SLATE.
+ *
+ * @param write_curr Tier 2 instruction write buffer.
+ * @param cache_entries Number of cache entries to emit.
+ * @return Pointer to end of tier 2 instruction write buffer.
+ *
+*/
static inline _Py_CODEUNIT *
emit_cache_entries(_Py_CODEUNIT *write_curr, int cache_entries)
{
@@ -916,6 +1017,13 @@ emit_cache_entries(_Py_CODEUNIT *write_curr, int cache_entries)
#define BB_IS_TYPE_BRANCH(bb_id_raw) (bb_id_raw & 1)
#define MAKE_TAGGED_BB_ID(bb_id, type_branch) (bb_id << 1 | type_branch)
+/**
+ * @brief Write a BB's ID to a CACHE entry.
+ * @param cache The CACHE entry to write to.
+ * @param bb_id The BB's ID.
+ * @param is_type_guard Whether the BB ends with a type guard.
+ *
+*/
static inline void
write_bb_id(_PyBBBranchCache *cache, int bb_id, bool is_type_guard) {
assert((uint16_t)(bb_id) == bb_id);
@@ -924,8 +1032,11 @@ write_bb_id(_PyBBBranchCache *cache, int bb_id, bool is_type_guard) {
cache->bb_id_tagged = MAKE_TAGGED_BB_ID((uint16_t)bb_id, is_type_guard);
}
-// The order/hierarchy to emit type guards
-// NEED TO ADD TO THIS EVERY TIME WE ADD A NEW ONE.
+/**
+ * @brief The order/hierarchy to emit type guards.
+ *
+ * NEED TO ADD TO THIS EVERY TIME WE ADD A NEW ONE.
+*/
static int type_guard_ladder[256] = {
-1,
BINARY_CHECK_FLOAT,
@@ -935,8 +1046,11 @@ static int type_guard_ladder[256] = {
-1,
};
-// Type guard to index in the ladder.
-// KEEP IN SYNC WITH INDEX IN type_guard_ladder
+/**
+ * @brief Type guard to index in the ladder.
+ *
+ * KEEP IN SYNC WITH INDEX IN type_guard_ladder
+*/
static int type_guard_to_index[256] = {
[BINARY_CHECK_FLOAT] = 1,
[BINARY_CHECK_INT] = 2,
@@ -944,6 +1058,14 @@ static int type_guard_to_index[256] = {
};
+/**
+ * @brief Emit a type guard.
+ * @param write_curr The tier 2 instruction write buffer.
+ * @param guard_opcode The opcode of the type guard.
+ * @param guard_oparg The oparg of the type guard.
+ * @param bb_id The BB ID of the current BB we're writing to.
+ * @return Pointer to new end of the tier 2 instruction write buffer.
+*/
static inline _Py_CODEUNIT *
emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int guard_oparg, int bb_id)
{
@@ -963,13 +1085,24 @@ emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int guard_oparg, int
return write_curr;
}
-// Converts the tier 1 branch bytecode to tier 2 branch bytecode.
-// This converts sequence of instructions like
-// POP_JUMP_IF_FALSE
-// to
-// BB_TEST_POP_IF_FALSE
-// BB_BRANCH
-// CACHE (bb_id of the current BB << 1 | is_type_branch)
+/**
+ * @brief Converts the tier 1 branch bytecode to tier 2 branch bytecode.
+ *
+ * This converts sequence of instructions like
+ * POP_JUMP_IF_FALSE
+ * to
+ * BB_TEST_POP_IF_FALSE
+ * BB_BRANCH
+ * CACHE (bb_id of the current BB << 1 | is_type_branch)*
+ *
+ * @param type_context The type_context of the current BB.
+ * @param write_curr The tier 2 instruction write buffer.
+ * @param branch The tier 1 branch instruction to convert.
+ * @param bb_id The BB_ID of the current BB.
+ * @param oparg Oparg of the branch instruction (respects EXTENDED_ARGS).
+ * @return The updated tier 2 instruction write buffer end.
+ *
+*/
static inline _Py_CODEUNIT *
emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
_Py_CODEUNIT branch, int bb_id, int oparg)
@@ -1096,6 +1229,13 @@ emit_logical_branch(_PyTier2TypeContext *type_context, _Py_CODEUNIT *write_curr,
}
}
+/**
+ * @brief Emits the exit of a scope.
+ * @param write_curr The tier 2 instruction write buffer.
+ * @param exit The tier 1 exit instruction.
+ * @param type_context The BB's type context.
+ * @return The updated tier 2 instruction write buffer end.
+*/
static inline _Py_CODEUNIT *
emit_scope_exit(_Py_CODEUNIT *write_curr, _Py_CODEUNIT exit,
_PyTier2TypeContext *type_context)
@@ -1126,6 +1266,13 @@ emit_scope_exit(_Py_CODEUNIT *write_curr, _Py_CODEUNIT exit,
}
}
+/**
+ * @brief Emit a single instruction. (Respects EXTENDED_ARG).
+ * @param write_curr The tier 2 instruction write buffer.
+ * @param opcode The instruction's opcode.
+ * @param oparg The instruction's oparg.
+ * @return The updated tier 2 instruction write buffer end.
+*/
static inline _Py_CODEUNIT *
emit_i(_Py_CODEUNIT *write_curr, int opcode, int oparg)
{
@@ -1140,9 +1287,18 @@ emit_i(_Py_CODEUNIT *write_curr, int opcode, int oparg)
return write_curr;
}
-// Note: we're copying over the actual caches to preserve information!
-// This way instructions that we can't type propagate over still stay
-// optimized.
+
+/**
+ * @brief Copy over cache entries, preserving their information.
+ * Note: we're copying over the actual caches to preserve information!
+ * This way instructions that we can't type propagate over still stay
+ * optimized.
+ *
+ * @param write_curr The tier 2 instruction write buffer
+ * @param cache The tier 1 CACHE to copy.
+ * @param n_entries How many CACHE entries to copy.
+ * @return The updated tier 2 instruction write buffer end.
+*/
static inline _Py_CODEUNIT *
copy_cache_entries(_Py_CODEUNIT *write_curr, _Py_CODEUNIT *cache, int n_entries)
{
@@ -1155,7 +1311,12 @@ copy_cache_entries(_Py_CODEUNIT *write_curr, _Py_CODEUNIT *cache, int n_entries)
}
-
+/**
+ * @brief Checks if the current instruction is a backwards jump target.
+ * @param co The code object the instruction belongs to.
+ * @param curr The current instruction to check.
+ * @return Yes/No.
+*/
static int
IS_BACKWARDS_JUMP_TARGET(PyCodeObject *co, _Py_CODEUNIT *curr)
{
@@ -1172,7 +1333,19 @@ IS_BACKWARDS_JUMP_TARGET(PyCodeObject *co, _Py_CODEUNIT *curr)
return 0;
}
-// 1 for error, 0 for success.
+/**
+ * @brief Adds BB metadata to the jump 2D array that a tier 2 code object contains.
+ * This happens when a BB is a backwards jump target.
+ *
+ * @param t2_info Tier 2 info of that code object.
+ * @param meta The BB metadata to add.
+ * @param backwards_jump_target Offset (in number of codeunits) from start of code object where
+ * the backwards jump target is located.
+ *
+ * @param starting_context The type context at the start of the jump target BB.
+ * @param tier1_start The tier 1 starting instruction of the jump target BB.
+ * @return 1 for error, 0 for success.
+*/
static inline int
add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
int backwards_jump_target, _PyTier2TypeContext *starting_context,
@@ -1211,13 +1384,30 @@ add_metadata_to_jump_2d_array(_PyTier2Info *t2_info, _PyTier2BBMetadata *meta,
return 0;
}
-// This converts sequence of instructions like
-// BINARY_OP (ADD)
-// to
-// BINARY_CHECK_INT
-// BB_BRANCH
-// CACHE (bb_id of the current BB << 1 | is_type_branch)
-// // The BINARY_ADD then goes to the next BB
+/**
+ * @brief Infers the correct BINARY_OP to use. This is where we choose to emit
+ * more efficient arithmetic instructions.
+ *
+ * This converts sequence of instructions like
+ * BINARY_OP (ADD)
+ * to
+ * BINARY_CHECK_INT
+ * BB_BRANCH
+ * CACHE (bb_id of the current BB << 1 | is_type_branch)
+ * // The BINARY_ADD then goes to the next BB
+ *
+ * @param t2_start Start of the current basic block.
+ * @param oparg Oparg of the BINARY_OP.
+ * @param needs_guard Signals to the caller whether they should emit a type guard.
+ * @param prev_type_guard The previous basic block's ending type guard (this is
+ * required for the ladder of types).
+ *
+ * @param raw_op The tier 0/1 BINARY_OP.
+ * @param write_curr Tier 2 instruction write buffer.
+ * @param type_context Current type context to base our decisions on.
+ * @param bb_id The current BB's ID.
+ * @return Updated tier 2 instruction write buffer end.
+*/
static inline _Py_CODEUNIT *
infer_BINARY_OP(
_Py_CODEUNIT *t2_start,
@@ -1296,6 +1486,24 @@ infer_BINARY_OP(
return NULL;
}
+/**
+ * @brief Infers the correct BINARY_SUBSCR to use. This is where we choose to emit
+ * more efficient container instructions.
+ *
+ * @param t2_start Start of the current basic block.
+ * @param oparg Oparg of the BINARY_OP.
+ * @param needs_guard Signals to the caller whether they should emit a type guard.
+ * @param prev_type_guard The previous basic block's ending type guard (this is
+ * required for the ladder of types).
+ *
+ * @param raw_op The tier 0/1 BINARY_OP.
+ * @param write_curr Tier 2 instruction write buffer.
+ * @param type_context Current type context to base our decisions on.
+ * @param bb_id The current BB's ID.
+ * @param store Whether it's a store instruction (STORE_SUBSCR) or not (BINARY_SUBSCR).
+ * @return Updated tier 2 instruction write buffer end.
+ * @return
+*/
static inline _Py_CODEUNIT *
infer_BINARY_SUBSCR(
_Py_CODEUNIT *t2_start,
@@ -1344,23 +1552,36 @@ infer_BINARY_SUBSCR(
return NULL;
}
+/**
+ * @brief Whether this is an unboxed type.
+ * @param t The type to check.
+ * @return Yes/No.
+*/
static inline bool
is_unboxed_type(PyTypeObject *t)
{
return t == &PyRawFloat_Type;
}
-// Detects a BB from the current instruction start to the end of the first basic block it sees.
-// Then emits the instructions into the bb space.
-//
-// Instructions emitted depend on the type_context.
-// For example, if it sees a BINARY_ADD instruction, but it knows the two operands are already of
-// type PyLongObject, a BINARY_ADD_INT_REST will be emitted without an type checks.
-//
-// However, if one of the operands are unknown, a logical chain of CHECK instructions will be emitted,
-// and the basic block will end at the first of the chain.
-//
-// Note: a BB end also includes a type guard.
+
+/**
+ * @brief Detects a BB from the current instruction start to the end of the first basic block it sees. Then emits the instructions into the bb space.
+ *
+ * Instructions emitted depend on the type_context.
+ * For example, if it sees a BINARY_ADD instruction, but it knows the two operands are already of
+ * type PyLongObject, a BINARY_ADD_INT_REST will be emitted without an type checks.
+ *
+ * However, if one of the operands are unknown, a logical chain of CHECK instructions will be
+ * emitted, and the basic block will end at the first of the chain.
+ * Note: a BB end also includes a type guard.
+ *
+ * @param co The code object we're optimizing.
+ * @param bb_space The BB space of the code object to write to.
+ * @param prev_type_guard The type guard that ended the previous basic block (if present).
+ * @param tier1_start The tier 1 instructions to start referring from.
+ * @param starting_type_context The starting type context for this new basic block.
+ * @return A new tier 2 basic block.
+*/
_PyTier2BBMetadata *
_PyTier2_Code_DetectAndEmitBB(
PyCodeObject *co,
@@ -1829,6 +2050,12 @@ compare_ints(const void *a, const void *b)
return *(int *)a - *(int *)b;
}
+/**
+ * @brief Allocates the 2D array required to store information about backwards jump targets.
+ * @param backwards_jump_count How many backwards jump targets there are.
+ * @param backward_jump_target_bb_pairs Triplet information required about the backward jump target.
+ * @return 0 on success 1 on error.
+*/
static int
allocate_jump_offset_2d_array(int backwards_jump_count,
_PyTier2BBStartTypeContextTriplet **backward_jump_target_bb_pairs)
@@ -1854,8 +2081,12 @@ allocate_jump_offset_2d_array(int backwards_jump_count,
return 1;
}
-// Returns 1 on error, 0 on success. Populates the backwards jump target offset
-// array for a code object..
+
+/**
+ * @brief Populates the backwards jump target offset array for a code object.
+ * @param co The code object to populate.
+ * @return Returns 1 on error, 0 on success.
+*/
static int
_PyCode_Tier2FillJumpTargets(PyCodeObject *co)
{
@@ -1955,7 +2186,11 @@ _PyCode_Tier2FillJumpTargets(PyCodeObject *co)
}
-
+/**
+ * @brief Initializes the tier 2 info of a code object.
+ * @param co The code object.
+ * @return The newly allocated tier 2 info.
+*/
static _PyTier2Info *
_PyTier2Info_Initialize(PyCodeObject *co)
{
@@ -1989,12 +2224,19 @@ _PyTier2Info_Initialize(PyCodeObject *co)
////////// OVERALL TIER2 FUNCTIONS
-// We use simple heuristics to determine if there are operations
-// we can optimize in this.
-// Specifically, we are looking for the presence of PEP 659
-// specialized forms of bytecode, because this indicates
-// that it's a known form.
-// ADD MORE HERE AS WE GO ALONG.
+/**
+ * @brief Whether the opcode is optimizable.
+ *
+ * We use simple heuristics to determine if there are operations we can optimize.
+ * Specifically, we are looking for the presence of PEP 659 (tier 1)
+ * specialized forms of bytecode, because this indicates that it's a known form.
+ *
+ * ADD MORE HERE AS WE GO ALONG.
+ *
+ * @param opcode The opcode of the instruction.
+ * @param oparg The oparg of the instruction.
+ * @return Yes/No.
+*/
static inline int
IS_OPTIMIZABLE_OPCODE(int opcode, int oparg)
{
@@ -2014,6 +2256,11 @@ IS_OPTIMIZABLE_OPCODE(int opcode, int oparg)
}
}
+/**
+ * @brief Single scan to replace RESUME and JUMP_BACKWARD instructions to faster
+ * variants so they stop warming up the tier 2.
+ * @param co The code object to optimize.
+*/
static inline void
replace_resume_and_jump_backwards(PyCodeObject *co)
{
@@ -2034,9 +2281,17 @@ replace_resume_and_jump_backwards(PyCodeObject *co)
}
}
-// 1. Initialize whatever we need.
-// 2. Create the entry BB.
-// 3. Jump into that BB.
+/**
+ * @brief Initializes the tier 2 of a code object. Called upon first transition from tier 1
+ * to tier 2, when a code object is deemed hot.
+ *
+ * 1. Initialize whatever we need.
+ * 2. Create the entry BB.
+ * 3. Jump into that BB.
+ * @param frame The current executing frame.
+ * @param next_instr The next instruction of said frame.
+ * @return The next instruction (tier 2) to execute.
+*/
static _Py_CODEUNIT *
_PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
@@ -2132,7 +2387,13 @@ _PyCode_Tier2Initialize(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
////////// CEVAL FUNCTIONS
-// Tier 2 warmup counter
+/**
+ * @brief Tier 2 warmup counter.
+ * @param frame Currente executing frame.
+ * @param next_instr The next instruction that frame is executing.
+ * @return The next instruction that should be executed (no change if the code object
+ * is not hot enough).
+*/
_Py_CODEUNIT *
_PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
{
@@ -2152,6 +2413,19 @@ _PyCode_Tier2Warmup(_PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr)
return next_instr;
}
+/**
+ * @brief Generates the next BB with a type context given.
+ *
+ * @param frame The current executing frame.
+ * @param bb_id_tagged The tagged version of the BB_ID (see macros above to understand).
+ * @param curr_executing_instr The current executing instruction in that frame.
+ * @param jumpby How many instructions to jump by before we start scanning what to generate.
+ * @param tier1_fallback Signals the tier 1 instruction to fall back to should generation fail.
+ * @param bb_flag Whether to genreate consequent or alternative BB.
+ * @param type_context_copy A given type context to start with.
+ * @param custom_tier1_end Custom tier 1 instruction to fall back to should we fail.
+ * @return The new BB's metadata.
+*/
_PyTier2BBMetadata *
_PyTier2_GenerateNextBBMetaWithTypeContext(
_PyInterpreterFrame *frame,
@@ -2210,9 +2484,16 @@ _PyTier2_GenerateNextBBMetaWithTypeContext(
return metadata;
}
-// Lazily generates successive BBs when required.
-// The first basic block created will always be directly after the current tier 2 code.
-// The second basic block created will always require a jump.
+/**
+ * @brief Generates the next BB, with an automatically inferred type context.
+ * @param frame The current executing frame.
+ * @param bb_id_tagged The tagged version of the BB_ID (see macros above to understand).
+ * @param curr_executing_instr The current executing instruction in that frame.
+ * @param jumpby How many instructions to jump by before we start scanning what to generate.
+ * @param tier1_fallback Signals the tier 1 instruction to fall back to should generation fail.
+ * @param bb_flag Whether to genreate consequent or alternative BB.
+ * @return The new BB's metadata.
+*/
_PyTier2BBMetadata *
_PyTier2_GenerateNextBBMeta(
_PyInterpreterFrame *frame,
@@ -2250,6 +2531,19 @@ _PyTier2_GenerateNextBBMeta(
return next;
}
+/**
+ * @brief Lazily generates successive BBs when required.
+ * The first basic block created will always be directly after the current tier 2 code.
+ * The second basic block created will always require a jump.
+ *
+ * @param frame The current executing frame.
+ * @param bb_id_tagged The tagged version of the BB_ID (see macros above to understand).
+ * @param curr_executing_instr The current executing instruction in that frame.
+ * @param jumpby How many instructions to jump by before we start scanning what to generate.
+ * @param tier1_fallback Signals the tier 1 instruction to fall back to should generation fail.
+ * @param bb_flag Whether to genreate consequent or alternative BB.
+ * @return The next tier 2 instruction to execute.
+*/
_Py_CODEUNIT *
_PyTier2_GenerateNextBB(
_PyInterpreterFrame *frame,
@@ -2272,9 +2566,13 @@ _PyTier2_GenerateNextBB(
return metadata->tier2_start;
}
-// Calculates the difference between two type contexts.
-// A positive number indicating the distance is returned.
-// Incompatible type contexts return INT_MAX.
+/**
+ * @brief Calculates the difference between two type contexts.
+ * @param ctx1 The base type context.
+ * @param ctx2 The type context to compare with.
+ * @return A positive number indicating the distance is returned.
+ * Incompatible type contexts return INT_MAX.
+*/
static int
diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
{
@@ -2347,6 +2645,19 @@ diff_typecontext(_PyTier2TypeContext *ctx1, _PyTier2TypeContext *ctx2)
return diff;
}
+/**
+ * @brief Locate the BB corresponding to a backwards jump target. Matches also the type context.
+ * If it fails to find a matching type context, a new backwards jump BB is generated with
+ * more specific type context.
+ *
+ * @param frame The current executing frame.
+ * @param bb_id_tagged The tagged version of the BB_ID (see macros above to understand).
+ * @param jumpby How many instructions away is the backwards jump target.
+ * @param tier1_fallback Signals the tier 1 instruction to fall back to should generation fail.
+ * @param curr Current executing instruction
+ * @param stacklevel The stack level of the operand stack.
+ * @return The next tier 2 instruction to execute.
+*/
_Py_CODEUNIT *
_PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged, int jumpby,
_Py_CODEUNIT **tier1_fallback,
@@ -2470,22 +2781,25 @@ _PyTier2_LocateJumpBackwardsBB(_PyInterpreterFrame *frame, uint16_t bb_id_tagged
return target_metadata->tier2_start;
}
-/*
-At generation of the second outgoing edge (basic block), the instructions look like this:
-
-BB_TEST_POP_IF_TRUE
-BB_BRANCH_IF_FLAG_SET
-CACHE
-
-Since both edges are now generated, we want to rewrite it to:
-BB_TEST_POP_IF_TRUE
-BB_JUMP_IF_FLAG_SET
-CACHE (will be converted to EXTENDED_ARGS if we need a bigger jump)
-
-Backwards jumps are handled by another function.
+/**
+ * @brief Rewrites the BB_BRANCH_IF* instructions to a forward jump.
+ * At generation of the second outgoing edge (basic block), the instructions look like this:
+ * BB_TEST_POP_IF_TRUE
+ * BB_BRANCH_IF_FLAG_SET
+ * CACHE
+ *
+ * Since both edges are now generated, we want to rewrite it to:
+ *
+ * BB_TEST_POP_IF_TRUE
+ * BB_JUMP_IF_FLAG_SET
+ * CACHE (will be converted to EXTENDED_ARGS if we need a bigger jump)
+ *
+ * Backwards jumps are handled by another function.
+ *
+ * @param bb_branch Whether the next BB to execute is the consequent/alternative BB.
+ * @param target The jump target.
*/
-
void
_PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target)
{
@@ -2514,21 +2828,24 @@ _PyTier2_RewriteForwardJump(_Py_CODEUNIT *bb_branch, _Py_CODEUNIT *target)
}
-/*
-Before:
-
-EXTENDED_ARG/NOP
-BB_JUMP_BACKWARD_LAZY
-CACHE
-
-
-After:
-
-EXTENDED_ARG (if needed, else NOP)
-JUMP_BACKWARD_QUICK
-END_FOR
+/**
+ * @brief Rewrites a BB_JUMP_BACKWARD_LAZY to a more efficient standard BACKWARD_JUMP.
+ *
+ * Before:
+ *
+ * EXTENDED_ARG/NOP
+ * BB_JUMP_BACKWARD_LAZY
+ * CACHE
+ *
+ * After:
+ *
+ * EXTENDED_ARG (if needed, else NOP)
+ * JUMP_BACKWARD_QUICK
+ * END_FOR
+ *
+ * @param jump_backward_lazy The backwards jump instruction.
+ * @param target The target we're jumping to.
*/
-
void
_PyTier2_RewriteBackwardJump(_Py_CODEUNIT *jump_backward_lazy, _Py_CODEUNIT *target)
{
From ab2145e19d2d739a606976f5b0813d84386ebf34 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sat, 8 Apr 2023 02:31:59 +0800
Subject: [PATCH 263/280] Add instructions
---
CS4215.md | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 CS4215.md
diff --git a/CS4215.md b/CS4215.md
new file mode 100644
index 00000000000000..5cba2b2c0f459e
--- /dev/null
+++ b/CS4215.md
@@ -0,0 +1,20 @@
+# A Lazy Basic Block Versioning Interpreter for CPython.
+
+# Build instructions
+
+You should follow the official CPython build instructions for your platform.
+https://devguide.python.org/getting-started/setup-building/
+
+We have one major difference - you must have a pre-existing Python installation.
+Preferrably Python 3.9 or higher. On MacOS/Unix systems, that Python installation
+*must* be located at `python3`.
+
+The main reason for this limitation is that Python is used to bootstrap the compilation
+of Python. However, since our interpreter is unable to run a large part of the Python
+language, our interpreter cannot be used as a bootstrap Python.
+
+
+# Where are files located?
+
+The majority of the changes and functionality are in `Python/tier2.c`. Doxygen documentation
+is written alongside the code.
\ No newline at end of file
From 59f885d0c7ae1d8d58fef25e0a4f98d969a547d7 Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Sat, 8 Apr 2023 17:20:04 +0800
Subject: [PATCH 264/280] Docs: Added typeprop documentation (#36)
---
Python/tier2.c | 61 +++++++++++++++++++++++++++++++++++---------------
1 file changed, 43 insertions(+), 18 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 4dc65af081e0b2..8ade0405756cd5 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -248,12 +248,21 @@ typenode_get_type(_Py_TYPENODE_t node)
}
}
-// @TODO @JULES
/**
- * @brief
- * @param src
- * @param dst
- * @param src_is_new
+ * @brief Performs TYPE_SET operation. dst tree becomes part of src tree
+ *
+ * If src_is_new is set, src is interpreted as a TYPE_ROOT
+ * not part of the type_context. Otherwise, it is interpreted as a pointer
+ * to a _Py_TYPENODE_t.
+ *
+ * If src_is_new:
+ * Overwrites the root of the dst tree with the src node
+ * else:
+ * Makes the root of the dst tree a TYPE_REF to src
+ *
+ * @param src Source node
+ * @param dst Destination node
+ * @param src_is_new true if src is not part of the type_context
*/
static void
__type_propagate_TYPE_SET(
@@ -298,13 +307,24 @@ __type_propagate_TYPE_SET(
}
}
-// @TODO @JULES
/**
- * @brief
- * @param type_context
- * @param src
- * @param dst
- * @param src_is_new
+ * @brief Performs TYPE_OVERWRITE operation. dst node gets overwritten by src node
+ *
+ * If src_is_new is set, src is interpreted as a TYPE_ROOT
+ * not part of the type_context. Otherwise, it is interpreted as a pointer
+ * to a _Py_TYPENODE_t.
+ *
+ * If src_is_new:
+ * Removes dst node from its tree (+fixes all the references to dst)
+ * Overwrite the dst node with the src node
+ * else:
+ * Removes dst node from its tree (+fixes all the references to dst)
+ * Makes the root of the dst tree a TYPE_REF to src
+ *
+ * @param type_context Type context to modify
+ * @param src Source node
+ * @param dst Destination node
+ * @param src_is_new true if src is not part of the type_context
*/
static void
__type_propagate_TYPE_OVERWRITE(
@@ -421,15 +441,20 @@ __type_propagate_TYPE_OVERWRITE(
}
}
-
-// @TODO @JULES
/**
- * @brief
- * @param type_context
- * @param src
- * @param dst
- *
+ * @brief Performs TYPE_SWAP operation. dst node and src node swap positions
+ *
* src and dst are assumed to already be within the type context
+ *
+ * If src and dst are the same tree
+ * Do nothing
+ * else:
+ * Fix all references of dst to point to src and vice versa
+ *
+ * @param type_context Type context to modify
+ * @param src Source node
+ * @param dst Destination node
+ *
*/
static void
__type_propagate_TYPE_SWAP(
From badce84b8a199b66b4f308686de27bc69756a5e6 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Tue, 11 Apr 2023 16:49:07 +0800
Subject: [PATCH 265/280] Fix type guard emitting
---
Python/tier2.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/Python/tier2.c b/Python/tier2.c
index 4dc65af081e0b2..7b2428cfb94b2b 100644
--- a/Python/tier2.c
+++ b/Python/tier2.c
@@ -1074,9 +1074,14 @@ emit_type_guard(_Py_CODEUNIT *write_curr, int guard_opcode, int guard_oparg, int
_PyOpcode_OpName[guard_opcode]);
#endif
write_curr->op.code = guard_opcode;
- write_curr->op.arg = guard_oparg;
+ write_curr->op.arg = guard_oparg & 0xFF;
write_curr++;
- _py_set_opcode(write_curr, BB_BRANCH);
+
+ write_curr->op.code = NOP;
+ write_curr->op.arg = 0;
+ write_curr++;
+
+ write_curr->op.code = BB_BRANCH;
write_curr->op.arg = 0;
write_curr++;
_PyBBBranchCache *cache = (_PyBBBranchCache *)write_curr;
@@ -2460,8 +2465,12 @@ _PyTier2_GenerateNextBBMetaWithTypeContext(
__type_stack_shrink(&(type_context_copy->type_stack_ptr), n_required_pop);
}
// For type branches, they directly precede the bb branch instruction
+ // It's always
+ // TYPE_BRANCH
+ // NOP
+ // BB_BRANCH
_Py_CODEUNIT *prev_type_guard = BB_IS_TYPE_BRANCH(bb_id_tagged)
- ? curr_executing_instr - 1 : NULL;
+ ? curr_executing_instr - 2 : NULL;
if (BB_TEST_IS_SUCCESSOR(bb_flag) && prev_type_guard != NULL) {
// Propagate the type guard information.
#if TYPEPROP_DEBUG && defined(Py_DEBUG)
From fd338dfc1d32f829684af06dd1c02c15e2140d06 Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Thu, 13 Apr 2023 16:38:13 +0800
Subject: [PATCH 266/280] Tests: Modified dis.py and added primitive tests
(#37)
* Tests: Modified dis.py and added primitive tests
* Docs: Typos and more documentation
* Test: Fixed minor issue
---
CS4215.md | 22 +++-
Lib/dis.py | 10 +-
Python/tier2.c | 4 +-
tier2_test.py | 322 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 350 insertions(+), 8 deletions(-)
create mode 100644 tier2_test.py
diff --git a/CS4215.md b/CS4215.md
index 5cba2b2c0f459e..78a71fdd52125d 100644
--- a/CS4215.md
+++ b/CS4215.md
@@ -16,5 +16,23 @@ language, our interpreter cannot be used as a bootstrap Python.
# Where are files located?
-The majority of the changes and functionality are in `Python/tier2.c`. Doxygen documentation
-is written alongside the code.
\ No newline at end of file
+The majority of the changes and functionality are in `Python/tier2.c` where Doxygen documentation
+is written alongside the code, and in `Tools/cases_generator/` which contains the DSL implementation.
+
+# Running tests
+
+We've written simple tests of the main functionalities.
+Unfortunately we did not have time to write comprehensive tests, and it doesn't seem worth it eitherways given the experimental nature of this project.
+
+After building, run `python tier2_test.py` in the repository's root folder.
+
+# Debugging output
+
+In `tier2.c`, two flags can be set to print debug messages:
+```c
+// Prints codegen debug messages
+#define BB_DEBUG 0
+
+// Prints typeprop debug messages
+#define TYPEPROP_DEBUG 0
+```
\ No newline at end of file
diff --git a/Lib/dis.py b/Lib/dis.py
index 65fd696012718f..501035389a1d81 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -474,6 +474,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
for i in range(start, end):
labels.add(target)
starts_line = None
+ ret = []
for offset, op, arg in _unpack_opargs(code):
if linestarts is not None:
starts_line = linestarts.get(offset, None)
@@ -534,9 +535,9 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
if arg & (1<locals[0], int->stack[0], int->stack[1]]
+ "BINARY_CHECK_INT",
+ "NOP",
+ "BB_BRANCH_IF_FLAG_UNSET", # Fallthrough!
+
+ # Should propagate the result as int
+ # TYPE_OVERWRITE
+ # Locals: [int]
+ # Stack : [int->locals[0], int]
+ "BINARY_OP_ADD_INT_REST",
+
+ # There should be no more guards here
+ # if the type propagator is working
+ "BINARY_OP_ADD_INT_REST",
+ "RETURN_VALUE"
+]
+insts = dis.get_instructions(test_typeprop1, tier2=True)
+for x,y in zip(insts, expected):
+ assert x.opname == y
+
+bytecode = b"".join([
+ # Tests TYPE_SWAP
+ writeinst("RESUME", 0),
+ writeinst("LOAD_FAST", 0), # float
+ writeinst("LOAD_FAST", 1), # int
+ writeinst("SWAP", 2), # Stack: [int, float]
+
+ writeinst("COPY", 1),
+ # Should generate the FLOAT specialisation
+ writeinst("BINARY_OP", 0),
+ writeinst("CACHE", 0), # For tier1
+
+ writeinst("SWAP", 2), # [float, int]
+ writeinst("COPY", 1),
+ # Should generate the INT specialisation
+ writeinst("BINARY_OP", 0),
+ writeinst("CACHE", 0), # For tier1
+
+ # float + int
+ writeinst("BINARY_OP", 0),
+ writeinst("CACHE", 0), # For tier1
+ writeinst("RETURN_VALUE", 0)
+])
+
+def test_typeprop2(a,b):
+ # Dummy code won't be ran
+ return a+(a+(a+a))
+
+# Switch to bytecode
+test_typeprop2.__code__ = test_typeprop2.__code__.replace(co_code=bytecode)
+test_typeprop2(0.1,1)
+
+trigger_tier2(test_typeprop2, (0.1,1))
+expected = [
+ "RESUME_QUICK",
+ "LOAD_FAST",
+ "LOAD_FAST",
+ "SWAP",
+ "COPY",
+
+ # Should gen specialised float
+ "BINARY_CHECK_FLOAT",
+ "NOP",
+ "BB_BRANCH_IF_FLAG_UNSET",
+ "BINARY_OP_ADD_FLOAT_UNBOXED",
+ "SWAP",
+ "COPY",
+
+ # Ladder of types guards
+ "BINARY_CHECK_FLOAT",
+ "NOP",
+ "BB_BRANCH_IF_FLAG_SET",
+
+ # Should gen specialised int
+ "BINARY_CHECK_INT",
+ "NOP",
+ "BB_BRANCH_IF_FLAG_UNSET",
+ "BINARY_OP_ADD_INT_REST",
+ # Don't care about the rest of the insts
+]
+insts = dis.get_instructions(test_typeprop2, tier2=True)
+# Assert the value is correct
+assert abs(test_typeprop2(0.1,1) - 2.2) < 0.001
+for x,y in zip(insts, expected):
+ assert x.opname == y
+
+
+#######################################
+# Type guard #
+# + Float unboxing #
+# + Jump rewriting test #
+#######################################
+
+def test_guard_elimination(a,b):
+ x = b
+ y = b
+ # First a+x should inform the type prop that
+ # `a`, `x`, `b` and `y` are int
+ # So guard should be eliminated in (a+x) + y
+ return a + x + y
+
+trigger_tier2(test_guard_elimination, (0,0))
+expected = [
+ # From tier1 bytecode
+ "RESUME_QUICK",
+ "LOAD_FAST",
+ "STORE_FAST",
+ "LOAD_FAST",
+ "STORE_FAST",
+ "LOAD_FAST",
+ "LOAD_FAST",
+
+ "BINARY_CHECK_FLOAT", # First ladder check
+ "NOP",
+ "BB_BRANCH_IF_FLAG_SET",
+ "BINARY_CHECK_INT", # Second ladder check
+ "NOP",
+ "BB_BRANCH_IF_FLAG_UNSET", # Fall through!
+
+ "BINARY_OP_ADD_INT_REST", # a+x
+ "LOAD_FAST",
+ "BINARY_OP_ADD_INT_REST", # (a+x) + y (guard eliminated)
+ "RETURN_VALUE"
+]
+insts = dis.get_instructions(test_guard_elimination, tier2=True)
+for x,y in zip(insts, expected):
+ assert x.opname == y
+
+# We only wanna test the stability of the first type guards
+# later on
+first_guard_test_until = insts[-1].offset
+
+# Trigger generation of other branch
+test_guard_elimination(0.1, 0.1)
+insts = dis.get_instructions(test_guard_elimination, tier2=True)
+expected = [
+ # From tier1 bytecode
+ "RESUME_QUICK",
+ "LOAD_FAST",
+ "STORE_FAST",
+ "LOAD_FAST",
+ "STORE_FAST",
+ "LOAD_FAST",
+ "LOAD_FAST",
+
+ "BINARY_CHECK_FLOAT", # First ladder check
+ "NOP",
+ "BB_JUMP_IF_FLAG_SET", # Rewrite to jump to float case
+ "POP_TOP", # Pop result
+
+ # The same as above
+ "BINARY_CHECK_INT",
+ "NOP",
+ "BB_BRANCH_IF_FLAG_UNSET",
+ "BINARY_OP_ADD_INT_REST",
+ "LOAD_FAST",
+ "BINARY_OP_ADD_INT_REST",
+ "RETURN_VALUE",
+
+ # Float case
+ "BINARY_OP_ADD_FLOAT_UNBOXED", # Unbox
+ "LOAD_FAST",
+ "UNBOX_FLOAT", # Unbox local
+ "STORE_FAST_UNBOXED_BOXED", # Store unboxed float into local
+ "LOAD_FAST_NO_INCREF", # Load (unboxed) local again
+ "BINARY_OP_ADD_FLOAT_UNBOXED", # No type guard here
+ "BOX_FLOAT", # Box to return
+ "RETURN_VALUE"
+]
+
+test_guard_elimination(1,1)
+for x,y in zip(insts, expected):
+ assert x.opname == y
+
+# Perform other polymorphism stuff
+# We've not implemented type guard elimination
+# For these mixed types (e.g., float+int)
+# So these will generate more type guards with the same
+# mechanisms as above.
+# So codegen wise tier2 takes a while to stabilise
+assert (test_guard_elimination(1,0.1) - 1.2) < 0.001
+assert (test_guard_elimination(0.1,1) - 2.1) < 0.001
+assert (test_guard_elimination(.4,.5) - 1.4) < 0.001
+assert test_guard_elimination(2,3) == 8
+
+# At this point all cases should be generated
+# so check if the generated cases are the same
+expected = dis.get_instructions(test_guard_elimination, tier2=True)
+test_guard_elimination(-192,203)
+test_guard_elimination(2.3, 12)
+test_guard_elimination(324, 0.12)
+test_guard_elimination(0.12,32.1)
+insts = dis.get_instructions(test_guard_elimination, tier2=True)
+
+# Make sure the first type guard is stable
+for x,y in zip(insts, expected):
+ if x.offset >= first_guard_test_until:
+ break
+ assert x.opname == y.opname
+
+
+######################
+# Backward jump test #
+# + loop peeling #
+######################
+
+def test_backwards_jump(a):
+ for i in range(64):
+ a = i + a
+ return a
+
+# Trigger only one JUMP_BACKWARD_QUICK
+# i.e., perfect specialisation the first time
+trigger_tier2(test_backwards_jump, (0,))
+
+# Make sure it looped 64 times
+assert test_backwards_jump(7) == 2023 # <-- Hi! ~ Jules
+
+# Make sure it jumped to the correct spot
+insts = dis.get_instructions(test_backwards_jump, tier2=True)
+backwards_jump = next(x for x in insts if x.opname == "JUMP_BACKWARD_QUICK")
+instidx, jmp_target = next((i,x) for i,x in enumerate(insts) if x.offset == backwards_jump.argval)
+assert jmp_target.opname == "NOP" # Space for an EXTENDED_ARG
+assert insts[instidx + 1].opname == "BB_TEST_ITER_RANGE" # The loop predicate
+
+
+def test_loop_peeling(a):
+ for i in range(64):
+ a = float(i) + a
+ return a
+
+# This triggers loop peeling, because
+# the first iteration `a` type is int
+# and the 2nd iteration `a` type is float
+# This should triger a JUMP_FORWARD in place of
+# a JUMP_BACKWARD_QUICK
+trigger_tier2(test_loop_peeling, (0,))
+
+# Make sure it looped 64 times
+assert abs(test_loop_peeling(7) - 2023) < 0.001
+
+# Make sure the JUMP_FORWARD jumped correctly
+insts = dis.get_instructions(test_loop_peeling, tier2=True)
+forwards_jump = next(x for x in insts if x.opname == "JUMP_FORWARD")
+instidx, jmp_target = next((i,x) for i,x in enumerate(insts) if x.offset == forwards_jump.argval)
+assert jmp_target.opname == "NOP" # Space for an EXTENDED_ARG
+assert insts[instidx + 1].opname == "BB_TEST_ITER_RANGE" # The loop predicate
+
+# We also need to make sure JUMP_FORWARD
+# jumped into the float-specialised loop body
+endidx, _ = next(
+ (i,x) for i,x in enumerate(insts)
+ if (x.opname == "JUMP_BACKWARD_QUICK" and x.offset > jmp_target.offset))
+# Check for existence of float-specialised instruction in loop body
+assert any(1 for _ in
+ filter(lambda i: i.opname == 'BINARY_OP_ADD_FLOAT_UNBOXED', insts[instidx:endidx]))
+
From d1f3a740d88247db5ea0036f5c864f02cb1d75f6 Mon Sep 17 00:00:00 2001
From: Jules <57632293+JuliaPoo@users.noreply.github.com>
Date: Fri, 14 Apr 2023 21:44:09 +0800
Subject: [PATCH 267/280] Test: Added test for container specialisation (#39)
---
tier2_test.py | 69 +++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 61 insertions(+), 8 deletions(-)
diff --git a/tier2_test.py b/tier2_test.py
index 499de3b47195a0..eb372b87b6ac8a 100644
--- a/tier2_test.py
+++ b/tier2_test.py
@@ -25,9 +25,9 @@ def writeinst(opc:str, arg:int=0):
return bytes(inst)
-###################
-# Type prop tests #
-###################
+################################################
+# Type prop tests: TYPE_SET and TYPE_OVERWRITE #
+################################################
def test_typeprop1(a):
# Dummy code won't be ran
@@ -83,6 +83,10 @@ def test_typeprop1(a):
for x,y in zip(insts, expected):
assert x.opname == y
+################################################
+# Type prop tests: TYPE_SWAP #
+################################################
+
bytecode = b"".join([
# Tests TYPE_SWAP
writeinst("RESUME", 0),
@@ -151,9 +155,10 @@ def test_typeprop2(a,b):
#######################################
-# Type guard #
+# Tests for: Type guard #
# + Float unboxing #
# + Jump rewriting test #
+# + Tier2 guard stability #
#######################################
def test_guard_elimination(a,b):
@@ -264,10 +269,9 @@ def test_guard_elimination(a,b):
assert x.opname == y.opname
-######################
-# Backward jump test #
-# + loop peeling #
-######################
+##############################
+# Test: Backward jump offset #
+##############################
def test_backwards_jump(a):
for i in range(64):
@@ -289,6 +293,10 @@ def test_backwards_jump(a):
assert insts[instidx + 1].opname == "BB_TEST_ITER_RANGE" # The loop predicate
+######################
+# Test: Loop peeling #
+######################
+
def test_loop_peeling(a):
for i in range(64):
a = float(i) + a
@@ -320,3 +328,48 @@ def test_loop_peeling(a):
assert any(1 for _ in
filter(lambda i: i.opname == 'BINARY_OP_ADD_FLOAT_UNBOXED', insts[instidx:endidx]))
+
+##################################
+# Test: Container specialisation #
+##################################
+
+def test_container(l):
+ l[2] = l[0] + l[1]
+
+
+trigger_tier2(test_container, ([1,2,3,4],))
+insts = dis.get_instructions(test_container, tier2=True)
+expected = [
+ "RESUME_QUICK",
+ "LOAD_FAST",
+ "LOAD_CONST",
+
+ "CHECK_LIST",
+ "NOP",
+ "BB_BRANCH_IF_FLAG_UNSET", # Fallthrough!
+
+ # Type prop from const array: No type guard needed
+ "BINARY_SUBSCR_LIST_INT_REST",
+ "LOAD_FAST",
+ "LOAD_CONST",
+ # CHECK_LIST should eliminate the type guard here
+ "BINARY_SUBSCR_LIST_INT_REST",
+
+ # We haven't implemented type prop into container types
+ # so these checks should get generated
+ "BINARY_CHECK_FLOAT",
+ "NOP",
+ "BB_BRANCH_IF_FLAG_SET",
+ "BINARY_CHECK_INT",
+ "NOP",
+ "BB_BRANCH_IF_FLAG_UNSET",
+ "BINARY_OP_ADD_INT_REST",
+
+ "LOAD_FAST",
+ "LOAD_CONST",
+ # CHECK_LIST should eliminate the type guard here
+ "STORE_SUBSCR_LIST_INT_REST",
+ "RETURN_CONST",
+]
+for x,y in zip(insts, expected):
+ assert x.opname == y
From 3366ecac548412f77530c1e480274d7df1985db5 Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sat, 15 Apr 2023 23:33:40 +0800
Subject: [PATCH 268/280] modify test
---
bm_nbody.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bm_nbody.py b/bm_nbody.py
index 51c2d383a47c4c..f9642bb9480318 100644
--- a/bm_nbody.py
+++ b/bm_nbody.py
@@ -143,7 +143,7 @@ def add_cmdline_args(cmd, args):
# Warmup
bench_nbody(128, DEFAULT_REFERENCE, 128)
# Showtime
- print("It's showtime")
+ print("Starting benchmark...")
taken = bench_nbody(DEFAULT_ITERATIONS, DEFAULT_REFERENCE, 50_000)
print("Time taken is", taken, "s")
\ No newline at end of file
From 223949acd1b7266936c17a5e2b352101a97065ed Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sat, 15 Apr 2023 23:40:25 +0800
Subject: [PATCH 269/280] update build info
---
CS4215.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CS4215.md b/CS4215.md
index 78a71fdd52125d..a1e03357240abf 100644
--- a/CS4215.md
+++ b/CS4215.md
@@ -13,6 +13,8 @@ The main reason for this limitation is that Python is used to bootstrap the comp
of Python. However, since our interpreter is unable to run a large part of the Python
language, our interpreter cannot be used as a bootstrap Python.
+During the build process, errors may be printed, and the build process may error. However,
+the final Python executable should still be generated.
# Where are files located?
From ce3724f547939958354af521563fa8f53722bba2 Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sun, 16 Apr 2023 00:05:21 +0800
Subject: [PATCH 270/280] Create bm_float_unboxed.py
---
bm_float_unboxed.py | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 bm_float_unboxed.py
diff --git a/bm_float_unboxed.py b/bm_float_unboxed.py
new file mode 100644
index 00000000000000..22f586eca2983a
--- /dev/null
+++ b/bm_float_unboxed.py
@@ -0,0 +1,17 @@
+import time
+
+def f(a, b):
+ for _ in range(10_000_000):
+ return a + b + a * a + b - a - b
+
+# Warmup
+f(1.0, 2.0)
+f(1.0, 2.0)
+
+# Running the actual benchmark
+
+print("Starting benchmark...")
+start = time.perf_counter()
+for _ in range(100_000_000):
+ f(1.0, 2.0)
+print("Time taken is", time.perf_counter() - start, "s")
From aef35162896c7c4d1efcf251445a5b4ee88bc998 Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sun, 16 Apr 2023 00:19:26 +0800
Subject: [PATCH 271/280] Commit pylbbv results
---
...e3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt | 2 ++
.../ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt | 2 ++
2 files changed, 4 insertions(+)
create mode 100644 tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt
create mode 100644 tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt
diff --git a/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt b/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt
new file mode 100644
index 00000000000000..7be97a697c0e16
--- /dev/null
+++ b/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt
@@ -0,0 +1,2 @@
+Starting benchmark...
+Time taken is 29.23269283899981 s
diff --git a/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt b/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt
new file mode 100644
index 00000000000000..7600d9f45e9409
--- /dev/null
+++ b/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt
@@ -0,0 +1,2 @@
+Starting benchmark...
+Time taken is 31.180609329999697 s
From 6ee3fdb95310319bb055f8c0de898629aff73786 Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sun, 16 Apr 2023 00:43:17 +0800
Subject: [PATCH 272/280] Update bm_float_unboxed.py
---
bm_float_unboxed.py | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/bm_float_unboxed.py b/bm_float_unboxed.py
index 22f586eca2983a..604cfb543b5a5a 100644
--- a/bm_float_unboxed.py
+++ b/bm_float_unboxed.py
@@ -1,17 +1,18 @@
import time
-def f(a, b):
- for _ in range(10_000_000):
- return a + b + a * a + b - a - b
+def f():
+ a = 1.0
+ b = 2.0
+ return a + b + a * a + b - a - b
# Warmup
-f(1.0, 2.0)
-f(1.0, 2.0)
+f()
+f()
# Running the actual benchmark
print("Starting benchmark...")
start = time.perf_counter()
for _ in range(100_000_000):
- f(1.0, 2.0)
-print("Time taken is", time.perf_counter() - start, "s")
+ f()
+print("Time taken is", time.perf_counter() - start, "s")
\ No newline at end of file
From 19e6fe237454730984a14c852477339fff923d19 Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sun, 16 Apr 2023 00:52:14 +0800
Subject: [PATCH 273/280] Remove old benchmarks
---
...e3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt | 2 --
.../ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt | 2 --
2 files changed, 4 deletions(-)
delete mode 100644 tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt
delete mode 100644 tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt
diff --git a/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt b/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt
deleted file mode 100644
index 7be97a697c0e16..00000000000000
--- a/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_float_unboxed.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Starting benchmark...
-Time taken is 29.23269283899981 s
diff --git a/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt b/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt
deleted file mode 100644
index 7600d9f45e9409..00000000000000
--- a/tier2_results/pylbbv/ce3724f547939958354af521563fa8f53722bba2_bm_nbody.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Starting benchmark...
-Time taken is 31.180609329999697 s
From d5f2da4be505c0146b64dc3d490e5784a14029c7 Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sun, 16 Apr 2023 00:58:03 +0800
Subject: [PATCH 274/280] Add benchmark results for cpython and pylbbv
---
...703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt | 2 ++
.../7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt | 2 ++
...9e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt | 2 ++
.../19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt | 2 ++
4 files changed, 8 insertions(+)
create mode 100644 tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt
create mode 100644 tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt
create mode 100644 tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt
create mode 100644 tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt
diff --git a/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt b/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt
new file mode 100644
index 00000000000000..13c24db7b66454
--- /dev/null
+++ b/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt
@@ -0,0 +1,2 @@
+Starting benchmark...
+Time taken is 14.229669059000116 s
diff --git a/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt b/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt
new file mode 100644
index 00000000000000..24d83b8ce7b5e5
--- /dev/null
+++ b/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt
@@ -0,0 +1,2 @@
+Starting benchmark...
+Time taken is 30.047266386000047 s
diff --git a/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt b/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt
new file mode 100644
index 00000000000000..ffb5ec08375952
--- /dev/null
+++ b/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt
@@ -0,0 +1,2 @@
+Starting benchmark...
+Time taken is 13.191304593000496 s
diff --git a/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt b/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt
new file mode 100644
index 00000000000000..fcca05abcdc198
--- /dev/null
+++ b/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt
@@ -0,0 +1,2 @@
+Starting benchmark...
+Time taken is 30.5506420720003 s
From 3d44cb4dcbda80c750e292a9598cfddb41a92aff Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sun, 16 Apr 2023 15:04:02 +0800
Subject: [PATCH 275/280] Test literally only float ops in bm_float_unboxed
---
bm_float_unboxed.py | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/bm_float_unboxed.py b/bm_float_unboxed.py
index 604cfb543b5a5a..0c79fcd6311c8e 100644
--- a/bm_float_unboxed.py
+++ b/bm_float_unboxed.py
@@ -1,18 +1,17 @@
import time
-def f():
- a = 1.0
- b = 2.0
- return a + b + a * a + b - a - b
+def f(a, b, loops=200_000_000):
+ z = a + b
+ for _ in range(loops):
+ z + z + z + z + z + z + z + z + z + z + z + z + z + z + z + z
# Warmup
-f()
-f()
+f(1.0 , 2.0, 64)
+f(1.0 , 2.0, 64)
# Running the actual benchmark
print("Starting benchmark...")
start = time.perf_counter()
-for _ in range(100_000_000):
- f()
+f(1.0 , 2.0)
print("Time taken is", time.perf_counter() - start, "s")
\ No newline at end of file
From 0e81dc427532d0b14020d0565187824e8ef34b95 Mon Sep 17 00:00:00 2001
From: kenjin-work
Date: Sun, 16 Apr 2023 15:23:40 +0800
Subject: [PATCH 276/280] New bm_nbody_float results
---
...703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt | 2 +-
.../7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt | 2 +-
...9e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt | 2 --
.../19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt | 2 --
...d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_float_unboxed.txt | 2 ++
.../3d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_nbody.txt | 2 ++
6 files changed, 6 insertions(+), 6 deletions(-)
delete mode 100644 tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt
delete mode 100644 tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt
create mode 100644 tier2_results/pylbbv/3d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_float_unboxed.txt
create mode 100644 tier2_results/pylbbv/3d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_nbody.txt
diff --git a/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt b/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt
index 13c24db7b66454..6d7260a34045bd 100644
--- a/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt
+++ b/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_float_unboxed.txt
@@ -1,2 +1,2 @@
Starting benchmark...
-Time taken is 14.229669059000116 s
+Time taken is 21.911415401999875 s
diff --git a/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt b/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt
index 24d83b8ce7b5e5..20a86e19604e7b 100644
--- a/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt
+++ b/tier2_results/cpython/7703def37e4fa7d25c3d23756de8f527daa4e165_bm_nbody.txt
@@ -1,2 +1,2 @@
Starting benchmark...
-Time taken is 30.047266386000047 s
+Time taken is 30.735117989000173 s
diff --git a/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt b/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt
deleted file mode 100644
index ffb5ec08375952..00000000000000
--- a/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_float_unboxed.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Starting benchmark...
-Time taken is 13.191304593000496 s
diff --git a/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt b/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt
deleted file mode 100644
index fcca05abcdc198..00000000000000
--- a/tier2_results/pylbbv/19e6fe237454730984a14c852477339fff923d19_bm_nbody.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Starting benchmark...
-Time taken is 30.5506420720003 s
diff --git a/tier2_results/pylbbv/3d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_float_unboxed.txt b/tier2_results/pylbbv/3d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_float_unboxed.txt
new file mode 100644
index 00000000000000..09c519cefe8e5e
--- /dev/null
+++ b/tier2_results/pylbbv/3d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_float_unboxed.txt
@@ -0,0 +1,2 @@
+Starting benchmark...
+Time taken is 13.180637167999976 s
diff --git a/tier2_results/pylbbv/3d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_nbody.txt b/tier2_results/pylbbv/3d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_nbody.txt
new file mode 100644
index 00000000000000..6192c2d72646ee
--- /dev/null
+++ b/tier2_results/pylbbv/3d44cb4dcbda80c750e292a9598cfddb41a92aff_bm_nbody.txt
@@ -0,0 +1,2 @@
+Starting benchmark...
+Time taken is 30.82709504000013 s
From 3a4c75db2d0fa6a7fcf322116361c0141dbf6a1a Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sun, 16 Apr 2023 16:55:06 +0800
Subject: [PATCH 277/280] Add tests for BB_TEST_ITER specialisation
---
tier2_test.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/tier2_test.py b/tier2_test.py
index eb372b87b6ac8a..50de7692fcbf70 100644
--- a/tier2_test.py
+++ b/tier2_test.py
@@ -1,5 +1,7 @@
import dis
+print("Begin tests...")
+
#########
# Utils #
#########
@@ -373,3 +375,49 @@ def test_container(l):
]
for x,y in zip(insts, expected):
assert x.opname == y
+
+####################################################
+# Tests for: Tier 2 BB_TEST_ITER specialisation #
+####################################################
+
+lst = [1, 2, 3]
+def test_iter_list(a):
+ for i in lst:
+ a = i + a
+ return a
+
+# Trigger only one JUMP_BACKWARD_QUICK
+# i.e., perfect specialisation the first time
+trigger_tier2(test_iter_list, (0,))
+
+# Make sure it looped 64 times
+assert test_iter_list(0) == 6
+
+# Make sure it jumped to the correct spot
+insts = dis.get_instructions(test_iter_list, tier2=True)
+backwards_jump = next(x for x in insts if x.opname == "JUMP_BACKWARD_QUICK")
+instidx, jmp_target = next((i,x) for i,x in enumerate(insts) if x.offset == backwards_jump.argval)
+assert jmp_target.opname == "NOP" # Space for an EXTENDED_ARG
+assert insts[instidx + 1].opname == "BB_TEST_ITER_LIST" # The loop predicate
+
+
+def test_iter_tuple(a):
+ for i in (1, 2, 3):
+ a = i + a
+ return a
+
+# Trigger only one JUMP_BACKWARD_QUICK
+# i.e., perfect specialisation the first time
+trigger_tier2(test_iter_tuple, (0,))
+
+# Make sure it looped 64 times
+assert test_iter_tuple(0) == 6
+
+# Make sure it jumped to the correct spot
+insts = dis.get_instructions(test_iter_tuple, tier2=True)
+backwards_jump = next(x for x in insts if x.opname == "JUMP_BACKWARD_QUICK")
+instidx, jmp_target = next((i,x) for i,x in enumerate(insts) if x.offset == backwards_jump.argval)
+assert jmp_target.opname == "NOP" # Space for an EXTENDED_ARG
+assert insts[instidx + 1].opname == "BB_TEST_ITER_TUPLE" # The loop predicate
+
+print("Tests completed")
\ No newline at end of file
From ea992b35bef5c688c6c8851a22d132f0f6384739 Mon Sep 17 00:00:00 2001
From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Date: Sun, 16 Apr 2023 19:56:42 +0800
Subject: [PATCH 278/280] Create CPython_Tier_2_LBBV_Report_For_Repo.pdf
---
.../CPython_Tier_2_LBBV_Report_For_Repo.pdf | Bin 0 -> 2634616 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 report/CPython_Tier_2_LBBV_Report_For_Repo.pdf
diff --git a/report/CPython_Tier_2_LBBV_Report_For_Repo.pdf b/report/CPython_Tier_2_LBBV_Report_For_Repo.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..2a0ee715f55a68105ba13957cec83001c22c1ced
GIT binary patch
literal 2634616
zcmeFYWmKcVwyul2)3^m|+}+*X8h59G;BHOhZjHM`V6?hcK+bLqAAI%n;3_qcce
zI=^U{LYld7=RGf~9o(+z?^C9;gj+uiEzz6^uS;6u00vKd1ZA}dw
zEImw308F1H05dxaD?1ZAfI$+V4Pa(v=K`>?aRGDz3<>~d4lV$L7=Vd|k(reZz#tD`
zW(F{*{_Vlc&cz1c=Z7=3HTgR(;Qz|wb0O^iwh&c!J5vCIhO(*I=Q2!f&7CX&Ow6Ct
z6t}c?GIjX8S{pi7&DbTBosGM%%&0%4B6Q^*%^&YIas*ZP1u;4wZC)Y?^29Z4>ofElb*7lQ(l{08{!%oKjaQtxxc1A=lA!QX8;RwBfn74Nc
zz&S*2!z&g716i;{RsQP-|1ppMs~+)x2>U<9|54z76!;$n{zrlTQQ-eaflrqByJi0i
zisTJ#Og~|Oh3TK$xPqOjt&p*kCD0bY%K5K4XD91VbaDi+aB=<9O3~2VRPG;iq2_3+
z@ULcuztw-OIR9z&PfQUpbTYICn*SR!F?0Mi_}2vfOK4F6urhx#ngW20mGOTu4l4)K
zf8O>D3_kb#EuE3Bk*=<_KEMbFqyak(I9|*!b3E~nZqVW=aI%T6EZynq7@=G~D>YCW
z;Lo<-uSSp)oIxP)1LM$-8Q8e^b=E62=8X8p29Ac8Lk3z-e)_>VUO#NPB!Od%?HA!U
z-|wGk6X#RRI0?UbKF%lor9dA4O-icer8cac5m^>tj?#%d*n(J3F{
z^JVHSU54+W`I3^B)F8`v;1YIZHO_ShW38{_e5n@kVqj<69HKqd-xV9+d^#+`7FtLb
zdBaIq8w460I;z-BgASs}U0RE})9uHNan;WYUgSm$X5|%bXm?6yIpI}78tki6ZvJFY
z&0-WeV}LEkzJ>eY7D-uJ`TTU)1r889zO-?Lp(CaIzF6^JHRsS|6%py)?N${ZFZ4+C)j^0u7=d2Hd&ZH&Gbr&>ivMv{2+p~J^Q9ML
z*Lis3{|!`sS^R&yi;%4?(8=)=OIbOY|KVn=od4ueR!-LcDF0=C<y_h)}|53*v_X)m+!oa_H`?rRTi;ey-Vso-G(R2Re7T}YYKYIXJSpGQ(gR-e3
z(AmNG)4f?a|C4+F_4$AI?SH((|EF*Ni`cn7zlZ-$u^HI9(Yp;-Z$5#F*&zsJ8cLg1
z`VOvg{yNtvb>c5}NzhscDpz!?nzGyAJDIz)4Ol3s~o;h{0o^AYWcH$tNb3^