Skip to content

[SYCL] Don't include <complex> from <sycl/sycl.hpp> #11196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Sep 22, 2023
Merged
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
7 changes: 7 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4853,9 +4853,16 @@ void Clang::ConstructHostCompilerJob(Compilation &C, const JobAction &JA,
llvm::sys::path::append(BaseDir, "..", "include");
SmallString<128> SYCLDir(BaseDir);
llvm::sys::path::append(SYCLDir, "sycl");
// This is used to provide our wrappers around STL headers that provide
// additional functions/template specializations when the user includes those
// STL headers in their programs (e.g., <complex>).
SmallString<128> STLWrappersDir(SYCLDir);
llvm::sys::path::append(STLWrappersDir, "stl_wrappers");
HostCompileArgs.push_back("-I");
HostCompileArgs.push_back(TCArgs.MakeArgString(SYCLDir));
HostCompileArgs.push_back("-I");
HostCompileArgs.push_back(TCArgs.MakeArgString(STLWrappersDir));
HostCompileArgs.push_back("-I");
HostCompileArgs.push_back(TCArgs.MakeArgString(BaseDir));

if (!OutputAdded) {
Expand Down
24 changes: 16 additions & 8 deletions clang/lib/Driver/ToolChains/SYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1122,16 +1122,24 @@ SYCLToolChain::GetCXXStdlibType(const ArgList &Args) const {
void SYCLToolChain::AddSYCLIncludeArgs(const clang::driver::Driver &Driver,
const ArgList &DriverArgs,
ArgStringList &CC1Args) {
// Add ../include/sycl and ../include (in that order)
SmallString<128> P(Driver.getInstalledDir());
llvm::sys::path::append(P, "..");
llvm::sys::path::append(P, "include");
SmallString<128> SYCLP(P);
llvm::sys::path::append(SYCLP, "sycl");
// Add ../include/sycl, ../include/sycl/stl_wrappers and ../include (in that
// order).
SmallString<128> IncludePath(Driver.getInstalledDir());
llvm::sys::path::append(IncludePath, "..");
llvm::sys::path::append(IncludePath, "include");
SmallString<128> SYCLPath(IncludePath);
llvm::sys::path::append(SYCLPath, "sycl");
// This is used to provide our wrappers around STL headers that provide
// additional functions/template specializations when the user includes those
// STL headers in their programs (e.g., <complex>).
SmallString<128> STLWrappersPath(SYCLPath);
llvm::sys::path::append(STLWrappersPath, "stl_wrappers");
CC1Args.push_back("-internal-isystem");
CC1Args.push_back(DriverArgs.MakeArgString(SYCLP));
CC1Args.push_back(DriverArgs.MakeArgString(SYCLPath));
CC1Args.push_back("-internal-isystem");
CC1Args.push_back(DriverArgs.MakeArgString(P));
CC1Args.push_back(DriverArgs.MakeArgString(STLWrappersPath));
CC1Args.push_back("-internal-isystem");
CC1Args.push_back(DriverArgs.MakeArgString(IncludePath));
}

void SYCLToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
Expand Down
14 changes: 12 additions & 2 deletions clang/test/Driver/sycl-offload.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,18 @@
// Verify header search dirs are added with -fsycl
// RUN: %clang -### -fsycl %s 2>&1 | FileCheck %s -check-prefixes=CHECK-HEADER-DIR
// RUN: %clang_cl -### -fsycl %s 2>&1 | FileCheck %s -check-prefixes=CHECK-HEADER-DIR
// CHECK-HEADER-DIR: clang{{.*}} "-fsycl-is-device"{{.*}} "-internal-isystem" "{{.*}}bin{{[/\\]+}}..{{[/\\]+}}include{{[/\\]+}}sycl" "-internal-isystem" "{{.*}}bin{{[/\\]+}}..{{[/\\]+}}include"
// CHECK-HEADER-DIR: clang{{.*}} "-fsycl-is-host"{{.*}} "-internal-isystem" "{{.*}}bin{{[/\\]+}}..{{[/\\]+}}include{{[/\\]+}}sycl" "-internal-isystem" "{{.*}}bin{{[/\\]+}}..{{[/\\]+}}include"{{.*}}
// CHECK-HEADER-DIR: clang{{.*}} "-fsycl-is-device"
// CHECK-HEADER-DIR-SAME: "-internal-isystem" "[[ROOT:[^"]*]]bin{{[/\\]+}}..{{[/\\]+}}include{{[/\\]+}}sycl"
// CHECK-HEADER-DIR-NOT: -internal-isystem
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previous version didn't catch cases where we had extra includes between the ones we match (due to {{.*}}bin greedy regex). This update is both catching that and doesn't require extra long lines making it more readable.

// CHECK-HEADER-DIR-SAME: "-internal-isystem" "[[ROOT]]bin{{[/\\]+}}..{{[/\\]+}}include{{[/\\]+}}sycl{{[/\\]+}}stl_wrappers"
// CHECK-HEADER-DIR-NOT: -internal-isystem
// CHECK-HEADER-DIR-SAME: "-internal-isystem" "[[ROOT]]bin{{[/\\]+}}..{{[/\\]+}}include"
// CHECK-HEADER-DIR: clang{{.*}} "-fsycl-is-host"
// CHECK-HEADER-DIR-SAME: "-internal-isystem" "[[ROOT]]bin{{[/\\]+}}..{{[/\\]+}}include{{[/\\]+}}sycl"
// CHECK-HEADER-DIR-NOT: -internal-isystem
// CHECK-HEADER-DIR-SAME: "-internal-isystem" "[[ROOT]]bin{{[/\\]+}}..{{[/\\]+}}include{{[/\\]+}}sycl{{[/\\]+}}stl_wrappers"
// CHECK-HEADER-DIR-NOT: -internal-isystem
// CHECK-HEADER-DIR-SAME: "-internal-isystem" "[[ROOT]]bin{{[/\\]+}}..{{[/\\]+}}include"

/// Check for option incompatibility with -fsycl
// RUN: not %clang -### -fsycl -ffreestanding %s 2>&1 \
Expand Down
13 changes: 0 additions & 13 deletions sycl/include/CL/__spirv/spirv_ops.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1212,19 +1212,6 @@ __CLC_BF16_SCAL_VEC(uint32_t)
#undef __CLC_BF16_SCAL_VEC
#undef __CLC_BF16

__SYCL_CONVERGENT__ extern __DPCPP_SYCL_EXTERNAL
__SYCL_EXPORT __spv::complex_half
__spirv_GroupCMulINTEL(unsigned int, unsigned int,
__spv::complex_half) noexcept;
__SYCL_CONVERGENT__ extern __DPCPP_SYCL_EXTERNAL
__SYCL_EXPORT __spv::complex_float
__spirv_GroupCMulINTEL(unsigned int, unsigned int,
__spv::complex_float) noexcept;
__SYCL_CONVERGENT__ extern __DPCPP_SYCL_EXTERNAL
__SYCL_EXPORT __spv::complex_double
__spirv_GroupCMulINTEL(unsigned int, unsigned int,
__spv::complex_double) noexcept;

extern __DPCPP_SYCL_EXTERNAL int32_t __spirv_BuiltInGlobalHWThreadIDINTEL();
extern __DPCPP_SYCL_EXTERNAL int32_t __spirv_BuiltInSubDeviceIDINTEL();

Expand Down
22 changes: 0 additions & 22 deletions sycl/include/CL/__spirv/spirv_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include <sycl/detail/defines.hpp> // for SYCL_EXT_ONEAPI_MATRIX_VERSION
#include <sycl/half_type.hpp> // for half

#include <complex> // for complex
#include <cstddef> // for size_t
#include <cstdint> // for uint32_t

Expand Down Expand Up @@ -130,27 +129,6 @@ enum class MatrixLayout : uint32_t {

enum class MatrixUse : uint32_t { MatrixA = 0, MatrixB = 1, Accumulator = 2 };

struct complex_float {
complex_float() = default;
complex_float(std::complex<float> x) : real(x.real()), imag(x.imag()) {}
operator std::complex<float>() { return {real, imag}; }
float real, imag;
};

struct complex_double {
complex_double() = default;
complex_double(std::complex<double> x) : real(x.real()), imag(x.imag()) {}
operator std::complex<double>() { return {real, imag}; }
double real, imag;
};

struct complex_half {
complex_half() = default;
complex_half(std::complex<sycl::half> x) : real(x.real()), imag(x.imag()) {}
operator std::complex<sycl::half>() { return {real, imag}; }
sycl::half real, imag;
};

#if (SYCL_EXT_ONEAPI_MATRIX_VERSION > 1)
template <typename T, std::size_t R, std::size_t C, MatrixLayout L,
Scope::Flag S = Scope::Flag::Subgroup,
Expand Down
5 changes: 5 additions & 0 deletions sycl/include/sycl/builtins.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
#include <sycl/builtins_scalar_gen.hpp>
#include <sycl/builtins_vector_gen.hpp>

// We don't use the same exception specifier as <cmath> so we get warnings if
// our code is processed before STL's <cmath>.
// TODO: We should remove this dependency alltogether in a subsequent patch.
#include <cmath>

#ifdef __SYCL_DEVICE_ONLY__
extern "C" {
extern __DPCPP_SYCL_EXTERNAL int abs(int x);
Expand Down
17 changes: 9 additions & 8 deletions sycl/include/sycl/detail/generic_type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

#pragma once

#include <CL/__spirv/spirv_types.hpp> // for complex_double, comple...
#include <sycl/access/access.hpp> // for decorated, address_space
#include <sycl/aliases.hpp> // for half, cl_char, cl_double
#include <sycl/detail/generic_type_lists.hpp> // for nonconst_address_space...
Expand All @@ -18,7 +17,6 @@
#include <sycl/half_type.hpp> // for BIsRepresentationT
#include <sycl/multi_ptr.hpp> // for multi_ptr, address_spa...

#include <complex> // for complex
#include <cstddef> // for byte
#include <cstdint> // for uint8_t
#include <limits> // for numeric_limits
Expand Down Expand Up @@ -485,13 +483,16 @@ using select_cl_scalar_float_t =
select_apply_cl_scalar_t<T, std::false_type, sycl::opencl::cl_half,
sycl::opencl::cl_float, sycl::opencl::cl_double>;

// Use SFINAE so that std::complex specialization could be implemented in
// include/sycl/stl_wrappers/complex that would only be available if STL's
// <complex> is included by users.
template <typename T, typename = void> struct select_cl_scalar_complex_or_T {
using type = T;
};

template <typename T>
using select_cl_scalar_complex_or_T_t = std::conditional_t<
std::is_same_v<T, std::complex<float>>, __spv::complex_float,
std::conditional_t<std::is_same_v<T, std::complex<double>>,
__spv::complex_double,
std::conditional_t<std::is_same_v<T, std::complex<half>>,
__spv::complex_half, T>>>;
using select_cl_scalar_complex_or_T_t =
typename select_cl_scalar_complex_or_T<T>::type;

template <typename T>
using select_cl_scalar_integral_t =
Expand Down
10 changes: 2 additions & 8 deletions sycl/include/sycl/ext/oneapi/functional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,13 @@ struct GroupOpTag<T, std::enable_if_t<detail::is_sgenfloat<T>::value>> {
using type = GroupOpFP;
};

template <typename T>
struct GroupOpTag<
T, std::enable_if_t<std::is_same<T, std::complex<half>>::value ||
std::is_same<T, std::complex<float>>::value ||
std::is_same<T, std::complex<double>>::value>> {
using type = GroupOpC;
};

template <typename T>
struct GroupOpTag<T, std::enable_if_t<detail::is_genbool<T>::value>> {
using type = GroupOpBool;
};

// GroupOpC (std::complex) is handled in sycl/stl_wrappers/complex.

#define __SYCL_CALC_OVERLOAD(GroupTag, SPIRVOperation, BinaryOperation) \
template <__spv::GroupOperation O, typename Group, typename T> \
static T calc(Group g, GroupTag, T x, BinaryOperation) { \
Expand Down
1 change: 1 addition & 0 deletions sycl/include/sycl/ext/oneapi/sub_group_mask.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <sycl/exception.hpp> // for errc, exception
#include <sycl/id.hpp> // for id
#include <sycl/marray.hpp> // for marray
#include <sycl/types.hpp> // for vec

#include <assert.h> // for assert
#include <climits> // for CHAR_BIT
Expand Down
13 changes: 5 additions & 8 deletions sycl/include/sycl/group_algorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
#endif
#endif

#include <complex> // for complex
#include <stddef.h> // for size_t
#include <type_traits> // for enable_if_t, decay_t, integra...

Expand Down Expand Up @@ -127,13 +126,11 @@ using is_multiplies = std::integral_constant<
std::is_same_v<BinaryOperation, sycl::multiplies<void>>>;

// ---- is_complex
// NOTE: std::complex<long double> not yet supported by group algorithms.
template <typename T>
struct is_complex
: std::integral_constant<bool,
std::is_same_v<T, std::complex<half>> ||
std::is_same_v<T, std::complex<float>> ||
std::is_same_v<T, std::complex<double>>> {};
// Use SFINAE so that the "true" branch could be implemented in
// include/sycl/stl_wrappers/complex that would only be available if STL's
// <complex> is included by users.
template <typename T, typename = void>
struct is_complex : public std::false_type {};

// ---- is_arithmetic_or_complex
template <typename T>
Expand Down
9 changes: 5 additions & 4 deletions sycl/include/sycl/known_identity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include <sycl/marray.hpp> // for marray
#include <sycl/types.hpp> // for vec

#include <complex> // for complex
#include <cstddef> // for byte, size_t
#include <functional> // for logical_and, logical_or
#include <limits> // for numeric_limits
Expand Down Expand Up @@ -75,9 +74,11 @@ using IsLogicalOR =
std::is_same_v<BinaryOperation, sycl::logical_or<T>> ||
std::is_same_v<BinaryOperation, sycl::logical_or<void>>>;

template <typename T>
using isComplex = std::bool_constant<std::is_same_v<T, std::complex<float>> ||
std::is_same_v<T, std::complex<double>>>;
// Use SFINAE so that the "true" branch could be implemented in
// include/sycl/stl_wrappers/complex that would only be available if STL's
// <complex> is included by users.
template <typename T, typename = void>
struct isComplex : public std::false_type {};

// Identity = 0
template <typename T, class BinaryOperation>
Expand Down
121 changes: 121 additions & 0 deletions sycl/include/sycl/stl_wrappers/complex
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//==---------------- <complex> wrapper around STL --------------------------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// STL's <complex> includes <cmath> which, in turn, pollutes global namespace.
// As such, we cannot include <complex> from SYCL headers unconditionally and
// have to provide support for std::complex only when the customer included
// <complex> explicitly. Do that by providing our own <complex> that is
// implemented as a wrapper around the STL header using "#include_next"
// functionality.

#pragma once

// Include real STL <complex> header - the next one from the include search
// directories.
#if defined(__has_include_next)
// GCC/clang support go through this path.
#include_next <complex>
#else
// MSVC doesn't support "#include_next", so we have to be creative.
// Our header is located in "stl_wrappers/complex" so it won't be picked by the
// following include. MSVC's installation, on the other hand, has the layout
// where the following would result in the <complex> we want. This is obviously
// hacky, but the best we can do...
#include <../include/complex>
#endif

// Now that we have std::complex available, implement SYCL functionality related
// to it.

#include <type_traits>

#include <CL/__spirv/spirv_ops.hpp> // for __SYCL_CONVERGENT__
#include <sycl/half_type.hpp> // for half

// We provide std::complex specializations here for the following:
// select_cl_scalar_complex_or_T:
#include <sycl/detail/generic_type_traits.hpp>
// sycl::detail::GroupOpTag:
#include <sycl/ext/oneapi/functional.hpp>
// sycl::detail::is_complex:
#include <sycl/group_algorithm.hpp>
// sycl::detail::isComplex
#include <sycl/known_identity.hpp>

namespace __spv {
struct complex_float {
complex_float() = default;
complex_float(std::complex<float> x) : real(x.real()), imag(x.imag()) {}
operator std::complex<float>() { return {real, imag}; }
float real, imag;
};

struct complex_double {
complex_double() = default;
complex_double(std::complex<double> x) : real(x.real()), imag(x.imag()) {}
operator std::complex<double>() { return {real, imag}; }
double real, imag;
};

struct complex_half {
complex_half() = default;
complex_half(std::complex<sycl::half> x) : real(x.real()), imag(x.imag()) {}
operator std::complex<sycl::half>() { return {real, imag}; }
sycl::half real, imag;
};
} // namespace __spv

__SYCL_CONVERGENT__ extern __DPCPP_SYCL_EXTERNAL
__SYCL_EXPORT __spv::complex_half
__spirv_GroupCMulINTEL(unsigned int, unsigned int,
__spv::complex_half) noexcept;
__SYCL_CONVERGENT__ extern __DPCPP_SYCL_EXTERNAL
__SYCL_EXPORT __spv::complex_float
__spirv_GroupCMulINTEL(unsigned int, unsigned int,
__spv::complex_float) noexcept;
__SYCL_CONVERGENT__ extern __DPCPP_SYCL_EXTERNAL
__SYCL_EXPORT __spv::complex_double
__spirv_GroupCMulINTEL(unsigned int, unsigned int,
__spv::complex_double) noexcept;

namespace sycl {
inline namespace _V1 {
namespace detail {
template <typename T>
struct isComplex<T, std::enable_if_t<std::is_same_v<T, std::complex<float>> ||
std::is_same_v<T, std::complex<double>>>>
: public std::true_type {};

// NOTE: std::complex<long double> not yet supported by group algorithms.
template <typename T>
struct is_complex<T, std::enable_if_t<std::is_same_v<T, std::complex<half>> ||
std::is_same_v<T, std::complex<float>> ||
std::is_same_v<T, std::complex<double>>>>
: public std::true_type {};

#ifdef __SYCL_DEVICE_ONLY__
template <typename T>
struct GroupOpTag<
T, std::enable_if_t<std::is_same<T, std::complex<half>>::value ||
std::is_same<T, std::complex<float>>::value ||
std::is_same<T, std::complex<double>>::value>> {
using type = GroupOpC;
};
#endif

template <typename T>
struct select_cl_scalar_complex_or_T<T,
std::enable_if_t<is_complex<T>::value>> {
using type = std::conditional_t<
std::is_same_v<T, std::complex<float>>, __spv::complex_float,
std::conditional_t<std::is_same_v<T, std::complex<double>>,
__spv::complex_double, __spv::complex_half>>;
};
} // namespace detail
} // namespace _V1
} // namespace sycl
Loading