diff options
Diffstat (limited to 'flang/unittests')
-rw-r--r-- | flang/unittests/Evaluate/reshape.cpp | 2 | ||||
-rw-r--r-- | flang/unittests/Runtime/CMakeLists.txt | 10 | ||||
-rw-r--r-- | flang/unittests/Runtime/buffer.cpp | 115 | ||||
-rw-r--r-- | flang/unittests/Runtime/hello.cpp | 526 | ||||
-rw-r--r-- | flang/unittests/RuntimeGTest/CMakeLists.txt | 7 | ||||
-rw-r--r-- | flang/unittests/RuntimeGTest/MiscIntrinsic.cpp | 70 | ||||
-rw-r--r-- | flang/unittests/RuntimeGTest/Numeric.cpp | 156 | ||||
-rw-r--r-- | flang/unittests/RuntimeGTest/NumericalFormatTest.cpp | 694 | ||||
-rw-r--r-- | flang/unittests/RuntimeGTest/Random.cpp | 63 | ||||
-rw-r--r-- | flang/unittests/RuntimeGTest/Reduction.cpp | 265 | ||||
-rw-r--r-- | flang/unittests/RuntimeGTest/tools.h | 56 |
11 files changed, 1431 insertions, 533 deletions
diff --git a/flang/unittests/Evaluate/reshape.cpp b/flang/unittests/Evaluate/reshape.cpp index bcc8b49f054d..a51acdb5fca8 100644 --- a/flang/unittests/Evaluate/reshape.cpp +++ b/flang/unittests/Evaluate/reshape.cpp @@ -52,7 +52,7 @@ int main() { MATCH(2, pad.GetDimension(1).Extent()); MATCH(3, pad.GetDimension(2).Extent()); - auto result{RESHAPE(*source, *shape, &pad)}; + auto result{RTNAME(Reshape)(*source, *shape, &pad)}; TEST(result.get() != nullptr); result->Check(); MATCH(sizeof(std::int32_t), result->ElementBytes()); diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt index b80eceac7446..cc7ac7277183 100644 --- a/flang/unittests/Runtime/CMakeLists.txt +++ b/flang/unittests/Runtime/CMakeLists.txt @@ -23,11 +23,6 @@ add_flang_nongtest_unittest(format FortranRuntime ) -add_flang_nongtest_unittest(hello - RuntimeTesting - FortranRuntime -) - # This test is not run by default as it requires input. add_executable(external-hello-world external-hello.cpp @@ -46,3 +41,8 @@ add_flang_nongtest_unittest(list-input RuntimeTesting FortranRuntime ) + +add_flang_nongtest_unittest(buffer + RuntimeTesting + FortranRuntime +) diff --git a/flang/unittests/Runtime/buffer.cpp b/flang/unittests/Runtime/buffer.cpp new file mode 100644 index 000000000000..f5eca0338939 --- /dev/null +++ b/flang/unittests/Runtime/buffer.cpp @@ -0,0 +1,115 @@ +#include "../../runtime/buffer.h" +#include "testing.h" +#include <algorithm> +#include <cstdint> +#include <cstring> +#include <memory> + +static constexpr std::size_t tinyBuffer{32}; +using FileOffset = std::int64_t; +using namespace Fortran::runtime; +using namespace Fortran::runtime::io; + +class Store : public FileFrame<Store, tinyBuffer> { +public: + explicit Store(std::size_t bytes = 65536) : bytes_{bytes} { + data_.reset(new char[bytes]); + std::memset(&data_[0], 0, bytes); + } + std::size_t bytes() const { return bytes_; } + void set_enforceSequence(bool yes = true) { enforceSequence_ = yes; } + void set_expect(FileOffset to) { expect_ = to; } + + std::size_t Read(FileOffset at, char *to, std::size_t minBytes, + std::size_t maxBytes, IoErrorHandler &handler) { + if (enforceSequence_ && at != expect_) { + handler.SignalError("Read(%d,%d,%d) not at expected %d", + static_cast<int>(at), static_cast<int>(minBytes), + static_cast<int>(maxBytes), static_cast<int>(expect_)); + } else if (at < 0 || at + minBytes > bytes_) { + handler.SignalError("Read(%d,%d,%d) is out of bounds", + static_cast<int>(at), static_cast<int>(minBytes), + static_cast<int>(maxBytes)); + } + auto result{std::min<std::size_t>(maxBytes, bytes_ - at)}; + std::memcpy(to, &data_[at], result); + expect_ = at + result; + return result; + } + std::size_t Write(FileOffset at, const char *from, std::size_t bytes, + IoErrorHandler &handler) { + if (enforceSequence_ && at != expect_) { + handler.SignalError("Write(%d,%d) not at expected %d", + static_cast<int>(at), static_cast<int>(bytes), + static_cast<int>(expect_)); + } else if (at < 0 || at + bytes > bytes_) { + handler.SignalError("Write(%d,%d) is out of bounds", static_cast<int>(at), + static_cast<int>(bytes)); + } + std::memcpy(&data_[at], from, bytes); + expect_ = at + bytes; + return bytes; + } + +private: + std::size_t bytes_; + std::unique_ptr<char[]> data_; + bool enforceSequence_{false}; + FileOffset expect_{0}; +}; + +inline int ChunkSize(int j, int most) { + // 31, 1, 29, 3, 27, ... + j %= tinyBuffer; + auto chunk{ + static_cast<int>(((j % 2) ? j : (tinyBuffer - 1 - j)) % tinyBuffer)}; + return std::min(chunk, most); +} + +inline int ValueFor(int at) { return (at ^ (at >> 8)) & 0xff; } + +int main() { + StartTests(); + Terminator terminator{__FILE__, __LINE__}; + IoErrorHandler handler{terminator}; + Store store; + store.set_enforceSequence(true); + const auto bytes{static_cast<FileOffset>(store.bytes())}; + // Fill with an assortment of chunks + int at{0}, j{0}; + while (at < bytes) { + auto chunk{ChunkSize(j, static_cast<int>(bytes - at))}; + store.WriteFrame(at, chunk, handler); + char *to{store.Frame()}; + for (int k{0}; k < chunk; ++k) { + to[k] = ValueFor(at + k); + } + at += chunk; + ++j; + } + store.Flush(handler); + // Validate + store.set_expect(0); + at = 0; + while (at < bytes) { + auto chunk{ChunkSize(j, static_cast<int>(bytes - at))}; + std::size_t frame{store.ReadFrame(at, chunk, handler)}; + if (frame < static_cast<std::size_t>(chunk)) { + Fail() << "Badly-sized ReadFrame at " << at << ", chunk=" << chunk + << ", got " << frame << '\n'; + break; + } + const char *from{store.Frame()}; + for (int k{0}; k < chunk; ++k) { + auto expect{static_cast<char>(ValueFor(at + k))}; + if (from[k] != expect) { + Fail() << "At " << at << '+' << k << '(' << (at + k) << "), read " + << (from[k] & 0xff) << ", expected " << static_cast<int>(expect) + << '\n'; + } + } + at += chunk; + ++j; + } + return EndTests(); +} diff --git a/flang/unittests/Runtime/hello.cpp b/flang/unittests/Runtime/hello.cpp deleted file mode 100644 index d17c98e74c13..000000000000 --- a/flang/unittests/Runtime/hello.cpp +++ /dev/null @@ -1,526 +0,0 @@ -// Basic sanity tests of I/O API; exhaustive testing will be done in Fortran - -#include "testing.h" -#include "../../runtime/descriptor.h" -#include "../../runtime/io-api.h" -#include <cstring> - -using namespace Fortran::runtime; -using namespace Fortran::runtime::io; - -static bool test(const char *format, const char *expect, std::string &&got) { - std::string want{expect}; - want.resize(got.length(), ' '); - if (got != want) { - Fail() << '\'' << format << "' failed;\n got '" << got - << "',\nexpected '" << want << "'\n"; - return false; - } - return true; -} - -static void hello() { - char buffer[32]; - const char *format{"(6HHELLO,,A6,2X,I3,1X,'0x',Z8,1X,L1)"}; - auto cookie{IONAME(BeginInternalFormattedOutput)( - buffer, sizeof buffer, format, std::strlen(format))}; - IONAME(OutputAscii)(cookie, "WORLD", 5); - IONAME(OutputInteger64)(cookie, 678); - IONAME(OutputInteger64)(cookie, 0xfeedface); - IONAME(OutputLogical)(cookie, true); - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << "hello: '" << format << "' failed, status " - << static_cast<int>(status) << '\n'; - } else { - test(format, "HELLO, WORLD 678 0xFEEDFACE T", - std::string{buffer, sizeof buffer}); - } -} - -static void multiline() { - char buffer[5][32]; - StaticDescriptor<1> staticDescriptor[2]; - Descriptor &whole{staticDescriptor[0].descriptor()}; - SubscriptValue extent[]{5}; - whole.Establish(TypeCode{CFI_type_char}, sizeof buffer[0], &buffer, 1, extent, - CFI_attribute_pointer); - whole.Dump(); - whole.Check(); - Descriptor §ion{staticDescriptor[1].descriptor()}; - SubscriptValue lowers[]{0}, uppers[]{4}, strides[]{1}; - section.Establish(whole.type(), whole.ElementBytes(), nullptr, 1, extent, - CFI_attribute_pointer); - if (auto error{ - CFI_section(§ion.raw(), &whole.raw(), lowers, uppers, strides)}) { - Fail() << "multiline: CFI_section failed: " << error << '\n'; - return; - } - section.Dump(); - section.Check(); - const char *format{ - "('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,17X,'abcd',1(2I4))"}; - auto cookie{IONAME(BeginInternalArrayFormattedOutput)( - section, format, std::strlen(format))}; - IONAME(OutputAscii)(cookie, "WORLD", 5); - IONAME(OutputAscii)(cookie, "HELLO", 5); - IONAME(OutputInteger64)(cookie, 789); - for (int j{666}; j <= 999; j += 111) { - IONAME(OutputInteger64)(cookie, j); - } - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << "multiline: '" << format << "' failed, status " - << static_cast<int>(status) << '\n'; - } else { - test(format, - ">HELLO, WORLD <" - " " - "789 abcd 666 777" - " 888 999 " - " ", - std::string{buffer[0], sizeof buffer}); - } -} - -static void listInputTest() { - static const char input[]{",1*,(5.,6..)"}; - auto cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)}; - float z[6]; - for (int j{0}; j < 6; ++j) { - z[j] = -(j + 1); - } - for (int j{0}; j < 6; j += 2) { - if (!IONAME(InputComplex32)(cookie, &z[j])) { - Fail() << "InputComplex32 failed\n"; - } - } - auto status{IONAME(EndIoStatement)(cookie)}; - if (status) { - Fail() << "Failed complex list-directed input, status " - << static_cast<int>(status) << '\n'; - } else { - char output[33]; - output[32] = '\0'; - cookie = IONAME(BeginInternalListOutput)(output, 32); - for (int j{0}; j < 6; j += 2) { - if (!IONAME(OutputComplex32)(cookie, z[j], z[j + 1])) { - Fail() << "OutputComplex32 failed\n"; - } - } - status = IONAME(EndIoStatement)(cookie); - static const char expect[33]{" (-1.,-2.) (-3.,-4.) (5.,6.) "}; - if (status) { - Fail() << "Failed complex list-directed output, status " - << static_cast<int>(status) << '\n'; - } else if (std::strncmp(output, expect, 33) != 0) { - Fail() << "Failed complex list-directed output, expected '" << expect - << "', but got '" << output << "'\n"; - } - } -} - -static void descrOutputTest() { - char buffer[9]; - // Formatted - const char *format{"(2A4)"}; - auto cookie{IONAME(BeginInternalFormattedOutput)( - buffer, sizeof buffer, format, std::strlen(format))}; - StaticDescriptor<1> staticDescriptor; - Descriptor &desc{staticDescriptor.descriptor()}; - SubscriptValue extent[]{2}; - char data[2][4]; - std::memcpy(data[0], "ABCD", 4); - std::memcpy(data[1], "EFGH", 4); - desc.Establish(TypeCode{CFI_type_char}, sizeof data[0], &data, 1, extent); - desc.Dump(); - desc.Check(); - IONAME(OutputDescriptor)(cookie, desc); - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << "descrOutputTest: '" << format << "' failed, status " - << static_cast<int>(status) << '\n'; - } else { - test("descrOutputTest(formatted)", "ABCDEFGH ", - std::string{buffer, sizeof buffer}); - } - // List-directed - cookie = IONAME(BeginInternalListOutput)(buffer, sizeof buffer); - IONAME(OutputDescriptor)(cookie, desc); - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << "descrOutputTest: list-directed failed, status " - << static_cast<int>(status) << '\n'; - } else { - test("descrOutputTest(list)", " ABCDEFGH", - std::string{buffer, sizeof buffer}); - } -} - -static void realTest(const char *format, double x, const char *expect) { - char buffer[800]; - auto cookie{IONAME(BeginInternalFormattedOutput)( - buffer, sizeof buffer, format, std::strlen(format))}; - IONAME(OutputReal64)(cookie, x); - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << '\'' << format << "' failed, status " << static_cast<int>(status) - << '\n'; - } else { - test(format, expect, std::string{buffer, sizeof buffer}); - } -} - -static void realInTest( - const char *format, const char *data, std::uint64_t want) { - auto cookie{IONAME(BeginInternalFormattedInput)( - data, std::strlen(data), format, std::strlen(format))}; - union { - double x; - std::uint64_t raw; - } u; - u.raw = 0; - IONAME(EnableHandlers)(cookie, true, true, true, true, true); - IONAME(InputReal64)(cookie, u.x); - char iomsg[65]; - iomsg[0] = '\0'; - iomsg[sizeof iomsg - 1] = '\0'; - IONAME(GetIoMsg)(cookie, iomsg, sizeof iomsg - 1); - auto status{IONAME(EndIoStatement)(cookie)}; - if (status) { - Fail() << '\'' << format << "' failed reading '" << data << "', status " - << static_cast<int>(status) << " iomsg '" << iomsg << "'\n"; - } else if (u.raw != want) { - Fail() << '\'' << format << "' failed reading '" << data << "', want 0x"; - Fail().write_hex(want) << ", got 0x" << u.raw << '\n'; - } -} - -int main() { - StartTests(); - - hello(); - multiline(); - - static const char *zeroes[][2]{ - {"(E32.17,';')", " 0.00000000000000000E+00;"}, - {"(F32.17,';')", " 0.00000000000000000;"}, - {"(G32.17,';')", " 0.0000000000000000 ;"}, - {"(DC,E32.17,';')", " 0,00000000000000000E+00;"}, - {"(DC,F32.17,';')", " 0,00000000000000000;"}, - {"(DC,G32.17,';')", " 0,0000000000000000 ;"}, - {"(D32.17,';')", " 0.00000000000000000D+00;"}, - {"(E32.17E1,';')", " 0.00000000000000000E+0;"}, - {"(G32.17E1,';')", " 0.0000000000000000 ;"}, - {"(E32.17E0,';')", " 0.00000000000000000E+0;"}, - {"(G32.17E0,';')", " 0.0000000000000000 ;"}, - {"(1P,E32.17,';')", " 0.00000000000000000E+00;"}, - {"(1PE32.17,';')", " 0.00000000000000000E+00;"}, // no comma - {"(1P,F32.17,';')", " 0.00000000000000000;"}, - {"(1P,G32.17,';')", " 0.0000000000000000 ;"}, - {"(2P,E32.17,';')", " 00.0000000000000000E+00;"}, - {"(-1P,E32.17,';')", " 0.00000000000000000E+00;"}, - {"(G0,';')", "0.;"}, {}}; - for (int j{0}; zeroes[j][0]; ++j) { - realTest(zeroes[j][0], 0.0, zeroes[j][1]); - } - - static const char *ones[][2]{ - {"(E32.17,';')", " 0.10000000000000000E+01;"}, - {"(F32.17,';')", " 1.00000000000000000;"}, - {"(G32.17,';')", " 1.0000000000000000 ;"}, - {"(E32.17E1,';')", " 0.10000000000000000E+1;"}, - {"(G32.17E1,';')", " 1.0000000000000000 ;"}, - {"(E32.17E0,';')", " 0.10000000000000000E+1;"}, - {"(G32.17E0,';')", " 1.0000000000000000 ;"}, - {"(E32.17E4,';')", " 0.10000000000000000E+0001;"}, - {"(G32.17E4,';')", " 1.0000000000000000 ;"}, - {"(1P,E32.17,';')", " 1.00000000000000000E+00;"}, - {"(1PE32.17,';')", " 1.00000000000000000E+00;"}, // no comma - {"(1P,F32.17,';')", " 10.00000000000000000;"}, - {"(1P,G32.17,';')", " 1.0000000000000000 ;"}, - {"(ES32.17,';')", " 1.00000000000000000E+00;"}, - {"(2P,E32.17,';')", " 10.0000000000000000E-01;"}, - {"(2P,G32.17,';')", " 1.0000000000000000 ;"}, - {"(-1P,E32.17,';')", " 0.01000000000000000E+02;"}, - {"(-1P,G32.17,';')", " 1.0000000000000000 ;"}, - {"(G0,';')", "1.;"}, {}}; - for (int j{0}; ones[j][0]; ++j) { - realTest(ones[j][0], 1.0, ones[j][1]); - } - - realTest("(E32.17,';')", -1.0, " -0.10000000000000000E+01;"); - realTest("(F32.17,';')", -1.0, " -1.00000000000000000;"); - realTest("(G32.17,';')", -1.0, " -1.0000000000000000 ;"); - realTest("(G0,';')", -1.0, "-1.;"); - - volatile union { - double d; - std::uint64_t n; - } u; - u.n = 0x8000000000000000; // -0 - realTest("(E9.1,';')", u.d, " -0.0E+00;"); - realTest("(F4.0,';')", u.d, " -0.;"); - realTest("(G8.0,';')", u.d, "-0.0E+00;"); - realTest("(G8.1,';')", u.d, " -0. ;"); - realTest("(G0,';')", u.d, "-0.;"); - u.n = 0x7ff0000000000000; // +Inf - realTest("(E9.1,';')", u.d, " Inf;"); - realTest("(F9.1,';')", u.d, " Inf;"); - realTest("(G9.1,';')", u.d, " Inf;"); - realTest("(SP,E9.1,';')", u.d, " +Inf;"); - realTest("(SP,F9.1,';')", u.d, " +Inf;"); - realTest("(SP,G9.1,';')", u.d, " +Inf;"); - realTest("(G0,';')", u.d, "Inf;"); - u.n = 0xfff0000000000000; // -Inf - realTest("(E9.1,';')", u.d, " -Inf;"); - realTest("(F9.1,';')", u.d, " -Inf;"); - realTest("(G9.1,';')", u.d, " -Inf;"); - realTest("(G0,';')", u.d, "-Inf;"); - u.n = 0x7ff0000000000001; // NaN - realTest("(E9.1,';')", u.d, " NaN;"); - realTest("(F9.1,';')", u.d, " NaN;"); - realTest("(G9.1,';')", u.d, " NaN;"); - realTest("(G0,';')", u.d, "NaN;"); - u.n = 0xfff0000000000001; // NaN (sign irrelevant) - realTest("(E9.1,';')", u.d, " NaN;"); - realTest("(F9.1,';')", u.d, " NaN;"); - realTest("(G9.1,';')", u.d, " NaN;"); - realTest("(SP,E9.1,';')", u.d, " NaN;"); - realTest("(SP,F9.1,';')", u.d, " NaN;"); - realTest("(SP,G9.1,';')", u.d, " NaN;"); - realTest("(G0,';')", u.d, "NaN;"); - - u.n = 0x3fb999999999999a; // 0.1 rounded - realTest("(E62.55,';')", u.d, - " 0.1000000000000000055511151231257827021181583404541015625E+00;"); - realTest("(E0.0,';')", u.d, "0.E+00;"); - realTest("(E0.55,';')", u.d, - "0.1000000000000000055511151231257827021181583404541015625E+00;"); - realTest("(E0,';')", u.d, ".1E+00;"); - realTest("(F58.55,';')", u.d, - " 0.1000000000000000055511151231257827021181583404541015625;"); - realTest("(F0.0,';')", u.d, "0.;"); - realTest("(F0.55,';')", u.d, - ".1000000000000000055511151231257827021181583404541015625;"); - realTest("(F0,';')", u.d, ".1;"); - realTest("(G62.55,';')", u.d, - " 0.1000000000000000055511151231257827021181583404541015625 ;"); - realTest("(G0.0,';')", u.d, "0.;"); - realTest("(G0.55,';')", u.d, - ".1000000000000000055511151231257827021181583404541015625;"); - realTest("(G0,';')", u.d, ".1;"); - - u.n = 0x3ff8000000000000; // 1.5 - realTest("(E9.2,';')", u.d, " 0.15E+01;"); - realTest("(F4.1,';')", u.d, " 1.5;"); - realTest("(G7.1,';')", u.d, " 2. ;"); - realTest("(RN,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RN,F3.0,';')", u.d, " 2.;"); - realTest("(RN,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RN,G7.1,';')", u.d, " 2. ;"); - realTest("(RD,E8.1,';')", u.d, " 0.1E+01;"); - realTest("(RD,F3.0,';')", u.d, " 1.;"); - realTest("(RD,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RD,G7.1,';')", u.d, " 1. ;"); - realTest("(RU,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RU,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RU,G7.1,';')", u.d, " 2. ;"); - realTest("(RZ,E8.1,';')", u.d, " 0.1E+01;"); - realTest("(RZ,F3.0,';')", u.d, " 1.;"); - realTest("(RZ,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RZ,G7.1,';')", u.d, " 1. ;"); - realTest("(RC,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RC,F3.0,';')", u.d, " 2.;"); - realTest("(RC,G7.0,';')", u.d, " 0.E+01;"); - realTest("(RC,G7.1,';')", u.d, " 2. ;"); - - // TODO continue F and G editing tests on these data - - u.n = 0xbff8000000000000; // -1.5 - realTest("(E9.2,';')", u.d, "-0.15E+01;"); - realTest("(RN,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RD,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RU,E8.1,';')", u.d, "-0.1E+01;"); - realTest("(RZ,E8.1,';')", u.d, "-0.1E+01;"); - realTest("(RC,E8.1,';')", u.d, "-0.2E+01;"); - - u.n = 0x4004000000000000; // 2.5 - realTest("(E9.2,';')", u.d, " 0.25E+01;"); - realTest("(RN,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RD,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RU,E8.1,';')", u.d, " 0.3E+01;"); - realTest("(RZ,E8.1,';')", u.d, " 0.2E+01;"); - realTest("(RC,E8.1,';')", u.d, " 0.3E+01;"); - - u.n = 0xc004000000000000; // -2.5 - realTest("(E9.2,';')", u.d, "-0.25E+01;"); - realTest("(RN,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RD,E8.1,';')", u.d, "-0.3E+01;"); - realTest("(RU,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RZ,E8.1,';')", u.d, "-0.2E+01;"); - realTest("(RC,E8.1,';')", u.d, "-0.3E+01;"); - - u.n = 1; // least positive nonzero subnormal - realTest("(E32.17,';')", u.d, " 0.49406564584124654-323;"); - realTest("(ES32.17,';')", u.d, " 4.94065645841246544-324;"); - realTest("(EN32.17,';')", u.d, " 4.94065645841246544-324;"); - realTest("(E759.752,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "75386825064197182655334472656250-323;"); - realTest("(G0,';')", u.d, ".5-323;"); - realTest("(E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726562-323;"); - realTest("(RN,E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726562-323;"); - realTest("(RD,E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726562-323;"); - realTest("(RU,E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726563-323;"); - realTest("(RC,E757.750,';')", u.d, - " 0." - "494065645841246544176568792868221372365059802614324764425585682500675507" - "270208751865299836361635992379796564695445717730926656710355939796398774" - "796010781878126300713190311404527845817167848982103688718636056998730723" - "050006387409153564984387312473397273169615140031715385398074126238565591" - "171026658556686768187039560310624931945271591492455329305456544401127480" - "129709999541931989409080416563324524757147869014726780159355238611550134" - "803526493472019379026810710749170333222684475333572083243193609238289345" - "836806010601150616980975307834227731832924790498252473077637592724787465" - "608477820373446969953364701797267771758512566055119913150489110145103786" - "273816725095583738973359899366480994116420570263709027924276754456522908" - "753868250641971826553344726563-323;"); - - u.n = 0x10000000000000; // least positive nonzero normal - realTest("(E723.716,';')", u.d, - " 0." - "222507385850720138309023271733240406421921598046233183055332741688720443" - "481391819585428315901251102056406733973103581100515243416155346010885601" - "238537771882113077799353200233047961014744258363607192156504694250373420" - "837525080665061665815894872049117996859163964850063590877011830487479978" - "088775374994945158045160505091539985658247081864511353793580499211598108" - "576605199243335211435239014879569960959128889160299264151106346631339366" - "347758651302937176204732563178148566435087212282863764204484681140761391" - "147706280168985324411002416144742161856716615054015428508471675290190316" - "132277889672970737312333408698898317506783884692609277397797285865965494" - "10913690954061364675687023986783152906809846172109246253967285156250-" - "307;"); - realTest("(G0,';')", u.d, ".22250738585072014-307;"); - - u.n = 0x7fefffffffffffffuLL; // greatest finite - realTest("(E32.17,';')", u.d, " 0.17976931348623157+309;"); - realTest("(E317.310,';')", u.d, - " 0." - "179769313486231570814527423731704356798070567525844996598917476803157260" - "780028538760589558632766878171540458953514382464234321326889464182768467" - "546703537516986049910576551282076245490090389328944075868508455133942304" - "583236903222948165808559332123348274797826204144723168738177180919299881" - "2504040261841248583680+309;"); - realTest("(ES317.310,';')", u.d, - " 1." - "797693134862315708145274237317043567980705675258449965989174768031572607" - "800285387605895586327668781715404589535143824642343213268894641827684675" - "467035375169860499105765512820762454900903893289440758685084551339423045" - "832369032229481658085593321233482747978262041447231687381771809192998812" - "5040402618412485836800+308;"); - realTest("(EN319.310,';')", u.d, - " 179." - "769313486231570814527423731704356798070567525844996598917476803157260780" - "028538760589558632766878171540458953514382464234321326889464182768467546" - "703537516986049910576551282076245490090389328944075868508455133942304583" - "236903222948165808559332123348274797826204144723168738177180919299881250" - "4040261841248583680000+306;"); - realTest("(G0,';')", u.d, ".17976931348623157+309;"); - - realTest("(F5.3,';')", 25., "*****;"); - realTest("(F5.3,';')", 2.5, "2.500;"); - realTest("(F5.3,';')", 0.25, "0.250;"); - realTest("(F5.3,';')", 0.025, "0.025;"); - realTest("(F5.3,';')", 0.0025, "0.003;"); - realTest("(F5.3,';')", 0.00025, "0.000;"); - realTest("(F5.3,';')", 0.000025, "0.000;"); - realTest("(F5.3,';')", -25., "*****;"); - realTest("(F5.3,';')", -2.5, "*****;"); - realTest("(F5.3,';')", -0.25, "-.250;"); - realTest("(F5.3,';')", -0.025, "-.025;"); - realTest("(F5.3,';')", -0.0025, "-.003;"); - realTest("(F5.3,';')", -0.00025, "-.000;"); - realTest("(F5.3,';')", -0.000025, "-.000;"); - - realInTest("(F18.0)", " 0", 0x0); - realInTest("(F18.0)", " ", 0x0); - realInTest("(F18.0)", " -0", 0x8000000000000000); - realInTest("(F18.0)", " 01", 0x3ff0000000000000); - realInTest("(F18.0)", " 1", 0x3ff0000000000000); - realInTest("(F18.0)", " 125.", 0x405f400000000000); - realInTest("(F18.0)", " 12.5", 0x4029000000000000); - realInTest("(F18.0)", " 1.25", 0x3ff4000000000000); - realInTest("(F18.0)", " 01.25", 0x3ff4000000000000); - realInTest("(F18.0)", " .125", 0x3fc0000000000000); - realInTest("(F18.0)", " 0.125", 0x3fc0000000000000); - realInTest("(F18.0)", " .0625", 0x3fb0000000000000); - realInTest("(F18.0)", " 0.0625", 0x3fb0000000000000); - realInTest("(F18.0)", " 125", 0x405f400000000000); - realInTest("(F18.1)", " 125", 0x4029000000000000); - realInTest("(F18.2)", " 125", 0x3ff4000000000000); - realInTest("(F18.3)", " 125", 0x3fc0000000000000); - realInTest("(-1P,F18.0)", " 125", 0x4093880000000000); // 1250 - realInTest("(1P,F18.0)", " 125", 0x4029000000000000); // 12.5 - realInTest("(BZ,F18.0)", " 125 ", 0x4093880000000000); // 1250 - realInTest("(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000); // 1.25e13 - realInTest("(DC,F18.0)", " 12,5", 0x4029000000000000); - - listInputTest(); - descrOutputTest(); - - return EndTests(); -} diff --git a/flang/unittests/RuntimeGTest/CMakeLists.txt b/flang/unittests/RuntimeGTest/CMakeLists.txt index f26cb44be5fe..80314c124831 100644 --- a/flang/unittests/RuntimeGTest/CMakeLists.txt +++ b/flang/unittests/RuntimeGTest/CMakeLists.txt @@ -1,7 +1,12 @@ add_flang_unittest(FlangRuntimeTests CharacterTest.cpp - RuntimeCrashTest.cpp CrashHandlerFixture.cpp + MiscIntrinsic.cpp + Numeric.cpp + NumericalFormatTest.cpp + Random.cpp + Reduction.cpp + RuntimeCrashTest.cpp ) target_link_libraries(FlangRuntimeTests diff --git a/flang/unittests/RuntimeGTest/MiscIntrinsic.cpp b/flang/unittests/RuntimeGTest/MiscIntrinsic.cpp new file mode 100644 index 000000000000..62213d01021e --- /dev/null +++ b/flang/unittests/RuntimeGTest/MiscIntrinsic.cpp @@ -0,0 +1,70 @@ +//===-- flang/unittests/RuntimeGTest/MiscIntrinsic.cpp ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "tools.h" +#include "../../runtime/allocatable.h" +#include "../../runtime/cpp-type.h" +#include "../../runtime/descriptor.h" +#include "../../runtime/misc-intrinsic.h" + +using namespace Fortran::runtime; + +// TRANSFER examples from Fortran 2018 + +TEST(MiscIntrinsic, TransferScalar) { + StaticDescriptor<2, true, 2> staticDesc[2]; + auto &result{staticDesc[0].descriptor()}; + auto source{MakeArray<TypeCategory::Integer, 4>( + std::vector<int>{}, std::vector<std::int32_t>{1082130432})}; + auto &mold{staticDesc[1].descriptor()}; + mold.Establish(TypeCategory::Real, 4, nullptr, 0); + RTNAME(Transfer)(result, *source, mold, __FILE__, __LINE__); + EXPECT_EQ(result.rank(), 0); + EXPECT_EQ(result.type().raw(), (TypeCode{TypeCategory::Real, 4}.raw())); + EXPECT_EQ(*result.OffsetElement<float>(), 4.0); + result.Destroy(); +} + +TEST(MiscIntrinsic, TransferMold) { + StaticDescriptor<2, true, 2> staticDesc[2]; + auto &result{staticDesc[0].descriptor()}; + auto source{MakeArray<TypeCategory::Real, 4>( + std::vector<int>{3}, std::vector<float>{1.1F, 2.2F, 3.3F})}; + auto &mold{staticDesc[1].descriptor()}; + SubscriptValue extent[1]{1}; + mold.Establish(TypeCategory::Complex, 4, nullptr, 1, extent); + RTNAME(Transfer)(result, *source, mold, __FILE__, __LINE__); + EXPECT_EQ(result.rank(), 1); + EXPECT_EQ(result.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(result.GetDimension(0).Extent(), 2); + EXPECT_EQ(result.type().raw(), (TypeCode{TypeCategory::Complex, 4}.raw())); + EXPECT_EQ(result.OffsetElement<float>()[0], 1.1F); + EXPECT_EQ(result.OffsetElement<float>()[1], 2.2F); + EXPECT_EQ(result.OffsetElement<float>()[2], 3.3F); + EXPECT_EQ(result.OffsetElement<float>()[3], 0.0F); + result.Destroy(); +} + +TEST(MiscIntrinsic, TransferSize) { + StaticDescriptor<2, true, 2> staticDesc[2]; + auto &result{staticDesc[0].descriptor()}; + auto source{MakeArray<TypeCategory::Real, 4>( + std::vector<int>{3}, std::vector<float>{1.1F, 2.2F, 3.3F})}; + auto &mold{staticDesc[1].descriptor()}; + SubscriptValue extent[1]{1}; + mold.Establish(TypeCategory::Complex, 4, nullptr, 1, extent); + RTNAME(TransferSize)(result, *source, mold, __FILE__, __LINE__, 1); + EXPECT_EQ(result.rank(), 1); + EXPECT_EQ(result.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(result.GetDimension(0).Extent(), 1); + EXPECT_EQ(result.type().raw(), (TypeCode{TypeCategory::Complex, 4}.raw())); + EXPECT_EQ(result.OffsetElement<float>()[0], 1.1F); + EXPECT_EQ(result.OffsetElement<float>()[1], 2.2F); + result.Destroy(); +} diff --git a/flang/unittests/RuntimeGTest/Numeric.cpp b/flang/unittests/RuntimeGTest/Numeric.cpp new file mode 100644 index 000000000000..b930b0a708de --- /dev/null +++ b/flang/unittests/RuntimeGTest/Numeric.cpp @@ -0,0 +1,156 @@ +//===-- flang/unittests/RuntimeGTest/Numeric.cpp ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "../../runtime/numeric.h" +#include "gtest/gtest.h" +#include <cmath> +#include <limits> + +using namespace Fortran::runtime; +using Fortran::common::TypeCategory; +template <int KIND> using Int = CppTypeFor<TypeCategory::Integer, KIND>; +template <int KIND> using Real = CppTypeFor<TypeCategory::Real, KIND>; + +// Simple tests of numeric intrinsic functions using examples from Fortran 2018 + +TEST(Numeric, Aint) { + EXPECT_EQ(RTNAME(Aint4_4)(Real<4>{3.7}), 3.0); + EXPECT_EQ(RTNAME(Aint8_4)(Real<8>{-3.7}), -3.0); + EXPECT_EQ(RTNAME(Aint8_8)(Real<8>{0}), 0.0); + EXPECT_EQ(RTNAME(Aint4_4)(std::numeric_limits<Real<4>>::infinity()), + std::numeric_limits<Real<4>>::infinity()); + EXPECT_TRUE( + std::isnan(RTNAME(Aint8_8)(std::numeric_limits<Real<8>>::quiet_NaN()))); +} + +TEST(Numeric, Anint) { + EXPECT_EQ(RTNAME(Anint4_4)(Real<4>{2.783}), 3.0); + EXPECT_EQ(RTNAME(Anint8_4)(Real<8>{-2.783}), -3.0); + EXPECT_EQ(RTNAME(Anint4_4)(Real<4>{2.5}), 3.0); + EXPECT_EQ(RTNAME(Anint8_4)(Real<8>{-2.5}), -3.0); + EXPECT_EQ(RTNAME(Anint8_8)(Real<8>{0}), 0.0); + EXPECT_EQ(RTNAME(Anint4_4)(std::numeric_limits<Real<4>>::infinity()), + std::numeric_limits<Real<4>>::infinity()); + EXPECT_TRUE( + std::isnan(RTNAME(Aint8_8)(std::numeric_limits<Real<8>>::quiet_NaN()))); +} + +TEST(Numeric, Ceiling) { + EXPECT_EQ(RTNAME(Ceiling4_4)(Real<4>{3.7}), 4); + EXPECT_EQ(RTNAME(Ceiling8_8)(Real<8>{-3.7}), -3); + EXPECT_EQ(RTNAME(Ceiling4_1)(Real<4>{0}), 0); +} + +TEST(Numeric, Floor) { + EXPECT_EQ(RTNAME(Floor4_4)(Real<4>{3.7}), 3); + EXPECT_EQ(RTNAME(Floor8_8)(Real<8>{-3.7}), -4); + EXPECT_EQ(RTNAME(Floor4_1)(Real<4>{0}), 0); +} + +TEST(Numeric, Exponent) { + EXPECT_EQ(RTNAME(Exponent4_4)(Real<4>{0}), 0); + EXPECT_EQ(RTNAME(Exponent4_8)(Real<4>{1.0}), 1); + EXPECT_EQ(RTNAME(Exponent8_4)(Real<8>{4.1}), 3); + EXPECT_EQ(RTNAME(Exponent8_8)(std::numeric_limits<Real<8>>::infinity()), + std::numeric_limits<Int<8>>::max()); + EXPECT_EQ(RTNAME(Exponent8_8)(std::numeric_limits<Real<8>>::quiet_NaN()), + std::numeric_limits<Int<8>>::max()); +} + +TEST(Numeric, Fraction) { + EXPECT_EQ(RTNAME(Fraction4)(Real<4>{0}), 0); + EXPECT_EQ(RTNAME(Fraction8)(Real<8>{3.0}), 0.75); + EXPECT_TRUE( + std::isnan(RTNAME(Fraction4)(std::numeric_limits<Real<4>>::infinity()))); + EXPECT_TRUE( + std::isnan(RTNAME(Fraction8)(std::numeric_limits<Real<8>>::quiet_NaN()))); +} + +TEST(Numeric, Mod) { + EXPECT_EQ(RTNAME(ModInteger1)(Int<1>{8}, Int<1>(5)), 3); + EXPECT_EQ(RTNAME(ModInteger4)(Int<4>{-8}, Int<4>(5)), -3); + EXPECT_EQ(RTNAME(ModInteger2)(Int<2>{8}, Int<2>(-5)), 3); + EXPECT_EQ(RTNAME(ModInteger8)(Int<8>{-8}, Int<8>(-5)), -3); + EXPECT_EQ(RTNAME(ModReal4)(Real<4>{8.0}, Real<4>(5.0)), 3.0); + EXPECT_EQ(RTNAME(ModReal4)(Real<4>{-8.0}, Real<4>(5.0)), -3.0); + EXPECT_EQ(RTNAME(ModReal8)(Real<8>{8.0}, Real<8>(-5.0)), 3.0); + EXPECT_EQ(RTNAME(ModReal8)(Real<8>{-8.0}, Real<8>(-5.0)), -3.0); +} + +TEST(Numeric, Modulo) { + EXPECT_EQ(RTNAME(ModuloInteger1)(Int<1>{8}, Int<1>(5)), 3); + EXPECT_EQ(RTNAME(ModuloInteger4)(Int<4>{-8}, Int<4>(5)), 2); + EXPECT_EQ(RTNAME(ModuloInteger2)(Int<2>{8}, Int<2>(-5)), -2); + EXPECT_EQ(RTNAME(ModuloInteger8)(Int<8>{-8}, Int<8>(-5)), -3); + EXPECT_EQ(RTNAME(ModuloReal4)(Real<4>{8.0}, Real<4>(5.0)), 3.0); + EXPECT_EQ(RTNAME(ModuloReal4)(Real<4>{-8.0}, Real<4>(5.0)), 2.0); + EXPECT_EQ(RTNAME(ModuloReal8)(Real<8>{8.0}, Real<8>(-5.0)), -2.0); + EXPECT_EQ(RTNAME(ModuloReal8)(Real<8>{-8.0}, Real<8>(-5.0)), -3.0); +} + +TEST(Numeric, Nearest) { + EXPECT_EQ(RTNAME(Nearest4)(Real<4>{0}, true), + std::numeric_limits<Real<4>>::denorm_min()); + EXPECT_EQ(RTNAME(Nearest4)(Real<4>{3.0}, true), + Real<4>{3.0} + std::ldexp(Real<4>{1.0}, -22)); + EXPECT_EQ(RTNAME(Nearest8)(Real<8>{1.0}, true), + Real<8>{1.0} + std::ldexp(Real<8>{1.0}, -52)); + EXPECT_EQ(RTNAME(Nearest8)(Real<8>{1.0}, false), + Real<8>{1.0} - std::ldexp(Real<8>{1.0}, -52)); +} + +TEST(Numeric, Nint) { + EXPECT_EQ(RTNAME(Nint4_4)(Real<4>{2.783}), 3); + EXPECT_EQ(RTNAME(Nint8_4)(Real<8>{-2.783}), -3); + EXPECT_EQ(RTNAME(Nint4_4)(Real<4>{2.5}), 3); + EXPECT_EQ(RTNAME(Nint8_4)(Real<8>{-2.5}), -3); + EXPECT_EQ(RTNAME(Nint8_8)(Real<8>{0}), 0); +} + +TEST(Numeric, RRSpacing) { + EXPECT_EQ(RTNAME(RRSpacing8)(Real<8>{0}), 0); + EXPECT_EQ(RTNAME(RRSpacing4)(Real<4>{-3.0}), 0.75 * (1 << 24)); + EXPECT_EQ(RTNAME(RRSpacing8)(Real<8>{-3.0}), 0.75 * (std::int64_t{1} << 53)); + EXPECT_TRUE( + std::isnan(RTNAME(RRSpacing4)(std::numeric_limits<Real<4>>::infinity()))); + EXPECT_TRUE(std::isnan( + RTNAME(RRSpacing8)(std::numeric_limits<Real<8>>::quiet_NaN()))); +} + +TEST(Numeric, Scale) { + EXPECT_EQ(RTNAME(Scale4)(Real<4>{0}, 0), 0); + EXPECT_EQ(RTNAME(Scale4)(Real<4>{1.0}, 0), 1.0); + EXPECT_EQ(RTNAME(Scale4)(Real<4>{1.0}, 1), 2.0); + EXPECT_EQ(RTNAME(Scale4)(Real<4>{1.0}, -1), 0.5); + EXPECT_TRUE( + std::isinf(RTNAME(Scale4)(std::numeric_limits<Real<4>>::infinity(), 1))); + EXPECT_TRUE( + std::isnan(RTNAME(Scale8)(std::numeric_limits<Real<8>>::quiet_NaN(), 1))); +} + +TEST(Numeric, SetExponent) { + EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{0}, 0), 0); + EXPECT_EQ(RTNAME(SetExponent8)(Real<8>{0}, 666), 0); + EXPECT_EQ(RTNAME(SetExponent8)(Real<8>{3.0}, 0), 1.5); + EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{1.0}, 0), 1.0); + EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{1.0}, 1), 2.0); + EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{1.0}, -1), 0.5); + EXPECT_TRUE(std::isnan( + RTNAME(SetExponent4)(std::numeric_limits<Real<4>>::infinity(), 1))); + EXPECT_TRUE(std::isnan( + RTNAME(SetExponent8)(std::numeric_limits<Real<8>>::quiet_NaN(), 1))); +} + +TEST(Numeric, Spacing) { + EXPECT_EQ(RTNAME(Spacing8)(Real<8>{0}), std::numeric_limits<Real<8>>::min()); + EXPECT_EQ(RTNAME(Spacing4)(Real<4>{3.0}), std::ldexp(Real<4>{1.0}, -22)); + EXPECT_TRUE( + std::isnan(RTNAME(Spacing4)(std::numeric_limits<Real<4>>::infinity()))); + EXPECT_TRUE( + std::isnan(RTNAME(Spacing8)(std::numeric_limits<Real<8>>::quiet_NaN()))); +} diff --git a/flang/unittests/RuntimeGTest/NumericalFormatTest.cpp b/flang/unittests/RuntimeGTest/NumericalFormatTest.cpp new file mode 100644 index 000000000000..7788c436cdab --- /dev/null +++ b/flang/unittests/RuntimeGTest/NumericalFormatTest.cpp @@ -0,0 +1,694 @@ +//===-- flang/unittests/RuntimeGTest/NumericalFormatTest.cpp ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CrashHandlerFixture.h" +#include "../../runtime/descriptor.h" +#include "../../runtime/io-api.h" +#include <algorithm> +#include <array> +#include <cstring> +#include <gtest/gtest.h> +#include <tuple> + +using namespace Fortran::runtime; +using namespace Fortran::runtime::io; + +static bool CompareFormattedStrings( + const std::string &expect, const std::string &&got) { + std::string want{expect}; + want.resize(got.size(), ' '); + return want == got; +} + +static bool CompareFormattedStrings( + const char *expect, const std::string &&got) { + return CompareFormattedStrings(std::string(expect), std::move(got)); +} + +// Perform format and compare the result with expected value +static bool CompareFormatReal( + const char *format, double x, const char *expect) { + char buffer[800]; + auto *cookie{IONAME(BeginInternalFormattedOutput)( + buffer, sizeof buffer, format, std::strlen(format))}; + IONAME(OutputReal64)(cookie, x); + auto status{IONAME(EndIoStatement)(cookie)}; + + EXPECT_EQ(status, 0); + return CompareFormattedStrings(expect, std::string{buffer, sizeof buffer}); +} + +// Convert raw uint64 into double, perform format, and compare with expected +static bool CompareFormatReal( + const char *format, std::uint64_t xInt, const char *expect) { + double x; + static_assert(sizeof(double) == sizeof(std::uint64_t), + "Size of double != size of uint64_t!"); + std::memcpy(&x, &xInt, sizeof xInt); + return CompareFormatReal(format, x, expect); +} + +struct IOApiTests : CrashHandlerFixture {}; + +TEST(IOApiTests, HelloWorldOutputTest) { + static constexpr int bufferSize{32}; + char buffer[bufferSize]; + + // Create format for all types and values to be written + const char *format{"(6HHELLO,,A6,2X,I3,1X,'0x',Z8,1X,L1)"}; + auto *cookie{IONAME(BeginInternalFormattedOutput)( + buffer, bufferSize, format, std::strlen(format))}; + + // Write string, integer, and logical values to buffer + IONAME(OutputAscii)(cookie, "WORLD", 5); + IONAME(OutputInteger64)(cookie, 678); + IONAME(OutputInteger64)(cookie, 0xfeedface); + IONAME(OutputLogical)(cookie, true); + + // Ensure IO succeeded + auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(status, 0) << "hello: '" << format << "' failed, status " + << static_cast<int>(status); + + // Ensure final buffer matches expected string output + static const std::string expect{"HELLO, WORLD 678 0xFEEDFACE T"}; + ASSERT_TRUE( + CompareFormattedStrings(expect, std::string{buffer, sizeof buffer})) + << "Expected '" << expect << "', got " << buffer; +} + +TEST(IOApiTests, MultilineOutputTest) { + // Allocate buffer for multiline output + static constexpr int numLines{5}; + static constexpr int lineLength{32}; + static char buffer[numLines][lineLength]; + + // Create descriptor for entire buffer + static constexpr int staticDescriptorMaxRank{1}; + static StaticDescriptor<staticDescriptorMaxRank> wholeStaticDescriptor; + static Descriptor &whole{wholeStaticDescriptor.descriptor()}; + static SubscriptValue extent[]{numLines}; + whole.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/lineLength, &buffer, + staticDescriptorMaxRank, extent, CFI_attribute_pointer); + whole.Dump(stderr); + whole.Check(); + + // Create descriptor for buffer section + static StaticDescriptor<staticDescriptorMaxRank> sectionStaticDescriptor; + static Descriptor §ion{sectionStaticDescriptor.descriptor()}; + static const SubscriptValue lowers[]{0}, uppers[]{4}, strides[]{1}; + section.Establish(whole.type(), /*elementBytes=*/whole.ElementBytes(), + nullptr, /*maxRank=*/staticDescriptorMaxRank, extent, + CFI_attribute_pointer); + + // Ensure C descriptor address `section.raw()` is updated without error + const auto error{ + CFI_section(§ion.raw(), &whole.raw(), lowers, uppers, strides)}; + ASSERT_EQ(error, 0) << "multiline: CFI_section failed: " << error; + section.Dump(stderr); + section.Check(); + + // Create format string and initialize IO operation + const char *format{ + "('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,17X,'abcd',1(2I4))"}; + static auto *cookie{IONAME(BeginInternalArrayFormattedOutput)( + section, format, std::strlen(format))}; + + // Write data to buffer + IONAME(OutputAscii)(cookie, "WORLD", 5); + IONAME(OutputAscii)(cookie, "HELLO", 5); + IONAME(OutputInteger64)(cookie, 789); + for (int j{666}; j <= 999; j += 111) { + IONAME(OutputInteger64)(cookie, j); + } + + // Ensure no errors occured in write operations above + const auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(status, 0) << "multiline: '" << format << "' failed, status " + << static_cast<int>(status); + + static const std::string expect{">HELLO, WORLD <" + " " + "789 abcd 666 777" + " 888 999 " + " "}; + // Ensure formatted string matches expected output + ASSERT_TRUE( + CompareFormattedStrings(expect, std::string{buffer[0], sizeof buffer})) + << "Expected " << expect << " but got " << buffer; +} + +TEST(IOApiTests, ListInputTest) { + static const char input[]{",1*,(5.,6..)"}; + static auto *cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)}; + + // Create real values for IO tests + static constexpr int numRealValues{6}; + static float z[numRealValues]; + for (int j{0}; j < numRealValues; ++j) { + z[j] = -(j + 1); + } + + // Ensure reading complex values to floats does not result in an error + for (int j{0}; j < numRealValues; j += 2) { + ASSERT_TRUE(IONAME(InputComplex32)(cookie, &z[j])) + << "InputComplex32 failed with value " << z[j]; + } + + // Ensure no IO errors occured during IO operations above + static auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(status, 0) << "Failed complex list-directed input, status " + << static_cast<int>(status); + + // Ensure writing complex values from floats does not result in an error + static constexpr int bufferSize{33}; + static char output[bufferSize]; + output[bufferSize - 1] = '\0'; + cookie = IONAME(BeginInternalListOutput)(output, bufferSize - 1); + for (int j{0}; j < numRealValues; j += 2) { + ASSERT_TRUE(IONAME(OutputComplex32)(cookie, z[j], z[j + 1])) + << "OutputComplex32 failed when outputting value " << z[j] << ", " + << z[j + 1]; + } + + // Ensure no IO errors occured during IO operations above + status = IONAME(EndIoStatement)(cookie); + ASSERT_EQ(status, 0) << "Failed complex list-directed output, status " + << static_cast<int>(status); + + // Verify output buffer against expected value + static const char expect[bufferSize]{" (-1.,-2.) (-3.,-4.) (5.,6.) "}; + ASSERT_EQ(std::strncmp(output, expect, bufferSize), 0) + << "Failed complex list-directed output, expected '" << expect + << "', but got '" << output << "'"; +} + +TEST(IOApiTests, DescriptorOutputTest) { + static constexpr int bufferSize{9}; + static char buffer[bufferSize]; + static const char *format{"(2A4)"}; + static auto *cookie{IONAME(BeginInternalFormattedOutput)( + buffer, bufferSize, format, std::strlen(format))}; + + // Create descriptor for output + static constexpr int staticDescriptorMaxRank{1}; + static StaticDescriptor<staticDescriptorMaxRank> staticDescriptor; + static Descriptor &desc{staticDescriptor.descriptor()}; + static constexpr int subscriptExtent{2}; + static const SubscriptValue extent[]{subscriptExtent}; + + // Manually write to descriptor buffer + static constexpr int dataLength{4}; + static char data[subscriptExtent][dataLength]; + std::memcpy(data[0], "ABCD", dataLength); + std::memcpy(data[1], "EFGH", dataLength); + desc.Establish(TypeCode{CFI_type_char}, dataLength, &data, + staticDescriptorMaxRank, extent); + desc.Dump(stderr); + desc.Check(); + IONAME(OutputDescriptor)(cookie, desc); + + // Ensure no errors were encountered in initializing the cookie and descriptor + static auto formatStatus{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(formatStatus, 0) + << "descrOutputTest: '" << format << "' failed, status " + << static_cast<int>(formatStatus); + + // Ensure buffer matches expected output + ASSERT_TRUE( + CompareFormattedStrings("ABCDEFGH ", std::string{buffer, sizeof buffer})); + + // Begin list-directed output on cookie by descriptor + cookie = IONAME(BeginInternalListOutput)(buffer, sizeof buffer); + IONAME(OutputDescriptor)(cookie, desc); + + // Ensure list-directed output does not result in an IO error + static auto listDirectedStatus{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(listDirectedStatus, 0) + << "descrOutputTest: list-directed failed, status " + << static_cast<int>(listDirectedStatus); + + // Ensure buffer matches expected output + ASSERT_TRUE( + CompareFormattedStrings(" ABCDEFGH", std::string{buffer, sizeof buffer})); +} + +//------------------------------------------------------------------------------ +/// Tests for output formatting real values +//------------------------------------------------------------------------------ + +TEST(IOApiTests, FormatZeroes) { + static constexpr std::pair<const char *, const char *> zeroes[]{ + {"(E32.17,';')", " 0.00000000000000000E+00;"}, + {"(F32.17,';')", " 0.00000000000000000;"}, + {"(G32.17,';')", " 0.0000000000000000 ;"}, + {"(DC,E32.17,';')", " 0,00000000000000000E+00;"}, + {"(DC,F32.17,';')", " 0,00000000000000000;"}, + {"(DC,G32.17,';')", " 0,0000000000000000 ;"}, + {"(D32.17,';')", " 0.00000000000000000D+00;"}, + {"(E32.17E1,';')", " 0.00000000000000000E+0;"}, + {"(G32.17E1,';')", " 0.0000000000000000 ;"}, + {"(E32.17E0,';')", " 0.00000000000000000E+0;"}, + {"(G32.17E0,';')", " 0.0000000000000000 ;"}, + {"(1P,E32.17,';')", " 0.00000000000000000E+00;"}, + {"(1PE32.17,';')", " 0.00000000000000000E+00;"}, // no comma + {"(1P,F32.17,';')", " 0.00000000000000000;"}, + {"(1P,G32.17,';')", " 0.0000000000000000 ;"}, + {"(2P,E32.17,';')", " 00.0000000000000000E+00;"}, + {"(-1P,E32.17,';')", " 0.00000000000000000E+00;"}, + {"(G0,';')", "0.;"}, + }; + + for (auto const &[format, expect] : zeroes) { + ASSERT_TRUE(CompareFormatReal(format, 0.0, expect)) + << "Failed to format " << format << ", expected " << expect; + } +} + +TEST(IOApiTests, FormatOnes) { + static constexpr std::pair<const char *, const char *> ones[]{ + {"(E32.17,';')", " 0.10000000000000000E+01;"}, + {"(F32.17,';')", " 1.00000000000000000;"}, + {"(G32.17,';')", " 1.0000000000000000 ;"}, + {"(E32.17E1,';')", " 0.10000000000000000E+1;"}, + {"(G32.17E1,';')", " 1.0000000000000000 ;"}, + {"(E32.17E0,';')", " 0.10000000000000000E+1;"}, + {"(G32.17E0,';')", " 1.0000000000000000 ;"}, + {"(E32.17E4,';')", " 0.10000000000000000E+0001;"}, + {"(G32.17E4,';')", " 1.0000000000000000 ;"}, + {"(1P,E32.17,';')", " 1.00000000000000000E+00;"}, + {"(1PE32.17,';')", " 1.00000000000000000E+00;"}, // no comma + {"(1P,F32.17,';')", " 10.00000000000000000;"}, + {"(1P,G32.17,';')", " 1.0000000000000000 ;"}, + {"(ES32.17,';')", " 1.00000000000000000E+00;"}, + {"(2P,E32.17,';')", " 10.0000000000000000E-01;"}, + {"(2P,G32.17,';')", " 1.0000000000000000 ;"}, + {"(-1P,E32.17,';')", " 0.01000000000000000E+02;"}, + {"(-1P,G32.17,';')", " 1.0000000000000000 ;"}, + {"(G0,';')", "1.;"}, + }; + + for (auto const &[format, expect] : ones) { + ASSERT_TRUE(CompareFormatReal(format, 1.0, expect)) + << "Failed to format " << format << ", expected " << expect; + } +} + +TEST(IOApiTests, FormatNegativeOnes) { + static constexpr std::tuple<const char *, const char *> negOnes[]{ + {"(E32.17,';')", " -0.10000000000000000E+01;"}, + {"(F32.17,';')", " -1.00000000000000000;"}, + {"(G32.17,';')", " -1.0000000000000000 ;"}, + {"(G0,';')", "-1.;"}, + }; + for (auto const &[format, expect] : negOnes) { + ASSERT_TRUE(CompareFormatReal(format, -1.0, expect)) + << "Failed to format " << format << ", expected " << expect; + } +} + +// Each test case contains a raw uint64, a format string for a real value, and +// the expected resulting string from formatting the raw uint64. The double +// representation of the uint64 is commented above each test case. +TEST(IOApiTests, FormatDoubleValues) { + + using TestCaseTy = std::tuple<std::uint64_t, + std::vector<std::tuple<const char *, const char *>>>; + static const std::vector<TestCaseTy> testCases{ + {// -0 + 0x8000000000000000, + { + {"(E9.1,';')", " -0.0E+00;"}, + {"(F4.0,';')", " -0.;"}, + {"(G8.0,';')", "-0.0E+00;"}, + {"(G8.1,';')", " -0. ;"}, + {"(G0,';')", "-0.;"}, + {"(E9.1,';')", " -0.0E+00;"}, + }}, + {// +Inf + 0x7ff0000000000000, + { + {"(E9.1,';')", " Inf;"}, + {"(F9.1,';')", " Inf;"}, + {"(G9.1,';')", " Inf;"}, + {"(SP,E9.1,';')", " +Inf;"}, + {"(SP,F9.1,';')", " +Inf;"}, + {"(SP,G9.1,';')", " +Inf;"}, + {"(G0,';')", "Inf;"}, + }}, + {// -Inf + 0xfff0000000000000, + { + {"(E9.1,';')", " -Inf;"}, + {"(F9.1,';')", " -Inf;"}, + {"(G9.1,';')", " -Inf;"}, + {"(G0,';')", "-Inf;"}, + }}, + {// NaN + 0x7ff0000000000001, + { + {"(E9.1,';')", " NaN;"}, + {"(F9.1,';')", " NaN;"}, + {"(G9.1,';')", " NaN;"}, + {"(G0,';')", "NaN;"}, + }}, + {// NaN (sign irrelevant) + 0xfff0000000000001, + { + {"(E9.1,';')", " NaN;"}, + {"(F9.1,';')", " NaN;"}, + {"(G9.1,';')", " NaN;"}, + {"(SP,E9.1,';')", " NaN;"}, + {"(SP,F9.1,';')", " NaN;"}, + {"(SP,G9.1,';')", " NaN;"}, + {"(G0,';')", "NaN;"}, + }}, + {// 0.1 rounded + 0x3fb999999999999a, + { + {"(E62.55,';')", + " 0.1000000000000000055511151231257827021181583404541015625E+" + "00;"}, + {"(E0.0,';')", "0.E+00;"}, + {"(E0.55,';')", + "0.1000000000000000055511151231257827021181583404541015625E+" + "00;"}, + {"(E0,';')", ".1E+00;"}, + {"(F58.55,';')", + " 0." + "1000000000000000055511151231257827021181583404541015625;"}, + {"(F0.0,';')", "0.;"}, + {"(F0.55,';')", + ".1000000000000000055511151231257827021181583404541015625;"}, + {"(F0,';')", ".1;"}, + {"(G62.55,';')", + " 0.1000000000000000055511151231257827021181583404541015625 " + " ;"}, + {"(G0.0,';')", "0.;"}, + {"(G0.55,';')", + ".1000000000000000055511151231257827021181583404541015625;"}, + {"(G0,';')", ".1;"}, + }}, + {// 1.5 + 0x3ff8000000000000, + { + {"(E9.2,';')", " 0.15E+01;"}, + {"(F4.1,';')", " 1.5;"}, + {"(G7.1,';')", " 2. ;"}, + {"(RN,E8.1,';')", " 0.2E+01;"}, + {"(RN,F3.0,';')", " 2.;"}, + {"(RN,G7.0,';')", " 0.E+01;"}, + {"(RN,G7.1,';')", " 2. ;"}, + {"(RD,E8.1,';')", " 0.1E+01;"}, + {"(RD,F3.0,';')", " 1.;"}, + {"(RD,G7.0,';')", " 0.E+01;"}, + {"(RD,G7.1,';')", " 1. ;"}, + {"(RU,E8.1,';')", " 0.2E+01;"}, + {"(RU,G7.0,';')", " 0.E+01;"}, + {"(RU,G7.1,';')", " 2. ;"}, + {"(RZ,E8.1,';')", " 0.1E+01;"}, + {"(RZ,F3.0,';')", " 1.;"}, + {"(RZ,G7.0,';')", " 0.E+01;"}, + {"(RZ,G7.1,';')", " 1. ;"}, + {"(RC,E8.1,';')", " 0.2E+01;"}, + {"(RC,F3.0,';')", " 2.;"}, + {"(RC,G7.0,';')", " 0.E+01;"}, + {"(RC,G7.1,';')", " 2. ;"}, + }}, + {// -1.5 + 0xbff8000000000000, + { + {"(E9.2,';')", "-0.15E+01;"}, + {"(RN,E8.1,';')", "-0.2E+01;"}, + {"(RD,E8.1,';')", "-0.2E+01;"}, + {"(RU,E8.1,';')", "-0.1E+01;"}, + {"(RZ,E8.1,';')", "-0.1E+01;"}, + {"(RC,E8.1,';')", "-0.2E+01;"}, + }}, + {// 2.5 + 0x4004000000000000, + { + {"(E9.2,';')", " 0.25E+01;"}, + {"(RN,E8.1,';')", " 0.2E+01;"}, + {"(RD,E8.1,';')", " 0.2E+01;"}, + {"(RU,E8.1,';')", " 0.3E+01;"}, + {"(RZ,E8.1,';')", " 0.2E+01;"}, + {"(RC,E8.1,';')", " 0.3E+01;"}, + }}, + {// -2.5 + 0xc004000000000000, + { + {"(E9.2,';')", "-0.25E+01;"}, + {"(RN,E8.1,';')", "-0.2E+01;"}, + {"(RD,E8.1,';')", "-0.3E+01;"}, + {"(RU,E8.1,';')", "-0.2E+01;"}, + {"(RZ,E8.1,';')", "-0.2E+01;"}, + {"(RC,E8.1,';')", "-0.3E+01;"}, + }}, + {// least positive nonzero subnormal + 1, + { + {"(E32.17,';')", " 0.49406564584124654-323;"}, + {"(ES32.17,';')", " 4.94065645841246544-324;"}, + {"(EN32.17,';')", " 4.94065645841246544-324;"}, + {"(E759.752,';')", + " 0." + "494065645841246544176568792868221372365059802614324764425585" + "682500675507270208751865299836361635992379796564695445717730" + "926656710355939796398774796010781878126300713190311404527845" + "817167848982103688718636056998730723050006387409153564984387" + "312473397273169615140031715385398074126238565591171026658556" + "686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355" + "238611550134803526493472019379026810710749170333222684475333" + "572083243193609238289345836806010601150616980975307834227731" + "832924790498252473077637592724787465608477820373446969953364" + "701797267771758512566055119913150489110145103786273816725095" + "583738973359899366480994116420570263709027924276754456522908" + "75386825064197182655334472656250-323;"}, + {"(G0,';')", ".5-323;"}, + {"(E757.750,';')", + " 0." + "494065645841246544176568792868221372365059802614324764425585" + "682500675507270208751865299836361635992379796564695445717730" + "926656710355939796398774796010781878126300713190311404527845" + "817167848982103688718636056998730723050006387409153564984387" + "312473397273169615140031715385398074126238565591171026658556" + "686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355" + "238611550134803526493472019379026810710749170333222684475333" + "572083243193609238289345836806010601150616980975307834227731" + "832924790498252473077637592724787465608477820373446969953364" + "701797267771758512566055119913150489110145103786273816725095" + "583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726562-323;"}, + {"(RN,E757.750,';')", + " 0." + "494065645841246544176568792868221372365059802614324764425585" + "682500675507270208751865299836361635992379796564695445717730" + "926656710355939796398774796010781878126300713190311404527845" + "817167848982103688718636056998730723050006387409153564984387" + "312473397273169615140031715385398074126238565591171026658556" + "686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355" + "238611550134803526493472019379026810710749170333222684475333" + "572083243193609238289345836806010601150616980975307834227731" + "832924790498252473077637592724787465608477820373446969953364" + "701797267771758512566055119913150489110145103786273816725095" + "583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726562-323;"}, + {"(RD,E757.750,';')", + " 0." + "494065645841246544176568792868221372365059802614324764425585" + "682500675507270208751865299836361635992379796564695445717730" + "926656710355939796398774796010781878126300713190311404527845" + "817167848982103688718636056998730723050006387409153564984387" + "312473397273169615140031715385398074126238565591171026658556" + "686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355" + "238611550134803526493472019379026810710749170333222684475333" + "572083243193609238289345836806010601150616980975307834227731" + "832924790498252473077637592724787465608477820373446969953364" + "701797267771758512566055119913150489110145103786273816725095" + "583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726562-323;"}, + {"(RU,E757.750,';')", + " 0." + "494065645841246544176568792868221372365059802614324764425585" + "682500675507270208751865299836361635992379796564695445717730" + "926656710355939796398774796010781878126300713190311404527845" + "817167848982103688718636056998730723050006387409153564984387" + "312473397273169615140031715385398074126238565591171026658556" + "686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355" + "238611550134803526493472019379026810710749170333222684475333" + "572083243193609238289345836806010601150616980975307834227731" + "832924790498252473077637592724787465608477820373446969953364" + "701797267771758512566055119913150489110145103786273816725095" + "583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726563-323;"}, + {"(RC,E757.750,';')", + " 0." + "494065645841246544176568792868221372365059802614324764425585" + "682500675507270208751865299836361635992379796564695445717730" + "926656710355939796398774796010781878126300713190311404527845" + "817167848982103688718636056998730723050006387409153564984387" + "312473397273169615140031715385398074126238565591171026658556" + "686768187039560310624931945271591492455329305456544401127480" + "129709999541931989409080416563324524757147869014726780159355" + "238611550134803526493472019379026810710749170333222684475333" + "572083243193609238289345836806010601150616980975307834227731" + "832924790498252473077637592724787465608477820373446969953364" + "701797267771758512566055119913150489110145103786273816725095" + "583738973359899366480994116420570263709027924276754456522908" + "753868250641971826553344726563-323;"}, + }}, + {// least positive nonzero normal + 0x10000000000000, + { + {"(E723.716,';')", + " 0." + "222507385850720138309023271733240406421921598046233183055332" + "741688720443481391819585428315901251102056406733973103581100" + "515243416155346010885601238537771882113077799353200233047961" + "014744258363607192156504694250373420837525080665061665815894" + "872049117996859163964850063590877011830487479978088775374994" + "945158045160505091539985658247081864511353793580499211598108" + "576605199243335211435239014879569960959128889160299264151106" + "346631339366347758651302937176204732563178148566435087212282" + "863764204484681140761391147706280168985324411002416144742161" + "856716615054015428508471675290190316132277889672970737312333" + "408698898317506783884692609277397797285865965494109136909540" + "61364675687023986783152906809846172109246253967285156250-" + "307;"}, + {"(G0,';')", ".22250738585072014-307;"}, + }}, + {// greatest finite + 0x7fefffffffffffffuLL, + { + {"(E32.17,';')", " 0.17976931348623157+309;"}, + {"(E317.310,';')", + " 0." + "179769313486231570814527423731704356798070567525844996598917" + "476803157260780028538760589558632766878171540458953514382464" + "234321326889464182768467546703537516986049910576551282076245" + "490090389328944075868508455133942304583236903222948165808559" + "332123348274797826204144723168738177180919299881250404026184" + "1248583680+309;"}, + {"(ES317.310,';')", + " 1." + "797693134862315708145274237317043567980705675258449965989174" + "768031572607800285387605895586327668781715404589535143824642" + "343213268894641827684675467035375169860499105765512820762454" + "900903893289440758685084551339423045832369032229481658085593" + "321233482747978262041447231687381771809192998812504040261841" + "2485836800+308;"}, + {"(EN319.310,';')", + " 179." + "769313486231570814527423731704356798070567525844996598917476" + "803157260780028538760589558632766878171540458953514382464234" + "321326889464182768467546703537516986049910576551282076245490" + "090389328944075868508455133942304583236903222948165808559332" + "123348274797826204144723168738177180919299881250404026184124" + "8583680000+306;"}, + {"(G0,';')", ".17976931348623157+309;"}, + }}, + }; + + for (auto const &[value, cases] : testCases) { + for (auto const &[format, expect] : cases) { + ASSERT_TRUE(CompareFormatReal(format, value, expect)) + << "Failed to format " << format << ", expected " << expect; + } + } + + using IndividualTestCaseTy = std::tuple<const char *, double, const char *>; + static std::vector<IndividualTestCaseTy> individualTestCases{ + {"(F5.3,';')", 25., "*****;"}, + {"(F5.3,';')", 2.5, "2.500;"}, + {"(F5.3,';')", 0.25, "0.250;"}, + {"(F5.3,';')", 0.025, "0.025;"}, + {"(F5.3,';')", 0.0025, "0.003;"}, + {"(F5.3,';')", 0.00025, "0.000;"}, + {"(F5.3,';')", 0.000025, "0.000;"}, + {"(F5.3,';')", -25., "*****;"}, + {"(F5.3,';')", -2.5, "*****;"}, + {"(F5.3,';')", -0.25, "-.250;"}, + {"(F5.3,';')", -0.025, "-.025;"}, + {"(F5.3,';')", -0.0025, "-.003;"}, + {"(F5.3,';')", -0.00025, "-.000;"}, + {"(F5.3,';')", -0.000025, "-.000;"}, + }; + + for (auto const &[format, value, expect] : individualTestCases) { + ASSERT_TRUE(CompareFormatReal(format, value, expect)) + << "Failed to format " << format << ", expected " << expect; + } +} + +//------------------------------------------------------------------------------ +/// Tests for input formatting real values +//------------------------------------------------------------------------------ + +// Ensure double input values correctly map to raw uint64 values +TEST(IOApiTests, FormatDoubleInputValues) { + using TestCaseTy = std::tuple<const char *, const char *, std::uint64_t>; + static std::vector<TestCaseTy> testCases{ + {"(F18.0)", " 0", 0x0}, + {"(F18.0)", " ", 0x0}, + {"(F18.0)", " -0", 0x8000000000000000}, + {"(F18.0)", " 01", 0x3ff0000000000000}, + {"(F18.0)", " 1", 0x3ff0000000000000}, + {"(F18.0)", " 125.", 0x405f400000000000}, + {"(F18.0)", " 12.5", 0x4029000000000000}, + {"(F18.0)", " 1.25", 0x3ff4000000000000}, + {"(F18.0)", " 01.25", 0x3ff4000000000000}, + {"(F18.0)", " .125", 0x3fc0000000000000}, + {"(F18.0)", " 0.125", 0x3fc0000000000000}, + {"(F18.0)", " .0625", 0x3fb0000000000000}, + {"(F18.0)", " 0.0625", 0x3fb0000000000000}, + {"(F18.0)", " 125", 0x405f400000000000}, + {"(F18.1)", " 125", 0x4029000000000000}, + {"(F18.2)", " 125", 0x3ff4000000000000}, + {"(F18.3)", " 125", 0x3fc0000000000000}, + {"(-1P,F18.0)", " 125", 0x4093880000000000}, // 1250 + {"(1P,F18.0)", " 125", 0x4029000000000000}, // 12.5 + {"(BZ,F18.0)", " 125 ", 0x4093880000000000}, // 1250 + {"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000}, // 1.25e13 + {"(DC,F18.0)", " 12,5", 0x4029000000000000}, + }; + for (auto const &[format, data, want] : testCases) { + auto *cookie{IONAME(BeginInternalFormattedInput)( + data, std::strlen(data), format, std::strlen(format))}; + union { + double x; + std::uint64_t raw; + } u; + u.raw = 0; + + // Read buffer into union value + IONAME(EnableHandlers)(cookie, true, true, true, true, true); + IONAME(InputReal64)(cookie, u.x); + + static constexpr int bufferSize{65}; + static char iomsg[bufferSize]; + std::memset(iomsg, '\0', bufferSize - 1); + + // Ensure no errors were encountered reading input buffer into union value + IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1); + static auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(status, 0) << '\'' << format << "' failed reading '" << data + << "', status " << static_cast<int>(status) + << " iomsg '" << iomsg << "'"; + + // Ensure raw uint64 value matches expected conversion from double + ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data + << "', want 0x" << std::hex << want << ", got 0x" + << u.raw; + } +} diff --git a/flang/unittests/RuntimeGTest/Random.cpp b/flang/unittests/RuntimeGTest/Random.cpp new file mode 100644 index 000000000000..f16f3dc1a47f --- /dev/null +++ b/flang/unittests/RuntimeGTest/Random.cpp @@ -0,0 +1,63 @@ +//===-- flang/unittests/RuntimeGTest/Random.cpp -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "../../runtime/random.h" +#include "gtest/gtest.h" +#include "../../runtime/descriptor.h" +#include "../../runtime/type-code.h" +#include <cmath> + +using namespace Fortran::runtime; + +TEST(RandomNumber, Real4) { + StaticDescriptor<1> statDesc; + Descriptor &harvest{statDesc.descriptor()}; + static constexpr int n{10000}; + float xs[n]{}; + SubscriptValue extent[1]{n}; + harvest.Establish(TypeCategory::Real, 4, xs, 1, extent); + RTNAME(RandomNumber)(harvest, __FILE__, __LINE__); + double sum{0}; + for (int j{0}; j < n; ++j) { + sum += xs[j]; + } + double mean{sum / n}; + std::fprintf(stderr, "mean of %d random numbers: %g\n", n, mean); + EXPECT_GE(mean, 0.95 * 0.5); // mean of uniform dist [0..1] is of course 0.5 + EXPECT_LE(mean, 1.05 * 0.5); + double sumsq{0}; + for (int j{0}; j < n; ++j) { + double diff{xs[j] - mean}; + sumsq += diff * diff; + } + double sdev{std::sqrt(sumsq / n)}; + std::fprintf(stderr, "stddev of %d random numbers: %g\n", n, sdev); + double expect{1.0 / std::sqrt(12.0)}; // stddev of uniform dist [0..1] + EXPECT_GE(sdev, 0.95 * expect); + EXPECT_LT(sdev, 1.05 * expect); +} + +TEST(RandomNumber, RandomSeed) { + StaticDescriptor<1> statDesc[2]; + Descriptor &desc{statDesc[0].descriptor()}; + std::int32_t n; + desc.Establish(TypeCategory::Integer, 4, &n, 0, nullptr); + RTNAME(RandomSeedSize)(desc, __FILE__, __LINE__); + EXPECT_EQ(n, 1); + SubscriptValue extent[1]{1}; + desc.Establish(TypeCategory::Integer, 4, &n, 1, extent); + RTNAME(RandomSeedGet)(desc, __FILE__, __LINE__); + Descriptor &harvest{statDesc[1].descriptor()}; + float x; + harvest.Establish(TypeCategory::Real, 4, &x, 1, extent); + RTNAME(RandomNumber)(harvest, __FILE__, __LINE__); + float got{x}; + RTNAME(RandomSeedPut)(desc, __FILE__, __LINE__); // n from RandomSeedGet() + RTNAME(RandomNumber)(harvest, __FILE__, __LINE__); + EXPECT_EQ(x, got); +} diff --git a/flang/unittests/RuntimeGTest/Reduction.cpp b/flang/unittests/RuntimeGTest/Reduction.cpp new file mode 100644 index 000000000000..111b5674285f --- /dev/null +++ b/flang/unittests/RuntimeGTest/Reduction.cpp @@ -0,0 +1,265 @@ +//===-- flang/unittests/RuntimeGTest/Reductions.cpp -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "../../runtime/reduction.h" +#include "gtest/gtest.h" +#include "tools.h" +#include "../../runtime/allocatable.h" +#include "../../runtime/cpp-type.h" +#include "../../runtime/descriptor.h" +#include "../../runtime/type-code.h" +#include <cstdint> +#include <cstring> +#include <string> +#include <vector> + +using namespace Fortran::runtime; +using Fortran::common::TypeCategory; + +TEST(Reductions, SumInt4) { + auto array{MakeArray<TypeCategory::Integer, 4>( + std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})}; + std::int32_t sum{RTNAME(SumInteger4)(*array, __FILE__, __LINE__)}; + EXPECT_EQ(sum, 21) << sum; +} + +TEST(Reductions, DimMaskProductInt4) { + std::vector<int> shape{2, 3}; + auto array{MakeArray<TypeCategory::Integer, 4>( + shape, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})}; + auto mask{MakeArray<TypeCategory::Logical, 1>( + shape, std::vector<bool>{true, false, false, true, true, true})}; + StaticDescriptor<1> statDesc; + Descriptor &prod{statDesc.descriptor()}; + RTNAME(ProductDim)(prod, *array, 1, __FILE__, __LINE__, &*mask); + EXPECT_EQ(prod.rank(), 1); + EXPECT_EQ(prod.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(prod.GetDimension(0).Extent(), 3); + EXPECT_EQ(*prod.ZeroBasedIndexedElement<std::int32_t>(0), 1); + EXPECT_EQ(*prod.ZeroBasedIndexedElement<std::int32_t>(1), 4); + EXPECT_EQ(*prod.ZeroBasedIndexedElement<std::int32_t>(2), 30); + EXPECT_EQ(RTNAME(SumInteger4)(prod, __FILE__, __LINE__), 35); + prod.Destroy(); +} + +TEST(Reductions, DoubleMaxMin) { + std::vector<int> shape{3, 4, 2}; // rows, columns, planes + // 0 -3 6 -9 12 -15 18 -21 + // -1 4 -7 10 -13 16 -19 22 + // 2 -5 8 -11 14 -17 20 22 <- note last two are equal to test + // BACK= + auto array{MakeArray<TypeCategory::Real, 8>(shape, + std::vector<double>{0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, + 14, -15, 16, -17, 18, -19, 20, -21, 22, 22})}; + EXPECT_EQ(RTNAME(MaxvalReal8)(*array, __FILE__, __LINE__), 22.0); + EXPECT_EQ(RTNAME(MinvalReal8)(*array, __FILE__, __LINE__), -21.0); + StaticDescriptor<2> statDesc; + Descriptor &loc{statDesc.descriptor()}; + RTNAME(Maxloc) + (loc, *array, /*KIND=*/8, __FILE__, __LINE__, /*MASK=*/nullptr, + /*BACK=*/false); + EXPECT_EQ(loc.rank(), 1); + EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw())); + EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(loc.GetDimension(0).Extent(), 3); + EXPECT_EQ( + *array->Element<double>(loc.ZeroBasedIndexedElement<SubscriptValue>(0)), + 22.0); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(0), 2); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(1), 4); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(2), 2); + loc.Destroy(); + RTNAME(Maxloc) + (loc, *array, /*KIND=*/8, __FILE__, __LINE__, /*MASK=*/nullptr, + /*BACK=*/true); + EXPECT_EQ(loc.rank(), 1); + EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw())); + EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(loc.GetDimension(0).Extent(), 3); + EXPECT_EQ( + *array->Element<double>(loc.ZeroBasedIndexedElement<SubscriptValue>(0)), + 22.0); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(0), 3); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(1), 4); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(2), 2); + loc.Destroy(); + RTNAME(MinlocDim) + (loc, *array, /*KIND=*/2, /*DIM=*/1, __FILE__, __LINE__, /*MASK=*/nullptr, + /*BACK=*/false); + EXPECT_EQ(loc.rank(), 2); + EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 2}.raw())); + EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(loc.GetDimension(0).Extent(), 4); + EXPECT_EQ(loc.GetDimension(1).LowerBound(), 1); + EXPECT_EQ(loc.GetDimension(1).Extent(), 2); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(0), 2); // -1 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(1), 3); // -5 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(2), 2); // -2 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(3), 3); // -11 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(4), 2); // -13 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(5), 3); // -17 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(6), 2); // -19 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(7), 1); // -21 + loc.Destroy(); + auto mask{MakeArray<TypeCategory::Logical, 1>(shape, + std::vector<bool>{false, false, false, false, false, true, false, true, + false, false, true, true, true, false, false, true, false, true, true, + true, false, true, true, true})}; + RTNAME(MaxlocDim) + (loc, *array, /*KIND=*/2, /*DIM=*/3, __FILE__, __LINE__, /*MASK=*/&*mask, + false); + EXPECT_EQ(loc.rank(), 2); + EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 2}.raw())); + EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(loc.GetDimension(0).Extent(), 3); + EXPECT_EQ(loc.GetDimension(1).LowerBound(), 1); + EXPECT_EQ(loc.GetDimension(1).Extent(), 4); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(0), 2); // 12 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(1), 0); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(2), 0); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(3), 2); // -15 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(4), 0); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(5), 1); // -5 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(6), 2); // 18 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(7), 1); // -7 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(8), 0); + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(9), 2); // -21 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(10), 2); // 22 + EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(11), 2); // 22 + loc.Destroy(); +} + +TEST(Reductions, Character) { + std::vector<int> shape{2, 3}; + auto array{MakeArray<TypeCategory::Character, 1>(shape, + std::vector<std::string>{"abc", "def", "ghi", "jkl", "mno", "abc"}, 3)}; + StaticDescriptor<1> statDesc; + Descriptor &res{statDesc.descriptor()}; + RTNAME(MaxvalCharacter)(res, *array, __FILE__, __LINE__); + EXPECT_EQ(res.rank(), 0); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Character, 1}.raw())); + EXPECT_EQ(std::memcmp(res.OffsetElement<char>(), "mno", 3), 0); + res.Destroy(); + RTNAME(MinvalCharacter)(res, *array, __FILE__, __LINE__); + EXPECT_EQ(res.rank(), 0); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Character, 1}.raw())); + EXPECT_EQ(std::memcmp(res.OffsetElement<char>(), "abc", 3), 0); + res.Destroy(); + RTNAME(Maxloc) + (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/nullptr, + /*BACK=*/false); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 3); + res.Destroy(); + auto mask{MakeArray<TypeCategory::Logical, 1>( + shape, std::vector<bool>{false, true, false, true, false, true})}; + RTNAME(Maxloc) + (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/&*mask, + /*BACK=*/false); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 2); + res.Destroy(); + RTNAME(Minloc) + (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/nullptr, + /*BACK=*/false); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1); + res.Destroy(); + RTNAME(Minloc) + (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/nullptr, + /*BACK=*/true); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 3); + res.Destroy(); + RTNAME(Minloc) + (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/&*mask, + /*BACK=*/true); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 3); + res.Destroy(); +} + +TEST(Reductions, Logical) { + std::vector<int> shape{2, 2}; + auto array{MakeArray<TypeCategory::Logical, 4>( + shape, std::vector<std::int32_t>{false, false, true, true})}; + ASSERT_EQ(array->ElementBytes(), std::size_t{4}); + EXPECT_EQ(RTNAME(All)(*array, __FILE__, __LINE__), false); + EXPECT_EQ(RTNAME(Any)(*array, __FILE__, __LINE__), true); + EXPECT_EQ(RTNAME(Count)(*array, __FILE__, __LINE__), 2); + StaticDescriptor<2> statDesc; + Descriptor &res{statDesc.descriptor()}; + RTNAME(AllDim)(res, *array, /*DIM=*/1, __FILE__, __LINE__); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1); + res.Destroy(); + RTNAME(AllDim)(res, *array, /*DIM=*/2, __FILE__, __LINE__); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 0); + res.Destroy(); + RTNAME(AnyDim)(res, *array, /*DIM=*/1, __FILE__, __LINE__); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1); + res.Destroy(); + RTNAME(AnyDim)(res, *array, /*DIM=*/2, __FILE__, __LINE__); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1); + res.Destroy(); + RTNAME(CountDim)(res, *array, /*DIM=*/1, /*KIND=*/4, __FILE__, __LINE__); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 2); + res.Destroy(); + RTNAME(CountDim)(res, *array, /*DIM=*/2, /*KIND=*/8, __FILE__, __LINE__); + EXPECT_EQ(res.rank(), 1); + EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw())); + EXPECT_EQ(res.GetDimension(0).LowerBound(), 1); + EXPECT_EQ(res.GetDimension(0).Extent(), 2); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int64_t>(0), 1); + EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int64_t>(1), 1); + res.Destroy(); +} diff --git a/flang/unittests/RuntimeGTest/tools.h b/flang/unittests/RuntimeGTest/tools.h new file mode 100644 index 000000000000..c2c31dcef414 --- /dev/null +++ b/flang/unittests/RuntimeGTest/tools.h @@ -0,0 +1,56 @@ +//===-- flang/unittests/RuntimeGTest/tools.h --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_UNITTESTS_RUNTIME_TOOLS_H_ +#define FORTRAN_UNITTESTS_RUNTIME_TOOLS_H_ + +#include "gtest/gtest.h" +#include "../../runtime/allocatable.h" +#include "../../runtime/cpp-type.h" +#include "../../runtime/descriptor.h" +#include "../../runtime/type-code.h" +#include <cstdint> +#include <cstring> +#include <vector> + +namespace Fortran::runtime { + +template <typename A> +static void StoreElement(void *p, const A &x, std::size_t bytes) { + std::memcpy(p, &x, bytes); +} + +template <typename CHAR> +static void StoreElement( + void *p, const std::basic_string<CHAR> &str, std::size_t bytes) { + ASSERT_LE(bytes, sizeof(CHAR) * str.size()); + std::memcpy(p, str.data(), bytes); +} + +template <TypeCategory CAT, int KIND, typename A> +static OwningPtr<Descriptor> MakeArray(const std::vector<int> &shape, + const std::vector<A> &data, std::size_t elemLen = KIND) { + auto rank{static_cast<int>(shape.size())}; + auto result{Descriptor::Create(TypeCode{CAT, KIND}, elemLen, nullptr, rank, + nullptr, CFI_attribute_allocatable)}; + for (int j{0}; j < rank; ++j) { + result->GetDimension(j).SetBounds(1, shape[j]); + } + int stat{result->Allocate()}; + EXPECT_EQ(stat, 0) << stat; + EXPECT_LE(data.size(), result->Elements()); + char *p{result->OffsetElement<char>()}; + for (A x : data) { + StoreElement(p, x, elemLen); + p += elemLen; + } + return result; +} + +} // namespace Fortran::runtime +#endif // FORTRAN_UNITTESTS_RUNTIME_TOOLS_H_ |