diff --git a/fvtest/compilertriltest/ArithmeticTest.cpp b/fvtest/compilertriltest/ArithmeticTest.cpp index c9302e997f..b441896ec7 100644 --- a/fvtest/compilertriltest/ArithmeticTest.cpp +++ b/fvtest/compilertriltest/ArithmeticTest.cpp @@ -23,15 +23,6 @@ #include "default_compiler.hpp" #include "omrformatconsts.h" -#include - -#if defined(J9ZOS390) || defined(AIXPPC) -namespace std -{ - using ::isnan; -} -#endif - template T add(T l, T r) { return l + r; @@ -713,6 +704,10 @@ class FloatArithmetic : public TRTest::BinaryOpTest {}; TEST_P(FloatArithmetic, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.lhs) || std::isnan(param.rhs) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Float" @@ -736,11 +731,7 @@ TEST_P(FloatArithmetic, UsingConst) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.lhs, param.rhs); volatile auto act = entry_point(); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } TEST_P(FloatArithmetic, UsingLoadParam) { @@ -767,16 +758,16 @@ TEST_P(FloatArithmetic, UsingLoadParam) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.lhs, param.rhs); volatile auto act = entry_point(param.lhs, param.rhs); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } TEST_P(FloatArithmetic, UsingLoadParamAndLoadConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.lhs) || std::isnan(param.rhs) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Float args=[Float]" @@ -799,11 +790,7 @@ TEST_P(FloatArithmetic, UsingLoadParamAndLoadConst) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.lhs, param.rhs); volatile auto act = entry_point(param.lhs); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } INSTANTIATE_TEST_CASE_P(ArithmeticTest, FloatArithmetic, ::testing::Combine( @@ -1051,6 +1038,10 @@ class DoubleArithmetic : public TRTest::BinaryOpTest {}; TEST_P(DoubleArithmetic, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.lhs) || std::isnan(param.rhs) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Double" @@ -1074,11 +1065,7 @@ TEST_P(DoubleArithmetic, UsingConst) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.lhs, param.rhs); volatile auto act = entry_point(); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } TEST_P(DoubleArithmetic, UsingLoadParam) { @@ -1105,16 +1092,16 @@ TEST_P(DoubleArithmetic, UsingLoadParam) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.lhs, param.rhs); volatile auto act = entry_point(param.lhs, param.rhs); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } TEST_P(DoubleArithmetic, UsingLoadParamAndLoadConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.lhs) || std::isnan(param.rhs) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Double args=[Double]" @@ -1137,11 +1124,7 @@ TEST_P(DoubleArithmetic, UsingLoadParamAndLoadConst) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.lhs, param.rhs); volatile auto act = entry_point(param.lhs); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } INSTANTIATE_TEST_CASE_P(ArithmeticTest, DoubleArithmetic, ::testing::Combine( @@ -1170,6 +1153,10 @@ class FloatUnaryArithmetic : public TRTest::UnaryOpTest {}; TEST_P(FloatUnaryArithmetic, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.value) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Float" @@ -1191,11 +1178,7 @@ TEST_P(FloatUnaryArithmetic, UsingConst) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.value); volatile auto act = entry_point(); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } TEST_P(FloatUnaryArithmetic, UsingLoadParam) { @@ -1221,11 +1204,7 @@ TEST_P(FloatUnaryArithmetic, UsingLoadParam) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.value); volatile auto act = entry_point(param.value); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } INSTANTIATE_TEST_CASE_P(ArithmeticTest, FloatUnaryArithmetic, ::testing::Combine( @@ -1245,6 +1224,10 @@ class DoubleUnaryArithmetic : public TRTest::UnaryOpTest {}; TEST_P(DoubleUnaryArithmetic, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.value) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Double" @@ -1266,11 +1249,7 @@ TEST_P(DoubleUnaryArithmetic, UsingConst) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.value); volatile auto act = entry_point(); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } TEST_P(DoubleUnaryArithmetic, UsingLoadParam) { @@ -1296,11 +1275,7 @@ TEST_P(DoubleUnaryArithmetic, UsingLoadParam) { auto entry_point = compiler.getEntryPoint(); volatile auto exp = param.oracle(param.value); volatile auto act = entry_point(param.value); - if (std::isnan(exp)) { - ASSERT_EQ(std::isnan(exp), std::isnan(act)); - } else { - ASSERT_EQ(exp, act); - } + ASSERT_EQ(exp, act); } INSTANTIATE_TEST_CASE_P(ArithmeticTest, DoubleUnaryArithmetic, ::testing::Combine( diff --git a/fvtest/compilertriltest/CMakeLists.txt b/fvtest/compilertriltest/CMakeLists.txt index 7338a2c990..df820b0e21 100644 --- a/fvtest/compilertriltest/CMakeLists.txt +++ b/fvtest/compilertriltest/CMakeLists.txt @@ -30,6 +30,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) omr_add_executable(comptest main.cpp + JitTest.cpp JitTestUtilitiesTest.cpp ILValidatorTest.cpp ArithmeticTest.cpp diff --git a/fvtest/compilertriltest/CompareTest.cpp b/fvtest/compilertriltest/CompareTest.cpp index f98fb96bcc..15981032d2 100644 --- a/fvtest/compilertriltest/CompareTest.cpp +++ b/fvtest/compilertriltest/CompareTest.cpp @@ -731,26 +731,38 @@ bool smallFp_filter(std::tuple a) } int32_t fcmpeq(float l, float r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l == r) ? 1 : 0; } int32_t fcmpne(float l, float r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l != r) ? 1 : 0; } int32_t fcmpgt(float l, float r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l > r) ? 1 : 0; } int32_t fcmpge(float l, float r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l >= r) ? 1 : 0; } int32_t fcmplt(float l, float r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l < r) ? 1 : 0; } int32_t fcmple(float l, float r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l <= r) ? 1 : 0; } @@ -759,6 +771,13 @@ class FloatCompare : public TRTest::OpCodeTest {}; TEST_P(FloatCompare, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( param.opcode == "fcmpne" && (std::isnan(param.lhs) || std::isnan(param.rhs)) ) { + SKIP_ON_POWER(KnownBug) << "fcmpne returns wrong value on POWER (see #5152)"; + } + if ( std::isnan(param.lhs) || std::isnan(param.rhs) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, 1024, "(method return=Int32 " @@ -785,6 +804,10 @@ TEST_P(FloatCompare, UsingConst) { TEST_P(FloatCompare, UsingLoadParam) { auto param = TRTest::to_struct(GetParam()); + if ( param.opcode == "fcmpne" && (std::isnan(param.lhs) || std::isnan(param.rhs)) ) { + SKIP_ON_POWER(KnownBug) << "fcmpne returns wrong value on POWER (see #5152)"; + } + char inputTrees[160] = {0}; std::snprintf(inputTrees, 160, "(method return=Int32 args=[Float, Float] " @@ -821,26 +844,38 @@ INSTANTIATE_TEST_CASE_P(CompareTest, FloatCompare, ::testing::Combine( ))); int32_t dcmpeq(double l, double r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l == r) ? 1 : 0; } int32_t dcmpne(double l, double r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l != r) ? 1 : 0; } int32_t dcmpgt(double l, double r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l > r) ? 1 : 0; } int32_t dcmpge(double l, double r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l >= r) ? 1 : 0; } int32_t dcmplt(double l, double r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l < r) ? 1 : 0; } int32_t dcmple(double l, double r) { + if (std::isnan(l)) return 0; + if (std::isnan(r)) return 0; return (l <= r) ? 1 : 0; } @@ -849,6 +884,13 @@ class DoubleCompare : public TRTest::OpCodeTest {}; TEST_P(DoubleCompare, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( param.opcode == "dcmpne" && (std::isnan(param.lhs) || std::isnan(param.rhs)) ) { + SKIP_ON_POWER(KnownBug) << "dcmpne returns wrong value on POWER (see #5152)"; + } + if ( std::isnan(param.lhs) || std::isnan(param.rhs) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, 1024, "(method return=Int32 " @@ -875,6 +917,10 @@ TEST_P(DoubleCompare, UsingConst) { TEST_P(DoubleCompare, UsingLoadParam) { auto param = TRTest::to_struct(GetParam()); + if ( param.opcode == "dcmpne" && (std::isnan(param.lhs) || std::isnan(param.rhs)) ) { + SKIP_ON_POWER(KnownBug) << "dcmpne returns wrong value on POWER (see #5152)"; + } + char inputTrees[160] = {0}; std::snprintf(inputTrees, 160, "(method return=Int32 args=[Double, Double] " @@ -911,26 +957,38 @@ INSTANTIATE_TEST_CASE_P(CompareTest, DoubleCompare, ::testing::Combine( ))); int32_t iffcmpeq(float l, float r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l == r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t iffcmpne(float l, float r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l != r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t iffcmplt(float l, float r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l < r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t iffcmple(float l, float r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l <= r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t iffcmpge(float l, float r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l >= r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t iffcmpgt(float l, float r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l > r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } @@ -941,6 +999,13 @@ TEST_P(FloatIfCompare, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( param.opcode == "iffcmpne" && (std::isnan(param.lhs) || std::isnan(param.rhs)) ) { + SKIP_ON_POWER(KnownBug) << "iffcmpne returns wrong value on POWER (see #5152)"; + } + if ( std::isnan(param.lhs) || std::isnan(param.rhs) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[256] = {0}; std::snprintf(inputTrees, 256, "(method return=Int32 " @@ -971,6 +1036,10 @@ TEST_P(FloatIfCompare, UsingLoadParam) { auto param = TRTest::to_struct(GetParam()); + if ( param.opcode == "iffcmpne" && (std::isnan(param.lhs) || std::isnan(param.rhs)) ) { + SKIP_ON_POWER(KnownBug) << "iffcmpne returns wrong value on POWER (see #5152)"; + } + char inputTrees[256] = {0}; std::snprintf(inputTrees, 256, "(method return=Int32 args=[Float, Float] " @@ -1009,26 +1078,38 @@ INSTANTIATE_TEST_CASE_P(CompareTest, FloatIfCompare, ::testing::Combine( ))); int32_t ifdcmpeq(double l, double r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l == r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t ifdcmpne(double l, double r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l != r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t ifdcmplt(double l, double r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l < r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t ifdcmple(double l, double r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l <= r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t ifdcmpge(double l, double r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l >= r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } int32_t ifdcmpgt(double l, double r) { + if (std::isnan(l)) return IFCMP_FALSE_NUM; + if (std::isnan(r)) return IFCMP_FALSE_NUM; return (l > r) ? IFCMP_TRUE_NUM : IFCMP_FALSE_NUM; } @@ -1039,6 +1120,13 @@ TEST_P(DoubleIfCompare, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( param.opcode == "ifdcmpne" && (std::isnan(param.lhs) || std::isnan(param.rhs)) ) { + SKIP_ON_POWER(KnownBug) << "ifdcmpne returns wrong value on POWER (see #5152)"; + } + if ( std::isnan(param.lhs) || std::isnan(param.rhs) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, 1024, "(method return=Int32 " @@ -1069,6 +1157,10 @@ TEST_P(DoubleIfCompare, UsingLoadParam) { auto param = TRTest::to_struct(GetParam()); + if ( param.opcode == "ifdcmpne" && (std::isnan(param.lhs) || std::isnan(param.rhs)) ) { + SKIP_ON_POWER(KnownBug) << "ifdcmpne returns wrong value on POWER (see #5152)"; + } + char inputTrees[256] = {0}; std::snprintf(inputTrees, 256, "(method return=Int32 args=[Double, Double] " diff --git a/fvtest/compilertriltest/JitTest.cpp b/fvtest/compilertriltest/JitTest.cpp new file mode 100644 index 0000000000..1ad6073741 --- /dev/null +++ b/fvtest/compilertriltest/JitTest.cpp @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2020, 2020 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at http://eclipse.org/legal/epl-2.0 + * or the Apache License, Version 2.0 which accompanies this distribution + * and is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License, v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception [1] and GNU General Public + * License, version 2 with the OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception + *******************************************************************************/ + +#include "JitTest.hpp" + +namespace testing { +namespace internal { + +template<> +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const float& lhs, + const float& rhs) { + if ((std::isnan(lhs) && std::isnan(rhs)) || (lhs == rhs)) { + return AssertionSuccess(); + } + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +template<> +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const volatile float& lhs, + const volatile float& rhs) { + if ((std::isnan(lhs) && std::isnan(rhs)) || (lhs == rhs)) { + return AssertionSuccess(); + } + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +template<> +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const double& lhs, + const double& rhs) { + if ((std::isnan(lhs) && std::isnan(rhs)) || (lhs == rhs)) { + return AssertionSuccess(); + } + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +template<> +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const volatile double& lhs, + const volatile double& rhs) { + if ((std::isnan(lhs) && std::isnan(rhs)) || (lhs == rhs)) { + return AssertionSuccess(); + } + return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs); +} + +} // namespace internal +} // namespace testing diff --git a/fvtest/compilertriltest/JitTest.hpp b/fvtest/compilertriltest/JitTest.hpp index 96965aa477..ff931fa636 100644 --- a/fvtest/compilertriltest/JitTest.hpp +++ b/fvtest/compilertriltest/JitTest.hpp @@ -21,12 +21,12 @@ #ifndef JITTEST_HPP #define JITTEST_HPP - #include #include #include #include #include +#include #include "control/Options.hpp" #include "optimizer/Optimizer.hpp" #include "ilgen/MethodBuilder.hpp" @@ -38,6 +38,7 @@ #define EXPECT_NULL(pointer) EXPECT_EQ(NULL, (pointer)) #define EXPECT_NOTNULL(pointer) EXPECT_TRUE(NULL != (pointer)) + #define TRIL(code) #code namespace TRTest @@ -465,6 +466,10 @@ inline std::vector const_values() std::numeric_limits::max(), static_cast(std::numeric_limits::min() + 1), static_cast(std::numeric_limits::max() - 1), + std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + std::numeric_limits::quiet_NaN(), + -std::numeric_limits::quiet_NaN(), 0x0000005F, 0x00000088, static_cast(0x80FF0FF0), @@ -494,6 +499,10 @@ inline std::vector const_values() std::numeric_limits::max(), static_cast(std::numeric_limits::min() + 1), static_cast(std::numeric_limits::max() - 1), + std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + std::numeric_limits::quiet_NaN(), + -std::numeric_limits::quiet_NaN(), 0x0000005F, 0x00000088, static_cast(0x80FF0FF0), @@ -599,6 +608,53 @@ class SkipHelper SkipReason reason_; }; +/* + * A workaround for XLC which does not have std::isnan() + */ +#if defined(J9ZOS390) || defined(AIXPPC) +namespace std +{ + using ::isnan; +} +#endif + +/* + * To allow testing against NaNs in floating-point tests using standard + * ASSERT_EQ() and EXPECT_EQ(), we provide specialized comparator for + * float and double types that makes NaN equal to NaN (for testing + * purposes) + */ +namespace testing { +namespace internal { + +template<> +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const float& lhs, + const float& rhs); +template<> +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const volatile float& lhs, + const volatile float& rhs); + +template<> +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const double& lhs, + const double& rhs); + +template<> +AssertionResult CmpHelperEQ(const char* lhs_expression, + const char* rhs_expression, + const volatile double& lhs, + const volatile double& rhs); + + +} // namespace internal +} // namespace testing + + /** * @brief A macro to allow a test to be conditionally skipped * @@ -680,6 +736,20 @@ class SkipHelper #define SKIP_ON_PPC64LE(reason) \ SKIP_ON(OMRPORT_ARCH_PPC64LE, reason) +/* + * @brief A macro to allow a test to be conditionally skipped all supported POWER + * architectures (PPC, PPC64, PPC64le) + * + * The basic syntax for using this macro is: + * + * SKIP_ON_POWER() << ; + * + */ +#define SKIP_ON_POWER(reason) \ + SKIP_IF( !strcmp(OMRPORT_ARCH_PPC, omrsysinfo_get_CPU_architecture()) \ + || !strcmp(OMRPORT_ARCH_PPC64, omrsysinfo_get_CPU_architecture()) \ + || !strcmp(OMRPORT_ARCH_PPC64LE, omrsysinfo_get_CPU_architecture()), reason) + /* * @brief A macro to allow a test to be conditionally skipped on S390 * @@ -730,6 +800,19 @@ class SkipHelper else \ SKIP_ON(OMRPORT_ARCH_S390X, reason) +/* + * @brief A macro to allow a test to be conditionally skipped on z/OS. + * + * The basic syntax for using this macro is: + * + * SKIP_ON_ZOS() << ; + * + */ +#define SKIP_ON_ZOS(reason) \ + SKIP_IF( (!strcmp(OMRPORT_ARCH_S390, omrsysinfo_get_CPU_architecture()) || !strcmp(OMRPORT_ARCH_S390X, omrsysinfo_get_CPU_architecture())) \ + && strcmp("Linux", omrsysinfo_get_OS_type()), reason) + + /* * @brief A macro to allow a test to be conditionally skipped on AMD64 * diff --git a/fvtest/compilertriltest/MaxMinTest.cpp b/fvtest/compilertriltest/MaxMinTest.cpp index b1ebdfcf57..28d5c1a975 100644 --- a/fvtest/compilertriltest/MaxMinTest.cpp +++ b/fvtest/compilertriltest/MaxMinTest.cpp @@ -40,18 +40,26 @@ int64_t lmin(int64_t l, int64_t r) { } float f_max(float l, float r) { + if (std::isnan(l)) return std::numeric_limits::quiet_NaN(); + if (std::isnan(r)) return std::numeric_limits::quiet_NaN(); return std::max(l,r); } float f_min(float l, float r) { + if (std::isnan(l)) return std::numeric_limits::quiet_NaN(); + if (std::isnan(r)) return std::numeric_limits::quiet_NaN(); return std::min(l,r); } double dmax(double l, double r) { + if (std::isnan(l)) return std::numeric_limits::quiet_NaN(); + if (std::isnan(r)) return std::numeric_limits::quiet_NaN(); return std::max(l,r); } double dmin(double l, double r) { + if (std::isnan(l)) return std::numeric_limits::quiet_NaN(); + if (std::isnan(r)) return std::numeric_limits::quiet_NaN(); return std::min(l,r); } @@ -193,6 +201,13 @@ TEST_P(FloatMaxMin, UsingConst) { SKIP_ON_HAMMER(KnownBug) << "The AMD64 code generator currently doesn't support fmax/fmin (see issue #4276)"; auto param = TRTest::to_struct(GetParam()); + + if (std::isnan(param.lhs) || std::isnan(param.rhs)) { + SKIP_ON_POWER(KnownBug) << "fmin / fmax returns wrong value for NaN on POWER (see issue #5156)"; + SKIP_ON_S390(KnownBug) << "fmin / fmax returns wrong value for NaN on Z (see issue #5157)"; + SKIP_ON_S390X(KnownBug) << "fmin / fmax returns wrong value for NaN on Z (see issue #5157)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Float" @@ -223,6 +238,12 @@ TEST_P(FloatMaxMin, UsingLoadParam) { auto param = TRTest::to_struct(GetParam()); + if (std::isnan(param.lhs) || std::isnan(param.rhs)) { + SKIP_ON_POWER(KnownBug) << "fmin / fmax returns wrong value for NaN on POWER (see issue #5156)"; + SKIP_ON_S390(KnownBug) << "fmin / fmax returns wrong value for NaN on Z (see issue #5157)"; + SKIP_ON_S390X(KnownBug) << "fmin / fmax returns wrong value for NaN on Z (see issue #5157)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Float args=[Float, Float]" @@ -256,11 +277,17 @@ INSTANTIATE_TEST_CASE_P(MaxMin, FloatMaxMin, ::testing::Combine( class DoubleMaxMin : public TRTest::BinaryOpTest {}; TEST_P(DoubleMaxMin, UsingConst) { - SKIP_ON_X86(KnownBug) << "The X86 code generator currently doesn't support fmax/fmin (see issue #4276)"; - SKIP_ON_HAMMER(KnownBug) << "The AMD64 code generator currently doesn't support fmax/fmin (see issue #4276)"; + SKIP_ON_X86(KnownBug) << "The X86 code generator currently doesn't support dmax/dmin (see issue #4276)"; + SKIP_ON_HAMMER(KnownBug) << "The AMD64 code generator currently doesn't support dmax/dmin (see issue #4276)"; auto param = TRTest::to_struct(GetParam()); + if (std::isnan(param.lhs) || std::isnan(param.rhs)) { + SKIP_ON_POWER(KnownBug) << "dmin / dmax returns wrong value for NaN on POWER (see issue #5156)"; + SKIP_ON_S390(KnownBug) << "dmin / dmax returns wrong value for NaN on Z (see issue #5157)"; + SKIP_ON_S390X(KnownBug) << "dmin / dmax returns wrong value for NaN on Z (see issue #5157)"; + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Double" @@ -286,11 +313,18 @@ TEST_P(DoubleMaxMin, UsingConst) { } TEST_P(DoubleMaxMin, UsingLoadParam) { - SKIP_ON_X86(KnownBug) << "The X86 code generator currently doesn't support fmax/fmin (see issue #4276)"; - SKIP_ON_HAMMER(KnownBug) << "The AMD64 code generator currently doesn't support fmax/fmin (see issue #4276)"; + SKIP_ON_X86(KnownBug) << "The X86 code generator currently doesn't support dmax/dmin (see issue #4276)"; + SKIP_ON_HAMMER(KnownBug) << "The AMD64 code generator currently doesn't support dmax/dmin (see issue #4276)"; auto param = TRTest::to_struct(GetParam()); + if (std::isnan(param.lhs) || std::isnan(param.rhs)) { + SKIP_ON_POWER(KnownBug) << "dmin / dmax returns wrong value for NaN on POWER (see issue #5156)"; + SKIP_ON_S390(KnownBug) << "dmin / dmax returns wrong value for NaN on Z (see issue #5157)"; + SKIP_ON_S390X(KnownBug) << "dmin / dmax returns wrong value for NaN on Z (see issue #5157)"; + + } + char inputTrees[1024] = {0}; std::snprintf(inputTrees, sizeof(inputTrees), "(method return=Double args=[Double, Double]" diff --git a/fvtest/compilertriltest/TypeConversionTest.cpp b/fvtest/compilertriltest/TypeConversionTest.cpp index b66b91655c..ce197871b0 100644 --- a/fvtest/compilertriltest/TypeConversionTest.cpp +++ b/fvtest/compilertriltest/TypeConversionTest.cpp @@ -954,18 +954,22 @@ bool fp_filter(F a) } int32_t f2i(float x) { + if (std::isnan(x)) return 0; return static_cast(x); } int64_t f2l(float x) { + if (std::isnan(x)) return 0; return static_cast(x); } int32_t d2i(double x) { + if (std::isnan(x)) return 0; return static_cast(x); } int64_t d2l(double x) { + if (std::isnan(x)) return 0; return static_cast(x); } @@ -979,6 +983,10 @@ TEST_P(FloatToInt32, UsingConst) { && (OMRPORT_ARCH_HAMMER == arch), KnownBug) << "f2i test behaves unexpectedly on x86-64 with certain high input values (see issue #3602)"; + if ( std::isnan(param.value) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[160] = {0}; std::snprintf(inputTrees, 160, "(method return=Int32" @@ -1041,6 +1049,10 @@ class FloatToInt64 : public TRTest::UnaryOpTest {}; TEST_P(FloatToInt64, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.value) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[160] = {0}; std::snprintf(inputTrees, 160, "(method return=Int64" @@ -1098,6 +1110,10 @@ class DoubleToInt32 : public TRTest::UnaryOpTest {}; TEST_P(DoubleToInt32, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.value) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[512] = {0}; std::snprintf(inputTrees, 512, "(method return=Int32" @@ -1155,6 +1171,10 @@ class DoubleToInt64 : public TRTest::UnaryOpTest {}; TEST_P(DoubleToInt64, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.value) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[512] = {0}; std::snprintf(inputTrees, 512, "(method return=Int64" @@ -1227,6 +1247,10 @@ class FloatToDouble : public TRTest::UnaryOpTest {}; TEST_P(FloatToDouble, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.value) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[160] = {0}; std::snprintf(inputTrees, 160, "(method return=Double" @@ -1292,6 +1316,10 @@ class DoubleToFloat : public TRTest::UnaryOpTest {}; TEST_P(DoubleToFloat, UsingConst) { auto param = TRTest::to_struct(GetParam()); + if ( std::isnan(param.value) ) { + SKIP_ON_ZOS(KnownBug) << "TRIL parser cannot handle NaN values on zOS (see issue #5183)"; + } + char inputTrees[512] = {0}; std::snprintf(inputTrees, 512, "(method return=Float" diff --git a/fvtest/tril/tril/parser.cpp b/fvtest/tril/tril/parser.cpp index a4ca27362e..8ed747b571 100644 --- a/fvtest/tril/tril/parser.cpp +++ b/fvtest/tril/tril/parser.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include class Token { @@ -236,6 +237,7 @@ class ASTstates { stateTransition(ID, isdigit, ID); stateTransition(MINUS, "0", ZERO); stateTransition(MINUS, isNonZeroDigit, INT); + stateTransition(MINUS, isalpha, ID); stateTransition(ZERO, isdigit, INT); stateTransition(ZERO, "x", HEXINT); stateTransition(HEXINT, isxdigit, HEXINT); @@ -362,7 +364,7 @@ class TokenIter { * INTEGER: [-]?[0-9]+ | [-]?0x[0-9a-fA-F]+ * DOUBLE: [-]?[0-9]+[.][0-9]+ * STRING: \"[^"]*\" - * IDENTIFIER: @?[a-zA-Z][a-zA-Z0-9]* + * IDENTIFIER: [@-]?[a-zA-Z][a-zA-Z0-9]* */ @@ -391,8 +393,32 @@ ASTValue* buildNodeValue(Token token) { } char * cstr = new char[tokenValue.size() + 1]; strcpy(cstr, tokenValue.c_str()); - if (tokenType == Token::STRING_ || tokenType == Token::ID) { - return createStrValue(cstr); + if (tokenType == Token::STRING_) { + return createStrValue(cstr); + } else if (tokenType == Token::ID) { + if (tokenValue == "inf") { + return createFloatingPointValue( std::numeric_limits::infinity()); + } else if (tokenValue == "-inf") { + return createFloatingPointValue(-std::numeric_limits::infinity()); + } else if (tokenValue == "nan") { + return createFloatingPointValue( std::numeric_limits::quiet_NaN()); + } else if (tokenValue == "-nan") { + return createFloatingPointValue(-std::numeric_limits::quiet_NaN()); + } else if (tokenValue == "INF") { + return createFloatingPointValue( std::numeric_limits::infinity()); + } else if (tokenValue == "-INF") { + return createFloatingPointValue(-std::numeric_limits::infinity()); + } else if (tokenValue == "NAN") { + return createFloatingPointValue( std::numeric_limits::quiet_NaN()); + } else if (tokenValue == "-NAN") { + return createFloatingPointValue(-std::numeric_limits::quiet_NaN()); + } else if (tokenValue == "NaNQ") { + return createFloatingPointValue( std::numeric_limits::quiet_NaN()); + } else if (tokenValue == "-NaNQ") { + return createFloatingPointValue(-std::numeric_limits::quiet_NaN()); + } else { + return createStrValue(cstr); + } } else if (tokenType == Token::DOUBLE_) { double tmpdouble = std::atof(cstr); return createFloatingPointValue(tmpdouble);