class Z::CodeGen
- Z::CodeGen
- Z::Ast::Visitor
- Reference
- Object
Overview
= x86_64 calling conventions
Graciously taken from https://aaronbloomfield.github.io/pdr/book/x86-64bit-ccc-chapter.pdf
== Caller
The caller should adhere to the following rules when invoking a subroutine:
-
Before calling a subroutine, the caller should save the contents of certain registers that are designated caller-saved. The caller-saved registers are
r10
,r11
, and any registers that parameters are put into. If you want the contents of these registers to be preserved across the subroutine call, push them onto the stack. -
To pass parameters to the subroutine, we put up to six of them into registers (in order:
rdi
,rsi
,rdx
,rcx
,r8
,r9
). If there are more than six parameters to the subroutine, then push the rest onto the stack in reverse order (i.e, last parameter first) - since the stack grows down, the first of the extra parameters (really the seventh parameter) parameter will be stored at the lowest address (this inversion of parameters was historically used to allow functions to be passed a variable number of parameters). -
To call the subroutine, use the
call
instruction. This instruction places the return address on top of the parameters on the stack, and branches to the subroutine code. -
After the subroutine returns, (i.e, immediately following the call instruction) the caller must remove any additional parameters (beyond the six stored in registers) from the stack. This restores the stack to its state before the call was performed.
-
The caller can expect to find the return value of the subroutine in register
rax
. -
The caller restores the contents of caller-saved registers (
r10
,r11
, and any in the parameter passing registers) by popping them off of the stack. The caller can assume that no other registers were modified by the subroutine.
== Callee
The definition of the subroutine should adhere to the following rules:
-
Allocate local variables by using registers or making space on the stack. Recall, the stack grows down, so to make space on the top of the stack, the stack pointer should be decremented. The amount by which the stack pointer is decremented depends on the number of local variables needed. For example, if a local
float
and a locallong
(12 bytes total) were required, the stack pointer would need to be decremented by 12 to make space for these local variables:
sub rsp, 12
As with parameters, local variables will be located at known offsets
from the stack pointer.
2. Next, the values of any registers that are designated callee-saved that
will be used by the function must be saved. To save registers, push them
onto the stack. The callee-saved registers are `rbx`, `rbp`,and `r12`
through `r15` (`rsp` will also be preserved by the call convention, but
need not be pushed onthe stack during this step). After these three
actions are performed, the actual operation of the subroutine may
proceed. When the subroutine is ready to return, the call convention
rules continue:
3. When the function is done, the return value for the function should be
placed in `rax` if it is not already there.
4. The function must restore the old values of any callee-saved registers
(`rbx`, `rbp`, and `r12` through `r15`) that were modified. The register
contents are restored by popping them from the stack. Note, the
registers should be popped in the inverse order that they were pushed.
5. Next, we deallocate local variables. The easiest way to do this is to
add to `rsp` the same amount that was subtracted from it in step 1.
6. Finally, we return to the caller by executing a `ret` instruction. This
instruction will find and remove the appropriate return address from
the stack.
Links:
- https://cs.lmu.edu/~ray/notes/asmtoexe/
- https://nasm.us/doc/
Defined in:
codegen/codegen.crConstant Summary
-
CALL_REGS =
{1 => :rdi, 2 => :rsi, 3 => :rdx, 4 => :rcx, 5 => :r8, 6 => :r9}
Instance Method Summary
- #visit(node : Ast::Asm, io : IO)
- #visit(node : Ast::AsmInstructionList, io : IO)
- #visit(node : Ast::AsmIdent, io : IO)
- #visit(node : Ast::AsmImm, io : IO)
- #visit(node : Ast::AsmLabel, io : IO)
- #visit(node : Ast::Program, io : IO)
- #visit(node : Ast::FnParam, io : IO)
- #visit(node : Ast::Fn, io : IO)
- #visit(node : Ast::Block, io : IO)
- #visit(node : Ast::FnCall, io : IO)
- #visit(node : Ast::FnArg, io : IO)
- #visit(node : Ast::Stmt, io : IO)
- #visit(node : Ast::Expr, io : IO)
- #visit(node : Ast::Cond, io : IO)
- #visit(node : Ast::Clause, io : IO)
- #visit(node : Ast::While, io : IO)
- #visit(node : Ast::Return, io : IO)
- #visit(node : Ast::Lvar, io : IO)
- #visit(node : Ast::Ident, io : IO)
- #visit(node : Ast::NumberLiteral, io : IO)
- #visit(node : Ast::Assignment, io : IO)
- #visit(node : Ast::Neg, io : IO)
- #visit(node : Ast::BinOp, io : IO)
- #visit(node : Ast::Nop, io : IO)