• 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 InstIsaDescription
21  def initialize(inst, dscr)
22    @inst = inst
23    @dscr = dscr
24  end
25
26  def pseudo?
27    @dscr.flags.include? "pseudo"
28  end
29
30  def has_dst?
31    return false if pseudo?
32    !@dscr.signature.empty? && @dscr.signature[0].split('-').include?('d')
33  end
34end
35
36class IRInstruction
37  attr_reader :index, :inputs, :name, :bb, :fields, :modifiers, :dscr
38  attr_accessor :type, :annotation, :debug_info
39
40  DebugInfo = Struct.new(:dir, :file, :line)
41
42  def initialize(name, index, bb, **kwargs)
43    @name = name
44    @index = index
45    @bb = bb
46    @dscr = OpenStruct.new(InstructionsData.instructions[name])
47    @fields = kwargs
48    @inputs = []
49    @type = nil
50    @modifiers = []
51    @debug_info = DebugInfo.new
52    abort "Wrong instructions #{name}" unless InstructionsData.instructions.include? name
53  end
54
55  def set_parameter_index(index)
56    raise "Trying to set argument index for non Parameter instruction" unless IsParameter?
57    @fields[:ArgIndex] = index
58  end
59
60  def no_dce
61    SetFlag('inst_flags::NO_DCE')
62  end
63
64  def set_access_type(type)
65    SetAccessType("DynObjectAccessType::#{type.to_s.upcase}")
66  end
67
68  def set_access_mode(mode)
69    SetAccessMode("DynObjectAccessMode::#{mode.to_s.upcase}")
70  end
71
72  def global?
73    return IsConstant? || IsLiveIn? || IsElse? || IsParameter?
74  end
75
76  def terminator?
77    IsReturn? || IsReturnVoid? || IsThrow? || has_modifier?(:Terminator)
78  end
79
80  def has_modifier?(mod)
81    @modifiers.any? { |x| x[0] == mod}
82  end
83
84  def opcode
85    @name
86  end
87
88  def pseudo?
89    @dscr.flags.include? "pseudo"
90  end
91
92  def dynamic_inputs?
93    @dscr.signature&.any? { |operand| operand.end_with?('-dyn') }
94  end
95
96  def add_inputs(insts)
97    @inputs += insts
98  end
99
100  def mw(&block)
101    @type = "mw"
102    if !block.nil?
103      @bb.function.process_inst(self, &block)
104    end
105    self
106  end
107
108  def ref_uint(&block)
109    @type = "ref_uint"
110    if !block.nil?
111      @bb.function.process_inst(self, &block)
112    end
113    self
114  end
115
116  def method_missing(method, *args, **kwargs, &block)
117    if Options.compiling
118      @modifiers << [method, args]
119      @bb.function.process_inst(self, &block) unless block.nil?
120      self
121    else
122      super
123    end
124  end
125
126  def Method(name, setter = :TypeId)
127    index = @bb.function.external_funcs.index(name)
128    if index.nil?
129        index = @bb.function.external_funcs.size
130        @bb.function.external_funcs << name
131    end
132    send(setter, index)
133  end
134
135  def emit_ir
136    opc = opcode()
137    Output << "// #{self.to_s}"
138    Output << "// #{self.annotation}"
139    if IsConstant?()
140      ss = "CONSTANT(#{@index}, #{@fields[:Value]})"
141    elsif IsParameter?()
142      Output.println "PARAMETER(#{@index}, #{@fields[:ArgIndex]}).#{@type}();"
143      return
144    elsif IsElse?
145      return
146    else
147      opc = :If if IsWhile?
148      opc = :Phi if IsWhilePhi?
149      ss = "INST(#{@index}, Opcode::#{opc})"
150    end
151
152    if IsIntrinsic? || IsCallIndirect? || IsCall? || IsCallDynamic?
153      inputs = @inputs.map do |input|
154        t = input.type == "mw" ? "IrConstructor::MARK_WORD_TYPE" : "DataType::#{input.get_type_for_cpp}"
155        "{#{t}, #{input.index}}"
156      end.join(", ")
157      ss += ".Inputs({#{inputs}})" unless inputs.empty?
158    else
159      raise "Instruction has unresolved inputs: #{self}" if @inputs.any? {|x| x.nil? }
160      inputs = @inputs.map(&:index).join(", ")
161      ss += ".Inputs(#{inputs})" unless inputs.empty?
162    end
163
164    type = @type == :void ? :v0id : @type
165    ss += ".#{type}()" unless type.nil?
166    @modifiers.each do |mod|
167      ss += ".#{modifier_to_s(mod)}"
168    end
169    ss += ".Loc(DIR_#{@debug_info.dir}, FILE_#{@debug_info.file}, #{@debug_info.line})" if @debug_info.line
170    ss += ';'
171    Output.println ss
172  end
173
174  def to_s
175    "Inst(id=#{@index}, opc=#{@name})"
176  end
177
178  def inspect
179    to_s
180  end
181
182  def get_type_for_cpp
183    @@type_map ||= {
184      nil   => :NO_TYPE,
185      :i8   => :INT8,
186      :i16  => :INT16,
187      :i32  => :INT32,
188      :i64  => :INT64,
189      :u8   => :UINT8,
190      :u16  => :UINT16,
191      :u32  => :UINT32,
192      :u64  => :UINT64,
193      :f32  => :FLOAT32,
194      :f64  => :FLOAT64,
195      :b    => :BOOL,
196      :ref  => :REFERENCE,
197      :ptr  => :POINTER,
198      :void => :VOID,
199      :any  => :ANY,
200      :ref_uint => :'GetIntTypeForReference(GetGraph()->GetArch())'
201    }
202    res = @@type_map[@type&.to_sym]
203    raise "Wrong type: #{@type}" if res.nil?
204    res
205  end
206
207  def self.setup
208    InstructionsData.types[:word] = nil
209    InstructionsData.types[:sword] = nil
210    InstructionsData.types.each do |name, _|
211      name = name == 'bool' ? :b : name.to_sym
212      define_method(name) do |&block|
213        @type = name == :word ? (Options.arch_64_bits? ? 'u64' : 'u32') :
214               (name == :sword ? (Options.arch_64_bits? ? 'i64' : 'i32') : name)
215        @bb.function.process_inst(self, &block) if !block.nil?
216        self
217      end
218    end
219
220    InstructionsData.instructions.each do |opcode, inst|
221      define_method("Is#{opcode}?".to_sym) do
222        @name == inst['opcode'].to_sym
223      end
224    end
225
226    # Generate concise functions for creating condition code: If().CC(:CC_EQ) => If().EQ
227    [:EQ, :NE, :GE, :GT, :LE, :LT, :A, :AE, :B, :BE].each do |x| define_method(x) do |&block|
228        send(:CC, "CC_#{x}".to_sym, &block)
229        self
230      end
231    end
232  end
233
234  def generate_builder
235    Output << "// #{self.to_s}"
236    Output << "// #{self.annotation}"
237    # TODO(mbolshov): raise 'No LiveIn/LiveOut are allowed in IR Builder generator' if IsLiveIn? || IsLiveOut?
238    if IsReturnVoid?
239      Output.println('return nullptr;')
240    elsif IsReturn?
241      raise "Return has #{@inputs.size}" if @inputs.size != 1
242
243      Output.println("return #{@inputs.first.local_var_name};")
244    elsif IsConstant?
245      Output.println("// NOLINTNEXTLINE(readability-magic-numbers)")
246      Output.println("auto* #{local_var_name} = graph->FindOrCreateConstant(#{@fields[:Value]});")
247    else
248      if IsCall?
249        index = @modifiers.detect {|mod| mod[0] == :TypeId}[1][0]
250        name = @bb.function.external_funcs[index].snakecase
251        intrinsic_id = name.split('_')[0..-2].join('_').upcase
252        intrinsic_id = "RuntimeInterface::IntrinsicId::INTRINSIC_#{intrinsic_id}"
253        Output.println("auto* #{local_var_name} = graph->CreateInstIntrinsic(DataType::#{get_type_for_cpp}, pc);")
254        Output.println("#{local_var_name}->SetIntrinsicId(#{intrinsic_id});")
255        Output.println("#{local_var_name}->SetFlag(inst_flags::CAN_THROW);")
256        Output.println("AdjustFlags(#{intrinsic_id}, #{local_var_name});")
257      else
258        Output.println("auto* #{local_var_name} = graph->CreateInst#{@name}(DataType::#{get_type_for_cpp}, pc);")
259      end
260      generate_inst_inputs
261      if IsCmp? || IsCompare? || IsIf? || IsIfImm? || IsSelect? || IsSelectImm?
262        Output.println("#{local_var_name}->SetOperandsType(DataType::#{@inputs.first.get_type_for_cpp});")
263      end
264      generate_inst_modifiers
265      if IsIntrinsic?
266        intrinsic_id = @modifiers.detect {|mod| mod[0] == :IntrinsicId}[1][0]
267        Output.println("AdjustFlags(#{intrinsic_id}, #{local_var_name});")
268      end
269      if IsPhi?
270        Output.println("bb_#{@bb.index}->AppendPhi(#{local_var_name});")
271      else
272        Output.println("bb_#{@bb.index}->AppendInst(#{local_var_name});")
273      end
274    end
275  end
276
277  # name of local variable in generated IR Builder source code
278  def local_var_name
279    IsParameter? ? "p_#{@index}" : "l_#{@index}"
280  end
281
282  def modifier_to_s(mod)
283    "#{mod[0]}(#{mod[1].join(', ')})"
284  end
285
286  def generate_inst_inputs
287    need_save_state = IsCall?
288    raise 'SaveState is added only for instructions with dynamic number of inputs' if need_save_state && !dynamic_inputs?
289    num_inputs = @inputs.size + (need_save_state ? 1 : 0)
290    if dynamic_inputs?
291      Output.println("// NOLINTNEXTLINE(readability-magic-numbers)")
292      Output.println("#{local_var_name}->ReserveInputs(#{num_inputs});")
293      Output.println("#{local_var_name}->AllocateInputTypes(graph->GetAllocator(), #{num_inputs});") unless IsPhi?
294    end
295    @inputs.each_with_index do |input, i|
296      input_name = input.local_var_name
297      if dynamic_inputs?
298        Output.println("#{local_var_name}->AppendInput(#{input_name});")
299        Output.println("#{local_var_name}->AddInputType(DataType::#{input.get_type_for_cpp});") unless IsPhi?
300      else
301        Output.println("#{local_var_name}->SetInput(#{i}, #{input_name});")
302      end
303    end
304    if need_save_state
305      # SaveState is the last input by convention:
306      save_state_var_name = "#{local_var_name}_save_state"
307      Output.println("auto #{save_state_var_name} = inst_builder->CreateSaveState(Opcode::SaveState, pc);");
308      Output.println("#{local_var_name}->AppendInput(#{save_state_var_name});")
309      Output.println("#{local_var_name}->AddInputType(DataType::NO_TYPE);")
310      Output.println("bb_#{@bb.index}->AppendInst(#{save_state_var_name});")
311    end
312  end
313
314  def generate_inst_modifiers
315    @modifiers.each do |mod|
316      next if IsCall?
317
318      mod[0] = 'SetOperandsType' if mod[0] == :SrcType
319      mod[0] = 'SetCc' if mod[0] == :CC
320      prefix = 'Set' unless mod[0].to_s.start_with?('Set')
321      Output.println("// NOLINTNEXTLINE(readability-magic-numbers)")
322      Output.println("#{local_var_name}->#{prefix}#{modifier_to_s(mod)};")
323    end
324  end
325
326  def dump(stm = STDOUT)
327    mods = @modifiers.empty? ? "" : ".#{@modifiers.join('.')}"
328    type = @type.nil? ? "" : ".#{@type}"
329    stm.print("#{index}.#{@name}#{type}")
330    if !@inputs.empty?
331      inputs = @inputs.map { |x| "#{x.index}"}
332      stm.print(" (#{inputs.join(', ')})")
333    end
334    stm.print(mods)
335    if !@fields.empty?
336      stm.print(", #{@fields}")
337    end
338  end
339
340  def inspect
341    ss = StringIO.new
342    dump(ss)
343    ss.string
344  end
345
346  def to_s
347    inspect
348  end
349
350end
351