diff options
Diffstat (limited to 'tool/ruby_vm')
47 files changed, 410 insertions, 824 deletions
diff --git a/tool/ruby_vm/controllers/application_controller.rb b/tool/ruby_vm/controllers/application_controller.rb index 25c10947ed..f6c0e39600 100644 --- a/tool/ruby_vm/controllers/application_controller.rb +++ b/tool/ruby_vm/controllers/application_controller.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -16,10 +15,11 @@ require_relative '../models/typemap' require_relative '../loaders/vm_opts_h' class ApplicationController - def generate i, destdir + def generate i, destdir, basedir path = Pathname.new i dst = destdir ? Pathname.new(destdir).join(i) : Pathname.new(i) - dumper = RubyVM::Dumper.new dst + base = basedir ? Pathname.new(basedir) : Pathname.pwd + dumper = RubyVM::Dumper.new dst, base.expand_path return [path, dumper] end end diff --git a/tool/ruby_vm/helpers/c_escape.rb b/tool/ruby_vm/helpers/c_escape.rb index fa3cb8b1aa..628cb0428b 100644 --- a/tool/ruby_vm/helpers/c_escape.rb +++ b/tool/ruby_vm/helpers/c_escape.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -17,7 +16,10 @@ module RubyVM::CEscape # generate comment, with escaps. def commentify str - return "/* #{str.strip.b.gsub '*/', '*\\/'} */" + unless str = str.dump[/\A"\K.*(?="\z)/] + raise Encoding::CompatibilityError, "must be ASCII-compatible (#{str.encoding})" + end + return "/* #{str.gsub('*/', '*\\/').gsub('/*', '/\\*')} */" end # Mimic gensym of CL. @@ -60,7 +62,7 @@ module RubyVM::CEscape "\x18"=>"\\x18", "\x19"=>"\\x19", "\x1A"=>"\\x1a", "\e"=>"\\x1b", "\x1C"=>"\\x1c", "\x1D"=>"\\x1d", "\x1E"=>"\\x1e", "\x1F"=>"\\x1f", " "=> " ", "!"=> "!", "\""=> "\\\"", "#"=> "#", - "$"=> "$", "%"=> "%", "&"=> "&", "'"=> "\\'", + "$"=> "$", "%"=> "%", "&"=> "&", "'"=> "'", "("=> "(", ")"=> ")", "*"=> "*", "+"=> "+", ","=> ",", "-"=> "-", "."=> ".", "/"=> "/", "0"=> "0", "1"=> "1", "2"=> "2", "3"=> "3", diff --git a/tool/ruby_vm/helpers/dumper.rb b/tool/ruby_vm/helpers/dumper.rb index 98104f4b92..f0758dd44f 100644 --- a/tool/ruby_vm/helpers/dumper.rb +++ b/tool/ruby_vm/helpers/dumper.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -28,16 +27,12 @@ class RubyVM::Dumper path = Pathname.new(__FILE__) path = (path.relative_path_from(Pathname.pwd) rescue path).dirname path += '../views' - path += spec - src = path.read mode: 'rt:utf-8:utf-8' + path += Pathname.pwd.join(spec).expand_path.to_s.sub("#{@base}/", '') + src = path.expand_path.read mode: 'rt:utf-8:utf-8' rescue Errno::ENOENT raise "don't know how to generate #{path}" else - if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+ - erb = ERB.new(src, trim_mode: '%-') - else - erb = ERB.new(src, nil, '%-') - end + erb = ERB.new(src, trim_mode: '%-') erb.filename = path.to_path return erb end @@ -85,10 +80,11 @@ class RubyVM::Dumper . join end - def initialize dst + def initialize dst, base @erb = {} @empty = new_binding @file = cstr dst.to_path + @base = base end def render partial, opts = { :locals => {} } diff --git a/tool/ruby_vm/helpers/scanner.rb b/tool/ruby_vm/helpers/scanner.rb index ef6de8120e..8998abb2d3 100644 --- a/tool/ruby_vm/helpers/scanner.rb +++ b/tool/ruby_vm/helpers/scanner.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- diff --git a/tool/ruby_vm/loaders/insns_def.rb b/tool/ruby_vm/loaders/insns_def.rb index 47e4ba29f5..d45d0ba83c 100644 --- a/tool/ruby_vm/loaders/insns_def.rb +++ b/tool/ruby_vm/loaders/insns_def.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -25,10 +24,10 @@ grammar = %r' (?<ws> \g<comment> | \s ){0} (?<ident> [_a-zA-Z] [0-9_a-zA-Z]* ){0} (?<type> (?: \g<keyword> \g<ws>+ )* \g<ident> ){0} - (?<arg> \g<type> \g<ws>+ \g<ident> | \.\.\. ){0} + (?<arg> \g<type> \g<ws>+ \g<ident> ){0} (?<argv> (?# empty ) | void | - \g<arg> (?: \g<ws>* , \g<ws>* \g<arg> \g<ws>* )* ){0} + (?: \.\.\. | \g<arg>) (?: \g<ws>* , \g<ws>* \g<arg> \g<ws>* )* ){0} (?<pragma> \g<ws>* // \s* attr \g<ws>+ (?<pragma:type> \g<type> ) \g<ws>+ (?<pragma:name> \g<ident> ) \g<ws>* @@ -57,6 +56,10 @@ until scanner.eos? do ope = split.(scanner["insn:opes"]) pop = split.(scanner["insn:pops"]) ret = split.(scanner["insn:rets"]) + if ope.include?("...") + raise sprintf("parse error at %s:%d:%s: operands cannot be variadic", + scanner.__FILE__, scanner.__LINE__, name) + end attrs = [] while l2 = scanner.scan(/\G#{grammar}\g<pragma>/o) do diff --git a/tool/ruby_vm/loaders/opt_insn_unif_def.rb b/tool/ruby_vm/loaders/opt_insn_unif_def.rb index aa6fd79e79..0750f1823a 100644 --- a/tool/ruby_vm/loaders/opt_insn_unif_def.rb +++ b/tool/ruby_vm/loaders/opt_insn_unif_def.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- diff --git a/tool/ruby_vm/loaders/opt_operand_def.rb b/tool/ruby_vm/loaders/opt_operand_def.rb index 29aef8a325..e08509a433 100644 --- a/tool/ruby_vm/loaders/opt_operand_def.rb +++ b/tool/ruby_vm/loaders/opt_operand_def.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- diff --git a/tool/ruby_vm/loaders/vm_opts_h.rb b/tool/ruby_vm/loaders/vm_opts_h.rb index 3f05c270ee..d626ea0296 100644 --- a/tool/ruby_vm/loaders/vm_opts_h.rb +++ b/tool/ruby_vm/loaders/vm_opts_h.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- diff --git a/tool/ruby_vm/models/attribute.rb b/tool/ruby_vm/models/attribute.rb index de35e7234a..177b701b92 100644 --- a/tool/ruby_vm/models/attribute.rb +++ b/tool/ruby_vm/models/attribute.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -21,7 +20,7 @@ class RubyVM::Attribute @key = opts[:name] @expr = RubyVM::CExpr.new location: opts[:location], expr: opts[:expr] @type = opts[:type] - @ope_decls = @insn.opes.map do |operand| + @ope_decls = @insn.operands.map do |operand| decl = operand[:decl] if @key == 'comptime_sp_inc' && operand[:type] == 'CALL_DATA' decl = decl.gsub('CALL_DATA', 'CALL_INFO').gsub('cd', 'ci') diff --git a/tool/ruby_vm/models/bare_instructions.rb b/tool/ruby_vm/models/bare_instruction.rb index b0cc83a65a..f87dd74179 100755..100644 --- a/tool/ruby_vm/models/bare_instructions.rb +++ b/tool/ruby_vm/models/bare_instruction.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -15,8 +14,8 @@ require_relative 'c_expr' require_relative 'typemap' require_relative 'attribute' -class RubyVM::BareInstructions - attr_reader :template, :name, :opes, :pops, :rets, :decls, :expr +class RubyVM::BareInstruction + attr_reader :template, :name, :operands, :pops, :rets, :decls, :expr def initialize opts = {} @template = opts[:template] @@ -24,7 +23,7 @@ class RubyVM::BareInstructions @loc = opts[:location] @sig = opts[:signature] @expr = RubyVM::CExpr.new opts[:expr] - @opes = typesplit @sig[:ope] + @operands = typesplit @sig[:ope] @pops = typesplit @sig[:pop].reject {|i| i == '...' } @rets = typesplit @sig[:ret].reject {|i| i == '...' } @attrs = opts[:attributes].map {|i| @@ -51,7 +50,7 @@ class RubyVM::BareInstructions def call_attribute name return sprintf 'attr_%s_%s(%s)', name, @name, \ - @opes.map {|i| i[:name] }.compact.join(', ') + @operands.map {|i| i[:name] }.compact.join(', ') end def has_attribute? k @@ -65,7 +64,7 @@ class RubyVM::BareInstructions end def width - return 1 + opes.size + return 1 + operands.size end def declarations @@ -98,7 +97,7 @@ class RubyVM::BareInstructions end def operands_info - opes.map {|o| + operands.map {|o| c, _ = RubyVM::Typemap.fetch o[:type] next c }.join @@ -108,10 +107,6 @@ class RubyVM::BareInstructions /\b(false|0)\b/ !~ @attrs.fetch('handles_sp').expr.expr end - def always_leaf? - @attrs.fetch('leaf').expr.expr == 'true;' - end - def handle_canary stmt # Stack canary is basically a good thing that we want to add, however: # @@ -133,7 +128,7 @@ class RubyVM::BareInstructions end def has_ope? var - return @opes.any? {|i| i[:name] == var[:name] } + return @operands.any? {|i| i[:name] == var[:name] } end def has_pop? var @@ -145,6 +140,10 @@ class RubyVM::BareInstructions @variables.find { |_, var_info| var_info[:type] == 'CALL_DATA' } end + def zjit_profile? + @attrs.fetch('zjit_profile').expr.expr != 'false;' + end + private def check_attribute_consistency @@ -176,18 +175,19 @@ class RubyVM::BareInstructions # Beware: order matters here because some attribute depends another. generate_attribute 'const char*', 'name', "insn_name(#{bin})" generate_attribute 'enum ruby_vminsn_type', 'bin', bin - generate_attribute 'rb_num_t', 'open', opes.size + generate_attribute 'rb_num_t', 'open', operands.size generate_attribute 'rb_num_t', 'popn', pops.size generate_attribute 'rb_num_t', 'retn', rets.size generate_attribute 'rb_num_t', 'width', width generate_attribute 'rb_snum_t', 'sp_inc', rets.size - pops.size generate_attribute 'bool', 'handles_sp', default_definition_of_handles_sp generate_attribute 'bool', 'leaf', default_definition_of_leaf + generate_attribute 'bool', 'zjit_profile', false end def default_definition_of_handles_sp # Insn with ISEQ should yield it; can handle sp. - return opes.any? {|o| o[:type] == 'ISEQ' } + return operands.any? {|o| o[:type] == 'ISEQ' } end def default_definition_of_leaf @@ -224,13 +224,13 @@ class RubyVM::BareInstructions new h.merge(:template => h) } - def self.fetch name + def self.find(name) @instances.find do |insn| insn.name == name end or raise IndexError, "instruction not found: #{name}" end - def self.to_a + def self.all @instances end end diff --git a/tool/ruby_vm/models/c_expr.rb b/tool/ruby_vm/models/c_expr.rb index 073112f545..095ff4f1d9 100644 --- a/tool/ruby_vm/models/c_expr.rb +++ b/tool/ruby_vm/models/c_expr.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -36,6 +35,10 @@ class RubyVM::CExpr end def inspect - sprintf "#<%s:%d %s>", @__FILE__, @__LINE__, @expr + if @__LINE__ + sprintf "#<%s:%d %s>", @__FILE__, @__LINE__, @expr + else + sprintf "#<%s %s>", @__FILE__, @expr + end end end diff --git a/tool/ruby_vm/models/instructions.rb b/tool/ruby_vm/models/instructions.rb index 1198c7a4a6..7be7064b14 100644 --- a/tool/ruby_vm/models/instructions.rb +++ b/tool/ruby_vm/models/instructions.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -10,13 +9,15 @@ # conditions mentioned in the file COPYING are met. Consult the file for # details. -require_relative 'bare_instructions' -require_relative 'operands_unifications' -require_relative 'instructions_unifications' +require_relative 'bare_instruction' +require_relative 'operands_unification' +require_relative 'instructions_unification' +require_relative 'trace_instruction' +require_relative 'zjit_instruction' -RubyVM::Instructions = RubyVM::BareInstructions.to_a + \ - RubyVM::OperandsUnifications.to_a + \ - RubyVM::InstructionsUnifications.to_a - -require_relative 'trace_instructions' +RubyVM::Instructions = RubyVM::BareInstruction.all + + RubyVM::OperandsUnification.all + + RubyVM::InstructionsUnification.all + + RubyVM::TraceInstruction.all + + RubyVM::ZJITInstruction.all RubyVM::Instructions.freeze diff --git a/tool/ruby_vm/models/instructions_unifications.rb b/tool/ruby_vm/models/instructions_unification.rb index 214ba5fcc2..5c798e6d54 100644 --- a/tool/ruby_vm/models/instructions_unifications.rb +++ b/tool/ruby_vm/models/instructions_unification.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -12,9 +11,9 @@ require_relative '../helpers/c_escape' require_relative '../loaders/opt_insn_unif_def' -require_relative 'bare_instructions' +require_relative 'bare_instruction' -class RubyVM::InstructionsUnifications +class RubyVM::InstructionsUnification include RubyVM::CEscape attr_reader :name @@ -23,7 +22,7 @@ class RubyVM::InstructionsUnifications @location = opts[:location] @name = namegen opts[:signature] @series = opts[:signature].map do |i| - RubyVM::BareInstructions.fetch i # Misshit is fatal + RubyVM::BareInstruction.find(i) # Misshit is fatal end end @@ -37,7 +36,7 @@ class RubyVM::InstructionsUnifications new h end - def self.to_a + def self.all @instances end end diff --git a/tool/ruby_vm/models/operands_unifications.rb b/tool/ruby_vm/models/operands_unification.rb index ee4e3a695d..ce118648ca 100644 --- a/tool/ruby_vm/models/operands_unifications.rb +++ b/tool/ruby_vm/models/operands_unification.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -12,16 +11,16 @@ require_relative '../helpers/c_escape' require_relative '../loaders/opt_operand_def' -require_relative 'bare_instructions' +require_relative 'bare_instruction' -class RubyVM::OperandsUnifications < RubyVM::BareInstructions +class RubyVM::OperandsUnification < RubyVM::BareInstruction include RubyVM::CEscape attr_reader :preamble, :original, :spec def initialize opts = {} name = opts[:signature][0] - @original = RubyVM::BareInstructions.fetch name + @original = RubyVM::BareInstruction.find(name) template = @original.template parts = compose opts[:location], opts[:signature], template[:signature] json = template.dup @@ -38,8 +37,8 @@ class RubyVM::OperandsUnifications < RubyVM::BareInstructions end def operand_shift_of var - before = @original.opes.find_index var - after = @opes.find_index var + before = @original.operands.find_index var + after = @operands.find_index var raise "no #{var} for #{@name}" unless before and after return before - after end @@ -50,7 +49,7 @@ class RubyVM::OperandsUnifications < RubyVM::BareInstructions case val when '*' then next nil else - type = @original.opes[i][:type] + type = @original.operands[i][:type] expr = RubyVM::Typemap.typecast_to_VALUE type, val next "#{ptr}[#{i}] == #{expr}" end @@ -85,7 +84,7 @@ class RubyVM::OperandsUnifications < RubyVM::BareInstructions def compose location, spec, template name = namegen spec *, argv = *spec - opes = @original.opes + opes = @original.operands if opes.size != argv.size raise sprintf("operand size mismatch for %s (%s's: %d, given: %d)", name, template[:name], opes.size, argv.size) @@ -130,12 +129,12 @@ class RubyVM::OperandsUnifications < RubyVM::BareInstructions new h end - def self.to_a + def self.all @instances end def self.each_group - to_a.group_by(&:original).each_pair do |k, v| + all.group_by(&:original).each_pair do |k, v| yield k, v end end diff --git a/tool/ruby_vm/models/trace_instructions.rb b/tool/ruby_vm/models/trace_instruction.rb index 4ed4c8cb42..6a3ad53c44 100644 --- a/tool/ruby_vm/models/trace_instructions.rb +++ b/tool/ruby_vm/models/trace_instruction.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -11,9 +10,9 @@ # details. require_relative '../helpers/c_escape' -require_relative 'bare_instructions' +require_relative 'bare_instruction' -class RubyVM::TraceInstructions +class RubyVM::TraceInstruction include RubyVM::CEscape attr_reader :name @@ -61,11 +60,11 @@ class RubyVM::TraceInstructions private - @instances = RubyVM::Instructions.map {|i| new i } + @instances = (RubyVM::BareInstruction.all + + RubyVM::OperandsUnification.all + + RubyVM::InstructionsUnification.all).map {|i| new(i) } - def self.to_a + def self.all @instances end - - RubyVM::Instructions.push(*to_a) end diff --git a/tool/ruby_vm/models/typemap.rb b/tool/ruby_vm/models/typemap.rb index ed3aea7d2e..68ef5a41a5 100644 --- a/tool/ruby_vm/models/typemap.rb +++ b/tool/ruby_vm/models/typemap.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -16,6 +15,7 @@ RubyVM::Typemap = { "CDHASH" => %w[H TS_CDHASH], "IC" => %w[K TS_IC], "IVC" => %w[A TS_IVC], + "ICVARC" => %w[J TS_ICVARC], "ID" => %w[I TS_ID], "ISE" => %w[T TS_ISE], "ISEQ" => %w[S TS_ISEQ], diff --git a/tool/ruby_vm/models/zjit_instruction.rb b/tool/ruby_vm/models/zjit_instruction.rb new file mode 100644 index 0000000000..04764e4c61 --- /dev/null +++ b/tool/ruby_vm/models/zjit_instruction.rb @@ -0,0 +1,56 @@ +require_relative '../helpers/c_escape' +require_relative 'bare_instruction' + +# Profile YARV instructions to optimize code generated by ZJIT +class RubyVM::ZJITInstruction + include RubyVM::CEscape + + attr_reader :name + + def initialize(orig) + @orig = orig + @name = as_tr_cpp "zjit @ #{@orig.name}" + end + + def pretty_name + return sprintf "%s(...)(...)(...)", @name + end + + def jump_destination + return @orig.name + end + + def bin + return sprintf "BIN(%s)", @name + end + + def width + return @orig.width + end + + def operands_info + return @orig.operands_info + end + + def rets + return ['...'] + end + + def pops + return ['...'] + end + + def attributes + return [] + end + + def has_attribute?(*) + return false + end + + @instances = RubyVM::BareInstruction.all.filter(&:zjit_profile?).map {|i| new(i) } + + def self.all + @instances + end +end diff --git a/tool/ruby_vm/scripts/insns2vm.rb b/tool/ruby_vm/scripts/insns2vm.rb index 8325dd364f..ad8603b1a8 100644 --- a/tool/ruby_vm/scripts/insns2vm.rb +++ b/tool/ruby_vm/scripts/insns2vm.rb @@ -1,4 +1,3 @@ -#! /your/favourite/path/to/ruby # -*- Ruby -*- # -*- frozen_string_literal: true; -*- # -*- warn_indent: true; -*- @@ -15,10 +14,10 @@ require_relative '../controllers/application_controller.rb' module RubyVM::Insns2VM def self.router argv - options = { destdir: nil } + options = { destdir: nil, basedir: nil } targets = generate_parser(options).parse argv return targets.map do |i| - next ApplicationController.new.generate i, options[:destdir] + next ApplicationController.new.generate i, options[:destdir], options[:basedir] end end @@ -84,6 +83,14 @@ module RubyVM::Insns2VM options[:destdir] = dir end + this.on "--basedir=DIR", <<-'begin' do |dir| + Change the base directory from the current working directory + to the given path. Used for searching the source template. + begin + raise "directory was not found in '#{dir}'" unless Dir.exist?(dir) + options[:basedir] = dir + end + this.on "-V", "--[no-]verbose", <<-'end' Please let us ignore this and be modest. end diff --git a/tool/ruby_vm/tests/.gitkeep b/tool/ruby_vm/tests/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 --- a/tool/ruby_vm/tests/.gitkeep +++ /dev/null diff --git a/tool/ruby_vm/views/_comptime_insn_stack_increase.erb b/tool/ruby_vm/views/_comptime_insn_stack_increase.erb index b633ab4d32..8bb28db1c1 100644 --- a/tool/ruby_vm/views/_comptime_insn_stack_increase.erb +++ b/tool/ruby_vm/views/_comptime_insn_stack_increase.erb @@ -6,6 +6,16 @@ %# conditions mentioned in the file COPYING are met. Consult the file for %# details. %# +% +% stack_increase = proc do |i| +% if i.has_attribute?('sp_inc') +% '-127' +% else +% sprintf("%4d", i.rets.size - i.pops.size) +% end +% end +% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') } +% PUREFUNC(MAYBE_UNUSED(static int comptime_insn_stack_increase(int depth, int insn, const VALUE *opes))); PUREFUNC(static rb_snum_t comptime_insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes)); @@ -13,15 +23,14 @@ rb_snum_t comptime_insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes) { static const signed char t[] = { -% RubyVM::Instructions.each_slice 8 do |a| - <%= a.map { |i| - if i.has_attribute?('sp_inc') - '-127' - else - sprintf("%4d", i.rets.size - i.pops.size) - end - }.join(', ') -%>, +% insns.each_slice(8) do |row| + <%= row.map(&stack_increase).join(', ') -%>, +% end +#if USE_ZJIT +% zjit_insns.each_slice(8) do |row| + <%= row.map(&stack_increase).join(', ') -%>, % end +#endif }; signed char c = t[insn]; @@ -42,7 +51,7 @@ comptime_insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *o % end case <%= i.bin %>: return <%= attr_function %>(<%= - i.opes.map.with_index do |v, j| + i.operands.map.with_index do |v, j| if v[:type] == 'CALL_DATA' && i.has_attribute?('comptime_sp_inc') v = v.dup v[:type] = 'CALL_INFO' diff --git a/tool/ruby_vm/views/_insn_entry.erb b/tool/ruby_vm/views/_insn_entry.erb index bdd0fa3c7c..6ec33461c4 100644 --- a/tool/ruby_vm/views/_insn_entry.erb +++ b/tool/ruby_vm/views/_insn_entry.erb @@ -20,11 +20,11 @@ INSN_ENTRY(<%= insn.name %>) <%= render_c_expr konst -%> % end % -% insn.opes.each_with_index do |ope, i| +% insn.operands.each_with_index do |ope, i| <%= ope[:decl] %> = (<%= ope[:type] %>)GET_OPERAND(<%= i + 1 %>); % end # define INSN_ATTR(x) <%= insn.call_attribute(' ## x ## ') %> - bool leaf = INSN_ATTR(leaf); + const bool MAYBE_UNUSED(leaf) = INSN_ATTR(leaf); % insn.pops.reverse_each.with_index.reverse_each do |pop, i| <%= pop[:decl] %> = <%= insn.cast_from_VALUE pop, "TOPN(#{i})"%>; % end @@ -35,13 +35,13 @@ INSN_ENTRY(<%= insn.name %>) % end /* ### Instruction preambles. ### */ - if (! leaf) ADD_PC(INSN_ATTR(width)); + ADD_PC(INSN_ATTR(width)); % if insn.handles_sp? POPN(INSN_ATTR(popn)); % end <%= insn.handle_canary "SETUP_CANARY(leaf)" -%> COLLECT_USAGE_INSN(INSN_ATTR(bin)); -% insn.opes.each_with_index do |ope, i| +% insn.operands.each_with_index do |ope, i| COLLECT_USAGE_OPERAND(INSN_ATTR(bin), <%= i %>, <%= ope[:name] %>); % end % unless body.empty? @@ -68,7 +68,6 @@ INSN_ENTRY(<%= insn.name %>) VM_ASSERT(!RB_TYPE_P(TOPN(<%= i %>), T_MOVED)); % end % end - if (leaf) ADD_PC(INSN_ATTR(width)); # undef INSN_ATTR /* ### Leave the instruction. ### */ diff --git a/tool/ruby_vm/views/_insn_leaf_info.erb b/tool/ruby_vm/views/_insn_leaf_info.erb new file mode 100644 index 0000000000..f30366ffda --- /dev/null +++ b/tool/ruby_vm/views/_insn_leaf_info.erb @@ -0,0 +1,18 @@ +MAYBE_UNUSED(static bool insn_leaf(int insn, const VALUE *opes)); +static bool +insn_leaf(int insn, const VALUE *opes) +{ + switch (insn) { +% RubyVM::Instructions.each do |insn| +% next if insn.is_a?(RubyVM::TraceInstruction) || insn.is_a?(RubyVM::ZJITInstruction) + case <%= insn.bin %>: + return attr_leaf_<%= insn.name %>(<%= + insn.operands.map.with_index do |ope, i| + "(#{ope[:type]})opes[#{i}]" + end.join(', ') + %>); +% end + default: + return false; + } +} diff --git a/tool/ruby_vm/views/_insn_len_info.erb b/tool/ruby_vm/views/_insn_len_info.erb index b292f42fb5..b29a405918 100644 --- a/tool/ruby_vm/views/_insn_len_info.erb +++ b/tool/ruby_vm/views/_insn_len_info.erb @@ -5,17 +5,32 @@ %# 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. +% +% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') } +% CONSTFUNC(MAYBE_UNUSED(static int insn_len(VALUE insn))); +RUBY_SYMBOL_EXPORT_BEGIN /* for debuggers */ +extern const uint8_t rb_vm_insn_len_info[VM_INSTRUCTION_SIZE]; +RUBY_SYMBOL_EXPORT_END + +#ifdef RUBY_VM_INSNS_INFO +const uint8_t rb_vm_insn_len_info[] = { +% insns.each_slice(23) do |row| + <%= row.map(&:width).join(', ') -%>, +% end +#if USE_ZJIT +% zjit_insns.each_slice(23) do |row| + <%= row.map(&:width).join(', ') -%>, +% end +#endif +}; + +ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_len_info); +#endif + int insn_len(VALUE i) { - static const char t[] = { -% RubyVM::Instructions.each_slice 23 do |a| - <%= a.map(&:width).join(', ') -%>, -% end - }; - - ASSERT_VM_INSTRUCTION_SIZE(t); - return t[i]; + return rb_vm_insn_len_info[i]; } diff --git a/tool/ruby_vm/views/_insn_name_info.erb b/tool/ruby_vm/views/_insn_name_info.erb index 767346093e..2862908631 100644 --- a/tool/ruby_vm/views/_insn_name_info.erb +++ b/tool/ruby_vm/views/_insn_name_info.erb @@ -6,33 +6,54 @@ %# conditions mentioned in the file COPYING are met. Consult the file for %# details. % -% a = RubyVM::Instructions.map {|i| i.name } -% b = (0...a.size) -% c = a.inject([0]) {|r, i| r << (r[-1] + i.length + 1) } -% c.pop +% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') } +% +% next_offset = 0 +% name_offset = proc do |i| +% offset = sprintf("%4d", next_offset) +% next_offset += i.name.length + 1 # insn.name + \0 +% offset +% end % CONSTFUNC(MAYBE_UNUSED(static const char *insn_name(VALUE insn))); +RUBY_SYMBOL_EXPORT_BEGIN /* for debuggers */ +extern const int rb_vm_max_insn_name_size; +extern const char rb_vm_insn_name_base[]; +extern const unsigned short rb_vm_insn_name_offset[VM_INSTRUCTION_SIZE]; +RUBY_SYMBOL_EXPORT_END + #ifdef RUBY_VM_INSNS_INFO -const int rb_vm_max_insn_name_size = <%= a.map(&:size).max %>; -#endif +%# "trace_" is longer than "zjit_", so USE_ZJIT doesn't impact the max name size. +const int rb_vm_max_insn_name_size = <%= RubyVM::Instructions.map { |i| i.name.size }.max %>; -const char * -insn_name(VALUE i) -{ - static const char x[] = -% a.each do |i| - <%=cstr i%> "\0" +const char rb_vm_insn_name_base[] = +% insns.each do |i| + <%= cstr i.name %> "\0" +% end +#if USE_ZJIT +% zjit_insns.each do |i| + <%= cstr i.name %> "\0" % end - ; +#endif + ; - static const unsigned short y[] = { -% c.each_slice 12 do |d| - <%= d.map {|i| sprintf("%4d", i) }.join(', ') %>, +const unsigned short rb_vm_insn_name_offset[] = { +% insns.each_slice(12) do |row| + <%= row.map(&name_offset).join(', ') %>, +% end +#if USE_ZJIT +% zjit_insns.each_slice(12) do |row| + <%= row.map(&name_offset).join(', ') %>, % end - }; +#endif +}; - ASSERT_VM_INSTRUCTION_SIZE(y); +ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_name_offset); +#endif - return &x[y[i]]; +const char * +insn_name(VALUE i) +{ + return &rb_vm_insn_name_base[rb_vm_insn_name_offset[i]]; } diff --git a/tool/ruby_vm/views/_insn_operand_info.erb b/tool/ruby_vm/views/_insn_operand_info.erb index 69d361521a..410869fcd3 100644 --- a/tool/ruby_vm/views/_insn_operand_info.erb +++ b/tool/ruby_vm/views/_insn_operand_info.erb @@ -6,32 +6,55 @@ %# conditions mentioned in the file COPYING are met. Consult the file for %# details. % -% a = RubyVM::Instructions.map {|i| i.operands_info } -% b = (0...a.size) -% c = a.inject([0]) {|r, i| r << (r[-1] + i.length + 1) } -% c.pop +% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') } +% +% operands_info = proc { |i| sprintf("%-6s", cstr(i.operands_info)) } +% +% next_offset = 0 +% op_offset = proc do |i| +% offset = sprintf("%3d", next_offset) +% next_offset += i.operands_info.length + 1 # insn.operands_info + \0 +% offset +% end % CONSTFUNC(MAYBE_UNUSED(static const char *insn_op_types(VALUE insn))); CONSTFUNC(MAYBE_UNUSED(static int insn_op_type(VALUE insn, long pos))); -const char * -insn_op_types(VALUE i) -{ - static const char x[] = -% a.each_slice 5 do |d| - <%= d.map {|i| sprintf("%-6s", cstr(i)) }.join(' "\0" ') %> "\0" +RUBY_SYMBOL_EXPORT_BEGIN /* for debuggers */ +extern const char rb_vm_insn_op_base[]; +extern const unsigned short rb_vm_insn_op_offset[VM_INSTRUCTION_SIZE]; +RUBY_SYMBOL_EXPORT_END + +#ifdef RUBY_VM_INSNS_INFO +const char rb_vm_insn_op_base[] = +% insns.each_slice(5) do |row| + <%= row.map(&operands_info).join(' "\0" ') %> "\0" +% end +#if USE_ZJIT +% zjit_insns.each_slice(5) do |row| + <%= row.map(&operands_info).join(' "\0" ') %> "\0" % end - ; +#endif + ; - static const unsigned short y[] = { -% c.each_slice 12 do |d| - <%= d.map {|i| sprintf("%3d", i) }.join(', ') %>, +const unsigned short rb_vm_insn_op_offset[] = { +% insns.each_slice(12) do |row| + <%= row.map(&op_offset).join(', ') %>, +% end +#if USE_ZJIT +% zjit_insns.each_slice(12) do |row| + <%= row.map(&op_offset).join(', ') %>, % end - }; +#endif +}; - ASSERT_VM_INSTRUCTION_SIZE(y); +ASSERT_VM_INSTRUCTION_SIZE(rb_vm_insn_op_offset); +#endif - return &x[y[i]]; +const char * +insn_op_types(VALUE i) +{ + return &rb_vm_insn_op_base[rb_vm_insn_op_offset[i]]; } int diff --git a/tool/ruby_vm/views/_insn_sp_pc_dependency.erb b/tool/ruby_vm/views/_insn_sp_pc_dependency.erb deleted file mode 100644 index 95528fbbf4..0000000000 --- a/tool/ruby_vm/views/_insn_sp_pc_dependency.erb +++ /dev/null @@ -1,27 +0,0 @@ -%# -*- C -*- -%# Copyright (c) 2019 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. -%# -PUREFUNC(MAYBE_UNUSED(static bool insn_may_depend_on_sp_or_pc(int insn, const VALUE *opes))); - -static bool -insn_may_depend_on_sp_or_pc(int insn, const VALUE *opes) -{ - switch (insn) { -% RubyVM::Instructions.each do |insn| -% # handles_sp?: If true, it requires to move sp in JIT -% # always_leaf?: If false, it may call an arbitrary method. pc should be moved -% # before the call, and the method may refer to caller's pc (lineno). -% unless !insn.is_a?(RubyVM::TraceInstructions) && !insn.handles_sp? && insn.always_leaf? - case <%= insn.bin %>: -% end -% end - return true; - default: - return false; - } -} diff --git a/tool/ruby_vm/views/_insn_type_chars.erb b/tool/ruby_vm/views/_insn_type_chars.erb index 4e1f63e660..27daec6c6d 100644 --- a/tool/ruby_vm/views/_insn_type_chars.erb +++ b/tool/ruby_vm/views/_insn_type_chars.erb @@ -11,3 +11,22 @@ enum ruby_insn_type_chars { <%= t %> = '<%= c %>', % end }; + +static inline union iseq_inline_storage_entry * +ISEQ_IS_ENTRY_START(const struct rb_iseq_constant_body *body, char op_type) +{ + unsigned int relative_ic_offset = 0; + switch (op_type) { + case TS_IC: + relative_ic_offset += body->ise_size; + case TS_ISE: + relative_ic_offset += body->icvarc_size; + case TS_ICVARC: + relative_ic_offset += body->ivc_size; + case TS_IVC: + break; + default: + rb_bug("Wrong op type"); + } + return &body->is_entries[relative_ic_offset]; +} diff --git a/tool/ruby_vm/views/_leaf_helpers.erb b/tool/ruby_vm/views/_leaf_helpers.erb index 2637f8777d..2756fa2dec 100644 --- a/tool/ruby_vm/views/_leaf_helpers.erb +++ b/tool/ruby_vm/views/_leaf_helpers.erb @@ -16,13 +16,12 @@ leafness_of_defined(rb_num_t op_type) /* see also: vm_insnhelper.c:vm_defined() */ switch (op_type) { case DEFINED_IVAR: - case DEFINED_IVAR2: case DEFINED_GVAR: case DEFINED_CVAR: case DEFINED_YIELD: case DEFINED_REF: case DEFINED_ZSUPER: - return false; + return true; case DEFINED_CONST: case DEFINED_CONST_FROM: /* has rb_autoload_load(); */ diff --git a/tool/ruby_vm/views/_mjit_compile_insn.erb b/tool/ruby_vm/views/_mjit_compile_insn.erb deleted file mode 100644 index e688f0853e..0000000000 --- a/tool/ruby_vm/views/_mjit_compile_insn.erb +++ /dev/null @@ -1,91 +0,0 @@ -% # -*- 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. - fprintf(f, "{\n"); - { -% # compiler: Prepare operands which may be used by `insn.call_attribute` -% insn.opes.each_with_index do |ope, i| - MAYBE_UNUSED(<%= ope.fetch(:decl) %>) = (<%= ope.fetch(:type) %>)operands[<%= i %>]; -% end -% -% # JIT: Declare stack_size to be used in some macro of _mjit_compile_insn_body.erb - if (status->local_stack_p) { - fprintf(f, " MAYBE_UNUSED(unsigned int) stack_size = %u;\n", b->stack_size); - } -% -% # JIT: Declare variables for operands, popped values and return values -% insn.declarations.each do |decl| - fprintf(f, " <%= decl %>;\n"); -% end - -% # JIT: Set const expressions for `RubyVM::OperandsUnifications` insn -% insn.preamble.each do |amble| - fprintf(f, "<%= amble.expr.sub(/const \S+\s+/, '') %>\n"); -% end -% -% # JIT: Initialize operands -% insn.opes.each_with_index do |ope, i| - fprintf(f, " <%= ope.fetch(:name) %> = (<%= ope.fetch(:type) %>)0x%"PRIxVALUE";", operands[<%= i %>]); -% case ope.fetch(:type) -% when 'ID' - comment_id(f, (ID)operands[<%= i %>]); -% when 'CALL_DATA' - comment_id(f, vm_ci_mid(((CALL_DATA)operands[<%= i %>])->ci)); -% when 'VALUE' - if (SYMBOL_P((VALUE)operands[<%= i %>])) comment_id(f, SYM2ID((VALUE)operands[<%= i %>])); -% end - fprintf(f, "\n"); -% end -% -% # JIT: Initialize popped values -% insn.pops.reverse_each.with_index.reverse_each do |pop, i| - fprintf(f, " <%= pop.fetch(:name) %> = stack[%d];\n", b->stack_size - <%= i + 1 %>); -% end -% -% # JIT: move sp and pc if necessary -<%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%> -% -% # JIT: Print insn body in insns.def -<%= render 'mjit_compile_insn_body', locals: { insn: insn } -%> -% -% # JIT: Set return values -% insn.rets.reverse_each.with_index do |ret, i| -% # TOPN(n) = ... - fprintf(f, " stack[%d] = <%= ret.fetch(:name) %>;\n", b->stack_size + (int)<%= insn.call_attribute('sp_inc') %> - <%= i + 1 %>); -% end -% -% # JIT: We should evaluate ISeq modified for TracePoint if it's enabled. Note: This is slow. -% unless insn.always_leaf? - fprintf(f, " if (UNLIKELY(!mjit_call_p)) {\n"); - fprintf(f, " reg_cfp->sp = vm_base_ptr(reg_cfp) + %d;\n", b->stack_size + (int)<%= insn.call_attribute('sp_inc') %>); - if (!pc_moved_p) { - fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", next_pos); - } - fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel_invalidate_all);\n"); - fprintf(f, " goto cancel;\n"); - fprintf(f, " }\n"); -% end -% -% # compiler: Move JIT compiler's internal stack pointer - b->stack_size += <%= insn.call_attribute('sp_inc') %>; - } - fprintf(f, "}\n"); -% -% # compiler: If insn has conditional JUMP, the code should go to the branch not targeted by JUMP next. -% if insn.expr.expr =~ /if\s+\([^{}]+\)\s+\{[^{}]+JUMP\([^)]+\);[^{}]+\}/ - if (ALREADY_COMPILED_P(status, pos + insn_len(insn))) { - fprintf(f, "goto label_%d;\n", pos + insn_len(insn)); - } - else { - compile_insns(f, body, b->stack_size, pos + insn_len(insn), status); - } -% end -% -% # compiler: If insn returns (leave) or does longjmp (throw), the branch should no longer be compiled. TODO: create attr for it? -% if insn.expr.expr =~ /\sTHROW_EXCEPTION\([^)]+\);/ || insn.expr.expr =~ /\bvm_pop_frame\(/ - b->finish_p = TRUE; -% end diff --git a/tool/ruby_vm/views/_mjit_compile_insn_body.erb b/tool/ruby_vm/views/_mjit_compile_insn_body.erb deleted file mode 100644 index bc77b02b71..0000000000 --- a/tool/ruby_vm/views/_mjit_compile_insn_body.erb +++ /dev/null @@ -1,115 +0,0 @@ -% # -*- 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. -% -% to_cstr = lambda do |line| -% normalized = line.gsub(/\t/, ' ' * 8) -% indented = normalized.sub(/\A(?!#)/, ' ') # avoid indenting preprocessor -% rstring2cstr(indented.rstrip).sub(/"\z/, '\\n"') -% end -% -% # -% # Expand simple macro, which doesn't require dynamic C code. -% # -% expand_simple_macros = lambda do |arg_expr| -% arg_expr.dup.tap do |expr| -% # For `leave`. We can't proceed next ISeq in the same JIT function. -% expr.gsub!(/^(?<indent>\s*)RESTORE_REGS\(\);\n/) do -% indent = Regexp.last_match[:indent] -% <<-RESTORE_REGS.gsub(/^ +/, '') -% #if OPT_CALL_THREADED_CODE -% #{indent}rb_ec_thread_ptr(ec)->retval = val; -% #{indent}return 0; -% #else -% #{indent}return val; -% #endif -% RESTORE_REGS -% end -% expr.gsub!(/^(?<indent>\s*)NEXT_INSN\(\);\n/) do -% indent = Regexp.last_match[:indent] -% <<-end.gsub(/^ +/, '') -% #{indent}UNREACHABLE_RETURN(Qundef); -% end -% end -% end -% end -% -% # -% # Print a body of insn, but with macro expansion. -% # -% expand_simple_macros.call(insn.expr.expr).each_line do |line| -% # -% # Expand dynamic macro here (only JUMP for now) -% # -% # TODO: support combination of following macros in the same line -% case line -% when /\A\s+JUMP\((?<dest>[^)]+)\);\s+\z/ -% dest = Regexp.last_match[:dest] -% -% if insn.name == 'opt_case_dispatch' # special case... TODO: use another macro to avoid checking name - { - struct case_dispatch_var arg; - arg.f = f; - arg.base_pos = pos + insn_len(insn); - arg.last_value = Qundef; - - fprintf(f, " switch (<%= dest %>) {\n"); - st_foreach(RHASH_TBL_RAW(hash), compile_case_dispatch_each, (VALUE)&arg); - fprintf(f, " case %lu:\n", else_offset); - fprintf(f, " goto label_%lu;\n", arg.base_pos + else_offset); - fprintf(f, " }\n"); - } -% else -% # Before we `goto` next insn, we need to set return values, especially for getinlinecache -% insn.rets.reverse_each.with_index do |ret, i| -% # TOPN(n) = ... - fprintf(f, " stack[%d] = <%= ret.fetch(:name) %>;\n", b->stack_size + (int)<%= insn.call_attribute('sp_inc') %> - <%= i + 1 %>); -% end -% - next_pos = pos + insn_len(insn) + (unsigned int)<%= dest %>; - fprintf(f, " goto label_%d;\n", next_pos); -% end -% when /\A\s+CALL_SIMPLE_METHOD\(\);\s+\z/ -% # For `opt_xxx`'s fallbacks. - if (status->local_stack_p) { - 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_opt_insn);\n"); - fprintf(f, " goto cancel;\n"); -% when /\A(?<prefix>.+\b)INSN_LABEL\((?<name>[^)]+)\)(?<suffix>.+)\z/m -% prefix, name, suffix = Regexp.last_match[:prefix], Regexp.last_match[:name], Regexp.last_match[:suffix] - fprintf(f, " <%= prefix.gsub(/\t/, ' ' * 8) %>INSN_LABEL(<%= name %>_%d)<%= suffix.sub(/\n/, '\n') %>", pos); -% else -% if insn.handles_sp? -% # If insn.handles_sp? is true, cfp->sp might be changed inside insns (like vm_caller_setup_arg_block) -% # and thus we need to use cfp->sp, even when local_stack_p is TRUE. When insn.handles_sp? is true, -% # cfp->sp should be available too because _mjit_compile_pc_and_sp.erb sets it. - fprintf(f, <%= to_cstr.call(line) %>); -% else -% # If local_stack_p is TRUE and insn.handles_sp? is false, stack values are only available in local variables -% # for stack. So we need to replace those macros if local_stack_p is TRUE here. -% case line -% when /\bGET_SP\(\)/ -% # reg_cfp->sp - fprintf(f, <%= to_cstr.call(line.sub(/\bGET_SP\(\)/, '%s')) %>, (status->local_stack_p ? "(stack + stack_size)" : "GET_SP()")); -% when /\bSTACK_ADDR_FROM_TOP\((?<num>[^)]+)\)/ -% # #define STACK_ADDR_FROM_TOP(n) (GET_SP()-(n)) -% num = Regexp.last_match[:num] - fprintf(f, <%= to_cstr.call(line.sub(/\bSTACK_ADDR_FROM_TOP\(([^)]+)\)/, '%s')) %>, - (status->local_stack_p ? "(stack + (stack_size - (<%= num %>)))" : "STACK_ADDR_FROM_TOP(<%= num %>)")); -% when /\bTOPN\((?<num>[^)]+)\)/ -% # #define TOPN(n) (*(GET_SP()-(n)-1)) -% num = Regexp.last_match[:num] - fprintf(f, <%= to_cstr.call(line.sub(/\bTOPN\(([^)]+)\)/, '%s')) %>, - (status->local_stack_p ? "*(stack + (stack_size - (<%= num %>) - 1))" : "TOPN(<%= num %>)")); -% else - fprintf(f, <%= to_cstr.call(line) %>); -% end -% end -% end -% end diff --git a/tool/ruby_vm/views/_mjit_compile_invokebuiltin.erb b/tool/ruby_vm/views/_mjit_compile_invokebuiltin.erb deleted file mode 100644 index 9a9e53af40..0000000000 --- a/tool/ruby_vm/views/_mjit_compile_invokebuiltin.erb +++ /dev/null @@ -1,29 +0,0 @@ -% # -*- C -*- -% # Copyright (c) 2020 Urabe, Shyouhei. 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. -% -% insn.opes.each_with_index do |ope, i| - <%= ope.fetch(:decl) %> = (<%= ope.fetch(:type) %>)operands[<%= i %>]; -% end - rb_snum_t sp_inc = <%= insn.call_attribute('sp_inc') %>; - unsigned sp = b->stack_size + (unsigned)sp_inc; - VM_ASSERT(b->stack_size > -sp_inc); - VM_ASSERT(sp_inc < UINT_MAX - b->stack_size); - - if (bf->compiler) { - fprintf(f, "{\n"); - fprintf(f, " VALUE val;\n"); - bf->compiler(f, <%= - insn.name == 'invokebuiltin' ? '-1' : '(rb_num_t)operands[1]' - %>, b->stack_size); - fprintf(f, " stack[%u] = val;\n", sp - 1); - fprintf(f, "}\n"); -% if insn.name != 'opt_invokebuiltin_delegate_leave' - b->stack_size = sp; - break; -% end - } diff --git a/tool/ruby_vm/views/_mjit_compile_ivar.erb b/tool/ruby_vm/views/_mjit_compile_ivar.erb deleted file mode 100644 index e50f4bfc98..0000000000 --- a/tool/ruby_vm/views/_mjit_compile_ivar.erb +++ /dev/null @@ -1,101 +0,0 @@ -% # -*- 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. -% -% # Optimized case of get_instancevariable instruction. -#if OPT_IC_FOR_IVAR -{ -% # compiler: Prepare operands which may be used by `insn.call_attribute` -% insn.opes.each_with_index do |ope, i| - MAYBE_UNUSED(<%= ope.fetch(:decl) %>) = (<%= ope.fetch(:type) %>)operands[<%= i %>]; -% end -% # compiler: Use copied IVC to avoid race condition - IVC ic_copy = &(status->is_entries + ((union iseq_inline_storage_entry *)ic - body->is_entries))->iv_cache; -% - if (!status->compile_info->disable_ivar_cache && ic_copy->ic_serial) { // Only initialized (ic_serial > 0) IVCs are optimized -% # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`. -% # <%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%> -% -% # JIT: prepare vm_getivar/vm_setivar arguments and variables - fprintf(f, "{\n"); - fprintf(f, " VALUE obj = GET_SELF();\n"); - fprintf(f, " const st_index_t index = %"PRIuSIZE";\n", ic_copy->index); - if (status->merge_ivar_guards_p) { -% # JIT: Access ivar without checking these VM_ASSERTed prerequisites as we checked them in the beginning of `mjit_compile_body` - fprintf(f, " VM_ASSERT(RB_TYPE_P(obj, T_OBJECT));\n"); - fprintf(f, " VM_ASSERT((rb_serial_t)%"PRI_SERIALT_PREFIX"u == RCLASS_SERIAL(RBASIC(obj)->klass));\n", ic_copy->ic_serial); - fprintf(f, " VM_ASSERT(index < ROBJECT_NUMIV(obj));\n"); -% if insn.name == 'setinstancevariable' - fprintf(f, " if (LIKELY(!RB_OBJ_FROZEN(obj) && %sRB_FL_ANY_RAW(obj, ROBJECT_EMBED))) {\n", status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "!" : ""); - fprintf(f, " RB_OBJ_WRITE(obj, &ROBJECT(obj)->as.%s, stack[%d]);\n", - status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "heap.ivptr[index]" : "ary[index]", b->stack_size - 1); - fprintf(f, " }\n"); -% else - fprintf(f, " VALUE val;\n"); - fprintf(f, " if (LIKELY(%sRB_FL_ANY_RAW(obj, ROBJECT_EMBED) && (val = ROBJECT(obj)->as.%s) != Qundef)) {\n", - status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "!" : "", - status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "heap.ivptr[index]" : "ary[index]"); - fprintf(f, " stack[%d] = val;\n", b->stack_size); - fprintf(f, " }\n"); -%end - } - else { - fprintf(f, " const rb_serial_t ic_serial = (rb_serial_t)%"PRI_SERIALT_PREFIX"u;\n", ic_copy->ic_serial); -% # JIT: cache hit path of vm_getivar/vm_setivar, or cancel JIT (recompile it with exivar) -% if insn.name == 'setinstancevariable' - fprintf(f, " if (LIKELY(RB_TYPE_P(obj, T_OBJECT) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && index < ROBJECT_NUMIV(obj) && !RB_OBJ_FROZEN(obj))) {\n"); - fprintf(f, " VALUE *ptr = ROBJECT_IVPTR(obj);\n"); - fprintf(f, " RB_OBJ_WRITE(obj, &ptr[index], stack[%d]);\n", b->stack_size - 1); - fprintf(f, " }\n"); -% else - fprintf(f, " VALUE val;\n"); - fprintf(f, " if (LIKELY(RB_TYPE_P(obj, T_OBJECT) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && index < ROBJECT_NUMIV(obj) && (val = ROBJECT_IVPTR(obj)[index]) != Qundef)) {\n"); - fprintf(f, " stack[%d] = val;\n", b->stack_size); - fprintf(f, " }\n"); -% end - } - fprintf(f, " else {\n"); - fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", pos); - fprintf(f, " reg_cfp->sp = vm_base_ptr(reg_cfp) + %d;\n", b->stack_size); - fprintf(f, " goto ivar_cancel;\n"); - fprintf(f, " }\n"); - -% # compiler: Move JIT compiler's internal stack pointer - b->stack_size += <%= insn.call_attribute('sp_inc') %>; - fprintf(f, "}\n"); - break; - } -% if insn.name == 'getinstancevariable' - else if (!status->compile_info->disable_exivar_cache && ic_copy->ic_serial) { -% # JIT: optimize away motion of sp and pc. This path does not call rb_warning() and so it's always leaf and not `handles_sp`. -% # <%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%> -% -% # JIT: prepare vm_getivar's arguments and variables - fprintf(f, "{\n"); - fprintf(f, " VALUE obj = GET_SELF();\n"); - fprintf(f, " const rb_serial_t ic_serial = (rb_serial_t)%"PRI_SERIALT_PREFIX"u;\n", ic_copy->ic_serial); - fprintf(f, " const st_index_t index = %"PRIuSIZE";\n", ic_copy->index); -% # JIT: cache hit path of vm_getivar, or cancel JIT (recompile it without any ivar optimization) - fprintf(f, " struct gen_ivtbl *ivtbl;\n"); - fprintf(f, " VALUE val;\n"); - fprintf(f, " if (LIKELY(FL_TEST_RAW(obj, FL_EXIVAR) && ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass) && st_lookup(rb_ivar_generic_ivtbl(), (st_data_t)obj, (st_data_t *)&ivtbl) && index < ivtbl->numiv && (val = ivtbl->ivptr[index]) != Qundef)) {\n"); - fprintf(f, " stack[%d] = val;\n", b->stack_size); - fprintf(f, " }\n"); - fprintf(f, " else {\n"); - fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", pos); - fprintf(f, " reg_cfp->sp = vm_base_ptr(reg_cfp) + %d;\n", b->stack_size); - fprintf(f, " goto exivar_cancel;\n"); - fprintf(f, " }\n"); - -% # compiler: Move JIT compiler's internal stack pointer - b->stack_size += <%= insn.call_attribute('sp_inc') %>; - fprintf(f, "}\n"); - break; - } -% end -} -#endif // OPT_IC_FOR_IVAR diff --git a/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb b/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb deleted file mode 100644 index 683dce93bb..0000000000 --- a/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb +++ /dev/null @@ -1,37 +0,0 @@ -% # 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. -% -% # JIT: Move pc for catch table on catch_except_p, and for #caller_locations and rb_profile_frames on !insn.always_leaf? - MAYBE_UNUSED(bool pc_moved_p) = false; - if (body->catch_except_p || <%= insn.always_leaf? ? 'FALSE' : 'TRUE' %>) { - fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", next_pos); /* ADD_PC(INSN_ATTR(width)); */ - pc_moved_p = true; - } -% -% # JIT: move sp to use or preserve stack variables - if (status->local_stack_p) { -% # sp motion is optimized away for `handles_sp? #=> false` case. -% # Thus sp should be set properly before `goto cancel`. -% if insn.handles_sp? -% # JIT-only behavior (pushing JIT's local variables to VM's stack): - { - rb_snum_t i, push_size; - push_size = -<%= insn.call_attribute('sp_inc') %> + <%= insn.rets.size %> - <%= insn.pops.size %>; - fprintf(f, " reg_cfp->sp = vm_base_ptr(reg_cfp) + %ld;\n", push_size); /* POPN(INSN_ATTR(popn)); */ - for (i = 0; i < push_size; i++) { - fprintf(f, " *(reg_cfp->sp + %ld) = stack[%ld];\n", i - push_size, (rb_snum_t)b->stack_size - push_size + i); - } - } -% end - } - else { -% if insn.handles_sp? - fprintf(f, " reg_cfp->sp = vm_base_ptr(reg_cfp) + %d;\n", b->stack_size - <%= insn.pops.size %>); /* POPN(INSN_ATTR(popn)); */ -% else - fprintf(f, " reg_cfp->sp = vm_base_ptr(reg_cfp) + %d;\n", b->stack_size); -% end - } diff --git a/tool/ruby_vm/views/_mjit_compile_send.erb b/tool/ruby_vm/views/_mjit_compile_send.erb deleted file mode 100644 index 2dff0165bd..0000000000 --- a/tool/ruby_vm/views/_mjit_compile_send.erb +++ /dev/null @@ -1,115 +0,0 @@ -% # -*- 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. -% -% # Optimized case of send / opt_send_without_block instructions. -{ -% # compiler: Prepare operands which may be used by `insn.call_attribute` -% insn.opes.each_with_index do |ope, i| - MAYBE_UNUSED(<%= ope.fetch(:decl) %>) = (<%= ope.fetch(:type) %>)operands[<%= i %>]; -% end -% # compiler: Use captured cc to avoid race condition - const struct rb_callcache *captured_cc = captured_cc_entries(status)[call_data_index(cd, body)]; -% -% # compiler: Inline send insn where some supported fastpath is used. - const rb_iseq_t *iseq = NULL; - const CALL_INFO ci = cd->ci; - int kw_splat = IS_ARGS_KW_SPLAT(ci) > 0; - extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci); - if (!status->compile_info->disable_send_cache && has_valid_method_type(captured_cc) && ( -% # `CC_SET_FASTPATH(cd->cc, vm_call_cfunc_with_frame, ...)` in `vm_call_cfunc` - (vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_CFUNC - && !rb_splat_or_kwargs_p(ci) && !kw_splat) -% # `CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(...), vm_call_iseq_optimizable_p(...))` in `vm_callee_setup_arg`, -% # and support only non-VM_CALL_TAILCALL path inside it - || (vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_ISEQ - && fastpath_applied_iseq_p(ci, captured_cc, iseq = def_iseq_ptr(vm_cc_cme(captured_cc)->def)) - && !(vm_ci_flag(ci) & VM_CALL_TAILCALL)) - )) { - if (false && vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_CFUNC) - fprintf(stderr, " * %s\n", rb_id2name(vm_ci_mid(ci))); - - int sp_inc = (int)sp_inc_of_sendish(ci); - fprintf(f, "{\n"); - -% # JIT: Invalidate call cache if it requires vm_search_method. This allows to inline some of following things. - bool opt_class_of = !maybe_special_const_class_p(captured_cc->klass); // If true, use RBASIC_CLASS instead of CLASS_OF to reduce code size - fprintf(f, " const struct rb_callcache *cc = (const struct rb_callcache *)0x%"PRIxVALUE";\n", (VALUE)captured_cc); - fprintf(f, " const rb_callable_method_entry_t *cc_cme = (const rb_callable_method_entry_t *)0x%"PRIxVALUE";\n", (VALUE)vm_cc_cme(captured_cc)); - fprintf(f, " const VALUE recv = stack[%d];\n", b->stack_size + sp_inc - 1); - fprintf(f, " if (UNLIKELY(%s || !vm_cc_valid_p(cc, cc_cme, %s(recv)))) {\n", opt_class_of ? "RB_SPECIAL_CONST_P(recv)" : "false", opt_class_of ? "RBASIC_CLASS" : "CLASS_OF"); - fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", pos); - fprintf(f, " reg_cfp->sp = vm_base_ptr(reg_cfp) + %d;\n", b->stack_size); - fprintf(f, " goto send_cancel;\n"); - fprintf(f, " }\n"); - -% # JIT: move sp and pc if necessary -<%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%> - -% # JIT: If ISeq is inlinable, call the inlined method without pushing a frame. - if (iseq && status->inlined_iseqs != NULL && iseq->body == status->inlined_iseqs[pos]) { - fprintf(f, " {\n"); - fprintf(f, " VALUE orig_self = reg_cfp->self;\n"); - fprintf(f, " reg_cfp->self = stack[%d];\n", b->stack_size + sp_inc - 1); - fprintf(f, " stack[%d] = _mjit%d_inlined_%d(ec, reg_cfp, orig_self, original_iseq);\n", b->stack_size + sp_inc - 1, status->compiled_id, pos); - fprintf(f, " reg_cfp->self = orig_self;\n"); - fprintf(f, " }\n"); - } - else { -% # JIT: Forked `vm_sendish` (except method_explorer = vm_search_method_wrap) to inline various things - fprintf(f, " {\n"); - fprintf(f, " VALUE val;\n"); - fprintf(f, " struct rb_calling_info calling;\n"); -% if insn.name == 'send' - fprintf(f, " calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, (const struct rb_callinfo *)0x%"PRIxVALUE", (rb_iseq_t *)0x%"PRIxVALUE", FALSE);\n", (VALUE)ci, (VALUE)blockiseq); -% else - fprintf(f, " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n"); -% end - fprintf(f, " calling.kw_splat = %d;\n", kw_splat); - fprintf(f, " calling.recv = stack[%d];\n", b->stack_size + sp_inc - 1); - fprintf(f, " calling.argc = %d;\n", vm_ci_argc(ci)); - - if (vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_CFUNC) { -% # TODO: optimize this more - fprintf(f, " struct rb_call_data cc_cd = { .ci = (CALL_INFO)0x%"PRIxVALUE", .cc = cc };\n", (VALUE)ci); // creating local cd here because operand's cd->cc may not be the same as inlined cc. - fprintf(f, " val = vm_call_cfunc_with_frame(ec, reg_cfp, &calling, &cc_cd);\n"); - } - else { // VM_METHOD_TYPE_ISEQ -% # fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE - fprintf(f, " vm_call_iseq_setup_normal(ec, reg_cfp, &calling, cc_cme, 0, %d, %d);\n", iseq->body->param.size, iseq->body->local_table_size); - if (iseq->body->catch_except_p) { - fprintf(f, " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"); - fprintf(f, " val = vm_exec(ec, TRUE);\n"); - } - else { - fprintf(f, " if ((val = mjit_exec(ec)) == Qundef) {\n"); - fprintf(f, " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"); // This is vm_call0_body's code after vm_call_iseq_setup - fprintf(f, " val = vm_exec(ec, FALSE);\n"); - fprintf(f, " }\n"); - } - } - fprintf(f, " stack[%d] = val;\n", b->stack_size + sp_inc - 1); - fprintf(f, " }\n"); - -% # JIT: We should evaluate ISeq modified for TracePoint if it's enabled. Note: This is slow. - fprintf(f, " if (UNLIKELY(!mjit_call_p)) {\n"); - fprintf(f, " reg_cfp->sp = vm_base_ptr(reg_cfp) + %d;\n", b->stack_size + (int)<%= insn.call_attribute('sp_inc') %>); - if (!pc_moved_p) { - fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", next_pos); - } - fprintf(f, " RB_DEBUG_COUNTER_INC(mjit_cancel_invalidate_all);\n"); - fprintf(f, " goto cancel;\n"); - fprintf(f, " }\n"); - } - -% # compiler: Move JIT compiler's internal stack pointer - b->stack_size += <%= insn.call_attribute('sp_inc') %>; - - fprintf(f, "}\n"); - break; - } -} diff --git a/tool/ruby_vm/views/_sp_inc_helpers.erb b/tool/ruby_vm/views/_sp_inc_helpers.erb index d0b0bd79ef..740fe10142 100644 --- a/tool/ruby_vm/views/_sp_inc_helpers.erb +++ b/tool/ruby_vm/views/_sp_inc_helpers.erb @@ -18,7 +18,7 @@ sp_inc_of_sendish(const struct rb_callinfo *ci) * 3. Pop receiver. * 4. Push return value. */ - const int argb = (vm_ci_flag(ci) & VM_CALL_ARGS_BLOCKARG) ? 1 : 0; + const int argb = (vm_ci_flag(ci) & (VM_CALL_ARGS_BLOCKARG | VM_CALL_FORWARDING)) ? 1 : 0; const int argc = vm_ci_argc(ci); const int recv = 1; const int retn = 1; diff --git a/tool/ruby_vm/views/_trace_instruction.erb b/tool/ruby_vm/views/_trace_instruction.erb index be2b091d48..3588207d39 100644 --- a/tool/ruby_vm/views/_trace_instruction.erb +++ b/tool/ruby_vm/views/_trace_instruction.erb @@ -10,7 +10,12 @@ /* insn <%= insn.pretty_name %> */ INSN_ENTRY(<%= insn.name %>) { - vm_trace(ec, GET_CFP(), GET_PC()); - DISPATCH_ORIGINAL_INSN(<%= insn.jump_destination %>); + vm_trace(ec, GET_CFP()); +% if insn.name =~ +% /\Atrace_opt_(plus|minus|mult|div|mod|eq|neq|lt|le|gt|ge|ltlt|and|or|aref|aset|length|size|empty_p|nil_p|succ|not|regexpmatch2)\z/ +% jump_dest = "opt_send_without_block" +% end + <%= 'ADD_PC(1);' if insn.name == 'trace_opt_neq' %> + DISPATCH_ORIGINAL_INSN(<%= jump_dest || insn.jump_destination %>); END_INSN(<%= insn.name %>); } diff --git a/tool/ruby_vm/views/_zjit_helpers.erb b/tool/ruby_vm/views/_zjit_helpers.erb new file mode 100644 index 0000000000..1185dbd9d8 --- /dev/null +++ b/tool/ruby_vm/views/_zjit_helpers.erb @@ -0,0 +1,31 @@ +#if USE_ZJIT + +MAYBE_UNUSED(static int vm_bare_insn_to_zjit_insn(int insn)); +static int +vm_bare_insn_to_zjit_insn(int insn) +{ + switch (insn) { +% RubyVM::ZJITInstruction.all.each do |insn| + case BIN(<%= insn.jump_destination %>): + return <%= insn.bin %>; +% end + default: + return insn; + } +} + +MAYBE_UNUSED(static int vm_zjit_insn_to_bare_insn(int insn)); +static int +vm_zjit_insn_to_bare_insn(int insn) +{ + switch (insn) { +% RubyVM::ZJITInstruction.all.each do |insn| + case <%= insn.bin %>: + return BIN(<%= insn.jump_destination %>); +% end + default: + return insn; + } +} + +#endif diff --git a/tool/ruby_vm/views/_zjit_instruction.erb b/tool/ruby_vm/views/_zjit_instruction.erb new file mode 100644 index 0000000000..7fd657697c --- /dev/null +++ b/tool/ruby_vm/views/_zjit_instruction.erb @@ -0,0 +1,12 @@ +#if USE_ZJIT + +/* insn <%= insn.pretty_name %> */ +INSN_ENTRY(<%= insn.name %>) +{ + START_OF_ORIGINAL_INSN(<%= insn.name %>); + rb_zjit_profile_insn(BIN(<%= insn.jump_destination %>), ec); + DISPATCH_ORIGINAL_INSN(<%= insn.jump_destination %>); + END_INSN(<%= insn.name %>); +} + +#endif diff --git a/tool/ruby_vm/views/insns.inc.erb b/tool/ruby_vm/views/insns.inc.erb index 29981a8a2d..6521a89b8a 100644 --- a/tool/ruby_vm/views/insns.inc.erb +++ b/tool/ruby_vm/views/insns.inc.erb @@ -6,21 +6,36 @@ %# 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. +% +% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') } +% <%= render 'copyright' %> <%= render 'notice', locals: { this_file: 'contains YARV instruction list', edit: __FILE__, } -%> +#ifndef INSNS_INC +#define INSNS_INC 1 + /* BIN : Basic Instruction Name */ #define BIN(n) YARVINSN_##n enum ruby_vminsn_type { -% RubyVM::Instructions.each do |i| +% insns.each do |i| + <%= i.bin %>, +% end +#if USE_ZJIT +% zjit_insns.each do |i| <%= i.bin %>, % end +#endif VM_INSTRUCTION_SIZE }; +#define VM_BARE_INSTRUCTION_SIZE <%= RubyVM::Instructions.count { |i| i.name !~ /\A(trace|zjit)_/ } %> + #define ASSERT_VM_INSTRUCTION_SIZE(array) \ STATIC_ASSERT(numberof_##array, numberof(array) == VM_INSTRUCTION_SIZE) + +#endif diff --git a/tool/ruby_vm/views/insns_info.inc.erb b/tool/ruby_vm/views/insns_info.inc.erb index 2ca5aca7cf..48dd0e8832 100644 --- a/tool/ruby_vm/views/insns_info.inc.erb +++ b/tool/ruby_vm/views/insns_info.inc.erb @@ -11,12 +11,16 @@ this_file: 'contains instruction information for yarv instruction sequence.', edit: __FILE__, } %> +#ifndef INSNS_INFO_INC +#define INSNS_INFO_INC 1 <%= render 'insn_type_chars' %> <%= render 'insn_name_info' %> <%= render 'insn_len_info' %> <%= render 'insn_operand_info' %> <%= render 'leaf_helpers' %> <%= render 'sp_inc_helpers' %> +<%= render 'zjit_helpers' %> <%= render 'attributes' %> +<%= render 'insn_leaf_info' %> <%= render 'comptime_insn_stack_increase' %> -<%= render 'insn_sp_pc_dependency' %> +#endif diff --git a/tool/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb b/tool/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb new file mode 100644 index 0000000000..793528af5d --- /dev/null +++ b/tool/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb @@ -0,0 +1,14 @@ +module RubyVM::RJIT # :nodoc: all + Instruction = Data.define(:name, :bin, :len, :operands) + + INSNS = { +% RubyVM::Instructions.each_with_index do |insn, i| + <%= i %> => Instruction.new( + name: :<%= insn.name %>, + bin: <%= i %>, # BIN(<%= insn.name %>) + len: <%= insn.width %>, # insn_len + operands: <%= (insn.operands unless insn.name.start_with?(/trace_|zjit_/)).inspect %>, + ), +% end + } +end diff --git a/tool/ruby_vm/views/mjit_compile.inc.erb b/tool/ruby_vm/views/mjit_compile.inc.erb deleted file mode 100644 index 6836d59a4f..0000000000 --- a/tool/ruby_vm/views/mjit_compile.inc.erb +++ /dev/null @@ -1,112 +0,0 @@ -/* -*- 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 `reg_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 '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; -} diff --git a/tool/ruby_vm/views/opt_sc.inc.erb b/tool/ruby_vm/views/opt_sc.inc.erb deleted file mode 100644 index e58c81989f..0000000000 --- a/tool/ruby_vm/views/opt_sc.inc.erb +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- C -*- */ - -%# Copyright (c) 2017 Urabe, Shyouhei. 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. -% raise ':FIXME:TBW' if RubyVM::VmOptsH['STACK_CACHING'] -<%= render 'copyright' %> -<%= render 'notice', locals: { - this_file: 'is for threaded code', - edit: __FILE__, -} -%> - -#define SC_STATE_SIZE 6 - -#define SCS_XX 1 -#define SCS_AX 2 -#define SCS_BX 3 -#define SCS_AB 4 -#define SCS_BA 5 - -#define SC_ERROR 0xffffffff - -static const VALUE sc_insn_info[][SC_STATE_SIZE] = { -#define NO_SC { SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR, SC_ERROR } -% RubyVM::Instructions.each_slice 8 do |a| - <%= a.map{|i| 'NO_SC' }.join(', ') %>, -% end -#undef NO_SC -}; - -static const VALUE sc_insn_next[] = { -% RubyVM::Instructions.each_slice 8 do |a| - <%= a.map{|i| 'SCS_XX' }.join(', ') %>, -% end -}; - -ASSERT_VM_INSTRUCTION_SIZE(sc_insn_next); diff --git a/tool/ruby_vm/views/optinsn.inc.erb b/tool/ruby_vm/views/optinsn.inc.erb index 676f1edaba..9d9cf0a43a 100644 --- a/tool/ruby_vm/views/optinsn.inc.erb +++ b/tool/ruby_vm/views/optinsn.inc.erb @@ -23,20 +23,20 @@ insn_operands_unification(INSN *iobj) /* do nothing */; break; -% RubyVM::OperandsUnifications.each_group do |orig, unifs| +% RubyVM::OperandsUnification.each_group do |orig, unifs| case <%= orig.bin %>: % unifs.each do |insn| /* <%= insn.pretty_name %> */ if ( <%= insn.condition('op') %> ) { -% insn.opes.each_with_index do |o, x| +% insn.operands.each_with_index do |o, x| % n = insn.operand_shift_of(o) % if n != 0 then op[<%= x %>] = op[<%= x + n %>]; % end % end iobj->insn_id = <%= insn.bin %>; - iobj->operand_size = <%= insn.opes.size %>; + iobj->operand_size = <%= insn.operands.size %>; break; } % end @@ -55,12 +55,12 @@ rb_insn_unified_local_var_level(VALUE insn) /* optimize rule */ switch (insn) { default: - return -1; /* do nothing */; -% RubyVM::OperandsUnifications.each_group do |orig, unifs| + return -1; /* do nothing */; +% RubyVM::OperandsUnification.each_group do |orig, unifs| % unifs.each do|insn| case <%= insn.bin %>: % insn.spec.map{|(var,val)|val}.reject{|i| i == '*' }.each do |val| - return <%= val %>; + return <%= val %>; % break % end % end diff --git a/tool/ruby_vm/views/optunifs.inc.erb b/tool/ruby_vm/views/optunifs.inc.erb index e92a95beff..c096712936 100644 --- a/tool/ruby_vm/views/optunifs.inc.erb +++ b/tool/ruby_vm/views/optunifs.inc.erb @@ -7,7 +7,6 @@ %# conditions mentioned in the file COPYING are met. Consult the file for %# details. % raise ':FIXME:TBW' if RubyVM::VmOptsH['INSTRUCTIONS_UNIFICATION'] -% n = RubyVM::Instructions.size <%= render 'copyright' %> <%= render 'notice', locals: { this_file: 'is for threaded code', @@ -16,6 +15,4 @@ /* Let .bss section automatically initialize this variable */ /* cf. Section 6.7.8 of ISO/IEC 9899:1999 */ -static const int *const *const unified_insns_data[<%= n %>]; - -ASSERT_VM_INSTRUCTION_SIZE(unified_insns_data); +static const int *const *const unified_insns_data[VM_INSTRUCTION_SIZE]; diff --git a/tool/ruby_vm/views/vm.inc.erb b/tool/ruby_vm/views/vm.inc.erb index c1a3faf60a..38bf5f05ae 100644 --- a/tool/ruby_vm/views/vm.inc.erb +++ b/tool/ruby_vm/views/vm.inc.erb @@ -13,18 +13,22 @@ } -%> #include "vm_insnhelper.h" -% RubyVM::BareInstructions.to_a.each do |insn| +% RubyVM::BareInstruction.all.each do |insn| <%= render 'insn_entry', locals: { insn: insn } -%> % end % -% RubyVM::OperandsUnifications.to_a.each do |insn| +% RubyVM::OperandsUnification.all.each do |insn| <%= render 'insn_entry', locals: { insn: insn } -%> % end % -% RubyVM::InstructionsUnifications.to_a.each do |insn| +% RubyVM::InstructionsUnification.all.each do |insn| <%= render 'insn_entry', locals: { insn: insn } -%> % end % -% RubyVM::TraceInstructions.to_a.each do |insn| +% RubyVM::ZJITInstruction.all.each do |insn| +<%= render 'zjit_instruction', locals: { insn: insn } -%> +% end +% +% RubyVM::TraceInstruction.all.each do |insn| <%= render 'trace_instruction', locals: { insn: insn } -%> % end diff --git a/tool/ruby_vm/views/vmtc.inc.erb b/tool/ruby_vm/views/vmtc.inc.erb index 99cbd92614..39dc8bfa6b 100644 --- a/tool/ruby_vm/views/vmtc.inc.erb +++ b/tool/ruby_vm/views/vmtc.inc.erb @@ -6,6 +6,9 @@ %# 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. +% +% zjit_insns, insns = RubyVM::Instructions.partition { |i| i.name.start_with?('zjit_') } +% <%= render 'copyright' -%> <%= render 'notice', locals: { this_file: 'is for threaded code', @@ -13,9 +16,14 @@ } -%> static const void *const insns_address_table[] = { -% RubyVM::Instructions.each do |i| +% insns.each do |i| LABEL_PTR(<%= i.name %>), % end +#if USE_ZJIT +% zjit_insns.each do |i| + LABEL_PTR(<%= i.name %>), +% end +#endif }; ASSERT_VM_INSTRUCTION_SIZE(insns_address_table); |
