|
from __future__ import division, absolute_import, print_function |
|
|
|
|
|
import sys |
|
from os.path import join |
|
import warnings |
|
import copy |
|
import binascii |
|
|
|
from distutils.ccompiler import CompileError |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C_ABI_VERSION = 0x01000009 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C_API_VERSION = 0x00000009 |
|
|
|
class MismatchCAPIWarning(Warning): |
|
pass |
|
|
|
def is_released(config): |
|
"""Return True if a released version of numpy is detected.""" |
|
from distutils.version import LooseVersion |
|
|
|
v = config.get_version('../version.py') |
|
if v is None: |
|
raise ValueError("Could not get version") |
|
pv = LooseVersion(vstring=v).version |
|
if len(pv) > 3: |
|
return False |
|
return True |
|
|
|
def get_api_versions(apiversion, codegen_dir): |
|
"""Return current C API checksum and the recorded checksum for the given |
|
version of the C API version.""" |
|
api_files = [join(codegen_dir, 'numpy_api_order.txt'), |
|
join(codegen_dir, 'ufunc_api_order.txt')] |
|
|
|
|
|
|
|
sys.path.insert(0, codegen_dir) |
|
try: |
|
m = __import__('genapi') |
|
numpy_api = __import__('numpy_api') |
|
curapi_hash = m.fullapi_hash(numpy_api.full_api) |
|
apis_hash = m.get_versions_hash() |
|
finally: |
|
del sys.path[0] |
|
|
|
return curapi_hash, apis_hash[apiversion] |
|
|
|
def check_api_version(apiversion, codegen_dir): |
|
"""Emits a MismacthCAPIWarning if the C API version needs updating.""" |
|
curapi_hash, api_hash = get_api_versions(apiversion, codegen_dir) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not curapi_hash == api_hash: |
|
msg = "API mismatch detected, the C API version " \ |
|
"numbers have to be updated. Current C api version is %d, " \ |
|
"with checksum %s, but recorded checksum for C API version %d in " \ |
|
"codegen_dir/cversions.txt is %s. If functions were added in the " \ |
|
"C API, you have to update C_API_VERSION in %s." |
|
warnings.warn(msg % (apiversion, curapi_hash, apiversion, api_hash, |
|
__file__), |
|
MismatchCAPIWarning) |
|
|
|
MANDATORY_FUNCS = ["sin", "cos", "tan", "sinh", "cosh", "tanh", "fabs", |
|
"floor", "ceil", "sqrt", "log10", "log", "exp", "asin", |
|
"acos", "atan", "fmod", 'modf', 'frexp', 'ldexp'] |
|
|
|
|
|
|
|
OPTIONAL_STDFUNCS = ["expm1", "log1p", "acosh", "asinh", "atanh", |
|
"rint", "trunc", "exp2", "log2", "hypot", "atan2", "pow", |
|
"copysign", "nextafter", "ftello", "fseeko", |
|
"strtoll", "strtoull"] |
|
|
|
|
|
OPTIONAL_HEADERS = [ |
|
|
|
"xmmintrin.h", |
|
"emmintrin.h", |
|
] |
|
|
|
|
|
|
|
|
|
OPTIONAL_INTRINSICS = [("__builtin_isnan", '5.'), |
|
("__builtin_isinf", '5.'), |
|
("__builtin_isfinite", '5.'), |
|
("__builtin_bswap32", '5u'), |
|
("__builtin_bswap64", '5u'), |
|
("__builtin_expect", '5, 0'), |
|
("_mm_load_ps", '(float*)0', "xmmintrin.h"), |
|
("_mm_load_pd", '(double*)0', "emmintrin.h"), |
|
] |
|
|
|
|
|
|
|
|
|
OPTIONAL_FUNCTION_ATTRIBUTES = [('__attribute__((optimize("unroll-loops")))', |
|
'attribute_optimize_unroll_loops'), |
|
('__attribute__((optimize("O3")))', |
|
'attribute_optimize_opt_3'), |
|
('__attribute__((nonnull (1)))', |
|
'attribute_nonnull'), |
|
] |
|
|
|
|
|
OPTIONAL_VARIABLE_ATTRIBUTES = ["__thread", "__declspec(thread)"] |
|
|
|
|
|
OPTIONAL_STDFUNCS_MAYBE = ["expm1", "log1p", "acosh", "atanh", "asinh", "hypot", |
|
"copysign", "ftello", "fseeko"] |
|
|
|
|
|
C99_FUNCS = ["sin", "cos", "tan", "sinh", "cosh", "tanh", "fabs", "floor", |
|
"ceil", "rint", "trunc", "sqrt", "log10", "log", "log1p", "exp", |
|
"expm1", "asin", "acos", "atan", "asinh", "acosh", "atanh", |
|
"hypot", "atan2", "pow", "fmod", "modf", 'frexp', 'ldexp', |
|
"exp2", "log2", "copysign", "nextafter"] |
|
|
|
C99_FUNCS_SINGLE = [f + 'f' for f in C99_FUNCS] |
|
C99_FUNCS_EXTENDED = [f + 'l' for f in C99_FUNCS] |
|
|
|
C99_COMPLEX_TYPES = ['complex double', 'complex float', 'complex long double'] |
|
|
|
C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg', 'cexp', 'csqrt', 'clog', |
|
'ccos', 'csin', 'cpow'] |
|
|
|
def fname2def(name): |
|
return "HAVE_%s" % name.upper() |
|
|
|
def sym2def(symbol): |
|
define = symbol.replace(' ', '') |
|
return define.upper() |
|
|
|
def type2def(symbol): |
|
define = symbol.replace(' ', '_') |
|
return define.upper() |
|
|
|
|
|
def check_long_double_representation(cmd): |
|
cmd._check_compiler() |
|
body = LONG_DOUBLE_REPRESENTATION_SRC % {'type': 'long double'} |
|
|
|
|
|
src, object = cmd._compile(body, None, None, 'c') |
|
try: |
|
type = long_double_representation(pyod(object)) |
|
return type |
|
finally: |
|
cmd._clean() |
|
|
|
LONG_DOUBLE_REPRESENTATION_SRC = r""" |
|
/* "before" is 16 bytes to ensure there's no padding between it and "x". |
|
* We're not expecting any "long double" bigger than 16 bytes or with |
|
* alignment requirements stricter than 16 bytes. */ |
|
typedef %(type)s test_type; |
|
|
|
struct { |
|
char before[16]; |
|
test_type x; |
|
char after[8]; |
|
} foo = { |
|
{ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', |
|
'\001', '\043', '\105', '\147', '\211', '\253', '\315', '\357' }, |
|
-123456789.0, |
|
{ '\376', '\334', '\272', '\230', '\166', '\124', '\062', '\020' } |
|
}; |
|
""" |
|
|
|
def pyod(filename): |
|
"""Python implementation of the od UNIX utility (od -b, more exactly). |
|
|
|
Parameters |
|
---------- |
|
filename : str |
|
name of the file to get the dump from. |
|
|
|
Returns |
|
------- |
|
out : seq |
|
list of lines of od output |
|
|
|
Note |
|
---- |
|
We only implement enough to get the necessary information for long double |
|
representation, this is not intended as a compatible replacement for od. |
|
""" |
|
def _pyod2(): |
|
out = [] |
|
|
|
fid = open(filename, 'rb') |
|
try: |
|
yo = [int(oct(int(binascii.b2a_hex(o), 16))) for o in fid.read()] |
|
for i in range(0, len(yo), 16): |
|
line = ['%07d' % int(oct(i))] |
|
line.extend(['%03d' % c for c in yo[i:i+16]]) |
|
out.append(" ".join(line)) |
|
return out |
|
finally: |
|
fid.close() |
|
|
|
def _pyod3(): |
|
out = [] |
|
|
|
fid = open(filename, 'rb') |
|
try: |
|
yo2 = [oct(o)[2:] for o in fid.read()] |
|
for i in range(0, len(yo2), 16): |
|
line = ['%07d' % int(oct(i)[2:])] |
|
line.extend(['%03d' % int(c) for c in yo2[i:i+16]]) |
|
out.append(" ".join(line)) |
|
return out |
|
finally: |
|
fid.close() |
|
|
|
if sys.version_info[0] < 3: |
|
return _pyod2() |
|
else: |
|
return _pyod3() |
|
|
|
_BEFORE_SEQ = ['000', '000', '000', '000', '000', '000', '000', '000', |
|
'001', '043', '105', '147', '211', '253', '315', '357'] |
|
_AFTER_SEQ = ['376', '334', '272', '230', '166', '124', '062', '020'] |
|
|
|
_IEEE_DOUBLE_BE = ['301', '235', '157', '064', '124', '000', '000', '000'] |
|
_IEEE_DOUBLE_LE = _IEEE_DOUBLE_BE[::-1] |
|
_INTEL_EXTENDED_12B = ['000', '000', '000', '000', '240', '242', '171', '353', |
|
'031', '300', '000', '000'] |
|
_INTEL_EXTENDED_16B = ['000', '000', '000', '000', '240', '242', '171', '353', |
|
'031', '300', '000', '000', '000', '000', '000', '000'] |
|
_MOTOROLA_EXTENDED_12B = ['300', '031', '000', '000', '353', '171', |
|
'242', '240', '000', '000', '000', '000'] |
|
_IEEE_QUAD_PREC_BE = ['300', '031', '326', '363', '105', '100', '000', '000', |
|
'000', '000', '000', '000', '000', '000', '000', '000'] |
|
_IEEE_QUAD_PREC_LE = _IEEE_QUAD_PREC_BE[::-1] |
|
_DOUBLE_DOUBLE_BE = ['301', '235', '157', '064', '124', '000', '000', '000'] + \ |
|
['000'] * 8 |
|
_DOUBLE_DOUBLE_LE = ['000', '000', '000', '124', '064', '157', '235', '301'] + \ |
|
['000'] * 8 |
|
|
|
def long_double_representation(lines): |
|
"""Given a binary dump as given by GNU od -b, look for long double |
|
representation.""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
read = [''] * 32 |
|
saw = None |
|
for line in lines: |
|
|
|
|
|
for w in line.split()[1:]: |
|
read.pop(0) |
|
read.append(w) |
|
|
|
|
|
|
|
if read[-8:] == _AFTER_SEQ: |
|
saw = copy.copy(read) |
|
if read[:12] == _BEFORE_SEQ[4:]: |
|
if read[12:-8] == _INTEL_EXTENDED_12B: |
|
return 'INTEL_EXTENDED_12_BYTES_LE' |
|
if read[12:-8] == _MOTOROLA_EXTENDED_12B: |
|
return 'MOTOROLA_EXTENDED_12_BYTES_BE' |
|
elif read[:8] == _BEFORE_SEQ[8:]: |
|
if read[8:-8] == _INTEL_EXTENDED_16B: |
|
return 'INTEL_EXTENDED_16_BYTES_LE' |
|
elif read[8:-8] == _IEEE_QUAD_PREC_BE: |
|
return 'IEEE_QUAD_BE' |
|
elif read[8:-8] == _IEEE_QUAD_PREC_LE: |
|
return 'IEEE_QUAD_LE' |
|
elif read[8:-8] == _DOUBLE_DOUBLE_BE: |
|
return 'DOUBLE_DOUBLE_BE' |
|
elif read[8:-8] == _DOUBLE_DOUBLE_LE: |
|
return 'DOUBLE_DOUBLE_LE' |
|
elif read[:16] == _BEFORE_SEQ: |
|
if read[16:-8] == _IEEE_DOUBLE_LE: |
|
return 'IEEE_DOUBLE_LE' |
|
elif read[16:-8] == _IEEE_DOUBLE_BE: |
|
return 'IEEE_DOUBLE_BE' |
|
|
|
if saw is not None: |
|
raise ValueError("Unrecognized format (%s)" % saw) |
|
else: |
|
|
|
raise ValueError("Could not lock sequences (%s)" % saw) |
|
|