conversions: Use ARM emulation for aarch64 (#967)

The host compiler will not always calculate reference values
the same, depending on optimization level.  It generates
instructions that do not respond to CPU rounding mode in
the same way.  Use QCOM rounding mode emulation to correctly
calculate reference values on aarch64.
This commit is contained in:
Sreelakshmi Haridas Maruthur
2020-10-19 16:08:06 -06:00
committed by GitHub
parent 90c9ea5d7c
commit b165de7649
3 changed files with 38 additions and 20 deletions

View File

@@ -4,7 +4,7 @@ set (${MODULE_NAME}_SOURCES
Sleep.cpp test_conversions.cpp basic_test_conversions.cpp Sleep.cpp test_conversions.cpp basic_test_conversions.cpp
) )
if("${CLConform_TARGET_ARCH}" STREQUAL "ARM") if("${CLConform_TARGET_ARCH}" STREQUAL "ARM" OR "${CLConform_TARGET_ARCH}" STREQUAL "ARM64")
list(APPEND ${MODULE_NAME}_SOURCES fplib.cpp) list(APPEND ${MODULE_NAME}_SOURCES fplib.cpp)
endif() endif()

View File

@@ -21,11 +21,11 @@
#include "harness/mt19937.h" #include "harness/mt19937.h"
#if defined( __arm__ ) && defined( __GNUC__ ) #if (defined(__arm__) || defined(__aarch64__)) && defined(__GNUC__)
#include "fplib.h" #include "fplib.h"
#endif #endif
#if defined( __arm__ ) && defined( __GNUC__ ) #if (defined(__arm__) || defined(__aarch64__)) && defined(__GNUC__)
/* Rounding modes and saturation for use with qcom 64 bit to float conversion library */ /* Rounding modes and saturation for use with qcom 64 bit to float conversion library */
bool qcom_sat; bool qcom_sat;
roundingMode qcom_rm; roundingMode qcom_rm;
@@ -759,12 +759,18 @@ static void ulong2float( void *out, void *in)
((float*) out)[0] = (l == 0 ? 0.0f : (((cl_long)l < 0) ? result * 2.0f : result)); ((float*) out)[0] = (l == 0 ? 0.0f : (((cl_long)l < 0) ? result * 2.0f : result));
#else #else
cl_ulong l = ((cl_ulong*) in)[0]; cl_ulong l = ((cl_ulong*) in)[0];
#if defined( __arm__ ) && defined( __GNUC__ ) #if (defined(__arm__) || defined(__aarch64__)) && defined(__GNUC__)
/* ARM VFP doesn't have hardware instruction for converting from 64-bit integer to float types, hence GCC ARM uses the floating-point emulation code /* ARM VFP doesn't have hardware instruction for converting from 64-bit
* despite which -mfloat-abi setting it is. But the emulation code in libgcc.a has only one rounding mode (round to nearest even in this case) * integer to float types, hence GCC ARM uses the floating-point emulation
* code despite which -mfloat-abi setting it is. But the emulation code in
* libgcc.a has only one rounding mode (round to nearest even in this case)
* and ignores the user rounding mode setting in hardware. * and ignores the user rounding mode setting in hardware.
* As a result setting rounding modes in hardware won't give correct rounding results for type covert from 64-bit integer to float using GCC for ARM compiler * As a result setting rounding modes in hardware won't give correct
* so for testing different rounding modes, we need to use alternative reference function */ * rounding results for type covert from 64-bit integer to float using GCC
* for ARM compiler so for testing different rounding modes, we need to use
* alternative reference function. ARM64 does have an instruction, however
* we cannot guarantee the compiler will use it. On all ARM architechures
* use emulation to calculate reference.*/
((float*) out)[0] = qcom_u64_2_f32(l, qcom_sat, qcom_rm); ((float*) out)[0] = qcom_u64_2_f32(l, qcom_sat, qcom_rm);
#else #else
((float*) out)[0] = (l == 0 ? 0.0f : (float) l); // Per IEEE-754-2008 5.4.1, 0's always convert to +0.0 ((float*) out)[0] = (l == 0 ? 0.0f : (float) l); // Per IEEE-754-2008 5.4.1, 0's always convert to +0.0
@@ -806,12 +812,18 @@ static void long2float( void *out, void *in)
((float*) out)[0] = (l == 0 ? 0.0f : result); // Per IEEE-754-2008 5.4.1, 0's always convert to +0.0 ((float*) out)[0] = (l == 0 ? 0.0f : result); // Per IEEE-754-2008 5.4.1, 0's always convert to +0.0
#else #else
cl_long l = ((cl_long*) in)[0]; cl_long l = ((cl_long*) in)[0];
#if defined( __arm__ ) && defined( __GNUC__ ) #if (defined(__arm__) || defined(__aarch64__)) && defined(__GNUC__)
/* ARM VFP doesn't have hardware instruction for converting from 64-bit integer to float types, hence GCC ARM uses the floating-point emulation code /* ARM VFP doesn't have hardware instruction for converting from 64-bit
* despite which -mfloat-abi setting it is. But the emulation code in libgcc.a has only one rounding mode (round to nearest even in this case) * integer to float types, hence GCC ARM uses the floating-point emulation
* code despite which -mfloat-abi setting it is. But the emulation code in
* libgcc.a has only one rounding mode (round to nearest even in this case)
* and ignores the user rounding mode setting in hardware. * and ignores the user rounding mode setting in hardware.
* As a result setting rounding modes in hardware won't give correct rounding results for type covert from 64-bit integer to float using GCC for ARM compiler * As a result setting rounding modes in hardware won't give correct
* so for testing different rounding modes, we need to use alternative reference function */ * rounding results for type covert from 64-bit integer to float using GCC
* for ARM compiler so for testing different rounding modes, we need to use
* alternative reference function. ARM64 does have an instruction, however
* we cannot guarantee the compiler will use it. On all ARM architechures
* use emulation to calculate reference.*/
((float*) out)[0] = (l == 0 ? 0.0f : qcom_s64_2_f32(l, qcom_sat, qcom_rm)); ((float*) out)[0] = (l == 0 ? 0.0f : qcom_s64_2_f32(l, qcom_sat, qcom_rm));
#else #else
((float*) out)[0] = (l == 0 ? 0.0f : (float) l); // Per IEEE-754-2008 5.4.1, 0's always convert to +0.0 ((float*) out)[0] = (l == 0 ? 0.0f : (float) l); // Per IEEE-754-2008 5.4.1, 0's always convert to +0.0

View File

@@ -65,7 +65,7 @@
#define kCallStyleCount (kVectorSizeCount + 1 /* for implicit scalar */) #define kCallStyleCount (kVectorSizeCount + 1 /* for implicit scalar */)
#if defined( __arm__ ) && defined( __GNUC__ ) #if (defined(__arm__) || defined(__aarch64__)) && defined(__GNUC__)
#include "fplib.h" #include "fplib.h"
extern bool qcom_sat; extern bool qcom_sat;
extern roundingMode qcom_rm; extern roundingMode qcom_rm;
@@ -884,12 +884,18 @@ cl_int PrepareReference( cl_uint job_id, cl_uint thread_id, void *p )
if( info->sat ) if( info->sat )
f = gSaturatedConversions[ outType ][ inType ]; f = gSaturatedConversions[ outType ][ inType ];
#if defined( __arm__ ) && defined( __GNUC__ ) #if (defined(__arm__) || defined(__aarch64__)) && defined(__GNUC__)
/* ARM VFP doesn't have hardware instruction for converting from 64-bit integer to float types, hence GCC ARM uses the floating-point emulation code /* ARM VFP doesn't have hardware instruction for converting from 64-bit
* despite which -mfloat-abi setting it is. But the emulation code in libgcc.a has only one rounding mode (round to nearest even in this case) * integer to float types, hence GCC ARM uses the floating-point
* and ignores the user rounding mode setting in hardware. * emulation code despite which -mfloat-abi setting it is. But the
* As a result setting rounding modes in hardware won't give correct rounding results for type covert from 64-bit integer to float using GCC for ARM compiler * emulation code in libgcc.a has only one rounding mode (round to
* so for testing different rounding modes, we need to use alternative reference function */ * nearest even in this case) and ignores the user rounding mode setting
* in hardware. As a result setting rounding modes in hardware won't
* give correct rounding results for type covert from 64-bit integer to
* float using GCC for ARM compiler so for testing different rounding
* modes, we need to use alternative reference function. ARM64 does have
* an instruction, however we cannot guarantee the compiler will use it.
* On all ARM architechures use emulation to calculate reference.*/
switch (round) switch (round)
{ {
/* conversions to floating-point type use the current rounding mode. /* conversions to floating-point type use the current rounding mode.