module Ikra::RubyIntegration

Constants

ALL_ARRAY_COMMAND_TYPES
ALL_ARRAY_TYPES
ARRAY_COMMAND_TO_ARRAY_TYPE
BOOL
BOOL_S
FLOAT
FLOAT_S
INNER_TYPE
INT
INTERPRETER_ONLY_CLS_OBJ

No need to do type inference or code generation, if a method is called on an on an instance of one of these classes.

INT_S
LAUNCH_KERNEL
LOCATION_AWARE_ARRAY_ACCESS
LOCATION_AWARE_ARRAY_TYPE
MATH
PMAP_TYPE
PREDUCE_TYPE
PSTENCIL_TYPE
PZIP_TYPE
SYMBOLICALLY_EXECUTE_KERNEL
TYPE_BOOL_RETURN_BOOL
TYPE_INT_COERCE_TO_FLOAT

TODO: Handle non-singleton types

TYPE_INT_RETURN_INT
TYPE_NUMERIC_RETURN_BOOL

Public Class Methods

expect_singleton_args?(rcvr_type, method_name) click to toggle source
# File lib/ruby_core/ruby_integration.rb, line 68
def self.expect_singleton_args?(rcvr_type, method_name)
    return find_impl(rcvr_type, method_name).expect_singleton_args
end
get_implementation(receiver, method_name, arguments, translator, result_type) click to toggle source

Returns the implementation (CUDA source code snippet) for a method with name

method_name

defined on [rcvr_type].

This method also receives references to the receiver AST node and to AST nodes for arguments. In most cases, these AST nodes are directly translated to source code using `translator` (a [Translator::ASTTranslator]). However, if an implementation is given through a block ([Proc]), the implementation might decide to not use the translation (e.g., translation of parallel sections in host sections).

receiver

must have a singleton type.

# File lib/ruby_core/ruby_integration.rb, line 82
def self.get_implementation(receiver, method_name, arguments, translator, result_type)
    impl = find_impl(receiver.get_type.singleton_type, method_name)
    source = impl.implementation

    if source.is_a?(Proc)
        source = source.call(receiver, method_name, arguments, translator, result_type)
    end

    sub_code = arguments.map do |arg| arg.accept(translator.expression_translator) end
    sub_types = arguments.map do |arg| arg.get_type end

    if impl.pass_self
        sub_code.insert(0, receiver.accept(translator.expression_translator))
        sub_types.insert(0, receiver.get_type)
    end

    sub_indices = (0...source.length).find_all do |index| 
        source[index] == "#" 
    end
    substitutions = {}
    sub_indices.each do |index|
        if source[index + 1] == "F"
            # Insert float
            arg_index = source[index + 2].to_i

            if arg_index >= sub_code.size
                raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
            end

            substitutions["\#F#{arg_index}"] = code_argument(FLOAT_S, sub_types[arg_index], sub_code[arg_index])
        elsif source[index + 1] == "I"
            # Insert integer
            arg_index = source[index + 2].to_i

            if arg_index >= sub_code.size
                raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
            end

            substitutions["\#I#{arg_index}"] = code_argument(INT_S, sub_types[arg_index], sub_code[arg_index])
        elsif source[index + 1] == "B"
            # Insert integer
            arg_index = source[index + 2].to_i

            if arg_index >= sub_code.size
                raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
            end

            substitutions["\#B#{arg_index}"] = code_argument(BOOL_S, sub_types[arg_index], sub_code[arg_index])
        elsif source[index + 1] == "N"
            # Numeric, coerce integer to float
            arg_index = source[index + 2].to_i

            if arg_index >= sub_code.size
                raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
            end

            if sub_types[arg_index].include?(FLOAT_S)
                expected_type = FLOAT_S
            else
                expected_type = INT_S
            end

            substitutions["\#N#{arg_index}"] = code_argument(expected_type, sub_types[arg_index], sub_code[arg_index])
        else
            arg_index = source[index + 1].to_i

            if arg_index >= sub_code.size
                raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
            end

            substitutions["\##{arg_index}"] = sub_code[arg_index]
        end
    end

    substitutions.each do |key, value|
        # Do not use `gsub!` here!
        source = source.gsub(key, value)
    end
    
    return source
end
get_return_type(rcvr_type, method_name, *arg_types, send_node: nil) click to toggle source

Retrieves the return type of a method invocation for receiver type [rcvr_type], selector [method_name], and argument types [arg_types].

In addition, this method accepts an optional parameter [node] containing the send node (abstract syntax tree node). That node is passed to type inference procs. This is required for symbolic execution of array commands inside host sections.

# File lib/ruby_core/ruby_integration.rb, line 170
def self.get_return_type(rcvr_type, method_name, *arg_types, send_node: nil)
    return_type = find_impl(rcvr_type, method_name).return_type
    num_params = find_impl(rcvr_type, method_name).num_params

    if return_type.is_a?(Proc)
        # Return type depends on argument types
        if num_params.is_a?(Fixnum) && num_params != arg_types.size
            raise ArgumentError.new(
                "#{num_params} arguments expected but #{arg_types.size} given")
        elsif num_params.is_a?(Range) && !num_params.include?(arg_types.size)
            raise ArgumentError.new(
                "#{num_params} arguments expected but #{arg_types.size} given")
        else
            if send_node == nil
                return return_type.call(rcvr_type, *arg_types)
            else
                return return_type.call(rcvr_type, *arg_types, send_node: send_node)
            end
        end
    else
        return return_type
    end
end
has_implementation?(rcvr_type, method_name) click to toggle source
# File lib/ruby_core/ruby_integration.rb, line 60
def self.has_implementation?(rcvr_type, method_name)
    return find_impl(rcvr_type, method_name) != nil
end
implement( rcvr_type, method_name, return_type, num_params, impl, pass_self: true, expect_singleton_args: false) click to toggle source
# File lib/ruby_core/ruby_integration.rb, line 43
def self.implement(
    rcvr_type, 
    method_name, 
    return_type, 
    num_params, 
    impl, 
    pass_self: true, 
    expect_singleton_args: false)

    @@impls[rcvr_type][method_name] = Implementation.new(
        num_params: num_params,
        return_type: return_type,
        implementation: impl,
        pass_self: pass_self,
        expect_singleton_args: expect_singleton_args)
end
is_interpreter_only?(type) click to toggle source
# File lib/ruby_core/interpreter.rb, line 8
def self.is_interpreter_only?(type)
    if !type.is_a?(Types::ClassType)
        return false
    end

    return INTERPRETER_ONLY_CLS_OBJ.include?(type.cls)
end
should_pass_self?(rcvr_type, method_name) click to toggle source
# File lib/ruby_core/ruby_integration.rb, line 64
def self.should_pass_self?(rcvr_type, method_name)
    return find_impl(rcvr_type, method_name).pass_self
end

Private Class Methods

code_argument(expected_type, arg_type, code) click to toggle source
# File lib/ruby_core/ruby_integration.rb, line 196
def self.code_argument(expected_type, arg_type, code)
    if arg_type.is_singleton?
        if expected_type != arg_type.singleton_type
            # Try to cast
            return "((#{expected_type.to_c_type}) #{code})"
        else
            return code
        end
    else
        # Extract from union type
        result = StringIO.new

        result << "({ union_t arg = #{code};\n"
        result << "    #{expected_type.to_c_type} result;\n"
        result << "    switch (arg.class_id) {\n"

        for type in arg_type
            c_type = expected_type.to_c_type
            result << "        case #{type.class_id}:\n"
            # TODO: This works only for primitive types
            result << "            result = (#{c_type}) arg.value.#{type.to_c_type}_;\n"
            result << "            break;\n"
        end

        result << "        default:\n"
        result << "            // TODO: throw exception\n"
        result << "    }\n"
        result << "    result;\n"
        result << "})"

        return result.string
    end
end
find_impl(rcvr_type, method_name) click to toggle source
# File lib/ruby_core/ruby_integration.rb, line 230
def self.find_impl(rcvr_type, method_name)
    if @@impls.include?(rcvr_type) && @@impls[rcvr_type].include?(method_name)
        return @@impls[rcvr_type][method_name]
    else
        # Evaluate blocks
        for type_or_block in @@impls.keys
            if type_or_block.is_a?(Proc)
                if type_or_block.call(rcvr_type)
                    if @@impls[type_or_block].include?(method_name)
                        return @@impls[type_or_block][method_name]
                    end
                end
            end
        end

        # No implementation found
        return nil
    end
end