yadriggy 1.0.0
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 +7 -0
- data/.gitignore +13 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +108 -0
- data/Rakefile +10 -0
- data/lib/yadriggy.rb +32 -0
- data/lib/yadriggy/algebra.rb +497 -0
- data/lib/yadriggy/ast.rb +1839 -0
- data/lib/yadriggy/ast_location.rb +73 -0
- data/lib/yadriggy/ast_value.rb +428 -0
- data/lib/yadriggy/c.rb +11 -0
- data/lib/yadriggy/c/c.rb +220 -0
- data/lib/yadriggy/c/codegen.rb +481 -0
- data/lib/yadriggy/c/config.rb +51 -0
- data/lib/yadriggy/c/ctype.rb +118 -0
- data/lib/yadriggy/c/ctypecheck.rb +449 -0
- data/lib/yadriggy/c/ffi.rb +301 -0
- data/lib/yadriggy/c/opencl.rb +458 -0
- data/lib/yadriggy/c/program.rb +86 -0
- data/lib/yadriggy/c1.rb +10 -0
- data/lib/yadriggy/checker.rb +216 -0
- data/lib/yadriggy/eval.rb +200 -0
- data/lib/yadriggy/eval_all.rb +159 -0
- data/lib/yadriggy/pretty_print.rb +492 -0
- data/lib/yadriggy/printer.rb +82 -0
- data/lib/yadriggy/ruby_typecheck.rb +468 -0
- data/lib/yadriggy/ruby_typeinfer.rb +335 -0
- data/lib/yadriggy/source_code.rb +168 -0
- data/lib/yadriggy/syntax.rb +524 -0
- data/lib/yadriggy/type.rb +754 -0
- data/lib/yadriggy/typecheck.rb +277 -0
- data/lib/yadriggy/version.rb +5 -0
- data/yadriggy.gemspec +33 -0
- metadata +149 -0
@@ -0,0 +1,301 @@
|
|
1
|
+
# Copyright (C) 2017- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
require 'ffi'
|
4
|
+
|
5
|
+
module Yadriggy
|
6
|
+
module C
|
7
|
+
|
8
|
+
# @abstract
|
9
|
+
# A wrapper of memory object accessible from Ruby and C code.
|
10
|
+
#
|
11
|
+
class FFIArray
|
12
|
+
# @return [FFI::MemoryPointer] the object holding all data.
|
13
|
+
def memory_pointer()
|
14
|
+
@array
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(obj)
|
18
|
+
obj.is_a?(FFIArray) && @array == obj.memory_pointer
|
19
|
+
end
|
20
|
+
|
21
|
+
# Obtains the size.
|
22
|
+
# @return [Integer] the number of elements.
|
23
|
+
def size
|
24
|
+
@array.size / @array.type_size
|
25
|
+
end
|
26
|
+
|
27
|
+
# Obtain the size.
|
28
|
+
# @return [Integer] the number of elements.
|
29
|
+
def length() size() end
|
30
|
+
|
31
|
+
# Initializes the element values.
|
32
|
+
def set_values
|
33
|
+
size.times {|i| self[i] = yield i }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Converts all the elements into an array.
|
37
|
+
# This method is available only within Ruby.
|
38
|
+
def to_a(*args, &proc)
|
39
|
+
Array.new(size) {|i| self[i] }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Obtain the element type.
|
43
|
+
# All the subclasses have to override this method.
|
44
|
+
def self.element_type()
|
45
|
+
Undef
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Array of 32bit integers.
|
50
|
+
# In C, the type of this array is `arrayof(Int)`.
|
51
|
+
#
|
52
|
+
class IntArray < FFIArray
|
53
|
+
# @param [Integer] size array size.
|
54
|
+
# @param [FFI::MemoryPointer] ptr a memory pointer or nil.
|
55
|
+
# If nil, a new memory block is allocated.
|
56
|
+
def initialize(size, ptr=nil)
|
57
|
+
if ptr.nil?
|
58
|
+
@array = FFI::MemoryPointer.new(:int32, size)
|
59
|
+
else
|
60
|
+
@array = ptr
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def [](index)
|
65
|
+
@array.get_int32(index * 4)
|
66
|
+
end
|
67
|
+
|
68
|
+
def []=(index, value)
|
69
|
+
@array.put_int32(index * 4, value)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.element_type()
|
73
|
+
Integer
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Array of 64bit floating point numbers.
|
78
|
+
# In C, the type of this array is `arrayof(Float)`.
|
79
|
+
#
|
80
|
+
class FloatArray < FFIArray
|
81
|
+
# @param [Integer] size array size.
|
82
|
+
# @param [FFI::MemoryPointer] ptr a memory pointer or nil.
|
83
|
+
# If nil, a new memory block is allocated.
|
84
|
+
def initialize(size, ptr=nil)
|
85
|
+
if ptr.nil?
|
86
|
+
@array = FFI::MemoryPointer.new(:double, size)
|
87
|
+
else
|
88
|
+
@array = ptr
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def [](index)
|
93
|
+
@array.get_float64(index * 8)
|
94
|
+
end
|
95
|
+
|
96
|
+
def []=(index, value)
|
97
|
+
@array.put_float64(index * 8, value)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.element_type()
|
101
|
+
Float
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Array of 32bit floating point numbers.
|
106
|
+
# In C, the type of this array is `arrayof(Float32)`.
|
107
|
+
#
|
108
|
+
class Float32Array < FFIArray
|
109
|
+
# @param [Integer] size array size.
|
110
|
+
# @param [FFI::MemoryPointer] ptr a memory pointer or nil.
|
111
|
+
# If nil, a new memory block is allocated.
|
112
|
+
def initialize(size, ptr=nil)
|
113
|
+
if ptr.nil?
|
114
|
+
@array = FFI::MemoryPointer.new(:float, size)
|
115
|
+
else
|
116
|
+
@array = ptr
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def [](index)
|
121
|
+
@array.get_float32(index * 4)
|
122
|
+
end
|
123
|
+
|
124
|
+
def []=(index, value)
|
125
|
+
@array.put_float32(index * 4, value)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.element_type()
|
129
|
+
CType::Float32
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# @private
|
134
|
+
# Attaches functions to a module. The functions are retrieved
|
135
|
+
# from the library generated by compiling Ruby methods.
|
136
|
+
#
|
137
|
+
# @param [Module] module_obj the module object.
|
138
|
+
# @param [Array<String>] func_names the function names in C.
|
139
|
+
# @param [Array<Type>] method_types the types of the original methods.
|
140
|
+
# @param [String] lib_name the library name.
|
141
|
+
# @param [String] dir the directory where the library is found.
|
142
|
+
# @return [Module] the module object `module_obj`.
|
143
|
+
def self.attach(module_obj, func_names, method_types, lib_name, dir='./')
|
144
|
+
module_obj.module_eval('extend FFI::Library')
|
145
|
+
module_obj.ffi_lib("#{dir}lib#{lib_name}#{Config::LibExtension}")
|
146
|
+
func_names.each_with_index do |name,i|
|
147
|
+
mtype = method_types[i]
|
148
|
+
module_obj.attach_function((name + '__org').to_sym,
|
149
|
+
name.to_sym,
|
150
|
+
CFI::param_types(mtype),
|
151
|
+
CFI::return_type(mtype))
|
152
|
+
ivk_name = invoker_name(mtype)
|
153
|
+
module_obj.module_eval <<-code, __FILE__, __LINE__
|
154
|
+
def self.#{name}(*args)
|
155
|
+
Yadriggy::C::invoke#{ivk_name}(self, :#{name}__org, args)
|
156
|
+
end
|
157
|
+
code
|
158
|
+
end
|
159
|
+
module_obj
|
160
|
+
end
|
161
|
+
|
162
|
+
# @private
|
163
|
+
# Generates a Ruby source file. When the file is executed,
|
164
|
+
# it retrieves functions from the library generated by
|
165
|
+
# compiling Ruby methods, and then it attaches the functions
|
166
|
+
# to a specified module.
|
167
|
+
#
|
168
|
+
# @param [String] module_name the module name.
|
169
|
+
# @param [Array<String>] func_names the function names in C.
|
170
|
+
# @param [Array<Type>] method_types the types of the original methods.
|
171
|
+
# @param [String] lib_name the library name.
|
172
|
+
# @param [String] dir the directory where the library is found.
|
173
|
+
# @return [String] the generated file name.
|
174
|
+
def self.make_attach_file(module_name, func_names, method_types,
|
175
|
+
lib_name, dir='./')
|
176
|
+
file_name0 = module_name.gsub(/::|\./, '_').downcase
|
177
|
+
file_name = "#{dir}#{file_name0}.rb"
|
178
|
+
printer = Yadriggy::FilePrinter.new(file_name)
|
179
|
+
printer << "require 'yadriggy/c/ffi'" << :nl << :nl
|
180
|
+
printer << "module #{module_name} extend FFI::Library" << :nl
|
181
|
+
printer << " self.ffi_lib(\"#{dir}lib#{lib_name}#{Config::LibExtension}\")" << :nl
|
182
|
+
func_names.each_with_index do |name,i|
|
183
|
+
mtype = method_types[i]
|
184
|
+
printer << " self.attach_function(:\"#{name}__org\", "
|
185
|
+
printer << ":\"#{name}\", "
|
186
|
+
printer << "#{CFI::param_types(mtype).to_s}, "
|
187
|
+
printer << ":#{CFI::return_type(mtype)})"
|
188
|
+
printer << :nl
|
189
|
+
|
190
|
+
printer << " def self.#{name}(*args)" << :nl
|
191
|
+
printer << " Yadriggy::C::invoke#{invoker_name(mtype)}(self, "
|
192
|
+
printer << ":\"#{name}__org\", args)" << :nl
|
193
|
+
printer << ' end' << :nl
|
194
|
+
end
|
195
|
+
printer << 'end' << :nl
|
196
|
+
|
197
|
+
printer.output
|
198
|
+
printer.close
|
199
|
+
file_name
|
200
|
+
end
|
201
|
+
|
202
|
+
# @private
|
203
|
+
def self.invoker_name(mtype)
|
204
|
+
case ArrayType.role(MethodType.role(mtype)&.result_type)&.element_type
|
205
|
+
when RubyClass::Integer
|
206
|
+
'_int'
|
207
|
+
when RubyClass::Float
|
208
|
+
'_float'
|
209
|
+
when CType::Float32Type
|
210
|
+
'_float32'
|
211
|
+
else
|
212
|
+
''
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# @private
|
217
|
+
def self.invoke_int(module_obj, func_name, args)
|
218
|
+
r = invoke(module_obj, func_name, args)
|
219
|
+
IntArray.new(0, r)
|
220
|
+
end
|
221
|
+
|
222
|
+
# @private
|
223
|
+
def self.invoke_float(module_obj, func_name, args)
|
224
|
+
r = invoke(module_obj, func_name, args)
|
225
|
+
FloatArray.new(0, r)
|
226
|
+
end
|
227
|
+
|
228
|
+
# @private
|
229
|
+
def self.invoke_float32(module_obj, func_name, args)
|
230
|
+
r = invoke(module_obj, func_name, args)
|
231
|
+
Float32Array.new(0, r)
|
232
|
+
end
|
233
|
+
|
234
|
+
# @private
|
235
|
+
def self.invoke(module_obj, func_name, args)
|
236
|
+
args2 = args.map do |e|
|
237
|
+
if e.is_a?(IntArray) || e.is_a?(FloatArray) || e.is_a?(Float32Array)
|
238
|
+
e.memory_pointer
|
239
|
+
else
|
240
|
+
e
|
241
|
+
end
|
242
|
+
end
|
243
|
+
module_obj.method(func_name).call(*args2)
|
244
|
+
end
|
245
|
+
|
246
|
+
# @private
|
247
|
+
# C Function Interface
|
248
|
+
module CFI
|
249
|
+
def self.param_types(method_type)
|
250
|
+
method_type.params.map do |t|
|
251
|
+
c_type(t)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def self.return_type(method_type)
|
256
|
+
c_type(method_type.result_type)
|
257
|
+
end
|
258
|
+
|
259
|
+
# @param [Type] type
|
260
|
+
# @return [String] type name in C.
|
261
|
+
def self.c_type_name(type)
|
262
|
+
type = type.supertype if InstanceType.role(type)
|
263
|
+
if type == RubyClass::Integer || type == Integer
|
264
|
+
'int32_t'
|
265
|
+
elsif type == RubyClass::Float || type == Float
|
266
|
+
'double'
|
267
|
+
elsif type == CType::Void
|
268
|
+
'void'
|
269
|
+
elsif type == RubyClass::String || type == String
|
270
|
+
'char*'
|
271
|
+
elsif type == CType::Float32Type || type == CType::Float32
|
272
|
+
'float'
|
273
|
+
else
|
274
|
+
at = ArrayType.role(type)
|
275
|
+
raise "bad type #{type.inspect}" if at.nil?
|
276
|
+
"#{c_type_name(at.element_type)}*"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# @param [Type] type
|
281
|
+
# @return [Symbol] type name for FFI
|
282
|
+
def self.c_type(type)
|
283
|
+
type = type.supertype if InstanceType.role(type)
|
284
|
+
if type == RubyClass::Integer || type == Integer
|
285
|
+
:int32
|
286
|
+
elsif type == RubyClass::Float || type == Float
|
287
|
+
:double
|
288
|
+
elsif type == CType::Void
|
289
|
+
:void
|
290
|
+
elsif type == RubyClass::String || type == String
|
291
|
+
:string
|
292
|
+
elsif type == CType::Float32Type || type == CType::Float32
|
293
|
+
:float
|
294
|
+
else
|
295
|
+
raise "bad type #{type}" if ArrayType.role(type).nil?
|
296
|
+
:pointer
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
@@ -0,0 +1,458 @@
|
|
1
|
+
# Copyright (C) 2017- Shigeru Chiba. All rights reserved.
|
2
|
+
|
3
|
+
require 'yadriggy/c/ctype.rb'
|
4
|
+
require 'yadriggy/c/codegen'
|
5
|
+
require 'yadriggy/c/ctypecheck'
|
6
|
+
require 'yadriggy/c/program'
|
7
|
+
require 'yadriggy/c/config'
|
8
|
+
|
9
|
+
module Yadriggy
|
10
|
+
module C
|
11
|
+
# Compiles OpenCL methods into binary code.
|
12
|
+
#
|
13
|
+
def self.ocl_compile(obj, lib_name=nil, dir=Config::WorkDir,
|
14
|
+
module_name=nil)
|
15
|
+
mod, funcs = compile0(obj, lib_name, dir, module_name,
|
16
|
+
OclTypeChecker, OclCodeGen)
|
17
|
+
mod
|
18
|
+
end
|
19
|
+
|
20
|
+
class Program
|
21
|
+
# Compiles this class and makes a module including the
|
22
|
+
# compiled OpenCL methods.
|
23
|
+
#
|
24
|
+
# @param [String] module_name a module name.
|
25
|
+
# @param [String] lib_name the name of the generated library.
|
26
|
+
# @param [String] dir the directory name where generated files
|
27
|
+
# are stored. The default value is `.`.
|
28
|
+
# @param [Array<Object>] args an array of the arguments to the
|
29
|
+
# `initialize` method. The default value is `nil` (no arguments).
|
30
|
+
# @return [Module] the module where methods are attached.
|
31
|
+
def self.ocl_compile(module_name=nil, lib_name=nil,
|
32
|
+
dir: Config::WorkDir, args: nil)
|
33
|
+
if args.nil?
|
34
|
+
obj = self.new
|
35
|
+
else
|
36
|
+
obj = self.new(*args)
|
37
|
+
end
|
38
|
+
Yadriggy::C.ocl_compile(obj, lib_name, dir, module_name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module CType # also see ctype.rb
|
43
|
+
# OpenCL array.
|
44
|
+
#
|
45
|
+
class OclArray < IvarObj
|
46
|
+
def initialize(size)
|
47
|
+
@size = size
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.type()
|
51
|
+
Float32Type
|
52
|
+
end
|
53
|
+
|
54
|
+
def type()
|
55
|
+
Float32Type
|
56
|
+
end
|
57
|
+
|
58
|
+
def size()
|
59
|
+
@size
|
60
|
+
end
|
61
|
+
|
62
|
+
def sizes()
|
63
|
+
[ @size ]
|
64
|
+
end
|
65
|
+
|
66
|
+
def copyfrom(array, len) ! Integer
|
67
|
+
typedecl array: arrayof(Float32), len: Integer,
|
68
|
+
native: 'return 0;'
|
69
|
+
return 0
|
70
|
+
end
|
71
|
+
|
72
|
+
def copyto(array, len) ! Integer
|
73
|
+
typedecl array: arrayof(Float32), len: Integer,
|
74
|
+
native: 'return 0;'
|
75
|
+
return 0
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class OclTypeChecker < ClangTypeChecker
|
81
|
+
# A map from blocks to their names and free variables.
|
82
|
+
#
|
83
|
+
# @return [Hash<Block,Tuple<String,Hash<Symbol,Type>,Set<Object>>]
|
84
|
+
# a map to tuples of a block name, free variables, and instance
|
85
|
+
# variables accessed in the block.
|
86
|
+
attr_reader :blocks
|
87
|
+
|
88
|
+
def initialize(syntax=nil)
|
89
|
+
super(syntax)
|
90
|
+
@blocks = {}
|
91
|
+
@block_count = 0
|
92
|
+
end
|
93
|
+
|
94
|
+
def method_with_block?(name)
|
95
|
+
super || name == 'ocl_times'
|
96
|
+
end
|
97
|
+
|
98
|
+
def typecheck_call_with_block(ast)
|
99
|
+
if ast.name.name == 'ocl_times'
|
100
|
+
type_assert(type(ast.receiver) == RubyClass::Integer,
|
101
|
+
'the receiver must be an integer')
|
102
|
+
type_assert(ast.block.params.size == 1,
|
103
|
+
"wrong number of block parameters")
|
104
|
+
tenv = FreeVarFinder.new(type_env)
|
105
|
+
type_as(ast.block.params[0], RubyClass::Integer)
|
106
|
+
tenv.bind_name(ast.block.params[0], RubyClass::Integer)
|
107
|
+
tenv.bind_name(:return, Void)
|
108
|
+
|
109
|
+
old_ins_vars = @instance_variables
|
110
|
+
@instance_variables = Set.new
|
111
|
+
type(ast.block, tenv)
|
112
|
+
captured_ins_vars = @instance_variables
|
113
|
+
@instance_variables = old_ins_vars
|
114
|
+
@instance_variables += captured_ins_vars
|
115
|
+
|
116
|
+
@blocks[ast.block] = ["block#{@block_count}", tenv.free_variables,
|
117
|
+
captured_ins_vars]
|
118
|
+
@block_count += 1
|
119
|
+
Void
|
120
|
+
else
|
121
|
+
super
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
rule(ArrayRef) do
|
126
|
+
array_type = type(ast.array)
|
127
|
+
if array_type <= RubyClass[OclArray]
|
128
|
+
indexes = ast.indexes
|
129
|
+
type_assert(indexes.size == 1, 'bad array index')
|
130
|
+
itype = type(indexes[0])
|
131
|
+
type_assert(itype <= RubyClass::Integer, 'bad array index')
|
132
|
+
OclArray.type
|
133
|
+
else
|
134
|
+
proceed(ast)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# OpenCL-code generator
|
140
|
+
#
|
141
|
+
class OclCodeGen < CodeGen
|
142
|
+
|
143
|
+
# @see {OclTypeChecker#method_with_block?}
|
144
|
+
rule(Call) do
|
145
|
+
if ast.name.name == 'copyfrom'
|
146
|
+
@printer << 'ocl_err_check(clEnqueueWriteBuffer(commands, '
|
147
|
+
check(ast.receiver)
|
148
|
+
@printer << ', CL_TRUE, 0, sizeof(float) * '
|
149
|
+
check(ast.args[1]) # length
|
150
|
+
@printer << ', '
|
151
|
+
check(ast.args[0]) # array
|
152
|
+
@printer << ', 0, NULL, NULL), "' << ast.name.name << '")'
|
153
|
+
elsif ast.name.name == 'copyto'
|
154
|
+
@printer << 'ocl_err_check(clEnqueueReadBuffer(commands, '
|
155
|
+
check(ast.receiver)
|
156
|
+
@printer << ', CL_TRUE, 0, sizeof(float) * '
|
157
|
+
check(ast.args[1]) # length
|
158
|
+
@printer << ', '
|
159
|
+
check(ast.args[0]) # array
|
160
|
+
@printer << ', 0, NULL, NULL), "' << ast.name.name << '")'
|
161
|
+
elsif ast.name.name == 'ocl_times'
|
162
|
+
name_and_vars = @typechecker.blocks[ast.block]
|
163
|
+
@printer << name_and_vars[0] << '_call('
|
164
|
+
check(ast.receiver)
|
165
|
+
name_and_vars[1].each do |sym, t|
|
166
|
+
@printer << ', ' << sym.to_s
|
167
|
+
end
|
168
|
+
@printer << ')'
|
169
|
+
else
|
170
|
+
proceed(ast)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_cmd
|
175
|
+
super + Config::OpenCLoptions
|
176
|
+
end
|
177
|
+
|
178
|
+
def headers()
|
179
|
+
super
|
180
|
+
Config::OpenCLHeaders.each {|h| @printer << h << :nl }
|
181
|
+
@printer << :nl
|
182
|
+
end
|
183
|
+
|
184
|
+
def variable_declarations()
|
185
|
+
super
|
186
|
+
@gvariables.each do |obj, name|
|
187
|
+
if obj.is_a?(CType::OclArray)
|
188
|
+
@printer << 'static cl_mem ' << name << ';' << :nl
|
189
|
+
end
|
190
|
+
end
|
191
|
+
@typechecker.blocks.each do |obj, name_and_vars|
|
192
|
+
@printer << 'static cl_kernel ' << name_and_vars[0] << ';' << :nl
|
193
|
+
end
|
194
|
+
@printer << :nl
|
195
|
+
end
|
196
|
+
|
197
|
+
def preamble
|
198
|
+
super
|
199
|
+
@printer << 'int ocl_init(int);' << :nl
|
200
|
+
@printer << 'void ocl_finish();' << :nl << :nl
|
201
|
+
|
202
|
+
print_kernel_source
|
203
|
+
@printer << HelperSource
|
204
|
+
print_ocl_init
|
205
|
+
print_ocl_finish
|
206
|
+
print_callers
|
207
|
+
end
|
208
|
+
|
209
|
+
def expand_functions(func_names, func_types)
|
210
|
+
voidFunc = MethodType.new([], Void)
|
211
|
+
return func_names + ['ocl_init', 'ocl_finish'],
|
212
|
+
func_types + [MethodType.new([Integer], Void),
|
213
|
+
MethodType.new([], Void)]
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
|
218
|
+
def print_ocl_init
|
219
|
+
@printer << 'int ocl_init(int is_gpu) {' << :nl
|
220
|
+
@printer << ' if (ocl_initialized) return 0;' << :nl
|
221
|
+
@printer << ' ocl_initialized = 1;' << :nl
|
222
|
+
@printer << ' if (ocl_init0(is_gpu)) return 1;' << :nl << :nl
|
223
|
+
print_create_kernel_code
|
224
|
+
print_create_buffer_code
|
225
|
+
@printer << :nl << ' return 0;' << :nl << '}' << :nl << :nl
|
226
|
+
end
|
227
|
+
|
228
|
+
def print_ocl_finish
|
229
|
+
@printer << 'void ocl_finish() {' << :nl
|
230
|
+
@printer << ' if (!ocl_initialized) return;' << :nl
|
231
|
+
@printer << ' ocl_initialized = 0;' << :nl
|
232
|
+
@gvariables.each do |obj, name|
|
233
|
+
if obj.is_a?(CType::OclArray)
|
234
|
+
@printer << " clReleaseMemObject(#{name});" << :nl
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
@typechecker.blocks.each do |obj, name_vars|
|
239
|
+
@printer << ' clReleaseKernel(' << name_vars[0] << ');' << :nl
|
240
|
+
end
|
241
|
+
|
242
|
+
@printer << ' clReleaseProgram(program);' << :nl
|
243
|
+
@printer << ' clReleaseCommandQueue(commands);' << :nl
|
244
|
+
@printer << ' clReleaseContext(context);' << :nl
|
245
|
+
@printer << '}' << :nl << :nl
|
246
|
+
end
|
247
|
+
|
248
|
+
# generate kenrel source.
|
249
|
+
def print_kernel_source
|
250
|
+
@printer << 'static const char* kernelSource = ' << :nl << '"'
|
251
|
+
@printer = KernelPrinter.new(@printer)
|
252
|
+
@typechecker.blocks.each do |blk, name_vars|
|
253
|
+
func_name = name_vars[0]
|
254
|
+
@printer << '__kernel void ' << func_name << '('
|
255
|
+
|
256
|
+
all_free_vars = name_vars[1].to_a
|
257
|
+
all_free_vars += name_vars[2].map do |obj|
|
258
|
+
[@gvariables[obj], obj.class]
|
259
|
+
end
|
260
|
+
|
261
|
+
first = true
|
262
|
+
all_free_vars.each do |name, type|
|
263
|
+
if first then first = false else @printer << ', ' end
|
264
|
+
print_type_in_kernel(type)
|
265
|
+
@printer << name
|
266
|
+
end
|
267
|
+
@printer << ') {'
|
268
|
+
@printer.down
|
269
|
+
local_var_declarations(blk)
|
270
|
+
@printer << 'int ' << blk.params[0].name
|
271
|
+
@printer << ' = get_global_id(0);' << :nl
|
272
|
+
check(blk.body)
|
273
|
+
@printer << ';' unless blk.body.is_a?(Exprs)
|
274
|
+
@printer.up
|
275
|
+
@printer << '}' << :nl
|
276
|
+
end
|
277
|
+
@printer = @printer.printer
|
278
|
+
@printer << ' ";' << :nl << :nl
|
279
|
+
end
|
280
|
+
|
281
|
+
# @param [Type|Class] type
|
282
|
+
def print_type_in_kernel(type)
|
283
|
+
if type == CType::OclArray || type == RubyClass[CType::OclArray]
|
284
|
+
@printer << '__global float* '
|
285
|
+
else
|
286
|
+
@printer << c_type(type) << ' '
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def c_type(type)
|
291
|
+
if @printer.is_a?(KernelPrinter) &&
|
292
|
+
(type == RubyClass::Integer || type == Integer)
|
293
|
+
return 'int'
|
294
|
+
else
|
295
|
+
super
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class KernelPrinter
|
300
|
+
attr_reader :printer
|
301
|
+
|
302
|
+
def initialize(printer)
|
303
|
+
@printer = printer
|
304
|
+
end
|
305
|
+
|
306
|
+
def down()
|
307
|
+
@printer << "\"\\"
|
308
|
+
@printer.down
|
309
|
+
@printer << '"'
|
310
|
+
end
|
311
|
+
|
312
|
+
def up()
|
313
|
+
@printer << "\"\\"
|
314
|
+
@printer.up
|
315
|
+
@printer << '"'
|
316
|
+
end
|
317
|
+
|
318
|
+
def nl()
|
319
|
+
@printer << "\"\\" << :nl << '"'
|
320
|
+
self
|
321
|
+
end
|
322
|
+
|
323
|
+
def << (code)
|
324
|
+
code == :nl ? nl : @printer << code
|
325
|
+
self
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# generate the code for creating kernels.
|
330
|
+
def print_create_kernel_code
|
331
|
+
@printer << ' int err;' << :nl
|
332
|
+
@typechecker.blocks.each do |blk, name_vars|
|
333
|
+
@printer << ' ' << name_vars[0]
|
334
|
+
@printer << ' = clCreateKernel(program, "' << name_vars[0] << '", &err);' << :nl
|
335
|
+
@printer << ' if (err != CL_SUCCESS) {' << :nl
|
336
|
+
@printer << ' fprintf(stderr, "error: clCreateKernel\n");' << :nl
|
337
|
+
@printer << ' return 1; }' << :nl
|
338
|
+
end
|
339
|
+
@printer << :nl
|
340
|
+
end
|
341
|
+
|
342
|
+
# generate the code for creating buffers.
|
343
|
+
def print_create_buffer_code
|
344
|
+
@gvariables.each do |obj, name|
|
345
|
+
if obj.is_a?(CType::OclArray)
|
346
|
+
@printer << " #{name} = clCreateBuffer(context, CL_MEM_READ_WRITE,"\
|
347
|
+
" sizeof(float) * #{obj.size}, NULL, NULL);" << :nl
|
348
|
+
@printer << " if (!#{name}) {" << :nl
|
349
|
+
@printer << ' fprintf(stderr, "error: clCreateBuffer\n");'
|
350
|
+
@printer << ' return 1; }' << :nl
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def print_callers
|
356
|
+
@typechecker.blocks.each do |blk, name_and_vars|
|
357
|
+
name = name_and_vars[0]
|
358
|
+
@printer << 'static void ' << name << '_call(size_t p0'
|
359
|
+
name_and_vars[1].each_with_index do |name_type, i|
|
360
|
+
type = name_type[1]
|
361
|
+
tname = if type <= RubyClass[CType::OclArray]
|
362
|
+
'cl_mem'
|
363
|
+
else
|
364
|
+
c_type(type)
|
365
|
+
end
|
366
|
+
@printer << ', ' << tname << " p#{i + 1}"
|
367
|
+
end
|
368
|
+
@printer << ') {'
|
369
|
+
@printer.down
|
370
|
+
@printer << 'size_t global;' << :nl
|
371
|
+
@printer << 'int err = 0;' << :nl
|
372
|
+
|
373
|
+
i = name_and_vars[1].size
|
374
|
+
name_and_vars[2].each do |obj|
|
375
|
+
i += 1
|
376
|
+
@printer << "cl_mem p#{i} = #{@gvariables[obj]};" << :nl
|
377
|
+
end
|
378
|
+
i.times do |j|
|
379
|
+
@printer << "err |= clSetKernelArg(#{name}, #{j}, sizeof(p#{j + 1}), &p#{j + 1});" << :nl
|
380
|
+
end
|
381
|
+
|
382
|
+
@printer << 'ocl_err_check(err, "clSetKernelArg");' << :nl
|
383
|
+
@printer << 'global = p0;' << :nl
|
384
|
+
@printer << 'ocl_err_check(clEnqueueNDRangeKernel(commands, '
|
385
|
+
@printer << name << ', 1, NULL, &global, NULL, 0, NULL, NULL), "clEnqueueNDRangeKernel");' << :nl
|
386
|
+
@printer << 'clFinish(commands);' << :nl
|
387
|
+
@printer.up
|
388
|
+
@printer << '}' << :nl
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
HelperSource = <<'EOS'
|
393
|
+
static int ocl_initialized = 0;
|
394
|
+
static cl_device_id device_id;
|
395
|
+
static cl_context context;
|
396
|
+
static cl_command_queue commands;
|
397
|
+
static cl_program program;
|
398
|
+
|
399
|
+
static int ocl_err_check(int err, const char* msg) {
|
400
|
+
if (err == CL_SUCCESS)
|
401
|
+
return 0;
|
402
|
+
else {
|
403
|
+
fprintf(stderr, "OpenCL Error: %s, %d\n", msg, err);
|
404
|
+
return 1;
|
405
|
+
}
|
406
|
+
}
|
407
|
+
|
408
|
+
static int ocl_init0(int gpu) {
|
409
|
+
cl_device_id devices[4];
|
410
|
+
cl_uint num_devices;
|
411
|
+
int err = clGetDeviceIDs(NULL,
|
412
|
+
gpu > 0? CL_DEVICE_TYPE_GPU : CL_DEVICE_TYPE_CPU,
|
413
|
+
sizeof(devices) / sizeof(devices[0]), devices, &num_devices);
|
414
|
+
if (err != CL_SUCCESS) {
|
415
|
+
fprintf(stderr, "error: clGetDeviceIDs\n");
|
416
|
+
return 1;
|
417
|
+
}
|
418
|
+
|
419
|
+
int id = num_devices < gpu ? num_devices : gpu;
|
420
|
+
device_id = devices[id < 1 ? 0 : id - 1];
|
421
|
+
|
422
|
+
context = clCreateContext(0, 1, &device_id, NULL, NULL, &err);
|
423
|
+
if (!context) {
|
424
|
+
fprintf(stderr, "error: clCreateContext\n");
|
425
|
+
return 1;
|
426
|
+
}
|
427
|
+
|
428
|
+
commands = clCreateCommandQueue(context, device_id, 0, &err);
|
429
|
+
if (!commands) {
|
430
|
+
fprintf(stderr, "error: clCreateCommandQueue\n");
|
431
|
+
return 1;
|
432
|
+
}
|
433
|
+
|
434
|
+
program = clCreateProgramWithSource(context, 1,
|
435
|
+
(const char **)&kernelSource, NULL, &err);
|
436
|
+
if (!program) {
|
437
|
+
fprintf(stderr, "error: clCreateProgramWithSource\n");
|
438
|
+
return 1;
|
439
|
+
}
|
440
|
+
|
441
|
+
err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
|
442
|
+
if (err != CL_SUCCESS) {
|
443
|
+
size_t len;
|
444
|
+
char buffer[2048];
|
445
|
+
fprintf(stderr, "error: clBuildProgram\n");
|
446
|
+
clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG,
|
447
|
+
sizeof(buffer), buffer, &len);
|
448
|
+
fprintf(stderr, "%s\n", buffer);
|
449
|
+
return 1;
|
450
|
+
}
|
451
|
+
|
452
|
+
return 0;
|
453
|
+
}
|
454
|
+
|
455
|
+
EOS
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|