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,220 @@
1
+ /**********************************************************************
2
+
3
+ insnhelper.h - helper macros to implement each instructions
4
+
5
+ $Author$
6
+ created at: 04/01/01 15:50:34 JST
7
+
8
+ Copyright (C) 2004-2007 Koichi Sasada
9
+
10
+ **********************************************************************/
11
+
12
+ #ifndef RUBY_INSNHELPER_H
13
+ #define RUBY_INSNHELPER_H
14
+
15
+ /**
16
+ * VM Debug Level
17
+ *
18
+ * debug level:
19
+ * 0: no debug output
20
+ * 1: show instruction name
21
+ * 2: show stack frame when control stack frame is changed
22
+ * 3: show stack status
23
+ * 4: show register
24
+ * 5:
25
+ * 10: gc check
26
+ */
27
+
28
+ #ifndef VMDEBUG
29
+ #define VMDEBUG 0
30
+ #endif
31
+
32
+ #if 0
33
+ #undef VMDEBUG
34
+ #define VMDEBUG 3
35
+ #endif
36
+
37
+ enum {
38
+ BOP_PLUS,
39
+ BOP_MINUS,
40
+ BOP_MULT,
41
+ BOP_DIV,
42
+ BOP_MOD,
43
+ BOP_EQ,
44
+ BOP_EQQ,
45
+ BOP_LT,
46
+ BOP_LE,
47
+ BOP_LTLT,
48
+ BOP_AREF,
49
+ BOP_ASET,
50
+ BOP_LENGTH,
51
+ BOP_SIZE,
52
+ BOP_SUCC,
53
+ BOP_GT,
54
+ BOP_GE,
55
+ BOP_NOT,
56
+ BOP_NEQ,
57
+
58
+ BOP_LAST_
59
+ };
60
+
61
+ extern char ruby_vm_redefined_flag[BOP_LAST_];
62
+ extern VALUE ruby_vm_const_missing_count;
63
+
64
+
65
+ /**********************************************************/
66
+ /* deal with stack */
67
+ /**********************************************************/
68
+
69
+ #define PUSH(x) (SET_SV(x), INC_SP(1))
70
+ #define TOPN(n) (*(GET_SP()-(n)-1))
71
+ #define POPN(n) (DEC_SP(n))
72
+ #define POP() (DEC_SP(1))
73
+ #define STACK_ADDR_FROM_TOP(n) (GET_SP()-(n))
74
+
75
+ #define GET_TOS() (tos) /* dummy */
76
+
77
+ /**********************************************************/
78
+ /* deal with registers */
79
+ /**********************************************************/
80
+
81
+ #define REG_CFP (reg_cfp)
82
+ #define REG_PC (REG_CFP->pc)
83
+ #define REG_SP (REG_CFP->sp)
84
+ #define REG_LFP (REG_CFP->lfp)
85
+ #define REG_DFP (REG_CFP->dfp)
86
+
87
+ #define RESTORE_REGS() do { \
88
+ REG_CFP = th->cfp; \
89
+ } while (0)
90
+
91
+ #define REG_A reg_a
92
+ #define REG_B reg_b
93
+
94
+ #ifdef COLLECT_USAGE_ANALYSIS
95
+ #define USAGE_ANALYSIS_REGISTER_HELPER(a, b, v) \
96
+ (USAGE_ANALYSIS_REGISTER((a), (b)), (v))
97
+ #else
98
+ #define USAGE_ANALYSIS_REGISTER_HELPER(a, b, v) (v)
99
+ #endif
100
+
101
+ /* PC */
102
+ #define GET_PC() (USAGE_ANALYSIS_REGISTER_HELPER(0, 0, REG_PC))
103
+ #define SET_PC(x) (REG_PC = (USAGE_ANALYSIS_REGISTER_HELPER(0, 1, (x))))
104
+ #define GET_CURRENT_INSN() (*GET_PC())
105
+ #define GET_OPERAND(n) (GET_PC()[(n)])
106
+ #define ADD_PC(n) (SET_PC(REG_PC + (n)))
107
+
108
+ #define GET_PC_COUNT() (REG_PC - GET_ISEQ()->iseq_encoded)
109
+ #define JUMP(dst) (REG_PC += (dst))
110
+
111
+ /* FP */
112
+ #define GET_CFP() (USAGE_ANALYSIS_REGISTER_HELPER(2, 0, REG_CFP))
113
+ #define GET_LFP() (USAGE_ANALYSIS_REGISTER_HELPER(3, 0, REG_LFP))
114
+ #define SET_LFP(x) (REG_LFP = (USAGE_ANALYSIS_REGISTER_HELPER(3, 1, (x))))
115
+ #define GET_DFP() (USAGE_ANALYSIS_REGISTER_HELPER(4, 0, REG_DFP))
116
+ #define SET_DFP(x) (REG_DFP = (USAGE_ANALYSIS_REGISTER_HELPER(4, 1, (x))))
117
+
118
+ /* SP */
119
+ #define GET_SP() (USAGE_ANALYSIS_REGISTER_HELPER(1, 0, REG_SP))
120
+ #define SET_SP(x) (REG_SP = (USAGE_ANALYSIS_REGISTER_HELPER(1, 1, (x))))
121
+ #define INC_SP(x) (REG_SP += (USAGE_ANALYSIS_REGISTER_HELPER(1, 1, (x))))
122
+ #define DEC_SP(x) (REG_SP -= (USAGE_ANALYSIS_REGISTER_HELPER(1, 1, (x))))
123
+ #define SET_SV(x) (*GET_SP() = (x))
124
+ /* set current stack value as x */
125
+
126
+ #define GET_SP_COUNT() (REG_SP - th->stack)
127
+
128
+ /* instruction sequence C struct */
129
+ #define GET_ISEQ() (GET_CFP()->iseq)
130
+
131
+ /**********************************************************/
132
+ /* deal with variables */
133
+ /**********************************************************/
134
+
135
+ #define GET_PREV_DFP(dfp) ((VALUE *)((dfp)[0] & ~0x03))
136
+
137
+ #define GET_GLOBAL(entry) rb_gvar_get((struct rb_global_entry*)(entry))
138
+ #define SET_GLOBAL(entry, val) rb_gvar_set((struct rb_global_entry*)(entry), (val))
139
+
140
+ #define GET_CONST_INLINE_CACHE(dst) ((IC) * (GET_PC() + (dst) + 2))
141
+
142
+ /**********************************************************/
143
+ /* deal with values */
144
+ /**********************************************************/
145
+
146
+ #define GET_SELF() (USAGE_ANALYSIS_REGISTER_HELPER(5, 0, GET_CFP()->self))
147
+
148
+ /**********************************************************/
149
+ /* deal with control flow 2: method/iterator */
150
+ /**********************************************************/
151
+
152
+ #define COPY_CREF(c1, c2) do { \
153
+ NODE *__tmp_c2 = (c2); \
154
+ (c1)->nd_clss = __tmp_c2->nd_clss; \
155
+ (c1)->nd_visi = __tmp_c2->nd_visi;\
156
+ (c1)->nd_next = __tmp_c2->nd_next; \
157
+ if (__tmp_c2->flags & NODE_FL_CREF_PUSHED_BY_EVAL) { \
158
+ (c1)->flags |= NODE_FL_CREF_PUSHED_BY_EVAL; \
159
+ } \
160
+ } while (0)
161
+
162
+ #define CALL_METHOD(num, blockptr, flag, id, me, recv) do { \
163
+ VALUE v = vm_call_method(th, GET_CFP(), (num), (blockptr), (flag), (id), (me), (recv)); \
164
+ if (v == Qundef) { \
165
+ RESTORE_REGS(); \
166
+ NEXT_INSN(); \
167
+ } \
168
+ else { \
169
+ val = v; \
170
+ } \
171
+ } while (0)
172
+
173
+ #define GET_BLOCK_PTR() \
174
+ ((rb_block_t *)(GC_GUARDED_PTR_REF(GET_LFP()[0] & \
175
+ ((GET_LFP()[0] & 0x02) - 0x02))))
176
+
177
+ /**********************************************************/
178
+ /* deal with control flow 3: exception */
179
+ /**********************************************************/
180
+
181
+
182
+ /**********************************************************/
183
+ /* others */
184
+ /**********************************************************/
185
+
186
+ /* optimize insn */
187
+ #define FIXNUM_2_P(a, b) ((a) & (b) & 1)
188
+ #define BASIC_OP_UNREDEFINED_P(op) (LIKELY(ruby_vm_redefined_flag[(op)] == 0))
189
+ #define HEAP_CLASS_OF(obj) RBASIC(obj)->klass
190
+
191
+ #ifndef USE_IC_FOR_SPECIALIZED_METHOD
192
+ #define USE_IC_FOR_SPECIALIZED_METHOD 1
193
+ #endif
194
+
195
+ #if USE_IC_FOR_SPECIALIZED_METHOD
196
+
197
+ #define CALL_SIMPLE_METHOD(num, id, recv) do { \
198
+ VALUE klass = CLASS_OF(recv); \
199
+ CALL_METHOD((num), 0, 0, (id), vm_method_search((id), klass, ic), (recv)); \
200
+ } while (0)
201
+
202
+ #else
203
+
204
+ #define CALL_SIMPLE_METHOD(num, id, recv) do { \
205
+ VALUE klass = CLASS_OF(recv); \
206
+ CALL_METHOD((num), 0, 0, (id), rb_method_entry(klass, (id)), (recv)); \
207
+ } while (0)
208
+
209
+ #endif
210
+
211
+ static VALUE ruby_vm_global_state_version = 1;
212
+
213
+ #define GET_VM_STATE_VERSION() (ruby_vm_global_state_version)
214
+ #define INC_VM_STATE_VERSION() do { \
215
+ ruby_vm_global_state_version = (ruby_vm_global_state_version + 1); \
216
+ if (ruby_vm_global_state_version == 0) vm_clear_all_cache(); \
217
+ } while (0)
218
+ static void vm_clear_all_cache(void);
219
+
220
+ #endif /* RUBY_INSNHELPER_H */
@@ -0,0 +1,51 @@
1
+ /*-*-c-*-*/
2
+ /**********************************************************************
3
+
4
+ vm_opts.h - VM optimize option
5
+
6
+ $Author$
7
+
8
+ Copyright (C) 2004-2007 Koichi Sasada
9
+
10
+ **********************************************************************/
11
+
12
+
13
+ #ifndef RUBY_VM_OPTS_H
14
+ #define RUBY_VM_OPTS_H
15
+
16
+ /* Compile options.
17
+ * You can change these options at runtime by VM::CompileOption.
18
+ * Following definitions are default values.
19
+ */
20
+
21
+ #define OPT_TRACE_INSTRUCTION 1
22
+ #define OPT_TAILCALL_OPTIMIZATION 0
23
+ #define OPT_PEEPHOLE_OPTIMIZATION 1
24
+ #define OPT_SPECIALISED_INSTRUCTION 1
25
+ #define OPT_INLINE_CONST_CACHE 1
26
+
27
+
28
+ /* Build Options.
29
+ * You can't change these options at runtime.
30
+ */
31
+
32
+ /* C compiler depend */
33
+ #define OPT_DIRECT_THREADED_CODE 1
34
+ #define OPT_TOKEN_THREADED_CODE 0
35
+ #define OPT_CALL_THREADED_CODE 0
36
+
37
+ /* VM running option */
38
+ #define OPT_CHECKED_RUN 1
39
+ #define OPT_INLINE_METHOD_CACHE 1
40
+ #define OPT_BLOCKINLINING 0
41
+
42
+ /* architecture independent, affects generated code */
43
+ #define OPT_OPERANDS_UNIFICATION 0
44
+ #define OPT_INSTRUCTIONS_UNIFICATION 0
45
+ #define OPT_UNIFY_ALL_COMBINATION 0
46
+ #define OPT_STACK_CACHING 0
47
+
48
+ /* misc */
49
+ #define SUPPORT_JOKE 0
50
+
51
+ #endif /* RUBY_VM_OPTS_H */
data/lib/cast_off.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'cast_off.so'
2
+ require 'cast_off/util'
3
+ require 'cast_off/suggestion'
4
+ require 'cast_off/compile/dependency'
5
+ require 'cast_off/compile/method_information'
6
+ require 'cast_off/compile/configuration'
7
+ require 'cast_off/compile/code_manager'
8
+ require 'cast_off/compile'
9
+
10
+ module CastOff
11
+ extend CastOff::Util
12
+ extend CastOff::Compiler
13
+ end
14
+ CastOff.clear_settings()
15
+
@@ -0,0 +1,629 @@
1
+ # coding=utf-8
2
+
3
+ module CastOff
4
+ module Compiler
5
+ include CastOff::Util
6
+
7
+ DefaultSuggestionIO = Object.new
8
+ DefaultSuggestionIO.extend(CastOff::Util)
9
+ def DefaultSuggestionIO.puts(*msg)
10
+ vlog(msg)
11
+ end
12
+ @@suggestion_io = DefaultSuggestionIO
13
+ def set_suggestion_io(io)
14
+ @@suggestion_io = io
15
+ end
16
+
17
+ def verbose(b)
18
+ CastOff::Util.set_verbose_mode(b)
19
+ end
20
+
21
+ def clear()
22
+ CodeManager.clear()
23
+ end
24
+
25
+ @@blacklist = [
26
+ ]
27
+
28
+ @@autoload_proc = nil
29
+ def autoload()
30
+ return false if autocompile_running?
31
+ if autoload_running?
32
+ @@autoload_proc.call()
33
+ return true
34
+ end
35
+ return true if load()
36
+
37
+ compiled = nil
38
+ @@autoload_proc = lambda {
39
+ compiled = CodeManager.load_autocompiled() unless compiled
40
+ return false unless compiled
41
+ fin = __load(compiled)
42
+ hook_class_definition_end(nil) if fin
43
+ fin
44
+ }
45
+ hook_class_definition_end(@@autoload_proc)
46
+ true
47
+ end
48
+
49
+ def load(force = false)
50
+ return @@autoload_proc.call() if autoload_running? && !force
51
+ compiled = CodeManager.load_autocompiled()
52
+ return false unless compiled
53
+ __load(compiled)
54
+ end
55
+
56
+ @@compilation_threshold = 100
57
+ def compilation_threshold=(num)
58
+ raise(ArgumentError.new("first argument should be Integer")) unless num.is_a?(Integer)
59
+ raise(ArgumentError.new("threshold should be more than 0")) unless num >= 0
60
+ @@compilation_threshold = num
61
+ end
62
+
63
+ @@autocompile_proc = nil
64
+ @@compile_auto_incremental = true
65
+ def autocompile()
66
+ return false if autoload_running?
67
+ return true if autocompile_running?
68
+ class_table = {}
69
+ bind_table = {}
70
+ location_table = {}
71
+ cinfo_table = {}
72
+ compiled = []
73
+ @@autocompile_proc = lambda {|event, file, line, mid, bind, klass, cinfo|
74
+ return unless file
75
+ return if line < 0
76
+ return if event != 'call'
77
+ return if file =~ /\(/
78
+
79
+ # TODO should handle singleton class
80
+
81
+ # trace method invocation count
82
+ method_table = class_table[klass]
83
+ unless method_table
84
+ method_table = Hash.new(0)
85
+ class_table[klass] = method_table
86
+ end
87
+ count = (method_table[mid] += 1)
88
+ if count == 1
89
+ bind_table[[klass, mid]] = bind
90
+ location_table[[klass, mid]] = [File.expand_path(file), line]
91
+ end
92
+ if cinfo
93
+ table = (cinfo_table[[klass, mid]] ||= {})
94
+ table[cinfo] = true
95
+ end
96
+ if count == @@compilation_threshold && @@compile_auto_incremental
97
+ compiled << __autocompile(klass, mid, bind_table, location_table, compiled.size)
98
+ end
99
+ }
100
+ hook_method_invocation(@@autocompile_proc)
101
+ at_exit do
102
+ hook_method_invocation(nil) # clear trace
103
+ unless @@compile_auto_incremental
104
+ targets = []
105
+ class_table.each do |klass, method_table|
106
+ method_table.each do |mid, count|
107
+ next unless count >= @@compilation_threshold
108
+ targets << [klass, mid, count]
109
+ end
110
+ end
111
+ targets = sort_targets(targets, cinfo_table)
112
+ targets.each_with_index do |(klass, mid, count), index|
113
+ dlog("#{count}: #{klass} #{mid}")
114
+ compiled << __autocompile(klass, mid, bind_table, location_table, index)
115
+ end
116
+ end
117
+ compiled.compact!
118
+ CodeManager.dump_auto_compiled(compiled)
119
+ end
120
+ true
121
+ end
122
+
123
+ def compile_instance_methods(klass, bind = nil, skip = [])
124
+ raise ArgumentError.new("first argument should be Class") unless klass.instance_of?(Class)
125
+ raise ArgumentError.new("second argument should be Binding") unless !bind || bind.instance_of?(Binding)
126
+ logger = self
127
+ klass.class_eval do
128
+ instance_methods(false).each_with_index do |mid, idx|
129
+ next if skip.include?(mid)
130
+ args = [klass, mid, bind]
131
+ begin
132
+ CastOff.compile(*args.compact())
133
+ logger.vlog("#{idx}: compile #{mid}")
134
+ rescue UnsupportedError => e
135
+ logger.vlog("#{idx}: failed to compile #{self}##{mid} (#{e.message})")
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ @@original_instance_method_iseq = {}
142
+ def delete_original_instance_method_iseq(target, mid)
143
+ t = override_target(target, mid)
144
+ @@original_instance_method_iseq.delete([t, mid])
145
+ end
146
+
147
+ def compile(target, mid, bind_or_typemap = nil, typemap = nil)
148
+ case target
149
+ when Class, Module
150
+ # ok
151
+ else
152
+ raise(ArgumentError.new("first argument should be Class or Module"))
153
+ end
154
+ mid, bind, typemap = parse_arguments(mid, bind_or_typemap, typemap)
155
+ t = override_target(target, mid)
156
+ iseq = @@original_instance_method_iseq[[t, mid]] || get_iseq(target, mid, false)
157
+ manager, configuration, suggestion = compile_iseq(iseq, mid, typemap, false, bind)
158
+ manager.compilation_target_is_a(t, mid, false)
159
+ set_direct_call(target, mid, target.instance_of?(Class) ? :class : :module, manager, configuration)
160
+ load_binary(manager, configuration, suggestion, iseq, bind)
161
+ t = override_target(target, mid)
162
+ dlog("override target of #{target}##{mid} is #{t}")
163
+ __send__("register_method_#{manager.signiture}", t)
164
+ @@original_instance_method_iseq[[t, mid]] = iseq
165
+ @@manager_table[manager.signiture] = manager
166
+ true
167
+ end
168
+
169
+ @@original_singleton_method_iseq = {}
170
+ def delete_original_singleton_method_iseq(obj, mid)
171
+ @@original_singleton_method_iseq.delete([obj, mid])
172
+ end
173
+
174
+ def compile_singleton_method(obj, mid, bind_or_typemap = nil, typemap = nil)
175
+ mid, bind, typemap = parse_arguments(mid, bind_or_typemap, typemap)
176
+ iseq = @@original_singleton_method_iseq[[obj, mid]] || get_iseq(obj, mid, true)
177
+ manager, configuration, suggestion = compile_iseq(iseq, mid, typemap, false, bind)
178
+ manager.compilation_target_is_a(obj, mid, true)
179
+ set_direct_call(obj, mid, :singleton, manager, configuration)
180
+ load_binary(manager, configuration, suggestion, iseq, bind)
181
+ __send__("register_singleton_method_#{manager.signiture}", obj)
182
+ @@original_singleton_method_iseq[[obj, mid]] = iseq
183
+ @@manager_table[manager.signiture] = manager
184
+ true
185
+ end
186
+
187
+ @@loaded_binary = {}
188
+ def execute(typemap = nil, &block)
189
+ raise(ArgumentError.new('no block given')) unless block
190
+ iseq = get_iseq_from_block(block)
191
+ key = iseq.__id__
192
+ if !@@loaded_binary[key]
193
+ bind = block.binding
194
+ manager, configuration, suggestion = compile_iseq(iseq, nil, typemap, false, bind)
195
+ load_binary(manager, configuration, suggestion, iseq, bind)
196
+ @@loaded_binary[key] = manager.signiture
197
+ end
198
+ sign = @@loaded_binary[key]
199
+ recv = get_caller()
200
+ __send__(sign, recv)
201
+ end
202
+
203
+ private
204
+
205
+ def execute_no_hook()
206
+ bug() unless block_given?
207
+ begin
208
+ hook_m = hook_method_invocation(nil)
209
+ hook_c = hook_class_definition_end(nil)
210
+ yield
211
+ ensure
212
+ if hook_m
213
+ bug() unless autocompile_running?
214
+ hook_method_invocation(@@autocompile_proc)
215
+ end
216
+ if hook_c
217
+ bug() unless autoload_running?
218
+ hook_class_definition_end(@@autoload_proc)
219
+ end
220
+ end
221
+ end
222
+
223
+ def compile_iseq(iseq, mid, typemap, is_proc, bind)
224
+ filepath, line_no = *iseq.to_a.slice(7, 2)
225
+ raise(UnsupportedError.new(<<-EOS)) unless filepath && File.exist?(filepath)
226
+
227
+ Currently, CastOff cannot compile method which source file is not exist.
228
+ #{filepath.nil? ? 'nil' : filepath} is not exist.
229
+ EOS
230
+ manager = CodeManager.new(filepath, line_no)
231
+ suggestion = Suggestion.new(iseq, @@suggestion_io)
232
+ configuration = nil
233
+ manager.do_atomically() do
234
+ execute_no_hook() do
235
+ configuration = __compile(iseq, manager, typemap || {}, mid, is_proc, bind, suggestion)
236
+ end
237
+ end
238
+ bug() unless configuration.instance_of?(Configuration)
239
+ [manager, configuration, suggestion]
240
+ end
241
+
242
+ def parse_arguments(mid, bind_or_typemap, typemap)
243
+ case mid
244
+ when Symbol
245
+ # nothing to do
246
+ when String
247
+ mid = mid.intern
248
+ else
249
+ raise(ArgumentError.new('method name should be Symbol or String'))
250
+ end
251
+ case bind_or_typemap
252
+ when Binding
253
+ bind = bind_or_typemap
254
+ when Hash
255
+ raise(ArgumentError.new("Invalid arugments")) if typemap
256
+ bind = nil
257
+ typemap = bind_or_typemap
258
+ when NilClass
259
+ # nothing to do
260
+ bind = nil
261
+ else
262
+ raise(ArgumentError.new("third argument should be Binding or Hash"))
263
+ end
264
+ [mid, bind, typemap]
265
+ end
266
+
267
+ def union_base_configuration(conf, manager)
268
+ if CastOff.use_base_configuration?
269
+ u = manager.load_base_configuration()
270
+ unless u
271
+ bind = conf.bind
272
+ u = Configuration.new({}, bind ? bind.bind : nil)
273
+ manager.save_base_configuration(u)
274
+ end
275
+ conf.union(u)
276
+ end
277
+ end
278
+
279
+ @@manager_table = {}
280
+ def re_compile(signiture, sampling_table)
281
+ manager = @@manager_table[signiture]
282
+ return false unless manager
283
+ bug() unless manager.instance_of?(CodeManager)
284
+ reciever_result, return_value_result = parse_sampling_table(sampling_table)
285
+ update_p = update_base_configuration(manager, reciever_result, return_value_result)
286
+ compilation_target = manager.compilation_target
287
+ bug() unless compilation_target
288
+ target = compilation_target.target_object
289
+ mid = compilation_target.method_id
290
+ singleton = compilation_target.singleton_method?
291
+ vlog("re-compile(#{target}#{singleton ? '.' : '#'}#{mid}): update_p = #{update_p}, reciever_result = #{reciever_result}, return_value_result = #{return_value_result}")
292
+ return false unless update_p
293
+ ann = manager.load_annotation() || {}
294
+ bug() unless ann.instance_of?(Hash)
295
+ manager.version_up()
296
+ begin
297
+ __send__(singleton ? 'compile_singleton_method' : 'compile', target, mid, ann)
298
+ rescue => e
299
+ vlog("re-compilation failed: #{target}#{singleton ? '.' : '#'}#{mid}")
300
+ end
301
+ true
302
+ end
303
+
304
+ class ReCompilation < StandardError; end
305
+
306
+ def __compile(iseq, manager, annotation, mid, is_proc, bind, suggestion)
307
+ if reuse_compiled_binary? && !manager.target_file_updated?
308
+ # already compiled
309
+ if CastOff.development? || !CastOff.skip_configuration_check? || manager.last_configuration_enabled_development?
310
+ conf = Configuration.new(annotation, bind)
311
+ union_base_configuration(conf, manager)
312
+ last_conf = manager.load_last_configuration()
313
+ if last_conf && conf == last_conf
314
+ dlog("reuse compiled binary")
315
+ return last_conf
316
+ end
317
+ else
318
+ dlog("reuse compiled binary")
319
+ last_conf = manager.load_last_configuration()
320
+ if last_conf
321
+ return last_conf
322
+ end
323
+ conf = Configuration.new(annotation, bind)
324
+ union_base_configuration(conf, manager)
325
+ end
326
+ else
327
+ conf = Configuration.new(annotation, bind)
328
+ union_base_configuration(conf, manager)
329
+ end
330
+ vlog("use configuration #{conf}")
331
+
332
+ require 'cast_off/compile/namespace/uuid'
333
+ require 'cast_off/compile/namespace/namespace'
334
+ require 'cast_off/compile/instruction'
335
+ require 'cast_off/compile/iseq'
336
+ require 'cast_off/compile/ir/simple_ir'
337
+ require 'cast_off/compile/ir/operand'
338
+ require 'cast_off/compile/ir/sub_ir'
339
+ require 'cast_off/compile/ir/jump_ir'
340
+ require 'cast_off/compile/ir/param_ir'
341
+ require 'cast_off/compile/ir/call_ir'
342
+ require 'cast_off/compile/ir/return_ir'
343
+ require 'cast_off/compile/ir/guard_ir'
344
+ require 'cast_off/compile/translator'
345
+ require 'cast_off/compile/cfg'
346
+ require 'cast_off/compile/basicblock'
347
+ require 'cast_off/compile/stack'
348
+ require 'cast_off/compile/information'
349
+ conf.validate()
350
+ bug() unless conf
351
+ dep = Dependency.new()
352
+ block_inlining = true
353
+ while true
354
+ begin
355
+ translator = Translator.new(iseq, conf, mid, is_proc, block_inlining, suggestion, dep, manager)
356
+ c_source = translator.to_c()
357
+ break
358
+ rescue ReCompilation
359
+ bug() unless block_inlining
360
+ block_inlining = false # FIXME get re-compilation type from exception object
361
+ dlog("failed to inline block...")
362
+ end
363
+ end
364
+ conf.use_method_frame(!block_inlining)
365
+ bug() unless c_source
366
+ translator.suggest()
367
+ manager.compile_c_source(c_source, conf, dep)
368
+ manager.save_annotation(annotation)
369
+ manager.dump_development_mark(conf)
370
+ conf
371
+ end
372
+
373
+ def __load(compiled)
374
+ begin
375
+ compiled.dup.each do |entry|
376
+ klass, mid, singleton, file, line, bind = entry
377
+ if @@blacklist.include?(mid)
378
+ compiled.delete(entry)
379
+ next
380
+ end
381
+ bind = bind.bind if bind
382
+ entry.pop # release BindingWrapper
383
+ if singleton
384
+ iseq = @@original_singleton_method_iseq[[klass, mid]] || get_iseq(klass, mid, true)
385
+ else
386
+ t = override_target(klass, mid)
387
+ iseq = @@original_instance_method_iseq[[t, mid]] || get_iseq(klass, mid, false)
388
+ end
389
+ f, l = *iseq.to_a.slice(7, 2)
390
+ if f == file && l == line
391
+ begin
392
+ if singleton
393
+ CastOff.compile_singleton_method(klass, mid, bind)
394
+ else
395
+ CastOff.compile(klass, mid, bind)
396
+ end
397
+ vlog("load #{klass}##{mid}")
398
+ rescue UnsupportedError
399
+ vlog("unsupported #{klass}##{mid}")
400
+ end
401
+ else
402
+ dlog("iseq.filepath = #{f}, file = #{file}\niseq.line = #{l}, line = #{line}")
403
+ end
404
+ compiled.delete(entry)
405
+ end
406
+ if compiled.empty?
407
+ vlog("---------- load finish ----------")
408
+ true
409
+ else
410
+ false
411
+ end
412
+ rescue => e
413
+ vlog("catch exception #{e.class}: #{e}\n#{e.backtrace.join("\n")}")
414
+ false
415
+ end
416
+ end
417
+
418
+ def autocompile_running?
419
+ !!@@autocompile_proc
420
+ end
421
+
422
+ def autoload_running?
423
+ !!@@autoload_proc
424
+ end
425
+
426
+ def __autocompile(klass, mid, bind_table, location_table, index)
427
+ return nil unless klass.instance_of?(Class) || klass.instance_of?(Module) # FIXME
428
+ return nil if klass.name =~ /CastOff/ # ここで弾いておかないと、__compile の require で __load が走る。
429
+ # Namespace のほうはあらかじめ require しておくことで回避。
430
+ # Namespace 以外は CastOff を含むので問題無し。
431
+ if klass.instance_methods(false).include?(mid) || klass.private_instance_methods(false).include?(mid)
432
+ singleton = false
433
+ else
434
+ return nil unless klass.singleton_methods(false).include?(mid)
435
+ singleton = true
436
+ end
437
+ begin
438
+ bind = bind_table[[klass, mid]]
439
+ if singleton
440
+ CastOff.compile_singleton_method(klass, mid, bind)
441
+ else
442
+ CastOff.compile(klass, mid, bind)
443
+ end
444
+ location = location_table[[klass, mid]]
445
+ begin
446
+ Marshal.dump(klass)
447
+ rescue TypeError => e
448
+ vlog("failed to marshal dump #{klass}: #{e.message}")
449
+ return nil
450
+ end
451
+ vlog("#{index}: compile #{klass}#{singleton ? '.' : '#'}#{mid}")
452
+ [klass, mid, singleton] + location + [Configuration::BindingWrapper.new(bind)] # klass, mid, file, line, binding
453
+ rescue UnsupportedError => e
454
+ vlog("#{index}: failed to compile #{klass}#{singleton ? '.' : '#'}#{mid} (#{e.message})")
455
+ nil
456
+ end
457
+ end
458
+
459
+ def __sort_targets(entry, targets, cinfo_table)
460
+ result = []
461
+ unless cinfo_table[entry]
462
+ result << entry
463
+ dlog("<< #{entry}: nil")
464
+ return result # FIXME
465
+ end
466
+
467
+ targets.each do |te|
468
+ bug() if entry == te
469
+ next unless cinfo_table[te]
470
+ next unless cinfo_table[te].keys.include?(entry) # entry calls te
471
+ next if cinfo_table[entry].keys.include?(te) # cycle
472
+ targets.delete(te)
473
+ result += __sort_targets(te, targets, cinfo_table)
474
+ end
475
+ dlog("<< #{entry}: #{cinfo_table[entry].keys}")
476
+ result << entry
477
+ result
478
+ end
479
+
480
+ def sort_targets(targets, cinfo_table)
481
+ result = []
482
+ counts = {}
483
+ targets = targets.sort{|v0, v1| v1.last <=> v0.last}
484
+ targets.each{|klass, mid, count| counts[[klass, mid]] = count}
485
+ targets = targets.map{|klass, mid, count| [klass, mid]}
486
+ until targets.empty?
487
+ entry = targets.shift
488
+ result += __sort_targets(entry, targets, cinfo_table)
489
+ end
490
+ result.map!{|entry| entry + [counts[entry]]}
491
+ bug() unless result.size == counts.size
492
+ result
493
+ end
494
+
495
+ def parse_sampling_table(sampling_table)
496
+ reciever_result = {}
497
+ return_value_result = {}
498
+ sampling_table.each do |(key0, val0)|
499
+ case key0
500
+ when Symbol
501
+ bug() unless val0.is_a?(Hash)
502
+ reciever_result[key0] = val0.keys
503
+ when Class
504
+ bug() unless val0.is_a?(Hash)
505
+ newval = {}
506
+ val0.each do |(key1, val1)|
507
+ bug() unless key1.is_a?(Symbol)
508
+ bug() unless val1.is_a?(Hash)
509
+ newval[key1] = val1.keys
510
+ end
511
+ return_value_result[key0] = newval
512
+ else
513
+ bug("#{key0}, #{key0.class}")
514
+ end
515
+ end
516
+ bug() unless (reciever_result.keys & return_value_result.keys).empty?
517
+ [reciever_result, return_value_result]
518
+ end
519
+
520
+ def update_configuration(configuration, reciever_result, return_value_result)
521
+ update_p = false
522
+ update_p |= configuration.update_variable_configuration(reciever_result)
523
+ update_p |= configuration.update_return_value_configuration(return_value_result)
524
+ update_p
525
+ end
526
+
527
+ def update_base_configuration(manager, reciever_result, return_value_result)
528
+ base_configuration = manager.load_base_configuration()
529
+ unless base_configuration
530
+ last = manager.load_last_configuration()
531
+ bind = last ? (last.bind ? last.bind.bind : nil) : nil
532
+ base_configuration = Configuration.new({}, bind)
533
+ end
534
+ bug() unless base_configuration.instance_of?(Configuration)
535
+ update_p = update_configuration(base_configuration, reciever_result, return_value_result)
536
+ return false unless update_p
537
+ manager.save_base_configuration(base_configuration)
538
+ true
539
+ end
540
+
541
+ def set_sampling_table(suggestion, manager, configuration)
542
+ if CastOff.development?
543
+ h = Hash.new()
544
+ __send__("register_sampling_table_#{manager.signiture}", h)
545
+ suggestion.add_handler do
546
+ reciever_result, return_value_result = parse_sampling_table(h)
547
+ update_base_configuration(manager, reciever_result, return_value_result)
548
+ if reciever_result.size > 0
549
+ msg = "These are unresolved local variables sampling results."
550
+ ary = []
551
+ reciever_result.each do |key0, val0|
552
+ bug() unless key0.is_a?(Symbol)
553
+ bug() unless val0.is_a?(Array)
554
+ val0.each do |t|
555
+ ary << [key0.to_s, t.to_s]
556
+ end
557
+ end
558
+ suggestion.add_suggestion(msg, ["<Variable>", "<SamplingResultClass>"], ary)
559
+ end
560
+ if return_value_result.size > 0
561
+ msg = "These are unresolved method return values sampling results."
562
+ ary = []
563
+ return_value_result.each do |key0, val0|
564
+ bug() unless key0.is_a?(Class)
565
+ bug() unless val0.is_a?(Hash)
566
+ val0.each do |(mid, types)|
567
+ types.each{|t| ary << ["#{key0}##{mid}", t.to_s]}
568
+ end
569
+ end
570
+ suggestion.add_suggestion(msg, ["<Method>", "<SamplingResultClass>"], ary)
571
+ end
572
+
573
+ bug() unless configuration
574
+ s0 = configuration.to_s
575
+ update_p = update_configuration(configuration, reciever_result, return_value_result)
576
+ s1 = configuration.to_s
577
+ configuration.compact()
578
+ s2 = configuration.to_s
579
+ if update_p
580
+ bug() if s0 == s1
581
+ else
582
+ bug() if s0 != s1
583
+ end
584
+ if update_p
585
+ suggestion.add_suggestion("You specify following type map to CastOff", ["Your Annotation"], [[s0]], false)
586
+ suggestion.add_suggestion("CastOff suggests you to use following type map", ["CastOff Suggestion"], [[s2]], false)
587
+ end
588
+ end
589
+ end
590
+ end
591
+
592
+ def set_direct_call(obj, mid, type, manager, configuration)
593
+ bug() unless configuration
594
+ return if configuration.use_method_frame?
595
+ CastOff.should_be_call_directly(obj, mid, type)
596
+ end
597
+
598
+ def hook_method_override(manager, configuration, function_pointer_initializer)
599
+ bug() unless configuration
600
+ return unless configuration.alert_override?
601
+ dep = manager.load_dependency()
602
+ dep.check(configuration)
603
+ dep.hook(function_pointer_initializer)
604
+ end
605
+
606
+ def load_binary(manager, configuration, suggestion, iseq, bind)
607
+ execute_no_hook() do
608
+ so = manager.compiled_binary
609
+ sign = manager.signiture
610
+ bug("#{so} is not exist") unless File.exist?(so)
611
+ load_compiled_file(so)
612
+ function_pointer_initializer = "initialize_fptr_#{sign}".intern
613
+ hook_method_override(manager, configuration, function_pointer_initializer)
614
+ __send__("register_iseq_#{sign}", iseq)
615
+ __send__("register_ifunc_#{sign}")
616
+ set_sampling_table(suggestion, manager, configuration)
617
+ suggestion.dump_at_exit()
618
+ __send__(function_pointer_initializer)
619
+ __send__("prefetch_constants_#{sign}", bind) if bind
620
+ end
621
+ end
622
+
623
+ def capture_instruction()
624
+ a = [] # setlocal
625
+ a[0] = a # getlocal, setn, pop, leave
626
+ end
627
+ end
628
+ end
629
+