Skip to content

Commit 0b19860

Browse files
authored
[UR] Add support for feature guards (#18759)
Introduce the ability to wrap UR features in pre-processor guards based on the presence of the `guard: NAME` attribute in the yaml specs to support conditionally enabling features.
1 parent 4ec95b0 commit 0b19860

32 files changed

+458
-18
lines changed

unified-runtime/include/ur_print.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

unified-runtime/include/ur_print.hpp

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

unified-runtime/scripts/YaML.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This document describes the YaML format used by the scripts for the API specific
2222
* A header may take the following optional scalar fields: {`ordinal`, `version`}
2323
- `ordinal` will be used to override the default order (alphabetical) in which regions appear in the specification; `default="1000"`. Multiple regions with the same ordinal will be ordered alphabetically.
2424
- `version` can be used to define the minimum API version for all documents in the yml file; `default="1.0"`
25+
- `guard` takes a name, which must be a valid C identifier, that will be used to generate pre-processor guards around feature code
2526

2627
<table>
2728
<tr><th>YaML</th><th>C\C++</th><th>Python</th></tr>
@@ -58,6 +59,7 @@ n/a
5859
- `version` will be used to define the minimum API version in which the macro will appear; `default="1.0"` This will also affect the order in which the macro appears within its section.
5960
* A macro may take the following optional field which can be a scalar, a sequence of scalars or scalars to sequences: {`details`}
6061
- `details` will be used as the macro's detailed comment
62+
- `guard` takes a name, which must be a valid C identifier, that will be used to generate pre-processor guards around feature code
6163

6264
<table>
6365
<tr><th>YaML</th><th>C\C++</th><th>Python</th></tr>
@@ -149,6 +151,7 @@ n/a
149151
- `version` will be used to define the minimum API version in which the typedef will appear; `default="1.0"` This will also affect the order in which the typedef appears within its section and class.
150152
* A typedef may take the following optional field which can be a scalar, a sequence of scalars or scalars to sequences: {`details`}
151153
- `details` will be used as the typedef's detailed comment
154+
- `guard` takes a name, which must be a valid C identifier, that will be used to generate pre-processor guards around feature code
152155

153156
<table>
154157
<tr><th>YaML</th><th>C</th><th>C++</th><th>Python</th></tr>
@@ -201,6 +204,7 @@ class ur_name_t(c_void_p):
201204
- `desc` will be used as param's description comment
202205
- `name` must be a unique ISO-C standard identifier
203206
- `type` must be ISO-C standard data type
207+
- `guard` takes a name, which must be a valid C identifier, that will be used to generate pre-processor guards around feature code
204208

205209
<table>
206210
<tr><th>YaML</th><th>C</th><th>C++</th></tr>
@@ -247,6 +251,7 @@ std::function<void(void*)> ur_callback_t;
247251
- `loader_only` will be used to decide whether the handle can be instantiated and managed only by the loader.
248252
* A handle may take the following optional field which can be a scalar, a sequence of scalars or scalars to sequences: {`details`}
249253
- `details` will be used as the handle's detailed comment
254+
- `guard` takes a name, which must be a valid C identifier, that will be used to generate pre-processor guards around feature code
250255

251256
<table>
252257
<tr><th>YaML</th><th>C</th><th>C++</th><th>Python</th></tr>
@@ -344,6 +349,7 @@ plural form *enumerators* is abbreviated to `etors`.
344349
+ `version` will be used to define the minimum API version in which the etor will appear; `default="1.0"` This will also affect the order in which the etor appears within the enum.
345350
* An enum may take the following optional field which can be a scalar, a sequence of scalars or scalars to sequences: {`details`}
346351
- `details` will be used as the enum's detailed comment
352+
- `guard` takes a name, which must be a valid C identifier, that will be used to generate pre-processor guards around feature code
347353

348354
<table>
349355
<tr><th>YaML</th><th>C</th><th>C++</th><th>Python</th></tr>
@@ -491,6 +497,7 @@ class ur_name_flags_v(IntEnum):
491497
+ `tag` applies only to unions and refers to a value for when this member can be accessed.
492498
* A struct|union may take the following optional field which can be a scalar, a sequence of scalars or scalars to sequences: {`details`}
493499
- `details` will be used as the struct|union's detailed comment
500+
- `guard` takes a name, which must be a valid C identifier, that will be used to generate pre-processor guards around feature code
494501

495502
<table>
496503
<tr><th>YaML</th><th>C</th><th>C++</th><th>Python</th></tr>
@@ -652,6 +659,7 @@ class ur_name_t(Structure):
652659
- `returns` may contain a sequence of custom validation layer code blocks
653660
* A function may take the following optional field which can be a scalar, a sequence of scalars or scalars to sequences: {`details`}
654661
- `details` will be used as the function's detailed comment
662+
- `guard` takes a name, which must be a valid C identifier, that will be used to generate pre-processor guards around feature code
655663

656664
<table>
657665
<tr><th>YaML</th><th>C</th><th>C++</th><th>Python</th></tr>
@@ -762,6 +770,7 @@ _urClsNameFnName_t = CFUNCTYPE( ur_result_t, ur_cls_handle_t, c_ulong, POINTER(c
762770
- The next member may be the `_desc_t` used to create the object; the `name` must be `"desc"`
763771
* A class may take the following optional field which can be a scalar, a sequence of scalars or scalars to sequences: {`details`}
764772
- `details` will be used as the class's detailed comment
773+
- `guard` takes a name, which must be a valid C identifier, that will be used to generate pre-processor guards around feature code
765774

766775
<table>
767776
<tr><th>YaML</th><th>C++</th></tr>

unified-runtime/scripts/generate_ids.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,24 @@ def generate_function_type(
6565
}
6666
max_etor = get_max_enum(existing_function_type)
6767
functions = [
68-
obj["class"][len("$x") :] + obj["name"]
69-
for s in specs
70-
for obj in s["objects"]
71-
if obj["type"] == "function"
68+
obj for s in specs for obj in s["objects"] if obj["type"] == "function"
7269
]
7370
registry = list()
74-
for fname in functions:
71+
for function in functions:
72+
fname = function["class"][len("$x") :] + function["name"]
7573
etor_name = "$X_FUNCTION_" + util.to_snake_case(fname).upper()
7674
id = existing_etors.get(etor_name)
7775
if id is None:
7876
max_etor += 1
7977
id = max_etor
80-
registry.append(
81-
{"name": etor_name, "desc": f"Enumerator for $x{fname}", "value": str(id)}
82-
)
78+
entry = {
79+
"name": etor_name,
80+
"desc": f"Enumerator for $x{fname}",
81+
"value": str(id),
82+
}
83+
if "guard" in function:
84+
entry["guard"] = function["guard"]
85+
registry.append(entry)
8386
registry = sorted(registry, key=lambda x: int(x["value"]))
8487
existing_function_type["etors"] = registry
8588
update_fn(existing_function_type, meta)

unified-runtime/scripts/parse_specs.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,16 @@ def __validate_union_tag(d):
512512
if d.get("tag") is None:
513513
raise Exception(f"{d['name']} must include a 'tag' part of the union.")
514514

515+
def __validate_guard(d):
516+
if "guard" not in d:
517+
return
518+
if not isinstance(d["guard"], str):
519+
raise Exception("'guard' must be a string: '%s'" % type(d["guard"]))
520+
if not re.match(r"[A-Z][A-Z0-9_]*$", d["guard"]):
521+
raise Exception(
522+
"'guard' must a valid upper case C identifier: '%s'" % d["guard"]
523+
)
524+
515525
try:
516526
if "type" not in d:
517527
raise Exception("every document must have 'type'")
@@ -530,6 +540,7 @@ def __validate_union_tag(d):
530540

531541
__validate_ordinal(d)
532542
__validate_version(d)
543+
__validate_guard(d)
533544

534545
elif "macro" == d["type"]:
535546
if ("desc" not in d) or ("name" not in d) or ("value" not in d):
@@ -544,6 +555,7 @@ def __validate_union_tag(d):
544555
__validate_details(d)
545556
__validate_ordinal(d)
546557
__validate_version(d)
558+
__validate_guard(d)
547559

548560
elif "typedef" == d["type"]:
549561
if ("desc" not in d) or ("name" not in d) or ("value" not in d):
@@ -555,6 +567,7 @@ def __validate_union_tag(d):
555567
__validate_details(d)
556568
__validate_ordinal(d)
557569
__validate_version(d)
570+
__validate_guard(d)
558571

559572
elif "handle" == d["type"]:
560573
if ("desc" not in d) or ("name" not in d):
@@ -566,6 +579,7 @@ def __validate_union_tag(d):
566579
__validate_details(d)
567580
__validate_ordinal(d)
568581
__validate_version(d)
582+
__validate_guard(d)
569583

570584
elif "enum" == d["type"]:
571585
if ("desc" not in d) or ("name" not in d):
@@ -578,6 +592,7 @@ def __validate_union_tag(d):
578592
__validate_details(d)
579593
__validate_ordinal(d)
580594
__validate_version(d)
595+
__validate_guard(d)
581596

582597
elif "struct" == d["type"] or "union" == d["type"]:
583598
if ("desc" not in d) or ("name" not in d):
@@ -594,6 +609,7 @@ def __validate_union_tag(d):
594609
__validate_details(d)
595610
__validate_ordinal(d)
596611
__validate_version(d)
612+
__validate_guard(d)
597613

598614
elif "function" == d["type"]:
599615
if ("desc" not in d) or ("name" not in d):
@@ -612,6 +628,7 @@ def __validate_union_tag(d):
612628
__validate_details(d)
613629
__validate_ordinal(d)
614630
__validate_version(d)
631+
__validate_guard(d)
615632

616633
elif "class" == d["type"]:
617634
if ("desc" not in d) or ("name" not in d):
@@ -623,6 +640,7 @@ def __validate_union_tag(d):
623640
__validate_details(d)
624641
__validate_ordinal(d)
625642
__validate_version(d)
643+
__validate_guard(d)
626644

627645
return True
628646

@@ -1136,6 +1154,9 @@ def parse(section, version, tags, meta, ref):
11361154

11371155
if d["type"] == "enum" and d.get("extend"):
11381156
# enum extensions are resolved later
1157+
if "guard" in header and "guard" not in d:
1158+
for etor in d["etors"]:
1159+
etor["guard"] = header["guard"]
11391160
enum_extensions.append(d)
11401161
continue
11411162

@@ -1167,6 +1188,8 @@ def parse(section, version, tags, meta, ref):
11671188
name = name.replace(c, " ")
11681189
elif header:
11691190
for d in _make_versions(d, version):
1191+
if "guard" in header and "guard" not in d:
1192+
d["guard"] = header["guard"]
11701193
objects.append(d)
11711194
meta = _generate_meta(d, header["ordinal"], meta)
11721195

unified-runtime/scripts/templates/adapter.def.in.mako

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,11 @@ from templates import helper as th
77
LIBRARY @TARGET_LIBNAME@
88
EXPORTS
99
%for tbl in th.get_pfntables(specs, meta, n, tags):
10+
%if 'guard' in tbl:
11+
#if ${tbl['guard']}
12+
%endif
1013
${tbl['export']['name']}
14+
%if 'guard' in tbl:
15+
#endif // ${tbl['guard']}
16+
%endif
1117
%endfor

unified-runtime/scripts/templates/adapter.map.in.mako

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ from templates import helper as th
77
@TARGET_LIBNAME@ {
88
global:
99
%for tbl in th.get_pfntables(specs, meta, n, tags):
10+
%if 'guard' in tbl:
11+
#if ${tbl['guard']}
12+
%endif
1013
${tbl['export']['name']};
14+
%if 'guard' in tbl:
15+
#endif // ${tbl['guard']}
16+
%endif
1117
%endfor
1218
local:
1319
*;

unified-runtime/scripts/templates/api.cpp.mako

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ from templates import helper as th
2424
#include "${n}_api.h"
2525

2626
%for obj in th.extract_objs(specs, r"function"):
27+
%if 'guard' in obj:
28+
#if ${obj['guard']}
29+
%endif
2730
///////////////////////////////////////////////////////////////////////////////
2831
%if 'condition' in obj:
2932
#if ${th.subt(n, tags, obj['condition'])}
@@ -51,5 +54,8 @@ ${th.make_func_name(n, tags, obj)}(
5154
%if 'condition' in obj:
5255
#endif // ${th.subt(n, tags, obj['condition'])}
5356
%endif
57+
%if 'guard' in obj:
58+
#endif // ${obj['guard']}
59+
%endif
5460

5561
%endfor

unified-runtime/scripts/templates/api.h.mako

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,24 @@ extern "C" {
4242

4343
%for spec in specs:
4444
%if len(spec['objects']) and 'manifest' not in spec['name']:
45+
%if 'guard' in spec['header']:
46+
// ${spec['header']['guard']}
47+
%endif
4548
// ${th.subt(n, tags, spec['header']['desc'])}
4649
#if !defined(__GNUC__)
4750
#pragma region ${spec['name'].replace(' ', '_')}
4851
#endif
52+
%if 'guard' in spec:
53+
// end ${obj['guard']}
54+
%endif
4955
%endif
5056
%for obj in spec['objects']:
5157
%if "manifest" in obj['type']:
5258
<%continue%>
5359
%elif not re.match(r"class", obj['type']):
60+
%if 'guard' in obj and 'guard' not in spec['header']:
61+
// ${obj['guard']}
62+
%endif
5463
///////////////////////////////////////////////////////////////////////////////
5564
## MACRO ######################################################################
5665
%if re.match(r"macro", obj['type']):
@@ -141,13 +150,22 @@ typedef struct ${th.subt(n, tags, obj['name'])}_ *${th.subt(n, tags, obj['name']
141150
%if re.match(r"macro", obj['type']):
142151
#endif // ${th.make_macro_name(n, tags, obj, params=False)}
143152
%endif
153+
%if 'guard' in obj and 'guard' not in spec['header']:
154+
// end ${obj['guard']}
155+
%endif
144156
145157
%endif # not re.match(r"class", obj['type'])
146158
%endfor # obj in spec['objects']
147159
%if len(spec['objects']) and 'manifest' not in spec['name']:
160+
%if 'guard' in spec:
161+
// ${obj['guard']}
162+
%endif
148163
#if !defined(__GNUC__)
149164
#pragma endregion
150165
#endif
166+
%if 'guard' in spec['header']:
167+
// end ${spec['header']['guard']}
168+
%endif
151169
%endif
152170
%endfor # spec in specs
153171
%if n not in ["zet", "zes"]:
@@ -157,6 +175,9 @@ typedef struct ${th.subt(n, tags, obj['name'])}_ *${th.subt(n, tags, obj['name']
157175
#endif
158176
%for tbl in th.get_pfncbtables(specs, meta, n, tags):
159177
%for obj in tbl['functions']:
178+
%if 'guard' in obj:
179+
// ${obj['guard']}
180+
%endif
160181
%if obj['params']:
161182
///////////////////////////////////////////////////////////////////////////////
162183
/// @brief Function parameters for ${th.make_func_name(n, tags, obj)}
@@ -175,6 +196,9 @@ typedef struct ${th.make_pfncb_param_type(n, tags, obj)}
175196
#endif // ${th.subt(n, tags, obj['condition'])}
176197
%endif
177198
%endif
199+
%if 'guard' in obj:
200+
// end ${obj['guard']}
201+
%endif
178202
179203
%endfor
180204
%endfor

unified-runtime/scripts/templates/api_funcs.def.mako

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ from templates import helper as th
2828

2929
%for tbl in th.get_pfntables(specs, meta, n, tags):
3030
%for obj in tbl['functions']:
31+
%if 'guard' in obj:
32+
// ${obj['guard']}
33+
%endif
3134
_UR_API(${th.make_func_name(n, tags, obj)})
35+
%if 'guard' in obj:
36+
// end ${obj['guard']}
37+
%endif
3238
%endfor
3339
%endfor
3440
%for obj in th.get_loader_functions(specs, meta, n, tags):

0 commit comments

Comments
 (0)