fastruby 0.0.18 → 0.0.19

Sign up to get free protection for your applications and to get access to all the features.
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