mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-46823: Implement LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE superinstruction (GH-31484)
This commit is contained in:
		
							parent
							
								
									4fccf91073
								
							
						
					
					
						commit
						a52d2528a4
					
				
					 6 changed files with 91 additions and 4 deletions
				
			
		
							
								
								
									
										1
									
								
								Include/opcode.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Include/opcode.h
									
										
									
										generated
									
									
									
								
							|  | @ -181,6 +181,7 @@ extern "C" { | |||
| #define LOAD_FAST__LOAD_CONST           169 | ||||
| #define LOAD_CONST__LOAD_FAST           170 | ||||
| #define STORE_FAST__STORE_FAST          173 | ||||
| #define LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE 174 | ||||
| #define DO_TRACING                      255 | ||||
| #ifdef NEED_OPCODE_JUMP_TABLES | ||||
| static uint32_t _PyOpcode_RelativeJump[8] = { | ||||
|  |  | |||
|  | @ -295,6 +295,7 @@ def jabs_op(name, op): | |||
|     "LOAD_FAST__LOAD_CONST", | ||||
|     "LOAD_CONST__LOAD_FAST", | ||||
|     "STORE_FAST__STORE_FAST", | ||||
|     "LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE", | ||||
| ] | ||||
| _specialization_stats = [ | ||||
|     "success", | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| Implement a specialized combined opcode ``LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE``.  Patch by Dennis Sweeney. | ||||
|  | @ -3444,6 +3444,34 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         TARGET(LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE) { | ||||
|             assert(cframe.use_tracing == 0); | ||||
|             PyObject *owner = GETLOCAL(oparg); // borrowed
 | ||||
|             if (owner == NULL) { | ||||
|                 goto unbound_local_error; | ||||
|             } | ||||
|             // GET_CACHE(), but for the following opcode
 | ||||
|             assert(_Py_OPCODE(*next_instr) == LOAD_ATTR_INSTANCE_VALUE); | ||||
|             SpecializedCacheEntry *caches = _GetSpecializedCacheEntryForInstruction( | ||||
|                 first_instr, INSTR_OFFSET() + 1, _Py_OPARG(*next_instr)); | ||||
|             _PyAdaptiveEntry *cache0 = &caches[0].adaptive; | ||||
|             assert(cache0->version != 0); | ||||
|             PyTypeObject *tp = Py_TYPE(owner); | ||||
|             // These DEOPT_IF miss branches do PUSH(Py_NewRef(owner)).
 | ||||
|             DEOPT_IF(tp->tp_version_tag != cache0->version, | ||||
|                      LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE); | ||||
|             assert(tp->tp_dictoffset < 0); | ||||
|             assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); | ||||
|             PyDictValues *values = *_PyObject_ValuesPointer(owner); | ||||
|             DEOPT_IF(values == NULL, LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE); | ||||
|             PyObject *res = values->values[cache0->index]; | ||||
|             DEOPT_IF(res == NULL, LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE); | ||||
|             STAT_INC(LOAD_ATTR, hit); | ||||
|             PUSH(Py_NewRef(res)); | ||||
|             next_instr++; | ||||
|             NOTRACE_DISPATCH(); | ||||
|         } | ||||
| 
 | ||||
|         TARGET(LOAD_ATTR_INSTANCE_VALUE) { | ||||
|             assert(cframe.use_tracing == 0); | ||||
|             PyObject *owner = TOP(); | ||||
|  | @ -3452,13 +3480,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr | |||
|             SpecializedCacheEntry *caches = GET_CACHE(); | ||||
|             _PyAdaptiveEntry *cache0 = &caches[0].adaptive; | ||||
|             assert(cache0->version != 0); | ||||
|             DEOPT_IF(tp->tp_version_tag != cache0->version, LOAD_ATTR); | ||||
|             DEOPT_IF(tp->tp_version_tag != cache0->version, LOAD_ATTR_INSTANCE_VALUE); | ||||
|             assert(tp->tp_dictoffset < 0); | ||||
|             assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); | ||||
|             PyDictValues *values = *_PyObject_ValuesPointer(owner); | ||||
|             DEOPT_IF(values == NULL, LOAD_ATTR); | ||||
|             DEOPT_IF(values == NULL, LOAD_ATTR_INSTANCE_VALUE); | ||||
|             res = values->values[cache0->index]; | ||||
|             DEOPT_IF(res == NULL, LOAD_ATTR); | ||||
|             DEOPT_IF(res == NULL, LOAD_ATTR_INSTANCE_VALUE); | ||||
|             STAT_INC(LOAD_ATTR, hit); | ||||
|             Py_INCREF(res); | ||||
|             SET_TOP(res); | ||||
|  | @ -5515,6 +5543,52 @@ MISS_WITH_CACHE(BINARY_SUBSCR) | |||
| MISS_WITH_CACHE(UNPACK_SEQUENCE) | ||||
| MISS_WITH_OPARG_COUNTER(STORE_SUBSCR) | ||||
| 
 | ||||
| LOAD_ATTR_INSTANCE_VALUE_miss: | ||||
|         { | ||||
|             // Special-cased so that if LOAD_ATTR_INSTANCE_VALUE
 | ||||
|             // gets replaced, then any preceeding
 | ||||
|             // LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE gets replaced as well
 | ||||
|             STAT_INC(LOAD_ATTR_INSTANCE_VALUE, miss); | ||||
|             STAT_INC(LOAD_ATTR, miss); | ||||
|             _PyAdaptiveEntry *cache = &GET_CACHE()->adaptive; | ||||
|             cache->counter--; | ||||
|             if (cache->counter == 0) { | ||||
|                 next_instr[-1] = _Py_MAKECODEUNIT(LOAD_ATTR_ADAPTIVE, _Py_OPARG(next_instr[-1])); | ||||
|                 if (_Py_OPCODE(next_instr[-2]) == LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE) { | ||||
|                     next_instr[-2] = _Py_MAKECODEUNIT(LOAD_FAST, _Py_OPARG(next_instr[-2])); | ||||
|                     if (_Py_OPCODE(next_instr[-3]) == LOAD_FAST) { | ||||
|                         next_instr[-3] =  _Py_MAKECODEUNIT(LOAD_FAST__LOAD_FAST, _Py_OPARG(next_instr[-3])); | ||||
|                     } | ||||
|                 } | ||||
|                 STAT_INC(LOAD_ATTR, deopt); | ||||
|                 cache_backoff(cache); | ||||
|             } | ||||
|             oparg = cache->original_oparg; | ||||
|             JUMP_TO_INSTRUCTION(LOAD_ATTR); | ||||
|         } | ||||
| 
 | ||||
| LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE_miss: | ||||
|         { | ||||
|             // This is special-cased because we have a superinstruction
 | ||||
|             // that includes a specialized instruction.
 | ||||
|             // If the specialized portion misses, carry out
 | ||||
|             // the first instruction, then perform a miss
 | ||||
|             // for the second instruction as usual.
 | ||||
| 
 | ||||
|             // Do LOAD_FAST
 | ||||
|             { | ||||
|                 PyObject *value = GETLOCAL(oparg); | ||||
|                 assert(value != NULL); // Already checked if unbound
 | ||||
|                 Py_INCREF(value); | ||||
|                 PUSH(value); | ||||
|                 NEXTOPARG(); | ||||
|                 next_instr++; | ||||
|             } | ||||
| 
 | ||||
|             // Now we are in the correct state for LOAD_ATTR
 | ||||
|             goto LOAD_ATTR_INSTANCE_VALUE_miss; | ||||
|         } | ||||
| 
 | ||||
| binary_subscr_dict_error: | ||||
|         { | ||||
|             PyObject *sub = POP(); | ||||
|  |  | |||
							
								
								
									
										2
									
								
								Python/opcode_targets.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								Python/opcode_targets.h
									
										
									
										generated
									
									
									
								
							|  | @ -173,7 +173,7 @@ static void *opcode_targets[256] = { | |||
|     &&TARGET_CALL, | ||||
|     &&TARGET_KW_NAMES, | ||||
|     &&TARGET_STORE_FAST__STORE_FAST, | ||||
|     &&_unknown_opcode, | ||||
|     &&TARGET_LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE, | ||||
|     &&_unknown_opcode, | ||||
|     &&_unknown_opcode, | ||||
|     &&_unknown_opcode, | ||||
|  |  | |||
|  | @ -889,6 +889,16 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp | |||
|         return -1; | ||||
|     } | ||||
|     if (err) { | ||||
|         if (_Py_OPCODE(instr[0]) == LOAD_ATTR_INSTANCE_VALUE) { | ||||
|             // Note: instr[-1] exists because there's something on the stack,
 | ||||
|             // and instr[-2] exists because there's at least a RESUME as well.
 | ||||
|             if (_Py_OPCODE(instr[-1]) == LOAD_FAST) { | ||||
|                 instr[-1] = _Py_MAKECODEUNIT(LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE, _Py_OPARG(instr[-1])); | ||||
|                 if (_Py_OPCODE(instr[-2]) == LOAD_FAST__LOAD_FAST) { | ||||
|                     instr[-2] = _Py_MAKECODEUNIT(LOAD_FAST, _Py_OPARG(instr[-2])); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         goto success; | ||||
|     } | ||||
| fail: | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dennis Sweeney
						Dennis Sweeney