voodoo 0.7.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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