1#!/usr/bin/env ruby 2 3# Copyright (c) 2021-2024 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 MethodAsImm(name) 136 index = @bb.function.external_funcs.index(name) 137 if index.nil? 138 index = @bb.function.external_funcs.size 139 @bb.function.external_funcs << name 140 end 141 AddImm(index) 142 end 143 144 def emit_ir 145 opc = opcode() 146 Output << "// #{self.to_s}" 147 Output << "// #{self.annotation}" 148 if IsConstant?() 149 ss = "CONSTANT(#{@index}, #{@fields[:Value]})" 150 elsif IsParameter?() 151 Output.println "PARAMETER(#{@index}, #{@fields[:ArgIndex]}).#{@type}();" 152 return 153 elsif IsElse? 154 return 155 else 156 opc = :If if IsWhile? 157 opc = :Phi if IsWhilePhi? 158 ss = "INST(#{@index}, Opcode::#{opc})" 159 end 160 161 if IsIntrinsic? || IsCallIndirect? || IsCall? || IsCallDynamic? 162 inputs = @inputs.map do |input| 163 t = input.type == "mw" ? "IrConstructor::MARK_WORD_TYPE" : "DataType::#{input.get_type_for_cpp}" 164 "{#{t}, #{input.index}}" 165 end.join(", ") 166 ss += ".Inputs({#{inputs}})" unless inputs.empty? 167 else 168 raise "Instruction has unresolved inputs: #{self}" if @inputs.any? {|x| x.nil? } 169 inputs = @inputs.map(&:index).join(", ") 170 ss += ".Inputs(#{inputs})" unless inputs.empty? 171 end 172 173 type = @type == :void ? :v0id : @type 174 ss += ".#{type}()" unless type.nil? 175 @modifiers.each do |mod| 176 ss += ".#{modifier_to_s(mod)}" 177 end 178 ss += ".Loc(DIR_#{@debug_info.dir}, FILE_#{@debug_info.file}, #{@debug_info.line})" if @debug_info.line 179 ss += ';' 180 Output.println ss 181 end 182 183 def to_s 184 "Inst(id=#{@index}, opc=#{@name})" 185 end 186 187 def inspect 188 to_s 189 end 190 191 def get_type_for_cpp 192 @@type_map ||= { 193 nil => :NO_TYPE, 194 :i8 => :INT8, 195 :i16 => :INT16, 196 :i32 => :INT32, 197 :i64 => :INT64, 198 :u8 => :UINT8, 199 :u16 => :UINT16, 200 :u32 => :UINT32, 201 :u64 => :UINT64, 202 :f32 => :FLOAT32, 203 :f64 => :FLOAT64, 204 :b => :BOOL, 205 :ref => :REFERENCE, 206 :ptr => :POINTER, 207 :void => :VOID, 208 :any => :ANY, 209 :ref_uint => :'GetIntTypeForReference(GetGraph()->GetArch())' 210 } 211 res = @@type_map[@type&.to_sym] 212 raise "Wrong type: #{@type}" if res.nil? 213 res 214 end 215 216 def self.setup 217 InstructionsData.types[:word] = nil 218 InstructionsData.types[:sword] = nil 219 InstructionsData.types.each do |name, _| 220 name = name == 'bool' ? :b : name.to_sym 221 define_method(name) do |&block| 222 @type = name == :word ? (Options.arch_64_bits? ? 'u64' : 'u32') : 223 (name == :sword ? (Options.arch_64_bits? ? 'i64' : 'i32') : name) 224 @bb.function.process_inst(self, &block) if !block.nil? 225 self 226 end 227 end 228 229 InstructionsData.instructions.each do |opcode, inst| 230 define_method("Is#{opcode}?".to_sym) do 231 @name == inst['opcode'].to_sym 232 end 233 end 234 235 # Generate concise functions for creating condition code: If().CC(:CC_EQ) => If().EQ 236 [:EQ, :NE, :GE, :GT, :LE, :LT, :A, :AE, :B, :BE].each do |x| define_method(x) do |&block| 237 send(:CC, "CC_#{x}".to_sym, &block) 238 self 239 end 240 end 241 end 242 243 def generate_builder 244 Output << "// #{self.to_s}" 245 Output << "// #{self.annotation}" 246 # TODO(mbolshov): raise 'No LiveIn/LiveOut are allowed in IR Builder generator' if IsLiveIn? || IsLiveOut? 247 if IsReturnVoid? 248 Output.println('return nullptr;') 249 elsif IsReturn? 250 raise "Return has #{@inputs.size}" if @inputs.size != 1 251 252 Output.println("return #{@inputs.first.local_var_name};") 253 elsif IsConstant? 254 Output.println("// NOLINTNEXTLINE(readability-magic-numbers)") 255 Output.println("auto* #{local_var_name} = graph->FindOrCreateConstant(#{@fields[:Value]});") 256 else 257 if IsCall? 258 index = @modifiers.detect {|mod| mod[0] == :TypeId}[1][0] 259 name = @bb.function.external_funcs[index].snakecase 260 intrinsic_id = name.split('_')[0..-2].join('_').upcase 261 intrinsic_id = "RuntimeInterface::IntrinsicId::INTRINSIC_#{intrinsic_id}" 262 Output.println("auto* #{local_var_name} = graph->CreateInstIntrinsic(DataType::#{get_type_for_cpp}, pc);") 263 Output.println("#{local_var_name}->SetIntrinsicId(#{intrinsic_id});") 264 Output.println("#{local_var_name}->SetFlag(inst_flags::CAN_THROW);") 265 else 266 Output.println("auto* #{local_var_name} = graph->CreateInst#{@name}(DataType::#{get_type_for_cpp}, pc);") 267 end 268 generate_inst_inputs 269 if IsCmp? || IsCompare? || IsIf? || IsIfImm? || IsSelect? || IsSelectImm? 270 Output.println("#{local_var_name}->SetOperandsType(DataType::#{@inputs.first.get_type_for_cpp});") 271 end 272 generate_inst_modifiers 273 if IsPhi? 274 Output.println("bb_#{@bb.index}->AppendPhi(#{local_var_name});") 275 else 276 Output.println("bb_#{@bb.index}->AppendInst(#{local_var_name});") 277 end 278 end 279 end 280 281 # name of local variable in generated IR Builder source code 282 def local_var_name 283 IsParameter? ? "p_#{@index}" : "l_#{@index}" 284 end 285 286 def modifier_to_s(mod) 287 "#{mod[0]}(#{mod[1].join(', ')})" 288 end 289 290 def generate_inst_inputs 291 need_save_state = IsCall? 292 raise 'SaveState is added only for instructions with dynamic number of inputs' if need_save_state && !dynamic_inputs? 293 num_inputs = @inputs.size + (need_save_state ? 1 : 0) 294 if dynamic_inputs? 295 Output.println("// NOLINTNEXTLINE(readability-magic-numbers)") 296 Output.println("#{local_var_name}->ReserveInputs(#{num_inputs});") 297 Output.println("#{local_var_name}->AllocateInputTypes(graph->GetAllocator(), #{num_inputs});") unless IsPhi? 298 end 299 @inputs.each_with_index do |input, i| 300 input_name = input.local_var_name 301 if dynamic_inputs? 302 Output.println("#{local_var_name}->AppendInput(#{input_name});") 303 Output.println("#{local_var_name}->AddInputType(DataType::#{input.get_type_for_cpp});") unless IsPhi? 304 else 305 Output.println("#{local_var_name}->SetInput(#{i}, #{input_name});") 306 end 307 end 308 if need_save_state 309 # SaveState is the last input by convention: 310 save_state_var_name = "#{local_var_name}_save_state" 311 Output.println("auto #{save_state_var_name} = inst_builder->CreateSaveState(Opcode::SaveState, pc);"); 312 Output.println("#{local_var_name}->AppendInput(#{save_state_var_name});") 313 Output.println("#{local_var_name}->AddInputType(DataType::NO_TYPE);") 314 Output.println("bb_#{@bb.index}->AppendInst(#{save_state_var_name});") 315 end 316 end 317 318 def generate_inst_modifiers 319 @modifiers.each do |mod| 320 next if IsCall? 321 322 mod[0] = 'SetOperandsType' if mod[0] == :SrcType 323 mod[0] = 'SetCc' if mod[0] == :CC 324 prefix = 'Set' unless mod[0].to_s.start_with?('Set') 325 Output.println("// NOLINTNEXTLINE(readability-magic-numbers)") 326 Output.println("#{local_var_name}->#{prefix}#{modifier_to_s(mod)};") 327 end 328 end 329 330 def dump(stm = STDOUT) 331 mods = @modifiers.empty? ? "" : ".#{@modifiers.join('.')}" 332 type = @type.nil? ? "" : ".#{@type}" 333 stm.print("#{index}.#{@name}#{type}") 334 if !@inputs.empty? 335 inputs = @inputs.map { |x| "#{x.index}"} 336 stm.print(" (#{inputs.join(', ')})") 337 end 338 stm.print(mods) 339 if !@fields.empty? 340 stm.print(", #{@fields}") 341 end 342 end 343 344 def inspect 345 ss = StringIO.new 346 dump(ss) 347 ss.string 348 end 349 350 def to_s 351 inspect 352 end 353 354end 355