@@ -3444,6 +3444,34 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
34443444 }
34453445 }
34463446
3447+ TARGET (LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE ) {
3448+ assert (cframe .use_tracing == 0 );
3449+ PyObject * owner = GETLOCAL (oparg ); // borrowed
3450+ if (owner == NULL ) {
3451+ goto unbound_local_error ;
3452+ }
3453+ // GET_CACHE(), but for the following opcode
3454+ assert (_Py_OPCODE (* next_instr ) == LOAD_ATTR_INSTANCE_VALUE );
3455+ SpecializedCacheEntry * caches = _GetSpecializedCacheEntryForInstruction (
3456+ first_instr , INSTR_OFFSET () + 1 , _Py_OPARG (* next_instr ));
3457+ _PyAdaptiveEntry * cache0 = & caches [0 ].adaptive ;
3458+ assert (cache0 -> version != 0 );
3459+ PyTypeObject * tp = Py_TYPE (owner );
3460+ // These DEOPT_IF miss branches do PUSH(Py_NewRef(owner)).
3461+ DEOPT_IF (tp -> tp_version_tag != cache0 -> version ,
3462+ LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE );
3463+ assert (tp -> tp_dictoffset < 0 );
3464+ assert (tp -> tp_flags & Py_TPFLAGS_MANAGED_DICT );
3465+ PyDictValues * values = * _PyObject_ValuesPointer (owner );
3466+ DEOPT_IF (values == NULL , LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE );
3467+ PyObject * res = values -> values [cache0 -> index ];
3468+ DEOPT_IF (res == NULL , LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE );
3469+ STAT_INC (LOAD_ATTR , hit );
3470+ PUSH (Py_NewRef (res ));
3471+ next_instr ++ ;
3472+ NOTRACE_DISPATCH ();
3473+ }
3474+
34473475 TARGET (LOAD_ATTR_INSTANCE_VALUE ) {
34483476 assert (cframe .use_tracing == 0 );
34493477 PyObject * owner = TOP ();
@@ -3452,13 +3480,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
34523480 SpecializedCacheEntry * caches = GET_CACHE ();
34533481 _PyAdaptiveEntry * cache0 = & caches [0 ].adaptive ;
34543482 assert (cache0 -> version != 0 );
3455- DEOPT_IF (tp -> tp_version_tag != cache0 -> version , LOAD_ATTR );
3483+ DEOPT_IF (tp -> tp_version_tag != cache0 -> version , LOAD_ATTR_INSTANCE_VALUE );
34563484 assert (tp -> tp_dictoffset < 0 );
34573485 assert (tp -> tp_flags & Py_TPFLAGS_MANAGED_DICT );
34583486 PyDictValues * values = * _PyObject_ValuesPointer (owner );
3459- DEOPT_IF (values == NULL , LOAD_ATTR );
3487+ DEOPT_IF (values == NULL , LOAD_ATTR_INSTANCE_VALUE );
34603488 res = values -> values [cache0 -> index ];
3461- DEOPT_IF (res == NULL , LOAD_ATTR );
3489+ DEOPT_IF (res == NULL , LOAD_ATTR_INSTANCE_VALUE );
34623490 STAT_INC (LOAD_ATTR , hit );
34633491 Py_INCREF (res );
34643492 SET_TOP (res );
@@ -5515,6 +5543,52 @@ MISS_WITH_CACHE(BINARY_SUBSCR)
55155543MISS_WITH_CACHE (UNPACK_SEQUENCE )
55165544MISS_WITH_OPARG_COUNTER (STORE_SUBSCR )
55175545
5546+ LOAD_ATTR_INSTANCE_VALUE_miss :
5547+ {
5548+ // Special-cased so that if LOAD_ATTR_INSTANCE_VALUE
5549+ // gets replaced, then any preceeding
5550+ // LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE gets replaced as well
5551+ STAT_INC (LOAD_ATTR_INSTANCE_VALUE , miss );
5552+ STAT_INC (LOAD_ATTR , miss );
5553+ _PyAdaptiveEntry * cache = & GET_CACHE ()-> adaptive ;
5554+ cache -> counter -- ;
5555+ if (cache -> counter == 0 ) {
5556+ next_instr [-1 ] = _Py_MAKECODEUNIT (LOAD_ATTR_ADAPTIVE , _Py_OPARG (next_instr [-1 ]));
5557+ if (_Py_OPCODE (next_instr [-2 ]) == LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE ) {
5558+ next_instr [-2 ] = _Py_MAKECODEUNIT (LOAD_FAST , _Py_OPARG (next_instr [-2 ]));
5559+ if (_Py_OPCODE (next_instr [-3 ]) == LOAD_FAST ) {
5560+ next_instr [-3 ] = _Py_MAKECODEUNIT (LOAD_FAST__LOAD_FAST , _Py_OPARG (next_instr [-3 ]));
5561+ }
5562+ }
5563+ STAT_INC (LOAD_ATTR , deopt );
5564+ cache_backoff (cache );
5565+ }
5566+ oparg = cache -> original_oparg ;
5567+ JUMP_TO_INSTRUCTION (LOAD_ATTR );
5568+ }
5569+
5570+ LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE_miss :
5571+ {
5572+ // This is special-cased because we have a superinstruction
5573+ // that includes a specialized instruction.
5574+ // If the specialized portion misses, carry out
5575+ // the first instruction, then perform a miss
5576+ // for the second instruction as usual.
5577+
5578+ // Do LOAD_FAST
5579+ {
5580+ PyObject * value = GETLOCAL (oparg );
5581+ assert (value != NULL ); // Already checked if unbound
5582+ Py_INCREF (value );
5583+ PUSH (value );
5584+ NEXTOPARG ();
5585+ next_instr ++ ;
5586+ }
5587+
5588+ // Now we are in the correct state for LOAD_ATTR
5589+ goto LOAD_ATTR_INSTANCE_VALUE_miss ;
5590+ }
5591+
55185592binary_subscr_dict_error :
55195593 {
55205594 PyObject * sub = POP ();
0 commit comments