• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env ruby
2
3# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16require "ostruct"
17require_relative 'instructions_data'
18require_relative 'output'
19
20class IRInstruction
21  attr_reader :index, :inputs, :name, :bb, :fields, :modifiers, :dscr
22  attr_accessor :type, :annotation
23
24  def initialize(name, index, bb, **kwargs)
25    @name = name
26    @index = index
27    @bb = bb
28    @dscr = OpenStruct.new(InstructionsData.instructions[name])
29    @fields = kwargs
30    @inputs = []
31    @type = nil
32    @modifiers = []
33    abort "Wrong instructions #{name}" unless InstructionsData.instructions.include? name
34  end
35
36  def set_parameter_index(index)
37    raise "Trying to set argument index for non Parameter instruction" unless IsParameter?
38    @fields[:ArgIndex] = index
39  end
40
41  def no_dce
42    SetFlag('inst_flags::NO_DCE')
43  end
44
45  def global?
46    return IsConstant? || IsLiveIn? || IsElse? || IsParameter?
47  end
48
49  def terminator?
50    IsReturn? || IsReturnVoid? || IsThrow? || has_modifier?(:Terminator)
51  end
52
53  def has_modifier?(mod)
54    @modifiers.any? { |x| x[0] == mod}
55  end
56
57  def opcode
58    @name
59  end
60
61  def pseudo?
62    @dscr.flags.include? "pseudo"
63  end
64
65  def dynamic_inputs?
66    @dscr.signature&.any? { |operand| operand.end_with?('-dyn') }
67  end
68
69  def add_inputs(insts)
70    @inputs += insts
71  end
72
73  def mw(&block)
74    @type = "mw"
75    if !block.nil?
76      @bb.function.process_inst(self, &block)
77    end
78    self
79  end
80
81  def method_missing(method, *args, **kwargs, &block)
82    if Options.compiling
83      @modifiers << [method, args]
84      @bb.function.process_inst(self, &block) unless block.nil?
85      self
86    else
87      super
88    end
89  end
90
91  def Method(name, setter = :TypeId)
92    index = @bb.function.external_funcs.index(name)
93    if index.nil?
94        index = @bb.function.external_funcs.size
95        @bb.function.external_funcs << name
96    end
97    send(setter, index)
98  end
99
100  def emit_ir
101    opc = opcode()
102    Output << "// #{self.to_s}"
103    Output << "// #{self.annotation}"
104    if IsConstant?()
105      ss = "CONSTANT(#{@index}, #{@fields[:Value]})"
106    elsif IsParameter?()
107      Output.println "PARAMETER(#{@index}, #{@fields[:ArgIndex]}).#{@type}();"
108      return
109    elsif IsElse?
110      return
111    else
112      opc = :If if IsWhile?
113      opc = :Phi if IsWhilePhi?
114      ss = "INST(#{@index}, Opcode::#{opc})"
115    end
116
117    if IsIntrinsic? || IsCallIndirect? || IsCall?
118      inputs = @inputs.map do |input|
119        t = input.type == "mw" ? "IrConstructor::MARK_WORD_TYPE" : "DataType::#{input.get_type_for_cpp}"
120        "{#{t}, #{input.index}}"
121      end.join(", ")
122      ss += ".Inputs({#{inputs}})" unless inputs.empty?
123    else
124      raise "Instruction has unresolved inputs: #{self}" if @inputs.any? {|x| x.nil? }
125      inputs = @inputs.map(&:index).join(", ")
126      ss += ".Inputs(#{inputs})" unless inputs.empty?
127    end
128
129    type = @type == :void ? :v0id : @type
130    ss += ".#{type}()" unless type.nil?
131    @modifiers.each do |mod|
132      ss += ".#{modifier_to_s(mod)}"
133    end
134    ss += ";"
135    Output.println ss
136  end
137
138  def to_s
139    "Inst(id=#{@index}, opc=#{@name})"
140  end
141
142  def inspect
143    to_s
144  end
145
146  def get_type_for_cpp
147    @@type_map ||= {
148      nil   => :NO_TYPE,
149      :i8   => :INT8,
150      :i16  => :INT16,
151      :i32  => :INT32,
152      :i64  => :INT64,
153      :u8   => :UINT8,
154      :u16  => :UINT16,
155      :u32  => :UINT32,
156      :u64  => :UINT64,
157      :f32  => :FLOAT32,
158      :f64  => :FLOAT64,
159      :b    => :BOOL,
160      :ref  => :REFERENCE,
161      :ptr  => :POINTER,
162      :void => :VOID,
163      :any  => :ANY
164    }
165    res = @@type_map[@type&.to_sym]
166    raise "Wrong type: #{@type}" if res.nil?
167    res
168  end
169
170  def self.setup
171    InstructionsData.types[:word] = nil
172    InstructionsData.types[:sword] = nil
173    InstructionsData.types.each do |name, _|
174      name = name == 'bool' ? :b : name.to_sym
175      define_method(name) do |&block|
176        @type = name == :word ? (Options.arch_64_bits? ? 'u64' : 'u32') :
177               (name == :sword ? (Options.arch_64_bits? ? 'i64' : 'i32') : name)
178        @bb.function.process_inst(self, &block) if !block.nil?
179        self
180      end
181    end
182
183    InstructionsData.instructions.each do |opcode, inst|
184      define_method("Is#{opcode}?".to_sym) do
185        @name == inst['opcode'].to_sym
186      end
187    end
188
189    # Generate concise functions for creating condition code: If().CC(:CC_EQ) => If().EQ
190    [:EQ, :NE, :GE, :GT, :LE, :LT].each do |x| define_method(x) do |&block|
191        send(:CC, "CC_#{x}".to_sym, &block)
192        self
193      end
194    end
195  end
196
197  def generate_builder
198    # TODO(mbolshov): raise 'No LiveIn/LiveOut are allowed in IR Builder generator' if IsLiveIn? || IsLiveOut?
199    if IsReturnVoid?
200      Output.println('return nullptr;')
201    elsif IsReturn?
202      raise "Return has #{@inputs.size}" if @inputs.size != 1
203
204      Output.println("return #{@inputs.first.local_var_name};")
205    elsif IsConstant?
206      Output.println("// NOLINTNEXTLINE(readability-magic-numbers)")
207      Output.println("auto* #{local_var_name} = graph->FindOrCreateConstant(#{@fields[:Value]});")
208    else
209      if IsCall?
210        index = @modifiers.detect {|mod| mod[0] == :TypeId}[1][0]
211        name = @bb.function.external_funcs[index].snakecase
212        intrinsic_id = name.split('_')[0..-2].join('_').upcase
213        intrinsic_id = "RuntimeInterface::IntrinsicId::INTRINSIC_#{intrinsic_id}"
214        Output.println("auto* #{local_var_name} = graph->CreateInstIntrinsic(DataType::#{get_type_for_cpp}, pc);")
215        Output.println("#{local_var_name}->SetIntrinsicId(#{intrinsic_id});")
216        Output.println("#{local_var_name}->SetFlag(inst_flags::CAN_THROW);")
217        Output.println("AdjustFlags(#{intrinsic_id}, #{local_var_name});")
218      else
219        Output.println("auto* #{local_var_name} = graph->CreateInst#{@name}(DataType::#{get_type_for_cpp}, pc);")
220      end
221      generate_inst_inputs
222      generate_inst_modifiers
223      if IsIntrinsic?
224        intrinsic_id = @modifiers.detect {|mod| mod[0] == :IntrinsicId}[1][0]
225        Output.println("AdjustFlags(#{intrinsic_id}, #{local_var_name});")
226      end
227      if IsPhi?
228        Output.println("bb_#{@bb.index}->AppendPhi(#{local_var_name});")
229      else
230        Output.println("bb_#{@bb.index}->AppendInst(#{local_var_name});")
231      end
232    end
233    if IsCmp? || IsCompare? || IsIf? || IsIfImm? || IsSelect? || IsSelectImm?
234      Output.println("#{local_var_name}->SetOperandsType(DataType::#{@inputs.first.get_type_for_cpp});")
235    end
236  end
237
238  # name of local variable in generated IR Builder source code
239  def local_var_name
240    IsParameter? ? "p_#{@index}" : "l_#{@index}"
241  end
242
243  def modifier_to_s(mod)
244    "#{mod[0]}(#{mod[1].join(', ')})"
245  end
246
247  def generate_inst_inputs
248    need_save_state = IsCall?
249    raise 'SaveState is added only for instructions with dynamic number of inputs' if need_save_state && !dynamic_inputs?
250    num_inputs = @inputs.size + (need_save_state ? 1 : 0)
251    if dynamic_inputs?
252      Output.println("#{local_var_name}->ReserveInputs(#{num_inputs});")
253      Output.println("#{local_var_name}->AllocateInputTypes(graph->GetAllocator(), #{num_inputs});") unless IsPhi?
254    end
255    @inputs.each_with_index do |input, i|
256      input_name = input.local_var_name
257      if dynamic_inputs?
258        Output.println("#{local_var_name}->AppendInput(#{input_name});")
259        Output.println("#{local_var_name}->AddInputType(DataType::#{input.get_type_for_cpp});") unless IsPhi?
260      else
261        Output.println("#{local_var_name}->SetInput(#{i}, #{input_name});")
262      end
263    end
264    if need_save_state
265      # SaveState is the last input by convention:
266      save_state_var_name = "#{local_var_name}_save_state"
267      Output.println("auto #{save_state_var_name} = inst_builder->CreateSaveState(Opcode::SaveState, pc);");
268      Output.println("#{local_var_name}->AppendInput(#{save_state_var_name});")
269      Output.println("#{local_var_name}->AddInputType(DataType::NO_TYPE);")
270      Output.println("bb_#{@bb.index}->AppendInst(#{save_state_var_name});")
271    end
272  end
273
274  def generate_inst_modifiers
275    @modifiers.each do |mod|
276      next if IsCall?
277
278      mod[0] = 'SetOperandsType' if mod[0] == :SrcType
279      mod[0] = 'SetCc' if mod[0] == :CC
280      prefix = 'Set' unless mod[0].to_s.start_with?('Set')
281      Output.println("// NOLINTNEXTLINE(readability-magic-numbers)")
282      Output.println("#{local_var_name}->#{prefix}#{modifier_to_s(mod)};")
283    end
284  end
285
286  def dump(stm = STDOUT)
287    mods = @modifiers.empty? ? "" : ".#{@modifiers.join('.')}"
288    type = @type.nil? ? "" : ".#{@type}"
289    stm.print("#{index}.#{@name}#{type}")
290    if !@inputs.empty?
291      inputs = @inputs.map { |x| "#{x.index}"}
292      stm.print(" (#{inputs.join(', ')})")
293    end
294    stm.print(mods)
295    if !@fields.empty?
296      stm.print(", #{@fields}")
297    end
298  end
299
300  def inspect
301    ss = StringIO.new
302    dump(ss)
303    ss.string
304  end
305
306  def to_s
307    inspect
308  end
309
310end
311