Skip to content

gh-120619: Tier 2 partial evaluation pass foundations #123652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 42 additions & 27 deletions Include/internal/pycore_optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,13 @@ struct _Py_UopsSymbol {
PyTypeObject *typ; // Borrowed reference
PyObject *const_val; // Owned reference (!)
unsigned int type_version; // currently stores type version
int locals_idx;
char is_static; // used for binding-time analysis
};

#define UOP_FORMAT_TARGET 0
#define UOP_FORMAT_JUMP 1
void _PyUOpPrint(const _PyUOpInstruction*);

static inline uint32_t uop_get_target(const _PyUOpInstruction *inst)
{
Expand Down Expand Up @@ -199,14 +202,21 @@ static inline uint16_t uop_get_error_target(const _PyUOpInstruction *inst)

typedef struct _Py_UopsSymbol _Py_UopsSymbol;

typedef struct _Py_UopsLocalsPlusSlot {
_Py_UopsSymbol *sym;
char is_virtual;
} _Py_UopsLocalsPlusSlot;

struct _Py_UOpsAbstractFrame {
// Max stacklen
int stack_len;
int locals_len;

_Py_UopsSymbol **stack_pointer;
_Py_UopsSymbol **stack;
_Py_UopsSymbol **locals;
_Py_UopsLocalsPlusSlot *stack_pointer;
_Py_UopsLocalsPlusSlot *stack;
_Py_UopsLocalsPlusSlot *locals;

void *instr_ptr;
};

typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
Expand All @@ -229,34 +239,39 @@ struct _Py_UOpsContext {
// Arena for the symbolic types.
ty_arena t_arena;

_Py_UopsSymbol **n_consumed;
_Py_UopsSymbol **limit;
_Py_UopsSymbol *locals_and_stack[MAX_ABSTRACT_INTERP_SIZE];
_Py_UopsLocalsPlusSlot *n_consumed;
_Py_UopsLocalsPlusSlot *limit;
_Py_UopsLocalsPlusSlot locals_and_stack[MAX_ABSTRACT_INTERP_SIZE];

_PyUOpInstruction *trace_dest;
int n_trace_dest;
};

typedef struct _Py_UOpsContext _Py_UOpsContext;

extern bool _Py_uop_sym_is_null(_Py_UopsSymbol *sym);
extern bool _Py_uop_sym_is_not_null(_Py_UopsSymbol *sym);
extern bool _Py_uop_sym_is_const(_Py_UopsSymbol *sym);
extern PyObject *_Py_uop_sym_get_const(_Py_UopsSymbol *sym);
extern _Py_UopsSymbol *_Py_uop_sym_new_unknown(_Py_UOpsContext *ctx);
extern _Py_UopsSymbol *_Py_uop_sym_new_not_null(_Py_UOpsContext *ctx);
extern _Py_UopsSymbol *_Py_uop_sym_new_type(
extern bool _Py_uop_sym_is_null(_Py_UopsLocalsPlusSlot sym);
extern bool _Py_uop_sym_is_not_null(_Py_UopsLocalsPlusSlot sym);
extern bool _Py_uop_sym_is_const(_Py_UopsLocalsPlusSlot sym);
extern PyObject *_Py_uop_sym_get_const(_Py_UopsLocalsPlusSlot sym);
extern _Py_UopsLocalsPlusSlot _Py_uop_sym_new_unknown(_Py_UOpsContext *ctx);
extern _Py_UopsLocalsPlusSlot _Py_uop_sym_new_not_null(_Py_UOpsContext *ctx);
extern _Py_UopsLocalsPlusSlot _Py_uop_sym_new_type(
_Py_UOpsContext *ctx, PyTypeObject *typ);
extern _Py_UopsSymbol *_Py_uop_sym_new_const(_Py_UOpsContext *ctx, PyObject *const_val);
extern _Py_UopsSymbol *_Py_uop_sym_new_null(_Py_UOpsContext *ctx);
extern bool _Py_uop_sym_has_type(_Py_UopsSymbol *sym);
extern bool _Py_uop_sym_matches_type(_Py_UopsSymbol *sym, PyTypeObject *typ);
extern bool _Py_uop_sym_matches_type_version(_Py_UopsSymbol *sym, unsigned int version);
extern void _Py_uop_sym_set_null(_Py_UOpsContext *ctx, _Py_UopsSymbol *sym);
extern void _Py_uop_sym_set_non_null(_Py_UOpsContext *ctx, _Py_UopsSymbol *sym);
extern void _Py_uop_sym_set_type(_Py_UOpsContext *ctx, _Py_UopsSymbol *sym, PyTypeObject *typ);
extern bool _Py_uop_sym_set_type_version(_Py_UOpsContext *ctx, _Py_UopsSymbol *sym, unsigned int version);
extern void _Py_uop_sym_set_const(_Py_UOpsContext *ctx, _Py_UopsSymbol *sym, PyObject *const_val);
extern bool _Py_uop_sym_is_bottom(_Py_UopsSymbol *sym);
extern int _Py_uop_sym_truthiness(_Py_UopsSymbol *sym);
extern PyTypeObject *_Py_uop_sym_get_type(_Py_UopsSymbol *sym);
extern _Py_UopsLocalsPlusSlot _Py_uop_sym_new_const(_Py_UOpsContext *ctx, PyObject *const_val);
extern _Py_UopsLocalsPlusSlot _Py_uop_sym_new_null(_Py_UOpsContext *ctx);
extern bool _Py_uop_sym_has_type(_Py_UopsLocalsPlusSlot sym);
extern bool _Py_uop_sym_matches_type(_Py_UopsLocalsPlusSlot sym, PyTypeObject *typ);
extern bool _Py_uop_sym_matches_type_version(_Py_UopsLocalsPlusSlot sym, unsigned int version);
extern void _Py_uop_sym_set_locals_idx(_Py_UopsLocalsPlusSlot sym, int locals_idx);
extern int _Py_uop_sym_get_locals_idx(_Py_UopsLocalsPlusSlot sym);
extern void _Py_uop_sym_set_null(_Py_UOpsContext *ctx, _Py_UopsLocalsPlusSlot sym);
extern void _Py_uop_sym_set_non_null(_Py_UOpsContext *ctx, _Py_UopsLocalsPlusSlot sym);
extern void _Py_uop_sym_set_type(_Py_UOpsContext *ctx, _Py_UopsLocalsPlusSlot sym, PyTypeObject *typ);
extern bool _Py_uop_sym_set_type_version(_Py_UOpsContext *ctx, _Py_UopsLocalsPlusSlot sym, unsigned int version);
extern void _Py_uop_sym_set_const(_Py_UOpsContext *ctx, _Py_UopsLocalsPlusSlot sym, PyObject *const_val);
extern bool _Py_uop_sym_is_bottom(_Py_UopsLocalsPlusSlot sym);
extern int _Py_uop_sym_truthiness(_Py_UopsLocalsPlusSlot sym);
extern PyTypeObject *_Py_uop_sym_get_type(_Py_UopsLocalsPlusSlot sym);


extern void _Py_uop_abstractcontext_init(_Py_UOpsContext *ctx);
Expand All @@ -266,7 +281,7 @@ extern _Py_UOpsAbstractFrame *_Py_uop_frame_new(
_Py_UOpsContext *ctx,
PyCodeObject *co,
int curr_stackentries,
_Py_UopsSymbol **args,
_Py_UopsLocalsPlusSlot *args,
int arg_len);
extern int _Py_uop_frame_pop(_Py_UOpsContext *ctx);

Expand Down
44 changes: 22 additions & 22 deletions Include/internal/pycore_uop_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1481,6 +1481,33 @@ def fn(a):

fn(A())

def test_pe_load_fast_pop_top(self):
def thing(a):
x = 0
for i in range(20):
i
return i


res, ex = self._run_with_optimizer(thing, 1)
self.assertEqual(res, 19)
self.assertIsNotNone(ex)
self.assertEqual(list(iter_opnames(ex)).count("_POP_TOP"), 0)
self.assertTrue(ex.is_valid())

def test_pe_dead_store_elimination(self):
def thing(a):
x = 0
for i in range(20):
x = x
return i


res, ex = self._run_with_optimizer(thing, 1)
self.assertEqual(res, 19)
self.assertIsNotNone(ex)
self.assertEqual(list(iter_opnames(ex)).count("_LOAD_FAST_1"), 0)
self.assertTrue(ex.is_valid())

if __name__ == "__main__":
unittest.main()
12 changes: 6 additions & 6 deletions Lib/test/test_generated_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -1264,15 +1264,15 @@ def test_overridden_abstract_args(self):
"""
output = """
case OP: {
_Py_UopsSymbol *arg1;
_Py_UopsSymbol *out;
_Py_UopsLocalsPlusSlot arg1;
_Py_UopsLocalsPlusSlot out;
eggs();
stack_pointer[-1] = out;
break;
}

case OP2: {
_Py_UopsSymbol *out;
_Py_UopsLocalsPlusSlot out;
out = sym_new_not_null(ctx);
stack_pointer[-1] = out;
break;
Expand All @@ -1296,15 +1296,15 @@ def test_no_overridden_case(self):
"""
output = """
case OP: {
_Py_UopsSymbol *out;
_Py_UopsLocalsPlusSlot out;
out = sym_new_not_null(ctx);
stack_pointer[-1] = out;
break;
}

case OP2: {
_Py_UopsSymbol *arg1;
_Py_UopsSymbol *out;
_Py_UopsLocalsPlusSlot arg1;
_Py_UopsLocalsPlusSlot out;
stack_pointer[-1] = out;
break;
}
Expand Down
15 changes: 13 additions & 2 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -1958,7 +1958,7 @@ Objects/mimalloc/page.o: $(srcdir)/Objects/mimalloc/page-queue.c
regen-cases: \
regen-opcode-ids regen-opcode-targets regen-uop-ids regen-opcode-metadata-py \
regen-generated-cases regen-executor-cases regen-optimizer-cases \
regen-opcode-metadata regen-uop-metadata
regen-partial-evaluator-cases regen-opcode-metadata regen-uop-metadata

.PHONY: regen-opcode-ids
regen-opcode-ids:
Expand Down Expand Up @@ -2004,6 +2004,16 @@ regen-optimizer-cases:
$(srcdir)/Python/bytecodes.c
$(UPDATE_FILE) $(srcdir)/Python/optimizer_cases.c.h $(srcdir)/Python/optimizer_cases.c.h.new

.PHONY: regen-partial-evaluator-cases
regen-partial-evaluator-cases:
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/cases_generator/optimizer_generator.py \
-o $(srcdir)/Python/partial_evaluator_cases.c.h.new \
$(srcdir)/Python/optimizer_bytecodes.c \
$(srcdir)/Python/partial_evaluator_bytecodes.c \
$(srcdir)/Python/bytecodes.c
$(UPDATE_FILE) $(srcdir)/Python/partial_evaluator_cases.c.h $(srcdir)/Python/partial_evaluator_cases.c.h.new


.PHONY: regen-opcode-metadata
regen-opcode-metadata:
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/cases_generator/opcode_metadata_generator.py \
Expand Down Expand Up @@ -2041,7 +2051,8 @@ Python/optimizer.o: \
Python/optimizer_analysis.o: \
$(srcdir)/Include/internal/pycore_opcode_metadata.h \
$(srcdir)/Include/internal/pycore_optimizer.h \
$(srcdir)/Python/optimizer_cases.c.h
$(srcdir)/Python/optimizer_cases.c.h \
$(srcdir)/Python/partial_evaluator_cases.c.h

Python/frozen.o: $(FROZEN_FILES_OUT)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Set up a tier 2 partial evaluation pass. Patch by Ken Jin.
Loading
Loading