voodoo 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  require 'voodoo/generators/common_code_generator'
2
+ require 'set'
2
3
 
3
4
  module Voodoo
4
5
  # = ARM GNU Assembler Code Generator
@@ -44,7 +45,7 @@ module Voodoo
44
45
  # padding
45
46
  # argn
46
47
  # :
47
- # arg4
48
+ # arg4 <-- r11 points here
48
49
  # saved r14
49
50
  # saved r11
50
51
  # :
@@ -52,30 +53,44 @@ module Voodoo
52
53
  #
53
54
  # == Register Usage
54
55
  #
55
- # Inside a function, registers r4..r8, r10, and r11 are used for
56
- # local variables and function arguments.
56
+ # Inside a function, registers r4..r8 and r10 are used for local variables
57
+ # and function arguments, whereas r11 is used as a frame pointer.
57
58
  #
58
59
  # r12 is used as a temporary, and r3 is used when another temporary
59
60
  # is needed.
60
61
  #
61
62
  class ARMGasGenerator < CommonCodeGenerator
62
63
  def initialize params
63
- @WORDSIZE = 4
64
+ @WORDSIZE_BITS = 2
65
+ @WORDSIZE = 1 << @WORDSIZE_BITS
64
66
  @CODE_ALIGNMENT = 4
65
67
  @DATA_ALIGNMENT = @WORDSIZE
66
68
  @FUNCTION_ALIGNMENT = @WORDSIZE
67
-
69
+ @STACK_ALIGNMENT_BITS = 3
70
+ @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
71
+ # Registers used for storing local variables.
72
+ @LOCAL_REGISTERS = [:r4, :r5, :r6, :r7, :r8, :r10]
73
+ # Set of registers used for storing local variables.
74
+ @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
75
+ # Registers to be saved by save-frame.
76
+ @SAVE_FRAME_REGISTERS = [:r4, :r5, :r6, :r7, :r8,
77
+ :r9, :r10, :r11, :r13, :r14]
78
+ # Hash from register names to offsets in saved frame.
79
+ @SAVED_FRAME_LAYOUT = {}
80
+ @SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i }
81
+ @INITIAL_FRAME_SIZE = 2 * @WORDSIZE
68
82
  @NREGISTER_ARGS = 4
69
- @NREGISTER_LOCALS = 7
83
+ @NREGISTER_LOCALS = @LOCAL_REGISTERS.length
84
+ @FP = :r11
70
85
  @RETURN = :r0
71
- @TEMPORARY = :r12
86
+ @TEMPORARIES = [:r12, :r3, :r2, :r1]
72
87
  @constants = []
73
88
  @frame_offset = 0
74
89
  @frame_size = 0
75
90
  @function_end_label = nil
76
91
  @imports = {}
77
92
  @if_labels = []
78
- @saved_registers = []
93
+ @saved_registers = [] # registers we've saved in the current frame
79
94
  super params
80
95
  @output_file_suffix = '.s'
81
96
  @features.merge! \
@@ -93,54 +108,62 @@ module Voodoo
93
108
  label
94
109
  end
95
110
 
96
- def align alignment = nil
97
- unless alignment
98
- # Get default alignment
99
- case @section
100
- when :code
101
- alignment = @CODE_ALIGNMENT
102
- when :data
103
- alignment = @DATA_ALIGNMENT
104
- when :function
105
- alignment = @FUNCTION_ALIGNMENT
106
- else
107
- # Use data alignment as default
108
- alignment = @DATA_ALIGNMENT
109
- end
110
- end
111
- emit ".align #{alignment}\n" unless alignment == 0
111
+ # Returns the fp-relative offset for the nth (0-based) argument.
112
+ def arg_offset n
113
+ (n - @NREGISTER_ARGS) * @WORDSIZE
112
114
  end
113
-
114
- # Returns an sp-relative reference for the nth (0-based) argument.
115
+
116
+ # Returns an fp-relative reference for the nth (0-based) argument.
115
117
  def arg_reference n
116
- "[sp, \##{@frame_size + (n - @NREGISTER_ARGS) * @WORDSIZE}]"
118
+ offset_reference arg_offset(n)
117
119
  end
118
120
 
119
121
  # Return the register in which the nth (0-based) argument is stored, or
120
122
  # nil if not stored in a register
121
123
  def arg_register n
122
- # The first @NREGISTER_ARGS arguments are in the v registers,
123
- # the rest are on the stack.
124
- if register_arg? n
125
- "v#{n + 1}"
124
+ if n < @NREGISTER_ARGS
125
+ # We copied the argument to one of the local registers.
126
+ @LOCAL_REGISTERS[n]
126
127
  else
127
128
  nil
128
129
  end
129
130
  end
130
131
 
131
- # Test if op is a binary operation
132
- def assymetric_binop? op
133
- [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
132
+ def auto_bytes value, register
133
+ if value.kind_of? Integer
134
+ grow_frame value
135
+ else
136
+ temporary = register == :sp ? @TEMPORARIES[0] : register
137
+ load_value_into_register value, temporary
138
+ auto_bytes_register temporary
139
+ end
140
+ emit "cpy #{register}, sp\n" unless register == :sp
134
141
  end
135
-
136
- # Test if a value is an at-expression
137
- def at_expr? value
138
- value.respond_to?(:[]) && value[0] == :'@'
142
+
143
+ # auto-bytes where the value is supplied in a register and the return
144
+ # value will be in sp. register must not be sp.
145
+ def auto_bytes_register register
146
+ temporary = register == @TEMPORARIES[0] ? :r3 : @TEMPORARIES[0]
147
+ emit "add #{register}, #{register}, \##{@STACK_ALIGNMENT - 1}\n"
148
+ emit "mvn #{temporary}, \##{@STACK_ALIGNMENT - 1}\n"
149
+ emit "and #{register}, #{register}, #{temporary}\n"
150
+ emit "sub sp, #{register}\n"
139
151
  end
140
-
152
+
153
+ def auto_words value, register
154
+ if value.kind_of? Integer
155
+ auto_bytes(value * @WORDSIZE, register)
156
+ else
157
+ raise "Can't use :sp as a register for auto_words" if register == :sp
158
+ load_value_into_register value, register
159
+ emit "lsl #{register}, #{register}, \##{@WORDSIZE_BITS}\n"
160
+ auto_bytes_register register
161
+ emit "cpy #{register}, sp\n"
162
+ end
163
+ end
164
+
141
165
  # Begins a new block.
142
166
  def begin_block *code
143
- emit "# begin block\n"
144
167
  # If we are starting a block at top level, create a frame
145
168
  if @environment == @top_level
146
169
  nlocals = count_locals code
@@ -156,18 +179,18 @@ module Voodoo
156
179
  end
157
180
 
158
181
  @function_end_label = gensym
159
- emit "# function #{formals.join ' '}\n"
160
182
  environment = Environment.new @environment
161
- environment.add_args formals
183
+ formals.each_with_index do |formal, i|
184
+ if i < @NREGISTER_ARGS
185
+ environment.add_arg formal, arg_register(i)
186
+ else
187
+ environment.add_arg formal, arg_offset(i)
188
+ end
189
+ end
162
190
  @environment = environment
163
191
  emit_function_prologue formals, nlocals
164
192
  end
165
193
 
166
- # Test if op is a binary operation
167
- def binop? op
168
- assymetric_binop?(op) || symmetric_binop?(op)
169
- end
170
-
171
194
  # Define a byte with the given value
172
195
  def byte value
173
196
  emit ".byte #{value}\n"
@@ -175,8 +198,6 @@ module Voodoo
175
198
 
176
199
  # Call a function.
177
200
  def call func, *args
178
- emit "# call #{func} #{args.join ' '}\n"
179
-
180
201
  # Calculate how many arguments need to be pushed on
181
202
  # the stack, and allocate space for them.
182
203
  nstack_args = number_of_stack_arguments args.length
@@ -186,23 +207,27 @@ module Voodoo
186
207
 
187
208
  # Put stack arguments on the stack
188
209
  (@NREGISTER_ARGS...args.length).each do |n|
189
- load_value_into_register args[n], @TEMPORARY
190
- emit "str #{@TEMPORARY}, " +
191
- "[sp , \##{(n - @NREGISTER_ARGS) * @WORDSIZE}]\n"
210
+ with_temporary do |temporary|
211
+ load_value_into_register args[n], temporary
212
+ emit "str #{temporary}, " +
213
+ "[sp , \##{(n - @NREGISTER_ARGS) * @WORDSIZE}]\n"
214
+ end
192
215
  end
193
216
 
194
217
  # Put register arguments in the right registers
195
218
  nregister_args = number_of_register_arguments args.length
196
219
  nregister_args.times do |n|
197
- load_value_into_register args[n], :"a#{n + 1}"
220
+ load_value_into_register args[n], :"r#{n}"
198
221
  end
199
222
 
200
223
  # Call function
201
224
  if global? func
202
225
  emit "bl #{func}\n"
203
226
  else
204
- func_reg = load_value func
205
- emit "blx #{func_reg}\n"
227
+ with_temporary do |temporary|
228
+ func_reg = load_value func, temporary
229
+ emit "blx #{func_reg}\n"
230
+ end
206
231
  end
207
232
 
208
233
  # Restore original stack frame
@@ -224,18 +249,17 @@ module Voodoo
224
249
  # Save the registers we will clobber to the stack.
225
250
  clobbered = []
226
251
  nregister_vars.times do |i|
227
- clobbered << :"v#{i < 5 ? i + 1 : i + 2}"
252
+ clobbered << @LOCAL_REGISTERS[i]
228
253
  end
254
+ clobbered << @FP
229
255
  clobbered << :lr if save_lr
230
256
  @saved_registers = clobbered
231
257
  emit "stmfd sp!, {#{clobbered.join ', '}}\n"
258
+ emit "add #{@FP}, sp, \##{clobbered.length * @WORDSIZE}\n"
232
259
 
233
260
  # Calculate frame size so that the stack pointer will
234
261
  # be properly aligned at the end of emit_function_prologue.
235
- @frame_size = (clobbered.length + nstack_vars) * @WORDSIZE
236
- if @frame_size % 8 != 0
237
- @frame_size = (@frame_size + 7) / 8 * 8
238
- end
262
+ @frame_size = stack_align((clobbered.length + nstack_vars) * @WORDSIZE)
239
263
  extra_space = @frame_size - clobbered.length * @WORDSIZE
240
264
  if extra_space > 0
241
265
  emit "sub sp, sp, \##{extra_space}\n"
@@ -243,35 +267,26 @@ module Voodoo
243
267
  @frame_offset = 0
244
268
  end
245
269
 
270
+ # Emits a comment.
271
+ def comment text
272
+ emit "# #{text}\n"
273
+ end
274
+
246
275
  # Start a conditional using the specified branch instruction
247
276
  # after the comparison.
248
277
  def common_if comp, x, y = nil
249
- emit "# #{comp} #{x} #{y}\n"
250
-
251
- xreg = load_value x, @TEMPORARY
252
- yreg = load_value y, :a4
278
+ with_temporary do |temporary|
279
+ xreg = load_value x, temporary
280
+ yreg = load_value y, :a4
253
281
 
254
- falselabel = @environment.gensym
255
- @if_labels.push falselabel
256
-
257
- emit "cmp #{xreg}, #{yreg}\n"
258
-
259
- lut = { :ifeq => "bne", :ifge => "blt", :ifgt => "ble",
260
- :ifle => "bgt", :iflt => "bge", :ifne => "beq" }
261
- emit "#{lut[comp]} #{falselabel}\n"
262
- end
282
+ falselabel = @environment.gensym
283
+ @if_labels.push falselabel
263
284
 
264
- # Counts the number of local variables created in
265
- # a sequence of statements.
266
- def count_locals statements
267
- count = 0
268
- each_statement(statements) do |statement|
269
- if statement[0] == :let
270
- # let introduces a single local
271
- count = count + 1
272
- end
273
- end
274
- count
285
+ emit "cmp #{xreg}, #{yreg}\n"
286
+ lut = { :ifeq => "bne", :ifge => "blt", :ifgt => "ble",
287
+ :ifle => "bgt", :iflt => "bge", :ifne => "beq" }
288
+ emit "#{lut[comp]} #{falselabel}\n"
289
+ end
275
290
  end
276
291
 
277
292
  # Destroys the current stack frame.
@@ -279,10 +294,7 @@ module Voodoo
279
294
  def destroy_frame ret = false
280
295
  # Set sp back to where saved registers were stored
281
296
  saved = @saved_registers
282
- offset = @frame_size - saved.length * @WORDSIZE
283
- if offset != 0
284
- emit "add sp, sp, \##{offset}\n"
285
- end
297
+ emit "sub sp, #{@FP}, \##{saved.length * @WORDSIZE}\n"
286
298
 
287
299
  if ret
288
300
  index = saved.index :lr
@@ -293,10 +305,15 @@ module Voodoo
293
305
  end
294
306
  end
295
307
  emit "ldmfd sp!, {#{saved.join ', '}}\n"
296
-
308
+
297
309
  emit_constants if ret
298
310
  end
299
311
 
312
+ # Aligns on the next multiple of +n+ bytes.
313
+ def emit_align n
314
+ emit ".align #{n}\n"
315
+ end
316
+
300
317
  # Writes any constants that need to be written to the instruction
301
318
  # stream, and clears the list of constants that need to be written.
302
319
  def emit_constants
@@ -312,7 +329,7 @@ module Voodoo
312
329
  # Calculate the number of arguments we were passed in
313
330
  # registers, the total number of values we need to save
314
331
  # on the stack, then create a stack frame and save
315
- # the v registers we will be using.
332
+ # the registers we will be using.
316
333
  nregister_args = [formals.length, @NREGISTER_ARGS].min
317
334
  nvars = nregister_args + nlocals
318
335
  create_frame nvars, true
@@ -320,14 +337,44 @@ module Voodoo
320
337
  # Move arguments that were passed in registers into
321
338
  # callee-save registers.
322
339
  nregister_args.times do |i|
323
- emit "cpy v#{i + 1}, a#{i + 1}\n"
340
+ emit "cpy #{@LOCAL_REGISTERS[i]}, r#{i}\n"
341
+ end
342
+ end
343
+
344
+ # Emits a label type annotation.
345
+ def emit_label_type name, type
346
+ type_map = {
347
+ :code => "%function",
348
+ :data => "%object",
349
+ }
350
+ emit ".type #{name}, #{type_map[type]}\n"
351
+ end
352
+
353
+ # Emits a label size annotation.
354
+ def emit_label_size name
355
+ emit ".size #{name}, .-#{name}\n"
356
+ end
357
+
358
+ # Loads a word into a register.
359
+ def emit_load_word register, base, offset
360
+ if offset == 0
361
+ emit "ldr #{register}, [#{base}]\n"
362
+ else
363
+ emit "ldr #{register}, [#{base}, \##{offset * @WORDSIZE}]\n"
364
+ end
365
+ end
366
+
367
+ # Stores the value of a register in memory.
368
+ def emit_store_word register, base, offset
369
+ if offset == 0
370
+ emit "str #{register}, [#{base}]\n"
371
+ else
372
+ emit "str #{register}, [#{base}, \##{offset * @WORDSIZE}]\n"
324
373
  end
325
374
  end
326
375
 
327
376
  # Ends the current block.
328
377
  def end_block
329
- emit "# end block\n"
330
-
331
378
  # If we are returning to top level, restore stack pointer
332
379
  # and saved registers.
333
380
  if @environment.parent == @top_level
@@ -358,14 +405,12 @@ module Voodoo
358
405
  raise "Cannot end function when not in a function"
359
406
  end
360
407
 
361
- emit "# function epilogue\n"
362
408
  label @function_end_label
363
409
 
364
410
  destroy_frame true
365
411
  @frame_size = 0
366
412
  @frame_offset = 0
367
413
  @saved_registers = []
368
- emit "# end function\n\n"
369
414
  @environment = @top_level
370
415
  end
371
416
 
@@ -394,25 +439,55 @@ module Voodoo
394
439
  return
395
440
  end
396
441
 
397
- x = load_value expr[1], :a4
398
- y = load_value expr[2], @TEMPORARY
442
+ with_temporaries(2) do |t1,t2|
443
+ x = load_value expr[1], t1
399
444
 
400
- case op
401
- when :bsr
402
- emit "lsr #{register}, #{x}, #{y}\n"
403
- when :or
404
- emit "orr #{register}, #{x}, #{y}\n"
405
- when :rol
406
- emit "rsb #{y}, #{y}, #32\n"
407
- emit "ror #{register}, #{x}, #{y}\n"
408
- when :shl
409
- emit "lsl #{register}, #{x}, #{y}\n"
410
- when :shr
411
- emit "lsr #{register}, #{x}, #{y}\n"
412
- when :xor
413
- emit "eor #{register}, #{x}, #{y}\n"
414
- else
415
- emit "#{expr[0]} #{register}, #{x}, #{y}\n"
445
+ case op
446
+ when :mul
447
+ # Operand must be in a register for these ops.
448
+ y = load_value expr[2], t2
449
+ else
450
+ y = value_ref expr[2], t2
451
+ end
452
+
453
+ case op
454
+ when :bsr
455
+ emit "lsr #{register}, #{x}, #{y}\n"
456
+ when :or
457
+ emit "orr #{register}, #{x}, #{y}\n"
458
+ when :mul
459
+ # Can't store result in same register as first operand.
460
+ if register == x
461
+ if x == y
462
+ # All using the same register. Move x to a different
463
+ # register to make this work.
464
+ temp = (x == t1) ? t2 : t1
465
+ emit "cpy #{temp}, #{x}\n"
466
+ emit "mul #{register}, #{temp}, #{y}\n"
467
+ else
468
+ # Multiplication is commutative. Just swap x and y.
469
+ emit "mul #{register}, #{y}, #{x}\n"
470
+ end
471
+ else
472
+ # Common case, register and x are different.
473
+ emit "mul #{register}, #{x}, #{y}\n"
474
+ end
475
+ when :rol
476
+ if integer? expr[2]
477
+ y = "\##{32 - expr[2]}"
478
+ else
479
+ emit "rsb #{y}, #{y}, #32\n"
480
+ end
481
+ emit "ror #{register}, #{x}, #{y}\n"
482
+ when :shl
483
+ emit "lsl #{register}, #{x}, #{y}\n"
484
+ when :shr
485
+ emit "lsr #{register}, #{x}, #{y}\n"
486
+ when :xor
487
+ emit "eor #{register}, #{x}, #{y}\n"
488
+ else
489
+ emit "#{expr[0]} #{register}, #{x}, #{y}\n"
490
+ end
416
491
  end
417
492
  end
418
493
 
@@ -425,6 +500,10 @@ module Voodoo
425
500
  # Evaluate expression
426
501
  op = expr[0]
427
502
  case op
503
+ when :'auto-bytes'
504
+ auto_bytes expr[1], register
505
+ when :'auto-words'
506
+ auto_words expr[1], register
428
507
  when :call
429
508
  call *expr[1..-1]
430
509
  emit "cpy #{register}, #{@RETURN}\n" if register != @RETURN
@@ -434,8 +513,10 @@ module Voodoo
434
513
  get_word expr[1], expr[2], register
435
514
  when :not
436
515
  load_value_into_register expr[1], register
437
- emit "mvn #{@TEMPORARY}, #0\n"
438
- emit "eor #{register}, #{register}, #{@TEMPORARY}\n"
516
+ with_temporary do |temporary|
517
+ emit "mvn #{temporary}, #0\n"
518
+ emit "eor #{register}, #{register}, #{temporary}\n"
519
+ end
439
520
  else
440
521
  if binop? op
441
522
  eval_binop expr, register
@@ -451,14 +532,6 @@ module Voodoo
451
532
  symbols.each { |sym| emit ".globl #{sym}\n" }
452
533
  end
453
534
 
454
- # Add a function to the current section
455
- def function formals, *code
456
- nlocals = count_locals code
457
- begin_function formals, nlocals
458
- code.each { |action| add section, action }
459
- end_function
460
- end
461
-
462
535
  # Load byte from _base_ + _offset_ into _register_
463
536
  def get_byte base, offset, register
464
537
  # If base is an integer, but offset isn't, swap them
@@ -467,43 +540,53 @@ module Voodoo
467
540
  end
468
541
 
469
542
  if integer? offset
470
- base_reg = load_value base
471
- if offset == 0
472
- emit "ldrb #{register}, [#{base_reg}]\n"
473
- else
474
- emit "ldrb #{register}, [#{base_reg}, \##{offset}]\n"
543
+ with_temporary do |temporary|
544
+ base_reg = load_value base, temporary
545
+ if offset == 0
546
+ emit "ldrb #{register}, [#{base_reg}]\n"
547
+ else
548
+ emit "ldrb #{register}, [#{base_reg}, \##{offset}]\n"
549
+ end
475
550
  end
476
551
  else
477
- base_reg = load_value base
478
- offset_reg = load_value offset, :a4
479
- emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
552
+ with_temporaries(2) do |t1,t2|
553
+ base_reg = load_value base, t1
554
+ offset_reg = load_value offset, t2
555
+ emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
556
+ end
480
557
  end
481
558
  end
482
559
 
483
560
  # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
484
561
  def get_word base, offset, register
485
562
  if integer? offset
486
- base_reg = load_value base
487
- if offset == 0
488
- emit "ldr #{register}, [#{base_reg}]\n"
489
- else
490
- emit "ldr #{register}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
563
+ with_temporary do |temporary|
564
+ base_reg = load_value base, temporary
565
+ if offset == 0
566
+ emit "ldr #{register}, [#{base_reg}]\n"
567
+ else
568
+ emit "ldr #{register}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
569
+ end
491
570
  end
492
571
  else
493
- base_reg = load_value base
494
- offset_reg = load_value offset, :a4
495
- emit "ldr #{register}, [#{base_reg}, #{offset_reg}, LSL #2]\n"
572
+ with_temporaries(2) do |t1,t2|
573
+ base_reg = load_value base, t1
574
+ offset_reg = load_value offset, t2
575
+ emit "ldr #{register}, [#{base_reg}, #{offset_reg}, LSL #2]\n"
576
+ end
496
577
  end
497
578
  end
498
579
 
499
- # Test if a symbol refers to a global
500
- def global? symbol
501
- symbol?(symbol) && @environment[symbol] == nil
502
- end
503
-
504
580
  # Jump to a label.
505
581
  def goto label
506
- emit "b #{label}\n"
582
+ if global? label
583
+ emit "b #{label}\n"
584
+ else
585
+ with_temporary do |temporary|
586
+ register = load_value label, temporary
587
+ emit "cpy pc, #{register}\n"
588
+ end
589
+ end
507
590
 
508
591
  # If we have constants that need to be emitted, do so now
509
592
  emit_constants
@@ -512,7 +595,7 @@ module Voodoo
512
595
  # Grows the current frame by n words, plus padding to
513
596
  # respect alignment rules.
514
597
  def grow_frame nwords
515
- increment = (nwords * @WORDSIZE + 7) / 8 * 8
598
+ increment = stack_align(nwords * @WORDSIZE)
516
599
  emit "sub sp, sp, \##{increment}\n"
517
600
  @frame_size = @frame_size + increment
518
601
  @frame_offset = @frame_offset + increment
@@ -520,7 +603,6 @@ module Voodoo
520
603
 
521
604
  # Start the false path of a conditional.
522
605
  def ifelse
523
- emit "# else\n"
524
606
  newlabel = @environment.gensym
525
607
  goto newlabel
526
608
  lbl = @if_labels.pop
@@ -564,140 +646,69 @@ module Voodoo
564
646
  symbols.each { |sym| @imports[sym] = sym }
565
647
  end
566
648
 
567
- # Test if a value is an integer
568
- def integer? value
569
- value.kind_of? Integer
570
- end
571
-
572
- # Emit a label
573
- def label name
574
- emit "#{name}:\n"
575
- end
576
-
577
649
  # Introduce a new local variable
578
650
  def let symbol, *expr
579
- emit "# let #{symbol} #{expr.join ' '}\n"
580
651
  n = @environment.locals
581
- @environment.add_local symbol
582
-
583
652
  register = local_register n
653
+
584
654
  if register
585
655
  # We will use a register to store the value
656
+ @environment.add_local symbol, register
586
657
  eval_expr expr, register
587
658
  else
588
659
  # We will use the stack to store the value
589
- ref = local_reference n
590
- eval_expr expr, @TEMPORARY
591
- emit "str #{@TEMPORARY}, #{ref}\n"
660
+ offset = local_offset n
661
+ @environment.add_local symbol, offset
662
+ with_temporary do |temporary|
663
+ eval_expr expr, temporary
664
+ emit "str #{temporary}, #{offset_reference offset}\n"
665
+ end
592
666
  end
593
667
  end
594
668
 
595
- # Load the value at the given address.
596
- def load_at address, register = @TEMPORARY
669
+ # Loads the value at the given address.
670
+ def load_at address, register
597
671
  load_value_into_register address, register
598
672
  emit "ldr #{register}, [#{register}]\n"
599
673
  register
600
674
  end
601
675
 
602
- # Load a value into a register.
603
- # Returns the name of the register.
604
- # If the value was already in a register, the name of that
605
- # register is returned.
606
- # Else, the value is loaded into a register and the name of
607
- # that register is returned. The register to use in that case
608
- # may be specified using the optional second argument.
609
- def load_value x, register = @TEMPORARY
610
- if integer? x
611
- if x >= 0 && x <= 255
612
- emit "mov #{register}, \##{x}\n"
613
- return register
614
- elsif x >= -255 && x < 0
615
- emit "mvn #{register}, \##{-(x + 1)}\n"
616
- return register
617
- else
618
- lbl = add_constant x
619
- emit "ldr #{register}, #{lbl}\n"
620
- return register
621
- end
622
- elsif symbol? x
623
- binding = @environment[x]
624
- if binding
625
- case binding[0]
626
- when :arg
627
- n = binding[1]
628
- if register_arg? n
629
- return arg_register(n)
630
- else
631
- emit "ldr #{register}, #{arg_reference binding[1]}\n"
632
- return register
633
- end
634
- when :local
635
- n = binding[1]
636
- if register_local? n
637
- return local_register(n)
638
- else
639
- emit "ldr #{register}, #{local_reference n}\n"
640
- return register
641
- end
642
- else
643
- raise "Don't know how to load #{x.inspect}"
644
- end
645
- else
646
- # Assume global
647
- lbl = add_constant x
648
- emit "ldr #{register}, #{lbl}\n"
649
- return register
650
- end
651
- elsif at_expr? x
652
- load_at x[1], register
676
+ # Loads a value into some register.
677
+ # If the value is already in a register, does nothing.
678
+ # Else, loads the value into the register given as the
679
+ # second argument.
680
+ # Returns the name of the register the value is in.
681
+ def load_value x, register
682
+ ref = value_ref x, register
683
+ if register? ref
684
+ ref
653
685
  else
654
- raise "Don't know how to load #{x.inspect}"
686
+ emit "mov #{register}, #{ref}\n"
687
+ register
655
688
  end
656
689
  end
657
690
 
658
- # Load a value into a specific register
691
+ # Loads a value into a specific register.
659
692
  def load_value_into_register x, register
660
- reg = load_value x, register
661
- if reg != register
662
- emit "cpy #{register}, #{reg}\n"
663
- end
664
- end
665
-
666
- # Returns an sp-relative reference for the nth (0-based) local.
667
- def local_reference n
668
- "[sp, \##{@frame_offset + (number_of_register_arguments + n) * @WORDSIZE}]"
669
- end
670
-
671
- # Return the register in which the nth local (0-based) is stored, or
672
- # nil if not stored in a register
673
- def local_register n
674
- if register_local? n
675
- n = n + number_of_register_arguments
676
- if n < 5
677
- "v#{n + 1}"
693
+ ref = value_ref x, register
694
+ if ref != register
695
+ if register? ref
696
+ emit "cpy #{register}, #{ref}\n"
678
697
  else
679
- "v#{n + 2}"
698
+ emit "mov #{register}, #{ref}\n"
680
699
  end
681
- else
682
- nil
683
700
  end
701
+ register
684
702
  end
685
703
 
686
- # Calculate the number of register arguments,
687
- # given the total number of arguments.
688
- def number_of_register_arguments n = @environment.args
689
- [n, @NREGISTER_ARGS].min
690
- end
691
-
692
- # Calculate the number of stack arguments,
693
- # given the total number of arguments.
694
- def number_of_stack_arguments n = @environment.args
695
- [0, n - @NREGISTER_ARGS].max
704
+ # Returns the fp-relative reference for the nth (0-based) local.
705
+ def local_offset n
706
+ -@INITIAL_FRAME_SIZE - ((n + number_of_register_arguments) * @WORDSIZE)
696
707
  end
697
-
698
- # Returns true if the nth (0-based) argument is stored in a register
699
- def register_arg? n
700
- n < @NREGISTER_ARGS
708
+
709
+ # Given an offset, returns an fp-relative reference.
710
+ def offset_reference offset
711
+ "[#{@FP}, \##{offset}]"
701
712
  end
702
713
 
703
714
  # Returns true if the nth (0-based) local is stored in a register
@@ -710,50 +721,37 @@ module Voodoo
710
721
  # _words_ may contain an expression to be evaluated. The result
711
722
  # of the evaluation is returned from the function.
712
723
  def ret *words
713
- emit "# return #{words.join ' '}\n"
714
724
  # Compute return value and store it in @RETURN
715
725
  eval_expr(words, @RETURN) unless words.empty?
716
726
  # Go to epilogue
717
727
  goto @function_end_label
718
728
  end
719
-
729
+
720
730
  # Set a variable to the result of evaluating an expression
721
731
  def set symbol, *expr
722
- emit "# set #{symbol} #{expr.join ' '}\n"
723
-
724
- x = @environment[symbol]
725
- if x == nil
726
- raise "Cannot change value of constant #{symbol}"
727
- end
728
-
729
- register = nil
730
- case x[0]
731
- when :arg
732
- register = arg_register x[1]
733
- when :local
734
- register = local_register x[1]
735
- end
736
-
737
- if register
738
- # Set new value
739
- eval_expr expr, register
740
- else
741
- case x[0]
742
- when :local
743
- ref = local_reference x[1]
744
- when :arg
745
- ref = arg_reference x[1]
746
- else
747
- raise "??? #{sym} is neither a local nor an argument"
732
+ if at_expr? symbol
733
+ with_temporaries(2) do |t1,t2|
734
+ eval_expr expr, t1
735
+ register = load_value symbol[1], t2
736
+ emit "str #{t1}, [#{register}]\n"
748
737
  end
749
- eval_expr expr, @TEMPORARY
750
- emit "str #{@TEMPORARY}, #{ref}\n"
738
+ else
739
+ x = @environment[symbol]
740
+ if x == nil
741
+ raise "Cannot change value of constant #{symbol}"
742
+ elsif x.kind_of? Symbol
743
+ eval_expr expr, x
744
+ else
745
+ with_temporary do |temporary|
746
+ eval_expr expr, temporary
747
+ emit "str #{temporary}, #{offset_reference x}\n"
748
+ end
749
+ end
751
750
  end
752
751
  end
753
752
 
754
753
  # Set the byte at _base_ + _offset_ to _value_
755
754
  def set_byte base, offset, value
756
- emit "# set-byte #{base} #{offset} #{value}\n"
757
755
  # If base is an integer, but offset isn't, swap them
758
756
  if !integer?(offset) && integer?(base)
759
757
  base, offset = [offset, base]
@@ -761,22 +759,25 @@ module Voodoo
761
759
 
762
760
  if integer? offset
763
761
  base_reg = load_value base, :a4
764
- load_value_into_register value, @TEMPORARY
765
- if offset == 0
766
- emit "strb #{@TEMPORARY}, [#{base_reg}]\n"
767
- else
768
- emit "strb #{@TEMPORARY}, [#{base_reg}, \##{offset}]\n"
762
+ with_temporary do |temporary|
763
+ load_value_into_register value, temporary
764
+ if offset == 0
765
+ emit "strb #{temporary}, [#{base_reg}]\n"
766
+ else
767
+ emit "strb #{temporary}, [#{base_reg}, \##{offset}]\n"
768
+ end
769
769
  end
770
770
  else
771
771
  eval_binop [:add, base, offset], :a4
772
- load_value_into_register value, @TEMPORARY
773
- emit "strb #{@TEMPORARY}, [a4]\n"
772
+ with_temporary do |temporary|
773
+ load_value_into_register value, temporary
774
+ emit "strb #{temporary}, [a4]\n"
775
+ end
774
776
  end
775
777
  end
776
778
 
777
779
  # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
778
780
  def set_word base, offset, value
779
- emit "# set-word #{base} #{offset} #{value}\n"
780
781
  # If base is an integer, but offset isn't, swap them
781
782
  if !integer?(offset) && integer?(base)
782
783
  base, offset = [offset, base]
@@ -784,18 +785,22 @@ module Voodoo
784
785
 
785
786
  if integer? offset
786
787
  base_reg = load_value base, :a4
787
- load_value_into_register value, @TEMPORARY
788
- if offset == 0
789
- emit "str #{@TEMPORARY}, [#{base_reg}]\n"
790
- else
791
- emit "str #{@TEMPORARY}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
788
+ with_temporary do |temporary|
789
+ load_value_into_register value, temporary
790
+ if offset == 0
791
+ emit "str #{temporary}, [#{base_reg}]\n"
792
+ else
793
+ emit "str #{temporary}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
794
+ end
792
795
  end
793
796
  else
794
797
  load_value_into_register base, :a4
795
- load_value_into_register offset, @TEMPORARY
796
- emit "add a4, a4, #{@TEMPORARY}, LSL #2\n"
797
- load_value_into_register value, @TEMPORARY
798
- emit "str #{@TEMPORARY}, [a4]\n"
798
+ with_temporary do |temporary|
799
+ load_value_into_register offset, temporary
800
+ emit "add a4, a4, #{temporary}, LSL #2\n"
801
+ load_value_into_register value, temporary
802
+ emit "str #{temporary}, [a4]\n"
803
+ end
799
804
  end
800
805
  end
801
806
 
@@ -814,21 +819,8 @@ module Voodoo
814
819
  emit ".ascii \"#{code}\"\n"
815
820
  end
816
821
 
817
- # Test if a value is a symbol
818
- def symbol? value
819
- value.kind_of? Symbol
820
- end
821
-
822
- # Test if op is a symmetric binary operation (i.e. it will yield the
823
- # same result if the order of its source operands is changed).
824
- def symmetric_binop? op
825
- [:add, :and, :mul, :or, :xor].member? op
826
- end
827
-
828
822
  # Call a function, re-using the current call frame if possible.
829
823
  def tail_call func, *args
830
- emit "# tail-call #{func} #{args.join ' '}\n"
831
-
832
824
  # Compute number of stack arguments
833
825
  nstackargs = number_of_stack_arguments args.length
834
826
  # If we need more stack arguments than we have now,
@@ -883,41 +875,82 @@ module Voodoo
883
875
  saved = overwritten[sym]
884
876
  if saved
885
877
  # Saved value, load from stack
886
- reg = @TEMPORARY
887
- emit "ldr #{reg}, [sp, \##{saved}]\n"
878
+ with_temporary do |temporary|
879
+ emit "ldr #{temporary}, [sp, \##{saved}]\n"
880
+ emit "str #{temporary}, #{arg_reference i}\n"
881
+ end
888
882
  else
889
883
  # Regular value, use load_value
890
- reg = load_value arg
884
+ with_temporary do |temporary|
885
+ reg = load_value arg, temporary
886
+ emit "str #{reg}, #{arg_reference i}\n"
887
+ end
891
888
  end
892
- emit "str #{reg}, #{arg_reference i}\n"
893
889
  end
894
890
  end
895
891
 
896
- # Load address of function to be called into @TEMPORARY
897
- load_value_into_register func, @TEMPORARY
892
+ with_temporary do |temporary|
893
+ # Load address of function to be called
894
+ load_value_into_register func, temporary
898
895
 
899
- # Destroy current activation frame and enter func
900
- destroy_frame false
901
- emit "bx #{@TEMPORARY}\n"
896
+ # Destroy current activation frame and enter func
897
+ destroy_frame false
898
+ emit "bx #{temporary}\n"
899
+ end
902
900
  emit_constants
903
901
  end
904
902
 
903
+ # Returns a reference to a value.
904
+ # For immediate values that fit in 8 bits, this returns the
905
+ # value itself (in ARM syntax).
906
+ # For all other values, loads the value into a register and
907
+ # returns the name of the register. If the value is already
908
+ # in a register, the name of that register is returned. Else,
909
+ # the value is loaded into the register specified as the
910
+ # second argument.
911
+ def value_ref x, register
912
+ if substitution? x
913
+ x = substitute_number x[1]
914
+ end
915
+
916
+ if integer? x
917
+ if x >= 0 && x <= 255
918
+ return "\##{x}"
919
+ elsif x >= -255 && x < 0
920
+ emit "mvn #{register}, \##{-(x + 1)}\n"
921
+ return register
922
+ else
923
+ lbl = add_constant x
924
+ emit "ldr #{register}, #{lbl}\n"
925
+ return register
926
+ end
927
+ elsif symbol? x
928
+ binding = @environment[x]
929
+ if binding.kind_of? Symbol
930
+ # Value is already in a register. Return register name.
931
+ return binding
932
+ elsif binding.kind_of? Integer
933
+ # Value is on the stack. Load from the stack.
934
+ emit "ldr #{register}, #{offset_reference binding}\n"
935
+ return register
936
+ else
937
+ # Assume global
938
+ lbl = add_constant x
939
+ emit "ldr #{register}, #{lbl}\n"
940
+ return register
941
+ end
942
+ elsif at_expr? x
943
+ load_at x[1], register
944
+ else
945
+ raise "Don't know how to load #{x.inspect}"
946
+ end
947
+ end
948
+
905
949
  # Define a word with the given value
906
950
  def word value
907
951
  emit ".int #{value}\n"
908
952
  end
909
953
 
910
- # Write generated code to the given IO object.
911
- def write io
912
- @sections.each do |section,code|
913
- unless code.empty?
914
- io.puts ".section #{section.to_s}"
915
- io.puts code
916
- io.puts
917
- end
918
- end
919
- end
920
-
921
954
  end
922
955
 
923
956
  # Register class for little endian ARM