Skip to content

Commit

Permalink
Introduce math_compat.h for older Android versions (#28567)
Browse files Browse the repository at this point in the history
Summary:
When building with Android NDK platforms prior to android-21,
and when building for Android with libstdc++, there are some
gaps in the C and C++ standard libraries.  We use both for our
internal 32-bit builds, so we need PyTorch to support this platform.

All of the gaps are filled with this math_compat.h header, which
needs to be included in any file that uses one of the functions
that are not properly defined on Android.  The file is a bit
hack-tastic, but it is only used on a platform that is not receiving
updates, so there shouldn't be a risk of breakage in the future.
Pull Request resolved: #28567

Test Plan: Internal android build.

Differential Revision: D18099513

Pulled By: dreiss

fbshipit-source-id: 020aab19c6fa083206310b018925d92275d4a548
  • Loading branch information
dreiss authored and facebook-github-bot committed Nov 5, 2019
1 parent cb72c9f commit 0d9dc46
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 0 deletions.
1 change: 1 addition & 0 deletions aten/src/ATen/cpu/vec256/vec256_base.h
Expand Up @@ -12,6 +12,7 @@
#include <ATen/NumericUtils.h>
#include <c10/util/C++17.h>
#include <c10/util/BFloat16.h>
#include <c10/util/math_compat.h>
#include <ATen/native/cpu/zmath.h>
#include <c10/util/TypeCast.h>

Expand Down
1 change: 1 addition & 0 deletions aten/src/ATen/native/Distributions.cpp
Expand Up @@ -5,6 +5,7 @@
#include <ATen/ExpandUtils.h>
#include <ATen/NativeFunctions.h>
#include <c10/util/Exception.h>
#include <c10/util/math_compat.h>
#include <ATen/core/EnableNamedTensor.h>

#include <ATen/Utils.h>
Expand Down
1 change: 1 addition & 0 deletions aten/src/ATen/native/Math.h
Expand Up @@ -4,6 +4,7 @@
#include <cmath>
#include <limits>
#include <type_traits>
#include <c10/util/math_compat.h>

#ifndef M_PIf
#define M_PIf 3.1415926535f
Expand Down
1 change: 1 addition & 0 deletions aten/src/ATen/native/cpu/zmath.h
Expand Up @@ -2,6 +2,7 @@

// Complex number math operations that act as no-ops for other dtypes.
#include <complex>
#include <c10/util/math_compat.h>

namespace at { namespace native {
namespace {
Expand Down
1 change: 1 addition & 0 deletions aten/src/ATen/native/quantized/cpu/fake_quantize_core.cpp
Expand Up @@ -2,6 +2,7 @@
#include <ATen/NativeFunctions.h>
#include <ATen/native/TensorIterator.h>
#include <ATen/native/cpu/Loops.h>
#include <c10/util/math_compat.h>

/* Core operations for fake-quantization shared between per tensor
and per-channel fake quant */
Expand Down
1 change: 1 addition & 0 deletions aten/src/ATen/native/quantized/cpu/q_adaavgpool.cpp
Expand Up @@ -2,6 +2,7 @@
#include <ATen/NativeFunctions.h>
#include <ATen/Parallel.h>
#include <ATen/native/quantized/cpu/quantized_ops.h>
#include <c10/util/math_compat.h>

#include <algorithm>
#include <cmath>
Expand Down
1 change: 1 addition & 0 deletions aten/src/ATen/native/quantized/cpu/q_avgpool.cpp
Expand Up @@ -6,6 +6,7 @@
#include <ATen/native/quantized/cpu/qnnpack_utils.h>
#include <ATen/native/quantized/cpu/quantized_ops.h>
#include <caffe2/utils/threadpool/ThreadPoolMobile.h>
#include <c10/util/math_compat.h>

#include <algorithm>
#include <cmath>
Expand Down
2 changes: 2 additions & 0 deletions aten/src/THNN/generic/SoftPlus.c
Expand Up @@ -2,6 +2,8 @@
#define TH_GENERIC_FILE "THNN/generic/SoftPlus.c"
#else

#include <c10/util/math_compat.h>

void THNN_(SoftPlus_updateOutput)(
THNNState *state,
THTensor *input,
Expand Down
114 changes: 114 additions & 0 deletions c10/util/math_compat.h
@@ -0,0 +1,114 @@
#pragma once

#include <cmath>

// Android NDK platform < 21 with libstdc++ has spotty C++11 support.
// Various hacks in this header allow the rest of the codebase to use
// standard APIs.
#if defined(__ANDROID__) && __ANDROID_API__ < 21 && defined(__GLIBCXX__)

namespace std {
// Import double versions of these functions from the global namespace.
using ::acosh;
using ::asinh;
using ::atanh;
using ::erf;
using ::erfc;
using ::expm1;
using ::lgamma;
using ::log1p;
using ::nearbyint;
using ::round;
using ::tgamma;
using ::trunc;
using ::truncf;

// Define float versions the same way as more recent libstdc++
inline float acosh(float x) { return __builtin_acoshf(x); }
inline float asinh(float x) { return __builtin_asinhf(x); }
inline float atanh(float x) { return __builtin_atanhf(x); }
inline float copysign(float x, float y) { return __builtin_copysignf(x, y); }
inline float erf(float x) { return __builtin_erff(x); }
inline float erfc(float x) { return __builtin_erfcf(x); }
inline float expm1(float x) { return __builtin_expm1f(x); }
inline float fmax(float x, float y) { return __builtin_fmaxf(x, y); }
inline float fmin(float x, float y) { return __builtin_fminf(x, y); }
inline float lgamma(float x) { return __builtin_lgammaf(x); }
inline float log1p(float x) { return __builtin_log1pf(x); }
inline float nearbyint(float x) { return __builtin_nearbyintf(x); }
inline float remainder(float x, float y) { return __builtin_remainderf(x, y); }
inline float round(float x) { return __builtin_roundf(x); }
inline float tgamma(float x) { return __builtin_tgammaf(x); }
inline float trunc(float x) { return __builtin_truncf(x); }

// __builtin_nexttoward isn't doesn't work. It appears to try to
// link against the global nexttoward function, which is not present
// prior to API 18. Just bail for now.
inline float nexttoward(float x, long double y) {
throw std::runtime_error("std::nexttoward is not present on older Android");
}
inline double nexttoward(double x, long double y) {
throw std::runtime_error("std::nexttoward is not present on older Android");
}

// Define integral versions the same way as more recent libstdc++
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type acosh(T x) { return __builtin_acosh(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type asinh(T x) { return __builtin_asinh(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type atanh(T x) { return __builtin_atanh(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type erf(T x) { return __builtin_erf(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type erfc(T x) { return __builtin_erfc(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type expm1(T x) { return __builtin_expm1(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type lgamma(T x) { return __builtin_lgamma(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type log1p(T x) { return __builtin_log1p(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type nearbyint(T x) { return __builtin_nearbyint(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type round(T x) { return __builtin_round(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type tgamma(T x) { return __builtin_tgamma(x); }
template<typename T> typename std::enable_if<std::is_integral<T>::value, double>::type trunc(T x) { return __builtin_trunc(x); }

// Convoluted definition of these binary functions for overloads other than
// (float,float) and (double,double). Using a template from __gnu_cxx
// is dirty, but this code is only enabled on a dead platform, so there
// shouldn't be any risk of it breaking due to updates.
template<typename T, typename U>
typename __gnu_cxx::__promote_2<T, U>::__type
fmax(T x, U y) {
typedef typename __gnu_cxx::__promote_2<T, U>::__type type;
return fmax(type(x), type(y));
}
template<typename T, typename U>
typename __gnu_cxx::__promote_2<T, U>::__type
fmin(T x, U y) {
typedef typename __gnu_cxx::__promote_2<T, U>::__type type;
return fmin(type(x), type(y));
}
template<typename T, typename U>
typename __gnu_cxx::__promote_2<T, U>::__type
copysign(T x, U y) {
typedef typename __gnu_cxx::__promote_2<T, U>::__type type;
return copysign(type(x), type(y));
}
template<typename T, typename U>
typename __gnu_cxx::__promote_2<T, U>::__type
remainder(T x, U y) {
typedef typename __gnu_cxx::__promote_2<T, U>::__type type;
return remainder(type(x), type(y));
}

// log2 is a macro on Android API < 21, so we need to define it ourselves.
inline float log2(float arg) {
return ::log(arg) / ::log(2.0);
}
inline double log2(double arg) {
return ::log(arg) / ::log(2.0);
}
inline long double log2(long double arg) {
return ::log(arg) / ::log(2.0);
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value, double>::type
log2(T x) {
return ::log(x) / ::log(2.0);
}
}

#endif
1 change: 1 addition & 0 deletions torch/csrc/jit/register_prim_ops.cpp
Expand Up @@ -25,6 +25,7 @@
#include <ATen/core/ivalue.h>
#include <c10/core/thread_pool.h>
#include <c10/util/SmallVector.h>
#include <c10/util/math_compat.h>

#include <algorithm>
#include <bitset>
Expand Down

0 comments on commit 0d9dc46

Please sign in to comment.