Remove runtime NEON detection

Chrome does not detect NEON instruction set at runtime in WebRTC code starting
with M50, which is now in Stable. Remove support for runtime detection for
simplicity.

The only remaining piece of Chrome that will continue to depend on runtime
detection is /net, where devices with _broken_ neon support are also detected,
and it is not configurable via GYP/GN.

BUG=522035
NOPRESUBMIT=true

Review-Url: https://codereview.webrtc.org/1955413003
Cr-Commit-Position: refs/heads/master@{#12778}
This commit is contained in:
pasko 2016-05-17 10:56:40 -07:00 committed by Commit bot
parent de8739c120
commit e305d956c0
26 changed files with 38 additions and 116 deletions

View File

@ -137,8 +137,6 @@ config("common_config") {
defines += [ "WEBRTC_ARCH_ARM_V7" ]
if (arm_use_neon) {
defines += [ "WEBRTC_HAS_NEON" ]
} else if (arm_optionally_use_neon) {
defines += [ "WEBRTC_DETECT_NEON" ]
}
}
}

View File

@ -221,7 +221,7 @@
['target_arch=="arm" or target_arch=="arm64" or target_arch=="mipsel"', {
'prefer_fixed_point%': 1,
}],
['(target_arch=="arm" and (arm_neon==1 or arm_neon_optional==1)) or target_arch=="arm64"', {
['(target_arch=="arm" and arm_neon==1) or target_arch=="arm64"', {
'build_with_neon%': 1,
}],
['OS!="ios" and (target_arch!="arm" or arm_version>=7) and target_arch!="mips64el"', {
@ -352,9 +352,6 @@
['arm_neon==1', {
'defines': ['WEBRTC_HAS_NEON',],
}],
['arm_neon==0 and arm_neon_optional==1', {
'defines': ['WEBRTC_DETECT_NEON',],
}],
],
}],
],

View File

@ -85,8 +85,7 @@ declare_args() {
# Determines whether NEON code will be built.
rtc_build_with_neon =
(current_cpu == "arm" && (arm_use_neon || arm_optionally_use_neon)) ||
current_cpu == "arm64"
(current_cpu == "arm" && arm_use_neon) || current_cpu == "arm64"
# Enable this to use HW H.264 encoder/decoder on iOS PeerConnections.
# Enabling this may break interop with Android clients that support H264.

View File

@ -61,13 +61,6 @@ FIRFilter* FIRFilter::Create(const float* coefficients,
#elif defined(WEBRTC_HAS_NEON)
filter =
new FIRFilterNEON(coefficients, coefficients_length, max_input_length);
#elif defined(WEBRTC_DETECT_NEON)
if (WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) {
filter =
new FIRFilterNEON(coefficients, coefficients_length, max_input_length);
} else {
filter = new FIRFilterC(coefficients, coefficients_length);
}
#else
filter = new FIRFilterC(coefficients, coefficients_length);
#endif

View File

@ -136,12 +136,6 @@ void SincResampler::InitializeCPUSpecificFeatures() {
#elif defined(WEBRTC_HAS_NEON)
#define CONVOLVE_FUNC Convolve_NEON
void SincResampler::InitializeCPUSpecificFeatures() {}
#elif defined(WEBRTC_DETECT_NEON)
#define CONVOLVE_FUNC convolve_proc_
void SincResampler::InitializeCPUSpecificFeatures() {
convolve_proc_ = WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON ?
Convolve_NEON : Convolve_C;
}
#else
// Unknown architecture.
#define CONVOLVE_FUNC Convolve_C

View File

@ -107,7 +107,7 @@ class SincResampler {
static float Convolve_SSE(const float* input_ptr, const float* k1,
const float* k2,
double kernel_interpolation_factor);
#elif defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON)
#elif defined(WEBRTC_HAS_NEON)
static float Convolve_NEON(const float* input_ptr, const float* k1,
const float* k2,
double kernel_interpolation_factor);

View File

@ -106,10 +106,8 @@ extern "C" {
// Initialize SPL. Currently it contains only function pointer initialization.
// If the underlying platform is known to be ARM-Neon (WEBRTC_HAS_NEON defined),
// the pointers will be assigned to code optimized for Neon; otherwise
// if run-time Neon detection (WEBRTC_DETECT_NEON) is enabled, the pointers
// will be assigned to either Neon code or generic C code; otherwise, generic C
// code will be assigned.
// the pointers will be assigned to code optimized for Neon; otherwise, generic
// C code will be assigned.
// Note that this function MUST be called in any application that uses SPL
// functions.
void WebRtcSpl_Init();
@ -153,7 +151,7 @@ void WebRtcSpl_ZerosArrayW32(int32_t* vector,
typedef int16_t (*MaxAbsValueW16)(const int16_t* vector, size_t length);
extern MaxAbsValueW16 WebRtcSpl_MaxAbsValueW16;
int16_t WebRtcSpl_MaxAbsValueW16C(const int16_t* vector, size_t length);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
int16_t WebRtcSpl_MaxAbsValueW16Neon(const int16_t* vector, size_t length);
#endif
#if defined(MIPS32_LE)
@ -170,7 +168,7 @@ int16_t WebRtcSpl_MaxAbsValueW16_mips(const int16_t* vector, size_t length);
typedef int32_t (*MaxAbsValueW32)(const int32_t* vector, size_t length);
extern MaxAbsValueW32 WebRtcSpl_MaxAbsValueW32;
int32_t WebRtcSpl_MaxAbsValueW32C(const int32_t* vector, size_t length);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
int32_t WebRtcSpl_MaxAbsValueW32Neon(const int32_t* vector, size_t length);
#endif
#if defined(MIPS_DSP_R1_LE)
@ -187,7 +185,7 @@ int32_t WebRtcSpl_MaxAbsValueW32_mips(const int32_t* vector, size_t length);
typedef int16_t (*MaxValueW16)(const int16_t* vector, size_t length);
extern MaxValueW16 WebRtcSpl_MaxValueW16;
int16_t WebRtcSpl_MaxValueW16C(const int16_t* vector, size_t length);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
int16_t WebRtcSpl_MaxValueW16Neon(const int16_t* vector, size_t length);
#endif
#if defined(MIPS32_LE)
@ -204,7 +202,7 @@ int16_t WebRtcSpl_MaxValueW16_mips(const int16_t* vector, size_t length);
typedef int32_t (*MaxValueW32)(const int32_t* vector, size_t length);
extern MaxValueW32 WebRtcSpl_MaxValueW32;
int32_t WebRtcSpl_MaxValueW32C(const int32_t* vector, size_t length);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
int32_t WebRtcSpl_MaxValueW32Neon(const int32_t* vector, size_t length);
#endif
#if defined(MIPS32_LE)
@ -221,7 +219,7 @@ int32_t WebRtcSpl_MaxValueW32_mips(const int32_t* vector, size_t length);
typedef int16_t (*MinValueW16)(const int16_t* vector, size_t length);
extern MinValueW16 WebRtcSpl_MinValueW16;
int16_t WebRtcSpl_MinValueW16C(const int16_t* vector, size_t length);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
int16_t WebRtcSpl_MinValueW16Neon(const int16_t* vector, size_t length);
#endif
#if defined(MIPS32_LE)
@ -238,7 +236,7 @@ int16_t WebRtcSpl_MinValueW16_mips(const int16_t* vector, size_t length);
typedef int32_t (*MinValueW32)(const int32_t* vector, size_t length);
extern MinValueW32 WebRtcSpl_MinValueW32;
int32_t WebRtcSpl_MinValueW32C(const int32_t* vector, size_t length);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
int32_t WebRtcSpl_MinValueW32Neon(const int32_t* vector, size_t length);
#endif
#if defined(MIPS32_LE)
@ -531,7 +529,7 @@ void WebRtcSpl_CrossCorrelationC(int32_t* cross_correlation,
size_t dim_cross_correlation,
int right_shifts,
int step_seq2);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
void WebRtcSpl_CrossCorrelationNeon(int32_t* cross_correlation,
const int16_t* seq1,
const int16_t* seq2,
@ -698,7 +696,7 @@ int WebRtcSpl_DownsampleFastC(const int16_t* data_in,
size_t coefficients_length,
int factor,
size_t delay);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
int WebRtcSpl_DownsampleFastNeon(const int16_t* data_in,
size_t data_in_length,
int16_t* data_out,

View File

@ -28,8 +28,7 @@ CrossCorrelation WebRtcSpl_CrossCorrelation;
DownsampleFast WebRtcSpl_DownsampleFast;
ScaleAndAddVectorsWithRound WebRtcSpl_ScaleAndAddVectorsWithRound;
#if (defined(WEBRTC_DETECT_NEON) || !defined(WEBRTC_HAS_NEON)) && \
!defined(MIPS32_LE)
#if (!defined(WEBRTC_HAS_NEON)) && !defined(MIPS32_LE)
/* Initialize function pointers to the generic C version. */
static void InitPointersToC() {
WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16C;
@ -45,7 +44,7 @@ static void InitPointersToC() {
}
#endif
#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
/* Initialize function pointers to the Neon version. */
static void InitPointersToNeon() {
WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16Neon;
@ -84,19 +83,13 @@ static void InitPointersToMIPS() {
#endif
static void InitFunctionPointers(void) {
#if defined(WEBRTC_DETECT_NEON)
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
InitPointersToNeon();
} else {
InitPointersToC();
}
#elif defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
InitPointersToNeon();
#elif defined(MIPS32_LE)
InitPointersToMIPS();
#else
InitPointersToC();
#endif /* WEBRTC_DETECT_NEON */
#endif /* WEBRTC_HAS_NEON */
}
#if defined(WEBRTC_POSIX)

View File

@ -90,7 +90,7 @@ void WebRtcIsacfix_Spec2TimeC(int16_t* inreQ7,
int32_t* outre1Q16,
int32_t* outre2Q16);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
void WebRtcIsacfix_Time2SpecNeon(int16_t* inre1Q9,
int16_t* inre2Q9,
int16_t* outre,
@ -174,7 +174,7 @@ void WebRtcIsacfix_FilterMaLoopC(int16_t input0,
int32_t* ptr1,
int32_t* ptr2);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
int WebRtcIsacfix_AutocorrNeon(int32_t* __restrict r,
const int16_t* __restrict x,
int16_t N,

View File

@ -147,7 +147,7 @@ void WebRtcIsacfix_MatrixProduct2C(const int16_t matrix0[],
const int matrix0_index_factor,
const int matrix0_index_step);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
void WebRtcIsacfix_MatrixProduct1Neon(const int16_t matrix0[],
const int32_t matrix1[],
int32_t matrix_product[],

View File

@ -60,7 +60,7 @@ void WebRtcIsacfix_AllpassFilter2FixDec16C(
int32_t *filter_state_ch1,
int32_t *filter_state_ch2);
#if (defined WEBRTC_DETECT_NEON) || (defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
void WebRtcIsacfix_AllpassFilter2FixDec16Neon(
int16_t *data_ch1,
int16_t *data_ch2,

View File

@ -64,11 +64,7 @@ class FilterBanksTest : public testing::Test {
TEST_F(FilterBanksTest, AllpassFilter2FixDec16Test) {
CalculateResidualEnergyTester(WebRtcIsacfix_AllpassFilter2FixDec16C);
#ifdef WEBRTC_DETECT_NEON
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
CalculateResidualEnergyTester(WebRtcIsacfix_AllpassFilter2FixDec16Neon);
}
#elif defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
CalculateResidualEnergyTester(WebRtcIsacfix_AllpassFilter2FixDec16Neon);
#endif
}

View File

@ -59,11 +59,7 @@ class FiltersTest : public testing::Test {
TEST_F(FiltersTest, AutocorrFixTest) {
FiltersTester(WebRtcIsacfix_AutocorrC);
#ifdef WEBRTC_DETECT_NEON
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
FiltersTester(WebRtcIsacfix_AutocorrNeon);
}
#elif defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
FiltersTester(WebRtcIsacfix_AutocorrNeon);
#endif
}

View File

@ -201,7 +201,7 @@ int16_t WebRtcIsacfix_FreeInternal(ISACFIX_MainStruct *ISAC_main_inst)
* This function initializes function pointers for ARM Neon platform.
*/
#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
static void WebRtcIsacfix_InitNeon(void) {
WebRtcIsacfix_AutocorrFix = WebRtcIsacfix_AutocorrNeon;
WebRtcIsacfix_FilterMaLoopFix = WebRtcIsacfix_FilterMaLoopNeon;
@ -253,11 +253,7 @@ static void InitFunctionPointers(void) {
WebRtcIsacfix_MatrixProduct1 = WebRtcIsacfix_MatrixProduct1C;
WebRtcIsacfix_MatrixProduct2 = WebRtcIsacfix_MatrixProduct2C;
#ifdef WEBRTC_DETECT_NEON
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
WebRtcIsacfix_InitNeon();
}
#elif defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
WebRtcIsacfix_InitNeon();
#endif

View File

@ -57,8 +57,6 @@ void WebRtcIsacfix_PCorr2Q32(const int16_t* in, int32_t* logcorQ8) {
ysum32 += in[PITCH_CORR_LEN2 + k - 1] * in[PITCH_CORR_LEN2 + k - 1] >>
scaling;
// TODO(zhongwei.yao): Move this function into a separate NEON code file so
// that WEBRTC_DETECT_NEON could take advantage of it.
#ifdef WEBRTC_HAS_NEON
{
int32_t vbuff[4];

View File

@ -179,22 +179,14 @@ class TransformTest : public testing::Test {
TEST_F(TransformTest, Time2SpecTest) {
Time2SpecTester(WebRtcIsacfix_Time2SpecC);
#ifdef WEBRTC_DETECT_NEON
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
Time2SpecTester(WebRtcIsacfix_Time2SpecNeon);
}
#elif defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
Time2SpecTester(WebRtcIsacfix_Time2SpecNeon);
#endif
}
TEST_F(TransformTest, Spec2TimeTest) {
Spec2TimeTester(WebRtcIsacfix_Spec2TimeC);
#ifdef WEBRTC_DETECT_NEON
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
Spec2TimeTester(WebRtcIsacfix_Spec2TimeNeon);
}
#elif defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
Spec2TimeTester(WebRtcIsacfix_Spec2TimeNeon);
#endif
}

View File

@ -1486,10 +1486,6 @@ AecCore* WebRtcAec_CreateAec(int instance_count) {
#if defined(WEBRTC_HAS_NEON)
WebRtcAec_InitAec_neon();
#elif defined(WEBRTC_DETECT_NEON)
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
WebRtcAec_InitAec_neon();
}
#endif
aec_rdft_init();

View File

@ -239,7 +239,7 @@ void WebRtcAec_InitAec_SSE2(void);
#if defined(MIPS_FPU_LE)
void WebRtcAec_InitAec_mips(void);
#endif
#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
void WebRtcAec_InitAec_neon(void);
#endif

View File

@ -581,9 +581,5 @@ void aec_rdft_init(void) {
#endif
#if defined(WEBRTC_HAS_NEON)
aec_rdft_init_neon();
#elif defined(WEBRTC_DETECT_NEON)
if ((WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) != 0) {
aec_rdft_init_neon();
}
#endif
}

View File

@ -54,7 +54,7 @@ void aec_rdft_inverse_128(float* a);
#if defined(MIPS_FPU_LE)
void aec_rdft_init_mips(void);
#endif
#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
void aec_rdft_init_neon(void);
#endif

View File

@ -365,7 +365,7 @@ static void ResetAdaptiveChannelC(AecmCore* aecm) {
}
// Initialize function pointers for ARM Neon platform.
#if (defined WEBRTC_DETECT_NEON || defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
static void WebRtcAecm_InitNeon(void)
{
WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannelNeon;
@ -512,13 +512,7 @@ int WebRtcAecm_InitCore(AecmCore* const aecm, int samplingFreq) {
WebRtcAecm_StoreAdaptiveChannel = StoreAdaptiveChannelC;
WebRtcAecm_ResetAdaptiveChannel = ResetAdaptiveChannelC;
#ifdef WEBRTC_DETECT_NEON
uint64_t features = WebRtc_GetCPUFeaturesARM();
if ((features & kCPUFeatureNEON) != 0)
{
WebRtcAecm_InitNeon();
}
#elif defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
WebRtcAecm_InitNeon();
#endif

View File

@ -402,7 +402,7 @@ extern ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel;
// For the above function pointers, functions for generic platforms are declared
// and defined as static in file aecm_core.c, while those for ARM Neon platforms
// are declared below and defined in file aecm_core_neon.c.
#if defined(WEBRTC_DETECT_NEON) || defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
void WebRtcAecm_CalcLinearEnergiesNeon(AecmCore* aecm,
const uint16_t* far_spectrum,
int32_t* echo_est,

View File

@ -19,7 +19,7 @@
#include "webrtc/modules/audio_processing/ns/nsx_core.h"
#include "webrtc/system_wrappers/include/cpu_features_wrapper.h"
#if (defined WEBRTC_DETECT_NEON || defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
/* Tables are defined in ARM assembly files. */
extern const int16_t WebRtcNsx_kLogTable[9];
extern const int16_t WebRtcNsx_kCounterDiv[201];
@ -65,7 +65,7 @@ static const int16_t WebRtcNsx_kLogTableFrac[256] = {
237, 238, 238, 239, 240, 241, 241, 242, 243, 244, 244, 245, 246, 247, 247,
248, 249, 249, 250, 251, 252, 252, 253, 254, 255, 255
};
#endif // WEBRTC_DETECT_NEON || WEBRTC_HAS_NEON
#endif // WEBRTC_HAS_NEON
// Skip first frequency bins during estimation. (0 <= value < 64)
static const size_t kStartBand = 5;
@ -557,7 +557,7 @@ AnalysisUpdate WebRtcNsx_AnalysisUpdate;
Denormalize WebRtcNsx_Denormalize;
NormalizeRealBuffer WebRtcNsx_NormalizeRealBuffer;
#if (defined WEBRTC_DETECT_NEON || defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
// Initialize function pointers for ARM Neon platform.
static void WebRtcNsx_InitNeon(void) {
WebRtcNsx_NoiseEstimation = WebRtcNsx_NoiseEstimationNeon;
@ -762,12 +762,7 @@ int32_t WebRtcNsx_InitCore(NoiseSuppressionFixedC* inst, uint32_t fs) {
WebRtcNsx_Denormalize = DenormalizeC;
WebRtcNsx_NormalizeRealBuffer = NormalizeRealBufferC;
#ifdef WEBRTC_DETECT_NEON
uint64_t features = WebRtc_GetCPUFeaturesARM();
if ((features & kCPUFeatureNEON) != 0) {
WebRtcNsx_InitNeon();
}
#elif defined(WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
WebRtcNsx_InitNeon();
#endif

View File

@ -215,7 +215,7 @@ void WebRtcNsx_SpeechNoiseProb(NoiseSuppressionFixedC* inst,
uint32_t* priorLocSnr,
uint32_t* postLocSnr);
#if (defined WEBRTC_DETECT_NEON || defined WEBRTC_HAS_NEON)
#if defined(WEBRTC_HAS_NEON)
// For the above function pointers, functions for generic platforms are declared
// and defined as static in file nsx_core.c, while those for ARM Neon platforms
// are declared below and defined in file nsx_core_neon.c.

View File

@ -45,14 +45,6 @@ std::unique_ptr<DenoiserFilter> DenoiserFilter::Create(
filter.reset(new DenoiserFilterNEON());
if (cpu_type != nullptr)
*cpu_type = CPU_NEON;
#elif defined(WEBRTC_DETECT_NEON)
if (WebRtc_GetCPUFeaturesARM() & kCPUFeatureNEON) {
filter.reset(new DenoiserFilterNEON());
if (cpu_type != nullptr)
*cpu_type = CPU_NEON;
} else {
filter.reset(new DenoiserFilterC());
}
#else
filter.reset(new DenoiserFilterC());
#endif

View File

@ -57,8 +57,7 @@
// TODO(zhongwei.yao): WEBRTC_CPU_DETECTION is only used in one place; we should
// probably just remove it.
#if (defined(WEBRTC_ARCH_X86_FAMILY) && !defined(__SSE2__)) || \
defined(WEBRTC_DETECT_NEON)
#if (defined(WEBRTC_ARCH_X86_FAMILY) && !defined(__SSE2__))
#define WEBRTC_CPU_DETECTION
#endif