1b1994897Sopenharmony_ci# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
2b1994897Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
3b1994897Sopenharmony_ci# you may not use this file except in compliance with the License.
4b1994897Sopenharmony_ci# You may obtain a copy of the License at
5b1994897Sopenharmony_ci#
6b1994897Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0
7b1994897Sopenharmony_ci#
8b1994897Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
9b1994897Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
10b1994897Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11b1994897Sopenharmony_ci# See the License for the specific language governing permissions and
12b1994897Sopenharmony_ci# limitations under the License.
13b1994897Sopenharmony_ci
14b1994897Sopenharmony_cirequire 'delegate'
15b1994897Sopenharmony_cirequire 'ostruct'
16b1994897Sopenharmony_cirequire 'digest'
17b1994897Sopenharmony_cirequire 'set'
18b1994897Sopenharmony_ci
19b1994897Sopenharmony_cimodule Enumerable
20b1994897Sopenharmony_ci  def stable_sort_by
21b1994897Sopenharmony_ci    sort_by.with_index { |x, idx| [yield(x), idx] }
22b1994897Sopenharmony_ci  end
23b1994897Sopenharmony_ciend
24b1994897Sopenharmony_ci
25b1994897Sopenharmony_cimodule Util
26b1994897Sopenharmony_ci  module_function
27b1994897Sopenharmony_ci
28b1994897Sopenharmony_ci  def parse_acc_signature(sig)
29b1994897Sopenharmony_ci    res = []
30b1994897Sopenharmony_ci    if sig.include?('->')
31b1994897Sopenharmony_ci      src_type, dst_type = sig.match(/inout:(\w+)->(\w+)/).captures
32b1994897Sopenharmony_ci      res << Operand.new('acc', 'out', dst_type)
33b1994897Sopenharmony_ci      res << Operand.new('acc', 'in', src_type)
34b1994897Sopenharmony_ci    elsif sig.include?(':')
35b1994897Sopenharmony_ci      srcdst, type = sig.match(/(\w+):(\w+)/).captures
36b1994897Sopenharmony_ci      if srcdst == 'inout'
37b1994897Sopenharmony_ci        res << Operand.new('acc', 'out', type)
38b1994897Sopenharmony_ci        res << Operand.new('acc', 'in', type)
39b1994897Sopenharmony_ci      else
40b1994897Sopenharmony_ci        res << Operand.new('acc', srcdst, type)
41b1994897Sopenharmony_ci      end
42b1994897Sopenharmony_ci    elsif sig != 'none'
43b1994897Sopenharmony_ci      raise "Unexpected accumulator signature: #{sig}"
44b1994897Sopenharmony_ci    end
45b1994897Sopenharmony_ci    res
46b1994897Sopenharmony_ci  end
47b1994897Sopenharmony_ci
48b1994897Sopenharmony_ci  def parse_operand_signature(sig)
49b1994897Sopenharmony_ci    operand_parts = sig.split(':')
50b1994897Sopenharmony_ci    case operand_parts.size
51b1994897Sopenharmony_ci    when 1
52b1994897Sopenharmony_ci      name = operand_parts[0]
53b1994897Sopenharmony_ci      srcdst = :in
54b1994897Sopenharmony_ci      type = 'none'
55b1994897Sopenharmony_ci    when 2
56b1994897Sopenharmony_ci      name, type = operand_parts
57b1994897Sopenharmony_ci      srcdst = :in
58b1994897Sopenharmony_ci    when 3
59b1994897Sopenharmony_ci      name, srcdst, type = operand_parts
60b1994897Sopenharmony_ci    else
61b1994897Sopenharmony_ci      raise "Unexpected operand signature: #{sig}"
62b1994897Sopenharmony_ci    end
63b1994897Sopenharmony_ci    [name, srcdst, type]
64b1994897Sopenharmony_ci  end
65b1994897Sopenharmony_ciend
66b1994897Sopenharmony_ci
67b1994897Sopenharmony_cimodule FreezeMixin
68b1994897Sopenharmony_ci  class << self
69b1994897Sopenharmony_ci    def included(base)
70b1994897Sopenharmony_ci      base.extend ClassMethods
71b1994897Sopenharmony_ci    end
72b1994897Sopenharmony_ci  end
73b1994897Sopenharmony_ci
74b1994897Sopenharmony_ci  module ClassMethods
75b1994897Sopenharmony_ci    def freeze_defined_methods
76b1994897Sopenharmony_ci      @frozen = instance_methods.map { |m| [m, true] }.to_h
77b1994897Sopenharmony_ci    end
78b1994897Sopenharmony_ci
79b1994897Sopenharmony_ci    def frozen?(name)
80b1994897Sopenharmony_ci      defined?(@frozen) && @frozen[name]
81b1994897Sopenharmony_ci    end
82b1994897Sopenharmony_ci
83b1994897Sopenharmony_ci    def method_added(name)
84b1994897Sopenharmony_ci      raise "Method '#{name}' has been already defined" if frozen?(name)
85b1994897Sopenharmony_ci
86b1994897Sopenharmony_ci      super
87b1994897Sopenharmony_ci    end
88b1994897Sopenharmony_ci  end
89b1994897Sopenharmony_ciend
90b1994897Sopenharmony_ci
91b1994897Sopenharmony_ci# Methods for YAML instructions
92b1994897Sopenharmony_ci# 'Instruction' instances are created for every format of every isa.yaml
93b1994897Sopenharmony_ci# instruction and inherit properties of its instruction group.
94b1994897Sopenharmony_ci#
95b1994897Sopenharmony_ciclass Instruction < SimpleDelegator
96b1994897Sopenharmony_ci  # Signature without operands
97b1994897Sopenharmony_ci  def mnemonic
98b1994897Sopenharmony_ci    sig.split(' ')[0]
99b1994897Sopenharmony_ci  end
100b1994897Sopenharmony_ci
101b1994897Sopenharmony_ci  # Mnemonic stripped from type info
102b1994897Sopenharmony_ci  def stripped_mnemonic
103b1994897Sopenharmony_ci    mnemonic.split('.')[0]
104b1994897Sopenharmony_ci  end
105b1994897Sopenharmony_ci
106b1994897Sopenharmony_ci  # Unique (and not very long) identifier for all instructions
107b1994897Sopenharmony_ci  def opcode
108b1994897Sopenharmony_ci    mn = mnemonic.tr('.', '_')
109b1994897Sopenharmony_ci    fmt = format.pretty
110b1994897Sopenharmony_ci    if fmt == 'none'
111b1994897Sopenharmony_ci      mn
112b1994897Sopenharmony_ci    else
113b1994897Sopenharmony_ci      "#{mn}_#{fmt}"
114b1994897Sopenharmony_ci    end
115b1994897Sopenharmony_ci  end
116b1994897Sopenharmony_ci
117b1994897Sopenharmony_ci  def prefix
118b1994897Sopenharmony_ci    name = dig(:prefix)
119b1994897Sopenharmony_ci    Panda.prefixes_hash[name] if name
120b1994897Sopenharmony_ci  end
121b1994897Sopenharmony_ci
122b1994897Sopenharmony_ci  # Suggested handler name
123b1994897Sopenharmony_ci  def handler_name
124b1994897Sopenharmony_ci    opcode.upcase
125b1994897Sopenharmony_ci  end
126b1994897Sopenharmony_ci
127b1994897Sopenharmony_ci  def intrinsic_name
128b1994897Sopenharmony_ci    dig(:intrinsic_name)
129b1994897Sopenharmony_ci  end
130b1994897Sopenharmony_ci
131b1994897Sopenharmony_ci  def compilable?
132b1994897Sopenharmony_ci    properties.empty? || (!properties.include? 'not_compilable')
133b1994897Sopenharmony_ci  end
134b1994897Sopenharmony_ci
135b1994897Sopenharmony_ci  def inlinable?
136b1994897Sopenharmony_ci    properties.include? 'inlinable'
137b1994897Sopenharmony_ci  end
138b1994897Sopenharmony_ci
139b1994897Sopenharmony_ci  def opcode_idx
140b1994897Sopenharmony_ci    if prefix
141b1994897Sopenharmony_ci      dig(:opcode_idx) << 8 | prefix.opcode_idx
142b1994897Sopenharmony_ci    else
143b1994897Sopenharmony_ci      dig(:opcode_idx)
144b1994897Sopenharmony_ci    end
145b1994897Sopenharmony_ci  end
146b1994897Sopenharmony_ci
147b1994897Sopenharmony_ci  # Format instance for raw-data format name
148b1994897Sopenharmony_ci  def format
149b1994897Sopenharmony_ci    Panda.format_hash[dig(:format)]
150b1994897Sopenharmony_ci  end
151b1994897Sopenharmony_ci
152b1994897Sopenharmony_ci  # Array of explicit operands
153b1994897Sopenharmony_ci  cached def operands
154b1994897Sopenharmony_ci    return [] unless sig.include? ' '
155b1994897Sopenharmony_ci
156b1994897Sopenharmony_ci    _, operands = sig.match(/(\S+) (.+)/).captures
157b1994897Sopenharmony_ci    operands = operands.split(', ')
158b1994897Sopenharmony_ci    ops_encoding = format.encoding
159b1994897Sopenharmony_ci
160b1994897Sopenharmony_ci    count = 0
161b1994897Sopenharmony_ci    operands.map do |operand|
162b1994897Sopenharmony_ci      name, srcdst, type = Util.parse_operand_signature(operand)
163b1994897Sopenharmony_ci      if name.end_with?('id')
164b1994897Sopenharmony_ci        count += 1
165b1994897Sopenharmony_ci      end
166b1994897Sopenharmony_ci    end
167b1994897Sopenharmony_ci
168b1994897Sopenharmony_ci    id_count = 1
169b1994897Sopenharmony_ci    operands.map do |operand|
170b1994897Sopenharmony_ci      name, srcdst, type = Util.parse_operand_signature(operand)
171b1994897Sopenharmony_ci      key = name
172b1994897Sopenharmony_ci      if name.end_with?('id')
173b1994897Sopenharmony_ci        key = 'id'
174b1994897Sopenharmony_ci        if count > 1
175b1994897Sopenharmony_ci          key += id_count.to_s
176b1994897Sopenharmony_ci          id_count = id_count + 1
177b1994897Sopenharmony_ci        end
178b1994897Sopenharmony_ci      end
179b1994897Sopenharmony_ci      Operand.new(name, srcdst, type, ops_encoding[key].width, ops_encoding[key].offset)
180b1994897Sopenharmony_ci    end
181b1994897Sopenharmony_ci  end
182b1994897Sopenharmony_ci
183b1994897Sopenharmony_ci  # Used by compiler
184b1994897Sopenharmony_ci  # Operands array preceeded with accumulator as if it was a regular operand
185b1994897Sopenharmony_ci  # Registers that are both destination and source are uncoupled
186b1994897Sopenharmony_ci  cached def acc_and_operands
187b1994897Sopenharmony_ci    res = Util.parse_acc_signature(acc)
188b1994897Sopenharmony_ci    operands.each_with_object(res) do |op, ops|
189b1994897Sopenharmony_ci      if op.dst? && op.src?
190b1994897Sopenharmony_ci        ops << Operand.new(op.name, 'out', op.type, op.width, op.offset)
191b1994897Sopenharmony_ci        ops << Operand.new(op.name, 'in', op.type, op.width, op.offset)
192b1994897Sopenharmony_ci      else
193b1994897Sopenharmony_ci        ops << op
194b1994897Sopenharmony_ci      end
195b1994897Sopenharmony_ci    end
196b1994897Sopenharmony_ci  end
197b1994897Sopenharmony_ci
198b1994897Sopenharmony_ci  cached def properties
199b1994897Sopenharmony_ci    props = dig(:properties) || []
200b1994897Sopenharmony_ci    # Added for back compatibility:
201b1994897Sopenharmony_ci    add_props = []
202b1994897Sopenharmony_ci    add_props << 'acc_write' if acc_write?
203b1994897Sopenharmony_ci    add_props << 'acc_read' if acc_read?
204b1994897Sopenharmony_ci    add_props << 'acc_none' if acc_none?
205b1994897Sopenharmony_ci    props + add_props
206b1994897Sopenharmony_ci  end
207b1994897Sopenharmony_ci
208b1994897Sopenharmony_ci  cached def real_properties
209b1994897Sopenharmony_ci    filter = []
210b1994897Sopenharmony_ci    properties.each do |p|
211b1994897Sopenharmony_ci      if p != 'acc_write' && p != 'acc_read' && p != 'acc_none'
212b1994897Sopenharmony_ci        filter << p
213b1994897Sopenharmony_ci      end
214b1994897Sopenharmony_ci    end
215b1994897Sopenharmony_ci    filter << 'acc_write' if acc_write?
216b1994897Sopenharmony_ci    filter << 'acc_read' if acc_read?
217b1994897Sopenharmony_ci    filter << 'acc_none' if acc_none?
218b1994897Sopenharmony_ci    return filter
219b1994897Sopenharmony_ci  end
220b1994897Sopenharmony_ci
221b1994897Sopenharmony_ci
222b1994897Sopenharmony_ci  def type(index)
223b1994897Sopenharmony_ci    acc_and_operands.select(&:src?)[index].type || 'none'
224b1994897Sopenharmony_ci  end
225b1994897Sopenharmony_ci
226b1994897Sopenharmony_ci  # Type of single destination operand ("none" if there are no such)
227b1994897Sopenharmony_ci  def dtype
228b1994897Sopenharmony_ci    acc_and_operands.select(&:dst?).first&.type || 'none'
229b1994897Sopenharmony_ci  end
230b1994897Sopenharmony_ci
231b1994897Sopenharmony_ci  # Shortcut for querying 'float' property
232b1994897Sopenharmony_ci  def float?
233b1994897Sopenharmony_ci    properties.include? 'float'
234b1994897Sopenharmony_ci  end
235b1994897Sopenharmony_ci
236b1994897Sopenharmony_ci  # Shortcut for querying 'jump' property
237b1994897Sopenharmony_ci  def jump?
238b1994897Sopenharmony_ci    properties.include? 'jump'
239b1994897Sopenharmony_ci  end
240b1994897Sopenharmony_ci
241b1994897Sopenharmony_ci  # Shortcut for querying 'conditional' property
242b1994897Sopenharmony_ci  def conditional?
243b1994897Sopenharmony_ci    properties.include? 'conditional'
244b1994897Sopenharmony_ci  end
245b1994897Sopenharmony_ci
246b1994897Sopenharmony_ci  # Shortcut for querying 'x_none' exception
247b1994897Sopenharmony_ci  def throwing?
248b1994897Sopenharmony_ci    !exceptions.include? 'x_none'
249b1994897Sopenharmony_ci  end
250b1994897Sopenharmony_ci
251b1994897Sopenharmony_ci  def acc_read?
252b1994897Sopenharmony_ci    !acc_and_operands.select(&:acc?).select(&:src?).empty?
253b1994897Sopenharmony_ci  end
254b1994897Sopenharmony_ci
255b1994897Sopenharmony_ci  def acc_write?
256b1994897Sopenharmony_ci    !acc_and_operands.select(&:acc?).select(&:dst?).empty?
257b1994897Sopenharmony_ci  end
258b1994897Sopenharmony_ci
259b1994897Sopenharmony_ci  def acc_none?
260b1994897Sopenharmony_ci    acc_and_operands.select(&:acc?).empty?
261b1994897Sopenharmony_ci  end
262b1994897Sopenharmony_ci
263b1994897Sopenharmony_ci  def namespace
264b1994897Sopenharmony_ci    dig(:namespace) || 'core'
265b1994897Sopenharmony_ci  end
266b1994897Sopenharmony_ci
267b1994897Sopenharmony_ci  def is_range_0?
268b1994897Sopenharmony_ci    properties.include?('range_0')
269b1994897Sopenharmony_ci  end
270b1994897Sopenharmony_ci
271b1994897Sopenharmony_ci  def is_range_1?
272b1994897Sopenharmony_ci    properties.include?('range_1')
273b1994897Sopenharmony_ci  end
274b1994897Sopenharmony_ci
275b1994897Sopenharmony_ci  def is_range_instruction?
276b1994897Sopenharmony_ci    is_range_0? || is_range_1?
277b1994897Sopenharmony_ci  end
278b1994897Sopenharmony_ci
279b1994897Sopenharmony_ci  def is_return_instruction?
280b1994897Sopenharmony_ci    properties.include?('return')
281b1994897Sopenharmony_ci  end
282b1994897Sopenharmony_ci
283b1994897Sopenharmony_ci  def is_unconditional_throw_instruction?
284b1994897Sopenharmony_ci    dig(:prefix) == 'throw' && !properties.include?('conditional_throw')
285b1994897Sopenharmony_ci  end
286b1994897Sopenharmony_ci
287b1994897Sopenharmony_ci  include FreezeMixin
288b1994897Sopenharmony_ci  freeze_defined_methods
289b1994897Sopenharmony_ciend
290b1994897Sopenharmony_ci
291b1994897Sopenharmony_ciclass Prefix < SimpleDelegator
292b1994897Sopenharmony_ci  # Suggested handler name
293b1994897Sopenharmony_ci  def handler_name
294b1994897Sopenharmony_ci    name.upcase
295b1994897Sopenharmony_ci  end
296b1994897Sopenharmony_ciend
297b1994897Sopenharmony_ci
298b1994897Sopenharmony_ci# Dummy class for invalid handlers
299b1994897Sopenharmony_ciclass Invalid
300b1994897Sopenharmony_ci  def handler_name
301b1994897Sopenharmony_ci    'INVALID'
302b1994897Sopenharmony_ci  end
303b1994897Sopenharmony_ciend
304b1994897Sopenharmony_ci
305b1994897Sopenharmony_ci# Methods over format names
306b1994897Sopenharmony_ci#
307b1994897Sopenharmony_ciclass Format
308b1994897Sopenharmony_ci  attr_reader :name
309b1994897Sopenharmony_ci
310b1994897Sopenharmony_ci  def initialize(name)
311b1994897Sopenharmony_ci    @name = name
312b1994897Sopenharmony_ci  end
313b1994897Sopenharmony_ci
314b1994897Sopenharmony_ci  cached def pretty
315b1994897Sopenharmony_ci    name.sub('op_', '').gsub(/id[0-9]?/, 'id').gsub(/imm[0-9]?/, 'imm').gsub(/v[0-9]?/, 'v').gsub(/_([0-9]+)/, '\1')
316b1994897Sopenharmony_ci  end
317b1994897Sopenharmony_ci
318b1994897Sopenharmony_ci  def prefixed?
319b1994897Sopenharmony_ci    name.start_with?('pref_')
320b1994897Sopenharmony_ci  end
321b1994897Sopenharmony_ci
322b1994897Sopenharmony_ci  cached def size
323b1994897Sopenharmony_ci    bits = pretty.gsub(/[a-z]/, '').split('_').map(&:to_i).sum
324b1994897Sopenharmony_ci    raise "Incorrect format name #{name}" if bits % 8 != 0
325b1994897Sopenharmony_ci
326b1994897Sopenharmony_ci    opcode_bytes = prefixed? ? 2 : 1
327b1994897Sopenharmony_ci    bits / 8 + opcode_bytes
328b1994897Sopenharmony_ci  end
329b1994897Sopenharmony_ci
330b1994897Sopenharmony_ci  cached def encoding
331b1994897Sopenharmony_ci    return {} if name.end_with?('_none')
332b1994897Sopenharmony_ci
333b1994897Sopenharmony_ci    offset = prefixed? ? 16 : 8
334b1994897Sopenharmony_ci    encoding = {}
335b1994897Sopenharmony_ci    encoding.default_proc = proc { |_, k| raise KeyError, "#{k} not found" }
336b1994897Sopenharmony_ci    name.sub('pref_', '').sub('op_', '').split('_').each_slice(2).map do |name, width|
337b1994897Sopenharmony_ci      op = OpenStruct.new
338b1994897Sopenharmony_ci      op.name = name
339b1994897Sopenharmony_ci      op.width = width.to_i
340b1994897Sopenharmony_ci      op.offset = offset
341b1994897Sopenharmony_ci      offset += op.width
342b1994897Sopenharmony_ci      encoding[name] = op
343b1994897Sopenharmony_ci    end
344b1994897Sopenharmony_ci    encoding
345b1994897Sopenharmony_ci  end
346b1994897Sopenharmony_ci
347b1994897Sopenharmony_ci  include FreezeMixin
348b1994897Sopenharmony_ci  freeze_defined_methods
349b1994897Sopenharmony_ciend
350b1994897Sopenharmony_ci
351b1994897Sopenharmony_ci# Operand types and encoding
352b1994897Sopenharmony_ci#
353b1994897Sopenharmony_ciclass Operand
354b1994897Sopenharmony_ci  attr_reader :name, :type, :offset, :width
355b1994897Sopenharmony_ci
356b1994897Sopenharmony_ci  def initialize(name, srcdst, type, width = 0, offset = 0)
357b1994897Sopenharmony_ci    @name = name.to_s.gsub(/[0-9]/, '').to_sym
358b1994897Sopenharmony_ci    unless %i[v acc imm method_id type_id field_id string_id literalarray_id].include?(@name)
359b1994897Sopenharmony_ci      raise "Incorrect operand #{name}"
360b1994897Sopenharmony_ci    end
361b1994897Sopenharmony_ci
362b1994897Sopenharmony_ci    @srcdst = srcdst.to_sym || :in
363b1994897Sopenharmony_ci    types = %i[none u1 u2 i8 u8 i16 u16 i32 u32 b32 f32 i64 u64 b64 f64 ref top any]
364b1994897Sopenharmony_ci    raise "Incorrect type #{type}" unless types.include?(type.sub('[]', '').to_sym)
365b1994897Sopenharmony_ci
366b1994897Sopenharmony_ci    @type = type
367b1994897Sopenharmony_ci    @width = width
368b1994897Sopenharmony_ci    @offset = offset
369b1994897Sopenharmony_ci  end
370b1994897Sopenharmony_ci
371b1994897Sopenharmony_ci  def reg?
372b1994897Sopenharmony_ci    @name == :v
373b1994897Sopenharmony_ci  end
374b1994897Sopenharmony_ci
375b1994897Sopenharmony_ci  def acc?
376b1994897Sopenharmony_ci    @name == :acc
377b1994897Sopenharmony_ci  end
378b1994897Sopenharmony_ci
379b1994897Sopenharmony_ci  def imm?
380b1994897Sopenharmony_ci    @name == :imm
381b1994897Sopenharmony_ci  end
382b1994897Sopenharmony_ci
383b1994897Sopenharmony_ci  def is_float_imm?
384b1994897Sopenharmony_ci    %i[f32 f64].include?(@type.to_sym)
385b1994897Sopenharmony_ci  end
386b1994897Sopenharmony_ci
387b1994897Sopenharmony_ci  def is_signed_imm?
388b1994897Sopenharmony_ci    %i[i8 i16 i32 i64].include?(@type.to_sym)
389b1994897Sopenharmony_ci  end
390b1994897Sopenharmony_ci
391b1994897Sopenharmony_ci  def is_unsigned_imm?
392b1994897Sopenharmony_ci    %i[u1 u2 u8 u16 u32 u64].include?(@type.to_sym)
393b1994897Sopenharmony_ci  end
394b1994897Sopenharmony_ci
395b1994897Sopenharmony_ci  def id?
396b1994897Sopenharmony_ci    %i[method_id type_id field_id string_id literalarray_id].include?(@name)
397b1994897Sopenharmony_ci  end
398b1994897Sopenharmony_ci
399b1994897Sopenharmony_ci  def method_id?
400b1994897Sopenharmony_ci    %i[method_id].include?(@name)
401b1994897Sopenharmony_ci  end
402b1994897Sopenharmony_ci
403b1994897Sopenharmony_ci  def string_id?
404b1994897Sopenharmony_ci    %i[string_id].include?(@name)
405b1994897Sopenharmony_ci  end
406b1994897Sopenharmony_ci
407b1994897Sopenharmony_ci  def literalarray_id?
408b1994897Sopenharmony_ci    %i[literalarray_id].include?(@name)
409b1994897Sopenharmony_ci  end
410b1994897Sopenharmony_ci
411b1994897Sopenharmony_ci  def dst?
412b1994897Sopenharmony_ci    %i[inout out].include?(@srcdst)
413b1994897Sopenharmony_ci  end
414b1994897Sopenharmony_ci
415b1994897Sopenharmony_ci  def src?
416b1994897Sopenharmony_ci    %i[inout in].include?(@srcdst)
417b1994897Sopenharmony_ci  end
418b1994897Sopenharmony_ci
419b1994897Sopenharmony_ci  def size
420b1994897Sopenharmony_ci    @type[1..-1].to_i
421b1994897Sopenharmony_ci  end
422b1994897Sopenharmony_ci
423b1994897Sopenharmony_ci  include FreezeMixin
424b1994897Sopenharmony_ci  freeze_defined_methods
425b1994897Sopenharmony_ciend
426b1994897Sopenharmony_ci
427b1994897Sopenharmony_ci# Helper class for generating dispatch tables
428b1994897Sopenharmony_ciclass DispatchTable
429b1994897Sopenharmony_ci  # Canonical order of dispatch table consisting of
430b1994897Sopenharmony_ci  # * non-prefixed instructions handlers
431b1994897Sopenharmony_ci  # * invalid handlers
432b1994897Sopenharmony_ci  # * prefix handlers that re-dispatch to prefixed instruction based on second byte of opcode_idx
433b1994897Sopenharmony_ci  # * prefixed instructions handlers, in the order of prefixes
434b1994897Sopenharmony_ci  # Return array with proposed handler names
435b1994897Sopenharmony_ci  def handler_names
436b1994897Sopenharmony_ci    handlers = Panda.instructions.reject(&:prefix) +
437b1994897Sopenharmony_ci               Array.new(invalid_non_prefixed_interval.size, Invalid.new) +
438b1994897Sopenharmony_ci               Panda.prefixes.select(&:public?) +
439b1994897Sopenharmony_ci               Array.new(invalid_prefixes_interval.size, Invalid.new) +
440b1994897Sopenharmony_ci               Panda.prefixes.reject(&:public?) +
441b1994897Sopenharmony_ci               Panda.instructions.select(&:prefix).stable_sort_by { |i| Panda.prefixes_hash[i.prefix.name].opcode_idx }
442b1994897Sopenharmony_ci
443b1994897Sopenharmony_ci    handlers.map(&:handler_name)
444b1994897Sopenharmony_ci  end
445b1994897Sopenharmony_ci
446b1994897Sopenharmony_ci  def invalid_non_prefixed_interval
447b1994897Sopenharmony_ci    (Panda.instructions.reject(&:prefix).map(&:opcode_idx).max + 1)..(Panda.prefixes.map(&:opcode_idx).min - 1)
448b1994897Sopenharmony_ci  end
449b1994897Sopenharmony_ci
450b1994897Sopenharmony_ci  def invalid_prefixes_interval
451b1994897Sopenharmony_ci    max_invalid_idx = Panda.prefixes.reject(&:public?).map(&:opcode_idx).min || 256
452b1994897Sopenharmony_ci    (Panda.prefixes.select(&:public?).map(&:opcode_idx).max + 1)..(max_invalid_idx - 1)
453b1994897Sopenharmony_ci  end
454b1994897Sopenharmony_ci
455b1994897Sopenharmony_ci  # Maximum value for secondary dispatch index for given prefix name
456b1994897Sopenharmony_ci  def secondary_opcode_bound(prefix)
457b1994897Sopenharmony_ci    prefix_data[prefix.name][:number_of_insns] - 1
458b1994897Sopenharmony_ci  end
459b1994897Sopenharmony_ci
460b1994897Sopenharmony_ci  # Offset in dispatch table for handlers of instructions for given prefix name
461b1994897Sopenharmony_ci  def secondary_opcode_offset(prefix)
462b1994897Sopenharmony_ci    256 + prefix_data[prefix.name][:delta]
463b1994897Sopenharmony_ci  end
464b1994897Sopenharmony_ci
465b1994897Sopenharmony_ci  private
466b1994897Sopenharmony_ci
467b1994897Sopenharmony_ci  cached def prefix_data
468b1994897Sopenharmony_ci    cur_delta = 0
469b1994897Sopenharmony_ci    Panda.prefixes.each_with_object({}) do |p, obj|
470b1994897Sopenharmony_ci      prefix_instructions_num = Panda.instructions.select { |i| i.prefix && (i.prefix.name == p.name) }.size
471b1994897Sopenharmony_ci      obj[p.name] = { delta: cur_delta, number_of_insns: prefix_instructions_num }
472b1994897Sopenharmony_ci      cur_delta += prefix_instructions_num
473b1994897Sopenharmony_ci    end
474b1994897Sopenharmony_ci  end
475b1994897Sopenharmony_ciend
476b1994897Sopenharmony_ci
477b1994897Sopenharmony_ci# Auxilary classes for opcode assignment
478b1994897Sopenharmony_ciclass OpcodeAssigner
479b1994897Sopenharmony_ci  def initialize
480b1994897Sopenharmony_ci    @table = Hash.new { |h, k| h[k] = Set.new }
481b1994897Sopenharmony_ci    @all_opcodes = Set.new(0..255)
482b1994897Sopenharmony_ci  end
483b1994897Sopenharmony_ci
484b1994897Sopenharmony_ci  def consume(item)
485b1994897Sopenharmony_ci    raise 'Cannot consume instruction without opcode' unless item.opcode_idx
486b1994897Sopenharmony_ci
487b1994897Sopenharmony_ci    @table[prefix(item)] << item.opcode_idx
488b1994897Sopenharmony_ci  end
489b1994897Sopenharmony_ci
490b1994897Sopenharmony_ci  def yield_opcode(item)
491b1994897Sopenharmony_ci    return item.opcode_idx if item.opcode_idx
492b1994897Sopenharmony_ci
493b1994897Sopenharmony_ci    opcodes = @table[prefix(item)]
494b1994897Sopenharmony_ci    choose_opcode(opcodes)
495b1994897Sopenharmony_ci  end
496b1994897Sopenharmony_ci
497b1994897Sopenharmony_ci  private
498b1994897Sopenharmony_ci
499b1994897Sopenharmony_ci  def choose_opcode(occupied_opcodes)
500b1994897Sopenharmony_ci    (@all_opcodes - occupied_opcodes).min
501b1994897Sopenharmony_ci  end
502b1994897Sopenharmony_ci
503b1994897Sopenharmony_ci  def prefix(item)
504b1994897Sopenharmony_ci    item.prefix.nil? ? 'non_prefixed' : item.prefix
505b1994897Sopenharmony_ci  end
506b1994897Sopenharmony_ciend
507b1994897Sopenharmony_ci
508b1994897Sopenharmony_ciclass PrefixOpcodeAssigner < OpcodeAssigner
509b1994897Sopenharmony_ci  private
510b1994897Sopenharmony_ci
511b1994897Sopenharmony_ci  # override opcodes assignment for prefixes
512b1994897Sopenharmony_ci  def choose_opcode(occupied_opcodes)
513b1994897Sopenharmony_ci    (@all_opcodes - occupied_opcodes).max
514b1994897Sopenharmony_ci  end
515b1994897Sopenharmony_ciend
516b1994897Sopenharmony_ci
517b1994897Sopenharmony_ci# A bunch of handy methods for template generating
518b1994897Sopenharmony_ci#
519b1994897Sopenharmony_ci# All yaml properties are accessible by '.' syntax,
520b1994897Sopenharmony_ci# e.g. 'Panda::groups[0].instruction[0].format'
521b1994897Sopenharmony_ci#
522b1994897Sopenharmony_cimodule Panda
523b1994897Sopenharmony_ci  module_function
524b1994897Sopenharmony_ci
525b1994897Sopenharmony_ci  def properties
526b1994897Sopenharmony_ci    @data.properties +
527b1994897Sopenharmony_ci      [OpenStruct.new(tag: 'acc_none', description: 'Doesn\'t use accumulator register.'),
528b1994897Sopenharmony_ci       OpenStruct.new(tag: 'acc_read', description: 'Use accumulator as a first source operand.'),
529b1994897Sopenharmony_ci       OpenStruct.new(tag: 'acc_write', description: 'Use accumulator as a destination operand.')]
530b1994897Sopenharmony_ci  end
531b1994897Sopenharmony_ci
532b1994897Sopenharmony_ci  # Hash with exception tag as a key and exception description as a value
533b1994897Sopenharmony_ci  cached def exceptions_hash
534b1994897Sopenharmony_ci    convert_to_hash(exceptions)
535b1994897Sopenharmony_ci  end
536b1994897Sopenharmony_ci
537b1994897Sopenharmony_ci  # Hash with property tag as a key and property description as a value
538b1994897Sopenharmony_ci  cached def properties_hash
539b1994897Sopenharmony_ci    convert_to_hash(properties)
540b1994897Sopenharmony_ci  end
541b1994897Sopenharmony_ci
542b1994897Sopenharmony_ci  # Hash with verification tag as a key and verification description as a value
543b1994897Sopenharmony_ci  cached def verification_hash
544b1994897Sopenharmony_ci    convert_to_hash(verification)
545b1994897Sopenharmony_ci  end
546b1994897Sopenharmony_ci
547b1994897Sopenharmony_ci  cached def prefixes_hash
548b1994897Sopenharmony_ci    hash = prefixes.map { |p| [p.name, p] }.to_h
549b1994897Sopenharmony_ci    hash.default_proc = proc { |_, k| raise KeyError, "#{k} not found" }
550b1994897Sopenharmony_ci    hash
551b1994897Sopenharmony_ci  end
552b1994897Sopenharmony_ci
553b1994897Sopenharmony_ci  # Hash from format names to Format instances
554b1994897Sopenharmony_ci  cached def format_hash
555b1994897Sopenharmony_ci    each_data_instruction.with_object([]) do |instruction, fmts|
556b1994897Sopenharmony_ci      fmt_name = instruction.format
557b1994897Sopenharmony_ci      fmts << [fmt_name, Format.new(fmt_name)]
558b1994897Sopenharmony_ci    end.to_h
559b1994897Sopenharmony_ci  end
560b1994897Sopenharmony_ci
561b1994897Sopenharmony_ci  # Array of Instruction instances for every possible instruction
562b1994897Sopenharmony_ci  cached def instructions
563b1994897Sopenharmony_ci    opcodes = OpcodeAssigner.new
564b1994897Sopenharmony_ci    tmp_public = initialize_instructions(opcodes) { |ins| !ins.opcode_idx.nil? }
565b1994897Sopenharmony_ci    tmp_private = initialize_instructions(opcodes) { |ins| ins.opcode_idx.nil? }
566b1994897Sopenharmony_ci    tmp = tmp_public + tmp_private
567b1994897Sopenharmony_ci    @instructions = tmp.sort_by(&:opcode_idx)
568b1994897Sopenharmony_ci  end
569b1994897Sopenharmony_ci
570b1994897Sopenharmony_ci  cached def prefixes
571b1994897Sopenharmony_ci    opcodes = PrefixOpcodeAssigner.new
572b1994897Sopenharmony_ci    tmp_public = initialize_prefixes(opcodes) { |p| !p.opcode_idx.nil? }
573b1994897Sopenharmony_ci    tmp_private = initialize_prefixes(opcodes) { |p| p.opcode_idx.nil? }
574b1994897Sopenharmony_ci    tmp = tmp_public + tmp_private
575b1994897Sopenharmony_ci    @prefixes = tmp.sort_by(&:opcode_idx)
576b1994897Sopenharmony_ci  end
577b1994897Sopenharmony_ci
578b1994897Sopenharmony_ci  def dispatch_table
579b1994897Sopenharmony_ci    DispatchTable.new
580b1994897Sopenharmony_ci  end
581b1994897Sopenharmony_ci
582b1994897Sopenharmony_ci  # Array of all Format instances
583b1994897Sopenharmony_ci  def formats
584b1994897Sopenharmony_ci    format_hash.values.uniq(&:pretty).sort_by(&:pretty)
585b1994897Sopenharmony_ci  end
586b1994897Sopenharmony_ci
587b1994897Sopenharmony_ci  # delegating part of module
588b1994897Sopenharmony_ci  #
589b1994897Sopenharmony_ci  def wrap_data(data)
590b1994897Sopenharmony_ci    @data = data
591b1994897Sopenharmony_ci  end
592b1994897Sopenharmony_ci
593b1994897Sopenharmony_ci  def respond_to_missing?(method_name, include_private = false)
594b1994897Sopenharmony_ci    @data.respond_to?(method_name, include_private)
595b1994897Sopenharmony_ci  end
596b1994897Sopenharmony_ci
597b1994897Sopenharmony_ci  def method_missing(method, *args, &block)
598b1994897Sopenharmony_ci    if respond_to_missing? method
599b1994897Sopenharmony_ci      @data.send(method, *args, &block)
600b1994897Sopenharmony_ci    else
601b1994897Sopenharmony_ci      super
602b1994897Sopenharmony_ci    end
603b1994897Sopenharmony_ci  end
604b1994897Sopenharmony_ci
605b1994897Sopenharmony_ci  # private functions
606b1994897Sopenharmony_ci  #
607b1994897Sopenharmony_ci  private_class_method def convert_to_hash(arr)
608b1994897Sopenharmony_ci    hash = arr.map { |i| [i.tag, i.description] }.to_h
609b1994897Sopenharmony_ci    hash.default_proc = proc { |_, k| raise KeyError, "#{k} not found" }
610b1994897Sopenharmony_ci    hash
611b1994897Sopenharmony_ci  end
612b1994897Sopenharmony_ci
613b1994897Sopenharmony_ci  private_class_method def merge_group_and_insn(group, insn)
614b1994897Sopenharmony_ci    props = group.to_h
615b1994897Sopenharmony_ci    props.delete(:instructions)
616b1994897Sopenharmony_ci    props.merge(insn.to_h) do |_, old, new|
617b1994897Sopenharmony_ci      if old.is_a?(Array) && new.is_a?(Array)
618b1994897Sopenharmony_ci        old | new # extend array-like properties instead of overriding
619b1994897Sopenharmony_ci      else
620b1994897Sopenharmony_ci        new
621b1994897Sopenharmony_ci      end
622b1994897Sopenharmony_ci    end
623b1994897Sopenharmony_ci  end
624b1994897Sopenharmony_ci
625b1994897Sopenharmony_ci  private_class_method cached def each_data_instruction
626b1994897Sopenharmony_ci    # create separate instance for every instruction format and inherit group properties
627b1994897Sopenharmony_ci    groups.each_with_object([]) do |g, obj|
628b1994897Sopenharmony_ci      g.instructions.each do |i|
629b1994897Sopenharmony_ci        data_insn = merge_group_and_insn(g, i)
630b1994897Sopenharmony_ci        if data_insn[:opcode_idx] && (data_insn[:opcode_idx].size != data_insn[:format].size)
631b1994897Sopenharmony_ci          raise 'format and opcode_idx arrays should have equal size'
632b1994897Sopenharmony_ci        end
633b1994897Sopenharmony_ci
634b1994897Sopenharmony_ci        data_insn[:format].each_with_index do |f, idx|
635b1994897Sopenharmony_ci          insn = data_insn.dup
636b1994897Sopenharmony_ci          insn[:format] = f
637b1994897Sopenharmony_ci          insn[:opcode_idx] = data_insn[:opcode_idx][idx] if data_insn[:opcode_idx]
638b1994897Sopenharmony_ci          obj << OpenStruct.new(insn)
639b1994897Sopenharmony_ci        end
640b1994897Sopenharmony_ci      end
641b1994897Sopenharmony_ci    end.to_enum
642b1994897Sopenharmony_ci  end
643b1994897Sopenharmony_ci
644b1994897Sopenharmony_ci  private_class_method def initialize_instructions(opcodes, &block)
645b1994897Sopenharmony_ci    each_data_instruction.select(&block).each_with_object([]) do |instruction, insns|
646b1994897Sopenharmony_ci      insn = instruction.clone
647b1994897Sopenharmony_ci      insn[:public?] = !insn.opcode_idx.nil?
648b1994897Sopenharmony_ci      insn.opcode_idx = opcodes.yield_opcode(insn)
649b1994897Sopenharmony_ci      opcodes.consume(insn)
650b1994897Sopenharmony_ci      insns << Instruction.new(insn)
651b1994897Sopenharmony_ci    end
652b1994897Sopenharmony_ci  end
653b1994897Sopenharmony_ci
654b1994897Sopenharmony_ci  private_class_method def initialize_prefixes(opcodes, &block)
655b1994897Sopenharmony_ci    dig(:prefixes).select(&block).each_with_object([]) do |pref, res|
656b1994897Sopenharmony_ci      p = pref.clone
657b1994897Sopenharmony_ci      p[:public?] = !p.opcode_idx.nil?
658b1994897Sopenharmony_ci      p.opcode_idx = opcodes.yield_opcode(p)
659b1994897Sopenharmony_ci      opcodes.consume(p)
660b1994897Sopenharmony_ci      res << Prefix.new(p)
661b1994897Sopenharmony_ci    end
662b1994897Sopenharmony_ci  end
663b1994897Sopenharmony_ciend
664b1994897Sopenharmony_ci
665b1994897Sopenharmony_cidef Gen.on_require(data)
666b1994897Sopenharmony_ci  Panda.wrap_data(data)
667b1994897Sopenharmony_ciend
668