cast_off 0.2.0
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/README +578 -0
- data/README.en +256 -0
- data/bin/CastOff +145 -0
- data/cast_off.gemspec +25 -0
- data/ext/cast_off/cast_off.c.rb +1386 -0
- data/ext/cast_off/cast_off.h +24 -0
- data/ext/cast_off/depend +70 -0
- data/ext/cast_off/extconf.rb +19 -0
- data/ext/cast_off/generated_c_include/inline_api.h +507 -0
- data/ext/cast_off/generated_c_include/iter_api.h +595 -0
- data/ext/cast_off/generated_c_include/unbox_api.h.rb +76 -0
- data/ext/cast_off/generated_c_include/vm_api.h +751 -0
- data/ext/cast_off/ruby_source/atomic.h +56 -0
- data/ext/cast_off/ruby_source/constant.h +34 -0
- data/ext/cast_off/ruby_source/debug.h +41 -0
- data/ext/cast_off/ruby_source/eval_intern.h +234 -0
- data/ext/cast_off/ruby_source/gc.h +98 -0
- data/ext/cast_off/ruby_source/id.h +175 -0
- data/ext/cast_off/ruby_source/insns.inc +179 -0
- data/ext/cast_off/ruby_source/insns_info.inc +695 -0
- data/ext/cast_off/ruby_source/internal.h +227 -0
- data/ext/cast_off/ruby_source/iseq.h +125 -0
- data/ext/cast_off/ruby_source/manual_update.h +135 -0
- data/ext/cast_off/ruby_source/method.h +105 -0
- data/ext/cast_off/ruby_source/node.h +503 -0
- data/ext/cast_off/ruby_source/thread_pthread.h +51 -0
- data/ext/cast_off/ruby_source/thread_win32.h +40 -0
- data/ext/cast_off/ruby_source/vm_core.h +756 -0
- data/ext/cast_off/ruby_source/vm_exec.h +184 -0
- data/ext/cast_off/ruby_source/vm_insnhelper.c +1748 -0
- data/ext/cast_off/ruby_source/vm_insnhelper.h +220 -0
- data/ext/cast_off/ruby_source/vm_opts.h +51 -0
- data/lib/cast_off.rb +15 -0
- data/lib/cast_off/compile.rb +629 -0
- data/lib/cast_off/compile/basicblock.rb +144 -0
- data/lib/cast_off/compile/cfg.rb +391 -0
- data/lib/cast_off/compile/code_manager.rb +284 -0
- data/lib/cast_off/compile/configuration.rb +2368 -0
- data/lib/cast_off/compile/dependency.rb +240 -0
- data/lib/cast_off/compile/information.rb +775 -0
- data/lib/cast_off/compile/instruction.rb +446 -0
- data/lib/cast_off/compile/ir/call_ir.rb +2348 -0
- data/lib/cast_off/compile/ir/guard_ir.rb +423 -0
- data/lib/cast_off/compile/ir/jump_ir.rb +223 -0
- data/lib/cast_off/compile/ir/operand.rb +934 -0
- data/lib/cast_off/compile/ir/param_ir.rb +98 -0
- data/lib/cast_off/compile/ir/return_ir.rb +92 -0
- data/lib/cast_off/compile/ir/simple_ir.rb +808 -0
- data/lib/cast_off/compile/ir/sub_ir.rb +212 -0
- data/lib/cast_off/compile/iseq.rb +454 -0
- data/lib/cast_off/compile/method_information.rb +1384 -0
- data/lib/cast_off/compile/namespace/namespace.rb +556 -0
- data/lib/cast_off/compile/namespace/uuid.rb +323 -0
- data/lib/cast_off/compile/stack.rb +65 -0
- data/lib/cast_off/compile/translator.rb +1562 -0
- data/lib/cast_off/suggestion.rb +98 -0
- data/lib/cast_off/util.rb +58 -0
- metadata +107 -0
@@ -0,0 +1,446 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
|
3
|
+
module CastOff::Compiler
|
4
|
+
module Instruction
|
5
|
+
extend CastOff::Util
|
6
|
+
|
7
|
+
VM_CALL_ARGS_SPLAT_BIT = (0x01 << 1)
|
8
|
+
VM_CALL_ARGS_BLOCKARG_BIT = (0x01 << 2)
|
9
|
+
VM_CALL_FCALL_BIT = (0x01 << 3)
|
10
|
+
VM_CALL_VCALL_BIT = (0x01 << 4)
|
11
|
+
VM_CALL_TAILCALL_BIT = (0x01 << 5)
|
12
|
+
VM_CALL_TAILRECURSION_BIT = (0x01 << 6)
|
13
|
+
VM_CALL_SUPER_BIT = (0x01 << 7)
|
14
|
+
VM_CALL_OPT_SEND_BIT = (0x01 << 8)
|
15
|
+
|
16
|
+
DEFINED_IVAR = 1
|
17
|
+
DEFINED_IVAR2 = 2
|
18
|
+
DEFINED_GVAR = 3
|
19
|
+
DEFINED_CVAR = 4
|
20
|
+
DEFINED_CONST = 5
|
21
|
+
DEFINED_METHOD = 6
|
22
|
+
DEFINED_YIELD = 7
|
23
|
+
DEFINED_REF = 8
|
24
|
+
DEFINED_ZSUPER = 9
|
25
|
+
DEFINED_FUNC = 10
|
26
|
+
|
27
|
+
SupportInstruction = [
|
28
|
+
:trace, :nop, :putnil, :getdynamic, :send, :leave, :putobject, :putself, :putstring, :newrange,
|
29
|
+
:newarray, :duparray, :tostring, :concatstrings, :setdynamic, :newhash, :branchunless, :branchif, :toregexp,
|
30
|
+
:adjuststack, :dup, :dupn, :setn, :pop, :jump, :opt_plus, :opt_minus, :opt_mult, :opt_div, :opt_mod, :opt_succ,
|
31
|
+
:opt_lt, :opt_le, :opt_gt, :opt_ge, :opt_neq, :opt_eq, :opt_length, :opt_size, :opt_ltlt, :opt_aref, :opt_not,
|
32
|
+
:getinlinecache, :setinlinecache, :getconstant, :swap, :topn, :concatarray, :getinstancevariable, :setinstancevariable,
|
33
|
+
:getclassvariable, :setclassvariable, :getglobal, :setglobal, :getlocal, :setlocal, :opt_case_dispatch,
|
34
|
+
:opt_regexpmatch1, :opt_regexpmatch2, :expandarray, :splatarray, :checkincludearray, :getspecial, :invokeblock,
|
35
|
+
:throw, :defined,
|
36
|
+
:cast_off_prep, :cast_off_loop, :cast_off_cont, :cast_off_finl,
|
37
|
+
:cast_off_getlvar, :cast_off_setlvar, :cast_off_getivar, :cast_off_setivar,
|
38
|
+
:cast_off_getcvar, :cast_off_setcvar, :cast_off_getgvar, :cast_off_setgvar,
|
39
|
+
:cast_off_handle_optional_args, :cast_off_fetch_args, :cast_off_decl_arg, :cast_off_decl_var,
|
40
|
+
:cast_off_getconst, :cast_off_enter_block, :cast_off_leave_block, :cast_off_continue_loop,
|
41
|
+
:cast_off_break_block,
|
42
|
+
:cast_off_getdvar, :cast_off_setdvar,
|
43
|
+
]
|
44
|
+
|
45
|
+
IgnoreInstruction = [
|
46
|
+
:trace, :setinlinecache, :nop
|
47
|
+
]
|
48
|
+
|
49
|
+
BlockSeparator = [
|
50
|
+
:leave, :throw, :branchunless, :jump, :branchif, :cast_off_handle_optional_args,
|
51
|
+
:cast_off_enter_block, :cast_off_leave_block, :cast_off_continue_loop,
|
52
|
+
:cast_off_break_block,
|
53
|
+
]
|
54
|
+
|
55
|
+
BranchInstruction = [
|
56
|
+
:branchunless, :jump, :branchif, :cast_off_handle_optional_args,
|
57
|
+
:cast_off_enter_block, :cast_off_leave_block, :cast_off_continue_loop,
|
58
|
+
:cast_off_break_block,
|
59
|
+
]
|
60
|
+
|
61
|
+
JumpOrReturnInstruction = [
|
62
|
+
:leave, :throw, :jump, :cast_off_handle_optional_args,
|
63
|
+
:cast_off_enter_block, :cast_off_leave_block,
|
64
|
+
:cast_off_break_block,
|
65
|
+
]
|
66
|
+
|
67
|
+
TypeInfoUser = [
|
68
|
+
:send, :opt_plus, :opt_minus, :opt_mult, :opt_div, :opt_mod, :opt_lt, :opt_le, :opt_gt, :opt_ge, :opt_neq, :opt_eq, :opt_ltlt, :opt_aref,
|
69
|
+
:opt_length, :opt_size, :opt_not, :opt_succ,
|
70
|
+
:putstring, :newarray, :newhash, :duparray, :tostring, :toregexp, :concatstrings, :concatarray, :newrange, :opt_regexpmatch1, :opt_regexpmatch2,
|
71
|
+
:expandarray, :splatarray, :checkincludearray, :getconstant, :getspecial,
|
72
|
+
:branchif, :branchunless,
|
73
|
+
:cast_off_prep, :cast_off_loop, :cast_off_finl, :cast_off_fetch_args, :cast_off_handle_optional_args
|
74
|
+
#:opt_case_dispatch,
|
75
|
+
]
|
76
|
+
|
77
|
+
unless (BlockSeparator - SupportInstruction).empty?
|
78
|
+
bug()
|
79
|
+
end
|
80
|
+
|
81
|
+
unless (IgnoreInstruction - SupportInstruction).empty?
|
82
|
+
bug()
|
83
|
+
end
|
84
|
+
|
85
|
+
unless (BranchInstruction - SupportInstruction).empty?
|
86
|
+
bug()
|
87
|
+
end
|
88
|
+
|
89
|
+
unless (JumpOrReturnInstruction - SupportInstruction).empty?
|
90
|
+
bug()
|
91
|
+
end
|
92
|
+
|
93
|
+
unless (TypeInfoUser - SupportInstruction).empty?
|
94
|
+
bug()
|
95
|
+
end
|
96
|
+
|
97
|
+
class InsnInfo
|
98
|
+
include CastOff::Util
|
99
|
+
attr_reader :op, :argv, :pc, :iseq, :depth, :guard_label, :line, :ic_class
|
100
|
+
|
101
|
+
# pc > 0 : standard instruction
|
102
|
+
# pc == -1: cast_off instruction
|
103
|
+
# safe = true : should not be generate guard
|
104
|
+
# safe = false: should be generate guard
|
105
|
+
def initialize(insn, iseq, pc = -1, line = -1, safe = false, depth = nil)
|
106
|
+
@op, *@argv = *insn
|
107
|
+
bug() unless iseq.is_a?(Iseq)
|
108
|
+
@iseq = iseq
|
109
|
+
@pc = pc
|
110
|
+
@ic_class = class_information_in_ic(@iseq.iseq)
|
111
|
+
@line = line
|
112
|
+
@safe = safe
|
113
|
+
if pc == -1 && !safe
|
114
|
+
bug(op) if TypeInfoUser.include?(op)
|
115
|
+
end
|
116
|
+
@depth = depth
|
117
|
+
@guard_label = "guard_#{self.hash.to_s.gsub(/-/, "_")}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def size
|
121
|
+
@argv.size + 1
|
122
|
+
end
|
123
|
+
|
124
|
+
def set_stack_depth(depth)
|
125
|
+
@depth = depth
|
126
|
+
end
|
127
|
+
|
128
|
+
def need_guard?
|
129
|
+
@safe ? false : true
|
130
|
+
end
|
131
|
+
|
132
|
+
def to_s
|
133
|
+
"line[#{@line}]: #{source()}"
|
134
|
+
end
|
135
|
+
|
136
|
+
def source()
|
137
|
+
bug() unless @iseq
|
138
|
+
src = @iseq.source
|
139
|
+
if @line > 0 && src
|
140
|
+
src = src[@line - 1]
|
141
|
+
src ? src.sub(/^[\s]+/, '').chomp : ''
|
142
|
+
else
|
143
|
+
''
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def dup()
|
148
|
+
ret = InsnInfo.new(insn(), @iseq, @pc, @line, @safe)
|
149
|
+
ret.set_stack_depth(@depth)
|
150
|
+
ret
|
151
|
+
end
|
152
|
+
|
153
|
+
def update(n_insn)
|
154
|
+
n_op, *n_argv = *n_insn
|
155
|
+
case @op
|
156
|
+
when :getdynamic, :getlocal
|
157
|
+
bug() unless n_op == :cast_off_getlvar || n_op == :cast_off_getdvar
|
158
|
+
when :setdynamic, :setlocal
|
159
|
+
bug() unless n_op == :cast_off_setlvar || n_op == :cast_off_setdvar
|
160
|
+
when :getinstancevariable
|
161
|
+
bug() unless n_op == :cast_off_getivar
|
162
|
+
when :setinstancevariable
|
163
|
+
bug() unless n_op == :cast_off_setivar
|
164
|
+
when :getclassvariable
|
165
|
+
bug() unless n_op == :cast_off_getcvar
|
166
|
+
when :setclassvariable
|
167
|
+
bug() unless n_op == :cast_off_setcvar
|
168
|
+
when :getglobal
|
169
|
+
bug() unless n_op == :cast_off_getgvar
|
170
|
+
when :setglobal
|
171
|
+
bug() unless n_op == :cast_off_setgvar
|
172
|
+
else
|
173
|
+
bug()
|
174
|
+
end
|
175
|
+
@op = n_op
|
176
|
+
@argv = n_argv
|
177
|
+
end
|
178
|
+
|
179
|
+
def get_unsupport_message()
|
180
|
+
case @op
|
181
|
+
when :defineclass
|
182
|
+
"Currently, CastOff cannot handle class definition"
|
183
|
+
when :putspecialobject, :putiseq
|
184
|
+
"Currently, CastOff cannot handle #{@op} instruction which is used for method definition"
|
185
|
+
when :invokesuper
|
186
|
+
"Currently, CastOff cannot handle super"
|
187
|
+
else
|
188
|
+
"Currently, CastOff cannot handle #{@op} instruction"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def support?
|
193
|
+
if SupportInstruction.index(@op)
|
194
|
+
case @op
|
195
|
+
when :throw
|
196
|
+
support_throw_instruction?
|
197
|
+
else
|
198
|
+
true
|
199
|
+
end
|
200
|
+
else
|
201
|
+
false
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def ignore?
|
206
|
+
IgnoreInstruction.index(@op)
|
207
|
+
end
|
208
|
+
|
209
|
+
def get_iseq()
|
210
|
+
index = get_iseq_index()
|
211
|
+
index ? @argv[index] : nil
|
212
|
+
end
|
213
|
+
|
214
|
+
def set_iseq(iseq)
|
215
|
+
index = get_iseq_index()
|
216
|
+
bug() unless index
|
217
|
+
@argv[index] = iseq
|
218
|
+
end
|
219
|
+
|
220
|
+
def get_label()
|
221
|
+
index = get_label_index()
|
222
|
+
index ? @argv[index] : nil
|
223
|
+
end
|
224
|
+
|
225
|
+
def set_label(label)
|
226
|
+
index = get_label_index()
|
227
|
+
bug() unless index
|
228
|
+
@argv[index] = label
|
229
|
+
end
|
230
|
+
|
231
|
+
def popnum()
|
232
|
+
case @op
|
233
|
+
when :leave, :throw
|
234
|
+
return 1
|
235
|
+
when :dup
|
236
|
+
return 1
|
237
|
+
when :dupn
|
238
|
+
n = @argv[0]
|
239
|
+
return n
|
240
|
+
when :setn
|
241
|
+
n = @argv[0]
|
242
|
+
return n + 1
|
243
|
+
when :topn
|
244
|
+
n = @argv[0]
|
245
|
+
return n + 1
|
246
|
+
end
|
247
|
+
begin
|
248
|
+
return instruction_popnum(insn())
|
249
|
+
rescue ArgumentError => e
|
250
|
+
case @op
|
251
|
+
when :cast_off_prep
|
252
|
+
return @argv.last.popnum()
|
253
|
+
when :cast_off_loop
|
254
|
+
return 0 # push true(continue loop) or false(not continue loop)
|
255
|
+
when :cast_off_cont
|
256
|
+
return 0
|
257
|
+
when :cast_off_finl
|
258
|
+
return 0
|
259
|
+
when :cast_off_getlvar
|
260
|
+
return 0
|
261
|
+
when :cast_off_setlvar
|
262
|
+
return 1
|
263
|
+
when :cast_off_getivar
|
264
|
+
return 0
|
265
|
+
when :cast_off_setivar
|
266
|
+
return 1
|
267
|
+
when :cast_off_getcvar
|
268
|
+
return 0
|
269
|
+
when :cast_off_setcvar
|
270
|
+
return 1
|
271
|
+
when :cast_off_getgvar
|
272
|
+
return 0
|
273
|
+
when :cast_off_setgvar
|
274
|
+
return 1
|
275
|
+
when :cast_off_getdvar
|
276
|
+
return 0
|
277
|
+
when :cast_off_setdvar
|
278
|
+
return 1
|
279
|
+
when :cast_off_handle_optional_args
|
280
|
+
return 1
|
281
|
+
when :cast_off_fetch_args
|
282
|
+
return 0
|
283
|
+
when :cast_off_decl_arg, :cast_off_decl_var
|
284
|
+
return 0
|
285
|
+
when :cast_off_getconst
|
286
|
+
return 0
|
287
|
+
when :cast_off_enter_block, :cast_off_leave_block
|
288
|
+
return 0
|
289
|
+
when :cast_off_break_block
|
290
|
+
return 1
|
291
|
+
when :cast_off_continue_loop
|
292
|
+
return 1
|
293
|
+
else
|
294
|
+
bug("unsupported instruction #{@op}, #{@argv}")
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def pushnum()
|
300
|
+
case @op
|
301
|
+
when :leave, :throw
|
302
|
+
return 0
|
303
|
+
when :dup
|
304
|
+
return 2
|
305
|
+
when :dupn
|
306
|
+
n = @argv[0]
|
307
|
+
return n * 2
|
308
|
+
when :setn
|
309
|
+
n = @argv[0]
|
310
|
+
return n + 1
|
311
|
+
when :topn
|
312
|
+
n = @argv[0]
|
313
|
+
return n + 2
|
314
|
+
end
|
315
|
+
begin
|
316
|
+
return instruction_pushnum(insn())
|
317
|
+
rescue ArgumentError => e
|
318
|
+
case @op
|
319
|
+
when :cast_off_prep
|
320
|
+
return @argv.last.pushnum()
|
321
|
+
when :cast_off_loop
|
322
|
+
return 0 # push true(continue loop) or false(not continue loop)
|
323
|
+
when :cast_off_cont
|
324
|
+
return 0
|
325
|
+
when :cast_off_finl
|
326
|
+
return 1
|
327
|
+
when :cast_off_getlvar
|
328
|
+
return 1
|
329
|
+
when :cast_off_setlvar
|
330
|
+
return 0
|
331
|
+
when :cast_off_getivar
|
332
|
+
return 1
|
333
|
+
when :cast_off_setivar
|
334
|
+
return 0
|
335
|
+
when :cast_off_getcvar
|
336
|
+
return 1
|
337
|
+
when :cast_off_setcvar
|
338
|
+
return 0
|
339
|
+
when :cast_off_getgvar
|
340
|
+
return 1
|
341
|
+
when :cast_off_setgvar
|
342
|
+
return 0
|
343
|
+
when :cast_off_getdvar
|
344
|
+
return 1
|
345
|
+
when :cast_off_setdvar
|
346
|
+
return 0
|
347
|
+
when :cast_off_handle_optional_args
|
348
|
+
return 0
|
349
|
+
when :cast_off_fetch_args
|
350
|
+
return 1
|
351
|
+
when :cast_off_decl_arg, :cast_off_decl_var
|
352
|
+
return 0
|
353
|
+
when :cast_off_getconst
|
354
|
+
return 1
|
355
|
+
when :cast_off_enter_block, :cast_off_leave_block
|
356
|
+
return 0
|
357
|
+
when :cast_off_break_block
|
358
|
+
return 1
|
359
|
+
when :cast_off_continue_loop
|
360
|
+
return 0
|
361
|
+
else
|
362
|
+
bug("unsupported instruction #{@op}, #{@argv}")
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def stack_usage()
|
368
|
+
pushnum() - popnum()
|
369
|
+
end
|
370
|
+
|
371
|
+
def get_throw_info()
|
372
|
+
bug() unless @op == :throw
|
373
|
+
# 1.9.2 and 1.9.3 compatible
|
374
|
+
throw_state = @argv[0]
|
375
|
+
state = throw_state & 0xff;
|
376
|
+
flag = throw_state & 0x8000;
|
377
|
+
level = throw_state >> 16;
|
378
|
+
case state
|
379
|
+
when THROW_TAG_RETURN
|
380
|
+
type = :return
|
381
|
+
when THROW_TAG_BREAK
|
382
|
+
type = :break
|
383
|
+
when THROW_TAG_NEXT
|
384
|
+
type = :next
|
385
|
+
when THROW_TAG_RETRY
|
386
|
+
type = :retry
|
387
|
+
when THROW_TAG_REDO
|
388
|
+
type = :redo
|
389
|
+
when THROW_TAG_RAISE
|
390
|
+
type = :raise
|
391
|
+
when THROW_TAG_THROW
|
392
|
+
type = :throw
|
393
|
+
when THROW_TAG_FATAL
|
394
|
+
type = :fatal
|
395
|
+
else
|
396
|
+
bug()
|
397
|
+
end
|
398
|
+
[type, state, flag, level]
|
399
|
+
end
|
400
|
+
|
401
|
+
private
|
402
|
+
|
403
|
+
def insn()
|
404
|
+
[@op] + @argv
|
405
|
+
end
|
406
|
+
|
407
|
+
def get_label_index()
|
408
|
+
case @op
|
409
|
+
when :jump, :branchif, :branchunless
|
410
|
+
0
|
411
|
+
when :getinlinecacge
|
412
|
+
0
|
413
|
+
when :onceinlinecache, :opt_case_dispatch
|
414
|
+
bug()
|
415
|
+
else
|
416
|
+
nil
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
def get_iseq_index()
|
421
|
+
case @op
|
422
|
+
when :send
|
423
|
+
2
|
424
|
+
when :invokesuper, :defineclass
|
425
|
+
1
|
426
|
+
when :putiseq
|
427
|
+
0
|
428
|
+
else
|
429
|
+
nil
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def support_throw_instruction?
|
434
|
+
type, state, flag, level = get_throw_info()
|
435
|
+
return false unless flag == 0
|
436
|
+
case type
|
437
|
+
when :return, :break
|
438
|
+
true
|
439
|
+
else
|
440
|
+
false
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
@@ -0,0 +1,2348 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
|
3
|
+
module CastOff
|
4
|
+
module Compiler
|
5
|
+
module SimpleIR
|
6
|
+
class CallIR < IR
|
7
|
+
include Instruction
|
8
|
+
|
9
|
+
attr_reader :argc, :return_value, :variables_without_result, :variables, :result_variable, :values
|
10
|
+
|
11
|
+
def initialize(param, argc, return_value, insn, cfg)
|
12
|
+
super(insn, cfg)
|
13
|
+
@argc = argc
|
14
|
+
bug() unless return_value.is_a?(TmpVariable)
|
15
|
+
@return_value = return_value
|
16
|
+
bug() unless param.is_a?(Array)
|
17
|
+
bug() unless param.size == @argc
|
18
|
+
@param = param
|
19
|
+
@sampling_return_value = false
|
20
|
+
@values = [@return_value]
|
21
|
+
@variables = []
|
22
|
+
@variables_without_result = []
|
23
|
+
@variables << @return_value
|
24
|
+
@result_variable = @return_value
|
25
|
+
@source = @insn.source
|
26
|
+
@source = @source.empty? ? nil : @source
|
27
|
+
@source_line = @insn.line.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def propergate_guard_usage()
|
31
|
+
bug()
|
32
|
+
end
|
33
|
+
|
34
|
+
### unboxing begin ###
|
35
|
+
def unboxing_prelude()
|
36
|
+
can_not_unbox()
|
37
|
+
end
|
38
|
+
|
39
|
+
def propergate_value_which_can_not_unbox(defs)
|
40
|
+
# nothing to do
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
def propergate_box_value(defs)
|
45
|
+
# nothing to do
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def propergate_unbox_value(defs)
|
50
|
+
return false if @return_value.can_not_unbox?
|
51
|
+
@return_value.unbox()
|
52
|
+
end
|
53
|
+
|
54
|
+
#private
|
55
|
+
|
56
|
+
def can_not_unbox()
|
57
|
+
params = param_variables()
|
58
|
+
params.each{|p| p.can_not_unbox()}
|
59
|
+
@return_value.can_not_unbox()
|
60
|
+
end
|
61
|
+
### unboxing end ###
|
62
|
+
|
63
|
+
def propergate_exact_class(defs)
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
def sampling_return_value()
|
68
|
+
@sampling_return_value = true
|
69
|
+
end
|
70
|
+
|
71
|
+
def sampling_return_value?
|
72
|
+
@sampling_return_value
|
73
|
+
end
|
74
|
+
|
75
|
+
def param_irs()
|
76
|
+
@param.dup()
|
77
|
+
end
|
78
|
+
|
79
|
+
def param_variables()
|
80
|
+
@param.map{|p| p.param_value}
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_c(params)
|
84
|
+
param = []
|
85
|
+
ret = []
|
86
|
+
@argc.times do
|
87
|
+
bug() if params.empty?
|
88
|
+
param.unshift(params.pop)
|
89
|
+
end
|
90
|
+
s = sampling_variable()
|
91
|
+
ret << s if s
|
92
|
+
bug() unless param_variables() == param
|
93
|
+
bug() unless @param.size() == @argc
|
94
|
+
ret.join("\n")
|
95
|
+
end
|
96
|
+
|
97
|
+
def type_propergation()
|
98
|
+
bug()
|
99
|
+
end
|
100
|
+
|
101
|
+
def harmless?(recv_p)
|
102
|
+
bug("#{self}, #{self.class}")
|
103
|
+
end
|
104
|
+
|
105
|
+
def side_effect?
|
106
|
+
bug()
|
107
|
+
end
|
108
|
+
|
109
|
+
def should_be_alive?
|
110
|
+
bug()
|
111
|
+
end
|
112
|
+
|
113
|
+
def mark(defs)
|
114
|
+
param = param_irs()
|
115
|
+
if should_be_alive?
|
116
|
+
if !alive?
|
117
|
+
alive()
|
118
|
+
param.each{|p| p.alive()}
|
119
|
+
true
|
120
|
+
else
|
121
|
+
false
|
122
|
+
end
|
123
|
+
else
|
124
|
+
alive? ? param.inject(false){|change, p| p.alive() || change} : false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class VMInsnIR < CallIR
|
130
|
+
attr_reader :argv
|
131
|
+
|
132
|
+
def initialize(param, argc, return_value, insn, cfg)
|
133
|
+
bug() unless insn.is_a?(InsnInfo)
|
134
|
+
super(param, argc, return_value, insn, cfg)
|
135
|
+
@opecode = insn.op
|
136
|
+
@operands = insn.argv
|
137
|
+
end
|
138
|
+
|
139
|
+
def propergate_guard_usage()
|
140
|
+
bug()
|
141
|
+
end
|
142
|
+
|
143
|
+
def to_c(params)
|
144
|
+
case @opecode
|
145
|
+
when :putstring, :newarray, :newhash, :duparray, :tostring, :toregexp, :concatstrings, :concatarray, \
|
146
|
+
:newrange, :opt_regexpmatch1, :opt_regexpmatch2, :getconstant, :getspecial, :expandarray_pre, \
|
147
|
+
:expandarray_loop, :expandarray_splat, :expandarray_post_loop, :expandarray_post_splat, :splatarray, \
|
148
|
+
:checkincludearray_pre, :checkincludearray_case, :checkincludearray_when, :cast_off_fetch_args, :defined
|
149
|
+
# nothing to do
|
150
|
+
else
|
151
|
+
bug("unexpected instruction #{@opecode}")
|
152
|
+
end
|
153
|
+
super(params)
|
154
|
+
end
|
155
|
+
|
156
|
+
def dont_duplicate_if_harmless(obj)
|
157
|
+
return nil if @configuration.force_duplicate_literal?
|
158
|
+
usage = get_usage()
|
159
|
+
harmless = true
|
160
|
+
if usage[:escape]
|
161
|
+
harmless = false
|
162
|
+
else
|
163
|
+
usage.each do |(u, recv_p)|
|
164
|
+
u_recv, u_ir = u
|
165
|
+
unless u_ir.harmless?(recv_p)
|
166
|
+
harmless = false
|
167
|
+
break
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
if harmless
|
172
|
+
return " #{@return_value} = #{obj};"
|
173
|
+
else
|
174
|
+
if @configuration.development?
|
175
|
+
if usage[:escape]
|
176
|
+
u = usage[:escape]
|
177
|
+
msg = "escape to #{u.is_a?(ReturnIR) ? 'caller' : 'pointer'}"
|
178
|
+
else
|
179
|
+
msg = usage.keys.map {|(u_recv, u_ir)|
|
180
|
+
bug(u_ir) unless u_ir.is_a?(CallIR)
|
181
|
+
if u_ir.dispatch_method?
|
182
|
+
"used by #{u_ir.to_verbose_string}"
|
183
|
+
else
|
184
|
+
s = u_ir.insn.source
|
185
|
+
s.empty? ? nil : "used by #{s}"
|
186
|
+
end
|
187
|
+
}.compact.join("\n")
|
188
|
+
end
|
189
|
+
@translator.add_literal_suggestion([get_definition_str(obj), msg, @source_line, @source]) if @source
|
190
|
+
end
|
191
|
+
return nil
|
192
|
+
end
|
193
|
+
bug()
|
194
|
+
end
|
195
|
+
|
196
|
+
def type_propergation()
|
197
|
+
super()
|
198
|
+
end
|
199
|
+
|
200
|
+
def side_effect?
|
201
|
+
bug()
|
202
|
+
end
|
203
|
+
|
204
|
+
def should_be_alive?
|
205
|
+
side_effect?
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class Putstring < VMInsnIR
|
210
|
+
def initialize(param, argc, return_value, insn, cfg)
|
211
|
+
super(param, argc, return_value, insn, cfg)
|
212
|
+
bug() unless @argc == 1
|
213
|
+
bug() unless @param[0].param_value.is_a?(Literal)
|
214
|
+
end
|
215
|
+
|
216
|
+
def propergate_guard_usage()
|
217
|
+
params = param_irs()
|
218
|
+
params.each{|p| p.need_guard(false)}
|
219
|
+
end
|
220
|
+
|
221
|
+
def propergate_exact_class(defs)
|
222
|
+
@return_value.class_exact? ? false : @return_value.is_class_exact()
|
223
|
+
end
|
224
|
+
|
225
|
+
def to_c(params)
|
226
|
+
ret = []
|
227
|
+
ret << super(params)
|
228
|
+
param = param_variables()
|
229
|
+
str = param.shift
|
230
|
+
bug() unless str.is_a?(Literal)
|
231
|
+
code = dont_duplicate_if_harmless(str)
|
232
|
+
code = " #{@return_value} = rb_str_resurrect(#{str});" unless code
|
233
|
+
ret << code
|
234
|
+
bug() unless param.empty?
|
235
|
+
ret.join("\n")
|
236
|
+
end
|
237
|
+
|
238
|
+
def type_propergation(defs)
|
239
|
+
@return_value.is_static([String])
|
240
|
+
end
|
241
|
+
|
242
|
+
def harmless?(recv_p)
|
243
|
+
true
|
244
|
+
end
|
245
|
+
|
246
|
+
def side_effect?
|
247
|
+
false
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
class Newarray < VMInsnIR
|
252
|
+
def initialize(param, argc, return_value, insn, cfg)
|
253
|
+
super(param, argc, return_value, insn, cfg)
|
254
|
+
end
|
255
|
+
|
256
|
+
def propergate_guard_usage()
|
257
|
+
params = param_irs()
|
258
|
+
params.each{|p| p.need_guard(false)}
|
259
|
+
end
|
260
|
+
|
261
|
+
### unboxing begin ###
|
262
|
+
def unboxing_prelude()
|
263
|
+
params = param_variables()
|
264
|
+
params.each{|p| p.can_not_unbox()}
|
265
|
+
@return_value.can_unbox()
|
266
|
+
end
|
267
|
+
### unboxing end ###
|
268
|
+
|
269
|
+
def propergate_exact_class(defs)
|
270
|
+
@return_value.class_exact? ? false : @return_value.is_class_exact()
|
271
|
+
end
|
272
|
+
|
273
|
+
def to_c(params)
|
274
|
+
ret = []
|
275
|
+
ret << super(params)
|
276
|
+
param = param_variables()
|
277
|
+
if @argc == 0
|
278
|
+
ret << " #{@return_value} = rb_ary_new2(0);"
|
279
|
+
else
|
280
|
+
c_ary = @insn.iseq.use_temporary_c_ary(@argc)
|
281
|
+
@argc.times{|i| ret << " #{c_ary}[#{i}] = #{param.shift};"}
|
282
|
+
ret << " #{@return_value} = rb_ary_new4((long)#{@argc}, #{c_ary});"
|
283
|
+
end
|
284
|
+
bug() unless param.empty?
|
285
|
+
ret.join("\n")
|
286
|
+
end
|
287
|
+
|
288
|
+
def type_propergation(defs)
|
289
|
+
@return_value.is_static([Array])
|
290
|
+
end
|
291
|
+
|
292
|
+
def harmless?(recv_p)
|
293
|
+
false
|
294
|
+
end
|
295
|
+
|
296
|
+
def side_effect?
|
297
|
+
false
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
class Newhash < VMInsnIR
|
302
|
+
def initialize(param, argc, return_value, insn, cfg)
|
303
|
+
super(param, argc, return_value, insn, cfg)
|
304
|
+
end
|
305
|
+
|
306
|
+
def propergate_guard_usage()
|
307
|
+
params = param_irs()
|
308
|
+
params.each{|p| p.need_guard(false)}
|
309
|
+
end
|
310
|
+
|
311
|
+
def propergate_exact_class(defs)
|
312
|
+
@return_value.class_exact? ? false : @return_value.is_class_exact()
|
313
|
+
end
|
314
|
+
|
315
|
+
def to_c(params)
|
316
|
+
ret = []
|
317
|
+
ret << super(params)
|
318
|
+
param = param_variables()
|
319
|
+
ret << " cast_off_tmp = rb_hash_new();"
|
320
|
+
bug() unless @argc % 2 == 0
|
321
|
+
(@argc / 2).times{ ret << " rb_hash_aset(cast_off_tmp, #{param.shift}, #{param.shift});"}
|
322
|
+
ret << " #{@return_value} = cast_off_tmp;"
|
323
|
+
bug() unless param.empty?
|
324
|
+
ret.join("\n")
|
325
|
+
end
|
326
|
+
|
327
|
+
def type_propergation(defs)
|
328
|
+
@return_value.is_static([Hash])
|
329
|
+
end
|
330
|
+
|
331
|
+
def harmless?(recv_p)
|
332
|
+
false
|
333
|
+
end
|
334
|
+
|
335
|
+
def side_effect?
|
336
|
+
false
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
class Duparray < VMInsnIR
|
341
|
+
def initialize(param, argc, return_value, insn, cfg)
|
342
|
+
super(param, argc, return_value, insn, cfg)
|
343
|
+
bug() unless @argc == 1
|
344
|
+
bug() unless @param[0].param_value.is_a?(Literal)
|
345
|
+
end
|
346
|
+
|
347
|
+
def propergate_guard_usage()
|
348
|
+
params = param_irs()
|
349
|
+
params.each{|p| p.need_guard(false)}
|
350
|
+
end
|
351
|
+
|
352
|
+
def propergate_exact_class(defs)
|
353
|
+
@return_value.class_exact? ? false : @return_value.is_class_exact()
|
354
|
+
end
|
355
|
+
|
356
|
+
def to_c(params)
|
357
|
+
ret = []
|
358
|
+
ret << super(params)
|
359
|
+
param = param_variables()
|
360
|
+
ary = param.shift
|
361
|
+
bug() unless ary.is_a?(Literal)
|
362
|
+
code = dont_duplicate_if_harmless(ary)
|
363
|
+
code = " #{@return_value} = rb_ary_resurrect(#{ary});" unless code
|
364
|
+
ret << code
|
365
|
+
bug() unless param.empty?
|
366
|
+
ret.join("\n")
|
367
|
+
end
|
368
|
+
|
369
|
+
def type_propergation(defs)
|
370
|
+
@return_value.is_static([Array])
|
371
|
+
end
|
372
|
+
|
373
|
+
def side_effect?
|
374
|
+
false
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
class Tostring < VMInsnIR
|
379
|
+
def initialize(param, argc, return_value, insn, cfg)
|
380
|
+
super(param, argc, return_value, insn, cfg)
|
381
|
+
bug() unless @param.size() == 1
|
382
|
+
end
|
383
|
+
|
384
|
+
def propergate_guard_usage()
|
385
|
+
params = param_irs()
|
386
|
+
params.each{|p| p.need_guard(true)}
|
387
|
+
end
|
388
|
+
|
389
|
+
def propergate_exact_class(defs)
|
390
|
+
@return_value.class_exact? ? false : @return_value.is_class_exact()
|
391
|
+
end
|
392
|
+
|
393
|
+
def to_c(params)
|
394
|
+
ret = []
|
395
|
+
ret << super(params)
|
396
|
+
param = param_variables()
|
397
|
+
obj = param.shift
|
398
|
+
if obj.is_just?(String)
|
399
|
+
ret << " #{@return_value} = #{obj};"
|
400
|
+
else
|
401
|
+
ret << " #{@return_value} = rb_obj_as_string(#{obj});"
|
402
|
+
end
|
403
|
+
bug() unless param.empty?
|
404
|
+
ret.join("\n")
|
405
|
+
end
|
406
|
+
|
407
|
+
def type_propergation(defs)
|
408
|
+
@return_value.is_static([String])
|
409
|
+
end
|
410
|
+
|
411
|
+
def harmless?(recv_p)
|
412
|
+
@param[0].param_value.is_just?(String)
|
413
|
+
end
|
414
|
+
|
415
|
+
def side_effect?
|
416
|
+
# FIXME return false when reciever class to_s method is no-side-effect.
|
417
|
+
true
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
class Toregexp < VMInsnIR
|
422
|
+
def initialize(param, argc, return_value, insn, cfg)
|
423
|
+
super(param, argc, return_value, insn, cfg)
|
424
|
+
end
|
425
|
+
|
426
|
+
def propergate_guard_usage()
|
427
|
+
params = param_irs()
|
428
|
+
params.each{|p| p.need_guard(false)}
|
429
|
+
end
|
430
|
+
|
431
|
+
def propergate_exact_class(defs)
|
432
|
+
@return_value.class_exact? ? false : @return_value.is_class_exact()
|
433
|
+
end
|
434
|
+
|
435
|
+
def to_c(params)
|
436
|
+
ret = []
|
437
|
+
ret << super(params)
|
438
|
+
param = param_variables()
|
439
|
+
# ruby doesn't export rb_reg_new_ary, so use rb_str_new, rb_str_append, and rb_reg_new_str
|
440
|
+
opt = @operands[0]
|
441
|
+
base = param.shift
|
442
|
+
bug() unless base.class_exact? && base.is_just?(String)
|
443
|
+
ret << " cast_off_tmp = rb_str_dup(#{base});"
|
444
|
+
until param.empty?
|
445
|
+
ret << " rb_str_append(cast_off_tmp, #{param.shift});"
|
446
|
+
end
|
447
|
+
ret << " #{@return_value} = rb_reg_new_str(cast_off_tmp, (int)#{opt});"
|
448
|
+
bug() unless param.empty?
|
449
|
+
ret.join("\n")
|
450
|
+
end
|
451
|
+
|
452
|
+
def type_propergation(defs)
|
453
|
+
@return_value.is_static([Regexp])
|
454
|
+
end
|
455
|
+
|
456
|
+
def harmless?(recv_p)
|
457
|
+
true
|
458
|
+
end
|
459
|
+
|
460
|
+
def side_effect?
|
461
|
+
true
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
class Concatstrings < VMInsnIR
|
466
|
+
def initialize(param, argc, return_value, insn, cfg)
|
467
|
+
super(param, argc, return_value, insn, cfg)
|
468
|
+
end
|
469
|
+
|
470
|
+
def propergate_guard_usage()
|
471
|
+
params = param_irs()
|
472
|
+
params.each{|p| p.need_guard(false)}
|
473
|
+
end
|
474
|
+
|
475
|
+
def propergate_exact_class(defs)
|
476
|
+
@return_value.class_exact? ? false : @return_value.is_class_exact()
|
477
|
+
end
|
478
|
+
|
479
|
+
def to_c(params)
|
480
|
+
ret = []
|
481
|
+
ret << super(params)
|
482
|
+
param = param_variables()
|
483
|
+
ret << " cast_off_tmp = rb_str_new(0, 0);"
|
484
|
+
@argc.times{ret << " rb_str_append(cast_off_tmp, #{param.shift});"}
|
485
|
+
ret << " #{@return_value} = cast_off_tmp;"
|
486
|
+
bug() unless param.empty?
|
487
|
+
ret.join("\n")
|
488
|
+
end
|
489
|
+
|
490
|
+
def type_propergation(defs)
|
491
|
+
@return_value.is_static([String])
|
492
|
+
end
|
493
|
+
|
494
|
+
def harmless?(recv_p)
|
495
|
+
true
|
496
|
+
end
|
497
|
+
|
498
|
+
def side_effect?
|
499
|
+
false
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
class Concatarray < VMInsnIR
|
504
|
+
def initialize(param, argc, return_value, insn, cfg)
|
505
|
+
super(param, argc, return_value, insn, cfg)
|
506
|
+
end
|
507
|
+
|
508
|
+
def propergate_guard_usage()
|
509
|
+
params = param_irs()
|
510
|
+
params.each{|p| p.need_guard(false)}
|
511
|
+
end
|
512
|
+
|
513
|
+
def propergate_exact_class(defs)
|
514
|
+
@return_value.class_exact? ? false : @return_value.is_class_exact()
|
515
|
+
end
|
516
|
+
|
517
|
+
def to_c(params)
|
518
|
+
ret = []
|
519
|
+
ret << super(params)
|
520
|
+
param = param_variables()
|
521
|
+
ary1 = param.shift
|
522
|
+
ary2 = param.shift
|
523
|
+
# FIXME C のヘッダに記述
|
524
|
+
c_ary = @insn.iseq.use_temporary_c_ary(2)
|
525
|
+
ret << <<-EOS
|
526
|
+
#{c_ary}[0] = rb_check_convert_type(#{ary1}, T_ARRAY, "Array", "to_a");
|
527
|
+
#{c_ary}[1] = rb_check_convert_type(#{ary2}, T_ARRAY, "Array", "to_a");
|
528
|
+
if (NIL_P(#{c_ary}[0])) #{c_ary}[0] = rb_ary_new3(1, #{ary1});
|
529
|
+
if (NIL_P(#{c_ary}[1])) #{c_ary}[1] = rb_ary_new3(1, #{ary2});
|
530
|
+
if (#{c_ary}[0] == #{ary1}) #{c_ary}[0] = rb_ary_dup(#{ary1});
|
531
|
+
#{@return_value} = rb_ary_concat(#{c_ary}[0], #{c_ary}[1]);
|
532
|
+
EOS
|
533
|
+
bug() unless param.empty?
|
534
|
+
ret.join("\n")
|
535
|
+
end
|
536
|
+
|
537
|
+
def type_propergation(defs)
|
538
|
+
@return_value.is_static([Array])
|
539
|
+
end
|
540
|
+
|
541
|
+
def harmless?(recv_p)
|
542
|
+
false # can be call to_a method
|
543
|
+
end
|
544
|
+
|
545
|
+
def side_effect?
|
546
|
+
true
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
class Newrange < VMInsnIR
|
551
|
+
def initialize(param, argc, return_value, insn, cfg)
|
552
|
+
super(param, argc, return_value, insn, cfg)
|
553
|
+
end
|
554
|
+
|
555
|
+
def propergate_guard_usage()
|
556
|
+
params = param_irs()
|
557
|
+
params.each{|p| p.need_guard(false)}
|
558
|
+
end
|
559
|
+
|
560
|
+
def propergate_exact_class(defs)
|
561
|
+
@return_value.class_exact? ? false : @return_value.is_class_exact()
|
562
|
+
end
|
563
|
+
|
564
|
+
def to_c(params)
|
565
|
+
ret = []
|
566
|
+
ret << super(params)
|
567
|
+
param = param_variables()
|
568
|
+
low = param.shift
|
569
|
+
high = param.shift
|
570
|
+
ret << " #{@return_value} = rb_range_new(#{low}, #{high}, (int)#{@operands[0]});"
|
571
|
+
bug() unless param.empty?
|
572
|
+
ret.join("\n")
|
573
|
+
end
|
574
|
+
|
575
|
+
def type_propergation(defs)
|
576
|
+
@return_value.is_static([Range])
|
577
|
+
end
|
578
|
+
|
579
|
+
def harmless?(recv_p)
|
580
|
+
false
|
581
|
+
end
|
582
|
+
|
583
|
+
def side_effect?
|
584
|
+
false
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
class OptRegexpmatch1 < VMInsnIR
|
589
|
+
def initialize(param, argc, return_value, insn, cfg)
|
590
|
+
super(param, argc, return_value, insn, cfg)
|
591
|
+
end
|
592
|
+
|
593
|
+
def propergate_guard_usage()
|
594
|
+
params = param_irs()
|
595
|
+
params.each{|p| p.need_guard(false)}
|
596
|
+
end
|
597
|
+
|
598
|
+
def to_c(params)
|
599
|
+
ret = []
|
600
|
+
ret << super(params)
|
601
|
+
param = param_variables()
|
602
|
+
r = param.shift
|
603
|
+
obj = param.shift
|
604
|
+
ret << " #{@return_value} = rb_reg_match(#{r}, #{obj});"
|
605
|
+
bug() unless param.empty?
|
606
|
+
ret.join("\n")
|
607
|
+
end
|
608
|
+
|
609
|
+
def type_propergation(defs)
|
610
|
+
@return_value.is_dynamic()
|
611
|
+
end
|
612
|
+
|
613
|
+
def harmless?(recv_p)
|
614
|
+
true
|
615
|
+
end
|
616
|
+
|
617
|
+
def side_effect?
|
618
|
+
true
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
class OptRegexpmatch2 < VMInsnIR
|
623
|
+
def initialize(param, argc, return_value, insn, cfg)
|
624
|
+
super(param, argc, return_value, insn, cfg)
|
625
|
+
end
|
626
|
+
|
627
|
+
def propergate_guard_usage()
|
628
|
+
params = param_irs()
|
629
|
+
params.each{|p| p.need_guard(true)}
|
630
|
+
end
|
631
|
+
|
632
|
+
def to_c(params)
|
633
|
+
ret = []
|
634
|
+
ret << super(params)
|
635
|
+
param = param_variables()
|
636
|
+
# FIXME 型を見て string だった場合は、分岐を消す
|
637
|
+
obj2 = param.shift
|
638
|
+
obj1 = param.shift
|
639
|
+
if obj2.is_just?(String)
|
640
|
+
ret << " #{@return_value} = rb_reg_match(#{obj1}, #{obj2});"
|
641
|
+
else
|
642
|
+
id = @translator.allocate_id("=~".intern)
|
643
|
+
ret << <<-EOS
|
644
|
+
if (TYPE(#{obj2}) == T_STRING) {
|
645
|
+
#{@return_value} = rb_reg_match(#{obj1}, #{obj2});
|
646
|
+
}
|
647
|
+
else {
|
648
|
+
#{@return_value} = rb_funcall(#{obj2}, #{id}, 1, #{obj1});
|
649
|
+
}
|
650
|
+
EOS
|
651
|
+
end
|
652
|
+
bug() unless param.empty?
|
653
|
+
ret.join("\n")
|
654
|
+
end
|
655
|
+
|
656
|
+
def type_propergation(defs)
|
657
|
+
@return_value.is_dynamic()
|
658
|
+
end
|
659
|
+
|
660
|
+
def harmless?(recv_p)
|
661
|
+
true
|
662
|
+
end
|
663
|
+
|
664
|
+
def side_effect?
|
665
|
+
true
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
class Getspecial < VMInsnIR
|
670
|
+
def initialize(param, argc, return_value, insn, cfg)
|
671
|
+
super(param, argc, return_value, insn, cfg)
|
672
|
+
end
|
673
|
+
|
674
|
+
def propergate_guard_usage()
|
675
|
+
params = param_irs()
|
676
|
+
params.each{|p| p.need_guard(false)}
|
677
|
+
end
|
678
|
+
|
679
|
+
def to_c(params)
|
680
|
+
ret = []
|
681
|
+
ret << super(params)
|
682
|
+
param = param_variables()
|
683
|
+
key, type = *@operands
|
684
|
+
if type == 0
|
685
|
+
bug()
|
686
|
+
else
|
687
|
+
if @configuration.allow_builtin_variable_incompatibility?
|
688
|
+
if type & 0x01 != 0
|
689
|
+
case (type >> 1).chr
|
690
|
+
when '&'
|
691
|
+
ret << " #{@return_value} = rb_reg_last_match(rb_backref_get());"
|
692
|
+
when '`'
|
693
|
+
ret << " #{@return_value} = rb_reg_match_pre(rb_backref_get());"
|
694
|
+
when '\''
|
695
|
+
ret << " #{@return_value} = rb_reg_match_post(rb_backref_get());"
|
696
|
+
when '+'
|
697
|
+
ret << " #{@return_value} = rb_reg_match_last(rb_backref_get());"
|
698
|
+
else
|
699
|
+
bug()
|
700
|
+
end
|
701
|
+
else
|
702
|
+
ret << " #{@return_value} = rb_reg_nth_match((int)(#{type >> 1}), rb_backref_get());"
|
703
|
+
end
|
704
|
+
else
|
705
|
+
raise(UnsupportedError.new(<<-EOS))
|
706
|
+
|
707
|
+
$&, $`, $\, $+, $0...$9 are incompatible.
|
708
|
+
If you want to use these variables, use CastOff.allow_builtin_variable_incompatibility(true).
|
709
|
+
EOS
|
710
|
+
end
|
711
|
+
end
|
712
|
+
bug() unless param.empty?
|
713
|
+
ret.join("\n")
|
714
|
+
end
|
715
|
+
|
716
|
+
def type_propergation(defs)
|
717
|
+
@return_value.is_dynamic()
|
718
|
+
end
|
719
|
+
|
720
|
+
def side_effect?
|
721
|
+
false
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
class ExpandarrayPre < VMInsnIR
|
726
|
+
def initialize(param, argc, return_value, insn, cfg)
|
727
|
+
super(param, argc, return_value, insn, cfg)
|
728
|
+
end
|
729
|
+
|
730
|
+
def propergate_guard_usage()
|
731
|
+
params = param_irs()
|
732
|
+
params.each{|p| p.need_guard(false)}
|
733
|
+
end
|
734
|
+
|
735
|
+
def to_c(params)
|
736
|
+
ret = []
|
737
|
+
ret << super(params)
|
738
|
+
param = param_variables()
|
739
|
+
# このチェックがなくならないことを前提に、この命令に対するガードを削除したので、
|
740
|
+
# このチェックは削除しないこと
|
741
|
+
ary = param.shift
|
742
|
+
ret << " if (TYPE(#{ary}) != T_ARRAY) #{@return_value} = rb_ary_to_ary(#{ary});"
|
743
|
+
bug() unless param.empty?
|
744
|
+
ret.join("\n")
|
745
|
+
end
|
746
|
+
|
747
|
+
def harmless?(recv_p)
|
748
|
+
false # can be return self
|
749
|
+
end
|
750
|
+
|
751
|
+
def type_propergation(defs)
|
752
|
+
@return_value.is_static([Array])
|
753
|
+
end
|
754
|
+
|
755
|
+
def side_effect?
|
756
|
+
true
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
class ExpandarrayLoop < VMInsnIR
|
761
|
+
def initialize(param, argc, return_value, insn, cfg)
|
762
|
+
super(param, argc, return_value, insn, cfg)
|
763
|
+
end
|
764
|
+
|
765
|
+
def propergate_guard_usage()
|
766
|
+
params = param_irs()
|
767
|
+
params.each{|p| p.need_guard(false)}
|
768
|
+
end
|
769
|
+
|
770
|
+
def to_c(params)
|
771
|
+
ret = []
|
772
|
+
ret << super(params)
|
773
|
+
param = param_variables()
|
774
|
+
index = @operands[0]
|
775
|
+
ary = param.shift
|
776
|
+
ret << <<-EOS
|
777
|
+
if (RARRAY_LEN(#{ary}) > #{index}) {
|
778
|
+
#{@return_value} = RARRAY_PTR(#{ary})[#{index}];
|
779
|
+
} else {
|
780
|
+
#{@return_value} = Qnil;
|
781
|
+
}
|
782
|
+
EOS
|
783
|
+
bug() unless param.empty?
|
784
|
+
ret.join("\n")
|
785
|
+
end
|
786
|
+
|
787
|
+
def type_propergation(defs)
|
788
|
+
@return_value.is_dynamic()
|
789
|
+
end
|
790
|
+
|
791
|
+
def side_effect?
|
792
|
+
false
|
793
|
+
end
|
794
|
+
end
|
795
|
+
|
796
|
+
class ExpandarraySplat < VMInsnIR
|
797
|
+
def initialize(param, argc, return_value, insn, cfg)
|
798
|
+
super(param, argc, return_value, insn, cfg)
|
799
|
+
end
|
800
|
+
|
801
|
+
def propergate_guard_usage()
|
802
|
+
params = param_irs()
|
803
|
+
params.each{|p| p.need_guard(false)}
|
804
|
+
end
|
805
|
+
|
806
|
+
def to_c(params)
|
807
|
+
ret = []
|
808
|
+
ret << super(params)
|
809
|
+
param = param_variables()
|
810
|
+
num = @operands[0]
|
811
|
+
ary = param.shift
|
812
|
+
ret << <<-EOS
|
813
|
+
if (#{num} > RARRAY_LEN(#{ary})) {
|
814
|
+
#{@return_value} = rb_ary_new();
|
815
|
+
} else {
|
816
|
+
#{@return_value} = rb_ary_new4(RARRAY_LEN(#{ary}) - #{num}, RARRAY_PTR(#{ary}) + #{num});
|
817
|
+
}
|
818
|
+
EOS
|
819
|
+
bug() unless param.empty?
|
820
|
+
ret.join("\n")
|
821
|
+
end
|
822
|
+
|
823
|
+
def type_propergation(defs)
|
824
|
+
@return_value.is_static([Array])
|
825
|
+
end
|
826
|
+
|
827
|
+
def side_effect?
|
828
|
+
false
|
829
|
+
end
|
830
|
+
end
|
831
|
+
|
832
|
+
class ExpandarrayPostLoop < VMInsnIR
|
833
|
+
def initialize(param, argc, return_value, insn, cfg)
|
834
|
+
super(param, argc, return_value, insn, cfg)
|
835
|
+
end
|
836
|
+
|
837
|
+
def propergate_guard_usage()
|
838
|
+
params = param_irs()
|
839
|
+
params.each{|p| p.need_guard(false)}
|
840
|
+
end
|
841
|
+
|
842
|
+
def to_c(params)
|
843
|
+
ret = []
|
844
|
+
ret << super(params)
|
845
|
+
param = param_variables()
|
846
|
+
num = @operands[0]
|
847
|
+
index = @operands[1]
|
848
|
+
ary = param.shift
|
849
|
+
ret << <<-EOS
|
850
|
+
if (RARRAY_LEN(#{ary}) < #{num - index}) {
|
851
|
+
#{@return_value} = Qnil;
|
852
|
+
} else {
|
853
|
+
#{@return_value} = RARRAY_PTR(#{ary})[RARRAY_LEN(#{ary}) - #{num - index - 1}];
|
854
|
+
}
|
855
|
+
EOS
|
856
|
+
bug() unless param.empty?
|
857
|
+
ret.join("\n")
|
858
|
+
end
|
859
|
+
|
860
|
+
def type_propergation(defs)
|
861
|
+
@return_value.is_dynamic()
|
862
|
+
end
|
863
|
+
|
864
|
+
def side_effect?
|
865
|
+
false
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
class ExpandarrayPostSplat < VMInsnIR
|
870
|
+
def initialize(param, argc, return_value, insn, cfg)
|
871
|
+
super(param, argc, return_value, insn, cfg)
|
872
|
+
end
|
873
|
+
|
874
|
+
def propergate_guard_usage()
|
875
|
+
params = param_irs()
|
876
|
+
params.each{|p| p.need_guard(false)}
|
877
|
+
end
|
878
|
+
|
879
|
+
def to_c(params)
|
880
|
+
ret = []
|
881
|
+
ret << super(params)
|
882
|
+
param = param_variables()
|
883
|
+
num = @operands[0]
|
884
|
+
ary = param.shift
|
885
|
+
ret << <<-EOS
|
886
|
+
if (RARRAY_LEN(#{ary}) < #{num}) {
|
887
|
+
#{@return_value} = rb_ary_new4(1, RARRAY_PTR(#{ary}));
|
888
|
+
} else {
|
889
|
+
#{@return_value} = rb_ary_new4(RARRAY_LEN(#{ary}) - #{num - 1}, RARRAY_PTR(#{ary}));
|
890
|
+
}
|
891
|
+
EOS
|
892
|
+
bug() unless param.empty?
|
893
|
+
ret.join("\n")
|
894
|
+
end
|
895
|
+
|
896
|
+
def type_propergation(defs)
|
897
|
+
@return_value.is_static([Array])
|
898
|
+
end
|
899
|
+
|
900
|
+
def side_effect?
|
901
|
+
false
|
902
|
+
end
|
903
|
+
end
|
904
|
+
|
905
|
+
class Splatarray < VMInsnIR
|
906
|
+
def initialize(param, argc, return_value, insn, cfg)
|
907
|
+
super(param, argc, return_value, insn, cfg)
|
908
|
+
end
|
909
|
+
|
910
|
+
def propergate_guard_usage()
|
911
|
+
params = param_irs()
|
912
|
+
params.each{|p| p.need_guard(false)}
|
913
|
+
end
|
914
|
+
|
915
|
+
def to_c(params)
|
916
|
+
ret = []
|
917
|
+
ret << super(params)
|
918
|
+
param = param_variables()
|
919
|
+
ary = param.shift
|
920
|
+
# FIXME ary の型を考慮
|
921
|
+
# Nil にならないと分かっていたら、nil かどうかの確認は消せる
|
922
|
+
ret << <<-EOS
|
923
|
+
cast_off_tmp = rb_check_convert_type(#{ary}, T_ARRAY, "Array", "to_a");
|
924
|
+
if (NIL_P(cast_off_tmp)) cast_off_tmp = rb_ary_new3(1, #{ary});
|
925
|
+
#{@return_value} = cast_off_tmp;
|
926
|
+
EOS
|
927
|
+
bug() unless param.empty?
|
928
|
+
ret.join("\n")
|
929
|
+
end
|
930
|
+
|
931
|
+
def type_propergation(defs)
|
932
|
+
@return_value.is_static([Array])
|
933
|
+
end
|
934
|
+
|
935
|
+
def harmless?(recv_p)
|
936
|
+
false # can be return self
|
937
|
+
end
|
938
|
+
|
939
|
+
def side_effect?
|
940
|
+
true
|
941
|
+
end
|
942
|
+
end
|
943
|
+
|
944
|
+
class CheckincludearrayPre < VMInsnIR
|
945
|
+
def initialize(param, argc, return_value, insn, cfg)
|
946
|
+
super(param, argc, return_value, insn, cfg)
|
947
|
+
end
|
948
|
+
|
949
|
+
def propergate_guard_usage()
|
950
|
+
params = param_irs()
|
951
|
+
params.each{|p| p.need_guard(false)}
|
952
|
+
end
|
953
|
+
|
954
|
+
def to_c(params)
|
955
|
+
ret = []
|
956
|
+
ret << super(params)
|
957
|
+
param = param_variables()
|
958
|
+
ary = param.shift
|
959
|
+
# このチェックがなくならないことを前提に、この命令に対するガードを削除したので、
|
960
|
+
# このチェックは削除しないこと
|
961
|
+
ret << " if (TYPE(#{ary}) != T_ARRAY) #{@return_value} = rb_Array(#{ary});"
|
962
|
+
bug() unless param.empty?
|
963
|
+
ret.join("\n")
|
964
|
+
end
|
965
|
+
|
966
|
+
def type_propergation(defs)
|
967
|
+
@return_value.is_static([Array])
|
968
|
+
end
|
969
|
+
|
970
|
+
def side_effect?
|
971
|
+
true
|
972
|
+
end
|
973
|
+
end
|
974
|
+
|
975
|
+
class CheckincludearrayCase < VMInsnIR
|
976
|
+
def initialize(param, argc, return_value, insn, cfg)
|
977
|
+
super(param, argc, return_value, insn, cfg)
|
978
|
+
end
|
979
|
+
|
980
|
+
def propergate_guard_usage()
|
981
|
+
params = param_irs()
|
982
|
+
params.each{|p| p.need_guard(false)}
|
983
|
+
end
|
984
|
+
|
985
|
+
def to_c(params)
|
986
|
+
ret = []
|
987
|
+
ret << super(params)
|
988
|
+
param = param_variables()
|
989
|
+
ary = param.shift
|
990
|
+
obj = param.shift
|
991
|
+
ret << <<-EOS
|
992
|
+
cast_off_tmp = Qfalse;
|
993
|
+
{
|
994
|
+
int i;
|
995
|
+
for (i = 0; i < RARRAY_LEN(#{ary}); i++) {
|
996
|
+
if (RTEST(rb_funcall2(RARRAY_PTR(#{ary})[i], idEqq, 1, &#{obj}))) {
|
997
|
+
cast_off_tmp = Qtrue;
|
998
|
+
break;
|
999
|
+
}
|
1000
|
+
}
|
1001
|
+
}
|
1002
|
+
#{@return_value} = cast_off_tmp;
|
1003
|
+
EOS
|
1004
|
+
bug() unless param.empty?
|
1005
|
+
ret.join("\n")
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
def type_propergation(defs)
|
1009
|
+
@return_value.is_static([TrueClass, FalseClass])
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
def harmless?(recv_p)
|
1013
|
+
false
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
def side_effect?
|
1017
|
+
true
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
class CheckincludearrayWhen < VMInsnIR
|
1022
|
+
def initialize(param, argc, return_value, insn, cfg)
|
1023
|
+
super(param, argc, return_value, insn, cfg)
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
def propergate_guard_usage()
|
1027
|
+
params = param_irs()
|
1028
|
+
params.each{|p| p.need_guard(false)}
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def to_c(params)
|
1032
|
+
ret = []
|
1033
|
+
ret << super(params)
|
1034
|
+
param = param_variables()
|
1035
|
+
ary = param.shift
|
1036
|
+
ret << <<-EOS
|
1037
|
+
cast_off_tmp = Qfalse;
|
1038
|
+
{
|
1039
|
+
int i;
|
1040
|
+
for (i = 0; i < RARRAY_LEN(#{ary}); i++) {
|
1041
|
+
if (RTEST(RARRAY_PTR(#{ary})[i])) {
|
1042
|
+
cast_off_tmp = Qtrue;
|
1043
|
+
break;
|
1044
|
+
}
|
1045
|
+
}
|
1046
|
+
}
|
1047
|
+
#{@return_value} = cast_off_tmp;
|
1048
|
+
EOS
|
1049
|
+
bug() unless param.empty?
|
1050
|
+
ret.join("\n")
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
def type_propergation(defs)
|
1054
|
+
@return_value.is_static([TrueClass, FalseClass])
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
def side_effect?
|
1058
|
+
false
|
1059
|
+
end
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
class Defined < VMInsnIR
|
1063
|
+
def initialize(param, argc, return_value, insn, cfg)
|
1064
|
+
super(param, argc, return_value, insn, cfg)
|
1065
|
+
argv = insn.argv
|
1066
|
+
@defined_t = argv[0]
|
1067
|
+
sym = argv[1]
|
1068
|
+
bug() unless sym.instance_of?(Symbol)
|
1069
|
+
@id = @translator.allocate_id(sym)
|
1070
|
+
@needstr = argv[2] # Qtrue or Qfalse
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
def propergate_guard_usage()
|
1074
|
+
params = param_irs()
|
1075
|
+
params.each{|p| p.need_guard(false)}
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
def to_c(params)
|
1079
|
+
ret = []
|
1080
|
+
ret << super(params)
|
1081
|
+
param = param_variables()
|
1082
|
+
val = param.pop()
|
1083
|
+
|
1084
|
+
case @defined_t
|
1085
|
+
when DEFINED_IVAR
|
1086
|
+
ret << <<-EOS
|
1087
|
+
if (rb_ivar_defined(self, #{@id})) {
|
1088
|
+
#{@return_value} = #{@needstr ? 'rb_str_new2("instance-variable")' : 'Qtrue'};
|
1089
|
+
} else {
|
1090
|
+
#{@return_value} = Qnil;
|
1091
|
+
}
|
1092
|
+
EOS
|
1093
|
+
when DEFINED_GVAR
|
1094
|
+
ret << <<-EOS
|
1095
|
+
if (rb_gvar_defined(rb_global_entry(#{@id}))) {
|
1096
|
+
#{@return_value} = #{@needstr ? 'rb_str_new2("global-variable")' : 'Qtrue'};
|
1097
|
+
} else {
|
1098
|
+
#{@return_value} = Qnil;
|
1099
|
+
}
|
1100
|
+
EOS
|
1101
|
+
when DEFINED_FUNC
|
1102
|
+
ret << <<-EOS
|
1103
|
+
if (rb_method_boundp(rb_class_of(#{val}), #{@id}), 0) {
|
1104
|
+
#{@return_value} = #{@needstr ? 'rb_str_new2("method")' : 'Qtrue'};
|
1105
|
+
} else {
|
1106
|
+
#{@return_value} = Qnil;
|
1107
|
+
}
|
1108
|
+
EOS
|
1109
|
+
else
|
1110
|
+
bug()
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
bug() unless param.empty?
|
1114
|
+
ret.join("\n")
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
def type_propergation(defs)
|
1118
|
+
if @needstr
|
1119
|
+
@return_value.is_static([NilClass, String])
|
1120
|
+
else
|
1121
|
+
@return_value.is_static([NilClass, TrueClass])
|
1122
|
+
end
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
def harmless?(recv_p)
|
1126
|
+
true
|
1127
|
+
end
|
1128
|
+
|
1129
|
+
def should_be_alive?
|
1130
|
+
false
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
def side_effect?
|
1134
|
+
false
|
1135
|
+
end
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
class CastOffFetchArgs < VMInsnIR
|
1139
|
+
def initialize(param, argc, return_value, insn, cfg)
|
1140
|
+
super(param, argc, return_value, insn, cfg)
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
def propergate_guard_usage()
|
1144
|
+
params = param_irs()
|
1145
|
+
params.each{|p| p.need_guard(false)}
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
def to_c(params)
|
1149
|
+
ret = []
|
1150
|
+
ret << super(params)
|
1151
|
+
param = param_variables()
|
1152
|
+
argv = @operands
|
1153
|
+
if argv[0]
|
1154
|
+
# root iseq
|
1155
|
+
must, opt, rest, post, block, args = *argv[0]
|
1156
|
+
ret << <<-EOS
|
1157
|
+
{
|
1158
|
+
int num = rb_scan_args(argc, argv, \"#{must}#{opt}#{rest}#{post}#{block}\", &#{args.join(", &")});
|
1159
|
+
#{@return_value} = INT2FIX(num);
|
1160
|
+
}
|
1161
|
+
EOS
|
1162
|
+
else
|
1163
|
+
# child iseq
|
1164
|
+
bug() if @translator.inline_block?
|
1165
|
+
ret << " #{@return_value} = INT2FIX(num);"
|
1166
|
+
end
|
1167
|
+
bug() unless param.empty?
|
1168
|
+
ret.join("\n")
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
def type_propergation(defs)
|
1172
|
+
@return_value.is_static([Fixnum])
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
def side_effect?
|
1176
|
+
true
|
1177
|
+
end
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
class LoopIR < CallIR
|
1181
|
+
attr_reader :loopkey, :loop_phase
|
1182
|
+
|
1183
|
+
def initialize(loopkey, phase, param, argc, return_value, insn, cfg)
|
1184
|
+
super(param, argc, return_value, insn, cfg)
|
1185
|
+
@loop_phase = phase
|
1186
|
+
@loopkey = loopkey
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
def propergate_guard_usage()
|
1190
|
+
params = param_irs()
|
1191
|
+
return if params.empty?
|
1192
|
+
recv = params.shift()
|
1193
|
+
recv.need_guard(true)
|
1194
|
+
params.each{|p| p.need_guard(false)}
|
1195
|
+
end
|
1196
|
+
|
1197
|
+
def to_c(params)
|
1198
|
+
ret = []
|
1199
|
+
ret << super(params)
|
1200
|
+
param = param_variables()
|
1201
|
+
recv, *rest = *param
|
1202
|
+
s_param = rest.empty? ? nil : ", #{rest.join(", ")}"
|
1203
|
+
case @loop_phase
|
1204
|
+
when :cast_off_prep
|
1205
|
+
if @translator.inline_block?
|
1206
|
+
unless @loopkey.resolved?
|
1207
|
+
@translator.unsupported_or_re_compilation(<<-EOS)
|
1208
|
+
|
1209
|
+
<<< Failed to resolve iterater reciever type >>>
|
1210
|
+
You should specify iterator reciever type, method name = #{@loopkey.id}.
|
1211
|
+
-------------------------------------------------------------------------
|
1212
|
+
Target file is (#{@translator.target_name()}).
|
1213
|
+
Call site is (#{@insn}).
|
1214
|
+
EOS
|
1215
|
+
end
|
1216
|
+
@dependency.add(@loopkey.klass, @loopkey.id, true)
|
1217
|
+
ret << " #{@return_value} = #{@loopkey.prep_func}(&#{@loopkey}, #{recv}, #{@argc - 1}#{s_param});"
|
1218
|
+
else
|
1219
|
+
ret << <<-EOS
|
1220
|
+
cast_off_set_block(#{@loopkey.block_iseq.block_generator()}(self));
|
1221
|
+
#{@return_value} = rb_funcall(#{recv}, #{@translator.allocate_id(@loopkey.id)}, #{@argc - 1}#{s_param});
|
1222
|
+
#{@insn.iseq.update_dfp()}
|
1223
|
+
EOS
|
1224
|
+
end
|
1225
|
+
when :cast_off_loop
|
1226
|
+
if @translator.inline_block?
|
1227
|
+
bug() unless param.size == 1
|
1228
|
+
ret << " #{@return_value} = #{@loopkey.loop_func}(&#{@loopkey}, #{param[0]}, #{@loopkey.argc}, cast_off_argv, #{@loopkey.splat? ? 1 : 0}, #{@loopkey.arg_argc}, #{@loopkey.arg_post_len}, #{@loopkey.arg_post_start}, #{@loopkey.arg_rest_index});"
|
1229
|
+
end
|
1230
|
+
when :cast_off_finl
|
1231
|
+
if @translator.inline_block?
|
1232
|
+
bug() unless param.empty?
|
1233
|
+
ret << " #{@return_value} = #{@loopkey.finl_func}(&#{@loopkey});"
|
1234
|
+
end
|
1235
|
+
else
|
1236
|
+
bug()
|
1237
|
+
end
|
1238
|
+
ret.join("\n")
|
1239
|
+
end
|
1240
|
+
|
1241
|
+
def type_propergation(defs)
|
1242
|
+
if @loop_phase == :cast_off_prep
|
1243
|
+
recv = @param[0].param_value
|
1244
|
+
@loopkey.resolve(recv.types) unless recv.undefined? || recv.dynamic?
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
case @loop_phase
|
1248
|
+
when :cast_off_prep
|
1249
|
+
return @return_value.is_dynamic()
|
1250
|
+
when :cast_off_loop
|
1251
|
+
return @return_value.is_static([TrueClass, FalseClass])
|
1252
|
+
when :cast_off_finl
|
1253
|
+
return @return_value.is_dynamic()
|
1254
|
+
end
|
1255
|
+
bug()
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
def harmless?(recv_p)
|
1259
|
+
false
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
def should_be_alive?
|
1263
|
+
true
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
def to_verbose_string()
|
1267
|
+
recv = param_variables()[0]
|
1268
|
+
"[#{recv.dynamic? ? "dynamic" : recv.types.join(", ")}]##{@loopkey.id}"
|
1269
|
+
end
|
1270
|
+
end
|
1271
|
+
|
1272
|
+
SupportLoopInstruction = {
|
1273
|
+
MethodWrapper.new(ClassWrapper.new(Array, true), :map) => 'Array_map',
|
1274
|
+
MethodWrapper.new(ClassWrapper.new(Array, true), :map!) => 'Array_map_bang',
|
1275
|
+
MethodWrapper.new(ClassWrapper.new(Array, true), :each) => 'Array_each',
|
1276
|
+
MethodWrapper.new(ClassWrapper.new(Fixnum, true), :times) => 'Fixnum_times',
|
1277
|
+
}
|
1278
|
+
|
1279
|
+
class LoopKey
|
1280
|
+
include CastOff::Util
|
1281
|
+
|
1282
|
+
attr_reader :id, :key, :argc, :klass
|
1283
|
+
attr_accessor :block_iseq
|
1284
|
+
|
1285
|
+
def initialize(id, key, args, insn, translator)
|
1286
|
+
@id = id # method name
|
1287
|
+
@key = key # loop number
|
1288
|
+
@args = args
|
1289
|
+
@argc = args.size
|
1290
|
+
|
1291
|
+
@translator = translator
|
1292
|
+
@insn = insn
|
1293
|
+
|
1294
|
+
@signiture = nil
|
1295
|
+
@klass = nil
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
def resolved?
|
1299
|
+
!!@signiture
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
def resolve(classes)
|
1303
|
+
classes.each do |c|
|
1304
|
+
bug() unless c.is_a?(ClassWrapper)
|
1305
|
+
# method exist
|
1306
|
+
begin
|
1307
|
+
sign = SupportLoopInstruction[MethodWrapper.new(c, @id)]
|
1308
|
+
rescue CompileError
|
1309
|
+
sign = nil
|
1310
|
+
end
|
1311
|
+
if @translator.inline_block? && !sign
|
1312
|
+
@translator.unsupported_or_re_compilation("Unsupported loop method: #{c}##{@id}")
|
1313
|
+
end
|
1314
|
+
@translator.unsupported_or_re_compilation(<<-EOS) if @signiture && @signiture != sign && @translator.inline_block?
|
1315
|
+
|
1316
|
+
Currently, CastOff doesn't support a method invocation which target is not single and which takes a block.
|
1317
|
+
#{@klass}##{@id} and #{c}##{@id} are different methods.
|
1318
|
+
----------------------------------------------------------------------------------------------------------
|
1319
|
+
Target file is (#{@translator.target_name()}).
|
1320
|
+
Call site is (#{@insn}).
|
1321
|
+
EOS
|
1322
|
+
@signiture = sign
|
1323
|
+
@klass = c
|
1324
|
+
end
|
1325
|
+
bug() if @translator.inline_block? && !resolved?
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
def to_s
|
1329
|
+
(resolved? && @signiture) ? "cast_off_#{@signiture}_#{@key}" : "unresolved"
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
def splat?
|
1333
|
+
bug() unless @block_iseq
|
1334
|
+
@block_iseq.args.splat?
|
1335
|
+
end
|
1336
|
+
|
1337
|
+
def arg_argc
|
1338
|
+
bug() unless @block_iseq
|
1339
|
+
@block_iseq.args.argc
|
1340
|
+
end
|
1341
|
+
|
1342
|
+
def arg_post_len
|
1343
|
+
bug() unless @block_iseq
|
1344
|
+
@block_iseq.args.post_len
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
def arg_post_start
|
1348
|
+
bug() unless @block_iseq
|
1349
|
+
@block_iseq.args.post_start
|
1350
|
+
end
|
1351
|
+
|
1352
|
+
def arg_rest_index
|
1353
|
+
bug() unless @block_iseq
|
1354
|
+
@block_iseq.args.rest_index
|
1355
|
+
end
|
1356
|
+
|
1357
|
+
def decl?
|
1358
|
+
resolved?
|
1359
|
+
end
|
1360
|
+
|
1361
|
+
def decl
|
1362
|
+
bug() unless resolved?
|
1363
|
+
bug() unless @signiture
|
1364
|
+
"cast_off_#{@signiture}_t #{self.to_s}"
|
1365
|
+
end
|
1366
|
+
|
1367
|
+
def prep_func
|
1368
|
+
bug() unless resolved?
|
1369
|
+
bug() unless @signiture
|
1370
|
+
"cast_off_#{@signiture}_prep"
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
def loop_func
|
1374
|
+
bug() unless resolved?
|
1375
|
+
bug() unless @signiture
|
1376
|
+
"cast_off_#{@signiture}_loop"
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
def finl_func
|
1380
|
+
bug() unless resolved?
|
1381
|
+
bug() unless @signiture
|
1382
|
+
"cast_off_#{@signiture}_finl"
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
def dopt_func
|
1386
|
+
bug() unless resolved?
|
1387
|
+
bug() unless @signiture
|
1388
|
+
"cast_off_#{@signiture}_construct_frame"
|
1389
|
+
end
|
1390
|
+
end
|
1391
|
+
|
1392
|
+
SPLATCALL_LIMIT = 25
|
1393
|
+
class YieldIR < CallIR
|
1394
|
+
def initialize(flags, param, argc, return_value, insn, cfg)
|
1395
|
+
super(param, argc, return_value, insn, cfg)
|
1396
|
+
@flags = flags
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
def propergate_guard_usage()
|
1400
|
+
params = param_irs()
|
1401
|
+
params.each{|p| p.need_guard(splatyield?)}
|
1402
|
+
end
|
1403
|
+
|
1404
|
+
SPLATCALL_TEMPLATE_YIELD = ERB.new(<<-EOS, 0, '%-', 'io')
|
1405
|
+
%bug() unless param.size > 0
|
1406
|
+
{
|
1407
|
+
int argc = <%= @argc - 1 %>;
|
1408
|
+
VALUE argv[<%= SPLATCALL_LIMIT %>]; /* FIXME */
|
1409
|
+
%splat_param = param.last()
|
1410
|
+
%normal_param = param.slice(0, param.size() - 1)
|
1411
|
+
VALUE ary = <%= splat_param %>;
|
1412
|
+
%if splat_param.is_just?(Array)
|
1413
|
+
% is_ary = true
|
1414
|
+
VALUE tmp = ary;
|
1415
|
+
%else
|
1416
|
+
% is_ary = false
|
1417
|
+
VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a");
|
1418
|
+
%end
|
1419
|
+
|
1420
|
+
%normal_param.each_with_index do |p, i|
|
1421
|
+
argv[<%= i %>] = <%= p %>;
|
1422
|
+
%end
|
1423
|
+
%unless is_ary
|
1424
|
+
if (NIL_P(tmp)) {
|
1425
|
+
/* do nothing */
|
1426
|
+
} else {
|
1427
|
+
%else
|
1428
|
+
{
|
1429
|
+
%end
|
1430
|
+
VALUE *ptr;
|
1431
|
+
long i, len = RARRAY_LEN(tmp);
|
1432
|
+
ptr = RARRAY_PTR(tmp);
|
1433
|
+
argc += len;
|
1434
|
+
if (UNLIKELY(argc > <%= SPLATCALL_LIMIT %>)) {
|
1435
|
+
rb_raise(rb_eCastOffExecutionError, "Too large array(limit = <%= SPLATCALL_LIMIT %>)");
|
1436
|
+
}
|
1437
|
+
for (i = 0; i < len; i++) {
|
1438
|
+
argv[<%= normal_param.size %> + i] = ptr[i];
|
1439
|
+
}
|
1440
|
+
}
|
1441
|
+
rb_yield_values2(argc, argv);
|
1442
|
+
}
|
1443
|
+
EOS
|
1444
|
+
|
1445
|
+
def to_c(params)
|
1446
|
+
ret = []
|
1447
|
+
ret << super(params)
|
1448
|
+
param = param_variables()
|
1449
|
+
|
1450
|
+
if splatyield?
|
1451
|
+
ret << SPLATCALL_TEMPLATE_YIELD.trigger(binding)
|
1452
|
+
else
|
1453
|
+
if param.empty?
|
1454
|
+
ret << " #{@return_value} = rb_yield(Qundef);"
|
1455
|
+
else
|
1456
|
+
ret << " #{@return_value} = rb_yield_values(#{param.size}, #{param.join(", ")});"
|
1457
|
+
end
|
1458
|
+
end
|
1459
|
+
|
1460
|
+
ret.join("\n")
|
1461
|
+
end
|
1462
|
+
|
1463
|
+
def to_verbose_string()
|
1464
|
+
"yield"
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
def type_propergation(defs)
|
1468
|
+
@return_value.is_dynamic()
|
1469
|
+
end
|
1470
|
+
|
1471
|
+
def harmless?(recv_p)
|
1472
|
+
false
|
1473
|
+
end
|
1474
|
+
|
1475
|
+
def should_be_alive?
|
1476
|
+
true
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
def side_effect?
|
1480
|
+
true
|
1481
|
+
end
|
1482
|
+
|
1483
|
+
def splatyield?
|
1484
|
+
(@flags & VM_CALL_ARGS_SPLAT_BIT) != 0
|
1485
|
+
end
|
1486
|
+
end
|
1487
|
+
|
1488
|
+
class InvokeIR < CallIR
|
1489
|
+
attr_reader :method_id
|
1490
|
+
|
1491
|
+
IncompatMethods = [
|
1492
|
+
:eval, # use vm_get_ruby_level_caller_cfp
|
1493
|
+
:binding, # use vm_get_ruby_level_caller_cfp
|
1494
|
+
:block_given?, # use vm_get_ruby_level_caller_cfp
|
1495
|
+
:iterator?, # use vm_get_ruby_level_caller_cfp
|
1496
|
+
]
|
1497
|
+
|
1498
|
+
def initialize(mid, flags, param, argc, return_value, insn, cfg)
|
1499
|
+
super(param, argc, return_value, insn, cfg)
|
1500
|
+
@flags = flags
|
1501
|
+
@method_id = mid
|
1502
|
+
|
1503
|
+
if fcall? && IncompatMethods.include?(@method_id)
|
1504
|
+
raise(UnsupportedError.new(<<-EOS))
|
1505
|
+
|
1506
|
+
Currently, #{@method_id} method is incompatible.
|
1507
|
+
You should not use #{@method_id} method in compilation target of CastOff.
|
1508
|
+
EOS
|
1509
|
+
end
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
def propergate_guard_usage()
|
1513
|
+
params = param_irs()
|
1514
|
+
bug() if params.empty?
|
1515
|
+
recv = params.shift()
|
1516
|
+
recv.need_guard(true)
|
1517
|
+
if splatcall? || specializecall?
|
1518
|
+
params.each{|p| p.need_guard(true)}
|
1519
|
+
else
|
1520
|
+
params.each{|p| p.need_guard(false)}
|
1521
|
+
end
|
1522
|
+
end
|
1523
|
+
|
1524
|
+
def specializecall?
|
1525
|
+
params = param_variables()
|
1526
|
+
recv, *args = *params
|
1527
|
+
bug() if recv.undefined?
|
1528
|
+
if recv.dynamic? || @configuration.force_dispatch_method?
|
1529
|
+
return false
|
1530
|
+
else
|
1531
|
+
bug() if recv.types.empty?
|
1532
|
+
recv.types.each do |klass|
|
1533
|
+
bug() unless klass.is_a?(ClassWrapper)
|
1534
|
+
next if klass.get_method_type(@method_id) != :cfunc
|
1535
|
+
next unless @configuration.should_be_call_directly?(klass, @method_id)
|
1536
|
+
cfunc_argc = klass.get_cfunc_argc(@method_id)
|
1537
|
+
bug() unless cfunc_argc
|
1538
|
+
if klass.String? || klass.Array? || klass.Fixnum? || klass.Float?
|
1539
|
+
# != とかのために、クラス単位で分ける必要がある。
|
1540
|
+
return true if SpecializeTable0[[MethodWrapper.new(klass, @method_id), @argc]]
|
1541
|
+
end
|
1542
|
+
# FIXME specializecall 側を修正
|
1543
|
+
return true if SpecializeTable1[[@method_id, @argc, MethodWrapper.new(klass, @method_id)]]
|
1544
|
+
end
|
1545
|
+
end
|
1546
|
+
false
|
1547
|
+
end
|
1548
|
+
|
1549
|
+
def to_verbose_string()
|
1550
|
+
recv = param_variables()[0]
|
1551
|
+
"[#{recv.dynamic? ? "dynamic" : recv.types.join(", ")}]##{@method_id}"
|
1552
|
+
end
|
1553
|
+
|
1554
|
+
def fcall?
|
1555
|
+
bug() unless @flags
|
1556
|
+
(@flags & VM_CALL_FCALL_BIT) != 0
|
1557
|
+
end
|
1558
|
+
|
1559
|
+
def blockarg?
|
1560
|
+
bug() unless @flags
|
1561
|
+
(@flags & VM_CALL_ARGS_BLOCKARG_BIT) != 0
|
1562
|
+
end
|
1563
|
+
|
1564
|
+
def splatcall?
|
1565
|
+
bug() unless @flags
|
1566
|
+
(@flags & VM_CALL_ARGS_SPLAT_BIT) != 0
|
1567
|
+
end
|
1568
|
+
|
1569
|
+
SPLATCALL_TEMPLATE_CFUNC = ERB.new(<<-EOS, 0, '%-', 'io')
|
1570
|
+
%bug() unless param.size > 0
|
1571
|
+
{
|
1572
|
+
int argc = <%= argc - 2 %>;
|
1573
|
+
%splat_param = param.last()
|
1574
|
+
%normal_param = param.slice(0, param.size() - 1)
|
1575
|
+
VALUE ary = <%= splat_param %>;
|
1576
|
+
%if splat_param.is_just?(Array)
|
1577
|
+
% is_ary = true
|
1578
|
+
VALUE tmp = ary;
|
1579
|
+
%else
|
1580
|
+
% is_ary = false
|
1581
|
+
VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a");
|
1582
|
+
%end
|
1583
|
+
VALUE *ptr = NULL;
|
1584
|
+
|
1585
|
+
%unless is_ary
|
1586
|
+
if (NIL_P(tmp)) {
|
1587
|
+
/* do nothing */
|
1588
|
+
} else {
|
1589
|
+
%end
|
1590
|
+
ptr = RARRAY_PTR(tmp);
|
1591
|
+
argc += RARRAY_LEN(tmp);
|
1592
|
+
%unless is_ary
|
1593
|
+
}
|
1594
|
+
%end
|
1595
|
+
if (UNLIKELY(argc != <%= splat_call_argc %>)) {
|
1596
|
+
rb_raise(rb_eCastOffExecutionError, "wrong number of arguments (<%= splat_call_argc %> for %d)", argc);
|
1597
|
+
}
|
1598
|
+
%unless is_ary
|
1599
|
+
if (ptr) {
|
1600
|
+
%end
|
1601
|
+
%rest = ""
|
1602
|
+
%(splat_call_argc - normal_param.size).times do |i|
|
1603
|
+
% rest += ", ptr[\#{i}]"
|
1604
|
+
%end
|
1605
|
+
<%= @return_value %> = <%= fnam_code %>(<%= recv %><%= normal_param.empty? ? nil : ", \#{normal_param.join(", ")}" %><%= rest %>);
|
1606
|
+
%unless is_ary
|
1607
|
+
} else {
|
1608
|
+
<%= @return_value %> = <%= fnam_code %>(<%= recv %><%= normal_param.empty? ? nil : ", \#{normal_param.join(", ")}" %>);
|
1609
|
+
}
|
1610
|
+
%end
|
1611
|
+
}
|
1612
|
+
EOS
|
1613
|
+
|
1614
|
+
SPLATCALL_TEMPLATE_ARGV = ERB.new(<<-EOS, 0, '%-', 'io')
|
1615
|
+
%bug() unless param.size > 0
|
1616
|
+
{
|
1617
|
+
int argc = <%= argc - 2 %>;
|
1618
|
+
VALUE argv[<%= SPLATCALL_LIMIT %>];
|
1619
|
+
%splat_param = param.last()
|
1620
|
+
%normal_param = param.slice(0, param.size() - 1)
|
1621
|
+
VALUE ary = <%= splat_param %>;
|
1622
|
+
%if splat_param.is_just?(Array)
|
1623
|
+
% is_ary = true
|
1624
|
+
VALUE tmp = ary;
|
1625
|
+
%else
|
1626
|
+
% is_ary = false
|
1627
|
+
VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a");
|
1628
|
+
%end
|
1629
|
+
|
1630
|
+
%normal_param.each_with_index do |p, i|
|
1631
|
+
argv[<%= i %>] = <%= p %>;
|
1632
|
+
%end
|
1633
|
+
%unless is_ary
|
1634
|
+
if (NIL_P(tmp)) {
|
1635
|
+
/* do nothing */
|
1636
|
+
} else {
|
1637
|
+
%else
|
1638
|
+
{
|
1639
|
+
%end
|
1640
|
+
VALUE *ptr;
|
1641
|
+
long i, len = RARRAY_LEN(tmp);
|
1642
|
+
ptr = RARRAY_PTR(tmp);
|
1643
|
+
argc += len;
|
1644
|
+
if (UNLIKELY(argc > <%= SPLATCALL_LIMIT %>)) {
|
1645
|
+
rb_raise(rb_eCastOffExecutionError, "Too large array(limit = <%= SPLATCALL_LIMIT %>)");
|
1646
|
+
}
|
1647
|
+
for (i = 0; i < len; i++) {
|
1648
|
+
argv[<%= normal_param.size %> + i] = ptr[i];
|
1649
|
+
}
|
1650
|
+
}
|
1651
|
+
<%= call_code %>
|
1652
|
+
}
|
1653
|
+
EOS
|
1654
|
+
|
1655
|
+
def funcall_fptr(klass, argc, suffix = nil)
|
1656
|
+
bug() unless klass.nil? || klass.instance_of?(ClassWrapper)
|
1657
|
+
(klass && !suffix) ? @translator.allocate_function_pointer(klass, @method_id, -3, argc) : "rb_funcall#{suffix}"
|
1658
|
+
end
|
1659
|
+
|
1660
|
+
def funcall_code(klass, id, recv, param, argc)
|
1661
|
+
if blockarg?
|
1662
|
+
bug() if param.empty?
|
1663
|
+
blockarg = param.last()
|
1664
|
+
param = param.slice(0, param.size - 1)
|
1665
|
+
argc = argc - 1
|
1666
|
+
ret = " handle_blockarg(#{blockarg});\n"
|
1667
|
+
if splatcall?
|
1668
|
+
fptr = funcall_fptr(klass, nil, 2)
|
1669
|
+
call_code = "#{@return_value} = #{fptr}(#{recv}, #{id}, argc, argv);"
|
1670
|
+
ret += SPLATCALL_TEMPLATE_ARGV.trigger(binding)
|
1671
|
+
else
|
1672
|
+
fptr = funcall_fptr(klass, param.size)
|
1673
|
+
ret += " #{@return_value} = #{fptr}(#{recv}, #{id}, #{param.size}#{param.empty? ? nil : ", #{param.join(", ")}"});"
|
1674
|
+
end
|
1675
|
+
ret
|
1676
|
+
else
|
1677
|
+
if splatcall?
|
1678
|
+
fptr = funcall_fptr(klass, nil, 2)
|
1679
|
+
call_code = "#{@return_value} = #{fptr}(#{recv}, #{id}, argc, argv);"
|
1680
|
+
SPLATCALL_TEMPLATE_ARGV.trigger(binding)
|
1681
|
+
else
|
1682
|
+
fptr = funcall_fptr(klass, param.size)
|
1683
|
+
" #{@return_value} = #{fptr}(#{recv}, #{id}, #{argc - 1}#{param.empty? ? nil : ", #{param.join(", ")}"});"
|
1684
|
+
end
|
1685
|
+
end
|
1686
|
+
end
|
1687
|
+
|
1688
|
+
StringWrapper = ClassWrapper.new(String, true)
|
1689
|
+
FixnumWrapper = ClassWrapper.new(Fixnum, true)
|
1690
|
+
FloatWrapper = ClassWrapper.new(Float, true)
|
1691
|
+
ArrayWrapper = ClassWrapper.new(Array, true)
|
1692
|
+
ObjectWrapper = ClassWrapper.new(Object, true)
|
1693
|
+
NilClassWrapper = ClassWrapper.new(NilClass, true)
|
1694
|
+
FalseClassWrapper = ClassWrapper.new(FalseClass, true)
|
1695
|
+
BasicObjectWrapper = ClassWrapper.new(BasicObject, true)
|
1696
|
+
KernelWrapper = ModuleWrapper.new(Kernel)
|
1697
|
+
SpecializeTable0 = {
|
1698
|
+
[MethodWrapper.new(StringWrapper, :<<), 2] => [['concat', [String], nil, String, false, false]],
|
1699
|
+
[MethodWrapper.new(StringWrapper, :+), 2] => [['plus', [String], nil, String, false, false]],
|
1700
|
+
[MethodWrapper.new(StringWrapper, :==), 2] => [['eq', [String], nil, [TrueClass, FalseClass], false, false]],
|
1701
|
+
[MethodWrapper.new(StringWrapper, :===), 2] => [['eqq', [String], nil, [TrueClass, FalseClass], false, false]],
|
1702
|
+
[MethodWrapper.new(StringWrapper, :!=), 2] => [['neq', [String], nil, [TrueClass, FalseClass], false, false]],
|
1703
|
+
[MethodWrapper.new(StringWrapper, :empty?), 1] => [['empty_p', [], nil, [TrueClass, FalseClass], false, false]],
|
1704
|
+
[MethodWrapper.new(ArrayWrapper, :[]), 2] => [['entry', [Fixnum], nil, nil, false, false]],
|
1705
|
+
[MethodWrapper.new(ArrayWrapper, :[]=), 3] => [['store', [Fixnum, nil], nil, nil, false, false]],
|
1706
|
+
[MethodWrapper.new(ArrayWrapper, :length), 1] => [['length', [], Fixnum, nil, false, false]], # FIXME
|
1707
|
+
[MethodWrapper.new(ArrayWrapper, :size), 1] => [['size', [], Fixnum, nil, false, false]], # FIXME
|
1708
|
+
[MethodWrapper.new(ArrayWrapper, :empty?), 1] => [['empty_p', [], nil, [TrueClass, FalseClass], false, false]],
|
1709
|
+
[MethodWrapper.new(ArrayWrapper, :last), 1] => [['last', [], nil, nil, false, false]],
|
1710
|
+
[MethodWrapper.new(ArrayWrapper, :first), 1] => [['first', [], nil, nil, false, false]],
|
1711
|
+
[MethodWrapper.new(FixnumWrapper, :+), 2] => [['fixnum_plus', [Fixnum], Fixnum, nil, true, true], ['float_plus', [Float], nil, Float, true, true]], # FIXME
|
1712
|
+
[MethodWrapper.new(FixnumWrapper, :-), 2] => [['fixnum_minus', [Fixnum], Fixnum, nil, true, true], ['float_minus', [Float], nil, Float, true, true]], # FIXME
|
1713
|
+
[MethodWrapper.new(FixnumWrapper, :*), 2] => [['fixnum_mult', [Fixnum], Fixnum, nil, true, true], ['float_mult', [Float], nil, Float, true, true]], # FIXME
|
1714
|
+
[MethodWrapper.new(FixnumWrapper, :<=), 2] => [['le', [Fixnum], nil, [TrueClass, FalseClass], false, false]],
|
1715
|
+
[MethodWrapper.new(FixnumWrapper, :<), 2] => [['lt', [Fixnum], nil, [TrueClass, FalseClass], false, false]],
|
1716
|
+
[MethodWrapper.new(FixnumWrapper, :>=), 2] => [['ge', [Fixnum], nil, [TrueClass, FalseClass], false, false]],
|
1717
|
+
[MethodWrapper.new(FixnumWrapper, :>), 2] => [['gt', [Fixnum], nil, [TrueClass, FalseClass], false, false]],
|
1718
|
+
[MethodWrapper.new(FixnumWrapper, :==), 2] => [['eq', [Fixnum], nil, [TrueClass, FalseClass], false, false]],
|
1719
|
+
[MethodWrapper.new(FixnumWrapper, :===), 2] => [['eqq', [Fixnum], nil, [TrueClass, FalseClass], false, false]],
|
1720
|
+
[MethodWrapper.new(FixnumWrapper, :!=), 2] => [['neq', [Fixnum], nil, [TrueClass, FalseClass], false, false]],
|
1721
|
+
[MethodWrapper.new(FixnumWrapper, :&), 2] => [['and', [Fixnum], Fixnum, nil, false, false]], # FIXME
|
1722
|
+
[MethodWrapper.new(FloatWrapper, :+), 2] => [['float_plus', [Float], nil, Float, true, true], ['fixnum_plus', [Fixnum], nil, Float, true, true]],
|
1723
|
+
[MethodWrapper.new(FloatWrapper, :-), 2] => [['float_minus', [Float], nil, Float, true, true], ['fixnum_minus', [Fixnum], nil, Float, true, true]],
|
1724
|
+
[MethodWrapper.new(FloatWrapper, :*), 2] => [['float_mult', [Float], nil, Float, true, true], ['fixnum_mult', [Fixnum], nil, Float, true, true]],
|
1725
|
+
[MethodWrapper.new(FloatWrapper, :/), 2] => [['float_div', [Float], nil, Float, true, true], ['fixnum_div', [Fixnum], nil, Float, true, true]],
|
1726
|
+
[MethodWrapper.new(FloatWrapper, :>), 2] => [['float_gt', [Float], nil, [TrueClass, FalseClass], false, true]],
|
1727
|
+
}
|
1728
|
+
SpecializeTable1 = {
|
1729
|
+
[:==, 2, MethodWrapper.new(ObjectWrapper, :==)] => :specialized_object_eq,
|
1730
|
+
[:nil?, 1, MethodWrapper.new(NilClassWrapper, :nil?)] => :specialized_nilclass_nil?,
|
1731
|
+
[:nil?, 1, MethodWrapper.new(KernelWrapper, :nil?)] => :specialized_kernel_nil?,
|
1732
|
+
[:!, 1, MethodWrapper.new(BasicObjectWrapper, :!)] => :specialized_basicobject_not,
|
1733
|
+
}
|
1734
|
+
|
1735
|
+
def specialized_object_eq(klass, mid, argc, recv, param)
|
1736
|
+
bug() unless mid == :==
|
1737
|
+
bug() unless argc == 2
|
1738
|
+
bug() unless param.size() == 1
|
1739
|
+
return " #{@return_value} = (#{recv} == #{param[0]}) ? Qtrue : Qfalse;"
|
1740
|
+
end
|
1741
|
+
|
1742
|
+
def specialized_nilclass_nil?(klass, mid, argc, recv, param)
|
1743
|
+
bug() unless mid == :nil?
|
1744
|
+
bug() unless argc == 1
|
1745
|
+
bug() unless param.empty?
|
1746
|
+
return " #{@return_value} = Qtrue;"
|
1747
|
+
end
|
1748
|
+
|
1749
|
+
def specialized_kernel_nil?(klass, mid, argc, recv, param)
|
1750
|
+
bug() unless mid == :nil?
|
1751
|
+
bug() unless argc == 1
|
1752
|
+
bug() unless param.empty?
|
1753
|
+
return " #{@return_value} = Qfalse;"
|
1754
|
+
end
|
1755
|
+
|
1756
|
+
def specialized_basicobject_not(klass, mid, argc, recv, param)
|
1757
|
+
bug() unless mid == :!
|
1758
|
+
bug() unless argc == 1
|
1759
|
+
bug() unless param.empty?
|
1760
|
+
classes = recv.types
|
1761
|
+
case classes.size
|
1762
|
+
when 0
|
1763
|
+
bug()
|
1764
|
+
when 1
|
1765
|
+
case classes[0]
|
1766
|
+
when NilClassWrapper, FalseClassWrapper
|
1767
|
+
return " #{@return_value} = Qtrue;"
|
1768
|
+
else
|
1769
|
+
return " #{@return_value} = Qfalse;"
|
1770
|
+
end
|
1771
|
+
when 2
|
1772
|
+
if classes == [NilClassWrapper, FalseClassWrapper] || classes == [FalseClassWrapper, NilClassWrapper]
|
1773
|
+
return " #{@return_value} = Qtrue;"
|
1774
|
+
elsif !classes.include?(NilClassWrapper) && !classes.include?(FalseClassWrapper)
|
1775
|
+
return " #{@return_value} = Qfalse;"
|
1776
|
+
else
|
1777
|
+
return " #{@return_value} = RTEST(#{recv}) ? Qfalse : Qtrue;"
|
1778
|
+
end
|
1779
|
+
else
|
1780
|
+
return " #{@return_value} = RTEST(#{recv}) ? Qfalse : Qtrue;"
|
1781
|
+
end
|
1782
|
+
bug()
|
1783
|
+
end
|
1784
|
+
|
1785
|
+
def specialized_code(klass, mid, argc, recv, param)
|
1786
|
+
bug() unless klass.is_a?(ClassWrapper)
|
1787
|
+
return false unless @configuration.enable_inline_api?
|
1788
|
+
# FIXME 関数ポインタが実行時に同一であることをチェック
|
1789
|
+
if klass.String? || klass.Array? || klass.Fixnum? || klass.Float?
|
1790
|
+
entries = SpecializeTable0[[MethodWrapper.new(klass, mid), argc]]
|
1791
|
+
return false unless entries
|
1792
|
+
entry = nil
|
1793
|
+
entries.each do |e|
|
1794
|
+
bug() unless e.size == 6
|
1795
|
+
name, t_param, t_result, exact_classes, can_unbox_result, can_unbox_param = e
|
1796
|
+
bug("param = #{param}, t_param = #{t_param}") unless param.size == t_param.size
|
1797
|
+
fin = true
|
1798
|
+
(param + [@return_value]).zip(t_param + [t_result]).each do |p, t|
|
1799
|
+
next if t.nil?
|
1800
|
+
unless p.is_just?(t)
|
1801
|
+
#if @source && @configuration.development?
|
1802
|
+
#@translator.add_inlineapi_suggestion(["#{klass}###{mid}", "1st argument can be not #{t}", @source_line, @source])
|
1803
|
+
#end
|
1804
|
+
fin = false
|
1805
|
+
break
|
1806
|
+
end
|
1807
|
+
end
|
1808
|
+
if fin
|
1809
|
+
entry = e
|
1810
|
+
break
|
1811
|
+
end
|
1812
|
+
end
|
1813
|
+
return false unless entry
|
1814
|
+
name, t_param, t_result, exact_classes, can_unbox_result, can_unbox_param = entry
|
1815
|
+
|
1816
|
+
s_param = param.empty? ? '' : ", #{param.join(", ")}"
|
1817
|
+
if can_unbox_param
|
1818
|
+
suffix = ([recv] + param + [@return_value]).map{|p|
|
1819
|
+
if p.unboxed?
|
1820
|
+
bug() if p.dynamic?
|
1821
|
+
bug() unless p.types.size == 1
|
1822
|
+
c = p.types[0]
|
1823
|
+
case c
|
1824
|
+
when FloatWrapper
|
1825
|
+
'double'
|
1826
|
+
when FixnumWrapper
|
1827
|
+
'long'
|
1828
|
+
else
|
1829
|
+
bug()
|
1830
|
+
end
|
1831
|
+
else
|
1832
|
+
'VALUE'
|
1833
|
+
end
|
1834
|
+
}.join("_")
|
1835
|
+
return " #{@return_value} = cast_off_inline_#{klass.to_s.downcase}_#{name}_#{suffix}(#{recv}#{s_param});"
|
1836
|
+
else
|
1837
|
+
return " #{@return_value} = cast_off_inline_#{klass.to_s.downcase}_#{name}(#{recv}#{s_param});"
|
1838
|
+
end
|
1839
|
+
else
|
1840
|
+
m = SpecializeTable1[[mid, argc, MethodWrapper.new(klass, mid)]]
|
1841
|
+
return m ? __send__(m, klass, mid, argc, recv, param) : false
|
1842
|
+
end
|
1843
|
+
bug()
|
1844
|
+
end
|
1845
|
+
|
1846
|
+
def call_cfunc_code(klass, mid, cfunc_argc, recv, param, argc)
|
1847
|
+
bug() unless klass.is_a?(ClassWrapper)
|
1848
|
+
bug() unless @configuration.should_be_call_directly?(klass, mid)
|
1849
|
+
if !splatcall? && sp = specialized_code(klass, mid, argc, recv, param)
|
1850
|
+
return sp
|
1851
|
+
else
|
1852
|
+
id = @translator.allocate_id(mid)
|
1853
|
+
ary = []
|
1854
|
+
if @configuration.enable_trace?
|
1855
|
+
ary << "#ifdef CAST_OFF_ENABLE_TRACE"
|
1856
|
+
ary << " trace_recv = #{recv}; trace_klass = rb_class_of(#{recv});"
|
1857
|
+
ary << " EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, trace_recv, #{id}, trace_klass);"
|
1858
|
+
ary << "#endif"
|
1859
|
+
end
|
1860
|
+
fptr = @translator.allocate_function_pointer(klass, mid, cfunc_argc, param.size)
|
1861
|
+
case cfunc_argc
|
1862
|
+
when -2
|
1863
|
+
if splatcall?
|
1864
|
+
return nil # use funcall
|
1865
|
+
else
|
1866
|
+
c_ary = @insn.iseq.use_temporary_c_ary(param.size)
|
1867
|
+
param.each_with_index do |arg, i|
|
1868
|
+
ary << " #{c_ary}[#{i}] = #{arg};"
|
1869
|
+
end
|
1870
|
+
ary << " #{@return_value} = (*#{fptr})(#{recv}, rb_ary_new4(#{argc - 1}, #{c_ary}));"
|
1871
|
+
end
|
1872
|
+
when -1
|
1873
|
+
if splatcall?
|
1874
|
+
call_code = "#{@return_value} = (*#{fptr})(argc, argv, #{recv});"
|
1875
|
+
ary << SPLATCALL_TEMPLATE_ARGV.trigger(binding)
|
1876
|
+
else
|
1877
|
+
c_ary = @insn.iseq.use_temporary_c_ary(param.size)
|
1878
|
+
param.each_with_index do |arg, i|
|
1879
|
+
ary << " #{c_ary}[#{i}] = #{arg};"
|
1880
|
+
end
|
1881
|
+
ary << " #{@return_value} = (*#{fptr})(#{argc - 1}, #{c_ary}, #{recv});"
|
1882
|
+
end
|
1883
|
+
when 0..15
|
1884
|
+
if splatcall?
|
1885
|
+
splat_call_argc = cfunc_argc
|
1886
|
+
fnam_code = "(*#{fptr})"
|
1887
|
+
ary << SPLATCALL_TEMPLATE_CFUNC.trigger(binding)
|
1888
|
+
else
|
1889
|
+
ary << " #{@return_value} = (*#{fptr})(#{recv}#{param.empty? ? nil : ", #{param.join(", ")}"});"
|
1890
|
+
end
|
1891
|
+
else
|
1892
|
+
raise(CompileError.new("too many arguments #{klass}##{mid}"))
|
1893
|
+
end
|
1894
|
+
if @configuration.enable_trace?
|
1895
|
+
ary << "#ifdef CAST_OFF_ENABLE_TRACE"
|
1896
|
+
ary << " EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, trace_recv, #{id}, trace_klass);"
|
1897
|
+
ary << "#endif"
|
1898
|
+
end
|
1899
|
+
return ary.join("\n")
|
1900
|
+
end
|
1901
|
+
bug()
|
1902
|
+
end
|
1903
|
+
|
1904
|
+
def recursive_call_class?(klass, mid)
|
1905
|
+
same_class = false
|
1906
|
+
bug() unless klass.is_a?(ClassWrapper)
|
1907
|
+
if @translator.reciever_class && @translator.reciever_class.include?(klass)
|
1908
|
+
same_class = true
|
1909
|
+
end
|
1910
|
+
if same_class && mid == @translator.mid
|
1911
|
+
true
|
1912
|
+
else
|
1913
|
+
false
|
1914
|
+
end
|
1915
|
+
end
|
1916
|
+
|
1917
|
+
def recursive_call_var?(recv, mid)
|
1918
|
+
same_class = false
|
1919
|
+
if @translator.reciever_class && @translator.reciever_class.size() == 1
|
1920
|
+
same_class = true if recv.is_just?(@translator.reciever_class[0])
|
1921
|
+
end
|
1922
|
+
same_class = true if recv.is_a?(Self) && (@translator.inline_block? || @insn.iseq.root?)
|
1923
|
+
if same_class && mid == @translator.mid
|
1924
|
+
true
|
1925
|
+
else
|
1926
|
+
false
|
1927
|
+
end
|
1928
|
+
end
|
1929
|
+
|
1930
|
+
def recursive_call_code(recv, param, argc)
|
1931
|
+
return funcall_code(nil, @translator.allocate_id(@method_id), recv, param, @argc) if !@translator.inline_block?
|
1932
|
+
fname = @translator.this_function_name()
|
1933
|
+
if @translator.complex_call?
|
1934
|
+
if splatcall?
|
1935
|
+
call_code = "#{@return_value} = #{fname}(argc, argv, #{recv});"
|
1936
|
+
return SPLATCALL_TEMPLATE_ARGV.trigger(binding)
|
1937
|
+
else
|
1938
|
+
ret = ""
|
1939
|
+
c_ary = @insn.iseq.use_temporary_c_ary(param.size)
|
1940
|
+
param.each_with_index do |arg, i|
|
1941
|
+
ret += " #{c_ary}[#{i}] = #{arg};\n"
|
1942
|
+
end
|
1943
|
+
return ret + " #{@return_value} = #{fname}(#{argc - 1}, #{c_ary}, #{recv});"
|
1944
|
+
end
|
1945
|
+
else
|
1946
|
+
if splatcall?
|
1947
|
+
splat_call_argc = @translator.root_iseq.args.arg_size
|
1948
|
+
fnam_code = fname
|
1949
|
+
return SPLATCALL_TEMPLATE_CFUNC.trigger(binding)
|
1950
|
+
else
|
1951
|
+
if param.size == @translator.root_iseq.args.arg_size
|
1952
|
+
return " #{@return_value} = #{fname}(#{recv}#{param.empty? ? nil : ", #{param.join(", ")}"});"
|
1953
|
+
else
|
1954
|
+
return funcall_code(nil, @translator.allocate_id(@method_id), recv, param, @argc)
|
1955
|
+
end
|
1956
|
+
end
|
1957
|
+
end
|
1958
|
+
bug()
|
1959
|
+
end
|
1960
|
+
|
1961
|
+
def same_call_code?(ary)
|
1962
|
+
bug() if ary.empty?
|
1963
|
+
code = ary[0][1]
|
1964
|
+
ary.each{|(k, c)| return false unless code == c}
|
1965
|
+
true
|
1966
|
+
end
|
1967
|
+
|
1968
|
+
FuncallThreshold = 10
|
1969
|
+
MultiCallTemplate = ERB.new(<<-'end', 0, '%-', 'io')
|
1970
|
+
%ary = []
|
1971
|
+
%mid = @method_id
|
1972
|
+
%if types.size < FuncallThreshold
|
1973
|
+
% funcall = false
|
1974
|
+
% types.each do |klass|
|
1975
|
+
% if @translator.get_c_classname(klass)
|
1976
|
+
% call_code = not_funcall_code(klass, mid, recv, param, @argc)
|
1977
|
+
% if call_code
|
1978
|
+
% ary << [klass, call_code]
|
1979
|
+
% else
|
1980
|
+
% begin
|
1981
|
+
% @dependency.add(klass, @method_id, false)
|
1982
|
+
% ary << [klass, funcall_code(klass, id, recv, param, @argc)]
|
1983
|
+
% rescue CompileError => e
|
1984
|
+
% funcall = true
|
1985
|
+
% dlog("catch exception #{e}")
|
1986
|
+
% end
|
1987
|
+
% end
|
1988
|
+
% else
|
1989
|
+
% funcall = true
|
1990
|
+
% end
|
1991
|
+
% end
|
1992
|
+
%else
|
1993
|
+
% funcall = true
|
1994
|
+
%end
|
1995
|
+
%if ary.empty?
|
1996
|
+
% bug() unless funcall
|
1997
|
+
<%= funcall_code(nil, id, recv, param, @argc) %>
|
1998
|
+
%elsif !funcall && same_call_code?(ary)
|
1999
|
+
<%= ary[0][1] %>
|
2000
|
+
%else
|
2001
|
+
cast_off_tmp = rb_class_of(<%= recv %>);
|
2002
|
+
if (0) {
|
2003
|
+
% ary.each do |(klass, call_code)|
|
2004
|
+
} else if (cast_off_tmp == <%= @translator.get_c_classname(klass) %>) {
|
2005
|
+
<%= call_code %>
|
2006
|
+
% end
|
2007
|
+
} else {
|
2008
|
+
% if funcall
|
2009
|
+
<%= funcall_code(nil, id, recv, param, @argc) %>
|
2010
|
+
% else
|
2011
|
+
rb_raise(rb_eCastOffExecutionError, "type mismatch: reciever = <%= recv %>, method name = <%= id %>");
|
2012
|
+
% end
|
2013
|
+
}
|
2014
|
+
%end
|
2015
|
+
|
2016
|
+
end
|
2017
|
+
|
2018
|
+
def not_funcall_code(klass, mid, recv, param, argc)
|
2019
|
+
bug() unless klass.is_a?(ClassWrapper)
|
2020
|
+
case klass.get_method_type(mid)
|
2021
|
+
when :cfunc
|
2022
|
+
cfunc_argc = klass.get_cfunc_argc(mid)
|
2023
|
+
bug() unless cfunc_argc
|
2024
|
+
if @configuration.should_be_call_directly?(klass, mid)
|
2025
|
+
@dependency.add(klass, mid, false)
|
2026
|
+
return call_cfunc_code(klass, mid, cfunc_argc, recv, param, argc)
|
2027
|
+
else
|
2028
|
+
return nil # shoud be use funcall
|
2029
|
+
end
|
2030
|
+
when :attrset
|
2031
|
+
raise(CompileError.new("invalid call site")) if splatcall? || param.size() != 1
|
2032
|
+
@dependency.add(klass, mid, false)
|
2033
|
+
return " #{@return_value} = rb_ivar_set(#{recv}, #{@translator.allocate_id(klass.get_attr_id(mid))}, #{param[0]});"
|
2034
|
+
when :ivar
|
2035
|
+
raise(CompileError.new("invalid call site")) if splatcall? || param.size() != 0
|
2036
|
+
@dependency.add(klass, mid, false)
|
2037
|
+
return " #{@return_value} = rb_attr_get(#{recv}, #{@translator.allocate_id(klass.get_attr_id(mid))});"
|
2038
|
+
when false
|
2039
|
+
return recursive_call_class?(klass, mid) ? recursive_call_code(recv, param, argc) : nil
|
2040
|
+
end
|
2041
|
+
bug()
|
2042
|
+
end
|
2043
|
+
|
2044
|
+
def to_c(params)
|
2045
|
+
ret = []
|
2046
|
+
ret << super(params)
|
2047
|
+
param = param_variables()
|
2048
|
+
recv = param.shift
|
2049
|
+
id = @translator.allocate_id(@method_id)
|
2050
|
+
bug() if recv.undefined?
|
2051
|
+
if @configuration.development? && sampling_return_value?
|
2052
|
+
ret << " cast_off_tmp = #{recv};"
|
2053
|
+
end
|
2054
|
+
if blockarg?
|
2055
|
+
ret << funcall_code(nil, id, recv, param, @argc)
|
2056
|
+
elsif recv.dynamic? || @configuration.force_dispatch_method?
|
2057
|
+
if @configuration.force_dispatch_method?
|
2058
|
+
# ユーザからの指定に基づいているので、Suggestion は吐かない
|
2059
|
+
ret << funcall_code(nil, id, recv, param, @argc)
|
2060
|
+
elsif recursive_call_var?(recv, @method_id)
|
2061
|
+
ret << recursive_call_code(recv, param, @argc)
|
2062
|
+
else
|
2063
|
+
if @configuration.development? && @source
|
2064
|
+
@translator.add_type_suggestion([get_definition_str(recv), @method_id.to_s, @source_line, @source])
|
2065
|
+
end
|
2066
|
+
ret << funcall_code(nil, id, recv, param, @argc)
|
2067
|
+
end
|
2068
|
+
else
|
2069
|
+
bug() if recv.types.empty?
|
2070
|
+
if recv.types.size() == 1
|
2071
|
+
multicall = false
|
2072
|
+
else
|
2073
|
+
multicall = true
|
2074
|
+
end
|
2075
|
+
if multicall
|
2076
|
+
types = recv.types
|
2077
|
+
types.each{|t| bug() unless t.is_a?(ClassWrapper) }
|
2078
|
+
nil_wrapper = ClassWrapper.new(NilClass, true)
|
2079
|
+
if types.size() == 2 && (types[0] == nil_wrapper || types[1] == nil_wrapper)
|
2080
|
+
# FIXME ここで nil もしくは別クラスだったらどうこうという処理は行わずに
|
2081
|
+
# データフロー解析時に nil もしくは別のクラスという形だったら
|
2082
|
+
# 中間コードレベルで nil かどうかの条件分岐を入れたほうが
|
2083
|
+
# 定数伝播などによる最適化まで期待できる分、性能が向上すると思う。
|
2084
|
+
nil_index = (types[0] == nil_wrapper ? 0 : 1)
|
2085
|
+
else_index = 1 - nil_index
|
2086
|
+
nil_code = not_funcall_code(nil_wrapper, @method_id, recv, param, @argc)
|
2087
|
+
if !nil_code
|
2088
|
+
nil_code = funcall_code(nil, id, recv, param, @argc)
|
2089
|
+
end
|
2090
|
+
else_class = types[else_index]
|
2091
|
+
else_code = not_funcall_code(else_class, @method_id, recv, param, @argc)
|
2092
|
+
if !else_code
|
2093
|
+
@dependency.add(else_class, @method_id, false)
|
2094
|
+
else_code = funcall_code(else_class, id, recv, param, @argc)
|
2095
|
+
end
|
2096
|
+
if nil_code == else_code
|
2097
|
+
ret << " #{nil_code}"
|
2098
|
+
else
|
2099
|
+
ret << <<-EOS
|
2100
|
+
if (#{recv} == Qnil) {
|
2101
|
+
#{nil_code}
|
2102
|
+
} else {
|
2103
|
+
#{else_code}
|
2104
|
+
}
|
2105
|
+
EOS
|
2106
|
+
end
|
2107
|
+
else
|
2108
|
+
ret << MultiCallTemplate.trigger(binding)
|
2109
|
+
end
|
2110
|
+
else # singlecall
|
2111
|
+
klass = recv.types[0]
|
2112
|
+
c = not_funcall_code(klass, @method_id, recv, param, @argc)
|
2113
|
+
if c
|
2114
|
+
ret << c
|
2115
|
+
else
|
2116
|
+
begin
|
2117
|
+
@dependency.add(klass, @method_id, false)
|
2118
|
+
rescue CompileError
|
2119
|
+
# 通過していない分岐をコンパイルするときに通る
|
2120
|
+
klass = nil
|
2121
|
+
end
|
2122
|
+
ret << funcall_code(klass, id, recv, param, @argc)
|
2123
|
+
end
|
2124
|
+
end
|
2125
|
+
end
|
2126
|
+
if @configuration.development? && sampling_return_value?
|
2127
|
+
ret << " sampling_poscall(#{@return_value}, cast_off_tmp, ID2SYM(rb_intern(#{@method_id.to_s.inspect})));"
|
2128
|
+
end
|
2129
|
+
ret.join("\n")
|
2130
|
+
end
|
2131
|
+
|
2132
|
+
def type_propergation(defs)
|
2133
|
+
return false if @return_value.dynamic?
|
2134
|
+
recv = @param[0].param_value
|
2135
|
+
return if recv.undefined?
|
2136
|
+
change = false
|
2137
|
+
recv = @param[0].param_value
|
2138
|
+
bug() if recv.undefined?
|
2139
|
+
if recv.dynamic?
|
2140
|
+
dynamic = true
|
2141
|
+
else
|
2142
|
+
dynamic = false
|
2143
|
+
types = recv.types
|
2144
|
+
types.each do |t|
|
2145
|
+
return_value_class = @translator.return_value_class(t, @method_id)
|
2146
|
+
if return_value_class
|
2147
|
+
return_value_class.each{|c| change = true if @return_value.is_also(c)}
|
2148
|
+
else
|
2149
|
+
dynamic = true
|
2150
|
+
break
|
2151
|
+
end
|
2152
|
+
end
|
2153
|
+
end
|
2154
|
+
if dynamic
|
2155
|
+
change |= @return_value.is_dynamic()
|
2156
|
+
end
|
2157
|
+
change
|
2158
|
+
end
|
2159
|
+
|
2160
|
+
def harmless?(recv_p)
|
2161
|
+
recv = @param[0].param_value
|
2162
|
+
bug() if recv.undefined?
|
2163
|
+
return false if recv.dynamic?
|
2164
|
+
recv.types.each do |t|
|
2165
|
+
return false unless @configuration.harmless?(t, @method_id, recv_p)
|
2166
|
+
end
|
2167
|
+
recv.types.each do |t|
|
2168
|
+
ok = @configuration.use_method_information(t, @method_id)
|
2169
|
+
return false unless ok
|
2170
|
+
end
|
2171
|
+
return true
|
2172
|
+
end
|
2173
|
+
|
2174
|
+
def side_effect?
|
2175
|
+
recv = @param[0].param_value
|
2176
|
+
bug() if recv.undefined?
|
2177
|
+
if recv.dynamic?
|
2178
|
+
se = true
|
2179
|
+
else
|
2180
|
+
se = false
|
2181
|
+
recv.types.each do |t|
|
2182
|
+
if @configuration.side_effect?(t, @method_id)
|
2183
|
+
se = true
|
2184
|
+
break
|
2185
|
+
end
|
2186
|
+
end
|
2187
|
+
end
|
2188
|
+
if !se
|
2189
|
+
recv.types.each do |t|
|
2190
|
+
ok = @configuration.use_method_information(t, @method_id)
|
2191
|
+
return true unless ok
|
2192
|
+
end
|
2193
|
+
end
|
2194
|
+
se
|
2195
|
+
end
|
2196
|
+
|
2197
|
+
def should_be_alive?
|
2198
|
+
side_effect?
|
2199
|
+
end
|
2200
|
+
|
2201
|
+
def propergate_exact_class(defs)
|
2202
|
+
if @return_value.class_exact?
|
2203
|
+
false
|
2204
|
+
elsif class_exact?
|
2205
|
+
@return_value.is_class_exact()
|
2206
|
+
true
|
2207
|
+
else
|
2208
|
+
false
|
2209
|
+
end
|
2210
|
+
end
|
2211
|
+
|
2212
|
+
### unboxing begin ###
|
2213
|
+
def unboxing_prelude()
|
2214
|
+
params = param_variables()
|
2215
|
+
recv, *args = *params
|
2216
|
+
bug() if recv.undefined?
|
2217
|
+
bug() unless instance_of?(InvokeIR)
|
2218
|
+
if recv.dynamic? || @configuration.force_dispatch_method?
|
2219
|
+
can_not_unbox()
|
2220
|
+
return
|
2221
|
+
else
|
2222
|
+
bug() if recv.types.empty?
|
2223
|
+
unless recv.types.size() == 1
|
2224
|
+
can_not_unbox()
|
2225
|
+
return
|
2226
|
+
end
|
2227
|
+
klass = recv.types[0]
|
2228
|
+
bug() unless klass.is_a?(ClassWrapper)
|
2229
|
+
unless klass.get_method_type(@method_id) == :cfunc
|
2230
|
+
can_not_unbox()
|
2231
|
+
return
|
2232
|
+
end
|
2233
|
+
unless @configuration.should_be_call_directly?(klass, @method_id)
|
2234
|
+
can_not_unbox()
|
2235
|
+
return
|
2236
|
+
end
|
2237
|
+
cfunc_argc = klass.get_cfunc_argc(@method_id)
|
2238
|
+
bug() unless cfunc_argc
|
2239
|
+
entries = SpecializeTable0[[MethodWrapper.new(klass, @method_id), argc]]
|
2240
|
+
unless entries
|
2241
|
+
can_not_unbox()
|
2242
|
+
return
|
2243
|
+
end
|
2244
|
+
entry = nil
|
2245
|
+
entries.each do |e|
|
2246
|
+
bug() unless e.size == 6
|
2247
|
+
name, t_args, t_result, exact_classes, can_unbox_result, can_unbox_param = e
|
2248
|
+
bug() unless args.size == t_args.size
|
2249
|
+
fin = true
|
2250
|
+
(args + [@return_value]).zip(t_args + [t_result]).each do |p, t|
|
2251
|
+
next if t.nil?
|
2252
|
+
unless p.is_just?(t)
|
2253
|
+
fin = false
|
2254
|
+
break
|
2255
|
+
end
|
2256
|
+
end
|
2257
|
+
if fin
|
2258
|
+
entry = e
|
2259
|
+
break
|
2260
|
+
end
|
2261
|
+
end
|
2262
|
+
unless entry
|
2263
|
+
can_not_unbox()
|
2264
|
+
return
|
2265
|
+
end
|
2266
|
+
name, t_args, t_result, exact_classes, can_unbox_result, can_unbox_param = entry
|
2267
|
+
if can_unbox_param
|
2268
|
+
params.each do |p|
|
2269
|
+
next unless p.box_unbox_undefined? # for guard
|
2270
|
+
p.can_unbox? ? p.can_unbox() : p.can_not_unbox()
|
2271
|
+
end
|
2272
|
+
else
|
2273
|
+
params.each{|p| p.can_not_unbox()}
|
2274
|
+
end
|
2275
|
+
if can_unbox_result
|
2276
|
+
@return_value.can_unbox()
|
2277
|
+
else
|
2278
|
+
@return_value.can_not_unbox()
|
2279
|
+
end
|
2280
|
+
return
|
2281
|
+
end
|
2282
|
+
end
|
2283
|
+
### unboxing end ###
|
2284
|
+
|
2285
|
+
def inlining_target?
|
2286
|
+
false # TODO
|
2287
|
+
end
|
2288
|
+
|
2289
|
+
private
|
2290
|
+
|
2291
|
+
def class_exact?
|
2292
|
+
params = param_variables()
|
2293
|
+
recv, *args = *params
|
2294
|
+
bug() if recv.undefined?
|
2295
|
+
if recv.dynamic? || @configuration.force_dispatch_method? || !instance_of?(InvokeIR)
|
2296
|
+
return false
|
2297
|
+
else
|
2298
|
+
bug() if recv.types.empty?
|
2299
|
+
unless recv.types.size() == 1
|
2300
|
+
return false
|
2301
|
+
end
|
2302
|
+
klass = recv.types[0]
|
2303
|
+
bug() unless klass.is_a?(ClassWrapper)
|
2304
|
+
unless klass.get_method_type(@method_id) == :cfunc
|
2305
|
+
return false
|
2306
|
+
end
|
2307
|
+
unless @configuration.should_be_call_directly?(klass, @method_id)
|
2308
|
+
return false
|
2309
|
+
end
|
2310
|
+
cfunc_argc = klass.get_cfunc_argc(@method_id)
|
2311
|
+
bug() unless cfunc_argc
|
2312
|
+
entries = SpecializeTable0[[MethodWrapper.new(klass, @method_id), argc]]
|
2313
|
+
return false unless entries
|
2314
|
+
entry = nil
|
2315
|
+
entries.each do |e|
|
2316
|
+
bug() unless e.size == 6
|
2317
|
+
name, t_args, t_result, exact_classes, can_unbox_result, can_unbox_param = e
|
2318
|
+
bug() unless args.size == t_args.size
|
2319
|
+
fin = true
|
2320
|
+
(args + [@return_value]).zip(t_args + [t_result]).each do |p, t|
|
2321
|
+
next if t.nil?
|
2322
|
+
unless p.is_just?(t)
|
2323
|
+
fin = false
|
2324
|
+
break
|
2325
|
+
end
|
2326
|
+
end
|
2327
|
+
if fin
|
2328
|
+
entry = e
|
2329
|
+
break
|
2330
|
+
end
|
2331
|
+
end
|
2332
|
+
unless entry
|
2333
|
+
return false
|
2334
|
+
end
|
2335
|
+
name, t_args, t_result, exact_classes, can_unbox_result, can_unbox_param = entry
|
2336
|
+
return false unless exact_classes
|
2337
|
+
if @return_value.is_just?(exact_classes)
|
2338
|
+
return true
|
2339
|
+
else
|
2340
|
+
return false
|
2341
|
+
end
|
2342
|
+
end
|
2343
|
+
end
|
2344
|
+
end
|
2345
|
+
end
|
2346
|
+
end
|
2347
|
+
end
|
2348
|
+
|