-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexit_compiler.rb
164 lines (137 loc) · 4.83 KB
/
exit_compiler.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
module RubyVM::RJIT
class ExitCompiler
def initialize = freeze
# Used for invalidating a block on entry.
# @param pc [Integer]
# @param asm [RubyVM::RJIT::Assembler]
def compile_entry_exit(pc, ctx, asm, cause:)
# Fix pc/sp offsets for the interpreter
save_pc_and_sp(pc, ctx, asm, reset_sp_offset: false)
# Increment per-insn exit counter
count_insn_exit(pc, asm)
# Restore callee-saved registers
asm.comment("#{cause}: entry exit")
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
asm.mov(C_RET, Qundef)
asm.ret
end
# Set to cfp->jit_return by default for leave insn
# @param asm [RubyVM::RJIT::Assembler]
def compile_leave_exit(asm)
asm.comment('default cfp->jit_return')
# Restore callee-saved registers
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
# :rax is written by #leave
asm.ret
end
# Fire cfunc events on invalidation by TracePoint
# @param asm [RubyVM::RJIT::Assembler]
def compile_full_cfunc_return(asm)
# This chunk of code expects REG_EC to be filled properly and
# RAX to contain the return value of the C method.
asm.comment('full cfunc return')
asm.mov(C_ARGS[0], EC)
asm.mov(C_ARGS[1], :rax)
asm.call(C.rjit_full_cfunc_return)
# TODO: count the exit
# Restore callee-saved registers
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
asm.mov(C_RET, Qundef)
asm.ret
end
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
def compile_side_exit(pc, ctx, asm)
# Fix pc/sp offsets for the interpreter
save_pc_and_sp(pc, ctx.dup, asm) # dup to avoid sp_offset update
# Increment per-insn exit counter
count_insn_exit(pc, asm)
# Restore callee-saved registers
asm.comment("exit to interpreter on #{pc_to_insn(pc).name}")
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
asm.mov(C_RET, Qundef)
asm.ret
end
# @param asm [RubyVM::RJIT::Assembler]
# @param entry_stub [RubyVM::RJIT::EntryStub]
def compile_entry_stub(asm, entry_stub)
# Call rb_rjit_entry_stub_hit
asm.comment('entry stub hit')
asm.mov(C_ARGS[0], to_value(entry_stub))
asm.call(C.rb_rjit_entry_stub_hit)
# Jump to the address returned by rb_rjit_entry_stub_hit
asm.jmp(:rax)
end
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
# @param branch_stub [RubyVM::RJIT::BranchStub]
# @param target0_p [TrueClass,FalseClass]
def compile_branch_stub(ctx, asm, branch_stub, target0_p)
# Call rb_rjit_branch_stub_hit
iseq = branch_stub.iseq
if C.rjit_opts.dump_disasm && C.imemo_type_p(iseq, C.imemo_iseq) # Guard against ISEQ GC at random moments
asm.comment("branch stub hit: #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{iseq_lineno(iseq, target0_p ? branch_stub.target0.pc : branch_stub.target1.pc)}")
end
asm.mov(:rdi, to_value(branch_stub))
asm.mov(:esi, ctx.sp_offset)
asm.mov(:edx, target0_p ? 1 : 0)
asm.call(C.rb_rjit_branch_stub_hit)
# Jump to the address returned by rb_rjit_branch_stub_hit
asm.jmp(:rax)
end
private
def pc_to_insn(pc)
Compiler.decode_insn(C.VALUE.new(pc).*)
end
# @param pc [Integer]
# @param asm [RubyVM::RJIT::Assembler]
def count_insn_exit(pc, asm)
if C.rjit_opts.stats
insn = Compiler.decode_insn(C.VALUE.new(pc).*)
asm.comment("increment insn exit: #{insn.name}")
asm.mov(:rax, (C.rjit_insn_exits + insn.bin).to_i)
asm.add([:rax], 1) # TODO: lock
end
if C.rjit_opts.trace_exits
asm.comment('rjit_record_exit_stack')
asm.mov(C_ARGS[0], pc)
asm.call(C.rjit_record_exit_stack)
end
end
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
def save_pc_and_sp(pc, ctx, asm, reset_sp_offset: true)
# Update pc (TODO: manage PC offset?)
asm.comment("save PC#{' and SP' if ctx.sp_offset != 0} to CFP")
asm.mov(:rax, pc) # rax = jit.pc
asm.mov([CFP, C.rb_control_frame_t.offsetof(:pc)], :rax) # cfp->pc = rax
# Update sp
if ctx.sp_offset != 0
asm.add(SP, C.VALUE.size * ctx.sp_offset) # sp += stack_size
asm.mov([CFP, C.rb_control_frame_t.offsetof(:sp)], SP) # cfp->sp = sp
if reset_sp_offset
ctx.sp_offset = 0
end
end
end
def to_value(obj)
GC_REFS << obj
C.to_value(obj)
end
def iseq_lineno(iseq, pc)
C.rb_iseq_line_no(iseq, (pc - iseq.body.iseq_encoded.to_i) / C.VALUE.size)
rescue RangeError # bignum too big to convert into `unsigned long long' (RangeError)
-1
end
end
end