Skip to content

Commit

Permalink
i#6580: AArch64 codec.py: Remove variable reuse in function naming fl…
Browse files Browse the repository at this point in the history
…ow (#6595)

This patch refactors how the script gets hold of the name of generated
functions. Previously there was a pass in opndset_naming() that
overwrote the value of the opndset attribute. This was causing issues
with type checking because this changed the type of an opndset from a
tuple or string to always a string which was flagged as an error when
.split() was used on it.

As part of this change the fall-through code became completely dead code
and has been removed. It was obsoleted originally by the addition of +
masks for the operands.

It was also revealed by this change that several encodings in the codecs
were no longer used and represented less correct versions of other
encodings. They have been removed.

issue: #6580
  • Loading branch information
joshua-warburton authored Jan 29, 2024
1 parent 4f8e12f commit 1d4659e
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 110 deletions.
147 changes: 47 additions & 100 deletions core/ir/aarch64/codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@
N = 32 # bits in an instruction word
ONES = (1 << N) - 1

# Stores instances of FallthroughDecode objects for resolution of overlapping
# encodings.
FALLTHROUGH = dict()

opnd_header = '/* This file was generated by codec.py from opnd_defs.txt and the codec_<version>.txt files. */\n\n'
opcode_header = '/* This file was generated by codec.py from codec_<version>.txt files. */\n\n'

Expand Down Expand Up @@ -87,13 +83,6 @@ def __init__(self, fixed, dsts, srcs, enc_order):
self.srcs = srcs
self.enc_order = enc_order

class FallthroughDecode:
def __init__(self, opcode, opndset='', decode_clause='', decode_function=''):
self.flag_name = opcode + '_fallthrough_flag'
self.opndset = opndset
self.decode_clause = decode_clause
self.decode_function = decode_function

class Pattern:
def __init__(self, pattern, opcode_bits, opnd_bits, high_soft_bits, opcode, opndset, enum, feat):
self.pattern = pattern
Expand All @@ -106,6 +95,7 @@ def __init__(self, pattern, opcode_bits, opnd_bits, high_soft_bits, opcode, opnd
self.opndset = opndset
self.enum = enum
self.feat = feat
self._generated_name = None

def __iter__(self):
for field in (self.opcode_bits, self.opnd_bits, self.opcode, self.opndset):
Expand All @@ -124,16 +114,25 @@ def ignored_bit_mask(self):
def set_bits(self):
return self.opcode_bits | self.high_soft_bits

def set_generated_name(self, name):
"""
Generated names need to take account of all possible patterns,
so can't be simply derived from a single pattern
"""
self._generated_name = name

@property
def generated_name(self):
if self._generated_name is None:
raise Exception("Attempted to use generated name before it was set")
return self._generated_name


def codec_header(isa_version):
return '/* This file was generated by codec.py from codec_%s.txt. */\n\n' % isa_version

def fallthrough_instr_id(opcode, opcode_bits, opnd_bits):
return '%s_%08x_%08x' % (opcode, opcode_bits, opnd_bits)

def generate_opndset_decoders(opndsettab, opndtab):
c = []
c += ['bool {} = false;'.format(opcode.flag_name) for opcode in
FALLTHROUGH.values()]
c += ['\n']
for name in sorted(opndsettab):
opnd_set = opndsettab[name]
Expand All @@ -142,7 +141,7 @@ def generate_opndset_decoders(opndsettab, opndtab):
c += ['static bool',
'decode_opnds%s(uint enc, dcontext_t *dcontext, byte *pc, '
'instr_t *instr, int opcode)' % name, '{']
if dsts + srcs != []:
if dsts or srcs:
vars = (['dst%d' % i for i in range(len(dsts))] +
['src%d' % i for i in range(len(srcs))])
tests = (['!decode_opnd_%s(enc & 0x%08x, opcode, pc, &dst%d)' %
Expand Down Expand Up @@ -189,7 +188,7 @@ def indent_append(text):

not_zero_mask = 0
try:
opnd_set = opndsettab[pattern.opndset]
opnd_set = opndsettab[pattern.generated_name]
for mask in (opndtab[o].non_zero for o in opnd_set.dsts + opnd_set.srcs):
not_zero_mask |= mask
except KeyError:
Expand Down Expand Up @@ -220,20 +219,8 @@ def indent_append(text):
'manually handled in codec.c\'s decode_common().' % pattern.opcode)
else:
indent_append(' ASSERT(0);')
enc_key = fallthrough_instr_id(
pattern.opcode, pattern.opcode_bits, pattern.opnd_bits)
if enc_key in FALLTHROUGH and pattern.opndset == FALLTHROUGH[enc_key].opndset:
indent_append(' %s = true;' % FALLTHROUGH[enc_key].flag_name)
FALLTHROUGH[enc_key].decode_clause = \
'if ((enc & 0x%08x) == 0x%08x && %s == true)' % \
(((1 << N) - 1) & pattern.ignored_bit_mask(), pattern.opcode_bits, \
FALLTHROUGH[enc_key].flag_name)
FALLTHROUGH[enc_key].decode_function = \
'return decode_opnds%s(enc, dc, pc, instr, OP_%s);' % \
(pattern.opndset, pattern.opcode)
else:
indent_append(' return decode_opnds%s(enc, dc, pc, '
'instr, OP_%s);' % (pattern.opndset, pattern.opcode))
indent_append(' return decode_opnds%s(enc, dc, pc, '
'instr, OP_%s);' % (pattern.generated_name, pattern.opcode))
if opc_props[pattern.opcode].nzcv_rw != 'n':
indent_append('}')
return
Expand Down Expand Up @@ -276,9 +263,6 @@ def indent_append(text):
'decoder_' + curr_isa + '(uint enc, dcontext_t *dc, byte *pc, instr_t *instr)',
'{']
gen(c, patterns, 1)
for opcode in FALLTHROUGH.values():
c += [' %s' % opcode.decode_clause]
c += [' %s' % opcode.decode_function]
# Call the next version of the decoder if defined.
if next_isa != '':
c.append(' return decoder_' + next_isa + '(enc, dc, pc, instr);')
Expand All @@ -297,7 +281,7 @@ def find_required(fixed, reordered, i, opndtab):
if opndtab[reordered[j][2]].gen & used & ~known != 0:
req = req + ['%s%d' % (reordered[j][0], reordered[j][1])]
known = known | opndtab[reordered[j][2]].gen
return 'enc' if req == [] else '(enc | %s)' % ' | '.join(req)
return 'enc' if not req else '(%s)' % ' | '.join(['enc'] + req)

def make_enc(n, reordered, f, opndtab):
(ds, i, ot) = reordered[n]
Expand Down Expand Up @@ -362,11 +346,11 @@ def generate_opndset_encoders(opndsettab, opndtab):
def generate_encoder(patterns, opndsettab, opndtab, opc_props, curr_isa, next_isa):
c = []
case = dict()
for p in patterns:
(opcode_bits, opnd_bits, opcode, opndset) = p
if opcode not in case:
case[opcode] = []
case[opcode].append(p)
for pattern in patterns:
try:
case[pattern.opcode].append(pattern)
except KeyError:
case[pattern.opcode] = [pattern]

c += ['static uint',
'encoder_' + curr_isa + '(byte *pc, instr_t *instr, decode_info_t *di)',
Expand All @@ -375,9 +359,8 @@ def generate_encoder(patterns, opndsettab, opndtab, opc_props, curr_isa, next_is
' (void)enc;',
' switch (instr->opcode) {']

def reorder_key(t):
opcode_bits, opnd_bits, opcode, opndset = t
return (opcode, opndset, opcode_bits, opnd_bits)
def pattern_sort_key(p):
return (p.opcode, p.generated_name, p.opcode_bits, p.opnd_bits)

for opcode in sorted(case):
c.append(' case OP_%s:' % opcode)
Expand All @@ -386,26 +369,26 @@ def reorder_key(t):
c.append(' if (!proc_has_feature(FEATURE_%s))' % opc_props[opcode].feat)
c.append(' return ENCFAIL;')
c.append('# endif')
patterns = sorted(case[opcode], key=reorder_key)
patterns = sorted(case[opcode], key=pattern_sort_key)
last_pattern = patterns.pop()
for pattern in patterns:
c.append(' enc = encode_opnds%s(pc, instr, 0x%08x, di);' % (
opnd_stem(pattern.opndset), pattern.set_bits()))
opnd_stem(pattern.generated_name), pattern.set_bits()))
c.append(' if (enc != ENCFAIL)')
c.append(' return enc;')
# Fallthrough to call the next version of the encoder if defined.
if next_isa != '':
c.append(' enc = encode_opnds%s(pc, instr, 0x%08x, di);' % (
last_pattern.opndset, last_pattern.set_bits()))
last_pattern.generated_name, last_pattern.set_bits()))
c.append(' if (enc != ENCFAIL)')
c.append(' return enc;')
c += [' break;']
else:
c.append(' return encode_opnds%s(pc, instr, 0x%08x, di);' % (
last_pattern.opndset, last_pattern.set_bits()))
last_pattern.generated_name, last_pattern.set_bits()))
c += [' }']
# Call the next version of the encoder if defined.
if next_isa != '':
if next_isa:
c += [' return encoder_' + next_isa + '(pc, instr, di);']
else:
c += [' return ENCFAIL;']
Expand Down Expand Up @@ -551,15 +534,15 @@ def __getitem__(self, key):
for line in (l.split('#')[0].strip() for l in file):
if not line:
continue
if not re.match('^[x\?\-\+]{32} +[a-zA-Z_0-9]+$', line):
if not re.match(r'^[x\?\-\+]{32} +[a-zA-Z_0-9]+$', line):
raise Exception('Cannot parse line: %s in %s' % (line, file_msg))
# Syntax: mask opndtype
mask, opndtype = line.split()
if opndtype in opndtab:
raise Exception('Repeated definition of opndtype %s in %s' % (opndtype, file_msg))
opndtab[opndtype] = Opnd(int(re.sub('[x\+]', '1', re.sub('[^x^\+]', '0', mask)), 2),
int(re.sub('\?', '1', re.sub('[^\?]', '0', mask)), 2),
int(re.sub('\+', '1', re.sub('[^\+]', '0', mask)), 2))
opndtab[opndtype] = Opnd(int(re.sub(r'[x\+]', '1', re.sub(r'[^x^\+]', '0', mask)), 2),
int(re.sub(r'\?', '1', re.sub(r'[^\?]', '0', mask)), 2),
int(re.sub(r'\+', '1', re.sub(r'[^\+]', '0', mask)), 2))
except IOError as e:
raise Exception('Unable to read operand definitions file, {}: {}'.format(path, e.strerror))

Expand All @@ -574,7 +557,7 @@ def read_codec_file(path):
for line in (l.split('#')[0].strip() for l in file):
if not line:
continue
if re.match('^[01x\^]{32} +[n|r|w|rw|wr|er|ew]+ +[0-9]+ +[a-zA-Z0-9]* +[a-zA-Z_0-9][a-zA-Z_0-9 \.]*:[a-zA-Z_0-9 \.]*$', line):
if re.match(r'^[01x\^]{32} +[n|r|w|rw|wr|er|ew]+ +[0-9]+ +[a-zA-Z0-9]* +[a-zA-Z_0-9][a-zA-Z_0-9 \.]*:[a-zA-Z_0-9 \.]*$', line):
# Syntax: pattern opcode opndtype* : opndtype*
pattern, nzcv_rw_flag, enum, feat, opcode, args = line.split(None, 5)
dsts, srcs = [a.split() for a in args.split(':')]
Expand All @@ -584,7 +567,7 @@ def read_codec_file(path):
patterns.append(Pattern(pattern, opcode_bits, opnd_bits, high_soft_bits, opcode, (dsts, srcs), enum, feat))
opc_props[opcode] = Opcode(opcode, nzcv_rw_flag, feat)
continue
if re.match('^[01x\^]{32} +[n|r|w|rw|wr|er|ew]+ +[0-9]+ +[a-zA-Z0-9]* +[a-zA-Z_0-9]+ +[a-zA-Z_0-9]+', line):
if re.match(r'^[01x\^]{32} +[n|r|w|rw|wr|er|ew]+ +[0-9]+ +[a-zA-Z0-9]* +[a-zA-Z_0-9]+ +[a-zA-Z_0-9]+', line):
# Syntax: pattern opcode opndset
pattern, nzcv_rw_flag, enum, feat, opcode, opndset = line.split()
opcode_bits = int(re.sub('x', '0', pattern), 2)
Expand Down Expand Up @@ -618,47 +601,14 @@ def consistency_check(patterns, opndtab):
for ot in dsts + srcs:
try:
unhandled_bits &= ~opndtab[ot].gen
except KeyError:
raise Exception('Undefined opndtype %s in:\n%s' %
(opnd_stem(ot), pattern_to_str(*p)))
except KeyError as e :
raise Exception(
'Undefined opndtype %s in:\n%s' %
(opnd_stem(ot), pattern_to_str(*p))) from e
if unhandled_bits:
raise Exception('Unhandled bits:\n%32s in:\n%s' %
(re.sub('1', 'x', re.sub('0', ' ', bin(unhandled_bits)[2:])),
pattern_to_str(*p)))
# Detect and mark overlapping patterns for special handling. Named as
# 'fallthrough' because the special handling is done at the end of the
# decoder's main if/then/else clauses block.
for i, pattern_a in enumerate(patterns):
for pattern_b in patterns[:i]:
non_zero_bits_a = 0
non_zero_bits_b = 0
try:
for opnd in (opndtab[op] for op in pattern_a.all_opnds()):
non_zero_bits_a &= opnd.non_zero
except KeyError:
pass
try:
for opnd in (opndtab[op] for op in pattern_b.all_opnds()):
non_zero_bits_b &= opnd.non_zero
except KeyError:
pass

zero_overlap = (
non_zero_bits_a & pattern_b.opnd_bits == 0 or
non_zero_bits_b & pattern_b.opnd_bits == 0)

if ((pattern_b.opcode_bits ^ pattern_a.opcode_bits) &
~pattern_b.opnd_bits & ~pattern_a.opnd_bits == 0 and
not zero_overlap):
print('Overlap found between:\n%s\nand\n%s' %
(pattern_to_str(*pattern_b),
pattern_to_str(*pattern_a)))
enc_key = fallthrough_instr_id(pattern_a.opcode, pattern_a.opcode_bits, pattern_a.opnd_bits)
if enc_key in FALLTHROUGH:
raise Exception('Error: multiple overlaps detected for '
'%s. Unable to resolve.\n' % enc_key)
print('Resolving overlap.')
FALLTHROUGH[enc_key] = FallthroughDecode(enc_key)

# This function reorders the operands for encoding so that no operand encoder
# requires bits that are generated by an operand encoder that has not yet
Expand Down Expand Up @@ -697,18 +647,15 @@ def opndset_naming(patterns, opndtab):
opndsettab = dict() # maps generated name to original opndsets
for p in patterns:
if type(p.opndset) is str:
new_opndset = '_' + p.opndset
function_name = '_' + p.opndset
else:
(dsts, srcs) = p.opndset
h = (' '.join(dsts), ' '.join(srcs), p.opnd_bits)
new_opndset = 'gen_%08x_%08x' % (opndsets[h], p.opnd_bits)
function_name = 'gen_%08x_%08x' % (opndsets[h], p.opnd_bits)
reordered = reorder_opnds(ONES & ~p.opnd_bits, dsts, srcs, opndtab)
if not new_opndset in opndsettab:
opndsettab[new_opndset] = reordered
p.opndset = new_opndset
enc_key = fallthrough_instr_id(p.opcode, p.opcode_bits, p.opnd_bits)
if enc_key in FALLTHROUGH:
FALLTHROUGH[enc_key].opndset = new_opndset
if not function_name in opndsettab:
opndsettab[function_name] = reordered
p.set_generated_name(function_name)
return (patterns, opndsettab)

def main():
Expand Down
Loading

0 comments on commit 1d4659e

Please sign in to comment.