From 8e2ad186717d102887f0f70e6510ea44103bcaa2 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 28 Jun 2023 10:46:19 -0700 Subject: [PATCH 01/28] Make Emscripten trampolines work with JSPI There is a WIP proposal to enable webassembly stack switching which have been implemented in v8: https://github.com/WebAssembly/js-promise-integration It is not possible to switch stacks that contain JS frames so the Emscripten JS trampolines that allow calling functions with the wrong number of arguments don't work in this case. However, the js-promise-integration proposal requires the [type reflection for Wasm/JS API](https://github.com/WebAssembly/js-types) proposal, which allows us to actually count the number of arguments a function expects. For better compatibility with stack switching, this PR checks if type reflection is available, and if so we use a switch block to decide the appropriate signature. If type reflection is unavailable, we should use the current EMJS trampoline. We cache the function argument counts since when I didn't cache them performance was negatively affected. --- Include/internal/pycore_object.h | 14 +++++-- Objects/descrobject.c | 22 +++++++---- Objects/methodobject.c | 7 ---- Python/emscripten_trampoline.c | 67 ++++++++++++++++++++++++++++++++ configure | 2 +- configure.ac | 2 +- 6 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 Python/emscripten_trampoline.c diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 2358f48738a905..b5d59425e2023d 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -426,11 +426,19 @@ PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, PyObject *); * trampoline mitigates common occurrences of bad fpcasts on Emscripten. */ #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) +extern PyObject* +_PyEM_TrampolineCall(PyCFunctionWithKeywords func, + PyObject* self, + PyObject* args, + PyObject* kw); + #define _PyCFunction_TrampolineCall(meth, self, args) \ - _PyCFunctionWithKeywords_TrampolineCall( \ + _PyEM_TrampolineCall( \ (*(PyCFunctionWithKeywords)(void(*)(void))(meth)), (self), (args), NULL) -extern PyObject* _PyCFunctionWithKeywords_TrampolineCall( - PyCFunctionWithKeywords meth, PyObject *, PyObject *, PyObject *); + +#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ + _PyEM_TrampolineCall((meth), (self), (args), (kw)) + #else #define _PyCFunction_TrampolineCall(meth, self, args) \ (meth)((self), (args)) diff --git a/Objects/descrobject.c b/Objects/descrobject.c index a81490285eed4a..ccb265edd089fd 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -18,15 +18,21 @@ class property "propertyobject *" "&PyProperty_Type" // see pycore_object.h #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -#include -EM_JS(int, descr_set_trampoline_call, (setter set, PyObject *obj, PyObject *value, void *closure), { - return wasmTable.get(set)(obj, value, closure); -}); - -EM_JS(PyObject*, descr_get_trampoline_call, (getter get, PyObject *obj, void *closure), { - return wasmTable.get(get)(obj, closure); -}); +extern PyObject* +_PyEM_TrampolineCall(PyCFunctionWithKeywords func, + PyObject* self, + PyObject* args, + PyObject* kw); + +#define descr_set_trampoline_call(set, obj, value, closure) \ + ((int)_PyEM_TrampolineCall((PyCFunctionWithKeywords)(set), (obj), (value), (PyObject*)(closure))) + + +#define descr_get_trampoline_call(get, obj, closure) \ + _PyEM_TrampolineCall((PyCFunctionWithKeywords)(get), (obj), (PyObject*)(closure), NULL) + #else + #define descr_set_trampoline_call(set, obj, value, closure) \ (set)((obj), (value), (closure)) diff --git a/Objects/methodobject.c b/Objects/methodobject.c index fe081992d51fda..c25ccd424ebc89 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -551,10 +551,3 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs) return _Py_CheckFunctionResult(tstate, func, result, NULL); } -#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -#include - -EM_JS(PyObject*, _PyCFunctionWithKeywords_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *self, PyObject *args, PyObject *kw), { - return wasmTable.get(func)(self, args, kw); -}); -#endif diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c new file mode 100644 index 00000000000000..4be63d13f016f6 --- /dev/null +++ b/Python/emscripten_trampoline.c @@ -0,0 +1,67 @@ +#if defined(PY_CALL_TRAMPOLINE) + +#include +#include + +EMSCRIPTEN_KEEPALIVE int _PyEM_type_reflection_available; + +EM_JS_DEPS(_PyEMJS_TrampolineCall, "$getWasmTableEntry") +EM_JS(PyObject*, _PyEMJS_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { + return getWasmTableEntry(func)(arg1, arg2, arg3); +} +); + +EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), { + let n = _PyEM_CountFuncParams.cache.get(func); + if (n !== undefined) { + return n; + } + n = WebAssembly.Function.type(getWasmTableEntry(func)).parameters.length; + _PyEM_CountFuncParams.cache.set(func, n); + return n; +} +_PyEM_CountFuncParams.cache = new Map(); +switch(typeof Module.preRun) { + case "undefined": + Module.preRun = []; + break; + case "function": + Module.preRun = [Module.preRun]; + break; +} +Module.preRun.push(() => HEAP32[__PyEM_type_reflection_available/4] = "Function" in WebAssembly); +) + + +typedef PyObject* (*zero_arg)(void); +typedef PyObject* (*one_arg)(PyObject*); +typedef PyObject* (*two_arg)(PyObject*, PyObject*); +typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*); + +PyObject* +_PyEM_TrampolineCall(PyCFunctionWithKeywords func, + PyObject* self, + PyObject* args, + PyObject* kw) +{ + if (!_PyEM_type_reflection_available) { + return _PyEMJS_TrampolineCall(func, self, args, kw); + } else { + switch (_PyEM_CountFuncParams(func)) { + case 0: + return ((zero_arg)func)(); + case 1: + return ((one_arg)func)(self); + case 2: + return ((two_arg)func)(self, args); + case 3: + return ((three_arg)func)(self, args, kw); + default: + PyErr_SetString(PyExc_SystemError, "Handler takes too many arguments"); + return NULL; + } + } +} + + +#endif diff --git a/configure b/configure index e6fb5e3c2b0c2f..07301042299fd4 100755 --- a/configure +++ b/configure @@ -17099,7 +17099,7 @@ PLATFORM_OBJS= case $ac_sys_system in #( Emscripten) : - as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o' + as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o Python/emscripten_trampoline.o' as_fn_append PLATFORM_HEADERS ' $(srcdir)/Include/internal/pycore_emscripten_signal.h' ;; #( *) : diff --git a/configure.ac b/configure.ac index a1ee78047692fd..0a917e43e3563c 100644 --- a/configure.ac +++ b/configure.ac @@ -4770,7 +4770,7 @@ PLATFORM_OBJS= AS_CASE([$ac_sys_system], [Emscripten], [ - AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o']) + AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o Python/emscripten_trampoline.o']) AS_VAR_APPEND([PLATFORM_HEADERS], [' $(srcdir)/Include/internal/pycore_emscripten_signal.h']) ], ) From 6903fd5ac1409563a556d1dcaa40395475d32580 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 29 Jun 2023 09:43:02 -0700 Subject: [PATCH 02/28] Add blurb --- .../2023-06-29-09-42-56.gh-issue-106213.TCUgzM.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-06-29-09-42-56.gh-issue-106213.TCUgzM.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-06-29-09-42-56.gh-issue-106213.TCUgzM.rst b/Misc/NEWS.d/next/Core and Builtins/2023-06-29-09-42-56.gh-issue-106213.TCUgzM.rst new file mode 100644 index 00000000000000..431f9cc0e4bb7d --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-06-29-09-42-56.gh-issue-106213.TCUgzM.rst @@ -0,0 +1,2 @@ +Changed the way that Emscripten call trampolines work for compatibility with +Wasm/JS Promise integration. From af6850ccd01c5d571777373e20bc6ec6e7a6a23d Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 29 Jun 2023 13:29:13 -0700 Subject: [PATCH 03/28] Add more comments --- .../internal/pycore_emscripten_trampoline.h | 54 +++++++++++++++++++ Include/internal/pycore_object.h | 38 ++----------- Python/emscripten_trampoline.c | 28 ++++++---- Python/pylifecycle.c | 5 ++ configure | 2 +- configure.ac | 2 +- 6 files changed, 82 insertions(+), 47 deletions(-) create mode 100644 Include/internal/pycore_emscripten_trampoline.h diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h new file mode 100644 index 00000000000000..2a928fc203929f --- /dev/null +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -0,0 +1,54 @@ +#ifndef Py_EMSCRIPTEN_TRAMPOLINE_H +#define Py_EMSCRIPTEN_TRAMPOLINE_H + + +/** + * C function call trampolines to mitigate bad function pointer casts. + * + * Section 6.3.2.3, paragraph 8 reads: + * + * A pointer to a function of one type may be converted to a pointer to a + * function of another type and back again; the result shall compare equal to + * the original pointer. If a converted pointer is used to call a function + * whose type is not compatible with the pointed-to type, the behavior is + * undefined. + * + * Typical native ABIs ignore additional arguments or fill in missing values + * with 0/NULL in function pointer cast. Compilers do not show warnings when a + * function pointer is explicitly casted to an incompatible type. + * + * Bad fpcasts are an issue in WebAssembly. WASM's indirect_call has strict + * function signature checks. Argument count, types, and return type must match. + * + * Third party code unintentionally rely on problematic fpcasts. The call + * trampoline mitigates common occurrences of bad fpcasts on Emscripten. + */ + +#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) + +void _Py_EmscriptenTrampoline_Init(void); + +PyObject* +_PyEM_TrampolineCall(PyCFunctionWithKeywords func, + PyObject* self, + PyObject* args, + PyObject* kw); + +#define _PyCFunction_TrampolineCall(meth, self, args) \ + _PyEM_TrampolineCall( \ + (*(PyCFunctionWithKeywords)(void(*)(void))(meth)), (self), (args), NULL) + +#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ + _PyEM_TrampolineCall((meth), (self), (args), (kw)) + +#else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) + +#define _Py_EmscriptenTrampoline_Init() + +#define _PyCFunction_TrampolineCall(meth, self, args) \ + (meth)((self), (args)) +#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ + (meth)((self), (args), (kw)) + +#endif // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) +#endif // ndef Py_EMSCRIPTEN_SIGNAL_H \ No newline at end of file diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index b5d59425e2023d..0ff6c565722c4a 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -10,10 +10,13 @@ extern "C" { #include #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() +#include "pycore_emscripten_trampoline.h" // _PyCFunction_TrampolineCall() #include "pycore_interp.h" // PyInterpreterState.gc #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_runtime.h" // _PyRuntime + + /* We need to maintain an internal copy of Py{Var}Object_HEAD_INIT to avoid designated initializer conflicts in C++20. If we use the deinition in object.h, we will be mixing designated and non-designated initializers in @@ -411,41 +414,6 @@ extern int _PyObject_IsInstanceDictEmpty(PyObject *); PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, PyObject *); -/* C function call trampolines to mitigate bad function pointer casts. - * - * Typical native ABIs ignore additional arguments or fill in missing - * values with 0/NULL in function pointer cast. Compilers do not show - * warnings when a function pointer is explicitly casted to an - * incompatible type. - * - * Bad fpcasts are an issue in WebAssembly. WASM's indirect_call has strict - * function signature checks. Argument count, types, and return type must - * match. - * - * Third party code unintentionally rely on problematic fpcasts. The call - * trampoline mitigates common occurrences of bad fpcasts on Emscripten. - */ -#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -extern PyObject* -_PyEM_TrampolineCall(PyCFunctionWithKeywords func, - PyObject* self, - PyObject* args, - PyObject* kw); - -#define _PyCFunction_TrampolineCall(meth, self, args) \ - _PyEM_TrampolineCall( \ - (*(PyCFunctionWithKeywords)(void(*)(void))(meth)), (self), (args), NULL) - -#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ - _PyEM_TrampolineCall((meth), (self), (args), (kw)) - -#else -#define _PyCFunction_TrampolineCall(meth, self, args) \ - (meth)((self), (args)) -#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ - (meth)((self), (args), (kw)) -#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE - #ifdef __cplusplus } #endif diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 4be63d13f016f6..709c4f76a5fa50 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -3,14 +3,30 @@ #include #include -EMSCRIPTEN_KEEPALIVE int _PyEM_type_reflection_available; +static int _PyEM_type_reflection_available; +/** + * This is the GoogleChromeLabs approved way to feature detect type-reflection: + * https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/type-reflection/index.js + */ +void +_Py_EmscriptenTrampoline_Init(){ + _PyEM_type_reflection_available = EM_ASM_INT({return "function" in WebAssembly}); +} + +/** + * Backwards compatible trampoline works with all JS runtimes + */ EM_JS_DEPS(_PyEMJS_TrampolineCall, "$getWasmTableEntry") EM_JS(PyObject*, _PyEMJS_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { return getWasmTableEntry(func)(arg1, arg2, arg3); } ); +/** + * In runtimes with WebAssembly type reflection, count the number of parameters + * and cast to the appropriate signature + */ EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), { let n = _PyEM_CountFuncParams.cache.get(func); if (n !== undefined) { @@ -21,15 +37,6 @@ EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), { return n; } _PyEM_CountFuncParams.cache = new Map(); -switch(typeof Module.preRun) { - case "undefined": - Module.preRun = []; - break; - case "function": - Module.preRun = [Module.preRun]; - break; -} -Module.preRun.push(() => HEAP32[__PyEM_type_reflection_available/4] = "Function" in WebAssembly); ) @@ -38,6 +45,7 @@ typedef PyObject* (*one_arg)(PyObject*); typedef PyObject* (*two_arg)(PyObject*, PyObject*); typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*); + PyObject* _PyEM_TrampolineCall(PyCFunctionWithKeywords func, PyObject* self, diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 1df35ef4278677..615ad95d203e3c 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -6,6 +6,7 @@ #include "pycore_ceval.h" // _PyEval_FiniGIL() #include "pycore_context.h" // _PyContext_Init() #include "pycore_dict.h" // _PyDict_Fini() +#include "pycode_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() #include "pycore_exceptions.h" // _PyExc_InitTypes() #include "pycore_fileutils.h" // _Py_ResetForceASCII() #include "pycore_floatobject.h" // _PyFloat_InitTypes() @@ -541,7 +542,11 @@ pycore_init_runtime(_PyRuntimeState *runtime, if (_PyStatus_EXCEPTION(status)) { return status; } + + _Py_EmscriptenTrampoline_Init(); + return _PyStatus_OK(); + } diff --git a/configure b/configure index 07301042299fd4..d91b77ab371975 100755 --- a/configure +++ b/configure @@ -17100,7 +17100,7 @@ case $ac_sys_system in #( Emscripten) : as_fn_append PLATFORM_OBJS ' Python/emscripten_signal.o Python/emscripten_trampoline.o' - as_fn_append PLATFORM_HEADERS ' $(srcdir)/Include/internal/pycore_emscripten_signal.h' + as_fn_append PLATFORM_HEADERS ' $(srcdir)/Include/internal/pycore_emscripten_signal.h $(srcdir)/Include/internal/pycore_emscripten_trampoline.h' ;; #( *) : ;; diff --git a/configure.ac b/configure.ac index 0a917e43e3563c..a6790ed94a51f0 100644 --- a/configure.ac +++ b/configure.ac @@ -4771,7 +4771,7 @@ PLATFORM_OBJS= AS_CASE([$ac_sys_system], [Emscripten], [ AS_VAR_APPEND([PLATFORM_OBJS], [' Python/emscripten_signal.o Python/emscripten_trampoline.o']) - AS_VAR_APPEND([PLATFORM_HEADERS], [' $(srcdir)/Include/internal/pycore_emscripten_signal.h']) + AS_VAR_APPEND([PLATFORM_HEADERS], [' $(srcdir)/Include/internal/pycore_emscripten_signal.h $(srcdir)/Include/internal/pycore_emscripten_trampoline.h']) ], ) AC_SUBST([PLATFORM_HEADERS]) From ffb24ff2d09c03b39a375b367e4ccc3a9356ac0c Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 29 Jun 2023 13:37:14 -0700 Subject: [PATCH 04/28] Fix typo --- Include/internal/pycore_emscripten_trampoline.h | 2 +- Python/pylifecycle.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h index 2a928fc203929f..51ef976f2b03ac 100644 --- a/Include/internal/pycore_emscripten_trampoline.h +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -51,4 +51,4 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func, (meth)((self), (args), (kw)) #endif // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -#endif // ndef Py_EMSCRIPTEN_SIGNAL_H \ No newline at end of file +#endif // ndef Py_EMSCRIPTEN_SIGNAL_H diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 615ad95d203e3c..055610e1cd86b4 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -6,7 +6,7 @@ #include "pycore_ceval.h" // _PyEval_FiniGIL() #include "pycore_context.h" // _PyContext_Init() #include "pycore_dict.h" // _PyDict_Fini() -#include "pycode_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() +#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() #include "pycore_exceptions.h" // _PyExc_InitTypes() #include "pycore_fileutils.h" // _Py_ResetForceASCII() #include "pycore_floatobject.h" // _PyFloat_InitTypes() From 85d88ece87b039fb8883451fc25faf43dd3241d7 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 29 Jun 2023 13:39:40 -0700 Subject: [PATCH 05/28] Cleanup --- Python/pylifecycle.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 055610e1cd86b4..b0a360beb72705 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -546,7 +546,6 @@ pycore_init_runtime(_PyRuntimeState *runtime, _Py_EmscriptenTrampoline_Init(); return _PyStatus_OK(); - } From 9d8f23e584ec0f97a6a75ee55de0256af374d501 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 29 Jun 2023 17:49:53 -0700 Subject: [PATCH 06/28] Use EM_JS instead of EM_ASM --- Python/emscripten_trampoline.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 709c4f76a5fa50..e7c8bfe5e9bce0 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -3,15 +3,19 @@ #include #include -static int _PyEM_type_reflection_available; +static int type_reflection_available; /** * This is the GoogleChromeLabs approved way to feature detect type-reflection: * https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/type-reflection/index.js */ +EM_JS(int, _PyEM_detect_type_reflection, (), { + return "function" in WebAssembly; +}); + void _Py_EmscriptenTrampoline_Init(){ - _PyEM_type_reflection_available = EM_ASM_INT({return "function" in WebAssembly}); + type_reflection_available = _PyEM_detect_type_reflection(); } /** @@ -71,5 +75,4 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func, } } - #endif From 2dc1399644c0055cd16e9bc28c3366a93dd4716c Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 29 Jun 2023 21:43:39 -0700 Subject: [PATCH 07/28] Fix typo --- Python/emscripten_trampoline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index e7c8bfe5e9bce0..9da85eef3b75f4 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -56,7 +56,7 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func, PyObject* args, PyObject* kw) { - if (!_PyEM_type_reflection_available) { + if (!type_reflection_available) { return _PyEMJS_TrampolineCall(func, self, args, kw); } else { switch (_PyEM_CountFuncParams(func)) { From d900dfdf125dd20df947a765a849a3edce023e07 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 6 Jul 2023 09:43:25 -0700 Subject: [PATCH 08/28] Move type reflection flag into _PyRuntime --- Include/internal/pycore_runtime.h | 5 +++++ Python/emscripten_trampoline.c | 11 ++++++----- Python/pylifecycle.c | 3 --- Python/pystate.c | 2 ++ 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 5ed97e9715b2b0..fc7d788a4787fe 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -77,6 +77,11 @@ typedef struct pyruntimestate { /* Is Python fully initialized? Set to 1 by Py_Initialize() */ int initialized; +#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) + /* Choose between trampoline based on type reflection vs based on EM_JS */ + int wasm_type_reflection_available; +#endif + /* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize() is called again. diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 9da85eef3b75f4..33c9d25d2e092c 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -1,9 +1,9 @@ #if defined(PY_CALL_TRAMPOLINE) -#include +#include // EM_JS, EM_JS_DEPS #include +#include "pycore_runtime.h" // _PyRuntime -static int type_reflection_available; /** * This is the GoogleChromeLabs approved way to feature detect type-reflection: @@ -14,8 +14,9 @@ EM_JS(int, _PyEM_detect_type_reflection, (), { }); void -_Py_EmscriptenTrampoline_Init(){ - type_reflection_available = _PyEM_detect_type_reflection(); +_Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime) +{ + runtime->wasm_type_reflection_available = _PyEM_detect_type_reflection(); } /** @@ -56,7 +57,7 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func, PyObject* args, PyObject* kw) { - if (!type_reflection_available) { + if (!_PyRuntime.wasm_type_reflection_available) { return _PyEMJS_TrampolineCall(func, self, args, kw); } else { switch (_PyEM_CountFuncParams(func)) { diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index af0af7a3e0718e..299a318f8821ee 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -7,7 +7,6 @@ #include "pycore_codecs.h" // _PyCodec_Lookup() #include "pycore_context.h" // _PyContext_Init() #include "pycore_dict.h" // _PyDict_Fini() -#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() #include "pycore_exceptions.h" // _PyExc_InitTypes() #include "pycore_fileutils.h" // _Py_ResetForceASCII() #include "pycore_floatobject.h" // _PyFloat_InitTypes() @@ -544,8 +543,6 @@ pycore_init_runtime(_PyRuntimeState *runtime, return status; } - _Py_EmscriptenTrampoline_Init(); - return _PyStatus_OK(); } diff --git a/Python/pystate.c b/Python/pystate.c index a9b404bd5c93e3..4b2c6ce1e6c6c6 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2,6 +2,7 @@ /* Thread and interpreter state structures and their interfaces */ #include "Python.h" +#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() #include "pycore_ceval.h" #include "pycore_code.h" // stats #include "pycore_dtoa.h" // _dtoa_state_INIT() @@ -452,6 +453,7 @@ init_runtime(_PyRuntimeState *runtime, runtime->unicode_state.ids.next_index = unicode_next_index; runtime->_initialized = 1; + _Py_EmscriptenTrampoline_Init(runtime); } PyStatus From cd3f8bf57f374bb25349c7aeeb0c98bcaa13303a Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 6 Jul 2023 19:59:35 -0700 Subject: [PATCH 09/28] Update pycore_emscripten_trampoline.h declarations --- Include/internal/pycore_emscripten_trampoline.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h index 51ef976f2b03ac..1b1f1dd56d55d1 100644 --- a/Include/internal/pycore_emscripten_trampoline.h +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -26,7 +26,7 @@ #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -void _Py_EmscriptenTrampoline_Init(void); +void _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime); PyObject* _PyEM_TrampolineCall(PyCFunctionWithKeywords func, @@ -43,7 +43,7 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func, #else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -#define _Py_EmscriptenTrampoline_Init() +#define _Py_EmscriptenTrampoline_Init(runtime) #define _PyCFunction_TrampolineCall(meth, self, args) \ (meth)((self), (args)) From 39518484efc2ad2e5000c89659ff4109fc379601 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 6 Jul 2023 20:15:57 -0700 Subject: [PATCH 10/28] Include pycore_runtime.h --- Include/internal/pycore_emscripten_trampoline.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h index 1b1f1dd56d55d1..e5031c836211e3 100644 --- a/Include/internal/pycore_emscripten_trampoline.h +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -1,6 +1,7 @@ #ifndef Py_EMSCRIPTEN_TRAMPOLINE_H #define Py_EMSCRIPTEN_TRAMPOLINE_H +#include "pycore_runtime.h" // _PyRuntimeState /** * C function call trampolines to mitigate bad function pointer casts. From dfd43d7f174fe8dc144ad1b1c8d0e3c09461c4e1 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sat, 8 Jul 2023 19:58:08 -0700 Subject: [PATCH 11/28] Fix typo --- Python/emscripten_trampoline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 33c9d25d2e092c..d88df8b5b2ce3e 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -10,7 +10,7 @@ * https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/type-reflection/index.js */ EM_JS(int, _PyEM_detect_type_reflection, (), { - return "function" in WebAssembly; + return "Function" in WebAssembly; }); void From b0b50f437e421b5fdfd5521d0c094300d885498b Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 9 Jul 2023 22:09:05 -0700 Subject: [PATCH 12/28] Use wasmTable.get instead of getWasmTableEntry Profiling shows that this is actually a bit faster --- Python/emscripten_trampoline.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index d88df8b5b2ce3e..7d02e7d7095f2d 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -22,11 +22,9 @@ _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime) /** * Backwards compatible trampoline works with all JS runtimes */ -EM_JS_DEPS(_PyEMJS_TrampolineCall, "$getWasmTableEntry") EM_JS(PyObject*, _PyEMJS_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { - return getWasmTableEntry(func)(arg1, arg2, arg3); -} -); + return wasmTable.get(func)(arg1, arg2, arg3); +}); /** * In runtimes with WebAssembly type reflection, count the number of parameters From 1c1b9ed7e6d6ec54bdeb9c17344820f16f8e86e0 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 9 Jul 2023 22:10:15 -0700 Subject: [PATCH 13/28] Replace another instance of getWasmTableEntry with wasmTable.get --- Python/emscripten_trampoline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 7d02e7d7095f2d..425733eacefe45 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -35,7 +35,7 @@ EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), { if (n !== undefined) { return n; } - n = WebAssembly.Function.type(getWasmTableEntry(func)).parameters.length; + n = WebAssembly.Function.type(wasmTable.get(func)).parameters.length; _PyEM_CountFuncParams.cache.set(func, n); return n; } From 44f0371da0f1b1c6acf08a5f161e569abeaa71dc Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 10 Jul 2023 16:02:06 -0700 Subject: [PATCH 14/28] Use branch in macro to choose trampoline One of the cpython tests (test_plistlib.TestBinaryPlistlib.test_deep_nesting) seems to be extremely sensitive to stack utilization and stack overflows in our CI when even a tiny amount of extra stack is used. I doubt this is a problem in real life -- I certainly cannot reproduce it outside of the CI service. But this rearranges the code to avoid any additional stack usage so that CI will pass. --- .../internal/pycore_emscripten_trampoline.h | 20 ++++++++--- Objects/descrobject.c | 20 +---------- Python/emscripten_trampoline.c | 35 +++++++++---------- 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h index e5031c836211e3..900d527e48d289 100644 --- a/Include/internal/pycore_emscripten_trampoline.h +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -30,10 +30,22 @@ void _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime); PyObject* -_PyEM_TrampolineCall(PyCFunctionWithKeywords func, - PyObject* self, - PyObject* args, - PyObject* kw); +_PyEM_TrampolineCall_JS(PyCFunctionWithKeywords func, + PyObject* self, + PyObject* args, + PyObject* kw); + +PyObject* +_PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, + PyObject* self, + PyObject* args, + PyObject* kw); + +#define _PyEM_TrampolineCall(meth, self, args, kw) \ + ((_PyRuntime.wasm_type_reflection_available) ? \ + (_PyEM_TrampolineCall_Reflection(meth, self, args, kw)) : \ + (_PyEM_TrampolineCall_JS(meth, self, args, kw))) + #define _PyCFunction_TrampolineCall(meth, self, args) \ _PyEM_TrampolineCall( \ diff --git a/Objects/descrobject.c b/Objects/descrobject.c index ccb265edd089fd..1cb11fd6cd6942 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -4,6 +4,7 @@ #include "pycore_abstract.h" // _PyObject_RealIsSubclass() #include "pycore_call.h" // _PyStack_AsDict() #include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate() +#include "pycore_emscripten_trampoline.h" // _PyEM_TrampolineCall() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_tuple.h" // _PyTuple_ITEMS() @@ -16,31 +17,12 @@ class property "propertyobject *" "&PyProperty_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=556352653fd4c02e]*/ -// see pycore_object.h -#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) -extern PyObject* -_PyEM_TrampolineCall(PyCFunctionWithKeywords func, - PyObject* self, - PyObject* args, - PyObject* kw); - #define descr_set_trampoline_call(set, obj, value, closure) \ ((int)_PyEM_TrampolineCall((PyCFunctionWithKeywords)(set), (obj), (value), (PyObject*)(closure))) - #define descr_get_trampoline_call(get, obj, closure) \ _PyEM_TrampolineCall((PyCFunctionWithKeywords)(get), (obj), (PyObject*)(closure), NULL) -#else - -#define descr_set_trampoline_call(set, obj, value, closure) \ - (set)((obj), (value), (closure)) - -#define descr_get_trampoline_call(get, obj, closure) \ - (get)((obj), (closure)) - -#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE - static void descr_dealloc(PyDescrObject *descr) { diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 425733eacefe45..d2d24a6e0a097a 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -22,9 +22,10 @@ _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime) /** * Backwards compatible trampoline works with all JS runtimes */ -EM_JS(PyObject*, _PyEMJS_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { +EM_JS(PyObject*, _PyEM_TrampolineCall_JS, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { return wasmTable.get(func)(arg1, arg2, arg3); -}); +} +); /** * In runtimes with WebAssembly type reflection, count the number of parameters @@ -50,27 +51,23 @@ typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*); PyObject* -_PyEM_TrampolineCall(PyCFunctionWithKeywords func, +_PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, PyObject* self, PyObject* args, PyObject* kw) { - if (!_PyRuntime.wasm_type_reflection_available) { - return _PyEMJS_TrampolineCall(func, self, args, kw); - } else { - switch (_PyEM_CountFuncParams(func)) { - case 0: - return ((zero_arg)func)(); - case 1: - return ((one_arg)func)(self); - case 2: - return ((two_arg)func)(self, args); - case 3: - return ((three_arg)func)(self, args, kw); - default: - PyErr_SetString(PyExc_SystemError, "Handler takes too many arguments"); - return NULL; - } + switch (_PyEM_CountFuncParams(func)) { + case 0: + return ((zero_arg)func)(); + case 1: + return ((one_arg)func)(self); + case 2: + return ((two_arg)func)(self, args); + case 3: + return ((three_arg)func)(self, args, kw); + default: + PyErr_SetString(PyExc_SystemError, "Handler takes too many arguments"); + return NULL; } } From bd609e5596eb8058c259ef52be8e9b2b7c7b9226 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 11 Jul 2023 14:04:13 -0700 Subject: [PATCH 15/28] Fix non emscripten platforms --- .../internal/pycore_emscripten_trampoline.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h index 900d527e48d289..74ea6a2e284c9d 100644 --- a/Include/internal/pycore_emscripten_trampoline.h +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -46,6 +46,14 @@ _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, (_PyEM_TrampolineCall_Reflection(meth, self, args, kw)) : \ (_PyEM_TrampolineCall_JS(meth, self, args, kw))) +#else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) + +#define _Py_EmscriptenTrampoline_Init(runtime) + +#define _PyEM_TrampolineCall(meth, self, args, kw) \ + (meth)((self), (args), (kw)) + +#endif // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) #define _PyCFunction_TrampolineCall(meth, self, args) \ _PyEM_TrampolineCall( \ @@ -54,14 +62,4 @@ _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, #define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ _PyEM_TrampolineCall((meth), (self), (args), (kw)) -#else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) - -#define _Py_EmscriptenTrampoline_Init(runtime) - -#define _PyCFunction_TrampolineCall(meth, self, args) \ - (meth)((self), (args)) -#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ - (meth)((self), (args), (kw)) - -#endif // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) #endif // ndef Py_EMSCRIPTEN_SIGNAL_H From a00d94fee9534b764299ebf489eae45dd8998558 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 11 Jul 2023 14:13:56 -0700 Subject: [PATCH 16/28] Fix compile warnings on non-emscripten platforms --- .../internal/pycore_emscripten_trampoline.h | 34 ++++++++++++++----- Objects/descrobject.c | 8 +---- Python/emscripten_trampoline.c | 2 +- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h index 74ea6a2e284c9d..848390d8e76181 100644 --- a/Include/internal/pycore_emscripten_trampoline.h +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -43,23 +43,39 @@ _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, #define _PyEM_TrampolineCall(meth, self, args, kw) \ ((_PyRuntime.wasm_type_reflection_available) ? \ - (_PyEM_TrampolineCall_Reflection(meth, self, args, kw)) : \ - (_PyEM_TrampolineCall_JS(meth, self, args, kw))) + (_PyEM_TrampolineCall_Reflection((PyCFunctionWithKeywords)(meth), (self), (args), (kw))) : \ + (_PyEM_TrampolineCall_Javascript((PyCFunctionWithKeywords)(meth), (self), (args), (kw)))) + +#define _PyCFunction_TrampolineCall(meth, self, args) \ + _PyEM_TrampolineCall( \ + (*(PyCFunctionWithKeywords)(void(*)(void))(meth)), (self), (args), NULL) + +#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ + _PyEM_TrampolineCall((meth), (self), (args), (kw)) + +#define descr_set_trampoline_call(set, obj, value, closure) \ + ((int)_PyEM_TrampolineCall((PyCFunctionWithKeywords)(set), (obj), (value), (PyObject*)(closure))) + +#define descr_get_trampoline_call(get, obj, closure) \ + _PyEM_TrampolineCall((PyCFunctionWithKeywords)(get), (obj), (PyObject*)(closure), NULL) + #else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) #define _Py_EmscriptenTrampoline_Init(runtime) -#define _PyEM_TrampolineCall(meth, self, args, kw) \ +#define _PyCFunction_TrampolineCall(meth, self, args) \ + (meth)((self), (args)) + +#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ (meth)((self), (args), (kw)) -#endif // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) +#define descr_set_trampoline_call(set, obj, value, closure) \ + (set)((obj), (value), (closure)) -#define _PyCFunction_TrampolineCall(meth, self, args) \ - _PyEM_TrampolineCall( \ - (*(PyCFunctionWithKeywords)(void(*)(void))(meth)), (self), (args), NULL) +#define descr_get_trampoline_call(get, obj, closure) \ + (get)((obj), (closure)) -#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \ - _PyEM_TrampolineCall((meth), (self), (args), (kw)) +#endif // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) #endif // ndef Py_EMSCRIPTEN_SIGNAL_H diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 1cb11fd6cd6942..e7a0be2373c221 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -4,7 +4,7 @@ #include "pycore_abstract.h" // _PyObject_RealIsSubclass() #include "pycore_call.h" // _PyStack_AsDict() #include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate() -#include "pycore_emscripten_trampoline.h" // _PyEM_TrampolineCall() +#include "pycore_emscripten_trampoline.h" // descr_set_trampoline_call(), descr_get_trampoline_call() #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_tuple.h" // _PyTuple_ITEMS() @@ -17,12 +17,6 @@ class property "propertyobject *" "&PyProperty_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=556352653fd4c02e]*/ -#define descr_set_trampoline_call(set, obj, value, closure) \ - ((int)_PyEM_TrampolineCall((PyCFunctionWithKeywords)(set), (obj), (value), (PyObject*)(closure))) - -#define descr_get_trampoline_call(get, obj, closure) \ - _PyEM_TrampolineCall((PyCFunctionWithKeywords)(get), (obj), (PyObject*)(closure), NULL) - static void descr_dealloc(PyDescrObject *descr) { diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index d2d24a6e0a097a..7148a548be72e2 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -22,7 +22,7 @@ _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime) /** * Backwards compatible trampoline works with all JS runtimes */ -EM_JS(PyObject*, _PyEM_TrampolineCall_JS, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { +EM_JS(PyObject*, _PyEM_TrampolineCall_Javascript, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { return wasmTable.get(func)(arg1, arg2, arg3); } ); From 68f4dbaaf967128764fe3f24c256f519f6f85b38 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 11 Jul 2023 14:20:16 -0700 Subject: [PATCH 17/28] Fix spelling --- Include/internal/pycore_emscripten_trampoline.h | 4 ++-- Python/emscripten_trampoline.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h index 848390d8e76181..c9631e99911165 100644 --- a/Include/internal/pycore_emscripten_trampoline.h +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -30,7 +30,7 @@ void _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime); PyObject* -_PyEM_TrampolineCall_JS(PyCFunctionWithKeywords func, +_PyEM_TrampolineCall_JavaScript(PyCFunctionWithKeywords func, PyObject* self, PyObject* args, PyObject* kw); @@ -44,7 +44,7 @@ _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, #define _PyEM_TrampolineCall(meth, self, args, kw) \ ((_PyRuntime.wasm_type_reflection_available) ? \ (_PyEM_TrampolineCall_Reflection((PyCFunctionWithKeywords)(meth), (self), (args), (kw))) : \ - (_PyEM_TrampolineCall_Javascript((PyCFunctionWithKeywords)(meth), (self), (args), (kw)))) + (_PyEM_TrampolineCall_JavaScript((PyCFunctionWithKeywords)(meth), (self), (args), (kw)))) #define _PyCFunction_TrampolineCall(meth, self, args) \ _PyEM_TrampolineCall( \ diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 7148a548be72e2..77eddd3cb16eff 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -22,7 +22,7 @@ _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime) /** * Backwards compatible trampoline works with all JS runtimes */ -EM_JS(PyObject*, _PyEM_TrampolineCall_Javascript, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { +EM_JS(PyObject*, _PyEM_TrampolineCall_JavaScript, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { return wasmTable.get(func)(arg1, arg2, arg3); } ); From 1d49e79c1de689e20d364cddae688c3c700b6aa5 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 11 Jul 2023 14:21:12 -0700 Subject: [PATCH 18/28] Fix formatting --- Include/internal/pycore_emscripten_trampoline.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_emscripten_trampoline.h b/Include/internal/pycore_emscripten_trampoline.h index c9631e99911165..e519c99ad86cce 100644 --- a/Include/internal/pycore_emscripten_trampoline.h +++ b/Include/internal/pycore_emscripten_trampoline.h @@ -31,9 +31,9 @@ void _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime); PyObject* _PyEM_TrampolineCall_JavaScript(PyCFunctionWithKeywords func, - PyObject* self, - PyObject* args, - PyObject* kw); + PyObject* self, + PyObject* args, + PyObject* kw); PyObject* _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, From afb9ad52317c370d435c4150e07e320dcfebf53d Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 11 Jul 2023 14:23:39 -0700 Subject: [PATCH 19/28] Revert whitespace changes --- Include/internal/pycore_object.h | 2 -- Python/pylifecycle.c | 1 - 2 files changed, 3 deletions(-) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 0ff6c565722c4a..cb2b68a3bf9500 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -15,8 +15,6 @@ extern "C" { #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_runtime.h" // _PyRuntime - - /* We need to maintain an internal copy of Py{Var}Object_HEAD_INIT to avoid designated initializer conflicts in C++20. If we use the deinition in object.h, we will be mixing designated and non-designated initializers in diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 299a318f8821ee..cf8b4379c1467f 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -542,7 +542,6 @@ pycore_init_runtime(_PyRuntimeState *runtime, if (_PyStatus_EXCEPTION(status)) { return status; } - return _PyStatus_OK(); } From aa213e0aceeca738da5f3b6be7463b7489948cbf Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 11 Jul 2023 14:24:48 -0700 Subject: [PATCH 20/28] Call _Py_EmscriptenTrampoline_Init before setting _initialized to 1 --- Python/pystate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Python/pystate.c b/Python/pystate.c index 4b2c6ce1e6c6c6..a62222c4db53a7 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -452,8 +452,9 @@ init_runtime(_PyRuntimeState *runtime, runtime->unicode_state.ids.next_index = unicode_next_index; - runtime->_initialized = 1; _Py_EmscriptenTrampoline_Init(runtime); + + runtime->_initialized = 1; } PyStatus From 00afb9070bc53b56cca3930942c3b92bc44d9ad6 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 11 Jul 2023 14:27:33 -0700 Subject: [PATCH 21/28] Sort include list --- Python/pystate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pystate.c b/Python/pystate.c index a62222c4db53a7..b7d0adbfc37159 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2,10 +2,10 @@ /* Thread and interpreter state structures and their interfaces */ #include "Python.h" -#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() #include "pycore_ceval.h" #include "pycore_code.h" // stats #include "pycore_dtoa.h" // _dtoa_state_INIT() +#include "pycore_emscripten_trampoline.h" // _Py_EmscriptenTrampoline_Init() #include "pycore_frame.h" #include "pycore_initconfig.h" #include "pycore_object.h" // _PyType_InitCache() From 20ad6abbd0a8f470f7f31986895e282961ddaba2 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Tue, 11 Jul 2023 14:34:58 -0700 Subject: [PATCH 22/28] Follow pep 7 --- Python/emscripten_trampoline.c | 62 +++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/Python/emscripten_trampoline.c b/Python/emscripten_trampoline.c index 77eddd3cb16eff..2a80ec4f18d757 100644 --- a/Python/emscripten_trampoline.c +++ b/Python/emscripten_trampoline.c @@ -1,6 +1,6 @@ #if defined(PY_CALL_TRAMPOLINE) -#include // EM_JS, EM_JS_DEPS +#include // EM_JS #include #include "pycore_runtime.h" // _PyRuntime @@ -10,19 +10,24 @@ * https://github.com/GoogleChromeLabs/wasm-feature-detect/blob/main/src/detectors/type-reflection/index.js */ EM_JS(int, _PyEM_detect_type_reflection, (), { - return "Function" in WebAssembly; + return "Function" in WebAssembly; }); void _Py_EmscriptenTrampoline_Init(_PyRuntimeState *runtime) { - runtime->wasm_type_reflection_available = _PyEM_detect_type_reflection(); + runtime->wasm_type_reflection_available = _PyEM_detect_type_reflection(); } /** * Backwards compatible trampoline works with all JS runtimes */ -EM_JS(PyObject*, _PyEM_TrampolineCall_JavaScript, (PyCFunctionWithKeywords func, PyObject *arg1, PyObject *arg2, PyObject *arg3), { +EM_JS(PyObject*, +_PyEM_TrampolineCall_JavaScript, (PyCFunctionWithKeywords func, + PyObject *arg1, + PyObject *arg2, + PyObject *arg3), +{ return wasmTable.get(func)(arg1, arg2, arg3); } ); @@ -31,14 +36,16 @@ EM_JS(PyObject*, _PyEM_TrampolineCall_JavaScript, (PyCFunctionWithKeywords func, * In runtimes with WebAssembly type reflection, count the number of parameters * and cast to the appropriate signature */ -EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), { - let n = _PyEM_CountFuncParams.cache.get(func); - if (n !== undefined) { +EM_JS(int, _PyEM_CountFuncParams, (PyCFunctionWithKeywords func), +{ + let n = _PyEM_CountFuncParams.cache.get(func); + + if (n !== undefined) { + return n; + } + n = WebAssembly.Function.type(wasmTable.get(func)).parameters.length; + _PyEM_CountFuncParams.cache.set(func, n); return n; - } - n = WebAssembly.Function.type(wasmTable.get(func)).parameters.length; - _PyEM_CountFuncParams.cache.set(func, n); - return n; } _PyEM_CountFuncParams.cache = new Map(); ) @@ -52,23 +59,24 @@ typedef PyObject* (*three_arg)(PyObject*, PyObject*, PyObject*); PyObject* _PyEM_TrampolineCall_Reflection(PyCFunctionWithKeywords func, - PyObject* self, - PyObject* args, - PyObject* kw) + PyObject* self, + PyObject* args, + PyObject* kw) { - switch (_PyEM_CountFuncParams(func)) { - case 0: - return ((zero_arg)func)(); - case 1: - return ((one_arg)func)(self); - case 2: - return ((two_arg)func)(self, args); - case 3: - return ((three_arg)func)(self, args, kw); - default: - PyErr_SetString(PyExc_SystemError, "Handler takes too many arguments"); - return NULL; - } + switch (_PyEM_CountFuncParams(func)) { + case 0: + return ((zero_arg)func)(); + case 1: + return ((one_arg)func)(self); + case 2: + return ((two_arg)func)(self, args); + case 3: + return ((three_arg)func)(self, args, kw); + default: + PyErr_SetString(PyExc_SystemError, + "Handler takes too many arguments"); + return NULL; + } } #endif From 1b6b6af1a9a66bccb9a8795679b7999e9acfad99 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 20 Aug 2023 20:44:29 +0200 Subject: [PATCH 23/28] Move wasm_type_reflection_available to end of struct --- Include/internal/pycore_runtime.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 082c2e6e103308..4c5e84d2be307e 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -165,12 +165,6 @@ typedef struct pyruntimestate { /* Is Python fully initialized? Set to 1 by Py_Initialize() */ int initialized; - -#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) - /* Choose between trampoline based on type reflection vs based on EM_JS */ - int wasm_type_reflection_available; -#endif - /* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize() is called again. @@ -273,6 +267,12 @@ typedef struct pyruntimestate { /* PyInterpreterState.interpreters.main */ PyInterpreterState _main_interpreter; + +#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) + /* Choose between trampoline based on type reflection vs based on EM_JS */ + int wasm_type_reflection_available; +#endif + } _PyRuntimeState; From 381bb126a3948c9dc30dddf27725a4d988e8ffa1 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 20 Aug 2023 20:48:32 +0200 Subject: [PATCH 24/28] Address review comments --- Include/internal/pycore_runtime.h | 5 +++-- Python/pystate.c | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 4c5e84d2be307e..481216abb0a94f 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -269,8 +269,9 @@ typedef struct pyruntimestate { PyInterpreterState _main_interpreter; #if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) - /* Choose between trampoline based on type reflection vs based on EM_JS */ - int wasm_type_reflection_available; + // Used in "Python/emscripten_trampoline.c" to choose between type + // reflection trampoline and EM_JS trampoline. + bool wasm_type_reflection_available; #endif } _PyRuntimeState; diff --git a/Python/pystate.c b/Python/pystate.c index e45ee28103b265..eca51328ecd289 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -452,7 +452,9 @@ init_runtime(_PyRuntimeState *runtime, runtime->unicode_state.ids.next_index = unicode_next_index; +#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) _Py_EmscriptenTrampoline_Init(runtime); +#endif runtime->_initialized = 1; } From 58c76132d1ada60a5d41800f6627f80b9b41b9c9 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 21 Aug 2023 23:03:29 +0200 Subject: [PATCH 25/28] Update Include/internal/pycore_runtime.h Co-authored-by: T. Wouters --- Include/internal/pycore_runtime.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 481216abb0a94f..16b2d8b6f150e6 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -165,6 +165,7 @@ typedef struct pyruntimestate { /* Is Python fully initialized? Set to 1 by Py_Initialize() */ int initialized; + /* Set by Py_FinalizeEx(). Only reset to NULL if Py_Initialize() is called again. From 22a8c755136725ede5df13e1e5f04cf7bb78ec91 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Fri, 1 Sep 2023 17:18:36 -0700 Subject: [PATCH 26/28] Fix merge --- Include/internal/pycore_object.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index dfe1ac2f90495e..2d50f42c9c614d 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -455,6 +455,10 @@ extern int _PyObject_IsAbstract(PyObject *); extern int _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method); extern PyObject* _PyObject_NextNotImplemented(PyObject *); +// Pickle support. +// Export for '_datetime' shared extension +PyAPI_FUNC(PyObject*) _PyObject_GetState(PyObject *); + /* C function call trampolines to mitigate bad function pointer casts. * * Typical native ABIs ignore additional arguments or fill in missing From 4aa148735cf2fe35868d544eb80a7a2cd87862de Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sat, 9 Sep 2023 09:35:10 -0700 Subject: [PATCH 27/28] Test setting ac_cv_libatomic_needed=no --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 35fe87f709e8b4..7232c1f47c9ff6 100755 --- a/configure +++ b/configure @@ -27774,7 +27774,7 @@ then : else $as_nop if test "$cross_compiling" = yes then : - ac_cv_libatomic_needed=yes + ac_cv_libatomic_needed=no else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ From a57dfdca5e460da979b25f533b08ec16d264fbff Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sat, 9 Sep 2023 14:09:44 -0700 Subject: [PATCH 28/28] Set libatomic_needed=no if cross compiling --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d146cec1b46167..1f24de5831fc66 100644 --- a/configure.ac +++ b/configure.ac @@ -7009,7 +7009,7 @@ int main() } ]])],[ ac_cv_libatomic_needed=no -],[ac_cv_libatomic_needed=yes],[ac_cv_libatomic_needed=yes]) +],[ac_cv_libatomic_needed=yes],[ac_cv_libatomic_needed=no]) ]) AS_VAR_IF([ac_cv_libatomic_needed], [yes],