#if !defined(TORCH_STABLE_ONLY) && !defined(TORCH_TARGET_VERSION) #pragma once #include #include #include #include #include namespace at::vec { // Note [CPU_CAPABILITY namespace] // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // This header, and all of its subheaders, will be compiled with // different architecture flags for each supported set of vector // intrinsics. So we need to make sure they aren't inadvertently // linked together. We do this by declaring objects in an `inline // namespace` which changes the name mangling, but can still be // accessed as `at::vec`. inline namespace CPU_CAPABILITY { template <> struct is_vec_specialized_for : std::bool_constant {}; template <> class Vectorized { private: float64x2_t values; public: using value_type = double; using size_type = int; static constexpr size_type size() { return 2; } Vectorized() { values = vdupq_n_f64(0.0); } Vectorized(float64x2_t v) : values(v) {} Vectorized(double val) { values = vdupq_n_f64(val); } template < typename... Args, typename = std::enable_if_t<(sizeof...(Args) == size())>> Vectorized(Args... vals) { __at_align__ double buffer[size()] = {vals...}; values = vld1q_f64(buffer); } operator float64x2_t() const { return values; } template static Vectorized blend( const Vectorized& a, const Vectorized& b) { // Build an array of flags: each bit of element is 1 if the corresponding // bit in 'mask' is set, 0 otherwise. uint64x2_t maskArray = { (mask & 1ULL) ? 0xFFFFFFFFFFFFFFFF : 0, (mask & 2ULL) ? 0xFFFFFFFFFFFFFFFF : 0}; // Use BSL to select elements from b where the mask is 1, else from a return vbslq_f64(maskArray, b.values, a.values); } static Vectorized blendv( const Vectorized& a, const Vectorized& b, const Vectorized& mask_) { return vbslq_f64(vreinterpretq_u64_f64(mask_.values), b.values, a.values); } template static Vectorized arange( double base = 0., step_t step = static_cast(1)) { return {base, base + static_cast(step)}; } static inline Vectorized set( const Vectorized& a, const Vectorized& b, int64_t count = size()) { if (count == 0) { return a; } else if (count >= 2) { return b; } else { float64x2_t c = {b.values[0], a.values[1]}; return c; } } static Vectorized loadu(const void* ptr, int64_t count = size()) { if (count == size()) { return vld1q_f64(reinterpret_cast(ptr)); } else if (count == 1) { float64x1_t x = vld1_f64(reinterpret_cast(ptr)); float64x1_t z = {0.0}; return vcombine_f64(x, z); } else { return vdupq_n_f64(0.0); } } void store(void* ptr, int64_t count = size()) const { if (count == size()) { vst1q_f64(reinterpret_cast(ptr), values); } else if (count == 1) { vst1_f64(reinterpret_cast(ptr), vget_low_f64(values)); } } const double& operator[](int idx) const = delete; double& operator[](int idx) = delete; int64_t zero_mask() const { // returns an integer mask where all zero elements are translated to 1-bit // and others are translated to 0-bit uint64x2_t cmpReg = vceqzq_f64(values); uint64x2_t mask = {1, 2}; uint64x2_t res = vandq_u64(cmpReg, mask); return res[0] | res[1]; } Vectorized isnan() const { // NaN check return vreinterpretq_f64_u32( vmvnq_u32(vreinterpretq_u32_u64(vceqq_f64(values, values)))); } bool has_inf_nan() const { Vectorized x = vsubq_f64(values, values); float64x2_t r = x.isnan(); uint64x2_t u = vreinterpretq_u64_f64(r); return u[0] | u[1]; } Vectorized map(double (*f)(double)) const { float64x2_t result; result[0] = f(values[0]); result[1] = f(values[1]); return result; } Vectorized map2( const Vectorized& second, double (*const f)(double, double)) const { float64x2_t result; result[0] = f(values[0], second.values[0]); result[1] = f(values[1], second.values[1]); return result; } Vectorized abs() const { return vabsq_f64(values); } Vectorized angle() const { auto zero = Vectorized(0.0); auto pi = Vectorized(c10::pi); auto tmp = blendv(zero, pi, vreinterpretq_f64_u64(vcltzq_f64(values))); return blendv(tmp, *this, isnan()); } Vectorized real() const { return *this; } Vectorized imag() const { return Vectorized(0.0); } Vectorized conj() const { return *this; } Vectorized acos() const { return USE_SLEEF( Vectorized(Sleef_acosd2_u10(values)), map(std::acos)); } Vectorized acosh() const { return USE_SLEEF( Vectorized(Sleef_acoshd2_u10(values)), map(std::acosh)); } Vectorized asin() const { return USE_SLEEF( Vectorized(Sleef_asind2_u10(values)), map(std::asin)); } Vectorized asinh() const { return USE_SLEEF( Vectorized(Sleef_asinhd2_u10(values)), map(std::asinh)); } Vectorized atan() const { return USE_SLEEF( Vectorized(Sleef_atand2_u10(values)), map(std::atan)); } Vectorized atanh() const { return USE_SLEEF( Vectorized(Sleef_atanhd2_u10(values)), map(std::atanh)); } Vectorized atan2(const Vectorized& b) const { USE_SLEEF( { return Vectorized(Sleef_atan2d2_u10(values, b)); }, { __at_align__ double tmp[size()]; __at_align__ double tmp_b[size()]; store(tmp); b.store(tmp_b); for (int64_t i = 0; i < size(); i++) { tmp[i] = std::atan2(tmp[i], tmp_b[i]); } return loadu(tmp); }); } Vectorized copysign(const Vectorized& sign) const { USE_SLEEF( { return Vectorized(Sleef_copysignd2(values, sign)); }, { __at_align__ double tmp[size()]; __at_align__ double tmp_sign[size()]; store(tmp); sign.store(tmp_sign); for (int64_t i = 0; i < size(); i++) { tmp[i] = std::copysign(tmp[i], tmp_sign[i]); } return loadu(tmp); }); } Vectorized erf() const { return USE_SLEEF( Vectorized(Sleef_erfd2_u10(values)), map(std::erf)); } Vectorized erfc() const { return USE_SLEEF( Vectorized(Sleef_erfcd2_u15(values)), map(std::erfc)); } Vectorized exp() const { return USE_SLEEF( Vectorized(Sleef_expd2_u10(values)), map(std::exp)); } Vectorized exp2() const { return USE_SLEEF( Vectorized(Sleef_exp2d2_u10(values)), map(std::exp2)); } Vectorized expm1() const { return USE_SLEEF( Vectorized(Sleef_expm1d2_u10(values)), map(std::expm1)); } Vectorized fmod(const Vectorized& q) const { USE_SLEEF( { return Vectorized(Sleef_fmodd2(values, q)); }, { __at_align__ double tmp[size()]; __at_align__ double tmp_q[size()]; store(tmp); q.store(tmp_q); for (int64_t i = 0; i < size(); i++) { tmp[i] = std::fmod(tmp[i], tmp_q[i]); } return loadu(tmp); }); } Vectorized hypot(const Vectorized& b) const { USE_SLEEF( { return Vectorized(Sleef_hypotd2_u05(values, b)); }, { __at_align__ double tmp[size()]; __at_align__ double tmp_b[size()]; store(tmp); b.store(tmp_b); for (int64_t i = 0; i < size(); i++) { tmp[i] = std::hypot(tmp[i], tmp_b[i]); } return loadu(tmp); }); } Vectorized i0() const { return map(calc_i0); } Vectorized nextafter(const Vectorized& b) const { USE_SLEEF( { return Vectorized(Sleef_nextafterd2(values, b)); }, { __at_align__ double tmp[size()]; __at_align__ double tmp_b[size()]; store(tmp); b.store(tmp_b); for (int64_t i = 0; i < size(); ++i) { tmp[i] = std::nextafter(tmp[i], tmp_b[i]); } return loadu(tmp); }); } Vectorized log() const { return USE_SLEEF( Vectorized(Sleef_logd2_u10(values)), map(std::log)); } Vectorized log2() const { return USE_SLEEF( Vectorized(Sleef_log2d2_u10(values)), map(std::log2)); } Vectorized log10() const { return USE_SLEEF( Vectorized(Sleef_log10d2_u10(values)), map(std::log10)); } Vectorized log1p() const { return USE_SLEEF( Vectorized(Sleef_log1pd2_u10(values)), map(std::log1p)); } Vectorized frac() const; Vectorized sin() const { return USE_SLEEF( Vectorized(Sleef_sind2_u10(values)), map(std::sin)); } Vectorized sinh() const { return USE_SLEEF( Vectorized(Sleef_sinhd2_u10(values)), map(std::sinh)); } Vectorized cos() const { return USE_SLEEF( Vectorized(Sleef_cosd2_u10(values)), map(std::cos)); } Vectorized cosh() const { return USE_SLEEF( Vectorized(Sleef_coshd2_u10(values)), map(std::cosh)); } Vectorized pow(const Vectorized& b) const { USE_SLEEF( { return Vectorized(Sleef_powd2_u10(values, b)); }, { __at_align__ double tmp[size()]; __at_align__ double tmp_b[size()]; store(tmp); b.store(tmp_b); for (int64_t i = 0; i < size(); i++) { tmp[i] = std::pow(tmp[i], tmp_b[i]); } return loadu(tmp); }); } // Comparison using the _CMP_**_OQ predicate. // `O`: get false if an operand is NaN // `Q`: do not raise if an operand is NaN Vectorized tan() const { return USE_SLEEF( Vectorized(Sleef_tand2_u10(values)), map(std::tan)); } Vectorized tanh() const { return USE_SLEEF( Vectorized(Sleef_tanhd2_u10(values)), map(std::tanh)); } Vectorized lgamma() const { return USE_SLEEF( Vectorized(Sleef_lgammad2_u10(values)), map(std::lgamma)); } Vectorized erfinv() const { return map(calc_erfinv); } Vectorized exp_u20() const { return exp(); } Vectorized fexp_u20() const { return exp(); } Vectorized i0e() const { return map(calc_i0e); } Vectorized digamma() const { return map(calc_digamma); } Vectorized igamma(const Vectorized& x) const { __at_align__ double tmp[size()]; __at_align__ double tmp_x[size()]; store(tmp); x.store(tmp_x); for (int64_t i = 0; i < size(); i++) { tmp[i] = calc_igamma(tmp[i], tmp_x[i]); } return loadu(tmp); } Vectorized igammac(const Vectorized& x) const { __at_align__ double tmp[size()]; __at_align__ double tmp_x[size()]; store(tmp); x.store(tmp_x); for (int64_t i = 0; i < size(); i++) { tmp[i] = calc_igammac(tmp[i], tmp_x[i]); } return loadu(tmp); } Vectorized ceil() const { return vrndpq_f64(values); } Vectorized floor() const { return vrndmq_f64(values); } Vectorized neg() const { return vnegq_f64(values); } Vectorized round() const { return vrndiq_f64(values); } Vectorized trunc() const { return vrndq_f64(values); } Vectorized sqrt() const { return vsqrtq_f64(values); } Vectorized reciprocal() const { return vdivq_f64(vdupq_n_f64(1.0), values); } Vectorized rsqrt() const { return vdivq_f64(vdupq_n_f64(1.0), vsqrtq_f64(values)); } double reduce_add() const { return vaddvq_f64(values); } double reduce_max() const { return vmaxvq_f64(values); } Vectorized operator==(const Vectorized& other) const { return Vectorized( vreinterpretq_f64_u64(vceqq_f64(values, other.values))); } Vectorized operator!=(const Vectorized& other) const { float64x2_t r0 = vreinterpretq_f64_u32( vmvnq_u32(vreinterpretq_u32_u64(vceqq_f64(values, other.values)))); return Vectorized(r0); } Vectorized operator<(const Vectorized& other) const { return Vectorized( vreinterpretq_f64_u64(vcltq_f64(values, other.values))); } Vectorized operator<=(const Vectorized& other) const { return Vectorized( vreinterpretq_f64_u64(vcleq_f64(values, other.values))); } Vectorized operator>(const Vectorized& other) const { return Vectorized( vreinterpretq_f64_u64(vcgtq_f64(values, other.values))); } Vectorized operator>=(const Vectorized& other) const { return Vectorized( vreinterpretq_f64_u64(vcgeq_f64(values, other.values))); } Vectorized eq(const Vectorized& other) const; Vectorized ne(const Vectorized& other) const; Vectorized gt(const Vectorized& other) const; Vectorized ge(const Vectorized& other) const; Vectorized lt(const Vectorized& other) const; Vectorized le(const Vectorized& other) const; }; template <> Vectorized inline operator+( const Vectorized& a, const Vectorized& b) { return vaddq_f64(a, b); } template <> Vectorized inline operator-( const Vectorized& a, const Vectorized& b) { return vsubq_f64(a, b); } template <> Vectorized inline operator*( const Vectorized& a, const Vectorized& b) { return vmulq_f64(a, b); } template <> Vectorized inline operator/( const Vectorized& a, const Vectorized& b) { return vdivq_f64(a, b); } // frac. Implement this here so we can use subtraction Vectorized inline Vectorized::frac() const { return *this - this->trunc(); } // Implements the IEEE 754 201X `maximum` operation, which propagates NaN if // either input is a NaN. template <> Vectorized inline maximum( const Vectorized& a, const Vectorized& b) { return vmaxq_f64(a, b); } // Implements the IEEE 754 201X `minimum` operation, which propagates NaN if // either input is a NaN. template <> Vectorized inline minimum( const Vectorized& a, const Vectorized& b) { return vminq_f64(a, b); } template <> Vectorized inline clamp( const Vectorized& a, const Vectorized& min, const Vectorized& max) { return vminq_f64(max, vmaxq_f64(min, a)); } template <> Vectorized inline clamp_max( const Vectorized& a, const Vectorized& max) { return vminq_f64(max, a); } template <> Vectorized inline clamp_min( const Vectorized& a, const Vectorized& min) { return vmaxq_f64(min, a); } template <> Vectorized inline operator&( const Vectorized& a, const Vectorized& b) { return vreinterpretq_f64_u64( vandq_u64(vreinterpretq_u64_f64(a), vreinterpretq_u64_f64(b))); } template <> Vectorized inline operator|( const Vectorized& a, const Vectorized& b) { return vreinterpretq_f64_u64( vorrq_u64(vreinterpretq_u64_f64(a), vreinterpretq_u64_f64(b))); } template <> Vectorized inline operator^( const Vectorized& a, const Vectorized& b) { return vreinterpretq_f64_u64( veorq_u64(vreinterpretq_u64_f64(a), vreinterpretq_u64_f64(b))); } inline Vectorized Vectorized::eq( const Vectorized& other) const { return (*this == other) & Vectorized(1.0); } inline Vectorized Vectorized::ne( const Vectorized& other) const { return (*this != other) & Vectorized(1.0); } inline Vectorized Vectorized::gt( const Vectorized& other) const { return (*this > other) & Vectorized(1.0); } inline Vectorized Vectorized::ge( const Vectorized& other) const { return (*this >= other) & Vectorized(1.0); } inline Vectorized Vectorized::lt( const Vectorized& other) const { return (*this < other) & Vectorized(1.0); } inline Vectorized Vectorized::le( const Vectorized& other) const { return (*this <= other) & Vectorized(1.0); } template <> Vectorized inline fmadd( const Vectorized& a, const Vectorized& b, const Vectorized& c) { return vfmaq_f64(c, a, b); } template <> Vectorized inline fnmadd( const Vectorized& a, const Vectorized& b, const Vectorized& c) { return vfmsq_f64(c, a, b); } template <> Vectorized inline fmsub( const Vectorized& a, const Vectorized& b, const Vectorized& c) { return vfmaq_f64(vnegq_f64(c), a, b); } template <> Vectorized inline fnmsub( const Vectorized& a, const Vectorized& b, const Vectorized& c) { return vfmsq_f64(vnegq_f64(c), a, b); } } // namespace CPU_CAPABILITY } // namespace at::vec #else #error "This file should not be included when either TORCH_STABLE_ONLY or TORCH_TARGET_VERSION is defined." #endif // !defined(TORCH_STABLE_ONLY) && !defined(TORCH_TARGET_VERSION)