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