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.
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
#target ⇒ Object (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.
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_target ⇒ Object
(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, = {}) 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, = {}) 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, ) 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 :"#{[: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, = {}) 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 |