The MIPS code generator generates assembly code for use with the GNU assembler.
The first four arguments are passed in the registers $4 through $7. Any additional arguments are passed on the stack, starting at $sp + 16. Words $sp through $sp + 12 will be available for the called function to use. $sp will always be a multiple of 8.
The return address for the called function is passed in $31.
When performing a position-independent call, the address of the called function is passed in $25.
The called function will store its return value in $2.
The called function is required to preserve the values of registers $16 through $23 and register $30.
This calling convention is compatible with the System V ELF ABI.
Call frames have the following layout:
When a function is called, it receives a stack frame that looks like the following:
: old frame argn : arg4 empty3 empty2 empty1 empty0 <-- $sp points here
The function prologue of functions generated by this code generator creates activation frames that look as follows:
: old frame argn : arg4 arg3 arg2 arg1 arg0 <– $fp points here return address pointer to Global Offset Table saved $fp local0 : local7 local8 : localn padding <– $sp points here
In words:
The four empty slots provided by the caller are used to store arguments 0 through 3 (originally passed in $4 through $7), if necessary.
The stack frame created below the four slots provided by the caller contains the following data, in order:
Saved return address (originally passed in $31), if necessary.
Saved pointer to global offset table (computed from $25), if necessary.
Saved value of $fp, if necessary.
Saved values of caller’s locals 0 through 7 (originally in $16 through $23), if necessary.
Values of our locals > 8, if necessary.
Padding to align $sp on a multiple of 8, if necessary.
In accordance with the System V ELF ABI for MIPS, the pointer to the global offset table is calculated as follows:
$gp = _gp_disp + $25
where $gp is the register to store the pointer, $25 is the register holding the address of the called function, and _gp_disp is the offset between the beginning of the function and the global offset table.
Callee-Save Registers
Registers $16 through $23 and $28 through $30 are callee-save.
All other registers are caller-save.
# File voodoo/generators/mips_gas_generator.rb, line 108 def initialize params @WORDSIZE_BITS = 2 @WORDSIZE = 1 << @WORDSIZE_BITS @CODE_ALIGNMENT = 0 @DATA_ALIGNMENT = @WORDSIZE @STACK_ALIGNMENT_BITS = 3 @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS @FP_OFFSET = -3 * @WORDSIZE @FUNCTION_ALIGNMENT = @WORDSIZE @GP_OFFSET = -2 * @WORDSIZE @INITIAL_FRAME_SIZE = 4 * @WORDSIZE @REGISTER_ARG_BASE = 4 @NREGISTER_ARGS = 4 @LOCAL_REGISTERS = [:'$16', :'$17', :'$18', :'$19', :'$20', :'$21', :'$22', :'$23'] @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS @REGISTER_LOCAL_BASE = 16 @NREGISTER_LOCALS = @LOCAL_REGISTERS.length @RA_OFFSET = -@WORDSIZE @RETURN = :'$2' @TEMPORARY = :'$1' @FUNCTION = :'$25' @GOT = :'$28' @SAVE_FRAME_REGISTERS = [:'$16', :'$17', :'$18', :'$19', :'$20', :'$21', :'$22', :'$23', :'$28', :'$29', :'$30'] @SAVED_FRAME_LAYOUT = {} @SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i } @TEMPORARIES = [:'$8', :'$9', :'$10', :'$11', :'$12', :'$13', :'$14', :'$15'] @function_end_label = nil @if_labels = [] super params @output_file_suffix = '.s' @saved_registers = [] @features.merge! :'bits-per-word' => '32', :'bytes-per-word' => '4' case @architecture when :mips @features[:'byte-order'] = 'big-endian' when :mipsel @features[:'byte-order'] = 'little-endian' else raise ArgumentError.new("#{self.class} does not support " + "architecture #{@architecture}") end end
Adds immediate to source and stores the result in target.
# File voodoo/generators/mips_gas_generator.rb, line 158 def addiu target, source, immediate, temporary = @TEMPORARY if immediate >= -32768 && immediate < 32767 emit "addiu #{target}, #{source}, #{immediate}\n" elsif source == "$0" emit "lui #{target}, #{(immediate >> 16) & 0xffff}\n" emit "ori #{target}, #{target}, #{immediate & 0xffff}\n" else addiu temporary, "$0", immediate emit "addu #{target}, #{source}, #{temporary}\n" end end
Return the offset from the frame base at which the nth argument is stored.
# File voodoo/generators/mips_gas_generator.rb, line 172 def arg_offset n n * @WORDSIZE end
Returns an $fp-relative reference for the nth (0-based) argument.
# File voodoo/generators/mips_gas_generator.rb, line 177 def arg_reference n offset_reference(arg_offset(n)) end
Returns the register in which the nth (0-based) argument is stored, or nil if not stored in a register.
# File voodoo/generators/mips_gas_generator.rb, line 183 def arg_register n if register_arg? n "$#{@REGISTER_ARG_BASE + n}" else nil end end
# File voodoo/generators/mips_gas_generator.rb, line 191 def auto_bytes n, register if n.kind_of? Integer # Maintain stack alignment n = (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT grow_stack n else temporary = @TEMPORARIES.pop begin load_value_into_register n, register emit "addi #{register}, #{@STACK_ALIGNMENT - 1}\n" emit "li #{temporary}, -#{@STACK_ALIGNMENT}\n" emit "and #{register}, #{register}, #{temporary}\n" emit "sub $sp, #{register}\n" ensure @TEMPORARIES.push temporary end end emit "move #{register}, $sp\n" unless register == "$sp" end
# File voodoo/generators/mips_gas_generator.rb, line 211 def auto_words n, register if n.kind_of? Integer auto_bytes n * @WORDSIZE, register else load_value_into_register n, register if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS bits = @STACK_ALIGNMENT_BITS - @WORDSIZE_BITS emit "addi #{register}, #{(1 << bits) - 1}\n" emit "srl #{register}, #{register}, #{bits}\n" emit "sll #{register}, #{register}, #{@STACK_ALIGNMENT_BITS}\n" else emit "sll #{register}, #{register}, #{@WORDSIZE_BITS}\n" end emit "sub $sp, $sp, #{register}\n" emit "move #{register}, $sp\n" unless register == "$sp" end end
Begins a new block.
# File voodoo/generators/mips_gas_generator.rb, line 230 def begin_block *code # If we are at top-level, create a frame if @environment == @top_level create_frame count_locals(code) end environment = Environment.new @environment @environment = environment end
Emits function prologue and declare formals as function arguments.
# File voodoo/generators/mips_gas_generator.rb, line 240 def begin_function formals, nlocals if @environment != @top_level raise "Cannot begin function when already in a function" end environment = Environment.new @environment @frame_size = 0 formals.each {|x| environment.add_arg x, arg_offset(environment.args)} @environment = environment @function_end_label = gensym emit_function_prologue formals, nlocals end
Defines a byte with the given value.
# File voodoo/generators/mips_gas_generator.rb, line 254 def byte value emit ".byte #{value}\n" end
Calls a function.
# File voodoo/generators/mips_gas_generator.rb, line 259 def call func, *args # Grow the stack frame, subject to 3 conditions: # 1. There must be enough space to hold all arguments # 2. $sp must remain a multiple of 8 # 3. We must provide space for at least 4 words increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8 grow_stack increment # Put arguments in the right places n = 0 while n < args.length if n < @NREGISTER_ARGS # Put arguments up to @NREGISTER_ARGS in registers load_value_into_register args[n], arg_register(n) else # Put other arguments on the stack load_value_into_register args[n], @TEMPORARY emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n" end n = n + 1 end # Load function address if global? func @symbol_tracker.use func if @relocated_symbols.include? func # Load address from global offset table emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n" else # Assume label defined in this module emit "lui #{@FUNCTION}, %hi(#{func})\n" emit "addiu #{@FUNCTION}, %lo(#{func})\n" end else # Load address load_value_into_register func, "#{@FUNCTION}" end # Perform call emit "jalr #{@FUNCTION}\n" # brach delay slot emit "nop\n" # Restore original stack frame grow_stack -increment # restore gp emit "lw #{@GOT}, #{@GP_OFFSET}($fp)\n" end
Starts a conditional using the specified branch instruction after the comparison.
# File voodoo/generators/mips_gas_generator.rb, line 316 def common_if comp, x, y = nil temporaries = @TEMPORARIES.dup xreg = load_value x, temporaries[0] temporaries.delete xreg yreg = load_value y, temporaries[0] temporaries.delete yreg falselabel = @environment.gensym @if_labels.push falselabel case comp when :ifeq emit "bne #{xreg}, #{yreg}, #{falselabel}\n" when :ifge emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n" emit "bne #{@TEMPORARY}, $0, #{falselabel}\n" when :ifgt emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n" emit "beq #{@TEMPORARY}, $0, #{falselabel}\n" when :ifle emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n" emit "bne #{@TEMPORARY}, $0, #{falselabel}\n" when :iflt emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n" emit "beq #{@TEMPORARY}, $0, #{falselabel}\n" when :ifne emit "beq #{xreg}, #{yreg}, #{falselabel}\n" else raise "Unknown conditional: #{comp}" end emit "nop\n" end
Creates an activation frame which can store nlocals local variables.
# File voodoo/generators/mips_gas_generator.rb, line 350 def create_frame nlocals @frame_size = @INITIAL_FRAME_SIZE if nlocals > 0 @frame_size = @frame_size + (nlocals * @WORDSIZE + 7) / 8 * 8 end grow_stack @frame_size @saved_registers = [] end
# File voodoo/generators/mips_gas_generator.rb, line 359 def emit_align alignment emit ".align #{alignment}\n" end
Exports symbols from the current section.
# File voodoo/generators/mips_gas_generator.rb, line 364 def emit_export *symbols symbols.each { |sym| emit ".globl #{sym}\n" } end
Emits function prologue.
# File voodoo/generators/mips_gas_generator.rb, line 369 def emit_function_prologue formals = [], nlocals = 0 # Calculate new value for $gp emit "lui #{@GOT}, %hi(_gp_disp)\n" emit "addiu #{@GOT}, %lo(_gp_disp)\n" emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n" create_frame nlocals # Save return address emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n" # Save gp emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n" # Save fp emit "sw $fp, #{@frame_size - 3 * @WORDSIZE}($sp)\n" # Point fp at where sp was before we created the frame addiu "$fp", "$sp", @frame_size # Save parameters 0 .. 3 in the stack space that the caller # should have allocated for them. [@NREGISTER_ARGS, formals.length].min.times do |n| ref = offset_reference(@environment[formals[n]]) emit "sw $#{n + @REGISTER_ARG_BASE}, #{ref}\n" end end
Emits a label size annotation.
# File voodoo/generators/mips_gas_generator.rb, line 407 def emit_label_size name emit ".size #{name}, .-#{name}\n" end
Emits a label type annotation.
# File voodoo/generators/mips_gas_generator.rb, line 398 def emit_label_type name, type type_map = { :code => "@function", :data => "@object", } emit ".type #{name}, #{type_map[type]}\n" end
Loads a word into a register.
# File voodoo/generators/mips_gas_generator.rb, line 412 def emit_load_word register, base, offset emit "lw #{register}, #{offset * @WORDSIZE}(#{base})\n" end
Stores the value of a register in memory.
# File voodoo/generators/mips_gas_generator.rb, line 417 def emit_store_word register, base, offset emit "sw #{register}, #{offset * @WORDSIZE}(#{base})\n" end
Ends the current block.
# File voodoo/generators/mips_gas_generator.rb, line 448 def end_block # If we are returning to top level, restore stack pointer if @environment.parent == @top_level grow_stack -@frame_size @saved_registers = [] end # Restore old value of @environment @environment = @environment.parent end
Ends a function body.
# File voodoo/generators/mips_gas_generator.rb, line 422 def end_function if @environment == @top_level raise "Cannot end function when not in a function" end label @function_end_label # Restore saved locals [@NREGISTER_LOCALS, @environment.locals].min.times do |n| emit "lw #{local_register n}, #{local_reference n}\n" end @saved_registers = [] # Load return address emit "lw $31, #{@RA_OFFSET}($fp)\n" # Restore stack pointer emit "move $sp, $fp\n" # Restore frame pointer emit "lw $fp, #{@FP_OFFSET}($fp)\n" # Return emit "jr $31\n" emit "nop\n" @function_end_label = nil @environment = @top_level end
Ends a conditional.
# File voodoo/generators/mips_gas_generator.rb, line 460 def end_if label = @if_labels.pop emit "#{label}:\n" end
Evaluates the binary operation expr and store the result in register.
# File voodoo/generators/mips_gas_generator.rb, line 466 def eval_binop expr, register temporaries = @TEMPORARIES.dup x = load_value expr[1], temporaries[0] temporaries.delete x y = load_value expr[2], temporaries[0] temporaries.delete y case expr[0] when :asr emit "srav #{register}, #{x}, #{y}\n" when :bsr emit "srlv #{register}, #{x}, #{y}\n" when :div emit "div $0, #{x}, #{y}\n" emit "mflo #{register}\n" when :mod emit "div $0, #{x}, #{y}\n" emit "mfhi #{register}\n" when :mul emit "mult #{x}, #{y}\n" emit "mflo #{register}\n" when :rol emit "subu #{@TEMPORARY}, $0, #{y}\n" emit "srlv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n" emit "sllv #{register}, #{x}, #{y}\n" emit "or #{register}, #{register}, #{@TEMPORARY}\n" when :ror emit "subu #{@TEMPORARY}, $0, #{y}\n" emit "sllv #{@TEMPORARY}, #{x}, #{@TEMPORARY}\n" emit "srlv #{register}, #{x}, #{y}\n" emit "or #{register}, #{register}, #{@TEMPORARY}\n" when :shl emit "sllv #{register}, #{x}, #{y}\n" when :shr emit "srlv #{register}, #{x}, #{y}\n" else emit "#{expr[0]} #{register}, #{x}, #{y}\n" end end
Evaluates the expression expr and store the result in register.
# File voodoo/generators/mips_gas_generator.rb, line 507 def eval_expr expr, register if expr.length == 1 # Load value load_value_into_register expr[0], register else # Evaluate expression op = expr[0] case op when :'auto-bytes' auto_bytes expr[1], register when :'auto-words' auto_words expr[1], register when :call call *expr[1..-1] emit "move #{register}, #{@RETURN}\n" if register != @RETURN when :'get-byte' get_byte expr[1], expr[2], register when :'get-word' get_word expr[1], expr[2], register when :not eval_binop [:nor, 0, expr[1]], register else if binop? op eval_binop expr, register else raise "Not a magic word: #{op}" end end end end
Loads byte from base + offset into register.
# File voodoo/generators/mips_gas_generator.rb, line 539 def get_byte base, offset, register # If base is an integer, but offset isn't, swap them if !integer?(offset) && integer?(base) base, offset = [offset, base] end if integer? offset base_reg = load_value base emit "lb #{register}, #{offset}(#{base_reg})\n" else eval_binop [:add, base, offset], @TEMPORARY emit "lb #{register}, 0(#{@TEMPORARY})\n" end end
Loads word from base + offset * _@WORDSIZE_ into register.
# File voodoo/generators/mips_gas_generator.rb, line 555 def get_word base, offset, register if integer? offset base_reg = load_value base emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n" else offset_reg = @TEMPORARIES.pop begin load_value_into_register offset, offset_reg base_reg = load_value base emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n" emit "lw #{register}, 0(#{@TEMPORARY})\n" ensure @TEMPORARIES.push offset_reg end end end
Jumps to an address.
# File voodoo/generators/mips_gas_generator.rb, line 574 def goto address if global? address emit "j #{address}\n" else register = load_value address, @TEMPORARY emit "jr #{register}\n" end emit "nop\n" end
Grows the stack by n bytes.
# File voodoo/generators/mips_gas_generator.rb, line 585 def grow_stack n addiu "$sp", "$sp", -n end
Starts the false path of a conditional.
# File voodoo/generators/mips_gas_generator.rb, line 590 def ifelse newlabel = @environment.gensym emit "j #{newlabel}\n" emit "nop\n" label = @if_labels.pop emit "#{label}:\n" @if_labels.push newlabel end
Tests if x is equal to y
# File voodoo/generators/mips_gas_generator.rb, line 600 def ifeq x, y common_if :ifeq, x, y end
Tests if x is greater than or equal to y
# File voodoo/generators/mips_gas_generator.rb, line 605 def ifge x, y common_if :ifge, x, y end
Tests if x is strictly greater than y
# File voodoo/generators/mips_gas_generator.rb, line 610 def ifgt x, y common_if :ifgt, x, y end
Tests if x is less than or equal to y
# File voodoo/generators/mips_gas_generator.rb, line 615 def ifle x, y common_if :ifle, x, y end
Tests if x is strictly less than y
# File voodoo/generators/mips_gas_generator.rb, line 620 def iflt x, y common_if :iflt, x, y end
Tests if x different from y
# File voodoo/generators/mips_gas_generator.rb, line 625 def ifne x, y common_if :ifne, x, y end
Introduces a new local variable.
# File voodoo/generators/mips_gas_generator.rb, line 630 def let symbol, *expr n = @environment.locals register = local_register n ref = local_reference n if register # We will use a register to store the value @environment.add_local symbol, register # Save current value of register emit "sw #{register}, #{ref}\n" @saved_registers << register # Set new value eval_expr expr, register else # We will use the stack to store the value @environment.add_local symbol, local_offset(n) eval_expr expr, @TEMPORARY emit "sw #{@TEMPORARY}, #{ref}\n" end end
Loads the value at the given address.
# File voodoo/generators/mips_gas_generator.rb, line 651 def load_at address, register = @TEMPORARY load_value_into_register address, register emit "lw #{register}, 0(#{register})\n" register end
Loads a value into a register. Returns the name of the register. If the value was already in a register, the name of that register is returned. Else, the value is loaded into a register and the name of that register is returned. The register to use in that case may be specified using the optional second argument.
# File voodoo/generators/mips_gas_generator.rb, line 664 def load_value x, register = @TEMPORARY if substitution? x x = substitute_number x[1] end if x == 0 return "$0" elsif integer? x addiu register, "$0", x return register elsif symbol? x binding = @environment[x] if register? binding # Value is already in a register. Return register name. return binding elsif binding.kind_of? Integer # Load value from memory. emit "lw #{register}, #{offset_reference binding}\n" return register else # Assume global @symbol_tracker.use x if @relocated_symbols.include? x emit "lw #{register}, %got(#{x})(#{@GOT})\n" else emit "lw #{register}, %got(#{x})(#{@GOT})\n" emit "addiu #{register}, %lo(#{x})\n" end return register end elsif at_expr? x load_at x[1], register else raise "Don't know how to load #{x.inspect}" end end
Loads a value into a specific register.
# File voodoo/generators/mips_gas_generator.rb, line 702 def load_value_into_register x, register reg = load_value x, register if reg != register emit "move #{register}, #{reg}\n" end end
Returns the offset from the frame base at which the nth local is stored. For register locals this is the offset at which the saved value (from the calling function) is stored.
# File voodoo/generators/mips_gas_generator.rb, line 712 def local_offset n -(n + 4) * @WORDSIZE end
Returns an $sp-relative reference for the nth (0-based) local.
# File voodoo/generators/mips_gas_generator.rb, line 717 def local_reference n offset_reference(local_offset(n)) end
Returns the register in which the nth local (0-based) is stored, or nil if not stored in a register.
# File voodoo/generators/mips_gas_generator.rb, line 723 def local_register n @LOCAL_REGISTERS[n] end
Computes the maximum number of locals that would fit in the current frame size.
# File voodoo/generators/mips_gas_generator.rb, line 729 def max_locals # + 1, because the initial frame size has room for 1 local (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE + 1 end
Given an offset relative to the base of the frame, returns an reference to that memory location.
# File voodoo/generators/mips_gas_generator.rb, line 736 def offset_reference offset "#{offset}($fp)" end
Returns true if the nth (0-based) local is stored in a register.
# File voodoo/generators/mips_gas_generator.rb, line 741 def register_local? n n < @NREGISTER_LOCALS end
Returns from a function.
words may contain an expression to be evaluated. The result of the evaluation is returned from the function.
# File voodoo/generators/mips_gas_generator.rb, line 749 def ret *words # Compute return value and store it in @RETURN eval_expr(words, @RETURN) unless words.empty? # Go to epilogue goto @function_end_label end
Saves the current frame at the given location.
# File voodoo/generators/mips_gas_generator.rb, line 757 def save_frame location load_value_into_register location, @TEMPORARY @SAVE_FRAME_REGISTERS.each_with_index do |register,i| emit "sw #{register}, #{i * @WORDSIZE}(#{@TEMPORARY})\n" end end
Sets a variable to the result of evaluating an expression.
# File voodoo/generators/mips_gas_generator.rb, line 765 def set symbol, *expr if at_expr? symbol eval_expr expr, @RETURN load_value_into_register symbol.to_s[1..-1].to_sym, @TEMPORARY emit "sw #{@RETURN}, 0(#{@TEMPORARY})\n" else x = @environment[symbol] if x == nil raise "Cannot change value of constant #{symbol}" elsif register? x eval_expr expr, x else # Should be an integer. eval_expr expr, @TEMPORARY emit "sw #{@TEMPORARY}, #{offset_reference x}\n" end end end
Sets the byte at base + offset to value.
# File voodoo/generators/mips_gas_generator.rb, line 785 def set_byte base, offset, value # If base is an integer, but offset isn't, swap them if !integer?(offset) && integer?(base) base, offset = [offset, base] end value_reg = @TEMPORARIES.pop load_value_into_register value, value_reg begin if integer? offset base_reg = load_value base emit "sb #{value_reg}, #{offset}(#{base_reg})\n" else eval_binop [:add, base, offset], @TEMPORARY emit "sb #{value_reg}, 0(#{@TEMPORARY})\n" end ensure @TEMPORARIES.push value_reg end end
Sets the word at base + offset * +@WORDSIZE+ to value.
# File voodoo/generators/mips_gas_generator.rb, line 807 def set_word base, offset, value value_reg = @TEMPORARIES.pop load_value_into_register value, value_reg begin if integer? offset base_reg = load_value base emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n" else offset_reg = @TEMPORARIES.pop begin load_value_into_register offset, offset_reg base_reg = load_value base emit "sll #{offset_reg}, #{offset_reg}, 2\n" emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n" emit "sw #{value_reg}, 0(#{@TEMPORARY})\n" ensure @TEMPORARIES.push offset_reg end end ensure @TEMPORARIES.push value_reg end end
Defines a string with the given value.
# File voodoo/generators/mips_gas_generator.rb, line 832 def string value code = '' value.each_byte do |b| if b == 92 code << "\\\\" elsif b >= 32 && b < 127 && b != 34 code << b.chr else code << sprintf("\\%03o", b) end end emit ".ascii \"#{code}\"\n" end
Calls a function, re-using the current call frame if possible.
# File voodoo/generators/mips_gas_generator.rb, line 847 def tail_call func, *args # Compute number of stack arguments nstackargs = number_of_stack_arguments args.length # If we need more stack arguments than we have now, # perform a normal call and return if nstackargs > number_of_stack_arguments(@environment.args) emit "# Not enough space for proper tail call; using regular call\n" ret :call, func, *args end # Back up any stack arguments we will need after we overwrite them temporaries = @TEMPORARIES.dup nlocals = @environment.locals (@NREGISTER_ARGS...args.length).each do |i| x = @environment[args[i]] if integer?(x) && x < arg_offset(i) # args[i] is in a location that will have been overwritten by the # time we get around to passing argument i to the called function. # Make a backup of the value before we overwrite it. if temporaries.empty? # Oh dear, we're out of temporaries. # Store the value in the current stack frame, extending it # as necessary. if nlocals >= max_locals grow_stack 8 end reg = load_value args[i] emit "sw #{reg}, #{local_reference nlocals}\n" args[i] = [:local, nlocals] nlocals = nlocals + 1 else # Load the argument into a temporary reg = temporaries.shift load_value_into_register args[i], reg # Update args[i] so we know how to get to it later args[i] = [:reg, reg] end end end # Set stack arguments (@NREGISTER_ARGS...args.length).each do |i| arg = args[i] reg = @TEMPORARY # Load value if arg.kind_of? Array # Special cases, created above case arg[0] when :reg reg = arg[1] when :local emit "lw #{reg}, #{local_reference arg[1]}\n" end else load_value_into_register arg, reg end # Store value in designated place emit "sw #{reg}, #{arg_reference i}\n" end # Set register arguments [@NREGISTER_ARGS,args.length].min.times do |i| reg = arg_register i load_value_into_register args[i], reg end # Load function address if global? func if @relocated_symbols.include? func # Load address from global offset table emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n" else # Assume label defined in this module emit "lui #{@FUNCTION}, %hi(#{func})\n" emit "addiu #{@FUNCTION}, %lo(#{func})\n" end else # Load address load_value_into_register func, "#{@FUNCTION}" end # Restore saved registers [@NREGISTER_LOCALS, @environment.locals].min.times do |n| emit "lw #{local_register n}, #{local_reference n}\n" end # Restore return address emit "lw $31, #{@RA_OFFSET}($fp)\n" # Restore stack pointer emit "move $sp, $fp\n" # Restore frame pointer emit "lw $fp, #{@FP_OFFSET}($fp)\n" # Perform call emit "jr #{@FUNCTION}\n" # brach delay slot emit "nop\n" end
Generated with the Darkfish Rdoc Generator 2.
Emits a comment.