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
60 # Register used as scratch register
74 # Registers used to store locals
76 @NLOCAL_REGS = @LOCAL_REGS.length
77 @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGS
78 @SAVED_FRAME_LAYOUT = { :ebx => 0, :edi => 1, :esi => 2, :esp => 3, :ebp => 4 }
79 @SAVE_FRAME_REGISTERS = @SAVED_FRAME_LAYOUT.keys
82 :'bits-per-word' => '32',
83 :'byte-order' => 'little-endian',
84 :'bytes-per-word' => '4'
87 # Returns the offset of the nth argument.
92 # Emits function preamble and declare +formals+ as function arguments.
93 def begin_function formals, nlocals
94 environment = Environment.new @environment
95 @environment = environment
96 emit "push #{@BP}\nmov #{@BP}, #{@SP}\n"
97 formals.each_with_index do |arg,i|
98 environment.add_arg arg, arg_offset(i)
100 emit "sub #{@SP}, #{nlocals * @WORDSIZE}\n"
105 revargs = args.reverse
106 revargs.each { |arg| push arg }
107 use_value "call", func
109 emit "add esp, #{@WORDSIZE * args.length}\n"
113 # If the nth local is stored in a register, returns that register.
114 # Otherwise, returns the offset from the frame pointer.
115 def local_offset_or_register n
119 # Push a word on the stack
121 value_ref = load_value value, "ebx"
122 emit "push dword #{value_ref}\n"
125 # Call a function, re-using the current call frame if possible
126 def tail_call fun, *args
127 if args.length > @environment.args
128 # Not enough space to do proper tail call; do normal call instead
129 emit "; not enough space for proper tail call; changed to regular call\n"
130 ret :call, fun, *args
132 # If any arguments are going to be overwritten before they are
133 # used, save them to new local variables and use those instead.
134 (args.length - 1).downto(0) do |i|
136 next unless symbol?(arg)
137 old_arg_offset = @environment[arg]
138 next if old_arg_offset == nil || old_arg_offset < 0
139 # arg is an argument that was passed on the stack.
140 new_arg_offset = arg_offset i
141 next unless old_arg_offset > new_arg_offset
142 # arg will be overwritten before it is used.
143 # Save it in a newly created temporary variable,
144 # then use that instead.
145 newsym = @environment.gensym
150 # Same for the function we will be calling.
152 offset = @environment[fun]
153 if offset != nil && offset > 0
154 newsym = @environment.gensym
162 (args.length - 1).downto(0).each do |i|
165 value_ref = load_value arg, :eax
166 newarg_ref = "[ebp + #{arg_offset i}]"
167 # Elide code if source is same as destination
168 unless value_ref == newarg_ref
169 if memory_operand?(value_ref)
170 emit "mov eax, #{value_ref}\n"
173 emit "mov #{@WORD_NAME} #{newarg_ref}, #{value_ref}\n"
179 emit "mov esp, ebp\npop ebp\n"
184 def use_value operation, value
185 value_ref = load_value value, :eax
186 emit "#{operation} #{value_ref}\n"
189 # Define a machine word with the given value
197 Voodoo::CodeGenerator.register_generator I386NasmGenerator,
198 :architecture => :i386,