voodoo 0.7.0 → 1.0.0

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.
@@ -0,0 +1,927 @@
1
+ require 'voodoo/generators/common_code_generator'
2
+
3
+ module Voodoo
4
+ # = ARM GNU Assembler Code Generator
5
+ #
6
+ # The ARM code generator generates assembly code for use with
7
+ # the GNU assembler.
8
+ #
9
+ # == Calling Convention
10
+ #
11
+ # The first four arguments are passed in the registers r0 through r3.
12
+ # Any additional arguments are passed on the stack, starting at
13
+ # r13. r13 will always be a multiple of 8.
14
+ #
15
+ # The return address for the called function is passed in r14.
16
+ #
17
+ # The called function will store its return value in r0.
18
+ #
19
+ # The called function is required to preserve the values of registers
20
+ # r4 through r11 and register r13.
21
+ #
22
+ # This calling convention is compatible with the Procedure Call
23
+ # Standard for the ARM Architecture (AAPCS).
24
+ #
25
+ # == Call Frames
26
+ #
27
+ # Call frames have the following layout:
28
+ #
29
+ # When a function is called, it receives a stack frame that looks like
30
+ # the following:
31
+ #
32
+ # :
33
+ # old frame
34
+ # padding
35
+ # argn
36
+ # :
37
+ # arg4 <-- r13 points here
38
+ #
39
+ # The function prologue of functions generated by this code generator
40
+ # creates activation frames that look as follows:
41
+ #
42
+ # :
43
+ # old frame
44
+ # padding
45
+ # argn
46
+ # :
47
+ # arg4
48
+ # saved r14
49
+ # saved r11
50
+ # :
51
+ # saved r4 <-- r13 points here
52
+ #
53
+ # == Register Usage
54
+ #
55
+ # Inside a function, registers r4..r8, r10, and r11 are used for
56
+ # local variables and function arguments.
57
+ #
58
+ # r12 is used as a temporary, and r3 is used when another temporary
59
+ # is needed.
60
+ #
61
+ class ARMGasGenerator < CommonCodeGenerator
62
+ def initialize params
63
+ @WORDSIZE = 4
64
+ @CODE_ALIGNMENT = 4
65
+ @DATA_ALIGNMENT = @WORDSIZE
66
+ @FUNCTION_ALIGNMENT = @WORDSIZE
67
+
68
+ @NREGISTER_ARGS = 4
69
+ @NREGISTER_LOCALS = 7
70
+ @RETURN = :r0
71
+ @TEMPORARY = :r12
72
+ @constants = []
73
+ @frame_offset = 0
74
+ @frame_size = 0
75
+ @function_end_label = nil
76
+ @imports = {}
77
+ @if_labels = []
78
+ @saved_registers = []
79
+ super params
80
+ @output_file_suffix = '.s'
81
+ @features.merge! \
82
+ :'bits-per-word' => '32',
83
+ :'byte-order' => 'little-endian',
84
+ :'bytes-per-word' => 4
85
+ end
86
+
87
+ # Create an entry in the constants table,
88
+ # returning the label that will refer to the constant.
89
+ # The value may be an integer or a label.
90
+ def add_constant value
91
+ label = gensym
92
+ @constants << [label, value]
93
+ label
94
+ end
95
+
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
112
+ end
113
+
114
+ # Returns an sp-relative reference for the nth (0-based) argument.
115
+ def arg_reference n
116
+ "[sp, \##{@frame_size + (n - @NREGISTER_ARGS) * @WORDSIZE}]"
117
+ end
118
+
119
+ # Return the register in which the nth (0-based) argument is stored, or
120
+ # nil if not stored in a register
121
+ 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}"
126
+ else
127
+ nil
128
+ end
129
+ end
130
+
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)
134
+ end
135
+
136
+ # Test if a value is an at-expression
137
+ def at_expr? value
138
+ value.respond_to?(:[]) && value[0] == :'@'
139
+ end
140
+
141
+ # Begins a new block.
142
+ def begin_block *code
143
+ emit "# begin block\n"
144
+ # If we are starting a block at top level, create a frame
145
+ if @environment == @top_level
146
+ nlocals = count_locals code
147
+ create_frame nlocals, false
148
+ end
149
+ @environment = Environment.new @environment
150
+ end
151
+
152
+ # Emit function prologue and declare _formals_ as function arguments
153
+ def begin_function formals, nlocals
154
+ if @environment != @top_level
155
+ raise "Can only begin a function at top level"
156
+ end
157
+
158
+ @function_end_label = gensym
159
+ emit "# function #{formals.join ' '}\n"
160
+ environment = Environment.new @environment
161
+ environment.add_args formals
162
+ @environment = environment
163
+ emit_function_prologue formals, nlocals
164
+ end
165
+
166
+ # Test if op is a binary operation
167
+ def binop? op
168
+ assymetric_binop?(op) || symmetric_binop?(op)
169
+ end
170
+
171
+ # Define a byte with the given value
172
+ def byte value
173
+ emit ".byte #{value}\n"
174
+ end
175
+
176
+ # Call a function.
177
+ def call func, *args
178
+ emit "# call #{func} #{args.join ' '}\n"
179
+
180
+ # Calculate how many arguments need to be pushed on
181
+ # the stack, and allocate space for them.
182
+ nstack_args = number_of_stack_arguments args.length
183
+ old_frame_offset = @frame_offset
184
+ old_frame_size = @frame_size
185
+ grow_frame nstack_args if nstack_args > 0
186
+
187
+ # Put stack arguments on the stack
188
+ (@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"
192
+ end
193
+
194
+ # Put register arguments in the right registers
195
+ nregister_args = number_of_register_arguments args.length
196
+ nregister_args.times do |n|
197
+ load_value_into_register args[n], :"a#{n + 1}"
198
+ end
199
+
200
+ # Call function
201
+ if global? func
202
+ emit "bl #{func}\n"
203
+ else
204
+ func_reg = load_value func
205
+ emit "blx #{func_reg}\n"
206
+ end
207
+
208
+ # Restore original stack frame
209
+ if old_frame_size != @frame_size
210
+ emit "add sp, sp, \##{@frame_size - old_frame_size}\n"
211
+ @frame_offset = old_frame_offset
212
+ @frame_size = old_frame_size
213
+ end
214
+ end
215
+
216
+ # Creates a stack frame for the given number of arguments
217
+ # and local variables.
218
+ def create_frame nvars, save_lr = true
219
+ # Calculate how many variables we will store in registers,
220
+ # and how many on the stack.
221
+ nregister_vars = [nvars, @NREGISTER_LOCALS].min
222
+ nstack_vars = nvars - nregister_vars
223
+
224
+ # Save the registers we will clobber to the stack.
225
+ clobbered = []
226
+ nregister_vars.times do |i|
227
+ clobbered << :"v#{i < 5 ? i + 1 : i + 2}"
228
+ end
229
+ clobbered << :lr if save_lr
230
+ @saved_registers = clobbered
231
+ emit "stmfd sp!, {#{clobbered.join ', '}}\n"
232
+
233
+ # Calculate frame size so that the stack pointer will
234
+ # 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
239
+ extra_space = @frame_size - clobbered.length * @WORDSIZE
240
+ if extra_space > 0
241
+ emit "sub sp, sp, \##{extra_space}\n"
242
+ end
243
+ @frame_offset = 0
244
+ end
245
+
246
+ # Start a conditional using the specified branch instruction
247
+ # after the comparison.
248
+ 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
253
+
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
263
+
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
275
+ end
276
+
277
+ # Destroys the current stack frame.
278
+ # If ret is true, loads the saved value of lr into pc.
279
+ def destroy_frame ret = false
280
+ # Set sp back to where saved registers were stored
281
+ saved = @saved_registers
282
+ offset = @frame_size - saved.length * @WORDSIZE
283
+ if offset != 0
284
+ emit "add sp, sp, \##{offset}\n"
285
+ end
286
+
287
+ if ret
288
+ index = saved.index :lr
289
+ if index
290
+ saved[index] = :pc
291
+ else
292
+ raise "Request to load saved lr into pc, but lr has not been saved"
293
+ end
294
+ end
295
+ emit "ldmfd sp!, {#{saved.join ', '}}\n"
296
+
297
+ emit_constants if ret
298
+ end
299
+
300
+ # Writes any constants that need to be written to the instruction
301
+ # stream, and clears the list of constants that need to be written.
302
+ def emit_constants
303
+ @constants.each do |x|
304
+ label x[0]
305
+ word x[1]
306
+ end
307
+ @constants = []
308
+ end
309
+
310
+ # Emit function prologue.
311
+ def emit_function_prologue formals = [], nlocals = 0
312
+ # Calculate the number of arguments we were passed in
313
+ # registers, the total number of values we need to save
314
+ # on the stack, then create a stack frame and save
315
+ # the v registers we will be using.
316
+ nregister_args = [formals.length, @NREGISTER_ARGS].min
317
+ nvars = nregister_args + nlocals
318
+ create_frame nvars, true
319
+
320
+ # Move arguments that were passed in registers into
321
+ # callee-save registers.
322
+ nregister_args.times do |i|
323
+ emit "cpy v#{i + 1}, a#{i + 1}\n"
324
+ end
325
+ end
326
+
327
+ # Ends the current block.
328
+ def end_block
329
+ emit "# end block\n"
330
+
331
+ # If we are returning to top level, restore stack pointer
332
+ # and saved registers.
333
+ if @environment.parent == @top_level
334
+ offset = @frame_size - @saved_registers.length * @WORDSIZE
335
+ if offset > 0
336
+ emit "add sp, sp, \##{offset}\n"
337
+ end
338
+ emit "ldmfd sp!, {#{@saved_registers.join ', '}}\n"
339
+ @frame_size = 0
340
+ @frame_offset = 0
341
+ @saved_registers = []
342
+
343
+ # If we need to emit constants, do so now
344
+ unless @constants.empty?
345
+ lbl = gensym
346
+ goto lbl
347
+ label lbl
348
+ end
349
+ end
350
+
351
+ # Restore old value of @environment
352
+ @environment = @environment.parent
353
+ end
354
+
355
+ # Ends a function body.
356
+ def end_function
357
+ if @environment == @top_level
358
+ raise "Cannot end function when not in a function"
359
+ end
360
+
361
+ emit "# function epilogue\n"
362
+ label @function_end_label
363
+
364
+ destroy_frame true
365
+ @frame_size = 0
366
+ @frame_offset = 0
367
+ @saved_registers = []
368
+ emit "# end function\n\n"
369
+ @environment = @top_level
370
+ end
371
+
372
+ # Ends a conditional.
373
+ def end_if
374
+ label @if_labels.pop
375
+ end
376
+
377
+ # Evaluate the binary operation expr and store the result in register
378
+ def eval_binop expr, register
379
+ op = expr[0]
380
+
381
+ # Emulation for div and mod, which ARM does not have instructions for
382
+ case op
383
+ when :div
384
+ func = :"__aeabi_idiv"
385
+ import func unless @imports.has_key? func
386
+ call func, expr[1], expr[2]
387
+ emit "cpy #{register}, r0\n" if register != :r0
388
+ return
389
+ when :mod
390
+ func = :"__aeabi_idivmod"
391
+ import func unless @imports.has_key? func
392
+ call func, expr[1], expr[2]
393
+ emit "cpy #{register}, r1\n" if register != :r1
394
+ return
395
+ end
396
+
397
+ x = load_value expr[1], :a4
398
+ y = load_value expr[2], @TEMPORARY
399
+
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"
416
+ end
417
+ end
418
+
419
+ # Evaluates the expression +expr+ and stores the result in +register+.
420
+ def eval_expr expr, register
421
+ if expr.length == 1
422
+ # Load value
423
+ load_value_into_register expr[0], register
424
+ else
425
+ # Evaluate expression
426
+ op = expr[0]
427
+ case op
428
+ when :call
429
+ call *expr[1..-1]
430
+ emit "cpy #{register}, #{@RETURN}\n" if register != @RETURN
431
+ when :'get-byte'
432
+ get_byte expr[1], expr[2], register
433
+ when :'get-word'
434
+ get_word expr[1], expr[2], register
435
+ when :not
436
+ load_value_into_register expr[1], register
437
+ emit "mvn #{@TEMPORARY}, #0\n"
438
+ emit "eor #{register}, #{register}, #{@TEMPORARY}\n"
439
+ else
440
+ if binop? op
441
+ eval_binop expr, register
442
+ else
443
+ raise "Not a magic word: #{op}"
444
+ end
445
+ end
446
+ end
447
+ end
448
+
449
+ # Export symbols from the current section
450
+ def export *symbols
451
+ symbols.each { |sym| emit ".globl #{sym}\n" }
452
+ end
453
+
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
+ # Load byte from _base_ + _offset_ into _register_
463
+ def get_byte base, offset, register
464
+ # If base is an integer, but offset isn't, swap them
465
+ if !integer?(offset) && integer?(base)
466
+ base, offset = [offset, base]
467
+ end
468
+
469
+ 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"
475
+ end
476
+ else
477
+ base_reg = load_value base
478
+ offset_reg = load_value offset, :a4
479
+ emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
480
+ end
481
+ end
482
+
483
+ # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
484
+ def get_word base, offset, register
485
+ 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"
491
+ end
492
+ 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"
496
+ end
497
+ end
498
+
499
+ # Test if a symbol refers to a global
500
+ def global? symbol
501
+ symbol?(symbol) && @environment[symbol] == nil
502
+ end
503
+
504
+ # Jump to a label.
505
+ def goto label
506
+ emit "b #{label}\n"
507
+
508
+ # If we have constants that need to be emitted, do so now
509
+ emit_constants
510
+ end
511
+
512
+ # Grows the current frame by n words, plus padding to
513
+ # respect alignment rules.
514
+ def grow_frame nwords
515
+ increment = (nwords * @WORDSIZE + 7) / 8 * 8
516
+ emit "sub sp, sp, \##{increment}\n"
517
+ @frame_size = @frame_size + increment
518
+ @frame_offset = @frame_offset + increment
519
+ end
520
+
521
+ # Start the false path of a conditional.
522
+ def ifelse
523
+ emit "# else\n"
524
+ newlabel = @environment.gensym
525
+ goto newlabel
526
+ lbl = @if_labels.pop
527
+ label lbl
528
+ @if_labels.push newlabel
529
+ end
530
+
531
+ # Test if x is equal to y
532
+ def ifeq x, y
533
+ common_if :ifeq, x, y
534
+ end
535
+
536
+ # Test if x is greater than or equal to y
537
+ def ifge x, y
538
+ common_if :ifge, x, y
539
+ end
540
+
541
+ # Test if x is strictly greater than y
542
+ def ifgt x, y
543
+ common_if :ifgt, x, y
544
+ end
545
+
546
+ # Test if x is less than or equal to y
547
+ def ifle x, y
548
+ common_if :ifle, x, y
549
+ end
550
+
551
+ # Test if x is strictly less than y
552
+ def iflt x, y
553
+ common_if :iflt, x, y
554
+ end
555
+
556
+ # Test if x different from y
557
+ def ifne x, y
558
+ common_if :ifne, x, y
559
+ end
560
+
561
+ # Import labels into the current section
562
+ def import *symbols
563
+ # Record imported labels in @imports
564
+ symbols.each { |sym| @imports[sym] = sym }
565
+ end
566
+
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
+ # Introduce a new local variable
578
+ def let symbol, *expr
579
+ emit "# let #{symbol} #{expr.join ' '}\n"
580
+ n = @environment.locals
581
+ @environment.add_local symbol
582
+
583
+ register = local_register n
584
+ if register
585
+ # We will use a register to store the value
586
+ eval_expr expr, register
587
+ else
588
+ # 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"
592
+ end
593
+ end
594
+
595
+ # Load the value at the given address.
596
+ def load_at address, register = @TEMPORARY
597
+ load_value_into_register address, register
598
+ emit "ldr #{register}, [#{register}]\n"
599
+ register
600
+ end
601
+
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
653
+ else
654
+ raise "Don't know how to load #{x.inspect}"
655
+ end
656
+ end
657
+
658
+ # Load a value into a specific register
659
+ 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}"
678
+ else
679
+ "v#{n + 2}"
680
+ end
681
+ else
682
+ nil
683
+ end
684
+ end
685
+
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
696
+ 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
701
+ end
702
+
703
+ # Returns true if the nth (0-based) local is stored in a register
704
+ def register_local? n
705
+ (n + number_of_register_arguments) < @NREGISTER_LOCALS
706
+ end
707
+
708
+ # Returns from a function.
709
+ #
710
+ # _words_ may contain an expression to be evaluated. The result
711
+ # of the evaluation is returned from the function.
712
+ def ret *words
713
+ emit "# return #{words.join ' '}\n"
714
+ # Compute return value and store it in @RETURN
715
+ eval_expr(words, @RETURN) unless words.empty?
716
+ # Go to epilogue
717
+ goto @function_end_label
718
+ end
719
+
720
+ # Set a variable to the result of evaluating an expression
721
+ 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"
748
+ end
749
+ eval_expr expr, @TEMPORARY
750
+ emit "str #{@TEMPORARY}, #{ref}\n"
751
+ end
752
+ end
753
+
754
+ # Set the byte at _base_ + _offset_ to _value_
755
+ def set_byte base, offset, value
756
+ emit "# set-byte #{base} #{offset} #{value}\n"
757
+ # If base is an integer, but offset isn't, swap them
758
+ if !integer?(offset) && integer?(base)
759
+ base, offset = [offset, base]
760
+ end
761
+
762
+ if integer? offset
763
+ 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"
769
+ end
770
+ else
771
+ eval_binop [:add, base, offset], :a4
772
+ load_value_into_register value, @TEMPORARY
773
+ emit "strb #{@TEMPORARY}, [a4]\n"
774
+ end
775
+ end
776
+
777
+ # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
778
+ def set_word base, offset, value
779
+ emit "# set-word #{base} #{offset} #{value}\n"
780
+ # If base is an integer, but offset isn't, swap them
781
+ if !integer?(offset) && integer?(base)
782
+ base, offset = [offset, base]
783
+ end
784
+
785
+ if integer? offset
786
+ 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"
792
+ end
793
+ else
794
+ 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"
799
+ end
800
+ end
801
+
802
+ # Define a string with the given value
803
+ def string value
804
+ code = ''
805
+ value.each_byte do |b|
806
+ if b == 92
807
+ code << "\\\\"
808
+ elsif b >= 32 && b < 127 && b != 34
809
+ code << b.chr
810
+ else
811
+ code << sprintf("\\%03o", b)
812
+ end
813
+ end
814
+ emit ".ascii \"#{code}\"\n"
815
+ end
816
+
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
+ # Call a function, re-using the current call frame if possible.
829
+ def tail_call func, *args
830
+ emit "# tail-call #{func} #{args.join ' '}\n"
831
+
832
+ # Compute number of stack arguments
833
+ nstackargs = number_of_stack_arguments args.length
834
+ # If we need more stack arguments than we have now,
835
+ # perform a normal call and return
836
+ if nstackargs > number_of_stack_arguments(@environment.args)
837
+ emit "# Not enough space for proper tail call; using regular call\n"
838
+ ret :call, func, *args
839
+ end
840
+
841
+ # We will assign arguments from left to right.
842
+ # Find places that we will overwrite before we read them,
843
+ # and store their values in some newly allocated stack space.
844
+ old_frame_offset = @frame_offset
845
+ old_frame_size = @frame_size
846
+ overwritten = {}
847
+ (@NREGISTER_ARGS...args.length).each do |i|
848
+ arg = args[i]
849
+ arg = arg[1] if at_expr? arg
850
+ if symbol?(arg)
851
+ binding = @environment[arg]
852
+ if binding[0] == :arg && binding[1] >= @NREGISTER_ARGS &&
853
+ binding[1] < i
854
+ # Argument i is a stack argument, but the value we will assign
855
+ # it is a stack argument that comes before it, so we will
856
+ # have overwritten it by the time we get to it.
857
+ overwritten[arg] = nil
858
+ end
859
+ end
860
+ end
861
+
862
+ unless overwritten.empty?
863
+ # Allocate space for arguments to be saved
864
+ grow_frame overwritten.length
865
+ # Save values
866
+ offset = 0
867
+ overwritten.each_key do |key|
868
+ reg = load_value key
869
+ emit "str #{reg}, [sp, \##{offset}]\n"
870
+ overwritten[key] = offset
871
+ offset = offset + @WORDSIZE
872
+ end
873
+ end
874
+
875
+ # Assign arguments
876
+ args.each_index do |i|
877
+ arg = args[i]
878
+ if register_arg? i
879
+ load_value_into_register arg, "a#{i + 1}"
880
+ else
881
+ # Test if this is a value we saved
882
+ sym = at_expr?(arg) ? arg[1] : arg
883
+ saved = overwritten[sym]
884
+ if saved
885
+ # Saved value, load from stack
886
+ reg = @TEMPORARY
887
+ emit "ldr #{reg}, [sp, \##{saved}]\n"
888
+ else
889
+ # Regular value, use load_value
890
+ reg = load_value arg
891
+ end
892
+ emit "str #{reg}, #{arg_reference i}\n"
893
+ end
894
+ end
895
+
896
+ # Load address of function to be called into @TEMPORARY
897
+ load_value_into_register func, @TEMPORARY
898
+
899
+ # Destroy current activation frame and enter func
900
+ destroy_frame false
901
+ emit "bx #{@TEMPORARY}\n"
902
+ emit_constants
903
+ end
904
+
905
+ # Define a word with the given value
906
+ def word value
907
+ emit ".int #{value}\n"
908
+ end
909
+
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
+ end
922
+
923
+ # Register class for little endian ARM
924
+ Voodoo::CodeGenerator.register_generator ARMGasGenerator,
925
+ :architecture => :arm,
926
+ :format => :gas
927
+ end