mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	GH-118093: Add tier two support for LOAD_ATTR_PROPERTY (GH-122283)
This commit is contained in:
		
							parent
							
								
									5e686ff57d
								
							
						
					
					
						commit
						5f6001130f
					
				
					 9 changed files with 168 additions and 99 deletions
				
			
		|  | @ -2243,32 +2243,30 @@ dummy_func( | |||
|             unused/2 + | ||||
|             _LOAD_ATTR_CLASS; | ||||
| 
 | ||||
|         inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused, unused if (0))) { | ||||
|             PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); | ||||
| 
 | ||||
|         op(_LOAD_ATTR_PROPERTY_FRAME, (fget/4, owner -- new_frame: _PyInterpreterFrame *)) { | ||||
|             assert((oparg & 1) == 0); | ||||
|             DEOPT_IF(tstate->interp->eval_frame); | ||||
| 
 | ||||
|             PyTypeObject *cls = Py_TYPE(owner_o); | ||||
|             assert(type_version != 0); | ||||
|             DEOPT_IF(cls->tp_version_tag != type_version); | ||||
|             assert(Py_IS_TYPE(fget, &PyFunction_Type)); | ||||
|             PyFunctionObject *f = (PyFunctionObject *)fget; | ||||
|             assert(func_version != 0); | ||||
|             DEOPT_IF(f->func_version != func_version); | ||||
|             PyCodeObject *code = (PyCodeObject *)f->func_code; | ||||
|             assert(code->co_argcount == 1); | ||||
|             DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED); | ||||
|             DEOPT_IF(code->co_kwonlyargcount); | ||||
|             DEOPT_IF(code->co_argcount != 1); | ||||
|             DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); | ||||
|             STAT_INC(LOAD_ATTR, hit); | ||||
|             Py_INCREF(fget); | ||||
|             _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); | ||||
|             // Manipulate stack directly because we exit with DISPATCH_INLINED().
 | ||||
|             STACK_SHRINK(1); | ||||
|             new_frame = _PyFrame_PushUnchecked(tstate, f, 1); | ||||
|             new_frame->localsplus[0] = owner; | ||||
|             frame->return_offset = (uint16_t)(next_instr - this_instr); | ||||
|             DISPATCH_INLINED(new_frame); | ||||
|         } | ||||
| 
 | ||||
|         macro(LOAD_ATTR_PROPERTY) = | ||||
|             unused/1 + | ||||
|             _CHECK_PEP_523 + | ||||
|             _GUARD_TYPE_VERSION + | ||||
|             unused/2 + | ||||
|             _LOAD_ATTR_PROPERTY_FRAME + | ||||
|             _SAVE_RETURN_OFFSET + | ||||
|             _PUSH_FRAME; | ||||
| 
 | ||||
|         inst(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, (unused/1, type_version/2, func_version/2, getattribute/4, owner -- unused, unused if (0))) { | ||||
|             PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										34
									
								
								Python/executor_cases.c.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										34
									
								
								Python/executor_cases.c.h
									
										
									
										generated
									
									
									
								
							|  | @ -2507,7 +2507,39 @@ | |||
| 
 | ||||
|         /* _LOAD_ATTR_CLASS is split on (oparg & 1) */ | ||||
| 
 | ||||
|         /* _LOAD_ATTR_PROPERTY is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ | ||||
|         case _LOAD_ATTR_PROPERTY_FRAME: { | ||||
|             _PyStackRef owner; | ||||
|             _PyInterpreterFrame *new_frame; | ||||
|             oparg = CURRENT_OPARG(); | ||||
|             owner = stack_pointer[-1]; | ||||
|             PyObject *fget = (PyObject *)CURRENT_OPERAND(); | ||||
|             assert((oparg & 1) == 0); | ||||
|             assert(Py_IS_TYPE(fget, &PyFunction_Type)); | ||||
|             PyFunctionObject *f = (PyFunctionObject *)fget; | ||||
|             PyCodeObject *code = (PyCodeObject *)f->func_code; | ||||
|             if ((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED) { | ||||
|                 UOP_STAT_INC(uopcode, miss); | ||||
|                 JUMP_TO_JUMP_TARGET(); | ||||
|             } | ||||
|             if (code->co_kwonlyargcount) { | ||||
|                 UOP_STAT_INC(uopcode, miss); | ||||
|                 JUMP_TO_JUMP_TARGET(); | ||||
|             } | ||||
|             if (code->co_argcount != 1) { | ||||
|                 UOP_STAT_INC(uopcode, miss); | ||||
|                 JUMP_TO_JUMP_TARGET(); | ||||
|             } | ||||
|             if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) { | ||||
|                 UOP_STAT_INC(uopcode, miss); | ||||
|                 JUMP_TO_JUMP_TARGET(); | ||||
|             } | ||||
|             STAT_INC(LOAD_ATTR, hit); | ||||
|             Py_INCREF(fget); | ||||
|             new_frame = _PyFrame_PushUnchecked(tstate, f, 1); | ||||
|             new_frame->localsplus[0] = owner; | ||||
|             stack_pointer[-1].bits = (uintptr_t)new_frame; | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */ | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										79
									
								
								Python/generated_cases.c.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										79
									
								
								Python/generated_cases.c.h
									
										
									
										generated
									
									
									
								
							|  | @ -4429,32 +4429,63 @@ | |||
|             INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); | ||||
|             static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); | ||||
|             _PyStackRef owner; | ||||
|             _PyInterpreterFrame *new_frame; | ||||
|             /* Skip 1 cache entry */ | ||||
|             // _CHECK_PEP_523
 | ||||
|             { | ||||
|                 DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); | ||||
|             } | ||||
|             // _GUARD_TYPE_VERSION
 | ||||
|             owner = stack_pointer[-1]; | ||||
|             uint32_t type_version = read_u32(&this_instr[2].cache); | ||||
|             uint32_t func_version = read_u32(&this_instr[4].cache); | ||||
|             PyObject *fget = read_obj(&this_instr[6].cache); | ||||
|             PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner); | ||||
|             assert((oparg & 1) == 0); | ||||
|             DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); | ||||
|             PyTypeObject *cls = Py_TYPE(owner_o); | ||||
|             assert(type_version != 0); | ||||
|             DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); | ||||
|             assert(Py_IS_TYPE(fget, &PyFunction_Type)); | ||||
|             PyFunctionObject *f = (PyFunctionObject *)fget; | ||||
|             assert(func_version != 0); | ||||
|             DEOPT_IF(f->func_version != func_version, LOAD_ATTR); | ||||
|             PyCodeObject *code = (PyCodeObject *)f->func_code; | ||||
|             assert(code->co_argcount == 1); | ||||
|             DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); | ||||
|             STAT_INC(LOAD_ATTR, hit); | ||||
|             Py_INCREF(fget); | ||||
|             _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); | ||||
|             // Manipulate stack directly because we exit with DISPATCH_INLINED().
 | ||||
|             STACK_SHRINK(1); | ||||
|             new_frame->localsplus[0] = owner; | ||||
|             frame->return_offset = (uint16_t)(next_instr - this_instr); | ||||
|             DISPATCH_INLINED(new_frame); | ||||
|             { | ||||
|                 uint32_t type_version = read_u32(&this_instr[2].cache); | ||||
|                 PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner)); | ||||
|                 assert(type_version != 0); | ||||
|                 DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); | ||||
|             } | ||||
|             /* Skip 2 cache entries */ | ||||
|             // _LOAD_ATTR_PROPERTY_FRAME
 | ||||
|             { | ||||
|                 PyObject *fget = read_obj(&this_instr[6].cache); | ||||
|                 assert((oparg & 1) == 0); | ||||
|                 assert(Py_IS_TYPE(fget, &PyFunction_Type)); | ||||
|                 PyFunctionObject *f = (PyFunctionObject *)fget; | ||||
|                 PyCodeObject *code = (PyCodeObject *)f->func_code; | ||||
|                 DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED, LOAD_ATTR); | ||||
|                 DEOPT_IF(code->co_kwonlyargcount, LOAD_ATTR); | ||||
|                 DEOPT_IF(code->co_argcount != 1, LOAD_ATTR); | ||||
|                 DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); | ||||
|                 STAT_INC(LOAD_ATTR, hit); | ||||
|                 Py_INCREF(fget); | ||||
|                 new_frame = _PyFrame_PushUnchecked(tstate, f, 1); | ||||
|                 new_frame->localsplus[0] = owner; | ||||
|             } | ||||
|             // _SAVE_RETURN_OFFSET
 | ||||
|             { | ||||
|                 #if TIER_ONE | ||||
|                 frame->return_offset = (uint16_t)(next_instr - this_instr); | ||||
|                 #endif | ||||
|                 #if TIER_TWO | ||||
|                 frame->return_offset = oparg; | ||||
|                 #endif | ||||
|             } | ||||
|             // _PUSH_FRAME
 | ||||
|             { | ||||
|                 // Write it out explicitly because it's subtly different.
 | ||||
|                 // Eventually this should be the only occurrence of this code.
 | ||||
|                 assert(tstate->interp->eval_frame == NULL); | ||||
|                 stack_pointer += -1; | ||||
|                 assert(WITHIN_STACK_BOUNDS()); | ||||
|                 _PyFrame_SetStackPointer(frame, stack_pointer); | ||||
|                 new_frame->previous = frame; | ||||
|                 CALL_STAT_INC(inlined_py_calls); | ||||
|                 frame = tstate->current_frame = new_frame; | ||||
|                 tstate->py_recursion_remaining--; | ||||
|                 LOAD_SP(); | ||||
|                 LOAD_IP(0); | ||||
|                 LLTRACE_RESUME_FRAME(); | ||||
|             } | ||||
|             DISPATCH(); | ||||
|         } | ||||
| 
 | ||||
|         TARGET(LOAD_ATTR_SLOT) { | ||||
|  |  | |||
|  | @ -797,7 +797,10 @@ translate_bytecode_to_trace( | |||
| 
 | ||||
|                         if (uop == _PUSH_FRAME) { | ||||
|                             assert(i + 1 == nuops); | ||||
|                             if (opcode == FOR_ITER_GEN || opcode == SEND_GEN) { | ||||
|                             if (opcode == FOR_ITER_GEN || | ||||
|                                 opcode == LOAD_ATTR_PROPERTY || | ||||
|                                 opcode == SEND_GEN) | ||||
|                             { | ||||
|                                 DPRINTF(2, "Bailing due to dynamic target\n"); | ||||
|                                 ADD_TO_TRACE(uop, oparg, 0, target); | ||||
|                                 ADD_TO_TRACE(_DYNAMIC_EXIT, 0, 0, 0); | ||||
|  |  | |||
							
								
								
									
										7
									
								
								Python/optimizer_cases.c.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								Python/optimizer_cases.c.h
									
										
									
										generated
									
									
									
								
							|  | @ -1160,7 +1160,12 @@ | |||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         /* _LOAD_ATTR_PROPERTY is not a viable micro-op for tier 2 */ | ||||
|         case _LOAD_ATTR_PROPERTY_FRAME: { | ||||
|             _PyInterpreterFrame *new_frame; | ||||
|             new_frame = sym_new_not_null(ctx); | ||||
|             stack_pointer[-1] = (_Py_UopsSymbol *)new_frame; | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         /* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 */ | ||||
| 
 | ||||
|  |  | |||
|  | @ -963,15 +963,10 @@ _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *nam | |||
|                 SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_METHOD); | ||||
|                 goto fail; | ||||
|             } | ||||
|             uint32_t version = function_get_version(fget, LOAD_ATTR); | ||||
|             if (version == 0) { | ||||
|                 goto fail; | ||||
|             } | ||||
|             if (_PyInterpreterState_GET()->eval_frame) { | ||||
|                 SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER); | ||||
|                 goto fail; | ||||
|             } | ||||
|             write_u32(lm_cache->keys_version, version); | ||||
|             assert(type->tp_version_tag != 0); | ||||
|             write_u32(lm_cache->type_version, type->tp_version_tag); | ||||
|             /* borrowed */ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Brandt Bucher
						Brandt Bucher