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
1 change: 1 addition & 0 deletions src/plugins/intel_npu/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ if (ENABLE_NPU_PLUGIN_ENGINE)
add_subdirectory(compiler_adapter)
add_subdirectory(backend)
add_subdirectory(plugin)
add_subdirectory(capabilities)
endif()
37 changes: 37 additions & 0 deletions src/plugins/intel_npu/src/capabilities/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright (C) 2025 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
#

set(TARGET_NAME openvino_npu_capabilities)

file(GLOB_RECURSE SOURCES *.cpp *.hpp *.h)
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES})

add_library(${TARGET_NAME} STATIC ${SOURCES})
add_library(openvino::npu_capabilities ALIAS ${TARGET_NAME})
set_target_properties(${TARGET_NAME} PROPERTIES EXPORT_NAME npu_capabilities)

target_include_directories(${TARGET_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)

target_link_libraries(${TARGET_NAME}
PRIVATE
openvino::npu_al
openvino::npu_common
openvino::reference
openvino::runtime
)

set_target_properties(${TARGET_NAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION_RELEASE ${ENABLE_LTO})
ov_add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME})

#
# targets install
#
ov_install_static_lib(${TARGET_NAME} ${NPU_PLUGIN_COMPONENT})

ov_developer_package_export_targets(TARGET openvino::npu_capabilities
INSTALL_INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}/include/)
67 changes: 67 additions & 0 deletions src/plugins/intel_npu/src/capabilities/include/check.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache 2.0
//

#pragma once

#include <cstddef>
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <vector>
#include <type_traits>

#include "meta.hpp"
#include "concepts.hpp"
#include "tlv.hpp"

namespace compat {

struct HeaderHash {
std::size_t operator()(const tlv::Header& header) const noexcept {
return std::hash<uint16_t>{}(header.type);
}
};

struct HeaderEqual {
bool operator()(const tlv::Header& lhs, const tlv::Header& rhs) const noexcept {
return lhs.type == rhs.type;
}
};

using Capabilities = std::unordered_map<tlv::Header, std::unique_ptr<const concepts::Capability>, HeaderHash, HeaderEqual>;
using Requirements = std::unordered_map<tlv::Header, const concepts::Requirement*, HeaderHash, HeaderEqual>;

bool isCompatible(const Capabilities& capabilities, const Requirements& requirements);

struct ByteArrayView {
template <class T>
explicit ByteArrayView(const T& object)
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): reinterpret_cast is by class design
: ByteArrayView(reinterpret_cast<const uint8_t*>(&object), sizeof(object)) {
static_assert(meta::IsMemCopyable<typename std::decay<T>::type>::value, "ByteArrayView supports only trivially copyable type of standard layout");
}

template <class T>
/* implicit */ ByteArrayView(const std::vector<T>& buffer)
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): reinterpret_cast is by class design
: ByteArrayView(reinterpret_cast<const uint8_t*>(buffer.data()), buffer.size()) {
static_assert(meta::IsMemCopyable<T>::value, "ByteArrayView supports only trivially copyable type of standard layout");
}

ByteArrayView(const uint8_t* ptr, size_t size);

const uint8_t* ptr;
size_t size;
};

namespace tlv {

Requirements parseRequirements(ByteArrayView section);
Capabilities parseHWCapabilities(ByteArrayView capabilities);
Capabilities parseSWCapabilities(ByteArrayView capabilities);

} // namespace tlv

} // namespace compat
64 changes: 64 additions & 0 deletions src/plugins/intel_npu/src/capabilities/include/concepts.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache 2.0
//

#pragma once

#include <stdio.h>
#include <cassert>
#include <type_traits>

#include "meta.hpp"

namespace compat {
namespace concepts {

struct Requirement {};
static_assert(std::is_empty<Requirement>::value, "Requirement is just a named void*");

struct Capability {
virtual ~Capability() = default;

Capability() = default;
Capability(const Capability&) = delete;
Capability(Capability&&) = delete;
Capability& operator=(const Capability&) = delete;
Capability& operator=(Capability&&) = delete;

virtual bool isSatisfied(const Requirement*) const = 0;
};

template <class T>
class CapabilityModel : public Capability {
static_assert(meta::IsCapability<T>::value, "Type does not satisfy Capability concept");

public:
explicit CapabilityModel(const T* self) : _self(self) {
// assert(_self != nullptr);
}

bool isSatisfied(const Requirement* requirement) const override {
printf("!!! CapabilityModel: self = %p !!!\n", _self);
printf("!!! CapabilityModel: requ = %p !!!\n", requirement);
if constexpr (std::is_empty<T>::value) {
// TODO: it's unexpected, throw?
return true;
} else {
if constexpr (meta::HasStaticMemberFunctionCheck<T>::value) {
return T::isCompatible(*reinterpret_cast<const T*>(requirement));
} else {
return _self->isCompatible(*reinterpret_cast<const T*>(requirement));
}
// return _self->isCompatible(*reinterpret_cast<const T*>(requirement));
}

return false;
}

private:
const T* _self;
};

} // namespace concepts
} // namespace compat
115 changes: 115 additions & 0 deletions src/plugins/intel_npu/src/capabilities/include/meta.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache 2.0
//

#pragma once

#include <cstdint>
#include <type_traits>

namespace compat {
namespace meta {

// helper class to serve as C++17 std::void_t replacement
template <class...>
struct void_t { // NOLINT: void_t name is intentionally the same as in C++17
using type = void;
};

// IsMemCopyable<T>::value == true, if T "most likely" can be safely
// memcpy'ed to a byte array by code compiled with one compiler and
// then reinterpret_cast'ed back by another compiler.
// "Most likely" means there're still exceptions, under which T satisfies
// the criteria, but still can't be safely memcpy'ed, specifically:
// 1) T contains non-static data-members that are either pointers or references
// 2) T uses implicit padding
// while 2) can be mitigated via pragma pack(1) to ensure no implicit padding
// the only way to enforce 1) is code-review and custom static analysis tools
template <class, class = void>
struct IsMemCopyable : std::false_type {};

template <class T>
struct IsMemCopyable<T, typename void_t<
typename std::enable_if<
std::is_trivially_copyable<T>::value && std::is_standard_layout<T>::value
>::type
>::type> : std::true_type {};

constexpr bool OPTIONAL = false;
constexpr bool REQUIRED = !OPTIONAL;

template <uint16_t id, bool policy = OPTIONAL>
struct CapabilityNo {
static constexpr auto ID = id;
static constexpr auto POLICY = policy;
};

template <class, class = void>
struct HasID : std::false_type {};

template <class T>
struct HasID<T,
typename std::enable_if<
std::is_same<decltype(T::ID), const uint16_t>::value
>::type
> : std::true_type {};

template <class, class = void>
struct HasPolicy : std::false_type {};

template <class T>
struct HasPolicy<T,
typename std::enable_if<
std::is_same<decltype(T::POLICY), const bool>::value
>::type
> : std::true_type {};

template <class, class = void>
struct HasMemberFunctionCheck : std::false_type {};

template <class T>
struct HasMemberFunctionCheck<T, typename void_t<
typename std::enable_if<
std::is_member_function_pointer<decltype(&T::isCompatible)>::value
>::type,
decltype(static_cast<bool(T::*)(const T&) const>(&T::isCompatible))
>::type> : std::true_type {};

template <class, class = void>
struct HasStaticMemberFunctionCheck : std::false_type {};

template <class T>
struct HasStaticMemberFunctionCheck<T, typename void_t<
typename std::enable_if<
!std::is_member_function_pointer<decltype(&T::isCompatible)>::value
>::type,
decltype(static_cast<bool(*)(const T&)>(&T::isCompatible))
>::type> : std::true_type {};

// IsCapability<T>::value == true, if and only if
// 1) IsMemCopyable<T>::value == true
// 2) T has static constexpr/const data-member ID of type uint16_t
// 3) T has static constexpr/const data-member POLICY of type bool
// 4) T is either
// 4.1) empty and has no member-function isCompatible, or
// 4.2) non-empty and has non-static const member-function with name isCompatible
// that takes l-value reference to const T and returns bool, or
// 4.3) non-empty and has static member-function with name isCompatible
// that takes l-value reference to const T and returns bool

template <class, class = void>
struct IsCapability : std::false_type {};

template <class T>
struct IsCapability<T, typename void_t<
typename std::enable_if<
HasID<T>::value &&
HasPolicy<T>::value &&
((std::is_empty<T>::value && !HasMemberFunctionCheck<T>::value && !HasStaticMemberFunctionCheck<T>::value) ||
(!std::is_empty<T>::value && (HasMemberFunctionCheck<T>::value || HasStaticMemberFunctionCheck<T>::value)))
>::type
>::type> : std::true_type {};

} // namespace meta
} // namespace compat
69 changes: 69 additions & 0 deletions src/plugins/intel_npu/src/capabilities/include/registry.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache 2.0
//

#pragma once

#include <cassert>
#include <cstdint>
#include <memory>
#include <unordered_map>

#include "meta.hpp"
#include "concepts.hpp"

namespace compat {

namespace {

template <class T>
std::unique_ptr<const concepts::Capability> createParser(const uint8_t* bytes) {
// assert(bytes != nullptr);
// it is safe and intentional to do reinterpret_cast here
// as Capability classes binary representation is kept cross-compatible
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
return std::make_unique<const concepts::CapabilityModel<T>>(reinterpret_cast<const T*>(bytes));
}

} // namespace

class Registry {
public:
static const Registry& get();

bool isRegistered(uint16_t type) const;

template <class T>
bool isRegistered() const {
static_assert(meta::IsCapability<T>::value, "Type does not satisfy Capability concept");
return isRegistered(T::ID);
}

std::unique_ptr<const concepts::Capability> parseHW(uint16_t type, const uint8_t* bytes) const;
std::unique_ptr<const concepts::Capability> parseSW(uint16_t type, const uint8_t* bytes) const;

private:
Registry();
friend Registry& createRegistry();

Registry(const Registry&) = delete;
Registry(Registry&&) = delete;
Registry& operator=(const Registry&) = delete;
Registry& operator=(Registry&&) = delete;
~Registry() = default;

using CapabilityCreateFn = std::unique_ptr<const concepts::Capability>(*)(const uint8_t* bytes);
std::unordered_map<uint16_t, CapabilityCreateFn> _hwCapabilities;
std::unordered_map<uint16_t, CapabilityCreateFn> _swCapabilities;

template <class T>
static void registerCapability(std::unordered_map<uint16_t, CapabilityCreateFn>& capabilities) {
static_assert(meta::IsCapability<T>::value, "Type does not satisfy Capability concept");

const auto iteratorAndStatus = capabilities.emplace(T::ID, compat::createParser<T>);
assert(std::get<1>(iteratorAndStatus));
}
};

} // namespace compat
Loading
Loading