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