Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH, BLD: Fix math feature detection for wasm #21277

Merged
merged 3 commits into from Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/release/upcoming_changes/21154.improvement.rst
@@ -0,0 +1,7 @@
Math C library feature detection now uses correct signatures
------------------------------------------------------------
Compiling is preceded by a detection phase to determine whether the
underlying libc supports certain math operations. Previously this code
did not respect the proper signatures. Fixing this enables compilation
for the ``wasm-ld`` backend (compilation for web assembly) and reduces
the number of warnings.
1 change: 1 addition & 0 deletions numpy/core/feature_detection_locale.h
@@ -0,0 +1 @@
long double strtold_l(const char*, char**, locale_t);
107 changes: 107 additions & 0 deletions numpy/core/feature_detection_math.h
@@ -0,0 +1,107 @@
double expm1(double);
double log1p(double);
double acosh(double);
double asinh(double);
double atanh(double);
double rint(double);
double trunc(double);
double exp2(double);
double log2(double);
double hypot(double, double);
double atan2(double, double);
double pow(double, double);
double copysign(double, double);
double nextafter(double, double);
long long strtoll(const char*, char**, int);
unsigned long long strtoull(const char*, char**, int);
double cbrt(double);
long double sinl(long double);
long double cosl(long double);
long double tanl(long double);
long double sinhl(long double);
long double coshl(long double);
long double tanhl(long double);
long double fabsl(long double);
long double floorl(long double);
long double ceill(long double);
long double rintl(long double);
long double truncl(long double);
long double sqrtl(long double);
long double log10l(long double);
long double logl(long double);
long double log1pl(long double);
long double expl(long double);
long double expm1l(long double);
long double asinl(long double);
long double acosl(long double);
long double atanl(long double);
long double asinhl(long double);
long double acoshl(long double);
long double atanhl(long double);
long double hypotl(long double, long double);
long double atan2l(long double, long double);
long double powl(long double, long double);
long double fmodl(long double, long double);
long double modfl(long double, long double*);
long double frexpl(long double, int*);
long double ldexpl(long double, int);
long double exp2l(long double);
long double log2l(long double);
long double copysignl(long double, long double);
long double nextafterl(long double, long double);
long double cbrtl(long double);
float sinf(float);
float cosf(float);
float tanf(float);
float sinhf(float);
float coshf(float);
float tanhf(float);
float fabsf(float);
float floorf(float);
float ceilf(float);
float rintf(float);
float truncf(float);
float sqrtf(float);
float log10f(float);
float logf(float);
float log1pf(float);
float expf(float);
float expm1f(float);
float asinf(float);
float acosf(float);
float atanf(float);
float asinhf(float);
float acoshf(float);
float atanhf(float);
float hypotf(float, float);
float atan2f(float, float);
float powf(float, float);
float fmodf(float, float);
float modff(float, float*);
float frexpf(float, int*);
float ldexpf(float, int);
float exp2f(float);
float log2f(float);
float copysignf(float, float);
float nextafterf(float, float);
float cbrtf(float);
double sin(double);
double cos(double);
double tan(double);
double sinh(double);
double cosh(double);
double tanh(double);
double fabs(double);
double floor(double);
double ceil(double);
double sqrt(double);
double log10(double);
double log(double);
double exp(double);
double asin(double);
double acos(double);
double atan(double);
double fmod(double, double);
double modf(double, double*);
double frexp(double, int*);
double ldexp(double, int);
4 changes: 4 additions & 0 deletions numpy/core/feature_detection_misc.h
@@ -0,0 +1,4 @@
#include <stddef.h>

int backtrace(void **, int);
int madvise(void *, size_t, int);
6 changes: 6 additions & 0 deletions numpy/core/feature_detection_stdio.h
@@ -0,0 +1,6 @@
#include <stdio.h>
#include <fcntl.h>

off_t ftello(FILE *stream);
int fseeko(FILE *stream, off_t offset, int whence);
int fallocate(int, int, off_t, off_t);
79 changes: 61 additions & 18 deletions numpy/core/setup.py
Expand Up @@ -125,32 +125,48 @@ def win32_checks(deflist):
deflist.append('FORCE_NO_LONG_DOUBLE_FORMATTING')

def check_math_capabilities(config, ext, moredefs, mathlibs):
def check_func(func_name):
return config.check_func(func_name, libraries=mathlibs,
decl=True, call=True)

def check_funcs_once(funcs_name):
decl = dict([(f, True) for f in funcs_name])
st = config.check_funcs_once(funcs_name, libraries=mathlibs,
decl=decl, call=decl)
def check_func(
func_name,
decl=False,
headers=["feature_detection_math.h"],
):
return config.check_func(
func_name,
libraries=mathlibs,
decl=decl,
call=True,
call_args=FUNC_CALL_ARGS[func_name],
headers=headers,
)

def check_funcs_once(funcs_name, headers=["feature_detection_math.h"]):
call = dict([(f, True) for f in funcs_name])
call_args = dict([(f, FUNC_CALL_ARGS[f]) for f in funcs_name])
st = config.check_funcs_once(
funcs_name,
libraries=mathlibs,
decl=False,
call=call,
call_args=call_args,
headers=headers,
)
if st:
moredefs.extend([(fname2def(f), 1) for f in funcs_name])
return st

def check_funcs(funcs_name):
def check_funcs(funcs_name, headers=["feature_detection_math.h"]):
# Use check_funcs_once first, and if it does not work, test func per
# func. Return success only if all the functions are available
if not check_funcs_once(funcs_name):
if not check_funcs_once(funcs_name, headers=headers):
# Global check failed, check func per func
for f in funcs_name:
if check_func(f):
if check_func(f, headers=headers):
moredefs.append((fname2def(f), 1))
return 0
else:
return 1

#use_msvc = config.check_decl("_MSC_VER")

if not check_funcs_once(MANDATORY_FUNCS):
raise SystemError("One of the required function to build numpy is not"
" available (the list is %s)." % str(MANDATORY_FUNCS))
Expand All @@ -165,15 +181,34 @@ def check_funcs(funcs_name):
for f in OPTIONAL_STDFUNCS_MAYBE:
if config.check_decl(fname2def(f),
headers=["Python.h", "math.h"]):
OPTIONAL_STDFUNCS.remove(f)
if f in OPTIONAL_STDFUNCS:
OPTIONAL_STDFUNCS.remove(f)
else:
OPTIONAL_FILE_FUNCS.remove(f)


check_funcs(OPTIONAL_STDFUNCS)
check_funcs(OPTIONAL_FILE_FUNCS, headers=["feature_detection_stdio.h"])
check_funcs(OPTIONAL_MISC_FUNCS, headers=["feature_detection_misc.h"])



for h in OPTIONAL_HEADERS:
if config.check_func("", decl=False, call=False, headers=[h]):
h = h.replace(".", "_").replace(os.path.sep, "_")
moredefs.append((fname2def(h), 1))

# Try with both "locale.h" and "xlocale.h"
locale_headers = [
"stdlib.h",
"xlocale.h",
"feature_detection_locale.h",
]
if not check_funcs(OPTIONAL_LOCALE_FUNCS, headers=locale_headers):
# It didn't work with xlocale.h, maybe it will work with locale.h?
locale_headers[1] = "locale.h"
check_funcs(OPTIONAL_LOCALE_FUNCS, headers=locale_headers)

for tup in OPTIONAL_INTRINSICS:
headers = None
if len(tup) == 2:
Expand Down Expand Up @@ -394,20 +429,28 @@ def check_types(config_cmd, ext, build_dir):
def check_mathlib(config_cmd):
# Testing the C math library
mathlibs = []
mathlibs_choices = [[], ['m'], ['cpml']]
mathlib = os.environ.get('MATHLIB')
mathlibs_choices = [[], ["m"], ["cpml"]]
mathlib = os.environ.get("MATHLIB")
if mathlib:
mathlibs_choices.insert(0, mathlib.split(','))
mathlibs_choices.insert(0, mathlib.split(","))
for libs in mathlibs_choices:
if config_cmd.check_func("exp", libraries=libs, decl=True, call=True):
if config_cmd.check_func(
"log",
libraries=libs,
call_args="0",
decl="double log(double);",
call=True
):
mathlibs = libs
break
else:
raise RuntimeError(
"math library missing; rerun setup.py after setting the "
"MATHLIB env variable")
"MATHLIB env variable"
)
return mathlibs


def visibility_define(config):
"""Return the define value to use for NPY_VISIBILITY_HIDDEN (may be empty
string)."""
Expand Down
39 changes: 34 additions & 5 deletions numpy/core/setup_common.py
@@ -1,8 +1,9 @@
# Code common to build tools
import sys
import warnings
import copy
import pathlib
import sys
import textwrap
import warnings

from numpy.distutils.misc_util import mingw32

Expand Down Expand Up @@ -101,6 +102,32 @@ def check_api_version(apiversion, codegen_dir):
warnings.warn(msg % (apiversion, curapi_hash, apiversion, api_hash,
__file__),
MismatchCAPIWarning, stacklevel=2)


FUNC_CALL_ARGS = {}

def set_sig(sig):
prefix, _, args = sig.partition("(")
args = args.rpartition(")")[0]
funcname = prefix.rpartition(" ")[-1]
args = [arg.strip() for arg in args.split(",")]
FUNC_CALL_ARGS[funcname] = ", ".join("(%s) 0" % arg for arg in args)


for file in [
"feature_detection_locale.h",
"feature_detection_math.h",
"feature_detection_misc.h",
"feature_detection_stdio.h",
]:
with open(pathlib.Path(__file__).parent / file) as f:
for line in f:
if line.startswith("#"):
continue
if not line.strip():
continue
set_sig(line)

# Mandatory functions: if not found, fail the build
MANDATORY_FUNCS = ["sin", "cos", "tan", "sinh", "cosh", "tanh", "fabs",
"floor", "ceil", "sqrt", "log10", "log", "exp", "asin",
Expand All @@ -110,9 +137,11 @@ def check_api_version(apiversion, codegen_dir):
# replacement implementation. Note that some of these are C99 functions.
OPTIONAL_STDFUNCS = ["expm1", "log1p", "acosh", "asinh", "atanh",
"rint", "trunc", "exp2", "log2", "hypot", "atan2", "pow",
"copysign", "nextafter", "ftello", "fseeko",
"strtoll", "strtoull", "cbrt", "strtold_l", "fallocate",
"backtrace", "madvise"]
"copysign", "nextafter", "strtoll", "strtoull", "cbrt"]

OPTIONAL_LOCALE_FUNCS = ["strtold_l"]
OPTIONAL_FILE_FUNCS = ["ftello", "fseeko", "fallocate"]
OPTIONAL_MISC_FUNCS = ["backtrace", "madvise"]


OPTIONAL_HEADERS = [
Expand Down