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_import, #emit_label, #emit_voodoo, #export, #features, #function, #gensym, #global?, #has_feature?, #import, #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
# 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
  @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.



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

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.



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

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

#arg_reference(n) ⇒ Object

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



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

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



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

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



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

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.



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

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



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

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.



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

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



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

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



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

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

#call(func, *args) ⇒ Object

Call a function.



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

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.



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

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.



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

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.



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

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.



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

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.



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

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.



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

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

#emit_export(*symbols) ⇒ Object

Declares symbols to be exported.



341
342
343
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 341

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

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

Emit function prologue.



346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 346

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.



372
373
374
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 372

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

#emit_label_type(name, type) ⇒ Object

Emits a label type annotation.



363
364
365
366
367
368
369
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 363

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.



377
378
379
380
381
382
383
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 377

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.



386
387
388
389
390
391
392
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 386

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.



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

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.



422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 422

def end_function
  if @environment == @top_level
    raise "Cannot end function when not in a function"
  end

  label @function_end_label

  destroy_frame true
  @frame_size = 0
  @frame_offset = 0
  @saved_registers = []
  @saved_size = 0
  @environment = @top_level
end

#end_ifObject

Ends a conditional.



438
439
440
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 438

def end_if
  label @if_labels.pop
end

#eval_binop(expr, register) ⇒ Object

Evaluate the binary operation expr and store the result in register



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

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

Evaluates the expression expr and stores the result in register.



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
545
546
547
548
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 515

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

Load byte from base + offset into register



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

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



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

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.



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

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.



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

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.



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

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



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

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

#ifge(x, y) ⇒ Object

Test if x is greater than or equal to y



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

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

#ifgt(x, y) ⇒ Object

Test if x is strictly greater than y



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

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

#ifle(x, y) ⇒ Object

Test if x is less than or equal to y



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

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

#iflt(x, y) ⇒ Object

Test if x is strictly less than y



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

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

#ifne(x, y) ⇒ Object

Test if x different from y



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

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

#let(symbol, *expr) ⇒ Object

Introduce a new local variable



659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 659

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.



679
680
681
682
683
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 679

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.



690
691
692
693
694
695
696
697
698
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 690

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.



701
702
703
704
705
706
707
708
709
710
711
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 701

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.



714
715
716
717
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 714

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.



720
721
722
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 720

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)


725
726
727
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 725

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.



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

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



741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 741

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



764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 764

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



790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 790

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



818
819
820
821
822
823
824
825
826
827
828
829
830
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 818

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.



833
834
835
836
837
838
839
840
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
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 833

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.



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
955
956
957
958
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 921

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



961
962
963
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 961

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