[clang] [Clang][Sema] Extend test coverage for SVE/SME builtin usage. (PR #156908)
Kerry McLaughlin via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 30 02:29:54 PDT 2025
================
@@ -0,0 +1,538 @@
+#!/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.
+ at 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)
+
+ param_types: List[str] = []
+ if params:
+ # 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):
+ continue
+ if s not in unique:
+ unique.append(s)
+
+ return unique
+
+
+def cc1_args_for_features(features: set[str]) -> str:
+ return " ".join("-target-feature +" + s for s in sorted(features))
+
+
+def sanitise_guard(s: str) -> str:
+ """Rewrite guard strings in a form more suitable for file naming."""
+ replacements = {
+ ",": "_AND_",
+ "|": "_OR_",
+ "(": "_LP_",
+ ")": "_RP_",
+ }
+ for k, v in replacements.items():
+ s = s.replace(k, v)
+
+ # Collapse multiple underscores
+ s = re.sub(r"_+", "_", s)
+ return s.strip("_")
+
+
+def make_filename(prefix: str, ctx: BuiltinContext, ext: str) -> str:
+ parts = [sanitise_guard(ctx.guard), sanitise_guard(ctx.streaming_guard)]
+ sanitised_guard = "___".join(p for p in parts if p)
+
+ if "streaming-only" in ctx.flags:
+ prefix += "_streaming_only"
+ elif "streaming-compatible" in ctx.flags:
+ prefix += "_streaming_compatible"
+ elif "feature-dependent" in ctx.flags:
+ prefix += "_feature_dependent"
+ else:
+ prefix += "_non_streaming_only"
+
+ return f"{prefix}_{sanitised_guard}{ext}"
+
+
+# --- Code Generation --------------------------------------------------------
+
+
+def emit_streaming_guard_run_lines(ctx: BuiltinContext) -> str:
+ """Emit lit RUN lines that will exercise the relevent Sema diagnistics."""
+ run_prefix = "// RUN: %clang_cc1 %s -fsyntax-only -triple aarch64-none-linux-gnu"
+ out: List[str] = []
+
+ # All RUN lines have SVE and SME enabled
+ guard_features = expand_feature_guard(ctx.guard, ctx.flags, "sme")
+ streaming_guard_features = expand_feature_guard(
+ ctx.streaming_guard, ctx.flags, "sve"
+ )
+
+ if "streaming-only" in ctx.flags:
+ assert not guard_features
+ # Generate RUN lines for features only availble to streaming functions
+ for feats in streaming_guard_features:
+ out.append(
+ f"{run_prefix} {cc1_args_for_features(feats)} -verify=streaming-guard"
+ )
+ elif "streaming-compatible" in ctx.flags:
+ assert not guard_features
+ # NOTE: Streaming compatible builtins don't require SVE.
+ # Generate RUN lines for features available to all functions.
+ for feats in expand_feature_guard(ctx.streaming_guard, ctx.flags):
+ out.append(f"{run_prefix} {cc1_args_for_features(feats)} -verify")
+ out.append("// expected-no-diagnostics")
+ elif "feature-dependent" in ctx.flags:
+ assert guard_features and streaming_guard_features
+ combined_features = expand_feature_guard(
+ ctx.guard + "," + ctx.streaming_guard, ctx.flags
+ )
+
+ # Generate RUN lines for features only availble to normal functions
----------------
kmclaughlin-arm wrote:
```suggestion
# Generate RUN lines for features only available to normal functions
```
https://github.com/llvm/llvm-project/pull/156908
More information about the cfe-commits
mailing list