1 require 'voodoo/generators/common_code_generator'
2 require 'voodoo/generators/nasm_generator'
5 # = i386 NASM Code Generator
7 # The i386 NASM code generator generates i386 assembly code for use with
8 # the {Netwide Assembler}[http://www.nasm.us/].
10 # == Calling Convention
12 # Function arguments are pushed on the stack in reverse order, so that
13 # the first argument is pushed last. Each argument occupies one word
14 # of stack space. These arguments are removed from the stack by the
15 # caller after the called function returns.
17 # The return value is passed in +eax+.
21 # Call frames have the following layout:
34 # == Callee-Save Registers
36 # +ebp+, +ebx+, +edi+, +esi+, and +esp+ are callee-save registers.
38 # All other registers are caller-save registers.
40 class I386NasmGenerator < NasmGenerator
43 def initialize params = {}
44 # Number of bytes in a word
46 @WORDSIZE = 1 << @WORDSIZE_BITS
47 # Word name in NASM lingo
49 # Default alignment for code
51 # Default alignment for data
52 @DATA_ALIGNMENT = @WORDSIZE
53 # Default alignment for functions
54 @FUNCTION_ALIGNMENT = 16
56 @STACK_ALIGNMENT_BITS = @WORDSIZE_BITS
57 @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
58 # Register used for return values
72 # Registers used to store locals
74 @NLOCAL_REGISTERS = @LOCAL_REGISTERS.length
75 @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
76 @SAVE_FRAME_REGISTERS = [:ebx, :edi, :esi, :esp, :ebp]
77 @SAVED_FRAME_LAYOUT = {}
78 @SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i }
83 :'bits-per-word' => '32',
84 :'byte-order' => 'little-endian',
85 :'bytes-per-word' => '4'
88 # Returns the offset of the nth argument.
93 # Emits function preamble and declare +formals+ as function arguments.
94 def begin_function formals, nlocals
95 environment = Environment.new @environment
96 @environment = environment
97 emit "push #{@BP}\nmov #{@BP}, #{@SP}\n"
98 formals.each_with_index do |arg,i|
99 environment.add_arg arg, arg_offset(i)
101 emit "sub #{@SP}, #{nlocals * @WORDSIZE}\n"
106 revargs = args.reverse
107 revargs.each { |arg| push arg }
108 use_value "call", func
110 emit "add esp, #{@WORDSIZE * args.length}\n"
114 # If the nth local is stored in a register, returns that register.
115 # Otherwise, returns the offset from the frame pointer.
116 def local_offset_or_register n
120 # Push a word on the stack
122 value_ref = load_value value, "ebx"
123 emit "push dword #{value_ref}\n"
126 # Call a function, re-using the current call frame if possible
127 def tail_call fun, *args
128 if args.length > @environment.args
129 # Not enough space to do proper tail call; do normal call instead
130 emit "; not enough space for proper tail call; changed to regular call\n"
131 ret :call, fun, *args
133 # If any arguments are going to be overwritten before they are
134 # used, save them to new local variables and use those instead.
135 (args.length - 1).downto(0) do |i|
137 next unless symbol?(arg)
138 old_arg_offset = @environment[arg]
139 next if old_arg_offset == nil || old_arg_offset < 0
140 # arg is an argument that was passed on the stack.
141 new_arg_offset = arg_offset i
142 next unless old_arg_offset > new_arg_offset
143 # arg will be overwritten before it is used.
144 # Save it in a newly created temporary variable,
145 # then use that instead.
146 newsym = @environment.gensym
151 # Same for the function we will be calling.
153 offset = @environment[fun]
154 if offset != nil && offset > 0
155 newsym = @environment.gensym
163 (args.length - 1).downto(0).each do |i|
166 value_ref = load_value arg, :eax
167 newarg_ref = "[ebp + #{arg_offset i}]"
168 # Elide code if source is same as destination
169 unless value_ref == newarg_ref
170 if memory_operand?(value_ref)
171 emit "mov eax, #{value_ref}\n"
174 emit "mov #{@WORD_NAME} #{newarg_ref}, #{value_ref}\n"
180 emit "mov esp, ebp\npop ebp\n"
185 def use_value operation, value
186 value_ref = load_value value, :eax
187 emit "#{operation} #{value_ref}\n"
190 # Define a machine word with the given value
198 Voodoo::CodeGenerator.register_generator I386NasmGenerator,
199 :architecture => :i386,