Class JitAllocationModel
The implements the Variable Allocation phase of the JitCompiler
using a very simple
placement and another "voting" algorithm to decide the allocated JVM variable types. We place/map
variables by their storage varnodes, coalescing them as needed. Coalescing is performed for
overlapping, but not abutting varnodes. This allocation is anticipated by the
JitVarScopeModel
, which performs the actual coalescing. Because multiple SSA variables
will almost certainly occupy the same varnode, we employ another voting system. For example, the
register RAX
may be re-used many times within a passage. In some cases, it might be used
to return a floating-point value. In others (and probably more commonly) it will be used
to return an integral value. The more common case in the passage determines the JVM type of the
local variable allocated for RAX
. Note that variables which occupy only part of a
coalesced varnode always vote for a JVM int
, because of the shifting and masking required
to extract that part.
The allocation process is very simple, presuming successful type assignment:
- Vote Tabulation
- Index Reservation
- Handler Creation
Vote Tabulation
Every SSA variable (excluding constants and memory variables) contributes a vote for the type of its allocated local. If the varnode matches exactly, the vote is for the JVM type of the variable's assigned p-code type. The type mapping is simple: For integral types, we allocate using the smaller JVM type that fits the p-code type. For floating-point types, we allocate using the JVM type that exactly matches the p-code type. If the varnode is larger, i.e., because it's the result of coalescing, then the vote is for the smaller JVM integer type that fits the full varnode. Consider the following p-code:
1. RAX = FLOAT_ADD RCX, RDX 2. EAX = FLOAT_ADD EBX, 0x3f800000:4 # 1.0f
Several values and variables are at play here. We tabulate the type assignments and resulting votes:
SSA Var | Type | Varnode | Vote |
---|---|---|---|
RCX in |
float8 |
RCX |
double |
RDX in |
float8 |
RDX |
double |
RAX 1 |
float8 |
RAX |
double |
EBX in |
float4 |
EBX |
float |
0x3f800000:4 |
float4 |
||
EAX 2 |
float4 |
RAX |
long |
The registers RCX
, RDX
, and EBX
are trivially allocated as locals of JVM
types double
, double
, and float
, respectively. It is also worth noting
that 0x3f800000
is allocated as a float
constant in the classfile's constant
pool. Now, we consider RAX
. The varnodes for RAX
1 and
EAX
2 are coalesced to RAX
. RAX
1 casts its vote for
double
; whereas, EAX
2 casts its vote for long
. This is because
placing EAX
2's value into the larger varnode requires bitwise operators, which
on the JVM, require integer operands. Thus the votes result in a tie, and favoring integral
types, we allocate RAX
in a JVM long
.
Index Reservation
After all the votes have been tabulated, we go through the results in address order, reserving
JVM local indices and assigning types. Note that we must reserve two indices for every variable
of type long
or double
, as specific by the JVM. Each of these reservations is
tracked in a JitAllocationModel.JvmLocal
. Note that index 0 is already reserved by the JVM for the
this
ref, so we start our counting at 1. Also, some portions of the code generator may
need to allocate additional temporary locals, so we must allow access to the next free index
after all reservations are complete.
Handler Creation
This actually extends a little beyond allocation, but this is a suitable place for it: All SSA
values are assigned a handler, including constants and memory variables. Variables which access
the same varnode get the same handler. For varnodes that are allocated in a JVM local, we create
a handler that generates loads and stores to that local, e.g., iload
. For
constant varnodes, we create a handler that generates ldc
instructions. For
memory varnodes, we create a handler that generates a sequence of method invocations on the
state
. The code generator will delegate to these handlers in
order to generate reads and writes of the corresponding variables, as well as to prepare any
resources to facilitate access, e.g., pre-fetching items from the
state
in the generated constructor.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final record
The handler for a p-code variable allocated in one JVMdouble
.static final record
The handler for a p-code variable allocated in one JVMfloat
.static final record
The handler for a p-code variable allocated in one JVMint
.static final record
An allocated JVM localstatic final record
The handler for a p-code variable allocated in one JVMlong
.static final record
A portion of a multi-local variable handler.static final record
The handler for a variable allocated in a composition of localsstatic enum
A dummy handler for values/variables that are not allocated in JVM localsstatic interface
A handler for p-code variables composed of a single JVM local variable.static interface
A handler that knows how to load and store variable values onto and from the JVM stack. -
Constructor Summary
ConstructorsConstructorDescriptionJitAllocationModel
(JitAnalysisContext context, JitDataFlowModel dfm, JitVarScopeModel vsm, JitTypeModel tm) Construct the allocation model. -
Method Summary
Modifier and TypeMethodDescriptionGet all of the locals allocatedgetHandler
(JitVal v) Get the handler for the given value (constant or variable in the use-def graph)localsForVn
(Varnode vn) Get all of the locals allocated for the given varnodeint
Get the next free local index without reserving it
-
Constructor Details
-
JitAllocationModel
public JitAllocationModel(JitAnalysisContext context, JitDataFlowModel dfm, JitVarScopeModel vsm, JitTypeModel tm) Construct the allocation model.- Parameters:
context
- the analysis contextdfm
- the data flow moelvsm
- the variable scope modeltm
- the type model
-
-
Method Details
-
nextFreeLocal
public int nextFreeLocal()Get the next free local index without reserving itThis should be used by operator code generators after all the
state
bypassing local variables have been allocated. The variables should be scoped to that operator only, so that the ids used are freed for the next operator.- Returns:
- the next id
-
getHandler
Get the handler for the given value (constant or variable in the use-def graph)- Parameters:
v
- the value- Returns:
- the handler
-
allLocals
Get all of the locals allocated- Returns:
- the locals
-
localsForVn
Get all of the locals allocated for the given varnode- Parameters:
vn
- the varnode- Returns:
- the locals
-