|
28 | 28 | #include "pycore_initconfig.h" // _Py_GetConfigsAsDict() |
29 | 29 | #include "pycore_instruction_sequence.h" // _PyInstructionSequence_New() |
30 | 30 | #include "pycore_interpframe.h" // _PyFrame_GetFunction() |
| 31 | +#include "pycore_interpframe_structs.h" // _PyInterpreterFrame |
31 | 32 | #include "pycore_object.h" // _PyObject_IsFreed() |
32 | 33 | #include "pycore_optimizer.h" // _Py_Executor_DependsOn |
33 | 34 | #include "pycore_pathconfig.h" // _PyPathConfig_ClearGlobal() |
@@ -693,6 +694,110 @@ set_eval_frame_record(PyObject *self, PyObject *list) |
693 | 694 | Py_RETURN_NONE; |
694 | 695 | } |
695 | 696 |
|
| 697 | +typedef struct { |
| 698 | + bool initialized; |
| 699 | + _PyInterpreterFrame frame; |
| 700 | +} JitFrame; |
| 701 | + |
| 702 | +int |
| 703 | +reifier(_PyInterpreterFrame *frame, PyObject *executable) |
| 704 | +{ |
| 705 | + JitFrame *jitframe = (JitFrame*)((char *)frame - offsetof(JitFrame, frame)); |
| 706 | + jitframe->initialized = true; |
| 707 | + frame->f_locals = NULL; |
| 708 | + PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj); |
| 709 | + frame->f_globals = func->func_globals; // borrowed |
| 710 | + frame->f_builtins = func->func_builtins; // borrowed |
| 711 | + frame->frame_obj = NULL; |
| 712 | +#ifdef Py_GIL_DISABLED |
| 713 | + frame->tlbc_index = 0; |
| 714 | +#endif |
| 715 | + PyUnstable_PyJitExecutable *jit_exec = (PyUnstable_PyJitExecutable*)executable; |
| 716 | + if (jit_exec->je_state == NULL) { |
| 717 | + return 0; |
| 718 | + } |
| 719 | + |
| 720 | + PyObject *res = PyObject_CallNoArgs(jit_exec->je_state); |
| 721 | + if (res == NULL) { |
| 722 | + return -1; |
| 723 | + } |
| 724 | + |
| 725 | + // let the test-state function fill in details on the frame |
| 726 | + if (PyDict_Check(res)) { |
| 727 | + PyObject *globals = PyDict_GetItemString(res, "globals"); |
| 728 | + if (globals != NULL) { |
| 729 | + frame->f_globals = globals; |
| 730 | + } |
| 731 | + PyObject *builtins = PyDict_GetItemString(res, "builtins"); |
| 732 | + if (builtins != NULL) { |
| 733 | + frame->f_builtins = builtins; |
| 734 | + } |
| 735 | + PyObject *instr_ptr = PyDict_GetItemString(res, "instr_ptr"); |
| 736 | + if (instr_ptr != NULL) { |
| 737 | + frame->instr_ptr = _PyCode_CODE((PyCodeObject *)func->func_code) + |
| 738 | + PyLong_AsLong(instr_ptr); |
| 739 | + } |
| 740 | + } |
| 741 | + Py_DECREF(res); |
| 742 | + return 0; |
| 743 | +} |
| 744 | + |
| 745 | +static PyObject * |
| 746 | +call_with_jit_frame(PyObject *self, PyObject *args) |
| 747 | +{ |
| 748 | + PyObject *fakefunc; // used for f_funcobj as-if we were that JITed function |
| 749 | + PyObject *call; // the thing to call for testing purposes |
| 750 | + PyObject *callargs; // the arguments to provide for the test call |
| 751 | + PyObject *state = NULL; // a state object provided to the reifier, for tests we |
| 752 | + // callback on it to populate fields. |
| 753 | + if (!PyArg_ParseTuple(args, "OOO|O", &fakefunc, &call, &callargs, &state)) { |
| 754 | + return NULL; |
| 755 | + } |
| 756 | + if (!PyTuple_Check(callargs)) { |
| 757 | + PyErr_SetString(PyExc_TypeError, "callargs must be a tuple"); |
| 758 | + return NULL; |
| 759 | + } |
| 760 | + |
| 761 | + PyThreadState *tstate = PyThreadState_Get(); |
| 762 | + PyCodeObject *code = (PyCodeObject *)((PyFunctionObject *)fakefunc)->func_code; |
| 763 | + PyObject *executable = PyUnstable_MakeJITExecutable(reifier, code, state); |
| 764 | + if (executable == NULL) { |
| 765 | + return NULL; |
| 766 | + } |
| 767 | + |
| 768 | + // Create JIT frame and push onto the _PyInterprerFrame stack. |
| 769 | + JitFrame frame; |
| 770 | + frame.initialized = false; |
| 771 | + // Initialize minimal set of fields |
| 772 | + frame.frame.previous = tstate->current_frame; |
| 773 | + frame.frame.f_executable = PyStackRef_FromPyObjectSteal(executable); |
| 774 | + frame.frame.f_funcobj = PyStackRef_FromPyObjectNew(fakefunc); |
| 775 | + frame.frame.instr_ptr = _PyCode_CODE(code) + code->_co_firsttraceable; |
| 776 | + frame.frame.stackpointer = &frame.frame.localsplus[0]; |
| 777 | + frame.frame.owner = FRAME_OWNED_BY_THREAD; |
| 778 | + tstate->current_frame = &frame.frame; |
| 779 | + |
| 780 | + // call the test function |
| 781 | + PyObject *res = PyObject_Call(call, callargs, NULL); |
| 782 | + |
| 783 | + tstate->current_frame = frame.frame.previous; |
| 784 | + // the test function may have caused the frame to get reified. |
| 785 | + if (frame.initialized && frame.frame.frame_obj != NULL) { |
| 786 | + // remove our reifier |
| 787 | + PyStackRef_CLOSE(frame.frame.f_executable); |
| 788 | + frame.frame.f_executable = PyStackRef_FromPyObjectNew(code); |
| 789 | + |
| 790 | + // Transfer ownership to the reified frame object |
| 791 | + _PyFrame_ClearExceptCode(&frame.frame); |
| 792 | + } |
| 793 | + else { |
| 794 | + // Pop frame from the stack |
| 795 | + PyStackRef_CLOSE(frame.frame.f_executable); |
| 796 | + PyStackRef_CLOSE(frame.frame.f_funcobj); |
| 797 | + } |
| 798 | + return res; |
| 799 | +} |
| 800 | + |
696 | 801 | /*[clinic input] |
697 | 802 |
|
698 | 803 | _testinternalcapi.compiler_cleandoc -> object |
@@ -2517,6 +2622,7 @@ static PyMethodDef module_functions[] = { |
2517 | 2622 | {"DecodeLocaleEx", decode_locale_ex, METH_VARARGS}, |
2518 | 2623 | {"set_eval_frame_default", set_eval_frame_default, METH_NOARGS, NULL}, |
2519 | 2624 | {"set_eval_frame_record", set_eval_frame_record, METH_O, NULL}, |
| 2625 | + {"call_with_jit_frame", call_with_jit_frame, METH_VARARGS, NULL}, |
2520 | 2626 | _TESTINTERNALCAPI_COMPILER_CLEANDOC_METHODDEF |
2521 | 2627 | _TESTINTERNALCAPI_NEW_INSTRUCTION_SEQUENCE_METHODDEF |
2522 | 2628 | _TESTINTERNALCAPI_COMPILER_CODEGEN_METHODDEF |
|
0 commit comments