1 require 'voodoo/generators/nasm_generator'
4 # = AMD64 NASM Code Generator
6 # Code generator that emits NASM assembly code for AMD64 processors.
8 # == Calling Convention
10 # The calling convention implemented by this code generator is
11 # compatible with the System V ABI for AMD64, provided that all
12 # arguments are integers or pointers.
14 # Arguments are passed in registers. The registers are used in the
24 # Additional arguments are pushed on the stack, starting with the last
25 # argument and working backwards. These arguments are removed from the
26 # stack by the caller, after the called function returns.
28 # The return value is passed in +rax+.
30 # For varargs functions, +rax+ must be set to an upper bound on the
31 # number of vector arguments. Since the code generator does not know
32 # whether the called function is a varargs function, this is always
33 # done. Since the code generator never passes any vector arguments,
34 # this means +rax+ is set to +0+ before each call.
53 class AMD64NasmGenerator < NasmGenerator
54 def initialize params = {}
55 # Number of bytes in a word
57 # Word name in NASM lingo
59 # Default alignment for code
61 # Default alignment for data
62 @DATA_ALIGNMENT = @WORDSIZE
63 # Default alignment for functions
64 @FUNCTION_ALIGNMENT = 16
65 # Register used for return values
67 # Register used as scratch register
69 # Registers used for argument passing
70 @ARG_REGS = ['rdi', 'rsi', 'rdx', 'rcx', 'r8', 'r9']
86 # Define a machine word with the given value.
97 emit "; call #{func} #{args.join ' '}\n"
98 # First couple of arguments go in registers
99 register_args = args[0..(number_of_register_arguments - 1)] || []
100 # Rest of arguments go on the stack
101 stack_args = args[number_of_register_arguments..-1] || []
102 emit "; register_args: #{register_args.inspect}\n"
103 emit "; stack_args: #{stack_args.inspect}\n"
104 # Push stack arguments
105 stack_args.reverse.each { |arg| push_qword arg }
106 # Load register arguments
107 register_args.each_with_index do |arg,i|
108 register = @ARG_REGS[i]
109 value_ref = load_value arg, register
110 if value_ref != register
111 emit "mov #{register}, #{value_ref}\n"
115 value_ref = load_value func, @SCRATCH_REG
116 emit "xor rax, rax\n"
117 # If value_ref is a symbol, use PLT-relative addressing
119 emit "call #{value_ref} wrt ..plt\n"
121 emit "call #{value_ref}\n"
124 unless stack_args.empty?
125 emit "add rsp, #{stack_args.length * @WORDSIZE}\n"
129 # Emit function prologue.
130 def emit_function_prologue formals = []
131 emit "push rbp\nmov rbp, rsp\n"
132 unless formals.empty?
133 register_args = formals[0...number_of_register_arguments]
134 register_args.each_with_index do |arg,i|
135 emit "push #{@ARG_REGS[i]}\n"
140 # Call a function, re-using the current call frame if possible.
141 def tail_call func, *args
142 emit "; tail-call #{func} #{args.join ' '}\n"
143 # Compute required number of stack words
144 nstackargs = number_of_stack_arguments args.length
145 # If we need more stack arguments than we have now,
146 # perform a normal call and return
147 if nstackargs > number_of_stack_arguments(@environment.args)
148 emit "; Not enough space for proper tail call; using regular call\n"
149 ret :call, func, *args
152 # If any arguments are going to be overwritten before they are
153 # used, save them to new local variables and use those instead.
156 arg = (i >= 0) ? args[i] : func
159 x = @environment[arg]
160 if x && x[0] == :arg && x[1] < args.length && x[1] > i &&
161 (i >= 0 || func != args[x[1]])
163 newsym = @environment.gensym
176 # Set stack arguments
177 if args.length > number_of_register_arguments
178 (args.length - 1).downto(number_of_register_arguments).each do |i|
181 value_ref = load_value arg, @SCRATCH_REG
182 newarg_ref = load_arg i
183 # Elide code if source is same as destination
184 unless value_ref == newarg_ref
185 emit "mov #{@SCRATCH_REG}, #{value_ref}\n"
186 emit "mov #{newarg_ref}, #{@SCRATCH_REG}\n"
191 # Set register arguments
192 number_of_register_arguments(args.length).times do |i|
193 register = @ARG_REGS[i]
194 load_value_into_register args[i], register
198 func_ref = load_value func, @BX
201 # If func_ref is a symbol, use PLT-relative addressing
203 emit "jmp #{func_ref} wrt ..plt\n"
205 emit "jmp #{func_ref}\n"
213 # Load the value of the nth argument
214 def load_arg n, reg = @SCRATCH_REG
215 if register_argument?(n)
216 # Arguments that were originally passed in a register
218 "[rbp - #{(n + 1) * @WORDSIZE}]"
220 # Arguments that were originally passed on the stack
221 # are now above rbp, starting 2 words above it
222 "[rbp + #{(n + 2 - number_of_register_arguments) * @WORDSIZE}]"
226 # Load the value of the nth local variable
227 def load_local n, reg = @SCRATCH_REG
228 # If there current function has any arguments,
229 # local variables are offset by
230 # number_of_register_arguments(number_of_arguments)
232 offset = number_of_register_arguments(@environment.args) * @WORDSIZE
233 "[rbp - #{offset + (n + 1) * @WORDSIZE}]"
240 # Introduce a new local variable
241 def let symbol, *words
242 emit "; let #{symbol} #{words.join ' '}\n"
243 @environment.add_local symbol
244 eval_expr words, @RETURN_REG
245 emit "push #{@RETURN_REG}\n"
252 # Load a value and push it on the stack.
254 value_ref = load_value value, @SCRATCH_REG
255 emit "push qword #{value_ref}\n"
258 # Calculate the number of register arguments,
259 # given the total number of arguments.
260 # If _n_ is +nil+, returns the maximum number of
261 # register arguments.
262 def number_of_register_arguments n = nil
266 [@ARG_REGS.length, n].min
270 # Calculate the number of stack arguments,
271 # given the total number of arguments.
272 def number_of_stack_arguments n
273 [0, n - number_of_register_arguments].max
276 # Tests if the nth argument is a register argument.
277 def register_argument? n
278 n < number_of_register_arguments
284 Voodoo::CodeGenerator.register_generator AMD64NasmGenerator,
285 :architecture => :amd64,