Thinking about it, I only need to setup registers via the allocator for
input arguments and such when they are primed. I also need to consider the
stack. I do wonder though if I need a sub-variant of the OS to choose the
calling convention. Such as instead of generic I have eabi or o32. These would
just be generic targets for the most part. When it comes to a specific
system such as the CI40 or GCW it can use the appropriate ABI as such. So
instead of say
linux.generic, for MIPS there will be
selecting the EABI calling convention.
Thinking about it, adding support for new systems is currently complex because there needs to be all these classes and packages declared across a large range of areas. Adding base PowerPC projects adds another 6 projects. Essentially with my given goal of literally being write once and run anywhere, there will quite literally be perhaps 1000 projects. This would be an unmaintainable mess. Many systems share the same thing, but just have a few differences such as the ABI that is used and how the target is built. There are also emulator considerations also. Right now the emulator for one is split across many packages for each CPU and such.
What I propose then is merging all of the architectures and variants of a
given OS and just put it into a single package. So that
every Linux system and all of its variants. Also the
TargetBuilder as it
stands is a bit ugly and will not work as much either. That has to be adjusted
a bit for it to work. But merging the emulator into a single set would be a
good choice because it would all be compacted into a single place. Although
not as modular, it can essentially be a multi-system emulator so to speak. The
purpose of the emulator is to just test targets for the most part.
I also am a bit distracted, I should be implementing the emulators first before I target a given system, so I can work out how it works and such. The emulators do not need to be very complex. However, since emulation is part of the core of target system building, I should refactor the target build system. I am thinking of a just essentially an enter build. Say the user requests a given triplet, it will go through all services and try building one that works. I can have a special exception indicating that the given target is not supported for the given builder. I thought about removing the OS variants, but that would be needed in cases where the operating system calls differ for the same target (such as MIPS o32 and n32 for example).
So as it stands right now,
TargetBuilder is essentially a factory and its
methods are called as if they were static. This needs to change because it is
very ugly and complicates things.
One thing I can do is move the "can it JIT?" to the build instance instead
of having it in
TargetBuilder, because it may be possible for the same OS
which has a variant which does not support a JIT (perhaps a ROM based Linux
system that will never run JARs).
BuildConfig should get
PackageList so the build instance can pluck any
packages that are needed.
However, I would have to do some adjusting for that, eventually.
My new main looks much nicer.
I do like how this code is turning out.
Had an idea for the registers. Right now they are associated with and they return stuff such as floating point. I was thinking of having some kind of register association kind of setup. One that is set by the API completely instead of one set by the registers. Then this way I can have cases where I have 32-bit integer registers and 64-bit floating point ones, and vice versa.
I do wonder though if I could actually remove
JITOutputFactory now and just
use the output classes directly.
It is a bad idea and there is no reason for
MIPSABI to cache the ABI anymore.
So specifying the registers sizes in the ABI should work much better. There
can also be a means of specifying which registers actually exist for a given
ABI also. So for example on PowerPC with a G4, there will be AltiVec registers
and those can be added, while on older CPUs such as the G3 they cannot be
valid. The same thing happens with MIPS because newer revisions have removed
HI registers (used for division). So in short, having the types
GenericRegister would be a bad idea. I plan instead to just
have it where it can be a
GenericABI.getRegisterIntType() kind of thing.
So currently, the soft, hard, and anything in between stuff is rather bad. There are CPUs such as MIPS ones where there can be no FPU, or the FPU can be 32-bit on a 64-bit system or 64-bit on a 32-bit system. The floats do not have to even match at all. So I believe I will change this to:
Software will be purely in software, while hard32 will do
float in hardware
double in software. The last
hard64 can do both kinds of floats no
So I did much refactoring today, although still not yet done.
So now I just have to move over the code that exists in
refactor that into the new
I am going to want to hide all of the internal builder stuff which should not be exposed at all.
So the next thing to do would be to setup the
JITOutputConfig from the
input. That can be handled by a method for the most part. Soon I would have
worked on SquirrelJME for 10 hours, despite having barely any sleep at all
last night. However the refactor is going well, hopefully tomorrow I can finish
the refactor if I do not finish it today. Otherwise I should work on the
emulator also, although I may work on the JIT.
So as I said before, I can use the registration system to not need service
JITOutputFactory, although the factory is still needed.
Also removing the "supports config" would be good too, this would prevent double checks and such.
So compilation wise, I am at the same point. However since I currently TODO in code I cannot get to the linker stage yet. However since I want to get rid of the old crusty code, I will bring in the linker part.
However, I can always wait for that, I do have version control and I can see
all of my old code. So I will set a note. Revision
24503a462c85ef804c8fe8d008ee538ccc21bb02 will contain the old information
needed for linking and such.
And the refactor is complete for the most part except for the linking part, but I can do that when I get the JIT code done. I should however next get the emulator working and running the simple hello binaries I have.
Ok, so for the emulator, I can have a similar registration system that I have for the configuration. Basically I can have common stuff across architectures and such. Take for example mount points on Linux, this can be handled by a POSIX mount point class. I can then use this for other POSIX-like OSes too. So instead of rewriting it over and over for each OS when it is the same, it will cut down on code duplication. Then I can get rid of the OS and architecture specific configurations and just have a single one. For protection I can make it immutable also. So basically, since many details on a target system are unknown and vary widely across systems, this would be the most sane choice because then there would be unimplemented stuff or things that throw exceptions saying it is not supported. So for sanity, this would work out well. I would then suppose that I need a basic CPU setup that can run instructions, and then have a initializer and a undefined behavior trapper. So when a system call happens in the CPU code, a special handler handles it. This way there only has to be a single CPU emulator for each system. So similar to what I planned before but easier to work with. Then it should be much easier to add emulation of other systems to see how they sort of work out before I support linking and code generation for them. One thing I can do though is have a bunch of source code that implements a large number of operations, basically a test binary. I can run this on the host system and also in the emulator. I would most likely stick to just CPU instructions however. Although one issue would be the fact that some CPUs vary. Regardless, writing the emulator will help give me a feel out how to generate code.
Then for the register allocator, the first thing would be to prime the
arguments. However, now that I have
GenericABI I can essentially just for
the most part assign every local and setup the initial stack as if the method
were entered. So I essentially refactored two sets of code. The good thing is
that right now is that I do not believe the base JIT needs refactoring at all.
The generic JIT just needed new register handling. But now that register
handling should be much easier to work with and should scale better.
So I believe first I will target Linux MIPS and PowerPC. Then once I have that setup, I shall implement the bulk of the library. Implementing the library will be a bit tedious, but if I stick to what I use then I can get away with not implementing parts of it. Most of the basic CLDC library is the collections though. After that would be MEEP which is a bit more complex. One thing I will have to consider is the LUI code. I will be needing my own terminal emulation code along with perhaps PTYs. I suppose for starters I can support the vt family since that is pretty much a defacto standard.
So to recap, the current tasks would be:
- Priming the arguments in the JIT.
- Writing the emulator.
These should not be difficult at all since their algorithms are quite simple. Hopefully in two to three weeks I can have a sort of demo, even if it just prints text to the console. And since I currently lack sleep, I shall be sleeping very soon now.