@@ -290,6 +290,9 @@ def str_extract(self, lvalue_formatter):
s = 's' if self.sign else ''
return f'{s}extract{bitop_width}(insn, {self.pos}, {self.len})'
+ def referenced_fields(self):
+ return []
+
def __eq__(self, other):
return self.sign == other.sign and self.mask == other.mask
@@ -321,6 +324,12 @@ def str_extract(self, lvalue_formatter):
pos += f.len
return ret
+ def referenced_fields(self):
+ l = []
+ for f in self.subs:
+ l.extend(f.referenced_fields())
+ return l
+
def __ne__(self, other):
if len(self.subs) != len(other.subs):
return True
@@ -347,6 +356,9 @@ def __str__(self):
def str_extract(self, lvalue_formatter):
return str(self.value)
+ def referenced_fields(self):
+ return []
+
def __cmp__(self, other):
return self.value - other.value
# end ConstField
@@ -367,6 +379,9 @@ def str_extract(self, lvalue_formatter):
return (self.func + '(ctx, '
+ self.base.str_extract(lvalue_formatter) + ')')
+ def referenced_fields(self):
+ return self.base.referenced_fields()
+
def __eq__(self, other):
return self.func == other.func and self.base == other.base
@@ -388,6 +403,9 @@ def __str__(self):
def str_extract(self, lvalue_formatter):
return self.func + '(ctx)'
+ def referenced_fields(self):
+ return []
+
def __eq__(self, other):
return self.func == other.func
@@ -395,6 +413,32 @@ def __ne__(self, other):
return not self.__eq__(other)
# end ParameterField
+class NamedField:
+ """Class representing a field already named in the pattern"""
+ def __init__(self, name, sign, len):
+ self.mask = 0
+ self.sign = sign
+ self.len = len
+ self.name = name
+
+ def __str__(self):
+ return self.name
+
+ def str_extract(self, lvalue_formatter):
+ global bitop_width
+ s = 's' if self.sign else ''
+ lvalue = lvalue_formatter(self.name)
+ return f'{s}extract{bitop_width}({lvalue}, 0, {self.len})'
+
+ def referenced_fields(self):
+ return [self.name]
+
+ def __eq__(self, other):
+ return self.name == other.name
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+# end NamedField
class Arguments:
"""Class representing the extracted fields of a format"""
@@ -418,7 +462,6 @@ def output_def(self):
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):
@@ -432,6 +475,7 @@ def __init__(self, name, lineno, base, fixb, fixm, udfm, fldm, flds, w):
self.fieldmask = fldm
self.fields = flds
self.width = w
+ self.dangling = None
def __str__(self):
return self.name + ' ' + str_match_bits(self.fixedbits, self.fixedmask)
@@ -439,10 +483,51 @@ def __str__(self):
def str1(self, i):
return str_indent(i) + self.__str__()
+ def dangling_references(self):
+ # Return a list of all named references which aren't satisfied
+ # directly by this format/pattern. This will be either:
+ # * a format referring to a field which is specified by the
+ # pattern(s) using it
+ # * a pattern referring to a field which is specified by the
+ # format it uses
+ # * a user error (referring to a field that doesn't exist at all)
+ if self.dangling is None:
+ # Compute this once and cache the answer
+ dangling = []
+ for n, f in self.fields.items():
+ for r in f.referenced_fields():
+ if r not in self.fields:
+ dangling.append(r)
+ self.dangling = dangling
+ return self.dangling
+
def output_fields(self, indent, lvalue_formatter):
+ # We use a topological sort to ensure that any use of NamedField
+ # comes after the initialization of the field it is referencing.
+ graph = {}
for n, f in self.fields.items():
- output(indent, lvalue_formatter(n), ' = ',
- f.str_extract(lvalue_formatter), ';\n')
+ refs = f.referenced_fields()
+ graph[n] = refs
+
+ try:
+ ts = TopologicalSorter(graph)
+ for n in ts.static_order():
+ # We only want to emit assignments for the keys
+ # in our fields list, not for anything that ends up
+ # in the tsort graph only because it was referenced as
+ # a NamedField.
+ try:
+ f = self.fields[n]
+ output(indent, lvalue_formatter(n), ' = ',
+ f.str_extract(lvalue_formatter), ';\n')
+ except KeyError:
+ pass
+ except CycleError as e:
+ # The second element of args is a list of nodes which form
+ # a cycle (there might be others too, but only one is reported).
+ # Pretty-print it to tell the user.
+ cycle = ' => '.join(e.args[1])
+ error(self.lineno, 'field definitions form a cycle: ' + cycle)
# end General
@@ -477,10 +562,36 @@ def output_code(self, i, extracted, outerbits, outermask):
ind = str_indent(i)
arg = self.base.base.name
output(ind, '/* ', self.file, ':', str(self.lineno), ' */\n')
+ # We might have named references in the format that refer to fields
+ # in the pattern, or named references in the pattern that refer
+ # to fields in the format. This affects whether we extract the fields
+ # for the format before or after the ones for the pattern.
+ # For simplicity we don't allow cross references in both directions.
+ # This is also where we catch the syntax error of referring to
+ # a nonexistent field.
+ fmt_refs = self.base.dangling_references()
+ for r in fmt_refs:
+ if r not in self.fields:
+ error(self.lineno, f'format refers to undefined field {r}')
+ pat_refs = self.dangling_references()
+ for r in pat_refs:
+ if r not in self.base.fields:
+ error(self.lineno, f'pattern refers to undefined field {r}')
+ if pat_refs and fmt_refs:
+ error(self.lineno, ('pattern that uses fields defined in format '
+ 'cannot use format that uses fields defined '
+ 'in pattern'))
+ if fmt_refs:
+ # pattern fields first
+ self.output_fields(ind, lambda n: 'u.f_' + arg + '.' + n)
+ assert not extracted, "dangling fmt refs but it was already extracted"
if not extracted:
output(ind, self.base.extract_name(),
'(ctx, &u.f_', arg, ', insn);\n')
- self.output_fields(ind, lambda n: 'u.f_' + arg + '.' + n)
+ if not fmt_refs:
+ # pattern fields last
+ self.output_fields(ind, lambda n: 'u.f_' + arg + '.' + n)
+
output(ind, 'if (', translate_prefix, '_', self.name,
'(ctx, &u.f_', arg, ')) return true;\n')
@@ -626,8 +737,10 @@ 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:
+ # extract the fields now. But don't do it if the format relies
+ # on named fields from the insn pattern, as those won't have
+ # been initialised at this point.
+ if not extracted and self.base and not self.base.dangling_references():
output(ind, self.base.extract_name(),
'(ctx, &u.f_', self.base.base.name, ', insn);\n')
extracted = True
@@ -749,6 +862,7 @@ def parse_field(lineno, name, toks):
"""Parse one instruction field from TOKS at LINENO"""
global fields
global insnwidth
+ global re_C_ident
# A "simple" field will have only one entry;
# a "multifield" will have several.
@@ -763,6 +877,25 @@ def parse_field(lineno, name, toks):
func = func[1]
continue
+ if re.fullmatch(re_C_ident + ':s[0-9]+', t):
+ # Signed named field
+ subtoks = t.split(':')
+ n = subtoks[0]
+ le = int(subtoks[1])
+ f = NamedField(n, True, le)
+ subs.append(f)
+ width += le
+ continue
+ if re.fullmatch(re_C_ident + ':[0-9]+', t):
+ # Unsigned named field
+ subtoks = t.split(':')
+ n = subtoks[0]
+ le = int(subtoks[1])
+ f = NamedField(n, False, le)
+ subs.append(f)
+ width += le
+ continue
+
if re.fullmatch('[0-9]+:s[0-9]+', t):
# Signed field extract
subtoks = t.split(':s')