Voodoo::ARMGasGenerator

ARM GNU Assembler Code Generator

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

Calling Convention

The first four arguments are passed in the registers r0 through r3. Any additional arguments are passed on the stack, starting at r13. r13 will always be a multiple of 8.

The return address for the called function is passed in r14.

The called function will store its return value in r0.

The called function is required to preserve the values of registers r4 through r11 and register r13.

This calling convention is compatible with the Procedure Call Standard for the ARM Architecture (AAPCS).

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
padding
argn
:
arg4        <-- r13 points here

The function prologue of functions generated by this code generator creates activation frames that look as follows:

:
old frame
padding
argn
:
arg4       <-- r11 points here
saved r14
saved r11
:
saved r4
local6
:
localn     <-- r13 points here

Register Usage

Inside a function, registers r4..r8 and r10 are used for local variables and function arguments, whereas r11 is used as a frame pointer.

r12 is used as a temporary, and r3, r2 and r1 are used when more temporaries are needed.

Local Variables

Local variables are used to store both the first 4 function arguments and any variables declared inside the function (arguments beyond the 4th are stored on the stack, as per the calling convention). Local variables beyond the 6th are stored on the stack, starting below the saved registers and counting down.

Public Class Methods

new(params) click to toggle source
# File voodoo/generators/arm_gas_generator.rb, line 74
def initialize params
  @BYTES_PER_INSTRUCTION = 4
  @WORDSIZE_BITS = 2
  @WORDSIZE = 1 << @WORDSIZE_BITS
  @CODE_ALIGNMENT = 4
  @DATA_ALIGNMENT = @WORDSIZE
  @FUNCTION_ALIGNMENT = @WORDSIZE
  @STACK_ALIGNMENT_BITS = 3
  @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
  # Registers used for storing local variables.
  @LOCAL_REGISTERS = [:r4, :r5, :r6, :r7, :r8, :r10]
  # Set of registers used for storing local variables.
  @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
  # Registers to be saved by save-frame.
  @SAVE_FRAME_REGISTERS = [:r4, :r5, :r6, :r7, :r8,
                           :r9, :r10, :r11, :r13, :r14]
  # Hash from register names to offsets in saved frame.
  @SAVED_FRAME_LAYOUT = {}
  @SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i }
  @INITIAL_FRAME_SIZE = 2 * @WORDSIZE
  @MAX_CONSTANT_POOL_OFFSET = 4096
  @NREGISTER_ARGS = 4
  @NREGISTER_LOCALS = @LOCAL_REGISTERS.length
  @FP = :r11
  @RETURN = :r0
  @TEMPORARIES = [:r12, :r3, :r2, :r1]
  @bytes_emitted_since_constants = 0
  @constants = {}
  @emitting_constants = false
  @frame_offset = 0
  @frame_size = 0
  @function_end_label = nil
  @if_labels = []
  @saved_registers = []       # registers we've saved in the current frame
  @saved_size = 0             # bytes dedicated to saved registers
  super params
  @output_file_suffix = '.s'
  @features.merge!          :'bits-per-word' => '32',
    :'byte-order' => 'little-endian',
    :'bytes-per-word' => 4
end

Public Instance Methods

_common_emit(code) click to toggle source
Alias for: emit
add_constant(value) click to toggle source

Create an entry in the constants table, returning the label that will refer to the constant. The value may be an integer or a label.

# File voodoo/generators/arm_gas_generator.rb, line 120
def add_constant value
  # If the value is already in the constant pool, return its label.
  # Else, add it to the pool with a new label and return that label.
  @constants.fetch(value) do |value|
    label = gensym
    @constants[value] = label
    label
  end
end
arg_offset(n) click to toggle source

Returns the fp-relative offset for the nth (0-based) argument.

# File voodoo/generators/arm_gas_generator.rb, line 131
def arg_offset n
  (n - @NREGISTER_ARGS) * @WORDSIZE
end
arg_reference(n) click to toggle source

Returns an fp-relative reference for the nth (0-based) argument.

# File voodoo/generators/arm_gas_generator.rb, line 136
def arg_reference n
  offset_reference arg_offset(n)
end
arg_register(n) click to toggle source

Return the register in which the nth (0-based) argument is stored, or nil if not stored in a register

# File voodoo/generators/arm_gas_generator.rb, line 142
def arg_register n
  if n < @NREGISTER_ARGS
    # We copied the argument to one of the local registers.
    @LOCAL_REGISTERS[n]
  else
    nil
  end
end
auto_bytes(value, register) click to toggle source
# File voodoo/generators/arm_gas_generator.rb, line 151
def auto_bytes value, register
  if value.kind_of? Integer
    grow_frame value
  else
    temporary = register == :sp ? @TEMPORARIES[0] : register
    load_value_into_register value, temporary
    auto_bytes_register temporary
  end
  emit "cpy #{register}, sp\n" unless register == :sp
end
auto_bytes_register(register) click to toggle source

auto-bytes where the value is supplied in a register and the return value will be in sp. register must not be sp.

# File voodoo/generators/arm_gas_generator.rb, line 164
def auto_bytes_register register
  temporary = register == @TEMPORARIES[0] ? :r3 : @TEMPORARIES[0]
  emit "add #{register}, #{register}, \##{@STACK_ALIGNMENT - 1}\n"
  emit "mvn #{temporary}, \##{@STACK_ALIGNMENT - 1}\n"
  emit "and #{register}, #{register}, #{temporary}\n"
  emit "sub sp, #{register}\n"
end
auto_words(value, register) click to toggle source
# File voodoo/generators/arm_gas_generator.rb, line 172
def auto_words value, register
  if value.kind_of? Integer
    auto_bytes(value * @WORDSIZE, register)
  else
    raise "Can't use :sp as a register for auto_words" if register == :sp
    load_value_into_register value, register
    emit "lsl #{register}, #{register}, \##{@WORDSIZE_BITS}\n"
    auto_bytes_register register
    emit "cpy #{register}, sp\n"
  end
end
begin_block(*code) click to toggle source

Begins a new block.

# File voodoo/generators/arm_gas_generator.rb, line 185
def begin_block *code
  # If we are starting a block at top level, create a frame
  if @environment == @top_level
    nlocals = count_locals code
    create_frame nlocals, false
  end
  @environment = Environment.new @environment
end
begin_function(formals, nlocals) click to toggle source

Emit function prologue and declare formals as function arguments

# File voodoo/generators/arm_gas_generator.rb, line 195
def begin_function formals, nlocals
  if @environment != @top_level
    raise "Can only begin a function at top level"
  end

  @function_end_label = gensym
  environment = Environment.new @environment
  formals.each_with_index do |formal, i|
    if i < @NREGISTER_ARGS
      environment.add_arg formal, arg_register(i)
    else
      environment.add_arg formal, arg_offset(i)
    end
  end
  @environment = environment
  emit_function_prologue formals, nlocals
end
byte(value) click to toggle source

Define a byte with the given value

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

Call a function.

# File voodoo/generators/arm_gas_generator.rb, line 219
def call func, *args
  # Calculate how many arguments need to be pushed on
  # the stack, and allocate space for them.
  nstack_args = number_of_stack_arguments args.length
  old_frame_offset = @frame_offset
  old_frame_size = @frame_size
  grow_frame nstack_args if nstack_args > 0

  # Put stack arguments on the stack
  (@NREGISTER_ARGS...args.length).each do |n|
    with_temporary do |temporary|
      load_value_into_register args[n], temporary
      emit "str #{temporary}, " +
        "[sp , \##{(n - @NREGISTER_ARGS) * @WORDSIZE}]\n"
    end
  end

  # Put register arguments in the right registers
  nregister_args = number_of_register_arguments args.length
  nregister_args.times do |n|
      load_value_into_register args[n], :"r#{n}"        
  end

  # Call function
  if global? func
    @symbol_tracker.use func
    emit "bl #{func}\n"
  else
    with_temporary do |temporary|
      func_reg = load_value func, temporary
      emit "blx #{func_reg}\n"
    end
  end

  # Restore original stack frame
  if old_frame_size != @frame_size
    emit "add sp, sp, \##{@frame_size - old_frame_size}\n"
    @frame_offset = old_frame_offset
    @frame_size = old_frame_size
  end
end
comment(text) click to toggle source

Emits a comment.

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

Start a conditional using the specified branch instruction after the comparison.

# File voodoo/generators/arm_gas_generator.rb, line 298
def common_if comp, x, y = nil
  with_temporary do |temporary|
    xreg = load_value x, temporary
    yreg = load_value y, :a4

    falselabel = @environment.gensym
    @if_labels.push falselabel

    emit "cmp #{xreg}, #{yreg}\n"
    lut = { :ifeq => "bne", :ifge => "blt", :ifgt => "ble",
      :ifle => "bgt", :iflt => "bge", :ifne => "beq" }
    emit "#{lut[comp]} #{falselabel}\n"
  end
end
create_frame(nvars, save_lr = true) click to toggle source

Creates a stack frame for the given number of arguments and local variables.

# File voodoo/generators/arm_gas_generator.rb, line 263
def create_frame nvars, save_lr = true
  # Calculate how many variables we will store in registers,
  # and how many on the stack.
  nregister_vars = [nvars, @NREGISTER_LOCALS].min
  nstack_vars = nvars - nregister_vars

  # Save the registers we will clobber to the stack.
  clobbered = []
  nregister_vars.times do |i|
    clobbered << @LOCAL_REGISTERS[i]
  end
  clobbered << @FP
  clobbered << :lr if save_lr
  @saved_registers = clobbered
  @saved_size = @saved_registers.length * @WORDSIZE
  emit "stmfd sp!, {#{clobbered.join ', '}}\n"
  emit "add #{@FP}, sp, \##{clobbered.length * @WORDSIZE}\n"

  # Calculate frame size so that the stack pointer will
  # be properly aligned at the end of emit_function_prologue.
  @frame_size = stack_align((clobbered.length + nstack_vars) * @WORDSIZE)
  extra_space = @frame_size - clobbered.length * @WORDSIZE
  if extra_space > 0
    emit "sub sp, sp, \##{extra_space}\n"
  end
  @frame_offset = 0
end
destroy_frame(ret = false) click to toggle source

Destroys the current stack frame. If ret is true, loads the saved value of lr into pc.

# File voodoo/generators/arm_gas_generator.rb, line 315
def destroy_frame ret = false
  # Set sp back to where saved registers were stored
  saved = @saved_registers
  emit "sub sp, #{@FP}, \##{saved.length * @WORDSIZE}\n"

  if ret
    index = saved.index :lr
    if index
      saved[index] = :pc
    else
      raise "Request to load saved lr into pc, but lr has not been saved"
    end
  end
  emit "ldmfd sp!, {#{saved.join ', '}}\n"
  
  emit_constants if ret
end
emit(code) click to toggle source

Adds code to the current section.

# File voodoo/generators/arm_gas_generator.rb, line 336
def emit code
  _common_emit code
  if real_section_name(@section) == '.text'
    @bytes_emitted_since_constants = @bytes_emitted_since_constants +
      @BYTES_PER_INSTRUCTION
    if !@emitting_constants &&
        (@bytes_emitted_since_constants + @WORDSIZE * @constants.length) >
        @MAX_CONSTANT_POOL_OFFSET - 2 * @BYTES_PER_INSTRUCTION
      emit_constants_with_goto
    end
  end
end
Also aliased as: _common_emit
emit_align(n) click to toggle source

Aligns on the next multiple of n bytes.

# File voodoo/generators/arm_gas_generator.rb, line 350
def emit_align n
  emit ".align #{n}\n"
end
emit_constants() click to toggle source

Writes any constants that need to be written to the instruction stream, and clears the list of constants that need to be written.

# File voodoo/generators/arm_gas_generator.rb, line 356
def emit_constants
  old_emitting_constants = @emitting_constants
  begin
    @emitting_constants = true
    @constants.each do |value,l|
      label l
      word value
    end
    @constants = {}
    @bytes_emitted_since_constants = 0
  ensure
    @emitting_constants = old_emitting_constants
  end
end
emit_constants_with_goto() click to toggle source

Emits constants, and code that jumps over the constants pool.

# File voodoo/generators/arm_gas_generator.rb, line 372
def emit_constants_with_goto
  old_emitting_constants = @emitting_constants
  begin
    @emitting_constants = true
    lbl = gensym
    goto lbl
    label lbl
  ensure
    @emitting_constants = old_emitting_constants
  end
end
emit_export(*symbols) click to toggle source

Declares symbols to be exported.

# File voodoo/generators/arm_gas_generator.rb, line 385
def emit_export *symbols
  symbols.each { |sym| emit ".globl #{sym}\n" }
end
emit_function_prologue(formals = [], nlocals = 0) click to toggle source

Emit function prologue.

# File voodoo/generators/arm_gas_generator.rb, line 390
def emit_function_prologue formals = [], nlocals = 0
  # Calculate the number of arguments we were passed in
  # registers, the total number of values we need to save
  # on the stack, then create a stack frame and save
  # the registers we will be using.
  nregister_args = [formals.length, @NREGISTER_ARGS].min
  nvars = nregister_args + nlocals
  create_frame nvars, true

  # Move arguments that were passed in registers into
  # callee-save registers.
  nregister_args.times do |i|
    emit "cpy #{@LOCAL_REGISTERS[i]}, r#{i}\n"
  end
end
emit_label_size(name) click to toggle source

Emits a label size annotation.

# File voodoo/generators/arm_gas_generator.rb, line 416
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/arm_gas_generator.rb, line 407
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/arm_gas_generator.rb, line 421
def emit_load_word register, base, offset
  if offset == 0
    emit "ldr #{register}, [#{base}]\n"
  else
    emit "ldr #{register}, [#{base}, \##{offset * @WORDSIZE}]\n"
  end
end
emit_store_word(register, base, offset) click to toggle source

Stores the value of a register in memory.

# File voodoo/generators/arm_gas_generator.rb, line 430
def emit_store_word register, base, offset
  if offset == 0
    emit "str #{register}, [#{base}]\n"
  else
    emit "str #{register}, [#{base}, \##{offset * @WORDSIZE}]\n"
  end
end
end_block() click to toggle source

Ends the current block.

# File voodoo/generators/arm_gas_generator.rb, line 439
def end_block
  # If we are returning to top level, restore stack pointer
  # and saved registers.
  if @environment.parent == @top_level
    offset = @frame_size - @saved_size
    if offset > 0
      emit "add sp, sp, \##{offset}\n"
    end
    emit "ldmfd sp!, {#{@saved_registers.join ', '}}\n"
    @frame_size = 0
    @frame_offset = 0
    @saved_registers = []
    @saved_size = 0

    # If we need to emit constants, do so now
    unless @constants.empty?
      emit_constants_with_goto
    end
  end

  # Restore old value of @environment
  @environment = @environment.parent
end
end_function() click to toggle source

Ends a function body.

# File voodoo/generators/arm_gas_generator.rb, line 464
def end_function
  if @environment == @top_level
    raise "Cannot end function when not in a function"
  end

  label @function_end_label

  destroy_frame true
  @frame_size = 0
  @frame_offset = 0
  @saved_registers = []
  @saved_size = 0
  @environment = @top_level
end
end_if() click to toggle source

Ends a conditional.

# File voodoo/generators/arm_gas_generator.rb, line 480
def end_if
  label @if_labels.pop
end
eval_binop(expr, register) click to toggle source

Evaluate the binary operation expr and store the result in register

# File voodoo/generators/arm_gas_generator.rb, line 485
def eval_binop expr, register
  op = expr[0]

  # Emulation for div and mod, which ARM does not have instructions for
  case op
  when :div
    func = :"__aeabi_idiv"
    import func unless @relocated_symbols.member? func
    call func, expr[1], expr[2]
    emit "cpy #{register}, r0\n" if register != :r0
    return
  when :mod
    func = :"__aeabi_idivmod"
    import func unless @relocated_symbols.member? func
    call func, expr[1], expr[2]
    emit "cpy #{register}, r1\n" if register != :r1
    return
  end

  with_temporaries(2) do |t1,t2|
    x = load_value expr[1], t1

    case op
    when :mul
      # Operand must be in a register for these ops.
      y = load_value expr[2], t2
    else
      y = value_ref expr[2], t2
    end

    case op
    when :bsr
      emit "lsr #{register}, #{x}, #{y}\n"
    when :or
        emit "orr #{register}, #{x}, #{y}\n"
    when :mul
      # Can't store result in same register as first operand.
      if register == x
        if x == y
          # All using the same register. Move x to a different
          # register to make this work.
          temp = (x == t1) ? t2 : t1
          emit "cpy #{temp}, #{x}\n"
          emit "mul #{register}, #{temp}, #{y}\n"
        else
          # Multiplication is commutative. Just swap x and y.
          emit "mul #{register}, #{y}, #{x}\n"
        end
      else
        # Common case, register and x are different.
        emit "mul #{register}, #{x}, #{y}\n"
      end
    when :rol
      if integer? expr[2]
        y = "\##{32 - expr[2]}"
      else
        emit "rsb #{y}, #{y}, #32\n"
      end
      emit "ror #{register}, #{x}, #{y}\n"
    when :shl
      emit "lsl #{register}, #{x}, #{y}\n"
    when :shr
      emit "lsr #{register}, #{x}, #{y}\n"
    when :xor
      emit "eor #{register}, #{x}, #{y}\n"
    else
      emit "#{expr[0]} #{register}, #{x}, #{y}\n"
    end
  end
end
eval_expr(expr, register) click to toggle source

Evaluates the expression expr and stores the result in register.

# File voodoo/generators/arm_gas_generator.rb, line 557
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 "cpy #{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
      load_value_into_register expr[1], register
      with_temporary do |temporary|
        emit "mvn #{temporary}, #0\n"
        emit "eor #{register}, #{register}, #{temporary}\n"
      end
    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

Load byte from base + offset into register

# File voodoo/generators/arm_gas_generator.rb, line 593
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
    with_temporary do |temporary|
      base_reg = load_value base, temporary
      if offset == 0
        emit "ldrb #{register}, [#{base_reg}]\n"
      else
        emit "ldrb #{register}, [#{base_reg}, \##{offset}]\n"
      end
    end
  else
    with_temporaries(2) do |t1,t2|
      base_reg = load_value base, t1
      offset_reg = load_value offset, t2
      emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
    end
  end
end
get_word(base, offset, register) click to toggle source

Load word from base + offset * _@WORDSIZE_ into register

# File voodoo/generators/arm_gas_generator.rb, line 618
def get_word base, offset, register
  if integer? offset
    with_temporary do |temporary|
      base_reg = load_value base, temporary
      if offset == 0
        emit "ldr #{register}, [#{base_reg}]\n"
      else
        emit "ldr #{register}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
      end
    end
  else
    with_temporaries(2) do |t1,t2|
      base_reg = load_value base, t1
      offset_reg = load_value offset, t2
      emit "ldr #{register}, [#{base_reg}, #{offset_reg}, LSL #2]\n"
    end
  end
end
goto(label) click to toggle source

Jump to a label.

# File voodoo/generators/arm_gas_generator.rb, line 638
def goto label
  if global? label
    emit "b #{label}\n"
  else
    with_temporary do |temporary|
      register = load_value label, temporary
      emit "cpy pc, #{register}\n"
    end
  end

  # If we have constants that need to be emitted, do so now
  emit_constants
end
grow_frame(nwords) click to toggle source

Grows the current frame by n words, plus padding to respect alignment rules.

# File voodoo/generators/arm_gas_generator.rb, line 654
def grow_frame nwords
  increment = stack_align(nwords * @WORDSIZE)
  emit "sub sp, sp, \##{increment}\n"
  @frame_size = @frame_size + increment
  @frame_offset = @frame_offset + increment
end
ifelse() click to toggle source

Start the false path of a conditional.

# File voodoo/generators/arm_gas_generator.rb, line 662
def ifelse
  newlabel = @environment.gensym
  goto newlabel
  lbl = @if_labels.pop
  label lbl
  @if_labels.push newlabel
end
ifeq(x, y) click to toggle source

Test if x is equal to y

# File voodoo/generators/arm_gas_generator.rb, line 671
def ifeq x, y
  common_if :ifeq, x, y
end
ifge(x, y) click to toggle source

Test if x is greater than or equal to y

# File voodoo/generators/arm_gas_generator.rb, line 676
def ifge x, y
  common_if :ifge, x, y
end
ifgt(x, y) click to toggle source

Test if x is strictly greater than y

# File voodoo/generators/arm_gas_generator.rb, line 681
def ifgt x, y
  common_if :ifgt, x, y
end
ifle(x, y) click to toggle source

Test if x is less than or equal to y

# File voodoo/generators/arm_gas_generator.rb, line 686
def ifle x, y
  common_if :ifle, x, y
end
iflt(x, y) click to toggle source

Test if x is strictly less than y

# File voodoo/generators/arm_gas_generator.rb, line 691
def iflt x, y
  common_if :iflt, x, y
end
ifne(x, y) click to toggle source

Test if x different from y

# File voodoo/generators/arm_gas_generator.rb, line 696
def ifne x, y
  common_if :ifne, x, y
end
let(symbol, *expr) click to toggle source

Introduce a new local variable

# File voodoo/generators/arm_gas_generator.rb, line 701
def let symbol, *expr
  n = @environment.locals
  register = local_register n

  if register
    # We will use a register to store the value
    @environment.add_local symbol, register
    eval_expr expr, register
  else
    # We will use the stack to store the value
    offset = local_offset n
    @environment.add_local symbol, offset
    with_temporary do |temporary|
      eval_expr expr, temporary
      emit "str #{temporary}, #{offset_reference offset}\n"
    end
  end
end
load_at(address, register) click to toggle source

Loads the value at the given address.

# File voodoo/generators/arm_gas_generator.rb, line 721
def load_at address, register
  load_value_into_register address, register
  emit "ldr #{register}, [#{register}]\n"
  register
end
load_value(x, register) click to toggle source

Loads a value into some register. If the value is already in a register, does nothing. Else, loads the value into the register given as the second argument. Returns the name of the register the value is in.

# File voodoo/generators/arm_gas_generator.rb, line 732
def load_value x, register
  ref = value_ref x, register
  if register? ref
    ref
  else
    emit "mov #{register}, #{ref}\n"
    register
  end
end
load_value_into_register(x, register) click to toggle source

Loads a value into a specific register.

# File voodoo/generators/arm_gas_generator.rb, line 743
def load_value_into_register x, register
  ref = value_ref x, register
  if ref != register
    if register? ref
      emit "cpy #{register}, #{ref}\n"
    else
      emit "mov #{register}, #{ref}\n"
    end
  end
  register
end
local_offset(n) click to toggle source

Returns the fp-relative reference for the nth (0-based) local.

# File voodoo/generators/arm_gas_generator.rb, line 756
def local_offset n
  nstack_locals = n + number_of_register_arguments - @NREGISTER_LOCALS
  -(nstack_locals + 1) * @WORDSIZE - @saved_size
end
offset_reference(offset) click to toggle source

Given an offset, returns an fp-relative reference.

# File voodoo/generators/arm_gas_generator.rb, line 762
def offset_reference offset
  "[#{@FP}, \##{offset}]"
end
register_local?(n) click to toggle source

Returns true if the nth (0-based) local is stored in a register

# File voodoo/generators/arm_gas_generator.rb, line 767
def register_local? n
  (n + number_of_register_arguments) < @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/arm_gas_generator.rb, line 775
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
set(symbol, *expr) click to toggle source

Set a variable to the result of evaluating an expression

# File voodoo/generators/arm_gas_generator.rb, line 783
def set symbol, *expr
  if at_expr? symbol
    with_temporaries(2) do |t1,t2|
      eval_expr expr, t1
      register = load_value symbol[1], t2
      emit "str #{t1}, [#{register}]\n"
    end
  else
    x = @environment[symbol]
    if x == nil
      raise "Cannot change value of constant #{symbol}"
    elsif x.kind_of? Symbol
      eval_expr expr, x
    else
      with_temporary do |temporary|
        eval_expr expr, temporary
        emit "str #{temporary}, #{offset_reference x}\n"
      end
    end
  end
end
set_byte(base, offset, value) click to toggle source

Set the byte at base + offset to value

# File voodoo/generators/arm_gas_generator.rb, line 806
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

  if integer? offset
    base_reg = load_value base, :a4
    with_temporary do |temporary|
      load_value_into_register value, temporary
      if offset == 0
        emit "strb #{temporary}, [#{base_reg}]\n"
      else
        emit "strb #{temporary}, [#{base_reg}, \##{offset}]\n"
      end
    end
  else
    eval_binop [:add, base, offset], :a4
    with_temporary do |temporary|
      load_value_into_register value, temporary
      emit "strb #{temporary}, [a4]\n"
    end
  end
end
set_word(base, offset, value) click to toggle source

Set the word at base + offset * +@WORDSIZE+ to value

# File voodoo/generators/arm_gas_generator.rb, line 832
def set_word base, offset, value
  # 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, :a4
    with_temporary do |temporary|
      load_value_into_register value, temporary
      if offset == 0
        emit "str #{temporary}, [#{base_reg}]\n"
      else
        emit "str #{temporary}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
      end
    end
  else
    load_value_into_register base, :a4
    with_temporary do |temporary|
      load_value_into_register offset, temporary
      emit "add a4, a4, #{temporary}, LSL #2\n"
      load_value_into_register value, temporary
      emit "str #{temporary}, [a4]\n"
    end
  end
end
string(value) click to toggle source

Define a string with the given value

# File voodoo/generators/arm_gas_generator.rb, line 860
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

Call a function, re-using the current call frame if possible.

# File voodoo/generators/arm_gas_generator.rb, line 875
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

  # We will assign arguments from left to right.
  # Find places that we will overwrite before we read them,
  # and store their values in some newly allocated stack space.
  old_frame_offset = @frame_offset
  old_frame_size = @frame_size
  overwritten = {}
  (@NREGISTER_ARGS...args.length).each do |i|
    arg = args[i]
    arg = arg[1] if at_expr? arg
    if symbol?(arg)
      binding = @environment[arg]
      if binding[0] == :arg && binding[1] >= @NREGISTER_ARGS &&
          binding[1] < i
        # Argument i is a stack argument, but the value we will assign
        # it is a stack argument that comes before it, so we will
        # have overwritten it by the time we get to it.
        overwritten[arg] = nil
      end
    end
  end
  
  unless overwritten.empty?
    # Allocate space for arguments to be saved
    grow_frame overwritten.length
    # Save values
    offset = 0
    overwritten.each_key do |key|
      reg = load_value key
      emit "str #{reg}, [sp, \##{offset}]\n"
      overwritten[key] = offset
      offset = offset + @WORDSIZE
    end
  end

  # Assign arguments
  args.each_index do |i|
    arg = args[i]
    if register_arg? i
      load_value_into_register arg, "a#{i + 1}"
    else
      # Test if this is a value we saved
      sym = at_expr?(arg) ? arg[1] : arg
      saved = overwritten[sym]
      if saved
        # Saved value, load from stack
        with_temporary do |temporary|
          emit "ldr #{temporary}, [sp, \##{saved}]\n"
          emit "str #{temporary}, #{arg_reference i}\n"
        end
      else
        # Regular value, use load_value
        with_temporary do |temporary|
          reg = load_value arg, temporary
          emit "str #{reg}, #{arg_reference i}\n"
        end
      end
    end
  end

  with_temporary do |temporary|
    # Load address of function to be called
    load_value_into_register func, temporary

    # Destroy current activation frame and enter func
    destroy_frame false
    emit "bx #{temporary}\n"
  end
  emit_constants
end
value_ref(x, register) click to toggle source

Returns a reference to a value. For immediate values that fit in 8 bits, this returns the value itself (in ARM syntax). For all other values, loads the value into a register and returns the name of the register. If the value is already in a register, the name of that register is returned. Else, the value is loaded into the register specified as the second argument.

# File voodoo/generators/arm_gas_generator.rb, line 963
def value_ref x, register
  if substitution? x
    x = substitute_number x[1]
  end
    
  if integer? x
    if x >= 0 && x <= 255
      return "\##{x}"
    elsif x >= -255 && x < 0
      emit "mvn #{register}, \##{-(x + 1)}\n"
      return register
    else
      lbl = add_constant x
      emit "ldr #{register}, #{lbl}\n"
      return register
    end
  elsif symbol? x
    binding = @environment[x]
    if binding.kind_of? Symbol
      # Value is already in a register. Return register name.
      return binding
    elsif binding.kind_of? Integer
      # Value is on the stack. Load from the stack.
      emit "ldr #{register}, #{offset_reference binding}\n"
      return register
    else
      # Assume global
      @symbol_tracker.use x
      lbl = add_constant x
      emit "ldr #{register}, #{lbl}\n"
      return register
    end
  elsif at_expr? x
    load_at x[1], register
  else
    raise "Don't know how to load #{x.inspect}"
  end
end
word(value) click to toggle source

Define a word with the given value

# File voodoo/generators/arm_gas_generator.rb, line 1003
def word value
  emit ".int #{value}\n"
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.