Optimize `LOAD_FAST` opcodes into faster versions that load borrowed references onto the operand stack when we can prove that the lifetime of the local outlives the lifetime of the temporary that is loaded onto the stack.
931 lines
31 KiB
C
931 lines
31 KiB
C
#include "Python.h"
|
|
#include "pycore_optimizer.h"
|
|
#include "pycore_uops.h"
|
|
#include "pycore_uop_ids.h"
|
|
#include "internal/pycore_moduleobject.h"
|
|
|
|
#define op(name, ...) /* NAME is ignored */
|
|
|
|
typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
|
|
|
|
/* Shortened forms for convenience */
|
|
#define sym_is_not_null _Py_uop_sym_is_not_null
|
|
#define sym_is_const _Py_uop_sym_is_const
|
|
#define sym_get_const _Py_uop_sym_get_const
|
|
#define sym_new_unknown _Py_uop_sym_new_unknown
|
|
#define sym_new_not_null _Py_uop_sym_new_not_null
|
|
#define sym_new_type _Py_uop_sym_new_type
|
|
#define sym_is_null _Py_uop_sym_is_null
|
|
#define sym_new_const _Py_uop_sym_new_const
|
|
#define sym_new_null _Py_uop_sym_new_null
|
|
#define sym_matches_type _Py_uop_sym_matches_type
|
|
#define sym_matches_type_version _Py_uop_sym_matches_type_version
|
|
#define sym_get_type _Py_uop_sym_get_type
|
|
#define sym_has_type _Py_uop_sym_has_type
|
|
#define sym_set_null(SYM) _Py_uop_sym_set_null(ctx, SYM)
|
|
#define sym_set_non_null(SYM) _Py_uop_sym_set_non_null(ctx, SYM)
|
|
#define sym_set_type(SYM, TYPE) _Py_uop_sym_set_type(ctx, SYM, TYPE)
|
|
#define sym_set_type_version(SYM, VERSION) _Py_uop_sym_set_type_version(ctx, SYM, VERSION)
|
|
#define sym_set_const(SYM, CNST) _Py_uop_sym_set_const(ctx, SYM, CNST)
|
|
#define sym_is_bottom _Py_uop_sym_is_bottom
|
|
#define frame_new _Py_uop_frame_new
|
|
#define frame_pop _Py_uop_frame_pop
|
|
#define sym_new_tuple _Py_uop_sym_new_tuple
|
|
#define sym_tuple_getitem _Py_uop_sym_tuple_getitem
|
|
#define sym_tuple_length _Py_uop_sym_tuple_length
|
|
#define sym_is_immortal _Py_uop_sym_is_immortal
|
|
#define sym_new_truthiness _Py_uop_sym_new_truthiness
|
|
|
|
extern int
|
|
optimize_to_bool(
|
|
_PyUOpInstruction *this_instr,
|
|
JitOptContext *ctx,
|
|
JitOptSymbol *value,
|
|
JitOptSymbol **result_ptr);
|
|
|
|
extern void
|
|
eliminate_pop_guard(_PyUOpInstruction *this_instr, bool exit);
|
|
|
|
extern PyCodeObject *get_code(_PyUOpInstruction *op);
|
|
|
|
static int
|
|
dummy_func(void) {
|
|
|
|
PyCodeObject *co;
|
|
int oparg;
|
|
JitOptSymbol *flag;
|
|
JitOptSymbol *left;
|
|
JitOptSymbol *right;
|
|
JitOptSymbol *value;
|
|
JitOptSymbol *res;
|
|
JitOptSymbol *iter;
|
|
JitOptSymbol *top;
|
|
JitOptSymbol *bottom;
|
|
_Py_UOpsAbstractFrame *frame;
|
|
_Py_UOpsAbstractFrame *new_frame;
|
|
JitOptContext *ctx;
|
|
_PyUOpInstruction *this_instr;
|
|
_PyBloomFilter *dependencies;
|
|
int modified;
|
|
int curr_space;
|
|
int max_space;
|
|
_PyUOpInstruction *first_valid_check_stack;
|
|
_PyUOpInstruction *corresponding_check_stack;
|
|
|
|
// BEGIN BYTECODES //
|
|
|
|
op(_LOAD_FAST_CHECK, (-- value)) {
|
|
value = GETLOCAL(oparg);
|
|
// We guarantee this will error - just bail and don't optimize it.
|
|
if (sym_is_null(value)) {
|
|
ctx->done = true;
|
|
}
|
|
}
|
|
|
|
op(_LOAD_FAST, (-- value)) {
|
|
value = GETLOCAL(oparg);
|
|
}
|
|
|
|
op(_LOAD_FAST_BORROW, (-- value)) {
|
|
value = GETLOCAL(oparg);
|
|
}
|
|
|
|
op(_LOAD_FAST_AND_CLEAR, (-- value)) {
|
|
value = GETLOCAL(oparg);
|
|
JitOptSymbol *temp = sym_new_null(ctx);
|
|
GETLOCAL(oparg) = temp;
|
|
}
|
|
|
|
op(_STORE_FAST, (value --)) {
|
|
GETLOCAL(oparg) = value;
|
|
}
|
|
|
|
op(_PUSH_NULL, (-- res)) {
|
|
res = sym_new_null(ctx);
|
|
}
|
|
|
|
op(_GUARD_BOTH_INT, (left, right -- left, right)) {
|
|
if (sym_matches_type(left, &PyLong_Type)) {
|
|
if (sym_matches_type(right, &PyLong_Type)) {
|
|
REPLACE_OP(this_instr, _NOP, 0, 0);
|
|
}
|
|
else {
|
|
REPLACE_OP(this_instr, _GUARD_TOS_INT, 0, 0);
|
|
}
|
|
}
|
|
else {
|
|
if (sym_matches_type(right, &PyLong_Type)) {
|
|
REPLACE_OP(this_instr, _GUARD_NOS_INT, 0, 0);
|
|
}
|
|
}
|
|
sym_set_type(left, &PyLong_Type);
|
|
sym_set_type(right, &PyLong_Type);
|
|
}
|
|
|
|
op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner)) {
|
|
assert(type_version);
|
|
if (sym_matches_type_version(owner, type_version)) {
|
|
REPLACE_OP(this_instr, _NOP, 0, 0);
|
|
} else {
|
|
// add watcher so that whenever the type changes we invalidate this
|
|
PyTypeObject *type = _PyType_LookupByVersion(type_version);
|
|
// if the type is null, it was not found in the cache (there was a conflict)
|
|
// with the key, in which case we can't trust the version
|
|
if (type) {
|
|
// if the type version was set properly, then add a watcher
|
|
// if it wasn't this means that the type version was previously set to something else
|
|
// and we set the owner to bottom, so we don't need to add a watcher because we must have
|
|
// already added one earlier.
|
|
if (sym_set_type_version(owner, type_version)) {
|
|
PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type);
|
|
_Py_BloomFilter_Add(dependencies, type);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
op(_GUARD_BOTH_FLOAT, (left, right -- left, right)) {
|
|
if (sym_matches_type(left, &PyFloat_Type)) {
|
|
if (sym_matches_type(right, &PyFloat_Type)) {
|
|
REPLACE_OP(this_instr, _NOP, 0, 0);
|
|
}
|
|
else {
|
|
REPLACE_OP(this_instr, _GUARD_TOS_FLOAT, 0, 0);
|
|
}
|
|
}
|
|
else {
|
|
if (sym_matches_type(right, &PyFloat_Type)) {
|
|
REPLACE_OP(this_instr, _GUARD_NOS_FLOAT, 0, 0);
|
|
}
|
|
}
|
|
|
|
sym_set_type(left, &PyFloat_Type);
|
|
sym_set_type(right, &PyFloat_Type);
|
|
}
|
|
|
|
op(_GUARD_BOTH_UNICODE, (left, right -- left, right)) {
|
|
if (sym_matches_type(left, &PyUnicode_Type) &&
|
|
sym_matches_type(right, &PyUnicode_Type)) {
|
|
REPLACE_OP(this_instr, _NOP, 0 ,0);
|
|
}
|
|
sym_set_type(left, &PyUnicode_Type);
|
|
sym_set_type(right, &PyUnicode_Type);
|
|
}
|
|
|
|
op(_BINARY_OP, (left, right -- res)) {
|
|
bool lhs_int = sym_matches_type(left, &PyLong_Type);
|
|
bool rhs_int = sym_matches_type(right, &PyLong_Type);
|
|
bool lhs_float = sym_matches_type(left, &PyFloat_Type);
|
|
bool rhs_float = sym_matches_type(right, &PyFloat_Type);
|
|
if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) {
|
|
// There's something other than an int or float involved:
|
|
res = sym_new_unknown(ctx);
|
|
}
|
|
else if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) {
|
|
// This one's fun... the *type* of the result depends on the
|
|
// *values* being exponentiated. However, exponents with one
|
|
// constant part are reasonably common, so it's probably worth
|
|
// trying to infer some simple cases:
|
|
// - A: 1 ** 1 -> 1 (int ** int -> int)
|
|
// - B: 1 ** -1 -> 1.0 (int ** int -> float)
|
|
// - C: 1.0 ** 1 -> 1.0 (float ** int -> float)
|
|
// - D: 1 ** 1.0 -> 1.0 (int ** float -> float)
|
|
// - E: -1 ** 0.5 ~> 1j (int ** float -> complex)
|
|
// - F: 1.0 ** 1.0 -> 1.0 (float ** float -> float)
|
|
// - G: -1.0 ** 0.5 ~> 1j (float ** float -> complex)
|
|
if (rhs_float) {
|
|
// Case D, E, F, or G... can't know without the sign of the LHS
|
|
// or whether the RHS is whole, which isn't worth the effort:
|
|
res = sym_new_unknown(ctx);
|
|
}
|
|
else if (lhs_float) {
|
|
// Case C:
|
|
res = sym_new_type(ctx, &PyFloat_Type);
|
|
}
|
|
else if (!sym_is_const(ctx, right)) {
|
|
// Case A or B... can't know without the sign of the RHS:
|
|
res = sym_new_unknown(ctx);
|
|
}
|
|
else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, right))) {
|
|
// Case B:
|
|
res = sym_new_type(ctx, &PyFloat_Type);
|
|
}
|
|
else {
|
|
// Case A:
|
|
res = sym_new_type(ctx, &PyLong_Type);
|
|
}
|
|
}
|
|
else if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) {
|
|
res = sym_new_type(ctx, &PyFloat_Type);
|
|
}
|
|
else if (lhs_int && rhs_int) {
|
|
res = sym_new_type(ctx, &PyLong_Type);
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyFloat_Type);
|
|
}
|
|
}
|
|
|
|
op(_BINARY_OP_ADD_INT, (left, right -- res)) {
|
|
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
|
|
assert(PyLong_CheckExact(sym_get_const(ctx, left)));
|
|
assert(PyLong_CheckExact(sym_get_const(ctx, right)));
|
|
PyObject *temp = _PyLong_Add((PyLongObject *)sym_get_const(ctx, left),
|
|
(PyLongObject *)sym_get_const(ctx, right));
|
|
if (temp == NULL) {
|
|
goto error;
|
|
}
|
|
res = sym_new_const(ctx, temp);
|
|
Py_DECREF(temp);
|
|
// TODO gh-115506:
|
|
// replace opcode with constant propagated one and add tests!
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyLong_Type);
|
|
}
|
|
}
|
|
|
|
op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) {
|
|
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
|
|
assert(PyLong_CheckExact(sym_get_const(ctx, left)));
|
|
assert(PyLong_CheckExact(sym_get_const(ctx, right)));
|
|
PyObject *temp = _PyLong_Subtract((PyLongObject *)sym_get_const(ctx, left),
|
|
(PyLongObject *)sym_get_const(ctx, right));
|
|
if (temp == NULL) {
|
|
goto error;
|
|
}
|
|
res = sym_new_const(ctx, temp);
|
|
Py_DECREF(temp);
|
|
// TODO gh-115506:
|
|
// replace opcode with constant propagated one and add tests!
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyLong_Type);
|
|
}
|
|
}
|
|
|
|
op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) {
|
|
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
|
|
assert(PyLong_CheckExact(sym_get_const(ctx, left)));
|
|
assert(PyLong_CheckExact(sym_get_const(ctx, right)));
|
|
PyObject *temp = _PyLong_Multiply((PyLongObject *)sym_get_const(ctx, left),
|
|
(PyLongObject *)sym_get_const(ctx, right));
|
|
if (temp == NULL) {
|
|
goto error;
|
|
}
|
|
res = sym_new_const(ctx, temp);
|
|
Py_DECREF(temp);
|
|
// TODO gh-115506:
|
|
// replace opcode with constant propagated one and add tests!
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyLong_Type);
|
|
}
|
|
}
|
|
|
|
op(_BINARY_OP_ADD_FLOAT, (left, right -- res)) {
|
|
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
|
|
assert(PyFloat_CheckExact(sym_get_const(ctx, left)));
|
|
assert(PyFloat_CheckExact(sym_get_const(ctx, right)));
|
|
PyObject *temp = PyFloat_FromDouble(
|
|
PyFloat_AS_DOUBLE(sym_get_const(ctx, left)) +
|
|
PyFloat_AS_DOUBLE(sym_get_const(ctx, right)));
|
|
if (temp == NULL) {
|
|
goto error;
|
|
}
|
|
res = sym_new_const(ctx, temp);
|
|
Py_DECREF(temp);
|
|
// TODO gh-115506:
|
|
// replace opcode with constant propagated one and update tests!
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyFloat_Type);
|
|
}
|
|
}
|
|
|
|
op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res)) {
|
|
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
|
|
assert(PyFloat_CheckExact(sym_get_const(ctx, left)));
|
|
assert(PyFloat_CheckExact(sym_get_const(ctx, right)));
|
|
PyObject *temp = PyFloat_FromDouble(
|
|
PyFloat_AS_DOUBLE(sym_get_const(ctx, left)) -
|
|
PyFloat_AS_DOUBLE(sym_get_const(ctx, right)));
|
|
if (temp == NULL) {
|
|
goto error;
|
|
}
|
|
res = sym_new_const(ctx, temp);
|
|
Py_DECREF(temp);
|
|
// TODO gh-115506:
|
|
// replace opcode with constant propagated one and update tests!
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyFloat_Type);
|
|
}
|
|
}
|
|
|
|
op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) {
|
|
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
|
|
assert(PyFloat_CheckExact(sym_get_const(ctx, left)));
|
|
assert(PyFloat_CheckExact(sym_get_const(ctx, right)));
|
|
PyObject *temp = PyFloat_FromDouble(
|
|
PyFloat_AS_DOUBLE(sym_get_const(ctx, left)) *
|
|
PyFloat_AS_DOUBLE(sym_get_const(ctx, right)));
|
|
if (temp == NULL) {
|
|
goto error;
|
|
}
|
|
res = sym_new_const(ctx, temp);
|
|
Py_DECREF(temp);
|
|
// TODO gh-115506:
|
|
// replace opcode with constant propagated one and update tests!
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyFloat_Type);
|
|
}
|
|
}
|
|
|
|
op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) {
|
|
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
|
|
assert(PyUnicode_CheckExact(sym_get_const(ctx, left)));
|
|
assert(PyUnicode_CheckExact(sym_get_const(ctx, right)));
|
|
PyObject *temp = PyUnicode_Concat(sym_get_const(ctx, left), sym_get_const(ctx, right));
|
|
if (temp == NULL) {
|
|
goto error;
|
|
}
|
|
res = sym_new_const(ctx, temp);
|
|
Py_DECREF(temp);
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyUnicode_Type);
|
|
}
|
|
}
|
|
|
|
op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- )) {
|
|
JitOptSymbol *res;
|
|
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
|
|
assert(PyUnicode_CheckExact(sym_get_const(ctx, left)));
|
|
assert(PyUnicode_CheckExact(sym_get_const(ctx, right)));
|
|
PyObject *temp = PyUnicode_Concat(sym_get_const(ctx, left), sym_get_const(ctx, right));
|
|
if (temp == NULL) {
|
|
goto error;
|
|
}
|
|
res = sym_new_const(ctx, temp);
|
|
Py_DECREF(temp);
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyUnicode_Type);
|
|
}
|
|
// _STORE_FAST:
|
|
GETLOCAL(this_instr->operand0) = res;
|
|
}
|
|
|
|
op(_BINARY_OP_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame: _Py_UOpsAbstractFrame *)) {
|
|
new_frame = NULL;
|
|
ctx->done = true;
|
|
}
|
|
|
|
op(_TO_BOOL, (value -- res)) {
|
|
if (!optimize_to_bool(this_instr, ctx, value, &res)) {
|
|
res = sym_new_truthiness(ctx, value, true);
|
|
}
|
|
}
|
|
|
|
op(_TO_BOOL_BOOL, (value -- res)) {
|
|
if (!optimize_to_bool(this_instr, ctx, value, &res)) {
|
|
sym_set_type(value, &PyBool_Type);
|
|
res = sym_new_truthiness(ctx, value, true);
|
|
}
|
|
}
|
|
|
|
op(_TO_BOOL_INT, (value -- res)) {
|
|
if (!optimize_to_bool(this_instr, ctx, value, &res)) {
|
|
sym_set_type(value, &PyLong_Type);
|
|
res = sym_new_truthiness(ctx, value, true);
|
|
}
|
|
}
|
|
|
|
op(_TO_BOOL_LIST, (value -- res)) {
|
|
if (!optimize_to_bool(this_instr, ctx, value, &res)) {
|
|
sym_set_type(value, &PyList_Type);
|
|
res = sym_new_type(ctx, &PyBool_Type);
|
|
}
|
|
}
|
|
|
|
op(_TO_BOOL_NONE, (value -- res)) {
|
|
if (!optimize_to_bool(this_instr, ctx, value, &res)) {
|
|
sym_set_const(value, Py_None);
|
|
res = sym_new_const(ctx, Py_False);
|
|
}
|
|
}
|
|
|
|
op(_GUARD_TOS_UNICODE, (value -- value)) {
|
|
if (sym_matches_type(value, &PyUnicode_Type)) {
|
|
REPLACE_OP(this_instr, _NOP, 0, 0);
|
|
}
|
|
sym_set_type(value, &PyUnicode_Type);
|
|
}
|
|
|
|
op(_TO_BOOL_STR, (value -- res)) {
|
|
if (!optimize_to_bool(this_instr, ctx, value, &res)) {
|
|
res = sym_new_truthiness(ctx, value, true);
|
|
}
|
|
}
|
|
|
|
op(_UNARY_NOT, (value -- res)) {
|
|
sym_set_type(value, &PyBool_Type);
|
|
res = sym_new_truthiness(ctx, value, false);
|
|
}
|
|
|
|
op(_COMPARE_OP, (left, right -- res)) {
|
|
if (oparg & 16) {
|
|
res = sym_new_type(ctx, &PyBool_Type);
|
|
}
|
|
else {
|
|
res = _Py_uop_sym_new_not_null(ctx);
|
|
}
|
|
}
|
|
|
|
op(_COMPARE_OP_INT, (left, right -- res)) {
|
|
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
|
|
assert(PyLong_CheckExact(sym_get_const(ctx, left)));
|
|
assert(PyLong_CheckExact(sym_get_const(ctx, right)));
|
|
PyObject *tmp = PyObject_RichCompare(sym_get_const(ctx, left),
|
|
sym_get_const(ctx, right),
|
|
oparg >> 5);
|
|
if (tmp == NULL) {
|
|
goto error;
|
|
}
|
|
assert(PyBool_Check(tmp));
|
|
assert(_Py_IsImmortal(tmp));
|
|
REPLACE_OP(this_instr, _POP_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)tmp);
|
|
res = sym_new_const(ctx, tmp);
|
|
Py_DECREF(tmp);
|
|
}
|
|
else {
|
|
res = sym_new_type(ctx, &PyBool_Type);
|
|
}
|
|
}
|
|
|
|
op(_COMPARE_OP_FLOAT, (left, right -- res)) {
|
|
res = sym_new_type(ctx, &PyBool_Type);
|
|
}
|
|
|
|
op(_COMPARE_OP_STR, (left, right -- res)) {
|
|
res = sym_new_type(ctx, &PyBool_Type);
|
|
}
|
|
|
|
op(_IS_OP, (left, right -- res)) {
|
|
res = sym_new_type(ctx, &PyBool_Type);
|
|
}
|
|
|
|
op(_CONTAINS_OP, (left, right -- res)) {
|
|
res = sym_new_type(ctx, &PyBool_Type);
|
|
}
|
|
|
|
op(_LOAD_CONST, (-- value)) {
|
|
PyObject *val = PyTuple_GET_ITEM(co->co_consts, this_instr->oparg);
|
|
int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE;
|
|
REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val);
|
|
value = sym_new_const(ctx, val);
|
|
}
|
|
|
|
op(_LOAD_CONST_MORTAL, (-- value)) {
|
|
PyObject *val = PyTuple_GET_ITEM(co->co_consts, this_instr->oparg);
|
|
int opcode = _Py_IsImmortal(val) ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE;
|
|
REPLACE_OP(this_instr, opcode, 0, (uintptr_t)val);
|
|
value = sym_new_const(ctx, val);
|
|
}
|
|
|
|
op(_LOAD_CONST_IMMORTAL, (-- value)) {
|
|
PyObject *val = PyTuple_GET_ITEM(co->co_consts, this_instr->oparg);
|
|
REPLACE_OP(this_instr, _LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val);
|
|
value = sym_new_const(ctx, val);
|
|
}
|
|
|
|
op(_LOAD_SMALL_INT, (-- value)) {
|
|
PyObject *val = PyLong_FromLong(this_instr->oparg);
|
|
value = sym_new_const(ctx, val);
|
|
}
|
|
|
|
op(_LOAD_CONST_INLINE, (ptr/4 -- value)) {
|
|
value = sym_new_const(ctx, ptr);
|
|
}
|
|
|
|
op(_LOAD_CONST_INLINE_BORROW, (ptr/4 -- value)) {
|
|
value = sym_new_const(ctx, ptr);
|
|
}
|
|
|
|
op(_POP_TOP_LOAD_CONST_INLINE, (ptr/4, pop -- value)) {
|
|
value = sym_new_const(ctx, ptr);
|
|
}
|
|
|
|
op(_POP_TOP_LOAD_CONST_INLINE_BORROW, (ptr/4, pop -- value)) {
|
|
value = sym_new_const(ctx, ptr);
|
|
}
|
|
|
|
op(_COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) {
|
|
assert(oparg > 0);
|
|
top = bottom;
|
|
}
|
|
|
|
op(_SWAP, (bottom[1], unused[oparg-2], top[1] -- bottom[1], unused[oparg-2], top[1])) {
|
|
JitOptSymbol *temp = bottom[0];
|
|
bottom[0] = top[0];
|
|
top[0] = temp;
|
|
assert(oparg >= 2);
|
|
}
|
|
|
|
op(_LOAD_ATTR_INSTANCE_VALUE, (offset/1, owner -- attr)) {
|
|
attr = sym_new_not_null(ctx);
|
|
(void)offset;
|
|
}
|
|
|
|
op(_LOAD_ATTR_MODULE, (dict_version/2, owner, index/1 -- attr)) {
|
|
(void)dict_version;
|
|
(void)index;
|
|
attr = NULL;
|
|
if (sym_is_const(ctx, owner)) {
|
|
PyModuleObject *mod = (PyModuleObject *)sym_get_const(ctx, owner);
|
|
if (PyModule_CheckExact(mod)) {
|
|
PyObject *dict = mod->md_dict;
|
|
uint64_t watched_mutations = get_mutations(dict);
|
|
if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
|
|
PyDict_Watch(GLOBALS_WATCHER_ID, dict);
|
|
_Py_BloomFilter_Add(dependencies, dict);
|
|
PyObject *res = convert_global_to_const(this_instr, dict, true);
|
|
attr = sym_new_const(ctx, res);
|
|
}
|
|
}
|
|
}
|
|
if (attr == NULL) {
|
|
/* No conversion made. We don't know what `attr` is. */
|
|
attr = sym_new_not_null(ctx);
|
|
}
|
|
}
|
|
|
|
op (_PUSH_NULL_CONDITIONAL, ( -- null[oparg & 1])) {
|
|
if (oparg & 1) {
|
|
REPLACE_OP(this_instr, _PUSH_NULL, 0, 0);
|
|
null[0] = sym_new_null(ctx);
|
|
}
|
|
else {
|
|
REPLACE_OP(this_instr, _NOP, 0, 0);
|
|
}
|
|
}
|
|
|
|
op(_LOAD_ATTR, (owner -- attr, self_or_null[oparg&1])) {
|
|
(void)owner;
|
|
attr = sym_new_not_null(ctx);
|
|
if (oparg &1) {
|
|
self_or_null[0] = sym_new_unknown(ctx);
|
|
}
|
|
}
|
|
|
|
op(_LOAD_ATTR_WITH_HINT, (hint/1, owner -- attr)) {
|
|
attr = sym_new_not_null(ctx);
|
|
(void)hint;
|
|
}
|
|
|
|
op(_LOAD_ATTR_SLOT, (index/1, owner -- attr)) {
|
|
attr = sym_new_not_null(ctx);
|
|
(void)index;
|
|
}
|
|
|
|
op(_LOAD_ATTR_CLASS, (descr/4, owner -- attr)) {
|
|
attr = sym_new_not_null(ctx);
|
|
(void)descr;
|
|
}
|
|
|
|
op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self)) {
|
|
(void)descr;
|
|
attr = sym_new_not_null(ctx);
|
|
self = owner;
|
|
}
|
|
|
|
op(_LOAD_ATTR_METHOD_NO_DICT, (descr/4, owner -- attr, self)) {
|
|
(void)descr;
|
|
attr = sym_new_not_null(ctx);
|
|
self = owner;
|
|
}
|
|
|
|
op(_LOAD_ATTR_METHOD_LAZY_DICT, (descr/4, owner -- attr, self)) {
|
|
(void)descr;
|
|
attr = sym_new_not_null(ctx);
|
|
self = owner;
|
|
}
|
|
|
|
op(_LOAD_ATTR_PROPERTY_FRAME, (fget/4, owner -- new_frame: _Py_UOpsAbstractFrame *)) {
|
|
(void)fget;
|
|
new_frame = NULL;
|
|
ctx->done = true;
|
|
}
|
|
|
|
op(_INIT_CALL_BOUND_METHOD_EXACT_ARGS, (callable[1], self_or_null[1], unused[oparg] -- callable[1], self_or_null[1], unused[oparg])) {
|
|
callable[0] = sym_new_not_null(ctx);
|
|
self_or_null[0] = sym_new_not_null(ctx);
|
|
}
|
|
|
|
op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
|
|
if (sym_is_const(ctx, callable) && sym_matches_type(callable, &PyFunction_Type)) {
|
|
assert(PyFunction_Check(sym_get_const(ctx, callable)));
|
|
REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, func_version);
|
|
this_instr->operand1 = (uintptr_t)sym_get_const(ctx, callable);
|
|
}
|
|
sym_set_type(callable, &PyFunction_Type);
|
|
}
|
|
|
|
op(_CHECK_FUNCTION_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) {
|
|
assert(sym_matches_type(callable, &PyFunction_Type));
|
|
if (sym_is_const(ctx, callable)) {
|
|
if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) {
|
|
PyFunctionObject *func = (PyFunctionObject *)sym_get_const(ctx, callable);
|
|
PyCodeObject *co = (PyCodeObject *)func->func_code;
|
|
if (co->co_argcount == oparg + !sym_is_null(self_or_null)) {
|
|
REPLACE_OP(this_instr, _NOP, 0 ,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) {
|
|
sym_set_null(null);
|
|
sym_set_type(callable, &PyMethod_Type);
|
|
}
|
|
|
|
op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _Py_UOpsAbstractFrame *)) {
|
|
int argcount = oparg;
|
|
|
|
PyCodeObject *co = NULL;
|
|
assert((this_instr + 2)->opcode == _PUSH_FRAME);
|
|
co = get_code_with_logging((this_instr + 2));
|
|
if (co == NULL) {
|
|
ctx->done = true;
|
|
break;
|
|
}
|
|
|
|
|
|
assert(self_or_null != NULL);
|
|
assert(args != NULL);
|
|
if (sym_is_not_null(self_or_null)) {
|
|
// Bound method fiddling, same as _INIT_CALL_PY_EXACT_ARGS in VM
|
|
args--;
|
|
argcount++;
|
|
}
|
|
|
|
if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) {
|
|
new_frame = frame_new(ctx, co, 0, args, argcount);
|
|
} else {
|
|
new_frame = frame_new(ctx, co, 0, NULL, 0);
|
|
|
|
}
|
|
}
|
|
|
|
op(_MAYBE_EXPAND_METHOD, (callable, self_or_null, args[oparg] -- func, maybe_self, args[oparg])) {
|
|
(void)args;
|
|
func = sym_new_not_null(ctx);
|
|
maybe_self = sym_new_not_null(ctx);
|
|
}
|
|
|
|
op(_PY_FRAME_GENERAL, (callable, self_or_null, args[oparg] -- new_frame: _Py_UOpsAbstractFrame *)) {
|
|
PyCodeObject *co = NULL;
|
|
assert((this_instr + 2)->opcode == _PUSH_FRAME);
|
|
co = get_code_with_logging((this_instr + 2));
|
|
if (co == NULL) {
|
|
ctx->done = true;
|
|
break;
|
|
}
|
|
|
|
new_frame = frame_new(ctx, co, 0, NULL, 0);
|
|
}
|
|
|
|
op(_PY_FRAME_KW, (callable, self_or_null, args[oparg], kwnames -- new_frame: _Py_UOpsAbstractFrame *)) {
|
|
new_frame = NULL;
|
|
ctx->done = true;
|
|
}
|
|
|
|
op(_CHECK_AND_ALLOCATE_OBJECT, (type_version/2, callable, null, args[oparg] -- self, init, args[oparg])) {
|
|
(void)type_version;
|
|
(void)args;
|
|
self = sym_new_not_null(ctx);
|
|
init = sym_new_not_null(ctx);
|
|
}
|
|
|
|
op(_CREATE_INIT_FRAME, (self, init, args[oparg] -- init_frame: _Py_UOpsAbstractFrame *)) {
|
|
init_frame = NULL;
|
|
ctx->done = true;
|
|
}
|
|
|
|
op(_RETURN_VALUE, (retval -- res)) {
|
|
JitOptSymbol *temp = retval;
|
|
DEAD(retval);
|
|
SAVE_STACK();
|
|
ctx->frame->stack_pointer = stack_pointer;
|
|
frame_pop(ctx);
|
|
stack_pointer = ctx->frame->stack_pointer;
|
|
|
|
/* Stack space handling */
|
|
assert(corresponding_check_stack == NULL);
|
|
assert(co != NULL);
|
|
int framesize = co->co_framesize;
|
|
assert(framesize > 0);
|
|
assert(framesize <= curr_space);
|
|
curr_space -= framesize;
|
|
|
|
co = get_code(this_instr);
|
|
if (co == NULL) {
|
|
// might be impossible, but bailing is still safe
|
|
ctx->done = true;
|
|
}
|
|
RELOAD_STACK();
|
|
res = temp;
|
|
}
|
|
|
|
op(_RETURN_GENERATOR, ( -- res)) {
|
|
SYNC_SP();
|
|
ctx->frame->stack_pointer = stack_pointer;
|
|
frame_pop(ctx);
|
|
stack_pointer = ctx->frame->stack_pointer;
|
|
res = sym_new_unknown(ctx);
|
|
|
|
/* Stack space handling */
|
|
assert(corresponding_check_stack == NULL);
|
|
assert(co != NULL);
|
|
int framesize = co->co_framesize;
|
|
assert(framesize > 0);
|
|
assert(framesize <= curr_space);
|
|
curr_space -= framesize;
|
|
|
|
co = get_code(this_instr);
|
|
if (co == NULL) {
|
|
// might be impossible, but bailing is still safe
|
|
ctx->done = true;
|
|
}
|
|
}
|
|
|
|
op(_YIELD_VALUE, (unused -- res)) {
|
|
res = sym_new_unknown(ctx);
|
|
}
|
|
|
|
op(_FOR_ITER_GEN_FRAME, ( -- )) {
|
|
/* We are about to hit the end of the trace */
|
|
ctx->done = true;
|
|
}
|
|
|
|
op(_SEND_GEN_FRAME, ( -- )) {
|
|
// We are about to hit the end of the trace:
|
|
ctx->done = true;
|
|
}
|
|
|
|
op(_CHECK_STACK_SPACE, ( --)) {
|
|
assert(corresponding_check_stack == NULL);
|
|
corresponding_check_stack = this_instr;
|
|
}
|
|
|
|
op (_CHECK_STACK_SPACE_OPERAND, (framesize/2 -- )) {
|
|
(void)framesize;
|
|
/* We should never see _CHECK_STACK_SPACE_OPERANDs.
|
|
* They are only created at the end of this pass. */
|
|
Py_UNREACHABLE();
|
|
}
|
|
|
|
op(_PUSH_FRAME, (new_frame: _Py_UOpsAbstractFrame * -- )) {
|
|
SYNC_SP();
|
|
ctx->frame->stack_pointer = stack_pointer;
|
|
ctx->frame = new_frame;
|
|
ctx->curr_frame_depth++;
|
|
stack_pointer = new_frame->stack_pointer;
|
|
co = get_code(this_instr);
|
|
if (co == NULL) {
|
|
// should be about to _EXIT_TRACE anyway
|
|
ctx->done = true;
|
|
break;
|
|
}
|
|
|
|
/* Stack space handling */
|
|
int framesize = co->co_framesize;
|
|
assert(framesize > 0);
|
|
curr_space += framesize;
|
|
if (curr_space < 0 || curr_space > INT32_MAX) {
|
|
// won't fit in signed 32-bit int
|
|
ctx->done = true;
|
|
break;
|
|
}
|
|
max_space = curr_space > max_space ? curr_space : max_space;
|
|
if (first_valid_check_stack == NULL) {
|
|
first_valid_check_stack = corresponding_check_stack;
|
|
}
|
|
else if (corresponding_check_stack) {
|
|
// delete all but the first valid _CHECK_STACK_SPACE
|
|
corresponding_check_stack->opcode = _NOP;
|
|
}
|
|
corresponding_check_stack = NULL;
|
|
}
|
|
|
|
op(_UNPACK_SEQUENCE, (seq -- values[oparg])) {
|
|
/* This has to be done manually */
|
|
for (int i = 0; i < oparg; i++) {
|
|
values[i] = sym_new_unknown(ctx);
|
|
}
|
|
}
|
|
|
|
op(_UNPACK_EX, (seq -- values[oparg & 0xFF], unused, unused[oparg >> 8])) {
|
|
/* This has to be done manually */
|
|
int totalargs = (oparg & 0xFF) + (oparg >> 8) + 1;
|
|
for (int i = 0; i < totalargs; i++) {
|
|
values[i] = sym_new_unknown(ctx);
|
|
}
|
|
}
|
|
|
|
op(_ITER_NEXT_RANGE, (iter -- iter, next)) {
|
|
next = sym_new_type(ctx, &PyLong_Type);
|
|
}
|
|
|
|
op(_GUARD_IS_TRUE_POP, (flag -- )) {
|
|
if (sym_is_const(ctx, flag)) {
|
|
PyObject *value = sym_get_const(ctx, flag);
|
|
assert(value != NULL);
|
|
eliminate_pop_guard(this_instr, value != Py_True);
|
|
}
|
|
sym_set_const(flag, Py_True);
|
|
}
|
|
|
|
op(_GUARD_IS_FALSE_POP, (flag -- )) {
|
|
if (sym_is_const(ctx, flag)) {
|
|
PyObject *value = sym_get_const(ctx, flag);
|
|
assert(value != NULL);
|
|
eliminate_pop_guard(this_instr, value != Py_False);
|
|
}
|
|
sym_set_const(flag, Py_False);
|
|
}
|
|
|
|
op(_GUARD_IS_NONE_POP, (flag -- )) {
|
|
if (sym_is_const(ctx, flag)) {
|
|
PyObject *value = sym_get_const(ctx, flag);
|
|
assert(value != NULL);
|
|
eliminate_pop_guard(this_instr, !Py_IsNone(value));
|
|
}
|
|
else if (sym_has_type(flag)) {
|
|
assert(!sym_matches_type(flag, &_PyNone_Type));
|
|
eliminate_pop_guard(this_instr, true);
|
|
}
|
|
sym_set_const(flag, Py_None);
|
|
}
|
|
|
|
op(_GUARD_IS_NOT_NONE_POP, (flag -- )) {
|
|
if (sym_is_const(ctx, flag)) {
|
|
PyObject *value = sym_get_const(ctx, flag);
|
|
assert(value != NULL);
|
|
eliminate_pop_guard(this_instr, Py_IsNone(value));
|
|
}
|
|
else if (sym_has_type(flag)) {
|
|
assert(!sym_matches_type(flag, &_PyNone_Type));
|
|
eliminate_pop_guard(this_instr, false);
|
|
}
|
|
}
|
|
|
|
op(_CHECK_PEP_523, (--)) {
|
|
/* Setting the eval frame function invalidates
|
|
* all executors, so no need to check dynamically */
|
|
if (_PyInterpreterState_GET()->eval_frame == NULL) {
|
|
REPLACE_OP(this_instr, _NOP, 0 ,0);
|
|
}
|
|
}
|
|
|
|
op(_LOAD_SPECIAL, (owner -- attr, self_or_null)) {
|
|
attr = sym_new_not_null(ctx);
|
|
self_or_null = sym_new_unknown(ctx);
|
|
}
|
|
|
|
op(_JUMP_TO_TOP, (--)) {
|
|
ctx->done = true;
|
|
}
|
|
|
|
op(_EXIT_TRACE, (exit_p/4 --)) {
|
|
(void)exit_p;
|
|
ctx->done = true;
|
|
}
|
|
|
|
op(_REPLACE_WITH_TRUE, (value -- res)) {
|
|
res = sym_new_const(ctx, Py_True);
|
|
}
|
|
|
|
op(_BUILD_TUPLE, (values[oparg] -- tup)) {
|
|
tup = sym_new_tuple(ctx, oparg, values);
|
|
}
|
|
|
|
op(_UNPACK_SEQUENCE_TWO_TUPLE, (seq -- val1, val0)) {
|
|
val0 = sym_tuple_getitem(ctx, seq, 0);
|
|
val1 = sym_tuple_getitem(ctx, seq, 1);
|
|
}
|
|
|
|
op(_UNPACK_SEQUENCE_TUPLE, (seq -- values[oparg])) {
|
|
for (int i = 0; i < oparg; i++) {
|
|
values[i] = sym_tuple_getitem(ctx, seq, i);
|
|
}
|
|
}
|
|
|
|
|
|
// END BYTECODES //
|
|
|
|
}
|