Commit ddfcf007 authored by Florian Hahn's avatar Florian Hahn
Browse files

i#3044 AArch64 SVE: Add SVE support to generation scripts.

Issue: #3044

Change-Id: I8af06df778c203498a5c247bdc19b5c2064a1766
No related merge requests found
Showing with 162 additions and 53 deletions
+162 -53
......@@ -29,6 +29,18 @@
# DAMAGE.
ARG_TO_REG = {
'h0': ('Rd', 'The output register.'),
'h5': ('Rm', 'The first input register.'),
'h16': ('Rn', 'The second input register.'),
'h10': ('Ra', 'The third input register.'),
's0': ('Rd', 'The output register.'),
's5': ('Rm', 'The first input register.'),
's16': ('Rn', 'The second input register.'),
's10': ('Ra', 'The third input register.'),
'd0': ('Rd', 'The output register.'),
'd5': ('Rm', 'The first input register.'),
'd16': ('Rn', 'The second input register.'),
'd10': ('Ra', 'The third input register.'),
'dq0': ('Rd', 'The output register.'),
'dq5': ('Rm', 'The first input register.'),
'dq16': ('Rn', 'The second input register.'),
......@@ -37,17 +49,17 @@ ARG_TO_REG = {
'float_reg5': ('Rm', 'The first input register.'),
'float_reg16': ('Rn', 'The second input register.'),
'float_reg10': ('Ra', 'The third input register.'),
'sd_sz': ('width', 'The vector element width. Use either OPND_CREATE_HALF(), \n * OPND_CREATE_SINGLE() or OPND_CREATE_DOUBLE().'),
'hsd_sz': ('width', 'The vector element width. Use either OPND_CREATE_HALF(), \n * OPND_CREATE_SINGLE() or OPND_CREATE_DOUBLE().'),
'bhsd_sz': ('width', 'The vector element width. Use either OPND_CREATE_BYTE(), OPND_CREATE_HALF(), \n * OPND_CREATE_SINGLE() or OPND_CREATE_DOUBLE().'),
'bhs_sz': ('width', 'The vector element width. Use either OPND_CREATE_BYTE(), OPND_CREATE_HALF(), \n * OPND_CREATE_SINGLE().'),
'hs_sz': ('width', 'The vector element width. Use either OPND_CREATE_HALF(), \n * OPND_CREATE_SINGLE().'),
'b_sz': ('width', 'The vector element width. Use either OPND_CREATE_BYTE().'),
}
TYPE_TO_STR2 = {
'advsimd': 'vector',
'float': 'scalar',
'z0': ('Zd', 'The output SVE vector register.'),
'z5': ('Zm', 'The first input SVE vector register.'),
'z16': ('Zn', 'The second input SVE vector register.'),
'z10': ('Za', 'The third input SVE vector register.'),
'p10_low': ('Pg', 'Predicate register for predicated instruction, P0-P7.'),
'sd_sz': ('width', 'The vector element width. Use either OPND_CREATE_HALF(),\n * OPND_CREATE_SINGLE() or OPND_CREATE_DOUBLE().'),
'hsd_sz': ('width', 'The vector element width. Use either OPND_CREATE_HALF(),\n * OPND_CREATE_SINGLE() or OPND_CREATE_DOUBLE().'),
'bhsd_sz': ('width', 'The vector element width. Use either OPND_CREATE_BYTE(),\n * OPND_CREATE_HALF(), OPND_CREATE_SINGLE() or OPND_CREATE_DOUBLE().'),
'bhs_sz': ('width', 'The vector element width. Use either OPND_CREATE_BYTE(),\n * OPND_CREATE_HALF() or OPND_CREATE_SINGLE().'),
'hs_sz': ('width', 'The vector element width. Use either OPND_CREATE_HALF() or\n * OPND_CREATE_SINGLE().'),
'b_sz': ('width', 'The vector element width. Use OPND_CREATE_BYTE().'),
}
......@@ -60,18 +72,18 @@ def get_doc_comments(enc):
params = [' * \param {}'.format(get_param_comment(p)) for p in enc.outputs] + \
[' * \param {}'.format(get_param_comment(p)) for p in enc.inputs_no_dst()]
if enc.reads_dst():
if enc.reads_dst:
params[0] = params[0] + ' The instruction also reads this register.'
comment = """
/**
* Creates a {} {} instruction.
* \param dc The void * dcontext used to allocate memory for the instr_t.
* \param dc The void * dcontext used to allocate memory for the instr_t.
{}
*/
"""
return comment.format(
enc.mnemonic, enc.type_as_str(), '\n'.join(params))
enc.mnemonic.upper(), enc.type_as_str(), '\n'.join(params))
def get_macro(enc):
......@@ -81,8 +93,8 @@ def get_macro(enc):
args_def = ', '.join(ARG_TO_REG[p][0]
for p in enc.outputs + enc.inputs_no_dst())
return '#define INSTR_CREATE_{}_{}(dc, {}) \\\n{}'.format(
enc.mnemonic, TYPE_TO_STR2[enc.class_info['instr-class']], args_def, call)
return '#define {}(dc, {}) \\\n{}'.format(
enc.get_macro_name() , args_def, call)
def get_macro_string(enc):
......
......@@ -34,12 +34,17 @@ import subprocess
TYPE_TO_STR2 = {
'advsimd': 'vector',
'sve': 'vector',
'float': 'scalar',
}
def generate_size_strs(selected):
def generate_size_strs(selected, enc):
res = []
if enc.is_sve():
if selected == 'bhsd':
return ['$0x00', '$0x01', '$0x02', '$0x03']
assert False
for s in selected:
if s == 'b':
res += ['$0x00', '$0x00']
......@@ -62,6 +67,14 @@ def generate_vreg_strs(selected):
filtered = [(asm, ir) for (asm, ir) in combined if asm[-1] in selected]
return zip(*filtered)
def generate_vreg_sve_strs(selected):
full_asm = ['z{}.b', 'z{}.h', 'z{}.s', 'z{}.d']
full_ir = ['%z{}', '%z{}', '%z{}', '%z{}']
combined = zip(full_asm, full_ir)
filtered = [ (asm, ir) for (asm, ir) in combined if asm[-1] in selected ]
return zip(*filtered)
def generate_reg_strs(op, enc):
"""
......@@ -73,21 +86,28 @@ def generate_reg_strs(op, enc):
different element sizes.
"""
if op.endswith('_sz'):
return [], generate_size_strs(op.replace('_sz', ''))
return [], generate_size_strs(op.replace('_sz', ''), enc)
if not op.startswith('dq') and not op.startswith('float_reg'):
if not op.startswith('dq') and not op.startswith('float_reg') and not op.startswith('z') and not op.startswith('p'):
num = random.randint(0, 31)
return ['{}{}'.format(op[0], num)], ['%{}{}'.format(op[0], num)]
asm = []
ir = []
num = random.randint(0, 31)
if op.startswith('dq'):
temp_asm, temp_ir = generate_vreg_strs(enc.get_selected_sizes())
elif op.startswith('float_reg'):
temp_asm = ['d{}', 's{}', 'h{}']
temp_ir = ['%d{}', '%s{}', '%h{}']
elif op.startswith('z'):
temp_asm, temp_ir = generate_vreg_sve_strs(enc.get_selected_sizes())
elif op.startswith('p10_low'):
num = random.randint(0, 7)
temp_asm = ['p{}/m'] * 4
temp_ir = ['%p{}'] * 4
num = random.randint(0, 31)
for (t_asm, t_ir) in zip(temp_asm, temp_ir):
asm.append(t_asm.format(num))
ir.append(t_ir.format(num))
......@@ -106,25 +126,25 @@ def ir_arg_to_c(ir):
"""
Returns a string to create ir in C.
"""
for (ir_prefix, c) in [('%d', 'D'), ('%q', 'Q'), ('%s', 'S'), ('%h', 'H')]:
for (ir_prefix, c) in [('%d', 'D'), ('%q', 'Q'), ('%s', 'S'), ('%h', 'H'), ('%z', 'Z'), ('%p', 'P')]:
if not ir.startswith(ir_prefix):
continue
return "opnd_create_reg(DR_REG_" + c + ir.replace(ir_prefix, '') + ")"
return SIZE_TO_MACRO[ir]
def generate_api_test(opcode, ir_ops, str_type):
def generate_api_test(enc, ir_ops):
"""
Returns C statements to create an instruction with opcode and ir_ops as
arguments, as well as a call to test_instr_encoding for the generated
instruction.
"""
c_args = [ir_arg_to_c(ir) for ir in ir_ops]
macro_start = ' instr = INSTR_CREATE_{}_{}(dc,'.format(opcode, str_type)
macro_start = ' instr = {}(dc,'.format(enc.get_macro_name())
return '\n'.join(['',
'{}\n{});'.format(macro_start,
',\n'.join((len(macro_start) - 3) * ' ' + arg for arg in c_args)),
' test_instr_encoding(dc, OP_{}, instr);'.format(opcode)])
' test_instr_encoding(dc, OP_{}, instr);'.format(enc.mnemonic)])
def generate_dis_test(mnemonic, asm_ops, ir_str):
......@@ -142,7 +162,7 @@ def generate_dis_test(mnemonic, asm_ops, ir_str):
f.write('\n')
subprocess.check_call(
["gcc", "-march=armv8.3-a", "-c", "-o", "/tmp/autogen.o", "/tmp/autogen.s", ])
["gcc", "-march=armv8.3-a+sve", "-c", "-o", "/tmp/autogen.o", "/tmp/autogen.s", ])
out = subprocess.check_output(["objdump", "-d", "/tmp/autogen.o"])
enc_str = out.decode('utf-8').split('\n')[-2][6:14]
return '{} : {:<40} : {}'.format(enc_str, asm_str, ir_str)
......@@ -168,7 +188,9 @@ def num_combinations_to_test(enc):
szs = [s for s in enc.inputs if s.endswith('sz')]
if len(szs) == 0:
return 2
return len(list(generate_vreg_strs(szs[0].replace('_sz', ''))))
return len(generate_size_strs(szs[0].replace('_sz', ''), enc))
elif enc.is_sve():
return 4
def generate_test_strings(enc):
......@@ -184,7 +206,7 @@ def generate_test_strings(enc):
api_tests_expected = []
num_strs = num_combinations_to_test(enc)
# Requires GCC8....
# Requires GCC8...., so manually add them for now.
if enc.mnemonic in ('fmlal', 'fmlal2', 'fmlsl', 'fmlsl2'):
asm_ops.append(['v2.2s', 'v6.4s'])
output_ir.append(['%d2', '%q6'])
......@@ -196,9 +218,10 @@ def generate_test_strings(enc):
asm, ir = generate_reg_strs(op, enc)
asm_ops.append(asm)
output_ir.append(ir)
if enc.reads_dst():
if enc.reads_dst and not enc.is_sve():
input_ir.append(ir)
for op in enc.inputs:
asm, ir = generate_reg_strs(op, enc)
if op == 'dq0':
......@@ -208,6 +231,13 @@ def generate_test_strings(enc):
if ir:
input_ir.append(ir)
if enc.reads_dst and enc.is_sve():
if 'p10_low' in enc.inputs or 'p10' in enc.inputs:
asm_ops[2] = asm_ops[0]
input_ir[1] = output_ir[0]
else:
assert False
# Above we built tables like
# input_asm = [ [ v1, v4],
# [ v9, v7] ]
......@@ -225,13 +255,12 @@ def generate_test_strings(enc):
test_lines.append(generate_dis_test(enc.mnemonic, asm, ir_str))
if enc.reads_dst():
if enc.reads_dst and not enc.is_sve():
api_in_ir = in_ir[1:]
else:
api_in_ir = in_ir
api_tests.append(generate_api_test(enc.mnemonic, out_ir +
api_in_ir, TYPE_TO_STR2[enc.class_info['instr-class']]))
api_tests.append(generate_api_test(enc, out_ir + api_in_ir))
api_tests_expected.append(ir_str)
return test_lines, api_tests, api_tests_expected
......@@ -70,6 +70,7 @@ def should_print_box(box):
# Subset of types we can handle.
DATATYPE_TO_REG = {
'sve+': 'z',
'advsimd+single-and-double': 'dq',
'advsimd+half': 'dq',
'advsimd+complex': 'dq',
......@@ -120,6 +121,18 @@ def only_b(mnemonic):
def only_h(mnemonic):
return mnemonic in ('fmlal', 'fmlal2', 'fmlsl', 'fmlsl2')
def get_name_suffix(enc):
TYPE_TO_STR2 = {
'advsimd': 'vector',
'float': 'scalar',
'sve': 'sve',
}
if enc.is_sve() and enc.is_predicated():
return 'sve_pred'
return TYPE_TO_STR2[enc.class_info['instr-class']]
@attr.s
class Encoding(object):
......@@ -138,6 +151,13 @@ class Encoding(object):
and box.attrib['name'] != 'type' and box.attrib['name'] != 'size']
relevant_ops = sorted(relevant_ops, key=op_sort_key, reverse=True)
if self.is_sve():
self.reads_dst = 'Zdn' in [op.attrib['name'] for op in relevant_ops]
else:
self.reads_dst = self.mnemonic in (
'fmla', 'fmlal', 'fmlal2', 'fmls', 'fmlsl', 'fmlsl2', 'mla',
'mls')
m = re.match(r'.*_([HSD][HSD])_.*', self.enctags)
if m:
ot, it = m.group(1)[0].lower(), m.group(1)[1].lower()
......@@ -145,21 +165,40 @@ class Encoding(object):
self.inputs = [it + '5']
return
self.inputs = [
self.operand_to_reg_class(op) for op in relevant_ops if op.attrib.get(
'name', '') != 'Rd']
self.inputs = [self.operand_to_reg_class(op)
for op in relevant_ops if not self._is_result_reg(op.attrib.get('name', ''))]
self.outputs = [self.operand_to_reg_class(
op) for op in relevant_ops if op.attrib.get('name', '') == 'Rd']
self.outputs = [self.operand_to_reg_class(op)
for op in relevant_ops if self._is_result_reg(op.attrib.get('name', ''))]
if self.reads_dst():
if self.reads_dst:
assert len(self.outputs) == 1
self.inputs = self.outputs + self.inputs
# Move the predicate register for predicated instructions as first
# input argument.
if 'p10' in self.inputs:
self.inputs.remove('p10')
self.inputs.insert(0, 'p10')
if 'p10_low' in self.inputs:
self.inputs.remove('p10_low')
self.inputs.insert(0, 'p10_low')
sz = self._get_size_op()
if sz:
self.inputs.append(sz)
def is_sve(self):
return self.class_info['instr-class'] == 'sve'
def is_predicated(self):
return 'p10_low' in self.inputs
def _is_result_reg(self, name):
if self.is_sve():
return name in ('Zd', 'Zdn')
return name == 'Rd'
def _get_size_op(self):
if self.class_id in ('asimdsamefp16',):
return 'h_sz'
......@@ -203,17 +242,15 @@ class Encoding(object):
return 'b'
def inputs_no_dst(self):
if self.reads_dst():
if self.reads_dst and not self.is_sve():
return self.inputs[1:]
return self.inputs
def reads_dst(self):
return self.mnemonic in ('fmla', 'fmls',)
def type_as_str(self):
TYPE_TO_STR = {
'advsimd': 'vector',
'float': 'floating point',
'sve': 'scalable vector',
}
return TYPE_TO_STR[self.class_info['instr-class']]
......@@ -233,18 +270,27 @@ class Encoding(object):
prefix = ''
if name == 'rot':
prefix = 'rot'
else:
assert name.startswith('R')
prefix = DATATYPE_TO_REG[get_type_key(self.class_info)]
if name == 'Pg':
# Width 3 means P0-P7 only.
if int(box.attrib['width']) == 3:
return 'p{}_low'.format(bit_start)
assert int(box.attrib['width']) == 3
return 'p{}'.format(bit_start)
assert name.startswith('R') or name.startswith('Z')
prefix = DATATYPE_TO_REG[get_type_key(self.class_info)]
return '{}{}'.format(prefix, bit_start)
def get_macro_name(self):
return 'INSTR_CREATE_{}_{}'.format(self.mnemonic, get_name_suffix(self))
def parse_encoding(enc_row):
name = enc_row.find(
"./td[@class='iformname']").text.split(' ')[0].replace(',', '').lower()
if 'label' in enc_row.attrib:
name = enc_row.attrib['label'].lower()
if 'encname' in enc_row.attrib:
name = enc_row.attrib['encname'].split('_')[0].lower()
bitfields = [f.text for f in enc_row.findall("./td[@class='bitfield']")]
return (name, bitfields, enc_row.attrib['encname'])
......@@ -274,9 +320,23 @@ CLASS_INFOS = {
'instr-class': 'float',
'datatype': 'single',
},
'sve_int_bin_cons_arit_0':{
'instr-class': 'sve',
'datatype': '',
},
'sve_int_bin_pred_log': {
'instr-class': 'sve',
'datatype': '',
},
}
def get_opcode(known, ci):
if ci['instr-class'] == 'sve':
return known['opc']
return known.get('opcode', '')
def parse_instr_class(iclass):
class_id = iclass.attrib['id']
class_info = CLASS_INFOS[class_id]
......@@ -293,16 +353,21 @@ def parse_instr_class(iclass):
encs = []
for (mnemonic, known_vals, label) in encs_xml:
known = {k: v for (k, v) in zip(headers, known_vals)}
if should_ignore(mnemonic) or mnemonic + known['opcode'] in seen:
# For SVE, there is no size entry in the details/info table per encoding
# group....
if class_info['instr-class'] == 'sve':
known['size'] = None
opcode = get_opcode(known, class_info)
if should_ignore(mnemonic) or mnemonic + opcode in seen:
continue
# floatdp2 lists all operand widths (h, s, d) explicitly. We want to
# have a single line with float_reg for them.
if class_id in (
'floatdp1',
'floatdp2',
'floatdp3') and xml[0] != 'fcvt':
seen.add(xml[0] + known['opcode'])
if (class_id in ('floatdp1', 'floatdp2', 'floatdp3') and
mnemonic != 'fcvt'):
seen.add(mnemonic + opcode)
del known['type']
# Those instructions have 0x/1x as their size, but x == 1 is reserved.
......@@ -351,8 +416,11 @@ def main(encindex, only, to_generate):
print("\n".join(line for sl in dis_a64 for line in sl))
print("\n\nir_aarch64.c:")
print("\n/* {} */".format(name))
print("static void\n{}(void *dc)\n{{\n byte *pc; ".format(c.attrib['id']) +
"\n instr_t *instr;\n\n")
print("\n /* {} */".format(name))
print("\n".join(line for sl in api for line in sl))
print("}")
print("\n\nir_aarch64.expect:")
print("\n".join(line for sl in expected for line in sl))
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment