[PATCH v2 39/47] testsuite, sycl: add SYCL support

Tankut Baris Aktemur
2024-12-13T16:32:56Z

Add lib files, board file, and basic test scenarios for SYCL. The
scenarios test the ability to define and hit breakpoints inside and
outside a SYCL kernel. The test cases are skipped if it is found out
that the compiler does not support SYCL.

Here is a sample command to run all SYCL tests on the CPU target
device using the Intel® oneAPI DPC++/C++ Compiler:

$ make check TESTS="gdb.sycl/*.exp" \
RUNTESTFLAGS="OFFLOAD_DEVICE_GROUP=cpu CXX_FOR_TARGET=icpx"

Alternatively, the intel-offload board file can be used. This can be
more practical to pick the right compilers.

$ make check TESTS="gdb.sycl/*.exp" \
RUNTESTFLAGS="OFFLOAD_DEVICE_GROUP=cpu --target_board='intel-offload'"

Running the GPU tests requires that a gdbserver executable built for
the intelgt target exists in the path and its name is
'gdbserver-intelgt'.

Co-authored-by: Abdul Basit Ijaz <abdul.b.ijaz@intel.com>
Co-authored-by: Stephan Rohr <stephan.rohr@intel.com>
---
gdb/testsuite/README | 9 +
gdb/testsuite/boards/intel-offload.exp | 36 +++
gdb/testsuite/gdb.sycl/break.exp | 63 +++++
gdb/testsuite/gdb.sycl/break2.exp | 66 ++++++
gdb/testsuite/gdb.sycl/single-task.cpp | 50 ++++
gdb/testsuite/lib/gdb.exp | 17 +-
gdb/testsuite/lib/intelgt-utils.exp | 43 ++++
gdb/testsuite/lib/sycl-devices.cpp | 107 +++++++++
gdb/testsuite/lib/sycl-hello.cpp | 43 ++++
gdb/testsuite/lib/sycl-util.cpp | 135 +++++++++++
gdb/testsuite/lib/sycl.exp | 410 +++++++++++++++++++++++++++++++++
11 files changed, 978 insertions(+), 1 deletion(-)

diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index 06664d723e7902a22fc9a0e63627e2780fae8d22..f18f205a05091109d8ec4585c12455cc6e4724d4 100644
--- a/gdb/testsuite/README
+++ b/gdb/testsuite/README
@@ -340,6 +340,15 @@ by switching to a different user on the same machine. These users
will have random files copied into their $HOME directories, so it is a
good idea to setup new users just for this purpose.

+OFFLOAD_DEVICE_GROUP
+
+This option can be used to restrict the offloading tests to run only
+on a specific group/family of devices. Multiple target device groups
+can be selected by passing a comma separated list. By default, it is
+set to "cpu,gpu,accelerator". Example use:
+
+ make check RUNTESTFLAGS='OFFLOAD_DEVICE_GROUP="gpu,cpu"'
+
Testing All Simple Boards
*************************

diff --git a/gdb/testsuite/boards/intel-offload.exp b/gdb/testsuite/boards/intel-offload.exp
new file mode 100755
index 0000000000000000000000000000000000000000..5045a7718c9b068e0afc46de10c88de40aeba2fa
--- /dev/null
+++ b/gdb/testsuite/boards/intel-offload.exp
@@ -0,0 +1,36 @@
+# Copyright 2022-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is a dejagnu "board file" and is used for running the
+# SYCL testsuite.
+#
+# Example usage:
+# bash$ make check TESTS="gdb.sycl/*.exp" RUNTESTFLAGS='--target_board=intel-offload'
+
+load_generic_config "unix"
+process_multilib_options ""
+load_board_description "local-board"
+
+set gdb_test_timeout 100
+
+unset_board_info isremote
+set_board_info isremote 0
+
+set_board_info compiler "icx"
+set_board_info c++compiler "icpx"
+set_board_info f90compiler "ifx"
+
+puts "Info: Using timeout value $gdb_test_timeout"
+puts "Info: Using C++ compiler icpx and Fortran compiler ifx"
diff --git a/gdb/testsuite/gdb.sycl/break.exp b/gdb/testsuite/gdb.sycl/break.exp
new file mode 100644
index 0000000000000000000000000000000000000000..fe45a154bc015bf978a76b410758e7bb625c1f87
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break.exp
@@ -0,0 +1,63 @@
+# Copyright 2019-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDB's support for SYCL; in particular, inserting and hitting
+# breakpoints inside and outside a kernel.
+
+load_lib sycl.exp
+
+standard_testfile single-task.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ # Set breakpoints inside and outside the kernel.
+ array set bp_locations_kernel {}
+ set num_kernel_locations 4
+
+ gdb_breakpoint "$srcfile:[gdb_get_line_number line-after-kernel]" \
+ {message}
+
+ for {set i 1} {$i <= $num_kernel_locations} {incr i} {
+ set bp_locations_kernel($i) [gdb_get_line_number "kernel-line-$i"]
+ gdb_breakpoint "$srcfile:$bp_locations_kernel($i)" {message}
+ }
+
+ # Test that we actually hit the breakpoints.
+ for {set i 1} {$i <= $num_kernel_locations} {incr i} {
+ gdb_continue_to_breakpoint "kernel line $i" \
+ ".*$srcfile:$bp_locations_kernel($i).*"
+ }
+
+ gdb_continue_to_breakpoint "line after kernel" \
+ ".*$srcfile:[gdb_get_line_number line-after-kernel].*"
+ }
+}
diff --git a/gdb/testsuite/gdb.sycl/break2.exp b/gdb/testsuite/gdb.sycl/break2.exp
new file mode 100644
index 0000000000000000000000000000000000000000..33ec33addf739f476615303c7574f67eae109d87
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break2.exp
@@ -0,0 +1,66 @@
+# Copyright 2019-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDB's support for SYCL; in particular, inserting and hitting
+# breakpoints inside and outside a kernel. This test is similar to
+# break.exp except that not all the breakpoints are defined at the
+# beginning. A couple in- and out-kernel breakpoints are defined when
+# we are inside the kernel.
+
+load_lib sycl.exp
+
+standard_testfile single-task.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return 0
+}
+
+if {[build_executable "failed to compile $srcfile" \
+ "${binfile}" $srcfile {sycl debug}]} {
+ return -1
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart "${binfile}"
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ # Set breakpoints inside and outside the kernel.
+ set after_kernel [gdb_get_line_number "line-after-kernel"]
+ set inside_kernel_1 [gdb_get_line_number "kernel-line-1"]
+ set inside_kernel_2 [gdb_get_line_number "kernel-line-2"]
+ set inside_kernel_3 [gdb_get_line_number "kernel-line-3"]
+ set inside_kernel_4 [gdb_get_line_number "kernel-line-4"]
+
+ gdb_breakpoint $inside_kernel_2 {message}
+ gdb_continue_to_breakpoint "kernel line 2" \
+ ".*$srcfile:$inside_kernel_2.*"
+
+ # Now we are inside the kernel and defining a kernel breakpoint.
+ gdb_breakpoint $inside_kernel_4 {message}
+ gdb_continue_to_breakpoint "kernel line 4" \
+ ".*$srcfile:$inside_kernel_4.*"
+
+ # We are still inside the kernel and defining a host breakpoint.
+ gdb_breakpoint $after_kernel {message}
+ gdb_continue_to_breakpoint "line after kernel" \
+ ".*$srcfile:$after_kernel.*"
+ }
+}
diff --git a/gdb/testsuite/gdb.sycl/single-task.cpp b/gdb/testsuite/gdb.sycl/single-task.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dc6105c03174da7ba7af98dce59ff1b5d95831c8
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/single-task.cpp
@@ -0,0 +1,50 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include "../lib/sycl-util.cpp"
+
+int
+main (int argc, char *argv[])
+{
+ int data[3] = {7, 8, 9};
+
+ { /* Extra scope enforces waiting on the kernel. */
+ sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+ sycl::buffer<int, 1> buf {data, sycl::range<1> {3}};
+
+ deviceQueue.submit ([&] (sycl::handler& cgh) /* line-before-kernel */
+ {
+ auto numbers = buf.get_access<sycl::access::mode::read_write> (cgh);
+
+ cgh.single_task<class simple_kernel> ([=] ()
+ {
+ int ten = numbers[1] + 2; /* kernel-line-1 */
+ int four = numbers[2] - 5; /* kernel-line-2 */
+ int fourteen = ten + four; /* kernel-line-3 */
+ numbers[0] = fourteen * 3; /* kernel-line-4 */
+ });
+ });
+ }
+
+#ifndef OMIT_REPORT
+ std::cout << "Result is " << data[0] << std::endl; /* line-after-kernel */
+#endif
+
+ return data[0] == 42 ? 0 : 1; /* return-stmt */
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 7ee2043f0f88602f64943e5083be2566b1f33692..d7179db4af47b2e1f01d73875a4294329ab16112 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -6526,6 +6526,21 @@ proc gdb_compile_openmp {source dest type options} {
return [gdb_compile $source $dest $type $options]
}

+# Build a SYCL program from SOURCE. See prefatory comment for
+# gdb_compile, above, for discussion of the parameters to this proc.
+
+proc gdb_compile_sycl {source dest type options} {
+ set new_options {additional_flags=-fsycl}
+ lappend new_options {additional_flags=-fsycl-unnamed-lambda}
+ lappend new_options {c++}
+ lappend new_options {optimize=-O0}
+
+ # If the optimize option is given multiple times, only the last use is
+ # significant in DEJAGNU. So, to make optimize option set in the test
+ # significant, input 'options' are appended to the end here.
+ return [gdb_compile $source $dest $type [concat $new_options $options]]
+}
+
# Send a command to GDB.
# For options for TYPE see gdb_stdin_log_write

@@ -8729,7 +8744,7 @@ proc build_executable_from_specs {testname executable options args} {
set binfile [standard_output_file $executable]

set func gdb_compile
- set func_index [lsearch -regexp $options {^(pthreads|shlib|shlib_pthreads|openmp)$}]
+ set func_index [lsearch -regexp $options {^(pthreads|shlib|shlib_pthreads|openmp|sycl)$}]
if {$func_index != -1} {
set func "${func}_[lindex $options $func_index]"
}
diff --git a/gdb/testsuite/lib/intelgt-utils.exp b/gdb/testsuite/lib/intelgt-utils.exp
new file mode 100644
index 0000000000000000000000000000000000000000..6e99c0fe78c74dd12b3c6f97eff30f86bbe97d3f
--- /dev/null
+++ b/gdb/testsuite/lib/intelgt-utils.exp
@@ -0,0 +1,43 @@
+# Copyright 2020-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Intelgt utility procedures.
+
+# The lock file used to ensure that only one GDB has access to the GPU
+# at a time.
+
+set intelgt_lock_filename intelgt-parallel.lock
+
+# Return non-zero if the instruction at ADDRESS is compact, 0 otherwise.
+
+proc is_compact_insn {address} {
+ # Check the CmptCtrl flag (bit 29).
+ set test "is compact insn"
+ set is_compact [get_integer_valueof \
+ "((unsigned char *)${address})\[3\] & 0x20" 0 $test]
+ return $is_compact
+}
+
+# Set the breakpoint bit of the instruction at ADDRESS.
+
+proc_with_prefix set_breakpoint_bit {address} {
+ # Set Bit 7 on a compacted instruction, Bit 30 on a full instruction.
+ set test "set bp bit"
+ if {[is_compact_insn $address]} {
+ gdb_test "print/x ((unsigned char *)${address})\[0\] |= 0x80" "" $test
+ } else {
+ gdb_test "print/x ((unsigned char *)${address})\[3\] |= 0x40" "" $test
+ }
+}
diff --git a/gdb/testsuite/lib/sycl-devices.cpp b/gdb/testsuite/lib/sycl-devices.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ac1ca67faaf31934576a2b8c0932ca9165f8df04
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-devices.cpp
@@ -0,0 +1,107 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2022-2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Utility file for SYCL test programs to get list of available devices. */
+
+#include <sycl/sycl.hpp>
+#include
+
+static std::string
+get_backend_name (sycl::backend backend_arg)
+{
+ std::string backend_name;
+
+ if (backend_arg == sycl::backend::opencl)
+ backend_name = "opencl";
+ else if (backend_arg == sycl::backend::ext_oneapi_level_zero)
+ backend_name = "ext_oneapi_level_zero";
+ else
+ {
+ std::cout << "SYCL: Unrecognized backend." << std::endl;
+ exit (1);
+ }
+
+ return backend_name;
+}
+
+static std::string
+get_device_type (sycl::info::device_type type)
+{
+ std::string type_name;
+
+ if (type == sycl::info::device_type::cpu)
+ type_name = "cpu";
+ else if (type == sycl::info::device_type::gpu)
+ type_name = "gpu";
+ else if (type == sycl::info::device_type::accelerator)
+ type_name = "accelerator";
+ else
+ {
+ std::cout << "SYCL: Unrecognized device type." << std::endl;
+ exit (1);
+ }
+
+ return type_name;
+}
+
+int
+main ()
+{
+ const std::vector<sycl::device> devices
+ = sycl::device::get_devices (sycl::info::device_type::all);
+
+ if (devices.empty ())
+ {
+ std::cout << "SYCL: Could not find any device" << std::endl;
+ exit (1);
+ }
+
+ std::map<std::string, int> device_types;
+
+ for (const sycl::device &device : devices)
+ {
+ const std::string dev_name
+ = device.get_info<sycl::info::device::name> ();
+ const std::string version
+ = device.get_info<sycl::info::device::driver_version> ();
+ const std::string backend_name
+ = get_backend_name (device.get_backend ());
+
+ const std::string type
+ = get_device_type (device.get_info<sycl::info::device::device_type> ());
+
+ if (backend_name == "")
+ continue;
+
+ std::string dev_key {dev_name + ";" + backend_name + ";" + version
+ + ";" + type};
+ device_types[dev_key]++;
+ }
+
+ std::cout << "SYCL: List of Target devices: [";
+ int index = 0;
+ for (const auto& [dev_key, count] : device_types)
+ {
+ index++;
+ std::cout << dev_key << ";" << count;
+ if (index < device_types.size ())
+ std::cout << ",";
+ }
+ std::cout << "]" << std::endl;
+
+ return 0;
+}
diff --git a/gdb/testsuite/lib/sycl-hello.cpp b/gdb/testsuite/lib/sycl-hello.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8de4892ce31d2111e2c11f15b72226c80724eac5
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-hello.cpp
@@ -0,0 +1,43 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include "sycl-util.cpp"
+
+static int numbers[8];
+
+int
+main (int argc, char *argv[])
+{
+ sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+ sycl::range<1> length {8};
+
+ { /* Extra scope enforces waiting on the kernel. */
+ sycl::buffer<int, 1> buf {numbers, length};
+ deviceQueue.submit ([&] (sycl::handler& cgh)
+ {
+ auto accessor = buf.get_access<sycl::access::mode::read_write> (cgh);
+
+ cgh.parallel_for<class SyclHello> (length, [=] (sycl::id<1> wiID)
+ {
+ accessor[wiID] = wiID[0] + 1; /* inside-kernel */
+ });
+ });
+ }
+
+ return 0;
+}
diff --git a/gdb/testsuite/lib/sycl-util.cpp b/gdb/testsuite/lib/sycl-util.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99e6981d8b2df84cfc4f0540a1111fdadd095416
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-util.cpp
@@ -0,0 +1,135 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Utility file for SYCL test programs to enable explicit selection of
+ a SYCL device. Include this file in each SYCL test program. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include <vector>
+
+static sycl::info::device_type
+get_device_type (const std::string &type_arg)
+{
+ sycl::info::device_type type;
+
+ if (type_arg.compare ("cpu") == 0)
+ type = sycl::info::device_type::cpu;
+ else if (type_arg.compare ("gpu") == 0)
+ type = sycl::info::device_type::gpu;
+ else if (type_arg.compare ("accelerator") == 0)
+ type = sycl::info::device_type::accelerator;
+ else
+ {
+ std::cout << "SYCL: Unrecognized device type '"
+ << type_arg << "'" << std::endl;
+ exit (1);
+ }
+
+ return type;
+}
+
+static sycl::backend
+get_backend_type (const std::string &backend_arg)
+{
+ sycl::backend backend;
+
+ if (backend_arg.compare ("opencl") == 0)
+ backend = sycl::backend::opencl;
+ else if (backend_arg.compare ("ext_oneapi_level_zero") == 0
+ || backend_arg.compare ("level_zero") == 0)
+ backend = sycl::backend::ext_oneapi_level_zero;
+ else
+ {
+ std::cout << "SYCL: Unrecognized backend '"
+ << backend_arg << "'" << std::endl;
+ exit (1);
+ }
+
+ return backend;
+}
+
+static std::vector<sycl::device>
+get_sycl_devices (int argc, char *argv[])
+{
+ if (argc <= 3)
+ {
+ std::cout << "Usage: " << argv[0]
+ << " <cpu|gpu|accelerator>"
+ << " <device name substring>"
+ << " <backend name opencl|level_zero>" << std::endl;
+ exit (1);
+ }
+
+ std::string type_arg {argv[1]};
+ std::string name_arg {argv[2]};
+ std::string backend_arg {argv[3]};
+
+ sycl::info::device_type type = get_device_type (type_arg);
+ sycl::backend backend_type = get_backend_type (backend_arg);
+
+ std::vector<sycl::device> devices = sycl::device::get_devices (type);
+
+ std::vector<sycl::device> filtered_devices;
+ for (const sycl::device &device : devices)
+ {
+ std::string dev_name = device.get_info<sycl::info::device::name> ();
+ std::string platform_name
+ = device.get_platform ().get_info<sycl::info::platform::name> ();
+ std::string version
+ = device.get_info<sycl::info::device::driver_version> ();
+ sycl::backend backend = device.get_backend ();
+
+ if (dev_name.find (name_arg) != std::string::npos
+ && backend == backend_type)
+ filtered_devices.push_back (device);
+ }
+
+ if (filtered_devices.empty ())
+ {
+ std::cout << "SYCL: Could not select a device" << std::endl;
+ exit (1);
+ }
+
+ return filtered_devices;
+}
+
+static void
+print_device (const sycl::device &device)
+{
+ std::string dev_name
+ = device.get_info<sycl::info::device::name> ();
+ std::string platform_name
+ = device.get_platform ().get_info<sycl::info::platform::name> ();
+ std::string version
+ = device.get_info<sycl::info::device::driver_version> ();
+
+ std::cout << "[" << dev_name << "]"
+ << " from [" << platform_name << "]"
+ << " version [" << version << "]";
+}
+
+static sycl::queue
+get_sycl_queue (int argc, char *argv[])
+{
+ sycl::device device = get_sycl_devices (argc, argv)[0];
+ std::cout << "SYCL: Using device: ";
+ print_device (device);
+ std::cout << std::endl;
+
+ return sycl::queue {device}; /* return-sycl-queue */
+}
diff --git a/gdb/testsuite/lib/sycl.exp b/gdb/testsuite/lib/sycl.exp
new file mode 100644
index 0000000000000000000000000000000000000000..c7e0d527bd3c1e6b54523ec5b4ecee4786094e02
--- /dev/null
+++ b/gdb/testsuite/lib/sycl.exp
@@ -0,0 +1,410 @@
+# Copyright 2019-2024 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Support library for testing SYCL GDB features
+#
+# A particular SYCL device can be selected by passing the SYCL program
+# three command-line arguments:
+# 1. the device type, whose value is in {cpu, gpu, accelerator}.
+# 2. a substring of the device name.
+# 3. backend name.
+#
+# To set these arguments properly, use a SYCL board file, and
+# make your test program select a queue via the get_sycl_queue
+# function in gdb.sycl/sycl-util.cpp. See gdb.sycl/sycl-hello.cpp
+# for a sample SYCL program setup.
+
+load_lib intelgt-utils.exp
+
+# Define global variables for the driver version and platform.
+
+if {![info exists OFFLOAD_DEVICE_GROUP]} {
+ set OFFLOAD_DEVICE_GROUP "cpu,gpu,accelerator"
+}
+
+verbose -log "OFFLOAD_DEVICE_GROUP is '$OFFLOAD_DEVICE_GROUP'"
+
+# Return true if the SYCL device selected via the board file
+# matches the arguments. Otherwise return false.
+# Input arg DEVICE contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version.
+# See an example in init_sycl_devices_list.
+
+proc require_sycl_device {device type name} {
+ set args_list [sycl_get_device_args $device]
+ if {[llength $args_list] <= 2} {
+ return 0
+ }
+
+ set type_match [expr {[lindex $args_list 0] eq $type}]
+ set name_match [string match $name [lindex $args_list 1]]
+
+ return [expr $type_match && $name_match]
+}
+
+# Run a test on the target to check if it recognizes SYCL.
+# Remove device from the available devices list if SYCL is not supported
+# and return the updated list.
+
+proc get_sycl_supported_devices {sycl_device_list} {
+ global srcdir inferior_exited_re
+
+ set supported_sycl_device_list {}
+
+ # Set up, compile, and execute a simple SYCL program.
+ set exe [standard_output_file sycl-hello]
+ set src "$srcdir/lib/sycl-hello.cpp"
+
+ if {[build_executable "failed to compile $src" \
+ $exe $src {sycl debug}]} {
+ verbose "SYCL: Compilation failed" 0
+ return 1
+ }
+ verbose -log "\nSYCL: Compilation succeeded"
+
+ foreach device $sycl_device_list {
+ if ![is_sycl_device_filtered $device] {
+ continue
+ }
+
+ clean_restart "${exe}"
+
+ if {![sycl_start $device]} {
+ verbose "SYCL: Support not detected for ${device}" 0
+ gdb_exit
+ continue
+ }
+
+ set inside_kernel [gdb_get_line_number "inside-kernel" $src]
+ gdb_breakpoint "sycl-hello.cpp:$inside_kernel"
+
+ set result 1
+ sycl_with_intelgt_lock $device {
+ gdb_test_multiple "continue" "continue" {
+ -re -wrap "$inferior_exited_re normally].*" {
+ set result 1
+ }
+ -re -wrap "$inferior_exited_re with code.*" {
+ set result 1
+ }
+ -re -wrap "(?:Breakpoint) .* (at|in).*sycl-hello.cpp:$inside_kernel.*" {
+ set result 0
+ }
+ -re -wrap "received signal SIGABRT, Aborted.*" {
+ set result 1
+ }
+ }
+ }
+
+ if {$result == 0} {
+ verbose "SYCL: Support detected for ${device}" 0
+ lappend supported_sycl_device_list "${device}"
+ } else {
+ verbose "SYCL: Support not detected for ${device}" 0
+ }
+
+ gdb_exit
+ }
+
+ return $supported_sycl_device_list
+}
+
+# Run the program under debug by passing DEVICE as the command line
+# argument. If the device is not Intel GT, stop at main. If the
+# device is Intel GT, continue until the zeContextCreate API call and
+# attempt to create another inferior connected to an Intel GT
+# gdbserver target.
+#
+# Return 1 on success, 0 on failure.
+
+proc sycl_start {device} {
+ if {[require_sycl_device $device "gpu" "Intel*"]} {
+ # To debug an Intel GT device, we create an additional
+ # inferior. For the multi-target setting to work, we
+ # need to operate in target-non-stop mode.
+ gdb_test_no_output "maint set target-non-stop on"
+ }
+
+ set args ""
+ foreach arg [sycl_get_device_args $device] {
+ append args "'$arg' "
+ }
+ gdb_test_no_output "set args $args"
+
+ if {![runto_main]} {
+ return 0
+ }
+
+ if {[require_sycl_device $device "gpu" "Intel*"]} {
+ # For the Intel GT target, define a "hook" BP at a
+ # Level-Zero API function at which the Level-Zero backend
+ # would have been already initialized, attaching to the
+ # device would be expected to succeed. Once we hit the
+ # hook BP, we create the additional inferior.
+ gdb_breakpoint "zeContextCreate" {*}"allow-pending temporary"
+ set hook_bp_hit [gdb_continue_to_breakpoint "hook bp" \
+ "zeContextCreate\[^\r\n\]*"]
+ if {$hook_bp_hit != 0} {
+ return 0
+ }
+
+ with_test_prefix "$device" {
+ set inf_pid [get_inferior_pid]
+ gdb_test "add-inferior -no-connection"
+ gdb_test "inferior 2"
+ set cmd "gdbserver-intelgt --once --attach - $inf_pid"
+ gdb_test "target remote | $cmd" \
+ "Remote debugging.*" "connect the remote target"
+ gdb_test "inferior 1"
+ gdb_test "set schedule-multi on"
+ gdb_test "info inferior" ".*" "inferiors for logging"
+ }
+ }
+
+ return 1
+}
+
+# Get list of devices and return 0 if device list is non-empty else
+# return 1. Each device entry of this list contains ";" separated
+# following information:
+# Device name;Backend Type;Backend Platform version.
+# e.g.
+# Intel(R) ..;ext_oneapi_level_zero;1.3.24347
+
+gdb_caching_proc init_sycl_devices_list {} {
+ global srcdir
+ global inferior_exited_re
+ global sycl_device_list
+
+ set sycl_device_list {}
+ set supported_sycl_device_list {}
+
+ # Set up, compile, and execute a simple SYCL program.
+ set exe [standard_output_file sycl-devices]
+ set src "$srcdir/lib/sycl-devices.cpp"
+
+ if {![test_compiler_info {icx-*} c++]} {
+ unsupported "SYCL tests supported only for dpcpp and icpx compilers"
+ return $sycl_device_list
+ }
+
+ if {[prepare_for_testing "failed to compile $src" \
+ $exe $src {sycl debug}]} {
+ verbose "SYCL: Compilation failed" 0
+ return $sycl_device_list
+ }
+ verbose -log "\nSYCL: Compilation succeeded"
+
+ if {![runto_main]} {
+ untested "failed to run sycl-devices to main"
+ return $sycl_device_list
+ }
+
+ set result 1
+ gdb_test_multiple "continue" "continue" {
+ -re "SYCL: List of Target devices: \\\[(\[^\r\n\]+)\\\]" {
+ set sycl_device_list [split $expect_out(1,string) ","]
+ exp_continue
+ }
+ -re -wrap "$inferior_exited_re normally].*" {
+ set result 0
+ }
+ -re -wrap "$inferior_exited_re with code.*" {
+ set result 1
+ }
+ }
+
+ set supported_sycl_device_list [get_sycl_supported_devices $sycl_device_list]
+ if {($result == 0) && ([llength $supported_sycl_device_list] > 0)} {
+ verbose "SYCL: Devices found: $supported_sycl_device_list" 0
+ } else {
+ set result 1
+ verbose "SYCL: No device found" 0
+ }
+
+ gdb_exit
+
+ return $supported_sycl_device_list
+}
+
+# Return the ID of the current thread (<inferior number>.<thread
+# number>). This procedure can be more practical than using the
+# $_thread and $_inferior convenience variables, because if the SYCL
+# kernel is offloaded to a CPU target, the current thread would be a
+# single integer, but if offloaded to a GPU, it may be an
+# inferior-qualified number like N.M.
+proc get_current_thread {location} {
+ global decimal
+
+ gdb_test_multiple "thread" "get current thread at $location" {
+ -re -wrap "Current thread is ($decimal|$decimal\.$decimal).*" {
+ pass $gdb_test_name
+ return $expect_out(1,string)
+ }
+ -re -wrap "" {
+ fail $gdb_test_name
+ }
+ }
+ return 0
+}
+
+# Returns 1 if the target device is selected via OFFLOAD_DEVICE_GROUP
+# and 0 otherwise.
+# DEVICE contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version.
+# See an example in init_sycl_devices_list.
+
+proc is_sycl_device_filtered {device} {
+ global OFFLOAD_DEVICE_GROUP
+
+ # Filter according to OFFLOAD_DEVICE_GROUP.
+ set device_info [split "$device" ";"]
+ set backend [lindex $device_info 1]
+ set device_type [lindex $device_info 3]
+
+ if {[lsearch -nocase [split $OFFLOAD_DEVICE_GROUP ","] $device_type] < 0} {
+ verbose -log "SYCL: device type $device_type is unwanted, skipping '$device'"
+ return 0
+ }
+
+ if {$device_type == "gpu"
+ && [string match -nocase "*opencl*" $backend]} {
+ verbose -log "SYCL: unsupported combination: $device_type & $backend"
+ return 0
+ }
+
+ return 1
+}
+
+# Returns number of devices found in device string.
+
+proc sycl_get_device_count {device} {
+ set device_info [split "$device" ";"]
+ set device_count [lindex $device_info 4]
+ return $device_count
+}
+
+# Gets the list of args required for running the SYCL tests, where input device
+# contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version;Device Type;count.
+# See an example in init_sycl_devices_list.
+
+proc sycl_get_device_args {device} {
+ global hex
+
+ set device_info [split "$device" ";"]
+ set sycl_driver_platform [lindex $device_info 1]
+ set sycl_driver_version [lindex $device_info 2]
+ set device_type [lindex $device_info 3]
+ set device_name ""
+ set args_list {}
+
+ if {$device_type eq "gpu"} {
+ lappend args_list "gpu"
+ lappend args_list [lindex $device_info 0]
+ } elseif {$device_type eq "cpu"} {
+ lappend args_list "cpu"
+ if {[string match "*Intel*" $device]} {
+ lappend args_list "Intel"
+ }
+ } elseif {$device_type eq "accelerator"} {
+ lappend args_list "accelerator"
+ if {[string match "*Intel*" $device]} {
+ lappend args_list "Intel"
+ }
+ } else {
+ verbose "SYCL: Unexpected device type: ${device_type}" 0
+ }
+ lappend args_list $sycl_driver_platform
+ lappend args_list $sycl_driver_version
+ return $args_list
+}
+
+# Gets the prefix string required for the SYCL tests.
+# DEVICE contains ";" separated following information:
+# Device name;Backend Type;Backend Platform version;Device type.
+# e.g.
+# Intel(R) ...;ext_oneapi_level_zero;1.3.24347;gpu
+# Function returns ":" separated test prefix which has following info:
+# In case of non GPU device: Device type:Backend type:cpp
+# and in case of GPU: Device type GPU: Backend type: Graphics device ID
+# e.g. gpu:opencl:{0x1234}
+
+proc sycl_get_device_prefix {device} {
+ global hex
+ set args_list [sycl_get_device_args $device]
+
+ if {[string match -nocase "*Graphics*" $device]
+ || [string match -nocase "*GPU*" $device]} {
+ # In case of GPU device, add device ID to the prefix to get a unique
+ # test name for multi GPU test machines.
+ return "[lindex $args_list 0]:[lindex $args_list 2]:\
+ {[regexp -all -inline $hex [lindex $args_list 1]]}"
+ }
+ return "[lindex $args_list 0]:[lindex $args_list 2]:cpp"
+}
+
+# Run BODY under the lock, if DEVICE is an Intel GPU. Also calls
+# gdb_exit before releasing the GPU lock.
+#
+# See the similar 'with_gpu_lock' in rocm.exp.
+
+proc sycl_with_intelgt_lock {device body} {
+ with_test_prefix [sycl_get_device_prefix $device] {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ set code [catch {uplevel 1 $body} result]
+ } else {
+ with_lock $::intelgt_lock_filename {
+ set code [catch {uplevel 1 $body} result]
+ }
+ }
+
+ # In case BODY returned early due to some testcase failing.
+ gdb_exit
+ }
+
+ if {$code == 1} {
+ return -code $code -errorinfo $::errorInfo \
+ -errorcode $::errorCode $result
+ } else {
+ return -code $code $result
+ }
+}
+
+# Get the namespace version for the SYCL header corresponding to the compiler
+# used. Return 0 for older compilers using SYCL without namespace versioning.
+
+proc get_sycl_header_version {} {
+ if {[test_compiler_info {icx-202[3-9]-*} c++]} {
+ return 1
+ }
+
+ return 0
+}
+
+# Spawn a SYCL program targeting DEVICE.
+
+proc spawn_sycl_proc {executable device} {
+ # We directly use 'remote_spawn' to be able to pass
+ # the program arguments.
+ set command [list $executable]
+ foreach arg [sycl_get_device_args $device] {
+ lappend command $arg
+ }
+ verbose -log "command: $command"
+
+ set spawn_id [remote_spawn target $command]
+ return $spawn_id
+}