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:
18 # - @FUNCTION_ALIGNMENT
20 # - @STACK_ALIGNMENT_BITS
25 # - @AX, @BX, @CX, @DX, @BP, and @SP
26 class NasmGenerator < CommonCodeGenerator
27 def initialize params = {}
30 @output_file_suffix = '.asm'
37 # Export symbols from the current section
39 case real_section_name(section)
41 symbols.each { |sym| emit "global #{sym}:function\n" }
43 @relocated_symbols.merge symbols
44 symbols.each { |sym| emit "global #{sym}:data #{sym}.end-#{sym}\n" }
48 # Continue execution at the given address
50 with_temporary do |temporary|
51 value_ref = load_value value, temporary
52 emit "jmp #{value_ref}\n"
56 # Import labels into the current section
58 @symbol_tracker.define *symbols
59 if real_section_name(section) != ".text"
60 @relocated_symbols.merge symbols
62 emit "extern #{symbols.join ', '}\n"
70 def emit_label_size name
74 def emit_label_type name, type
82 # Define a byte with the given value
87 # Define a dword with the given value
92 # Define a qword with the given value
97 # Define a string with the given value
101 value.each_byte do |b|
102 if b >= 32 && b < 127 && b != 39
106 code << ',' unless code.empty?
115 code << ',' unless code.empty?
120 code << "'" if in_quote
128 def emit_align alignment
129 emit "align #{alignment}\n"
136 # Emits function epilogue.
137 def emit_function_epilogue formals = []
141 # Ends a function body
143 if @environment == @top_level
144 raise "Cannot end function when not in a function"
146 @environment = @top_level
150 # Returns from a function.
152 # _words_ may contain an expression to be evaluated. The result
153 # of the evaluation is returned from the function.
155 eval_expr(words) unless words.empty?
156 emit_function_epilogue
164 # Begins a new block.
165 def begin_block *code
166 # If entering a block at top level,
167 # Save @BP, then set @BP to @SP
168 if @environment == @top_level
170 emit "mov #{@BP}, #{@SP}\n"
172 environment = Environment.new @environment
173 @environment = environment
176 # Ends the current block.
178 # Restore old value of @environment
179 @environment = @environment.parent
181 # If returning to top level, restore old @BP
182 emit "leave\n" if @environment == @top_level
186 # == Memory Allocation
189 # Allocates n bytes on the stack and stores a pointer to the allocated
190 # memory in the specified register. The number of bytes is rounded up
191 # to the nearest multiple of @STACK_ALIGNMENT.
192 def auto_bytes n, register = @RETURN_REG
193 if n.kind_of? Integer
194 auto_bytes_immediate n, register
196 load_value_into_register n, register
197 auto_bytes_register register, register
201 # Implements auto_bytes where the number of bytes to allocate is given
202 # as an immediate value.
203 def auto_bytes_immediate nbytes, register
204 nbytes = ((nbytes + @STACK_ALIGNMENT - 1) >> @STACK_ALIGNMENT_BITS) <<
205 @STACK_ALIGNMENT_BITS
206 emit "sub #{@SP}, #{nbytes}\n"
207 emit "mov #{register}, #{@SP}\n" if register != @SP
210 # Implements auto_bytes where the number of bytes is supplied in a
212 def auto_bytes_register nbytes, register = @RETURN_REG
213 emit "add #{nbytes}, #{@STACK_ALIGNMENT - 1}\n"
214 emit "shr #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
215 emit "shl #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
216 emit "sub #{@SP}, #{nbytes}\n"
217 emit "mov #{register}, #{@SP}\n" if register != @SP
220 # Allocates n words on the stack and stores a pointer to the allocated
221 # memory in the specified register.
222 def auto_words n, register = @RETURN_REG
223 if n.kind_of? Integer
224 auto_bytes_immediate n * @WORDSIZE, register
226 load_value_into_register n, register
227 if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
228 emit "add #{register}, " +
229 "#{(1 << @STACK_ALIGNMENT_BITS >> @WORDSIZE_BITS) - 1}\n"
230 emit "shr #{register}, #{@STACK_ALIGNMENT_BITS - @WORDSIZE_BITS}\n"
231 emit "shl #{register}, #{STACK_ALIGNMENT_BITS}\n"
233 emit "shl #{register}, #{@WORDSIZE_BITS}\n"
235 emit "sub #{@SP}, #{register}\n"
236 emit "mov #{register}, #{@SP}\n" if register != @SP
244 # Introduces a new local variable.
245 def let symbol, *words
246 loc = local_offset_or_register @environment.locals
247 @environment.add_local symbol, loc
255 # End a conditional body
257 label = @if_labels.pop
262 # == Value Classification
265 # Tests if an operand is an immediate operand
266 def immediate_operand? operand
270 # Tests if an operand is a memory operand
271 def memory_operand? operand
272 operand.kind_of?(String) && operand[0] == ?[
279 # Loads a word into a register.
280 def emit_load_word register, base, offset = 0
282 emit "mov #{register}, [#{base}]\n"
284 emit "mov #{register}, [#{base} + #{offset} * #{@WORDSIZE}]\n"
288 # Create a value reference to an address.
289 # Invoking this code may clobber @BX and/or @CX
290 def load_address base, offset, scale
291 base_ref = load_value base, @BX
292 offset_ref = load_value offset, @CX
296 # Only an integer base
299 # Some complex base; load in @BX
300 emit "mov #{@BX}, #{base_ref}\n"
304 if integer? offset_ref
305 # Only a scaled offset
306 "[#{offset_ref.to_i * scale}]"
308 # Some complex offset; load in @CX
309 emit "mov #{@CX}, #{offset_ref}\n"
310 "[#{@CX} * #{scale}]"
312 elsif integer? base_ref
313 if integer? offset_ref
314 # All integers, combine them together
315 "[#{base_ref.to_i + (offset_ref.to_i * scale)}]"
317 # Complex offset; use @CX
318 emit "mov #{@CX}, #{offset_ref}\n"
319 "[#{base_ref} + #{@CX} * #{scale}]"
321 elsif integer? offset_ref
322 # Complex base, integer offset; use @BX
323 emit "mov #{@BX}, #{base_ref}\n"
324 "[#{@BX} + #{offset_ref.to_i * scale}]"
326 # Both base and offset are complex
327 # Use both @BX and @CX
328 emit "mov #{@BX}, #{base_ref}\n"
329 emit "mov #{@CX}, #{offset_ref}\n"
330 "[#{@BX} + #{@CX} * #{scale}]"
334 # Load the value at the given address.
335 # Invoking this code may clobber @BX.
336 def load_at address, reg
337 if integer?(address) || (global?(address) &&
338 !@relocated_symbols.include?(address))
341 load_value_into_register address, @BX
346 # Loads the value associated with the given symbol.
347 def load_symbol symbol, reg
348 x = @environment[symbol]
351 elsif x.kind_of? Integer
355 @symbol_tracker.use symbol
356 if @relocated_symbols.include? symbol
357 load_symbol_from_got symbol, reg
365 # Returns a string that can be used to refer to the loaded value.
366 def load_value value, reg
367 if substitution? value
368 value = substitute_number value[1]
372 if @WORDSIZE > 4 && (value < -2147483648 || value > 2147483647)
373 # AMD64 can load immediate values that are outside the range
374 # that can be represented as a 32-bit signed integer, but
375 # only with a mov instruction that loads the value into a
377 emit "mov #{@WORD_NAME} #{reg}, #{value}\n"
383 load_symbol value, reg
385 load_at value[1], reg
387 raise "Don't know how to load #{value.inspect}"
391 # Loads a value into a register.
392 def load_value_into_register value, register
393 value_ref = load_value value, register
394 set_register register, value_ref
401 # Stores the value of a register in memory.
402 def emit_store_word register, base, offset = 0
404 emit "mov [#{base}], #{register}\n"
406 emit "mov [#{base} + #{offset} * #{@WORDSIZE}], #{register}\n"
410 # Evaluate the expr in words and store the result in target
411 def set target, *words
413 raise "Cannot change value of integer #{target}"
414 elsif global?(target)
415 raise "Cannot change value of global #{target}"
418 if words.length != 1 || words[0] != target
419 if symbol?(target) && symbol?(@environment[target])
420 eval_expr words, @environment[target]
422 eval_expr words, @RETURN_REG
423 target_ref = load_value target, @BX
424 emit "mov #{target_ref}, #{@RETURN_REG}\n"
429 # Set the byte at _base_ + _offset_ to _value_
430 def set_byte base, offset, value
431 if immediate_operand?(value)
434 load_value_into_register value, @RETURN_REG
437 addr_ref = load_address base, offset, 1
438 emit "mov byte #{addr_ref}, #{value_ref}\n"
441 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
442 def set_word base, offset, value
443 if immediate_operand?(value)
444 value_ref = load_value value, @RETURN_REG
446 load_value_into_register value, @RETURN_REG
447 value_ref = @RETURN_REG
449 addr_ref = load_address base, offset, @WORDSIZE
450 emit "mov #{@WORD_NAME} #{addr_ref}, #{value_ref}\n"
453 # Divide x by y and store the quotient in target
456 target_ref = load_value target, @BX
457 emit "mov #{target_ref}, #{@AX}\n"
460 # Divide target by x and store the quotient in target
462 div target, target, x
465 # Divide x by y and store the remainder in target
468 target_ref = load_value target, @BX
469 emit "mov #{target_ref}, #{@DX}\n"
472 # Divide target by x and store the remainder in target
474 mod target, target, x
477 # Multiply x by y and store the result in target
480 target_ref = load_value target, @BX
481 emit "mov #{target_ref}, #{@RETURN_REG}\n"
484 # Multiply target by x and store the result in target
486 mul target, target, x
494 # The quotient is stored in @AX, the remainder in @DX.
496 with_temporary do |temporary|
497 x_ref = load_value_into_register x, @AX
498 y_ref = load_value y, temporary
499 emit "mov #{@DX}, #{@AX}\n"
500 emit "sar #{@DX}, #{@WORDSIZE * 8 - 1}\n"
501 if immediate_operand?(y_ref)
502 set_register @BX, y_ref
505 emit "idiv #{@WORD_NAME} #{y_ref}\n"
510 # Evaluate an expression.
511 # The result is stored in _register_ (@RETURN_REG by default).
512 # The following registers may be clobbered: @AX, @BX, @CX, @DX
513 def eval_expr words, register = @RETURN_REG
516 emit "xor #{register}, #{register}\n"
518 load_value_into_register words[0], register
523 when :asr, :bsr, :rol, :ror, :shl, :shr
527 load_value_into_register words[2], @CX
530 load_value_into_register words[1], register
531 emit "#{action_to_mnemonic op} #{register}, #{y_ref}\n"
533 auto_bytes words[1], register
535 auto_words words[1], register
538 emit "mov #{register}, #{@RETURN_REG}\n" if register != @RETURN_REG
540 eval_div words[1], words[2]
541 set_register register, @AX
543 # Get address reference
544 address_ref = load_address words[1], words[2], 1
545 # Load byte from address
548 set_register register, 0
549 set_register :al, address_ref
551 set_register register, 0
552 set_register :bl, address_ref
554 set_register register, 0
555 set_register :cl, address_ref
557 set_register register, 0
558 set_register :dl, address_ref
561 set_register :al, address_ref
562 set_register register, @AX
565 address_ref = load_address words[1], words[2], @WORDSIZE
566 set_register register, address_ref
568 eval_div words[1], words[2]
569 set_register register, @DX
571 eval_mul words[1], words[2], register
573 load_value_into_register words[1], register
574 emit "not #{register}\n"
577 x_ref = load_value words[1], @DX
578 y_ref = load_value words[2], @BX
579 emit "mov #{register}, #{x_ref}\n" unless register == x_ref
580 emit "#{op} #{register}, #{y_ref}\n"
582 raise "Not a magic word: #{words[0]}"
589 # The result is stored in @AX by default, but
590 # a different register can be specified by passing
592 def eval_mul x, y, register = @AX
593 x_ref = load_value x, @DX
594 y_ref = load_value y, @BX
596 if immediate_operand? x_ref
597 if immediate_operand? y_ref
598 set_register register, x_ref * y_ref
600 emit "imul #{register}, #{y_ref}, #{x_ref}\n"
602 elsif immediate_operand? y_ref
603 emit "imul #{register}, #{x_ref}, #{y_ref}\n"
604 elsif y_ref != register
605 emit "mov #{register}, #{x_ref}\n" unless x_ref == register
606 emit "imul #{register}, #{y_ref}\n"
608 emit "imul #{register}, #{x_ref}\n"
616 # Start a conditional using the specified branch instruction
617 # after the comparison.
618 def common_if branch, x, y = nil
619 # Inverses of branches. E.g.
634 y_ref = load_value y, @DX
635 x_ref = load_value x, @AX
636 if immediate_operand?(x_ref)
637 # Can't have an immediate value as the first operand.
638 if immediate_operand?(y_ref)
639 # Both are immediates. Put the first in a register.
640 emit "mov #{@AX}, #{x_ref}\n"
643 # y isn't immediate; swap x and y.
644 x_ref, y_ref = [y_ref, x_ref]
645 branch = inverse_branch[branch]
647 elsif memory_operand?(x_ref) && memory_operand?(y_ref)
648 # Can't have two memory operands. Move the first into a register.
649 emit "mov #{@AX}, #{x_ref}\n"
652 truelabel = @environment.gensym
653 falselabel = @environment.gensym
654 @if_labels.push falselabel
656 emit "cmp #{@WORD_NAME} #{x_ref}, #{y_ref}\n"
657 emit "#{branch} #{truelabel}\n"
658 emit "jmp #{falselabel}\n"
659 emit "#{truelabel}:\n"
664 label = @if_labels.pop
668 # Start the false path of a conditional.
670 newlabel = @environment.gensym
671 emit "jmp #{newlabel}\n"
672 label = @if_labels.pop
674 @if_labels.push newlabel
677 # Test if x is equal to y
682 # Test if x is greater than or equal to y
687 # Test if x is strictly greater than y
692 # Test if x is less than or equal to y
697 # Test if x is strictly less than y
702 # Test if x different from y
711 # Translates a Voodoo action name to an x86 mnemonic
712 def action_to_mnemonic action
728 # Returns a memory reference for the address at the given offset
729 # from the frame pointer.
730 def offset_reference offset
732 "[#{@BP} + #{offset}]"
734 "[#{@BP} - #{-offset}]"
740 # Set a register to a value.
741 # The value must be a valid operand to the mov instruction.
742 def set_register register, value_ref
747 emit "xor #{register}, #{register}\n"
749 emit "mov #{register}, #{value_ref}\n"
757 # Write generated code to the given IO object.
759 io.puts "bits #{@WORDSIZE * 8}\n\n"
760 @sections.each do |section,code|
762 io.puts "section #{section.to_s}"