crystalizer 0.2.2

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 (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
+