The i386 NASM code generator generates i386 assembly code for use with the Netwide Assembler.
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 have the following layout:
argn : arg1 arg0 <-- ebp + 8 oldeip <-- ebp + 4 oldebp <-- ebp local0 <-- ebp - 4 local1 <-- ebp - 8 : localn <-- esp
ebp, ebx, edi, esi, and esp are callee-save registers.
All other registers are caller-save registers.
# File 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
Returns the offset of the nth argument.
# File voodoo/generators/i386_nasm_generator.rb, line 90 def arg_offset n 8 + (n * @WORDSIZE) end
Emits function preamble and declare formals as function arguments.
# File 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
Calls a function.
# File 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
Loads the address of the global offset table into the given register.
# File 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
Loads a symbol from the global offset table.
# File 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
If the nth local is stored in a register, returns that register. Otherwise, returns the offset from the frame pointer.
# File voodoo/generators/i386_nasm_generator.rb, line 134 def local_offset_or_register n (n + 1) * -@WORDSIZE end
Push a word on the stack
# File voodoo/generators/i386_nasm_generator.rb, line 139 def push value value_ref = load_value value, "ebx" emit "push dword #{value_ref}\n" end
Call a function, re-using the current call frame if possible
# File 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
Generated with the Darkfish Rdoc Generator 2.