//==---------------- kernel_properties.hpp - DPC++ Explicit SIMD API -------==//
//
// 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
//
//===----------------------------------------------------------------------===//
// APIs for setting kernel properties interpreted by GPU software stack.
//===----------------------------------------------------------------------===//

#pragma once

#include <sycl/ext/intel/experimental/esimd/common.hpp>
#include <sycl/ext/intel/experimental/esimd/detail/misc_intrin.hpp>

#include <sycl/detail/boost/mp11.hpp>

namespace sycl {
__SYCL_INLINE_VER_NAMESPACE(_V1) {
namespace ext::intel::experimental::esimd {

namespace kernel_properties {

/// <summary>
///  This namespace contains APIs to set kernel properties.
/// </summary>

// Implementation note: <property_class>::value fields should match property IDs
// specified in llvm/lib/SYCLLowerIR/ESIMD/LowerESIMD.cpp

namespace detail {
// Proxy to access private property classes' fields from the API code.
template <class T> struct proxy {
  static constexpr int value = T::value;
};
} // namespace detail

/// A boolean property which requests the compiler to double the amount of
/// general-purpose registers available to a thread at the expense of reducing
/// the amount of available hardware threads.
struct use_double_grf_tag {
  template <class T> friend struct detail::proxy;

private:
  // Property identifier
  static constexpr int value = 0;
};

inline constexpr use_double_grf_tag use_double_grf = {};

} // namespace kernel_properties

namespace __MP11_NS = sycl::detail::boost::mp11;

// TODO this should be replaced with the generic SYCL compile-time properites
// mechanism once implementaion is available.
// https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/proposed/sycl_ext_oneapi_kernel_properties.asciidoc

template <class... KernelProps>
void set_kernel_properties(KernelProps... props) {
  // TODO check for duplicates
  using Props = __MP11_NS::mp_list<KernelProps...>;
  __MP11_NS::mp_for_each<Props>([&](auto Prop) {
    using PropT = decltype(Prop);
    constexpr bool IsDoubleGRF =
        std::is_same_v<PropT, kernel_properties::use_double_grf_tag>;
    if constexpr (IsDoubleGRF) {
      __esimd_set_kernel_properties(
          kernel_properties::detail::proxy<
              kernel_properties::use_double_grf_tag>::value);
    } else {
      static_assert(IsDoubleGRF &&
                    "set_kernel_properties: invalid ESIMD kernel property");
    }
  });
}

} // namespace ext::intel::experimental::esimd
} // __SYCL_INLINE_VER_NAMESPACE(_V1)
} // namespace sycl
