Skip to content

Commit 16713ea

Browse files
authored
[UR] Allow loader to skip adapters based on prefilter device type. (#17072)
Addresses URT-855
1 parent 6ef8448 commit 16713ea

File tree

11 files changed

+357
-82
lines changed

11 files changed

+357
-82
lines changed

sycl/test-e2e/FilterSelector/select_device_cpu.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ int main() {
2525
{
2626
device d(default_selector_v);
2727
string name = d.get_platform().get_info<info::platform::name>();
28-
assert(name.find("OpenCL") != string::npos &&
29-
"default_selector failed to find cpu device");
28+
assert((name.find("OpenCL") != string::npos) ||
29+
(name.find("NATIVE_CPU") != string::npos) &&
30+
"default_selector failed to find cpu device");
3031
}
3132
{
3233
try {

unified-runtime/scripts/YaML.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This document describes the YaML format used by the scripts for the API specific
99

1010
## YML Syntax
1111
* Each document in the yml file represents an entry in the specification.
12-
* Every document must have a `type` scalar: {`header`, `macro`, `typedef`, `const`, `enum`, `struct`, `handle`, `function`, `class`}
12+
* Every document must have a `type` scalar: {`header`, `macro`, `typedef`, `const`, `enum`, `struct`, `handle`, `function`, `class`, `manifest`}
1313
* All scalars must be strings. The document writer is responsible for using explicit string notification where the yml parser may perform implicit conversion.
1414
* Custom names must be tagged using `$` followed by the tag name. The tag names are defined in the `config.ini` section. There are two tag variations for replacement:
1515
- `$x` : lower_case
@@ -831,6 +831,17 @@ namespace ur {
831831
</td></tr>
832832
</table>
833833

834+
#### type: manifest
835+
* A manifest encodes meta information about an adapter library.
836+
* These don't cause anything to be added to the spec or headers, they're intended to generate code for the loader.
837+
* A manifest requires the following scalar fields: {`name`, `backend`}
838+
- `name` must be a string unique to the adapter.
839+
- `name` should be identical to how the name appears in the adapter's library name. E.g. `libur_adapter_my_adapter` should have the name string `my_adapter`.
840+
- `backend` must be an etor of `$x_adapter_backend_t`.
841+
- `backend` must not be `$X_ADAPTER_BACKEND_UNKNOWN`.
842+
* a manifest requires the following sequence of scalars: {`device_types`}
843+
- `device_types` must be an etor of `$x_device_type_t`
844+
834845
## Extensions
835846
* Each extensions must be defined in a unique `.yml` file
836847
* The extension file must be added to the section being extended; i.e. extensions to core APIs must be added to the `core` folder, etc.

unified-runtime/scripts/core/CONTRIB.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,3 +357,16 @@ to easily differentiate experimental feature symbols, the following conventions
357357
* All structures, enumerations, and other types must follow
358358
``${x}_exp_name_t`` name case convention.
359359
## --validate=on
360+
361+
New Adapters
362+
============
363+
364+
The UR loader will only load UR adapter libraries made known to it via entries
365+
in `scripts/core/manifests.yml`_. New adapters must have their own entry in that
366+
file before the loader will recognize them. See `YaML.md`_ for details about
367+
manifest semantics.
368+
369+
.. _scripts/core/manifests.yml:
370+
https://github.com/intel/llvm/blob/sycl/unified-runtime/scripts/core/manifests.yml
371+
.. _YaML.md:
372+
https://github.com/intel/llvm/blob/sycl/unified-runtime/scripts/YaML.md
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--- #--------------------------------------------------------------------------
2+
type: header
3+
desc: "Intel $OneApi Unified Runtime adapter manifests"
4+
ordinal: "99"
5+
--- #--------------------------------------------------------------------------
6+
type: manifest
7+
name: opencl
8+
backend: $X_ADAPTER_BACKEND_OPENCL
9+
device_types:
10+
- $X_DEVICE_TYPE_ALL
11+
--- #--------------------------------------------------------------------------
12+
type: manifest
13+
name: cuda
14+
backend: $X_ADAPTER_BACKEND_CUDA
15+
device_types:
16+
- $X_DEVICE_TYPE_GPU
17+
--- #--------------------------------------------------------------------------
18+
type: manifest
19+
name: hip
20+
backend: $X_ADAPTER_BACKEND_HIP
21+
device_types:
22+
- $X_DEVICE_TYPE_GPU
23+
--- #--------------------------------------------------------------------------
24+
type: manifest
25+
name: level_zero
26+
backend: $X_ADAPTER_BACKEND_LEVEL_ZERO
27+
device_types:
28+
- $X_DEVICE_TYPE_ALL
29+
--- #--------------------------------------------------------------------------
30+
type: manifest
31+
name: level_zero_v2
32+
backend: $X_ADAPTER_BACKEND_LEVEL_ZERO
33+
device_types:
34+
- $X_DEVICE_TYPE_ALL
35+
--- #--------------------------------------------------------------------------
36+
type: manifest
37+
name: native_cpu
38+
backend: $X_ADAPTER_BACKEND_NATIVE_CPU
39+
device_types:
40+
- $X_DEVICE_TYPE_CPU

unified-runtime/scripts/generate_code.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,26 @@ def _mako_lib_cpp(path, namespace, tags, version, specs, meta):
225225
specs=specs,
226226
meta=meta,
227227
)
228+
229+
template = "manifests.hpp.mako"
230+
fin = os.path.join(templates_dir, template)
231+
232+
name = "%s_manifests" % (namespace)
233+
filename = "%s.hpp" % name
234+
fout = os.path.join(path, filename)
235+
236+
print("Generating %s..." % fout)
237+
loc += util.makoWrite(
238+
fin,
239+
fout,
240+
name=name,
241+
ver=version,
242+
namespace=namespace,
243+
tags=tags,
244+
specs=specs,
245+
meta=meta,
246+
)
247+
228248
return loc
229249

230250

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,16 @@ extern "C" {
4141
#endif
4242

4343
%for spec in specs:
44-
%if len(spec['objects']):
44+
%if len(spec['objects']) and 'manifest' not in spec['name']:
4545
// ${th.subt(n, tags, spec['header']['desc'])}
4646
#if !defined(__GNUC__)
4747
#pragma region ${spec['name'].replace(' ', '_')}
4848
#endif
4949
%endif
5050
%for obj in spec['objects']:
51-
%if not re.match(r"class", obj['type']):
51+
%if "manifest" in obj['type']:
52+
<%continue%>
53+
%elif not re.match(r"class", obj['type']):
5254
///////////////////////////////////////////////////////////////////////////////
5355
## MACRO ######################################################################
5456
%if re.match(r"macro", obj['type']):
@@ -142,7 +144,7 @@ typedef struct ${th.subt(n, tags, obj['name'])}_ *${th.subt(n, tags, obj['name']
142144
143145
%endif # not re.match(r"class", obj['type'])
144146
%endfor # obj in spec['objects']
145-
%if len(spec['objects']):
147+
%if len(spec['objects']) and 'manifest' not in spec['name']:
146148
#if !defined(__GNUC__)
147149
#pragma endregion
148150
#endif

unified-runtime/scripts/templates/helper.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,20 @@ def get_adapter_handles(specs):
739739
return objs
740740

741741

742+
"""
743+
Public:
744+
returns a list of all adapter manifests
745+
"""
746+
747+
748+
def get_adapter_manifests(specs):
749+
objs = []
750+
for s in specs:
751+
for obj in s["objects"]:
752+
if "manifest" in obj["type"]:
753+
objs.append(obj)
754+
return objs
755+
742756
"""
743757
Public:
744758
returns a list of all loader API functions' names
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<%!
2+
from templates import helper as th
3+
%>/*
4+
*
5+
* Copyright (C) 2025 Intel Corporation
6+
*
7+
* Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM
8+
* Exceptions.
9+
* See LICENSE.TXT
10+
*
11+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
12+
*
13+
* @file ${name}.hpp
14+
*
15+
*/
16+
17+
// Auto-generated file, do not edit.
18+
19+
#pragma once
20+
21+
#include <string>
22+
#include <vector>
23+
24+
#include "ur_util.hpp"
25+
#include <ur_api.h>
26+
27+
namespace ur_loader {
28+
struct ur_adapter_manifest {
29+
std::string name;
30+
std::string library;
31+
ur_adapter_backend_t backend;
32+
std::vector<ur_device_type_t> device_types;
33+
};
34+
35+
const std::vector<ur_adapter_manifest> ur_adapter_manifests = {
36+
%for manifest in th.get_adapter_manifests(specs):
37+
{
38+
"${manifest['name']}",
39+
MAKE_LIBRARY_NAME("ur_adapter_${manifest['name']}", "0"),
40+
${th.subt(namespace, tags, manifest['backend'])},
41+
{
42+
%for device_type in manifest['device_types']:
43+
${th.subt(namespace, tags, device_type)},
44+
%endfor
45+
}
46+
},
47+
%endfor
48+
};
49+
}

unified-runtime/source/common/ur_util.hpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,14 +207,17 @@ using EnvVarMap = std::map<std::string, std::vector<std::string>>;
207207
/// map[param_1] = [value_1, value_2]
208208
/// map[param_2] = [value_1]
209209
/// @param env_var_name name of an environment variable to be parsed
210+
/// @param reject_empy whether to throw an error on discovering an empty value
211+
/// @param allow_duplicate whether to allow multiple pairs with the same key
210212
/// @return std::optional with a possible map with parsed parameters as keys and
211213
/// vectors of strings containing parsed values as keys.
212214
/// Otherwise, optional is set to std::nullopt when the environment
213215
/// variable is not set or is empty.
214216
/// @throws std::invalid_argument() when the parsed environment variable has
215217
/// wrong format
216218
inline std::optional<EnvVarMap> getenv_to_map(const char *env_var_name,
217-
bool reject_empty = true) {
219+
bool reject_empty = true,
220+
bool allow_duplicate = false) {
218221
char main_delim = ';';
219222
char key_value_delim = ':';
220223
char values_delim = ',';
@@ -247,7 +250,7 @@ inline std::optional<EnvVarMap> getenv_to_map(const char *env_var_name,
247250
std::getline(kv_ss, key, key_value_delim);
248251
std::getline(kv_ss, values);
249252
if (key.empty() || (reject_empty && values.empty()) ||
250-
map.find(key) != map.end()) {
253+
(map.find(key) != map.end() && !allow_duplicate)) {
251254
throw_wrong_format_map(env_var_name, *env_var);
252255
}
253256

@@ -264,7 +267,11 @@ inline std::optional<EnvVarMap> getenv_to_map(const char *env_var_name,
264267
}
265268
values_vec.push_back(value);
266269
}
267-
map[key] = values_vec;
270+
if (map.find(key) != map.end()) {
271+
map[key].insert(map[key].end(), values_vec.begin(), values_vec.end());
272+
} else {
273+
map[key] = values_vec;
274+
}
268275
}
269276
return map;
270277
}

0 commit comments

Comments
 (0)