@@ -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+
152161PyDictKeysObject* 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+
209247PyObject* 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
524588void AttributeMutator::reset () {
525- set_type ( nullptr , Kind:: kEmpty ) ;
589+ type_ = 0 ;
526590}
527591
528592bool AttributeMutator::isEmpty () const {
529- return get_kind () == Kind:: kEmpty ;
593+ return type_ == 0 ;
530594}
531595
532596void AttributeMutator::set_combined (PyTypeObject* type) {
@@ -576,13 +640,22 @@ void AttributeMutator::set_split(
576640
577641inline int
578642AttributeMutator::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
601674inline 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);
0 commit comments