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