Skip to content

Commit db83117

Browse files
authored
Merge pull request #2261 from againull/againull/2d_block_exp
Add new device descriptor to query 2D block array capabilities of the Intel GPU
2 parents 0a90db9 + c79df59 commit db83117

25 files changed

+333
-23
lines changed

cmake/FetchLevelZero.cmake

+26-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ set(UR_LEVEL_ZERO_LOADER_LIBRARY "" CACHE FILEPATH "Path of the Level Zero Loade
77
set(UR_LEVEL_ZERO_INCLUDE_DIR "" CACHE FILEPATH "Directory containing the Level Zero Headers")
88
set(UR_LEVEL_ZERO_LOADER_REPO "" CACHE STRING "Github repo to get the Level Zero loader sources from")
99
set(UR_LEVEL_ZERO_LOADER_TAG "" CACHE STRING " GIT tag of the Level Loader taken from github repo")
10+
set(UR_COMPUTE_RUNTIME_REPO "" CACHE STRING "Github repo to get the compute runtime sources from")
11+
set(UR_COMPUTE_RUNTIME_TAG "" CACHE STRING " GIT tag of the compute runtime taken from github repo")
1012

1113
# Copy Level Zero loader/headers locally to the build to avoid leaking their path.
1214
set(LEVEL_ZERO_COPY_DIR ${CMAKE_CURRENT_BINARY_DIR}/level_zero_loader)
@@ -87,8 +89,31 @@ target_link_libraries(LevelZeroLoader
8789
INTERFACE "${LEVEL_ZERO_LIB_NAME}"
8890
)
8991

92+
file(GLOB LEVEL_ZERO_LOADER_API_HEADERS "${LEVEL_ZERO_INCLUDE_DIR}/*.h")
93+
file(COPY ${LEVEL_ZERO_LOADER_API_HEADERS} DESTINATION ${LEVEL_ZERO_INCLUDE_DIR}/level_zero)
9094
add_library(LevelZeroLoader-Headers INTERFACE)
9195
target_include_directories(LevelZeroLoader-Headers
92-
INTERFACE "$<BUILD_INTERFACE:${LEVEL_ZERO_INCLUDE_DIR}>"
96+
INTERFACE "$<BUILD_INTERFACE:${LEVEL_ZERO_INCLUDE_DIR};${LEVEL_ZERO_INCLUDE_DIR}/level_zero>"
97+
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
98+
)
99+
100+
if (UR_COMPUTE_RUNTIME_REPO STREQUAL "")
101+
set(UR_COMPUTE_RUNTIME_REPO "https://github.com/intel/compute-runtime.git")
102+
endif()
103+
if (UR_COMPUTE_RUNTIME_TAG STREQUAL "")
104+
set(UR_COMPUTE_RUNTIME_TAG 24.39.31294.12)
105+
endif()
106+
include(FetchContent)
107+
# Sparse fetch only the dir with level zero headers to avoid pulling in the entire compute-runtime.
108+
FetchContentSparse_Declare(compute-runtime-level-zero-headers ${UR_COMPUTE_RUNTIME_REPO} "${UR_COMPUTE_RUNTIME_TAG}" "level_zero/include")
109+
FetchContent_GetProperties(compute-runtime-level-zero-headers)
110+
if(NOT compute-runtime-level-zero-headers_POPULATED)
111+
FetchContent_Populate(compute-runtime-level-zero-headers)
112+
endif()
113+
add_library(ComputeRuntimeLevelZero-Headers INTERFACE)
114+
set(COMPUTE_RUNTIME_LEVEL_ZERO_INCLUDE "${compute-runtime-level-zero-headers_SOURCE_DIR}/../..")
115+
message(STATUS "Level Zero Adapter: Using Level Zero headers from ${COMPUTE_RUNTIME_LEVEL_ZERO_INCLUDE}")
116+
target_include_directories(ComputeRuntimeLevelZero-Headers
117+
INTERFACE "$<BUILD_INTERFACE:${COMPUTE_RUNTIME_LEVEL_ZERO_INCLUDE}>"
93118
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
94119
)

include/ur_api.h

+24-1
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,8 @@ typedef enum ur_device_info_t {
17051705
UR_DEVICE_INFO_ENQUEUE_NATIVE_COMMAND_SUPPORT_EXP = 0x2020, ///< [::ur_bool_t] returns true if the device supports enqueueing of native
17061706
///< work
17071707
UR_DEVICE_INFO_LOW_POWER_EVENTS_EXP = 0x2021, ///< [::ur_bool_t] returns true if the device supports low-power events.
1708+
UR_DEVICE_INFO_2D_BLOCK_ARRAY_CAPABILITIES_EXP = 0x2022, ///< [::ur_exp_device_2d_block_array_capability_flags_t] return a bit-field
1709+
///< of Intel GPU 2D block array capabilities
17081710
/// @cond
17091711
UR_DEVICE_INFO_FORCE_UINT32 = 0x7fffffff
17101712
/// @endcond
@@ -1730,7 +1732,7 @@ typedef enum ur_device_info_t {
17301732
/// - ::UR_RESULT_ERROR_INVALID_NULL_HANDLE
17311733
/// + `NULL == hDevice`
17321734
/// - ::UR_RESULT_ERROR_INVALID_ENUMERATION
1733-
/// + `::UR_DEVICE_INFO_LOW_POWER_EVENTS_EXP < propName`
1735+
/// + `::UR_DEVICE_INFO_2D_BLOCK_ARRAY_CAPABILITIES_EXP < propName`
17341736
/// - ::UR_RESULT_ERROR_UNSUPPORTED_ENUMERATION
17351737
/// + If `propName` is not supported by the adapter.
17361738
/// - ::UR_RESULT_ERROR_INVALID_SIZE
@@ -7428,6 +7430,27 @@ urEnqueueWriteHostPipe(
74287430
///< an element of the phEventWaitList array.
74297431
);
74307432

7433+
#if !defined(__GNUC__)
7434+
#pragma endregion
7435+
#endif
7436+
// Intel 'oneAPI' Unified Runtime Experimental device descriptor for querying Intel device 2D block array capabilities
7437+
#if !defined(__GNUC__)
7438+
#pragma region 2d_block_array_capabilities_(experimental)
7439+
#endif
7440+
///////////////////////////////////////////////////////////////////////////////
7441+
/// @brief Intel GPU 2D block array capabilities
7442+
typedef uint32_t ur_exp_device_2d_block_array_capability_flags_t;
7443+
typedef enum ur_exp_device_2d_block_array_capability_flag_t {
7444+
UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_LOAD = UR_BIT(0), ///< Load instructions are supported
7445+
UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_STORE = UR_BIT(1), ///< Store instructions are supported
7446+
/// @cond
7447+
UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_FORCE_UINT32 = 0x7fffffff
7448+
/// @endcond
7449+
7450+
} ur_exp_device_2d_block_array_capability_flag_t;
7451+
/// @brief Bit Mask for validating ur_exp_device_2d_block_array_capability_flags_t
7452+
#define UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAGS_MASK 0xfffffffc
7453+
74317454
#if !defined(__GNUC__)
74327455
#pragma endregion
74337456
#endif

include/ur_print.h

+8
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,14 @@ UR_APIEXPORT ur_result_t UR_APICALL urPrintMapFlags(enum ur_map_flag_t value, ch
874874
/// - `buff_size < out_size`
875875
UR_APIEXPORT ur_result_t UR_APICALL urPrintUsmMigrationFlags(enum ur_usm_migration_flag_t value, char *buffer, const size_t buff_size, size_t *out_size);
876876

877+
///////////////////////////////////////////////////////////////////////////////
878+
/// @brief Print ur_exp_device_2d_block_array_capability_flag_t enum
879+
/// @returns
880+
/// - ::UR_RESULT_SUCCESS
881+
/// - ::UR_RESULT_ERROR_INVALID_SIZE
882+
/// - `buff_size < out_size`
883+
UR_APIEXPORT ur_result_t UR_APICALL urPrintExpDevice_2dBlockArrayCapabilityFlags(enum ur_exp_device_2d_block_array_capability_flag_t value, char *buffer, const size_t buff_size, size_t *out_size);
884+
877885
///////////////////////////////////////////////////////////////////////////////
878886
/// @brief Print ur_exp_image_copy_flag_t enum
879887
/// @returns

include/ur_print.hpp

+78
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ inline ur_result_t printFlag<ur_map_flag_t>(std::ostream &os, uint32_t flag);
194194
template <>
195195
inline ur_result_t printFlag<ur_usm_migration_flag_t>(std::ostream &os, uint32_t flag);
196196

197+
template <>
198+
inline ur_result_t printFlag<ur_exp_device_2d_block_array_capability_flag_t>(std::ostream &os, uint32_t flag);
199+
197200
template <>
198201
inline ur_result_t printFlag<ur_exp_image_copy_flag_t>(std::ostream &os, uint32_t flag);
199202

@@ -328,6 +331,7 @@ inline std::ostream &operator<<(std::ostream &os, [[maybe_unused]] const struct
328331
inline std::ostream &operator<<(std::ostream &os, enum ur_execution_info_t value);
329332
inline std::ostream &operator<<(std::ostream &os, enum ur_map_flag_t value);
330333
inline std::ostream &operator<<(std::ostream &os, enum ur_usm_migration_flag_t value);
334+
inline std::ostream &operator<<(std::ostream &os, enum ur_exp_device_2d_block_array_capability_flag_t value);
331335
inline std::ostream &operator<<(std::ostream &os, enum ur_exp_image_copy_flag_t value);
332336
inline std::ostream &operator<<(std::ostream &os, enum ur_exp_sampler_cubemap_filter_mode_t value);
333337
inline std::ostream &operator<<(std::ostream &os, enum ur_exp_external_mem_type_t value);
@@ -2665,6 +2669,9 @@ inline std::ostream &operator<<(std::ostream &os, enum ur_device_info_t value) {
26652669
case UR_DEVICE_INFO_LOW_POWER_EVENTS_EXP:
26662670
os << "UR_DEVICE_INFO_LOW_POWER_EVENTS_EXP";
26672671
break;
2672+
case UR_DEVICE_INFO_2D_BLOCK_ARRAY_CAPABILITIES_EXP:
2673+
os << "UR_DEVICE_INFO_2D_BLOCK_ARRAY_CAPABILITIES_EXP";
2674+
break;
26682675
default:
26692676
os << "unknown enumerator";
26702677
break;
@@ -4472,6 +4479,19 @@ inline ur_result_t printTagged(std::ostream &os, const void *ptr, ur_device_info
44724479

44734480
os << ")";
44744481
} break;
4482+
case UR_DEVICE_INFO_2D_BLOCK_ARRAY_CAPABILITIES_EXP: {
4483+
const ur_exp_device_2d_block_array_capability_flags_t *tptr = (const ur_exp_device_2d_block_array_capability_flags_t *)ptr;
4484+
if (sizeof(ur_exp_device_2d_block_array_capability_flags_t) > size) {
4485+
os << "invalid size (is: " << size << ", expected: >=" << sizeof(ur_exp_device_2d_block_array_capability_flags_t) << ")";
4486+
return UR_RESULT_ERROR_INVALID_SIZE;
4487+
}
4488+
os << (const void *)(tptr) << " (";
4489+
4490+
ur::details::printFlag<ur_exp_device_2d_block_array_capability_flag_t>(os,
4491+
*tptr);
4492+
4493+
os << ")";
4494+
} break;
44754495
default:
44764496
os << "unknown enumerator";
44774497
return UR_RESULT_ERROR_INVALID_ENUMERATION;
@@ -9455,6 +9475,64 @@ inline ur_result_t printFlag<ur_usm_migration_flag_t>(std::ostream &os, uint32_t
94559475
}
94569476
} // namespace ur::details
94579477
///////////////////////////////////////////////////////////////////////////////
9478+
/// @brief Print operator for the ur_exp_device_2d_block_array_capability_flag_t type
9479+
/// @returns
9480+
/// std::ostream &
9481+
inline std::ostream &operator<<(std::ostream &os, enum ur_exp_device_2d_block_array_capability_flag_t value) {
9482+
switch (value) {
9483+
case UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_LOAD:
9484+
os << "UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_LOAD";
9485+
break;
9486+
case UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_STORE:
9487+
os << "UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_STORE";
9488+
break;
9489+
default:
9490+
os << "unknown enumerator";
9491+
break;
9492+
}
9493+
return os;
9494+
}
9495+
9496+
namespace ur::details {
9497+
///////////////////////////////////////////////////////////////////////////////
9498+
/// @brief Print ur_exp_device_2d_block_array_capability_flag_t flag
9499+
template <>
9500+
inline ur_result_t printFlag<ur_exp_device_2d_block_array_capability_flag_t>(std::ostream &os, uint32_t flag) {
9501+
uint32_t val = flag;
9502+
bool first = true;
9503+
9504+
if ((val & UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_LOAD) == (uint32_t)UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_LOAD) {
9505+
val ^= (uint32_t)UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_LOAD;
9506+
if (!first) {
9507+
os << " | ";
9508+
} else {
9509+
first = false;
9510+
}
9511+
os << UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_LOAD;
9512+
}
9513+
9514+
if ((val & UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_STORE) == (uint32_t)UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_STORE) {
9515+
val ^= (uint32_t)UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_STORE;
9516+
if (!first) {
9517+
os << " | ";
9518+
} else {
9519+
first = false;
9520+
}
9521+
os << UR_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_STORE;
9522+
}
9523+
if (val != 0) {
9524+
std::bitset<32> bits(val);
9525+
if (!first) {
9526+
os << " | ";
9527+
}
9528+
os << "unknown bit flags " << bits;
9529+
} else if (first) {
9530+
os << "0";
9531+
}
9532+
return UR_RESULT_SUCCESS;
9533+
}
9534+
} // namespace ur::details
9535+
///////////////////////////////////////////////////////////////////////////////
94589536
/// @brief Print operator for the ur_exp_image_copy_flag_t type
94599537
/// @returns
94609538
/// std::ostream &
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<%
2+
OneApi=tags['$OneApi']
3+
x=tags['$x']
4+
X=x.upper()
5+
%>
6+
7+
.. _experimental-2D-block-array-capabilities:
8+
9+
================================================================================
10+
2D Block Array Capabilities
11+
================================================================================
12+
13+
.. warning::
14+
15+
Experimental features:
16+
17+
* May be replaced, updated, or removed at any time.
18+
* Do not require maintaining API/ABI stability of their own additions over
19+
time.
20+
* Do not require conformance testing of their own additions.
21+
22+
23+
Motivation
24+
--------------------------------------------------------------------------------
25+
Some Intel GPU devices support 2D block array operations which may be used to optimize applications on Intel GPUs.
26+
This extension provides a device descriptor which allows to query the 2D block array capabilities of a device.
27+
28+
API
29+
--------------------------------------------------------------------------------
30+
31+
Enums
32+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
33+
34+
* ${x}_device_info_t
35+
* ${X}_DEVICE_INFO_2D_BLOCK_ARRAY_CAPABILITIES_EXP
36+
37+
* ${x}_exp_device_2d_block_array_capability_flags_t
38+
* ${X}_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_LOAD
39+
* ${X}_EXP_DEVICE_2D_BLOCK_ARRAY_CAPABILITY_FLAG_STORE
40+
41+
Changelog
42+
--------------------------------------------------------------------------------
43+
44+
+-----------+------------------------+
45+
| Revision | Changes |
46+
+===========+========================+
47+
| 1.0 | Initial Draft |
48+
+-----------+------------------------+
49+
50+
51+
Support
52+
--------------------------------------------------------------------------------
53+
54+
Adapters which support this experimental feature *must* return ${X}_RESULT_SUCCESS from
55+
the ${x}DeviceGetInfo call with the new ${X}_DEVICE_INFO_2D_BLOCK_ARRAY_CAPABILITIES_EXP
56+
device descriptor.
57+
58+
59+
Contributors
60+
--------------------------------------------------------------------------------
61+
62+
* Artur Gainullin `[email protected] <[email protected]>`_
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#
2+
# Copyright (C) 2024 Intel Corporation
3+
#
4+
# Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
5+
# See LICENSE.TXT
6+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
#
8+
# See YaML.md for syntax definition
9+
#
10+
--- #--------------------------------------------------------------------------
11+
type: header
12+
desc: "Intel $OneApi Unified Runtime Experimental device descriptor for querying Intel device 2D block array capabilities"
13+
ordinal: "99"
14+
--- #--------------------------------------------------------------------------
15+
type: enum
16+
extend: true
17+
typed_etors: true
18+
desc: "Extension enum to $x_device_info_t to query Intel device 2D block array capabilities."
19+
name: $x_device_info_t
20+
etors:
21+
- name: 2D_BLOCK_ARRAY_CAPABILITIES_EXP
22+
value: "0x2022"
23+
desc: "[$x_exp_device_2d_block_array_capability_flags_t] return a bit-field of Intel GPU 2D block array capabilities"
24+
--- #--------------------------------------------------------------------------
25+
type: enum
26+
desc: "Intel GPU 2D block array capabilities"
27+
class: $xDevice
28+
name: $x_exp_device_2d_block_array_capability_flags_t
29+
etors:
30+
- name: LOAD
31+
desc: "Load instructions are supported"
32+
value: "$X_BIT(0)"
33+
- name: STORE
34+
desc: "Store instructions are supported"
35+
value: "$X_BIT(1)"
36+

source/adapters/cuda/device.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,9 @@ UR_APIEXPORT ur_result_t UR_APICALL urDeviceGetInfo(ur_device_handle_t hDevice,
10881088
case UR_DEVICE_INFO_GPU_EU_COUNT_PER_SUBSLICE:
10891089
case UR_DEVICE_INFO_GPU_HW_THREADS_PER_EU:
10901090
return UR_RESULT_ERROR_UNSUPPORTED_ENUMERATION;
1091-
1091+
case UR_DEVICE_INFO_2D_BLOCK_ARRAY_CAPABILITIES_EXP:
1092+
return ReturnValue(
1093+
static_cast<ur_exp_device_2d_block_array_capability_flags_t>(0));
10921094
case UR_DEVICE_INFO_COMMAND_BUFFER_SUPPORT_EXP:
10931095
case UR_DEVICE_INFO_COMMAND_BUFFER_EVENT_SUPPORT_EXP:
10941096
return ReturnValue(true);

source/adapters/hip/device.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,9 @@ UR_APIEXPORT ur_result_t UR_APICALL urDeviceGetInfo(ur_device_handle_t hDevice,
905905
case UR_DEVICE_INFO_IL_VERSION:
906906
case UR_DEVICE_INFO_ASYNC_BARRIER:
907907
return UR_RESULT_ERROR_UNSUPPORTED_ENUMERATION;
908+
case UR_DEVICE_INFO_2D_BLOCK_ARRAY_CAPABILITIES_EXP:
909+
return ReturnValue(
910+
static_cast<ur_exp_device_2d_block_array_capability_flags_t>(0));
908911
case UR_DEVICE_INFO_COMMAND_BUFFER_SUPPORT_EXP: {
909912
int DriverVersion = 0;
910913
UR_CHECK_ERROR(hipDriverGetVersion(&DriverVersion));

source/adapters/level_zero/CMakeLists.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ if(UR_BUILD_ADAPTER_L0)
5858
# 'utils' target from 'level-zero-loader' includes path which is prefixed
5959
# in the source directory, this breaks the installation of 'utils' target.
6060
set_target_properties(utils PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "")
61-
install(TARGETS ur_umf LevelZeroLoader LevelZeroLoader-Headers ze_loader utils
61+
install(TARGETS ur_umf LevelZeroLoader LevelZeroLoader-Headers ComputeRuntimeLevelZero-Headers ze_loader utils
6262
EXPORT ${PROJECT_NAME}-targets
6363
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
6464
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
@@ -109,6 +109,7 @@ if(UR_BUILD_ADAPTER_L0)
109109
${PROJECT_NAME}::umf
110110
LevelZeroLoader
111111
LevelZeroLoader-Headers
112+
ComputeRuntimeLevelZero-Headers
112113
)
113114

114115
target_include_directories(ur_adapter_level_zero PRIVATE
@@ -203,6 +204,7 @@ if(UR_BUILD_ADAPTER_L0_V2)
203204
${PROJECT_NAME}::umf
204205
LevelZeroLoader
205206
LevelZeroLoader-Headers
207+
ComputeRuntimeLevelZero-Headers
206208
)
207209

208210
target_include_directories(ur_adapter_level_zero_v2 PRIVATE

source/adapters/level_zero/common.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "common.hpp"
1212
#include "logger/ur_logger.hpp"
1313
#include "usm.hpp"
14+
#include <level_zero/include/ze_intel_gpu.h>
1415

1516
ur_result_t ze2urResult(ze_result_t ZeResult) {
1617
if (ZeResult == ZE_RESULT_SUCCESS)
@@ -330,6 +331,14 @@ template <> zes_structure_type_t getZesStructureType<zes_mem_properties_t>() {
330331
return ZES_STRUCTURE_TYPE_MEM_PROPERTIES;
331332
}
332333

334+
#ifdef ZE_INTEL_DEVICE_BLOCK_ARRAY_EXP_NAME
335+
template <>
336+
ze_structure_type_t
337+
getZeStructureType<ze_intel_device_block_array_exp_properties_t>() {
338+
return ZE_INTEL_DEVICE_BLOCK_ARRAY_EXP_PROPERTIES;
339+
}
340+
#endif // ZE_INTEL_DEVICE_BLOCK_ARRAY_EXP_NAME
341+
333342
// Global variables for ZER_EXT_RESULT_ADAPTER_SPECIFIC_ERROR
334343
thread_local ur_result_t ErrorMessageCode = UR_RESULT_SUCCESS;
335344
thread_local char ErrorMessage[MaxMessageSize];

0 commit comments

Comments
 (0)