• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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