class Ikra::Translator::CommandTranslator::ProgramBuilder

Builds the entire CUDA program. A CUDA program may consist of multiple kernels, but has at least one kernel. The generated code performs the following steps:

  1. Insert header of CUDA file.

  2. For every kernel: Build all methods, blocks, and kernels.

  3. Build the program entry point (including kernel launchers).

Attributes

array_command_structs[R]

An array of array command structs.

environment_builder[R]
kernel_launchers[R]
kernels[R]
root_command[R]
structs[R]

An array of structs definitions ([Types::StructType] instances) that should be generated for this program.

Public Class Methods

new(environment_builder:, root_command:) click to toggle source
# File lib/translator/program_builder.rb, line 27
def initialize(environment_builder:, root_command:)
    @kernel_launchers = []
    @kernels = Set.new([])
    @environment_builder = environment_builder
    @root_command = root_command

    # The collection of structs is a [Set]. Struct types are unique, i.e., there
    # are never two equal struct types with different object identity.
    @structs = Set.new
    @array_command_structs = Set.new
end

Public Instance Methods

add_array_command_struct(*structs) click to toggle source
# File lib/translator/program_builder.rb, line 39
def add_array_command_struct(*structs)
    for struct in structs
        array_command_structs.add(struct)
    end
end
add_kernel_launcher(launcher) click to toggle source
# File lib/translator/program_builder.rb, line 45
def add_kernel_launcher(launcher)
    @kernel_launchers.push(launcher)
end
build_kernel_launchers() click to toggle source

Build kernel invocations

# File lib/translator/program_builder.rb, line 65
def build_kernel_launchers
    return kernel_launchers.map do |launcher|
        launcher.build_kernel_launcher
    end.join("")
end
execute() click to toggle source

Generates the source code for the CUDA program, compiles it with nvcc and executes the program.

# File lib/translator/program_builder.rb, line 51
def execute
    source = build_program

    launcher = Launcher.new(
        source: source,
        environment_builder: environment_builder,
        result_type: result_type,
        root_command: root_command)

    launcher.compile
    return launcher.execute
end

Protected Instance Methods

all_kernel_builders() click to toggle source
# File lib/translator/program_builder.rb, line 101
def all_kernel_builders
    return kernel_launchers.map do |launcher|
        launcher.kernel_builders
    end.flatten
end
assert_ready_to_build() click to toggle source
# File lib/translator/program_builder.rb, line 73
def assert_ready_to_build
    if kernel_launchers.size == 0
        raise AssertionError.new(
            "Not ready to build (ProgramBuilder): No kernel launcher defined")
    end
end
build_array_command_struct_types() click to toggle source
# File lib/translator/program_builder.rb, line 97
def build_array_command_struct_types
    return array_command_structs.to_a.join("\n") + "\n"
end
build_environment_struct() click to toggle source

Build environment struct definition

# File lib/translator/program_builder.rb, line 86
def build_environment_struct
    return environment_builder.build_environment_struct
end
build_header() click to toggle source

Build header of CUDA source code file

# File lib/translator/program_builder.rb, line 81
def build_header
    return Translator.read_file(file_name: "header.cpp")
end
build_header_structs() click to toggle source

Build the struct type for `result_t`.

# File lib/translator/program_builder.rb, line 171
def build_header_structs
    header_structs = Translator.read_file(file_name: "header_structs.cpp",
        replacements: {"result_type" => result_type.to_c_type})
end
build_kernels() click to toggle source

Build methods, blocks and kernels

# File lib/translator/program_builder.rb, line 108
def build_kernels
    result = ""

    for builder in all_kernel_builders
        # Check whether kernel was already build before
        if kernels.include?(builder)
            next
        else
            kernels.add(builder)
        end

        result = result + builder.build_methods
        result = result + builder.build_blocks
        result = result + builder.build_kernel
    end

    return result
end
build_memory_free() click to toggle source

Free device memory

# File lib/translator/program_builder.rb, line 158
def build_memory_free
    result = ""

    for launcher in kernel_launchers
        if !launcher.reuse_memory?
            result = result + launcher.build_device_memory_free
        end
    end

    return result
end
build_program() click to toggle source

Builds the CUDA program. Returns the source code string.

# File lib/translator/program_builder.rb, line 177
def build_program
    assert_ready_to_build

    result = build_header + build_struct_types + build_header_structs + 
        build_array_command_struct_types + build_environment_struct + 
        build_kernels

    # Build program entry point
    return result + Translator.read_file(file_name: "entry_point.cpp", replacements: {
        "prepare_environment" => environment_builder.build_environment_variable,
        "launch_all_kernels" => build_kernel_launchers,
        "free_device_memory" => build_memory_free,
        "host_env_var_name" => Constants::ENV_HOST_IDENTIFIER,
        "host_result_array" => host_result_expression})
end
build_struct_types() click to toggle source

Generate all struct types (except for array command struct types).

# File lib/translator/program_builder.rb, line 91
def build_struct_types
    return structs.map do |struct_type|
            struct_type.generate_definition
        end.join("\n") + "\n"
end
host_result_expression() click to toggle source
# File lib/translator/program_builder.rb, line 127
def host_result_expression
    # Read some fields from last kernel launch configuration
    result_device_ptr = kernel_launchers.last.kernel_result_var_name
    result_c_type = kernel_launchers.last.result_type.to_c_type
    result_size = root_command.size

    if result_device_ptr == nil
        raise AssertionError.new(
            "Result variable name of final kernel launcher not set")
    end

    # Build result values: `variable_size_array_t` struct. This struct contains a
    # pointer to the result array and stores the size of the result.
    result_device_variable_array_t = "variable_size_array_t((void *) #{result_device_ptr}, #{result_size})"

    return Translator.read_file(file_name: "memcpy_device_to_host_expr.cpp", replacements: {
        "type" => result_c_type,
        "device_array" => result_device_variable_array_t})
end
result_type() click to toggle source

Returns the result type of this program. The result type must always be a union type that includes a [Types::LocationAwareArrayType] object, because this way we can support return types where the inner type of an array is unknown at compile time.

# File lib/translator/program_builder.rb, line 151
def result_type
    return Types::LocationAwareVariableSizeArrayType.new(
        kernel_launchers.last.result_type,
        location: :host).to_union_type
end