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