Skip to content

Commit 5b5b02a

Browse files
alexmalyshevmeta-codesync[bot]
authored andcommitted
Capture the CPython function entrypoint on CinderX startup
Summary: I'm experimenting with making the JIT do some compilation during `jit::initialize()`. In order for that to function correctly, the function entrypoint has to already be captured before the JIT starts updating any function objects. CinderX will now load `ast.parse()` and grab the entrypoint from there. This function is chosen on a whim. The requirements we have from it are that it be implemented in Python and it not cause too many other modules to be imported. `ast` fills the bill as the CinderX package needs it anyway for the bytecode compiler. Reviewed By: DinoV Differential Revision: D91380058 fbshipit-source-id: fcdfc145a6256051de10131850cafb228800e1cf
1 parent adb8d9f commit 5b5b02a

1 file changed

Lines changed: 31 additions & 10 deletions

File tree

cinderx/_cinderx-lib.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -470,21 +470,41 @@ PyObject* get_entire_call_stack_as_qualnames_with_lineno_and_frame(
470470
}
471471
#endif
472472

473-
void ensurePyFunctionVectorcall(BorrowedRef<PyFunctionObject> func) {
473+
// Capture the default vectorcall entrypoint for functions.
474+
int ensurePyFunctionVectorcall() {
474475
#if PY_VERSION_HEX < 0x030F0000
475-
if (!Ci_PyFunction_Vectorcall) {
476-
// capture the original vectorcall function on the first function
477-
// creation
478-
Ci_PyFunction_Vectorcall = func->vectorcall;
476+
// Picking a function that has a high chance of being implemented in Python,
477+
// and is extremely likely to be loaded already.
478+
const char* mod_name = "site";
479+
const char* func_name = "addsitedir";
480+
481+
auto mod = Ref<>::steal(PyImport_ImportModule(mod_name));
482+
if (mod == nullptr) {
483+
return -1;
484+
}
485+
auto obj = Ref<>::steal(PyObject_GetAttrString(mod, func_name));
486+
if (obj == nullptr) {
487+
return -1;
488+
}
489+
if (!PyFunction_Check(obj)) {
490+
PyErr_Format(
491+
PyExc_RuntimeError,
492+
"Tried to load a Python function (%s.%s()) but got a '%.200s'",
493+
mod_name,
494+
func_name,
495+
Py_TYPE(obj)->tp_name);
496+
return -1;
479497
}
498+
BorrowedRef<PyFunctionObject> func{obj};
499+
Ci_PyFunction_Vectorcall = func->vectorcall;
480500
#endif
501+
502+
return 0;
481503
}
482504

483505
// Schedule a function to be JIT-compiled. If that fails, then also try
484506
// compiling a perf trampoline for the Python function.
485507
void scheduleCompile(BorrowedRef<PyFunctionObject> func) {
486-
ensurePyFunctionVectorcall(func);
487-
488508
bool scheduled =
489509
jit::shouldScheduleCompile(func) && jit::scheduleJitCompile(func);
490510
if (!scheduled && jit::perf::isPreforkCompilationEnabled()) {
@@ -778,9 +798,6 @@ int cinderx_func_watcher(
778798
PyObject* new_value) {
779799
switch (event) {
780800
case PyFunction_EVENT_CREATE:
781-
// Using Ci_PyFunction_Vectorcall before calling scheduleCompile, so we
782-
// need to ensure that the global variable is defined.
783-
ensurePyFunctionVectorcall(func);
784801
// Update the new function's vectorcall to have it run with Static Python
785802
// if it needs to.
786803
func->vectorcall = getInterpretedVectorcall(func);
@@ -1179,6 +1196,10 @@ PyMethodDef _cinderx_methods[] = {
11791196
int _cinderx_exec_impl(PyObject* m) {
11801197
cinderx::initStaticObjects();
11811198

1199+
// The JIT is going to need the Python function entrypoint during its
1200+
// initialization.
1201+
ensurePyFunctionVectorcall();
1202+
11821203
// The state will be destroyed in module_free(), which gets called even if
11831204
// this function exits early with an error.
11841205
void* state_mem = PyModule_GetState(m);

0 commit comments

Comments
 (0)