voodoo 1.0.1 → 1.0.2

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