Class: Ikra::Translator::HostSectionCommandTranslator

Inherits:
CommandTranslator show all
Defined in:
lib/translator/host_section/array_host_section_command.rb,
lib/translator/host_section/array_in_host_section_command.rb

Instance Attribute Summary

Attributes inherited from CommandTranslator

#environment_builder, #kernel_launcher_stack, #object_tracer, #program_builder, #root_command

Instance Method Summary collapse

Methods inherited from CommandTranslator

#build_command_translation_result, #kernel_builder, #kernel_launcher, next_unique_id, #pop_kernel_launcher, #push_kernel_launcher, translate_command, #translate_entire_input, #translate_input, #visit_array_combine_command, #visit_array_command, #visit_array_identity_command, #visit_array_index_command, #visit_array_reduce_command, #visit_array_stencil_command, #visit_array_zip_command

Methods inherited from Symbolic::Visitor

#visit_array_combine_command, #visit_array_command, #visit_array_identity_command, #visit_array_index_command, #visit_array_reduce_command, #visit_array_select_command, #visit_array_stencil_command, #visit_array_zip_command, #visit_fixed_size_array_in_host_section_command

Constructor Details

#initialize(root_command:) ⇒ HostSectionCommandTranslator

Returns a new instance of HostSectionCommandTranslator.



9
10
11
12
13
14
15
16
# File 'lib/translator/host_section/array_host_section_command.rb', line 9

def initialize(root_command:)
    super

    # Use a different program builder
    @program_builder = HostSectionProgramBuilder.new(
        environment_builder: environment_builder, 
        root_command: root_command)
end

Instance Method Details

#start_translationObject



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/translator/host_section/array_host_section_command.rb', line 18

def start_translation
    Log.info("HostSectionCommandTranslator: Starting translation...")

    # Trace all objects
    @object_tracer = TypeInference::ObjectTracer.new(root_command)
    all_objects = object_tracer.trace_all

    # Translate the command (might create additional kernels)
    root_command.accept(self)

    # Add SoA arrays to environment
    object_tracer.register_soa_arrays(environment_builder)
end

#visit_array_host_section_command(command) ⇒ Object



32
33
34
35
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/translator/host_section/array_host_section_command.rb', line 32

def visit_array_host_section_command(command)
    Log.info("Translating ArrayHostSectionCommand [#{command.unique_id}]")

    super

    # A host section must be a top-level (root) command. It uses a special
    # [HostSectionProgramBuilder].

    block_def_node = command.block_def_node

    # Cannot use the normal `translate_block` method here, this is special!
    # TODO: There's some duplication here with [BlockTranslator]

    # Build hash of parameter name -> type mappings
    block_parameter_types = {}
    command.block_parameter_names.each_with_index do |name, index|
        block_parameter_types[name] = command.section_input[index].ikra_type.to_union_type
    end

    parameter_types_string = "[" + block_parameter_types.map do |id, type| "#{id}: #{type}" end.join(", ") + "]"
    Log.info("Translating block with input types #{parameter_types_string}")

    # Add information to block_def_node
    block_def_node.parameters_names_and_types = block_parameter_types

    # Insert return statements (also done by type inference visitor, but we need
    # it now)
    block_def_node.accept(LastStatementReturnsVisitor.new)

    # Insert synthetic __call__ send nodes for return values
    block_def_node.accept(ParallelSectionInvocationVisitor.new)

    # Concert to SSA form
    AST::SSAGenerator.transform_to_ssa!(block_def_node)

    # Type inference
    type_inference_visitor = TypeInference::Visitor.new
    result_type = type_inference_visitor.process_block(block_def_node)

    for singleton_type in result_type
        if !singleton_type.is_a?(Types::LocationAwareArrayType)
            raise AssertionError.new("Return value of host section must be a LocationAwareArrayType. Found a code path with #{singleton_type}.")
        end
    end

    # C++/CUDA code generation
    ast_translator = HostSectionASTTranslator.new(command_translator: self)

    # Auxiliary methods are instance methods that are called by the host section
    aux_methods = type_inference_visitor.all_methods.map do |method|
        ast_translator.translate_method(method)
    end

    # Build C++ function
    function_translation = ast_translator.translate_block(block_def_node)

    # Declare local variables
    block_def_node.local_variables_names_and_types.each do |name, type|
        function_translation.prepend("#{type.to_c_type} #{name};\n")
    end

    mangled_name = "_host_section_#{command.unique_id}_"
    function_parameters = [
        "#{Constants::ENV_TYPE} *#{Constants::ENV_HOST_IDENTIFIER}",
        "#{Constants::ENV_TYPE} *#{Constants::ENV_DEVICE_IDENTIFIER}",
        "#{Constants::PROGRAM_RESULT_TYPE} *#{Constants::PROGRAM_RESULT_IDENTIFIER}"]

    # Define incoming values (parameters). These must all be array commands for now.
    parameter_def = block_parameter_types.map do |name, type|
        if type.singleton_type.is_a?(Symbolic::ArrayCommand)
            # Should be initialized with new array command struct
            "#{type.singleton_type.to_c_type} #{name} = new #{type.singleton_type.to_c_type[0...-2]}();"
        else
            "#{type.singleton_type.to_c_type} #{name};"
        end
    end.join("\n") + "\n"

    translation_result = Translator.read_file(
        file_name: "host_section_block_function_head.cpp",
        replacements: { 
            "name" => mangled_name, 
            "result_type" => result_type.to_c_type,
            "parameters" => function_parameters.join(", "),
            "body" => Translator.wrap_in_c_block(parameter_def + function_translation)})

    program_builder.host_section_source = translation_result

    # Build function invocation
    args = [
        Constants::ENV_HOST_IDENTIFIER, 
        Constants::ENV_DEVICE_IDENTIFIER,
        Constants::PROGRAM_RESULT_IDENTIFIER]

    # Generate code that transfers data back to host. By creating a synthetic send
    # node here, we can let the compiler generate a switch statement if the type of
    # the return value (array) cannot be determined uniquely at compile time.
    host_section_invocation = AST::SourceCodeExprNode.new(
        code: "#{mangled_name}(#{args.join(", ")})")
    host_section_invocation.merge_union_type(result_type)
    device_to_host_transfer_node = AST::SendNode.new(
        receiver: host_section_invocation,
        selector: :__to_host_array__)

    # Type inference is a prerequisite for code generation
    type_inference_visitor.visit_send_node(device_to_host_transfer_node)

    program_builder.host_result_expression = device_to_host_transfer_node.accept(
        ast_translator.expression_translator)
    program_builder.result_type = device_to_host_transfer_node.get_type

    Log.info("DONE translating ArrayHostSectionCommand [#{command.unique_id}]")

    # This method has no return value (for the moment)
end

#visit_array_in_host_section_command(command) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/translator/host_section/array_in_host_section_command.rb', line 4

def visit_array_in_host_section_command(command)
    Log.info("Translating ArrayInHostSectionCommand [#{command.unique_id}]")

    super

    # This is a root command, determine grid/block dimensions
    kernel_launcher.configure_grid(command.size, block_size: command.block_size)

    array_input_id = "_array_#{self.class.next_unique_id}_"
    kernel_builder.add_additional_parameters("#{command.base_type.to_c_type} *#{array_input_id}")

    # Add placeholder for argument (input array). This should be done here to preserve
    # the order or arguments.
    kernel_launcher.add_additional_arguments(proc do |cmd|
        # `cmd` is a reference to the command being launched (which might be merged
        # with other commands). Based on that information, we can generate an 
        # expression that returns the input array.
        arg = Translator::KernelLaunchArgumentGenerator.generate_arg(
            command, cmd, "cmd")

        if arg == nil
            raise AssertionError.new("Argument not found: Trying to launch command #{cmd.unique_id}, looking for result of command #{command.unique_id}")
        end

        arg
    end)

    command_translation = build_command_translation_result(
        result: "#{array_input_id}[_tid_]",
        command: command)

    Log.info("DONE translating ArrayInHostSectionCommand [#{command.unique_id}]")

    return command_translation
end