1#!/usr/bin/env python3
2
3# This script was committed on 20/11/2019 and it would probably make sense to remove
4# it after the next release branches.
5
6# This script is pipe based and converts an arm_neon.td (or arm_fp16.td) file
7# using the old single-char type modifiers to an equivalent new-style form where
8# each modifier is orthogonal and they can be composed.
9#
10# It was used to directly generate the .td files on master, so if you have any
11# local additions I would suggest implementing any modifiers here, and running
12# it over your entire pre-merge .td files rather than trying to resolve any
13# conflicts manually.
14
15import re, sys
16MOD_MAP = {
17    'v': 'v',
18    'x': 'S',
19    'u': 'U',
20    'd': '.',
21    'g': 'q',
22    'j': 'Q',
23    'w': '>Q',
24    'n': '>',
25    'h': '<',
26    'q': '<Q',
27    'e': '<U',
28    'm': '<q',
29    'i': 'I',
30    'l': 'IU>',
31    's': '1',
32    'z': '1<',
33    'r': '1>',
34    'b': '1U',
35    '$': '1S',
36    'k': 'Q',
37    '2': '2',
38    '3': '3',
39    '4': '4',
40    'B': '2Q',
41    'C': '3Q',
42    'D': '4Q',
43    'p': '*',
44    'c': 'c*',
45    '7': '<<q',
46    '8': '<<',
47    '9': '<<Q',
48    't': 'p'
49    }
50
51
52def typespec_elt_size(typespec):
53    if 'c' in typespec:
54        return 8
55    elif 's' in typespec or 'h' in typespec:
56        return 16
57    elif 'i' in typespec or 'f' in typespec:
58        return 32
59    elif 'l' in typespec or 'd' in typespec:
60        return 64
61    elif 'k' in typespec:
62        return 128
63
64def get_resize(cur, desired):
65    res = ''
66    while cur < desired:
67        res += '>'
68        cur *= 2
69    while cur > desired:
70        res += '<'
71        cur /= 2
72    return res
73
74
75def remap_protocol(proto, typespec, name):
76    key_type = 0
77
78    # Conversions like to see the integer type so they know signedness.
79    if 'vcvt' in name and '_f' in name and name != 'vcvt_f32_f64' and name != 'vcvt_f64_f32':
80        key_type = 1
81    default_width = typespec_elt_size(typespec)
82    inconsistent_width = False
83    for elt in typespec:
84        new_width = typespec_elt_size(elt)
85        if new_width and new_width != default_width:
86            inconsistent_width = True
87
88    res = ''
89    for i, c in enumerate(proto):
90        # void and pointers make for bad discriminators in CGBuiltin.cpp.
91        if c in 'vcp':
92                key_type += 1
93
94        if c in MOD_MAP:
95            cur_mod = MOD_MAP[c]
96        elif inconsistent_width:
97            # Otherwise it's a fixed output width modifier.
98            sys.stderr.write(f'warning: {name} uses fixed output size but has inconsistent input widths: {proto} {typespec}\n')
99
100        if c == 'Y':
101            # y: scalar of half float
102            resize = get_resize(default_width, 16)
103            cur_mod = f'1F{resize}'
104        elif c == 'y':
105            # y: scalar of float
106            resize = get_resize(default_width, 32)
107            cur_mod = f'1F{resize}'
108        elif c == 'o':
109            # o: scalar of double
110            resize = get_resize(default_width, 64)
111            cur_mod = f'1F{resize}'
112        elif c == 'I':
113            # I: scalar of 32-bit signed
114            resize = get_resize(default_width, 32)
115            cur_mod = f'1S{resize}'
116        elif c == 'L':
117            # L: scalar of 64-bit signed
118            resize = get_resize(default_width, 64)
119            cur_mod = f'1S{resize}'
120        elif c == 'U':
121            # I: scalar of 32-bit unsigned
122            resize = get_resize(default_width, 32)
123            cur_mod = f'1U{resize}'
124        elif c == 'O':
125            # O: scalar of 64-bit unsigned
126            resize = get_resize(default_width, 64)
127            cur_mod = f'1U{resize}'
128        elif c == 'f':
129            # f: float (int args)
130            resize = get_resize(default_width, 32)
131            cur_mod = f'F{resize}'
132        elif c == 'F':
133            # F: double (int args)
134            resize = get_resize(default_width, 64)
135            cur_mod = f'F{resize}'
136        elif c == 'H':
137            # H: half (int args)
138            resize = get_resize(default_width, 16)
139            cur_mod = f'F{resize}'
140        elif c == '0':
141            # 0: half (int args), ignore 'Q' size modifier.
142            resize = get_resize(default_width, 16)
143            cur_mod = f'Fq{resize}'
144        elif c == '1':
145            # 1: half (int args), force 'Q' size modifier.
146            resize = get_resize(default_width, 16)
147            cur_mod = f'FQ{resize}'
148
149        if len(cur_mod) == 0:
150            raise Exception(f'WTF: {c} in {name}')
151
152        if key_type != 0 and key_type == i:
153            cur_mod += '!'
154
155        if len(cur_mod) == 1:
156            res += cur_mod
157        else:
158            res += '(' + cur_mod + ')'
159
160    return res
161
162def replace_insts(m):
163    start, end = m.span('proto')
164    start -= m.start()
165    end -= m.start()
166    new_proto = remap_protocol(m['proto'], m['kinds'], m['name'])
167    return m.group()[:start] + new_proto + m.group()[end:]
168
169INST = re.compile(r'Inst<"(?P<name>.*?)",\s*"(?P<proto>.*?)",\s*"(?P<kinds>.*?)"')
170
171new_td = INST.sub(replace_insts, sys.stdin.read())
172sys.stdout.write(new_td)
173