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']
71 @NREG_ARGS = @ARG_REGS.length
86 :'bits-per-word' => '64',
87 :'byte-order' => 'little-endian',
88 :'bytes-per-word' => '8'
95 # Define a machine word with the given value.
104 # Emits function preamble and declare +formals+ as function arguments.
105 def begin_function *formals
106 environment = Environment.new @environment
107 @environment = environment
108 emit "push rbp\nmov rbp, rsp\n"
109 register_args = formals[0...number_of_register_arguments]
110 register_args.each_with_index do |arg,i|
111 let_register arg, @ARG_REGS[i]
113 (@NREG_ARGS...formals.length).each do |i|
114 environment.add_arg formals[i], arg_offset(i)
120 emit "; call #{func} #{args.join ' '}\n"
121 # First couple of arguments go in registers
122 register_args = args[0..(number_of_register_arguments - 1)] || []
123 # Rest of arguments go on the stack
124 stack_args = args[number_of_register_arguments..-1] || []
125 emit "; register_args: #{register_args.inspect}\n"
126 emit "; stack_args: #{stack_args.inspect}\n"
127 # Push stack arguments
128 stack_args.reverse.each { |arg| push_qword arg }
129 # Load register arguments
130 register_args.each_with_index do |arg,i|
131 register = @ARG_REGS[i]
132 value_ref = load_value arg, register
133 if value_ref != register
134 emit "mov #{register}, #{value_ref}\n"
138 value_ref = load_value func, @SCRATCH_REG
139 emit "xor rax, rax\n"
140 # If value_ref is a symbol, use PLT-relative addressing
142 emit "call #{value_ref} wrt ..plt\n"
144 emit "call #{value_ref}\n"
147 unless stack_args.empty?
148 emit "add rsp, #{stack_args.length * @WORDSIZE}\n"
152 # Call a function, re-using the current call frame if possible.
153 def tail_call func, *args
154 emit "; tail-call #{func} #{args.join ' '}\n"
155 # Compute required number of stack words
156 nstackargs = number_of_stack_arguments args.length
157 # If we need more stack arguments than we have now,
158 # perform a normal call and return
159 if nstackargs > number_of_stack_arguments(@environment.args)
160 emit "; Not enough space for proper tail call; using regular call\n"
161 ret :call, func, *args
164 # If any arguments are going to be overwritten before they are
165 # used, save them to new local variables and use those instead.
166 (args.length - 1).downto(@NREG_ARGS) do |i|
168 next unless symbol?(arg)
169 old_arg_offset = @environment[arg]
170 next if old_arg_offset == nil || old_arg_offset < 0
171 # arg is an argument that was passed on the stack.
172 new_arg_offset = arg_offset i
173 next unless old_arg_offset < new_arg_offset
174 # arg will be overwritten before it is used.
175 # Save it in a newly created temporary variable,
176 # then use that instead.
177 newsym = @environment.gensym
182 # Same for the function we will be calling.
184 offset = @environment[func]
185 if offset != nil && offset > 0
186 newsym = @environment.gensym
192 # Set stack arguments
193 if args.length > number_of_register_arguments
194 (args.length - 1).downto(number_of_register_arguments).each do |i|
197 value_ref = load_value arg, @SCRATCH_REG
198 newarg_ref = load_arg i
199 # Elide code if source is same as destination
200 unless value_ref == newarg_ref
201 emit "mov #{@SCRATCH_REG}, #{value_ref}\n"
202 emit "mov #{newarg_ref}, #{@SCRATCH_REG}\n"
207 # Set register arguments
208 number_of_register_arguments(args.length).times do |i|
209 register = @ARG_REGS[i]
210 load_value_into_register args[i], register
214 func_ref = load_value func, @BX
217 # If func_ref is a symbol, use PLT-relative addressing
219 emit "jmp #{func_ref} wrt ..plt\n"
221 emit "jmp #{func_ref}\n"
229 # Loads the value of the nth argument.
230 def load_arg n, reg = @SCRATCH_REG
231 if register_argument?(n)
232 # Arguments that were originally passed in a register
234 "[rbp - #{(n + 1) * @WORDSIZE}]"
236 # Arguments that were originally passed on the stack
237 # are now above rbp, starting 2 words above it
238 "[rbp + #{(n + 2 - number_of_register_arguments) * @WORDSIZE}]"
246 # Introduce a new local variable
247 def let symbol, *words
248 eval_expr words, @RETURN_REG
249 let_register symbol, @RETURN_REG
252 # Allocates space for a variable, adds it to the environment,
253 # and initializes it to the value in a register.
254 def let_register symbol, register
255 emit "push #{register}\n"
256 @environment.bytes = @environment.bytes + 8
257 offset = @environment.offset - 8
258 @environment.offset = offset
259 @environment.add_local symbol, offset
266 # Returns the offset from rbp at which the nth argument is stored.
271 (n - @NREG_ARGS) * 8 + 16
275 # Load a value and push it on the stack.
277 value_ref = load_value value, @SCRATCH_REG
278 emit "push qword #{value_ref}\n"
281 # Calculate the number of register arguments,
282 # given the total number of arguments.
283 # If _n_ is +nil+, returns the maximum number of
284 # register arguments.
285 def number_of_register_arguments n = nil
293 # Calculate the number of stack arguments,
294 # given the total number of arguments.
295 def number_of_stack_arguments n
296 [0, n - number_of_register_arguments].max
299 # Tests if the nth argument is a register argument.
300 def register_argument? n
301 n < number_of_register_arguments
307 Voodoo::CodeGenerator.register_generator AMD64NasmGenerator,
308 :architecture => :amd64,