Looks like I forgot to commit build instructions.


I should likely rename the build system stuff from hairball to something more noticable.


Actually with this operation code, I can easily change operations so that they become other operations. For example if a get is made of a field which is final and has an assigned value, it can just be turned into a sipush or a LDC instead of a field read. However for fields, the instances would need to be popped also, so they cannot cleanly be aliased. However for static fields this is always the case. Constant values are always constant. I suppose for instance field reads which are constant like this, I can have it so a null check is performed, then the value is placed onto the stack accordingly. This would require new instructions to be defined however which are not in normally valid programs. However, since my instructions are of any range, I can instead just use really high impossible values for the operations since these operations would have no representation that is possible. Also, I can do the same for LDC operations so they are just a constant value push for the most part. If I can remove the need for the byte code to require the compiler and interpreter to access the constant pool, that would be a bonus.


I can also replace other instructions also. The byte code program has access to the lookup and everything is statically determined. However, some instructions can operate on unknown objects such as instanceof. However, the compiler could detect if an object would be an instance in specific cases and then just push a boolean to the stack. So when a NRProgram is executed there would never be an instanceof check on known objects of a given class type. Since everything is statically determined, optimizations are easier to make. When it comes to run-time JIT and cache however, I would likely compile the entire program first and then link it in as an addition to the current JVM. Although this would be a slow initial start, everything is compiled at the same time which means whole program optimization is possible. Then that compilation is cached so that recompilation does not have to occur again. However if the JAR or any of its dependencies change, then the entire program must be recompiled again before it safely can be used. So basically the JIT can be seen as a collection of JARs to native program. So for example on an N64, the binary would be generated, then it would be loaded into memory. Any memory that remains is available to programs to use. The whole program optimization could also merge methods together so that for example my large bulk of TODO throwing code essentially is merged into a single method which would save space.


What I can do instead of a large number of push operations for constants, I can instead just change them to a single push of a value. Then in the interpreter and compiler I can just check the value to be pushed and then handle it accordingly. This would save conditions in the switch because a large number of the operations are for the most part duplicates. This would also result in much smaller code since the cases would be condensed. Then for stack shuffling such as pop and push, I can instead have a generic stack shuffling operation which pops all the desired values, then pushes them based on the input values. All of the pop, push, and dup variants would then use this single shuffle mechanism.


Also, the byte code representation could optimize exception handling for example. For example athrow could be represented as clear the temporaries, place the given item onto the stack by itself, and then jump to a given address. Also thinking about it, the byte code representation could perform simple operations also somewhat. So the byte code representation could be a very early optimization sequence where easily optimized things can be placed. So there could be some stack caching and NOPs in place of operations. However at first I suppose I should perform the obvious operations. At least with a potentially optimized athrow it might be done. However, I suppose to maximize basic byte code optimization I also need to know the class types of values on the stack and such, beyond of whether they are just objects or not. Specifying the object types would be very useful in the optimizations I am talking about. The stack and local states could also have that kind of optimization where values are null and such. The types would also be handy during generating NRProgram.


So to recap, give types and potential values for stack entries in the byte code representation. This information could then be used as a kind of optimization and cache. This would complicate the byte code representation however. I would suppose for ease and simplicity that I should not do this and keep the byte code mostly pure in representation. Optimizations such as this could be laid out when NRProgram is to be generated. So if an optimization is not very easily performed then it should not be performed. This would make the initial loading of the byte code simple and closer to the specification. Writing the more difficult optimizations would mean that they would have to be tested. However the byte code representation would only really be used at the early stage. When it comes to actual native execution, the compiler will never be called because everything would already be compiled when a program is ran.


I will need to calculate jump sources.


Alternatively I can precalculate the stack data using basic simple types and such then have the full information when needed. Alternatively, the stack state of the input operation if it is not implicit is the one that precedes it.


Even though I do not have value type or multiple return values I can instead use an array for information passing and initialization of operation information.


I completely did not realize that today was Friday the 13th.


Then with the stack operations, this means that stack shuffling can be done in a single operaton, which would save some time.


So now back to invoke special again. Although this time I can have the method to execute for virtual, special, and interface calls handled by the byte code representation rather than the interpreter/compiler itself.


Definitely did not write that much code today, distractions and such.


In fact, for invocations they can all be treated as if they were static. Well not really because of null pointer checks.