include llama.cpp since there were some edits
This commit is contained in:
1 parent
db7f28d087
commit
8c66990853
3918 files changed
+1640170
-1
No files matched your search
@@ -0,0 +1,128 @@
|
||||
file(TO_CMAKE_PATH "${HEXAGON_SDK_ROOT}" HEXAGON_SDK_ROOT)
|
||||
file(TO_CMAKE_PATH "${HEXAGON_TOOLS_ROOT}" HEXAGON_TOOLS_ROOT)
|
||||
|
||||
if (NOT IS_DIRECTORY "${HEXAGON_SDK_ROOT}")
|
||||
message(FATAL_ERROR "Make sure HEXAGON_SDK_ROOT point to the correct Hexagon SDK installation.")
|
||||
endif()
|
||||
|
||||
if (NOT IS_DIRECTORY "${HEXAGON_TOOLS_ROOT}")
|
||||
message("Try to read HEXAGON_TOOLS_ROOT from hexagon_sdk.json")
|
||||
file(READ "${HEXAGON_SDK_ROOT}/hexagon_sdk.json" HEXAGON_SDK_CONFIG_PATH)
|
||||
string(JSON HEXAGON_TOOLS_PATH GET ${HEXAGON_SDK_CONFIG_PATH} "root" "tools" "info" 0 "path")
|
||||
message("Found HEXAGON_TOOLS_PATH: ${HEXAGON_TOOLS_PATH}")
|
||||
set(HEXAGON_TOOLS_ROOT "${HEXAGON_SDK_ROOT}/${HEXAGON_TOOLS_PATH}")
|
||||
file(TO_CMAKE_PATH "${HEXAGON_TOOLS_ROOT}" HEXAGON_TOOLS_ROOT)
|
||||
if (NOT IS_DIRECTORY "${HEXAGON_TOOLS_ROOT}")
|
||||
message(FATAL_ERROR "Make sure HEXAGON_TOOLS_ROOT point to the correct Hexagon SDK installation.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "hexagon: using ${HEXAGON_SDK_ROOT} and ${HEXAGON_TOOLS_ROOT} for building libggml-htp skels")
|
||||
|
||||
include(${HEXAGON_SDK_ROOT}/build/cmake/hexagon_fun.cmake)
|
||||
include(ExternalProject)
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
option(GGML_HEXAGON_HTP_DEBUG "ggml-hexagon: enable HTP debug output" OFF)
|
||||
set(GGML_HEXAGON_HTP_BUILD_TYPE "Release" CACHE STRING "ggml-hexagon: HTP skel build type (Release, RelWithDebInfo, Debug)")
|
||||
set(GGML_HEXAGON_HTP_CERT "$ENV{HEXAGON_HTP_CERT}" CACHE PATH "ggml-hexagon: enable HTP library signing using certificate")
|
||||
|
||||
add_library(htp_iface OBJECT
|
||||
${CMAKE_CURRENT_BINARY_DIR}/htp_iface_stub.c)
|
||||
|
||||
set_target_properties(htp_iface PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_include_directories(htp_iface PUBLIC
|
||||
${HEXAGON_SDK_ROOT}/incs
|
||||
${HEXAGON_SDK_ROOT}/incs/stddef
|
||||
${HEXAGON_SDK_ROOT}/utils/examples
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/htp
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
build_idl(htp/htp_iface.idl htp_iface)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES Android)
|
||||
target_link_options(htp_iface PUBLIC -llog -ldl)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES Windows)
|
||||
target_precompile_headers(htp_iface PUBLIC <sal.h>)
|
||||
else()
|
||||
target_link_options(htp_iface PUBLIC -ldl)
|
||||
endif()
|
||||
|
||||
set(TARGET_NAME ggml-hexagon)
|
||||
ggml_add_backend_library(${TARGET_NAME}
|
||||
ggml-hexagon.cpp
|
||||
htp-common.h
|
||||
htp-kparams.cpp
|
||||
htp-kparams.h
|
||||
htp-opnode.cpp
|
||||
htp-opnode.h
|
||||
htp-fusion.cpp
|
||||
htp-fusion.h
|
||||
htp-repack.cpp
|
||||
htp-repack.h
|
||||
htp-drv.cpp
|
||||
htp-drv.h
|
||||
libdl.h
|
||||
../../include/ggml-hexagon.h)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE htp_iface)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/htp ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Build HTP skels
|
||||
set(HTP_SKELS)
|
||||
set(HTP_PROJECTS)
|
||||
function(build_htp_skel V)
|
||||
ExternalProject_Add(htp-${V}
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/htp BUILD_ALWAYS ON
|
||||
CMAKE_ARGS
|
||||
-DCMAKE_BUILD_TYPE=${GGML_HEXAGON_HTP_BUILD_TYPE}
|
||||
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_CURRENT_SOURCE_DIR}/htp/cmake-toolchain.cmake
|
||||
-DCMAKE_INSTALL_LIBDIR=${CMAKE_CURRENT_BINARY_DIR}
|
||||
-DHEXAGON_SDK_ROOT=${HEXAGON_SDK_ROOT}
|
||||
-DHEXAGON_TOOLS_ROOT=${HEXAGON_TOOLS_ROOT}
|
||||
-DHEXAGON_HTP_DEBUG=${GGML_HEXAGON_HTP_DEBUG}
|
||||
-DDSP_VERSION=${V}
|
||||
-DPython3_EXECUTABLE=${Python3_EXECUTABLE}
|
||||
-DPREBUILT_LIB_DIR="toolv21_${V}")
|
||||
list(APPEND HTP_SKELS ${CMAKE_CURRENT_BINARY_DIR}/libggml-htp-${V}.so)
|
||||
list(APPEND HTP_PROJECTS htp-${V})
|
||||
set(HTP_SKELS ${HTP_SKELS} PARENT_SCOPE)
|
||||
set(HTP_PROJECTS ${HTP_PROJECTS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
build_htp_skel(v73)
|
||||
build_htp_skel(v75)
|
||||
build_htp_skel(v79)
|
||||
build_htp_skel(v81)
|
||||
build_htp_skel(v85)
|
||||
|
||||
# Install Hexagon skels required at runtime
|
||||
install(FILES ${HTP_SKELS} TYPE LIB)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES Windows AND GGML_HEXAGON_HTP_CERT)
|
||||
file(TO_CMAKE_PATH "$ENV{WINDOWS_SDK_BIN}/arm64" WINSDK_BIN0_ARM64)
|
||||
file(TO_CMAKE_PATH "$ENV{WINDOWS_SDK_BIN}/x86" WINSDK_BIN0_X86)
|
||||
file(TO_CMAKE_PATH "$ENV{WindowsSdkVerBinPath}/arm64" WINSDK_BIN1_ARM64)
|
||||
file(TO_CMAKE_PATH "$ENV{WindowsSdkVerBinPath}/x86" WINSDK_BIN1_X86)
|
||||
|
||||
set(WINSDK_PATHS ${WINSDK_BIN0_ARM64} ${WINSDK_BIN0_X86} ${WINSDK_BIN1_ARM64} ${WINSDK_BIN1_X86})
|
||||
|
||||
find_program(INF2CAT NAMES inf2cat.exe PATHS ${WINSDK_PATHS} REQUIRED)
|
||||
find_program(SIGNTOOL NAMES signtool.exe PATHS ${WINSDK_PATHS} REQUIRED)
|
||||
|
||||
message(STATUS "hexagon: using ${GGML_HEXAGON_HTP_CERT} to sign libggml-htp skels")
|
||||
|
||||
set(LIBGGML_HTP_CAT ${CMAKE_CURRENT_BINARY_DIR}/libggml-htp.cat)
|
||||
add_custom_target(libggml-htp-cat
|
||||
BYPRODUCTS ${LIBGGML_HTP_CAT}
|
||||
DEPENDS libggml-htp.inf ${HTP_PROJECTS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/libggml-htp.inf ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND ${INF2CAT} /driver:${CMAKE_CURRENT_BINARY_DIR} /os:10_25H2_ARM64
|
||||
COMMAND ${SIGNTOOL} sign /fd sha256 /f ${GGML_HEXAGON_HTP_CERT} ${LIBGGML_HTP_CAT}
|
||||
COMMENT "generating and signing libggml-htp.cat file"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_dependencies(${TARGET_NAME} libggml-htp-cat)
|
||||
install(FILES ${LIBGGML_HTP_CAT} TYPE LIB)
|
||||
endif()
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,106 @@
|
||||
#ifndef HTP_COMMON_H
|
||||
#define HTP_COMMON_H
|
||||
|
||||
#include "htp-opnode.h"
|
||||
#include "htp-repack.h"
|
||||
|
||||
struct htp_tensor_extra {
|
||||
std::vector<uint8_t> shadow_buf;
|
||||
size_t shadow_size { 0 };
|
||||
uint32_t flags { 0 };
|
||||
};
|
||||
|
||||
extern int opt_arch;
|
||||
extern size_t opt_ndev;
|
||||
extern size_t opt_nhvx;
|
||||
extern int opt_nhmx;
|
||||
extern size_t opt_vmem;
|
||||
extern int opt_etm;
|
||||
extern int opt_verbose;
|
||||
extern int opt_profile;
|
||||
extern bool opt_hostbuf;
|
||||
extern bool opt_dma64;
|
||||
extern int opt_power_mode;
|
||||
|
||||
extern size_t opt_mbuf_dyn;
|
||||
extern size_t opt_mbuf_static;
|
||||
extern size_t opt_mbuf_total;
|
||||
|
||||
extern int opt_mm_select;
|
||||
extern int opt_fa_select;
|
||||
extern int opt_fa_split;
|
||||
extern int opt_gdn_select;
|
||||
extern int opt_ar_select;
|
||||
extern int opt_ar_scatter;
|
||||
|
||||
extern int opt_opbatch;
|
||||
extern int opt_opqueue;
|
||||
extern int opt_optrace;
|
||||
extern int opt_oppoll;
|
||||
extern int opt_opfusion;
|
||||
extern int opt_rt_repack;
|
||||
|
||||
#ifndef HEX_VERBOSE
|
||||
#define HEX_VERBOSE(...) \
|
||||
if (opt_verbose) GGML_LOG_DEBUG(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
static inline bool htp_tensor_is_fuseable(const struct ggml_tensor * t) {
|
||||
if (!t || !t->extra) return false;
|
||||
const auto * extra = (const struct htp_tensor_extra *) t->extra;
|
||||
return (extra->flags & HTP_TENSOR_FUSEABLE) != 0;
|
||||
}
|
||||
|
||||
static inline bool htp_can_rt_repack(const struct ggml_tensor * t) {
|
||||
if (opt_rt_repack == 0 || !htp_is_tiled_weight_tensor(t)) {
|
||||
return false;
|
||||
}
|
||||
if (t->buffer && ggml_backend_buft_is_host(t->buffer->buft)) {
|
||||
return false;
|
||||
}
|
||||
return opt_rt_repack > 1 || ggml_is_quantized(t->type);
|
||||
}
|
||||
|
||||
static inline bool htp_tensor_is_tiled(const struct ggml_tensor * t) {
|
||||
const auto * extra = (const struct htp_tensor_extra *) t->extra;
|
||||
if (extra && (extra->flags & HTP_TENSOR_TILED)) {
|
||||
return true;
|
||||
}
|
||||
return htp_can_rt_repack(t);
|
||||
}
|
||||
|
||||
static inline bool htp_needs_repack(const struct ggml_tensor * t) {
|
||||
const auto * extra = (const struct htp_tensor_extra *) t->extra;
|
||||
if (extra && (extra->flags & HTP_TENSOR_TILED)) {
|
||||
return false;
|
||||
}
|
||||
return htp_can_rt_repack(t);
|
||||
}
|
||||
|
||||
static inline bool htp_tensors_overlap(const struct ggml_tensor * a, const struct ggml_tensor * b) {
|
||||
const uintptr_t a0 = (uintptr_t) a->data;
|
||||
const uintptr_t b0 = (uintptr_t) b->data;
|
||||
const uintptr_t a1 = a0 + ggml_nbytes(a);
|
||||
const uintptr_t b1 = b0 + ggml_nbytes(b);
|
||||
|
||||
return a0 < b1 && b0 < a1;
|
||||
}
|
||||
|
||||
static inline bool htp_can_row_partition(const struct ggml_tensor * t) {
|
||||
if (t->ne[1] > 1 && (t->nb[1] & 127) != 0) return false;
|
||||
if (t->ne[2] > 1 && (t->nb[2] & 127) != 0) return false;
|
||||
if (t->ne[3] > 1 && (t->nb[3] & 127) != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ggml_hexagon_session;
|
||||
|
||||
const char * htp_session_name(const struct ggml_hexagon_session * sess);
|
||||
size_t htp_session_vtcm_size(const struct ggml_hexagon_session * sess);
|
||||
uint32_t htp_session_n_threads(const struct ggml_hexagon_session * sess);
|
||||
uint32_t htp_session_n_hmx(const struct ggml_hexagon_session * sess);
|
||||
uint32_t htp_session_mdev_count(const struct ggml_hexagon_session * sess);
|
||||
|
||||
htp_op_code htp_op_remap(const struct ggml_tensor * t);
|
||||
|
||||
#endif // HTP_COMMON_H
|
||||
@@ -0,0 +1,425 @@
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <winevt.h>
|
||||
#else
|
||||
# include <dlfcn.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#pragma clang diagnostic ignored "-Wgnu-anonymous-struct"
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
#pragma clang diagnostic ignored "-Wsign-compare"
|
||||
#pragma clang diagnostic ignored "-Wlanguage-extension-token"
|
||||
#pragma clang diagnostic ignored "-Wmicrosoft-enum-value"
|
||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||
|
||||
#include "ggml-impl.h"
|
||||
#include "htp-drv.h"
|
||||
#include "libdl.h"
|
||||
|
||||
#include <domain.h>
|
||||
|
||||
//
|
||||
// Driver API types
|
||||
//
|
||||
|
||||
typedef void * (*rpcmem_alloc_pfn_t)(int heapid, uint32_t flags, int size);
|
||||
typedef void * (*rpcmem_alloc2_pfn_t)(int heapid, uint32_t flags, size_t size);
|
||||
typedef void (*rpcmem_free_pfn_t)(void * po);
|
||||
typedef int (*rpcmem_to_fd_pfn_t)(void * po);
|
||||
|
||||
typedef AEEResult (*dspqueue_create_pfn_t)(int domain,
|
||||
uint32_t flags,
|
||||
uint32_t req_queue_size,
|
||||
uint32_t resp_queue_size,
|
||||
dspqueue_callback_t packet_callback,
|
||||
dspqueue_callback_t error_callback,
|
||||
void * callback_context,
|
||||
dspqueue_t * queue);
|
||||
typedef AEEResult (*dspqueue_close_pfn_t)(dspqueue_t queue);
|
||||
typedef AEEResult (*dspqueue_export_pfn_t)(dspqueue_t queue, uint64_t *queue_id);
|
||||
typedef AEEResult (*dspqueue_write_pfn_t)(dspqueue_t queue, uint32_t flags,
|
||||
uint32_t num_buffers,
|
||||
struct dspqueue_buffer *buffers,
|
||||
uint32_t message_length,
|
||||
const uint8_t *message,
|
||||
uint32_t timeout_us);
|
||||
typedef AEEResult (*dspqueue_read_pfn_t)(dspqueue_t queue, uint32_t *flags,
|
||||
uint32_t max_buffers, uint32_t *num_buffers,
|
||||
struct dspqueue_buffer *buffers,
|
||||
uint32_t max_message_length,
|
||||
uint32_t *message_length, uint8_t *message,
|
||||
uint32_t timeout_us);
|
||||
typedef AEEResult (*dspqueue_read_noblock_pfn_t)(dspqueue_t queue, uint32_t *flags,
|
||||
uint32_t max_buffers, uint32_t *num_buffers,
|
||||
struct dspqueue_buffer *buffers,
|
||||
uint32_t max_message_length,
|
||||
uint32_t *message_length, uint8_t *message);
|
||||
typedef int (*fastrpc_mmap_pfn_t)(int domain, int fd, void *addr, int offset, size_t length, enum fastrpc_map_flags flags);
|
||||
typedef int (*fastrpc_munmap_pfn_t)(int domain, int fd, void *addr, size_t length);
|
||||
|
||||
typedef int (*remote_handle64_open_pfn_t)(const char* name, remote_handle64 *ph);
|
||||
typedef int (*remote_handle64_invoke_pfn_t)(remote_handle64 h, uint32_t dwScalars, remote_arg *pra);
|
||||
typedef int (*remote_handle64_close_pfn_t)(remote_handle h);
|
||||
typedef int (*remote_handle_control_pfn_t)(uint32_t req, void* data, uint32_t datalen);
|
||||
typedef int (*remote_handle64_control_pfn_t)(remote_handle64 h, uint32_t req, void* data, uint32_t datalen);
|
||||
typedef int (*remote_session_control_pfn_t)(uint32_t req, void *data, uint32_t datalen);
|
||||
typedef int (*remote_system_request_pfn_t)(system_req_payload * req);
|
||||
|
||||
//
|
||||
// Driver API pfns
|
||||
//
|
||||
|
||||
rpcmem_alloc_pfn_t rpcmem_alloc_pfn = nullptr;
|
||||
rpcmem_alloc2_pfn_t rpcmem_alloc2_pfn = nullptr;
|
||||
rpcmem_free_pfn_t rpcmem_free_pfn = nullptr;
|
||||
rpcmem_to_fd_pfn_t rpcmem_to_fd_pfn = nullptr;
|
||||
|
||||
fastrpc_mmap_pfn_t fastrpc_mmap_pfn = nullptr;
|
||||
fastrpc_munmap_pfn_t fastrpc_munmap_pfn = nullptr;
|
||||
|
||||
dspqueue_create_pfn_t dspqueue_create_pfn = nullptr;
|
||||
dspqueue_close_pfn_t dspqueue_close_pfn = nullptr;
|
||||
dspqueue_export_pfn_t dspqueue_export_pfn = nullptr;
|
||||
dspqueue_write_pfn_t dspqueue_write_pfn = nullptr;
|
||||
dspqueue_read_pfn_t dspqueue_read_pfn = nullptr;
|
||||
dspqueue_read_noblock_pfn_t dspqueue_read_noblock_pfn = nullptr;
|
||||
|
||||
remote_handle64_open_pfn_t remote_handle64_open_pfn = nullptr;
|
||||
remote_handle64_invoke_pfn_t remote_handle64_invoke_pfn = nullptr;
|
||||
remote_handle64_close_pfn_t remote_handle64_close_pfn = nullptr;
|
||||
remote_handle_control_pfn_t remote_handle_control_pfn = nullptr;
|
||||
remote_handle64_control_pfn_t remote_handle64_control_pfn = nullptr;
|
||||
remote_session_control_pfn_t remote_session_control_pfn = nullptr;
|
||||
remote_system_request_pfn_t remote_system_request_pfn = nullptr;
|
||||
|
||||
//
|
||||
// Driver API
|
||||
//
|
||||
|
||||
void * rpcmem_alloc(int heapid, uint32_t flags, int size) {
|
||||
return rpcmem_alloc_pfn(heapid, flags, size);
|
||||
}
|
||||
|
||||
void * rpcmem_alloc2(int heapid, uint32_t flags, size_t size) {
|
||||
if (rpcmem_alloc2_pfn) {
|
||||
return rpcmem_alloc2_pfn(heapid, flags, size);
|
||||
} else {
|
||||
GGML_LOG_INFO("ggml-hex: rpcmem_alloc2 not found, falling back to rpcmem_alloc\n");
|
||||
return rpcmem_alloc_pfn(heapid, flags, size);
|
||||
}
|
||||
}
|
||||
|
||||
void rpcmem_free(void * po) {
|
||||
return rpcmem_free_pfn(po);
|
||||
}
|
||||
|
||||
int rpcmem_to_fd(void * po) {
|
||||
return rpcmem_to_fd_pfn(po);
|
||||
}
|
||||
|
||||
HTPDRV_API int fastrpc_mmap(int domain, int fd, void * addr, int offset, size_t length, enum fastrpc_map_flags flags) {
|
||||
return fastrpc_mmap_pfn(domain, fd, addr, offset, length, flags);
|
||||
}
|
||||
|
||||
HTPDRV_API int fastrpc_munmap(int domain, int fd, void * addr, size_t length) {
|
||||
return fastrpc_munmap_pfn(domain, fd, addr, length);
|
||||
}
|
||||
|
||||
AEEResult dspqueue_create(int domain,
|
||||
uint32_t flags,
|
||||
uint32_t req_queue_size,
|
||||
uint32_t resp_queue_size,
|
||||
dspqueue_callback_t packet_callback,
|
||||
dspqueue_callback_t error_callback,
|
||||
void * callback_context,
|
||||
dspqueue_t * queue) {
|
||||
return dspqueue_create_pfn(domain, flags, req_queue_size, resp_queue_size, packet_callback, error_callback,
|
||||
callback_context, queue);
|
||||
}
|
||||
|
||||
AEEResult dspqueue_close(dspqueue_t queue) {
|
||||
return dspqueue_close_pfn(queue);
|
||||
}
|
||||
|
||||
AEEResult dspqueue_export(dspqueue_t queue, uint64_t * queue_id) {
|
||||
return dspqueue_export_pfn(queue, queue_id);
|
||||
}
|
||||
|
||||
AEEResult dspqueue_write(dspqueue_t queue,
|
||||
uint32_t flags,
|
||||
uint32_t num_buffers,
|
||||
struct dspqueue_buffer * buffers,
|
||||
uint32_t message_length,
|
||||
const uint8_t * message,
|
||||
uint32_t timeout_us) {
|
||||
return dspqueue_write_pfn(queue, flags, num_buffers, buffers, message_length, message, timeout_us);
|
||||
}
|
||||
|
||||
AEEResult dspqueue_read(dspqueue_t queue,
|
||||
uint32_t * flags,
|
||||
uint32_t max_buffers,
|
||||
uint32_t * num_buffers,
|
||||
struct dspqueue_buffer * buffers,
|
||||
uint32_t max_message_length,
|
||||
uint32_t * message_length,
|
||||
uint8_t * message,
|
||||
uint32_t timeout_us) {
|
||||
#ifdef _WIN32
|
||||
if (timeout_us == 0) {
|
||||
return dspqueue_read_noblock_pfn(queue, flags, max_buffers, num_buffers, buffers, max_message_length,
|
||||
message_length, message);
|
||||
}
|
||||
#endif
|
||||
return dspqueue_read_pfn(queue, flags, max_buffers, num_buffers, buffers, max_message_length, message_length,
|
||||
message, timeout_us);
|
||||
}
|
||||
|
||||
HTPDRV_API int remote_handle64_open(const char * name, remote_handle64 * ph) {
|
||||
return remote_handle64_open_pfn(name, ph);
|
||||
}
|
||||
|
||||
HTPDRV_API int remote_handle64_invoke(remote_handle64 h, uint32_t dwScalars, remote_arg * pra) {
|
||||
return remote_handle64_invoke_pfn(h, dwScalars, pra);
|
||||
}
|
||||
|
||||
HTPDRV_API int remote_handle64_close(remote_handle64 h) {
|
||||
return remote_handle64_close_pfn(h);
|
||||
}
|
||||
|
||||
HTPDRV_API int remote_handle_control(uint32_t req, void * data, uint32_t datalen) {
|
||||
return remote_handle_control_pfn(req, data, datalen);
|
||||
}
|
||||
|
||||
HTPDRV_API int remote_handle64_control(remote_handle64 h, uint32_t req, void * data, uint32_t datalen) {
|
||||
return remote_handle64_control_pfn(h, req, data, datalen);
|
||||
}
|
||||
|
||||
HTPDRV_API int remote_session_control(uint32_t req, void * data, uint32_t datalen) {
|
||||
return remote_session_control_pfn(req, data, datalen);
|
||||
}
|
||||
|
||||
HTPDRV_API int remote_system_request(system_req_payload * req) {
|
||||
if (!remote_system_request_pfn) {
|
||||
return AEE_EUNSUPPORTEDAPI;
|
||||
}
|
||||
return remote_system_request_pfn(req);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static std::string wstr_to_str(std::wstring_view wstr) {
|
||||
std::string result;
|
||||
if (wstr.empty()) {
|
||||
return result;
|
||||
}
|
||||
auto bytes_needed = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
wstr.data(), (int) wstr.size(),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
if (bytes_needed == 0) {
|
||||
GGML_LOG_ERROR("ggml-hex: WideCharToMultiByte failed. Error %lu\n", GetLastError());
|
||||
throw std::runtime_error("Invalid wstring input");
|
||||
}
|
||||
|
||||
result.resize(bytes_needed, '\0');
|
||||
int bytes_written = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
wstr.data(), (int) wstr.size(),
|
||||
result.data(), bytes_needed,
|
||||
nullptr, nullptr);
|
||||
if (bytes_written == 0) {
|
||||
GGML_LOG_ERROR("ggml-hex: WideCharToMultiByte failed. Error %lu\n", GetLastError());
|
||||
throw std::runtime_error("Wstring conversion failed");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string get_driver_path() {
|
||||
std::wstring serviceName = L"qcnspmcdm";
|
||||
std::string result;
|
||||
|
||||
// Get a handle to the SCM database.
|
||||
SC_HANDLE schSCManager = OpenSCManagerW(NULL, NULL, STANDARD_RIGHTS_READ);
|
||||
if (nullptr == schSCManager) {
|
||||
GGML_LOG_ERROR("ggml-hex: Failed to open SCManager. Error: %lu\n", GetLastError());
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get a handle to the service.
|
||||
SC_HANDLE schService = OpenServiceW(schSCManager, // SCM database
|
||||
serviceName.c_str(), // name of service
|
||||
SERVICE_QUERY_CONFIG); // need query config access
|
||||
|
||||
if (nullptr == schService) {
|
||||
GGML_LOG_ERROR("ggml-hex: Failed to open qcnspmcdm service. Error: %lu\n", GetLastError());
|
||||
CloseServiceHandle(schSCManager);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Store the size of buffer used as an output.
|
||||
DWORD bufferSize;
|
||||
if (!QueryServiceConfigW(schService, NULL, 0, &bufferSize) &&
|
||||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
|
||||
GGML_LOG_ERROR("ggml-hex: Failed to query service config. Error: %lu\n", GetLastError());
|
||||
CloseServiceHandle(schService);
|
||||
CloseServiceHandle(schSCManager);
|
||||
return result;
|
||||
}
|
||||
// Get the configuration of the service.
|
||||
LPQUERY_SERVICE_CONFIGW serviceConfig =
|
||||
static_cast<LPQUERY_SERVICE_CONFIGW>(LocalAlloc(LMEM_FIXED, bufferSize));
|
||||
if (!QueryServiceConfigW(schService, serviceConfig, bufferSize, &bufferSize)) {
|
||||
fprintf(stderr, "ggml-hex: Failed to query service config. Error: %lu\n", GetLastError());
|
||||
LocalFree(serviceConfig);
|
||||
CloseServiceHandle(schService);
|
||||
CloseServiceHandle(schSCManager);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read the driver file path get its parent directory
|
||||
std::wstring driverPath = std::wstring(serviceConfig->lpBinaryPathName);
|
||||
driverPath = driverPath.substr(0, driverPath.find_last_of(L"\\"));
|
||||
|
||||
// Clean up resources
|
||||
LocalFree(serviceConfig);
|
||||
CloseServiceHandle(schService);
|
||||
CloseServiceHandle(schSCManager);
|
||||
|
||||
// Driver path would contain invalid path string, like:
|
||||
// \SystemRoot\System32\DriverStore\FileRepository\qcadsprpc8280.inf_arm64_c2b9460c9a072f37
|
||||
// "\SystemRoot" should be replace with a correct one (e.g. C:\Windows)
|
||||
const std::wstring systemRootPlaceholder = L"\\SystemRoot";
|
||||
if (0 != driverPath.compare(0, systemRootPlaceholder.length(), systemRootPlaceholder)) {
|
||||
GGML_LOG_ERROR("ggml-hex: String pattern not found in driver path.\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Replace \SystemRoot with an absolute path from system ENV windir
|
||||
const std::wstring systemRootEnv = L"windir";
|
||||
|
||||
// Query the number of wide characters this variable requires
|
||||
DWORD numWords = GetEnvironmentVariableW(systemRootEnv.c_str(), NULL, 0);
|
||||
if (numWords == 0) {
|
||||
GGML_LOG_ERROR("ggml-hex: Failed get systemRoot environment variable\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Query the actual system root name from environment variable
|
||||
std::vector<wchar_t> systemRoot(numWords + 1);
|
||||
numWords = GetEnvironmentVariableW(systemRootEnv.c_str(), systemRoot.data(), numWords + 1);
|
||||
if (numWords == 0) {
|
||||
GGML_LOG_ERROR("ggml-hex: Failed to read windir environment variable\n");
|
||||
return result;
|
||||
}
|
||||
driverPath.replace(0, systemRootPlaceholder.length(), std::wstring(systemRoot.data()));
|
||||
|
||||
return wstr_to_str(driverPath);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
using dl_handle_ptr = std::unique_ptr<dl_handle, dl_handle_deleter>;
|
||||
|
||||
int htpdrv_init() {
|
||||
static dl_handle_ptr lib_cdsp_rpc_handle = nullptr;
|
||||
static bool initialized = false;
|
||||
#ifdef _WIN32
|
||||
std::string drv_path = get_driver_path() + "\\" + "libcdsprpc.dll";
|
||||
#else
|
||||
std::string drv_path = "libcdsprpc.so";
|
||||
#endif
|
||||
if (initialized) {
|
||||
GGML_LOG_INFO("ggml-hex: Driver already loaded\n");
|
||||
return AEE_SUCCESS;
|
||||
}
|
||||
GGML_LOG_INFO("ggml-hex: Loading driver %s\n", drv_path.c_str());
|
||||
|
||||
fs::path path{ drv_path.c_str() };
|
||||
dl_handle_ptr handle { dl_load_library(path) };
|
||||
if (!handle) {
|
||||
GGML_LOG_ERROR("ggml-hex: failed to load %s: %s\n", path.u8string().c_str(), dl_error());
|
||||
return AEE_EUNABLETOLOAD;
|
||||
}
|
||||
|
||||
#define dlsym(drv, type, pfn, symbol, ignore) \
|
||||
do { \
|
||||
pfn = (type) dl_get_sym(drv, #symbol); \
|
||||
if (!ignore && nullptr == pfn) { \
|
||||
GGML_LOG_ERROR("ggml-hex: failed to dlsym %s\n", #symbol); \
|
||||
return AEE_EUNABLETOLOAD; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
dlsym(handle.get(), rpcmem_alloc_pfn_t, rpcmem_alloc_pfn, rpcmem_alloc, false);
|
||||
dlsym(handle.get(), rpcmem_alloc2_pfn_t, rpcmem_alloc2_pfn, rpcmem_alloc2, true);
|
||||
dlsym(handle.get(), rpcmem_free_pfn_t, rpcmem_free_pfn, rpcmem_free, false);
|
||||
dlsym(handle.get(), rpcmem_to_fd_pfn_t, rpcmem_to_fd_pfn, rpcmem_to_fd, false);
|
||||
dlsym(handle.get(), fastrpc_mmap_pfn_t, fastrpc_mmap_pfn, fastrpc_mmap, false);
|
||||
dlsym(handle.get(), fastrpc_munmap_pfn_t, fastrpc_munmap_pfn, fastrpc_munmap, false);
|
||||
dlsym(handle.get(), dspqueue_create_pfn_t, dspqueue_create_pfn, dspqueue_create, false);
|
||||
dlsym(handle.get(), dspqueue_close_pfn_t, dspqueue_close_pfn, dspqueue_close, false);
|
||||
dlsym(handle.get(), dspqueue_export_pfn_t, dspqueue_export_pfn, dspqueue_export, false);
|
||||
dlsym(handle.get(), dspqueue_write_pfn_t, dspqueue_write_pfn, dspqueue_write, false);
|
||||
dlsym(handle.get(), dspqueue_read_pfn_t, dspqueue_read_pfn, dspqueue_read, false);
|
||||
dlsym(handle.get(), dspqueue_read_noblock_pfn_t, dspqueue_read_noblock_pfn, dspqueue_read_noblock, false);
|
||||
dlsym(handle.get(), remote_handle64_open_pfn_t, remote_handle64_open_pfn, remote_handle64_open, false);
|
||||
dlsym(handle.get(), remote_handle64_invoke_pfn_t, remote_handle64_invoke_pfn, remote_handle64_invoke, false);
|
||||
dlsym(handle.get(), remote_handle_control_pfn_t, remote_handle_control_pfn, remote_handle_control, false);
|
||||
dlsym(handle.get(), remote_handle64_control_pfn_t, remote_handle64_control_pfn, remote_handle64_control, false);
|
||||
dlsym(handle.get(), remote_session_control_pfn_t, remote_session_control_pfn, remote_session_control, false);
|
||||
dlsym(handle.get(), remote_handle64_close_pfn_t, remote_handle64_close_pfn, remote_handle64_close, false);
|
||||
dlsym(handle.get(), remote_system_request_pfn_t, remote_system_request_pfn, remote_system_request, true);
|
||||
|
||||
lib_cdsp_rpc_handle = std::move(handle);
|
||||
initialized = true;
|
||||
|
||||
return AEE_SUCCESS;
|
||||
}
|
||||
|
||||
domain * htpdrv_get_domain(int domain_id) {
|
||||
int i = 0;
|
||||
int size = sizeof(supported_domains) / sizeof(domain);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (supported_domains[i].id == domain_id) {
|
||||
return &supported_domains[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int htpdrv_get_arch(int domain, int * arch) {
|
||||
if (!remote_handle_control_pfn) {
|
||||
GGML_LOG_ERROR("ggml-hex: remote_handle_control is not supported on this device\n");
|
||||
return AEE_EUNSUPPORTEDAPI;
|
||||
}
|
||||
|
||||
struct remote_dsp_capability arch_ver;
|
||||
arch_ver.domain = (uint32_t) domain;
|
||||
arch_ver.attribute_ID = ARCH_VER;
|
||||
arch_ver.capability = (uint32_t) 0;
|
||||
|
||||
int err = remote_handle_control(DSPRPC_GET_DSP_INFO, &arch_ver, sizeof(arch_ver));
|
||||
if ((err & 0xff) == (AEE_EUNSUPPORTEDAPI & 0xff)) {
|
||||
GGML_LOG_ERROR("ggml-hex: FastRPC capability API is not supported on this device\n");
|
||||
return AEE_EUNSUPPORTEDAPI;
|
||||
}
|
||||
|
||||
if (err != AEE_SUCCESS) {
|
||||
GGML_LOG_ERROR("ggml-hex: FastRPC capability query failed (err %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
uint32_t val = arch_ver.capability & 0xff;
|
||||
*arch = (int) ((val >> 4) * 10 + (val & 0x0f));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# pragma clang diagnostic ignored "-Wignored-attributes"
|
||||
#endif
|
||||
|
||||
#include <AEEStdErr.h>
|
||||
#include <rpcmem.h>
|
||||
#include <remote.h>
|
||||
#include <dspqueue.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
# ifdef GGML_BACKEND_BUILD
|
||||
# define HTPDRV_API __declspec(dllexport) extern
|
||||
# else
|
||||
# define HTPDRV_API __declspec(dllimport) extern
|
||||
# endif
|
||||
#else
|
||||
# define HTPDRV_API __attribute__ ((visibility ("default"))) extern
|
||||
#endif
|
||||
|
||||
/* Offset to differentiate HLOS and Hexagon error codes.
|
||||
Stores the value of AEE_EOFFSET for Hexagon. */
|
||||
#ifndef DSP_OFFSET
|
||||
# define DSP_OFFSET 0x80000400
|
||||
#endif
|
||||
|
||||
/* Errno for connection reset by peer. */
|
||||
#ifndef ECONNRESET
|
||||
# ifdef __hexagon__
|
||||
# define ECONNRESET 104
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Abstraction of different OS specific sleep APIs.
|
||||
SLEEP accepts input in seconds. */
|
||||
#ifndef SLEEP
|
||||
# ifdef __hexagon__
|
||||
# define SLEEP(x) \
|
||||
{ /* Do nothing for simulator. */ \
|
||||
}
|
||||
# else
|
||||
# ifdef _WIN32
|
||||
# define SLEEP(x) Sleep(1000 * x) /* Sleep accepts input in milliseconds. */
|
||||
# else
|
||||
# define SLEEP(x) sleep(x) /* sleep accepts input in seconds. */
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Include windows specific header files. */
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <sysinfoapi.h>
|
||||
# define _CRT_SECURE_NO_WARNINGS 1
|
||||
# define _WINSOCK_DEPRECATED_NO_WARNINGS 1
|
||||
#endif
|
||||
|
||||
/* Includes and defines for all HLOS except windows */
|
||||
#if !defined(__hexagon__) && !defined(_WIN32)
|
||||
# include "unistd.h"
|
||||
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
/* Includes and defines for Hexagon and all HLOS except Windows. */
|
||||
#if !defined(_WIN32)
|
||||
/* Weak reference to remote symbol for compilation. */
|
||||
# pragma weak remote_session_control
|
||||
# pragma weak remote_handle_control
|
||||
# pragma weak remote_handle64_control
|
||||
# pragma weak fastrpc_mmap
|
||||
# pragma weak fastrpc_munmap
|
||||
# pragma weak rpcmem_alloc2
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# pragma weak remote_system_request
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# define DSPQUEUE_TIMEOUT DSPQUEUE_TIMEOUT_NONE
|
||||
#else
|
||||
# define DSPQUEUE_TIMEOUT 1000000
|
||||
#endif
|
||||
|
||||
/**
|
||||
* htpdrv_init API: driver interface entry point
|
||||
*
|
||||
* @return Return AEE error codes as defined in Hexagon SDK.
|
||||
*/
|
||||
HTPDRV_API int htpdrv_init(void);
|
||||
|
||||
/**
|
||||
* htpdrv_get_domain API: get domain struct from domain value.
|
||||
*
|
||||
* @param[in] domain value of a domain
|
||||
* @return Returns domain struct of the domain if it is supported or else
|
||||
* returns NULL.
|
||||
*
|
||||
*/
|
||||
HTPDRV_API domain * htpdrv_get_domain(int domain_id);
|
||||
|
||||
/**
|
||||
* htpdrv_get_arch API: query the Hexagon processor architecture version information
|
||||
*
|
||||
* @param[in] domain_id value of a domain
|
||||
* @param[out] Arch version (73, 75, ...)
|
||||
* @return 0 if query is successful.
|
||||
* non-zero if error, return value points to the error.
|
||||
*
|
||||
*/
|
||||
HTPDRV_API int htpdrv_get_arch(int domain, int * arch);
|
||||
|
||||
HTPDRV_API int remote_system_request(system_req_payload * req);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,649 @@
|
||||
#include "htp-fusion.h"
|
||||
#include "htp-kparams.h"
|
||||
#include "ggml-impl.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
static inline bool htp_same_tensor_data(const ggml_tensor * a, const ggml_tensor * b) {
|
||||
return a && b && (a == b || a->data == b->data);
|
||||
}
|
||||
|
||||
static inline bool htp_same_shape_and_contiguity(const ggml_tensor * a, const ggml_tensor * b) {
|
||||
return ggml_are_same_shape(a, b) && (ggml_is_contiguous(a) == ggml_is_contiguous(b));
|
||||
}
|
||||
|
||||
static inline bool htp_inplace_overlap_unsafe(const ggml_tensor * dst, const ggml_tensor * in) {
|
||||
return htp_tensors_overlap(dst, in) && (dst->data != in->data || !ggml_are_same_stride(dst, in));
|
||||
}
|
||||
|
||||
static inline bool htp_inputs_overlap_unsafe(const htp_opnode & prev, const ggml_tensor * dst, const ggml_tensor * extra = nullptr) {
|
||||
if (extra && htp_inplace_overlap_unsafe(dst, extra)) return true;
|
||||
for (const auto * in : prev.inputs) {
|
||||
if (htp_inplace_overlap_unsafe(dst, in)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool htp_match_consumer(
|
||||
const htp_opnode & prev,
|
||||
const htp_opnode & cur,
|
||||
bool commutative,
|
||||
const ggml_tensor ** other_src = nullptr
|
||||
) {
|
||||
if (prev.outputs.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
const ggml_tensor * prev_out = prev.dst();
|
||||
if (!htp_tensor_is_fuseable(prev_out)) {
|
||||
return false;
|
||||
}
|
||||
if (htp_same_tensor_data(cur.src0(), prev_out)) {
|
||||
if (other_src) *other_src = cur.src1();
|
||||
return true;
|
||||
}
|
||||
if (commutative && htp_same_tensor_data(cur.src1(), prev_out)) {
|
||||
if (other_src) *other_src = cur.src0();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_supported_mul_mat_nx_kernel(const ggml_tensor * src0, const struct htp_mm_kernel_params * kparams) {
|
||||
if (kparams->n_hmx) {
|
||||
return kparams->kernel_type == HTP_MM_KERNEL_HMX_2D;
|
||||
}
|
||||
return htp_tensor_is_tiled(src0) && (kparams->kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW);
|
||||
}
|
||||
|
||||
static bool is_supported_mul_mat_id_nx_kernel(const ggml_tensor * src0, const struct htp_mm_kernel_params * kparams) {
|
||||
if (kparams->n_hmx) {
|
||||
return kparams->kernel_type == HTP_MM_KERNEL_HMX_2D;
|
||||
}
|
||||
return htp_tensor_is_tiled(src0) &&
|
||||
(kparams->kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW || kparams->kernel_type == HTP_MM_KERNEL_HVX_QUANT_BLOCK);
|
||||
}
|
||||
|
||||
// --- Fusion rules ---
|
||||
|
||||
static bool fuse_allreduce_add(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
if (opt_ar_select != 2 || prev.opcode != HTP_OP_ALLREDUCE) return false;
|
||||
|
||||
const auto * ar_kparams = prev.kparams<htp_allreduce_kernel_params>();
|
||||
const uint32_t rank = (uint32_t) ar_kparams->rank;
|
||||
const uint32_t n_ranks = (uint32_t) ar_kparams->n_ranks;
|
||||
|
||||
const ggml_tensor * ar_local = prev.inputs[rank];
|
||||
if (!htp_tensor_is_fuseable(ar_local)) return false;
|
||||
|
||||
const ggml_tensor * add_dst = cur.dst();
|
||||
const ggml_tensor * res_tensor = nullptr;
|
||||
if (htp_same_tensor_data(cur.src0(), ar_local)) {
|
||||
res_tensor = cur.src1();
|
||||
} else if (htp_same_tensor_data(cur.src1(), ar_local)) {
|
||||
res_tensor = cur.src0();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ar_local->type != res_tensor->type) return false;
|
||||
|
||||
const bool is_same_shape = ggml_are_same_shape(ar_local, res_tensor);
|
||||
const bool is_row_bcast = !is_same_shape && (ar_local->ne[0] == res_tensor->ne[0] &&
|
||||
res_tensor->ne[1] == 1 && res_tensor->ne[2] == 1 && res_tensor->ne[3] == 1);
|
||||
if (!is_same_shape && !is_row_bcast) return false;
|
||||
|
||||
if (is_same_shape && !ggml_are_same_stride(ar_local, res_tensor)) {
|
||||
return false;
|
||||
}
|
||||
if (ggml_is_contiguous(ar_local) != ggml_is_contiguous(add_dst)) return false;
|
||||
|
||||
const bool is_shard_ok = n_ranks <= HTP_OP_MAX_OUTPUTS && add_dst->data == ar_local->data;
|
||||
|
||||
struct htp_allreduce_kernel_params kparams;
|
||||
if (!htp_kparams_solve_allreduce(sess, add_dst, rank, n_ranks, true, is_row_bcast, is_shard_ok, &kparams)) {
|
||||
HEX_VERBOSE("ggml-hex: %s skip ALLREDUCE_ADD fusion: solver failed\n", htp_session_name(sess));
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool scatter_ok = kparams.n_dsts > 1;
|
||||
kparams.mode = scatter_ok ? HTP_ALLREDUCE_SHARDED_FANOUT : HTP_ALLREDUCE_FULL;
|
||||
|
||||
for (uint32_t r = 0; r < n_ranks; r++) {
|
||||
const ggml_tensor * ar_src = prev.inputs[r];
|
||||
if (!htp_tensors_overlap(add_dst, ar_src)) continue;
|
||||
if (scatter_ok && r == rank) continue;
|
||||
HEX_VERBOSE("ggml-hex: %s skip ALLREDUCE_ADD fusion: dst overlaps allreduce src %u\n", htp_session_name(sess), r);
|
||||
return false;
|
||||
}
|
||||
|
||||
prev.opcode = HTP_OP_ALLREDUCE_ADD;
|
||||
prev.inputs.push_back(res_tensor);
|
||||
if (kparams.mode != HTP_ALLREDUCE_SHARDED_FANOUT) {
|
||||
prev.outputs = { add_dst };
|
||||
}
|
||||
prev.fused.push_back(cur.node);
|
||||
prev.set_kparams(kparams);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_rms_norm_mul(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
if (prev.opcode != HTP_OP_RMS_NORM) return false;
|
||||
|
||||
const ggml_tensor * weight = nullptr;
|
||||
if (!htp_match_consumer(prev, cur, /*commutative=*/ true, &weight)) return false;
|
||||
|
||||
const ggml_tensor * src0 = prev.src0();
|
||||
const ggml_tensor * dst = cur.dst();
|
||||
if (src0->type != GGML_TYPE_F32 || src0->ne[0] != weight->ne[0] || !htp_same_shape_and_contiguity(prev.dst(), dst)) return false;
|
||||
if (htp_inputs_overlap_unsafe(prev, dst, weight)) return false;
|
||||
|
||||
const bool is_row_bcast = (weight->ne[1] == 1 && weight->ne[2] == 1 && weight->ne[3] == 1);
|
||||
const bool is_same_shape = ggml_are_same_shape(src0, weight);
|
||||
if (!is_row_bcast && !is_same_shape) return false;
|
||||
|
||||
struct htp_unary_kernel_params kparams;
|
||||
if (!htp_kparams_solve_rms_norm_mul(sess, src0, weight, dst, &kparams)) {
|
||||
HEX_VERBOSE("ggml-hex: %s skip RMS_NORM_MUL fusion: VTCM needed (%d) > budget (%zu)\n",
|
||||
htp_session_name(sess), kparams.vtcm_size, htp_session_vtcm_size(sess));
|
||||
return false;
|
||||
}
|
||||
|
||||
prev.opcode = HTP_OP_RMS_NORM_MUL;
|
||||
prev.inputs = { src0, weight };
|
||||
prev.outputs = { dst };
|
||||
prev.fused.push_back(cur.node);
|
||||
prev.set_kparams(kparams);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_rms_norm_scale(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
GGML_UNUSED(sess);
|
||||
if (prev.opcode != HTP_OP_RMS_NORM && prev.opcode != HTP_OP_RMS_NORM_MUL) return false;
|
||||
if (!htp_match_consumer(prev, cur, /*commutative=*/ false)) return false;
|
||||
|
||||
const ggml_tensor * src0 = prev.src0();
|
||||
const ggml_tensor * dst = cur.dst();
|
||||
if (src0->type != GGML_TYPE_F32 || !htp_same_shape_and_contiguity(prev.dst(), dst)) return false;
|
||||
if (htp_inputs_overlap_unsafe(prev, dst)) return false;
|
||||
|
||||
float scale = 0.0f;
|
||||
float bias = 0.0f;
|
||||
memcpy(&scale, (const float *) cur.params + 0, sizeof(float));
|
||||
memcpy(&bias, (const float *) cur.params + 1, sizeof(float));
|
||||
if (bias != 0.0f || scale == 0.0f) return false;
|
||||
|
||||
float prev_scale = 0.0f;
|
||||
memcpy(&prev_scale, &prev.params[1], sizeof(float));
|
||||
if (prev_scale != 0.0f) {
|
||||
scale *= prev_scale;
|
||||
}
|
||||
memcpy(&prev.params[1], &scale, sizeof(float));
|
||||
|
||||
prev.outputs[0] = dst;
|
||||
prev.fused.push_back(cur.node);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_mul_mat_add(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
if (prev.opcode != HTP_OP_MUL_MAT) return false;
|
||||
|
||||
const ggml_tensor * src2 = nullptr;
|
||||
if (!htp_match_consumer(prev, cur, /*commutative=*/ true, &src2)) return false;
|
||||
if (src2->type != GGML_TYPE_F32) return false;
|
||||
|
||||
const ggml_tensor * src0 = prev.node->src[0];
|
||||
const ggml_tensor * src1 = prev.node->src[1];
|
||||
const ggml_tensor * dst = cur.node;
|
||||
if (!ggml_are_same_shape(prev.node, dst)) return false;
|
||||
|
||||
const bool prev_collapsed = htp_matmul_can_collapse(src0, src1, prev.node);
|
||||
const bool cur_collapsed = htp_matmul_add_can_collapse(src0, src1, src2, dst);
|
||||
if (prev_collapsed != cur_collapsed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto * orig_kparams = prev.kparams<htp_mm_kernel_params>();
|
||||
struct htp_mm_kernel_params kparams;
|
||||
if (!htp_kparams_solve_matmul_add(sess, src0, src1, src2, dst, &kparams)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int src1_nrows = src1->ne[1] * src1->ne[2] * src1->ne[3];
|
||||
if (kparams.n_hmx == 0 && src1_nrows != 1) return false;
|
||||
|
||||
if (kparams.n_hmx > 0 && orig_kparams->n_hmx > 0) {
|
||||
if (kparams.m_chunk < orig_kparams->m_chunk ||
|
||||
kparams.n_chunk < orig_kparams->n_chunk ||
|
||||
kparams.n_act_threads < orig_kparams->n_act_threads) {
|
||||
HEX_VERBOSE("ggml-hex: %s skip MUL_MAT_ADD fusion: HMX efficiency reduced (m %d->%d, n %d->%d, th %d->%d)\n",
|
||||
htp_session_name(sess), orig_kparams->m_chunk, kparams.m_chunk,
|
||||
orig_kparams->n_chunk, kparams.n_chunk,
|
||||
orig_kparams->n_act_threads, kparams.n_act_threads);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
prev.opcode = HTP_OP_MUL_MAT_ADD;
|
||||
prev.inputs = { src0, src1, src2 };
|
||||
prev.outputs = { dst };
|
||||
htp_opnode_collapse(sess, prev);
|
||||
prev.fused.push_back(cur.node);
|
||||
prev.set_kparams(kparams);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_mul_mat_nx(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
if (prev.opcode != HTP_OP_MUL_MAT && prev.opcode != HTP_OP_MUL_MAT_NX) return false;
|
||||
|
||||
const uint32_t curr_n = (uint32_t) prev.outputs.size();
|
||||
if (curr_n >= HTP_OP_MAX_OUTPUTS || curr_n + 1 >= HTP_OP_MAX_INPUTS) return false;
|
||||
|
||||
const ggml_tensor * w0 = prev.inputs[0];
|
||||
const ggml_tensor * x_orig = prev.node->src[1];
|
||||
const ggml_tensor * w_in = cur.node->src[0];
|
||||
const ggml_tensor * x_in = cur.node->src[1];
|
||||
const ggml_tensor * d_in = cur.node;
|
||||
|
||||
auto * orig_kparams = prev.kparams<htp_mm_kernel_params>();
|
||||
const auto * cur_kparams = cur.kparams<htp_mm_kernel_params>();
|
||||
|
||||
if (x_in != x_orig || w_in->type != w0->type || w_in->ne[0] != w0->ne[0] ||
|
||||
w0->ne[2] != 1 || w0->ne[3] != 1 || w_in->ne[2] != 1 || w_in->ne[3] != 1 ||
|
||||
htp_tensor_is_tiled(w_in) != htp_tensor_is_tiled(w0) ||
|
||||
orig_kparams->n_hmx != cur_kparams->n_hmx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool collapse = htp_matmul_can_collapse(w0, x_orig, prev.node) && htp_matmul_can_collapse(w_in, x_in, d_in);
|
||||
if ((x_orig->ne[2] > 1 || x_orig->ne[3] > 1) && !collapse) return false;
|
||||
|
||||
if (prev.opcode == HTP_OP_MUL_MAT_NX) {
|
||||
orig_kparams->n_weights = curr_n + 1;
|
||||
} else {
|
||||
struct htp_mm_kernel_params kparams;
|
||||
const ggml_tensor x_collapsed = collapse ? htp_tensor_collapse_rows(x_orig) : *x_orig;
|
||||
if (!htp_kparams_solve_mmnx(sess, w0, &x_collapsed, curr_n + 1, &kparams)) return false;
|
||||
if (!is_supported_mul_mat_nx_kernel(w0, &kparams)) return false;
|
||||
|
||||
prev.set_kparams(kparams);
|
||||
prev.opcode = HTP_OP_MUL_MAT_NX;
|
||||
}
|
||||
|
||||
prev.inputs.insert(prev.inputs.begin() + curr_n, w_in);
|
||||
prev.outputs.push_back(collapse ? prev.add_dummy(htp_tensor_collapse_rows(d_in)) : d_in);
|
||||
prev.fused.push_back(cur.node);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_mul_mat_id_nx(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
if (prev.opcode != HTP_OP_MUL_MAT_ID && prev.opcode != HTP_OP_MUL_MAT_ID_NX) return false;
|
||||
|
||||
const uint32_t curr_n = (uint32_t) prev.outputs.size();
|
||||
if (curr_n >= HTP_OP_MAX_OUTPUTS || curr_n + 2 >= HTP_OP_MAX_INPUTS) return false;
|
||||
|
||||
const ggml_tensor * w0 = prev.inputs[0];
|
||||
const ggml_tensor * x = prev.inputs[curr_n];
|
||||
const ggml_tensor * ids = prev.inputs[curr_n + 1];
|
||||
const ggml_tensor * w_in = cur.src0();
|
||||
const ggml_tensor * x_in = cur.src1();
|
||||
const ggml_tensor * ids_in = cur.node->src[2];
|
||||
const ggml_tensor * d_in = cur.dst();
|
||||
|
||||
auto * orig_kparams = prev.kparams<htp_mm_kernel_params>();
|
||||
const auto * cur_kparams = cur.kparams<htp_mm_kernel_params>();
|
||||
|
||||
if (x_in != x || ids_in != ids || w_in->type != w0->type ||
|
||||
w_in->ne[0] != w0->ne[0] || w_in->ne[2] != w0->ne[2] ||
|
||||
htp_tensor_is_tiled(w_in) != htp_tensor_is_tiled(w0) ||
|
||||
orig_kparams->n_hmx != cur_kparams->n_hmx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct htp_mm_kernel_params kparams;
|
||||
if (d_in->nb[1] == prev.node->nb[1]) {
|
||||
kparams = *orig_kparams;
|
||||
kparams.n_weights = curr_n + 1;
|
||||
} else if (!htp_kparams_solve_mmidnx(sess, w0, x, d_in, curr_n + 1, &kparams)) {
|
||||
return false;
|
||||
}
|
||||
if (!is_supported_mul_mat_id_nx_kernel(w0, &kparams)) return false;
|
||||
|
||||
prev.opcode = HTP_OP_MUL_MAT_ID_NX;
|
||||
prev.inputs.insert(prev.inputs.begin() + curr_n, w_in);
|
||||
prev.outputs.push_back(d_in);
|
||||
prev.fused.push_back(cur.node);
|
||||
prev.set_kparams(kparams);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_gdn_cpy(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
GGML_UNUSED(sess);
|
||||
if (prev.opcode != HTP_OP_GATED_DELTA_NET || prev.outputs.size() != 1) return false;
|
||||
|
||||
const ggml_tensor * gdn_out = prev.dst();
|
||||
const ggml_tensor * cpy_node = cur.node;
|
||||
const ggml_tensor * cpy_src = cur.src0();
|
||||
const ggml_tensor * cpy_dst = cur.dst();
|
||||
|
||||
if (gdn_out->type != GGML_TYPE_F32 || cpy_src->type != GGML_TYPE_F32 || cpy_dst->type != GGML_TYPE_F32) return false;
|
||||
if ((gdn_out->flags & GGML_TENSOR_FLAG_OUTPUT) || (cpy_node->flags & GGML_TENSOR_FLAG_OUTPUT)) return false;
|
||||
|
||||
const ggml_tensor * v = prev.node->src[2];
|
||||
if (!v) return false;
|
||||
|
||||
const int64_t S_v = v->ne[0];
|
||||
const int64_t H = v->ne[1];
|
||||
const int64_t n_tokens = v->ne[2];
|
||||
const int64_t n_seqs = v->ne[3];
|
||||
const int64_t K = ggml_get_op_params_i32(prev.node, 0);
|
||||
const size_t tail_off = (size_t) S_v * H * n_tokens * n_seqs * sizeof(float);
|
||||
const int64_t D = S_v * S_v * H;
|
||||
const int64_t n_written = std::min<int64_t>(n_tokens, K);
|
||||
|
||||
if (cpy_src->op != GGML_OP_VIEW || !htp_same_tensor_data(cpy_src->view_src, gdn_out) ||
|
||||
cpy_src->view_offs != tail_off || !ggml_is_contiguous(cpy_src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cpy_dst->ne[0] != D || cpy_dst->ne[1] != n_seqs || cpy_dst->ne[2] != n_written || !ggml_is_contiguous(cpy_dst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prev.outputs.push_back(cpy_dst);
|
||||
prev.fused.push_back(cur.node);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_ssm_conv_silu(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
GGML_UNUSED(sess);
|
||||
if (prev.opcode != HTP_OP_SSM_CONV) return false;
|
||||
|
||||
auto * kparams = prev.kparams<htp_ssm_conv_kernel_params>();
|
||||
if (kparams->silu) return false;
|
||||
if (!htp_match_consumer(prev, cur, /*commutative=*/ false)) return false;
|
||||
|
||||
const ggml_tensor * conv_dst = prev.dst();
|
||||
const ggml_tensor * dst = cur.dst();
|
||||
if (!ggml_are_same_layout(conv_dst, dst)) return false;
|
||||
if (htp_tensors_overlap(dst, prev.src0()) || htp_tensors_overlap(dst, prev.src1())) return false;
|
||||
|
||||
kparams->silu = 1;
|
||||
prev.outputs[0] = dst;
|
||||
prev.fused.push_back(cur.node);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_repeat_get_rows(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
if (prev.opcode != HTP_OP_REPEAT) return false;
|
||||
if (!htp_match_consumer(prev, cur, /*commutative=*/ false)) return false;
|
||||
|
||||
const ggml_tensor * repeat_out = prev.dst();
|
||||
const ggml_tensor * repeat_src0 = prev.src0();
|
||||
const ggml_tensor * gr_src1 = cur.src1();
|
||||
const ggml_tensor * gr_dst = cur.dst();
|
||||
|
||||
if (repeat_src0->ne[0] != repeat_out->ne[0] || repeat_src0->ne[1] != repeat_out->ne[1]) return false;
|
||||
if (repeat_src0->type != repeat_out->type) return false;
|
||||
|
||||
struct htp_get_rows_kernel_params kparams;
|
||||
if (!htp_kparams_solve_repeat_get_rows(sess, repeat_src0, gr_src1, gr_dst, &kparams) || kparams.vtcm_size == 0) return false;
|
||||
|
||||
prev.opcode = HTP_OP_GET_ROWS;
|
||||
prev.inputs = { repeat_src0, gr_src1 };
|
||||
prev.outputs = { gr_dst };
|
||||
prev.fused.push_back(cur.node);
|
||||
prev.set_kparams(kparams);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_unary_mul_glu(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
htp_op_code glu_op = HTP_OP_INVALID;
|
||||
switch (prev.opcode) {
|
||||
case HTP_OP_UNARY_SILU: glu_op = HTP_OP_GLU_SWIGLU; break;
|
||||
case HTP_OP_UNARY_GELU: glu_op = HTP_OP_GLU_GEGLU; break;
|
||||
case HTP_OP_UNARY_GELU_QUICK: glu_op = HTP_OP_GLU_GEGLU_QUICK; break;
|
||||
case HTP_OP_UNARY_GELU_ERF: glu_op = HTP_OP_GLU_GEGLU_ERF; break;
|
||||
case HTP_OP_UNARY_RELU: glu_op = HTP_OP_GLU_REGLU; break;
|
||||
case HTP_OP_UNARY_SIGMOID: glu_op = HTP_OP_GLU_SIGMOID; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
const ggml_tensor * up = nullptr;
|
||||
if (!htp_match_consumer(prev, cur, /*commutative=*/ true, &up)) return false;
|
||||
|
||||
const ggml_tensor * gate = prev.src0();
|
||||
const ggml_tensor * dst = cur.dst();
|
||||
if (htp_inputs_overlap_unsafe(prev, dst, up)) return false;
|
||||
|
||||
prev.opcode = glu_op;
|
||||
prev.inputs = { gate, up };
|
||||
prev.outputs = { dst };
|
||||
if (!htp_kparams_solve(sess, prev)) return false;
|
||||
|
||||
prev.fused.push_back(cur.node);
|
||||
memset(prev.params, 0, sizeof(prev.params));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_mul_mul_scalar(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
if (prev.opcode != HTP_OP_MUL || prev.inputs.size() != 2) return false;
|
||||
|
||||
const auto * k0 = prev.kparams<htp_binary_kernel_params>();
|
||||
const auto * k1 = cur.kparams<htp_binary_kernel_params>();
|
||||
if (k0->kernel_type != HTP_BINARY_KERNEL_SCALAR || k1->kernel_type != HTP_BINARY_KERNEL_SCALAR) return false;
|
||||
if (k0->src1_strided || k1->src1_strided) return false;
|
||||
|
||||
const ggml_tensor * src2 = nullptr;
|
||||
if (!htp_match_consumer(prev, cur, /*commutative=*/ false, &src2)) return false;
|
||||
|
||||
const ggml_tensor * src1 = prev.src1();
|
||||
const ggml_tensor * dst = cur.dst();
|
||||
if (!ggml_are_same_shape(src1, src2) || !htp_same_shape_and_contiguity(prev.dst(), dst)) return false;
|
||||
if (htp_inputs_overlap_unsafe(prev, dst, src2)) return false;
|
||||
|
||||
const size_t vtcm_size = htp_session_vtcm_size(sess);
|
||||
struct htp_binary_kernel_params kparams = *k0;
|
||||
kparams.src2_size = kparams.src1_size;
|
||||
kparams.rows_per_buffer = 0;
|
||||
if (kparams.src1_size > 65536 || kparams.src1_size + kparams.src2_size > vtcm_size) return false;
|
||||
|
||||
struct htp_binary_vtcm_layout L;
|
||||
htp_binary_vtcm_layout_build(&L, &kparams, vtcm_size);
|
||||
if (L.rows_per_buffer == 0 || L.total_bytes > vtcm_size) return false;
|
||||
|
||||
kparams.rows_per_buffer = L.rows_per_buffer;
|
||||
kparams.vtcm_size = L.total_bytes;
|
||||
|
||||
prev.inputs.push_back(src2);
|
||||
prev.outputs[0] = dst;
|
||||
prev.fused.push_back(cur.node);
|
||||
prev.set_kparams(kparams);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fuse_binary_nx(const ggml_hexagon_session * sess, htp_opnode & prev, const htp_opnode & cur) {
|
||||
if (prev.opcode != cur.opcode) return false;
|
||||
|
||||
const uint32_t curr_n = (uint32_t) prev.inputs.size();
|
||||
if (curr_n < 2 || curr_n >= HTP_OP_MAX_INPUTS) return false;
|
||||
|
||||
const auto * k0 = prev.kparams<htp_binary_kernel_params>();
|
||||
const auto * k1 = cur.kparams<htp_binary_kernel_params>();
|
||||
const bool k0_ok = (k0->kernel_type == HTP_BINARY_KERNEL_SAME_SHAPE) ||
|
||||
(k0->kernel_type == HTP_BINARY_KERNEL_CHUNKED && !k0->is_scalar);
|
||||
const bool k1_ok = (k1->kernel_type == HTP_BINARY_KERNEL_SAME_SHAPE) ||
|
||||
(k1->kernel_type == HTP_BINARY_KERNEL_CHUNKED && !k1->is_scalar);
|
||||
if (!k0_ok || !k1_ok) return false;
|
||||
|
||||
const bool is_comm = (cur.opcode == HTP_OP_ADD || cur.opcode == HTP_OP_MUL);
|
||||
const ggml_tensor * new_src = nullptr;
|
||||
if (!htp_match_consumer(prev, cur, is_comm, &new_src)) return false;
|
||||
|
||||
const ggml_tensor * src0 = prev.inputs[0];
|
||||
const ggml_tensor * dst = cur.dst();
|
||||
if (!ggml_are_same_shape(src0, prev.inputs[1]) ||
|
||||
!ggml_are_same_shape(src0, new_src) ||
|
||||
!htp_same_shape_and_contiguity(prev.dst(), dst)) {
|
||||
return false;
|
||||
}
|
||||
if (htp_inputs_overlap_unsafe(prev, dst, new_src)) return false;
|
||||
|
||||
const size_t vtcm_size = htp_session_vtcm_size(sess);
|
||||
struct htp_binary_kernel_params kparams = *k0;
|
||||
struct htp_binary_vtcm_layout L;
|
||||
memset(&L, 0, sizeof(L));
|
||||
|
||||
const uint32_t n_srcs = curr_n + 1;
|
||||
if (k0->kernel_type == HTP_BINARY_KERNEL_SAME_SHAPE || k1->kernel_type == HTP_BINARY_KERNEL_SAME_SHAPE) {
|
||||
if (k0->kernel_type != HTP_BINARY_KERNEL_SAME_SHAPE) {
|
||||
kparams = *k1;
|
||||
}
|
||||
kparams.n_srcs = (uint8_t) n_srcs;
|
||||
kparams.rows_per_buffer = 0;
|
||||
|
||||
auto check_split = [&](const ggml_tensor * t) {
|
||||
if (src0->ne[2] > 1 && t->nb[2] != t->ne[1] * t->nb[1]) kparams.split_at_ne01 = 1;
|
||||
if (src0->ne[3] > 1 && t->nb[3] != t->ne[2] * t->nb[2]) kparams.split_at_ne02 = 1;
|
||||
};
|
||||
for (const auto * in : prev.inputs) check_split(in);
|
||||
check_split(new_src);
|
||||
check_split(dst);
|
||||
|
||||
htp_binary_vtcm_layout_build(&L, &kparams, vtcm_size);
|
||||
}
|
||||
|
||||
if (L.rows_per_buffer == 0 || L.total_bytes == 0 || L.total_bytes > vtcm_size) {
|
||||
bool all_contig = ggml_is_contiguous(new_src) && ggml_is_contiguous(dst);
|
||||
for (const auto * in : prev.inputs) {
|
||||
all_contig = all_contig && ggml_is_contiguous(in);
|
||||
}
|
||||
if (!all_contig) return false;
|
||||
|
||||
const uint32_t total_elems = (uint32_t) ggml_nelements(src0);
|
||||
const uint32_t elem_size = (uint32_t) ggml_type_size(src0->type);
|
||||
const uint32_t max_threads = htp_session_n_threads(sess);
|
||||
if (!htp_binary_solve_chunked(&L, &kparams, total_elems, elem_size, max_threads, n_srcs, false, vtcm_size)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
kparams.rows_per_buffer = L.rows_per_buffer;
|
||||
kparams.vtcm_size = (uint32_t) L.total_bytes;
|
||||
|
||||
prev.inputs.push_back(new_src);
|
||||
prev.outputs[0] = dst;
|
||||
prev.fused.push_back(cur.node);
|
||||
prev.set_kparams(kparams);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool htp_fusion_can_stack(const struct ggml_tensor * a) {
|
||||
if (!a || !a->src[0] || !a->src[1]) return false;
|
||||
switch (a->op) {
|
||||
case GGML_OP_MUL_MAT:
|
||||
if (!htp_fusion_enabled(HTP_FUSE_MUL_MAT_NX)) return false;
|
||||
if (htp_is_hadamard(a)) return false;
|
||||
if (a->src[0]->ne[2] != 1 || a->src[0]->ne[3] != 1) return false;
|
||||
return ggml_is_quantized(a->src[0]->type);
|
||||
case GGML_OP_MUL_MAT_ID:
|
||||
if (!htp_fusion_enabled(HTP_FUSE_MUL_MAT_ID_NX)) return false;
|
||||
return a->src[2] != nullptr && ggml_is_quantized(a->src[0]->type);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool htp_fusion_can_stack(const struct ggml_tensor * a, const struct ggml_tensor * b) {
|
||||
if (!htp_fusion_can_stack(b) || a->op != b->op || a->src[1] != b->src[1]) {
|
||||
return false;
|
||||
}
|
||||
if (a->op == GGML_OP_MUL_MAT_ID && a->src[2] != b->src[2]) {
|
||||
return false;
|
||||
}
|
||||
const ggml_tensor * w0 = a->src[0];
|
||||
const ggml_tensor * w1 = b->src[0];
|
||||
return w0->type == w1->type && w0->ne[0] == w1->ne[0] && w0->ne[2] == w1->ne[2];
|
||||
}
|
||||
|
||||
int htp_fusion_max(const struct ggml_cgraph * graph, int idx) {
|
||||
const int n = graph->n_nodes;
|
||||
const ggml_op op0 = graph->nodes[idx]->op;
|
||||
if (op0 != GGML_OP_ADD && op0 != GGML_OP_NORM && op0 != GGML_OP_RMS_NORM) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum ggml_op ops[HTP_OP_MAX_FUSED];
|
||||
ops[0] = op0;
|
||||
|
||||
int f = idx + 1;
|
||||
while (f < n && f < idx + HTP_OP_MAX_FUSED) {
|
||||
const ggml_op op = graph->nodes[f]->op;
|
||||
if (op != GGML_OP_ADD && op != GGML_OP_MUL && op != GGML_OP_SCALE &&
|
||||
op != GGML_OP_NORM && op != GGML_OP_RMS_NORM) {
|
||||
break;
|
||||
}
|
||||
ops[f - idx] = op;
|
||||
f++;
|
||||
}
|
||||
|
||||
f -= idx;
|
||||
for (; f > 1; f--) {
|
||||
if (ggml_can_fuse(graph, idx, ops, f)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
void htp_fusion_tag_graph(struct ggml_cgraph * graph) {
|
||||
for (int i = 0; i < graph->n_nodes; i++) {
|
||||
auto * extra = (htp_tensor_extra *) graph->nodes[i]->extra;
|
||||
if (!extra) continue;
|
||||
|
||||
extra->flags &= ~HTP_TENSOR_FUSEABLE;
|
||||
|
||||
const ggml_op op = graph->nodes[i]->op;
|
||||
if ((op == GGML_OP_RMS_NORM || op == GGML_OP_SCALE || op == GGML_OP_REPEAT ||
|
||||
op == GGML_OP_UNARY || op == GGML_OP_ADD || op == GGML_OP_SUB ||
|
||||
op == GGML_OP_MUL || op == GGML_OP_DIV || op == GGML_OP_MUL_MAT_ID ||
|
||||
op == GGML_OP_SSM_CONV ||
|
||||
(op == GGML_OP_MUL_MAT && !htp_is_hadamard(graph->nodes[i]))) &&
|
||||
ggml_node_has_n_uses(graph, i, 1)) {
|
||||
extra->flags |= HTP_TENSOR_FUSEABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool htp_fusion_try_fuse(const struct ggml_hexagon_session * sess, struct htp_opnode & prev, const struct htp_opnode & cur) {
|
||||
if (!opt_opfusion) return false;
|
||||
|
||||
switch (cur.opcode) {
|
||||
case HTP_OP_ADD:
|
||||
return (htp_fusion_enabled(HTP_FUSE_ALLREDUCE_ADD) && fuse_allreduce_add(sess, prev, cur)) ||
|
||||
(htp_fusion_enabled(HTP_FUSE_MUL_MAT_ADD) && fuse_mul_mat_add(sess, prev, cur)) ||
|
||||
(htp_fusion_enabled(HTP_FUSE_BINARY_NX) && fuse_binary_nx(sess, prev, cur));
|
||||
case HTP_OP_MUL:
|
||||
return (htp_fusion_enabled(HTP_FUSE_RMS_NORM_MUL) && fuse_rms_norm_mul(sess, prev, cur)) ||
|
||||
(htp_fusion_enabled(HTP_FUSE_UNARY_MUL_GLU) && fuse_unary_mul_glu(sess, prev, cur)) ||
|
||||
(htp_fusion_enabled(HTP_FUSE_MUL_MUL_SCALAR) && fuse_mul_mul_scalar(sess, prev, cur)) ||
|
||||
(htp_fusion_enabled(HTP_FUSE_BINARY_NX) && fuse_binary_nx(sess, prev, cur));
|
||||
case HTP_OP_SUB:
|
||||
case HTP_OP_DIV:
|
||||
return htp_fusion_enabled(HTP_FUSE_BINARY_NX) && fuse_binary_nx(sess, prev, cur);
|
||||
case HTP_OP_SCALE:
|
||||
return htp_fusion_enabled(HTP_FUSE_RMS_NORM_SCALE) && fuse_rms_norm_scale(sess, prev, cur);
|
||||
case HTP_OP_UNARY_SILU:
|
||||
return htp_fusion_enabled(HTP_FUSE_SSM_CONV_SILU) && fuse_ssm_conv_silu(sess, prev, cur);
|
||||
case HTP_OP_GET_ROWS:
|
||||
return htp_fusion_enabled(HTP_FUSE_REPEAT_GET_ROWS) && fuse_repeat_get_rows(sess, prev, cur);
|
||||
case HTP_OP_CPY:
|
||||
return htp_fusion_enabled(HTP_FUSE_GDN_CPY) && fuse_gdn_cpy(sess, prev, cur);
|
||||
case HTP_OP_MUL_MAT:
|
||||
return htp_fusion_enabled(HTP_FUSE_MUL_MAT_NX) && fuse_mul_mat_nx(sess, prev, cur);
|
||||
case HTP_OP_MUL_MAT_ID:
|
||||
return htp_fusion_enabled(HTP_FUSE_MUL_MAT_ID_NX) && fuse_mul_mat_id_nx(sess, prev, cur);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef HTP_FUSION_H
|
||||
#define HTP_FUSION_H
|
||||
|
||||
#include "htp-common.h"
|
||||
|
||||
enum htp_fusion_flags {
|
||||
HTP_FUSE_ALLREDUCE_ADD = (1 << 1), // 2
|
||||
HTP_FUSE_RMS_NORM_MUL = (1 << 2), // 4
|
||||
HTP_FUSE_MUL_MAT_ADD = (1 << 3), // 8
|
||||
HTP_FUSE_MUL_MAT_NX = (1 << 4), // 16
|
||||
HTP_FUSE_MUL_MAT_ID_NX = (1 << 5), // 32
|
||||
HTP_FUSE_GDN_CPY = (1 << 6), // 64
|
||||
HTP_FUSE_REPEAT_GET_ROWS = (1 << 7), // 128
|
||||
HTP_FUSE_UNARY_MUL_GLU = (1 << 8), // 256
|
||||
HTP_FUSE_MUL_MUL_SCALAR = (1 << 9), // 512
|
||||
HTP_FUSE_BINARY_NX = (1 << 10), // 1024
|
||||
HTP_FUSE_RMS_NORM_SCALE = (1 << 11), // 2048
|
||||
HTP_FUSE_SSM_CONV_SILU = (1 << 12), // 4096
|
||||
};
|
||||
|
||||
static inline bool htp_fusion_enabled(int flag) {
|
||||
if (opt_opfusion <= 0) return false;
|
||||
if (opt_opfusion == 1) return true; // 1 enables all
|
||||
return (opt_opfusion & flag) != 0;
|
||||
}
|
||||
|
||||
bool htp_fusion_can_stack(const struct ggml_tensor * a);
|
||||
bool htp_fusion_can_stack(const struct ggml_tensor * a, const struct ggml_tensor * b);
|
||||
int htp_fusion_max(const struct ggml_cgraph * graph, int idx);
|
||||
void htp_fusion_tag_graph(struct ggml_cgraph * graph);
|
||||
bool htp_fusion_try_fuse(const struct ggml_hexagon_session * sess, struct htp_opnode & prev, const struct htp_opnode & cur);
|
||||
|
||||
#endif // HTP_FUSION_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,136 @@
|
||||
#ifndef HTP_KPARAMS_H
|
||||
#define HTP_KPARAMS_H
|
||||
|
||||
#include "htp-common.h"
|
||||
|
||||
#include "htp/allreduce-ops.h"
|
||||
#include "htp/argsort-ops.h"
|
||||
#include "htp/binary-ops.h"
|
||||
#include "htp/concat-ops.h"
|
||||
#include "htp/cpy-ops.h"
|
||||
#include "htp/cumsum-ops.h"
|
||||
#include "htp/diag-ops.h"
|
||||
#include "htp/fill-ops.h"
|
||||
#include "htp/flash-attn-ops.h"
|
||||
#include "htp/gated-delta-net-ops.h"
|
||||
#include "htp/get-rows-ops.h"
|
||||
#include "htp/glu-ops.h"
|
||||
#include "htp/im2col-ops.h"
|
||||
#include "htp/matmul-ops.h"
|
||||
#include "htp/pad-ops.h"
|
||||
#include "htp/pool-ops.h"
|
||||
#include "htp/repeat-ops.h"
|
||||
#include "htp/roll-ops.h"
|
||||
#include "htp/rope-ops.h"
|
||||
#include "htp/set-rows-ops.h"
|
||||
#include "htp/softmax-ops.h"
|
||||
#include "htp/solve-tri-ops.h"
|
||||
#include "htp/ssm-conv.h"
|
||||
#include "htp/sum-rows-ops.h"
|
||||
#include "htp/unary-ops.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
bool htp_rows_stride(const int64_t * ne, const size_t * nb, size_t * stride);
|
||||
bool htp_matmul_can_collapse(const struct ggml_tensor * src0, const struct ggml_tensor * src1, const struct ggml_tensor * dst);
|
||||
bool htp_matmul_add_can_collapse(const struct ggml_tensor * src0, const struct ggml_tensor * src1, const struct ggml_tensor * src2, const struct ggml_tensor * dst);
|
||||
ggml_tensor htp_tensor_collapse_rows(const struct ggml_tensor * t);
|
||||
|
||||
template <typename... Args>
|
||||
auto htp_kparams_type(bool (*)(Args...)) -> std::remove_pointer_t<std::remove_reference_t<decltype((std::declval<Args>(), ...))>>;
|
||||
|
||||
template <auto SolveFn, typename... Args>
|
||||
static inline bool htp_kparams_check(Args &&... args) {
|
||||
decltype(htp_kparams_type(SolveFn)) kparams;
|
||||
return SolveFn(std::forward<Args>(args)..., &kparams);
|
||||
}
|
||||
|
||||
struct htp_opnode;
|
||||
|
||||
bool htp_kparams_solve(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
struct htp_opnode & node
|
||||
);
|
||||
|
||||
void htp_opnode_collapse(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
struct htp_opnode & node
|
||||
);
|
||||
|
||||
bool htp_kparams_solve(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * op,
|
||||
void * kparams = nullptr
|
||||
);
|
||||
|
||||
static inline bool htp_kparams_check(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * op
|
||||
) {
|
||||
return htp_kparams_solve(sess, op, nullptr);
|
||||
}
|
||||
|
||||
bool htp_kparams_solve_allreduce(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * dst,
|
||||
uint32_t rank,
|
||||
uint32_t n_ranks,
|
||||
bool has_add,
|
||||
bool is_row_bcast,
|
||||
bool is_shard_ok,
|
||||
struct htp_allreduce_kernel_params * kparams
|
||||
);
|
||||
|
||||
bool htp_kparams_solve_rms_norm_mul(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * src1,
|
||||
const struct ggml_tensor * dst,
|
||||
struct htp_unary_kernel_params * kparams
|
||||
);
|
||||
|
||||
bool htp_kparams_solve_matmul_add(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * src1,
|
||||
const struct ggml_tensor * src2,
|
||||
const struct ggml_tensor * dst,
|
||||
struct htp_mm_kernel_params * kparams
|
||||
);
|
||||
|
||||
bool htp_kparams_solve_mmnx(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * act,
|
||||
int32_t n_weights,
|
||||
struct htp_mm_kernel_params * kparams
|
||||
);
|
||||
|
||||
bool htp_kparams_solve_mmidnx(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * act,
|
||||
const struct ggml_tensor * dst,
|
||||
int32_t n_weights,
|
||||
struct htp_mm_kernel_params * kparams
|
||||
);
|
||||
|
||||
bool htp_kparams_solve_repeat_get_rows(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * src1,
|
||||
const struct ggml_tensor * dst,
|
||||
struct htp_get_rows_kernel_params * kparams
|
||||
);
|
||||
|
||||
bool htp_kparams_solve_unary_mul_glu(
|
||||
const struct ggml_hexagon_session * sess,
|
||||
uint32_t op,
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * src1,
|
||||
const struct ggml_tensor * dst,
|
||||
struct htp_glu_kernel_params * kparams
|
||||
);
|
||||
|
||||
#endif // HTP_KPARAMS_H
|
||||
@@ -0,0 +1,260 @@
|
||||
#include "htp-opnode.h"
|
||||
|
||||
#include "htp/allreduce-ops.h"
|
||||
#include "htp/argsort-ops.h"
|
||||
#include "htp/binary-ops.h"
|
||||
#include "htp/cumsum-ops.h"
|
||||
#include "htp/diag-ops.h"
|
||||
#include "htp/fill-ops.h"
|
||||
#include "htp/flash-attn-ops.h"
|
||||
#include "htp/gated-delta-net-ops.h"
|
||||
#include "htp/get-rows-ops.h"
|
||||
#include "htp/glu-ops.h"
|
||||
#include "htp/matmul-ops.h"
|
||||
#include "htp/pad-ops.h"
|
||||
#include "htp/repeat-ops.h"
|
||||
#include "htp/roll-ops.h"
|
||||
#include "htp/softmax-ops.h"
|
||||
#include "htp/solve-tri-ops.h"
|
||||
#include "htp/ssm-conv.h"
|
||||
#include "htp/sum-rows-ops.h"
|
||||
#include "htp/unary-ops.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
std::string htp_opnode::name() const {
|
||||
switch (opcode) {
|
||||
case HTP_OP_MDEV_GROUP: return "MDEV_GROUP";
|
||||
case HTP_OP_CPY_FENCE: return "CPY+FENCE";
|
||||
case HTP_OP_FENCE: return (node && node->op_params[1] == 0) ? "FENCE_WAIT" : "FENCE_SIGNAL";
|
||||
case HTP_OP_ALLREDUCE: return "ALLREDUCE";
|
||||
case HTP_OP_ALLREDUCE_ADD: return "ALLREDUCE+ADD";
|
||||
case HTP_OP_HADAMARD: return "MUL_MAT_HADAMARD";
|
||||
case HTP_OP_MUL_MAT_NX: return "MUL_MAT_NX";
|
||||
case HTP_OP_MUL_MAT_ID_NX: return "MUL_MAT_ID_NX";
|
||||
case HTP_OP_ADD:
|
||||
case HTP_OP_SUB:
|
||||
case HTP_OP_MUL:
|
||||
case HTP_OP_DIV:
|
||||
if (!fused.empty()) {
|
||||
const auto * kp = kparams<htp_binary_kernel_params>();
|
||||
if (kp->kernel_type != HTP_BINARY_KERNEL_SCALAR) {
|
||||
switch (opcode) {
|
||||
case HTP_OP_ADD: return "ADD_NX";
|
||||
case HTP_OP_SUB: return "SUB_NX";
|
||||
case HTP_OP_MUL: return "MUL_NX";
|
||||
case HTP_OP_DIV: return "DIV_NX";
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return "";
|
||||
}
|
||||
if (htp_is_hadamard(node)) {
|
||||
return "MUL_MAT_HADAMARD";
|
||||
}
|
||||
|
||||
std::string res = ggml_op_desc(node);
|
||||
for (const auto * t : fused) {
|
||||
res += "+";
|
||||
res += ggml_op_desc(t);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char * format_tensor_name(const struct ggml_tensor * t) {
|
||||
return t ? t->name : "NONE";
|
||||
}
|
||||
|
||||
static std::string format_tensor_dims(const struct ggml_tensor * t) {
|
||||
if (!t) {
|
||||
return "NONE";
|
||||
}
|
||||
char buf[64];
|
||||
if (t->ne[2] == 1 && t->ne[3] == 1) {
|
||||
snprintf(buf, sizeof(buf), "%d:%d", (int) t->ne[0], (int) t->ne[1]);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%d:%d:%d:%d", (int) t->ne[0], (int) t->ne[1], (int) t->ne[2], (int) t->ne[3]);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char * format_tensor_type(const struct ggml_tensor * t) {
|
||||
return t ? ggml_type_name(t->type) : "NONE";
|
||||
}
|
||||
|
||||
static std::string format_tensor_strides(const struct ggml_tensor * t) {
|
||||
if (!t) {
|
||||
return "NONE";
|
||||
}
|
||||
char buf[64];
|
||||
const char * c = ggml_is_contiguous(t) ? "" : "!";
|
||||
if (t->ne[2] == 1 && t->ne[3] == 1) {
|
||||
snprintf(buf, sizeof(buf), "%zu:%zu%s", (size_t) t->nb[0], (size_t) t->nb[1], c);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%zu:%zu:%zu:%zu%s", (size_t) t->nb[0], (size_t) t->nb[1], (size_t) t->nb[2], (size_t) t->nb[3], c);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char * format_tensor_buff(const struct ggml_tensor * t) {
|
||||
if (t && t->buffer) {
|
||||
return ggml_backend_buffer_name(t->buffer);
|
||||
}
|
||||
return "NONE";
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
static std::string format_op_tensors(const htp_opnode & node, Fn fn) {
|
||||
std::string res;
|
||||
const auto & inputs = node.get_inputs();
|
||||
const auto & outputs = node.get_outputs();
|
||||
|
||||
if (!inputs.empty()) {
|
||||
for (size_t i = 0; i < inputs.size(); i++) {
|
||||
if (i > 0) res += " x ";
|
||||
res += fn(inputs[i]);
|
||||
}
|
||||
res += " -> ";
|
||||
}
|
||||
|
||||
if (outputs.empty()) {
|
||||
res += fn(node.dst());
|
||||
} else {
|
||||
for (size_t i = 0; i < outputs.size(); i++) {
|
||||
if (i > 0) res += " x ";
|
||||
res += fn(outputs[i]);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::string format_kernel_params(const htp_opnode & node) {
|
||||
char str[128];
|
||||
const size_t max_size = sizeof(str);
|
||||
|
||||
if (node.opcode == HTP_OP_MUL_MAT || node.opcode == HTP_OP_MUL_MAT_ID ||
|
||||
node.opcode == HTP_OP_MUL_MAT_NX || node.opcode == HTP_OP_MUL_MAT_ID_NX ||
|
||||
node.opcode == HTP_OP_MUL_MAT_ADD) {
|
||||
const auto * kparams = node.kparams<htp_mm_kernel_params>();
|
||||
const char * path = "unknown";
|
||||
int32_t type = kparams->kernel_type;
|
||||
if (type == HTP_MM_KERNEL_HMX_2D || type == HTP_MM_KERNEL_HMX_BATCHED) {
|
||||
path = (kparams->tile_size > 0) ? "hmx-tiled" : "hmx-flat";
|
||||
} else if (type == HTP_MM_KERNEL_HVX_F16_F16_VTCM || type == HTP_MM_KERNEL_HVX_F32_F32_VTCM ||
|
||||
type == HTP_MM_KERNEL_HVX_BF16_F32_VTCM) {
|
||||
path = "hvx-flat";
|
||||
} else if (type == HTP_MM_KERNEL_HVX_QUANT_ROW || type == HTP_MM_KERNEL_HVX_QUANT_BLOCK) {
|
||||
path = "hvx-tiled";
|
||||
}
|
||||
snprintf(str, max_size, "%s vtcm %d", path, (int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_FLASH_ATTN_EXT) {
|
||||
const auto * kparams = node.kparams<htp_fa_kernel_params>();
|
||||
const char * path = "unknown";
|
||||
int32_t type = kparams->kernel_type;
|
||||
if (type == HTP_FA_KERNEL_HMX) {
|
||||
path = kparams->u.hmx.pipeline ? "hmx-pipe" : "hmx-seq";
|
||||
} else if (type == HTP_FA_KERNEL_HVX) {
|
||||
path = "hvx";
|
||||
}
|
||||
snprintf(str, max_size, "%s vtcm %d", path, (int) kparams->vtcm_size);
|
||||
} else if (htp_op_is_unary(node.opcode)) {
|
||||
const auto * kparams = node.kparams<htp_unary_kernel_params>();
|
||||
snprintf(str, max_size, "%s vtcm %d", kparams->col_tile ? "wide-row" : "row-block", (int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_MDEV_GROUP && node.node) {
|
||||
snprintf(str, max_size, "idx %d count %d", (int) node.node->op_params[0], (int) node.dst()->ne[1]);
|
||||
} else if ((node.opcode == HTP_OP_FENCE || node.opcode == HTP_OP_CPY_FENCE) && node.node) {
|
||||
snprintf(str, max_size, "seq 0x%x", (uint32_t) node.node->op_params[0]);
|
||||
} else if (node.opcode == HTP_OP_ALLREDUCE && node.node) {
|
||||
snprintf(str, max_size, "seq 0x%x -> 0x%x", (uint32_t) node.node->op_params[0], (uint32_t) node.node->op_params[1]);
|
||||
} else if (node.opcode == HTP_OP_SSM_CONV) {
|
||||
const auto * kparams = node.kparams<htp_ssm_conv_kernel_params>();
|
||||
snprintf(str, max_size, "%s vtcm %d", kparams->n_t == 1 ? "decode" : "prefill", (int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_SOFTMAX) {
|
||||
const auto * kparams = node.kparams<htp_softmax_kernel_params>();
|
||||
snprintf(str, max_size, "k%d nth %d vtcm %d", (int) kparams->kernel_type, (int) kparams->n_threads, (int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_GATED_DELTA_NET) {
|
||||
const auto * kparams = node.kparams<htp_gdn_kernel_params>();
|
||||
const char * path = (kparams->kernel_type == HTP_GDN_KERNEL_HMX_CHUNKED) ? "hmx-chunked" : "hvx-recurrent";
|
||||
snprintf(str, max_size, "%s-%s vtcm %u", path, kparams->kda ? "kda" : "scalar",
|
||||
(unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_MUL || node.opcode == HTP_OP_ADD || node.opcode == HTP_OP_ADD_ID ||
|
||||
node.opcode == HTP_OP_SUB || node.opcode == HTP_OP_DIV) {
|
||||
const auto * kparams = node.kparams<htp_binary_kernel_params>();
|
||||
snprintf(str, max_size, "vtcm %u", (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_ARGSORT || node.opcode == HTP_OP_TOP_K) {
|
||||
const auto * kparams = node.kparams<htp_sort_kernel_params>();
|
||||
snprintf(str, max_size, "%s nth %d nchk %d chk %d vtcm %d",
|
||||
node.opcode == HTP_OP_TOP_K ? "top_k" : "argsort",
|
||||
(int) kparams->n_threads, (int) kparams->n_chunks,
|
||||
(int) kparams->chunk_elems, (int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_GET_ROWS) {
|
||||
const auto * kparams = node.kparams<htp_get_rows_kernel_params>();
|
||||
const char * ktype_str = "unknown";
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_GET_ROWS_KERNEL_SAMETYPE: ktype_str = "sametype"; break;
|
||||
case HTP_GET_ROWS_KERNEL_TILED: ktype_str = "tiled"; break;
|
||||
case HTP_GET_ROWS_KERNEL_FLAT: ktype_str = "flat"; break;
|
||||
}
|
||||
snprintf(str, max_size, "%s%s vtcm %u", ktype_str, kparams->n_threads > 1 ? "-multi" : "", (unsigned int) kparams->vtcm_size);
|
||||
} else if (htp_op_is_glu(node.opcode)) {
|
||||
const auto * kparams = node.kparams<htp_glu_kernel_params>();
|
||||
snprintf(str, max_size, "nth %d vtcm %u", (int) kparams->n_threads, (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_PAD) {
|
||||
const auto * kparams = node.kparams<htp_pad_kernel_params>();
|
||||
snprintf(str, max_size, "k%d nth %d vtcm %u", (int) kparams->kernel_type, (int) kparams->n_threads, (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_CUMSUM) {
|
||||
const auto * kparams = node.kparams<htp_cumsum_kernel_params>();
|
||||
snprintf(str, max_size, "%s nth %d vtcm %u", kparams->use_dma ? "dma" : "hvx", (int) kparams->n_threads, (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_FILL) {
|
||||
const auto * kparams = node.kparams<htp_fill_kernel_params>();
|
||||
snprintf(str, max_size, "%s nth %d", kparams->kernel_type == HTP_FILL_KERNEL_CONTIGUOUS ? "contig" : "strided", (int) kparams->n_threads);
|
||||
} else if (node.opcode == HTP_OP_ROLL) {
|
||||
const auto * kparams = node.kparams<htp_roll_kernel_params>();
|
||||
snprintf(str, max_size, "k%d nth %d", (int) kparams->kernel_type, (int) kparams->n_threads);
|
||||
} else if (node.opcode == HTP_OP_DIAG) {
|
||||
const auto * kparams = node.kparams<htp_diag_kernel_params>();
|
||||
snprintf(str, max_size, "%s nth %d vtcm %u", kparams->use_dma ? "dma" : "hvx", (int) kparams->n_threads, (unsigned int) kparams->vtcm_size);
|
||||
} else if (node.opcode == HTP_OP_SOLVE_TRI) {
|
||||
const auto * kparams = node.kparams<htp_solve_tri_kernel_params>();
|
||||
snprintf(str, max_size, "%s nth %d", kparams->batched ? "batched" : "chunked", (int) kparams->n_threads);
|
||||
} else {
|
||||
snprintf(str, max_size, "----");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static void append_section(std::string & res, const std::string & sec) {
|
||||
if (!res.empty()) {
|
||||
res += "|";
|
||||
}
|
||||
res += sec;
|
||||
}
|
||||
|
||||
std::string htp_format_tensor(const ggml_tensor * t, uint32_t flags) {
|
||||
std::string res;
|
||||
if (flags & HTP_OPFMT_NAMES) append_section(res, format_tensor_name(t));
|
||||
if (flags & HTP_OPFMT_DIMS) append_section(res, format_tensor_dims(t));
|
||||
if (flags & HTP_OPFMT_TYPES) append_section(res, format_tensor_type(t));
|
||||
if (flags & HTP_OPFMT_STRIDES) append_section(res, format_tensor_strides(t));
|
||||
if (flags & HTP_OPFMT_BUFFS) append_section(res, format_tensor_buff(t));
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string htp_format_op(const htp_opnode & node, uint32_t flags) {
|
||||
std::string res;
|
||||
if (flags & HTP_OPFMT_OP) append_section(res, node.name());
|
||||
if (flags & HTP_OPFMT_NAMES) append_section(res, format_op_tensors(node, format_tensor_name));
|
||||
if (flags & HTP_OPFMT_DIMS) append_section(res, format_op_tensors(node, format_tensor_dims));
|
||||
if (flags & HTP_OPFMT_TYPES) append_section(res, format_op_tensors(node, format_tensor_type));
|
||||
if (flags & HTP_OPFMT_STRIDES) append_section(res, format_op_tensors(node, format_tensor_strides));
|
||||
if (flags & HTP_OPFMT_BUFFS) append_section(res, format_op_tensors(node, format_tensor_buff));
|
||||
if (flags & HTP_OPFMT_KPARAMS) append_section(res, format_kernel_params(node));
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifndef HTP_OPNODE_H
|
||||
#define HTP_OPNODE_H
|
||||
|
||||
#include "ggml-backend-impl.h"
|
||||
#include "ggml-impl.h"
|
||||
#include "htp/htp-ops.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define HTP_OP_MAX_FUSED 16
|
||||
|
||||
// local std::inplace_vector replacement
|
||||
template <typename T, size_t MAX_N>
|
||||
struct htp_opvec {
|
||||
T data[MAX_N] {};
|
||||
size_t n = 0;
|
||||
|
||||
htp_opvec() = default;
|
||||
htp_opvec(std::initializer_list<T> init) {
|
||||
GGML_ASSERT(init.size() <= MAX_N);
|
||||
for (const auto & v : init) {
|
||||
data[n++] = v;
|
||||
}
|
||||
}
|
||||
|
||||
size_t size() const { return n; }
|
||||
bool empty() const { return n == 0; }
|
||||
void clear() { n = 0; }
|
||||
T * begin() { return data; }
|
||||
T * end() { return data + n; }
|
||||
const T * begin() const { return data; }
|
||||
const T * end() const { return data + n; }
|
||||
T & back() { return data[n - 1]; }
|
||||
const T & back() const { return data[n - 1]; }
|
||||
T & operator[](size_t i) { return data[i]; }
|
||||
const T & operator[](size_t i) const { return data[i]; }
|
||||
|
||||
void push_back(T val) {
|
||||
GGML_ASSERT(n < MAX_N);
|
||||
data[n++] = val;
|
||||
}
|
||||
|
||||
void insert(T * pos, T val) {
|
||||
GGML_ASSERT(n < MAX_N);
|
||||
for (T * p = data + n; p > pos; --p) {
|
||||
*p = *(p - 1);
|
||||
}
|
||||
*pos = val;
|
||||
n++;
|
||||
}
|
||||
};
|
||||
|
||||
static inline bool htp_is_hadamard(const struct ggml_tensor * t) {
|
||||
return t && t->op == GGML_OP_MUL_MAT && ggml_get_op_params_i32(t, 1) == GGML_HINT_SRC0_IS_HADAMARD;
|
||||
}
|
||||
|
||||
struct htp_opnode {
|
||||
const ggml_tensor * node { nullptr };
|
||||
htp_op_code opcode { HTP_OP_INVALID };
|
||||
int32_t params[HTP_OP_MAX_PARAMS] {0};
|
||||
int32_t kernel_params[HTP_OP_MAX_KERN_PARAMS] {0};
|
||||
|
||||
std::vector<std::shared_ptr<ggml_tensor>> dummy;
|
||||
|
||||
htp_opvec<const ggml_tensor *, HTP_OP_MAX_FUSED> fused;
|
||||
htp_opvec<const ggml_tensor *, HTP_OP_MAX_INPUTS> inputs;
|
||||
htp_opvec<const ggml_tensor *, HTP_OP_MAX_OUTPUTS> outputs;
|
||||
|
||||
int n_active_src(const ggml_tensor * t) const {
|
||||
if (!t) return 0;
|
||||
for (int i = GGML_MAX_SRC - 1; i >= 0; i--) {
|
||||
if (t->src[i]) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init(const ggml_tensor * node) {
|
||||
static_assert(sizeof(this->params) == sizeof(this->node->op_params), "params size mismatch");
|
||||
|
||||
this->node = node;
|
||||
if (!node) return;
|
||||
|
||||
memcpy(this->params, this->node->op_params, sizeof(this->params));
|
||||
|
||||
// Build inputs (preserving optional nullptrs)
|
||||
if (htp_is_hadamard(this->node)) {
|
||||
this->inputs.push_back(this->node->src[1]);
|
||||
} else {
|
||||
int n_inputs = n_active_src(this->node);
|
||||
for (int i = 0; i < n_inputs; i++) {
|
||||
this->inputs.push_back(this->node->src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Build outputs
|
||||
this->outputs.push_back(this->dst());
|
||||
}
|
||||
|
||||
htp_opnode(htp_op_code opcode = HTP_OP_INVALID, const ggml_tensor * node = nullptr) : opcode(opcode) {
|
||||
init(node);
|
||||
}
|
||||
|
||||
htp_opnode(const ggml_tensor * node) : opcode(HTP_OP_INVALID) {
|
||||
init(node);
|
||||
}
|
||||
|
||||
const ggml_tensor * src0() const { return node->src[0]; }
|
||||
const ggml_tensor * src1() const { return node->src[1]; }
|
||||
const ggml_tensor * dst() const { return outputs.empty() ? node : outputs.back(); }
|
||||
|
||||
ggml_tensor * add_dummy(const ggml_tensor & t) {
|
||||
dummy.push_back(std::make_shared<ggml_tensor>(t));
|
||||
return dummy.back().get();
|
||||
}
|
||||
|
||||
const htp_opvec<const ggml_tensor *, HTP_OP_MAX_INPUTS> & get_inputs() const {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
const htp_opvec<const ggml_tensor *, HTP_OP_MAX_OUTPUTS> & get_outputs() const {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T * kparams() {
|
||||
static_assert(sizeof(T) <= sizeof(kernel_params), "kparams size exceeds HTP_OP_MAX_KERN_PARAMS");
|
||||
static_assert(alignof(T) <= alignof(int32_t), "kparams alignment exceeds int32_t");
|
||||
return reinterpret_cast<T *>(kernel_params);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T * kparams() const {
|
||||
static_assert(sizeof(T) <= sizeof(kernel_params), "kparams size exceeds HTP_OP_MAX_KERN_PARAMS");
|
||||
static_assert(alignof(T) <= alignof(int32_t), "kparams alignment exceeds int32_t");
|
||||
return reinterpret_cast<const T *>(kernel_params);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_kparams(const T & kp) {
|
||||
static_assert(sizeof(T) <= sizeof(kernel_params), "kparams size exceeds HTP_OP_MAX_KERN_PARAMS");
|
||||
static_assert(alignof(T) <= alignof(int32_t), "kparams alignment exceeds int32_t");
|
||||
memcpy(kernel_params, &kp, sizeof(T));
|
||||
}
|
||||
|
||||
std::string name() const;
|
||||
};
|
||||
|
||||
enum htp_opformat_flags {
|
||||
HTP_OPFMT_OP = (1 << 0),
|
||||
HTP_OPFMT_NAMES = (1 << 1),
|
||||
HTP_OPFMT_DIMS = (1 << 2),
|
||||
HTP_OPFMT_TYPES = (1 << 3),
|
||||
HTP_OPFMT_STRIDES = (1 << 4),
|
||||
HTP_OPFMT_BUFFS = (1 << 5),
|
||||
HTP_OPFMT_KPARAMS = (1 << 6),
|
||||
|
||||
HTP_OPFMT_EXEC = HTP_OPFMT_OP | HTP_OPFMT_NAMES | HTP_OPFMT_DIMS | HTP_OPFMT_TYPES | HTP_OPFMT_STRIDES | HTP_OPFMT_BUFFS | HTP_OPFMT_KPARAMS,
|
||||
HTP_OPFMT_SUPP = HTP_OPFMT_OP | HTP_OPFMT_NAMES | HTP_OPFMT_DIMS | HTP_OPFMT_TYPES | HTP_OPFMT_STRIDES | HTP_OPFMT_BUFFS,
|
||||
HTP_OPFMT_PROF = HTP_OPFMT_OP | HTP_OPFMT_NAMES | HTP_OPFMT_DIMS | HTP_OPFMT_TYPES | HTP_OPFMT_STRIDES | HTP_OPFMT_KPARAMS,
|
||||
HTP_OPFMT_TENSOR = HTP_OPFMT_NAMES | HTP_OPFMT_DIMS | HTP_OPFMT_TYPES | HTP_OPFMT_STRIDES,
|
||||
};
|
||||
|
||||
std::string htp_format_op(const htp_opnode & node, uint32_t flags = HTP_OPFMT_EXEC);
|
||||
std::string htp_format_tensor(const ggml_tensor * t, uint32_t flags = HTP_OPFMT_TENSOR);
|
||||
|
||||
#endif // HTP_OPNODE_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,69 @@
|
||||
#ifndef HTP_REPACK_H
|
||||
#define HTP_REPACK_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ggml.h"
|
||||
#include "htp/htp-tensor.h"
|
||||
|
||||
static inline bool htp_is_tiled_weight_type(enum ggml_type type) {
|
||||
return type == GGML_TYPE_Q4_0 || type == GGML_TYPE_Q4_1 ||
|
||||
type == GGML_TYPE_Q8_0 || type == GGML_TYPE_IQ4_NL ||
|
||||
type == GGML_TYPE_MXFP4 || type == GGML_TYPE_Q1_0 ||
|
||||
type == GGML_TYPE_Q6_K ||
|
||||
type == GGML_TYPE_Q4_K || type == GGML_TYPE_Q5_K ||
|
||||
type == GGML_TYPE_Q3_K || type == GGML_TYPE_Q2_K ||
|
||||
type == GGML_TYPE_Q2_0 ||
|
||||
type == GGML_TYPE_F16 || type == GGML_TYPE_F32 ||
|
||||
type == GGML_TYPE_BF16;
|
||||
}
|
||||
|
||||
static inline bool htp_is_tiled_weight_tensor(const struct ggml_tensor * t) {
|
||||
if (!ggml_is_contiguous(t) || ggml_is_view(t)) {
|
||||
return false;
|
||||
}
|
||||
if (!htp_is_tiled_weight_type(t->type)) {
|
||||
return false;
|
||||
}
|
||||
if (t->ne[0] < 32) {
|
||||
return false;
|
||||
}
|
||||
if (!ggml_is_quantized(t->type)) {
|
||||
return (t->ne[0] % 32 == 0) && (t->ne[1] % 32 == 0) && (t->ne[2] == 1) && (t->ne[3] == 1);
|
||||
}
|
||||
return t->ne[3] == 1;
|
||||
}
|
||||
|
||||
// Size of one tiled row in the DSP tiled layout. The Q3_K..Q6_K tiles store uncompressed scales/mins,
|
||||
// so they are larger than the ggml blocks. For the other tiled types (Q2_K included) the tile has the same size.
|
||||
static inline size_t htp_tiled_row_size(enum ggml_type type, int64_t ne0) {
|
||||
if (type == GGML_TYPE_F16 || type == GGML_TYPE_F32 || type == GGML_TYPE_BF16) {
|
||||
return (size_t) ne0 * sizeof(ggml_fp16_t);
|
||||
}
|
||||
if (type == GGML_TYPE_Q6_K) {
|
||||
return (size_t) (ne0 / 32) * (HTP_TENSOR_TILE_SIZE_Q6_K / 32);
|
||||
}
|
||||
if (type == GGML_TYPE_Q2_0) {
|
||||
return (size_t) (ne0 / 64) * 18;
|
||||
}
|
||||
if (type == GGML_TYPE_Q4_K) {
|
||||
return (size_t) (ne0 / 32) * (HTP_TENSOR_TILE_SIZE_Q4_K / 32);
|
||||
}
|
||||
if (type == GGML_TYPE_Q5_K) {
|
||||
return (size_t) (ne0 / 32) * (HTP_TENSOR_TILE_SIZE_Q5_K / 32);
|
||||
}
|
||||
if (type == GGML_TYPE_Q3_K) {
|
||||
return (size_t) (ne0 / 32) * (HTP_TENSOR_TILE_SIZE_Q3_K / 32);
|
||||
}
|
||||
return ggml_row_size(type, ne0);
|
||||
}
|
||||
|
||||
static inline bool htp_is_hmx_weight_type(enum ggml_type type) {
|
||||
return htp_is_tiled_weight_type(type);
|
||||
}
|
||||
|
||||
void htp_repack_tensor_tiled(struct ggml_tensor * t, const void * data, size_t size);
|
||||
void htp_unpack_tensor_tiled(void * data, const struct ggml_tensor * t, size_t offset, size_t size);
|
||||
|
||||
#endif // HTP_REPACK_H
|
||||
@@ -0,0 +1,69 @@
|
||||
cmake_minimum_required(VERSION 3.22.2)
|
||||
project(ggml-htp C CXX ASM)
|
||||
|
||||
include(${HEXAGON_SDK_ROOT}/build/cmake/hexagon_fun.cmake)
|
||||
|
||||
include_directories(
|
||||
${HEXAGON_SDK_ROOT}/incs
|
||||
${HEXAGON_SDK_ROOT}/incs/stddef
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../../include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(HTP_LIB ggml-htp-${DSP_VERSION})
|
||||
|
||||
add_library(${HTP_LIB} SHARED
|
||||
main.c
|
||||
htp_iface_skel.c
|
||||
work-queue.c
|
||||
dma-queue.c
|
||||
hmx-queue.c
|
||||
htp-dcache.c
|
||||
matmul-ops.c
|
||||
flash-attn-ops.c
|
||||
gated-delta-net-ops.c
|
||||
binary-ops.c
|
||||
unary-ops.c
|
||||
sum-rows-ops.c
|
||||
softmax-ops.c
|
||||
glu-ops.c
|
||||
rope-ops.c
|
||||
set-rows-ops.c
|
||||
get-rows-ops.c
|
||||
cpy-ops.c
|
||||
gather-rows.c
|
||||
repeat-ops.c
|
||||
ssm-conv.c
|
||||
cumsum-ops.c
|
||||
fill-ops.c
|
||||
concat-ops.c
|
||||
diag-ops.c
|
||||
solve-tri-ops.c
|
||||
pad-ops.c
|
||||
argsort-ops.c
|
||||
im2col-ops.c
|
||||
roll-ops.c
|
||||
pool-ops.c
|
||||
allreduce-ops.c
|
||||
)
|
||||
|
||||
target_compile_definitions(${HTP_LIB} PRIVATE
|
||||
$<IF:$<BOOL:${HEXAGON_HTP_DEBUG}>,HTP_DEBUG=1,NDEBUG=1>
|
||||
$<IF:$<BOOL:${HEXAGON_HTP_DEBUG}>,FARF_HIGH=1,>)
|
||||
|
||||
build_idl(htp_iface.idl ${HTP_LIB})
|
||||
|
||||
set_target_properties(${HTP_LIB} PROPERTIES EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if (NOT Python3_EXECUTABLE)
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
endif()
|
||||
|
||||
add_custom_command(TARGET ${HTP_LIB} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Adding SWIV section $<TARGET_FILE_NAME:${HTP_LIB}>"
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/htp-swiv.py -i $<TARGET_FILE:${HTP_LIB}> -o $<TARGET_FILE:${HTP_LIB}>
|
||||
VERBATIM)
|
||||
|
||||
install(TARGETS ${HTP_LIB})
|
||||
@@ -0,0 +1,475 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <HAP_perf.h>
|
||||
#include <stdatomic.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "dma-queue.h"
|
||||
#include "hex-profile.h"
|
||||
#include "allreduce-ops.h"
|
||||
#include "htp-fence.h"
|
||||
|
||||
struct htp_allreduce_context {
|
||||
struct htp_ops_context * octx;
|
||||
uint32_t n_ranks;
|
||||
uint32_t n_dsts;
|
||||
uint32_t nelem;
|
||||
uint32_t ne0;
|
||||
uint32_t ne1;
|
||||
uint32_t row_size_aligned;
|
||||
uint32_t rank_elem_start;
|
||||
uint32_t rank_nelem;
|
||||
uint32_t elems_per_thread;
|
||||
uint32_t block_elems;
|
||||
uint32_t vtcm_size_per_thread;
|
||||
bool is_row_bcast;
|
||||
uint8_t * src_spad_base[HTP_ALLREDUCE_MAX_RANKS];
|
||||
uint8_t * dst_spad_base;
|
||||
uint8_t * res_spad_base;
|
||||
};
|
||||
|
||||
#define DEFINE_ALLREDUCE_THREAD_DMA_1D(SUFFIX, TYPE, HVX_ADD_FN, HAS_ADD) \
|
||||
static void allreduce_thread_dma_1d_##SUFFIX(unsigned int nth, unsigned int ith, void * data) { \
|
||||
struct htp_allreduce_context * actx = (struct htp_allreduce_context *) data; \
|
||||
struct htp_ops_context * octx = actx->octx; \
|
||||
\
|
||||
const uint32_t n_ranks = actx->n_ranks; \
|
||||
const uint32_t n_dsts = actx->n_dsts; \
|
||||
const uint32_t block_elems = actx->block_elems; \
|
||||
\
|
||||
const uint32_t dr = actx->elems_per_thread; \
|
||||
const uint32_t ir0 = actx->rank_elem_start + dr * ith; \
|
||||
const uint32_t ir1 = MIN(ir0 + dr, actx->rank_elem_start + actx->rank_nelem); \
|
||||
if (ir0 >= ir1) return; \
|
||||
\
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith]; \
|
||||
dma_queue * dma_q = octx->ctx->dma[ith]; \
|
||||
\
|
||||
const size_t vtcm_thread_offset = ith * actx->vtcm_size_per_thread; \
|
||||
uint8_t * dst_spad_base = actx->dst_spad_base + vtcm_thread_offset; \
|
||||
uint8_t * res_spad_base = HAS_ADD ? (actx->res_spad_base + vtcm_thread_offset) : NULL; \
|
||||
\
|
||||
const size_t spad_half = actx->vtcm_size_per_thread / 2; \
|
||||
uint32_t ir_prefetch = ir0; \
|
||||
int spad_idx = 0; \
|
||||
\
|
||||
for (int k = 0; k < 2 && ir_prefetch < ir1; k++) { \
|
||||
uint32_t cur_elems = MIN(block_elems, ir1 - ir_prefetch); \
|
||||
size_t cur_bytes = cur_elems * sizeof(TYPE); \
|
||||
uint8_t * d_spad = dst_spad_base + spad_idx * spad_half; \
|
||||
for (uint32_t d = 0; d < n_dsts; d++) { \
|
||||
dma_addr_t d_ddr = octx->dsts[d]->data + ir_prefetch * sizeof(TYPE); \
|
||||
dma_queue_push(dma_q, dma_make_data(d_ddr, d_spad), cur_bytes, cur_bytes, cur_bytes, 0); \
|
||||
} \
|
||||
for (uint32_t s = 0; s < n_ranks; s++) { \
|
||||
uint8_t * s_spad = actx->src_spad_base[s] + vtcm_thread_offset + spad_idx * spad_half; \
|
||||
const dma_addr_t s_ddr = octx->src[s]->data + ir_prefetch * sizeof(TYPE); \
|
||||
dma_queue_push(dma_q, dma_make_data(s_spad, s_ddr), cur_bytes, cur_bytes, cur_bytes, 1); \
|
||||
} \
|
||||
if (HAS_ADD) { \
|
||||
uint8_t * r_spad = res_spad_base + spad_idx * spad_half; \
|
||||
const dma_addr_t r_ddr = octx->src[2 * n_ranks]->data + ir_prefetch * sizeof(TYPE); \
|
||||
dma_queue_push(dma_q, dma_make_data(r_spad, r_ddr), cur_bytes, cur_bytes, cur_bytes, 1); \
|
||||
} \
|
||||
ir_prefetch += cur_elems; \
|
||||
spad_idx ^= 1; \
|
||||
} \
|
||||
\
|
||||
int comp_spad_idx = 0; \
|
||||
for (uint32_t ir = ir0; ir < ir1; ) { \
|
||||
uint32_t cur_elems = MIN(block_elems, ir1 - ir); \
|
||||
size_t cur_bytes = cur_elems * sizeof(TYPE); \
|
||||
for (uint32_t d = 0; d < n_dsts; d++) { \
|
||||
dma_queue_pop(dma_q); \
|
||||
} \
|
||||
for (uint32_t s = 0; s < n_ranks; s++) { \
|
||||
dma_queue_pop(dma_q); \
|
||||
} \
|
||||
if (HAS_ADD) { \
|
||||
dma_queue_pop(dma_q); \
|
||||
} \
|
||||
uint8_t * d_spad = dst_spad_base + comp_spad_idx * spad_half; \
|
||||
uint8_t * s0_spad = actx->src_spad_base[0] + vtcm_thread_offset + comp_spad_idx * spad_half; \
|
||||
uint8_t * s1_spad = actx->src_spad_base[1] + vtcm_thread_offset + comp_spad_idx * spad_half; \
|
||||
uint8_t * r_spad = HAS_ADD ? (res_spad_base + comp_spad_idx * spad_half) : NULL; \
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir); \
|
||||
HVX_ADD_FN(d_spad, s0_spad, s1_spad, cur_elems); \
|
||||
for (uint32_t s = 2; s < n_ranks; s++) { \
|
||||
uint8_t * ss_spad = actx->src_spad_base[s] + vtcm_thread_offset + comp_spad_idx * spad_half; \
|
||||
HVX_ADD_FN(d_spad, d_spad, ss_spad, cur_elems); \
|
||||
} \
|
||||
if (HAS_ADD) { \
|
||||
HVX_ADD_FN(d_spad, d_spad, r_spad, cur_elems); \
|
||||
} \
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir); \
|
||||
for (uint32_t d = 0; d < n_dsts; d++) { \
|
||||
dma_addr_t d_ddr = octx->dsts[d]->data + ir * sizeof(TYPE); \
|
||||
dma_queue_push(dma_q, dma_make_data(d_ddr, d_spad), cur_bytes, cur_bytes, cur_bytes, 1); \
|
||||
} \
|
||||
if (ir_prefetch < ir1) { \
|
||||
uint32_t next_elems = MIN(block_elems, ir1 - ir_prefetch); \
|
||||
size_t next_bytes = next_elems * sizeof(TYPE); \
|
||||
for (uint32_t s = 0; s < n_ranks; s++) { \
|
||||
uint8_t * s_spad = actx->src_spad_base[s] + vtcm_thread_offset + comp_spad_idx * spad_half; \
|
||||
const dma_addr_t s_next = octx->src[s]->data + ir_prefetch * sizeof(TYPE); \
|
||||
dma_queue_push(dma_q, dma_make_data(s_spad, s_next), next_bytes, next_bytes, next_bytes, 1); \
|
||||
} \
|
||||
if (HAS_ADD) { \
|
||||
uint8_t * r_spad_next = res_spad_base + comp_spad_idx * spad_half; \
|
||||
const dma_addr_t r_next = octx->src[2 * n_ranks]->data + ir_prefetch * sizeof(TYPE); \
|
||||
dma_queue_push(dma_q, dma_make_data(r_spad_next, r_next), next_bytes, next_bytes, next_bytes, 1); \
|
||||
} \
|
||||
ir_prefetch += next_elems; \
|
||||
} \
|
||||
comp_spad_idx ^= 1; \
|
||||
ir += cur_elems; \
|
||||
} \
|
||||
dma_queue_flush(dma_q); \
|
||||
}
|
||||
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_1D(f16, __fp16, hvx_add_f16_aaa, 0)
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_1D(f32, float, hvx_add_f32_aaa, 0)
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_1D(add_f16, __fp16, hvx_add_f16_aaa, 1)
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_1D(add_f32, float, hvx_add_f32_aaa, 1)
|
||||
|
||||
#define DEFINE_ALLREDUCE_THREAD_DMA_2D(SUFFIX, TYPE, HVX_ADD_FN, HAS_ADD, IS_ROW_BCAST) \
|
||||
static void allreduce_thread_dma_2d_##SUFFIX(unsigned int nth, unsigned int ith, void * data) { \
|
||||
struct htp_allreduce_context * actx = (struct htp_allreduce_context *) data; \
|
||||
struct htp_ops_context * octx = actx->octx; \
|
||||
\
|
||||
const uint32_t n_ranks = actx->n_ranks; \
|
||||
const uint32_t n_dsts = actx->n_dsts; \
|
||||
const uint32_t ne0 = actx->ne0; \
|
||||
const uint32_t block_rows = actx->block_elems; \
|
||||
const uint32_t row_size_aligned = actx->row_size_aligned; \
|
||||
const uint32_t row_bytes = ne0 * sizeof(TYPE); \
|
||||
\
|
||||
const uint32_t dr = actx->elems_per_thread; \
|
||||
const uint32_t r0 = actx->rank_elem_start + dr * ith; \
|
||||
const uint32_t r1 = MIN(r0 + dr, actx->rank_elem_start + actx->rank_nelem); \
|
||||
if (r0 >= r1) return; \
|
||||
\
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith]; \
|
||||
dma_queue * dma_q = octx->ctx->dma[ith]; \
|
||||
\
|
||||
const size_t vtcm_thread_offset = ith * actx->vtcm_size_per_thread; \
|
||||
uint8_t * dst_spad_base = actx->dst_spad_base + vtcm_thread_offset; \
|
||||
uint8_t * res_spad_base = HAS_ADD ? (IS_ROW_BCAST ? actx->res_spad_base : (actx->res_spad_base + vtcm_thread_offset)) : NULL; \
|
||||
\
|
||||
const size_t spad_half = actx->vtcm_size_per_thread / 2; \
|
||||
uint32_t r_prefetch = r0; \
|
||||
int spad_idx = 0; \
|
||||
\
|
||||
for (int k = 0; k < 2 && r_prefetch < r1; k++) { \
|
||||
uint32_t cur_rows = MIN(block_rows, r1 - r_prefetch); \
|
||||
uint8_t * d_spad = dst_spad_base + spad_idx * spad_half; \
|
||||
for (uint32_t d = 0; d < n_dsts; d++) { \
|
||||
dma_addr_t d_ddr = octx->dsts[d]->data + r_prefetch * octx->dsts[d]->nb[1]; \
|
||||
dma_queue_push(dma_q, dma_make_data(d_ddr, d_spad), octx->dsts[d]->nb[1], row_size_aligned, row_bytes, 0); \
|
||||
} \
|
||||
for (uint32_t s = 0; s < n_ranks; s++) { \
|
||||
uint8_t * s_spad = actx->src_spad_base[s] + vtcm_thread_offset + spad_idx * spad_half; \
|
||||
const dma_addr_t s_ddr = octx->src[s]->data + r_prefetch * octx->src[s]->nb[1]; \
|
||||
dma_queue_push(dma_q, dma_make_data(s_spad, s_ddr), row_size_aligned, octx->src[s]->nb[1], row_bytes, cur_rows); \
|
||||
} \
|
||||
if (HAS_ADD && !IS_ROW_BCAST) { \
|
||||
uint8_t * r_spad = res_spad_base + spad_idx * spad_half; \
|
||||
const dma_addr_t r_ddr = octx->src[2 * n_ranks]->data + r_prefetch * octx->src[2 * n_ranks]->nb[1]; \
|
||||
dma_queue_push(dma_q, dma_make_data(r_spad, r_ddr), row_size_aligned, octx->src[2 * n_ranks]->nb[1], row_bytes, cur_rows); \
|
||||
} \
|
||||
r_prefetch += cur_rows; \
|
||||
spad_idx ^= 1; \
|
||||
} \
|
||||
\
|
||||
int comp_spad_idx = 0; \
|
||||
for (uint32_t r = r0; r < r1; ) { \
|
||||
uint32_t cur_rows = MIN(block_rows, r1 - r); \
|
||||
for (uint32_t d = 0; d < n_dsts; d++) { \
|
||||
dma_queue_pop(dma_q); \
|
||||
} \
|
||||
for (uint32_t s = 0; s < n_ranks; s++) { \
|
||||
dma_queue_pop(dma_q); \
|
||||
} \
|
||||
if (HAS_ADD && !IS_ROW_BCAST) { \
|
||||
dma_queue_pop(dma_q); \
|
||||
} \
|
||||
uint8_t * d_spad = dst_spad_base + comp_spad_idx * spad_half; \
|
||||
uint8_t * s0_spad = actx->src_spad_base[0] + vtcm_thread_offset + comp_spad_idx * spad_half; \
|
||||
uint8_t * s1_spad = actx->src_spad_base[1] + vtcm_thread_offset + comp_spad_idx * spad_half; \
|
||||
uint8_t * r_spad = (HAS_ADD && !IS_ROW_BCAST) ? (res_spad_base + comp_spad_idx * spad_half) : NULL; \
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r); \
|
||||
for (uint32_t row = 0; row < cur_rows; row++) { \
|
||||
uint8_t * d_row = d_spad + row * row_size_aligned; \
|
||||
const uint8_t * s0_row = s0_spad + row * row_size_aligned; \
|
||||
const uint8_t * s1_row = s1_spad + row * row_size_aligned; \
|
||||
HVX_ADD_FN(d_row, s0_row, s1_row, ne0); \
|
||||
for (uint32_t s = 2; s < n_ranks; s++) { \
|
||||
const uint8_t * ss_row = actx->src_spad_base[s] + vtcm_thread_offset + comp_spad_idx * spad_half + row * row_size_aligned; \
|
||||
HVX_ADD_FN(d_row, d_row, ss_row, ne0); \
|
||||
} \
|
||||
if (HAS_ADD) { \
|
||||
const uint8_t * res_row = IS_ROW_BCAST ? res_spad_base : (r_spad + row * row_size_aligned); \
|
||||
HVX_ADD_FN(d_row, d_row, res_row, ne0); \
|
||||
} \
|
||||
} \
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r); \
|
||||
for (uint32_t d = 0; d < n_dsts; d++) { \
|
||||
dma_addr_t d_ddr = octx->dsts[d]->data + r * octx->dsts[d]->nb[1]; \
|
||||
dma_queue_push(dma_q, dma_make_data(d_ddr, d_spad), octx->dsts[d]->nb[1], row_size_aligned, row_bytes, cur_rows); \
|
||||
} \
|
||||
if (r_prefetch < r1) { \
|
||||
uint32_t next_rows = MIN(block_rows, r1 - r_prefetch); \
|
||||
for (uint32_t s = 0; s < n_ranks; s++) { \
|
||||
uint8_t * s_spad = actx->src_spad_base[s] + vtcm_thread_offset + comp_spad_idx * spad_half; \
|
||||
const dma_addr_t s_next = octx->src[s]->data + r_prefetch * octx->src[s]->nb[1]; \
|
||||
dma_queue_push(dma_q, dma_make_data(s_spad, s_next), row_size_aligned, octx->src[s]->nb[1], row_bytes, next_rows); \
|
||||
} \
|
||||
if (HAS_ADD && !IS_ROW_BCAST) { \
|
||||
uint8_t * r_spad_next = res_spad_base + comp_spad_idx * spad_half; \
|
||||
const dma_addr_t r_next = octx->src[2 * n_ranks]->data + r_prefetch * octx->src[2 * n_ranks]->nb[1]; \
|
||||
dma_queue_push(dma_q, dma_make_data(r_spad_next, r_next), row_size_aligned, octx->src[2 * n_ranks]->nb[1], row_bytes, next_rows); \
|
||||
} \
|
||||
r_prefetch += next_rows; \
|
||||
} \
|
||||
comp_spad_idx ^= 1; \
|
||||
r += cur_rows; \
|
||||
} \
|
||||
dma_queue_flush(dma_q); \
|
||||
}
|
||||
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_2D(f16, __fp16, hvx_add_f16_aaa, 0, 0)
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_2D(f32, float, hvx_add_f32_aaa, 0, 0)
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_2D(add_f16, __fp16, hvx_add_f16_aaa, 1, 0)
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_2D(add_f32, float, hvx_add_f32_aaa, 1, 0)
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_2D(add_bcast_f16, __fp16, hvx_add_f16_aaa, 1, 1)
|
||||
DEFINE_ALLREDUCE_THREAD_DMA_2D(add_bcast_f32, float, hvx_add_f32_aaa, 1, 1)
|
||||
|
||||
static int validate_allreduce(
|
||||
struct htp_ops_context * octx,
|
||||
const struct htp_allreduce_kernel_params * kparams,
|
||||
uint32_t n_ranks
|
||||
) {
|
||||
if (kparams->n_threads == 0 || (uint32_t) kparams->n_threads > octx->ctx->n_threads) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
octx->n_threads = (uint32_t) kparams->n_threads;
|
||||
|
||||
if (kparams->vtcm_size_per_thread <= 0 || kparams->vtcm_size <= 0) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const bool has_add = (octx->op == HTP_OP_ALLREDUCE_ADD);
|
||||
const size_t n_vtcm_buffers = htp_allreduce_vtcm_buffer_count(
|
||||
n_ranks, octx->n_threads, has_add, kparams->is_row_bcast != 0);
|
||||
const size_t vtcm_size = n_vtcm_buffers * (size_t) kparams->vtcm_size_per_thread;
|
||||
if (vtcm_size != (size_t) kparams->vtcm_size) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
if (vtcm_size > octx->ctx->vtcm_size) {
|
||||
return HTP_STATUS_VTCM_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (octx->dst->type != HTP_TYPE_F16 && octx->dst->type != HTP_TYPE_F32) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
int op_allreduce(struct htp_ops_context * octx) {
|
||||
if (octx->ctx->mdev.count > 1 && octx->ctx->mdev.idx > 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
const struct htp_allreduce_kernel_params * kparams = (const struct htp_allreduce_kernel_params *) octx->kernel_params;
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
const uint32_t rank = (uint32_t) kparams->rank;
|
||||
const uint32_t n_ranks = (uint32_t) kparams->n_ranks;
|
||||
|
||||
if (n_ranks < 2 || n_ranks > HTP_ALLREDUCE_MAX_RANKS || rank >= n_ranks) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const uint32_t fence_seq_entry = (uint32_t) octx->op_params[0];
|
||||
const uint32_t fence_seq_exit = (uint32_t) octx->op_params[1];
|
||||
|
||||
const struct htp_tensor * my_sync = octx->src[n_ranks + rank];
|
||||
atomic_uint * my_fence = (atomic_uint *) (uintptr_t) my_sync->data;
|
||||
|
||||
const int status = validate_allreduce(octx, kparams, n_ranks);
|
||||
if (status != HTP_STATUS_OK) {
|
||||
if (status == HTP_STATUS_NO_SUPPORT) {
|
||||
FARF(ERROR, "ggml-hex: allreduce unsupported type %d : rank %u\n", dst->type, rank);
|
||||
}
|
||||
htp_fence_write(my_fence, fence_seq_exit, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
const bool has_add = (octx->op == HTP_OP_ALLREDUCE_ADD);
|
||||
const uint32_t nelem = dst->ne[0] * dst->ne[1] * dst->ne[2] * dst->ne[3];
|
||||
const int32_t mode = kparams->mode;
|
||||
|
||||
// 1. Entry Barrier: Synchronize all ranks before reading
|
||||
struct htp_thread_trace * tr0 = &octx->ctx->trace[0];
|
||||
htp_trace_event_start(tr0, HTP_TRACE_EVT_FENCE, (uint16_t) fence_seq_entry);
|
||||
|
||||
htp_fence_write(my_fence, fence_seq_entry, octx->status);
|
||||
|
||||
for (uint32_t j = 0; j < n_ranks; j++) {
|
||||
if (j == rank) continue;
|
||||
const struct htp_tensor * peer_sync = octx->src[n_ranks + j];
|
||||
atomic_uint * peer_fence = (atomic_uint *) (uintptr_t) peer_sync->data;
|
||||
uint64_t spins = 0;
|
||||
while (1) {
|
||||
uint32_t peer_seq;
|
||||
uint32_t peer_status;
|
||||
htp_fence_read(peer_fence, &peer_seq, &peer_status);
|
||||
if ((int32_t)(peer_seq - fence_seq_entry) >= 0) {
|
||||
if (peer_status > HTP_STATUS_OK) {
|
||||
FARF(ERROR, "ggml-hex: allreduce entry peer %u failed with status %u\n", j, peer_status);
|
||||
htp_fence_write(my_fence, fence_seq_exit, peer_status);
|
||||
htp_trace_event_stop(tr0, HTP_TRACE_EVT_FENCE, (uint16_t) fence_seq_entry);
|
||||
return peer_status;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (++spins > HTP_FENCE_TIMEOUT) {
|
||||
FARF(ERROR, "ggml-hex: allreduce entry fence-wait TIMEOUT : rank %u waiting on %u fence %p seq 0x%x peer-seq 0x%x\n",
|
||||
rank, j, peer_fence, fence_seq_entry, peer_seq);
|
||||
htp_fence_write(my_fence, fence_seq_exit, HTP_STATUS_INTERNAL_ERR);
|
||||
htp_trace_event_stop(tr0, HTP_TRACE_EVT_FENCE, (uint16_t) fence_seq_entry);
|
||||
return HTP_STATUS_INTERNAL_ERR;
|
||||
}
|
||||
hex_pause();
|
||||
}
|
||||
}
|
||||
asm volatile ("syncht" : : : "memory");
|
||||
|
||||
htp_trace_event_stop(tr0, HTP_TRACE_EVT_FENCE, (uint16_t) fence_seq_entry);
|
||||
|
||||
// 2. Multi-threaded Reduction across assigned rank chunk
|
||||
if (nelem > 0) {
|
||||
const uint32_t n_threads = (uint32_t) kparams->n_threads;
|
||||
const uint32_t block_elems = (uint32_t) kparams->block_elems;
|
||||
const uint32_t elems_per_thread = (uint32_t) kparams->elems_per_thread;
|
||||
const uint32_t vtcm_size_per_thread = (uint32_t) kparams->vtcm_size_per_thread;
|
||||
|
||||
struct htp_allreduce_context actx;
|
||||
actx.octx = octx;
|
||||
actx.n_ranks = n_ranks;
|
||||
actx.n_dsts = (uint32_t) kparams->n_dsts ? (uint32_t) kparams->n_dsts : n_ranks;
|
||||
actx.nelem = nelem;
|
||||
actx.ne0 = (uint32_t) kparams->ne0;
|
||||
actx.ne1 = (uint32_t) kparams->ne1;
|
||||
actx.row_size_aligned = (uint32_t) kparams->row_size_aligned;
|
||||
actx.rank_elem_start = (uint32_t) kparams->rank_elem_start;
|
||||
actx.rank_nelem = (uint32_t) kparams->rank_nelem;
|
||||
actx.elems_per_thread = elems_per_thread;
|
||||
actx.block_elems = block_elems;
|
||||
actx.vtcm_size_per_thread = vtcm_size_per_thread;
|
||||
actx.is_row_bcast = (kparams->is_row_bcast != 0);
|
||||
|
||||
work_queue_func_t reduce_fun = NULL;
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_ALLREDUCE_KERNEL_DMA_1D:
|
||||
if (has_add) {
|
||||
reduce_fun = (dst->type == HTP_TYPE_F16) ? allreduce_thread_dma_1d_add_f16 : allreduce_thread_dma_1d_add_f32;
|
||||
} else {
|
||||
reduce_fun = (dst->type == HTP_TYPE_F16) ? allreduce_thread_dma_1d_f16 : allreduce_thread_dma_1d_f32;
|
||||
}
|
||||
break;
|
||||
case HTP_ALLREDUCE_KERNEL_DMA_2D:
|
||||
if (has_add) {
|
||||
if (kparams->is_row_bcast) {
|
||||
reduce_fun = (dst->type == HTP_TYPE_F16) ? allreduce_thread_dma_2d_add_bcast_f16 : allreduce_thread_dma_2d_add_bcast_f32;
|
||||
} else {
|
||||
reduce_fun = (dst->type == HTP_TYPE_F16) ? allreduce_thread_dma_2d_add_f16 : allreduce_thread_dma_2d_add_f32;
|
||||
}
|
||||
} else {
|
||||
reduce_fun = (dst->type == HTP_TYPE_F16) ? allreduce_thread_dma_2d_f16 : allreduce_thread_dma_2d_f32;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
FARF(ERROR, "ggml-hex: allreduce unsupported kernel %d : rank %u\n", kparams->kernel_type, rank);
|
||||
htp_fence_write(my_fence, fence_seq_exit, HTP_STATUS_NO_SUPPORT);
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
uint8_t * vtcm_ptr = (uint8_t *) octx->ctx->vtcm_base;
|
||||
for (uint32_t s = 0; s < n_ranks; s++) {
|
||||
actx.src_spad_base[s] = vtcm_ptr;
|
||||
vtcm_ptr += n_threads * vtcm_size_per_thread;
|
||||
}
|
||||
actx.dst_spad_base = vtcm_ptr;
|
||||
vtcm_ptr += n_threads * vtcm_size_per_thread;
|
||||
if (has_add) {
|
||||
actx.res_spad_base = vtcm_ptr;
|
||||
vtcm_ptr += (actx.is_row_bcast ? 1 : n_threads) * vtcm_size_per_thread;
|
||||
}
|
||||
|
||||
if (has_add && actx.is_row_bcast) {
|
||||
const dma_addr_t r_ddr = octx->src[2 * n_ranks]->data;
|
||||
const uint32_t row_bytes = actx.ne0 * (dst->type == HTP_TYPE_F16 ? sizeof(__fp16) : sizeof(float));
|
||||
dma_queue * dma_q = octx->ctx->dma[0];
|
||||
dma_queue_push(dma_q, dma_make_data(actx.res_spad_base, r_ddr), actx.row_size_aligned, 0, row_bytes, 1);
|
||||
dma_queue_pop(dma_q);
|
||||
}
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, reduce_fun, &actx, n_threads);
|
||||
}
|
||||
|
||||
// 4. Exit Barrier: Synchronize all ranks after writing
|
||||
htp_trace_event_start(tr0, HTP_TRACE_EVT_FENCE, (uint16_t) fence_seq_exit);
|
||||
|
||||
// drain fan-out DMA writes before exit barrier
|
||||
if (mode == HTP_ALLREDUCE_SHARDED_FANOUT) {
|
||||
asm volatile ("syncht" : : : "memory");
|
||||
}
|
||||
|
||||
htp_fence_write(my_fence, fence_seq_exit, octx->status);
|
||||
|
||||
for (uint32_t j = 0; j < n_ranks; j++) {
|
||||
if (j == rank) continue;
|
||||
const struct htp_tensor * peer_sync = octx->src[n_ranks + j];
|
||||
atomic_uint * peer_fence = (atomic_uint *) (uintptr_t) peer_sync->data;
|
||||
uint64_t spins = 0;
|
||||
while (1) {
|
||||
uint32_t peer_seq;
|
||||
uint32_t peer_status;
|
||||
htp_fence_read(peer_fence, &peer_seq, &peer_status);
|
||||
if ((int32_t)(peer_seq - fence_seq_exit) >= 0) {
|
||||
if (peer_status > HTP_STATUS_OK) {
|
||||
FARF(ERROR, "ggml-hex: allreduce exit peer %u failed with status %u\n", j, peer_status);
|
||||
htp_fence_write(my_fence, fence_seq_exit, peer_status);
|
||||
htp_trace_event_stop(tr0, HTP_TRACE_EVT_FENCE, (uint16_t) fence_seq_exit);
|
||||
return peer_status;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (++spins > HTP_FENCE_TIMEOUT) {
|
||||
FARF(ERROR, "ggml-hex: allreduce exit fence-wait TIMEOUT : rank %u waiting on %u fence %p seq 0x%x peer-seq 0x%x\n",
|
||||
rank, j, peer_fence, fence_seq_exit, peer_seq);
|
||||
htp_fence_write(my_fence, fence_seq_exit, HTP_STATUS_INTERNAL_ERR);
|
||||
htp_trace_event_stop(tr0, HTP_TRACE_EVT_FENCE, (uint16_t) fence_seq_exit);
|
||||
return HTP_STATUS_INTERNAL_ERR;
|
||||
}
|
||||
hex_pause();
|
||||
}
|
||||
}
|
||||
asm volatile ("syncht" : : : "memory");
|
||||
|
||||
htp_trace_event_stop(tr0, HTP_TRACE_EVT_FENCE, (uint16_t) fence_seq_exit);
|
||||
|
||||
return octx->status;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef ALLREDUCE_OPS_H
|
||||
#define ALLREDUCE_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define HTP_ALLREDUCE_MAX_RANKS 4
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum htp_allreduce_kernel_type {
|
||||
HTP_ALLREDUCE_KERNEL_UNSUPPORTED = 0,
|
||||
HTP_ALLREDUCE_KERNEL_DMA_1D,
|
||||
HTP_ALLREDUCE_KERNEL_DMA_2D,
|
||||
};
|
||||
|
||||
enum htp_allreduce_mode {
|
||||
HTP_ALLREDUCE_FULL = 0, // all ranks reduce full tensor
|
||||
HTP_ALLREDUCE_SHARDED_FANOUT = 1, // each rank reduces 1/N shard and fans out to all buffers
|
||||
};
|
||||
|
||||
static inline size_t htp_allreduce_vtcm_buffer_count(
|
||||
uint32_t n_ranks,
|
||||
uint32_t n_threads,
|
||||
bool has_add,
|
||||
bool is_row_bcast
|
||||
) {
|
||||
return (size_t) (n_ranks + 1) * n_threads + (has_add ? (is_row_bcast ? 1 : n_threads) : 0);
|
||||
}
|
||||
|
||||
struct htp_allreduce_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
int32_t rank;
|
||||
int32_t n_ranks;
|
||||
int32_t n_threads;
|
||||
int32_t block_elems; // 1D: block_elems, 2D: block_rows
|
||||
int32_t elems_per_thread; // 1D: nelem_per_thread, 2D: nrows_per_thread
|
||||
int32_t vtcm_size_per_thread;
|
||||
int32_t vtcm_size;
|
||||
int32_t ne0;
|
||||
int32_t ne1;
|
||||
int32_t row_size_aligned;
|
||||
int32_t rank_elem_start;
|
||||
int32_t rank_nelem;
|
||||
int32_t n_dsts;
|
||||
int32_t is_row_bcast;
|
||||
int32_t mode; // enum htp_allreduce_mode: FULL or SHARDED_FANOUT
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_allreduce_kernel_params) <= 128, "htp_allreduce_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_allreduce_kernel_params) <= 128, "htp_allreduce_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ALLREDUCE_OPS_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,163 @@
|
||||
#ifndef HTP_ARGSORT_OPS_H
|
||||
#define HTP_ARGSORT_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
struct htp_sort_kernel_params {
|
||||
uint32_t n_threads;
|
||||
uint32_t order; // GGML_SORT_ORDER_ASC (0) or GGML_SORT_ORDER_DESC (1)
|
||||
uint32_t is_top_k; // 1 if TOP_K, 0 if ARGSORT
|
||||
uint32_t chunk_elems;
|
||||
uint32_t n_chunks;
|
||||
uint32_t vtcm_size;
|
||||
uint32_t merge_elems;
|
||||
uint32_t n_slots;
|
||||
struct fastdiv_values div_ne01;
|
||||
struct fastdiv_values div_ne01_ne02;
|
||||
};
|
||||
|
||||
struct htp_sort_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t phase1_slot_size;
|
||||
size_t off_phase1;
|
||||
size_t off_merge_values;
|
||||
size_t off_merge_indices;
|
||||
uint32_t chunk_elems;
|
||||
uint32_t n_chunks;
|
||||
uint32_t merge_elems;
|
||||
uint32_t n_threads;
|
||||
uint32_t n_slots;
|
||||
};
|
||||
|
||||
static inline void htp_sort_vtcm_layout_build(
|
||||
struct htp_sort_vtcm_layout * L,
|
||||
uint32_t total_rows,
|
||||
uint32_t chunk_elems,
|
||||
uint32_t n_chunks,
|
||||
uint32_t merge_elems,
|
||||
uint32_t n_threads,
|
||||
uint32_t n_slots) {
|
||||
|
||||
memset(L, 0, sizeof(*L));
|
||||
L->chunk_elems = chunk_elems;
|
||||
L->n_chunks = n_chunks;
|
||||
L->merge_elems = merge_elems;
|
||||
L->n_threads = n_threads;
|
||||
L->n_slots = n_slots;
|
||||
|
||||
const size_t values_size = ((chunk_elems * sizeof(float)) + 127) & ~127;
|
||||
const size_t indices_size = ((chunk_elems * sizeof(int32_t)) + 127) & ~127;
|
||||
L->phase1_slot_size = ((values_size + indices_size) + 255) & ~255;
|
||||
|
||||
size_t off = 0;
|
||||
if (total_rows > 1) {
|
||||
VTCM_LAYOUT_ALLOC(off, off_phase1, L->phase1_slot_size * n_slots * n_threads);
|
||||
} else {
|
||||
VTCM_LAYOUT_ALLOC(off, off_phase1, L->phase1_slot_size * n_chunks * n_slots);
|
||||
const size_t merge_values_size = ((merge_elems * sizeof(float)) + 127) & ~127;
|
||||
const size_t merge_indices_size = ((merge_elems * sizeof(int32_t)) + 127) & ~127;
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_merge_values, merge_values_size, n_chunks > 1);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_merge_indices, merge_indices_size, n_chunks > 1);
|
||||
}
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
static inline bool htp_sort_solve_layout(
|
||||
struct htp_sort_vtcm_layout * layout,
|
||||
uint32_t ne00,
|
||||
uint32_t total_rows,
|
||||
uint32_t k,
|
||||
uint32_t n_threads_max,
|
||||
size_t vtcm_budget,
|
||||
bool is_top_k) {
|
||||
|
||||
memset(layout, 0, sizeof(*layout));
|
||||
|
||||
if (total_rows > 1) {
|
||||
uint32_t n_threads = total_rows < n_threads_max ? total_rows : n_threads_max;
|
||||
|
||||
uint32_t n_vec = (ne00 + 31) / 32;
|
||||
uint32_t n_vec_pow2 = 1;
|
||||
while (n_vec_pow2 < n_vec) n_vec_pow2 <<= 1;
|
||||
uint32_t ne00_padded = n_vec_pow2 * 32;
|
||||
|
||||
size_t values_size = ((ne00_padded * sizeof(float)) + 127) & ~127;
|
||||
size_t indices_size = ((ne00_padded * sizeof(int32_t)) + 127) & ~127;
|
||||
size_t spad_per_slot = ((values_size + indices_size) + 255) & ~255;
|
||||
|
||||
uint32_t n_slots = 2;
|
||||
if (spad_per_slot * 2 > vtcm_budget) {
|
||||
n_slots = 1;
|
||||
}
|
||||
|
||||
size_t spad_per_thread = spad_per_slot * n_slots;
|
||||
|
||||
while (n_threads > 1 && (spad_per_thread * n_threads) > vtcm_budget) {
|
||||
n_threads--;
|
||||
}
|
||||
|
||||
htp_sort_vtcm_layout_build(layout, total_rows, ne00_padded, 1, 0, n_threads, n_slots);
|
||||
return layout->total_bytes <= vtcm_budget;
|
||||
}
|
||||
|
||||
uint32_t n_vec = (ne00 + 31) / 32;
|
||||
uint32_t n_vec_pow2 = 1;
|
||||
while (n_vec_pow2 < n_vec) n_vec_pow2 <<= 1;
|
||||
|
||||
uint32_t n_chunks = 1;
|
||||
if (ne00 > 1024) {
|
||||
while (n_chunks * 2 <= n_threads_max && n_chunks * 2 <= n_vec_pow2) {
|
||||
n_chunks *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t chunk_n_vec = n_vec_pow2 / n_chunks;
|
||||
uint32_t chunk_elems = chunk_n_vec * 32;
|
||||
uint32_t merge_elems = 0;
|
||||
|
||||
if (n_chunks > 1) {
|
||||
if (is_top_k) {
|
||||
uint32_t local_k = k < chunk_elems ? k : chunk_elems;
|
||||
uint32_t total_candidates = n_chunks * local_k;
|
||||
uint32_t merge_n_vec = (total_candidates + 31) / 32;
|
||||
uint32_t merge_n_vec_pow2 = 1;
|
||||
while (merge_n_vec_pow2 < merge_n_vec) merge_n_vec_pow2 <<= 1;
|
||||
merge_elems = merge_n_vec_pow2 * 32;
|
||||
} else {
|
||||
merge_elems = n_vec_pow2 * 32;
|
||||
}
|
||||
}
|
||||
|
||||
htp_sort_vtcm_layout_build(layout, 1, chunk_elems, n_chunks, merge_elems, n_chunks, 1);
|
||||
|
||||
if (layout->total_bytes > vtcm_budget && n_chunks > 1) {
|
||||
n_chunks = 1;
|
||||
chunk_elems = n_vec_pow2 * 32;
|
||||
merge_elems = 0;
|
||||
htp_sort_vtcm_layout_build(layout, 1, chunk_elems, n_chunks, merge_elems, 1, 1);
|
||||
}
|
||||
|
||||
if (layout->total_bytes > vtcm_budget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n_chunks == 1 && layout->phase1_slot_size * 2 <= vtcm_budget) {
|
||||
htp_sort_vtcm_layout_build(layout, 1, chunk_elems, 1, 0, 1, 2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_sort_kernel_params) <= 128, "htp_sort_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_sort_kernel_params) <= 128, "htp_sort_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_ARGSORT_OPS_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,247 @@
|
||||
#ifndef HTP_BINARY_OPS_H
|
||||
#define HTP_BINARY_OPS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
enum htp_binary_kernel_type {
|
||||
HTP_BINARY_KERNEL_SAME_SHAPE = 0,
|
||||
HTP_BINARY_KERNEL_ROW_BCAST,
|
||||
HTP_BINARY_KERNEL_SCALAR,
|
||||
HTP_BINARY_KERNEL_ADD_ID,
|
||||
HTP_BINARY_KERNEL_COMPLEX,
|
||||
HTP_BINARY_KERNEL_REPEAT,
|
||||
HTP_BINARY_KERNEL_CHUNKED,
|
||||
};
|
||||
|
||||
struct htp_binary_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t split_at_ne01;
|
||||
uint8_t split_at_ne02;
|
||||
uint8_t src1_strided;
|
||||
|
||||
uint8_t n_srcs;
|
||||
uint8_t is_scalar;
|
||||
uint8_t pad[2];
|
||||
|
||||
uint32_t n_threads;
|
||||
uint32_t rows_per_buffer;
|
||||
|
||||
uint32_t src0_row_size_aligned;
|
||||
uint32_t src1_row_size_aligned;
|
||||
uint32_t dst_row_size_aligned;
|
||||
|
||||
uint32_t src1_size;
|
||||
uint32_t src2_size;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
uint32_t chunk_size;
|
||||
uint32_t chunk_bytes;
|
||||
|
||||
struct fastdiv_values div_ne01;
|
||||
struct fastdiv_values div_ne02;
|
||||
struct fastdiv_values div_ne012;
|
||||
|
||||
struct fastdiv_values div_ne11;
|
||||
struct fastdiv_values div_ne12;
|
||||
struct fastdiv_values div_ne13;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_binary_kernel_params) <= 128, "htp_binary_kernel_params is too large for kernel_params blob");
|
||||
|
||||
struct ggml_tensor;
|
||||
static inline bool htp_binary_strides_safe(
|
||||
const struct ggml_tensor * src0,
|
||||
const struct ggml_tensor * src1,
|
||||
const struct ggml_tensor * dst,
|
||||
size_t elem_size
|
||||
) {
|
||||
if ((src0->ne[0] != 1 && src0->nb[0] != elem_size) ||
|
||||
(src1->ne[0] != 1 && src1->nb[0] != elem_size) ||
|
||||
(dst->ne[0] != 1 && dst->nb[0] != elem_size)) {
|
||||
return false;
|
||||
}
|
||||
if ((src0->ne[1] > 1 && src0->nb[1] < src0->ne[0] * elem_size) ||
|
||||
(src1->ne[1] > 1 && src1->nb[1] < src1->ne[0] * elem_size) ||
|
||||
(dst->ne[1] > 1 && dst->nb[1] < dst->ne[0] * elem_size)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_binary_kernel_params) <= 128, "htp_binary_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
struct htp_binary_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src0;
|
||||
size_t off_src1;
|
||||
size_t off_src2;
|
||||
size_t off_dst;
|
||||
|
||||
size_t src0_bytes_per_thread;
|
||||
size_t src1_bytes_per_thread;
|
||||
size_t dst_bytes_per_thread;
|
||||
|
||||
size_t src0_spad_half_size;
|
||||
size_t src1_spad_half_size;
|
||||
size_t dst_spad_half_size;
|
||||
|
||||
size_t src1_size;
|
||||
size_t src2_size;
|
||||
uint32_t rows_per_buffer;
|
||||
};
|
||||
|
||||
static inline void htp_binary_vtcm_layout_build(
|
||||
struct htp_binary_vtcm_layout * L,
|
||||
const struct htp_binary_kernel_params * kparams,
|
||||
size_t vtcm_size
|
||||
) {
|
||||
memset(L, 0, sizeof(*L));
|
||||
|
||||
const uint32_t n_threads = kparams->n_threads;
|
||||
if (n_threads == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t n_srcs = kparams->n_srcs > 2 ? kparams->n_srcs : 2;
|
||||
|
||||
if (kparams->kernel_type == HTP_BINARY_KERNEL_CHUNKED) {
|
||||
const size_t chunk_bytes = kparams->chunk_bytes;
|
||||
if (chunk_bytes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
L->src0_bytes_per_thread = 2 * chunk_bytes;
|
||||
L->src1_bytes_per_thread = kparams->is_scalar ? 0 : (2 * (n_srcs - 1) * chunk_bytes);
|
||||
L->dst_bytes_per_thread = 2 * chunk_bytes;
|
||||
|
||||
L->src0_spad_half_size = chunk_bytes;
|
||||
L->src1_spad_half_size = kparams->is_scalar ? 0 : chunk_bytes;
|
||||
L->dst_spad_half_size = chunk_bytes;
|
||||
|
||||
L->rows_per_buffer = 1;
|
||||
L->src1_size = 0;
|
||||
|
||||
const size_t src0_total = n_threads * L->src0_bytes_per_thread;
|
||||
const size_t src1_total = kparams->is_scalar ? 128 : (n_threads * L->src1_bytes_per_thread);
|
||||
const size_t dst_total = n_threads * L->dst_bytes_per_thread;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src0, src0_total);
|
||||
VTCM_LAYOUT_ALLOC(off, off_src1, src1_total);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, dst_total);
|
||||
|
||||
if (off > vtcm_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
L->total_bytes = off;
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t spad_row_total = (kparams->kernel_type == HTP_BINARY_KERNEL_SAME_SHAPE)
|
||||
? 2 * (kparams->src0_row_size_aligned + (n_srcs - 1) * kparams->src1_row_size_aligned + kparams->dst_row_size_aligned)
|
||||
: 2 * (kparams->src0_row_size_aligned + kparams->dst_row_size_aligned);
|
||||
|
||||
const size_t static_vtcm_size = kparams->src1_size + kparams->src2_size;
|
||||
if (spad_row_total == 0 || vtcm_size < static_vtcm_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t rows_per_buffer = (kparams->rows_per_buffer > 0)
|
||||
? kparams->rows_per_buffer
|
||||
: (vtcm_size - static_vtcm_size) / (n_threads * spad_row_total);
|
||||
if (rows_per_buffer == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
L->rows_per_buffer = (uint32_t) rows_per_buffer;
|
||||
L->src1_size = kparams->src1_size;
|
||||
L->src2_size = kparams->src2_size;
|
||||
|
||||
L->src0_bytes_per_thread = rows_per_buffer * 2 * kparams->src0_row_size_aligned;
|
||||
L->dst_bytes_per_thread = rows_per_buffer * 2 * kparams->dst_row_size_aligned;
|
||||
L->src1_bytes_per_thread = (kparams->kernel_type == HTP_BINARY_KERNEL_SAME_SHAPE)
|
||||
? rows_per_buffer * 2 * (n_srcs - 1) * kparams->src1_row_size_aligned
|
||||
: 0;
|
||||
|
||||
L->src0_spad_half_size = L->src0_bytes_per_thread / 2;
|
||||
L->src1_spad_half_size = (kparams->kernel_type == HTP_BINARY_KERNEL_SAME_SHAPE)
|
||||
? rows_per_buffer * kparams->src1_row_size_aligned
|
||||
: 0;
|
||||
L->dst_spad_half_size = L->dst_bytes_per_thread / 2;
|
||||
|
||||
const size_t src0_total = n_threads * L->src0_bytes_per_thread;
|
||||
const size_t src1_total = (kparams->src1_size > 0)
|
||||
? kparams->src1_size
|
||||
: n_threads * L->src1_bytes_per_thread;
|
||||
const size_t src2_total = kparams->src2_size;
|
||||
const size_t dst_total = n_threads * L->dst_bytes_per_thread;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src0, src0_total);
|
||||
VTCM_LAYOUT_ALLOC(off, off_src1, src1_total);
|
||||
VTCM_LAYOUT_ALLOC(off, off_src2, src2_total);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, dst_total);
|
||||
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
static inline bool htp_binary_solve_chunked(
|
||||
struct htp_binary_vtcm_layout * L,
|
||||
struct htp_binary_kernel_params * kparams,
|
||||
uint32_t total_elems,
|
||||
uint32_t elem_size,
|
||||
uint32_t max_threads,
|
||||
uint32_t n_srcs,
|
||||
bool is_scalar,
|
||||
size_t vtcm_size
|
||||
) {
|
||||
const size_t n_bufs = is_scalar ? 2 : (size_t) n_srcs + 1;
|
||||
const size_t scalar_bytes = is_scalar ? 128 : 0;
|
||||
const size_t budget_per_buf = (max_threads > 0 && vtcm_size > scalar_bytes)
|
||||
? hex_align_down((vtcm_size - scalar_bytes) / (2 * max_threads * n_bufs), 128)
|
||||
: 0;
|
||||
const uint32_t max_chunk_bytes = (budget_per_buf > 0 && budget_per_buf < 32768) ? (uint32_t) budget_per_buf : 32768u;
|
||||
const uint32_t max_chunk_elems = max_chunk_bytes / elem_size;
|
||||
if (max_chunk_elems == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t min_chunk_elems = 256 < max_chunk_elems ? 256 : max_chunk_elems;
|
||||
const uint32_t target_chunk_elems = hex_round_up((total_elems + (2 * max_threads) - 1) / (2 * max_threads), 32);
|
||||
const uint32_t chunk_size = (target_chunk_elems < min_chunk_elems) ? min_chunk_elems
|
||||
: (target_chunk_elems > max_chunk_elems) ? max_chunk_elems
|
||||
: target_chunk_elems;
|
||||
const uint32_t chunk_bytes = hex_round_up(chunk_size * elem_size, 128);
|
||||
const uint32_t total_chunks = (total_elems + chunk_size - 1) / chunk_size;
|
||||
const uint32_t n_threads = (total_chunks >= 2) ? (max_threads < total_chunks ? max_threads : total_chunks) : 1;
|
||||
|
||||
kparams->kernel_type = HTP_BINARY_KERNEL_CHUNKED;
|
||||
kparams->n_threads = n_threads;
|
||||
kparams->n_srcs = (uint8_t) n_srcs;
|
||||
kparams->rows_per_buffer = 1;
|
||||
kparams->src1_row_size_aligned = is_scalar ? 0 : kparams->src0_row_size_aligned;
|
||||
kparams->src1_size = 0;
|
||||
kparams->chunk_size = chunk_size;
|
||||
kparams->chunk_bytes = chunk_bytes;
|
||||
kparams->is_scalar = is_scalar ? 1 : 0;
|
||||
|
||||
htp_binary_vtcm_layout_build(L, kparams, vtcm_size);
|
||||
if (L->total_bytes == 0 || L->total_bytes > vtcm_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
kparams->vtcm_size = (uint32_t) L->total_bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,155 @@
|
||||
if (HEXAGON_TOOLCHAIN_INCLUDED)
|
||||
return()
|
||||
endif()
|
||||
set(HEXAGON_TOOLCHAIN_INCLUDED true)
|
||||
|
||||
# Cross Compiling for Hexagon
|
||||
set(HEXAGON TRUE)
|
||||
set(CMAKE_SYSTEM_NAME QURT)
|
||||
set(CMAKE_SYSTEM_PROCESSOR Hexagon)
|
||||
set(CMAKE_SYSTEM_VERSION "1") #${HEXAGON_PLATFORM_LEVEL})
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
set(CUSTOM_RUNELF_PATH "")
|
||||
|
||||
if (NOT HEXAGON_SDK_ROOT)
|
||||
set(HEXAGON_SDK_ROOT $ENV{HEXAGON_SDK_ROOT})
|
||||
endif()
|
||||
|
||||
if (NOT HEXAGON_TOOLS_ROOT)
|
||||
if (DEFINED ENV{HEXAGON_TOOLS_ROOT})
|
||||
set(HEXAGON_TOOLS_ROOT $ENV{HEXAGON_TOOLS_ROOT})
|
||||
endif()
|
||||
if(NOT HEXAGON_TOOLS_ROOT)
|
||||
set(HEXAGON_TOOLS_ROOT $ENV{DEFAULT_HEXAGON_TOOLS_ROOT})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(TO_CMAKE_PATH "${HEXAGON_TOOLS_ROOT}" HEXAGON_TOOLS_ROOT)
|
||||
file(TO_CMAKE_PATH "${HEXAGON_SDK_ROOT}" HEXAGON_SDK_ROOT)
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)
|
||||
set(HEXAGON_TOOLCHAIN_SUFFIX .exe)
|
||||
endif()
|
||||
message(DEBUG "CMAKE_HOST_SYSTEM_NAME:${CMAKE_HOST_SYSTEM_NAME}")
|
||||
|
||||
include(${HEXAGON_SDK_ROOT}/build/cmake/hexagon_arch.cmake)
|
||||
|
||||
set(HEXAGON_TOOLCHAIN ${HEXAGON_TOOLS_ROOT})
|
||||
set(HEXAGON_LIB_DIR "${HEXAGON_TOOLCHAIN}/Tools/target/hexagon/lib")
|
||||
set(HEXAGON_ISS_DIR ${HEXAGON_TOOLCHAIN}/Tools/lib/iss)
|
||||
|
||||
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
||||
HEXAGON_SDK_ROOT
|
||||
HEXAGON_TOOLS_ROOT
|
||||
)
|
||||
|
||||
# QURT Related includes and linker flags
|
||||
set(V_ARCH ${HEXAGON_ARCH})
|
||||
set(_QURT_INSTALL_DIR "${HEXAGON_SDK_ROOT}/rtos/qurt/ADSP${V_ARCH}MP${V_ARCH_EXTN}")
|
||||
set(_QURT_INSTALL_DIR "${HEXAGON_SDK_ROOT}/rtos/qurt/compute${V_ARCH}${V_ARCH_EXTN}")
|
||||
|
||||
if (${TREE} MATCHES PAKMAN)
|
||||
set(_QURT_INSTALL_DIR "${QURT_IMAGE_DIR}/compute${V_ARCH}${V_ARCH_EXTN}")
|
||||
endif()
|
||||
message(DEBUG "_QURT_INSTALL_DIR:${_QURT_INSTALL_DIR}")
|
||||
set(RTOS_DIR ${_QURT_INSTALL_DIR})
|
||||
set(QCC_DIR "${HEXAGON_QCC_DIR}/${V_ARCH}/G0")
|
||||
set(TARGET_DIR "${HEXAGON_LIB_DIR}/${V_ARCH}/G0")
|
||||
|
||||
include_directories(
|
||||
${_QURT_INSTALL_DIR}/include
|
||||
${_QURT_INSTALL_DIR}/include/qurt
|
||||
${_QURT_INSTALL_DIR}/include/posix
|
||||
)
|
||||
|
||||
set(QURT_START_LINK_LIBS)
|
||||
set(QURT_START_LINK_LIBS
|
||||
"${TARGET_DIR}/init.o"
|
||||
"${RTOS_DIR}/lib/crt1.o"
|
||||
"${RTOS_DIR}/lib/debugmon.o"
|
||||
"${RTOS_DIR}/lib/libqurt.a"
|
||||
"${TARGET_DIR}/libc.a"
|
||||
"${TARGET_DIR}/libqcc.a"
|
||||
"${TARGET_DIR}/libhexagon.a"
|
||||
"${RTOS_DIR}/lib/libqurtcfs.a"
|
||||
"${RTOS_DIR}/lib/libtimer_island.a"
|
||||
"${RTOS_DIR}/lib/libtimer_main.a"
|
||||
"${RTOS_DIR}/lib/libposix.a"
|
||||
)
|
||||
STRING(REPLACE ";" " " QURT_START_LINK_LIBS "${QURT_START_LINK_LIBS}")
|
||||
|
||||
set(QURT_END_LINK_LIBS ${TARGET_DIR}/fini.o)
|
||||
|
||||
# Non QURT related includes and linker flags
|
||||
|
||||
set(TARGET_DIR_NOOS "${HEXAGON_TOOLCHAIN}/Tools/target/hexagon/lib/${HEXAGON_ARCH}")
|
||||
|
||||
if (NOT NO_WRAP_MEM_API)
|
||||
set(WRAP_MALLOC -Wl,--wrap=malloc)
|
||||
set(WRAP_CALLOC -Wl,--wrap=calloc)
|
||||
set(WRAP_FREE -Wl,--wrap=free)
|
||||
set(WRAP_REALLOC -Wl,--wrap=realloc)
|
||||
set(WRAP_MEMALIGN -Wl,--wrap=memalign)
|
||||
endif()
|
||||
|
||||
set(ARCH_FLAGS "-mcpu=${V_ARCH} -m${V_ARCH} -mhvx=${V_ARCH} -mhmx")
|
||||
|
||||
set(PIC_SHARED_LD_FLAGS
|
||||
${ARCH_FLAGS}
|
||||
-G0
|
||||
-fpic
|
||||
-Wl,-Bsymbolic
|
||||
-Wl,-L${TARGET_DIR_NOOS}/G0/pic
|
||||
-Wl,-L${HEXAGON_TOOLCHAIN}/Tools/target/hexagon/lib/
|
||||
-Wl,--no-threads ${WRAP_MALLOC} ${WRAP_CALLOC} ${WRAP_FREE} ${WRAP_REALLOC} ${WRAP_MEMALIGN}
|
||||
-shared
|
||||
"-o <TARGET> <SONAME_FLAG><TARGET_SONAME>"
|
||||
"<LINK_FLAGS>"
|
||||
-Wl,--start-group
|
||||
"<OBJECTS>"
|
||||
"<LINK_LIBRARIES>"
|
||||
-Wl,--end-group
|
||||
-lc
|
||||
)
|
||||
STRING(REPLACE ";" " " PIC_SHARED_LD_FLAGS "${PIC_SHARED_LD_FLAGS}")
|
||||
|
||||
set(HEXAGON_PIC_SHARED_LINK_OPTIONS "${PIC_SHARED_LD_FLAGS}")
|
||||
|
||||
# System include paths
|
||||
include_directories(SYSTEM ${HEXAGON_SDK_ROOT}/incs)
|
||||
include_directories(SYSTEM ${HEXAGON_SDK_ROOT}/incs/stddef)
|
||||
include_directories(SYSTEM ${HEXAGON_SDK_ROOT}/ipc/fastrpc/incs)
|
||||
|
||||
# LLVM toolchain setup
|
||||
# Compiler paths, options and architecture
|
||||
set(CMAKE_C_COMPILER ${HEXAGON_TOOLCHAIN}/Tools/bin/hexagon-clang${HEXAGON_TOOLCHAIN_SUFFIX})
|
||||
set(CMAKE_CXX_COMPILER ${HEXAGON_TOOLCHAIN}/Tools/bin/hexagon-clang++${HEXAGON_TOOLCHAIN_SUFFIX})
|
||||
set(CMAKE_AR ${HEXAGON_TOOLCHAIN}/Tools/bin/hexagon-ar${HEXAGON_TOOLCHAIN_SUFFIX})
|
||||
set(CMAKE_ASM_COMPILER ${HEXAGON_TOOLCHAIN}/Tools/bin/hexagon-clang++${HEXAGON_TOOLCHAIN_SUFFIX})
|
||||
set(HEXAGON_LINKER ${CMAKE_C_COMPILER})
|
||||
set(CMAKE_PREFIX_PATH ${HEXAGON_TOOLCHAIN}/Tools/target/hexagon)
|
||||
|
||||
set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,")
|
||||
set(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG "-Wl,-soname,")
|
||||
|
||||
# Compiler Options
|
||||
set(COMMON_FLAGS "${ARCH_FLAGS} -fvectorize -flto -Wall -Werror -fno-zero-initialized-in-bss -G0 -fdata-sections -fpic ${XQF_ARGS}")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${COMMON_FLAGS} -O0 -D_DEBUG -g")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${COMMON_FLAGS} -O2 -g")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${COMMON_FLAGS} -O2")
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${COMMON_FLAGS} -O0 -D_DEBUG -g")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${COMMON_FLAGS} -O2 -g")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${COMMON_FLAGS} -O2")
|
||||
|
||||
set(CMAKE_ASM_FLAGS_DEBUG "${COMMON_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
set(CMAKE_ASM_FLAGS_RELEASE "${COMMON_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
set(CMAKE_ASM_FLAGS_RELWITHDEBINFO "${COMMON_FLAGS} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" )
|
||||
|
||||
#Linker Options
|
||||
set(CMAKE_C_CREATE_SHARED_LIBRARY "${HEXAGON_LINKER} ${HEXAGON_PIC_SHARED_LINK_OPTIONS}")
|
||||
set(CMAKE_CXX_CREATE_SHARED_LIBRARY "${HEXAGON_LINKER} ${HEXAGON_PIC_SHARED_LINK_OPTIONS}")
|
||||
@@ -0,0 +1,349 @@
|
||||
#include "concat-ops.h"
|
||||
#include "dma-queue.h"
|
||||
#include "hex-common.h"
|
||||
#include "dma-copy.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "hex-profile.h"
|
||||
#include "hexagon_protos.h"
|
||||
#include "hexagon_types.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-fence.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "hvx_hexagon_protos.h"
|
||||
#include "gather-rows.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct htp_concat_context {
|
||||
struct htp_ops_context * octx;
|
||||
uint8_t * spad0_base;
|
||||
uint8_t * spad1_base;
|
||||
uint32_t spad0_size_per_thread;
|
||||
uint32_t spad1_size_per_thread;
|
||||
uint32_t row_start;
|
||||
uint32_t nrows;
|
||||
uint32_t nrows_per_thread;
|
||||
uint32_t nplanes;
|
||||
struct fastdiv_values div_ne2;
|
||||
};
|
||||
|
||||
static inline dma_addr_t concat_plane_addr(const struct htp_tensor * t, uint32_t p, const struct fastdiv_values * div_ne2, uint32_t ne2) {
|
||||
const uint32_t i3 = fastdiv(p, div_ne2);
|
||||
const uint32_t i2 = p - i3 * ne2;
|
||||
return t->data + i2 * t->nb[2] + i3 * t->nb[3];
|
||||
}
|
||||
|
||||
static void concat_2d_f32_transposed(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct htp_concat_context * cctx = (struct htp_concat_context *) data;
|
||||
struct htp_ops_context * octx = cctx->octx;
|
||||
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * src1 = octx->src[1];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
const uint32_t src0_ne0 = src0->ne[0];
|
||||
const uint32_t src1_ne0 = src1->ne[0];
|
||||
|
||||
const uint32_t row_end = cctx->row_start + cctx->nrows;
|
||||
const uint32_t start_i = cctx->row_start + ith * cctx->nrows_per_thread;
|
||||
const uint32_t end_i = (start_i + cctx->nrows_per_thread < row_end) ? (start_i + cctx->nrows_per_thread) : row_end;
|
||||
if (start_i >= end_i) return;
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
|
||||
uint8_t * spad0_base = cctx->spad0_base + ith * cctx->spad0_size_per_thread;
|
||||
uint8_t * spad1_base = cctx->spad1_base + ith * cctx->spad1_size_per_thread;
|
||||
|
||||
const uint32_t block_i = 32;
|
||||
const uint32_t spad1_stride = block_i * sizeof(float);
|
||||
|
||||
const HVX_Vector offsets = hvx_vec_gather_offsets_w(spad1_stride);
|
||||
const uint32_t src1_ne0_padded = hex_round_up(src1_ne0, 32);
|
||||
const uint32_t src0_row_bytes = src0_ne0 * sizeof(float);
|
||||
const uint32_t src0_row_padded = hex_round_up(src0_row_bytes, VLEN);
|
||||
const uint32_t src0_pre = src0_row_padded - src0_row_bytes;
|
||||
const uint32_t spad0_row_bytes = src0_row_padded + src1_ne0_padded * sizeof(float);
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
const struct fastdiv_values * div_ne2 = &cctx->div_ne2;
|
||||
const uint32_t ne2 = dst->ne[2];
|
||||
|
||||
uint32_t p = 0;
|
||||
uint32_t i = start_i;
|
||||
|
||||
const dma_addr_t src1_addr = concat_plane_addr(src1, p, div_ne2, ne2) + i * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, src1_addr), spad1_stride, src1->nb[0], MIN(end_i - i, block_i) * sizeof(float), src1_ne0);
|
||||
|
||||
const dma_addr_t src0_addr = concat_plane_addr(src0, p, div_ne2, ne2) + i * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, src0_addr), spad0_row_bytes, src0->nb[1], src0_row_bytes, MIN(end_i - i, block_i));
|
||||
|
||||
dma_queue_pop(dma_q); // src1
|
||||
|
||||
while (p < cctx->nplanes) {
|
||||
const uint32_t current_block_i = MIN(end_i - i, block_i);
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
for (uint32_t j = 0; j < src1_ne0; j += 32) {
|
||||
const uint8_t * src_ptr = spad1_base + j * spad1_stride;
|
||||
uint8_t * dst_ptr = spad0_base + src0_row_padded + j * sizeof(float);
|
||||
hvx_transpose_32x32_w_gather(dst_ptr, spad0_row_bytes, src_ptr, spad1_stride, offsets, current_block_i, MIN(src1_ne0 - j, 32));
|
||||
}
|
||||
hvx_gather_sync(spad0_base + src0_row_padded);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
|
||||
uint32_t np = p;
|
||||
uint32_t ni = i + block_i;
|
||||
if (ni >= end_i) {
|
||||
ni = start_i;
|
||||
np++;
|
||||
}
|
||||
const bool has_next = np < cctx->nplanes;
|
||||
const uint32_t next_block_i = MIN(end_i - ni, block_i);
|
||||
|
||||
// spad1 is free after the gather sync, prefetch next src1 ahead of the dst write
|
||||
if (has_next) {
|
||||
const dma_addr_t nsrc1_addr = concat_plane_addr(src1, np, div_ne2, ne2) + ni * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, nsrc1_addr), spad1_stride, src1->nb[0], next_block_i * sizeof(float), src1_ne0);
|
||||
}
|
||||
|
||||
dma_queue_pop(dma_q); // src0
|
||||
|
||||
const dma_addr_t dst_addr = concat_plane_addr(dst, p, div_ne2, ne2) + i * dst->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(dst_addr, spad0_base + src0_pre), dst->nb[1], spad0_row_bytes, (src0_ne0 + src1_ne0) * sizeof(float), current_block_i);
|
||||
|
||||
if (has_next) {
|
||||
dma_queue_pop(dma_q); // next src1
|
||||
}
|
||||
dma_queue_pop(dma_q); // dst
|
||||
|
||||
// spad0 is free after the dst write
|
||||
if (has_next) {
|
||||
const dma_addr_t nsrc0_addr = concat_plane_addr(src0, np, div_ne2, ne2) + ni * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, nsrc0_addr), spad0_row_bytes, src0->nb[1], src0_row_bytes, next_block_i);
|
||||
}
|
||||
|
||||
p = np;
|
||||
i = ni;
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
static void concat_2d_f16_transposed(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct htp_concat_context * cctx = (struct htp_concat_context *) data;
|
||||
struct htp_ops_context * octx = cctx->octx;
|
||||
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * src1 = octx->src[1];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
const uint32_t src0_ne0 = src0->ne[0];
|
||||
const uint32_t src1_ne0 = src1->ne[0];
|
||||
|
||||
const uint32_t row_end = cctx->row_start + cctx->nrows;
|
||||
const uint32_t start_i = cctx->row_start + ith * cctx->nrows_per_thread;
|
||||
const uint32_t end_i = (start_i + cctx->nrows_per_thread < row_end) ? (start_i + cctx->nrows_per_thread) : row_end;
|
||||
if (start_i >= end_i) return;
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
|
||||
uint8_t * spad0_base = cctx->spad0_base + ith * cctx->spad0_size_per_thread;
|
||||
uint8_t * spad1_base = cctx->spad1_base + ith * cctx->spad1_size_per_thread;
|
||||
|
||||
const uint32_t block_i = 64;
|
||||
const uint32_t spad1_stride = block_i * sizeof(__fp16);
|
||||
|
||||
const HVX_Vector offsets = hvx_vec_gather_offsets_h(spad1_stride);
|
||||
const uint32_t src1_ne0_padded = hex_round_up(src1_ne0, 64);
|
||||
const uint32_t src0_row_bytes = src0_ne0 * sizeof(__fp16);
|
||||
const uint32_t src0_row_padded = hex_round_up(src0_row_bytes, VLEN);
|
||||
const uint32_t src0_pre = src0_row_padded - src0_row_bytes;
|
||||
const uint32_t spad0_row_bytes = src0_row_padded + src1_ne0_padded * sizeof(__fp16);
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
const struct fastdiv_values * div_ne2 = &cctx->div_ne2;
|
||||
const uint32_t ne2 = dst->ne[2];
|
||||
|
||||
uint32_t p = 0;
|
||||
uint32_t i = start_i;
|
||||
|
||||
const dma_addr_t src1_addr = concat_plane_addr(src1, p, div_ne2, ne2) + i * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, src1_addr), spad1_stride, src1->nb[0], MIN(end_i - i, block_i) * sizeof(__fp16), src1_ne0);
|
||||
|
||||
const dma_addr_t src0_addr = concat_plane_addr(src0, p, div_ne2, ne2) + i * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, src0_addr), spad0_row_bytes, src0->nb[1], src0_row_bytes, MIN(end_i - i, block_i));
|
||||
|
||||
dma_queue_pop(dma_q); // src1
|
||||
|
||||
while (p < cctx->nplanes) {
|
||||
const uint32_t current_block_i = MIN(end_i - i, block_i);
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
for (uint32_t j = 0; j < src1_ne0; j += 64) {
|
||||
const uint8_t * src_ptr = spad1_base + j * spad1_stride;
|
||||
uint8_t * dst_ptr = spad0_base + src0_row_padded + j * sizeof(__fp16);
|
||||
hvx_transpose_64x64_h_gather(dst_ptr, spad0_row_bytes, src_ptr, spad1_stride, offsets, current_block_i, MIN(src1_ne0 - j, 64));
|
||||
}
|
||||
hvx_gather_sync(spad0_base + src0_row_padded);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
|
||||
uint32_t np = p;
|
||||
uint32_t ni = i + block_i;
|
||||
if (ni >= end_i) {
|
||||
ni = start_i;
|
||||
np++;
|
||||
}
|
||||
const bool has_next = np < cctx->nplanes;
|
||||
const uint32_t next_block_i = MIN(end_i - ni, block_i);
|
||||
|
||||
// spad1 is free after the gather sync, prefetch next src1 ahead of the dst write
|
||||
if (has_next) {
|
||||
const dma_addr_t nsrc1_addr = concat_plane_addr(src1, np, div_ne2, ne2) + ni * src1->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad1_base, nsrc1_addr), spad1_stride, src1->nb[0], next_block_i * sizeof(__fp16), src1_ne0);
|
||||
}
|
||||
|
||||
dma_queue_pop(dma_q); // src0
|
||||
|
||||
const dma_addr_t dst_addr = concat_plane_addr(dst, p, div_ne2, ne2) + i * dst->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(dst_addr, spad0_base + src0_pre), dst->nb[1], spad0_row_bytes, (src0_ne0 + src1_ne0) * sizeof(__fp16), current_block_i);
|
||||
|
||||
if (has_next) {
|
||||
dma_queue_pop(dma_q); // next src1
|
||||
}
|
||||
dma_queue_pop(dma_q); // dst
|
||||
|
||||
// spad0 is free after the dst write
|
||||
if (has_next) {
|
||||
const dma_addr_t nsrc0_addr = concat_plane_addr(src0, np, div_ne2, ne2) + ni * src0->nb[1];
|
||||
dma_queue_push(dma_q, dma_make_data(spad0_base + src0_pre, nsrc0_addr), spad0_row_bytes, src0->nb[1], src0_row_bytes, next_block_i);
|
||||
}
|
||||
|
||||
p = np;
|
||||
i = ni;
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
static int concat_regular(struct htp_ops_context * octx, int dim, uint32_t type_size) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * src1 = octx->src[1];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
struct htp_tensor view0 = *dst;
|
||||
struct htp_tensor view1 = *dst;
|
||||
for (int d = 0; d < HTP_OP_MAX_DIMS; d++) {
|
||||
view0.ne[d] = src0->ne[d];
|
||||
view1.ne[d] = src1->ne[d];
|
||||
}
|
||||
view1.data += src0->ne[dim] * dst->nb[dim];
|
||||
|
||||
const uint32_t total_rows_0 = src0->ne[1] * src0->ne[2] * src0->ne[3];
|
||||
const uint32_t total_rows_1 = src1->ne[1] * src1->ne[2] * src1->ne[3];
|
||||
|
||||
uint32_t rstart0 = 0, nrows0 = total_rows_0;
|
||||
uint32_t rstart1 = 0, nrows1 = total_rows_1;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range0 = htp_tensor_mdev_partition(
|
||||
total_rows_0, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
rstart0 = range0.start;
|
||||
nrows0 = range0.count;
|
||||
|
||||
const struct htp_tensor_mdev_range range1 = htp_tensor_mdev_partition(
|
||||
total_rows_1, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
rstart1 = range1.start;
|
||||
nrows1 = range1.count;
|
||||
}
|
||||
|
||||
dma_queue * q = octx->ctx->dma[0];
|
||||
|
||||
dma_cpy_sametype_sameshape_range(q, &view0, src0, type_size, rstart0, nrows0);
|
||||
dma_cpy_sametype_sameshape_range(q, &view1, src1, type_size, rstart1, nrows1);
|
||||
dma_queue_flush(q);
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int concat_transposed(struct htp_ops_context * octx, const struct htp_concat_kernel_params * kparams, uint32_t type_size) {
|
||||
if (!htp_ops_context_set_n_threads(octx, kparams->n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
const uint32_t total_rows = dst->ne[1];
|
||||
uint32_t row_start = 0;
|
||||
uint32_t nrows = total_rows;
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(total_rows, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
row_start = range.start;
|
||||
nrows = range.count;
|
||||
}
|
||||
|
||||
if (nrows == 0 || dst->ne[2] == 0 || dst->ne[3] == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (kparams->vtcm_size > octx->ctx->vtcm_size) {
|
||||
return HTP_STATUS_VTCM_TOO_SMALL;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
struct htp_concat_transposed_vtcm_layout layout;
|
||||
htp_concat_transposed_vtcm_layout_build(&layout, octx->src[0]->ne[0], octx->src[1]->ne[0], type_size, n_threads);
|
||||
|
||||
uint8_t * vtcm_base = (uint8_t *) octx->ctx->vtcm_base;
|
||||
|
||||
struct htp_concat_context cctx;
|
||||
cctx.octx = octx;
|
||||
cctx.spad0_base = VTCM_LAYOUT_PTR(uint8_t, vtcm_base, layout.off_spad0);
|
||||
cctx.spad1_base = VTCM_LAYOUT_PTR(uint8_t, vtcm_base, layout.off_spad1);
|
||||
cctx.spad0_size_per_thread = layout.src0_spad_size_per_thread;
|
||||
cctx.spad1_size_per_thread = layout.src1_spad_size_per_thread;
|
||||
cctx.row_start = row_start;
|
||||
cctx.nrows = nrows;
|
||||
cctx.nplanes = dst->ne[2] * dst->ne[3];
|
||||
cctx.div_ne2 = kparams->u.transposed.div_ne2;
|
||||
cctx.nrows_per_thread = fastdiv(nrows + n_threads - 1, &octx->n_threads_div);
|
||||
|
||||
work_queue_func_t worker_func = (type_size == 4) ? concat_2d_f32_transposed : concat_2d_f16_transposed;
|
||||
work_queue_run(octx->ctx->work_queue, worker_func, &cctx, n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
int op_concat(struct htp_ops_context * octx) {
|
||||
const struct htp_concat_kernel_params * kparams = (const struct htp_concat_kernel_params *) octx->kernel_params;
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * src1 = octx->src[1];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
int status = HTP_STATUS_OK;
|
||||
const uint32_t type_size = (dst->type == HTP_TYPE_F32 || dst->type == HTP_TYPE_I32) ? 4 : 2;
|
||||
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_CONCAT_KERNEL_REGULAR:
|
||||
status = concat_regular(octx, kparams->dim, type_size);
|
||||
break;
|
||||
|
||||
case HTP_CONCAT_KERNEL_TRANSPOSED:
|
||||
status = concat_transposed(octx, kparams, type_size);
|
||||
break;
|
||||
|
||||
case HTP_CONCAT_KERNEL_GATHER_ROWS:
|
||||
status = htp_gather_rows(octx, src0, src1, &kparams->u.gather, kparams->n_threads);
|
||||
break;
|
||||
|
||||
default:
|
||||
status = HTP_STATUS_NO_SUPPORT;
|
||||
break;
|
||||
}
|
||||
|
||||
htp_ops_context_set_status(octx, status);
|
||||
|
||||
return octx->status;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef HTP_CONCAT_OPS_H
|
||||
#define HTP_CONCAT_OPS_H
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include "gather-rows.h"
|
||||
#include <stdint.h>
|
||||
|
||||
enum htp_concat_kernel_type {
|
||||
HTP_CONCAT_KERNEL_UNSUPPORTED = 0,
|
||||
HTP_CONCAT_KERNEL_REGULAR = 1,
|
||||
HTP_CONCAT_KERNEL_TRANSPOSED = 2,
|
||||
HTP_CONCAT_KERNEL_GATHER_ROWS = 3,
|
||||
};
|
||||
|
||||
struct htp_concat_transposed_params {
|
||||
struct fastdiv_values div_ne2;
|
||||
};
|
||||
|
||||
struct htp_concat_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t dim;
|
||||
uint8_t n_threads;
|
||||
uint8_t pad;
|
||||
|
||||
uint32_t vtcm_size;
|
||||
|
||||
union {
|
||||
struct htp_concat_transposed_params transposed;
|
||||
struct htp_gather_rows_params gather;
|
||||
} u;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_concat_kernel_params) <= 128, "htp_concat_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_concat_kernel_params) <= 128, "htp_concat_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
struct htp_concat_transposed_vtcm_layout {
|
||||
uint32_t src0_spad_size_per_thread;
|
||||
uint32_t src1_spad_size_per_thread;
|
||||
size_t off_spad0;
|
||||
size_t off_spad1;
|
||||
uint32_t total_bytes;
|
||||
};
|
||||
|
||||
static inline void htp_concat_transposed_vtcm_layout_build(
|
||||
struct htp_concat_transposed_vtcm_layout * L,
|
||||
uint32_t src0_ne0,
|
||||
uint32_t src1_ne0,
|
||||
uint32_t type_size,
|
||||
uint32_t n_threads) {
|
||||
|
||||
uint32_t block_i = (type_size == 4) ? 32 : 64;
|
||||
uint32_t spad1_stride = block_i * type_size;
|
||||
uint32_t src1_ne0_padded = hex_round_up(src1_ne0, block_i);
|
||||
uint32_t spad0_row_bytes = hex_round_up(src0_ne0 * type_size, 128) + src1_ne0_padded * type_size;
|
||||
|
||||
L->src0_spad_size_per_thread = block_i * spad0_row_bytes;
|
||||
L->src1_spad_size_per_thread = src1_ne0_padded * spad1_stride;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_spad0, (size_t) n_threads * L->src0_spad_size_per_thread);
|
||||
VTCM_LAYOUT_ALLOC(off, off_spad1, (size_t) n_threads * L->src1_spad_size_per_thread);
|
||||
L->total_bytes = (uint32_t) off;
|
||||
}
|
||||
|
||||
#endif // HTP_CONCAT_OPS_H
|
||||
@@ -0,0 +1,488 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <HAP_perf.h>
|
||||
#include <qurt_memory.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "cpy-ops.h"
|
||||
#include "dma-copy.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-fence.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "gather-rows.h"
|
||||
|
||||
struct htp_copy_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_copy_kernel_params * kparams;
|
||||
|
||||
uint32_t row_start;
|
||||
uint32_t nrows;
|
||||
uint32_t src0_nrows_per_thread;
|
||||
|
||||
uint32_t elem_start;
|
||||
uint32_t nelem;
|
||||
uint32_t elem_per_thread;
|
||||
|
||||
struct htp_copy_convert_vtcm_layout layout;
|
||||
uint8_t * vtcm_src0;
|
||||
uint8_t * vtcm_dst;
|
||||
};
|
||||
|
||||
#define cpy_preamble \
|
||||
const struct htp_tensor *src0 = octx->src[0]; \
|
||||
const struct htp_tensor *dst = octx->dst; \
|
||||
\
|
||||
const uint32_t ne00 = src0->ne[0]; \
|
||||
const uint32_t ne01 = src0->ne[1]; \
|
||||
const uint32_t ne02 = src0->ne[2]; \
|
||||
const uint32_t ne03 = src0->ne[3]; \
|
||||
\
|
||||
const uint32_t nb00 = src0->nb[0]; \
|
||||
const uint32_t nb01 = src0->nb[1]; \
|
||||
const uint32_t nb02 = src0->nb[2]; \
|
||||
const uint32_t nb03 = src0->nb[3]; \
|
||||
\
|
||||
const uint32_t ne0 = dst->ne[0]; \
|
||||
const uint32_t ne1 = dst->ne[1]; \
|
||||
const uint32_t ne2 = dst->ne[2]; \
|
||||
const uint32_t ne3 = dst->ne[3]; \
|
||||
\
|
||||
const uint32_t nb0 = dst->nb[0]; \
|
||||
const uint32_t nb1 = dst->nb[1]; \
|
||||
const uint32_t nb2 = dst->nb[2]; \
|
||||
const uint32_t nb3 = dst->nb[3];
|
||||
|
||||
#define DEFINE_CPY_RESHAPE(NAME, ELEM_TYPE, ELEM_SIZE) \
|
||||
static void cpy_thread_##NAME##_reshape(unsigned int nth, unsigned int ith, void * data) { \
|
||||
struct htp_copy_context * ct = (struct htp_copy_context *) data; \
|
||||
struct htp_ops_context * octx = ct->octx; \
|
||||
cpy_preamble; \
|
||||
const uint32_t th_nelem = ct->elem_per_thread; \
|
||||
const uint32_t th_start = ct->elem_start + ith * th_nelem; \
|
||||
const uint32_t th_end = MIN(th_start + th_nelem, ct->elem_start + ct->nelem); \
|
||||
if (th_start >= th_end) return; \
|
||||
\
|
||||
dma_queue * dma_q = octx->ctx->dma[ith]; \
|
||||
\
|
||||
const uint32_t ne01_ne00 = ne01 * ne00; \
|
||||
const uint32_t ne02_ne01_ne00 = ne02 * ne01_ne00; \
|
||||
const uint32_t ne1_ne0 = ne1 * ne0; \
|
||||
const uint32_t ne2_ne1_ne0 = ne2 * ne1_ne0; \
|
||||
\
|
||||
const struct htp_copy_reshape_params * rsh = &ct->kparams->u.reshape; \
|
||||
uint32_t e = th_start; \
|
||||
uint32_t i13 = fastdiv(e, &rsh->div_ne2_ne1_ne0); \
|
||||
uint32_t rem = e - i13 * ne2_ne1_ne0; \
|
||||
uint32_t i12 = fastdiv(rem, &rsh->div_ne1_ne0); \
|
||||
uint32_t rem2 = rem - i12 * ne1_ne0; \
|
||||
uint32_t i11 = fastdiv(rem2, &rsh->div_ne0); \
|
||||
uint32_t i10 = rem2 - i11 * ne0; \
|
||||
\
|
||||
uint32_t i03 = fastdiv(e, &rsh->div_ne02_ne01_ne00); \
|
||||
uint32_t rem_s = e - i03 * ne02_ne01_ne00; \
|
||||
uint32_t i02 = fastdiv(rem_s, &rsh->div_ne01_ne00); \
|
||||
uint32_t rem2_s = rem_s - i02 * ne01_ne00; \
|
||||
uint32_t i01 = fastdiv(rem2_s, &rsh->div_ne00); \
|
||||
uint32_t i00 = rem2_s - i01 * ne00; \
|
||||
\
|
||||
dma_addr_t dst_addr = dst->data + i10*nb0 + i11*nb1 + i12*nb2 + i13*nb3; \
|
||||
dma_addr_t src0_addr = src0->data + i00*nb00 + i01*nb01 + i02*nb02 + i03*nb03; \
|
||||
\
|
||||
const bool rows_contig = (nb00 == ELEM_SIZE) && (nb0 == ELEM_SIZE); \
|
||||
\
|
||||
while (e < th_end) { \
|
||||
const uint32_t run = MIN(MIN(ne00 - i00, ne0 - i10), th_end - e); \
|
||||
if (rows_contig) { \
|
||||
dma_cpy_sametype_reshape_contig(dma_q, dst_addr, src0_addr, run * ELEM_SIZE); \
|
||||
} else { \
|
||||
dma_cpy_push_2d_chunked(dma_q, dst_addr, src0_addr, nb0, nb00, ELEM_SIZE, run); \
|
||||
} \
|
||||
e += run; \
|
||||
\
|
||||
dst_addr += run * nb0; \
|
||||
i10 += run; \
|
||||
if (i10 == ne0) { \
|
||||
i10 = 0; \
|
||||
if (++i11 == ne1) { \
|
||||
i11 = 0; \
|
||||
if (++i12 == ne2) { \
|
||||
i12 = 0; \
|
||||
i13++; \
|
||||
} \
|
||||
} \
|
||||
dst_addr = dst->data + i11*nb1 + i12*nb2 + i13*nb3; \
|
||||
} \
|
||||
\
|
||||
src0_addr += run * nb00; \
|
||||
i00 += run; \
|
||||
if (i00 == ne00) { \
|
||||
i00 = 0; \
|
||||
if (++i01 == ne01) { \
|
||||
i01 = 0; \
|
||||
if (++i02 == ne02) { \
|
||||
i02 = 0; \
|
||||
i03++; \
|
||||
} \
|
||||
} \
|
||||
src0_addr = src0->data + i01*nb01 + i02*nb02 + i03*nb03; \
|
||||
} \
|
||||
} \
|
||||
dma_queue_flush(dma_q); \
|
||||
}
|
||||
|
||||
DEFINE_CPY_RESHAPE(f32, float, 4)
|
||||
DEFINE_CPY_RESHAPE(f16, __fp16, 2)
|
||||
DEFINE_CPY_RESHAPE(i32, int32_t, 4)
|
||||
|
||||
struct cpy_row_cursor {
|
||||
uint32_t r;
|
||||
uint32_t i01;
|
||||
uint32_t i02;
|
||||
uint32_t i03;
|
||||
};
|
||||
|
||||
static inline void cpy_cursor_advance(struct cpy_row_cursor * c, uint32_t n, uint32_t ne01, uint32_t ne02) {
|
||||
c->r += n;
|
||||
c->i01 += n;
|
||||
if (c->i01 == ne01) {
|
||||
c->i01 = 0;
|
||||
if (++c->i02 == ne02) {
|
||||
c->i02 = 0;
|
||||
c->i03++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFINE_CPY_CONVERT_SAMESHAPE(NAME, CONV_FUNC) \
|
||||
static void cpy_thread_##NAME##_sameshape(unsigned int nth, unsigned int ith, void * data) { \
|
||||
struct htp_copy_context * ct = (struct htp_copy_context *) data; \
|
||||
struct htp_ops_context * octx = ct->octx; \
|
||||
cpy_preamble; \
|
||||
\
|
||||
const uint32_t dr = ct->src0_nrows_per_thread; \
|
||||
const uint32_t ir0 = ct->row_start + dr * ith; \
|
||||
const uint32_t ir1 = MIN(ir0 + dr, ct->row_start + ct->nrows); \
|
||||
if (ir0 >= ir1) return; \
|
||||
\
|
||||
dma_queue * dma_q = octx->ctx->dma[ith]; \
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith]; \
|
||||
\
|
||||
const struct htp_copy_convert_params * cvt = &ct->kparams->u.convert; \
|
||||
const struct htp_copy_convert_vtcm_layout * L = &ct->layout; \
|
||||
const uint32_t src0_buf_size = L->src0_buf_size; \
|
||||
const uint32_t dst_buf_size = L->dst_buf_size; \
|
||||
const uint32_t src0_row_stride = L->src0_row_stride; \
|
||||
const uint32_t dst_row_stride = L->dst_row_stride; \
|
||||
const uint32_t blk_rows = L->blk_rows; \
|
||||
uint8_t * vtcm_src0_base = ct->vtcm_src0 + ith * L->spad0_size_per_thread; \
|
||||
uint8_t * vtcm_dst_base = ct->vtcm_dst + ith * L->spad1_size_per_thread; \
|
||||
const uint32_t src0_row_size = ne00 * ct->kparams->src0_type_size; \
|
||||
const uint32_t dst_row_size = ne00 * ct->kparams->dst_type_size; \
|
||||
\
|
||||
const uint32_t ne02_ne01 = ne02 * ne01; \
|
||||
uint32_t i03 = fastdiv(ir0, &cvt->div_ne02_ne01); \
|
||||
uint32_t rem = ir0 - i03 * ne02_ne01; \
|
||||
uint32_t i02 = fastdiv(rem, &cvt->div_ne01); \
|
||||
uint32_t i01 = rem - i02 * ne01; \
|
||||
\
|
||||
struct cpy_row_cursor f = { ir0, i01, i02, i03 }; \
|
||||
struct cpy_row_cursor c = f; \
|
||||
\
|
||||
for (uint32_t b = 0; b < 2 && f.r < ir1; ++b) { \
|
||||
uint8_t * src_spad = vtcm_src0_base + b * src0_buf_size; \
|
||||
uint8_t * dst_spad = vtcm_dst_base + b * dst_buf_size; \
|
||||
const uint32_t nr = MIN(blk_rows, MIN(ne01 - f.i01, ir1 - f.r)); \
|
||||
dma_addr_t f_src0_addr = src0->data + f.i01*nb01 + f.i02*nb02 + f.i03*nb03; \
|
||||
dma_queue_push(dma_q, dma_make_data(dst->data, dst_spad), \
|
||||
dst_row_size, dst_buf_size, dst_row_size, 0); \
|
||||
dma_queue_push(dma_q, dma_make_data(src_spad, f_src0_addr), \
|
||||
src0_row_stride, nb01, src0_row_size, nr); \
|
||||
cpy_cursor_advance(&f, nr, ne01, ne02); \
|
||||
} \
|
||||
\
|
||||
for (uint32_t blk = 0; c.r < ir1; ++blk) { \
|
||||
const uint32_t nr = MIN(blk_rows, MIN(ne01 - c.i01, ir1 - c.r)); \
|
||||
uint8_t * dst_spad = (uint8_t *) (uintptr_t) dma_queue_pop(dma_q).src; \
|
||||
uint8_t * src_spad = (uint8_t *) (uintptr_t) dma_queue_pop(dma_q).dst; \
|
||||
\
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) blk); \
|
||||
for (uint32_t r = 0; r < nr; ++r) { \
|
||||
CONV_FUNC(dst_spad + r * dst_row_stride, src_spad + r * src0_row_stride, ne00); \
|
||||
} \
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) blk); \
|
||||
\
|
||||
dma_addr_t c_dst_addr = dst->data + c.i01*nb1 + c.i02*nb2 + c.i03*nb3; \
|
||||
dma_queue_push(dma_q, dma_make_data(c_dst_addr, dst_spad), \
|
||||
nb1, dst_row_stride, dst_row_size, nr); \
|
||||
cpy_cursor_advance(&c, nr, ne01, ne02); \
|
||||
\
|
||||
if (f.r < ir1) { \
|
||||
const uint32_t fnr = MIN(blk_rows, MIN(ne01 - f.i01, ir1 - f.r)); \
|
||||
dma_addr_t f_src0_addr = src0->data + f.i01*nb01 + f.i02*nb02 + f.i03*nb03; \
|
||||
dma_queue_push(dma_q, dma_make_data(src_spad, f_src0_addr), \
|
||||
src0_row_stride, nb01, src0_row_size, fnr); \
|
||||
cpy_cursor_advance(&f, fnr, ne01, ne02); \
|
||||
} \
|
||||
} \
|
||||
dma_queue_flush(dma_q); \
|
||||
}
|
||||
|
||||
DEFINE_CPY_CONVERT_SAMESHAPE(f16_f32, hvx_copy_f16_f32_aa)
|
||||
DEFINE_CPY_CONVERT_SAMESHAPE(f32_f16, hvx_copy_f32_f16_aa)
|
||||
DEFINE_CPY_CONVERT_SAMESHAPE(i32_f32, hvx_copy_i32_f32_aa)
|
||||
DEFINE_CPY_CONVERT_SAMESHAPE(f32_i32, hvx_copy_f32_i32_aa)
|
||||
|
||||
static int cpy_scalar(struct htp_ops_context * octx, const struct htp_copy_kernel_params * kparams) {
|
||||
if (octx->ctx->mdev.count > 1 && octx->ctx->mdev.idx > 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
if (src0->type == dst->type) {
|
||||
dma_cpy_sametype_reshape_contig(octx->ctx->dma[0], dst->data, src0->data, kparams->src0_type_size);
|
||||
dma_queue_flush(octx->ctx->dma[0]);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[0];
|
||||
uint8_t * s_ptr = VTCM_LAYOUT_PTR(uint8_t, octx->ctx->vtcm_base, 0);
|
||||
uint8_t * d_ptr = VTCM_LAYOUT_PTR(uint8_t, octx->ctx->vtcm_base, VLEN);
|
||||
dma_addr_t s_vtcm = (dma_addr_t)(uintptr_t) s_ptr;
|
||||
dma_addr_t d_vtcm = (dma_addr_t)(uintptr_t) d_ptr;
|
||||
const uint32_t s_size = kparams->src0_type_size;
|
||||
const uint32_t d_size = kparams->dst_type_size;
|
||||
|
||||
dma_queue_push(dma_q, dma_make_data(s_vtcm, src0->data), s_size, s_size, s_size, 1);
|
||||
dma_queue_pop(dma_q);
|
||||
|
||||
const HVX_Vector v_src = hvx_vmem(s_ptr);
|
||||
HVX_Vector v_dst;
|
||||
|
||||
if (src0->type == HTP_TYPE_F32 && dst->type == HTP_TYPE_I32) {
|
||||
v_dst = Q6_Vw_equals_Vsf(v_src);
|
||||
} else if (src0->type == HTP_TYPE_I32 && dst->type == HTP_TYPE_F32) {
|
||||
v_dst = Q6_Vsf_equals_Vw(v_src);
|
||||
} else if (src0->type == HTP_TYPE_F32 && dst->type == HTP_TYPE_F16) {
|
||||
v_dst = hvx_vec_f32_to_f16(v_src, v_src);
|
||||
} else if (src0->type == HTP_TYPE_F16 && dst->type == HTP_TYPE_F32) {
|
||||
v_dst = Q6_V_lo_W(hvx_vec_f16_to_f32(v_src));
|
||||
} else {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
hvx_vmem(d_ptr) = v_dst;
|
||||
|
||||
dma_queue_push(dma_q, dma_make_data(dst->data, d_vtcm), d_size, d_size, d_size, 1);
|
||||
dma_queue_flush(dma_q);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int cpy_1d_contig(struct htp_ops_context * octx, const struct htp_copy_kernel_params * kparams) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
uint32_t elem_start = 0;
|
||||
uint32_t nelem = kparams->total_elems;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
kparams->total_elems, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
elem_start = range.start;
|
||||
nelem = range.count;
|
||||
}
|
||||
|
||||
if (nelem > 0) {
|
||||
dma_queue * q = octx->ctx->dma[0];
|
||||
const uint32_t type_size = kparams->src0_type_size;
|
||||
dma_addr_t dst_addr = dst->data + elem_start * type_size;
|
||||
dma_addr_t src0_addr = src0->data + elem_start * type_size;
|
||||
dma_cpy_sametype_reshape_contig(q, dst_addr, src0_addr, nelem * type_size);
|
||||
dma_queue_flush(q);
|
||||
}
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int cpy_sameshape_sametype(struct htp_ops_context * octx, const struct htp_copy_kernel_params * kparams) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
uint32_t row_start = 0;
|
||||
uint32_t nrows = kparams->total_rows;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
kparams->total_rows, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
row_start = range.start;
|
||||
nrows = range.count;
|
||||
}
|
||||
|
||||
if (nrows > 0) {
|
||||
dma_queue * q = octx->ctx->dma[0];
|
||||
dma_cpy_sametype_sameshape_range(q, dst, src0, kparams->src0_type_size, row_start, nrows);
|
||||
dma_queue_flush(q);
|
||||
}
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int cpy_sameshape_convert(struct htp_ops_context * octx, const struct htp_copy_kernel_params * kparams) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, kparams->n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
uint32_t row_start = 0;
|
||||
uint32_t nrows = kparams->total_rows;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
kparams->total_rows, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
row_start = range.start;
|
||||
nrows = range.count;
|
||||
}
|
||||
|
||||
if (nrows == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (kparams->vtcm_size > octx->ctx->vtcm_size) {
|
||||
return HTP_STATUS_VTCM_TOO_SMALL;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
const struct htp_copy_convert_params * cvt = &kparams->u.convert;
|
||||
|
||||
struct htp_copy_context ct;
|
||||
htp_copy_convert_vtcm_layout_build(&ct.layout, src0->ne[0], kparams->src0_type_size, kparams->dst_type_size, cvt->blk_rows, n_threads);
|
||||
|
||||
ct.octx = octx;
|
||||
ct.kparams = kparams;
|
||||
ct.row_start = row_start;
|
||||
ct.nrows = nrows;
|
||||
ct.src0_nrows_per_thread = fastdiv(nrows + n_threads - 1, &octx->n_threads_div);
|
||||
|
||||
uint8_t * vtcm_base = (uint8_t *) octx->ctx->vtcm_base;
|
||||
ct.vtcm_src0 = VTCM_LAYOUT_PTR(uint8_t, vtcm_base, ct.layout.off_src0);
|
||||
ct.vtcm_dst = VTCM_LAYOUT_PTR(uint8_t, vtcm_base, ct.layout.off_dst);
|
||||
|
||||
work_queue_func_t copy_fun = NULL;
|
||||
if (dst->type == HTP_TYPE_F16 && src0->type == HTP_TYPE_F32) {
|
||||
copy_fun = cpy_thread_f16_f32_sameshape;
|
||||
} else if (dst->type == HTP_TYPE_F32 && src0->type == HTP_TYPE_F16) {
|
||||
copy_fun = cpy_thread_f32_f16_sameshape;
|
||||
} else if (dst->type == HTP_TYPE_I32 && src0->type == HTP_TYPE_F32) {
|
||||
copy_fun = cpy_thread_i32_f32_sameshape;
|
||||
} else if (dst->type == HTP_TYPE_F32 && src0->type == HTP_TYPE_I32) {
|
||||
copy_fun = cpy_thread_f32_i32_sameshape;
|
||||
} else {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, copy_fun, &ct, n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
static int cpy_reshape(struct htp_ops_context * octx, const struct htp_copy_kernel_params * kparams) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, kparams->n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
uint32_t elem_start = 0;
|
||||
uint32_t nelem = kparams->total_elems;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
kparams->total_elems, 1, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
elem_start = range.start;
|
||||
nelem = range.count;
|
||||
}
|
||||
|
||||
if (nelem == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
struct htp_copy_context ct;
|
||||
ct.octx = octx;
|
||||
ct.kparams = kparams;
|
||||
ct.elem_start = elem_start;
|
||||
ct.nelem = nelem;
|
||||
ct.elem_per_thread = fastdiv(nelem + n_threads - 1, &octx->n_threads_div);
|
||||
|
||||
work_queue_func_t copy_fun = NULL;
|
||||
switch (src0->type) {
|
||||
case HTP_TYPE_F32: copy_fun = cpy_thread_f32_reshape; break;
|
||||
case HTP_TYPE_F16: copy_fun = cpy_thread_f16_reshape; break;
|
||||
case HTP_TYPE_I32: copy_fun = cpy_thread_i32_reshape; break;
|
||||
default: return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, copy_fun, &ct, n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
int op_cpy(struct htp_ops_context * octx) {
|
||||
const struct htp_copy_kernel_params * kparams = (const struct htp_copy_kernel_params *) octx->kernel_params;
|
||||
int status = HTP_STATUS_OK;
|
||||
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_COPY_KERNEL_SCALAR:
|
||||
status = cpy_scalar(octx, kparams);
|
||||
break;
|
||||
case HTP_COPY_KERNEL_1D_CONTIG:
|
||||
status = cpy_1d_contig(octx, kparams);
|
||||
break;
|
||||
case HTP_COPY_KERNEL_SAMESHAPE_SAMETYPE:
|
||||
status = cpy_sameshape_sametype(octx, kparams);
|
||||
break;
|
||||
case HTP_COPY_KERNEL_SAMESHAPE_CONVERT:
|
||||
status = cpy_sameshape_convert(octx, kparams);
|
||||
break;
|
||||
case HTP_COPY_KERNEL_RESHAPE:
|
||||
status = cpy_reshape(octx, kparams);
|
||||
break;
|
||||
case HTP_COPY_KERNEL_GATHER_ROWS:
|
||||
status = htp_gather_rows(octx, octx->src[0], NULL, &kparams->u.gather, kparams->n_threads);
|
||||
break;
|
||||
default:
|
||||
status = HTP_STATUS_NO_SUPPORT;
|
||||
break;
|
||||
}
|
||||
|
||||
htp_ops_context_set_status(octx, status);
|
||||
|
||||
if (octx->op == HTP_OP_CPY_FENCE) {
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
htp_mdev_group_barrier(octx);
|
||||
}
|
||||
|
||||
if (octx->ctx->mdev.idx == 0) {
|
||||
const struct htp_tensor * sync = octx->src[1];
|
||||
const uint32_t seq = (uint32_t) octx->op_params[0];
|
||||
atomic_uint * sync_fence = (atomic_uint *) (uintptr_t) sync->data;
|
||||
htp_fence_write(sync_fence, seq, octx->status);
|
||||
|
||||
FARF(HIGH, "ggml-hex: sync-release : fence %p seq 0x%x status %d\n", sync_fence, seq, octx->status);
|
||||
}
|
||||
}
|
||||
|
||||
return octx->status;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
#ifndef HTP_CPY_OPS_H
|
||||
#define HTP_CPY_OPS_H
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include "gather-rows.h"
|
||||
#include <stdint.h>
|
||||
|
||||
enum htp_copy_kernel_type {
|
||||
HTP_COPY_KERNEL_UNSUPPORTED = 0,
|
||||
HTP_COPY_KERNEL_1D_CONTIG = 1,
|
||||
HTP_COPY_KERNEL_SAMESHAPE_SAMETYPE = 2,
|
||||
HTP_COPY_KERNEL_SAMESHAPE_CONVERT = 3,
|
||||
HTP_COPY_KERNEL_RESHAPE = 4,
|
||||
HTP_COPY_KERNEL_SCALAR = 5,
|
||||
HTP_COPY_KERNEL_GATHER_ROWS = 6,
|
||||
};
|
||||
|
||||
struct htp_copy_convert_params {
|
||||
uint32_t blk_rows; // rows moved per DMA descriptor
|
||||
struct fastdiv_values div_ne01;
|
||||
struct fastdiv_values div_ne02_ne01;
|
||||
};
|
||||
|
||||
struct htp_copy_reshape_params {
|
||||
struct fastdiv_values div_ne0;
|
||||
struct fastdiv_values div_ne1_ne0;
|
||||
struct fastdiv_values div_ne2_ne1_ne0;
|
||||
struct fastdiv_values div_ne00;
|
||||
struct fastdiv_values div_ne01_ne00;
|
||||
struct fastdiv_values div_ne02_ne01_ne00;
|
||||
};
|
||||
|
||||
struct htp_copy_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t src0_type_size;
|
||||
uint8_t dst_type_size;
|
||||
uint8_t n_threads;
|
||||
|
||||
uint32_t total_elems;
|
||||
uint32_t total_rows;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
union {
|
||||
struct htp_copy_convert_params convert;
|
||||
struct htp_copy_reshape_params reshape;
|
||||
struct htp_gather_rows_params gather;
|
||||
} u;
|
||||
};
|
||||
|
||||
// Size of one VTCM staging buffer: big enough that a DMA descriptor moves many short
|
||||
// rows at once. The copy kernels keep four per thread (two in flight each way), about
|
||||
// 1 MB of VTCM with 8 threads.
|
||||
#define HTP_COPY_VTCM_BLK_BYTES (32 * 1024)
|
||||
|
||||
struct htp_copy_convert_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src0;
|
||||
size_t off_dst;
|
||||
uint32_t src0_row_stride;
|
||||
uint32_t dst_row_stride;
|
||||
uint32_t blk_rows;
|
||||
uint32_t src0_buf_size;
|
||||
uint32_t dst_buf_size;
|
||||
uint32_t spad0_size_per_thread;
|
||||
uint32_t spad1_size_per_thread;
|
||||
};
|
||||
|
||||
static inline void htp_copy_convert_vtcm_layout_build(
|
||||
struct htp_copy_convert_vtcm_layout * L,
|
||||
uint32_t ne00,
|
||||
uint32_t src_type_size,
|
||||
uint32_t dst_type_size,
|
||||
uint32_t blk_rows,
|
||||
uint32_t n_threads) {
|
||||
|
||||
const uint32_t src0_row_stride = hex_round_up(ne00 * src_type_size, 256);
|
||||
const uint32_t dst_row_stride = hex_round_up(ne00 * dst_type_size, 256);
|
||||
|
||||
L->src0_row_stride = src0_row_stride;
|
||||
L->dst_row_stride = dst_row_stride;
|
||||
L->blk_rows = blk_rows;
|
||||
L->src0_buf_size = blk_rows * src0_row_stride;
|
||||
L->dst_buf_size = blk_rows * dst_row_stride;
|
||||
L->spad0_size_per_thread = 2 * L->src0_buf_size;
|
||||
L->spad1_size_per_thread = 2 * L->dst_buf_size;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src0, (size_t) n_threads * L->spad0_size_per_thread);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, (size_t) n_threads * L->spad1_size_per_thread);
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
// ne01 bounds a block: rows of one block share the dim-1 stride.
|
||||
static inline void htp_copy_convert_solve_layout(
|
||||
struct htp_copy_convert_vtcm_layout * layout,
|
||||
uint32_t ne00,
|
||||
uint32_t ne01,
|
||||
uint32_t src_type_size,
|
||||
uint32_t dst_type_size,
|
||||
uint32_t n_threads) {
|
||||
|
||||
const uint32_t src0_row_stride = hex_round_up(ne00 * src_type_size, 256);
|
||||
const uint32_t dst_row_stride = hex_round_up(ne00 * dst_type_size, 256);
|
||||
|
||||
const uint32_t max_row = src0_row_stride > dst_row_stride ? src0_row_stride : dst_row_stride;
|
||||
uint32_t blk_rows = HTP_COPY_VTCM_BLK_BYTES / max_row;
|
||||
if (blk_rows > ne01) {
|
||||
blk_rows = ne01;
|
||||
}
|
||||
if (blk_rows == 0) {
|
||||
blk_rows = 1;
|
||||
}
|
||||
|
||||
htp_copy_convert_vtcm_layout_build(layout, ne00, src_type_size, dst_type_size, blk_rows, n_threads);
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_copy_kernel_params) <= 128, "htp_copy_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_copy_kernel_params) <= 128, "htp_copy_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_CPY_OPS_H
|
||||
@@ -0,0 +1,290 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <HAP_perf.h>
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "hex-common.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include "hvx-types.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "dma-queue.h"
|
||||
#include "cumsum-ops.h"
|
||||
|
||||
#define htp_cumsum_tensors_preamble \
|
||||
const struct htp_tensor * restrict src0 = octx->src[0]; \
|
||||
const struct htp_tensor * restrict dst = octx->dst; \
|
||||
\
|
||||
const uint32_t ne00 = src0->ne[0]; \
|
||||
const uint32_t ne01 = src0->ne[1]; \
|
||||
const uint32_t ne02 = src0->ne[2]; \
|
||||
const uint32_t ne03 = src0->ne[3]; \
|
||||
\
|
||||
const uint32_t ne0 = dst->ne[0]; \
|
||||
const uint32_t ne1 = dst->ne[1]; \
|
||||
const uint32_t ne2 = dst->ne[2]; \
|
||||
const uint32_t ne3 = dst->ne[3]; \
|
||||
\
|
||||
const uint32_t nb00 = src0->nb[0]; \
|
||||
const uint32_t nb01 = src0->nb[1]; \
|
||||
const uint32_t nb02 = src0->nb[2]; \
|
||||
const uint32_t nb03 = src0->nb[3]; \
|
||||
\
|
||||
const uint32_t nb0 = dst->nb[0]; \
|
||||
const uint32_t nb1 = dst->nb[1]; \
|
||||
const uint32_t nb2 = dst->nb[2]; \
|
||||
const uint32_t nb3 = dst->nb[3];
|
||||
|
||||
struct htp_cumsum_context {
|
||||
struct htp_ops_context * octx;
|
||||
size_t src_row_size;
|
||||
size_t dst_row_size;
|
||||
size_t src_row_size_aligned;
|
||||
size_t dst_row_size_aligned;
|
||||
uint32_t rows_per_thread;
|
||||
uint32_t total_rows;
|
||||
uint32_t row_start;
|
||||
};
|
||||
|
||||
#define htp_cumsum_preamble \
|
||||
struct htp_cumsum_context * cctx = (struct htp_cumsum_context *) data; \
|
||||
struct htp_ops_context * octx = cctx->octx; \
|
||||
htp_cumsum_tensors_preamble; \
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HVX prefix scan helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#if __HVX_ARCH__ > 75
|
||||
static inline HVX_Vector hvx_cumsum_vadd(HVX_Vector a, HVX_Vector b) {
|
||||
return Q6_Vsf_vadd_VsfVsf(a, b);
|
||||
}
|
||||
#else
|
||||
static inline HVX_Vector hvx_cumsum_vadd(HVX_Vector a, HVX_Vector b) {
|
||||
return Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(a, b));
|
||||
}
|
||||
#endif // __HVX_ARCH__ > 75
|
||||
|
||||
static inline HVX_Vector hvx_prefix_scan_f32(HVX_Vector v, HVX_Vector carry_in) {
|
||||
const HVX_Vector zero = Q6_V_vsplat_R(0);
|
||||
|
||||
v = hvx_cumsum_vadd(v, Q6_V_vlalign_VVR(v, zero, 4));
|
||||
v = hvx_cumsum_vadd(v, Q6_V_vlalign_VVR(v, zero, 8));
|
||||
v = hvx_cumsum_vadd(v, Q6_V_vlalign_VVR(v, zero, 16));
|
||||
v = hvx_cumsum_vadd(v, Q6_V_vlalign_VVR(v, zero, 32));
|
||||
v = hvx_cumsum_vadd(v, Q6_V_vlalign_VVR(v, zero, 64));
|
||||
v = hvx_cumsum_vadd(v, carry_in);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_splat_last_f32(HVX_Vector v) {
|
||||
return hvx_vec_repl4(Q6_V_vror_VR(v, 124));
|
||||
}
|
||||
|
||||
static inline void hvx_cumsum_row_f32(const float * restrict src, float * restrict dst, uint32_t n) {
|
||||
const uint32_t nvec = n >> 5;
|
||||
const uint32_t nloe = n & (VLEN_FP32 - 1);
|
||||
|
||||
HVX_Vector carry = Q6_V_vsplat_R(0);
|
||||
|
||||
for (uint32_t i = 0; i < nvec; i++) {
|
||||
HVX_Vector v = *((const HVX_UVector *) (src + i * VLEN_FP32));
|
||||
v = hvx_prefix_scan_f32(v, carry);
|
||||
hvx_vec_store_u(dst + i * VLEN_FP32, VLEN, v);
|
||||
carry = hvx_splat_last_f32(v);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
float acc = hvx_vec_get_f32(carry);
|
||||
const float * src_tail = src + nvec * VLEN_FP32;
|
||||
float * dst_tail = dst + nvec * VLEN_FP32;
|
||||
for (uint32_t i = 0; i < nloe; i++) {
|
||||
acc += src_tail[i];
|
||||
dst_tail[i] = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Per thread worker: Double-buffered DMA
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void cumsum_thread_f32_dma(unsigned int nth, unsigned int ith, void * data) {
|
||||
htp_cumsum_preamble;
|
||||
|
||||
const uint32_t ir0 = cctx->row_start + cctx->rows_per_thread * ith;
|
||||
const uint32_t ir1 = MIN(ir0 + cctx->rows_per_thread, cctx->row_start + cctx->total_rows);
|
||||
|
||||
if (ir0 >= ir1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t src_row_size = cctx->src_row_size;
|
||||
const size_t dst_row_size = cctx->dst_row_size;
|
||||
const size_t src_row_size_aligned = cctx->src_row_size_aligned;
|
||||
const size_t dst_row_size_aligned = cctx->dst_row_size_aligned;
|
||||
|
||||
const dma_addr_t src_data = src0->data;
|
||||
const dma_addr_t dst_data = dst->data;
|
||||
|
||||
uint8_t * src_spad = octx->src0_spad.data + (ith * src_row_size_aligned * 2);
|
||||
uint8_t * dst_spad = octx->dst_spad.data + (ith * dst_row_size_aligned * 2);
|
||||
|
||||
for (uint32_t ir = ir0, spad_idx = 0; ir < ir1 && spad_idx < 2; ir++, spad_idx++) {
|
||||
// Dummy dst writeback to establish queue ordering
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(dst_data, dst_spad + (spad_idx * dst_row_size_aligned)),
|
||||
dst_row_size, dst_row_size_aligned, dst_row_size, 0);
|
||||
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad + (spad_idx * src_row_size_aligned),
|
||||
src_data + (ir * src_row_size)),
|
||||
src_row_size_aligned, src_row_size, src_row_size, 1);
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
for (uint32_t ir = ir0; ir < ir1; ir++) {
|
||||
float * dst_spad_row = (float *) dma_queue_pop(dma_q).src;
|
||||
float * src_spad_row = (float *) dma_queue_pop(dma_q).dst;
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir);
|
||||
hvx_cumsum_row_f32(src_spad_row, dst_spad_row, ne00);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir);
|
||||
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(dst_data + (ir * dst_row_size), dst_spad_row),
|
||||
dst_row_size, dst_row_size_aligned, dst_row_size, 1);
|
||||
|
||||
const uint32_t next_row = ir + 2;
|
||||
if (next_row < ir1) {
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad_row, src_data + (next_row * src_row_size)),
|
||||
src_row_size_aligned, src_row_size, src_row_size, 1);
|
||||
}
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
|
||||
FARF(HIGH, "cumsum-f32-dma %d/%d: %ux%ux%ux%u (%u:%u) -> %ux%ux%ux%u\n",
|
||||
ith, nth, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], ir0, ir1,
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3]);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Per thread worker: Direct HVX (no DMA)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void cumsum_thread_f32(unsigned int nth, unsigned int ith, void * data) {
|
||||
htp_cumsum_preamble;
|
||||
|
||||
const uint8_t * src_data = (const uint8_t *) src0->data;
|
||||
uint8_t * dst_data = (uint8_t *) dst->data;
|
||||
|
||||
const uint32_t ir0 = cctx->row_start + cctx->rows_per_thread * ith;
|
||||
const uint32_t ir1 = MIN(ir0 + cctx->rows_per_thread, cctx->row_start + cctx->total_rows);
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir0);
|
||||
|
||||
for (uint32_t ir = ir0; ir < ir1; ir++) {
|
||||
const float * restrict src_row = (const float *) (src_data + ir * cctx->src_row_size);
|
||||
float * restrict dst_row = (float *) (dst_data + ir * cctx->dst_row_size);
|
||||
hvx_cumsum_row_f32(src_row, dst_row, ne00);
|
||||
}
|
||||
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir0);
|
||||
|
||||
FARF(HIGH, "cumsum-f32 %d/%d: %ux%ux%ux%u (%u:%u) -> %ux%ux%ux%u\n",
|
||||
ith, nth, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], ir0, ir1,
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3]);
|
||||
}
|
||||
|
||||
int op_cumsum_f32(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const struct htp_cumsum_kernel_params * kparams =
|
||||
(const struct htp_cumsum_kernel_params *) octx->kernel_params;
|
||||
|
||||
const uint32_t total_rows = kparams->total_rows;
|
||||
const size_t src_row_size = src0->nb[1];
|
||||
const size_t dst_row_size = dst->nb[1];
|
||||
|
||||
uint32_t row_start = 0;
|
||||
uint32_t nrows = total_rows;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
uint32_t rows_per_chunk = 0;
|
||||
htp_tensor_mdev_rows_per_chunk(dst, sizeof(float), (uint32_t) dst_row_size, &rows_per_chunk);
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(total_rows, rows_per_chunk, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
row_start = range.start;
|
||||
nrows = range.count;
|
||||
}
|
||||
|
||||
if (nrows == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, MIN(kparams->n_threads, nrows))) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
if (!kparams->use_dma && (htp_tensor_is_extended(src0) || htp_tensor_is_extended(dst))) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
struct htp_cumsum_vtcm_layout layout = { 0 };
|
||||
if (kparams->use_dma) {
|
||||
htp_cumsum_vtcm_layout_build(&layout, src_row_size, dst_row_size, n_threads);
|
||||
|
||||
octx->src0_spad.size_per_thread = layout.src0_bytes_per_thread;
|
||||
octx->dst_spad.size_per_thread = layout.dst_bytes_per_thread;
|
||||
octx->src0_spad.size = n_threads * layout.src0_bytes_per_thread;
|
||||
octx->dst_spad.size = n_threads * layout.dst_bytes_per_thread;
|
||||
octx->src0_spad.data = VTCM_LAYOUT_PTR(uint8_t, octx->ctx->vtcm_base, layout.off_src0);
|
||||
octx->dst_spad.data = VTCM_LAYOUT_PTR(uint8_t, octx->ctx->vtcm_base, layout.off_dst);
|
||||
octx->src0_spad.src = NULL;
|
||||
octx->dst_spad.src = NULL;
|
||||
}
|
||||
|
||||
struct htp_cumsum_context cctx = {
|
||||
.octx = octx,
|
||||
.src_row_size = src_row_size,
|
||||
.dst_row_size = dst_row_size,
|
||||
.src_row_size_aligned = layout.src_row_size_aligned,
|
||||
.dst_row_size_aligned = layout.dst_row_size_aligned,
|
||||
.rows_per_thread = fastdiv(nrows + n_threads - 1, &octx->n_threads_div),
|
||||
.total_rows = nrows,
|
||||
.row_start = row_start,
|
||||
};
|
||||
|
||||
if (kparams->use_dma) {
|
||||
work_queue_run(octx->ctx->work_queue, cumsum_thread_f32_dma, &cctx, n_threads);
|
||||
} else {
|
||||
work_queue_run(octx->ctx->work_queue, cumsum_thread_f32, &cctx, n_threads);
|
||||
}
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
int op_cumsum(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
switch (dst->type) {
|
||||
case HTP_TYPE_F32:
|
||||
return op_cumsum_f32(octx);
|
||||
default:
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef HTP_CUMSUM_OPS_H
|
||||
#define HTP_CUMSUM_OPS_H
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct htp_cumsum_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src0;
|
||||
size_t off_dst;
|
||||
size_t src_row_size_aligned;
|
||||
size_t dst_row_size_aligned;
|
||||
size_t src0_bytes_per_thread;
|
||||
size_t dst_bytes_per_thread;
|
||||
};
|
||||
|
||||
static inline void htp_cumsum_vtcm_layout_build(
|
||||
struct htp_cumsum_vtcm_layout * L,
|
||||
size_t src_row_size,
|
||||
size_t dst_row_size,
|
||||
uint32_t n_threads
|
||||
) {
|
||||
L->src_row_size_aligned = hex_round_up(src_row_size, 128);
|
||||
L->dst_row_size_aligned = hex_round_up(dst_row_size, 128);
|
||||
L->src0_bytes_per_thread = 2 * L->src_row_size_aligned;
|
||||
L->dst_bytes_per_thread = 2 * L->dst_row_size_aligned;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src0, L->src0_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, L->dst_bytes_per_thread * n_threads);
|
||||
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
struct htp_cumsum_kernel_params {
|
||||
uint32_t n_threads;
|
||||
uint32_t total_rows;
|
||||
uint32_t use_dma;
|
||||
uint32_t vtcm_size;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_cumsum_kernel_params) <= 128, "htp_cumsum_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_cumsum_kernel_params) <= 128, "htp_cumsum_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_CUMSUM_OPS_H
|
||||
@@ -0,0 +1,243 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <HAP_perf.h>
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "hex-common.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include "hvx-types.h"
|
||||
#include "hex-utils.h"
|
||||
#include "hvx-copy.h"
|
||||
#include "dma-queue.h"
|
||||
#include "diag-ops.h"
|
||||
|
||||
#define htp_diag_tensors_preamble \
|
||||
const struct htp_tensor * restrict src0 = octx->src[0]; \
|
||||
const struct htp_tensor * restrict dst = octx->dst; \
|
||||
\
|
||||
const uint32_t ne02 = src0->ne[2]; \
|
||||
\
|
||||
const uint32_t ne0 = dst->ne[0]; \
|
||||
const uint32_t ne1 = dst->ne[1]; \
|
||||
\
|
||||
const uint32_t nb02 = src0->nb[2]; \
|
||||
const uint32_t nb03 = src0->nb[3]; \
|
||||
\
|
||||
const uint32_t nb1 = dst->nb[1]; \
|
||||
const uint32_t nb2 = dst->nb[2]; \
|
||||
const uint32_t nb3 = dst->nb[3];
|
||||
|
||||
struct htp_diag_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_diag_kernel_params * kparams;
|
||||
size_t src_batch_size;
|
||||
size_t dst_row_size;
|
||||
size_t src_batch_size_aligned;
|
||||
size_t dst_row_size_aligned;
|
||||
uint32_t batches_per_thread;
|
||||
uint32_t total_batches;
|
||||
uint32_t batch_start;
|
||||
};
|
||||
|
||||
#define htp_diag_preamble \
|
||||
struct htp_diag_context * dctx = (struct htp_diag_context *) data; \
|
||||
struct htp_ops_context * octx = dctx->octx; \
|
||||
htp_diag_tensors_preamble;
|
||||
|
||||
static inline void hvx_diag_row_f32(const float * restrict src, float * restrict dst,
|
||||
uint32_t row_idx, uint32_t n) {
|
||||
hvx_splat_f32_a((uint8_t *) dst, 0.0f, n);
|
||||
dst[row_idx] = src[row_idx];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Per thread worker: DMA src fetch, compute in VTCM, DMA dst writeback
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void diag_thread_f32_dma(unsigned int nth, unsigned int ith, void * data) {
|
||||
htp_diag_preamble;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
|
||||
const uint32_t ib0 = dctx->batch_start + dctx->batches_per_thread * ith;
|
||||
const uint32_t ib1 = MIN(ib0 + dctx->batches_per_thread, dctx->batch_start + dctx->total_batches);
|
||||
|
||||
if (ib0 >= ib1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t src_batch_size = dctx->src_batch_size;
|
||||
const size_t dst_row_size = dctx->dst_row_size;
|
||||
const size_t src_batch_size_aligned = dctx->src_batch_size_aligned;
|
||||
const size_t dst_row_size_aligned = dctx->dst_row_size_aligned;
|
||||
|
||||
const dma_addr_t src_data = src0->data;
|
||||
const dma_addr_t dst_data = dst->data;
|
||||
|
||||
// 1 src buffer + 1 dst row buffer per thread in VTCM
|
||||
uint8_t * src_spad = octx->src0_spad.data + (ith * src_batch_size_aligned);
|
||||
uint8_t * dst_spad = octx->dst_spad.data + (ith * dst_row_size_aligned);
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
for (uint32_t ib = ib0; ib < ib1; ib++) {
|
||||
const uint32_t i3 = fastdiv(ib, &dctx->kparams->div_ne02);
|
||||
const uint32_t i2 = ib - i3 * ne02;
|
||||
|
||||
const dma_addr_t src_batch = src_data + i3 * nb03 + i2 * nb02;
|
||||
|
||||
// Fetch source vector into VTCM
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad, src_batch),
|
||||
src_batch_size_aligned, src_batch_size, src_batch_size, 1);
|
||||
dma_queue_flush(dma_q);
|
||||
|
||||
const float * src_spad_f32 = (const float *) src_spad;
|
||||
float * dst_spad_f32 = (float *) dst_spad;
|
||||
|
||||
for (uint32_t i1 = 0; i1 < ne1; i1++) {
|
||||
// Compute row in VTCM
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) (ib * ne1 + i1));
|
||||
hvx_diag_row_f32(src_spad_f32, dst_spad_f32, i1, ne0);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) (ib * ne1 + i1));
|
||||
|
||||
// Write completed row back to DDR
|
||||
const dma_addr_t dst_row = dst_data + i3 * nb3 + i2 * nb2 + i1 * nb1;
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(dst_row, dst_spad),
|
||||
dst_row_size, dst_row_size_aligned, dst_row_size, 1);
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
}
|
||||
|
||||
FARF(HIGH, "diag-f32-dma %d/%d: %ux%ux%ux%u (%u:%u) -> %ux%ux%ux%u\n",
|
||||
ith, nth, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], ib0, ib1,
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3]);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Per thread worker: Direct HVX (no DMA)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void diag_thread_f32(unsigned int nth, unsigned int ith, void * data) {
|
||||
htp_diag_preamble;
|
||||
|
||||
const uint8_t * src_data = (const uint8_t *) src0->data;
|
||||
uint8_t * dst_data = (uint8_t *) dst->data;
|
||||
|
||||
const uint32_t ib0 = dctx->batch_start + dctx->batches_per_thread * ith;
|
||||
const uint32_t ib1 = MIN(ib0 + dctx->batches_per_thread, dctx->batch_start + dctx->total_batches);
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ib0);
|
||||
|
||||
for (uint32_t ib = ib0; ib < ib1; ib++) {
|
||||
const uint32_t i3 = fastdiv(ib, &dctx->kparams->div_ne02);
|
||||
const uint32_t i2 = ib - i3 * ne02;
|
||||
|
||||
const float * restrict src_batch = (const float *)(src_data + i3 * nb03 + i2 * nb02);
|
||||
|
||||
for (uint32_t i1 = 0; i1 < ne1; i1++) {
|
||||
float * restrict dst_row = (float *)(dst_data + i3 * nb3 + i2 * nb2 + i1 * nb1);
|
||||
hvx_diag_row_f32(src_batch, dst_row, i1, ne0);
|
||||
}
|
||||
}
|
||||
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ib0);
|
||||
|
||||
FARF(HIGH, "diag-f32 %d/%d: %ux%ux%ux%u (%u:%u) -> %ux%ux%ux%u\n",
|
||||
ith, nth, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3], ib0, ib1,
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3]);
|
||||
}
|
||||
|
||||
int op_diag_f32(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const struct htp_diag_kernel_params * kparams =
|
||||
(const struct htp_diag_kernel_params *) octx->kernel_params;
|
||||
|
||||
const uint32_t total_batches = kparams->total_batches;
|
||||
|
||||
uint32_t batch_start = 0;
|
||||
uint32_t nbatches = total_batches;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const bool can_split = (kparams->can_split != 0) && htp_tensor_mdev_data_aligned(dst);
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(total_batches, can_split ? kparams->batches_per_chunk : 0, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
batch_start = range.start;
|
||||
nbatches = range.count;
|
||||
}
|
||||
|
||||
if (nbatches == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, MIN(kparams->n_threads, nbatches))) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
if (!kparams->use_dma && (htp_tensor_is_extended(src0) || htp_tensor_is_extended(dst))) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
const size_t src_batch_size = src0->ne[0] * sizeof(float);
|
||||
const size_t dst_row_size = dst->ne[0] * sizeof(float);
|
||||
|
||||
struct htp_diag_vtcm_layout layout = { 0 };
|
||||
if (kparams->use_dma) {
|
||||
htp_diag_vtcm_layout_build(&layout, src_batch_size, dst_row_size, n_threads);
|
||||
|
||||
octx->src0_spad.size_per_thread = layout.src0_bytes_per_thread;
|
||||
octx->dst_spad.size_per_thread = layout.dst_bytes_per_thread;
|
||||
octx->src0_spad.size = n_threads * layout.src0_bytes_per_thread;
|
||||
octx->dst_spad.size = n_threads * layout.dst_bytes_per_thread;
|
||||
octx->src0_spad.data = VTCM_LAYOUT_PTR(uint8_t, octx->ctx->vtcm_base, layout.off_src0);
|
||||
octx->dst_spad.data = VTCM_LAYOUT_PTR(uint8_t, octx->ctx->vtcm_base, layout.off_dst);
|
||||
octx->src0_spad.src = NULL;
|
||||
octx->dst_spad.src = NULL;
|
||||
}
|
||||
|
||||
struct htp_diag_context dctx = {
|
||||
.octx = octx,
|
||||
.kparams = kparams,
|
||||
.src_batch_size = src_batch_size,
|
||||
.dst_row_size = dst_row_size,
|
||||
.src_batch_size_aligned = layout.src_batch_size_aligned,
|
||||
.dst_row_size_aligned = layout.dst_row_size_aligned,
|
||||
.batches_per_thread = fastdiv(nbatches + n_threads - 1, &octx->n_threads_div),
|
||||
.total_batches = nbatches,
|
||||
.batch_start = batch_start,
|
||||
};
|
||||
|
||||
if (kparams->use_dma) {
|
||||
work_queue_run(octx->ctx->work_queue, diag_thread_f32_dma, &dctx, n_threads);
|
||||
} else {
|
||||
work_queue_run(octx->ctx->work_queue, diag_thread_f32, &dctx, n_threads);
|
||||
}
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
int op_diag(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
int err = HTP_STATUS_OK;
|
||||
|
||||
switch (dst->type) {
|
||||
case HTP_TYPE_F32:
|
||||
err = op_diag_f32(octx);
|
||||
break;
|
||||
default:
|
||||
err = HTP_STATUS_NO_SUPPORT;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef HTP_DIAG_OPS_H
|
||||
#define HTP_DIAG_OPS_H
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct htp_diag_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src0;
|
||||
size_t off_dst;
|
||||
size_t src_batch_size_aligned;
|
||||
size_t dst_row_size_aligned;
|
||||
size_t src0_bytes_per_thread;
|
||||
size_t dst_bytes_per_thread;
|
||||
};
|
||||
|
||||
static inline void htp_diag_vtcm_layout_build(
|
||||
struct htp_diag_vtcm_layout * L,
|
||||
size_t src_batch_size,
|
||||
size_t dst_row_size,
|
||||
uint32_t n_threads
|
||||
) {
|
||||
L->src_batch_size_aligned = hex_round_up(src_batch_size, 128);
|
||||
L->dst_row_size_aligned = hex_round_up(dst_row_size, 128);
|
||||
L->src0_bytes_per_thread = L->src_batch_size_aligned;
|
||||
L->dst_bytes_per_thread = L->dst_row_size_aligned;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src0, L->src0_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, L->dst_bytes_per_thread * n_threads);
|
||||
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
struct htp_diag_kernel_params {
|
||||
uint32_t n_threads;
|
||||
uint32_t total_batches;
|
||||
uint32_t use_dma;
|
||||
uint32_t can_split;
|
||||
uint32_t batches_per_chunk;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
struct fastdiv_values div_ne02;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_diag_kernel_params) <= 128, "htp_diag_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_diag_kernel_params) <= 128, "htp_diag_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_DIAG_OPS_H
|
||||
@@ -0,0 +1,169 @@
|
||||
#ifndef HTP_DMA_COPY_H
|
||||
#define HTP_DMA_COPY_H
|
||||
|
||||
// DDR<->DDR DMA copies of same-type, same-shape tensors with arbitrary strides.
|
||||
// Used by CPY for the copy itself and by CONCAT, which is two such copies into
|
||||
// two views of its destination. Every helper only pushes descriptors; the
|
||||
// caller flushes the queue when it needs the data.
|
||||
|
||||
#include "dma-queue.h"
|
||||
#include "hex-common.h"
|
||||
#include "htp-tensor.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Contiguous byte run, as 1d transfers of at most DMA_SAFE_CHUNK_SIZE each.
|
||||
static inline void dma_cpy_sametype_reshape_contig(dma_queue * dma_q,
|
||||
dma_addr_t dst,
|
||||
dma_addr_t src0,
|
||||
uint32_t total_bytes) {
|
||||
if (total_bytes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t max_chunk = DMA_SAFE_CHUNK_SIZE;
|
||||
while (total_bytes > 0) {
|
||||
const uint32_t chunk = MIN(total_bytes, max_chunk);
|
||||
if (!dma_queue_push(dma_q, dma_make_data(dst, src0), chunk, chunk, chunk, /*nrows=*/1)) {
|
||||
dma_queue_flush(dma_q);
|
||||
dma_queue_push(dma_q, dma_make_data(dst, src0), chunk, chunk, chunk, /*nrows=*/1);
|
||||
}
|
||||
dst += chunk;
|
||||
src0 += chunk;
|
||||
total_bytes -= chunk;
|
||||
}
|
||||
}
|
||||
|
||||
// One 2d transfer, split at the 16-bit nrows field.
|
||||
static inline void dma_cpy_push_2d_chunked(dma_queue * dma_q,
|
||||
dma_addr_t dst,
|
||||
dma_addr_t src,
|
||||
size_t dst_stride,
|
||||
size_t src_stride,
|
||||
size_t row_size,
|
||||
uint32_t nrows) {
|
||||
if (row_size == 0 || nrows == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (nrows > 0) {
|
||||
const uint32_t cur_rows = MIN(nrows, DMA_MAX_NROWS);
|
||||
if (!dma_queue_push(dma_q, dma_make_data(dst, src), dst_stride, src_stride, row_size, cur_rows)) {
|
||||
dma_queue_flush(dma_q);
|
||||
dma_queue_push(dma_q, dma_make_data(dst, src), dst_stride, src_stride, row_size, cur_rows);
|
||||
}
|
||||
dst += cur_rows * dst_stride;
|
||||
src += cur_rows * src_stride;
|
||||
nrows -= cur_rows;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy a range of rows [row_start, row_start + nrows) from src0 into dst:
|
||||
// same type, same ne[], any nb[] above dim 0, dim 0 dense on both sides (nb[0] == elem_size).
|
||||
static inline void dma_cpy_sametype_sameshape_range(dma_queue * dma_q,
|
||||
const struct htp_tensor * dst,
|
||||
const struct htp_tensor * src0,
|
||||
uint32_t elem_size,
|
||||
uint32_t row_start,
|
||||
uint32_t nrows) {
|
||||
if (nrows == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t ne00 = src0->ne[0];
|
||||
const uint32_t ne01 = src0->ne[1];
|
||||
const uint32_t ne02 = src0->ne[2];
|
||||
const uint32_t ne03 = src0->ne[3];
|
||||
|
||||
if (ne00 == 0 || ne01 == 0 || ne02 == 0 || ne03 == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t nb01 = src0->nb[1];
|
||||
const uint32_t nb02 = src0->nb[2];
|
||||
const uint32_t nb03 = src0->nb[3];
|
||||
|
||||
const uint32_t nb1 = dst->nb[1];
|
||||
const uint32_t nb2 = dst->nb[2];
|
||||
const uint32_t nb3 = dst->nb[3];
|
||||
|
||||
const bool contiguous = htp_tensor_is_contiguous(src0, elem_size) && htp_tensor_is_contiguous(dst, elem_size);
|
||||
|
||||
if (contiguous) {
|
||||
dma_cpy_sametype_reshape_contig(dma_q,
|
||||
dst->data + (dma_addr_t) row_start * ne00 * elem_size,
|
||||
src0->data + (dma_addr_t) row_start * ne00 * elem_size,
|
||||
nrows * ne00 * elem_size);
|
||||
return;
|
||||
}
|
||||
|
||||
// The single-descriptor path flattens (i01,i02,i03) into one row index, so every
|
||||
// row must sit at a constant stride: nb01 on the source, nb1 on the destination.
|
||||
// Walk the outer dims and require each to continue that progression. A dim of
|
||||
// extent 1 spans no rows, so it is skipped -- but its own stride must NOT then be
|
||||
// used to justify the next dim's stride, which is what comparing nb03 against
|
||||
// ne02*nb02 did: ggml leaves the stride of an extent-1 dim meaningless, so a view
|
||||
// could pass the check while its rows were nowhere near that stride.
|
||||
uint32_t exp_src = ne01 * nb01;
|
||||
uint32_t exp_dst = ne01 * nb1;
|
||||
bool contiguous_outer = true;
|
||||
if (ne02 != 1) {
|
||||
contiguous_outer = contiguous_outer && (nb02 == exp_src) && (nb2 == exp_dst);
|
||||
}
|
||||
exp_src *= ne02;
|
||||
exp_dst *= ne02;
|
||||
if (ne03 != 1) {
|
||||
contiguous_outer = contiguous_outer && (nb03 == exp_src) && (nb3 == exp_dst);
|
||||
}
|
||||
|
||||
if (contiguous_outer) {
|
||||
dma_cpy_push_2d_chunked(dma_q,
|
||||
dst->data + (dma_addr_t) row_start * nb1,
|
||||
src0->data + (dma_addr_t) row_start * nb01,
|
||||
nb1, nb01, ne00 * elem_size, nrows);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t ne02_ne01 = ne02 * ne01;
|
||||
uint32_t i03 = row_start / ne02_ne01;
|
||||
uint32_t rem = row_start - i03 * ne02_ne01;
|
||||
uint32_t i02 = rem / ne01;
|
||||
uint32_t i01 = rem - i02 * ne01;
|
||||
|
||||
dma_addr_t cur_dst = dst->data + (dma_addr_t) i01 * nb1 + (dma_addr_t) i02 * nb2 + (dma_addr_t) i03 * nb3;
|
||||
dma_addr_t cur_src0 = src0->data + (dma_addr_t) i01 * nb01 + (dma_addr_t) i02 * nb02 + (dma_addr_t) i03 * nb03;
|
||||
|
||||
uint32_t r = row_start;
|
||||
const uint32_t row_end = row_start + nrows;
|
||||
while (r < row_end) {
|
||||
uint32_t cur_rows = MIN(row_end - r, ne01 - i01);
|
||||
dma_cpy_push_2d_chunked(dma_q, cur_dst, cur_src0, nb1, nb01, ne00 * elem_size, cur_rows);
|
||||
r += cur_rows;
|
||||
i01 += cur_rows;
|
||||
if (i01 == ne01) {
|
||||
i01 = 0;
|
||||
if (++i02 == ne02) {
|
||||
i02 = 0;
|
||||
i03++;
|
||||
}
|
||||
cur_dst = dst->data + (dma_addr_t) i02 * nb2 + (dma_addr_t) i03 * nb3;
|
||||
cur_src0 = src0->data + (dma_addr_t) i02 * nb02 + (dma_addr_t) i03 * nb03;
|
||||
} else {
|
||||
cur_dst += cur_rows * nb1;
|
||||
cur_src0 += cur_rows * nb01;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy src0 into dst: same type, same ne[], any nb[] above dim 0, dim 0 dense on
|
||||
// both sides (nb[0] == elem_size).
|
||||
static inline void dma_cpy_sametype_sameshape(dma_queue * dma_q,
|
||||
const struct htp_tensor * dst,
|
||||
const struct htp_tensor * src0,
|
||||
uint32_t elem_size) {
|
||||
const uint32_t total_rows = src0->ne[1] * src0->ne[2] * src0->ne[3];
|
||||
dma_cpy_sametype_sameshape_range(dma_q, dst, src0, elem_size, 0, total_rows);
|
||||
}
|
||||
|
||||
#endif /* HTP_DMA_COPY_H */
|
||||
@@ -0,0 +1,212 @@
|
||||
#include "dma-queue.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
|
||||
static inline uint32_t pow2_ceil(uint32_t x) {
|
||||
if (x <= 1) {
|
||||
return 1;
|
||||
}
|
||||
int p = 2;
|
||||
x--;
|
||||
while (x >>= 1) {
|
||||
p <<= 1;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline uintptr_t align_up(uintptr_t addr, size_t align) {
|
||||
return (addr + align - 1) & ~(align - 1);
|
||||
}
|
||||
|
||||
static inline size_t dma_ring_sizeof(size_t capacity) {
|
||||
capacity = pow2_ceil(capacity);
|
||||
|
||||
size_t size_r = sizeof(dma_ring);
|
||||
size_t offset_desc = align_up(size_r, HEX_L2_LINE_SIZE);
|
||||
size_t size_desc = capacity * sizeof(dma_descriptor_2d);
|
||||
size_t offset_data = align_up(offset_desc + size_desc, HEX_L2_LINE_SIZE);
|
||||
size_t size_data = capacity * sizeof(dma_data);
|
||||
|
||||
return offset_data + size_data;
|
||||
}
|
||||
|
||||
static inline dma_ring * dma_ring_init(void * ptr, size_t capacity, struct htp_thread_trace * trace) {
|
||||
capacity = pow2_ceil(capacity);
|
||||
|
||||
size_t size_r = sizeof(dma_ring);
|
||||
size_t offset_desc = align_up(size_r, HEX_L2_LINE_SIZE);
|
||||
size_t size_desc = capacity * sizeof(dma_descriptor_2d);
|
||||
size_t offset_data = align_up(offset_desc + size_desc, HEX_L2_LINE_SIZE);
|
||||
|
||||
dma_ring * r = (dma_ring *) ptr;
|
||||
r->trace = trace;
|
||||
r->capacity = capacity;
|
||||
r->idx_mask = capacity - 1;
|
||||
r->push_idx = 0;
|
||||
r->pop_idx = 0;
|
||||
r->desc = (dma_descriptor_2d *) ((uintptr_t) ptr + offset_desc);
|
||||
r->data = (dma_data *) ((uintptr_t) ptr + offset_data);
|
||||
r->tail = &r->desc[capacity - 1];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
size_t dma_queue_sizeof(size_t capacity) {
|
||||
size_t size_q = sizeof(dma_queue);
|
||||
size_t offset_r0 = align_up(size_q, HEX_L2_LINE_SIZE);
|
||||
size_t size_r0 = dma_ring_sizeof(capacity);
|
||||
size_t offset_r1 = align_up(offset_r0 + size_r0, HEX_L2_LINE_SIZE);
|
||||
size_t size_r1 = dma_ring_sizeof(DMA_FALLBACK_CAPACITY);
|
||||
|
||||
return offset_r1 + size_r1;
|
||||
}
|
||||
|
||||
size_t dma_queue_alignof(void) {
|
||||
return HEX_L2_LINE_SIZE;
|
||||
}
|
||||
|
||||
dma_queue_t dma_queue_init(void * ptr, size_t capacity, struct htp_thread_trace * trace) {
|
||||
size_t total_size = dma_queue_sizeof(capacity);
|
||||
memset(ptr, 0, total_size);
|
||||
|
||||
dma_queue * q = (dma_queue *) ptr;
|
||||
|
||||
size_t size_q = sizeof(dma_queue);
|
||||
size_t offset_r0 = align_up(size_q, HEX_L2_LINE_SIZE);
|
||||
size_t size_r0 = dma_ring_sizeof(capacity);
|
||||
size_t offset_r1 = align_up(offset_r0 + size_r0, HEX_L2_LINE_SIZE);
|
||||
|
||||
q->ring0 = dma_ring_init((void *) ((uintptr_t) ptr + offset_r0), capacity, trace);
|
||||
q->ring1 = dma_ring_init((void *) ((uintptr_t) ptr + offset_r1), DMA_FALLBACK_CAPACITY, trace);
|
||||
q->alias = false;
|
||||
|
||||
FARF(HIGH, "dma-queue: capacity %u, unified memory size %zu\n", (unsigned) capacity, total_size);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
void dma_queue_free(dma_queue_t q) {
|
||||
(void) q;
|
||||
}
|
||||
|
||||
size_t dma_queue_alias_sizeof(void) {
|
||||
return sizeof(dma_queue);
|
||||
}
|
||||
|
||||
dma_queue_t dma_queue_alias_init(void * ptr, dma_queue_t main_q) {
|
||||
dma_queue * q = (dma_queue *) ptr;
|
||||
memset(q, 0, sizeof(dma_queue));
|
||||
|
||||
q->ring0 = main_q->ring0;
|
||||
q->ring1 = main_q->ring1;
|
||||
q->alias = true;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
void dma_queue_alias_free(dma_queue_t q) {
|
||||
(void) q;
|
||||
}
|
||||
|
||||
bool dma_queue_push_fallback_2d(dma_queue * q, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows) {
|
||||
dma_ring * r0 = q->ring0;
|
||||
dma_ring * r1 = q->ring1;
|
||||
|
||||
if (((r0->push_idx + 1) & r0->idx_mask) == r0->pop_idx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
r1->tail = r0->tail;
|
||||
|
||||
size_t rem_rows = nrows;
|
||||
dma_addr_t cur_dst = ddata.dst;
|
||||
dma_addr_t cur_src = ddata.src;
|
||||
|
||||
while (rem_rows > 0) {
|
||||
const uint32_t cur_rows = MIN(rem_rows, DMA_MAX_NROWS);
|
||||
dma_data cur_data = dma_make_data(cur_dst, cur_src);
|
||||
if (!dma_ring_push_single_2d(r1, cur_data, dst_stride, src_stride, row_size, cur_rows)) {
|
||||
dma_ring_flush(r1);
|
||||
dma_ring_push_single_2d(r1, cur_data, dst_stride, src_stride, row_size, cur_rows);
|
||||
}
|
||||
cur_dst += cur_rows * dst_stride;
|
||||
cur_src += cur_rows * src_stride;
|
||||
rem_rows -= cur_rows;
|
||||
}
|
||||
|
||||
dma_ring_flush(r1);
|
||||
r0->tail = r1->tail;
|
||||
|
||||
return dma_ring_push_single_2d(r0, ddata, 0, 0, 0, /*nrows=*/ 0);
|
||||
}
|
||||
|
||||
bool dma_queue_push_fallback_contig(dma_queue * q, dma_data ddata, size_t total) {
|
||||
dma_ring * r0 = q->ring0;
|
||||
dma_ring * r1 = q->ring1;
|
||||
|
||||
if (((r0->push_idx + 1) & r0->idx_mask) == r0->pop_idx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
r1->tail = r0->tail;
|
||||
|
||||
size_t rem_bytes = total;
|
||||
dma_addr_t cur_dst = ddata.dst;
|
||||
dma_addr_t cur_src = ddata.src;
|
||||
|
||||
while (rem_bytes > 0) {
|
||||
const uint32_t cur_bytes = MIN(rem_bytes, DMA_SAFE_CHUNK_SIZE);
|
||||
dma_data cur_data = dma_make_data(cur_dst, cur_src);
|
||||
if (!dma_ring_push_single_contig(r1, cur_data, cur_bytes)) {
|
||||
dma_ring_flush(r1);
|
||||
dma_ring_push_single_contig(r1, cur_data, cur_bytes);
|
||||
}
|
||||
cur_dst += cur_bytes;
|
||||
cur_src += cur_bytes;
|
||||
rem_bytes -= cur_bytes;
|
||||
}
|
||||
|
||||
dma_ring_flush(r1);
|
||||
r0->tail = r1->tail;
|
||||
|
||||
return dma_ring_push_single_1d(r0, ddata, /*size=*/ 0);
|
||||
}
|
||||
|
||||
#if __HVX_ARCH__ < 75
|
||||
|
||||
bool dma_queue_push_fallback_1d(dma_queue * q, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows) {
|
||||
dma_ring * r0 = q->ring0;
|
||||
dma_ring * r1 = q->ring1;
|
||||
|
||||
if (((r0->push_idx + 1) & r0->idx_mask) == r0->pop_idx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
r1->tail = r0->tail;
|
||||
|
||||
size_t rem_rows = nrows;
|
||||
dma_addr_t cur_dst = ddata.dst;
|
||||
dma_addr_t cur_src = ddata.src;
|
||||
|
||||
while (rem_rows > 0) {
|
||||
dma_data cur_data = dma_make_data(cur_dst, cur_src);
|
||||
if (!dma_ring_push_single_1d(r1, cur_data, row_size)) {
|
||||
dma_ring_flush(r1);
|
||||
dma_ring_push_single_1d(r1, cur_data, row_size);
|
||||
}
|
||||
cur_dst += dst_stride;
|
||||
cur_src += src_stride;
|
||||
rem_rows -= 1;
|
||||
}
|
||||
|
||||
dma_ring_flush(r1);
|
||||
r0->tail = r1->tail;
|
||||
|
||||
return dma_ring_push_single_1d(r0, ddata, /*size=*/ 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,525 @@
|
||||
#ifndef HTP_DMA_H
|
||||
#define HTP_DMA_H
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <hexagon_types.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "hex-utils.h"
|
||||
|
||||
#include "hex-profile.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Define the HW descriptor structs here since the ones in HexSDK are a bit out of date
|
||||
typedef struct dma_descriptor_1d_s {
|
||||
void * next;
|
||||
uint32_t size:24;
|
||||
uint32_t desc_size:2;
|
||||
uint32_t dst_comp:1;
|
||||
uint32_t src_comp:1;
|
||||
uint32_t dst_bypass:1;
|
||||
uint32_t src_bypass:1;
|
||||
uint32_t order:1;
|
||||
uint32_t done:1;
|
||||
uint32_t src;
|
||||
uint32_t dst;
|
||||
} dma_descriptor_1d;
|
||||
|
||||
#if __HVX_ARCH__ < 75
|
||||
|
||||
typedef struct dma_descriptor_2d_s {
|
||||
void * next;
|
||||
uint32_t reserved0:24;
|
||||
uint32_t desc_size:2;
|
||||
uint32_t dst_comp:1;
|
||||
uint32_t src_comp:1;
|
||||
uint32_t dst_bypass:1;
|
||||
uint32_t src_bypass:1;
|
||||
uint32_t order:1;
|
||||
uint32_t done:1;
|
||||
uint32_t src;
|
||||
uint32_t dst;
|
||||
uint32_t desc_type:8;
|
||||
uint32_t reserved1:24;
|
||||
uint32_t row_size:16;
|
||||
uint32_t nrows:16;
|
||||
uint32_t src_stride:16;
|
||||
uint32_t dst_stride:16;
|
||||
uint32_t src_offset:16;
|
||||
uint32_t dst_offset:16;
|
||||
} dma_descriptor_2d;
|
||||
|
||||
#else
|
||||
|
||||
typedef struct dma_descriptor_2d_s {
|
||||
void * next;
|
||||
uint32_t dst_stride:24;
|
||||
uint32_t desc_size:2;
|
||||
uint32_t dst_comp:1;
|
||||
uint32_t src_comp:1;
|
||||
uint32_t dst_bypass:1;
|
||||
uint32_t src_bypass:1;
|
||||
uint32_t order:1;
|
||||
uint32_t done:1;
|
||||
uint32_t src;
|
||||
uint32_t dst;
|
||||
uint32_t desc_type:8;
|
||||
#if __HVX_ARCH__ > 79
|
||||
uint32_t src_upper:8;
|
||||
uint32_t dst_upper:8;
|
||||
uint32_t allocation:2;
|
||||
uint32_t reserved0:2;
|
||||
uint32_t transform:4;
|
||||
#else
|
||||
uint32_t reserved0:24;
|
||||
#endif
|
||||
uint32_t row_size:24;
|
||||
uint32_t nrows_lo:8;
|
||||
uint32_t nrows_hi:8;
|
||||
uint32_t src_stride:24;
|
||||
uint32_t offset:24;
|
||||
uint32_t reserved1:8;
|
||||
} dma_descriptor_2d;
|
||||
|
||||
#endif
|
||||
|
||||
#if __HVX_ARCH__ > 79
|
||||
typedef uint64_t dma_addr_t;
|
||||
#else
|
||||
typedef uint32_t dma_addr_t;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
dma_addr_t dst;
|
||||
dma_addr_t src;
|
||||
} dma_data;
|
||||
|
||||
// Hardware descriptor field limits
|
||||
#define DMA_MAX_NROWS 0xFFFFu // 16-bit HW descriptor limit (65535)
|
||||
#define DMA_MAX_SIZE_16B 0xFFFFu // 16-bit HW descriptor limit for row_size (65535)
|
||||
#define DMA_MAX_STRIDE_16B 0xFFFFu // 16-bit HW descriptor limit for strides (65535)
|
||||
#define DMA_MAX_SIZE_24B 0x00FFFFFFu // 24-bit HW descriptor limit for row_size / 1D size (16MB - 1)
|
||||
#define DMA_MAX_STRIDE_24B 0x00FFFFFFu // 24-bit HW descriptor limit for strides (16MB - 1)
|
||||
#define DMA_SAFE_CHUNK_SIZE 0x00F00000u // ~15MB safe contiguous chunk size
|
||||
|
||||
#if __HVX_ARCH__ < 75
|
||||
#define DMA_MAX_2D_ROW_SIZE DMA_MAX_SIZE_16B
|
||||
#define DMA_MAX_2D_STRIDE DMA_MAX_STRIDE_16B
|
||||
#else
|
||||
#define DMA_MAX_2D_ROW_SIZE DMA_MAX_SIZE_24B
|
||||
#define DMA_MAX_2D_STRIDE DMA_MAX_STRIDE_24B
|
||||
#endif
|
||||
|
||||
#define DMA_FALLBACK_CAPACITY 16u // descriptors in secondary fallback ring
|
||||
|
||||
typedef struct dma_ring_s dma_ring;
|
||||
struct dma_ring_s {
|
||||
dma_descriptor_2d * desc; // descriptor pointers
|
||||
dma_descriptor_2d * tail; // tail pointer
|
||||
dma_data * data; // dst/src data
|
||||
uint32_t push_idx;
|
||||
uint32_t pop_idx;
|
||||
uint32_t capacity;
|
||||
uint32_t idx_mask;
|
||||
struct htp_thread_trace * trace;
|
||||
};
|
||||
|
||||
typedef struct dma_queue_s dma_queue;
|
||||
typedef dma_queue * dma_queue_t;
|
||||
|
||||
struct dma_queue_s {
|
||||
dma_ring * ring0; // Main descriptor ring state
|
||||
dma_ring * ring1; // Secondary fallback descriptor ring state
|
||||
bool alias; // When set, dma_queue_delete will not free the ring
|
||||
};
|
||||
|
||||
size_t dma_queue_sizeof(size_t capacity);
|
||||
size_t dma_queue_alignof(void);
|
||||
dma_queue_t dma_queue_init(void * ptr, size_t capacity, struct htp_thread_trace * trace);
|
||||
void dma_queue_free(dma_queue_t q);
|
||||
|
||||
size_t dma_queue_alias_sizeof(void);
|
||||
dma_queue_t dma_queue_alias_init(void * ptr, dma_queue_t main_q);
|
||||
void dma_queue_alias_free(dma_queue_t q);
|
||||
|
||||
bool dma_queue_push_fallback_2d(dma_queue * q, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows);
|
||||
bool dma_queue_push_fallback_contig(dma_queue * q, dma_data ddata, size_t total);
|
||||
#if __HVX_ARCH__ < 75
|
||||
bool dma_queue_push_fallback_1d(dma_queue * q, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows);
|
||||
#endif
|
||||
|
||||
// TODO: technically we don't need these and could use Q6_dmstart/wait/etc instead
|
||||
// but those do not seem to always compiler properly.
|
||||
static inline void dmstart(void * next) {
|
||||
asm volatile(" release(%0):at" : : "r"(next));
|
||||
asm volatile(" dmstart(%0)" : : "r"(next));
|
||||
}
|
||||
|
||||
static inline void dmlink(void * cur, void * next) {
|
||||
asm volatile(" release(%0):at" : : "r"(next));
|
||||
asm volatile(" dmlink(%0, %1)" : : "r"(cur), "r"(next));
|
||||
}
|
||||
|
||||
static inline unsigned int dmpoll(void) {
|
||||
unsigned int ret = 0;
|
||||
asm volatile(" %0 = dmpoll" : "=r"(ret) : : "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline unsigned int dmwait(void) {
|
||||
unsigned int ret = 0;
|
||||
asm volatile(" %0 = dmwait" : "=r"(ret) : : "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline dma_data dma_make_data_impl(dma_addr_t dst, dma_addr_t src)
|
||||
{
|
||||
dma_data d = { dst, src };
|
||||
return d;
|
||||
}
|
||||
|
||||
#define dma_make_data(dst, src) dma_make_data_impl((dma_addr_t) (dst), (dma_addr_t) (src))
|
||||
|
||||
static inline bool dma_ring_push_single_1d(dma_ring * r, dma_data ddata, size_t size) {
|
||||
#if __HVX_ARCH__ > 79
|
||||
assert(!((ddata.src | ddata.dst) >> 32) || size == 0);
|
||||
#endif
|
||||
|
||||
if (((r->push_idx + 1) & r->idx_mask) == r->pop_idx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dma_descriptor_1d * desc = (dma_descriptor_1d *) &r->desc[r->push_idx];
|
||||
desc->src = (uint32_t) ddata.src;
|
||||
desc->dst = (uint32_t) ddata.dst;
|
||||
desc->size = size;
|
||||
|
||||
r->data[r->push_idx] = ddata;
|
||||
|
||||
htp_trace_event_start(r->trace, HTP_TRACE_EVT_DMA, r->push_idx);
|
||||
|
||||
if (size) {
|
||||
desc->next = NULL;
|
||||
desc->desc_size = 0; // 1D mode
|
||||
desc->src_bypass = 1;
|
||||
desc->dst_bypass = 1;
|
||||
desc->order = 0;
|
||||
desc->done = 0;
|
||||
|
||||
dmlink(r->tail, desc);
|
||||
r->tail = (dma_descriptor_2d *) desc;
|
||||
} else {
|
||||
desc->desc_size = 0;
|
||||
desc->done = 1;
|
||||
}
|
||||
|
||||
r->push_idx = (r->push_idx + 1) & r->idx_mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool dma_ring_push_single_2d(dma_ring * r, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows) {
|
||||
#if __HVX_ARCH__ > 79
|
||||
assert(!((ddata.src | ddata.dst) >> 40) || nrows == 0);
|
||||
const uint32_t src_hi = (uint32_t) (ddata.src >> 32);
|
||||
const uint32_t dst_hi = (uint32_t) (ddata.dst >> 32);
|
||||
const bool is_ext = (src_hi | dst_hi) != 0;
|
||||
#endif
|
||||
|
||||
if (((r->push_idx + 1) & r->idx_mask) == r->pop_idx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dma_descriptor_2d * desc = &r->desc[r->push_idx];
|
||||
|
||||
desc->next = NULL;
|
||||
desc->reserved1 = 0;
|
||||
desc->desc_size = 1; // 2d mode
|
||||
desc->src_bypass = 1;
|
||||
desc->dst_bypass = 1;
|
||||
desc->src_comp = 0;
|
||||
desc->dst_comp = 0;
|
||||
desc->order = 0;
|
||||
desc->done = 0;
|
||||
desc->src_stride = src_stride;
|
||||
desc->dst_stride = dst_stride;
|
||||
desc->src = (uint32_t) ddata.src;
|
||||
desc->dst = (uint32_t) ddata.dst;
|
||||
desc->row_size = row_size;
|
||||
|
||||
#if __HVX_ARCH__ < 75
|
||||
desc->reserved0 = 0;
|
||||
desc->desc_type = 0; // 2d (16-bit) mode
|
||||
desc->nrows = nrows;
|
||||
desc->src_offset = 0;
|
||||
desc->dst_offset = 0;
|
||||
#else
|
||||
#if __HVX_ARCH__ > 79
|
||||
desc->src_upper = src_hi;
|
||||
desc->dst_upper = dst_hi;
|
||||
desc->allocation = 0;
|
||||
desc->reserved0 = 0;
|
||||
desc->transform = 0;
|
||||
desc->desc_type = is_ext ? 10 : 9; // 2d 40-bit or 24-bit mode
|
||||
#else
|
||||
desc->reserved0 = 0;
|
||||
desc->desc_type = 9; // 2d (24-bit) mode
|
||||
#endif
|
||||
desc->nrows_lo = (nrows & 0xff);
|
||||
desc->nrows_hi = (nrows >> 8);
|
||||
desc->offset = 0;
|
||||
#endif
|
||||
|
||||
r->data[r->push_idx] = ddata;
|
||||
|
||||
htp_trace_event_start(r->trace, HTP_TRACE_EVT_DMA, r->push_idx);
|
||||
|
||||
if (nrows) {
|
||||
dmlink(r->tail, desc);
|
||||
r->tail = desc;
|
||||
} else {
|
||||
desc->done = 1;
|
||||
}
|
||||
|
||||
r->push_idx = (r->push_idx + 1) & r->idx_mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if __HVX_ARCH__ < 75
|
||||
static inline bool dma_ring_push_single_contig(dma_ring * r, dma_data ddata, size_t size) {
|
||||
return dma_ring_push_single_1d(r, ddata, size);
|
||||
}
|
||||
#else
|
||||
static inline bool dma_ring_push_single_contig(dma_ring * r, dma_data ddata, size_t size) {
|
||||
return dma_ring_push_single_2d(r, ddata, size, size, size, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline dma_data dma_ring_pop(dma_ring * r) {
|
||||
dma_data ddata = { 0 };
|
||||
|
||||
if (r->push_idx == r->pop_idx) {
|
||||
return ddata;
|
||||
}
|
||||
|
||||
ddata = r->data[r->pop_idx];
|
||||
|
||||
volatile dma_descriptor_2d * desc = &r->desc[r->pop_idx];
|
||||
|
||||
// Wait for desc to complete
|
||||
if (!desc->done) {
|
||||
// FARF(ALWAYS, "dma-poll: idx %u dst %p src %p", r->pop_idx, ddata.dst, ddata.src);
|
||||
while (!desc->done) {
|
||||
dmpoll();
|
||||
}
|
||||
}
|
||||
|
||||
htp_trace_event_stop(r->trace, HTP_TRACE_EVT_DMA, r->pop_idx);
|
||||
|
||||
r->pop_idx = (r->pop_idx + 1) & r->idx_mask;
|
||||
return ddata;
|
||||
}
|
||||
|
||||
static inline dma_data dma_ring_pop_nowait(dma_ring * r) {
|
||||
dma_data ddata = { 0 };
|
||||
|
||||
if (r->push_idx == r->pop_idx) {
|
||||
return ddata;
|
||||
}
|
||||
|
||||
ddata = r->data[r->pop_idx];
|
||||
|
||||
htp_trace_event_stop(r->trace, HTP_TRACE_EVT_DMA, r->pop_idx);
|
||||
|
||||
r->pop_idx = (r->pop_idx + 1) & r->idx_mask;
|
||||
return ddata;
|
||||
}
|
||||
|
||||
static inline bool dma_ring_empty(dma_ring * r) {
|
||||
return r->push_idx == r->pop_idx;
|
||||
}
|
||||
|
||||
static inline void dma_ring_flush(dma_ring * r) {
|
||||
while (!dma_ring_empty(r)) {
|
||||
dma_ring_pop(r);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t dma_ring_depth(dma_ring * r) {
|
||||
return (r->push_idx - r->pop_idx) & r->idx_mask;
|
||||
}
|
||||
|
||||
static inline uint32_t dma_ring_capacity(dma_ring * r) {
|
||||
return r->capacity;
|
||||
}
|
||||
|
||||
static inline bool dma_queue_push_single_1d(dma_queue * q, dma_data ddata, size_t size) {
|
||||
return dma_ring_push_single_1d(q->ring0, ddata, size);
|
||||
}
|
||||
|
||||
static inline bool dma_queue_push_single_2d(dma_queue * q, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows) {
|
||||
return dma_ring_push_single_2d(q->ring0, ddata, dst_stride, src_stride, row_size, nrows);
|
||||
}
|
||||
|
||||
static inline dma_data dma_queue_pop(dma_queue * q) {
|
||||
return dma_ring_pop(q->ring0);
|
||||
}
|
||||
|
||||
static inline dma_data dma_queue_pop_nowait(dma_queue * q) {
|
||||
return dma_ring_pop_nowait(q->ring0);
|
||||
}
|
||||
|
||||
static inline bool dma_queue_empty(dma_queue * q) {
|
||||
return dma_ring_empty(q->ring0);
|
||||
}
|
||||
|
||||
static inline void dma_queue_flush(dma_queue * q) {
|
||||
dma_ring_flush(q->ring0);
|
||||
}
|
||||
|
||||
static inline uint32_t dma_queue_depth(dma_queue * q) {
|
||||
return dma_ring_depth(q->ring0);
|
||||
}
|
||||
|
||||
static inline uint32_t dma_queue_capacity(dma_queue * q) {
|
||||
return dma_ring_capacity(q->ring0);
|
||||
}
|
||||
|
||||
static inline bool dma_queue_push(dma_queue * q, dma_data ddata, size_t dst_stride, size_t src_stride, size_t row_size, size_t nrows) {
|
||||
if (__builtin_expect(nrows == 0, 0)) {
|
||||
return dma_ring_push_single_1d(q->ring0, ddata, 0);
|
||||
}
|
||||
|
||||
// 1. Hot path: Contiguous or single-row (80-90% of calls)
|
||||
if (nrows == 1 || !((row_size ^ src_stride) | (row_size ^ dst_stride))) {
|
||||
const size_t total = row_size * nrows;
|
||||
if (__builtin_expect(total <= DMA_MAX_SIZE_24B, 1)) {
|
||||
return dma_ring_push_single_contig(q->ring0, ddata, total);
|
||||
}
|
||||
return dma_queue_push_fallback_contig(q, ddata, total);
|
||||
}
|
||||
|
||||
// 2. Hot path: Standard strided 2D (10-20% of calls)
|
||||
if (__builtin_expect(nrows <= DMA_MAX_NROWS &&
|
||||
(row_size | src_stride | dst_stride) <= DMA_MAX_2D_ROW_SIZE, 1)) {
|
||||
return dma_ring_push_single_2d(q->ring0, ddata, dst_stride, src_stride, row_size, nrows);
|
||||
}
|
||||
|
||||
// 3. Cold path: Descriptor chunking fallbacks (< 0.1%)
|
||||
#if __HVX_ARCH__ < 75
|
||||
if (row_size <= DMA_MAX_SIZE_16B && (src_stride | dst_stride) <= DMA_MAX_STRIDE_16B) {
|
||||
return dma_queue_push_fallback_2d(q, ddata, dst_stride, src_stride, row_size, nrows);
|
||||
}
|
||||
return dma_queue_push_fallback_1d(q, ddata, dst_stride, src_stride, row_size, nrows);
|
||||
#else
|
||||
return dma_queue_push_fallback_2d(q, ddata, dst_stride, src_stride, row_size, nrows);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void dma_sync_read(dma_queue * dma_q, void * dst, dma_addr_t src, size_t bytes) {
|
||||
const uint32_t b = (uint32_t) bytes;
|
||||
if (b > 0) {
|
||||
dma_queue_push(dma_q, dma_make_data(dst, src), b, b, b, 1);
|
||||
dma_queue_pop(dma_q);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dma_sync_write(dma_queue * dma_q, dma_addr_t dst, const void * src, size_t bytes) {
|
||||
const uint32_t b = (uint32_t) bytes;
|
||||
if (b > 0) {
|
||||
dma_queue_push(dma_q, dma_make_data(dst, src), b, b, b, 1);
|
||||
dma_queue_pop(dma_q);
|
||||
}
|
||||
}
|
||||
|
||||
#define DMA_CACHE_MAX_SIZE 128U
|
||||
|
||||
// Fully assoc LRU cache
|
||||
typedef struct __attribute__((aligned(128))) {
|
||||
uint8_t *base;
|
||||
uint32_t line_size;
|
||||
uint32_t capacity;
|
||||
dma_addr_t src[DMA_CACHE_MAX_SIZE];
|
||||
uint16_t age[DMA_CACHE_MAX_SIZE];
|
||||
} dma_cache_fa;
|
||||
|
||||
static inline void dma_cache_fa_init(dma_cache_fa *c, uint8_t *base, uint32_t line_size, uint32_t capacity)
|
||||
{
|
||||
c->capacity = (capacity > DMA_CACHE_MAX_SIZE) ? DMA_CACHE_MAX_SIZE : capacity;
|
||||
c->base = base;
|
||||
c->line_size = line_size;
|
||||
|
||||
for (unsigned i=0; i < c->capacity; i++) {
|
||||
c->src[i] = 0;
|
||||
c->age[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool dma_cache_fa_push(dma_queue *q, dma_cache_fa *c, dma_addr_t src_addr, uint32_t dst_stride, uint32_t src_stride, uint32_t row_size, uint32_t nrows)
|
||||
{
|
||||
uint32_t o_idx = 0;
|
||||
uint16_t o_age = 0;
|
||||
uint8_t * dst = 0;
|
||||
|
||||
for (unsigned i=0; i < c->capacity; i++) {
|
||||
if (c->src[i] == src_addr) {
|
||||
c->age[i] = 0;
|
||||
dst = c->base + (i * c->line_size); nrows = 0; // dummy dma
|
||||
} else {
|
||||
c->age[i]++;
|
||||
if (c->age[i] > o_age) { o_age = c->age[i]; o_idx = i; }
|
||||
}
|
||||
}
|
||||
if (!dst) {
|
||||
c->age[o_idx] = 0;
|
||||
c->src[o_idx] = src_addr;
|
||||
dst = c->base + o_idx * c->line_size; // normal nrows dma
|
||||
return dma_queue_push(q, dma_make_data(dst, src_addr), dst_stride, src_stride, row_size, nrows);
|
||||
}
|
||||
|
||||
return dma_queue_push_single_1d(q, dma_make_data(dst, src_addr), 0);
|
||||
}
|
||||
|
||||
// Direct mapped cache
|
||||
typedef struct __attribute__((aligned(128))) {
|
||||
uint8_t *base;
|
||||
uint32_t line_size;
|
||||
uint32_t capacity;
|
||||
uint32_t idx_mask;
|
||||
dma_addr_t src[DMA_CACHE_MAX_SIZE];
|
||||
} dma_cache_dm;
|
||||
|
||||
static inline void dma_cache_dm_init(dma_cache_dm *c, uint8_t *base, uint32_t line_size, uint32_t capacity)
|
||||
{
|
||||
c->capacity = (capacity > DMA_CACHE_MAX_SIZE) ? DMA_CACHE_MAX_SIZE : capacity;
|
||||
c->idx_mask = c->capacity - 1;
|
||||
c->base = base;
|
||||
c->line_size = line_size;
|
||||
|
||||
for (unsigned i=0; i < c->capacity; i++) {
|
||||
c->src[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool dma_cache_dm_push(dma_queue *q, dma_cache_dm *c, uint32_t slot, dma_addr_t src_addr, uint32_t dst_stride, uint32_t src_stride, uint32_t row_size, uint32_t nrows)
|
||||
{
|
||||
const uint32_t i = slot & c->idx_mask;
|
||||
uint8_t * dst = c->base + (i * c->line_size);
|
||||
|
||||
if (c->src[i] == src_addr) {
|
||||
return dma_queue_push_single_1d(q, dma_make_data(dst, src_addr), 0); // dummy dma
|
||||
}
|
||||
|
||||
c->src[i] = src_addr;
|
||||
return dma_queue_push(q, dma_make_data(dst, src_addr), dst_stride, src_stride, row_size, nrows);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* HTP_DMA_H */
|
||||
@@ -0,0 +1,162 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-profile.h"
|
||||
|
||||
#include "hvx-copy.h"
|
||||
#include "hvx-utils.h"
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "fill-ops.h"
|
||||
|
||||
// ggml op_params layout for FILL:
|
||||
// op_params[0] (as float) - the scalar fill value
|
||||
|
||||
#define fill_preamble \
|
||||
const struct htp_tensor * dst = octx->dst; \
|
||||
\
|
||||
const uint32_t ne0 = dst->ne[0]; \
|
||||
const uint32_t ne1 = dst->ne[1]; \
|
||||
const uint32_t ne2 = dst->ne[2]; \
|
||||
const uint32_t ne3 = dst->ne[3]; \
|
||||
\
|
||||
const uint32_t nb1 = dst->nb[1]; \
|
||||
const uint32_t nb2 = dst->nb[2]; \
|
||||
const uint32_t nb3 = dst->nb[3];
|
||||
|
||||
struct htp_fill_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_fill_kernel_params * kparams;
|
||||
uint32_t nrows_per_thread;
|
||||
uint32_t total_rows; // ne1 * ne2 * ne3
|
||||
uint32_t row_start;
|
||||
HVX_Vector splat_vec;
|
||||
uint32_t elem_size;
|
||||
};
|
||||
|
||||
static void fill_thread_contiguous(unsigned int nth, unsigned int ith, void * data) {
|
||||
const struct htp_fill_context * fctx = (const struct htp_fill_context *) data;
|
||||
struct htp_ops_context * octx = fctx->octx;
|
||||
fill_preamble;
|
||||
|
||||
// Parallelise over the flat row index spanning ne1*ne2*ne3
|
||||
const uint32_t ir0 = fctx->row_start + fctx->nrows_per_thread * ith;
|
||||
const uint32_t ir1 = MIN(ir0 + fctx->nrows_per_thread, fctx->row_start + fctx->total_rows);
|
||||
|
||||
if (ir0 >= ir1) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir0);
|
||||
|
||||
const uint32_t elem_start = ir0 * ne0;
|
||||
const uint32_t elem_end = ir1 * ne0;
|
||||
uint8_t * dst_ptr = (uint8_t *) dst->data + elem_start * fctx->elem_size;
|
||||
hvx_splat_u(dst_ptr, fctx->splat_vec, elem_end - elem_start, fctx->elem_size);
|
||||
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir1);
|
||||
FARF(HIGH, "fill-contig %u/%u: rows %u:%u\n", ith, nth, ir0, ir1);
|
||||
}
|
||||
|
||||
static void fill_thread_strided(unsigned int nth, unsigned int ith, void * data) {
|
||||
const struct htp_fill_context * fctx = (const struct htp_fill_context *) data;
|
||||
struct htp_ops_context * octx = fctx->octx;
|
||||
const struct htp_fill_kernel_params * kparams = fctx->kparams;
|
||||
fill_preamble;
|
||||
|
||||
// Parallelise over the flat row index spanning ne1*ne2*ne3
|
||||
const uint32_t ir0 = fctx->row_start + fctx->nrows_per_thread * ith;
|
||||
const uint32_t ir1 = MIN(ir0 + fctx->nrows_per_thread, fctx->row_start + fctx->total_rows);
|
||||
|
||||
if (ir0 >= ir1) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir0);
|
||||
|
||||
for (uint32_t ir = ir0; ir < ir1; ++ir) {
|
||||
const uint32_t i3 = fastdiv(ir, &kparams->div_ne1_ne2);
|
||||
const uint32_t rem = ir - i3 * (ne1 * ne2);
|
||||
const uint32_t i2 = fastdiv(rem, &kparams->div_ne1);
|
||||
const uint32_t i1 = rem - i2 * ne1;
|
||||
uint8_t * dst_ptr = (uint8_t *) dst->data + i1*nb1 + i2*nb2 + i3*nb3;
|
||||
hvx_splat_u(dst_ptr, fctx->splat_vec, ne0, fctx->elem_size);
|
||||
}
|
||||
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir1);
|
||||
FARF(HIGH, "fill-strided %u/%u: rows %u:%u\n", ith, nth, ir0, ir1);
|
||||
}
|
||||
|
||||
int op_fill(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const struct htp_fill_kernel_params * kparams =
|
||||
(const struct htp_fill_kernel_params *) octx->kernel_params;
|
||||
|
||||
if (htp_tensor_is_extended(dst)) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
uint32_t row_start = 0;
|
||||
uint32_t nrows = kparams->total_rows;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(kparams->total_rows, htp_tensor_mdev_data_aligned(dst) ? kparams->rows_per_chunk : 0, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
row_start = range.start;
|
||||
nrows = range.count;
|
||||
}
|
||||
|
||||
if (nrows == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, MIN(kparams->n_threads, nrows))) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
FARF(HIGH, "fill: (%ux%ux%ux%u) type=%u ktype=%u\n",
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3], dst->type, kparams->kernel_type);
|
||||
|
||||
float val_f32 = 0.f;
|
||||
memcpy(&val_f32, &octx->op_params[0], sizeof(float));
|
||||
|
||||
struct htp_fill_context fctx = {
|
||||
.octx = octx,
|
||||
.kparams = kparams,
|
||||
.nrows_per_thread = fastdiv(nrows + n_threads - 1, &octx->n_threads_div),
|
||||
.total_rows = nrows,
|
||||
.row_start = row_start,
|
||||
.elem_size = kparams->elem_size,
|
||||
};
|
||||
|
||||
switch (dst->type) {
|
||||
case HTP_TYPE_F32:
|
||||
fctx.splat_vec = hvx_vec_splat_f32(val_f32);
|
||||
break;
|
||||
case HTP_TYPE_F16:
|
||||
fctx.splat_vec = hvx_vec_splat_f16((_Float16) val_f32);
|
||||
break;
|
||||
default:
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
work_queue_func_t worker = (kparams->kernel_type == HTP_FILL_KERNEL_CONTIGUOUS)
|
||||
? fill_thread_contiguous
|
||||
: fill_thread_strided;
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, worker, &fctx, n_threads);
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef HTP_FILL_OPS_H
|
||||
#define HTP_FILL_OPS_H
|
||||
|
||||
#include "hex-fastdiv.h"
|
||||
#include <stdint.h>
|
||||
|
||||
enum htp_fill_kernel_type {
|
||||
HTP_FILL_KERNEL_CONTIGUOUS = 0,
|
||||
HTP_FILL_KERNEL_STRIDED = 1,
|
||||
};
|
||||
|
||||
struct htp_fill_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint32_t n_threads;
|
||||
uint32_t total_rows;
|
||||
uint32_t elem_size;
|
||||
uint32_t rows_per_chunk;
|
||||
|
||||
struct fastdiv_values div_ne1;
|
||||
struct fastdiv_values div_ne1_ne2;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_fill_kernel_params) <= 128, "htp_fill_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_fill_kernel_params) <= 128, "htp_fill_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_FILL_OPS_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,410 @@
|
||||
#ifndef HTP_FLASH_ATTN_OPS_H
|
||||
#define HTP_FLASH_ATTN_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hex-fastdiv.h"
|
||||
#include "hex-common.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Tile constants (mirrored from hmx-utils.h for use on host side if needed)
|
||||
#define HTP_FA_HMX_TILE_SIZE 2048
|
||||
#define HMX_FP16_TILE_SIZE 2048
|
||||
#define HMX_FP16_TILE_N_ROWS 32
|
||||
#define HMX_FP16_TILE_N_COLS 32
|
||||
#define HMX_FP16_TILE_N_ELMS 1024
|
||||
|
||||
#define HVX_FA_DMA_CACHE_SIZE 128
|
||||
#define HMX_FA_DMA_CACHE_SIZE 4
|
||||
|
||||
|
||||
#define HTP_FA_M_INITIAL_VAL -10000.0f
|
||||
|
||||
enum htp_fa_kernel_type {
|
||||
HTP_FA_KERNEL_UNSUPPORTED = 0,
|
||||
HTP_FA_KERNEL_HVX,
|
||||
HTP_FA_KERNEL_HMX
|
||||
};
|
||||
|
||||
// Multicore: gcd(n_kv_heads, N) teams own the KV heads, team members share the Q-blocks
|
||||
#define HTP_FA_FLAG_TEAM_SPLIT 0x01
|
||||
|
||||
static inline void htp_fa_mdev_teams(uint32_t n_kv_heads, uint32_t mdev_count, bool head_split,
|
||||
uint32_t * n_teams, uint32_t * team_size) {
|
||||
const uint32_t t = (head_split && n_kv_heads > 0 && mdev_count > 1) ? hex_gcd_u32(n_kv_heads, mdev_count) : 1;
|
||||
*n_teams = t;
|
||||
*team_size = mdev_count > 1 ? mdev_count / t : 1;
|
||||
}
|
||||
|
||||
struct htp_fa_kernel_params {
|
||||
uint8_t kernel_type; // enum htp_fa_kernel_type
|
||||
uint8_t head_split; // 1 = partition by KV heads in multicore, 0 = token partition
|
||||
uint8_t flags; // HTP_FA_FLAG_*
|
||||
uint8_t n_threads; // Number of threads to run
|
||||
|
||||
// Common parameters
|
||||
uint16_t Br;
|
||||
uint16_t Bc;
|
||||
uint16_t n_kv_blocks; // also HVX's n_blocks
|
||||
uint16_t G; // GQA factor (n_heads / n_kv_heads)
|
||||
|
||||
float scale;
|
||||
float max_bias;
|
||||
float logit_softcap;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
float m0;
|
||||
float m1;
|
||||
uint32_t n_head_log2;
|
||||
|
||||
struct fastdiv_values src3_div2;
|
||||
struct fastdiv_values src3_div3;
|
||||
|
||||
struct fastdiv_values broadcast_rk2;
|
||||
struct fastdiv_values broadcast_rk3;
|
||||
struct fastdiv_values broadcast_rv2;
|
||||
struct fastdiv_values broadcast_rv3;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t g_br;
|
||||
int32_t mask_broadcast;
|
||||
int32_t pipeline;
|
||||
struct fastdiv_values div_G;
|
||||
} hmx;
|
||||
struct {
|
||||
uint32_t size_q_row_padded;
|
||||
uint32_t size_k_row_padded;
|
||||
uint32_t size_v_row_padded;
|
||||
struct fastdiv_values src0_div21;
|
||||
struct fastdiv_values src0_div1;
|
||||
} hvx;
|
||||
} u;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_fa_kernel_params) <= 128, "htp_fa_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_fa_kernel_params) <= 128, "htp_fa_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
// VTCM region layout for the HMX flash-attention kernel.
|
||||
//
|
||||
// Single source of truth for both the host (which needs the total size to pick a
|
||||
// (Br, Bc) tiling that fits the VTCM budget) and the device (which needs the actual
|
||||
// byte offsets to place each scratch buffer). Building the layout once and reading
|
||||
// offsets/total from it makes host estimate and device allocation impossible to
|
||||
// desync -- previously they were duplicated formulas in two files and drifted.
|
||||
//
|
||||
// All fields are byte offsets / byte sizes -- no HVX_Vector type is named here so the
|
||||
// header stays host-includable. The device casts (base + off_*) to the proper type.
|
||||
// An offset of 0 marks a region that is not allocated for this configuration (only
|
||||
// off_v_tiles[1], which exists only when pipelining); the device sets such pointers NULL.
|
||||
struct hmx_fa_vtcm_layout {
|
||||
// Byte offsets from vtcm_base for each region.
|
||||
size_t off_q_tiles;
|
||||
size_t off_q_dma;
|
||||
size_t off_o_tiles[2];
|
||||
size_t off_k_fp16[2];
|
||||
size_t off_v_fp16[2];
|
||||
size_t off_k_tiles[2];
|
||||
size_t off_v_tiles[2];
|
||||
size_t off_s_tiles[2];
|
||||
size_t off_p_tiles[2];
|
||||
size_t off_d_tiles[2];
|
||||
size_t off_d_inv_l;
|
||||
size_t off_m_vec;
|
||||
size_t off_l_vec;
|
||||
size_t off_s_rowmax;
|
||||
size_t off_p_rowsum;
|
||||
size_t off_row_bufs;
|
||||
size_t off_hmx_scales_id;
|
||||
size_t off_hmx_scales_qk;
|
||||
size_t off_mask_buf;
|
||||
size_t off_slopes;
|
||||
size_t off_sinks;
|
||||
|
||||
// Region byte sizes reused by the device at runtime (not just for allocation).
|
||||
size_t q_tile_bytes;
|
||||
size_t o_tile_bytes;
|
||||
size_t k_tile_bytes;
|
||||
size_t s_tile_bytes; // S and P tiles (same size)
|
||||
size_t d_tile_bytes; // d_tiles[0..1] + d_inv_l, allocated back to back
|
||||
size_t m_line_bytes; // one mask row
|
||||
size_t m_buf_slot_bytes; // one dma_cache slot = align_up(Br * m_line_bytes, 4096)
|
||||
size_t col_vec_bytes;
|
||||
size_t sinks_bytes;
|
||||
|
||||
// Derived strides.
|
||||
size_t row_buf_stride; // HVX vectors (128B) per row buffer
|
||||
size_t mask_buf_row_stride; // __fp16 elements per row in the mask buffer
|
||||
|
||||
bool pipeline;
|
||||
size_t total_bytes;
|
||||
};
|
||||
|
||||
#define FA_MAX_KV_BLOCKS 1024
|
||||
|
||||
// Build the VTCM layout.
|
||||
|
||||
static inline void hmx_fa_vtcm_layout_build(struct hmx_fa_vtcm_layout * L,
|
||||
size_t gqa_factor, size_t DK, size_t DV,
|
||||
size_t Br, size_t Bc, size_t n_threads,
|
||||
bool pipeline, bool is_q_fp32,
|
||||
bool has_sinks, size_t n_heads) {
|
||||
const size_t g_br = hex_align_up(gqa_factor * Br, HMX_FP16_TILE_N_ROWS);
|
||||
const size_t q_tile_size = hex_align_up(g_br * DK * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
|
||||
const size_t o_tile_size = hex_align_up(g_br * DV * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
|
||||
const size_t k_tile_size = hex_align_up(Bc * DK * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
|
||||
const size_t v_tile_size = hex_align_up(Bc * DV * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
|
||||
const size_t s_tile_size = hex_align_up(g_br * Bc * sizeof(__fp16), HTP_FA_HMX_TILE_SIZE);
|
||||
|
||||
// The rescale matrices are diagonal: the HMX kernels only ever load the g_br/32
|
||||
// tiles that sit on the diagonal, so store just those, packed back to back with
|
||||
// a stride of one tile. The old [g_br, g_br] square layout allocated g_br/32
|
||||
// times more than it used, which is also why a second D buffer was unaffordable.
|
||||
const size_t d_tile_size = (g_br / HMX_FP16_TILE_N_ROWS) * HTP_FA_HMX_TILE_SIZE;
|
||||
|
||||
const size_t q_dma_size = hex_align_up(g_br * DK * (is_q_fp32 ? sizeof(float) : sizeof(__fp16)), 128);
|
||||
const size_t k_dma_size = hex_align_up(Bc * hex_round_up(DK * sizeof(__fp16), 128), 128);
|
||||
const size_t v_dma_size = hex_align_up(Bc * hex_round_up(DV * sizeof(__fp16), 128), 128);
|
||||
const size_t col_vec_size = hex_align_up(g_br * sizeof(float), 256);
|
||||
const size_t row_vec_size = hex_align_up(Bc * sizeof(__fp16), 256);
|
||||
const size_t m_line_size = hex_align_up(Bc * sizeof(__fp16), 128);
|
||||
const size_t m_buf_slot = hex_align_up(Br * m_line_size, 256);
|
||||
const size_t m_buf_size = m_buf_slot * HMX_FA_DMA_CACHE_SIZE;
|
||||
const size_t slopes_size = hex_align_up(g_br * sizeof(__fp16), 128);
|
||||
const size_t sinks_size = hex_round_up(n_heads * sizeof(float), 128);
|
||||
|
||||
size_t off = 0;
|
||||
|
||||
// Group A (Part 1 - HMX Tiled buffers)
|
||||
VTCM_LAYOUT_ALLOC(off, off_q_tiles, q_tile_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o_tiles[0], o_tile_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o_tiles[1], o_tile_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_d_tiles[0], d_tile_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_d_tiles[1], d_tile_size, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off, off_d_inv_l, d_tile_size);
|
||||
|
||||
// Group B & C share start offset (Group B tiles must be 2KB aligned)
|
||||
size_t off_group_b_c = hex_align_up(off, HTP_FA_HMX_TILE_SIZE);
|
||||
|
||||
// Group B: Compute-only buffers
|
||||
size_t off_group_b = off_group_b_c;
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_k_tiles[0], k_tile_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_k_tiles[1], k_tile_size, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_v_tiles[0], v_tile_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_v_tiles[1], v_tile_size, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_s_tiles[0], s_tile_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_s_tiles[1], s_tile_size, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_p_tiles[0], s_tile_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_p_tiles[1], s_tile_size, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_s_rowmax, col_vec_size);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_p_rowsum, col_vec_size);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_row_bufs, row_vec_size * 2 * n_threads);
|
||||
|
||||
const size_t group_b_size = off_group_b - off_group_b_c;
|
||||
|
||||
// Group C: Q fetch DMA buffer
|
||||
size_t off_group_c = off_group_b_c;
|
||||
VTCM_LAYOUT_ALLOC(off_group_c, off_q_dma, q_dma_size);
|
||||
|
||||
const size_t group_c_size = off_group_c - off_group_b_c;
|
||||
|
||||
off = off_group_b_c + hex_smax(group_b_size, group_c_size);
|
||||
|
||||
// Group A (Part 2 - remaining non-HMX buffers)
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_fp16[0], k_dma_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_fp16[1], k_dma_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_v_fp16[0], v_dma_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_v_fp16[1], v_dma_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_m_vec, col_vec_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_l_vec, col_vec_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_hmx_scales_id, 256);
|
||||
VTCM_LAYOUT_ALLOC(off, off_hmx_scales_qk, 256);
|
||||
VTCM_LAYOUT_ALLOC(off, off_mask_buf, m_buf_size);
|
||||
VTCM_LAYOUT_ALLOC(off, off_slopes, slopes_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_sinks, sinks_size, has_sinks);
|
||||
|
||||
L->q_tile_bytes = q_tile_size;
|
||||
L->o_tile_bytes = o_tile_size;
|
||||
L->k_tile_bytes = k_tile_size;
|
||||
L->col_vec_bytes = col_vec_size;
|
||||
L->s_tile_bytes = s_tile_size;
|
||||
// Measured from the actual offsets rather than assumed to be N * d_tile_size, so
|
||||
// that inserting a region between them (or adding padding to VTCM_LAYOUT_ALLOC)
|
||||
// cannot silently leave the tail of the run unzeroed.
|
||||
L->d_tile_bytes = (L->off_d_inv_l + d_tile_size) - L->off_d_tiles[0];
|
||||
L->m_line_bytes = m_line_size;
|
||||
L->m_buf_slot_bytes = m_buf_slot;
|
||||
L->row_buf_stride = row_vec_size / 128;
|
||||
L->mask_buf_row_stride = m_line_size / sizeof(__fp16);
|
||||
L->sinks_bytes = has_sinks ? sinks_size : 0;
|
||||
L->pipeline = pipeline;
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
// Exact VTCM usage for a given (gqa_factor, DK, DV, Br, Bc) configuration.
|
||||
static inline size_t hmx_fa_compute_vtcm_usage(size_t gqa_factor, size_t DK, size_t DV, size_t Br, size_t Bc, size_t n_threads, bool pipeline, bool is_q_fp32, bool has_sinks, size_t n_heads) {
|
||||
struct hmx_fa_vtcm_layout L;
|
||||
hmx_fa_vtcm_layout_build(&L, gqa_factor, DK, DV, Br, Bc, n_threads, pipeline, is_q_fp32, has_sinks, n_heads);
|
||||
return L.total_bytes;
|
||||
}
|
||||
|
||||
#define FA_HVX_BLOCK_SIZE 64
|
||||
#define FA_HVX_G_MAX 8
|
||||
|
||||
struct hvx_fa_vtcm_layout {
|
||||
size_t off_q;
|
||||
size_t off_k;
|
||||
size_t off_v;
|
||||
size_t off_m;
|
||||
size_t off_a;
|
||||
size_t off_sinks;
|
||||
|
||||
size_t size_q_block;
|
||||
size_t size_k_block;
|
||||
size_t size_v_block;
|
||||
size_t size_m_block;
|
||||
size_t size_vkq_acc;
|
||||
size_t size_sinks;
|
||||
|
||||
size_t total_bytes;
|
||||
};
|
||||
|
||||
static inline void hvx_fa_vtcm_layout_build(struct hvx_fa_vtcm_layout * L,
|
||||
size_t DK, size_t DV,
|
||||
bool is_q_fp32, bool has_mask,
|
||||
bool has_sinks, size_t n_heads,
|
||||
size_t n_threads) {
|
||||
const size_t size_q_row_padded = hex_round_up(DK * (is_q_fp32 ? 4 : 2), 128);
|
||||
const size_t size_k_row_padded = hex_round_up(DK * sizeof(__fp16), 128);
|
||||
const size_t size_v_row_padded = hex_round_up(DV * sizeof(__fp16), 128);
|
||||
|
||||
const size_t size_q_block = size_q_row_padded * FA_HVX_G_MAX;
|
||||
const size_t size_k_block = size_k_row_padded * FA_HVX_BLOCK_SIZE;
|
||||
const size_t size_v_block = size_v_row_padded * FA_HVX_BLOCK_SIZE;
|
||||
const size_t size_m_block = hex_round_up(FA_HVX_BLOCK_SIZE * sizeof(__fp16), 128);
|
||||
const size_t size_vkq_acc = hex_round_up(DV * sizeof(float), 128) * FA_HVX_G_MAX;
|
||||
const size_t size_sinks = hex_round_up(n_heads * sizeof(float), 128);
|
||||
|
||||
size_t off = 0;
|
||||
|
||||
VTCM_LAYOUT_ALLOC(off, off_q, size_q_block * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k, size_k_block * 2 * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_v, size_v_block * 2 * n_threads);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_m, size_m_block * HVX_FA_DMA_CACHE_SIZE * n_threads, has_mask);
|
||||
VTCM_LAYOUT_ALLOC(off, off_a, size_vkq_acc * n_threads);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_sinks, size_sinks, has_sinks);
|
||||
|
||||
L->size_q_block = size_q_block;
|
||||
L->size_k_block = size_k_block;
|
||||
L->size_v_block = size_v_block;
|
||||
L->size_m_block = size_m_block;
|
||||
L->size_vkq_acc = size_vkq_acc;
|
||||
L->size_sinks = has_sinks ? size_sinks : 0;
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
static inline size_t hvx_fa_compute_vtcm_usage(size_t DK, size_t DV, bool is_q_fp32, bool has_mask, bool has_sinks, size_t n_heads, size_t n_threads) {
|
||||
struct hvx_fa_vtcm_layout L;
|
||||
hvx_fa_vtcm_layout_build(&L, DK, DV, is_q_fp32, has_mask, has_sinks, n_heads, n_threads);
|
||||
return L.total_bytes;
|
||||
}
|
||||
|
||||
#define FA_MIN_KV_BLOCKS 3
|
||||
|
||||
// Cost-based (Br, Bc) search for flash attention with pipeline constraint.
|
||||
static inline int hmx_fa_find_chunk_size(size_t * Br_out,
|
||||
size_t * Bc_out,
|
||||
size_t gqa_factor,
|
||||
size_t DK,
|
||||
size_t DV,
|
||||
size_t qo_len,
|
||||
size_t kv_len,
|
||||
size_t vtcm_budget,
|
||||
size_t n_threads,
|
||||
bool is_q_fp32,
|
||||
bool has_sinks,
|
||||
size_t n_heads,
|
||||
size_t q_split) {
|
||||
const size_t T = HMX_FP16_TILE_N_ROWS; // 32
|
||||
const size_t br_unit = hmx_ceil_div(T, gqa_factor);
|
||||
const size_t bc_unit = HMX_FP16_TILE_N_COLS * 2; // 64
|
||||
const bool can_pipeline = (kv_len >= FA_MIN_KV_BLOCKS * bc_unit && n_threads >= 2);
|
||||
|
||||
// Br_max: largest Br aligned to br_unit that does not exceed qo_len.
|
||||
const size_t Br_max = qo_len >= br_unit ? hex_align_down(qo_len, br_unit) : br_unit;
|
||||
|
||||
// Pipeline constraint: cap Bc so n_kv_blocks >= FA_MIN_KV_BLOCKS.
|
||||
// Only relax when kv_len is too short to form enough blocks.
|
||||
size_t Bc_limit = can_pipeline ? hex_align_down(kv_len / FA_MIN_KV_BLOCKS, bc_unit) :
|
||||
(kv_len >= bc_unit ? hex_align_down(kv_len, bc_unit) : bc_unit);
|
||||
// For small-Q SWA-sized KV caches (qo_len <= 16, kv_len <= 2048), cap Bc at 256 for block-skipping granularity
|
||||
if (qo_len <= 16 && kv_len <= 2048 && Bc_limit > 256) {
|
||||
Bc_limit = 256;
|
||||
}
|
||||
// Cost coefficients calibrated from profiling
|
||||
const size_t c_q_fixed = 800; // per-Q-block: q_load + epilogue o_update + o_norm + o_store
|
||||
const size_t c_iter_base = 200; // per-KV-iter base (HMX dot/update + DMA)
|
||||
const size_t c_softmax = 600; // per 64-row vector chunk on HVX
|
||||
|
||||
size_t best_cost = SIZE_MAX, best_mn = 0;
|
||||
size_t best_Br = 0, best_Bc = 0;
|
||||
|
||||
for (size_t Br = Br_max; Br >= br_unit; Br -= br_unit) {
|
||||
// Try all Bc candidates from Bc_limit down to bc_unit
|
||||
for (size_t Bc = Bc_limit; Bc >= bc_unit; Bc -= bc_unit) {
|
||||
size_t vtcm_needed = hmx_fa_compute_vtcm_usage(gqa_factor, DK, DV, Br, Bc, n_threads, can_pipeline, is_q_fp32, has_sinks, n_heads);
|
||||
if (vtcm_needed <= vtcm_budget) {
|
||||
// This Bc fits for this Br!
|
||||
const size_t q_blocks = (qo_len + Br - 1) / Br;
|
||||
const size_t kv_blocks = (kv_len + Bc - 1) / Bc;
|
||||
const size_t actual_threads = (kv_blocks >= 3 && n_threads >= 2) ? n_threads : 1;
|
||||
const size_t n_rows_g = Br * gqa_factor;
|
||||
const size_t n_row_vec_cnt = (n_rows_g + 63) / 64;
|
||||
const size_t n_use = n_row_vec_cnt < actual_threads ? n_row_vec_cnt : actual_threads;
|
||||
const size_t vecs_per_t = n_use > 0 ? (n_row_vec_cnt + n_use - 1) / n_use : 1;
|
||||
|
||||
const size_t c_iter_actual = c_iter_base + c_softmax * vecs_per_t;
|
||||
const size_t q_blocks_nsp = (q_blocks + q_split - 1) / (q_split ? q_split : 1);
|
||||
const size_t cost = q_blocks_nsp * (c_q_fixed + kv_blocks * c_iter_actual);
|
||||
const size_t mn = Br * Bc;
|
||||
|
||||
if (cost < best_cost || (cost == best_cost && mn > best_mn)) {
|
||||
best_cost = cost;
|
||||
best_mn = mn;
|
||||
best_Br = Br;
|
||||
best_Bc = Bc;
|
||||
}
|
||||
// Since we iterate Bc from largest to smallest, this is the largest Bc that fits
|
||||
// for this Br. We can break to the next Br.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Br == br_unit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_Br == 0 || best_Bc == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*Br_out = best_Br;
|
||||
*Bc_out = best_Bc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HTP_FLASH_ATTN_OPS_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,297 @@
|
||||
#ifndef HTP_GATED_DELTA_NET_OPS_H
|
||||
#define HTP_GATED_DELTA_NET_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hex-fastdiv.h"
|
||||
#include "hex-common.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
#define HTP_GDN_MAX_SV 128
|
||||
#define HTP_GDN_CHUNK_SIZE 64
|
||||
#define HTP_GDN_MIN_TOKENS 8
|
||||
|
||||
#ifndef HMX_FP16_TILE_SIZE
|
||||
#define HMX_FP16_TILE_SIZE 2048
|
||||
#endif
|
||||
|
||||
enum htp_gdn_kernel_type {
|
||||
HTP_GDN_KERNEL_HVX_RECURRENT = 0,
|
||||
HTP_GDN_KERNEL_HMX_CHUNKED = 1,
|
||||
};
|
||||
|
||||
struct htp_gdn_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t pipeline;
|
||||
uint16_t chunk_size;
|
||||
uint16_t n_chunks;
|
||||
uint16_t n_heads_batch;
|
||||
|
||||
uint32_t n_threads;
|
||||
uint32_t S_v;
|
||||
uint32_t H;
|
||||
uint32_t n_tokens;
|
||||
uint32_t n_seqs;
|
||||
uint32_t K;
|
||||
|
||||
uint32_t total_rows;
|
||||
uint32_t kda;
|
||||
uint32_t vtcm_size;
|
||||
uint32_t state_seq_stride;
|
||||
uint32_t state_size_per_snap;
|
||||
|
||||
float scale;
|
||||
|
||||
struct fastdiv_values div_H;
|
||||
struct fastdiv_values div_q1;
|
||||
struct fastdiv_values div_k1;
|
||||
struct fastdiv_values div_rq3;
|
||||
struct fastdiv_values div_rk3;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_gdn_kernel_params) <= 128, "htp_gdn_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_gdn_kernel_params) <= 128, "htp_gdn_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
struct htp_gdn_vtcm_layout {
|
||||
size_t state_aligned;
|
||||
size_t bytes_per_thread;
|
||||
size_t off_state;
|
||||
size_t total_bytes;
|
||||
};
|
||||
|
||||
static inline void htp_gdn_vtcm_layout_build(
|
||||
struct htp_gdn_vtcm_layout * L,
|
||||
uint32_t S_v,
|
||||
uint32_t n_threads
|
||||
) {
|
||||
size_t state_bytes = (size_t) S_v * S_v * sizeof(float);
|
||||
L->state_aligned = hex_round_up(state_bytes, 128);
|
||||
L->bytes_per_thread = 2 * L->state_aligned;
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_state, L->bytes_per_thread * n_threads);
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
struct htp_gdn_hmx_vtcm_layout {
|
||||
size_t off_s_state;
|
||||
size_t off_s_f16;
|
||||
size_t off_s_col_tiles;
|
||||
size_t off_s_update_f32;
|
||||
size_t off_s_update_tiles;
|
||||
|
||||
size_t off_q_f32[2];
|
||||
size_t off_k_f32[2];
|
||||
size_t off_v_f32[2];
|
||||
size_t off_g_f32[2];
|
||||
size_t off_b_f32[2];
|
||||
size_t off_g_raw[2];
|
||||
size_t off_b_raw[2];
|
||||
size_t off_o_f32[2];
|
||||
|
||||
size_t off_v_inter_f32;
|
||||
size_t off_o_inter_f32;
|
||||
size_t off_o_intra_f32;
|
||||
size_t off_k_f16;
|
||||
size_t off_v_prime_f16;
|
||||
size_t off_delta_f16;
|
||||
size_t off_d_f16;
|
||||
|
||||
size_t off_q_row_tiles;
|
||||
size_t off_q_prime_row_tiles;
|
||||
size_t off_k_row_tiles;
|
||||
size_t off_k_col_tiles;
|
||||
size_t off_k_prime_row_tiles;
|
||||
size_t off_k_col_tiles_64x128;
|
||||
size_t off_kk_tiles;
|
||||
size_t off_qk_tiles;
|
||||
size_t off_v_inter_tiles;
|
||||
size_t off_o_inter_tiles;
|
||||
size_t off_inv_row_tiles;
|
||||
size_t off_a_row_tiles;
|
||||
size_t off_v_prime_col_tiles;
|
||||
size_t off_delta_tiles;
|
||||
size_t off_delta_col_tiles;
|
||||
size_t off_o_intra_tiles;
|
||||
size_t off_d_row_tiles;
|
||||
|
||||
size_t off_gamma;
|
||||
size_t off_lambda_init;
|
||||
size_t off_decay_m;
|
||||
size_t off_decay_a;
|
||||
size_t off_rows_kk;
|
||||
size_t off_rows_qk;
|
||||
size_t off_rows_inv;
|
||||
size_t off_rows_a;
|
||||
|
||||
size_t off_thread_scratch;
|
||||
size_t off_scales_1;
|
||||
|
||||
size_t state_f32_bytes;
|
||||
size_t state_f16_bytes;
|
||||
size_t state_tiles_bytes;
|
||||
size_t dma_chunk_bytes;
|
||||
size_t act_f16_bytes;
|
||||
size_t tile_64xSv_bytes;
|
||||
size_t tile_64x64_bytes;
|
||||
|
||||
uint32_t n_heads_batch;
|
||||
uint32_t n_threads;
|
||||
bool pipeline;
|
||||
size_t total_bytes;
|
||||
};
|
||||
|
||||
static inline void htp_gdn_hmx_vtcm_layout_build(
|
||||
struct htp_gdn_hmx_vtcm_layout * L,
|
||||
uint32_t S_v,
|
||||
uint32_t chunk_size,
|
||||
uint32_t n_heads_batch,
|
||||
uint32_t n_threads,
|
||||
bool pipeline
|
||||
) {
|
||||
memset(L, 0, sizeof(*L));
|
||||
L->n_heads_batch = n_heads_batch;
|
||||
L->n_threads = n_threads;
|
||||
L->pipeline = pipeline;
|
||||
|
||||
const size_t bh = (size_t) n_heads_batch;
|
||||
const size_t nth = (size_t) (n_threads > 0 ? n_threads : 1);
|
||||
|
||||
const size_t state_f32_sz = hex_round_up(S_v * S_v * sizeof(float), 2048);
|
||||
const size_t state_f16_sz = hex_round_up(S_v * S_v * sizeof(__fp16), 2048);
|
||||
const size_t n_sv_tiles = S_v / 32;
|
||||
const size_t state_tiles_sz = n_sv_tiles * n_sv_tiles * 2048;
|
||||
|
||||
const size_t dma_chunk_sz = hex_round_up(chunk_size * S_v * sizeof(float), 2048);
|
||||
const size_t dma_scalar_sz = hex_round_up(chunk_size * sizeof(float), 128);
|
||||
|
||||
const size_t act_f16_sz = hex_round_up(chunk_size * S_v * sizeof(__fp16), 2048);
|
||||
const size_t tile_64xSv_sz = 2 * n_sv_tiles * 2048;
|
||||
const size_t tile_64x64_sz = 4 * 2048;
|
||||
|
||||
const size_t decay_sz = 64 * 64 * sizeof(__fp16);
|
||||
const size_t row_vecs_sz = 64 * 128;
|
||||
|
||||
L->state_f32_bytes = state_f32_sz;
|
||||
L->state_f16_bytes = state_f16_sz;
|
||||
L->state_tiles_bytes = state_tiles_sz;
|
||||
L->dma_chunk_bytes = dma_chunk_sz;
|
||||
L->act_f16_bytes = act_f16_sz;
|
||||
L->tile_64xSv_bytes = tile_64xSv_sz;
|
||||
L->tile_64x64_bytes = tile_64x64_sz;
|
||||
|
||||
size_t off = 0;
|
||||
|
||||
VTCM_LAYOUT_ALLOC(off, off_s_state, bh * state_f32_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_s_f16, bh * state_f16_sz);
|
||||
off = hex_align_up(off, HMX_FP16_TILE_SIZE);
|
||||
VTCM_LAYOUT_ALLOC(off, off_s_col_tiles, bh * state_tiles_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_s_update_f32, bh * state_f32_sz);
|
||||
off = hex_align_up(off, HMX_FP16_TILE_SIZE);
|
||||
VTCM_LAYOUT_ALLOC(off, off_s_update_tiles, bh * state_tiles_sz);
|
||||
|
||||
VTCM_LAYOUT_ALLOC(off, off_q_f32[0], bh * dma_chunk_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_q_f32[1], bh * dma_chunk_sz, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_f32[0], bh * dma_chunk_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_k_f32[1], bh * dma_chunk_sz, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off, off_v_f32[0], bh * dma_chunk_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_v_f32[1], bh * dma_chunk_sz, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off, off_g_f32[0], bh * dma_scalar_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_g_f32[1], bh * dma_scalar_sz, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off, off_b_f32[0], bh * dma_scalar_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_b_f32[1], bh * dma_scalar_sz, pipeline);
|
||||
const size_t raw_gb_sz = hex_round_up(bh * chunk_size * sizeof(float), 128);
|
||||
VTCM_LAYOUT_ALLOC(off, off_g_raw[0], raw_gb_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_g_raw[1], raw_gb_sz, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off, off_b_raw[0], raw_gb_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_b_raw[1], raw_gb_sz, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o_f32[0], bh * dma_chunk_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off, off_o_f32[1], bh * dma_chunk_sz, pipeline);
|
||||
|
||||
VTCM_LAYOUT_ALLOC(off, off_v_inter_f32, bh * dma_chunk_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o_inter_f32, bh * dma_chunk_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o_intra_f32, bh * dma_chunk_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_f16, bh * act_f16_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_v_prime_f16, bh * act_f16_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_delta_f16, bh * act_f16_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_d_f16, bh * act_f16_sz);
|
||||
|
||||
off = hex_align_up(off, HMX_FP16_TILE_SIZE);
|
||||
VTCM_LAYOUT_ALLOC(off, off_q_row_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_q_prime_row_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_row_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_col_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_prime_row_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_k_col_tiles_64x128, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_kk_tiles, bh * tile_64x64_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_qk_tiles, bh * tile_64x64_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_v_inter_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o_inter_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_inv_row_tiles, bh * tile_64x64_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_a_row_tiles, bh * tile_64x64_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_v_prime_col_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_delta_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_delta_col_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_o_intra_tiles, bh * tile_64xSv_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_d_row_tiles, bh * tile_64xSv_sz);
|
||||
|
||||
VTCM_LAYOUT_ALLOC(off, off_gamma, bh * hex_round_up(chunk_size * sizeof(float), 128));
|
||||
VTCM_LAYOUT_ALLOC(off, off_lambda_init, bh * hex_round_up(chunk_size * sizeof(float), 128));
|
||||
VTCM_LAYOUT_ALLOC(off, off_decay_m, bh * decay_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_decay_a, bh * decay_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_rows_kk, bh * row_vecs_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_rows_qk, bh * row_vecs_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_rows_inv, bh * row_vecs_sz);
|
||||
VTCM_LAYOUT_ALLOC(off, off_rows_a, bh * row_vecs_sz);
|
||||
|
||||
const size_t thread_scratch_sz = 64 * 128;
|
||||
off = hex_align_up(off, HMX_FP16_TILE_SIZE);
|
||||
VTCM_LAYOUT_ALLOC(off, off_thread_scratch, nth * thread_scratch_sz);
|
||||
off = hex_align_up(off, HMX_FP16_TILE_SIZE);
|
||||
VTCM_LAYOUT_ALLOC(off, off_scales_1, HMX_FP16_TILE_SIZE);
|
||||
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
static inline bool htp_gdn_hmx_solve_layout(
|
||||
struct htp_gdn_hmx_vtcm_layout * layout_out,
|
||||
uint32_t S_v,
|
||||
uint32_t chunk_size,
|
||||
uint32_t total_rows,
|
||||
size_t vtcm_budget,
|
||||
uint32_t n_threads,
|
||||
bool pipeline,
|
||||
uint32_t * n_heads_batch_out
|
||||
) {
|
||||
uint32_t max_batch = 8;
|
||||
if (max_batch > total_rows) {
|
||||
max_batch = total_rows;
|
||||
}
|
||||
if (max_batch > n_threads) {
|
||||
max_batch = n_threads;
|
||||
}
|
||||
static const uint32_t candidates[] = { 8, 6, 4, 2, 1 };
|
||||
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) {
|
||||
uint32_t bh = candidates[i];
|
||||
if (bh > max_batch) {
|
||||
continue;
|
||||
}
|
||||
struct htp_gdn_hmx_vtcm_layout L;
|
||||
htp_gdn_hmx_vtcm_layout_build(&L, S_v, chunk_size, bh, n_threads, pipeline);
|
||||
if (L.total_bytes <= vtcm_budget) {
|
||||
*layout_out = L;
|
||||
*n_heads_batch_out = bh;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (pipeline) {
|
||||
return htp_gdn_hmx_solve_layout(layout_out, S_v, chunk_size, total_rows, vtcm_budget, n_threads, false, n_heads_batch_out);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // HTP_GATED_DELTA_NET_OPS_H
|
||||
@@ -0,0 +1,403 @@
|
||||
#include "gather-rows.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-profile.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "dma-queue.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "work-queue.h"
|
||||
|
||||
typedef void (*gather_rows_compute_fn_t)(
|
||||
uint8_t * restrict out, const uint8_t * restrict region, uint32_t nvec,
|
||||
const struct htp_gather_rows_params * restrict kparams,
|
||||
const HVX_Vector * restrict tab, const HVX_Vector * restrict inc, HVX_Vector * restrict cur);
|
||||
|
||||
struct gather_rows_task {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_tensor * a;
|
||||
const struct htp_tensor * b;
|
||||
dma_addr_t dst;
|
||||
uint32_t r_start;
|
||||
uint32_t rows;
|
||||
uint32_t rows_per_thread;
|
||||
const struct htp_gather_rows_params * kparams;
|
||||
gather_rows_compute_fn_t compute;
|
||||
};
|
||||
|
||||
static inline __attribute__((always_inline)) void gather_rows_dma_in(
|
||||
dma_queue * q, const struct gather_rows_task * task,
|
||||
uint8_t * region, uint32_t r, uint32_t nr, uint32_t elem_size) {
|
||||
const struct htp_tensor * a = task->a;
|
||||
const struct htp_tensor * b = task->b;
|
||||
const struct htp_gather_rows_params * kparams = task->kparams;
|
||||
|
||||
const size_t a_size = (nr - 1) * a->nb[1] + kparams->na * elem_size;
|
||||
dma_queue_push(q, dma_make_data(region, a->data + r * a->nb[1]), a_size, a_size, a_size, 1);
|
||||
if (b) {
|
||||
if (kparams->b_dense) {
|
||||
const size_t b_size = (nr - 1) * b->nb[1] + kparams->nb * elem_size;
|
||||
dma_queue_push(q, dma_make_data(region + kparams->v1, b->data + r * b->nb[1]), b_size, b_size, b_size, 1);
|
||||
} else {
|
||||
const size_t b_size = (nr - 1) * b->nb[1] + elem_size;
|
||||
for (uint32_t k = 0; k < kparams->nb; k++) {
|
||||
dma_queue_push(q, dma_make_data(region + kparams->v1 + k * kparams->span1,
|
||||
b->data + k * b->nb[0] + r * b->nb[1]), b_size, b_size, b_size, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void gather_rows_dma_out(dma_queue * q, const struct gather_rows_task * task,
|
||||
uint8_t * out, uint32_t r, uint32_t nr, uint32_t elem_size) {
|
||||
const struct htp_gather_rows_params * kparams = task->kparams;
|
||||
const size_t out_size = nr * kparams->ne * elem_size;
|
||||
dma_queue_push(q, dma_make_data(task->dst + r * kparams->ne * elem_size, out), out_size, out_size, out_size, 1);
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void gather_rows_init_tables_f32(
|
||||
HVX_Vector * tab, HVX_Vector * inc,
|
||||
const struct htp_gather_rows_params * kparams,
|
||||
const struct htp_tensor * a, const struct htp_tensor * b) {
|
||||
|
||||
const int32_t s_a = kparams->rg * a->nb[1];
|
||||
const int32_t s_b = b ? kparams->rg * b->nb[1] : 0;
|
||||
const uint32_t b_nb1 = b ? b->nb[1] : 0;
|
||||
uint32_t row = 0;
|
||||
uint32_t col = 0;
|
||||
int32_t t_buf[32] __attribute__((aligned(128)));
|
||||
int32_t s_buf[32] __attribute__((aligned(128)));
|
||||
for (uint32_t k = 0; k < kparams->vg; k++) {
|
||||
for (uint32_t lane = 0; lane < 32; lane++) {
|
||||
if (col < kparams->na) {
|
||||
t_buf[lane] = row * a->nb[1] + col * 4;
|
||||
s_buf[lane] = s_a;
|
||||
} else {
|
||||
t_buf[lane] = kparams->v1 + (col - kparams->na) * kparams->span1 + row * b_nb1;
|
||||
s_buf[lane] = s_b;
|
||||
}
|
||||
if (++col == kparams->ne) {
|
||||
col = 0;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
tab[k] = *(const HVX_Vector *) t_buf;
|
||||
inc[k] = *(const HVX_Vector *) s_buf;
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void gather_rows_init_tables_f16(
|
||||
HVX_Vector * tab, HVX_Vector * inc,
|
||||
const struct htp_gather_rows_params * kparams,
|
||||
const struct htp_tensor * a, const struct htp_tensor * b) {
|
||||
|
||||
const int16_t s_a = (int16_t) (kparams->rg * a->nb[1]);
|
||||
const int16_t s_b = (int16_t) (b ? kparams->rg * b->nb[1] : 0);
|
||||
const uint32_t b_nb1 = b ? b->nb[1] : 0;
|
||||
uint32_t row = 0;
|
||||
uint32_t col = 0;
|
||||
int16_t t_buf[64] __attribute__((aligned(128)));
|
||||
int16_t s_buf[64] __attribute__((aligned(128)));
|
||||
for (uint32_t k = 0; k < kparams->vg; k++) {
|
||||
for (uint32_t lane = 0; lane < 64; lane++) {
|
||||
if (col < kparams->na) {
|
||||
t_buf[lane] = (int16_t) (row * a->nb[1] + col * 2);
|
||||
s_buf[lane] = s_a;
|
||||
} else {
|
||||
t_buf[lane] = (int16_t) (kparams->v1 + (col - kparams->na) * kparams->span1 + row * b_nb1);
|
||||
s_buf[lane] = s_b;
|
||||
}
|
||||
if (++col == kparams->ne) {
|
||||
col = 0;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
tab[k] = *(const HVX_Vector *) t_buf;
|
||||
inc[k] = *(const HVX_Vector *) s_buf;
|
||||
}
|
||||
}
|
||||
|
||||
static void gather_rows_compute_f32_vg1(
|
||||
uint8_t * restrict out, const uint8_t * restrict region, uint32_t nvec,
|
||||
const struct htp_gather_rows_params * restrict kparams,
|
||||
const HVX_Vector * restrict tab, const HVX_Vector * restrict inc, HVX_Vector * restrict cur) {
|
||||
(void) cur;
|
||||
const uint32_t region_size = kparams->region_size;
|
||||
HVX_Vector vcur = tab[0];
|
||||
const HVX_Vector vinc = inc[0];
|
||||
#pragma unroll(4)
|
||||
for (uint32_t v = 0; v < nvec; v++) {
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (out + v * 128), (size_t) region, region_size, vcur);
|
||||
vcur = Q6_Vw_vadd_VwVw(vcur, vinc);
|
||||
}
|
||||
}
|
||||
|
||||
static void gather_rows_compute_f32_vgn(
|
||||
uint8_t * restrict out, const uint8_t * restrict region, uint32_t nvec,
|
||||
const struct htp_gather_rows_params * restrict kparams,
|
||||
const HVX_Vector * restrict tab, const HVX_Vector * restrict inc, HVX_Vector * restrict cur) {
|
||||
const uint32_t vg = kparams->vg;
|
||||
const uint32_t region_size = kparams->region_size;
|
||||
for (uint32_t k = 0; k < vg; k++) {
|
||||
cur[k] = tab[k];
|
||||
}
|
||||
uint32_t k = 0;
|
||||
#pragma unroll(4)
|
||||
for (uint32_t v = 0; v < nvec; v++) {
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (out + v * 128), (size_t) region, region_size, cur[k]);
|
||||
cur[k] = Q6_Vw_vadd_VwVw(cur[k], inc[k]);
|
||||
if (++k == vg) {
|
||||
k = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gather_rows_compute_f16_vg1(
|
||||
uint8_t * restrict out, const uint8_t * restrict region, uint32_t nvec,
|
||||
const struct htp_gather_rows_params * restrict kparams,
|
||||
const HVX_Vector * restrict tab, const HVX_Vector * restrict inc, HVX_Vector * restrict cur) {
|
||||
(void) cur;
|
||||
const uint32_t region_size = kparams->region_size;
|
||||
HVX_Vector vcur = tab[0];
|
||||
const HVX_Vector vinc = inc[0];
|
||||
#pragma unroll(4)
|
||||
for (uint32_t v = 0; v < nvec; v++) {
|
||||
Q6_vgather_ARMVh((HVX_Vector *) (out + v * 128), (size_t) region, region_size, vcur);
|
||||
vcur = Q6_Vh_vadd_VhVh(vcur, vinc);
|
||||
}
|
||||
}
|
||||
|
||||
static void gather_rows_compute_f16_vgn(
|
||||
uint8_t * restrict out, const uint8_t * restrict region, uint32_t nvec,
|
||||
const struct htp_gather_rows_params * restrict kparams,
|
||||
const HVX_Vector * restrict tab, const HVX_Vector * restrict inc, HVX_Vector * restrict cur) {
|
||||
const uint32_t vg = kparams->vg;
|
||||
const uint32_t region_size = kparams->region_size;
|
||||
for (uint32_t k = 0; k < vg; k++) {
|
||||
cur[k] = tab[k];
|
||||
}
|
||||
uint32_t k = 0;
|
||||
#pragma unroll(4)
|
||||
for (uint32_t v = 0; v < nvec; v++) {
|
||||
Q6_vgather_ARMVh((HVX_Vector *) (out + v * 128), (size_t) region, region_size, cur[k]);
|
||||
cur[k] = Q6_Vh_vadd_VhVh(cur[k], inc[k]);
|
||||
if (++k == vg) {
|
||||
k = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gather_rows_thread_f32(unsigned int nth, unsigned int ith, void * data) {
|
||||
(void) nth;
|
||||
const struct gather_rows_task * task = (const struct gather_rows_task *) data;
|
||||
const struct htp_gather_rows_params * kparams = task->kparams;
|
||||
const struct htp_tensor * a = task->a;
|
||||
const struct htp_tensor * b = task->b;
|
||||
|
||||
const uint32_t r_offset = MIN(ith * task->rows_per_thread, task->rows);
|
||||
const uint32_t r_beg = task->r_start + r_offset;
|
||||
const uint32_t r_end = task->r_start + MIN(r_offset + task->rows_per_thread, task->rows);
|
||||
if (r_beg >= r_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &task->octx->ctx->trace[ith];
|
||||
|
||||
struct htp_gather_rows_vtcm_layout layout;
|
||||
htp_gather_rows_vtcm_layout_build(&layout, kparams->vg, kparams->region_size, kparams->nr_max, kparams->ne, 4);
|
||||
|
||||
uint8_t * base = VTCM_LAYOUT_PTR(uint8_t, task->octx->ctx->vtcm_base, (size_t) ith * kparams->slice);
|
||||
HVX_Vector * tab = VTCM_LAYOUT_PTR(HVX_Vector, base, layout.off_tab);
|
||||
HVX_Vector * inc = VTCM_LAYOUT_PTR(HVX_Vector, base, layout.off_inc);
|
||||
HVX_Vector * cur = VTCM_LAYOUT_PTR(HVX_Vector, base, layout.off_cur);
|
||||
|
||||
uint8_t * region[2] = {
|
||||
VTCM_LAYOUT_PTR(uint8_t, base, layout.off_region0),
|
||||
VTCM_LAYOUT_PTR(uint8_t, base, layout.off_region1),
|
||||
};
|
||||
uint8_t * out[2] = {
|
||||
VTCM_LAYOUT_PTR(uint8_t, base, layout.off_out0),
|
||||
VTCM_LAYOUT_PTR(uint8_t, base, layout.off_out1),
|
||||
};
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_INIT, (uint16_t) r_beg);
|
||||
gather_rows_init_tables_f32(tab, inc, kparams, a, b);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_INIT, (uint16_t) r_beg);
|
||||
|
||||
dma_queue * q = task->octx->ctx->dma[ith];
|
||||
const uint32_t n_in = 1 + (task->b ? (kparams->b_dense ? 1 : kparams->nb) : 0);
|
||||
const uint32_t nr_max = kparams->nr_max;
|
||||
const uint32_t nr0 = MIN(nr_max, r_end - r_beg);
|
||||
|
||||
dma_queue_push(q, dma_make_data(task->dst, out[0]), 0, 0, 0, 0); // dummy out
|
||||
gather_rows_dma_in(q, task, region[0], r_beg, nr0, 4);
|
||||
|
||||
const uint32_t r1 = r_beg + nr_max;
|
||||
if (r1 < r_end) {
|
||||
const uint32_t nr1 = MIN(nr_max, r_end - r1);
|
||||
dma_queue_push(q, dma_make_data(task->dst, out[1]), 0, 0, 0, 0); // dummy out
|
||||
gather_rows_dma_in(q, task, region[1], r1, nr1, 4);
|
||||
}
|
||||
|
||||
uint32_t buf = 0;
|
||||
for (uint32_t r = r_beg; r < r_end; r += nr_max) {
|
||||
const uint32_t nr = MIN(nr_max, r_end - r);
|
||||
const uint32_t next_buf = buf ^ 1;
|
||||
|
||||
dma_queue_pop(q); // complete out
|
||||
for (uint32_t i = 0; i < n_in; i++) {
|
||||
dma_queue_pop(q);
|
||||
}
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r);
|
||||
const uint32_t nvec = (nr * kparams->ne + 31) >> 5;
|
||||
task->compute(out[buf], region[buf], nvec, kparams, tab, inc, cur);
|
||||
hvx_gather_sync(out[buf]);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r);
|
||||
|
||||
gather_rows_dma_out(q, task, out[buf], r, nr, 4);
|
||||
|
||||
const uint32_t r_prefetch = r + 2 * nr_max;
|
||||
if (r_prefetch < r_end) {
|
||||
const uint32_t nr_prefetch = MIN(nr_max, r_end - r_prefetch);
|
||||
gather_rows_dma_in(q, task, region[buf], r_prefetch, nr_prefetch, 4);
|
||||
}
|
||||
|
||||
buf = next_buf;
|
||||
}
|
||||
dma_queue_flush(q);
|
||||
}
|
||||
|
||||
static void gather_rows_thread_f16(unsigned int nth, unsigned int ith, void * data) {
|
||||
(void) nth;
|
||||
const struct gather_rows_task * task = (const struct gather_rows_task *) data;
|
||||
const struct htp_gather_rows_params * kparams = task->kparams;
|
||||
const struct htp_tensor * a = task->a;
|
||||
const struct htp_tensor * b = task->b;
|
||||
|
||||
const uint32_t r_offset = MIN(ith * task->rows_per_thread, task->rows);
|
||||
const uint32_t r_beg = task->r_start + r_offset;
|
||||
const uint32_t r_end = task->r_start + MIN(r_offset + task->rows_per_thread, task->rows);
|
||||
if (r_beg >= r_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &task->octx->ctx->trace[ith];
|
||||
|
||||
struct htp_gather_rows_vtcm_layout layout;
|
||||
htp_gather_rows_vtcm_layout_build(&layout, kparams->vg, kparams->region_size, kparams->nr_max, kparams->ne, 2);
|
||||
|
||||
uint8_t * base = VTCM_LAYOUT_PTR(uint8_t, task->octx->ctx->vtcm_base, (size_t) ith * kparams->slice);
|
||||
HVX_Vector * tab = VTCM_LAYOUT_PTR(HVX_Vector, base, layout.off_tab);
|
||||
HVX_Vector * inc = VTCM_LAYOUT_PTR(HVX_Vector, base, layout.off_inc);
|
||||
HVX_Vector * cur = VTCM_LAYOUT_PTR(HVX_Vector, base, layout.off_cur);
|
||||
|
||||
uint8_t * region[2] = {
|
||||
VTCM_LAYOUT_PTR(uint8_t, base, layout.off_region0),
|
||||
VTCM_LAYOUT_PTR(uint8_t, base, layout.off_region1),
|
||||
};
|
||||
uint8_t * out[2] = {
|
||||
VTCM_LAYOUT_PTR(uint8_t, base, layout.off_out0),
|
||||
VTCM_LAYOUT_PTR(uint8_t, base, layout.off_out1),
|
||||
};
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_INIT, (uint16_t) r_beg);
|
||||
gather_rows_init_tables_f16(tab, inc, kparams, a, b);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_INIT, (uint16_t) r_beg);
|
||||
|
||||
dma_queue * q = task->octx->ctx->dma[ith];
|
||||
const uint32_t n_in = 1 + (task->b ? (kparams->b_dense ? 1 : kparams->nb) : 0);
|
||||
|
||||
const uint32_t nr_max = kparams->nr_max;
|
||||
const uint32_t nr0 = MIN(nr_max, r_end - r_beg);
|
||||
dma_queue_push(q, dma_make_data(task->dst, out[0]), 0, 0, 0, 0); // dummy out
|
||||
gather_rows_dma_in(q, task, region[0], r_beg, nr0, 2);
|
||||
|
||||
const uint32_t r1 = r_beg + nr_max;
|
||||
if (r1 < r_end) {
|
||||
const uint32_t nr1 = MIN(nr_max, r_end - r1);
|
||||
dma_queue_push(q, dma_make_data(task->dst, out[1]), 0, 0, 0, 0); // dummy out
|
||||
gather_rows_dma_in(q, task, region[1], r1, nr1, 2);
|
||||
}
|
||||
|
||||
uint32_t buf = 0;
|
||||
for (uint32_t r = r_beg; r < r_end; r += nr_max) {
|
||||
const uint32_t nr = MIN(nr_max, r_end - r);
|
||||
const uint32_t next_buf = buf ^ 1;
|
||||
|
||||
dma_queue_pop(q); // complete out
|
||||
for (uint32_t i = 0; i < n_in; i++) {
|
||||
dma_queue_pop(q);
|
||||
}
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r);
|
||||
const uint32_t nvec = (nr * kparams->ne + 63) >> 6;
|
||||
task->compute(out[buf], region[buf], nvec, kparams, tab, inc, cur);
|
||||
hvx_gather_sync(out[buf]);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) r);
|
||||
|
||||
gather_rows_dma_out(q, task, out[buf], r, nr, 2);
|
||||
|
||||
const uint32_t r_prefetch = r + 2 * nr_max;
|
||||
if (r_prefetch < r_end) {
|
||||
const uint32_t nr_prefetch = MIN(nr_max, r_end - r_prefetch);
|
||||
gather_rows_dma_in(q, task, region[buf], r_prefetch, nr_prefetch, 2);
|
||||
}
|
||||
|
||||
buf = next_buf;
|
||||
}
|
||||
dma_queue_flush(q);
|
||||
}
|
||||
|
||||
int htp_gather_rows(
|
||||
struct htp_ops_context * octx,
|
||||
const struct htp_tensor * a,
|
||||
const struct htp_tensor * b,
|
||||
const struct htp_gather_rows_params * kparams,
|
||||
uint32_t n_threads) {
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
assert(kparams->slice * octx->n_threads <= octx->ctx->vtcm_size);
|
||||
|
||||
const uint32_t total_rows = kparams->total_rows;
|
||||
uint32_t r_start = 0;
|
||||
uint32_t r_count = total_rows;
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
total_rows, kparams->rg, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
r_start = range.start;
|
||||
r_count = range.count;
|
||||
}
|
||||
|
||||
if (r_count == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
const uint32_t elem_size = (a->type == HTP_TYPE_F16) ? 2 : 4;
|
||||
|
||||
struct gather_rows_task task;
|
||||
task.octx = octx;
|
||||
task.a = a;
|
||||
task.b = b;
|
||||
task.dst = octx->dst->data;
|
||||
task.r_start = r_start;
|
||||
task.rows = r_count;
|
||||
task.kparams = kparams;
|
||||
task.rows_per_thread = kparams->rows_per_thread;
|
||||
|
||||
if (elem_size == 2) {
|
||||
task.compute = (kparams->vg == 1) ? gather_rows_compute_f16_vg1 : gather_rows_compute_f16_vgn;
|
||||
} else {
|
||||
task.compute = (kparams->vg == 1) ? gather_rows_compute_f32_vg1 : gather_rows_compute_f32_vgn;
|
||||
}
|
||||
|
||||
work_queue_func_t worker_func = (elem_size == 2) ? gather_rows_thread_f16 : gather_rows_thread_f32;
|
||||
work_queue_run(octx->ctx->work_queue, worker_func, &task, octx->n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
#ifndef HTP_GATHER_ROWS_H
|
||||
#define HTP_GATHER_ROWS_H
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef GGML_MAX_DIMS
|
||||
#define GGML_MAX_DIMS 4
|
||||
#endif
|
||||
|
||||
struct htp_gather_rows_params {
|
||||
uint32_t na;
|
||||
uint32_t nb;
|
||||
uint32_t ne;
|
||||
uint32_t rg;
|
||||
uint32_t vg;
|
||||
uint32_t nr_max;
|
||||
uint32_t v1;
|
||||
uint32_t span1;
|
||||
uint32_t region_size;
|
||||
uint32_t slice;
|
||||
uint32_t total_rows;
|
||||
uint32_t rows_per_thread;
|
||||
uint8_t b_dense;
|
||||
uint8_t pad[3];
|
||||
};
|
||||
|
||||
struct htp_gather_rows_vtcm_layout {
|
||||
size_t off_tab;
|
||||
size_t off_inc;
|
||||
size_t off_cur;
|
||||
size_t off_region0;
|
||||
size_t off_out0;
|
||||
size_t off_region1;
|
||||
size_t off_out1;
|
||||
size_t total_per_thread;
|
||||
};
|
||||
|
||||
static inline void htp_gather_rows_vtcm_layout_build(
|
||||
struct htp_gather_rows_vtcm_layout * L,
|
||||
uint32_t vg,
|
||||
uint32_t region_size,
|
||||
uint32_t nr_max,
|
||||
uint32_t ne,
|
||||
uint32_t elem_size) {
|
||||
|
||||
const size_t vec_bytes = (size_t) vg * 128;
|
||||
const size_t region_aligned = hex_round_up(region_size, 128);
|
||||
const size_t out_aligned = hex_round_up(region_aligned + (size_t) nr_max * ne * elem_size, 128) - region_aligned;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_tab, vec_bytes);
|
||||
VTCM_LAYOUT_ALLOC(off, off_inc, vec_bytes);
|
||||
VTCM_LAYOUT_ALLOC(off, off_cur, vec_bytes);
|
||||
VTCM_LAYOUT_ALLOC(off, off_region0, region_aligned);
|
||||
VTCM_LAYOUT_ALLOC(off, off_out0, out_aligned);
|
||||
VTCM_LAYOUT_ALLOC(off, off_region1, region_aligned);
|
||||
VTCM_LAYOUT_ALLOC(off, off_out1, out_aligned);
|
||||
L->total_per_thread = off;
|
||||
}
|
||||
|
||||
static inline bool htp_gather_rows_solve_layout(
|
||||
struct htp_gather_rows_params * kparams,
|
||||
uint32_t na, uint32_t a_nb1,
|
||||
uint32_t nb, uint32_t b_nb1, bool b_dense,
|
||||
uint32_t elem_size,
|
||||
uint32_t total_rows,
|
||||
uint32_t n_threads,
|
||||
uint32_t mdev_count,
|
||||
uint32_t vtcm_size) {
|
||||
|
||||
const uint32_t max_ne0 = 128 / elem_size;
|
||||
const uint32_t ne = na + nb;
|
||||
if (ne == 0 || ne > max_ne0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elem_size != 4 && elem_size != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t v_lanes = 128 / elem_size;
|
||||
const uint32_t g = hex_gcd_u32(ne, v_lanes);
|
||||
const uint32_t rg = v_lanes / g;
|
||||
const uint32_t vg = ne / g;
|
||||
|
||||
const uint32_t slice = (vtcm_size / n_threads) & ~127u;
|
||||
const uint32_t total_chunks = total_rows / rg;
|
||||
const uint32_t chunks_per_dev = (total_chunks + mdev_count - 1) / mdev_count;
|
||||
const uint32_t max_rows_per_dev = (mdev_count > 1 && total_chunks >= mdev_count)
|
||||
? (chunks_per_dev * rg + (total_rows % rg))
|
||||
: total_rows;
|
||||
const uint32_t rows_per_thread = hex_round_up((max_rows_per_dev + n_threads - 1) / n_threads, rg);
|
||||
|
||||
const uint32_t fixed = 3 * vg * 128;
|
||||
const uint32_t per_row = a_nb1 + (nb > 0 ? (b_dense ? b_nb1 : nb * b_nb1) : 0) + ne * elem_size;
|
||||
if (slice <= fixed + 2 * per_row * rg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t avail_per_buf = (slice - fixed) / 2;
|
||||
uint32_t nr_max = MIN(avail_per_buf / per_row, rows_per_thread);
|
||||
if (elem_size == 2) {
|
||||
const uint32_t per_row_region = a_nb1 + (nb > 0 ? (b_dense ? b_nb1 : nb * b_nb1) : 0);
|
||||
if (per_row_region > 0) {
|
||||
nr_max = MIN(nr_max, (32768u - 256u) / per_row_region);
|
||||
}
|
||||
}
|
||||
nr_max = (nr_max / rg) * rg;
|
||||
|
||||
uint32_t v1 = 0;
|
||||
uint32_t span1 = 0;
|
||||
uint32_t region_size = 0;
|
||||
|
||||
while (nr_max > 0) {
|
||||
v1 = hex_round_up((nr_max - 1) * a_nb1 + na * elem_size, 128);
|
||||
if (nb > 0) {
|
||||
if (b_dense) {
|
||||
span1 = elem_size;
|
||||
region_size = v1 + (nr_max - 1) * b_nb1 + nb * elem_size;
|
||||
} else {
|
||||
span1 = (nr_max - 1) * b_nb1 + elem_size;
|
||||
region_size = v1 + nb * span1;
|
||||
}
|
||||
} else {
|
||||
span1 = 0;
|
||||
region_size = v1;
|
||||
}
|
||||
|
||||
struct htp_gather_rows_vtcm_layout layout;
|
||||
htp_gather_rows_vtcm_layout_build(&layout, vg, region_size, nr_max, ne, elem_size);
|
||||
|
||||
if ((elem_size != 2 || region_size <= 32768) && layout.total_per_thread <= slice) {
|
||||
break;
|
||||
}
|
||||
|
||||
nr_max -= rg;
|
||||
}
|
||||
|
||||
if (nr_max == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
kparams->na = na;
|
||||
kparams->nb = nb;
|
||||
kparams->ne = ne;
|
||||
kparams->rg = rg;
|
||||
kparams->vg = vg;
|
||||
kparams->nr_max = nr_max;
|
||||
kparams->v1 = v1;
|
||||
kparams->span1 = span1;
|
||||
kparams->region_size = region_size;
|
||||
kparams->slice = slice;
|
||||
kparams->total_rows = total_rows;
|
||||
kparams->rows_per_thread = rows_per_thread;
|
||||
kparams->b_dense = b_dense ? 1 : 0;
|
||||
kparams->pad[0] = 0;
|
||||
kparams->pad[1] = 0;
|
||||
kparams->pad[2] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
struct ggml_tensor;
|
||||
static inline bool htp_gather_rows_tensor_outer_contiguous(const struct ggml_tensor * t) {
|
||||
size_t next_nb = ((const size_t *) t->nb)[1] * ((const int64_t *) t->ne)[1];
|
||||
for (int i = 2; i < GGML_MAX_DIMS; i++) {
|
||||
if (((const int64_t *) t->ne)[i] != 1 && ((const size_t *) t->nb)[i] != next_nb) {
|
||||
return false;
|
||||
}
|
||||
next_nb *= ((const int64_t *) t->ne)[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
struct htp_ops_context;
|
||||
struct htp_tensor;
|
||||
|
||||
int htp_gather_rows(
|
||||
struct htp_ops_context * octx,
|
||||
const struct htp_tensor * a,
|
||||
const struct htp_tensor * b,
|
||||
const struct htp_gather_rows_params * kparams,
|
||||
uint32_t n_threads);
|
||||
#endif
|
||||
|
||||
#endif // HTP_GATHER_ROWS_H
|
||||
@@ -0,0 +1,510 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <HAP_perf.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "hex-common.h"
|
||||
#include "dma-queue.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "hvx-quant.h"
|
||||
#include "get-rows-ops.h"
|
||||
#include "work-queue.h"
|
||||
|
||||
struct get_rows_context;
|
||||
|
||||
typedef uint32_t (*get_rows_read_index_fn_t)(const void * src1, uint32_t idx, int idx_type);
|
||||
typedef void (*get_rows_flat_compute_fn_t)(float * dst_spad, const void * src_spad, uint32_t cur_elems);
|
||||
typedef void (*get_rows_tiled_compute_fn_t)(float * dst_spad, const uint8_t * src_spad, uint32_t n_k_tiles, uint32_t tile_stride, uint32_t row);
|
||||
|
||||
struct get_rows_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_get_rows_kernel_params * kparams;
|
||||
struct htp_get_rows_vtcm_layout vtcm_layout;
|
||||
uint8_t * vtcm_base;
|
||||
const void * src1_data;
|
||||
get_rows_read_index_fn_t read_index;
|
||||
get_rows_flat_compute_fn_t compute_flat;
|
||||
get_rows_tiled_compute_fn_t compute_tiled;
|
||||
uint32_t task_start;
|
||||
uint32_t tasks;
|
||||
uint32_t tasks_per_thread;
|
||||
uint32_t tile_size;
|
||||
uint32_t tile_stride;
|
||||
uint32_t src0_row_size;
|
||||
uint32_t dst_row_size;
|
||||
int idx_type;
|
||||
};
|
||||
|
||||
#define get_rows_preamble \
|
||||
const uint32_t ne00 = octx->src[0]->ne[0]; \
|
||||
const uint32_t ne01 = octx->src[0]->ne[1]; \
|
||||
const uint32_t ne02 = octx->src[0]->ne[2]; \
|
||||
const uint32_t ne03 = octx->src[0]->ne[3]; \
|
||||
\
|
||||
const uint32_t ne10 = octx->src[1]->ne[0]; \
|
||||
const uint32_t ne11 = octx->src[1]->ne[1]; \
|
||||
const uint32_t ne12 = octx->src[1]->ne[2]; \
|
||||
const uint32_t ne13 = octx->src[1]->ne[3]; \
|
||||
\
|
||||
const uint32_t ne0 = octx->dst->ne[0]; \
|
||||
const uint32_t ne1 = octx->dst->ne[1]; \
|
||||
const uint32_t ne2 = octx->dst->ne[2]; \
|
||||
const uint32_t ne3 = octx->dst->ne[3]; \
|
||||
\
|
||||
const uint32_t nb01 = octx->src[0]->nb[1]; \
|
||||
const uint32_t nb02 = octx->src[0]->nb[2]; \
|
||||
const uint32_t nb03 = octx->src[0]->nb[3]; \
|
||||
\
|
||||
const uint32_t nb10 = octx->src[1]->nb[0]; \
|
||||
const uint32_t nb11 = octx->src[1]->nb[1]; \
|
||||
const uint32_t nb12 = octx->src[1]->nb[2]; \
|
||||
\
|
||||
const uint32_t nb1 = octx->dst->nb[1]; \
|
||||
const uint32_t nb2 = octx->dst->nb[2]; \
|
||||
const uint32_t nb3 = octx->dst->nb[3]; \
|
||||
\
|
||||
const uint32_t nr = ne10 * ne11 * ne12;
|
||||
|
||||
static uint32_t get_rows_read_index_ddr(
|
||||
const void * src1,
|
||||
uint32_t idx,
|
||||
int idx_type
|
||||
) {
|
||||
const uint32_t off = idx * ((idx_type == HTP_TYPE_I64) ? sizeof(int64_t) : sizeof(int32_t));
|
||||
return *(const uint32_t *) ((const uint8_t *) src1 + off);
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) uint32_t get_rows_read_index_vtcm(
|
||||
const void * src1,
|
||||
uint32_t idx,
|
||||
int idx_type
|
||||
) {
|
||||
const uint32_t off = idx * ((idx_type == HTP_TYPE_I64) ? sizeof(int64_t) : sizeof(int32_t));
|
||||
const HVX_Vector v = hvx_vmem((const uint8_t *) src1 + off); // off & ~127u in HW
|
||||
// vextract is normally slow, but here it avoids an HVX->L2 store and L1D miss
|
||||
return (uint32_t) Q6_R_vextract_VR(v, off);
|
||||
}
|
||||
|
||||
static void get_rows_thread_sametype(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct get_rows_context * grctx = (struct get_rows_context *) data;
|
||||
struct htp_ops_context * octx = grctx->octx;
|
||||
const struct htp_get_rows_kernel_params * kparams = grctx->kparams;
|
||||
get_rows_preamble;
|
||||
|
||||
const uint32_t dr = grctx->tasks_per_thread;
|
||||
const uint32_t ir0 = grctx->task_start + dr * ith;
|
||||
if (ir0 >= grctx->task_start + grctx->tasks) {
|
||||
return;
|
||||
}
|
||||
const uint32_t ir1 = MIN(ir0 + dr, grctx->task_start + grctx->tasks);
|
||||
const uint32_t row_size_bytes = grctx->src0_row_size;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
|
||||
for (uint32_t i = ir0; i < ir1; ++i) {
|
||||
const uint32_t i12 = fastdiv(i, &kparams->div_ne10_ne11);
|
||||
const uint32_t rem = i - i12 * ne11 * ne10;
|
||||
const uint32_t i11 = fastdiv(rem, &kparams->div_ne10);
|
||||
const uint32_t i10 = rem - i11 * ne10;
|
||||
|
||||
const uint32_t i01 = grctx->read_index(grctx->src1_data, i, grctx->idx_type);
|
||||
assert(i01 < ne01);
|
||||
|
||||
const uint32_t q02 = fastdiv(i11, &kparams->div_ne02);
|
||||
const uint32_t i02 = i11 - q02 * ne02;
|
||||
const uint32_t q03 = fastdiv(i12, &kparams->div_ne03);
|
||||
const uint32_t i03 = i12 - q03 * ne03;
|
||||
|
||||
const dma_addr_t src0_data = octx->src[0]->data + i01*nb01 + i02*nb02 + i03*nb03;
|
||||
const dma_addr_t dst_data = octx->dst->data + i10*nb1 + i11*nb2 + i12*nb3;
|
||||
|
||||
while (!dma_queue_push(dma_q, dma_make_data(dst_data, src0_data), nb1, nb01, row_size_bytes, 1)) {
|
||||
dma_queue_pop(dma_q);
|
||||
}
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct get_rows_flat_task {
|
||||
dma_addr_t src0_data;
|
||||
dma_addr_t dst_data;
|
||||
};
|
||||
|
||||
static inline struct get_rows_flat_task get_rows_flat_calc_task(
|
||||
const struct htp_ops_context * octx,
|
||||
const struct get_rows_context * grctx,
|
||||
uint32_t i,
|
||||
bool need_src0
|
||||
) {
|
||||
const struct htp_get_rows_kernel_params * kparams = grctx->kparams;
|
||||
get_rows_preamble;
|
||||
|
||||
const uint32_t i12 = fastdiv(i, &kparams->div_ne10_ne11);
|
||||
const uint32_t rem = i - i12 * ne11 * ne10;
|
||||
const uint32_t i11 = fastdiv(rem, &kparams->div_ne10);
|
||||
const uint32_t i10 = rem - i11 * ne10;
|
||||
|
||||
struct get_rows_flat_task task;
|
||||
task.src0_data = 0;
|
||||
task.dst_data = octx->dst->data + i10*nb1 + i11*nb2 + i12*nb3;
|
||||
|
||||
if (need_src0) {
|
||||
const uint32_t i01 = grctx->read_index(grctx->src1_data, i, grctx->idx_type);
|
||||
assert(i01 < ne01);
|
||||
|
||||
const uint32_t q02 = fastdiv(i11, &kparams->div_ne02);
|
||||
const uint32_t i02 = i11 - q02 * ne02;
|
||||
const uint32_t q03 = fastdiv(i12, &kparams->div_ne03);
|
||||
const uint32_t i03 = i12 - q03 * ne03;
|
||||
|
||||
task.src0_data = octx->src[0]->data + i01*nb01 + i02*nb02 + i03*nb03;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
static void get_rows_thread_flat(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct get_rows_context * grctx = (struct get_rows_context *) data;
|
||||
struct htp_ops_context * octx = grctx->octx;
|
||||
get_rows_preamble;
|
||||
|
||||
const uint32_t dr = grctx->tasks_per_thread;
|
||||
const uint32_t ir0 = grctx->task_start + dr * ith;
|
||||
if (ir0 >= grctx->task_start + grctx->tasks) {
|
||||
return;
|
||||
}
|
||||
const uint32_t ir1 = MIN(ir0 + dr, grctx->task_start + grctx->tasks);
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
const struct htp_get_rows_vtcm_layout * vtcm_layout = &grctx->vtcm_layout;
|
||||
uint8_t * vtcm_src0 = VTCM_LAYOUT_PTR(uint8_t, grctx->vtcm_base, vtcm_layout->off_src0) + ith * vtcm_layout->src0_bytes_per_thread;
|
||||
uint8_t * vtcm_dst = VTCM_LAYOUT_PTR(uint8_t, grctx->vtcm_base, vtcm_layout->off_dst) + ith * vtcm_layout->dst_bytes_per_thread;
|
||||
const uint32_t src0_bytes = grctx->src0_row_size;
|
||||
const uint32_t dst_bytes = grctx->dst_row_size;
|
||||
get_rows_flat_compute_fn_t compute = grctx->compute_flat;
|
||||
|
||||
for (uint32_t step = 0, spad_idx = 0; step < ir1 - ir0 && spad_idx < 2; ++step, spad_idx++) {
|
||||
const uint32_t i = ir0 + step;
|
||||
struct get_rows_flat_task task = get_rows_flat_calc_task(octx, grctx, i, true);
|
||||
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(octx->dst->data,
|
||||
vtcm_dst + spad_idx * vtcm_layout->dst_spad_half_size),
|
||||
dst_bytes, vtcm_layout->dst_spad_half_size, dst_bytes, 0);
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(vtcm_src0 + spad_idx * vtcm_layout->src0_spad_half_size, task.src0_data),
|
||||
vtcm_layout->src0_spad_half_size, src0_bytes, src0_bytes, 1);
|
||||
}
|
||||
|
||||
for (uint32_t step = 0; step < ir1 - ir0; ++step) {
|
||||
const uint32_t i = ir0 + step;
|
||||
void * dst_spad = (void *) dma_queue_pop(dma_q).src;
|
||||
void * src_spad = (void *) dma_queue_pop(dma_q).dst;
|
||||
|
||||
struct get_rows_flat_task task = get_rows_flat_calc_task(octx, grctx, i, false);
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
compute((float *) dst_spad, src_spad, ne00);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(task.dst_data, dst_spad),
|
||||
dst_bytes, vtcm_layout->dst_spad_half_size, dst_bytes, 1);
|
||||
|
||||
const uint32_t next_step = step + 2;
|
||||
if (next_step < ir1 - ir0) {
|
||||
const uint32_t pi = ir0 + next_step;
|
||||
struct get_rows_flat_task next_task = get_rows_flat_calc_task(octx, grctx, pi, true);
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad, next_task.src0_data),
|
||||
vtcm_layout->src0_spad_half_size, src0_bytes, src0_bytes, 1);
|
||||
}
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct get_rows_tiled_task {
|
||||
dma_addr_t tile_src_base;
|
||||
dma_addr_t dst_data;
|
||||
uint32_t row;
|
||||
};
|
||||
|
||||
static inline struct get_rows_tiled_task get_rows_tiled_calc_task(
|
||||
const struct htp_ops_context * octx,
|
||||
const struct get_rows_context * grctx,
|
||||
uint32_t i,
|
||||
uint32_t n_k_tiles,
|
||||
uint32_t tile_size
|
||||
) {
|
||||
const struct htp_get_rows_kernel_params * kparams = grctx->kparams;
|
||||
get_rows_preamble;
|
||||
|
||||
const uint32_t i12 = fastdiv(i, &kparams->div_ne10_ne11);
|
||||
const uint32_t rem = i - i12 * ne11 * ne10;
|
||||
const uint32_t i11 = fastdiv(rem, &kparams->div_ne10);
|
||||
const uint32_t i10 = rem - i11 * ne10;
|
||||
const uint32_t i01 = grctx->read_index(grctx->src1_data, i, grctx->idx_type);
|
||||
assert(i01 < ne01);
|
||||
|
||||
const uint32_t q02 = fastdiv(i11, &kparams->div_ne02);
|
||||
const uint32_t i02 = i11 - q02 * ne02;
|
||||
const uint32_t q03 = fastdiv(i12, &kparams->div_ne03);
|
||||
const uint32_t i03 = i12 - q03 * ne03;
|
||||
const uint32_t column_tile = i01 / HTP_TENSOR_TILE_N_ROWS;
|
||||
const uint32_t row = i01 % HTP_TENSOR_TILE_N_ROWS;
|
||||
const dma_addr_t matrix = octx->src[0]->data + i02*nb02 + i03*nb03;
|
||||
|
||||
struct get_rows_tiled_task task;
|
||||
task.tile_src_base = matrix + (column_tile * n_k_tiles) * tile_size;
|
||||
task.dst_data = octx->dst->data + i10*nb1 + i11*nb2 + i12*nb3;
|
||||
task.row = row;
|
||||
return task;
|
||||
}
|
||||
|
||||
static void get_rows_thread_tiled(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct get_rows_context * grctx = (struct get_rows_context *) data;
|
||||
struct htp_ops_context * octx = grctx->octx;
|
||||
get_rows_preamble;
|
||||
|
||||
const uint32_t dr = grctx->tasks_per_thread;
|
||||
const uint32_t ir0 = grctx->task_start + dr * ith;
|
||||
if (ir0 >= grctx->task_start + grctx->tasks) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t ir1 = MIN(ir0 + dr, grctx->task_start + grctx->tasks);
|
||||
const uint32_t n_k_tiles = ne00 / htp_tensor_get_tile_k(octx->src[0]->type);
|
||||
const struct htp_get_rows_vtcm_layout * vtcm_layout = &grctx->vtcm_layout;
|
||||
uint8_t * src_spad_base = VTCM_LAYOUT_PTR(uint8_t, grctx->vtcm_base, vtcm_layout->off_src0) + ith * vtcm_layout->src0_bytes_per_thread;
|
||||
uint8_t * dst_spad_base = VTCM_LAYOUT_PTR(uint8_t, grctx->vtcm_base, vtcm_layout->off_dst) + ith * vtcm_layout->dst_bytes_per_thread;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
const uint32_t tile_size = grctx->tile_size;
|
||||
const uint32_t tile_stride = grctx->tile_stride;
|
||||
const uint32_t dst_bytes = grctx->dst_row_size;
|
||||
get_rows_tiled_compute_fn_t compute_tiled = grctx->compute_tiled;
|
||||
|
||||
for (uint32_t step = 0, spad_idx = 0; step < ir1 - ir0 && spad_idx < 2; ++step, ++spad_idx) {
|
||||
const uint32_t i = ir0 + step;
|
||||
struct get_rows_tiled_task task = get_rows_tiled_calc_task(octx, grctx, i, n_k_tiles, tile_size);
|
||||
|
||||
// Dummy writeback to prime the queue with dst descriptor
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(task.dst_data, dst_spad_base + spad_idx * vtcm_layout->dst_spad_half_size),
|
||||
dst_bytes, vtcm_layout->dst_spad_half_size, dst_bytes, 0);
|
||||
|
||||
// Prefetch row tiles
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad_base + spad_idx * vtcm_layout->src0_spad_half_size, task.tile_src_base),
|
||||
tile_stride, tile_size, tile_size, n_k_tiles);
|
||||
}
|
||||
|
||||
for (uint32_t step = 0; step < ir1 - ir0; ++step) {
|
||||
const uint32_t i = ir0 + step;
|
||||
float * dst_spad = (float *) dma_queue_pop(dma_q).src;
|
||||
uint8_t * src_spad = (uint8_t *) dma_queue_pop(dma_q).dst;
|
||||
|
||||
struct get_rows_tiled_task task = get_rows_tiled_calc_task(octx, grctx, i, n_k_tiles, tile_size);
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
compute_tiled(dst_spad, src_spad, n_k_tiles, tile_stride, task.row);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) i);
|
||||
|
||||
// Real writeback of dst_spad
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(task.dst_data, dst_spad),
|
||||
dst_bytes, vtcm_layout->dst_spad_half_size, dst_bytes, 1);
|
||||
|
||||
const uint32_t next_step = step + 2;
|
||||
if (next_step < ir1 - ir0) {
|
||||
const uint32_t ni = ir0 + next_step;
|
||||
struct get_rows_tiled_task next_task = get_rows_tiled_calc_task(octx, grctx, ni, n_k_tiles, tile_size);
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad, next_task.tile_src_base),
|
||||
tile_stride, tile_size, tile_size, n_k_tiles);
|
||||
}
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) void get_rows_dma_src1(
|
||||
struct htp_ops_context * octx,
|
||||
uint8_t * vtcm_src1,
|
||||
uint32_t elem_size
|
||||
) {
|
||||
const uint32_t ne10 = octx->src[1]->ne[0];
|
||||
const uint32_t ne11 = octx->src[1]->ne[1];
|
||||
const uint32_t ne12 = octx->src[1]->ne[2];
|
||||
const uint32_t ne13 = octx->src[1]->ne[3];
|
||||
const uint32_t nb11 = octx->src[1]->nb[1];
|
||||
const uint32_t nb12 = octx->src[1]->nb[2];
|
||||
const uint32_t nb13 = octx->src[1]->nb[3];
|
||||
|
||||
dma_queue * dma_q = octx->ctx->dma[0];
|
||||
uint8_t * cur_dst = vtcm_src1;
|
||||
for (uint32_t i13 = 0; i13 < ne13; ++i13) {
|
||||
for (uint32_t i12 = 0; i12 < ne12; ++i12) {
|
||||
const dma_addr_t src_plane = octx->src[1]->data + i12 * nb12 + i13 * nb13;
|
||||
while (!dma_queue_push(dma_q, dma_make_data(cur_dst, src_plane),
|
||||
ne10 * elem_size, nb11, ne10 * elem_size, ne11)) {
|
||||
dma_queue_pop(dma_q);
|
||||
}
|
||||
cur_dst += ne10 * ne11 * elem_size;
|
||||
}
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
int op_get_rows(struct htp_ops_context * octx) {
|
||||
const struct htp_get_rows_kernel_params * kparams = (const struct htp_get_rows_kernel_params *) octx->kernel_params;
|
||||
|
||||
const uint32_t ne00 = octx->src[0]->ne[0];
|
||||
const uint32_t src0_row_size = htp_tensor_get_row_size(octx->src[0]->type, ne00);
|
||||
if (src0_row_size == 0) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
if (kparams->kernel_type == HTP_GET_ROWS_KERNEL_SAMETYPE) {
|
||||
if (octx->src[0]->type != octx->dst->type) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
} else {
|
||||
if (octx->dst->type != HTP_TYPE_F32) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (octx->src[1]->type != HTP_TYPE_I32 && octx->src[1]->type != HTP_TYPE_I64) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const uint32_t total_tasks = kparams->total_tasks;
|
||||
const uint32_t dst_row_size = (kparams->kernel_type == HTP_GET_ROWS_KERNEL_SAMETYPE) ? src0_row_size : ne00 * sizeof(float);
|
||||
|
||||
uint32_t task_start = 0;
|
||||
uint32_t tasks = total_tasks;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
uint32_t tasks_per_chunk = 1;
|
||||
htp_tensor_mdev_rows_per_chunk(dst, (uint32_t) dst->nb[0], dst_row_size, &tasks_per_chunk);
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(total_tasks, tasks_per_chunk, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
task_start = range.start;
|
||||
tasks = range.count;
|
||||
}
|
||||
|
||||
if (tasks == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, kparams->n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
struct get_rows_context grctx;
|
||||
grctx.octx = octx;
|
||||
grctx.kparams = kparams;
|
||||
grctx.vtcm_base = (uint8_t *)octx->ctx->vtcm_base;
|
||||
grctx.compute_flat = NULL;
|
||||
grctx.compute_tiled = NULL;
|
||||
grctx.task_start = task_start;
|
||||
grctx.tasks = tasks;
|
||||
grctx.tasks_per_thread = fastdiv(tasks + n_threads - 1, &octx->n_threads_div);
|
||||
grctx.tile_size = 0;
|
||||
grctx.tile_stride = 0;
|
||||
grctx.src0_row_size = src0_row_size;
|
||||
grctx.dst_row_size = dst_row_size;
|
||||
grctx.idx_type = octx->src[1]->type;
|
||||
|
||||
htp_get_rows_vtcm_layout_build(&grctx.vtcm_layout, kparams->kernel_type, octx->src[0]->type, ne00, total_tasks, grctx.idx_type, n_threads);
|
||||
|
||||
if (grctx.vtcm_layout.total_bytes > octx->ctx->vtcm_size) {
|
||||
FARF(ERROR, "get-rows: VTCM reservation %zu is too small, needed %zu\n",
|
||||
octx->ctx->vtcm_size, grctx.vtcm_layout.total_bytes);
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
work_queue_func_t q_func = NULL;
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_GET_ROWS_KERNEL_SAMETYPE:
|
||||
q_func = get_rows_thread_sametype;
|
||||
break;
|
||||
case HTP_GET_ROWS_KERNEL_TILED:
|
||||
switch (octx->src[0]->type) {
|
||||
case HTP_TYPE_F16: grctx.compute_tiled = hvx_dequantize_row_tiled_f16_f32; break;
|
||||
case HTP_TYPE_Q1_0: grctx.compute_tiled = hvx_dequantize_row_tiled_q1_0_f32; break;
|
||||
case HTP_TYPE_Q2_0: grctx.compute_tiled = hvx_dequantize_row_tiled_q2_0_f32; break;
|
||||
case HTP_TYPE_Q8_0: grctx.compute_tiled = hvx_dequantize_row_tiled_q8_0_f32; break;
|
||||
case HTP_TYPE_Q4_0: grctx.compute_tiled = hvx_dequantize_row_tiled_q4_0_f32; break;
|
||||
case HTP_TYPE_Q4_K: grctx.compute_tiled = hvx_dequantize_row_tiled_q4_k_f32; break;
|
||||
case HTP_TYPE_Q5_K: grctx.compute_tiled = hvx_dequantize_row_tiled_q5_k_f32; break;
|
||||
case HTP_TYPE_Q6_K: grctx.compute_tiled = hvx_dequantize_row_tiled_q6_k_f32; break;
|
||||
default: return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
grctx.tile_size = htp_tensor_get_tile_size(octx->src[0]->type);
|
||||
grctx.tile_stride = htp_tensor_get_tile_padded_size(octx->src[0]->type);
|
||||
q_func = get_rows_thread_tiled;
|
||||
break;
|
||||
case HTP_GET_ROWS_KERNEL_FLAT:
|
||||
switch (octx->src[0]->type) {
|
||||
case HTP_TYPE_F16: grctx.compute_flat = hvx_dequantize_row_flat_f16_f32; break;
|
||||
case HTP_TYPE_BF16: grctx.compute_flat = hvx_dequantize_row_flat_bf16_f32; break;
|
||||
case HTP_TYPE_Q1_0: grctx.compute_flat = hvx_dequantize_row_flat_q1_0_f32; break;
|
||||
case HTP_TYPE_Q2_0: grctx.compute_flat = hvx_dequantize_row_flat_q2_0_f32; break;
|
||||
case HTP_TYPE_Q8_0: grctx.compute_flat = hvx_dequantize_row_flat_q8_0_f32; break;
|
||||
case HTP_TYPE_Q4_0: grctx.compute_flat = hvx_dequantize_row_flat_q4_0_f32; break;
|
||||
case HTP_TYPE_Q4_K: grctx.compute_flat = hvx_dequantize_row_flat_q4_k_f32; break;
|
||||
case HTP_TYPE_Q5_K: grctx.compute_flat = hvx_dequantize_row_flat_q5_k_f32; break;
|
||||
case HTP_TYPE_Q6_K: grctx.compute_flat = hvx_dequantize_row_flat_q6_k_f32; break;
|
||||
default: return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
q_func = get_rows_thread_flat;
|
||||
break;
|
||||
default:
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
const uint32_t elem_size = (grctx.idx_type == HTP_TYPE_I64) ? sizeof(int64_t) : sizeof(int32_t);
|
||||
const size_t src1_bytes = (size_t) total_tasks * elem_size;
|
||||
|
||||
if (!htp_tensor_is_extended(octx->src[1]) && src1_bytes <= 128 &&
|
||||
htp_tensor_is_contiguous(octx->src[1], elem_size)) {
|
||||
grctx.src1_data = (const void *) (uintptr_t) octx->src[1]->data;
|
||||
grctx.read_index = get_rows_read_index_ddr;
|
||||
} else {
|
||||
uint8_t * vtcm_src1 = VTCM_LAYOUT_PTR(uint8_t, grctx.vtcm_base, grctx.vtcm_layout.off_src1);
|
||||
get_rows_dma_src1(octx, vtcm_src1, elem_size);
|
||||
grctx.src1_data = vtcm_src1;
|
||||
grctx.read_index = get_rows_read_index_vtcm;
|
||||
}
|
||||
|
||||
FARF(HIGH, "get-rows: (%ux%ux%ux%u) x (%ux%ux%ux%u) -> (%ux%ux%ux%u) : src0-vtcm-size %zu dst-vtcm-size %zu kernel-type %d n-threads %d\n",
|
||||
octx->src[0]->ne[0], octx->src[0]->ne[1], octx->src[0]->ne[2], octx->src[0]->ne[3],
|
||||
octx->src[1]->ne[0], octx->src[1]->ne[1], octx->src[1]->ne[2], octx->src[1]->ne[3],
|
||||
octx->dst->ne[0], octx->dst->ne[1], octx->dst->ne[2], octx->dst->ne[3],
|
||||
grctx.vtcm_layout.src0_bytes_per_thread * n_threads,
|
||||
grctx.vtcm_layout.dst_bytes_per_thread * n_threads,
|
||||
kparams->kernel_type, n_threads);
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, q_func, &grctx, n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#ifndef HTP_GET_ROWS_OPS_H
|
||||
#define HTP_GET_ROWS_OPS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
enum htp_get_rows_kernel_type {
|
||||
HTP_GET_ROWS_KERNEL_SAMETYPE = 0,
|
||||
HTP_GET_ROWS_KERNEL_TILED,
|
||||
HTP_GET_ROWS_KERNEL_FLAT,
|
||||
};
|
||||
|
||||
struct htp_get_rows_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint32_t n_threads;
|
||||
uint32_t total_tasks;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
// Fastdiv helpers
|
||||
struct fastdiv_values div_ne10;
|
||||
struct fastdiv_values div_ne10_ne11;
|
||||
struct fastdiv_values div_ne02;
|
||||
struct fastdiv_values div_ne03;
|
||||
};
|
||||
|
||||
struct htp_get_rows_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src1;
|
||||
size_t src1_bytes;
|
||||
size_t off_src0;
|
||||
size_t off_dst;
|
||||
|
||||
size_t src0_bytes_per_thread;
|
||||
size_t dst_bytes_per_thread;
|
||||
|
||||
size_t src0_spad_half_size;
|
||||
size_t dst_spad_half_size;
|
||||
};
|
||||
|
||||
static inline void htp_get_rows_vtcm_layout_build(
|
||||
struct htp_get_rows_vtcm_layout * L,
|
||||
int kernel_type,
|
||||
int type,
|
||||
uint32_t ne00,
|
||||
uint32_t total_tasks,
|
||||
int idx_type,
|
||||
uint32_t n_threads) {
|
||||
|
||||
memset(L, 0, sizeof(*L));
|
||||
|
||||
const size_t elem_size = (idx_type == HTP_TYPE_I64) ? sizeof(int64_t) : sizeof(int32_t);
|
||||
const size_t src1_raw_bytes = total_tasks * elem_size;
|
||||
const size_t src1_bytes = ((src1_raw_bytes + 127) & ~127) + 128;
|
||||
|
||||
L->src1_bytes = src1_bytes;
|
||||
const size_t src1_alloc_bytes = (src1_bytes + 255) & ~255;
|
||||
|
||||
if (kernel_type != HTP_GET_ROWS_KERNEL_SAMETYPE) {
|
||||
size_t src0_row_size = 0;
|
||||
if (kernel_type == HTP_GET_ROWS_KERNEL_TILED) {
|
||||
const size_t tile_stride = htp_tensor_get_tile_padded_size(type);
|
||||
const uint32_t n_k_tiles = ne00 / htp_tensor_get_tile_k(type);
|
||||
src0_row_size = n_k_tiles > 0 ? (n_k_tiles * tile_stride) : tile_stride;
|
||||
} else {
|
||||
src0_row_size = htp_tensor_get_row_size(type, ne00);
|
||||
}
|
||||
|
||||
L->src0_spad_half_size = (src0_row_size + 255) & ~255;
|
||||
L->dst_spad_half_size = (ne00 * sizeof(float) + 255) & ~255;
|
||||
L->src0_bytes_per_thread = 2 * L->src0_spad_half_size;
|
||||
L->dst_bytes_per_thread = 2 * L->dst_spad_half_size;
|
||||
}
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src1, src1_alloc_bytes);
|
||||
VTCM_LAYOUT_ALLOC(off, off_src0, L->src0_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, L->dst_bytes_per_thread * n_threads);
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_get_rows_kernel_params) <= 128, "htp_get_rows_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_get_rows_kernel_params) <= 128, "htp_get_rows_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_GET_ROWS_OPS_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,76 @@
|
||||
#ifndef HTP_GLU_OPS_H
|
||||
#define HTP_GLU_OPS_H
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct htp_glu_kernel_params {
|
||||
uint32_t n_threads;
|
||||
uint32_t vtcm_row_per_thread;
|
||||
uint32_t vtcm_size;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_glu_kernel_params) <= 128, "htp_glu_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_glu_kernel_params) <= 128, "htp_glu_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
static inline bool htp_op_is_glu(uint32_t opcode) {
|
||||
switch (opcode) {
|
||||
case HTP_OP_GLU_SWIGLU:
|
||||
case HTP_OP_GLU_SWIGLU_OAI:
|
||||
case HTP_OP_GLU_SWIGLU_CLAMP:
|
||||
case HTP_OP_GLU_GEGLU:
|
||||
case HTP_OP_GLU_GEGLU_QUICK:
|
||||
case HTP_OP_GLU_GEGLU_ERF:
|
||||
case HTP_OP_GLU_REGLU:
|
||||
case HTP_OP_GLU_SIGMOID:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
struct htp_glu_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src0;
|
||||
size_t off_src1;
|
||||
size_t off_dst;
|
||||
|
||||
size_t row_size_aligned;
|
||||
size_t src0_bytes_per_thread;
|
||||
size_t src1_bytes_per_thread;
|
||||
size_t dst_bytes_per_thread;
|
||||
|
||||
uint32_t vtcm_row_per_thread;
|
||||
uint32_t block;
|
||||
};
|
||||
|
||||
static inline void htp_glu_vtcm_layout_build(
|
||||
struct htp_glu_vtcm_layout * L,
|
||||
size_t dst_row_size,
|
||||
uint32_t n_threads,
|
||||
uint32_t vtcm_row_per_thread
|
||||
) {
|
||||
const size_t row_size_aligned = hex_round_up(dst_row_size, 128);
|
||||
|
||||
L->row_size_aligned = row_size_aligned;
|
||||
L->vtcm_row_per_thread = vtcm_row_per_thread;
|
||||
L->block = vtcm_row_per_thread >> 1;
|
||||
|
||||
L->src0_bytes_per_thread = row_size_aligned * vtcm_row_per_thread;
|
||||
L->src1_bytes_per_thread = row_size_aligned * vtcm_row_per_thread;
|
||||
L->dst_bytes_per_thread = row_size_aligned * vtcm_row_per_thread;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src0, L->src0_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_src1, L->src1_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, L->dst_bytes_per_thread * n_threads);
|
||||
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
#endif // HTP_GLU_OPS_H
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef HEX_BITMAP_H
|
||||
#define HEX_BITMAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
static inline void bitmap_set(uint32_t * bitmap, uint32_t idx) {
|
||||
bitmap[idx / 32] |= (1U << (idx % 32));
|
||||
}
|
||||
|
||||
static inline void bitmap_clear(uint32_t * bitmap, uint32_t idx) {
|
||||
bitmap[idx / 32] &= ~(1U << (idx % 32));
|
||||
}
|
||||
|
||||
static inline bool bitmap_test(const uint32_t * bitmap, uint32_t idx) {
|
||||
return (bitmap[idx / 32] & (1U << (idx % 32))) != 0;
|
||||
}
|
||||
|
||||
static inline void bitmap_reset(uint32_t * bitmap, size_t size_in_bits) {
|
||||
memset(bitmap, 0, ((size_in_bits + 31) / 32) * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
#endif // HEX_BITMAP_H
|
||||
@@ -0,0 +1,93 @@
|
||||
#ifndef HEX_COMMON_H
|
||||
#define HEX_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX ((size_t)-1)
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef HEX_L2_LINE_SIZE
|
||||
#define HEX_L2_LINE_SIZE 128
|
||||
#endif
|
||||
|
||||
static inline uint32_t hex_ceil_pow2(uint32_t x) {
|
||||
if (x <= 1) { return 1; }
|
||||
int p = 2;
|
||||
x--;
|
||||
while (x >>= 1) { p <<= 1; }
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline size_t hmx_ceil_div(size_t num, size_t den) {
|
||||
return (num + den - 1) / den;
|
||||
}
|
||||
|
||||
static inline int32_t hex_is_aligned(const void * addr, uint32_t align) {
|
||||
return ((size_t) addr & (align - 1)) == 0;
|
||||
}
|
||||
|
||||
static inline size_t hex_align_up(size_t v, size_t align) {
|
||||
return hmx_ceil_div(v, align) * align;
|
||||
}
|
||||
|
||||
static inline size_t hex_align_down(size_t v, size_t align) {
|
||||
return (v / align) * align;
|
||||
}
|
||||
|
||||
static inline int32_t hex_is_one_chunk(void * addr, uint32_t n, uint32_t chunk_size) {
|
||||
uint32_t left_off = (size_t) addr & (chunk_size - 1);
|
||||
uint32_t right_off = left_off + n;
|
||||
return right_off <= chunk_size;
|
||||
}
|
||||
|
||||
static inline uint32_t hex_round_up(uint32_t n, uint32_t m) {
|
||||
return m * ((n + m - 1) / m);
|
||||
}
|
||||
|
||||
static inline size_t hex_smin(size_t a, size_t b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
static inline size_t hex_smax(size_t a, size_t b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
static inline void hex_swap_ptr(void ** p1, void ** p2) {
|
||||
void * t = *p1;
|
||||
*p1 = *p2;
|
||||
*p2 = t;
|
||||
}
|
||||
|
||||
static inline bool hex_mul_overflow(size_t a, size_t b, size_t *out) {
|
||||
if (a != 0 && b > SIZE_MAX / a) return true;
|
||||
*out = a * b;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool hex_add_overflow(size_t a, size_t b, size_t *out) {
|
||||
if (a > SIZE_MAX - b) return true;
|
||||
*out = a + b;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline uint32_t hex_gcd_u32(uint32_t a, uint32_t b) {
|
||||
while (b != 0) {
|
||||
uint32_t t = b;
|
||||
b = a % b;
|
||||
a = t;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
#endif // HEX_COMMON_H
|
||||
@@ -0,0 +1,86 @@
|
||||
#ifndef HEX_DUMP_H
|
||||
#define HEX_DUMP_H
|
||||
|
||||
#include <HAP_farf.h>
|
||||
|
||||
static inline void hex_dump_int8_line(char * pref, const int8_t * x, int n) {
|
||||
char str[1024], *p = str, *p_end = str + sizeof(str);
|
||||
p += snprintf(p, p_end - p, "%s: ", pref);
|
||||
for (int i = 0; i < n && p < p_end; i++) {
|
||||
p += snprintf(p, p_end - p, "%d, ", x[i]);
|
||||
}
|
||||
FARF(HIGH, "%s\n", str);
|
||||
}
|
||||
|
||||
static inline void hex_dump_uint8_line(char * pref, const uint8_t * x, uint32_t n) {
|
||||
char str[1024], *p = str, *p_end = str + sizeof(str);
|
||||
p += snprintf(p, p_end - p, "%s: ", pref);
|
||||
for (int i = 0; i < n && p < p_end; i++) {
|
||||
p += snprintf(p, p_end - p, "%d, ", x[i]);
|
||||
}
|
||||
FARF(HIGH, "%s\n", str);
|
||||
}
|
||||
|
||||
static inline void hex_dump_uint32_line(char * pref, const uint32_t * x, uint32_t n) {
|
||||
char str[1024], *p = str, *p_end = str + sizeof(str);
|
||||
p += snprintf(p, p_end - p, "%s: ", pref);
|
||||
for (int i = 0; i < n; i++) {
|
||||
p += snprintf(p, p_end - p, "%u, ", (unsigned int) x[i]);
|
||||
}
|
||||
FARF(HIGH, "%s\n", str);
|
||||
}
|
||||
|
||||
static inline void hex_dump_int32_line(char * pref, const int32_t * x, uint32_t n) {
|
||||
char str[1024], *p = str, *p_end = str + sizeof(str);
|
||||
p += snprintf(p, p_end - p, "%s: ", pref);
|
||||
for (int i = 0; i < n; i++) {
|
||||
p += snprintf(p, p_end - p, "%d, ", (int) x[i]);
|
||||
}
|
||||
FARF(HIGH, "%s\n", str);
|
||||
}
|
||||
|
||||
static inline void hex_dump_f16_line(char * pref, const __fp16 * x, uint32_t n) {
|
||||
char str[1024], *p = str, *p_end = str + sizeof(str);
|
||||
p += snprintf(p, p_end - p, "%s: ", pref);
|
||||
for (int i = 0; i < n; i++) {
|
||||
p += snprintf(p, p_end - p, "%.6f, ", (float) x[i]);
|
||||
}
|
||||
FARF(HIGH, "%s\n", str);
|
||||
}
|
||||
|
||||
static inline void hex_dump_f32_line(char * pref, const float * x, uint32_t n) {
|
||||
char str[1024], *p = str, *p_end = str + sizeof(str);
|
||||
p += snprintf(p, p_end - p, "%s: ", pref);
|
||||
for (int i = 0; i < n; i++) {
|
||||
p += snprintf(p, p_end - p, "%.6f, ", x[i]);
|
||||
}
|
||||
FARF(HIGH, "%s\n", str);
|
||||
}
|
||||
|
||||
static inline void hex_dump_f32(char * pref, const float * x, uint32_t n) {
|
||||
uint32_t n0 = n / 16;
|
||||
uint32_t n1 = n % 16;
|
||||
|
||||
uint32_t i = 0;
|
||||
for (; i < n0; i++) {
|
||||
hex_dump_f32_line(pref, x + (16 * i), 16);
|
||||
}
|
||||
if (n1) {
|
||||
hex_dump_f32_line(pref, x + (16 * i), n1);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hex_dump_f16(char * pref, const __fp16 * x, uint32_t n) {
|
||||
uint32_t n0 = n / 16;
|
||||
uint32_t n1 = n % 16;
|
||||
|
||||
uint32_t i = 0;
|
||||
for (; i < n0; i++) {
|
||||
hex_dump_f16_line(pref, x + (16 * i), 16);
|
||||
}
|
||||
if (n1) {
|
||||
hex_dump_f16_line(pref, x + (16 * i), n1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HEX_DUMP_H */
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef HEX_FASTDIV_H
|
||||
#define HEX_FASTDIV_H
|
||||
|
||||
// See https://gmplib.org/~tege/divcnst-pldi94.pdf figure 4.1.
|
||||
// Precompute mp (m' in the paper) and L such that division
|
||||
// can be computed using a multiply (high 32b of 64b result)
|
||||
// and a shift:
|
||||
//
|
||||
// n/d = (mulhi(n, mp) + n) >> L;
|
||||
struct fastdiv_values {
|
||||
uint32_t mp;
|
||||
uint32_t l;
|
||||
};
|
||||
|
||||
static inline struct fastdiv_values init_fastdiv_values(uint32_t d) {
|
||||
struct fastdiv_values result = { 0, 0 };
|
||||
// compute L = ceil(log2(d));
|
||||
while (result.l < 32 && ((uint32_t) 1 << result.l) < d) {
|
||||
++(result.l);
|
||||
}
|
||||
|
||||
result.mp = (uint32_t) (((uint64_t) 1 << 32) * (((uint64_t) 1 << result.l) - d) / d + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline uint32_t fastdiv(uint32_t n, const struct fastdiv_values * vals) {
|
||||
// Compute high 32 bits of n * mp
|
||||
const uint32_t hi = (uint32_t) (((uint64_t) n * vals->mp) >> 32); // mulhi(n, mp)
|
||||
// add n, apply bit shift
|
||||
return (hi + n) >> vals->l;
|
||||
}
|
||||
|
||||
static inline uint32_t fastmodulo(uint32_t n, uint32_t d, const struct fastdiv_values * vals) {
|
||||
return n - fastdiv(n, vals) * d;
|
||||
}
|
||||
|
||||
#endif /* HEX_FASTDIV_H */
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef HEX_PROFILE_H
|
||||
#define HEX_PROFILE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <qurt.h>
|
||||
|
||||
#include "hex-utils.h"
|
||||
#include "htp-ops.h"
|
||||
|
||||
#define HTP_TRACE_EVT_START 0
|
||||
#define HTP_TRACE_EVT_STOP 1
|
||||
|
||||
#ifndef HEX_NUM_PMU_COUNTERS
|
||||
#define HEX_NUM_PMU_COUNTERS 8
|
||||
#endif
|
||||
|
||||
static inline void hex_get_pmu(uint32_t counters[]) {
|
||||
#if __HVX_ARCH__ >= 79
|
||||
asm volatile("%0 = upmucnt0" : "=r"(counters[0]));
|
||||
asm volatile("%0 = upmucnt1" : "=r"(counters[1]));
|
||||
asm volatile("%0 = upmucnt2" : "=r"(counters[2]));
|
||||
asm volatile("%0 = upmucnt3" : "=r"(counters[3]));
|
||||
asm volatile("%0 = upmucnt4" : "=r"(counters[4]));
|
||||
asm volatile("%0 = upmucnt5" : "=r"(counters[5]));
|
||||
asm volatile("%0 = upmucnt6" : "=r"(counters[6]));
|
||||
asm volatile("%0 = upmucnt7" : "=r"(counters[7]));
|
||||
#else
|
||||
counters[0] = qurt_pmu_get(QURT_PMUCNT0);
|
||||
counters[1] = qurt_pmu_get(QURT_PMUCNT1);
|
||||
counters[2] = qurt_pmu_get(QURT_PMUCNT2);
|
||||
counters[3] = qurt_pmu_get(QURT_PMUCNT3);
|
||||
counters[4] = qurt_pmu_get(QURT_PMUCNT4);
|
||||
counters[5] = qurt_pmu_get(QURT_PMUCNT5);
|
||||
counters[6] = qurt_pmu_get(QURT_PMUCNT6);
|
||||
counters[7] = qurt_pmu_get(QURT_PMUCNT7);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct htp_thread_trace {
|
||||
uint32_t count;
|
||||
uint32_t max_events;
|
||||
struct htp_trace_desc * events;
|
||||
};
|
||||
|
||||
static inline void htp_trace_event(struct htp_thread_trace * tr, uint16_t id, uint16_t info, uint32_t type) {
|
||||
if (!tr->max_events) {
|
||||
return;
|
||||
}
|
||||
uint32_t i = tr->count++;
|
||||
if (i < tr->max_events) {
|
||||
tr->events[i].id = id;
|
||||
tr->events[i].info = info | (type == HTP_TRACE_EVT_STOP ? 0x8000 : 0);
|
||||
tr->events[i].cycles = (uint32_t) hex_get_cycles();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void htp_trace_event_start(struct htp_thread_trace * tr, uint16_t id, uint16_t info) {
|
||||
htp_trace_event(tr, id, info, HTP_TRACE_EVT_START);
|
||||
}
|
||||
|
||||
static inline void htp_trace_event_stop(struct htp_thread_trace * tr, uint16_t id, uint16_t info) {
|
||||
htp_trace_event(tr, id, info, HTP_TRACE_EVT_STOP);
|
||||
}
|
||||
|
||||
#endif /* HEX_PROFILE_H */
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef HEX_UTILS_H
|
||||
#define HEX_UTILS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <qurt_memory.h>
|
||||
#include <qurt.h>
|
||||
|
||||
#include "hexagon_types.h"
|
||||
#include "hexagon_protos.h"
|
||||
|
||||
#include "hex-fastdiv.h"
|
||||
#include "hex-dump.h"
|
||||
#include "hex-common.h"
|
||||
|
||||
static inline uint64_t hex_get_cycles() {
|
||||
uint64_t cycles = 0;
|
||||
asm volatile(" %0 = c15:14\n" : "=r"(cycles));
|
||||
return cycles;
|
||||
}
|
||||
|
||||
static inline uint64_t hex_get_pktcnt() {
|
||||
uint64_t pktcnt;
|
||||
asm volatile(" %0 = c19:18\n" : "=r"(pktcnt));
|
||||
return pktcnt;
|
||||
}
|
||||
|
||||
static inline void hex_l2fetch(const void * p, uint32_t width, uint32_t stride, uint32_t height) {
|
||||
const uint64_t control = Q6_P_combine_RR(stride, Q6_R_combine_RlRl(width, height));
|
||||
Q6_l2fetch_AP((void *) p, control);
|
||||
}
|
||||
|
||||
static inline void hex_l2fetch_block(const void * addr, size_t size) {
|
||||
if (size == 0) return;
|
||||
const uint32_t width = 16384; // 16KB rows
|
||||
const uint32_t height = (size + width - 1) / width;
|
||||
hex_l2fetch(addr, width, width, height);
|
||||
}
|
||||
|
||||
#define HEX_L2_LINE_SIZE 128
|
||||
#define HEX_L2_BLOCK_SIZE (HEX_L2_LINE_SIZE * 4) // flush granularity (chunks per thread)
|
||||
#define HEX_L2_FLUSH_WQ_THRESHOLD (4 * 1024)
|
||||
#define HEX_L2_FLUSH_ALL_THRESHOLD (4 * 1024 * 1024)
|
||||
|
||||
static inline void hex_l2flush(void * addr, size_t size) {
|
||||
if (size == 0) return;
|
||||
const uint32_t s = ((uint32_t) addr) & ~(HEX_L2_LINE_SIZE - 1);
|
||||
const uint32_t e = (((uint32_t) addr) + size + HEX_L2_LINE_SIZE - 1) & ~(HEX_L2_LINE_SIZE - 1);
|
||||
for (uint32_t i = s; i < e; i += HEX_L2_LINE_SIZE) {
|
||||
Q6_dccleaninva_A((void *) i);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hex_pause() {
|
||||
asm volatile(" pause(#255)\n");
|
||||
}
|
||||
|
||||
#endif /* HEX_UTILS_H */
|
||||
@@ -0,0 +1,705 @@
|
||||
#ifndef HMX_FA_KERNELS_H
|
||||
#define HMX_FA_KERNELS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "hvx-utils.h"
|
||||
#include "hmx-utils.h"
|
||||
#include "hex-fastdiv.h"
|
||||
|
||||
// HMX-specific parameters, offsets and inner kernels for Flash Attention
|
||||
|
||||
// Scatter offsets for diagonal tile: entry[2i] = i*136, entry[2i+1] = i*136+6
|
||||
// 136 = 4 * 32 + 8 = byte offset to diagonal in a 32x32 fp16 interleaved tile
|
||||
static const int16_t d_tile_scatter_offsets[64] __attribute__((aligned(128))) = {
|
||||
0 * 136, 0 * 136 + 6,
|
||||
1 * 136, 1 * 136 + 6,
|
||||
2 * 136, 2 * 136 + 6,
|
||||
3 * 136, 3 * 136 + 6,
|
||||
4 * 136, 4 * 136 + 6,
|
||||
5 * 136, 5 * 136 + 6,
|
||||
6 * 136, 6 * 136 + 6,
|
||||
7 * 136, 7 * 136 + 6,
|
||||
8 * 136, 8 * 136 + 6,
|
||||
9 * 136, 9 * 136 + 6,
|
||||
10 * 136, 10 * 136 + 6,
|
||||
11 * 136, 11 * 136 + 6,
|
||||
12 * 136, 12 * 136 + 6,
|
||||
13 * 136, 13 * 136 + 6,
|
||||
14 * 136, 14 * 136 + 6,
|
||||
15 * 136, 15 * 136 + 6,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
};
|
||||
// Inner HMX tile computation kernels
|
||||
|
||||
static inline void hmx_fa_qk_dot_tile(
|
||||
const __fp16 * row_tiles,
|
||||
const __fp16 * col_tiles,
|
||||
__fp16 * out_tile,
|
||||
size_t n_dot_tiles
|
||||
) {
|
||||
if (n_dot_tiles == 2) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
HMX_LOAD_MPY_F16("%3", "%4", "%0")
|
||||
:
|
||||
: "r"(2047),
|
||||
"r"(row_tiles + 0 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 0 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 1 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 1 * HMX_FP16_TILE_N_ELMS)
|
||||
);
|
||||
} else if (n_dot_tiles == 4) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
HMX_LOAD_MPY_F16("%3", "%4", "%0")
|
||||
HMX_LOAD_MPY_F16("%5", "%6", "%0")
|
||||
HMX_LOAD_MPY_F16("%7", "%8", "%0")
|
||||
:
|
||||
: "r"(2047),
|
||||
"r"(row_tiles + 0 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 0 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 1 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 1 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 2 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 2 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 3 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 3 * HMX_FP16_TILE_N_ELMS)
|
||||
);
|
||||
} else if (n_dot_tiles == 8) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
HMX_LOAD_MPY_F16("%3", "%4", "%0")
|
||||
HMX_LOAD_MPY_F16("%5", "%6", "%0")
|
||||
HMX_LOAD_MPY_F16("%7", "%8", "%0")
|
||||
HMX_LOAD_MPY_F16("%9", "%10", "%0")
|
||||
HMX_LOAD_MPY_F16("%11", "%12", "%0")
|
||||
HMX_LOAD_MPY_F16("%13", "%14", "%0")
|
||||
HMX_LOAD_MPY_F16("%15", "%16", "%0")
|
||||
:
|
||||
: "r"(2047),
|
||||
"r"(row_tiles + 0 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 0 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 1 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 1 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 2 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 2 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 3 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 3 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 4 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 4 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 5 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 5 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 6 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 6 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(row_tiles + 7 * HMX_FP16_TILE_N_ELMS), "r"(col_tiles + 7 * HMX_FP16_TILE_N_ELMS)
|
||||
);
|
||||
} else {
|
||||
for (size_t k = 0; k < n_dot_tiles; ++k) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
:
|
||||
: "r"(2047), "r"(row_tiles), "r"(col_tiles)
|
||||
);
|
||||
row_tiles += HMX_FP16_TILE_N_ELMS;
|
||||
col_tiles += HMX_FP16_TILE_N_ELMS;
|
||||
}
|
||||
}
|
||||
asm volatile(
|
||||
HMX_STORE_AFTER_F16("%0", "%1")
|
||||
:
|
||||
: "r"(out_tile), "r"(0)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
static inline void hmx_fa_o_update_tile(
|
||||
const __fp16 * d_diag,
|
||||
const __fp16 * o_rc,
|
||||
const __fp16 * p_tile_in,
|
||||
const __fp16 * v_tile_in,
|
||||
__fp16 * o_tile_out,
|
||||
size_t n_col_tiles
|
||||
) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
:
|
||||
: "r"(2047), "r"(d_diag), "r"(o_rc)
|
||||
);
|
||||
if (n_col_tiles == 2) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
HMX_LOAD_MPY_F16("%3", "%4", "%0")
|
||||
:
|
||||
: "r"(2047),
|
||||
"r"(p_tile_in + 0 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 0 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 1 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 1 * HMX_FP16_TILE_N_ELMS)
|
||||
);
|
||||
} else if (n_col_tiles == 4) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
HMX_LOAD_MPY_F16("%3", "%4", "%0")
|
||||
HMX_LOAD_MPY_F16("%5", "%6", "%0")
|
||||
HMX_LOAD_MPY_F16("%7", "%8", "%0")
|
||||
:
|
||||
: "r"(2047),
|
||||
"r"(p_tile_in + 0 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 0 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 1 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 1 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 2 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 2 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 3 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 3 * HMX_FP16_TILE_N_ELMS)
|
||||
);
|
||||
} else if (n_col_tiles == 8) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
HMX_LOAD_MPY_F16("%3", "%4", "%0")
|
||||
HMX_LOAD_MPY_F16("%5", "%6", "%0")
|
||||
HMX_LOAD_MPY_F16("%7", "%8", "%0")
|
||||
HMX_LOAD_MPY_F16("%9", "%10", "%0")
|
||||
HMX_LOAD_MPY_F16("%11", "%12", "%0")
|
||||
HMX_LOAD_MPY_F16("%13", "%14", "%0")
|
||||
HMX_LOAD_MPY_F16("%15", "%16", "%0")
|
||||
:
|
||||
: "r"(2047),
|
||||
"r"(p_tile_in + 0 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 0 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 1 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 1 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 2 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 2 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 3 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 3 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 4 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 4 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 5 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 5 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 6 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 6 * HMX_FP16_TILE_N_ELMS),
|
||||
"r"(p_tile_in + 7 * HMX_FP16_TILE_N_ELMS), "r"(v_tile_in + 7 * HMX_FP16_TILE_N_ELMS)
|
||||
);
|
||||
} else {
|
||||
for (size_t k = 0; k < n_col_tiles; ++k) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
:
|
||||
: "r"(2047), "r"(p_tile_in), "r"(v_tile_in)
|
||||
);
|
||||
p_tile_in += HMX_FP16_TILE_N_ELMS;
|
||||
v_tile_in += HMX_FP16_TILE_N_ELMS;
|
||||
}
|
||||
}
|
||||
asm volatile(
|
||||
HMX_STORE_AFTER_F16("%0", "%1")
|
||||
:
|
||||
: "r"(o_tile_out), "r"(0)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
static inline void hmx_fa_o_norm_tile(
|
||||
const __fp16 * d_diag,
|
||||
const __fp16 * o_rc,
|
||||
__fp16 * o_out
|
||||
) {
|
||||
asm volatile(
|
||||
HMX_LOAD_MPY_F16("%1", "%2", "%0")
|
||||
:
|
||||
: "r"(2047), "r"(d_diag), "r"(o_rc)
|
||||
);
|
||||
asm volatile(
|
||||
HMX_STORE_AFTER_F16("%0", "%1")
|
||||
:
|
||||
: "r"(o_out), "r"(0)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
static inline void hmx_fa_q_prep_fp32_d2(
|
||||
__fp16 * vtcm_q_tiles, const uint8_t * temp_q_vtcm,
|
||||
size_t start, size_t end, size_t g_rows_end,
|
||||
size_t DK, size_t G, size_t n_rows_q,
|
||||
const struct fastdiv_values * div_G, bool q_transposed
|
||||
) {
|
||||
for (size_t r = start; r < end; r += 2) {
|
||||
size_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
size_t r1 = r % HMX_FP16_TILE_N_ROWS;
|
||||
__fp16 * out_base = vtcm_q_tiles + r0 * HMX_FP16_TILE_N_ROWS * DK;
|
||||
|
||||
if (r >= g_rows_end) {
|
||||
((HVX_Vector *) (out_base + 0 * HMX_FP16_TILE_N_ELMS))[r1 / 2] = Q6_V_vzero();
|
||||
((HVX_Vector *) (out_base + 1 * HMX_FP16_TILE_N_ELMS))[r1 / 2] = Q6_V_vzero();
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t q_idx0 = fastdiv(r + 0, div_G);
|
||||
const size_t h_idx0 = fastmodulo(r + 0, G, div_G);
|
||||
const size_t q_idx1 = fastdiv(r + 1, div_G);
|
||||
const size_t h_idx1 = fastmodulo(r + 1, G, div_G);
|
||||
|
||||
const size_t offset0 = q_transposed ? (h_idx0 * n_rows_q + q_idx0) : (q_idx0 * G + h_idx0);
|
||||
const size_t offset1 = q_transposed ? (h_idx1 * n_rows_q + q_idx1) : (q_idx1 * G + h_idx1);
|
||||
|
||||
const HVX_Vector * pv_in0 = (const HVX_Vector *) (temp_q_vtcm + offset0 * DK * sizeof(float));
|
||||
const HVX_Vector * pv_in1 = (r + 1 < g_rows_end)
|
||||
? (const HVX_Vector *) (temp_q_vtcm + offset1 * DK * sizeof(float))
|
||||
: NULL;
|
||||
|
||||
{
|
||||
HVX_Vector v0 = pv_in0[0];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[0] : Q6_V_vzero();
|
||||
HVX_Vector v_hf = hvx_vec_f32_to_f16_shuff(v0, v1);
|
||||
((HVX_Vector *) (out_base + 0 * HMX_FP16_TILE_N_ELMS))[r1 / 2] = v_hf;
|
||||
}
|
||||
{
|
||||
HVX_Vector v0 = pv_in0[1];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[1] : Q6_V_vzero();
|
||||
HVX_Vector v_hf = hvx_vec_f32_to_f16_shuff(v0, v1);
|
||||
((HVX_Vector *) (out_base + 1 * HMX_FP16_TILE_N_ELMS))[r1 / 2] = v_hf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hmx_fa_q_prep_fp32_d4(
|
||||
__fp16 * vtcm_q_tiles, const uint8_t * temp_q_vtcm,
|
||||
size_t start, size_t end, size_t g_rows_end,
|
||||
size_t DK, size_t G, size_t n_rows_q,
|
||||
const struct fastdiv_values * div_G, bool q_transposed
|
||||
) {
|
||||
for (size_t r = start; r < end; r += 2) {
|
||||
size_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
size_t r1 = r % HMX_FP16_TILE_N_ROWS;
|
||||
__fp16 * out_base = vtcm_q_tiles + r0 * HMX_FP16_TILE_N_ROWS * DK;
|
||||
|
||||
if (r >= g_rows_end) {
|
||||
for (uint32_t d = 0; d < 4; ++d) {
|
||||
((HVX_Vector *) (out_base + d * HMX_FP16_TILE_N_ELMS))[r1 / 2] = Q6_V_vzero();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t q_idx0 = fastdiv(r + 0, div_G);
|
||||
const size_t h_idx0 = fastmodulo(r + 0, G, div_G);
|
||||
const size_t q_idx1 = fastdiv(r + 1, div_G);
|
||||
const size_t h_idx1 = fastmodulo(r + 1, G, div_G);
|
||||
|
||||
const size_t offset0 = q_transposed ? (h_idx0 * n_rows_q + q_idx0) : (q_idx0 * G + h_idx0);
|
||||
const size_t offset1 = q_transposed ? (h_idx1 * n_rows_q + q_idx1) : (q_idx1 * G + h_idx1);
|
||||
|
||||
const HVX_Vector * pv_in0 = (const HVX_Vector *) (temp_q_vtcm + offset0 * DK * sizeof(float));
|
||||
const HVX_Vector * pv_in1 = (r + 1 < g_rows_end)
|
||||
? (const HVX_Vector *) (temp_q_vtcm + offset1 * DK * sizeof(float))
|
||||
: NULL;
|
||||
|
||||
for (uint32_t d = 0; d < 4; ++d) {
|
||||
HVX_Vector v0 = pv_in0[d];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[d] : Q6_V_vzero();
|
||||
HVX_Vector v_hf = hvx_vec_f32_to_f16_shuff(v0, v1);
|
||||
((HVX_Vector *) (out_base + d * HMX_FP16_TILE_N_ELMS))[r1 / 2] = v_hf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hmx_fa_q_prep_fp32(
|
||||
__fp16 * vtcm_q_tiles, const uint8_t * temp_q_vtcm,
|
||||
size_t start, size_t end, size_t g_rows_end,
|
||||
size_t DK, size_t G, size_t n_rows_q,
|
||||
const struct fastdiv_values * div_G, uint32_t d_limit, bool q_transposed
|
||||
) {
|
||||
for (size_t r = start; r < end; r += 2) {
|
||||
size_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
size_t r1 = r % HMX_FP16_TILE_N_ROWS;
|
||||
__fp16 * out_base = vtcm_q_tiles + r0 * HMX_FP16_TILE_N_ROWS * DK;
|
||||
|
||||
if (r >= g_rows_end) {
|
||||
for (uint32_t d = 0; d < d_limit; ++d) {
|
||||
((HVX_Vector *) (out_base + d * HMX_FP16_TILE_N_ELMS))[r1 / 2] = Q6_V_vzero();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t q_idx0 = fastdiv(r + 0, div_G);
|
||||
const size_t h_idx0 = fastmodulo(r + 0, G, div_G);
|
||||
const size_t q_idx1 = fastdiv(r + 1, div_G);
|
||||
const size_t h_idx1 = fastmodulo(r + 1, G, div_G);
|
||||
|
||||
const size_t offset0 = q_transposed ? (h_idx0 * n_rows_q + q_idx0) : (q_idx0 * G + h_idx0);
|
||||
const size_t offset1 = q_transposed ? (h_idx1 * n_rows_q + q_idx1) : (q_idx1 * G + h_idx1);
|
||||
|
||||
const HVX_Vector * pv_in0 = (const HVX_Vector *) (temp_q_vtcm + offset0 * DK * sizeof(float));
|
||||
const HVX_Vector * pv_in1 = (r + 1 < g_rows_end)
|
||||
? (const HVX_Vector *) (temp_q_vtcm + offset1 * DK * sizeof(float))
|
||||
: NULL;
|
||||
|
||||
for (uint32_t d = 0; d < d_limit; ++d) {
|
||||
HVX_Vector v0 = pv_in0[d];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[d] : Q6_V_vzero();
|
||||
HVX_Vector v_hf = hvx_vec_f32_to_f16_shuff(v0, v1);
|
||||
|
||||
HVX_Vector * out_tile = (HVX_Vector *) (out_base + d * HMX_FP16_TILE_N_ELMS);
|
||||
out_tile[r1 / 2] = v_hf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hmx_fa_q_prep_fp16_d1(
|
||||
__fp16 * vtcm_q_tiles, const uint8_t * temp_q_vtcm,
|
||||
size_t start, size_t end, size_t g_rows_end,
|
||||
size_t DK, size_t G, size_t n_rows_q,
|
||||
const struct fastdiv_values * div_G, bool q_transposed
|
||||
) {
|
||||
for (size_t r = start; r < end; r += 2) {
|
||||
size_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
size_t r1 = r % HMX_FP16_TILE_N_ROWS;
|
||||
__fp16 * out_base = vtcm_q_tiles + r0 * HMX_FP16_TILE_N_ROWS * DK;
|
||||
|
||||
if (r >= g_rows_end) {
|
||||
__fp16 * out_dtile = out_base + 0 * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
*pv_out0 = Q6_V_vzero();
|
||||
*pv_out1 = Q6_V_vzero();
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t q_idx0 = fastdiv(r + 0, div_G);
|
||||
const size_t h_idx0 = fastmodulo(r + 0, G, div_G);
|
||||
const size_t q_idx1 = fastdiv(r + 1, div_G);
|
||||
const size_t h_idx1 = fastmodulo(r + 1, G, div_G);
|
||||
|
||||
const size_t offset0 = q_transposed ? (h_idx0 * n_rows_q + q_idx0) : (q_idx0 * G + h_idx0);
|
||||
const size_t offset1 = q_transposed ? (h_idx1 * n_rows_q + q_idx1) : (q_idx1 * G + h_idx1);
|
||||
|
||||
const HVX_Vector * pv_in0 = (const HVX_Vector *) (temp_q_vtcm + offset0 * DK * sizeof(__fp16));
|
||||
const HVX_Vector * pv_in1 = (r + 1 < g_rows_end)
|
||||
? (const HVX_Vector *) (temp_q_vtcm + offset1 * DK * sizeof(__fp16))
|
||||
: NULL;
|
||||
|
||||
HVX_Vector v0 = pv_in0[0];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[0] : Q6_V_vzero();
|
||||
HVX_VectorPair vp = Q6_W_vshuff_VVR(v1, v0, -2);
|
||||
|
||||
__fp16 * out_dtile = out_base + 0 * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
|
||||
*pv_out0 = Q6_V_lo_W(vp);
|
||||
*pv_out1 = Q6_V_hi_W(vp);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hmx_fa_q_prep_fp16_d2(
|
||||
__fp16 * vtcm_q_tiles, const uint8_t * temp_q_vtcm,
|
||||
size_t start, size_t end, size_t g_rows_end,
|
||||
size_t DK, size_t G, size_t n_rows_q,
|
||||
const struct fastdiv_values * div_G, bool q_transposed
|
||||
) {
|
||||
for (size_t r = start; r < end; r += 2) {
|
||||
size_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
size_t r1 = r % HMX_FP16_TILE_N_ROWS;
|
||||
__fp16 * out_base = vtcm_q_tiles + r0 * HMX_FP16_TILE_N_ROWS * DK;
|
||||
|
||||
if (r >= g_rows_end) {
|
||||
for (uint32_t d = 0; d < 2; ++d) {
|
||||
__fp16 * out_dtile = out_base + d * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
*pv_out0 = Q6_V_vzero();
|
||||
*pv_out1 = Q6_V_vzero();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t q_idx0 = fastdiv(r + 0, div_G);
|
||||
const size_t h_idx0 = fastmodulo(r + 0, G, div_G);
|
||||
const size_t q_idx1 = fastdiv(r + 1, div_G);
|
||||
const size_t h_idx1 = fastmodulo(r + 1, G, div_G);
|
||||
|
||||
const size_t offset0 = q_transposed ? (h_idx0 * n_rows_q + q_idx0) : (q_idx0 * G + h_idx0);
|
||||
const size_t offset1 = q_transposed ? (h_idx1 * n_rows_q + q_idx1) : (q_idx1 * G + h_idx1);
|
||||
|
||||
const HVX_Vector * pv_in0 = (const HVX_Vector *) (temp_q_vtcm + offset0 * DK * sizeof(__fp16));
|
||||
const HVX_Vector * pv_in1 = (r + 1 < g_rows_end)
|
||||
? (const HVX_Vector *) (temp_q_vtcm + offset1 * DK * sizeof(__fp16))
|
||||
: NULL;
|
||||
|
||||
{
|
||||
HVX_Vector v0 = pv_in0[0];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[0] : Q6_V_vzero();
|
||||
HVX_VectorPair vp = Q6_W_vshuff_VVR(v1, v0, -2);
|
||||
|
||||
__fp16 * out_dtile = out_base + 0 * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
|
||||
*pv_out0 = Q6_V_lo_W(vp);
|
||||
*pv_out1 = Q6_V_hi_W(vp);
|
||||
}
|
||||
{
|
||||
HVX_Vector v0 = pv_in0[1];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[1] : Q6_V_vzero();
|
||||
HVX_VectorPair vp = Q6_W_vshuff_VVR(v1, v0, -2);
|
||||
|
||||
__fp16 * out_dtile = out_base + 1 * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
|
||||
*pv_out0 = Q6_V_lo_W(vp);
|
||||
*pv_out1 = Q6_V_hi_W(vp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hmx_fa_q_prep_fp16(
|
||||
__fp16 * vtcm_q_tiles, const uint8_t * temp_q_vtcm,
|
||||
size_t start, size_t end, size_t g_rows_end,
|
||||
size_t DK, size_t G, size_t n_rows_q,
|
||||
const struct fastdiv_values * div_G, uint32_t d_limit, bool q_transposed
|
||||
) {
|
||||
for (size_t r = start; r < end; r += 2) {
|
||||
size_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
size_t r1 = r % HMX_FP16_TILE_N_ROWS;
|
||||
__fp16 * out_base = vtcm_q_tiles + r0 * HMX_FP16_TILE_N_ROWS * DK;
|
||||
|
||||
if (r >= g_rows_end) {
|
||||
for (uint32_t d = 0; d < d_limit; ++d) {
|
||||
__fp16 * out_dtile = out_base + d * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
*pv_out0 = Q6_V_vzero();
|
||||
*pv_out1 = Q6_V_vzero();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t q_idx0 = fastdiv(r + 0, div_G);
|
||||
const size_t h_idx0 = fastmodulo(r + 0, G, div_G);
|
||||
const size_t q_idx1 = fastdiv(r + 1, div_G);
|
||||
const size_t h_idx1 = fastmodulo(r + 1, G, div_G);
|
||||
|
||||
const size_t offset0 = q_transposed ? (h_idx0 * n_rows_q + q_idx0) : (q_idx0 * G + h_idx0);
|
||||
const size_t offset1 = q_transposed ? (h_idx1 * n_rows_q + q_idx1) : (q_idx1 * G + h_idx1);
|
||||
|
||||
const HVX_Vector * pv_in0 = (const HVX_Vector *) (temp_q_vtcm + offset0 * DK * sizeof(__fp16));
|
||||
const HVX_Vector * pv_in1 = (r + 1 < g_rows_end)
|
||||
? (const HVX_Vector *) (temp_q_vtcm + offset1 * DK * sizeof(__fp16))
|
||||
: NULL;
|
||||
|
||||
for (uint32_t d = 0; d < d_limit; ++d) {
|
||||
HVX_Vector v0 = pv_in0[d];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[d] : Q6_V_vzero();
|
||||
HVX_VectorPair vp = Q6_W_vshuff_VVR(v1, v0, -2);
|
||||
|
||||
__fp16 * out_dtile = out_base + d * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
|
||||
*pv_out0 = Q6_V_lo_W(vp);
|
||||
*pv_out1 = Q6_V_hi_W(vp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Head-dim-padded Q-prep (f32). Used when DK is not a multiple of 64.
|
||||
static inline void hmx_fa_q_prep_fp32_pad(__fp16 * vtcm_q_tiles,
|
||||
const uint8_t * temp_q_vtcm,
|
||||
size_t start,
|
||||
size_t end,
|
||||
size_t g_rows_end,
|
||||
size_t dk_in,
|
||||
size_t dk_out,
|
||||
size_t G,
|
||||
size_t n_rows_q,
|
||||
const struct fastdiv_values * div_G,
|
||||
bool q_transposed) {
|
||||
const uint32_t n_out_tiles = (uint32_t) (dk_out / 32);
|
||||
for (size_t r = start; r < end; r += 2) {
|
||||
size_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
size_t r1 = r % HMX_FP16_TILE_N_ROWS;
|
||||
__fp16 * out_base = vtcm_q_tiles + r0 * HMX_FP16_TILE_N_ROWS * dk_out;
|
||||
|
||||
if (r >= g_rows_end) {
|
||||
for (uint32_t d = 0; d < n_out_tiles; ++d) {
|
||||
((HVX_Vector *) (out_base + d * HMX_FP16_TILE_N_ELMS))[r1 / 2] = Q6_V_vzero();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t q_idx0 = fastdiv(r + 0, div_G);
|
||||
const size_t h_idx0 = fastmodulo(r + 0, G, div_G);
|
||||
const size_t q_idx1 = fastdiv(r + 1, div_G);
|
||||
const size_t h_idx1 = fastmodulo(r + 1, G, div_G);
|
||||
|
||||
const size_t offset0 = q_transposed ? (h_idx0 * n_rows_q + q_idx0) : (q_idx0 * G + h_idx0);
|
||||
const size_t offset1 = q_transposed ? (h_idx1 * n_rows_q + q_idx1) : (q_idx1 * G + h_idx1);
|
||||
|
||||
const HVX_UVector * pv_in0 = (const HVX_UVector *) (temp_q_vtcm + offset0 * dk_in * sizeof(float));
|
||||
const HVX_UVector * pv_in1 = (r + 1 < g_rows_end) ? (const HVX_UVector *) (temp_q_vtcm + offset1 * dk_in * sizeof(float)) : NULL;
|
||||
|
||||
for (uint32_t d = 0; d < n_out_tiles; ++d) {
|
||||
HVX_Vector * out_tile = (HVX_Vector *) (out_base + d * HMX_FP16_TILE_N_ELMS);
|
||||
const size_t base_lane = (size_t) d * 32;
|
||||
const size_t real_lanes = (base_lane < dk_in) ? hex_smin(32, dk_in - base_lane) : 0;
|
||||
|
||||
if (real_lanes == 0) {
|
||||
out_tile[r1 / 2] = Q6_V_vzero();
|
||||
continue;
|
||||
}
|
||||
|
||||
HVX_Vector v0 = pv_in0[d];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[d] : Q6_V_vzero();
|
||||
if (real_lanes < 32) {
|
||||
// Straddle tile: keep the first real_lanes floats, zero the padded tail so
|
||||
// the packed f16 lanes beyond DK are zero.
|
||||
const HVX_VectorPred keep = Q6_Q_vsetq_R((uint32_t) (real_lanes * sizeof(float)));
|
||||
v0 = Q6_V_vmux_QVV(keep, v0, Q6_V_vzero());
|
||||
v1 = Q6_V_vmux_QVV(keep, v1, Q6_V_vzero());
|
||||
}
|
||||
out_tile[r1 / 2] = hvx_vec_f32_to_f16_shuff(v0, v1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Head-dim-padded Q-prep (f16). Used when DK is not a multiple of 64.
|
||||
static inline void hmx_fa_q_prep_fp16_pad(__fp16 * vtcm_q_tiles,
|
||||
const uint8_t * temp_q_vtcm,
|
||||
size_t start,
|
||||
size_t end,
|
||||
size_t g_rows_end,
|
||||
size_t dk_in,
|
||||
size_t dk_out,
|
||||
size_t G,
|
||||
size_t n_rows_q,
|
||||
const struct fastdiv_values * div_G,
|
||||
bool q_transposed) {
|
||||
const uint32_t n_out_pairs = (uint32_t) (dk_out / 64);
|
||||
for (size_t r = start; r < end; r += 2) {
|
||||
size_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
size_t r1 = r % HMX_FP16_TILE_N_ROWS;
|
||||
__fp16 * out_base = vtcm_q_tiles + r0 * HMX_FP16_TILE_N_ROWS * dk_out;
|
||||
|
||||
if (r >= g_rows_end) {
|
||||
for (uint32_t d = 0; d < n_out_pairs; ++d) {
|
||||
__fp16 * out_dtile = out_base + d * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
*pv_out0 = Q6_V_vzero();
|
||||
*pv_out1 = Q6_V_vzero();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t q_idx0 = fastdiv(r + 0, div_G);
|
||||
const size_t h_idx0 = fastmodulo(r + 0, G, div_G);
|
||||
const size_t q_idx1 = fastdiv(r + 1, div_G);
|
||||
const size_t h_idx1 = fastmodulo(r + 1, G, div_G);
|
||||
|
||||
const size_t offset0 = q_transposed ? (h_idx0 * n_rows_q + q_idx0) : (q_idx0 * G + h_idx0);
|
||||
const size_t offset1 = q_transposed ? (h_idx1 * n_rows_q + q_idx1) : (q_idx1 * G + h_idx1);
|
||||
|
||||
const HVX_UVector * pv_in0 = (const HVX_UVector *) (temp_q_vtcm + offset0 * dk_in * sizeof(__fp16));
|
||||
const HVX_UVector * pv_in1 = (r + 1 < g_rows_end) ? (const HVX_UVector *) (temp_q_vtcm + offset1 * dk_in * sizeof(__fp16)) : NULL;
|
||||
|
||||
for (uint32_t d = 0; d < n_out_pairs; ++d) {
|
||||
__fp16 * out_dtile = out_base + d * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
|
||||
const size_t base_lane = (size_t) d * 64;
|
||||
const size_t real_lanes = (base_lane < dk_in) ? hex_smin(64, dk_in - base_lane) : 0;
|
||||
|
||||
if (real_lanes == 0) {
|
||||
*pv_out0 = Q6_V_vzero();
|
||||
*pv_out1 = Q6_V_vzero();
|
||||
continue;
|
||||
}
|
||||
|
||||
HVX_Vector v0 = pv_in0[d];
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[d] : Q6_V_vzero();
|
||||
if (real_lanes < 64) {
|
||||
const HVX_VectorPred keep = Q6_Q_vsetq_R((uint32_t) (real_lanes * sizeof(__fp16)));
|
||||
v0 = Q6_V_vmux_QVV(keep, v0, Q6_V_vzero());
|
||||
v1 = Q6_V_vmux_QVV(keep, v1, Q6_V_vzero());
|
||||
}
|
||||
HVX_VectorPair vp = Q6_W_vshuff_VVR(v1, v0, -2);
|
||||
*pv_out0 = Q6_V_lo_W(vp);
|
||||
*pv_out1 = Q6_V_hi_W(vp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hmx_fa_q_prep_fallback(
|
||||
__fp16 * vtcm_q_tiles, uintptr_t q_data,
|
||||
size_t q_nb1, size_t q_nb2, size_t q_nb3,
|
||||
uint32_t q_start, uint32_t kv_head, uint32_t ib3,
|
||||
size_t start, size_t end, size_t n_rows_g,
|
||||
size_t G, size_t dk_in, size_t dk_out, bool is_q_fp32,
|
||||
const struct fastdiv_values * div_G
|
||||
) {
|
||||
for (size_t r = start; r < end; r += 2) {
|
||||
const size_t q_idx0 = fastdiv(r + 0, div_G);
|
||||
const size_t h_idx0 = fastmodulo(r + 0, G, div_G);
|
||||
const size_t q_idx1 = fastdiv(r + 1, div_G);
|
||||
const size_t h_idx1 = fastmodulo(r + 1, G, div_G);
|
||||
|
||||
const uint8_t * q_ptr0 = (r + 0 < n_rows_g) ? ((const uint8_t *) q_data + (q_start + q_idx0) * q_nb1 +
|
||||
(kv_head * G + h_idx0) * q_nb2 + ib3 * q_nb3) :
|
||||
NULL;
|
||||
const uint8_t * q_ptr1 = (r + 1 < n_rows_g) ? ((const uint8_t *) q_data + (q_start + q_idx1) * q_nb1 +
|
||||
(kv_head * G + h_idx1) * q_nb2 + ib3 * q_nb3) :
|
||||
NULL;
|
||||
|
||||
size_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
size_t r1 = r % HMX_FP16_TILE_N_ROWS;
|
||||
__fp16 * out_base = vtcm_q_tiles + r0 * HMX_FP16_TILE_N_ROWS * dk_out;
|
||||
|
||||
if (is_q_fp32) {
|
||||
const HVX_UVector * pv_in0 = q_ptr0 ? (const HVX_UVector *) q_ptr0 : NULL;
|
||||
const HVX_UVector * pv_in1 = q_ptr1 ? (const HVX_UVector *) q_ptr1 : NULL;
|
||||
|
||||
for (uint32_t d = 0; d < dk_out / 32; ++d) {
|
||||
HVX_Vector * out_tile = (HVX_Vector *) (out_base + d * HMX_FP16_TILE_N_ELMS);
|
||||
const size_t base_lane = (size_t) d * 32;
|
||||
const size_t real_lanes = (base_lane < dk_in) ? hex_smin(32, dk_in - base_lane) : 0;
|
||||
|
||||
if (real_lanes == 0) {
|
||||
out_tile[r1 / 2] = Q6_V_vzero();
|
||||
continue;
|
||||
}
|
||||
HVX_Vector v0 = pv_in0 ? pv_in0[d] : Q6_V_vzero();
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[d] : Q6_V_vzero();
|
||||
if (real_lanes < 32) {
|
||||
const HVX_VectorPred keep = Q6_Q_vsetq_R((uint32_t) (real_lanes * sizeof(float)));
|
||||
v0 = Q6_V_vmux_QVV(keep, v0, Q6_V_vzero());
|
||||
v1 = Q6_V_vmux_QVV(keep, v1, Q6_V_vzero());
|
||||
}
|
||||
out_tile[r1 / 2] = hvx_vec_f32_to_f16_shuff(v0, v1);
|
||||
}
|
||||
} else {
|
||||
const HVX_UVector * pv_in0 = q_ptr0 ? (const HVX_UVector *) q_ptr0 : NULL;
|
||||
const HVX_UVector * pv_in1 = q_ptr1 ? (const HVX_UVector *) q_ptr1 : NULL;
|
||||
|
||||
for (uint32_t d = 0; d < dk_out / 64; ++d) {
|
||||
__fp16 * out_dtile = out_base + d * HMX_FP16_TILE_N_ELMS * 2;
|
||||
HVX_Vector * pv_out0 = ((HVX_Vector *) out_dtile) + r1 / 2;
|
||||
HVX_Vector * pv_out1 = pv_out0 + 16;
|
||||
|
||||
const size_t base_lane = (size_t) d * 64;
|
||||
const size_t real_lanes = (base_lane < dk_in) ? hex_smin(64, dk_in - base_lane) : 0;
|
||||
|
||||
if (real_lanes == 0) {
|
||||
*pv_out0 = Q6_V_vzero();
|
||||
*pv_out1 = Q6_V_vzero();
|
||||
continue;
|
||||
}
|
||||
HVX_Vector v0 = pv_in0 ? pv_in0[d] : Q6_V_vzero();
|
||||
HVX_Vector v1 = pv_in1 ? pv_in1[d] : Q6_V_vzero();
|
||||
if (real_lanes < 64) {
|
||||
const HVX_VectorPred keep = Q6_Q_vsetq_R((uint32_t) (real_lanes * sizeof(__fp16)));
|
||||
v0 = Q6_V_vmux_QVV(keep, v0, Q6_V_vzero());
|
||||
v1 = Q6_V_vmux_QVV(keep, v1, Q6_V_vzero());
|
||||
}
|
||||
HVX_VectorPair vp = Q6_W_vshuff_VVR(v1, v0, -2);
|
||||
*pv_out0 = Q6_V_lo_W(vp);
|
||||
*pv_out1 = Q6_V_hi_W(vp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HMX_FA_KERNELS_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,170 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <qurt_thread.h>
|
||||
#include <qurt_futex.h>
|
||||
#include <qurt_hvx.h>
|
||||
|
||||
#include <HAP_compute_res.h>
|
||||
|
||||
#include "hmx-queue.h"
|
||||
|
||||
#define QURT_LOWEST_PRIO (254)
|
||||
|
||||
static inline void hmx_lock(hmx_queue_t q)
|
||||
{
|
||||
if (!q->hmx_locked) {
|
||||
HAP_compute_res_hmx_lock(q->hap_rctx);
|
||||
q->hmx_locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hmx_unlock(hmx_queue_t q)
|
||||
{
|
||||
if (q->hmx_locked) {
|
||||
HAP_compute_res_hmx_unlock(q->hap_rctx);
|
||||
q->hmx_locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hmx_queue_process(hmx_queue_t q, bool* killed) {
|
||||
unsigned int ir = atomic_load(&q->idx_read);
|
||||
|
||||
while (ir != atomic_load(&q->idx_write)) {
|
||||
struct hmx_queue_desc *d = &q->desc[ir];
|
||||
if (!d->done) {
|
||||
FARF(HIGH, "hmx-queue-process: ir %u func %p data %p", ir, d->func, d->data);
|
||||
|
||||
uintptr_t sig = (uintptr_t) d->func;
|
||||
switch (sig) {
|
||||
case HMX_QUEUE_NOOP: /* noop */; break;
|
||||
case HMX_QUEUE_KILL: *killed = true; break;
|
||||
case HMX_QUEUE_SUSPEND: hmx_unlock(q); break;
|
||||
case HMX_QUEUE_WAKEUP: hmx_lock(q); break;
|
||||
default:
|
||||
hmx_lock(q);
|
||||
htp_trace_event_start(q->trace, HTP_TRACE_EVT_HMX_COMP, ir);
|
||||
d->func(d->data);
|
||||
htp_trace_event_stop(q->trace, HTP_TRACE_EVT_HMX_COMP, ir);
|
||||
break;
|
||||
}
|
||||
|
||||
atomic_fetch_add(&d->done, 1);
|
||||
}
|
||||
|
||||
ir = (ir + 1) & q->idx_mask;
|
||||
atomic_store(&q->idx_read, ir);
|
||||
}
|
||||
}
|
||||
|
||||
static void hmx_queue_thread(void * arg) {
|
||||
hmx_queue_t q = (hmx_queue_t) arg;
|
||||
|
||||
FARF(HIGH, "hmx-queue-thread: started");
|
||||
|
||||
bool killed = false;
|
||||
|
||||
unsigned int poll_cnt = HMX_QUEUE_POLL_COUNT;
|
||||
unsigned int prev_seqn = 0;
|
||||
while (!killed) {
|
||||
unsigned int seqn = atomic_load(&q->seqn);
|
||||
if (seqn == prev_seqn) {
|
||||
// drop HVX context while spinning
|
||||
if (poll_cnt > 1 && poll_cnt == HMX_QUEUE_POLL_COUNT) {
|
||||
qurt_hvx_unlock();
|
||||
}
|
||||
if (--poll_cnt) { hex_pause(); continue; }
|
||||
FARF(HIGH, "hmx-queue-thread: sleeping");
|
||||
qurt_futex_wait(&q->seqn, prev_seqn);
|
||||
poll_cnt = HMX_QUEUE_POLL_COUNT;
|
||||
continue;
|
||||
}
|
||||
prev_seqn = seqn;
|
||||
poll_cnt = HMX_QUEUE_POLL_COUNT;
|
||||
|
||||
FARF(HIGH, "hmx-queue-thread: new work");
|
||||
|
||||
hmx_queue_process(q, &killed);
|
||||
}
|
||||
|
||||
FARF(HIGH, "hmx-queue-thread: stopped");
|
||||
}
|
||||
|
||||
size_t hmx_queue_sizeof(size_t capacity, uint32_t stack_size) {
|
||||
capacity = hex_ceil_pow2(capacity);
|
||||
size_t size_q = hex_align_up(sizeof(struct hmx_queue_s), HEX_L2_LINE_SIZE);
|
||||
size_t size_desc = hex_align_up(capacity * sizeof(struct hmx_queue_desc), HEX_L2_LINE_SIZE);
|
||||
size_t size_stack = stack_size;
|
||||
return size_q + size_desc + size_stack;
|
||||
}
|
||||
|
||||
size_t hmx_queue_alignof(void) {
|
||||
return HEX_L2_LINE_SIZE;
|
||||
}
|
||||
|
||||
hmx_queue_t hmx_queue_init(void * ptr, size_t capacity, uint32_t stack_size, uint32_t hap_rctx, struct htp_thread_trace * trace) {
|
||||
capacity = hex_ceil_pow2(capacity);
|
||||
size_t size_q = hex_align_up(sizeof(struct hmx_queue_s), HEX_L2_LINE_SIZE);
|
||||
size_t size_desc = hex_align_up(capacity * sizeof(struct hmx_queue_desc), HEX_L2_LINE_SIZE);
|
||||
|
||||
uint8_t * block = (uint8_t *) ptr;
|
||||
|
||||
hmx_queue_t q = (hmx_queue_t) block; block += size_q;
|
||||
memset(q, 0, sizeof(struct hmx_queue_s));
|
||||
|
||||
q->capacity = capacity;
|
||||
q->idx_mask = capacity - 1;
|
||||
q->hap_rctx = hap_rctx;
|
||||
q->external_mem = true;
|
||||
|
||||
q->desc = (struct hmx_queue_desc *) block; block += size_desc;
|
||||
memset(q->desc, 0, capacity * sizeof(struct hmx_queue_desc));
|
||||
|
||||
q->stack = block;
|
||||
memset(q->stack, 0, stack_size);
|
||||
|
||||
q->trace = trace;
|
||||
|
||||
// Match caller thread priority (same pattern as worker-pool.c).
|
||||
int prio = qurt_thread_get_priority(qurt_thread_get_id());
|
||||
if (prio < 1) {
|
||||
prio = 1;
|
||||
}
|
||||
if (prio > QURT_LOWEST_PRIO) {
|
||||
prio = QURT_LOWEST_PRIO;
|
||||
}
|
||||
|
||||
qurt_thread_attr_t attr;
|
||||
qurt_thread_attr_init(&attr);
|
||||
qurt_thread_attr_set_stack_addr(&attr, q->stack);
|
||||
qurt_thread_attr_set_stack_size(&attr, stack_size);
|
||||
qurt_thread_attr_set_priority(&attr, prio);
|
||||
qurt_thread_attr_set_name(&attr, "hmx-queue");
|
||||
|
||||
int err = qurt_thread_create(&q->thread, &attr, hmx_queue_thread, q);
|
||||
if (err) {
|
||||
FARF(ERROR, "hmx-worker: thread create failed (%d)", err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FARF(HIGH, "hmx-queue: capacity %u\n", capacity);
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
void hmx_queue_free(hmx_queue_t q) {
|
||||
if (!q) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell the worker to exit.
|
||||
hmx_queue_flush(q);
|
||||
hmx_queue_signal(q, HMX_QUEUE_KILL);
|
||||
hmx_queue_flush(q);
|
||||
|
||||
int status;
|
||||
qurt_thread_join(q->thread, &status);
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#ifndef HMX_QUEUE_H
|
||||
#define HMX_QUEUE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include <hexagon_types.h>
|
||||
#include <qurt_thread.h>
|
||||
#include <qurt_futex.h>
|
||||
#include <HAP_farf.h>
|
||||
|
||||
#include "hex-utils.h"
|
||||
#include "hex-profile.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if __HVX_ARCH__ > 79
|
||||
#define HMX_QUEUE_POLL_COUNT 2000
|
||||
#else
|
||||
#define HMX_QUEUE_POLL_COUNT 1
|
||||
#endif
|
||||
|
||||
typedef void (*hmx_queue_func)(void *);
|
||||
|
||||
// Dummy funcs used as signals
|
||||
enum hmx_queue_signal {
|
||||
HMX_QUEUE_NOOP = 0, // aka NULL
|
||||
HMX_QUEUE_WAKEUP,
|
||||
HMX_QUEUE_SUSPEND,
|
||||
HMX_QUEUE_KILL
|
||||
};
|
||||
|
||||
struct hmx_queue_desc {
|
||||
hmx_queue_func func;
|
||||
void * data;
|
||||
atomic_uint done;
|
||||
};
|
||||
|
||||
struct hmx_queue_s {
|
||||
struct hmx_queue_desc * desc;
|
||||
atomic_uint idx_write; // updated by producer (push)
|
||||
atomic_uint idx_read; // updated by consumer (process)
|
||||
unsigned int idx_pop; // updated by producer (pop)
|
||||
uint32_t idx_mask;
|
||||
uint32_t capacity;
|
||||
|
||||
atomic_uint seqn; // incremented for all pushes, used with futex
|
||||
qurt_thread_t thread;
|
||||
void * stack;
|
||||
uint32_t hap_rctx;
|
||||
bool hmx_locked;
|
||||
struct htp_thread_trace * trace;
|
||||
bool external_mem; // memory owned externally
|
||||
};
|
||||
|
||||
typedef struct hmx_queue_s * hmx_queue_t;
|
||||
|
||||
size_t hmx_queue_sizeof(size_t capacity, uint32_t stack_size);
|
||||
size_t hmx_queue_alignof(void);
|
||||
hmx_queue_t hmx_queue_init(void * ptr, size_t capacity, uint32_t stack_size, uint32_t hap_rctx, struct htp_thread_trace * trace);
|
||||
void hmx_queue_free(hmx_queue_t q);
|
||||
|
||||
static inline struct hmx_queue_desc hmx_queue_make_desc(hmx_queue_func func, void * data) {
|
||||
struct hmx_queue_desc d = { func, data };
|
||||
return d;
|
||||
}
|
||||
|
||||
static inline bool hmx_queue_push(hmx_queue_t q, struct hmx_queue_desc d) {
|
||||
unsigned int ir = atomic_load(&q->idx_read);
|
||||
unsigned int iw = atomic_load(&q->idx_write);
|
||||
|
||||
if (((iw + 1) & q->idx_mask) == ir) {
|
||||
FARF(HIGH, "hmx-queue-push: queue is full\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
atomic_store(&d.done, 0);
|
||||
|
||||
FARF(HIGH, "hmx-queue-push: iw %u func %p data %p\n", iw, d.func, d.data);
|
||||
|
||||
q->desc[iw] = d;
|
||||
atomic_store(&q->idx_write, (iw + 1) & q->idx_mask);
|
||||
// wake up our thread
|
||||
atomic_fetch_add(&q->seqn, 1);
|
||||
qurt_futex_wake(&q->seqn, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool hmx_queue_signal(hmx_queue_t q, enum hmx_queue_signal sig) {
|
||||
return hmx_queue_push(q, hmx_queue_make_desc((hmx_queue_func) sig, NULL));
|
||||
}
|
||||
|
||||
static inline bool hmx_queue_empty(hmx_queue_t q) {
|
||||
return q->idx_pop == atomic_load(&q->idx_write);
|
||||
}
|
||||
|
||||
static inline uint32_t hmx_queue_depth(hmx_queue_t q) {
|
||||
return (atomic_load(&q->idx_write) - atomic_load(&q->idx_read)) & q->idx_mask;
|
||||
}
|
||||
|
||||
static inline uint32_t hmx_queue_capacity(hmx_queue_t q) {
|
||||
return q->capacity;
|
||||
}
|
||||
|
||||
static inline struct hmx_queue_desc hmx_queue_pop_one(hmx_queue_t q) {
|
||||
unsigned int ip = q->idx_pop;
|
||||
unsigned int iw = atomic_load(&q->idx_write);
|
||||
|
||||
struct hmx_queue_desc rd = { NULL, NULL };
|
||||
if (ip == iw) {
|
||||
return rd;
|
||||
}
|
||||
|
||||
// Wait for desc to complete
|
||||
struct hmx_queue_desc * d = &q->desc[ip];
|
||||
while (!atomic_load(&d->done)) {
|
||||
FARF(HIGH, "hmx-queue-pop: waiting for HMX queue : %u\n", ip);
|
||||
hex_pause();
|
||||
}
|
||||
|
||||
rd = *d;
|
||||
q->idx_pop = (ip + 1) & q->idx_mask;
|
||||
|
||||
FARF(HIGH, "hmx-queue-pop: ip %u func %p data %p\n", ip, rd.func, rd.data);
|
||||
return rd;
|
||||
}
|
||||
|
||||
static inline struct hmx_queue_desc hmx_queue_pop(hmx_queue_t q) {
|
||||
while (1) {
|
||||
struct hmx_queue_desc d = hmx_queue_pop_one(q);
|
||||
|
||||
uint32_t sig = (uint32_t) d.func;
|
||||
if (sig && sig <= HMX_QUEUE_KILL)
|
||||
continue;
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hmx_queue_flush(hmx_queue_t q) {
|
||||
while (hmx_queue_pop_one(q).func != NULL) ;
|
||||
}
|
||||
|
||||
static inline void hmx_queue_wakeup(hmx_queue_t q) {
|
||||
hmx_queue_signal(q, HMX_QUEUE_WAKEUP);
|
||||
}
|
||||
|
||||
static inline void hmx_queue_suspend(hmx_queue_t q) {
|
||||
hmx_queue_signal(q, HMX_QUEUE_SUSPEND);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* HMX_QUEUE_H */
|
||||
@@ -0,0 +1,230 @@
|
||||
// HMX tile-level inline helpers (FP16 32x32 tile operations).
|
||||
// Ported from htp-ops-lib/include/dsp/hmx_utils.h. (https://github.com/haozixu/htp-ops-lib)
|
||||
|
||||
#ifndef HMX_UTILS_H
|
||||
#define HMX_UTILS_H
|
||||
|
||||
#include "hvx-base.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <hexagon_types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define HMX_FP16_TILE_N_ROWS 32
|
||||
#define HMX_FP16_TILE_N_COLS 32
|
||||
#define HMX_FP16_TILE_N_ELMS 1024
|
||||
#define HMX_FP16_TILE_SIZE 2048
|
||||
|
||||
// Initialise aligned 256-byte area with scale vector + zero padding.
|
||||
static inline void hmx_init_column_scales(void *out_scales, HVX_Vector v_scale) {
|
||||
volatile HVX_Vector *pv = (HVX_Vector *) out_scales;
|
||||
pv[0] = v_scale;
|
||||
pv[1] = Q6_V_vzero();
|
||||
}
|
||||
|
||||
// --- Shared scatter offsets and interleave helper ---
|
||||
|
||||
// vscatter offsets for fused dequant+transpose: write K-values directly to [K][N] tile.
|
||||
// word[i] = i*128 maps K-row-pair i to byte offset i*128.
|
||||
// Column offset (n*4) is added at runtime. Entries 0..15 cover one tile (region 2047);
|
||||
// entries 16..31 cover the next adjacent tile (region 4095) - pick region size at the
|
||||
// call site to scatter into one tile (masked) or two contiguous tiles (unmasked).
|
||||
static const int32_t hmx_transpose_scatter_offsets[32] __attribute__((aligned(VLEN))) = {
|
||||
0 * 128, 1 * 128, 2 * 128, 3 * 128, 4 * 128, 5 * 128, 6 * 128, 7 * 128, 8 * 128, 9 * 128, 10 * 128,
|
||||
11 * 128, 12 * 128, 13 * 128, 14 * 128, 15 * 128, 16 * 128, 17 * 128, 18 * 128, 19 * 128, 20 * 128, 21 * 128,
|
||||
22 * 128, 23 * 128, 24 * 128, 25 * 128, 26 * 128, 27 * 128, 28 * 128, 29 * 128, 30 * 128, 31 * 128,
|
||||
};
|
||||
|
||||
// Scatter row-major FP16 data (in VTCM scratch) into transposed [K][N] tiles.
|
||||
// vtcm_src: [n_cols][src_stride] row-major fp16 (only first k elements per row are used)
|
||||
// vtcm_dst: [n_col_tiles][n_k_tiles][HMX_FP16_TILE_N_ELMS] tile-major interleaved fp16
|
||||
// Processes rows [start_row, end_row) for multi-thread slicing.
|
||||
// Full range: start_row=0, end_row=n_cols.
|
||||
static inline void hmx_interleave_rows_to_tiles(__fp16 * restrict vtcm_dst,
|
||||
const __fp16 * restrict vtcm_src,
|
||||
uint32_t n_cols,
|
||||
uint32_t k,
|
||||
size_t src_stride,
|
||||
uint32_t start_row,
|
||||
uint32_t end_row) {
|
||||
assert(k % HMX_FP16_TILE_N_COLS == 0);
|
||||
|
||||
const uint32_t n_k_tiles = k / HMX_FP16_TILE_N_COLS;
|
||||
const HVX_Vector v_scat_base = hvx_vmem(hmx_transpose_scatter_offsets);
|
||||
const HVX_Vector v_scat_step = Q6_V_vsplat_R(4);
|
||||
const HVX_VectorPred q_mask64 = Q6_Q_vsetq_R(64);
|
||||
// Each hvx_vmemu load brings 64 fp16 = 128 bytes covering 2 adjacent K-tiles.
|
||||
// When n_k_tiles is even, scatter into 2 K-tiles per call (region 4095, no mask)
|
||||
// using the upper half of hmx_transpose_scatter_offsets. Tail one K-tile (when
|
||||
// n_k_tiles is odd) falls back to single-tile masked scatter.
|
||||
const bool pair_scatter = (n_k_tiles & 1) == 0;
|
||||
const size_t pair_region = (size_t) (2 * HMX_FP16_TILE_SIZE - 1);
|
||||
const size_t single_region = (size_t) (HMX_FP16_TILE_SIZE - 1);
|
||||
__builtin_assume(k > 0);
|
||||
__builtin_assume(end_row > start_row);
|
||||
|
||||
if (pair_scatter) {
|
||||
// Step c by 64 fp16 (two K-tiles per scatter), advance dst by 2 tiles per iter.
|
||||
const uint32_t c_step = 2 * HMX_FP16_TILE_N_COLS;
|
||||
const size_t c_byte_step = (size_t) c_step * sizeof(__fp16);
|
||||
const size_t dst_step = 2 * (size_t) HMX_FP16_TILE_N_ELMS;
|
||||
const uint32_t n_c_iters = k / c_step;
|
||||
|
||||
for (uint32_t r = start_row; r < end_row; r += 2) {
|
||||
const uint32_t ct = r / HMX_FP16_TILE_N_ROWS;
|
||||
const uint32_t local_r = r % HMX_FP16_TILE_N_ROWS;
|
||||
const bool row0_valid = r < n_cols;
|
||||
const bool next_row_valid = (r + 1) < end_row && (r + 1) < n_cols;
|
||||
const HVX_Vector v_off0 = Q6_Vw_vadd_VwVw(v_scat_base, Q6_V_vsplat_R(local_r * 4));
|
||||
const HVX_Vector v_off1 = Q6_Vw_vadd_VwVw(v_off0, v_scat_step);
|
||||
|
||||
__fp16 * tile_base = vtcm_dst + (size_t) ct * n_k_tiles * HMX_FP16_TILE_N_ELMS;
|
||||
const uint8_t * p0 = row0_valid ? (const uint8_t *) (vtcm_src + r * src_stride) : NULL;
|
||||
const uint8_t * p1 = next_row_valid ? (const uint8_t *) (vtcm_src + (r + 1) * src_stride) : NULL;
|
||||
|
||||
assert(!p0 || hex_is_aligned(p0, 128));
|
||||
assert(!p1 || hex_is_aligned(p1, 128));
|
||||
assert(c_byte_step % 128 == 0);
|
||||
|
||||
if (p0 && p1) {
|
||||
for (uint32_t i = 0; i < n_c_iters; ++i) {
|
||||
HVX_Vector v0 = hvx_vmem(p0); p0 += c_byte_step;
|
||||
HVX_Vector v1 = hvx_vmem(p1); p1 += c_byte_step;
|
||||
Q6_vscatter_RMVwV((size_t) tile_base, pair_region, v_off0, v0);
|
||||
Q6_vscatter_RMVwV((size_t) tile_base, pair_region, v_off1, v1);
|
||||
tile_base += dst_step;
|
||||
}
|
||||
} else {
|
||||
const HVX_Vector vzero = Q6_V_vzero();
|
||||
for (uint32_t i = 0; i < n_c_iters; ++i) {
|
||||
HVX_Vector v0 = p0 ? hvx_vmem(p0) : vzero;
|
||||
if (p0) p0 += c_byte_step;
|
||||
HVX_Vector v1 = p1 ? hvx_vmem(p1) : vzero;
|
||||
if (p1) p1 += c_byte_step;
|
||||
Q6_vscatter_RMVwV((size_t) tile_base, pair_region, v_off0, v0);
|
||||
Q6_vscatter_RMVwV((size_t) tile_base, pair_region, v_off1, v1);
|
||||
tile_base += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback: scatter one K-tile per call (region 2047, masked).
|
||||
const uint32_t c_step = HMX_FP16_TILE_N_COLS;
|
||||
const size_t c_byte_step = (size_t) c_step * sizeof(__fp16);
|
||||
const size_t dst_step = (size_t) HMX_FP16_TILE_N_ELMS;
|
||||
const uint32_t n_c_iters = k / c_step;
|
||||
|
||||
for (uint32_t r = start_row; r < end_row; r += 2) {
|
||||
const uint32_t ct = r / HMX_FP16_TILE_N_ROWS;
|
||||
const uint32_t local_r = r % HMX_FP16_TILE_N_ROWS;
|
||||
const bool row0_valid = r < n_cols;
|
||||
const bool next_row_valid = (r + 1) < end_row && (r + 1) < n_cols;
|
||||
const HVX_Vector v_off0 = Q6_Vw_vadd_VwVw(v_scat_base, Q6_V_vsplat_R(local_r * 4));
|
||||
const HVX_Vector v_off1 = Q6_Vw_vadd_VwVw(v_off0, v_scat_step);
|
||||
|
||||
__fp16 * tile_base = vtcm_dst + (size_t) ct * n_k_tiles * HMX_FP16_TILE_N_ELMS;
|
||||
const uint8_t * p0 = row0_valid ? (const uint8_t *) (vtcm_src + r * src_stride) : NULL;
|
||||
const uint8_t * p1 = next_row_valid ? (const uint8_t *) (vtcm_src + (r + 1) * src_stride) : NULL;
|
||||
|
||||
if (p0 && p1) {
|
||||
for (uint32_t i = 0; i < n_c_iters; ++i) {
|
||||
HVX_Vector v0 = hvx_vmemu(p0); p0 += c_byte_step;
|
||||
HVX_Vector v1 = hvx_vmemu(p1); p1 += c_byte_step;
|
||||
Q6_vscatter_QRMVwV(q_mask64, (size_t) tile_base, single_region, v_off0, v0);
|
||||
Q6_vscatter_QRMVwV(q_mask64, (size_t) tile_base, single_region, v_off1, v1);
|
||||
tile_base += dst_step;
|
||||
}
|
||||
} else {
|
||||
const HVX_Vector vzero = Q6_V_vzero();
|
||||
for (uint32_t i = 0; i < n_c_iters; ++i) {
|
||||
HVX_Vector v0 = p0 ? hvx_vmemu(p0) : vzero;
|
||||
if (p0) p0 += c_byte_step;
|
||||
HVX_Vector v1 = p1 ? hvx_vmemu(p1) : vzero;
|
||||
if (p1) p1 += c_byte_step;
|
||||
Q6_vscatter_QRMVwV(q_mask64, (size_t) tile_base, single_region, v_off0, v0);
|
||||
Q6_vscatter_QRMVwV(q_mask64, (size_t) tile_base, single_region, v_off1, v1);
|
||||
tile_base += dst_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Interleave row-major FP16 data into column-major tile format.
|
||||
// Input: [n_rows, head_dim] row-major. Output: tile[dim_tile][row_tile].
|
||||
// Processes rows [start_row, end_row) for multi-thread slicing.
|
||||
// Full range: start_row=0, end_row=n_rows.
|
||||
static inline void hmx_interleave_cols_to_tiles(__fp16 * restrict tiles_out,
|
||||
const __fp16 * restrict src,
|
||||
uint32_t n_rows,
|
||||
uint32_t head_dim,
|
||||
size_t src_stride,
|
||||
uint32_t n_row_tiles,
|
||||
uint32_t start_row,
|
||||
uint32_t end_row) {
|
||||
__builtin_assume(head_dim > 0);
|
||||
const size_t tile_stride_elms = (size_t) n_row_tiles * HMX_FP16_TILE_N_ELMS;
|
||||
|
||||
for (uint32_t r = start_row; r < end_row; r += 2) {
|
||||
const bool next_row_valid = (r + 1) < end_row && (r + 1) < n_rows;
|
||||
|
||||
const HVX_Vector * pv_in0 = (const HVX_Vector *) (src + r * src_stride);
|
||||
const HVX_Vector * pv_in1 = next_row_valid ? (const HVX_Vector *) (src + (r + 1) * src_stride) : NULL;
|
||||
|
||||
// Row-pair invariants hoisted out of the c loop.
|
||||
const uint32_t r0 = r / HMX_FP16_TILE_N_ROWS;
|
||||
const uint32_t r1_half = (r % HMX_FP16_TILE_N_ROWS) / 2;
|
||||
|
||||
// tb0 starts at tile (c0=0, r0); tb1 at the adjacent dim-tile (c0=1, r0).
|
||||
// Each c step (+= 64) advances both by 2 dim-tiles worth of fp16.
|
||||
__fp16 * tb0 = tiles_out + (size_t) r0 * HMX_FP16_TILE_N_ELMS;
|
||||
__fp16 * tb1 = tb0 + tile_stride_elms;
|
||||
const size_t tb_step = 2 * tile_stride_elms;
|
||||
|
||||
if (pv_in1) {
|
||||
for (uint32_t c = 0; c < head_dim; c += 64) {
|
||||
HVX_Vector v0 = *pv_in0++;
|
||||
HVX_Vector v1 = *pv_in1++;
|
||||
HVX_VectorPair vp = Q6_W_vshuff_VVR(v1, v0, -2);
|
||||
((HVX_Vector *) tb0)[r1_half] = Q6_V_lo_W(vp);
|
||||
((HVX_Vector *) tb1)[r1_half] = Q6_V_hi_W(vp);
|
||||
tb0 += tb_step;
|
||||
tb1 += tb_step;
|
||||
}
|
||||
} else {
|
||||
const HVX_Vector vzero = Q6_V_vzero();
|
||||
for (uint32_t c = 0; c < head_dim; c += 64) {
|
||||
HVX_Vector v0 = *pv_in0++;
|
||||
HVX_VectorPair vp = Q6_W_vshuff_VVR(vzero, v0, -2);
|
||||
((HVX_Vector *) tb0)[r1_half] = Q6_V_lo_W(vp);
|
||||
((HVX_Vector *) tb1)[r1_half] = Q6_V_hi_W(vp);
|
||||
tb0 += tb_step;
|
||||
tb1 += tb_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- HMX inline asm macros for load-store packetization ---
|
||||
#define HMX_LOAD_MPY_F16(act, wt, range) \
|
||||
"{\n" \
|
||||
" activation.hf = mxmem(" act ", " range ")\n" \
|
||||
" weight.hf = mxmem(" wt ", " range ")\n" \
|
||||
"}\n"
|
||||
|
||||
#define HMX_LOAD_MPY_DEEP_F16(act, wt, range) \
|
||||
"{\n" \
|
||||
" activation.hf = mxmem(" act ", " range "):deep\n" \
|
||||
" weight.hf = mxmem(" wt ", " range ")\n" \
|
||||
"}\n"
|
||||
|
||||
#define HMX_STORE_AFTER_F16(out, scale_reg) \
|
||||
"mxmem(" out ", " scale_reg "):after.hf = acc\n"
|
||||
|
||||
#define HMX_SET_BIAS(scales) \
|
||||
"bias = mxmem2(" scales ")\n"
|
||||
|
||||
#define HMX_CLRACC_F16() \
|
||||
"mxclracc.hf\n"
|
||||
|
||||
#endif // HMX_UTILS_H
|
||||
@@ -0,0 +1,190 @@
|
||||
#ifndef HTP_CTX_H
|
||||
#define HTP_CTX_H
|
||||
|
||||
#include "dma-queue.h"
|
||||
#include "hmx-queue.h"
|
||||
#include "htp-dcache.h"
|
||||
#include "htp-ops.h"
|
||||
#include "hex-profile.h"
|
||||
#include "work-queue.h"
|
||||
#include "hex-fastdiv.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <dspqueue.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef HTP_MAX_NTHREADS
|
||||
#define HTP_MAX_NTHREADS 10
|
||||
#endif
|
||||
|
||||
#define HTP_MAX_MMAPS 64
|
||||
|
||||
// Memory mapping
|
||||
struct htp_mmap {
|
||||
uint64_t size;
|
||||
uint64_t base;
|
||||
uint32_t fd;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
// Scratchpad state
|
||||
struct htp_spad {
|
||||
const struct htp_tensor * src; // original src of the data (for reuse)
|
||||
uint8_t * data; // pointer to an area in vtcm
|
||||
uint32_t stride; // stride used inside this spad
|
||||
uint32_t size; // total size
|
||||
uint32_t size_per_thread; // size per thread
|
||||
};
|
||||
|
||||
struct htp_mdev_group {
|
||||
uint16_t idx;
|
||||
uint16_t count;
|
||||
struct fastdiv_values count_div;
|
||||
uint8_t * fence_base;
|
||||
uint32_t fence_seq;
|
||||
};
|
||||
|
||||
struct htp_context;
|
||||
|
||||
// Context while processing an Op
|
||||
// TODO: fold this into the main context
|
||||
struct htp_ops_context {
|
||||
struct htp_context * ctx;
|
||||
|
||||
enum htp_op_code op; // FIXME: rename to opcode
|
||||
int32_t op_params[HTP_OP_MAX_PARAMS];
|
||||
int32_t kernel_params[HTP_OP_MAX_KERN_PARAMS];
|
||||
|
||||
const struct htp_tensor * src[HTP_OP_MAX_INPUTS];
|
||||
union {
|
||||
const struct htp_tensor * dst;
|
||||
const struct htp_tensor * dsts[HTP_OP_MAX_OUTPUTS];
|
||||
};
|
||||
|
||||
// TODO convert these to an array
|
||||
struct htp_spad src0_spad;
|
||||
struct htp_spad src1_spad;
|
||||
struct htp_spad dst_spad;
|
||||
|
||||
uint32_t flags;
|
||||
uint32_t n_threads;
|
||||
struct fastdiv_values n_threads_div;
|
||||
int status;
|
||||
|
||||
// Must stay last: not cleared by htp_ops_context_reset(), ops init the caches they use
|
||||
union {
|
||||
dma_cache_dm dma_cache_dm[HTP_MAX_NTHREADS];
|
||||
dma_cache_fa dma_cache_fa[HTP_MAX_NTHREADS];
|
||||
} __attribute__((aligned(128)));
|
||||
};
|
||||
|
||||
// Main context for htp DSP backend
|
||||
struct htp_context {
|
||||
dspqueue_t dsp_queue;
|
||||
|
||||
struct htp_mmap mmap[HTP_MAX_MMAPS];
|
||||
dma_queue_t dma[HTP_MAX_NTHREADS];
|
||||
struct htp_thread_trace trace[HTP_MAX_NTHREADS + 1];
|
||||
work_queue_t work_queue;
|
||||
hmx_queue_t hmx_queue;
|
||||
|
||||
uint32_t n_threads;
|
||||
struct fastdiv_values n_threads_div;
|
||||
|
||||
int thread_id;
|
||||
int thread_prio;
|
||||
|
||||
bool hmx_enabled;
|
||||
bool etm;
|
||||
uint32_t profiler;
|
||||
|
||||
uint8_t * vtcm_base;
|
||||
size_t vtcm_size;
|
||||
uint32_t vtcm_rctx;
|
||||
atomic_bool vtcm_valid;
|
||||
atomic_bool vtcm_needs_release;
|
||||
|
||||
uint64_t max_vmem;
|
||||
struct htp_dirty_range dirty_ranges[HTP_MAX_DIRTY_RANGES];
|
||||
|
||||
// Persistent DDR scratchpad for MUL_MAT_ID mappings
|
||||
void * ddr_spad_base;
|
||||
size_t ddr_spad_size;
|
||||
|
||||
struct htp_mdev_group mdev;
|
||||
struct htp_ops_context octx;
|
||||
|
||||
qurt_thread_t main_thread;
|
||||
void * main_stack;
|
||||
atomic_bool killed;
|
||||
size_t footprint;
|
||||
};
|
||||
|
||||
static inline void htp_ops_context_reset(struct htp_ops_context * octx, struct htp_context * ctx) {
|
||||
memset(octx, 0, offsetof(struct htp_ops_context, dma_cache_dm));
|
||||
octx->ctx = ctx;
|
||||
octx->n_threads = ctx->n_threads;
|
||||
octx->n_threads_div = ctx->n_threads_div;
|
||||
}
|
||||
|
||||
static inline bool htp_ops_context_set_n_threads(struct htp_ops_context * octx, uint32_t n_threads) {
|
||||
if (n_threads == 0 || n_threads > octx->ctx->n_threads) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n_threads != octx->n_threads) {
|
||||
octx->n_threads = n_threads;
|
||||
octx->n_threads_div = n_threads == octx->ctx->n_threads
|
||||
? octx->ctx->n_threads_div
|
||||
: init_fastdiv_values(n_threads);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void htp_ops_context_set_status(struct htp_ops_context * octx, int status) {
|
||||
if (status > HTP_STATUS_OK && octx->status == HTP_STATUS_OK) {
|
||||
octx->status = status;
|
||||
}
|
||||
}
|
||||
|
||||
int op_matmul(struct htp_ops_context * octx);
|
||||
int op_matmul_id(struct htp_ops_context * octx);
|
||||
int op_matmul_nx(struct htp_ops_context * octx);
|
||||
int op_matmul_id_nx(struct htp_ops_context * octx);
|
||||
int op_binary(struct htp_ops_context * octx);
|
||||
int op_unary(struct htp_ops_context * octx);
|
||||
int op_sum(struct htp_ops_context * octx);
|
||||
int op_sum_rows(struct htp_ops_context * octx);
|
||||
int op_argmax(struct htp_ops_context * octx);
|
||||
int op_glu(struct htp_ops_context * octx);
|
||||
int op_softmax(struct htp_ops_context * octx);
|
||||
int op_add_id(struct htp_ops_context * octx);
|
||||
int op_rope(struct htp_ops_context * octx);
|
||||
int op_flash_attn_ext(struct htp_ops_context * octx);
|
||||
int op_set_rows(struct htp_ops_context * octx);
|
||||
int op_get_rows(struct htp_ops_context * octx);
|
||||
int op_cpy(struct htp_ops_context * octx);
|
||||
int op_repeat(struct htp_ops_context * octx);
|
||||
int op_argsort(struct htp_ops_context * octx);
|
||||
int op_top_k(struct htp_ops_context * octx);
|
||||
int op_ssm_conv(struct htp_ops_context * octx);
|
||||
int op_cumsum(struct htp_ops_context * octx);
|
||||
int op_fill(struct htp_ops_context * octx);
|
||||
int op_concat(struct htp_ops_context * octx);
|
||||
int op_diag(struct htp_ops_context * octx);
|
||||
int op_solve_tri(struct htp_ops_context * octx);
|
||||
int op_gated_delta_net(struct htp_ops_context * octx);
|
||||
int op_pad(struct htp_ops_context * octx);
|
||||
int op_im2col(struct htp_ops_context * octx);
|
||||
int op_im2col_3d(struct htp_ops_context * octx);
|
||||
int op_pool_2d(struct htp_ops_context * octx);
|
||||
int op_pool_1d(struct htp_ops_context * octx);
|
||||
int op_allreduce(struct htp_ops_context * octx);
|
||||
int op_roll(struct htp_ops_context * octx);
|
||||
|
||||
#endif /* HTP_CTX_H */
|
||||
@@ -0,0 +1,360 @@
|
||||
#include "htp-dcache.h"
|
||||
|
||||
#include <qurt.h>
|
||||
#include <qurt_memory.h>
|
||||
#include <HAP_farf.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-utils.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "work-queue.h"
|
||||
|
||||
struct l2flush_range {
|
||||
uint32_t start; // line-aligned start address
|
||||
uint32_t end; // line-aligned end address
|
||||
uint32_t block_first; // global block index of this range's first block
|
||||
uint32_t n_blocks; // number of HEX_L2_BLOCK_SIZE chunks (last may be partial)
|
||||
};
|
||||
|
||||
struct l2flush_multi_task {
|
||||
struct htp_thread_trace * trace;
|
||||
struct l2flush_range ranges[HTP_MAX_DIRTY_RANGES];
|
||||
uint32_t n_ranges;
|
||||
uint32_t total_blocks;
|
||||
uint32_t blocks_per_thread;
|
||||
};
|
||||
|
||||
static void flush_all_dcache(struct htp_context * ctx) {
|
||||
struct htp_thread_trace * tr = &ctx->trace[0];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_L2FLUSH, 0);
|
||||
qurt_mem_cache_clean((qurt_addr_t) 0, 0, QURT_MEM_CACHE_FLUSH_INVALIDATE_ALL, QURT_MEM_DCACHE);
|
||||
hex_l2fetch_block(ctx, ctx->footprint);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_L2FLUSH, 0);
|
||||
memset(ctx->dirty_ranges, 0, sizeof(ctx->dirty_ranges));
|
||||
}
|
||||
|
||||
static void l2flush_multi_worker(unsigned int n, unsigned int i, void * data) {
|
||||
struct l2flush_multi_task * task = (struct l2flush_multi_task *) data;
|
||||
(void) n;
|
||||
|
||||
const uint32_t gb_first = i * task->blocks_per_thread;
|
||||
uint32_t gb_last = gb_first + task->blocks_per_thread;
|
||||
if (gb_last > task->total_blocks) {
|
||||
gb_last = task->total_blocks;
|
||||
}
|
||||
if (gb_first >= gb_last) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &task->trace[i];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_L2FLUSH, gb_first);
|
||||
|
||||
for (uint32_t r = 0; r < task->n_ranges; r++) {
|
||||
const struct l2flush_range * rg = &task->ranges[r];
|
||||
const uint32_t rb_first = rg->block_first;
|
||||
const uint32_t rb_last = rg->block_first + rg->n_blocks;
|
||||
|
||||
const uint32_t lo = gb_first > rb_first ? gb_first : rb_first;
|
||||
const uint32_t hi = gb_last < rb_last ? gb_last : rb_last;
|
||||
if (lo >= hi) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t s = rg->start + (lo - rb_first) * HEX_L2_BLOCK_SIZE;
|
||||
uint32_t e = rg->start + (hi - rb_first) * HEX_L2_BLOCK_SIZE;
|
||||
if (e > rg->end) {
|
||||
e = rg->end;
|
||||
}
|
||||
hex_l2flush((void *) (uintptr_t) s, e - s);
|
||||
}
|
||||
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_L2FLUSH, gb_first);
|
||||
}
|
||||
|
||||
static void merge_dirty_ranges(struct htp_context * ctx) {
|
||||
for (uint32_t i = 0; i < HTP_MAX_DIRTY_RANGES; i++) {
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[i];
|
||||
if (!r->start) continue;
|
||||
|
||||
for (uint32_t j = 0; j < HTP_MAX_DIRTY_RANGES;) {
|
||||
struct htp_dirty_range * s = &ctx->dirty_ranges[j];
|
||||
if (i == j || !s->start || r->end < s->start || s->end < r->start) {
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
r->start = MIN(r->start, s->start);
|
||||
r->end = MAX(r->end, s->end);
|
||||
s->start = 0;
|
||||
s->end = 0;
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void htp_dcache_mark_dirty_tensors(struct htp_context * ctx, const struct htp_tensor * const * tensors, uint32_t n) {
|
||||
const struct htp_tensor * pending[HTP_OP_MAX_OUTPUTS];
|
||||
uint32_t n_pending = 0;
|
||||
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
const struct htp_tensor * t = tensors[i];
|
||||
if (!t || (t->flags & (HTP_TENSOR_WEIGHT | HTP_TENSOR_FENCE))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t t_start = t->data;
|
||||
uint32_t t_end = t_start + t->size;
|
||||
|
||||
bool merged = false;
|
||||
for (uint32_t j = 0; j < HTP_MAX_DIRTY_RANGES; j++) {
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[j];
|
||||
if (!r->start) continue;
|
||||
|
||||
if (r->start <= t_end && t_start <= r->end) {
|
||||
uint32_t new_start = (t_start < r->start) ? t_start : r->start;
|
||||
uint32_t new_end = (t_end > r->end) ? t_end : r->end;
|
||||
r->start = new_start;
|
||||
r->end = new_end;
|
||||
merged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!merged) {
|
||||
pending[n_pending++] = t;
|
||||
}
|
||||
}
|
||||
|
||||
merge_dirty_ranges(ctx);
|
||||
|
||||
if (n_pending == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t empty_indices[HTP_MAX_DIRTY_RANGES];
|
||||
uint32_t active_indices[HTP_MAX_DIRTY_RANGES];
|
||||
uint32_t n_active = 0;
|
||||
uint32_t n_empty = 0;
|
||||
for (uint32_t j = 0; j < HTP_MAX_DIRTY_RANGES; j++) {
|
||||
if (ctx->dirty_ranges[j].start) {
|
||||
active_indices[n_active++] = j;
|
||||
} else {
|
||||
empty_indices[n_empty++] = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (n_pending <= n_empty) {
|
||||
for (uint32_t i = 0; i < n_pending; i++) {
|
||||
uint32_t idx = empty_indices[i];
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[idx];
|
||||
r->start = pending[i]->data;
|
||||
r->end = pending[i]->data + pending[i]->size;
|
||||
}
|
||||
merge_dirty_ranges(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t n_evict = n_pending - n_empty;
|
||||
uint32_t total_evict_size = 0;
|
||||
for (uint32_t i = 0; i < n_evict; i++) {
|
||||
uint32_t idx = active_indices[i];
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[idx];
|
||||
total_evict_size += r->end - r->start;
|
||||
}
|
||||
|
||||
if (total_evict_size > HEX_L2_FLUSH_ALL_THRESHOLD) {
|
||||
flush_all_dcache(ctx);
|
||||
for (uint32_t i = 0; i < n_pending; i++) {
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[i];
|
||||
r->start = pending[i]->data;
|
||||
r->end = pending[i]->data + pending[i]->size;
|
||||
}
|
||||
merge_dirty_ranges(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (total_evict_size > HEX_L2_FLUSH_WQ_THRESHOLD && ctx->n_threads > 1 && n_evict <= HTP_MAX_DIRTY_RANGES) {
|
||||
struct l2flush_multi_task task;
|
||||
task.trace = ctx->trace;
|
||||
task.n_ranges = n_evict;
|
||||
|
||||
uint32_t block_acc = 0;
|
||||
for (uint32_t i = 0; i < n_evict; i++) {
|
||||
uint32_t idx = active_indices[i];
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[idx];
|
||||
|
||||
struct l2flush_range * rg = &task.ranges[i];
|
||||
rg->start = hex_align_down((size_t) r->start, HEX_L2_LINE_SIZE);
|
||||
rg->end = hex_align_up((size_t) r->end, HEX_L2_LINE_SIZE);
|
||||
rg->block_first = block_acc;
|
||||
rg->n_blocks = (rg->end - rg->start + HEX_L2_BLOCK_SIZE - 1) / HEX_L2_BLOCK_SIZE;
|
||||
block_acc += rg->n_blocks;
|
||||
}
|
||||
|
||||
task.total_blocks = block_acc;
|
||||
task.blocks_per_thread = fastdiv(block_acc + ctx->n_threads - 1, &ctx->n_threads_div);
|
||||
|
||||
work_queue_run(ctx->work_queue, l2flush_multi_worker, &task, ctx->n_threads);
|
||||
} else {
|
||||
struct htp_thread_trace * tr = &ctx->trace[0];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_L2FLUSH, 0);
|
||||
for (uint32_t i = 0; i < n_evict; i++) {
|
||||
uint32_t idx = active_indices[i];
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[idx];
|
||||
uint32_t size = r->end - r->start;
|
||||
hex_l2flush((void *) (uintptr_t) r->start, size);
|
||||
}
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_L2FLUSH, 0);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < n_evict; i++) {
|
||||
uint32_t idx = active_indices[i];
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[idx];
|
||||
r->start = pending[i]->data;
|
||||
r->end = pending[i]->data + pending[i]->size;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < n_empty; i++) {
|
||||
uint32_t idx = empty_indices[i];
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[idx];
|
||||
r->start = pending[n_evict + i]->data;
|
||||
r->end = pending[n_evict + i]->data + pending[n_evict + i]->size;
|
||||
}
|
||||
|
||||
merge_dirty_ranges(ctx);
|
||||
}
|
||||
|
||||
static void make_tensor_clean(struct htp_context * ctx, const struct htp_tensor * t) {
|
||||
if (!t || (t->flags & (HTP_TENSOR_WEIGHT | HTP_TENSOR_FENCE))) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t t_start = t->data;
|
||||
uint32_t t_end = t_start + t->size;
|
||||
|
||||
for (uint32_t i = 0; i < HTP_MAX_DIRTY_RANGES; i++) {
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[i];
|
||||
if (!r->start) continue;
|
||||
|
||||
if (r->start < t_end && t_start < r->end) {
|
||||
if (t_start <= r->start && r->end <= t_end) {
|
||||
r->start = 0;
|
||||
r->end = 0;
|
||||
} else if (t_start <= r->start) {
|
||||
r->start = t_end;
|
||||
} else if (r->end <= t_end) {
|
||||
r->end = t_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_tensor_dirty(struct htp_context * ctx, const struct htp_tensor * t) {
|
||||
if (!t || (t->flags & (HTP_TENSOR_WEIGHT | HTP_TENSOR_FENCE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t t_start = t->data;
|
||||
uint32_t t_end = t_start + t->size;
|
||||
|
||||
for (uint32_t i = 0; i < HTP_MAX_DIRTY_RANGES; i++) {
|
||||
struct htp_dirty_range * r = &ctx->dirty_ranges[i];
|
||||
if (!r->start) continue;
|
||||
|
||||
if (r->start < t_end && t_start < r->end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void flush_dirty_ranges(struct htp_context * ctx, const struct htp_dirty_range * ranges, uint32_t n_ranges, uint64_t total_dirty) {
|
||||
if (total_dirty >= HEX_L2_FLUSH_WQ_THRESHOLD && ctx->n_threads > 1) {
|
||||
struct l2flush_multi_task task;
|
||||
task.trace = ctx->trace;
|
||||
task.n_ranges = n_ranges;
|
||||
|
||||
uint32_t block_acc = 0;
|
||||
for (uint32_t i = 0; i < n_ranges; i++) {
|
||||
const struct htp_dirty_range * r = &ranges[i];
|
||||
struct l2flush_range * rg = &task.ranges[i];
|
||||
rg->start = hex_align_down((size_t) r->start, HEX_L2_LINE_SIZE);
|
||||
rg->end = hex_align_up((size_t) r->end, HEX_L2_LINE_SIZE);
|
||||
rg->block_first = block_acc;
|
||||
rg->n_blocks = (rg->end - rg->start + HEX_L2_BLOCK_SIZE - 1) / HEX_L2_BLOCK_SIZE;
|
||||
block_acc += rg->n_blocks;
|
||||
}
|
||||
|
||||
task.total_blocks = block_acc;
|
||||
task.blocks_per_thread = fastdiv(block_acc + ctx->n_threads - 1, &ctx->n_threads_div);
|
||||
|
||||
work_queue_run(ctx->work_queue, l2flush_multi_worker, &task, ctx->n_threads);
|
||||
} else {
|
||||
struct htp_thread_trace * tr = &ctx->trace[0];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_L2FLUSH, 0);
|
||||
for (uint32_t i = 0; i < n_ranges; i++) {
|
||||
const struct htp_dirty_range * r = &ranges[i];
|
||||
hex_l2flush((void *) (uintptr_t) r->start, r->end - r->start);
|
||||
}
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_L2FLUSH, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void htp_dcache_flush_dirty_ranges(struct htp_context * ctx) {
|
||||
struct htp_dirty_range ranges[HTP_MAX_DIRTY_RANGES];
|
||||
uint32_t n_ranges = 0;
|
||||
uint64_t total_dirty = 0;
|
||||
|
||||
for (uint32_t i = 0; i < HTP_MAX_DIRTY_RANGES; i++) {
|
||||
const struct htp_dirty_range * r = &ctx->dirty_ranges[i];
|
||||
if (!r->start) {
|
||||
continue;
|
||||
}
|
||||
ranges[n_ranges++] = *r;
|
||||
total_dirty += r->end - r->start;
|
||||
}
|
||||
|
||||
if (total_dirty == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (total_dirty > HEX_L2_FLUSH_ALL_THRESHOLD) {
|
||||
flush_all_dcache(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
flush_dirty_ranges(ctx, ranges, n_ranges, total_dirty);
|
||||
memset(ctx->dirty_ranges, 0, sizeof(ctx->dirty_ranges));
|
||||
}
|
||||
|
||||
void htp_dcache_flush_dirty_tensors(struct htp_context * ctx, const struct htp_tensor * const * tensors, uint32_t n) {
|
||||
const struct htp_tensor * dirty_tensors[HTP_OP_MAX_INPUTS];
|
||||
struct htp_dirty_range ranges[HTP_OP_MAX_INPUTS];
|
||||
uint32_t n_dirty = 0;
|
||||
uint64_t total_dirty = 0;
|
||||
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
const struct htp_tensor * t = tensors[i];
|
||||
if (is_tensor_dirty(ctx, t)) {
|
||||
dirty_tensors[n_dirty++] = t;
|
||||
ranges[n_dirty - 1].start = t->data;
|
||||
ranges[n_dirty - 1].end = t->data + t->size;
|
||||
total_dirty += t->size;
|
||||
}
|
||||
}
|
||||
|
||||
if (total_dirty == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (total_dirty > HEX_L2_FLUSH_ALL_THRESHOLD) {
|
||||
flush_all_dcache(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
flush_dirty_ranges(ctx, ranges, n_dirty, total_dirty);
|
||||
for (uint32_t i = 0; i < n_dirty; i++) {
|
||||
make_tensor_clean(ctx, dirty_tensors[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef HTP_DCACHE_H
|
||||
#define HTP_DCACHE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "htp-ops.h"
|
||||
|
||||
#define HTP_MAX_DIRTY_RANGES 64
|
||||
|
||||
struct htp_dirty_range {
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
};
|
||||
|
||||
struct htp_context;
|
||||
|
||||
void htp_dcache_flush_dirty_ranges(struct htp_context * ctx);
|
||||
void htp_dcache_flush_dirty_tensors(struct htp_context * ctx, const struct htp_tensor * const * tensors, uint32_t n);
|
||||
void htp_dcache_mark_dirty_tensors(struct htp_context * ctx, const struct htp_tensor * const * tensors, uint32_t n);
|
||||
|
||||
#endif // HTP_DCACHE_H
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef HTP_FENCE_H
|
||||
#define HTP_FENCE_H
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <HAP_farf.h>
|
||||
|
||||
#include "hex-utils.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-ctx.h"
|
||||
|
||||
static inline atomic_uint * htp_mdev_fence_slot(const void * fence_base, uint32_t idx) {
|
||||
return (atomic_uint *) ((const uint8_t *) fence_base + (size_t) idx * HTP_FENCE_SLOT_SIZE);
|
||||
}
|
||||
|
||||
static inline void htp_fence_write(void * fence_ptr, uint32_t seq, uint32_t status) {
|
||||
atomic_uint * fence = (atomic_uint *) fence_ptr;
|
||||
atomic_store(&fence[1], status);
|
||||
atomic_store(&fence[0], seq);
|
||||
asm volatile ("syncht" : : : "memory");
|
||||
Q6_dccleaninva_A((void *) fence);
|
||||
}
|
||||
|
||||
static inline void htp_fence_read(const void * fence_ptr, uint32_t * seq, uint32_t * status) {
|
||||
const atomic_uint * fence = (const atomic_uint *) fence_ptr;
|
||||
Q6_dccleaninva_A((void *) fence);
|
||||
asm volatile ("syncht" : : : "memory");
|
||||
*seq = atomic_load(&fence[0]);
|
||||
*status = atomic_load(&fence[1]);
|
||||
}
|
||||
|
||||
static inline void htp_mdev_group_barrier(struct htp_ops_context * octx) {
|
||||
struct htp_context * ctx = octx->ctx;
|
||||
if (ctx->mdev.count <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t seq = ++ctx->mdev.fence_seq;
|
||||
|
||||
struct htp_thread_trace * tr = &ctx->trace[0];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_FENCE, (uint16_t) seq);
|
||||
|
||||
const uint32_t mdev_idx = ctx->mdev.idx;
|
||||
const uint32_t mdev_count = ctx->mdev.count;
|
||||
|
||||
uint8_t * fence_base = ctx->mdev.fence_base;
|
||||
atomic_uint * my_fence = htp_mdev_fence_slot(fence_base, mdev_idx);
|
||||
htp_fence_write(my_fence, seq, octx->status);
|
||||
|
||||
for (uint32_t d = 0; d < mdev_count; d++) {
|
||||
if (d == mdev_idx) continue;
|
||||
atomic_uint * peer_fence = htp_mdev_fence_slot(fence_base, d);
|
||||
uint64_t spins = 0;
|
||||
while (1) {
|
||||
uint32_t peer_seq;
|
||||
uint32_t peer_status;
|
||||
htp_fence_read(peer_fence, &peer_seq, &peer_status);
|
||||
if ((int32_t)(peer_seq - seq) >= 0) {
|
||||
if (peer_status > HTP_STATUS_OK) {
|
||||
FARF(ERROR, "ggml-hex: mdev %u peer %u failed with status %u : seq 0x%08x\n",
|
||||
mdev_idx, d, peer_status, seq);
|
||||
htp_ops_context_set_status(octx, peer_status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (++spins == 10000) {
|
||||
FARF(ALWAYS, "ggml-hex: mdev %u waiting for mdev %u : seq 0x%08x (b %u op %u) my-fence %p peer-fence %p peer-seq 0x%08x (diff %d)\n",
|
||||
mdev_idx, d, seq, seq >> 12, seq & 0xfff, my_fence, peer_fence, peer_seq, (int32_t)(peer_seq - seq));
|
||||
}
|
||||
if (spins > HTP_FENCE_TIMEOUT) {
|
||||
FARF(ERROR, "ggml-hex: mdev %u timeout waiting for mdev %u : seq 0x%08x (b %u op %u) peer-fence %p peer-seq 0x%08x\n",
|
||||
mdev_idx, d, seq, seq >> 12, seq & 0xfff, peer_fence, peer_seq);
|
||||
htp_ops_context_set_status(octx, HTP_STATUS_INTERNAL_ERR);
|
||||
break;
|
||||
}
|
||||
hex_pause();
|
||||
}
|
||||
}
|
||||
asm volatile ("syncht" : : : "memory");
|
||||
|
||||
if (octx->status > HTP_STATUS_OK) {
|
||||
htp_fence_write(my_fence, seq, octx->status);
|
||||
}
|
||||
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_FENCE, (uint16_t) seq);
|
||||
}
|
||||
|
||||
#endif // HTP_FENCE_H
|
||||
@@ -0,0 +1,281 @@
|
||||
#ifndef HTP_OPS_H
|
||||
#define HTP_OPS_H
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
// ggml-common.h must be included prio to this header
|
||||
|
||||
enum htp_status {
|
||||
HTP_STATUS_OK = 1,
|
||||
HTP_STATUS_INTERNAL_ERR = 2,
|
||||
HTP_STATUS_NO_SUPPORT = 3,
|
||||
HTP_STATUS_INVAL_PARAMS = 4,
|
||||
HTP_STATUS_VTCM_TOO_SMALL = 5,
|
||||
};
|
||||
|
||||
// First set of values must match the ggml_type.
|
||||
// Duplicated here because we can't include full ggml.h in the htp build.
|
||||
// We have some static_asserts in the cpp code to ensure things are in sync.
|
||||
enum htp_data_type {
|
||||
HTP_TYPE_F32 = 0,
|
||||
HTP_TYPE_F16 = 1,
|
||||
HTP_TYPE_Q4_0 = 2,
|
||||
HTP_TYPE_Q4_1 = 3,
|
||||
HTP_TYPE_Q8_0 = 8,
|
||||
HTP_TYPE_Q2_K = 10,
|
||||
HTP_TYPE_Q3_K = 11,
|
||||
HTP_TYPE_Q4_K = 12,
|
||||
HTP_TYPE_Q5_K = 13,
|
||||
HTP_TYPE_Q6_K = 14,
|
||||
HTP_TYPE_IQ4_NL = 20,
|
||||
HTP_TYPE_I32 = 26,
|
||||
HTP_TYPE_I64 = 27,
|
||||
HTP_TYPE_BF16 = 30,
|
||||
HTP_TYPE_MXFP4 = 39,
|
||||
HTP_TYPE_Q1_0 = 41,
|
||||
HTP_TYPE_Q2_0 = 42,
|
||||
|
||||
HTP_TYPE_INVALID
|
||||
};
|
||||
|
||||
// Do not reorder first 4 (used as an index)
|
||||
enum htp_op_code {
|
||||
HTP_OP_MUL = 0,
|
||||
HTP_OP_ADD = 1,
|
||||
HTP_OP_SUB = 2,
|
||||
HTP_OP_DIV = 3,
|
||||
HTP_OP_MUL_MAT,
|
||||
HTP_OP_MUL_MAT_ID,
|
||||
HTP_OP_MUL_MAT_NX,
|
||||
HTP_OP_MUL_MAT_ID_NX,
|
||||
HTP_OP_MUL_MAT_ADD,
|
||||
HTP_OP_RMS_NORM,
|
||||
HTP_OP_RMS_NORM_MUL,
|
||||
HTP_OP_UNARY_SILU,
|
||||
HTP_OP_UNARY_GELU,
|
||||
HTP_OP_UNARY_SIGMOID,
|
||||
HTP_OP_UNARY_EXP,
|
||||
HTP_OP_UNARY_NEG,
|
||||
HTP_OP_UNARY_SOFTPLUS,
|
||||
HTP_OP_UNARY_TANH,
|
||||
HTP_OP_UNARY_ABS,
|
||||
HTP_OP_UNARY_LOG,
|
||||
HTP_OP_UNARY_RELU,
|
||||
HTP_OP_UNARY_STEP,
|
||||
HTP_OP_GLU_SWIGLU,
|
||||
HTP_OP_GLU_SWIGLU_OAI,
|
||||
HTP_OP_GLU_GEGLU,
|
||||
HTP_OP_GLU_GEGLU_QUICK,
|
||||
HTP_OP_SOFTMAX,
|
||||
HTP_OP_ADD_ID,
|
||||
HTP_OP_ROPE,
|
||||
HTP_OP_FLASH_ATTN_EXT,
|
||||
HTP_OP_SET_ROWS,
|
||||
HTP_OP_GET_ROWS,
|
||||
HTP_OP_SCALE,
|
||||
HTP_OP_CPY,
|
||||
HTP_OP_CPY_FENCE,
|
||||
HTP_OP_ARGSORT,
|
||||
HTP_OP_TOP_K,
|
||||
HTP_OP_SQR,
|
||||
HTP_OP_SQRT,
|
||||
HTP_OP_SUM,
|
||||
HTP_OP_SUM_ROWS,
|
||||
HTP_OP_SSM_CONV,
|
||||
HTP_OP_REPEAT,
|
||||
HTP_OP_CUMSUM,
|
||||
HTP_OP_FILL,
|
||||
HTP_OP_DIAG,
|
||||
HTP_OP_SOLVE_TRI,
|
||||
HTP_OP_L2_NORM,
|
||||
HTP_OP_GATED_DELTA_NET,
|
||||
HTP_OP_TRI,
|
||||
HTP_OP_PAD,
|
||||
HTP_OP_NORM,
|
||||
HTP_OP_CONCAT,
|
||||
HTP_OP_CLAMP,
|
||||
HTP_OP_LEAKY_RELU,
|
||||
HTP_OP_IM2COL,
|
||||
HTP_OP_FENCE,
|
||||
HTP_OP_ALLREDUCE,
|
||||
HTP_OP_ALLREDUCE_ADD,
|
||||
HTP_OP_GLU_SWIGLU_CLAMP,
|
||||
HTP_OP_MDEV_GROUP,
|
||||
HTP_OP_ROLL,
|
||||
HTP_OP_ARGMAX,
|
||||
HTP_OP_UNARY_GELU_ERF,
|
||||
HTP_OP_GLU_GEGLU_ERF,
|
||||
HTP_OP_POOL_2D,
|
||||
HTP_OP_POOL_1D,
|
||||
HTP_OP_UNARY_GELU_QUICK,
|
||||
HTP_OP_IM2COL_3D,
|
||||
HTP_OP_HADAMARD,
|
||||
HTP_OP_GLU_REGLU,
|
||||
HTP_OP_GLU_SIGMOID,
|
||||
HTP_OP_UNARY_CEIL,
|
||||
HTP_OP_UNARY_SGN,
|
||||
|
||||
HTP_OP_INVALID
|
||||
};
|
||||
|
||||
#define HTP_OP_MAX_DIMS 4 // aka GGML_MAX_DIMS
|
||||
#define HTP_OP_MAX_INPUTS 10 // aka GGML_MAX_SRCS
|
||||
#define HTP_OP_MAX_OUTPUTS 4
|
||||
#define HTP_OP_MAX_PARAMS 16 // aka GGML_MAX_OP_PARAMS
|
||||
#define HTP_OP_MAX_KERN_PARAMS 32
|
||||
|
||||
#define HTP_OP_MAX_BUFS 64
|
||||
#define HTP_OP_MAX_TENSORS 65534 // must stay under 64K (uint16)
|
||||
|
||||
#define HTP_FENCE_TIMEOUT (1000000000ULL)
|
||||
#define HTP_FENCE_SLOT_SIZE 128
|
||||
|
||||
#define HTP_OP_MAX_VMEM_DEFAULT (3355443200u)
|
||||
|
||||
#define HTP_MMAP_MAX_VMEM (2147483648u)
|
||||
|
||||
enum htp_tensor_flags {
|
||||
HTP_TENSOR_WEIGHT = (1U << 0), // Tensor buffer model weight data (not compute)
|
||||
HTP_TENSOR_TILED = (1U << 1), // Tensor is in tiled format
|
||||
HTP_TENSOR_FENCE = (1U << 2), // Tensor is synchronization fence (explicitly managed)
|
||||
HTP_TENSOR_FUSEABLE = (1U << 3), // Tensor can be fused (host-only)
|
||||
};
|
||||
|
||||
enum htp_buf_flags {
|
||||
HTP_BUF_EXTENDED = (1U << 0),
|
||||
};
|
||||
|
||||
// Tensor descriptor
|
||||
struct htp_tensor {
|
||||
uint64_t data; // Buffer offset in the messages, and data pointer on the NPU
|
||||
uint32_t size; // Data size in bytes
|
||||
uint32_t flags; // Buffer / tensor flags
|
||||
uint32_t type; // Data type
|
||||
uint16_t bi; // Buffer index
|
||||
uint16_t ti; // Tensor index
|
||||
uint32_t ne[HTP_OP_MAX_DIMS]; // Number of elements
|
||||
uint32_t nb[HTP_OP_MAX_DIMS]; // Stride in bytes (see ggml.h ggml_tensor)
|
||||
};
|
||||
|
||||
// Buffer descriptor
|
||||
struct htp_buf_desc {
|
||||
uint64_t base; // base address
|
||||
uint64_t size; // total size
|
||||
uint32_t flags; // HTP_BUF_*
|
||||
uint32_t fd; // file descriptor
|
||||
};
|
||||
|
||||
enum htp_op_flags {
|
||||
HTP_OPFLAGS_STUB = (1U << 0),
|
||||
};
|
||||
|
||||
// Op descriptor
|
||||
struct htp_op_desc {
|
||||
uint32_t opcode; // GGML/HTP Op
|
||||
uint32_t flags; // Op flags
|
||||
int32_t params[HTP_OP_MAX_PARAMS]; // Params for the op, e.g. epsilon of RMS norm
|
||||
int32_t kernel_params[HTP_OP_MAX_KERN_PARAMS]; // generic blob for host-precomputed parameters
|
||||
uint16_t src[HTP_OP_MAX_INPUTS]; // Input tensors indices
|
||||
uint16_t dst[HTP_OP_MAX_OUTPUTS]; // Output tensor indices
|
||||
uint16_t pad[2]; // padding to align to 64 bits
|
||||
};
|
||||
|
||||
#ifndef HTP_MAX_NTHREADS
|
||||
#define HTP_MAX_NTHREADS 10
|
||||
#endif
|
||||
|
||||
#define HTP_TRACE_MAX_EVENTS 256
|
||||
|
||||
enum htp_profiler_mode {
|
||||
HTP_PROF_DISABLED = 0,
|
||||
HTP_PROF_BASIC = 1,
|
||||
HTP_PROF_PMU = 2,
|
||||
HTP_PROF_TRACE = 3,
|
||||
};
|
||||
|
||||
// DCVS/HMX power profile requested for the session.
|
||||
enum htp_power_mode {
|
||||
HTP_POWER_MODE_LOW_POWER_SAVER = 0,
|
||||
HTP_POWER_MODE_POWER_SAVER = 1,
|
||||
HTP_POWER_MODE_HIGH_POWER_SAVER = 2,
|
||||
HTP_POWER_MODE_LOW_BALANCED = 3,
|
||||
HTP_POWER_MODE_BALANCED = 4,
|
||||
HTP_POWER_MODE_HIGH_PERFORMANCE = 5,
|
||||
HTP_POWER_MODE_SUSTAINED_HIGH_PERFORMANCE = 6,
|
||||
HTP_POWER_MODE_BURST = 7,
|
||||
|
||||
HTP_POWER_MODE_COUNT
|
||||
};
|
||||
|
||||
enum htp_trace_event_id {
|
||||
HTP_TRACE_EVT_DMA = 0,
|
||||
HTP_TRACE_EVT_L2FLUSH = 1,
|
||||
HTP_TRACE_EVT_INIT = 2,
|
||||
HTP_TRACE_EVT_BUFF = 3,
|
||||
HTP_TRACE_EVT_FENCE = 4,
|
||||
|
||||
HTP_TRACE_EVT_HVX_COMP = 20,
|
||||
HTP_TRACE_EVT_HVX_A_QUANT = 21,
|
||||
HTP_TRACE_EVT_HVX_A_PREP = 22,
|
||||
HTP_TRACE_EVT_HVX_W_DEQUANT = 23,
|
||||
HTP_TRACE_EVT_HVX_W_PREP = 24,
|
||||
HTP_TRACE_EVT_HVX_O_PROC = 25,
|
||||
HTP_TRACE_EVT_HVX_FA_QK = 26,
|
||||
HTP_TRACE_EVT_HVX_FA_SFM = 27,
|
||||
HTP_TRACE_EVT_HVX_FA_Q_PREP = 28,
|
||||
HTP_TRACE_EVT_HVX_FA_K_PREP = 29,
|
||||
HTP_TRACE_EVT_HVX_FA_V_PREP = 30,
|
||||
|
||||
HTP_TRACE_EVT_HVX_GDN_PREP = 31,
|
||||
HTP_TRACE_EVT_HVX_GDN_SOLVE = 32,
|
||||
HTP_TRACE_EVT_HVX_GDN_V_PREP = 33,
|
||||
HTP_TRACE_EVT_HVX_GDN_D_PREP = 34,
|
||||
HTP_TRACE_EVT_HVX_GDN_OUT = 35,
|
||||
HTP_TRACE_EVT_HVX_GDN_STATE = 36,
|
||||
HTP_TRACE_EVT_HVX_GDN_REM = 37,
|
||||
|
||||
HTP_TRACE_EVT_HMX_COMP = 40,
|
||||
};
|
||||
|
||||
struct htp_trace_desc {
|
||||
uint32_t cycles; // lower 32-bits of cycle counter
|
||||
uint16_t id; // Event ID
|
||||
uint16_t info; // bit 15: is_stop. bits 14-0: tile/chunk index or other metadata.
|
||||
};
|
||||
|
||||
#define HTP_PROF_PMU_NCNT 8
|
||||
|
||||
// Profile descriptor
|
||||
struct htp_prof_desc {
|
||||
uint32_t opcode; // GGML/HTP Op
|
||||
uint32_t usecs; // Number of usec
|
||||
uint32_t cycles_start; // Start cycle counter
|
||||
uint32_t cycles_stop; // Stop cycle counter
|
||||
uint32_t pmu[HTP_PROF_PMU_NCNT]; // PMU counters
|
||||
};
|
||||
|
||||
struct htp_opbatch_req {
|
||||
uint64_t seq; // Sequence number
|
||||
uint32_t n_bufs; // Number of buffers
|
||||
uint32_t n_tensors; // Number of tensors
|
||||
uint32_t n_ops; // Number of ops
|
||||
uint32_t n_traces; // Number of trace descriptors per thread
|
||||
// struct htp_buf_desc bufs[]; -- dspqueue buf 0
|
||||
// struct htp_tensor tensors[]; -- dspqueue buf 0
|
||||
// struct htp_op_desc ops[]; -- dspqueue buf 0
|
||||
};
|
||||
|
||||
struct htp_opbatch_rsp {
|
||||
uint64_t seq; // Sequence number
|
||||
uint64_t cycles_start; // Start cycle counter
|
||||
uint64_t cycles_stop; // Stop cycle counter
|
||||
uint32_t status; // HTP_STATUS_...
|
||||
uint32_t n_bufs; // Number of buffers
|
||||
uint32_t n_tensors; // Number of tensors
|
||||
uint32_t n_ops; // Number of op profile descriptors
|
||||
uint32_t usecs; // Number of usec
|
||||
uint32_t n_traces[HTP_MAX_NTHREADS + 1];
|
||||
// struct htp_prof_desc profs[]; -- dspqueue buf 0
|
||||
};
|
||||
|
||||
#endif /* HTP_OPS_H */
|
||||
@@ -0,0 +1,512 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
import zlib
|
||||
|
||||
ELF_MAGIC = b'\x7fELF'
|
||||
ELF_CLASS_INDEX = 4
|
||||
ELFCLASS32 = 1
|
||||
ELFCLASS64 = 2
|
||||
ELF_VERSION_INDEX = 6
|
||||
EV_CURRENT = 1
|
||||
MAX_PHDR_COUNT = 100
|
||||
|
||||
LOAD_TYPE = 0x1
|
||||
|
||||
SHT_NOBITS = 8
|
||||
|
||||
SWIV_MAGIC = b'SWIV'
|
||||
SWIV_SEG_SIZE_MIN = 16
|
||||
# SHT_LOUSER plus ASCII "SWIV".
|
||||
SWIV_SECTION_TYPE = 0xD3574956
|
||||
|
||||
logger = logging.getLogger("htp-swiv")
|
||||
|
||||
|
||||
class Elf_Ehdr_common:
|
||||
s = struct.Struct('16sHHI')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf_Ehdr_common.s).unpack(data)
|
||||
self.e_ident = unpacked_data[0]
|
||||
self.e_type = unpacked_data[1]
|
||||
self.e_machine = unpacked_data[2]
|
||||
self.e_version = unpacked_data[3]
|
||||
|
||||
|
||||
class Elf32_Ehdr:
|
||||
s = struct.Struct('16sHHIIIIIHHHHHH')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf32_Ehdr.s).unpack(data)
|
||||
self.e_ident = unpacked_data[0]
|
||||
self.e_type = unpacked_data[1]
|
||||
self.e_machine = unpacked_data[2]
|
||||
self.e_version = unpacked_data[3]
|
||||
self.e_entry = unpacked_data[4]
|
||||
self.e_phoff = unpacked_data[5]
|
||||
self.e_shoff = unpacked_data[6]
|
||||
self.e_flags = unpacked_data[7]
|
||||
self.e_ehsize = unpacked_data[8]
|
||||
self.e_phentsize = unpacked_data[9]
|
||||
self.e_phnum = unpacked_data[10]
|
||||
self.e_shentsize = unpacked_data[11]
|
||||
self.e_shnum = unpacked_data[12]
|
||||
self.e_shstrndx = unpacked_data[13]
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.e_ident,
|
||||
self.e_type,
|
||||
self.e_machine,
|
||||
self.e_version,
|
||||
self.e_entry,
|
||||
self.e_phoff,
|
||||
self.e_shoff,
|
||||
self.e_flags,
|
||||
self.e_ehsize,
|
||||
self.e_phentsize,
|
||||
self.e_phnum,
|
||||
self.e_shentsize,
|
||||
self.e_shnum,
|
||||
self.e_shstrndx
|
||||
]
|
||||
|
||||
return (Elf32_Ehdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf32_Phdr:
|
||||
s = struct.Struct('I' * 8)
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf32_Phdr.s).unpack(data)
|
||||
self.p_type = unpacked_data[0]
|
||||
self.p_offset = unpacked_data[1]
|
||||
self.p_vaddr = unpacked_data[2]
|
||||
self.p_paddr = unpacked_data[3]
|
||||
self.p_filesz = unpacked_data[4]
|
||||
self.p_memsz = unpacked_data[5]
|
||||
self.p_flags = unpacked_data[6]
|
||||
self.p_align = unpacked_data[7]
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.p_type,
|
||||
self.p_offset,
|
||||
self.p_vaddr,
|
||||
self.p_paddr,
|
||||
self.p_filesz,
|
||||
self.p_memsz,
|
||||
self.p_flags,
|
||||
self.p_align
|
||||
]
|
||||
|
||||
return (Elf32_Phdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf32_Shdr:
|
||||
s = struct.Struct('I' * 10)
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf32_Shdr.s).unpack(data)
|
||||
self.sh_name = unpacked_data[0]
|
||||
self.sh_type = unpacked_data[1]
|
||||
self.sh_flags = unpacked_data[2]
|
||||
self.sh_addr = unpacked_data[3]
|
||||
self.sh_offset = unpacked_data[4]
|
||||
self.sh_size = unpacked_data[5]
|
||||
self.sh_link = unpacked_data[6]
|
||||
self.sh_info = unpacked_data[7]
|
||||
self.sh_addralign = unpacked_data[8]
|
||||
self.sh_entsize = unpacked_data[9]
|
||||
self.sh_name_str = ""
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.sh_name,
|
||||
self.sh_type,
|
||||
self.sh_flags,
|
||||
self.sh_addr,
|
||||
self.sh_offset,
|
||||
self.sh_size,
|
||||
self.sh_link,
|
||||
self.sh_info,
|
||||
self.sh_addralign,
|
||||
self.sh_entsize]
|
||||
|
||||
return (Elf32_Shdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf64_Ehdr:
|
||||
s = struct.Struct('16sHHIQQQIHHHHHH')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf64_Ehdr.s).unpack(data)
|
||||
self.e_ident = unpacked_data[0]
|
||||
self.e_type = unpacked_data[1]
|
||||
self.e_machine = unpacked_data[2]
|
||||
self.e_version = unpacked_data[3]
|
||||
self.e_entry = unpacked_data[4]
|
||||
self.e_phoff = unpacked_data[5]
|
||||
self.e_shoff = unpacked_data[6]
|
||||
self.e_flags = unpacked_data[7]
|
||||
self.e_ehsize = unpacked_data[8]
|
||||
self.e_phentsize = unpacked_data[9]
|
||||
self.e_phnum = unpacked_data[10]
|
||||
self.e_shentsize = unpacked_data[11]
|
||||
self.e_shnum = unpacked_data[12]
|
||||
self.e_shstrndx = unpacked_data[13]
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.e_ident,
|
||||
self.e_type,
|
||||
self.e_machine,
|
||||
self.e_version,
|
||||
self.e_entry,
|
||||
self.e_phoff,
|
||||
self.e_shoff,
|
||||
self.e_flags,
|
||||
self.e_ehsize,
|
||||
self.e_phentsize,
|
||||
self.e_phnum,
|
||||
self.e_shentsize,
|
||||
self.e_shnum,
|
||||
self.e_shstrndx
|
||||
]
|
||||
|
||||
return (Elf64_Ehdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf64_Phdr:
|
||||
s = struct.Struct('IIQQQQQQ')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf64_Phdr.s).unpack(data)
|
||||
self.p_type = unpacked_data[0]
|
||||
self.p_flags = unpacked_data[1]
|
||||
self.p_offset = unpacked_data[2]
|
||||
self.p_vaddr = unpacked_data[3]
|
||||
self.p_paddr = unpacked_data[4]
|
||||
self.p_filesz = unpacked_data[5]
|
||||
self.p_memsz = unpacked_data[6]
|
||||
self.p_align = unpacked_data[7]
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.p_type,
|
||||
self.p_flags,
|
||||
self.p_offset,
|
||||
self.p_vaddr,
|
||||
self.p_paddr,
|
||||
self.p_filesz,
|
||||
self.p_memsz,
|
||||
self.p_align
|
||||
]
|
||||
|
||||
return (Elf64_Phdr.s).pack(*values)
|
||||
|
||||
|
||||
class Elf64_Shdr:
|
||||
s = struct.Struct('IIQQQQIIQQ')
|
||||
|
||||
def __init__(self, data):
|
||||
unpacked_data = (Elf64_Shdr.s).unpack(data)
|
||||
self.sh_name = unpacked_data[0]
|
||||
self.sh_type = unpacked_data[1]
|
||||
self.sh_flags = unpacked_data[2]
|
||||
self.sh_addr = unpacked_data[3]
|
||||
self.sh_offset = unpacked_data[4]
|
||||
self.sh_size = unpacked_data[5]
|
||||
self.sh_link = unpacked_data[6]
|
||||
self.sh_info = unpacked_data[7]
|
||||
self.sh_addralign = unpacked_data[8]
|
||||
self.sh_entsize = unpacked_data[9]
|
||||
self.sh_name_str = ""
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.sh_name,
|
||||
self.sh_type,
|
||||
self.sh_flags,
|
||||
self.sh_addr,
|
||||
self.sh_offset,
|
||||
self.sh_size,
|
||||
self.sh_link,
|
||||
self.sh_info,
|
||||
self.sh_addralign,
|
||||
self.sh_entsize]
|
||||
|
||||
return (Elf64_Shdr.s).pack(*values)
|
||||
|
||||
|
||||
class SWIV_Seg:
|
||||
s = struct.Struct('4sIQ')
|
||||
|
||||
def __init__(self):
|
||||
self.magic = SWIV_MAGIC
|
||||
self.crc = 0
|
||||
self.padding = 0
|
||||
|
||||
def get_packed_data(self):
|
||||
values = [self.magic,
|
||||
self.crc,
|
||||
self.padding
|
||||
]
|
||||
|
||||
return (SWIV_Seg.s).pack(*values)
|
||||
|
||||
|
||||
def is_elf64(elf_header):
|
||||
return elf_header.e_ident[ELF_CLASS_INDEX] == ELFCLASS64
|
||||
|
||||
|
||||
def verify_elf_header(elf_header):
|
||||
ident = elf_header.e_ident
|
||||
return (ident.startswith(ELF_MAGIC)
|
||||
and ident[ELF_CLASS_INDEX] in (ELFCLASS32, ELFCLASS64)
|
||||
and ident[ELF_VERSION_INDEX] == EV_CURRENT)
|
||||
|
||||
|
||||
def file_copy_offset(in_fp, in_off, out_fp, out_off, num_bytes):
|
||||
in_fp.seek(in_off)
|
||||
read_in = in_fp.read(num_bytes)
|
||||
out_fp.seek(out_off)
|
||||
out_fp.write(read_in)
|
||||
|
||||
return num_bytes
|
||||
|
||||
|
||||
def preprocess_elf_file(elf_file_name):
|
||||
with open(elf_file_name, 'rb') as elf_fp:
|
||||
elf_header = Elf_Ehdr_common(elf_fp.read(Elf_Ehdr_common.s.size))
|
||||
|
||||
if verify_elf_header(elf_header) is False:
|
||||
raise RuntimeError("Input ELF verification failed : " + elf_file_name)
|
||||
|
||||
elf_fp.seek(0)
|
||||
|
||||
if is_elf64(elf_header):
|
||||
elf_header = Elf64_Ehdr(elf_fp.read(Elf64_Ehdr.s.size))
|
||||
else:
|
||||
elf_header = Elf32_Ehdr(elf_fp.read(Elf32_Ehdr.s.size))
|
||||
|
||||
phdr_table = []
|
||||
|
||||
phdr_size = elf_header.e_phentsize
|
||||
|
||||
file_offset = elf_header.e_phoff
|
||||
elf_fp.seek(file_offset)
|
||||
|
||||
for _ in range(elf_header.e_phnum):
|
||||
if is_elf64(elf_header):
|
||||
phdr_table.append(Elf64_Phdr(elf_fp.read(phdr_size)))
|
||||
else:
|
||||
phdr_table.append(Elf32_Phdr(elf_fp.read(phdr_size)))
|
||||
|
||||
return [elf_header, phdr_table]
|
||||
|
||||
|
||||
def process_section_header(elf_file_name):
|
||||
with open(elf_file_name, 'rb') as elf_fp:
|
||||
elf_header = Elf_Ehdr_common(elf_fp.read(Elf_Ehdr_common.s.size))
|
||||
|
||||
if verify_elf_header(elf_header) is False:
|
||||
raise RuntimeError("Input ELF verification failed : " + elf_file_name)
|
||||
|
||||
elf_fp.seek(0)
|
||||
|
||||
if is_elf64(elf_header):
|
||||
elf_header = Elf64_Ehdr(elf_fp.read(Elf64_Ehdr.s.size))
|
||||
else:
|
||||
elf_header = Elf32_Ehdr(elf_fp.read(Elf32_Ehdr.s.size))
|
||||
|
||||
shdr_table = []
|
||||
|
||||
shdr_size = elf_header.e_shentsize
|
||||
elf_fp.seek(elf_header.e_shoff)
|
||||
|
||||
for _ in range(elf_header.e_shnum):
|
||||
if is_elf64(elf_header):
|
||||
shdr_table.append(Elf64_Shdr(elf_fp.read(shdr_size)))
|
||||
else:
|
||||
shdr_table.append(Elf32_Shdr(elf_fp.read(shdr_size)))
|
||||
|
||||
return shdr_table
|
||||
|
||||
|
||||
def crc32_update_file(elf_fp, offset, size, crc):
|
||||
elf_fp.seek(offset)
|
||||
remaining = size
|
||||
while remaining > 0:
|
||||
chunk = elf_fp.read(min(1024 * 1024, remaining))
|
||||
if not chunk:
|
||||
raise RuntimeError("Unexpected EOF while calculating CRC")
|
||||
crc = zlib.crc32(chunk, crc)
|
||||
remaining -= len(chunk)
|
||||
return crc
|
||||
|
||||
|
||||
def crc32_update_zeros(size, crc):
|
||||
zero_chunk = b'\x00' * min(1024 * 1024, size)
|
||||
remaining = size
|
||||
while remaining > 0:
|
||||
chunk = zero_chunk[:min(len(zero_chunk), remaining)]
|
||||
crc = zlib.crc32(chunk, crc)
|
||||
remaining -= len(chunk)
|
||||
return crc
|
||||
|
||||
|
||||
def crc32_calculate(elf_fp, phdr_table):
|
||||
crc = 0
|
||||
|
||||
# SWIV validates loadable segment contents, including BSS as zeros.
|
||||
for curr_phdr in phdr_table:
|
||||
if curr_phdr.p_type != LOAD_TYPE:
|
||||
continue
|
||||
|
||||
crc = crc32_update_file(elf_fp, curr_phdr.p_offset, curr_phdr.p_filesz, crc)
|
||||
|
||||
zero_size = curr_phdr.p_memsz - curr_phdr.p_filesz
|
||||
if zero_size > 0:
|
||||
crc = crc32_update_zeros(zero_size, crc)
|
||||
|
||||
crc &= 0xffffffff
|
||||
logger.info("calculated CRC: %#x", crc)
|
||||
|
||||
return crc
|
||||
|
||||
|
||||
def verify_swiv_seg(elf_in_fp, offset):
|
||||
|
||||
elf_in_fp.seek(offset)
|
||||
swiv_seg = SWIV_Seg()
|
||||
unpacked_data = (SWIV_Seg.s).unpack(elf_in_fp.read(SWIV_SEG_SIZE_MIN))
|
||||
swiv_seg.magic = unpacked_data[0]
|
||||
|
||||
return swiv_seg.magic == SWIV_MAGIC
|
||||
|
||||
|
||||
def same_output_file(input_elf, output_elf):
|
||||
try:
|
||||
return os.path.exists(output_elf) and os.path.samefile(input_elf, output_elf)
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
|
||||
def make_output_target(input_elf, output_elf):
|
||||
if not same_output_file(input_elf, output_elf):
|
||||
return output_elf, None
|
||||
|
||||
output_dir = os.path.dirname(os.path.abspath(output_elf))
|
||||
output_base = os.path.basename(output_elf)
|
||||
fd, temp_output = tempfile.mkstemp(prefix=output_base + ".", suffix=".tmp", dir=output_dir)
|
||||
os.close(fd)
|
||||
shutil.copymode(input_elf, temp_output)
|
||||
return temp_output, temp_output
|
||||
|
||||
|
||||
def swiv_generate(input_elf, output_elf):
|
||||
output_target, temp_output = make_output_target(input_elf, output_elf)
|
||||
|
||||
try:
|
||||
with open(input_elf, "rb") as elf_in_fp, open(output_target, "wb+") as elf_out_fp:
|
||||
[elf_header, phdr_table] = preprocess_elf_file(input_elf)
|
||||
|
||||
num_phdrs = elf_header.e_phnum
|
||||
num_shdrs = elf_header.e_shnum
|
||||
phdr_size = elf_header.e_phentsize
|
||||
|
||||
if num_phdrs == 0 or num_phdrs > MAX_PHDR_COUNT:
|
||||
raise RuntimeError("Input ELF has invalid number of program headers.")
|
||||
|
||||
if elf_header.e_shoff == 0 or num_shdrs == 0:
|
||||
raise RuntimeError("Input ELF does not contain a section header table.")
|
||||
|
||||
shdr_table_input = process_section_header(input_elf)
|
||||
shdr_table_output = process_section_header(input_elf)
|
||||
|
||||
last_phdr = phdr_table[num_phdrs - 1]
|
||||
if last_phdr.p_memsz == SWIV_SEG_SIZE_MIN and verify_swiv_seg(elf_in_fp, last_phdr.p_offset):
|
||||
raise RuntimeError("A SWIV segment is detected in the input ELF.")
|
||||
|
||||
last_shdr = shdr_table_input[elf_header.e_shnum - 1]
|
||||
if last_shdr.sh_size == SWIV_SEG_SIZE_MIN and verify_swiv_seg(elf_in_fp, last_shdr.sh_offset):
|
||||
raise RuntimeError("A SWIV section is detected in the input ELF.")
|
||||
|
||||
if is_elf64(elf_header):
|
||||
swiv_shdr = Elf64_Shdr(b'\0' * Elf64_Shdr.s.size)
|
||||
else:
|
||||
swiv_shdr = Elf32_Shdr(b'\0' * Elf32_Shdr.s.size)
|
||||
|
||||
swiv_shdr.sh_name = 0
|
||||
swiv_shdr.sh_type = SWIV_SECTION_TYPE
|
||||
swiv_shdr.sh_flags = 0
|
||||
swiv_shdr.sh_addr = 0
|
||||
swiv_shdr.sh_size = SWIV_SEG_SIZE_MIN
|
||||
swiv_shdr.sh_offset = elf_header.e_shoff + (elf_header.e_shnum + 1) * elf_header.e_shentsize
|
||||
shdr_table_output.append(swiv_shdr)
|
||||
elf_header.e_shnum += 1
|
||||
|
||||
if elf_header.e_phoff != 0:
|
||||
for phdr in phdr_table:
|
||||
file_copy_offset(elf_in_fp, phdr.p_offset, elf_out_fp, phdr.p_offset, phdr.p_filesz)
|
||||
|
||||
elf_out_fp.seek(0)
|
||||
elf_out_fp.write(elf_header.get_packed_data())
|
||||
|
||||
phdr_start = elf_header.e_phoff
|
||||
for phdr in phdr_table:
|
||||
elf_out_fp.seek(phdr_start)
|
||||
elf_out_fp.write(phdr.get_packed_data())
|
||||
phdr_start += phdr_size
|
||||
|
||||
for shdr in shdr_table_input[:num_shdrs]:
|
||||
if shdr.sh_type == SHT_NOBITS or shdr.sh_size == 0:
|
||||
continue
|
||||
file_copy_offset(elf_in_fp, shdr.sh_offset, elf_out_fp, shdr.sh_offset, shdr.sh_size)
|
||||
|
||||
shdr_start = elf_header.e_shoff
|
||||
for shdr in shdr_table_output:
|
||||
elf_out_fp.seek(shdr_start)
|
||||
elf_out_fp.write(shdr.get_packed_data())
|
||||
shdr_start += elf_header.e_shentsize
|
||||
|
||||
elf_out_fp.flush()
|
||||
|
||||
swiv_seg = SWIV_Seg()
|
||||
swiv_seg.crc = crc32_calculate(elf_out_fp, phdr_table)
|
||||
elf_out_fp.seek(swiv_shdr.sh_offset)
|
||||
elf_out_fp.write(swiv_seg.get_packed_data())
|
||||
|
||||
if temp_output is not None:
|
||||
os.replace(temp_output, output_elf)
|
||||
temp_output = None
|
||||
|
||||
return 0
|
||||
finally:
|
||||
if temp_output is not None and os.path.exists(temp_output):
|
||||
os.unlink(temp_output)
|
||||
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(description="Add a Software Image Validation (SWIV) section to an HTP ELF file.")
|
||||
parser.add_argument("-i", "--input", dest="input_file", metavar="INPUT", required=True)
|
||||
parser.add_argument("-o", "--output", dest="output_file", metavar="OUTPUT", required=True)
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="show calculated CRC")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO if args.verbose else logging.WARNING,
|
||||
format="%(message)s",
|
||||
)
|
||||
|
||||
try:
|
||||
return swiv_generate(args.input_file, args.output_file)
|
||||
except RuntimeError as exc:
|
||||
logger.error("ERROR: %s", exc)
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
@@ -0,0 +1,199 @@
|
||||
#ifndef HTP_TENSOR_H
|
||||
#define HTP_TENSOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "htp-ops.h"
|
||||
#include "hex-bitmap.h"
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
|
||||
#define HTP_TENSOR_MDEV_LINE_SIZE 128
|
||||
|
||||
// Currently HMX requires 32x32 tile formats (do not change)
|
||||
#define HTP_TENSOR_TILE_N_COLS 32
|
||||
#define HTP_TENSOR_TILE_N_ROWS 32
|
||||
|
||||
#define HTP_TENSOR_TILE_SIZE_F16 2048
|
||||
#define HTP_TENSOR_TILE_SIZE_Q4_0 576
|
||||
#define HTP_TENSOR_TILE_SIZE_Q4_1 640
|
||||
#define HTP_TENSOR_TILE_SIZE_Q8_0 1088
|
||||
#define HTP_TENSOR_TILE_SIZE_IQ4_NL 576
|
||||
#define HTP_TENSOR_TILE_SIZE_MXFP4 544
|
||||
#define HTP_TENSOR_TILE_SIZE_Q4_K 640
|
||||
#define HTP_TENSOR_TILE_SIZE_Q5_K 768
|
||||
#define HTP_TENSOR_TILE_SIZE_Q6_K 896
|
||||
#define HTP_TENSOR_TILE_SIZE_Q3_K 512
|
||||
#define HTP_TENSOR_TILE_SIZE_Q2_K 336
|
||||
#define HTP_TENSOR_TILE_K_Q1_0 128
|
||||
#define HTP_TENSOR_TILE_SIZE_Q1_0 576
|
||||
#define HTP_TENSOR_TILE_K_Q2_0 64
|
||||
#define HTP_TENSOR_TILE_SIZE_Q2_0 576
|
||||
|
||||
// Tiled weight tensor layouts (Q4_0, Q8_0, IQ4_NL, MXFP4, Q1_0, Q2_0, K-quants):
|
||||
// See docs/backend/snapdragon/developer.md ("Tiled Weight Formats").
|
||||
|
||||
struct htp_tensor_mdev_range {
|
||||
uint32_t start;
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
static inline bool htp_tensor_is_extended(const struct htp_tensor * t) {
|
||||
return t && (t->data >> 32) != 0;
|
||||
}
|
||||
|
||||
static inline bool htp_tensor_is_contiguous(const struct htp_tensor * t, uint32_t type_size) {
|
||||
uint32_t next_nb = type_size;
|
||||
if (t->ne[0] != 1 && t->nb[0] != next_nb) {
|
||||
return false;
|
||||
}
|
||||
next_nb *= t->ne[0];
|
||||
for (int i = 1; i < HTP_OP_MAX_DIMS; i++) {
|
||||
if (t->ne[i] != 1 && t->nb[i] != next_nb) {
|
||||
return false;
|
||||
}
|
||||
next_nb *= t->ne[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool htp_tensor_is_permuted(const struct htp_tensor * t) {
|
||||
return t->nb[0] > t->nb[1] || t->nb[1] > t->nb[2] || t->nb[2] > t->nb[3];
|
||||
}
|
||||
|
||||
static inline bool htp_tensor_mdev_data_aligned(const struct htp_tensor * t) {
|
||||
return ((uintptr_t) t->data & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) == 0;
|
||||
}
|
||||
|
||||
static inline bool htp_tensor_can_row_partition(const struct htp_tensor * t, uint32_t elem_size) {
|
||||
if (!htp_tensor_mdev_data_aligned(t)) {
|
||||
return false;
|
||||
}
|
||||
if (t->ne[0] != 1 && t->nb[0] != elem_size) {
|
||||
return false;
|
||||
}
|
||||
if (htp_tensor_is_permuted(t)) {
|
||||
return false;
|
||||
}
|
||||
if (t->ne[1] > 1 && (t->nb[1] & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) != 0) return false;
|
||||
if (t->ne[2] > 1 && (t->nb[2] & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) != 0) return false;
|
||||
if (t->ne[3] > 1 && (t->nb[3] & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool htp_tensor_mdev_rows_per_chunk(const struct htp_tensor * t, uint32_t elem_size, uint32_t row_size, uint32_t * rows_per_chunk) {
|
||||
*rows_per_chunk = 0;
|
||||
|
||||
if (!htp_tensor_mdev_data_aligned(t)) {
|
||||
return false;
|
||||
}
|
||||
if (t->ne[0] != 1 && t->nb[0] != elem_size) {
|
||||
return false;
|
||||
}
|
||||
if (htp_tensor_is_permuted(t)) {
|
||||
return false;
|
||||
}
|
||||
if (t->ne[1] > 1 && (t->nb[1] & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) == 0 &&
|
||||
(t->ne[2] <= 1 || (t->nb[2] & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) == 0) &&
|
||||
(t->ne[3] <= 1 || (t->nb[3] & (HTP_TENSOR_MDEV_LINE_SIZE - 1)) == 0)) {
|
||||
*rows_per_chunk = 1;
|
||||
return true;
|
||||
}
|
||||
if (t->nb[1] == row_size &&
|
||||
(t->ne[2] <= 1 || t->nb[2] == t->nb[1] * t->ne[1]) &&
|
||||
(t->ne[3] <= 1 || t->nb[3] == t->nb[2] * t->ne[2])) {
|
||||
*rows_per_chunk = (row_size > 0) ? (HTP_TENSOR_MDEV_LINE_SIZE / hex_gcd_u32(row_size, HTP_TENSOR_MDEV_LINE_SIZE)) : 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct htp_tensor_mdev_range htp_tensor_mdev_partition(uint32_t total_units, uint32_t units_per_chunk, uint32_t mdev_idx, uint32_t mdev_count, const struct fastdiv_values * mdev_count_div) {
|
||||
struct htp_tensor_mdev_range range = { 0, total_units };
|
||||
|
||||
if (mdev_count <= 1) {
|
||||
return range;
|
||||
}
|
||||
|
||||
if (units_per_chunk == 0) {
|
||||
range.start = (mdev_idx == 0) ? 0 : total_units;
|
||||
range.count = (mdev_idx == 0) ? total_units : 0;
|
||||
return range;
|
||||
}
|
||||
|
||||
const uint32_t total_chunks = total_units / units_per_chunk;
|
||||
if (total_chunks < mdev_count) {
|
||||
range.start = (mdev_idx == 0) ? 0 : total_units;
|
||||
range.count = (mdev_idx == 0) ? total_units : 0;
|
||||
return range;
|
||||
}
|
||||
|
||||
const uint32_t chunks_per_mdev = fastdiv(total_chunks + mdev_count - 1, mdev_count_div);
|
||||
range.start = MIN(mdev_idx * chunks_per_mdev * units_per_chunk, total_units);
|
||||
if (mdev_idx == mdev_count - 1) {
|
||||
range.count = total_units - range.start;
|
||||
} else {
|
||||
range.count = MIN(chunks_per_mdev * units_per_chunk, total_units - range.start);
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
static inline uint32_t htp_tensor_get_tile_size(int type) {
|
||||
switch (type) {
|
||||
case HTP_TYPE_Q4_0: return HTP_TENSOR_TILE_SIZE_Q4_0;
|
||||
case HTP_TYPE_IQ4_NL: return HTP_TENSOR_TILE_SIZE_IQ4_NL;
|
||||
case HTP_TYPE_Q4_1: return HTP_TENSOR_TILE_SIZE_Q4_1;
|
||||
case HTP_TYPE_Q4_K: return HTP_TENSOR_TILE_SIZE_Q4_K;
|
||||
case HTP_TYPE_Q8_0: return HTP_TENSOR_TILE_SIZE_Q8_0;
|
||||
case HTP_TYPE_Q5_K: return HTP_TENSOR_TILE_SIZE_Q5_K;
|
||||
case HTP_TYPE_Q6_K: return HTP_TENSOR_TILE_SIZE_Q6_K;
|
||||
case HTP_TYPE_Q3_K: return HTP_TENSOR_TILE_SIZE_Q3_K;
|
||||
case HTP_TYPE_Q2_K: return HTP_TENSOR_TILE_SIZE_Q2_K;
|
||||
case HTP_TYPE_Q1_0: return HTP_TENSOR_TILE_SIZE_Q1_0;
|
||||
case HTP_TYPE_Q2_0: return HTP_TENSOR_TILE_SIZE_Q2_0;
|
||||
case HTP_TYPE_MXFP4: return HTP_TENSOR_TILE_SIZE_MXFP4;
|
||||
case HTP_TYPE_F16: return HTP_TENSOR_TILE_SIZE_F16;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t htp_tensor_get_tile_k(int type) {
|
||||
switch (type) {
|
||||
case HTP_TYPE_Q1_0: return HTP_TENSOR_TILE_K_Q1_0;
|
||||
case HTP_TYPE_Q2_0: return HTP_TENSOR_TILE_K_Q2_0;
|
||||
default: return 32;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t htp_tensor_get_tile_padded_size(int type) {
|
||||
return (htp_tensor_get_tile_size(type) + 127u) & ~127u;
|
||||
}
|
||||
|
||||
static inline bool htp_tensor_is_tiled(const struct htp_tensor * t) {
|
||||
return t && (t->flags & HTP_TENSOR_TILED) != 0;
|
||||
}
|
||||
|
||||
static inline uint32_t htp_tensor_get_row_size(int type, uint32_t ne00) {
|
||||
switch (type) {
|
||||
case HTP_TYPE_F32: return ne00 * 4;
|
||||
case HTP_TYPE_F16:
|
||||
case HTP_TYPE_BF16: return ne00 * 2;
|
||||
case HTP_TYPE_Q8_0: return (ne00 / 32) * 34;
|
||||
case HTP_TYPE_Q4_0:
|
||||
case HTP_TYPE_IQ4_NL: return (ne00 / 32) * 18;
|
||||
case HTP_TYPE_Q4_1: return (ne00 / 32) * 20;
|
||||
case HTP_TYPE_MXFP4: return (ne00 / 32) * 17;
|
||||
case HTP_TYPE_Q2_K: return (ne00 / 256) * 84;
|
||||
case HTP_TYPE_Q2_0: return (ne00 / 64) * 18;
|
||||
case HTP_TYPE_Q3_K: return (ne00 / 256) * 110;
|
||||
case HTP_TYPE_Q4_K: return (ne00 / 256) * 144;
|
||||
case HTP_TYPE_Q5_K: return (ne00 / 256) * 176;
|
||||
case HTP_TYPE_Q6_K: return (ne00 / 256) * 210;
|
||||
case HTP_TYPE_Q1_0: return (ne00 / HTP_TENSOR_TILE_K_Q1_0) * 18;
|
||||
case HTP_TYPE_I32: return ne00 * 4;
|
||||
case HTP_TYPE_I64: return ne00 * 8;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HTP_TENSOR_H
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef HTP_VTCM_H
|
||||
#define HTP_VTCM_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static inline uint8_t *vtcm_seq_alloc(uint8_t **vtcm_ptr, size_t size) {
|
||||
uint8_t *p = *vtcm_ptr;
|
||||
*vtcm_ptr += size;
|
||||
return p;
|
||||
}
|
||||
|
||||
#define VTCM_LAYOUT_ALLOC(off, field, sz) do { (L)->field = (off); (off) += (sz); } while (0)
|
||||
#define VTCM_LAYOUT_ALLOC_OPTIONAL(off, field, sz, cond) do { if (cond) { VTCM_LAYOUT_ALLOC(off, field, sz); } else { (L)->field = 0; } } while (0)
|
||||
|
||||
#define VTCM_LAYOUT_PTR(type, base, offset) ((type *)((uint8_t *)(base) + (offset)))
|
||||
#define VTCM_LAYOUT_PTR_OPTIONAL(type, base, offset, cond) ((cond) ? VTCM_LAYOUT_PTR(type, base, offset) : NULL)
|
||||
|
||||
#endif // HTP_VTCM_H
|
||||
@@ -0,0 +1,23 @@
|
||||
// FastRPC IDL interface for GGML HTP
|
||||
|
||||
#ifndef HTP_IDL
|
||||
#define HTP_IDL
|
||||
|
||||
#include "AEEStdDef.idl"
|
||||
#include "remote.idl"
|
||||
|
||||
struct htp_iface_pmu_conf {
|
||||
uint32 events[8];
|
||||
};
|
||||
|
||||
interface htp_iface : remote_handle64 {
|
||||
AEEResult start(in uint32 sess_id, in uint64 dsp_queue_id, in uint32 n_hvx, in uint32 n_hmx, in uint64 max_vmem, in uint32 power_mode);
|
||||
AEEResult stop();
|
||||
AEEResult mmap(in uint32 fd, in uint64 size);
|
||||
AEEResult munmap(in uint32 fd);
|
||||
AEEResult profiler(in uint32 mode, in htp_iface_pmu_conf pmu);
|
||||
AEEResult etm(in uint32 enable);
|
||||
AEEResult hwinfo(rout uint32 n_threads, rout uint32 n_hvx, rout uint32 n_hmx, rout uint64 vtcm_size);
|
||||
};
|
||||
|
||||
#endif /* HTP_IDL */
|
||||
@@ -0,0 +1,953 @@
|
||||
#ifndef HVX_ARITH_H
|
||||
#define HVX_ARITH_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-floor.h"
|
||||
#include "hex-utils.h"
|
||||
|
||||
//
|
||||
// Binary operations (add, mul, sub)
|
||||
//
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
#define hvx_arith_loop_body(dst_type, src0_type, src1_type, elem_size, vec_store, vec_op) \
|
||||
do { \
|
||||
dst_type * vdst = (dst_type *) dst; \
|
||||
src0_type * vsrc0 = (src0_type *) src0; \
|
||||
src1_type * vsrc1 = (src1_type *) src1; \
|
||||
\
|
||||
const uint32_t epv = 128 / (elem_size); \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = vec_op(vsrc0[i], vsrc1[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = vec_op(vsrc0[i], vsrc1[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * (elem_size), v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#if __HVX_ARCH__ < 79
|
||||
|
||||
#define HVX_OP_ADD_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(a, b))
|
||||
#define HVX_OP_SUB_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(a, b))
|
||||
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(a, b))
|
||||
|
||||
#else
|
||||
|
||||
#define HVX_OP_ADD_F32(a, b) Q6_Vsf_vadd_VsfVsf(a, b)
|
||||
#define HVX_OP_SUB_F32(a, b) Q6_Vsf_vsub_VsfVsf(a, b)
|
||||
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_vmpy_VsfVsf(a, b)
|
||||
|
||||
#endif
|
||||
|
||||
#define HVX_OP_ADD_F16(a, b) hvx_vec_add_f16_f16(a, b)
|
||||
#define HVX_OP_SUB_F16(a, b) hvx_vec_sub_f16_f16(a, b)
|
||||
#define HVX_OP_MUL_F16(a, b) hvx_vec_mul_f16_f16(a, b)
|
||||
|
||||
// Generic macro to define alignment permutations for an op
|
||||
#define DEFINE_HVX_BINARY_OP_VARIANTS(OP_NAME, OP_MACRO, ELEM_TYPE) \
|
||||
static inline void OP_NAME##_aaa(uint8_t * dst, const uint8_t * src0, const uint8_t * src1, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
assert((uintptr_t) src0 % 128 == 0); \
|
||||
assert((uintptr_t) src1 % 128 == 0); \
|
||||
hvx_arith_loop_body(HVX_Vector, HVX_Vector, HVX_Vector, sizeof(ELEM_TYPE), hvx_vec_store_a, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_aau(uint8_t * dst, const uint8_t * src0, const uint8_t * src1, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
assert((uintptr_t) src0 % 128 == 0); \
|
||||
hvx_arith_loop_body(HVX_Vector, HVX_Vector, HVX_UVector, sizeof(ELEM_TYPE), hvx_vec_store_a, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_aua(uint8_t * dst, const uint8_t * src0, const uint8_t * src1, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
assert((uintptr_t) src1 % 128 == 0); \
|
||||
hvx_arith_loop_body(HVX_Vector, HVX_UVector, HVX_Vector, sizeof(ELEM_TYPE), hvx_vec_store_a, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_auu(uint8_t * dst, const uint8_t * src0, const uint8_t * src1, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
hvx_arith_loop_body(HVX_Vector, HVX_UVector, HVX_UVector, sizeof(ELEM_TYPE), hvx_vec_store_a, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_uaa(uint8_t * dst, const uint8_t * src0, const uint8_t * src1, uint32_t n) { \
|
||||
assert((uintptr_t) src0 % 128 == 0); \
|
||||
assert((uintptr_t) src1 % 128 == 0); \
|
||||
hvx_arith_loop_body(HVX_UVector, HVX_Vector, HVX_Vector, sizeof(ELEM_TYPE), hvx_vec_store_u, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_uau(uint8_t * dst, const uint8_t * src0, const uint8_t * src1, uint32_t n) { \
|
||||
assert((uintptr_t) src0 % 128 == 0); \
|
||||
hvx_arith_loop_body(HVX_UVector, HVX_Vector, HVX_UVector, sizeof(ELEM_TYPE), hvx_vec_store_u, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_uua(uint8_t * dst, const uint8_t * src0, const uint8_t * src1, uint32_t n) { \
|
||||
assert((uintptr_t) src1 % 128 == 0); \
|
||||
hvx_arith_loop_body(HVX_UVector, HVX_UVector, HVX_Vector, sizeof(ELEM_TYPE), hvx_vec_store_u, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_uuu(uint8_t * dst, const uint8_t * src0, const uint8_t * src1, uint32_t n) { \
|
||||
hvx_arith_loop_body(HVX_UVector, HVX_UVector, HVX_UVector, sizeof(ELEM_TYPE), hvx_vec_store_u, OP_MACRO); \
|
||||
} \
|
||||
|
||||
DEFINE_HVX_BINARY_OP_VARIANTS(hvx_add_f32, HVX_OP_ADD_F32, float)
|
||||
DEFINE_HVX_BINARY_OP_VARIANTS(hvx_sub_f32, HVX_OP_SUB_F32, float)
|
||||
DEFINE_HVX_BINARY_OP_VARIANTS(hvx_mul_f32, HVX_OP_MUL_F32, float)
|
||||
|
||||
DEFINE_HVX_BINARY_OP_VARIANTS(hvx_add_f16, HVX_OP_ADD_F16, _Float16)
|
||||
DEFINE_HVX_BINARY_OP_VARIANTS(hvx_sub_f16, HVX_OP_SUB_F16, _Float16)
|
||||
DEFINE_HVX_BINARY_OP_VARIANTS(hvx_mul_f16, HVX_OP_MUL_F16, _Float16)
|
||||
|
||||
// Dispatcher logic
|
||||
#define HVX_BINARY_DISPATCHER(OP_NAME) \
|
||||
static inline void OP_NAME(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, const uint32_t num_elems) { \
|
||||
if (hex_is_aligned((void *) dst, 128)) { \
|
||||
if (hex_is_aligned((void *) src0, 128)) { \
|
||||
if (hex_is_aligned((void *) src1, 128)) OP_NAME##_aaa(dst, src0, src1, num_elems); \
|
||||
else OP_NAME##_aau(dst, src0, src1, num_elems); \
|
||||
} else { \
|
||||
if (hex_is_aligned((void *) src1, 128)) OP_NAME##_aua(dst, src0, src1, num_elems); \
|
||||
else OP_NAME##_auu(dst, src0, src1, num_elems); \
|
||||
} \
|
||||
} else { \
|
||||
if (hex_is_aligned((void *) src0, 128)) { \
|
||||
if (hex_is_aligned((void *) src1, 128)) OP_NAME##_uaa(dst, src0, src1, num_elems); \
|
||||
else OP_NAME##_uau(dst, src0, src1, num_elems); \
|
||||
} else { \
|
||||
if (hex_is_aligned((void *) src1, 128)) OP_NAME##_uua(dst, src0, src1, num_elems); \
|
||||
else OP_NAME##_uuu(dst, src0, src1, num_elems); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
HVX_BINARY_DISPATCHER(hvx_add_f32)
|
||||
HVX_BINARY_DISPATCHER(hvx_sub_f32)
|
||||
HVX_BINARY_DISPATCHER(hvx_mul_f32)
|
||||
|
||||
HVX_BINARY_DISPATCHER(hvx_add_f16)
|
||||
HVX_BINARY_DISPATCHER(hvx_sub_f16)
|
||||
HVX_BINARY_DISPATCHER(hvx_mul_f16)
|
||||
|
||||
// Mul-Mul Optimized
|
||||
static inline void hvx_mul_mul_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, const uint8_t * restrict src2, const uint32_t num_elems) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src0 % 128 == 0);
|
||||
assert((unsigned long) src1 % 128 == 0);
|
||||
assert((unsigned long) src2 % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc0 = (HVX_Vector *) src0;
|
||||
HVX_Vector * restrict vsrc1 = (HVX_Vector *) src1;
|
||||
HVX_Vector * restrict vsrc2 = (HVX_Vector *) src2;
|
||||
|
||||
const uint32_t elem_size = sizeof(float);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = num_elems / epv;
|
||||
const uint32_t nloe = num_elems % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
HVX_Vector v1 = HVX_OP_MUL_F32(vsrc0[i], vsrc1[i]);
|
||||
vdst[i] = HVX_OP_MUL(v1, vsrc2[i]);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_Vector v1 = HVX_OP_MUL_F32(vsrc0[i], vsrc1[i]);
|
||||
HVX_Vector v2 = HVX_OP_MUL_F32(v1, vsrc2[i]);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v2);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_mul_mul_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, const uint8_t * restrict src2, const uint32_t num_elems) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src0 % 128 == 0);
|
||||
assert((unsigned long) src1 % 128 == 0);
|
||||
assert((unsigned long) src2 % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc0 = (HVX_Vector *) src0;
|
||||
HVX_Vector * restrict vsrc1 = (HVX_Vector *) src1;
|
||||
HVX_Vector * restrict vsrc2 = (HVX_Vector *) src2;
|
||||
|
||||
const uint32_t elem_size = sizeof(_Float16);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = num_elems / epv;
|
||||
const uint32_t nloe = num_elems % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
HVX_Vector v1 = HVX_OP_MUL_F16(vsrc0[i], vsrc1[i]);
|
||||
vdst[i] = HVX_OP_MUL_F16(v1, vsrc2[i]);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_Vector v1 = HVX_OP_MUL_F16(vsrc0[i], vsrc1[i]);
|
||||
HVX_Vector v2 = HVX_OP_MUL_F16(v1, vsrc2[i]);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v2);
|
||||
}
|
||||
}
|
||||
|
||||
// Scalar Operations
|
||||
|
||||
#define hvx_scalar_loop_body(dst_type, src_type, elem_size, vec_store, scalar_op_macro) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t epv = 128 / (elem_size); \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
HVX_Vector v = vsrc[i]; \
|
||||
vdst[i] = scalar_op_macro(v); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = vsrc[i]; \
|
||||
v = scalar_op_macro(v); \
|
||||
vec_store((void *) &vdst[i], nloe * (elem_size), v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define HVX_OP_ADD_SCALAR_F32(v) \
|
||||
({ \
|
||||
const HVX_VectorPred pred_inf = Q6_Q_vcmp_eq_VwVw(inf, v); \
|
||||
HVX_Vector out = HVX_OP_ADD_F32(v, val_vec); \
|
||||
Q6_V_vmux_QVV(pred_inf, inf, out); \
|
||||
})
|
||||
|
||||
#define HVX_OP_MUL_SCALAR_F32(v) HVX_OP_MUL_F32(v, val_vec)
|
||||
#define HVX_OP_SUB_SCALAR_F32(v) HVX_OP_SUB_F32(v, val_vec)
|
||||
|
||||
#define HVX_OP_ADD_SCALAR_F16(v) \
|
||||
({ \
|
||||
const HVX_VectorPred pred_inf = Q6_Q_vcmp_eq_VhVh(inf, v); \
|
||||
HVX_Vector out = HVX_OP_ADD_F16(v, val_vec); \
|
||||
Q6_V_vmux_QVV(pred_inf, inf, out); \
|
||||
})
|
||||
|
||||
#define HVX_OP_MUL_SCALAR_F16(v) HVX_OP_MUL_F16(v, val_vec)
|
||||
#define HVX_OP_SUB_SCALAR_F16(v) HVX_OP_SUB_F16(v, val_vec)
|
||||
|
||||
// Scalar Variants
|
||||
|
||||
// Generic macro to define alignment permutations for an op
|
||||
#define DEFINE_HVX_BINARY_SCALAR_OP_VARIANTS(OP_NAME, OP_MACRO, SPLAT_MACRO, ELEM_TYPE) \
|
||||
static inline void OP_NAME##_aa(uint8_t * restrict dst, const uint8_t * restrict src, const ELEM_TYPE val, uint32_t n) { \
|
||||
const HVX_Vector val_vec = SPLAT_MACRO(val); \
|
||||
const HVX_Vector inf = SPLAT_MACRO((ELEM_TYPE)INFINITY); UNUSED(inf); \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
assert((uintptr_t) src % 128 == 0); \
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_Vector, sizeof(ELEM_TYPE), hvx_vec_store_a, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_au(uint8_t * restrict dst, const uint8_t * restrict src, const ELEM_TYPE val, uint32_t n) { \
|
||||
const HVX_Vector val_vec = SPLAT_MACRO(val); \
|
||||
const HVX_Vector inf = SPLAT_MACRO((ELEM_TYPE)INFINITY); UNUSED(inf); \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_UVector, sizeof(ELEM_TYPE), hvx_vec_store_a, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_ua(uint8_t * restrict dst, const uint8_t * restrict src, const ELEM_TYPE val, uint32_t n) { \
|
||||
const HVX_Vector val_vec = SPLAT_MACRO(val); \
|
||||
const HVX_Vector inf = SPLAT_MACRO((ELEM_TYPE)INFINITY); UNUSED(inf); \
|
||||
assert((uintptr_t) src % 128 == 0); \
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_Vector, sizeof(ELEM_TYPE), hvx_vec_store_u, OP_MACRO); \
|
||||
} \
|
||||
static inline void OP_NAME##_uu(uint8_t * restrict dst, const uint8_t * restrict src, const ELEM_TYPE val, uint32_t n) { \
|
||||
const HVX_Vector val_vec = SPLAT_MACRO(val); \
|
||||
const HVX_Vector inf = SPLAT_MACRO((ELEM_TYPE)INFINITY); UNUSED(inf); \
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_UVector, sizeof(ELEM_TYPE), hvx_vec_store_u, OP_MACRO); \
|
||||
} \
|
||||
|
||||
DEFINE_HVX_BINARY_SCALAR_OP_VARIANTS(hvx_add_scalar_f32, HVX_OP_ADD_SCALAR_F32, hvx_vec_splat_f32, float)
|
||||
DEFINE_HVX_BINARY_SCALAR_OP_VARIANTS(hvx_sub_scalar_f32, HVX_OP_SUB_SCALAR_F32, hvx_vec_splat_f32, float)
|
||||
DEFINE_HVX_BINARY_SCALAR_OP_VARIANTS(hvx_mul_scalar_f32, HVX_OP_MUL_SCALAR_F32, hvx_vec_splat_f32, float)
|
||||
|
||||
DEFINE_HVX_BINARY_SCALAR_OP_VARIANTS(hvx_add_scalar_f16, HVX_OP_ADD_SCALAR_F16, hvx_vec_splat_f16, _Float16)
|
||||
DEFINE_HVX_BINARY_SCALAR_OP_VARIANTS(hvx_sub_scalar_f16, HVX_OP_SUB_SCALAR_F16, hvx_vec_splat_f16, _Float16)
|
||||
DEFINE_HVX_BINARY_SCALAR_OP_VARIANTS(hvx_mul_scalar_f16, HVX_OP_MUL_SCALAR_F16, hvx_vec_splat_f16, _Float16)
|
||||
|
||||
// Dispatcher logic
|
||||
#define HVX_BINARY_SCALAR_DISPATCHER(OP_NAME, ELEM_TYPE) \
|
||||
static inline void OP_NAME(uint8_t * restrict dst, const uint8_t * restrict src, const ELEM_TYPE val, const uint32_t num_elems) { \
|
||||
if (hex_is_aligned((void *) dst, 128) && hex_is_aligned((void *) src, 128)) { \
|
||||
OP_NAME##_aa(dst, src, val, num_elems); \
|
||||
} else if (hex_is_aligned((void *) dst, 128)) { \
|
||||
OP_NAME##_au(dst, src, val, num_elems); \
|
||||
} else if (hex_is_aligned((void *) src, 128)) { \
|
||||
OP_NAME##_ua(dst, src, val, num_elems); \
|
||||
} else { \
|
||||
OP_NAME##_uu(dst, src, val, num_elems); \
|
||||
} \
|
||||
}
|
||||
|
||||
HVX_BINARY_SCALAR_DISPATCHER(hvx_add_scalar_f32, float)
|
||||
HVX_BINARY_SCALAR_DISPATCHER(hvx_sub_scalar_f32, float)
|
||||
HVX_BINARY_SCALAR_DISPATCHER(hvx_mul_scalar_f32, float)
|
||||
|
||||
HVX_BINARY_SCALAR_DISPATCHER(hvx_add_scalar_f16, _Float16)
|
||||
HVX_BINARY_SCALAR_DISPATCHER(hvx_sub_scalar_f16, _Float16)
|
||||
HVX_BINARY_SCALAR_DISPATCHER(hvx_mul_scalar_f16, _Float16)
|
||||
|
||||
// MIN Scalar variants
|
||||
|
||||
#define HVX_OP_MIN_SCALAR(v) Q6_Vsf_vmin_VsfVsf(val_vec, v)
|
||||
|
||||
static inline void hvx_min_scalar_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, const float val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f32(val);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_Vector, sizeof(float), hvx_vec_store_a, HVX_OP_MIN_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_min_scalar_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, const float val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f32(val);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_UVector, sizeof(float), hvx_vec_store_a, HVX_OP_MIN_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_min_scalar_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, const float val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f32(val);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_Vector, sizeof(float), hvx_vec_store_u, HVX_OP_MIN_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_min_scalar_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, const float val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f32(val);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_UVector, sizeof(float), hvx_vec_store_u, HVX_OP_MIN_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_min_scalar_f32(uint8_t * restrict dst, const uint8_t * restrict src, const float val, const int num_elems) {
|
||||
if (hex_is_aligned((void *) dst, 128) && hex_is_aligned((void *) src, 128)) {
|
||||
hvx_min_scalar_f32_aa(dst, src, val, num_elems);
|
||||
} else if (hex_is_aligned((void *) dst, 128)) {
|
||||
hvx_min_scalar_f32_au(dst, src, val, num_elems);
|
||||
} else if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_min_scalar_f32_ua(dst, src, val, num_elems);
|
||||
} else {
|
||||
hvx_min_scalar_f32_uu(dst, src, val, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
// MAX Scalar variants
|
||||
|
||||
#define HVX_OP_MAX_SCALAR(v) Q6_Vsf_vmax_VsfVsf(val_vec, v)
|
||||
|
||||
static inline void hvx_max_scalar_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, const float val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f32(val);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_Vector, sizeof(float), hvx_vec_store_a, HVX_OP_MAX_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_max_scalar_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, const float val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f32(val);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_UVector, sizeof(float), hvx_vec_store_a, HVX_OP_MAX_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_max_scalar_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, const float val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f32(val);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_Vector, sizeof(float), hvx_vec_store_u, HVX_OP_MAX_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_max_scalar_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, const float val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f32(val);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_UVector, sizeof(float), hvx_vec_store_u, HVX_OP_MAX_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_max_scalar_f32(uint8_t * restrict dst, const uint8_t * restrict src, const float val, const int num_elems) {
|
||||
if (hex_is_aligned((void *) dst, 128) && hex_is_aligned((void *) src, 128)) {
|
||||
hvx_max_scalar_f32_aa(dst, src, val, num_elems);
|
||||
} else if (hex_is_aligned((void *) dst, 128)) {
|
||||
hvx_max_scalar_f32_au(dst, src, val, num_elems);
|
||||
} else if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_max_scalar_f32_ua(dst, src, val, num_elems);
|
||||
} else {
|
||||
hvx_max_scalar_f32_uu(dst, src, val, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
#define HVX_OP_MIN_SCALAR_F16(v) Q6_Vhf_vmin_VhfVhf(val_vec, v)
|
||||
|
||||
static inline void hvx_min_scalar_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f16(val);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_Vector, sizeof(_Float16), hvx_vec_store_a, HVX_OP_MIN_SCALAR_F16);
|
||||
}
|
||||
|
||||
static inline void hvx_min_scalar_f16_au(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f16(val);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_UVector, sizeof(_Float16), hvx_vec_store_a, HVX_OP_MIN_SCALAR_F16);
|
||||
}
|
||||
|
||||
static inline void hvx_min_scalar_f16_ua(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f16(val);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_Vector, sizeof(_Float16), hvx_vec_store_u, HVX_OP_MIN_SCALAR_F16);
|
||||
}
|
||||
|
||||
static inline void hvx_min_scalar_f16_uu(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 val, uint32_t n) {
|
||||
const HVX_Vector val_vec = hvx_vec_splat_f16(val);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_UVector, sizeof(_Float16), hvx_vec_store_u, HVX_OP_MIN_SCALAR_F16);
|
||||
}
|
||||
|
||||
static inline void hvx_min_scalar_f16(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 val, const int num_elems) {
|
||||
if (hex_is_aligned((void *) dst, 128) && hex_is_aligned((void *) src, 128)) {
|
||||
hvx_min_scalar_f16_aa(dst, src, val, num_elems);
|
||||
} else if (hex_is_aligned((void *) dst, 128)) {
|
||||
hvx_min_scalar_f16_au(dst, src, val, num_elems);
|
||||
} else if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_min_scalar_f16_ua(dst, src, val, num_elems);
|
||||
} else {
|
||||
hvx_min_scalar_f16_uu(dst, src, val, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
// CLAMP Scalar variants
|
||||
|
||||
#define HVX_OP_CLAMP_SCALAR(v) \
|
||||
({ \
|
||||
HVX_VectorPred pred_cap_right = Q6_Q_vcmp_gt_VsfVsf(v, max_vec); \
|
||||
HVX_VectorPred pred_cap_left = Q6_Q_vcmp_gt_VsfVsf(min_vec, v); \
|
||||
HVX_Vector tmp = Q6_V_vmux_QVV(pred_cap_right, max_vec, v); \
|
||||
Q6_V_vmux_QVV(pred_cap_left, min_vec, tmp); \
|
||||
})
|
||||
|
||||
static inline void hvx_clamp_scalar_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, const float min, const float max, uint32_t n) {
|
||||
const HVX_Vector min_vec = hvx_vec_splat_f32(min);
|
||||
const HVX_Vector max_vec = hvx_vec_splat_f32(max);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_Vector, sizeof(float), hvx_vec_store_a, HVX_OP_CLAMP_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_clamp_scalar_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, const float min, const float max, uint32_t n) {
|
||||
const HVX_Vector min_vec = hvx_vec_splat_f32(min);
|
||||
const HVX_Vector max_vec = hvx_vec_splat_f32(max);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_UVector, sizeof(float), hvx_vec_store_a, HVX_OP_CLAMP_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_clamp_scalar_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, const float min, const float max, uint32_t n) {
|
||||
const HVX_Vector min_vec = hvx_vec_splat_f32(min);
|
||||
const HVX_Vector max_vec = hvx_vec_splat_f32(max);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_Vector, sizeof(float), hvx_vec_store_u, HVX_OP_CLAMP_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_clamp_scalar_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, const float min, const float max, uint32_t n) {
|
||||
const HVX_Vector min_vec = hvx_vec_splat_f32(min);
|
||||
const HVX_Vector max_vec = hvx_vec_splat_f32(max);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_UVector, sizeof(float), hvx_vec_store_u, HVX_OP_CLAMP_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_clamp_scalar_f32(uint8_t * restrict dst, const uint8_t * restrict src, const float min, const float max, const int num_elems) {
|
||||
if (hex_is_aligned((void *) dst, 128) && hex_is_aligned((void *) src, 128)) {
|
||||
hvx_clamp_scalar_f32_aa(dst, src, min, max, num_elems);
|
||||
} else if (hex_is_aligned((void *) dst, 128)) {
|
||||
hvx_clamp_scalar_f32_au(dst, src, min, max, num_elems);
|
||||
} else if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_clamp_scalar_f32_ua(dst, src, min, max, num_elems);
|
||||
} else {
|
||||
hvx_clamp_scalar_f32_uu(dst, src, min, max, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
#define HVX_OP_CLAMP_SCALAR_F16(v) \
|
||||
({ \
|
||||
HVX_VectorPred pred_cap_right = Q6_Q_vcmp_gt_VhfVhf(v, max_vec); \
|
||||
HVX_VectorPred pred_cap_left = Q6_Q_vcmp_gt_VhfVhf(min_vec, v); \
|
||||
HVX_Vector tmp = Q6_V_vmux_QVV(pred_cap_right, max_vec, v); \
|
||||
Q6_V_vmux_QVV(pred_cap_left, min_vec, tmp); \
|
||||
})
|
||||
|
||||
static inline void hvx_clamp_scalar_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 min, const _Float16 max, uint32_t n) {
|
||||
const HVX_Vector min_vec = hvx_vec_splat_f16(min);
|
||||
const HVX_Vector max_vec = hvx_vec_splat_f16(max);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_Vector, sizeof(_Float16), hvx_vec_store_a, HVX_OP_CLAMP_SCALAR_F16);
|
||||
}
|
||||
|
||||
static inline void hvx_clamp_scalar_f16_au(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 min, const _Float16 max, uint32_t n) {
|
||||
const HVX_Vector min_vec = hvx_vec_splat_f16(min);
|
||||
const HVX_Vector max_vec = hvx_vec_splat_f16(max);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_UVector, sizeof(_Float16), hvx_vec_store_a, HVX_OP_CLAMP_SCALAR_F16);
|
||||
}
|
||||
|
||||
static inline void hvx_clamp_scalar_f16_ua(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 min, const _Float16 max, uint32_t n) {
|
||||
const HVX_Vector min_vec = hvx_vec_splat_f16(min);
|
||||
const HVX_Vector max_vec = hvx_vec_splat_f16(max);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_Vector, sizeof(_Float16), hvx_vec_store_u, HVX_OP_CLAMP_SCALAR_F16);
|
||||
}
|
||||
|
||||
static inline void hvx_clamp_scalar_f16_uu(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 min, const _Float16 max, uint32_t n) {
|
||||
const HVX_Vector min_vec = hvx_vec_splat_f16(min);
|
||||
const HVX_Vector max_vec = hvx_vec_splat_f16(max);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_UVector, sizeof(_Float16), hvx_vec_store_u, HVX_OP_CLAMP_SCALAR_F16);
|
||||
}
|
||||
|
||||
static inline void hvx_clamp_scalar_f16(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 min, const _Float16 max, const int num_elems) {
|
||||
if (hex_is_aligned((void *) dst, 128) && hex_is_aligned((void *) src, 128)) {
|
||||
hvx_clamp_scalar_f16_aa(dst, src, min, max, num_elems);
|
||||
} else if (hex_is_aligned((void *) dst, 128)) {
|
||||
hvx_clamp_scalar_f16_au(dst, src, min, max, num_elems);
|
||||
} else if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_clamp_scalar_f16_ua(dst, src, min, max, num_elems);
|
||||
} else {
|
||||
hvx_clamp_scalar_f16_uu(dst, src, min, max, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
#define HVX_OP_LEAKY_RELU_SCALAR(v) \
|
||||
({ \
|
||||
HVX_VectorPred pred_neg = Q6_Q_vcmp_gt_VsfVsf(zero_vec, v); \
|
||||
HVX_Vector scaled = HVX_OP_MUL_F32(v, ns_vec); \
|
||||
Q6_V_vmux_QVV(pred_neg, scaled, v); \
|
||||
})
|
||||
|
||||
static inline void hvx_leaky_relu_scalar_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, const float ns, uint32_t n) {
|
||||
const HVX_Vector zero_vec = hvx_vec_splat_f32(0.0f);
|
||||
const HVX_Vector ns_vec = hvx_vec_splat_f32(ns);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_Vector, sizeof(float), hvx_vec_store_a, HVX_OP_LEAKY_RELU_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_leaky_relu_scalar_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, const float ns, uint32_t n) {
|
||||
const HVX_Vector zero_vec = hvx_vec_splat_f32(0.0f);
|
||||
const HVX_Vector ns_vec = hvx_vec_splat_f32(ns);
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_Vector, HVX_UVector, sizeof(float), hvx_vec_store_a, HVX_OP_LEAKY_RELU_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_leaky_relu_scalar_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, const float ns, uint32_t n) {
|
||||
const HVX_Vector zero_vec = hvx_vec_splat_f32(0.0f);
|
||||
const HVX_Vector ns_vec = hvx_vec_splat_f32(ns);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_Vector, sizeof(float), hvx_vec_store_u, HVX_OP_LEAKY_RELU_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_leaky_relu_scalar_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, const float ns, uint32_t n) {
|
||||
const HVX_Vector zero_vec = hvx_vec_splat_f32(0.0f);
|
||||
const HVX_Vector ns_vec = hvx_vec_splat_f32(ns);
|
||||
hvx_scalar_loop_body(HVX_UVector, HVX_UVector, sizeof(float), hvx_vec_store_u, HVX_OP_LEAKY_RELU_SCALAR);
|
||||
}
|
||||
|
||||
static inline void hvx_leaky_relu_scalar_f32(uint8_t * restrict dst, const uint8_t * restrict src, const float ns, const int num_elems) {
|
||||
if (hex_is_aligned((void *) dst, 128) && hex_is_aligned((void *) src, 128)) {
|
||||
hvx_leaky_relu_scalar_f32_aa(dst, src, ns, num_elems);
|
||||
} else if (hex_is_aligned((void *) dst, 128)) {
|
||||
hvx_leaky_relu_scalar_f32_au(dst, src, ns, num_elems);
|
||||
} else if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_leaky_relu_scalar_f32_ua(dst, src, ns, num_elems);
|
||||
} else {
|
||||
hvx_leaky_relu_scalar_f32_uu(dst, src, ns, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Abs
|
||||
//
|
||||
|
||||
static inline void hvx_abs_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t elem_size = sizeof(float);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = n / epv;
|
||||
const uint32_t nloe = n % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
vdst[i] = hvx_vec_abs_f32(vsrc[i]);
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_Vector v = hvx_vec_abs_f32(vsrc[i]);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v);
|
||||
}
|
||||
}
|
||||
|
||||
#define hvx_abs_f16_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(_Float16); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = hvx_vec_abs_f16(vsrc[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = hvx_vec_abs_f16(vsrc[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_abs_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_abs_f16_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_abs_f16_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_abs_f16_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_abs_f16_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_abs_f16_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_abs_f16_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_abs_f16_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_abs_f16(uint8_t * restrict dst, const uint8_t * restrict src, const uint32_t num_elems) {
|
||||
if (hex_is_aligned((void *) dst, 128)) {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_abs_f16_aa(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_abs_f16_au(dst, src, num_elems);
|
||||
}
|
||||
} else {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_abs_f16_ua(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_abs_f16_uu(dst, src, num_elems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Step
|
||||
//
|
||||
|
||||
static inline void hvx_step_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t elem_size = sizeof(float);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = n / epv;
|
||||
const uint32_t nloe = n % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
vdst[i] = hvx_vec_step_f32(vsrc[i]);
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_Vector v = hvx_vec_step_f32(vsrc[i]);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_sgn_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t elem_size = sizeof(float);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = n / epv;
|
||||
const uint32_t nloe = n % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
vdst[i] = hvx_vec_sgn_f32(vsrc[i]);
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_Vector v = hvx_vec_sgn_f32(vsrc[i]);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_sgn_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t elem_size = sizeof(_Float16);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = n / epv;
|
||||
const uint32_t nloe = n % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]);
|
||||
vdst[i] = hvx_vec_f32_to_f16(hvx_vec_sgn_f32(Q6_V_lo_W(p)), hvx_vec_sgn_f32(Q6_V_hi_W(p)));
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]);
|
||||
HVX_Vector v = hvx_vec_f32_to_f16(hvx_vec_sgn_f32(Q6_V_lo_W(p)), hvx_vec_sgn_f32(Q6_V_hi_W(p)));
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_step_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t elem_size = sizeof(_Float16);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = n / epv;
|
||||
const uint32_t nloe = n % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
vdst[i] = hvx_vec_step_f16(vsrc[i]);
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_Vector v = hvx_vec_step_f16(vsrc[i]);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Ceil
|
||||
//
|
||||
|
||||
static inline void hvx_ceil_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t elem_size = sizeof(float);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = n / epv;
|
||||
const uint32_t nloe = n % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
vdst[i] = hvx_vec_ceil_f32(vsrc[i]);
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_Vector v = hvx_vec_ceil_f32(vsrc[i]);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_ceil_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t elem_size = sizeof(_Float16);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = n / epv;
|
||||
const uint32_t nloe = n % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]);
|
||||
vdst[i] = hvx_vec_f32_to_f16(hvx_vec_ceil_f32(Q6_V_lo_W(p)), hvx_vec_ceil_f32(Q6_V_hi_W(p)));
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]);
|
||||
HVX_Vector v = hvx_vec_f32_to_f16(hvx_vec_ceil_f32(Q6_V_lo_W(p)), hvx_vec_ceil_f32(Q6_V_hi_W(p)));
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Square
|
||||
//
|
||||
|
||||
#define hvx_sqr_f32_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(float); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = HVX_OP_MUL_F32(vsrc[i], vsrc[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = HVX_OP_MUL_F32(vsrc[i], vsrc[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_sqr_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sqr_f32_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sqr_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_sqr_f32_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sqr_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sqr_f32_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_sqr_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_sqr_f32_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_sqr_f32(uint8_t * restrict dst, const uint8_t * restrict src, const uint32_t num_elems) {
|
||||
if (hex_is_aligned((void *) dst, 128)) {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_sqr_f32_aa(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_sqr_f32_au(dst, src, num_elems);
|
||||
}
|
||||
} else {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_sqr_f32_ua(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_sqr_f32_uu(dst, src, num_elems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define hvx_sqr_f16_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(_Float16); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = HVX_OP_MUL_F16(vsrc[i], vsrc[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = HVX_OP_MUL_F16(vsrc[i], vsrc[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_sqr_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sqr_f16_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sqr_f16_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_sqr_f16_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sqr_f16_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sqr_f16_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_sqr_f16_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_sqr_f16_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_sqr_f16(uint8_t * restrict dst, const uint8_t * restrict src, const uint32_t num_elems) {
|
||||
if (hex_is_aligned((void *) dst, 128)) {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_sqr_f16_aa(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_sqr_f16_au(dst, src, num_elems);
|
||||
}
|
||||
} else {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
hvx_sqr_f16_ua(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_sqr_f16_uu(dst, src, num_elems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef HVX_OP_ADD_F32
|
||||
#undef HVX_OP_SUB_F32
|
||||
#undef HVX_OP_MUL_F32
|
||||
#undef HVX_OP_ADD_F16
|
||||
#undef HVX_OP_SUB_F16
|
||||
#undef HVX_OP_MUL_F16
|
||||
#undef hvx_arith_loop_body
|
||||
#undef HVX_OP_ADD_SCALAR_F32
|
||||
#undef HVX_OP_SUB_SCALAR_F32
|
||||
#undef HVX_OP_MUL_SCALAR_F32
|
||||
#undef HVX_OP_ADD_SCALAR_F16
|
||||
#undef HVX_OP_SUB_SCALAR_F16
|
||||
#undef HVX_OP_MUL_SCALAR_F16
|
||||
#undef hvx_scalar_loop_body
|
||||
#undef HVX_OP_MIN_SCALAR
|
||||
#undef HVX_OP_MAX_SCALAR
|
||||
#undef HVX_OP_MIN_SCALAR_F16
|
||||
#undef HVX_OP_CLAMP_SCALAR
|
||||
#undef HVX_OP_CLAMP_SCALAR_F16
|
||||
#undef HVX_OP_LEAKY_RELU_SCALAR
|
||||
#undef DEFINE_HVX_BINARY_OP_VARIANTS
|
||||
#undef DEFINE_HVX_BINARY_SCALAR_OP_VARIANTS
|
||||
#undef HVX_BINARY_DISPATCHER
|
||||
#undef UNUSED
|
||||
|
||||
#endif // HVX_ARITH_H
|
||||
@@ -0,0 +1,450 @@
|
||||
#ifndef HVX_BASE_H
|
||||
#define HVX_BASE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "hex-utils.h"
|
||||
#include "hvx-types.h"
|
||||
|
||||
#define hvx_vmem(A) *((HVX_Vector *)(A))
|
||||
#define hvx_vmemu(A) *((HVX_UVector *)(A))
|
||||
|
||||
static inline void hvx_vec_store_u(void * restrict dst, uint32_t n, HVX_Vector v) {
|
||||
// Rotate as needed.
|
||||
v = Q6_V_vlalign_VVR(v, v, (size_t) dst);
|
||||
|
||||
uint32_t left_off = (size_t) dst & 127;
|
||||
uint32_t right_off = left_off + n;
|
||||
|
||||
HVX_VectorPred ql_not = Q6_Q_vsetq_R((size_t) dst);
|
||||
HVX_VectorPred qr = Q6_Q_vsetq2_R(right_off);
|
||||
|
||||
if (right_off > 128) {
|
||||
Q6_vmem_QRIV(qr, (HVX_Vector *) dst + 1, v);
|
||||
// all 1's
|
||||
qr = Q6_Q_vcmp_eq_VbVb(v, v);
|
||||
}
|
||||
|
||||
ql_not = Q6_Q_or_QQn(ql_not, qr);
|
||||
Q6_vmem_QnRIV(ql_not, (HVX_Vector *) dst, v);
|
||||
}
|
||||
|
||||
static inline void hvx_vec_store_a(void * restrict dst, uint32_t n, HVX_Vector v) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
HVX_VectorPred m = Q6_Q_or_QQn(Q6_Q_vsetq_R((unsigned long) dst), Q6_Q_vsetq2_R(n));
|
||||
Q6_vmem_QnRIV(m, (HVX_Vector *) dst, v);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_splat_f32(float v) {
|
||||
union { float f; uint32_t i; } u = { .f = v };
|
||||
return Q6_V_vsplat_R(u.i);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_splat_f16(_Float16 v) {
|
||||
union { __fp16 f; uint16_t i; } u = { .f = v };
|
||||
return Q6_Vh_vsplat_R(u.i);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_repl4(HVX_Vector v) {
|
||||
// vdelta control to replicate first 4 bytes across all elements
|
||||
static const uint8_t __attribute__((aligned(128))) repl[128] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x40, 0x40, 0x40, 0x40, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
};
|
||||
|
||||
HVX_Vector ctrl = *(HVX_Vector *) repl;
|
||||
return Q6_V_vdelta_VV(v, ctrl);
|
||||
}
|
||||
|
||||
static inline float hvx_vec_get_f32(HVX_Vector v) {
|
||||
float __attribute__((aligned(128))) x;
|
||||
hvx_vec_store_a(&x, 4, v);
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline int32_t hvx_vec_get_i32(HVX_Vector v) {
|
||||
int32_t __attribute__((aligned(128))) x;
|
||||
hvx_vec_store_a(&x, 4, v);
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline _Float16 hvx_vec_get_f16(HVX_Vector v) {
|
||||
_Float16 __attribute__((aligned(128))) x;
|
||||
hvx_vec_store_a(&x, 2, v);
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_abs_f16(HVX_Vector v) {
|
||||
// abs by clearing the fp16 sign bit
|
||||
HVX_Vector mask = Q6_Vh_vsplat_R(0x7fff);
|
||||
return Q6_V_vand_VV(v, mask);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_neg_f16(HVX_Vector v) {
|
||||
// neg by setting the fp16 sign bit
|
||||
HVX_Vector mask = Q6_Vh_vsplat_R(0x8000);
|
||||
return Q6_V_vxor_VV(v, mask);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_abs_f32(HVX_Vector v) {
|
||||
// abs by clearing the fp32 sign bit
|
||||
HVX_Vector mask = Q6_V_vsplat_R(0x7fffffff);
|
||||
return Q6_V_vand_VV(v, mask);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_neg_f32(HVX_Vector v) {
|
||||
#if __HVX_ARCH__ > 75
|
||||
return Q6_Vsf_vfneg_Vsf(v);
|
||||
#else
|
||||
// neg by setting the fp32 sign bit
|
||||
HVX_Vector mask = Q6_V_vsplat_R(0x80000000);
|
||||
return Q6_V_vxor_VV(v, mask);
|
||||
#endif // __HVX_ARCH__ > 75
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_step_f32(HVX_Vector v) {
|
||||
const HVX_Vector zero = Q6_V_vzero();
|
||||
const HVX_Vector one = hvx_vec_splat_f32(1.0f);
|
||||
HVX_VectorPred q = Q6_Q_vcmp_gt_VsfVsf(v, zero);
|
||||
return Q6_V_vmux_QVV(q, one, zero);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_sgn_f32(HVX_Vector v) {
|
||||
const HVX_Vector zero = Q6_V_vzero();
|
||||
const HVX_Vector one = hvx_vec_splat_f32(1.0f);
|
||||
const HVX_Vector sign_mask = Q6_V_vsplat_R(0x80000000);
|
||||
const HVX_Vector mag = hvx_vec_abs_f32(v);
|
||||
const HVX_Vector signed_one = Q6_V_vor_VV(one, Q6_V_vand_VV(v, sign_mask));
|
||||
// The ordered magnitude comparison maps NaN and both signed zeros to zero.
|
||||
const HVX_VectorPred nonzero = Q6_Q_vcmp_gt_VsfVsf(mag, zero);
|
||||
return Q6_V_vmux_QVV(nonzero, signed_one, zero);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_step_f16(HVX_Vector v) {
|
||||
const HVX_Vector zero = Q6_V_vzero();
|
||||
const HVX_Vector one = hvx_vec_splat_f16((_Float16) 1.0f);
|
||||
HVX_VectorPred q = Q6_Q_vcmp_gt_VhfVhf(v, zero);
|
||||
return Q6_V_vmux_QVV(q, one, zero);
|
||||
}
|
||||
|
||||
static inline HVX_VectorPred hvx_vec_is_nan_f16(HVX_Vector v) {
|
||||
const HVX_Vector vnan_exp = Q6_Vh_vsplat_R(0x7C00);
|
||||
const HVX_Vector vnan_frac = Q6_Vh_vsplat_R(0x7FFF);
|
||||
|
||||
// get pred of which are NaN, i.e., exponent bits all 1s and fraction bits non 0s
|
||||
HVX_VectorPred p_exp = Q6_Q_vcmp_eq_VhVh(Q6_V_vand_VV(v, vnan_exp), vnan_exp);
|
||||
HVX_VectorPred p_frac = Q6_Q_not_Q(Q6_Q_vcmp_eq_VhVh(Q6_V_vand_VV(v, vnan_frac), vnan_exp));
|
||||
return Q6_Q_and_QQ(p_exp, p_frac);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_f32_to_f16_shuff(HVX_Vector v0, HVX_Vector v1) {
|
||||
#if __HVX_ARCH__ >= 81
|
||||
HVX_Vector q0 = Q6_Vqf32_equals_Vsf(v0);
|
||||
HVX_Vector q1 = Q6_Vqf32_equals_Vsf(v1);
|
||||
#else
|
||||
const HVX_Vector zero = Q6_V_vzero();
|
||||
HVX_Vector q0 = Q6_Vqf32_vadd_VsfVsf(v0, zero);
|
||||
HVX_Vector q1 = Q6_Vqf32_vadd_VsfVsf(v1, zero);
|
||||
#endif
|
||||
return Q6_Vhf_equals_Wqf32(Q6_W_vcombine_VV(q1, q0));
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_f32_to_f16(HVX_Vector v0, HVX_Vector v1) {
|
||||
return Q6_Vh_vdeal_Vh(hvx_vec_f32_to_f16_shuff(v0, v1));
|
||||
}
|
||||
|
||||
#if __HVX_ARCH__ >= 79
|
||||
static inline HVX_VectorPair hvx_vec_f16_to_f32_shuff(HVX_Vector v) {
|
||||
const HVX_Vector one = hvx_vec_splat_f16(1.0);
|
||||
HVX_VectorPair p = Q6_Wsf_vmpy_VhfVhf(v, one);
|
||||
return Q6_W_vcombine_VV(Q6_V_hi_W(p), Q6_V_lo_W(p));
|
||||
}
|
||||
static inline HVX_VectorPair hvx_vec_f16_to_f32(HVX_Vector v) {
|
||||
const HVX_Vector one = hvx_vec_splat_f16(1.0);
|
||||
HVX_VectorPair p = Q6_Wsf_vmpy_VhfVhf(Q6_Vh_vshuff_Vh(v), one);
|
||||
return Q6_W_vcombine_VV(Q6_V_hi_W(p), Q6_V_lo_W(p));
|
||||
}
|
||||
#else
|
||||
static inline HVX_VectorPair hvx_vec_f16_to_f32_shuff(HVX_Vector v) {
|
||||
const HVX_Vector one = hvx_vec_splat_f16(1.0);
|
||||
HVX_VectorPair p = Q6_Wqf32_vmpy_VhfVhf(v, one);
|
||||
return Q6_W_vcombine_VV(Q6_Vsf_equals_Vqf32(Q6_V_hi_W(p)), Q6_Vsf_equals_Vqf32(Q6_V_lo_W(p)));
|
||||
}
|
||||
static inline HVX_VectorPair hvx_vec_f16_to_f32(HVX_Vector v) {
|
||||
const HVX_Vector one = hvx_vec_splat_f16(1.0);
|
||||
HVX_VectorPair p = Q6_Wqf32_vmpy_VhfVhf(Q6_Vh_vshuff_Vh(v), one);
|
||||
return Q6_W_vcombine_VV(Q6_Vsf_equals_Vqf32(Q6_V_hi_W(p)), Q6_Vsf_equals_Vqf32(Q6_V_lo_W(p)));
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline HVX_Vector hvx_vec_i16_from_hf_rnd_sat(HVX_Vector vin) {
|
||||
// This looks complicated.
|
||||
// Ideally should just be Q6_Vh_equals_Vhf(vin)
|
||||
// but that instruction does not do proper rounding.
|
||||
|
||||
// convert to qf32, multiplying by 1.0 in the process.
|
||||
HVX_VectorPair v32 = Q6_Wqf32_vmpy_VhfVhf(vin, Q6_Vh_vsplat_R(0x3C00));
|
||||
|
||||
// 'in-range' values are +/32752.
|
||||
// add 192K to it, convert to sf
|
||||
HVX_Vector v192K = Q6_V_vsplat_R(0x48400000);
|
||||
HVX_Vector vsf_0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vsf(Q6_V_lo_W(v32), v192K));
|
||||
HVX_Vector vsf_1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vsf(Q6_V_hi_W(v32), v192K));
|
||||
|
||||
// for in-range cases, result is {163858... 229360} so the exponent is always 144.
|
||||
// if we extract bits 21..0 as a signed quantity, and round 6 bits off, that will be the answer.
|
||||
// Start by <<10 to get the final 'sign' bit in bit 15...
|
||||
vsf_0 = Q6_Vw_vasl_VwR(vsf_0, 10);
|
||||
vsf_1 = Q6_Vw_vasl_VwR(vsf_1, 10);
|
||||
|
||||
// now round down to 16
|
||||
return Q6_Vh_vround_VwVw_sat(vsf_1, vsf_0);
|
||||
}
|
||||
|
||||
#if __HVX_ARCH__ < 79
|
||||
|
||||
static inline HVX_VectorPair hvx_vec_mpyacc_f32_f16(HVX_VectorPair acc, HVX_Vector x, HVX_Vector y)
|
||||
{
|
||||
HVX_VectorPair m = Q6_Wqf32_vmpy_VhfVhf(x, y);
|
||||
HVX_Vector a0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vsf(Q6_V_lo_W(m), Q6_V_lo_W(acc)));
|
||||
HVX_Vector a1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vsf(Q6_V_hi_W(m), Q6_V_hi_W(acc)));
|
||||
return Q6_W_vcombine_VV(a1, a0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline HVX_VectorPair hvx_vec_mpyacc_f32_f16(HVX_VectorPair acc, HVX_Vector x, HVX_Vector y)
|
||||
{
|
||||
return Q6_Wsf_vmpyacc_WsfVhfVhf(acc, x, y);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if __HVX_ARCH__ < 79
|
||||
|
||||
static inline HVX_Vector hvx_vec_add_f16_f16(HVX_Vector a, HVX_Vector b)
|
||||
{
|
||||
const HVX_Vector negone = Q6_Vh_vsplat_R(0xBC00); // -1.0 in IEEE FP16
|
||||
const HVX_Vector one = Q6_Vh_vsplat_R(0x3C00); // 1.0 in IEEE FP16
|
||||
HVX_VectorPair a_p = Q6_Wqf32_vmpy_VhfVhf(a, one);
|
||||
HVX_VectorPair b_p = Q6_Wqf32_vmpy_VhfVhf(b, negone);
|
||||
HVX_Vector a0 = Q6_Vqf32_vsub_Vqf32Vqf32(Q6_V_lo_W(a_p), Q6_V_lo_W(b_p));
|
||||
HVX_Vector a1 = Q6_Vqf32_vsub_Vqf32Vqf32(Q6_V_hi_W(a_p), Q6_V_hi_W(b_p));
|
||||
return Q6_Vhf_equals_Wqf32(Q6_W_vcombine_VV(a1, a0));
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_sub_f16_f16(HVX_Vector a, HVX_Vector b)
|
||||
{
|
||||
const HVX_Vector negone = Q6_Vh_vsplat_R(0xBC00); // -1.0 in IEEE FP16
|
||||
const HVX_Vector one = Q6_Vh_vsplat_R(0x3C00); // 1.0 in IEEE FP16
|
||||
HVX_VectorPair a_p = Q6_Wqf32_vmpy_VhfVhf(a, one);
|
||||
HVX_VectorPair b_p = Q6_Wqf32_vmpy_VhfVhf(b, negone);
|
||||
HVX_Vector a0 = Q6_Vqf32_vadd_Vqf32Vqf32(Q6_V_lo_W(a_p), Q6_V_lo_W(b_p));
|
||||
HVX_Vector a1 = Q6_Vqf32_vadd_Vqf32Vqf32(Q6_V_hi_W(a_p), Q6_V_hi_W(b_p));
|
||||
return Q6_Vhf_equals_Wqf32(Q6_W_vcombine_VV(a1, a0));
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_mul_f16_f16(HVX_Vector a, HVX_Vector b)
|
||||
{
|
||||
return Q6_Vhf_equals_Wqf32(Q6_Wqf32_vmpy_VhfVhf(a, b));
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_add_f32_f32(HVX_Vector a, HVX_Vector b) {
|
||||
return Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(a, b));
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_sub_f32_f32(HVX_Vector a, HVX_Vector b) {
|
||||
return Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(a, b));
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_mul_f32_f32(HVX_Vector a, HVX_Vector b) {
|
||||
return Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(a, b));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline HVX_Vector hvx_vec_add_f16_f16(HVX_Vector a, HVX_Vector b)
|
||||
{
|
||||
return Q6_Vhf_vadd_VhfVhf(a, b);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_sub_f16_f16(HVX_Vector a, HVX_Vector b)
|
||||
{
|
||||
return Q6_Vhf_vsub_VhfVhf(a, b);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_mul_f16_f16(HVX_Vector a, HVX_Vector b)
|
||||
{
|
||||
return Q6_Vhf_vmpy_VhfVhf(a, b);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_add_f32_f32(HVX_Vector a, HVX_Vector b) {
|
||||
return Q6_Vsf_vadd_VsfVsf(a, b);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_sub_f32_f32(HVX_Vector a, HVX_Vector b) {
|
||||
return Q6_Vsf_vsub_VsfVsf(a, b);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_mul_f32_f32(HVX_Vector a, HVX_Vector b) {
|
||||
return Q6_Vsf_vmpy_VsfVsf(a, b);
|
||||
}
|
||||
|
||||
#endif // __HVX_ARCH__ < 79
|
||||
|
||||
static inline HVX_Vector hvx_vec_load_act_tile(const uint8_t * y_q, uint32_t kt, HVX_Vector * v_act_all) {
|
||||
if (kt % 4 == 0) {
|
||||
*v_act_all = hvx_vmem(y_q + kt * 32);
|
||||
return *v_act_all;
|
||||
} else if (kt % 4 == 1) {
|
||||
return Q6_V_vror_VR(*v_act_all, 32);
|
||||
} else if (kt % 4 == 2) {
|
||||
return Q6_V_vror_VR(*v_act_all, 64);
|
||||
} else {
|
||||
return Q6_V_vror_VR(*v_act_all, 96);
|
||||
}
|
||||
}
|
||||
|
||||
// Full tile, in place. tmp: 32 vectors scratch.
|
||||
// Use when result must stay in m. Slowest (spills).
|
||||
static inline void hvx_transpose_32x32_w_shuff_inplace(HVX_Vector * restrict m, HVX_Vector * restrict tmp) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
HVX_VectorPair p = Q6_W_vshuff_VVR(m[2*i + 1], m[2*i], -4);
|
||||
tmp[2*i + 0] = Q6_V_lo_W(p);
|
||||
tmp[2*i + 1] = Q6_V_hi_W(p);
|
||||
}
|
||||
|
||||
for (int b = 0; b < 32; b += 4) {
|
||||
HVX_VectorPair p0 = Q6_W_vshuff_VVR(tmp[b + 2], tmp[b + 0], -8);
|
||||
HVX_VectorPair p1 = Q6_W_vshuff_VVR(tmp[b + 3], tmp[b + 1], -8);
|
||||
m[b + 0] = Q6_V_lo_W(p0); m[b + 1] = Q6_V_hi_W(p0);
|
||||
m[b + 2] = Q6_V_lo_W(p1); m[b + 3] = Q6_V_hi_W(p1);
|
||||
}
|
||||
|
||||
for (int b = 0; b < 32; b += 8) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
HVX_VectorPair p = Q6_W_vshuff_VVR(m[b + i + 4], m[b + i], -16);
|
||||
tmp[b + 2*i + 0] = Q6_V_lo_W(p);
|
||||
tmp[b + 2*i + 1] = Q6_V_hi_W(p);
|
||||
}
|
||||
}
|
||||
|
||||
for (int b = 0; b < 32; b += 16) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
HVX_VectorPair p = Q6_W_vshuff_VVR(tmp[b + i + 8], tmp[b + i], -32);
|
||||
m[b + 2*i + 0] = Q6_V_lo_W(p);
|
||||
m[b + 2*i + 1] = Q6_V_hi_W(p);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
HVX_VectorPair p = Q6_W_vshuff_VVR(m[i + 16], m[i], -64);
|
||||
tmp[2 * i + 0] = Q6_V_lo_W(p);
|
||||
tmp[2 * i + 1] = Q6_V_hi_W(p);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
m[i] = tmp[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Rows [0, nrows) of the transpose of src[0, ncols) to dst. src is clobbered, must not alias dst.
|
||||
// Use when not in VTCM or for full tiles.
|
||||
static inline void hvx_transpose_32x32_w_shuff(uint8_t * restrict dst, uint32_t dst_stride, HVX_Vector * restrict src,
|
||||
uint32_t nrows, uint32_t ncols) {
|
||||
for (uint32_t g = 0; g < ncols; g += 8) {
|
||||
HVX_Vector * s = src + g;
|
||||
|
||||
HVX_VectorPair a0 = Q6_W_vshuff_VVR(s[1], s[0], -4);
|
||||
HVX_VectorPair a1 = Q6_W_vshuff_VVR(s[3], s[2], -4);
|
||||
HVX_VectorPair a2 = Q6_W_vshuff_VVR(s[5], s[4], -4);
|
||||
HVX_VectorPair a3 = Q6_W_vshuff_VVR(s[7], s[6], -4);
|
||||
|
||||
HVX_VectorPair b0 = Q6_W_vshuff_VVR(Q6_V_lo_W(a1), Q6_V_lo_W(a0), -8);
|
||||
HVX_VectorPair b1 = Q6_W_vshuff_VVR(Q6_V_hi_W(a1), Q6_V_hi_W(a0), -8);
|
||||
HVX_VectorPair b2 = Q6_W_vshuff_VVR(Q6_V_lo_W(a3), Q6_V_lo_W(a2), -8);
|
||||
HVX_VectorPair b3 = Q6_W_vshuff_VVR(Q6_V_hi_W(a3), Q6_V_hi_W(a2), -8);
|
||||
|
||||
HVX_VectorPair c0 = Q6_W_vshuff_VVR(Q6_V_lo_W(b2), Q6_V_lo_W(b0), -16);
|
||||
HVX_VectorPair c1 = Q6_W_vshuff_VVR(Q6_V_hi_W(b2), Q6_V_hi_W(b0), -16);
|
||||
HVX_VectorPair c2 = Q6_W_vshuff_VVR(Q6_V_lo_W(b3), Q6_V_lo_W(b1), -16);
|
||||
HVX_VectorPair c3 = Q6_W_vshuff_VVR(Q6_V_hi_W(b3), Q6_V_hi_W(b1), -16);
|
||||
|
||||
s[0] = Q6_V_lo_W(c0); s[1] = Q6_V_hi_W(c0);
|
||||
s[2] = Q6_V_lo_W(c1); s[3] = Q6_V_hi_W(c1);
|
||||
s[4] = Q6_V_lo_W(c2); s[5] = Q6_V_hi_W(c2);
|
||||
s[6] = Q6_V_lo_W(c3); s[7] = Q6_V_hi_W(c3);
|
||||
}
|
||||
|
||||
for (uint32_t k = 0; 4 * k < nrows; k++) {
|
||||
HVX_VectorPair d0 = Q6_W_vshuff_VVR(src[k + 8], src[k], -32);
|
||||
HVX_VectorPair d1 = Q6_W_vshuff_VVR(src[k + 24], src[k + 16], -32);
|
||||
|
||||
HVX_VectorPair e0 = Q6_W_vshuff_VVR(Q6_V_lo_W(d1), Q6_V_lo_W(d0), -64);
|
||||
HVX_VectorPair e1 = Q6_W_vshuff_VVR(Q6_V_hi_W(d1), Q6_V_hi_W(d0), -64);
|
||||
|
||||
uint8_t * d = dst + 4 * k * dst_stride;
|
||||
*(HVX_Vector *) (d + 0 * dst_stride) = Q6_V_lo_W(e0);
|
||||
*(HVX_Vector *) (d + 1 * dst_stride) = Q6_V_hi_W(e0);
|
||||
*(HVX_Vector *) (d + 2 * dst_stride) = Q6_V_lo_W(e1);
|
||||
*(HVX_Vector *) (d + 3 * dst_stride) = Q6_V_hi_W(e1);
|
||||
}
|
||||
}
|
||||
|
||||
// Specialized store that will follow outstanding scatters or gathers to make sure they complete
|
||||
static inline void hvx_gather_sync(void * vtcm_addr) {
|
||||
asm volatile("vmem(%0+#0):scatter_release\n"
|
||||
"v0 = vmem(%0+#0)\n" :: "r"(vtcm_addr) : "v0", "memory");
|
||||
}
|
||||
|
||||
// Lane k = k * stride. Offsets for the _gather transposes.
|
||||
static inline HVX_Vector hvx_vec_gather_offsets_w(uint32_t stride) {
|
||||
int32_t offsets[32] __attribute__((aligned(128)));
|
||||
for (int k = 0; k < 32; k++) {
|
||||
offsets[k] = k * stride;
|
||||
}
|
||||
return *(HVX_Vector *) offsets;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_gather_offsets_h(uint32_t stride) {
|
||||
int16_t offsets[64] __attribute__((aligned(128)));
|
||||
for (int k = 0; k < 64; k++) {
|
||||
offsets[k] = k * stride;
|
||||
}
|
||||
return *(HVX_Vector *) offsets;
|
||||
}
|
||||
|
||||
// Rows [0, nrows) of the transpose of ncols src vectors (src_stride apart) to dst.
|
||||
// src, dst in VTCM. Async: hvx_gather_sync() before use.
|
||||
// Use for partial tiles (small ncols). Fewest packets.
|
||||
static inline void hvx_transpose_32x32_w_gather(uint8_t * restrict dst, uint32_t dst_stride, const uint8_t * restrict src,
|
||||
uint32_t src_stride, HVX_Vector offsets, uint32_t nrows, uint32_t ncols) {
|
||||
const uint32_t mu = ncols * src_stride;
|
||||
|
||||
#pragma unroll(4)
|
||||
for (uint32_t r = 0; r < nrows; r++) {
|
||||
Q6_vgather_ARMVw((HVX_Vector *) (dst + r * dst_stride), (size_t) (src + r * sizeof(int32_t)), mu, offsets);
|
||||
}
|
||||
}
|
||||
|
||||
// 16-bit version of hvx_transpose_32x32_w_gather.
|
||||
static inline void hvx_transpose_64x64_h_gather(uint8_t * restrict dst, uint32_t dst_stride, const uint8_t * restrict src,
|
||||
uint32_t src_stride, HVX_Vector offsets, uint32_t nrows, uint32_t ncols) {
|
||||
const uint32_t mu = ncols * src_stride;
|
||||
|
||||
#pragma unroll(4)
|
||||
for (uint32_t r = 0; r < nrows; r++) {
|
||||
Q6_vgather_ARMVh((HVX_Vector *) (dst + r * dst_stride), (size_t) (src + r * sizeof(int16_t)), mu, offsets);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HVX_BASE_H */
|
||||
@@ -0,0 +1,332 @@
|
||||
#ifndef HVX_COPY_H
|
||||
#define HVX_COPY_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hvx-base.h"
|
||||
|
||||
#define hvx_splat_pragma(x) _Pragma(#x)
|
||||
#define hvx_splat_loop_body(dst_type, vec_store, unroll_cnt) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
\
|
||||
uint32_t nvec = n / (128 / elem_size); \
|
||||
uint32_t nloe = n % (128 / elem_size); \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
hvx_splat_pragma(unroll(unroll_cnt)) \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = src; \
|
||||
} \
|
||||
if (nloe) { \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, src); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_splat_a(void * restrict dst, HVX_Vector src, uint32_t n, uint32_t elem_size) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_splat_loop_body(HVX_Vector, hvx_vec_store_a, 4);
|
||||
}
|
||||
|
||||
static inline void hvx_splat_u(void * restrict dst, HVX_Vector src, uint32_t n, uint32_t elem_size) {
|
||||
hvx_splat_loop_body(HVX_UVector, hvx_vec_store_u, 4);
|
||||
}
|
||||
|
||||
static inline void hvx_splat_f32_a(void * restrict dst, float v, uint32_t n) {
|
||||
hvx_splat_a(dst, hvx_vec_splat_f32(v), n, sizeof(float));
|
||||
}
|
||||
|
||||
static inline void hvx_splat_f32_u(void * restrict dst, float v, uint32_t n) {
|
||||
hvx_splat_u(dst, hvx_vec_splat_f32(v), n, sizeof(float));
|
||||
}
|
||||
|
||||
static inline void hvx_splat_f16_a(void * restrict dst, _Float16 v, uint32_t n) {
|
||||
hvx_splat_a(dst, hvx_vec_splat_f16(v), n, sizeof(__fp16));
|
||||
}
|
||||
|
||||
static inline void hvx_splat_f16_u(void * restrict dst, _Float16 v, uint32_t n) {
|
||||
hvx_splat_u(dst, hvx_vec_splat_f16(v), n, sizeof(__fp16));
|
||||
}
|
||||
|
||||
static inline void hvx_splat_u16_a(void * restrict dst, uint16_t v, uint32_t n) {
|
||||
hvx_splat_a(dst, Q6_Vh_vsplat_R(v), n, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
static inline void hvx_splat_u16_u(void * restrict dst, uint16_t v, uint32_t n) {
|
||||
hvx_splat_u(dst, Q6_Vh_vsplat_R(v), n, sizeof(uint16_t));
|
||||
}
|
||||
|
||||
static inline void hvx_splat_u8_a(void * restrict dst, uint8_t v, uint32_t n) {
|
||||
hvx_splat_a(dst, Q6_Vb_vsplat_R(v), n, 1);
|
||||
}
|
||||
|
||||
static inline void hvx_splat_u8_u(void * restrict dst, uint8_t v, uint32_t n) {
|
||||
hvx_splat_u(dst, Q6_Vb_vsplat_R(v), n, 1);
|
||||
}
|
||||
|
||||
#define hvx_copy_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { vdst[i] = vsrc[i]; } \
|
||||
if (nloe) { \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, vsrc[i]); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Generic copy routines
|
||||
static inline void hvx_copy_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n, uint32_t elem_size) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_copy_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_copy_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n, uint32_t elem_size) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_copy_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_copy_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n, uint32_t elem_size) {
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_copy_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_copy_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n, uint32_t elem_size) {
|
||||
hvx_copy_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
// copy n fp16 elements : destination and source are aligned to HVX Vector (128)
|
||||
static inline void hvx_copy_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_aa(dst, src, n, sizeof(__fp16));
|
||||
}
|
||||
|
||||
// copy n fp16 elements : destination is aligned, source is unaligned
|
||||
static inline void hvx_copy_f16_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_au(dst, src, n, sizeof(__fp16));
|
||||
}
|
||||
|
||||
// copy n fp16 elements : destination is unaligned, source is aligned
|
||||
static inline void hvx_copy_f16_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_ua(dst, src, n, sizeof(__fp16));
|
||||
}
|
||||
|
||||
// copy n fp16 elements : destination and source are unaligned
|
||||
static inline void hvx_copy_f16_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_uu(dst, src, n, sizeof(__fp16));
|
||||
}
|
||||
|
||||
// copy n fp32 elements : destination and source are aligned to HVX Vector (128)
|
||||
static inline void hvx_copy_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_aa(dst, src, n, sizeof(float));
|
||||
}
|
||||
|
||||
// copy n fp32 elements : destination is aligned, source is unaligned
|
||||
static inline void hvx_copy_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_au(dst, src, n, sizeof(float));
|
||||
}
|
||||
|
||||
// copy n fp32 elements : destination is unaligned, source is aligned
|
||||
static inline void hvx_copy_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_ua(dst, src, n, sizeof(float));
|
||||
}
|
||||
|
||||
// copy n fp32 elements : destination and source are unaligned
|
||||
static inline void hvx_copy_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_uu(dst, src, n, sizeof(float));
|
||||
}
|
||||
|
||||
//// fp32 -> fp16
|
||||
|
||||
#define hvx_copy_f16_f32_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(__fp16); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = hvx_vec_f32_to_f16(vsrc[i*2+0], vsrc[i*2+1]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = hvx_vec_f32_to_f16(vsrc[i*2+0], vsrc[i*2+1]); \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// copy/convert n fp32 elements into n fp16 elements : destination and source are aligned
|
||||
static inline void hvx_copy_f16_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_copy_f16_f32_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
// copy/convert n fp32 elements into n fp16 elements : destination is aligned, source is unaligned
|
||||
static inline void hvx_copy_f16_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_copy_f16_f32_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
// copy/convert n fp32 elements into n fp16 elements : destination is unaligned, source is aligned
|
||||
static inline void hvx_copy_f16_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_copy_f16_f32_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
// copy/convert n fp32 elements into n fp16 elements : destination and source are unaligned
|
||||
static inline void hvx_copy_f16_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_f16_f32_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
//// fp16 -> fp32
|
||||
|
||||
#define hvx_copy_f32_f16_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const HVX_Vector one = hvx_vec_splat_f16(1.0); \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(__fp16); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (i = 0; i < nvec; ++i) { \
|
||||
HVX_VectorPair p = Q6_Wqf32_vmpy_VhfVhf(Q6_Vh_vshuff_Vh(vsrc[i]), one); \
|
||||
vdst[i*2] = Q6_Vsf_equals_Vqf32(Q6_V_lo_W(p)); \
|
||||
vdst[i*2+1] = Q6_Vsf_equals_Vqf32(Q6_V_hi_W(p)); \
|
||||
} \
|
||||
\
|
||||
if (nloe) { \
|
||||
HVX_VectorPair p = Q6_Wqf32_vmpy_VhfVhf(Q6_Vh_vshuff_Vh(vsrc[i]), one); \
|
||||
\
|
||||
HVX_Vector vd = Q6_V_lo_W(p); \
|
||||
i = 2 * i; \
|
||||
\
|
||||
if (nloe >= 32) { \
|
||||
vdst[i] = Q6_Vsf_equals_Vqf32(vd); \
|
||||
nloe -= 32; ++i; vd = Q6_V_hi_W(p); \
|
||||
} \
|
||||
\
|
||||
if (nloe) { \
|
||||
vd = Q6_Vsf_equals_Vqf32(vd); \
|
||||
hvx_vec_store_u(&vdst[i], nloe * sizeof(float), vd); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// copy/convert n fp16 elements into n fp32 elements : destination and source are aligned
|
||||
static inline void hvx_copy_f32_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_copy_f32_f16_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
// copy/convert n fp16 elements into n fp32 elements : destination is aligned, source is unaligned
|
||||
static inline void hvx_copy_f32_f16_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_copy_f32_f16_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
// copy/convert n fp16 elements into n fp32 elements : destination is unaligned, source is aligned
|
||||
static inline void hvx_copy_f32_f16_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_copy_f32_f16_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
// copy/convert n fp16 elements into n fp32 elements : destination and source are unaligned
|
||||
static inline void hvx_copy_f32_f16_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_f32_f16_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
//// fp32 -> int32
|
||||
|
||||
#define hvx_copy_i32_f32_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(int32_t); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = Q6_Vw_equals_Vsf(vsrc[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = Q6_Vw_equals_Vsf(vsrc[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// copy/convert n fp32 elements into n int32 elements : destination and source are aligned
|
||||
static inline void hvx_copy_i32_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_copy_i32_f32_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
// copy/convert n fp32 elements into n int32 elements : destination and source are unaligned
|
||||
static inline void hvx_copy_i32_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_i32_f32_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
//// int32 -> fp32
|
||||
|
||||
#define hvx_copy_f32_i32_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(float); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = Q6_Vsf_equals_Vw(vsrc[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = Q6_Vsf_equals_Vw(vsrc[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// copy/convert n int32 elements into n fp32 elements : destination and source are aligned
|
||||
static inline void hvx_copy_f32_i32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_copy_f32_i32_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
// copy/convert n int32 elements into n fp32 elements : destination and source are unaligned
|
||||
static inline void hvx_copy_f32_i32_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_copy_f32_i32_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
#endif // HVX_COPY_H
|
||||
@@ -0,0 +1,291 @@
|
||||
#ifndef HVX_DIV_H
|
||||
#define HVX_DIV_H
|
||||
|
||||
#include <HAP_farf.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hvx-base.h"
|
||||
#include "hex-utils.h"
|
||||
#include "hvx-inverse.h"
|
||||
#include "hvx-arith.h"
|
||||
|
||||
#if __HVX_ARCH__ < 79
|
||||
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(a, b))
|
||||
#define HVX_OP_MUL_F16(a, b) Q6_Vhf_equals_Wqf32(Q6_Wqf32_vmpy_VhfVhf(a, b))
|
||||
#else
|
||||
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_vmpy_VsfVsf(a, b)
|
||||
#define HVX_OP_MUL_F16(a, b) Q6_Vhf_vmpy_VhfVhf(a, b)
|
||||
#endif
|
||||
|
||||
// Compute div by scaler in f32. Requires first by expanding fp32 to fp16 and converting the result back to fp32.
|
||||
static inline HVX_Vector hvx_div_mul_f16_const_using_f32(HVX_Vector vec1_hf, HVX_Vector vec2_sf_const, HVX_Vector vec_hf_one_1_0) {
|
||||
#if __HVX_ARCH__ < 79
|
||||
HVX_VectorPair src_to_f32 = Q6_Wqf32_vmpy_VhfVhf(vec1_hf, vec_hf_one_1_0);
|
||||
HVX_Vector src_to_f32_0 = Q6_Vsf_equals_Vqf32(Q6_V_lo_W(src_to_f32));
|
||||
HVX_Vector src_to_f32_1 = Q6_Vsf_equals_Vqf32(Q6_V_hi_W(src_to_f32));
|
||||
#else
|
||||
HVX_VectorPair src_to_f32 = Q6_Wsf_vmpy_VhfVhf(vec1_hf, vec_hf_one_1_0);
|
||||
HVX_Vector src_to_f32_0 = Q6_V_lo_W(src_to_f32);
|
||||
HVX_Vector src_to_f32_1 = Q6_V_hi_W(src_to_f32);
|
||||
#endif
|
||||
|
||||
HVX_Vector div_f32_0 = HVX_OP_MUL_F32(src_to_f32_0, vec2_sf_const);
|
||||
HVX_Vector div_f32_1 = HVX_OP_MUL_F32(src_to_f32_1, vec2_sf_const);
|
||||
|
||||
#if __HVX_ARCH__ < 79
|
||||
HVX_Vector res = hvx_vec_f32_to_f16(div_f32_0, div_f32_1);
|
||||
#else
|
||||
HVX_Vector res = Q6_Vhf_vcvt_VsfVsf(div_f32_0, div_f32_1);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
// Variant for <v79: Use pre-computed f16 reciprocal constant
|
||||
static inline HVX_Vector hvx_div_mul_f16_const_using_f16(HVX_Vector vec1_hf, HVX_Vector const_inv_hf) {
|
||||
// Multiply by pre-computed f16 reciprocal constant
|
||||
return HVX_OP_MUL_F16(vec1_hf, const_inv_hf);
|
||||
}
|
||||
|
||||
#define hvx_div_scaler_f16_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
HVX_Vector hf_one = Q6_Vh_vsplat_R(0x3C00); \
|
||||
\
|
||||
const uint32_t nvec = n / VLEN_FP16; \
|
||||
const uint32_t nloe = n % VLEN_FP16; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
HVX_Vector res; \
|
||||
if (__HVX_ARCH__ < 79) { \
|
||||
res = hvx_div_mul_f16_const_using_f16(vsrc[i], val_vec_f16); \
|
||||
} else { \
|
||||
res = hvx_div_mul_f16_const_using_f32(vsrc[i], val_vec_f32, hf_one); \
|
||||
} \
|
||||
vdst[i] = res; \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector res; \
|
||||
if (__HVX_ARCH__ < 79) { \
|
||||
res = hvx_div_mul_f16_const_using_f16(vsrc[i], val_vec_f16); \
|
||||
} else { \
|
||||
res = hvx_div_mul_f16_const_using_f32(vsrc[i], val_vec_f32, hf_one); \
|
||||
} \
|
||||
vec_store((void *) &vdst[i], nloe * SIZEOF_FP16, res); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_div_scalar_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 val, uint32_t n) {
|
||||
const HVX_Vector val_vec_f32 = hvx_vec_splat_f32(1.0f/((float)val));
|
||||
const HVX_Vector val_vec_f16 = hvx_vec_splat_f16(1.0f / val);
|
||||
assert((uintptr_t) dst % 128 == 0);
|
||||
assert((uintptr_t) src % 128 == 0);
|
||||
hvx_div_scaler_f16_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
static inline void hvx_div_scalar_f16_au(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 val, uint32_t n) {
|
||||
const HVX_Vector val_vec_f32 = hvx_vec_splat_f32(1.0f/((float)val));
|
||||
const HVX_Vector val_vec_f16 = hvx_vec_splat_f16(1.0f / val);
|
||||
assert((uintptr_t) dst % 128 == 0);
|
||||
hvx_div_scaler_f16_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
static inline void hvx_div_scalar_f16_ua(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 val, uint32_t n) {
|
||||
const HVX_Vector val_vec_f32 = hvx_vec_splat_f32(1.0f/((float)val));
|
||||
const HVX_Vector val_vec_f16 = hvx_vec_splat_f16(1.0f / val);
|
||||
assert((uintptr_t) src % 128 == 0);
|
||||
hvx_div_scaler_f16_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
static inline void hvx_div_scalar_f16_uu(uint8_t * restrict dst, const uint8_t * restrict src, const _Float16 val, uint32_t n) {
|
||||
const HVX_Vector val_vec_f32 = hvx_vec_splat_f32(1.0f/((float)val));
|
||||
const HVX_Vector val_vec_f16 = hvx_vec_splat_f16(1.0f / val);
|
||||
hvx_div_scaler_f16_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
// Compute div by using hvx_vec_inverse_f32_guard. Requires first by exapnding fp32 to fp16 and convert the result back to fp32.
|
||||
static inline HVX_Vector hvx_vec_div_f16_using_f32(HVX_Vector vec1, HVX_Vector vec2, HVX_Vector f32_nan_inf_mask, HVX_Vector vec_hf_one_1_0) {
|
||||
#if __HVX_ARCH__ < 79
|
||||
// Convert first input to fp32
|
||||
HVX_VectorPair vec1_to_f32 = Q6_Wqf32_vmpy_VhfVhf(vec1, vec_hf_one_1_0); // *1.0
|
||||
HVX_Vector vec1_to_f32_0 = Q6_Vsf_equals_Vqf32(Q6_V_lo_W(vec1_to_f32));
|
||||
HVX_Vector vec1_to_f32_1 = Q6_Vsf_equals_Vqf32(Q6_V_hi_W(vec1_to_f32));
|
||||
|
||||
// Convert second input to fp32
|
||||
HVX_VectorPair vec2_to_f32 = Q6_Wqf32_vmpy_VhfVhf(vec2, vec_hf_one_1_0); // *1.0
|
||||
HVX_Vector vec2_to_f32_0 = Q6_Vsf_equals_Vqf32(Q6_V_lo_W(vec2_to_f32));
|
||||
HVX_Vector vec2_to_f32_1 = Q6_Vsf_equals_Vqf32(Q6_V_hi_W(vec2_to_f32));
|
||||
#else
|
||||
// Convert first input to fp32
|
||||
HVX_VectorPair vec1_to_f32 = Q6_Wsf_vmpy_VhfVhf(vec1, vec_hf_one_1_0); // *1.0
|
||||
HVX_Vector vec1_to_f32_0 = Q6_V_lo_W(vec1_to_f32);
|
||||
HVX_Vector vec1_to_f32_1 = Q6_V_hi_W(vec1_to_f32);
|
||||
|
||||
// Convert second input to fp32
|
||||
HVX_VectorPair vec2_to_f32 = Q6_Wsf_vmpy_VhfVhf(vec2, vec_hf_one_1_0); // *1.0
|
||||
HVX_Vector vec2_to_f32_0 = Q6_V_lo_W(vec2_to_f32);
|
||||
HVX_Vector vec2_to_f32_1 = Q6_V_hi_W(vec2_to_f32);
|
||||
#endif
|
||||
|
||||
// Inverse second input in fp32
|
||||
HVX_Vector vec2_inv_f32_0 = hvx_vec_inverse_f32_guard(vec2_to_f32_0, f32_nan_inf_mask);
|
||||
HVX_Vector vec2_inv_f32_1 = hvx_vec_inverse_f32_guard(vec2_to_f32_1, f32_nan_inf_mask);
|
||||
|
||||
// Multiply first input by inverse of second, in fp32
|
||||
HVX_Vector div_f32_0 = HVX_OP_MUL_F32(vec1_to_f32_0, vec2_inv_f32_0);
|
||||
HVX_Vector div_f32_1 = HVX_OP_MUL_F32(vec1_to_f32_1, vec2_inv_f32_1);
|
||||
|
||||
// Convert back to fp16
|
||||
#if __HVX_ARCH__ < 79
|
||||
HVX_Vector recip = hvx_vec_f32_to_f16(div_f32_0, div_f32_1);
|
||||
#else
|
||||
HVX_Vector recip = Q6_Vhf_vcvt_VsfVsf(div_f32_0, div_f32_1);
|
||||
#endif
|
||||
|
||||
return recip;
|
||||
}
|
||||
|
||||
// Hybrid approach: f16 reciprocal for <v79, f32 precision for >=v79
|
||||
static inline HVX_Vector hvx_vec_hybrid_div_f16(HVX_Vector vec1, HVX_Vector vec2, HVX_Vector f32_nan_inf_mask, HVX_Vector f16_nan_inf_mask, HVX_Vector vec_hf_one_1_0) {
|
||||
#if __HVX_ARCH__ < 79
|
||||
// For older architectures, use f16 reciprocal to avoid NaN/-inf issues
|
||||
HVX_Vector vec2_inv = hvx_vec_inverse_f16_guard(vec2, f16_nan_inf_mask);
|
||||
return HVX_OP_MUL_F16(vec1, vec2_inv);
|
||||
#else
|
||||
return hvx_vec_div_f16_using_f32(vec1, vec2, f32_nan_inf_mask, vec_hf_one_1_0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define hvx_div_f16_loop_body(dst_type, src0_type, src1_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src0_type * restrict vsrc0 = (src0_type *) src0; \
|
||||
src1_type * restrict vsrc1 = (src1_type *) src1; \
|
||||
\
|
||||
const HVX_Vector f32_nan_inf_mask = Q6_V_vsplat_R(0x7f800000); \
|
||||
const HVX_Vector f16_nan_inf_mask = Q6_Vh_vsplat_R(0x7c00); \
|
||||
const HVX_Vector hf_one = Q6_Vh_vsplat_R(0x3C00); \
|
||||
\
|
||||
const uint32_t nvec = n / VLEN_FP16; \
|
||||
const uint32_t nloe = n % VLEN_FP16; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
HVX_Vector res = hvx_vec_hybrid_div_f16(vsrc0[i], vsrc1[i], \
|
||||
f32_nan_inf_mask, f16_nan_inf_mask, \
|
||||
hf_one); \
|
||||
vdst[i] = res; \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector res = hvx_vec_hybrid_div_f16(vsrc0[i], vsrc1[i], \
|
||||
f32_nan_inf_mask, f16_nan_inf_mask, \
|
||||
hf_one); \
|
||||
vec_store((void *) &vdst[i], nloe * SIZEOF_FP16, res); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define hvx_div_f32_loop_body(dst_type, src0_type, src1_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src0_type * restrict vsrc0 = (src0_type *) src0; \
|
||||
src1_type * restrict vsrc1 = (src1_type *) src1; \
|
||||
\
|
||||
const HVX_Vector nan_inf_mask = Q6_V_vsplat_R(0x7f800000); \
|
||||
\
|
||||
const uint32_t nvec = n / VLEN_FP32; \
|
||||
const uint32_t nloe = n % VLEN_FP32; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
HVX_Vector inv_src1 = hvx_vec_inverse_f32_guard(vsrc1[i], nan_inf_mask); \
|
||||
HVX_Vector res = HVX_OP_MUL_F32(vsrc0[i], inv_src1); \
|
||||
vdst[i] = res; \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector inv_src1 = hvx_vec_inverse_f32_guard(vsrc1[i], nan_inf_mask); \
|
||||
HVX_Vector res = HVX_OP_MUL_F32(vsrc0[i], inv_src1); \
|
||||
vec_store((void *) &vdst[i], nloe * SIZEOF_FP32, res); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Generic macro to define alignment permutations for an op
|
||||
#define DEFINE_HVX_DIV_OP_VARIANTS(OP_NAME, OP_LOOP_BODY) \
|
||||
static inline void OP_NAME##_aaa(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
assert((uintptr_t) src0 % 128 == 0); \
|
||||
assert((uintptr_t) src1 % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_Vector, HVX_Vector, HVX_Vector, hvx_vec_store_a); \
|
||||
} \
|
||||
static inline void OP_NAME##_aau(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
assert((uintptr_t) src0 % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_Vector, HVX_Vector, HVX_UVector, hvx_vec_store_a); \
|
||||
} \
|
||||
static inline void OP_NAME##_aua(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
assert((uintptr_t) src1 % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_Vector, HVX_UVector, HVX_Vector, hvx_vec_store_a); \
|
||||
} \
|
||||
static inline void OP_NAME##_auu(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_Vector, HVX_UVector, HVX_UVector, hvx_vec_store_a); \
|
||||
} \
|
||||
static inline void OP_NAME##_uaa(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, uint32_t n) { \
|
||||
assert((uintptr_t) src0 % 128 == 0); \
|
||||
assert((uintptr_t) src1 % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_UVector, HVX_Vector, HVX_Vector, hvx_vec_store_u); \
|
||||
} \
|
||||
static inline void OP_NAME##_uau(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, uint32_t n) { \
|
||||
assert((uintptr_t) src0 % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_UVector, HVX_Vector, HVX_UVector, hvx_vec_store_u); \
|
||||
} \
|
||||
static inline void OP_NAME##_uua(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, uint32_t n) { \
|
||||
assert((uintptr_t) src1 % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_UVector, HVX_UVector, HVX_Vector, hvx_vec_store_u); \
|
||||
} \
|
||||
static inline void OP_NAME##_uuu(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, uint32_t n) { \
|
||||
OP_LOOP_BODY(HVX_UVector, HVX_UVector, HVX_UVector, hvx_vec_store_u); \
|
||||
} \
|
||||
|
||||
// Dispatcher logic
|
||||
#define HVX_DIV_DISPATCHER(OP_NAME) \
|
||||
static inline void OP_NAME(uint8_t * restrict dst, const uint8_t * restrict src0, const uint8_t * restrict src1, const uint32_t num_elems) { \
|
||||
if (hex_is_aligned((void *) dst, 128)) { \
|
||||
if (hex_is_aligned((void *) src0, 128)) { \
|
||||
if (hex_is_aligned((void *) src1, 128)) OP_NAME##_aaa(dst, src0, src1, num_elems); \
|
||||
else OP_NAME##_aau(dst, src0, src1, num_elems); \
|
||||
} else { \
|
||||
if (hex_is_aligned((void *) src1, 128)) OP_NAME##_aua(dst, src0, src1, num_elems); \
|
||||
else OP_NAME##_auu(dst, src0, src1, num_elems); \
|
||||
} \
|
||||
} else { \
|
||||
if (hex_is_aligned((void *) src0, 128)) { \
|
||||
if (hex_is_aligned((void *) src1, 128)) OP_NAME##_uaa(dst, src0, src1, num_elems); \
|
||||
else OP_NAME##_uau(dst, src0, src1, num_elems); \
|
||||
} else { \
|
||||
if (hex_is_aligned((void *) src1, 128)) OP_NAME##_uua(dst, src0, src1, num_elems); \
|
||||
else OP_NAME##_uuu(dst, src0, src1, num_elems); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_HVX_DIV_OP_VARIANTS(hvx_div_f32, hvx_div_f32_loop_body)
|
||||
DEFINE_HVX_DIV_OP_VARIANTS(hvx_div_f16, hvx_div_f16_loop_body)
|
||||
|
||||
HVX_DIV_DISPATCHER(hvx_div_f32)
|
||||
HVX_DIV_DISPATCHER(hvx_div_f16)
|
||||
|
||||
#undef HVX_OP_MUL_F32
|
||||
#undef HVX_OP_MUL_F16
|
||||
|
||||
#endif // HVX_DIV_H
|
||||
@@ -0,0 +1,129 @@
|
||||
#ifndef HVX_DUMP_H
|
||||
#define HVX_DUMP_H
|
||||
|
||||
#include <HAP_farf.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hex-utils.h"
|
||||
#include "hvx-types.h"
|
||||
|
||||
static void hvx_vec_dump_f16_n(char * pref, HVX_Vector v, uint32_t n) {
|
||||
HVX_VectorAlias u = { .v = v };
|
||||
|
||||
const uint32_t n0 = n / 16;
|
||||
const uint32_t n1 = n % 16;
|
||||
int i = 0;
|
||||
for (; i < n0; i++) {
|
||||
hex_dump_f16_line(pref, u.fp16 + (16 * i), 16);
|
||||
}
|
||||
if (n1) {
|
||||
hex_dump_f16_line(pref, u.fp16 + (16 * i), n1);
|
||||
}
|
||||
}
|
||||
|
||||
static void hvx_vec_dump_f16(char * pref, HVX_Vector v) {
|
||||
hvx_vec_dump_f16_n(pref, v, 64);
|
||||
}
|
||||
|
||||
static void hvx_vec_dump_f32_n(char * pref, HVX_Vector v, uint32_t n) {
|
||||
HVX_VectorAlias u = { .v = v };
|
||||
|
||||
const uint32_t n0 = n / 16;
|
||||
const uint32_t n1 = n % 16;
|
||||
int i = 0;
|
||||
for (; i < n0; i++) {
|
||||
hex_dump_f32_line(pref, u.fp32 + (16 * i), 16);
|
||||
}
|
||||
if (n1) {
|
||||
hex_dump_f32_line(pref, u.fp32 + (16 * i), n1);
|
||||
}
|
||||
}
|
||||
|
||||
static void hvx_vec_dump_f32_hmt(char * pref, HVX_Vector v) {
|
||||
union {
|
||||
HVX_Vector v;
|
||||
float d[32];
|
||||
} u = { .v = v };
|
||||
|
||||
FARF(HIGH, "%s: %.6f %.6f %.6f %.6f ... %.6f %.6f %.6f %.6f ... %.6f %.6f %.6f %.6f\n", pref, u.d[0], u.d[1],
|
||||
u.d[2], u.d[3], u.d[12], u.d[13], u.d[14], u.d[15], u.d[28], u.d[29], u.d[30], u.d[31]);
|
||||
}
|
||||
|
||||
static void hvx_vec_dump_f32(char * pref, HVX_Vector v) {
|
||||
hvx_vec_dump_f32_n(pref, v, 32);
|
||||
}
|
||||
|
||||
static void hvx_vec_dump_int32(char * pref, HVX_Vector v) {
|
||||
union {
|
||||
HVX_Vector v;
|
||||
int32_t d[32];
|
||||
} u = { .v = v };
|
||||
|
||||
for (int i = 0; i < 32 / 16; i++) {
|
||||
hex_dump_int32_line(pref, u.d + (16 * i), 16);
|
||||
}
|
||||
}
|
||||
|
||||
static void hvx_vec_dump_int32_hmt(char * pref, HVX_Vector v) {
|
||||
union {
|
||||
HVX_Vector v;
|
||||
int32_t d[32];
|
||||
} u = { .v = v };
|
||||
|
||||
FARF(HIGH, "%s: %d %d %d %d ... %d %d %d %d ... %d %d %d %d\n", pref, u.d[0], u.d[1], u.d[2], u.d[3], u.d[12],
|
||||
u.d[13], u.d[14], u.d[15], u.d[28], u.d[29], u.d[30], u.d[31]);
|
||||
}
|
||||
|
||||
static void hvx_vec_dump_int8_hmt(char * pref, HVX_Vector v) {
|
||||
union {
|
||||
HVX_Vector v;
|
||||
int8_t d[128];
|
||||
} u = { .v = v };
|
||||
|
||||
FARF(HIGH, "%s: %d %d %d %d ... %d %d %d %d ... %d %d %d %d\n", pref, u.d[0], u.d[1], u.d[2], u.d[3], u.d[60],
|
||||
u.d[61], u.d[62], u.d[63], u.d[124], u.d[125], u.d[126], u.d[127]);
|
||||
}
|
||||
|
||||
static void hvx_vec_dump_int8(char * pref, HVX_Vector v) {
|
||||
union {
|
||||
HVX_Vector v;
|
||||
int8_t d[128];
|
||||
} u = { .v = v };
|
||||
|
||||
for (int i = 0; i < 128 / 16; i++) {
|
||||
hex_dump_int8_line(pref, u.d + (16 * i), 16);
|
||||
}
|
||||
}
|
||||
|
||||
static void hvx_vec_dump_uint8(char * pref, HVX_Vector v) {
|
||||
union {
|
||||
HVX_Vector v;
|
||||
uint8_t d[128];
|
||||
} u = { .v = v };
|
||||
|
||||
for (int i = 0; i < 128 / 16; i++) {
|
||||
hex_dump_uint8_line(pref, u.d + (16 * i), 16);
|
||||
}
|
||||
}
|
||||
|
||||
static bool hvx_vec_eq(HVX_Vector v0, HVX_Vector v1, size_t n) {
|
||||
typedef union {
|
||||
HVX_Vector v;
|
||||
int8_t d[128];
|
||||
} U;
|
||||
|
||||
U u0 = { .v = v0 };
|
||||
U u1 = { .v = v1 };
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (u0.d[i] != u1.d[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* HVX_DUMP_H */
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef HVX_ERF_H
|
||||
#define HVX_ERF_H
|
||||
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-exp.h"
|
||||
#include "hvx-inverse.h"
|
||||
|
||||
// Maximum error is about 1.5e-7 for the Abramowitz-Stegun approximation.
|
||||
static __attribute__((noinline)) HVX_Vector hvx_vec_erf_f32(HVX_Vector x) {
|
||||
const HVX_Vector zero = hvx_vec_splat_f32(0.0f);
|
||||
const HVX_Vector ax = hvx_vec_abs_f32(x);
|
||||
HVX_Vector t = hvx_vec_inverse_f32(hvx_vec_add_f32_f32(
|
||||
hvx_vec_splat_f32(1.0f), hvx_vec_mul_f32_f32(hvx_vec_splat_f32(0.3275911f), ax)));
|
||||
|
||||
HVX_Vector poly = hvx_vec_splat_f32(1.061405429f);
|
||||
poly = hvx_vec_add_f32_f32(hvx_vec_splat_f32(-1.453152027f), hvx_vec_mul_f32_f32(poly, t));
|
||||
poly = hvx_vec_add_f32_f32(hvx_vec_splat_f32(1.421413741f), hvx_vec_mul_f32_f32(poly, t));
|
||||
poly = hvx_vec_add_f32_f32(hvx_vec_splat_f32(-0.284496736f), hvx_vec_mul_f32_f32(poly, t));
|
||||
poly = hvx_vec_add_f32_f32(hvx_vec_splat_f32(0.254829592f), hvx_vec_mul_f32_f32(poly, t));
|
||||
|
||||
const HVX_Vector exp_term = hvx_vec_exp_f32(hvx_vec_neg_f32(hvx_vec_mul_f32_f32(ax, ax)));
|
||||
HVX_Vector result = hvx_vec_sub_f32_f32(hvx_vec_splat_f32(1.0f),
|
||||
hvx_vec_mul_f32_f32(hvx_vec_mul_f32_f32(poly, t), exp_term));
|
||||
|
||||
const HVX_VectorPred neg = Q6_Q_vcmp_gt_VsfVsf(zero, x);
|
||||
result = Q6_V_vmux_QVV(neg, hvx_vec_neg_f32(result), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// GELU_ERF uses the erf definition.
|
||||
static inline HVX_Vector hvx_vec_gelu_erf_f32(HVX_Vector x) {
|
||||
const HVX_Vector scale = hvx_vec_splat_f32(0.7071067811865475f);
|
||||
const HVX_Vector half = hvx_vec_splat_f32(0.5f);
|
||||
const HVX_Vector one = hvx_vec_splat_f32(1.0f);
|
||||
const HVX_Vector max_x = hvx_vec_splat_f32(10.0f);
|
||||
const HVX_Vector min_x = hvx_vec_splat_f32(-10.0f);
|
||||
const HVX_VectorPred neg_large = Q6_Q_vcmp_gt_VsfVsf(min_x, x);
|
||||
const HVX_VectorPred pos_large = Q6_Q_vcmp_gt_VsfVsf(x, max_x);
|
||||
HVX_Vector x_calc = Q6_V_vmux_QVV(neg_large, min_x, x);
|
||||
x_calc = Q6_V_vmux_QVV(pos_large, max_x, x_calc);
|
||||
const HVX_Vector erf = hvx_vec_erf_f32(hvx_vec_mul_f32_f32(x_calc, scale));
|
||||
|
||||
HVX_Vector result = hvx_vec_mul_f32_f32(hvx_vec_mul_f32_f32(half, x_calc), hvx_vec_add_f32_f32(one, erf));
|
||||
|
||||
result = Q6_V_vmux_QVV(neg_large, hvx_vec_splat_f32(0.0f), result);
|
||||
result = Q6_V_vmux_QVV(pos_large, x, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void hvx_gelu_erf_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t elem_size = sizeof(float);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = n / epv;
|
||||
const uint32_t nloe = n % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
vdst[i] = hvx_vec_gelu_erf_f32(vsrc[i]);
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_Vector v = hvx_vec_gelu_erf_f32(vsrc[i]);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HVX_ERF_H */
|
||||
@@ -0,0 +1,255 @@
|
||||
#ifndef HVX_EXP_H
|
||||
#define HVX_EXP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-floor.h"
|
||||
|
||||
#define EXP_COEFF_5 (0x39506967) // 0.000198757 = 1/(7!)
|
||||
#define EXP_COEFF_4 (0x3AB743CE) // 0.0013982 = 1/(6!)
|
||||
#define EXP_COEFF_3 (0x3C088908) // 0.00833345 = 1/(5!)
|
||||
#define EXP_COEFF_2 (0x3D2AA9C1) // 0.416658 = 1/(4!)
|
||||
#define EXP_COEFF_1 (0x3E2AAAAA) // 0.16666667 = 1/(3!)
|
||||
#define EXP_COEFF_0 (0x3F000000) // 0.5 = 1/(2!)
|
||||
#define EXP_LOGN2 (0x3F317218) // ln(2) = 0.6931471805
|
||||
#define EXP_LOG2E (0x3FB8AA3B) // log2(e) = 1/ln(2) = 1.4426950408
|
||||
#define EXP_LOG2E_F 1.44269504f
|
||||
#define EXP_ONE (0x3f800000) // 1.0
|
||||
#define EXP_RANGE_R (0x42B17218) // ln(FLT_MAX) approx = 88.7228
|
||||
#define EXP_RANGE_L (0xC2B00000) // -88.0 (approx log(FLT_MIN))
|
||||
|
||||
static inline HVX_Vector hvx_vec_exp_f32(HVX_Vector in_vec) {
|
||||
HVX_Vector z_qf32_v;
|
||||
HVX_Vector x_v;
|
||||
HVX_Vector x_qf32_v;
|
||||
HVX_Vector y_v;
|
||||
HVX_Vector k_v;
|
||||
HVX_Vector f_v;
|
||||
HVX_Vector epsilon_v;
|
||||
HVX_Vector log2e = Q6_V_vsplat_R(EXP_LOG2E);
|
||||
HVX_Vector logn2 = Q6_V_vsplat_R(EXP_LOGN2);
|
||||
HVX_Vector E_const;
|
||||
HVX_Vector zero_v = Q6_V_vzero();
|
||||
|
||||
// exp(x) is approximated as follows:
|
||||
// f = floor(x/ln(2)) = floor(x*log2(e))
|
||||
// epsilon = x - f*ln(2)
|
||||
// exp(x) = exp(epsilon+f*ln(2))
|
||||
// = exp(epsilon)*exp(f*ln(2))
|
||||
// = exp(epsilon)*2^f
|
||||
//
|
||||
// Since epsilon is close to zero, it can be approximated with its Taylor series:
|
||||
// exp(x) ~= 1+x+x^2/2!+x^3/3!+...+x^n/n!+...
|
||||
// Preserving the first eight elements, we get:
|
||||
// exp(x) ~= 1+x+e0*x^2+e1*x^3+e2*x^4+e3*x^5+e4*x^6+e5*x^7
|
||||
// = 1+x+(E0+(E1+(E2+(E3+(E4+E5*x)*x)*x)*x)*x)*x^2
|
||||
|
||||
HVX_Vector temp_v = in_vec;
|
||||
|
||||
// Clamp inputs to (-88.0, 88.0) to avoid overflow/underflow
|
||||
HVX_VectorPred pred_cap_right = Q6_Q_vcmp_gt_VsfVsf(in_vec, Q6_V_vsplat_R(EXP_RANGE_R));
|
||||
HVX_VectorPred pred_cap_left = Q6_Q_vcmp_gt_VsfVsf(Q6_V_vsplat_R(EXP_RANGE_L), in_vec);
|
||||
|
||||
in_vec = Q6_V_vmux_QVV(pred_cap_right, Q6_V_vsplat_R(EXP_RANGE_R), temp_v);
|
||||
in_vec = Q6_V_vmux_QVV(pred_cap_left, Q6_V_vsplat_R(EXP_RANGE_L), in_vec);
|
||||
|
||||
epsilon_v = Q6_Vqf32_vmpy_VsfVsf(log2e, in_vec);
|
||||
epsilon_v = Q6_Vsf_equals_Vqf32(epsilon_v);
|
||||
|
||||
// f_v is the floating point result and k_v is the integer result
|
||||
f_v = hvx_vec_floor_f32(epsilon_v);
|
||||
k_v = hvx_vec_truncate_f32(f_v);
|
||||
|
||||
x_qf32_v = Q6_Vqf32_vadd_VsfVsf(in_vec, zero_v);
|
||||
|
||||
// x = x - f_v * logn2;
|
||||
epsilon_v = Q6_Vqf32_vmpy_VsfVsf(f_v, logn2);
|
||||
x_qf32_v = Q6_Vqf32_vsub_Vqf32Vqf32(x_qf32_v, epsilon_v);
|
||||
// normalize before every QFloat's vmpy
|
||||
x_qf32_v = Q6_Vqf32_vadd_Vqf32Vsf(x_qf32_v, zero_v);
|
||||
|
||||
x_v = Q6_Vsf_equals_Vqf32(x_qf32_v);
|
||||
|
||||
// z = x * x;
|
||||
z_qf32_v = Q6_Vqf32_vmpy_Vqf32Vqf32(x_qf32_v, x_qf32_v);
|
||||
z_qf32_v = Q6_Vqf32_vadd_Vqf32Vsf(z_qf32_v, zero_v);
|
||||
|
||||
// y = E4 + E5 * x;
|
||||
E_const = Q6_V_vsplat_R(EXP_COEFF_5);
|
||||
y_v = Q6_Vqf32_vmpy_VsfVsf(E_const, x_v);
|
||||
E_const = Q6_V_vsplat_R(EXP_COEFF_4);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, E_const);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, zero_v);
|
||||
|
||||
// y = E3 + y * x;
|
||||
E_const = Q6_V_vsplat_R(EXP_COEFF_3);
|
||||
y_v = Q6_Vqf32_vmpy_Vqf32Vqf32(y_v, x_qf32_v);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, E_const);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, zero_v);
|
||||
|
||||
// y = E2 + y * x;
|
||||
E_const = Q6_V_vsplat_R(EXP_COEFF_2);
|
||||
y_v = Q6_Vqf32_vmpy_Vqf32Vqf32(y_v, x_qf32_v);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, E_const);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, zero_v);
|
||||
|
||||
// y = E1 + y * x;
|
||||
E_const = Q6_V_vsplat_R(EXP_COEFF_1);
|
||||
y_v = Q6_Vqf32_vmpy_Vqf32Vqf32(y_v, x_qf32_v);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, E_const);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, zero_v);
|
||||
|
||||
// y = E0 + y * x;
|
||||
E_const = Q6_V_vsplat_R(EXP_COEFF_0);
|
||||
y_v = Q6_Vqf32_vmpy_Vqf32Vqf32(y_v, x_qf32_v);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, E_const);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, zero_v);
|
||||
|
||||
// y = x + y * z;
|
||||
y_v = Q6_Vqf32_vmpy_Vqf32Vqf32(y_v, z_qf32_v);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vqf32(y_v, x_qf32_v);
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, zero_v);
|
||||
|
||||
// y = y + 1.0;
|
||||
y_v = Q6_Vqf32_vadd_Vqf32Vsf(y_v, Q6_V_vsplat_R(EXP_ONE));
|
||||
|
||||
// insert exponents
|
||||
// y = ldexpf(y, k);
|
||||
// y_v += k_v; // qf32
|
||||
// modify exponent
|
||||
|
||||
y_v = Q6_Vsf_equals_Vqf32(y_v);
|
||||
|
||||
// add k_v to the exponent of y_v
|
||||
HVX_Vector y_v_exponent = Q6_Vw_vasl_VwR(y_v, 1);
|
||||
|
||||
y_v_exponent = Q6_Vuw_vlsr_VuwR(y_v_exponent, IEEE_VSF_MANTLEN + 1);
|
||||
y_v_exponent = Q6_Vw_vadd_VwVw(k_v, y_v_exponent);
|
||||
|
||||
// exponent cannot be negative; if overflow is detected, result is set to zero
|
||||
HVX_VectorPred qy_v_negative_exponent = Q6_Q_vcmp_gt_VwVw(zero_v, y_v_exponent);
|
||||
|
||||
y_v = Q6_Vw_vaslacc_VwVwR(y_v, k_v, IEEE_VSF_MANTLEN);
|
||||
|
||||
y_v = Q6_V_vmux_QVV(qy_v_negative_exponent, zero_v, y_v);
|
||||
|
||||
return y_v;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_exp_f32_guard(HVX_Vector in_vec, HVX_Vector max_exp, HVX_Vector inf) {
|
||||
const HVX_VectorPred pred0 = Q6_Q_vcmp_gt_VsfVsf(in_vec, max_exp);
|
||||
|
||||
HVX_Vector out = hvx_vec_exp_f32(in_vec);
|
||||
|
||||
return Q6_V_vmux_QVV(pred0, inf, out);
|
||||
}
|
||||
|
||||
static inline void hvx_exp_f32(uint8_t * restrict dst, const uint8_t * restrict src, const int num_elems, bool negate) {
|
||||
int left_over = num_elems & (VLEN_FP32 - 1);
|
||||
int num_elems_whole = num_elems - left_over;
|
||||
|
||||
int unaligned_addr = 0;
|
||||
int unaligned_loop = 0;
|
||||
if ((0 == hex_is_aligned((void *) src, VLEN)) || (0 == hex_is_aligned((void *) dst, VLEN))) {
|
||||
unaligned_addr = 1;
|
||||
}
|
||||
// assert((0 == unaligned_addr) || (0 == num_elems_whole));
|
||||
if ((1 == unaligned_addr) && (num_elems_whole != 0)) {
|
||||
unaligned_loop = 1;
|
||||
}
|
||||
|
||||
HVX_Vector vec_out = Q6_V_vzero();
|
||||
|
||||
static const float kInf = INFINITY;
|
||||
static const float kMaxExp = 88.7228f;
|
||||
|
||||
const HVX_Vector max_exp = hvx_vec_splat_f32(kMaxExp);
|
||||
const HVX_Vector inf = hvx_vec_splat_f32(kInf);
|
||||
|
||||
if (0 == unaligned_loop) {
|
||||
HVX_Vector * p_vec_in1 = (HVX_Vector *) src;
|
||||
HVX_Vector * p_vec_out = (HVX_Vector *) dst;
|
||||
|
||||
#pragma unroll(2)
|
||||
for (int i = 0; i < num_elems_whole; i += VLEN_FP32) {
|
||||
if (true == negate) {
|
||||
HVX_Vector neg_vec_in = hvx_vec_neg_f32(*p_vec_in1++);
|
||||
*p_vec_out++ = hvx_vec_exp_f32_guard(neg_vec_in, max_exp, inf);
|
||||
} else {
|
||||
*p_vec_out++ = hvx_vec_exp_f32_guard(*p_vec_in1++, max_exp, inf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#pragma unroll(2)
|
||||
for (int i = 0; i < num_elems_whole; i += VLEN_FP32) {
|
||||
HVX_Vector in = *(HVX_UVector *) (src + i * SIZEOF_FP32);
|
||||
|
||||
if (true == negate) {
|
||||
HVX_Vector neg_vec_in = hvx_vec_neg_f32(in);
|
||||
*(HVX_UVector *) (dst + i * SIZEOF_FP32) = hvx_vec_exp_f32_guard(neg_vec_in, max_exp, inf);
|
||||
} else {
|
||||
*(HVX_UVector *) (dst + i * SIZEOF_FP32) = hvx_vec_exp_f32_guard(in, max_exp, inf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (left_over > 0) {
|
||||
const float * srcf = (float *) src + num_elems_whole;
|
||||
float * dstf = (float *) dst + num_elems_whole;
|
||||
|
||||
HVX_Vector in = *(HVX_UVector *) srcf;
|
||||
|
||||
if (true == negate) {
|
||||
HVX_Vector neg_vec_in = hvx_vec_neg_f32(in);
|
||||
|
||||
vec_out = hvx_vec_exp_f32_guard(neg_vec_in, max_exp, inf);
|
||||
} else {
|
||||
vec_out = hvx_vec_exp_f32_guard(in, max_exp, inf);
|
||||
}
|
||||
|
||||
hvx_vec_store_u((void *) dstf, left_over * SIZEOF_FP32, vec_out);
|
||||
}
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_exp2_f16(HVX_Vector x_v) {
|
||||
const HVX_Vector zero_v = Q6_V_vzero();
|
||||
const HVX_Vector half_hf_v = Q6_Vh_vsplat_R(0x3800); // fp16 0.5
|
||||
|
||||
// Clamp input to prevent integer underflow in FP16-to-INT16 conversion
|
||||
const HVX_Vector v_clamp_min = hvx_vec_splat_f16(-24.0f);
|
||||
x_v = Q6_Vhf_vmax_VhfVhf(v_clamp_min, x_v);
|
||||
|
||||
// k = round_toward_neg_inf(x); f = (float)k; frac = x - f
|
||||
HVX_Vector x_minus_half = Q6_Vhf_equals_Vqf16(Q6_Vqf16_vsub_VhfVhf(x_v, half_hf_v));
|
||||
HVX_Vector k_v = Q6_Vh_equals_Vhf(x_minus_half); // truncate to int16
|
||||
HVX_Vector f_v = Q6_Vhf_equals_Vh(k_v); // back to fp16
|
||||
|
||||
HVX_Vector x_qf16 = Q6_Vqf16_vsub_VhfVhf(x_v, f_v); // fractional part in qf16
|
||||
|
||||
// Horner: y = ((((E5*x + E4)*x + E3)*x + E2)*x + E1)*x + E0
|
||||
HVX_Vector y = Q6_Vqf16_vmpy_Vqf16Vqf16(Q6_Vh_vsplat_R(0x5082), x_qf16); // E5*x
|
||||
y = Q6_Vqf16_vadd_Vqf16Vhf(y, Q6_Vh_vsplat_R(0x157d)); // + E4
|
||||
y = Q6_Vqf16_vmpy_Vqf16Vqf16(y, x_qf16);
|
||||
y = Q6_Vqf16_vadd_Vqf16Vhf(y, Q6_Vh_vsplat_R(0x20ed)); // + E3
|
||||
y = Q6_Vqf16_vmpy_Vqf16Vqf16(y, x_qf16);
|
||||
y = Q6_Vqf16_vadd_Vqf16Vhf(y, Q6_Vh_vsplat_R(0x2b1b)); // + E2
|
||||
y = Q6_Vqf16_vmpy_Vqf16Vqf16(y, x_qf16);
|
||||
y = Q6_Vqf16_vadd_Vqf16Vhf(y, Q6_Vh_vsplat_R(0x33b0)); // + E1
|
||||
y = Q6_Vqf16_vmpy_Vqf16Vqf16(y, x_qf16);
|
||||
y = Q6_Vqf16_vadd_Vqf16Vhf(y, Q6_Vh_vsplat_R(0x398c)); // + E0
|
||||
y = Q6_Vqf16_vmpy_Vqf16Vqf16(y, x_qf16); // y = y * x
|
||||
y = Q6_Vqf16_vadd_Vqf16Vhf(y, Q6_Vh_vsplat_R(0x3c00)); // + 1.0
|
||||
|
||||
// Combine polynomial (mantissa) with integer part (exponent): result = y * 2^k
|
||||
y = Q6_Vhf_equals_Vqf16(y);
|
||||
HVX_Vector y_exp = Q6_Vuh_vlsr_VuhR(Q6_Vh_vasl_VhR(y, 1), 11);
|
||||
y_exp = Q6_Vh_vadd_VhVh(k_v, y_exp);
|
||||
HVX_VectorPred q_underflow = Q6_Q_vcmp_gt_VhVh(zero_v, y_exp);
|
||||
y = Q6_Vh_vaslacc_VhVhR(y, k_v, 10);
|
||||
return Q6_V_vmux_QVV(q_underflow, zero_v, y);
|
||||
}
|
||||
|
||||
#endif /* HVX_EXP_H */
|
||||
@@ -0,0 +1,303 @@
|
||||
#ifndef HVX_FA_KERNELS_H
|
||||
#define HVX_FA_KERNELS_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include "hvx-utils.h"
|
||||
|
||||
// Little inner kernels for HVX
|
||||
|
||||
#if __HVX_ARCH__ < 79
|
||||
#define HVX_OP_ADD_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(a, b))
|
||||
#define HVX_OP_SUB_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(a, b))
|
||||
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(a, b))
|
||||
#else
|
||||
#define HVX_OP_ADD_F32(a, b) Q6_Vsf_vadd_VsfVsf(a, b)
|
||||
#define HVX_OP_SUB_F32(a, b) Q6_Vsf_vsub_VsfVsf(a, b)
|
||||
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_vmpy_VsfVsf(a, b)
|
||||
#endif
|
||||
|
||||
// This is a bit of a hack because the compiler is struggling to properly inline
|
||||
// the default hvx_vec_f32_to_f16 with output into the local array.
|
||||
static __attribute__((unused)) __attribute__((noinline)) void hvx_vec_f32_to_f16_a(void *ptr, HVX_Vector v0, HVX_Vector v1)
|
||||
{
|
||||
*(HVX_Vector *) ptr = hvx_vec_f32_to_f16(v0, v1);
|
||||
}
|
||||
|
||||
// Dot product of two F16 vectors, accumulating to float
|
||||
static inline void hvx_dot_f16_f16_aa(float * restrict r, const void * restrict x, const void * restrict y, unsigned int n, float s) {
|
||||
const HVX_Vector * restrict vx = (const HVX_Vector * restrict) x; // fp16
|
||||
const HVX_Vector * restrict vy = (const HVX_Vector * restrict) y; // fp16
|
||||
|
||||
uint32_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
||||
uint32_t nloe = n % VLEN_FP16; // leftover elements
|
||||
|
||||
HVX_VectorPair rsum_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(4)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
rsum_p = hvx_vec_mpyacc_f32_f16(rsum_p, vx[i], vy[i]);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 2);
|
||||
HVX_Vector y_hf = Q6_V_vand_QV(bmask, vy[i]);
|
||||
HVX_Vector x_hf = Q6_V_vand_QV(bmask, vx[i]);
|
||||
|
||||
rsum_p = hvx_vec_mpyacc_f32_f16(rsum_p, x_hf, y_hf);
|
||||
}
|
||||
|
||||
HVX_Vector rsum = HVX_OP_ADD_F32(Q6_V_lo_W(rsum_p), Q6_V_hi_W(rsum_p));
|
||||
rsum = HVX_OP_MUL_F32(hvx_vec_splat_f32(s), hvx_vec_reduce_sum_f32(rsum));
|
||||
hvx_vec_store_u(r, 4, rsum);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_dot_f16_f16_aa_rx4(const void * restrict y,
|
||||
const uint8_t * restrict x,
|
||||
const size_t stride_x,
|
||||
const size_t nvec,
|
||||
const size_t nloe) {
|
||||
const HVX_Vector * restrict vx0 = (const HVX_Vector * restrict) x; // fp16
|
||||
const HVX_Vector * restrict vx1 = (const HVX_Vector * restrict) (x + stride_x); // fp16
|
||||
const HVX_Vector * restrict vx2 = (const HVX_Vector * restrict) (x + stride_x * 2); // fp16
|
||||
const HVX_Vector * restrict vx3 = (const HVX_Vector * restrict) (x + stride_x * 3); // fp16
|
||||
const HVX_Vector * restrict vy = (const HVX_Vector * restrict) y; // fp16
|
||||
|
||||
HVX_VectorPair rsum0_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
||||
HVX_VectorPair rsum1_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
||||
HVX_VectorPair rsum2_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
||||
HVX_VectorPair rsum3_p = Q6_W_vcombine_VV(Q6_V_vsplat_R(0), Q6_V_vsplat_R(0));
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
for (i = 0; i < nvec; i++) {
|
||||
HVX_Vector y_hf = vy[i];
|
||||
HVX_Vector x0_hf = vx0[i];
|
||||
HVX_Vector x1_hf = vx1[i];
|
||||
HVX_Vector x2_hf = vx2[i];
|
||||
HVX_Vector x3_hf = vx3[i];
|
||||
|
||||
rsum0_p = hvx_vec_mpyacc_f32_f16(rsum0_p, x0_hf, y_hf);
|
||||
rsum1_p = hvx_vec_mpyacc_f32_f16(rsum1_p, x1_hf, y_hf);
|
||||
rsum2_p = hvx_vec_mpyacc_f32_f16(rsum2_p, x2_hf, y_hf);
|
||||
rsum3_p = hvx_vec_mpyacc_f32_f16(rsum3_p, x3_hf, y_hf);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
// Load x (fp16) and zero-out unused elements
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 2);
|
||||
HVX_Vector y_hf = Q6_V_vand_QV(bmask, vy[i]);
|
||||
HVX_Vector x0_hf = Q6_V_vand_QV(bmask, vx0[i]);
|
||||
HVX_Vector x1_hf = Q6_V_vand_QV(bmask, vx1[i]);
|
||||
HVX_Vector x2_hf = Q6_V_vand_QV(bmask, vx2[i]);
|
||||
HVX_Vector x3_hf = Q6_V_vand_QV(bmask, vx3[i]);
|
||||
|
||||
rsum0_p = hvx_vec_mpyacc_f32_f16(rsum0_p, x0_hf, y_hf);
|
||||
rsum1_p = hvx_vec_mpyacc_f32_f16(rsum1_p, x1_hf, y_hf);
|
||||
rsum2_p = hvx_vec_mpyacc_f32_f16(rsum2_p, x2_hf, y_hf);
|
||||
rsum3_p = hvx_vec_mpyacc_f32_f16(rsum3_p, x3_hf, y_hf);
|
||||
}
|
||||
|
||||
HVX_Vector rsum0 = HVX_OP_ADD_F32(Q6_V_lo_W(rsum0_p), Q6_V_hi_W(rsum0_p));
|
||||
HVX_Vector rsum1 = HVX_OP_ADD_F32(Q6_V_lo_W(rsum1_p), Q6_V_hi_W(rsum1_p));
|
||||
HVX_Vector rsum2 = HVX_OP_ADD_F32(Q6_V_lo_W(rsum2_p), Q6_V_hi_W(rsum2_p));
|
||||
HVX_Vector rsum3 = HVX_OP_ADD_F32(Q6_V_lo_W(rsum3_p), Q6_V_hi_W(rsum3_p));
|
||||
|
||||
HVX_Vector_x4 rsum0123 = { .v = { rsum0, rsum1, rsum2, rsum3 } };
|
||||
return hvx_vec_reduce_sum_f32x4(rsum0123);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_dot_f16_f16_aa_rx32(const void * restrict y,
|
||||
const uint8_t * restrict x,
|
||||
const size_t stride_x,
|
||||
const size_t n,
|
||||
float s) {
|
||||
|
||||
const size_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
||||
const size_t nloe = n % VLEN_FP16; // leftover elements
|
||||
|
||||
HVX_Vector sums = Q6_V_vzero();
|
||||
const size_t stride_x_4 = stride_x * 4;
|
||||
for (uint32_t j = 0; j < VLEN_FP32; j += 4) {
|
||||
HVX_Vector sums_x4 = hvx_dot_f16_f16_aa_rx4(y, x, stride_x, nvec, nloe);
|
||||
HVX_VectorPred pred = Q6_Q_vsetq_R(j * SIZEOF_FP32);
|
||||
sums = Q6_V_vmux_QVV(pred, sums, sums_x4);
|
||||
x += stride_x_4;
|
||||
}
|
||||
|
||||
return HVX_OP_MUL_F32(hvx_vec_splat_f32(s), sums);
|
||||
}
|
||||
|
||||
// MAD: y (F32) += x (F16) * s (F16)
|
||||
static inline void hvx_mad_f32_f16_aa(float * restrict y, const void * restrict x, const __fp16 * restrict s, uint32_t n) {
|
||||
const HVX_Vector * restrict vx0 = (const HVX_Vector *) x;
|
||||
|
||||
HVX_VectorPair * restrict vy_p = (HVX_VectorPair *) y;
|
||||
HVX_Vector * restrict vy = (HVX_Vector *) y;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
||||
uint32_t nloe = n % VLEN_FP16; // leftover elements
|
||||
|
||||
HVX_Vector S0 = hvx_vec_splat_f16(*s);
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; ++i) {
|
||||
vy_p[i] = hvx_vec_mpyacc_f32_f16(vy_p[i], Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPair xy_p = vy_p[i];
|
||||
xy_p = hvx_vec_mpyacc_f32_f16(xy_p, Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
||||
|
||||
HVX_Vector xy = Q6_V_lo_W(xy_p);
|
||||
i = 2 * i; // index for vy
|
||||
|
||||
if (nloe >= VLEN_FP32) {
|
||||
vy[i] = xy;
|
||||
nloe -= VLEN_FP32; ++i; xy = Q6_V_hi_W(xy_p);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
hvx_vec_store_a(&vy[i], nloe * 4, xy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MAD: y (F32) += x0 (F16) * s0 (F16) + x1 (F16) * s1 (F16)
|
||||
static inline void hvx_mad_f32_f16_aa_rx2(float * restrict y, const void * restrict x0, const void * restrict x1,
|
||||
const __fp16 * restrict s0, const __fp16 * restrict s1, uint32_t n) {
|
||||
const HVX_Vector * restrict vx0 = (const HVX_Vector *) x0;
|
||||
const HVX_Vector * restrict vx1 = (const HVX_Vector *) x1;
|
||||
|
||||
HVX_VectorPair * restrict vy_p = (HVX_VectorPair *) y;
|
||||
HVX_Vector * restrict vy = (HVX_Vector *) y;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
||||
uint32_t nloe = n % VLEN_FP16; // leftover elements
|
||||
|
||||
HVX_Vector S0 = hvx_vec_splat_f16(*s0);
|
||||
HVX_Vector S1 = hvx_vec_splat_f16(*s1);
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; ++i) {
|
||||
vy_p[i] = hvx_vec_mpyacc_f32_f16(vy_p[i], Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
||||
vy_p[i] = hvx_vec_mpyacc_f32_f16(vy_p[i], Q6_Vh_vshuff_Vh(vx1[i]), S1);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPair xy_p = vy_p[i];
|
||||
xy_p = hvx_vec_mpyacc_f32_f16(xy_p, Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
||||
xy_p = hvx_vec_mpyacc_f32_f16(xy_p, Q6_Vh_vshuff_Vh(vx1[i]), S1);
|
||||
|
||||
HVX_Vector xy = Q6_V_lo_W(xy_p);
|
||||
i = 2 * i; // index for vy
|
||||
|
||||
if (nloe >= VLEN_FP32) {
|
||||
vy[i] = xy;
|
||||
nloe -= VLEN_FP32; ++i; xy = Q6_V_hi_W(xy_p);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
hvx_vec_store_a(&vy[i], nloe * 4, xy);
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline void hvx_mad_f32_f16_aa_vec(float * restrict y, const void * restrict x, HVX_Vector S0, uint32_t n) {
|
||||
const HVX_Vector * restrict vx0 = (const HVX_Vector *) x;
|
||||
|
||||
HVX_VectorPair * restrict vy_p = (HVX_VectorPair *) y;
|
||||
HVX_Vector * restrict vy = (HVX_Vector *) y;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
||||
uint32_t nloe = n % VLEN_FP16; // leftover elements
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; ++i) {
|
||||
vy_p[i] = hvx_vec_mpyacc_f32_f16(vy_p[i], Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPair xy_p = vy_p[i];
|
||||
xy_p = hvx_vec_mpyacc_f32_f16(xy_p, Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
||||
|
||||
HVX_Vector xy = Q6_V_lo_W(xy_p);
|
||||
i = 2 * i; // index for vy
|
||||
|
||||
if (nloe >= VLEN_FP32) {
|
||||
vy[i] = xy;
|
||||
nloe -= VLEN_FP32; ++i; xy = Q6_V_hi_W(xy_p);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
hvx_vec_store_a(&vy[i], nloe * 4, xy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_mad_f32_f16_aa_rx2_vec(float * restrict y, const void * restrict x0, const void * restrict x1,
|
||||
HVX_Vector S0, HVX_Vector S1, uint32_t n) {
|
||||
const HVX_Vector * restrict vx0 = (const HVX_Vector *) x0;
|
||||
const HVX_Vector * restrict vx1 = (const HVX_Vector *) x1;
|
||||
|
||||
HVX_VectorPair * restrict vy_p = (HVX_VectorPair *) y;
|
||||
HVX_Vector * restrict vy = (HVX_Vector *) y;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
||||
uint32_t nloe = n % VLEN_FP16; // leftover elements
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; ++i) {
|
||||
vy_p[i] = hvx_vec_mpyacc_f32_f16(vy_p[i], Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
||||
vy_p[i] = hvx_vec_mpyacc_f32_f16(vy_p[i], Q6_Vh_vshuff_Vh(vx1[i]), S1);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPair xy_p = vy_p[i];
|
||||
xy_p = hvx_vec_mpyacc_f32_f16(xy_p, Q6_Vh_vshuff_Vh(vx0[i]), S0);
|
||||
xy_p = hvx_vec_mpyacc_f32_f16(xy_p, Q6_Vh_vshuff_Vh(vx1[i]), S1);
|
||||
|
||||
HVX_Vector xy = Q6_V_lo_W(xy_p);
|
||||
i = 2 * i; // index for vy
|
||||
|
||||
if (nloe >= VLEN_FP32) {
|
||||
vy[i] = xy;
|
||||
nloe -= VLEN_FP32; ++i; xy = Q6_V_hi_W(xy_p);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
hvx_vec_store_a(&vy[i], nloe * 4, xy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_scale_vec_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, const uint32_t n, HVX_Vector vs) {
|
||||
assert((size_t) dst % 128 == 0);
|
||||
assert((size_t) src % 128 == 0);
|
||||
|
||||
const HVX_Vector * restrict vsrc = (const HVX_Vector * restrict) src;
|
||||
HVX_Vector * restrict vdst = (HVX_Vector * restrict) dst;
|
||||
|
||||
const uint32_t nvec = n / VLEN_FP32;
|
||||
const uint32_t nloe = n % VLEN_FP32;
|
||||
|
||||
uint32_t i = 0;
|
||||
#pragma unroll(4)
|
||||
for (; i < nvec; ++i) {
|
||||
vdst[i] = HVX_OP_MUL_F32(vsrc[i], vs);
|
||||
}
|
||||
if (nloe) {
|
||||
hvx_vec_store_a(&vdst[i], nloe * sizeof(float), HVX_OP_MUL_F32(vsrc[i], vs));
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HVX_FA_KERNELS_H */
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef HVX_FLASH_ATTN_H
|
||||
#define HVX_FLASH_ATTN_H
|
||||
|
||||
#include <math.h>
|
||||
#include "hvx-utils.h"
|
||||
|
||||
// Scalar helper to compute a single ALiBi slope.
|
||||
static inline float alibi_slope(uint32_t h, uint32_t n_head_log2, float m0, float m1) {
|
||||
return (h < n_head_log2) ? powf(m0, h + 1) : powf(m1, 2 * (h - n_head_log2) + 1);
|
||||
}
|
||||
|
||||
// Vectorized helper to compute 32 ALiBi slopes starting from (kv_head * G).
|
||||
static inline HVX_Vector hvx_alibi_slopes(
|
||||
uint32_t kv_head,
|
||||
uint32_t G,
|
||||
uint32_t n_head_log2,
|
||||
float m0,
|
||||
float m1
|
||||
) {
|
||||
static const float ramp_32[32] __attribute__((aligned(128))) = {
|
||||
0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f,
|
||||
8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f,
|
||||
16.0f, 17.0f, 18.0f, 19.0f, 20.0f, 21.0f, 22.0f, 23.0f,
|
||||
24.0f, 25.0f, 26.0f, 27.0f, 28.0f, 29.0f, 30.0f, 31.0f
|
||||
};
|
||||
HVX_Vector v_ramp = hvx_vmem(ramp_32);
|
||||
HVX_Vector v_h_base = hvx_vec_splat_f32((float)(kv_head * G));
|
||||
HVX_Vector v_h = hvx_vec_add_f32_f32(v_h_base, v_ramp);
|
||||
|
||||
// Compute exponent_m0: h + 1
|
||||
HVX_Vector v_exp_m0 = hvx_vec_add_f32_f32(v_h, hvx_vec_splat_f32(1.0f));
|
||||
|
||||
// Compute exponent_m1: 2 * (h - n_head_log2) + 1
|
||||
HVX_Vector v_n_head_log2 = hvx_vec_splat_f32((float)n_head_log2);
|
||||
HVX_Vector v_h_minus = hvx_vec_sub_f32_f32(v_h, v_n_head_log2);
|
||||
HVX_Vector v_exp_m1 = hvx_vec_add_f32_f32(hvx_vec_mul_f32_f32(hvx_vec_splat_f32(2.0f), v_h_minus), hvx_vec_splat_f32(1.0f));
|
||||
|
||||
// Compute powers
|
||||
HVX_Vector v_pow_m0 = hvx_vec_pow_const_base_f32(m0, v_exp_m0);
|
||||
HVX_Vector v_pow_m1 = hvx_vec_pow_const_base_f32(m1, v_exp_m1);
|
||||
|
||||
// Select based on h < n_head_log2
|
||||
HVX_VectorPred p_cond = Q6_Q_vcmp_gt_VsfVsf(v_n_head_log2, v_h); // v_n_head_log2 > v_h <=> h < n_head_log2
|
||||
return Q6_V_vmux_QVV(p_cond, v_pow_m0, v_pow_m1);
|
||||
}
|
||||
|
||||
#endif /* HVX_FLASH_ATTN_H */
|
||||
@@ -0,0 +1,108 @@
|
||||
#ifndef HVX_FLOOR_H
|
||||
#define HVX_FLOOR_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hvx-base.h"
|
||||
|
||||
#define IEEE_VSF_EXPLEN (8)
|
||||
#define IEEE_VSF_EXPBIAS (127)
|
||||
#define IEEE_VSF_EXPMASK (0xFF)
|
||||
#define IEEE_VSF_MANTLEN (23)
|
||||
#define IEEE_VSF_MANTMASK (0x7FFFFF)
|
||||
#define IEEE_VSF_MIMPMASK (0x800000)
|
||||
|
||||
static inline HVX_Vector hvx_vec_truncate_f32(HVX_Vector in_vec) {
|
||||
HVX_Vector mask_mant_v = Q6_V_vsplat_R(IEEE_VSF_MANTMASK);
|
||||
HVX_Vector mask_impl_v = Q6_V_vsplat_R(IEEE_VSF_MIMPMASK);
|
||||
HVX_Vector const_zero_v = Q6_V_vzero();
|
||||
|
||||
HVX_VectorPred q_negative = Q6_Q_vcmp_gt_VwVw(const_zero_v, in_vec);
|
||||
|
||||
HVX_Vector expval_v = in_vec >> IEEE_VSF_MANTLEN;
|
||||
expval_v &= IEEE_VSF_EXPMASK;
|
||||
expval_v -= IEEE_VSF_EXPBIAS;
|
||||
|
||||
// negative exp == fractional value
|
||||
HVX_VectorPred q_negexp = Q6_Q_vcmp_gt_VwVw(const_zero_v, expval_v);
|
||||
|
||||
HVX_Vector rshift_v = IEEE_VSF_MANTLEN - expval_v; // fractional bits - exp shift
|
||||
|
||||
HVX_Vector mant_v = in_vec & mask_mant_v; // obtain mantissa
|
||||
HVX_Vector vout = Q6_Vw_vadd_VwVw(mant_v, mask_impl_v); // add implicit 1.0
|
||||
|
||||
vout = Q6_Vw_vasr_VwVw(vout, rshift_v); // shift to obtain truncated integer
|
||||
vout = Q6_V_vmux_QVV(q_negexp, const_zero_v, vout); // expval<0 -> 0
|
||||
|
||||
HVX_Vector neg_vout = -vout;
|
||||
|
||||
vout = Q6_V_vmux_QVV(q_negative, neg_vout, vout); // handle negatives
|
||||
|
||||
return (vout);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_floor_f32(HVX_Vector in_vec) {
|
||||
HVX_Vector mask_mant_v = Q6_V_vsplat_R(IEEE_VSF_MANTMASK);
|
||||
HVX_Vector mask_impl_v = Q6_V_vsplat_R(IEEE_VSF_MIMPMASK);
|
||||
HVX_Vector const_mnlen_v = Q6_V_vsplat_R(IEEE_VSF_MANTLEN);
|
||||
HVX_Vector const_zero_v = Q6_V_vzero();
|
||||
HVX_Vector const_negone_v = Q6_V_vsplat_R(0xbf800000); // -1 IEEE vsf
|
||||
|
||||
HVX_VectorPred q_negative = Q6_Q_vcmp_gt_VwVw(const_zero_v, in_vec);
|
||||
|
||||
HVX_Vector expval_v = in_vec >> IEEE_VSF_MANTLEN;
|
||||
expval_v &= IEEE_VSF_EXPMASK;
|
||||
expval_v -= IEEE_VSF_EXPBIAS;
|
||||
|
||||
HVX_VectorPred q_negexp = Q6_Q_vcmp_gt_VwVw(const_zero_v, expval_v);
|
||||
HVX_VectorPred q_expltmn = Q6_Q_vcmp_gt_VwVw(const_mnlen_v, expval_v);
|
||||
HVX_VectorPred q_negexp_pos = Q6_Q_vcmp_gtand_QVwVw(q_negexp, in_vec, const_zero_v);
|
||||
HVX_VectorPred q_negexp_neg = Q6_Q_vcmp_gtand_QVwVw(q_negexp, const_zero_v, in_vec);
|
||||
|
||||
// if expval < 0 (q_negexp) // <0, floor is 0
|
||||
// if vin > 0
|
||||
// floor = 0
|
||||
// if vin < 0
|
||||
// floor = -1
|
||||
// if expval < mant_len (q_expltmn) // >0, but fraction may exist
|
||||
// get sign (q_negative)
|
||||
// mask >> expval // fraction bits to mask off
|
||||
// vout = ~(mask) // apply mask to remove fraction
|
||||
// if (qneg) // negative floor is one less (more, sign bit for neg)
|
||||
// vout += ((impl_mask) >> expval)
|
||||
// if (mask && vin)
|
||||
// vout = vin
|
||||
// else // already an integer
|
||||
// ; // no change
|
||||
|
||||
// compute floor
|
||||
mask_mant_v >>= expval_v;
|
||||
HVX_Vector neg_addin_v = mask_impl_v >> expval_v;
|
||||
HVX_Vector vout_neg_addin = Q6_Vw_vadd_VwVw(in_vec, neg_addin_v);
|
||||
HVX_Vector vout = Q6_V_vmux_QVV(q_negative, vout_neg_addin, in_vec);
|
||||
|
||||
HVX_Vector mask_chk_v = Q6_V_vand_VV(in_vec, mask_mant_v); // chk if bits set
|
||||
HVX_VectorPred q_integral = Q6_Q_vcmp_eq_VwVw(const_zero_v, mask_chk_v);
|
||||
|
||||
HVX_Vector not_mask_v = Q6_V_vnot_V(mask_mant_v); // frac bits to clear
|
||||
HVX_Vector vfrfloor_v = Q6_V_vand_VV(vout, not_mask_v); // clear frac bits
|
||||
|
||||
vout = in_vec;
|
||||
vout = Q6_V_vmux_QVV(q_expltmn, vfrfloor_v, vout); // expval<mant
|
||||
vout = Q6_V_vmux_QVV(q_integral, in_vec, vout); // integral values
|
||||
vout = Q6_V_vmux_QVV(q_negexp_pos, const_zero_v, vout); // expval<0 x>0 -> 0
|
||||
vout = Q6_V_vmux_QVV(q_negexp_neg, const_negone_v, vout); // expval<0 x<0 -> -1
|
||||
|
||||
return vout;
|
||||
}
|
||||
|
||||
// ceil(x) = -floor(-x); keep +-0 as is since floor(-0) returns -1
|
||||
static inline HVX_Vector hvx_vec_ceil_f32(HVX_Vector in_vec) {
|
||||
HVX_Vector vout = hvx_vec_neg_f32(hvx_vec_floor_f32(hvx_vec_neg_f32(in_vec)));
|
||||
HVX_Vector vabs = Q6_V_vand_VV(in_vec, Q6_V_vsplat_R(0x7fffffff));
|
||||
HVX_VectorPred q_zero = Q6_Q_vcmp_eq_VwVw(vabs, Q6_V_vzero());
|
||||
return Q6_V_vmux_QVV(q_zero, in_vec, vout);
|
||||
}
|
||||
|
||||
#endif /* HVX_FLOOR_H */
|
||||
@@ -0,0 +1,210 @@
|
||||
#ifndef HVX_INVERSE_H
|
||||
#define HVX_INVERSE_H
|
||||
|
||||
#include <HAP_farf.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hvx-base.h"
|
||||
|
||||
// ====================================================
|
||||
// FUNCTION: 1/(x+1) y(0) = 1, y(0.5) = 0.6667, y(1) = 0.5
|
||||
// Order:3; continuity: True; Ends forced: True
|
||||
// Mode: unsigned; Result fractional bits: 14
|
||||
// Peak Error: 1.1295e-04 Rms Error: 2.8410e-05 Mean Error: 1.1370e-05
|
||||
// 32769 -32706 31252 -10589
|
||||
// 32590 -30635 22793 -4493
|
||||
// 32066 -27505 16481 -2348
|
||||
// 31205 -24054 11849 -1306
|
||||
|
||||
static inline HVX_Vector hvx_vec_recip_xp1_O3_unsigned(HVX_Vector vx) {
|
||||
// input is 0..0xffff representing 0.0 .. 1.0
|
||||
HVX_Vector p;
|
||||
p = Q6_Vh_vlut4_VuhPh(vx, 0xFAE6F6D4EE73D6A3ull);
|
||||
p = Q6_Vh_vmpa_VhVhVuhPuh_sat(p, vx, 0x2E49406159097A14ull);
|
||||
p = Q6_Vh_vmps_VhVhVuhPuh_sat(p, vx, 0x5DF66B7177AB7FC2ull);
|
||||
p = Q6_Vh_vmpa_VhVhVuhPuh_sat(p, vx, 0x79E57D427F4E8001ull);
|
||||
return p; // signed result, 14 fractional bits
|
||||
}
|
||||
|
||||
// Find reciprocal of fp16.
|
||||
// (1) first, convert to fp32, multiplying by 1.0; this is done to
|
||||
// handle denormals. Ignoring sign and zero, result should be at
|
||||
// least 5.9604645e-08 (32-bit code 0x33800000) and at most 131008 (0x47ffe000)
|
||||
// (exponent in range [103,143])
|
||||
// (2) extract the mantissa into 16-bit unsigned; find reciprocal using a fitted poly
|
||||
// (3) put this, along with '253-exp' (exp from (1)) together to make an qf32
|
||||
// (4) convert that to fp16
|
||||
// (5) put sign back in. Also, if the original value (w/o sign) was <0x81, replace
|
||||
// the result with the max value.
|
||||
static inline HVX_Vector hvx_vec_inverse_f16(HVX_Vector vals) {
|
||||
HVX_Vector em_mask = Q6_Vh_vsplat_R(0x7FFF);
|
||||
HVX_Vector avals = Q6_V_vand_VV(vals, em_mask);
|
||||
HVX_VectorPred is_neg = Q6_Q_vcmp_gt_VhVh(avals, vals);
|
||||
// is too small to 1/x ? for 'standard' fp16, this would be 0x101
|
||||
HVX_VectorPred is_small = Q6_Q_vcmp_gt_VhVh(Q6_Vh_vsplat_R(0x101), avals);
|
||||
|
||||
HVX_VectorPair to_qf32 = Q6_Wqf32_vmpy_VhfVhf(avals, Q6_Vh_vsplat_R(0x3C00)); // *1.0
|
||||
HVX_Vector to_f32_0 = Q6_Vsf_equals_Vqf32(Q6_V_lo_W(to_qf32));
|
||||
HVX_Vector to_f32_1 = Q6_Vsf_equals_Vqf32(Q6_V_hi_W(to_qf32));
|
||||
|
||||
// bits 22..13 contain the mantissa now (w/o hidden bit); move to bit 14..5 of a 16-bit vector
|
||||
HVX_Vector mant_u16 = Q6_Vh_vshuffo_VhVh(Q6_Vw_vasl_VwR(to_f32_1, 9), Q6_Vw_vasl_VwR(to_f32_0, 9));
|
||||
// likewise extract the upper 16 from each, containing the exponents in range 103..142
|
||||
HVX_Vector exp_u16 = Q6_Vh_vshuffo_VhVh(to_f32_1, to_f32_0);
|
||||
//Get exponent in IEEE 32-bit representation
|
||||
exp_u16 = Q6_Vuh_vlsr_VuhR(exp_u16, 7);
|
||||
|
||||
// so, mant_u16 contains an unbiased mantissa in upper 10 bits of each u16 lane
|
||||
// We can consider it to be x-1.0, with 16 fractional bits, where 'x' is in range [1.0,2.0)
|
||||
// Use poly to transform to 1/x, with 14 fractional bits
|
||||
//
|
||||
HVX_Vector rm = hvx_vec_recip_xp1_O3_unsigned(mant_u16);
|
||||
|
||||
HVX_Vector vcl0 = Q6_Vuh_vcl0_Vuh(rm); //count leading zeros
|
||||
|
||||
// Get mantissa for 16-bit representation
|
||||
HVX_Vector mant_recip = Q6_V_vand_VV(Q6_Vh_vasr_VhR(Q6_Vh_vasl_VhVh(rm, vcl0), 5), Q6_Vh_vsplat_R(0x03FF));
|
||||
|
||||
//Compute Reciprocal Exponent
|
||||
HVX_Vector exp_recip =
|
||||
Q6_Vh_vsub_VhVh(Q6_Vh_vsub_VhVh(Q6_Vh_vsplat_R(254), exp_u16), Q6_Vh_vsub_VhVh(vcl0, Q6_Vh_vsplat_R(1)));
|
||||
//Convert it for 16-bit representation
|
||||
exp_recip = Q6_Vh_vadd_VhVh_sat(Q6_Vh_vsub_VhVh(exp_recip, Q6_Vh_vsplat_R(127)), Q6_Vh_vsplat_R(15));
|
||||
exp_recip = Q6_Vh_vasl_VhR(exp_recip, 10);
|
||||
|
||||
//Merge exponent and mantissa for reciprocal
|
||||
HVX_Vector recip = Q6_V_vor_VV(exp_recip, mant_recip);
|
||||
// map 'small' inputs to standard largest value 0x7bff
|
||||
recip = Q6_V_vmux_QVV(is_small, Q6_Vh_vsplat_R(0x7bff), recip);
|
||||
// add sign back
|
||||
recip = Q6_V_vandor_VQR(recip, is_neg, 0x80008000);
|
||||
return recip;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_inverse_f32(HVX_Vector v_sf) {
|
||||
HVX_Vector inv_aprox_sf = Q6_V_vsplat_R(0x7EEEEBB3);
|
||||
HVX_Vector two_sf = hvx_vec_splat_f32(2.0);
|
||||
|
||||
// First approximation
|
||||
HVX_Vector i_sf = Q6_Vw_vsub_VwVw(inv_aprox_sf, v_sf);
|
||||
|
||||
HVX_Vector r_qf;
|
||||
|
||||
// Refine
|
||||
r_qf = Q6_Vqf32_vmpy_VsfVsf(
|
||||
i_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(two_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(i_sf, v_sf)))));
|
||||
r_qf = Q6_Vqf32_vmpy_Vqf32Vqf32(
|
||||
r_qf, Q6_Vqf32_vsub_VsfVsf(two_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(r_qf), v_sf))));
|
||||
r_qf = Q6_Vqf32_vmpy_Vqf32Vqf32(
|
||||
r_qf, Q6_Vqf32_vsub_VsfVsf(two_sf, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(r_qf), v_sf))));
|
||||
|
||||
return Q6_Vsf_equals_Vqf32(r_qf);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_inverse_f32_guard(HVX_Vector v_sf, HVX_Vector nan_inf_mask) {
|
||||
HVX_Vector out = hvx_vec_inverse_f32(v_sf);
|
||||
|
||||
HVX_Vector masked_out = Q6_V_vand_VV(out, nan_inf_mask);
|
||||
const HVX_VectorPred pred = Q6_Q_vcmp_eq_VwVw(nan_inf_mask, masked_out);
|
||||
|
||||
return Q6_V_vmux_QVV(pred, Q6_V_vzero(), out);
|
||||
}
|
||||
|
||||
#define hvx_inverse_f32_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const HVX_Vector nan_inf_mask = Q6_V_vsplat_R(0x7f800000); \
|
||||
\
|
||||
const uint32_t nvec = n / VLEN_FP32; \
|
||||
const uint32_t nloe = n % VLEN_FP32; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = hvx_vec_inverse_f32_guard(vsrc[i], nan_inf_mask); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = hvx_vec_inverse_f32_guard(vsrc[i], nan_inf_mask); \
|
||||
vec_store((void *) &vdst[i], nloe * SIZEOF_FP32, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline HVX_Vector hvx_vec_inverse_f16_guard(HVX_Vector v_sf, HVX_Vector nan_inf_mask) {
|
||||
HVX_Vector out = hvx_vec_inverse_f16(v_sf);
|
||||
|
||||
HVX_Vector masked_out = Q6_V_vand_VV(out, nan_inf_mask);
|
||||
const HVX_VectorPred pred = Q6_Q_vcmp_eq_VhVh(nan_inf_mask, masked_out);
|
||||
|
||||
return Q6_V_vmux_QVV(pred, Q6_V_vzero(), out);
|
||||
}
|
||||
|
||||
#define hvx_inverse_f16_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const HVX_Vector nan_inf_mask = Q6_Vh_vsplat_R(0x7c00); \
|
||||
\
|
||||
const uint32_t nvec = n / VLEN_FP16; \
|
||||
const uint32_t nloe = n % VLEN_FP16; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = hvx_vec_inverse_f16_guard(vsrc[i], nan_inf_mask); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = hvx_vec_inverse_f16_guard(vsrc[i], nan_inf_mask); \
|
||||
vec_store((void *) &vdst[i], nloe * SIZEOF_FP16, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Generic macro to define alignment permutations for an op
|
||||
#define DEFINE_HVX_INV_OP_VARIANTS(OP_NAME, OP_LOOP_BODY) \
|
||||
static inline void OP_NAME##_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
assert((uintptr_t) src % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_Vector, HVX_Vector, hvx_vec_store_a); \
|
||||
} \
|
||||
static inline void OP_NAME##_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) { \
|
||||
assert((uintptr_t) dst % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_Vector, HVX_UVector, hvx_vec_store_a); \
|
||||
} \
|
||||
static inline void OP_NAME##_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) { \
|
||||
assert((uintptr_t) src % 128 == 0); \
|
||||
OP_LOOP_BODY(HVX_UVector, HVX_Vector, hvx_vec_store_u); \
|
||||
} \
|
||||
static inline void OP_NAME##_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) { \
|
||||
OP_LOOP_BODY(HVX_UVector, HVX_UVector, hvx_vec_store_u); \
|
||||
} \
|
||||
|
||||
// Dispatcher logic
|
||||
#define HVX_INV_DISPATCHER(OP_NAME) \
|
||||
static inline void OP_NAME(uint8_t * restrict dst, const uint8_t * restrict src, const uint32_t num_elems) { \
|
||||
if (hex_is_aligned((void *) dst, 128) && hex_is_aligned((void *) src, 128)) { \
|
||||
OP_NAME##_aa(dst, src, num_elems); \
|
||||
} else if (hex_is_aligned((void *) dst, 128)) { \
|
||||
OP_NAME##_au(dst, src, num_elems); \
|
||||
} else if (hex_is_aligned((void *) src, 128)) { \
|
||||
OP_NAME##_ua(dst, src, num_elems); \
|
||||
} else { \
|
||||
OP_NAME##_uu(dst, src, num_elems); \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_HVX_INV_OP_VARIANTS(hvx_inverse_f32, hvx_inverse_f32_loop_body)
|
||||
DEFINE_HVX_INV_OP_VARIANTS(hvx_inverse_f16, hvx_inverse_f16_loop_body)
|
||||
|
||||
HVX_INV_DISPATCHER(hvx_inverse_f32)
|
||||
HVX_INV_DISPATCHER(hvx_inverse_f16)
|
||||
|
||||
#endif // HVX_INVERSE_H
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef HVX_LOG_H
|
||||
#define HVX_LOG_H
|
||||
|
||||
#include "hvx-base.h"
|
||||
|
||||
// Approximates ln(x) element-wise for float vectors.
|
||||
// x must contain positive float elements.
|
||||
// Uses Abramowitz & Stegun polynomial approximation 4.1.44 for ln(1+y) over [0, 1].
|
||||
static inline HVX_Vector hvx_vec_log_f32(HVX_Vector x) {
|
||||
// x = m * 2^e, where m in [1, 2)
|
||||
HVX_Vector biased_e = Q6_Vuw_vlsr_VuwR(x, 23);
|
||||
HVX_Vector e_int = Q6_Vw_vsub_VwVw(biased_e, Q6_V_vsplat_R(127));
|
||||
HVX_Vector e_float = Q6_Vsf_equals_Vw(e_int);
|
||||
|
||||
// Extract mantissa and set exponent to 127 (which represents float value in [1.0, 2.0))
|
||||
HVX_Vector mant_mask = Q6_V_vsplat_R(0x007FFFFF);
|
||||
HVX_Vector exp_127 = Q6_V_vsplat_R(0x3F800000);
|
||||
HVX_Vector m = Q6_V_vor_VV(Q6_V_vand_VV(x, mant_mask), exp_127);
|
||||
|
||||
// y = m - 1.0f, y in [0, 1)
|
||||
HVX_Vector y = hvx_vec_sub_f32_f32(m, hvx_vec_splat_f32(1.0f));
|
||||
|
||||
// Abramowitz & Stegun 4.1.44 polynomial approximation of ln(1+y)
|
||||
HVX_Vector c;
|
||||
HVX_Vector res;
|
||||
|
||||
c = hvx_vec_splat_f32(-0.0064535442f);
|
||||
res = hvx_vec_mul_f32_f32(y, c);
|
||||
|
||||
c = hvx_vec_splat_f32(0.0360884937f);
|
||||
res = hvx_vec_add_f32_f32(res, c);
|
||||
res = hvx_vec_mul_f32_f32(y, res);
|
||||
|
||||
c = hvx_vec_splat_f32(-0.0953293897f);
|
||||
res = hvx_vec_add_f32_f32(res, c);
|
||||
res = hvx_vec_mul_f32_f32(y, res);
|
||||
|
||||
c = hvx_vec_splat_f32(0.1676540711f);
|
||||
res = hvx_vec_add_f32_f32(res, c);
|
||||
res = hvx_vec_mul_f32_f32(y, res);
|
||||
|
||||
c = hvx_vec_splat_f32(-0.2407338084f);
|
||||
res = hvx_vec_add_f32_f32(res, c);
|
||||
res = hvx_vec_mul_f32_f32(y, res);
|
||||
|
||||
c = hvx_vec_splat_f32(0.3317990258f);
|
||||
res = hvx_vec_add_f32_f32(res, c);
|
||||
res = hvx_vec_mul_f32_f32(y, res);
|
||||
|
||||
c = hvx_vec_splat_f32(-0.4998741238f);
|
||||
res = hvx_vec_add_f32_f32(res, c);
|
||||
res = hvx_vec_mul_f32_f32(y, res);
|
||||
|
||||
c = hvx_vec_splat_f32(0.9999964239f);
|
||||
res = hvx_vec_add_f32_f32(res, c);
|
||||
res = hvx_vec_mul_f32_f32(y, res);
|
||||
|
||||
// ln(x) = e * ln(2) + ln(1+y)
|
||||
HVX_Vector ln2 = hvx_vec_splat_f32(0.69314718056f);
|
||||
HVX_Vector term_e = hvx_vec_mul_f32_f32(e_float, ln2);
|
||||
|
||||
return hvx_vec_add_f32_f32(term_e, res);
|
||||
}
|
||||
|
||||
static inline void hvx_log_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t elem_size = sizeof(float);
|
||||
const uint32_t epv = 128 / elem_size;
|
||||
const uint32_t nvec = n / epv;
|
||||
const uint32_t nloe = n % epv;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
vdst[i] = hvx_vec_log_f32(vsrc[i]);
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_Vector v = hvx_vec_log_f32(vsrc[i]);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * elem_size, v);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute log(x) for f16 by promoting to f32, applying hvx_vec_log_f32, and narrowing back.
|
||||
static inline void hvx_log_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
|
||||
HVX_Vector * restrict vdst = (HVX_Vector *) dst;
|
||||
HVX_Vector * restrict vsrc = (HVX_Vector *) src;
|
||||
|
||||
const uint32_t nvec = n / VLEN_FP16;
|
||||
const uint32_t nloe = n % VLEN_FP16;
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
_Pragma("unroll(4)")
|
||||
for (; i < nvec; i++) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]);
|
||||
HVX_Vector r0 = hvx_vec_log_f32(Q6_V_lo_W(p));
|
||||
HVX_Vector r1 = hvx_vec_log_f32(Q6_V_hi_W(p));
|
||||
vdst[i] = hvx_vec_f32_to_f16(r0, r1);
|
||||
}
|
||||
if (nloe) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]);
|
||||
HVX_Vector r0 = hvx_vec_log_f32(Q6_V_lo_W(p));
|
||||
HVX_Vector r1 = hvx_vec_log_f32(Q6_V_hi_W(p));
|
||||
HVX_Vector v = hvx_vec_f32_to_f16(r0, r1);
|
||||
hvx_vec_store_a((void *) &vdst[i], nloe * SIZEOF_FP16, v);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HVX_LOG_H */
|
||||
@@ -0,0 +1,575 @@
|
||||
#ifndef HVX_MM_KERNELS_FLOAT_H
|
||||
#define HVX_MM_KERNELS_FLOAT_H
|
||||
|
||||
#include "hvx-utils.h"
|
||||
#include "htp-tensor.h"
|
||||
|
||||
// Float activation copy/quantization kernels (DDR -> VTCM)
|
||||
|
||||
static inline void quantize_f32_f32_kernel(
|
||||
const uint8_t * restrict src_data,
|
||||
uint8_t * restrict dst_data,
|
||||
uint8_t * restrict tmp_data,
|
||||
uint32_t ne0,
|
||||
uint32_t nrows,
|
||||
size_t src_stride,
|
||||
size_t dst_stride
|
||||
) {
|
||||
(void) tmp_data;
|
||||
const size_t src_row_size = ne0 * sizeof(float);
|
||||
for (uint32_t i = 0; i < nrows; ++i) {
|
||||
hex_l2fetch(src_data, src_row_size, src_stride, 2);
|
||||
hvx_copy_f32_au(dst_data, src_data, ne0);
|
||||
|
||||
dst_data += dst_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void quantize_f32_f16_kernel(
|
||||
const uint8_t * restrict src_data,
|
||||
uint8_t * restrict dst_data,
|
||||
uint8_t * restrict tmp_data,
|
||||
uint32_t ne0,
|
||||
uint32_t nrows,
|
||||
size_t src_stride,
|
||||
size_t dst_stride
|
||||
) {
|
||||
(void) tmp_data;
|
||||
const size_t src_row_size = ne0 * sizeof(float);
|
||||
for (uint32_t i = 0; i < nrows; ++i) {
|
||||
hex_l2fetch(src_data, src_row_size, src_stride, 2);
|
||||
hvx_copy_f16_f32_au(dst_data, src_data, ne0);
|
||||
|
||||
dst_data += dst_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void quantize_f16_f16_kernel(
|
||||
const uint8_t * restrict src_data,
|
||||
uint8_t * restrict dst_data,
|
||||
uint8_t * restrict tmp_data,
|
||||
uint32_t ne0,
|
||||
uint32_t nrows,
|
||||
size_t src_stride,
|
||||
size_t dst_stride
|
||||
) {
|
||||
(void) tmp_data;
|
||||
const size_t src_row_size = ne0 * sizeof(float);
|
||||
for (uint32_t i = 0; i < nrows; ++i) {
|
||||
hex_l2fetch(src_data, src_row_size, src_stride, 2);
|
||||
hvx_copy_f16_au(dst_data, src_data, ne0);
|
||||
|
||||
dst_data += dst_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
// Float dot product kernels (HVX)
|
||||
|
||||
#if __HVX_ARCH__ < 79
|
||||
#define HVX_OP_ADD_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(a, b))
|
||||
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(a, b))
|
||||
#else
|
||||
#define HVX_OP_ADD_F32(a, b) Q6_Vsf_vadd_VsfVsf(a, b)
|
||||
#define HVX_OP_MUL_F32(a, b) Q6_Vsf_vmpy_VsfVsf(a, b)
|
||||
#endif
|
||||
|
||||
static inline void flat_vec_dot_f32_f32_aa_1x1(const uint32_t n, float * restrict s, const void * restrict vx, const void * restrict vy) {
|
||||
const HVX_Vector * restrict x = (const HVX_Vector *) vx;
|
||||
const HVX_Vector * restrict y = (const HVX_Vector *) vy;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP32; // num full fp32 hvx vectors
|
||||
uint32_t nloe = n % VLEN_FP32; // leftover elements
|
||||
|
||||
HVX_Vector rsum = Q6_V_vzero();
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(4)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
HVX_Vector prod = HVX_OP_MUL_F32(x[i], y[i]);
|
||||
rsum = HVX_OP_ADD_F32(rsum, prod);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector x_sf = Q6_V_vand_QV(bmask, x[i]);
|
||||
HVX_Vector y_sf = Q6_V_vand_QV(bmask, y[i]);
|
||||
HVX_Vector prod = HVX_OP_MUL_F32(x_sf, y_sf);
|
||||
rsum = HVX_OP_ADD_F32(rsum, prod);
|
||||
}
|
||||
|
||||
*s = hvx_vec_get_f32(hvx_vec_reduce_sum_f32(rsum));
|
||||
}
|
||||
|
||||
static inline void flat_vec_dot_f32_f32_aa_2x1(const uint32_t n, float * restrict s0,
|
||||
const void * restrict vx0, const void * restrict vx1,
|
||||
const void * restrict vy0) {
|
||||
const HVX_Vector * restrict x0 = (const HVX_Vector *) vx0;
|
||||
const HVX_Vector * restrict x1 = (const HVX_Vector *) vx1;
|
||||
const HVX_Vector * restrict y = (const HVX_Vector *) vy0;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP32;
|
||||
uint32_t nloe = n % VLEN_FP32;
|
||||
|
||||
HVX_Vector rsum0 = Q6_V_vzero();
|
||||
HVX_Vector rsum1 = Q6_V_vzero();
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
HVX_Vector y_sf = y[i];
|
||||
HVX_Vector prod0 = HVX_OP_MUL_F32(x0[i], y_sf);
|
||||
HVX_Vector prod1 = HVX_OP_MUL_F32(x1[i], y_sf);
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, prod0);
|
||||
rsum1 = HVX_OP_ADD_F32(rsum1, prod1);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector y_sf = Q6_V_vand_QV(bmask, y[i]);
|
||||
HVX_Vector x0_sf = Q6_V_vand_QV(bmask, x0[i]);
|
||||
HVX_Vector x1_sf = Q6_V_vand_QV(bmask, x1[i]);
|
||||
HVX_Vector prod0 = HVX_OP_MUL_F32(x0_sf, y_sf);
|
||||
HVX_Vector prod1 = HVX_OP_MUL_F32(x1_sf, y_sf);
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, prod0);
|
||||
rsum1 = HVX_OP_ADD_F32(rsum1, prod1);
|
||||
}
|
||||
|
||||
HVX_Vector rsum = hvx_vec_reduce_sum_f32x2(rsum0, rsum1);
|
||||
hvx_vec_store_u(s0, 8, rsum);
|
||||
}
|
||||
|
||||
static inline void flat_vec_dot_f32_f32_aa_2x2(const uint32_t n, float * restrict s0, float * restrict s1,
|
||||
const void * restrict vx0, const void * restrict vx1,
|
||||
const void * restrict vy0, const void * restrict vy1) {
|
||||
const HVX_Vector * restrict x0 = (const HVX_Vector *) vx0;
|
||||
const HVX_Vector * restrict x1 = (const HVX_Vector *) vx1;
|
||||
const HVX_Vector * restrict y0 = (const HVX_Vector *) vy0;
|
||||
const HVX_Vector * restrict y1 = (const HVX_Vector *) vy1;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP32;
|
||||
uint32_t nloe = n % VLEN_FP32;
|
||||
|
||||
HVX_Vector r0_c0_sum = Q6_V_vzero();
|
||||
HVX_Vector r0_c1_sum = Q6_V_vzero();
|
||||
HVX_Vector r1_c0_sum = Q6_V_vzero();
|
||||
HVX_Vector r1_c1_sum = Q6_V_vzero();
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
HVX_Vector r0_sf = x0[i];
|
||||
HVX_Vector r1_sf = x1[i];
|
||||
HVX_Vector c0_sf = y0[i];
|
||||
HVX_Vector c1_sf = y1[i];
|
||||
|
||||
r0_c0_sum = HVX_OP_ADD_F32(r0_c0_sum, HVX_OP_MUL_F32(r0_sf, c0_sf));
|
||||
r0_c1_sum = HVX_OP_ADD_F32(r0_c1_sum, HVX_OP_MUL_F32(r0_sf, c1_sf));
|
||||
r1_c0_sum = HVX_OP_ADD_F32(r1_c0_sum, HVX_OP_MUL_F32(r1_sf, c0_sf));
|
||||
r1_c1_sum = HVX_OP_ADD_F32(r1_c1_sum, HVX_OP_MUL_F32(r1_sf, c1_sf));
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
|
||||
HVX_Vector r0_sf = Q6_V_vand_QV(bmask, x0[i]);
|
||||
HVX_Vector r1_sf = Q6_V_vand_QV(bmask, x1[i]);
|
||||
HVX_Vector c0_sf = Q6_V_vand_QV(bmask, y0[i]);
|
||||
HVX_Vector c1_sf = Q6_V_vand_QV(bmask, y1[i]);
|
||||
|
||||
r0_c0_sum = HVX_OP_ADD_F32(r0_c0_sum, HVX_OP_MUL_F32(r0_sf, c0_sf));
|
||||
r0_c1_sum = HVX_OP_ADD_F32(r0_c1_sum, HVX_OP_MUL_F32(r0_sf, c1_sf));
|
||||
r1_c0_sum = HVX_OP_ADD_F32(r1_c0_sum, HVX_OP_MUL_F32(r1_sf, c0_sf));
|
||||
r1_c1_sum = HVX_OP_ADD_F32(r1_c1_sum, HVX_OP_MUL_F32(r1_sf, c1_sf));
|
||||
}
|
||||
|
||||
// Reduce and store results
|
||||
HVX_Vector r0_r1_c0_sum = hvx_vec_reduce_sum_f32x2(r0_c0_sum, r1_c0_sum);
|
||||
HVX_Vector r0_r1_c1_sum = hvx_vec_reduce_sum_f32x2(r0_c1_sum, r1_c1_sum);
|
||||
|
||||
hvx_vec_store_u(s0, 8, r0_r1_c0_sum);
|
||||
hvx_vec_store_u(s1, 8, r0_r1_c1_sum);
|
||||
}
|
||||
|
||||
static inline void flat_vec_dot_bf16_f32_aa_1x1(const uint32_t n, float * restrict s, const void * restrict vx, const void * restrict vy) {
|
||||
const HVX_Vector * restrict x = (const HVX_Vector *) vx;
|
||||
const HVX_Vector * restrict y = (const HVX_Vector *) vy;
|
||||
|
||||
uint32_t nvec = n / 64;
|
||||
uint32_t rem = n % 64;
|
||||
|
||||
HVX_Vector rsum0 = Q6_V_vzero();
|
||||
HVX_Vector rsum1 = Q6_V_vzero();
|
||||
|
||||
uint32_t i = 0;
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
HVX_Vector v_bf16 = x[i];
|
||||
HVX_VectorPair p = Q6_Ww_vunpack_Vh(v_bf16);
|
||||
HVX_Vector w0_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p), 16);
|
||||
HVX_Vector w1_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p), 16);
|
||||
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, HVX_OP_MUL_F32(w0_f32, y[2 * i + 0]));
|
||||
rsum1 = HVX_OP_ADD_F32(rsum1, HVX_OP_MUL_F32(w1_f32, y[2 * i + 1]));
|
||||
}
|
||||
|
||||
if (rem) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq2_R(rem * 2);
|
||||
HVX_Vector v_bf16 = Q6_V_vand_QV(bmask, x[i]);
|
||||
HVX_VectorPair p = Q6_Ww_vunpack_Vh(v_bf16);
|
||||
HVX_Vector w0_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p), 16);
|
||||
HVX_Vector w1_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p), 16);
|
||||
|
||||
if (rem <= 32) {
|
||||
HVX_VectorPred a_mask = Q6_Q_vsetq2_R(rem * 4);
|
||||
HVX_Vector y0 = Q6_V_vand_QV(a_mask, y[2 * i + 0]);
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, HVX_OP_MUL_F32(w0_f32, y0));
|
||||
} else {
|
||||
HVX_Vector y0 = y[2 * i + 0];
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, HVX_OP_MUL_F32(w0_f32, y0));
|
||||
|
||||
HVX_VectorPred a_mask = Q6_Q_vsetq2_R((rem - 32) * 4);
|
||||
HVX_Vector y1 = Q6_V_vand_QV(a_mask, y[2 * i + 1]);
|
||||
rsum1 = HVX_OP_ADD_F32(rsum1, HVX_OP_MUL_F32(w1_f32, y1));
|
||||
}
|
||||
}
|
||||
|
||||
HVX_Vector rsum = HVX_OP_ADD_F32(rsum0, rsum1);
|
||||
*s = hvx_vec_get_f32(hvx_vec_reduce_sum_f32(rsum));
|
||||
}
|
||||
|
||||
static inline void flat_vec_dot_bf16_f32_aa_2x1(const uint32_t n, float * restrict s0,
|
||||
const void * restrict vx0, const void * restrict vx1,
|
||||
const void * restrict vy0) {
|
||||
const HVX_Vector * restrict x0 = (const HVX_Vector *) vx0;
|
||||
const HVX_Vector * restrict x1 = (const HVX_Vector *) vx1;
|
||||
const HVX_Vector * restrict y = (const HVX_Vector *) vy0;
|
||||
|
||||
uint32_t nvec = n / 64;
|
||||
uint32_t rem = n % 64;
|
||||
|
||||
HVX_Vector rsum0 = Q6_V_vzero();
|
||||
HVX_Vector rsum1 = Q6_V_vzero();
|
||||
|
||||
uint32_t i = 0;
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
HVX_VectorPair p0 = Q6_Ww_vunpack_Vh(x0[i]);
|
||||
HVX_VectorPair p1 = Q6_Ww_vunpack_Vh(x1[i]);
|
||||
HVX_Vector w00_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p0), 16);
|
||||
HVX_Vector w01_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p0), 16);
|
||||
HVX_Vector w10_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p1), 16);
|
||||
HVX_Vector w11_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p1), 16);
|
||||
|
||||
HVX_Vector y0 = y[2 * i + 0];
|
||||
HVX_Vector y1 = y[2 * i + 1];
|
||||
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, HVX_OP_MUL_F32(w00_f32, y0));
|
||||
rsum1 = HVX_OP_ADD_F32(rsum1, HVX_OP_MUL_F32(w10_f32, y0));
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, HVX_OP_MUL_F32(w01_f32, y1));
|
||||
rsum1 = HVX_OP_ADD_F32(rsum1, HVX_OP_MUL_F32(w11_f32, y1));
|
||||
}
|
||||
|
||||
if (rem) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq2_R(rem * 2);
|
||||
HVX_VectorPair p0 = Q6_Ww_vunpack_Vh(Q6_V_vand_QV(bmask, x0[i]));
|
||||
HVX_VectorPair p1 = Q6_Ww_vunpack_Vh(Q6_V_vand_QV(bmask, x1[i]));
|
||||
HVX_Vector w00_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p0), 16);
|
||||
HVX_Vector w01_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p0), 16);
|
||||
HVX_Vector w10_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p1), 16);
|
||||
HVX_Vector w11_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p1), 16);
|
||||
|
||||
if (rem <= 32) {
|
||||
HVX_VectorPred a_mask = Q6_Q_vsetq2_R(rem * 4);
|
||||
HVX_Vector y0 = Q6_V_vand_QV(a_mask, y[2 * i + 0]);
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, HVX_OP_MUL_F32(w00_f32, y0));
|
||||
rsum1 = HVX_OP_ADD_F32(rsum1, HVX_OP_MUL_F32(w10_f32, y0));
|
||||
} else {
|
||||
HVX_Vector y0 = y[2 * i + 0];
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, HVX_OP_MUL_F32(w00_f32, y0));
|
||||
rsum1 = HVX_OP_ADD_F32(rsum1, HVX_OP_MUL_F32(w10_f32, y0));
|
||||
|
||||
HVX_VectorPred a_mask = Q6_Q_vsetq2_R((rem - 32) * 4);
|
||||
HVX_Vector y1 = Q6_V_vand_QV(a_mask, y[2 * i + 1]);
|
||||
rsum0 = HVX_OP_ADD_F32(rsum0, HVX_OP_MUL_F32(w01_f32, y1));
|
||||
rsum1 = HVX_OP_ADD_F32(rsum1, HVX_OP_MUL_F32(w11_f32, y1));
|
||||
}
|
||||
}
|
||||
|
||||
HVX_Vector rsum = hvx_vec_reduce_sum_f32x2(rsum0, rsum1);
|
||||
hvx_vec_store_u(s0, 8, rsum);
|
||||
}
|
||||
|
||||
static inline void flat_vec_dot_bf16_f32_aa_2x2(const uint32_t n, float * restrict s0, float * restrict s1,
|
||||
const void * restrict vx0, const void * restrict vx1,
|
||||
const void * restrict vy0, const void * restrict vy1) {
|
||||
const HVX_Vector * restrict x0 = (const HVX_Vector *) vx0;
|
||||
const HVX_Vector * restrict x1 = (const HVX_Vector *) vx1;
|
||||
const HVX_Vector * restrict y0 = (const HVX_Vector *) vy0;
|
||||
const HVX_Vector * restrict y1 = (const HVX_Vector *) vy1;
|
||||
|
||||
uint32_t nvec = n / 64;
|
||||
uint32_t rem = n % 64;
|
||||
|
||||
HVX_Vector r0_c0_sum = Q6_V_vzero();
|
||||
HVX_Vector r0_c1_sum = Q6_V_vzero();
|
||||
HVX_Vector r1_c0_sum = Q6_V_vzero();
|
||||
HVX_Vector r1_c1_sum = Q6_V_vzero();
|
||||
|
||||
uint32_t i = 0;
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
HVX_VectorPair p0 = Q6_Ww_vunpack_Vh(x0[i]);
|
||||
HVX_VectorPair p1 = Q6_Ww_vunpack_Vh(x1[i]);
|
||||
HVX_Vector w00_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p0), 16);
|
||||
HVX_Vector w01_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p0), 16);
|
||||
HVX_Vector w10_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p1), 16);
|
||||
HVX_Vector w11_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p1), 16);
|
||||
|
||||
HVX_Vector c0_0 = y0[2 * i + 0];
|
||||
HVX_Vector c0_1 = y0[2 * i + 1];
|
||||
HVX_Vector c1_0 = y1[2 * i + 0];
|
||||
HVX_Vector c1_1 = y1[2 * i + 1];
|
||||
|
||||
r0_c0_sum = HVX_OP_ADD_F32(r0_c0_sum, HVX_OP_MUL_F32(w00_f32, c0_0));
|
||||
r0_c1_sum = HVX_OP_ADD_F32(r0_c1_sum, HVX_OP_MUL_F32(w00_f32, c1_0));
|
||||
r1_c0_sum = HVX_OP_ADD_F32(r1_c0_sum, HVX_OP_MUL_F32(w10_f32, c0_0));
|
||||
r1_c1_sum = HVX_OP_ADD_F32(r1_c1_sum, HVX_OP_MUL_F32(w10_f32, c1_0));
|
||||
|
||||
r0_c0_sum = HVX_OP_ADD_F32(r0_c0_sum, HVX_OP_MUL_F32(w01_f32, c0_1));
|
||||
r0_c1_sum = HVX_OP_ADD_F32(r0_c1_sum, HVX_OP_MUL_F32(w01_f32, c1_1));
|
||||
r1_c0_sum = HVX_OP_ADD_F32(r1_c0_sum, HVX_OP_MUL_F32(w11_f32, c0_1));
|
||||
r1_c1_sum = HVX_OP_ADD_F32(r1_c1_sum, HVX_OP_MUL_F32(w11_f32, c1_1));
|
||||
}
|
||||
|
||||
if (rem) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq2_R(rem * 2);
|
||||
HVX_VectorPair p0 = Q6_Ww_vunpack_Vh(Q6_V_vand_QV(bmask, x0[i]));
|
||||
HVX_VectorPair p1 = Q6_Ww_vunpack_Vh(Q6_V_vand_QV(bmask, x1[i]));
|
||||
HVX_Vector w00_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p0), 16);
|
||||
HVX_Vector w01_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p0), 16);
|
||||
HVX_Vector w10_f32 = Q6_Vw_vasl_VwR(Q6_V_lo_W(p1), 16);
|
||||
HVX_Vector w11_f32 = Q6_Vw_vasl_VwR(Q6_V_hi_W(p1), 16);
|
||||
|
||||
if (rem <= 32) {
|
||||
HVX_VectorPred a_mask = Q6_Q_vsetq2_R(rem * 4);
|
||||
HVX_Vector c0_0 = Q6_V_vand_QV(a_mask, y0[2 * i + 0]);
|
||||
HVX_Vector c1_0 = Q6_V_vand_QV(a_mask, y1[2 * i + 0]);
|
||||
r0_c0_sum = HVX_OP_ADD_F32(r0_c0_sum, HVX_OP_MUL_F32(w00_f32, c0_0));
|
||||
r0_c1_sum = HVX_OP_ADD_F32(r0_c1_sum, HVX_OP_MUL_F32(w00_f32, c1_0));
|
||||
r1_c0_sum = HVX_OP_ADD_F32(r1_c0_sum, HVX_OP_MUL_F32(w10_f32, c0_0));
|
||||
r1_c1_sum = HVX_OP_ADD_F32(r1_c1_sum, HVX_OP_MUL_F32(w10_f32, c1_0));
|
||||
} else {
|
||||
HVX_Vector c0_0 = y0[2 * i + 0];
|
||||
HVX_Vector c1_0 = y1[2 * i + 0];
|
||||
r0_c0_sum = HVX_OP_ADD_F32(r0_c0_sum, HVX_OP_MUL_F32(w00_f32, c0_0));
|
||||
r0_c1_sum = HVX_OP_ADD_F32(r0_c1_sum, HVX_OP_MUL_F32(w00_f32, c1_0));
|
||||
r1_c0_sum = HVX_OP_ADD_F32(r1_c0_sum, HVX_OP_MUL_F32(w10_f32, c0_0));
|
||||
r1_c1_sum = HVX_OP_ADD_F32(r1_c1_sum, HVX_OP_MUL_F32(w10_f32, c1_0));
|
||||
|
||||
HVX_VectorPred a_mask = Q6_Q_vsetq2_R((rem - 32) * 4);
|
||||
HVX_Vector c0_1 = Q6_V_vand_QV(a_mask, y0[2 * i + 1]);
|
||||
HVX_Vector c1_1 = Q6_V_vand_QV(a_mask, y1[2 * i + 1]);
|
||||
r0_c0_sum = HVX_OP_ADD_F32(r0_c0_sum, HVX_OP_MUL_F32(w01_f32, c0_1));
|
||||
r0_c1_sum = HVX_OP_ADD_F32(r0_c1_sum, HVX_OP_MUL_F32(w01_f32, c1_1));
|
||||
r1_c0_sum = HVX_OP_ADD_F32(r1_c0_sum, HVX_OP_MUL_F32(w11_f32, c0_1));
|
||||
r1_c1_sum = HVX_OP_ADD_F32(r1_c1_sum, HVX_OP_MUL_F32(w11_f32, c1_1));
|
||||
}
|
||||
}
|
||||
|
||||
HVX_Vector r0_r1_c0_sum = hvx_vec_reduce_sum_f32x2(r0_c0_sum, r1_c0_sum);
|
||||
HVX_Vector r0_r1_c1_sum = hvx_vec_reduce_sum_f32x2(r0_c1_sum, r1_c1_sum);
|
||||
|
||||
hvx_vec_store_u(s0, 8, r0_r1_c0_sum);
|
||||
hvx_vec_store_u(s1, 8, r0_r1_c1_sum);
|
||||
}
|
||||
|
||||
#undef HVX_OP_ADD_F32
|
||||
#undef HVX_OP_MUL_F32
|
||||
|
||||
static inline void flat_vec_dot_f16_f16_aa_1x1(const uint32_t n, float * restrict s, const void * restrict vx, const void * restrict vy) {
|
||||
const HVX_Vector * restrict x = (const HVX_Vector *) vx;
|
||||
const HVX_Vector * restrict y = (const HVX_Vector *) vy;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP16; // num full fp16 hvx vectors
|
||||
uint32_t nloe = n % VLEN_FP16; // leftover elements
|
||||
|
||||
HVX_VectorPair rsum_p = Q6_W_vzero();
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(4)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
rsum_p = hvx_vec_mpyacc_f32_f16(rsum_p, x[i], y[i]);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 2);
|
||||
HVX_Vector x_hf = Q6_V_vand_QV(bmask, x[i]);
|
||||
HVX_Vector y_hf = Q6_V_vand_QV(bmask, y[i]);
|
||||
rsum_p = hvx_vec_mpyacc_f32_f16(rsum_p, x_hf, y_hf);
|
||||
}
|
||||
|
||||
HVX_Vector rsum = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(rsum_p), Q6_V_hi_W(rsum_p)));
|
||||
hvx_vec_store_u(s, 4, hvx_vec_reduce_sum_f32(rsum));
|
||||
}
|
||||
|
||||
static inline void flat_vec_dot_f16_f16_aa_2x1(const uint32_t n, float * restrict s0,
|
||||
const void * restrict vx0, const void * restrict vx1,
|
||||
const void * restrict vy0) {
|
||||
const HVX_Vector * restrict x0 = (const HVX_Vector *) vx0;
|
||||
const HVX_Vector * restrict x1 = (const HVX_Vector *) vx1;
|
||||
const HVX_Vector * restrict y = (const HVX_Vector *) vy0;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP16;
|
||||
uint32_t nloe = n % VLEN_FP16;
|
||||
|
||||
HVX_VectorPair rsum0_p = Q6_W_vzero();
|
||||
HVX_VectorPair rsum1_p = Q6_W_vzero();
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
HVX_Vector y_hf = y[i];
|
||||
rsum0_p = hvx_vec_mpyacc_f32_f16(rsum0_p, x0[i], y_hf);
|
||||
rsum1_p = hvx_vec_mpyacc_f32_f16(rsum1_p, x1[i], y_hf);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 2);
|
||||
HVX_Vector y_hf = Q6_V_vand_QV(bmask, y[i]);
|
||||
HVX_Vector x0_hf = Q6_V_vand_QV(bmask, x0[i]);
|
||||
HVX_Vector x1_hf = Q6_V_vand_QV(bmask, x1[i]);
|
||||
rsum0_p = hvx_vec_mpyacc_f32_f16(rsum0_p, x0_hf, y_hf);
|
||||
rsum1_p = hvx_vec_mpyacc_f32_f16(rsum1_p, x1_hf, y_hf);
|
||||
}
|
||||
|
||||
HVX_Vector rsum0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(rsum0_p), Q6_V_hi_W(rsum0_p)));
|
||||
HVX_Vector rsum1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(rsum1_p), Q6_V_hi_W(rsum1_p)));
|
||||
HVX_Vector rsum = hvx_vec_reduce_sum_f32x2(rsum0, rsum1);
|
||||
hvx_vec_store_u(s0, 8, rsum);
|
||||
}
|
||||
|
||||
static inline void flat_vec_dot_f16_f16_aa_2x2(const uint32_t n, float * restrict s0, float * restrict s1,
|
||||
const void * restrict vx0, const void * restrict vx1,
|
||||
const void * restrict vy0, const void * restrict vy1) {
|
||||
const HVX_Vector * restrict x0 = (const HVX_Vector *) vx0;
|
||||
const HVX_Vector * restrict x1 = (const HVX_Vector *) vx1;
|
||||
const HVX_Vector * restrict y0 = (const HVX_Vector *) vy0;
|
||||
const HVX_Vector * restrict y1 = (const HVX_Vector *) vy1;
|
||||
|
||||
uint32_t nvec = n / VLEN_FP16;
|
||||
uint32_t nloe = n % VLEN_FP16;
|
||||
|
||||
// Row sums (sf) - 4 accumulators for 2x2 tile
|
||||
HVX_VectorPair r0_c0_sum_p = Q6_W_vzero();
|
||||
HVX_VectorPair r0_c1_sum_p = Q6_W_vzero();
|
||||
HVX_VectorPair r1_c0_sum_p = Q6_W_vzero();
|
||||
HVX_VectorPair r1_c1_sum_p = Q6_W_vzero();
|
||||
|
||||
uint32_t i = 0;
|
||||
|
||||
#pragma unroll(2)
|
||||
for (i = 0; i < nvec; i++) {
|
||||
HVX_Vector r0_hf = x0[i];
|
||||
HVX_Vector r1_hf = x1[i];
|
||||
HVX_Vector c0_hf = y0[i];
|
||||
HVX_Vector c1_hf = y1[i];
|
||||
|
||||
// Compute 4 dot products: r0xc0, r0xc1, r1xc0, r1xc1
|
||||
r0_c0_sum_p = hvx_vec_mpyacc_f32_f16(r0_c0_sum_p, r0_hf, c0_hf);
|
||||
r0_c1_sum_p = hvx_vec_mpyacc_f32_f16(r0_c1_sum_p, r0_hf, c1_hf);
|
||||
r1_c0_sum_p = hvx_vec_mpyacc_f32_f16(r1_c0_sum_p, r1_hf, c0_hf);
|
||||
r1_c1_sum_p = hvx_vec_mpyacc_f32_f16(r1_c1_sum_p, r1_hf, c1_hf);
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 2);
|
||||
|
||||
HVX_Vector r0_hf = Q6_V_vand_QV(bmask, x0[i]);
|
||||
HVX_Vector r1_hf = Q6_V_vand_QV(bmask, x1[i]);
|
||||
HVX_Vector c0_hf = Q6_V_vand_QV(bmask, y0[i]);
|
||||
HVX_Vector c1_hf = Q6_V_vand_QV(bmask, y1[i]);
|
||||
|
||||
r0_c0_sum_p = hvx_vec_mpyacc_f32_f16(r0_c0_sum_p, r0_hf, c0_hf);
|
||||
r0_c1_sum_p = hvx_vec_mpyacc_f32_f16(r0_c1_sum_p, r0_hf, c1_hf);
|
||||
r1_c0_sum_p = hvx_vec_mpyacc_f32_f16(r1_c0_sum_p, r1_hf, c0_hf);
|
||||
r1_c1_sum_p = hvx_vec_mpyacc_f32_f16(r1_c1_sum_p, r1_hf, c1_hf);
|
||||
}
|
||||
|
||||
HVX_Vector r0_c0_sum = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(r0_c0_sum_p), Q6_V_hi_W(r0_c0_sum_p)));
|
||||
HVX_Vector r0_c1_sum = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(r0_c1_sum_p), Q6_V_hi_W(r0_c1_sum_p)));
|
||||
HVX_Vector r1_c0_sum = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(r1_c0_sum_p), Q6_V_hi_W(r1_c0_sum_p)));
|
||||
HVX_Vector r1_c1_sum = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(r1_c1_sum_p), Q6_V_hi_W(r1_c1_sum_p)));
|
||||
|
||||
// Reduce and store results
|
||||
HVX_Vector r0_r1_c0_sum = hvx_vec_reduce_sum_f32x2(r0_c0_sum, r1_c0_sum);
|
||||
HVX_Vector r0_r1_c1_sum = hvx_vec_reduce_sum_f32x2(r0_c1_sum, r1_c1_sum);
|
||||
|
||||
hvx_vec_store_u(&s0[0], 8, r0_r1_c0_sum); // row0,col0 row1,col0
|
||||
hvx_vec_store_u(&s1[0], 8, r0_r1_c1_sum); // row0,col1 row1,col1
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline void hvx_tensor_add_f32_grid(
|
||||
const struct htp_tensor * restrict dst,
|
||||
const struct htp_tensor * restrict src2,
|
||||
uint32_t start_row,
|
||||
uint32_t end_row,
|
||||
uint32_t start_col,
|
||||
uint32_t end_col,
|
||||
const struct fastdiv_values * div_ne11_12,
|
||||
const struct fastdiv_values * div_ne11
|
||||
) {
|
||||
if (start_row >= end_row || start_col >= end_col) return;
|
||||
const uint32_t nb1 = dst->nb[1]; // row stride in bytes
|
||||
|
||||
const uint32_t ne11 = dst->ne[1];
|
||||
const uint32_t ne12 = dst->ne[2];
|
||||
const uint32_t ne11_12 = ne11 * ne12;
|
||||
|
||||
const bool is_broadcast1 = (src2->ne[1] == 1);
|
||||
const bool is_broadcast2 = (src2->ne[2] == 1);
|
||||
const bool is_broadcast3 = (src2->ne[3] == 1);
|
||||
|
||||
for (uint32_t r = start_row; r < end_row; r++) {
|
||||
float * dst_row = (float *) ((uint8_t *) dst->data + (size_t) r * nb1);
|
||||
|
||||
uint32_t i13 = fastdiv(r, div_ne11_12);
|
||||
uint32_t i12 = fastdiv(r - i13 * ne11_12, div_ne11);
|
||||
uint32_t i11 = r - i13 * ne11_12 - i12 * ne11;
|
||||
|
||||
uint32_t i23 = is_broadcast3 ? 0 : i13;
|
||||
uint32_t i22 = is_broadcast2 ? 0 : i12;
|
||||
uint32_t i21 = is_broadcast1 ? 0 : i11;
|
||||
|
||||
const float * src2_row = (const float *) ((const uint8_t *) src2->data +
|
||||
(size_t) i21 * src2->nb[1] + (size_t) i22 * src2->nb[2] + (size_t) i23 * src2->nb[3]);
|
||||
|
||||
float * dst_ptr = &dst_row[start_col];
|
||||
const float * src2_ptr = &src2_row[start_col];
|
||||
int remaining = end_col - start_col;
|
||||
while (remaining >= 32) {
|
||||
HVX_Vector v_out = hvx_vmemu(dst_ptr);
|
||||
HVX_Vector v_z = hvx_vmemu(src2_ptr);
|
||||
hvx_vmemu(dst_ptr) = hvx_vec_add_f32_f32(v_out, v_z);
|
||||
dst_ptr += 32;
|
||||
src2_ptr += 32;
|
||||
remaining -= 32;
|
||||
}
|
||||
if (remaining > 0) {
|
||||
HVX_Vector v_out = hvx_vmemu(dst_ptr);
|
||||
HVX_Vector v_z = hvx_vmemu(src2_ptr);
|
||||
hvx_vec_store_u(dst_ptr, remaining * sizeof(float), hvx_vec_add_f32_f32(v_out, v_z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HVX_MM_KERNELS_FLOAT_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,462 @@
|
||||
#ifndef HVX_NORM_H
|
||||
#define HVX_NORM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-reduce.h"
|
||||
#include "hvx-inverse.h"
|
||||
#include "hvx-sqrt.h"
|
||||
#include "hvx-repl.h"
|
||||
|
||||
static inline void hvx_fast_rms_norm_f32(const uint8_t * restrict src,
|
||||
uint8_t * restrict dst,
|
||||
const int num_elems,
|
||||
float epsilon,
|
||||
float scale) {
|
||||
|
||||
const HVX_Vector * restrict v_src = (HVX_Vector *) src;
|
||||
HVX_Vector * restrict v_dst = (HVX_Vector *) dst;
|
||||
|
||||
const int nvec = num_elems / VLEN_FP32; // number of full vectors
|
||||
const int nloe = num_elems % VLEN_FP32; // leftover elements
|
||||
|
||||
// Compute sum of squares for full vectors
|
||||
HVX_Vector sum_v = Q6_V_vsplat_R(0x00000000);
|
||||
HVX_Vector epsilon_v = hvx_vec_splat_f32(epsilon);
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_Vector v1 = v_src[i];
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, v2);
|
||||
}
|
||||
|
||||
// Handle tail elements using vectorized ops with masking
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, v2);
|
||||
}
|
||||
|
||||
// Reduce HVX sum
|
||||
sum_v = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_v));
|
||||
|
||||
HVX_Vector t_v = hvx_vec_splat_f32((float) num_elems);
|
||||
HVX_Vector denom_v = hvx_vec_inverse_f32(t_v);
|
||||
HVX_Vector mean_v = Q6_Vqf32_vmpy_VsfVsf(sum_v, denom_v);
|
||||
HVX_Vector mean_epsilon_v = Q6_Vqf32_vadd_Vqf32Vsf(mean_v, epsilon_v);
|
||||
|
||||
// Scale full vectors
|
||||
HVX_Vector scale_v = hvx_vec_rsqrt_f32(Q6_Vsf_equals_Vqf32(mean_epsilon_v));
|
||||
if (scale != 0.0f) {
|
||||
scale_v = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(scale_v, hvx_vec_splat_f32(scale)));
|
||||
}
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_Vector v1 = v_src[i];
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, scale_v);
|
||||
v_dst[i] = Q6_Vsf_equals_Vqf32(v2);
|
||||
}
|
||||
|
||||
// Handle tail elements using vectorized ops with masking
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, scale_v);
|
||||
HVX_Vector result = Q6_Vsf_equals_Vqf32(v2);
|
||||
|
||||
// Store with masking to avoid overwriting memory beyond the tensor
|
||||
hvx_vec_store_a(&v_dst[nvec], nloe * 4, result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_fast_rms_norm_mul_f32(const uint8_t * restrict src,
|
||||
const uint8_t * restrict weight,
|
||||
uint8_t * restrict dst,
|
||||
const int num_elems,
|
||||
float epsilon,
|
||||
float scale) {
|
||||
const HVX_Vector * restrict v_src = (const HVX_Vector *) src;
|
||||
const HVX_Vector * restrict v_weight = (const HVX_Vector *) weight;
|
||||
HVX_Vector * restrict v_dst = (HVX_Vector *) dst;
|
||||
|
||||
const int nvec = num_elems / VLEN_FP32; // number of full vectors
|
||||
const int nloe = num_elems % VLEN_FP32; // leftover elements
|
||||
|
||||
// Compute sum of squares for full vectors
|
||||
HVX_Vector sum_v = Q6_V_vsplat_R(0x00000000);
|
||||
HVX_Vector epsilon_v = hvx_vec_splat_f32(epsilon);
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_Vector v1 = v_src[i];
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, v2);
|
||||
}
|
||||
|
||||
// Handle tail elements using vectorized ops with masking
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, v2);
|
||||
}
|
||||
|
||||
// Reduce HVX sum
|
||||
sum_v = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_v));
|
||||
|
||||
HVX_Vector t_v = hvx_vec_splat_f32((float) num_elems);
|
||||
HVX_Vector denom_v = hvx_vec_inverse_f32(t_v);
|
||||
HVX_Vector mean_v = Q6_Vqf32_vmpy_VsfVsf(sum_v, denom_v);
|
||||
HVX_Vector mean_epsilon_v = Q6_Vqf32_vadd_Vqf32Vsf(mean_v, epsilon_v);
|
||||
|
||||
// Scale and multiply
|
||||
HVX_Vector scale_v = hvx_vec_rsqrt_f32(Q6_Vsf_equals_Vqf32(mean_epsilon_v));
|
||||
if (scale != 0.0f) {
|
||||
scale_v = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(scale_v, hvx_vec_splat_f32(scale)));
|
||||
}
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_Vector v1 = v_src[i];
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, scale_v);
|
||||
HVX_Vector v3 = Q6_Vsf_equals_Vqf32(v2);
|
||||
HVX_Vector result = Q6_Vqf32_vmpy_VsfVsf(v3, v_weight[i]);
|
||||
v_dst[i] = Q6_Vsf_equals_Vqf32(result);
|
||||
}
|
||||
|
||||
// Handle tail elements using vectorized ops with masking
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, scale_v);
|
||||
HVX_Vector v3 = Q6_Vsf_equals_Vqf32(v2);
|
||||
HVX_Vector result = Q6_Vqf32_vmpy_VsfVsf(v3, v_weight[nvec]);
|
||||
HVX_Vector res_v = Q6_Vsf_equals_Vqf32(result);
|
||||
|
||||
// Store with masking to avoid overwriting memory beyond the tensor
|
||||
hvx_vec_store_a(&v_dst[nvec], nloe * 4, res_v);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_fast_norm_f32(const uint8_t * restrict src,
|
||||
uint8_t * restrict dst,
|
||||
const int num_elems,
|
||||
float epsilon) {
|
||||
|
||||
const HVX_Vector * restrict v_src = (HVX_Vector *) src;
|
||||
HVX_Vector * restrict v_dst = (HVX_Vector *) dst;
|
||||
|
||||
const int nvec = num_elems / VLEN_FP32; // number of full vectors
|
||||
const int nloe = num_elems % VLEN_FP32; // leftover elements
|
||||
|
||||
// Compute sum of squares and sum of values for full vectors
|
||||
HVX_Vector sum_sq_v = Q6_V_vsplat_R(0x00000000);
|
||||
HVX_Vector sum_x_v = Q6_V_vsplat_R(0x00000000);
|
||||
HVX_Vector epsilon_v = hvx_vec_splat_f32(epsilon);
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_Vector v1 = v_src[i];
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
|
||||
sum_sq_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_sq_v, v2);
|
||||
sum_x_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_x_v, Q6_Vqf32_vadd_VsfVsf(v1, Q6_V_vzero()));
|
||||
}
|
||||
|
||||
// Handle tail elements using vectorized ops with masking
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
|
||||
sum_sq_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_sq_v, v2);
|
||||
sum_x_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_x_v, Q6_Vqf32_vadd_VsfVsf(v1, Q6_V_vzero()));
|
||||
}
|
||||
|
||||
// Reduce HVX sums
|
||||
sum_sq_v = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_sq_v));
|
||||
sum_x_v = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_x_v));
|
||||
|
||||
HVX_Vector t_v = hvx_vec_splat_f32((float) num_elems);
|
||||
HVX_Vector denom_v = hvx_vec_inverse_f32(t_v);
|
||||
HVX_Vector mean_sq_v = Q6_Vqf32_vmpy_VsfVsf(sum_sq_v, denom_v);
|
||||
HVX_Vector mean_x_v = Q6_Vqf32_vmpy_VsfVsf(sum_x_v, denom_v);
|
||||
HVX_Vector mean_x_sq_v = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(mean_x_v), Q6_Vsf_equals_Vqf32(mean_x_v));
|
||||
HVX_Vector var_v = Q6_Vqf32_vsub_Vqf32Vqf32(mean_sq_v, mean_x_sq_v);
|
||||
HVX_Vector var_epsilon_v = Q6_Vqf32_vadd_Vqf32Vsf(var_v, epsilon_v);
|
||||
|
||||
// scale = rsqrt(variance + epsilon), mean_x broadcast for subtraction
|
||||
HVX_Vector scale_v = hvx_vec_rsqrt_f32(Q6_Vsf_equals_Vqf32(var_epsilon_v));
|
||||
HVX_Vector mean_x_b = hvx_vec_repl_f32(Q6_Vsf_equals_Vqf32(mean_x_v));
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_Vector v1 = v_src[i];
|
||||
HVX_Vector v2 = Q6_Vqf32_vsub_VsfVsf(v1, mean_x_b);
|
||||
HVX_Vector v3 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(v2), scale_v);
|
||||
v_dst[i] = Q6_Vsf_equals_Vqf32(v3);
|
||||
}
|
||||
|
||||
// Handle tail elements using vectorized ops with masking
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_Vector v2 = Q6_Vqf32_vsub_VsfVsf(v1, mean_x_b);
|
||||
HVX_Vector v3 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(v2), scale_v);
|
||||
HVX_Vector result = Q6_Vsf_equals_Vqf32(v3);
|
||||
|
||||
// Store with masking to avoid overwriting memory beyond the tensor
|
||||
hvx_vec_store_a(&v_dst[nvec], nloe * 4, result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_fast_l2_norm_f32(const uint8_t * restrict src,
|
||||
uint8_t * restrict dst,
|
||||
const int num_elems,
|
||||
float epsilon) {
|
||||
|
||||
const HVX_Vector * restrict v_src = (HVX_Vector *) src;
|
||||
HVX_Vector * restrict v_dst = (HVX_Vector *) dst;
|
||||
|
||||
HVX_Vector sum_v = hvx_vec_splat_f32(0.0f);
|
||||
|
||||
const int nvec = num_elems / VLEN_FP32;
|
||||
const int nloe = num_elems % VLEN_FP32;
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_Vector v1 = v_src[i];
|
||||
HVX_Vector sq = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, sq);
|
||||
}
|
||||
|
||||
// Include tail elements in the sum-of-squares using a predicate mask
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_Vector sq = Q6_Vqf32_vmpy_VsfVsf(v1, v1);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, sq);
|
||||
}
|
||||
|
||||
// Compute scale = 1/fmax(sqrt(sum), epsilon) entirely in HVX registers.
|
||||
// hvx_vec_rsqrt_f32 + hvx_vec_inverse_f32 avoids scalar extraction.
|
||||
HVX_Vector sum_sf = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_v));
|
||||
HVX_Vector rsqrt_v = hvx_vec_rsqrt_f32(sum_sf); // 1/sqrt(sum)
|
||||
HVX_Vector sqrt_v = hvx_vec_inverse_f32(rsqrt_v); // sqrt(sum)
|
||||
HVX_Vector epsilon_v = hvx_vec_splat_f32(epsilon);
|
||||
HVX_Vector denom_v = Q6_Vsf_vmax_VsfVsf(sqrt_v, epsilon_v); // fmax(sqrt(sum), epsilon)
|
||||
HVX_Vector scale_v = hvx_vec_inverse_f32(denom_v); // 1/fmax(sqrt(sum), epsilon)
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_Vector v1 = v_src[i];
|
||||
v_dst[i] = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(v1, scale_v));
|
||||
}
|
||||
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * 4);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_Vector result = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(v1, scale_v));
|
||||
hvx_vec_store_a(&v_dst[nvec], nloe * 4, result);
|
||||
}
|
||||
}
|
||||
|
||||
// F16 norm kernels: reduce and scale in f32 (via promote/narrow), matching the
|
||||
// precision-preserving pattern used by the flash-attn f16 kernels.
|
||||
|
||||
static inline void hvx_fast_rms_norm_f16(const uint8_t * restrict src,
|
||||
uint8_t * restrict dst,
|
||||
const int num_elems,
|
||||
float epsilon) {
|
||||
|
||||
const HVX_Vector * restrict v_src = (HVX_Vector *) src;
|
||||
HVX_Vector * restrict v_dst = (HVX_Vector *) dst;
|
||||
|
||||
const int nvec = num_elems / VLEN_FP16; // number of full f16 vectors
|
||||
const int nloe = num_elems % VLEN_FP16; // leftover elements
|
||||
|
||||
HVX_Vector sum_v = Q6_V_vsplat_R(0x00000000);
|
||||
HVX_Vector epsilon_v = hvx_vec_splat_f32(epsilon);
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v_src[i]);
|
||||
HVX_Vector p0 = Q6_V_lo_W(p);
|
||||
HVX_Vector p1 = Q6_V_hi_W(p);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, Q6_Vqf32_vmpy_VsfVsf(p0, p0));
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, Q6_Vqf32_vmpy_VsfVsf(p1, p1));
|
||||
}
|
||||
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * SIZEOF_FP16);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v1);
|
||||
HVX_Vector p0 = Q6_V_lo_W(p);
|
||||
HVX_Vector p1 = Q6_V_hi_W(p);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, Q6_Vqf32_vmpy_VsfVsf(p0, p0));
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, Q6_Vqf32_vmpy_VsfVsf(p1, p1));
|
||||
}
|
||||
|
||||
sum_v = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_v));
|
||||
|
||||
HVX_Vector t_v = hvx_vec_splat_f32((float) num_elems);
|
||||
HVX_Vector denom_v = hvx_vec_inverse_f32(t_v);
|
||||
HVX_Vector mean_v = Q6_Vqf32_vmpy_VsfVsf(sum_v, denom_v);
|
||||
HVX_Vector mean_epsilon_v = Q6_Vqf32_vadd_Vqf32Vsf(mean_v, epsilon_v);
|
||||
|
||||
HVX_Vector scale_v = hvx_vec_rsqrt_f32(Q6_Vsf_equals_Vqf32(mean_epsilon_v));
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v_src[i]);
|
||||
HVX_Vector r0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(p), scale_v));
|
||||
HVX_Vector r1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(p), scale_v));
|
||||
v_dst[i] = hvx_vec_f32_to_f16(r0, r1);
|
||||
}
|
||||
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * SIZEOF_FP16);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v1);
|
||||
HVX_Vector r0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(p), scale_v));
|
||||
HVX_Vector r1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(p), scale_v));
|
||||
HVX_Vector result = hvx_vec_f32_to_f16(r0, r1);
|
||||
hvx_vec_store_a(&v_dst[nvec], nloe * SIZEOF_FP16, result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_fast_norm_f16(const uint8_t * restrict src,
|
||||
uint8_t * restrict dst,
|
||||
const int num_elems,
|
||||
float epsilon) {
|
||||
|
||||
const HVX_Vector * restrict v_src = (HVX_Vector *) src;
|
||||
HVX_Vector * restrict v_dst = (HVX_Vector *) dst;
|
||||
|
||||
const int nvec = num_elems / VLEN_FP16;
|
||||
const int nloe = num_elems % VLEN_FP16;
|
||||
|
||||
HVX_Vector sum_sq_v = Q6_V_vsplat_R(0x00000000);
|
||||
HVX_Vector sum_x_v = Q6_V_vsplat_R(0x00000000);
|
||||
HVX_Vector epsilon_v = hvx_vec_splat_f32(epsilon);
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v_src[i]);
|
||||
HVX_Vector p0 = Q6_V_lo_W(p);
|
||||
HVX_Vector p1 = Q6_V_hi_W(p);
|
||||
sum_sq_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_sq_v, Q6_Vqf32_vmpy_VsfVsf(p0, p0));
|
||||
sum_sq_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_sq_v, Q6_Vqf32_vmpy_VsfVsf(p1, p1));
|
||||
sum_x_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_x_v, Q6_Vqf32_vadd_VsfVsf(p0, Q6_V_vzero()));
|
||||
sum_x_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_x_v, Q6_Vqf32_vadd_VsfVsf(p1, Q6_V_vzero()));
|
||||
}
|
||||
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * SIZEOF_FP16);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v1);
|
||||
HVX_Vector p0 = Q6_V_lo_W(p);
|
||||
HVX_Vector p1 = Q6_V_hi_W(p);
|
||||
sum_sq_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_sq_v, Q6_Vqf32_vmpy_VsfVsf(p0, p0));
|
||||
sum_sq_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_sq_v, Q6_Vqf32_vmpy_VsfVsf(p1, p1));
|
||||
sum_x_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_x_v, Q6_Vqf32_vadd_VsfVsf(p0, Q6_V_vzero()));
|
||||
sum_x_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_x_v, Q6_Vqf32_vadd_VsfVsf(p1, Q6_V_vzero()));
|
||||
}
|
||||
|
||||
sum_sq_v = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_sq_v));
|
||||
sum_x_v = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_x_v));
|
||||
|
||||
HVX_Vector t_v = hvx_vec_splat_f32((float) num_elems);
|
||||
HVX_Vector denom_v = hvx_vec_inverse_f32(t_v);
|
||||
HVX_Vector mean_sq_v = Q6_Vqf32_vmpy_VsfVsf(sum_sq_v, denom_v);
|
||||
HVX_Vector mean_x_v = Q6_Vqf32_vmpy_VsfVsf(sum_x_v, denom_v);
|
||||
HVX_Vector mean_x_sq_v = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(mean_x_v), Q6_Vsf_equals_Vqf32(mean_x_v));
|
||||
HVX_Vector var_v = Q6_Vqf32_vsub_Vqf32Vqf32(mean_sq_v, mean_x_sq_v);
|
||||
HVX_Vector var_epsilon_v = Q6_Vqf32_vadd_Vqf32Vsf(var_v, epsilon_v);
|
||||
|
||||
HVX_Vector scale_v = hvx_vec_rsqrt_f32(Q6_Vsf_equals_Vqf32(var_epsilon_v));
|
||||
HVX_Vector mean_x_b = hvx_vec_repl_f32(Q6_Vsf_equals_Vqf32(mean_x_v));
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v_src[i]);
|
||||
HVX_Vector d0 = Q6_Vqf32_vsub_VsfVsf(Q6_V_lo_W(p), mean_x_b);
|
||||
HVX_Vector d1 = Q6_Vqf32_vsub_VsfVsf(Q6_V_hi_W(p), mean_x_b);
|
||||
HVX_Vector r0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(d0), scale_v));
|
||||
HVX_Vector r1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(d1), scale_v));
|
||||
v_dst[i] = hvx_vec_f32_to_f16(r0, r1);
|
||||
}
|
||||
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * SIZEOF_FP16);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v1);
|
||||
HVX_Vector d0 = Q6_Vqf32_vsub_VsfVsf(Q6_V_lo_W(p), mean_x_b);
|
||||
HVX_Vector d1 = Q6_Vqf32_vsub_VsfVsf(Q6_V_hi_W(p), mean_x_b);
|
||||
HVX_Vector r0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(d0), scale_v));
|
||||
HVX_Vector r1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(d1), scale_v));
|
||||
HVX_Vector result = hvx_vec_f32_to_f16(r0, r1);
|
||||
hvx_vec_store_a(&v_dst[nvec], nloe * SIZEOF_FP16, result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_fast_l2_norm_f16(const uint8_t * restrict src,
|
||||
uint8_t * restrict dst,
|
||||
const int num_elems,
|
||||
float epsilon) {
|
||||
|
||||
const HVX_Vector * restrict v_src = (HVX_Vector *) src;
|
||||
HVX_Vector * restrict v_dst = (HVX_Vector *) dst;
|
||||
|
||||
const int nvec = num_elems / VLEN_FP16;
|
||||
const int nloe = num_elems % VLEN_FP16;
|
||||
|
||||
HVX_Vector sum_v = hvx_vec_splat_f32(0.0f);
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v_src[i]);
|
||||
HVX_Vector p0 = Q6_V_lo_W(p);
|
||||
HVX_Vector p1 = Q6_V_hi_W(p);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, Q6_Vqf32_vmpy_VsfVsf(p0, p0));
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, Q6_Vqf32_vmpy_VsfVsf(p1, p1));
|
||||
}
|
||||
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * SIZEOF_FP16);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v1);
|
||||
HVX_Vector p0 = Q6_V_lo_W(p);
|
||||
HVX_Vector p1 = Q6_V_hi_W(p);
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, Q6_Vqf32_vmpy_VsfVsf(p0, p0));
|
||||
sum_v = Q6_Vqf32_vadd_Vqf32Vqf32(sum_v, Q6_Vqf32_vmpy_VsfVsf(p1, p1));
|
||||
}
|
||||
|
||||
HVX_Vector sum_sf = hvx_vec_reduce_sum_f32(Q6_Vsf_equals_Vqf32(sum_v));
|
||||
HVX_Vector rsqrt_v = hvx_vec_rsqrt_f32(sum_sf);
|
||||
HVX_Vector sqrt_v = hvx_vec_inverse_f32(rsqrt_v);
|
||||
HVX_Vector epsilon_v = hvx_vec_splat_f32(epsilon);
|
||||
HVX_Vector denom_v = Q6_Vsf_vmax_VsfVsf(sqrt_v, epsilon_v);
|
||||
HVX_Vector scale_v = hvx_vec_inverse_f32(denom_v);
|
||||
|
||||
#pragma unroll(4)
|
||||
for (int i = 0; i < nvec; i++) {
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v_src[i]);
|
||||
HVX_Vector r0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(p), scale_v));
|
||||
HVX_Vector r1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(p), scale_v));
|
||||
v_dst[i] = hvx_vec_f32_to_f16(r0, r1);
|
||||
}
|
||||
|
||||
if (nloe > 0) {
|
||||
HVX_VectorPred bmask = Q6_Q_vsetq_R(nloe * SIZEOF_FP16);
|
||||
HVX_Vector v1 = Q6_V_vand_QV(bmask, v_src[nvec]);
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(v1);
|
||||
HVX_Vector r0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(p), scale_v));
|
||||
HVX_Vector r1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(p), scale_v));
|
||||
HVX_Vector result = hvx_vec_f32_to_f16(r0, r1);
|
||||
hvx_vec_store_a(&v_dst[nvec], nloe * SIZEOF_FP16, result);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HVX_NORM_H
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef HVX_POW_H
|
||||
#define HVX_POW_H
|
||||
|
||||
#include <math.h>
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-exp.h"
|
||||
#include "hvx-log.h"
|
||||
|
||||
// Approximates base^exponent element-wise for float vectors.
|
||||
// base must be a positive constant. exponent is an HVX f32 vector.
|
||||
// Uses base^x = exp(x * ln(base)).
|
||||
static inline HVX_Vector hvx_vec_pow_const_base_f32(float base, HVX_Vector exponent) {
|
||||
float ln_base = logf(base);
|
||||
HVX_Vector ln_base_v = hvx_vec_splat_f32(ln_base);
|
||||
HVX_Vector x = hvx_vec_mul_f32_f32(exponent, ln_base_v);
|
||||
|
||||
static const float kInf = INFINITY;
|
||||
static const float kMaxExp = 88.7228f;
|
||||
|
||||
const HVX_Vector max_exp = hvx_vec_splat_f32(kMaxExp);
|
||||
const HVX_Vector inf = hvx_vec_splat_f32(kInf);
|
||||
|
||||
return hvx_vec_exp_f32_guard(x, max_exp, inf);
|
||||
}
|
||||
|
||||
// Approximates base^exponent element-wise for float vectors.
|
||||
// base and exponent are HVX f32 vectors. base elements must be positive.
|
||||
// Uses base^exponent = exp(exponent * ln(base)).
|
||||
static inline HVX_Vector hvx_vec_pow_f32(HVX_Vector base, HVX_Vector exponent) {
|
||||
HVX_Vector ln_base = hvx_vec_log_f32(base);
|
||||
HVX_Vector x = hvx_vec_mul_f32_f32(exponent, ln_base);
|
||||
|
||||
static const float kInf = INFINITY;
|
||||
static const float kMaxExp = 88.7228f;
|
||||
|
||||
const HVX_Vector max_exp = hvx_vec_splat_f32(kMaxExp);
|
||||
const HVX_Vector inf = hvx_vec_splat_f32(kInf);
|
||||
|
||||
return hvx_vec_exp_f32_guard(x, max_exp, inf);
|
||||
}
|
||||
|
||||
#endif /* HVX_POW_H */
|
||||
@@ -0,0 +1,726 @@
|
||||
#ifndef HVX_QUANT_H
|
||||
#define HVX_QUANT_H
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "htp-tensor.h"
|
||||
#include "hvx-arith.h"
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-reduce.h"
|
||||
#include "hvx-repl.h"
|
||||
#include "hvx-utils.h"
|
||||
|
||||
#ifndef GGML_COMMON_DECL_C
|
||||
#define GGML_COMMON_DECL_C
|
||||
#endif
|
||||
#include "ggml-common.h"
|
||||
#include "ggml-impl.h"
|
||||
|
||||
static inline void hvx_quantize_row_q8_0_f32(void * restrict dst_ptr, const float * restrict src_ptr, int n) {
|
||||
const int nb = n / QK8_0;
|
||||
block_q8_0 * dst = (block_q8_0 *) dst_ptr;
|
||||
HVX_Vector zero = Q6_V_vzero();
|
||||
|
||||
int i = 0;
|
||||
for (; i + 3 < nb; i += 4) {
|
||||
HVX_Vector * vx = (HVX_Vector *) (src_ptr + i * QK8_0);
|
||||
|
||||
HVX_Vector vmax0_sf = hvx_vec_reduce_max_f32(hvx_vec_abs_f32(vx[0]));
|
||||
HVX_Vector vmax1_sf = hvx_vec_reduce_max_f32(hvx_vec_abs_f32(vx[1]));
|
||||
HVX_Vector vmax2_sf = hvx_vec_reduce_max_f32(hvx_vec_abs_f32(vx[2]));
|
||||
HVX_Vector vmax3_sf = hvx_vec_reduce_max_f32(hvx_vec_abs_f32(vx[3]));
|
||||
|
||||
HVX_Vector vx0_qf = Q6_Vqf32_vsub_VsfVsf(vx[0], zero);
|
||||
HVX_Vector vx1_qf = Q6_Vqf32_vsub_VsfVsf(vx[1], zero);
|
||||
HVX_Vector vx2_qf = Q6_Vqf32_vsub_VsfVsf(vx[2], zero);
|
||||
HVX_Vector vx3_qf = Q6_Vqf32_vsub_VsfVsf(vx[3], zero);
|
||||
|
||||
HVX_Vector vmax0_qf = Q6_Vqf32_vsub_VsfVsf(vmax0_sf, zero);
|
||||
HVX_Vector vmax1_qf = Q6_Vqf32_vsub_VsfVsf(vmax1_sf, zero);
|
||||
HVX_Vector vmax2_qf = Q6_Vqf32_vsub_VsfVsf(vmax2_sf, zero);
|
||||
HVX_Vector vmax3_qf = Q6_Vqf32_vsub_VsfVsf(vmax3_sf, zero);
|
||||
|
||||
HVX_Vector vmax01_hf = Q6_Vh_vdeal_Vh(Q6_Vhf_equals_Wqf32(Q6_W_vcombine_VV(vmax1_qf, vmax0_qf)));
|
||||
HVX_Vector vmax23_hf = Q6_Vh_vdeal_Vh(Q6_Vhf_equals_Wqf32(Q6_W_vcombine_VV(vmax3_qf, vmax2_qf)));
|
||||
|
||||
HVX_Vector vx01_hf = Q6_Vh_vdeal_Vh(Q6_Vhf_equals_Wqf32(Q6_W_vcombine_VV(vx1_qf, vx0_qf)));
|
||||
HVX_Vector vx23_hf = Q6_Vh_vdeal_Vh(Q6_Vhf_equals_Wqf32(Q6_W_vcombine_VV(vx3_qf, vx2_qf)));
|
||||
|
||||
HVX_Vector vd01_qf16 = Q6_Vqf16_vmpy_VhfVhf(vmax01_hf, Q6_Vh_vsplat_R(0x2008)); // 1.0 / 127.0
|
||||
HVX_Vector vd23_qf16 = Q6_Vqf16_vmpy_VhfVhf(vmax23_hf, Q6_Vh_vsplat_R(0x2008)); // 1.0 / 127.0
|
||||
HVX_Vector vd01_hf = Q6_Vhf_equals_Vqf16(vd01_qf16);
|
||||
HVX_Vector vd23_hf = Q6_Vhf_equals_Vqf16(vd23_qf16);
|
||||
|
||||
HVX_Vector vd01_inv_hf = hvx_vec_inverse_f16(vd01_hf);
|
||||
HVX_Vector vd23_inv_hf = hvx_vec_inverse_f16(vd23_hf);
|
||||
vx01_hf = Q6_Vhf_equals_Vqf16(Q6_Vqf16_vmpy_VhfVhf(vx01_hf, vd01_inv_hf));
|
||||
vx23_hf = Q6_Vhf_equals_Vqf16(Q6_Vqf16_vmpy_VhfVhf(vx23_hf, vd23_inv_hf));
|
||||
|
||||
HVX_Vector vx01_i16 = hvx_vec_i16_from_hf_rnd_sat(vx01_hf);
|
||||
HVX_Vector vx23_i16 = hvx_vec_i16_from_hf_rnd_sat(vx23_hf);
|
||||
HVX_Vector vx_i8 = Q6_Vb_vpack_VhVh_sat(vx23_i16, vx01_i16);
|
||||
|
||||
hvx_vec_store_u(&dst[i + 0].d, 2, vd01_hf);
|
||||
hvx_vec_store_u(dst[i + 0].qs, 32, vx_i8);
|
||||
|
||||
hvx_vec_store_u(&dst[i + 1].d, 2, Q6_V_vror_VR(vd01_hf, 64));
|
||||
hvx_vec_store_u(dst[i + 1].qs, 32, Q6_V_vror_VR(vx_i8, 32));
|
||||
|
||||
hvx_vec_store_u(&dst[i + 2].d, 2, vd23_hf);
|
||||
hvx_vec_store_u(dst[i + 2].qs, 32, Q6_V_vror_VR(vx_i8, 64));
|
||||
|
||||
hvx_vec_store_u(&dst[i + 3].d, 2, Q6_V_vror_VR(vd23_hf, 64));
|
||||
hvx_vec_store_u(dst[i + 3].qs, 32, Q6_V_vror_VR(vx_i8, 96));
|
||||
}
|
||||
|
||||
for (; i < nb; i++) {
|
||||
const float * block_src = src_ptr + i * QK8_0;
|
||||
HVX_Vector vx = *(const HVX_UVector *) block_src;
|
||||
HVX_Vector v_abs = hvx_vec_abs_f32(vx);
|
||||
HVX_Vector v_max = hvx_vec_reduce_max_f32(v_abs);
|
||||
float amax = hvx_vec_get_f32(v_max);
|
||||
|
||||
const float d = amax / 127.0f;
|
||||
const float id = d ? (1.0f / d) : 0.0f;
|
||||
dst[i].d = GGML_FP32_TO_FP16(d);
|
||||
|
||||
HVX_Vector vid = hvx_vec_splat_f32(id);
|
||||
HVX_Vector v_scaled = hvx_vec_mul_f32_f32(vx, vid);
|
||||
HVX_Vector v_scaled_qf = Q6_Vqf32_vsub_VsfVsf(v_scaled, zero);
|
||||
HVX_Vector v_scaled_hf = Q6_Vh_vdeal_Vh(Q6_Vhf_equals_Wqf32(Q6_W_vcombine_VV(zero, v_scaled_qf)));
|
||||
HVX_Vector v_i16 = hvx_vec_i16_from_hf_rnd_sat(v_scaled_hf);
|
||||
HVX_Vector v_i8 = Q6_Vb_vpack_VhVh_sat(zero, v_i16);
|
||||
|
||||
hvx_vec_store_u(dst[i].qs, 32, v_i8);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_q8_0_f32(float * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
const int nb = n / QK8_0;
|
||||
const block_q8_0 * src = (const block_q8_0 *) src_ptr;
|
||||
|
||||
for (int i = 0; i < nb; i++) {
|
||||
HVX_Vector vd_f16 = Q6_Vh_vsplat_R(*(const int16_t *) &src[i].d);
|
||||
HVX_VectorPair vp_f32 = hvx_vec_f16_to_f32(vd_f16);
|
||||
HVX_Vector vd = Q6_V_lo_W(vp_f32);
|
||||
|
||||
HVX_Vector vq_i8 = *(const HVX_UVector *) src[i].qs;
|
||||
|
||||
HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(vq_i8);
|
||||
HVX_Vector v_i16 = Q6_V_lo_W(p16);
|
||||
HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(v_i16);
|
||||
HVX_Vector v_i32 = Q6_V_lo_W(p32);
|
||||
|
||||
HVX_Vector v_f32 = Q6_Vsf_equals_Vw(v_i32);
|
||||
HVX_Vector res = hvx_vec_mul_f32_f32(v_f32, vd);
|
||||
|
||||
float * block_dst = dst_ptr + i * QK8_0;
|
||||
hvx_vmem(block_dst) = res;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_q8_0_f16(__fp16 * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
const int nb = n / QK8_0;
|
||||
const block_q8_0 * src = (const block_q8_0 *) src_ptr;
|
||||
|
||||
for (int i = nb - 1; i >= 0; i--) {
|
||||
HVX_Vector vd_f16 = Q6_Vh_vsplat_R(*(const int16_t *) &src[i].d);
|
||||
HVX_VectorPair vp_f32 = hvx_vec_f16_to_f32(vd_f16);
|
||||
HVX_Vector vd = Q6_V_lo_W(vp_f32);
|
||||
|
||||
HVX_Vector vq_i8 = *(const HVX_UVector *) src[i].qs;
|
||||
|
||||
HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(vq_i8);
|
||||
HVX_Vector v_i16 = Q6_V_lo_W(p16);
|
||||
HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(v_i16);
|
||||
HVX_Vector v_i32 = Q6_V_lo_W(p32);
|
||||
|
||||
HVX_Vector v_f32 = Q6_Vsf_equals_Vw(v_i32);
|
||||
HVX_Vector res_f32 = hvx_vec_mul_f32_f32(v_f32, vd);
|
||||
|
||||
HVX_Vector res_f16 = hvx_vec_f32_to_f16(res_f32, Q6_V_vzero());
|
||||
|
||||
__fp16 * block_dst = dst_ptr + i * QK8_0;
|
||||
hvx_vec_store_u(block_dst, QK8_0 * sizeof(__fp16), res_f16);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_f16_f32(float * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
const uint32_t nvec = n / VLEN_FP32;
|
||||
const uint32_t nloe = n % VLEN_FP32;
|
||||
const _Float16 * src = (const _Float16 *) src_ptr;
|
||||
|
||||
for (uint32_t i = 0; i < nvec; i++) {
|
||||
HVX_Vector v_f16 = *(const HVX_UVector *) (src + i * VLEN_FP32);
|
||||
HVX_VectorPair vp_f32 = hvx_vec_f16_to_f32(v_f16);
|
||||
HVX_Vector res = Q6_V_lo_W(vp_f32);
|
||||
|
||||
hvx_vmem(dst_ptr + i * VLEN_FP32) = res;
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_Vector v_f16 = *(const HVX_UVector *) (src + nvec * VLEN_FP32);
|
||||
HVX_VectorPair vp_f32 = hvx_vec_f16_to_f32(v_f16);
|
||||
HVX_Vector res = Q6_V_lo_W(vp_f32);
|
||||
|
||||
hvx_vec_store_a(dst_ptr + nvec * VLEN_FP32, nloe * sizeof(float), res);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_bf16_f32(float * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
const uint32_t nvec = n / VLEN_FP32;
|
||||
const uint32_t nloe = n % VLEN_FP32;
|
||||
const uint16_t * src = (const uint16_t *) src_ptr;
|
||||
|
||||
for (uint32_t i = 0; i < nvec; i++) {
|
||||
HVX_Vector v_bf16 = *(const HVX_UVector *) (src + i * VLEN_FP32);
|
||||
HVX_VectorPair p = Q6_Ww_vunpack_Vh(v_bf16);
|
||||
HVX_Vector res = Q6_Vw_vasl_VwR(Q6_V_lo_W(p), 16);
|
||||
|
||||
hvx_vmem(dst_ptr + i * VLEN_FP32) = res;
|
||||
}
|
||||
|
||||
if (nloe) {
|
||||
HVX_Vector v_bf16 = *(const HVX_UVector *) (src + nvec * VLEN_FP32);
|
||||
HVX_VectorPair p = Q6_Ww_vunpack_Vh(v_bf16);
|
||||
HVX_Vector res = Q6_Vw_vasl_VwR(Q6_V_lo_W(p), 16);
|
||||
|
||||
hvx_vec_store_a(dst_ptr + nvec * VLEN_FP32, nloe * sizeof(float), res);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_q1_0_f32(float * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
static const uint32_t __attribute__((aligned(128))) bit_mask[32] = {
|
||||
1u << 0, 1u << 1, 1u << 2, 1u << 3, 1u << 4, 1u << 5, 1u << 6, 1u << 7,
|
||||
1u << 8, 1u << 9, 1u << 10, 1u << 11, 1u << 12, 1u << 13, 1u << 14, 1u << 15,
|
||||
1u << 16, 1u << 17, 1u << 18, 1u << 19, 1u << 20, 1u << 21, 1u << 22, 1u << 23,
|
||||
1u << 24, 1u << 25, 1u << 26, 1u << 27, 1u << 28, 1u << 29, 1u << 30, 1u << 31,
|
||||
};
|
||||
const int nb = n / QK1_0;
|
||||
const block_q1_0 * src = (const block_q1_0 *) src_ptr;
|
||||
const HVX_Vector v_bit_mask = *(const HVX_Vector *) bit_mask;
|
||||
const HVX_Vector v_zero = Q6_V_vzero();
|
||||
const HVX_Vector v_sign_bit = Q6_V_vsplat_R(0x80000000);
|
||||
|
||||
for (int i = 0; i < nb; i++) {
|
||||
HVX_Vector vd_f16 = Q6_Vh_vsplat_R(*(const int16_t *) &src[i].d);
|
||||
HVX_Vector vd = Q6_V_lo_W(hvx_vec_f16_to_f32(vd_f16));
|
||||
HVX_Vector neg_vd = Q6_V_vxor_VV(vd, v_sign_bit);
|
||||
HVX_Vector v_qs = *(const HVX_UVector *) src[i].qs;
|
||||
|
||||
float * block_dst = dst_ptr + i * QK1_0;
|
||||
#pragma unroll
|
||||
for (int q = 0; q < 4; q++) {
|
||||
HVX_Vector v_bits = hvx_vec_repl_u32(q == 0 ? v_qs : Q6_V_vror_VR(v_qs, 4 * q));
|
||||
HVX_VectorPred q_zero = Q6_Q_vcmp_eq_VwVw(Q6_V_vand_VV(v_bits, v_bit_mask), v_zero);
|
||||
hvx_vmem(block_dst + q * 32) = Q6_V_vmux_QVV(q_zero, neg_vd, vd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_q2_0_f32(float * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
const int nb = n / QK2_0;
|
||||
const block_q2_0 * src = (const block_q2_0 *) src_ptr;
|
||||
const HVX_Vector mask_03 = Q6_Vb_vsplat_R(0x03);
|
||||
const HVX_Vector v_one = Q6_Vb_vsplat_R(1);
|
||||
|
||||
for (int i = 0; i < nb; i++) {
|
||||
HVX_Vector vd_f16 = Q6_Vh_vsplat_R(*(const int16_t *) &src[i].d);
|
||||
HVX_Vector vd = Q6_V_lo_W(hvx_vec_f16_to_f32(vd_f16));
|
||||
|
||||
HVX_Vector vq = *(const HVX_UVector *) src[i].qs;
|
||||
HVX_Vector v0 = Q6_V_vand_VV(vq, mask_03);
|
||||
HVX_Vector v1 = Q6_V_vand_VV(Q6_Vub_vlsr_VubR(vq, 2), mask_03);
|
||||
HVX_Vector v2 = Q6_V_vand_VV(Q6_Vub_vlsr_VubR(vq, 4), mask_03);
|
||||
HVX_Vector v3 = Q6_Vub_vlsr_VubR(vq, 6);
|
||||
|
||||
HVX_Vector v01 = Q6_V_lo_W(Q6_W_vshuff_VVR(v1, v0, -1));
|
||||
HVX_Vector v23 = Q6_V_lo_W(Q6_W_vshuff_VVR(v3, v2, -1));
|
||||
HVX_Vector v64 = Q6_Vb_vsub_VbVb(Q6_V_lo_W(Q6_W_vshuff_VVR(v23, v01, -2)), v_one);
|
||||
|
||||
HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(v64);
|
||||
HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16));
|
||||
|
||||
float * block_dst = dst_ptr + i * QK2_0;
|
||||
hvx_vmem(block_dst + 0) = hvx_vec_mul_f32_f32(Q6_Vsf_equals_Vw(Q6_V_lo_W(p32)), vd);
|
||||
hvx_vmem(block_dst + 32) = hvx_vec_mul_f32_f32(Q6_Vsf_equals_Vw(Q6_V_hi_W(p32)), vd);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_q4_0_f32(float * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
const int nb = n / QK4_0;
|
||||
const block_q4_0 * src = (const block_q4_0 *) src_ptr;
|
||||
|
||||
const HVX_VectorPred q16 = Q6_Q_vsetq_R(16);
|
||||
const HVX_Vector v_eight = Q6_Vb_vsplat_R(8);
|
||||
const HVX_Vector v_zero = Q6_V_vzero();
|
||||
|
||||
for (int i = 0; i < nb; i++) {
|
||||
HVX_Vector vd_f16 = Q6_Vh_vsplat_R(*(const int16_t *) &src[i].d);
|
||||
HVX_VectorPair vp_f32 = hvx_vec_f16_to_f32(vd_f16);
|
||||
HVX_Vector vd = Q6_V_lo_W(vp_f32);
|
||||
|
||||
HVX_Vector vq = *(const HVX_UVector *) src[i].qs;
|
||||
HVX_Vector v_lo = Q6_V_vand_VV(vq, Q6_Vb_vsplat_R(0x0F));
|
||||
HVX_Vector v_hi = Q6_Vub_vlsr_VubR(vq, 4);
|
||||
|
||||
HVX_Vector v_lo_s8 = Q6_Vb_vsub_VbVb(v_lo, v_eight);
|
||||
HVX_Vector v_hi_s8 = Q6_Vb_vsub_VbVb(v_hi, v_eight);
|
||||
|
||||
HVX_Vector v32 = Q6_V_vmux_QVV(q16, v_lo_s8, Q6_V_vlalign_VVR(v_hi_s8, v_zero, 16));
|
||||
|
||||
HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(v32);
|
||||
HVX_Vector v_i16 = Q6_V_lo_W(p16);
|
||||
HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(v_i16);
|
||||
HVX_Vector v_i32 = Q6_V_lo_W(p32);
|
||||
|
||||
HVX_Vector v_f32 = Q6_Vsf_equals_Vw(v_i32);
|
||||
HVX_Vector res = hvx_vec_mul_f32_f32(v_f32, vd);
|
||||
|
||||
hvx_vmem(dst_ptr + i * QK4_0) = res;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_q4_k_f32(float * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
const int nb = n / QK_K;
|
||||
const block_q4_K * src = (const block_q4_K *) src_ptr;
|
||||
|
||||
for (int i = 0; i < nb; i++) {
|
||||
const float d = GGML_FP16_TO_FP32(src[i].d);
|
||||
const float min = GGML_FP16_TO_FP32(src[i].dmin);
|
||||
const uint8_t * sc = src[i].scales;
|
||||
|
||||
float * y = dst_ptr + i * QK_K;
|
||||
|
||||
for (int step = 0; step < 4; ++step) {
|
||||
uint8_t sc1, m1_u, sc2, m2_u;
|
||||
if (step < 2) {
|
||||
sc1 = sc[2 * step + 0] & 63;
|
||||
m1_u = sc[2 * step + 4] & 63;
|
||||
sc2 = sc[2 * step + 1] & 63;
|
||||
m2_u = sc[2 * step + 5] & 63;
|
||||
} else {
|
||||
const int s = step - 2;
|
||||
sc1 = (sc[8 + 2 * s + 0] & 0xF) | ((sc[2 * s + 0] >> 6) << 4);
|
||||
m1_u = (sc[8 + 2 * s + 0] >> 4) | ((sc[4 + 2 * s + 0] >> 6) << 4);
|
||||
sc2 = (sc[8 + 2 * s + 1] & 0xF) | ((sc[2 * s + 1] >> 6) << 4);
|
||||
m2_u = (sc[8 + 2 * s + 1] >> 4) | ((sc[4 + 2 * s + 1] >> 6) << 4);
|
||||
}
|
||||
|
||||
HVX_Vector vq = *(const HVX_UVector *) (src[i].qs + step * 32);
|
||||
HVX_Vector v_lo = Q6_V_vand_VV(vq, Q6_Vb_vsplat_R(0x0F));
|
||||
HVX_Vector v_hi = Q6_Vub_vlsr_VubR(vq, 4);
|
||||
|
||||
HVX_VectorPair p16_lo = Q6_Wuh_vunpack_Vub(v_lo);
|
||||
HVX_VectorPair p32_lo = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16_lo));
|
||||
HVX_Vector v_i32_lo = Q6_V_lo_W(p32_lo);
|
||||
HVX_Vector v_f32_lo = Q6_Vsf_equals_Vw(v_i32_lo);
|
||||
|
||||
HVX_Vector vd1 = hvx_vec_splat_f32(d * sc1);
|
||||
HVX_Vector vm1 = hvx_vec_splat_f32(min * m1_u);
|
||||
HVX_Vector v_out1 = hvx_vec_sub_f32_f32(hvx_vec_mul_f32_f32(v_f32_lo, vd1), vm1);
|
||||
hvx_vmem(y + 0) = v_out1;
|
||||
|
||||
HVX_VectorPair p16_hi = Q6_Wuh_vunpack_Vub(v_hi);
|
||||
HVX_VectorPair p32_hi = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16_hi));
|
||||
HVX_Vector v_i32_hi = Q6_V_lo_W(p32_hi);
|
||||
HVX_Vector v_f32_hi = Q6_Vsf_equals_Vw(v_i32_hi);
|
||||
|
||||
HVX_Vector vd2 = hvx_vec_splat_f32(d * sc2);
|
||||
HVX_Vector vm2 = hvx_vec_splat_f32(min * m2_u);
|
||||
HVX_Vector v_out2 = hvx_vec_sub_f32_f32(hvx_vec_mul_f32_f32(v_f32_hi, vd2), vm2);
|
||||
hvx_vmem(y + 32) = v_out2;
|
||||
|
||||
y += 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_q5_k_f32(float * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
const int nb = n / QK_K;
|
||||
const block_q5_K * src = (const block_q5_K *) src_ptr;
|
||||
|
||||
for (int i = 0; i < nb; i++) {
|
||||
const float d = GGML_FP16_TO_FP32(src[i].d);
|
||||
const float min = GGML_FP16_TO_FP32(src[i].dmin);
|
||||
const uint8_t * sc = src[i].scales;
|
||||
|
||||
HVX_Vector v_qh = *(const HVX_UVector *) src[i].qh;
|
||||
float * y = dst_ptr + i * QK_K;
|
||||
|
||||
for (int step = 0; step < 4; ++step) {
|
||||
uint8_t sc1, m1_u, sc2, m2_u;
|
||||
if (step < 2) {
|
||||
sc1 = sc[2 * step + 0] & 63;
|
||||
m1_u = sc[2 * step + 4] & 63;
|
||||
sc2 = sc[2 * step + 1] & 63;
|
||||
m2_u = sc[2 * step + 5] & 63;
|
||||
} else {
|
||||
const int s = step - 2;
|
||||
sc1 = (sc[8 + 2 * s + 0] & 0xF) | ((sc[2 * s + 0] >> 6) << 4);
|
||||
m1_u = (sc[8 + 2 * s + 0] >> 4) | ((sc[4 + 2 * s + 0] >> 6) << 4);
|
||||
sc2 = (sc[8 + 2 * s + 1] & 0xF) | ((sc[2 * s + 1] >> 6) << 4);
|
||||
m2_u = (sc[8 + 2 * s + 1] >> 4) | ((sc[4 + 2 * s + 1] >> 6) << 4);
|
||||
}
|
||||
|
||||
HVX_Vector v_ql = *(const HVX_UVector *) (src[i].qs + step * 32);
|
||||
HVX_Vector v_lo = Q6_V_vand_VV(v_ql, Q6_Vb_vsplat_R(0x0F));
|
||||
HVX_Vector v_hi = Q6_Vub_vlsr_VubR(v_ql, 4);
|
||||
|
||||
HVX_Vector v_h1 = Q6_Vw_vasl_VwR(Q6_V_vand_VV(Q6_Vub_vlsr_VubR(v_qh, 2 * step), Q6_Vb_vsplat_R(1)), 4);
|
||||
HVX_Vector v_val1 = Q6_V_vor_VV(v_lo, v_h1);
|
||||
|
||||
HVX_VectorPair p16_1 = Q6_Wuh_vunpack_Vub(v_val1);
|
||||
HVX_VectorPair p32_1 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16_1));
|
||||
HVX_Vector v_i32_1 = Q6_V_lo_W(p32_1);
|
||||
HVX_Vector v_f32_1 = Q6_Vsf_equals_Vw(v_i32_1);
|
||||
|
||||
HVX_Vector vd1 = hvx_vec_splat_f32(d * sc1);
|
||||
HVX_Vector vm1 = hvx_vec_splat_f32(min * m1_u);
|
||||
HVX_Vector v_out1 = hvx_vec_sub_f32_f32(hvx_vec_mul_f32_f32(v_f32_1, vd1), vm1);
|
||||
hvx_vmem(y + 0) = v_out1;
|
||||
|
||||
HVX_Vector v_h2 = Q6_Vw_vasl_VwR(Q6_V_vand_VV(Q6_Vub_vlsr_VubR(v_qh, 2 * step + 1), Q6_Vb_vsplat_R(1)), 4);
|
||||
HVX_Vector v_val2 = Q6_V_vor_VV(v_hi, v_h2);
|
||||
|
||||
HVX_VectorPair p16_2 = Q6_Wuh_vunpack_Vub(v_val2);
|
||||
HVX_VectorPair p32_2 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16_2));
|
||||
HVX_Vector v_i32_2 = Q6_V_lo_W(p32_2);
|
||||
HVX_Vector v_f32_2 = Q6_Vsf_equals_Vw(v_i32_2);
|
||||
|
||||
HVX_Vector vd2 = hvx_vec_splat_f32(d * sc2);
|
||||
HVX_Vector vm2 = hvx_vec_splat_f32(min * m2_u);
|
||||
HVX_Vector v_out2 = hvx_vec_sub_f32_f32(hvx_vec_mul_f32_f32(v_f32_2, vd2), vm2);
|
||||
hvx_vmem(y + 32) = v_out2;
|
||||
|
||||
y += 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_flat_q6_k_f32(float * restrict dst_ptr, const void * restrict src_ptr, uint32_t n) {
|
||||
const int nb = n / QK_K;
|
||||
const block_q6_K * src = (const block_q6_K *) src_ptr;
|
||||
|
||||
const HVX_VectorPred q64 = Q6_Q_vsetq_R(64);
|
||||
const HVX_Vector v32_b = Q6_Vb_vsplat_R(32);
|
||||
const HVX_Vector mask_0f = Q6_Vb_vsplat_R(0x0F);
|
||||
const HVX_Vector mask_03 = Q6_Vb_vsplat_R(0x03);
|
||||
|
||||
for (int i = 0; i < nb; i++) {
|
||||
const float d = GGML_FP16_TO_FP32(src[i].d);
|
||||
|
||||
const uint8_t * restrict ql = src[i].ql;
|
||||
const uint8_t * restrict qh = src[i].qh;
|
||||
const int8_t * restrict sc = src[i].scales;
|
||||
|
||||
float * y = dst_ptr + i * QK_K;
|
||||
|
||||
for (int half = 0; half < 2; ++half) {
|
||||
float s[8];
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
s[j] = d * sc[j];
|
||||
}
|
||||
|
||||
HVX_Vector vscale1 = Q6_V_vmux_QVV(q64, hvx_vec_splat_f32(s[0]), hvx_vec_splat_f32(s[1]));
|
||||
HVX_Vector vscale2 = Q6_V_vmux_QVV(q64, hvx_vec_splat_f32(s[2]), hvx_vec_splat_f32(s[3]));
|
||||
HVX_Vector vscale3 = Q6_V_vmux_QVV(q64, hvx_vec_splat_f32(s[4]), hvx_vec_splat_f32(s[5]));
|
||||
HVX_Vector vscale4 = Q6_V_vmux_QVV(q64, hvx_vec_splat_f32(s[6]), hvx_vec_splat_f32(s[7]));
|
||||
|
||||
HVX_Vector v_ql0 = *(const HVX_UVector *) (ql);
|
||||
HVX_Vector v_ql1 = *(const HVX_UVector *) (ql + 32);
|
||||
HVX_Vector v_qh = *(const HVX_UVector *) (qh);
|
||||
|
||||
HVX_Vector ql0_lo = Q6_V_vand_VV(v_ql0, mask_0f);
|
||||
HVX_Vector qh_0 = Q6_Vw_vasl_VwR(Q6_V_vand_VV(v_qh, mask_03), 4);
|
||||
HVX_Vector v_q1 = Q6_Vb_vsub_VbVb(Q6_V_vor_VV(ql0_lo, qh_0), v32_b);
|
||||
|
||||
HVX_Vector ql1_lo = Q6_V_vand_VV(v_ql1, mask_0f);
|
||||
HVX_Vector qh_1 = Q6_Vw_vasl_VwR(Q6_V_vand_VV(Q6_Vub_vlsr_VubR(v_qh, 2), mask_03), 4);
|
||||
HVX_Vector v_q2 = Q6_Vb_vsub_VbVb(Q6_V_vor_VV(ql1_lo, qh_1), v32_b);
|
||||
|
||||
HVX_Vector ql0_hi = Q6_Vub_vlsr_VubR(v_ql0, 4);
|
||||
HVX_Vector qh_2 = Q6_Vw_vasl_VwR(Q6_V_vand_VV(Q6_Vub_vlsr_VubR(v_qh, 4), mask_03), 4);
|
||||
HVX_Vector v_q3 = Q6_Vb_vsub_VbVb(Q6_V_vor_VV(ql0_hi, qh_2), v32_b);
|
||||
|
||||
HVX_Vector ql1_hi = Q6_Vub_vlsr_VubR(v_ql1, 4);
|
||||
HVX_Vector qh_3 = Q6_Vw_vasl_VwR(Q6_V_vand_VV(Q6_Vub_vlsr_VubR(v_qh, 6), mask_03), 4);
|
||||
HVX_Vector v_q4 = Q6_Vb_vsub_VbVb(Q6_V_vor_VV(ql1_hi, qh_3), v32_b);
|
||||
|
||||
HVX_VectorPair p16_1 = Q6_Wh_vunpack_Vb(v_q1);
|
||||
HVX_VectorPair p32_1 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16_1));
|
||||
HVX_Vector v_f32_1 = Q6_Vsf_equals_Vw(Q6_V_lo_W(p32_1));
|
||||
hvx_vmem(y + 0) = hvx_vec_mul_f32_f32(v_f32_1, vscale1);
|
||||
|
||||
HVX_VectorPair p16_2 = Q6_Wh_vunpack_Vb(v_q2);
|
||||
HVX_VectorPair p32_2 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16_2));
|
||||
HVX_Vector v_f32_2 = Q6_Vsf_equals_Vw(Q6_V_lo_W(p32_2));
|
||||
hvx_vmem(y + 32) = hvx_vec_mul_f32_f32(v_f32_2, vscale2);
|
||||
|
||||
HVX_VectorPair p16_3 = Q6_Wh_vunpack_Vb(v_q3);
|
||||
HVX_VectorPair p32_3 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16_3));
|
||||
HVX_Vector v_f32_3 = Q6_Vsf_equals_Vw(Q6_V_lo_W(p32_3));
|
||||
hvx_vmem(y + 64) = hvx_vec_mul_f32_f32(v_f32_3, vscale3);
|
||||
|
||||
HVX_VectorPair p16_4 = Q6_Wh_vunpack_Vb(v_q4);
|
||||
HVX_VectorPair p32_4 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16_4));
|
||||
HVX_Vector v_f32_4 = Q6_Vsf_equals_Vw(Q6_V_lo_W(p32_4));
|
||||
hvx_vmem(y + 96) = hvx_vec_mul_f32_f32(v_f32_4, vscale4);
|
||||
|
||||
y += 128;
|
||||
ql += 64;
|
||||
qh += 32;
|
||||
sc += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bytes row, row + 32, row + 64 and row + 96 of v, to bytes 0..3
|
||||
static inline HVX_Vector hvx_dequantize_gather4(HVX_Vector v, uint32_t row) {
|
||||
v = Q6_V_vror_VR(v, row);
|
||||
HVX_Vector packed = Q6_V_vmux_QVV(Q6_Q_vsetq_R(1), v, Q6_V_vror_VR(v, 31));
|
||||
packed = Q6_V_vmux_QVV(Q6_Q_vsetq_R(2), packed, Q6_V_vror_VR(v, 62));
|
||||
return Q6_V_vmux_QVV(Q6_Q_vsetq_R(3), packed, Q6_V_vror_VR(v, 93));
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_tiled_q8_0_f32(float * dst_spad, const uint8_t * src_spad, uint32_t n_k_tiles, uint32_t tile_stride, uint32_t row) {
|
||||
const HVX_VectorPred first2 = Q6_Q_vsetq_R(2);
|
||||
const HVX_VectorPred first4 = Q6_Q_vsetq_R(4);
|
||||
|
||||
for (uint32_t k_tile = 0; k_tile < n_k_tiles; ++k_tile) {
|
||||
const uint8_t * tile = src_spad + k_tile * tile_stride;
|
||||
float * dst = dst_spad + k_tile * HTP_TENSOR_TILE_N_COLS;
|
||||
|
||||
HVX_Vector vq = Q6_V_vzero();
|
||||
for (int group = 7; group >= 0; --group) {
|
||||
const HVX_Vector v = Q6_V_vror_VR(hvx_vmem(tile + group * VLEN), 2 * row);
|
||||
const HVX_Vector packed = Q6_V_vmux_QVV(first2, v, Q6_V_vror_VR(v, 62));
|
||||
vq = Q6_V_vmux_QVV(first4, packed, Q6_V_vror_VR(vq, VLEN - 4));
|
||||
}
|
||||
|
||||
const HVX_Vector scales = hvx_vmem(tile + 1024);
|
||||
const HVX_Vector scale_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 2 * row));
|
||||
const HVX_Vector scale = Q6_V_lo_W(hvx_vec_f16_to_f32(scale_hf));
|
||||
const HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(vq);
|
||||
const HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16));
|
||||
HVX_Vector values = hvx_vec_mul_f32_f32(Q6_Vsf_equals_Vw(Q6_V_lo_W(p32)), scale);
|
||||
*(HVX_Vector *) dst = values;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_tiled_q4_0_f32(float * dst_spad, const uint8_t * src_spad, uint32_t n_k_tiles, uint32_t tile_stride, uint32_t row) {
|
||||
const HVX_VectorPred first4 = Q6_Q_vsetq_R(4);
|
||||
|
||||
for (uint32_t k_tile = 0; k_tile < n_k_tiles; ++k_tile) {
|
||||
const uint8_t * tile = src_spad + k_tile * tile_stride;
|
||||
float * dst = dst_spad + k_tile * HTP_TENSOR_TILE_N_COLS;
|
||||
|
||||
HVX_Vector vq = Q6_V_vzero();
|
||||
for (int group = 3; group >= 0; --group) {
|
||||
vq = Q6_V_vmux_QVV(first4, hvx_dequantize_gather4(hvx_vmem(tile + group * VLEN), row), Q6_V_vror_VR(vq, VLEN - 4));
|
||||
}
|
||||
|
||||
const HVX_Vector lo = Q6_V_vand_VV(vq, Q6_Vb_vsplat_R(0x0F));
|
||||
const HVX_Vector hi = Q6_Vub_vlsr_VubR(vq, 4);
|
||||
vq = Q6_V_lo_W(Q6_W_vshuff_VVR(hi, lo, -1));
|
||||
vq = Q6_Vb_vsub_VbVb(vq, Q6_Vb_vsplat_R(8));
|
||||
|
||||
const HVX_Vector scales = hvx_vmem(tile + 512);
|
||||
const HVX_Vector scale_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 2 * row));
|
||||
const HVX_Vector scale = Q6_V_lo_W(hvx_vec_f16_to_f32(scale_hf));
|
||||
const HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(vq);
|
||||
const HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16));
|
||||
HVX_Vector values = hvx_vec_mul_f32_f32(Q6_Vsf_equals_Vw(Q6_V_lo_W(p32)), scale);
|
||||
*(HVX_Vector *) dst = values;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_tiled_q4_k_f32(float * dst_spad, const uint8_t * src_spad, uint32_t n_k_tiles, uint32_t tile_stride, uint32_t row) {
|
||||
const HVX_VectorPred first4 = Q6_Q_vsetq_R(4);
|
||||
|
||||
for (uint32_t k_tile = 0; k_tile < n_k_tiles; ++k_tile) {
|
||||
const uint8_t * tile = src_spad + k_tile * tile_stride;
|
||||
float * dst = dst_spad + k_tile * HTP_TENSOR_TILE_N_COLS;
|
||||
|
||||
HVX_Vector vq = Q6_V_vzero();
|
||||
for (int group = 3; group >= 0; --group) {
|
||||
vq = Q6_V_vmux_QVV(first4, hvx_dequantize_gather4(hvx_vmem(tile + group * VLEN), row), Q6_V_vror_VR(vq, VLEN - 4));
|
||||
}
|
||||
|
||||
const HVX_Vector lo = Q6_V_vand_VV(vq, Q6_Vb_vsplat_R(0x0F));
|
||||
const HVX_Vector hi = Q6_Vub_vlsr_VubR(vq, 4);
|
||||
vq = Q6_V_lo_W(Q6_W_vshuff_VVR(hi, lo, -1));
|
||||
|
||||
const HVX_Vector scales = hvx_vmem(tile + 512);
|
||||
const HVX_Vector scale_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 4 * row));
|
||||
const HVX_Vector scale = Q6_V_lo_W(hvx_vec_f16_to_f32(scale_hf));
|
||||
const HVX_Vector offset_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 4 * row + 2));
|
||||
const HVX_Vector offset = Q6_V_lo_W(hvx_vec_f16_to_f32(offset_hf));
|
||||
|
||||
const HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(vq);
|
||||
const HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16));
|
||||
HVX_Vector values = hvx_vec_mul_f32_f32(Q6_Vsf_equals_Vw(Q6_V_lo_W(p32)), scale);
|
||||
*(HVX_Vector *) dst = hvx_vec_add_f32_f32(values, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_tiled_q5_k_f32(float * dst_spad, const uint8_t * src_spad, uint32_t n_k_tiles, uint32_t tile_stride, uint32_t row) {
|
||||
const HVX_VectorPred first4 = Q6_Q_vsetq_R(4);
|
||||
const HVX_Vector one = Q6_Vb_vsplat_R(1);
|
||||
|
||||
for (uint32_t k_tile = 0; k_tile < n_k_tiles; ++k_tile) {
|
||||
const uint8_t * tile = src_spad + k_tile * tile_stride;
|
||||
float * dst = dst_spad + k_tile * HTP_TENSOR_TILE_N_COLS;
|
||||
|
||||
const HVX_Vector p5 = hvx_dequantize_gather4(hvx_vmem(tile + 640), row);
|
||||
HVX_Vector vq = Q6_V_vzero();
|
||||
HVX_Vector vh = Q6_V_vzero();
|
||||
for (int group = 3; group >= 0; --group) {
|
||||
vq = Q6_V_vmux_QVV(first4, hvx_dequantize_gather4(hvx_vmem(tile + group * VLEN), row), Q6_V_vror_VR(vq, VLEN - 4));
|
||||
vh = Q6_V_vmux_QVV(first4, Q6_Vub_vlsr_VubR(p5, 2 * group), Q6_V_vror_VR(vh, VLEN - 4));
|
||||
}
|
||||
|
||||
const HVX_Vector lo = Q6_V_vor_VV(Q6_V_vand_VV(vq, Q6_Vb_vsplat_R(0x0F)), Q6_Vw_vasl_VwR(Q6_V_vand_VV(vh, one), 4));
|
||||
const HVX_Vector hi = Q6_V_vor_VV(Q6_Vub_vlsr_VubR(vq, 4), Q6_Vw_vasl_VwR(Q6_V_vand_VV(Q6_Vub_vlsr_VubR(vh, 1), one), 4));
|
||||
vq = Q6_V_lo_W(Q6_W_vshuff_VVR(hi, lo, -1));
|
||||
|
||||
const HVX_Vector scales = hvx_vmem(tile + 512);
|
||||
const HVX_Vector scale_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 4 * row));
|
||||
const HVX_Vector scale = Q6_V_lo_W(hvx_vec_f16_to_f32(scale_hf));
|
||||
const HVX_Vector offset_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 4 * row + 2));
|
||||
const HVX_Vector offset = Q6_V_lo_W(hvx_vec_f16_to_f32(offset_hf));
|
||||
|
||||
const HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(vq);
|
||||
const HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16));
|
||||
HVX_Vector values = hvx_vec_mul_f32_f32(Q6_Vsf_equals_Vw(Q6_V_lo_W(p32)), scale);
|
||||
*(HVX_Vector *) dst = hvx_vec_add_f32_f32(values, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_tiled_q6_k_f32(float * dst_spad, const uint8_t * src_spad, uint32_t n_k_tiles, uint32_t tile_stride, uint32_t row) {
|
||||
const HVX_VectorPred first4 = Q6_Q_vsetq_R(4);
|
||||
const HVX_VectorPred first16 = Q6_Q_vsetq_R(16 * sizeof(float));
|
||||
const HVX_Vector mask_0f = Q6_Vb_vsplat_R(0x0F);
|
||||
const HVX_Vector mask_03 = Q6_Vb_vsplat_R(0x03);
|
||||
|
||||
for (uint32_t k_tile = 0; k_tile < n_k_tiles; ++k_tile) {
|
||||
const uint8_t * tile = src_spad + k_tile * tile_stride;
|
||||
float * dst = dst_spad + k_tile * HTP_TENSOR_TILE_N_COLS;
|
||||
|
||||
HVX_Vector vq = Q6_V_vzero();
|
||||
for (int group = 7; group >= 0; --group) {
|
||||
const HVX_Vector lo_plane = Q6_V_vror_VR(hvx_vmem(tile + (group >> 1) * VLEN), 4 * row);
|
||||
const HVX_Vector hi_plane = Q6_V_vror_VR(hvx_vmem(tile + 512 + (group >> 2) * VLEN), 4 * row);
|
||||
const HVX_Vector lo = (group & 1) ? Q6_Vub_vlsr_VubR(lo_plane, 4) : Q6_V_vand_VV(lo_plane, mask_0f);
|
||||
const HVX_Vector hi = Q6_Vub_vlsr_VubR(hi_plane, 2 * (group & 3));
|
||||
const HVX_Vector packed = Q6_V_vor_VV(lo, Q6_Vw_vasl_VwR(Q6_V_vand_VV(hi, mask_03), 4));
|
||||
vq = Q6_V_vmux_QVV(first4, packed, Q6_V_vror_VR(vq, VLEN - 4));
|
||||
}
|
||||
|
||||
const HVX_Vector scales = hvx_vmem(tile + 768);
|
||||
const HVX_Vector scale_lo_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 2 * row));
|
||||
const HVX_Vector scale_hi_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 64 + 2 * row));
|
||||
const HVX_Vector scale_lo = Q6_V_lo_W(hvx_vec_f16_to_f32(scale_lo_hf));
|
||||
const HVX_Vector scale_hi = Q6_V_lo_W(hvx_vec_f16_to_f32(scale_hi_hf));
|
||||
const HVX_Vector scale = Q6_V_vmux_QVV(first16, scale_lo, scale_hi);
|
||||
const HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(Q6_Vb_vsub_VbVb(vq, Q6_Vb_vsplat_R(32)));
|
||||
const HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16));
|
||||
*(HVX_Vector *) dst = hvx_vec_mul_f32_f32(Q6_Vsf_equals_Vw(Q6_V_lo_W(p32)), scale);
|
||||
}
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_dequantize_tiled_f16_gather4(const uint8_t * p, uint32_t r_byte) {
|
||||
HVX_Vector w0 = Q6_V_vror_VR(hvx_vmem(p + 0 * VLEN), r_byte);
|
||||
HVX_Vector w1 = Q6_V_vror_VR(hvx_vmem(p + 1 * VLEN), r_byte);
|
||||
HVX_Vector w01 = Q6_V_lo_W(Q6_W_vshuff_VVR(w1, w0, 4));
|
||||
HVX_Vector w2 = Q6_V_vror_VR(hvx_vmem(p + 2 * VLEN), r_byte);
|
||||
HVX_Vector w3 = Q6_V_vror_VR(hvx_vmem(p + 3 * VLEN), r_byte);
|
||||
HVX_Vector w23 = Q6_V_lo_W(Q6_W_vshuff_VVR(w3, w2, 4));
|
||||
return Q6_V_lo_W(Q6_W_vshuff_VVR(w23, w01, 8));
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_tiled_f16_f32(float * dst_spad, const uint8_t * src_spad, uint32_t n_k_tiles, uint32_t tile_stride, uint32_t row) {
|
||||
const uint32_t r_byte = 4 * row;
|
||||
|
||||
for (uint32_t k_tile = 0; k_tile < n_k_tiles; ++k_tile) {
|
||||
const uint8_t * tile = src_spad + k_tile * tile_stride;
|
||||
float * dst = dst_spad + k_tile * HTP_TENSOR_TILE_N_COLS;
|
||||
|
||||
HVX_Vector g0 = hvx_dequantize_tiled_f16_gather4(tile + 0 * VLEN, r_byte);
|
||||
HVX_Vector g1 = hvx_dequantize_tiled_f16_gather4(tile + 4 * VLEN, r_byte);
|
||||
HVX_Vector g01 = Q6_V_lo_W(Q6_W_vshuff_VVR(g1, g0, 16));
|
||||
|
||||
HVX_Vector g2 = hvx_dequantize_tiled_f16_gather4(tile + 8 * VLEN, r_byte);
|
||||
HVX_Vector g3 = hvx_dequantize_tiled_f16_gather4(tile + 12 * VLEN, r_byte);
|
||||
HVX_Vector g23 = Q6_V_lo_W(Q6_W_vshuff_VVR(g3, g2, 16));
|
||||
|
||||
HVX_Vector g = Q6_V_lo_W(Q6_W_vshuff_VVR(g23, g01, 32));
|
||||
*(HVX_Vector *) dst = Q6_V_lo_W(hvx_vec_f16_to_f32(g));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_tiled_q1_0_f32(float * dst_spad, const uint8_t * src_spad, uint32_t n_k_tiles, uint32_t tile_stride, uint32_t row) {
|
||||
static const uint32_t __attribute__((aligned(128))) bit_mask[32] = {
|
||||
1u << 0, 1u << 8, 1u << 16, 1u << 24,
|
||||
1u << 1, 1u << 9, 1u << 17, 1u << 25,
|
||||
1u << 2, 1u << 10, 1u << 18, 1u << 26,
|
||||
1u << 3, 1u << 11, 1u << 19, 1u << 27,
|
||||
1u << 4, 1u << 12, 1u << 20, 1u << 28,
|
||||
1u << 5, 1u << 13, 1u << 21, 1u << 29,
|
||||
1u << 6, 1u << 14, 1u << 22, 1u << 30,
|
||||
1u << 7, 1u << 15, 1u << 23, 1u << 31,
|
||||
};
|
||||
const HVX_Vector v_bit_mask = *(const HVX_Vector *) bit_mask;
|
||||
const HVX_Vector v_zero = Q6_V_vzero();
|
||||
const HVX_Vector v_sign_bit = Q6_V_vsplat_R(0x80000000);
|
||||
|
||||
for (uint32_t k_tile = 0; k_tile < n_k_tiles; ++k_tile) {
|
||||
const uint8_t * tile = src_spad + k_tile * tile_stride;
|
||||
float * dst = dst_spad + k_tile * HTP_TENSOR_TILE_K_Q1_0;
|
||||
|
||||
const HVX_Vector scales = hvx_vmem(tile + 512);
|
||||
const HVX_Vector scale_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 2 * row));
|
||||
const HVX_Vector vd = Q6_V_lo_W(hvx_vec_f16_to_f32(scale_hf));
|
||||
const HVX_Vector neg_vd = Q6_V_vxor_VV(vd, v_sign_bit);
|
||||
|
||||
#pragma unroll
|
||||
for (int q = 0; q < 4; ++q) {
|
||||
const HVX_Vector v_bits = hvx_vec_repl_u32(Q6_V_vror_VR(hvx_vmem(tile + q * VLEN), 4 * row));
|
||||
const HVX_VectorPred q_zero = Q6_Q_vcmp_eq_VwVw(Q6_V_vand_VV(v_bits, v_bit_mask), v_zero);
|
||||
hvx_vmem(dst + q * 32) = Q6_V_vmux_QVV(q_zero, neg_vd, vd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_dequantize_row_tiled_q2_0_f32(float * dst_spad, const uint8_t * src_spad, uint32_t n_k_tiles, uint32_t tile_stride, uint32_t row) {
|
||||
const HVX_Vector mask_03 = Q6_Vb_vsplat_R(0x03);
|
||||
const HVX_Vector v_one = Q6_Vb_vsplat_R(1);
|
||||
|
||||
for (uint32_t k_tile = 0; k_tile < n_k_tiles; ++k_tile) {
|
||||
const uint8_t * tile = src_spad + k_tile * tile_stride;
|
||||
float * dst = dst_spad + k_tile * HTP_TENSOR_TILE_K_Q2_0;
|
||||
|
||||
const HVX_Vector v_packed = hvx_dequantize_tiled_f16_gather4(tile, 4 * row);
|
||||
const HVX_Vector p0 = Q6_V_vand_VV(v_packed, mask_03);
|
||||
const HVX_Vector p1 = Q6_V_vand_VV(Q6_Vub_vlsr_VubR(v_packed, 2), mask_03);
|
||||
const HVX_Vector p2 = Q6_V_vand_VV(Q6_Vub_vlsr_VubR(v_packed, 4), mask_03);
|
||||
const HVX_Vector p3 = Q6_Vub_vlsr_VubR(v_packed, 6);
|
||||
|
||||
const HVX_Vector p01 = Q6_V_lo_W(Q6_W_vshuff_VVR(p1, p0, -4));
|
||||
const HVX_Vector p23 = Q6_V_lo_W(Q6_W_vshuff_VVR(p3, p2, -4));
|
||||
const HVX_Vector vq = Q6_Vb_vsub_VbVb(Q6_V_lo_W(Q6_W_vshuff_VVR(p23, p01, -8)), v_one);
|
||||
|
||||
const HVX_Vector scales = hvx_vmem(tile + 512);
|
||||
const HVX_Vector scale_hf = hvx_vec_repl_f16(Q6_V_vror_VR(scales, 2 * row));
|
||||
const HVX_Vector scale = Q6_V_lo_W(hvx_vec_f16_to_f32(scale_hf));
|
||||
|
||||
const HVX_VectorPair p16 = Q6_Wh_vunpack_Vb(vq);
|
||||
const HVX_VectorPair p32 = Q6_Ww_vunpack_Vh(Q6_V_lo_W(p16));
|
||||
hvx_vmem(dst + 0) = hvx_vec_mul_f32_f32(Q6_Vsf_equals_Vw(Q6_V_lo_W(p32)), scale);
|
||||
hvx_vmem(dst + 32) = hvx_vec_mul_f32_f32(Q6_Vsf_equals_Vw(Q6_V_hi_W(p32)), scale);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HVX_QUANT_H
|
||||
@@ -0,0 +1,409 @@
|
||||
#ifndef HVX_REDUCE_H
|
||||
#define HVX_REDUCE_H
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "hex-utils.h"
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-types.h"
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_n_i32(HVX_Vector in, unsigned int n) {
|
||||
unsigned int total = n * 4; // total vec nbytes
|
||||
unsigned int width = 4; // int32
|
||||
|
||||
HVX_Vector sum = in, sum_t;
|
||||
while (width < total) {
|
||||
sum_t = Q6_V_vror_VR(sum, width); // rotate right
|
||||
sum = Q6_Vw_vadd_VwVw(sum_t, sum); // elementwise sum
|
||||
width = width << 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_i32(HVX_Vector in) {
|
||||
return hvx_vec_reduce_sum_n_i32(in, 32);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_n_qf32(HVX_Vector in, unsigned int n) {
|
||||
unsigned int total = n * 4; // total vec nbytes
|
||||
unsigned int width = 4; // fp32 nbytes
|
||||
|
||||
HVX_Vector sum = in, sum_t;
|
||||
while (width < total) {
|
||||
sum_t = Q6_V_vror_VR(Q6_Vsf_equals_Vqf32(sum), width); // rotate right
|
||||
sum = Q6_Vqf32_vadd_Vqf32Vsf(sum, sum_t); // elementwise sum
|
||||
width = width << 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_qf32(HVX_Vector in) {
|
||||
return hvx_vec_reduce_sum_n_qf32(in, 32);
|
||||
}
|
||||
|
||||
#if __HVX_ARCH__ > 75
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_f32x4(HVX_Vector_x4 in) {
|
||||
HVX_VectorPair sum_p01 = Q6_W_vshuff_VVR(in.v[1], in.v[0], 4);
|
||||
HVX_VectorPair sum_p23 = Q6_W_vshuff_VVR(in.v[3], in.v[2], 4);
|
||||
HVX_Vector sum_sf01 = Q6_Vsf_vadd_VsfVsf(Q6_V_lo_W(sum_p01), Q6_V_hi_W(sum_p01));
|
||||
HVX_Vector sum_sf23 = Q6_Vsf_vadd_VsfVsf(Q6_V_lo_W(sum_p23), Q6_V_hi_W(sum_p23));
|
||||
|
||||
HVX_VectorPair sum_p0123 = Q6_W_vshuff_VVR(sum_sf23, sum_sf01, 8);
|
||||
HVX_Vector sum_sf = Q6_Vsf_vadd_VsfVsf(Q6_V_lo_W(sum_p0123), Q6_V_hi_W(sum_p0123));
|
||||
|
||||
sum_sf = Q6_Vsf_vadd_VsfVsf(sum_sf, Q6_V_vror_VR(sum_sf, VLEN / 2));
|
||||
sum_sf = Q6_Vsf_vadd_VsfVsf(sum_sf, Q6_V_vror_VR(sum_sf, VLEN / 4));
|
||||
sum_sf = Q6_Vsf_vadd_VsfVsf(sum_sf, Q6_V_vror_VR(sum_sf, VLEN / 8));
|
||||
return sum_sf;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_f32x2(HVX_Vector in0, HVX_Vector in1) {
|
||||
HVX_VectorPair sump = Q6_W_vshuff_VVR(in1, in0, 4);
|
||||
HVX_Vector sum_sf = Q6_Vsf_vadd_VsfVsf(Q6_V_lo_W(sump), Q6_V_hi_W(sump));
|
||||
|
||||
sum_sf = Q6_Vsf_vadd_VsfVsf(sum_sf, Q6_V_vror_VR(sum_sf, VLEN / 2));
|
||||
sum_sf = Q6_Vsf_vadd_VsfVsf(sum_sf, Q6_V_vror_VR(sum_sf, VLEN / 4));
|
||||
sum_sf = Q6_Vsf_vadd_VsfVsf(sum_sf, Q6_V_vror_VR(sum_sf, VLEN / 8));
|
||||
sum_sf = Q6_Vsf_vadd_VsfVsf(sum_sf, Q6_V_vror_VR(sum_sf, VLEN / 16));
|
||||
return sum_sf;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_n_f32(HVX_Vector in, unsigned int n) {
|
||||
unsigned int total = n * 4; // total vec nbytes
|
||||
unsigned int width = 4; // fp32 nbytes
|
||||
|
||||
HVX_Vector sum = in, sum_t;
|
||||
while (width < total) {
|
||||
sum_t = Q6_V_vror_VR(sum, width); // rotate right
|
||||
sum = Q6_Vsf_vadd_VsfVsf(sum, sum_t); // elementwise sum
|
||||
width = width << 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_f32x4(HVX_Vector_x4 in) {
|
||||
HVX_VectorPair sum_p01 = Q6_W_vshuff_VVR(in.v[1], in.v[0], 4);
|
||||
HVX_VectorPair sum_p23 = Q6_W_vshuff_VVR(in.v[3], in.v[2], 4);
|
||||
HVX_Vector sum_qf01 = Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(sum_p01), Q6_V_hi_W(sum_p01));
|
||||
HVX_Vector sum_qf23 = Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(sum_p23), Q6_V_hi_W(sum_p23));
|
||||
|
||||
HVX_VectorPair sum_p0123 = Q6_W_vshuff_VVR(Q6_Vsf_equals_Vqf32(sum_qf23), Q6_Vsf_equals_Vqf32(sum_qf01), 8);
|
||||
HVX_Vector sum_qf = Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(sum_p0123), Q6_V_hi_W(sum_p0123));
|
||||
|
||||
sum_qf = Q6_Vqf32_vadd_Vqf32Vsf(sum_qf, Q6_V_vror_VR(Q6_Vsf_equals_Vqf32(sum_qf), VLEN / 2));
|
||||
sum_qf = Q6_Vqf32_vadd_Vqf32Vsf(sum_qf, Q6_V_vror_VR(Q6_Vsf_equals_Vqf32(sum_qf), VLEN / 4));
|
||||
sum_qf = Q6_Vqf32_vadd_Vqf32Vsf(sum_qf, Q6_V_vror_VR(Q6_Vsf_equals_Vqf32(sum_qf), VLEN / 8));
|
||||
return Q6_Vsf_equals_Vqf32(sum_qf);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_f32x2(HVX_Vector in0, HVX_Vector in1) {
|
||||
HVX_VectorPair sump = Q6_W_vshuff_VVR(in1, in0, 4);
|
||||
HVX_Vector sum_qf = Q6_Vqf32_vadd_VsfVsf(Q6_V_lo_W(sump), Q6_V_hi_W(sump));
|
||||
|
||||
sum_qf = Q6_Vqf32_vadd_Vqf32Vsf(sum_qf, Q6_V_vror_VR(Q6_Vsf_equals_Vqf32(sum_qf), VLEN / 2));
|
||||
sum_qf = Q6_Vqf32_vadd_Vqf32Vsf(sum_qf, Q6_V_vror_VR(Q6_Vsf_equals_Vqf32(sum_qf), VLEN / 4));
|
||||
sum_qf = Q6_Vqf32_vadd_Vqf32Vsf(sum_qf, Q6_V_vror_VR(Q6_Vsf_equals_Vqf32(sum_qf), VLEN / 8));
|
||||
sum_qf = Q6_Vqf32_vadd_Vqf32Vsf(sum_qf, Q6_V_vror_VR(Q6_Vsf_equals_Vqf32(sum_qf), VLEN / 16));
|
||||
return Q6_Vsf_equals_Vqf32(sum_qf);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_n_f32(HVX_Vector in, unsigned int n) {
|
||||
unsigned int total = n * 4; // total vec nbytes
|
||||
unsigned int width = 4; // fp32 nbytes
|
||||
|
||||
HVX_Vector sum = in, sum_t;
|
||||
while (width < total) {
|
||||
sum_t = Q6_V_vror_VR(sum, width); // rotate right
|
||||
sum = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(sum, sum_t)); // elementwise sum
|
||||
width = width << 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_sum_f32(HVX_Vector in) {
|
||||
return hvx_vec_reduce_sum_n_f32(in, 32);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_max_f16(HVX_Vector in) {
|
||||
unsigned total = 128; // total vec nbytes
|
||||
unsigned width = 2; // fp16 nbytes
|
||||
|
||||
HVX_Vector _max = in, _max_t;
|
||||
while (width < total) {
|
||||
_max_t = Q6_V_vror_VR(_max, width); // rotate right
|
||||
_max = Q6_Vhf_vmax_VhfVhf(_max_t, _max); // elementwise max
|
||||
width = width << 1;
|
||||
}
|
||||
|
||||
return _max;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_max2_f16(HVX_Vector in, HVX_Vector _max) {
|
||||
unsigned total = 128; // total vec nbytes
|
||||
unsigned width = 2; // fp32 nbytes
|
||||
|
||||
HVX_Vector _max_t;
|
||||
|
||||
_max = Q6_Vhf_vmax_VhfVhf(in, _max);
|
||||
while (width < total) {
|
||||
_max_t = Q6_V_vror_VR(_max, width); // rotate right
|
||||
_max = Q6_Vhf_vmax_VhfVhf(_max_t, _max); // elementwise max
|
||||
width = width << 1;
|
||||
}
|
||||
|
||||
return _max;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_max_f32(HVX_Vector in) {
|
||||
unsigned total = 128; // total vec nbytes
|
||||
unsigned width = 4; // fp32 nbytes
|
||||
|
||||
HVX_Vector _max = in, _max_t;
|
||||
while (width < total) {
|
||||
_max_t = Q6_V_vror_VR(_max, width); // rotate right
|
||||
_max = Q6_Vsf_vmax_VsfVsf(_max_t, _max); // elementwise max
|
||||
width = width << 1;
|
||||
}
|
||||
|
||||
return _max;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_max2_f32(HVX_Vector in, HVX_Vector _max) {
|
||||
unsigned total = 128; // total vec nbytes
|
||||
unsigned width = 4; // fp32 nbytes
|
||||
|
||||
HVX_Vector _max_t;
|
||||
|
||||
_max = Q6_Vsf_vmax_VsfVsf(in, _max);
|
||||
while (width < total) {
|
||||
_max_t = Q6_V_vror_VR(_max, width); // rotate right
|
||||
_max = Q6_Vsf_vmax_VsfVsf(_max_t, _max); // elementwise max
|
||||
width = width << 1;
|
||||
}
|
||||
|
||||
return _max;
|
||||
}
|
||||
|
||||
#define hvx_reduce_loop_body(src_type, init_vec, pad_vec, vec_op, reduce_op, scalar_reduce) \
|
||||
do { \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
HVX_Vector acc = init_vec; \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(float); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = num_elems / epv; \
|
||||
const uint32_t nloe = num_elems % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
acc = vec_op(acc, vsrc[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
const float * srcf = (const float *) src + i * epv; \
|
||||
HVX_Vector in = *(HVX_UVector *) srcf; \
|
||||
HVX_Vector temp = Q6_V_valign_VVR(in, pad_vec, nloe * elem_size); \
|
||||
acc = vec_op(acc, temp); \
|
||||
} \
|
||||
HVX_Vector v = reduce_op(acc); \
|
||||
return scalar_reduce(v); \
|
||||
} while(0)
|
||||
|
||||
#define HVX_REDUCE_MAX_OP(acc, val) Q6_Vsf_vmax_VsfVsf(acc, val)
|
||||
#define HVX_REDUCE_SUM_OP(acc, val) Q6_Vqf32_vadd_VsfVsf(Q6_Vsf_equals_Vqf32(acc), val)
|
||||
#define HVX_SUM_SQ_OP(acc, val) Q6_Vqf32_vadd_Vqf32Vqf32(acc, Q6_Vqf32_vmpy_VsfVsf(val, val))
|
||||
#define HVX_REDUCE_MAX_SCALAR(v) hvx_vec_get_f32(v)
|
||||
#define HVX_REDUCE_SUM_SCALAR(v) hvx_vec_get_f32(Q6_Vsf_equals_Vqf32(v))
|
||||
|
||||
// Max variants
|
||||
|
||||
static inline float hvx_reduce_max_f32_a(const uint8_t * restrict src, const int num_elems) {
|
||||
HVX_Vector init_vec = hvx_vec_splat_f32(((const float *) src)[0]);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_reduce_loop_body(HVX_Vector, init_vec, init_vec, HVX_REDUCE_MAX_OP, hvx_vec_reduce_max_f32, HVX_REDUCE_MAX_SCALAR);
|
||||
}
|
||||
|
||||
static inline float hvx_reduce_max_f32_u(const uint8_t * restrict src, const int num_elems) {
|
||||
HVX_Vector init_vec = hvx_vec_splat_f32(((const float *) src)[0]);
|
||||
hvx_reduce_loop_body(HVX_UVector, init_vec, init_vec, HVX_REDUCE_MAX_OP, hvx_vec_reduce_max_f32, HVX_REDUCE_MAX_SCALAR);
|
||||
}
|
||||
|
||||
static inline float hvx_reduce_max_f32(const uint8_t * restrict src, const int num_elems) {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
return hvx_reduce_max_f32_a(src, num_elems);
|
||||
} else {
|
||||
return hvx_reduce_max_f32_u(src, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
// Sum variants
|
||||
|
||||
static inline float hvx_reduce_sum_f32_a(const uint8_t * restrict src, const int num_elems) {
|
||||
HVX_Vector init_vec = Q6_V_vsplat_R(0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_reduce_loop_body(HVX_Vector, init_vec, init_vec, HVX_REDUCE_SUM_OP, hvx_vec_reduce_sum_qf32, HVX_REDUCE_SUM_SCALAR);
|
||||
}
|
||||
|
||||
static inline float hvx_reduce_sum_f32_u(const uint8_t * restrict src, const int num_elems) {
|
||||
HVX_Vector init_vec = Q6_V_vsplat_R(0);
|
||||
hvx_reduce_loop_body(HVX_UVector, init_vec, init_vec, HVX_REDUCE_SUM_OP, hvx_vec_reduce_sum_qf32, HVX_REDUCE_SUM_SCALAR);
|
||||
}
|
||||
|
||||
static inline float hvx_reduce_sum_f32(const uint8_t * restrict src, const int num_elems) {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
return hvx_reduce_sum_f32_a(src, num_elems);
|
||||
} else {
|
||||
return hvx_reduce_sum_f32_u(src, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
// Sum of squares variants
|
||||
|
||||
static inline float hvx_sum_of_squares_f32_a(const uint8_t * restrict src, const int num_elems) {
|
||||
HVX_Vector init_vec = Q6_V_vsplat_R(0);
|
||||
assert((uintptr_t) src % 128 == 0);
|
||||
hvx_reduce_loop_body(HVX_Vector, init_vec, init_vec, HVX_SUM_SQ_OP, hvx_vec_reduce_sum_qf32, HVX_REDUCE_SUM_SCALAR);
|
||||
}
|
||||
|
||||
static inline float hvx_sum_of_squares_f32_u(const uint8_t * restrict src, const int num_elems) {
|
||||
HVX_Vector init_vec = Q6_V_vsplat_R(0);
|
||||
hvx_reduce_loop_body(HVX_UVector, init_vec, init_vec, HVX_SUM_SQ_OP, hvx_vec_reduce_sum_qf32, HVX_REDUCE_SUM_SCALAR);
|
||||
}
|
||||
|
||||
static inline float hvx_sum_of_squares_f32(const uint8_t * restrict src, const int num_elems) {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
return hvx_sum_of_squares_f32_a(src, num_elems);
|
||||
} else {
|
||||
return hvx_sum_of_squares_f32_u(src, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
// Signed 32-bit Integer Max variants
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_max_n_i32(HVX_Vector in, unsigned int n) {
|
||||
unsigned int total = n * 4; // total vec nbytes
|
||||
unsigned int width = 4; // int32 nbytes
|
||||
|
||||
HVX_Vector max_val = in, max_t;
|
||||
while (width < total) {
|
||||
max_t = Q6_V_vror_VR(max_val, width); // rotate right
|
||||
max_val = Q6_Vw_vmax_VwVw(max_t, max_val); // elementwise signed max
|
||||
width = width << 1;
|
||||
}
|
||||
return max_val;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_reduce_max_i32(HVX_Vector in) {
|
||||
return hvx_vec_reduce_max_n_i32(in, 32);
|
||||
}
|
||||
|
||||
static inline int32_t hvx_reduce_max_i32_a(const uint8_t * restrict src, const int num_elems) {
|
||||
HVX_Vector init_vec = Q6_V_vsplat_R(((const int32_t *) src)[0]);
|
||||
HVX_Vector pad_vec = Q6_V_vsplat_R(0x80000000);
|
||||
assert((uintptr_t) src % 128 == 0);
|
||||
hvx_reduce_loop_body(HVX_Vector, init_vec, pad_vec, Q6_Vw_vmax_VwVw, hvx_vec_reduce_max_i32, hvx_vec_get_i32);
|
||||
}
|
||||
|
||||
static inline int32_t hvx_reduce_max_i32_u(const uint8_t * restrict src, const int num_elems) {
|
||||
HVX_Vector init_vec = Q6_V_vsplat_R(((const int32_t *) src)[0]);
|
||||
HVX_Vector pad_vec = Q6_V_vsplat_R(0x80000000);
|
||||
hvx_reduce_loop_body(HVX_UVector, init_vec, pad_vec, Q6_Vw_vmax_VwVw, hvx_vec_reduce_max_i32, hvx_vec_get_i32);
|
||||
}
|
||||
|
||||
static inline int32_t hvx_reduce_max_i32(const uint8_t * restrict src, const int num_elems) {
|
||||
if (hex_is_aligned((void *) src, 128)) {
|
||||
return hvx_reduce_max_i32_a(src, num_elems);
|
||||
} else {
|
||||
return hvx_reduce_max_i32_u(src, num_elems);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hvx_argmax_f32(
|
||||
const float * restrict src,
|
||||
uint32_t n,
|
||||
uint32_t offset,
|
||||
float * out_val,
|
||||
int32_t * out_idx
|
||||
) {
|
||||
if (n == 0) {
|
||||
*out_val = -INFINITY;
|
||||
*out_idx = (int32_t) offset;
|
||||
return;
|
||||
}
|
||||
|
||||
if (n < 32 || !hex_is_aligned((void *) src, 128)) {
|
||||
float best_val = src[0];
|
||||
int32_t best_idx = (int32_t) offset;
|
||||
for (uint32_t i = 1; i < n; i++) {
|
||||
if (src[i] > best_val) {
|
||||
best_val = src[i];
|
||||
best_idx = (int32_t) (offset + i);
|
||||
}
|
||||
}
|
||||
*out_val = best_val;
|
||||
*out_idx = best_idx;
|
||||
return;
|
||||
}
|
||||
|
||||
static const int32_t c_lane_idx[32] __attribute__((aligned(128))) = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
|
||||
};
|
||||
|
||||
const HVX_Vector v_init_idx = *(const HVX_Vector *) c_lane_idx;
|
||||
const HVX_Vector v_step = Q6_V_vsplat_R(32);
|
||||
HVX_Vector v_cur_idx = Q6_Vw_vadd_VwVw(v_init_idx, Q6_V_vsplat_R((int32_t) offset));
|
||||
HVX_Vector v_max_val = hvx_vec_splat_f32(-INFINITY);
|
||||
HVX_Vector v_max_idx = v_cur_idx;
|
||||
|
||||
const HVX_Vector * vsrc = (const HVX_Vector *) src;
|
||||
const uint32_t nvec = n / 32;
|
||||
|
||||
for (uint32_t vi = 0; vi < nvec; vi++) {
|
||||
HVX_Vector v = vsrc[vi];
|
||||
HVX_VectorPred pred = Q6_Q_vcmp_gt_VsfVsf(v, v_max_val);
|
||||
v_max_val = Q6_V_vmux_QVV(pred, v, v_max_val);
|
||||
v_max_idx = Q6_V_vmux_QVV(pred, v_cur_idx, v_max_idx);
|
||||
v_cur_idx = Q6_Vw_vadd_VwVw(v_cur_idx, v_step);
|
||||
}
|
||||
|
||||
HVX_VectorAlias u_val, u_idx;
|
||||
u_val.v = v_max_val;
|
||||
u_idx.v = v_max_idx;
|
||||
|
||||
float best_val = u_val.fp32[0];
|
||||
int32_t best_idx = (int32_t) u_idx.w[0];
|
||||
for (int i = 1; i < 32; i++) {
|
||||
if (u_val.fp32[i] > best_val) {
|
||||
best_val = u_val.fp32[i];
|
||||
best_idx = (int32_t) u_idx.w[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = nvec * 32; i < n; i++) {
|
||||
if (src[i] > best_val) {
|
||||
best_val = src[i];
|
||||
best_idx = (int32_t) (offset + i);
|
||||
}
|
||||
}
|
||||
|
||||
*out_val = best_val;
|
||||
*out_idx = best_idx;
|
||||
}
|
||||
|
||||
#undef hvx_reduce_loop_body
|
||||
#undef HVX_REDUCE_MAX_OP
|
||||
#undef HVX_REDUCE_SUM_OP
|
||||
#undef HVX_REDUCE_MAX_SCALAR
|
||||
#undef HVX_REDUCE_SUM_SCALAR
|
||||
#undef HVX_SUM_SQ_OP
|
||||
|
||||
#endif /* HVX_REDUCE_H */
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef HVX_REPL_H
|
||||
#define HVX_REPL_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hvx-base.h"
|
||||
|
||||
static inline HVX_Vector hvx_vec_repl(HVX_Vector v, const uint8_t * ctrl) {
|
||||
return Q6_V_vdelta_VV(v, hvx_vmem(ctrl));
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_repl_u32(HVX_Vector v) {
|
||||
// vdelta control to replicate first 4 bytes across all lanes
|
||||
static const uint8_t __attribute__((aligned(128))) repl[128] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x40, 0x40, 0x40, 0x40, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
};
|
||||
return hvx_vec_repl(v, repl);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_repl_f32(HVX_Vector v) {
|
||||
// vdelta control to replicate first 4 bytes across all lanes
|
||||
static const uint8_t __attribute__((aligned(128))) repl[128] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x40, 0x40, 0x40, 0x40, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x20, 0x20, 0x20, 0x20, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x10, 0x10, 0x10, 0x10, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
};
|
||||
return hvx_vec_repl(v, repl);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_repl_f16(HVX_Vector v) {
|
||||
// vdelta control to replicate first two bytes across all lanes
|
||||
static const uint8_t __attribute__((aligned(128))) repl[128] = {
|
||||
0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x40, 0x40, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
};
|
||||
return hvx_vec_repl(v, repl);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_repl_2x_f16(HVX_Vector v) {
|
||||
// vdelta control to splat a pair of f16s: first half = f16[0], second half = f16[1]
|
||||
static const uint8_t __attribute__((aligned(128))) repl[128] = {
|
||||
0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02,
|
||||
0x02, 0x02, 0x40, 0x40, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04,
|
||||
0x02, 0x02, 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04,
|
||||
0x02, 0x02, 0x20, 0x20, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04,
|
||||
0x02, 0x02, 0x10, 0x10, 0x02, 0x02, 0x04, 0x04, 0x02, 0x02, 0x08, 0x08, 0x02, 0x02, 0x04, 0x04,
|
||||
};
|
||||
return hvx_vec_repl(v, repl);
|
||||
}
|
||||
|
||||
#endif // HVX_REPL_H
|
||||
@@ -0,0 +1,199 @@
|
||||
#ifndef HVX_SCALE_H
|
||||
#define HVX_SCALE_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hvx-base.h"
|
||||
|
||||
#define hvx_scale_f32_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
HVX_Vector vs = hvx_vec_splat_f32(scale); \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(float); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; ++i) { \
|
||||
HVX_Vector v = Q6_Vqf32_vmpy_VsfVsf(vsrc[i], vs); \
|
||||
vdst[i] = Q6_Vsf_equals_Vqf32(v); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = Q6_Vqf32_vmpy_VsfVsf(vsrc[i], vs); \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, Q6_Vsf_equals_Vqf32(v)); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_scale_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale) {
|
||||
assert((size_t) dst % 128 == 0);
|
||||
assert((size_t) src % 128 == 0);
|
||||
hvx_scale_f32_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale) {
|
||||
assert((size_t) dst % 128 == 0);
|
||||
hvx_scale_f32_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale) {
|
||||
assert((size_t) src % 128 == 0);
|
||||
hvx_scale_f32_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale) {
|
||||
hvx_scale_f32_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_f32(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale) {
|
||||
if (((size_t) dst & 127) == 0) {
|
||||
if (((size_t) src & 127) == 0) {
|
||||
hvx_scale_f32_aa(dst, src, n, scale);
|
||||
} else {
|
||||
hvx_scale_f32_au(dst, src, n, scale);
|
||||
}
|
||||
} else {
|
||||
if (((size_t) src & 127) == 0) {
|
||||
hvx_scale_f32_ua(dst, src, n, scale);
|
||||
} else {
|
||||
hvx_scale_f32_uu(dst, src, n, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define hvx_scale_offset_f32_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
HVX_Vector vs = hvx_vec_splat_f32(scale); \
|
||||
HVX_Vector vo = hvx_vec_splat_f32(offset); \
|
||||
\
|
||||
const uint32_t elem_size = sizeof(float); \
|
||||
const uint32_t epv = 128 / elem_size; \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; ++i) { \
|
||||
HVX_Vector v = Q6_Vqf32_vadd_Vqf32Vsf(Q6_Vqf32_vmpy_VsfVsf(vsrc[i], vs), vo); \
|
||||
vdst[i] = Q6_Vsf_equals_Vqf32(v); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector v = Q6_Vqf32_vadd_Vqf32Vsf(Q6_Vqf32_vmpy_VsfVsf(vsrc[i], vs), vo); \
|
||||
vec_store((void *) &vdst[i], nloe * elem_size, Q6_Vsf_equals_Vqf32(v)); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_scale_offset_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
assert((size_t) dst % 128 == 0);
|
||||
assert((size_t) src % 128 == 0);
|
||||
hvx_scale_offset_f32_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_offset_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
assert((size_t) dst % 128 == 0);
|
||||
hvx_scale_offset_f32_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_offset_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
assert((size_t) src % 128 == 0);
|
||||
hvx_scale_offset_f32_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_offset_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
hvx_scale_offset_f32_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_offset_f32(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
if (((size_t) dst & 127) == 0) {
|
||||
if (((size_t) src & 127) == 0) {
|
||||
hvx_scale_offset_f32_aa(dst, src, n, scale, offset);
|
||||
} else {
|
||||
hvx_scale_offset_f32_au(dst, src, n, scale, offset);
|
||||
}
|
||||
} else {
|
||||
if (((size_t) src & 127) == 0) {
|
||||
hvx_scale_offset_f32_ua(dst, src, n, scale, offset);
|
||||
} else {
|
||||
hvx_scale_offset_f32_uu(dst, src, n, scale, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scale+offset computed by promoting f16 -> f32, then narrowing the result back to f16.
|
||||
#define hvx_scale_offset_f16_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
HVX_Vector vs = hvx_vec_splat_f32(scale); \
|
||||
HVX_Vector vo = hvx_vec_splat_f32(offset); \
|
||||
\
|
||||
const uint32_t nvec = n / VLEN_FP16; \
|
||||
const uint32_t nloe = n % VLEN_FP16; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; ++i) { \
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]); \
|
||||
HVX_Vector r0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vsf(Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(p), vs), vo)); \
|
||||
HVX_Vector r1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vsf(Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(p), vs), vo)); \
|
||||
vdst[i] = hvx_vec_f32_to_f16(r0, r1); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]); \
|
||||
HVX_Vector r0 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vsf(Q6_Vqf32_vmpy_VsfVsf(Q6_V_lo_W(p), vs), vo)); \
|
||||
HVX_Vector r1 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vsf(Q6_Vqf32_vmpy_VsfVsf(Q6_V_hi_W(p), vs), vo)); \
|
||||
HVX_Vector v = hvx_vec_f32_to_f16(r0, r1); \
|
||||
vec_store((void *) &vdst[i], nloe * SIZEOF_FP16, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_scale_offset_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
assert((size_t) dst % 128 == 0);
|
||||
assert((size_t) src % 128 == 0);
|
||||
hvx_scale_offset_f16_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_offset_f16_au(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
assert((size_t) dst % 128 == 0);
|
||||
hvx_scale_offset_f16_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_offset_f16_ua(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
assert((size_t) src % 128 == 0);
|
||||
hvx_scale_offset_f16_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_offset_f16_uu(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
hvx_scale_offset_f16_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_scale_offset_f16(uint8_t * restrict dst, const uint8_t * restrict src, const int n, const float scale, const float offset) {
|
||||
if (((size_t) dst & 127) == 0) {
|
||||
if (((size_t) src & 127) == 0) {
|
||||
hvx_scale_offset_f16_aa(dst, src, n, scale, offset);
|
||||
} else {
|
||||
hvx_scale_offset_f16_au(dst, src, n, scale, offset);
|
||||
}
|
||||
} else {
|
||||
if (((size_t) src & 127) == 0) {
|
||||
hvx_scale_offset_f16_ua(dst, src, n, scale, offset);
|
||||
} else {
|
||||
hvx_scale_offset_f16_uu(dst, src, n, scale, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HVX_SCALE_H
|
||||
@@ -0,0 +1,243 @@
|
||||
#ifndef HVX_SIGMOID_H
|
||||
#define HVX_SIGMOID_H
|
||||
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-inverse.h"
|
||||
#include "hvx-exp.h"
|
||||
|
||||
#define FAST_SIGMOID_LOG2F (0x3fb8aa3b) // 1.442695022
|
||||
#define FAST_SIGMOID_C1 (0x3d009076) // 0.03138777
|
||||
#define FAST_SIGMOID_C2 (0x3e8d74bd) // 0.276281267
|
||||
#define FAST_SIGMOID_C3 (0x3f000000) // 0.5
|
||||
|
||||
static inline HVX_Vector hvx_vec_fast_sigmoid_f32(HVX_Vector v) {
|
||||
v = Q6_Vqf32_vmpy_VsfVsf(v, Q6_V_vsplat_R(FAST_SIGMOID_LOG2F));
|
||||
v = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(v), Q6_V_vsplat_R(FAST_SIGMOID_C3));
|
||||
|
||||
HVX_Vector in_int = hvx_vec_truncate_f32(Q6_Vsf_equals_Vqf32(v));
|
||||
HVX_Vector x = Q6_Vqf32_vsub_Vqf32Vsf(v, Q6_Vsf_equals_Vw(in_int));
|
||||
HVX_Vector xx = Q6_Vqf32_vmpy_Vqf32Vqf32(x, x);
|
||||
|
||||
HVX_Vector v1 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(xx), Q6_V_vsplat_R(FAST_SIGMOID_C2));
|
||||
v1 = Q6_Vqf32_vadd_Vqf32Vsf(v1, Q6_V_vsplat_R(FAST_SIGMOID_LOG2F));
|
||||
|
||||
HVX_Vector v2 = Q6_Vqf32_vmpy_VsfVsf(Q6_Vsf_equals_Vqf32(x), Q6_V_vsplat_R(FAST_SIGMOID_C1));
|
||||
v2 = Q6_Vqf32_vmpy_Vqf32Vqf32(v2, xx);
|
||||
v2 = Q6_Vqf32_vadd_Vqf32Vqf32(v2, x);
|
||||
|
||||
HVX_Vector v3 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_Vqf32Vqf32(v2, v1));
|
||||
HVX_Vector v3_exponent = Q6_Vw_vasl_VwR(v3, 1);
|
||||
v3_exponent = Q6_Vuw_vlsr_VuwR(v3_exponent, 24);
|
||||
v3_exponent = Q6_Vw_vadd_VwVw(in_int, v3_exponent);
|
||||
v3 = Q6_Vw_vaslacc_VwVwR(v3, in_int, 24);
|
||||
|
||||
HVX_Vector v4 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_Vqf32Vqf32(v2, v1));
|
||||
HVX_Vector v5 = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(v3, v4));
|
||||
|
||||
HVX_Vector res = hvx_vec_inverse_f32(v5);
|
||||
res = Q6_Vqf32_vmpy_VsfVsf(v3, res);
|
||||
|
||||
return Q6_Vsf_equals_Vqf32(res);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_fast_sigmoid_f32_guard(HVX_Vector v,
|
||||
HVX_Vector one,
|
||||
HVX_Vector max_exp,
|
||||
HVX_Vector min_exp) {
|
||||
const HVX_VectorPred pred_max = Q6_Q_vcmp_gt_VsfVsf(max_exp, v);
|
||||
const HVX_VectorPred pred_min = Q6_Q_vcmp_gt_VsfVsf(v, min_exp);
|
||||
|
||||
HVX_Vector out = hvx_vec_fast_sigmoid_f32(v);
|
||||
out = Q6_V_vmux_QVV(pred_max, out, one);
|
||||
return Q6_V_vmux_QVV(pred_min, out, Q6_V_vzero());
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_tanh_f32(HVX_Vector x) {
|
||||
// tanh(x) = 2 * sigmoid(2x) - 1
|
||||
HVX_Vector two = hvx_vec_splat_f32(2.0f);
|
||||
HVX_Vector one = hvx_vec_splat_f32(1.0f);
|
||||
HVX_Vector x2 = Q6_Vqf32_vmpy_VsfVsf(x, two);
|
||||
|
||||
HVX_Vector max_exp = hvx_vec_splat_f32(87.f);
|
||||
HVX_Vector min_exp = hvx_vec_splat_f32(-87.f);
|
||||
|
||||
HVX_Vector sig2x = hvx_vec_fast_sigmoid_f32_guard(Q6_Vsf_equals_Vqf32(x2), one, max_exp, min_exp);
|
||||
|
||||
HVX_Vector res = Q6_Vqf32_vmpy_VsfVsf(sig2x, two);
|
||||
res = Q6_Vqf32_vsub_Vqf32Vsf(res, one);
|
||||
return Q6_Vsf_equals_Vqf32(res);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_silu_f32(HVX_Vector x) {
|
||||
HVX_Vector one = hvx_vec_splat_f32(1.0f);
|
||||
HVX_Vector max_exp = hvx_vec_splat_f32(87.f);
|
||||
HVX_Vector min_exp = hvx_vec_splat_f32(-87.f);
|
||||
HVX_Vector sig = hvx_vec_fast_sigmoid_f32_guard(x, one, max_exp, min_exp);
|
||||
return Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(x, sig));
|
||||
}
|
||||
|
||||
#define hvx_sigmoid_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const HVX_Vector one = hvx_vec_splat_f32(1.f); \
|
||||
const HVX_Vector max_exp = hvx_vec_splat_f32(87.f); \
|
||||
const HVX_Vector min_exp = hvx_vec_splat_f32(-87.f); \
|
||||
\
|
||||
const uint32_t epv = 128 / sizeof(float); \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = hvx_vec_fast_sigmoid_f32_guard(vsrc[i], one, max_exp, min_exp); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector tmp = hvx_vec_fast_sigmoid_f32_guard(vsrc[i], one, max_exp, min_exp); \
|
||||
vec_store((void *) &vdst[i], nloe * sizeof(float), tmp); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define hvx_tanh_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t epv = 128 / sizeof(float); \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = hvx_vec_tanh_f32(vsrc[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector tmp = hvx_vec_tanh_f32(vsrc[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * sizeof(float), tmp); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_sigmoid_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sigmoid_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sigmoid_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_sigmoid_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sigmoid_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sigmoid_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_sigmoid_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_sigmoid_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_tanh_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_tanh_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_fast_sigmoid_f16(HVX_Vector x_v) {
|
||||
const HVX_Vector v_one = hvx_vec_splat_f16(1.0f);
|
||||
const HVX_Vector v_neg_log2e = hvx_vec_splat_f16(-EXP_LOG2E_F);
|
||||
const HVX_Vector em_mask = Q6_Vh_vsplat_R(0x7FFF);
|
||||
|
||||
// Compute absolute value of x_v
|
||||
HVX_Vector abs_x = Q6_V_vand_VV(x_v, em_mask);
|
||||
|
||||
// Compute u = -abs_x * log2(e) <= 0.
|
||||
HVX_Vector u = hvx_vec_mul_f16_f16(abs_x, v_neg_log2e);
|
||||
|
||||
// Clamp input to prevent underflow in exp2
|
||||
const HVX_Vector v_clamp_min = hvx_vec_splat_f16(-24.0f);
|
||||
u = Q6_Vhf_vmax_VhfVhf(v_clamp_min, u);
|
||||
|
||||
HVX_Vector exp_val = hvx_vec_exp2_f16(u);
|
||||
HVX_Vector denom = hvx_vec_add_f16_f16(v_one, exp_val);
|
||||
HVX_Vector sig_abs = hvx_vec_inverse_f16(denom);
|
||||
|
||||
// check if x_v < 0 (using integer comparison on absolute value)
|
||||
HVX_VectorPred is_neg = Q6_Q_vcmp_gt_VhVh(abs_x, x_v);
|
||||
|
||||
// If x_v < 0, return 1.0f - sig_abs
|
||||
HVX_Vector sig_neg = Q6_Vhf_equals_Vqf16(Q6_Vqf16_vsub_VhfVhf(v_one, sig_abs));
|
||||
return Q6_V_vmux_QVV(is_neg, sig_neg, sig_abs);
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_tanh_f16(HVX_Vector x) {
|
||||
// tanh(x) = 2 * sigmoid(2x) - 1
|
||||
const HVX_Vector v_two = hvx_vec_splat_f16(2.0f);
|
||||
|
||||
HVX_Vector x2 = hvx_vec_mul_f16_f16(x, v_two);
|
||||
HVX_Vector sig2x = hvx_vec_fast_sigmoid_f16(x2);
|
||||
|
||||
const HVX_Vector v_neg_one = hvx_vec_splat_f16(-1.0f);
|
||||
return hvx_vec_add_f16_f16(hvx_vec_mul_f16_f16(sig2x, v_two), v_neg_one);
|
||||
}
|
||||
|
||||
#define hvx_sigmoid_f16_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t epv = 128 / sizeof(_Float16); \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = hvx_vec_fast_sigmoid_f16(vsrc[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector tmp = hvx_vec_fast_sigmoid_f16(vsrc[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * sizeof(_Float16), tmp); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define hvx_tanh_f16_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t epv = 128 / sizeof(_Float16); \
|
||||
const uint32_t nvec = n / epv; \
|
||||
const uint32_t nloe = n % epv; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
vdst[i] = hvx_vec_tanh_f16(vsrc[i]); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector tmp = hvx_vec_tanh_f16(vsrc[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * sizeof(_Float16), tmp); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_sigmoid_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sigmoid_f16_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_tanh_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_tanh_f16_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
#endif /* HVX_SIGMOID_H */
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifndef HVX_SIN_COS_H
|
||||
#define HVX_SIN_COS_H
|
||||
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-floor.h"
|
||||
|
||||
// Range-reduce x to y in [-pi/2, pi/2] and the quadrant sign (-1)^n.
|
||||
// Floor/truncate need IEEE bits, so convert qf32 back to sf before them.
|
||||
static inline void hvx_vec_sincos_reduce_f32(HVX_Vector x, HVX_Vector * y, HVX_Vector * sign) {
|
||||
HVX_Vector const_inv_pi = hvx_vec_splat_f32(0.3183098861837907f);
|
||||
HVX_Vector const_half = hvx_vec_splat_f32(0.5f);
|
||||
HVX_Vector const_pi = hvx_vec_splat_f32(3.141592653589793f);
|
||||
HVX_Vector const_one = hvx_vec_splat_f32(1.0f);
|
||||
HVX_Vector const_neg_one = hvx_vec_splat_f32(-1.0f);
|
||||
HVX_Vector const_one_i = Q6_V_vsplat_R(1);
|
||||
|
||||
HVX_Vector x_over_pi = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(x, const_inv_pi));
|
||||
x_over_pi = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(x_over_pi, const_half));
|
||||
|
||||
HVX_Vector n_float = hvx_vec_floor_f32(x_over_pi);
|
||||
HVX_Vector n_int = hvx_vec_truncate_f32(n_float);
|
||||
|
||||
HVX_Vector n_pi = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(n_float, const_pi));
|
||||
*y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vsub_VsfVsf(x, n_pi));
|
||||
|
||||
HVX_VectorPred is_odd = Q6_Q_vcmp_eq_VwVw(Q6_V_vand_VV(n_int, const_one_i), const_one_i);
|
||||
*sign = Q6_V_vmux_QVV(is_odd, const_neg_one, const_one);
|
||||
}
|
||||
|
||||
static inline void hvx_vec_sincos_f32(HVX_Vector x, HVX_Vector * vcos, HVX_Vector * vsin) {
|
||||
HVX_Vector y;
|
||||
HVX_Vector sign;
|
||||
hvx_vec_sincos_reduce_f32(x, &y, &sign);
|
||||
|
||||
HVX_Vector z = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(y, y));
|
||||
|
||||
HVX_Vector c4 = hvx_vec_splat_f32(2.3557242013849433e-05f);
|
||||
HVX_Vector c3 = hvx_vec_splat_f32(-0.0013871428263450528f);
|
||||
HVX_Vector c2 = hvx_vec_splat_f32(0.041665895266688284f);
|
||||
HVX_Vector c1 = hvx_vec_splat_f32(-0.4999999360426369f);
|
||||
HVX_Vector c0 = hvx_vec_splat_f32(0.9999999999071725f);
|
||||
|
||||
HVX_Vector cos_y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(c3, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(z, c4))));
|
||||
cos_y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(c2, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(z, cos_y))));
|
||||
cos_y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(c1, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(z, cos_y))));
|
||||
cos_y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(c0, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(z, cos_y))));
|
||||
|
||||
HVX_Vector s4 = hvx_vec_splat_f32(2.642186986152672e-06f);
|
||||
HVX_Vector s3 = hvx_vec_splat_f32(-0.00019825318964070864f);
|
||||
HVX_Vector s2 = hvx_vec_splat_f32(0.00833326283319605f);
|
||||
HVX_Vector s1 = hvx_vec_splat_f32(-0.16666666082087775f);
|
||||
HVX_Vector s0 = hvx_vec_splat_f32(0.999999999915155f);
|
||||
|
||||
HVX_Vector sin_y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(s3, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(z, s4))));
|
||||
sin_y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(s2, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(z, sin_y))));
|
||||
sin_y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(s1, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(z, sin_y))));
|
||||
sin_y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vadd_VsfVsf(s0, Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(z, sin_y))));
|
||||
sin_y = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(y, sin_y));
|
||||
|
||||
*vcos = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(cos_y, sign));
|
||||
*vsin = Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(sin_y, sign));
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_cos_f32(HVX_Vector x) {
|
||||
HVX_Vector vcos;
|
||||
HVX_Vector vsin;
|
||||
hvx_vec_sincos_f32(x, &vcos, &vsin);
|
||||
return vcos;
|
||||
}
|
||||
|
||||
static inline HVX_Vector hvx_vec_sin_f32(HVX_Vector x) {
|
||||
HVX_Vector vcos;
|
||||
HVX_Vector vsin;
|
||||
hvx_vec_sincos_f32(x, &vcos, &vsin);
|
||||
return vsin;
|
||||
}
|
||||
|
||||
#endif /* HVX_SIN_COS_H */
|
||||
@@ -0,0 +1,189 @@
|
||||
#ifndef HVX_SQRT_H
|
||||
#define HVX_SQRT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hex-utils.h"
|
||||
|
||||
#include "hvx-base.h"
|
||||
|
||||
#define RSQRT_CONST 0x5f3759df // Constant for fast inverse square root calculation
|
||||
#define RSQRT_ONE_HALF 0x3f000000 // 0.5
|
||||
#define RSQRT_THREE_HALVES 0x3fc00000 // 1.5
|
||||
|
||||
#if __HVX_ARCH__ < 79
|
||||
#define HVX_OP_MUL(a, b) Q6_Vsf_equals_Vqf32(Q6_Vqf32_vmpy_VsfVsf(a, b))
|
||||
#else
|
||||
#define HVX_OP_MUL(a, b) Q6_Vsf_vmpy_VsfVsf(a, b)
|
||||
#endif
|
||||
|
||||
static inline HVX_Vector hvx_vec_rsqrt_f32(HVX_Vector in_vec) {
|
||||
//Algorithm :
|
||||
// x2 = input*0.5
|
||||
// y = * (long *) &input
|
||||
// y = 0x5f3759df - (y>>1)
|
||||
// y = y*(threehalfs - x2*y*y)
|
||||
|
||||
HVX_Vector rsqrtconst = Q6_V_vsplat_R(RSQRT_CONST);
|
||||
HVX_Vector onehalf = Q6_V_vsplat_R(RSQRT_ONE_HALF);
|
||||
HVX_Vector threehalfs = Q6_V_vsplat_R(RSQRT_THREE_HALVES);
|
||||
|
||||
HVX_Vector x2, y, ypower2, temp;
|
||||
|
||||
x2 = Q6_Vqf32_vmpy_VsfVsf(in_vec, onehalf);
|
||||
x2 = Q6_Vqf32_vadd_Vqf32Vsf(x2, Q6_V_vzero());
|
||||
|
||||
y = Q6_Vw_vasr_VwR(in_vec, 1);
|
||||
y = Q6_Vw_vsub_VwVw(rsqrtconst, y);
|
||||
|
||||
// 1st iteration
|
||||
ypower2 = Q6_Vqf32_vmpy_VsfVsf(y, y);
|
||||
ypower2 = Q6_Vqf32_vadd_Vqf32Vsf(ypower2, Q6_V_vzero());
|
||||
temp = Q6_Vqf32_vmpy_Vqf32Vqf32(x2, ypower2);
|
||||
temp = Q6_Vqf32_vsub_VsfVsf(threehalfs, Q6_Vsf_equals_Vqf32(temp));
|
||||
temp = Q6_Vqf32_vmpy_VsfVsf(y, Q6_Vsf_equals_Vqf32(temp));
|
||||
|
||||
// 2nd iteration
|
||||
y = Q6_Vqf32_vadd_Vqf32Vsf(temp, Q6_V_vzero());
|
||||
ypower2 = Q6_Vqf32_vmpy_Vqf32Vqf32(y, y);
|
||||
ypower2 = Q6_Vqf32_vadd_Vqf32Vsf(ypower2, Q6_V_vzero());
|
||||
temp = Q6_Vqf32_vmpy_Vqf32Vqf32(x2, ypower2);
|
||||
temp = Q6_Vqf32_vsub_VsfVsf(threehalfs, Q6_Vsf_equals_Vqf32(temp));
|
||||
temp = Q6_Vqf32_vmpy_Vqf32Vqf32(y, temp);
|
||||
|
||||
// 3rd iteration
|
||||
y = Q6_Vqf32_vadd_Vqf32Vsf(temp, Q6_V_vzero());
|
||||
ypower2 = Q6_Vqf32_vmpy_Vqf32Vqf32(y, y);
|
||||
ypower2 = Q6_Vqf32_vadd_Vqf32Vsf(ypower2, Q6_V_vzero());
|
||||
temp = Q6_Vqf32_vmpy_Vqf32Vqf32(x2, ypower2);
|
||||
temp = Q6_Vqf32_vsub_VsfVsf(threehalfs, Q6_Vsf_equals_Vqf32(temp));
|
||||
temp = Q6_Vqf32_vmpy_Vqf32Vqf32(y, temp);
|
||||
|
||||
return Q6_Vsf_equals_Vqf32(temp);
|
||||
}
|
||||
|
||||
// Compute sqrt(x) as x*inv_sqrt(x)
|
||||
#define hvx_sqrt_f32_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t nvec = n / VLEN_FP32; \
|
||||
const uint32_t nloe = n % VLEN_FP32; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
HVX_Vector inv_sqrt = hvx_vec_rsqrt_f32(vsrc[i]); \
|
||||
HVX_Vector sqrt_res = HVX_OP_MUL(inv_sqrt, vsrc[i]); \
|
||||
vdst[i] = sqrt_res; \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_Vector inv_sqrt = hvx_vec_rsqrt_f32(vsrc[i]); \
|
||||
HVX_Vector sqrt_res = HVX_OP_MUL(inv_sqrt, vsrc[i]); \
|
||||
vec_store((void *) &vdst[i], nloe * SIZEOF_FP32, sqrt_res); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_sqrt_f32_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sqrt_f32_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sqrt_f32_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_sqrt_f32_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sqrt_f32_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sqrt_f32_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_sqrt_f32_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_sqrt_f32_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_sqrt_f32(uint8_t * restrict dst, const uint8_t * restrict src, const int num_elems) {
|
||||
if ((unsigned long) dst % 128 == 0) {
|
||||
if ((unsigned long) src % 128 == 0) {
|
||||
hvx_sqrt_f32_aa(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_sqrt_f32_au(dst, src, num_elems);
|
||||
}
|
||||
} else {
|
||||
if ((unsigned long) src % 128 == 0) {
|
||||
hvx_sqrt_f32_ua(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_sqrt_f32_uu(dst, src, num_elems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute sqrt(x) for f16 by promoting to f32, applying hvx_vec_rsqrt_f32, and narrowing back.
|
||||
#define hvx_sqrt_f16_loop_body(dst_type, src_type, vec_store) \
|
||||
do { \
|
||||
dst_type * restrict vdst = (dst_type *) dst; \
|
||||
src_type * restrict vsrc = (src_type *) src; \
|
||||
\
|
||||
const uint32_t nvec = n / VLEN_FP16; \
|
||||
const uint32_t nloe = n % VLEN_FP16; \
|
||||
\
|
||||
uint32_t i = 0; \
|
||||
\
|
||||
_Pragma("unroll(4)") \
|
||||
for (; i < nvec; i++) { \
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]); \
|
||||
HVX_Vector r0 = HVX_OP_MUL(hvx_vec_rsqrt_f32(Q6_V_lo_W(p)), Q6_V_lo_W(p)); \
|
||||
HVX_Vector r1 = HVX_OP_MUL(hvx_vec_rsqrt_f32(Q6_V_hi_W(p)), Q6_V_hi_W(p)); \
|
||||
vdst[i] = hvx_vec_f32_to_f16(r0, r1); \
|
||||
} \
|
||||
if (nloe) { \
|
||||
HVX_VectorPair p = hvx_vec_f16_to_f32(vsrc[i]); \
|
||||
HVX_Vector r0 = HVX_OP_MUL(hvx_vec_rsqrt_f32(Q6_V_lo_W(p)), Q6_V_lo_W(p)); \
|
||||
HVX_Vector r1 = HVX_OP_MUL(hvx_vec_rsqrt_f32(Q6_V_hi_W(p)), Q6_V_hi_W(p)); \
|
||||
HVX_Vector v = hvx_vec_f32_to_f16(r0, r1); \
|
||||
vec_store((void *) &vdst[i], nloe * SIZEOF_FP16, v); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void hvx_sqrt_f16_aa(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sqrt_f16_loop_body(HVX_Vector, HVX_Vector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sqrt_f16_au(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) dst % 128 == 0);
|
||||
hvx_sqrt_f16_loop_body(HVX_Vector, HVX_UVector, hvx_vec_store_a);
|
||||
}
|
||||
|
||||
static inline void hvx_sqrt_f16_ua(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
assert((unsigned long) src % 128 == 0);
|
||||
hvx_sqrt_f16_loop_body(HVX_UVector, HVX_Vector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_sqrt_f16_uu(uint8_t * restrict dst, const uint8_t * restrict src, uint32_t n) {
|
||||
hvx_sqrt_f16_loop_body(HVX_UVector, HVX_UVector, hvx_vec_store_u);
|
||||
}
|
||||
|
||||
static inline void hvx_sqrt_f16(uint8_t * restrict dst, const uint8_t * restrict src, const int num_elems) {
|
||||
if ((unsigned long) dst % 128 == 0) {
|
||||
if ((unsigned long) src % 128 == 0) {
|
||||
hvx_sqrt_f16_aa(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_sqrt_f16_au(dst, src, num_elems);
|
||||
}
|
||||
} else {
|
||||
if ((unsigned long) src % 128 == 0) {
|
||||
hvx_sqrt_f16_ua(dst, src, num_elems);
|
||||
} else {
|
||||
hvx_sqrt_f16_uu(dst, src, num_elems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HVX_SQRT_H */
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef HVX_TYPES_H
|
||||
#define HVX_TYPES_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <hexagon_types.h>
|
||||
|
||||
#define SIZEOF_FP32 (4)
|
||||
#define SIZEOF_FP16 (2)
|
||||
#define VLEN (128)
|
||||
#define VLEN_FP32 (VLEN / SIZEOF_FP32)
|
||||
#define VLEN_FP16 (VLEN / SIZEOF_FP16)
|
||||
|
||||
typedef union {
|
||||
HVX_Vector v;
|
||||
uint8_t b[VLEN];
|
||||
uint16_t h[VLEN_FP16];
|
||||
uint32_t w[VLEN_FP32];
|
||||
__fp16 fp16[VLEN_FP16];
|
||||
float fp32[VLEN_FP32];
|
||||
} __attribute__((aligned(VLEN), packed)) HVX_VectorAlias;
|
||||
|
||||
typedef struct {
|
||||
HVX_Vector v[2];
|
||||
} HVX_Vector_x2;
|
||||
|
||||
typedef struct {
|
||||
HVX_Vector v[4];
|
||||
} HVX_Vector_x4;
|
||||
|
||||
typedef struct {
|
||||
HVX_Vector v[8];
|
||||
} HVX_Vector_x8;
|
||||
|
||||
#endif /* HVX_TYPES_H */
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef HVX_UTILS_H
|
||||
#define HVX_UTILS_H
|
||||
|
||||
#include "hex-utils.h"
|
||||
|
||||
#include "hvx-types.h"
|
||||
#include "hvx-copy.h"
|
||||
#include "hvx-repl.h"
|
||||
#include "hvx-scale.h"
|
||||
#include "hvx-exp.h"
|
||||
#include "hvx-erf.h"
|
||||
#include "hvx-inverse.h"
|
||||
#include "hvx-reduce.h"
|
||||
#include "hvx-sigmoid.h"
|
||||
#include "hvx-sqrt.h"
|
||||
#include "hvx-arith.h"
|
||||
#include "hvx-div.h"
|
||||
#include "hvx-floor.h"
|
||||
#include "hvx-sin-cos.h"
|
||||
#include "hvx-base.h"
|
||||
#include "hvx-pow.h"
|
||||
#include "hvx-log.h"
|
||||
#include "hvx-norm.h"
|
||||
|
||||
#endif /* HVX_UTILS_H */
|
||||
@@ -0,0 +1,932 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <hexagon_protos.h>
|
||||
#include <hexagon_types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "dma-queue.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "im2col-ops.h"
|
||||
|
||||
struct htp_im2col_context {
|
||||
struct htp_ops_context * octx;
|
||||
|
||||
uint32_t pe_row_base; // first output row index assigned to this dev (DMA path)
|
||||
uint32_t pe_nrows; // number of output rows assigned to this dev (DMA path)
|
||||
uint32_t pe_rows_per_thread; // output rows per worker
|
||||
uint32_t pe_src_row_bytes; // one output row's source, rounded 256
|
||||
uint32_t pe_dst_row_bytes; // one output row's dst: OW*patch_stride*2, rounded 256
|
||||
|
||||
// Patch-embed DMA path VTCM ping-pong.
|
||||
uint8_t * pe_vtcm_src; // base of the 2x src buffers region
|
||||
uint8_t * pe_vtcm_dst; // base of the 2x dst buffers region
|
||||
uint32_t pe_src_size_per_thread; // 2 * pe_src_row_bytes
|
||||
uint32_t pe_dst_size_per_thread; // 2 * pe_dst_row_bytes
|
||||
|
||||
uint32_t pe_owb; // output-col block size
|
||||
uint32_t pe_wb; // staged source window width
|
||||
uint32_t pe_nbpr; // blocks per row
|
||||
|
||||
// Precomputed dimensions for worker threads
|
||||
uint32_t N;
|
||||
uint32_t IC;
|
||||
uint32_t ID, IH, IW;
|
||||
uint32_t KD, KH, KW;
|
||||
uint32_t OD, OH, OW;
|
||||
uint32_t patch_stride;
|
||||
|
||||
// Per-thread initial coordinates (removes division from thread prologue)
|
||||
uint32_t thread_in[HTP_MAX_NTHREADS];
|
||||
uint32_t thread_iod[HTP_MAX_NTHREADS];
|
||||
uint32_t thread_ioh[HTP_MAX_NTHREADS];
|
||||
|
||||
void * compute;
|
||||
uint32_t dst_elem;
|
||||
};
|
||||
|
||||
typedef void (*im2col_blocked_compute_fn_t)(
|
||||
const struct htp_im2col_context * ictx,
|
||||
void * restrict dstb, const float * restrict srcb,
|
||||
float * restrict scratch,
|
||||
uint32_t nb, uint32_t c0, uint32_t ioh);
|
||||
|
||||
typedef void (*im2col_patchembed_compute_fn_t)(
|
||||
const struct htp_im2col_context * ictx,
|
||||
void * restrict dstb, const float * restrict srcb, uint32_t ioh);
|
||||
|
||||
typedef void (*im2col_3d_blocked_compute_fn_t)(
|
||||
const struct htp_im2col_context * ictx,
|
||||
void * restrict dstb, const float * restrict srcb,
|
||||
float * restrict scratch,
|
||||
uint32_t nb, uint32_t c0, uint32_t iod, uint32_t ioh);
|
||||
|
||||
typedef void (*im2col_3d_patchembed_compute_fn_t)(
|
||||
const struct htp_im2col_context * ictx,
|
||||
void * restrict dstb, const float * restrict srcb);
|
||||
|
||||
struct im2col_cursor {
|
||||
uint32_t r;
|
||||
uint32_t in;
|
||||
uint32_t iod;
|
||||
uint32_t ioh;
|
||||
uint32_t c0;
|
||||
};
|
||||
|
||||
static inline void im2col_step_row(struct im2col_cursor * c, const struct htp_im2col_context * ictx) {
|
||||
c->r++;
|
||||
c->ioh++;
|
||||
if (c->ioh >= ictx->OH) {
|
||||
c->ioh = 0;
|
||||
c->in++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void im2col_step_block(struct im2col_cursor * c, const struct htp_im2col_context * ictx) {
|
||||
c->c0 += ictx->pe_owb;
|
||||
if (c->c0 >= ictx->OW) {
|
||||
c->c0 = 0;
|
||||
c->r++;
|
||||
c->ioh++;
|
||||
if (c->ioh >= ictx->OH) {
|
||||
c->ioh = 0;
|
||||
c->in++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void im2col_blocked_dma_stage(struct htp_im2col_context * ictx, dma_queue * dma_q,
|
||||
float * srcb, const struct im2col_cursor * c) {
|
||||
const uint32_t in = c->in, ioh = c->ioh, c0 = c->c0;
|
||||
const struct htp_ops_context * octx = ictx->octx;
|
||||
const struct htp_tensor * src1 = octx->src[1];
|
||||
const int32_t s0 = octx->op_params[0], s1 = octx->op_params[1];
|
||||
const int32_t p0 = octx->op_params[2], p1 = octx->op_params[3];
|
||||
const int32_t d1 = octx->op_params[5];
|
||||
const bool is_2D = (octx->op_params[6] == 1);
|
||||
const uint32_t IC = ictx->IC, IH = ictx->IH, IW = ictx->IW;
|
||||
const uint32_t KH = ictx->KH;
|
||||
const uint32_t Wb = ictx->pe_wb;
|
||||
const dma_addr_t src_data = src1->data;
|
||||
const int32_t win0 = (int32_t) c0 * s0 - p0;
|
||||
const size_t src_channel_stride = is_2D ? src1->nb[2] : src1->nb[1];
|
||||
const size_t batch_offset = is_2D ? (size_t) in * src1->nb[3] : (size_t) in * src1->nb[2];
|
||||
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) {
|
||||
const int32_t iih = (int32_t) ioh * s1 + (int32_t) ikh * d1 - p1;
|
||||
if (iih < 0 || iih >= (int32_t) IH) {
|
||||
dma_queue_push(dma_q, dma_make_data(srcb, 0), 0, 0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
const int32_t lo = win0 < 0 ? -win0 : 0;
|
||||
int32_t hi = (int32_t) IW - win0;
|
||||
if (hi > (int32_t) Wb) {
|
||||
hi = (int32_t) Wb;
|
||||
}
|
||||
if (hi <= lo) {
|
||||
dma_queue_push(dma_q, dma_make_data(srcb, 0), 0, 0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
const uint32_t cpw = (uint32_t) (hi - lo);
|
||||
float * vdst = srcb + (size_t) ikh * Wb + lo;
|
||||
const dma_addr_t vsrc = src_data + batch_offset + (size_t) iih * src1->nb[1] + (size_t) (win0 + lo) * src1->nb[0];
|
||||
dma_queue_push(dma_q, dma_make_data(vdst, vsrc),
|
||||
(size_t) KH * Wb * sizeof(float), src_channel_stride,
|
||||
cpw * sizeof(float), IC);
|
||||
}
|
||||
}
|
||||
|
||||
#define IM2COL_BLOCKED_COMPUTE_BODY(FNAME, DST_CTYPE, COPY_FN, SPLAT_FN) \
|
||||
static void FNAME( \
|
||||
const struct htp_im2col_context * ictx, \
|
||||
void * restrict dstb_raw, const float * restrict srcb, \
|
||||
float * restrict scratch, \
|
||||
uint32_t nb, uint32_t c0, uint32_t ioh) { \
|
||||
DST_CTYPE * restrict dstb = (DST_CTYPE *) dstb_raw; \
|
||||
const struct htp_ops_context * octx = ictx->octx; \
|
||||
const int32_t s0 = octx->op_params[0], s1 = octx->op_params[1]; \
|
||||
const int32_t p0 = octx->op_params[2], p1 = octx->op_params[3]; \
|
||||
const int32_t d0 = octx->op_params[4], d1 = octx->op_params[5]; \
|
||||
const uint32_t IC = ictx->IC, IH = ictx->IH, IW = ictx->IW; \
|
||||
const uint32_t KH = ictx->KH, KW = ictx->KW; \
|
||||
const uint32_t Wb = ictx->pe_wb; \
|
||||
const uint32_t patch_stride = ictx->patch_stride; \
|
||||
const int32_t win0 = (int32_t) c0 * s0 - p0; \
|
||||
\
|
||||
for (uint32_t j = 0; j < nb; j++) { \
|
||||
const uint32_t iow = c0 + j; \
|
||||
DST_CTYPE * dst_patch = dstb + (size_t) j * patch_stride; \
|
||||
const int32_t iiw0 = (int32_t) iow * s0 - p0; \
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) { \
|
||||
const int32_t iih = (int32_t) ioh * s1 + (int32_t) ikh * d1 - p1; \
|
||||
const int okh = (iih >= 0 && iih < (int32_t) IH); \
|
||||
for (uint32_t iic = 0; iic < IC; iic++) { \
|
||||
DST_CTYPE * out_run = dst_patch + (size_t) (iic * KH + ikh) * KW; \
|
||||
if (!okh) { \
|
||||
SPLAT_FN(out_run, 0, KW); \
|
||||
continue; \
|
||||
} \
|
||||
const float * vrow = srcb + (size_t) (iic * KH + ikh) * Wb; \
|
||||
if (d0 == 1) { \
|
||||
const int32_t lo = iiw0 < 0 ? -iiw0 : 0; \
|
||||
int32_t hi = (int32_t) IW - iiw0; \
|
||||
if (hi > (int32_t) KW) { \
|
||||
hi = (int32_t) KW; \
|
||||
} \
|
||||
if (hi <= lo) { \
|
||||
SPLAT_FN(out_run, 0, KW); \
|
||||
} else { \
|
||||
if (lo > 0) { \
|
||||
SPLAT_FN(out_run, 0, (uint32_t) lo); \
|
||||
} \
|
||||
COPY_FN((uint8_t *) (out_run + lo), \
|
||||
(const uint8_t *) (vrow + (iiw0 + lo - win0)), \
|
||||
(uint32_t) (hi - lo)); \
|
||||
if (hi < (int32_t) KW) { \
|
||||
SPLAT_FN(out_run + hi, 0, (uint32_t) (KW - hi)); \
|
||||
} \
|
||||
} \
|
||||
continue; \
|
||||
} \
|
||||
for (uint32_t ikw = 0; ikw < KW; ikw += 64) { \
|
||||
const uint32_t chunk = MIN(KW - ikw, 64); \
|
||||
for (uint32_t c = 0; c < chunk; c++) { \
|
||||
const int32_t iiw = iiw0 + (int32_t) (ikw + c) * d0; \
|
||||
scratch[c] = (iiw < 0 || iiw >= (int32_t) IW) ? 0.0f : vrow[iiw - win0]; \
|
||||
} \
|
||||
COPY_FN((uint8_t *) (out_run + ikw), (const uint8_t *) scratch, chunk); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
IM2COL_BLOCKED_COMPUTE_BODY(im2col_blocked_compute_f16, uint16_t, hvx_copy_f16_f32_uu, hvx_splat_u16_u)
|
||||
IM2COL_BLOCKED_COMPUTE_BODY(im2col_blocked_compute_f32, float, hvx_copy_f32_uu, hvx_splat_f32_u)
|
||||
|
||||
static void im2col_blocked_dma_thread(unsigned int nth, unsigned int ith, void * data) {
|
||||
(void) nth;
|
||||
struct htp_im2col_context * ictx = (struct htp_im2col_context *) data;
|
||||
struct htp_ops_context * octx = ictx->octx;
|
||||
struct htp_thread_trace * restrict tr = &octx->ctx->trace[ith];
|
||||
const uint32_t OW = ictx->OW;
|
||||
const uint32_t KH = ictx->KH;
|
||||
const uint32_t owb = ictx->pe_owb;
|
||||
const uint32_t nbpr = ictx->pe_nbpr;
|
||||
const uint32_t patch_stride = ictx->patch_stride;
|
||||
const uint32_t dst_elem = ictx->dst_elem;
|
||||
im2col_blocked_compute_fn_t compute = (im2col_blocked_compute_fn_t) ictx->compute;
|
||||
const dma_addr_t dst_data = octx->dst->data;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
uint8_t * srcb_base = ictx->pe_vtcm_src + ith * ictx->pe_src_size_per_thread;
|
||||
uint8_t * dstb_base = ictx->pe_vtcm_dst + ith * ictx->pe_dst_size_per_thread;
|
||||
float * srcb2[2] = { (float *) srcb_base, (float *) (srcb_base + ictx->pe_src_row_bytes) };
|
||||
void * dstb2[2] = { dstb_base, dstb_base + ictx->pe_dst_row_bytes };
|
||||
float * scratch = (float *) (srcb_base + 2 * ictx->pe_src_row_bytes);
|
||||
const uint32_t row_end_max = ictx->pe_row_base + ictx->pe_nrows;
|
||||
const uint32_t row_start = ictx->pe_row_base + ictx->pe_rows_per_thread * ith;
|
||||
const uint32_t row_end = MIN(row_start + ictx->pe_rows_per_thread, row_end_max);
|
||||
if (row_start >= row_end) {
|
||||
return;
|
||||
}
|
||||
const uint32_t nrows_local = row_end - row_start;
|
||||
const uint32_t total_blocks = nrows_local * nbpr;
|
||||
if (total_blocks == 0) {
|
||||
return;
|
||||
}
|
||||
struct im2col_cursor cur = {
|
||||
.r = row_start,
|
||||
.in = ictx->thread_in[ith],
|
||||
.iod = 0,
|
||||
.ioh = ictx->thread_ioh[ith],
|
||||
.c0 = 0,
|
||||
};
|
||||
struct im2col_cursor stage = cur;
|
||||
for (uint32_t i = 0; i < total_blocks && i < 2; i++) {
|
||||
dma_queue_push(dma_q, dma_make_data(dst_data, dstb2[i]), 0, 0, 0, 0);
|
||||
im2col_blocked_dma_stage(ictx, dma_q, srcb2[i], &stage);
|
||||
im2col_step_block(&stage, ictx);
|
||||
}
|
||||
for (uint32_t bi = 0; bi < total_blocks; bi++) {
|
||||
const uint32_t buf = bi & 1u;
|
||||
float * srcb = srcb2[buf];
|
||||
void * dstb = dstb2[buf];
|
||||
const uint32_t nb = MIN(owb, OW - cur.c0);
|
||||
dma_queue_pop(dma_q);
|
||||
for (uint32_t k = 0; k < KH; k++) {
|
||||
dma_queue_pop(dma_q);
|
||||
}
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, cur.r);
|
||||
compute(ictx, dstb, srcb, scratch, nb, cur.c0, cur.ioh);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, cur.r);
|
||||
const dma_addr_t ddr = dst_data + (size_t) (cur.r * OW + cur.c0) * patch_stride * dst_elem;
|
||||
dma_queue_push(dma_q, dma_make_data(ddr, dstb),
|
||||
nb * patch_stride * dst_elem, nb * patch_stride * dst_elem,
|
||||
nb * patch_stride * dst_elem, 1);
|
||||
if (bi + 2 < total_blocks) {
|
||||
im2col_blocked_dma_stage(ictx, dma_q, srcb, &stage);
|
||||
im2col_step_block(&stage, ictx);
|
||||
}
|
||||
im2col_step_block(&cur, ictx);
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
static void im2col_patchembed_dma_stage(struct htp_im2col_context * ictx, dma_queue * dma_q,
|
||||
float * srcb, const struct im2col_cursor * c) {
|
||||
const struct htp_ops_context * octx = ictx->octx;
|
||||
const uint32_t IC = ictx->IC, IH = ictx->IH, IW = ictx->IW;
|
||||
const uint32_t KH = ictx->KH;
|
||||
const uint32_t in = c->in, ioh = c->ioh;
|
||||
const size_t src_channel_stride = (size_t) IH * IW * sizeof(float);
|
||||
const size_t vtcm_channel_stride = (size_t) KH * IW * sizeof(float);
|
||||
const dma_addr_t src_data = octx->src[1]->data;
|
||||
const dma_addr_t vsrc = src_data + (size_t) (((in * IC) * IH + ioh * KH) * IW) * sizeof(float);
|
||||
|
||||
dma_queue_push(dma_q, dma_make_data(srcb, vsrc),
|
||||
vtcm_channel_stride, src_channel_stride,
|
||||
vtcm_channel_stride, IC);
|
||||
}
|
||||
|
||||
static void im2col_patchembed_compute_f16(
|
||||
const struct htp_im2col_context * ictx,
|
||||
void * restrict dstb_raw, const float * restrict srcb, uint32_t ioh) {
|
||||
uint16_t * restrict dstb = (uint16_t *) dstb_raw;
|
||||
const uint32_t IC = ictx->IC, IH = ictx->IH, IW = ictx->IW;
|
||||
const uint32_t KH = ictx->KH, KW = ictx->KW;
|
||||
const uint32_t OW = ictx->OW, patch_stride = ictx->patch_stride;
|
||||
|
||||
for (uint32_t iow = 0; iow < OW; iow++) {
|
||||
uint16_t * dst_patch = dstb + (size_t) iow * patch_stride;
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) {
|
||||
const int32_t iih = (int32_t) ioh * (int32_t) KH + (int32_t) ikh;
|
||||
for (uint32_t iic = 0; iic < IC; iic++) {
|
||||
uint16_t * out_run = dst_patch + (size_t) (iic * KH + ikh) * KW;
|
||||
if (iih < 0 || iih >= (int32_t) IH) {
|
||||
hvx_splat_u16_u(out_run, 0, KW);
|
||||
continue;
|
||||
}
|
||||
const float * src_run = srcb + (size_t) (iic * KH + ikh) * IW + (size_t) iow * KW;
|
||||
hvx_copy_f16_f32_uu((uint8_t *) out_run, (const uint8_t *) src_run, KW);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void im2col_patchembed_compute_f32(
|
||||
const struct htp_im2col_context * ictx,
|
||||
void * restrict dstb_raw, const float * restrict srcb, uint32_t ioh) {
|
||||
float * restrict dstb = (float *) dstb_raw;
|
||||
const uint32_t IC = ictx->IC, IH = ictx->IH, IW = ictx->IW;
|
||||
const uint32_t KH = ictx->KH, KW = ictx->KW;
|
||||
const uint32_t OW = ictx->OW, patch_stride = ictx->patch_stride;
|
||||
|
||||
for (uint32_t iow = 0; iow < OW; iow++) {
|
||||
float * dst_patch = dstb + (size_t) iow * patch_stride;
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) {
|
||||
const int32_t iih = (int32_t) ioh * (int32_t) KH + (int32_t) ikh;
|
||||
for (uint32_t iic = 0; iic < IC; iic++) {
|
||||
float * out_run = dst_patch + (size_t) (iic * KH + ikh) * KW;
|
||||
if (iih < 0 || iih >= (int32_t) IH) {
|
||||
hvx_splat_f32_u(out_run, 0, KW);
|
||||
continue;
|
||||
}
|
||||
const float * src_run = srcb + (size_t) (iic * KH + ikh) * IW + (size_t) iow * KW;
|
||||
hvx_copy_f32_uu((uint8_t *) out_run, (const uint8_t *) src_run, KW);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void im2col_patchembed_dma_thread(unsigned int nth, unsigned int ith, void * data) {
|
||||
(void) nth;
|
||||
struct htp_im2col_context * ictx = (struct htp_im2col_context *) data;
|
||||
struct htp_ops_context * octx = ictx->octx;
|
||||
struct htp_thread_trace * restrict tr = &octx->ctx->trace[ith];
|
||||
const uint32_t OW = ictx->OW;
|
||||
const uint32_t patch_stride = ictx->patch_stride;
|
||||
const uint32_t dst_elem = ictx->dst_elem;
|
||||
im2col_patchembed_compute_fn_t compute = (im2col_patchembed_compute_fn_t) ictx->compute;
|
||||
const dma_addr_t dst_data = octx->dst->data;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
uint8_t * srcb_base = ictx->pe_vtcm_src + ith * ictx->pe_src_size_per_thread;
|
||||
uint8_t * dstb_base = ictx->pe_vtcm_dst + ith * ictx->pe_dst_size_per_thread;
|
||||
float * srcb2[2] = { (float *) srcb_base, (float *) (srcb_base + ictx->pe_src_row_bytes) };
|
||||
void * dstb2[2] = { dstb_base, dstb_base + ictx->pe_dst_row_bytes };
|
||||
const uint32_t row_end_max = ictx->pe_row_base + ictx->pe_nrows;
|
||||
const uint32_t row_start = ictx->pe_row_base + ictx->pe_rows_per_thread * ith;
|
||||
const uint32_t row_end = MIN(row_start + ictx->pe_rows_per_thread, row_end_max);
|
||||
if (row_start >= row_end) {
|
||||
return;
|
||||
}
|
||||
const uint32_t total_rows = row_end - row_start;
|
||||
if (total_rows == 0) {
|
||||
return;
|
||||
}
|
||||
struct im2col_cursor cur = {
|
||||
.r = row_start,
|
||||
.in = ictx->thread_in[ith],
|
||||
.iod = 0,
|
||||
.ioh = ictx->thread_ioh[ith],
|
||||
.c0 = 0,
|
||||
};
|
||||
struct im2col_cursor stage = cur;
|
||||
for (uint32_t i = 0; i < total_rows && i < 2; i++) {
|
||||
dma_queue_push(dma_q, dma_make_data(dst_data, dstb2[i]), 0, 0, 0, 0);
|
||||
im2col_patchembed_dma_stage(ictx, dma_q, srcb2[i], &stage);
|
||||
im2col_step_row(&stage, ictx);
|
||||
}
|
||||
for (uint32_t ri = 0; ri < total_rows; ri++) {
|
||||
const uint32_t buf = ri & 1u;
|
||||
float * srcb = srcb2[buf];
|
||||
void * dstb = dstb2[buf];
|
||||
dma_queue_pop(dma_q);
|
||||
dma_queue_pop(dma_q);
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, cur.r);
|
||||
compute(ictx, dstb, srcb, cur.ioh);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, cur.r);
|
||||
const dma_addr_t ddr_row = dst_data + (size_t) cur.r * OW * patch_stride * dst_elem;
|
||||
dma_queue_push(dma_q, dma_make_data(ddr_row, dstb),
|
||||
OW * patch_stride * dst_elem, OW * patch_stride * dst_elem,
|
||||
OW * patch_stride * dst_elem, 1);
|
||||
if (ri + 2 < total_rows) {
|
||||
im2col_patchembed_dma_stage(ictx, dma_q, srcb, &stage);
|
||||
im2col_step_row(&stage, ictx);
|
||||
}
|
||||
im2col_step_row(&cur, ictx);
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
static inline void im2col_3d_step_row(struct im2col_cursor * c, const struct htp_im2col_context * ictx) {
|
||||
c->r++;
|
||||
c->ioh++;
|
||||
if (c->ioh >= ictx->OH) {
|
||||
c->ioh = 0;
|
||||
c->iod++;
|
||||
if (c->iod >= ictx->OD) {
|
||||
c->iod = 0;
|
||||
c->in++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void im2col_3d_step_block(struct im2col_cursor * c, const struct htp_im2col_context * ictx) {
|
||||
c->c0 += ictx->pe_owb;
|
||||
if (c->c0 >= ictx->OW) {
|
||||
c->c0 = 0;
|
||||
c->r++;
|
||||
c->ioh++;
|
||||
if (c->ioh >= ictx->OH) {
|
||||
c->ioh = 0;
|
||||
c->iod++;
|
||||
if (c->iod >= ictx->OD) {
|
||||
c->iod = 0;
|
||||
c->in++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void im2col_3d_blocked_dma_stage(struct htp_im2col_context * ictx, dma_queue * dma_q,
|
||||
float * srcb, const struct im2col_cursor * c) {
|
||||
const uint32_t in = c->in, iod = c->iod, ioh = c->ioh, c0 = c->c0;
|
||||
const struct htp_ops_context * octx = ictx->octx;
|
||||
const struct htp_tensor * src1 = octx->src[1];
|
||||
const int32_t s0 = octx->op_params[0], s1 = octx->op_params[1], s2 = octx->op_params[2];
|
||||
const int32_t p0 = octx->op_params[3], p1 = octx->op_params[4], p2 = octx->op_params[5];
|
||||
const int32_t d1 = octx->op_params[7], d2 = octx->op_params[8];
|
||||
const uint32_t IC = ictx->IC;
|
||||
const uint32_t ID = ictx->ID, IH = ictx->IH, IW = ictx->IW;
|
||||
const uint32_t KD = ictx->KD, KH = ictx->KH;
|
||||
const uint32_t Wb = ictx->pe_wb;
|
||||
const int32_t win0 = (int32_t) c0 * s0 - p0;
|
||||
const dma_addr_t src_data = src1->data;
|
||||
const size_t src_channel_stride = src1->nb[3];
|
||||
const size_t vtcm_channel_stride = (size_t) KD * KH * Wb * sizeof(float);
|
||||
const size_t batch_offset = (size_t) (in * IC) * src1->nb[3];
|
||||
|
||||
for (uint32_t ikd = 0; ikd < KD; ikd++) {
|
||||
const int32_t iid = (int32_t) iod * s2 + (int32_t) ikd * d2 - p2;
|
||||
const bool valid_d = (iid >= 0 && iid < (int32_t) ID);
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) {
|
||||
const int32_t iih = (int32_t) ioh * s1 + (int32_t) ikh * d1 - p1;
|
||||
if (!valid_d || iih < 0 || iih >= (int32_t) IH) {
|
||||
dma_queue_push(dma_q, dma_make_data(srcb, 0), 0, 0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
const int32_t lo = win0 < 0 ? -win0 : 0;
|
||||
int32_t hi = (int32_t) IW - win0;
|
||||
if (hi > (int32_t) Wb) {
|
||||
hi = (int32_t) Wb;
|
||||
}
|
||||
if (hi <= lo) {
|
||||
dma_queue_push(dma_q, dma_make_data(srcb, 0), 0, 0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
float * vdst = srcb + (size_t) (ikd * KH + ikh) * Wb + lo;
|
||||
const dma_addr_t vsrc = src_data + batch_offset +
|
||||
(size_t) iid * src1->nb[2] +
|
||||
(size_t) iih * src1->nb[1] +
|
||||
(size_t) (win0 + lo) * src1->nb[0];
|
||||
dma_queue_push(dma_q, dma_make_data(vdst, vsrc),
|
||||
vtcm_channel_stride, src_channel_stride,
|
||||
(size_t) (hi - lo) * sizeof(float), IC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define IM2COL_3D_BLOCKED_COMPUTE_BODY(FNAME, DST_CTYPE, COPY_FN, SPLAT_FN) \
|
||||
static void FNAME(const struct htp_im2col_context * ictx, \
|
||||
void * restrict dstb_raw, const float * restrict srcb, \
|
||||
float * restrict scratch, \
|
||||
uint32_t nb, uint32_t c0, uint32_t iod, uint32_t ioh) { \
|
||||
DST_CTYPE * restrict dstb = (DST_CTYPE *) dstb_raw; \
|
||||
const struct htp_ops_context * octx = ictx->octx; \
|
||||
const int32_t s0 = octx->op_params[0], s1 = octx->op_params[1], s2 = octx->op_params[2]; \
|
||||
const int32_t p0 = octx->op_params[3], p1 = octx->op_params[4], p2 = octx->op_params[5]; \
|
||||
const int32_t d0 = octx->op_params[6], d1 = octx->op_params[7], d2 = octx->op_params[8]; \
|
||||
const uint32_t IC = ictx->IC; \
|
||||
const uint32_t ID = ictx->ID, IH = ictx->IH, IW = ictx->IW; \
|
||||
const uint32_t KD = ictx->KD, KH = ictx->KH, KW = ictx->KW; \
|
||||
const uint32_t patch_stride = ictx->patch_stride; \
|
||||
const uint32_t Wb = ictx->pe_wb; \
|
||||
const int32_t win0 = (int32_t) c0 * s0 - p0; \
|
||||
for (uint32_t j = 0; j < nb; j++) { \
|
||||
const uint32_t iow = c0 + j; \
|
||||
const int32_t iiw0 = (int32_t) iow * s0 - p0; \
|
||||
DST_CTYPE * dst_patch = dstb + (size_t) j * patch_stride; \
|
||||
for (uint32_t iic = 0; iic < IC; iic++) { \
|
||||
for (uint32_t ikd = 0; ikd < KD; ikd++) { \
|
||||
const int32_t iid = (int32_t) iod * s2 + (int32_t) ikd * d2 - p2; \
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) { \
|
||||
const int32_t iih = (int32_t) ioh * s1 + (int32_t) ikh * d1 - p1; \
|
||||
DST_CTYPE * out_run = dst_patch + \
|
||||
(size_t) (iic * KD * KH + ikd * KH + ikh) * KW; \
|
||||
if (iid < 0 || iid >= (int32_t) ID || iih < 0 || iih >= (int32_t) IH) { \
|
||||
SPLAT_FN(out_run, 0, KW); \
|
||||
continue; \
|
||||
} \
|
||||
const float * vrow = srcb + (size_t) (iic * KD * KH + ikd * KH + ikh) * Wb; \
|
||||
if (d0 == 1) { \
|
||||
const int32_t lo = iiw0 < 0 ? -iiw0 : 0; \
|
||||
int32_t hi = (int32_t) IW - iiw0; \
|
||||
if (hi > (int32_t) KW) { \
|
||||
hi = (int32_t) KW; \
|
||||
} \
|
||||
if (hi <= lo) { \
|
||||
SPLAT_FN(out_run, 0, KW); \
|
||||
} else { \
|
||||
if (lo > 0) { \
|
||||
SPLAT_FN(out_run, 0, (uint32_t) lo); \
|
||||
} \
|
||||
COPY_FN((uint8_t *) (out_run + lo), \
|
||||
(const uint8_t *) (vrow + (iiw0 + lo - win0)), \
|
||||
(uint32_t) (hi - lo)); \
|
||||
if (hi < (int32_t) KW) { \
|
||||
SPLAT_FN(out_run + hi, 0, (uint32_t) (KW - hi)); \
|
||||
} \
|
||||
} \
|
||||
continue; \
|
||||
} \
|
||||
for (uint32_t ikw = 0; ikw < KW; ikw += 64) { \
|
||||
const uint32_t chunk = MIN(KW - ikw, 64); \
|
||||
for (uint32_t c = 0; c < chunk; c++) { \
|
||||
const int32_t iiw = iiw0 + (int32_t) (ikw + c) * d0; \
|
||||
scratch[c] = (iiw < 0 || iiw >= (int32_t) IW) ? 0.0f : vrow[iiw - win0]; \
|
||||
} \
|
||||
COPY_FN((uint8_t *) (out_run + ikw), (const uint8_t *) scratch, chunk); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
IM2COL_3D_BLOCKED_COMPUTE_BODY(im2col_3d_blocked_compute_f16, uint16_t, hvx_copy_f16_f32_uu, hvx_splat_u16_u)
|
||||
IM2COL_3D_BLOCKED_COMPUTE_BODY(im2col_3d_blocked_compute_f32, float, hvx_copy_f32_uu, hvx_splat_f32_u)
|
||||
|
||||
static void im2col_3d_blocked_dma_thread(unsigned int nth, unsigned int ith, void * data) {
|
||||
(void) nth;
|
||||
struct htp_im2col_context * ictx = (struct htp_im2col_context *) data;
|
||||
struct htp_ops_context * octx = ictx->octx;
|
||||
struct htp_thread_trace * restrict tr = &octx->ctx->trace[ith];
|
||||
const struct htp_tensor * restrict dst = octx->dst;
|
||||
const uint32_t OW = ictx->OW;
|
||||
const uint32_t KD = ictx->KD, KH = ictx->KH;
|
||||
const uint32_t kd_kh = KD * KH;
|
||||
const uint32_t patch_stride = ictx->patch_stride;
|
||||
const uint32_t owb = ictx->pe_owb;
|
||||
const uint32_t nbpr = ictx->pe_nbpr;
|
||||
const uint32_t dst_elem = ictx->dst_elem;
|
||||
const dma_addr_t dst_data = dst->data;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
uint8_t * srcb_base = ictx->pe_vtcm_src + ith * ictx->pe_src_size_per_thread;
|
||||
uint8_t * dstb_base = ictx->pe_vtcm_dst + ith * ictx->pe_dst_size_per_thread;
|
||||
float * srcb2[2] = { (float *) srcb_base, (float *) (srcb_base + ictx->pe_src_row_bytes) };
|
||||
void * dstb2[2] = { dstb_base, dstb_base + ictx->pe_dst_row_bytes };
|
||||
float * scratch = (float *) (srcb_base + 2 * ictx->pe_src_row_bytes);
|
||||
const uint32_t row_end_max = ictx->pe_row_base + ictx->pe_nrows;
|
||||
const uint32_t row_start = ictx->pe_row_base + ictx->pe_rows_per_thread * ith;
|
||||
const uint32_t row_end = MIN(row_start + ictx->pe_rows_per_thread, row_end_max);
|
||||
if (row_start >= row_end) {
|
||||
return;
|
||||
}
|
||||
const uint32_t nrows_local = row_end - row_start;
|
||||
const uint32_t total_blocks = nrows_local * nbpr;
|
||||
if (total_blocks == 0) {
|
||||
return;
|
||||
}
|
||||
struct im2col_cursor cur = {
|
||||
.r = row_start,
|
||||
.in = ictx->thread_in[ith],
|
||||
.iod = ictx->thread_iod[ith],
|
||||
.ioh = ictx->thread_ioh[ith],
|
||||
.c0 = 0,
|
||||
};
|
||||
struct im2col_cursor stage = cur;
|
||||
for (uint32_t i = 0; i < total_blocks && i < 2; i++) {
|
||||
dma_queue_push(dma_q, dma_make_data(dst_data, dstb2[i]), 0, 0, 0, 0);
|
||||
im2col_3d_blocked_dma_stage(ictx, dma_q, srcb2[i], &stage);
|
||||
im2col_3d_step_block(&stage, ictx);
|
||||
}
|
||||
|
||||
im2col_3d_blocked_compute_fn_t compute = (im2col_3d_blocked_compute_fn_t) ictx->compute;
|
||||
for (uint32_t bi = 0; bi < total_blocks; bi++) {
|
||||
const uint32_t buf = bi & 1u;
|
||||
float * srcb = srcb2[buf];
|
||||
void * dstb = dstb2[buf];
|
||||
const uint32_t nb = MIN(owb, OW - cur.c0);
|
||||
dma_queue_pop(dma_q);
|
||||
for (uint32_t p = 0; p < kd_kh; p++) {
|
||||
dma_queue_pop(dma_q);
|
||||
}
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, cur.r);
|
||||
compute(ictx, dstb, srcb, scratch, nb, cur.c0, cur.iod, cur.ioh);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, cur.r);
|
||||
const dma_addr_t ddr = dst_data + (size_t) (cur.r * OW + cur.c0) * patch_stride * dst_elem;
|
||||
dma_queue_push(dma_q, dma_make_data(ddr, dstb),
|
||||
nb * patch_stride * dst_elem, nb * patch_stride * dst_elem,
|
||||
nb * patch_stride * dst_elem, 1);
|
||||
if (bi + 2 < total_blocks) {
|
||||
im2col_3d_blocked_dma_stage(ictx, dma_q, srcb, &stage);
|
||||
im2col_3d_step_block(&stage, ictx);
|
||||
}
|
||||
im2col_3d_step_block(&cur, ictx);
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
static void im2col_3d_patchembed_dma_stage(struct htp_im2col_context * ictx, dma_queue * dma_q,
|
||||
float * srcb, const struct im2col_cursor * c) {
|
||||
const struct htp_ops_context * octx = ictx->octx;
|
||||
const uint32_t IC = ictx->IC;
|
||||
const uint32_t ID = ictx->ID, IH = ictx->IH, IW = ictx->IW;
|
||||
const uint32_t KD = ictx->KD, KH = ictx->KH;
|
||||
const dma_addr_t src_data = octx->src[1]->data;
|
||||
const uint32_t in = c->in, iod = c->iod, ioh = c->ioh;
|
||||
const size_t src_channel_stride = (size_t) ID * IH * IW * sizeof(float);
|
||||
const size_t vtcm_channel_stride = (size_t) KD * KH * IW * sizeof(float);
|
||||
const size_t row_size = (size_t) KH * IW * sizeof(float);
|
||||
|
||||
for (uint32_t ikd = 0; ikd < KD; ikd++) {
|
||||
const uint32_t iid = iod * KD + ikd;
|
||||
float * vdst = srcb + (size_t) ikd * KH * IW;
|
||||
const dma_addr_t vsrc = src_data + (size_t) (((in * IC) * ID + iid) * IH + ioh * KH) * IW * sizeof(float);
|
||||
dma_queue_push(dma_q, dma_make_data(vdst, vsrc),
|
||||
vtcm_channel_stride, src_channel_stride, row_size, IC);
|
||||
}
|
||||
}
|
||||
static void im2col_3d_patchembed_compute_f16(
|
||||
const struct htp_im2col_context * ictx,
|
||||
void * restrict dstb_raw, const float * restrict srcb) {
|
||||
uint16_t * restrict dstb = (uint16_t *) dstb_raw;
|
||||
const uint32_t OW = ictx->OW;
|
||||
const uint32_t IC = ictx->IC;
|
||||
const uint32_t KD = ictx->KD;
|
||||
const uint32_t KH = ictx->KH;
|
||||
const uint32_t KW = ictx->KW;
|
||||
const uint32_t IW = ictx->IW;
|
||||
const uint32_t patch_stride = ictx->patch_stride;
|
||||
|
||||
for (uint32_t iow = 0; iow < OW; iow++) {
|
||||
uint16_t * dst_patch = dstb + (size_t) iow * patch_stride;
|
||||
for (uint32_t iic = 0; iic < IC; iic++) {
|
||||
for (uint32_t ikd = 0; ikd < KD; ikd++) {
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) {
|
||||
uint16_t * out_run = dst_patch + (size_t) (iic * KD * KH + ikd * KH + ikh) * KW;
|
||||
const float * src_run = srcb + (size_t) (iic * KD * KH + ikd * KH + ikh) * IW + (size_t) iow * KW;
|
||||
hvx_copy_f16_f32_uu((uint8_t *) out_run, (const uint8_t *) src_run, KW);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void im2col_3d_patchembed_compute_f32(
|
||||
const struct htp_im2col_context * ictx,
|
||||
void * restrict dstb_raw, const float * restrict srcb) {
|
||||
float * restrict dstb = (float *) dstb_raw;
|
||||
const uint32_t OW = ictx->OW;
|
||||
const uint32_t IC = ictx->IC;
|
||||
const uint32_t KD = ictx->KD;
|
||||
const uint32_t KH = ictx->KH;
|
||||
const uint32_t KW = ictx->KW;
|
||||
const uint32_t IW = ictx->IW;
|
||||
const uint32_t patch_stride = ictx->patch_stride;
|
||||
|
||||
for (uint32_t iow = 0; iow < OW; iow++) {
|
||||
float * dst_patch = dstb + (size_t) iow * patch_stride;
|
||||
for (uint32_t iic = 0; iic < IC; iic++) {
|
||||
for (uint32_t ikd = 0; ikd < KD; ikd++) {
|
||||
for (uint32_t ikh = 0; ikh < KH; ikh++) {
|
||||
float * out_run = dst_patch + (size_t) (iic * KD * KH + ikd * KH + ikh) * KW;
|
||||
const float * src_run = srcb + (size_t) (iic * KD * KH + ikd * KH + ikh) * IW + (size_t) iow * KW;
|
||||
hvx_copy_f32_uu((uint8_t *) out_run, (const uint8_t *) src_run, KW);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void im2col_3d_patchembed_dma_thread(unsigned int nth, unsigned int ith, void * data) {
|
||||
(void) nth;
|
||||
struct htp_im2col_context * ictx = (struct htp_im2col_context *) data;
|
||||
struct htp_ops_context * octx = ictx->octx;
|
||||
struct htp_thread_trace * restrict tr = &octx->ctx->trace[ith];
|
||||
const struct htp_tensor * restrict dst = octx->dst;
|
||||
const uint32_t KD = ictx->KD;
|
||||
const uint32_t OW = ictx->OW;
|
||||
const uint32_t patch_stride = ictx->patch_stride;
|
||||
const uint32_t dst_elem = ictx->dst_elem;
|
||||
const dma_addr_t dst_data = dst->data;
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
const uint32_t row_end_max = ictx->pe_row_base + ictx->pe_nrows;
|
||||
const uint32_t row_start = ictx->pe_row_base + ictx->pe_rows_per_thread * ith;
|
||||
const uint32_t row_end = MIN(row_start + ictx->pe_rows_per_thread, row_end_max);
|
||||
if (row_start >= row_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t nrows_local = row_end - row_start;
|
||||
uint8_t * src_base = ictx->pe_vtcm_src + ith * ictx->pe_src_size_per_thread;
|
||||
uint8_t * dstb_base = ictx->pe_vtcm_dst + ith * ictx->pe_dst_size_per_thread;
|
||||
float * srcb2[2] = { (float *) src_base, (float *) (src_base + ictx->pe_src_row_bytes) };
|
||||
void * dstb2[2] = { dstb_base, dstb_base + ictx->pe_dst_row_bytes };
|
||||
struct im2col_cursor cur = {
|
||||
.r = row_start,
|
||||
.in = ictx->thread_in[ith],
|
||||
.iod = ictx->thread_iod[ith],
|
||||
.ioh = ictx->thread_ioh[ith],
|
||||
.c0 = 0,
|
||||
};
|
||||
struct im2col_cursor stage = cur;
|
||||
for (uint32_t i = 0; i < nrows_local && i < 2; i++) {
|
||||
dma_queue_push(dma_q, dma_make_data(dst_data, dstb2[i]), 0, 0, 0, 0);
|
||||
im2col_3d_patchembed_dma_stage(ictx, dma_q, srcb2[i], &stage);
|
||||
im2col_3d_step_row(&stage, ictx);
|
||||
}
|
||||
|
||||
im2col_3d_patchembed_compute_fn_t compute = (im2col_3d_patchembed_compute_fn_t) ictx->compute;
|
||||
for (uint32_t idx = 0; idx < nrows_local; idx++) {
|
||||
const uint32_t buf = idx & 1u;
|
||||
float * srcb = srcb2[buf];
|
||||
void * dstb = dstb2[buf];
|
||||
dma_queue_pop(dma_q);
|
||||
for (uint32_t p = 0; p < KD; p++) {
|
||||
dma_queue_pop(dma_q);
|
||||
}
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, cur.r);
|
||||
compute(ictx, dstb, srcb);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, cur.r);
|
||||
const dma_addr_t ddr_row = dst_data + (size_t) cur.r * OW * patch_stride * dst_elem;
|
||||
dma_queue_push(dma_q, dma_make_data(ddr_row, dstb),
|
||||
OW * patch_stride * dst_elem, OW * patch_stride * dst_elem,
|
||||
OW * patch_stride * dst_elem, 1);
|
||||
if (idx + 2 < nrows_local) {
|
||||
im2col_3d_patchembed_dma_stage(ictx, dma_q, srcb, &stage);
|
||||
im2col_3d_step_row(&stage, ictx);
|
||||
}
|
||||
im2col_3d_step_row(&cur, ictx);
|
||||
}
|
||||
dma_queue_flush(dma_q);
|
||||
}
|
||||
|
||||
int op_im2col(struct htp_ops_context * octx) {
|
||||
const struct htp_im2col_kernel_params * kparams = (const struct htp_im2col_kernel_params *) octx->kernel_params;
|
||||
if (kparams->kernel_type == HTP_IM2COL_KERNEL_UNSUPPORTED) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
uint32_t row_base = 0;
|
||||
uint32_t nrows = kparams->total_rows;
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range =
|
||||
htp_tensor_mdev_partition(kparams->total_rows, htp_tensor_mdev_data_aligned(dst) ? kparams->rows_per_chunk : 0,
|
||||
octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
row_base = range.start;
|
||||
nrows = range.count;
|
||||
}
|
||||
|
||||
if (nrows == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, MIN((uint32_t) kparams->n_threads, nrows))) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
struct htp_im2col_vtcm_layout layout;
|
||||
htp_im2col_vtcm_layout_build(&layout, kparams->src_row_bytes, kparams->dst_row_bytes, kparams->n_threads);
|
||||
|
||||
struct htp_im2col_context ictx = { 0 };
|
||||
uint8_t * const base = octx->ctx->vtcm_base;
|
||||
|
||||
const uint32_t pth = octx->n_threads;
|
||||
ictx.octx = octx;
|
||||
ictx.pe_row_base = row_base;
|
||||
ictx.pe_nrows = nrows;
|
||||
ictx.pe_rows_per_thread = fastdiv(nrows + pth - 1, &octx->n_threads_div);
|
||||
ictx.pe_src_row_bytes = kparams->src_row_bytes;
|
||||
ictx.pe_dst_row_bytes = kparams->dst_row_bytes;
|
||||
ictx.pe_vtcm_src = VTCM_LAYOUT_PTR(uint8_t, base, layout.off_src);
|
||||
ictx.pe_vtcm_dst = VTCM_LAYOUT_PTR(uint8_t, base, layout.off_dst);
|
||||
ictx.pe_src_size_per_thread = layout.src_bytes_per_thread;
|
||||
ictx.pe_dst_size_per_thread = layout.dst_bytes_per_thread;
|
||||
ictx.pe_owb = kparams->owb;
|
||||
ictx.pe_wb = kparams->wb;
|
||||
ictx.pe_nbpr = kparams->nbpr;
|
||||
ictx.N = kparams->N;
|
||||
ictx.IC = kparams->IC;
|
||||
ictx.ID = kparams->ID;
|
||||
ictx.IH = kparams->IH;
|
||||
ictx.IW = kparams->IW;
|
||||
ictx.KD = kparams->KD;
|
||||
ictx.KH = kparams->KH;
|
||||
ictx.KW = kparams->KW;
|
||||
ictx.OD = kparams->OD;
|
||||
ictx.OH = kparams->OH;
|
||||
ictx.OW = kparams->OW;
|
||||
ictx.patch_stride = kparams->patch_stride;
|
||||
|
||||
for (uint32_t ith = 0; ith < pth; ith++) {
|
||||
const uint32_t r = row_base + ictx.pe_rows_per_thread * ith;
|
||||
const uint32_t in = fastdiv(r, &kparams->div_OH);
|
||||
ictx.thread_ioh[ith] = r - in * kparams->OH;
|
||||
ictx.thread_in[ith] = in;
|
||||
}
|
||||
|
||||
ictx.dst_elem = dst->type == HTP_TYPE_F16 ? sizeof(__fp16) : sizeof(float);
|
||||
|
||||
if (kparams->kernel_type == HTP_IM2COL_KERNEL_PATCHEMBED_DMA) {
|
||||
ictx.compute = dst->type == HTP_TYPE_F16 ? (void *) im2col_patchembed_compute_f16
|
||||
: (void *) im2col_patchembed_compute_f32;
|
||||
work_queue_run(octx->ctx->work_queue, im2col_patchembed_dma_thread, &ictx, pth);
|
||||
return HTP_STATUS_OK;
|
||||
} else if (kparams->kernel_type == HTP_IM2COL_KERNEL_BLOCKED_DMA) {
|
||||
ictx.compute = dst->type == HTP_TYPE_F16 ? (void *) im2col_blocked_compute_f16
|
||||
: (void *) im2col_blocked_compute_f32;
|
||||
work_queue_run(octx->ctx->work_queue, im2col_blocked_dma_thread, &ictx, pth);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
int op_im2col_3d(struct htp_ops_context * octx) {
|
||||
const struct htp_im2col_kernel_params * kparams =
|
||||
(const struct htp_im2col_kernel_params *) octx->kernel_params;
|
||||
if (kparams->kernel_type == HTP_IM2COL_KERNEL_UNSUPPORTED) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
uint32_t row_base = 0;
|
||||
uint32_t nrows = kparams->total_rows;
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range =
|
||||
htp_tensor_mdev_partition(kparams->total_rows, htp_tensor_mdev_data_aligned(dst) ? kparams->rows_per_chunk : 0,
|
||||
octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
row_base = range.start;
|
||||
nrows = range.count;
|
||||
}
|
||||
|
||||
if (nrows == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, MIN((uint32_t) kparams->n_threads, nrows))) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
struct htp_im2col_vtcm_layout layout;
|
||||
htp_im2col_vtcm_layout_build(&layout, kparams->src_row_bytes, kparams->dst_row_bytes, kparams->n_threads);
|
||||
|
||||
struct htp_im2col_context ictx = { 0 };
|
||||
uint8_t * const base = octx->ctx->vtcm_base;
|
||||
|
||||
const uint32_t pth = octx->n_threads;
|
||||
ictx.octx = octx;
|
||||
ictx.pe_row_base = row_base;
|
||||
ictx.pe_nrows = nrows;
|
||||
ictx.pe_rows_per_thread = fastdiv(nrows + pth - 1, &octx->n_threads_div);
|
||||
ictx.pe_src_row_bytes = kparams->src_row_bytes;
|
||||
ictx.pe_dst_row_bytes = kparams->dst_row_bytes;
|
||||
ictx.pe_vtcm_src = VTCM_LAYOUT_PTR(uint8_t, base, layout.off_src);
|
||||
ictx.pe_vtcm_dst = VTCM_LAYOUT_PTR(uint8_t, base, layout.off_dst);
|
||||
ictx.pe_src_size_per_thread = layout.src_bytes_per_thread;
|
||||
ictx.pe_dst_size_per_thread = layout.dst_bytes_per_thread;
|
||||
ictx.pe_owb = kparams->owb;
|
||||
ictx.pe_wb = kparams->wb;
|
||||
ictx.pe_nbpr = kparams->nbpr;
|
||||
ictx.N = kparams->N;
|
||||
ictx.IC = kparams->IC;
|
||||
ictx.ID = kparams->ID;
|
||||
ictx.IH = kparams->IH;
|
||||
ictx.IW = kparams->IW;
|
||||
ictx.KD = kparams->KD;
|
||||
ictx.KH = kparams->KH;
|
||||
ictx.KW = kparams->KW;
|
||||
ictx.OD = kparams->OD;
|
||||
ictx.OH = kparams->OH;
|
||||
ictx.OW = kparams->OW;
|
||||
ictx.patch_stride = kparams->patch_stride;
|
||||
|
||||
for (uint32_t ith = 0; ith < pth; ith++) {
|
||||
const uint32_t rem = row_base + ictx.pe_rows_per_thread * ith;
|
||||
const uint32_t q_oh = fastdiv(rem, &kparams->div_OH);
|
||||
ictx.thread_ioh[ith] = rem - q_oh * kparams->OH;
|
||||
const uint32_t in = fastdiv(q_oh, &kparams->div_OD);
|
||||
ictx.thread_iod[ith] = q_oh - in * kparams->OD;
|
||||
ictx.thread_in[ith] = in;
|
||||
}
|
||||
|
||||
ictx.dst_elem = dst->type == HTP_TYPE_F16 ? sizeof(__fp16) : sizeof(float);
|
||||
|
||||
if (kparams->kernel_type == HTP_IM2COL_KERNEL_PATCHEMBED_DMA) {
|
||||
ictx.compute = dst->type == HTP_TYPE_F16 ? (void *) im2col_3d_patchembed_compute_f16
|
||||
: (void *) im2col_3d_patchembed_compute_f32;
|
||||
work_queue_run(octx->ctx->work_queue, im2col_3d_patchembed_dma_thread, &ictx, pth);
|
||||
return HTP_STATUS_OK;
|
||||
} else if (kparams->kernel_type == HTP_IM2COL_KERNEL_BLOCKED_DMA) {
|
||||
ictx.compute = dst->type == HTP_TYPE_F16 ? (void *) im2col_3d_blocked_compute_f16
|
||||
: (void *) im2col_3d_blocked_compute_f32;
|
||||
work_queue_run(octx->ctx->work_queue, im2col_3d_blocked_dma_thread, &ictx, pth);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef HTP_IM2COL_OPS_H
|
||||
#define HTP_IM2COL_OPS_H
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include <stdint.h>
|
||||
|
||||
enum htp_im2col_kernel_type {
|
||||
HTP_IM2COL_KERNEL_UNSUPPORTED = 0,
|
||||
HTP_IM2COL_KERNEL_PATCHEMBED_DMA = 1,
|
||||
HTP_IM2COL_KERNEL_BLOCKED_DMA = 2,
|
||||
};
|
||||
|
||||
struct htp_im2col_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src;
|
||||
size_t off_dst;
|
||||
uint32_t src_bytes_per_thread;
|
||||
uint32_t dst_bytes_per_thread;
|
||||
};
|
||||
|
||||
static inline void htp_im2col_vtcm_layout_build(
|
||||
struct htp_im2col_vtcm_layout * L,
|
||||
uint32_t src_row_bytes,
|
||||
uint32_t dst_row_bytes,
|
||||
uint32_t n_threads) {
|
||||
|
||||
L->src_bytes_per_thread = 2 * src_row_bytes + 256;
|
||||
L->dst_bytes_per_thread = 2 * dst_row_bytes;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src, (size_t) L->src_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, (size_t) L->dst_bytes_per_thread * n_threads);
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
struct htp_im2col_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint8_t n_threads;
|
||||
|
||||
uint32_t vtcm_size;
|
||||
uint32_t src_row_bytes;
|
||||
uint32_t dst_row_bytes;
|
||||
|
||||
uint32_t owb;
|
||||
uint32_t wb;
|
||||
uint32_t nbpr;
|
||||
|
||||
uint32_t N;
|
||||
uint32_t IC;
|
||||
uint32_t ID, IH, IW;
|
||||
uint32_t KD, KH, KW;
|
||||
uint32_t OD, OH, OW;
|
||||
uint32_t patch_stride;
|
||||
uint32_t total_rows;
|
||||
uint32_t rows_per_chunk;
|
||||
|
||||
struct fastdiv_values div_OH;
|
||||
struct fastdiv_values div_OD;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_im2col_kernel_params) <= 128, "htp_im2col_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_im2col_kernel_params) <= 128, "htp_im2col_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_IM2COL_OPS_H
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,774 @@
|
||||
#ifndef HTP_MATMUL_OPS_H
|
||||
#define HTP_MATMUL_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "hex-common.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// --- HMX Tile Constraints ---
|
||||
#define HTP_MM_HMX_TILE_N_COLS 32
|
||||
#define HTP_MM_HMX_TILE_N_ROWS 32
|
||||
#define HTP_MM_HMX_TILE_SIZE (32 * 32 * sizeof(__fp16)) // 2048 bytes
|
||||
#define HTP_MM_HMX_TILE_N_ELMS 1024
|
||||
#define HTP_MM_HMX_MIN_NROWS 4
|
||||
|
||||
#define HTP_MM_WEIGHT_TILE_K_Q2_0 64
|
||||
#define HTP_MM_WEIGHT_TILE_SIZE_Q2_0 HTP_TENSOR_TILE_SIZE_Q2_0
|
||||
#define HTP_MM_WEIGHT_ALIGNED_TILE_SIZE_Q2_0 640
|
||||
|
||||
// --- Activation Tiled Block Sizes (including padding) ---
|
||||
#define HTP_MM_ACT_BLOCK_K 128
|
||||
#define HTP_MM_ACT_TILE_SIZE_Q8_0 1152
|
||||
#define HTP_MM_ACT_TILE_SIZE_Q8_1 1280
|
||||
#define HTP_MM_ACT_TILE_SIZE_F16 2048
|
||||
|
||||
#define HTP_MM_MAX_PREFETCH 16
|
||||
|
||||
// --- Solver Cost Model Penalty Weights (HMX-specific) ---
|
||||
#define HTP_MM_HMX_COST_W_DEQUANT 3 // cost penalty for quantized weight loading/dequantization
|
||||
#define HTP_MM_HMX_COST_A_CONVERT 2 // cost penalty for activation loading/conversion
|
||||
|
||||
// --- DMA Activation Transfer Configuration ---
|
||||
#define HTP_MM_DMA_ACT_ROWS_PER_STEP 2
|
||||
#define HTP_MM_DMA_ACT_MULTIPLIER (2 * HTP_MM_DMA_ACT_ROWS_PER_STEP)
|
||||
|
||||
enum htp_mm_kernel_type {
|
||||
HTP_MM_KERNEL_UNSUPPORTED = 0,
|
||||
|
||||
// HMX paths
|
||||
HTP_MM_KERNEL_HMX_2D,
|
||||
HTP_MM_KERNEL_HMX_BATCHED,
|
||||
|
||||
// HVX floating-point paths
|
||||
HTP_MM_KERNEL_HVX_F16_F16_VTCM,
|
||||
HTP_MM_KERNEL_HVX_F32_F32_VTCM,
|
||||
HTP_MM_KERNEL_HVX_BF16_F32_VTCM,
|
||||
|
||||
// HVX quantized paths
|
||||
HTP_MM_KERNEL_HVX_QUANT_ROW, // standard row-wise parallel quantization
|
||||
HTP_MM_KERNEL_HVX_QUANT_BLOCK, // parallel block-wise quantization
|
||||
};
|
||||
|
||||
// Op-specific struct for precomputed matmul params
|
||||
struct htp_mm_kernel_params {
|
||||
uint8_t kernel_type; // enum htp_mm_kernel_type
|
||||
uint8_t pipeline; // 1 = pipelined execution, 0 = standard
|
||||
uint8_t unused; // reserved
|
||||
uint8_t n_hmx; // 1 = use HMX, 0 = use HVX
|
||||
|
||||
uint8_t n_threads; // Number of threads to spawn
|
||||
uint8_t n_act_threads; // Number of threads for activation preparation
|
||||
uint8_t n_prefetch; // Prefetch lookahead buffers/rows in VTCM
|
||||
uint8_t n_weights; // Number of weights for fused NX
|
||||
|
||||
int32_t m_chunk; // Row chunk size (M chunk)
|
||||
int32_t n_chunk; // Col chunk size (N chunk)
|
||||
int32_t tile_size; // Weight tile size
|
||||
int32_t aligned_tile_size; // Aligned weight tile size (padded to 128)
|
||||
int32_t act_row_size; // Row size for activation scratchpad
|
||||
int32_t vtcm_size; // Total required scratchpad size in VTCM
|
||||
int32_t vtcm_bias_size; // bias scratchpad size in VTCM (fused only)
|
||||
|
||||
// Precomputed division values
|
||||
struct fastdiv_values div_ne12_ne1;
|
||||
struct fastdiv_values div_ne1;
|
||||
struct fastdiv_values div_r2;
|
||||
struct fastdiv_values div_r3;
|
||||
struct fastdiv_values div_ne12;
|
||||
struct fastdiv_values div_n_act_threads;
|
||||
struct fastdiv_values div_ne00_padded;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_mm_kernel_params) <= 128, "htp_matmul_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_mm_kernel_params) <= 128, "htp_matmul_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
struct mmid_row_mapping {
|
||||
uint32_t i1;
|
||||
uint32_t i2;
|
||||
};
|
||||
|
||||
// Search for optimal (mc, nc) chunk sizes within VTCM budget.
|
||||
static inline int htp_mm_hmx_compute_chunks(size_t vtcm_total,
|
||||
size_t overhead,
|
||||
size_t per_n_cost,
|
||||
size_t per_m_cost,
|
||||
size_t per_mn_cost,
|
||||
size_t m,
|
||||
size_t n,
|
||||
size_t m_block_cost,
|
||||
size_t n_block_cost,
|
||||
size_t * m_chunk_out,
|
||||
size_t * n_chunk_out,
|
||||
size_t * total_out) {
|
||||
if (m == 0 || n == 0) return -1;
|
||||
if (vtcm_total <= overhead) return -1;
|
||||
if (per_n_cost == 0 || per_m_cost == 0 || per_mn_cost == 0) return -1;
|
||||
|
||||
const size_t usable = vtcm_total - overhead;
|
||||
|
||||
size_t best_cost = SIZE_MAX;
|
||||
size_t best_tail_waste = SIZE_MAX;
|
||||
size_t best_mn = 0;
|
||||
size_t best_m = 0, best_n = 0;
|
||||
|
||||
const size_t max_nc_budget = (usable / per_n_cost);
|
||||
const size_t n_max = hex_align_down(hex_smin((size_t)n, max_nc_budget), HTP_MM_HMX_TILE_N_COLS);
|
||||
for (size_t nc = n_max; nc >= HTP_MM_HMX_TILE_N_COLS; nc -= HTP_MM_HMX_TILE_N_COLS) {
|
||||
size_t n_fixed = 0, ncmn = 0, mc_denom = 0;
|
||||
if (hex_mul_overflow(nc, per_n_cost, &n_fixed)) continue;
|
||||
if (n_fixed >= usable) goto next_nc;
|
||||
|
||||
if (hex_mul_overflow(nc, per_mn_cost, &ncmn)) goto next_nc;
|
||||
if (hex_add_overflow(per_m_cost, ncmn, &mc_denom) || mc_denom == 0) goto next_nc;
|
||||
|
||||
{
|
||||
size_t remain = usable - n_fixed;
|
||||
size_t mc = remain / mc_denom;
|
||||
mc = hex_align_down(mc, HTP_MM_HMX_TILE_N_ROWS);
|
||||
mc = hex_smin(mc, m);
|
||||
|
||||
if (mc == 0) {
|
||||
goto next_nc;
|
||||
}
|
||||
|
||||
size_t mblocks = ((size_t) m + mc - 1) / mc;
|
||||
size_t nblocks = ((size_t) n + nc - 1) / nc;
|
||||
size_t cost = mblocks * m_block_cost + nblocks * n_block_cost;
|
||||
size_t rem = n % nc;
|
||||
size_t tail_waste = (rem == 0) ? 0 : (nc - rem);
|
||||
size_t mn = mc * nc;
|
||||
if (cost < best_cost ||
|
||||
(cost == best_cost && tail_waste < best_tail_waste) ||
|
||||
(cost == best_cost && tail_waste == best_tail_waste && mn > best_mn)) {
|
||||
best_cost = cost;
|
||||
best_tail_waste = tail_waste;
|
||||
best_mn = mn;
|
||||
best_m = mc;
|
||||
best_n = nc;
|
||||
}
|
||||
}
|
||||
|
||||
next_nc:
|
||||
if (nc == HTP_MM_HMX_TILE_N_COLS) break; // avoid size_t underflow
|
||||
}
|
||||
|
||||
if (best_m == 0 || best_n == 0) return -1;
|
||||
|
||||
// Compute exact total (with overflow checks)
|
||||
size_t t0 = 0, t1 = 0, t2 = 0, mn = 0, total = 0;
|
||||
if (hex_mul_overflow(best_n, per_n_cost, &t0)) return -1;
|
||||
if (hex_mul_overflow(best_m, per_m_cost, &t1)) return -1;
|
||||
if (hex_mul_overflow(best_m, best_n, &mn)) return -1;
|
||||
if (hex_mul_overflow(mn, per_mn_cost, &t2)) return -1;
|
||||
if (hex_add_overflow(t0, t1, &total)) return -1;
|
||||
if (hex_add_overflow(total, t2, &total)) return -1;
|
||||
if (hex_add_overflow(total, overhead, &total)) return -1;
|
||||
|
||||
*m_chunk_out = best_m;
|
||||
*n_chunk_out = best_n;
|
||||
*total_out = total;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// weight types whose tiles carry a per-block offset (x = d * q + m): the activations need block sums (q8_1)
|
||||
// (Q2_K: per-16 k sums, q8_1_s16)
|
||||
static inline bool htp_mm_weight_has_offset(int weight_type) {
|
||||
return weight_type == HTP_TYPE_Q4_1 || weight_type == HTP_TYPE_Q4_K || weight_type == HTP_TYPE_Q5_K ||
|
||||
weight_type == HTP_TYPE_Q2_K || weight_type == HTP_TYPE_Q2_0;
|
||||
}
|
||||
|
||||
// --- Activation/Row Size Helpers ---
|
||||
static inline size_t htp_mm_q8_0_tiled_row_size(uint32_t ne) {
|
||||
const uint32_t ne_padded = hex_round_up(ne, HTP_MM_ACT_BLOCK_K);
|
||||
const uint32_t nb_32 = ne_padded / 32;
|
||||
return nb_32 * HTP_MM_ACT_TILE_SIZE_Q8_0;
|
||||
}
|
||||
|
||||
static inline size_t htp_mm_q8_1_tiled_row_size(uint32_t ne) {
|
||||
const uint32_t ne_padded = hex_round_up(ne, HTP_MM_ACT_BLOCK_K);
|
||||
const uint32_t nb_32 = ne_padded / 32;
|
||||
return nb_32 * HTP_MM_ACT_TILE_SIZE_Q8_1;
|
||||
}
|
||||
|
||||
static inline size_t htp_mm_f16_tiled_row_size(uint32_t ne) {
|
||||
const uint32_t ne_padded = hex_round_up(ne, HTP_MM_ACT_BLOCK_K);
|
||||
const uint32_t nb_32 = ne_padded / 32;
|
||||
return nb_32 * HTP_MM_ACT_TILE_SIZE_F16;
|
||||
}
|
||||
|
||||
static inline size_t htp_mm_tiled_act_row_size(int weight_type, uint32_t ne) {
|
||||
if (weight_type == HTP_TYPE_F16) {
|
||||
return htp_mm_f16_tiled_row_size(ne);
|
||||
}
|
||||
return htp_mm_weight_has_offset(weight_type) ? htp_mm_q8_1_tiled_row_size(ne) : htp_mm_q8_0_tiled_row_size(ne);
|
||||
}
|
||||
|
||||
static inline size_t htp_mm_act_raw_row_size(uint32_t ne) {
|
||||
return hex_round_up(ne, HTP_MM_ACT_BLOCK_K) * sizeof(float);
|
||||
}
|
||||
|
||||
static inline size_t htp_mm_round_up(size_t n, size_t m) {
|
||||
return ((n + m - 1) / m) * m;
|
||||
}
|
||||
|
||||
static inline bool htp_mm_hmx_pipeline(uint32_t m) {
|
||||
return m > 32;
|
||||
}
|
||||
|
||||
static inline void htp_mm_hmx_get_2d_chunk_costs(
|
||||
int wtype, uint32_t k, bool pipeline, uint32_t aligned_tile_size,
|
||||
size_t * size_per_n_out, size_t * size_per_m_out, size_t * size_per_mn_out
|
||||
) {
|
||||
const bool is_quant = (wtype != HTP_TYPE_F16 && wtype != HTP_TYPE_F32 && wtype != HTP_TYPE_BF16);
|
||||
const size_t row_stride = htp_tensor_get_row_size(wtype, k);
|
||||
const size_t vec_dot_size = k * sizeof(uint16_t);
|
||||
const uint32_t n_weight_tiles = k / htp_tensor_get_tile_k(wtype);
|
||||
const size_t qweight_row_stride = is_quant ? (size_t)(n_weight_tiles * aligned_tile_size) / 32 : 0;
|
||||
|
||||
*size_per_n_out = (pipeline ? 2 : 1) * (is_quant ? qweight_row_stride : row_stride) +
|
||||
(pipeline ? 2 * vec_dot_size : vec_dot_size);
|
||||
*size_per_m_out = vec_dot_size;
|
||||
*size_per_mn_out = (pipeline ? 2 : 1) * sizeof(uint16_t);
|
||||
}
|
||||
|
||||
static inline void htp_mm_hmx_get_batched_chunk_costs(
|
||||
uint32_t k, uint32_t group_size,
|
||||
size_t * size_per_n_out, size_t * size_per_m_out, size_t * size_per_mn_out
|
||||
) {
|
||||
const size_t vec_dot_size = k * sizeof(uint16_t);
|
||||
*size_per_n_out = 3 * vec_dot_size;
|
||||
*size_per_m_out = group_size * vec_dot_size;
|
||||
*size_per_mn_out = sizeof(uint16_t);
|
||||
}
|
||||
|
||||
static inline size_t htp_mm_hmx_get_2d_overhead(bool pipeline, bool is_matmul_id) {
|
||||
size_t num_regions = pipeline ? 7 : (is_matmul_id ? 4 : 5);
|
||||
return num_regions * HTP_MM_HMX_TILE_SIZE + 256;
|
||||
}
|
||||
|
||||
static inline size_t htp_mm_hmx_get_batched_overhead(void) {
|
||||
return 5 * HTP_MM_HMX_TILE_SIZE + 256;
|
||||
}
|
||||
|
||||
struct htp_mm_hmx_vtcm_layout {
|
||||
// Byte offsets from vtcm_base for each region
|
||||
size_t off_weight[2]; // [1] is only used when pipelined
|
||||
size_t off_act;
|
||||
size_t off_act_f32; // fp32 activation conversion scratch
|
||||
size_t off_dst[2]; // [1] is only used when pipelined
|
||||
size_t off_scratch[2]; // dequantization scratch pads
|
||||
size_t off_scales; // HMX scales (256 bytes)
|
||||
size_t off_bias; // bias in VTCM
|
||||
|
||||
// Cached sizes of regions for HMX kernel use
|
||||
size_t weight_area_bytes;
|
||||
size_t act_area_bytes;
|
||||
size_t act_f32_bytes;
|
||||
size_t output_area_bytes;
|
||||
size_t scratch_bytes[2];
|
||||
size_t act_head_stride;
|
||||
size_t bias_bytes;
|
||||
|
||||
size_t total_bytes;
|
||||
};
|
||||
|
||||
struct htp_mm_hvx_vtcm_layout {
|
||||
// Byte offsets from vtcm_base for each region
|
||||
size_t off_act; // vtcm_act (activation)
|
||||
size_t off_src0; // vtcm_src0 (weight/Wk)
|
||||
size_t off_bias; // vtcm_bias (bias / fused add only)
|
||||
size_t off_dst; // vtcm_dst (output scratch)
|
||||
size_t off_act_raw; // vtcm_act_raw (raw activation DMA staging)
|
||||
|
||||
// Cached sizes
|
||||
size_t src0_bytes;
|
||||
size_t act_bytes;
|
||||
size_t bias_bytes;
|
||||
size_t dst_bytes;
|
||||
size_t act_raw_bytes;
|
||||
|
||||
size_t total_bytes;
|
||||
};
|
||||
|
||||
static inline void htp_mm_hmx_vtcm_layout_build(
|
||||
struct htp_mm_hmx_vtcm_layout * L,
|
||||
int kernel_type,
|
||||
int wtype,
|
||||
uint32_t k,
|
||||
size_t mc,
|
||||
size_t nc,
|
||||
uint32_t group_size,
|
||||
bool pipeline,
|
||||
uint32_t act_threads,
|
||||
uint32_t aligned_tile_size,
|
||||
size_t bias_size
|
||||
) {
|
||||
size_t off = 0;
|
||||
|
||||
if (kernel_type == HTP_MM_KERNEL_HMX_BATCHED) {
|
||||
const size_t vec_dot_size = k * sizeof(uint16_t);
|
||||
const size_t act_head_stride = mc * k;
|
||||
const size_t weight_area_size = hex_align_up(nc * vec_dot_size, HTP_MM_HMX_TILE_SIZE);
|
||||
const size_t activation_area_size = hex_align_up(group_size * act_head_stride * sizeof(uint16_t), HTP_MM_HMX_TILE_SIZE);
|
||||
const size_t output_area_size = hex_align_up(group_size * mc * nc * sizeof(uint16_t), HTP_MM_HMX_TILE_SIZE);
|
||||
const size_t scratch_area_size = hex_align_up(nc * vec_dot_size, HTP_MM_HMX_TILE_SIZE);
|
||||
const size_t min_f32_size = hex_align_up(act_threads * HTP_MM_DMA_ACT_MULTIPLIER * k * sizeof(float), 128);
|
||||
|
||||
// Group A: Permanent activation tiles and scales
|
||||
size_t off_group_a = 0;
|
||||
VTCM_LAYOUT_ALLOC(off_group_a, off_act, activation_area_size);
|
||||
VTCM_LAYOUT_ALLOC(off_group_a, off_scales, HTP_MM_HMX_TILE_SIZE); // Padded to 2K for alignment and future persistent data
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_a, off_bias, hex_align_up(bias_size, HTP_MM_HMX_TILE_SIZE), bias_size > 0);
|
||||
|
||||
// Group B: Compute-only buffers (starts at off_group_a)
|
||||
size_t off_group_b = off_group_a;
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_weight[0], weight_area_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_weight[1], weight_area_size, false);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_dst[0], output_area_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_dst[1], output_area_size, false);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_scratch[0], scratch_area_size);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_scratch[1], scratch_area_size);
|
||||
|
||||
const size_t group_b_size = off_group_b - off_group_a;
|
||||
|
||||
// Group C: Activation prep temporary buffer (overlaps Group B, starting at off_group_a)
|
||||
const size_t max_f32_size = act_threads * 64 * k * sizeof(float);
|
||||
const size_t act_f32_size = hex_align_up(hex_smin(max_f32_size, hex_smax(min_f32_size, group_b_size)), 128);
|
||||
size_t off_group_c = off_group_a;
|
||||
VTCM_LAYOUT_ALLOC(off_group_c, off_act_f32, act_f32_size);
|
||||
|
||||
const size_t group_c_size = off_group_c - off_group_a;
|
||||
|
||||
L->weight_area_bytes = weight_area_size;
|
||||
L->act_area_bytes = activation_area_size;
|
||||
L->act_f32_bytes = act_f32_size;
|
||||
L->output_area_bytes = output_area_size;
|
||||
L->scratch_bytes[0] = scratch_area_size;
|
||||
L->scratch_bytes[1] = scratch_area_size;
|
||||
L->act_head_stride = act_head_stride;
|
||||
L->bias_bytes = bias_size;
|
||||
|
||||
off = off_group_a + hex_smax(group_b_size, group_c_size);
|
||||
} else {
|
||||
// HTP_MM_KERNEL_HMX_2D
|
||||
const bool is_quant = (wtype != HTP_TYPE_F16 && wtype != HTP_TYPE_F32 && wtype != HTP_TYPE_BF16);
|
||||
const size_t row_stride = htp_tensor_get_row_size(wtype, k);
|
||||
const size_t vec_dot_size = k * sizeof(uint16_t);
|
||||
const uint32_t n_weight_tiles = k / htp_tensor_get_tile_k(wtype);
|
||||
|
||||
const size_t min_f32_size = hex_align_up(act_threads * HTP_MM_DMA_ACT_MULTIPLIER * k * sizeof(float), 128);
|
||||
const size_t weight_area_size = is_quant
|
||||
? hex_align_up((nc / 32) * n_weight_tiles * aligned_tile_size, HTP_MM_HMX_TILE_SIZE)
|
||||
: hex_align_up(nc * row_stride, HTP_MM_HMX_TILE_SIZE);
|
||||
const size_t act_area_size = hex_align_up(mc * vec_dot_size, HTP_MM_HMX_TILE_SIZE);
|
||||
const size_t output_area_size = hex_align_up(mc * nc * sizeof(__fp16), HTP_MM_HMX_TILE_SIZE);
|
||||
|
||||
const size_t scratch0_size = hex_align_up(nc * vec_dot_size, HTP_MM_HMX_TILE_SIZE);
|
||||
const size_t scratch1_size = pipeline ? scratch0_size : 0;
|
||||
|
||||
// Group A: Scales and activation tiles (must not overlap with Group B or C)
|
||||
size_t off_group_a = 0;
|
||||
VTCM_LAYOUT_ALLOC(off_group_a, off_scales, HTP_MM_HMX_TILE_SIZE); // Padded to 2K for alignment and future persistent data
|
||||
VTCM_LAYOUT_ALLOC(off_group_a, off_act, act_area_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_a, off_bias, hex_align_up(bias_size, HTP_MM_HMX_TILE_SIZE), bias_size > 0);
|
||||
|
||||
// Group B: Compute-only buffers (starts at off_group_a)
|
||||
size_t off_group_b = off_group_a;
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_weight[0], weight_area_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_weight[1], weight_area_size, pipeline);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_dst[0], output_area_size);
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_scratch[0], scratch0_size);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_scratch[1], scratch0_size, pipeline);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_dst[1], output_area_size, pipeline);
|
||||
|
||||
const size_t group_b_size = off_group_b - off_group_a;
|
||||
|
||||
// Group C: Activation prep temporary buffer (overlaps Group B, starting at off_group_a)
|
||||
const size_t max_f32_size = act_threads * 64 * k * sizeof(float);
|
||||
const size_t act_f32_size = hex_align_up(hex_smin(max_f32_size, hex_smax(min_f32_size, group_b_size)), 128);
|
||||
size_t off_group_c = off_group_a;
|
||||
VTCM_LAYOUT_ALLOC(off_group_c, off_act_f32, act_f32_size);
|
||||
|
||||
const size_t group_c_size = off_group_c - off_group_a;
|
||||
|
||||
L->weight_area_bytes = weight_area_size;
|
||||
L->act_area_bytes = act_area_size;
|
||||
L->act_f32_bytes = act_f32_size;
|
||||
L->output_area_bytes = output_area_size;
|
||||
L->scratch_bytes[0] = scratch0_size;
|
||||
L->scratch_bytes[1] = scratch1_size;
|
||||
L->act_head_stride = 0;
|
||||
L->bias_bytes = bias_size;
|
||||
|
||||
off = off_group_a + hex_smax(group_b_size, group_c_size);
|
||||
}
|
||||
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
static inline void htp_mm_hvx_vtcm_layout_build(
|
||||
struct htp_mm_hvx_vtcm_layout * L,
|
||||
int kernel_type,
|
||||
int wtype,
|
||||
uint32_t ne10, // k
|
||||
uint32_t act_nrows, // m_total
|
||||
uint32_t n_threads,
|
||||
size_t dst_row_size,
|
||||
size_t src0_row_size,
|
||||
size_t act_row_size,
|
||||
size_t bias_row_size,
|
||||
uint32_t n_prefetch,
|
||||
bool is_matmul_id,
|
||||
bool is_fused_nx
|
||||
) {
|
||||
(void)act_row_size;
|
||||
size_t src0_sz = 0;
|
||||
size_t act_sz = 0;
|
||||
size_t bias_sz = bias_row_size > 0 ? htp_mm_round_up(bias_row_size, 128) : 0;
|
||||
size_t dst_sz = 0;
|
||||
size_t act_raw_sz = 0;
|
||||
|
||||
if (is_fused_nx) {
|
||||
uint32_t aligned_tile_size = htp_tensor_get_tile_padded_size(wtype);
|
||||
uint32_t n_k_tiles = (ne10 + htp_tensor_get_tile_k(wtype) - 1) / htp_tensor_get_tile_k(wtype);
|
||||
uint32_t tile_row_size = n_k_tiles * aligned_tile_size;
|
||||
size_t weight_sz_per_thread = hex_round_up(n_prefetch * tile_row_size, 128);
|
||||
|
||||
size_t tiled_act_row_size = htp_mm_tiled_act_row_size(wtype, ne10);
|
||||
size_t q_act_sz = hex_round_up(tiled_act_row_size * act_nrows, 128);
|
||||
size_t raw_row_size = htp_mm_act_raw_row_size(ne10);
|
||||
|
||||
src0_sz = weight_sz_per_thread * n_threads; // shared single-weight prefetch buffer
|
||||
act_sz = q_act_sz; // quantized activation buffer
|
||||
bias_sz = 0;
|
||||
dst_sz = 0;
|
||||
act_raw_sz = hex_round_up(raw_row_size * act_nrows, 128);
|
||||
} else if (is_matmul_id) {
|
||||
const size_t act_row_size_tiled = htp_mm_tiled_act_row_size(wtype, ne10);
|
||||
const uint32_t aligned_tile_size = htp_tensor_get_tile_padded_size(wtype);
|
||||
const uint32_t n_k_tiles = (ne10 + htp_tensor_get_tile_k(wtype) - 1) / htp_tensor_get_tile_k(wtype);
|
||||
const uint32_t tile_row_size = n_k_tiles * aligned_tile_size;
|
||||
size_t src0_sz_per_thread = htp_mm_round_up(n_prefetch * tile_row_size, 256);
|
||||
|
||||
act_sz = htp_mm_round_up(act_row_size_tiled * act_nrows, 256);
|
||||
|
||||
size_t raw_row_size = htp_mm_act_raw_row_size(ne10);
|
||||
|
||||
src0_sz = src0_sz_per_thread * n_threads;
|
||||
dst_sz = 0;
|
||||
bias_sz = 0;
|
||||
act_raw_sz = hex_round_up(raw_row_size * act_nrows, 128);
|
||||
} else {
|
||||
const size_t src0_row_size_padded = htp_mm_round_up(src0_row_size, 128);
|
||||
const size_t dst_nrows = (act_nrows > 1) ? 0 : 1;
|
||||
|
||||
switch (kernel_type) {
|
||||
case HTP_MM_KERNEL_HVX_F16_F16_VTCM: {
|
||||
size_t f16_act_row_size = htp_mm_round_up(ne10 * 2, 128);
|
||||
act_sz = htp_mm_round_up(f16_act_row_size * act_nrows, 256);
|
||||
src0_sz = htp_mm_round_up(n_prefetch * src0_row_size_padded, 256) * n_threads;
|
||||
dst_sz = dst_nrows > 0 ? htp_mm_round_up(dst_row_size, 128) * n_threads : 0;
|
||||
act_raw_sz = hex_round_up(hex_round_up(ne10 * sizeof(float), 128) * act_nrows, 128);
|
||||
break;
|
||||
}
|
||||
case HTP_MM_KERNEL_HVX_F32_F32_VTCM:
|
||||
case HTP_MM_KERNEL_HVX_BF16_F32_VTCM: {
|
||||
size_t f32_act_row_size = htp_mm_round_up(ne10 * 4, 128);
|
||||
act_sz = htp_mm_round_up(f32_act_row_size * act_nrows, 256);
|
||||
src0_sz = htp_mm_round_up(n_prefetch * src0_row_size_padded, 256) * n_threads;
|
||||
dst_sz = dst_nrows > 0 ? htp_mm_round_up(dst_row_size, 128) * n_threads : 0;
|
||||
act_raw_sz = 0;
|
||||
break;
|
||||
}
|
||||
case HTP_MM_KERNEL_HVX_QUANT_BLOCK:
|
||||
case HTP_MM_KERNEL_HVX_QUANT_ROW: {
|
||||
size_t q_act_row_size = htp_mm_tiled_act_row_size(wtype, ne10);
|
||||
uint32_t aligned_tile_size = htp_tensor_get_tile_padded_size(wtype);
|
||||
uint32_t n_weight_tiles = (ne10 + htp_tensor_get_tile_k(wtype) - 1) / htp_tensor_get_tile_k(wtype);
|
||||
uint32_t tile_row_size = n_weight_tiles * aligned_tile_size;
|
||||
size_t tiled_vtcm_size = htp_mm_round_up(n_prefetch * tile_row_size, 256);
|
||||
|
||||
src0_sz = tiled_vtcm_size * n_threads;
|
||||
act_sz = htp_mm_round_up(q_act_row_size * act_nrows, 256);
|
||||
|
||||
size_t dst_slice_per_thread = (dst_nrows > 0 && act_nrows == 1) ? htp_mm_round_up((dst_row_size + n_threads - 1) / n_threads, 128) : 0;
|
||||
dst_sz = dst_slice_per_thread * n_threads;
|
||||
size_t raw_row_size = htp_mm_act_raw_row_size(ne10);
|
||||
act_raw_sz = hex_round_up(raw_row_size * act_nrows, 128);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Group A: Persistent buffers across chunk compute
|
||||
size_t off_group_a = 0;
|
||||
VTCM_LAYOUT_ALLOC(off_group_a, off_act, act_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_a, off_bias, bias_sz, bias_sz > 0);
|
||||
|
||||
// Group B: Compute-only buffers (starts at off_group_a)
|
||||
size_t off_group_b = off_group_a;
|
||||
VTCM_LAYOUT_ALLOC(off_group_b, off_src0, src0_sz);
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_b, off_dst, dst_sz, dst_sz > 0);
|
||||
const size_t group_b_size = off_group_b - off_group_a;
|
||||
|
||||
// Group C: Raw activation staging buffer (overlaps Group B, starts at off_group_a)
|
||||
size_t off_group_c = off_group_a;
|
||||
VTCM_LAYOUT_ALLOC_OPTIONAL(off_group_c, off_act_raw, act_raw_sz, act_raw_sz > 0);
|
||||
const size_t group_c_size = off_group_c - off_group_a;
|
||||
|
||||
L->src0_bytes = src0_sz;
|
||||
L->act_bytes = act_sz;
|
||||
L->bias_bytes = bias_sz;
|
||||
L->dst_bytes = dst_sz;
|
||||
L->act_raw_bytes = act_raw_sz;
|
||||
L->total_bytes = off_group_a + hex_smax(group_b_size, group_c_size);
|
||||
}
|
||||
|
||||
static inline bool htp_mm_hvx_solve_vtcm_params(
|
||||
int kernel_type,
|
||||
int wtype,
|
||||
uint32_t ne10,
|
||||
uint32_t act_nrows,
|
||||
uint32_t n_threads,
|
||||
size_t dst_row_size,
|
||||
size_t src0_row_size,
|
||||
size_t act_row_size,
|
||||
size_t bias_row_size,
|
||||
uint32_t n_prefetch,
|
||||
size_t vtcm_budget,
|
||||
struct htp_mm_hvx_vtcm_layout * L_out,
|
||||
uint32_t * m_chunk_out
|
||||
) {
|
||||
struct htp_mm_hvx_vtcm_layout L;
|
||||
htp_mm_hvx_vtcm_layout_build(
|
||||
&L, kernel_type, wtype, ne10, act_nrows, n_threads,
|
||||
dst_row_size, src0_row_size, act_row_size, bias_row_size, n_prefetch, false, false
|
||||
);
|
||||
|
||||
if (L.total_bytes <= vtcm_budget) {
|
||||
*L_out = L;
|
||||
*m_chunk_out = act_nrows;
|
||||
return true;
|
||||
}
|
||||
|
||||
const size_t fixed_bytes = L.src0_bytes + L.bias_bytes + L.dst_bytes;
|
||||
if (vtcm_budget <= fixed_bytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t avail_act = vtcm_budget - fixed_bytes;
|
||||
size_t row_size = 0;
|
||||
if (kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW || kernel_type == HTP_MM_KERNEL_HVX_QUANT_BLOCK) {
|
||||
row_size = htp_mm_tiled_act_row_size(wtype, ne10);
|
||||
} else if (kernel_type == HTP_MM_KERNEL_HVX_F16_F16_VTCM) {
|
||||
row_size = hex_round_up(ne10 * 2, 128);
|
||||
} else {
|
||||
row_size = hex_round_up(ne10 * 4, 128);
|
||||
}
|
||||
if (row_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t eff_row_size = row_size;
|
||||
if (kernel_type == HTP_MM_KERNEL_HVX_QUANT_ROW || kernel_type == HTP_MM_KERNEL_HVX_QUANT_BLOCK) {
|
||||
eff_row_size += htp_mm_act_raw_row_size(ne10);
|
||||
}
|
||||
|
||||
uint32_t m_chunk = (uint32_t) (avail_act / eff_row_size);
|
||||
if (m_chunk > 1) {
|
||||
m_chunk &= ~1U;
|
||||
}
|
||||
if (m_chunk > act_nrows) {
|
||||
m_chunk = act_nrows;
|
||||
}
|
||||
if (m_chunk < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
htp_mm_hvx_vtcm_layout_build(
|
||||
&L, kernel_type, wtype, ne10, m_chunk, n_threads,
|
||||
dst_row_size, src0_row_size, act_row_size, bias_row_size, n_prefetch, false, false
|
||||
);
|
||||
|
||||
while (m_chunk > 2 && L.total_bytes > vtcm_budget) {
|
||||
m_chunk -= 2;
|
||||
htp_mm_hvx_vtcm_layout_build(
|
||||
&L, kernel_type, wtype, ne10, m_chunk, n_threads,
|
||||
dst_row_size, src0_row_size, act_row_size, bias_row_size, n_prefetch, false, false
|
||||
);
|
||||
}
|
||||
|
||||
if (L.total_bytes <= vtcm_budget) {
|
||||
*L_out = L;
|
||||
*m_chunk_out = m_chunk;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline size_t htp_mm_hmx_get_2d_vtcm_size(
|
||||
int wtype, uint32_t k, size_t mc, size_t nc, bool pipeline, uint32_t act_threads, uint32_t aligned_tile_size, size_t bias_size
|
||||
) {
|
||||
struct htp_mm_hmx_vtcm_layout L;
|
||||
htp_mm_hmx_vtcm_layout_build(&L, HTP_MM_KERNEL_HMX_2D, wtype, k, mc, nc, 1, pipeline, act_threads, aligned_tile_size, bias_size);
|
||||
return L.total_bytes;
|
||||
}
|
||||
|
||||
static inline size_t htp_mm_hmx_get_batched_vtcm_size(
|
||||
int wtype, uint32_t k, size_t mc, size_t nc, uint32_t group_size, bool pipeline, uint32_t act_threads, size_t bias_size) {
|
||||
(void)pipeline;
|
||||
struct htp_mm_hmx_vtcm_layout L;
|
||||
htp_mm_hmx_vtcm_layout_build(&L, HTP_MM_KERNEL_HMX_BATCHED, wtype, k, mc, nc, group_size, false, act_threads, 0, bias_size);
|
||||
return L.total_bytes;
|
||||
}
|
||||
|
||||
static inline bool htp_mm_hmx_solve_batched_params(
|
||||
int wtype,
|
||||
uint32_t k,
|
||||
uint32_t ne01_padded,
|
||||
uint32_t ne11,
|
||||
uint32_t group_size,
|
||||
int n_threads,
|
||||
bool pipeline,
|
||||
size_t bias_size,
|
||||
size_t vtcm_budget,
|
||||
size_t * m_chunk_out,
|
||||
size_t * n_chunk_out,
|
||||
int * act_threads_out,
|
||||
size_t * vtcm_size_out
|
||||
) {
|
||||
size_t best_mblocks = SIZE_MAX;
|
||||
int best_act_threads = 0;
|
||||
size_t best_m_chunk = 0;
|
||||
size_t best_n_chunk = 0;
|
||||
size_t best_vtcm_size = 0;
|
||||
|
||||
int act_threads = n_threads;
|
||||
while (act_threads >= 1) {
|
||||
size_t group_overhead = htp_mm_hmx_get_batched_overhead() + (bias_size > 0 ? hex_align_up(bias_size, HTP_MM_HMX_TILE_SIZE) : 0);
|
||||
size_t group_size_per_n, group_size_per_m, group_size_per_mn;
|
||||
htp_mm_hmx_get_batched_chunk_costs(k, group_size, &group_size_per_n, &group_size_per_m, &group_size_per_mn);
|
||||
|
||||
size_t m_chunk_candidate = 0;
|
||||
size_t n_chunk_candidate = 0;
|
||||
size_t vtcm_size_candidate = 0;
|
||||
|
||||
if (htp_mm_hmx_compute_chunks(vtcm_budget, group_overhead, group_size_per_n, group_size_per_m, group_size_per_mn, hex_align_up(ne11, 32), ne01_padded,
|
||||
(size_t) ne01_padded * HTP_MM_HMX_COST_W_DEQUANT, (size_t) ne11 * HTP_MM_HMX_COST_A_CONVERT,
|
||||
&m_chunk_candidate, &n_chunk_candidate, &vtcm_size_candidate) == 0) {
|
||||
size_t exact_size = htp_mm_hmx_get_batched_vtcm_size(wtype, k, m_chunk_candidate, n_chunk_candidate, group_size, pipeline, act_threads, bias_size);
|
||||
if (exact_size <= vtcm_budget) {
|
||||
size_t mblocks = ((size_t) ne11 + m_chunk_candidate - 1) / m_chunk_candidate;
|
||||
if (mblocks < best_mblocks || (mblocks == best_mblocks && act_threads > best_act_threads)) {
|
||||
best_mblocks = mblocks;
|
||||
best_act_threads = act_threads;
|
||||
best_m_chunk = m_chunk_candidate;
|
||||
best_n_chunk = n_chunk_candidate;
|
||||
best_vtcm_size = exact_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (act_threads == 1) {
|
||||
act_threads = 0;
|
||||
} else {
|
||||
act_threads /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_act_threads > 0) {
|
||||
*m_chunk_out = best_m_chunk;
|
||||
*n_chunk_out = best_n_chunk;
|
||||
*vtcm_size_out = best_vtcm_size;
|
||||
*act_threads_out = best_act_threads;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool htp_mm_hmx_solve_2d_params(
|
||||
int wtype,
|
||||
uint32_t k,
|
||||
uint32_t m_id_rows,
|
||||
uint32_t ne01_padded,
|
||||
uint32_t ne11_padded,
|
||||
uint32_t m_for_cost,
|
||||
int n_threads,
|
||||
bool pipeline,
|
||||
bool is_matmul_id,
|
||||
uint32_t aligned_tile_size,
|
||||
size_t bias_size,
|
||||
size_t vtcm_budget,
|
||||
size_t * m_chunk_out,
|
||||
size_t * n_chunk_out,
|
||||
int * act_threads_out,
|
||||
size_t * vtcm_size_out
|
||||
) {
|
||||
size_t best_mblocks = SIZE_MAX;
|
||||
int best_act_threads = 0;
|
||||
size_t best_m_chunk = 0;
|
||||
size_t best_n_chunk = 0;
|
||||
size_t best_vtcm_size = 0;
|
||||
|
||||
const int m_for_chunks = is_matmul_id ? hex_align_up(m_id_rows, 32) : ne11_padded;
|
||||
|
||||
int act_threads = n_threads;
|
||||
while (act_threads >= 1) {
|
||||
size_t simple_2d_overhead = htp_mm_hmx_get_2d_overhead(pipeline, is_matmul_id) + (bias_size > 0 ? hex_align_up(bias_size, HTP_MM_HMX_TILE_SIZE) : 0);
|
||||
size_t simple_2d_size_per_n, simple_2d_size_per_m, simple_2d_size_per_mn;
|
||||
htp_mm_hmx_get_2d_chunk_costs(wtype, k, pipeline, aligned_tile_size, &simple_2d_size_per_n, &simple_2d_size_per_m, &simple_2d_size_per_mn);
|
||||
|
||||
size_t m_chunk_candidate = 0;
|
||||
size_t n_chunk_candidate = 0;
|
||||
size_t vtcm_size_candidate = 0;
|
||||
|
||||
if (htp_mm_hmx_compute_chunks(vtcm_budget, simple_2d_overhead, simple_2d_size_per_n, simple_2d_size_per_m, simple_2d_size_per_mn, m_for_chunks, ne01_padded,
|
||||
(size_t) ne01_padded * HTP_MM_HMX_COST_W_DEQUANT, (size_t) m_for_cost * HTP_MM_HMX_COST_A_CONVERT,
|
||||
&m_chunk_candidate, &n_chunk_candidate, &vtcm_size_candidate) == 0) {
|
||||
size_t exact_size = htp_mm_hmx_get_2d_vtcm_size(wtype, k, m_chunk_candidate, n_chunk_candidate, pipeline, is_matmul_id ? 0 : act_threads, aligned_tile_size, bias_size);
|
||||
if (exact_size <= vtcm_budget) {
|
||||
size_t mblocks = ((size_t) m_for_cost + m_chunk_candidate - 1) / m_chunk_candidate;
|
||||
if (mblocks < best_mblocks || (mblocks == best_mblocks && act_threads > best_act_threads)) {
|
||||
best_mblocks = mblocks;
|
||||
best_act_threads = act_threads;
|
||||
best_m_chunk = m_chunk_candidate;
|
||||
best_n_chunk = n_chunk_candidate;
|
||||
best_vtcm_size = exact_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (act_threads == 1) {
|
||||
act_threads = 0;
|
||||
} else {
|
||||
act_threads /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_act_threads > 0) {
|
||||
*m_chunk_out = best_m_chunk;
|
||||
*n_chunk_out = best_n_chunk;
|
||||
*vtcm_size_out = best_vtcm_size;
|
||||
*act_threads_out = best_act_threads;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HTP_MATMUL_OPS_H
|
||||
@@ -0,0 +1,588 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <HAP_perf.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dma-queue.h"
|
||||
#include "hvx-utils.h"
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "hex-common.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include "pad-ops.h"
|
||||
|
||||
/* Circular wrap: maps any integer x into [0, n) */
|
||||
static inline uint32_t wrap_around(int32_t x, uint32_t n, const struct fastdiv_values * div_n) {
|
||||
if (x >= 0) {
|
||||
const uint32_t ux = (uint32_t) x;
|
||||
return ux < n ? ux : fastmodulo(ux, n, div_n);
|
||||
}
|
||||
const uint32_t ux = (uint32_t) (-x);
|
||||
const uint32_t rem = ux < n ? ux : fastmodulo(ux, n, div_n);
|
||||
return rem ? (n - rem) : 0;
|
||||
}
|
||||
|
||||
/* Decompose a flat dst row index into (i1, i2, i3) */
|
||||
static inline void pad_decompose_row(uint32_t ir, uint32_t ne1, uint32_t ne2,
|
||||
const struct htp_pad_kernel_params * kparams,
|
||||
uint32_t *i1, uint32_t *i2, uint32_t *i3) {
|
||||
*i3 = fastdiv(ir, &kparams->div_ne1_ne2);
|
||||
const uint32_t rem = ir - (*i3) * (ne1 * ne2);
|
||||
*i2 = fastdiv(rem, &kparams->div_ne1);
|
||||
*i1 = rem - (*i2) * ne1;
|
||||
}
|
||||
|
||||
/* Return non-zero if row (i1,i2,i3) falls in the non-padded interior */
|
||||
static inline int pad_is_interior(uint32_t i1, uint32_t i2, uint32_t i3,
|
||||
int32_t lp1, int32_t rp1, uint32_t ne1,
|
||||
int32_t lp2, int32_t rp2, uint32_t ne2,
|
||||
int32_t lp3, int32_t rp3, uint32_t ne3) {
|
||||
return ((int32_t)i1 >= lp1 && (int32_t)i1 < (int32_t)ne1 - rp1) &&
|
||||
((int32_t)i2 >= lp2 && (int32_t)i2 < (int32_t)ne2 - rp2) &&
|
||||
((int32_t)i3 >= lp3 && (int32_t)i3 < (int32_t)ne3 - rp3);
|
||||
}
|
||||
|
||||
/* Compute the DDR src row pointer for a zero-pad interior row */
|
||||
static inline const uint8_t * pad_src_row_ptr(const struct htp_tensor * src,
|
||||
uint32_t i1, uint32_t i2, uint32_t i3,
|
||||
int32_t lp1, int32_t lp2, int32_t lp3) {
|
||||
return (const uint8_t *) src->data
|
||||
+ (i1 - (uint32_t)lp1) * src->nb[1]
|
||||
+ (i2 - (uint32_t)lp2) * src->nb[2]
|
||||
+ (i3 - (uint32_t)lp3) * src->nb[3];
|
||||
}
|
||||
|
||||
static inline dma_addr_t pad_src_row_data(const struct htp_tensor * src,
|
||||
uint32_t i1, uint32_t i2, uint32_t i3,
|
||||
int32_t lp1, int32_t lp2, int32_t lp3) {
|
||||
return src->data
|
||||
+ (i1 - (uint32_t)lp1) * src->nb[1]
|
||||
+ (i2 - (uint32_t)lp2) * src->nb[2]
|
||||
+ (i3 - (uint32_t)lp3) * src->nb[3];
|
||||
}
|
||||
|
||||
/* Compute the DDR src row pointer for a circular row (wrap-around indexing) */
|
||||
static inline const uint8_t * pad_circ_src_row_ptr(const struct htp_tensor * src,
|
||||
const struct htp_pad_kernel_params * kparams,
|
||||
uint32_t i1, uint32_t i2, uint32_t i3,
|
||||
int32_t lp1, int32_t lp2, int32_t lp3) {
|
||||
return (const uint8_t *) src->data
|
||||
+ wrap_around((int32_t)i1 - lp1, src->ne[1], &kparams->div_ne01) * src->nb[1]
|
||||
+ wrap_around((int32_t)i2 - lp2, src->ne[2], &kparams->div_ne02) * src->nb[2]
|
||||
+ wrap_around((int32_t)i3 - lp3, src->ne[3], &kparams->div_ne03) * src->nb[3];
|
||||
}
|
||||
|
||||
static inline dma_addr_t pad_circ_src_row_data(const struct htp_tensor * src,
|
||||
const struct htp_pad_kernel_params * kparams,
|
||||
uint32_t i1, uint32_t i2, uint32_t i3,
|
||||
int32_t lp1, int32_t lp2, int32_t lp3) {
|
||||
return src->data
|
||||
+ wrap_around((int32_t)i1 - lp1, src->ne[1], &kparams->div_ne01) * src->nb[1]
|
||||
+ wrap_around((int32_t)i2 - lp2, src->ne[2], &kparams->div_ne02) * src->nb[2]
|
||||
+ wrap_around((int32_t)i3 - lp3, src->ne[3], &kparams->div_ne03) * src->nb[3];
|
||||
}
|
||||
|
||||
struct htp_pad_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_pad_kernel_params * kparams;
|
||||
|
||||
int32_t lp0, rp0;
|
||||
int32_t lp1, rp1;
|
||||
int32_t lp2, rp2;
|
||||
int32_t lp3, rp3;
|
||||
|
||||
uint32_t nrows_per_thread;
|
||||
uint32_t total_dst_rows;
|
||||
uint32_t row_start;
|
||||
|
||||
size_t type_size;
|
||||
|
||||
// Row sizes for DMA kernel (populated when VTCM is available)
|
||||
size_t src_row_size;
|
||||
size_t src_row_size_aligned;
|
||||
size_t dst_row_size;
|
||||
size_t dst_row_size_aligned;
|
||||
};
|
||||
|
||||
#define htp_pad_preamble \
|
||||
const struct htp_tensor * src = octx->src[0]; \
|
||||
const struct htp_tensor * dst = octx->dst; \
|
||||
\
|
||||
const uint32_t ne00 = src->ne[0]; \
|
||||
const uint32_t nb00 = src->nb[0]; \
|
||||
\
|
||||
const uint32_t ne0 = dst->ne[0]; \
|
||||
const uint32_t ne1 = dst->ne[1]; \
|
||||
const uint32_t ne2 = dst->ne[2]; \
|
||||
const uint32_t ne3 = dst->ne[3]; \
|
||||
\
|
||||
const uint32_t nb1 = dst->nb[1]; \
|
||||
const uint32_t nb2 = dst->nb[2]; \
|
||||
const uint32_t nb3 = dst->nb[3]; \
|
||||
\
|
||||
const int32_t lp0 = pctx->lp0, rp0 = pctx->rp0; \
|
||||
const int32_t lp1 = pctx->lp1, rp1 = pctx->rp1; \
|
||||
const int32_t lp2 = pctx->lp2, rp2 = pctx->rp2; \
|
||||
const int32_t lp3 = pctx->lp3, rp3 = pctx->rp3; \
|
||||
\
|
||||
const size_t type_size = pctx->type_size; \
|
||||
\
|
||||
const uint32_t row_start = pctx->row_start + pctx->nrows_per_thread * ith; \
|
||||
const uint32_t row_end = MIN(row_start + pctx->nrows_per_thread, pctx->row_start + pctx->total_dst_rows);
|
||||
|
||||
|
||||
#define htp_pad_dma_preamble \
|
||||
const size_t src_row_size = pctx->src_row_size; \
|
||||
const size_t src_row_size_aligned = pctx->src_row_size_aligned; \
|
||||
const size_t dst_row_size = pctx->dst_row_size; \
|
||||
const size_t dst_row_size_aligned = pctx->dst_row_size_aligned; \
|
||||
\
|
||||
uint8_t * src_spad_base = octx->src0_spad.data + ith * octx->src0_spad.size_per_thread; \
|
||||
uint8_t * dst_spad_base = octx->dst_spad.data + ith * octx->dst_spad.size_per_thread; \
|
||||
\
|
||||
dma_queue * dma_q = octx->ctx->dma[ith];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HVX vectorized PAD kernel
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void pad_job_per_thread_hvx(unsigned int nth, unsigned int ith, void * data) {
|
||||
const struct htp_pad_context * pctx = (const struct htp_pad_context *) data;
|
||||
struct htp_ops_context * octx = pctx->octx;
|
||||
htp_pad_preamble;
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, row_start);
|
||||
|
||||
for (uint32_t dst_row = row_start; dst_row < row_end; dst_row++) {
|
||||
uint32_t i1, i2, i3;
|
||||
pad_decompose_row(dst_row, ne1, ne2, pctx->kparams, &i1, &i2, &i3);
|
||||
|
||||
uint8_t * dst_ptr = (uint8_t *) dst->data + i1 * nb1 + i2 * nb2 + i3 * nb3;
|
||||
|
||||
const int interior = pad_is_interior(i1, i2, i3,
|
||||
lp1, rp1, ne1,
|
||||
lp2, rp2, ne2,
|
||||
lp3, rp3, ne3);
|
||||
|
||||
if (!interior) {
|
||||
hvx_splat_f32_u(dst_ptr, 0.0f, ne0);
|
||||
} else {
|
||||
const uint8_t * src_ptr = pad_src_row_ptr(src, i1, i2, i3, lp1, lp2, lp3);
|
||||
|
||||
if (lp0 > 0) {
|
||||
hvx_splat_f32_u(dst_ptr, 0.0f, (uint32_t)lp0);
|
||||
}
|
||||
|
||||
uint8_t * dst_row_start = dst_ptr + (size_t)lp0 * type_size;
|
||||
if (nb00 == type_size) {
|
||||
hvx_copy_f32_uu(dst_row_start, src_ptr, ne00);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < ne00; i++) {
|
||||
memcpy(dst_row_start + i * type_size,
|
||||
src_ptr + (size_t)i * nb00,
|
||||
type_size);
|
||||
}
|
||||
}
|
||||
|
||||
if (rp0 > 0) {
|
||||
hvx_splat_f32_u(dst_ptr + ((size_t)lp0 + ne00) * type_size, 0.0f, (uint32_t)rp0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, row_start);
|
||||
|
||||
FARF(HIGH, "pad-hvx %d/%d: (%ux%ux%ux%u) -> (%ux%ux%ux%u) rows %u:%u\n",
|
||||
ith, nth,
|
||||
src->ne[0], src->ne[1], src->ne[2], src->ne[3],
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
row_start, row_end);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HVX + DMA PAD kernel - aligned, double-buffered
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void pad_job_per_thread_hvx_dma(unsigned int nth, unsigned int ith, void * data) {
|
||||
const struct htp_pad_context * pctx = (const struct htp_pad_context *) data;
|
||||
struct htp_ops_context * octx = pctx->octx;
|
||||
htp_pad_preamble;
|
||||
htp_pad_dma_preamble;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Priming phase: push 2 pairs of (dummy_dst_DMA, src_DMA) to seed the
|
||||
// double-buffer pipeline before the main loop begins.
|
||||
// -----------------------------------------------------------------------
|
||||
for (uint32_t ir = row_start, spad_idx = 0; ir < row_end && spad_idx < 2; ir++, spad_idx++) {
|
||||
uint8_t * src_spad_cur = src_spad_base + spad_idx * src_row_size_aligned;
|
||||
uint8_t * dst_spad_cur = dst_spad_base + spad_idx * dst_row_size_aligned;
|
||||
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(dst->data, dst_spad_cur),
|
||||
dst_row_size, dst_row_size_aligned, dst_row_size, 0);
|
||||
|
||||
uint32_t i1, i2, i3;
|
||||
pad_decompose_row(ir, ne1, ne2, pctx->kparams, &i1, &i2, &i3);
|
||||
const int interior = pad_is_interior(i1, i2, i3,
|
||||
lp1, rp1, ne1,
|
||||
lp2, rp2, ne2,
|
||||
lp3, rp3, ne3);
|
||||
|
||||
const dma_addr_t src_data = interior
|
||||
? pad_src_row_data(src, i1, i2, i3, lp1, lp2, lp3) : src->data;
|
||||
|
||||
// Interior row: real DMA (1 row) from DDR to VTCM.
|
||||
// Border row: null DMA (nrows=0)
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad_cur, src_data),
|
||||
src_row_size_aligned, src_row_size, src_row_size, interior ? 1 : 0);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Main loop: pop completed DMAs, compute in VTCM with aligned HVX ops,
|
||||
// push dst DMA and prefetch src for the next+1 row.
|
||||
// -----------------------------------------------------------------------
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
for (uint32_t ir = row_start; ir < row_end; ir++) {
|
||||
uint8_t * dst_spad_cur = (uint8_t *) dma_queue_pop(dma_q).src;
|
||||
uint8_t * src_spad_cur = (uint8_t *) dma_queue_pop(dma_q).dst;
|
||||
|
||||
uint32_t i1, i2, i3;
|
||||
pad_decompose_row(ir, ne1, ne2, pctx->kparams, &i1, &i2, &i3);
|
||||
|
||||
const dma_addr_t dst_data = dst->data + i1 * nb1 + i2 * nb2 + i3 * nb3;
|
||||
|
||||
const int interior = pad_is_interior(i1, i2, i3,
|
||||
lp1, rp1, ne1,
|
||||
lp2, rp2, ne2,
|
||||
lp3, rp3, ne3);
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir);
|
||||
if (!interior) {
|
||||
hvx_splat_f32_a(dst_spad_cur, 0.0f, ne0);
|
||||
} else {
|
||||
hvx_splat_f32_a(dst_spad_cur, 0.0f, ne0);
|
||||
|
||||
uint8_t * dst_interior = dst_spad_cur + (size_t)lp0 * type_size;
|
||||
|
||||
if ((uintptr_t)dst_interior % VLEN == 0) {
|
||||
hvx_copy_f32_aa(dst_interior, src_spad_cur, ne00);
|
||||
} else {
|
||||
hvx_copy_f32_ua(dst_interior, src_spad_cur, ne00);
|
||||
}
|
||||
}
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir);
|
||||
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(dst_data, dst_spad_cur),
|
||||
dst_row_size, dst_row_size_aligned, dst_row_size, 1);
|
||||
|
||||
const uint32_t next_row = ir + 2;
|
||||
if (next_row < row_end) {
|
||||
uint32_t ni1, ni2, ni3;
|
||||
pad_decompose_row(next_row, ne1, ne2, pctx->kparams, &ni1, &ni2, &ni3);
|
||||
const int next_interior = pad_is_interior(ni1, ni2, ni3,
|
||||
lp1, rp1, ne1,
|
||||
lp2, rp2, ne2,
|
||||
lp3, rp3, ne3);
|
||||
const dma_addr_t next_src_data = next_interior
|
||||
? pad_src_row_data(src, ni1, ni2, ni3, lp1, lp2, lp3) : src->data;
|
||||
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad_cur, next_src_data),
|
||||
src_row_size_aligned, src_row_size, src_row_size, next_interior ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
|
||||
FARF(HIGH, "pad-hvx-dma %d/%d: (%ux%ux%ux%u) -> (%ux%ux%ux%u) rows %u:%u\n",
|
||||
ith, nth,
|
||||
src->ne[0], src->ne[1], src->ne[2], src->ne[3],
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
row_start, row_end);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HVX circular PAD kernel
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void pad_job_per_thread_hvx_circular(unsigned int nth, unsigned int ith, void * data) {
|
||||
const struct htp_pad_context * pctx = (const struct htp_pad_context *) data;
|
||||
struct htp_ops_context * octx = pctx->octx;
|
||||
htp_pad_preamble;
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, row_start);
|
||||
|
||||
for (uint32_t dst_row = row_start; dst_row < row_end; dst_row++) {
|
||||
uint32_t i1, i2, i3;
|
||||
pad_decompose_row(dst_row, ne1, ne2, pctx->kparams, &i1, &i2, &i3);
|
||||
|
||||
uint8_t * dst_ptr = (uint8_t *) dst->data + i1 * nb1 + i2 * nb2 + i3 * nb3;
|
||||
const uint8_t * src_row = pad_circ_src_row_ptr(src, pctx->kparams, i1, i2, i3, lp1, lp2, lp3);
|
||||
|
||||
if (nb00 == type_size) {
|
||||
|
||||
if (lp0 > 0) {
|
||||
if ((uint32_t)lp0 < 32) {
|
||||
memcpy(dst_ptr,
|
||||
src_row + (size_t)(ne00 - (uint32_t)lp0) * type_size,
|
||||
(size_t)lp0 * type_size);
|
||||
} else {
|
||||
hvx_copy_f32_uu(dst_ptr,
|
||||
src_row + (size_t)(ne00 - (uint32_t)lp0) * type_size,
|
||||
(uint32_t)lp0);
|
||||
}
|
||||
}
|
||||
hvx_copy_f32_uu(dst_ptr + (size_t)lp0 * type_size, src_row, ne00);
|
||||
if (rp0 > 0) {
|
||||
if ((uint32_t)rp0 < 32) {
|
||||
memcpy(dst_ptr + ((size_t)lp0 + ne00) * type_size,
|
||||
src_row,
|
||||
(size_t)rp0 * type_size);
|
||||
} else {
|
||||
hvx_copy_f32_uu(dst_ptr + ((size_t)lp0 + ne00) * type_size,
|
||||
src_row,
|
||||
(uint32_t)rp0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < (uint32_t)lp0; i++) {
|
||||
*(float *)(dst_ptr + i * type_size) =
|
||||
*(const float *)(src_row + (size_t)(ne00 - (uint32_t)lp0 + i) * nb00);
|
||||
}
|
||||
for (uint32_t i = 0; i < ne00; i++) {
|
||||
*(float *)(dst_ptr + ((size_t)lp0 + i) * type_size) =
|
||||
*(const float *)(src_row + (size_t)i * nb00);
|
||||
}
|
||||
for (uint32_t i = 0; i < (uint32_t)rp0; i++) {
|
||||
*(float *)(dst_ptr + ((size_t)lp0 + ne00 + i) * type_size) =
|
||||
*(const float *)(src_row + (size_t)i * nb00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, row_start);
|
||||
|
||||
FARF(HIGH, "pad-hvx-circ %d/%d: (%ux%ux%ux%u) -> (%ux%ux%ux%u) rows %u:%u\n",
|
||||
ith, nth,
|
||||
src->ne[0], src->ne[1], src->ne[2], src->ne[3],
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
row_start, row_end);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HVX + DMA circular PAD kernel - aligned, double-buffered
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void pad_job_per_thread_hvx_circular_dma(unsigned int nth, unsigned int ith, void * data) {
|
||||
const struct htp_pad_context * pctx = (const struct htp_pad_context *) data;
|
||||
struct htp_ops_context * octx = pctx->octx;
|
||||
htp_pad_preamble;
|
||||
htp_pad_dma_preamble;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Priming phase: push 2 pairs of (dummy_dst_DMA, src_DMA) to seed the
|
||||
// double-buffer pipeline. Every row is a real src DMA (no null DMAs).
|
||||
// -----------------------------------------------------------------------
|
||||
for (uint32_t ir = row_start, spad_idx = 0; ir < row_end && spad_idx < 2; ir++, spad_idx++) {
|
||||
uint8_t * src_spad_cur = src_spad_base + spad_idx * src_row_size_aligned;
|
||||
uint8_t * dst_spad_cur = dst_spad_base + spad_idx * dst_row_size_aligned;
|
||||
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(dst->data, dst_spad_cur),
|
||||
dst_row_size, dst_row_size_aligned, dst_row_size, 0);
|
||||
|
||||
uint32_t pi1, pi2, pi3;
|
||||
pad_decompose_row(ir, ne1, ne2, pctx->kparams, &pi1, &pi2, &pi3);
|
||||
const dma_addr_t src_data = pad_circ_src_row_data(src, pctx->kparams, pi1, pi2, pi3, lp1, lp2, lp3);
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad_cur, src_data),
|
||||
src_row_size_aligned, src_row_size, src_row_size, 1);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Main loop: pop completed DMAs, assemble circular row in VTCM with
|
||||
// aligned HVX ops, push dst DMA and prefetch src for the next+1 row.
|
||||
// -----------------------------------------------------------------------
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
|
||||
for (uint32_t ir = row_start; ir < row_end; ir++) {
|
||||
uint8_t * dst_spad_cur = (uint8_t *) dma_queue_pop(dma_q).src;
|
||||
uint8_t * src_spad_cur = (uint8_t *) dma_queue_pop(dma_q).dst;
|
||||
|
||||
uint32_t i1, i2, i3;
|
||||
pad_decompose_row(ir, ne1, ne2, pctx->kparams, &i1, &i2, &i3);
|
||||
const dma_addr_t dst_data = dst->data + i1 * nb1 + i2 * nb2 + i3 * nb3;
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir);
|
||||
if (lp0 > 0) {
|
||||
uint8_t * dst_left = dst_spad_cur;
|
||||
const uint8_t * src_left = src_spad_cur + (size_t)(ne00 - (uint32_t)lp0) * type_size;
|
||||
if ((uint32_t)lp0 < 32) {
|
||||
memcpy(dst_left, src_left, (size_t)lp0 * type_size);
|
||||
} else {
|
||||
hvx_copy_f32_uu(dst_left, src_left, (uint32_t)lp0);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t * dst_mid = dst_spad_cur + (size_t)lp0 * type_size;
|
||||
if ((uintptr_t)dst_mid % VLEN == 0) {
|
||||
hvx_copy_f32_aa(dst_mid, src_spad_cur, ne00);
|
||||
} else {
|
||||
hvx_copy_f32_ua(dst_mid, src_spad_cur, ne00);
|
||||
}
|
||||
}
|
||||
|
||||
if (rp0 > 0) {
|
||||
uint8_t * dst_right = dst_spad_cur + ((size_t)lp0 + ne00) * type_size;
|
||||
if ((uint32_t)rp0 < 32) {
|
||||
memcpy(dst_right, src_spad_cur, (size_t)rp0 * type_size);
|
||||
} else {
|
||||
if ((uintptr_t)dst_right % VLEN == 0) {
|
||||
hvx_copy_f32_aa(dst_right, src_spad_cur, (uint32_t)rp0);
|
||||
} else {
|
||||
hvx_copy_f32_ua(dst_right, src_spad_cur, (uint32_t)rp0);
|
||||
}
|
||||
}
|
||||
}
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) ir);
|
||||
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(dst_data, dst_spad_cur),
|
||||
dst_row_size, dst_row_size_aligned, dst_row_size, 1);
|
||||
|
||||
const uint32_t next_row = ir + 2;
|
||||
if (next_row < row_end) {
|
||||
uint32_t nri1, nri2, nri3;
|
||||
pad_decompose_row(next_row, ne1, ne2, pctx->kparams, &nri1, &nri2, &nri3);
|
||||
const dma_addr_t next_src_data = pad_circ_src_row_data(src, pctx->kparams, nri1, nri2, nri3, lp1, lp2, lp3);
|
||||
dma_queue_push(dma_q,
|
||||
dma_make_data(src_spad_cur, next_src_data),
|
||||
src_row_size_aligned, src_row_size, src_row_size, 1);
|
||||
}
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_q);
|
||||
|
||||
FARF(HIGH, "pad-hvx-circ-dma %d/%d: (%ux%ux%ux%u) -> (%ux%ux%ux%u) rows %u:%u\n",
|
||||
ith, nth,
|
||||
src->ne[0], src->ne[1], src->ne[2], src->ne[3],
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
row_start, row_end);
|
||||
}
|
||||
|
||||
int op_pad(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const struct htp_pad_kernel_params * kparams =
|
||||
(const struct htp_pad_kernel_params *) octx->kernel_params;
|
||||
|
||||
const int32_t lp0 = octx->op_params[0];
|
||||
const int32_t rp0 = octx->op_params[1];
|
||||
const int32_t lp1 = octx->op_params[2];
|
||||
const int32_t rp1 = octx->op_params[3];
|
||||
const int32_t lp2 = octx->op_params[4];
|
||||
const int32_t rp2 = octx->op_params[5];
|
||||
const int32_t lp3 = octx->op_params[6];
|
||||
const int32_t rp3 = octx->op_params[7];
|
||||
const int32_t circular = octx->op_params[8];
|
||||
|
||||
const uint32_t total_dst_rows = kparams->total_rows;
|
||||
const size_t type_size = kparams->elem_size;
|
||||
const size_t src_row_size = src0->ne[0] * type_size;
|
||||
const size_t dst_row_size = dst->ne[0] * type_size;
|
||||
|
||||
uint32_t row_start = 0;
|
||||
uint32_t nrows = total_dst_rows;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
uint32_t rows_per_chunk = 0;
|
||||
htp_tensor_mdev_rows_per_chunk(dst, type_size, (uint32_t) dst_row_size, &rows_per_chunk);
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(total_dst_rows, rows_per_chunk, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
row_start = range.start;
|
||||
nrows = range.count;
|
||||
}
|
||||
|
||||
if (nrows == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, MIN(kparams->n_threads, nrows))) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
if (!kparams->use_dma && (htp_tensor_is_extended(src0) || htp_tensor_is_extended(dst))) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
struct htp_pad_vtcm_layout layout = { 0 };
|
||||
if (kparams->use_dma) {
|
||||
htp_pad_vtcm_layout_build(&layout, src_row_size, dst_row_size, n_threads);
|
||||
|
||||
octx->src0_spad.size_per_thread = layout.src0_bytes_per_thread;
|
||||
octx->dst_spad.size_per_thread = layout.dst_bytes_per_thread;
|
||||
octx->src0_spad.size = n_threads * layout.src0_bytes_per_thread;
|
||||
octx->dst_spad.size = n_threads * layout.dst_bytes_per_thread;
|
||||
octx->src0_spad.data = VTCM_LAYOUT_PTR(uint8_t, octx->ctx->vtcm_base, layout.off_src0);
|
||||
octx->dst_spad.data = VTCM_LAYOUT_PTR(uint8_t, octx->ctx->vtcm_base, layout.off_dst);
|
||||
octx->src0_spad.src = NULL;
|
||||
octx->dst_spad.src = NULL;
|
||||
}
|
||||
|
||||
struct htp_pad_context pctx = {
|
||||
.octx = octx,
|
||||
.kparams = kparams,
|
||||
.lp0 = lp0, .rp0 = rp0,
|
||||
.lp1 = lp1, .rp1 = rp1,
|
||||
.lp2 = lp2, .rp2 = rp2,
|
||||
.lp3 = lp3, .rp3 = rp3,
|
||||
.nrows_per_thread = fastdiv(nrows + n_threads - 1, &octx->n_threads_div),
|
||||
.total_dst_rows = nrows,
|
||||
.row_start = row_start,
|
||||
.type_size = type_size,
|
||||
.src_row_size = src_row_size,
|
||||
.src_row_size_aligned = layout.src_row_size_aligned,
|
||||
.dst_row_size = dst_row_size,
|
||||
.dst_row_size_aligned = layout.dst_row_size_aligned,
|
||||
};
|
||||
|
||||
FARF(HIGH, "pad-hvx%s%s: (%ux%ux%ux%u) -> (%ux%ux%ux%u) pads=(%d,%d,%d,%d,%d,%d,%d,%d)\n",
|
||||
circular ? "-circ" : "",
|
||||
kparams->use_dma ? "-dma" : "",
|
||||
src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
lp0, rp0, lp1, rp1, lp2, rp2, lp3, rp3);
|
||||
|
||||
switch (kparams->kernel_type) {
|
||||
case HTP_PAD_KERNEL_HVX_CIRCULAR_DMA:
|
||||
work_queue_run(octx->ctx->work_queue, pad_job_per_thread_hvx_circular_dma, &pctx, n_threads);
|
||||
break;
|
||||
case HTP_PAD_KERNEL_HVX_CIRCULAR:
|
||||
work_queue_run(octx->ctx->work_queue, pad_job_per_thread_hvx_circular, &pctx, n_threads);
|
||||
break;
|
||||
case HTP_PAD_KERNEL_HVX_DMA:
|
||||
work_queue_run(octx->ctx->work_queue, pad_job_per_thread_hvx_dma, &pctx, n_threads);
|
||||
break;
|
||||
default:
|
||||
work_queue_run(octx->ctx->work_queue, pad_job_per_thread_hvx, &pctx, n_threads);
|
||||
break;
|
||||
}
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef HTP_PAD_OPS_H
|
||||
#define HTP_PAD_OPS_H
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "hex-fastdiv.h"
|
||||
#include "htp-vtcm.h"
|
||||
#include <stdint.h>
|
||||
|
||||
enum htp_pad_kernel_type {
|
||||
HTP_PAD_KERNEL_HVX = 0,
|
||||
HTP_PAD_KERNEL_HVX_DMA = 1,
|
||||
HTP_PAD_KERNEL_HVX_CIRCULAR = 2,
|
||||
HTP_PAD_KERNEL_HVX_CIRCULAR_DMA = 3,
|
||||
};
|
||||
|
||||
struct htp_pad_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src0;
|
||||
size_t off_dst;
|
||||
size_t src_row_size_aligned;
|
||||
size_t dst_row_size_aligned;
|
||||
size_t src0_bytes_per_thread;
|
||||
size_t dst_bytes_per_thread;
|
||||
};
|
||||
|
||||
static inline void htp_pad_vtcm_layout_build(
|
||||
struct htp_pad_vtcm_layout * L,
|
||||
size_t src_row_size,
|
||||
size_t dst_row_size,
|
||||
uint32_t n_threads
|
||||
) {
|
||||
L->src_row_size_aligned = hex_round_up(src_row_size, 128);
|
||||
L->dst_row_size_aligned = hex_round_up(dst_row_size, 128);
|
||||
L->src0_bytes_per_thread = 2 * L->src_row_size_aligned;
|
||||
L->dst_bytes_per_thread = 2 * L->dst_row_size_aligned;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src0, L->src0_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, L->dst_bytes_per_thread * n_threads);
|
||||
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
struct htp_pad_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint32_t n_threads;
|
||||
uint32_t total_rows;
|
||||
uint32_t use_dma;
|
||||
uint32_t elem_size;
|
||||
uint32_t vtcm_size;
|
||||
|
||||
struct fastdiv_values div_ne1;
|
||||
struct fastdiv_values div_ne1_ne2;
|
||||
struct fastdiv_values div_ne01;
|
||||
struct fastdiv_values div_ne02;
|
||||
struct fastdiv_values div_ne03;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_pad_kernel_params) <= 128, "htp_pad_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_pad_kernel_params) <= 128, "htp_pad_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_PAD_OPS_H
|
||||
@@ -0,0 +1,390 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
|
||||
#include <float.h>
|
||||
#include <HAP_farf.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "dma-queue.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "hvx-inverse.h"
|
||||
#include "hvx-types.h"
|
||||
#include "hvx-utils.h"
|
||||
#include "pool-ops.h"
|
||||
|
||||
#define HTP_POOL_MAX 0
|
||||
#define HTP_POOL_AVG 1
|
||||
|
||||
// Fast path: exact non-overlapping tiling (stride == kernel, no padding), kernel_x in {1,2}.
|
||||
// Every window is guaranteed fully in-bounds, so this never needs boundary clamping.
|
||||
static void pool_plane_hvx(
|
||||
const float * src, float * dst, const struct htp_pool_2d_kernel_params * p) {
|
||||
const bool is_max = (p->pool_op == HTP_POOL_MAX);
|
||||
const HVX_Vector scale = hvx_vec_splat_f32(p->inv_kernel_area);
|
||||
const HVX_Vector seed = is_max ? hvx_vec_splat_f32(-FLT_MAX) : Q6_V_vsplat_R(0);
|
||||
|
||||
for (uint32_t oy = 0; oy < p->dst_y; ++oy) {
|
||||
const uint32_t sy = oy * p->kernel_y;
|
||||
for (uint32_t ox = 0; ox < p->dst_x; ox += VLEN_FP32) {
|
||||
const uint32_t rem = p->dst_x - ox;
|
||||
const uint32_t nbytes = (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN;
|
||||
HVX_Vector acc = seed;
|
||||
for (uint32_t ky = 0; ky < p->kernel_y; ++ky) {
|
||||
const float * row = src + (sy + ky) * p->src_x;
|
||||
if (p->kernel_x == 2) {
|
||||
const HVX_Vector v0 = *(const HVX_UVector *) (row + ox * 2);
|
||||
const HVX_Vector v1 = *(const HVX_UVector *) (row + ox * 2 + VLEN_FP32);
|
||||
const HVX_VectorPair deinterleaved = Q6_W_vdeal_VVR(v1, v0, -4);
|
||||
const HVX_Vector lo = Q6_V_lo_W(deinterleaved);
|
||||
const HVX_Vector hi = Q6_V_hi_W(deinterleaved);
|
||||
acc = is_max ? Q6_Vsf_vmax_VsfVsf(acc, Q6_Vsf_vmax_VsfVsf(lo, hi))
|
||||
: hvx_vec_add_f32_f32(acc, hvx_vec_add_f32_f32(lo, hi));
|
||||
} else if (p->kernel_x == 1) {
|
||||
const HVX_Vector v = *(const HVX_UVector *) (row + ox);
|
||||
acc = is_max ? Q6_Vsf_vmax_VsfVsf(acc, v) : hvx_vec_add_f32_f32(acc, v);
|
||||
}
|
||||
}
|
||||
hvx_vec_store_u(dst + oy * p->dst_x + ox, nbytes, is_max ? acc : hvx_vec_mul_f32_f32(acc, scale));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Narrow exact-tiling path. The input is staged in VTCM with one vector of
|
||||
// guard space, so full-width loads are safe even when src_x is below 32.
|
||||
static void pool_plane_hvx_narrow(
|
||||
const float * src, float * dst, const struct htp_pool_2d_kernel_params * p) {
|
||||
const bool is_max = (p->pool_op == HTP_POOL_MAX);
|
||||
const HVX_Vector scale = hvx_vec_splat_f32(p->inv_kernel_area);
|
||||
const HVX_Vector seed = is_max ? hvx_vec_splat_f32(-FLT_MAX) : Q6_V_vsplat_R(0);
|
||||
|
||||
for (uint32_t oy = 0; oy < p->dst_y; ++oy) {
|
||||
const uint32_t sy = oy * p->kernel_y;
|
||||
HVX_Vector acc = seed;
|
||||
for (uint32_t ky = 0; ky < p->kernel_y; ++ky) {
|
||||
const float * row = src + (sy + ky) * p->src_x;
|
||||
const HVX_Vector v0 = *(const HVX_UVector *) row;
|
||||
HVX_Vector v = v0;
|
||||
if (p->kernel_x == 2) {
|
||||
// The second vector is zero because a narrow row has fewer
|
||||
// than 32 input elements. The low lanes still contain the
|
||||
// complete even/odd pairs needed by the output.
|
||||
const HVX_Vector zero = Q6_V_vsplat_R(0);
|
||||
const HVX_VectorPair deinterleaved = Q6_W_vdeal_VVR(zero, v0, -4);
|
||||
const HVX_Vector even = Q6_V_lo_W(deinterleaved);
|
||||
const HVX_Vector odd = Q6_V_hi_W(deinterleaved);
|
||||
v = is_max ? Q6_Vsf_vmax_VsfVsf(even, odd)
|
||||
: hvx_vec_add_f32_f32(even, odd);
|
||||
}
|
||||
acc = is_max ? Q6_Vsf_vmax_VsfVsf(acc, v)
|
||||
: hvx_vec_add_f32_f32(acc, v);
|
||||
}
|
||||
hvx_vec_store_u(dst + oy * p->dst_x, p->dst_x * sizeof(float),
|
||||
is_max ? acc : hvx_vec_mul_f32_f32(acc, scale));
|
||||
}
|
||||
}
|
||||
|
||||
static void pool_plane_global(
|
||||
const float * src, float * dst, const struct htp_pool_2d_kernel_params * p) {
|
||||
const uint32_t n = p->src_x * p->src_y;
|
||||
float val;
|
||||
if (p->pool_op == HTP_POOL_MAX) {
|
||||
val = hvx_reduce_max_f32((const uint8_t *) src, n);
|
||||
} else {
|
||||
val = hvx_reduce_sum_f32((const uint8_t *) src, n) * p->inv_kernel_area;
|
||||
}
|
||||
hvx_vec_store_u(dst, sizeof(float), hvx_vec_splat_f32(val));
|
||||
}
|
||||
|
||||
static void pool_plane_block(
|
||||
const float * src, float * dst, const struct htp_pool_2d_kernel_params * p) {
|
||||
for (uint32_t oy = 0; oy < p->dst_y; ++oy) {
|
||||
const float * row = src + oy * p->src_x;
|
||||
float * dst_row = dst + oy * p->dst_x;
|
||||
for (uint32_t ox = 0; ox < p->dst_x; ++ox) {
|
||||
const uint8_t * block = (const uint8_t *) (row + ox * p->kernel_x);
|
||||
float val;
|
||||
if (p->pool_op == HTP_POOL_MAX) {
|
||||
val = hvx_reduce_max_f32(block, p->kernel_x);
|
||||
} else {
|
||||
val = hvx_reduce_sum_f32(block, p->kernel_x) * p->inv_kernel_area;
|
||||
}
|
||||
hvx_vec_store_u(dst_row + ox, sizeof(float), hvx_vec_splat_f32(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// General path: arbitrary kernel/stride/padding
|
||||
|
||||
// Vertical padding is uniform across a whole output row (iy0 depends only on oy, not ox),
|
||||
// so it collapses to one valid-ky range per row instead of a per-element check.
|
||||
static inline void pool_row_bounds_y(
|
||||
const struct htp_pool_2d_kernel_params * p, uint32_t oy, int32_t * iy0, uint32_t * ky_lo, uint32_t * ky_hi) {
|
||||
*iy0 = (int32_t) (oy * p->stride_y) - p->pad_y;
|
||||
const int32_t lo = -(*iy0);
|
||||
const int32_t hi = (int32_t) p->src_y - *iy0;
|
||||
*ky_lo = (uint32_t) MAX(0, lo);
|
||||
*ky_hi = (uint32_t) MAX(0, MIN((int32_t) p->kernel_y, hi));
|
||||
}
|
||||
|
||||
static inline void pool_pixel_boundary_vec(
|
||||
const float * src, float * dst_row, const struct htp_pool_2d_kernel_params * p,
|
||||
uint32_t ox, int32_t iy0, uint32_t ky_lo, uint32_t ky_hi, bool is_max) {
|
||||
const int32_t ix0 = (int32_t) (ox * p->stride_x) - p->pad_x;
|
||||
const int32_t kx_lo = MAX(0, -ix0);
|
||||
const int32_t kx_hi = MIN((int32_t) p->kernel_x, (int32_t) p->src_x - ix0);
|
||||
|
||||
if (kx_lo >= kx_hi || ky_lo >= ky_hi) {
|
||||
HVX_Vector empty_val = is_max ? hvx_vec_splat_f32(-FLT_MAX) : Q6_V_vsplat_R(0);
|
||||
hvx_vec_store_u(dst_row + ox, sizeof(float), empty_val);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t valid_kx = (uint32_t) (kx_hi - kx_lo);
|
||||
const HVX_Vector mask_identity = is_max ? hvx_vec_splat_f32(-FLT_MAX) : Q6_V_vsplat_R(0);
|
||||
|
||||
HVX_Vector acc = mask_identity;
|
||||
for (uint32_t ky = ky_lo; ky < ky_hi; ++ky) {
|
||||
const float * row = src + (uint32_t) (iy0 + (int32_t) ky) * p->src_x;
|
||||
for (uint32_t k = 0; k < valid_kx; k += VLEN_FP32) {
|
||||
const uint32_t k_rem = valid_kx - k;
|
||||
const uint32_t n = (k_rem < VLEN_FP32) ? k_rem : VLEN_FP32;
|
||||
const HVX_VectorPred q = Q6_Q_vsetq_R(n * sizeof(float));
|
||||
const HVX_Vector raw = *(const HVX_UVector *) (row + ix0 + kx_lo + k);
|
||||
const HVX_Vector v = Q6_V_vmux_QVV(q, raw, mask_identity);
|
||||
acc = is_max ? Q6_Vsf_vmax_VsfVsf(acc, v) : hvx_vec_add_f32_f32(acc, v);
|
||||
}
|
||||
}
|
||||
|
||||
HVX_Vector reduced = is_max ? hvx_vec_reduce_max_f32(acc) : hvx_vec_reduce_sum_f32(acc);
|
||||
if (!is_max) {
|
||||
HVX_Vector scale_vec;
|
||||
if (p->avg_divide_count) {
|
||||
const uint32_t count = (ky_hi - ky_lo) * valid_kx;
|
||||
scale_vec = hvx_vec_inverse_f32(hvx_vec_splat_f32((float) count));
|
||||
} else {
|
||||
scale_vec = hvx_vec_splat_f32(p->inv_kernel_area);
|
||||
}
|
||||
reduced = hvx_vec_mul_f32_f32(reduced, scale_vec);
|
||||
}
|
||||
hvx_vec_store_u(dst_row + ox, sizeof(float), reduced);
|
||||
}
|
||||
|
||||
static inline void pool_row_general_boundary_vec(
|
||||
const float * src, float * dst_row, const struct htp_pool_2d_kernel_params * p,
|
||||
uint32_t ox_start, uint32_t ox_end, int32_t iy0, uint32_t ky_lo, uint32_t ky_hi, bool is_max) {
|
||||
for (uint32_t ox = ox_start; ox < ox_end; ++ox) {
|
||||
pool_pixel_boundary_vec(src, dst_row, p, ox, iy0, ky_lo, ky_hi, is_max);
|
||||
}
|
||||
}
|
||||
|
||||
// Vectorized interior loop.
|
||||
static inline void pool_row_general_vec(
|
||||
const float * src, float * dst_row, const struct htp_pool_2d_kernel_params * p,
|
||||
uint32_t ox_start, uint32_t ox_end, int32_t iy0, uint32_t ky_lo, uint32_t ky_hi, bool is_max) {
|
||||
if (ox_start >= ox_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->stride_x != 1 && p->stride_x != 2) {
|
||||
pool_row_general_boundary_vec(src, dst_row, p, ox_start, ox_end, iy0, ky_lo, ky_hi, is_max);
|
||||
return;
|
||||
}
|
||||
|
||||
const HVX_Vector scale = hvx_vec_splat_f32(p->inv_kernel_area);
|
||||
const HVX_Vector seed = is_max ? hvx_vec_splat_f32(-FLT_MAX) : Q6_V_vsplat_R(0);
|
||||
|
||||
for (uint32_t ox = ox_start; ox < ox_end; ox += VLEN_FP32) {
|
||||
const uint32_t rem = ox_end - ox;
|
||||
const uint32_t nbytes = (rem < VLEN_FP32) ? (rem * sizeof(float)) : VLEN;
|
||||
const int32_t ix0 = (int32_t) (ox * p->stride_x) - p->pad_x;
|
||||
HVX_Vector acc = seed;
|
||||
|
||||
for (uint32_t ky = ky_lo; ky < ky_hi; ++ky) {
|
||||
const float * row = src + (uint32_t) (iy0 + (int32_t) ky) * p->src_x;
|
||||
if (p->stride_x == 1) {
|
||||
for (uint32_t kx = 0; kx < p->kernel_x; ++kx) {
|
||||
const HVX_Vector v = *(const HVX_UVector *) (row + ix0 + (int32_t) kx);
|
||||
acc = is_max ? Q6_Vsf_vmax_VsfVsf(acc, v) : hvx_vec_add_f32_f32(acc, v);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t kx = 0; kx < p->kernel_x; kx += 2) {
|
||||
const HVX_Vector v0 = *(const HVX_UVector *) (row + ix0 + (int32_t) kx);
|
||||
const HVX_Vector v1 = *(const HVX_UVector *) (row + ix0 + (int32_t) kx + VLEN_FP32);
|
||||
const HVX_VectorPair deinterleaved = Q6_W_vdeal_VVR(v1, v0, -4);
|
||||
const HVX_Vector lo = Q6_V_lo_W(deinterleaved);
|
||||
acc = is_max ? Q6_Vsf_vmax_VsfVsf(acc, lo) : hvx_vec_add_f32_f32(acc, lo);
|
||||
if (kx + 1 < p->kernel_x) {
|
||||
const HVX_Vector hi = Q6_V_hi_W(deinterleaved);
|
||||
acc = is_max ? Q6_Vsf_vmax_VsfVsf(acc, hi) : hvx_vec_add_f32_f32(acc, hi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hvx_vec_store_u(dst_row + ox, nbytes, is_max ? acc : hvx_vec_mul_f32_f32(acc, scale));
|
||||
}
|
||||
}
|
||||
|
||||
static void pool_plane_general(
|
||||
const float * src, float * dst, const struct htp_pool_2d_kernel_params * p) {
|
||||
const bool is_max = (p->pool_op == HTP_POOL_MAX);
|
||||
|
||||
for (uint32_t oy = 0; oy < p->dst_y; ++oy) {
|
||||
int32_t iy0;
|
||||
uint32_t ky_lo, ky_hi;
|
||||
pool_row_bounds_y(p, oy, &iy0, &ky_lo, &ky_hi);
|
||||
float * dst_row = dst + oy * p->dst_x;
|
||||
|
||||
pool_row_general_boundary_vec(src, dst_row, p, 0, p->ox_lo, iy0, ky_lo, ky_hi, is_max);
|
||||
pool_row_general_vec(src, dst_row, p, p->ox_lo, p->ox_hi, iy0, ky_lo, ky_hi, is_max);
|
||||
pool_row_general_boundary_vec(src, dst_row, p, p->ox_hi, p->dst_x, iy0, ky_lo, ky_hi, is_max);
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*pool_plane_fn_t)(const float * src, float * dst, const struct htp_pool_2d_kernel_params * p);
|
||||
|
||||
struct pool_2d_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_pool_2d_kernel_params * kparams;
|
||||
struct htp_pool_vtcm_layout layout;
|
||||
pool_plane_fn_t pool_plane;
|
||||
uint32_t n_threads;
|
||||
uint32_t plane_start;
|
||||
uint32_t plane_count;
|
||||
uint32_t planes_per_thread;
|
||||
};
|
||||
|
||||
static void pool_2d_thread(unsigned int nth, unsigned int ith, void * data) {
|
||||
struct pool_2d_context * ctx = (struct pool_2d_context *) data;
|
||||
const struct htp_pool_2d_kernel_params * p = ctx->kparams;
|
||||
const struct htp_pool_vtcm_layout * L = &ctx->layout;
|
||||
const struct htp_tensor * src0 = ctx->octx->src[0];
|
||||
const struct htp_tensor * dst = ctx->octx->dst;
|
||||
pool_plane_fn_t pool_plane = ctx->pool_plane;
|
||||
const uint32_t planes_per_thread = ctx->planes_per_thread;
|
||||
const uint32_t first = ctx->plane_start + ith * planes_per_thread;
|
||||
const uint32_t last = MIN(first + planes_per_thread, ctx->plane_start + ctx->plane_count);
|
||||
|
||||
if (first >= last) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &ctx->octx->ctx->trace[ith];
|
||||
dma_queue * dma_queue = ctx->octx->ctx->dma[ith];
|
||||
|
||||
const uint32_t src_plane_bytes = L->src_plane_bytes;
|
||||
const uint32_t dst_plane_bytes = L->dst_plane_bytes;
|
||||
const uint32_t src_spad_half = (uint32_t) L->src_spad_half_size;
|
||||
const uint32_t dst_spad_half = (uint32_t) L->dst_spad_half_size;
|
||||
|
||||
uint8_t * vtcm_base = (uint8_t *) ctx->octx->ctx->vtcm_base;
|
||||
uint8_t * src_spad = VTCM_LAYOUT_PTR(uint8_t, vtcm_base, L->off_src) + ith * L->src_bytes_per_thread;
|
||||
uint8_t * dst_spad = VTCM_LAYOUT_PTR(uint8_t, vtcm_base, L->off_dst) + ith * L->dst_bytes_per_thread;
|
||||
|
||||
float * srcb2[2] = { (float *) src_spad, (float *) (src_spad + src_spad_half) };
|
||||
float * dstb2[2] = { (float *) dst_spad, (float *) (dst_spad + dst_spad_half) };
|
||||
|
||||
const uint32_t total = last - first;
|
||||
|
||||
// Warm up the pipeline: push up to 2 initial (dummy dst, src) transfer pairs.
|
||||
for (uint32_t i = 0; i < total && i < 2; ++i) {
|
||||
dma_queue_push(dma_queue,
|
||||
dma_make_data(dst->data, dstb2[i]),
|
||||
dst_plane_bytes, dst_spad_half,
|
||||
dst_plane_bytes, 0);
|
||||
|
||||
const dma_addr_t src_addr = src0->data + (first + i) * src_plane_bytes;
|
||||
dma_queue_push(dma_queue,
|
||||
dma_make_data(srcb2[i], src_addr),
|
||||
src_spad_half, src_plane_bytes,
|
||||
src_plane_bytes, 1);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < total; ++i) {
|
||||
const uint32_t plane = first + i;
|
||||
const uint32_t buf = i & 1u;
|
||||
float * srcb = srcb2[buf];
|
||||
float * dstb = dstb2[buf];
|
||||
|
||||
dma_queue_pop(dma_queue); // dst writeback from plane i - 2 (or dummy on iter 0, 1)
|
||||
dma_queue_pop(dma_queue); // input for plane i
|
||||
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) plane);
|
||||
pool_plane(srcb, dstb, p);
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, (uint16_t) plane);
|
||||
|
||||
const dma_addr_t dst_addr = dst->data + plane * dst_plane_bytes;
|
||||
dma_queue_push(dma_queue,
|
||||
dma_make_data(dst_addr, dstb),
|
||||
dst_plane_bytes, dst_spad_half,
|
||||
dst_plane_bytes, 1);
|
||||
|
||||
if (i + 2 < total) {
|
||||
const dma_addr_t next_src_addr = src0->data + (plane + 2) * src_plane_bytes;
|
||||
dma_queue_push(dma_queue,
|
||||
dma_make_data(srcb, next_src_addr),
|
||||
src_spad_half, src_plane_bytes,
|
||||
src_plane_bytes, 1);
|
||||
}
|
||||
}
|
||||
|
||||
dma_queue_flush(dma_queue);
|
||||
|
||||
FARF(HIGH, "pool2d-f32-dma %d/%d: %ux%ux%ux%u -> %ux%ux%ux%u (%u:%u)\n",
|
||||
ith, nth, src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3], first, last);
|
||||
(void) nth;
|
||||
}
|
||||
|
||||
int op_pool_2d(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
const struct htp_pool_2d_kernel_params * p =
|
||||
(const struct htp_pool_2d_kernel_params *) octx->kernel_params;
|
||||
|
||||
uint32_t plane_start = 0;
|
||||
uint32_t plane_count = p->planes;
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(
|
||||
plane_count, htp_tensor_mdev_data_aligned(dst) ? p->planes_per_chunk : 0,
|
||||
octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
plane_start = range.start;
|
||||
plane_count = range.count;
|
||||
}
|
||||
if (plane_count == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = MIN(p->n_threads, plane_count);
|
||||
if (!htp_ops_context_set_n_threads(octx, n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const uint32_t planes_per_thread = fastdiv(plane_count + n_threads - 1, &octx->n_threads_div);
|
||||
|
||||
pool_plane_fn_t pool_plane;
|
||||
switch (p->kernel_type) {
|
||||
case HTP_POOL_KERNEL_GLOBAL: pool_plane = pool_plane_global; break;
|
||||
case HTP_POOL_KERNEL_BLOCK: pool_plane = pool_plane_block; break;
|
||||
case HTP_POOL_KERNEL_HVX_NARROW: pool_plane = pool_plane_hvx_narrow; break;
|
||||
case HTP_POOL_KERNEL_HVX: pool_plane = pool_plane_hvx; break;
|
||||
default: pool_plane = pool_plane_general; break;
|
||||
}
|
||||
|
||||
struct pool_2d_context ctx = {
|
||||
.octx = octx,
|
||||
.kparams = p,
|
||||
.pool_plane = pool_plane,
|
||||
.n_threads = n_threads,
|
||||
.plane_start = plane_start,
|
||||
.plane_count = plane_count,
|
||||
.planes_per_thread = planes_per_thread,
|
||||
};
|
||||
htp_pool_vtcm_layout_build(&ctx.layout, p->src_x, p->src_y, p->dst_x, p->dst_y, n_threads);
|
||||
work_queue_run(octx->ctx->work_queue, pool_2d_thread, &ctx, n_threads);
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
int op_pool_1d(struct htp_ops_context * octx) {
|
||||
return op_pool_2d(octx);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef HTP_POOL_OPS_H
|
||||
#define HTP_POOL_OPS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hex-common.h"
|
||||
#include "htp-vtcm.h"
|
||||
|
||||
enum htp_pool_kernel_type {
|
||||
HTP_POOL_KERNEL_GENERAL = 0,
|
||||
HTP_POOL_KERNEL_HVX,
|
||||
HTP_POOL_KERNEL_HVX_NARROW,
|
||||
HTP_POOL_KERNEL_GLOBAL,
|
||||
HTP_POOL_KERNEL_BLOCK,
|
||||
};
|
||||
|
||||
struct htp_pool_2d_kernel_params {
|
||||
uint8_t kernel_type;
|
||||
uint32_t src_x;
|
||||
uint32_t src_y;
|
||||
uint32_t dst_x;
|
||||
uint32_t dst_y;
|
||||
uint32_t kernel_x;
|
||||
uint32_t kernel_y;
|
||||
uint32_t stride_x;
|
||||
uint32_t stride_y;
|
||||
int32_t pad_x;
|
||||
int32_t pad_y;
|
||||
uint32_t n_threads;
|
||||
uint32_t planes;
|
||||
uint32_t planes_per_chunk;
|
||||
uint32_t pool_op;
|
||||
uint32_t avg_divide_count;
|
||||
uint32_t ox_lo;
|
||||
uint32_t ox_hi;
|
||||
float inv_kernel_area;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_pool_2d_kernel_params) <= 128, "htp_pool_2d_kernel_params is too large");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_pool_2d_kernel_params) <= 128, "htp_pool_2d_kernel_params is too large");
|
||||
#endif
|
||||
|
||||
struct htp_pool_vtcm_layout {
|
||||
size_t total_bytes;
|
||||
size_t off_src;
|
||||
size_t off_dst;
|
||||
uint32_t src_plane_bytes;
|
||||
uint32_t dst_plane_bytes;
|
||||
size_t src_bytes_per_thread;
|
||||
size_t dst_bytes_per_thread;
|
||||
size_t src_spad_half_size;
|
||||
size_t dst_spad_half_size;
|
||||
};
|
||||
|
||||
static inline void htp_pool_vtcm_layout_build(
|
||||
struct htp_pool_vtcm_layout * L,
|
||||
uint32_t src_x,
|
||||
uint32_t src_y,
|
||||
uint32_t dst_x,
|
||||
uint32_t dst_y,
|
||||
uint32_t n_threads
|
||||
) {
|
||||
// Full-plane double buffering (256 bytes guard space for vector loads)
|
||||
L->src_plane_bytes = src_x * src_y * sizeof(float);
|
||||
L->dst_plane_bytes = dst_x * dst_y * sizeof(float);
|
||||
L->src_spad_half_size = hex_round_up(L->src_plane_bytes + 256, 128);
|
||||
L->dst_spad_half_size = hex_round_up(L->dst_plane_bytes, 128);
|
||||
L->src_bytes_per_thread = 2 * L->src_spad_half_size;
|
||||
L->dst_bytes_per_thread = 2 * L->dst_spad_half_size;
|
||||
|
||||
size_t off = 0;
|
||||
VTCM_LAYOUT_ALLOC(off, off_src, L->src_bytes_per_thread * n_threads);
|
||||
VTCM_LAYOUT_ALLOC(off, off_dst, L->dst_bytes_per_thread * n_threads);
|
||||
L->total_bytes = off;
|
||||
}
|
||||
|
||||
static inline bool htp_pool_solve_layout(
|
||||
struct htp_pool_vtcm_layout * layout,
|
||||
uint32_t src_x,
|
||||
uint32_t src_y,
|
||||
uint32_t dst_x,
|
||||
uint32_t dst_y,
|
||||
uint32_t n_threads,
|
||||
size_t vtcm_budget
|
||||
) {
|
||||
htp_pool_vtcm_layout_build(layout, src_x, src_y, dst_x, dst_y, n_threads);
|
||||
return layout->total_bytes <= vtcm_budget;
|
||||
}
|
||||
|
||||
#endif // HTP_POOL_OPS_H
|
||||
@@ -0,0 +1,163 @@
|
||||
#pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||||
|
||||
#include <HAP_farf.h>
|
||||
#include <HAP_perf.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "hvx-utils.h"
|
||||
|
||||
#define GGML_COMMON_DECL_C
|
||||
#include "ggml-common.h"
|
||||
#include "htp-ctx.h"
|
||||
#include "hex-common.h"
|
||||
#include "hex-profile.h"
|
||||
#include "htp-ops.h"
|
||||
#include "htp-tensor.h"
|
||||
#include "repeat-ops.h"
|
||||
|
||||
struct htp_repeat_context {
|
||||
struct htp_ops_context * octx;
|
||||
const struct htp_repeat_kernel_params * kparams;
|
||||
|
||||
uint32_t nrows_per_thread;
|
||||
uint32_t total_dst_rows; // ne1 * ne2 * ne3
|
||||
uint32_t row_start;
|
||||
|
||||
size_t type_size;
|
||||
};
|
||||
|
||||
static void repeat_job_per_thread(unsigned int nth, unsigned int ith, void * data) {
|
||||
const struct htp_repeat_context * rctx = (const struct htp_repeat_context *) data;
|
||||
struct htp_ops_context * octx = rctx->octx;
|
||||
const struct htp_repeat_kernel_params * kparams = rctx->kparams;
|
||||
const struct htp_tensor * src = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
const uint32_t ne00 = src->ne[0];
|
||||
const uint32_t ne01 = src->ne[1];
|
||||
const uint32_t ne02 = src->ne[2];
|
||||
const uint32_t ne03 = src->ne[3];
|
||||
|
||||
const uint32_t nb00 = src->nb[0];
|
||||
const uint32_t nb01 = src->nb[1];
|
||||
const uint32_t nb02 = src->nb[2];
|
||||
const uint32_t nb03 = src->nb[3];
|
||||
|
||||
const uint32_t ne0 = dst->ne[0];
|
||||
const uint32_t ne1 = dst->ne[1];
|
||||
const uint32_t ne2 = dst->ne[2];
|
||||
const uint32_t ne3 = dst->ne[3];
|
||||
|
||||
const uint32_t nb0 = dst->nb[0];
|
||||
const uint32_t nb1 = dst->nb[1];
|
||||
const uint32_t nb2 = dst->nb[2];
|
||||
const uint32_t nb3 = dst->nb[3];
|
||||
|
||||
const uint32_t nr0 = kparams->nr0;
|
||||
|
||||
const size_t row_bytes = ne00 * rctx->type_size;
|
||||
|
||||
const uint32_t row_start = rctx->row_start + rctx->nrows_per_thread * ith;
|
||||
const uint32_t row_end = MIN(row_start + rctx->nrows_per_thread, rctx->row_start + rctx->total_dst_rows);
|
||||
if (row_start >= row_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct htp_thread_trace * tr = &octx->ctx->trace[ith];
|
||||
htp_trace_event_start(tr, HTP_TRACE_EVT_HVX_COMP, row_start);
|
||||
|
||||
for (uint32_t dst_row = row_start; dst_row < row_end; dst_row++) {
|
||||
// Decompose flat dst row index into (i1, i2, i3)
|
||||
const uint32_t dst_row_div_ne1 = fastdiv(dst_row, &kparams->div_ne1);
|
||||
const uint32_t i1 = dst_row - dst_row_div_ne1 * ne1;
|
||||
const uint32_t i3 = fastdiv(dst_row, &kparams->div_ne12);
|
||||
const uint32_t i2 = dst_row_div_ne1 - i3 * ne2;
|
||||
|
||||
// Map to source indices (tiling)
|
||||
const uint32_t k1 = fastmodulo(i1, ne01, &kparams->div_ne01);
|
||||
const uint32_t k2 = fastmodulo(i2, ne02, &kparams->div_ne02);
|
||||
const uint32_t k3 = fastmodulo(i3, ne03, &kparams->div_ne03);
|
||||
|
||||
const uint8_t * src_row = (const uint8_t *) src->data + k1 * nb01 + k2 * nb02 + k3 * nb03;
|
||||
uint8_t * dst_base = (uint8_t *) dst->data + i1 * nb1 + i2 * nb2 + i3 * nb3;
|
||||
|
||||
// Tile along dimension 0
|
||||
for (uint32_t i0 = 0; i0 < nr0; i0++) {
|
||||
uint8_t * dst_ptr = dst_base + i0 * ne00 * nb0;
|
||||
memcpy(dst_ptr, src_row, row_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
htp_trace_event_stop(tr, HTP_TRACE_EVT_HVX_COMP, row_start);
|
||||
|
||||
FARF(HIGH, "repeat %d/%d: (%ux%ux%ux%u) -> (%ux%ux%ux%u) rows %u:%u\n",
|
||||
ith, nth, src->ne[0], src->ne[1], src->ne[2], src->ne[3],
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
row_start, row_end);
|
||||
}
|
||||
|
||||
int op_repeat(struct htp_ops_context * octx) {
|
||||
const struct htp_tensor * src0 = octx->src[0];
|
||||
const struct htp_tensor * dst = octx->dst;
|
||||
|
||||
size_t type_size;
|
||||
switch (src0->type) {
|
||||
case HTP_TYPE_F32: type_size = 4; break;
|
||||
case HTP_TYPE_F16: type_size = 2; break;
|
||||
default:
|
||||
FARF(ERROR, "repeat: unsupported type %u\n", src0->type);
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
if (htp_tensor_is_extended(src0) || htp_tensor_is_extended(dst)) {
|
||||
return HTP_STATUS_NO_SUPPORT;
|
||||
}
|
||||
|
||||
const struct htp_repeat_kernel_params * kparams =
|
||||
(const struct htp_repeat_kernel_params *) octx->kernel_params;
|
||||
|
||||
if (!htp_ops_context_set_n_threads(octx, kparams->n_threads)) {
|
||||
return HTP_STATUS_INVAL_PARAMS;
|
||||
}
|
||||
|
||||
const uint32_t total_dst_rows = dst->ne[1] * dst->ne[2] * dst->ne[3];
|
||||
const size_t dst_row_size = dst->ne[0] * type_size;
|
||||
|
||||
uint32_t row_start = 0;
|
||||
uint32_t nrows = total_dst_rows;
|
||||
|
||||
if (octx->ctx->mdev.count > 1) {
|
||||
uint32_t rows_per_chunk = 0;
|
||||
htp_tensor_mdev_rows_per_chunk(dst, type_size, (uint32_t) dst_row_size, &rows_per_chunk);
|
||||
const struct htp_tensor_mdev_range range = htp_tensor_mdev_partition(total_dst_rows, rows_per_chunk, octx->ctx->mdev.idx, octx->ctx->mdev.count, &octx->ctx->mdev.count_div);
|
||||
row_start = range.start;
|
||||
nrows = range.count;
|
||||
}
|
||||
|
||||
if (nrows == 0) {
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
|
||||
const uint32_t n_threads = octx->n_threads;
|
||||
|
||||
struct htp_repeat_context rctx = {
|
||||
.octx = octx,
|
||||
.kparams = kparams,
|
||||
.nrows_per_thread = fastdiv(nrows + n_threads - 1, &octx->n_threads_div),
|
||||
.total_dst_rows = nrows,
|
||||
.row_start = row_start,
|
||||
.type_size = type_size,
|
||||
};
|
||||
|
||||
FARF(HIGH, "repeat: (%ux%ux%ux%u) -> (%ux%ux%ux%u) nr=(%u,%u,%u,%u)\n",
|
||||
src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3],
|
||||
dst->ne[0], dst->ne[1], dst->ne[2], dst->ne[3],
|
||||
kparams->nr0, kparams->nr1, kparams->nr2, kparams->nr3);
|
||||
|
||||
work_queue_run(octx->ctx->work_queue, repeat_job_per_thread, &rctx, n_threads);
|
||||
|
||||
return HTP_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef HTP_REPEAT_OPS_H
|
||||
#define HTP_REPEAT_OPS_H
|
||||
|
||||
#include "hex-fastdiv.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct htp_repeat_kernel_params {
|
||||
uint32_t n_threads;
|
||||
uint32_t nr0;
|
||||
uint32_t nr1;
|
||||
uint32_t nr2;
|
||||
uint32_t nr3;
|
||||
|
||||
struct fastdiv_values div_ne1;
|
||||
struct fastdiv_values div_ne12;
|
||||
struct fastdiv_values div_ne01;
|
||||
struct fastdiv_values div_ne02;
|
||||
struct fastdiv_values div_ne03;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
static_assert(sizeof(struct htp_repeat_kernel_params) <= 128, "htp_repeat_kernel_params is too large for kernel_params blob");
|
||||
#else
|
||||
_Static_assert(sizeof(struct htp_repeat_kernel_params) <= 128, "htp_repeat_kernel_params is too large for kernel_params blob");
|
||||
#endif
|
||||
|
||||
#endif // HTP_REPEAT_OPS_H
|
||||
Loaded 100 of 3918 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user