Skip to content

Commit c2c1121

Browse files
jbower-fbmeta-codesync[bot]
authored andcommitted
Up/downgrade SplitMutator inline cache accessors as known values change
Summary: It's possible to get back to the containing `AttributeMutator` from a `SpltMutator` pointer as one contains the other, and there are also a few slots remaining in the 3-bit space of the `AttributeMutator::Kind`. Using this we can very cheaply add in some slightly more specialized behaviors when getting/setting instance attributes. Reviewed By: DinoV Differential Revision: D85970948 fbshipit-source-id: f893a503f814a9f4e2523c79d6d529fb293aa926
1 parent c424950 commit c2c1121

2 files changed

Lines changed: 110 additions & 17 deletions

File tree

cinderx/Jit/inline_cache.cpp

Lines changed: 98 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ void maybeCollectCacheStats(
149149

150150
} // namespace
151151

152+
void AttributeMutator::changeKindFromSplitInline(
153+
SplitMutator* split,
154+
Kind new_kind) {
155+
AttributeMutator* mutator = reinterpret_cast<AttributeMutator*>(
156+
reinterpret_cast<uintptr_t>(split) - offsetof(AttributeMutator, split_));
157+
mutator->type_ = reinterpret_cast<uintptr_t>(mutator->type()) |
158+
static_cast<uintptr_t>(new_kind);
159+
}
160+
152161
PyDictKeysObject* getSplitKeys(BorrowedRef<PyTypeObject> type) {
153162
assert(PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE));
154163
PyHeapTypeObject* ht = reinterpret_cast<PyHeapTypeObject*>(type.get());
@@ -195,8 +204,19 @@ PyObject* SplitMutator::getAttrInline(PyObject* obj, PyObject* name) {
195204
if (!ensureValueOffset(name)) {
196205
return PyObject_GetAttr(obj, name);
197206
}
207+
AttributeMutator::changeKindFromSplitInline(
208+
this, AttributeMutator::Kind::kSplitInlineKnownOffset);
209+
return getAttrInlineKnownOffset(obj, name);
210+
}
211+
212+
PyObject* SplitMutator::getAttrInlineKnownOffset(
213+
PyObject* obj,
214+
PyObject* name) {
198215
PyDictValues* values = _PyObject_InlineValues(obj);
199216
if (!values->valid) {
217+
// Downgrade to the slightly slower path in future
218+
AttributeMutator::changeKindFromSplitInline(
219+
this, AttributeMutator::Kind::kSplitKnownOffset);
200220
return getAttr(obj, name);
201221
}
202222
PyObject* result = values->values[val_offset];
@@ -206,6 +226,24 @@ PyObject* SplitMutator::getAttrInline(PyObject* obj, PyObject* name) {
206226
return Py_NewRef(result);
207227
}
208228

229+
PyObject* SplitMutator::getAttrSlowPath(
230+
PyObject* obj,
231+
PyObject* name,
232+
BorrowedRef<PyDictObject> dict) {
233+
PyObject* attr_o;
234+
int res = [&] {
235+
auto strong_ref = Ref<>::create(dict);
236+
return PyDict_GetItemRef(dict, name, &attr_o);
237+
}();
238+
if (res == 0) {
239+
return raise_attribute_error(obj, name);
240+
}
241+
if (res == -1) {
242+
return nullptr;
243+
}
244+
return attr_o;
245+
}
246+
209247
PyObject* SplitMutator::getAttr(PyObject* obj, PyObject* name) {
210248
BorrowedRef<PyDictObject> dict = _PyObject_GetManagedDict(obj);
211249

@@ -215,20 +253,25 @@ PyObject* SplitMutator::getAttr(PyObject* obj, PyObject* name) {
215253
if (dict == nullptr) {
216254
return PyObject_GetAttr(obj, name);
217255
}
218-
if (!ensureValueOffset(name) || dict->ma_keys != keys) {
219-
// Slow path
220-
PyObject* attr_o;
221-
int res = [&] {
222-
auto strong_ref = Ref<>::create(dict);
223-
return PyDict_GetItemRef(dict, name, &attr_o);
224-
}();
225-
if (res == 0) {
226-
return raise_attribute_error(obj, name);
227-
}
228-
if (res == -1) {
229-
return nullptr;
230-
}
231-
return attr_o;
256+
if (!ensureValueOffset(name)) {
257+
return getAttrSlowPath(obj, name, dict);
258+
}
259+
AttributeMutator::changeKindFromSplitInline(
260+
this, AttributeMutator::Kind::kSplitKnownOffset);
261+
return getAttrKnownOffset(obj, name);
262+
}
263+
264+
PyObject* SplitMutator::getAttrKnownOffset(PyObject* obj, PyObject* name) {
265+
BorrowedRef<PyDictObject> dict = _PyObject_GetManagedDict(obj);
266+
267+
JIT_DCHECK(
268+
PyDict_Check(dict), "Expected dict, got {}", Py_TYPE(dict)->tp_name);
269+
270+
if (dict == nullptr) {
271+
return PyObject_GetAttr(obj, name);
272+
}
273+
if (dict->ma_keys != keys) {
274+
return getAttrSlowPath(obj, name, dict);
232275
}
233276
JIT_DCHECK(
234277
DK_IS_UNICODE(keys) && val_offset < keys->dk_nentries,
@@ -247,9 +290,21 @@ int SplitMutator::setAttrInline(
247290
if (!ensureValueOffset(name)) {
248291
return PyObject_SetAttr(obj, name, value);
249292
}
293+
AttributeMutator::changeKindFromSplitInline(
294+
this, AttributeMutator::Kind::kSplitInlineKnownOffset);
295+
return setAttrInlineKnownOffset(obj, name, value);
296+
}
297+
298+
int SplitMutator::setAttrInlineKnownOffset(
299+
PyObject* obj,
300+
PyObject* name,
301+
PyObject* value) {
250302
PyDictValues* values = _PyObject_InlineValues(obj);
251303
PyDictObject* dict = _PyObject_GetManagedDict(obj);
252304
if (!values->valid || dict) {
305+
// Downgrade to the slightly slower path in future
306+
AttributeMutator::changeKindFromSplitInline(
307+
this, AttributeMutator::Kind::kSplitKnownOffset);
253308
return setAttr(obj, name, value);
254309
}
255310
auto old_value = Ref<>::steal(values->values[val_offset]);
@@ -264,6 +319,15 @@ int SplitMutator::setAttr(PyObject* obj, PyObject* name, PyObject* value) {
264319
if (!ensureValueOffset(name)) {
265320
return PyObject_SetAttr(obj, name, value);
266321
}
322+
AttributeMutator::changeKindFromSplitInline(
323+
this, AttributeMutator::Kind::kSplitKnownOffset);
324+
return setAttrKnownOffset(obj, name, value);
325+
}
326+
327+
int SplitMutator::setAttrKnownOffset(
328+
PyObject* obj,
329+
PyObject* name,
330+
PyObject* value) {
267331
BorrowedRef<PyDictObject> dict = _PyObject_GetManagedDict(obj);
268332
if (dict == nullptr) {
269333
return PyObject_SetAttr(obj, name, value);
@@ -522,11 +586,11 @@ PyTypeObject* AttributeMutator::type() const {
522586
}
523587

524588
void AttributeMutator::reset() {
525-
set_type(nullptr, Kind::kEmpty);
589+
type_ = 0;
526590
}
527591

528592
bool AttributeMutator::isEmpty() const {
529-
return get_kind() == Kind::kEmpty;
593+
return type_ == 0;
530594
}
531595

532596
void AttributeMutator::set_combined(PyTypeObject* type) {
@@ -576,13 +640,22 @@ void AttributeMutator::set_split(
576640

577641
inline int
578642
AttributeMutator::setAttr(PyObject* obj, PyObject* name, PyObject* value) {
643+
JIT_CHECK(
644+
!isEmpty(),
645+
"Empty attribute mutator setting field {} on object of type {}",
646+
repr(name),
647+
Py_TYPE(obj)->tp_name);
579648
AttributeMutator::Kind kind = get_kind();
580649
switch (kind) {
581650
case AttributeMutator::Kind::kSplit:
582651
return split_.setAttr(obj, name, value);
583652
#if PY_VERSION_HEX >= 0x030E0000
653+
case AttributeMutator::Kind::kSplitKnownOffset:
654+
return split_.setAttrKnownOffset(obj, name, value);
584655
case AttributeMutator::Kind::kSplitInline:
585656
return split_.setAttrInline(obj, name, value);
657+
case AttributeMutator::Kind::kSplitInlineKnownOffset:
658+
return split_.setAttrInlineKnownOffset(obj, name, value);
586659
#endif
587660
case AttributeMutator::Kind::kCombined:
588661
return combined_.setAttr(obj, name, value);
@@ -599,13 +672,22 @@ AttributeMutator::setAttr(PyObject* obj, PyObject* name, PyObject* value) {
599672
}
600673

601674
inline PyObject* AttributeMutator::getAttr(PyObject* obj, PyObject* name) {
675+
JIT_CHECK(
676+
!isEmpty(),
677+
"Empty attribute mutator getting field {} on object of type {}",
678+
repr(name),
679+
Py_TYPE(obj)->tp_name);
602680
AttributeMutator::Kind kind = get_kind();
603681
switch (kind) {
604682
case AttributeMutator::Kind::kSplit:
605683
return split_.getAttr(obj, name);
606684
#if PY_VERSION_HEX >= 0x030E0000
685+
case AttributeMutator::Kind::kSplitKnownOffset:
686+
return split_.getAttrKnownOffset(obj, name);
607687
case AttributeMutator::Kind::kSplitInline:
608688
return split_.getAttrInline(obj, name);
689+
case AttributeMutator::Kind::kSplitInlineKnownOffset:
690+
return split_.getAttrInlineKnownOffset(obj, name);
609691
#endif
610692
case AttributeMutator::Kind::kCombined:
611693
return combined_.getAttr(obj, name);

cinderx/Jit/inline_cache.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,16 @@ struct SplitMutator {
2323
PyObject* getAttr(PyObject* obj, PyObject* name);
2424
int setAttr(PyObject* obj, PyObject* name, PyObject* value);
2525
#if PY_VERSION_HEX >= 0x030E0000
26+
PyObject* getAttrKnownOffset(PyObject* obj, PyObject* name);
27+
int setAttrKnownOffset(PyObject* obj, PyObject* name, PyObject* value);
2628
PyObject* getAttrInline(PyObject* obj, PyObject* name);
29+
PyObject* getAttrSlowPath(
30+
PyObject* obj,
31+
PyObject* name,
32+
BorrowedRef<PyDictObject> dict);
2733
int setAttrInline(PyObject* obj, PyObject* name, PyObject* value);
34+
PyObject* getAttrInlineKnownOffset(PyObject* obj, PyObject* name);
35+
int setAttrInlineKnownOffset(PyObject* obj, PyObject* name, PyObject* value);
2836
#endif
2937
bool canInsertToSplitDict(BorrowedRef<PyDictObject> dict, BorrowedRef<> name);
3038
bool ensureValueOffset(BorrowedRef<> name);
@@ -78,9 +86,10 @@ class AttributeMutator {
7886
// Kind enum is designed to fit within 3 bits and it's value is embedded into
7987
// the type_ pointer
8088
enum class Kind : uint8_t {
81-
kEmpty,
8289
kSplit,
90+
kSplitKnownOffset,
8391
kSplitInline,
92+
kSplitInlineKnownOffset,
8493
kCombined,
8594
kDataDescr,
8695
kMemberDescr,
@@ -109,6 +118,8 @@ class AttributeMutator {
109118
PyObject* getAttr(PyObject* obj, PyObject* name);
110119
int setAttr(PyObject* obj, PyObject* name, PyObject* value);
111120

121+
static void changeKindFromSplitInline(SplitMutator* split, Kind new_kind);
122+
112123
private:
113124
void set_type(PyTypeObject* type, Kind kind);
114125
Kind get_kind() const;

0 commit comments

Comments
 (0)