| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
| |
|
| |
|
| |
|
|
|
|
|
|
|
|
|
| |
Based on apps/handle.subx (commit 4894), but we're able to simplify it
further now that we know more about the situation we find ourselves in.
6 instructions, zero pushes or pops.
Before I can start building this, I need to reorganize my use of handles.
They're going to be fat pointers so we can't store them in registers anymore.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
At the lowest level, SubX without syntax sugar uses names without prepositions.
For example, 01 and 03 are both called 'add', irrespective of source and
destination operand. Horizontal space is at a premium, and we rely on the
comments at the end of each line to fully describe what is happening.
Above that, however, we standardize on a slightly different naming convention
across:
a) SubX with syntax sugar,
b) Mu, and
c) the SubX code that the Mu compiler emits.
Conventions, in brief:
- by default, the source is on the left and destination on the right.
e.g. add %eax, 1/r32/ecx ("add eax to ecx")
- prepositions reverse the direction.
e.g. add-to %eax, 1/r32/ecx ("add ecx to eax")
subtract-from %eax, 1/r32/ecx ("subtract ecx from eax")
- by default, comparisons are left to right while 'compare<-' reverses.
Before, I was sometimes swapping args to make the operation more obvious,
but that would complicate the code-generation of the Mu compiler, and it's
nice to be able to read the output of the compiler just like hand-written
code.
One place where SubX differs from Mu: copy opcodes are called '<-' and
'->'. Hopefully that fits with the spirit of Mu rather than the letter
of the 'copy' and 'copy-to' instructions.
|
|
|
|
|
| |
This could be a can of worms, but I think I have a set of checks that will
keep use of addresses type-safe.
|
| |
|
| |
|
|
|
|
|
|
| |
I thought I needed to support compute-offset with literal index, but in
that case might as well just use an index literal directly. The 'index'
instruction with literals already supports non-power-of-2 sizes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
If indexing into a type with power-of-2-sized elements we can access them
in one instruction:
x/reg1: (addr int) <- index A/reg2: (addr array int), idx/reg3: int
This translates to a single instruction because x86 instructions support
an addressing mode with left-shifts.
For non-powers-of-2, however, we need a multiply. To keep things type-safe,
it is performed like this:
x/reg1: (offset T) <- compute-offset A: (addr array T), idx: int
y/reg2: (addr T) <- index A, x
An offset is just an int that is guaranteed to be a multiple of size-of(T).
Offsets can only be used in index instructions, and the types will eventually
be required to line up.
In the process, I have to expand Input-size because mu.subx is growing
big.
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
And we're using it now in factorial.mu!
In the process I had to fix a couple of bugs in pointer dereferencing.
There are still some limitations:
a) Indexing by a literal doesn't work yet.
b) Only arrays of ints supported so far.
Looking ahead, I'm not sure how I can support indexing arrays by non-literals
(variables in registers) unless the element size is a power of 2.
|
| |
|
|
|
|
|
|
|
|
| |
I'd been thinking I didn't need unconditional `break` instructions, but
I just realized that non-local unconditional breaks have a use. Stop over-thinking
this, just support everything.
The code is quite duplicated.
|
| |
|
| |
|
|
|