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_relative 'output' 17 18# Generates c++ source code, that replaces source instruction by the given graph. 19class GeneratorIrInline 20 21 def initialize 22 @func = nil 23 end 24 25 def generate_function(func) 26 @func = func 27 28 Output.scoped_puts "inline bool #{func.name}(Inst* source_inst) {" do 29 Output << "Graph* graph = source_inst->GetBasicBlock()->GetGraph();" 30 Output << "[[maybe_unused]] auto pc = source_inst->GetPc();" 31 Output << "[[maybe_unused]] auto save_state = source_inst->GetSaveState();" 32 Output << "[[maybe_unused]] auto current_inst = source_inst;" if @func.simple_control_flow? 33 func.params.keys.each_with_index do |_, index| 34 Output << "Inst* p_#{index} = source_inst->GetInput(#{index}).GetInst();" 35 end 36 Output << "[[maybe_unused]] auto *runtime = graph->GetRuntime();" 37 func.constants.each { |_, inst| generate_instruction(inst) } 38 39 if @func.simple_control_flow? 40 @func.basic_blocks.each { |bb| generate_bb(bb) } 41 else 42 generate_ir_with_control_flow 43 end 44 45 Output << "source_inst->GetBasicBlock()->RemoveInst(source_inst);" 46 Output << "return true;" 47 end 48 end 49 50 def generate_ir_with_control_flow 51 Output << "// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)" 52 Output << "auto cur_bb = source_inst->GetBasicBlock();" 53 Output << "[[maybe_unused]] auto end_bb = cur_bb->SplitBlockAfterInstruction(source_inst, false);" 54 @func.basic_blocks.each do |bb| 55 if false && bb.terminator? 56 Output << "auto* bb_#{bb.index} = end_bb;" 57 else 58 Output << "auto* bb_#{bb.index} = graph->CreateEmptyBlock(pc);" 59 Output << "bb_#{bb.index}->CopyTryCatchProps(cur_bb);" 60 Output << "cur_bb->GetLoop()->AppendBlock(bb_#{bb.index});" 61 end 62 end 63 Output << "cur_bb->AddSucc(bb_#{@func.basic_blocks.first.index});" 64 @func.basic_blocks.each do |bb| 65 name = "bb_#{bb.index}" 66 if bb.terminator? 67 raise "Terminator block should have no successors" if bb.true_succ || bb.false_succ 68 if bb.instructions.last.IsReturn? 69 Output << "#{name}->AddSucc(end_bb);" 70 else 71 Output << "#{name}->AddSucc(graph->GetEndBlock());" 72 end 73 else 74 Output << "#{name}->AddSucc(bb_#{bb.true_succ.index});" if bb.true_succ 75 Output << "#{name}->AddSucc(bb_#{bb.false_succ.index});" if bb.false_succ 76 end 77 end 78 @func.basic_blocks.each { |bb| generate_bb(bb) } 79 end 80 81 def generate_bb(bb) 82 Output << "/********************************************************" 83 Output << " * bb #{bb.index}" 84 Output << ' */' 85 Output << "// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)" 86 Output << "cur_bb = bb_#{bb.index};" unless @func.simple_control_flow? 87 bb.instructions.each { |inst| generate_instruction(inst) } 88 end 89 90 def generate_instruction(inst) 91 raise 'No LiveIn/LiveOut are allowed in IR Builder generator' if inst.IsLiveIn? || inst.IsLiveOut? 92 var_name = inst.local_var_name 93 Output << "// Inst #{inst}" 94 if inst.IsReturnVoid? 95 raise "ReturnVoid is not allowed in cpp mode" 96 elsif inst.IsReturn? 97 raise "Return has #{inst.inputs.size}" if inst.inputs.size != 1 98 99 Output << "source_inst->ReplaceUsers(#{inst.inputs.first.local_var_name});" 100 elsif inst.IsConstant? 101 Output << "auto* #{var_name} = graph->FindOrCreateConstant(#{inst.fields[:Value]});" 102 else 103 if inst.IsCall? 104 index = inst.modifiers.detect {|mod| mod[0] == :TypeId}[1][0] 105 name = inst.bb.function.external_funcs[index].snakecase 106 intrinsic_id = name.split('_')[0..-2].join('_').upcase 107 Output << "auto* #{var_name} = graph->CreateInstIntrinsic(DataType::#{inst.get_type_for_cpp}, pc);" 108 Output << "#{var_name}->SetIntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_#{intrinsic_id});" 109 Output << "#{var_name}->SetFlag(inst_flags::CAN_THROW);" 110 else 111 Output << "auto* #{var_name} = graph->CreateInst#{inst.name}(DataType::#{inst.get_type_for_cpp}, pc);" 112 end 113 Output << "#{var_name}->SetNeedBarrier(true);" if inst.IsStoreArray? && inst.type == :ref 114 generate_inst_inputs(inst) 115 if inst.IsCmp? || inst.IsCompare? || inst.IsIf? || inst.IsIfImm? || inst.IsSelect? || inst.IsSelectImm? 116 Output << "#{var_name}->SetOperandsType(DataType::#{inst.inputs.first.get_type_for_cpp});" 117 end 118 inst.generate_inst_modifiers 119 if inst.IsCastAnyTypeValue? || inst.IsCast? 120 input_name = get_inst_var_name(inst.inputs.first) 121 Output.scoped_puts "if (#{input_name}->GetOpcode() == Opcode::AnyTypeCheck) {" do 122 if inst.IsCastAnyTypeValue? 123 Output << "#{input_name}->CastToAnyTypeCheck()->SetAnyType(#{var_name}->GetAnyType());" 124 Output << "#{var_name}->SetAllowedInputType(#{input_name}->CastToAnyTypeCheck()->GetAllowedInputType());" 125 end 126 end 127 end 128 if inst.IsIntrinsic? 129 Output.scoped_puts "if (#{var_name}->RequireState()) {" do 130 Output << "#{var_name}->SetSaveState(save_state);" 131 end 132 elsif inst.dscr.flags.include?('require_state') 133 Output << "#{var_name}->SetSaveState(save_state);" 134 end 135 append_inst(inst.local_var_name, inst.IsPhi?) 136 end 137 end 138 139 def generate_inst_inputs(inst) 140 need_save_state = inst.IsCall? 141 raise 'SaveState is added only for instructions with dynamic number of inputs' if need_save_state && !inst.dynamic_inputs? 142 num_inputs = inst.inputs.size + (need_save_state ? 1 : 0) 143 var_name = inst.local_var_name 144 if inst.dynamic_inputs? 145 Output << "// NOLINTNEXTLINE(readability-magic-numbers)" 146 Output << "#{var_name}->ReserveInputs(#{num_inputs});" 147 Output << "#{var_name}->AllocateInputTypes(graph->GetAllocator(), #{num_inputs});" unless inst.IsPhi? 148 end 149 inst.inputs.each_with_index do |input, i| 150 input_name = input.IsParameter? ? "p_#{input.index}" : input.local_var_name 151 if inst.dynamic_inputs? 152 Output << "#{var_name}->AppendInput(#{input_name});" 153 Output << "#{var_name}->AddInputType(DataType::#{input.get_type_for_cpp});" unless inst.IsPhi? 154 else 155 Output << "#{var_name}->SetInput(#{i}, #{input_name});" 156 end 157 end 158 if need_save_state 159 save_state_var_name = "#{var_name}_save_state" 160 Output << "auto #{save_state_var_name} = graph->CreateInstSaveState(DataType::Type::NO_TYPE, pc);" 161 Output << "#{var_name}->AppendInput(#{save_state_var_name});" 162 Output << "#{var_name}->AddInputType(DataType::NO_TYPE);" 163 append_inst(save_state_var_name) 164 end 165 end 166 167 def append_inst(var_name, is_phi = false) 168 if @func.simple_control_flow? 169 Output << "current_inst->InsertAfter(#{var_name});" 170 Output << "current_inst = #{var_name};" 171 else 172 Output << "cur_bb->Append#{is_phi ? "Phi" : "Inst"}(#{var_name});" 173 end 174 end 175 176 def get_inst_var_name(inst) 177 inst.IsParameter? ? "p_#{inst.index}" : inst.local_var_name 178 end 179end 180