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   <-- 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 is used when another temporary is needed.

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?, #with_temporaries, #with_temporary, #write

Constructor Details

#initialize(params) ⇒ ARMGasGenerator

Returns a new instance of ARMGasGenerator.



63
64
65
66
67
68
69
70
71
72
73
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
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 63

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 = {}
  @if_labels = []
  @saved_registers = []       # registers we've saved in the current frame
  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.



105
106
107
108
109
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 105

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.



112
113
114
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 112

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

#arg_reference(n) ⇒ Object

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



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

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



123
124
125
126
127
128
129
130
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 123

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



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

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.



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

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



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

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.



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

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



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 176

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



195
196
197
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 195

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

#call(func, *args) ⇒ Object

Call a function.



200
201
202
203
204
205
206
207
208
209
210
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
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 200

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



271
272
273
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 271

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.



277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 277

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.



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 243

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



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 294

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.



313
314
315
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 313

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.



319
320
321
322
323
324
325
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 319

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.



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 328

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.



354
355
356
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 354

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

#emit_label_type(name, type) ⇒ Object

Emits a label type annotation.



345
346
347
348
349
350
351
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 345

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.



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

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.



368
369
370
371
372
373
374
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 368

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.



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 377

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_registers.length * @WORDSIZE
    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 = []

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



403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 403

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 = []
  @environment = @top_level
end

#end_ifObject

Ends a conditional.



418
419
420
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 418

def end_if
  label @if_labels.pop
end

#eval_binop(expr, register) ⇒ Object

Evaluate the binary operation expr and store the result in register



423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
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
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 423

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.has_key? 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.has_key? 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.



495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 495

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



531
532
533
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 531

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

#get_byte(base, offset, register) ⇒ Object

Load byte from base + offset into register



536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 536

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



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

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.



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

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.



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

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.



605
606
607
608
609
610
611
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 605

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



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

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

#ifge(x, y) ⇒ Object

Test if x is greater than or equal to y



619
620
621
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 619

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

#ifgt(x, y) ⇒ Object

Test if x is strictly greater than y



624
625
626
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 624

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

#ifle(x, y) ⇒ Object

Test if x is less than or equal to y



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

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

#iflt(x, y) ⇒ Object

Test if x is strictly less than y



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

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

#ifne(x, y) ⇒ Object

Test if x different from y



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

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

#import(*symbols) ⇒ Object

Import labels into the current section



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

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

#let(symbol, *expr) ⇒ Object

Introduce a new local variable



650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 650

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.



670
671
672
673
674
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 670

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.



681
682
683
684
685
686
687
688
689
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 681

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.



692
693
694
695
696
697
698
699
700
701
702
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 692

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.



705
706
707
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 705

def local_offset n
  -@INITIAL_FRAME_SIZE - ((n + number_of_register_arguments) * @WORDSIZE)
end

#offset_reference(offset) ⇒ Object

Given an offset, returns an fp-relative reference.



710
711
712
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 710

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)


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

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.



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

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



731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 731

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



754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 754

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



780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 780

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



808
809
810
811
812
813
814
815
816
817
818
819
820
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 808

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.



823
824
825
826
827
828
829
830
831
832
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
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 823

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.



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

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



950
951
952
# File 'lib/voodoo/generators/arm_gas_generator.rb', line 950

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