some updates to the documentation
[voodoo-lang.git] / lib / voodoo / generators / common_code_generator.rb
blobae59bfaba642567906144d0db3f0f10af49edaaf
1 require 'set'
3 module Voodoo
4   # Common base class for code generators.
5   #
6   # Code generators are expected to implement the following methods:
7   #
8   # - #new
9   # - #add
10   # - #add_function
11   # - #features
12   # - #gensym
13   # - #has_feature?
14   # - #output_file_name
15   # - #output_file_suffix
16   # - #saved_frame_size
17   #
18   # This class contains base implementations of some of these methods,
19   # which can be used and/or overridden by subclasses.
20   #
21   # An example of how to use the code generators provided by this module
22   # is provided on the main page of the documentation of the Voodoo module.
23   #
24   class CommonCodeGenerator
25     # == Public Interface
26     #
27     # These methods implement the public interface of code generators.
29     # Initializes the code generator.
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.
35     def initialize params = {}
36       @architecture = params[:architecture] || Config.default_architecture
37       @format = params[:format] || Config.default_format
38       @sections = {}
39       @section_aliases = {}
40       # Default section aliases. Subclasses can start from scratch by
41       # doing @section_aliases = {}
42       section_alias :code, ".text"
43       section_alias :functions, :code
44       section_alias :data, ".data"
45       self.section = :code
46       @top_level = Environment.initial_environment
47       @environment = @top_level
48       @output_file_suffix = '.o'
49       @open_labels = []     # Labels for which we must emit size annotations
50       @relocated_symbols = Set.new
51       @features = {
52         :voodoo => "1.1"    # Voodoo language version
53       }
54     end
56     # Adds code to the given section.
57     #
58     # Examples:
59     #   add :code, [:return, 0]
60     #   add :data, [:align], [:label, :xyzzy], [:word, 42]
61     #
62     # This method implements the required functionality in terms
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.
69     #
70     # - #align (*)
71     # - #block (*)
72     # - #byte
73     # - #call
74     # - #comment
75     # - #emit_label_size
76     # - #end_if
77     # - #export (*)
78     # - #function (*)
79     # - #goto
80     # - #group
81     # - #ifeq
82     # - #ifge
83     # - #ifgt
84     # - #ifle
85     # - #iflt
86     # - #ifne
87     # - #import (*)
88     # - #label (*)
89     # - #let
90     # - #restore_frame (*)
91     # - #restore_locals (*)
92     # - #ret
93     # - #save_frame (*)
94     # - #save_locals (*)
95     # - #set
96     # - #set_byte
97     # - #set_word
98     # - #string
99     # - #word
100     #
101     def add section, *code
102       in_section section do
103         code.each do |action|
104           keyword, args = action[0], action[1..-1]
105           case keyword
106           when :block
107             emit_voodoo :block
108             block *args
109             emit_voodoo :end, :block
110           when :function
111             emit_voodoo :function, *args[0]
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
124           when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
125             emit_voodoo keyword, *args[0]
126             truebody = action[2]
127             falsebody = action[3]
128             send keyword, action[1][0], action[1][1]
129             add section, *truebody
130             if falsebody && !falsebody.empty?
131               emit_voodoo :else
132               ifelse
133               add section, *falsebody
134             end
135             emit_voodoo :end, :if
136             end_if
137           when :label, :string, :word
138             send *action
139           when :return
140             emit_voodoo *action
141             send :ret, *args
142           else
143             emit_voodoo *action
144             underscored = keyword.to_s.gsub('-', '_').to_sym
145             send underscored, *args
146           end
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
155           end
156         end
157       end
158     end
160     # Adds a function.
161     #
162     # Parameters:
163     # [formals] an Array of formal parameter names
164     # [code] an Array of actions to be used as the function's body
165     #
166     # Example:
167     #   add_function [:n], [:return, :add, :n, 1]
168     def add_function formals, *code
169       add :functions, [:function, formals] + code
170     end
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
180     # Returns a new, unused symbol.
181     def gensym
182       Environment.gensym
183     end
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
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
197     # Returns the canonical output file suffix for this code generator.
198     def output_file_suffix
199       @output_file_suffix
200     end
202     # == Helper Methods
203     #
204     # These methods are intended to be used by subclasses.
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
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
226     # Tests if a value is an at-expression.
227     def at_expr? value
228       value.respond_to?(:[]) && value[0] == :'@'
229     end
231     # Tests if op is a binary operation.
232     def binop? op
233       assymetric_binop?(op) || symmetric_binop?(op)
234     end
236     # Processes code in its own block. Local variables can be
237     # introduced inside the block. They will be deleted at the
238     # end of the block.
239     #
240     # This method requires subclasses to implement begin_block and
241     # end_block.
242     def block *code
243       begin_block *code
244       code.each { |action| add section, action }
245       end_block
246     end
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
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
272     # Invokes block with each statement in the given list of statements.
273     # This iterator also descends into nested statements, calling
274     # block first with the outer statement, and then for each inner
275     # statement.
276     def each_statement statements, &block
277       statements.each do |statement|
278         yield statement
279         case statement[0]
280         when :block
281           each_statement statement[1..-1], &block
282         when :function
283           each_statement statement[2..-1], &block
284         when :ifeq, :ifle, :iflt, :ifge, :ifgt, :ifne
285           each_statement statement[2], &block
286           each_statement(statement[3], &block) if statement.length > 3
287         end
288       end
289     end
291     # Adds code to the current section.
292     def emit code
293       @sections[real_section_name(@section)] << code
294     end
296     # Emits a label.
297     def emit_label name
298       emit "#{name}:\n"
299     end
301     # Emits a comment with the given Voodoo code.
302     def emit_voodoo *code
303       comment code.join(' ')
304     end
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
315     # Adds a function to the current section.
316     # Requires subclasses to implement begin_function and end_function.
317     def function formals, *code
318       nlocals = count_locals code
319       begin_function formals, nlocals
320       code.each { |action| add section, action }
321       end_function
322     end
324     # Tests if a symbol refers to a global.
325     def global? symbol
326       symbol?(symbol) && @environment[symbol] == nil
327     end
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
336     end
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
349     # Tests if a value is an integer.
350     def integer? value
351       value.kind_of? Integer
352     end
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
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]
378     end
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
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
392     # Gets the real name of a section.
393     # Given a section name which may be an alias, this method returns the
394     # real name of the section.
395     def real_section_name name
396       given_name = name
397       while true
398         x = @section_aliases[name]
399         break if x == nil       # Not an alias, exit loop and return name
400         name = x
401         # If name == given_name, we're back where we started. Continuing
402         # would have us loop forever. Just return what we have now.
403         break if name == given_name
404       end
405       name
406     end
408     # Returns true if operand is a register, false otherwise.
409     def register? x
410       x.kind_of? Symbol
411     end
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
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
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
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
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
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
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
515     # Sets the current section.
516     def section= name
517       real_name = real_section_name name
518       @section = name
519       unless @sections.has_key? real_name
520         @sections[real_name] = ''
521       end
522     end
524     # Returns the name of the current section.
525     # If a name is given, sets the name of the current section first.
526     def section name = nil
527       self.section = name if name
528       @section
529     end
531     # Sets up _alias_name_ to refer to the same section as _original_name_.
532     def section_alias alias_name, original_name
533       @section_aliases[alias_name] = original_name
534     end
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
554       end
555     end
556     
557     # Tests if a value is a symbol.
558     def symbol? value
559       value.kind_of? Symbol
560     end
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
566     end
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
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
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
600     end
602     # Used for scoping. Maintains a symbol table and keeps track of the number
603     # of bytes that have been allocated on the stack.
604     class Environment
605       @@gensym_counter = 0
607       attr_accessor :bytes, :offset
608       attr_reader :args, :locals, :parent, :symbols
610       # Creates a new environment.
611       # If parent is specified, it will become the new environment's parent.
612       # The new environment inherits all symbols from the parent environment.
613       def initialize parent = nil
614         ## Parent environment
615         @parent = parent
616         ## Symbol lookup table
617         @symbols = parent ? parent.symbols.dup : {}
618         ## Number of arguments
619         @args = parent ? parent.args : 0
620         ## Number of local variables
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
626       end
628       # Adds symbol as an argument in this environment.
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
633         @args = @args + 1
634       end
636       # Adds each of symbols to the arguments in
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.
641       def add_args symbols
642         symbols.each do |sym|
643           if sym.respond_to? :[]
644             add_arg *sym
645           else
646             add_arg sym
647           end
648         end
649       end
651       # Adds symbol as a local variable in this environment.
652       def add_local symbol, info = nil
653         @symbols[symbol] = info
654         @locals = @locals + 1
655       end
657       # Adds each of symbols to the local variables in
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.
662       def add_locals symbols
663         symbols.each do |sym|
664           if sym.respond_to? :[]
665             add_local *sym
666           else
667             add_local sym
668           end
669         end
670       end
672       # Generates a new, unique symbol.
673       def gensym
674         Environment.gensym
675       end
677       # Looks up symbol in this environment.
678       def [] symbol
679         @symbols[symbol]
680       end
682       # Generates a new, unique symbol.
683       def self.gensym
684         @@gensym_counter = @@gensym_counter + 1
685         "_G#{@@gensym_counter}".to_sym
686       end
688       # Returns an initial, top-level environment.
689       def self.initial_environment
690         Environment.new
691       end
692     end
694     private
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
710   end