CCALL a ufuncptr of an exposed Mu function
Problem Description
At compile time, when RPython compiler is transforming the CFG into JIT bytecode
, it creates JitCode
for the callees of regular calls (jtransform.py:433). In doing so, it casts the function pointer of the corresponding graph into Address
(call.py:183), and stores it in the JitCode
instance (call.py:168). In the blackhole interpreter, it gets the function address, casts it to an integer and passes it to a call instruction (blackhole.py:1211).
RPython also supports casting a stored function address back to a function pointer to be invoked. This is demonstrated in the following test code snippet I wrote:
def test_fakeaddress_fncptr():
def f(a, b):
return a + b
def g(a, b):
return a - b
...
fp_f = lltype.getfunctionptr(graph_f)
fp_g = lltype.getfunctionptr(graph_g)
FUNCPTR = lltype.typeOf(fp_f)
fncadrs = map(llmemory.cast_ptr_to_adr, [fp_f, fp_g])
def main(argv):
fncadr = fncadrs[int(argv[1])]
fncptr = rffi.cast(FUNCPTR, fncadr)
print fncptr(int(argv[2]), int(argv[3]))
return 0
The code when compiles through the default C back-end runs correctly.
This raises the question on how Mu back-end should handle these cases.
Some issues raised by RPython's practices include:
casting a function pointer to data pointer
This is generally not a good practice in C. It is an undefined behaviour according to C standard and is conditionally supported in C++11. However, practically it is supported by major compilers on major platforms (the return value of dlsym()
depends on this to work).
For Mu back-end, however, it may not be necessary to perform such cast. It may be good enough just to store the function reference. But we are limited by the RPython source code. This ultimately reveals another mismatch of assumptions. RPython is built with the philosophy of C and there are many assumptions built-in to the compiler itself (especially JIT, since it was designed to emit machine code).
ufuncptr
from funcref
how to get a One way is through exposing the Mu function. Since the described scenario produces the function pointer in a heap object, this can be done statically. Another potential way is through 'pinning' a Mu function. This is raised in the relevant discussion in Mu Spec issue tracker (mu-spec#8).
ufuncptr
from within Mu
how to call a This touches on the semantic of CCALL
instruction. From the discussions in mu-spec#8, it seems that we can CCALL
an exposed Mu function pointer. But at this time the reference implementation has not dealt with this case.
Current Progress
As mentioned above, relevant issues are raised and discussed in mu-spec#8.
Currently waiting reference implementation to deal with this case.
On the RPython side, most of the work has been done. The back-end now can store a ufuncptr
as a result of statically exposing a Mu function; when it loads it back at run time, calling it is done through CCALL.
This is achieved through:
- adding a
_muexposedfunc
value type tomutype.py
; - modifying
graph_closure
function to add the function graph pointed to byllmemory.fakeaddress
to the graph closure set; - type, value and operation translation in
ll2mu
; - database picking up the exposed function
- code generator to declare the exposed function, and handle the case in heap building.
The code is committed on mu-rewrite-aot-jitsupport-issue15-ufuncptr-problem
branch, see commit ec1322ee.