Class: Voodoo::I386NasmGenerator

Inherits:
NasmGenerator show all
Defined in:
lib/voodoo/generators/i386_nasm_generator.rb

Overview

i386 NASM Code Generator

The i386 NASM code generator generates i386 assembly code for use with the Netwide Assembler.

Calling Convention

Function arguments are pushed on the stack in reverse order, so that the first argument is pushed last. Each argument occupies one word of stack space. These arguments are removed from the stack by the caller after the called function returns.

The return value is passed in eax.

Call Frames

Call frames have the following layout:

argn
:
arg1
arg0   <-- ebp + 8
oldeip <-- ebp + 4
oldebp <-- ebp
local0 <-- ebp - 4
local1 <-- ebp - 8
:
localn <-- esp

Direct Known Subclasses

I386ELFGenerator

Constant Summary collapse

WORDSIZE =
4

Instance Method Summary collapse

Methods inherited from NasmGenerator

#action_to_mnemonic, #align, #at_expr?, #begin_block, #begin_function, #binop, #binop2, #binop?, #byte, #comment, #common_if, #div, #div2, #dword, #emit_function_epilogue, #end_block, #end_function, #end_if, #eval_div, #eval_expr, #eval_mul, #export, #global?, #goto, #ifelse, #ifeq, #ifge, #ifgt, #ifle, #iflt, #ifne, #immediate_operand?, #import, #integer?, #label, #load_address, #load_at, #load_symbol, #load_value, #load_value_into_register, #memory_operand?, #mod, #mod2, #mul, #mul2, #qword, #ret, #set, #set_byte, #set_register, #set_word, #string, #symbol?, #symmetric_operation?, #write

Methods inherited from CommonCodeGenerator

#add, #add_function, #block, #each_statement, #emit, #features, #function, #gensym, #has_feature?, #in_section, #output_file_name, #output_file_suffix, #real_section_name, #section, #section=, #section_alias

Constructor Details

#initialize(params = {}) ⇒ I386NasmGenerator

Returns a new instance of I386NasmGenerator.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 37

def initialize params = {}
  # Number of bytes in a word
  @WORDSIZE = 4
  # Word name in NASM lingo
  @WORD_NAME = 'dword'
  # Default alignment for code
  @CODE_ALIGNMENT = 0
  # Default alignment for data
  @DATA_ALIGNMENT = @WORDSIZE
  # Default alignment for functions
  @FUNCTION_ALIGNMENT = 16
  # Register used for return values
  @RETURN_REG = 'eax'
  # Register used as scratch register
  @SCRATCH_REG = 'ebx'
  # Accumulator index
  @AX = 'eax'
  # Base index
  @BX = 'ebx'
  # Count index
  @CX = 'ecx'
  # Data index
  @DX = 'edx'
  # Base pointer
  @BP = 'ebp'
  # Stack pointer
  @SP = 'esp'
  super params
  @features.merge! \
    :'bits-per-word' => '32',
    :'byte-order' => 'little-endian',
    :'bytes-per-word' => '4'
end

Instance Method Details

#call(func, *args) ⇒ Object

Call a function



72
73
74
75
76
77
78
79
80
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 72

def call func, *args
  emit "; call #{func} #{args.join ' '}\n"
  revargs = args.reverse
  revargs.each { |arg| push arg }
  use_value "call", func
  if args.length > 0
    emit "add esp, #{WORDSIZE * args.length}\n"
  end
end

#emit_function_prologue(formals = []) ⇒ Object

Emit function prologue.



83
84
85
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 83

def emit_function_prologue formals = []
  emit "push ebp\nmov ebp, esp\n"
end

#let(symbol, *words) ⇒ Object

Introduce a new local variable



98
99
100
101
102
103
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 98

def let symbol, *words
  emit "; let #{symbol} #{words.join ' '}\n"
  @environment.add_local symbol
  eval_expr words
  emit "push eax\n"
end

#load_arg(n, reg = @SCRATCH_REG) ⇒ Object

Load the value of the nth argument



88
89
90
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 88

def load_arg n, reg = @SCRATCH_REG
  "[ebp + #{n * @WORDSIZE + 8}]"
end

#load_local(n, reg = @SCRATCH_REG) ⇒ Object

Load the value of the nth local variable



93
94
95
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 93

def load_local n, reg = @SCRATCH_REG
  "[ebp - #{(n + 1) * @WORDSIZE}]"
end

#push(value) ⇒ Object

Push a word on the stack



106
107
108
109
110
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 106

def push value
  #emit "; push #{value}\n"
  value_ref = load_value value, "ebx"
  emit "push dword #{value_ref}\n"
end

#tail_call(fun, *args) ⇒ Object

Call a function, re-using the current call frame if possible



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 113

def tail_call fun, *args
  emit "; tail-call #{fun} #{args.join ' '}\n"
  if args.length > @environment.args
    # Not enough space to do proper tail call; do normal call instead
    emit "; not enough space for proper tail call; changed to regular call\n"
    ret :call, fun, *args
  else
    # Any value in the current frame that is passed to the called
    # function must be copied to a local variable if it would otherwise
    # be overwritten before it is used
    i = args.length - 1
    while i >= -1
      arg = (i >= 0) ? args[i] : fun

      if symbol?(arg)
        x = @environment[arg]
        if x && x[0] == :arg && x[1] < args.length && x[1] > i &&
            (i >= 0 || fun != args[x[1]])
          # Save value
          newsym = @environment.gensym
          let newsym, arg
          # Change reference
          if i >= 0
            args[i] = newsym
          else
            fun = newsym
          end
        end
      end
      i = i - 1
    end

    # Set arguments
    if args.length > 0
      (args.length - 1).downto(0).each do |i|
        arg = args[i]
        
        value_ref = load_value arg, "eax"
        newarg_ref = "[ebp + #{(i + 2) * WORDSIZE}]"
        # Elide code if source is same as destination
        unless value_ref == newarg_ref
          if memory_operand?(value_ref)
            emit "mov eax, #{value_ref}\n"
            value_ref = "eax"
          end
          emit "mov #{@WORD_NAME} [ebp + #{(i + 2) * WORDSIZE}], " +
                "#{value_ref}\n"
        end
      end
    end

    # Tail call
    emit "mov esp, ebp\npop ebp\n"
    use_value "jmp", fun
  end
end

#use_value(operation, value) ⇒ Object



170
171
172
173
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 170

def use_value operation, value
  value_ref = load_value value, "eax"
  emit "#{operation} #{value_ref}\n"
end

#word(value) ⇒ Object

Define a machine word with the given value



176
177
178
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 176

def word value
  emit "dd #{value}\n"
end