Class: Voodoo::ARMGasGenerator

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

Overview

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.

Direct Known Subclasses

ARMELFGenerator

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, #features, #function, #gensym, #global?, #has_feature?, #in_section, #integer?, #label, #local_register, #number_of_register_arguments, #number_of_stack_arguments, #output_file_name, #output_file_suffix, #real_section_name, #register?, #register_arg?, #registers_for_locals, #restore_frame, #restore_locals, #restore_registers_from_frame, #save_frame, #save_frame_and_locals, #save_locals, #save_registers_to_frame, #saved_frame_size, #section, #section=, #section_alias, #stack_align, #substitute_number, #substitution?, #symbol?, #symmetric_binop?, #undefined_symbols, #with_temporaries, #with_temporary, #write

Constructor Details

#initialize(params) ⇒ ARMGasGenerator

Returns a new instance of ARMGasGenerator.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 74

def initialize params
  @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
  @NREGISTER_ARGS = 4
  @NREGISTER_LOCALS = @LOCAL_REGISTERS.length
  @FP = :r11
  @RETURN = :r0
  @TEMPORARIES = [:r12, :r3, :r2, :r1]
  @constants = []
  @frame_offset = 0
  @frame_size = 0
  @function_end_label = nil
  @imports = Set.new
  @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

Instance Method Details

#add_constant(value) ⇒ Object

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.



117
118
119
120
121
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 117

def add_constant value
  label = gensym
  @constants << [label, value]
  label
end

#arg_offset(n) ⇒ Object

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



124
125
126
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 124

def arg_offset n
  (n - @NREGISTER_ARGS) * @WORDSIZE
end

#arg_reference(n) ⇒ Object

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



129
130
131
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 129

def arg_reference n
  offset_reference arg_offset(n)
end

#arg_register(n) ⇒ Object

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



135
136
137
138
139
140
141
142
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 135

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



144
145
146
147
148
149
150
151
152
153
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 144

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

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



157
158
159
160
161
162
163
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 157

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



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 165

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

Begins a new block.



178
179
180
181
182
183
184
185
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 178

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

Emit function prologue and declare formals as function arguments



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 188

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

Define a byte with the given value



207
208
209
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 207

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

#call(func, *args) ⇒ Object

Call a function.



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 212

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

Emits a comment.



285
286
287
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 285

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

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

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



291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 291

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

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



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 256

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

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



308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 308

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_align(n) ⇒ Object

Aligns on the next multiple of n bytes.



327
328
329
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 327

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

#emit_constantsObject

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



333
334
335
336
337
338
339
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 333

def emit_constants
  @constants.each do |x|
    label x[0]
    word x[1]
  end
  @constants = []
end

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

Emit function prologue.



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 342

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

Emits a label size annotation.



368
369
370
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 368

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

#emit_label_type(name, type) ⇒ Object

Emits a label type annotation.



359
360
361
362
363
364
365
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 359

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.



373
374
375
376
377
378
379
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 373

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

Stores the value of a register in memory.



382
383
384
385
386
387
388
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 382

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_blockObject

Ends the current block.



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 391

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?
      lbl = gensym
      goto lbl
      label lbl
    end
  end

  # Restore old value of @environment
  @environment = @environment.parent
end

#end_functionObject

Ends a function body.



418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 418

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_ifObject

Ends a conditional.



434
435
436
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 434

def end_if
  label @if_labels.pop
end

#eval_binop(expr, register) ⇒ Object

Evaluate the binary operation expr and store the result in register



439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
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
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 439

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

Evaluates the expression expr and stores the result in register.



511
512
513
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
544
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 511

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

#export(*symbols) ⇒ Object

Export symbols from the current section



547
548
549
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 547

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

#get_byte(base, offset, register) ⇒ Object

Load byte from base + offset into register



552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 552

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

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



577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 577

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

Jump to a label.



597
598
599
600
601
602
603
604
605
606
607
608
609
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 597

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

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



613
614
615
616
617
618
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 613

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

#ifelseObject

Start the false path of a conditional.



621
622
623
624
625
626
627
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 621

def ifelse
  newlabel = @environment.gensym
  goto newlabel
  lbl = @if_labels.pop
  label lbl
  @if_labels.push newlabel
end

#ifeq(x, y) ⇒ Object

Test if x is equal to y



630
631
632
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 630

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

#ifge(x, y) ⇒ Object

Test if x is greater than or equal to y



635
636
637
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 635

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

#ifgt(x, y) ⇒ Object

Test if x is strictly greater than y



640
641
642
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 640

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

#ifle(x, y) ⇒ Object

Test if x is less than or equal to y



645
646
647
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 645

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

#iflt(x, y) ⇒ Object

Test if x is strictly less than y



650
651
652
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 650

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

#ifne(x, y) ⇒ Object

Test if x different from y



655
656
657
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 655

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

#import(*symbols) ⇒ Object

Import labels into the current section



660
661
662
663
664
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 660

def import *symbols
  # Record imported labels in @imports and @symbol_tracker
  @imports.merge symbols
  @symbol_tracker.define *symbols
end

#let(symbol, *expr) ⇒ Object

Introduce a new local variable



667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 667

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

Loads the value at the given address.



687
688
689
690
691
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 687

def load_at address, register
  load_value_into_register address, register
  emit "ldr #{register}, [#{register}]\n"
  register
end

#load_value(x, register) ⇒ Object

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.



698
699
700
701
702
703
704
705
706
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 698

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

Loads a value into a specific register.



709
710
711
712
713
714
715
716
717
718
719
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 709

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

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



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

def local_offset n
  nstack_locals = n + number_of_register_arguments - @NREGISTER_LOCALS
  -(nstack_locals + 1) * @WORDSIZE - @saved_size
end

#offset_reference(offset) ⇒ Object

Given an offset, returns an fp-relative reference.



728
729
730
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 728

def offset_reference offset
  "[#{@FP}, \##{offset}]"
end

#register_local?(n) ⇒ Boolean

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

Returns:

  • (Boolean)


733
734
735
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 733

def register_local? n
  (n + number_of_register_arguments) < @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.



741
742
743
744
745
746
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 741

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

Set a variable to the result of evaluating an expression



749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 749

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

Set the byte at base + offset to value



772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 772

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

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



798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 798

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

Define a string with the given value



826
827
828
829
830
831
832
833
834
835
836
837
838
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 826

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

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



841
842
843
844
845
846
847
848
849
850
851
852
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
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 841

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

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.



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
955
956
957
958
959
960
961
962
963
964
965
966
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 929

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

Define a word with the given value



969
970
971
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 969

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