Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion libsycl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,14 @@ add_custom_target(libsycl-runtime-libraries
)

add_subdirectory(src)

add_subdirectory(tools)

if(LLVM_INCLUDE_TESTS)
add_subdirectory(test_e2e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
add_subdirectory(test_e2e)
add_subdirectory(test)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left e2e in the name intentionally. In the end we will have same test folders as other projects: test, unittests + test_e2e. As in intel/llvm unittests and test won't require backend runtimes to be present on system to do check. while e2e testing require and so can't be joined with test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my point of view, we have two top level test directories artificially created due to attempting to move some tests requiring third-party software/hardware to llvm-test-suite repository.
I see no reason to complicate the implementation with this separation. I don't see such complications in other runtime library projects depending on third-party software/hardware (e.g. liboffload, libc++, etc.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have different configs for them. lit config and features for e2e tests are way more complex that is needed for test. test require compilation + sometimes tools like objdump to be run. That's it. I lot of stuff related to device choice, runtimes, build-only/run-only has no sense for that kind of tests.
I would prefer 2 different setups doing exactly what is needed for each kind of tests than merge them and have a monstrous config that should handle everything.
plus it may be easier to avoid a mess when compile-only and e2e tests are separated. we can have ./test/compile-only and ./test/e2e if you want to keep it under 'test' top folder but I don't see why it is better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's simpler. I don't understand why we need to maintain to different LIT config files when "compile-only" tests are sub-set of regular tests. We can use single config for both types of tests.

endif()

add_custom_target(libsycl-toolchain ALL
DEPENDS libsycl-runtime-libraries
sycl-ls
COMMENT "Building libsycl toolchain..."
)
26 changes: 26 additions & 0 deletions libsycl/test_e2e/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.20.0)

message("Configuring libsycl End-to-End Tests")

set(LIBSYCL_CXX_COMPILER "${LLVM_BINARY_DIR}/bin/clang++")
set(LIBSYCL_E2E_CXX_FLAGS "" CACHE STRING
"Flags passed to clang++ when building SYCL end-to-end tests")

configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py
MAIN_CONFIG
${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py
)

list(APPEND LIBSYCL_E2E_TEST_DEPS
libsycl-toolchain
FileCheck
not
)

add_lit_testsuite(check-sycl-e2e
"Running libsycl End-to-End tests"
${CMAKE_CURRENT_BINARY_DIR}
EXCLUDE_FROM_CHECK_ALL
DEPENDS ${LIBSYCL_E2E_TEST_DEPS})
84 changes: 84 additions & 0 deletions libsycl/test_e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
## Getting Started

This directory contains `libsycl` tests distributed in subdirectories based on
testing scope. `libsycl` uses LIT to configure and run its tests.

Please see the [Lit Command Guide](https://llvm.org/docs/CommandGuide/lit.html)
for more information about LIT.

## Prerequisites

* Target runtime(s) to execute tests on devices.
TODO: add link to liboffload instruction once they add it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't liboffload just be there if you build clang/llvm with sycl support? I was expecting that here will be a list of runtimes such as L0 which must be available for the test to run. Should we list here these runtimes one by one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your understanding is correct and the idea is to have an instruction about L0/CUDA/HIP runtimes setup here. And I believe this instruction must be provided on liboffload level since it is the runtime that directly works with them. This instruction is absent since liboffload has poor (and/or absent) documentation now. Intel liboffload team has docs in TODO list and will add it once it will be ready.

* Compiler & libsycl. Can be built following these
[instructions](/libsycl/docs/index.rst).

## Run the tests

`libsycl` is integrated via LLVM_ENABLE_RUNTIMES and is not visible as top
level target. Same is applicable for tests. To run `check-sycl-e2e` tests you
need to prefix `<build>/runtimes/runtimes-bins/` to the paths of all tests.
For example, to run all the libsycl end-to-end tests you can do:
```bash
<build>/bin/llvm-lit <build>/runtimes/runtimes-bins/libsycl/test_e2e
```

To run individual test, use the path to it instead.

If you are using `ninja` as your build system, you can run all the tests in the
libsycl testsuite as:

```bash
ninja -C <build>/runtimes/runtimes-bins check-sycl-e2e
```


## CMake parameters

These parameters can be used to configure tests:

`LIBSYCL_CXX_COMPILER` - path to compiler to use it for building tests.

`LIBSYCL_E2E_CXX_FLAGS` - flags to be passed to `LIBSYCL_CXX_COMPILER` when
building libsycl end-to-end tests.

`LLVM_LIT` - path to llvm-lit tool.

## Creating or modifying tests

### LIT feature checks

Following features can be passed to LIT via `REQUIRES`, `UNSUPPORTED`, etc.
filters to limit test execution to the specific environment.

#### Auto-detected features

The following features are automatically detected by `llvm-lit` by scanning the
environment:

* `linux` - host OS;
* `any-device-is-gpu` - device type to be available;
* `any-device-is-level_zero` - backend to be available;

Note: `sycl-ls` tool doesn't have assigned feature since it is essential for
tests configuration and is always available if test is executed.

### llvm-lit parameters

Following options can be passed to `llvm-lit` tool with `--param` option to
configure test execution:

* `libsycl_compiler` - full path to compiler to use;
* `extra_environment` - comma-separated list of variables with values to be
added to test environment. Can be also set by `LIT_EXTRA_ENVIRONMENT`
variable in CMake.
* `extra_system_environment` - comma-separated list of variables to be
propagated from the host environment to test environment. Can be also set by
`LIT_EXTRA_SYSTEM_ENVIRONMENT` variable in CMake.

Example:

```bash
<build>/bin/llvm-lit --param libsycl_compiler=path/to/clang++ \
<build>/runtimes/runtimes-bins/libsycl/test_e2e
```
121 changes: 121 additions & 0 deletions libsycl/test_e2e/basic/platform_get_devices.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// RUN: %clangxx %sycl_options %s -o %t.out
// RUN: %t.out
//
// Tests platform::get_devices for each device type.

#include <sycl.hpp>

#include <algorithm>
#include <iostream>

std::string BackendToString(sycl::backend Backend) {
switch (Backend) {
case sycl::backend::opencl:
return "opencl";
case sycl::backend::level_zero:
return "level_zero";
case sycl::backend::cuda:
return "cuda";
case sycl::backend::hip:
return "hip";
default:
return "unknown";
}
}

std::string DeviceTypeToString(sycl::info::device_type DevType) {
switch (DevType) {
case sycl::info::device_type::all:
return "device_type::all";
case sycl::info::device_type::cpu:
return "device_type::cpu";
case sycl::info::device_type::gpu:
return "device_type::gpu";
case sycl::info::device_type::accelerator:
return "device_type::accelerator";
case sycl::info::device_type::custom:
return "device_type::custom";
case sycl::info::device_type::automatic:
return "device_type::automatic";
case sycl::info::device_type::host:
return "device_type::host";
default:
return "UNKNOWN";
}
}

std::string GenerateDeviceDescription(sycl::info::device_type DevType,
const sycl::platform &Platform) {
return std::string(DeviceTypeToString(DevType)) + " (" +
BackendToString(Platform.get_backend()) + ")";
}

template <typename T1, typename T2>
int Check(const T1 &LHS, const T2 &RHS, std::string TestName) {
if (LHS != RHS) {
std::cerr << "Failed check " << LHS << " != " << RHS << ": " << TestName
<< std::endl;
return 1;
}
return 0;
}

int CheckDeviceType(const sycl::platform &P, sycl::info::device_type DevType,
std::vector<sycl::device> &AllDevices) {
assert(DevType != sycl::info::device_type::all);
int Failures = 0;

std::vector<sycl::device> Devices = P.get_devices(DevType);

if (DevType == sycl::info::device_type::automatic) {
if (AllDevices.empty()) {
Failures += Check(Devices.size(), 0,
"No devices reported for device_type::all query, but "
"device_type::automatic returns a device.");
} else {
Failures += Check(Devices.size(), 1,
"Number of devices for device_type::automatic query.");
if (Devices.size())
Failures += Check(
std::count(AllDevices.begin(), AllDevices.end(), Devices[0]), 1,
"Device is in the set of device_type::all devices in the "
"platform.");
}
return Failures;
}

// Count devices with the type;
size_t DevCount = 0;
for (sycl::device Device : Devices)
DevCount += (Device.get_info<sycl::info::device::device_type>() == DevType);

Failures += Check(Devices.size(), DevCount,
"Unexpected number of devices for " +
GenerateDeviceDescription(DevType, P));

Failures +=
Check(std::all_of(Devices.begin(), Devices.end(),
[&](const auto &Dev) {
return std::count(AllDevices.begin(),
AllDevices.end(), Dev) == 1;
}),
true,
"Not all devices for " + GenerateDeviceDescription(DevType, P) +
" appear in the list of all devices");

return Failures;
}

int main() {
int Failures = 0;
for (sycl::platform P : sycl::platform::get_platforms()) {
std::vector<sycl::device> Devices = P.get_devices();

for (sycl::info::device_type DevType :
{sycl::info::device_type::cpu, sycl::info::device_type::gpu,
sycl::info::device_type::accelerator, sycl::info::device_type::custom,
sycl::info::device_type::automatic, sycl::info::device_type::host})
Failures += CheckDeviceType(P, DevType, Devices);
}
return Failures;
}
Loading
Loading