aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python/optimizer_analysis.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/optimizer_analysis.c')
-rw-r--r--Python/optimizer_analysis.c157
1 files changed, 109 insertions, 48 deletions
diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c
index 387ebc813ab..fab6fef5ccd 100644
--- a/Python/optimizer_analysis.c
+++ b/Python/optimizer_analysis.c
@@ -19,16 +19,15 @@
#include "pycore_opcode_utils.h"
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_uop_metadata.h"
-#include "pycore_dict.h"
#include "pycore_long.h"
#include "pycore_interpframe.h" // _PyFrame_GetCode
#include "pycore_optimizer.h"
#include "pycore_object.h"
-#include "pycore_dict.h"
#include "pycore_function.h"
-#include "pycore_uop_metadata.h"
#include "pycore_uop_ids.h"
#include "pycore_range.h"
+#include "pycore_unicodeobject.h"
+#include "pycore_ceval.h"
#include <stdarg.h>
#include <stdbool.h>
@@ -106,6 +105,10 @@ convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop)
if ((int)index >= dict->ma_keys->dk_nentries) {
return NULL;
}
+ PyDictKeysObject *keys = dict->ma_keys;
+ if (keys->dk_version != inst->operand0) {
+ return NULL;
+ }
PyObject *res = entries[index].me_value;
if (res == NULL) {
return NULL;
@@ -320,7 +323,10 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
/* Shortened forms for convenience, used in optimizer_bytecodes.c */
#define sym_is_not_null _Py_uop_sym_is_not_null
#define sym_is_const _Py_uop_sym_is_const
+#define sym_is_safe_const _Py_uop_sym_is_safe_const
#define sym_get_const _Py_uop_sym_get_const
+#define sym_new_const_steal _Py_uop_sym_new_const_steal
+#define sym_get_const_as_stackref _Py_uop_sym_get_const_as_stackref
#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
@@ -336,6 +342,7 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
#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_set_compact_int(SYM) _Py_uop_sym_set_compact_int(ctx, SYM)
#define sym_is_bottom _Py_uop_sym_is_bottom
#define sym_truthiness _Py_uop_sym_truthiness
#define frame_new _Py_uop_frame_new
@@ -343,15 +350,19 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
#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_is_immortal _Py_uop_symbol_is_immortal
+#define sym_is_compact_int _Py_uop_sym_is_compact_int
+#define sym_new_compact_int _Py_uop_sym_new_compact_int
#define sym_new_truthiness _Py_uop_sym_new_truthiness
+#define JUMP_TO_LABEL(label) goto label;
+
static int
optimize_to_bool(
_PyUOpInstruction *this_instr,
JitOptContext *ctx,
- JitOptSymbol *value,
- JitOptSymbol **result_ptr)
+ JitOptRef value,
+ JitOptRef *result_ptr)
{
if (sym_matches_type(value, &PyBool_Type)) {
REPLACE_OP(this_instr, _NOP, 0, 0);
@@ -378,6 +389,23 @@ eliminate_pop_guard(_PyUOpInstruction *this_instr, bool exit)
}
}
+static JitOptRef
+lookup_attr(JitOptContext *ctx, _PyUOpInstruction *this_instr,
+ PyTypeObject *type, PyObject *name, uint16_t immortal,
+ uint16_t mortal)
+{
+ // The cached value may be dead, so we need to do the lookup again... :(
+ if (type && PyType_Check(type)) {
+ PyObject *lookup = _PyType_Lookup(type, name);
+ if (lookup) {
+ int opcode = _Py_IsImmortal(lookup) ? immortal : mortal;
+ REPLACE_OP(this_instr, opcode, 0, (uintptr_t)lookup);
+ return sym_new_const(ctx, lookup);
+ }
+ }
+ return sym_new_not_null(ctx);
+}
+
/* _PUSH_FRAME/_RETURN_VALUE's operand can be 0, a PyFunctionObject *, or a
* PyCodeObject *. Retrieve the code object if possible.
*/
@@ -426,6 +454,13 @@ get_code_with_logging(_PyUOpInstruction *op)
return co;
}
+// TODO (gh-134584) generate most of this table automatically
+const uint16_t op_without_decref_inputs[MAX_UOP_ID + 1] = {
+ [_BINARY_OP_MULTIPLY_FLOAT] = _BINARY_OP_MULTIPLY_FLOAT__NO_DECREF_INPUTS,
+ [_BINARY_OP_ADD_FLOAT] = _BINARY_OP_ADD_FLOAT__NO_DECREF_INPUTS,
+ [_BINARY_OP_SUBTRACT_FLOAT] = _BINARY_OP_SUBTRACT_FLOAT__NO_DECREF_INPUTS,
+};
+
/* 1 for success, 0 for not ready, cannot error at the moment. */
static int
optimize_uops(
@@ -463,7 +498,7 @@ optimize_uops(
int oparg = this_instr->oparg;
opcode = this_instr->opcode;
- JitOptSymbol **stack_pointer = ctx->frame->stack_pointer;
+ JitOptRef *stack_pointer = ctx->frame->stack_pointer;
#ifdef Py_DEBUG
if (get_lltrace() >= 3) {
@@ -526,6 +561,45 @@ error:
}
+const uint16_t op_without_push[MAX_UOP_ID + 1] = {
+ [_COPY] = _NOP,
+ [_LOAD_CONST_INLINE] = _NOP,
+ [_LOAD_CONST_INLINE_BORROW] = _NOP,
+ [_LOAD_CONST_UNDER_INLINE] = _POP_TOP_LOAD_CONST_INLINE,
+ [_LOAD_CONST_UNDER_INLINE_BORROW] = _POP_TOP_LOAD_CONST_INLINE_BORROW,
+ [_LOAD_FAST] = _NOP,
+ [_LOAD_FAST_BORROW] = _NOP,
+ [_LOAD_SMALL_INT] = _NOP,
+ [_POP_TOP_LOAD_CONST_INLINE] = _POP_TOP,
+ [_POP_TOP_LOAD_CONST_INLINE_BORROW] = _POP_TOP,
+ [_POP_TWO_LOAD_CONST_INLINE_BORROW] = _POP_TWO,
+ [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = _POP_CALL_TWO,
+};
+
+const bool op_skip[MAX_UOP_ID + 1] = {
+ [_NOP] = true,
+ [_CHECK_VALIDITY] = true,
+ [_CHECK_PERIODIC] = true,
+ [_SET_IP] = true,
+};
+
+const uint16_t op_without_pop[MAX_UOP_ID + 1] = {
+ [_POP_TOP] = _NOP,
+ [_POP_TOP_LOAD_CONST_INLINE] = _LOAD_CONST_INLINE,
+ [_POP_TOP_LOAD_CONST_INLINE_BORROW] = _LOAD_CONST_INLINE_BORROW,
+ [_POP_TWO] = _POP_TOP,
+ [_POP_TWO_LOAD_CONST_INLINE_BORROW] = _POP_TOP_LOAD_CONST_INLINE_BORROW,
+ [_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW,
+ [_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW] = _POP_CALL_LOAD_CONST_INLINE_BORROW,
+ [_POP_CALL_TWO] = _POP_CALL_ONE,
+ [_POP_CALL_ONE] = _POP_CALL,
+};
+
+const uint16_t op_without_pop_null[MAX_UOP_ID + 1] = {
+ [_POP_CALL] = _POP_TOP,
+ [_POP_CALL_LOAD_CONST_INLINE_BORROW] = _POP_TOP_LOAD_CONST_INLINE_BORROW,
+};
+
static int
remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size)
@@ -554,50 +628,37 @@ remove_unneeded_uops(_PyUOpInstruction *buffer, int buffer_size)
buffer[pc].opcode = _NOP;
}
break;
- case _POP_TOP:
- case _POP_TOP_LOAD_CONST_INLINE:
- case _POP_TOP_LOAD_CONST_INLINE_BORROW:
- case _POP_TWO_LOAD_CONST_INLINE_BORROW:
- optimize_pop_top_again:
+ default:
{
- _PyUOpInstruction *last = &buffer[pc-1];
- while (last->opcode == _NOP) {
- last--;
- }
- switch (last->opcode) {
- case _POP_TWO_LOAD_CONST_INLINE_BORROW:
- last->opcode = _POP_TOP;
- break;
- case _POP_TOP_LOAD_CONST_INLINE:
- case _POP_TOP_LOAD_CONST_INLINE_BORROW:
- last->opcode = _NOP;
- goto optimize_pop_top_again;
- case _COPY:
- case _LOAD_CONST_INLINE:
- case _LOAD_CONST_INLINE_BORROW:
- case _LOAD_FAST:
- case _LOAD_FAST_BORROW:
- case _LOAD_SMALL_INT:
- last->opcode = _NOP;
- if (opcode == _POP_TOP) {
- opcode = buffer[pc].opcode = _NOP;
- }
- else if (opcode == _POP_TOP_LOAD_CONST_INLINE) {
- opcode = buffer[pc].opcode = _LOAD_CONST_INLINE;
- }
- else if (opcode == _POP_TOP_LOAD_CONST_INLINE_BORROW) {
- opcode = buffer[pc].opcode = _LOAD_CONST_INLINE_BORROW;
- }
- else {
- assert(opcode == _POP_TWO_LOAD_CONST_INLINE_BORROW);
- opcode = buffer[pc].opcode = _POP_TOP_LOAD_CONST_INLINE_BORROW;
- goto optimize_pop_top_again;
+ // Cancel out pushes and pops, repeatedly. So:
+ // _LOAD_FAST + _POP_TWO_LOAD_CONST_INLINE_BORROW + _POP_TOP
+ // ...becomes:
+ // _NOP + _POP_TOP + _NOP
+ while (op_without_pop[opcode] || op_without_pop_null[opcode]) {
+ _PyUOpInstruction *last = &buffer[pc - 1];
+ while (op_skip[last->opcode]) {
+ last--;
+ }
+ if (op_without_push[last->opcode] && op_without_pop[opcode]) {
+ last->opcode = op_without_push[last->opcode];
+ opcode = buffer[pc].opcode = op_without_pop[opcode];
+ if (op_without_pop[last->opcode]) {
+ opcode = last->opcode;
+ pc = last - buffer;
}
+ }
+ else if (last->opcode == _PUSH_NULL) {
+ // Handle _POP_CALL and _POP_CALL_LOAD_CONST_INLINE_BORROW separately.
+ // This looks for a preceding _PUSH_NULL instruction and
+ // simplifies to _POP_TOP(_LOAD_CONST_INLINE_BORROW).
+ last->opcode = _NOP;
+ opcode = buffer[pc].opcode = op_without_pop_null[opcode];
+ assert(opcode);
+ }
+ else {
+ break;
+ }
}
- _Py_FALLTHROUGH;
- }
- default:
- {
/* _PUSH_FRAME doesn't escape or error, but it
* does need the IP for the return address */
bool needs_ip = opcode == _PUSH_FRAME;