voodoo 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,15 +2,15 @@ module Voodoo
2
2
  # Methods to get and set configuration parameters
3
3
  module Config
4
4
  IMPLEMENTATION_NAME = 'Voodoo Compiler'
5
- IMPLEMENTATION_VERSION = '1.0.1'
5
+ IMPLEMENTATION_VERSION = '1.0.2'
6
6
 
7
7
  # Class that holds configuration parameters
8
8
  class Configuration
9
9
  def initialize
10
10
  @default_architecture = :auto
11
11
  @default_format = :elf
12
- @gas_command = "/usr/bin/as"
13
- @nasm_command = "/usr/bin/nasm"
12
+ @gas_command = "as"
13
+ @nasm_command = "nasm"
14
14
  end
15
15
 
16
16
  attr_accessor :default_format,
@@ -0,0 +1,957 @@
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 <-- r11 points here
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 and r10 are used for local variables
56
+ # and function arguments, whereas r11 is used as a frame pointer.
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_BITS = 2
64
+ @WORDSIZE = 1 << @WORDSIZE_BITS
65
+ @CODE_ALIGNMENT = 4
66
+ @DATA_ALIGNMENT = @WORDSIZE
67
+ @FUNCTION_ALIGNMENT = @WORDSIZE
68
+ @STACK_ALIGNMENT_BITS = 3
69
+ @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
70
+
71
+ @INITIAL_FRAME_SIZE = 2 * @WORDSIZE
72
+ @NREGISTER_ARGS = 4
73
+ @NREGISTER_LOCALS = 6
74
+ @FP = :r11
75
+ @RETURN = :r0
76
+ @TEMPORARY = :r12
77
+ @constants = []
78
+ @frame_offset = 0
79
+ @frame_size = 0
80
+ @function_end_label = nil
81
+ @imports = {}
82
+ @if_labels = []
83
+ @saved_registers = []
84
+ super params
85
+ @output_file_suffix = '.s'
86
+ @features.merge! \
87
+ :'bits-per-word' => '32',
88
+ :'byte-order' => 'little-endian',
89
+ :'bytes-per-word' => 4
90
+ end
91
+
92
+ # Create an entry in the constants table,
93
+ # returning the label that will refer to the constant.
94
+ # The value may be an integer or a label.
95
+ def add_constant value
96
+ label = gensym
97
+ @constants << [label, value]
98
+ label
99
+ end
100
+
101
+ def align alignment = nil
102
+ unless alignment
103
+ # Get default alignment
104
+ case @section
105
+ when :code
106
+ alignment = @CODE_ALIGNMENT
107
+ when :data
108
+ alignment = @DATA_ALIGNMENT
109
+ when :function
110
+ alignment = @FUNCTION_ALIGNMENT
111
+ else
112
+ # Use data alignment as default
113
+ alignment = @DATA_ALIGNMENT
114
+ end
115
+ end
116
+ emit ".align #{alignment}\n" unless alignment == 0
117
+ end
118
+
119
+ # Returns the fp-relative offset for the nth (0-based) argument.
120
+ def arg_offset n
121
+ (n - @NREGISTER_ARGS) * @WORDSIZE
122
+ end
123
+
124
+ # Returns an fp-relative reference for the nth (0-based) argument.
125
+ def arg_reference n
126
+ offset_reference arg_offset(n)
127
+ end
128
+
129
+ # Return the register in which the nth (0-based) argument is stored, or
130
+ # nil if not stored in a register
131
+ def arg_register n
132
+ # The first @NREGISTER_ARGS arguments are in the v registers,
133
+ # the rest are on the stack.
134
+ if register_arg? n
135
+ "v#{n + 1}"
136
+ else
137
+ nil
138
+ end
139
+ end
140
+
141
+ # Test if op is a binary operation
142
+ def assymetric_binop? op
143
+ [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
144
+ end
145
+
146
+ # Test if a value is an at-expression
147
+ def at_expr? value
148
+ value.respond_to?(:[]) && value[0] == :'@'
149
+ end
150
+
151
+ def auto_bytes value, register
152
+ if value.kind_of? Integer
153
+ grow_frame value
154
+ else
155
+ temporary = register == :sp ? @TEMPORARY : register
156
+ load_value_into_register value, temporary
157
+ auto_bytes_register temporary
158
+ end
159
+ emit "cpy #{register}, sp\n" unless register == :sp
160
+ end
161
+
162
+ # auto-bytes where the value is supplied in a register and the return
163
+ # value will be in sp. register must not be sp.
164
+ def auto_bytes_register register
165
+ temporary = register == @TEMPORARY ? :r3 : @TEMPORARY
166
+ emit "add #{register}, #{register}, \##{@STACK_ALIGNMENT - 1}\n"
167
+ emit "mvn #{temporary}, \##{@STACK_ALIGNMENT - 1}\n"
168
+ emit "and #{register}, #{register}, #{temporary}\n"
169
+ emit "sub sp, #{temp1}\n"
170
+ end
171
+
172
+ def auto_words value, register
173
+ if value.kind_of? Integer
174
+ auto_bytes(value * @WORDSIZE, register)
175
+ else
176
+ temporary = register == :sp ? @TEMPORARY : register
177
+ load_value_into_register value, temporary
178
+ emit "lsl #{temporary}, #{temporary}, \##{@WORDSIZE_BITS}\n"
179
+ auto_bytes_register temporary
180
+ emit "cpy #{register}, sp\n" unless register == :sp
181
+ end
182
+ end
183
+
184
+ # Begins a new block.
185
+ def begin_block *code
186
+ emit "# begin block\n"
187
+ # If we are starting a block at top level, create a frame
188
+ if @environment == @top_level
189
+ nlocals = count_locals code
190
+ create_frame nlocals, false
191
+ end
192
+ @environment = Environment.new @environment
193
+ end
194
+
195
+ # Emit function prologue and declare _formals_ as function arguments
196
+ def begin_function formals, nlocals
197
+ if @environment != @top_level
198
+ raise "Can only begin a function at top level"
199
+ end
200
+
201
+ @function_end_label = gensym
202
+ emit "# function #{formals.join ' '}\n"
203
+ environment = Environment.new @environment
204
+ formals.each_with_index do |formal, i|
205
+ if i < @NREGISTER_ARGS
206
+ environment.add_arg formal, arg_register(i)
207
+ else
208
+ environment.add_arg formal, arg_offset(i)
209
+ end
210
+ end
211
+ @environment = environment
212
+ emit_function_prologue formals, nlocals
213
+ end
214
+
215
+ # Test if op is a binary operation
216
+ def binop? op
217
+ assymetric_binop?(op) || symmetric_binop?(op)
218
+ end
219
+
220
+ # Define a byte with the given value
221
+ def byte value
222
+ emit ".byte #{value}\n"
223
+ end
224
+
225
+ # Call a function.
226
+ def call func, *args
227
+ emit "# call #{func} #{args.join ' '}\n"
228
+
229
+ # Calculate how many arguments need to be pushed on
230
+ # the stack, and allocate space for them.
231
+ nstack_args = number_of_stack_arguments args.length
232
+ old_frame_offset = @frame_offset
233
+ old_frame_size = @frame_size
234
+ grow_frame nstack_args if nstack_args > 0
235
+
236
+ # Put stack arguments on the stack
237
+ (@NREGISTER_ARGS...args.length).each do |n|
238
+ load_value_into_register args[n], @TEMPORARY
239
+ emit "str #{@TEMPORARY}, " +
240
+ "[sp , \##{(n - @NREGISTER_ARGS) * @WORDSIZE}]\n"
241
+ end
242
+
243
+ # Put register arguments in the right registers
244
+ nregister_args = number_of_register_arguments args.length
245
+ nregister_args.times do |n|
246
+ load_value_into_register args[n], :"a#{n + 1}"
247
+ end
248
+
249
+ # Call function
250
+ if global? func
251
+ emit "bl #{func}\n"
252
+ else
253
+ func_reg = load_value func
254
+ emit "blx #{func_reg}\n"
255
+ end
256
+
257
+ # Restore original stack frame
258
+ if old_frame_size != @frame_size
259
+ emit "add sp, sp, \##{@frame_size - old_frame_size}\n"
260
+ @frame_offset = old_frame_offset
261
+ @frame_size = old_frame_size
262
+ end
263
+ end
264
+
265
+ # Creates a stack frame for the given number of arguments
266
+ # and local variables.
267
+ def create_frame nvars, save_lr = true
268
+ # Calculate how many variables we will store in registers,
269
+ # and how many on the stack.
270
+ nregister_vars = [nvars, @NREGISTER_LOCALS].min
271
+ nstack_vars = nvars - nregister_vars
272
+
273
+ # Save the registers we will clobber to the stack.
274
+ clobbered = []
275
+ nregister_vars.times do |i|
276
+ clobbered << :"v#{i < 5 ? i + 1 : i + 2}"
277
+ end
278
+ clobbered << @FP
279
+ clobbered << :lr if save_lr
280
+ @saved_registers = clobbered
281
+ emit "stmfd sp!, {#{clobbered.join ', '}}\n"
282
+ emit "add #{@FP}, sp, \##{clobbered.length * @WORDSIZE}\n"
283
+
284
+ # Calculate frame size so that the stack pointer will
285
+ # be properly aligned at the end of emit_function_prologue.
286
+ @frame_size = stack_align((clobbered.length + nstack_vars) * @WORDSIZE)
287
+ extra_space = @frame_size - clobbered.length * @WORDSIZE
288
+ if extra_space > 0
289
+ emit "sub sp, sp, \##{extra_space}\n"
290
+ end
291
+ @frame_offset = 0
292
+ end
293
+
294
+ # Start a conditional using the specified branch instruction
295
+ # after the comparison.
296
+ def common_if comp, x, y = nil
297
+ emit "# #{comp} #{x} #{y}\n"
298
+
299
+ xreg = load_value x, @TEMPORARY
300
+ yreg = load_value y, :a4
301
+
302
+ falselabel = @environment.gensym
303
+ @if_labels.push falselabel
304
+
305
+ emit "cmp #{xreg}, #{yreg}\n"
306
+
307
+ lut = { :ifeq => "bne", :ifge => "blt", :ifgt => "ble",
308
+ :ifle => "bgt", :iflt => "bge", :ifne => "beq" }
309
+ emit "#{lut[comp]} #{falselabel}\n"
310
+ end
311
+
312
+ # Counts the number of local variables created in
313
+ # a sequence of statements.
314
+ def count_locals statements
315
+ count = 0
316
+ each_statement(statements) do |statement|
317
+ if statement[0] == :let
318
+ # let introduces a single local
319
+ count = count + 1
320
+ end
321
+ end
322
+ count
323
+ end
324
+
325
+ # Destroys the current stack frame.
326
+ # If ret is true, loads the saved value of lr into pc.
327
+ def destroy_frame ret = false
328
+ # Set sp back to where saved registers were stored
329
+ saved = @saved_registers
330
+ emit "sub sp, #{@FP}, \##{saved.length * @WORDSIZE}\n"
331
+
332
+ if ret
333
+ index = saved.index :lr
334
+ if index
335
+ saved[index] = :pc
336
+ else
337
+ raise "Request to load saved lr into pc, but lr has not been saved"
338
+ end
339
+ end
340
+ emit "ldmfd sp!, {#{saved.join ', '}}\n"
341
+
342
+ emit_constants if ret
343
+ end
344
+
345
+ # Writes any constants that need to be written to the instruction
346
+ # stream, and clears the list of constants that need to be written.
347
+ def emit_constants
348
+ @constants.each do |x|
349
+ label x[0]
350
+ word x[1]
351
+ end
352
+ @constants = []
353
+ end
354
+
355
+ # Emit function prologue.
356
+ def emit_function_prologue formals = [], nlocals = 0
357
+ # Calculate the number of arguments we were passed in
358
+ # registers, the total number of values we need to save
359
+ # on the stack, then create a stack frame and save
360
+ # the v registers we will be using.
361
+ nregister_args = [formals.length, @NREGISTER_ARGS].min
362
+ nvars = nregister_args + nlocals
363
+ create_frame nvars, true
364
+
365
+ # Move arguments that were passed in registers into
366
+ # callee-save registers.
367
+ nregister_args.times do |i|
368
+ emit "cpy v#{i + 1}, a#{i + 1}\n"
369
+ end
370
+ end
371
+
372
+ # Ends the current block.
373
+ def end_block
374
+ emit "# end block\n"
375
+
376
+ # If we are returning to top level, restore stack pointer
377
+ # and saved registers.
378
+ if @environment.parent == @top_level
379
+ offset = @frame_size - @saved_registers.length * @WORDSIZE
380
+ if offset > 0
381
+ emit "add sp, sp, \##{offset}\n"
382
+ end
383
+ emit "ldmfd sp!, {#{@saved_registers.join ', '}}\n"
384
+ @frame_size = 0
385
+ @frame_offset = 0
386
+ @saved_registers = []
387
+
388
+ # If we need to emit constants, do so now
389
+ unless @constants.empty?
390
+ lbl = gensym
391
+ goto lbl
392
+ label lbl
393
+ end
394
+ end
395
+
396
+ # Restore old value of @environment
397
+ @environment = @environment.parent
398
+ end
399
+
400
+ # Ends a function body.
401
+ def end_function
402
+ if @environment == @top_level
403
+ raise "Cannot end function when not in a function"
404
+ end
405
+
406
+ emit "# function epilogue\n"
407
+ label @function_end_label
408
+
409
+ destroy_frame true
410
+ @frame_size = 0
411
+ @frame_offset = 0
412
+ @saved_registers = []
413
+ emit "# end function\n\n"
414
+ @environment = @top_level
415
+ end
416
+
417
+ # Ends a conditional.
418
+ def end_if
419
+ label @if_labels.pop
420
+ end
421
+
422
+ # Evaluate the binary operation expr and store the result in register
423
+ def eval_binop expr, register
424
+ op = expr[0]
425
+
426
+ # Emulation for div and mod, which ARM does not have instructions for
427
+ case op
428
+ when :div
429
+ func = :"__aeabi_idiv"
430
+ import func unless @imports.has_key? func
431
+ call func, expr[1], expr[2]
432
+ emit "cpy #{register}, r0\n" if register != :r0
433
+ return
434
+ when :mod
435
+ func = :"__aeabi_idivmod"
436
+ import func unless @imports.has_key? func
437
+ call func, expr[1], expr[2]
438
+ emit "cpy #{register}, r1\n" if register != :r1
439
+ return
440
+ end
441
+
442
+ x = load_value expr[1], :a4
443
+ y = load_value expr[2], @TEMPORARY
444
+
445
+ case op
446
+ when :bsr
447
+ emit "lsr #{register}, #{x}, #{y}\n"
448
+ when :or
449
+ emit "orr #{register}, #{x}, #{y}\n"
450
+ when :rol
451
+ emit "rsb #{y}, #{y}, #32\n"
452
+ emit "ror #{register}, #{x}, #{y}\n"
453
+ when :shl
454
+ emit "lsl #{register}, #{x}, #{y}\n"
455
+ when :shr
456
+ emit "lsr #{register}, #{x}, #{y}\n"
457
+ when :xor
458
+ emit "eor #{register}, #{x}, #{y}\n"
459
+ else
460
+ emit "#{expr[0]} #{register}, #{x}, #{y}\n"
461
+ end
462
+ end
463
+
464
+ # Evaluates the expression +expr+ and stores the result in +register+.
465
+ def eval_expr expr, register
466
+ if expr.length == 1
467
+ # Load value
468
+ load_value_into_register expr[0], register
469
+ else
470
+ # Evaluate expression
471
+ op = expr[0]
472
+ case op
473
+ when :'auto-bytes'
474
+ auto_bytes expr[1], register
475
+ when :'auto-words'
476
+ auto_words expr[1], register
477
+ when :call
478
+ call *expr[1..-1]
479
+ emit "cpy #{register}, #{@RETURN}\n" if register != @RETURN
480
+ when :'get-byte'
481
+ get_byte expr[1], expr[2], register
482
+ when :'get-word'
483
+ get_word expr[1], expr[2], register
484
+ when :not
485
+ load_value_into_register expr[1], register
486
+ emit "mvn #{@TEMPORARY}, #0\n"
487
+ emit "eor #{register}, #{register}, #{@TEMPORARY}\n"
488
+ else
489
+ if binop? op
490
+ eval_binop expr, register
491
+ else
492
+ raise "Not a magic word: #{op}"
493
+ end
494
+ end
495
+ end
496
+ end
497
+
498
+ # Export symbols from the current section
499
+ def export *symbols
500
+ symbols.each { |sym| emit ".globl #{sym}\n" }
501
+ end
502
+
503
+ # Add a function to the current section
504
+ def function formals, *code
505
+ nlocals = count_locals code
506
+ begin_function formals, nlocals
507
+ code.each { |action| add section, action }
508
+ end_function
509
+ end
510
+
511
+ # Load byte from _base_ + _offset_ into _register_
512
+ def get_byte base, offset, register
513
+ # If base is an integer, but offset isn't, swap them
514
+ if !integer?(offset) && integer?(base)
515
+ base, offset = [offset, base]
516
+ end
517
+
518
+ if integer? offset
519
+ base_reg = load_value base
520
+ if offset == 0
521
+ emit "ldrb #{register}, [#{base_reg}]\n"
522
+ else
523
+ emit "ldrb #{register}, [#{base_reg}, \##{offset}]\n"
524
+ end
525
+ else
526
+ base_reg = load_value base
527
+ offset_reg = load_value offset, :a4
528
+ emit "ldrb #{register}, [#{base_reg}, #{offset_reg}]\n"
529
+ end
530
+ end
531
+
532
+ # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
533
+ def get_word base, offset, register
534
+ if integer? offset
535
+ base_reg = load_value base
536
+ if offset == 0
537
+ emit "ldr #{register}, [#{base_reg}]\n"
538
+ else
539
+ emit "ldr #{register}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
540
+ end
541
+ else
542
+ base_reg = load_value base
543
+ offset_reg = load_value offset, :a4
544
+ emit "ldr #{register}, [#{base_reg}, #{offset_reg}, LSL #2]\n"
545
+ end
546
+ end
547
+
548
+ # Test if a symbol refers to a global
549
+ def global? symbol
550
+ symbol?(symbol) && @environment[symbol] == nil
551
+ end
552
+
553
+ # Jump to a label.
554
+ def goto label
555
+ emit "b #{label}\n"
556
+
557
+ # If we have constants that need to be emitted, do so now
558
+ emit_constants
559
+ end
560
+
561
+ # Grows the current frame by n words, plus padding to
562
+ # respect alignment rules.
563
+ def grow_frame nwords
564
+ increment = stack_align(nwords * @WORDSIZE)
565
+ emit "sub sp, sp, \##{increment}\n"
566
+ @frame_size = @frame_size + increment
567
+ @frame_offset = @frame_offset + increment
568
+ end
569
+
570
+ # Start the false path of a conditional.
571
+ def ifelse
572
+ emit "# else\n"
573
+ newlabel = @environment.gensym
574
+ goto newlabel
575
+ lbl = @if_labels.pop
576
+ label lbl
577
+ @if_labels.push newlabel
578
+ end
579
+
580
+ # Test if x is equal to y
581
+ def ifeq x, y
582
+ common_if :ifeq, x, y
583
+ end
584
+
585
+ # Test if x is greater than or equal to y
586
+ def ifge x, y
587
+ common_if :ifge, x, y
588
+ end
589
+
590
+ # Test if x is strictly greater than y
591
+ def ifgt x, y
592
+ common_if :ifgt, x, y
593
+ end
594
+
595
+ # Test if x is less than or equal to y
596
+ def ifle x, y
597
+ common_if :ifle, x, y
598
+ end
599
+
600
+ # Test if x is strictly less than y
601
+ def iflt x, y
602
+ common_if :iflt, x, y
603
+ end
604
+
605
+ # Test if x different from y
606
+ def ifne x, y
607
+ common_if :ifne, x, y
608
+ end
609
+
610
+ # Import labels into the current section
611
+ def import *symbols
612
+ # Record imported labels in @imports
613
+ symbols.each { |sym| @imports[sym] = sym }
614
+ end
615
+
616
+ # Test if a value is an integer
617
+ def integer? value
618
+ value.kind_of? Integer
619
+ end
620
+
621
+ # Emit a label
622
+ def label name
623
+ emit "#{name}:\n"
624
+ end
625
+
626
+ # Introduce a new local variable
627
+ def let symbol, *expr
628
+ n = @environment.locals
629
+ register = local_register n
630
+
631
+ if register
632
+ # We will use a register to store the value
633
+ @environment.add_local symbol, register
634
+ eval_expr expr, register
635
+ else
636
+ # We will use the stack to store the value
637
+ offset = local_offset n
638
+ @environment.add_local symbol, offset
639
+ eval_expr expr, @TEMPORARY
640
+ emit "str #{@TEMPORARY}, #{offset_reference offset}\n"
641
+ end
642
+ end
643
+
644
+ # Load the value at the given address.
645
+ def load_at address, register = @TEMPORARY
646
+ load_value_into_register address, register
647
+ emit "ldr #{register}, [#{register}]\n"
648
+ register
649
+ end
650
+
651
+ # Load a value into a register.
652
+ # Returns the name of the register.
653
+ # If the value was already in a register, the name of that
654
+ # register is returned.
655
+ # Else, the value is loaded into a register and the name of
656
+ # that register is returned. The register to use in that case
657
+ # may be specified using the optional second argument.
658
+ def load_value x, register = @TEMPORARY
659
+ if integer? x
660
+ if x >= 0 && x <= 255
661
+ emit "mov #{register}, \##{x}\n"
662
+ return register
663
+ elsif x >= -255 && x < 0
664
+ emit "mvn #{register}, \##{-(x + 1)}\n"
665
+ return register
666
+ else
667
+ lbl = add_constant x
668
+ emit "ldr #{register}, #{lbl}\n"
669
+ return register
670
+ end
671
+ elsif symbol? x
672
+ binding = @environment[x]
673
+ if binding.kind_of? String
674
+ # Value is already in a register. Return register name.
675
+ return binding
676
+ elsif binding.kind_of? Integer
677
+ # Value is on the stack. Load from the stack.
678
+ emit "ldr #{register}, #{offset_reference binding}\n"
679
+ return register
680
+ else
681
+ # Assume global
682
+ lbl = add_constant x
683
+ emit "ldr #{register}, #{lbl}\n"
684
+ return register
685
+ end
686
+ elsif at_expr? x
687
+ load_at x[1], register
688
+ else
689
+ raise "Don't know how to load #{x.inspect}"
690
+ end
691
+ end
692
+
693
+ # Load a value into a specific register
694
+ def load_value_into_register x, register
695
+ reg = load_value x, register
696
+ if reg != register
697
+ emit "cpy #{register}, #{reg}\n"
698
+ end
699
+ end
700
+
701
+ # Returns the fp-relative reference for the nth (0-based) local.
702
+ def local_offset n
703
+ -@INITIAL_FRAME_SIZE - (n * @WORDSIZE)
704
+ end
705
+
706
+ # Return the register in which the nth local (0-based) is stored, or
707
+ # nil if not stored in a register
708
+ def local_register n
709
+ if register_local? n
710
+ n = n + number_of_register_arguments
711
+ if n < 5
712
+ "v#{n + 1}"
713
+ else
714
+ "v#{n + 2}"
715
+ end
716
+ else
717
+ nil
718
+ end
719
+ end
720
+
721
+ # Calculate the number of register arguments,
722
+ # given the total number of arguments.
723
+ def number_of_register_arguments n = @environment.args
724
+ [n, @NREGISTER_ARGS].min
725
+ end
726
+
727
+ # Calculate the number of stack arguments,
728
+ # given the total number of arguments.
729
+ def number_of_stack_arguments n = @environment.args
730
+ [0, n - @NREGISTER_ARGS].max
731
+ end
732
+
733
+ # Given an offset, returns an fp-relative reference.
734
+ def offset_reference offset
735
+ "[#{@FP}, \##{offset}]"
736
+ end
737
+
738
+ # Returns true if the nth (0-based) argument is stored in a register
739
+ def register_arg? n
740
+ n < @NREGISTER_ARGS
741
+ end
742
+
743
+ # Returns true if the nth (0-based) local is stored in a register
744
+ def register_local? n
745
+ (n + number_of_register_arguments) < @NREGISTER_LOCALS
746
+ end
747
+
748
+ # Returns from a function.
749
+ #
750
+ # _words_ may contain an expression to be evaluated. The result
751
+ # of the evaluation is returned from the function.
752
+ def ret *words
753
+ emit "# return #{words.join ' '}\n"
754
+ # Compute return value and store it in @RETURN
755
+ eval_expr(words, @RETURN) unless words.empty?
756
+ # Go to epilogue
757
+ goto @function_end_label
758
+ end
759
+
760
+ # Set a variable to the result of evaluating an expression
761
+ def set symbol, *expr
762
+ if at_expr? symbol
763
+ eval_expr expr, :r3
764
+ register = load_value symbol[1]
765
+ emit "str r3, [#{register}]\n"
766
+ else
767
+ x = @environment[symbol]
768
+ if x == nil
769
+ raise "Cannot change value of constant #{symbol}"
770
+ elsif x.kind_of? String
771
+ eval_expr expr, x
772
+ else
773
+ eval_expr expr, @TEMPORARY
774
+ emit "str #{@TEMPORARY}, #{offset_reference x}\n"
775
+ end
776
+ end
777
+ end
778
+
779
+ # Set the byte at _base_ + _offset_ to _value_
780
+ def set_byte base, offset, value
781
+ emit "# set-byte #{base} #{offset} #{value}\n"
782
+ # If base is an integer, but offset isn't, swap them
783
+ if !integer?(offset) && integer?(base)
784
+ base, offset = [offset, base]
785
+ end
786
+
787
+ if integer? offset
788
+ base_reg = load_value base, :a4
789
+ load_value_into_register value, @TEMPORARY
790
+ if offset == 0
791
+ emit "strb #{@TEMPORARY}, [#{base_reg}]\n"
792
+ else
793
+ emit "strb #{@TEMPORARY}, [#{base_reg}, \##{offset}]\n"
794
+ end
795
+ else
796
+ eval_binop [:add, base, offset], :a4
797
+ load_value_into_register value, @TEMPORARY
798
+ emit "strb #{@TEMPORARY}, [a4]\n"
799
+ end
800
+ end
801
+
802
+ # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
803
+ def set_word base, offset, value
804
+ emit "# set-word #{base} #{offset} #{value}\n"
805
+ # If base is an integer, but offset isn't, swap them
806
+ if !integer?(offset) && integer?(base)
807
+ base, offset = [offset, base]
808
+ end
809
+
810
+ if integer? offset
811
+ base_reg = load_value base, :a4
812
+ load_value_into_register value, @TEMPORARY
813
+ if offset == 0
814
+ emit "str #{@TEMPORARY}, [#{base_reg}]\n"
815
+ else
816
+ emit "str #{@TEMPORARY}, [#{base_reg}, \##{offset * @WORDSIZE}]\n"
817
+ end
818
+ else
819
+ load_value_into_register base, :a4
820
+ load_value_into_register offset, @TEMPORARY
821
+ emit "add a4, a4, #{@TEMPORARY}, LSL #2\n"
822
+ load_value_into_register value, @TEMPORARY
823
+ emit "str #{@TEMPORARY}, [a4]\n"
824
+ end
825
+ end
826
+
827
+ # Given n, returns the nearest multiple of @STACK_ALIGNMENT
828
+ # that is >= n.
829
+ def stack_align n
830
+ (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
831
+ end
832
+
833
+ # Define a string with the given value
834
+ def string value
835
+ code = ''
836
+ value.each_byte do |b|
837
+ if b == 92
838
+ code << "\\\\"
839
+ elsif b >= 32 && b < 127 && b != 34
840
+ code << b.chr
841
+ else
842
+ code << sprintf("\\%03o", b)
843
+ end
844
+ end
845
+ emit ".ascii \"#{code}\"\n"
846
+ end
847
+
848
+ # Test if a value is a symbol
849
+ def symbol? value
850
+ value.kind_of? Symbol
851
+ end
852
+
853
+ # Test if op is a symmetric binary operation (i.e. it will yield the
854
+ # same result if the order of its source operands is changed).
855
+ def symmetric_binop? op
856
+ [:add, :and, :mul, :or, :xor].member? op
857
+ end
858
+
859
+ # Call a function, re-using the current call frame if possible.
860
+ def tail_call func, *args
861
+ emit "# tail-call #{func} #{args.join ' '}\n"
862
+
863
+ # Compute number of stack arguments
864
+ nstackargs = number_of_stack_arguments args.length
865
+ # If we need more stack arguments than we have now,
866
+ # perform a normal call and return
867
+ if nstackargs > number_of_stack_arguments(@environment.args)
868
+ emit "# Not enough space for proper tail call; using regular call\n"
869
+ ret :call, func, *args
870
+ end
871
+
872
+ # We will assign arguments from left to right.
873
+ # Find places that we will overwrite before we read them,
874
+ # and store their values in some newly allocated stack space.
875
+ old_frame_offset = @frame_offset
876
+ old_frame_size = @frame_size
877
+ overwritten = {}
878
+ (@NREGISTER_ARGS...args.length).each do |i|
879
+ arg = args[i]
880
+ arg = arg[1] if at_expr? arg
881
+ if symbol?(arg)
882
+ binding = @environment[arg]
883
+ if binding[0] == :arg && binding[1] >= @NREGISTER_ARGS &&
884
+ binding[1] < i
885
+ # Argument i is a stack argument, but the value we will assign
886
+ # it is a stack argument that comes before it, so we will
887
+ # have overwritten it by the time we get to it.
888
+ overwritten[arg] = nil
889
+ end
890
+ end
891
+ end
892
+
893
+ unless overwritten.empty?
894
+ # Allocate space for arguments to be saved
895
+ grow_frame overwritten.length
896
+ # Save values
897
+ offset = 0
898
+ overwritten.each_key do |key|
899
+ reg = load_value key
900
+ emit "str #{reg}, [sp, \##{offset}]\n"
901
+ overwritten[key] = offset
902
+ offset = offset + @WORDSIZE
903
+ end
904
+ end
905
+
906
+ # Assign arguments
907
+ args.each_index do |i|
908
+ arg = args[i]
909
+ if register_arg? i
910
+ load_value_into_register arg, "a#{i + 1}"
911
+ else
912
+ # Test if this is a value we saved
913
+ sym = at_expr?(arg) ? arg[1] : arg
914
+ saved = overwritten[sym]
915
+ if saved
916
+ # Saved value, load from stack
917
+ reg = @TEMPORARY
918
+ emit "ldr #{reg}, [sp, \##{saved}]\n"
919
+ else
920
+ # Regular value, use load_value
921
+ reg = load_value arg
922
+ end
923
+ emit "str #{reg}, #{arg_reference i}\n"
924
+ end
925
+ end
926
+
927
+ # Load address of function to be called into @TEMPORARY
928
+ load_value_into_register func, @TEMPORARY
929
+
930
+ # Destroy current activation frame and enter func
931
+ destroy_frame false
932
+ emit "bx #{@TEMPORARY}\n"
933
+ emit_constants
934
+ end
935
+
936
+ # Define a word with the given value
937
+ def word value
938
+ emit ".int #{value}\n"
939
+ end
940
+
941
+ # Write generated code to the given IO object.
942
+ def write io
943
+ @sections.each do |section,code|
944
+ unless code.empty?
945
+ io.puts ".section #{section.to_s}"
946
+ io.puts code
947
+ io.puts
948
+ end
949
+ end
950
+ end
951
+ end
952
+
953
+ # Register class for little endian ARM
954
+ Voodoo::CodeGenerator.register_generator ARMGasGenerator,
955
+ :architecture => :arm,
956
+ :format => :gas
957
+ end
@@ -305,7 +305,7 @@ module Voodoo
305
305
  def parse_symbol
306
306
  wrap_exceptions do
307
307
  name = ''
308
- while true
308
+ while lookahead != :eof
309
309
  case lookahead
310
310
  when "\\"
311
311
  name << parse_escape
@@ -525,6 +525,8 @@ module Voodoo
525
525
  # Else, nil is returned.
526
526
  def try_parse_token
527
527
  case lookahead
528
+ when :eof
529
+ nil
528
530
  when /\d|-/
529
531
  # Digit; parse number
530
532
  parse_number
@@ -606,8 +608,10 @@ module Voodoo
606
608
  raise
607
609
  rescue => e
608
610
  # Some other error; wrap in ParserInternalError.
609
- raise ParserInternalError.new(e, @input_name, @line,
610
- @column, @text)
611
+ wrapped = ParserInternalError.new(e, @input_name, @line,
612
+ @column, @text)
613
+ wrapped.set_backtrace e.backtrace
614
+ raise wrapped
611
615
  end
612
616
  end
613
617
 
metadata CHANGED
@@ -1,96 +1,81 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: voodoo
3
- version: !ruby/object:Gem::Version
4
- hash: 21
5
- prerelease: false
6
- segments:
7
- - 1
8
- - 0
9
- - 1
10
- version: 1.0.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Robbert Haarman
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-01-29 00:00:00 -08:00
19
- default_executable:
12
+ date: 2012-11-15 00:00:00.000000000 Z
20
13
  dependencies: []
14
+ description: ! 'The Voodoo compiler is an implementation of the Voodoo programming
21
15
 
22
- description: |
23
- The Voodoo compiler is an implementation of the Voodoo programming
24
16
  language, a simple low-level programming language.
25
-
17
+
18
+
26
19
  The compiler can be used both as a stand-alone program to
20
+
27
21
  compile Voodoo source code and as a Ruby module to generate code on
22
+
28
23
  the fly. Multiple target platforms are supported.
29
24
 
25
+ '
30
26
  email: voodoo@inglorion.net
31
- executables:
27
+ executables:
32
28
  - voodooc
33
29
  extensions: []
34
-
35
30
  extra_rdoc_files: []
36
-
37
- files:
31
+ files:
38
32
  - bin/voodooc
39
- - lib/voodoo.rb
40
- - lib/voodoo/parser.rb
41
- - lib/voodoo/code_generator.rb
42
- - lib/voodoo/compiler.rb
43
33
  - lib/voodoo/config.rb
34
+ - lib/voodoo/parser.rb
44
35
  - lib/voodoo/validator.rb
36
+ - lib/voodoo/generators/arm_gas_generator.rb~
45
37
  - lib/voodoo/generators/common_code_generator.rb
46
- - lib/voodoo/generators/mips_gas_generator.rb
47
- - lib/voodoo/generators/i386_elf_generator.rb
48
- - lib/voodoo/generators/command_postprocessor.rb
49
38
  - lib/voodoo/generators/mips_elf_generator.rb
50
- - lib/voodoo/generators/gas_elf_generator.rb
51
- - lib/voodoo/generators/arm_elf_generator.rb
52
- - lib/voodoo/generators/arm_gas_generator.rb
53
- - lib/voodoo/generators/amd64_nasm_generator.rb
39
+ - lib/voodoo/generators/command_postprocessor.rb
40
+ - lib/voodoo/generators/nasm_generator.rb
54
41
  - lib/voodoo/generators/nasm_elf_generator.rb
55
42
  - lib/voodoo/generators/amd64_elf_generator.rb
56
- - lib/voodoo/generators/nasm_generator.rb
43
+ - lib/voodoo/generators/arm_elf_generator.rb
44
+ - lib/voodoo/generators/arm_gas_generator.rb
57
45
  - lib/voodoo/generators/i386_nasm_generator.rb
58
- has_rdoc: true
46
+ - lib/voodoo/generators/mips_gas_generator.rb
47
+ - lib/voodoo/generators/gas_elf_generator.rb
48
+ - lib/voodoo/generators/i386_elf_generator.rb
49
+ - lib/voodoo/generators/amd64_nasm_generator.rb
50
+ - lib/voodoo/code_generator.rb
51
+ - lib/voodoo/compiler.rb
52
+ - lib/voodoo.rb
59
53
  homepage: http://inglorion.net/software/voodoo/
60
54
  licenses: []
61
-
62
55
  post_install_message:
63
- rdoc_options:
56
+ rdoc_options:
64
57
  - -m
65
58
  - Voodoo
66
59
  - -t
67
60
  - Voodoo Code Generator API Documentation
68
- require_paths:
61
+ require_paths:
69
62
  - lib
70
- required_ruby_version: !ruby/object:Gem::Requirement
63
+ required_ruby_version: !ruby/object:Gem::Requirement
71
64
  none: false
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- hash: 3
76
- segments:
77
- - 0
78
- version: "0"
79
- required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
70
  none: false
81
- requirements:
82
- - - ">="
83
- - !ruby/object:Gem::Version
84
- hash: 3
85
- segments:
86
- - 0
87
- version: "0"
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
88
75
  requirements: []
89
-
90
76
  rubyforge_project: voodoo
91
- rubygems_version: 1.3.7
77
+ rubygems_version: 1.8.23
92
78
  signing_key:
93
79
  specification_version: 3
94
80
  summary: Compiler for the Voodoo programming language
95
81
  test_files: []
96
-