================ @@ -0,0 +1,539 @@ +#!/usr/bin/env python3 +""" +Generate C test files that call ACLE builtins found in a JSON manifest. + +Expected JSON input format (array of objects): +[ + { + "guard": "sve,(sve2p1|sme)", + "streaming_guard": "sme", + "flags": "feature-dependent", + "builtin": "svint16_t svrevd_s16_z(svbool_t, svint16_t);" + }, + ... +] +""" + +from __future__ import annotations + +import argparse +import json +import re +import sys +from collections import defaultdict +from dataclasses import dataclass +from enum import Enum +from itertools import product +from pathlib import Path +from typing import Any, Dict, Iterable, List, Sequence, Tuple + +assert sys.version_info >= (3, 7), "Only Python 3.7+ is supported." + + +# Are we testing arm_sve.h or arm_sme.h based builtins. +class Mode(Enum): + SVE = "sve" + SME = "sme" + + +class FunctionType(Enum): + NORMAL = "normal" + STREAMING = "streaming" + STREAMING_COMPATIBLE = "streaming-compatible" + + +# Builtins are grouped by their required features. +@dataclass(frozen=True, order=True) +class BuiltinContext: + guard: str + streaming_guard: str + flags: tuple[str, ...] + + def __str__(self) -> str: + return ( + f"// Properties: " + f'guard="{self.guard}" ' + f'streaming_guard="{self.streaming_guard}" ' + f'flags="{",".join(self.flags)}"' + ) + + @classmethod + def from_json(cls, obj: dict[str, Any]) -> "BuiltinContext": + flags = tuple(p.strip() for p in obj["flags"].split(",") if p.strip()) + return cls(obj["guard"], obj["streaming_guard"], flags) + + +# --- Parsing builtins ------------------------------------------------------- + +# Captures the full function *declaration* inside the builtin string, e.g.: +# "svint16_t svrevd_s16_z(svbool_t, svint16_t);" +# group(1) => "svint16_t svrevd_s16_z" +# group(2) => "svbool_t, svint16_t" +FUNC_RE = re.compile(r"^\s*([a-zA-Z_][\w\s\*]*[\w\*])\s*\(\s*([^)]*)\s*\)\s*;\s*$") + +# Pulls the final word out of the left side (the function name). +NAME_RE = re.compile(r"([a-zA-Z_][\w]*)\s*$") + + +def parse_builtin_declaration(decl: str) -> Tuple[str, List[str]]: + """Return (func_name, param_types) from a builtin declaration string. + + Example: + decl = "svint16_t svrevd_s16_z(svbool_t, svint16_t);" + => ("svrevd_s16_z", ["svbool_t", "svint16_t"]) + """ + m = FUNC_RE.match(decl) + if not m: + raise ValueError(f"Unrecognized builtin declaration syntax: {decl!r}") + + left = m.group(1) # return type + name + params = m.group(2).strip() + + name_m = NAME_RE.search(left) + if not name_m: + raise ValueError(f"Could not find function name in: {decl!r}") + func_name = name_m.group(1) + + if not params: + param_types: List[str] = [] + else: + # Split by commas respecting no pointers/arrays with commas (not expected here) + param_types = [p.strip() for p in params.split(",") if p.strip()] + + return func_name, param_types + + +# --- Variable synthesis ----------------------------------------------------- + +# Pick a safe (ideally non-zero) value for literal types +LITERAL_TYPES_MAP: dict[str, str] = { + "ImmCheck0_0": "0", + "ImmCheck0_1": "1", + "ImmCheck0_2": "2", + "ImmCheck0_3": "2", + "ImmCheck0_7": "2", + "ImmCheck0_13": "2", + "ImmCheck0_15": "2", + "ImmCheck0_31": "2", + "ImmCheck0_63": "2", + "ImmCheck0_255": "2", + "ImmCheck1_1": "1", + "ImmCheck1_3": "2", + "ImmCheck1_7": "2", + "ImmCheck1_16": "2", + "ImmCheck1_32": "2", + "ImmCheck1_64": "2", + "ImmCheck2_4_Mul2": "2", + "ImmCheckComplexRot90_270": "90", + "ImmCheckComplexRotAll90": "90", + "ImmCheckCvt": "2", + "ImmCheckExtract": "2", + "ImmCheckLaneIndexCompRotate": "1", + "ImmCheckLaneIndexDot": "1", + "ImmCheckLaneIndex": "1", + "ImmCheckShiftLeft": "2", + "ImmCheckShiftRightNarrow": "2", + "ImmCheckShiftRight": "2", + "enum svpattern": "SV_MUL3", + "enum svprfop": "SV_PSTL1KEEP", + "void": "", +} + + +def make_arg_for_type(ty: str) -> Tuple[str, str]: + """Return (var_decl, var_use) for a parameter type. + + Literal types return an empty declaration and a value that will be accepted + by clang's semantic literal validation. + """ + # Compress whitespace and remove non-relevant qualifiers. + ty = re.sub(r"\s+", " ", ty.strip()).replace(" const", "") + if ty in LITERAL_TYPES_MAP: + return "", LITERAL_TYPES_MAP[ty] + + name = ty.replace(" ", "_").replace("*", "ptr") + "_val" + return f"{ty} {name};", name + + +# NOTE: Parsing is limited to the minimum required for guard strings. +# Specifically the expected input is of the form: +# feat1,feat2,...(feat3 | feat4 | ...),... +def expand_feature_guard( + guard: str, flags: Sequence[str], base_feature: str = "" +) -> list[set[str]]: + """ + Expand a guard expression where ',' = AND and '|' = OR, with parentheses + grouping OR-expressions. Returns a list of feature sets. + """ + if not guard: + return [] + + parts = re.split(r",(?![^(]*\))", guard) + + choices_per_part = [] + for part in parts: + if part.startswith("(") and part.endswith(")"): + choices_per_part.append(part[1:-1].split("|")) + else: + choices_per_part.append([part]) + + # Add feature that is common to all + if base_feature: + choices_per_part.append([base_feature]) + + if "requires-zt" in flags: + choices_per_part.append(["sme2"]) + + # construct list of feature sets + results = [] + for choice in product(*choices_per_part): + choice_set = set(choice) + results.append(choice_set) + + # remove superset and duplicates + unique = [] + for s in results: + if any(s > other for other in results if s is not other): ---------------- huntergr-arm wrote:
nit: `>` explicitly rejects the same set while testing for a superset, so the `if s is not other` isn't needed at the end. https://github.com/llvm/llvm-project/pull/156908 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits