Skip to content

Commit

Permalink
BUG: verify the OS supports avx instruction
Browse files Browse the repository at this point in the history
On some systems you can disable avx registers but the gcc builtin does
only checks if the cpu has the feature.
Before using avx functions check the OS support with xgetbv.
Closes gh-10787
Closes gh-9534
  • Loading branch information
juliantaylor committed Mar 29, 2018
1 parent e4d678a commit cde5583
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 18 deletions.
4 changes: 2 additions & 2 deletions numpy/core/code_generators/generate_umath.py
Expand Up @@ -966,7 +966,7 @@ def make_arrays(funcdict):
for vt in t.simd:
code2list.append(textwrap.dedent("""\
#ifdef HAVE_ATTRIBUTE_TARGET_{ISA}
if (NPY_CPU_SUPPORTS_{ISA}) {{
if (npy_cpu_supports("{ISA}")) {{
{fname}_functions[{idx}] = {type}_{fname}_{isa};
}}
#endif
Expand Down Expand Up @@ -1073,7 +1073,7 @@ def make_code(funcdict, filename):
Please make changes to the code generator program (%s)
**/
#include "cpuid.h"
%s
static int
Expand Down
16 changes: 0 additions & 16 deletions numpy/core/include/numpy/npy_common.h
Expand Up @@ -101,22 +101,6 @@
#endif
#endif

#ifdef HAVE___BUILTIN_CPU_SUPPORTS
#ifdef HAVE_ATTRIBUTE_TARGET_AVX2
#define NPY_CPU_SUPPORTS_AVX2 __builtin_cpu_supports("avx2")
#else
#define NPY_CPU_SUPPORTS_AVX2 0
#endif
#ifdef HAVE_ATTRIBUTE_TARGET_AVX
#define NPY_CPU_SUPPORTS_AVX __builtin_cpu_supports("avx")
#else
#define NPY_CPU_SUPPORTS_AVX 0
#endif
#else
#define NPY_CPU_SUPPORTS_AVX 0
#define NPY_CPU_SUPPORTS_AVX2 0
#endif

#if defined(_MSC_VER)
#define NPY_INLINE __inline
#elif defined(__GNUC__)
Expand Down
1 change: 1 addition & 0 deletions numpy/core/setup.py
Expand Up @@ -888,6 +888,7 @@ def generate_umath_c(ext, build_dir):
join('src', 'umath', 'loops.c.src'),
join('src', 'umath', 'ufunc_object.c'),
join('src', 'umath', 'extobj.c'),
join('src', 'umath', 'cpuid.c'),
join('src', 'umath', 'scalarmath.c.src'),
join('src', 'umath', 'ufunc_type_resolution.c'),
join('src', 'umath', 'override.c'),
Expand Down
1 change: 1 addition & 0 deletions numpy/core/setup_common.py
Expand Up @@ -146,6 +146,7 @@ def check_api_version(apiversion, codegen_dir):
"stdio.h", "LINK_AVX"),
("__asm__ volatile", '"vpand %ymm1, %ymm2, %ymm3"',
"stdio.h", "LINK_AVX2"),
("__asm__ volatile", '"xgetbv"', "stdio.h", "XGETBV"),
]

# function attributes
Expand Down
56 changes: 56 additions & 0 deletions numpy/core/src/umath/cpuid.c
@@ -0,0 +1,56 @@
#define _UMATHMODULE
#define NPY_NO_DEPRECATED_API NPY_API_VERSION

#include <Python.h>

#include "npy_config.h"

#define PY_ARRAY_UNIQUE_SYMBOL _npy_umathmodule_ARRAY_API
#define NO_IMPORT_ARRAY

#include "cpuid.h"

#define XCR_XFEATURE_ENABLED_MASK 0x0
#define XSTATE_SSE 0x2
#define XSTATE_YMM 0x4

/*
* verify the OS supports avx instructions
* it can be disabled in some OS, e.g. with the nosavex boot option of linux
*/
static NPY_INLINE
int os_avx_support(void)
{
#if HAVE_XGETBV
/*
* use bytes for xgetbv to avoid issues with compiler not knowing the
* instruction
*/
unsigned int eax, edx;
unsigned int ecx = XCR_XFEATURE_ENABLED_MASK;
__asm__("xgetbv" : "=a" (eax), "=d" (edx) : "c" (ecx));
return (eax & (XSTATE_SSE | XSTATE_YMM)) == (XSTATE_SSE | XSTATE_YMM);
#else
return 0;
#endif
}


/*
* Primitive cpu feature detect function
* Currently only supports checking for avx on gcc compatible compilers.
*/
NPY_NO_EXPORT int
npy_cpu_supports(const char * feature)
{
#ifdef HAVE___BUILTIN_CPU_SUPPORTS
if (strcmp(feature, "avx2") == 0) {
return __builtin_cpu_supports("avx2") && os_avx_support();
}
else if (strcmp(feature, "avx") == 0) {
return __builtin_cpu_supports("avx") && os_avx_support();
}
#endif

return 0;
}
9 changes: 9 additions & 0 deletions numpy/core/src/umath/cpuid.h
@@ -0,0 +1,9 @@
#ifndef _NPY_PRIVATE__CPUID_H_
#define _NPY_PRIVATE__CPUID_H_

#include <numpy/ndarraytypes.h> /* for NPY_NO_EXPORT */

NPY_NO_EXPORT int
npy_cpu_supports(const char * feature);

#endif

0 comments on commit cde5583

Please sign in to comment.