Skip to content

Commit 4cf53f1

Browse files
authored
[SYCL] add ESIMD tests (intel#29)
* add ESIMD tests * add ESIMD-specific LIT substitutions * align execution rules with llvm-test-suite
1 parent 7444a15 commit 4cf53f1

File tree

3 files changed

+275
-4
lines changed

3 files changed

+275
-4
lines changed

SYCL/ESIMD/accessor.cpp

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//==---------------- accessor.cpp - DPC++ ESIMD on-device test ------------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// TODO enable on Windows and Level Zero
9+
// REQUIRES: linux && gpu && opencl
10+
// RUN: %clangxx-esimd -fsycl -D_CRT_SECURE_NO_WARNINGS=1 %s -o %t.out
11+
// RUN: %ESIMD_RUN_PLACEHOLDER %t.out
12+
13+
// This test checks that accessor-based memory accesses work correctly in ESIMD.
14+
15+
#include "esimd_test_utils.hpp"
16+
17+
#include <CL/sycl.hpp>
18+
#include <CL/sycl/INTEL/esimd.hpp>
19+
20+
#include <iostream>
21+
22+
using namespace cl::sycl;
23+
24+
constexpr unsigned int VL = 1024 * 128;
25+
26+
using Ty = float;
27+
28+
int main() {
29+
Ty data0[VL] = {0};
30+
Ty data1[VL] = {0};
31+
constexpr Ty VAL = 5;
32+
33+
for (int i = 0; i < VL; i++) {
34+
data0[i] = i;
35+
}
36+
37+
try {
38+
queue q(esimd_test::ESIMDSelector{}, esimd_test::createExceptionHandler());
39+
40+
buffer<Ty, 1> buf0(data0, range<1>(VL));
41+
buffer<Ty, 1> buf1(data1, range<1>(VL));
42+
43+
q.submit([&](handler &cgh) {
44+
std::cout << "Running on "
45+
<< q.get_device().get_info<cl::sycl::info::device::name>()
46+
<< "\n";
47+
48+
auto acc0 = buf0.get_access<access::mode::read_write>(cgh);
49+
auto acc1 = buf1.get_access<access::mode::write>(cgh);
50+
51+
cgh.parallel_for<class Test>(
52+
range<1>(1), [=](sycl::id<1> i) SYCL_ESIMD_KERNEL {
53+
using namespace sycl::INTEL::gpu;
54+
unsigned int offset = 0;
55+
for (int k = 0; k < VL / 16; k++) {
56+
simd<Ty, 16> var = block_load<Ty, 16>(acc0, offset);
57+
var += VAL;
58+
block_store(acc0, offset, var);
59+
block_store(acc1, offset, var + 1);
60+
offset += 64;
61+
}
62+
});
63+
});
64+
65+
q.wait();
66+
67+
} catch (cl::sycl::exception const &e) {
68+
std::cout << "SYCL exception caught: " << e.what() << '\n';
69+
return 2;
70+
}
71+
72+
int err_cnt = 0;
73+
74+
for (int i = 0; i < VL; i++) {
75+
Ty gold0 = i + VAL;
76+
Ty gold1 = gold0 + 1;
77+
Ty val0 = data0[i];
78+
Ty val1 = data1[i];
79+
80+
if (val0 != gold0) {
81+
if (++err_cnt < 10)
82+
std::cerr << "*** ERROR at data0[" << i << "]: " << val0
83+
<< " != " << gold0 << "(gold)\n";
84+
}
85+
if (val1 != gold1) {
86+
if (++err_cnt < 10)
87+
std::cerr << "*** ERROR at data1[" << i << "]: " << val1
88+
<< " != " << gold1 << "(gold)\n";
89+
}
90+
}
91+
if (err_cnt == 0) {
92+
std::cout << "Passed\n";
93+
return 0;
94+
} else {
95+
std::cout << "Failed: " << err_cnt << " of " << VL << " errors\n";
96+
return 1;
97+
}
98+
}

SYCL/ESIMD/esimd_test_utils.hpp

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//==--------- esimd_test_utils.hpp - DPC++ ESIMD on-device test utilities --==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include <CL/sycl.hpp>
10+
11+
#define NOMINMAX
12+
13+
#include <algorithm>
14+
#include <cstring>
15+
#include <fstream>
16+
#include <iostream>
17+
#include <iterator>
18+
#include <vector>
19+
20+
using namespace cl::sycl;
21+
22+
namespace esimd_test {
23+
24+
// This is the class provided to SYCL runtime by the application to decide
25+
// on which device to run, or whether to run at all.
26+
// When selecting a device, SYCL runtime first takes (1) a selector provided by
27+
// the program or a default one and (2) the set of all available devices. Then
28+
// it passes each device to the '()' operator of the selector. Device, for
29+
// which '()' returned the highest number, is selected. If a negative number
30+
// was returned for all devices, then the selection process will cause an
31+
// exception.
32+
class ESIMDSelector : public device_selector {
33+
// Require GPU device unless HOST is requested in SYCL_DEVICE_TYPE env
34+
virtual int operator()(const device &device) const {
35+
if (const char *dev_type = getenv("SYCL_DEVICE_TYPE")) {
36+
if (!strcmp(dev_type, "GPU"))
37+
return device.is_gpu() ? 1000 : -1;
38+
if (!strcmp(dev_type, "HOST"))
39+
return device.is_host() ? 1000 : -1;
40+
std::cerr << "Supported 'SYCL_DEVICE_TYPE' env var values are 'GPU' and "
41+
"'HOST', '"
42+
<< dev_type << "' is not.\n";
43+
return -1;
44+
}
45+
// If "SYCL_DEVICE_TYPE" not defined, only allow gpu device
46+
return device.is_gpu() ? 1000 : -1;
47+
}
48+
};
49+
50+
inline auto createExceptionHandler() {
51+
return [](exception_list l) {
52+
for (auto ep : l) {
53+
try {
54+
std::rethrow_exception(ep);
55+
} catch (cl::sycl::exception &e0) {
56+
std::cout << "sycl::exception: " << e0.what() << std::endl;
57+
} catch (std::exception &e) {
58+
std::cout << "std::exception: " << e.what() << std::endl;
59+
} catch (...) {
60+
std::cout << "generic exception\n";
61+
}
62+
}
63+
};
64+
}
65+
66+
template <typename T>
67+
std::vector<T> read_binary_file(const char *fname, size_t num = 0) {
68+
std::vector<T> vec;
69+
std::ifstream ifs(fname, std::ios::in | std::ios::binary);
70+
if (ifs.good()) {
71+
ifs.unsetf(std::ios::skipws);
72+
std::streampos file_size;
73+
ifs.seekg(0, std::ios::end);
74+
file_size = ifs.tellg();
75+
ifs.seekg(0, std::ios::beg);
76+
size_t max_num = file_size / sizeof(T);
77+
vec.resize(num ? (std::min)(max_num, num) : max_num);
78+
ifs.read(reinterpret_cast<char *>(vec.data()), vec.size() * sizeof(T));
79+
}
80+
return vec;
81+
}
82+
83+
template <typename T>
84+
bool write_binary_file(const char *fname, const std::vector<T> &vec,
85+
size_t num = 0) {
86+
std::ofstream ofs(fname, std::ios::out | std::ios::binary);
87+
if (ofs.good()) {
88+
ofs.write(reinterpret_cast<const char *>(&vec[0]),
89+
(num ? num : vec.size()) * sizeof(T));
90+
ofs.close();
91+
}
92+
return !ofs.bad();
93+
}
94+
95+
template <typename T>
96+
bool cmp_binary_files(const char *fname1, const char *fname2, T tolerance) {
97+
const auto vec1 = read_binary_file<T>(fname1);
98+
const auto vec2 = read_binary_file<T>(fname2);
99+
if (vec1.size() != vec2.size()) {
100+
std::cerr << fname1 << " size is " << vec1.size();
101+
std::cerr << " whereas " << fname2 << " size is " << vec2.size()
102+
<< std::endl;
103+
return false;
104+
}
105+
for (size_t i = 0; i < vec1.size(); i++) {
106+
if (abs(vec1[i] - vec2[i]) > tolerance) {
107+
std::cerr << "Mismatch at " << i << ' ';
108+
if (sizeof(T) == 1) {
109+
std::cerr << (int)vec1[i] << " vs " << (int)vec2[i] << std::endl;
110+
} else {
111+
std::cerr << vec1[i] << " vs " << vec2[i] << std::endl;
112+
}
113+
return false;
114+
}
115+
}
116+
return true;
117+
}
118+
119+
// dump every element of sequence [first, last) to std::cout
120+
template <typename ForwardIt> void dump_seq(ForwardIt first, ForwardIt last) {
121+
using ValueT = typename std::iterator_traits<ForwardIt>::value_type;
122+
std::copy(first, last, std::ostream_iterator<ValueT>{std::cout, " "});
123+
std::cout << std::endl;
124+
}
125+
126+
// Checks wether ranges [first, last) and [ref_first, ref_last) are equal.
127+
// If a mismatch is found, dumps elements that differ and returns true,
128+
// otherwise false is returned.
129+
template <typename ForwardIt, typename RefForwardIt, typename BinaryPredicateT>
130+
bool check_fail_seq(ForwardIt first, ForwardIt last, RefForwardIt ref_first,
131+
RefForwardIt ref_last, BinaryPredicateT is_equal) {
132+
auto mism = std::mismatch(first, last, ref_first, is_equal);
133+
if (mism.first != last) {
134+
std::cout << "mismatch: returned " << *mism.first << std::endl;
135+
std::cout << " expected " << *mism.second << std::endl;
136+
return true;
137+
}
138+
return false;
139+
}
140+
141+
template <typename ForwardIt, typename RefForwardIt>
142+
bool check_fail_seq(ForwardIt first, ForwardIt last, RefForwardIt ref_first,
143+
RefForwardIt ref_last) {
144+
return check_fail_seq(
145+
first, last, ref_first, ref_last,
146+
[](const auto &lhs, const auto &rhs) { return lhs == rhs; });
147+
}
148+
149+
// analog to C++20 bit_cast
150+
template <typename To, typename From,
151+
typename std::enable_if<(sizeof(To) == sizeof(From)) &&
152+
std::is_trivially_copyable<From>::value &&
153+
std::is_trivial<To>::value,
154+
int>::type = 0>
155+
To bit_cast(const From &src) noexcept {
156+
To dst;
157+
std::memcpy(&dst, &src, sizeof(To));
158+
return dst;
159+
}
160+
161+
} // namespace esimd_test

SYCL/lit.cfg.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@
3838
llvm_config.with_system_environment(['PATH', 'OCL_ICD_FILENAMES',
3939
'CL_CONFIG_DEVICES', 'SYCL_DEVICE_ALLOWLIST', 'SYCL_CONFIG_FILE_NAME'])
4040

41-
config.substitutions.append( ('%clangxx', ' '+ config.dpcpp_compiler + ' ' + config.cxx_flags ) )
42-
config.substitutions.append( ('%clang', ' ' + config.dpcpp_compiler + ' ' + config.c_flags ) )
43-
config.substitutions.append( ('%threads_lib', config.sycl_threads_lib) )
44-
4541
llvm_config.with_environment('PATH', config.lit_tools_dir, append_path=True)
4642

4743
# Configure LD_LIBRARY_PATH or corresponding os-specific alternatives
@@ -102,6 +98,20 @@
10298
config.sycl_be +
10399
"' supported values are PI_OPENCL, PI_CUDA, PI_LEVEL_ZERO")
104100

101+
# ESIMD-specific setup. Requires OpenCL for now.
102+
if "opencl" in config.available_features:
103+
print(config.available_features)
104+
esimd_run_substitute = " env SYCL_BE=PI_OPENCL SYCL_DEVICE_TYPE=GPU SYCL_PROGRAM_COMPILE_OPTIONS=-cmc"
105+
config.substitutions.append( ('%ESIMD_RUN_PLACEHOLDER', esimd_run_substitute) )
106+
config.substitutions.append( ('%clangxx-esimd', config.dpcpp_compiler +
107+
' ' + '-fsycl-explicit-simd' + ' ' +
108+
config.cxx_flags ) )
109+
110+
config.substitutions.append( ('%clangxx', ' '+ config.dpcpp_compiler + ' ' + config.cxx_flags ) )
111+
config.substitutions.append( ('%clang', ' ' + config.dpcpp_compiler + ' ' + config.c_flags ) )
112+
config.substitutions.append( ('%threads_lib', config.sycl_threads_lib) )
113+
114+
105115
# Configure device-specific substitutions based on availability of corresponding
106116
# devices/runtimes
107117

@@ -158,6 +168,7 @@
158168
config.substitutions.append( ('%CPU_CHECK_PLACEHOLDER', cpu_check_substitute) )
159169
config.substitutions.append( ('%CPU_CHECK_ON_LINUX_PLACEHOLDER', cpu_check_on_linux_substitute) )
160170

171+
esimd_run_substitute = "true"
161172
gpu_run_substitute = "true"
162173
gpu_run_on_linux_substitute = "true "
163174
gpu_check_substitute = ""
@@ -173,6 +184,7 @@
173184
if platform.system() == "Linux":
174185
gpu_run_on_linux_substitute = "env SYCL_DEVICE_TYPE=GPU SYCL_BE={SYCL_BE} ".format(SYCL_BE=config.sycl_be)
175186
gpu_check_on_linux_substitute = "| FileCheck %s"
187+
176188
else:
177189
lit_config.warning("GPU device not used")
178190

0 commit comments

Comments
 (0)