xref: /llvm-project/clang/utils/convert_arm_neon.py (revision dd3c26a045c081620375a878159f536758baba6e)
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 main, 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
16
17MOD_MAP = {
18    "v": "v",
19    "x": "S",
20    "u": "U",
21    "d": ".",
22    "g": "q",
23    "j": "Q",
24    "w": ">Q",
25    "n": ">",
26    "h": "<",
27    "q": "<Q",
28    "e": "<U",
29    "m": "<q",
30    "i": "I",
31    "l": "IU>",
32    "s": "1",
33    "z": "1<",
34    "r": "1>",
35    "b": "1U",
36    "$": "1S",
37    "k": "Q",
38    "2": "2",
39    "3": "3",
40    "4": "4",
41    "B": "2Q",
42    "C": "3Q",
43    "D": "4Q",
44    "p": "*",
45    "c": "c*",
46    "7": "<<q",
47    "8": "<<",
48    "9": "<<Q",
49    "t": "p",
50}
51
52
53def typespec_elt_size(typespec):
54    if "c" in typespec:
55        return 8
56    elif "s" in typespec or "h" in typespec:
57        return 16
58    elif "i" in typespec or "f" in typespec:
59        return 32
60    elif "l" in typespec or "d" in typespec:
61        return 64
62    elif "k" in typespec:
63        return 128
64
65
66def get_resize(cur, desired):
67    res = ""
68    while cur < desired:
69        res += ">"
70        cur *= 2
71    while cur > desired:
72        res += "<"
73        cur /= 2
74    return res
75
76
77def remap_protocol(proto, typespec, name):
78    key_type = 0
79
80    # Conversions like to see the integer type so they know signedness.
81    if (
82        "vcvt" in name
83        and "_f" in name
84        and name != "vcvt_f32_f64"
85        and name != "vcvt_f64_f32"
86    ):
87        key_type = 1
88    default_width = typespec_elt_size(typespec)
89    inconsistent_width = False
90    for elt in typespec:
91        new_width = typespec_elt_size(elt)
92        if new_width and new_width != default_width:
93            inconsistent_width = True
94
95    res = ""
96    for i, c in enumerate(proto):
97        # void and pointers make for bad discriminators in CGBuiltin.cpp.
98        if c in "vcp":
99            key_type += 1
100
101        if c in MOD_MAP:
102            cur_mod = MOD_MAP[c]
103        elif inconsistent_width:
104            # Otherwise it's a fixed output width modifier.
105            sys.stderr.write(
106                f"warning: {name} uses fixed output size but has inconsistent input widths: {proto} {typespec}\n"
107            )
108
109        if c == "Y":
110            # y: scalar of half float
111            resize = get_resize(default_width, 16)
112            cur_mod = f"1F{resize}"
113        elif c == "y":
114            # y: scalar of float
115            resize = get_resize(default_width, 32)
116            cur_mod = f"1F{resize}"
117        elif c == "o":
118            # o: scalar of double
119            resize = get_resize(default_width, 64)
120            cur_mod = f"1F{resize}"
121        elif c == "I":
122            # I: scalar of 32-bit signed
123            resize = get_resize(default_width, 32)
124            cur_mod = f"1S{resize}"
125        elif c == "L":
126            # L: scalar of 64-bit signed
127            resize = get_resize(default_width, 64)
128            cur_mod = f"1S{resize}"
129        elif c == "U":
130            # I: scalar of 32-bit unsigned
131            resize = get_resize(default_width, 32)
132            cur_mod = f"1U{resize}"
133        elif c == "O":
134            # O: scalar of 64-bit unsigned
135            resize = get_resize(default_width, 64)
136            cur_mod = f"1U{resize}"
137        elif c == "f":
138            # f: float (int args)
139            resize = get_resize(default_width, 32)
140            cur_mod = f"F{resize}"
141        elif c == "F":
142            # F: double (int args)
143            resize = get_resize(default_width, 64)
144            cur_mod = f"F{resize}"
145        elif c == "H":
146            # H: half (int args)
147            resize = get_resize(default_width, 16)
148            cur_mod = f"F{resize}"
149        elif c == "0":
150            # 0: half (int args), ignore 'Q' size modifier.
151            resize = get_resize(default_width, 16)
152            cur_mod = f"Fq{resize}"
153        elif c == "1":
154            # 1: half (int args), force 'Q' size modifier.
155            resize = get_resize(default_width, 16)
156            cur_mod = f"FQ{resize}"
157
158        if len(cur_mod) == 0:
159            raise Exception(f"WTF: {c} in {name}")
160
161        if key_type != 0 and key_type == i:
162            cur_mod += "!"
163
164        if len(cur_mod) == 1:
165            res += cur_mod
166        else:
167            res += "(" + cur_mod + ")"
168
169    return res
170
171
172def replace_insts(m):
173    start, end = m.span("proto")
174    start -= m.start()
175    end -= m.start()
176    new_proto = remap_protocol(m["proto"], m["kinds"], m["name"])
177    return m.group()[:start] + new_proto + m.group()[end:]
178
179
180INST = re.compile(r'Inst<"(?P<name>.*?)",\s*"(?P<proto>.*?)",\s*"(?P<kinds>.*?)"')
181
182new_td = INST.sub(replace_insts, sys.stdin.read())
183sys.stdout.write(new_td)
184