Module: Ikra::Symbolic::ArrayCommand

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

@@unique_id =
1

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Types::RubyType

#class_id, #inspect, #is_primitive?, #is_union_type?, #should_generate_self_arg?, #to_array_type, #to_str, #to_union_type

Methods included from ParallelOperations

#&, #*, #+, #-, #/, #<, #<=, #>, #>=, #^, #pcombine, #pmap, #preduce, #pstencil, #pzip, #|

Instance Attribute Details

#blockObject

Returns the block of the parallel section or [nil] if none.



175
176
177
# File 'lib/symbolic/symbolic.rb', line 175

def block
  @block
end

#block_sizeObject (readonly)

Returns the value of attribute block_size.



158
159
160
# File 'lib/symbolic/symbolic.rb', line 158

def block_size
  @block_size
end

#generator_nodeObject (readonly)

A reference to the AST send node that generated this [ArrayCommand] (if inside a host section).



179
180
181
# File 'lib/symbolic/symbolic.rb', line 179

def generator_node
  @generator_node
end

#gpu_result_pointerObject

This field can only be used if keep is true



172
173
174
# File 'lib/symbolic/symbolic.rb', line 172

def gpu_result_pointer
  @gpu_result_pointer
end

#inputObject (readonly)

An array of commands that serve as input to this command. The number of input commands depends on the type of the command.



166
167
168
# File 'lib/symbolic/symbolic.rb', line 166

def input
  @input
end

#keepObject (readonly)

Indicates if result should be kept on the GPU for further processing.



169
170
171
# File 'lib/symbolic/symbolic.rb', line 169

def keep
  @keep
end

#unique_idObject (readonly)

Fixnum

Returns a unique ID for this command. It is used during name mangling in

the code generator to determine the name of array identifiers (and do other stuff?).



162
163
164
# File 'lib/symbolic/symbolic.rb', line 162

def unique_id
  @unique_id
end

Class Method Details

.included(base) ⇒ Object



8
9
10
# File 'lib/types/types/array_command_type.rb', line 8

def self.included(base)
    base.extend(ClassMethods)
end

.reset_unique_idObject



183
184
185
# File 'lib/symbolic/symbolic.rb', line 183

def self.reset_unique_id
    @@unique_id = 1
end

Instance Method Details

#==(other) ⇒ Object

Methods for equality and hash. These methods are required for comparing array commands for equality. This is necessary because every array command can also act as a type. Types must be comparable for equality.



225
226
227
228
229
230
231
232
233
234
# File 'lib/symbolic/symbolic.rb', line 225

def ==(other)
    # Important: ArrayCommands may be created over and over during type inference.
    # It is important that we compare values and not identities!
    
    return self.class == other.class &&
        block_size == other.block_size &&
        input == other.input &&
        keep == other.keep &&
        block_def_node == other.block_def_node
end

#[](index) ⇒ Object

—– ARRAY METHODS —–



249
250
251
252
# File 'lib/symbolic/symbolic.rb', line 249

def [](index)
    execute
    return @result[index]
end

#block_def_nodeObject

Returns the abstract syntax tree for a parallel section.



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/symbolic/symbolic.rb', line 334

def block_def_node
    if @ast == nil
        if block == nil
            return nil
        end

        # Get array of block parameter names
        block_params = block.parameters.map do |param|
            param[1]
        end

        parser_local_vars = command_binding.local_variables + block_params
        source = Parsing.parse_block(block, parser_local_vars)
        @ast = AST::BlockDefNode.new(
            parameters: block_params,
            ruby_block: block,      # necessary to get binding
            body: AST::Builder.from_parser_ast(source))
    end

    # Ensure `return` is there
    @ast.accept(Translator::LastStatementReturnsVisitor.new)

    return @ast
end

#block_parameter_namesArray(Symbol)

Returns a collection of the names of all block parameters.

Returns:



316
317
318
# File 'lib/symbolic/symbolic.rb', line 316

def block_parameter_names
    return block_def_node.parameters
end

#command_bindingObject

Returns the binding of this command. It is used to retrieve lexical variables that are used inside this parallel section.



361
362
363
364
365
366
367
368
369
# File 'lib/symbolic/symbolic.rb', line 361

def command_binding
    if @command_binding != nil
        return @command_binding
    elsif block != nil
        return block.binding
    else
        return nil
    end
end

#command_translator_classObject

The class or subclass of [CommandTranslator] that should be used for translating this command. May be overridden.



273
274
275
# File 'lib/symbolic/symbolic.rb', line 273

def command_translator_class
    return Translator::CommandTranslator
end

#dimensionsObject



327
328
329
330
331
# File 'lib/symbolic/symbolic.rb', line 327

def dimensions
    # Dimensions are defined in a root command. First input currently determines the
    # dimensions (even if there are multiple root commands).
    return input.first.command.dimensions
end

#each(&block) ⇒ Object



254
255
256
257
258
259
260
261
# File 'lib/symbolic/symbolic.rb', line 254

def each(&block)
    next_index = 0

    while next_index < size
        yield(self[next_index])
        next_index += 1
    end
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


240
241
242
# File 'lib/symbolic/symbolic.rb', line 240

def eql?(other)
    return self == other
end

#executeObject



277
278
279
280
281
# File 'lib/symbolic/symbolic.rb', line 277

def execute
    if @result == nil
        @result = command_translator_class.translate_command(self).execute
    end
end

#externalsObject

Returns a collection of external objects that are accessed within a parallel section.



393
394
395
# File 'lib/symbolic/symbolic.rb', line 393

def externals
    return lexical_externals.keys
end

#has_previous_result?Boolean

Returns:

  • (Boolean)


310
311
312
# File 'lib/symbolic/symbolic.rb', line 310

def has_previous_result?
    return !gpu_result_pointer.nil?
end

#hashObject



236
237
238
# File 'lib/symbolic/symbolic.rb', line 236

def hash
    return (block_size.hash + input.hash + keep.hash + block_def_node.hash) % 7546777
end

#ikra_typeObject

Every [ArrayCommand] has itself as an Ikra type. This integrates well with the current type inference approach and ‘ruby_core`.



109
110
111
# File 'lib/types/types/array_command_type.rb', line 109

def ikra_type
    return self
end

#initialize(block: nil, block_ast: nil, block_size: nil, keep: nil, generator_node: nil, command_binding: nil) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/symbolic/symbolic.rb', line 191

def initialize(
    block: nil, 
    block_ast: nil, 
    block_size: nil, 
    keep: nil, 
    generator_node: nil,
    command_binding: nil)

    super()

    set_unique_id

    # Set instance variables
    @block_size = block_size
    @keep = keep
    @generator_node = generator_node
    @command_binding = command_binding

    if block != nil and block_ast == nil
        @block = block
    elsif block == nil and block_ast != nil
        @ast = block_ast
    elsif block != nil and block_ast != nil
        raise ArgumentError.new("`block` and `block_ast` given. Expected at most one.")
    end
end

#lexical_externalsHash{Symbol => Object}

Returns a collection of lexical variables that are accessed within a parallel section.

Returns:



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/symbolic/symbolic.rb', line 374

def lexical_externals
    if block_def_node != nil && command_binding != nil
        all_lexical_vars = command_binding.local_variables
        lexical_vars_enumerator = AST::LexicalVariablesEnumerator.new(all_lexical_vars)
        block_def_node.accept(lexical_vars_enumerator)
        accessed_variables = lexical_vars_enumerator.lexical_variables

        result = Hash.new
        for var_name in accessed_variables
            result[var_name] = command_binding.local_variable_get(var_name)
        end

        return result
    else
        return {}
    end 
end

#pack(fmt) ⇒ Object



263
264
265
266
# File 'lib/symbolic/symbolic.rb', line 263

def pack(fmt)
    execute
    return @result.pack(fmt)
end

#post_execute(environment) ⇒ Object

This method is executed after execution of the parallel section has finish. The boolean return value indicates if a change has been registered or not.



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/symbolic/symbolic.rb', line 289

def post_execute(environment)
    if keep
        # The (temporary) result of this command should be kept on the GPU. Store a
        # pointer to the result in global memory in an instance variable.

        begin
            @gpu_result_pointer = environment[("prev_" + unique_id.to_s).to_sym].to_i
            Log.info("Kept pointer for result of command #{unique_id.to_s}: #{@gpu_result_pointer}")
            return true  
        rescue ArgumentError
            # No pointer saved for this command. This can happen if the result of this
            # command was already cached earlier and the cached result of a
            # computation based on this command was used now. 
            Log.info("No pointer kept for result of command #{unique_id.to_s}.")
            return false
        end
    end

    return false
end

#result_typeObject



113
114
115
116
117
118
119
120
# File 'lib/types/types/array_command_type.rb', line 113

def result_type
    # Result cache should be cached, just like the result itself
    if @result_type == nil
        @result_type = TypeInference::CommandInference.process_command(self)
    end

    return @result_type
end

#set_unique_idObject



397
398
399
400
401
# File 'lib/symbolic/symbolic.rb', line 397

def set_unique_id
    # Generate unique ID
    @unique_id = @@unique_id
    @@unique_id += 1
end

#sizeFixnum

Returns the size (number of elements) of the result, after executing the parallel section.

Returns:

Raises:

  • (NotImplementedError)


323
324
325
# File 'lib/symbolic/symbolic.rb', line 323

def size
    raise NotImplementedError.new
end

#to_c_typeObject



94
95
96
# File 'lib/types/types/array_command_type.rb', line 94

def to_c_type
    return "#{Translator::ArrayCommandStructBuilder.struct_name(self)} *"
end

#to_commandObject



283
284
285
# File 'lib/symbolic/symbolic.rb', line 283

def to_command
    return self
end

#to_ffi_typeObject



98
99
100
101
# File 'lib/types/types/array_command_type.rb', line 98

def to_ffi_type
    # TODO: This method is probably not required?
    return :pointer
end

#to_ruby_typeObject



103
104
105
# File 'lib/types/types/array_command_type.rb', line 103

def to_ruby_type
    return ArrayCommand
end

#to_sObject



187
188
189
# File 'lib/symbolic/symbolic.rb', line 187

def to_s
    return "[#{self.class.to_s}, size = #{size.to_s}]"
end

#with_index(&block) ⇒ Object



403
404
405
406
407
408
409
# File 'lib/symbolic/symbolic.rb', line 403

def with_index(&block)
    self.block = block
    @input.push(SingleInput.new(
        command: ArrayIndexCommand.new(dimensions: dimensions),
        pattern: :tid))
    return self
end