26
26
from pathlib import Path
27
27
from typing import Dict , Iterable , List , Sequence , Tuple
28
28
29
+
29
30
# Are we testing arm_sve.h or arm_sme.h based builtins.
30
31
class Mode (Enum ):
31
32
SVE = "sve"
32
33
SME = "sme"
33
34
35
+
34
36
class FunctionType (Enum ):
35
37
NORMAL = "normal"
36
38
STREAMING = "streaming"
37
39
STREAMING_COMPATIBLE = "streaming-compatible"
38
40
41
+
39
42
# Builtins are grouped by their required features.
40
43
@dataclass (frozen = True , order = True , slots = True )
41
44
class BuiltinContext :
@@ -44,16 +47,19 @@ class BuiltinContext:
44
47
flags : tuple [str , ...]
45
48
46
49
def __str__ (self ) -> str :
47
- return (f'// Properties: '
48
- f'guard="{ self .guard } " '
49
- f'streaming_guard="{ self .streaming_guard } " '
50
- f'flags="{ "," .join (self .flags )} "' )
50
+ return (
51
+ f"// Properties: "
52
+ f'guard="{ self .guard } " '
53
+ f'streaming_guard="{ self .streaming_guard } " '
54
+ f'flags="{ "," .join (self .flags )} "'
55
+ )
51
56
52
57
@classmethod
53
58
def from_json (cls , obj : dict [str , Any ]) -> "BuiltinContext" :
54
59
flags = tuple (p .strip () for p in obj ["flags" ].split ("," ) if p .strip ())
55
60
return cls (obj ["guard" ], obj ["streaming_guard" ], flags )
56
61
62
+
57
63
# --- Parsing builtins -------------------------------------------------------
58
64
59
65
# Captures the full function *declaration* inside the builtin string, e.g.:
@@ -65,6 +71,7 @@ def from_json(cls, obj: dict[str, Any]) -> "BuiltinContext":
65
71
# Pulls the final word out of the left side (the function name).
66
72
NAME_RE = re .compile (r"([a-zA-Z_][\w]*)\s*$" )
67
73
74
+
68
75
def parse_builtin_declaration (decl : str ) -> Tuple [str , List [str ]]:
69
76
"""Return (func_name, param_types) from a builtin declaration string.
70
77
@@ -88,10 +95,11 @@ def parse_builtin_declaration(decl: str) -> Tuple[str, List[str]]:
88
95
param_types : List [str ] = []
89
96
else :
90
97
# Split by commas respecting no pointers/arrays with commas (not expected here)
91
- param_types = [p .strip () for p in params .split (',' ) if p .strip ()]
98
+ param_types = [p .strip () for p in params .split ("," ) if p .strip ()]
92
99
93
100
return func_name , param_types
94
101
102
+
95
103
# --- Variable synthesis -----------------------------------------------------
96
104
97
105
# Pick a safe (ideally non-zero) value for literal types
@@ -125,8 +133,9 @@ def parse_builtin_declaration(decl: str) -> Tuple[str, List[str]]:
125
133
"ImmCheckShiftRight" : "2" ,
126
134
"enum svpattern" : "SV_MUL3" ,
127
135
"enum svprfop" : "SV_PSTL1KEEP" ,
128
- "void" : ""
129
- }
136
+ "void" : "" ,
137
+ }
138
+
130
139
131
140
def make_arg_for_type (ty : str ) -> Tuple [str , str ]:
132
141
"""Return (var_decl, var_use) for a parameter type.
@@ -142,18 +151,21 @@ def make_arg_for_type(ty: str) -> Tuple[str, str]:
142
151
name = ty .replace (" " , "_" ).replace ("*" , "ptr" ) + "_val"
143
152
return f"{ ty } { name } ;" , name
144
153
154
+
145
155
# NOTE: Parsing is limited to the minimum required for guard strings.
146
156
# Specifically the expected input is of the form:
147
157
# feat1,feat2,...(feat3 | feat4 | ...),...
148
- def expand_feature_guard (guard : str , flags : str , base_feature : str = None ) -> list [set [str ]]:
158
+ def expand_feature_guard (
159
+ guard : str , flags : str , base_feature : str = None
160
+ ) -> list [set [str ]]:
149
161
"""
150
162
Expand a guard expression where ',' = AND and '|' = OR, with parentheses
151
163
grouping OR-expressions. Returns a list of feature sets.
152
164
"""
153
165
if not guard :
154
- return [];
166
+ return []
155
167
156
- parts = re .split (r' ,(?![^(]*\))' , guard )
168
+ parts = re .split (r" ,(?![^(]*\))" , guard )
157
169
158
170
choices_per_part = []
159
171
for part in parts :
@@ -185,9 +197,11 @@ def expand_feature_guard(guard: str, flags: str, base_feature: str = None) -> li
185
197
186
198
return unique
187
199
200
+
188
201
def cc1_args_for_features (features : set [str ]) -> str :
189
202
return " " .join ("-target-feature +" + s for s in sorted (features ))
190
203
204
+
191
205
def sanitise_guard (s : str ) -> str :
192
206
"""Rewrite guard strings in a form more suitable for file naming."""
193
207
replacements = {
@@ -203,6 +217,7 @@ def sanitise_guard(s: str) -> str:
203
217
s = re .sub (r"_+" , "_" , s )
204
218
return s .strip ("_" )
205
219
220
+
206
221
def make_filename (prefix : str , ctx : BuiltinContext , ext : str ) -> str :
207
222
parts = [sanitise_guard (ctx .guard ), sanitise_guard (ctx .streaming_guard )]
208
223
sanitised_guard = "___" .join (p for p in parts if p )
@@ -218,22 +233,28 @@ def make_filename(prefix: str, ctx: BuiltinContext, ext: str) -> str:
218
233
219
234
return f"{ prefix } _{ sanitised_guard } { ext } "
220
235
236
+
221
237
# --- Code Generation --------------------------------------------------------
222
238
239
+
223
240
def emit_streaming_guard_run_lines (ctx : BuiltinContext ) -> str :
224
241
"""Emit lit RUN lines that will exercise the relevent Sema diagnistics."""
225
242
run_prefix = "// RUN: %clang_cc1 %s -fsyntax-only -triple aarch64-none-linux-gnu"
226
243
out : List [str ] = []
227
244
228
245
# All RUN lines have SVE and SME enabled
229
246
guard_features = expand_feature_guard (ctx .guard , ctx .flags , "sme" )
230
- streaming_guard_features = expand_feature_guard (ctx .streaming_guard , ctx .flags , "sve" )
247
+ streaming_guard_features = expand_feature_guard (
248
+ ctx .streaming_guard , ctx .flags , "sve"
249
+ )
231
250
232
251
if "streaming-only" in ctx .flags :
233
252
assert not guard_features
234
253
# Generate RUN lines for features only availble to streaming functions
235
254
for feats in streaming_guard_features :
236
- out .append (f"{ run_prefix } { cc1_args_for_features (feats )} -verify=streaming-guard" )
255
+ out .append (
256
+ f"{ run_prefix } { cc1_args_for_features (feats )} -verify=streaming-guard"
257
+ )
237
258
elif "streaming-compatible" in ctx .flags :
238
259
assert not guard_features
239
260
# NOTE: Streaming compatible builtins don't require SVE.
@@ -243,7 +264,9 @@ def emit_streaming_guard_run_lines(ctx: BuiltinContext) -> str:
243
264
out .append ("// expected-no-diagnostics" )
244
265
elif "feature-dependent" in ctx .flags :
245
266
assert guard_features and streaming_guard_features
246
- combined_features = expand_feature_guard (ctx .guard + "," + ctx .streaming_guard , ctx .flags )
267
+ combined_features = expand_feature_guard (
268
+ ctx .guard + "," + ctx .streaming_guard , ctx .flags
269
+ )
247
270
248
271
# Generate RUN lines for features only availble to normal functions
249
272
for feats in guard_features :
@@ -253,7 +276,9 @@ def emit_streaming_guard_run_lines(ctx: BuiltinContext) -> str:
253
276
# Geneate RUN lines for features only available to streaming functions
254
277
for feats in streaming_guard_features :
255
278
if feats not in combined_features :
256
- out .append (f"{ run_prefix } { cc1_args_for_features (feats )} -verify=streaming-guard" )
279
+ out .append (
280
+ f"{ run_prefix } { cc1_args_for_features (feats )} -verify=streaming-guard"
281
+ )
257
282
258
283
# Generate RUN lines for features available to all functions
259
284
for feats in combined_features :
@@ -268,7 +293,14 @@ def emit_streaming_guard_run_lines(ctx: BuiltinContext) -> str:
268
293
269
294
return "\n " .join (out )
270
295
271
- def emit_streaming_guard_function (ctx : BuiltinContext , var_decls : Sequence [str ], calls : Sequence [str ], func_name : str , func_type : FunctionType = FunctionType .NORMAL ) -> str :
296
+
297
+ def emit_streaming_guard_function (
298
+ ctx : BuiltinContext ,
299
+ var_decls : Sequence [str ],
300
+ calls : Sequence [str ],
301
+ func_name : str ,
302
+ func_type : FunctionType = FunctionType .NORMAL ,
303
+ ) -> str :
272
304
"""Emit a C function calling all builtins.
273
305
274
306
`calls` is a sequence of tuples: (name, call_line)
@@ -279,17 +311,23 @@ def emit_streaming_guard_function(ctx: BuiltinContext, var_decls: Sequence[str],
279
311
if func_type != FunctionType .STREAMING :
280
312
require_streaming_diagnostic = True
281
313
elif "streaming-compatible" in ctx .flags :
282
- pass # streaming compatible builtins are always available
314
+ pass # streaming compatible builtins are always available
283
315
elif "feature-dependent" in ctx .flags :
284
316
guard_features = expand_feature_guard (ctx .guard , ctx .flags , "sme" )
285
- streaming_guard_features = expand_feature_guard (ctx .streaming_guard , ctx .flags , "sve" )
286
- combined_features = expand_feature_guard (ctx .guard + "," + ctx .streaming_guard , ctx .flags )
317
+ streaming_guard_features = expand_feature_guard (
318
+ ctx .streaming_guard , ctx .flags , "sve"
319
+ )
320
+ combined_features = expand_feature_guard (
321
+ ctx .guard + "," + ctx .streaming_guard , ctx .flags
322
+ )
287
323
288
324
if func_type != FunctionType .NORMAL :
289
325
if any (feats not in combined_features for feats in guard_features ):
290
326
require_diagnostic = True
291
327
if func_type != FunctionType .STREAMING :
292
- if any (feats not in combined_features for feats in streaming_guard_features ):
328
+ if any (
329
+ feats not in combined_features for feats in streaming_guard_features
330
+ ):
293
331
require_streaming_diagnostic = True
294
332
else :
295
333
if func_type != FunctionType .NORMAL :
@@ -319,26 +357,35 @@ def emit_streaming_guard_function(ctx: BuiltinContext, var_decls: Sequence[str],
319
357
# Emit calls
320
358
for call in calls :
321
359
if require_diagnostic and require_streaming_diagnostic :
322
- out .append (" // guard-error@+2 {{builtin can only be called from a non-streaming function}}" )
323
- out .append (" // streaming-guard-error@+1 {{builtin can only be called from a streaming function}}" )
360
+ out .append (
361
+ " // guard-error@+2 {{builtin can only be called from a non-streaming function}}"
362
+ )
363
+ out .append (
364
+ " // streaming-guard-error@+1 {{builtin can only be called from a streaming function}}"
365
+ )
324
366
elif require_diagnostic :
325
- out .append (" // guard-error@+1 {{builtin can only be called from a non-streaming function}}" )
367
+ out .append (
368
+ " // guard-error@+1 {{builtin can only be called from a non-streaming function}}"
369
+ )
326
370
elif require_streaming_diagnostic :
327
- out .append (" // streaming-guard-error@+1 {{builtin can only be called from a streaming function}}" )
371
+ out .append (
372
+ " // streaming-guard-error@+1 {{builtin can only be called from a streaming function}}"
373
+ )
328
374
out .append (f" { call } " )
329
375
330
376
out .append ("}" )
331
377
return "\n " .join (out ) + "\n "
332
378
379
+
333
380
def natural_key (s : str ):
334
381
"""Allow sorting akin to "sort -V"""
335
- return [int (text ) if text .isdigit () else text
336
- for text in re . split ( r'(\d+)' , s )]
382
+ return [int (text ) if text .isdigit () else text for text in re . split ( r"(\d+)" , s )]
383
+
337
384
338
385
def build_calls_for_group (builtins : Iterable [str ]) -> Tuple [List [str ], List [str ]]:
339
386
"""From a list of builtin declaration strings, produce:
340
- - a sorted list of unique variable declarations
341
- - a sorted list of builtin calls
387
+ - a sorted list of unique variable declarations
388
+ - a sorted list of builtin calls
342
389
"""
343
390
var_decls : List [str ] = []
344
391
seen_types : set [str ] = set ()
@@ -363,6 +410,7 @@ def build_calls_for_group(builtins: Iterable[str]) -> Tuple[List[str], List[str]
363
410
364
411
return var_decls , calls
365
412
413
+
366
414
def gen_streaming_guard_tests (mode : MODE , json_path : Path , out_dir : Path ) -> None :
367
415
"""Generate a set of Clang Sema test files to ensure SVE/SME builtins are
368
416
callable based on the function type, or the required diagnostic is emitted.
@@ -381,8 +429,10 @@ def gen_streaming_guard_tests(mode: MODE, json_path: Path, out_dir: Path) -> Non
381
429
for builtin_ctx , builtin_decls in by_guard .items ():
382
430
var_decls , calls = build_calls_for_group (builtin_decls )
383
431
384
- out_parts : List [str ] = [];
385
- out_parts .append ("// NOTE: File has been autogenerated by utils/aarch64_builtins_test_generator.py" )
432
+ out_parts : List [str ] = []
433
+ out_parts .append (
434
+ "// NOTE: File has been autogenerated by utils/aarch64_builtins_test_generator.py"
435
+ )
386
436
out_parts .append (emit_streaming_guard_run_lines (builtin_ctx ))
387
437
out_parts .append ("" )
388
438
out_parts .append ("// REQUIRES: aarch64-registered-target" )
@@ -391,9 +441,23 @@ def gen_streaming_guard_tests(mode: MODE, json_path: Path, out_dir: Path) -> Non
391
441
out_parts .append ("" )
392
442
out_parts .append (str (builtin_ctx ))
393
443
out_parts .append ("" )
394
- out_parts .append (emit_streaming_guard_function (builtin_ctx , var_decls , calls , "test" ))
395
- out_parts .append (emit_streaming_guard_function (builtin_ctx , var_decls , calls , "test_streaming" , FunctionType .STREAMING ))
396
- out_parts .append (emit_streaming_guard_function (builtin_ctx , var_decls , calls , "test_streaming_compatible" , FunctionType .STREAMING_COMPATIBLE ))
444
+ out_parts .append (
445
+ emit_streaming_guard_function (builtin_ctx , var_decls , calls , "test" )
446
+ )
447
+ out_parts .append (
448
+ emit_streaming_guard_function (
449
+ builtin_ctx , var_decls , calls , "test_streaming" , FunctionType .STREAMING
450
+ )
451
+ )
452
+ out_parts .append (
453
+ emit_streaming_guard_function (
454
+ builtin_ctx ,
455
+ var_decls ,
456
+ calls ,
457
+ "test_streaming_compatible" ,
458
+ FunctionType .STREAMING_COMPATIBLE ,
459
+ )
460
+ )
397
461
398
462
output = "\n " .join (out_parts ).rstrip () + "\n "
399
463
@@ -402,42 +466,59 @@ def gen_streaming_guard_tests(mode: MODE, json_path: Path, out_dir: Path) -> Non
402
466
filename = make_filename (f"arm_{ mode .value } " , builtin_ctx , ".c" )
403
467
(out_dir / filename ).write_text (output )
404
468
else :
405
- print (output )
469
+ print (output )
406
470
407
471
return 0
408
472
473
+
409
474
# --- Main -------------------------------------------------------------------
410
475
476
+
411
477
def existing_file (path : str ) -> Path :
412
478
p = Path (path )
413
479
if not p .is_file ():
414
480
raise argparse .ArgumentTypeError (f"{ p } is not a valid file" )
415
481
return p
416
482
483
+
417
484
def main (argv : Sequence [str ] | None = None ) -> int :
418
485
ap = argparse .ArgumentParser (description = "Emit C tests for SVE/SME builtins" )
419
- ap .add_argument ("json" , type = existing_file ,
420
- help = "Path to json formatted builtin descriptions" )
421
- ap .add_argument ("--out-dir" , type = Path , default = None ,
422
- help = "Output directory (default: stdout)" )
423
- ap .add_argument ("--gen-streaming-guard-tests" , action = "store_true" ,
424
- help = "Generate C tests to validate SVE/SME builtin usage base on streaming attribute" )
425
- ap .add_argument ("--gen-target-guard-tests" , action = "store_true" ,
426
- help = "Generate C tests to validate SVE/SME builtin usage based on target features" )
427
- ap .add_argument ("--gen-builtin-tests" , action = "store_true" ,
428
- help = "Generate C tests to exercise SVE/SME builtins" )
429
- ap .add_argument ("--base-target-feature" , choices = ["sve" , "sme" ],
430
- help = "Force builtin source (sve: arm_sve.h, sme: arm_sme.h)" )
486
+ ap .add_argument (
487
+ "json" , type = existing_file , help = "Path to json formatted builtin descriptions"
488
+ )
489
+ ap .add_argument (
490
+ "--out-dir" , type = Path , default = None , help = "Output directory (default: stdout)"
491
+ )
492
+ ap .add_argument (
493
+ "--gen-streaming-guard-tests" ,
494
+ action = "store_true" ,
495
+ help = "Generate C tests to validate SVE/SME builtin usage base on streaming attribute" ,
496
+ )
497
+ ap .add_argument (
498
+ "--gen-target-guard-tests" ,
499
+ action = "store_true" ,
500
+ help = "Generate C tests to validate SVE/SME builtin usage based on target features" ,
501
+ )
502
+ ap .add_argument (
503
+ "--gen-builtin-tests" ,
504
+ action = "store_true" ,
505
+ help = "Generate C tests to exercise SVE/SME builtins" ,
506
+ )
507
+ ap .add_argument (
508
+ "--base-target-feature" ,
509
+ choices = ["sve" , "sme" ],
510
+ help = "Force builtin source (sve: arm_sve.h, sme: arm_sme.h)" ,
511
+ )
431
512
432
513
args = ap .parse_args (argv )
433
514
434
515
# When not forced, try to infer the mode from the input, defaulting to SVE.
435
516
if args .base_target_feature :
436
- mode = Mode (args .base_target_feature )
517
+ mode = Mode (args .base_target_feature )
437
518
elif args .json and args .json .name == "arm_sme_builtins.json" :
438
- mode = Mode .SME
519
+ mode = Mode .SME
439
520
else :
440
- mode = Mode .SVE
521
+ mode = Mode .SVE
441
522
442
523
# Generate test file
443
524
if args .gen_streaming_guard_tests :
@@ -449,5 +530,6 @@ def main(argv: Sequence[str] | None = None) -> int:
449
530
450
531
return 0
451
532
533
+
452
534
if __name__ == "__main__" :
453
535
raise SystemExit (main ())
0 commit comments