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