bpo-39156: Break up COMPARE_OP into four logically distinct opcodes. (GH-17754)

Break up COMPARE_OP into four logically distinct opcodes:
* COMPARE_OP for rich comparisons
* IS_OP for 'is' and 'is not' tests
* CONTAINS_OP for 'in' and 'is not' tests
* JUMP_IF_NOT_EXC_MATCH for checking exceptions in 'try-except' statements.
This commit is contained in:
Mark Shannon 2020-01-14 10:12:45 +00:00 committed by GitHub
parent 62e3973395
commit 9af0e47b17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 4909 additions and 4855 deletions

View file

@ -67,7 +67,6 @@ static void maybe_dtrace_line(PyFrameObject *, int *, int *, int *);
static void dtrace_function_entry(PyFrameObject *);
static void dtrace_function_return(PyFrameObject *);
static PyObject * cmp_outcome(PyThreadState *, int, PyObject *, PyObject *);
static PyObject * import_name(PyThreadState *, PyFrameObject *,
PyObject *, PyObject *, PyObject *);
static PyObject * import_from(PyThreadState *, PyObject *, PyObject *);
@ -2897,12 +2896,13 @@ main_loop:
}
case TARGET(COMPARE_OP): {
assert(oparg <= Py_GE);
PyObject *right = POP();
PyObject *left = TOP();
PyObject *res = cmp_outcome(tstate, oparg, left, right);
PyObject *res = PyObject_RichCompare(left, right, oparg);
SET_TOP(res);
Py_DECREF(left);
Py_DECREF(right);
SET_TOP(res);
if (res == NULL)
goto error;
PREDICT(POP_JUMP_IF_FALSE);
@ -2910,6 +2910,81 @@ main_loop:
DISPATCH();
}
case TARGET(IS_OP): {
PyObject *right = POP();
PyObject *left = TOP();
int res = (left == right)^oparg;
PyObject *b = res ? Py_True : Py_False;
Py_INCREF(b);
SET_TOP(b);
Py_DECREF(left);
Py_DECREF(right);
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
FAST_DISPATCH();
}
case TARGET(CONTAINS_OP): {
PyObject *right = POP();
PyObject *left = POP();
int res = PySequence_Contains(right, left);
Py_DECREF(left);
Py_DECREF(right);
if (res < 0) {
goto error;
}
PyObject *b = (res^oparg) ? Py_True : Py_False;
Py_INCREF(b);
PUSH(b);
PREDICT(POP_JUMP_IF_FALSE);
PREDICT(POP_JUMP_IF_TRUE);
FAST_DISPATCH();
}
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
"BaseException is not allowed"
case TARGET(JUMP_IF_NOT_EXC_MATCH): {
PyObject *right = POP();
PyObject *left = POP();
if (PyTuple_Check(right)) {
Py_ssize_t i, length;
length = PyTuple_GET_SIZE(right);
for (i = 0; i < length; i++) {
PyObject *exc = PyTuple_GET_ITEM(right, i);
if (!PyExceptionClass_Check(exc)) {
_PyErr_SetString(tstate, PyExc_TypeError,
CANNOT_CATCH_MSG);
Py_DECREF(left);
Py_DECREF(right);
goto error;
}
}
}
else {
if (!PyExceptionClass_Check(right)) {
_PyErr_SetString(tstate, PyExc_TypeError,
CANNOT_CATCH_MSG);
Py_DECREF(left);
Py_DECREF(right);
goto error;
}
}
int res = PyErr_GivenExceptionMatches(left, right);
Py_DECREF(left);
Py_DECREF(right);
if (res > 0) {
/* Exception matches -- Do nothing */;
}
else if (res == 0) {
JUMPTO(oparg);
}
else {
goto error;
}
DISPATCH();
}
case TARGET(IMPORT_NAME): {
PyObject *name = GETITEM(names, oparg);
PyObject *fromlist = POP();
@ -4951,62 +5026,6 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi)
return 1;
}
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
"BaseException is not allowed"
static PyObject *
cmp_outcome(PyThreadState *tstate, int op, PyObject *v, PyObject *w)
{
int res = 0;
switch (op) {
case PyCmp_IS:
res = (v == w);
break;
case PyCmp_IS_NOT:
res = (v != w);
break;
case PyCmp_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
break;
case PyCmp_NOT_IN:
res = PySequence_Contains(w, v);
if (res < 0)
return NULL;
res = !res;
break;
case PyCmp_EXC_MATCH:
if (PyTuple_Check(w)) {
Py_ssize_t i, length;
length = PyTuple_Size(w);
for (i = 0; i < length; i += 1) {
PyObject *exc = PyTuple_GET_ITEM(w, i);
if (!PyExceptionClass_Check(exc)) {
_PyErr_SetString(tstate, PyExc_TypeError,
CANNOT_CATCH_MSG);
return NULL;
}
}
}
else {
if (!PyExceptionClass_Check(w)) {
_PyErr_SetString(tstate, PyExc_TypeError,
CANNOT_CATCH_MSG);
return NULL;
}
}
res = PyErr_GivenExceptionMatches(v, w);
break;
default:
return PyObject_RichCompare(v, w, op);
}
v = res ? Py_True : Py_False;
Py_INCREF(v);
return v;
}
static PyObject *
import_name(PyThreadState *tstate, PyFrameObject *f,
PyObject *name, PyObject *fromlist, PyObject *level)

View file

@ -1021,7 +1021,11 @@ stack_effect(int opcode, int oparg, int jump)
case LOAD_ATTR:
return 0;
case COMPARE_OP:
case IS_OP:
case CONTAINS_OP:
return -1;
case JUMP_IF_NOT_EXC_MATCH:
return -2;
case IMPORT_NAME:
return -1;
case IMPORT_FROM:
@ -1502,6 +1506,12 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute)
return 0; \
}
#define ADDOP_COMPARE(C, CMP) { \
if (!compiler_addcompare((C), (cmpop_ty)(CMP))) \
return 0; \
}
/* VISIT and VISIT_SEQ takes an ASDL type as their second argument. They use
the ASDL name to synthesize the name of the C type and the visit function.
*/
@ -2433,35 +2443,49 @@ check_compare(struct compiler *c, expr_ty e)
return 1;
}
static int
cmpop(cmpop_ty op)
static int compiler_addcompare(struct compiler *c, cmpop_ty op)
{
int cmp;
switch (op) {
case Eq:
return PyCmp_EQ;
cmp = Py_EQ;
break;
case NotEq:
return PyCmp_NE;
cmp = Py_NE;
break;
case Lt:
return PyCmp_LT;
cmp = Py_LT;
break;
case LtE:
return PyCmp_LE;
cmp = Py_LE;
break;
case Gt:
return PyCmp_GT;
cmp = Py_GT;
break;
case GtE:
return PyCmp_GE;
cmp = Py_GE;
break;
case Is:
return PyCmp_IS;
ADDOP_I(c, IS_OP, 0);
return 1;
case IsNot:
return PyCmp_IS_NOT;
ADDOP_I(c, IS_OP, 1);
return 1;
case In:
return PyCmp_IN;
ADDOP_I(c, CONTAINS_OP, 0);
return 1;
case NotIn:
return PyCmp_NOT_IN;
ADDOP_I(c, CONTAINS_OP, 1);
return 1;
default:
return PyCmp_BAD;
Py_UNREACHABLE();
}
ADDOP_I(c, COMPARE_OP, cmp);
return 1;
}
static int
compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond)
{
@ -2526,14 +2550,12 @@ compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond)
(expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
ADDOP(c, DUP_TOP);
ADDOP(c, ROT_THREE);
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, i))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, i));
ADDOP_JABS(c, POP_JUMP_IF_FALSE, cleanup);
NEXT_BLOCK(c);
}
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, n))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, n));
ADDOP_JABS(c, cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next);
basicblock *end = compiler_new_block(c);
if (end == NULL)
@ -2976,8 +2998,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
[tb, val, exc] L1: DUP )
[tb, val, exc, exc] <evaluate E1> )
[tb, val, exc, exc, E1] COMPARE_OP EXC_MATCH ) only if E1
[tb, val, exc, 1-or-0] POP_JUMP_IF_FALSE L2 )
[tb, val, exc, exc, E1] JUMP_IF_NOT_EXC_MATCH L2 ) only if E1
[tb, val, exc] POP
[tb, val] <assign to V1> (or POP if no V1)
[tb] POP
@ -3029,8 +3050,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
if (handler->v.ExceptHandler.type) {
ADDOP(c, DUP_TOP);
VISIT(c, expr, handler->v.ExceptHandler.type);
ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH);
ADDOP_JABS(c, POP_JUMP_IF_FALSE, except);
ADDOP_JABS(c, JUMP_IF_NOT_EXC_MATCH, except);
}
ADDOP(c, POP_TOP);
if (handler->v.ExceptHandler.name) {
@ -3873,8 +3893,7 @@ compiler_compare(struct compiler *c, expr_ty e)
n = asdl_seq_LEN(e->v.Compare.ops) - 1;
if (n == 0) {
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, 0));
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, 0))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, 0));
}
else {
basicblock *cleanup = compiler_new_block(c);
@ -3885,14 +3904,12 @@ compiler_compare(struct compiler *c, expr_ty e)
(expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
ADDOP(c, DUP_TOP);
ADDOP(c, ROT_THREE);
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, i))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, i));
ADDOP_JABS(c, JUMP_IF_FALSE_OR_POP, cleanup);
NEXT_BLOCK(c);
}
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
ADDOP_I(c, COMPARE_OP,
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, n))));
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, n));
basicblock *end = compiler_new_block(c);
if (end == NULL)
return 0;

2930
Python/importlib.h generated

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -116,11 +116,11 @@ static void *opcode_targets[256] = {
&&TARGET_POP_JUMP_IF_FALSE,
&&TARGET_POP_JUMP_IF_TRUE,
&&TARGET_LOAD_GLOBAL,
&&TARGET_IS_OP,
&&TARGET_CONTAINS_OP,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
&&TARGET_SETUP_FINALLY,
&&_unknown_opcode,
&&TARGET_LOAD_FAST,

View file

@ -12,10 +12,10 @@
#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
#define CONDITIONAL_JUMP(op) (op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH)
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE \
|| op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH)
#define JUMPS_ON_TRUE(op) (op==POP_JUMP_IF_TRUE || op==JUMP_IF_TRUE_OR_POP)
#define GETJUMPTGT(arr, i) (get_arg(arr, i) / sizeof(_Py_CODEUNIT) + \
(ABSOLUTE_JUMP(_Py_OPCODE(arr[i])) ? 0 : i+1))
@ -194,6 +194,7 @@ markblocks(_Py_CODEUNIT *code, Py_ssize_t len)
case JUMP_IF_TRUE_OR_POP:
case POP_JUMP_IF_FALSE:
case POP_JUMP_IF_TRUE:
case JUMP_IF_NOT_EXC_MATCH:
case JUMP_ABSOLUTE:
case SETUP_FINALLY:
case SETUP_WITH:
@ -493,6 +494,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
case POP_JUMP_IF_TRUE:
case JUMP_IF_FALSE_OR_POP:
case JUMP_IF_TRUE_OR_POP:
case JUMP_IF_NOT_EXC_MATCH:
j = blocks[j / sizeof(_Py_CODEUNIT)] * sizeof(_Py_CODEUNIT);
break;