Class: Voodoo::CommonCodeGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/voodoo/generators/common_code_generator.rb

Overview

Common base class for code generators.

Code generators are expected to implement the following methods:

  • #new

  • #add

  • #add_function

  • #features

  • #gensym

  • #has_feature?

  • #output_file_name

  • #output_file_suffix

  • #saved_frame_size

This class contains base implementations of some of these methods, which can be used and/or overridden by subclasses.

An example of how to use the code generators provided by this module is provided on the main page of the documentation of the Voodoo module.

Direct Known Subclasses

ARMGasGenerator, MIPSGasGenerator, NasmGenerator

Defined Under Namespace

Classes: Environment

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ CommonCodeGenerator

Initializes the code generator. params is a hash of key-value pairs, and can be used to pass additional parameters to the generator. Standard parameters are :architecture and :format, which indicate the architecture and the output format. If these are not supplied, default values will be used. Subclasses may define additional parameters.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/voodoo/generators/common_code_generator.rb', line 35

def initialize params = {}
  @architecture = params[:architecture] || Config.default_architecture
  @format = params[:format] || Config.default_format
  @sections = {}
  @section_aliases = {}
  # Default section aliases. Subclasses can start from scratch by
  # doing @section_aliases = {}
  section_alias :code, ".text"
  section_alias :functions, :code
  section_alias :data, ".data"
  self.section = :code
  @top_level = Environment.initial_environment
  @environment = @top_level
  @output_file_suffix = '.o'
  @open_labels = []     # Labels for which we must emit size annotations
  @relocated_symbols = Set.new
  @features = {
    :voodoo => "1.1"    # Voodoo language version
  }
end

Instance Method Details

#add(section, *code) ⇒ Object

Adds code to the given section.

Examples:

add :code, [:return, 0]
add :data, [:align], [:label, :xyzzy], [:word, 42]

This method implements the required functionality in terms of a number of methods for individual incantations. These must be implemented by subclasses, although default implementations may be provided by CommonCodeGenerator. The following list contains the methods that the add method relies on. Methods that are provided by this class have been marked with a star. In general, these methods will require some functionality to be implemented by subclasses.

  • #align (*)

  • #block (*)

  • #byte

  • #call

  • #comment

  • #emit_label_size

  • #end_if

  • #export (*)

  • #function (*)

  • #goto

  • #group

  • #ifeq

  • #ifge

  • #ifgt

  • #ifle

  • #iflt

  • #ifne

  • #import (*)

  • #label (*)

  • #let

  • #restore_frame (*)

  • #restore_locals (*)

  • #ret

  • #save_frame (*)

  • #save_locals (*)

  • #set

  • #set_byte

  • #set_word

  • #string

  • #word



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/voodoo/generators/common_code_generator.rb', line 101

def add section, *code
  in_section section do
    code.each do |action|
      keyword, args = action[0], action[1..-1]
      case keyword
      when :block
        emit_voodoo :block
        block *args
        emit_voodoo :end, :block
      when :function
        emit_voodoo :function, *args[0]
        function args[0], *args[1..-1]
        emit_voodoo :end, :function
      when :group
        emit_voodoo :group
        old_open_labels = @open_labels
        begin
          @open_labels = []
          args.each { |statement| add section, statement }
        ensure
          @open_labels = old_open_labels
        end
        emit_voodoo :end, :group
      when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
        emit_voodoo keyword, *args[0]
        truebody = action[2]
        falsebody = action[3]
        send keyword, action[1][0], action[1][1]
        add section, *truebody
        if falsebody && !falsebody.empty?
          emit_voodoo :else
          ifelse
          add section, *falsebody
        end
        emit_voodoo :end, :if
        end_if
      when :label, :string, :word
        send *action
      when :return
        emit_voodoo *action
        send :ret, *args
      else
        emit_voodoo *action
 underscored = keyword.to_s.gsub('-', '_').to_sym
        send underscored, *args
      end

      # If we are on top-level and we have open labels and we just
      # emitted something that isn't a label, emit size annotations
      # for all open labels.
      if !@open_labels.empty? && keyword != :label &&
          @environment == @top_level
        @open_labels.each { |name| emit_label_size name }
        @open_labels.clear
      end
    end
  end
end

#add_function(formals, *code) ⇒ Object

Adds a function.

Parameters:

formals

an Array of formal parameter names

code

an Array of actions to be used as the function’s body

Example:

add_function [:n], [:return, :add, :n, 1]


168
169
170
# File 'lib/voodoo/generators/common_code_generator.rb', line 168

def add_function formals, *code
  add :functions, [:function, formals] + code
end

#align(alignment = nil) ⇒ Object

Emits a directive to align the code or data following this statement. If alignment is given, aligns on the next multiple of alignment bytes. Else, uses the default alignment for the current section.

This method requires the presence of a default_alignment method to calculate the default alignment for a given section, and an emit_align method to actually emit the target-specific code to align to a multiple of a given number of bytes. An implementation of default_alignment is provided in this class.



216
217
218
219
# File 'lib/voodoo/generators/common_code_generator.rb', line 216

def align alignment = nil
  alignment = default_alignment if alignment == nil
  emit_align alignment unless alignment == 0
end

#assymetric_binop?(op) ⇒ Boolean

Tests if op is a binary operation.

Returns:

  • (Boolean)


222
223
224
# File 'lib/voodoo/generators/common_code_generator.rb', line 222

def assymetric_binop? op
  [:asr, :bsr, :div, :mod, :rol, :ror, :shl, :shr, :sub].member?(op)
end

#at_expr?(value) ⇒ Boolean

Tests if a value is an at-expression.

Returns:

  • (Boolean)


227
228
229
# File 'lib/voodoo/generators/common_code_generator.rb', line 227

def at_expr? value
  value.respond_to?(:[]) && value[0] == :'@'
end

#binop?(op) ⇒ Boolean

Tests if op is a binary operation.

Returns:

  • (Boolean)


232
233
234
# File 'lib/voodoo/generators/common_code_generator.rb', line 232

def binop? op
  assymetric_binop?(op) || symmetric_binop?(op)
end

#block(*code) ⇒ Object

Processes code in its own block. Local variables can be introduced inside the block. They will be deleted at the end of the block.

This method requires subclasses to implement begin_block and end_block.



242
243
244
245
246
# File 'lib/voodoo/generators/common_code_generator.rb', line 242

def block *code
  begin_block *code
  code.each { |action| add section, action }
  end_block
end

#count_locals(statements) ⇒ Object

Returns the number of local variable slots required by a sequence of statements.



250
251
252
# File 'lib/voodoo/generators/common_code_generator.rb', line 250

def count_locals statements
  count_locals_helper statements, 0, 0
end

#default_alignment(section = @section) ⇒ Object

Returns the default alignment for the given section. If no section is specified, returns the alignment for the current section.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/voodoo/generators/common_code_generator.rb', line 257

def default_alignment section = @section
  # Get default alignment
  case section
  when :code
    @CODE_ALIGNMENT
  when :data
    @DATA_ALIGNMENT
  when :function
    @FUNCTION_ALIGNMENT
  else
    # Use data alignment as default
    @DATA_ALIGNMENT
  end
end

#each_statement(statements, &block) ⇒ Object

Invokes block with each statement in the given list of statements. This iterator also descends into nested statements, calling block first with the outer statement, and then for each inner statement.



276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/voodoo/generators/common_code_generator.rb', line 276

def each_statement statements, &block
  statements.each do |statement|
    yield statement
    case statement[0]
    when :block
      each_statement statement[1..-1], &block
    when :function
      each_statement statement[2..-1], &block
    when :ifeq, :ifle, :iflt, :ifge, :ifgt, :ifne
      each_statement statement[2], &block
      each_statement(statement[3], &block) if statement.length > 3
    end
  end
end

#emit(code) ⇒ Object

Adds code to the current section.



292
293
294
# File 'lib/voodoo/generators/common_code_generator.rb', line 292

def emit code
  @sections[real_section_name(@section)] << code
end

#emit_label(name) ⇒ Object

Emits a label.



297
298
299
# File 'lib/voodoo/generators/common_code_generator.rb', line 297

def emit_label name
  emit "#{name}:\n"
end

#emit_voodoo(*code) ⇒ Object

Emits a comment with the given Voodoo code.



302
303
304
# File 'lib/voodoo/generators/common_code_generator.rb', line 302

def emit_voodoo *code
  comment code.join(' ')
end

#export(*symbols) ⇒ Object

Declares that the given symbols are to be externally visible. Requires subclasses to implement emit_export.



308
309
310
311
312
313
# File 'lib/voodoo/generators/common_code_generator.rb', line 308

def export *symbols
  if real_section_name(section) == ".data"
    @relocated_symbols.merge symbols
  end
  emit_export *symbols
end

#featuresObject

Returns a hash describing the features supported by this code generator. The keys in this hash are the names of the supported features. The associated values are strings providing additional information about the feature, such as a version number.



176
177
178
# File 'lib/voodoo/generators/common_code_generator.rb', line 176

def features
  @features
end

#function(formals, *code) ⇒ Object

Adds a function to the current section. Requires subclasses to implement begin_function and end_function.



317
318
319
320
321
322
# File 'lib/voodoo/generators/common_code_generator.rb', line 317

def function formals, *code
  nlocals = count_locals code
  begin_function formals, nlocals
  code.each { |action| add section, action }
  end_function
end

#gensymObject

Returns a new, unused symbol.



181
182
183
# File 'lib/voodoo/generators/common_code_generator.rb', line 181

def gensym
  Environment.gensym
end

#global?(symbol) ⇒ Boolean

Tests if a symbol refers to a global.

Returns:

  • (Boolean)


325
326
327
# File 'lib/voodoo/generators/common_code_generator.rb', line 325

def global? symbol
  symbol?(symbol) && @environment[symbol] == nil
end

#has_feature?(name) ⇒ Boolean

Returns true if a feature is supported by this generator, false otherwise.

Returns:

  • (Boolean)


187
188
189
# File 'lib/voodoo/generators/common_code_generator.rb', line 187

def has_feature? name
  @features.has_key? name
end

#import(*symbols) ⇒ Object

Declares that the given symbols are imported from an external object. Requires subclasses to implement emit_import.



331
332
333
334
335
336
# File 'lib/voodoo/generators/common_code_generator.rb', line 331

def import *symbols
  if real_section_name(section) == ".data"
    @relocated_symbols.merge symbols
  end
  emit_import *symbols
end

#in_section(name, &block) ⇒ Object

Executes a block of code using the given section as the current section.



339
340
341
342
343
344
345
346
347
# File 'lib/voodoo/generators/common_code_generator.rb', line 339

def in_section name, &block
  oldsection = @section
  self.section = name
  begin
    yield
  ensure
    self.section = oldsection
  end
end

#integer?(value) ⇒ Boolean

Tests if a value is an integer.

Returns:

  • (Boolean)


350
351
352
# File 'lib/voodoo/generators/common_code_generator.rb', line 350

def integer? value
  value.kind_of? Integer
end

#label(name) ⇒ Object

Creates a label. Besides emitting the label name, this also annotates top-level objects with type and size as required for ELF shared libraries. Requires subclasses to emit emit_label and emit_label_type.



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/voodoo/generators/common_code_generator.rb', line 358

def label name
  # If we are at top level, emit type directive and arrange to
  # emit size directive.
  if @environment == @top_level
    case real_section_name(section)
    when ".text"
      type = :code
    else
      type = :data
    end
    emit_label_type name, type
    @open_labels << name
  end
  emit_label name
end

#local_register(n) ⇒ Object

Returns the register in which the nth local (0-based) is stored, or nil if not stored in a register.



376
377
378
# File 'lib/voodoo/generators/common_code_generator.rb', line 376

def local_register n
  @LOCAL_REGISTERS[n + number_of_register_arguments]
end

#number_of_register_arguments(n = @environment.args) ⇒ Object

Calculates the number of register arguments, given the total number of arguments.



382
383
384
# File 'lib/voodoo/generators/common_code_generator.rb', line 382

def number_of_register_arguments n = @environment.args
  [n, @NREGISTER_ARGS].min
end

#number_of_stack_arguments(n = @environment.args) ⇒ Object

Calculate the number of stack arguments, given the total number of arguments.



388
389
390
# File 'lib/voodoo/generators/common_code_generator.rb', line 388

def number_of_stack_arguments n = @environment.args
  [0, n - @NREGISTER_ARGS].max
end

#output_file_name(input_name) ⇒ Object

Given an input file name, returns the canonical output file name for this code generator.



193
194
195
# File 'lib/voodoo/generators/common_code_generator.rb', line 193

def output_file_name input_name
  input_name.sub(/\.voo$/, '') + @output_file_suffix
end

#output_file_suffixObject

Returns the canonical output file suffix for this code generator.



198
199
200
# File 'lib/voodoo/generators/common_code_generator.rb', line 198

def output_file_suffix
  @output_file_suffix
end

#real_section_name(name) ⇒ Object

Gets the real name of a section. Given a section name which may be an alias, this method returns the real name of the section.



395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/voodoo/generators/common_code_generator.rb', line 395

def real_section_name name
  given_name = name
  while true
    x = @section_aliases[name]
    break if x == nil       # Not an alias, exit loop and return name
    name = x
    # If name == given_name, we're back where we started. Continuing
    # would have us loop forever. Just return what we have now.
    break if name == given_name
  end
  name
end

#register?(x) ⇒ Boolean

Returns true if operand is a register, false otherwise.

Returns:

  • (Boolean)


409
410
411
# File 'lib/voodoo/generators/common_code_generator.rb', line 409

def register? x
  x.kind_of? Symbol
end

#register_arg?(n) ⇒ Boolean

Returns true if the nth (0-based) argument is stored in a register.

Returns:

  • (Boolean)


414
415
416
# File 'lib/voodoo/generators/common_code_generator.rb', line 414

def register_arg? n
  n < @NREGISTER_ARGS
end

#registers_for_locals(locals = []) ⇒ Object

Given some local variable names, returns the registers those variables are stored in. If no variable names are given, returns all registers used to store local variables.

Requires @LOCAL_REGISTERS_SET, a set of registers that are used to store local variables.



424
425
426
427
428
429
430
431
432
# File 'lib/voodoo/generators/common_code_generator.rb', line 424

def registers_for_locals locals = []
  locals = @environment.symbols.keys if locals.empty?
  registers = []
  locals.each do |sym|
    reg = @environment[sym]
    registers << reg if @LOCAL_REGISTERS_SET.include? @environment[sym]
  end
  registers
end

#restore_frame(frame) ⇒ Object

Restores the frame saved at the given location. Requires @SAVE_FRAME_REGISTERS, an array of register names that must be restored.



437
438
439
# File 'lib/voodoo/generators/common_code_generator.rb', line 437

def restore_frame frame
  restore_registers_from_frame frame, @SAVE_FRAME_REGISTERS
end

#restore_locals(frame, *locals) ⇒ Object

Restores local variables from a saved frame.



442
443
444
# File 'lib/voodoo/generators/common_code_generator.rb', line 442

def restore_locals frame, *locals
  restore_registers_from_frame frame, registers_for_locals(locals)
end

#restore_registers_from_frame(frame, registers) ⇒ Object

Helper function for restore_frame and restore_locals.

Requires @SAVED_FRAME_LAYOUT, a map from register names to positions in a saved frame, emit_load_word to load registers from memory, and load_value_into_register to load a Voodoo value into a CPU register.



451
452
453
454
455
456
457
458
459
# File 'lib/voodoo/generators/common_code_generator.rb', line 451

def restore_registers_from_frame frame, registers
  with_temporary do |temporary|
    load_value_into_register frame, temporary
    registers.each do |register|
      i = @SAVED_FRAME_LAYOUT[register]
      emit_load_word register, temporary, i
    end
  end
end

#save_frame(frame) ⇒ Object

Saves the current frame to the given location.

Requires @SAVE_FRAME_REGISTERS, an array of names of registers that must be saved, and @LOCAL_REGISTERS, the list of registers that are used to store values of local variables.



466
467
468
469
470
# File 'lib/voodoo/generators/common_code_generator.rb', line 466

def save_frame frame
  registers_to_save = @SAVE_FRAME_REGISTERS -
    (@saved_registers & @LOCAL_REGISTERS)
  save_registers_to_frame frame, registers_to_save
end

#save_frame_and_locals(frame, *locals) ⇒ Object

Saves the current frame to the given location. If locals are given, saves those locals to the frame, too. If no locals are given, saves all locals.

Requires @SAVE_FRAME_REGISTERS, an array of names of registers that must be saved, and @LOCAL_REGISTERS, the list of registers that are used to store values of local variables.



480
481
482
483
484
485
# File 'lib/voodoo/generators/common_code_generator.rb', line 480

def save_frame_and_locals frame, *locals
  registers_to_save = (@SAVE_FRAME_REGISTERS -
    (@saved_registers & @LOCAL_REGISTERS)) |
    registers_for_locals(locals)
  save_registers_to_frame frame, registers_to_save
end

#save_locals(frame, *locals) ⇒ Object

Saves local variables to the given frame. If no locals are specified, saves all locals. If locals are specified, saves only the specified ones.



490
491
492
# File 'lib/voodoo/generators/common_code_generator.rb', line 490

def save_locals frame, *locals
  save_registers_to_frame frame, registers_for_locals(locals)
end

#save_registers_to_frame(frame, registers) ⇒ Object

Helper function for save_frame and save_locals.

Requires @SAVED_FRAME_LAYOUT, a map from register names to positions in a saved frame, emit_store_word to store registers in memory, and load_value_into_register to load a Voodoo value into a CPU register.



499
500
501
502
503
504
505
506
507
508
# File 'lib/voodoo/generators/common_code_generator.rb', line 499

def save_registers_to_frame frame, registers
  return if registers.empty?
  with_temporary do |temporary|
    load_value_into_register frame, temporary
    registers.each do |register|
      i = @SAVED_FRAME_LAYOUT[register]
      emit_store_word register, temporary, i
    end
  end
end

#saved_frame_sizeObject

Returns the number of bytes necessary to save the current frame.



511
512
513
# File 'lib/voodoo/generators/common_code_generator.rb', line 511

def saved_frame_size
  @SAVED_FRAME_LAYOUT.length * @WORDSIZE
end

#section(name = nil) ⇒ Object

Returns the name of the current section. If a name is given, sets the name of the current section first.



526
527
528
529
# File 'lib/voodoo/generators/common_code_generator.rb', line 526

def section name = nil
  self.section = name if name
  @section
end

#section=(name) ⇒ Object

Sets the current section.



516
517
518
519
520
521
522
# File 'lib/voodoo/generators/common_code_generator.rb', line 516

def section= name
  real_name = real_section_name name
  @section = name
  unless @sections.has_key? real_name
    @sections[real_name] = ''
  end
end

#section_alias(alias_name, original_name) ⇒ Object

Sets up alias_name to refer to the same section as original_name.



532
533
534
# File 'lib/voodoo/generators/common_code_generator.rb', line 532

def section_alias alias_name, original_name
  @section_aliases[alias_name] = original_name
end

#stack_align(n) ⇒ Object

Given n, returns the nearest multiple of @STACK_ALIGNMENT that is >= n.



538
539
540
# File 'lib/voodoo/generators/common_code_generator.rb', line 538

def stack_align n
  (n + @STACK_ALIGNMENT - 1) / @STACK_ALIGNMENT * @STACK_ALIGNMENT
end

#substitute_number(key) ⇒ Object

Substitutes a numeric value for a given substitution key.



548
549
550
551
552
553
554
555
# File 'lib/voodoo/generators/common_code_generator.rb', line 548

def substitute_number key
  case key
  when :'saved-frame-size'
	saved_frame_size
  else
	@features[key].to_i
  end
end

#substitution?(x) ⇒ Boolean

Tests if a value is a substitution.

Returns:

  • (Boolean)


543
544
545
# File 'lib/voodoo/generators/common_code_generator.rb', line 543

def substitution? x
  x.respond_to?(:[]) && x[0] == :'%'
end

#symbol?(value) ⇒ Boolean

Tests if a value is a symbol.

Returns:

  • (Boolean)


558
559
560
# File 'lib/voodoo/generators/common_code_generator.rb', line 558

def symbol? value
  value.kind_of? Symbol
end

#symmetric_binop?(op) ⇒ Boolean

Test if op is a symmetric binary operation (i.e. it will yield the same result if the order of its source operands is changed).

Returns:

  • (Boolean)


564
565
566
# File 'lib/voodoo/generators/common_code_generator.rb', line 564

def symmetric_binop? op
  [:add, :and, :mul, :or, :xor].member? op
end

#with_temporaries(n, &block) ⇒ Object

Executes a block of code, passing the block the name of n unused temporary registers.

Requires @TEMPORARIES, an array of temporary registers.



572
573
574
575
576
577
578
579
580
581
582
583
# File 'lib/voodoo/generators/common_code_generator.rb', line 572

def with_temporaries n, &block
  if @TEMPORARIES.length < n
    raise "Out of temporary registers"
  else
    temporaries = @TEMPORARIES.shift n
    begin
      yield *temporaries
    ensure
      @TEMPORARIES.unshift *temporaries
    end
  end
end

#with_temporary(&block) ⇒ Object

Executes a block of code, passing the block the name of an unused temporary register as its first argument.



587
588
589
# File 'lib/voodoo/generators/common_code_generator.rb', line 587

def with_temporary &block
  with_temporaries 1, &block
end

#write(io) ⇒ Object

Writes generated code to the given IO object.



592
593
594
595
596
597
598
599
600
# File 'lib/voodoo/generators/common_code_generator.rb', line 592

def write io
  @sections.each do |section,code|
    unless code.empty?
      io.puts ".section #{section.to_s}"
      io.puts code
      io.puts
    end
  end
end