yadriggy 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ # Copyright (C) 2017- Shigeru Chiba. All rights reserved.
2
+
3
+ module Yadriggy
4
+ module C
5
+
6
+ # Compiler options etc.
7
+ #
8
+ module Config
9
+ # Host OS
10
+ # @return [Symbol] `:linux`, `:macos`, `:unknown`.
11
+ HostOS = case RbConfig::CONFIG['host_os']
12
+ when /linux/
13
+ :linux
14
+ when /darwin/
15
+ :macos
16
+ else
17
+ :unknown
18
+ end
19
+
20
+ # Working directory.
21
+ WorkDir = './yadriggy_tmp'
22
+
23
+ # Compiler command.
24
+ # @return [String]
25
+ Compiler = 'gcc -shared -fPIC -Ofast '
26
+
27
+ # Compiler option specifying the output file.
28
+ # @return [String]
29
+ CoptOutput = '-o '
30
+
31
+ # The suffix to the name of a shared library such as `.so`.
32
+ # It has to start with a dot.
33
+ LibExtension = HostOS == :macos ? '.dylib' : '.so'
34
+
35
+ # Lines inserted in the generated C source file.
36
+ Headers = [
37
+ '#include <stdint.h>',
38
+ '#include <time.h>',
39
+ '#include <math.h>',
40
+ '#include <stdio.h>'
41
+ ]
42
+
43
+ # Compiler option for OpenCL
44
+ # @return [String]
45
+ OpenCLoptions = '-framework opencl '
46
+
47
+ # Lines inserted in the generated OpenCL source file.
48
+ OpenCLHeaders = [ '#include <OpenCL/opencl.h>' ]
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,118 @@
1
+ # Copyright (C) 2017- Shigeru Chiba. All rights reserved.
2
+
3
+ require 'yadriggy'
4
+ require 'yadriggy/c/ffi'
5
+
6
+ module Yadriggy
7
+ module C
8
+ # Types availalbe in C code.
9
+ module CType
10
+ def typedecl(arg) end
11
+ def arrayof(t) t end
12
+
13
+ # Single-precision floating point numbers.
14
+ # This is used only as a type name.
15
+ # Note that `Float` represents double-precision floating point numbers.
16
+ # Do not make an instance.
17
+ class Float32
18
+ end
19
+
20
+ # @private
21
+ # An alias `RubyClass[Float32]`.
22
+ Float32Type = RubyClass[Float32]
23
+
24
+ Void = Yadriggy::Void # void
25
+ Int = Integer # int32_t
26
+
27
+ IntArray = Yadriggy::C::IntArray
28
+ FloatArray = Yadriggy::C::FloatArray
29
+ Float32Array = Yadriggy::C::Float32Array
30
+
31
+ # Types available only as a value of instance variable.
32
+ class IvarObj
33
+ include Yadriggy::C::CType
34
+ end
35
+
36
+ # @private
37
+ # Array type available only in C code.
38
+ # See also {IntArray} and {FloatArray} in `ffi.rb`.
39
+ class CArray < IvarObj
40
+ # @private
41
+ def typedecl(arg) end
42
+
43
+ # debug mode.
44
+ attr_accessor :debug
45
+
46
+ # @return [Array<Integer>] sizes array size.
47
+ attr_reader :sizes
48
+
49
+ # @param [Array<Integer>] sizes size of each dimension.
50
+ def initialize(*sizes)
51
+ raise 'unknown array size' unless sizes.size > 0
52
+ @sizes = sizes
53
+ @debug = false
54
+ end
55
+
56
+ # @private
57
+ # @abstract
58
+ # @return [Type] the element type.
59
+ def type()
60
+ Undef
61
+ end
62
+
63
+ # @private
64
+ def check_range(indexes)
65
+ raise 'wrong number of indexes' unless indexes.size == @sizes.size
66
+ indexes.each_index do |i|
67
+ if indexes[i] >= @sizes[i]
68
+ raise "out of range: #{self.to_s}[#{indexes[i]}]"
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ # Array of 32bit integers available only in C.
75
+ #
76
+ # @see IntArray
77
+ class IntCArray < CArray
78
+ def type()
79
+ RubyClass::Integer
80
+ end
81
+
82
+ def [](*indexes)
83
+ typedecl foreign: Int
84
+ check_range(indexes)
85
+ raise 'IntCArray is not available in Ruby' unless @debug
86
+ end
87
+
88
+ def []=(*indexes)
89
+ typedecl foreign: Void
90
+ check_range(indexes[0, indexes.size - 1])
91
+ raise 'IntCArray is not available in Ruby' unless @debug
92
+ end
93
+ end
94
+
95
+ # Array of floating point numbers available only in C.
96
+ #
97
+ # @see FloatArray
98
+ class FloatCArray < CArray
99
+ def type()
100
+ RubyClass::Float
101
+ end
102
+
103
+ def [](*indexes)
104
+ typedecl foreign: Float
105
+ check_range(indexes)
106
+ raise 'FloatCArray is not available in Ruby' unless @debug
107
+ end
108
+
109
+ def []=(*indexes)
110
+ typedecl foreign: Void
111
+ check_range(indexes[0, indexes.size - 1])
112
+ raise 'FloatCArray is not available in Ruby' unless @debug
113
+ end
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,449 @@
1
+ # Copyright (C) 2017- Shigeru Chiba. All rights reserved.
2
+
3
+ require 'yadriggy/c/ctype'
4
+ require 'set'
5
+
6
+ module Yadriggy
7
+ module C
8
+
9
+ class ArrayType < CompositeType
10
+ attr_reader :element_type
11
+
12
+ def initialize(element_type)
13
+ super(Array, element_type)
14
+ end
15
+
16
+ def element_type()
17
+ first_arg
18
+ end
19
+ end
20
+
21
+ # Type for expressions ending with a return statement.
22
+ #
23
+ class WithReturnType < OptionalRole
24
+ end
25
+
26
+ # Type for native methods.
27
+ #
28
+ class NativeMethodType < OptionalRole
29
+ # @return [String] the method body.
30
+ attr_reader :body
31
+
32
+ # @param [MethodType] method_type a method type.
33
+ # @param [String] body_code the body of a native method.
34
+ def initialize(method_type, body_code)
35
+ super(method_type)
36
+ @body = body_code
37
+ end
38
+ end
39
+
40
+ # Type for foreign methods.
41
+ #
42
+ class ForeignMethodType < OptionalRole
43
+ # @param [MethodType] method_type a method type.
44
+ def initialize(method_type)
45
+ super(method_type)
46
+ end
47
+ end
48
+
49
+ class ClangTypeChecker < RubyTypeInferer
50
+ include Yadriggy::C::CType
51
+
52
+ # @return [Hash<Def,Hash<Symbol,Type>>] a map from functions to their
53
+ # local variables.
54
+ attr_reader :local_vars_table
55
+
56
+ # @return [Set<IvarObj>] accessed instance variables.
57
+ attr_reader :instance_variables
58
+
59
+ def initialize(syntax=nil)
60
+ super(syntax || C::syntax)
61
+ @local_vars_table = {}
62
+ @instance_variables = Set.new
63
+ end
64
+
65
+ def valid_var_type?(t)
66
+ if t.is_a?(Type)
67
+ et = t.exact_type
68
+ et == Integer || et == Float || et == String || et == Float32
69
+ else
70
+ false
71
+ end
72
+ end
73
+
74
+ def valid_type?(t)
75
+ valid_var_type?(t) || ArrayType.role(t)
76
+ end
77
+
78
+ def is_subsumed_by?(sub_type, super_type)
79
+ (valid_var_type?(sub_type) && valid_var_type?(super_type)) ||
80
+ sub_type <= super_type
81
+ end
82
+
83
+ rule(:typedecl) do
84
+ unless ast.args.nil?
85
+ type_assert(ast.args.size == 1, 'bad typedecl')
86
+ type(ast.args[0])
87
+ end
88
+ Void
89
+ end
90
+
91
+ rule(:typedecl_hash) do
92
+ ast.pairs.each do |e|
93
+ t = typedecl_type(e[1])
94
+ declare_type(e[0].name, e[0], t)
95
+ end
96
+ Void
97
+ end
98
+
99
+ # @private
100
+ # @param [ASTnode] type_expr
101
+ def typedecl_type(type_expr)
102
+ if type_expr.is_a?(Call)
103
+ type_assert(type_expr.args.size == 1, 'bad array type')
104
+ etype = RubyClass[type_expr.args[0].value]
105
+ type_assert(etype != Undef, 'cannot resolve a type name')
106
+ type_assert(valid_var_type?(etype), "bad array type: #{etype}")
107
+ ArrayType.new(etype)
108
+ else
109
+ rt = type_expr.value
110
+ type_assert(rt != Undef, 'cannot resolve a type name')
111
+ if rt.is_a?(Module) && rt <= FFIArray
112
+ ArrayType.new(RubyClass[rt.element_type])
113
+ else
114
+ RubyClass[rt]
115
+ end
116
+ end
117
+ end
118
+
119
+ # @private
120
+ def declare_type(name, name_ast, t)
121
+ if name == 'return' || name == 'foreign'
122
+ type_assert(valid_type?(t) || t == Void,
123
+ "bad return type: #{t.name}")
124
+ check_duplicate(name, t)
125
+ type_env.bind_name(name.to_sym, t)
126
+ elsif name == 'native'
127
+ type_assert(t.is_a?(String), 'bad native argument. not String.')
128
+ type_assert(type_env.bound_name?(:native).nil?,
129
+ 'duplicate declaration: native')
130
+ type_env.bind_name(:native, InstanceType.new(t))
131
+ else
132
+ type_assert(valid_type?(t), "bad parameter type: #{name}")
133
+ check_duplicate(name, t)
134
+ bind_local_var(type_env, name_ast, t, false)
135
+ end
136
+ end
137
+
138
+ # @private
139
+ def check_duplicate(name, t)
140
+ old_type = type_env.bound_name?(name)
141
+ type_assert(old_type.nil? || old_type == t,
142
+ "incompatible or duplicate declaration: #{name}")
143
+ end
144
+
145
+ rule(:return_type) do
146
+ typedecl_type(ast.expr)
147
+ end
148
+
149
+ rule(Number) do
150
+ if ast.value.is_a?(Float)
151
+ RubyClass::Float
152
+ else
153
+ RubyClass::Integer
154
+ end
155
+ end
156
+
157
+ rule(Const) do
158
+ t = proceed(ast)
159
+ type_assert(t != Undef, 'unknown constant')
160
+ type_assert(valid_var_type?(t), 'bad constant type')
161
+ t
162
+ end
163
+
164
+ rule(ConstPathRef) do
165
+ v = ast.value
166
+ type_assert(v != Undef, 'unknown constant')
167
+ t = InstanceType.new(v)
168
+ type_assert(valid_var_type?(t), 'bad constant type')
169
+ t
170
+ end
171
+
172
+ rule(InstanceVariable) do
173
+ v = ast.value
174
+ key = type_env.context
175
+ if v == Undef
176
+ get_instance_variable_type(key, ast, false, DynType)
177
+ else
178
+ type_assert(v.is_a?(IvarObj), 'badly typed instance variable')
179
+ @instance_variables << v
180
+ get_instance_variable_type(key, ast, true, InstanceType.new(v))
181
+ end
182
+ end
183
+
184
+ rule(Assign) do
185
+ rtype = type(ast.right)
186
+ type_assert(valid_var_type?(rtype), 'bad assigned value')
187
+ if ast.left.is_a?(ArrayRef)
188
+ type(ast.left)
189
+ else
190
+ type_assert(ast.left.is_a?(IdentifierOrCall), 'bad assignment')
191
+ ltype = type_env.bound_name?(ast.left)
192
+ if ltype.nil? # if a new name is found,
193
+ bind_local_var(type_env, ast.left, rtype)
194
+ else
195
+ LocalVarType.role(ltype)&.definition = ast.left
196
+ ltype
197
+ end
198
+ end
199
+ end
200
+
201
+ rule(Binary) do
202
+ t1 = type(ast.left)
203
+ t2 = type(ast.right)
204
+ binary_cexpr_type(ast.op, t1, t2)
205
+ end
206
+
207
+ # @private
208
+ def binary_cexpr_type(op, t1, t2)
209
+ case op
210
+ when :+, :-, :*, :/
211
+ if t1 <= RubyClass::Float || t2 <= RubyClass::Float
212
+ return RubyClass::Float
213
+ elsif t1 <= Float32Type || t2 <= Float32Type
214
+ return Float32Type
215
+ else
216
+ return RubyClass::Integer
217
+ end
218
+ when :%
219
+ type_assert(t1 <= RubyClass::Integer &&
220
+ t2 <= RubyClass::Integer, 'bad operand type')
221
+ return RubyClass::Integer
222
+ when :<, :>, :<=, :>=, :==, :'&&', :'||'
223
+ return RubyClass::Boolean
224
+ else
225
+ type_assert(false, "bad operator: #{ast.op}")
226
+ end
227
+ end
228
+
229
+ rule(ArrayRef) do
230
+ array_type = type(ast.array)
231
+ indexes = ast.indexes
232
+ if InstanceType.role(array_type)&.object.is_a?(IvarObj)
233
+ sizes = array_type.object.sizes
234
+ type_assert(indexes.size == sizes.size, 'bad array index')
235
+ indexes.each do |idx|
236
+ type_assert(type(idx) <= RubyClass::Integer, 'bad array index')
237
+ end
238
+ array_type.object.type
239
+ else
240
+ type_assert(indexes.size == 1, 'bad array index')
241
+ itype = type(indexes[0])
242
+ type_assert(itype <= RubyClass::Integer, 'bad array index')
243
+
244
+ atype = ArrayType.role(array_type)
245
+ type_assert_false(atype.nil?, 'bad array access')
246
+ atype.element_type
247
+ end
248
+ end
249
+
250
+ rule(Unary) do
251
+ t = type(ast.expr)
252
+ type_assert(ast.op == :-@, "bad operator: #{ast.op}")
253
+ t
254
+ end
255
+
256
+ rule(Conditional) do
257
+ type(ast.cond)
258
+ t1 = type(ast.then)
259
+ ast.all_elsif.each do |cond_then|
260
+ type(cond_then[0])
261
+ type(cond_then[1])
262
+ end
263
+ t2 = type(ast.else)
264
+ if WithReturnType.role(t1) && WithReturnType.role(t2)
265
+ WithReturnType.new(UnionType.make(t1, t2))
266
+ elsif ast.op == :ifop
267
+ UnionType.make(t1, t2)
268
+ else
269
+ Void
270
+ end
271
+ end
272
+
273
+ rule(Loop) do
274
+ type(ast.cond)
275
+ type(ast.body, type_env)
276
+ Void
277
+ end
278
+
279
+ rule(ForLoop) do
280
+ ast.vars.each {|v| bind_local_var(type_env, v.name,
281
+ RubyClass::Integer) }
282
+ type_assert(type(ast.set.left) <= RubyClass::Integer, 'bad for-range')
283
+ type_assert(type(ast.set.right) <= RubyClass::Integer, 'bad for-range')
284
+ type(ast.body, type_env)
285
+ Void
286
+ end
287
+
288
+ rule(Return) do
289
+ t = proceed(ast)
290
+ ret_type = type_env.bound_name?(:return)
291
+ if ret_type == Void
292
+ type_assert(ast.values.size == 0, 'bad return')
293
+ else
294
+ type_assert(is_subsumed_by?(t, ret_type), 'bad return type')
295
+ end
296
+ WithReturnType.new(t)
297
+ end
298
+
299
+ rule(Call) do
300
+ method_name = ast.name.name
301
+ if method_with_block?(method_name)
302
+ type_assert(ast.block,
303
+ "no block given: #{method_name}")
304
+ type_assert(ast.receiver,
305
+ "no receiver given: #{method_name}")
306
+ typecheck_call_with_block(ast)
307
+ else
308
+ type_assert_false(ast.block,
309
+ "a block is not taken: #{method_name}")
310
+ t = proceed(ast)
311
+ type_assert(ResultType.role(t), "bad call to: #{method_name}")
312
+ t
313
+ end
314
+ end
315
+
316
+ # Specifies the names of methods with a block.
317
+ #
318
+ # @param [String] name a method name.
319
+ # @see {CodeGen#call_with_block}
320
+ # @see {#typecheck_call_with_block}
321
+ def method_with_block?(name)
322
+ name == 'times'
323
+ end
324
+
325
+ def typecheck_call_with_block(call_ast)
326
+ type_assert(ast.name.name == 'times',
327
+ "no such method: #{ast.name.name}")
328
+ type_assert(type(ast.receiver) == RubyClass::Integer,
329
+ 'the receiver must be an integer')
330
+ type_assert(ast.block.params.size == 1,
331
+ "wrong number of block parameters")
332
+ type_as(ast.block.params[0], RubyClass::Integer)
333
+ tenv = type_env.new_tenv
334
+ tenv.bind_name(ast.block.params[0], RubyClass::Integer)
335
+ tenv.bind_name(:return, Void)
336
+ type(ast.block, tenv)
337
+ Void
338
+ end
339
+
340
+ rule(Block) do
341
+ if ast.params.size == 0 && type_env.bound_name?(:return).nil? &&
342
+ ast.body.is_a?(Return)
343
+ case ast.body.values[0].usertype
344
+ when :expr, :method_call
345
+ type_env.bind_name(:return, type(ast.body.values[0]))
346
+ end
347
+ end
348
+
349
+ # a new type environment is created by a caller
350
+ # (i.e. typecheck_call_with_block).
351
+ def_block_rule(true, type_env)
352
+ end
353
+
354
+ rule(Def) do
355
+ def_block_rule(false, type_env.new_tenv)
356
+ end
357
+
358
+ private
359
+
360
+ def def_block_rule(is_block, new_tenv)
361
+ type_block(ast, new_tenv)
362
+
363
+ ptypes = ast.params.map { |v| new_tenv.bound_name?(v.name) }
364
+ ptypes.each_with_index do |t, i|
365
+ type_assert(t, "missing parameter type: #{ast.params[i].name}")
366
+ end
367
+ result_t = new_tenv.bound_name?(:return)
368
+ result_t = new_tenv.bound_name?(:foreign) if result_t.nil?
369
+ type_assert(result_t, 'no return type specified')
370
+
371
+ mtype = MethodType.new(ast, ptypes, result_t)
372
+ type_env.bind_name(ast.name.name, mtype.result) unless is_block
373
+
374
+ code = new_tenv.bound_name?(:native)
375
+ if code
376
+ ins_t = InstanceType.role(code)
377
+ type_assert(ins_t, 'bad native declaration')
378
+ return NativeMethodType.new(mtype, ins_t.object)
379
+ end
380
+
381
+ foreign_value = new_tenv.bound_name?(:foreign)
382
+ if foreign_value && !is_block
383
+ mtype2 = MethodType.new(nil, DynType, result_t)
384
+ return ForeignMethodType.new(mtype2)
385
+ end
386
+
387
+ type_assert_later_unless(is_block) do
388
+ body_t = type(ast.body, new_tenv)
389
+ wt = WithReturnType.role(body_t)
390
+ if result_t == Void
391
+ type_assert(wt.nil? || body_t == Void, 'non-void return statement')
392
+ else
393
+ type_assert(wt, 'no return statement')
394
+ type_assert(is_subsumed_by?(result_t, body_t), 'bad result type')
395
+ end
396
+
397
+ local_vars = {}
398
+ new_tenv.each do |name, type|
399
+ lvt = LocalVarType.role(type)
400
+ local_vars[name] = type unless lvt.nil?
401
+ end
402
+ ast.params.each do |p|
403
+ local_vars.delete(p.name.to_sym)
404
+ end
405
+
406
+ @local_vars_table[ast] = local_vars
407
+ end
408
+ mtype
409
+ end
410
+
411
+ def type_assert_later_unless(value, &proc)
412
+ unless value
413
+ check_later(&proc)
414
+ else
415
+ yield
416
+ end
417
+ end
418
+
419
+ # Do typing the given block according to typedecl.
420
+ #
421
+ def type_block(block_ast, new_env)
422
+ expr0 = nil
423
+ expr1 = nil
424
+ if block_ast.body.is_a?(Exprs)
425
+ size = block_ast.body.expressions.size
426
+ expr0 = block_ast.body.expressions[0] if size > 0
427
+ expr1 = block_ast.body.expressions[1] if size > 1
428
+ else
429
+ expr0 = block_ast.body
430
+ end
431
+
432
+ if expr0&.usertype == :return_type
433
+ new_env.bind_name(:return, type(expr0))
434
+ end
435
+
436
+ if expr0&.usertype == :typedecl
437
+ type(expr0, new_env)
438
+ elsif expr1&.usertype == :typedecl
439
+ type(expr1, new_env)
440
+ end
441
+
442
+ unless new_env.bound_name?(:return) || new_env.bound_name?(:foreign)
443
+ new_env.bind_name(:return, Void)
444
+ end
445
+ end
446
+
447
+ end
448
+ end
449
+ end