crystalizer 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. data/Changelog +27 -0
  2. data/README +79 -0
  3. data/Rakefile +24 -0
  4. data/TODO +14 -0
  5. data/VERSION +1 -0
  6. data/benchmarks/bench.rb +129 -0
  7. data/benchmarks/concretize_test.rb +26 -0
  8. data/benchmarks/extconf.rb +10 -0
  9. data/benchmarks/tak_rb.rb +7 -0
  10. data/benchmarks/tak_so.rb +7 -0
  11. data/benchmarks/tak_source.rb +16 -0
  12. data/bin/rb2cx +162 -0
  13. data/doc/eval2c.txt +246 -0
  14. data/doc/gen_html.rb +26 -0
  15. data/doc/html_template +10 -0
  16. data/doc/index.txt +169 -0
  17. data/doc/limitations.txt +529 -0
  18. data/doc/optimizations.txt +185 -0
  19. data/doc/rb2cx.txt +130 -0
  20. data/doc/style.css +27 -0
  21. data/lib/concretizer.rb +3 -0
  22. data/lib/ruby2cext/c_function.rb +617 -0
  23. data/lib/ruby2cext/common_node_comp.rb +1412 -0
  24. data/lib/ruby2cext/compiler.rb +311 -0
  25. data/lib/ruby2cext/concretize.rb +269 -0
  26. data/lib/ruby2cext/error.rb +15 -0
  27. data/lib/ruby2cext/eval2c.rb +126 -0
  28. data/lib/ruby2cext/parser.rb +36 -0
  29. data/lib/ruby2cext/plugin.rb +24 -0
  30. data/lib/ruby2cext/plugins/builtin_methods.rb +817 -0
  31. data/lib/ruby2cext/plugins/cache_call.rb +293 -0
  32. data/lib/ruby2cext/plugins/case_optimize.rb +102 -0
  33. data/lib/ruby2cext/plugins/const_cache.rb +36 -0
  34. data/lib/ruby2cext/plugins/direct_self_call.rb +70 -0
  35. data/lib/ruby2cext/plugins/inline_builtin.rb +797 -0
  36. data/lib/ruby2cext/plugins/inline_methods.rb +68 -0
  37. data/lib/ruby2cext/plugins/ivar_cache.rb +147 -0
  38. data/lib/ruby2cext/plugins/require_include.rb +69 -0
  39. data/lib/ruby2cext/plugins/util.rb +154 -0
  40. data/lib/ruby2cext/plugins/warnings.rb +121 -0
  41. data/lib/ruby2cext/scopes.rb +225 -0
  42. data/lib/ruby2cext/str_to_c_strlit.rb +12 -0
  43. data/lib/ruby2cext/tools.rb +80 -0
  44. data/lib/ruby2cext/version.rb +22 -0
  45. data/results +68 -0
  46. data/setup.rb +1585 -0
  47. data/stuff/builtin_methods.rb +69 -0
  48. data/stuff/builtin_methods_test.rb +37 -0
  49. data/test/bootstrap.rb +10 -0
  50. data/test/causes_crash_all_opts.rb +1165 -0
  51. data/test/eval2c/test_eval2c.rb +37 -0
  52. data/test/temp_17.rb +16 -0
  53. data/test/temp_18.rb +8 -0
  54. data/test/temp_19.rb +8 -0
  55. data/test/temp_2.rb +7 -0
  56. data/test/temp_20.rb +5 -0
  57. data/test/temp_21.rb +161 -0
  58. data/test/temp_22.rb +7 -0
  59. data/test/temp_23.rb +7 -0
  60. data/test/temp_24.rb +219 -0
  61. data/test/temp_25.rb +7 -0
  62. data/test/temp_26.rb +11 -0
  63. data/test/temp_27.rb +11 -0
  64. data/test/temp_28.rb +9 -0
  65. data/test/temp_29.rb +9 -0
  66. data/test/temp_3.rb +0 -0
  67. data/test/temp_30.rb +0 -0
  68. data/test/temp_31.rb +10 -0
  69. data/test/temp_32.rb +10 -0
  70. data/test/temp_33.rb +15 -0
  71. data/test/temp_34.rb +15 -0
  72. data/test/temp_35.rb +7 -0
  73. data/test/temp_36.rb +7 -0
  74. data/test/temp_37.rb +10 -0
  75. data/test/temp_38.rb +10 -0
  76. data/test/temp_39.rb +0 -0
  77. data/test/temp_4.rb +7 -0
  78. data/test/temp_40.rb +50 -0
  79. data/test/temp_41.rb +50 -0
  80. data/test/temp_42.rb +8 -0
  81. data/test/temp_43.rb +8 -0
  82. data/test/temp_44.rb +0 -0
  83. data/test/temp_48.rb +7 -0
  84. data/test/temp_49.rb +7 -0
  85. data/test/temp_5.rb +7 -0
  86. data/test/temp_59.rb +7 -0
  87. data/test/temp_6.rb +7 -0
  88. data/test/temp_60.rb +7 -0
  89. data/test/temp_68.rb +239 -0
  90. data/test/temp_7.rb +7 -0
  91. data/test/temp_70.rb +7 -0
  92. data/test/temp_71.rb +7 -0
  93. data/test/temp_72.rb +13 -0
  94. data/test/temp_73.rb +7 -0
  95. data/test/temp_74.rb +7 -0
  96. data/test/temp_76.rb +7 -0
  97. data/test/temp_77.rb +13 -0
  98. data/test/temp_79.rb +7 -0
  99. data/test/temp_8.rb +14 -0
  100. data/test/temp_81.rb +14 -0
  101. data/test/temp_83.rb +0 -0
  102. data/test/temp_84.rb +7 -0
  103. data/test/temp_85.rb +7 -0
  104. data/test/temp_86.rb +14 -0
  105. data/test/temp_87.rb +7 -0
  106. data/test/temp_88.rb +7 -0
  107. data/test/temp_89.rb +7 -0
  108. data/test/temp_9.rb +14 -0
  109. data/test/temp_90.rb +0 -0
  110. data/test/temp_91.rb +7 -0
  111. data/test/temp_92.rb +7 -0
  112. data/test/temp_93.rb +7 -0
  113. data/test/temp_94.rb +7 -0
  114. data/test/temp_95.rb +7 -0
  115. data/test/temp_96.rb +0 -0
  116. data/test/temp_97.rb +0 -0
  117. data/test/temp_98.rb +7 -0
  118. data/test/temp_99.rb +7 -0
  119. data/test/test_concretize.rb +132 -0
  120. data/test/test_concretize_all.rb +15 -0
  121. data/test/test_crystalize_block.rb +73 -0
  122. data/test/test_files/test.rb +615 -0
  123. data/test/test_files/vmode_test.rb +73 -0
  124. data/test/test_files/warn_test.rb +35 -0
  125. data/test/test_syntax.rb +25 -0
  126. metadata +268 -0
@@ -0,0 +1,293 @@
1
+ # this one does something like...
2
+ # @ location X
3
+ # there is a call of method y on z
4
+ # if z has the same class as last time
5
+ # then do something of a pseudo-ruby-call into that method, since
6
+ # we have a pointer saved to it from the previous time.
7
+ # the pseudo-ruby call being necessary so that we can pass in arguments in ruby like fashion
8
+ # to the C method, etc.
9
+ # what are the limitations?
10
+ #
11
+ # this one also conflicts with builtin_extensions
12
+ # which...might be faster?
13
+ # TODOR make the order matter
14
+ # TODOR various
15
+
16
+ module Ruby2CExtension::Plugins
17
+
18
+ class CacheCall < Ruby2CExtension::Plugin
19
+
20
+ include Util
21
+
22
+ def initialize(compiler, need_frame=true)
23
+ super(compiler)
24
+ @need_frame = need_frame || nil
25
+ @cache_index = Hash.new { |h,k| h[k] = h.size }
26
+ @argcs = {}
27
+ compiler.add_preprocessor(:call, &method(:handle_call))
28
+ compiler.add_preprocessor(:vcall, &method(:handle_call))
29
+ compiler.add_preprocessor(:fcall, &method(:handle_call))
30
+ compiler.add_preprocessor(:attrasgn, &method(:handle_call))
31
+ end
32
+
33
+ def handle_call(cfun, node)
34
+ ntype = node.first
35
+ hash = node.last
36
+ mid = hash[:mid]
37
+ args = hash[:args] || [:array, []]
38
+ argc = if args.first == :array
39
+ args = args.last
40
+ args.size
41
+ else
42
+ -1
43
+ end
44
+ @argcs[argc] = true
45
+ recv = hash[:recv]
46
+ allow_private = case ntype
47
+ when :fcall, :vcall
48
+ 1
49
+ else
50
+ recv.equal?(0) ? 1 : 0
51
+ end
52
+ recv = nil if recv.equal?(0)
53
+ recv ||= [:self, {}]
54
+ key = [mid, argc, allow_private]
55
+ if type=deduce_type(recv)
56
+ key << type
57
+ else
58
+ key << cfun.__id__ << recv.inspect
59
+ end
60
+ index = @cache_index[key]
61
+ entry = "method_cache[#{index}]"
62
+ assign = node.first.equal?(:attrasgn)
63
+ if argc >= 0
64
+ last = assign && args.pop
65
+ values(cfun, 1, last, recv, *args) { |last, recv, *args|
66
+ args << last if assign
67
+ args = args.inject("") { |s, arg| s.concat(", #{arg}") }
68
+ # cache_call1 means "one argument"
69
+ # delta == 0 means "they accept exactly as many args as I want to pass them"
70
+ # delta == 1 means "it accepts -1"
71
+ # it appears to
72
+ call = "cache_call#{argc}(#{allow_private}, &#{entry}, #{recv}, #{cfun.sym(mid)}#{args})"
73
+ if assign
74
+ cfun.l("#{call};")
75
+ last
76
+ else
77
+ call
78
+ end
79
+ }
80
+ else
81
+ recv = cfun.comp(recv)
82
+ cfun.c_scope_res {
83
+ cfun.l "VALUE recv = #{recv};"
84
+ cfun.build_args(args)
85
+ call = "cache_call(#{allow_private}, &#{entry}, recv, #{cfun.sym(mid)}, argc, argv)"
86
+ if assign
87
+ cfun.l("#{call};")
88
+ "argv[argc-1]"
89
+ else
90
+ call
91
+ end
92
+ }
93
+ end
94
+ end
95
+
96
+ def global_c_code
97
+ begin
98
+ #TODO: try handling NOEX_PROTECTED
99
+ code = %{
100
+ typedef struct {
101
+ VALUE klass;
102
+ VALUE (*func)(ANYARGS);
103
+ #{@need_frame && 'VALUE origin;'}
104
+ #{@need_frame && 'ID mid0;'}
105
+ } method_cache_entry;
106
+ static method_cache_entry method_cache[#{@cache_index.size}];
107
+ static void init_method_cache() {
108
+ method_cache_entry* entry;
109
+ for (entry = method_cache + #{@cache_index.size-1}; entry >= method_cache; --entry) {
110
+ entry->klass = 0;
111
+ entry->func = 0;
112
+ }
113
+ }
114
+ static VALUE recache(
115
+ int allow_private, method_cache_entry *entry,
116
+ VALUE recv, ID mid, int argc
117
+ ) {
118
+ VALUE klass = CLASS_OF(recv);
119
+ entry->klass = klass;
120
+ NODE* body;
121
+ while (!st_lookup(RCLASS(klass)->m_tbl, mid,
122
+ (st_data_t *)&body))
123
+ {
124
+ klass = RCLASS(klass)->super;
125
+ if (!klass) {
126
+ entry->klass += 2;
127
+ return;
128
+ }
129
+ }
130
+ if (!allow_private &&
131
+ (body->nd_noex & (NOEX_PRIVATE|NOEX_PROTECTED)))
132
+ {
133
+ entry->klass += 2;
134
+ return;
135
+ }
136
+ body = body->nd_body;
137
+ if (nd_type(body) == NODE_FBODY) {
138
+ #{@need_frame && 'entry->origin = body->nd_orig;'}
139
+ #{@need_frame && 'entry->mid0 = body->nd_mid;'}
140
+ body = body->nd_head;
141
+ } else {
142
+ #{@need_frame && 'entry->origin = klass;'}
143
+ #{@need_frame && 'entry->mid0 = mid;'}
144
+ }
145
+ if (nd_type(body) == NODE_CFUNC) {
146
+ entry->func = body->nd_cfnc;
147
+ if (body->nd_argc == -1) {
148
+ entry->klass += 1;
149
+ return;
150
+ } else if (body->nd_argc == argc) {
151
+ return;
152
+ }
153
+ }
154
+ entry->klass += 2;
155
+ return;
156
+ }
157
+ }
158
+ end
159
+ @argcs.keys.sort.each { |argc|
160
+ args = (0...argc).inject("") { |s, i|
161
+ s.concat(", arg#{i}")
162
+ }
163
+ args_declare = (0...argc).inject("") { |s, i|
164
+ s.concat(", VALUE arg#{i}")
165
+ }
166
+ argv_init = (0...argc).inject("") { |s, i|
167
+ s.concat("argv[#{i}]=arg#{i}; ")
168
+ }
169
+ argv = case argc
170
+ when 0
171
+ "0"
172
+ when 1
173
+ "&arg0"
174
+ else
175
+ "argv"
176
+ end
177
+ if @need_frame
178
+ # NOTE: uniq=(long)&_frame should be frame_unique++
179
+ pre_call = %({
180
+ VALUE res;
181
+ struct FRAME _frame = {
182
+ .self = recv,
183
+ .argc = #{(argc < 0) ? 'argc' : argc},
184
+ .last_func = mid,
185
+ .orig_func = entry->mid0,
186
+ .last_class = entry->origin,
187
+ .prev = ruby_frame,
188
+ .tmp = 0,
189
+ .node = ruby_current_node,
190
+ .iter = 0,
191
+ .flags = 0,
192
+ .uniq = (long)&_frame
193
+ };
194
+ ruby_frame = &_frame;
195
+ res = )
196
+ post_call = %(
197
+ ruby_frame = _frame.prev;
198
+ return res;
199
+ })
200
+ else
201
+ pre_call = "return "
202
+ post_call = ""
203
+ end
204
+ code.concat((argc < 0) ? %{
205
+ static inline VALUE cache_call(
206
+ int allow_private, method_cache_entry *entry,
207
+ VALUE recv, ID mid, int argc, VALUE *argv
208
+ ) {
209
+ while (1) {
210
+ VALUE klass = CLASS_OF(recv);
211
+ switch (entry->klass - klass) {
212
+ case 1:
213
+ #{pre_call}
214
+ (*entry->func)(argc, argv, recv);
215
+ #{post_call}
216
+ case 0:
217
+ case 2:
218
+ return (*(allow_private ? rb_funcall2 : rb_funcall3))(
219
+ recv, mid, argc, argv
220
+ );
221
+ default:
222
+ recache(allow_private, entry, recv, mid, argc);
223
+ }
224
+ }
225
+ }
226
+ } : (argc<=1) ? %{
227
+ static inline VALUE cache_call#{argc}(
228
+ int allow_private, method_cache_entry *entry,
229
+ VALUE recv, ID mid#{args_declare}
230
+ ) {
231
+ while (1) {
232
+ VALUE klass = CLASS_OF(recv);
233
+ VALUE delta = entry->klass - klass;
234
+ // here1
235
+ switch (delta) {
236
+ case 0:
237
+ case 1:
238
+ #{pre_call}
239
+ delta ? (*entry->func)(#{argc}, #{argv}, recv) :
240
+ (*entry->func)(recv#{args});
241
+ #{post_call}
242
+ case 2:
243
+ return (*(allow_private ? rb_funcall2 : rb_funcall3))(
244
+ recv, mid, #{argc}, #{argv}
245
+ );
246
+ default:
247
+ recache(allow_private, entry, recv, mid, #{argc});
248
+ }
249
+ }
250
+ }
251
+ } : %{
252
+ static inline VALUE cache_call#{argc}(
253
+ int allow_private, method_cache_entry *entry,
254
+ VALUE recv, ID mid#{args_declare}
255
+ ) {
256
+ while (1) {
257
+ VALUE klass = CLASS_OF(recv);
258
+ VALUE delta = entry->klass - klass;
259
+ if (delta==0) {
260
+ #{pre_call}
261
+ (*entry->func)(recv#{args});
262
+ #{post_call}
263
+ } else {
264
+ VALUE argv[#{argc}];
265
+ #{argv_init}
266
+ if (delta == 1) {
267
+ #{pre_call}
268
+ (*entry->func)(#{argc}, #{argv}, recv);
269
+ #{post_call}
270
+ } else if (delta == 2) {
271
+ return (*(allow_private ? rb_funcall2 : rb_funcall3))(
272
+ recv, mid, #{argc}, #{argv}
273
+ );
274
+ } else {
275
+ recache(allow_private, entry, recv, mid, #{argc});
276
+ }
277
+ }
278
+ }
279
+ }
280
+ })
281
+ }
282
+ code
283
+ end
284
+
285
+ def init_c_code
286
+ %{
287
+ init_method_cache();
288
+ }
289
+ end
290
+
291
+ end
292
+
293
+ end
@@ -0,0 +1,102 @@
1
+
2
+ module Ruby2CExtension::Plugins
3
+
4
+ class CaseOptimize < Ruby2CExtension::Plugin
5
+
6
+ include Ruby2CExtension::Tools::EnsureNodeTypeMixin
7
+
8
+ def initialize(compiler)
9
+ super
10
+ compiler.add_preprocessor(:case) { |cfun, node|
11
+ handle_case(cfun, node.last) || node
12
+ }
13
+ end
14
+
15
+ def fixnum?(node)
16
+ Array === node && node.first == :lit && Fixnum === node.last[:lit]
17
+ end
18
+
19
+ def fixed_immediate?(node)
20
+ # checks if the node is optimizable and if yes returns the equivalent C value
21
+ if fixnum?(node)
22
+ "LONG2FIX(#{node.last[:lit].inspect})"
23
+ elsif Array === node
24
+ case node.first
25
+ when :nil
26
+ "Qnil"
27
+ when :true
28
+ "Qtrue"
29
+ when :false
30
+ "Qfalse"
31
+ else
32
+ false
33
+ end
34
+ else
35
+ false
36
+ end
37
+ end
38
+
39
+ def handle_case(cfun, hash)
40
+ cur_when = hash[:body]
41
+ fallback_whens = []
42
+ opt_cases = []
43
+ while cur_when.first == :when
44
+ ensure_node_type(head = cur_when.last[:head], :array)
45
+ cases = head.last.map { |wn| fixed_immediate?(wn) }
46
+ break unless cases.all?
47
+ case_c_code = cases.map { |c| "case #{c}:" }.join("\n")
48
+ fixnum_whens = head.last.select { |wn| fixnum?(wn) }
49
+ unless fixnum_whens.empty?
50
+ goto_label = compiler.un("case_opt_label")
51
+ case_c_code << "\n#{goto_label}:"
52
+ fallback_whens << [:when, {
53
+ :body => "Qnil;\ngoto #{goto_label}", # TODO: evil, depends on impl. details of comp_case/handle_when
54
+ :head => [:array, fixnum_whens]
55
+ }]
56
+ end
57
+ opt_cases << [case_c_code, cur_when.last[:body]]
58
+ cur_when = cur_when.last[:next] || [:nil, {}]
59
+ end
60
+ return nil if opt_cases.empty? # nothing to optimize
61
+ rest = cur_when
62
+ if rest.first == :when # some whens are left construct a complete new case node
63
+ rest = [:case, {:head => "case_opt_val", :body => rest}]
64
+ end
65
+ cfun.instance_eval {
66
+ c_scope_res {
67
+ l "VALUE case_opt_val;"
68
+ l "case_opt_val = #{comp(hash[:head])};"
69
+ l "switch (case_opt_val) {"
70
+ opt_cases.each { |(case_code, body_node)|
71
+ l case_code
72
+ assign_res(comp(body_node))
73
+ l "break;"
74
+ }
75
+ l "default:"
76
+ if fallback_whens.empty?
77
+ assign_res(comp(rest))
78
+ else
79
+ # link the fallback_whens
80
+ fallback_whens.each_with_index { |wn, i|
81
+ fallback_whens[i - 1].last[:next] = wn if i > 0
82
+ }
83
+ fallback_whens.last.last[:next] = "Qundef"
84
+ c_if("!FIXNUM_P(case_opt_val)") {
85
+ assign_res(comp_case({:head => "case_opt_val", :body => fallback_whens.first}))
86
+ }
87
+ c_else {
88
+ assign_res("Qundef")
89
+ }
90
+ c_if("res == Qundef") {
91
+ assign_res(comp(rest))
92
+ }
93
+ end
94
+ l "}"
95
+ "res"
96
+ }
97
+ }
98
+ end
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,36 @@
1
+
2
+ module Ruby2CExtension::Plugins
3
+
4
+ class ConstCache < Ruby2CExtension::Plugin
5
+ def initialize(compiler)
6
+ super
7
+ compiler.add_preprocessor(:const) { |cfun, node|
8
+ cfun.instance_eval {
9
+ c_static_once {
10
+ comp_const(node.last)
11
+ }
12
+ }
13
+ }
14
+ compiler.add_preprocessor(:colon2) { |cfun, node|
15
+ mid = node.last[:mid]
16
+ if mid.to_s[0,1].downcase != mid.to_s[0,1] # then it is a constant
17
+ cfun.instance_eval {
18
+ c_static_once {
19
+ comp_colon2(node.last)
20
+ }
21
+ }
22
+ else
23
+ node
24
+ end
25
+ }
26
+ compiler.add_preprocessor(:colon3) { |cfun, node|
27
+ cfun.instance_eval {
28
+ c_static_once {
29
+ comp_colon3(node.last)
30
+ }
31
+ }
32
+ }
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,70 @@
1
+ # appears to only optimize calls
2
+ # to self that have no parameters?
3
+ # or possibly matching parameters?
4
+
5
+ module Ruby2CExtension::Plugins
6
+
7
+ class DirectSelfCall < Ruby2CExtension::Plugin
8
+
9
+ include Util
10
+ @@only_private = true
11
+ def DirectSelfCall.allow_public_methods
12
+ @@only_private = false
13
+ end
14
+
15
+ def call(cfun, node)
16
+ hash = node.last
17
+ recv = hash[:recv]
18
+ return node if recv && (!(Array === recv) || !recv.first.equal?(:self))
19
+ mid = hash[:mid]
20
+ name = @ruby2c_method[[@scope,mid]]
21
+ return node unless name
22
+ args = hash[:args] || [:array, []]
23
+ if Array == args and args.first.equal?(:array) and args.last.empty?
24
+ "#{name}(0, 0, #{cfun.get_self})"
25
+ else
26
+ cfun.c_scope_res {
27
+ cfun.build_args(args)
28
+ "#{name}(argc, argv, #{cfun.get_self})"
29
+ }
30
+ end
31
+ end
32
+
33
+ def initialize(compiler)
34
+ super
35
+ @ruby2c_method = {}
36
+ @scope = nil
37
+ compiler.add_preprocessor(:defn) { |cfun, node|
38
+ scope0 = @scope
39
+ hash = node.last
40
+ mid = hash[:mid]
41
+ @scope = cfun
42
+ name0 = "\0#{cfun.__id__}\0#{mid}\0"
43
+ name = name0.clone
44
+ @ruby2c_method[[cfun,mid]] = name
45
+ _add_fun = compiler.method(:add_fun)
46
+ klass = (class << compiler;self;end)
47
+ klass.send(:define_method, :add_fun) { |code, base_name|
48
+ name.replace(_add_fun[code, base_name])
49
+ code.gsub!(name0, name)
50
+ name.clone
51
+ }
52
+ ret = cfun.comp_defn(hash)
53
+ if @@only_private
54
+ unless cfun.scope.vmode.equal?(:private)
55
+ @ruby2c_method.delete([cfun,mid]) # pretend we never even saw this method...
56
+ end
57
+ end
58
+ @scope = scope0
59
+ klass.send(:define_method, :add_fun, _add_fun.unbind)
60
+ ret
61
+ }
62
+ compiler.add_preprocessor(:call, &method(:call))
63
+ compiler.add_preprocessor(:vcall, &method(:call))
64
+ compiler.add_preprocessor(:fcall, &method(:call))
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+