1 require 'voodoo/generators/common_code_generator'
4 # = NASM Code Generator
6 # The NASM code generator is a common base class for generators that output
7 # assembly code for use with the {Netwide Assembler}[http://www.nasm.us/].
9 # This class is used by both the I386NasmGenerator and the
10 # AMD64NasmGenerator, and contains the functionality that is common to
13 # To use the functionality from this class, a subclass must define the
14 # following methods and constants:
15 # - #emit_function_prologue
20 # - @FUNCTION_ALIGNMENT
24 # - @AX, @BX, @CX, and @DX
25 class NasmGenerator < CommonCodeGenerator
26 def initialize params = {}
29 @output_file_suffix = '.asm'
33 # == Information About the Generator
36 # Returns the number of bits per word for this code generator.
45 # Export symbols from the current section
47 emit "global #{symbols.join ', '}\n"
50 # Continue execution at the given address
52 emit "; goto #{value}\n"
53 value_ref = load_value value, @SCRATCH_REG
54 emit "jmp #{value_ref}\n"
57 # Import labels into the current section
59 emit "extern #{symbols.join ', '}\n"
62 # Define a label in the current section
71 # Define a byte with the given value
76 # Define a dword with the given value
81 # Define a qword with the given value
86 # Define a string with the given value
90 value.each_byte do |b|
91 if b >= 32 && b < 128 || byte == 39
95 code << ',' unless code.empty?
104 code << ',' unless code.empty?
109 code << "'" if in_quote
117 def align alignment = nil
119 # Get default alignment
122 alignment = @CODE_ALIGNMENT
124 alignment = @DATA_ALIGNMENT
126 alignment = @FUNCTION_ALIGNMENT
128 # Use data alignment as default
129 alignment = @DATA_ALIGNMENT
132 emit "align #{alignment}\n" unless alignment == 0
139 # Emit function preamble and declare _formals_ as function arguments
140 def begin_function *formals
141 emit "; function #{formals.join ' '}\n"
142 environment = Environment.new @environment
143 environment.add_args formals
144 @environment = environment
145 emit_function_prologue formals
148 # Emit function epilogue.
149 def emit_function_epilogue formals = []
153 # End a function body
155 emit "; end function\n\n"
156 if @environment == @top_level
157 raise "Cannot end function when not in a function"
159 @environment = @top_level
163 # Return a from a function.
165 # _words_ may contain an expression to be evaluated. The result
166 # of the evaluation is returned from the function.
168 emit "; return #{words.join ' '}\n"
170 emit_function_epilogue
178 # End a conditional body
180 label = @if_labels.pop
185 # == Value Classification
188 # Test if a value is an at-expression
190 value.respond_to?(:[]) && value[0] == :'@'
193 # Test if op is a binary operation
195 [:div, :mod, :sub].member?(op) || symmetric_operation?(op)
198 # Test if a value is an integer
200 value.kind_of? Integer
203 # Test if a symbol refers to a global
205 symbol?(symbol) && @environment[symbol] == nil
208 # Tests if an operand is an immediate operand
209 def immediate_operand? operand
210 integer?(operand) || global?(operand)
213 # Tests if an operand is a memory operand
214 def memory_operand? operand
218 # Test if a value is a symbol
220 value.kind_of? Symbol
223 # Test if op is a symmetric operation (i.e. it will yield the
224 # same result if the order of its source operands is changed).
225 def symmetric_operation? op
226 [:add, :and, :mul, :or, :xor].member? op
233 # Create a value reference to an address.
234 # Invoking this code may clobber @BX and/or @CX
235 def load_address base, offset, scale
236 base_ref = load_value base, @BX
237 offset_ref = load_value offset, @CX
241 # Only an integer base
244 # Some complex base; load in @BX
245 emit "mov #{@BX}, #{base_ref}\n"
249 if integer? offset_ref
250 # Only a scaled offset
251 "[#{offset_ref.to_i * scale}]"
253 # Some complex offset; load in @CX
254 emit "mov #{@CX}, #{offset_ref}\n"
255 "[#{@CX} * #{scale}]"
257 elsif integer? base_ref
258 if integer? offset_ref
259 # All integers, combine them together
260 "[#{base_ref.to_i + (offset_ref.to_i * scale)}]"
262 # Complex offset; use @CX
263 emit "mov #{@CX}, #{offset_ref}\n"
264 "[#{base_ref} + #{@CX} * #{scale}]"
266 elsif integer? offset_ref
267 # Complex base, integer offset; use @BX
268 emit "mov #{@BX}, #{base_ref}\n"
269 "[#{@BX} + #{offset_ref.to_i * scale}]"
271 # Both base and offset are complex
272 # Use both @BX and @CX
273 emit "mov #{@BX}, #{base_ref}\n"
274 emit "mov #{@CX}, #{offset_ref}\n"
275 "[#{@BX} + #{@CX} * #{scale}]"
279 # Load the value at the given address.
280 # Invoking this code may clobber @BX.
281 def load_at address, reg = @SCRATCH_REG
282 if integer?(address) || global?(address)
285 load_value_into_register address, @BX
290 # Load the value associated with the given symbol.
291 # Returns a string that can be used to refer to the loaded value.
292 def load_symbol symbol, reg = @SCRATCH_REG
293 x = @environment[symbol]
301 raise "Invalid variable type: #{x[0]}"
310 # Returns a string that can be used to refer to the loaded value.
311 def load_value value, reg = @SCRATCH_REG
313 # Integers can be used as is
316 load_symbol value, reg
318 load_at value[1], reg
320 raise "Don't know how to load #{value.inspect}"
324 # Load a value into a register.
325 def load_value_into_register value, register
326 load_code = load_value(value), register
327 if load_code == register.to_s
330 "mov #{register}, #{load_code}\n"
338 # Evaluate the expr in words and store the result in target
339 def set target, *words
341 raise "Cannot change value of integer #{target}"
342 elsif global?(target)
343 raise "Cannot change value of global #{target}"
346 emit "; set #{target} #{words.join ' '}\n"
348 if words[0] == target
349 emit "; nothing to do; destination equals source\n"
351 target_ref = load_value target, @BX
352 if integer?(words[0])
353 if words[0].to_i == 0
354 # Set destination to 0
355 emit "xor #{@AX}, #{@AX}\n"
356 emit "mov #{target_ref}, #{@AX}\n"
359 emit "mov #{@WORD_NAME} #{target_ref}, #{words[0]}\n"
362 # Copy source to destination
363 eval_expr words, @RETURN_REG
364 emit "mov #{target_ref}, #{@RETURN_REG}\n"
370 if words.length == 3 && binop?(op)
372 binop op, target, words[1], words[2]
374 # Not a binary operation
375 eval_expr words, @RETURN_REG
376 target_ref = load_value target, @BX
377 emit "mov #{target_ref}, #{@RETURN_REG}\n"
382 # Set the byte at _base_ + _offset_ to _value_
383 def set_byte base, offset, value
384 emit "; set-byte #{base} #{offset} #{value}\n"
385 value_ref = load_value value, @RETURN_REG
386 addr_ref = load_address base, offset, 1
387 emit "mov byte #{addr_ref}, #{value_ref}\n"
390 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
391 def set_word base, offset, value
392 emit "; set-word #{base} #{offset} #{value}\n"
393 if immediate_operand?(value)
396 load_value_into_register value, @RETURN_REG
397 value_ref = @RETURN_REG
399 addr_ref = load_address base, offset, @WORDSIZE
400 emit "mov #{@WORD_NAME} #{addr_ref}, #{value_ref}\n"
404 # == Binary Operations
407 # Emit code for a binary operation
408 def binop op, target, x, y
411 elsif symmetric_operation?(op) && y == target
414 # Cases that are handled specially
415 return div(target, x, y) if op == :div
416 return mod(target, x, y) if op == :mod
417 return mul(target, x, y) if op == :mul
419 target_ref = load_value target, @AX
420 x_ref = load_value x, @DX
421 y_ref = load_value y, @BX
423 if memory_operand?(target_ref)
424 if memory_operand?(x_ref) || memory_operand?(y_ref)
425 emit "mov #{@CX}, #{x_ref}\n"
426 emit "#{op} #{@CX}, #{y_ref}\n"
427 emit "mov #{target_ref}, #{@CX}\n"
429 emit "mov #{@WORD_NAME} #{target_ref}, #{x_ref}\n"
430 emit "#{op} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
433 raise "Can't happen: target_ref is #{target_ref.inspect}"
438 # Emit code for a binary operation where the first operand
440 def binop2 op, target, y
441 # Cases that are handled specially
442 return div2(target, target, y) if op == :div
443 return mod2(target, y) if op == :mod
444 return mul2(target, y) if op == :mul
446 target_ref = load_value target, @BX
447 y_ref = load_value y, @DX
448 if memory_operand?(target_ref) && memory_operand?(y_ref)
449 emit "mov #{@AX}, #{y_ref}\n"
450 emit "#{op} #{target_ref}, #{@AX}\n"
452 emit "#{op} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
456 # Divide x by y and store the quotient in target
459 target_ref = load_value target, @BX
460 emit "mov #{target_ref}, #{@AX}\n"
463 # Divide target by x and store the quotient in target
465 div target, target, x
468 # Divide x by y and store the remainder in target
471 target_ref = load_value target, @BX
472 emit "mov #{target_ref}, #{@DX}\n"
475 # Divide target by x and store the remainder in target
477 mod target, target, x
480 # Multiply x by y and store the result in target
483 target_ref = load_value target, @BX
484 emit "mov #{target_ref}, #{@RETURN_REG}\n"
487 # Multiply target by x and store the result in target
489 mul target, target, x
497 # The quotient is stored in @AX, the remainder in @DX.
499 x_ref = load_value_into_register x, @AX
500 y_ref = load_value y, @SCRATCH_REG
501 emit "mov #{@DX}, #{@AX}\n"
502 emit "sar #{@DX}, #{@WORDSIZE * 8 - 1}\n"
503 if immediate_operand?(y_ref)
504 set_register @BX, y_ref
507 emit "idiv #{@WORD_NAME} #{y_ref}\n"
511 # Evaluate an expression.
512 # The result is stored in _register_ (@RETURN_REG by default).
513 def eval_expr words, register = @RETURN_REG
516 emit "xor #{register}, #{register}\n"
518 load_value_into_register words[0], register
526 eval_div words[1], words[2]
527 set_register register, @AX
530 set_register register, 0
531 # Get address reference
532 address_ref = load_address words[1], words[2], 1
533 # Load byte from address
536 set_register 'al', address_ref
538 set_register 'bl', address_ref
540 set_register 'cl', address_ref
542 set_register 'dl', address_ref
545 set_register 'bl', address_ref
546 set_register register, @BX
549 address_ref = load_address words[1], words[2], @WORDSIZE
550 set_register register, address_ref
552 eval_div words[1], words[2]
553 set_register register, @DX
555 eval_mul words[1], words[2], register
557 load_value_into_register words[1], register
558 emit "not #{register}\n"
561 x_ref = load_value words[1], @DX
562 y_ref = load_value words[2], @BX
563 emit "mov #{register}, #{x_ref}\n"
564 emit "#{op} #{register}, #{y_ref}\n"
566 raise "Not a magic word: #{words[0]}"
573 # The result is stored in @AX by default, but
574 # a different register can be specified by passing
576 def eval_mul x, y, register = @AX
577 x_ref = load_value x, @DX
578 y_ref = load_value y, @BX
580 if immediate_operand? x_ref
581 if immediate_operand? y_ref
582 set_register register, x_ref * y_ref
584 emit "imul #{register}, #{y_ref}, #{x_ref}\n"
586 elsif immediate_operand? y_ref
587 emit "imul #{register}, #{x_ref}, #{y_ref}\n"
589 emit "mov #{register}, #{x_ref}\n"
590 emit "imul #{register}, #{y_ref}\n"
598 # Start a conditional using the specified branch instruction
599 # after the comparison.
600 def common_if branch, x, y = nil
601 load_value_into_register y, @DX if y
602 load_value_into_register x, @AX
603 truelabel = @environment.gensym
604 falselabel = @environment.gensym
605 @if_labels.push falselabel
607 emit "cmp #{@AX}, #{@DX}\n"
608 emit "#{branch} #{truelabel}\n"
609 emit "jmp #{falselabel}\n"
610 emit "#{truelabel}:\n"
615 label = @if_labels.pop
619 # Start the false path of a conditional.
622 newlabel = @environment.gensym
623 emit "jmp #{newlabel}\n"
624 label = @if_labels.pop
626 @if_labels.push newlabel
629 # Test if x is equal to y
631 emit "; ifeq #{x} #{y}\n"
635 # Test if x is greater than or equal to y
637 emit "; ifge #{x} #{y}\n"
638 common_if 'jge', x, y
641 # Test if x is strictly greater than y
643 emit "; ifgt #{x} #{y}\n"
647 # Test if x is less than or equal to y
649 emit "; ifle #{x} #{y}\n"
650 common_if 'jle', x, y
653 # Test if x is strictly less than y
655 emit "; iflt #{x} #{y}\n"
659 # Test if x different from y
661 emit "; ifne #{x} #{y}\n"
662 common_if 'jne', x, y
674 # Load a value into a register
675 def load_value_into_register value, register
676 value_ref = load_value value, register
677 set_register register, value_ref
680 # Set a register to a value.
681 # The value must be a valid operand to the mov instruction.
682 def set_register register, value_ref
687 emit "xor #{register}, #{register}\n"
689 emit "mov #{register}, #{value_ref}\n"
697 # Write generated code to the given IO object.
699 io.puts "bits #{@WORDSIZE * 8}\n\n"
700 @sections.each do |section,code|
702 io.puts "section #{section.to_s}"