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

Callee-Save Registers

ebp, ebx, edi, esi, and esp are callee-save registers.

All other registers are caller-save registers.

Direct Known Subclasses

I386ELFGenerator

Constant Summary collapse

WORDSIZE =
4

Instance Method Summary collapse

Methods inherited from NasmGenerator

#action_to_mnemonic, #auto_bytes, #auto_bytes_immediate, #auto_bytes_register, #auto_words, #begin_block, #byte, #comment, #common_if, #div, #div2, #dword, #emit_align, #emit_function_epilogue, #emit_label, #emit_label_size, #emit_label_type, #emit_load_word, #emit_store_word, #end_block, #end_function, #end_if, #eval_div, #eval_expr, #eval_mul, #export, #goto, #ifelse, #ifeq, #ifge, #ifgt, #ifle, #iflt, #ifne, #immediate_operand?, #import, #let, #load_address, #load_at, #load_symbol, #load_value, #load_value_into_register, #memory_operand?, #mod, #mod2, #mul, #mul2, #offset_reference, #qword, #ret, #set, #set_byte, #set_register, #set_word, #string, #write

Methods inherited from CommonCodeGenerator

#add, #add_function, #align, #assymetric_binop?, #at_expr?, #binop?, #block, #count_locals, #default_alignment, #each_statement, #emit, #emit_label, #emit_voodoo, #export, #features, #function, #gensym, #global?, #has_feature?, #import, #in_section, #integer?, #label, #local_register, #number_of_register_arguments, #number_of_stack_arguments, #output_file_name, #output_file_suffix, #real_section_name, #register?, #register_arg?, #registers_for_locals, #restore_frame, #restore_locals, #restore_registers_from_frame, #save_frame, #save_frame_and_locals, #save_locals, #save_registers_to_frame, #saved_frame_size, #section, #section=, #section_alias, #stack_align, #substitute_number, #substitution?, #symbol?, #symmetric_binop?, #with_temporaries, #with_temporary, #write

Constructor Details

#initialize(params = {}) ⇒ I386NasmGenerator

Returns a new instance of I386NasmGenerator.



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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 43

def initialize params = {}
  # Number of bytes in a word
  @WORDSIZE_BITS = 2
  @WORDSIZE = 1 << @WORDSIZE_BITS
  # 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
  # Stack alingment
  @STACK_ALIGNMENT_BITS = @WORDSIZE_BITS
  @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
  # Register used for return values
  @RETURN_REG = :eax
  # Accumulator index
  @AX = :eax
  # Base index
  @BX = :ebx
  # Count index
  @CX = :ecx
  # Data index
  @DX = :edx
  # Base pointer
  @BP = :ebp
  # Stack pointer
  @SP = :esp
  # Registers used to store locals
  @LOCAL_REGISTERS = []
  @NLOCAL_REGISTERS = @LOCAL_REGISTERS.length
  @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
  @SAVE_FRAME_REGISTERS = [:ebx, :edi, :esi, :esp, :ebp]
  @SAVED_FRAME_LAYOUT = {}
  @SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i }
  @TEMPORARIES = [:ebx]
  super params
  @saved_registers = []
  in_section(:data) { emit "extern _GLOBAL_OFFSET_TABLE_\n" }
  @features.merge! \
    :'bits-per-word' => '32',
    :'byte-order' => 'little-endian',
    :'bytes-per-word' => '4'
end

Instance Method Details

#arg_offset(n) ⇒ Object

Returns the offset of the nth argument.



90
91
92
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 90

def arg_offset n
  8 + (n * @WORDSIZE)
end

#begin_function(formals, nlocals) ⇒ Object

Emits function preamble and declare formals as function arguments.



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

def begin_function formals, nlocals
  environment = Environment.new @environment
  @environment = environment
  emit "push #{@BP}\nmov #{@BP}, #{@SP}\n"
  formals.each_with_index do |arg,i|
    environment.add_arg arg, arg_offset(i)
  end
  emit "sub #{@SP}, #{nlocals * @WORDSIZE}\n"      
end

#call(func, *args) ⇒ Object

Calls a function.



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

def call func, *args
  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

#load_got_address(reg) ⇒ Object

Loads the address of the global offset table into the given register.



116
117
118
119
120
121
122
123
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 116

def load_got_address reg
  lbl = gensym
  emit "call #{lbl}\n"
  emit "#{lbl}:\n"
  emit "pop #{reg}\n"
  emit "add #{reg}, _GLOBAL_OFFSET_TABLE_ + $$ - #{lbl} wrt ..gotpc\n"
  reg
end

#load_symbol_from_got(symbol, reg) ⇒ Object

Loads a symbol from the global offset table.



126
127
128
129
130
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 126

def load_symbol_from_got symbol, reg
  load_got_address reg
  emit "mov #{reg}, [#{reg} + #{symbol} wrt ..got]\n"
  reg
end

#local_offset_or_register(n) ⇒ Object

If the nth local is stored in a register, returns that register. Otherwise, returns the offset from the frame pointer.



134
135
136
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 134

def local_offset_or_register n
  (n + 1) * -@WORDSIZE
end

#push(value) ⇒ Object

Push a word on the stack



139
140
141
142
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 139

def push value
  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



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 145

def tail_call fun, *args
  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
    # If any arguments are going to be overwritten before they are
    # used, save them to new local variables and use those instead.
    (args.length - 1).downto(0) do |i|
      arg = args[i]
      next unless symbol?(arg)
      old_arg_offset = @environment[arg]
      next if old_arg_offset == nil || old_arg_offset < 0
      # arg is an argument that was passed on the stack.
      new_arg_offset = arg_offset i
      next unless old_arg_offset > new_arg_offset
      # arg will be overwritten before it is used.
      # Save it in a newly created temporary variable,
      # then use that instead.
      newsym = @environment.gensym
      let newsym, arg
      args[i] = newsym
    end

    # Same for the function we will be calling.
    if symbol?(fun)
      offset = @environment[fun]
      if offset != nil && offset > 0
        newsym = @environment.gensym
        let newsym, fun
        func = newsym
      end
    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 + #{arg_offset i}]"
        # 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} #{newarg_ref}, #{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



203
204
205
206
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 203

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



209
210
211
# File 'lib/voodoo/generators/i386_nasm_generator.rb', line 209

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