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,137 @@
1
+ # No explicit `require`s. This file should be includes via types.rb
2
+
3
+ module Ikra
4
+ module Types
5
+ class ArrayType
6
+ include RubyType
7
+
8
+ class << self
9
+ alias_method :new_original, :new
10
+
11
+ # Ensure singleton per class
12
+ def new(inner_type)
13
+ if @cache == nil
14
+ @cache = {}
15
+ @cache.default_proc = proc do |hash, key|
16
+ hash[key] = new_original(key)
17
+ end
18
+ end
19
+
20
+ return @cache[inner_type]
21
+ end
22
+ end
23
+
24
+ attr_reader :inner_type
25
+
26
+ def ==(other)
27
+ return other.class == self.class && other.inner_type == self.inner_type
28
+ end
29
+
30
+ def initialize(inner_type)
31
+ if not inner_type.is_union_type?
32
+ raise AssertionError.new("Union type expected")
33
+ end
34
+
35
+ @inner_type = inner_type
36
+ end
37
+
38
+ def to_s
39
+ return "[#{self.class.to_s}, inner_type = #{inner_type}]"
40
+ end
41
+
42
+ def to_c_type
43
+ return "#{@inner_type.to_c_type} *"
44
+ end
45
+
46
+ def to_ffi_type
47
+ return :pointer
48
+ end
49
+
50
+ def to_ruby_type
51
+ return ::Array
52
+ end
53
+ end
54
+
55
+ class LocationAwareArrayType < ArrayType
56
+ # Determines if the array is allocated on the host or on the device
57
+ attr_reader :location
58
+
59
+ def ==(other)
60
+ return super && self.location == other.location
61
+ end
62
+
63
+ def initialize(inner_type, location)
64
+ @inner_type = inner_type
65
+ @location = location
66
+ end
67
+
68
+ def to_c_type
69
+ return "variable_size_array_t"
70
+ end
71
+ end
72
+
73
+ class LocationAwareVariableSizeArrayType < LocationAwareArrayType
74
+ class << self
75
+ def new(inner_type, location: :device)
76
+ if @cache == nil
77
+ @cache = {}
78
+ @cache.default_proc = Proc.new do |hash, key|
79
+ hash[key] = new_original(*key)
80
+ end
81
+ end
82
+
83
+ return @cache[[inner_type, location]]
84
+ end
85
+ end
86
+
87
+ def to_command
88
+ # No fusion possible here. The first parameter (target) is a reference to the
89
+ # array command struct representing the [ArrayInHostSectionCommand].
90
+ # TODO: The code depends on the template (variable name `cmd` and `input_0`).
91
+ return Symbolic::ArrayInHostSectionCommand.new("((#{@inner_type.to_c_type} *) cmd->input_0)", @inner_type)
92
+ end
93
+ end
94
+
95
+ class LocationAwareFixedSizeArrayType < LocationAwareArrayType
96
+ class << self
97
+ def new(inner_type, dimensions, location: :device)
98
+ if @cache == nil
99
+ @cache = {}
100
+ @cache.default_proc = Proc.new do |hash, key|
101
+ hash[key] = new_original(*key)
102
+ end
103
+ end
104
+
105
+ return @cache[[inner_type, location, dimensions]]
106
+ end
107
+ end
108
+
109
+ attr_reader :dimensions
110
+
111
+ def initialize(inner_type, location, dimensions)
112
+ super(inner_type, location)
113
+ @dimensions = dimensions
114
+ end
115
+
116
+ def to_command
117
+ # No fusion possible here. The first parameter (target) is a reference to the
118
+ # array command struct representing the [ArrayInHostSectionCommand].
119
+ # TODO: The code depends on the template (variable name `cmd` and `input_0`).
120
+ return Symbolic::FixedSizeArrayInHostSectionCommand.new(
121
+ "((#{@inner_type.to_c_type} *) cmd->input_0)", @inner_type, @dimensions)
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ class Array
128
+ def ikra_type
129
+ inner_type = Ikra::Types::UnionType.new
130
+
131
+ self.each do |element|
132
+ inner_type.add(element.ikra_type)
133
+ end
134
+
135
+ return Ikra::Types::ArrayType.new(inner_type)
136
+ end
137
+ end
@@ -1,9 +1,9 @@
1
+ # No explicit `require`s. This file should be includes via types.rb
2
+
1
3
  require "set"
2
- require_relative "ruby_type"
3
- require_relative "union_type"
4
- require_relative "../sourcify/lib/sourcify"
5
- require_relative "../parsing"
6
- require_relative "../ast/builder"
4
+ require_relative "../../sourcify/lib/sourcify"
5
+ require_relative "../../parsing"
6
+ require_relative "../../ast/builder"
7
7
 
8
8
  module Ikra
9
9
  module Types
@@ -27,6 +27,10 @@ module Ikra
27
27
  end
28
28
  end
29
29
 
30
+ def ==(other)
31
+ return other.class == self.class && other.cls == self.cls
32
+ end
33
+
30
34
  def initialize(cls)
31
35
  @cls = cls
32
36
  @inst_vars_read = Set.new
@@ -71,23 +75,36 @@ module Ikra
71
75
  "obj_id_t"
72
76
  end
73
77
 
78
+ # Generates a class name for [@cls], which is a valid C++ identifier.
79
+ #
80
+ # For example:
81
+ # A --> A
82
+ # #<Class: A> --> singleton_A
83
+ def class_name
84
+ # Handle name generation for singleton classes
85
+ return ruby_name.gsub("\#<Class:", "singleton_").gsub(">", "")
86
+ end
87
+
74
88
  def mangled_method_name(selector)
75
- "_method_#{@cls.to_s}_#{selector}_"
89
+ "_method_#{class_name}_#{selector}_"
76
90
  end
77
91
 
78
92
  def inst_var_array_name(inst_var_name)
79
93
  if inst_var_name.to_s[0] != "@"
80
- raise "Expected instance variable identifier"
94
+ raise AssertionError.new("Expected instance variable identifier")
81
95
  end
82
96
 
83
- "_iv_#{@cls.to_s}_#{inst_var_name.to_s[1..-1]}_"
97
+ "_iv_#{class_name}_#{inst_var_name.to_s[1..-1]}_"
84
98
  end
85
99
 
86
100
  def method_ast(selector)
87
101
  source = Parsing.parse_method(cls.instance_method(selector))
88
- ast = AST::Builder.from_parser_ast(source)
89
- ast.class_owner = @cls
90
- ast
102
+ return AST::Builder.from_parser_ast(source)
103
+ end
104
+
105
+ def method_binding(selector)
106
+ # TODO: Fix binding
107
+ return cls.instance_method(selector).send(:binding)
91
108
  end
92
109
 
93
110
  def method_parameters(selector)
@@ -98,12 +115,13 @@ module Ikra
98
115
  end
99
116
  end
100
117
 
101
- def should_generate_type?
102
- to_ruby_type != Class
118
+ def should_generate_self_arg?
119
+ # Do not generate type for singleton classes
120
+ return !to_ruby_type.is_a?(Module.singleton_class)
103
121
  end
104
122
 
105
123
  def to_s
106
- "<class: #{@cls.to_s}>"
124
+ "<class: #{class_name}>"
107
125
  end
108
126
 
109
127
  def c_size
@@ -117,12 +135,18 @@ end
117
135
  class Object
118
136
  def self.to_ikra_type
119
137
  # TODO: should this method be defined on Class?
120
- Ikra::Types::ClassType.new(self)
138
+ return Ikra::Types::ClassType.new(self)
121
139
  end
122
140
 
123
- # Returns the [Ikra::Types::RubyType] for this class. This version of the method receives the actual object as a parameter. This is necessary for example to determine the exact type of an array (including inner type).
124
- def self.to_ikra_type_obj(object)
125
- to_ikra_type
141
+ # Returns the [Ikra::Types::RubyType] for this object. Instance of the same Ruby class can
142
+ # principally have different Ikra types. Thus, this method is defined as an instance method.
143
+ def ikra_type
144
+ if self.is_a?(Module)
145
+ return self.singleton_class.to_ikra_type
146
+ else
147
+ # TODO: Double check if we always want to have the singleton class?
148
+ return self.class.to_ikra_type
149
+ end
126
150
  end
127
151
  end
128
152
 
@@ -1,4 +1,4 @@
1
- require_relative "ruby_type"
1
+ # No explicit `require`s. This file should be includes via types.rb
2
2
 
3
3
  module Ikra
4
4
  module Types
@@ -6,19 +6,26 @@ module Ikra
6
6
  include RubyType
7
7
 
8
8
  attr_reader :c_size
9
+ attr_reader :class_id
9
10
 
10
- def initialize(c_type, ruby_type, c_size, ffi_type)
11
+ def initialize(c_type, ruby_type, c_size, ffi_type, class_id)
11
12
  @c_type = c_type
12
13
  @ruby_type = ruby_type
13
14
  @c_size = c_size
14
15
  @ffi_type = ffi_type
16
+ @class_id = class_id
15
17
  end
16
18
 
17
- Int = self.new("int", Fixnum, 4, :int)
18
- Float = self.new("float", Float, 4, :float)
19
- Bool = self.new("bool", TrueClass, 1, :bool)
20
- Void = self.new("void", nil, 0, :void)
19
+ Int = self.new("int", Fixnum, 4, :int, 1)
20
+ Float = self.new("float", Float, 4, :float, 2)
21
+ Bool = self.new("bool", TrueClass, 1, :bool, 3)
22
+ Void = self.new("void", nil, 0, :void, 4)
23
+ Nil = self.new("int", NilClass, 4, :int, 5)
21
24
 
25
+ def ==(other)
26
+ return other.is_a?(PrimitiveType) && other.class_id == class_id
27
+ end
28
+
22
29
  def to_ruby_type
23
30
  @ruby_type
24
31
  end
@@ -30,7 +37,7 @@ module Ikra
30
37
  def to_ffi_type
31
38
  @ffi_type
32
39
  end
33
-
40
+
34
41
  def is_primitive?
35
42
  true
36
43
  end
@@ -71,3 +78,9 @@ class FalseClass
71
78
  Ikra::Types::PrimitiveType::Bool
72
79
  end
73
80
  end
81
+
82
+ class NilClass
83
+ def self.to_ikra_type
84
+ Ikra::Types::PrimitiveType::Nil
85
+ end
86
+ end
@@ -0,0 +1,88 @@
1
+ require "set"
2
+
3
+ module Ikra
4
+ module Types
5
+
6
+ # Defines the minimal interface for Ikra types. Instances of {UnionType} are expected in most cases.
7
+ module RubyType
8
+ @@next_class_id = 10
9
+
10
+ def to_str
11
+ return to_s
12
+ end
13
+
14
+ def inspect
15
+ return to_s
16
+ end
17
+
18
+ def to_ruby_type
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def to_c_type
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def is_primitive?
27
+ false
28
+ end
29
+
30
+ def is_union_type?
31
+ false
32
+ end
33
+
34
+ def should_generate_self_arg?
35
+ return true
36
+ end
37
+
38
+ def to_array_type
39
+ # TODO: This should probably not return a union type by default
40
+ return ArrayType.new(self).to_union_type
41
+ end
42
+
43
+ def to_union_type
44
+ return UnionType.new(self)
45
+ end
46
+
47
+ def class_id
48
+ if @class_id == nil
49
+ @class_id = @@next_class_id
50
+ @@next_class_id += 1
51
+ end
52
+
53
+ @class_id
54
+ end
55
+
56
+ def eql?(other)
57
+ return self == other
58
+ end
59
+
60
+ def hash
61
+ # TODO: Implement
62
+ return 0
63
+ end
64
+ end
65
+
66
+ # This type is marker and denotes that an expression should be executed only in the Ruby
67
+ # interpreter. No CUDA code should be generated for such expressions.
68
+ class InterpreterOnlyType
69
+ include RubyType
70
+
71
+ def self.new
72
+ if @singleton_instance == nil
73
+ @singleton_instance = super
74
+ end
75
+
76
+ return @singleton_instance
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ class Array
83
+ def to_type_array_string
84
+ "[" + map do |set|
85
+ set.to_s
86
+ end.join(", ") + "]"
87
+ end
88
+ end
@@ -0,0 +1,179 @@
1
+ # No explicit `require`s. This file should be includes via types.rb
2
+
3
+ require "ffi"
4
+
5
+ module Ikra
6
+ module Types
7
+ # This type represents a C++ struct. It has fields, each of which has
8
+ # a type.
9
+ class StructType
10
+ include RubyType
11
+
12
+ attr_reader :fields
13
+
14
+ class << self
15
+ # Ensure singleton per class
16
+ def new(fields)
17
+ if @cache == nil
18
+ @cache = {}
19
+ end
20
+
21
+ if not @cache.include?(identifier_from_hash(fields))
22
+ @cache[identifier_from_hash(fields)] = super(fields)
23
+ end
24
+
25
+ @cache[identifier_from_hash(fields)]
26
+ end
27
+
28
+ # Generates a unique type identifier based on the types of the struct fields.
29
+ def identifier_from_hash(fields)
30
+ identifier = "indexed_struct_#{fields.size}_lt_"
31
+
32
+ type_parts = fields.map do |key, value|
33
+ value.to_c_type
34
+ end
35
+
36
+ identifier = identifier + type_parts.join("_")
37
+
38
+ return identifier + "_gt_t"
39
+ end
40
+ end
41
+
42
+ def ==(other)
43
+ return other.class == self.class && other.fields == self.fields
44
+ end
45
+
46
+ def initialize(fields)
47
+ fields.each do |key, value|
48
+ if not value.is_union_type?
49
+ raise AssertionError.new("Union type expected for field #{key}")
50
+ end
51
+ end
52
+
53
+ @fields = fields
54
+ end
55
+
56
+ def to_c_type
57
+ return StructType.identifier_from_hash(@fields)
58
+ end
59
+
60
+ def to_ffi_type
61
+ # TODO: Support transfering zipped data back from GPU
62
+ return to_ruby_type
63
+ end
64
+
65
+ def to_ruby_type
66
+ # TODO: general structs
67
+ raise NotImplementedError.new
68
+ end
69
+ end
70
+
71
+ # This type represents the type of an array that is the result of
72
+ # zipping two arrays. [ZipStructType] is similar to [StructType] but
73
+ # can be accessed via indices.
74
+ class ZipStructType < StructType
75
+ class << self
76
+ def new(*types)
77
+ identifiers = Array.new(types.size) do |index|
78
+ :"field_#{index}"
79
+ end
80
+
81
+ super(Hash[identifiers.zip(types)])
82
+ end
83
+ end
84
+
85
+ # Performs type inference for the result of accessing this Zip "Array" by index.
86
+ def get_return_type(selector, *arg_nodes)
87
+ # TODO: Can only handle single cases at the moment. This should eventually forward
88
+ # to Array integration code.
89
+
90
+ if selector != :"[]"
91
+ raise AssertionError.new(
92
+ "Selector not supported for ZipStructType: #{selector}")
93
+ end
94
+
95
+ if arg_nodes.size != 1
96
+ raise AssertionError.new("Expected exactly one argument")
97
+ end
98
+
99
+ if arg_nodes.first.class == AST::IntLiteralNode
100
+ if arg_nodes.first.value >= @fields.size
101
+ raise AssertionError.new(
102
+ "ZipStruct index out of bounds: #{arg_nodes.first.value}")
103
+ end
104
+
105
+ return self[arg_nodes.first.value]
106
+ else
107
+ return get_return_type_non_constant(selector)
108
+ end
109
+ end
110
+
111
+ # Performs type inference for the result of accessing this Zip "Array" by index.
112
+ def get_return_type_non_constant(selector)
113
+ # TODO: Can only handle single cases at the moment. This should eventually forward
114
+ # to Array integration code.
115
+
116
+ # TODO: This code assumes that the all struct elements have the same type
117
+ return self[0]
118
+ end
119
+
120
+ # Returns the type of the element at [index].
121
+ def [](index)
122
+ return @fields[:"field_#{index}"]
123
+ end
124
+
125
+ # A module that provides array-like functionality for FFI Struct types.
126
+ module ZipStruct
127
+ def self.included(base)
128
+ base.include(Enumerable)
129
+ end
130
+
131
+ def [](index)
132
+ # Out of bounds: returns nil
133
+ return super(:"field_#{index}")
134
+ end
135
+
136
+ def []=(index, value)
137
+ # TODO: What should we do if the type of a field changes?
138
+
139
+ # Fill up missing slots with `nil`
140
+ for id in (@fields.size)..index
141
+ super(:"field_{id}", nil)
142
+ end
143
+
144
+ super(:"field_{index}", value)
145
+ return value
146
+ end
147
+
148
+ def each(&block)
149
+ for index in 0...(@fields.size)
150
+ yield(self[index])
151
+ end
152
+ end
153
+ end
154
+
155
+ def to_ruby_type
156
+ # Cache struct types
157
+ if @struct_type == nil
158
+ # Create class
159
+ @struct_type = Class.new(FFI::Struct)
160
+ @struct_type.include(ZipStruct)
161
+
162
+ # Define layout of struct
163
+ var_names = Array.new(@fields.size) do |index|
164
+ :"field_#{index}"
165
+ end
166
+
167
+ var_types = var_names.map do |name|
168
+ @fields[name].to_ffi_type
169
+ end
170
+
171
+ layout = var_names.zip(var_types).flatten
172
+ @struct_type.layout(*layout)
173
+ end
174
+
175
+ return @struct_type
176
+ end
177
+ end
178
+ end
179
+ end