Instead of having a single class per instruction, I can always split it up into groups. Then this way I do not need to worry much about having tons of classes. However some operations may be more difficult to find. However, I would say that having less classes with more meat would be the better alternative than very slim classes. It would require switch statements in the classes though. Those 256 classes would count twords the total class limit I will be imposing on the VM. Right now I have 250 classes total in all of my sources (well, 250 .java files). Plus some nearby operations have similar function and such. It would also mean that the cache reference array is a bit smaller too (which can be a good thing). There would also be less potentially stale reference objects in memory also. So if I set the shift to say 4, then I only need 32 elements.
I do not actually need unique IDs for basic optimizations. I can just have an active stack slot so to speak which points to preallocated registers and such. So I will need a list of native registers and such.
I will need a kind of a virtual working system which is lazy. Basically I work with virtual registers which may be in locals or on the stack, then I determine operations which are to be performed on them in a linear sequence. When the registers go away (they are overwritten) then they are stored before they are destroyed. Probably do not need to store them when memory barriers are hit because these are just registers. So instead of generating code that just copies from one place to another, adds some values, then stores that result into a temporary. It is basically just:
- Read int value from local #4, push to stack
- Push 2 to the stack
- Add the top two stack elements (virtual stack, and virtual local #4)
- The result is stored on the virtual stack position.
The result will exist purely virtually and will not be stored by default in
most situations. However invoking a method will require that the value is
stored somewhere if it is not consumed by the method call. Well, actually I
might not even need to use the value at all. One could write byte code which
adds a bunch of values together and then just
pops the result meaning it was
pointless. So even during method calls I do not store it. The only time I have
to actually calculate the value is if it gets stored into a field (instance or
static), called into a method, thrown, allocate a new object, allocate a new
array (possibly even multidimensional), entering/exiting monitor. If some
operator chains are simple say byte code adds 2 + 2 + v, I can just turn that
into 4 + v. So if a bunch of math is performed on operations, I only really
have to perform the work when it becomes visible to the outside of the method.
This can be used for optimization and removing the bulk of Java's stack like
nature and could result in smaller code being generated. It would require
however that the position of exception handlers and the stack map table be
known for optimal work. Because if values are calculated and it just turns out
they are never worked with because some exception is tossed... Well actually
if they are in a local they will stick around to excpetion handlers. So for
each possible local variable state exists, there has to be one for each handler
of exceptions. So it is possible that operator chains can be ambigious in
this event. So I will have to specially handle exception handlers and operator
chains. So I propose that if a local is written to when there is an actual
exception handler that is present, that the operator chain is calculated. If
there is no exception handler and then suddenly there becomes one, then all
the operator chains associated with local values are calculated and code is
generated for them.
Essentially this results in operator chains for register values, which can be used to determine how values are used and such.
I made a base class JVMNativeRegisters with stuff in JVMProgramOutput, so I could likely simplify things a bit. I could have special program outputters which can virtualize int/long/float/double operations if not supported. This would be needed for 8-bit and 16-bit systems where 32-bit integer math is not supported at all.
JVMNativeRegisters and the related stuff to it right now is
a mistake before the operator chains.
JVMNativeRegister goes by for now.
What I need to determine next is the best way to store the operation chains. I also need a way to minimal represent the entire state of the program with reduced memory usage. Currently I was planning to use a map which stored a value for each logical operation and its entire state. However, some states do not really differ from other operations. For example, if I just add value to the stack, there is only a single change which is that added value. So what I currently have would essentially copy the entire state when a small difference could be done instead. Although there would be a slight speed penalty, there would at least be less memory usage. It would be a very small amount of memory saving, but it could be just enough for it to be worth it. It also would not require massive redundent copies of everything also. Then with this state class for all the operations I can have the operator chains for all of the variables added also. Usually only a single instruction will change values, so I would not need to for example have all these copies of the same operator chain.
Another thing to consider with operator chains would be to instead of having
it such as
a = 2 + x + y + z, it is instead
a = 2 + x,
b = a + y, and
c = b + z. This way previous operator chains could be changed while still
having the same value at the very end. When a calculation is performed and a
value is written then the operator chain would drop down and potentially be
replaced. So they must be mutable for those given operations.
Collections have binary search algorithms, I should just
use that to find things instead of inventing my own stuff.
Actually I could do a form of SSA with
JVMProgramState with the operator