aboutsummaryrefslogtreecommitdiff
path: root/scripts/decodetree.py
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2020-05-17 10:14:11 -0700
committerRichard Henderson <richard.henderson@linaro.org>2020-06-08 10:36:47 -0700
commit08561fc1283568d9892033f98651010a18d8f108 (patch)
tree67431768a032b0ccf22d2f82728855c4e6bd9866 /scripts/decodetree.py
parentb44b3449a08818f0eb25b93faaf535e9c9a85e50 (diff)
decodetree: Move semantic propagation into classes
Create ExcMultiPattern to hold an set of non-overlapping patterns. The body of build_tree, prop_format become member functions on this class. Add minimal member functions to Pattern and MultiPattern to allow recusion through the tree. Move the bulk of build_incmulti_pattern to prop_masks and prop_width in MultiPattern, since we will need this for both kinds of containers. Only perform prop_width for variablewidth. Remove global patterns variable, and pass down container object into parse_file from main. No functional change in all of this. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'scripts/decodetree.py')
-rwxr-xr-xscripts/decodetree.py462
1 files changed, 252 insertions, 210 deletions
diff --git a/scripts/decodetree.py b/scripts/decodetree.py
index 3307c74c30..0ba01e049c 100755
--- a/scripts/decodetree.py
+++ b/scripts/decodetree.py
@@ -31,7 +31,6 @@ variablewidth = False
fields = {}
arguments = {}
formats = {}
-patterns = []
allpatterns = []
anyextern = False
@@ -371,16 +370,27 @@ class Pattern(General):
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, pats):
+ def __init__(self, lineno):
self.file = input_file
self.lineno = lineno
- self.pats = pats
+ self.pats = []
self.base = None
self.fixedbits = 0
self.fixedmask = 0
@@ -396,22 +406,63 @@ class MultiPattern(General):
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 __init__(self, lineno, pats, fixb, fixm, udfm, w):
- self.file = input_file
- self.lineno = lineno
- self.pats = pats
- self.base = None
- self.fixedbits = fixb
- self.fixedmask = fixm
- self.undefmask = udfm
- self.width = w
-
def output_code(self, i, extracted, outerbits, outermask):
global translate_prefix
ind = str_indent(i)
@@ -431,6 +482,153 @@ class IncMultiPattern(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):
"""Parse one instruction field from TOKS at LINENO"""
global fields
@@ -587,18 +785,19 @@ def infer_format(arg, fieldmask, flds, width):
# 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"""
global fields
global arguments
global formats
- global patterns
global allpatterns
global re_ident
global insnwidth
global insnmask
global variablewidth
+ is_format = parent_pat is None
+
fixedmask = 0
fixedbits = 0
undefmask = 0
@@ -749,7 +948,7 @@ def parse_generic(lineno, is_format, name, toks):
error(lineno, 'field {0} not initialized'.format(f))
pat = Pattern(name, lineno, fmt, fixedbits, fixedmask,
undefmask, fieldmask, flds, width)
- patterns.append(pat)
+ parent_pat.pats.append(pat)
allpatterns.append(pat)
# Validate the masks that we have assembled.
@@ -769,61 +968,16 @@ def parse_generic(lineno, is_format, name, toks):
.format(allbits ^ insnmask))
# end parse_general
-def build_incmulti_pattern(lineno, pats):
- """Validate the Patterns going into a IncMultiPattern."""
- 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
- fixedbits = 0
- while repeat and fixedmask != 0:
- 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 = IncMultiPattern(lineno, pats, fixedbits, fixedmask, undefmask, width)
- patterns.append(mp)
-# end build_incmulti_pattern
-
-def parse_file(f):
+def parse_file(f, parent_pat):
"""Parse all of the patterns within a file"""
- global patterns
-
# Read all of the lines of the file. Concatenate lines
# ending in backslash; discard empty lines and comments.
toks = []
lineno = 0
nesting = 0
- saved_pats = []
+ nesting_pats = []
for line in f:
lineno += 1
@@ -868,16 +1022,20 @@ def parse_file(f):
# End nesting?
if name == '}':
- if nesting == 0:
- error(start_lineno, 'mismatched close brace')
if len(toks) != 0:
error(start_lineno, 'extra tokens after close brace')
+ if len(parent_pat.pats) < 2:
+ error(lineno, 'less than two patterns within braces')
+
+ try:
+ parent_pat = nesting_pats.pop()
+ except:
+ error(lineno, 'mismatched close brace')
+
nesting -= 2
if indent != nesting:
- error(start_lineno, 'indentation ', indent, ' != ', nesting)
- pats = patterns
- patterns = saved_pats.pop()
- build_incmulti_pattern(lineno, pats)
+ error(lineno, 'indentation ', indent, ' != ', nesting)
+
toks = []
continue
@@ -889,8 +1047,12 @@ def parse_file(f):
if name == '{':
if len(toks) != 0:
error(start_lineno, 'extra tokens after open brace')
- saved_pats.append(patterns)
- patterns = []
+
+ nested_pat = IncMultiPattern(start_lineno)
+ parent_pat.pats.append(nested_pat)
+ nesting_pats.append(parent_pat)
+ parent_pat = nested_pat
+
nesting += 2
toks = []
continue
@@ -901,121 +1063,13 @@ def parse_file(f):
elif name[0] == '&':
parse_arguments(start_lineno, name[1:], toks)
elif name[0] == '@':
- parse_generic(start_lineno, True, name[1:], toks)
+ parse_generic(start_lineno, None, name[1:], toks)
else:
- parse_generic(start_lineno, False, name, toks)
+ parse_generic(start_lineno, parent_pat, name, toks)
toks = []
# 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:
- # 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 = build_tree(l, b | outerbits, fullmask)
- t.subs.append((b, s))
-
- return t
-# end build_tree
-
-
class SizeTree:
"""Class representing a node in a size decode tree"""
@@ -1157,28 +1211,6 @@ def build_size_tree(pats, width, outerbits, outermask):
# 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):
"""Propagate minimum widths up the decode size tree"""
@@ -1199,7 +1231,6 @@ def prop_size(tree):
def main():
global arguments
global formats
- global patterns
global allpatterns
global translate_scope
global translate_prefix
@@ -1246,19 +1277,30 @@ def main():
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)
+ 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:
- stree = build_size_tree(patterns, 8, 0, 0)
+ for i in toppat.pats:
+ i.prop_width()
+ stree = build_size_tree(toppat.pats, 8, 0, 0)
prop_size(stree)
- dtree = build_tree(patterns, 0, 0)
- prop_format(dtree)
-
if output_file:
output_fd = open(output_file, 'w')
else:
@@ -1316,7 +1358,7 @@ def main():
f = arguments[n]
output(i4, i4, f.struct_name(), ' f_', f.name, ';\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('}\n')