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 emit "global #{symbols.join ', '}\n"
42 # Continue execution at the given address
44 with_temporary do |temporary|
45 value_ref = load_value value, temporary
46 emit "jmp #{value_ref}\n"
50 # Import labels into the current section
52 emit "extern #{symbols.join ', '}\n"
55 # Define a label in the current section
64 # Define a byte with the given value
69 # Define a dword with the given value
74 # Define a qword with the given value
79 # Define a string with the given value
83 value.each_byte do |b|
84 if b >= 32 && b < 127 && b != 39
88 code << ',' unless code.empty?
97 code << ',' unless code.empty?
102 code << "'" if in_quote
110 def emit_align alignment
111 emit "align #{alignment}\n"
118 # Emits function epilogue.
119 def emit_function_epilogue formals = []
123 # Ends a function body
125 if @environment == @top_level
126 raise "Cannot end function when not in a function"
128 @environment = @top_level
132 # Returns from a function.
134 # _words_ may contain an expression to be evaluated. The result
135 # of the evaluation is returned from the function.
137 eval_expr(words) unless words.empty?
138 emit_function_epilogue
146 # Begins a new block.
147 def begin_block *code
148 # If entering a block at top level,
149 # Save @BP, then set @BP to @SP
150 if @environment == @top_level
152 emit "mov #{@BP}, #{@SP}\n"
154 environment = Environment.new @environment
155 @environment = environment
158 # Ends the current block.
160 # Restore old value of @environment
161 @environment = @environment.parent
163 # If returning to top level, restore old @BP
164 emit "leave\n" if @environment == @top_level
168 # == Memory Allocation
171 # Allocates n bytes on the stack and stores a pointer to the allocated
172 # memory in the specified register. The number of bytes is rounded up
173 # to the nearest multiple of @STACK_ALIGNMENT.
174 def auto_bytes n, register = @RETURN_REG
175 if n.kind_of? Integer
176 auto_bytes_immediate n, register
178 load_value_into_register n, register
179 auto_bytes_register register, register
183 # Implements auto_bytes where the number of bytes to allocate is given
184 # as an immediate value.
185 def auto_bytes_immediate nbytes, register
186 nbytes = ((nbytes + @STACK_ALIGNMENT - 1) >> @STACK_ALIGNMENT_BITS) <<
187 @STACK_ALIGNMENT_BITS
188 emit "sub #{@SP}, #{nbytes}\n"
189 emit "mov #{register}, #{@SP}\n" if register != @SP
192 # Implements auto_bytes where the number of bytes is supplied in a
194 def auto_bytes_register nbytes, register = @RETURN_REG
195 emit "add #{nbytes}, #{@STACK_ALIGNMENT - 1}\n"
196 emit "shr #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
197 emit "shl #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
198 emit "sub #{@SP}, #{nbytes}\n"
199 emit "mov #{register}, #{@SP}\n" if register != @SP
202 # Allocates n words on the stack and stores a pointer to the allocated
203 # memory in the specified register.
204 def auto_words n, register = @RETURN_REG
205 if n.kind_of? Integer
206 auto_bytes_immediate n * @WORDSIZE, register
208 load_value_into_register n, register
209 if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
210 emit "add #{register}, " +
211 "#{(1 << @STACK_ALIGNMENT_BITS >> @WORDSIZE_BITS) - 1}\n"
212 emit "shr #{register}, #{@STACK_ALIGNMENT_BITS - @WORDSIZE_BITS}\n"
213 emit "shl #{register}, #{STACK_ALIGNMENT_BITS}\n"
215 emit "shl #{register}, #{@WORDSIZE_BITS}\n"
217 emit "sub #{@SP}, #{register}\n"
218 emit "mov #{register}, #{@SP}\n" if register != @SP
223 # Saving and restoring frames.
230 # Introduces a new local variable.
231 def let symbol, *words
232 loc = local_offset_or_register @environment.locals
233 @environment.add_local symbol, loc
241 # End a conditional body
243 label = @if_labels.pop
248 # == Value Classification
251 # Tests if an operand is an immediate operand
252 def immediate_operand? operand
256 # Tests if an operand is a memory operand
257 def memory_operand? operand
258 operand.kind_of?(String) && operand[0] == ?[
265 # Loads a word into a register.
266 def emit_load_word register, base, offset = 0
268 emit "mov #{register}, [#{base}]\n"
270 emit "mov #{register}, [#{base} + #{offset} * #{@WORDSIZE}]\n"
274 # Create a value reference to an address.
275 # Invoking this code may clobber @BX and/or @CX
276 def load_address base, offset, scale
277 base_ref = load_value base, @BX
278 offset_ref = load_value offset, @CX
282 # Only an integer base
285 # Some complex base; load in @BX
286 emit "mov #{@BX}, #{base_ref}\n"
290 if integer? offset_ref
291 # Only a scaled offset
292 "[#{offset_ref.to_i * scale}]"
294 # Some complex offset; load in @CX
295 emit "mov #{@CX}, #{offset_ref}\n"
296 "[#{@CX} * #{scale}]"
298 elsif integer? base_ref
299 if integer? offset_ref
300 # All integers, combine them together
301 "[#{base_ref.to_i + (offset_ref.to_i * scale)}]"
303 # Complex offset; use @CX
304 emit "mov #{@CX}, #{offset_ref}\n"
305 "[#{base_ref} + #{@CX} * #{scale}]"
307 elsif integer? offset_ref
308 # Complex base, integer offset; use @BX
309 emit "mov #{@BX}, #{base_ref}\n"
310 "[#{@BX} + #{offset_ref.to_i * scale}]"
312 # Both base and offset are complex
313 # Use both @BX and @CX
314 emit "mov #{@BX}, #{base_ref}\n"
315 emit "mov #{@CX}, #{offset_ref}\n"
316 "[#{@BX} + #{@CX} * #{scale}]"
320 # Load the value at the given address.
321 # Invoking this code may clobber @BX.
322 def load_at address, reg
323 if integer?(address) || global?(address)
326 load_value_into_register address, @BX
331 # Loads the value associated with the given symbol.
332 def load_symbol symbol, reg
333 x = @environment[symbol]
336 elsif x.kind_of? Integer
345 # Returns a string that can be used to refer to the loaded value.
346 def load_value value, reg
347 if substitution? value
348 value = substitute_number value[1]
352 if @WORDSIZE > 4 && (value < -2147483648 || value > 2147483647)
353 # AMD64 can load immediate values that are outside the range
354 # that can be represented as a 32-bit signed integer, but
355 # only with a mov instruction that loads the value into a
357 emit "mov #{@WORD_NAME} #{reg}, #{value}\n"
363 load_symbol value, reg
365 load_at value[1], reg
367 raise "Don't know how to load #{value.inspect}"
371 # Loads a value into a register.
372 def load_value_into_register value, register
373 value_ref = load_value value, register
374 set_register register, value_ref
381 # Stores the value of a register in memory.
382 def emit_store_word register, base, offset = 0
384 emit "mov [#{base}], #{register}\n"
386 emit "mov [#{base} + #{offset} * #{@WORDSIZE}], #{register}\n"
390 # Evaluate the expr in words and store the result in target
391 def set target, *words
393 raise "Cannot change value of integer #{target}"
394 elsif global?(target)
395 raise "Cannot change value of global #{target}"
398 if words.length != 1 || words[0] != target
399 if symbol?(target) && symbol?(@environment[target])
400 eval_expr words, @environment[target]
402 eval_expr words, @RETURN_REG
403 target_ref = load_value target, @BX
404 emit "mov #{target_ref}, #{@RETURN_REG}\n"
409 # Set the byte at _base_ + _offset_ to _value_
410 def set_byte base, offset, value
411 if immediate_operand?(value)
414 load_value_into_register value, @RETURN_REG
417 addr_ref = load_address base, offset, 1
418 emit "mov byte #{addr_ref}, #{value_ref}\n"
421 # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
422 def set_word base, offset, value
423 if immediate_operand?(value)
424 value_ref = load_value value, @RETURN_REG
426 load_value_into_register value, @RETURN_REG
427 value_ref = @RETURN_REG
429 addr_ref = load_address base, offset, @WORDSIZE
430 emit "mov #{@WORD_NAME} #{addr_ref}, #{value_ref}\n"
433 # Divide x by y and store the quotient in target
436 target_ref = load_value target, @BX
437 emit "mov #{target_ref}, #{@AX}\n"
440 # Divide target by x and store the quotient in target
442 div target, target, x
445 # Divide x by y and store the remainder in target
448 target_ref = load_value target, @BX
449 emit "mov #{target_ref}, #{@DX}\n"
452 # Divide target by x and store the remainder in target
454 mod target, target, x
457 # Multiply x by y and store the result in target
460 target_ref = load_value target, @BX
461 emit "mov #{target_ref}, #{@RETURN_REG}\n"
464 # Multiply target by x and store the result in target
466 mul target, target, x
474 # The quotient is stored in @AX, the remainder in @DX.
476 with_temporary do |temporary|
477 x_ref = load_value_into_register x, @AX
478 y_ref = load_value y, temporary
479 emit "mov #{@DX}, #{@AX}\n"
480 emit "sar #{@DX}, #{@WORDSIZE * 8 - 1}\n"
481 if immediate_operand?(y_ref)
482 set_register @BX, y_ref
485 emit "idiv #{@WORD_NAME} #{y_ref}\n"
490 # Evaluate an expression.
491 # The result is stored in _register_ (@RETURN_REG by default).
492 # The following registers may be clobbered: @AX, @BX, @CX, @DX
493 def eval_expr words, register = @RETURN_REG
496 emit "xor #{register}, #{register}\n"
498 load_value_into_register words[0], register
503 when :asr, :bsr, :rol, :ror, :shl, :shr
507 load_value_into_register words[2], @CX
510 load_value_into_register words[1], register
511 emit "#{action_to_mnemonic op} #{register}, #{y_ref}\n"
513 auto_bytes words[1], register
515 auto_words words[1], register
518 emit "mov #{register}, #{@RETURN_REG}\n" if register != @RETURN_REG
520 eval_div words[1], words[2]
521 set_register register, @AX
523 # Get address reference
524 address_ref = load_address words[1], words[2], 1
525 # Load byte from address
528 set_register register, 0
529 set_register :al, address_ref
531 set_register register, 0
532 set_register :bl, address_ref
534 set_register register, 0
535 set_register :cl, address_ref
537 set_register register, 0
538 set_register :dl, address_ref
541 set_register :al, address_ref
542 set_register register, @AX
545 address_ref = load_address words[1], words[2], @WORDSIZE
546 set_register register, address_ref
548 eval_div words[1], words[2]
549 set_register register, @DX
551 eval_mul words[1], words[2], register
553 load_value_into_register words[1], register
554 emit "not #{register}\n"
557 x_ref = load_value words[1], @DX
558 y_ref = load_value words[2], @BX
559 emit "mov #{register}, #{x_ref}\n" unless register == x_ref
560 emit "#{op} #{register}, #{y_ref}\n"
562 raise "Not a magic word: #{words[0]}"
569 # The result is stored in @AX by default, but
570 # a different register can be specified by passing
572 def eval_mul x, y, register = @AX
573 x_ref = load_value x, @DX
574 y_ref = load_value y, @BX
576 if immediate_operand? x_ref
577 if immediate_operand? y_ref
578 set_register register, x_ref * y_ref
580 emit "imul #{register}, #{y_ref}, #{x_ref}\n"
582 elsif immediate_operand? y_ref
583 emit "imul #{register}, #{x_ref}, #{y_ref}\n"
584 elsif y_ref != register
585 emit "mov #{register}, #{x_ref}\n" unless x_ref == register
586 emit "imul #{register}, #{y_ref}\n"
588 emit "imul #{register}, #{x_ref}\n"
596 # Start a conditional using the specified branch instruction
597 # after the comparison.
598 def common_if branch, x, y = nil
599 # Inverses of branches. E.g.
614 y_ref = load_value y, @DX
615 x_ref = load_value x, @AX
616 if immediate_operand?(x_ref)
617 # Can't have an immediate value as the first operand.
618 if immediate_operand?(y_ref)
619 # Both are immediates. Put the first in a register.
620 emit "mov #{@AX}, #{x_ref}\n"
623 # y isn't immediate; swap x and y.
624 x_ref, y_ref = [y_ref, x_ref]
625 branch = inverse_branch[branch]
627 elsif memory_operand?(x_ref) && memory_operand?(y_ref)
628 # Can't have two memory operands. Move the first into a register.
629 emit "mov #{@AX}, #{x_ref}\n"
632 truelabel = @environment.gensym
633 falselabel = @environment.gensym
634 @if_labels.push falselabel
636 emit "cmp #{@WORD_NAME} #{x_ref}, #{y_ref}\n"
637 emit "#{branch} #{truelabel}\n"
638 emit "jmp #{falselabel}\n"
639 emit "#{truelabel}:\n"
644 label = @if_labels.pop
648 # Start the false path of a conditional.
650 newlabel = @environment.gensym
651 emit "jmp #{newlabel}\n"
652 label = @if_labels.pop
654 @if_labels.push newlabel
657 # Test if x is equal to y
662 # Test if x is greater than or equal to y
667 # Test if x is strictly greater than y
672 # Test if x is less than or equal to y
677 # Test if x is strictly less than y
682 # Test if x different from y
691 # Translates a Voodoo action name to an x86 mnemonic
692 def action_to_mnemonic action
708 # Returns a memory reference for the address at the given offset
709 # from the frame pointer.
710 def offset_reference offset
712 "[#{@BP} + #{offset}]"
714 "[#{@BP} - #{-offset}]"
720 # Set a register to a value.
721 # The value must be a valid operand to the mov instruction.
722 def set_register register, value_ref
727 emit "xor #{register}, #{register}\n"
729 emit "mov #{register}, #{value_ref}\n"
737 # Write generated code to the given IO object.
739 io.puts "bits #{@WORDSIZE * 8}\n\n"
740 @sections.each do |section,code|
742 io.puts "section #{section.to_s}"