Skip to content

Commit dfd99d7

Browse files
committed
Add slots object property tracking for the Tier 2 JIT optimizer
1 parent 76b484b commit dfd99d7

File tree

6 files changed

+147
-9
lines changed

6 files changed

+147
-9
lines changed

Include/internal/pycore_optimizer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ extern JitOptRef _Py_uop_sym_new_truthiness(JitOptContext *ctx, JitOptRef value,
205205
extern bool _Py_uop_sym_is_compact_int(JitOptRef sym);
206206
extern JitOptRef _Py_uop_sym_new_compact_int(JitOptContext *ctx);
207207
extern void _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef sym);
208+
extern JitOptRef _Py_uop_sym_new_slots_object(JitOptContext *ctx, unsigned int type_version);
209+
extern JitOptRef _Py_uop_sym_slots_getattr(JitOptContext *ctx, JitOptRef ref, uint16_t slot_index);
210+
extern void _Py_uop_sym_slots_setattr(JitOptContext *ctx, JitOptRef ref, uint16_t slot_index, JitOptRef value);
208211

209212
extern void _Py_uop_abstractcontext_init(JitOptContext *ctx);
210213
extern void _Py_uop_abstractcontext_fini(JitOptContext *ctx);

Include/internal/pycore_optimizer_types.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ typedef enum _JitSymType {
4040
JIT_SYM_TUPLE_TAG = 8,
4141
JIT_SYM_TRUTHINESS_TAG = 9,
4242
JIT_SYM_COMPACT_INT = 10,
43+
JIT_SYM_SLOTS_TAG = 11,
4344
} JitSymType;
4445

4546
typedef struct _jit_opt_known_class {
@@ -76,6 +77,20 @@ typedef struct {
7677
uint8_t tag;
7778
} JitOptCompactInt;
7879

80+
#define MAX_SYMBOLIC_SLOTS_SIZE 4
81+
82+
typedef struct {
83+
uint16_t slot_index;
84+
uint16_t symbol;
85+
} JitOptSlotMapping;
86+
87+
typedef struct _jit_opt_slots {
88+
uint8_t tag;
89+
uint8_t num_slots;
90+
uint32_t type_version;
91+
JitOptSlotMapping slots[MAX_SYMBOLIC_SLOTS_SIZE];
92+
} JitOptSlotsObject;
93+
7994
typedef union _jit_opt_symbol {
8095
uint8_t tag;
8196
JitOptKnownClass cls;
@@ -84,6 +99,7 @@ typedef union _jit_opt_symbol {
8499
JitOptTuple tuple;
85100
JitOptTruthiness truthiness;
86101
JitOptCompactInt compact;
102+
JitOptSlotsObject slots;
87103
} JitOptSymbol;
88104

89105
// This mimics the _PyStackRef API

Python/optimizer_analysis.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ add_op(JitOptContext *ctx, _PyUOpInstruction *this_instr,
247247
#define sym_is_compact_int _Py_uop_sym_is_compact_int
248248
#define sym_new_compact_int _Py_uop_sym_new_compact_int
249249
#define sym_new_truthiness _Py_uop_sym_new_truthiness
250+
#define sym_new_slots_object _Py_uop_sym_new_slots_object
251+
#define sym_slots_getattr _Py_uop_sym_slots_getattr
252+
#define sym_slots_setattr _Py_uop_sym_slots_setattr
250253

251254
#define JUMP_TO_LABEL(label) goto label;
252255

Python/optimizer_bytecodes.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
3838
#define sym_new_compact_int _Py_uop_sym_new_compact_int
3939
#define sym_is_compact_int _Py_uop_sym_is_compact_int
4040
#define sym_new_truthiness _Py_uop_sym_new_truthiness
41+
#define sym_new_slots_object _Py_uop_sym_new_slots_object
42+
#define sym_slots_getattr _Py_uop_sym_slots_getattr
43+
#define sym_slots_setattr _Py_uop_sym_slots_setattr
4144

4245
extern int
4346
optimize_to_bool(
@@ -123,8 +126,7 @@ dummy_func(void) {
123126
}
124127

125128
op(_STORE_ATTR_SLOT, (index/1, value, owner -- o)) {
126-
(void)index;
127-
(void)value;
129+
sym_slots_setattr(ctx, owner, (uint16_t)index, value);
128130
o = owner;
129131
}
130132

@@ -709,8 +711,7 @@ dummy_func(void) {
709711
}
710712

711713
op(_LOAD_ATTR_SLOT, (index/1, owner -- attr, o)) {
712-
attr = sym_new_not_null(ctx);
713-
(void)index;
714+
attr = sym_slots_getattr(ctx, owner, (uint16_t)index);
714715
o = owner;
715716
}
716717

Python/optimizer_cases.c.h

Lines changed: 2 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer_symbols.c

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ _PyUOpSymPrint(JitOptRef ref)
113113
case JIT_SYM_COMPACT_INT:
114114
printf("<compact_int at %p>", (void *)sym);
115115
break;
116+
case JIT_SYM_SLOTS_TAG:
117+
printf("<slots[%d] v%u at %p>", sym->slots.num_slots, sym->slots.type_version, (void *)sym);
118+
break;
116119
default:
117120
printf("<tag=%d at %p>", sym->tag, (void *)sym);
118121
break;
@@ -319,6 +322,11 @@ _Py_uop_sym_set_type(JitOptContext *ctx, JitOptRef ref, PyTypeObject *typ)
319322
sym_set_bottom(ctx, sym);
320323
}
321324
return;
325+
case JIT_SYM_SLOTS_TAG:
326+
if (typ->tp_version_tag != sym->slots.type_version) {
327+
sym_set_bottom(ctx, sym);
328+
}
329+
return;
322330
}
323331
}
324332

@@ -382,6 +390,12 @@ _Py_uop_sym_set_type_version(JitOptContext *ctx, JitOptRef ref, unsigned int ver
382390
return false;
383391
}
384392
return true;
393+
case JIT_SYM_SLOTS_TAG:
394+
if (version != sym->slots.type_version) {
395+
sym_set_bottom(ctx, sym);
396+
return false;
397+
}
398+
return true;
385399
}
386400
Py_UNREACHABLE();
387401
}
@@ -474,6 +488,9 @@ _Py_uop_sym_set_const(JitOptContext *ctx, JitOptRef ref, PyObject *const_val)
474488
sym_set_bottom(ctx, sym);
475489
}
476490
return;
491+
case JIT_SYM_SLOTS_TAG:
492+
sym_set_bottom(ctx, sym);
493+
return;
477494
}
478495
}
479496

@@ -593,7 +610,8 @@ _Py_uop_sym_get_type(JitOptRef ref)
593610
return &PyBool_Type;
594611
case JIT_SYM_COMPACT_INT:
595612
return &PyLong_Type;
596-
613+
case JIT_SYM_SLOTS_TAG:
614+
return _PyType_LookupByVersion(sym->slots.type_version);
597615
}
598616
Py_UNREACHABLE();
599617
}
@@ -621,6 +639,8 @@ _Py_uop_sym_get_type_version(JitOptRef ref)
621639
return PyBool_Type.tp_version_tag;
622640
case JIT_SYM_COMPACT_INT:
623641
return PyLong_Type.tp_version_tag;
642+
case JIT_SYM_SLOTS_TAG:
643+
return sym->slots.type_version;
624644
}
625645
Py_UNREACHABLE();
626646
}
@@ -655,6 +675,7 @@ _Py_uop_sym_truthiness(JitOptContext *ctx, JitOptRef ref)
655675
case JIT_SYM_NON_NULL_TAG:
656676
case JIT_SYM_UNKNOWN_TAG:
657677
case JIT_SYM_COMPACT_INT:
678+
case JIT_SYM_SLOTS_TAG:
658679
return -1;
659680
case JIT_SYM_KNOWN_CLASS_TAG:
660681
/* TODO :
@@ -811,6 +832,7 @@ _Py_uop_sym_set_compact_int(JitOptContext *ctx, JitOptRef ref)
811832
return;
812833
case JIT_SYM_TUPLE_TAG:
813834
case JIT_SYM_TRUTHINESS_TAG:
835+
case JIT_SYM_SLOTS_TAG:
814836
sym_set_bottom(ctx, sym);
815837
return;
816838
case JIT_SYM_BOTTOM_TAG:
@@ -859,6 +881,74 @@ _Py_uop_sym_new_compact_int(JitOptContext *ctx)
859881
return PyJitRef_Wrap(sym);
860882
}
861883

884+
JitOptRef
885+
_Py_uop_sym_new_slots_object(JitOptContext *ctx, unsigned int type_version)
886+
{
887+
JitOptSymbol *res = sym_new(ctx);
888+
if (res == NULL) {
889+
return out_of_space_ref(ctx);
890+
}
891+
res->tag = JIT_SYM_SLOTS_TAG;
892+
res->slots.num_slots = 0;
893+
res->slots.type_version = type_version;
894+
return PyJitRef_Wrap(res);
895+
}
896+
897+
JitOptRef
898+
_Py_uop_sym_slots_getattr(JitOptContext *ctx, JitOptRef ref, uint16_t slot_index)
899+
{
900+
JitOptSymbol *sym = PyJitRef_Unwrap(ref);
901+
902+
if (sym->tag == JIT_SYM_SLOTS_TAG) {
903+
// Linear search through the mapping array
904+
for (int i = 0; i < sym->slots.num_slots; i++) {
905+
if (sym->slots.slots[i].slot_index == slot_index) {
906+
return PyJitRef_Wrap(allocation_base(ctx) + sym->slots.slots[i].symbol);
907+
}
908+
}
909+
}
910+
911+
// Not found, return not_null
912+
return _Py_uop_sym_new_not_null(ctx);
913+
}
914+
915+
void
916+
_Py_uop_sym_slots_setattr(JitOptContext *ctx, JitOptRef ref, uint16_t slot_index, JitOptRef value)
917+
{
918+
JitOptSymbol *sym = PyJitRef_Unwrap(ref);
919+
920+
if (sym->tag == JIT_SYM_TYPE_VERSION_TAG) {
921+
uint32_t version = sym->version.version;
922+
sym->tag = JIT_SYM_SLOTS_TAG;
923+
sym->slots.type_version = version;
924+
sym->slots.num_slots = 0;
925+
}
926+
else if (sym->tag == JIT_SYM_KNOWN_CLASS_TAG) {
927+
uint32_t version = sym->cls.version;
928+
sym->tag = JIT_SYM_SLOTS_TAG;
929+
sym->slots.type_version = version;
930+
sym->slots.num_slots = 0;
931+
}
932+
else if (sym->tag != JIT_SYM_SLOTS_TAG) {
933+
return;
934+
}
935+
936+
if (sym->slots.num_slots >= MAX_SYMBOLIC_SLOTS_SIZE) {
937+
return;
938+
}
939+
940+
for (int i = 0; i < sym->slots.num_slots; i++) {
941+
if (sym->slots.slots[i].slot_index == slot_index) {
942+
sym->slots.slots[i].symbol = (uint16_t)(PyJitRef_Unwrap(value) - allocation_base(ctx));
943+
return;
944+
}
945+
}
946+
947+
int idx = sym->slots.num_slots++;
948+
sym->slots.slots[idx].slot_index = slot_index;
949+
sym->slots.slots[idx].symbol = (uint16_t)(PyJitRef_Unwrap(value) - allocation_base(ctx));
950+
}
951+
862952
// 0 on success, -1 on error.
863953
_Py_UOpsAbstractFrame *
864954
_Py_uop_frame_new(
@@ -1186,6 +1276,33 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
11861276
TEST_PREDICATE(_Py_uop_sym_matches_type(ref_int, &PyLong_Type), "43 is not an int");
11871277
TEST_PREDICATE(_Py_uop_sym_get_const(ctx, ref_int) == val_43, "43 isn't 43");
11881278

1279+
JitOptRef slots_obj = _Py_uop_sym_new_slots_object(ctx, 42);
1280+
TEST_PREDICATE(!_Py_uop_sym_is_null(slots_obj), "slots object is NULL");
1281+
TEST_PREDICATE(_Py_uop_sym_is_not_null(slots_obj), "slots object is not not-null");
1282+
TEST_PREDICATE(_Py_uop_sym_get_type_version(slots_obj) == 42,
1283+
"slots object has wrong type version");
1284+
1285+
JitOptRef slot_val = _Py_uop_sym_new_const(ctx, val_42);
1286+
_Py_uop_sym_slots_setattr(ctx, slots_obj, 0, slot_val);
1287+
JitOptRef retrieved = _Py_uop_sym_slots_getattr(ctx, slots_obj, 0);
1288+
TEST_PREDICATE(_Py_uop_sym_get_const(ctx, retrieved) == val_42,
1289+
"slots getattr(0) didn't return val_42");
1290+
1291+
JitOptRef missing = _Py_uop_sym_slots_getattr(ctx, slots_obj, 99);
1292+
TEST_PREDICATE(_Py_uop_sym_is_not_null(missing), "missing slot is not not-null");
1293+
TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, missing), "missing slot is const");
1294+
1295+
JitOptRef slot_val2 = _Py_uop_sym_new_const(ctx, val_43);
1296+
_Py_uop_sym_slots_setattr(ctx, slots_obj, 0, slot_val2);
1297+
retrieved = _Py_uop_sym_slots_getattr(ctx, slots_obj, 0);
1298+
TEST_PREDICATE(_Py_uop_sym_get_const(ctx, retrieved) == val_43,
1299+
"slots getattr(0) didn't return val_43 after update");
1300+
1301+
JitOptRef slots_obj2 = _Py_uop_sym_new_slots_object(ctx, 42);
1302+
_Py_uop_sym_set_type_version(ctx, slots_obj2, 43);
1303+
TEST_PREDICATE(_Py_uop_sym_is_bottom(slots_obj2),
1304+
"slots object with wrong type version isn't bottom");
1305+
11891306
_Py_uop_abstractcontext_fini(ctx);
11901307
Py_DECREF(val_42);
11911308
Py_DECREF(val_43);

0 commit comments

Comments
 (0)