Class: Tem::Builders::Assembler
- Inherits:
-
Object
- Object
- Tem::Builders::Assembler
- 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
-
#target ⇒ Object
readonly
The module / class impacted by the builder.
Class Method Summary collapse
-
.define_assembler(class_or_module) {|new(class_or_module)| ... } ⇒ Object
Creates a builder targeting a module / class.
Instance Method Summary collapse
-
#augment_target ⇒ Object
(private) Augments the target with the assemble method.
-
#data_directive(name, options = {}) ⇒ Object
Defines the methods for implementing a data-emitting directive.
-
#initialize(target) ⇒ Assembler
constructor
Creates a builder targeting a module / class.
-
#label_directive(name, options = {}) ⇒ Object
Defines the methods for implementing a labeling directive.
-
#special_label_directive(name, label_name) ⇒ Object
Defines the methods for implementing a special label directive.
-
#stack_directive(name, options) ⇒ Object
Defines the methods for implementing a stack directive.
-
#target_isa(isa_module) ⇒ Object
Defines the ISA targeted by the assembler.
-
#zeros_directive(name, options = {}) ⇒ Object
Defines the methods for implementing a zero-inserting directive.
Constructor Details
#initialize(target) ⇒ Assembler
Creates a builder targeting a module / class.
237 238 239 240 |
# File 'lib/tem/builders/assembler.rb', line 237 def initialize(target) @target = target @isa, @abi, @proxy = nil, nil, nil end |
Instance Attribute Details
#target ⇒ Object (readonly)
The module / class impacted by the builder.
234 235 236 |
# File 'lib/tem/builders/assembler.rb', line 234 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.
9 10 11 |
# File 'lib/tem/builders/assembler.rb', line 9 def self.define_assembler(class_or_module) # :yields: abi yield new(class_or_module) end |
Instance Method Details
#augment_target ⇒ Object
(private) Augments the target with the assemble method.
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/tem/builders/assembler.rb', line 203 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
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/tem/builders/assembler.rb', line 126 def data_directive(name, = {}) 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
67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/tem/builders/assembler.rb', line 67 def label_directive(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 |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
84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/tem/builders/assembler.rb', line 84 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
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/tem/builders/assembler.rb', line 36 def stack_directive(name, ) unless @proxy_class raise "target_isa must be called before other builder methods" end # Capture these in the closure. stack_label = [: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.
17 18 19 20 21 22 23 24 25 26 |
# File 'lib/tem/builders/assembler.rb', line 17 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
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/tem/builders/assembler.rb', line 101 def zeros_directive(name, = {}) 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 |