1 require 'voodoo/code_generator'
16 ## Class to generate i386 assembly code for the Netwide Assembler (NASM)
17 class I386NasmGenerator < CommonCodeGenerator
25 def output_file_name input_name
26 input_name.sub(/\.voo$/, '') + '.asm'
29 # Emit code for a binary operation
30 def binop op, target, x, y
33 elsif symmetric_operation?(op) && y == target
36 # Cases that are handled specially
37 return div(target, x, y) if op == :div
38 return mod(target, x, y) if op == :mod
39 return mul(target, x, y) if op == :mul
41 target_ref = load_value target, "eax"
42 x_ref = load_value x, "edx"
43 y_ref = load_value y, "ebx"
45 if memory_operand?(target_ref)
46 if memory_operand?(x_ref) || memory_operand?(y_ref)
47 emit "mov ecx, #{x_ref}\n"
48 emit "#{op} ecx, #{y_ref}\n"
49 emit "mov #{target_ref}, ecx\n"
51 emit "mov dword #{target_ref}, #{x_ref}\n"
52 emit "#{op} dword #{target_ref}, #{y_ref}\n"
55 raise "Can't happen: target_ref is #{target_ref.inspect}"
60 # Emit code for a binary operation where the first operand
62 def binop2 op, target, y
63 # Cases that are handled specially
64 return div2(target, y) if op == :div
65 return mod2(target, y) if op == :mod
66 return mul2(target, y) if op == :mul
68 target_ref = load_value target, "ebx"
69 y_ref = load_value y, "edx"
70 if memory_operand?(target_ref) && memory_operand?(y_ref)
71 emit "mov eax, #{y_ref}\n"
72 emit "#{op} dword #{target_ref}, eax\n"
74 emit "#{op} dword #{target_ref}, #{y_ref}\n"
78 # tests if a value is an at-reference
79 def at_reference? value
83 # tests if op is a binary operation
85 [:div, :mod, :sub].member?(op) || symmetric_operation?(op)
93 emit "; call #{func} #{args.join ' '}\n"
94 revargs = args.reverse
95 revargs.each { |arg| push arg }
96 use_value "call", func
98 emit "add esp, #{WORDSIZE * args.length}\n"
107 # Divide x by y and store the result in target
110 target_ref = load_value target, "ebx"
111 emit "mov #{target_ref}, eax\n"
114 # Divide target by x and store the result in target
117 target_ref = load_value target, "ebx"
118 emit "mov #{target_ref}, eax\n"
121 # emit some code to the current section
123 @sections[@section] << code
126 # end a function body
128 emit "; end function\n\n"
129 if @environment == @top_level
130 raise "Cannot end function when not in a function"
132 @environment = @top_level
137 label = @if_labels.pop
141 # evaluates an expr and stores the result in eax
146 emit "xor #{target_ref}, #{target_ref}\n"
148 value_ref = load_value words[0], "ebx"
149 emit "mov #{target_ref}, #{value_ref}\n"
157 eval_div words[1], words[2]
159 emit "xor dword #{target_ref}, #{target_ref}\n"
160 address_ref = load_address words[1], words[2], 1
161 emit "mov byte al, #{address_ref}\n"
163 value_ref = load_address words[1], words[2], WORDSIZE
164 emit "mov dword #{target_ref}, #{value_ref}\n"
166 eval_div words[1], words[2]
167 emit "mov eax, edx\n"
169 eval_mul target_ref, words[1], words[2]
171 x_ref = load_value words[1], "edx"
172 emit "mov #{target_ref}, #{x_ref}\n"
173 emit "not #{target_ref}\n"
176 x_ref = load_value words[1], "edx"
177 y_ref = load_value words[2], "ebx"
178 emit "mov #{target_ref}, #{x_ref}\n"
179 emit "#{op} #{target_ref}, #{y_ref}\n"
181 raise "Not a magic word: #{words[0]}"
187 # Divide x by y, leaving the quotient in eax and the remainder in edx
189 x_ref = load_value x, "ebx"
190 y_ref = load_value y, "ecx"
191 emit "mov eax, #{x_ref}\n"
192 emit "xor edx, edx\n"
193 if immediate_operand?(y_ref)
194 emit "mov ecx, #{y_ref}\n"
197 emit "idiv dword #{y_ref}\n"
201 # Multiply x by y and store the result in target
202 def eval_mul target_ref, x, y
203 # Assumes target_ref is not ebx or edx
204 x_ref = load_value x, "edx"
205 y_ref = load_value y, "ebx"
207 if immediate_operand? x_ref
208 if immediate_operand? y_ref
209 emit "mov #{target_ref}, #{x_ref}\n"
210 emit "imul #{target_ref}, #{y_ref}\n"
212 emit "imul #{target_ref}, #{y_ref}, #{x_ref}\n"
214 elsif immediate_operand? y_ref
215 emit "imul #{target_ref}, #{x_ref}, #{y_ref}\n"
217 emit "mov #{target_ref}, #{x_ref}\n"
218 emit "imul dword #{y_ref}\n"
223 emit "global #{symbols.join ', '}\n"
226 # start a function definition
227 def begin_function *args
228 emit "\n; function #{args.join ' '}\n"
229 environment = Environment.new @environment
230 environment.add_args args
231 @environment = environment
232 emit "push ebp\nmov ebp, esp\n"
235 # tests if a symbol is a global variable
237 @environment[symbol] == nil
241 emit "; goto #{value}\n"
242 ref = load_value value, "eax"
243 emit "jmp #{value_ref}\n"
248 newlabel = @environment.gensym
249 emit "jmp #{newlabel}\n"
250 label = @if_labels.pop
252 @if_labels.push newlabel
255 def if_epilogue truelabel, falselabel
256 emit "jmp #{falselabel}\n"
257 emit "#{truelabel}:\n"
260 # Tests if x is equal to y
262 emit "; ifeq #{x} #{y}\n"
263 truelabel, falselabel = if_prologue x, y
264 emit "cmp eax, edx\n"
265 emit "je #{truelabel}\n"
266 if_epilogue truelabel, falselabel
269 # Tests if x is greater than or equal to y
271 emit "; ifge #{x} #{y}\n"
272 truelabel, falselabel = if_prologue x, y
273 emit "cmp eax, edx\n"
274 emit "jge #{truelabel}\n"
275 if_epilogue truelabel, falselabel
278 # Tests if x is strictly greater than y
280 emit "; ifgt #{x} #{y}\n"
281 truelabel, falselabel = if_prologue x, y
282 emit "cmp eax, edx\n"
283 emit "jg #{truelabel}\n"
284 if_epilogue truelabel, falselabel
287 # Tests if x is less than or equal to y
289 emit "; ifle #{x} #{y}\n"
290 truelabel, falselabel = if_prologue x, y
291 emit "cmp eax, edx\n"
292 emit "jle #{truelabel}\n"
293 if_epilogue truelabel, falselabel
296 # Tests if x is strictly less than y
298 emit "; iflt #{x} #{y}\n"
299 truelabel, falselabel = if_prologue x, y
300 emit "cmp eax, edx\n"
301 emit "jl #{truelabel}\n"
302 if_epilogue truelabel, falselabel
305 # Tests if x different from y
307 emit "; ifne #{x} #{y}\n"
308 truelabel, falselabel = if_prologue x, y
309 emit "cmp eax, edx\n"
310 emit "jne #{truelabel}\n"
311 if_epilogue truelabel, falselabel
314 def if_prologue x, y = nil
315 ref2 = load_value y, "edx" if y
316 ref = load_value x, "eax"
317 emit "mov edx, #{ref2}\n" if y && ref2 != "edx"
318 emit "mov eax, #{ref}\n" unless ref == "eax"
320 truelabel = @environment.gensym
321 falselabel = @environment.gensym
322 @if_labels.push falselabel
323 [truelabel, falselabel]
326 # Tests if an operand is an immediate operand
327 def immediate_operand? operand
328 integer?(operand) || (global?(operand) && operand !~ /^e[abcd]x$/)
332 emit "extern #{symbols.join ', '}\n"
335 # tests if a value is an integer
337 value.kind_of? Integer
344 def let symbol, *words
345 emit "; let #{symbol} #{words.join ' '}\n"
346 @environment.add_local symbol
351 def load_address base, offset, scale
352 base_ref = load_value base, "ebx"
353 offset_ref = load_value offset, "ecx"
359 emit "mov ebx, #{base_ref}\n"
362 elsif base_ref == '0'
363 if integer? offset_ref
364 "[#{offset_ref.to_i * scale}]"
366 emit "mov ecx, #{offset_ref}\n"
369 elsif integer? base_ref
370 if integer? offset_ref
371 "[#{base_ref.to_i + (offset_ref.to_i * scale)}]"
373 emit "mov ecx, #{offset_ref}\n"
374 "[#{base_ref} + ecx * #{scale}]"
376 elsif integer? offset_ref
377 emit "mov ebx, #{base_ref}\n"
378 "[ebx + #{offset_ref.to_i * scale}]"
380 emit "mov ebx, #{base_ref}\n"
381 emit "mov ecx, #{offset_ref}\n"
382 "[ebx + ecx * #{scale}]"
386 # symbol -> value of symbol
387 # @symbol -> value at address in symbol
389 def load_value value, free_register
390 if value_reference? value
391 symbol_value value_symbol(value)
392 elsif at_reference? value
393 symbol = value_symbol value
397 emit "mov #{free_register}, #{symbol_value symbol}\n"
398 "[" + free_register + "]"
403 raise "Invalid value: #{value.inspect}"
407 # Tests if an operand is a memory operand
408 def memory_operand? operand
412 # Divide x by y and store the remainder in target
415 target_ref = load_value target, "ebx"
416 emit "mov #{target_ref}, edx\n"
419 # Divide target by x and store the remainder in target
422 target_ref = load_value target, "ebx"
423 emit "mov #{target_ref}, edx\n"
426 # Multiply x by y and store the result in target
428 # Assumes that eval_mul does not clobber ecx
429 target_ref = load_value target, "ecx"
431 emit "mov #{target_ref}, eax\n"
434 # Multiply target by x, storing the result in target
436 target_ref = load_value target, "ebx"
437 x_ref = load_value x, "edx"
438 emit "mov eax, #{target_ref}\n"
439 if immediate_operand?(x_ref)
440 emit "imul eax, #{x_ref}\n"
442 emit "imul dword #{x_ref}\n"
444 emit "mov #{target_ref}, eax\n"
447 def public_label label
448 emit "global #{label}\n#{label}:\n"
452 #emit "; push #{value}\n"
453 value_ref = load_value value, "ebx"
454 emit "push dword #{value_ref}\n"
458 emit "; return #{words.join ' '}\n"
460 emit "mov esp, ebp\npop ebp\nret\n"
463 # Evaluate the expr in words and store the result in target
464 def set target, *words
466 raise "Cannot change value of integer #{target}"
467 elsif value_reference?(target) && global?(target)
468 raise "Cannot change value of global #{target}"
471 emit "; set #{target} #{words.join ' '}\n"
473 if words[0] == target
474 emit "; nothing to do; destination equals source\n"
476 target_ref = load_value target, "ebx"
477 if integer?(words[0])
478 if words[0].to_i == 0
479 # Set destination to 0
480 emit "xor eax, eax\nmov #{target_ref}, eax\n"
483 emit "mov dword #{target_ref}, #{words[0]}\n"
486 # Copy source to destination
488 emit "mov dword #{target_ref}, eax\n"
494 if words.length == 3 && binop?(op)
496 binop op, target, words[1], words[2]
498 # Not a binary operation
500 target_ref = load_value target, "ebx"
501 emit "mov dword #{target_ref}, eax\n"
506 def set_byte base, offset, value
507 emit "; set-byte #{base} #{offset} #{value}\n"
508 value_ref = load_value value, "eax"
509 addr_ref = load_address base, offset, 1
510 emit "mov byte #{addr_ref}, #{value_ref}\n"
513 def set_word base, offset, value
514 emit "; set-word #{base} #{offset} #{value}\n"
515 value_ref = load_value value, "eax"
516 addr_ref = load_address base, offset, WORDSIZE
517 emit "mov dword #{addr_ref}, #{value_ref}\n"
524 if b >= 32 && b < 128
543 def symbol_value symbol
544 x = @environment[symbol]
548 "[ebp + #{WORDSIZE * x[1] + (2 * WORDSIZE)}]"
550 "[ebp - #{WORDSIZE * x[1] + WORDSIZE}]"
552 raise "Invalid variable type: #{x[0]}"
560 # Test if op is a symmetric operation (i.e. it will yield the
561 # same result if the order of its source operands is changed).
562 def symmetric_operation? op
563 [:add, :and, :mul, :or, :xor].member? op
566 def tail_call fun, *args
567 emit "; tail-call #{fun} #{args.join ' '}\n"
568 if args.length > @environment.args
569 # Not enough space to do proper tail call; do normal call instead
570 emit "; not enough space for proper tail call; changed to regular call\n"
571 ret 'call', fun, *args
573 # Any value in the current frame that is passed to the called
574 # function must be copied to a local variable if it would otherwise
575 # be overwritten before it is used
578 arg = (i >= 0) ? args[i] : fun
580 if value_reference?(arg) || at_reference?(arg)
581 symbol = value_symbol arg
582 x = @environment[symbol]
583 if x && x[0] == :arg && x[1] < args.length && x[1] > i &&
584 (i >= 0 || fun != args[x[1]])
586 newsym = @environment.gensym
589 newref = at_reference?(arg) ? "@#{newsym}" : newsym
602 (args.length - 1 .. 0).each do |i|
605 value_ref = load_value arg, "eax"
606 newarg_ref = "[ebp + #{(i + 2) * WORDSIZE}]"
607 # Elide code if source is same as destination
608 unless value_ref == newarg_ref
609 emit "mov [ebp + #{(i + 2) * WORDSIZE}], #{value_ref}\n"
615 emit "mov esp, ebp\npop ebp\n"
620 def use_value operation, value
621 value_ref = load_value value, "eax"
622 emit "#{operation} #{value_ref}\n"
625 # tests if a value is a value reference
626 def value_reference? value
627 !integer?(value) && value.to_s =~ /^\.?(\w|-)+$/
630 # extracts the symbol name from a value
631 def value_symbol value
632 value.to_s[0] == ?@ ? value.to_s[1..-1].to_sym : value
640 io.puts "bits 32\n\n"
641 @sections.each do |section,code|
645 section_name = '.text'
647 section_name = '.data'
649 section_name = '.text'
651 section_name = section.to_s
653 io.puts "section #{section_name}"
663 attr_reader :args, :locals, :symbols
665 def initialize parent = nil
666 ## Parent environment
668 ## Symbol lookup table
669 @symbols = parent ? parent.symbols.dup : {}
670 ## Number of arguments
671 @args = parent ? parent.args : 0
672 ## Number of local variables
673 @locals = parent ? parent.locals : 0
677 @symbols[symbol] = [:arg, @args]
682 symbols.each { |sym| add_arg sym }
686 @symbols[symbol] = [:local, @locals]
687 @locals = @locals + 1
690 def add_locals symbols
691 symbols.each { |sym| add_local sym }
695 @@gensym_counter = @@gensym_counter + 1
696 ".G#{@@gensym_counter}"
703 def self.initial_environment
710 Voodoo::CodeGenerator.register_generator I386NasmGenerator,
711 :architecture => :i386,