ikra 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ast/builder.rb +225 -77
- data/lib/ast/host_section_builder.rb +38 -0
- data/lib/ast/interpreter.rb +67 -0
- data/lib/ast/lexical_variables_enumerator.rb +3 -2
- data/lib/ast/nodes.rb +521 -31
- data/lib/ast/printer.rb +116 -18
- data/lib/ast/ssa_generator.rb +192 -0
- data/lib/ast/visitor.rb +235 -21
- data/lib/config/configuration.rb +28 -3
- data/lib/config/os_configuration.rb +62 -9
- data/lib/cpu/cpu_implementation.rb +39 -0
- data/lib/ikra.rb +13 -3
- data/lib/resources/cuda/allocate_device_memory.cpp +5 -0
- data/lib/resources/cuda/allocate_host_memory.cpp +1 -0
- data/lib/resources/cuda/allocate_memcpy_environment_to_device.cpp +11 -0
- data/lib/resources/cuda/ast/assignment.cpp +1 -0
- data/lib/resources/cuda/block_function_head.cpp +7 -1
- data/lib/resources/cuda/entry_point.cpp +47 -0
- data/lib/resources/cuda/env_builder_copy_array.cpp +8 -2
- data/lib/resources/cuda/free_device_memory.cpp +3 -0
- data/lib/resources/cuda/free_memory_for_command.cpp +24 -0
- data/lib/resources/cuda/header.cpp +23 -9
- data/lib/resources/cuda/header_structs.cpp +92 -0
- data/lib/resources/cuda/host_section_block_function_head.cpp +12 -0
- data/lib/resources/cuda/host_section_entry_point.cpp +55 -0
- data/lib/resources/cuda/host_section_free_device_memory.cpp +18 -0
- data/lib/resources/cuda/host_section_launch_parallel_section.cpp +14 -0
- data/lib/resources/cuda/host_section_malloc_memcpy_device_to_host.cpp +10 -0
- data/lib/resources/cuda/kernel.cpp +9 -2
- data/lib/resources/cuda/launch_kernel.cpp +5 -0
- data/lib/resources/cuda/memcpy_device_to_host.cpp +3 -0
- data/lib/resources/cuda/memcpy_device_to_host_expr.cpp +10 -0
- data/lib/resources/cuda/reduce_body.cpp +88 -0
- data/lib/resources/cuda/stencil_array_reconstruction.cpp +2 -0
- data/lib/resources/cuda/stencil_body.cpp +16 -0
- data/lib/resources/cuda/struct_definition.cpp +4 -0
- data/lib/ruby_core/array.rb +34 -0
- data/lib/ruby_core/array_command.rb +313 -0
- data/lib/ruby_core/core.rb +103 -0
- data/lib/ruby_core/interpreter.rb +16 -0
- data/lib/ruby_core/math.rb +32 -0
- data/lib/ruby_core/ruby_integration.rb +256 -0
- data/lib/symbolic/host_section.rb +115 -0
- data/lib/symbolic/input.rb +87 -0
- data/lib/symbolic/input_visitor.rb +68 -0
- data/lib/symbolic/symbolic.rb +793 -117
- data/lib/symbolic/visitor.rb +70 -8
- data/lib/translator/array_command_struct_builder.rb +163 -0
- data/lib/translator/ast_translator.rb +572 -0
- data/lib/translator/block_translator.rb +104 -48
- data/lib/translator/commands/array_combine_command.rb +41 -0
- data/lib/translator/commands/array_identity_command.rb +28 -0
- data/lib/translator/commands/array_index_command.rb +52 -0
- data/lib/translator/commands/array_reduce_command.rb +135 -0
- data/lib/translator/commands/array_stencil_command.rb +129 -0
- data/lib/translator/commands/array_zip_command.rb +30 -0
- data/lib/translator/commands/command_translator.rb +264 -0
- data/lib/translator/cuda_errors.rb +32 -0
- data/lib/translator/environment_builder.rb +263 -0
- data/lib/translator/host_section/array_host_section_command.rb +150 -0
- data/lib/translator/host_section/array_in_host_section_command.rb +41 -0
- data/lib/translator/host_section/ast_translator.rb +14 -0
- data/lib/translator/host_section/parallel_section_invocation_visitor.rb +20 -0
- data/lib/translator/host_section/program_builder.rb +89 -0
- data/lib/translator/input_translator.rb +226 -0
- data/lib/translator/kernel_builder.rb +137 -0
- data/lib/translator/kernel_launcher/for_loop_kernel_launcher.rb +40 -0
- data/lib/translator/kernel_launcher/kernel_launcher.rb +259 -0
- data/lib/translator/kernel_launcher/while_loop_kernel_launcher.rb +38 -0
- data/lib/translator/last_returns_visitor.rb +19 -10
- data/lib/translator/program_builder.rb +197 -0
- data/lib/translator/program_launcher.rb +273 -0
- data/lib/translator/struct_type.rb +55 -0
- data/lib/translator/translator.rb +34 -11
- data/lib/translator/variable_classifier_visitor.rb +56 -0
- data/lib/types/inference/ast_inference.rb +586 -0
- data/lib/types/inference/clear_types_visitor.rb +11 -0
- data/lib/types/inference/command_inference.rb +101 -0
- data/lib/types/inference/input_inference.rb +62 -0
- data/lib/types/{object_tracer.rb → inference/object_tracer.rb} +5 -6
- data/lib/types/inference/ruby_extension.rb +35 -0
- data/lib/types/inference/symbol_table.rb +131 -0
- data/lib/types/types.rb +14 -0
- data/lib/types/types/array_command_type.rb +123 -0
- data/lib/types/types/array_type.rb +137 -0
- data/lib/types/{class_type.rb → types/class_type.rb} +42 -18
- data/lib/types/{primitive_type.rb → types/primitive_type.rb} +20 -7
- data/lib/types/types/ruby_type.rb +88 -0
- data/lib/types/types/struct_type.rb +179 -0
- data/lib/types/types/union_type.rb +239 -0
- metadata +160 -18
- data/lib/ast/method_definition.rb +0 -37
- data/lib/ast/translator.rb +0 -264
- data/lib/resources/cuda/kernel_launcher.cpp +0 -28
- data/lib/scope.rb +0 -166
- data/lib/translator/command_translator.rb +0 -421
- data/lib/translator/local_variables_enumerator.rb +0 -35
- data/lib/translator/method_translator.rb +0 -24
- data/lib/types/array_type.rb +0 -51
- data/lib/types/ruby_extension.rb +0 -67
- data/lib/types/ruby_type.rb +0 -45
- data/lib/types/type_inference.rb +0 -382
- data/lib/types/union_type.rb +0 -155
data/lib/symbolic/visitor.rb
CHANGED
@@ -1,14 +1,26 @@
|
|
1
1
|
module Ikra
|
2
2
|
module Symbolic
|
3
|
-
class
|
3
|
+
class ArrayIndexCommand
|
4
4
|
def accept(visitor)
|
5
|
-
visitor.
|
5
|
+
visitor.visit_array_index_command(self)
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
class
|
9
|
+
class ArrayCombineCommand
|
10
10
|
def accept(visitor)
|
11
|
-
visitor.
|
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
|
76
|
+
def visit_array_combine_command(command)
|
33
77
|
visit_array_command(command)
|
34
78
|
end
|
35
79
|
|
36
|
-
def
|
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
|