Voodoo::MIPSGasGenerator

MIPS GNU Assembler Code Generator

The MIPS code generator generates assembly code for use with the GNU assembler.

Calling Convention

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

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:

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.

Public Class Methods

new(params) click to toggle source
# 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

Public Instance Methods

addiu(target, source, immediate, temporary = @TEMPORARY) click to toggle source

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
arg_offset(n) click to toggle source

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
arg_reference(n) click to toggle source

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
arg_register(n) click to toggle source

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
auto_bytes(n, register) click to toggle source
# 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
auto_words(n, register) click to toggle source
# 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
begin_block(*code) click to toggle source

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
begin_function(formals, nlocals) click to toggle source

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
byte(value) click to toggle source

Defines a byte with the given value.

# File voodoo/generators/mips_gas_generator.rb, line 254
def byte value
  emit ".byte #{value}\n"
end
call(func, *args) click to toggle source

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
comment(text) click to toggle source

Emits a comment.

# File voodoo/generators/mips_gas_generator.rb, line 310
def comment text
  emit "# #{text}\n"
end
common_if(comp, x, y = nil) click to toggle source

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
create_frame(nlocals) click to toggle source

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
emit_align(alignment) click to toggle source
# File voodoo/generators/mips_gas_generator.rb, line 359
def emit_align alignment
  emit ".align #{alignment}\n"
end
emit_export(*symbols) click to toggle source

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
emit_function_prologue(formals = [], nlocals = 0) click to toggle source

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
emit_label_size(name) click to toggle source

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
emit_label_type(name, type) click to toggle source

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
emit_load_word(register, base, offset) click to toggle source

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
emit_store_word(register, base, offset) click to toggle source

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
end_block() click to toggle source

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
end_function() click to toggle source

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
end_if() click to toggle source

Ends a conditional.

# File voodoo/generators/mips_gas_generator.rb, line 460
def end_if
  label = @if_labels.pop
  emit "#{label}:\n"
end
eval_binop(expr, register) click to toggle source

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
eval_expr(expr, register) click to toggle source

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
get_byte(base, offset, register) click to toggle source

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
get_word(base, offset, register) click to toggle source

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
goto(address) click to toggle source

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
grow_stack(n) click to toggle source

Grows the stack by n bytes.

# File voodoo/generators/mips_gas_generator.rb, line 585
def grow_stack n
  addiu "$sp", "$sp", -n
end
ifelse() click to toggle source

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
ifeq(x, y) click to toggle source

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
ifge(x, y) click to toggle source

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
ifgt(x, y) click to toggle source

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
ifle(x, y) click to toggle source

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
iflt(x, y) click to toggle source

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
ifne(x, y) click to toggle source

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
let(symbol, *expr) click to toggle source

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
load_at(address, register = @TEMPORARY) click to toggle source

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
load_value(x, register = @TEMPORARY) click to toggle source

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
load_value_into_register(x, register) click to toggle source

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
local_offset(n) click to toggle source

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
local_reference(n) click to toggle source

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
local_register(n) click to toggle source

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
max_locals() click to toggle source

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
offset_reference(offset) click to toggle source

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
register_local?(n) click to toggle source

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
ret(*words) click to toggle source

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
save_frame(location) click to toggle source

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
set(symbol, *expr) click to toggle source

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
set_byte(base, offset, value) click to toggle source

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
set_word(base, offset, value) click to toggle source

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
string(value) click to toggle source

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
tail_call(func, *args) click to toggle source

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
word(value) click to toggle source

Defines a word with the given value

# File voodoo/generators/mips_gas_generator.rb, line 951
def word value
  emit ".int #{value}\n"
end
write(io) click to toggle source

Write generated code to the given IO object.

# File voodoo/generators/mips_gas_generator.rb, line 956
def write io
  io.puts ".set noat"
  @sections.each do |section,code|
    unless code.empty?
      io.puts ".section #{section.to_s}"
      io.puts code
      io.puts
    end
  end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.