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