aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2022-11-27 22:23:35 -0800
committerTakashi Kokubun <takashikkbn@gmail.com>2022-11-27 22:47:46 -0800
commit2c1c0d3df033da6745aa0f828a704bb46a535fbc (patch)
tree3fe85f3ef967e2f041141e099881d49a1e7d21ea
parent87e7b640ebb6002236e601d09347850fab5858df (diff)
downloadruby-2c1c0d3df033da6745aa0f828a704bb46a535fbc.tar.gz
MJIT: Get rid of C.fprintf
Faster code generation and cleaner code.
-rw-r--r--lib/mjit/compiler.rb82
-rw-r--r--mjit_c.rb8
-rw-r--r--mjit_compiler.c9
3 files changed, 47 insertions, 52 deletions
diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb
index e0bd5e85b9..dfd52ce855 100644
--- a/lib/mjit/compiler.rb
+++ b/lib/mjit/compiler.rb
@@ -19,46 +19,50 @@ module RubyVM::MJIT
]
class << Compiler = Module.new
+ # @param iseq [RubyVM::MJIT::CPointer::Struct_rb_iseq_struct]
# @param funcname [String]
- def compile(f, iseq, funcname, id)
+ # @param id [Integer]
+ # @return [String,NilClass]
+ def compile(iseq, funcname, id)
status = C.compile_status.new # not freed for now
status.compiled_iseq = iseq.body
status.compiled_id = id
init_compile_status(status, iseq.body, true) # not freed for now
if iseq.body.ci_size > 0 && status.cc_entries_index == -1
- return false
+ return nil
end
init_ivar_compile_status(iseq.body, status)
+ src = +''
if !status.compile_info.disable_send_cache && !status.compile_info.disable_inlining
- unless precompile_inlinable_iseqs(f, iseq, status)
- return false
+ unless precompile_inlinable_iseqs(src, iseq, status)
+ return nil
end
end
- C.fprintf(f, "VALUE\n#{funcname}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)\n{\n")
- success = compile_body(f, iseq, status)
- C.fprintf(f, "\n} // end of #{funcname}\n")
+ src << "VALUE\n#{funcname}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)\n{\n"
+ success = compile_body(src, iseq, status)
+ src << "\n} // end of #{funcname}\n"
- return success
+ return success ? src : nil
rescue Exception => e # should we use rb_rescue in C instead?
if C.mjit_opts.warnings || C.mjit_opts.verbose > 0
$stderr.puts "MJIT error: #{e.full_message}"
end
- return false
+ return nil
end
private
- def compile_body(f, iseq, status)
+ def compile_body(src, iseq, status)
status.success = true
status.local_stack_p = !iseq.body.catch_except_p
if status.local_stack_p
- src = +" VALUE stack[#{iseq.body.stack_max}];\n"
+ src << " VALUE stack[#{iseq.body.stack_max}];\n"
else
- src = +" VALUE *stack = reg_cfp->sp;\n"
+ src << " VALUE *stack = reg_cfp->sp;\n"
end
unless status.inlined_iseqs.nil? # i.e. compile root
@@ -90,16 +94,15 @@ module RubyVM::MJIT
src << " }\n"
end
- C.fprintf(f, src)
- compile_insns(0, 0, status, iseq.body, f)
- compile_cancel_handler(f, iseq.body, status)
- C.fprintf(f, "#undef GET_SELF\n")
+ compile_insns(0, 0, status, iseq.body, src)
+ compile_cancel_handler(src, iseq.body, status)
+ src << "#undef GET_SELF\n"
return status.success
end
# Compile one conditional branch. If it has branchXXX insn, this should be
# called multiple times for each branch.
- def compile_insns(stack_size, pos, status, body, f)
+ def compile_insns(stack_size, pos, status, body, src)
branch = C.compile_branch.new # not freed for now
branch.stack_size = stack_size
branch.finish_p = false
@@ -108,8 +111,8 @@ module RubyVM::MJIT
insn = INSNS.fetch(C.rb_vm_insn_decode(body.iseq_encoded[pos]))
status.stack_size_for_pos[pos] = branch.stack_size
- C.fprintf(f, "\nlabel_#{pos}: /* #{insn.name} */\n")
- pos = compile_insn(insn, pos, status, body.iseq_encoded + (pos+1), body, branch, f)
+ src << "\nlabel_#{pos}: /* #{insn.name} */\n"
+ pos = compile_insn(insn, pos, status, body.iseq_encoded + (pos+1), body, branch, src)
if status.success && branch.stack_size > body.stack_max
if mjit_opts.warnings || mjit_opts.verbose > 0
$stderr.puts "MJIT warning: JIT stack size (#{branch.stack_size}) exceeded its max size (#{body.stack_max})"
@@ -126,11 +129,11 @@ module RubyVM::MJIT
# When you add a new instruction to insns.def, it would be nice to have JIT compilation support here but
# it's optional. This JIT compiler just ignores ISeq which includes unknown instruction, and ISeq which
# does not have it can be compiled as usual.
- def compile_insn(insn, pos, status, operands, body, b, f)
+ def compile_insn(insn, pos, status, operands, body, b, src)
sp_inc = C.mjit_call_attribute_sp_inc(insn.bin, operands)
next_pos = pos + insn.len
- result = compile_insn_entry(f, insn, b.stack_size, sp_inc, status.local_stack_p, pos, next_pos, insn.len,
+ result = compile_insn_entry(insn, b.stack_size, sp_inc, status.local_stack_p, pos, next_pos, insn.len,
status.inlined_iseqs.nil?, status, operands, body)
if result.nil?
if C.mjit_opts.warnings || C.mjit_opts.verbose > 0
@@ -138,9 +141,9 @@ module RubyVM::MJIT
end
status.success = false
else
- src, next_pos, finish_p, compile_insns_p = result
+ result_src, next_pos, finish_p, compile_insns_p = result
- C.fprintf(f, src)
+ src << result_src
b.stack_size += sp_inc
if finish_p
@@ -148,9 +151,9 @@ module RubyVM::MJIT
end
if compile_insns_p
if already_compiled?(status, pos + insn.len)
- C.fprintf(f, "goto label_#{pos + insn.len};\n")
+ src << "goto label_#{pos + insn.len};\n"
else
- compile_insns(b.stack_size, pos + insn.len, status, body, f)
+ compile_insns(b.stack_size, pos + insn.len, status, body, src)
end
end
end
@@ -158,7 +161,7 @@ module RubyVM::MJIT
# If next_pos is already compiled and this branch is not finished yet,
# next instruction won't be compiled in C code next and will need `goto`.
if !b.finish_p && next_pos < body.iseq_size && already_compiled?(status, next_pos)
- C.fprintf(f, "goto label_#{next_pos};\n")
+ src << "goto label_#{next_pos};\n"
# Verify stack size assumption is the same among multiple branches
if status.stack_size_for_pos[next_pos] != b.stack_size
@@ -172,7 +175,7 @@ module RubyVM::MJIT
return next_pos
end
- def compile_insn_entry(f, insn, stack_size, sp_inc, local_stack_p, pos, next_pos, insn_len, inlined_iseq_p, status, operands, body)
+ def compile_insn_entry(insn, stack_size, sp_inc, local_stack_p, pos, next_pos, insn_len, inlined_iseq_p, status, operands, body)
finish_p = false
compile_insns = false
@@ -630,8 +633,8 @@ module RubyVM::MJIT
end
# Print the block to cancel inlined method call. It's supporting only `opt_send_without_block` for now.
- def compile_inlined_cancel_handler(f, body, inline_context)
- src = +"\ncancel:\n"
+ def compile_inlined_cancel_handler(src, body, inline_context)
+ src << "\ncancel:\n"
src << " RB_DEBUG_COUNTER_INC(mjit_cancel);\n"
src << " rb_mjit_recompile_inlining(original_iseq);\n"
@@ -660,17 +663,16 @@ module RubyVM::MJIT
# We're not just returning Qundef here so that caller's normal cancel handler can
# push back `stack` to `cfp->sp`.
src << " return vm_exec(ec, false);\n"
- C.fprintf(f, src)
end
# Print the block to cancel JIT execution.
- def compile_cancel_handler(f, body, status)
+ def compile_cancel_handler(src, body, status)
if status.inlined_iseqs.nil? # the current ISeq is being inlined
- compile_inlined_cancel_handler(f, body, status.inline_context)
+ compile_inlined_cancel_handler(src, body, status.inline_context)
return
end
- src = +"\nsend_cancel:\n"
+ src << "\nsend_cancel:\n"
src << " RB_DEBUG_COUNTER_INC(mjit_cancel_send_inline);\n"
src << " rb_mjit_recompile_send(original_iseq);\n"
src << " goto cancel;\n"
@@ -697,10 +699,9 @@ module RubyVM::MJIT
end
end
src << " return Qundef;\n"
- C.fprintf(f, src)
end
- def precompile_inlinable_child_iseq(f, child_iseq, status, ci, cc, pos)
+ def precompile_inlinable_child_iseq(src, child_iseq, status, ci, cc, pos)
child_status = C.compile_status.new # not freed for now
child_status.compiled_iseq = status.compiled_iseq
child_status.compiled_id = status.compiled_id
@@ -714,20 +715,19 @@ module RubyVM::MJIT
end
init_ivar_compile_status(child_iseq.body, child_status)
- src = +"ALWAYS_INLINE(static VALUE _mjit#{status.compiled_id}_inlined_#{pos}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq));\n"
+ src << "ALWAYS_INLINE(static VALUE _mjit#{status.compiled_id}_inlined_#{pos}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq));\n"
src << "static inline VALUE\n_mjit#{status.compiled_id}_inlined_#{pos}(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE orig_self, const rb_iseq_t *original_iseq)\n{\n"
src << " const VALUE *orig_pc = reg_cfp->pc;\n"
src << " VALUE *orig_sp = reg_cfp->sp;\n"
- C.fprintf(f, src)
- success = compile_body(f, child_iseq, child_status)
+ success = compile_body(src, child_iseq, child_status)
- C.fprintf(f, "\n} /* end of _mjit#{status.compiled_id}_inlined_#{pos} */\n\n")
+ src << "\n} /* end of _mjit#{status.compiled_id}_inlined_#{pos} */\n\n"
return success;
end
- def precompile_inlinable_iseqs(f, iseq, status)
+ def precompile_inlinable_iseqs(src, iseq, status)
body = iseq.body
pos = 0
while pos < body.iseq_size
@@ -745,7 +745,7 @@ module RubyVM::MJIT
$stderr.puts "JIT inline: #{child_location.label}@#{C.rb_iseq_path(child_iseq)}:#{C.rb_iseq_first_lineno(child_iseq)} " \
"=> #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{C.rb_iseq_first_lineno(iseq)}"
end
- if !precompile_inlinable_child_iseq(f, child_iseq, status, ci, cc, pos)
+ if !precompile_inlinable_child_iseq(src, child_iseq, status, ci, cc, pos)
return false
end
end
diff --git a/mjit_c.rb b/mjit_c.rb
index c240a5ec4b..5ccc40aaf9 100644
--- a/mjit_c.rb
+++ b/mjit_c.rb
@@ -114,14 +114,6 @@ module RubyVM::MJIT
iseq_addr = Primitive.cexpr! 'PTR2NUM((VALUE)rb_iseqw_to_iseq(iseqw))'
rb_iseq_t.new(iseq_addr)
end
-
- # TODO: remove this after migration
- def fprintf(f, str)
- Primitive.cstmt! %{
- fprintf((FILE *)NUM2PTR(f), "%s", RSTRING_PTR(str));
- return Qnil;
- }
- end
end
### MJIT bindgen begin ###
diff --git a/mjit_compiler.c b/mjit_compiler.c
index 0e9f499424..be3942a7b0 100644
--- a/mjit_compiler.c
+++ b/mjit_compiler.c
@@ -123,11 +123,14 @@ mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname, int id)
mjit_call_p = false; // Avoid impacting JIT metrics by itself
extern VALUE rb_mMJITCompiler;
- bool success = RTEST(rb_funcall(rb_mMJITCompiler, rb_intern("compile"), 4,
- PTR2NUM((VALUE)f), rb_ptr("rb_iseq_t", iseq), rb_str_new_cstr(funcname), INT2NUM(id)));
+ VALUE src = rb_funcall(rb_mMJITCompiler, rb_intern("compile"), 3,
+ rb_ptr("rb_iseq_t", iseq), rb_str_new_cstr(funcname), INT2NUM(id));
+ if (!NIL_P(src)) {
+ fprintf(f, "%s", RSTRING_PTR(src));
+ }
mjit_call_p = original_call_p;
- return success;
+ return !NIL_P(src);
}
//