fastruby 0.0.18 → 0.0.19

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 (35) hide show
  1. data/CHANGELOG +10 -0
  2. data/Rakefile +2 -1
  3. data/benchmarks/benchmark.rb~ +2 -4
  4. data/benchmarks/benchmark3.rb~ +2 -4
  5. data/ext/fastruby_base/fastruby_base.inl +1 -1
  6. data/lib/fastruby/builder.rb +8 -7
  7. data/lib/fastruby/fastruby_sexp.rb +18 -0
  8. data/lib/fastruby/inliner/modules/call.rb +254 -67
  9. data/lib/fastruby/inliner/modules/recursive.rb +5 -0
  10. data/lib/fastruby/reductor/modules/case.rb +2 -1
  11. data/lib/fastruby/translator/modules/block.rb +65 -59
  12. data/lib/fastruby/translator/modules/call.rb +7 -4
  13. data/lib/fastruby/translator/modules/exceptions.rb +66 -64
  14. data/lib/fastruby/translator/modules/flow.rb +8 -8
  15. data/lib/fastruby/translator/modules/literal.rb +5 -4
  16. data/lib/fastruby/translator/modules/method_group.rb +6 -5
  17. data/lib/fastruby/translator/modules/nonlocal.rb +153 -11
  18. data/lib/fastruby/translator/modules/static.rb +8 -8
  19. data/lib/fastruby/translator/modules/variable.rb +15 -15
  20. data/lib/fastruby/translator/translator.rb +156 -59
  21. data/lib/fastruby.rb +1 -1
  22. data/lib/fastruby.rb~ +36 -0
  23. data/spec/fastruby/inliner/modules/call_spec.rb +0 -0
  24. data/spec/fastruby/translator/modules/nonlocal_spec.rb +0 -0
  25. data/spec/fastruby/translator/translator_spec.rb +0 -0
  26. data/spec/ruby/base_spec.rb~ +5 -5
  27. data/spec/ruby/block/break_spec.rb~ +236 -0
  28. data/spec/ruby/block/lambda_spec.rb~ +38 -0
  29. data/spec/ruby/block/next_spec.rb~ +85 -0
  30. data/spec/ruby/call/base_call_spec.rb~ +83 -0
  31. data/spec/ruby/defn/replacement_spec.rb +52 -2
  32. data/spec/ruby/defn/replacement_spec.rb~ +52 -2
  33. data/spec/ruby/exception/base_spec.rb +22 -1
  34. data/spec/ruby/return_spec.rb~ +99 -0
  35. metadata +30 -10
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ 0.0.19 Fixed temporal variable names of case blocks to make it deterministic and allow caching
2
+
3
+ Fixed bug of duplicated entries on $LOAD_PATH, significant performance improvement saves 80% of time both with caching and without caching
4
+
5
+ Inline of methods with iter calls
6
+
7
+ Inline of iter calls
8
+
9
+ Use of observer to allow replacement of CFUNC calls (example: Fixnum#+)
10
+
1
11
  0.0.18 Optimize of exising methods using "optimize" (only pure ruby methods are supported)
2
12
 
3
13
  Implemented new cache by syntax tree, more accurate, fixing a few cache bugs and compatible with inlining
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ require "rspec/core/rake_task"
7
7
 
8
8
  spec = Gem::Specification.new do |s|
9
9
  s.name = 'fastruby'
10
- s.version = '0.0.18'
10
+ s.version = '0.0.19'
11
11
  s.author = 'Dario Seminara'
12
12
  s.email = 'robertodarioseminara@gmail.com'
13
13
  s.platform = Gem::Platform::RUBY
@@ -17,6 +17,7 @@ spec = Gem::Specification.new do |s|
17
17
  s.add_dependency "ruby_parser", ">= 2.0.6"
18
18
  s.add_dependency "define_method_handler", ">= 0.0.5"
19
19
  s.add_dependency "method_source", ">= 0.6.7"
20
+ s.add_dependency "ruby2ruby", ">= 1.3.1"
20
21
  s.has_rdoc = true
21
22
  s.extra_rdoc_files = [ 'README' ]
22
23
  s.extensions = FileList["ext/**/extconf.rb"].to_a
@@ -1,11 +1,9 @@
1
1
  require "rubygems"
2
2
  require "fastruby"
3
3
 
4
- def lvar_type(*x); end
5
-
6
4
  class X
7
5
  def foo(a,b)
8
- return a+b
6
+ a+b
9
7
  end
10
8
  end
11
9
 
@@ -17,7 +15,7 @@ class Y
17
15
 
18
16
  ret = 0
19
17
  while i > 0
20
- ret = x.foo(i,i)
18
+ ret = (i+i)
21
19
  i = i - 1
22
20
  end
23
21
  return ret
@@ -1,11 +1,9 @@
1
1
  require "rubygems"
2
2
  require "fastruby"
3
3
 
4
- def lvar_type(*x); end
5
-
6
4
  class X
7
5
  def foo
8
- yield(1,2,3)
6
+ yield(1)
9
7
  end
10
8
  end
11
9
 
@@ -17,7 +15,7 @@ class Y
17
15
 
18
16
  ret = 0
19
17
  while i > 0
20
- x.foo do |a,b,c|
18
+ x.foo do |a|
21
19
  end
22
20
  i = i - 1
23
21
  end
@@ -402,6 +402,7 @@ static VALUE fastruby_method_tree_pointer(VALUE self) {
402
402
 
403
403
  if (rb_tree_pointer == Qnil) {
404
404
  VALUE* tree_pointer = malloc(sizeof(VALUE*));
405
+ rb_gc_register_address(tree_pointer);
405
406
  *tree_pointer = Qnil;
406
407
  rb_tree_pointer = LONG2FIX(tree_pointer);
407
408
  rb_ivar_set(self, rb_intern("@tree"), rb_tree_pointer);
@@ -412,7 +413,6 @@ static VALUE fastruby_method_tree_pointer(VALUE self) {
412
413
 
413
414
  static VALUE fastruby_method_tree_eq(VALUE self, VALUE val) {
414
415
  VALUE* tree_pointer = (VALUE*)FIX2LONG(fastruby_method_tree_pointer(self));
415
- rb_ivar_set(self, rb_intern("__current_tree"), val);
416
416
  *tree_pointer = val;
417
417
  return Qnil;
418
418
  }
@@ -23,6 +23,10 @@ require "fastruby/logging"
23
23
  require "fastruby/getlocals"
24
24
  require "fastruby_load_path"
25
25
  require "fastruby/inliner/inliner"
26
+ require "fastruby/translator/translator"
27
+ require "rubygems"
28
+ require "inline"
29
+ require "fastruby/inline_extension"
26
30
 
27
31
  require FastRuby.fastruby_load_path + "/../ext/fastruby_base/fastruby_base"
28
32
 
@@ -68,11 +72,6 @@ module FastRuby
68
72
  def rebuild(signature, noreturn = false)
69
73
  no_cache = false
70
74
  mname = FastRuby.make_str_signature(@method_name, signature)
71
-
72
- require "fastruby/translator/translator"
73
- require "rubygems"
74
- require "inline"
75
- require "fastruby/inline_extension"
76
75
 
77
76
  inliner = FastRuby::Inliner.new
78
77
 
@@ -119,13 +118,15 @@ module FastRuby
119
118
  end
120
119
 
121
120
  inliner.inlined_methods.each do |inlined_method|
122
- inlined_method.observe("#{@owner}##{@method_name}") do |imethod|
121
+ inlined_method.observe("#{@owner}##{@method_name}#{mname}") do |imethod|
123
122
  rebuild(signature, noreturn)
124
123
  end
125
124
  end
126
125
 
127
126
  alt_options = options.dup
128
127
  alt_options.delete(:self)
128
+
129
+ #require "pry"; binding.pry
129
130
  code_sha1 = FastRuby.cache.hash_snippet(inlined_tree.inspect, FastRuby::VERSION + signature.map(&:to_s).join('-') + alt_options.inspect)
130
131
 
131
132
  paths = FastRuby.cache.retrieve(code_sha1)
@@ -224,7 +225,7 @@ module FastRuby
224
225
  $class_self = old_class_self
225
226
  end
226
227
 
227
- observe("#{@owner}##{@method_name}") do |imethod|
228
+ observe("#{@owner}##{@method_name}#{mname}") do |imethod|
228
229
  if tree
229
230
  rebuild(signature, noreturn)
230
231
  end
@@ -32,6 +32,24 @@ module FastRuby
32
32
  }
33
33
  end
34
34
 
35
+ def to_sexp
36
+ ret = s()
37
+ each do |subtree|
38
+ if subtree.respond_to?(:to_sexp)
39
+ ret << subtree.to_sexp
40
+ else
41
+ ret << subtree
42
+ end
43
+ end
44
+
45
+ ret
46
+ end
47
+
48
+ def to_ruby
49
+ require "ruby2ruby"
50
+ Ruby2Ruby.new.process(to_sexp)
51
+ end
52
+
35
53
  def map
36
54
  sexp = FastRubySexp.new
37
55
  self.each do |subtree|
@@ -24,82 +24,132 @@ require "define_method_handler"
24
24
 
25
25
  module FastRuby
26
26
  class Inliner
27
+
28
+ class BlockProcessing
29
+ def initialize(inlined_name, break_inlined_name)
30
+ @inlined_name = inlined_name
31
+ @break_inlined_name = break_inlined_name
32
+ end
33
+
34
+ define_method_handler(:process, :priority => -100) { |tree|
35
+ tree.map &method(:process)
36
+ }
37
+
38
+ define_method_handler(:process, :priority => 1000) { |tree|
39
+ tree
40
+ }.condition{|tree| not tree.respond_to?(:node_type) }
41
+
42
+ define_method_handler(:process) { |tree|
43
+ if tree[1]
44
+ fs(:call, nil, :_throw, fs(:arglist, fs(:lit,@inlined_name.to_sym), process(tree[1])))
45
+ else
46
+ fs(:call, nil, :_throw, fs(:arglist, fs(:lit,@inlined_name.to_sym), fs(:nil)))
47
+ end
48
+ }.condition{|tree| tree.node_type == :next}
49
+
50
+ define_method_handler(:process) { |tree|
51
+ if tree[1]
52
+ fs(:call, nil, :_throw, fs(:arglist, fs(:lit,@break_inlined_name.to_sym), process(tree[1])))
53
+ else
54
+ fs(:call, nil, :_throw, fs(:arglist, fs(:lit,@break_inlined_name.to_sym), fs(:nil)))
55
+ end
56
+ }.condition{|tree| tree.node_type == :break and @break_inlined_name}
57
+
58
+ define_method_handler(:process) { |tree|
59
+ fs(:call, nil, :_loop, fs(:arglist, fs(:lit,@inlined_name.to_sym)))
60
+ }.condition{|tree| tree.node_type == :redo}
61
+
62
+ define_method_handler(:process) { |tree|
63
+ if tree[3]
64
+ fs(:iter, process(tree[1]), tree[2], tree[3].duplicate)
65
+ else
66
+ fs(:iter, process(tree[1]), tree[2])
67
+ end
68
+ }.condition{|tree| tree.node_type == :iter}
69
+ end
70
+
27
71
  def inline_local_name(method_name, local_name)
28
72
  "__inlined_#{method_name}_#{local_name}".to_sym
29
73
  end
30
74
 
31
- define_method_handler(:inline) { |tree|
32
- ret_tree = fs(:iter)
33
- ret_tree << tree[1].duplicate
34
-
35
- tree[2..-1].each do |subtree|
36
- ret_tree << inline(subtree)
37
- end
38
-
39
- ret_tree
40
-
41
- }.condition{|tree| tree.node_type == :iter}
75
+ def method_obj_or_gtfo(klass, method_name)
76
+ return nil unless klass.respond_to?(:fastruby_method)
77
+ klass.fastruby_method(method_name)
78
+ end
42
79
 
43
- define_method_handler(:inline) { |tree|
44
-
45
- next tree if tree.find_tree(:block_pass)
46
-
47
- recv_tree = tree[1] || fs(:self)
48
- method_name = tree[2]
49
- args_tree = tree[3]
50
-
51
- if method_name == :lvar_type
52
- lvar_name = args_tree[1][1] || args_tree[1][2]
53
- lvar_type = eval(args_tree[2][1].to_s)
54
-
55
- @infer_lvar_map[lvar_name] = lvar_type
56
- next tree
80
+ def add_prefix(tree, prefix)
81
+ tree = tree.duplicate
82
+ tree.walk_tree do |subtree|
83
+ if subtree.node_type == :lvar or subtree.node_type == :lasgn
84
+ subtree[1] = inline_local_name(prefix, subtree[1])
85
+ add_local subtree[1]
86
+ end
57
87
  end
58
88
 
59
- recvtype = infer_type(recv_tree)
60
-
61
- if recvtype
62
- # search the tree of target method
63
- next tree unless recvtype.respond_to?(:fastruby_method)
64
-
65
- mobject = recvtype.fastruby_method(method_name)
89
+ tree
90
+ end
91
+
92
+ def catch_block(name,tree)
93
+ fs(:block,fs(:iter, fs(:call, nil, :_catch, fs(:arglist, fs(:lit,name.to_sym))),nil,tree))
94
+ end
95
+
96
+ def method_tree_to_inlined_block(mobject, call_tree, method_name, block_args_tree = nil, block_tree = nil)
97
+ args_tree = call_tree[3]
98
+ recv_tree = call_tree[1] || fs(:self)
66
99
 
67
- next tree unless mobject
100
+ target_method_tree = mobject.tree
68
101
 
69
- target_method_tree = mobject.tree
70
-
71
- next tree unless target_method_tree
72
- next tree if target_method_tree.find_tree(:iter)
73
- target_method_tree_args = target_method_tree[2]
102
+ @method_index = (@method_index || 0) + 1
74
103
 
75
- next tree if target_method_tree_args.find{|subtree| subtree.to_s =~ /^\*/}
76
-
77
-
78
- target_method_tree_block = target_method_tree.find_tree(:scope)[1].duplicate
104
+ prefix = method_name.to_s + "_" + @method_index.to_s
105
+ target_method_tree_block = add_prefix(target_method_tree.find_tree(:scope)[1], prefix)
79
106
 
80
- target_method_tree_block.walk_tree do |subtree|
81
- if subtree.node_type == :lvar or subtree.node_type == :lasgn
82
- subtree[1] = inline_local_name(method_name, subtree[1])
83
- add_local subtree[1]
107
+ if target_method_tree_block.find_tree(:return)
108
+ inlined_name = inline_local_name(method_name, "main_return_tagname")
109
+ target_method_tree_block = catch_block(inlined_name,target_method_tree_block)
110
+
111
+ target_method_tree_block.walk_tree do |subtree|
112
+ if subtree[0] == :return
113
+ if subtree[1]
114
+ subtree[0..-1] = fs(:call, nil, :_throw, fs(:arglist, fs(:lit,inlined_name.to_sym), subtree[1]))
115
+ else
116
+ subtree[0..-1] = fs(:call, nil, :_throw, fs(:arglist, fs(:lit,inlined_name.to_sym), fs(:nil)))
117
+ end
118
+ end
84
119
  end
85
120
  end
86
121
 
122
+ target_method_tree_args = target_method_tree[2]
123
+
87
124
  newblock = fs(:block)
88
125
 
89
126
  (1..args_tree.size-1).each do |i|
90
127
  itype = infer_type(args_tree[i])
91
- inlined_name = inline_local_name(method_name, target_method_tree_args[i])
128
+ inlined_name = inline_local_name(prefix, target_method_tree_args[i])
92
129
 
93
130
  add_local inlined_name
94
131
 
95
132
  self.extra_inferences[inlined_name] = itype if itype
96
- newblock << fs(:lasgn, inlined_name, args_tree[i].duplicate)
133
+ newblock << fs(:lasgn, inlined_name, recursive_inline(args_tree[i].duplicate))
97
134
  end
98
135
 
99
- inlined_name = inline_local_name(method_name, :self)
136
+ inlined_name = inline_local_name(prefix, :self)
100
137
  add_local inlined_name
101
138
  newblock << fs(:lasgn, inlined_name, recv_tree.duplicate)
102
139
 
140
+ return nil if target_method_tree_block.find_tree(:return)
141
+
142
+ break_tag = nil
143
+
144
+ if block_tree
145
+ block_tree = recursive_inline(block_tree)
146
+ if block_tree.find_tree(:break) # FIXME: discard nested iter calls on finding
147
+ break_tag = inline_local_name(prefix, "__break_tag")
148
+ end
149
+ end
150
+
151
+ block_num = 0
152
+
103
153
  target_method_tree_block.walk_tree do |subtree|
104
154
  if subtree.node_type == :call
105
155
  if subtree[1] == nil
@@ -112,38 +162,175 @@ module FastRuby
112
162
  end
113
163
  if subtree.node_type == :self
114
164
  subtree[0] = :lvar
115
- subtree[1] = inline_local_name(method_name, :self)
165
+ subtree[1] = inline_local_name(prefix, :self)
116
166
  end
117
- end
118
-
119
- (1..target_method_tree_block.size-1).each do |i|
120
- subtree = target_method_tree_block[i]
121
-
122
- if subtree.find_tree(:return)
123
- if i == target_method_tree_block.size-1
124
- if subtree.node_type == :return
125
- if subtree[1]
126
- if subtree[1].find_tree(:return)
127
- next tree
167
+ if subtree.node_type == :yield
168
+ if block_tree
169
+ # inline yield
170
+ yield_call_args = subtree.duplicate
171
+
172
+ subtree[0..-1] = fs(:block)
173
+
174
+ if block_args_tree
175
+ return nil if yield_call_args[1..-1].find{|x| x.node_type == :splat}
176
+ if block_args_tree.node_type == :masgn
177
+ return nil if block_args_tree[1].size != yield_call_args.size
178
+ return nil if block_args_tree[1][1..-1].find{|x| x.node_type == :splat}
179
+
180
+ (1..yield_call_args.size-1).each do |i|
181
+ inlined_name = block_args_tree[1][i][1]
182
+ add_local inlined_name
183
+ subtree << fs(:lasgn, inlined_name, yield_call_args[i])
128
184
  end
185
+ else
186
+ return nil if 2 != yield_call_args.size
187
+
188
+ inlined_name = block_args_tree[1]
189
+ add_local inlined_name
190
+ subtree << fs(:lasgn, inlined_name, yield_call_args[1])
129
191
  end
192
+ else
193
+ return nil if yield_call_args.size > 1
194
+ end
195
+
196
+ if block_tree.find_tree(:next) or block_tree.find_tree(:redo) or break_tag
197
+ inlined_name = inline_local_name(prefix, "block_block_#{block_num}")
198
+ block_num = block_num + 1
130
199
 
131
- subtree[0..-1] = subtree[1]
200
+ alt_block_tree = BlockProcessing.new(inlined_name, break_tag).process(block_tree)
201
+ alt_block_tree = catch_block(inlined_name,alt_block_tree)
202
+ else
203
+ alt_block_tree = block_tree.duplicate
132
204
  end
205
+ subtree << alt_block_tree
133
206
  else
134
- # methods with return cannot be inlined
135
- next tree
207
+ subtree[0..-1] = fs(:call, fs(:nil), :raise, fs(:arglist, fs(:const, :LocalJumpError), fs(:str, "no block given")))
136
208
  end
137
209
  end
138
-
139
- newblock << subtree
140
210
  end
141
211
 
142
212
  @inlined_methods << mobject
213
+
214
+ if break_tag
215
+ inner_block = fs(:block)
216
+ target_method_tree_block[1..-1].each do |subtree|
217
+ inner_block << subtree
218
+ end
219
+
220
+ newblock << catch_block(break_tag,inner_block)
221
+ else
222
+ target_method_tree_block[1..-1].each do |subtree|
223
+ newblock << subtree
224
+ end
225
+ end
143
226
  newblock
227
+ end
228
+
229
+ define_method_handler(:inline) { |tree|
230
+ tree
231
+ }.condition{|tree| tree.node_type == :defs}
232
+
233
+ define_method_handler(:inline) { |tree|
234
+ ret_tree = fs(:iter)
235
+ ret_tree << tree[1].duplicate
236
+
237
+ call_tree = tree[1]
238
+ recv_tree = call_tree[1] || fs(:self)
239
+ method_name = call_tree[2]
240
+ args_tree = call_tree[3]
241
+ block_tree = tree[3] || fs(:nil)
242
+ block_args_tree = tree[2]
243
+
244
+ tree[2..-1].each do |subtree|
245
+ ret_tree << inline(subtree)
246
+ end
247
+
248
+ next ret_tree if block_tree.find_tree(:retry)
249
+
250
+ recvtype = infer_type(recv_tree)
251
+
252
+ if recvtype
253
+ # search the tree of target method
254
+ mobject = method_obj_or_gtfo(recvtype,method_name)
255
+
256
+ next tree unless mobject
257
+ next tree unless mobject.tree
258
+
259
+ exit_now = false
260
+ if block_tree.find_tree(:break) or block_tree.find_tree(:return)
261
+ mobject.tree.walk_tree do |subtree|
262
+ if subtree.node_type == :iter
263
+ if subtree.find_tree(:yield)
264
+ exit_now = true
265
+ break
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ next tree if exit_now
272
+
273
+ target_method_tree_args = mobject.tree[2]
274
+ next tree if target_method_tree_args.find{|subtree| subtree.to_s =~ /^\*/}
275
+
276
+ method_tree_to_inlined_block(mobject, call_tree, method_name, block_args_tree, block_tree) || ret_tree
277
+
278
+ else
279
+ # nothing to do, we don't know what is the method
280
+ ret_tree
281
+ end
282
+ }.condition{|tree| tree.node_type == :iter}
283
+
284
+ define_method_handler(:inline) { |tree|
285
+
286
+ next tree if tree.find_tree(:block_pass)
287
+
288
+ recv_tree = tree[1] || fs(:self)
289
+ method_name = tree[2]
290
+ args_tree = tree[3]
291
+
292
+ if method_name == :lvar_type
293
+ lvar_name = args_tree[1][1] || args_tree[1][2]
294
+ lvar_type = eval(args_tree[2][1].to_s)
295
+
296
+ @infer_lvar_map[lvar_name] = lvar_type
297
+ next recursive_inline(tree)
298
+ end
299
+
300
+ recvtype = infer_type(recv_tree)
301
+
302
+ if recvtype
303
+ # search the tree of target method
304
+ mobject = method_obj_or_gtfo(recvtype,method_name)
305
+
306
+ next recursive_inline(tree) unless mobject
307
+ next recursive_inline(tree) unless mobject.tree
308
+
309
+ exit_now = false
310
+ mobject.tree.walk_tree do |subtree|
311
+ if subtree.node_type == :iter
312
+ if subtree.find_tree(:return)
313
+ exit_now = true
314
+ break
315
+ end
316
+ end
317
+ end
318
+
319
+ next recursive_inline(tree) if exit_now
320
+
321
+ target_method_tree_args = if mobject.tree.node_type == :defn
322
+ mobject.tree[2]
323
+ else
324
+ mobject.tree[3]
325
+ end
326
+
327
+ next recursive_inline(tree) if target_method_tree_args.find{|subtree| subtree.to_s =~ /^\*/}
328
+
329
+ method_tree_to_inlined_block(mobject, tree, method_name) || recursive_inline(tree)
330
+
144
331
  else
145
332
  # nothing to do, we don't know what is the method
146
- tree
333
+ recursive_inline(tree)
147
334
  end
148
335
  }.condition{|tree| tree.node_type == :call}
149
336
  end
@@ -24,6 +24,11 @@ require "define_method_handler"
24
24
 
25
25
  module FastRuby
26
26
  class Inliner
27
+
28
+ def recursive_inline(tree)
29
+ tree.map &method(:inline)
30
+ end
31
+
27
32
  define_method_handler(:inline, :priority => -100) {|tree|
28
33
  tree.map &method(:inline)
29
34
  }.condition{|tree| tree.respond_to?(:node_type)}
@@ -41,7 +41,8 @@ module FastRuby
41
41
  end
42
42
 
43
43
  reduce_for(:case) do |tree|
44
- temporal_var_name = "temporal_case_var_#{rand(1000000000)}".to_sym
44
+ @case_index = (@case_index || 0) + 1
45
+ temporal_var_name = "temporal_case_var_#{@case_index}".to_sym
45
46
  ifs = when_array_to_if(tree[2..-1], temporal_var_name)
46
47
  fs(:block, fs(:lasgn, temporal_var_name, tree[1]), ifs)
47
48
  end