ikra 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ast/builder.rb +225 -77
  3. data/lib/ast/host_section_builder.rb +38 -0
  4. data/lib/ast/interpreter.rb +67 -0
  5. data/lib/ast/lexical_variables_enumerator.rb +3 -2
  6. data/lib/ast/nodes.rb +521 -31
  7. data/lib/ast/printer.rb +116 -18
  8. data/lib/ast/ssa_generator.rb +192 -0
  9. data/lib/ast/visitor.rb +235 -21
  10. data/lib/config/configuration.rb +28 -3
  11. data/lib/config/os_configuration.rb +62 -9
  12. data/lib/cpu/cpu_implementation.rb +39 -0
  13. data/lib/ikra.rb +13 -3
  14. data/lib/resources/cuda/allocate_device_memory.cpp +5 -0
  15. data/lib/resources/cuda/allocate_host_memory.cpp +1 -0
  16. data/lib/resources/cuda/allocate_memcpy_environment_to_device.cpp +11 -0
  17. data/lib/resources/cuda/ast/assignment.cpp +1 -0
  18. data/lib/resources/cuda/block_function_head.cpp +7 -1
  19. data/lib/resources/cuda/entry_point.cpp +47 -0
  20. data/lib/resources/cuda/env_builder_copy_array.cpp +8 -2
  21. data/lib/resources/cuda/free_device_memory.cpp +3 -0
  22. data/lib/resources/cuda/free_memory_for_command.cpp +24 -0
  23. data/lib/resources/cuda/header.cpp +23 -9
  24. data/lib/resources/cuda/header_structs.cpp +92 -0
  25. data/lib/resources/cuda/host_section_block_function_head.cpp +12 -0
  26. data/lib/resources/cuda/host_section_entry_point.cpp +55 -0
  27. data/lib/resources/cuda/host_section_free_device_memory.cpp +18 -0
  28. data/lib/resources/cuda/host_section_launch_parallel_section.cpp +14 -0
  29. data/lib/resources/cuda/host_section_malloc_memcpy_device_to_host.cpp +10 -0
  30. data/lib/resources/cuda/kernel.cpp +9 -2
  31. data/lib/resources/cuda/launch_kernel.cpp +5 -0
  32. data/lib/resources/cuda/memcpy_device_to_host.cpp +3 -0
  33. data/lib/resources/cuda/memcpy_device_to_host_expr.cpp +10 -0
  34. data/lib/resources/cuda/reduce_body.cpp +88 -0
  35. data/lib/resources/cuda/stencil_array_reconstruction.cpp +2 -0
  36. data/lib/resources/cuda/stencil_body.cpp +16 -0
  37. data/lib/resources/cuda/struct_definition.cpp +4 -0
  38. data/lib/ruby_core/array.rb +34 -0
  39. data/lib/ruby_core/array_command.rb +313 -0
  40. data/lib/ruby_core/core.rb +103 -0
  41. data/lib/ruby_core/interpreter.rb +16 -0
  42. data/lib/ruby_core/math.rb +32 -0
  43. data/lib/ruby_core/ruby_integration.rb +256 -0
  44. data/lib/symbolic/host_section.rb +115 -0
  45. data/lib/symbolic/input.rb +87 -0
  46. data/lib/symbolic/input_visitor.rb +68 -0
  47. data/lib/symbolic/symbolic.rb +793 -117
  48. data/lib/symbolic/visitor.rb +70 -8
  49. data/lib/translator/array_command_struct_builder.rb +163 -0
  50. data/lib/translator/ast_translator.rb +572 -0
  51. data/lib/translator/block_translator.rb +104 -48
  52. data/lib/translator/commands/array_combine_command.rb +41 -0
  53. data/lib/translator/commands/array_identity_command.rb +28 -0
  54. data/lib/translator/commands/array_index_command.rb +52 -0
  55. data/lib/translator/commands/array_reduce_command.rb +135 -0
  56. data/lib/translator/commands/array_stencil_command.rb +129 -0
  57. data/lib/translator/commands/array_zip_command.rb +30 -0
  58. data/lib/translator/commands/command_translator.rb +264 -0
  59. data/lib/translator/cuda_errors.rb +32 -0
  60. data/lib/translator/environment_builder.rb +263 -0
  61. data/lib/translator/host_section/array_host_section_command.rb +150 -0
  62. data/lib/translator/host_section/array_in_host_section_command.rb +41 -0
  63. data/lib/translator/host_section/ast_translator.rb +14 -0
  64. data/lib/translator/host_section/parallel_section_invocation_visitor.rb +20 -0
  65. data/lib/translator/host_section/program_builder.rb +89 -0
  66. data/lib/translator/input_translator.rb +226 -0
  67. data/lib/translator/kernel_builder.rb +137 -0
  68. data/lib/translator/kernel_launcher/for_loop_kernel_launcher.rb +40 -0
  69. data/lib/translator/kernel_launcher/kernel_launcher.rb +259 -0
  70. data/lib/translator/kernel_launcher/while_loop_kernel_launcher.rb +38 -0
  71. data/lib/translator/last_returns_visitor.rb +19 -10
  72. data/lib/translator/program_builder.rb +197 -0
  73. data/lib/translator/program_launcher.rb +273 -0
  74. data/lib/translator/struct_type.rb +55 -0
  75. data/lib/translator/translator.rb +34 -11
  76. data/lib/translator/variable_classifier_visitor.rb +56 -0
  77. data/lib/types/inference/ast_inference.rb +586 -0
  78. data/lib/types/inference/clear_types_visitor.rb +11 -0
  79. data/lib/types/inference/command_inference.rb +101 -0
  80. data/lib/types/inference/input_inference.rb +62 -0
  81. data/lib/types/{object_tracer.rb → inference/object_tracer.rb} +5 -6
  82. data/lib/types/inference/ruby_extension.rb +35 -0
  83. data/lib/types/inference/symbol_table.rb +131 -0
  84. data/lib/types/types.rb +14 -0
  85. data/lib/types/types/array_command_type.rb +123 -0
  86. data/lib/types/types/array_type.rb +137 -0
  87. data/lib/types/{class_type.rb → types/class_type.rb} +42 -18
  88. data/lib/types/{primitive_type.rb → types/primitive_type.rb} +20 -7
  89. data/lib/types/types/ruby_type.rb +88 -0
  90. data/lib/types/types/struct_type.rb +179 -0
  91. data/lib/types/types/union_type.rb +239 -0
  92. metadata +160 -18
  93. data/lib/ast/method_definition.rb +0 -37
  94. data/lib/ast/translator.rb +0 -264
  95. data/lib/resources/cuda/kernel_launcher.cpp +0 -28
  96. data/lib/scope.rb +0 -166
  97. data/lib/translator/command_translator.rb +0 -421
  98. data/lib/translator/local_variables_enumerator.rb +0 -35
  99. data/lib/translator/method_translator.rb +0 -24
  100. data/lib/types/array_type.rb +0 -51
  101. data/lib/types/ruby_extension.rb +0 -67
  102. data/lib/types/ruby_type.rb +0 -45
  103. data/lib/types/type_inference.rb +0 -382
  104. data/lib/types/union_type.rb +0 -155
@@ -1,14 +1,26 @@
1
1
  module Ikra
2
2
  module Symbolic
3
- class ArrayNewCommand
3
+ class ArrayIndexCommand
4
4
  def accept(visitor)
5
- visitor.visit_array_new_command(self)
5
+ visitor.visit_array_index_command(self)
6
6
  end
7
7
  end
8
8
 
9
- class ArrayMapCommand
9
+ class ArrayCombineCommand
10
10
  def accept(visitor)
11
- visitor.visit_array_map_command(self)
11
+ visitor.visit_array_combine_command(self)
12
+ end
13
+ end
14
+
15
+ class ArrayReduceCommand
16
+ def accept(visitor)
17
+ visitor.visit_array_reduce_command(self)
18
+ end
19
+ end
20
+
21
+ class ArrayStencilCommand
22
+ def accept(visitor)
23
+ visitor.visit_array_stencil_command(self)
12
24
  end
13
25
  end
14
26
 
@@ -24,28 +36,78 @@ module Ikra
24
36
  end
25
37
  end
26
38
 
39
+ class ArrayZipCommand
40
+ def accept(visitor)
41
+ visitor.visit_array_zip_command(self)
42
+ end
43
+ end
44
+
45
+ class ArrayHostSectionCommand
46
+ def accept(visitor)
47
+ visitor.visit_array_host_section_command(self)
48
+ end
49
+ end
50
+
51
+ class ArrayInHostSectionCommand
52
+ def accept(visitor)
53
+ visitor.visit_array_in_host_section_command(self)
54
+ end
55
+ end
56
+
57
+ class FixedSizeArrayInHostSectionCommand < ArrayInHostSectionCommand
58
+ def accept(visitor)
59
+ visitor.visit_fixed_size_array_in_host_section_command(self)
60
+ end
61
+ end
62
+
27
63
  class Visitor
28
64
  def visit_array_command(command)
65
+ for input in command.input
66
+ if input.command.is_a?(ArrayCommand)
67
+ input.command.accept(self)
68
+ end
69
+ end
70
+ end
29
71
 
72
+ def visit_array_index_command(command)
73
+ visit_array_command(command)
30
74
  end
31
75
 
32
- def visit_array_new_command(command)
76
+ def visit_array_combine_command(command)
33
77
  visit_array_command(command)
34
78
  end
35
79
 
36
- def visit_array_map_command(command)
80
+ def visit_array_reduce_command(command)
81
+ visit_array_command(command)
82
+ end
83
+
84
+ def visit_array_stencil_command(command)
37
85
  visit_array_command(command)
38
- command.target.accept(self)
39
86
  end
40
87
 
41
88
  def visit_array_select_command(command)
42
89
  visit_array_command(command)
43
- command.target.accept(self)
44
90
  end
45
91
 
46
92
  def visit_array_identity_command(command)
47
93
  visit_array_command(command)
48
94
  end
95
+
96
+ def visit_array_in_host_section_command(command)
97
+ visit_array_command(command)
98
+ end
99
+
100
+ def visit_array_zip_command(command)
101
+ visit_array_command(command)
102
+ end
103
+
104
+ def visit_array_host_section_command(command)
105
+
106
+ end
107
+
108
+ def visit_fixed_size_array_in_host_section_command(command)
109
+ visit_array_in_host_section_command(command)
110
+ end
49
111
  end
50
112
  end
51
113
  end
@@ -0,0 +1,163 @@
1
+ module Ikra
2
+ module Translator
3
+ # This class looks for `input_command` in the tree of commands (and inputs) provided
4
+ # by `relative_command`. It traverses this tree and generates and expression that can
5
+ # be used to access `input_command` from `relative_command`, which is represented by
6
+ # an array_command struct in the CUDA code and referenced with `command_expr`.
7
+ # TODO: This class should be a visitor, but we need to pass additional values (`path`)
8
+ # along the way.
9
+ module KernelLaunchArgumentGenerator
10
+ def self.generate_arg(input_command, relative_command, command_expr)
11
+ return visit_array_command(input_command, relative_command, command_expr)
12
+ end
13
+
14
+ def self.visit_array_command(input_command, command, path)
15
+ if command.is_a?(Symbolic::ArrayInHostSectionCommand)
16
+ if command.equal?(input_command)
17
+ # This should be passed as an argument
18
+ return "((#{command.base_type.to_c_type} *) #{path}->input_0.content)"
19
+ else
20
+ # This is not the one we are looking for
21
+ return nil
22
+ end
23
+ else
24
+ command.input.each_with_index do |input, index|
25
+ if input.command.is_a?(Symbolic::ArrayCommand)
26
+ result = visit_array_command(
27
+ input_command, input.command, "#{path}->input_#{index}")
28
+
29
+ if result != nil
30
+ return "((#{input_command.base_type.to_c_type} *) #{result})"
31
+ end
32
+ end
33
+ end
34
+
35
+ return nil
36
+ end
37
+ end
38
+ end
39
+
40
+ class ArrayCommandStructBuilder < Symbolic::Visitor
41
+ def self.struct_name(command)
42
+ return "array_command_#{command.unique_id}"
43
+ end
44
+
45
+ # This class determines if a `size` instance method should be generated for an
46
+ # array_command struct type. This is the case iff struct, or its first input, or
47
+ # the first input of its first input, etc., is an ArrayInHostSectionCommand.
48
+ # The size of such arrays is in general not known at compile time.
49
+ class RequireRuntimeSizeChecker < Symbolic::Visitor
50
+ def self.require_size_function?(command)
51
+ return command.accept(self.new)
52
+ end
53
+
54
+ def visit_array_reduce_command(command)
55
+ # Size is always 1
56
+ return false
57
+ end
58
+
59
+ def visit_array_identity_command(command)
60
+ # Fully fused, size known at compile time
61
+ return false
62
+ end
63
+
64
+ def visit_array_in_host_section_command(command)
65
+ # Cannot be fused, size unknown at compile time
66
+ return true
67
+ end
68
+
69
+ def visit_fixed_size_array_in_host_section_command(command)
70
+ # Size is part of the type/command
71
+ return false
72
+ end
73
+
74
+ def visit_array_command(command)
75
+ if command.input.size == 0
76
+ return false
77
+ else
78
+ return command.input.first.command.accept(self)
79
+ end
80
+ end
81
+ end
82
+
83
+ # This class builds a struct containing references to input (depending) commands for
84
+ # a certain array command. It is a subclass of [Symbolic::Visitor] but does not
85
+ # traverse the tree. We just take advantage of the double dispatch here.
86
+ class SingleStructBuilder < Symbolic::Visitor
87
+ def struct_name(command)
88
+ return ArrayCommandStructBuilder.struct_name(command)
89
+ end
90
+
91
+ def visit_array_command(command)
92
+ this_name = struct_name(command)
93
+ struct_def = "struct #{this_name} {\n"
94
+
95
+ # Debug information
96
+ struct_def = struct_def + " // #{command.class}\n"
97
+
98
+ # Generate fields
99
+ struct_def = struct_def + " #{command.result_type.to_c_type} *result;\n"
100
+
101
+ all_params = ["#{command.result_type.to_c_type} *result = NULL"]
102
+ all_initializers = ["result(result)"]
103
+
104
+ command.input.each_with_index do |input, index|
105
+ if input.command.is_a?(Symbolic::ArrayCommand)
106
+ struct_def = struct_def + " #{struct_name(input.command)} *input_#{index};\n"
107
+ all_params.push("#{struct_name(input.command)} *input_#{index} = NULL")
108
+ all_initializers.push("input_#{index}(input_#{index})")
109
+ end
110
+ end
111
+
112
+ # Generate constructor
113
+ struct_def = struct_def + " __host__ __device__ #{this_name}(#{all_params.join(', ')}) : #{all_initializers.join(', ')} { }\n"
114
+
115
+ # Add instance methods
116
+ if RequireRuntimeSizeChecker.require_size_function?(command)
117
+ # ArrayIndexCommand does not have any input, as an example. But in this
118
+ # case, we also do not need the `size` function, because it is a root
119
+ # command that can be fused.
120
+ struct_def = struct_def + " int size() { return input_0->size(); }\n"
121
+ end
122
+
123
+ struct_def = struct_def + "};"
124
+ end
125
+
126
+ def visit_array_in_host_section_command(command)
127
+ this_name = struct_name(command)
128
+ struct_def = "struct #{this_name} {\n"
129
+
130
+ # Debug information
131
+ struct_def = struct_def + " // #{command.class}\n"
132
+
133
+ struct_def = struct_def + " #{command.result_type.to_c_type} *result;\n"
134
+ struct_def = struct_def + " variable_size_array_t input_0;\n"
135
+ struct_def = struct_def + " __host__ __device__ #{this_name}(#{command.result_type.to_c_type} *result = NULL, variable_size_array_t input_0 = variable_size_array_t::error_return_value) : result(result), input_0(input_0) { }\n"
136
+
137
+ # Add instance methods
138
+ struct_def = struct_def + " int size() { return input_0.size; }\n"
139
+
140
+ return struct_def + "};"
141
+ end
142
+ end
143
+
144
+ attr_reader :all_structs
145
+
146
+ def initialize
147
+ @all_structs = []
148
+ @builder = SingleStructBuilder.new
149
+ end
150
+
151
+ def visit_array_command(command)
152
+ super
153
+ @all_structs.push(command.accept(@builder))
154
+ end
155
+
156
+ def self.build_all_structs(command)
157
+ visitor = self.new
158
+ command.accept(visitor)
159
+ return visitor.all_structs
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,572 @@
1
+ require_relative "../ast/nodes.rb"
2
+ require_relative "../ast/visitor.rb"
3
+ require_relative "../ruby_core/ruby_integration"
4
+
5
+ # Rule: every statement ends with newline
6
+ # TODO: Add proper exceptions (CompilationError)
7
+
8
+ module Ikra
9
+ module Translator
10
+ class ASTTranslator
11
+ class ExpressionTranslator
12
+ # This class should not inherit from [AST::Visitor]. Otherwise, the dispatch
13
+ # mechanisum to [StatementTranslator] will not work properly anymore.
14
+
15
+ attr_reader :translator
16
+
17
+ def initialize(translator)
18
+ @translator = translator
19
+ end
20
+
21
+ def expression_translator
22
+ return self
23
+ end
24
+
25
+ def statement_translator
26
+ return translator.statement_translator
27
+ end
28
+
29
+ def method_missing(symbol, *args)
30
+ if symbol.to_s.start_with?("visit_")
31
+ if statement_translator.respond_to?(symbol)
32
+ return statements_as_expression(
33
+ statement_translator.send(symbol, *args) + "return NULL;")
34
+ else
35
+ super
36
+ end
37
+ else
38
+ return translator.send(symbol, *args)
39
+ end
40
+ end
41
+
42
+ def visit_behavior_node(node)
43
+ raise AssertionError.new(
44
+ "Methods/blocks cannot be translated as an expression")
45
+ end
46
+
47
+ def visit_source_code_expr_node(node)
48
+ return node.code
49
+ end
50
+
51
+ # TODO: Should never translate a hash node (check `with_index` in host section)
52
+ def visit_hash_node(node)
53
+ return ""
54
+ end
55
+
56
+ def visit_const_node(node)
57
+ raise NotImplementedError.new
58
+ end
59
+
60
+ def visit_lvar_read_node(node)
61
+ return node.mangled_identifier.to_s
62
+ end
63
+
64
+ def visit_lvar_write_node(node)
65
+ if node.value.get_type.is_singleton? and !node.symbol_table[node.identifier].is_singleton?
66
+ # The assigned value is singleton, but the variable is not
67
+ singleton_assignment = wrap_in_union_type(
68
+ node.value.accept(expression_translator),
69
+ node.value.get_type.singleton_type)
70
+ return Translator.read_file(file_name: "ast/assignment.cpp",
71
+ replacements: {
72
+ "source" => singleton_assignment,
73
+ "target" => node.mangled_identifier.to_s})
74
+ else
75
+ return Translator.read_file(file_name: "ast/assignment.cpp",
76
+ replacements: {
77
+ "source" => node.value.accept(expression_translator),
78
+ "target" => node.mangled_identifier.to_s})
79
+ end
80
+ end
81
+
82
+ def visit_ivar_read_node(node)
83
+ array_identifier = node.enclosing_class.ruby_class.to_ikra_type.inst_var_array_name(identifier)
84
+ return "#{Constants::ENV_IDENTIFIER}->#{array_identifier}[#{Constants::SELF_IDENTIFIER}]"
85
+ end
86
+
87
+ def visit_array_node(node)
88
+ elements = node.values.map do |value|
89
+ value.accept(self)
90
+ end
91
+
92
+ return "(new #{node.get_type.to_c_type[0..-2]} {#{elements.join(', ')}})"
93
+ end
94
+
95
+ def visit_int_node(node)
96
+ return node.value.to_s
97
+ end
98
+
99
+ def visit_nil_node(node)
100
+ return "0"
101
+ end
102
+
103
+ def visit_float_node(node)
104
+ return node.value.to_s
105
+ end
106
+
107
+ def visit_bool_node(node)
108
+ return node.value.to_s
109
+ end
110
+
111
+ def visit_if_node(node)
112
+ # Make every branch return
113
+ node.accept(LastStatementReturnsVisitor.new)
114
+
115
+ # Wrap in StatementExpression
116
+ return statements_as_expression(node.accept(statement_translator))
117
+ end
118
+
119
+ def visit_ternary_node(node)
120
+ return "((#{node.condition.accept(expression_translator)}) ? (#{node.true_val.accept(expression_translator)}) : (#{node.false_val.accept(expression_translator)}))"
121
+ end
122
+
123
+
124
+ def visit_begin_node(node)
125
+ if node.body_stmts.size == 0
126
+ raise AssertionError.new("Empty BeginNode cannot be an expression")
127
+ elsif node.body_stmts.size == 1
128
+ # Preserve brackets
129
+ return "(#{node.body_stmts.first.accept(self)})"
130
+ else
131
+ # Wrap in lambda
132
+ # Do not worry about scope of varibles, they will all be declared at the
133
+ # beginning of the function
134
+ node.accept(LastStatementReturnsVisitor.new)
135
+ return statements_as_expression(node.accept(statement_translator))
136
+ end
137
+ end
138
+
139
+ # Builds a synthetic [AST::SourceCodeExprNode] with a type and a translation.
140
+ def build_synthetic_code_node(code, type)
141
+ node = AST::SourceCodeExprNode.new(code: code)
142
+ node.merge_union_type(type.to_union_type)
143
+ return node
144
+ end
145
+
146
+ def generate_polymorphic_switch(node, &block)
147
+ poly_id = temp_identifier_id
148
+ node_identifer = "_polytemp_expr_#{poly_id}"
149
+ header = "#{define_assign_variable(node_identifer, node)}\nswitch (#{node_identifer}.class_id)\n"
150
+ case_statements = []
151
+
152
+ if node.get_type.size == 0
153
+ raise AssertionError.new("Cannot generate switch for empty UnionType")
154
+ end
155
+
156
+ for type in node.get_type
157
+ if type == Types::PrimitiveType::Int
158
+ self_node = build_synthetic_code_node(
159
+ "#{node_identifer}.value.int_", type)
160
+ elsif type == Types::PrimitiveType::Float
161
+ self_node = build_synthetic_code_node(
162
+ "#{node_identifer}.value.float_", type)
163
+ elsif type == Types::PrimitiveType::Bool
164
+ self_node = build_synthetic_code_node(
165
+ "#{node_identifer}.value.bool_", type)
166
+ elsif type == Types::PrimitiveType::Nil
167
+ self_node = build_synthetic_code_node(
168
+ "#{node_identifer}.value.int_", type)
169
+ elsif type.is_a?(Symbolic::ArrayCommand)
170
+ self_node = build_synthetic_code_node(
171
+ "(#{type.to_c_type}) #{node_identifer}.value.pointer", type)
172
+ elsif type.is_a?(Types::LocationAwareArrayType)
173
+ # TODO: Should not use variable_size_array for fixed size arrays
174
+ self_node = build_synthetic_code_node(
175
+ "#{node_identifer}.value.variable_size_array", type)
176
+ else
177
+ self_node = build_synthetic_code_node(
178
+ "#{node_identifer}.value.object_id", type)
179
+ end
180
+
181
+ debug_info = "#{type.to_s} (#{type.to_ruby_type.to_s})"
182
+
183
+ case_statements.push("case #{type.class_id}: /* #{debug_info} */ #{yield(self_node)} break;")
184
+ end
185
+
186
+ return header + wrap_in_c_block(case_statements.join("\n"))
187
+ end
188
+
189
+ def visit_send_node(node)
190
+ if node.receiver.get_type.is_singleton?
191
+ return_type = node.return_type_by_recv_type[
192
+ node.receiver.get_type.singleton_type]
193
+
194
+ invocation = generate_send_for_singleton(
195
+ node,
196
+ node.receiver,
197
+ return_type)
198
+
199
+ if return_type.is_singleton? and
200
+ !node.get_type.is_singleton?
201
+
202
+ invocation = wrap_in_union_type(
203
+ invocation,
204
+ return_type.singleton_type)
205
+ end
206
+
207
+ return invocation
208
+ else
209
+ # Polymorphic case
210
+ result_identifier = "_polytemp_result_#{temp_identifier_id}"
211
+ declare_result_var = "#{node.get_type.to_c_type} #{result_identifier};\n"
212
+
213
+ case_statements = generate_polymorphic_switch(node.receiver) do |self_node|
214
+ # The singleton type in the current case
215
+ type = self_node.get_type.singleton_type
216
+
217
+ # The return type (result type) in the current case (could be polym.)
218
+ return_type = node.return_type_by_recv_type[type]
219
+
220
+ # Generate method invocation
221
+ invocation = generate_send_for_singleton(
222
+ node,
223
+ self_node,
224
+ return_type)
225
+
226
+ if return_type.is_singleton? and
227
+ !node.get_type.is_singleton?
228
+ # The return value of this particular invocation (singleton type
229
+ # recv) is singleton, but in general this send can return many
230
+ # types
231
+ invocation = wrap_in_union_type(
232
+ invocation,
233
+ return_type.singleton_type)
234
+ end
235
+
236
+ "#{result_identifier} = #{invocation};"
237
+ end
238
+
239
+ # TODO: compound statements only work with the GNU C++ compiler
240
+ return "(" + wrap_in_c_block(
241
+ declare_result_var +
242
+ wrap_in_c_block(case_statements) +
243
+ result_identifier + ";")[0..-2] + ")"
244
+ end
245
+ end
246
+
247
+ def build_switch_for_args(nodes, accumulator = [], &block)
248
+ if nodes.size == 0
249
+ # This was the last argument, we are done with nesting switch
250
+ # stmts. The accumulator contains all singleton-typed self_nodes.
251
+ return yield(accumulator)
252
+ end
253
+
254
+ next_node = nodes.first
255
+
256
+ if next_node.get_type.is_singleton?
257
+ # This node has a singleton type. We're done with this one.
258
+ return build_switch_for_args(nodes.drop(1), accumulator + [next_node], &block)
259
+ elsif next_node.get_type.size == 0
260
+ raise "AssertionError: Found empty UnionType"
261
+ else
262
+ return generate_polymorphic_switch(next_node) do |sing_node|
263
+ build_switch_for_args(nodes.drop(1), accumulator + [sing_node], &block)
264
+ end
265
+ end
266
+ end
267
+
268
+ def generate_send_for_singleton(node, singleton_recv, return_type)
269
+ recv_type = singleton_recv.get_type.singleton_type
270
+
271
+ if RubyIntegration.is_interpreter_only?(recv_type)
272
+ # No translation necessary
273
+ return nil
274
+ elsif RubyIntegration.has_implementation?(recv_type, node.selector)
275
+ # Some implementations accept only singleton-typed arguments
276
+ if RubyIntegration.expect_singleton_args?(recv_type, node.selector)
277
+ # Generate additional switch statements (one per non-sing. arg.).
278
+ # Go through all possible combinations of types (for arguments).
279
+ result_identifier = "_polytemp_result_#{temp_identifier_id}"
280
+ declare_result_var = "#{return_type.to_c_type} #{result_identifier};\n"
281
+
282
+ case_stmts = build_switch_for_args(node.arguments) do |all_sing_args|
283
+ # TODO: Do we really have to redo type inference here?
284
+ all_sing_arg_types = all_sing_args.map do |arg|
285
+ arg.get_type
286
+ end
287
+
288
+ this_return_type = RubyIntegration.get_return_type(
289
+ singleton_recv.get_type.singleton_type,
290
+ node.selector,
291
+ *all_sing_arg_types,
292
+ send_node: node)
293
+
294
+ impl = RubyIntegration.get_implementation(
295
+ singleton_recv,
296
+ node.selector,
297
+ all_sing_args,
298
+ translator,
299
+ this_return_type)
300
+
301
+ if this_return_type.is_singleton? and
302
+ !return_type.is_singleton?
303
+
304
+ impl = wrap_in_union_type(
305
+ impl,
306
+ this_return_type.singleton_type)
307
+ end
308
+
309
+ "#{result_identifier} = #{impl};"
310
+ end
311
+
312
+ return "(" + wrap_in_c_block(
313
+ declare_result_var +
314
+ wrap_in_c_block(case_stmts) +
315
+ result_identifier + ";")[0..-2] + ")"
316
+ else
317
+ # The easy case: Anything is fine (but might fail in ruby_integration)
318
+ return RubyIntegration.get_implementation(
319
+ singleton_recv,
320
+ node.selector,
321
+ node.arguments,
322
+ translator,
323
+ return_type)
324
+ end
325
+ elsif recv_type.is_a?(Types::StructType)
326
+ first_arg = node.arguments.first
327
+
328
+ if first_arg.is_a?(AST::IntLiteralNode)
329
+ # Reading the struct at a constant position
330
+ return recv_type.generate_read(
331
+ singleton_recv.accept(self),
332
+ node.selector,
333
+ first_arg.accept(self))
334
+ else
335
+ # Reading the struct at a non-constant position
336
+ id = temp_identifier_id
337
+ name = "_temp_var_#{id}"
338
+ first_arg_eval = first_arg.accept(self)
339
+
340
+ # Store index in local variable, then generate non-constant access
341
+ # TODO: Statement expression is potentially inefficient
342
+ return "({ int #{name} = #{first_arg_eval};\n" +
343
+ recv_type.generate_non_constant_read(
344
+ singleton_recv.accept(self),
345
+ node.selector,
346
+ name) + "; })"
347
+ end
348
+ else
349
+ args = [Constants::ENV_IDENTIFIER]
350
+
351
+ if recv_type.should_generate_self_arg?
352
+ args.push(singleton_recv.accept(self))
353
+ else
354
+ args.push("NULL")
355
+ end
356
+
357
+ args.push(*(node.arguments.map do |arg| arg.accept(self) end))
358
+ args_string = args.join(", ")
359
+
360
+ return "#{node.receiver.get_type.singleton_type.mangled_method_name(node.selector)}(#{args_string})"
361
+ end
362
+ end
363
+
364
+ def visit_return_node(node)
365
+ raise AssertionError.new("ReturnNode is never an expression")
366
+ end
367
+ end
368
+
369
+ class StatementTranslator
370
+ # This class should not inherit from [AST::Visitor]. Otherwise, the dispatch
371
+ # mechanisum to [ExpressionTranslator] will not work properly anymore.
372
+
373
+ attr_reader :translator
374
+
375
+ def initialize(translator)
376
+ @translator = translator
377
+ end
378
+
379
+ def expression_translator
380
+ return translator.expression_translator
381
+ end
382
+
383
+ def statement_translator
384
+ return self
385
+ end
386
+
387
+ def method_missing(symbol, *args)
388
+ if symbol.to_s.start_with?("visit_")
389
+ if expression_translator.respond_to?(symbol)
390
+ return expression_translator.send(symbol, *args) + ";\n"
391
+ else
392
+ super
393
+ end
394
+ else
395
+ return translator.send(symbol, *args)
396
+ end
397
+ end
398
+
399
+ def visit_behavior_node(node)
400
+ raise AssertionError.new("Methods/blocks cannot be translated as a statement")
401
+ end
402
+
403
+ def visit_root_node(node)
404
+ return node.single_child.accept(self)
405
+ end
406
+
407
+ def visit_for_node(node)
408
+ loop_header = "for (#{node.iterator_identifier.to_s} = #{node.range_from.accept(expression_translator)}; #{node.iterator_identifier.to_s} <= #{node.range_to.accept(expression_translator)}; #{node.iterator_identifier.to_s}++)"
409
+
410
+ return loop_header +
411
+ "\n" +
412
+ node.body_stmts.accept(self) +
413
+ "#{node.iterator_identifier.to_s}--;\n"
414
+ end
415
+
416
+ def visit_while_node(node)
417
+ return "while (#{node.condition.accept(expression_translator)})\n#{node.body_stmts.accept(self)}"
418
+ end
419
+
420
+ def visit_while_post_node(node)
421
+ return "do #{node.body_stmts.accept(self)}while (#{node.condition.accept(expression_translator)});\n"
422
+ end
423
+
424
+ def visit_until_node(node)
425
+ return "while (#{node.condition.accept(expression_translator)})\n#{node.body_stmts.accept(self)}"
426
+ end
427
+
428
+ def visit_until_post_node(node)
429
+ return "do #{node.body_stmts.accept(self)}while (#{node.condition.accept(expression_translator)});\n"
430
+ end
431
+
432
+ def visit_break_node(node)
433
+ return "break;\n"
434
+ end
435
+
436
+ def visit_if_node(node)
437
+ return "if (#{node.condition.accept(expression_translator)})\n" +
438
+ node.true_body_stmts.accept(self) +
439
+ "else\n" +
440
+ node.false_body_stmts.accept(self)
441
+ end
442
+
443
+ def visit_begin_node(node)
444
+ if node.body_stmts.size == 0
445
+ return wrap_in_c_block("")
446
+ end
447
+
448
+ body_translated = node.body_stmts.map do |stmt|
449
+ stmt.accept(self)
450
+ end.join("")
451
+
452
+ return wrap_in_c_block(body_translated)
453
+ end
454
+
455
+ def visit_return_node(node)
456
+ return "return #{node.value.accept(expression_translator)};\n"
457
+ end
458
+ end
459
+
460
+ attr_reader :expression_translator
461
+
462
+ attr_reader :statement_translator
463
+
464
+ def initialize
465
+ @expression_translator = ExpressionTranslator.new(self)
466
+ @statement_translator = StatementTranslator.new(self)
467
+ end
468
+
469
+ def statements_as_expression(str)
470
+ return "[&]#{wrap_in_c_block(str, omit_newl: true)}()"
471
+ end
472
+
473
+ def indent_block(str)
474
+ return str.split("\n").map do |line| " " + line end.join("\n")
475
+ end
476
+
477
+ def wrap_in_c_block(str, omit_newl: false)
478
+ result = "{\n" + indent_block(str) + "\n}"
479
+
480
+ if omit_newl
481
+ return result
482
+ else
483
+ return result + "\n"
484
+ end
485
+ end
486
+
487
+ @@next_temp_identifier_id = 0
488
+ def temp_identifier_id
489
+ @@next_temp_identifier_id += 1
490
+ @@next_temp_identifier_id
491
+ end
492
+
493
+ # Generates code that assigns the value of a node to a newly-defined variable.
494
+ def define_assign_variable(name, node)
495
+ type = node.get_type.to_c_type
496
+ return "#{type} #{name} = #{node.accept(expression_translator)};"
497
+ end
498
+
499
+ def wrap_in_union_type(str, type)
500
+ if type == Types::PrimitiveType::Int
501
+ return "union_t(#{type.class_id}, union_v_t::from_int(#{str}))"
502
+ elsif type == Types::PrimitiveType::Float
503
+ return "union_t(#{type.class_id}, union_v_t::from_float(#{str}))"
504
+ elsif type == Types::PrimitiveType::Bool
505
+ return "union_t(#{type.class_id}, union_v_t::from_bool(#{str}))"
506
+ elsif type == Types::PrimitiveType::Nil
507
+ return "union_t(#{type.class_id}, union_v_t::from_int(#{str}))"
508
+ elsif type.is_a?(Symbolic::ArrayCommand)
509
+ return "union_t(#{type.class_id}, union_v_t::from_pointer((void *) #{str}))"
510
+ elsif type.is_a?(Types::LocationAwareArrayType)
511
+ # TODO: Should not use variable_size_array for fixed size arrays
512
+ return "union_t(#{type.class_id}, union_v_t::from_variable_size_array_t(#{str}))"
513
+ elsif !type.is_a?(Types::UnionType)
514
+ return "union_t(#{type.class_id}, union_v_t::from_object_id(#{str}))"
515
+ else
516
+ raise AssertionError.new("UnionType found but singleton type expected")
517
+ end
518
+ end
519
+
520
+ def self.translate_block(block_def_node)
521
+ return self.new.translate_block(block_def_node)
522
+ end
523
+
524
+ def translate_block(block_def_node)
525
+ return block_def_node.body.accept(statement_translator)
526
+ end
527
+
528
+ def self.translate_method(method_def_node)
529
+ return self.new.translate_method(method_def_node)
530
+ end
531
+
532
+ def generate_method_signature(meth_def_node)
533
+ method_params = ([
534
+ "environment_t * #{Constants::ENV_IDENTIFIER}",
535
+ "#{meth_def_node.parent.get_type.to_c_type} #{Constants::SELF_IDENTIFIER}"] +
536
+ meth_def_node.parameters_names_and_types.map do |name, type|
537
+ "#{type.singleton_type.to_c_type} #{name}"
538
+ end).join(", ")
539
+
540
+ signature = "__device__ #{meth_def_node.get_type.singleton_type.to_c_type} #{meth_def_node.parent.get_type.mangled_method_name(meth_def_node.name)}(#{method_params})"
541
+
542
+ return signature
543
+ end
544
+
545
+ def translate_method_predecl(meth_def_node)
546
+ return generate_method_signature(meth_def_node) + ";"
547
+ end
548
+
549
+ def translate_method(meth_def_node)
550
+ # TODO: merge with BlockTranslator
551
+ # TODO: load environment variables
552
+
553
+ # Declare local variables
554
+ local_variables_def = ""
555
+ meth_def_node.local_variables_names_and_types.each do |name, type|
556
+ local_variables_def += "#{type.to_c_type} #{name};\n"
557
+ end
558
+
559
+ # TODO: There should be a better way to ensure that we don't generate methods
560
+ # multiple times.
561
+ mangled_name = meth_def_node.parent.get_type.mangled_method_name(meth_def_node.name)
562
+ def_label = "def_label_#{mangled_name}"
563
+
564
+ return "#ifndef #{def_label}\n#define #{def_label}\n" +
565
+ generate_method_signature(meth_def_node) +
566
+ "\n" +
567
+ wrap_in_c_block(local_variables_def + meth_def_node.body.accept(statement_translator)) +
568
+ "#endif"
569
+ end
570
+ end
571
+ end
572
+ end