methods with scalarized arguments
rwestrel at redhat.com
Fri May 18 15:07:29 UTC 2018
> Are you imagining a single nmethod with two entry points? Currently,
> nmethods *do* have two entry points for distinct calling sequences.
> This might add two more: <VEP, UEP> x <Buffered, Scalarized>.
The way the calling convention is implemented in MVT, scalarized
arguments can be in registers or on stack. There are cases where the
scalarized calling convention needs more stack spaces for arguments than
the buffered calling convention. Something like:
m(v1, v2, v3, v4, v5)
a buffered call would have all 5 arguments in registers but a scalarized
call could required stack space for some of the arguments (say if all 5
values have 4 integer fields). So if m() has 2 entry points, the
buffered entry point will need to extend the area on the stack for
arguments so it can put scalarized arguments on the stack before it
jumps to the scalarized entry point. That stack space is in the caller
so that wouldn't play well with stack walking.
Tobias suggested 2 entry points and one calls the other: the buffered
entry point allocates stack space, shuffle arguments, pushes some on the
stack and calls the scalarized entry point. That would solve the stack
space problem but quite likely introduces other challenges (do we emit
the call from one entry to the other at compile time or call into the
runtime and resolve it? Is the buffered entry point apparent in C2 IR or
is it a custom generated blob of assembly? How does this affect stack
walking? Do we want to filter one of the activation of method m() from
the stack that are reported on exceptions etc.?)
Or we compile 2 separates methods which sound like a waste of
resources, requires runtime code to keep track of 2 separate nmethods
for 1 method, runtime logic for dispatching and compilation policy
change to trigger compilation of either one of the nmethods.
If we go with the 2 entry point solution, then null values are never
allowed in compiled code. With the 2 nmethods solution, the buffered
nmethod could support running with null values at full speed (i.e. null
values would not have to be gated so they don't enter the method). And
it doesn't matter if m() is legacy or not. If it's legacy and null are
passed around then only the buffered nmethod would ever be compiled and
executed. If it's legacy and null are not passed then only the
scalarized nmethod would ever be compiled and executed.
More information about the valhalla-dev