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.
- data/Changelog +27 -0
- data/README +79 -0
- data/Rakefile +24 -0
- data/TODO +14 -0
- data/VERSION +1 -0
- data/benchmarks/bench.rb +129 -0
- data/benchmarks/concretize_test.rb +26 -0
- data/benchmarks/extconf.rb +10 -0
- data/benchmarks/tak_rb.rb +7 -0
- data/benchmarks/tak_so.rb +7 -0
- data/benchmarks/tak_source.rb +16 -0
- data/bin/rb2cx +162 -0
- data/doc/eval2c.txt +246 -0
- data/doc/gen_html.rb +26 -0
- data/doc/html_template +10 -0
- data/doc/index.txt +169 -0
- data/doc/limitations.txt +529 -0
- data/doc/optimizations.txt +185 -0
- data/doc/rb2cx.txt +130 -0
- data/doc/style.css +27 -0
- data/lib/concretizer.rb +3 -0
- data/lib/ruby2cext/c_function.rb +617 -0
- data/lib/ruby2cext/common_node_comp.rb +1412 -0
- data/lib/ruby2cext/compiler.rb +311 -0
- data/lib/ruby2cext/concretize.rb +269 -0
- data/lib/ruby2cext/error.rb +15 -0
- data/lib/ruby2cext/eval2c.rb +126 -0
- data/lib/ruby2cext/parser.rb +36 -0
- data/lib/ruby2cext/plugin.rb +24 -0
- data/lib/ruby2cext/plugins/builtin_methods.rb +817 -0
- data/lib/ruby2cext/plugins/cache_call.rb +293 -0
- data/lib/ruby2cext/plugins/case_optimize.rb +102 -0
- data/lib/ruby2cext/plugins/const_cache.rb +36 -0
- data/lib/ruby2cext/plugins/direct_self_call.rb +70 -0
- data/lib/ruby2cext/plugins/inline_builtin.rb +797 -0
- data/lib/ruby2cext/plugins/inline_methods.rb +68 -0
- data/lib/ruby2cext/plugins/ivar_cache.rb +147 -0
- data/lib/ruby2cext/plugins/require_include.rb +69 -0
- data/lib/ruby2cext/plugins/util.rb +154 -0
- data/lib/ruby2cext/plugins/warnings.rb +121 -0
- data/lib/ruby2cext/scopes.rb +225 -0
- data/lib/ruby2cext/str_to_c_strlit.rb +12 -0
- data/lib/ruby2cext/tools.rb +80 -0
- data/lib/ruby2cext/version.rb +22 -0
- data/results +68 -0
- data/setup.rb +1585 -0
- data/stuff/builtin_methods.rb +69 -0
- data/stuff/builtin_methods_test.rb +37 -0
- data/test/bootstrap.rb +10 -0
- data/test/causes_crash_all_opts.rb +1165 -0
- data/test/eval2c/test_eval2c.rb +37 -0
- data/test/temp_17.rb +16 -0
- data/test/temp_18.rb +8 -0
- data/test/temp_19.rb +8 -0
- data/test/temp_2.rb +7 -0
- data/test/temp_20.rb +5 -0
- data/test/temp_21.rb +161 -0
- data/test/temp_22.rb +7 -0
- data/test/temp_23.rb +7 -0
- data/test/temp_24.rb +219 -0
- data/test/temp_25.rb +7 -0
- data/test/temp_26.rb +11 -0
- data/test/temp_27.rb +11 -0
- data/test/temp_28.rb +9 -0
- data/test/temp_29.rb +9 -0
- data/test/temp_3.rb +0 -0
- data/test/temp_30.rb +0 -0
- data/test/temp_31.rb +10 -0
- data/test/temp_32.rb +10 -0
- data/test/temp_33.rb +15 -0
- data/test/temp_34.rb +15 -0
- data/test/temp_35.rb +7 -0
- data/test/temp_36.rb +7 -0
- data/test/temp_37.rb +10 -0
- data/test/temp_38.rb +10 -0
- data/test/temp_39.rb +0 -0
- data/test/temp_4.rb +7 -0
- data/test/temp_40.rb +50 -0
- data/test/temp_41.rb +50 -0
- data/test/temp_42.rb +8 -0
- data/test/temp_43.rb +8 -0
- data/test/temp_44.rb +0 -0
- data/test/temp_48.rb +7 -0
- data/test/temp_49.rb +7 -0
- data/test/temp_5.rb +7 -0
- data/test/temp_59.rb +7 -0
- data/test/temp_6.rb +7 -0
- data/test/temp_60.rb +7 -0
- data/test/temp_68.rb +239 -0
- data/test/temp_7.rb +7 -0
- data/test/temp_70.rb +7 -0
- data/test/temp_71.rb +7 -0
- data/test/temp_72.rb +13 -0
- data/test/temp_73.rb +7 -0
- data/test/temp_74.rb +7 -0
- data/test/temp_76.rb +7 -0
- data/test/temp_77.rb +13 -0
- data/test/temp_79.rb +7 -0
- data/test/temp_8.rb +14 -0
- data/test/temp_81.rb +14 -0
- data/test/temp_83.rb +0 -0
- data/test/temp_84.rb +7 -0
- data/test/temp_85.rb +7 -0
- data/test/temp_86.rb +14 -0
- data/test/temp_87.rb +7 -0
- data/test/temp_88.rb +7 -0
- data/test/temp_89.rb +7 -0
- data/test/temp_9.rb +14 -0
- data/test/temp_90.rb +0 -0
- data/test/temp_91.rb +7 -0
- data/test/temp_92.rb +7 -0
- data/test/temp_93.rb +7 -0
- data/test/temp_94.rb +7 -0
- data/test/temp_95.rb +7 -0
- data/test/temp_96.rb +0 -0
- data/test/temp_97.rb +0 -0
- data/test/temp_98.rb +7 -0
- data/test/temp_99.rb +7 -0
- data/test/test_concretize.rb +132 -0
- data/test/test_concretize_all.rb +15 -0
- data/test/test_crystalize_block.rb +73 -0
- data/test/test_files/test.rb +615 -0
- data/test/test_files/vmode_test.rb +73 -0
- data/test/test_files/warn_test.rb +35 -0
- data/test/test_syntax.rb +25 -0
- 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
|
+
|