From f1a654648b7d86cc52225455d72fe69e8920318f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 8 Nov 2022 08:22:56 -0800 Subject: [PATCH] GH-98831: Simple input-output stack effects for bytecodes.c (#99120) --- Python/bytecodes.c | 280 +++++++------------- Python/ceval.c | 9 + Python/generated_cases.c.h | 332 +++++++++++++----------- Tools/cases_generator/generate_cases.py | 99 ++++--- Tools/cases_generator/parser.py | 70 +++-- 5 files changed, 384 insertions(+), 406 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 636d2d88cb2..8469f7f01f2 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -43,6 +43,7 @@ void _PyUnicode_ExactDealloc(PyObject *); #define SET_TOP(v) (stack_pointer[-1] = (v)) #define SET_SECOND(v) (stack_pointer[-2] = (v)) #define PEEK(n) (stack_pointer[-(n)]) +#define POKE(n, v) (stack_pointer[-(n)] = (v)) #define PUSH(val) (*(stack_pointer++) = (val)) #define POP() (*(--stack_pointer)) #define TOP() PEEK(1) @@ -63,12 +64,13 @@ do { \ /* Flow control macros */ #define DEOPT_IF(cond, instname) ((void)0) +#define ERROR_IF(cond, labelname) ((void)0) #define JUMPBY(offset) ((void)0) #define GO_TO_INSTRUCTION(instname) ((void)0) #define DISPATCH_SAME_OPARG() ((void)0) #define DISPATCH() ((void)0) -#define inst(name) case name: +#define inst(name, ...) case name: #define super(name) static int SUPER_##name #define family(name) static int family_##name @@ -79,6 +81,10 @@ typedef struct { PyObject *kwnames; } CallShape; +// Dummy variables for stack effects. +static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub; +static PyObject *container, *start, *stop, *v; + static PyObject * dummy_func( PyThreadState *tstate, @@ -104,12 +110,10 @@ dummy_func( and that all operation that succeed call DISPATCH() ! */ // BEGIN BYTECODES // - // stack effect: ( -- ) - inst(NOP) { + inst(NOP, (--)) { } - // stack effect: ( -- ) - inst(RESUME) { + inst(RESUME, (--)) { assert(tstate->cframe == &cframe); assert(frame == cframe.current_frame); if (_Py_atomic_load_relaxed_int32(eval_breaker) && oparg < 2) { @@ -117,45 +121,31 @@ dummy_func( } } - // stack effect: ( -- __0) - inst(LOAD_CLOSURE) { + inst(LOAD_CLOSURE, (-- value)) { /* We keep LOAD_CLOSURE so that the bytecode stays more readable. */ - PyObject *value = GETLOCAL(oparg); - if (value == NULL) { - goto unbound_local_error; - } + value = GETLOCAL(oparg); + ERROR_IF(value == NULL, unbound_local_error); Py_INCREF(value); - PUSH(value); } - // stack effect: ( -- __0) - inst(LOAD_FAST_CHECK) { - PyObject *value = GETLOCAL(oparg); - if (value == NULL) { - goto unbound_local_error; - } + inst(LOAD_FAST_CHECK, (-- value)) { + value = GETLOCAL(oparg); + ERROR_IF(value == NULL, unbound_local_error); Py_INCREF(value); - PUSH(value); } - // stack effect: ( -- __0) - inst(LOAD_FAST) { - PyObject *value = GETLOCAL(oparg); + inst(LOAD_FAST, (-- value)) { + value = GETLOCAL(oparg); assert(value != NULL); Py_INCREF(value); - PUSH(value); } - // stack effect: ( -- __0) - inst(LOAD_CONST) { - PyObject *value = GETITEM(consts, oparg); + inst(LOAD_CONST, (-- value)) { + value = GETITEM(consts, oparg); Py_INCREF(value); - PUSH(value); } - // stack effect: (__0 -- ) - inst(STORE_FAST) { - PyObject *value = POP(); + inst(STORE_FAST, (value --)) { SETLOCAL(oparg, value); } @@ -165,9 +155,7 @@ dummy_func( super(STORE_FAST__STORE_FAST) = STORE_FAST + STORE_FAST; super (LOAD_CONST__LOAD_FAST) = LOAD_CONST + LOAD_FAST; - // stack effect: (__0 -- ) - inst(POP_TOP) { - PyObject *value = POP(); + inst(POP_TOP, (value --)) { Py_DECREF(value); } @@ -177,166 +165,113 @@ dummy_func( BASIC_PUSH(NULL); } - // stack effect: (__0, __1 -- ) - inst(END_FOR) { - PyObject *value = POP(); - Py_DECREF(value); - value = POP(); - Py_DECREF(value); + inst(END_FOR, (value1, value2 --)) { + Py_DECREF(value1); + Py_DECREF(value2); } - // stack effect: ( -- ) - inst(UNARY_POSITIVE) { - PyObject *value = TOP(); - PyObject *res = PyNumber_Positive(value); + inst(UNARY_POSITIVE, (value -- res)) { + res = PyNumber_Positive(value); Py_DECREF(value); - SET_TOP(res); - if (res == NULL) - goto error; + ERROR_IF(res == NULL, error); } - // stack effect: ( -- ) - inst(UNARY_NEGATIVE) { - PyObject *value = TOP(); - PyObject *res = PyNumber_Negative(value); + inst(UNARY_NEGATIVE, (value -- res)) { + res = PyNumber_Negative(value); Py_DECREF(value); - SET_TOP(res); - if (res == NULL) - goto error; + ERROR_IF(res == NULL, error); } - // stack effect: ( -- ) - inst(UNARY_NOT) { - PyObject *value = TOP(); + inst(UNARY_NOT, (value -- res)) { int err = PyObject_IsTrue(value); Py_DECREF(value); + ERROR_IF(err < 0, error); if (err == 0) { - Py_INCREF(Py_True); - SET_TOP(Py_True); - DISPATCH(); + res = Py_True; } - else if (err > 0) { - Py_INCREF(Py_False); - SET_TOP(Py_False); - DISPATCH(); + else { + res = Py_False; } - STACK_SHRINK(1); - goto error; + Py_INCREF(res); } - // stack effect: ( -- ) - inst(UNARY_INVERT) { - PyObject *value = TOP(); - PyObject *res = PyNumber_Invert(value); + inst(UNARY_INVERT, (value -- res)) { + res = PyNumber_Invert(value); Py_DECREF(value); - SET_TOP(res); - if (res == NULL) - goto error; + ERROR_IF(res == NULL, error); } - // stack effect: (__0 -- ) - inst(BINARY_OP_MULTIPLY_INT) { + inst(BINARY_OP_MULTIPLY_INT, (left, right -- prod)) { assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); STAT_INC(BINARY_OP, hit); - PyObject *prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); - SET_SECOND(prod); + prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - STACK_SHRINK(1); - if (prod == NULL) { - goto error; - } + ERROR_IF(prod == NULL, error); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); } - // stack effect: (__0 -- ) - inst(BINARY_OP_MULTIPLY_FLOAT) { + inst(BINARY_OP_MULTIPLY_FLOAT, (left, right -- prod)) { assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); 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; - PyObject *prod = PyFloat_FromDouble(dprod); - SET_SECOND(prod); + prod = PyFloat_FromDouble(dprod); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); - STACK_SHRINK(1); - if (prod == NULL) { - goto error; - } + ERROR_IF(prod == NULL, error); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); } - // stack effect: (__0 -- ) - inst(BINARY_OP_SUBTRACT_INT) { + inst(BINARY_OP_SUBTRACT_INT, (left, right -- sub)) { assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); STAT_INC(BINARY_OP, hit); - PyObject *sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); - SET_SECOND(sub); + sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - STACK_SHRINK(1); - if (sub == NULL) { - goto error; - } + ERROR_IF(sub == NULL, error); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); } - // stack effect: (__0 -- ) - inst(BINARY_OP_SUBTRACT_FLOAT) { + inst(BINARY_OP_SUBTRACT_FLOAT, (left, right -- sub)) { assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); 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; - PyObject *sub = PyFloat_FromDouble(dsub); - SET_SECOND(sub); + sub = PyFloat_FromDouble(dsub); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); - STACK_SHRINK(1); - if (sub == NULL) { - goto error; - } + ERROR_IF(sub == NULL, error); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); } - // stack effect: (__0 -- ) - inst(BINARY_OP_ADD_UNICODE) { + inst(BINARY_OP_ADD_UNICODE, (left, right -- res)) { assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); STAT_INC(BINARY_OP, hit); - PyObject *res = PyUnicode_Concat(left, right); - STACK_SHRINK(1); - SET_TOP(res); + res = PyUnicode_Concat(left, right); _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (TOP() == NULL) { - goto error; - } + ERROR_IF(res == NULL, error); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); } - // stack effect: (__0 -- ) - inst(BINARY_OP_INPLACE_ADD_UNICODE) { + // This is a subtle one. It's a super-instruction for + // BINARY_OP_ADD_UNICODE followed by STORE_FAST + // where the store goes into the left argument. + // So the inputs are the same as for all BINARY_OP + // specializations, but there is no output. + // At the end we just skip over the STORE_FAST. + inst(BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) { assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); 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]; @@ -358,107 +293,75 @@ dummy_func( */ assert(Py_REFCNT(left) >= 2); _Py_DECREF_NO_DEALLOC(left); - STACK_SHRINK(2); PyUnicode_Append(target_local, right); _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (*target_local == NULL) { - goto error; - } + ERROR_IF(*target_local == NULL, error); // The STORE_FAST is already done. JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP + 1); } - // stack effect: (__0 -- ) - inst(BINARY_OP_ADD_FLOAT) { + inst(BINARY_OP_ADD_FLOAT, (left, right -- sum)) { assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); 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; - PyObject *sum = PyFloat_FromDouble(dsum); - SET_SECOND(sum); + sum = PyFloat_FromDouble(dsum); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); - STACK_SHRINK(1); - if (sum == NULL) { - goto error; - } + ERROR_IF(sum == NULL, error); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); } - // stack effect: (__0 -- ) - inst(BINARY_OP_ADD_INT) { + inst(BINARY_OP_ADD_INT, (left, right -- sum)) { assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); STAT_INC(BINARY_OP, hit); - PyObject *sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); - SET_SECOND(sum); + sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - STACK_SHRINK(1); - if (sum == NULL) { - goto error; - } + ERROR_IF(sum == NULL, error); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); } - // stack effect: (__0 -- ) - inst(BINARY_SUBSCR) { - PyObject *sub = POP(); - PyObject *container = TOP(); - PyObject *res = PyObject_GetItem(container, sub); + inst(BINARY_SUBSCR, (container, sub -- res)) { + res = PyObject_GetItem(container, sub); Py_DECREF(container); Py_DECREF(sub); - SET_TOP(res); - if (res == NULL) - goto error; + ERROR_IF(res == NULL, error); JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR); } - // stack effect: (__0, __1 -- ) - inst(BINARY_SLICE) { - PyObject *stop = POP(); - PyObject *start = POP(); - PyObject *container = TOP(); - + inst(BINARY_SLICE, (container, start, stop -- 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) { - goto error; + res = NULL; } - PyObject *res = PyObject_GetItem(container, slice); - Py_DECREF(slice); - if (res == NULL) { - goto error; + else { + res = PyObject_GetItem(container, slice); + Py_DECREF(slice); } - SET_TOP(res); Py_DECREF(container); + ERROR_IF(res == NULL, error); } - // stack effect: (__0, __1, __2, __3 -- ) - inst(STORE_SLICE) { - PyObject *stop = POP(); - PyObject *start = POP(); - PyObject *container = TOP(); - PyObject *v = SECOND(); - + inst(STORE_SLICE, (v, container, start, stop -- )) { PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); + int err; if (slice == NULL) { - goto error; + err = 1; } - int err = PyObject_SetItem(container, slice, v); - Py_DECREF(slice); - if (err) { - goto error; + else { + err = PyObject_SetItem(container, slice, v); + Py_DECREF(slice); } - STACK_SHRINK(2); Py_DECREF(v); Py_DECREF(container); + ERROR_IF(err, error); } // stack effect: (__0 -- ) @@ -607,21 +510,14 @@ dummy_func( PREDICT(JUMP_BACKWARD); } - // stack effect: (__0, __1, __2 -- ) - inst(STORE_SUBSCR) { - PyObject *sub = TOP(); - PyObject *container = SECOND(); - PyObject *v = THIRD(); + inst(STORE_SUBSCR, (v, container, sub -- )) { int err; - STACK_SHRINK(3); /* container[sub] = v */ err = PyObject_SetItem(container, sub, v); Py_DECREF(v); Py_DECREF(container); Py_DECREF(sub); - if (err != 0) { - goto error; - } + ERROR_IF(err != 0, error); JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR); } diff --git a/Python/ceval.c b/Python/ceval.c index 46fad972b06..4828adb576f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -806,6 +806,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define THIRD() (stack_pointer[-3]) #define FOURTH() (stack_pointer[-4]) #define PEEK(n) (stack_pointer[-(n)]) +#define POKE(n, v) (stack_pointer[-(n)] = (v)) #define SET_TOP(v) (stack_pointer[-1] = (v)) #define SET_SECOND(v) (stack_pointer[-2] = (v)) #define BASIC_STACKADJ(n) (stack_pointer += n) @@ -1274,6 +1275,14 @@ unbound_local_error: 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: call_shape.kwnames = NULL; /* Double-check exception status. */ diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 6ea1f48e629..4e9b665db1a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -15,51 +15,57 @@ } TARGET(LOAD_CLOSURE) { + PyObject *value; /* We keep LOAD_CLOSURE so that the bytecode stays more readable. */ - PyObject *value = GETLOCAL(oparg); - if (value == NULL) { - goto unbound_local_error; - } + value = GETLOCAL(oparg); + if (value == NULL) goto unbound_local_error; Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); DISPATCH(); } TARGET(LOAD_FAST_CHECK) { - PyObject *value = GETLOCAL(oparg); - if (value == NULL) { - goto unbound_local_error; - } + PyObject *value; + value = GETLOCAL(oparg); + if (value == NULL) goto unbound_local_error; Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); DISPATCH(); } TARGET(LOAD_FAST) { - PyObject *value = GETLOCAL(oparg); + PyObject *value; + value = GETLOCAL(oparg); assert(value != NULL); Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); DISPATCH(); } TARGET(LOAD_CONST) { PREDICTED(LOAD_CONST); - PyObject *value = GETITEM(consts, oparg); + PyObject *value; + value = GETITEM(consts, oparg); Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); DISPATCH(); } TARGET(STORE_FAST) { - PyObject *value = POP(); + PyObject *value = PEEK(1); SETLOCAL(oparg, value); + STACK_SHRINK(1); DISPATCH(); } TARGET(POP_TOP) { - PyObject *value = POP(); + PyObject *value = PEEK(1); Py_DECREF(value); + STACK_SHRINK(1); DISPATCH(); } @@ -70,163 +76,158 @@ } TARGET(END_FOR) { - PyObject *value = POP(); - Py_DECREF(value); - value = POP(); - Py_DECREF(value); + PyObject *value2 = PEEK(1); + PyObject *value1 = PEEK(2); + Py_DECREF(value1); + Py_DECREF(value2); + STACK_SHRINK(2); DISPATCH(); } TARGET(UNARY_POSITIVE) { - PyObject *value = TOP(); - PyObject *res = PyNumber_Positive(value); + PyObject *value = PEEK(1); + PyObject *res; + res = PyNumber_Positive(value); Py_DECREF(value); - SET_TOP(res); - if (res == NULL) - goto error; + if (res == NULL) goto pop_1_error; + POKE(1, res); DISPATCH(); } TARGET(UNARY_NEGATIVE) { - PyObject *value = TOP(); - PyObject *res = PyNumber_Negative(value); + PyObject *value = PEEK(1); + PyObject *res; + res = PyNumber_Negative(value); Py_DECREF(value); - SET_TOP(res); - if (res == NULL) - goto error; + if (res == NULL) goto pop_1_error; + POKE(1, res); DISPATCH(); } TARGET(UNARY_NOT) { - PyObject *value = TOP(); + PyObject *value = PEEK(1); + PyObject *res; int err = PyObject_IsTrue(value); Py_DECREF(value); + if (err < 0) goto pop_1_error; if (err == 0) { - Py_INCREF(Py_True); - SET_TOP(Py_True); - DISPATCH(); + res = Py_True; } - else if (err > 0) { - Py_INCREF(Py_False); - SET_TOP(Py_False); - DISPATCH(); + else { + res = Py_False; } - STACK_SHRINK(1); - goto error; + Py_INCREF(res); + POKE(1, res); + DISPATCH(); } TARGET(UNARY_INVERT) { - PyObject *value = TOP(); - PyObject *res = PyNumber_Invert(value); + PyObject *value = PEEK(1); + PyObject *res; + res = PyNumber_Invert(value); Py_DECREF(value); - SET_TOP(res); - if (res == NULL) - goto error; + 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); - PyObject *left = SECOND(); - PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); STAT_INC(BINARY_OP, hit); - PyObject *prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); - SET_SECOND(prod); + prod = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - STACK_SHRINK(1); - if (prod == NULL) { - goto error; - } + if (prod == NULL) goto pop_2_error; JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); + STACK_SHRINK(1); + POKE(1, prod); DISPATCH(); } TARGET(BINARY_OP_MULTIPLY_FLOAT) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *prod; assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); 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; - PyObject *prod = PyFloat_FromDouble(dprod); - SET_SECOND(prod); + prod = PyFloat_FromDouble(dprod); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); - STACK_SHRINK(1); - if (prod == NULL) { - goto error; - } + if (prod == NULL) goto pop_2_error; JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); + STACK_SHRINK(1); + POKE(1, prod); DISPATCH(); } TARGET(BINARY_OP_SUBTRACT_INT) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *sub; assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP); STAT_INC(BINARY_OP, hit); - PyObject *sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); - SET_SECOND(sub); + sub = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - STACK_SHRINK(1); - if (sub == NULL) { - goto error; - } + if (sub == NULL) goto pop_2_error; JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); + STACK_SHRINK(1); + POKE(1, sub); DISPATCH(); } TARGET(BINARY_OP_SUBTRACT_FLOAT) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *sub; assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); 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; - PyObject *sub = PyFloat_FromDouble(dsub); - SET_SECOND(sub); + sub = PyFloat_FromDouble(dsub); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); - STACK_SHRINK(1); - if (sub == NULL) { - goto error; - } + if (sub == NULL) goto pop_2_error; JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); + STACK_SHRINK(1); + POKE(1, sub); DISPATCH(); } TARGET(BINARY_OP_ADD_UNICODE) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *res; assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); STAT_INC(BINARY_OP, hit); - PyObject *res = PyUnicode_Concat(left, right); - STACK_SHRINK(1); - SET_TOP(res); + res = PyUnicode_Concat(left, right); _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (TOP() == NULL) { - goto error; - } + if (res == NULL) goto pop_2_error; JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); + STACK_SHRINK(1); + POKE(1, res); DISPATCH(); } TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); 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]; @@ -248,108 +249,108 @@ */ assert(Py_REFCNT(left) >= 2); _Py_DECREF_NO_DEALLOC(left); - STACK_SHRINK(2); PyUnicode_Append(target_local, right); _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (*target_local == NULL) { - goto error; - } + 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); - PyObject *left = SECOND(); - PyObject *right = TOP(); 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; - PyObject *sum = PyFloat_FromDouble(dsum); - SET_SECOND(sum); + sum = PyFloat_FromDouble(dsum); _Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc); _Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc); - STACK_SHRINK(1); - if (sum == NULL) { - goto error; - } + if (sum == NULL) goto pop_2_error; JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); + STACK_SHRINK(1); + POKE(1, sum); DISPATCH(); } TARGET(BINARY_OP_ADD_INT) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *sum; assert(cframe.use_tracing == 0); - PyObject *left = SECOND(); - PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP); STAT_INC(BINARY_OP, hit); - PyObject *sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); - SET_SECOND(sum); + sum = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - STACK_SHRINK(1); - if (sum == NULL) { - goto error; - } + if (sum == NULL) goto pop_2_error; JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP); + STACK_SHRINK(1); + POKE(1, sum); DISPATCH(); } TARGET(BINARY_SUBSCR) { PREDICTED(BINARY_SUBSCR); - PyObject *sub = POP(); - PyObject *container = TOP(); - PyObject *res = PyObject_GetItem(container, sub); + PyObject *sub = PEEK(1); + PyObject *container = PEEK(2); + PyObject *res; + res = PyObject_GetItem(container, sub); Py_DECREF(container); Py_DECREF(sub); - SET_TOP(res); - if (res == NULL) - goto error; + if (res == NULL) goto pop_2_error; JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR); + STACK_SHRINK(1); + POKE(1, res); DISPATCH(); } TARGET(BINARY_SLICE) { - PyObject *stop = POP(); - PyObject *start = POP(); - PyObject *container = TOP(); - + 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) { - goto error; + res = NULL; } - PyObject *res = PyObject_GetItem(container, slice); - Py_DECREF(slice); - if (res == NULL) { - goto error; + else { + res = PyObject_GetItem(container, slice); + Py_DECREF(slice); } - SET_TOP(res); Py_DECREF(container); + if (res == NULL) goto pop_3_error; + STACK_SHRINK(2); + POKE(1, res); DISPATCH(); } TARGET(STORE_SLICE) { - PyObject *stop = POP(); - PyObject *start = POP(); - PyObject *container = TOP(); - PyObject *v = SECOND(); - + 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) { - goto error; + err = 1; } - int err = PyObject_SetItem(container, slice, v); - Py_DECREF(slice); - if (err) { - goto error; + else { + err = PyObject_SetItem(container, slice, v); + Py_DECREF(slice); } - STACK_SHRINK(2); Py_DECREF(v); Py_DECREF(container); + if (err) goto pop_4_error; + STACK_SHRINK(4); DISPATCH(); } @@ -500,20 +501,18 @@ TARGET(STORE_SUBSCR) { PREDICTED(STORE_SUBSCR); - PyObject *sub = TOP(); - PyObject *container = SECOND(); - PyObject *v = THIRD(); + PyObject *sub = PEEK(1); + PyObject *container = PEEK(2); + PyObject *v = PEEK(3); int err; - STACK_SHRINK(3); /* container[sub] = v */ err = PyObject_SetItem(container, sub, v); Py_DECREF(v); Py_DECREF(container); Py_DECREF(sub); - if (err != 0) { - goto error; - } + if (err != 0) goto pop_3_error; JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR); + STACK_SHRINK(3); DISPATCH(); } @@ -3876,82 +3875,99 @@ TARGET(LOAD_FAST__LOAD_FAST) { { - PyObject *value = GETLOCAL(oparg); + PyObject *value; + value = GETLOCAL(oparg); assert(value != NULL); Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); } NEXTOPARG(); next_instr++; { - PyObject *value = GETLOCAL(oparg); + PyObject *value; + value = GETLOCAL(oparg); assert(value != NULL); Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); } DISPATCH(); } TARGET(LOAD_FAST__LOAD_CONST) { { - PyObject *value = GETLOCAL(oparg); + PyObject *value; + value = GETLOCAL(oparg); assert(value != NULL); Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); } NEXTOPARG(); next_instr++; { - PyObject *value = GETITEM(consts, oparg); + PyObject *value; + value = GETITEM(consts, oparg); Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); } DISPATCH(); } TARGET(STORE_FAST__LOAD_FAST) { { - PyObject *value = POP(); + PyObject *value = PEEK(1); SETLOCAL(oparg, value); + STACK_SHRINK(1); } NEXTOPARG(); next_instr++; { - PyObject *value = GETLOCAL(oparg); + PyObject *value; + value = GETLOCAL(oparg); assert(value != NULL); Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); } DISPATCH(); } TARGET(STORE_FAST__STORE_FAST) { { - PyObject *value = POP(); + PyObject *value = PEEK(1); SETLOCAL(oparg, value); + STACK_SHRINK(1); } NEXTOPARG(); next_instr++; { - PyObject *value = POP(); + PyObject *value = PEEK(1); SETLOCAL(oparg, value); + STACK_SHRINK(1); } DISPATCH(); } TARGET(LOAD_CONST__LOAD_FAST) { { - PyObject *value = GETITEM(consts, oparg); + PyObject *value; + value = GETITEM(consts, oparg); Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); } NEXTOPARG(); next_instr++; { - PyObject *value = GETLOCAL(oparg); + PyObject *value; + value = GETLOCAL(oparg); assert(value != NULL); Py_INCREF(value); - PUSH(value); + STACK_GROW(1); + POKE(1, value); } DISPATCH(); } diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index c5a436d08aa..e5610343880 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -5,10 +5,10 @@ # TODO: Reuse C generation framework from deepfreeze.py? import argparse -import io import os import re import sys +from typing import TextIO, cast import parser from parser import InstDef # TODO: Use parser.InstDef @@ -20,13 +20,13 @@ arg_parser.add_argument("-c", "--compare", action="store_true") arg_parser.add_argument("-q", "--quiet", action="store_true") -def eopen(filename: str, mode: str = "r"): +def eopen(filename: str, mode: str = "r") -> TextIO: if filename == "-": if "r" in mode: return sys.stdin else: return sys.stdout - return open(filename, mode) + return cast(TextIO, open(filename, mode)) def parse_cases( @@ -67,42 +67,72 @@ def always_exits(block: parser.Block) -> bool: return line.startswith(("goto ", "return ", "DISPATCH", "GO_TO_", "Py_UNREACHABLE()")) -def write_cases(f: io.TextIOBase, instrs: list[InstDef], supers: list[parser.Super]): - predictions = set() - for inst in instrs: - for target in re.findall(r"(?:PREDICT|GO_TO_INSTRUCTION)\((\w+)\)", inst.block.text): +def write_instr(instr: InstDef, predictions: set[str], indent: str, f: TextIO, dedent: int = 0): + assert instr.block + if dedent < 0: + indent += " " * -dedent + # TODO: Is it better to count forward or backward? + for i, input in enumerate(reversed(instr.inputs), 1): + f.write(f"{indent} PyObject *{input} = PEEK({i});\n") + for output in instr.outputs: + if output not in instr.inputs: + f.write(f"{indent} PyObject *{output};\n") + assert instr.block is not None + blocklines = instr.block.to_text(dedent=dedent).splitlines(True) + # Remove blank lines from ends + while blocklines and not blocklines[0].strip(): + blocklines.pop(0) + while blocklines and not blocklines[-1].strip(): + blocklines.pop() + # Remove leading '{' and trailing '}' + assert blocklines and blocklines[0].strip() == "{" + assert blocklines and blocklines[-1].strip() == "}" + blocklines.pop() + blocklines.pop(0) + # Remove trailing blank lines + while blocklines and not blocklines[-1].strip(): + blocklines.pop() + # Write the body + ninputs = len(instr.inputs or ()) + for line in blocklines: + if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*$", line): + space, cond, label = m.groups() + # ERROR_IF() must remove the inputs from the stack. + # The code block is responsible for DECREF()ing them. + if ninputs: + f.write(f"{space}if ({cond}) goto pop_{ninputs}_{label};\n") + else: + f.write(f"{space}if ({cond}) goto {label};\n") + else: + f.write(line) + noutputs = len(instr.outputs or ()) + diff = noutputs - ninputs + if diff > 0: + f.write(f"{indent} STACK_GROW({diff});\n") + elif diff < 0: + f.write(f"{indent} STACK_SHRINK({-diff});\n") + for i, output in enumerate(reversed(instr.outputs or ()), 1): + if output not in (instr.inputs or ()): + f.write(f"{indent} POKE({i}, {output});\n") + assert instr.block + +def write_cases(f: TextIO, instrs: list[InstDef], supers: list[parser.Super]): + predictions: set[str] = set() + for instr in instrs: + assert isinstance(instr, InstDef) + assert instr.block is not None + for target in re.findall(r"(?:PREDICT|GO_TO_INSTRUCTION)\((\w+)\)", instr.block.text): predictions.add(target) indent = " " f.write(f"// This file is generated by {os.path.relpath(__file__)}\n") - f.write("// Do not edit!\n") + f.write(f"// Do not edit!\n") instr_index: dict[str, InstDef] = {} for instr in instrs: - assert isinstance(instr, InstDef) instr_index[instr.name] = instr f.write(f"\n{indent}TARGET({instr.name}) {{\n") if instr.name in predictions: f.write(f"{indent} PREDICTED({instr.name});\n") - # input = ", ".join(instr.inputs) - # output = ", ".join(instr.outputs) - # f.write(f"{indent} // {input} -- {output}\n") - assert instr.block - blocklines = instr.block.text.splitlines(True) - # Remove blank lines from ends - while blocklines and not blocklines[0].strip(): - blocklines.pop(0) - while blocklines and not blocklines[-1].strip(): - blocklines.pop() - # Remove leading '{' and trailing '}' - assert blocklines and blocklines[0].strip() == "{" - assert blocklines and blocklines[-1].strip() == "}" - blocklines.pop() - blocklines.pop(0) - # Remove trailing blank lines - while blocklines and not blocklines[-1].strip(): - blocklines.pop() - # Write the body - for line in blocklines: - f.write(line) + write_instr(instr, predictions, indent, f) assert instr.block if not always_exits(instr.block): f.write(f"{indent} DISPATCH();\n") @@ -114,14 +144,13 @@ def write_cases(f: io.TextIOBase, instrs: list[InstDef], supers: list[parser.Sup components = [instr_index[name] for name in sup.ops] f.write(f"\n{indent}TARGET({sup.name}) {{\n") for i, instr in enumerate(components): + assert instr.block if i > 0: f.write(f"{indent} NEXTOPARG();\n") f.write(f"{indent} next_instr++;\n") - text = instr.block.to_text(-4) - textlines = text.splitlines(True) - textlines = [line for line in textlines if not line.strip().startswith("PREDICTED(")] - text = "".join(textlines) - f.write(f"{indent} {text.strip()}\n") + f.write(f"{indent} {{\n") + write_instr(instr, predictions, indent, f, dedent=-4) + f.write(f" {indent}}}\n") f.write(f"{indent} DISPATCH();\n") f.write(f"{indent}}}\n") diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index f603bc6a986..9e95cdb42d4 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -57,11 +57,28 @@ class Block(Node): @dataclass -class InstDef(Node): +class InstHeader(Node): name: str - inputs: list[str] | None - outputs: list[str] | None - block: Block | None + inputs: list[str] + outputs: list[str] + + +@dataclass +class InstDef(Node): + header: InstHeader + block: Block + + @property + def name(self): + return self.header.name + + @property + def inputs(self): + return self.header.inputs + + @property + def outputs(self): + return self.header.outputs @dataclass @@ -82,30 +99,42 @@ class Parser(PLexer): def inst_def(self) -> InstDef | None: if header := self.inst_header(): if block := self.block(): - header.block = block - return header + return InstDef(header, block) raise self.make_syntax_error("Expected block") return None @contextual - def inst_header(self): + def inst_header(self) -> InstHeader | None: # inst(NAME) | inst(NAME, (inputs -- outputs)) # TODO: Error out when there is something unexpected. - # TODO: Make INST a keyword in the lexer. + # TODO: Make INST a keyword in the lexer.`` if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text == "inst": if (self.expect(lx.LPAREN) and (tkn := self.expect(lx.IDENTIFIER))): name = tkn.text if self.expect(lx.COMMA): inp, outp = self.stack_effect() - if (self.expect(lx.RPAREN) - and self.peek().kind == lx.LBRACE): - return InstDef(name, inp, outp, []) + if self.expect(lx.RPAREN): + if ((tkn := self.peek()) + and tkn.kind == lx.LBRACE): + self.check_overlaps(inp, outp) + return InstHeader(name, inp, outp) elif self.expect(lx.RPAREN): - return InstDef(name, None, None, []) + return InstHeader(name, [], []) return None - def stack_effect(self): + def check_overlaps(self, inp: list[str], outp: list[str]): + for i, name in enumerate(inp): + try: + j = outp.index(name) + except ValueError: + continue + else: + if i != j: + raise self.make_syntax_error( + f"Input {name!r} at pos {i} repeated in output at different pos {j}") + + def stack_effect(self) -> tuple[list[str], list[str]]: # '(' [inputs] '--' [outputs] ')' if self.expect(lx.LPAREN): inp = self.inputs() or [] @@ -115,7 +144,7 @@ class Parser(PLexer): return inp, outp raise self.make_syntax_error("Expected stack effect") - def inputs(self): + def inputs(self) -> list[str] | None: # input (, input)* here = self.getpos() if inp := self.input(): @@ -128,7 +157,7 @@ class Parser(PLexer): self.setpos(here) return None - def input(self): + def input(self) -> str | None: # IDENTIFIER if (tkn := self.expect(lx.IDENTIFIER)): if self.expect(lx.LBRACKET): @@ -148,7 +177,7 @@ class Parser(PLexer): return "??" return None - def outputs(self): + def outputs(self) -> list[str] | None: # output (, output)* here = self.getpos() if outp := self.output(): @@ -161,7 +190,7 @@ class Parser(PLexer): self.setpos(here) return None - def output(self): + def output(self) -> str | None: return self.input() # TODO: They're not quite the same. @contextual @@ -176,7 +205,6 @@ class Parser(PLexer): return res def ops(self) -> list[str] | None: - here = self.getpos() if tkn := self.expect(lx.IDENTIFIER): ops = [tkn.text] while self.expect(lx.PLUS): @@ -197,7 +225,7 @@ class Parser(PLexer): return Family(tkn.text, members) return None - def members(self): + def members(self) -> list[str] | None: here = self.getpos() if tkn := self.expect(lx.IDENTIFIER): near = self.getpos() @@ -214,8 +242,8 @@ class Parser(PLexer): tokens = self.c_blob() return Block(tokens) - def c_blob(self): - tokens = [] + def c_blob(self) -> list[lx.Token]: + tokens: list[lx.Token] = [] level = 0 while tkn := self.next(raw=True): if tkn.kind in (lx.LBRACE, lx.LPAREN, lx.LBRACKET):