From c49f48ce7780f21b8f6489d2454779bbb84e716b Mon Sep 17 00:00:00 2001 From: Akira Saitoh Date: Fri, 10 Mar 2023 09:26:22 +0900 Subject: [PATCH] Add unit tests for arrayset evaluator This commit adds unit tests of arrayset evaluator to ArrayTest test suite. Signed-off-by: Akira Saitoh --- fvtest/compilertriltest/ArrayTest.cpp | 336 ++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) diff --git a/fvtest/compilertriltest/ArrayTest.cpp b/fvtest/compilertriltest/ArrayTest.cpp index 129771d47ed..7ed747f2dee 100644 --- a/fvtest/compilertriltest/ArrayTest.cpp +++ b/fvtest/compilertriltest/ArrayTest.cpp @@ -21,6 +21,8 @@ #include "OpCodeTest.hpp" #include "default_compiler.hpp" +#include "omrformatconsts.h" +#include #include static const int32_t returnValueForArraycmpGreaterThan = 2; @@ -445,3 +447,337 @@ static std::vector> createArraycmpNotEqualParam() { return v; } INSTANTIATE_TEST_CASE_P(ArraycmpTest, ArraycmpNotEqualTest, ::testing::ValuesIn(createArraycmpNotEqualParam())); + +template +void assert_array_equals(std::vector &s1, std::vector &s2) { + auto it1 = s1.begin(); + auto it2 = s2.begin(); + while (it1 != s1.end()) { + ASSERT_EQ(*it1, *it2) << "Found mismatch at pos = " << (it1 - s1.begin()); + it1++; + it2++; + } +} +template +class IntArraysetTestBase : public TRTest::JitTest { + public: + T getValue2(T value) { return value ^ -1;} +}; +template +class FloatArraysetTestBase : public TRTest::JitTest { + public: + T getValue2(T value) { return (value != static_cast(0)) ? static_cast(0) : static_cast(1);} +}; + +#define ParameterizedArraysetTest(baseclass, type, typeName, elemType, elemPrefix, formatString) \ +/** \ + * @brief TestFixture class for arrayset test \ + * \ + * @details Used for arrayset test. \ + * The first parameter is the value set to the array. \ + * The second parameter is the length parameter for the arraycmp evaluator. \ + */ \ +class type##ArraysetTest : public baseclass, \ + public ::testing::WithParamInterface> {}; \ +TEST_P(type##ArraysetTest, UsingConstLenConstVal) { \ + SKIP_ON_ARM(MissingImplementation); \ + SKIP_ON_RISCV(MissingImplementation); \ + SKIP_ON_X86(KnownBug) << "The x86 code generator sets wrong value in certain circumstances (issue #6964)"; \ + auto value = std::get<0>(GetParam()); \ + auto length = std::get<1>(GetParam()); \ + auto value2 = getValue2(value); \ + char inputTrees[1024] = {0}; \ + std::snprintf(inputTrees, sizeof(inputTrees), \ + "(method return=NoType args=[Address]" \ + " (block" \ + " (arrayset address=0 args=[Address " typeName " Int64]" \ + " (aload parm=0)" \ + " (" elemPrefix "const " formatString ")" \ + " (lconst %" OMR_PRId64 "))" \ + " (return)))", \ + value, length \ + ); \ + auto trees = parseString(inputTrees); \ + \ + ASSERT_NOTNULL(trees); \ + \ + Tril::DefaultCompiler compiler(trees); \ + \ + ASSERT_EQ(0, compiler.compile()) << "Compilation failed unexpectedly\n" << "Input trees: " << inputTrees; \ + auto nelem = length / sizeof(elemType); \ + std::vector s1(nelem + 1, value2); \ + std::vector s2(nelem + 1, value); \ + /* Verifies that arrayset does not write past the end */ \ + s2[nelem] = value2; \ + auto entry_point = compiler.getEntryPoint(); \ + entry_point(&s1[0]); \ + assert_array_equals(s1, s2); \ +} \ +TEST_P(type##ArraysetTest, UsingConstLen) { \ + SKIP_ON_ARM(MissingImplementation); \ + SKIP_ON_RISCV(MissingImplementation); \ + SKIP_ON_X86(KnownBug) << "The x86 code generator sets wrong value in certain circumstances (issue #6964)"; \ + auto value = std::get<0>(GetParam()); \ + auto length = std::get<1>(GetParam()); \ + auto value2 = getValue2(value); \ + char inputTrees[1024] = {0}; \ + std::snprintf(inputTrees, sizeof(inputTrees), \ + "(method return=NoType args=[Address, " typeName " ]" \ + " (block" \ + " (arrayset address=0 args=[Address " typeName " Int64]" \ + " (aload parm=0)" \ + " (" elemPrefix "load parm=1)" \ + " (lconst %" OMR_PRId64 "))" \ + " (return)))", \ + length \ + ); \ + auto trees = parseString(inputTrees); \ + \ + ASSERT_NOTNULL(trees); \ + \ + Tril::DefaultCompiler compiler(trees); \ + \ + ASSERT_EQ(0, compiler.compile()) << "Compilation failed unexpectedly\n" << "Input trees: " << inputTrees; \ + auto nelem = length / sizeof(elemType); \ + std::vector s1(nelem + 1, value2); \ + std::vector s2(nelem + 1, value); \ + /* Verifies that arrayset does not write past the end */ \ + s2[nelem] = value2; \ + auto entry_point = compiler.getEntryPoint(); \ + entry_point(&s1[0], value); \ + assert_array_equals(s1, s2); \ +} \ +TEST_P(type##ArraysetTest, UsingVariableLenConstVal) { \ + SKIP_ON_ARM(MissingImplementation); \ + SKIP_ON_RISCV(MissingImplementation); \ + auto value = std::get<0>(GetParam()); \ + auto length = std::get<1>(GetParam()); \ + auto value2 = getValue2(value); \ + char inputTrees[1024] = {0}; \ + std::snprintf(inputTrees, sizeof(inputTrees), \ + "(method return=NoType args=[Address, Int64]" \ + " (block" \ + " (arrayset address=0 args=[Address " typeName " Int64]" \ + " (aload parm=0)" \ + " (" elemPrefix "const " formatString ")" \ + " (lload parm=1))" \ + " (return)))", \ + value \ + ); \ + auto trees = parseString(inputTrees); \ + \ + ASSERT_NOTNULL(trees); \ + \ + Tril::DefaultCompiler compiler(trees); \ + \ + ASSERT_EQ(0, compiler.compile()) << "Compilation failed unexpectedly\n" << "Input trees: " << inputTrees; \ + auto nelem = length / sizeof(elemType); \ + std::vector s1(nelem + 1, value2); \ + std::vector s2(nelem + 1, value); \ + /* Verifies that arrayset does not write past the end */ \ + s2[nelem] = value2; \ + auto entry_point = compiler.getEntryPoint(); \ + entry_point(&s1[0], length); \ + assert_array_equals(s1, s2); \ +} \ +TEST_P(type##ArraysetTest, UsingVariableLen) { \ + SKIP_ON_ARM(MissingImplementation); \ + SKIP_ON_RISCV(MissingImplementation); \ + auto value = std::get<0>(GetParam()); \ + auto length = std::get<1>(GetParam()); \ + auto value2 = getValue2(value); \ + char inputTrees[1024] = {0}; \ + std::snprintf(inputTrees, sizeof(inputTrees), \ + "(method return=NoType args=[Address, " typeName ", Int64]" \ + " (block" \ + " (arrayset address=0 args=[Address " typeName " Int64]" \ + " (aload parm=0)" \ + " (" elemPrefix "load parm=1)" \ + " (lload parm=2))" \ + " (return)))" \ + ); \ + auto trees = parseString(inputTrees); \ + \ + ASSERT_NOTNULL(trees); \ + \ + Tril::DefaultCompiler compiler(trees); \ + \ + ASSERT_EQ(0, compiler.compile()) << "Compilation failed unexpectedly\n" << "Input trees: " << inputTrees; \ + auto nelem = length / sizeof(elemType); \ + std::vector s1(nelem + 1, value2); \ + std::vector s2(nelem + 1, value); \ + /* Verifies that arrayset does not write past the end */ \ + s2[nelem] = value2; \ + auto entry_point = compiler.getEntryPoint(); \ + entry_point(&s1[0], value, length); \ + assert_array_equals(s1, s2); \ +} \ + +ParameterizedArraysetTest(IntArraysetTestBase, Int8, "Int8", int8_t, "b", "%d") +ParameterizedArraysetTest(IntArraysetTestBase, Int16, "Int16", int16_t, "s", "%d") +ParameterizedArraysetTest(IntArraysetTestBase, Int32, "Int32", int32_t, "i", "%d") +ParameterizedArraysetTest(IntArraysetTestBase, Int64, "Int64", int64_t, "l", "%" OMR_PRId64) +ParameterizedArraysetTest(FloatArraysetTestBase, Float, "Float", float, "f", "%f") +ParameterizedArraysetTest(FloatArraysetTestBase, Double, "Double", double, "d", "%lf") + +template +bool arraysetFp_filter(T a) + { + // workaround: avoid failure caused by snprintf("%f") + return std::isnan(a) || (std::abs(a) < 0.01 && a != 0.0); + } + +template +std::vector> generateArraysetParm(const std::vector &constVals) { + int index = 0; + int nVals = constVals.size(); + int step = sizeof(T); + std::vector> v; + for (int length = step; length <= 1024; length += step) { + for (int i = 0; i < 2; i++) { + v.push_back(std::make_tuple(constVals[(index++) % nVals], length)); + } + } + /* Large sized arrays */ + for (int length = 16384; length <= 16416; length += step) { + v.push_back(std::make_tuple(constVals[(index++) % nVals], length)); + } + return v; +} +template +std::vector> generateArraysetParm() { + return generateArraysetParm(TRTest::const_values()); +} +template +std::vector> generateArraysetFPParm() { + return generateArraysetParm(TRTest::filter(TRTest::const_values(), arraysetFp_filter)); +} +INSTANTIATE_TEST_CASE_P(ArraysetTest, Int8ArraysetTest, testing::ValuesIn(generateArraysetParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, Int16ArraysetTest, testing::ValuesIn(generateArraysetParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, Int32ArraysetTest, testing::ValuesIn(generateArraysetParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, Int64ArraysetTest, testing::ValuesIn(generateArraysetParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, FloatArraysetTest, testing::ValuesIn(generateArraysetFPParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, DoubleArraysetTest, testing::ValuesIn(generateArraysetFPParm())); + +#define ParameterizedArraysetUnalignedTest(baseclass, type, typeName, elemType, elemPrefix) \ +/** \ + * @brief TestFixture class for unaligned arrayset test \ + * \ + * @details Used for unaligned arrayset test. \ + * The first parameter is the value set to the array. \ + * The second parameter is the length parameter for the arraycmp evaluator. \ + * The third parameter is the residue of src address modulo 16. \ + */ \ +class type##ArraysetUnalignedTest : public baseclass, \ + public ::testing::WithParamInterface> {}; \ +TEST_P(type##ArraysetUnalignedTest, UsingConstLen) { \ + SKIP_ON_ARM(MissingImplementation); \ + SKIP_ON_RISCV(MissingImplementation); \ + SKIP_ON_X86(KnownBug) << "The x86 code generator sets wrong value in certain circumstances (issue #6964)"; \ + \ + auto value = std::get<0>(GetParam()); \ + auto length = std::get<1>(GetParam()); \ + auto offset = std::get<2>(GetParam()) / sizeof(elemType); \ + auto value2 = getValue2(value); \ + char inputTrees[1024] = {0}; \ + std::snprintf(inputTrees, sizeof(inputTrees), \ + "(method return=NoType args=[Address, " typeName "]" \ + " (block" \ + " (arrayset address=0 args=[Address, " typeName ", Int64]" \ + " (aload parm=0)" \ + " (" elemPrefix "load parm=1)" \ + " (lconst %" OMR_PRId64 "))" \ + " (return)))", \ + length \ + ); \ + auto trees = parseString(inputTrees); \ + \ + ASSERT_NOTNULL(trees); \ + \ + Tril::DefaultCompiler compiler(trees); \ + \ + ASSERT_EQ(0, compiler.compile()) << "Compilation failed unexpectedly\n" << "Input trees: " << inputTrees; \ + auto nelem = length / sizeof(elemType); \ + std::vector s1(nelem + 1 + offset, value2); \ + std::vector s2(nelem + 1 + offset, value2); \ + /* Verifies that arrayset does not write before the beginning or past the end */ \ + std::fill(s2.begin() + offset, s2.end() - 1, value); \ + \ + auto entry_point = compiler.getEntryPoint(); \ + entry_point(&s1[offset], value); \ + assert_array_equals(s1, s2); \ +} \ +TEST_P(type##ArraysetUnalignedTest, UsingVariableLen) { \ + SKIP_ON_ARM(MissingImplementation); \ + SKIP_ON_RISCV(MissingImplementation); \ + \ + auto value = std::get<0>(GetParam()); \ + auto length = std::get<1>(GetParam()); \ + auto offset = std::get<2>(GetParam()) / sizeof(elemType); \ + auto value2 = getValue2(value); \ + char inputTrees[1024] = {0}; \ + std::snprintf(inputTrees, sizeof(inputTrees), \ + "(method return=NoType args=[Address, " typeName ", Int64]" \ + " (block" \ + " (arrayset address=0 args=[Address, " typeName ", Int64]" \ + " (aload parm=0)" \ + " (" elemPrefix "load parm=1)" \ + " (lload parm=2))" \ + " (return)))" \ + ); \ + auto trees = parseString(inputTrees); \ + \ + ASSERT_NOTNULL(trees); \ + \ + Tril::DefaultCompiler compiler(trees); \ + \ + ASSERT_EQ(0, compiler.compile()) << "Compilation failed unexpectedly\n" << "Input trees: " << inputTrees; \ + auto nelem = length / sizeof(elemType); \ + std::vector s1(nelem + 1 + offset, value2); \ + std::vector s2(nelem + 1 + offset, value2); \ + /* Verifies that arrayset does not write before the beginning or past the end */ \ + std::fill(s2.begin() + offset, s2.end() - 1, value); \ + \ + auto entry_point = compiler.getEntryPoint(); \ + entry_point(&s1[offset], value, length); \ + assert_array_equals(s1, s2); \ +} \ + +ParameterizedArraysetUnalignedTest(IntArraysetTestBase, Int8, "Int8", int8_t, "b") +ParameterizedArraysetUnalignedTest(IntArraysetTestBase, Int16, "Int16", int16_t, "s") +ParameterizedArraysetUnalignedTest(IntArraysetTestBase, Int32, "Int32", int32_t, "i") +ParameterizedArraysetUnalignedTest(IntArraysetTestBase, Int64, "Int64", int64_t, "l") +ParameterizedArraysetUnalignedTest(FloatArraysetTestBase, Float, "Float", float, "f") +ParameterizedArraysetUnalignedTest(FloatArraysetTestBase, Double, "Double", double, "d") + +template +std::vector> generateArraysetUnalignedParm(const std::vector &constVals) { + int index = 0; + int nVals = constVals.size(); + int step = sizeof(T); + int m = 4096 / ((1024 / step) * (15 / step)); + int n = std::min(std::max(m, 1), 2); + std::vector> v; + for (int length = step; length <= 1024; length += step) { + for (int offset = step; offset < 16; offset += step) { + for (int i = 0; i < n; i++) { + v.push_back(std::make_tuple(constVals[(index++) % nVals], length, offset)); + } + } + } + return v; +} +template +std::vector> generateArraysetUnalignedParm() { + return generateArraysetUnalignedParm(TRTest::const_values()); +} +template +std::vector> generateArraysetUnalignedFPParm() { + return generateArraysetUnalignedParm(TRTest::filter(TRTest::const_values(), arraysetFp_filter)); +} +INSTANTIATE_TEST_CASE_P(ArraysetTest, Int8ArraysetUnalignedTest, testing::ValuesIn(generateArraysetUnalignedParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, Int16ArraysetUnalignedTest, testing::ValuesIn(generateArraysetUnalignedParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, Int32ArraysetUnalignedTest, testing::ValuesIn(generateArraysetUnalignedParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, Int64ArraysetUnalignedTest, testing::ValuesIn(generateArraysetUnalignedParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, FloatArraysetUnalignedTest, testing::ValuesIn(generateArraysetUnalignedFPParm())); +INSTANTIATE_TEST_CASE_P(ArraysetTest, DoubleArraysetUnalignedTest, testing::ValuesIn(generateArraysetUnalignedFPParm()));