aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/devel/decodetree.rst58
-rwxr-xr-xscripts/decodetree.py165
-rw-r--r--tests/decode/err_pattern_group_overlap1.decode6
3 files changed, 207 insertions, 22 deletions
diff --git a/docs/devel/decodetree.rst b/docs/devel/decodetree.rst
index 62cb7f687c..44ac621ea8 100644
--- a/docs/devel/decodetree.rst
+++ b/docs/devel/decodetree.rst
@@ -161,3 +161,61 @@ which will, in part, invoke::
and::
trans_addl_i(ctx, &arg_opi, insn)
+
+Pattern Groups
+==============
+
+Syntax::
+
+ group := '{' ( pat_def | group )+ '}'
+
+A *group* begins with a lone open-brace, with all subsequent lines
+indented two spaces, and ending with a lone close-brace. Groups
+may be nested, increasing the required indentation of the lines
+within the nested group to two spaces per nesting level.
+
+Unlike ungrouped patterns, grouped patterns are allowed to overlap.
+Conflicts are resolved by selecting the patterns in order. If all
+of the fixedbits for a pattern match, its translate function will
+be called. If the translate function returns false, then subsequent
+patterns within the group will be matched.
+
+The following example from PA-RISC shows specialization of the *or*
+instruction::
+
+ {
+ {
+ nop 000010 ----- ----- 0000 001001 0 00000
+ copy 000010 00000 r1:5 0000 001001 0 rt:5
+ }
+ or 000010 rt2:5 r1:5 cf:4 001001 0 rt:5
+ }
+
+When the *cf* field is zero, the instruction has no side effects,
+and may be specialized. When the *rt* field is zero, the output
+is discarded and so the instruction has no effect. When the *rt2*
+field is zero, the operation is ``reg[rt] | 0`` and so encodes
+the canonical register copy operation.
+
+The output from the generator might look like::
+
+ switch (insn & 0xfc000fe0) {
+ case 0x08000240:
+ /* 000010.. ........ ....0010 010..... */
+ if ((insn & 0x0000f000) == 0x00000000) {
+ /* 000010.. ........ 00000010 010..... */
+ if ((insn & 0x0000001f) == 0x00000000) {
+ /* 000010.. ........ 00000010 01000000 */
+ extract_decode_Fmt_0(&u.f_decode0, insn);
+ if (trans_nop(ctx, &u.f_decode0)) return true;
+ }
+ if ((insn & 0x03e00000) == 0x00000000) {
+ /* 00001000 000..... 00000010 010..... */
+ extract_decode_Fmt_1(&u.f_decode1, insn);
+ if (trans_copy(ctx, &u.f_decode1)) return true;
+ }
+ }
+ extract_decode_Fmt_2(&u.f_decode2, insn);
+ if (trans_or(ctx, &u.f_decode2)) return true;
+ return false;
+ }
diff --git a/scripts/decodetree.py b/scripts/decodetree.py
index cc5fa1a8ab..2711c6ca9e 100755
--- a/scripts/decodetree.py
+++ b/scripts/decodetree.py
@@ -31,6 +31,7 @@ fields = {}
arguments = {}
formats = {}
patterns = []
+allpatterns = []
translate_prefix = 'trans'
translate_scope = 'static '
@@ -300,13 +301,7 @@ class General:
self.fields = flds
def __str__(self):
- r = self.name
- if self.base:
- r = r + ' ' + self.base.name
- else:
- r = r + ' ' + str(self.fields)
- r = r + ' ' + str_match_bits(self.fixedbits, self.fixedmask)
- return r
+ return self.name + ' ' + str_match_bits(self.fixedbits, self.fixedmask)
def str1(self, i):
return str_indent(i) + self.__str__()
@@ -353,6 +348,47 @@ class Pattern(General):
# end Pattern
+class MultiPattern(General):
+ """Class representing an overlapping set of instruction patterns"""
+
+ def __init__(self, lineno, pats, fixb, fixm, udfm):
+ self.file = input_file
+ self.lineno = lineno
+ self.pats = pats
+ self.base = None
+ self.fixedbits = fixb
+ self.fixedmask = fixm
+ self.undefmask = udfm
+
+ def __str__(self):
+ r = "{"
+ for p in self.pats:
+ r = r + ' ' + str(p)
+ return r + "}"
+
+ def output_decl(self):
+ for p in self.pats:
+ p.output_decl()
+
+ 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 MultiPattern
+
+
def parse_field(lineno, name, toks):
"""Parse one instruction field from TOKS at LINENO"""
global fields
@@ -505,6 +541,7 @@ def parse_generic(lineno, is_format, name, toks):
global arguments
global formats
global patterns
+ global allpatterns
global re_ident
global insnwidth
global insnmask
@@ -649,6 +686,7 @@ def parse_generic(lineno, is_format, name, toks):
pat = Pattern(name, lineno, fmt, fixedbits, fixedmask,
undefmask, fieldmask, flds)
patterns.append(pat)
+ allpatterns.append(pat)
# Validate the masks that we have assembled.
if fieldmask & fixedmask:
@@ -667,17 +705,66 @@ def parse_generic(lineno, is_format, name, toks):
.format(allbits ^ insnmask))
# end parse_general
+def build_multi_pattern(lineno, pats):
+ """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
+
+ 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)
+ patterns.append(mp)
+# end build_multi_pattern
def parse_file(f):
"""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 = []
+
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:
@@ -687,10 +774,18 @@ def parse_file(f):
if len(toks) != 0:
# Next line after continuation
toks.extend(t)
- elif len(t) == 0:
- # Empty line
- continue
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?
@@ -698,21 +793,47 @@ def parse_file(f):
toks.pop()
continue
- if len(toks) < 2:
- error(lineno, 'short line')
-
name = toks[0]
del toks[0]
+ # 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')
+ nesting -= 2
+ if indent != nesting:
+ error(start_lineno, 'indentation ', indent, ' != ', nesting)
+ pats = patterns
+ patterns = saved_pats.pop()
+ build_multi_pattern(lineno, pats)
+ toks = []
+ continue
+
+ # Everything else should have current indentation.
+ if indent != nesting:
+ error(start_lineno, 'indentation ', indent, ' != ', nesting)
+
+ # Start nesting?
+ if name == '{':
+ if len(toks) != 0:
+ error(start_lineno, 'extra tokens after open brace')
+ saved_pats.append(patterns)
+ patterns = []
+ nesting += 2
+ toks = []
+ continue
+
# Determine the type of object needing to be parsed.
if name[0] == '%':
- parse_field(lineno, name[1:], toks)
+ parse_field(start_lineno, name[1:], toks)
elif name[0] == '&':
- parse_arguments(lineno, name[1:], toks)
+ parse_arguments(start_lineno, name[1:], toks)
elif name[0] == '@':
- parse_generic(lineno, True, name[1:], toks)
+ parse_generic(start_lineno, True, name[1:], toks)
else:
- parse_generic(lineno, False, name, toks)
+ parse_generic(start_lineno, False, name, toks)
toks = []
# end parse_file
@@ -789,11 +910,10 @@ def build_tree(pats, outerbits, outermask):
innermask &= i.fixedmask
if innermask == 0:
- pnames = []
+ text = 'overlapping patterns:'
for p in pats:
- pnames.append(p.name + ':' + p.file + ':' + str(p.lineno))
- error_with_file(pats[0].file, pats[0].lineno,
- 'overlapping patterns:', pnames)
+ text += '\n' + p.file + ':' + str(p.lineno) + ': ' + str(p)
+ error_with_file(pats[0].file, pats[0].lineno, text)
fullmask = outermask | innermask
@@ -846,6 +966,7 @@ def main():
global arguments
global formats
global patterns
+ global allpatterns
global translate_scope
global translate_prefix
global output_fd
@@ -907,7 +1028,7 @@ def main():
# Make sure that the argument sets are the same, and declare the
# function only once.
out_pats = {}
- for i in patterns:
+ for i in allpatterns:
if i.name in out_pats:
p = out_pats[i.name]
if i.base.base != p.base.base:
diff --git a/tests/decode/err_pattern_group_overlap1.decode b/tests/decode/err_pattern_group_overlap1.decode
new file mode 100644
index 0000000000..ebe3030d26
--- /dev/null
+++ b/tests/decode/err_pattern_group_overlap1.decode
@@ -0,0 +1,6 @@
+one 00000000000000000000000000000000
+{
+ two 0000000000000000000000000000000 s:1
+ three 000000000000000000000000000000 s:1 0
+}
+