gh-132732: Automatically constant evaluate pure operations (GH-132733)

This adds a "macro" to the optimizer DSL called "REPLACE_OPCODE_IF_EVALUATES_PURE", which allows automatically constant evaluating a bytecode body if certain inputs have no side effects upon evaluations (such as ints, strings, and floats).


Co-authored-by: Tomas R. <tomas.roun8@gmail.com>
This commit is contained in:
Ken Jin 2025-06-27 19:37:44 +08:00 committed by GitHub
parent c45f4f3ebe
commit 695ab61351
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 706 additions and 122 deletions

View file

@ -181,6 +181,7 @@ dummy_func(void) {
}
op(_BINARY_OP, (lhs, rhs -- res)) {
REPLACE_OPCODE_IF_EVALUATES_PURE(lhs, rhs);
bool lhs_int = sym_matches_type(lhs, &PyLong_Type);
bool rhs_int = sym_matches_type(rhs, &PyLong_Type);
bool lhs_float = sym_matches_type(lhs, &PyFloat_Type);
@ -235,35 +236,23 @@ dummy_func(void) {
}
op(_BINARY_OP_ADD_INT, (left, right -- res)) {
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
res = sym_new_compact_int(ctx);
}
op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) {
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
res = sym_new_compact_int(ctx);
}
op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) {
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
res = sym_new_compact_int(ctx);
}
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);
}
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
res = sym_new_type(ctx, &PyFloat_Type);
// TODO (gh-134584): Refactor this to use another uop
if (PyJitRef_IsBorrowed(left) && PyJitRef_IsBorrowed(right)) {
REPLACE_OP(this_instr, op_without_decref_inputs[opcode], oparg, 0);
@ -271,23 +260,8 @@ dummy_func(void) {
}
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);
}
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
res = sym_new_type(ctx, &PyFloat_Type);
// TODO (gh-134584): Refactor this to use another uop
if (PyJitRef_IsBorrowed(left) && PyJitRef_IsBorrowed(right)) {
REPLACE_OP(this_instr, op_without_decref_inputs[opcode], oparg, 0);
@ -295,23 +269,8 @@ dummy_func(void) {
}
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);
}
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
res = sym_new_type(ctx, &PyFloat_Type);
// TODO (gh-134584): Refactor this to use another uop
if (PyJitRef_IsBorrowed(left) && PyJitRef_IsBorrowed(right)) {
REPLACE_OP(this_instr, op_without_decref_inputs[opcode], oparg, 0);
@ -319,19 +278,8 @@ dummy_func(void) {
}
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);
}
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
res = sym_new_type(ctx, &PyUnicode_Type);
}
op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- )) {
@ -443,6 +391,7 @@ dummy_func(void) {
}
op(_UNARY_NOT, (value -- res)) {
REPLACE_OPCODE_IF_EVALUATES_PURE(value);
sym_set_type(value, &PyBool_Type);
res = sym_new_truthiness(ctx, value, false);
}