Class: Tem::Builders::Assembler

Inherits:
Object
  • Object
show all
Defined in:
lib/tem/builders/assembler.rb

Overview

Builder class for the code assembler builder.

Defined Under Namespace

Modules: CodeBuilderBase Classes: ProxyBase

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target) ⇒ Assembler

Creates a builder targeting a module / class.



244
245
246
247
# File 'lib/tem/builders/assembler.rb', line 244

def initialize(target)
  @target = target
  @isa, @abi, @proxy = nil, nil, nil    
end

Instance Attribute Details

#targetObject (readonly)

The module / class impacted by the builder.



241
242
243
# File 'lib/tem/builders/assembler.rb', line 241

def target
  @target
end

Class Method Details

.define_assembler(class_or_module) {|new(class_or_module)| ... } ⇒ Object

Creates a builder targeting a module / class.

The given parameter should be a class or module.

Yields:

  • (new(class_or_module))


16
17
18
# File 'lib/tem/builders/assembler.rb', line 16

def self.define_assembler(class_or_module)  # :yields: abi
  yield new(class_or_module)
end

Instance Method Details

#augment_targetObject

(private) Augments the target with the assemble method.



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/tem/builders/assembler.rb', line 210

def augment_target
  # Capture this data in the closure.
  proxy_class = @proxy_class
  builder_class = @builder_class
  defines = Proc.new do
    # Assembles code.
    def assemble(&block)
      _assemble block
    end
    
    # Internal method for assembling code.
    #
    # We need to use a block to define this method, so we can capture the
    # outer variables (proxy_class and builder_class) in its closure. However,
    # blocks can't take blocks as parameters (at least not in MRI 1.8). So
    # we use a regular method definition (assemble) which wraps the block
    # into a Proc received by _assemble.
    define_method :_assemble do |block|
      code_builder = builder_class.new
      code_builder.start_assembling
      proxy = proxy_class.new(code_builder)
      block.call proxy
      code_builder.done_assembling proxy
    end
    private :_assemble
  end
  @target.class_eval(&defines)
  (class << @target; self; end).module_eval(&defines)        
end

#data_directive(name, options = {}) ⇒ Object

Defines the methods for implementing a data-emitting directive.

The following method is defined in the proxy for a directive named ‘name’:

* name(abi_type, values = 1) -> emits the given values as abi_type


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/tem/builders/assembler.rb', line 133

def data_directive(name, options = {})
  unless @proxy_class
    raise "target_isa must be called before other builder methods"
  end
  # Capture this in the closure.
  abi = @abi
  proxy_defines = Proc.new do
    define_method name.to_sym do |abi_type, values|
      values = [values] unless values.instance_of? Array
      data = []
      values.each { |value| data += abi.send :"to_#{abi_type}", value }
      @assembler.emit_bytes :immed, :emit => data
    end
  end    
  @proxy_class.class_eval(&proxy_defines)
  (class << @proxy_class; self; end).module_eval(&proxy_defines)    
end

#label_directive(name, options = {}) ⇒ Object

Defines the methods for implementing a labeling directive.

The following method is defined in the proxy for a directive named ‘name’:

* name(label_name) -> creates a label named label_name at the current byte


74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/tem/builders/assembler.rb', line 74

def label_directive(name, options = {})
  unless @proxy_class
    raise "target_isa must be called before other builder methods"
  end
  proxy_defines = Proc.new do
    define_method name.to_sym do |label_name|
      @assembler.emit_label label_name.to_sym
    end
  end    
  @proxy_class.class_eval(&proxy_defines)
  (class << @proxy_class; self; end).module_eval(&proxy_defines)    
end

#special_label_directive(name, label_name) ⇒ Object

Defines the methods for implementing a special label directive.

The following method is defined in the proxy for a directive named ‘name’:

* name -> creates a label named label_name at the current byte


91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/tem/builders/assembler.rb', line 91

def special_label_directive(name, label_name)
  unless @proxy_class
    raise "target_isa must be called before other builder methods"
  end
  proxy_defines = Proc.new do
    define_method name.to_sym do
      @assembler.emit_label label_name.to_sym
    end
  end    
  @proxy_class.class_eval(&proxy_defines)
  (class << @proxy_class; self; end).module_eval(&proxy_defines)
end

#stack_directive(name, options) ⇒ Object

Defines the methods for implementing a stack directive.

The following options are supported:

label:: the label serving as the stack marker (required)
slot_type:: the ABI type representing a stack slot

The following method is defined in the proxy for a directive named ‘name’:

* name(slots = 0) -> places a stack marker and allocates stack slots


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/tem/builders/assembler.rb', line 43

def stack_directive(name, options)
  unless @proxy_class
    raise "target_isa must be called before other builder methods"
  end
  # Capture these in the closure.
  stack_label = options[:label]
  slot_length = @abi.send :"#{options[:slot_type]}_length" 
  proxy_defines = Proc.new do
    define_method name.to_sym do |*args|
      case args.length
      when 0
        slots = 0
      when 1
        slots = args.first
      else
        raise "#{name}: given #{args.length} arguments, wanted at most 1"
      end
      @assembler.emit_label stack_label
      if slots > 0
        @assembler.emit_bytes name, :emit => Array.new(slots * slot_length, 0)
      end
    end
  end    
  @proxy_class.class_eval(&proxy_defines)
  (class << @proxy_class; self; end).module_eval(&proxy_defines)    
end

#target_isa(isa_module) ⇒ Object

Defines the ISA targeted by the assembler.

This method should be called early in the assembler definition. It creates the proxy and builder classes for the assembling process.



24
25
26
27
28
29
30
31
32
33
# File 'lib/tem/builders/assembler.rb', line 24

def target_isa(isa_module)    
  @isa = isa_module
  target.const_set :Isa, @isa
  @abi = @isa.const_get :Abi
  target.const_set :Abi, @abi
  
  define_proxy_class
  define_builder_class
  augment_target
end

#zeros_directive(name, options = {}) ⇒ Object

Defines the methods for implementing a zero-inserting directive.

The following method is defined in the proxy for a directive named ‘name’:

* name(abi_type, count = 1) -> creates count zeros of abi_type


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/tem/builders/assembler.rb', line 108

def zeros_directive(name, options = {})
  unless @proxy_class
    raise "target_isa must be called before other builder methods"
  end
  # Capture this in the closure.
  abi = @abi
  proxy_defines = Proc.new do
    define_method name.to_sym do |*args|
      if args.length == 1 || args.length == 2
        type_name, count = args[0], args[1] || 1
      else
        raise "#{name}: given #{args.length} arguments, wanted 1 or 2"
      end
      bytes = count * abi.send(:"#{type_name}_length")
      @assembler.emit_bytes name, :emit => Array.new(bytes, 0)
    end
  end    
  @proxy_class.class_eval(&proxy_defines)
  (class << @proxy_class; self; end).module_eval(&proxy_defines)    
end