merge C base name prefix to nfn

This commit is contained in:
2026-09-27 00:59:42 +08:00
parent 9fc23c29fb
commit e24efd9f9c
52 changed files with 1002 additions and 1142 deletions
+42
View File
@@ -0,0 +1,42 @@
#include "NitloadFrameWork.h"
#include "nfn_intrinsic.matkernel.h"
#include <stdint.h>
#ifdef _WIN32
/* Define and initialize a global zero kernel aligned to 64 bytes for universal ISA usage */
__declspec(align(64)) MAT_KERNEL(u64) NFN_ZERO_KERNEL;
static void init_zero_kernel(void)
{
for (int c = 0; c < 4; ++c) {
for (int r = 0; r < 4; ++r) {
NFN_ZERO_KERNEL.m[r][c] = 0;
}
}
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
init_zero_kernel();
return 1;
case DLL_THREAD_ATTACH:
return 1;
case DLL_THREAD_DETACH:
return 1;
case DLL_PROCESS_DETACH:
return 1;
break;
}
return TRUE;
}
#else
attribute((constructor)) void DllMain()
{
init_zero_kernel();
}
#endif // _WIN32
+121
View File
@@ -0,0 +1,121 @@
#include "NitloadFrameWork.h"
NFN_API void NFN_DECL nfn_load_glfw_functions(GLFWFunctionTable *table)
{
table->glfwCreateCursor = glfwCreateCursor;
table->glfwCreateStandardCursor = glfwCreateStandardCursor;
table->glfwCreateWindow = glfwCreateWindow;
table->glfwDefaultWindowHints = glfwDefaultWindowHints;
table->glfwDestroyCursor = glfwDestroyCursor;
table->glfwDestroyWindow = glfwDestroyWindow;
table->glfwExtensionSupported = glfwExtensionSupported;
table->glfwFocusWindow = glfwFocusWindow;
table->glfwGetClipboardString = glfwGetClipboardString;
table->glfwGetCurrentContext = glfwGetCurrentContext;
table->glfwGetCursorPos = glfwGetCursorPos;
table->glfwGetError = glfwGetError;
table->glfwGetFrameBufferSize = glfwGetFramebufferSize;
table->glfwGetGamepadName = glfwGetGamepadName;
table->glfwGetGamepadState = glfwGetGamepadState;
table->glfwGetGammaRamp = glfwGetGammaRamp;
table->glfwGetInputMode = glfwGetInputMode;
table->glfwGetJoystickAxes = glfwGetJoystickAxes;
table->glfwGetJoystickButtons = glfwGetJoystickButtons;
table->glfwGetJoystickGUID = glfwGetJoystickGUID;
table->glfwGetJoystickHats = glfwGetJoystickHats;
table->glfwGetJoystickName = glfwGetJoystickName;
table->glfwGetJoystickUserPointer = glfwGetJoystickUserPointer;
table->glfwGetKey = glfwGetKey;
table->glfwGetKeyName = glfwGetKeyName;
table->glfwGetKeyScancode = glfwGetKeyScancode;
table->glfwGetMonitorContentScale = glfwGetMonitorContentScale;
table->glfwGetMonitorName = glfwGetMonitorName;
table->glfwGetMonitorPhysicalSize = glfwGetMonitorPhysicalSize;
table->glfwGetMonitorPos = glfwGetMonitorPos;
table->glfwGetMonitors = glfwGetMonitors;
table->glfwGetMonitorUserPointer = glfwGetMonitorUserPointer;
table->glfwGetMonitorWorkarea = glfwGetMonitorWorkarea;
table->glfwGetMouseButton = glfwGetMouseButton;
table->glfwGetPrimaryMonitor = glfwGetPrimaryMonitor;
table->glfwGetProcAddress = glfwGetProcAddress;
table->glfwGetRequiredInstanceExtensions = glfwGetRequiredInstanceExtensions;
table->glfwGetTime = glfwGetTime;
table->glfwGetTimerFrequency = glfwGetTimerFrequency;
table->glfwGetTimerValue = glfwGetTimerValue;
table->glfwGetVersion = glfwGetVersion;
table->glfwGetVersionString = glfwGetVersionString;
table->glfwGetVideoMode = glfwGetVideoMode;
table->glfwGetVideoModes = glfwGetVideoModes;
table->glfwGetWindowAttrib = glfwGetWindowAttrib;
table->glfwGetWindowContentScale = glfwGetWindowContentScale;
table->glfwGetWindowFrameSize = glfwGetWindowFrameSize;
table->glfwGetWindowMonitor = glfwGetWindowMonitor;
table->glfwGetWindowOpacity = glfwGetWindowOpacity;
table->glfwGetWindowPos = glfwGetWindowPos;
table->glfwGetWindowSize = glfwGetWindowSize;
table->glfwGetWindowUserPointer = glfwGetWindowUserPointer;
table->glfwHideWindow = glfwHideWindow;
table->glfwIconifyWindow = glfwIconifyWindow;
table->glfwInit = glfwInit;
table->glfwInitHint = glfwInitHint;
table->glfwJoystickIsGamePad = glfwJoystickIsGamepad;
table->glfwJoystickPresent = glfwJoystickPresent;
table->glfwMakeContextCurrent = glfwMakeContextCurrent;
table->glfwMaximizeWindow = glfwMaximizeWindow;
table->glfwPollEvents = glfwPollEvents;
table->glfwPostEmptyEvent = glfwPostEmptyEvent;
table->glfwRawMouseMotionSupported = glfwRawMouseMotionSupported;
table->glfwRequestWindowAttention = glfwRequestWindowAttention;
table->glfwRestoreWindow = glfwRestoreWindow;
table->glfwSetCharCallback = glfwSetCharCallback;
table->glfwSetCharModsCallback = glfwSetCharModsCallback;
table->glfwSetClipboardString = glfwSetClipboardString;
table->glfwSetCursor = glfwSetCursor;
table->glfwSetCursorEnterCallback = glfwSetCursorEnterCallback;
table->glfwSetCursorPos = glfwSetCursorPos;
table->glfwSetCursorPosCallback = glfwSetCursorPosCallback;
table->glfwSetDropCallback = glfwSetDropCallback;
table->glfwSetErrorCallback = glfwSetErrorCallback;
table->glfwSetFramebufferSizeCallback = glfwSetFramebufferSizeCallback;
table->glfwSetGamma = glfwSetGamma;
table->glfwSetGammaRamp = glfwSetGammaRamp;
table->glfwSetInputMode = glfwSetInputMode;
table->glfwSetJoystickCallback = glfwSetJoystickCallback;
table->glfwSetJoystickUserPointer = glfwSetJoystickUserPointer;
table->glfwSetKeyCallback = glfwSetKeyCallback;
table->glfwSetMonitorCallback = glfwSetMonitorCallback;
table->glfwSetMonitorUserPointer = glfwSetMonitorUserPointer;
table->glfwSetMouseButtonCallback = glfwSetMouseButtonCallback;
table->glfwSetScrollCallback = glfwSetScrollCallback;
table->glfwSetTime = glfwSetTime;
table->glfwSetWindowAspectRatio = glfwSetWindowAspectRatio;
table->glfwSetWindowAttrib = glfwSetWindowAttrib;
table->glfwSetWindowCloseCallback = glfwSetWindowCloseCallback;
table->glfwSetWindowContentScaleCallback = glfwSetWindowContentScaleCallback;
table->glfwSetWindowFocusCallback = glfwSetWindowFocusCallback;
table->glfwSetWindowIcon = glfwSetWindowIcon;
table->glfwSetWindowIconifyCallback = glfwSetWindowIconifyCallback;
table->glfwSetWindowMaximizeCallback = glfwSetWindowMaximizeCallback;
table->glfwSetWindowMonitor = glfwSetWindowMonitor;
table->glfwSetWindowOpacity = glfwSetWindowOpacity;
table->glfwSetWindowPos = glfwSetWindowPos;
table->glfwSetWindowPosCallback = glfwSetWindowPosCallback;
table->glfwSetWindowRefreshCallback = glfwSetWindowRefreshCallback;
table->glfwSetWindowShouldClose = glfwSetWindowShouldClose;
table->glfwSetWindowSize = glfwSetWindowSize;
table->glfwSetWindowSizeCallback = glfwSetWindowSizeCallback;
table->glfwSetWindowSizeLimits = glfwSetWindowSizeLimits;
table->glfwSetWindowTitle = glfwSetWindowTitle;
table->glfwSetWindowUserPointer = glfwSetWindowUserPointer;
table->glfwShowWindow = glfwShowWindow;
table->glfwSwapBuffers = glfwSwapBuffers;
table->glfwSwapInterval = glfwSwapInterval;
table->glfwTerminate = glfwTerminate;
table->glfwUpdateGamePadMappings = glfwUpdateGamepadMappings;
table->glfwVulkanSupported = glfwVulkanSupported;
table->glfwWaitEvents = glfwWaitEvents;
table->glfwWaitEventsTimeout = glfwWaitEventsTimeout;
table->glfwWindowHint = glfwWindowHint;
table->glfwWindowHintString = glfwWindowHintString;
table->glfwWindowShouldClose = glfwWindowShouldClose;
}
@@ -0,0 +1,118 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "NitloadFrameWork.h"
#include "nfn_intrinsic.h"
#include "nfn_intrinsic.std.h"
#include "nfn_intrinsic.context.x86.h"
PRIVATE void load_intrinsic_sse(nfn_intrinsic *context, SIMDID id);
PRIVATE void load_intrinsic_avx(nfn_intrinsic *context, SIMDID id);
PRIVATE void load_intrinsic_512(nfn_intrinsic *context, SIMDID id);
NFN_API void NFN_DECL nfn_load_intrinsic_function(void *intrinsic_context, uint64_t id)
{
nfn_null_assert(intrinsic_context);
SIMDID simdid;
if (id) {
simdid.mask = id;
} else {
simdid = nfn_get_simd_level_global();
}
nfn_intrinsic *context = (nfn_intrinsic *)intrinsic_context;
switch (simdid.make_byte[1]) {
case SIMDID_MAIN_LVL_SSE:
load_intrinsic_sse(context, simdid);
break;
case SIMDID_MAIN_LVL_AVX2:
load_intrinsic_avx(context, simdid);
break;
case SIMDID_MAIN_LVL_AVX512:
case SIMDID_MAIN_LVL_AVX10:
load_intrinsic_512(context, simdid);
break;
}
}
void load_intrinsic_sse(nfn_intrinsic *context, SIMDID id)
{
context->city_hash_simplify = city_hash_simplify_sse;
context->factorial_simd = factorial_simd_sse;
context->mat_mk[F32].proc_array[Add] = GET_MK_PROC_STD(float, sse, , add);
context->mat_mk[F32].proc_array[Fill] = GET_MK_PROC_STD(float, sse, , fill);
context->mat_mk[F32].proc_array[ScalarMul] = GET_MK_PROC_STD(float, sse, , scalar_mul);
context->mat_mk[F32].proc_array[Sub] = GET_MK_PROC_STD(float, sse, , sub);
context->mat_mk[F32].proc_array[Transpose] = GET_MK_PROC_STD(float, sse, , transpose);
context->mat_mk[F32].proc_array[Fma] = GET_MK_PROC_STD(float, sse, , fma);
context->mat_mk[F32].proc_array[Pivot] = NULL;
context->mat_mk[F64].proc_array[Add] = GET_MK_PROC_STD(double, sse, , add);
context->mat_mk[F64].proc_array[Fill] = GET_MK_PROC_STD(double, sse, , fill);
context->mat_mk[F64].proc_array[ScalarMul] = GET_MK_PROC_STD(double, sse, , scalar_mul);
context->mat_mk[F64].proc_array[Sub] = GET_MK_PROC_STD(double, sse, , sub);
context->mat_mk[F64].proc_array[Transpose] = NULL;
context->mat_mk[F64].proc_array[Fma] = GET_MK_PROC_STD(double, sse, , fma);
context->mat_mk[F64].proc_array[Pivot] = NULL;
context->vec_f32_normalize_3 = f32_normalize;
}
void load_intrinsic_avx(nfn_intrinsic *context, SIMDID id)
{
char avx_seg = id.make_byte[4];
char is_fma = (avx_seg & SIMDID_AVX_FMA) != 0;
context->city_hash_simplify = city_hash_simplify_sse;
context->factorial_simd = factorial_simd_sse;
context->mat_mk[F32].proc_array[Add] = GET_MK_PROC_STD(float, avx, , add);
context->mat_mk[F32].proc_array[Fill] = GET_MK_PROC_STD(float, avx, , fill);
context->mat_mk[F32].proc_array[ScalarMul] = GET_MK_PROC_STD(float, avx, , scalar_mul);
context->mat_mk[F32].proc_array[Sub] = GET_MK_PROC_STD(float, avx, , sub);
context->mat_mk[F32].proc_array[Transpose] = GET_MK_PROC_STD(float, avx, , transpose);
context->mat_mk[F32].proc_array[Fma] = is_fma ? GET_MK_PROC_STD(float, avx, _fma, fma) :
GET_MK_PROC_STD(float, avx, , fma);
context->mat_mk[F32].proc_array[Pivot] = NULL;
context->mat_mk[F64].proc_array[Add] = GET_MK_PROC_STD(double, avx, , add);
context->mat_mk[F64].proc_array[Fill] = GET_MK_PROC_STD(double, avx, , fill);
context->mat_mk[F64].proc_array[ScalarMul] = GET_MK_PROC_STD(double, avx, , scalar_mul);
context->mat_mk[F64].proc_array[Sub] = GET_MK_PROC_STD(double, avx, , sub);
context->mat_mk[F64].proc_array[Transpose] = NULL;
context->mat_mk[F64].proc_array[Fma] = is_fma ? GET_MK_PROC_STD(double, avx, _fma, fma) :
GET_MK_PROC_STD(double, avx, , fma);
context->mat_mk[F64].proc_array[Pivot] = NULL;
context->vec_f32_normalize_3 = f32_normalize;
}
void load_intrinsic_512(nfn_intrinsic *context, SIMDID id)
{
context->city_hash_simplify = city_hash_simplify_sse;
context->factorial_simd = factorial_simd_sse;
context->mat_mk[F32].proc_array[Add] = GET_MK_PROC_STD(float, 512, , add);
context->mat_mk[F32].proc_array[Fill] = GET_MK_PROC_STD(float, 512, , fill);
context->mat_mk[F32].proc_array[ScalarMul] = GET_MK_PROC_STD(float, 512, , scalar_mul);
context->mat_mk[F32].proc_array[Sub] = GET_MK_PROC_STD(float, 512, , sub);
context->mat_mk[F32].proc_array[Transpose] = GET_MK_PROC_STD(float, 512, , transpose);
context->mat_mk[F32].proc_array[Fma] = GET_MK_PROC_STD(float, 512, , fma);
context->mat_mk[F32].proc_array[Pivot] = NULL;
context->mat_mk[F64].proc_array[Add] = GET_MK_PROC_STD(double, 512, , add);
context->mat_mk[F64].proc_array[Fill] = GET_MK_PROC_STD(double, 512, , fill);
context->mat_mk[F64].proc_array[ScalarMul] = GET_MK_PROC_STD(double, 512, , scalar_mul);
context->mat_mk[F64].proc_array[Sub] = GET_MK_PROC_STD(double, 512, , sub);
context->mat_mk[F64].proc_array[Transpose] = NULL;
context->mat_mk[F64].proc_array[Fma] = GET_MK_PROC_STD(double, 512, , fma);
context->mat_mk[F64].proc_array[Pivot] = NULL;
context->vec_f32_normalize_3 = f32_normalize;
}
#endif
+369
View File
@@ -0,0 +1,369 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include <immintrin.h>
#include "nfn_intrinsic.h"
#include "nfn_intrinsic.std.h"
#include "nfn_intrinsic.context.x86.h"
NFN_MK_PROC_DECL_STD(float, sse, , fill)
{
__mk_param_std(ANS_SCALAR, float);
register __m128 vec = _mm_set_ps1(scalar.f32[0]);
for (size_t i = 0; i < count_2; i++) {
ans[i].x[0] = vec;
ans[i].x[1] = vec;
ans[i].x[2] = vec;
ans[i].x[3] = vec;
}
}
NFN_MK_PROC_DECL_STD(float, avx, , fill)
{
__mk_param_std(ANS_SCALAR, float);
register __m256 vec = _mm256_broadcast_ss(&scalar.f32[0]);
for (size_t i = 0; i < count_2; i++) {
ans[i].y[0] = vec;
ans[i].y[1] = vec;
}
}
NFN_MK_PROC_DECL_STD(float, 512, , fill)
{
__mk_param_std(ANS_SCALAR, float);
register __m512 vec = _mm512_broadcastss_ps(scalar.m128);
for (size_t i = 0; i < count_2; i++) {
ans[i].z[0] = vec;
}
}
NFN_MK_PROC_DECL_STD(double, sse, , fill)
{
__mk_param_std(ANS_SCALAR, double);
register __m128d vec_d = _mm_set1_pd(scalar.f64[0]);
register __m128 vec = _mm_castpd_ps(vec_d);
for (size_t i = 0; i < count_2; i++) {
ans[i].x[0] = vec;
ans[i].x[1] = vec;
ans[i].x[2] = vec;
ans[i].x[3] = vec;
ans[i].x[4] = vec;
ans[i].x[5] = vec;
ans[i].x[6] = vec;
ans[i].x[7] = vec;
}
}
NFN_MK_PROC_DECL_STD(double, avx, , fill)
{
__mk_param_std(ANS_SCALAR, double);
register __m256d vec = _mm256_broadcast_sd(&scalar.f64[0]);
for (size_t i = 0; i < count_2; i++) {
ans[i].y[0] = _mm256_castpd_ps(vec);
ans[i].y[1] = _mm256_castpd_ps( vec);
ans[i].y[2] = _mm256_castpd_ps( vec);
ans[i].y[3] = _mm256_castpd_ps( vec);
}
}
NFN_MK_PROC_DECL_STD(double, 512, , fill)
{
__mk_param_std(ANS_SCALAR, double);
register __m512d vec = _mm512_set1_pd(scalar.f64[0]);
for (size_t i = 0; i < count_2; i++) {
ans[i].z[0] = _mm512_castpd_ps(vec);
ans[i].z[1] = _mm512_castpd_ps(vec);
}
}
NFN_MK_PROC_DECL_STD(float, sse, , add)
{
__mk_param_std(LEFT_RIGHT_ANS, float);
for (size_t i = 0; i < count_0; i++) {
register __m128 c0 = _mm_add_ps(left[i].x[0], right[i].x[0]);
register __m128 c1 = _mm_add_ps(left[i].x[1], right[i].x[1]);
register __m128 c2 = _mm_add_ps(left[i].x[2], right[i].x[2]);
register __m128 c3 = _mm_add_ps(left[i].x[3], right[i].x[3]);
ans[i].x[0] = c0;
ans[i].x[1] = c1;
ans[i].x[2] = c2;
ans[i].x[3] = c3;
}
}
NFN_MK_PROC_DECL_STD(float, avx, , add)
{
__mk_param_std(LEFT_RIGHT_ANS, float);
for (size_t i = 0; i < count_0; i++) {
size_t s = sizeof(MAT_KERNEL(float));
register __m256 c0 = _mm256_add_ps(left[i].y[0], right[i].y[0]);
register __m256 c1 = _mm256_add_ps(left[i].y[1], right[i].y[1]);
ans[i].y[0] = c0;
ans[i].y[1] = c1;
}
}
NFN_MK_PROC_DECL_STD(float, 512, , add)
{
__mk_param_std(LEFT_RIGHT_ANS, float);
for (size_t i = 0; i < count_0; i++) {
register __m512 c0 = _mm512_add_ps(left[i].z[0], right[i].z[0]);
ans[i].z[0] = c0;
}
}
NFN_MK_PROC_DECL_STD(double, sse, , add)
{
__mk_param_std( LEFT_RIGHT_ANS, double);
for (size_t i = 0; i < count_0; i++) {
register __m128d c0 = _mm_add_pd(_mm_castps_pd(left[i].x[0]),
_mm_castps_pd(right[i].x[0]));
register __m128d c1 = _mm_add_pd(_mm_castps_pd(left[i].x[1]),
_mm_castps_pd(right[i].x[1]));
register __m128d c2 = _mm_add_pd(_mm_castps_pd(left[i].x[2]),
_mm_castps_pd(right[i].x[2]));
register __m128d c3 = _mm_add_pd(_mm_castps_pd(left[i].x[3]),
_mm_castps_pd(right[i].x[3]));
register __m128d c4 = _mm_add_pd(_mm_castps_pd(left[i].x[4]),
_mm_castps_pd(right[i].x[4]));
register __m128d c5 = _mm_add_pd(_mm_castps_pd(left[i].x[5]),
_mm_castps_pd(right[i].x[5]));
register __m128d c6 = _mm_add_pd(_mm_castps_pd(left[i].x[6]),
_mm_castps_pd(right[i].x[6]));
register __m128d c7 = _mm_add_pd(_mm_castps_pd(left[i].x[7]),
_mm_castps_pd(right[i].x[7]));
ans[i].x[0] = _mm_castpd_ps(c0);
ans[i].x[1] = _mm_castpd_ps(c1);
ans[i].x[2] = _mm_castpd_ps(c2);
ans[i].x[3] = _mm_castpd_ps(c3);
ans[i].x[4] = _mm_castpd_ps(c4);
ans[i].x[5] = _mm_castpd_ps(c5);
ans[i].x[6] = _mm_castpd_ps(c6);
ans[i].x[7] = _mm_castpd_ps(c7);
}
}
NFN_MK_PROC_DECL_STD(double, avx, , add)
{
__mk_param_std( LEFT_RIGHT_ANS, double);
for (size_t i = 0; i < count_0; i++) {
size_t s = sizeof(MAT_KERNEL(float));
register __m256d c0 = _mm256_add_pd(left[i].y[0], right[i].y[0]);
register __m256d c1 = _mm256_add_pd(left[i].y[1], right[i].y[1]);
register __m256d c2 = _mm256_add_pd(left[i].y[0], right[i].y[0]);
register __m256d c3 = _mm256_add_pd(left[i].y[1], right[i].y[1]);
ans[i].y[0] = c0;
ans[i].y[1] = c1;
ans[i].y[2] = c2;
ans[i].y[3] = c3;
}
}
NFN_MK_PROC_DECL_STD(double, 512, , add)
{
__mk_param_std( LEFT_RIGHT_ANS, double);
for (size_t i = 0; i < count_0; i++) {
register __m512d c0 = _mm512_add_pd(_mm512_castps_pd( left[i].z[0]), _mm512_castps_pd( right[i].z[0]));
register __m512d c1 = _mm512_add_pd(_mm512_castps_pd( left[i].z[1]), _mm512_castps_pd( right[i].z[1]));
ans[i].z[0] = c0;
ans[i].z[1] = c1;
}
}
NFN_MK_PROC_DECL_STD(float, sse, , sub)
{
__mk_param_std( LEFT_RIGHT_ANS, float);
for (size_t i = 0; i < count_0; i++) {
register __m128 c0 = _mm_sub_ps(left[i].x[0], right[i].x[0]);
register __m128 c1 = _mm_sub_ps(left[i].x[1], right[i].x[1]);
register __m128 c2 = _mm_sub_ps(left[i].x[2], right[i].x[2]);
register __m128 c3 = _mm_sub_ps(left[i].x[3], right[i].x[3]);
ans[i].x[0] = c0;
ans[i].x[1] = c1;
ans[i].x[2] = c2;
ans[i].x[3] = c3;
}
}
NFN_MK_PROC_DECL_STD(float, avx, , sub)
{
__mk_param_std( LEFT_RIGHT_ANS, float);
for (size_t i = 0; i < count_0; i++) {
register __m256 c0 = _mm256_sub_ps(left[i].y[0], right[i].y[0]);
register __m256 c1 = _mm256_sub_ps(left[i].y[1], right[i].y[1]);
ans[i].y[0] = c0;
ans[i].y[1] = c1;
}
}
NFN_MK_PROC_DECL_STD(float, 512, , sub)
{
__mk_param_std( LEFT_RIGHT_ANS, float);
for (size_t i = 0; i < count_0; i++) {
register __m512 c0 = _mm512_sub_ps(left[i].z[0], right[i].z[0]);
ans[i].z[0] = c0;
}
}
NFN_MK_PROC_DECL_STD(double, sse, , sub)
{
__mk_param_std( LEFT_RIGHT_ANS, double);
for (size_t i = 0; i < count_0; i++) {
register __m128d c0 = _mm_sub_pd(left[i].x[0], right[i].x[0]);
register __m128d c1 = _mm_sub_pd(left[i].x[1], right[i].x[1]);
register __m128d c2 = _mm_sub_pd(left[i].x[2], right[i].x[2]);
register __m128d c3 = _mm_sub_pd(left[i].x[3], right[i].x[3]);
register __m128d c4 = _mm_sub_pd(left[i].x[4], right[i].x[4]);
register __m128d c5 = _mm_sub_pd(left[i].x[5], right[i].x[5]);
register __m128d c6 = _mm_sub_pd(left[i].x[6], right[i].x[6]);
register __m128d c7 = _mm_sub_pd(left[i].x[7], right[i].x[7]);
ans[i].x[0] = c0;
ans[i].x[1] = c1;
ans[i].x[2] = c2;
ans[i].x[3] = c3;
ans[i].x[4] = c4;
ans[i].x[5] = c5;
ans[i].x[6] = c6;
ans[i].x[7] = c7;
}
}
NFN_MK_PROC_DECL_STD(double, avx, , sub)
{
__mk_param_std( LEFT_RIGHT_ANS, double);
for (size_t i = 0; i < count_0; i++) {
register __m256 c0 = _mm256_sub_ps(left[i].y[0], right[i].y[0]);
register __m256 c1 = _mm256_sub_ps(left[i].y[1], right[i].y[1]);
ans[i].y[0] = c0;
ans[i].y[1] = c1;
}
}
NFN_MK_PROC_DECL_STD(double, 512, , sub)
{
__mk_param_std( LEFT_RIGHT_ANS, double);
for (size_t i = 0; i < count_0; i++) {
register __m512 c0 = _mm512_sub_ps(left[i].z[0], right[i].z[0]);
ans[i].z[0] = c0;
}
}
NFN_MK_PROC_DECL_STD(float, sse, , scalar_mul)
{
__mk_param_std( RIGHT_ANS_SCALAR, float);
register __m128 vec = _mm_set_ps1(scalar.f32[0]);
for (size_t i = 0; i < count_1; i++) {
register __m128 c0 = _mm_mul_ps(right[i].x[0], vec);
register __m128 c1 = _mm_mul_ps(right[i].x[1], vec);
register __m128 c2 = _mm_mul_ps(right[i].x[2], vec);
register __m128 c3 = _mm_mul_ps(right[i].x[3], vec);
ans[i].x[0] = c0;
ans[i].x[1] = c1;
ans[i].x[2] = c2;
ans[i].x[3] = c3;
}
}
NFN_MK_PROC_DECL_STD(float, avx, , scalar_mul)
{
__mk_param_std( RIGHT_ANS_SCALAR, float);
register __m256 vec = _mm256_broadcast_ss(&scalar.f32[0]);
for (size_t i = 0; i < count_1; i++) {
register __m256 c0 = _mm256_mul_ps(right[i].y[0], vec);
register __m256 c1 = _mm256_mul_ps(right[i].y[1], vec);
ans[i].y[0] = c0;
ans[i].y[1] = c1;
}
}
NFN_MK_PROC_DECL_STD(float, 512, , scalar_mul)
{
__mk_param_std( RIGHT_ANS_SCALAR, float);
register __m512 vec = _mm512_broadcastss_ps(scalar.m128);
for (size_t i = 0; i < count_1; i++) {
register __m512 c0 = _mm512_mul_ps(right[i].z[0], vec);
ans[i].z[0] = c0;
}
}
NFN_MK_PROC_DECL_STD(double, sse, , scalar_mul)
{
__mk_param_std( RIGHT_ANS_SCALAR, double);
register __m128d vec = _mm_set1_pd(scalar.f64[0]);
for (size_t i = 0; i < count_1; i++) {
register __m128d c0 = _mm_mul_pd(right[i].x[0], vec);
register __m128d c1 = _mm_mul_pd(right[i].x[1], vec);
register __m128d c2 = _mm_mul_pd(right[i].x[2], vec);
register __m128d c3 = _mm_mul_pd(right[i].x[3], vec);
register __m128d c4 = _mm_mul_pd(right[i].x[4], vec);
register __m128d c5 = _mm_mul_pd(right[i].x[5], vec);
register __m128d c6 = _mm_mul_pd(right[i].x[6], vec);
register __m128d c7 = _mm_mul_pd(right[i].x[7], vec);
ans[i].x[0] = c0;
ans[i].x[1] = c1;
ans[i].x[2] = c2;
ans[i].x[3] = c3;
ans[i].x[4] = c4;
ans[i].x[5] = c5;
ans[i].x[6] = c6;
ans[i].x[7] = c7;
}
}
NFN_MK_PROC_DECL_STD(double, avx, , scalar_mul)
{
__mk_param_std( RIGHT_ANS_SCALAR, double);
register __m256d vec = _mm256_broadcast_sd(&scalar.f64[0]);
for (size_t i = 0; i < count_1; i++) {
register __m256d c0 = _mm256_mul_pd(right[i].y[0], vec);
register __m256d c1 = _mm256_mul_pd(right[i].y[1], vec);
register __m256d c2 = _mm256_mul_pd(right[i].y[2], vec);
register __m256d c3 = _mm256_mul_pd(right[i].y[3], vec);
ans[i].y[0] = c0;
ans[i].y[1] = c1;
ans[i].y[2] = c2;
ans[i].y[3] = c3;
}
}
NFN_MK_PROC_DECL_STD(double, 512, , scalar_mul)
{
__mk_param_std( RIGHT_ANS_SCALAR, double);
register __m512d vec = _mm512_set1_pd(scalar.f64[0]);
for (size_t i = 0; i < count_1; i++) {
register __m512d c0 = _mm512_mul_pd(right[i].z[0], vec);
register __m512d c1 = _mm512_mul_pd(right[i].z[1], vec);
ans[i].z[0] = c0;
ans[i].z[1] = c1;
}
}
#endif
+184
View File
@@ -0,0 +1,184 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "nfn_intrinsic.h"
#include "nfn_intrinsic.std.h"
#include "nfn_intrinsic.context.x86.h"
#include <math.h>
static const float quality_threshold = 1e-6f;
NFN_MK_PROC_DECL_STD(float, sse, , try_plu)
{
__mk_param_std(LEFT_RIGHT_ANS_SCALAR, float);
/* Aliases */
MAT_KERNEL(float) *upper = left; /* U */
MAT_KERNEL(float) *lower = right; /* L (unit diag) */
MAT_KERNEL(float) *source = ans; /* A */
unsigned char *pivot = (unsigned char *)&scalar.i64[0]; /* length = 4 */
int *success = (int *)&scalar.i64[1];
/* Init permutation and outputs */
pivot[0] = 0;
pivot[1] = 1;
pivot[2] = 2;
pivot[3] = 3;
*success = 1;
/* Working copy of A in column-major a[col][row], load via SIMD view */
MAT_KERNEL(float) a;
const __m128 z = _mm_setzero_ps();
for (int c = 0; c < 4; ++c) {
__m128 col = _mm_load_ps((float *)&source->x[c]);
_mm_storeu_ps((float *)&a.x[c], col);
_mm_store_ps((float *)&upper->x[c], z);
_mm_store_ps((float *)&lower->x[c], z);
}
const float eps = 1e-12f;
for (int k = 0; k < 4; ++k) {
/* Pivot search on column k, rows k..3 */
int p = k;
float maxabs = fabsf(a.m[k][p]);
for (int r = k + 1; r < 4; ++r) {
float v = fabsf(a.m[k][r]);
if (v > maxabs) {
maxabs = v;
p = r;
}
}
if (maxabs < eps) {
*success = 0;
/* leave outputs zeroed */
return;
}
/* Swap rows if needed */
if (p != k) {
for (int c = 0; c < 4; ++c) {
float tmp = a.m[c][k];
a.m[c][k] = a.m[c][p];
a.m[c][p] = tmp;
}
unsigned char tmpi = pivot[k];
pivot[k] = pivot[p];
pivot[p] = tmpi;
}
float pivot_val = a.m[k][k];
/* Eliminate below pivot: store multipliers in L column k */
for (int r = k + 1; r < 4; ++r) {
float factor = a.m[k][r] / pivot_val;
lower->m[k][r] = factor;
for (int c = k; c < 4; ++c) {
a.m[c][r] -= factor * a.m[c][k];
}
}
/* Set U row k (columns k..3) and L diagonal */
lower->m[k][k] = 1.0f;
for (int c = k; c < 4; ++c) {
upper->m[c][k] = a.m[c][k];
}
}
/* Ensure remaining diagonal ones for L */
for (int d = 0; d < 4; ++d) {
if (lower->m[d][d] == 0.0f)
lower->m[d][d] = 1.0f;
}
}
#define DOT_F32_SSE(a, b, ans) \
do { \
__m128 _p = _mm_mul_ps((a), (b)); /* p0 p1 p2 p3 */ \
__m128 _t = _mm_shuffle_ps(_p, _p, _MM_SHUFFLE(2, 3, 0, 1)); \
_p = _mm_add_ps(_p, _t); \
_t = _mm_movehl_ps(_t, _p); \
ans = _mm_cvtss_f32(_mm_add_ss(_p, _t)); \
} while (0)
#define FIND_MAX_F32_SSE(a, b) \
do { \
__m128 _t = _mm_movehl_ps(a, a); \
a = _mm_max_ps(a, _t); \
_t = _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 3, 0, 1)); \
b = _mm_max_ps(a, _t); \
} while (0)
/*
* layout of scalarpack:
*
*/
NFN_MK_PROC_DECL_STD(float, sse, , quality)
{
__mk_param_std(RIGHT_ANS, float);
register __m128 tiny = _mm_set1_ps(1e-6f);
register __m128 col0 = _mm_load_ps((float *)&right->x[0]);
register __m128 col1 = _mm_load_ps((float *)&right->x[1]);
register __m128 col2 = _mm_load_ps((float *)&right->x[2]);
register __m128 col3 = _mm_load_ps((float *)&right->x[3]);
NFN_ALIGNAS(16) float self_dot_src[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
// try in reigister mix later
DOT_F32_SSE(col0, col0, self_dot_src[0]);
DOT_F32_SSE(col1, col1, self_dot_src[1]);
DOT_F32_SSE(col2, col2, self_dot_src[2]);
DOT_F32_SSE(col3, col3, self_dot_src[3]);
register __m128 self_dot = _mm_load_ps(self_dot_src);
register __m128 max;
FIND_MAX_F32_SSE(self_dot, max);
//float norm = _mm_cvtss_f32(max);
// min on cross
register __m128 col_shuff_1 = _mm_shuffle_ps(col1, col1, _MM_SHUFFLE(0, 3, 2, 1));
register __m128 col_shuff_2 = _mm_shuffle_ps(col2, col2, _MM_SHUFFLE(1, 0, 3, 2));
register __m128 col_shuff_3 = _mm_shuffle_ps(col3, col3, _MM_SHUFFLE(2, 1, 0, 3));
register __m128 min = _mm_min_ps(col0, col_shuff_1);
register __m128 min0 = _mm_min_ps(col_shuff_2, col_shuff_3);
min = _mm_min_ps(min, min0);
float quality = _mm_cvtss_f32(_mm_div_ps(min, _mm_add_ps(max, tiny)));
ans->m[0][0] = quality;
if (quality < quality_threshold) {
return;
}
NFN_ALIGNAS(16) float cross_dot_src[2][4] = { 0 };
DOT_F32_SSE(col0, col1, cross_dot_src[0][0]);
DOT_F32_SSE(col0, col2, cross_dot_src[0][1]);
DOT_F32_SSE(col0, col3, cross_dot_src[0][2]);
DOT_F32_SSE(col1, col2, cross_dot_src[0][3]);
DOT_F32_SSE(col1, col3, cross_dot_src[1][0]);
DOT_F32_SSE(col2, col3, cross_dot_src[1][1]);
register __m128 cross_dot_0 = _mm_load_ps(cross_dot_src[0]);
register __m128 cross_dot_1 = _mm_load_ps(cross_dot_src[1]);
register __m128 base_0 = _mm_shuffle_ps(self_dot, self_dot, _MM_SHUFFLE(1, 0, 0, 0));
register __m128 base_1 = _mm_shuffle_ps(self_dot, self_dot, _MM_SHUFFLE(3, 3, 2, 1));
register __m128 relation_0 =
_mm_div_ps(cross_dot_0, _mm_add_ps(_mm_mul_ps(base_0, base_1), tiny));
base_0 = _mm_shuffle_ps(self_dot, self_dot, _MM_SHUFFLE(2, 3, 2, 1));
base_1 = _mm_shuffle_ps(self_dot, self_dot, _MM_SHUFFLE(3, 3, 3, 3));
register __m128 relation_1 =
_mm_div_ps(cross_dot_0, _mm_add_ps(_mm_mul_ps(base_0, base_1), tiny));
relation_0 = _mm_max_ps(relation_0, relation_1);
FIND_MAX_F32_SSE(relation_0, relation_0);
ans->m[0][1] = _mm_cvtss_f32(relation_0);
}
#endif
@@ -0,0 +1,56 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "nfn_intrinsic.h"
#include "nfn_intrinsic.context.x86.h"
#include "nfn_intrinsic.std.h"
// Matrix transpose implementations
NFN_MK_PROC_DECL_STD(float, sse, , transpose)
{
__mk_param_std(RIGHT_ANS, float);
register __m128 col0 = right->x[0];
register __m128 col1 = right->x[1];
register __m128 col2 = right->x[2];
register __m128 col3 = right->x[3];
register __m128 tmp0 = _mm_unpacklo_ps(col0, col1);
register __m128 tmp1 = _mm_unpackhi_ps(col0, col1);
register __m128 tmp2 = _mm_unpacklo_ps(col2, col3);
register __m128 tmp3 = _mm_unpackhi_ps(col2, col3);
ans->x[0] = _mm_movelh_ps(tmp0, tmp2);
ans->x[1] = _mm_movehl_ps(tmp2, tmp0);
ans->x[2] = _mm_movelh_ps(tmp1, tmp3);
ans->x[3] = _mm_movehl_ps(tmp3, tmp1);
}
NFN_MK_PROC_DECL_STD(float, avx, , transpose)
{
__mk_param_std(RIGHT_ANS, float);
register __m256 col_low = right->y[0];
register __m256 col_high = right->y[1];
register __m256 unpack_low = _mm256_unpacklo_ps(col_low, col_high);
register __m256 unpack_high = _mm256_unpackhi_ps(col_low, col_high);
col_low = _mm256_permute2f128_ps(unpack_low, unpack_high, 0x20);
col_high = _mm256_permute2f128_ps(unpack_low, unpack_high, 0x31);
ans->y[0] = _mm256_shuffle_ps(col_high, col_low, _MM_SHUFFLE(2, 0, 2, 0));
ans->y[1] = _mm256_shuffle_ps(col_high, col_low, _MM_SHUFFLE(3, 1, 3, 1));
}
NFN_MK_PROC_DECL_STD(float, 512, , transpose)
{
__mk_param_std(RIGHT_ANS, float);
register __m512 matrix = right->z[0];
const __m512i transpose_indices =
_mm512_setr_epi32(0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15);
ans->z[0] = _mm512_permutexvar_ps(transpose_indices, matrix);
}
#endif
+288
View File
@@ -0,0 +1,288 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "nfn_intrinsic.context.x86.h"
/*
* pack left matrix data to L4 buffer
*
* CONVENSIONS:
*
* buffer_1 is pointer to original colum,
* offset_1 is the offset to the original column,
* count_1 is the count can be packed
*
* C# shceduler is responsible for calculating the offset and count based on the actual buffer size and matrix dimension,
* and make sure that count_1 will not exceeds lenght of the column
*
* scalar pack carries column length and offset data:
* [column length, empty, empty, empty]
*/
NFN_MK_PROC_DECL_STD(float, sse, , fma_pack_left)
{
__mk_param_std(RIGHT_ANS_SCALAR, float);
// for SSE branch, no need for rounding, a direct copy is sufficient as long as the buffer is large enough to hold the packed data
memcpy(ans, right + offset_1, sizeof(MAT_KERNEL(float)) * count_2);
}
NFN_MK_PROC_DECL_STD(float, avx, , fma_pack_left)
{
__mk_param_std(RIGHT_ANS_SCALAR, float);
size_t i = 0;
for (; i < (count_1 & (~0x1)); i += 2) {
register __m256 ker_0_left = _mm256_load_ps((float *)&right[offset_1 + i].y[0]);
register __m256 ker_0_right = _mm256_load_ps((float *)&right[offset_1 + i].y[1]);
register __m256 ker_1_left = _mm256_load_ps((float *)&right[offset_1 + i].y[0]);
register __m256 ker_1_right = _mm256_load_ps((float *)&right[offset_1 + i].y[1]);
register __m256 col_0 = _mm256_unpacklo_ps(ker_0_left, ker_1_left);
register __m256 col_1 = _mm256_unpackhi_ps(ker_0_left, ker_1_left);
register __m256 col_2 = _mm256_unpacklo_ps(ker_0_right, ker_1_right);
register __m256 col_3 = _mm256_unpackhi_ps(ker_0_right, ker_1_right);
_mm256_store_ps((float *)&ans[i].y[0], col_0);
_mm256_store_ps((float *)&ans[i].y[1], col_1);
_mm256_store_ps((float *)&ans[i].y[2], col_2);
_mm256_store_ps((float *)&ans[i].y[3], col_3);
}
if (count_1 & 0x1) {
register __m256 ker_0_left = _mm256_load_ps((float *)&right[offset_1 + i].y[0]);
register __m256 ker_0_right = _mm256_load_ps((float *)&right[offset_1 + i].y[1]);
register __m256 zero = _mm256_setzero_ps();
register __m256 col_0 = _mm256_unpacklo_ps(ker_0_left, zero);
register __m256 col_1 = _mm256_unpackhi_ps(ker_0_left, zero);
register __m256 col_2 = _mm256_unpacklo_ps(ker_0_right, zero);
register __m256 col_3 = _mm256_unpackhi_ps(ker_0_right, zero);
_mm256_store_ps((float *)&ans[i].y[0], col_0);
_mm256_store_ps((float *)&ans[i].y[1], col_1);
_mm256_store_ps((float *)&ans[i].y[2], col_2);
_mm256_store_ps((float *)&ans[i].y[3], col_3);
}
}
NFN_MK_PROC_DECL_STD(float, 512, , fma_pack_left)
{
__mk_param_std(RIGHT_ANS_SCALAR, float);
size_t i = 0;
register __m512 ker_0;
register __m512 ker_1;
register __m512 ker_2;
register __m512 ker_3;
register __m512 col_0;
register __m512 col_1;
register __m512 col_2;
register __m512 col_3;
for (; i < (count_1 & (~0x3)); i += 4) {
ker_0 = _mm512_load_ps((float *)&right[offset_1 + i].z[0]);
ker_1 = _mm512_load_ps((float *)&right[offset_1 + i].z[1]);
ker_2 = _mm512_load_ps((float *)&right[offset_1 + i].z[2]);
ker_3 = _mm512_load_ps((float *)&right[offset_1 + i].z[3]);
col_0 = _mm512_unpacklo_ps(ker_0, ker_2);
col_1 = _mm512_unpackhi_ps(ker_0, ker_2);
col_2 = _mm512_unpacklo_ps(ker_1, ker_3);
col_3 = _mm512_unpackhi_ps(ker_1, ker_3);
_mm512_store_ps((float *)&ans[i].z[0],
_mm512_shuffle_f32x4(col_0, col_2, _MM_SHUFFLE(1, 0, 1, 0)));
_mm512_store_ps((float *)&ans[i].z[1],
_mm512_shuffle_f32x4(col_0, col_2, _MM_SHUFFLE(3, 2, 3, 2)));
_mm512_store_ps((float *)&ans[i].z[2],
_mm512_shuffle_f32x4(col_1, col_3, _MM_SHUFFLE(1, 0, 1, 0)));
_mm512_store_ps((float *)&ans[i].z[3],
_mm512_shuffle_f32x4(col_1, col_3, _MM_SHUFFLE(3, 2, 3, 2)));
}
switch (count_1 & 0x3) {
case 1:
ker_0 = _mm512_load_ps((float *)&right[offset_1 + i].z[0]);
col_0 = _mm512_castps128_ps512(_mm512_extractf32x4_ps(ker_0, 0));
col_1 = _mm512_castps128_ps512(_mm512_extractf32x4_ps(ker_0, 1));
col_2 = _mm512_castps128_ps512(_mm512_extractf32x4_ps(ker_0, 2));
col_3 = _mm512_castps128_ps512(_mm512_extractf32x4_ps(ker_0, 3));
_mm512_store_ps((float *)&ans[i].z[0], col_0);
_mm512_store_ps((float *)&ans[i].z[1], col_1);
_mm512_store_ps((float *)&ans[i].z[2], col_2);
_mm512_store_ps((float *)&ans[i].z[3], col_3);
case 2:
ker_0 = _mm512_load_ps((float *)&right[offset_1 + i].z[0]);
ker_1 = _mm512_load_ps((float *)&right[offset_1 + i].z[1]);
col_0 = _mm512_castps256_ps512(_mm512_extractf32x8_ps(ker_0, 0));
col_1 = _mm512_castps256_ps512(_mm512_extractf32x8_ps(ker_0, 1));
col_2 = _mm512_castps256_ps512(_mm512_extractf32x8_ps(ker_1, 0));
col_3 = _mm512_castps256_ps512(_mm512_extractf32x8_ps(ker_1, 1));
_mm512_store_ps((float *)&ans[i].z[0], col_0);
_mm512_store_ps((float *)&ans[i].z[1], col_1);
_mm512_store_ps((float *)&ans[i].z[2], col_2);
_mm512_store_ps((float *)&ans[i].z[3], col_3);
case 3:
ker_0 = _mm512_load_ps((float *)&right[offset_1 + i].z[0]);
ker_1 = _mm512_load_ps((float *)&right[offset_1 + i].z[1]);
ker_2 = _mm512_load_ps((float *)&right[offset_1 + i].z[2]);
ker_3 = _mm512_setzero_ps();
col_0 = _mm512_unpacklo_ps(ker_0, ker_2);
col_1 = _mm512_unpackhi_ps(ker_0, ker_2);
col_2 = _mm512_unpacklo_ps(ker_1, ker_3);
col_3 = _mm512_unpackhi_ps(ker_1, ker_3);
_mm512_store_ps((float *)&ans[i].z[0],
_mm512_shuffle_f32x4(col_0, col_2, _MM_SHUFFLE(1, 0, 1, 0)));
_mm512_store_ps((float *)&ans[i].z[1],
_mm512_shuffle_f32x4(col_0, col_2, _MM_SHUFFLE(3, 2, 3, 2)));
_mm512_store_ps((float *)&ans[i].z[2],
_mm512_shuffle_f32x4(col_1, col_3, _MM_SHUFFLE(1, 0, 1, 0)));
_mm512_store_ps((float *)&ans[i].z[3],
_mm512_shuffle_f32x4(col_1, col_3, _MM_SHUFFLE(3, 2, 3, 2)));
default:
break;
}
}
/*
* pack right matrix data to L4 buffer
*
* CONVENSIONS:
*
* buffer_1 is pointer to original row,
* offset_1 is the offset to the original row,
* count_1 is the count can be packed
*
* C# shceduler is responsible for calculating the offset and count based on the actual buffer size and matrix dimension,
* and make sure that count_1 will not exceeds lenght of the row
*
* scalar pack carries column length and offset data:
* [column length, empty, empty, empty]
*/
NFN_MK_PROC_DECL_STD(float, sse, , fma_pack_right)
{
__mk_param_std(RIGHT_ANS_SCALAR, float);
int col_length = scalar.i32[0];
for (size_t i = 0; i < count_1; i++) {
ans[i] = right[(offset_1 + i) * col_length];
}
}
NFN_MK_PROC_DECL_STD(float, avx, , fma_pack_right)
{
__mk_param_std(RIGHT_ANS_SCALAR, float);
int col_length = scalar.i32[0];
size_t i = 0;
for (; i < (count_1 & (~0x1)); i += 2) {
register __m256 ker_0_left =
_mm256_load_ps((float *)&right[(offset_1 + i) * col_length].y[0]);
register __m256 ker_0_right =
_mm256_load_ps((float *)&right[(offset_1 + i) * col_length].y[1]);
register __m256 ker_1_left =
_mm256_load_ps((float *)&right[(offset_1 + i + 1) * col_length].y[0]);
register __m256 ker_1_right =
_mm256_load_ps((float *)&right[(offset_1 + i + 1) * col_length].y[1]);
register __m256 col_0 = _mm256_unpacklo_ps(ker_0_left, ker_1_left);
register __m256 col_1 = _mm256_unpackhi_ps(ker_0_left, ker_1_left);
register __m256 col_2 = _mm256_unpacklo_ps(ker_0_right, ker_1_right);
register __m256 col_3 = _mm256_unpackhi_ps(ker_0_right, ker_1_right);
_mm256_store_ps((float *)&ans[i].y[0], col_0);
_mm256_store_ps((float *)&ans[i].y[1], col_1);
_mm256_store_ps((float *)&ans[i].y[2], col_2);
_mm256_store_ps((float *)&ans[i].y[3], col_3);
}
if (count_1 & 0x1) {
register __m256 ker_0_left =
_mm256_load_ps((float *)&right[(offset_1 + i) * col_length].y[0]);
register __m256 ker_0_right =
_mm256_load_ps((float *)&right[(offset_1 + i) * col_length].y[1]);
register __m256 zero = _mm256_setzero_ps();
register __m256 col_0 = _mm256_unpacklo_ps(ker_0_left, zero);
register __m256 col_1 = _mm256_unpackhi_ps(ker_0_left, zero);
register __m256 col_2 = _mm256_unpacklo_ps(ker_0_right, zero);
register __m256 col_3 = _mm256_unpackhi_ps(ker_0_right, zero);
_mm256_store_ps((float *)&ans[i].y[0], col_0);
_mm256_store_ps((float *)&ans[i].y[1], col_1);
_mm256_store_ps((float *)&ans[i].y[2], col_2);
_mm256_store_ps((float *)&ans[i].y[3], col_3);
}
}
NFN_MK_PROC_DECL_STD(float, 512, , fma_pack_right)
{
__mk_param_std(RIGHT_ANS_SCALAR, float);
int col_length = scalar.i32[0];
size_t i = 0;
register __m512 ker_0;
register __m512 ker_1;
register __m512 ker_2;
register __m512 ker_3;
register __m512 col_0;
register __m512 col_1;
register __m512 col_2;
register __m512 col_3;
for (; i < (count_1 & (~0x3)); i += 4) {
ker_0 = _mm512_load_ps((float *)&right[(offset_1 + i + 0) * col_length].z[0]);
ker_1 = _mm512_load_ps((float *)&right[(offset_1 + i + 1) * col_length].z[0]);
ker_2 = _mm512_load_ps((float *)&right[(offset_1 + i + 2) * col_length].z[0]);
ker_3 = _mm512_load_ps((float *)&right[(offset_1 + i + 3) * col_length].z[0]);
col_0 = _mm512_unpacklo_ps(ker_0, ker_2);
col_1 = _mm512_unpackhi_ps(ker_0, ker_2);
col_2 = _mm512_unpacklo_ps(ker_1, ker_3);
col_3 = _mm512_unpackhi_ps(ker_1, ker_3);
_mm512_store_ps((float *)&ans[i].z[0],
_mm512_shuffle_f32x4(col_0, col_2, _MM_SHUFFLE(1, 0, 1, 0)));
_mm512_store_ps((float *)&ans[i].z[1],
_mm512_shuffle_f32x4(col_0, col_2, _MM_SHUFFLE(3, 2, 3, 2)));
_mm512_store_ps((float *)&ans[i].z[2],
_mm512_shuffle_f32x4(col_1, col_3, _MM_SHUFFLE(1, 0, 1, 0)));
_mm512_store_ps((float *)&ans[i].z[3],
_mm512_shuffle_f32x4(col_1, col_3, _MM_SHUFFLE(3, 2, 3, 2)));
}
switch (count_1 & 0x3) {
case 1:
ker_0 = _mm512_load_ps((float *)&right[(offset_1 + i) * col_length].z[0]);
col_0 = _mm512_castps128_ps512(_mm512_extractf32x4_ps(ker_0, 0));
col_1 = _mm512_castps128_ps512(_mm512_extractf32x4_ps(ker_0, 1));
col_2 = _mm512_castps128_ps512(_mm512_extractf32x4_ps(ker_0, 2));
col_3 = _mm512_castps128_ps512(_mm512_extractf32x4_ps(ker_0, 3));
_mm512_store_ps((float *)&ans[i].z[0], col_0);
_mm512_store_ps((float *)&ans[i].z[1], col_1);
_mm512_store_ps((float *)&ans[i].z[2], col_2);
_mm512_store_ps((float *)&ans[i].z[3], col_3);
case 2:
ker_0 = _mm512_load_ps((float *)&right[(offset_1 + i + 0) * col_length].z[0]);
ker_1 = _mm512_load_ps((float *)&right[(offset_1 + i + 1) * col_length].z[1]);
col_0 = _mm512_castps256_ps512(_mm512_extractf32x8_ps(ker_0, 0));
col_1 = _mm512_castps256_ps512(_mm512_extractf32x8_ps(ker_0, 1));
col_2 = _mm512_castps256_ps512(_mm512_extractf32x8_ps(ker_1, 0));
col_3 = _mm512_castps256_ps512(_mm512_extractf32x8_ps(ker_1, 1));
_mm512_store_ps((float *)&ans[i].z[0], col_0);
_mm512_store_ps((float *)&ans[i].z[1], col_1);
_mm512_store_ps((float *)&ans[i].z[2], col_2);
_mm512_store_ps((float *)&ans[i].z[3], col_3);
case 3:
ker_0 = _mm512_load_ps((float *)&right[(offset_1 + i + 0) * col_length].z[0]);
ker_1 = _mm512_load_ps((float *)&right[(offset_1 + i + 1) * col_length].z[1]);
ker_2 = _mm512_load_ps((float *)&right[(offset_1 + i + 2) * col_length].z[2]);
ker_3 = _mm512_setzero_ps();
col_0 = _mm512_unpacklo_ps(ker_0, ker_2);
col_1 = _mm512_unpackhi_ps(ker_0, ker_2);
col_2 = _mm512_unpacklo_ps(ker_1, ker_3);
col_3 = _mm512_unpackhi_ps(ker_1, ker_3);
_mm512_store_ps((float *)&ans[i].z[0],
_mm512_shuffle_f32x4(col_0, col_2, _MM_SHUFFLE(1, 0, 1, 0)));
_mm512_store_ps((float *)&ans[i].z[1],
_mm512_shuffle_f32x4(col_0, col_2, _MM_SHUFFLE(3, 2, 3, 2)));
_mm512_store_ps((float *)&ans[i].z[2],
_mm512_shuffle_f32x4(col_1, col_3, _MM_SHUFFLE(1, 0, 1, 0)));
_mm512_store_ps((float *)&ans[i].z[3],
_mm512_shuffle_f32x4(col_1, col_3, _MM_SHUFFLE(3, 2, 3, 2)));
default:
break;
}
}
#endif
+375
View File
@@ -0,0 +1,375 @@
#include "nfn_target.h"
#include <immintrin.h>
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
// #include "nfn_intrinsic.h"
#include "nfn_template.fma.x86.h"
#include "nfn_intrinsic.std.h"
#include "nfn_intrinsic.context.x86.h"
#include <stdio.h>
#define _FMA_SELECT(i) _MM_SHUFFLE(i, i, i, i)
NFN_MK_PROC_DECL_STD(float, sse, , fma)
{
__mk_param_std(LEFT_RIGHT_ANS, float);
NFN_MAT_KERNEL_LOCAL *restrict ans_cur = ans + offset_2;
register __m128 c0 = _mm_load_ps((float *)&ans_cur->x[0]);
register __m128 c1 = _mm_load_ps((float *)&ans_cur->x[1]);
register __m128 c2 = _mm_load_ps((float *)&ans_cur->x[2]);
register __m128 c3 = _mm_load_ps((float *)&ans_cur->x[3]);
register __m128 bcol;
for (size_t z = 0; z < count_0; z++) {
NFN_MAT_KERNEL_LOCAL const *right_base = right;
NFN_MAT_KERNEL_LOCAL const *left_base = left;
const __m128 a0 = _mm_load_ps((float *)&left_base->x[0]);
const __m128 a1 = _mm_load_ps((float *)&left_base->x[1]);
const __m128 a2 = _mm_load_ps((float *)&left_base->x[2]);
const __m128 a3 = _mm_load_ps((float *)&left_base->x[3]);
/* Column 0 */
bcol = _mm_load_ps((float *)&right_base->x[0]);
c0 = _mm_add_ps(c0, _mm_mul_ps(a0, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(0))));
c0 = _mm_add_ps(c0, _mm_mul_ps(a1, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(1))));
c0 = _mm_add_ps(c0, _mm_mul_ps(a2, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(2))));
c0 = _mm_add_ps(c0, _mm_mul_ps(a3, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(3))));
/* Column 1 */
bcol = _mm_load_ps((float *)&right_base->x[1]);
c1 = _mm_add_ps(c1, _mm_mul_ps(a0, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(0))));
c1 = _mm_add_ps(c1, _mm_mul_ps(a1, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(1))));
c1 = _mm_add_ps(c1, _mm_mul_ps(a2, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(2))));
c1 = _mm_add_ps(c1, _mm_mul_ps(a3, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(3))));
/* Column 2 */
bcol = _mm_load_ps((float *)&right_base->x[2]);
c2 = _mm_add_ps(c2, _mm_mul_ps(a0, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(0))));
c2 = _mm_add_ps(c2, _mm_mul_ps(a1, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(1))));
c2 = _mm_add_ps(c2, _mm_mul_ps(a2, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(2))));
c2 = _mm_add_ps(c2, _mm_mul_ps(a3, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(3))));
/* Column 3 */
bcol = _mm_load_ps((float *)&right_base->x[3]);
c3 = _mm_add_ps(c3, _mm_mul_ps(a0, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(0))));
c3 = _mm_add_ps(c3, _mm_mul_ps(a1, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(1))));
c3 = _mm_add_ps(c3, _mm_mul_ps(a2, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(2))));
c3 = _mm_add_ps(c3, _mm_mul_ps(a3, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(3))));
}
_mm_store_ps((float *)&ans_cur->x[0], c0);
_mm_store_ps((float *)&ans_cur->x[1], c1);
_mm_store_ps((float *)&ans_cur->x[2], c2);
_mm_store_ps((float *)&ans_cur->x[3], c3);
}
NFN_MK_PROC_DECL_STD(float, avx, , fma)
{
__mk_param_std(LEFT_RIGHT_ANS, float);
NFN_MAT_KERNEL_LOCAL *restrict ans_cur = ans + offset_2;
register __m256 c0 = _mm256_load_ps((float *)&ans_cur->y[0]);
register __m256 c1 = _mm256_load_ps((float *)&ans_cur->y[1]);
register __m256 c2 = _mm256_load_ps((float *)&ans_cur->y[2]);
register __m256 c3 = _mm256_load_ps((float *)&ans_cur->y[3]);
for (size_t z = 0; z < count_0; z++) {
NFN_MAT_KERNEL_LOCAL const *b_base = right;
NFN_MAT_KERNEL_LOCAL const *a_base = left;
register __m256 b0 = _mm256_load_ps((float *)&b_base->y[0]);
register __m256 b1 = _mm256_load_ps((float *)&b_base->y[1]);
register __m256 b2 = _mm256_load_ps((float *)&b_base->y[2]);
register __m256 b3 = _mm256_load_ps((float *)&b_base->y[3]);
register __m256 a0 = _mm256_load_ps((float *)&a_base->y[0]);
register __m256 a1 = _mm256_load_ps((float *)&a_base->y[1]);
register __m256 a2 = _mm256_load_ps((float *)&a_base->y[2]);
register __m256 a3 = _mm256_load_ps((float *)&a_base->y[3]);
NFN_MK_FMA_F32_AVX2_CYCLE(0);
NFN_MK_FMA_F32_AVX2_CYCLE(1);
NFN_MK_FMA_F32_AVX2_CYCLE(2);
NFN_MK_FMA_F32_AVX2_CYCLE(3);
}
_mm256_store_ps((float *)&(ans_cur->y[0]), c0);
_mm256_store_ps((float *)&(ans_cur->y[1]), c1);
_mm256_store_ps((float *)&(ans_cur->y[2]), c2);
_mm256_store_ps((float *)&(ans_cur->y[3]), c3);
}
NFN_MK_PROC_DECL_STD(float, avx, _fma, fma)
{
__mk_param_std(LEFT_RIGHT_ANS, float);
NFN_MAT_KERNEL_LOCAL *restrict ans_cur = ans + offset_2;
register __m256 c0 = _mm256_load_ps((float *)&ans_cur->y[0]);
register __m256 c1 = _mm256_load_ps((float *)&ans_cur->y[1]);
register __m256 c2 = _mm256_load_ps((float *)&ans_cur->y[2]);
register __m256 c3 = _mm256_load_ps((float *)&ans_cur->y[3]);
for (size_t z = 0; z < count_0; z += 2) {
const NFN_MAT_KERNEL_LOCAL *a_base = left + z;
const NFN_MAT_KERNEL_LOCAL *b_base = right + z;
register __m256 b0 = _mm256_load_ps((float *)&b_base->y[0]);
register __m256 b1 = _mm256_load_ps((float *)&b_base->y[1]);
register __m256 b2 = _mm256_load_ps((float *)&b_base->y[2]);
register __m256 b3 = _mm256_load_ps((float *)&b_base->y[3]);
register __m256 a0 = _mm256_load_ps((float *)&a_base->y[0]);
register __m256 a1 = _mm256_load_ps((float *)&a_base->y[1]);
register __m256 a2 = _mm256_load_ps((float *)&a_base->y[2]);
register __m256 a3 = _mm256_load_ps((float *)&a_base->y[3]);
NFN_MK_FMA_F32_AVX2_CYCLE(0);
NFN_MK_FMA_F32_AVX2_CYCLE(1);
NFN_MK_FMA_F32_AVX2_CYCLE(2);
NFN_MK_FMA_F32_AVX2_CYCLE(3);
}
_mm256_store_ps((float *)&(ans_cur->y[0]), c0);
_mm256_store_ps((float *)&(ans_cur->y[1]), c1);
_mm256_store_ps((float *)&(ans_cur->y[2]), c2);
_mm256_store_ps((float *)&(ans_cur->y[3]), c3);
}
NFN_MK_PROC_DECL_STD(float, 512, , fma)
{
__mk_param_std(LEFT_RIGHT_ANS, float);
NFN_MAT_KERNEL_LOCAL *restrict ans_cur = ans + offset_2;
register __m512 c0 = _mm512_load_ps((float *)&ans_cur->z[0]);
register __m512 c1 = _mm512_load_ps((float *)&ans_cur->z[0]);
register __m512 c2 = _mm512_load_ps((float *)&ans_cur->z[0]);
register __m512 c3 = _mm512_load_ps((float *)&ans_cur->z[0]);
for (size_t z = 0; z < count_0; z += 4) {
const NFN_MAT_KERNEL_LOCAL *a_base = left + z;
const NFN_MAT_KERNEL_LOCAL *b_base = right + z;
register __m512 b0 = _mm512_load_ps((float *)&b_base->z[0]);
register __m512 b1 = _mm512_load_ps((float *)&b_base->z[1]);
register __m512 b2 = _mm512_load_ps((float *)&b_base->z[2]);
register __m512 b3 = _mm512_load_ps((float *)&b_base->z[3]);
register __m512 a0 = _mm512_load_ps((float *)&a_base->z[0]);
register __m512 a1 = _mm512_load_ps((float *)&a_base->z[1]);
register __m512 a2 = _mm512_load_ps((float *)&a_base->z[2]);
register __m512 a3 = _mm512_load_ps((float *)&a_base->z[3]);
NFN_MK_FMA_F32_512_CYCLE(0);
NFN_MK_FMA_F32_512_CYCLE(1);
NFN_MK_FMA_F32_512_CYCLE(2);
NFN_MK_FMA_F32_512_CYCLE(3);
}
_mm512_store_ps((float *)&ans_cur->z[0], c0);
_mm512_store_ps((float *)&ans_cur->z[1], c1);
_mm512_store_ps((float *)&ans_cur->z[2], c2);
_mm512_store_ps((float *)&ans_cur->z[3], c3);
}
NFN_MK_PROC_DECL_STD(double, sse, , fma)
{
__mk_param_std(LEFT_RIGHT_ANS, double);
NFN_MAT_KERNEL_LOCAL *restrict ans_cur = ans + offset_2;
register __m128d c0_lo = ans_cur->x[0];
register __m128d c0_hi = ans_cur->x[1];
register __m128d c1_lo = ans_cur->x[2];
register __m128d c1_hi = ans_cur->x[3];
register __m128d c2_lo = ans_cur->x[4];
register __m128d c2_hi = ans_cur->x[5];
register __m128d c3_lo = ans_cur->x[6];
register __m128d c3_hi = ans_cur->x[7];
for (size_t z = 0; z < count_0; z++) {
NFN_MAT_KERNEL_LOCAL const *right_base = right;
NFN_MAT_KERNEL_LOCAL const *left_base = left;
const double *right_values = &right_base->m[0][0];
const double *left_values = &left_base->m[0][0];
NFN_MK_FMA_F64_SSE_PHASE(0);
NFN_MK_FMA_F64_SSE_PHASE(1);
NFN_MK_FMA_F64_SSE_PHASE(2);
NFN_MK_FMA_F64_SSE_PHASE(3);
}
ans_cur->x[0] = c0_lo;
ans_cur->x[1] = c0_hi;
ans_cur->x[2] = c1_lo;
ans_cur->x[3] = c1_hi;
ans_cur->x[4] = c2_lo;
ans_cur->x[5] = c2_hi;
ans_cur->x[6] = c3_lo;
ans_cur->x[7] = c3_hi;
}
NFN_MK_PROC_DECL_STD(double, avx, , fma)
{
__mk_param_std(LEFT_RIGHT_ANS, double);
NFN_MAT_KERNEL_LOCAL *restrict ans_cur = ans + offset_2;
register __m256d c0 = _mm256_load_pd((double *)&ans_cur->y[0]);
register __m256d c1 = _mm256_load_pd((double *)&ans_cur->y[1]);
register __m256d c2 = _mm256_load_pd((double *)&ans_cur->y[2]);
register __m256d c3 = _mm256_load_pd((double *)&ans_cur->y[3]);
register __m256d bcol;
for (size_t z = 0; z < count_0; z++) {
NFN_MAT_KERNEL_LOCAL const *right_base = right;
NFN_MAT_KERNEL_LOCAL const *left_base = left;
const __m256d a0 = _mm256_load_pd((double *)&left_base->y[0]);
const __m256d a1 = _mm256_load_pd((double *)&left_base->y[1]);
const __m256d a2 = _mm256_load_pd((double *)&left_base->y[2]);
const __m256d a3 = _mm256_load_pd((double *)&left_base->y[3]);
/* Column 0 */
bcol = _mm256_load_pd((double *)&right_base->y[0]);
c0 = _mm256_add_pd(c0,
_mm256_mul_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0))));
c0 = _mm256_add_pd(c0,
_mm256_mul_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1))));
c0 = _mm256_add_pd(c0,
_mm256_mul_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2))));
c0 = _mm256_add_pd(c0,
_mm256_mul_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3))));
/* Column 1 */
bcol = _mm256_load_pd((double *)&right_base->y[1]);
c1 = _mm256_add_pd(c1,
_mm256_mul_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0))));
c1 = _mm256_add_pd(c1,
_mm256_mul_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1))));
c1 = _mm256_add_pd(c1,
_mm256_mul_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2))));
c1 = _mm256_add_pd(c1,
_mm256_mul_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3))));
/* Column 2 */
bcol = _mm256_load_pd((double *)&right_base->y[2]);
c2 = _mm256_add_pd(c2,
_mm256_mul_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0))));
c2 = _mm256_add_pd(c2,
_mm256_mul_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1))));
c2 = _mm256_add_pd(c2,
_mm256_mul_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2))));
c2 = _mm256_add_pd(c2,
_mm256_mul_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3))));
/* Column 3 */
bcol = _mm256_load_pd((double *)&right_base->y[3]);
c3 = _mm256_add_pd(c3,
_mm256_mul_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0))));
c3 = _mm256_add_pd(c3,
_mm256_mul_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1))));
c3 = _mm256_add_pd(c3,
_mm256_mul_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2))));
c3 = _mm256_add_pd(c3,
_mm256_mul_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3))));
}
_mm256_store_pd((double *)&ans_cur->y[0], c0);
_mm256_store_pd((double *)&ans_cur->y[1], c1);
_mm256_store_pd((double *)&ans_cur->y[2], c2);
_mm256_store_pd((double *)&ans_cur->y[3], c3);
}
NFN_MK_PROC_DECL_STD(double, avx, _fma, fma)
{
__mk_param_std(LEFT_RIGHT_ANS, double);
NFN_MAT_KERNEL_LOCAL *restrict ans_cur = ans + offset_2;
register __m256d c0 = _mm256_load_pd((double *)&ans_cur->y[0]);
register __m256d c1 = _mm256_load_pd((double *)&ans_cur->y[1]);
register __m256d c2 = _mm256_load_pd((double *)&ans_cur->y[2]);
register __m256d c3 = _mm256_load_pd((double *)&ans_cur->y[3]);
register __m256d bcol;
for (size_t z = 0; z < count_0; z++) {
NFN_MAT_KERNEL_LOCAL const *right_base = right;
NFN_MAT_KERNEL_LOCAL const *left_base = left;
const __m256d a0 = _mm256_load_pd((double *)&left_base->y[0]);
const __m256d a1 = _mm256_load_pd((double *)&left_base->y[1]);
const __m256d a2 = _mm256_load_pd((double *)&left_base->y[2]);
const __m256d a3 = _mm256_load_pd((double *)&left_base->y[3]);
/* Column 0 */
bcol = _mm256_load_pd((double *)&right_base->y[0]);
c0 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c0);
c0 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c0);
c0 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c0);
c0 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c0);
/* Column 1 */
bcol = _mm256_load_pd((double *)&right_base->y[1]);
c1 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c1);
c1 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c1);
c1 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c1);
c1 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c1);
/* Column 2 */
bcol = _mm256_load_pd((double *)&right_base->y[2]);
c2 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c2);
c2 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c2);
c2 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c2);
c2 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c2);
/* Column 3 */
bcol = _mm256_load_pd((double *)&right_base->y[3]);
c3 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c3);
c3 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c3);
c3 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c3);
c3 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c3);
}
_mm256_store_pd((double *)&ans_cur->y[0], c0);
_mm256_store_pd((double *)&ans_cur->y[1], c1);
_mm256_store_pd((double *)&ans_cur->y[2], c2);
_mm256_store_pd((double *)&ans_cur->y[3], c3);
}
NFN_MK_PROC_DECL_STD(double, 512, , fma)
{
__mk_param_std(LEFT_RIGHT_ANS, double);
NFN_MAT_KERNEL_LOCAL *restrict ans_cur = ans + offset_2;
register __m512d c0 = _mm512_load_pd((double *)&ans_cur->z[0]);
register __m512d c1 = _mm512_load_pd((double *)&ans_cur->z[1]);
register __m512d c2 = _mm512_load_pd((double *)&ans_cur->z[2]);
register __m512d c3 = _mm512_load_pd((double *)&ans_cur->z[3]);
for (size_t z = 0; z < count_0; z += 2) {
NFN_MAT_KERNEL_LOCAL const *right_base = right + z;
NFN_MAT_KERNEL_LOCAL const *left_base = left + z;
register __m512d a0 = _mm512_load_pd((double *)&left_base->z[0]);
register __m512d a1 = _mm512_load_pd((double *)&left_base->z[1]);
register __m512d a2 = _mm512_load_pd((double *)&left_base->z[2]);
register __m512d a3 = _mm512_load_pd((double *)&left_base->z[3]);
register __m512d b0 = _mm512_load_pd((double *)&right_base->z[0]);
register __m512d b1 = _mm512_load_pd((double *)&right_base->z[1]);
register __m512d b2 = _mm512_load_pd((double *)&right_base->z[2]);
register __m512d b3 = _mm512_load_pd((double *)&right_base->z[3]);
NFN_MK_FMA_F64_AVX2_CYCLE(0);
NFN_MK_FMA_F64_AVX2_CYCLE(1);
NFN_MK_FMA_F64_AVX2_CYCLE(2);
NFN_MK_FMA_F64_AVX2_CYCLE(3);
}
_mm512_store_pd((double *)&ans_cur->z[0], c0);
_mm512_store_pd((double *)&ans_cur->z[1], c1);
_mm512_store_pd((double *)&ans_cur->z[2], c2);
_mm512_store_pd((double *)&ans_cur->z[3], c3);
}
#endif
@@ -0,0 +1,50 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "nfn_internal.h"
#include "nfn_intrinsic.context.x86.h"
#include "nfn_intrinsic.std.h"
#include "nfn_intrinsic.matkernel.h"
#include <emmintrin.h>
#include <immintrin.h>
#include <pmmintrin.h>
NFN_MK_PROC_DECL_STD(float, sse, , k_quality)
{
__mk_param_std(RIGHT_ANS_SCALAR, float);
}
INTERNAL NFN_STD_TARGET_ATTR(sse, ) void kp_check_f32(mat_kernel *kernel)
{
typedef MAT_KERNEL(float) NFN_MAT;
NFN_MAT const *k = (NFN_MAT const *)kernel;
float cross[4];
cross[0] = k->m[0][0];
cross[1] = k->m[1][1];
cross[2] = k->m[2][2];
cross[3] = k->m[3][3];
float evaluation[4];
register __m128 cross_vec = _mm_loadu_ps((float *)cross);
register __m128 abs_mask = _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff));
cross_vec = _mm_and_ps(cross_vec, abs_mask);
register __m128 average_vec = _mm_hadd_ps(cross_vec, cross_vec);
average_vec = _mm_hadd_ps(average_vec, average_vec);
register __m128 quarter_vec = _mm_set1_ps(0.25f);
average_vec = _mm_mul_ps(average_vec, quarter_vec);
register __m128 stddev_vec = _mm_sub_ps(cross_vec, average_vec);
stddev_vec = _mm_mul_ps(stddev_vec, stddev_vec);
stddev_vec = _mm_hadd_ps(stddev_vec, stddev_vec);
stddev_vec = _mm_hadd_ps(stddev_vec, stddev_vec);
stddev_vec = _mm_mul_ps(stddev_vec, quarter_vec);
stddev_vec = _mm_sqrt_ps(stddev_vec);
evaluation[0] = _mm_cvtss_f32(average_vec);
evaluation[1] = _mm_cvtss_f32(stddev_vec);
}
#endif
@@ -0,0 +1,33 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "nfn_internal.h"
#include "nfn_intrinsic.h"
static __m128i facIncrement;
static __m128i facBase;
INTERNAL uint64_t NFN_DECL factorial_simd_sse(int n)
{
facIncrement = _mm_set1_epi32(4);
facBase = _mm_setr_epi32(1, 2, 3, 4);
__m128i baseResult = facBase;
__m128i facBaseLocal = facBase;
const int cycle = (n - 3) >> 2;
const uint32_t rest = (cycle << 2) + 1;
for (size_t i = 0; i < cycle; i++) {
facBaseLocal = _mm_add_epi32(facBaseLocal, facIncrement);
baseResult = _mm_mullo_epi32(baseResult, facBaseLocal);
}
NFN_ALIGNAS(16) int32_t lanes[4];
_mm_store_si128((__m128i *)lanes, baseResult);
uint64_t result = (uint64_t)lanes[0] * (uint64_t)lanes[1] * (uint64_t)lanes[2] *
(uint64_t)lanes[3];
for (uint64_t i = rest; i < n; i++) {
result *= i;
}
return result;
}
#endif
+50
View File
@@ -0,0 +1,50 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "NitloadFrameWork.h"
INTERNAL int NFN_DECL city_hash_simplify_sse(byte const *str, int const length)
{
uint32_t hash = length, size = length << 1;
byte const *end = str + size;
byte const *ptr = str;
if (size < 4) {
for (; ptr < end; ptr += 2) {
hash = _mm_crc32_u16(hash, *ptr);
}
} else if (size < 16) {
for (; ptr < end; ptr += 4) {
hash = _mm_crc32_u32(hash, ((uint32_t const *)ptr)[0]);
}
for (; ptr < end; ptr++) {
hash = _mm_crc32_u16(hash, *ptr);
}
} else {
byte const *last_128 = end - 16;
byte const *last_32 = end - 4;
uint64_t hash64 = size;
for (; ptr < last_128; ptr += 16) {
__m128i vec = _mm_loadu_si128((__m128i const *)ptr);
hash64 = _mm_crc32_u64(hash64, _mm_extract_epi64(vec, 0));
hash64 = _mm_crc32_u64(hash64, _mm_extract_epi64(vec, 1));
}
hash = _mm_crc32_u8(hash, hash64);
hash = _mm_crc32_u8(hash, hash64 >> 32);
for (; ptr < last_32; ptr += 4) {
hash = _mm_crc32_u32(hash, ((uint32_t const *)ptr)[0]);
}
for (; ptr < end; ptr += 2) {
hash = _mm_crc32_u8(hash, *ptr);
}
}
hash ^= hash >> 16;
hash *= 0x85ebca6b;
hash ^= hash >> 13;
hash *= 0xc2b2ae35;
hash ^= hash >> 16;
return hash;
}
#endif
+329
View File
@@ -0,0 +1,329 @@
#include "nfn_target.h"
/* clang-format off */
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "nfn_intrinsic.h"
#include "NitloadFrameWork.h"
static SIMDID global_simdid = { 0 };
#include "nfn_intrinsic.context.x86.h"
#include <immintrin.h>
#include <string.h>
#if defined(_MSC_VER)
#include <intrin.h>
#else
#include <cpuid.h>
#endif
static void get_cpuid(int leaf, int subleaf, int *eax, int *ebx, int *ecx, int *edx)
{
#if defined(_MSC_VER)
int regs[4];
__cpuidex(regs, leaf, subleaf);
*eax = regs[0];
*ebx = regs[1];
*ecx = regs[2];
*edx = regs[3];
#else
unsigned int a, b, c, d;
__cpuid_count((unsigned int)leaf, (unsigned int)subleaf, a, b, c, d);
*eax = (int)a;
*ebx = (int)b;
*ecx = (int)c;
*edx = (int)d;
#endif
}
/* XCR0 is per logical processor. Reading it after thread affinity is applied
* verifies that the OS saves/restores the register state needed by this core. */
static unsigned long long read_xcr0(void)
{
#if defined(_MSC_VER)
return _xgetbv(0);
#else
return __builtin_ia32_xgetbv(0);
#endif
}
static int os_has_xsave_state(unsigned long long required)
{
return (read_xcr0() & required) == required;
}
#define XCR0_X87 (1ull << 0)
#define XCR0_XMM (1ull << 1)
#define XCR0_YMM (1ull << 2)
#define XCR0_OPMASK (1ull << 5)
#define XCR0_ZMM_HI256 (1ull << 6)
#define XCR0_HI16_ZMM (1ull << 7)
#define XCR0_AVX_STATE (XCR0_X87 | XCR0_XMM | XCR0_YMM)
#define XCR0_AVX512_STATE \
(XCR0_AVX_STATE | XCR0_OPMASK | XCR0_ZMM_HI256 | XCR0_HI16_ZMM)
static void decode_family_model(int cpuid1_eax, int *family, int *model)
{
int family_value = 0;
int model_value = 0;
const int base_fam = (cpuid1_eax >> 8) & 0xF;
const int ext_fam = (cpuid1_eax >> 20) & 0xFF;
const int base_model = (cpuid1_eax >> 4) & 0xF;
const int ext_model = (cpuid1_eax >> 16) & 0xF;
if (base_fam == 0xF) {
family_value = base_fam + ext_fam;
} else {
family_value = base_fam;
}
if ((base_fam == 0x6) || (base_fam == 0xF)) {
model_value = (ext_model << 4) | base_model;
} else {
model_value = base_model;
}
*family = family_value;
*model = model_value;
}
/* AVX-512 execution-width classification for AMD.
* Family 19h (Zen4) uses a 256-bit dual-pumped path.
* Family 1Ah models 00h-0Fh are documented for the full-width EPYC 9005
* implementation. Models 10h-1Fh are intentionally UNKNOWN: that CPUID
* range is used by both full-width EPYC 9005 Zen5c and dual-pumped EPYC
* 8005 products, and CPUID has no architectural execution-width bit. */
static uint64_t amd_avx512_impl(int family, int model)
{
if (family == 0x19) {
return SIMDID_AVX512_IMPL_DUAL;
}
if (family == 0x1A) {
if (model >= 0x00 && model <= 0x0F) {
return SIMDID_AVX512_IMPL_NATIVE;
}
}
return SIMDID_AVX512_IMPL_UNKNOWN;
}
/* Returns 1 if this AMD/Hygon CPU uses split-lane YMM (two fused 128-bit execution units).
* - AMD Zen1 (family 0x17, model < 0x30) and Zen+ (0x18-0x2F) use a split/glued path.
* - Hygon Dhyana is architecturally Zen1-based; always split.
* - AMD Zen2+ (family 0x17 model >= 0x30, family 0x19+) have native 256-bit execution. */
static int is_amd_glue_ymm(const char *vendor, int cpuid1_eax)
{
int family = 0;
int model = 0;
if (strcmp(vendor, "HygonGenuine") == 0) {
return 1;
}
if (strcmp(vendor, "AuthenticAMD") != 0) {
return 0;
}
decode_family_model(cpuid1_eax, &family, &model);
return (family == 0x17) && (model < 0x30);
}
NFN_API SIMDID NFN_DECL nfn_get_simd_level_local()
{
SIMDID simdid = { 0 };
int eax = 0;
int ebx = 0;
int ecx = 0;
int edx = 0;
int family = 0;
int model = 0;
get_cpuid(0, 0, &eax, &ebx, &ecx, &edx);
const int max_basic_leaf = eax;
char vendor[13];
memcpy(vendor + 0, &ebx, 4);
memcpy(vendor + 4, &edx, 4);
memcpy(vendor + 8, &ecx, 4);
vendor[12] = '\0';
const int is_intel = (strcmp(vendor, "GenuineIntel") == 0);
const int is_amd = (strcmp(vendor, "AuthenticAMD") == 0);
const int is_hygon = (strcmp(vendor, "HygonGenuine") == 0);
const int is_centaur = (strcmp(vendor, "CentaurHauls") == 0);
const int is_zhaoxin = (strcmp(vendor, " Shanghai ") == 0);
const int is_via = is_centaur || is_zhaoxin;
get_cpuid(1, 0, &eax, &ebx, &ecx, &edx);
const int cpuid1_eax = eax;
decode_family_model(cpuid1_eax, &family, &model);
(void)model;
const int has_sse42 = (ecx & (1 << 20)) != 0;
const int has_avx = (ecx & (1 << 28)) != 0;
const int has_osxsave = (ecx & (1 << 27)) != 0;
const int has_fma = (ecx & (1 << 12)) != 0;
const int has_f16c = (ecx & (1 << 29)) != 0;
const uint64_t root = SIMDID_PACK_ROOT(SIMDID_ROOT_X86_64);
uint64_t manu_code = SIMDID_MANU_UNKNOWN;
if (is_intel) {
manu_code = SIMDID_MANU_INTEL;
} else if (is_amd) {
manu_code = SIMDID_MANU_AMD;
} else if (is_hygon) {
manu_code = SIMDID_MANU_HYGON;
} else if (is_via) {
manu_code = SIMDID_MANU_VIA;
}
const uint64_t manu = SIMDID_PACK_MANU(manu_code);
uint64_t main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_NONE);
uint64_t avx_bits = 0;
uint64_t avx512_bits = 0;
uint64_t avx10_bits = 0;
uint64_t uarch_bits = 0;
int has_hybrid = 0;
if (has_sse42) {
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_SSE);
}
if (has_avx && has_osxsave && os_has_xsave_state(XCR0_AVX_STATE)) {
avx_bits |= SIMDID_AVX;
if (is_amd_glue_ymm(vendor, cpuid1_eax) || is_via) {
avx_bits |= SIMDID_AVX_DUAL;
}
if (max_basic_leaf >= 7) {
get_cpuid(7, 0, &eax, &ebx, &ecx, &edx);
const int has_avx2 = (ebx & (1 << 5)) != 0;
const int has_avx512f = (ebx & (1 << 16)) != 0;
const int has_avx512dq = (ebx & (1 << 17)) != 0;
const int has_avx512cd = (ebx & (1 << 28)) != 0;
const int has_avx512bw = (ebx & (1 << 30)) != 0;
const int has_avx512vl = (ebx & (1u << 31)) != 0;
const int has_avx512_vnni = (ecx & (1 << 11)) != 0;
has_hybrid = (edx & (1 << 15)) != 0;
int has_avx10 = 0;
if (eax >= 1) {
get_cpuid(7, 1, &eax, &ebx, &ecx, &edx);
has_avx10 = (edx & (1 << 19)) != 0;
}
if (has_avx2) {
avx_bits |= SIMDID_AVX2;
if (has_fma) {
avx_bits |= SIMDID_AVX_FMA;
}
if (has_f16c) {
avx_bits |= SIMDID_AVX_F16C;
}
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX2);
}
if (has_avx512f && has_avx512cd && has_avx512dq && has_avx512bw &&
has_avx512vl && os_has_xsave_state(XCR0_AVX512_STATE)) {
avx512_bits |= SIMDID_AVX512_BASE;
if (has_avx512_vnni) {
avx512_bits |= SIMDID_AVX512_VNNI;
}
if (is_intel) {
avx512_bits |= SIMDID_AVX512_IMPL_NATIVE;
} else if (is_amd) {
avx512_bits |= amd_avx512_impl(family, model);
} else {
avx512_bits |= SIMDID_AVX512_IMPL_UNKNOWN;
}
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX512);
if (has_avx10 && (max_basic_leaf >= 0x24)) {
int a10_eax = 0;
int a10_ebx = 0;
int a10_ecx = 0;
int a10_edx = 0;
int avx10_ver = 0;
get_cpuid(0x24, 0, &a10_eax, &a10_ebx, &a10_ecx, &a10_edx);
avx10_ver = a10_ebx & 0xFF;
if (avx10_ver >= 1) {
unsigned max_avx10_subleaf = (unsigned)a10_eax;
unsigned stored_version = (unsigned)avx10_ver;
if (stored_version > SIMDID_AVX10_VERSION_MASK) {
stored_version = SIMDID_AVX10_VERSION_MASK;
}
avx10_bits = stored_version;
if (max_avx10_subleaf >= 1) {
get_cpuid(0x24, 1, &a10_eax, &a10_ebx,
&a10_ecx, &a10_edx);
if ((a10_ecx & (1 << 2)) != 0) {
avx10_bits |= SIMDID_AVX10_VNNI_INT;
}
}
main_lvl = SIMDID_PACK_MAIN(SIMDID_MAIN_LVL_AVX10);
}
}
}
}
}
if (has_hybrid && (max_basic_leaf >= 0x1A)) {
int h_eax = 0;
int h_ebx = 0;
int h_ecx = 0;
int h_edx = 0;
get_cpuid(0x1A, 0, &h_eax, &h_ebx, &h_ecx, &h_edx);
{
const unsigned core_type = (unsigned)(h_eax >> 24) & 0xFFU;
if (core_type == 0x40U) {
uarch_bits |= SIMDID_UARCH_HYBRID_P;
} else if (core_type == 0x20U) {
uarch_bits |= SIMDID_UARCH_HYBRID_E;
}
}
} else {
uarch_bits |= SIMDID_UARCH_HYBRID_P | SIMDID_UARCH_HYBRID_E;
}
{
const int has_avx_dual = (avx_bits & SIMDID_AVX_DUAL) != 0;
const uint64_t avx512_impl = avx512_bits & SIMDID_AVX512_IMPL_MASK;
const int has_avx512_dual = avx512_impl == SIMDID_AVX512_IMPL_DUAL;
if (has_avx_dual || has_avx512_dual) {
uarch_bits |= SIMDID_UARCH_SIMD_DUAL;
}
}
{
uint64_t final_mask = root;
final_mask |= manu;
final_mask |= main_lvl;
final_mask |= SIMDID_PACK_AVX512(avx512_bits);
final_mask |= SIMDID_PACK_AVX(avx_bits);
final_mask |= SIMDID_PACK_AVX10(avx10_bits);
final_mask |= SIMDID_PACK_UARCH(uarch_bits);
simdid.mask = final_mask;
}
return simdid;
}
NFN_API SIMDID NFN_DECL nfn_get_simd_level_global()
{
if (global_simdid.mask) {
return global_simdid;
}
global_simdid = nfn_get_simd_level_local();
return global_simdid;
}
#endif
@@ -0,0 +1,222 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "NitloadFrameWork.h"
#include "nfn_intrinsic.context.x86.h"
/* -----------------------------------------------------------------------
* Field extraction helpers.
*
* SIMDID layout (64 bits, little-endian byte order in make_byte[]):
* [3..0] Root ISA make_byte[0] low nibble
* [7..4] Manufacture make_byte[0] high nibble
* [15..8] MainLevel make_byte[1]
* [31..16] AVX512 Feature make_byte[2..3] (16 bits)
* [39..32] AVX Feature make_byte[4]
* [47..40] AVX10 Feature make_byte[5]
* [55..48] uArchHi make_byte[6]
* [63..56] Reserved make_byte[7]
* ----------------------------------------------------------------------- */
#define FIELD_MAIN(id) ((unsigned char)(id).make_byte[1])
#define FIELD_AVX512(id) \
((uint16_t)((unsigned char)(id).make_byte[2] | ((unsigned char)(id).make_byte[3] << 8)))
#define FIELD_AVX(id) ((unsigned char)(id).make_byte[4])
#define FIELD_AVX10(id) ((unsigned char)(id).make_byte[5])
#define FIELD_UARCH(id) ((unsigned char)(id).make_byte[6])
/* Portable popcount for a 16-bit value. */
static int popcount16(uint16_t v)
{
int c = 0;
while (v) {
v &= v - 1;
c++;
}
return c;
}
/* Map AVX512_IMPL 2-bit field to a strength ordinal for tie-breaking.
* Index = impl value (0=UNKNOWN, 1=DUAL, 2=NATIVE, 3=reserved).
* Strength: DUAL(0) < UNKNOWN(1) < NATIVE(2). */
static const int impl_strength_table[4] = { 1, 0, 2, 1 };
static int impl_strength(uint16_t avx512)
{
unsigned impl = (avx512 >> SIMDID_AVX512_IMPL_SHIFT) & 0x3u;
return impl_strength_table[impl];
}
/* -----------------------------------------------------------------------
* nfn_get_unite_simd
*
* Produce the conservative intersection of two SIMDIDs — the strongest
* capability set that is safe to use on BOTH cores:
* - MainLevel : min (only assume what both cores support)
* - AVX512 features : bitwise AND (IMPL field: pick the weaker one)
* - AVX features : bitwise AND
* - AVX10 version : min; discrete features: bitwise AND
* - Root / Manu : keep left (must be same arch in practice)
* - uArchHi : bitwise OR (HYBRID_P/E union; SIMD_DUAL set if either core is dual)
* ----------------------------------------------------------------------- */
NFN_API SIMDID NFN_DECL nfn_get_unite_simd(SIMDID left, SIMDID right)
{
SIMDID out;
out.mask = 0;
/* Root + Manufacture (byte 0): prefer left (same arch assumed) */
unsigned char lb0 = (unsigned char)left.make_byte[0];
unsigned char rb0 = (unsigned char)right.make_byte[0];
out.make_byte[0] = (char)(lb0 ? lb0 : rb0);
/* MainLevel (byte 1): min */
unsigned char lm = FIELD_MAIN(left);
unsigned char rm = FIELD_MAIN(right);
out.make_byte[1] = (char)(lm < rm ? lm : rm);
/* AVX512 features (bytes 2-3): AND all bits, then fix IMPL to weaker */
uint16_t l512 = FIELD_AVX512(left);
uint16_t r512 = FIELD_AVX512(right);
uint16_t merged = l512 & r512;
/* Clear the IMPL field, then set the weaker one */
int ls = impl_strength(l512);
int rs = impl_strength(r512);
int s = (ls <= rs) ? l512 : r512; /* pick the one with weaker strength ordinal */
unsigned weak_impl = (s >> SIMDID_AVX512_IMPL_SHIFT) & 0x3u;
merged &= ~(uint16_t)SIMDID_AVX512_IMPL_MASK;
merged |= (uint16_t)(weak_impl << SIMDID_AVX512_IMPL_SHIFT);
out.make_byte[2] = (char)(merged & 0xFF);
out.make_byte[3] = (char)((merged >> 8) & 0xFF);
/* AVX (byte 4): AND */
out.make_byte[4] = left.make_byte[4] & right.make_byte[4];
/* AVX10 (byte 5): minimum version and common discrete features. */
unsigned char lavx10 = FIELD_AVX10(left);
unsigned char ravx10 = FIELD_AVX10(right);
unsigned char lver = lavx10 & SIMDID_AVX10_VERSION_MASK;
unsigned char rver = ravx10 & SIMDID_AVX10_VERSION_MASK;
unsigned char common = (lavx10 & ravx10) & SIMDID_AVX10_VNNI_INT;
out.make_byte[5] = (char)((lver < rver ? lver : rver) | common);
/* uArchHi (byte 6):
* topology bits [1..0]: AND — conservative intersection (weaker topology wins)
* SIMD_DUAL bit [2]: OR — dual if either core is dual */
unsigned char lu = FIELD_UARCH(left);
unsigned char ru = FIELD_UARCH(right);
unsigned char topo = (lu & 0x3u) & (ru & 0x3u);
unsigned char dual = (lu | ru) & (unsigned char)SIMDID_UARCH_SIMD_DUAL;
out.make_byte[6] = (char)(topo | dual);
/* Reserved tail byte. */
out.make_byte[7] = 0;
return out;
}
/* -----------------------------------------------------------------------
* nfn_compare_simd
*
* Compare two SIMDIDs by ISA strength. Returns:
* +1 if left is strictly stronger than right
* -1 if left is strictly weaker than right
* 0 if equal
*
* Comparison priority (most significant first):
*
* 1. MainLevel ([15..8])
* Higher value = stronger. This is the primary discriminator:
* NONE < SSE < AVX2 < AVX512 < AVX10
*
* 2. AVX10 version and discrete features ([47..40])
* Only meaningful when both MainLevel == AVX10.
* Higher versions are stronger; VNNI_INT is a tie-breaker.
*
* 3. AVX512 BASE bit (bit 4 of [31..16])
* Only meaningful when both MainLevel >= AVX512.
* BASE=1 (F+CD+VL+DQ+BW all present) > BASE=0.
*
* 4. AVX512 extension popcount
* VNNI is currently the only tracked AVX512 extension.
* The IMPL and BASE bits are masked out before counting so they don't
* inflate the extension count.
*
* 5. AVX512 IMPL strength (tie-break, performance hint)
* DUAL(weakest) < UNKNOWN < NATIVE(strongest).
* This is not ISA capability but execution-width risk.
*
* 6. uArchHi topology bits [1..0]
* Numeric value: unified(11=3) > P-core(10=2) > E-core(01=1) > unknown(00=0).
* Higher value = stronger / higher-capability core form.
*
* 7. uArchHi SIMD_DUAL (final tie-break)
* Non-dual (SIMD_DUAL=0) > dual (SIMD_DUAL=1).
* ----------------------------------------------------------------------- */
NFN_API int NFN_DECL nfn_compare_simd(SIMDID left, SIMDID right)
{
/* 1. MainLevel */
unsigned lm = FIELD_MAIN(left);
unsigned rm = FIELD_MAIN(right);
if (lm != rm)
return lm > rm ? 1 : -1;
/* 2. AVX10 version and discrete features (only at AVX10). */
if (lm >= SIMDID_MAIN_LVL_AVX10) {
unsigned la = FIELD_AVX10(left);
unsigned ra = FIELD_AVX10(right);
unsigned lv = la & SIMDID_AVX10_VERSION_MASK;
unsigned rv = ra & SIMDID_AVX10_VERSION_MASK;
if (lv != rv)
return lv > rv ? 1 : -1;
int lvnni = (la & SIMDID_AVX10_VNNI_INT) != 0;
int rvnni = (ra & SIMDID_AVX10_VNNI_INT) != 0;
if (lvnni != rvnni)
return lvnni ? 1 : -1;
}
/* 3. AVX512 BASE (only when MainLevel >= AVX512) */
if (lm >= SIMDID_MAIN_LVL_AVX512) {
uint16_t l512 = FIELD_AVX512(left);
uint16_t r512 = FIELD_AVX512(right);
int lb = (l512 & SIMDID_AVX512_BASE) != 0;
int rb = (r512 & SIMDID_AVX512_BASE) != 0;
if (lb != rb)
return lb > rb ? 1 : -1;
// 4. Extension popcount (mask out BASE + IMPL + reserved low nibble)
int drop = ~(SIMDID_AVX512_BASE | SIMDID_AVX512_IMPL_MASK | 0x000Fu);
uint16_t ext_mask = drop & (l512 | r512); /* only consider bits set in either */
int lp = popcount16(l512 & ext_mask);
int rp = popcount16(r512 & ext_mask);
if (lp != rp)
return lp > rp ? 1 : -1;
/* 5. IMPL strength */
int ls = impl_strength(l512);
int rs = impl_strength(r512);
if (ls != rs)
return ls > rs ? 1 : -1;
}
/* 6. uArchHi topology bits [1..0]: unified(3) > P(2) > E(1) > unknown(0) */
unsigned lu = FIELD_UARCH(left);
unsigned ru = FIELD_UARCH(right);
unsigned lt = lu & 0x3u;
unsigned rt = ru & 0x3u;
if (lt != rt)
return lt > rt ? 1 : -1;
/* 7. uArchHi SIMD_DUAL: non-dual (SIMD_DUAL=0) > dual (SIMD_DUAL=1) */
int ld = (lu & SIMDID_UARCH_SIMD_DUAL) != 0;
int rd = (ru & SIMDID_UARCH_SIMD_DUAL) != 0;
if (ld != rd)
return ld ? -1 : 1; /* non-dual is stronger */
return 0;
}
#endif
+332
View File
@@ -0,0 +1,332 @@
#include "nfn_target.h"
#if NFN_IS_ARCH(NFN_ARCH_X86_64)
#include "nfn_intrinsic.h"
#include "nfn_intrinsic.std.h"
#include "nfn_intrinsic.context.x86.h"
#define _FMA_SELECT(i) _MM_SHUFFLE(i, i, i, i)
#define GET_INDEX(col_len, x, y) (col_len * x + y)
// float
NFN_SK_PROC_DECL_STD(float, sse, , fma)
{
__sk_param_std(float);
register __m128 c0 = _mm_load_ps((float *)&ans->x[0]);
register __m128 c1 = _mm_load_ps((float *)&ans->x[1]);
register __m128 c2 = _mm_load_ps((float *)&ans->x[2]);
register __m128 c3 = _mm_load_ps((float *)&ans->x[3]);
register __m128 bcol;
const __m128 a0 = _mm_load_ps((float *)&left->x[0]);
const __m128 a1 = _mm_load_ps((float *)&left->x[1]);
const __m128 a2 = _mm_load_ps((float *)&left->x[2]);
const __m128 a3 = _mm_load_ps((float *)&left->x[3]);
/* Column 0 */
bcol = _mm_load_ps((float *)&right->x[0]);
c0 = _mm_add_ps(c0, _mm_mul_ps(a0, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(0))));
c0 = _mm_add_ps(c0, _mm_mul_ps(a1, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(1))));
c0 = _mm_add_ps(c0, _mm_mul_ps(a2, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(2))));
c0 = _mm_add_ps(c0, _mm_mul_ps(a3, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(3))));
/* Column 1 */
bcol = _mm_load_ps((float *)&right->x[1]);
c1 = _mm_add_ps(c1, _mm_mul_ps(a0, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(0))));
c1 = _mm_add_ps(c1, _mm_mul_ps(a1, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(1))));
c1 = _mm_add_ps(c1, _mm_mul_ps(a2, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(2))));
c1 = _mm_add_ps(c1, _mm_mul_ps(a3, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(3))));
/* Column 2 */
bcol = _mm_load_ps((float *)&right->x[2]);
c2 = _mm_add_ps(c2, _mm_mul_ps(a0, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(0))));
c2 = _mm_add_ps(c2, _mm_mul_ps(a1, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(1))));
c2 = _mm_add_ps(c2, _mm_mul_ps(a2, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(2))));
c2 = _mm_add_ps(c2, _mm_mul_ps(a3, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(3))));
/* Column 3 */
bcol = _mm_load_ps((float *)&right->x[3]);
c3 = _mm_add_ps(c3, _mm_mul_ps(a0, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(0))));
c3 = _mm_add_ps(c3, _mm_mul_ps(a1, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(1))));
c3 = _mm_add_ps(c3, _mm_mul_ps(a2, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(2))));
c3 = _mm_add_ps(c3, _mm_mul_ps(a3, _mm_shuffle_ps(bcol, bcol, _FMA_SELECT(3))));
_mm_store_ps((float *)&ans->x[0], c0);
_mm_store_ps((float *)&ans->x[1], c1);
_mm_store_ps((float *)&ans->x[2], c2);
_mm_store_ps((float *)&ans->x[3], c3);
}
NFN_SK_PROC_DECL_STD(float, avx, , fma)
{
__sk_param_std(float);
register __m256 c01 = _mm256_load_ps((float *)&ans->y[0]);
register __m256 c23 = _mm256_load_ps((float *)&ans->y[1]);
register __m256 b01 = _mm256_load_ps((float *)&right->y[0]);
register __m256 b23 = _mm256_load_ps((float *)&right->y[1]);
register __m256 a0 = _mm256_broadcast_ps(&left->x[0]);
c01 = _mm256_add_ps(_mm256_mul_ps(a0, b01), c01);
c23 = _mm256_add_ps(_mm256_mul_ps(a0, b23), c23);
register __m256 a1 = _mm256_broadcast_ps(&left->x[1]);
c01 = _mm256_add_ps(_mm256_mul_ps(a1, b01), c01);
c23 = _mm256_add_ps(_mm256_mul_ps(a1, b23), c23);
register __m256 a2 = _mm256_broadcast_ps(&left->x[2]);
c01 = _mm256_add_ps(_mm256_mul_ps(a2, b01), c01);
c23 = _mm256_add_ps(_mm256_mul_ps(a2, b23), c23);
register __m256 a3 = _mm256_broadcast_ps(&left->x[3]);
c01 = _mm256_add_ps(_mm256_mul_ps(a3, b01), c01);
c23 = _mm256_add_ps(_mm256_mul_ps(a3, b23), c23);
_mm256_store_ps((float *)&ans->y[0], c01);
_mm256_store_ps((float *)&ans->y[1], c23);
}
NFN_SK_PROC_DECL_STD(float, avx, _fma, fma)
{
__sk_param_std(float);
register __m256 c01 = _mm256_load_ps((float *)&ans->y[0]);
register __m256 c23 = _mm256_load_ps((float *)&ans->y[1]);
register __m256 b01 = _mm256_load_ps((float *)&right->y[0]);
register __m256 b23 = _mm256_load_ps((float *)&right->y[1]);
register __m256 a0 = _mm256_broadcast_ps(&left->x[0]);
c01 = _mm256_fmadd_ps(a0, b01, c01);
c23 = _mm256_fmadd_ps(a0, b23, c23);
register __m256 a1 = _mm256_broadcast_ps(&left->x[1]);
c01 = _mm256_fmadd_ps(a1, b01, c01);
c23 = _mm256_fmadd_ps(a1, b23, c23);
register __m256 a2 = _mm256_broadcast_ps(&left->x[2]);
c01 = _mm256_fmadd_ps(a2, b01, c01);
c23 = _mm256_fmadd_ps(a2, b23, c23);
register __m256 a3 = _mm256_broadcast_ps(&left->x[3]);
c01 = _mm256_fmadd_ps(a3, b01, c01);
c23 = _mm256_fmadd_ps(a3, b23, c23);
_mm256_store_ps((float *)&ans->y[0], c01);
_mm256_store_ps((float *)&ans->y[1], c23);
}
NFN_SK_PROC_DECL_STD(float, 512, , fma)
{
__sk_param_std(float);
register __m512 c = _mm512_load_ps((float *)ans);
// full right load:[kb_col0 | kb_col1 | kb_col2 | kb_col3]
register __m512 b = _mm512_load_ps((float *)right);
// k=0: broadcast ka_col0 到4个lane,permute_ps 取 kb(0,j)
register __m512 ak;
ak = _mm512_broadcast_f32x4(left->x[0]);
c = _mm512_fmadd_ps(ak, _mm512_permute_ps(b, _FMA_SELECT(0)), c);
// k=1
ak = _mm512_broadcast_f32x4(left->x[1]);
c = _mm512_fmadd_ps(ak, _mm512_permute_ps(b, _FMA_SELECT(1)), c);
// k=2
ak = _mm512_broadcast_f32x4(left->x[2]);
c = _mm512_fmadd_ps(ak, _mm512_permute_ps(b, _FMA_SELECT(2)), c);
// k=3
ak = _mm512_broadcast_f32x4(left->x[3]);
c = _mm512_fmadd_ps(ak, _mm512_permute_ps(b, _FMA_SELECT(3)), c);
_mm512_store_ps((float *)ans, c);
}
// double
// clang-format off
#define DOUBLE_KERNEL_FMA_CYCLE(i) \
do { \
/* k = 0 */ \
register __m128d b_lo = _mm_load_pd(right_values + (2 * i + 0)); \
register __m128d b_hi = _mm_load_pd(right_values + (2 * i + 1)); \
register __m128d a_lo = _mm_load_pd(left_values + (0 * 2 + 0)); \
register __m128d a_hi = _mm_load_pd(left_values + (0 * 2 + 1)); \
register __m128d b = _mm_shuffle_pd(b_lo, b_lo, 0x0); \
c##i##_lo = _mm_add_pd(c##i##_lo, _mm_mul_pd(a_lo, b)); \
c##i##_hi = _mm_add_pd(c##i##_hi, _mm_mul_pd(a_hi, b)); \
\
/* group = 1 */ \
a_lo = _mm_load_pd(left_values + (1 * 2 + 0)); \
a_hi = _mm_load_pd(left_values + (1 * 2 + 1)); \
b = _mm_shuffle_pd(b_lo, b_lo, 0x3); \
c##i##_lo = _mm_add_pd(c##i##_lo, _mm_mul_pd(a_lo, b)); \
c##i##_hi = _mm_add_pd(c##i##_hi, _mm_mul_pd(a_hi, b)); \
\
/* groupp = 2 */ \
a_lo = _mm_load_pd(left_values + (2 * 2 + 0)); \
a_hi = _mm_load_pd(left_values + (2 * 2 + 1)); \
b = _mm_shuffle_pd(b_hi, b_hi, 0x0); \
c##i##_lo = _mm_add_pd(c##i##_lo, _mm_mul_pd(a_lo, b)); \
c##i##_hi = _mm_add_pd(c##i##_hi, _mm_mul_pd(a_hi, b)); \
\
/* froup = 3 */ \
a_lo = _mm_load_pd(left_values + (3 * 2 + 0)); \
a_hi = _mm_load_pd(left_values + (3 * 2 + 1)); \
b = _mm_shuffle_pd(b_hi, b_hi, 0x3); \
c##i##_lo = _mm_add_pd(c##i##_lo, _mm_mul_pd(a_lo, b)); \
c##i##_hi = _mm_add_pd(c##i##_hi, _mm_mul_pd(a_hi, b)); \
} while (0)
// clang-format on
NFN_SK_PROC_DECL_STD(double, sse, , fma)
{
__sk_param_std(double);
const double *left_values = &left->m[0][0];
const double *right_values = &right->m[0][0];
register __m128d c0_lo = ans->x[0];
register __m128d c0_hi = ans->x[1];
register __m128d c1_lo = ans->x[2];
register __m128d c1_hi = ans->x[3];
register __m128d c2_lo = ans->x[4];
register __m128d c2_hi = ans->x[5];
register __m128d c3_lo = ans->x[6];
register __m128d c3_hi = ans->x[7];
DOUBLE_KERNEL_FMA_CYCLE(0);
DOUBLE_KERNEL_FMA_CYCLE(1);
DOUBLE_KERNEL_FMA_CYCLE(2);
DOUBLE_KERNEL_FMA_CYCLE(3);
ans->x[0] = c0_lo;
ans->x[1] = c0_hi;
ans->x[2] = c1_lo;
ans->x[3] = c1_hi;
ans->x[4] = c2_lo;
ans->x[5] = c2_hi;
ans->x[6] = c3_lo;
ans->x[7] = c3_hi;
}
// clang-format off
NFN_SK_PROC_DECL_STD(double, avx, , fma)
{
__sk_param_std(double);
register __m256d c0 = _mm256_load_pd((double *)&ans->y[0]);
register __m256d c1 = _mm256_load_pd((double *)&ans->y[1]);
register __m256d c2 = _mm256_load_pd((double *)&ans->y[2]);
register __m256d c3 = _mm256_load_pd((double *)&ans->y[3]);
register __m256d bcol;
const __m256d a0 = _mm256_load_pd((double *)&left->y[0]);
const __m256d a1 = _mm256_load_pd((double *)&left->y[1]);
const __m256d a2 = _mm256_load_pd((double *)&left->y[2]);
const __m256d a3 = _mm256_load_pd((double *)&left->y[3]);
/* Column 0 */
bcol = _mm256_load_pd((double *)&right->y[0]);
c0 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c0);
c0 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c0);
c0 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c0);
c0 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c0);
/* Column 1 */
bcol = _mm256_load_pd((double *)&right->y[1]);
c1 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c1);
c1 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c1);
c1 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c1);
c1 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c1);
/* Column 2 */
bcol = _mm256_load_pd((double *)&right->y[2]);
c2 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c2);
c2 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c2);
c2 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c2);
c2 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c2);
/* Column 3 */
bcol = _mm256_load_pd((double *)&right->y[3]);
c3 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c3);
c3 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c3);
c3 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c3);
c3 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c3);
_mm256_store_pd((double *)&ans->y[0], c0);
_mm256_store_pd((double *)&ans->y[1], c1);
_mm256_store_pd((double *)&ans->y[2], c2);
_mm256_store_pd((double *)&ans->y[3], c3);
}
// clang-format on
NFN_SK_PROC_DECL_STD(double, avx, _fma, fma)
{
__sk_param_std(double);
register __m256d c0 = _mm256_load_pd((double *)&ans->y[0]);
register __m256d c1 = _mm256_load_pd((double *)&ans->y[1]);
register __m256d c2 = _mm256_load_pd((double *)&ans->y[2]);
register __m256d c3 = _mm256_load_pd((double *)&ans->y[3]);
register __m256d bcol;
const __m256d a0 = _mm256_load_pd((double *)&left->y[0]);
const __m256d a1 = _mm256_load_pd((double *)&left->y[1]);
const __m256d a2 = _mm256_load_pd((double *)&left->y[2]);
const __m256d a3 = _mm256_load_pd((double *)&left->y[3]);
/* Column 0 */
bcol = _mm256_load_pd((double *)&right->y[0]);
c0 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c0);
c0 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c0);
c0 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c0);
c0 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c0);
/* Column 1 */
bcol = _mm256_load_pd((double *)&right->y[1]);
c1 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c1);
c1 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c1);
c1 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c1);
c1 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c1);
/* Column 2 */
bcol = _mm256_load_pd((double *)&right->y[2]);
c2 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c2);
c2 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c2);
c2 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c2);
c2 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c2);
/* Column 3 */
bcol = _mm256_load_pd((double *)&right->y[3]);
c3 = _mm256_fmadd_pd(a0, _mm256_permute4x64_pd(bcol, _FMA_SELECT(0)), c3);
c3 = _mm256_fmadd_pd(a1, _mm256_permute4x64_pd(bcol, _FMA_SELECT(1)), c3);
c3 = _mm256_fmadd_pd(a2, _mm256_permute4x64_pd(bcol, _FMA_SELECT(2)), c3);
c3 = _mm256_fmadd_pd(a3, _mm256_permute4x64_pd(bcol, _FMA_SELECT(3)), c3);
_mm256_store_pd((double *)&ans->y[0], c0);
_mm256_store_pd((double *)&ans->y[1], c1);
_mm256_store_pd((double *)&ans->y[2], c2);
_mm256_store_pd((double *)&ans->y[3], c3);
}
NFN_SK_PROC_DECL_STD(double, 512, , fma)
{
__sk_param_std(double);
register __m512d c01 = _mm512_load_pd((double *)&ans->z[0]);
register __m512d c23 = _mm512_load_pd((double *)&ans->z[1]);
register __m512d a01 = _mm512_load_pd((double *)&left->z[0]);
register __m512d a23 = _mm512_load_pd((double *)&left->z[1]);
register __m512d b0 = _mm512_broadcast_f64x4(right->y[0]);
c01 = _mm512_fmadd_pd(a01, b0, c01);
c23 = _mm512_fmadd_pd(a23, b0, c23);
register __m512d b1 = _mm512_broadcast_f64x4(right->y[1]);
c01 = _mm512_fmadd_pd(a01, b1, c01);
c23 = _mm512_fmadd_pd(a23, b1, c23);
register __m512d b2 = _mm512_broadcast_f64x4(right->y[2]);
c01 = _mm512_fmadd_pd(a01, b2, c01);
c23 = _mm512_fmadd_pd(a23, b2, c23);
register __m512d b3 = _mm512_broadcast_f64x4(right->y[3]);
c01 = _mm512_fmadd_pd(a01, b3, c01);
c23 = _mm512_fmadd_pd(a23, b3, c23);
_mm512_store_pd((double *)&ans->z[0], c01);
_mm512_store_pd((double *)&ans->z[1], c23);
}
#endif
+327
View File
@@ -0,0 +1,327 @@
#define _GNU_SOURCE
#include "nfn_target.h"
#if NFN_IS_OS(NFN_OS_LINUX)
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <linux/mempolicy.h>
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
typedef int (*nfn_linux_id_visitor)(int value, void *context);
typedef struct nfn_linux_counter_context {
int count;
} nfn_linux_counter_context;
typedef struct nfn_linux_max_context {
int max_value;
} nfn_linux_max_context;
typedef struct nfn_linux_fill_cpu_context {
uint64_t *internal;
uint32_t *global;
int index;
} nfn_linux_fill_cpu_context;
typedef struct nfn_linux_fill_node_count_context {
int *count_of_each_numa;
} nfn_linux_fill_node_count_context;
static int nfn_linux_read_first_line(char const *path, char *buffer, size_t buffer_size)
{
FILE *file = fopen(path, "r");
if (file == NULL)
return 0;
char *line = fgets(buffer, (int)buffer_size, file);
fclose(file);
return line != NULL;
}
static int nfn_linux_parse_nonnegative_int(char const **cursor, int *value)
{
char *end = NULL;
uintmax_t parsed_value = 0;
if (**cursor == '-')
return 0;
errno = 0;
parsed_value = strtoumax(*cursor, &end, 10);
if (end == *cursor || errno == ERANGE || parsed_value > (uintmax_t)INT_MAX)
return 0;
*cursor = end;
*value = (int)parsed_value;
return 1;
}
static int nfn_linux_visit_id_list(char const *text, nfn_linux_id_visitor visitor, void *context)
{
char const *cursor = text;
while (*cursor != '\0') {
while (*cursor == ' ' || *cursor == '\t' || *cursor == '\n' || *cursor == ',')
cursor++;
if (*cursor == '\0')
break;
int begin = 0;
if (!nfn_linux_parse_nonnegative_int(&cursor, &begin))
return 0;
int finish = begin;
if (*cursor == '-') {
cursor++;
if (!nfn_linux_parse_nonnegative_int(&cursor, &finish))
return 0;
}
if (finish < begin)
return 0;
for (int value = begin;; value++) {
if (!visitor(value, context))
return 0;
if (value == finish)
break;
}
}
return 1;
}
static int nfn_linux_count_id(int value, void *context)
{
(void)value;
((nfn_linux_counter_context *)context)->count++;
return 1;
}
static int nfn_linux_track_max_id(int value, void *context)
{
nfn_linux_max_context *max_context = (nfn_linux_max_context *)context;
if (value > max_context->max_value)
max_context->max_value = value;
return 1;
}
static int nfn_linux_fill_cpu_id(int value, void *context)
{
nfn_linux_fill_cpu_context *fill_context = (nfn_linux_fill_cpu_context *)context;
if (fill_context->internal != NULL)
fill_context->internal[fill_context->index] = (uint64_t)fill_context->index;
if (fill_context->global != NULL)
fill_context->global[fill_context->index] = (uint32_t)value;
fill_context->index++;
return 1;
}
static int nfn_linux_count_cpu_list(char const *path)
{
char buffer[256];
if (!nfn_linux_read_first_line(path, buffer, sizeof(buffer)))
return 0;
nfn_linux_counter_context counter = { 0 };
if (!nfn_linux_visit_id_list(buffer, nfn_linux_count_id, &counter))
return 0;
return counter.count;
}
static int nfn_linux_fill_node_cpu_count(int value, void *context)
{
char path[128];
snprintf(path, sizeof(path), "/sys/devices/system/node/node%d/cpulist", value);
((nfn_linux_fill_node_count_context *)context)->count_of_each_numa[value] =
nfn_linux_count_cpu_list(path);
return 1;
}
static int nfn_linux_node_exists(int numa_id)
{
char path[128];
snprintf(path, sizeof(path), "/sys/devices/system/node/node%d", numa_id);
return access(path, F_OK) == 0;
}
static size_t nfn_linux_round_up_to_page_size(size_t size)
{
int64_t page_size_value = (int64_t)sysconf(_SC_PAGESIZE);
size_t effective_page_size = (page_size_value > 0) ? (size_t)page_size_value : 4096u;
size_t rounded = size + effective_page_size - 1;
if (rounded < size)
return 0;
return rounded - (rounded % effective_page_size);
}
static int nfn_linux_bind_memory_to_node(void *address, size_t size, int numa_id, uint32_t flags)
{
#if defined(SYS_mbind)
size_t bits_per_word = sizeof(unsigned long) * 8u;
size_t word_count = ((size_t)numa_id / bits_per_word) + 1u;
unsigned long *node_mask = (unsigned long *)calloc(word_count, sizeof(unsigned long));
if (node_mask == NULL)
return 0;
node_mask[(size_t)numa_id / bits_per_word] = 1ul << ((size_t)numa_id % bits_per_word);
int syscall_result = (int)syscall(SYS_mbind, address, size, MPOL_BIND | MPOL_F_STATIC_NODES,
node_mask, (unsigned long)numa_id + 1ul, flags);
free(node_mask);
return syscall_result == 0;
#else
(void)address;
(void)size;
(void)numa_id;
(void)flags;
return 0;
#endif
}
static int nfn_linux_populate_writable_mapping(void *address, size_t size)
{
#if defined(MADV_POPULATE_WRITE)
if (madvise(address, size, MADV_POPULATE_WRITE) == 0)
return 1;
return errno == EINVAL;
#else
(void)address;
(void)size;
return 1;
#endif
}
/* Pin the calling thread to a single logical processor.
* core_id >= 0 : bind to that logical processor (global index, across groups).
* core_id < 0 : clear affinity constraint within the current group. */
NFN_API void NFN_DECL nfn_set_thread_affinity(int core_id, int numa_id)
{
int cpu_count = (int)sysconf(_SC_NPROCESSORS_ONLN);
if (cpu_count <= 0)
return;
cpu_set_t *set = CPU_ALLOC(cpu_count);
size_t size = CPU_ALLOC_SIZE(cpu_count);
if (set == NULL)
return;
CPU_ZERO_S(size, set);
if (core_id < 0) {
for (int i = 0; i < cpu_count; i++)
CPU_SET_S((int)i, size, set);
} else {
CPU_SET_S(core_id, size, set);
}
pthread_setaffinity_np(pthread_self(), size, set);
CPU_FREE(set);
}
NFN_API int NFN_DECL nfn_get_numa_overall(int *out_numa_count, int *count_of_each_numa)
{
char buffer[256];
if (!nfn_linux_read_first_line("/sys/devices/system/node/online", buffer, sizeof(buffer)))
return 0;
nfn_linux_max_context max_context = { -1 };
if (!nfn_linux_visit_id_list(buffer, nfn_linux_track_max_id, &max_context) ||
max_context.max_value < 0)
return 0;
if (out_numa_count != NULL)
*out_numa_count = max_context.max_value + 1;
if (count_of_each_numa != NULL) {
for (int index = 0; index <= max_context.max_value; index++)
count_of_each_numa[index] = 0;
nfn_linux_fill_node_count_context fill_context = { count_of_each_numa };
if (!nfn_linux_visit_id_list(buffer, nfn_linux_fill_node_cpu_count, &fill_context))
return 0;
}
return 1;
}
NFN_API int NFN_DECL nfn_get_numa_each(int numa_id, uint64_t *internal, uint32_t *global)
{
if (numa_id < 0)
return 0;
char path[128];
char buffer[256];
snprintf(path, sizeof(path), "/sys/devices/system/node/node%d/cpulist", numa_id);
if (!nfn_linux_read_first_line(path, buffer, sizeof(buffer)))
return 0;
nfn_linux_fill_cpu_context fill_context = { internal, global, 0 };
return nfn_linux_visit_id_list(buffer, nfn_linux_fill_cpu_id, &fill_context);
}
NFN_API void *NFN_DECL nfn_numa_alloc(size_t size, int numa_id)
{
size_t allocation_size = nfn_linux_round_up_to_page_size(size);
if (allocation_size == 0)
return NULL;
int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
#if defined(MAP_POPULATE)
if (numa_id >= 0)
mmap_flags |= MAP_POPULATE;
#endif
void *memory = mmap(NULL, allocation_size, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
if (memory == MAP_FAILED)
return NULL;
if (numa_id < 0)
return memory;
uint32_t bind_flags = 0u;
#if defined(MAP_POPULATE)
bind_flags = MPOL_MF_MOVE | MPOL_MF_STRICT;
#endif
if (!nfn_linux_node_exists(numa_id) ||
!nfn_linux_bind_memory_to_node(memory, allocation_size, numa_id, bind_flags) ||
!nfn_linux_populate_writable_mapping(memory, allocation_size)) {
munmap(memory, allocation_size);
return NULL;
}
return memory;
}
NFN_API void NFN_DECL nfn_numa_free(void *ptr, size_t size, int numa_id)
{
(void)numa_id;
if (ptr == NULL || size == 0)
return;
size_t allocation_size = nfn_linux_round_up_to_page_size(size);
if (allocation_size != 0)
munmap(ptr, allocation_size);
}
#endif
@@ -0,0 +1,95 @@
#include "nfn_target.h"
#include "NitloadFrameWork.h"
#if NFN_IS_OS(NFN_OS_WINDOWS)
/* Pin the calling thread to a single logical processor.
* core_id >= 0 : bind to that logical processor (global index, across groups).
* core_id < 0 : clear affinity constraint within the current group. */
NFN_API void NFN_DECL nfn_set_thread_affinity(int core_id, int numa_id)
{
if (core_id < 0 || numa_id < 0) {
/* Query which group the thread currently belongs to */
GROUP_AFFINITY current;
GetThreadGroupAffinity(GetCurrentThread(), &current);
/* Build a full mask for that group's actual core count */
DWORD count = GetActiveProcessorCount(current.Group);
GROUP_AFFINITY ga;
memset(&ga, 0, sizeof(ga));
ga.Group = current.Group;
ga.Mask = (count >= 64) ? ~(KAFFINITY)0 : ((KAFFINITY)1 << count) - 1;
SetThreadGroupAffinity(GetCurrentThread(), &ga, NULL);
return;
}
int index = core_id % 64;
DWORD count = GetActiveProcessorCount(numa_id);
if ((DWORD)index >= count)
return;
GROUP_AFFINITY ga;
memset(&ga, 0, sizeof(ga));
ga.Group = (WORD)numa_id;
ga.Mask = (KAFFINITY)1 << index;
SetThreadGroupAffinity(GetCurrentThread(), &ga, NULL);
}
NFN_API int NFN_DECL nfn_get_numa_overall(int *out_numa_count, int *count_of_each_numa)
{
ULONG highest;
if (!GetNumaHighestNodeNumber(&highest))
return 0;
*out_numa_count = (int)highest + 1;
for (UCHAR n = 0; n <= (UCHAR)highest; n++) {
ULONGLONG mask = 0;
GetNumaNodeProcessorMask(n, &mask);
count_of_each_numa[n] = (int)__popcnt64(mask);
}
return 1;
}
NFN_API int NFN_DECL nfn_get_numa_each(int numa_id, uint64_t *internal, uint32_t *global)
{
ULONGLONG mask = 0;
if (!GetNumaNodeProcessorMask((UCHAR)numa_id, &mask))
return 0;
int intra = 0;
while (mask) {
DWORD idx;
_BitScanForward64(&idx, mask);
if (internal != NULL)
internal[intra] = (uint64_t)intra;
global[intra] = (uint32_t)idx;
intra++;
mask &= mask - 1;
}
return 1;
}
NFN_API void *NFN_DECL nfn_numa_alloc(size_t size, int numa_id)
{
if (size == 0)
return NULL;
if (numa_id < 0)
return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
ULONG highest = 0;
if (!GetNumaHighestNodeNumber(&highest) || (ULONG)numa_id > highest)
return NULL;
return VirtualAllocExNuma(GetCurrentProcess(), NULL, size, MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE, (DWORD)numa_id);
}
NFN_API void NFN_DECL nfn_numa_free(void *ptr, size_t size, int numa_id)
{
(void)size;
(void)numa_id;
if (ptr != NULL)
VirtualFree(ptr, 0, MEM_RELEASE);
}
#endif
+32
View File
@@ -0,0 +1,32 @@
#include "nfn_intrinsic.h"
#include "nfn_internal.h"
INTERNAL void NFN_DECL f32_normalize(VEC_SEGMENT(float) *restrict source, VEC_SEGMENT(float) *restrict target)
{
register __m128 s = source->f32_x[0];
register __m128 a_length_sq = _mm_mul_ps(s, s);
a_length_sq = _mm_add_ps(a_length_sq,
_mm_shuffle_ps(a_length_sq, a_length_sq, _MM_SHUFFLE(2, 3, 0, 1)));
a_length_sq = _mm_add_ss(a_length_sq,
_mm_shuffle_ps(a_length_sq, a_length_sq, _MM_SHUFFLE(0, 0, 3, 2)));
__m128 a_inv_length = _mm_rsqrt_ps(a_length_sq);
s = _mm_mul_ps(s, a_inv_length);
__m128 dot_product = _mm_mul_ps(target->f32_x[0], s);
dot_product = _mm_add_ps(dot_product,
_mm_shuffle_ps(dot_product, dot_product, _MM_SHUFFLE(2, 3, 0, 1)));
dot_product = _mm_add_ss(dot_product,
_mm_shuffle_ps(dot_product, dot_product, _MM_SHUFFLE(0, 0, 3, 2)));
__m128 b_orthogonal = _mm_sub_ps(target->f32_x[0], _mm_mul_ps(s, dot_product));
__m128 b_length_sq = _mm_mul_ps(b_orthogonal, b_orthogonal);
b_length_sq = _mm_add_ps(b_length_sq,
_mm_shuffle_ps(b_length_sq, b_length_sq, _MM_SHUFFLE(2, 3, 0, 1)));
b_length_sq = _mm_add_ss(b_length_sq,
_mm_shuffle_ps(b_length_sq, b_length_sq, _MM_SHUFFLE(0, 0, 3, 2)));
__m128 b_inv_length = _mm_rsqrt_ps(b_length_sq);
target->f32_x[0] = _mm_mul_ps(b_orthogonal, b_inv_length);
}
+67
View File
@@ -0,0 +1,67 @@
#include "nfn_intrinsic.h"
#include "nfn_internal.h"
static const int avxdot_indices[] = { 0, 4, 1, 5, 2, 3, 6, 7 };
INTERNAL void FORCEINLINE NFN_DECL dot_41_sse(MAT_KERNEL(float) const *transpose,
__m128 const *vector, __m128 *ans)
{
*ans = _mm_dp_ps(transpose->x[0], *vector, 0B11110001);
*ans = _mm_insert_ps(*ans, _mm_dp_ps(transpose->x[1], *vector, 0B11110001),
SSE_INSERT_SIMPLE(1, 1));
*ans = _mm_insert_ps(*ans, _mm_dp_ps(transpose->x[2], *vector, 0B11110001),
SSE_INSERT_SIMPLE(1, 3));
*ans = _mm_insert_ps(*ans, _mm_dp_ps(transpose->x[3], *vector, 0B11110001),
SSE_INSERT_SIMPLE(1, 4));
}
INTERNAL void NFN_DECL dot_41_avx(MAT_KERNEL(float) const *transpose, __m128 const *vector,
__m128 *ans)
{
__m256 _double = _mm256_set_m128(vector[0], vector[0]);
__m256 tmp = _mm256_dp_ps(transpose->y[0], _double, 0B11110001);
tmp = _mm256_blend_ps(tmp, _mm256_dp_ps(transpose->y[0], _double, 0B11110010),
0B00100010);
*ans = _mm256_castps256_ps128(
_mm256_permutevar8x32_ps(tmp, _mm256_loadu_epi32(avxdot_indices)));
}
INTERNAL void NFN_DECL dot_42_sse(MAT_KERNEL(float) const *transpose, __m128 const *vector,
__m128 *ans)
{
ans[0] = _mm_dp_ps(transpose->x[0], vector[0], 0B11110001);
ans[0] = _mm_insert_ps(*ans, _mm_dp_ps(transpose->x[1], vector[0], 0B11110001),
SSE_INSERT_SIMPLE(1, 2));
ans[0] = _mm_insert_ps(*ans, _mm_dp_ps(transpose->x[2], vector[0], 0B11110001),
SSE_INSERT_SIMPLE(1, 3));
ans[0] = _mm_insert_ps(*ans, _mm_dp_ps(transpose->x[3], vector[0], 0B11110001),
SSE_INSERT_SIMPLE(1, 4));
ans[1] = _mm_dp_ps(transpose->x[0], vector[1], 0B11110001);
ans[1] = _mm_insert_ps(*ans, _mm_dp_ps(transpose->x[1], vector[1], 0B11110001),
SSE_INSERT_SIMPLE(1, 2));
ans[1] = _mm_insert_ps(*ans, _mm_dp_ps(transpose->x[2], vector[1], 0B11110001),
SSE_INSERT_SIMPLE(1, 3));
ans[1] = _mm_insert_ps(*ans, _mm_dp_ps(transpose->x[3], vector[1], 0B11110001),
SSE_INSERT_SIMPLE(1, 4));
}
INTERNAL void NFN_DECL dot_42_avx(MAT_KERNEL(float) const *transpose, __m128 const *vector,
__m128 *ans)
{
__m256 temp = transpose->y[0];
__m256 vector_stream = _mm256_set_m128(vector[1], vector[0]);
__m256 result = _mm256_dp_ps(temp, vector_stream, 0B11110001);
temp = _mm256_permute2f128_ps(temp, temp, 0B00000001);
result = _mm256_blend_ps(result, _mm256_dp_ps(temp, vector_stream, 0B11110010), 0B00100010);
temp = transpose->y[1];
result = _mm256_blend_ps(result, _mm256_dp_ps(temp, vector_stream, 0B11110100), 0B01000100);
temp = _mm256_permute2f128_ps(temp, temp, 0B00000001);
result = _mm256_blend_ps(result, _mm256_dp_ps(temp, vector_stream, 0B11111000), 0B10001000);
ans[0] = _mm256_castps256_ps128(result);
ans[1] = _mm256_extractf128_ps(result, 1);
ans[1] = _mm_shuffle_ps(ans[1], ans[1], _MM_SHUFFLE(2, 3, 0, 1));
}