All of the invoke operations are essentially the same and since I do not have
invokedynamic at all, I do not have to worry about the complexities
that it brings to the virtual machine.
The interface method reference would probably be best as a method, I do have to check them if an interface is used however.
Stack overflow and underflow would probably best throw an exception which is of a virtual machine exception.
The derivation fallback for the state of operations must not destroy the top of stack elements unless perhaps a specific flag is set.
For method calls which return no result, I will still need to generate a method call for it. Thus the atom will need an operator link itself.
It would likely be easier if I were to generate the SSA and the specified
chains and uniques and such on the fly rather than associating them with the
state of things. Essentially, instead of having operator links for locals,
stack items, and atoms I would instead have a program chain which performs
operations. As for the local and stack variables, I can have a change order
for that position (likely the PC address) which when a variable is changed then
the variable ID is incremented. Then
JVMProgramSlot.unique() would go away
because that points to the individual slot.
JVMOperatorLink would also go
away. This way the stack and locals would just keep their former states and
such. The change order could either be explicit and implicit potentially. If
a jump back is made and variables change, then a phi-function could be placed.
It would work with implicit IDs, because say the locals do not change at all,
then suddenly it does because a jump back is made for a loop, then it gets
updated. An alternative to all of this is have something similar, but where
each slot is still unique, exception a linear set of operations are performed
which describe what a program does. So all operations would reference a
unique variable. However if at a given time a variable has not changed value
then an older one is used in its place.
So for any given address operation, the virtual program would know the operations and the state of variables. It would appear as a waterfall with logs rolling off so to speak. So all operations would use a unique value for one at the given operation and the output of that is for the next operation. So for going backwards when an operation is performed, it must check its inputs to see if there is a change in the output. This would be done recursively for each variable as it is requested. The states of variables could be cached so they can be garbage collected as needed, the variables which do not need to be constant in memory. The ones that do remain constant are the ones which set an actual value.
To recap: input variables (which may be virtual), and an operation. If a
variable is virtual then it propogates up to find its value based on former
operations and such. Virtually all variables would end up being virtual. This
would cut on active memory. I can also have a pool of unique values which are
known to the entire program. I would have to handle situations where variables
are just copied to another place, in that case the unique variable number for
both locations would be the same. Then each unique variable would have a set
of operations which are performed on it. However, with propogation upwards this
would not be needed at all. The copy operation would be the operation itself
and it could just return its input as its output, so the unique variable list
is not needed. Thus if it eats
local#7@n then it will for its stack variables
have there exactly
local#7@n despite being in the stack. This would be done
instead of having its output be
stack#1@n+1. The operation would be cached
and the copy operation is pointless. When code generation time occurs, the
program can allocation registers and stack space. Pointless operations such
as copy would not be sent to the generator at all, unless it were really
Thinking about it (by not thinking about it), I can merge the byte code with this idea I have. I give it a program and instead of my own operations it uses the byte code. However it also has the cached operations and such which do things. First I get the byte code array, then I get the position of all the instructions. Then I run through them. The class can handle caching and such. It would be a hybrid of an interpreter, with caching, and SSA so to speak. It would also be combined into one. I can also have a cache of inputs and outputs. Then when it comes to code generation, I will have an SSA kind of form and I can just iterate over the byte code and generate code depending on the operations. Only a specific set of operations would need to be handled. If some instructions to special things, I can have an external do something class which describes what it does.
Exceptions and other verification details such as the
StackMapTable can be
merged into this.
Just started to implement this, it should in the end result in a cleaner and more pure interpreter in the long run. It would also likely use less memory than what I have done before.
I believe for simplicity in the operations I am going to condense the byte code addresses into list addresses. This would make iteration a bit simpler and all instructions would take up a single address, rather than multiple addresses. However, internally they would still take up multiple addresses.