I hit the same problem when compiling Cpython with the Cheerp[0] compiler in 2019.
I was also at first trying to patch the code but this practice is so pervasive in python C extensions that it was unwieldy.
My solution was also to write an LLVM pass, but different than the one explained in the article:
I wrote a pass that checks when a function address is taken and then immediately converted with a bitcast to a function type with a different number of arguments.
It then creates an adapter function that calls the original and pass more (or less) arguments to it with default values, and returns that function address instead of the original bitcasted one.
This is not a perfect solution, because in theory one can store the pointer and convert it to a different type later, but in the case of python C extensions this is sufficient to fix the issue (at least in my experiments), and has the advantage of not having any branch, and of not hurting code paths that call the function directly or with the right signature.
I'm confused how this would work. What about METH_VARARGS | METH_KEYWORDS functions? These are declared with type PyObject* f(PyObject*, PyObject*, PyObject*), are cast to PyObject *f(PyObject*, PyObject*) for the PyMethodDef, and then are cast back to their original correct type before being called. Wouldn't your approach generate bad shims for these?
3 comments
[ 4.0 ms ] story [ 13.6 ms ] threadI was also at first trying to patch the code but this practice is so pervasive in python C extensions that it was unwieldy.
My solution was also to write an LLVM pass, but different than the one explained in the article: I wrote a pass that checks when a function address is taken and then immediately converted with a bitcast to a function type with a different number of arguments. It then creates an adapter function that calls the original and pass more (or less) arguments to it with default values, and returns that function address instead of the original bitcasted one.
This is not a perfect solution, because in theory one can store the pointer and convert it to a different type later, but in the case of python C extensions this is sufficient to fix the issue (at least in my experiments), and has the advantage of not having any branch, and of not hurting code paths that call the function directly or with the right signature.
[0]: https://leaningtech.com/cheerp/