Mentions légales du service

Skip to content
Snippets Groups Projects
Commit f0b0460d authored by Berenger Bramas's avatar Berenger Bramas
Browse files

Use inastemp

parent 2c5546a4
Branches
Tags
No related merge requests found
// See LICENCE file at project root
#ifndef FAVX_HPP
#define FAVX_HPP
#include "FGlobal.hpp"
#ifndef SCALFMM_USE_AVX
#error The AVX header is included while SCALFMM_USE_AVX is turned OFF
#else
#include <immintrin.h>
#ifdef __AVXPE_INTEL_COMPILER
//Side effect operators DOUBLE
inline __m256d& operator+=(__m256d & a, const __m256d & b){
return (a = _mm256_add_pd (a,b));
}
inline __m256d& operator-=(__m256d& a, const __m256d& b){
return (a = _mm256_sub_pd (a,b));
}
inline __m256d& operator*=(__m256d& a, const __m256d& b){
return (a = _mm256_mul_pd (a,b));
}
inline __m256d& operator/=(__m256d& a, const __m256d& b){
return (a = _mm256_div_pd (a,b));
}
//No side effect operators DOUBLE
inline __m256d operator+(const __m256d& a,const __m256d& b){
return _mm256_add_pd (a,b);
}
inline __m256d operator-(const __m256d& a, const __m256d& b){
return _mm256_sub_pd (a,b);
}
inline __m256d operator*(const __m256d& v1, const __m256d& v2){
return _mm256_mul_pd(v1, v2);
}
inline __m256d operator/(const __m256d& v1, const __m256d& v2){
return _mm256_div_pd(v1, v2);
}
//Side effect operators SINGLE
inline __m256& operator+=(__m256 & a, const __m256 & b){
return (a = _mm256_add_ps (a,b));
}
inline __m256& operator-=(__m256& a, const __m256& b){
return (a = _mm256_sub_ps (a,b));
}
inline __m256& operator*=(__m256& a, const __m256& b){
return (a = _mm256_mul_ps (a,b));
}
inline __m256& operator/=(__m256& a, const __m256& b){
return (a = _mm256_div_ps (a,b));
}
//No side effect operators SINGLE
inline __m256 operator+(const __m256& a,const __m256& b){
return _mm256_add_ps (a,b);
}
inline __m256 operator-(const __m256& a, const __m256& b){
return _mm256_sub_ps (a,b);
}
inline __m256 operator*(const __m256& v1, const __m256& v2){
return _mm256_mul_ps(v1, v2);
}
inline __m256 operator/(const __m256& v1, const __m256& v2){
return _mm256_div_ps(v1, v2);
}
#endif
#endif
#endif
// See LICENCE file at project root
#ifndef FAVX2_HPP
#define FAVX2_HPP
#include "FGlobal.hpp"
#ifndef SCALFMM_USE_AVX2
#error The AVX header is included while SCALFMM_USE_AVX is turned OFF
#endif
#include <immintrin.h>
#ifdef __MIC__
//Side effect operators DOUBLE
inline __m512d& operator+=(__m512d & a, const __m512d & b){
return (a = _mm512_add_pd (a,b));
}
inline __m512d& operator-=(__m512d& a, const __m512d& b){
return (a = _mm512_sub_pd (a,b));
}
inline __m512d& operator*=(__m512d& a, const __m512d& b){
return (a = _mm512_mul_pd (a,b));
}
inline __m512d& operator/=(__m512d& a, const __m512d& b){
return (a = _mm512_div_pd (a,b));
}
//No side effect operators DOUBLE
inline __m512d operator+(const __m512d& a,const __m512d& b){
return _mm512_add_pd (a,b);
}
inline __m512d operator-(const __m512d& a, const __m512d& b){
return _mm512_sub_pd (a,b);
}
inline __m512d operator*(const __m512d& v1, const __m512d& v2){
return _mm512_mul_pd(v1, v2);
}
inline __m512d operator/(const __m512d& v1, const __m512d& v2){
return _mm512_div_pd(v1, v2);
}
//Side effect operators SINGLE
inline __m512& operator+=(__m512 & a, const __m512 & b){
return (a = _mm512_add_ps (a,b));
}
inline __m512& operator-=(__m512& a, const __m512& b){
return (a = _mm512_sub_ps (a,b));
}
inline __m512& operator*=(__m512& a, const __m512& b){
return (a = _mm512_mul_ps (a,b));
}
inline __m512& operator/=(__m512& a, const __m512& b){
return (a = _mm512_div_ps (a,b));
}
//No side effect operators SINGLE
inline __m512 operator+(const __m512& a,const __m512& b){
return _mm512_add_ps (a,b);
}
inline __m512 operator-(const __m512& a, const __m512& b){
return _mm512_sub_ps (a,b);
}
inline __m512 operator*(const __m512& v1, const __m512& v2){
return _mm512_mul_ps(v1, v2);
}
inline __m512 operator/(const __m512& v1, const __m512& v2){
return _mm512_div_ps(v1, v2);
}
#endif
#endif
......@@ -8,14 +8,6 @@
#include "FGlobal.hpp"
#ifdef SCALFMM_USE_SSE
#include "FSse.hpp"
#endif
#ifdef SCALFMM_USE_AVX
#include "FAvx.hpp"
#endif
/**
* @author Berenger Bramas (berenger.bramas@inria.fr)
......@@ -40,36 +32,6 @@ struct FMath{
return (inV < 0 ? -inV : inV);
}
#ifdef SCALFMM_USE_SSE
static __m128 Abs(const __m128 inV){
return _mm_max_ps(_mm_sub_ps(_mm_setzero_ps(), inV), inV);
}
static __m128d Abs(const __m128d inV){
return _mm_max_pd(_mm_sub_pd(_mm_setzero_pd(), inV), inV);
}
#endif
#ifdef SCALFMM_USE_AVX
static __m256 Abs(const __m256 inV){
return _mm256_max_ps(_mm256_sub_ps(_mm256_setzero_ps(), inV), inV);
}
static __m256d Abs(const __m256d inV){
return _mm256_max_pd(_mm256_sub_pd(_mm256_setzero_pd(), inV), inV);
}
#endif
#ifdef SCALFMM_USE_AVX2
#ifdef __MIC__
static __m512 Abs(const __m512 inV){
return _mm512_max_ps(_mm512_sub_ps(_mm512_setzero_ps(), inV), inV);
}
static __m512d Abs(const __m512d inV){
return _mm512_max_pd(_mm512_sub_pd(_mm512_setzero_pd(), inV), inV);
}
#endif
#endif
/** To get max between 2 values */
template <class NumType>
static NumType Max(const NumType inV1, const NumType inV2){
......@@ -82,53 +44,6 @@ struct FMath{
return (inV1 < inV2 ? inV1 : inV2);
}
#ifdef SCALFMM_USE_SSE
static __m128 Max(const __m128 inV1, const __m128 inV2){
return _mm_max_ps(inV1, inV2);
}
static __m128 Min(const __m128 inV1, const __m128 inV2){
return _mm_min_ps(inV1, inV2);
}
static __m128d Max(const __m128d inV1, const __m128d inV2){
return _mm_max_pd(inV1, inV2);
}
static __m128d Min(const __m128d inV1, const __m128d inV2){
return _mm_min_pd(inV1, inV2);
}
#endif
#ifdef SCALFMM_USE_AVX
static __m256 Max(const __m256 inV1, const __m256 inV2){
return _mm256_max_ps(inV1, inV2);
}
static __m256 Min(const __m256 inV1, const __m256 inV2){
return _mm256_min_ps(inV1, inV2);
}
static __m256d Max(const __m256d inV1, const __m256d inV2){
return _mm256_max_pd(inV1, inV2);
}
static __m256d Min(const __m256d inV1, const __m256d inV2){
return _mm256_min_pd(inV1, inV2);
}
#endif
#ifdef SCALFMM_USE_AVX2
#ifdef __MIC__
static __m512 Max(const __m512 inV1, const __m512 inV2){
return _mm512_max_ps(inV1, inV2);
}
static __m512 Min(const __m512 inV1, const __m512 inV2){
return _mm512_min_ps(inV1, inV2);
}
static __m512d Max(const __m512d inV1, const __m512d inV2){
return _mm512_max_pd(inV1, inV2);
}
static __m512d Min(const __m512d inV1, const __m512d inV2){
return _mm512_min_pd(inV1, inV2);
}
#endif
#endif
/** To know if 2 values seems to be equal */
template <class NumType>
static bool LookEqual(const NumType inV1, const NumType inV2){
......@@ -160,59 +75,6 @@ struct FMath{
return ceil(inValue);
}
#if defined(SCALFMM_USE_SSE ) && defined(__SSSE4_1__)
static __m128 dfloor(const __m128 inV){
return _mm_floor_ps(inV);
}
static __m128d dfloor(const __m128d inV){
return _mm_floor_pd(inV);
}
static __m128 Ceil(const __m128 inV){
return _mm_ceil_ps(inV);
}
static __m128d Ceil(const __m128d inV){
return _mm_ceil_pd(inV);
}
#endif
#ifdef SCALFMM_USE_AVX
static __m256 dfloor(const __m256 inV){
return _mm256_floor_ps(inV);
}
static __m256d dfloor(const __m256d inV){
return _mm256_floor_pd(inV);
}
static __m256 Ceil(const __m256 inV){
return _mm256_ceil_ps(inV);
}
static __m256d Ceil(const __m256d inV){
return _mm256_ceil_pd(inV);
}
#endif
#ifdef SCALFMM_USE_AVX2
#ifdef __MIC__
static __m512 dfloor(const __m512 inV){
return _mm512_floor_ps(inV);
}
static __m512d dfloor(const __m512d inV){
return _mm512_floor_pd(inV);
}
static __m512 Ceil(const __m512 inV){
return _mm512_ceil_ps(inV);
}
static __m512d Ceil(const __m512d inV){
return _mm512_ceil_pd(inV);
}
#endif
#endif
/** To get pow */
static double pow(double x, double y){
return ::pow(x,y);
......@@ -263,39 +125,6 @@ struct FMath{
}
#if defined(SCALFMM_USE_SSE ) && defined(__SSSE4_1__)
static __m128 FMAdd(const __m128 inV1, const __m128 inV2, const __m128 inV3){
return _mm_add_ps( _mm_mul_ps(inV1,inV2), inV3);
}
static __m128d FMAdd(const __m128d inV1, const __m128d inV2, const __m128d inV3){
return _mm_add_pd( _mm_mul_pd(inV1,inV2), inV3);
}
#endif
#ifdef SCALFMM_USE_AVX
static __m256 FMAdd(const __m256 inV1, const __m256 inV2, const __m256 inV3){
return _mm256_add_ps( _mm256_mul_ps(inV1,inV2), inV3);
}
static __m256d FMAdd(const __m256d inV1, const __m256d inV2, const __m256d inV3){
return _mm256_add_pd( _mm256_mul_pd(inV1,inV2), inV3);
}
#endif
#ifdef SCALFMM_USE_AVX2
#ifdef __MIC__
static __m512 FMAdd(const __m512 inV1, const __m512 inV2, const __m512 inV3){
//return _mm512_add_ps( _mm512_mul_ps(inV1,inV2), inV3);
return _mm512_fmadd_ps(inV1, inV2, inV3);
}
static __m512d FMAdd(const __m512d inV1, const __m512d inV2, const __m512d inV3){
//return _mm512_add_pd( _mm512_mul_pd(inV1,inV2), inV3);
return _mm512_fmadd_pd(inV1, inV2, inV3);
}
#endif
#endif
/** To get sqrt of a FReal */
static float Sqrt(const float inValue){
return sqrtf(inValue);
......@@ -303,123 +132,25 @@ struct FMath{
static double Sqrt(const double inValue){
return sqrt(inValue);
}
static float Rsqrt(const float inValue){
return float(1.0)/sqrtf(inValue);
}
static double Rsqrt(const double inValue){
return 1.0/sqrt(inValue);
}
#ifdef SCALFMM_USE_SSE
static __m128 Exp(const __m128 inV){
float ptr[4];
_mm_storeu_ps(ptr, inV);
for(int idx = 0 ; idx < 4 ; ++idx){
ptr[idx] = std::exp(ptr[idx]);
}
return _mm_loadu_ps(ptr);
}
static __m128d Exp(const __m128d inV){
double ptr[2];
_mm_storeu_pd(ptr, inV);
for(int idx = 0 ; idx < 2 ; ++idx){
ptr[idx] = std::exp(ptr[idx]);
}
return _mm_loadu_pd(ptr);
}
static __m128 Sqrt(const __m128 inV){
return _mm_sqrt_ps(inV);
template <class InaClass>
static InaClass Sqrt(const InaClass inValue){
return inValue.sqrt();
}
static __m128d Sqrt(const __m128d inV){
return _mm_sqrt_pd(inV);
template <class InaClass>
static InaClass Exp(const InaClass inValue){
return inValue.exp();
}
static __m128 Rsqrt(const __m128 inV){
return _mm_rsqrt_ps(inV);
}
static __m128d Rsqrt(const __m128d inV){
return _mm_set_pd1(1.0) / _mm_sqrt_pd(inV);
}
#endif
#ifdef SCALFMM_USE_AVX
static __m256 Exp(const __m256 inV){
float ptr[8];
_mm256_storeu_ps(ptr, inV);
for(int idx = 0 ; idx < 8 ; ++idx){
ptr[idx] = std::exp(ptr[idx]);
}
return _mm256_loadu_ps(ptr);
}
static __m256d Exp(const __m256d inV){
double ptr[4];
_mm256_storeu_pd(ptr, inV);
for(int idx = 0 ; idx < 4 ; ++idx){
ptr[idx] = std::exp(ptr[idx]);
}
return _mm256_loadu_pd(ptr);
}
static __m256 Sqrt(const __m256 inV){
return _mm256_sqrt_ps(inV);
}
static __m256d Sqrt(const __m256d inV){
return _mm256_sqrt_pd(inV);
}
static __m256 Rsqrt(const __m256 inV){
return _mm256_rsqrt_ps(inV);
}
static __m256d Rsqrt(const __m256d inV){
return _mm256_set1_pd(1.0) / _mm256_sqrt_pd(inV);
}
#endif
#ifdef SCALFMM_USE_AVX2
#ifdef __MIC__
static __m512 Exp(const __m512 inV){
float ptr[16];
_mm512_storeu_ps(ptr, inV);
for(int idx = 0 ; idx < 16 ; ++idx){
ptr[idx] = std::exp(ptr[idx]);
}
return _mm512_loadu_ps(ptr);
}
static __m512d Exp(const __m512d inV){
double ptr[8];
_mm512_storeu_pd(ptr, inV);
for(int idx = 0 ; idx < 8 ; ++idx){
ptr[idx] = std::exp(ptr[idx]);
}
return _mm512_loadu_pd(ptr);
}
static __m512d Exp(const __m512d inV){
return _mm512_sqrt_pd(inV);
}
static __m512 Sqrt(const __m512 inV){
return _mm512_sqrt_ps(inV);
}
static __m512d Sqrt(const __m512d inV){
return _mm512_sqrt_pd(inV);
static float Rsqrt(const float inValue){
return float(1.0)/sqrtf(inValue);
}
static __m512 Rsqrt(const __m512 inV){
return _mm512_rsqrt_ps(inV);
static double Rsqrt(const double inValue){
return 1.0/sqrt(inValue);
}
static __m512d Rsqrt(const __m512d inV){
return _mm512_set1_pd(1.0) / _mm512_sqrt_pd(inV);
}
#endif
#endif
/** To get Log of a FReal */
static float Log(const float inValue){
return logf(inValue);
......@@ -503,15 +234,6 @@ struct FMath{
return std::isfinite(value);
}
template <class NumType>
static NumType Zero();
template <class NumType>
static NumType One();
template <class DestType, class SrcType>
static DestType ConvertTo(const SrcType val);
/** A class to compute accuracy */
template <class FReal, class IndexType = FSize>
......@@ -531,10 +253,10 @@ struct FMath{
/** Add value to the current list */
void add(const FReal inGood, const FReal inBad){
l2Diff += (inBad - inGood) * (inBad - inGood);
l2Dot += inGood * inGood;
max = Max(max , Abs(inGood));
maxDiff = Max(maxDiff, Abs(inGood-inBad));
l2Diff += (inBad - inGood) * (inBad - inGood);
l2Dot += inGood * inGood;
max = Max(max , Abs(inGood));
maxDiff = Max(maxDiff, Abs(inGood-inBad));
nbElements += 1 ;
}
/** Add array of values */
......@@ -609,213 +331,5 @@ struct FMath{
};
};
template <>
inline float FMath::Zero<float>(){
return float(0.0);
}
template <>
inline double FMath::Zero<double>(){
return double(0.0);
}
template <>
inline float FMath::One<float>(){
return float(1.0);
}
template <>
inline double FMath::One<double>(){
return double(1.0);
}
template <>
inline float FMath::ConvertTo<float,float>(const float val){
return val;
}
template <>
inline double FMath::ConvertTo<double,double>(const double val){
return val;
}
template <>
inline float FMath::ConvertTo<float,const float*>(const float* val){
return *val;
}
template <>
inline double FMath::ConvertTo<double,const double*>(const double* val){
return *val;
}
#ifdef SCALFMM_USE_SSE
template <>
inline __m128 FMath::One<__m128>(){
return _mm_set_ps1(1.0);
}
template <>
inline __m128d FMath::One<__m128d>(){
return _mm_set_pd1(1.0);
}
template <>
inline __m128 FMath::Zero<__m128>(){
return _mm_setzero_ps();
}
template <>
inline __m128d FMath::Zero<__m128d>(){
return _mm_setzero_pd();
}
template <>
inline __m128 FMath::ConvertTo<__m128,float>(const float val){
return _mm_set_ps1(val);
}
template <>
inline __m128d FMath::ConvertTo<__m128d,double>(const double val){
return _mm_set_pd1(val);
}
template <>
inline __m128 FMath::ConvertTo<__m128,const float*>(const float* val){
return _mm_load1_ps(val);
}
template <>
inline __m128d FMath::ConvertTo<__m128d,const double*>(const double* val){
return _mm_load1_pd(val);
}
template <>
inline float FMath::ConvertTo<float,__m128>(const __m128 val){
__attribute__((aligned(16))) float buffer[4];
_mm_store_ps(buffer, val);
return buffer[0] + buffer[1] + buffer[2] + buffer[3];
}
template <>
inline double FMath::ConvertTo<double,__m128d>(const __m128d val){
__attribute__((aligned(16))) double buffer[2];
_mm_store_pd(buffer, val);
return buffer[0] + buffer[1];
}
#endif
#ifdef SCALFMM_USE_AVX
template <>
inline __m256 FMath::One<__m256>(){
return _mm256_set1_ps(1.0);
}
template <>
inline __m256d FMath::One<__m256d>(){
return _mm256_set1_pd(1.0);
}
template <>
inline __m256 FMath::Zero<__m256>(){
return _mm256_setzero_ps();
}
template <>
inline __m256d FMath::Zero<__m256d>(){
return _mm256_setzero_pd();
}
template <>
inline __m256 FMath::ConvertTo<__m256,float>(const float val){
return _mm256_set1_ps(val);
}
template <>
inline __m256d FMath::ConvertTo<__m256d,double>(const double val){
return _mm256_set1_pd(val);
}
template <>
inline __m256 FMath::ConvertTo<__m256,const float*>(const float* val){
return _mm256_broadcast_ss(val);
}
template <>
inline __m256d FMath::ConvertTo<__m256d,const double*>(const double* val){
return _mm256_broadcast_sd(val);
}
template <>
inline float FMath::ConvertTo<float,__m256>(const __m256 val){
__attribute__((aligned(32))) float buffer[8];
_mm256_store_ps(buffer, val);
return buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4] + buffer[5] + buffer[6] + buffer[7];
}
template <>
inline double FMath::ConvertTo<double,__m256d>(const __m256d val){
__attribute__((aligned(32))) double buffer[4];
_mm256_store_pd(buffer, val);
return buffer[0] + buffer[1] + buffer[2] + buffer[3];
}
#endif
#ifdef SCALFMM_USE_AVX2
#ifdef __MIC__
template <>
inline __m512 FMath::One<__m512>(){
return _mm512_set1_ps(1.0);
}
template <>
inline __m512d FMath::One<__m512d>(){
return _mm512_set1_pd(1.0);
}
template <>
inline __m512 FMath::Zero<__m512>(){
return _mm512_setzero_ps();
}
template <>
inline __m512d FMath::Zero<__m512d>(){
return _mm512_setzero_pd();
}
template <>
inline __m512 FMath::ConvertTo<__m512,__attribute__((aligned(64))) float>(const float val){
return _mm512_set1_ps(val);
}
template <>
inline __m512d FMath::ConvertTo<__m512d,__attribute__((aligned(64))) double>(const double val){
return _mm512_set1_pd(val);
}
template <>
inline __m512 FMath::ConvertTo<__m512,const __attribute__((aligned(64))) float*>(const float* val){
return _mm512_set1_ps(val[0]);
}
template <>
inline __m512d FMath::ConvertTo<__m512d,const __attribute__((aligned(64))) double*>(const double* val){
return _mm512_set1_pd(val[0]);
}
template <>
inline float FMath::ConvertTo<float,__m512>(const __m512 val){
__attribute__((aligned(64))) float buffer[16];
_mm512_store_ps(buffer, val);
return buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4] + buffer[5] + buffer[6] + buffer[7] + buffer[8] + buffer[9] + buffer[10] + buffer[11] + buffer[12] + buffer[13] + buffer[14] + buffer[15];
}
template <>
inline double FMath::ConvertTo<double,__m512d>(const __m512d val){
__attribute__((aligned(64))) double buffer[8];
_mm512_store_pd(buffer, val);
return buffer[0] + buffer[1] + buffer[2] + buffer[3] + buffer[4] + buffer[5] + buffer[6] + buffer[7];
}
#endif
#endif
#endif //FMATH_HPP
// See LICENCE file at project root
#ifndef FSSE_HPP
#define FSSE_HPP
#include "FGlobal.hpp"
#ifndef SCALFMM_USE_SSE
#error The SSE header is included while SCALFMM_USE_SSE is turned OFF
#endif
#include <xmmintrin.h> // SSE
#include <emmintrin.h> //SSE2
#include <pmmintrin.h> //SSE3
#ifdef __SSSE3__
#include <tmmintrin.h> //SSSE3
#endif
#ifdef __SSSE4_1__
#include <smmintrin.h> // SSE4
#endif
#ifndef _mm_set_pd1
// Looks like clang's emmintrin.h doesn't have this alternate name.
// But _mm_set1_pd is an equivalent to _mm_set_pd1.
#define _mm_set_pd1 _mm_set1_pd
#endif
#ifdef __SSEPE_INTEL_COMPILER
inline __m128d& operator+=(__m128d& v1, const __m128d& v2){
return (v1 = _mm_add_pd(v1, v2));
}
inline __m128d& operator-=(__m128d& v1, const __m128d& v2){
return (v1 = _mm_sub_pd(v1, v2));
}
inline __m128d& operator*=(__m128d& v1, const __m128d& v2){
return (v1 = _mm_mul_pd(v1, v2));
}
inline __m128d& operator/=(__m128d& v1, const __m128d& v2){
return (v1 = _mm_div_pd(v1, v2));
}
inline __m128d operator+(const __m128d& v1, const __m128d& v2){
return _mm_add_pd(v1, v2);
}
inline __m128d operator-(const __m128d& v1, const __m128d& v2){
return _mm_sub_pd(v1, v2);
}
inline __m128d operator*(const __m128d& v1, const __m128d& v2){
return _mm_mul_pd(v1, v2);
}
inline __m128d operator/(const __m128d& v1, const __m128d& v2){
return _mm_div_pd(v1, v2);
}
inline __m128& operator+=(__m128& v1, const __m128& v2){
return (v1 = _mm_add_ps(v1, v2));
}
inline __m128& operator-=(__m128& v1, const __m128& v2){
return (v1 = _mm_sub_ps(v1, v2));
}
inline __m128& operator*=(__m128& v1, const __m128& v2){
return (v1 = _mm_mul_ps(v1, v2));
}
inline __m128& operator/=(__m128& v1, const __m128& v2){
return (v1 = _mm_div_ps(v1, v2));
}
inline __m128 operator+(const __m128& v1, const __m128& v2){
return _mm_add_ps(v1, v2);
}
inline __m128 operator-(const __m128& v1, const __m128& v2){
return _mm_sub_ps(v1, v2);
}
inline __m128 operator*(const __m128& v1, const __m128& v2){
return _mm_mul_ps(v1, v2);
}
inline __m128 operator/(const __m128& v1, const __m128& v2){
return _mm_div_ps(v1, v2);
}
#endif
#endif // FSSE_HPP
inastemp @ 5f1c2dd4
Subproject commit 5f1c2dd418c99c2eaa9299633929bbc1ac75001e
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment