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>
This commit is contained in:
Richard Henderson 2020-05-17 10:14:11 -07:00
parent b44b3449a0
commit 08561fc128

View File

@ -31,7 +31,6 @@ variablewidth = False
fields = {} fields = {}
arguments = {} arguments = {}
formats = {} formats = {}
patterns = []
allpatterns = [] allpatterns = []
anyextern = False anyextern = False
@ -371,16 +370,27 @@ 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 a set of instruction patterns""" """Class representing a set of instruction patterns"""
def __init__(self, lineno, pats): 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 = 0 self.fixedbits = 0
self.fixedmask = 0 self.fixedmask = 0
@ -396,22 +406,63 @@ class MultiPattern(General):
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 # end MultiPattern
class IncMultiPattern(MultiPattern): class IncMultiPattern(MultiPattern):
"""Class representing an overlapping set of instruction patterns""" """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): def output_code(self, i, extracted, outerbits, outermask):
global translate_prefix global translate_prefix
ind = str_indent(i) ind = str_indent(i)
@ -431,6 +482,153 @@ class IncMultiPattern(MultiPattern):
#end IncMultiPattern #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):
"""Parse one instruction field from TOKS at LINENO""" """Parse one instruction field from TOKS at LINENO"""
global fields global fields
@ -587,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
@ -749,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.
@ -769,61 +968,16 @@ def parse_generic(lineno, is_format, name, toks):
.format(allbits ^ insnmask)) .format(allbits ^ insnmask))
# end parse_general # end parse_general
def build_incmulti_pattern(lineno, pats):
"""Validate the Patterns going into a IncMultiPattern."""
global patterns
global insnmask
if len(pats) < 2: def parse_file(f, parent_pat):
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):
"""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
@ -868,16 +1022,20 @@ def parse_file(f):
# End nesting? # End nesting?
if name == '}': if 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')
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 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_incmulti_pattern(lineno, pats)
toks = [] toks = []
continue continue
@ -889,8 +1047,12 @@ def parse_file(f):
if name == '{': if 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 = [] nested_pat = IncMultiPattern(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
@ -901,121 +1063,13 @@ 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 = []
# 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:
# 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 SizeTree:
"""Class representing a node in a size decode tree""" """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 # 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"""
@ -1199,7 +1231,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
@ -1246,18 +1277,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')
@ -1316,7 +1358,7 @@ def main():
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')