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, @DX, @BP, and @SP
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 < 127 && b != 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 # Begins a new block.
180 emit "; begin block\n"
181 # If entering a block at top level,
182 # Save @BP, then set @BP to @SP
183 if @environment == @top_level
185 emit "mov #{@BP}, #{@SP}\n"
187 environment = Environment.new @environment
188 @environment = environment
191 # Ends the current block.
195 # De-allocate block's variables
196 nvars = @environment.locals - @environment.parent.locals
197 emit "add #{@SP}, #{nvars * @WORDSIZE}\n" unless nvars == 0
199 # Restore old value of @environment
200 @environment = @environment.parent
202 # If returning to top level, restore old @BP
203 emit "pop #{@BP}\n" if @environment == @top_level
210 # End a conditional body
212 label = @if_labels.pop
217 # == Value Classification
220 # Test if a value is an at-expression
222 value.respond_to?(:[]) && value[0] == :'@'
225 # Test if op is a binary operation
227 [:div, :mod, :sub].member?(op) || symmetric_operation?(op)
230 # Test if a value is an integer
232 value.kind_of? Integer
235 # Test if a symbol refers to a global
237 symbol?(symbol) && @environment[symbol] == nil
240 # Tests if an operand is an immediate operand
241 def immediate_operand? operand
242 integer?(operand) || global?(operand)
245 # Tests if an operand is a memory operand
246 def memory_operand? operand
250 # Test if a value is a symbol
252 value.kind_of? Symbol
255 # Test if op is a symmetric operation (i.e. it will yield the
256 # same result if the order of its source operands is changed).
257 def symmetric_operation? op
258 [:add, :and, :mul, :or, :xor].member? op
265 # Create a value reference to an address.
266 # Invoking this code may clobber @BX and/or @CX
267 def load_address base, offset, scale
268 base_ref = load_value base, @BX
269 offset_ref = load_value offset, @CX
273 # Only an integer base
276 # Some complex base; load in @BX
277 emit "mov #{@BX}, #{base_ref}\n"
281 if integer? offset_ref
282 # Only a scaled offset
283 "[#{offset_ref.to_i * scale}]"
285 # Some complex offset; load in @CX
286 emit "mov #{@CX}, #{offset_ref}\n"
287 "[#{@CX} * #{scale}]"
289 elsif integer? base_ref
290 if integer? offset_ref
291 # All integers, combine them together
292 "[#{base_ref.to_i + (offset_ref.to_i * scale)}]"
294 # Complex offset; use @CX
295 emit "mov #{@CX}, #{offset_ref}\n"
296 "[#{base_ref} + #{@CX} * #{scale}]"
298 elsif integer? offset_ref
299 # Complex base, integer offset; use @BX
300 emit "mov #{@BX}, #{base_ref}\n"
301 "[#{@BX} + #{offset_ref.to_i * scale}]"
303 # Both base and offset are complex
304 # Use both @BX and @CX
305 emit "mov #{@BX}, #{base_ref}\n"
306 emit "mov #{@CX}, #{offset_ref}\n"
307 "[#{@BX} + #{@CX} * #{scale}]"
311 # Load the value at the given address.
312 # Invoking this code may clobber @BX.
313 def load_at address, reg = @SCRATCH_REG
314 if integer?(address) || global?(address)
317 load_value_into_register address, @BX
322 # Load the value associated with the given symbol.
323 # Returns a string that can be used to refer to the loaded value.
324 def load_symbol symbol, reg = @SCRATCH_REG
325 x = @environment[symbol]
333 raise "Invalid variable type: #{x[0]}"
342 # Returns a string that can be used to refer to the loaded value.
343 def load_value value, reg = @SCRATCH_REG
345 # Integers can be used as is
348 load_symbol value, reg
350 load_at value[1], reg
352 raise "Don't know how to load #{value.inspect}"
356 # Load a value into a register.
357 def load_value_into_register value, register
358 load_code = load_value(value), register
359 if load_code == register.to_s
362 "mov #{register}, #{load_code}\n"
370 # Evaluate the expr in words and store the result in target
371 def set target, *words
373 raise "Cannot change value of integer #{target}"
374 elsif global?(target)
375 raise "Cannot change value of global #{target}"
378 emit "; set #{target} #{words.join ' '}\n"
380 if words[0] == target
381 emit "; nothing to do; destination equals source\n"
383 target_ref = load_value target, @BX
384 if integer?(words[0])
385 if words[0].to_i == 0
386 # Set destination to 0
387 emit "xor #{@AX}, #{@AX}\n"
388 emit "mov #{target_ref}, #{@AX}\n"
391 emit "mov #{@WORD_NAME} #{target_ref}, #{words[0]}\n"
394 # Copy source to destination
395 eval_expr words, @RETURN_REG
396 emit "mov #{target_ref}, #{@RETURN_REG}\n"
402 if words.length == 3 && binop?(op)
404 binop op, target, words[1], words[2]
406 # Not a binary operation
407 eval_expr words, @RETURN_REG
408 target_ref = load_value target, @BX
409 emit "mov #{target_ref}, #{@RETURN_REG}\n"
414 # Set the byte at _base_ + _offset_ to _value_
415 def set_byte base, offset, value
416 emit "; set-byte #{base} #{offset} #{value}\n"
417 value_ref = load_value value, @RETURN_REG
418 addr_ref = load_address base, offset, 1
419 emit "mov byte #{addr_ref}, #{value_ref}\n"
422 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
423 def set_word base, offset, value
424 emit "; set-word #{base} #{offset} #{value}\n"
425 if immediate_operand?(value)
428 load_value_into_register value, @RETURN_REG
429 value_ref = @RETURN_REG
431 addr_ref = load_address base, offset, @WORDSIZE
432 emit "mov #{@WORD_NAME} #{addr_ref}, #{value_ref}\n"
436 # == Binary Operations
439 # Emit code for a binary operation
440 def binop op, target, x, y
443 elsif symmetric_operation?(op) && y == target
446 # Cases that are handled specially
447 return div(target, x, y) if op == :div
448 return mod(target, x, y) if op == :mod
449 return mul(target, x, y) if op == :mul
451 target_ref = load_value target, @AX
452 x_ref = load_value x, @DX
453 y_ref = load_value y, @BX
455 if memory_operand?(target_ref)
456 if memory_operand?(x_ref) || memory_operand?(y_ref)
457 emit "mov #{@CX}, #{x_ref}\n"
458 emit "#{op} #{@CX}, #{y_ref}\n"
459 emit "mov #{target_ref}, #{@CX}\n"
461 emit "mov #{@WORD_NAME} #{target_ref}, #{x_ref}\n"
462 emit "#{op} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
465 raise "Can't happen: target_ref is #{target_ref.inspect}"
470 # Emit code for a binary operation where the first operand
472 def binop2 op, target, y
473 # Cases that are handled specially
474 return div2(target, target, y) if op == :div
475 return mod2(target, y) if op == :mod
476 return mul2(target, y) if op == :mul
478 target_ref = load_value target, @BX
479 y_ref = load_value y, @DX
480 if memory_operand?(target_ref) && memory_operand?(y_ref)
481 emit "mov #{@AX}, #{y_ref}\n"
482 emit "#{op} #{target_ref}, #{@AX}\n"
484 emit "#{op} #{@WORD_NAME} #{target_ref}, #{y_ref}\n"
488 # Divide x by y and store the quotient in target
491 target_ref = load_value target, @BX
492 emit "mov #{target_ref}, #{@AX}\n"
495 # Divide target by x and store the quotient in target
497 div target, target, x
500 # Divide x by y and store the remainder in target
503 target_ref = load_value target, @BX
504 emit "mov #{target_ref}, #{@DX}\n"
507 # Divide target by x and store the remainder in target
509 mod target, target, x
512 # Multiply x by y and store the result in target
515 target_ref = load_value target, @BX
516 emit "mov #{target_ref}, #{@RETURN_REG}\n"
519 # Multiply target by x and store the result in target
521 mul target, target, x
529 # The quotient is stored in @AX, the remainder in @DX.
531 x_ref = load_value_into_register x, @AX
532 y_ref = load_value y, @SCRATCH_REG
533 emit "mov #{@DX}, #{@AX}\n"
534 emit "sar #{@DX}, #{@WORDSIZE * 8 - 1}\n"
535 if immediate_operand?(y_ref)
536 set_register @BX, y_ref
539 emit "idiv #{@WORD_NAME} #{y_ref}\n"
543 # Evaluate an expression.
544 # The result is stored in _register_ (@RETURN_REG by default).
545 def eval_expr words, register = @RETURN_REG
548 emit "xor #{register}, #{register}\n"
550 load_value_into_register words[0], register
558 eval_div words[1], words[2]
559 set_register register, @AX
562 set_register register, 0
563 # Get address reference
564 address_ref = load_address words[1], words[2], 1
565 # Load byte from address
568 set_register 'al', address_ref
570 set_register 'bl', address_ref
572 set_register 'cl', address_ref
574 set_register 'dl', address_ref
577 set_register 'bl', address_ref
578 set_register register, @BX
581 address_ref = load_address words[1], words[2], @WORDSIZE
582 set_register register, address_ref
584 eval_div words[1], words[2]
585 set_register register, @DX
587 eval_mul words[1], words[2], register
589 load_value_into_register words[1], register
590 emit "not #{register}\n"
593 x_ref = load_value words[1], @DX
594 y_ref = load_value words[2], @BX
595 emit "mov #{register}, #{x_ref}\n"
596 emit "#{op} #{register}, #{y_ref}\n"
598 raise "Not a magic word: #{words[0]}"
605 # The result is stored in @AX by default, but
606 # a different register can be specified by passing
608 def eval_mul x, y, register = @AX
609 x_ref = load_value x, @DX
610 y_ref = load_value y, @BX
612 if immediate_operand? x_ref
613 if immediate_operand? y_ref
614 set_register register, x_ref * y_ref
616 emit "imul #{register}, #{y_ref}, #{x_ref}\n"
618 elsif immediate_operand? y_ref
619 emit "imul #{register}, #{x_ref}, #{y_ref}\n"
621 emit "mov #{register}, #{x_ref}\n"
622 emit "imul #{register}, #{y_ref}\n"
630 # Start a conditional using the specified branch instruction
631 # after the comparison.
632 def common_if branch, x, y = nil
633 load_value_into_register y, @DX if y
634 load_value_into_register x, @AX
635 truelabel = @environment.gensym
636 falselabel = @environment.gensym
637 @if_labels.push falselabel
639 emit "cmp #{@AX}, #{@DX}\n"
640 emit "#{branch} #{truelabel}\n"
641 emit "jmp #{falselabel}\n"
642 emit "#{truelabel}:\n"
647 label = @if_labels.pop
651 # Start the false path of a conditional.
654 newlabel = @environment.gensym
655 emit "jmp #{newlabel}\n"
656 label = @if_labels.pop
658 @if_labels.push newlabel
661 # Test if x is equal to y
663 emit "; ifeq #{x} #{y}\n"
667 # Test if x is greater than or equal to y
669 emit "; ifge #{x} #{y}\n"
670 common_if 'jge', x, y
673 # Test if x is strictly greater than y
675 emit "; ifgt #{x} #{y}\n"
679 # Test if x is less than or equal to y
681 emit "; ifle #{x} #{y}\n"
682 common_if 'jle', x, y
685 # Test if x is strictly less than y
687 emit "; iflt #{x} #{y}\n"
691 # Test if x different from y
693 emit "; ifne #{x} #{y}\n"
694 common_if 'jne', x, y
706 # Load a value into a register
707 def load_value_into_register value, register
708 value_ref = load_value value, register
709 set_register register, value_ref
712 # Set a register to a value.
713 # The value must be a valid operand to the mov instruction.
714 def set_register register, value_ref
719 emit "xor #{register}, #{register}\n"
721 emit "mov #{register}, #{value_ref}\n"
729 # Write generated code to the given IO object.
731 io.puts "bits #{@WORDSIZE * 8}\n\n"
732 @sections.each do |section,code|
734 io.puts "section #{section.to_s}"