1 require 'voodoo/generators/nasm_generator'
4 # Code generator that emits NASM assembly code for AMD64 processors.
8 # The calling convention implemented by this code generator is
9 # compatible with the System V ABI for AMD64, provided that all
10 # arguments are integers or pointers.
12 # Arguments are passed in registers. The registers are used in the
22 # Additional arguments are pushed on the stack, starting with the last
23 # argument and working backwards.
25 # The return value is passed in +rax+.
27 # For varargs functions, +rax+ must be set to an upper bound on the
28 # number of vector arguments. Since the code generator does not know
29 # whether the called function is a varargs function, this is always
30 # done. Since the code generator never passes any vector arguments,
31 # this means +rax+ is set to +0+ before each call.
50 class AMD64NasmGenerator < NasmGenerator
52 # Number of bytes in a word
54 # Word name in NASM lingo
56 # Default alignment for code
58 # Default alignment for data
59 @DATA_ALIGNMENT = @WORDSIZE
60 # Default alignment for functions
61 @FUNCTION_ALIGNMENT = 16
62 # Register used for return values
64 # Register used as scratch register
66 # Registers used for argument passing
67 @ARG_REGS = ['rdi', 'rsi', 'rdx', 'rcx', 'r8', 'r9']
83 # Define a machine word with the given value.
94 emit "; call #{func} #{args.join ' '}\n"
95 # First couple of arguments go in registers
96 register_args = args[0..number_of_register_arguments] || []
97 # Rest of arguments go on the stack
98 stack_args = args[number_of_register_arguments..-1] || []
99 emit "; register_args: #{register_args.inspect}\n"
100 emit "; stack_args: #{stack_args.inspect}\n"
101 # Push stack arguments
102 stack_args.reverse.each { |arg| push_qword arg }
103 # Load register arguments
104 register_args.each_with_index do |arg,i|
105 register = @ARG_REGS[i]
106 value_ref = load_value arg, register
107 if value_ref != register
108 emit "mov #{register}, #{value_ref}\n"
112 value_ref = load_value func, @SCRATCH_REG
113 emit "xor rax, rax\n"
114 emit "call #{value_ref}\n"
116 unless stack_args.empty?
117 emit "add rsp, #{stack_args.length * @WORDSIZE}\n"
121 # Emit function prologue.
122 def emit_function_prologue formals = []
123 emit "push rbp\nmov rbp, rsp\n"
124 unless formals.empty?
125 register_args = formals[0...number_of_register_arguments]
126 register_args.each_with_index do |arg,i|
127 emit "push #{@ARG_REGS[i]}\n"
132 # Call a function, re-using the current call frame if possible.
133 def tail_call func, *args
134 emit "; tail-call #{func} #{args.join ' '}\n"
135 # Compute required number of stack words
136 nstackargs = number_of_stack_arguments args.length
137 # If we need more stack arguments than we have now,
138 # perform a normal call and return
139 if nstackargs > number_of_stack_arguments(@environment.args)
140 emit "; Not enough space for proper tail call; using regular call\n"
141 ret :call, func, *args
144 # If any arguments are going to be overwritten before they are
145 # used, save them to new local variables and use those instead.
148 arg = (i >= 0) ? args[i] : func
151 x = @environment[arg]
152 if x && x[0] == :arg && x[1] < args.length && x[1] > i &&
153 (i >= 0 || func != args[x[1]])
155 newsym = @environment.gensym
168 # Set stack arguments
169 if args.length > number_of_register_arguments
170 (args.length - 1 .. number_of_register_arguments).each do |i|
173 value_ref = load_value arg, @SCRATCH_REG
174 newarg_ref = load_arg i
175 # Elide code if source is same as destination
176 unless value_ref == newarg_ref
177 emit "mov #{@WORD_NAME} #{newarg_ref}, #{value_ref}\n"
182 # Set register arguments
184 number_of_register_arguments(args.length).times do |i|
185 register = @ARG_REGS[i]
186 load_value_into_register args[i], register
191 func_ref = load_value func, @BX
194 emit "jmp #{func_ref}\n"
201 # Load the value of the nth argument
202 def load_arg n, reg = @SCRATCH_REG
203 if register_argument?(n)
204 # Arguments that were originally passed in a register
206 "[rbp - #{(n + 1) * @WORDSIZE}]"
208 # Arguments that were originally passed on the stack
210 "[rbp + #{(n + 1 - number_of_register_arguments) * @WORDSIZE}]"
214 # Load the value of the nth local variable
215 def load_local n, reg = @SCRATCH_REG
216 # If there current function has any arguments,
217 # local variables are offset by
218 # number_of_register_arguments(number_of_arguments)
220 offset = number_of_register_arguments(@environment.args) * @WORDSIZE
221 "[rbp - #{offset + (n + 1) * @WORDSIZE}]"
228 # Introduce a new local variable
229 def let symbol, *words
230 emit "; let #{symbol} #{words.join ' '}\n"
231 @environment.add_local symbol
232 eval_expr words, @RETURN_REG
233 emit "push #{@RETURN_REG}\n"
240 # Load a value and push it on the stack.
242 value_ref = load_value value, @SCRATCH_REG
243 emit "push qword #{value_ref}\n"
246 # Calculate the number of register arguments,
247 # given the total number of arguments.
248 # If _n_ is +nil+, returns the maximum number of
249 # register arguments.
250 def number_of_register_arguments n = nil
254 [@ARG_REGS.length, n].min
258 # Calculate the number of stack arguments,
259 # given the total number of arguments.
260 def number_of_stack_arguments n
261 [0, n - number_of_register_arguments].max
264 # Tests if the nth argument is a register argument.
265 def register_argument? n
266 n < number_of_register_arguments
272 Voodoo::CodeGenerator.register_generator AMD64NasmGenerator,
273 :architecture => :amd64,