-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GH-117086: Fix function version numbering #117093
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3829,14 +3829,32 @@ dummy_func( | |
PyFunctionObject *func_obj = (PyFunctionObject *) | ||
PyFunction_New(codeobj, GLOBALS()); | ||
|
||
Py_DECREF(codeobj); | ||
if (func_obj == NULL) { | ||
GOTO_ERROR(error); | ||
} | ||
|
||
_PyFunction_SetVersion( | ||
func_obj, ((PyCodeObject *)codeobj)->co_version); | ||
PyCodeObject *code = (PyCodeObject *)codeobj; | ||
if (func_obj->func_builtins != tstate->interp->builtins) { | ||
_PyFunction_SetVersion(func_obj, 0); | ||
} | ||
else if (code->co_version == 0) { | ||
code->co_version = tstate->interp->func_state.next_version; | ||
if (tstate->interp->func_state.next_version != 0) { | ||
tstate->interp->func_state.next_version++; | ||
} | ||
assert(code->co_expected_globals_version == 0); | ||
if (PyDict_CheckExact(GLOBALS())) { | ||
code->co_expected_globals_version = ((PyDictObject *)GLOBALS())->ma_version_tag; | ||
} | ||
_PyFunction_SetVersion(func_obj, code->co_version); | ||
} | ||
else { | ||
if (PyDict_CheckExact(GLOBALS()) && | ||
code->co_expected_globals_version == ((PyDictObject *)GLOBALS())->ma_version_tag) { | ||
_PyFunction_SetVersion(func_obj, code->co_version); | ||
} | ||
} | ||
func = (PyObject *)func_obj; | ||
Py_DECREF(codeobj); | ||
Comment on lines
+3835
to
+3857
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels like an awfully large block of code for the JIT. Maybe it should be moved to a helper function? Also, my brain is too small to fully grasp what will happen if we have a local function that references a global and is repeatedly created (maybe a generator expression inside a hot loop), when the referenced global changes (or some other global, given how dict versions work IIRC). Is that going to completely disable tier 2 for that hot loop, or is it just going to use A variant of the new test may reveal what happens more easily than trying to reason it through... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Maybe, but that is the responsibility of the JIT builder. Let's not complicate the interpreter specification based on the current behavior of the JIT.
If the globals are changed, then we will get a different function version number. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to play with this to see what actually happens. As I said, my brain is too small to reason it through, and your response ("should") does not allay my worries. |
||
} | ||
|
||
inst(SET_FUNCTION_ATTRIBUTE, (attr, func -- func)) { | ||
|
@@ -3860,6 +3878,13 @@ dummy_func( | |
assert(PyTuple_CheckExact(attr)); | ||
assert(func_obj->func_defaults == NULL); | ||
func_obj->func_defaults = attr; | ||
PyCodeObject *code = (PyCodeObject *)func_obj->func_code; | ||
if (code->co_expected_number_of_defaults < 0) { | ||
code->co_expected_number_of_defaults = (int32_t)PyTuple_GET_SIZE(attr); | ||
} | ||
else if (code->co_expected_number_of_defaults != PyTuple_GET_SIZE(attr)) { | ||
func_obj->func_version = 0; | ||
} | ||
Comment on lines
+3881
to
+3887
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This works, but couldn't we let the bytecode compiler give us the number? Again, I worry a bit about the size of the JIT code. |
||
break; | ||
default: | ||
Py_UNREACHABLE(); | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this test only fails when tier 2 is selected (e.g.
-X uops
, or when the JIT is enabled).We have machinery in test_capi/test_opt.py to force the optimizer on. It feels pretty arbitrary whether this test belongs here or there. Can you explain the criteria for where a tier-2-related test should go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there are any. This test should pass for tier-1 and tier-2, as should all tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. In my mind this test should be moved to
test_capi/test_opt.py
and should be wrapped inwith temporary_optimizer(_testinternalcapi.new_uop_optimizer()):
. Otherwise it doesn't test anything unless build with the JIT or run with-Xuops
.