decodetree: Multi-cleanup

Includes multiple changes by Richard Henderson as follows:

- Use proper varargs to print the arguments. (2fd51b19c9)
- Rename MultiPattern to IncMultiPattern (040145c4f8)
- Split out MultiPattern from IncMultiPattern (df63044d02)
- Allow group covering the entire insn space (b44b3449a0)
- Move semantic propagation into classes (08561fc128)
- Implement non-overlapping groups (067e8b0f45)
- Drop check for less than 2 patterns in a group (fe079aa13d)
This commit is contained in:
Richard Henderson 2020-06-15 12:48:47 -04:00 committed by Lioncash
parent 7427cca6cc
commit b45a02e2f7

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# Copyright (c) 2018 Linaro Limited # Copyright (c) 2018 Linaro Limited
# #
# This library is free software; you can redistribute it and/or # This library is free software; you can redistribute it and/or
@ -17,139 +17,7 @@
# #
# Generate a decoding tree from a specification file. # Generate a decoding tree from a specification file.
# # See the syntax and semantics in docs/devel/decodetree.rst.
# The tree is built from instruction "patterns". A pattern may represent
# a single architectural instruction or a group of same, depending on what
# is convenient for further processing.
#
# Each pattern has "fixedbits" & "fixedmask", the combination of which
# describes the condition under which the pattern is matched:
#
# (insn & fixedmask) == fixedbits
#
# Each pattern may have "fields", which are extracted from the insn and
# passed along to the translator. Examples of such are registers,
# immediates, and sub-opcodes.
#
# In support of patterns, one may declare fields, argument sets, and
# formats, each of which may be re-used to simplify further definitions.
#
# *** Field syntax:
#
# field_def := '%' identifier ( unnamed_field )+ ( !function=identifier )?
# unnamed_field := number ':' ( 's' ) number
#
# For unnamed_field, the first number is the least-significant bit position of
# the field and the second number is the length of the field. If the 's' is
# present, the field is considered signed. If multiple unnamed_fields are
# present, they are concatenated. In this way one can define disjoint fields.
#
# If !function is specified, the concatenated result is passed through the
# named function, taking and returning an integral value.
#
# FIXME: the fields of the structure into which this result will be stored
# is restricted to "int". Which means that we cannot expand 64-bit items.
#
# Field examples:
#
# %disp 0:s16 -- sextract(i, 0, 16)
# %imm9 16:6 10:3 -- extract(i, 16, 6) << 3 | extract(i, 10, 3)
# %disp12 0:s1 1:1 2:10 -- sextract(i, 0, 1) << 11
# | extract(i, 1, 1) << 10
# | extract(i, 2, 10)
# %shimm8 5:s8 13:1 !function=expand_shimm8
# -- expand_shimm8(sextract(i, 5, 8) << 1
# | extract(i, 13, 1))
#
# *** Argument set syntax:
#
# args_def := '&' identifier ( args_elt )+ ( !extern )?
# args_elt := identifier
#
# Each args_elt defines an argument within the argument set.
# Each argument set will be rendered as a C structure "arg_$name"
# with each of the fields being one of the member arguments.
#
# If !extern is specified, the backing structure is assumed to
# have been already declared, typically via a second decoder.
#
# Argument set examples:
#
# &reg3 ra rb rc
# &loadstore reg base offset
#
# *** Format syntax:
#
# fmt_def := '@' identifier ( fmt_elt )+
# fmt_elt := fixedbit_elt | field_elt | field_ref | args_ref
# fixedbit_elt := [01.-]+
# field_elt := identifier ':' 's'? number
# field_ref := '%' identifier | identifier '=' '%' identifier
# args_ref := '&' identifier
#
# Defining a format is a handy way to avoid replicating groups of fields
# across many instruction patterns.
#
# A fixedbit_elt describes a contiguous sequence of bits that must
# be 1, 0, [.-] for don't care. The difference between '.' and '-'
# is that '.' means that the bit will be covered with a field or a
# final [01] from the pattern, and '-' means that the bit is really
# ignored by the cpu and will not be specified.
#
# A field_elt describes a simple field only given a width; the position of
# the field is implied by its position with respect to other fixedbit_elt
# and field_elt.
#
# If any fixedbit_elt or field_elt appear then all bits must be defined.
# Padding with a fixedbit_elt of all '.' is an easy way to accomplish that.
#
# A field_ref incorporates a field by reference. This is the only way to
# add a complex field to a format. A field may be renamed in the process
# via assignment to another identifier. This is intended to allow the
# same argument set be used with disjoint named fields.
#
# A single args_ref may specify an argument set to use for the format.
# The set of fields in the format must be a subset of the arguments in
# the argument set. If an argument set is not specified, one will be
# inferred from the set of fields.
#
# It is recommended, but not required, that all field_ref and args_ref
# appear at the end of the line, not interleaving with fixedbit_elf or
# field_elt.
#
# Format examples:
#
# @opr ...... ra:5 rb:5 ... 0 ....... rc:5
# @opi ...... ra:5 lit:8 1 ....... rc:5
#
# *** Pattern syntax:
#
# pat_def := identifier ( pat_elt )+
# pat_elt := fixedbit_elt | field_elt | field_ref
# | args_ref | fmt_ref | const_elt
# fmt_ref := '@' identifier
# const_elt := identifier '=' number
#
# The fixedbit_elt and field_elt specifiers are unchanged from formats.
# A pattern that does not specify a named format will have one inferred
# from a referenced argument set (if present) and the set of fields.
#
# A const_elt allows a argument to be set to a constant value. This may
# come in handy when fields overlap between patterns and one has to
# include the values in the fixedbit_elt instead.
#
# The decoder will call a translator function for each pattern matched.
#
# Pattern examples:
#
# addl_r 010000 ..... ..... .... 0000000 ..... @opr
# addl_i 010000 ..... ..... .... 0000000 ..... @opi
#
# which will, in part, invoke
#
# trans_addl_r(ctx, &arg_opr, insn)
# and
# trans_addl_i(ctx, &arg_opi, insn)
# #
import os import os
@ -163,7 +31,6 @@ variablewidth = False
fields = {} fields = {}
arguments = {} arguments = {}
formats = {} formats = {}
patterns = []
allpatterns = [] allpatterns = []
anyextern = False anyextern = False
@ -183,24 +50,26 @@ def error_with_file(file, lineno, *args):
global output_file global output_file
global output_fd global output_fd
prefix = ''
if file:
prefix += '{0}:'.format(file)
if lineno: if lineno:
r = '{0}:{1}: error:'.format(file, lineno) prefix += '{0}:'.format(lineno)
elif input_file: if prefix:
r = '{0}: error:'.format(file) prefix += ' '
else: print(prefix, end='error: ', file=sys.stderr)
r = 'error:' print(*args, file=sys.stderr)
for a in args:
r += ' ' + str(a)
r += '\n'
sys.stderr.write(r)
if output_file and output_fd: if output_file and output_fd:
output_fd.close() output_fd.close()
os.remove(output_file) os.remove(output_file)
exit(1) exit(1)
# end error_with_file
def error(lineno, *args): def error(lineno, *args):
error_with_file(input_file, lineno, args) error_with_file(input_file, lineno, *args)
# end error
def output(*args): def output(*args):
@ -209,13 +78,6 @@ def output(*args):
output_fd.write(a) output_fd.write(a)
if sys.version_info >= (3, 4):
re_fullmatch = re.fullmatch
else:
def re_fullmatch(pat, str):
return re.match('^' + pat + '$', str)
def output_autogen(): def output_autogen():
output('/* This file is autogenerated by scripts/decodetree.py. */\n\n') output('/* This file is autogenerated by scripts/decodetree.py. */\n\n')
@ -261,6 +123,7 @@ def is_pow2(x):
def ctz(x): def ctz(x):
"""Return the number of times 2 factors into X.""" """Return the number of times 2 factors into X."""
assert x != 0
r = 0 r = 0
while ((x >> r) & 1) == 0: while ((x >> r) & 1) == 0:
r += 1 r += 1
@ -268,6 +131,8 @@ def ctz(x):
def is_contiguous(bits): def is_contiguous(bits):
if bits == 0:
return -1
shift = ctz(bits) shift = ctz(bits)
if is_pow2((bits >> shift) + 1): if is_pow2((bits >> shift) + 1):
return shift return shift
@ -505,32 +370,99 @@ class Pattern(General):
output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n') output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n')
output(ind, 'if (', translate_prefix, '_', self.name, output(ind, 'if (', translate_prefix, '_', self.name,
'(ctx, &u.f_', arg, ')) return true;\n') '(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 # end Pattern
class MultiPattern(General): class MultiPattern(General):
"""Class representing an overlapping set of instruction patterns""" """Class representing a set of instruction patterns"""
def __init__(self, lineno, pats, fixb, fixm, udfm, w): def __init__(self, lineno):
self.file = input_file self.file = input_file
self.lineno = lineno self.lineno = lineno
self.pats = pats self.pats = []
self.base = None self.base = None
self.fixedbits = fixb self.fixedbits = 0
self.fixedmask = fixm self.fixedmask = 0
self.undefmask = udfm self.undefmask = 0
self.width = w self.width = None
def __str__(self): def __str__(self):
r = "{" r = 'group'
for p in self.pats: if self.fixedbits is not None:
r = r + ' ' + str(p) r += ' ' + str_match_bits(self.fixedbits, self.fixedmask)
return r + "}" return r
def output_decl(self): def output_decl(self):
for p in self.pats: for p in self.pats:
p.output_decl() 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): def output_code(self, i, extracted, outerbits, outermask):
global translate_prefix global translate_prefix
ind = str_indent(i) ind = str_indent(i)
@ -547,7 +479,154 @@ class MultiPattern(General):
output(ind, '}\n') output(ind, '}\n')
else: else:
p.output_code(i, extracted, p.fixedbits, p.fixedmask) p.output_code(i, extracted, p.fixedbits, p.fixedmask)
#end MultiPattern #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): def parse_field(lineno, name, toks):
@ -562,18 +641,18 @@ def parse_field(lineno, name, toks):
width = 0 width = 0
func = None func = None
for t in toks: for t in toks:
if re_fullmatch('!function=' + re_ident, t): if re.fullmatch('!function=' + re_ident, t):
if func: if func:
error(lineno, 'duplicate function') error(lineno, 'duplicate function')
func = t.split('=') func = t.split('=')
func = func[1] func = func[1]
continue continue
if re_fullmatch('[0-9]+:s[0-9]+', t): if re.fullmatch('[0-9]+:s[0-9]+', t):
# Signed field extract # Signed field extract
subtoks = t.split(':s') subtoks = t.split(':s')
sign = True sign = True
elif re_fullmatch('[0-9]+:[0-9]+', t): elif re.fullmatch('[0-9]+:[0-9]+', t):
# Unsigned field extract # Unsigned field extract
subtoks = t.split(':') subtoks = t.split(':')
sign = False sign = False
@ -622,11 +701,11 @@ def parse_arguments(lineno, name, toks):
flds = [] flds = []
extern = False extern = False
for t in toks: for t in toks:
if re_fullmatch('!extern', t): if re.fullmatch('!extern', t):
extern = True extern = True
anyextern = True anyextern = True
continue continue
if not re_fullmatch(re_ident, t): if not re.fullmatch(re_ident, t):
error(lineno, 'invalid argument set token "{0}"'.format(t)) error(lineno, 'invalid argument set token "{0}"'.format(t))
if t in flds: if t in flds:
error(lineno, 'duplicate argument "{0}"'.format(t)) error(lineno, 'duplicate argument "{0}"'.format(t))
@ -706,18 +785,19 @@ def infer_format(arg, fieldmask, flds, width):
# end infer_format # end infer_format
def parse_generic(lineno, is_format, name, toks): def parse_generic(lineno, parent_pat, name, toks):
"""Parse one instruction format from TOKS at LINENO""" """Parse one instruction format from TOKS at LINENO"""
global fields global fields
global arguments global arguments
global formats global formats
global patterns
global allpatterns global allpatterns
global re_ident global re_ident
global insnwidth global insnwidth
global insnmask global insnmask
global variablewidth global variablewidth
is_format = parent_pat is None
fixedmask = 0 fixedmask = 0
fixedbits = 0 fixedbits = 0
undefmask = 0 undefmask = 0
@ -755,13 +835,13 @@ def parse_generic(lineno, is_format, name, toks):
continue continue
# 'Foo=%Bar' imports a field with a different name. # 'Foo=%Bar' imports a field with a different name.
if re_fullmatch(re_ident + '=%' + re_ident, t): if re.fullmatch(re_ident + '=%' + re_ident, t):
(fname, iname) = t.split('=%') (fname, iname) = t.split('=%')
flds = add_field_byname(lineno, flds, fname, iname) flds = add_field_byname(lineno, flds, fname, iname)
continue continue
# 'Foo=number' sets an argument field to a constant value # 'Foo=number' sets an argument field to a constant value
if re_fullmatch(re_ident + '=[+-]?[0-9]+', t): if re.fullmatch(re_ident + '=[+-]?[0-9]+', t):
(fname, value) = t.split('=') (fname, value) = t.split('=')
value = int(value) value = int(value)
flds = add_field(lineno, flds, fname, ConstField(value)) flds = add_field(lineno, flds, fname, ConstField(value))
@ -769,7 +849,7 @@ def parse_generic(lineno, is_format, name, toks):
# Pattern of 0s, 1s, dots and dashes indicate required zeros, # Pattern of 0s, 1s, dots and dashes indicate required zeros,
# required ones, or dont-cares. # required ones, or dont-cares.
if re_fullmatch('[01.-]+', t): if re.fullmatch('[01.-]+', t):
shift = len(t) shift = len(t)
fms = t.replace('0', '1') fms = t.replace('0', '1')
fms = fms.replace('.', '0') fms = fms.replace('.', '0')
@ -786,7 +866,7 @@ def parse_generic(lineno, is_format, name, toks):
fixedmask = (fixedmask << shift) | fms fixedmask = (fixedmask << shift) | fms
undefmask = (undefmask << shift) | ubm undefmask = (undefmask << shift) | ubm
# Otherwise, fieldname:fieldwidth # Otherwise, fieldname:fieldwidth
elif re_fullmatch(re_ident + ':s?[0-9]+', t): elif re.fullmatch(re_ident + ':s?[0-9]+', t):
(fname, flen) = t.split(':') (fname, flen) = t.split(':')
sign = False sign = False
if flen[0] == 's': if flen[0] == 's':
@ -868,7 +948,7 @@ def parse_generic(lineno, is_format, name, toks):
error(lineno, 'field {0} not initialized'.format(f)) error(lineno, 'field {0} not initialized'.format(f))
pat = Pattern(name, lineno, fmt, fixedbits, fixedmask, pat = Pattern(name, lineno, fmt, fixedbits, fixedmask,
undefmask, fieldmask, flds, width) undefmask, fieldmask, flds, width)
patterns.append(pat) parent_pat.pats.append(pat)
allpatterns.append(pat) allpatterns.append(pat)
# Validate the masks that we have assembled. # Validate the masks that we have assembled.
@ -889,63 +969,15 @@ def parse_generic(lineno, is_format, name, toks):
# end parse_general # end parse_general
def build_multi_pattern(lineno, pats): def parse_file(f, parent_pat):
"""Validate the Patterns going into a MultiPattern."""
global patterns
global insnmask
if len(pats) < 2:
error(lineno, 'less than two patterns within braces')
fixedmask = insnmask
undefmask = insnmask
# Collect fixed/undefmask for all of the children.
# Move the defining lineno back to that of the first child.
for p in pats:
fixedmask &= p.fixedmask
undefmask &= p.undefmask
if p.lineno < lineno:
lineno = p.lineno
width = None
for p in pats:
if width is None:
width = p.width
elif width != p.width:
error(lineno, 'width mismatch in patterns within braces')
repeat = True
while repeat:
if fixedmask == 0:
error(lineno, 'no overlap in patterns within braces')
fixedbits = None
for p in pats:
thisbits = p.fixedbits & fixedmask
if fixedbits is None:
fixedbits = thisbits
elif fixedbits != thisbits:
fixedmask &= ~(fixedbits ^ thisbits)
break
else:
repeat = False
mp = MultiPattern(lineno, pats, fixedbits, fixedmask, undefmask, width)
patterns.append(mp)
# end build_multi_pattern
def parse_file(f):
"""Parse all of the patterns within a file""" """Parse all of the patterns within a file"""
global patterns
# Read all of the lines of the file. Concatenate lines # Read all of the lines of the file. Concatenate lines
# ending in backslash; discard empty lines and comments. # ending in backslash; discard empty lines and comments.
toks = [] toks = []
lineno = 0 lineno = 0
nesting = 0 nesting = 0
saved_pats = [] nesting_pats = []
for line in f: for line in f:
lineno += 1 lineno += 1
@ -989,17 +1021,23 @@ def parse_file(f):
del toks[0] del toks[0]
# End nesting? # End nesting?
if name == '}': if name == '}' or name == ']':
if nesting == 0:
error(start_lineno, 'mismatched close brace')
if len(toks) != 0: if len(toks) != 0:
error(start_lineno, 'extra tokens after close brace') 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 nesting -= 2
if indent != nesting: if indent != nesting:
error(start_lineno, 'indentation ', indent, ' != ', nesting) error(lineno, 'indentation ', indent, ' != ', nesting)
pats = patterns
patterns = saved_pats.pop()
build_multi_pattern(lineno, pats)
toks = [] toks = []
continue continue
@ -1008,11 +1046,18 @@ def parse_file(f):
error(start_lineno, 'indentation ', indent, ' != ', nesting) error(start_lineno, 'indentation ', indent, ' != ', nesting)
# Start nesting? # Start nesting?
if name == '{': if name == '{' or name == '[':
if len(toks) != 0: if len(toks) != 0:
error(start_lineno, 'extra tokens after open brace') error(start_lineno, 'extra tokens after open brace')
saved_pats.append(patterns)
patterns = [] 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 nesting += 2
toks = [] toks = []
continue continue
@ -1023,115 +1068,16 @@ def parse_file(f):
elif name[0] == '&': elif name[0] == '&':
parse_arguments(start_lineno, name[1:], toks) parse_arguments(start_lineno, name[1:], toks)
elif name[0] == '@': elif name[0] == '@':
parse_generic(start_lineno, True, name[1:], toks) parse_generic(start_lineno, None, name[1:], toks)
else: else:
parse_generic(start_lineno, False, name, toks) parse_generic(start_lineno, parent_pat, name, toks)
toks = [] toks = []
if nesting != 0:
error(lineno, 'missing close brace')
# end parse_file # end parse_file
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
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:
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 = build_tree(l, b | outerbits, fullmask)
t.subs.append((b, s))
return t
# end build_tree
class SizeTree: class SizeTree:
"""Class representing a node in a size decode tree""" """Class representing a node in a size decode tree"""
@ -1273,28 +1219,6 @@ def build_size_tree(pats, width, outerbits, outermask):
# end build_size_tree # end build_size_tree
def prop_format(tree):
"""Propagate Format objects into the decode tree"""
# Depth first search.
for (b, s) in tree.subs:
if isinstance(s, Tree):
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
# end prop_format
def prop_size(tree): def prop_size(tree):
"""Propagate minimum widths up the decode size tree""" """Propagate minimum widths up the decode size tree"""
@ -1315,7 +1239,6 @@ def prop_size(tree):
def main(): def main():
global arguments global arguments
global formats global formats
global patterns
global allpatterns global allpatterns
global translate_scope global translate_scope
global translate_prefix global translate_prefix
@ -1362,18 +1285,29 @@ def main():
if len(args) < 1: if len(args) < 1:
error(0, 'missing input file') error(0, 'missing input file')
toppat = ExcMultiPattern(0)
for filename in args: for filename in args:
input_file = filename input_file = filename
f = open(filename, 'r') f = open(filename, 'r')
parse_file(f) parse_file(f, toppat)
f.close() f.close()
if variablewidth: # We do not want to compute masks for toppat, because those masks
stree = build_size_tree(patterns, 8, 0, 0) # are used as a starting point for build_tree. For toppat, we must
prop_size(stree) # insist that decode begins from naught.
for i in toppat.pats:
i.prop_masks()
dtree = build_tree(patterns, 0, 0) toppat.build_tree()
prop_format(dtree) 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: if output_file:
output_fd = open(output_file, 'w') output_fd = open(output_file, 'w')
@ -1425,13 +1359,14 @@ def main():
'(DisasContext *ctx, ', insntype, ' insn)\n{\n') '(DisasContext *ctx, ', insntype, ' insn)\n{\n')
i4 = str_indent(4) i4 = str_indent(4)
if len(allpatterns) != 0: if len(allpatterns) != 0:
output(i4, 'union {\n') output(i4, 'union {\n')
for n in sorted(arguments.keys()): for n in sorted(arguments.keys()):
f = arguments[n] f = arguments[n]
output(i4, i4, f.struct_name(), ' f_', f.name, ';\n') output(i4, i4, f.struct_name(), ' f_', f.name, ';\n')
output(i4, '} u;\n\n') output(i4, '} u;\n\n')
dtree.output_code(4, False, 0, 0) toppat.output_code(4, False, 0, 0)
output(i4, 'return false;\n') output(i4, 'return false;\n')
output('}\n') output('}\n')