Monday, September 17, 2007

Factor x86 calling convention

I'm working on the x86 backend for the quotation compiler. This has given me an opportunity to tweak the calling convention used by compiled Factor code. I will outline the calling convention I use, and highlight the main differences from the standard C calling convention.

Registers are assigned as follows:
  • EAX - volatile scratch
  • ECX - volatile scratch
  • EDX - volatile scratch
  • EBX - volatile scratch
  • EBP - volatile scratch
  • ESI - data stack pointer
  • EDI - retain stack pointer
  • ESP - callstack pointer
Factor's compiler does not generate code which saves values in non-volatile registers, because that would complicate matters for the GC. Instead, the only location where values may be saved between subroutine calls is the data and retain stacks. So I use EBX and EBP as volatile scratch registers, since otherwise they'd be unused.

Stack frames are fixed-size, 16 bytes each, with no explicit back-chain pointers. Stack frames have the following layout:
  • 4 bytes current quotation position - used by code compiled with quotation compiler
  • 4 bytes current quotation - used by code compiled with quotation compiler
  • Pointer to start current of current code block - used by code GC as a root when scavenging the call stack
  • Return address for callee

Unlike typical x86 code which passes parameters on the call stack, I use registers to pass parameters to quotations and words.

Quotations are called with the following values in registers:
  • EAX - a tagged pointer to the quotation itself
  • ECX - a pointer to the start of the quotation's code block. The quotation's compiled definition saves this in the stack frame

Compound definitions and symbols are called with the following values in registers:
  • EAX - the word itself

Primitive words are called with the following values in registers:
  • EDX - the top of the call stack before the primitive call

Compiled words can be called without any parameters in registers.

Compiled words are responsible for a pointer to the start of their own in their stack frame, however the caller of a compiled quotation must do this for the quotation. This is because right now, compiled quotations must be fully position independent without requiring any relocation.

No comments: