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