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