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
@@ -0,0 +1,103 @@
1
+ module Ikra
2
+ module RubyIntegration
3
+ # TODO: Handle non-singleton types
4
+
5
+ TYPE_INT_COERCE_TO_FLOAT = proc do |recv, other|
6
+ if other.include?(FLOAT_S)
7
+ FLOAT
8
+ elsif other.include?(INT_S)
9
+ INT
10
+ else
11
+ # At least one of the types INT_S or FLOAT_S are required
12
+ raise RuntimeError.new("Operation defined numeric values only (found #{other})")
13
+ end
14
+ end
15
+
16
+ TYPE_INT_RETURN_INT = proc do |recv, other|
17
+ if !other.include?(INT_S)
18
+ raise RuntimeError.new("Operation defined Int values only (found #{other})")
19
+ end
20
+
21
+ INT
22
+ end
23
+
24
+ TYPE_NUMERIC_RETURN_BOOL = proc do |recv, other|
25
+ if !other.include?(INT_S) && !other.include?(FLOAT_S)
26
+ raise RuntimeError.new("Expected type Int or Float, found #{other}")
27
+ end
28
+
29
+ BOOL
30
+ end
31
+
32
+ TYPE_BOOL_RETURN_BOOL = proc do |recv, other|
33
+ if !other.include?(BOOL_S)
34
+ raise RuntimeError.new("Expected type Bool, found #{other}")
35
+ end
36
+
37
+ BOOL
38
+ end
39
+
40
+ # TODO: fix int % float
41
+ implement INT_S, :%, TYPE_INT_COERCE_TO_FLOAT, 1, "(#0 % #N1)"
42
+ implement INT_S, :&, TYPE_INT_RETURN_INT, 1, "(#0 & #I1)"
43
+ implement INT_S, :|, TYPE_INT_RETURN_INT, 1, "(#0 | #I1)"
44
+ implement INT_S, :*, TYPE_INT_COERCE_TO_FLOAT, 1, "(#0 * #N1)"
45
+ # TODO: Find better implementation for Int pow
46
+ implement INT_S, :**, INT, 1, "((int) pow((double) #0, (double) #F1))"
47
+ implement INT_S, :+, TYPE_INT_COERCE_TO_FLOAT, 1, "(#0 + #N1)"
48
+ implement INT_S, :-, TYPE_INT_COERCE_TO_FLOAT, 1, "(#0 - #N1)"
49
+ # TODO: Implement unary -
50
+ implement INT_S, :/, TYPE_INT_COERCE_TO_FLOAT, 1, "(#0 / #N1)"
51
+ implement INT_S, :<, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 < #N1)"
52
+ implement INT_S, :<<, TYPE_INT_RETURN_INT, 1, "(#0 << #I1)"
53
+ implement INT_S, :<=, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 <= #N1)"
54
+ # TODO: Implement <=>
55
+ implement INT_S, :==, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 == #N1)"
56
+ implement INT_S, :"!=", TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 != #N1)"
57
+ implement INT_S, :>, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 > #N1)"
58
+ implement INT_S, :>=, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 >= #N1)"
59
+ implement INT_S, :>>, TYPE_INT_RETURN_INT, 1, "(#0 >> #I1)"
60
+ implement INT_S, :"^", TYPE_INT_RETURN_INT, 1, "(#0 ^ IN1)"
61
+
62
+ implement INT_S, :abs, INT, 0, "({ int value = #0; value < 0 ? -value : value; })"
63
+ implement INT_S, :bit_length, INT, 1, "({ int value = #0; (int) ceil(log2f(value < 0 ? -value : value + 1)); })"
64
+ implement INT_S, :div, INT, 1, "((int) (#0 / #I1))"
65
+ implement INT_S, :even?, BOOL, 0, "(#0 % 2 == 0)"
66
+ implement INT_S, :fdiv, FLOAT, 1, "(#0 / #F1)"
67
+ implement INT_S, :magnitude, INT, 0, "({ int value = #0; value < 0 ? -value : value; })"
68
+ implement INT_S, :modulo, TYPE_INT_COERCE_TO_FLOAT, 1, "(#0 % #N1)"
69
+ implement INT_S, :odd?, BOOL, 0, "(#0 % 2 != 0)"
70
+ implement INT_S, :size, INT, 0, "sizeof(int)"
71
+ implement INT_S, :next, INT, 0, "(#0 + 1)"
72
+ implement INT_S, :succ, INT, 0, "(#0 + 1)"
73
+ implement INT_S, :zero?, BOOL, 0, "(#0 == 0)"
74
+ implement INT_S, :to_f, FLOAT, 1, "((float) #0)"
75
+ implement INT_S, :to_i, INT, 1, "#0"
76
+
77
+ implement FLOAT_S, :%, FLOAT, 1, "fmodf(#0, #F1)"
78
+ implement FLOAT_S, :*, FLOAT, 1, "(#0 * #N1)"
79
+ implement FLOAT_S, :**, FLOAT, 1, "powf(#0, #F1)"
80
+ implement FLOAT_S, :+, FLOAT, 1, "(#0 + #N1)"
81
+ implement FLOAT_S, :-, FLOAT, 1, "(#0 - #N1)"
82
+ # TODO: Implement unary -
83
+ implement FLOAT_S, :/, FLOAT, 1, "(#0 / #N1)"
84
+ implement FLOAT_S, :<, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 < #N1)"
85
+ implement FLOAT_S, :<=, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 <= #N1)"
86
+ # TODO: Implement <=>
87
+ implement FLOAT_S, :==, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 == #N1)"
88
+ implement FLOAT_S, :"!=", TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 != #N1)"
89
+ implement FLOAT_S, :>, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 > #N1)"
90
+ implement FLOAT_S, :>=, TYPE_NUMERIC_RETURN_BOOL, 1, "(#0 >= #N1)"
91
+
92
+ implement FLOAT_S, :abs, FLOAT, 1, "fabsf(#0)"
93
+ implement FLOAT_S, :floor, FLOAT, 1, "floorf(#0)"
94
+ implement FLOAT_S, :to_f, FLOAT, 1, "#0"
95
+ implement FLOAT_S, :to_i, INT, 1, "((int) #0)"
96
+
97
+ implement BOOL_S, :&, TYPE_BOOL_RETURN_BOOL, 1, "(#0 & #B1)"
98
+ implement BOOL_S, :"&&", TYPE_BOOL_RETURN_BOOL, 1, "(#0 && #B1)"
99
+ implement BOOL_S, :"^", TYPE_BOOL_RETURN_BOOL, 1, "(#0 ^ #B1)"
100
+ implement BOOL_S, :|, TYPE_BOOL_RETURN_BOOL, 1, "(#0 | #B1)"
101
+ implement BOOL_S, :"||", TYPE_BOOL_RETURN_BOOL, 1, "(#0 || #B1)"
102
+ end
103
+ end
@@ -0,0 +1,16 @@
1
+ module Ikra
2
+ module RubyIntegration
3
+ # No need to do type inference or code generation, if a method is called on an
4
+ # on an instance of one of these classes.
5
+ INTERPRETER_ONLY_CLS_OBJ = [Ikra::Symbolic.singleton_class]
6
+
7
+
8
+ def self.is_interpreter_only?(type)
9
+ if !type.is_a?(Types::ClassType)
10
+ return false
11
+ end
12
+
13
+ return INTERPRETER_ONLY_CLS_OBJ.include?(type.cls)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ module Ikra
2
+ module RubyIntegration
3
+ MATH = Math.singleton_class.to_ikra_type
4
+
5
+ implement MATH, :acos, FLOAT, 1, "acosf(#F0)", pass_self: false
6
+ implement MATH, :acosh, FLOAT, 1, "acoshf(#F0)", pass_self: false
7
+ implement MATH, :asin, FLOAT, 1, "asinf(#F0)", pass_self: false
8
+ implement MATH, :asinh, FLOAT, 1, "asinhf(#F0)", pass_self: false
9
+ implement MATH, :atan, FLOAT, 1, "atanf(#F0)", pass_self: false
10
+ implement MATH, :atan2, FLOAT, 2, "atan2f(#F0, #F1)", pass_self: false
11
+ implement MATH, :atanh, FLOAT, 1, "atanhf(#F0)", pass_self: false
12
+ implement MATH, :cbrt, FLOAT, 1, "cbrtf(#F0)", pass_self: false
13
+ implement MATH, :cos, FLOAT, 1, "cosf(#F0)", pass_self: false
14
+ implement MATH, :cosh, FLOAT, 1, "coshf(#F0)", pass_self: false
15
+ implement MATH, :erf, FLOAT, 1, "erff(#F0)", pass_self: false
16
+ implement MATH, :erfc, FLOAT, 1, "erfcf(#F0)", pass_self: false
17
+ implement MATH, :exp, FLOAT, 1, "expf(#F0)", pass_self: false
18
+ implement MATH, :gamma, FLOAT, 1, "tgammaf(#F0)", pass_self: false
19
+ implement MATH, :hypot, FLOAT, 2, "hypotf(#F0, #F1)", pass_self: false
20
+ implement MATH, :ldexp, FLOAT, 2, "ldexpf(#F0, #F1)", pass_self: false
21
+ implement MATH, :lgamma, FLOAT, 1, "lgammaf(#F0)", pass_self: false
22
+ implement MATH, :log, FLOAT, 1, "logf(#F0)", pass_self: false
23
+ implement MATH, :log10, FLOAT, 1, "log10f(#F0)", pass_self: false
24
+ implement MATH, :log2, FLOAT, 1, "log2f(#F0)", pass_self: false
25
+ implement MATH, :sin, FLOAT, 1, "sinf(#F0)", pass_self: false
26
+ implement MATH, :sinh, FLOAT, 1, "sinhf(#F0)", pass_self: false
27
+ implement MATH, :sqrt, FLOAT, 1, "sqrtf(#F0)", pass_self: false
28
+ implement MATH, :tan, FLOAT, 1, "tanf(#F0)", pass_self: false
29
+ implement MATH, :tanh, FLOAT, 1, "tanhf(#F0)", pass_self: false
30
+ end
31
+ end
32
+
@@ -0,0 +1,256 @@
1
+ require_relative "../types/types"
2
+
3
+ module Ikra
4
+ module RubyIntegration
5
+ INT = Types::UnionType.create_int
6
+ FLOAT = Types::UnionType.create_float
7
+ BOOL = Types::UnionType.create_bool
8
+
9
+ INT_S = INT.singleton_type
10
+ FLOAT_S = FLOAT.singleton_type
11
+ BOOL_S = BOOL.singleton_type
12
+
13
+ class Implementation
14
+ attr_reader :num_params
15
+ attr_reader :implementation
16
+ attr_reader :pass_self
17
+ attr_reader :return_type
18
+
19
+ # If set to true, all argument should have a singleton type. This is required for
20
+ # operations on ArrayCommands (e.g., pzip).
21
+ attr_reader :expect_singleton_args
22
+
23
+ def initialize(
24
+ num_params:,
25
+ return_type:,
26
+ implementation:,
27
+ pass_self: true,
28
+ expect_singleton_args: false)
29
+
30
+ @num_params = num_params
31
+ @implementation = implementation
32
+ @pass_self = pass_self
33
+ @return_type = return_type
34
+ @expect_singleton_args = expect_singleton_args
35
+ end
36
+ end
37
+
38
+ @@impls = {}
39
+ @@impls.default_proc = proc do |hash, key|
40
+ hash[key] = {}
41
+ end
42
+
43
+ def self.implement(
44
+ rcvr_type,
45
+ method_name,
46
+ return_type,
47
+ num_params,
48
+ impl,
49
+ pass_self: true,
50
+ expect_singleton_args: false)
51
+
52
+ @@impls[rcvr_type][method_name] = Implementation.new(
53
+ num_params: num_params,
54
+ return_type: return_type,
55
+ implementation: impl,
56
+ pass_self: pass_self,
57
+ expect_singleton_args: expect_singleton_args)
58
+ end
59
+
60
+ def self.has_implementation?(rcvr_type, method_name)
61
+ return find_impl(rcvr_type, method_name) != nil
62
+ end
63
+
64
+ def self.should_pass_self?(rcvr_type, method_name)
65
+ return find_impl(rcvr_type, method_name).pass_self
66
+ end
67
+
68
+ def self.expect_singleton_args?(rcvr_type, method_name)
69
+ return find_impl(rcvr_type, method_name).expect_singleton_args
70
+ end
71
+
72
+ # Returns the implementation (CUDA source code snippet) for a method with name
73
+ # [method_name] defined on [rcvr_type].
74
+ #
75
+ # This method also receives references to the receiver AST node and to AST nodes for
76
+ # arguments. In most cases, these AST nodes are directly translated to source code
77
+ # using `translator` (a [Translator::ASTTranslator]). However, if an implementation
78
+ # is given through a block ([Proc]), the implementation might decide to not use the
79
+ # translation (e.g., translation of parallel sections in host sections).
80
+ #
81
+ # [receiver] must have a singleton type.
82
+ def self.get_implementation(receiver, method_name, arguments, translator, result_type)
83
+ impl = find_impl(receiver.get_type.singleton_type, method_name)
84
+ source = impl.implementation
85
+
86
+ if source.is_a?(Proc)
87
+ source = source.call(receiver, method_name, arguments, translator, result_type)
88
+ end
89
+
90
+ sub_code = arguments.map do |arg| arg.accept(translator.expression_translator) end
91
+ sub_types = arguments.map do |arg| arg.get_type end
92
+
93
+ if impl.pass_self
94
+ sub_code.insert(0, receiver.accept(translator.expression_translator))
95
+ sub_types.insert(0, receiver.get_type)
96
+ end
97
+
98
+ sub_indices = (0...source.length).find_all do |index|
99
+ source[index] == "#"
100
+ end
101
+ substitutions = {}
102
+ sub_indices.each do |index|
103
+ if source[index + 1] == "F"
104
+ # Insert float
105
+ arg_index = source[index + 2].to_i
106
+
107
+ if arg_index >= sub_code.size
108
+ raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
109
+ end
110
+
111
+ substitutions["\#F#{arg_index}"] = code_argument(FLOAT_S, sub_types[arg_index], sub_code[arg_index])
112
+ elsif source[index + 1] == "I"
113
+ # Insert integer
114
+ arg_index = source[index + 2].to_i
115
+
116
+ if arg_index >= sub_code.size
117
+ raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
118
+ end
119
+
120
+ substitutions["\#I#{arg_index}"] = code_argument(INT_S, sub_types[arg_index], sub_code[arg_index])
121
+ elsif source[index + 1] == "B"
122
+ # Insert integer
123
+ arg_index = source[index + 2].to_i
124
+
125
+ if arg_index >= sub_code.size
126
+ raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
127
+ end
128
+
129
+ substitutions["\#B#{arg_index}"] = code_argument(BOOL_S, sub_types[arg_index], sub_code[arg_index])
130
+ elsif source[index + 1] == "N"
131
+ # Numeric, coerce integer to float
132
+ arg_index = source[index + 2].to_i
133
+
134
+ if arg_index >= sub_code.size
135
+ raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
136
+ end
137
+
138
+ if sub_types[arg_index].include?(FLOAT_S)
139
+ expected_type = FLOAT_S
140
+ else
141
+ expected_type = INT_S
142
+ end
143
+
144
+ substitutions["\#N#{arg_index}"] = code_argument(expected_type, sub_types[arg_index], sub_code[arg_index])
145
+ else
146
+ arg_index = source[index + 1].to_i
147
+
148
+ if arg_index >= sub_code.size
149
+ raise ArgumentError.new("Argument missing: Expected at least #{arg_index + 1}, found #{sub_code.size}")
150
+ end
151
+
152
+ substitutions["\##{arg_index}"] = sub_code[arg_index]
153
+ end
154
+ end
155
+
156
+ substitutions.each do |key, value|
157
+ # Do not use `gsub!` here!
158
+ source = source.gsub(key, value)
159
+ end
160
+
161
+ return source
162
+ end
163
+
164
+ # Retrieves the return type of a method invocation for receiver type [rcvr_type],
165
+ # selector [method_name], and argument types [arg_types].
166
+ #
167
+ # In addition, this method accepts an optional parameter [node] containing the send node
168
+ # (abstract syntax tree node). That node is passed to type inference procs. This is
169
+ # required for symbolic execution of array commands inside host sections.
170
+ def self.get_return_type(rcvr_type, method_name, *arg_types, send_node: nil)
171
+ return_type = find_impl(rcvr_type, method_name).return_type
172
+ num_params = find_impl(rcvr_type, method_name).num_params
173
+
174
+ if return_type.is_a?(Proc)
175
+ # Return type depends on argument types
176
+ if num_params.is_a?(Fixnum) && num_params != arg_types.size
177
+ raise ArgumentError.new(
178
+ "#{num_params} arguments expected but #{arg_types.size} given")
179
+ elsif num_params.is_a?(Range) && !num_params.include?(arg_types.size)
180
+ raise ArgumentError.new(
181
+ "#{num_params} arguments expected but #{arg_types.size} given")
182
+ else
183
+ if send_node == nil
184
+ return return_type.call(rcvr_type, *arg_types)
185
+ else
186
+ return return_type.call(rcvr_type, *arg_types, send_node: send_node)
187
+ end
188
+ end
189
+ else
190
+ return return_type
191
+ end
192
+ end
193
+
194
+ private
195
+
196
+ def self.code_argument(expected_type, arg_type, code)
197
+ if arg_type.is_singleton?
198
+ if expected_type != arg_type.singleton_type
199
+ # Try to cast
200
+ return "((#{expected_type.to_c_type}) #{code})"
201
+ else
202
+ return code
203
+ end
204
+ else
205
+ # Extract from union type
206
+ result = StringIO.new
207
+
208
+ result << "({ union_t arg = #{code};\n"
209
+ result << " #{expected_type.to_c_type} result;\n"
210
+ result << " switch (arg.class_id) {\n"
211
+
212
+ for type in arg_type
213
+ c_type = expected_type.to_c_type
214
+ result << " case #{type.class_id}:\n"
215
+ # TODO: This works only for primitive types
216
+ result << " result = (#{c_type}) arg.value.#{type.to_c_type}_;\n"
217
+ result << " break;\n"
218
+ end
219
+
220
+ result << " default:\n"
221
+ result << " // TODO: throw exception\n"
222
+ result << " }\n"
223
+ result << " result;\n"
224
+ result << "})"
225
+
226
+ return result.string
227
+ end
228
+ end
229
+
230
+ def self.find_impl(rcvr_type, method_name)
231
+ if @@impls.include?(rcvr_type) && @@impls[rcvr_type].include?(method_name)
232
+ return @@impls[rcvr_type][method_name]
233
+ else
234
+ # Evaluate blocks
235
+ for type_or_block in @@impls.keys
236
+ if type_or_block.is_a?(Proc)
237
+ if type_or_block.call(rcvr_type)
238
+ if @@impls[type_or_block].include?(method_name)
239
+ return @@impls[type_or_block][method_name]
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+ # No implementation found
246
+ return nil
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ require_relative "core"
253
+ require_relative "math"
254
+ require_relative "array"
255
+ require_relative "array_command"
256
+ require_relative "interpreter"
@@ -0,0 +1,115 @@
1
+ require_relative "../ast/host_section_builder"
2
+
3
+ module Ikra
4
+ module Symbolic
5
+ # The return value of a host section. For the moment, every host section can only have
6
+ # one result.
7
+ class ArrayHostSectionCommand
8
+ include ArrayCommand
9
+
10
+ attr_reader :block
11
+ attr_reader :section_input
12
+
13
+ def initialize(*section_input, &block)
14
+ @block = block
15
+ @section_input = section_input
16
+ end
17
+
18
+ def size
19
+ execute
20
+ return @result.size
21
+ end
22
+
23
+ # Returns the abstract syntax tree for this section.
24
+ def block_def_node
25
+ if @ast == nil
26
+ # Get array of block parameter names
27
+ block_params = block.parameters.map do |param|
28
+ param[1]
29
+ end
30
+
31
+ parser_local_vars = command_binding.local_variables + block_params
32
+ source = Parsing.parse_block(block, parser_local_vars)
33
+ @ast = AST::BlockDefNode.new(
34
+ parameters: block_params,
35
+ ruby_block: block, # necessary to get binding
36
+ body: AST::HostSectionBuilder.from_parser_ast(source))
37
+ end
38
+
39
+ return @ast
40
+ end
41
+
42
+ def command_translator_class
43
+ return Translator::HostSectionCommandTranslator
44
+ end
45
+
46
+ class Binding
47
+ def local_variables
48
+
49
+ end
50
+ end
51
+ end
52
+
53
+ # An array that that is referenced using C++/CUDA expressions. Such an array does not
54
+ # necessarily have to be present in the Ruby interpreter. Its size does also not have to
55
+ # be known at compile time.
56
+ class ArrayInHostSectionCommand
57
+ include ArrayCommand
58
+
59
+ attr_accessor :target
60
+ attr_accessor :base_type
61
+
62
+ def initialize(target, base_type, block_size: DEFAULT_BLOCK_SIZE)
63
+ super(block_size: block_size)
64
+
65
+ if base_type == nil
66
+ raise AssertionError.new("base_type missing")
67
+ end
68
+
69
+ # One thread per array element
70
+ @input = [SingleInput.new(command: target, pattern: :tid)]
71
+ @base_type = base_type
72
+ end
73
+
74
+ def size
75
+ # Size is not known at compile time. Return a source code string here.
76
+ return "#{input.first.command}->size()"
77
+ end
78
+
79
+ # TODO: Support multiple dimensions
80
+ def dimensions
81
+ return [size]
82
+ end
83
+
84
+ def ==(other)
85
+ return super(other) && base_type == other.base_type
86
+ end
87
+ end
88
+
89
+ class FixedSizeArrayInHostSectionCommand < ArrayInHostSectionCommand
90
+ include ArrayCommand
91
+
92
+ attr_accessor :target
93
+ attr_accessor :base_type
94
+ attr_accessor :dimensions
95
+
96
+ def initialize(target, base_type, dimensions, block_size: DEFAULT_BLOCK_SIZE)
97
+ super(target, base_type, block_size: block_size)
98
+
99
+ @dimensions = dimensions
100
+ end
101
+
102
+ def size
103
+ return dimensions.reduce(:*)
104
+ end
105
+
106
+ def ==(other)
107
+ return super(other) && dimensions == other.dimensions
108
+ end
109
+ end
110
+
111
+ def self.host_section(*section_input, &block)
112
+ return ArrayHostSectionCommand.new(*section_input, &block)
113
+ end
114
+ end
115
+ end