@@ -191,6 +191,18 @@ _PyJIT_Result tryCompile(BorrowedRef<PyFunctionObject> func) {
191191 return result;
192192}
193193
194+ void incrementShadowcodeCall ([[maybe_unused]] BorrowedRef<PyCodeObject> code) {
195+ #if SHADOWCODE_SUPPORTED
196+ // The interpreter will only increment up to the shadowcode threshold
197+ // PYSHADOW_INIT_THRESHOLD. After that, it will stop incrementing. If someone
198+ // sets -X jit-auto above the PYSHADOW_INIT_THRESHOLD, we still have to keep
199+ // counting.
200+ if (code->co_mutable ->ncalls > PYSHADOW_INIT_THRESHOLD ) {
201+ code->co_mutable ->ncalls ++;
202+ }
203+ #endif
204+ }
205+
194206// Python function entry point when AutoJIT is enabled.
195207PyObject* autoJITVectorcall (
196208 PyObject* func_obj,
@@ -207,16 +219,10 @@ PyObject* autoJITVectorcall(
207219
208220 // Interpret function as usual until it passes the call count threshold.
209221
210- // The interpreter will only increment up to the shadowcode threshold
211- // PYSHADOW_INIT_THRESHOLD. After that, it will stop incrementing. If someone
212- // sets -X jit-auto above the PYSHADOW_INIT_THRESHOLD, we still have to keep
213- // counting.
214- #if SHADOWCODE_SUPPORTED
215- if (code->co_mutable ->ncalls > PYSHADOW_INIT_THRESHOLD ) {
216- code->co_mutable ->ncalls ++;
217- }
218- #endif
219- if (countCalls (code) < jit::getConfig ().auto_jit_threshold ) {
222+ auto const calls = countCalls (code);
223+
224+ if (calls < jit::getConfig ().auto_jit_threshold ) {
225+ incrementShadowcodeCall (code);
220226 auto entry = getInterpretedVectorcall (func);
221227 return entry (func_obj, stack, nargsf, kwnames);
222228 }
@@ -226,6 +232,7 @@ PyObject* autoJITVectorcall(
226232 return nullptr ;
227233 }
228234 if (result == PYJIT_RESULT_RETRY ) {
235+ incrementShadowcodeCall (code);
229236 auto entry = getInterpretedVectorcall (func);
230237 return entry (func_obj, stack, nargsf, kwnames);
231238 }
@@ -1332,6 +1339,44 @@ PyObject* enable_jit(PyObject* /* self */, PyObject* /* arg */) {
13321339 Py_RETURN_NONE;
13331340}
13341341
1342+ PyObject* compile_after_n_calls (PyObject* /* self */ , PyObject* arg) {
1343+ Py_ssize_t calls = -1 ;
1344+ if (!PyArg_Parse (arg, " n:compile_after_n_calls" , &calls)) {
1345+ return nullptr ;
1346+ }
1347+ if (calls < 0 ) {
1348+ PyErr_Format (
1349+ PyExc_ValueError,
1350+ " Cannot configure JIT to compile functions after '%zd' calls" ,
1351+ calls);
1352+ return nullptr ;
1353+ }
1354+ if (calls == 0 ) {
1355+ PyErr_Format (
1356+ PyExc_ValueError,
1357+ " compile_after_n_calls(0) not supported yet, use PYTHONJITALL=1" );
1358+ return nullptr ;
1359+ }
1360+
1361+ getMutableConfig ().auto_jit_threshold = calls;
1362+ JIT_DLOG (" Configuring JIT to compile functions after {} calls" , calls);
1363+
1364+ Py_RETURN_NONE;
1365+ }
1366+
1367+ PyObject* auto_jit (PyObject* /* self */ , PyObject* /* arg */ ) {
1368+ // Default value that works well for most applications.
1369+ constexpr size_t kThreshold = 1000 ;
1370+
1371+ getMutableConfig ().auto_jit_threshold = kThreshold ;
1372+
1373+ JIT_DLOG (
1374+ " Configuring JIT to compile functions automatically using default "
1375+ " behavior" );
1376+
1377+ Py_RETURN_NONE;
1378+ }
1379+
13351380PyObject* get_batch_compilation_time_ms (PyObject*, PyObject*) {
13361381 return PyLong_FromLong (g_batch_compilation_time.count ());
13371382}
@@ -2300,6 +2345,16 @@ PyMethodDef jit_methods[] = {
23002345 METH_NOARGS ,
23012346 PyDoc_STR (" Re-enable the JIT and re-attach compiled onto previously "
23022347 " JIT-compiled functions." )},
2348+ {" auto" ,
2349+ auto_jit,
2350+ METH_NOARGS ,
2351+ PyDoc_STR (" Configure the JIT to automatically compile functions, using "
2352+ " default settings" )},
2353+ {" compile_after_n_calls" ,
2354+ compile_after_n_calls,
2355+ METH_O ,
2356+ PyDoc_STR (" Configure the JIT to automatically compile functions after "
2357+ " they are called a set number of times." )},
23032358 {" disassemble" , disassemble, METH_O , " Disassemble JIT compiled functions." },
23042359 {" dump_elf" ,
23052360 dump_elf,
0 commit comments