summaryrefslogtreecommitdiff
path: root/tool/ruby_vm/models/operands_unifications.rb
blob: c0ae0ece456b42c1afa333c0258f34a760da0305 (plain)
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
#! /your/favourite/path/to/ruby
# -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
# -*- frozen_string_literal: true; -*-
# -*- warn_indent: true; -*-
#
# 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.

require_relative '../helpers/c_escape'
require_relative '../loaders/opt_operand_def'
require_relative 'bare_instructions'

class RubyVM::OperandsUnifications < RubyVM::BareInstructions
  include RubyVM::CEscape

  attr_reader :preamble, :original, :spec

  def initialize location:, signature:
    name             = signature[0]
    @original        = RubyVM::BareInstructions.fetch name
    template         = @original.template
    parts            = compose location, signature, template[:signature]
    json             = template.dup
    json[:location]  = location
    json[:signature] = parts[:signature]
    json[:name]      = parts[:name]
    @preamble        = parts[:preamble]
    @spec            = parts[:spec]
    super template: template, **json
    parts[:vars].each do |v|
      @variables[v[:name]] ||= v
    end
  end

  def operand_shift_of var
    before = @original.opes.find_index var
    after  = @opes.find_index var
    raise "no #{var} for #{@name}" unless before and after
    return before - after
  end

  def condition ptr
    # :FIXME: I'm not sure if this method should be in model?
    exprs = @spec.each_with_index.map do |(var, val), i|
      case val when '*' then
        next nil
      else
        type = @original.opes[i][:type]
        expr = RubyVM::Typemap.typecast_to_VALUE type, val
        next "#{ptr}[#{i}] == #{expr}"
      end
    end
    exprs.compact!
    if exprs.size == 1 then
      return exprs[0]
    else
      exprs.map! {|i| "(#{i})" }
      return exprs.join ' && '
    end
  end

  private

  def namegen signature
    insn, argv = *signature
    wcary = argv.map do |i|
      case i when '*' then
        'WC'
      else
        i
      end
    end
    as_tr_cpp [insn, *wcary].join(', ')
  end

  def compose location, spec, template
    name    = namegen spec
    *, argv = *spec
    opes    = @original.opes
    if opes.size != argv.size
      raise sprintf("operand size mismatch for %s (%s's: %d, given: %d)",
                    name, template[:name], opes.size, argv.size)
    else
      src  = []
      mod  = []
      spec = []
      vars = []
      argv.each_index do |i|
        j = argv[i]
        k = opes[i]
        spec[i] = [k, j]
        case j when '*' then
          # operand is from iseq
          mod << k[:decl]
        else
          # operand is inside C
          vars << k
          src << {
            location: location,
            expr: "    #{k[:name]} = #{j};"
          }
        end
      end
      src.map! {|i| RubyVM::CExpr.new i }
      return {
        name: name,
        signature: {
          name: name,
          ope: mod,
          pop: template[:pop],
          ret: template[:ret],
        },
        preamble: src,
        vars: vars,
        spec: spec
      }
    end
  end

  @instances = RubyVM::OptOperandDef.map do |h|
    new(**h)
  end

  def self.to_a
    @instances
  end

  def self.each_group
    to_a.group_by(&:original).each_pair do |k, v|
      yield k, v
    end
  end
end