Assembler Enhancements
None of these proposals affect scoring or execution speed; they are only possibilities to improve code readability.
sz
and snz
instructions
I've often found myself writing patterns like this:
# adds c,d to a,b
add_128:
add b, b, d
leu d, b, d
jz skip_carry, d
add a, a, 1
skip_carry:
add a, a, d
ret a, b
Personally, I don't like that extra label floating around.
(This particular case would be rendered trivial by an adc
instruction, but I think introducing a status register goes against the goals of GOLF.)
I propose two new psuedo-instructions:
sz a ' | 1 | skip on zero | Skips the next instruction if a is zero.
snz a ' | 1 | skip on non-zero | Skips the next instruction if a is non-zero.
These would get transformed to jz
and jnz
instructions by the assembler, with the address to jump to set to the current address + two instructions. The previous sample could become:
# adds c,d to a,b
add_128:
add b, b, d
leu d, b, d
sz d
add a, a, 1
add a, a, d
ret a, b
#include
directive
As a simpler alternative to the dynamic/static linking proposals, you could add a function include(filename)
that would cause the assembler to insert the contents of the specified file at the location of the function call. This would allow splitting utility functions and lookup tables into different files without two much trouble.
Precomputed data offsets
My submission for "Testing if a number is a square" included this snippet:
add x, x, data(lookup_table)
sub x, x, 2**10
I could have saved one cycle if the assembler allowed me to do something like:
add x, x, data(lookup_table) - 2**10
I could have edited the binary myself, but as it was, a 0.2-cycle (on average) optimization was not worth it. However I could see places where this could be useful.
Automatic loop unrolling:
The same submission also included the following snippet:
and c, x, 0xFFFFFFFF00000000
jnz skip32, c
shl x, x, 32
sub b, b, 16
skip32:
and c, x, 0xFFFF000000000000
jnz skip16, c
shl x, x, 16
sub b, b, 8
skip16:
and c, x, 0xFF00000000000000
jnz skip8, c
shl x, x, 8
sub b, b, 4
skip8:
and c, x, 0xF000000000000000
jnz skip4, c
shl x, x, 4
sub b, b, 2
skip4:
and c, x, 0xC000000000000000
jnz skip2, c
shl x, x, 2
sub b, b, 1
skip2:
Hard to read and prone to mistakes. What if we could do something like:
for i in range(5,0,-1):
and c, x, 2**64 - 2**(64 - 2**i)
jnz skip<i>, c
shl x, x, 2**i
sub b, b, 2**(i-1)
skip<i>:
end
The syntax needs some work (especially the labels), but you get the idea. Here it's not so bad, but if you want to unroll (say) a 129-bit long division algorithm, where eliminating the conditional and jump would save ~20% on each cycle? Priceless.
Access to python libraries
In order to create the lookup table for the above problem, I did
lookup_table = bytes(int((16*n)**0.5) for n in range(2**10, 2**12))
But what if I wanted to make a lookup table for sin(x)
? If the assembler allowed an equivalent of include math
I could just use math.sin
. As it is I have to prepare the lookup table ahead of time and then paste it in as a huge string: not very pretty.
"Memory address 0xffffffffffffffff is special - stores to this address will be written to the virtual machine's stdin, reads come from stdout"
. You write to STDIN and you read from STDOUT? \$\endgroup\$mov
-ing immediates, instead of using the regular expensivelw
. Also it does not model real-world CPUs well. Self-modifying code is very expensive. \$\endgroup\$