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.
- 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
@@ -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
|