Class: Voodoo::MIPSGasGenerator

Inherits:
CommonCodeGenerator show all
Defined in:
lib/voodoo/generators/mips_gas_generator.rb

Overview

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:

  • 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.

Direct Known Subclasses

MIPSELFGenerator

Instance Method Summary collapse

Methods inherited from CommonCodeGenerator

#add, #add_function, #align, #assymetric_binop?, #at_expr?, #binop?, #block, #count_locals, #default_alignment, #each_statement, #emit, #emit_label, #emit_voodoo, #export, #features, #function, #gensym, #global?, #has_feature?, #import, #in_section, #integer?, #label, #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_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

Constructor Details

#initialize(params) ⇒ MIPSGasGenerator

Returns a new instance of MIPSGasGenerator.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/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
  @imports = {}
  @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

Instance Method Details

#addiu(target, source, immediate, temporary = @TEMPORARY) ⇒ Object

Adds immediate to source and stores the result in target.



159
160
161
162
163
164
165
166
167
168
169
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 159

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) ⇒ Object

Return the offset from the frame base at which the nth argument is stored.



173
174
175
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 173

def arg_offset n
  n * @WORDSIZE
end

#arg_reference(n) ⇒ Object

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



178
179
180
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 178

def arg_reference n
  offset_reference(arg_offset(n))
end

#arg_register(n) ⇒ Object

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



184
185
186
187
188
189
190
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 184

def arg_register n
  if register_arg? n
    "$#{@REGISTER_ARG_BASE + n}"
  else
    nil
  end
end

#auto_bytes(n, register) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 192

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) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 212

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) ⇒ Object

Begins a new block.



231
232
233
234
235
236
237
238
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 231

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) ⇒ Object

Emits function prologue and declare formals as function arguments.



241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 241

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) ⇒ Object

Defines a byte with the given value.



255
256
257
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 255

def byte value
  emit ".byte #{value}\n"
end

#call(func, *args) ⇒ Object

Calls a function.



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 260

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 @imports.has_key? 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) ⇒ Object

Emits a comment.



311
312
313
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 311

def comment text
  emit "# #{text}\n"
end

#common_if(comp, x, y = nil) ⇒ Object

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



317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 317

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) ⇒ Object

Creates an activation frame which can store nlocals local variables.



351
352
353
354
355
356
357
358
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 351

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) ⇒ Object



360
361
362
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 360

def emit_align alignment
  emit ".align #{alignment}\n"
end

#emit_export(*symbols) ⇒ Object

Exports symbols from the current section.



365
366
367
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 365

def emit_export *symbols
  symbols.each { |sym| emit ".globl #{sym}\n" }
end

#emit_function_prologue(formals = [], nlocals = 0) ⇒ Object

Emits function prologue.



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 370

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_import(*symbols) ⇒ Object

Imports labels into the current section.



399
400
401
402
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 399

def emit_import *symbols
  # Record imported labels in @imports
  symbols.each { |sym| @imports[sym] = sym }
end

#emit_label_size(name) ⇒ Object

Emits a label size annotation.



414
415
416
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 414

def emit_label_size name
  emit ".size #{name}, .-#{name}\n"
end

#emit_label_type(name, type) ⇒ Object

Emits a label type annotation.



405
406
407
408
409
410
411
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 405

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) ⇒ Object

Loads a word into a register.



419
420
421
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 419

def emit_load_word register, base, offset
  emit "lw #{register}, #{offset * @WORDSIZE}(#{base})\n"
end

#emit_store_word(register, base, offset) ⇒ Object

Stores the value of a register in memory.



424
425
426
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 424

def emit_store_word register, base, offset
  emit "sw #{register}, #{offset * @WORDSIZE}(#{base})\n"
end

#end_blockObject

Ends the current block.



455
456
457
458
459
460
461
462
463
464
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 455

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_functionObject

Ends a function body.



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 429

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_ifObject

Ends a conditional.



467
468
469
470
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 467

def end_if
  label = @if_labels.pop
  emit "#{label}:\n"
end

#eval_binop(expr, register) ⇒ Object

Evaluates the binary operation expr and store the result in register.



473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 473

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) ⇒ Object

Evaluates the expression expr and store the result in register.



514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 514

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) ⇒ Object

Loads byte from base + offset into register.



546
547
548
549
550
551
552
553
554
555
556
557
558
559
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 546

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) ⇒ Object

Loads word from base + offset * _@WORDSIZE_ into register.



562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 562

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) ⇒ Object

Jumps to an address.



581
582
583
584
585
586
587
588
589
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 581

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) ⇒ Object

Grows the stack by n bytes.



592
593
594
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 592

def grow_stack n
  addiu "$sp", "$sp", -n
end

#ifelseObject

Starts the false path of a conditional.



597
598
599
600
601
602
603
604
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 597

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) ⇒ Object

Tests if x is equal to y



607
608
609
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 607

def ifeq x, y
  common_if :ifeq, x, y
end

#ifge(x, y) ⇒ Object

Tests if x is greater than or equal to y



612
613
614
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 612

def ifge x, y
  common_if :ifge, x, y
end

#ifgt(x, y) ⇒ Object

Tests if x is strictly greater than y



617
618
619
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 617

def ifgt x, y
  common_if :ifgt, x, y
end

#ifle(x, y) ⇒ Object

Tests if x is less than or equal to y



622
623
624
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 622

def ifle x, y
  common_if :ifle, x, y
end

#iflt(x, y) ⇒ Object

Tests if x is strictly less than y



627
628
629
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 627

def iflt x, y
  common_if :iflt, x, y
end

#ifne(x, y) ⇒ Object

Tests if x different from y



632
633
634
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 632

def ifne x, y
  common_if :ifne, x, y
end

#let(symbol, *expr) ⇒ Object

Introduces a new local variable.



637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 637

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) ⇒ Object

Loads the value at the given address.



658
659
660
661
662
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 658

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) ⇒ Object

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.



671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 671

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
      if @relocated_symbols.include? x
        emit "lw #{register}, %got(#{x})(#{@GOT})\n"
      else
        emit "lui #{register}, %hi(#{x})\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) ⇒ Object

Loads a value into a specific register.



708
709
710
711
712
713
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 708

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) ⇒ Object

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.



718
719
720
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 718

def local_offset n
  -(n + 4) * @WORDSIZE
end

#local_reference(n) ⇒ Object

Returns an $sp-relative reference for the nth (0-based) local.



723
724
725
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 723

def local_reference n
  offset_reference(local_offset(n))
end

#local_register(n) ⇒ Object

Returns the register in which the nth local (0-based) is stored, or nil if not stored in a register.



729
730
731
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 729

def local_register n
  @LOCAL_REGISTERS[n]
end

#max_localsObject

Computes the maximum number of locals that would fit in the current frame size.



735
736
737
738
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 735

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) ⇒ Object

Given an offset relative to the base of the frame, returns an reference to that memory location.



742
743
744
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 742

def offset_reference offset
  "#{offset}($fp)"
end

#register_local?(n) ⇒ Boolean

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

Returns:

  • (Boolean)


747
748
749
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 747

def register_local? n
  n < @NREGISTER_LOCALS
end

#ret(*words) ⇒ Object

Returns from a function.

words may contain an expression to be evaluated. The result of the evaluation is returned from the function.



755
756
757
758
759
760
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 755

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) ⇒ Object

Saves the current frame at the given location.



763
764
765
766
767
768
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 763

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) ⇒ Object

Sets a variable to the result of evaluating an expression.



771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 771

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) ⇒ Object

Sets the byte at base + offset to value.



791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 791

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) ⇒ Object

Sets the word at base + offset * @WORDSIZE to value.



813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 813

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) ⇒ Object

Defines a string with the given value.



838
839
840
841
842
843
844
845
846
847
848
849
850
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 838

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) ⇒ Object

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



853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 853

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 @imports.has_key? 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) ⇒ Object

Defines a word with the given value



957
958
959
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 957

def word value
  emit ".int #{value}\n"
end

#write(io) ⇒ Object

Write generated code to the given IO object.



962
963
964
965
966
967
968
969
970
971
# File 'lib/voodoo/generators/mips_gas_generator.rb', line 962

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