1# Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# http://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13 14# ISAPI specialization for bytecode optimizer 15 16Instruction.class_eval do 17 def src_acc_kind 18 op = acc_and_operands.select { |op| op.acc? && op.src? }.first 19 raise "There is no src acc for #{mnemonic}" unless op 20 data_kind_helper(op) 21 end 22 23 def dst_acc_kind 24 op = acc_and_operands.select { |op| op.acc? && op.dst? }.first 25 raise "There is no dst acc for #{mnemonic}" unless op 26 data_kind_helper(op) 27 end 28 29 private 30 # return one of 32, 64, 'ref' 31 def data_kind_helper(op) 32 m = /[fiub](?<size>\d+)/.match(op.type) 33 if m 34 size = m[:size].to_i 35 if size == 64 36 return 64 37 else 38 return 32 39 end 40 end 41 return 'ref' if op.type == 'ref' 42 raise "Unexpected operand type #{op.type} in data_kind_helper" 43 end 44end 45 46def instruction_hash 47 unless defined? @instruction_hash 48 @instruction_hash = Hash.new { |_, key| raise "No instruction with '#{key}' mnemonic" } 49 Panda.instructions.each { |insn| @instruction_hash[insn.mnemonic] = insn } 50 end 51 @instruction_hash 52end 53 54# Classes for bytecode description 55Visitor = Struct.new(:ir_op, :switch) 56Switch = Struct.new(:expr, :cases) do 57 def encode 58 res = "switch (#{expr}) {\n" 59 cases.each do |c| 60 res << c.encode 61 end 62 res << "default: 63LOG(ERROR, BYTECODE_OPTIMIZER) << \"Codegen for \" << compiler::GetOpcodeString(inst->GetOpcode()) << \" failed\"; 64enc->success_ = false; 65}" 66 res 67 end 68 69 def check_width 70 res = "switch (#{expr}) {\n" 71 cases.each do |c| 72 res << c.check_width 73 end 74 res << "default: 75LOG(ERROR, BYTECODE_OPTIMIZER) << \"CheckWidth for \" << compiler::GetOpcodeString(inst->GetOpcode()) << \" failed\"; 76re->success_ = false; 77}" 78 res 79 end 80end 81 82Case = Struct.new(:types, :node) do 83 def proxy(method) 84 res = types.map { |type| "case #{type}:" }.join("\n") 85 res << " {\n" 86 res << node.send(method) 87 res << "break;\n}\n" 88 res 89 end 90 91 def encode 92 proxy(:encode) 93 end 94 95 def check_width 96 proxy(:check_width) 97 end 98end 99 100Leaf = Struct.new(:instruction, :args) do 101 def encode 102 res = "" 103 args_str = args.join(",\n") 104 if instruction.acc_read? 105 res << do_lda(instruction) 106 res << "\n" 107 end 108 res << "enc->result_.emplace_back(pandasm::Create_#{instruction.asm_token}(\n" 109 res << args_str 110 res << "\n));\n" 111 if instruction.acc_write? 112 res << do_sta(instruction) 113 res << "\n" 114 end 115 res 116 end 117 118 def check_width 119 reg = instruction.operands.select(&:reg?).first 120 if reg 121 "re->Check#{reg.width}Width(inst);\n" 122 else 123 "return;\n" 124 end 125 end 126end 127 128Empty = Struct.new(:dummy) do 129 def encode; end 130 def check_width; end 131end 132 133# Sugar for bytecode description 134def visit(ir_op) 135 @table ||= [] 136 @table << Visitor.new(ir_op, yield) 137end 138 139def visitors 140 @table 141end 142 143def switch(expr, cases) 144 Switch.new(expr, cases) 145end 146 147def plain(opcode, *args) 148 Leaf.new(instruction_hash[opcode], args.to_a) 149end 150 151def empty 152 Empty.new 153end 154 155def prefixed_case(prefix, types, node) 156 types = types.map { |t| "#{prefix}#{t}" } 157 Case.new(types, node) 158end 159 160def case_(types, opcode, *args) 161 prefixed_case("compiler::DataType::", types, plain(opcode, *args)) 162end 163 164def cc_case(types, opcode, *args) 165 prefixed_case("compiler::CC_", types, plain(opcode, *args)) 166end 167 168def case_switch(types, condition, inner_cases) 169 prefixed_case("compiler::DataType::", types, switch(condition, inner_cases)) 170end 171 172def case_true(opcode, *args) 173 Case.new(['1'], plain(opcode, *args)) 174end 175 176def case_false(opcode, *args) 177 Case.new(['0'], plain(opcode, *args)) 178end 179 180# Type/cc cases for instruction selection 181def i32_types 182 @i32_types ||= %w[BOOL UINT8 INT8 UINT16 INT16 UINT32 INT32] 183end 184 185def i64_types 186 @i64_types ||= %w[INT64 UINT64] 187end 188 189def f32_types 190 @f32_types ||= %w[FLOAT32] 191end 192 193def f64_types 194 @f64_types ||= %w[FLOAT64] 195end 196 197def b64_types 198 @b64_types ||= i64_types + f64_types 199end 200 201def b32_types 202 @b32_types ||= i32_types + f32_types 203end 204 205def void_types 206 @void_types ||= %w[VOID] 207end 208 209def cc_cases 210 @cc_cases ||= %w[EQ NE LT LE GT GE] 211end 212 213# Switch condition printers 214def type 215 'inst->GetType()' 216end 217 218def src_type 219 'inst->GetInputType(0)' 220end 221 222# we could use switch on 'bool' type for if-else purposes, but that hurts clang_tidy 223def if_acc?(reg) 224 "static_cast<int>(#{reg} == compiler::ACC_REG_ID)" 225end 226 227def if_fcmpg? 228 'static_cast<int>(inst->IsFcmpg())' 229end 230 231# Operand printers 232def dst_r 233 'inst->GetDstReg()' 234end 235 236def r(num) 237 "inst->GetSrcReg(#{num})" 238end 239 240def imm 241 'static_cast<int32_t>(inst->GetImm() & 0xffffffff)' 242end 243 244def label 245 'LabelName(inst->GetBasicBlock()->GetTrueSuccessor()->GetId())' 246end 247 248def string_id 249 'enc->ir_interface_->GetStringIdByOffset(inst->GetTypeId())' 250end 251 252def literalarray_id 253 'enc->ir_interface_->GetLiteralArrayIdByOffset(inst->GetTypeId()).value()' 254end 255 256def type_id 257 'enc->ir_interface_->GetTypeIdByOffset(inst->GetTypeId())' 258end 259 260def field_id 261 'enc->ir_interface_->GetFieldIdByOffset(inst->GetTypeId())' 262end 263 264# Lda/Sta printers 265def do_lda(instruction) 266 lda = case instruction.src_acc_kind 267 when 32 268 instruction_hash['lda'] 269 when 64 270 instruction_hash['lda.64'] 271 when 'ref' 272 instruction_hash['lda.obj'] 273 end 274 reg_num = if instruction.mnemonic.include?('ldarr') || instruction.mnemonic.include?('stobj') || instruction.mnemonic.include?('ststatic') 275 1 276 elsif instruction.mnemonic.include?('starr') 277 2 278 else 279 0 280 end 281 "if (inst->GetSrcReg(#{reg_num}) != compiler::ACC_REG_ID) { 282 enc->result_.emplace_back(pandasm::Create_#{lda.asm_token}(inst->GetSrcReg(#{reg_num}))); 283 }" 284end 285 286def do_sta(instruction) 287 sta = case instruction.dst_acc_kind 288 when 32 289 instruction_hash['sta'] 290 when 64 291 instruction_hash['sta.64'] 292 when 'ref' 293 instruction_hash['sta.obj'] 294 end 295 "if (inst->GetDstReg() != compiler::ACC_REG_ID) { 296 enc->result_.emplace_back(pandasm::Create_#{sta.asm_token}(inst->GetDstReg())); 297 }" 298end 299 300# Misc printers 301def visitor_sig(op_name, with_class = true) 302 "void #{'BytecodeGen::' if with_class}Visit#{op_name}(GraphVisitor* v, Inst* inst_base)" 303end 304 305# Bytecode description itself 306 307# Wrap all `insn` declaration in a function to call from template 308# (because Panda::instructions is initialized only in templates) 309def call_me_from_template 310 # Empty visitors for IR instructions we want to ignore 311 # (Add missing IRs on demand) 312 %w[Phi Try SaveState Parameter].each do |op| 313 visit(op) do 314 empty 315 end 316 end 317end 318