SquirrelJME/SquirrelJME

View on GitHub
assets/developer-notes/stephanie-gawroriski/2016/05/10.mkd

Summary

Maintainability
Test Coverage
# 2016/05/10

## 10:56

For some simplicity the code parser can handle generation of operation data,
one that provides stack operations and opcode arguments.

## 14:01

Actually for image downscaling with weights, say a region to downscale has a
bunch of colors in it. For example it is mostly white. My previous plan was if
that there were even a single pixel of the border color then it would be
border colored. However this may make things a bit ugly especially when it
comes to borders and such.

## 15:44

For `NCPOp` to work better, I will need to know the potential verification
state of the operation. However, if I ignore this then I can have the states
be a bit implicit so to speak. When I go through the actual decoded operations
I can determine if the stack states are correct and such. By then I would know
the start locations of all basic blocks for example. However the verification
state could still be indicated in the operation, using a similar means of
setting the data like I have done before.

## 15:51

Going to look at the statistics for this repository.

## 19:31

I must learn how to draw my own mascot.

## 19:38

The question is however, do all instructions require basic blocks to be
created for them? The basic blocks only cause stack entries to potentially be
cached. So instructions like `new` need it since that instruction can
initialize classes. Also `new` would likely be implemented as a method call to
an allocator. Instructions such as array store would not need them, but
potentially array loads would. However if an array is stored then it just
consumes temporary stack items. If an exception is thrown before the actual
store (out of bounds or `null`) then nothing is done. The same happens for load
also. However other operations such as put of static and get of static should
be in their own basic blocks since they could initialize classes for example.

## 19:44

Thinking about it, a large number of instructions do not need explicit basic
blocks at all. I can pretty much keep it limited to instructions after method
invocations and such and the targets of jumps.

## 19:45

Exception handlers only care about local variables and the stack items are
all temporary anyway. When a method is called, arguments are popped from the
stack. If I start a basic block on the invocation then all stack items are
saved and then they are saved again before the method call. So to think about
it, are basic blocks even needed at all? All stack items are essentially
temporary. If I calculate the jump targets, arguments, and verification info
for operations, I should then be able to just directly create executable code.
As stated before, locals would be written to immedietly for simplicity while
stack items would remain temporary. When a method call is performed, I would
just store the local variables and any stack items which were not popped and
move the values over so the calling convention is statisfied. Provided I
verify that the states are correct for the instructions things can get a bit
simplified. However, thinking about that. I do need basic blocks because of
jump targets. I only need to start a new basic block when execution flows to
another basic block, which saves the existing stack items. Doing it this way
would reduce the complexity of requiring SSA magic going backwards and setting
up phi functions for locals and stack items. So essentially it would just be a
simple register storage mechanism. Each basic block could then have its own
register mapping and stack mappings. So say for example a basic block jumps to
another and the registers/stack entries which are associated with the stack
are not exactly the same they would be moved around. Then if I follow the
write local immedietly rule, then that means I do not have to have a new basic
block after a method call or similar.

## 19:55

Then as before, operations such as push and pop would just have a basic "copy".
If the end of a basic block is reached then the copy is stored on the stack as
needed. The copy would not actually be set in a temporary register unless a
basic block boundary was reached. Then once flow passes to a new basic block,
all temporary register assignments are reset and such. For simplicity with
exception handlers, local variables would be bound to the same locations.

## 20:00

So perhaps instead of a code parser, I would instead have a byte code
representation. Setup an immutable class which is given the code attribute and
can setup operations for everything immutably. This representation would
**ONLY** concern itself with byte code, it does not execute it, it just
describes how it works. Then there is a code generator which takes this
representation and runs the code so that it creates a `NRProgram` with its
basic blocks and such. The byte code representation would still have to save
on memory by caching things and reading in things as needed however.

## 20:06

Then the byte code could be used for debugging such as `javap` or if a JAR file
exists and there is an exception and a debugger wants to be attached or
similar. Rather than just looking at native code it can actually see the
byte code that it is mapped to potentially.

## 21:07

This should in general result in much cleaner code.