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
21 # - @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 emit "global #{symbols.join ', '}\n"
42 # Continue execution at the given address
44 value_ref = load_value value, @SCRATCH_REG
45 emit "jmp #{value_ref}\n"
48 # Import labels into the current section
50 emit "extern #{symbols.join ', '}\n"
53 # Define a label in the current section
62 # Define a byte with the given value
67 # Define a dword with the given value
72 # Define a qword with the given value
77 # Define a string with the given value
81 value.each_byte do |b|
82 if b >= 32 && b < 127 && b != 39
86 code << ',' unless code.empty?
95 code << ',' unless code.empty?
100 code << "'" if in_quote
108 def align alignment = nil
110 # Get default alignment
113 alignment = @CODE_ALIGNMENT
115 alignment = @DATA_ALIGNMENT
117 alignment = @FUNCTION_ALIGNMENT
119 # Use data alignment as default
120 alignment = @DATA_ALIGNMENT
123 emit "align #{alignment}\n" unless alignment == 0
130 # Emit function epilogue.
131 def emit_function_epilogue formals = []
135 # End a function body
137 if @environment == @top_level
138 raise "Cannot end function when not in a function"
140 @environment = @top_level
144 # Return a from a function.
146 # _words_ may contain an expression to be evaluated. The result
147 # of the evaluation is returned from the function.
149 eval_expr(words) unless words.empty?
150 emit_function_epilogue
158 # Begins a new block.
159 def begin_block *code
160 # If entering a block at top level,
161 # Save @BP, then set @BP to @SP
162 if @environment == @top_level
164 emit "mov #{@BP}, #{@SP}\n"
166 environment = Environment.new @environment
167 @environment = environment
170 # Ends the current block.
172 # Restore old value of @environment
173 @environment = @environment.parent
175 # If returning to top level, restore old @BP
176 emit "leave\n" if @environment == @top_level
180 # == Memory Allocation
183 # Allocates n bytes on the stack and stores a pointer to the allocated
184 # memory in the specified register. The number of bytes is rounded up
185 # to the nearest multiple of @STACK_ALIGNMENT.
186 def auto_bytes n, register = @RETURN_REG
187 if n.kind_of? Integer
188 auto_bytes_immediate n, register
190 load_value_into_register n, register
191 auto_bytes_register register, register
195 # Implements auto_bytes where the number of bytes to allocate is given
196 # as an immediate value.
197 def auto_bytes_immediate nbytes, register
198 nbytes = ((nbytes + @STACK_ALIGNMENT - 1) >> @STACK_ALIGNMENT_BITS) <<
199 @STACK_ALIGNMENT_BITS
200 emit "sub #{@SP}, #{nbytes}\n"
201 emit "mov #{register}, #{@SP}\n" if register != @SP
204 # Implements auto_bytes where the number of bytes is supplied in a
206 def auto_bytes_register nbytes, register = @RETURN_REG
207 emit "add #{nbytes}, #{@STACK_ALIGNMENT - 1}\n"
208 emit "shr #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
209 emit "shl #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
210 emit "sub #{@SP}, #{nbytes}\n"
211 emit "mov #{register}, #{@SP}\n" if register != @SP
214 # Allocates n words on the stack and stores a pointer to the allocated
215 # memory in the specified register.
216 def auto_words n, register = @RETURN_REG
217 if n.kind_of? Integer
218 auto_bytes_immediate n * @WORDSIZE, register
220 load_value_into_register n, register
221 if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
222 emit "add #{register}, " +
223 "#{(1 << @STACK_ALIGNMENT_BITS >> @WORDSIZE_BITS) - 1}\n"
224 emit "shr #{register}, #{@STACK_ALIGNMENT_BITS - @WORDSIZE_BITS}\n"
225 emit "shl #{register}, #{STACK_ALIGNMENT_BITS}\n"
227 emit "shl #{register}, #{@WORDSIZE_BITS}\n"
229 emit "sub #{@SP}, #{register}\n"
230 emit "mov #{register}, #{@SP}\n" if register != @SP
235 # Saving and restoring frames.
238 # Given some local variable names, returns the registers those variables
239 # are stored in. If no variable names are given, returns all registers
240 # used to store local variables.
241 def registers_for_locals locals = []
242 locals = @environment.symbols.keys if locals.empty?
245 reg = @environment[sym]
246 registers << reg if @LOCAL_REGISTERS_SET.include? @environment[sym]
251 # Restores the frame saved at the given location.
252 def restore_frame frame
253 restore_registers_from_frame frame, @SAVE_FRAME_REGISTERS
256 # Restores local variables from a saved frame.
257 def restore_locals frame, *locals
258 restore_registers_from_frame frame, registers_for_locals(locals)
261 # Helper function for restore_frame and restore_locals.
262 def restore_registers_from_frame frame, registers
263 load_value_into_register frame, @SCRATCH_REG
264 registers.each do |register|
265 i = @SAVED_FRAME_LAYOUT[register]
266 emit "mov #{register}, [#{@SCRATCH_REG} + #{i} * #{@WORDSIZE}]\n"
270 # Saves the current frame to the given location.
272 save_registers_to_frame frame, @SAVE_FRAME_REGISTERS
275 # Saves local variables to the given frame.
276 # If no locals are specified, saves all locals.
277 # If locals are specified, saves only the specified ones.
278 def save_locals frame, *locals
279 save_registers_to_frame frame, registers_for_locals(locals)
282 # Helper function for save_frame and save_locals.
283 def save_registers_to_frame frame, registers
284 load_value_into_register frame, @SCRATCH_REG
285 registers.each do |register|
286 i = @SAVED_FRAME_LAYOUT[register]
287 emit "mov [#{@SCRATCH_REG} + #{i} * #{@WORDSIZE}], #{register}\n"
291 # Returns the number of bytes necessary to save the current frame.
293 @SAVED_FRAME_LAYOUT.length * @WORDSIZE
300 # Introduces a new local variable.
301 def let symbol, *words
302 loc = local_offset_or_register @environment.locals
303 @environment.add_local symbol, loc
311 # End a conditional body
313 label = @if_labels.pop
318 # == Value Classification
321 # Tests if an operand is an immediate operand
322 def immediate_operand? operand
326 # Tests if an operand is a memory operand
327 def memory_operand? operand
328 operand.kind_of?(String) && operand[0] == ?[
335 # Create a value reference to an address.
336 # Invoking this code may clobber @BX and/or @CX
337 def load_address base, offset, scale
338 base_ref = load_value base, @BX
339 offset_ref = load_value offset, @CX
343 # Only an integer base
346 # Some complex base; load in @BX
347 emit "mov #{@BX}, #{base_ref}\n"
351 if integer? offset_ref
352 # Only a scaled offset
353 "[#{offset_ref.to_i * scale}]"
355 # Some complex offset; load in @CX
356 emit "mov #{@CX}, #{offset_ref}\n"
357 "[#{@CX} * #{scale}]"
359 elsif integer? base_ref
360 if integer? offset_ref
361 # All integers, combine them together
362 "[#{base_ref.to_i + (offset_ref.to_i * scale)}]"
364 # Complex offset; use @CX
365 emit "mov #{@CX}, #{offset_ref}\n"
366 "[#{base_ref} + #{@CX} * #{scale}]"
368 elsif integer? offset_ref
369 # Complex base, integer offset; use @BX
370 emit "mov #{@BX}, #{base_ref}\n"
371 "[#{@BX} + #{offset_ref.to_i * scale}]"
373 # Both base and offset are complex
374 # Use both @BX and @CX
375 emit "mov #{@BX}, #{base_ref}\n"
376 emit "mov #{@CX}, #{offset_ref}\n"
377 "[#{@BX} + #{@CX} * #{scale}]"
381 # Load the value at the given address.
382 # Invoking this code may clobber @BX.
383 def load_at address, reg = @SCRATCH_REG
384 if integer?(address) || global?(address)
387 load_value_into_register address, @BX
392 # Loads the value associated with the given symbol.
393 def load_symbol symbol, reg = @SCRATCH_REG
394 x = @environment[symbol]
397 elsif x.kind_of? Integer
406 # Returns a string that can be used to refer to the loaded value.
407 def load_value value, reg = @SCRATCH_REG
408 if substitution? value
409 value = substitute_number value[1]
413 if @WORDSIZE > 4 && (value < -2147483648 || value > 2147483647)
414 # AMD64 can load immediate values that are outside the range
415 # that can be represented as a 32-bit signed integer, but
416 # only with a mov instruction that loads the value into a
418 emit "mov #{@WORD_NAME} #{reg}, #{value}\n"
424 load_symbol value, reg
426 load_at value[1], reg
428 raise "Don't know how to load #{value.inspect}"
432 # Loads a value into a register.
433 def load_value_into_register value, register
434 value_ref = load_value value, register
435 set_register register, value_ref
442 # Evaluate the expr in words and store the result in target
443 def set target, *words
445 raise "Cannot change value of integer #{target}"
446 elsif global?(target)
447 raise "Cannot change value of global #{target}"
450 if words.length != 1 || words[0] != target
451 if symbol?(target) && symbol?(@environment[target])
452 eval_expr words, @environment[target]
454 eval_expr words, @RETURN_REG
455 target_ref = load_value target, @BX
456 emit "mov #{target_ref}, #{@RETURN_REG}\n"
461 # Set the byte at _base_ + _offset_ to _value_
462 def set_byte base, offset, value
463 if immediate_operand?(value)
466 load_value_into_register value, @RETURN_REG
469 addr_ref = load_address base, offset, 1
470 emit "mov byte #{addr_ref}, #{value_ref}\n"
473 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
474 def set_word base, offset, value
475 if immediate_operand?(value)
476 value_ref = load_value value, @RETURN_REG
478 load_value_into_register value, @RETURN_REG
479 value_ref = @RETURN_REG
481 addr_ref = load_address base, offset, @WORDSIZE
482 emit "mov #{@WORD_NAME} #{addr_ref}, #{value_ref}\n"
485 # Divide x by y and store the quotient in target
488 target_ref = load_value target, @BX
489 emit "mov #{target_ref}, #{@AX}\n"
492 # Divide target by x and store the quotient in target
494 div target, target, x
497 # Divide x by y and store the remainder in target
500 target_ref = load_value target, @BX
501 emit "mov #{target_ref}, #{@DX}\n"
504 # Divide target by x and store the remainder in target
506 mod target, target, x
509 # Multiply x by y and store the result in target
512 target_ref = load_value target, @BX
513 emit "mov #{target_ref}, #{@RETURN_REG}\n"
516 # Multiply target by x and store the result in target
518 mul target, target, x
526 # The quotient is stored in @AX, the remainder in @DX.
528 x_ref = load_value_into_register x, @AX
529 y_ref = load_value y, @SCRATCH_REG
530 emit "mov #{@DX}, #{@AX}\n"
531 emit "sar #{@DX}, #{@WORDSIZE * 8 - 1}\n"
532 if immediate_operand?(y_ref)
533 set_register @BX, y_ref
536 emit "idiv #{@WORD_NAME} #{y_ref}\n"
540 # Evaluate an expression.
541 # The result is stored in _register_ (@RETURN_REG by default).
542 # The following registers may be clobbered: @AX, @BX, @CX, @DX
543 def eval_expr words, register = @RETURN_REG
546 emit "xor #{register}, #{register}\n"
548 load_value_into_register words[0], register
553 when :asr, :bsr, :rol, :ror, :shl, :shr
557 load_value_into_register words[2], @CX
560 load_value_into_register words[1], register
561 emit "#{action_to_mnemonic op} #{register}, #{y_ref}\n"
563 auto_bytes words[1], register
565 auto_words words[1], register
568 emit "mov #{register}, #{@RETURN_REG}\n" if register != @RETURN_REG
570 eval_div words[1], words[2]
571 set_register register, @AX
573 # Get address reference
574 address_ref = load_address words[1], words[2], 1
575 # Load byte from address
578 set_register register, 0
579 set_register :al, address_ref
581 set_register register, 0
582 set_register :bl, address_ref
584 set_register register, 0
585 set_register :cl, address_ref
587 set_register register, 0
588 set_register :dl, address_ref
591 set_register :al, address_ref
592 set_register register, @AX
595 address_ref = load_address words[1], words[2], @WORDSIZE
596 set_register register, address_ref
598 eval_div words[1], words[2]
599 set_register register, @DX
601 eval_mul words[1], words[2], register
603 load_value_into_register words[1], register
604 emit "not #{register}\n"
607 x_ref = load_value words[1], @DX
608 y_ref = load_value words[2], @BX
609 emit "mov #{register}, #{x_ref}\n" unless register == x_ref
610 emit "#{op} #{register}, #{y_ref}\n"
612 raise "Not a magic word: #{words[0]}"
619 # The result is stored in @AX by default, but
620 # a different register can be specified by passing
622 def eval_mul x, y, register = @AX
623 x_ref = load_value x, @DX
624 y_ref = load_value y, @BX
626 if immediate_operand? x_ref
627 if immediate_operand? y_ref
628 set_register register, x_ref * y_ref
630 emit "imul #{register}, #{y_ref}, #{x_ref}\n"
632 elsif immediate_operand? y_ref
633 emit "imul #{register}, #{x_ref}, #{y_ref}\n"
634 elsif y_ref != register
635 emit "mov #{register}, #{x_ref}\n" unless x_ref == register
636 emit "imul #{register}, #{y_ref}\n"
638 emit "imul #{register}, #{x_ref}\n"
646 # Start a conditional using the specified branch instruction
647 # after the comparison.
648 def common_if branch, x, y = nil
649 # Inverses of branches. E.g.
664 y_ref = load_value y, @DX
665 x_ref = load_value x, @AX
666 if immediate_operand?(x_ref)
667 # Can't have an immediate value as the first operand.
668 if immediate_operand?(y_ref)
669 # Both are immediates. Put the first in a register.
670 emit "mov #{@AX}, #{x_ref}\n"
673 # y isn't immediate; swap x and y.
674 x_ref, y_ref = [y_ref, x_ref]
675 branch = inverse_branch[branch]
677 elsif memory_operand?(x_ref) && memory_operand?(y_ref)
678 # Can't have two memory operands. Move the first into a register.
679 emit "mov #{@AX}, #{x_ref}\n"
682 truelabel = @environment.gensym
683 falselabel = @environment.gensym
684 @if_labels.push falselabel
686 emit "cmp #{@WORD_NAME} #{x_ref}, #{y_ref}\n"
687 emit "#{branch} #{truelabel}\n"
688 emit "jmp #{falselabel}\n"
689 emit "#{truelabel}:\n"
694 label = @if_labels.pop
698 # Start the false path of a conditional.
700 newlabel = @environment.gensym
701 emit "jmp #{newlabel}\n"
702 label = @if_labels.pop
704 @if_labels.push newlabel
707 # Test if x is equal to y
712 # Test if x is greater than or equal to y
717 # Test if x is strictly greater than y
722 # Test if x is less than or equal to y
727 # Test if x is strictly less than y
732 # Test if x different from y
741 # Translates a Voodoo action name to an x86 mnemonic
742 def action_to_mnemonic action
758 # Returns a memory reference for the address at the given offset
759 # from the frame pointer.
760 def offset_reference offset
762 "[#{@BP} + #{offset}]"
764 "[#{@BP} - #{-offset}]"
770 # Set a register to a value.
771 # The value must be a valid operand to the mov instruction.
772 def set_register register, value_ref
777 emit "xor #{register}, #{register}\n"
779 emit "mov #{register}, #{value_ref}\n"
787 # Write generated code to the given IO object.
789 io.puts "bits #{@WORDSIZE * 8}\n\n"
790 @sections.each do |section,code|
792 io.puts "section #{section.to_s}"