3 ***DISCLAIMER***: _These notes are from the defunct k8 project which_
4 _precedes SquirrelJME. The notes for SquirrelJME start on 2016/02/26!_
5 _The k8 project was effectively a Java SE 8 operating system and as such_
6 _all of the notes are in the context of that scope. That project is no_
7 _longer my goal as SquirrelJME is the spiritual successor to it._
11 Need to work on Allocation/Allocator changes today. One easy trick I can do to
12 support simpler code is to... well having current thoughts about register
13 saving. Do I caller save or callee save? I can callee save the registers which
14 are used, but that would complicate exception handling. I can caller save but
15 that may cause slower leaf calls and such (if the leaf does not use any of
16 those registers which were saved).
19 * Safer for the current method, the callee might be malicious and trash
21 * Can save only the registers which are used, does not have to save all of
23 * The callee does not have to save any registers before being used.
25 * Faster for leaf-methods if they do not modify many registers.
26 * Must save all registers that are used regardless if a method higher up on
27 the call-stack even uses them.
29 I could always use both though, however it would complicate both forms.
30 Regardless though when it comes to exceptions, both will have to get registers
31 restored. The thing is though about restoring registers when an exception is
32 called in a sub-method. That would require a double "throw" or a double run of
33 the handler finding code however. That would be easier at the cost of
34 exception handling speed. Or better yet, the exception handler knows the
35 location of the registers that need restoring so it can be implicit. This
36 means the stack has to be specially shaped before a method is called so the
37 handler can find the registers that need restoring when a handler is called.
38 There are two cases: where the handler exists in the method, and one where it
39 does not exist. If there is no handler in the current method then no registers
40 need to be restored if caller saved and it can drop out of the method to the
41 one that called it and repeat. If there is a handler in the current method,
42 then the bootstrapper can restore the registers that were set and then execute
43 the handler. I can have a sort of red-zone that is where the current call
44 before a method is done is set and a restore before an exception handler is
45 jumped into. This area can always exist in the method before a method is
46 called. The other values can be just set to zero (unused registers) or some
47 other funny value (0xDEADBEEF?). And that area would just be for all of the
48 caller saved registers that need to be saved before a method is invoked.
52 I will call the saved register area, the green zone. I can also remove the
53 stack top variable and gain an extra register.
57 My assembler is also a bit weak, I am going to need fragments which may be
58 resized as such when the final code is generated. For example, for simpler
59 code generation I will not know which native registers are used (and must be
60 saved before invoking a method) until I reach the end of the method. So the
61 assembler base needs a way to have sub-fragments which may have bits in them.
62 It must also be possible to select sub-fragments and write differing values
63 into each of them. Then in the end when the code is generated, the sub-
64 fragments are used as such and injected when needed. This also means that I
65 will need relocation info for relative stuff. So an address for an instruction
66 must point to another fragment and the offset of entry into that fragment.
67 This way at finalization time the fragments are linked together and addressed
72 Fell asleep for two hours.
76 One thing I could do is keep assembly code as fragments, although that would
77 destroy the ROM nature of things. I can delay linking fragments to when they
78 are actually used. Fragments can then be associated with say methods. For
79 example if there is a loop such as:
83 for (int i = 0; i != str.length(); i++)
84 System.err.println(str.charAt(i));
87 The call to `str.length()` would be a fragment calling that method. If at the
88 run-time the length method is indicated that it is a very simple method (that
89 just returns an internal final int value) then code that obtains the value
90 from that field can be used instead. The same could be done for fields when
91 they are volatile or not volatile.
95 This means the DynaRec will still remain a good portion of the running system
96 to be JIT-like when a fragment is turned into real code at load time. However
97 for some systems (say the Nintendo 64) it might be best to keep some stuff in
98 ROM since RAM is rather limited.
102 One thing I would like is if there were middle ground where I can have both.
103 Ahead of Time is a bit better, but I can still do a JIT with this. But,
104 caching is best as it reduces waste. Forcing a class to recompile because
105 another class had a field that changed volatile state is a bit ugly if the
106 class refering to it has not changed. So fragments that are linked in when the
107 code runs would be the best solution. That would be the slowest load time and
112 I could actually just have two things, a pre-linked where there are no
113 fragments. This would be used for ROM based systems, so that would never
114 inline and would always assume variable are volatile. The kernel would
115 initially use fragments, but then would be linked to remove such fragments.
119 One thing I can do for inline capable methods is to save a macro list so that
120 it may be modified slightly for handling of inline methods. That would require
121 a slight rework in my idea for code compilation though. The macro language
126 So, I believe I am heading now twords hybrid-JIT, where most of the work is
127 AOT (like the kernel and the class library). When the system sees a new class
128 (or a change class), it performs a recompilation on it and stores the result.
129 That result is then loaded by the run-time and checked for areas that need JIT
130 work done. If the class contains methods that need macros still to be run then
131 they are run, then the final result is linked into a binary. That binary is
132 then cached with dependency information (that it uses inlined macros from
133 another class) and then it is loaded and linked into the core run-time where
134 others may link into it. However, the class path may be different each time so
135 there would be tons of caches for every single variation of the classpath. So
136 depending on how long it takes in general, it might not be to bad to do it all
137 the time, although that is very wasteful. My main goal is to reduce waste but
138 also have it fast too.
142 Inlining would increase speed, but having this secondary JIT-like stuff going
143 on would reduce speed. I can code it in a way where I can switch out or easily
144 add this JIT-like system if the AOT system is deemed too slow. I do know that
145 when I get graphics in the future however (I would implement the newly
146 released Vulkan as it is simpler, then layer other graphical bits on top of
147 that), for a software renderer I can compile the SPIR-V code into Java
148 bytecode and then compile that code to native code, or do other similar
153 With the saved/unsaved stuff that I plan for, I do not have to have complex
154 allocation code now. I could just go the simple route and allocation positions
155 for all of the used registers instead. Then doing that, there is no need to
156 swizzle operands, which means the dynamic recompiler is faster at the cost of
157 the code being compiled being slightly larger in stack usage. However I still
158 need to load values which are on the stack into volatile registers and that
159 requires some swizzling, but only for those that are completely on the stack.
160 That only applies to volatile registers though.
164 Instead of setting unknown local variables to null, it would have been better
165 to use a method call of the specified class and type which throws the
166 exception when the object is attempted to be created. Currently 200 classes
167 have such instances. Grep tells me that there are 1,896 cases of this across
168 200 files so changing them all by hand is out of the question. I will need a
169 script that reads the type before hand to calculate the change to be made. A
170 simple slow-ish shell script could work. Read an input file line by line, then
171 determine if such things need to be wrapped or not. Since I have the value it
172 is supposed to be on the next line, things could be simpler.
176 Then I can also change all the standard markers for those classes also so that
177 their respective changes appear in the same commit rather than spreading them
178 out. Although it would probably be best to have a separated script for that
179 which performs a simpler sed. I could slowly change any files that have the
180 old meta information in them on each commit that I make, although there would
181 be the resulting unrelated changes (which would be a bit bad). There are 4166
182 files in the vmjrt directory, where 3948 end in java. When it comes to
183 implementing, it might be best to document interfaces only when needed. The
184 first goal would be to implement compact1 first, then everything else after
189 The output of the completion program could be an ASCII art table with rows and
190 columns. Columns for classes, interfaces, annotations, and total. Rows for the