/* -*- C -*- */ % # Copyright (c) 2018 Takashi Kokubun. All rights reserved. % # % # This file is a part of the programming language Ruby. Permission is hereby % # granted, to either redistribute and/or modify this file, provided that the % # conditions mentioned in the file COPYING are met. Consult the file for % # details. <%= render 'copyright' %> % % # This is an ERB template that generates Ruby code that generates C code that % # generates JIT-ed C code. <%= render 'notice', locals: { this_file: 'is the main part of compile_insn() in mjit_compile.c', edit: __FILE__, } -%> % % unsupported_insns = [ % 'defineclass', # low priority % ] % % opt_send_without_block = RubyVM::Instructions.find { |i| i.name == 'opt_send_without_block' } % if opt_send_without_block.nil? % raise 'opt_send_without_block not found' % end % % send_compatible_opt_insns = RubyVM::BareInstructions.to_a.select do |insn| % insn.name.start_with?('opt_') && opt_send_without_block.opes == insn.opes && % insn.expr.expr.lines.any? { |l| l.match(/\A\s+CALL_SIMPLE_METHOD\(\);\s+\z/) } % end.map(&:name) % % # Available variables and macros in JIT-ed function: % # ec: the first argument of _mjitXXX % # reg_cfp: the second argument of _mjitXXX % # GET_CFP(): refers to `reg_cfp` % # GET_EP(): refers to `reg_cfp->ep` % # GET_SP(): refers to `reg_cfp->sp`, or `(stack + stack_size)` if local_stack_p % # GET_SELF(): refers to `cfp_self` % # GET_LEP(): refers to `VM_EP_LEP(reg_cfp->ep)` % # EXEC_EC_CFP(): refers to `val = vm_exec(ec, true)` with frame setup % # CALL_METHOD(): using `GET_CFP()` and `EXEC_EC_CFP()` % # TOPN(): refers to `reg_cfp->sp`, or `*(stack + (stack_size - num - 1))` if local_stack_p % # STACK_ADDR_FROM_TOP(): refers to `reg_cfp->sp`, or `stack + (stack_size - num)` if local_stack_p % # DISPATCH_ORIGINAL_INSN(): expanded in _mjit_compile_insn.erb % # THROW_EXCEPTION(): specially defined for JIT % # RESTORE_REGS(): specially defined for `leave` switch (insn) { % (RubyVM::BareInstructions.to_a + RubyVM::OperandsUnifications.to_a).each do |insn| % next if unsupported_insns.include?(insn.name) case BIN(<%= insn.name %>): { % # Instruction-specific behavior in JIT % case insn.name % when 'opt_send_without_block', 'send' <%= render 'mjit_compile_send', locals: { insn: insn } -%> % when *send_compatible_opt_insns % # To avoid cancel, just emit `opt_send_without_block` instead of `opt_*` insn if call cache is populated. % cd_index = insn.opes.index { |o| o.fetch(:type) == 'CALL_DATA' } if (has_cache_for_send(captured_cc_entries(status)[call_data_index((CALL_DATA)operands[<%= cd_index %>], body)], BIN(<%= insn.name %>))) { <%= render 'mjit_compile_send', locals: { insn: opt_send_without_block } -%> <%= render 'mjit_compile_insn', locals: { insn: opt_send_without_block } -%> break; } % when 'getinstancevariable', 'setinstancevariable' <%= render 'mjit_compile_ivar', locals: { insn: insn } -%> % when 'invokebuiltin', 'opt_invokebuiltin_delegate' <%= render 'mjit_compile_invokebuiltin', locals: { insn: insn } -%> % when 'opt_getinlinecache' <%= render 'mjit_compile_getinlinecache', locals: { insn: insn } -%> % when 'leave', 'opt_invokebuiltin_delegate_leave' % # opt_invokebuiltin_delegate_leave also implements leave insn. We need to handle it here for inlining. % if insn.name == 'opt_invokebuiltin_delegate_leave' <%= render 'mjit_compile_invokebuiltin', locals: { insn: insn } -%> % else if (b->stack_size != 1) { if (mjit_opts.warnings || mjit_opts.verbose) fprintf(stderr, "MJIT warning: Unexpected JIT stack_size on leave: %d\n", b->stack_size); status->success = false; } % end % # Skip vm_pop_frame for inlined call if (status->inlined_iseqs != NULL) { // the current ISeq is NOT being inlined % # Cancel on interrupts to make leave insn leaf fprintf(f, " if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(ec))) {\n"); fprintf(f, " reg_cfp->sp = vm_base_ptr(reg_cfp) + %d;\n", b->stack_size); fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", pos); fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel_leave);\n"); fprintf(f, " goto cancel;\n"); fprintf(f, " }\n"); fprintf(f, " ec->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(reg_cfp);\n"); // vm_pop_frame } fprintf(f, " return stack[0];\n"); b->stack_size += <%= insn.call_attribute('sp_inc') %>; b->finish_p = TRUE; break; % end % % # Main insn implementation generated by insns.def <%= render 'mjit_compile_insn', locals: { insn: insn } -%> break; } % end % % # We don't support InstructionsUnifications yet because it's not used for now. % # We don't support TraceInstructions yet. There is no blocker for it but it's just not implemented. default: if (mjit_opts.warnings || mjit_opts.verbose) fprintf(stderr, "MJIT warning: Skipped to compile unsupported instruction: %s\n", insn_name(insn)); status->success = false; break; }