Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions Include/internal/pycore_optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ extern JitOptRef _Py_uop_sym_new_truthiness(JitOptContext *ctx, JitOptRef value,
extern bool _Py_uop_sym_is_compact_int(JitOptRef sym);
extern JitOptRef _Py_uop_sym_new_compact_int(JitOptContext *ctx);
extern void _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef sym);
extern JitOptRef _Py_uop_sym_new_slots_object(JitOptContext *ctx, unsigned int type_version);
extern JitOptRef _Py_uop_sym_slots_getattr(JitOptContext *ctx, JitOptRef ref, uint16_t slot_index);
extern void _Py_uop_sym_slots_setattr(JitOptContext *ctx, JitOptRef ref, uint16_t slot_index, JitOptRef value);

extern void _Py_uop_abstractcontext_init(JitOptContext *ctx);
extern void _Py_uop_abstractcontext_fini(JitOptContext *ctx);
Expand Down
27 changes: 27 additions & 0 deletions Include/internal/pycore_optimizer_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ extern "C" {

#define TY_ARENA_SIZE (UOP_MAX_TRACE_LENGTH * 5)

// Maximum slots per object tracked symbolically
#define MAX_SYMBOLIC_SLOTS_SIZE 16
#define SLOTS_ARENA_SIZE (MAX_SYMBOLIC_SLOTS_SIZE * 100)
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to adjust this arena_size.


// Need extras for root frame and for overflow frame (see TRACE_STACK_PUSH())
#define MAX_ABSTRACT_FRAME_DEPTH (16)

Expand All @@ -40,6 +44,7 @@ typedef enum _JitSymType {
JIT_SYM_TUPLE_TAG = 8,
JIT_SYM_TRUTHINESS_TAG = 9,
JIT_SYM_COMPACT_INT = 10,
JIT_SYM_SLOTS_TAG = 11,
} JitSymType;

typedef struct _jit_opt_known_class {
Expand Down Expand Up @@ -76,6 +81,18 @@ typedef struct {
uint8_t tag;
} JitOptCompactInt;

typedef struct {
uint16_t slot_index;
uint16_t symbol;
} JitOptSlotMapping;

Comment thread
cocolato marked this conversation as resolved.
typedef struct _jit_opt_slots {
uint8_t tag;
uint8_t num_slots;
uint32_t type_version;
JitOptSlotMapping *slots;
} JitOptSlotsObject;

typedef union _jit_opt_symbol {
uint8_t tag;
JitOptKnownClass cls;
Expand All @@ -84,6 +101,7 @@ typedef union _jit_opt_symbol {
JitOptTuple tuple;
JitOptTruthiness truthiness;
JitOptCompactInt compact;
JitOptSlotsObject slots;
} JitOptSymbol;

// This mimics the _PyStackRef API
Expand Down Expand Up @@ -112,6 +130,12 @@ typedef struct ty_arena {
JitOptSymbol arena[TY_ARENA_SIZE];
} ty_arena;

typedef struct slots_arena {
int slots_curr_number;
int slots_max_number;
JitOptSlotMapping arena[SLOTS_ARENA_SIZE];
} slots_arena;

typedef struct _JitOptContext {
char done;
char out_of_space;
Expand All @@ -126,6 +150,9 @@ typedef struct _JitOptContext {
// Arena for the symbolic types.
ty_arena t_arena;

// Arena for slots mappings.
slots_arena s_arena;

JitOptRef *n_consumed;
JitOptRef *limit;
JitOptRef locals_and_stack[MAX_ABSTRACT_INTERP_SIZE];
Expand Down
3 changes: 3 additions & 0 deletions Python/optimizer_analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ add_op(JitOptContext *ctx, _PyUOpInstruction *this_instr,
#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 sym_new_slots_object _Py_uop_sym_new_slots_object
#define sym_slots_getattr _Py_uop_sym_slots_getattr
#define sym_slots_setattr _Py_uop_sym_slots_setattr

#define JUMP_TO_LABEL(label) goto label;

Expand Down
9 changes: 5 additions & 4 deletions Python/optimizer_bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
#define sym_new_compact_int _Py_uop_sym_new_compact_int
#define sym_is_compact_int _Py_uop_sym_is_compact_int
#define sym_new_truthiness _Py_uop_sym_new_truthiness
#define sym_new_slots_object _Py_uop_sym_new_slots_object
#define sym_slots_getattr _Py_uop_sym_slots_getattr
#define sym_slots_setattr _Py_uop_sym_slots_setattr

extern int
optimize_to_bool(
Expand Down Expand Up @@ -123,8 +126,7 @@ dummy_func(void) {
}

op(_STORE_ATTR_SLOT, (index/1, value, owner -- o)) {
(void)index;
(void)value;
sym_slots_setattr(ctx, owner, (uint16_t)index, value);
o = owner;
}

Expand Down Expand Up @@ -709,8 +711,7 @@ dummy_func(void) {
}

op(_LOAD_ATTR_SLOT, (index/1, owner -- attr, o)) {
attr = sym_new_not_null(ctx);
(void)index;
attr = sym_slots_getattr(ctx, owner, (uint16_t)index);
o = owner;
}

Expand Down
6 changes: 2 additions & 4 deletions Python/optimizer_cases.c.h

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

151 changes: 150 additions & 1 deletion Python/optimizer_symbols.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ _PyUOpSymPrint(JitOptRef ref)
case JIT_SYM_COMPACT_INT:
printf("<compact_int at %p>", (void *)sym);
break;
case JIT_SYM_SLOTS_TAG:
PyTypeObject *slots_type = _PyType_LookupByVersion(sym->slots.type_version);
if (slots_type) {
printf("<%s slots[%d] v%u at %p>", slots_type->tp_name, sym->slots.num_slots, sym->slots.type_version, (void *)sym);
} else {
printf("<slots[%d] v%u at %p>", sym->slots.num_slots, sym->slots.type_version, (void *)sym);
}
break;
default:
printf("<tag=%d at %p>", sym->tag, (void *)sym);
break;
Expand Down Expand Up @@ -319,6 +327,11 @@ _Py_uop_sym_set_type(JitOptContext *ctx, JitOptRef ref, PyTypeObject *typ)
sym_set_bottom(ctx, sym);
}
return;
case JIT_SYM_SLOTS_TAG:
if (typ->tp_version_tag != sym->slots.type_version) {
sym_set_bottom(ctx, sym);
}
return;
}
}

Expand Down Expand Up @@ -382,6 +395,12 @@ _Py_uop_sym_set_type_version(JitOptContext *ctx, JitOptRef ref, unsigned int ver
return false;
}
return true;
case JIT_SYM_SLOTS_TAG:
if (version != sym->slots.type_version) {
sym_set_bottom(ctx, sym);
return false;
}
return true;
}
Py_UNREACHABLE();
}
Expand Down Expand Up @@ -474,6 +493,9 @@ _Py_uop_sym_set_const(JitOptContext *ctx, JitOptRef ref, PyObject *const_val)
sym_set_bottom(ctx, sym);
}
return;
case JIT_SYM_SLOTS_TAG:
sym_set_bottom(ctx, sym);
return;
}
}

Expand Down Expand Up @@ -593,7 +615,8 @@ _Py_uop_sym_get_type(JitOptRef ref)
return &PyBool_Type;
case JIT_SYM_COMPACT_INT:
return &PyLong_Type;

case JIT_SYM_SLOTS_TAG:
return _PyType_LookupByVersion(sym->slots.type_version);
}
Py_UNREACHABLE();
}
Expand Down Expand Up @@ -621,6 +644,8 @@ _Py_uop_sym_get_type_version(JitOptRef ref)
return PyBool_Type.tp_version_tag;
case JIT_SYM_COMPACT_INT:
return PyLong_Type.tp_version_tag;
case JIT_SYM_SLOTS_TAG:
return sym->slots.type_version;
}
Py_UNREACHABLE();
}
Expand Down Expand Up @@ -655,6 +680,7 @@ _Py_uop_sym_truthiness(JitOptContext *ctx, JitOptRef ref)
case JIT_SYM_NON_NULL_TAG:
case JIT_SYM_UNKNOWN_TAG:
case JIT_SYM_COMPACT_INT:
case JIT_SYM_SLOTS_TAG:
return -1;
case JIT_SYM_KNOWN_CLASS_TAG:
/* TODO :
Expand Down Expand Up @@ -811,6 +837,7 @@ _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef ref)
return;
case JIT_SYM_TUPLE_TAG:
case JIT_SYM_TRUTHINESS_TAG:
case JIT_SYM_SLOTS_TAG:
sym_set_bottom(ctx, sym);
return;
case JIT_SYM_BOTTOM_TAG:
Expand Down Expand Up @@ -859,6 +886,97 @@ _Py_uop_sym_new_compact_int(JitOptContext *ctx)
return PyJitRef_Wrap(sym);
}

JitOptRef
_Py_uop_sym_new_slots_object(JitOptContext *ctx, unsigned int type_version)
{
JitOptSymbol *res = sym_new(ctx);
if (res == NULL) {
return out_of_space_ref(ctx);
}
res->tag = JIT_SYM_SLOTS_TAG;
res->slots.num_slots = 0;
res->slots.slots = NULL;
res->slots.type_version = type_version;
return PyJitRef_Wrap(res);
}

JitOptRef
_Py_uop_sym_slots_getattr(JitOptContext *ctx, JitOptRef ref, uint16_t slot_index)
{
JitOptSymbol *sym = PyJitRef_Unwrap(ref);

if (sym->tag == JIT_SYM_SLOTS_TAG && sym->slots.slots != NULL) {
for (int i = 0; i < sym->slots.num_slots; i++) {
if (sym->slots.slots[i].slot_index == slot_index) {
return PyJitRef_Wrap(allocation_base(ctx) + sym->slots.slots[i].symbol);
}
}
}

return _Py_uop_sym_new_not_null(ctx);
}

static JitOptSlotMapping *
slots_arena_alloc(JitOptContext *ctx)
{
if (ctx->s_arena.slots_curr_number + MAX_SYMBOLIC_SLOTS_SIZE > ctx->s_arena.slots_max_number) {
return NULL;
}
JitOptSlotMapping *slots = &ctx->s_arena.arena[ctx->s_arena.slots_curr_number];
ctx->s_arena.slots_curr_number += MAX_SYMBOLIC_SLOTS_SIZE;
return slots;
}

void
_Py_uop_sym_slots_setattr(JitOptContext *ctx, JitOptRef ref, uint16_t slot_index, JitOptRef value)
{
JitOptSymbol *sym = PyJitRef_Unwrap(ref);

if (sym->tag == JIT_SYM_TYPE_VERSION_TAG) {
uint32_t version = sym->version.version;
sym->tag = JIT_SYM_SLOTS_TAG;
sym->slots.type_version = version;
sym->slots.num_slots = 0;
sym->slots.slots = slots_arena_alloc(ctx);
if (sym->slots.slots == NULL) {
return;
}
}
else if (sym->tag == JIT_SYM_KNOWN_CLASS_TAG) {
uint32_t version = sym->cls.version;
sym->tag = JIT_SYM_SLOTS_TAG;
sym->slots.type_version = version;
sym->slots.num_slots = 0;
sym->slots.slots = slots_arena_alloc(ctx);
if (sym->slots.slots == NULL) {
return;
}
}
Comment thread
cocolato marked this conversation as resolved.
Outdated
else if (sym->tag != JIT_SYM_SLOTS_TAG) {
return;
}
// Check if have arena space allocated
if (sym->slots.slots == NULL) {
sym->slots.slots = slots_arena_alloc(ctx);
if (sym->slots.slots == NULL) {
return;
}
}
// Check if the slot already exists
for (int i = 0; i < sym->slots.num_slots; i++) {
if (sym->slots.slots[i].slot_index == slot_index) {
sym->slots.slots[i].symbol = (uint16_t)(PyJitRef_Unwrap(value) - allocation_base(ctx));
return;
}
}
// Add new mapping if there's space
if (sym->slots.num_slots < MAX_SYMBOLIC_SLOTS_SIZE) {
int idx = sym->slots.num_slots++;
sym->slots.slots[idx].slot_index = slot_index;
sym->slots.slots[idx].symbol = (uint16_t)(PyJitRef_Unwrap(value) - allocation_base(ctx));
}
}

// 0 on success, -1 on error.
_Py_UOpsAbstractFrame *
_Py_uop_frame_new(
Expand Down Expand Up @@ -948,6 +1066,10 @@ _Py_uop_abstractcontext_init(JitOptContext *ctx)
ctx->t_arena.ty_curr_number = 0;
ctx->t_arena.ty_max_number = TY_ARENA_SIZE;

// Setup the arena for slot mappings.
ctx->s_arena.slots_curr_number = 0;
ctx->s_arena.slots_max_number = SLOTS_ARENA_SIZE;

// Frame setup
ctx->curr_frame_depth = 0;

Expand Down Expand Up @@ -1186,6 +1308,33 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
TEST_PREDICATE(_Py_uop_sym_matches_type(ref_int, &PyLong_Type), "43 is not an int");
TEST_PREDICATE(_Py_uop_sym_get_const(ctx, ref_int) == val_43, "43 isn't 43");

JitOptRef slots_obj = _Py_uop_sym_new_slots_object(ctx, 42);
TEST_PREDICATE(!_Py_uop_sym_is_null(slots_obj), "slots object is NULL");
TEST_PREDICATE(_Py_uop_sym_is_not_null(slots_obj), "slots object is not not-null");
TEST_PREDICATE(_Py_uop_sym_get_type_version(slots_obj) == 42,
"slots object has wrong type version");

JitOptRef slot_val = _Py_uop_sym_new_const(ctx, val_42);
_Py_uop_sym_slots_setattr(ctx, slots_obj, 0, slot_val);
JitOptRef retrieved = _Py_uop_sym_slots_getattr(ctx, slots_obj, 0);
TEST_PREDICATE(_Py_uop_sym_get_const(ctx, retrieved) == val_42,
"slots getattr(0) didn't return val_42");

JitOptRef missing = _Py_uop_sym_slots_getattr(ctx, slots_obj, 99);
TEST_PREDICATE(_Py_uop_sym_is_not_null(missing), "missing slot is not not-null");
TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, missing), "missing slot is const");

JitOptRef slot_val2 = _Py_uop_sym_new_const(ctx, val_43);
_Py_uop_sym_slots_setattr(ctx, slots_obj, 0, slot_val2);
retrieved = _Py_uop_sym_slots_getattr(ctx, slots_obj, 0);
TEST_PREDICATE(_Py_uop_sym_get_const(ctx, retrieved) == val_43,
"slots getattr(0) didn't return val_43 after update");

JitOptRef slots_obj2 = _Py_uop_sym_new_slots_object(ctx, 42);
_Py_uop_sym_set_type_version(ctx, slots_obj2, 43);
TEST_PREDICATE(_Py_uop_sym_is_bottom(slots_obj2),
"slots object with wrong type version isn't bottom");

_Py_uop_abstractcontext_fini(ctx);
Py_DECREF(val_42);
Py_DECREF(val_43);
Expand Down
Loading