2 # Common base class for code generators.
4 # Code generators are expected to implement the following methods:
13 # - #output_file_suffix
16 # This class contains base implementations of some of these methods,
17 # which can be used and/or overridden by subclasses.
19 # An example of how to use the code generators provided by this module
20 # is provided on the main page of the documentation of the Voodoo module.
22 class CommonCodeGenerator
25 # These methods implement the public interface of code generators.
27 # Initializes the code generator.
28 # _params_ shall be a hash containing parameters to the code generator,
29 # and shall at least contain the keys <tt>:architecture</tt> and
30 # <tt>:format</tt>, specifying the target architecture and output
31 # format, respectively.
32 def initialize params = {}
33 @architecture = params[:architecture] || Config.default_architecture
34 @format = params[:format] || Config.default_format
37 # Default section aliases. Subclasses can start from scratch by
38 # doing @section_aliases = {}
39 section_alias :code, ".text"
40 section_alias :functions, :code
41 section_alias :data, ".data"
43 @top_level = Environment.initial_environment
44 @environment = @top_level
45 @output_file_suffix = '.o'
47 :voodoo => "1.1" # Voodoo language version
51 # Adds code to the given section.
54 # add :code, [:return, 0]
55 # add :data, [:align], [:label, :xyzzy], [:word, 42]
57 # This method implements the required functionality in terms
58 # of the following methods, which must be implemented by subclasses:
95 def add section, *code
98 keyword, args = action[0], action[1..-1]
103 emit_voodoo :end, :block
105 emit_voodoo :function, *args[0]
106 function args[0], *args[1..-1]
107 emit_voodoo :end, :function
108 when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
109 emit_voodoo keyword, *args[0]
111 falsebody = action[3]
112 send keyword, action[1][0], action[1][1]
113 add section, *truebody
114 if falsebody && !falsebody.empty?
117 add section, *falsebody
119 emit_voodoo :end, :if
121 when :label, :string, :word
128 underscored = keyword.to_s.gsub('-', '_').to_sym
129 send underscored, *args
138 # [formals] an Array of formal parameter names
139 # [code] an Array of actions to be used as the function's body
142 # add_function [:n], [:return, :add, :n, 1]
143 def add_function formals, *code
144 add :functions, [:function, formals] + code
147 # Returns a hash describing the features supported by this code generator.
148 # The keys in this hash are the names of the supported features.
149 # The associated values are strings providing additional information about
150 # the feature, such as a version number.
155 # Returns a new, unused symbol.
160 # Returns true if a feature is supported by this generator,
162 def has_feature? name
163 @features.has_key? name
166 # Given an input file name, returns the canonical output file name
167 # for this code generator.
168 def output_file_name input_name
169 input_name.sub(/\.voo$/, '') + @output_file_suffix
172 # Returns the canonical output file suffix for this code generator
173 def output_file_suffix
179 # These methods are intended to be used by subclasses.
181 # Emits a directive to align the code or data following this
182 # statement. If +alignment+ is given, aligns on the next multiple
183 # of +alignment+ bytes. Else, uses the default alignment for the
186 # This method requires the presence of a default_alignment method
187 # to calculate the default alignment for a given section, and an
188 # emit_align method to actually emit the target-specific code to
189 # align to a multiple of a given number of bytes. An implementation
190 # of default_alignment is provided in this class.
191 def align alignment = nil
192 alignment = default_alignment if alignment == nil
193 emit_align alignment unless alignment == 0
196 # Tests if op is a binary operation.
197 def assymetric_binop? op
198 [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
201 # Tests if a value is an at-expression.
203 value.respond_to?(:[]) && value[0] == :'@'
206 # Test if op is a binary operation
208 assymetric_binop?(op) || symmetric_binop?(op)
211 # Processes code in its own block. Local variables can be
212 # introduced inside the block. They will be deleted at the
216 code.each { |action| add section, action }
220 # Counts the number of local variables created in
221 # a sequence of statements.
222 def count_locals statements
224 each_statement(statements) do |statement|
225 if statement[0] == :let
226 # let introduces a single local
233 # Returns the default alignment for the given section.
234 # If no section is specified, returns the alignment for the current
236 def default_alignment section = @section
237 # Get default alignment
246 # Use data alignment as default
251 # Invokes block with each statement in the given list of statements.
252 # This iterator also descends into nested statements, calling
253 # block first with the outer statement, and then for each inner
255 def each_statement statements, &block
256 statements.each do |statement|
260 each_statement statement[1..-1], &block
262 each_statement statement[2..-1], &block
263 when :ifeq, :ifle, :iflt, :ifge, :ifgt, :ifne
264 each_statement statement[2], &block
265 each_statement(statement[3], &block) if statement.length > 3
270 # Adds code to the current section.
272 @sections[real_section_name(@section)] << code
275 # Emits a comment with the given Voodoo code.
276 def emit_voodoo *code
277 comment code.join(' ')
280 # Adds a function to the current section.
281 def function formals, *code
282 nlocals = count_locals code
283 begin_function formals, nlocals
284 code.each { |action| add section, action }
288 # Tests if a symbol refers to a global.
290 symbol?(symbol) && @environment[symbol] == nil
293 # Executes a block of code using the given section as the current section.
294 def in_section name, &block
295 oldsection = @section
300 self.section = oldsection
304 # Tests if a value is an integer.
306 value.kind_of? Integer
309 # Returns the register in which the nth local (0-based) is stored, or
310 # nil if not stored in a register
312 @LOCAL_REGISTERS[n + number_of_register_arguments]
315 # Calculates the number of register arguments,
316 # given the total number of arguments.
317 def number_of_register_arguments n = @environment.args
318 [n, @NREGISTER_ARGS].min
321 # Calculate the number of stack arguments,
322 # given the total number of arguments.
323 def number_of_stack_arguments n = @environment.args
324 [0, n - @NREGISTER_ARGS].max
327 # Gets the real name of a section.
328 # Given a section name which may be an alias, this method returns the
329 # real name of the section.
330 def real_section_name name
333 x = @section_aliases[name]
334 break if x == nil # Not an alias, exit loop and return name
336 # If name == given_name, we're back where we started. Continuing
337 # would have us loop forever. Just return what we have now.
338 break if name == given_name
343 # Returns true if operand is a register, false otherwise.
348 # Returns true if the nth (0-based) argument is stored in a register
353 # Given some local variable names, returns the registers those variables
354 # are stored in. If no variable names are given, returns all registers
355 # used to store local variables.
357 # Requires @LOCAL_REGISTERS_SET, a set of registers that are used to
358 # store local variables.
359 def registers_for_locals locals = []
360 locals = @environment.symbols.keys if locals.empty?
363 reg = @environment[sym]
364 registers << reg if @LOCAL_REGISTERS_SET.include? @environment[sym]
369 # Restores the frame saved at the given location.
370 def restore_frame frame
371 restore_registers_from_frame frame, @SAVE_FRAME_REGISTERS
374 # Restores local variables from a saved frame.
375 def restore_locals frame, *locals
376 restore_registers_from_frame frame, registers_for_locals(locals)
379 # Helper function for restore_frame and restore_locals.
381 # Requires @SAVED_FRAME_LAYOUT, a map from register names to positions
382 # in a saved frame, emit_load_word to load registers from memory, and
383 # load_value_into_register to load a Voodoo value into a CPU register.
384 def restore_registers_from_frame frame, registers
385 with_temporary do |temporary|
386 load_value_into_register frame, temporary
387 registers.each do |register|
388 i = @SAVED_FRAME_LAYOUT[register]
389 emit_load_word register, temporary, i
394 # Saves the current frame to the given location.
396 registers_to_save = @SAVE_FRAME_REGISTERS -
397 (@saved_registers & @LOCAL_REGISTERS)
398 save_registers_to_frame frame, registers_to_save
401 # Saves local variables to the given frame.
402 # If no locals are specified, saves all locals.
403 # If locals are specified, saves only the specified ones.
404 def save_locals frame, *locals
405 save_registers_to_frame frame, registers_for_locals(locals)
408 # Helper function for save_frame and save_locals.
410 # Requires @SAVED_FRAME_LAYOUT, a map from register names to positions
411 # in a saved frame, emit_store_word to store registers in memory, and
412 # load_value_into_register to load a Voodoo value into a CPU register.
413 def save_registers_to_frame frame, registers
414 with_temporary do |temporary|
415 load_value_into_register frame, temporary
416 registers.each do |register|
417 i = @SAVED_FRAME_LAYOUT[register]
418 emit_store_word register, temporary, i
423 # Returns the number of bytes necessary to save the current frame.
425 @SAVED_FRAME_LAYOUT.length * @WORDSIZE
428 # Sets the current section.
430 real_name = real_section_name name
432 unless @sections.has_key? real_name
433 @sections[real_name] = ''
437 # Returns the name of the current section.
438 # If a name is given, sets the name of the current section first.
439 def section name = nil
440 self.section = name if name
444 # Set up +alias_name+ to refer to the same section as +original_name+.
445 def section_alias alias_name, original_name
446 @section_aliases[alias_name] = original_name
449 # Given n, returns the nearest multiple of @STACK_ALIGNMENT
452 (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
455 # Tests if a value is a substitution.
457 x.respond_to?(:[]) && x[0] == :'%'
460 # Substitutes a numeric value for a given substitution key.
461 def substitute_number key
463 when :'saved-frame-size'
470 # Tests if a value is a symbol.
472 value.kind_of? Symbol
475 # Test if op is a symmetric binary operation (i.e. it will yield the
476 # same result if the order of its source operands is changed).
477 def symmetric_binop? op
478 [:add, :and, :mul, :or, :xor].member? op
481 # Executes a block of code, passing the block the name of +n+
482 # unused temporary registers.
484 # Requires @TEMPORARIES, an array of temporary registers.
485 def with_temporaries n, &block
486 if @TEMPORARIES.length < n
487 raise "Out of temporary registers"
489 temporaries = @TEMPORARIES.shift n
493 @TEMPORARIES.unshift *temporaries
498 # Executes a block of code, passing the block the name of an unused
499 # temporary register as its first argument.
500 def with_temporary &block
501 with_temporaries 1, &block
504 # Writes generated code to the given IO object.
506 @sections.each do |section,code|
508 io.puts ".section #{section.to_s}"
515 # Used for scoping. Maintains a symbol table and keeps track of the number
516 # of bytes that have been allocated on the stack.
520 attr_accessor :bytes, :offset
521 attr_reader :args, :locals, :parent, :symbols
523 # Creates a new environment.
524 # If parent is specified, it will become the new environment's parent.
525 # The new environment inherits all symbols from the parent environment.
526 def initialize parent = nil
527 ## Parent environment
529 ## Symbol lookup table
530 @symbols = parent ? parent.symbols.dup : {}
531 ## Number of arguments
532 @args = parent ? parent.args : 0
533 ## Number of local variables
534 @locals = parent ? parent.locals : 0
535 ## Offset between base pointer and end of frame.
536 @offset = parent ? parent.offset : 0
537 ## Number of bytes allocated in this environment.
541 # Adds symbol as an argument in this environment.
542 # +info+ can be used by the code generator to store information it needs
544 def add_arg symbol, info = nil
545 @symbols[symbol] = info
549 # Adds each of symbols to the arguments in
551 # Each entry in +symbols+ can be either a symbol,
552 # or an array where the first element is the symbol and the second
553 # element is extra information to be stored with the symbol.
555 symbols.each do |sym|
556 if sym.respond_to? :[]
564 # Adds symbol as a local variable in this environment.
565 def add_local symbol, info = nil
566 @symbols[symbol] = info
567 @locals = @locals + 1
570 # Adds each of symbols to the local variables in
572 # Each entry in +symbols+ can be either a symbol,
573 # or an array where the first element is the symbol and the second
574 # element is extra information to be stored with the symbol.
575 def add_locals symbols
576 symbols.each do |sym|
577 if sym.respond_to? :[]
585 # Generates a new, unique symbol.
590 # Looks up symbol in this environment.
595 # Generates a new, unique symbol.
597 @@gensym_counter = @@gensym_counter + 1
598 "_G#{@@gensym_counter}".to_sym
601 # Returns an initial, top-level environment.
602 def self.initial_environment