Removed common NasmGenerator code from I386NasmGenerator.
[voodoo-lang.git] / lib / ruby / voodoo / generators / i386_nasm_generator.rb
blobb625cbde57ce1d5179ed197200f5216a5a7c34b1
1 require 'voodoo/generators/common_code_generator'
3 module Voodoo
4   # = i386 NASM Code Generator
5   #
6   # The i386 NASM code generator generates i386 assembly code for use with
7   # the {Netwide Assembler}[http://www.nasm.us/].
8   #
9   # == Call Frames
10   #
11   # Call frames have the following layout:
12   #
13   #   argn
14   #   :
15   #   arg1   <-- ebp + 8
16   #   oldeip <-- ebp + 4
17   #   oldebp <-- ebp
18   #   local1 <-- ebp - 4
19   #   local2 <-- ebp - 8
20   #   :
21   #   localn <-- esp
22   #
23   class I386NasmGenerator < NasmGenerator
24     WORDSIZE = 4
26     def initialize params
27       # Number of bytes in a word
28       @WORDSIZE = 4
29       # Word name in NASM lingo
30       @WORD_NAME = 'dword'
31       # Default alignment for code
32       @CODE_ALIGNMENT = 0
33       # Default alignment for data
34       @DATA_ALIGNMENT = @WORDSIZE
35       # Default alignment for functions
36       @FUNCTION_ALIGNMENT = 16
37       # Register used for return values
38       @RETURN_REG = 'eax'
39       # Register used as scratch register
40       @SCRATCH_REG = 'ebx'
41       # Accumulator index
42       @AX = 'eax'
43       # Base index
44       @BX = 'ebx'
45       # Count index
46       @CX = 'ecx'
47       # Data index
48       @DX = 'edx'
49       super params
50     end
52     # Call a function
53     def call func, *args
54       emit "; call #{func} #{args.join ' '}\n"
55       revargs = args.reverse
56       revargs.each { |arg| push arg }
57       use_value "call", func
58       if args.length > 0
59         emit "add esp, #{WORDSIZE * args.length}\n"
60       end
61     end
63     # Emit function prologue.
64     def emit_function_prologue formals = []
65       emit "push ebp\nmov ebp, esp\n"
66     end
68     # Load the value of the nth argument
69     def load_arg n, reg = @SCRATCH_REG
70       "[ebp + #{n * @WORDSIZE + 8}]"
71     end
73     # Load the value of the nth local variable
74     def load_local n, reg = @SCRATCH_REG
75       "[ebp - #{(n + 1) * @WORDSIZE}]"
76     end
78     # Introduce a new local variable
79     def let symbol, *words
80       emit "; let #{symbol} #{words.join ' '}\n"
81       @environment.add_local symbol
82       eval_expr words
83       emit "push eax\n"
84     end
86     # Push a word on the stack
87     def push value
88       #emit "; push #{value}\n"
89       value_ref = load_value value, "ebx"
90       emit "push dword #{value_ref}\n"
91     end
93     # Call a function, re-using the current call fram if possible
94     def tail_call fun, *args
95       emit "; tail-call #{fun} #{args.join ' '}\n"
96       if args.length > @environment.args
97         # Not enough space to do proper tail call; do normal call instead
98         emit "; not enough space for proper tail call; changed to regular call\n"
99         ret :call, fun, *args
100       else
101         # Any value in the current frame that is passed to the called
102         # function must be copied to a local variable if it would otherwise
103         # be overwritten before it is used
104         i = args.length - 1
105         while i >= -1
106           arg = (i >= 0) ? args[i] : fun
108           if symbol?(arg)
109             x = @environment[arg]
110             if x && x[0] == :arg && x[1] < args.length && x[1] > i &&
111                 (i >= 0 || fun != args[x[1]])
112               # Save value
113               newsym = @environment.gensym
114               let newsym, arg
115               # Change reference
116               if i >= 0
117                 args[i] = newsym
118               else
119                 fun = newsym
120               end
121             end
122           end
123           i = i - 1
124         end
126         # Set arguments
127         if args.length > 0
128           (args.length - 1 .. 0).each do |i|
129             arg = args[i]
130             
131             value_ref = load_value arg, "eax"
132             newarg_ref = "[ebp + #{(i + 2) * WORDSIZE}]"
133             # Elide code if source is same as destination
134             unless value_ref == newarg_ref
135               emit "mov [ebp + #{(i + 2) * WORDSIZE}], #{value_ref}\n"
136             end
137           end
138         end
140         # Tail call
141         emit "mov esp, ebp\npop ebp\n"
142         use_value "jmp", fun
143       end
144     end
146     def use_value operation, value
147       value_ref = load_value value, "eax"
148       emit "#{operation} #{value_ref}\n"
149     end
151     # Define a machine word with the given value
152     def word value
153       emit "dd #{value}\n"
154     end
156   end
158   # Register class
159   Voodoo::CodeGenerator.register_generator I386NasmGenerator,
160                                            :architecture => :i386,
161                                            :format => :nasm