voodoo 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  module Voodoo
2
4
  # Common base class for code generators.
3
5
  #
@@ -11,7 +13,7 @@ module Voodoo
11
13
  # - #has_feature?
12
14
  # - #output_file_name
13
15
  # - #output_file_suffix
14
- # - #write
16
+ # - #saved_frame_size
15
17
  #
16
18
  # This class contains base implementations of some of these methods,
17
19
  # which can be used and/or overridden by subclasses.
@@ -20,11 +22,16 @@ module Voodoo
20
22
  # is provided on the main page of the documentation of the Voodoo module.
21
23
  #
22
24
  class CommonCodeGenerator
25
+ # == Public Interface
26
+ #
27
+ # These methods implement the public interface of code generators.
28
+
23
29
  # Initializes the code generator.
24
- # _params_ shall be a hash containing parameters to the code generator,
25
- # and shall at least contain the keys <tt>:architecture</tt> and
26
- # <tt>:format</tt>, specifying the target architecture and output
27
- # format, respectively.
30
+ # _params_ is a hash of key-value pairs, and can be used to pass additional
31
+ # parameters to the generator.
32
+ # Standard parameters are :architecture and :format, which indicate the
33
+ # architecture and the output format. If these are not supplied, default
34
+ # values will be used. Subclasses may define additional parameters.
28
35
  def initialize params = {}
29
36
  @architecture = params[:architecture] || Config.default_architecture
30
37
  @format = params[:format] || Config.default_format
@@ -39,8 +46,10 @@ module Voodoo
39
46
  @top_level = Environment.initial_environment
40
47
  @environment = @top_level
41
48
  @output_file_suffix = '.o'
49
+ @open_labels = [] # Labels for which we must emit size annotations
50
+ @relocated_symbols = Set.new
42
51
  @features = {
43
- :voodoo => "1.0" # Voodoo language version
52
+ :voodoo => "1.1" # Voodoo language version
44
53
  }
45
54
  end
46
55
 
@@ -51,35 +60,41 @@ module Voodoo
51
60
  # add :data, [:align], [:label, :xyzzy], [:word, 42]
52
61
  #
53
62
  # This method implements the required functionality in terms
54
- # of the following methods, which must be implemented by subclasses:
63
+ # of a number of methods for individual incantations. These
64
+ # must be implemented by subclasses, although default implementations
65
+ # may be provided by CommonCodeGenerator. The following list contains
66
+ # the methods that the add method relies on. Methods that are provided
67
+ # by this class have been marked with a star. In general, these methods
68
+ # will require some functionality to be implemented by subclasses.
55
69
  #
56
- # - #align
57
- # - #asr
58
- # - #bsr
70
+ # - #align (*)
71
+ # - #block (*)
59
72
  # - #byte
60
73
  # - #call
74
+ # - #comment
75
+ # - #emit_label_size
61
76
  # - #end_if
62
- # - #export
63
- # - #begin_function
64
- # - #block
65
- # - #ifelse
77
+ # - #export (*)
78
+ # - #function (*)
79
+ # - #goto
80
+ # - #group
66
81
  # - #ifeq
67
82
  # - #ifge
68
83
  # - #ifgt
69
84
  # - #ifle
70
85
  # - #iflt
71
86
  # - #ifne
72
- # - #import
73
- # - #label
87
+ # - #import (*)
88
+ # - #label (*)
74
89
  # - #let
90
+ # - #restore_frame (*)
91
+ # - #restore_locals (*)
75
92
  # - #ret
76
- # - #rol
77
- # - #ror
93
+ # - #save_frame (*)
94
+ # - #save_locals (*)
78
95
  # - #set
79
96
  # - #set_byte
80
97
  # - #set_word
81
- # - #shl
82
- # - #shr
83
98
  # - #string
84
99
  # - #word
85
100
  #
@@ -89,35 +104,60 @@ module Voodoo
89
104
  keyword, args = action[0], action[1..-1]
90
105
  case keyword
91
106
  when :block
107
+ emit_voodoo :block
92
108
  block *args
109
+ emit_voodoo :end, :block
93
110
  when :function
111
+ emit_voodoo :function, *args[0]
94
112
  function args[0], *args[1..-1]
113
+ emit_voodoo :end, :function
114
+ when :group
115
+ emit_voodoo :group
116
+ old_open_labels = @open_labels
117
+ begin
118
+ @open_labels = []
119
+ args.each { |statement| add section, statement }
120
+ ensure
121
+ @open_labels = old_open_labels
122
+ end
123
+ emit_voodoo :end, :group
95
124
  when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
125
+ emit_voodoo keyword, *args[0]
96
126
  truebody = action[2]
97
127
  falsebody = action[3]
98
128
  send keyword, action[1][0], action[1][1]
99
129
  add section, *truebody
100
130
  if falsebody && !falsebody.empty?
131
+ emit_voodoo :else
101
132
  ifelse
102
133
  add section, *falsebody
103
134
  end
135
+ emit_voodoo :end, :if
104
136
  end_if
137
+ when :label, :string, :word
138
+ send *action
105
139
  when :return
140
+ emit_voodoo *action
106
141
  send :ret, *args
107
- when :'set-word'
108
- send :set_word, *args
109
- when :'set-byte'
110
- send :set_byte, *args
111
- when :'tail-call'
112
- send :tail_call, *args
113
142
  else
114
- send *action
143
+ emit_voodoo *action
144
+ underscored = keyword.to_s.gsub('-', '_').to_sym
145
+ send underscored, *args
146
+ end
147
+
148
+ # If we are on top-level and we have open labels and we just
149
+ # emitted something that isn't a label, emit size annotations
150
+ # for all open labels.
151
+ if !@open_labels.empty? && keyword != :label &&
152
+ @environment == @top_level
153
+ @open_labels.each { |name| emit_label_size name }
154
+ @open_labels.clear
115
155
  end
116
156
  end
117
157
  end
118
158
  end
119
159
 
120
- # Add function.
160
+ # Adds a function.
121
161
  #
122
162
  # Parameters:
123
163
  # [formals] an Array of formal parameter names
@@ -129,15 +169,106 @@ module Voodoo
129
169
  add :functions, [:function, formals] + code
130
170
  end
131
171
 
172
+ # Returns a hash describing the features supported by this code generator.
173
+ # The keys in this hash are the names of the supported features.
174
+ # The associated values are strings providing additional information about
175
+ # the feature, such as a version number.
176
+ def features
177
+ @features
178
+ end
179
+
180
+ # Returns a new, unused symbol.
181
+ def gensym
182
+ Environment.gensym
183
+ end
184
+
185
+ # Returns true if a feature is supported by this generator,
186
+ # false otherwise.
187
+ def has_feature? name
188
+ @features.has_key? name
189
+ end
190
+
191
+ # Given an input file name, returns the canonical output file name
192
+ # for this code generator.
193
+ def output_file_name input_name
194
+ input_name.sub(/\.voo$/, '') + @output_file_suffix
195
+ end
196
+
197
+ # Returns the canonical output file suffix for this code generator.
198
+ def output_file_suffix
199
+ @output_file_suffix
200
+ end
201
+
202
+ # == Helper Methods
203
+ #
204
+ # These methods are intended to be used by subclasses.
205
+
206
+ # Emits a directive to align the code or data following this
207
+ # statement. If _alignment_ is given, aligns on the next multiple
208
+ # of _alignment_ bytes. Else, uses the default alignment for the
209
+ # current section.
210
+ #
211
+ # This method requires the presence of a default_alignment method
212
+ # to calculate the default alignment for a given section, and an
213
+ # emit_align method to actually emit the target-specific code to
214
+ # align to a multiple of a given number of bytes. An implementation
215
+ # of default_alignment is provided in this class.
216
+ def align alignment = nil
217
+ alignment = default_alignment if alignment == nil
218
+ emit_align alignment unless alignment == 0
219
+ end
220
+
221
+ # Tests if op is a binary operation.
222
+ def assymetric_binop? op
223
+ [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
224
+ end
225
+
226
+ # Tests if a value is an at-expression.
227
+ def at_expr? value
228
+ value.respond_to?(:[]) && value[0] == :'@'
229
+ end
230
+
231
+ # Tests if op is a binary operation.
232
+ def binop? op
233
+ assymetric_binop?(op) || symmetric_binop?(op)
234
+ end
235
+
132
236
  # Processes code in its own block. Local variables can be
133
237
  # introduced inside the block. They will be deleted at the
134
238
  # end of the block.
239
+ #
240
+ # This method requires subclasses to implement begin_block and
241
+ # end_block.
135
242
  def block *code
136
243
  begin_block *code
137
244
  code.each { |action| add section, action }
138
245
  end_block
139
246
  end
140
247
 
248
+ # Returns the number of local variable slots required by
249
+ # a sequence of statements.
250
+ def count_locals statements
251
+ count_locals_helper statements, 0, 0
252
+ end
253
+
254
+ # Returns the default alignment for the given section.
255
+ # If no section is specified, returns the alignment for the current
256
+ # section.
257
+ def default_alignment section = @section
258
+ # Get default alignment
259
+ case section
260
+ when :code
261
+ @CODE_ALIGNMENT
262
+ when :data
263
+ @DATA_ALIGNMENT
264
+ when :function
265
+ @FUNCTION_ALIGNMENT
266
+ else
267
+ # Use data alignment as default
268
+ @DATA_ALIGNMENT
269
+ end
270
+ end
271
+
141
272
  # Invokes block with each statement in the given list of statements.
142
273
  # This iterator also descends into nested statements, calling
143
274
  # block first with the outer statement, and then for each inner
@@ -157,38 +288,108 @@ module Voodoo
157
288
  end
158
289
  end
159
290
 
160
- # Returns a hash describing the features supported by this code generator.
161
- # The keys in this hash are the names of the supported features.
162
- # The associated values are strings providing additional information about
163
- # the feature, such as a version number.
164
- def features
165
- @features
291
+ # Adds code to the current section.
292
+ def emit code
293
+ @sections[real_section_name(@section)] << code
166
294
  end
167
295
 
168
- # Add a function to the current section
296
+ # Emits a label.
297
+ def emit_label name
298
+ emit "#{name}:\n"
299
+ end
300
+
301
+ # Emits a comment with the given Voodoo code.
302
+ def emit_voodoo *code
303
+ comment code.join(' ')
304
+ end
305
+
306
+ # Declares that the given symbols are to be externally visible.
307
+ # Requires subclasses to implement emit_export.
308
+ def export *symbols
309
+ if real_section_name(section) == ".data"
310
+ @relocated_symbols.merge symbols
311
+ end
312
+ emit_export *symbols
313
+ end
314
+
315
+ # Adds a function to the current section.
316
+ # Requires subclasses to implement begin_function and end_function.
169
317
  def function formals, *code
170
- begin_function *formals
318
+ nlocals = count_locals code
319
+ begin_function formals, nlocals
171
320
  code.each { |action| add section, action }
172
321
  end_function
173
322
  end
174
323
 
175
- # Generate a new, unused symbol
176
- def gensym
177
- Environment.gensym
324
+ # Tests if a symbol refers to a global.
325
+ def global? symbol
326
+ symbol?(symbol) && @environment[symbol] == nil
178
327
  end
179
328
 
180
- # Add code to the current section
181
- def emit code
182
- @sections[real_section_name(@section)] << code
329
+ # Declares that the given symbols are imported from an external object.
330
+ # Requires subclasses to implement emit_import.
331
+ def import *symbols
332
+ if real_section_name(section) == ".data"
333
+ @relocated_symbols.merge symbols
334
+ end
335
+ emit_import *symbols
183
336
  end
184
337
 
185
- # Returns true if a feature is supported by this generator,
186
- # false otherwise.
187
- def has_feature? name
188
- @features.has_key? name
338
+ # Executes a block of code using the given section as the current section.
339
+ def in_section name, &block
340
+ oldsection = @section
341
+ self.section = name
342
+ begin
343
+ yield
344
+ ensure
345
+ self.section = oldsection
346
+ end
347
+ end
348
+
349
+ # Tests if a value is an integer.
350
+ def integer? value
351
+ value.kind_of? Integer
352
+ end
353
+
354
+ # Creates a label.
355
+ # Besides emitting the label name, this also annotates top-level
356
+ # objects with type and size as required for ELF shared libraries.
357
+ # Requires subclasses to emit emit_label and emit_label_type.
358
+ def label name
359
+ # If we are at top level, emit type directive and arrange to
360
+ # emit size directive.
361
+ if @environment == @top_level
362
+ case real_section_name(section)
363
+ when ".text"
364
+ type = :code
365
+ else
366
+ type = :data
367
+ end
368
+ emit_label_type name, type
369
+ @open_labels << name
370
+ end
371
+ emit_label name
372
+ end
373
+
374
+ # Returns the register in which the nth local (0-based) is stored, or
375
+ # nil if not stored in a register.
376
+ def local_register n
377
+ @LOCAL_REGISTERS[n + number_of_register_arguments]
189
378
  end
190
379
 
191
- # Get the real name of a section.
380
+ # Calculates the number of register arguments,
381
+ # given the total number of arguments.
382
+ def number_of_register_arguments n = @environment.args
383
+ [n, @NREGISTER_ARGS].min
384
+ end
385
+
386
+ # Calculate the number of stack arguments,
387
+ # given the total number of arguments.
388
+ def number_of_stack_arguments n = @environment.args
389
+ [0, n - @NREGISTER_ARGS].max
390
+ end
391
+
392
+ # Gets the real name of a section.
192
393
  # Given a section name which may be an alias, this method returns the
193
394
  # real name of the section.
194
395
  def real_section_name name
@@ -204,7 +405,114 @@ module Voodoo
204
405
  name
205
406
  end
206
407
 
207
- # Set the current section
408
+ # Returns true if operand is a register, false otherwise.
409
+ def register? x
410
+ x.kind_of? Symbol
411
+ end
412
+
413
+ # Returns true if the nth (0-based) argument is stored in a register.
414
+ def register_arg? n
415
+ n < @NREGISTER_ARGS
416
+ end
417
+
418
+ # Given some local variable names, returns the registers those variables
419
+ # are stored in. If no variable names are given, returns all registers
420
+ # used to store local variables.
421
+ #
422
+ # Requires @LOCAL_REGISTERS_SET, a set of registers that are used to
423
+ # store local variables.
424
+ def registers_for_locals locals = []
425
+ locals = @environment.symbols.keys if locals.empty?
426
+ registers = []
427
+ locals.each do |sym|
428
+ reg = @environment[sym]
429
+ registers << reg if @LOCAL_REGISTERS_SET.include? @environment[sym]
430
+ end
431
+ registers
432
+ end
433
+
434
+ # Restores the frame saved at the given location.
435
+ # Requires @SAVE_FRAME_REGISTERS, an array of register names that
436
+ # must be restored.
437
+ def restore_frame frame
438
+ restore_registers_from_frame frame, @SAVE_FRAME_REGISTERS
439
+ end
440
+
441
+ # Restores local variables from a saved frame.
442
+ def restore_locals frame, *locals
443
+ restore_registers_from_frame frame, registers_for_locals(locals)
444
+ end
445
+
446
+ # Helper function for restore_frame and restore_locals.
447
+ #
448
+ # Requires @SAVED_FRAME_LAYOUT, a map from register names to positions
449
+ # in a saved frame, emit_load_word to load registers from memory, and
450
+ # load_value_into_register to load a Voodoo value into a CPU register.
451
+ def restore_registers_from_frame frame, registers
452
+ with_temporary do |temporary|
453
+ load_value_into_register frame, temporary
454
+ registers.each do |register|
455
+ i = @SAVED_FRAME_LAYOUT[register]
456
+ emit_load_word register, temporary, i
457
+ end
458
+ end
459
+ end
460
+
461
+ # Saves the current frame to the given location.
462
+ #
463
+ # Requires @SAVE_FRAME_REGISTERS, an array of names of registers
464
+ # that must be saved, and @LOCAL_REGISTERS, the list of registers
465
+ # that are used to store values of local variables.
466
+ def save_frame frame
467
+ registers_to_save = @SAVE_FRAME_REGISTERS -
468
+ (@saved_registers & @LOCAL_REGISTERS)
469
+ save_registers_to_frame frame, registers_to_save
470
+ end
471
+
472
+ # Saves the current frame to the given location.
473
+ # If locals are given, saves those locals to the
474
+ # frame, too. If no locals are given, saves all
475
+ # locals.
476
+ #
477
+ # Requires @SAVE_FRAME_REGISTERS, an array of names of registers
478
+ # that must be saved, and @LOCAL_REGISTERS, the list of registers
479
+ # that are used to store values of local variables.
480
+ def save_frame_and_locals frame, *locals
481
+ registers_to_save = (@SAVE_FRAME_REGISTERS -
482
+ (@saved_registers & @LOCAL_REGISTERS)) |
483
+ registers_for_locals(locals)
484
+ save_registers_to_frame frame, registers_to_save
485
+ end
486
+
487
+ # Saves local variables to the given frame.
488
+ # If no locals are specified, saves all locals.
489
+ # If locals are specified, saves only the specified ones.
490
+ def save_locals frame, *locals
491
+ save_registers_to_frame frame, registers_for_locals(locals)
492
+ end
493
+
494
+ # Helper function for save_frame and save_locals.
495
+ #
496
+ # Requires @SAVED_FRAME_LAYOUT, a map from register names to positions
497
+ # in a saved frame, emit_store_word to store registers in memory, and
498
+ # load_value_into_register to load a Voodoo value into a CPU register.
499
+ def save_registers_to_frame frame, registers
500
+ return if registers.empty?
501
+ with_temporary do |temporary|
502
+ load_value_into_register frame, temporary
503
+ registers.each do |register|
504
+ i = @SAVED_FRAME_LAYOUT[register]
505
+ emit_store_word register, temporary, i
506
+ end
507
+ end
508
+ end
509
+
510
+ # Returns the number of bytes necessary to save the current frame.
511
+ def saved_frame_size
512
+ @SAVED_FRAME_LAYOUT.length * @WORDSIZE
513
+ end
514
+
515
+ # Sets the current section.
208
516
  def section= name
209
517
  real_name = real_section_name name
210
518
  @section = name
@@ -213,40 +521,90 @@ module Voodoo
213
521
  end
214
522
  end
215
523
 
524
+ # Returns the name of the current section.
525
+ # If a name is given, sets the name of the current section first.
216
526
  def section name = nil
217
527
  self.section = name if name
218
528
  @section
219
529
  end
220
530
 
221
- # Set up +alias_name+ to refer to the same section as +original_name+.
531
+ # Sets up _alias_name_ to refer to the same section as _original_name_.
222
532
  def section_alias alias_name, original_name
223
533
  @section_aliases[alias_name] = original_name
224
534
  end
225
535
 
226
- def in_section name, &block
227
- oldsection = @section
228
- self.section = name
229
- begin
230
- yield
231
- ensure
232
- self.section = oldsection
536
+ # Given n, returns the nearest multiple of @STACK_ALIGNMENT
537
+ # that is >= n.
538
+ def stack_align n
539
+ (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
540
+ end
541
+
542
+ # Tests if a value is a substitution.
543
+ def substitution? x
544
+ x.respond_to?(:[]) && x[0] == :'%'
545
+ end
546
+
547
+ # Substitutes a numeric value for a given substitution key.
548
+ def substitute_number key
549
+ case key
550
+ when :'saved-frame-size'
551
+ saved_frame_size
552
+ else
553
+ @features[key].to_i
233
554
  end
234
555
  end
556
+
557
+ # Tests if a value is a symbol.
558
+ def symbol? value
559
+ value.kind_of? Symbol
560
+ end
235
561
 
236
- # Given an input file name, returns the canonical output file name
237
- # for this code generator.
238
- def output_file_name input_name
239
- input_name.sub(/\.voo$/, '') + @output_file_suffix
562
+ # Test if op is a symmetric binary operation (i.e. it will yield the
563
+ # same result if the order of its source operands is changed).
564
+ def symmetric_binop? op
565
+ [:add, :and, :mul, :or, :xor].member? op
240
566
  end
241
567
 
242
- # Returns the canonical output file suffix for this code generator
243
- def output_file_suffix
244
- @output_file_suffix
568
+ # Executes a block of code, passing the block the name of +n+
569
+ # unused temporary registers.
570
+ #
571
+ # Requires @TEMPORARIES, an array of temporary registers.
572
+ def with_temporaries n, &block
573
+ if @TEMPORARIES.length < n
574
+ raise "Out of temporary registers"
575
+ else
576
+ temporaries = @TEMPORARIES.shift n
577
+ begin
578
+ yield *temporaries
579
+ ensure
580
+ @TEMPORARIES.unshift *temporaries
581
+ end
582
+ end
583
+ end
584
+
585
+ # Executes a block of code, passing the block the name of an unused
586
+ # temporary register as its first argument.
587
+ def with_temporary &block
588
+ with_temporaries 1, &block
589
+ end
590
+
591
+ # Writes generated code to the given IO object.
592
+ def write io
593
+ @sections.each do |section,code|
594
+ unless code.empty?
595
+ io.puts ".section #{section.to_s}"
596
+ io.puts code
597
+ io.puts
598
+ end
599
+ end
245
600
  end
246
601
 
602
+ # Used for scoping. Maintains a symbol table and keeps track of the number
603
+ # of bytes that have been allocated on the stack.
247
604
  class Environment
248
605
  @@gensym_counter = 0
249
606
 
607
+ attr_accessor :bytes, :offset
250
608
  attr_reader :args, :locals, :parent, :symbols
251
609
 
252
610
  # Creates a new environment.
@@ -261,30 +619,54 @@ module Voodoo
261
619
  @args = parent ? parent.args : 0
262
620
  ## Number of local variables
263
621
  @locals = parent ? parent.locals : 0
622
+ ## Offset between base pointer and end of frame.
623
+ @offset = parent ? parent.offset : 0
624
+ ## Number of bytes allocated in this environment.
625
+ @bytes = 0
264
626
  end
265
627
 
266
628
  # Adds symbol as an argument in this environment.
267
- def add_arg symbol
268
- @symbols[symbol] = [:arg, @args]
629
+ # +info+ can be used by the code generator to store information it needs
630
+ # about the symbol.
631
+ def add_arg symbol, info = nil
632
+ @symbols[symbol] = info
269
633
  @args = @args + 1
270
634
  end
271
635
 
272
636
  # Adds each of symbols to the arguments in
273
637
  # this environment.
638
+ # Each entry in +symbols+ can be either a symbol,
639
+ # or an array where the first element is the symbol and the second
640
+ # element is extra information to be stored with the symbol.
274
641
  def add_args symbols
275
- symbols.each { |sym| add_arg sym }
642
+ symbols.each do |sym|
643
+ if sym.respond_to? :[]
644
+ add_arg *sym
645
+ else
646
+ add_arg sym
647
+ end
648
+ end
276
649
  end
277
650
 
278
651
  # Adds symbol as a local variable in this environment.
279
- def add_local symbol
280
- @symbols[symbol] = [:local, @locals]
652
+ def add_local symbol, info = nil
653
+ @symbols[symbol] = info
281
654
  @locals = @locals + 1
282
655
  end
283
656
 
284
657
  # Adds each of symbols to the local variables in
285
658
  # this environment.
659
+ # Each entry in +symbols+ can be either a symbol,
660
+ # or an array where the first element is the symbol and the second
661
+ # element is extra information to be stored with the symbol.
286
662
  def add_locals symbols
287
- symbols.each { |sym| add_local sym }
663
+ symbols.each do |sym|
664
+ if sym.respond_to? :[]
665
+ add_local *sym
666
+ else
667
+ add_local sym
668
+ end
669
+ end
288
670
  end
289
671
 
290
672
  # Generates a new, unique symbol.
@@ -309,5 +691,21 @@ module Voodoo
309
691
  end
310
692
  end
311
693
 
694
+ private
695
+
696
+ # Returns the number of local variable slots required for the given
697
+ # statements, given the current count and required number of slots.
698
+ def count_locals_helper statements, count, max
699
+ statements.each do |statement|
700
+ case statement[0]
701
+ when :block
702
+ max = count_locals_helper statement[1..-1], count, max
703
+ when :let
704
+ count = count + 1
705
+ max = count if count > max
706
+ end
707
+ end
708
+ max
709
+ end
312
710
  end
313
711
  end