x86-64 has a lot of instructions. They are described in Volume 2 of the 5 Volume "Intel® 64 and IA-32 Architectures Software Developer’s Manual". Just this volume alone is over 2000 pages, which would take forever to fully implement. As a result, we will use only a subset of these instructions. This the rough plan:
- Most instructions like
addwill only be implemented with
- To accomplish something like
add rax, 1, we will use a temporary register
mov X, 1
add rax, X
- The constant propagation system will be able to provide enough information that we could eventually use
add r64 immXand similar if needed.
- Register allocation should handle the case
(set! x (+ 3 y))as:
mov x, 3
add x, y
(set! x (+ y 3)), in cases where
yis needed after and
xcan't take its place, will become the inefficient
mov x, y
mov rtemp, 3
add x, rtemp
- Loading constants into registers will be done efficiently, using the same strategy used by modern versions of
- Memory access will be done in the form
mov rdest, [roff + raddr]where
roffis the offset register. Doing memory access in this form was found to be much faster in simple benchmark test.
- Memory access to the stack will have an extra
suband more complicated dereference. GOAL code seems to avoid using the stack in most places, and I suspect the programmers attempted to avoid stack spills.
mov rdest, rsp: coloring move for upcoming subtract
sub rdest, roff: convert real pointer to GOAL pointer
mov rdest, [rdest + roff + variable_offset]: access memory through normal GOAL deref.
- Note - we should check that the register allocator gets this right always, and eliminates moves and avoid using a temporary register.
- Again, the constant propagation should give use enough information, if we ever want/need to implement a more efficient
mov rdest, [rsp + variable_offset]type instructions.
- Memory access to static data should use
mov rdest, [rip + offset]. And creating pointers to static data could be
lea rdest, [rip - roff + offset]