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_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 << "auto cur_bb = source_inst->GetBasicBlock();" 52 Output << "[[maybe_unused]] auto end_bb = cur_bb->SplitBlockAfterInstruction(source_inst, false);" 53 @func.basic_blocks.each do |bb| 54 if false && bb.terminator? 55 Output << "auto* bb_#{bb.index} = end_bb;" 56 else 57 Output << "auto* bb_#{bb.index} = graph->CreateEmptyBlock(pc);" 58 Output << "bb_#{bb.index}->CopyTryCatchProps(cur_bb);" 59 end 60 end 61 Output << "cur_bb->AddSucc(bb_#{@func.basic_blocks.first.index});" 62 @func.basic_blocks.each do |bb| 63 name = "bb_#{bb.index}" 64 if bb.terminator? 65 raise "Terminator block should have no successors" if bb.true_succ || bb.false_succ 66 if bb.instructions.last.IsReturn? 67 Output << "#{name}->AddSucc(end_bb);" 68 else 69 Output << "#{name}->AddSucc(graph->GetEndBlock());" 70 end 71 else 72 Output << "#{name}->AddSucc(bb_#{bb.true_succ.index});" if bb.true_succ 73 Output << "#{name}->AddSucc(bb_#{bb.false_succ.index});" if bb.false_succ 74 end 75 end 76 @func.basic_blocks.each { |bb| generate_bb(bb) } 77 end 78 79 def generate_bb(bb) 80 Output << "/********************************************************" 81 Output << " * bb #{bb.index}" 82 Output << ' */' 83 Output << "cur_bb = bb_#{bb.index};" unless @func.simple_control_flow? 84 bb.instructions.each { |inst| generate_instruction(inst) } 85 end 86 87 def generate_instruction(inst) 88 raise 'No LiveIn/LiveOut are allowed in IR Builder generator' if inst.IsLiveIn? || inst.IsLiveOut? 89 var_name = inst.local_var_name 90 Output << "// Inst #{inst}" 91 if inst.IsReturnVoid? 92 raise "ReturnVoid is not allowed in cpp mode" 93 elsif inst.IsReturn? 94 raise "Return has #{inst.inputs.size}" if inst.inputs.size != 1 95 96 Output << "source_inst->ReplaceUsers(#{inst.inputs.first.local_var_name});" 97 elsif inst.IsConstant? 98 Output << "auto* #{var_name} = graph->FindOrCreateConstant(#{inst.fields[:Value]});" 99 else 100 if inst.IsCall? 101 index = inst.modifiers.detect {|mod| mod[0] == :TypeId}[1][0] 102 name = inst.bb.function.external_funcs[index].snakecase 103 intrinsic_id = name.split('_')[0..-2].join('_').upcase 104 Output << "auto* #{var_name} = graph->CreateInstIntrinsic(DataType::#{inst.get_type_for_cpp}, pc);" 105 Output << "#{var_name}->SetIntrinsicId(RuntimeInterface::IntrinsicId::INTRINSIC_#{intrinsic_id});" 106 Output << "#{var_name}->SetFlag(inst_flags::CAN_THROW);" 107 else 108 Output << "auto* #{var_name} = graph->CreateInst#{inst.name}(DataType::#{inst.get_type_for_cpp}, pc);" 109 end 110 Output << "#{var_name}->SetNeedBarrier(true);" if inst.IsStoreArray? && inst.type == :ref 111 generate_inst_inputs(inst) 112 inst.generate_inst_modifiers 113 if inst.IsCastAnyTypeValue? || inst.IsCast? 114 input_name = get_inst_var_name(inst.inputs.first) 115 Output.scoped_puts "if (#{input_name}->GetOpcode() == Opcode::AnyTypeCheck) {" do 116 if inst.IsCastAnyTypeValue? 117 Output << "#{input_name}->CastToAnyTypeCheck()->SetAnyType(#{var_name}->GetAnyType());" 118 end 119 end 120 end 121 if inst.dscr.flags.include?('require_state') 122 Output << "#{var_name}->SetSaveState(save_state);" 123 end 124 append_inst(inst.local_var_name, inst.IsPhi?) 125 end 126 if inst.IsCmp? || inst.IsCompare? || inst.IsIf? || inst.IsIfImm? || inst.IsSelect? || inst.IsSelectImm? 127 Output << "#{var_name}->SetOperandsType(DataType::#{inst.inputs.first.get_type_for_cpp});" 128 end 129 end 130 131 def generate_inst_inputs(inst) 132 need_save_state = inst.IsCall? 133 raise 'SaveState is added only for instructions with dynamic number of inputs' if need_save_state && !inst.dynamic_inputs? 134 num_inputs = inst.inputs.size + (need_save_state ? 1 : 0) 135 var_name = inst.local_var_name 136 if inst.dynamic_inputs? 137 Output << "#{var_name}->ReserveInputs(#{num_inputs});" 138 Output << "#{var_name}->AllocateInputTypes(graph->GetAllocator(), #{num_inputs});" unless inst.IsPhi? 139 end 140 inst.inputs.each_with_index do |input, i| 141 input_name = input.IsParameter? ? "p_#{input.index}" : input.local_var_name 142 if inst.dynamic_inputs? 143 Output << "#{var_name}->AppendInput(#{input_name});" 144 Output << "#{var_name}->AddInputType(DataType::#{input.get_type_for_cpp});" unless inst.IsPhi? 145 else 146 Output << "#{var_name}->SetInput(#{i}, #{input_name});" 147 end 148 end 149 if need_save_state 150 save_state_var_name = "#{var_name}_save_state" 151 Output << "auto #{save_state_var_name} = graph->CreateInstSaveState(DataType::Type::NO_TYPE, pc);" 152 Output << "#{var_name}->AppendInput(#{save_state_var_name});" 153 Output << "#{var_name}->AddInputType(DataType::NO_TYPE);" 154 append_inst(save_state_var_name) 155 end 156 end 157 158 def append_inst(var_name, is_phi = false) 159 if @func.simple_control_flow? 160 Output << "current_inst->InsertAfter(#{var_name});" 161 Output << "current_inst = #{var_name};" 162 else 163 Output << "cur_bb->Append#{is_phi ? "Phi" : "Inst"}(#{var_name});" 164 end 165 end 166 167 def get_inst_var_name(inst) 168 inst.IsParameter? ? "p_#{inst.index}" : inst.local_var_name 169 end 170end