llvm/clang/utils/convert_arm_neon.py

#!/usr/bin/env python3

# This script was committed on 20/11/2019 and it would probably make sense to remove
# it after the next release branches.

# This script is pipe based and converts an arm_neon.td (or arm_fp16.td) file
# using the old single-char type modifiers to an equivalent new-style form where
# each modifier is orthogonal and they can be composed.
#
# It was used to directly generate the .td files on main, so if you have any
# local additions I would suggest implementing any modifiers here, and running
# it over your entire pre-merge .td files rather than trying to resolve any
# conflicts manually.

import re, sys

MOD_MAP = {
    "v": "v",
    "x": "S",
    "u": "U",
    "d": ".",
    "g": "q",
    "j": "Q",
    "w": ">Q",
    "n": ">",
    "h": "<",
    "q": "<Q",
    "e": "<U",
    "m": "<q",
    "i": "I",
    "l": "IU>",
    "s": "1",
    "z": "1<",
    "r": "1>",
    "b": "1U",
    "$": "1S",
    "k": "Q",
    "2": "2",
    "3": "3",
    "4": "4",
    "B": "2Q",
    "C": "3Q",
    "D": "4Q",
    "p": "*",
    "c": "c*",
    "7": "<<q",
    "8": "<<",
    "9": "<<Q",
    "t": "p",
}


def typespec_elt_size(typespec):
    if "c" in typespec:
        return 8
    elif "s" in typespec or "h" in typespec:
        return 16
    elif "i" in typespec or "f" in typespec:
        return 32
    elif "l" in typespec or "d" in typespec:
        return 64
    elif "k" in typespec:
        return 128


def get_resize(cur, desired):
    res = ""
    while cur < desired:
        res += ">"
        cur *= 2
    while cur > desired:
        res += "<"
        cur /= 2
    return res


def remap_protocol(proto, typespec, name):
    key_type = 0

    # Conversions like to see the integer type so they know signedness.
    if (
        "vcvt" in name
        and "_f" in name
        and name != "vcvt_f32_f64"
        and name != "vcvt_f64_f32"
    ):
        key_type = 1
    default_width = typespec_elt_size(typespec)
    inconsistent_width = False
    for elt in typespec:
        new_width = typespec_elt_size(elt)
        if new_width and new_width != default_width:
            inconsistent_width = True

    res = ""
    for i, c in enumerate(proto):
        # void and pointers make for bad discriminators in CGBuiltin.cpp.
        if c in "vcp":
            key_type += 1

        if c in MOD_MAP:
            cur_mod = MOD_MAP[c]
        elif inconsistent_width:
            # Otherwise it's a fixed output width modifier.
            sys.stderr.write(
                f"warning: {name} uses fixed output size but has inconsistent input widths: {proto} {typespec}\n"
            )

        if c == "Y":
            # y: scalar of half float
            resize = get_resize(default_width, 16)
            cur_mod = f"1F{resize}"
        elif c == "y":
            # y: scalar of float
            resize = get_resize(default_width, 32)
            cur_mod = f"1F{resize}"
        elif c == "o":
            # o: scalar of double
            resize = get_resize(default_width, 64)
            cur_mod = f"1F{resize}"
        elif c == "I":
            # I: scalar of 32-bit signed
            resize = get_resize(default_width, 32)
            cur_mod = f"1S{resize}"
        elif c == "L":
            # L: scalar of 64-bit signed
            resize = get_resize(default_width, 64)
            cur_mod = f"1S{resize}"
        elif c == "U":
            # I: scalar of 32-bit unsigned
            resize = get_resize(default_width, 32)
            cur_mod = f"1U{resize}"
        elif c == "O":
            # O: scalar of 64-bit unsigned
            resize = get_resize(default_width, 64)
            cur_mod = f"1U{resize}"
        elif c == "f":
            # f: float (int args)
            resize = get_resize(default_width, 32)
            cur_mod = f"F{resize}"
        elif c == "F":
            # F: double (int args)
            resize = get_resize(default_width, 64)
            cur_mod = f"F{resize}"
        elif c == "H":
            # H: half (int args)
            resize = get_resize(default_width, 16)
            cur_mod = f"F{resize}"
        elif c == "0":
            # 0: half (int args), ignore 'Q' size modifier.
            resize = get_resize(default_width, 16)
            cur_mod = f"Fq{resize}"
        elif c == "1":
            # 1: half (int args), force 'Q' size modifier.
            resize = get_resize(default_width, 16)
            cur_mod = f"FQ{resize}"

        if len(cur_mod) == 0:
            raise Exception(f"WTF: {c} in {name}")

        if key_type != 0 and key_type == i:
            cur_mod += "!"

        if len(cur_mod) == 1:
            res += cur_mod
        else:
            res += "(" + cur_mod + ")"

    return res


def replace_insts(m):
    start, end = m.span("proto")
    start -= m.start()
    end -= m.start()
    new_proto = remap_protocol(m["proto"], m["kinds"], m["name"])
    return m.group()[:start] + new_proto + m.group()[end:]


INST = re.compile(r'Inst<"(?P<name>.*?)",\s*"(?P<proto>.*?)",\s*"(?P<kinds>.*?)"')

new_td = INST.sub(replace_insts, sys.stdin.read())
sys.stdout.write(new_td)