[GR-20949] Non-array arguments in Truffle languages, to avoid boxing in CallTarget and Frame
Could it be possible/valuable to add some Object getArgument() to Frame to go alongside the existing Object[] getArguments()? With a corresponding new single-argument method on CallTarget.
There are reasonable language designs for which the extra indirection of boxing everything in an array is unnecessary or unsuitable. For instance:
-
A language which only supports single-argument functions (perhaps allowing the parameters to be declared as a destructuring pattern so that multiple-arguments can be simulated if desired).
-
A Lisp dialect which permits improper argument lists, i.e. lists with an extra item in the final cdr position instead of nil. An array isn't really an appropriate representation for this.
Even for a language which does expect a list of arguments an array isn't always the best we can do. If we have the opportunity to choose an appropriate carrier type for the whole arguments list this can allow us to flatten primitive arguments into it. Then there need only be one unnecessary indirection to fetch the whole arguments list object, rather than an unnecessary indirection for every primitive argument as is currently the case. (This also may allow for further improvement in the presence of inline types in some future version of the JVM.)
Is this feasible? Are there other, better paths towards supporting some measure of specialization/flattening in call targets?
Perhaps not having the shared "calling convention" of argument arrays between languages is a detriment to interop? (But we do have the InteropLibrary notion of arrays, so why not rely on that instead of a normal array?)
Tracking internally as Issue GR-20949.
We are already performing some of the mentioned optimizations on SVM EE.
On SVM EE the Object[] is virtualized the call site and the callee. Any Java primitive will be passed by value instead of reference, hence there is no Java boxing overhead at call boundaries. We have ideas to support more values like this. E.g. values that are annotated with @CompilerDirectives.ValueType but I cannot provide an ETA on this.
We had plans to implement a similar feature for HotSpot but it turns to be a very complex one, which we did not get around to implementing yet. It is unfortunately also not planned atm.
Could it be possible/valuable to add some Object getArgument() to Frame to go alongside the existing Object[] getArguments()? With a corresponding new single-argument method on CallTarget.
This would be beneficial if the parent frame is the only argument that needs to be passed. So far in most languages that is not the case and they need to pass additional values anyway, so passing the Frame as an extra argument is not really beneficial. In fact in many situations you don't want to pass a parent frame at all. Typically we do this by not passing it speculatively and then patching a flag at the call site using Truffle.getRuntime().iterateFrames the first time a parent frame is needed. This not just eliminates the need for passing the frame it also avoids materialization / keeps the parent frame virtual.
Perhaps not having the shared "calling convention" of argument arrays between languages is a detriment to interop?
In interop most Object[] arrays are virtual and should get escape analysed unless their length is not constant. If you have a concrete example where this does not work, I might be able to provide a recommendation.
I guess what eliasvasylenko means by "If we have the opportunity to choose an appropriate carrier type for the whole arguments list this can allow us to flatten primitive arguments into it." is that e.g. I can have a family of classes Args_n_m with n Object fields and m long fields, then choose the appropriate Args class for each function depending on its type or maybe even dynamic arguments shape. I'm using something similar to this for closure environments and data constructors for haskell.
I guess the idea is it might be relatively easy to add support for?
I guess the idea is it might be relatively easy to add support for?
Yes that would be relatively easy(tm). But not sure this is what you want and need. There are certain advantages to using Object[]. I will try to explain what i mean.
With Object[] we automatically virtualize on the caller and callee side, as well as we automatically inject the exact argument profile at the callee side. We can only do this because it is an Object[] and we know how to reallocate an Object[] at the callee side. The unsafe cast we are doing at the callee side is something a language implementation cannot do. Our goal is to make language implementations unsafe free, so adding such API is not really something we want to do.
With your own arguments object we would not be able to inject argument types into any of the argument accesses. With bytecode generation one could create dynamic signature classes that contain concrete types. But that is not really usable on SVM, so we don't really think of this as an option. However, we are currently developing a static object model, that will allow us to do something similar also on SVM, that we could maybe leverage in the future also for arguments. cc @ansalond @gilles-duboscq
@acertain how do you create your argument classes? Are you generating the classes using bytecode generation currently? Or do you have a small fixed set of argument classes that you would use? The latter would be an argument (no pun intended) for maybe supporting this already without the static object model in place.
I think our best bet at solving argument signatures more efficiently is the new static object model. That one will generate classes on HotSpot and have special support on SVM. The space required will be as efficient as for manually created Java objects and it would allow us to inject the argument profile for argument accesses. Unfortunately the new object model will need a few more months to mature until it would be ready test it for argument signatures.
Tracked internally here GR-20949 (not a link)