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.
Files changed (58) hide show
  1. data/README +578 -0
  2. data/README.en +256 -0
  3. data/bin/CastOff +145 -0
  4. data/cast_off.gemspec +25 -0
  5. data/ext/cast_off/cast_off.c.rb +1386 -0
  6. data/ext/cast_off/cast_off.h +24 -0
  7. data/ext/cast_off/depend +70 -0
  8. data/ext/cast_off/extconf.rb +19 -0
  9. data/ext/cast_off/generated_c_include/inline_api.h +507 -0
  10. data/ext/cast_off/generated_c_include/iter_api.h +595 -0
  11. data/ext/cast_off/generated_c_include/unbox_api.h.rb +76 -0
  12. data/ext/cast_off/generated_c_include/vm_api.h +751 -0
  13. data/ext/cast_off/ruby_source/atomic.h +56 -0
  14. data/ext/cast_off/ruby_source/constant.h +34 -0
  15. data/ext/cast_off/ruby_source/debug.h +41 -0
  16. data/ext/cast_off/ruby_source/eval_intern.h +234 -0
  17. data/ext/cast_off/ruby_source/gc.h +98 -0
  18. data/ext/cast_off/ruby_source/id.h +175 -0
  19. data/ext/cast_off/ruby_source/insns.inc +179 -0
  20. data/ext/cast_off/ruby_source/insns_info.inc +695 -0
  21. data/ext/cast_off/ruby_source/internal.h +227 -0
  22. data/ext/cast_off/ruby_source/iseq.h +125 -0
  23. data/ext/cast_off/ruby_source/manual_update.h +135 -0
  24. data/ext/cast_off/ruby_source/method.h +105 -0
  25. data/ext/cast_off/ruby_source/node.h +503 -0
  26. data/ext/cast_off/ruby_source/thread_pthread.h +51 -0
  27. data/ext/cast_off/ruby_source/thread_win32.h +40 -0
  28. data/ext/cast_off/ruby_source/vm_core.h +756 -0
  29. data/ext/cast_off/ruby_source/vm_exec.h +184 -0
  30. data/ext/cast_off/ruby_source/vm_insnhelper.c +1748 -0
  31. data/ext/cast_off/ruby_source/vm_insnhelper.h +220 -0
  32. data/ext/cast_off/ruby_source/vm_opts.h +51 -0
  33. data/lib/cast_off.rb +15 -0
  34. data/lib/cast_off/compile.rb +629 -0
  35. data/lib/cast_off/compile/basicblock.rb +144 -0
  36. data/lib/cast_off/compile/cfg.rb +391 -0
  37. data/lib/cast_off/compile/code_manager.rb +284 -0
  38. data/lib/cast_off/compile/configuration.rb +2368 -0
  39. data/lib/cast_off/compile/dependency.rb +240 -0
  40. data/lib/cast_off/compile/information.rb +775 -0
  41. data/lib/cast_off/compile/instruction.rb +446 -0
  42. data/lib/cast_off/compile/ir/call_ir.rb +2348 -0
  43. data/lib/cast_off/compile/ir/guard_ir.rb +423 -0
  44. data/lib/cast_off/compile/ir/jump_ir.rb +223 -0
  45. data/lib/cast_off/compile/ir/operand.rb +934 -0
  46. data/lib/cast_off/compile/ir/param_ir.rb +98 -0
  47. data/lib/cast_off/compile/ir/return_ir.rb +92 -0
  48. data/lib/cast_off/compile/ir/simple_ir.rb +808 -0
  49. data/lib/cast_off/compile/ir/sub_ir.rb +212 -0
  50. data/lib/cast_off/compile/iseq.rb +454 -0
  51. data/lib/cast_off/compile/method_information.rb +1384 -0
  52. data/lib/cast_off/compile/namespace/namespace.rb +556 -0
  53. data/lib/cast_off/compile/namespace/uuid.rb +323 -0
  54. data/lib/cast_off/compile/stack.rb +65 -0
  55. data/lib/cast_off/compile/translator.rb +1562 -0
  56. data/lib/cast_off/suggestion.rb +98 -0
  57. data/lib/cast_off/util.rb +58 -0
  58. 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
+