mirror of
https://github.com/xemu-project/xemu.git
synced 2024-12-20 02:48:36 +00:00
7aa12aa215
GCC supports "#pragma GCC diagnostic" since version 4.6, and Clang seems to support it, too, since its early versions 3.x. That means that our minimum required compiler versions all support this pragma already and we can remove the test from configure and all the related #ifdefs in the code. Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20200710045515.25986-1-thuth@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
1384 lines
40 KiB
Python
Executable File
1384 lines
40 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2018 Linaro Limited
|
|
#
|
|
# This library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2 of the License, or (at your option) any later version.
|
|
#
|
|
# This library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
#
|
|
# Generate a decoding tree from a specification file.
|
|
# See the syntax and semantics in docs/devel/decodetree.rst.
|
|
#
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
import getopt
|
|
|
|
insnwidth = 32
|
|
insnmask = 0xffffffff
|
|
variablewidth = False
|
|
fields = {}
|
|
arguments = {}
|
|
formats = {}
|
|
allpatterns = []
|
|
anyextern = False
|
|
|
|
translate_prefix = 'trans'
|
|
translate_scope = 'static '
|
|
input_file = ''
|
|
output_file = None
|
|
output_fd = None
|
|
insntype = 'uint32_t'
|
|
decode_function = 'decode'
|
|
|
|
re_ident = '[a-zA-Z][a-zA-Z0-9_]*'
|
|
|
|
|
|
def error_with_file(file, lineno, *args):
|
|
"""Print an error message from file:line and args and exit."""
|
|
global output_file
|
|
global output_fd
|
|
|
|
prefix = ''
|
|
if file:
|
|
prefix += '{0}:'.format(file)
|
|
if lineno:
|
|
prefix += '{0}:'.format(lineno)
|
|
if prefix:
|
|
prefix += ' '
|
|
print(prefix, end='error: ', file=sys.stderr)
|
|
print(*args, file=sys.stderr)
|
|
|
|
if output_file and output_fd:
|
|
output_fd.close()
|
|
os.remove(output_file)
|
|
exit(1)
|
|
# end error_with_file
|
|
|
|
|
|
def error(lineno, *args):
|
|
error_with_file(input_file, lineno, *args)
|
|
# end error
|
|
|
|
|
|
def output(*args):
|
|
global output_fd
|
|
for a in args:
|
|
output_fd.write(a)
|
|
|
|
|
|
def output_autogen():
|
|
output('/* This file is autogenerated by scripts/decodetree.py. */\n\n')
|
|
|
|
|
|
def str_indent(c):
|
|
"""Return a string with C spaces"""
|
|
return ' ' * c
|
|
|
|
|
|
def str_fields(fields):
|
|
"""Return a string uniquely identifing FIELDS"""
|
|
r = ''
|
|
for n in sorted(fields.keys()):
|
|
r += '_' + n
|
|
return r[1:]
|
|
|
|
|
|
def str_match_bits(bits, mask):
|
|
"""Return a string pretty-printing BITS/MASK"""
|
|
global insnwidth
|
|
|
|
i = 1 << (insnwidth - 1)
|
|
space = 0x01010100
|
|
r = ''
|
|
while i != 0:
|
|
if i & mask:
|
|
if i & bits:
|
|
r += '1'
|
|
else:
|
|
r += '0'
|
|
else:
|
|
r += '.'
|
|
if i & space:
|
|
r += ' '
|
|
i >>= 1
|
|
return r
|
|
|
|
|
|
def is_pow2(x):
|
|
"""Return true iff X is equal to a power of 2."""
|
|
return (x & (x - 1)) == 0
|
|
|
|
|
|
def ctz(x):
|
|
"""Return the number of times 2 factors into X."""
|
|
assert x != 0
|
|
r = 0
|
|
while ((x >> r) & 1) == 0:
|
|
r += 1
|
|
return r
|
|
|
|
|
|
def is_contiguous(bits):
|
|
if bits == 0:
|
|
return -1
|
|
shift = ctz(bits)
|
|
if is_pow2((bits >> shift) + 1):
|
|
return shift
|
|
else:
|
|
return -1
|
|
|
|
|
|
def eq_fields_for_args(flds_a, flds_b):
|
|
if len(flds_a) != len(flds_b):
|
|
return False
|
|
for k, a in flds_a.items():
|
|
if k not in flds_b:
|
|
return False
|
|
return True
|
|
|
|
|
|
def eq_fields_for_fmts(flds_a, flds_b):
|
|
if len(flds_a) != len(flds_b):
|
|
return False
|
|
for k, a in flds_a.items():
|
|
if k not in flds_b:
|
|
return False
|
|
b = flds_b[k]
|
|
if a.__class__ != b.__class__ or a != b:
|
|
return False
|
|
return True
|
|
|
|
|
|
class Field:
|
|
"""Class representing a simple instruction field"""
|
|
def __init__(self, sign, pos, len):
|
|
self.sign = sign
|
|
self.pos = pos
|
|
self.len = len
|
|
self.mask = ((1 << len) - 1) << pos
|
|
|
|
def __str__(self):
|
|
if self.sign:
|
|
s = 's'
|
|
else:
|
|
s = ''
|
|
return str(self.pos) + ':' + s + str(self.len)
|
|
|
|
def str_extract(self):
|
|
if self.sign:
|
|
extr = 'sextract32'
|
|
else:
|
|
extr = 'extract32'
|
|
return '{0}(insn, {1}, {2})'.format(extr, self.pos, self.len)
|
|
|
|
def __eq__(self, other):
|
|
return self.sign == other.sign and self.mask == other.mask
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
# end Field
|
|
|
|
|
|
class MultiField:
|
|
"""Class representing a compound instruction field"""
|
|
def __init__(self, subs, mask):
|
|
self.subs = subs
|
|
self.sign = subs[0].sign
|
|
self.mask = mask
|
|
|
|
def __str__(self):
|
|
return str(self.subs)
|
|
|
|
def str_extract(self):
|
|
ret = '0'
|
|
pos = 0
|
|
for f in reversed(self.subs):
|
|
if pos == 0:
|
|
ret = f.str_extract()
|
|
else:
|
|
ret = 'deposit32({0}, {1}, {2}, {3})' \
|
|
.format(ret, pos, 32 - pos, f.str_extract())
|
|
pos += f.len
|
|
return ret
|
|
|
|
def __ne__(self, other):
|
|
if len(self.subs) != len(other.subs):
|
|
return True
|
|
for a, b in zip(self.subs, other.subs):
|
|
if a.__class__ != b.__class__ or a != b:
|
|
return True
|
|
return False
|
|
|
|
def __eq__(self, other):
|
|
return not self.__ne__(other)
|
|
# end MultiField
|
|
|
|
|
|
class ConstField:
|
|
"""Class representing an argument field with constant value"""
|
|
def __init__(self, value):
|
|
self.value = value
|
|
self.mask = 0
|
|
self.sign = value < 0
|
|
|
|
def __str__(self):
|
|
return str(self.value)
|
|
|
|
def str_extract(self):
|
|
return str(self.value)
|
|
|
|
def __cmp__(self, other):
|
|
return self.value - other.value
|
|
# end ConstField
|
|
|
|
|
|
class FunctionField:
|
|
"""Class representing a field passed through a function"""
|
|
def __init__(self, func, base):
|
|
self.mask = base.mask
|
|
self.sign = base.sign
|
|
self.base = base
|
|
self.func = func
|
|
|
|
def __str__(self):
|
|
return self.func + '(' + str(self.base) + ')'
|
|
|
|
def str_extract(self):
|
|
return self.func + '(ctx, ' + self.base.str_extract() + ')'
|
|
|
|
def __eq__(self, other):
|
|
return self.func == other.func and self.base == other.base
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
# end FunctionField
|
|
|
|
|
|
class ParameterField:
|
|
"""Class representing a pseudo-field read from a function"""
|
|
def __init__(self, func):
|
|
self.mask = 0
|
|
self.sign = 0
|
|
self.func = func
|
|
|
|
def __str__(self):
|
|
return self.func
|
|
|
|
def str_extract(self):
|
|
return self.func + '(ctx)'
|
|
|
|
def __eq__(self, other):
|
|
return self.func == other.func
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
# end ParameterField
|
|
|
|
|
|
class Arguments:
|
|
"""Class representing the extracted fields of a format"""
|
|
def __init__(self, nm, flds, extern):
|
|
self.name = nm
|
|
self.extern = extern
|
|
self.fields = sorted(flds)
|
|
|
|
def __str__(self):
|
|
return self.name + ' ' + str(self.fields)
|
|
|
|
def struct_name(self):
|
|
return 'arg_' + self.name
|
|
|
|
def output_def(self):
|
|
if not self.extern:
|
|
output('typedef struct {\n')
|
|
for n in self.fields:
|
|
output(' int ', n, ';\n')
|
|
output('} ', self.struct_name(), ';\n\n')
|
|
# end Arguments
|
|
|
|
|
|
class General:
|
|
"""Common code between instruction formats and instruction patterns"""
|
|
def __init__(self, name, lineno, base, fixb, fixm, udfm, fldm, flds, w):
|
|
self.name = name
|
|
self.file = input_file
|
|
self.lineno = lineno
|
|
self.base = base
|
|
self.fixedbits = fixb
|
|
self.fixedmask = fixm
|
|
self.undefmask = udfm
|
|
self.fieldmask = fldm
|
|
self.fields = flds
|
|
self.width = w
|
|
|
|
def __str__(self):
|
|
return self.name + ' ' + str_match_bits(self.fixedbits, self.fixedmask)
|
|
|
|
def str1(self, i):
|
|
return str_indent(i) + self.__str__()
|
|
# end General
|
|
|
|
|
|
class Format(General):
|
|
"""Class representing an instruction format"""
|
|
|
|
def extract_name(self):
|
|
global decode_function
|
|
return decode_function + '_extract_' + self.name
|
|
|
|
def output_extract(self):
|
|
output('static void ', self.extract_name(), '(DisasContext *ctx, ',
|
|
self.base.struct_name(), ' *a, ', insntype, ' insn)\n{\n')
|
|
for n, f in self.fields.items():
|
|
output(' a->', n, ' = ', f.str_extract(), ';\n')
|
|
output('}\n\n')
|
|
# end Format
|
|
|
|
|
|
class Pattern(General):
|
|
"""Class representing an instruction pattern"""
|
|
|
|
def output_decl(self):
|
|
global translate_scope
|
|
global translate_prefix
|
|
output('typedef ', self.base.base.struct_name(),
|
|
' arg_', self.name, ';\n')
|
|
output(translate_scope, 'bool ', translate_prefix, '_', self.name,
|
|
'(DisasContext *ctx, arg_', self.name, ' *a);\n')
|
|
|
|
def output_code(self, i, extracted, outerbits, outermask):
|
|
global translate_prefix
|
|
ind = str_indent(i)
|
|
arg = self.base.base.name
|
|
output(ind, '/* ', self.file, ':', str(self.lineno), ' */\n')
|
|
if not extracted:
|
|
output(ind, self.base.extract_name(),
|
|
'(ctx, &u.f_', arg, ', insn);\n')
|
|
for n, f in self.fields.items():
|
|
output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n')
|
|
output(ind, 'if (', translate_prefix, '_', self.name,
|
|
'(ctx, &u.f_', arg, ')) return true;\n')
|
|
|
|
# Normal patterns do not have children.
|
|
def build_tree(self):
|
|
return
|
|
def prop_masks(self):
|
|
return
|
|
def prop_format(self):
|
|
return
|
|
def prop_width(self):
|
|
return
|
|
|
|
# end Pattern
|
|
|
|
|
|
class MultiPattern(General):
|
|
"""Class representing a set of instruction patterns"""
|
|
|
|
def __init__(self, lineno):
|
|
self.file = input_file
|
|
self.lineno = lineno
|
|
self.pats = []
|
|
self.base = None
|
|
self.fixedbits = 0
|
|
self.fixedmask = 0
|
|
self.undefmask = 0
|
|
self.width = None
|
|
|
|
def __str__(self):
|
|
r = 'group'
|
|
if self.fixedbits is not None:
|
|
r += ' ' + str_match_bits(self.fixedbits, self.fixedmask)
|
|
return r
|
|
|
|
def output_decl(self):
|
|
for p in self.pats:
|
|
p.output_decl()
|
|
|
|
def prop_masks(self):
|
|
global insnmask
|
|
|
|
fixedmask = insnmask
|
|
undefmask = insnmask
|
|
|
|
# Collect fixedmask/undefmask for all of the children.
|
|
for p in self.pats:
|
|
p.prop_masks()
|
|
fixedmask &= p.fixedmask
|
|
undefmask &= p.undefmask
|
|
|
|
# Widen fixedmask until all fixedbits match
|
|
repeat = True
|
|
fixedbits = 0
|
|
while repeat and fixedmask != 0:
|
|
fixedbits = None
|
|
for p in self.pats:
|
|
thisbits = p.fixedbits & fixedmask
|
|
if fixedbits is None:
|
|
fixedbits = thisbits
|
|
elif fixedbits != thisbits:
|
|
fixedmask &= ~(fixedbits ^ thisbits)
|
|
break
|
|
else:
|
|
repeat = False
|
|
|
|
self.fixedbits = fixedbits
|
|
self.fixedmask = fixedmask
|
|
self.undefmask = undefmask
|
|
|
|
def build_tree(self):
|
|
for p in self.pats:
|
|
p.build_tree()
|
|
|
|
def prop_format(self):
|
|
for p in self.pats:
|
|
p.build_tree()
|
|
|
|
def prop_width(self):
|
|
width = None
|
|
for p in self.pats:
|
|
p.prop_width()
|
|
if width is None:
|
|
width = p.width
|
|
elif width != p.width:
|
|
error_with_file(self.file, self.lineno,
|
|
'width mismatch in patterns within braces')
|
|
self.width = width
|
|
|
|
# end MultiPattern
|
|
|
|
|
|
class IncMultiPattern(MultiPattern):
|
|
"""Class representing an overlapping set of instruction patterns"""
|
|
|
|
def output_code(self, i, extracted, outerbits, outermask):
|
|
global translate_prefix
|
|
ind = str_indent(i)
|
|
for p in self.pats:
|
|
if outermask != p.fixedmask:
|
|
innermask = p.fixedmask & ~outermask
|
|
innerbits = p.fixedbits & ~outermask
|
|
output(ind, 'if ((insn & ',
|
|
'0x{0:08x}) == 0x{1:08x}'.format(innermask, innerbits),
|
|
') {\n')
|
|
output(ind, ' /* ',
|
|
str_match_bits(p.fixedbits, p.fixedmask), ' */\n')
|
|
p.output_code(i + 4, extracted, p.fixedbits, p.fixedmask)
|
|
output(ind, '}\n')
|
|
else:
|
|
p.output_code(i, extracted, p.fixedbits, p.fixedmask)
|
|
#end IncMultiPattern
|
|
|
|
|
|
class Tree:
|
|
"""Class representing a node in a decode tree"""
|
|
|
|
def __init__(self, fm, tm):
|
|
self.fixedmask = fm
|
|
self.thismask = tm
|
|
self.subs = []
|
|
self.base = None
|
|
|
|
def str1(self, i):
|
|
ind = str_indent(i)
|
|
r = '{0}{1:08x}'.format(ind, self.fixedmask)
|
|
if self.format:
|
|
r += ' ' + self.format.name
|
|
r += ' [\n'
|
|
for (b, s) in self.subs:
|
|
r += '{0} {1:08x}:\n'.format(ind, b)
|
|
r += s.str1(i + 4) + '\n'
|
|
r += ind + ']'
|
|
return r
|
|
|
|
def __str__(self):
|
|
return self.str1(0)
|
|
|
|
def output_code(self, i, extracted, outerbits, outermask):
|
|
ind = str_indent(i)
|
|
|
|
# If we identified all nodes below have the same format,
|
|
# extract the fields now.
|
|
if not extracted and self.base:
|
|
output(ind, self.base.extract_name(),
|
|
'(ctx, &u.f_', self.base.base.name, ', insn);\n')
|
|
extracted = True
|
|
|
|
# Attempt to aid the compiler in producing compact switch statements.
|
|
# If the bits in the mask are contiguous, extract them.
|
|
sh = is_contiguous(self.thismask)
|
|
if sh > 0:
|
|
# Propagate SH down into the local functions.
|
|
def str_switch(b, sh=sh):
|
|
return '(insn >> {0}) & 0x{1:x}'.format(sh, b >> sh)
|
|
|
|
def str_case(b, sh=sh):
|
|
return '0x{0:x}'.format(b >> sh)
|
|
else:
|
|
def str_switch(b):
|
|
return 'insn & 0x{0:08x}'.format(b)
|
|
|
|
def str_case(b):
|
|
return '0x{0:08x}'.format(b)
|
|
|
|
output(ind, 'switch (', str_switch(self.thismask), ') {\n')
|
|
for b, s in sorted(self.subs):
|
|
assert (self.thismask & ~s.fixedmask) == 0
|
|
innermask = outermask | self.thismask
|
|
innerbits = outerbits | b
|
|
output(ind, 'case ', str_case(b), ':\n')
|
|
output(ind, ' /* ',
|
|
str_match_bits(innerbits, innermask), ' */\n')
|
|
s.output_code(i + 4, extracted, innerbits, innermask)
|
|
output(ind, ' return false;\n')
|
|
output(ind, '}\n')
|
|
# end Tree
|
|
|
|
|
|
class ExcMultiPattern(MultiPattern):
|
|
"""Class representing a non-overlapping set of instruction patterns"""
|
|
|
|
def output_code(self, i, extracted, outerbits, outermask):
|
|
# Defer everything to our decomposed Tree node
|
|
self.tree.output_code(i, extracted, outerbits, outermask)
|
|
|
|
@staticmethod
|
|
def __build_tree(pats, outerbits, outermask):
|
|
# Find the intersection of all remaining fixedmask.
|
|
innermask = ~outermask & insnmask
|
|
for i in pats:
|
|
innermask &= i.fixedmask
|
|
|
|
if innermask == 0:
|
|
# Edge condition: One pattern covers the entire insnmask
|
|
if len(pats) == 1:
|
|
t = Tree(outermask, innermask)
|
|
t.subs.append((0, pats[0]))
|
|
return t
|
|
|
|
text = 'overlapping patterns:'
|
|
for p in pats:
|
|
text += '\n' + p.file + ':' + str(p.lineno) + ': ' + str(p)
|
|
error_with_file(pats[0].file, pats[0].lineno, text)
|
|
|
|
fullmask = outermask | innermask
|
|
|
|
# Sort each element of pats into the bin selected by the mask.
|
|
bins = {}
|
|
for i in pats:
|
|
fb = i.fixedbits & innermask
|
|
if fb in bins:
|
|
bins[fb].append(i)
|
|
else:
|
|
bins[fb] = [i]
|
|
|
|
# We must recurse if any bin has more than one element or if
|
|
# the single element in the bin has not been fully matched.
|
|
t = Tree(fullmask, innermask)
|
|
|
|
for b, l in bins.items():
|
|
s = l[0]
|
|
if len(l) > 1 or s.fixedmask & ~fullmask != 0:
|
|
s = ExcMultiPattern.__build_tree(l, b | outerbits, fullmask)
|
|
t.subs.append((b, s))
|
|
|
|
return t
|
|
|
|
def build_tree(self):
|
|
super().prop_format()
|
|
self.tree = self.__build_tree(self.pats, self.fixedbits,
|
|
self.fixedmask)
|
|
|
|
@staticmethod
|
|
def __prop_format(tree):
|
|
"""Propagate Format objects into the decode tree"""
|
|
|
|
# Depth first search.
|
|
for (b, s) in tree.subs:
|
|
if isinstance(s, Tree):
|
|
ExcMultiPattern.__prop_format(s)
|
|
|
|
# If all entries in SUBS have the same format, then
|
|
# propagate that into the tree.
|
|
f = None
|
|
for (b, s) in tree.subs:
|
|
if f is None:
|
|
f = s.base
|
|
if f is None:
|
|
return
|
|
if f is not s.base:
|
|
return
|
|
tree.base = f
|
|
|
|
def prop_format(self):
|
|
super().prop_format()
|
|
self.__prop_format(self.tree)
|
|
|
|
# end ExcMultiPattern
|
|
|
|
|
|
def parse_field(lineno, name, toks):
|
|
"""Parse one instruction field from TOKS at LINENO"""
|
|
global fields
|
|
global re_ident
|
|
global insnwidth
|
|
|
|
# A "simple" field will have only one entry;
|
|
# a "multifield" will have several.
|
|
subs = []
|
|
width = 0
|
|
func = None
|
|
for t in toks:
|
|
if re.fullmatch('!function=' + re_ident, t):
|
|
if func:
|
|
error(lineno, 'duplicate function')
|
|
func = t.split('=')
|
|
func = func[1]
|
|
continue
|
|
|
|
if re.fullmatch('[0-9]+:s[0-9]+', t):
|
|
# Signed field extract
|
|
subtoks = t.split(':s')
|
|
sign = True
|
|
elif re.fullmatch('[0-9]+:[0-9]+', t):
|
|
# Unsigned field extract
|
|
subtoks = t.split(':')
|
|
sign = False
|
|
else:
|
|
error(lineno, 'invalid field token "{0}"'.format(t))
|
|
po = int(subtoks[0])
|
|
le = int(subtoks[1])
|
|
if po + le > insnwidth:
|
|
error(lineno, 'field {0} too large'.format(t))
|
|
f = Field(sign, po, le)
|
|
subs.append(f)
|
|
width += le
|
|
|
|
if width > insnwidth:
|
|
error(lineno, 'field too large')
|
|
if len(subs) == 0:
|
|
if func:
|
|
f = ParameterField(func)
|
|
else:
|
|
error(lineno, 'field with no value')
|
|
else:
|
|
if len(subs) == 1:
|
|
f = subs[0]
|
|
else:
|
|
mask = 0
|
|
for s in subs:
|
|
if mask & s.mask:
|
|
error(lineno, 'field components overlap')
|
|
mask |= s.mask
|
|
f = MultiField(subs, mask)
|
|
if func:
|
|
f = FunctionField(func, f)
|
|
|
|
if name in fields:
|
|
error(lineno, 'duplicate field', name)
|
|
fields[name] = f
|
|
# end parse_field
|
|
|
|
|
|
def parse_arguments(lineno, name, toks):
|
|
"""Parse one argument set from TOKS at LINENO"""
|
|
global arguments
|
|
global re_ident
|
|
global anyextern
|
|
|
|
flds = []
|
|
extern = False
|
|
for t in toks:
|
|
if re.fullmatch('!extern', t):
|
|
extern = True
|
|
anyextern = True
|
|
continue
|
|
if not re.fullmatch(re_ident, t):
|
|
error(lineno, 'invalid argument set token "{0}"'.format(t))
|
|
if t in flds:
|
|
error(lineno, 'duplicate argument "{0}"'.format(t))
|
|
flds.append(t)
|
|
|
|
if name in arguments:
|
|
error(lineno, 'duplicate argument set', name)
|
|
arguments[name] = Arguments(name, flds, extern)
|
|
# end parse_arguments
|
|
|
|
|
|
def lookup_field(lineno, name):
|
|
global fields
|
|
if name in fields:
|
|
return fields[name]
|
|
error(lineno, 'undefined field', name)
|
|
|
|
|
|
def add_field(lineno, flds, new_name, f):
|
|
if new_name in flds:
|
|
error(lineno, 'duplicate field', new_name)
|
|
flds[new_name] = f
|
|
return flds
|
|
|
|
|
|
def add_field_byname(lineno, flds, new_name, old_name):
|
|
return add_field(lineno, flds, new_name, lookup_field(lineno, old_name))
|
|
|
|
|
|
def infer_argument_set(flds):
|
|
global arguments
|
|
global decode_function
|
|
|
|
for arg in arguments.values():
|
|
if eq_fields_for_args(flds, arg.fields):
|
|
return arg
|
|
|
|
name = decode_function + str(len(arguments))
|
|
arg = Arguments(name, flds.keys(), False)
|
|
arguments[name] = arg
|
|
return arg
|
|
|
|
|
|
def infer_format(arg, fieldmask, flds, width):
|
|
global arguments
|
|
global formats
|
|
global decode_function
|
|
|
|
const_flds = {}
|
|
var_flds = {}
|
|
for n, c in flds.items():
|
|
if c is ConstField:
|
|
const_flds[n] = c
|
|
else:
|
|
var_flds[n] = c
|
|
|
|
# Look for an existing format with the same argument set and fields
|
|
for fmt in formats.values():
|
|
if arg and fmt.base != arg:
|
|
continue
|
|
if fieldmask != fmt.fieldmask:
|
|
continue
|
|
if width != fmt.width:
|
|
continue
|
|
if not eq_fields_for_fmts(flds, fmt.fields):
|
|
continue
|
|
return (fmt, const_flds)
|
|
|
|
name = decode_function + '_Fmt_' + str(len(formats))
|
|
if not arg:
|
|
arg = infer_argument_set(flds)
|
|
|
|
fmt = Format(name, 0, arg, 0, 0, 0, fieldmask, var_flds, width)
|
|
formats[name] = fmt
|
|
|
|
return (fmt, const_flds)
|
|
# end infer_format
|
|
|
|
|
|
def parse_generic(lineno, parent_pat, name, toks):
|
|
"""Parse one instruction format from TOKS at LINENO"""
|
|
global fields
|
|
global arguments
|
|
global formats
|
|
global allpatterns
|
|
global re_ident
|
|
global insnwidth
|
|
global insnmask
|
|
global variablewidth
|
|
|
|
is_format = parent_pat is None
|
|
|
|
fixedmask = 0
|
|
fixedbits = 0
|
|
undefmask = 0
|
|
width = 0
|
|
flds = {}
|
|
arg = None
|
|
fmt = None
|
|
for t in toks:
|
|
# '&Foo' gives a format an explcit argument set.
|
|
if t[0] == '&':
|
|
tt = t[1:]
|
|
if arg:
|
|
error(lineno, 'multiple argument sets')
|
|
if tt in arguments:
|
|
arg = arguments[tt]
|
|
else:
|
|
error(lineno, 'undefined argument set', t)
|
|
continue
|
|
|
|
# '@Foo' gives a pattern an explicit format.
|
|
if t[0] == '@':
|
|
tt = t[1:]
|
|
if fmt:
|
|
error(lineno, 'multiple formats')
|
|
if tt in formats:
|
|
fmt = formats[tt]
|
|
else:
|
|
error(lineno, 'undefined format', t)
|
|
continue
|
|
|
|
# '%Foo' imports a field.
|
|
if t[0] == '%':
|
|
tt = t[1:]
|
|
flds = add_field_byname(lineno, flds, tt, tt)
|
|
continue
|
|
|
|
# 'Foo=%Bar' imports a field with a different name.
|
|
if re.fullmatch(re_ident + '=%' + re_ident, t):
|
|
(fname, iname) = t.split('=%')
|
|
flds = add_field_byname(lineno, flds, fname, iname)
|
|
continue
|
|
|
|
# 'Foo=number' sets an argument field to a constant value
|
|
if re.fullmatch(re_ident + '=[+-]?[0-9]+', t):
|
|
(fname, value) = t.split('=')
|
|
value = int(value)
|
|
flds = add_field(lineno, flds, fname, ConstField(value))
|
|
continue
|
|
|
|
# Pattern of 0s, 1s, dots and dashes indicate required zeros,
|
|
# required ones, or dont-cares.
|
|
if re.fullmatch('[01.-]+', t):
|
|
shift = len(t)
|
|
fms = t.replace('0', '1')
|
|
fms = fms.replace('.', '0')
|
|
fms = fms.replace('-', '0')
|
|
fbs = t.replace('.', '0')
|
|
fbs = fbs.replace('-', '0')
|
|
ubm = t.replace('1', '0')
|
|
ubm = ubm.replace('.', '0')
|
|
ubm = ubm.replace('-', '1')
|
|
fms = int(fms, 2)
|
|
fbs = int(fbs, 2)
|
|
ubm = int(ubm, 2)
|
|
fixedbits = (fixedbits << shift) | fbs
|
|
fixedmask = (fixedmask << shift) | fms
|
|
undefmask = (undefmask << shift) | ubm
|
|
# Otherwise, fieldname:fieldwidth
|
|
elif re.fullmatch(re_ident + ':s?[0-9]+', t):
|
|
(fname, flen) = t.split(':')
|
|
sign = False
|
|
if flen[0] == 's':
|
|
sign = True
|
|
flen = flen[1:]
|
|
shift = int(flen, 10)
|
|
if shift + width > insnwidth:
|
|
error(lineno, 'field {0} exceeds insnwidth'.format(fname))
|
|
f = Field(sign, insnwidth - width - shift, shift)
|
|
flds = add_field(lineno, flds, fname, f)
|
|
fixedbits <<= shift
|
|
fixedmask <<= shift
|
|
undefmask <<= shift
|
|
else:
|
|
error(lineno, 'invalid token "{0}"'.format(t))
|
|
width += shift
|
|
|
|
if variablewidth and width < insnwidth and width % 8 == 0:
|
|
shift = insnwidth - width
|
|
fixedbits <<= shift
|
|
fixedmask <<= shift
|
|
undefmask <<= shift
|
|
undefmask |= (1 << shift) - 1
|
|
|
|
# We should have filled in all of the bits of the instruction.
|
|
elif not (is_format and width == 0) and width != insnwidth:
|
|
error(lineno, 'definition has {0} bits'.format(width))
|
|
|
|
# Do not check for fields overlaping fields; one valid usage
|
|
# is to be able to duplicate fields via import.
|
|
fieldmask = 0
|
|
for f in flds.values():
|
|
fieldmask |= f.mask
|
|
|
|
# Fix up what we've parsed to match either a format or a pattern.
|
|
if is_format:
|
|
# Formats cannot reference formats.
|
|
if fmt:
|
|
error(lineno, 'format referencing format')
|
|
# If an argument set is given, then there should be no fields
|
|
# without a place to store it.
|
|
if arg:
|
|
for f in flds.keys():
|
|
if f not in arg.fields:
|
|
error(lineno, 'field {0} not in argument set {1}'
|
|
.format(f, arg.name))
|
|
else:
|
|
arg = infer_argument_set(flds)
|
|
if name in formats:
|
|
error(lineno, 'duplicate format name', name)
|
|
fmt = Format(name, lineno, arg, fixedbits, fixedmask,
|
|
undefmask, fieldmask, flds, width)
|
|
formats[name] = fmt
|
|
else:
|
|
# Patterns can reference a format ...
|
|
if fmt:
|
|
# ... but not an argument simultaneously
|
|
if arg:
|
|
error(lineno, 'pattern specifies both format and argument set')
|
|
if fixedmask & fmt.fixedmask:
|
|
error(lineno, 'pattern fixed bits overlap format fixed bits')
|
|
if width != fmt.width:
|
|
error(lineno, 'pattern uses format of different width')
|
|
fieldmask |= fmt.fieldmask
|
|
fixedbits |= fmt.fixedbits
|
|
fixedmask |= fmt.fixedmask
|
|
undefmask |= fmt.undefmask
|
|
else:
|
|
(fmt, flds) = infer_format(arg, fieldmask, flds, width)
|
|
arg = fmt.base
|
|
for f in flds.keys():
|
|
if f not in arg.fields:
|
|
error(lineno, 'field {0} not in argument set {1}'
|
|
.format(f, arg.name))
|
|
if f in fmt.fields.keys():
|
|
error(lineno, 'field {0} set by format and pattern'.format(f))
|
|
for f in arg.fields:
|
|
if f not in flds.keys() and f not in fmt.fields.keys():
|
|
error(lineno, 'field {0} not initialized'.format(f))
|
|
pat = Pattern(name, lineno, fmt, fixedbits, fixedmask,
|
|
undefmask, fieldmask, flds, width)
|
|
parent_pat.pats.append(pat)
|
|
allpatterns.append(pat)
|
|
|
|
# Validate the masks that we have assembled.
|
|
if fieldmask & fixedmask:
|
|
error(lineno, 'fieldmask overlaps fixedmask (0x{0:08x} & 0x{1:08x})'
|
|
.format(fieldmask, fixedmask))
|
|
if fieldmask & undefmask:
|
|
error(lineno, 'fieldmask overlaps undefmask (0x{0:08x} & 0x{1:08x})'
|
|
.format(fieldmask, undefmask))
|
|
if fixedmask & undefmask:
|
|
error(lineno, 'fixedmask overlaps undefmask (0x{0:08x} & 0x{1:08x})'
|
|
.format(fixedmask, undefmask))
|
|
if not is_format:
|
|
allbits = fieldmask | fixedmask | undefmask
|
|
if allbits != insnmask:
|
|
error(lineno, 'bits left unspecified (0x{0:08x})'
|
|
.format(allbits ^ insnmask))
|
|
# end parse_general
|
|
|
|
|
|
def parse_file(f, parent_pat):
|
|
"""Parse all of the patterns within a file"""
|
|
|
|
# Read all of the lines of the file. Concatenate lines
|
|
# ending in backslash; discard empty lines and comments.
|
|
toks = []
|
|
lineno = 0
|
|
nesting = 0
|
|
nesting_pats = []
|
|
|
|
for line in f:
|
|
lineno += 1
|
|
|
|
# Expand and strip spaces, to find indent.
|
|
line = line.rstrip()
|
|
line = line.expandtabs()
|
|
len1 = len(line)
|
|
line = line.lstrip()
|
|
len2 = len(line)
|
|
|
|
# Discard comments
|
|
end = line.find('#')
|
|
if end >= 0:
|
|
line = line[:end]
|
|
|
|
t = line.split()
|
|
if len(toks) != 0:
|
|
# Next line after continuation
|
|
toks.extend(t)
|
|
else:
|
|
# Allow completely blank lines.
|
|
if len1 == 0:
|
|
continue
|
|
indent = len1 - len2
|
|
# Empty line due to comment.
|
|
if len(t) == 0:
|
|
# Indentation must be correct, even for comment lines.
|
|
if indent != nesting:
|
|
error(lineno, 'indentation ', indent, ' != ', nesting)
|
|
continue
|
|
start_lineno = lineno
|
|
toks = t
|
|
|
|
# Continuation?
|
|
if toks[-1] == '\\':
|
|
toks.pop()
|
|
continue
|
|
|
|
name = toks[0]
|
|
del toks[0]
|
|
|
|
# End nesting?
|
|
if name == '}' or name == ']':
|
|
if len(toks) != 0:
|
|
error(start_lineno, 'extra tokens after close brace')
|
|
|
|
# Make sure { } and [ ] nest properly.
|
|
if (name == '}') != isinstance(parent_pat, IncMultiPattern):
|
|
error(lineno, 'mismatched close brace')
|
|
|
|
try:
|
|
parent_pat = nesting_pats.pop()
|
|
except:
|
|
error(lineno, 'extra close brace')
|
|
|
|
nesting -= 2
|
|
if indent != nesting:
|
|
error(lineno, 'indentation ', indent, ' != ', nesting)
|
|
|
|
toks = []
|
|
continue
|
|
|
|
# Everything else should have current indentation.
|
|
if indent != nesting:
|
|
error(start_lineno, 'indentation ', indent, ' != ', nesting)
|
|
|
|
# Start nesting?
|
|
if name == '{' or name == '[':
|
|
if len(toks) != 0:
|
|
error(start_lineno, 'extra tokens after open brace')
|
|
|
|
if name == '{':
|
|
nested_pat = IncMultiPattern(start_lineno)
|
|
else:
|
|
nested_pat = ExcMultiPattern(start_lineno)
|
|
parent_pat.pats.append(nested_pat)
|
|
nesting_pats.append(parent_pat)
|
|
parent_pat = nested_pat
|
|
|
|
nesting += 2
|
|
toks = []
|
|
continue
|
|
|
|
# Determine the type of object needing to be parsed.
|
|
if name[0] == '%':
|
|
parse_field(start_lineno, name[1:], toks)
|
|
elif name[0] == '&':
|
|
parse_arguments(start_lineno, name[1:], toks)
|
|
elif name[0] == '@':
|
|
parse_generic(start_lineno, None, name[1:], toks)
|
|
else:
|
|
parse_generic(start_lineno, parent_pat, name, toks)
|
|
toks = []
|
|
|
|
if nesting != 0:
|
|
error(lineno, 'missing close brace')
|
|
# end parse_file
|
|
|
|
|
|
class SizeTree:
|
|
"""Class representing a node in a size decode tree"""
|
|
|
|
def __init__(self, m, w):
|
|
self.mask = m
|
|
self.subs = []
|
|
self.base = None
|
|
self.width = w
|
|
|
|
def str1(self, i):
|
|
ind = str_indent(i)
|
|
r = '{0}{1:08x}'.format(ind, self.mask)
|
|
r += ' [\n'
|
|
for (b, s) in self.subs:
|
|
r += '{0} {1:08x}:\n'.format(ind, b)
|
|
r += s.str1(i + 4) + '\n'
|
|
r += ind + ']'
|
|
return r
|
|
|
|
def __str__(self):
|
|
return self.str1(0)
|
|
|
|
def output_code(self, i, extracted, outerbits, outermask):
|
|
ind = str_indent(i)
|
|
|
|
# If we need to load more bytes to test, do so now.
|
|
if extracted < self.width:
|
|
output(ind, 'insn = ', decode_function,
|
|
'_load_bytes(ctx, insn, {0}, {1});\n'
|
|
.format(extracted // 8, self.width // 8));
|
|
extracted = self.width
|
|
|
|
# Attempt to aid the compiler in producing compact switch statements.
|
|
# If the bits in the mask are contiguous, extract them.
|
|
sh = is_contiguous(self.mask)
|
|
if sh > 0:
|
|
# Propagate SH down into the local functions.
|
|
def str_switch(b, sh=sh):
|
|
return '(insn >> {0}) & 0x{1:x}'.format(sh, b >> sh)
|
|
|
|
def str_case(b, sh=sh):
|
|
return '0x{0:x}'.format(b >> sh)
|
|
else:
|
|
def str_switch(b):
|
|
return 'insn & 0x{0:08x}'.format(b)
|
|
|
|
def str_case(b):
|
|
return '0x{0:08x}'.format(b)
|
|
|
|
output(ind, 'switch (', str_switch(self.mask), ') {\n')
|
|
for b, s in sorted(self.subs):
|
|
innermask = outermask | self.mask
|
|
innerbits = outerbits | b
|
|
output(ind, 'case ', str_case(b), ':\n')
|
|
output(ind, ' /* ',
|
|
str_match_bits(innerbits, innermask), ' */\n')
|
|
s.output_code(i + 4, extracted, innerbits, innermask)
|
|
output(ind, '}\n')
|
|
output(ind, 'return insn;\n')
|
|
# end SizeTree
|
|
|
|
class SizeLeaf:
|
|
"""Class representing a leaf node in a size decode tree"""
|
|
|
|
def __init__(self, m, w):
|
|
self.mask = m
|
|
self.width = w
|
|
|
|
def str1(self, i):
|
|
ind = str_indent(i)
|
|
return '{0}{1:08x}'.format(ind, self.mask)
|
|
|
|
def __str__(self):
|
|
return self.str1(0)
|
|
|
|
def output_code(self, i, extracted, outerbits, outermask):
|
|
global decode_function
|
|
ind = str_indent(i)
|
|
|
|
# If we need to load more bytes, do so now.
|
|
if extracted < self.width:
|
|
output(ind, 'insn = ', decode_function,
|
|
'_load_bytes(ctx, insn, {0}, {1});\n'
|
|
.format(extracted // 8, self.width // 8));
|
|
extracted = self.width
|
|
output(ind, 'return insn;\n')
|
|
# end SizeLeaf
|
|
|
|
|
|
def build_size_tree(pats, width, outerbits, outermask):
|
|
global insnwidth
|
|
|
|
# Collect the mask of bits that are fixed in this width
|
|
innermask = 0xff << (insnwidth - width)
|
|
innermask &= ~outermask
|
|
minwidth = None
|
|
onewidth = True
|
|
for i in pats:
|
|
innermask &= i.fixedmask
|
|
if minwidth is None:
|
|
minwidth = i.width
|
|
elif minwidth != i.width:
|
|
onewidth = False;
|
|
if minwidth < i.width:
|
|
minwidth = i.width
|
|
|
|
if onewidth:
|
|
return SizeLeaf(innermask, minwidth)
|
|
|
|
if innermask == 0:
|
|
if width < minwidth:
|
|
return build_size_tree(pats, width + 8, outerbits, outermask)
|
|
|
|
pnames = []
|
|
for p in pats:
|
|
pnames.append(p.name + ':' + p.file + ':' + str(p.lineno))
|
|
error_with_file(pats[0].file, pats[0].lineno,
|
|
'overlapping patterns size {0}:'.format(width), pnames)
|
|
|
|
bins = {}
|
|
for i in pats:
|
|
fb = i.fixedbits & innermask
|
|
if fb in bins:
|
|
bins[fb].append(i)
|
|
else:
|
|
bins[fb] = [i]
|
|
|
|
fullmask = outermask | innermask
|
|
lens = sorted(bins.keys())
|
|
if len(lens) == 1:
|
|
b = lens[0]
|
|
return build_size_tree(bins[b], width + 8, b | outerbits, fullmask)
|
|
|
|
r = SizeTree(innermask, width)
|
|
for b, l in bins.items():
|
|
s = build_size_tree(l, width, b | outerbits, fullmask)
|
|
r.subs.append((b, s))
|
|
return r
|
|
# end build_size_tree
|
|
|
|
|
|
def prop_size(tree):
|
|
"""Propagate minimum widths up the decode size tree"""
|
|
|
|
if isinstance(tree, SizeTree):
|
|
min = None
|
|
for (b, s) in tree.subs:
|
|
width = prop_size(s)
|
|
if min is None or min > width:
|
|
min = width
|
|
assert min >= tree.width
|
|
tree.width = min
|
|
else:
|
|
min = tree.width
|
|
return min
|
|
# end prop_size
|
|
|
|
|
|
def main():
|
|
global arguments
|
|
global formats
|
|
global allpatterns
|
|
global translate_scope
|
|
global translate_prefix
|
|
global output_fd
|
|
global output_file
|
|
global input_file
|
|
global insnwidth
|
|
global insntype
|
|
global insnmask
|
|
global decode_function
|
|
global variablewidth
|
|
global anyextern
|
|
|
|
decode_scope = 'static '
|
|
|
|
long_opts = ['decode=', 'translate=', 'output=', 'insnwidth=',
|
|
'static-decode=', 'varinsnwidth=']
|
|
try:
|
|
(opts, args) = getopt.getopt(sys.argv[1:], 'o:vw:', long_opts)
|
|
except getopt.GetoptError as err:
|
|
error(0, err)
|
|
for o, a in opts:
|
|
if o in ('-o', '--output'):
|
|
output_file = a
|
|
elif o == '--decode':
|
|
decode_function = a
|
|
decode_scope = ''
|
|
elif o == '--static-decode':
|
|
decode_function = a
|
|
elif o == '--translate':
|
|
translate_prefix = a
|
|
translate_scope = ''
|
|
elif o in ('-w', '--insnwidth', '--varinsnwidth'):
|
|
if o == '--varinsnwidth':
|
|
variablewidth = True
|
|
insnwidth = int(a)
|
|
if insnwidth == 16:
|
|
insntype = 'uint16_t'
|
|
insnmask = 0xffff
|
|
elif insnwidth != 32:
|
|
error(0, 'cannot handle insns of width', insnwidth)
|
|
else:
|
|
assert False, 'unhandled option'
|
|
|
|
if len(args) < 1:
|
|
error(0, 'missing input file')
|
|
|
|
toppat = ExcMultiPattern(0)
|
|
|
|
for filename in args:
|
|
input_file = filename
|
|
f = open(filename, 'r')
|
|
parse_file(f, toppat)
|
|
f.close()
|
|
|
|
# We do not want to compute masks for toppat, because those masks
|
|
# are used as a starting point for build_tree. For toppat, we must
|
|
# insist that decode begins from naught.
|
|
for i in toppat.pats:
|
|
i.prop_masks()
|
|
|
|
toppat.build_tree()
|
|
toppat.prop_format()
|
|
|
|
if variablewidth:
|
|
for i in toppat.pats:
|
|
i.prop_width()
|
|
stree = build_size_tree(toppat.pats, 8, 0, 0)
|
|
prop_size(stree)
|
|
|
|
if output_file:
|
|
output_fd = open(output_file, 'w')
|
|
else:
|
|
output_fd = sys.stdout
|
|
|
|
output_autogen()
|
|
for n in sorted(arguments.keys()):
|
|
f = arguments[n]
|
|
f.output_def()
|
|
|
|
# A single translate function can be invoked for different patterns.
|
|
# Make sure that the argument sets are the same, and declare the
|
|
# function only once.
|
|
#
|
|
# If we're sharing formats, we're likely also sharing trans_* functions,
|
|
# but we can't tell which ones. Prevent issues from the compiler by
|
|
# suppressing redundant declaration warnings.
|
|
if anyextern:
|
|
output("#pragma GCC diagnostic push\n",
|
|
"#pragma GCC diagnostic ignored \"-Wredundant-decls\"\n",
|
|
"#ifdef __clang__\n"
|
|
"# pragma GCC diagnostic ignored \"-Wtypedef-redefinition\"\n",
|
|
"#endif\n\n")
|
|
|
|
out_pats = {}
|
|
for i in allpatterns:
|
|
if i.name in out_pats:
|
|
p = out_pats[i.name]
|
|
if i.base.base != p.base.base:
|
|
error(0, i.name, ' has conflicting argument sets')
|
|
else:
|
|
i.output_decl()
|
|
out_pats[i.name] = i
|
|
output('\n')
|
|
|
|
if anyextern:
|
|
output("#pragma GCC diagnostic pop\n\n")
|
|
|
|
for n in sorted(formats.keys()):
|
|
f = formats[n]
|
|
f.output_extract()
|
|
|
|
output(decode_scope, 'bool ', decode_function,
|
|
'(DisasContext *ctx, ', insntype, ' insn)\n{\n')
|
|
|
|
i4 = str_indent(4)
|
|
|
|
if len(allpatterns) != 0:
|
|
output(i4, 'union {\n')
|
|
for n in sorted(arguments.keys()):
|
|
f = arguments[n]
|
|
output(i4, i4, f.struct_name(), ' f_', f.name, ';\n')
|
|
output(i4, '} u;\n\n')
|
|
toppat.output_code(4, False, 0, 0)
|
|
|
|
output(i4, 'return false;\n')
|
|
output('}\n')
|
|
|
|
if variablewidth:
|
|
output('\n', decode_scope, insntype, ' ', decode_function,
|
|
'_load(DisasContext *ctx)\n{\n',
|
|
' ', insntype, ' insn = 0;\n\n')
|
|
stree.output_code(4, 0, 0, 0)
|
|
output('}\n')
|
|
|
|
if output_file:
|
|
output_fd.close()
|
|
# end main
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|