Ok so, the actual key code could have the processing information which generates instructions needed for the target. So for example one which uses byte code instructions would read the byte code and handle those things as such and place them into basic blocks. A zone key which uses the exception table would generate instructions using another means.
This means that zone keys are comparable to each other too.
Thinking about it, I likely do not even need
the zone and entry state could be represented by a single object.
But it would probably be easier to use it if the state maps were not a map
and it was just in the program state because then that means there is just
a reference to a single class:
But this would mean that there would be zones which are not known about potentially and then there might not be easure.
However, there would be a
jumpTo(ZoneKey, ImmutableVariableState) method
which creates and returns a basic block to be queued. However, in reality the
basic blocks could be mostly immutable. The queue would operate with block
keys which have a zone key and an immutable variable state. Rather than
operating purely in basic blocks.
The JIT is a really large package and I believe combining everything into a single package is a bit messy. Not everything needs to be directly connected to each other.
I could also probably make it so the information in a method is contained
within a single class, perhaps
ByteCode rather than having a large number of
classes for having the raw method code and other such things.
Also thinking about it, the exported symbols and such might not need that
strong handling they currently have at run-time. At run-time the linker really
only needs class names, field name/type, and method name/type. So the
linkage could very potentially be folded down into a much simpler form that
has less information contained within it. The same could be said for method
and field references too.
The regions of the program could be within
ByteCode instead instead of
having the code zones and such. I could also move the instruction out of
ByteCode into its own class and use private means of accessing parts of the
Allocation could be changed somewhat. Potentially even
combined into one. For example some 8-bit targets or strange targets might
have a clear mapping of how things are represented. For example, pointers to
other regions of memory might not have a clear cut representation. So for
example on the 286, pointers could be 24-bit in that they use a segment
along and a 16-bit memory address. So they cannot really be represented using
a single integer variable to be added and subtracted from normally. So I need
a unique pointer native type. Also another example could be that some targets
might not support registers containing shorter or longer values, just a
fixed size. Since I definitely do plan to now target the ZX Spectrum Next
which uses a Z80 I am going to need special pointers to refer to different
banks of memory. So this plain treating pointers as integers would be really
messy for these targets. I also do plan to support many wide targets so I
would essentially have to rework everything just to handle pointers.
So basically there would be a "give me a native type which can store the specified type" and force objects to be pointers essentially.
The classes do need flags however such as for interfaces, if they are final, and a few other things. That is needed for linking. Members also need those too, so there is probably not much I can do at all. However what I could do is remove a bunch of the preloaded constants and make them not exist at all. They really have no reason to exist and were just for making less objects visible. Also that information is only needed during class recompilation and runtime linking. However, this is only needed if there is an actual JIT because otherwise for targets without a JIT, everything is already prelinked into a single executable where nothing has to actually be checked. So this means that the symbols go out of the symbol package and instead go straight into the JIT where they can be molded to be simplified. So anything related to linking will require the JIT.
ByteCode is going to get the regions and such instead of the
native generator part of it.
It would very likely be far easier to create a new JIT project and move all the old code into that rather than trying to fix up pre-existing JIT with all of its old code.
ExecutableClass could probably be removed or changed a bit. At least
initially the interpreter could be ran as if there were no JIT installed at
all, so it is really an interpreter for things.
So there really is just a pointer for the most part, then classes could potentially be installed to be executable and such.
So register dictionaries could be forked into two specific things, a fixed set of registers or a dynamically generated unlimited set of registers. Both could be used in many cases.
Instead of saved/temporary requiring to be requested that can be used in the
register itself and associated with it. That is there is no
isRegisterTemporary(), it is just
Register.isTemporary(). Any and all
registers have to be saved before method calls so the condition for argument
registers could be avoided for example. However, alternatively there could be
a mapping for which registers are available. Would have to think about this.
There is stack caching, but it might not fully matter where registers are.
In reality though, the register handling could be done by the target specific
JITs rather than the JIT itself. Basically there would be an allocation
mutator which changes state according to the registers available and such. So
this would mean that the
VariableState and such is linked to this class.
But with an infinite set of registers for some targets, all would have to be
pushed to the queue for it to wait on registers for usage.
One thing though is that I seem to be going in circles finding the best solution to things. It has been a year of me writing the JIT over and over again to find something that works. Compilers are really messy, but the more I do it the more I learn.
Ok so if having a register dictionary does not work for a target and there be a variable state kind of thing in the native code, then I would say that it is similar to a dictionary but it pulls registers for usage.
I can however have a combined linking stage. My previous plan when creating combined executables would be to perform the linking step after all the individual binaries have been compiled. However one thing I can do is do something similar to what I have before and just have a single export/import table which the JIT will place things into. Basically there would be imports, exports, and it will maintain a list of unresolved imports. When an import is resolved it will not be in the unresolved list and will have a direct link to the export. This would work for the kernel itself and any built-in classes. Dynamically loading in new JARs would have to be done similarly, but the dynamically loaded classes would require the JIT itself. However one thing that changes in this case is the fact that some JARs could depend on a separate group of classes under the same name. Basically, there is a separation which could allow different versions of the same class to exist at any one time.
So the question is, can multiple versions of the same MIDlet exist at the same time? But, one consideration here is that right now I am talking about the kernel, in virtually every case there will only be a single version of any MIDlet installed. So effectively this is a non-problem. In the future when dynamically loading and linking JARs are supported, it could perform a similar means of having a duplicated link table that links into the kernel. Basically JIT the entire class, then for any unresolved imports just pull from the kernel. But the thing is, all classes pull from the kernel first when it comes to importing. So for system classes there can never be duplicate ones or JARs with different versions anyway. So this issue does not exist at all. So this means that the JIT has a global link table which is compiled into.
So this means that the interpreter changes to instead of loading and interpreting a single class at a time, it changes to instead be a compile of everything and then just running that thing. So I would say that the interpreter in this case just runs a single JAR under the interpreter potentially of course with modified runtime stuff as needed. Since the kernel and whatever is running will be prelinked.
So in this case, do I get rid of the interpreter and just have a native executable that is output? This would be similar to how I have done it in the past. This would handle the single link table, although testing the generation of code would be done after everything is compiled and linked together. I can remove the interpreter and instead of a very direct route to how code is ran. So basically I inspect the output machine code to make sure it is working. I for now drop the interpreter and treat the system and the JIT as if it were a one-shot output and link system.
You know, I am on PowerPC but my initial target is MIPS. I can run both natively (well the MIPS on other hardware), but PowerPC is the hardware I run. So I think I could implement a JIT which targets PowerPC first that way I can run it on my native Linux PowerPC system.
I could write something which runs directly on IEEE1275 which is bare metal on PowerMacs. It is really limited but I could use it as a base system where I can have a very close to the metal environment running. It would require a bit more work because there is no real file system (well there is one but it is read-only). But I think it would be best to get a Linux target working first.
But MIPS is the simplest.
Thinking about it, having the kernel just be a normal program works. For example it is possible for projects to be compiled and executed directly but I think I would want the kernel environment rather than something else.