ikra 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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