Class: Voodoo::I386NasmGenerator
- Inherits:
-
NasmGenerator
- Object
- CommonCodeGenerator
- NasmGenerator
- Voodoo::I386NasmGenerator
- 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
Constant Summary collapse
- WORDSIZE =
4
Instance Method Summary collapse
-
#arg_offset(n) ⇒ Object
Returns the offset of the nth argument.
-
#begin_function(formals, nlocals) ⇒ Object
Emits function preamble and declare
formals
as function arguments. -
#call(func, *args) ⇒ Object
Calls a function.
-
#initialize(params = {}) ⇒ I386NasmGenerator
constructor
A new instance of I386NasmGenerator.
-
#load_got_address(reg) ⇒ Object
Loads the address of the global offset table into the given register.
-
#load_symbol_from_got(symbol, reg) ⇒ Object
Loads a symbol from the global offset table.
-
#local_offset_or_register(n) ⇒ Object
If the nth local is stored in a register, returns that register.
-
#push(value) ⇒ Object
Push a word on the stack.
-
#tail_call(fun, *args) ⇒ Object
Call a function, re-using the current call frame if possible.
- #use_value(operation, value) ⇒ Object
-
#word(value) ⇒ Object
Define a machine word with the given value.
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_export, #emit_function_epilogue, #emit_import, #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, #goto, #ifelse, #ifeq, #ifge, #ifgt, #ifle, #iflt, #ifne, #immediate_operand?, #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_import, #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?, #undefined_symbols, #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 |