[flang-commits] [flang] f65f830 - [flang] runtime: fix output B/O/Z editing of "negative" values

peter klausler via flang-commits flang-commits at lists.llvm.org
Tue Oct 12 16:03:22 PDT 2021


Author: peter klausler
Date: 2021-10-12T16:02:07-07:00
New Revision: f65f830e5f935ab61ddb1f9af98ce3e5755eb2b2

URL: https://github.com/llvm/llvm-project/commit/f65f830e5f935ab61ddb1f9af98ce3e5755eb2b2
DIFF: https://github.com/llvm/llvm-project/commit/f65f830e5f935ab61ddb1f9af98ce3e5755eb2b2.diff

LOG: [flang] runtime: fix output B/O/Z editing of "negative" values

B/O/Z integer output editing must not reflect any sign extension
of scalar output values.  Add more size-dependent OutputInteger
I/O APIs and kind instantiations of EditIntegerOutput.

Differential Revision: https://reviews.llvm.org/D111678

Added: 
    

Modified: 
    flang/include/flang/Common/uint128.h
    flang/include/flang/Runtime/io-api.h
    flang/runtime/descriptor-io.h
    flang/runtime/edit-output.cpp
    flang/runtime/edit-output.h
    flang/runtime/io-api.cpp
    flang/unittests/Runtime/NumericalFormatTest.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index f5ac48de70989..68098da282f29 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Portable 128-bit unsigned integer arithmetic for use in impoverished
-// C++ implementations lacking __uint128_t.
+// Portable 128-bit integer arithmetic for use in impoverished C++
+// implementations lacking __uint128_t & __int128_t.
 
 #ifndef FORTRAN_COMMON_UINT128_H_
 #define FORTRAN_COMMON_UINT128_H_
@@ -47,6 +47,18 @@ template <bool IS_SIGNED = false> class Int128 {
   constexpr Int128 &operator=(const Int128 &) = default;
   constexpr Int128 &operator=(Int128 &&) = default;
 
+  constexpr Int128(const Int128<!IS_SIGNED> &n)
+      : low_{n.low()}, high_{n.high()} {}
+  constexpr Int128(Int128<!IS_SIGNED> &&n) : low_{n.low()}, high_{n.high()} {}
+  constexpr Int128 &operator=(const Int128<!IS_SIGNED> &n) {
+    low_ = n.low();
+    high_ = n.high();
+  }
+  constexpr Int128 &operator=(Int128<!IS_SIGNED> &&n) {
+    low_ = n.low();
+    high_ = n.high();
+  }
+
   constexpr Int128 operator+() const { return *this; }
   constexpr Int128 operator~() const { return {~high_, ~low_}; }
   constexpr Int128 operator-() const { return ~*this + 1; }

diff  --git a/flang/include/flang/Runtime/io-api.h b/flang/include/flang/Runtime/io-api.h
index d6ceec9cab519..800244c29884d 100644
--- a/flang/include/flang/Runtime/io-api.h
+++ b/flang/include/flang/Runtime/io-api.h
@@ -11,6 +11,7 @@
 #ifndef FORTRAN_RUNTIME_IO_API_H_
 #define FORTRAN_RUNTIME_IO_API_H_
 
+#include "flang/Common/uint128.h"
 #include "flang/Runtime/entry-names.h"
 #include "flang/Runtime/iostat.h"
 #include <cinttypes>
@@ -56,7 +57,7 @@ extern "C" {
 // These functions initiate data transfer statements (READ, WRITE, PRINT).
 // Example: PRINT *, 666 is implemented as the series of calls:
 //   Cookie cookie{BeginExternalListOutput(DefaultUnit,__FILE__,__LINE__)};
-//   OutputInteger64(cookie, 666);
+//   OutputInteger32(cookie, 666);
 //   EndIoStatement(cookie);
 
 // Internal I/O initiation
@@ -225,7 +226,13 @@ bool IONAME(OutputUnformattedBlock)(
 bool IONAME(InputUnformattedBlock)(
     Cookie, char *, std::size_t, std::size_t elementBytes);
 // Formatted (including list directed) I/O data items
+bool IONAME(OutputInteger8)(Cookie, std::int8_t);
+bool IONAME(OutputInteger16)(Cookie, std::int16_t);
+bool IONAME(OutputInteger32)(Cookie, std::int32_t);
 bool IONAME(OutputInteger64)(Cookie, std::int64_t);
+#ifdef __SIZEOF_INT128__
+bool IONAME(OutputInteger128)(Cookie, common::int128_t);
+#endif
 bool IONAME(InputInteger)(Cookie, std::int64_t &, int kind = 8);
 bool IONAME(OutputReal32)(Cookie, float);
 bool IONAME(InputReal32)(Cookie, float &);

diff  --git a/flang/runtime/descriptor-io.h b/flang/runtime/descriptor-io.h
index f2aa6a6aa326b..6246f3474af85 100644
--- a/flang/runtime/descriptor-io.h
+++ b/flang/runtime/descriptor-io.h
@@ -42,22 +42,22 @@ inline A &ExtractElement(IoStatementState &io, const Descriptor &descriptor,
 // automatic repetition counts, like "10*3.14159", for list-directed and
 // NAMELIST array output.
 
-template <typename A, Direction DIR>
+template <int KIND, Direction DIR>
 inline bool FormattedIntegerIO(
     IoStatementState &io, const Descriptor &descriptor) {
   std::size_t numElements{descriptor.Elements()};
   SubscriptValue subscripts[maxRank];
   descriptor.GetLowerBounds(subscripts);
+  using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
   for (std::size_t j{0}; j < numElements; ++j) {
     if (auto edit{io.GetNextDataEdit()}) {
-      A &x{ExtractElement<A>(io, descriptor, subscripts)};
+      IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
       if constexpr (DIR == Direction::Output) {
-        if (!EditIntegerOutput(io, *edit, static_cast<std::int64_t>(x))) {
+        if (!EditIntegerOutput<KIND>(io, *edit, x)) {
           return false;
         }
       } else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
-        if (!EditIntegerInput(io, *edit, reinterpret_cast<void *>(&x),
-                static_cast<int>(sizeof(A)))) {
+        if (!EditIntegerInput(io, *edit, reinterpret_cast<void *>(&x), KIND)) {
           return false;
         }
       }
@@ -183,15 +183,16 @@ inline bool FormattedCharacterIO(
   return true;
 }
 
-template <typename A, Direction DIR>
+template <int KIND, Direction DIR>
 inline bool FormattedLogicalIO(
     IoStatementState &io, const Descriptor &descriptor) {
   std::size_t numElements{descriptor.Elements()};
   SubscriptValue subscripts[maxRank];
   descriptor.GetLowerBounds(subscripts);
   auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
+  using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
   for (std::size_t j{0}; j < numElements; ++j) {
-    A &x{ExtractElement<A>(io, descriptor, subscripts)};
+    IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
     if (listOutput) {
       if (!ListDirectedLogicalOutput(io, *listOutput, x != 0)) {
         return false;
@@ -377,20 +378,15 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) {
     case TypeCategory::Integer:
       switch (kind) {
       case 1:
-        return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 1>, DIR>(
-            io, descriptor);
+        return FormattedIntegerIO<1, DIR>(io, descriptor);
       case 2:
-        return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 2>, DIR>(
-            io, descriptor);
+        return FormattedIntegerIO<2, DIR>(io, descriptor);
       case 4:
-        return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 4>, DIR>(
-            io, descriptor);
+        return FormattedIntegerIO<4, DIR>(io, descriptor);
       case 8:
-        return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 8>, DIR>(
-            io, descriptor);
+        return FormattedIntegerIO<8, DIR>(io, descriptor);
       case 16:
-        return FormattedIntegerIO<CppTypeFor<TypeCategory::Integer, 16>, DIR>(
-            io, descriptor);
+        return FormattedIntegerIO<16, DIR>(io, descriptor);
       default:
         handler.Crash(
             "DescriptorIO: Unimplemented INTEGER kind (%d) in descriptor",
@@ -452,17 +448,13 @@ static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor) {
     case TypeCategory::Logical:
       switch (kind) {
       case 1:
-        return FormattedLogicalIO<CppTypeFor<TypeCategory::Integer, 1>, DIR>(
-            io, descriptor);
+        return FormattedLogicalIO<1, DIR>(io, descriptor);
       case 2:
-        return FormattedLogicalIO<CppTypeFor<TypeCategory::Integer, 2>, DIR>(
-            io, descriptor);
+        return FormattedLogicalIO<2, DIR>(io, descriptor);
       case 4:
-        return FormattedLogicalIO<CppTypeFor<TypeCategory::Integer, 4>, DIR>(
-            io, descriptor);
+        return FormattedLogicalIO<4, DIR>(io, descriptor);
       case 8:
-        return FormattedLogicalIO<CppTypeFor<TypeCategory::Integer, 8>, DIR>(
-            io, descriptor);
+        return FormattedLogicalIO<8, DIR>(io, descriptor);
       default:
         handler.Crash(
             "DescriptorIO: Unimplemented LOGICAL kind (%d) in descriptor",

diff  --git a/flang/runtime/edit-output.cpp b/flang/runtime/edit-output.cpp
index d9634b8399493..c5d5d6c1f0ab2 100644
--- a/flang/runtime/edit-output.cpp
+++ b/flang/runtime/edit-output.cpp
@@ -12,27 +12,27 @@
 
 namespace Fortran::runtime::io {
 
-template <typename INT, typename UINT>
-bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit, INT n) {
-  char buffer[130], *end = &buffer[sizeof buffer], *p = end;
-  bool isNegative{false};
-  if constexpr (std::is_same_v<INT, UINT>) {
-    isNegative = (n >> (8 * sizeof(INT) - 1)) != 0;
-  } else {
-    isNegative = n < 0;
-  }
-  UINT un{static_cast<UINT>(isNegative ? -n : n)};
+template <int KIND>
+bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit,
+    common::HostSignedIntType<8 * KIND> n) {
+  char buffer[130], *end{&buffer[sizeof buffer]}, *p{end};
+  bool isNegative{n < 0};
+  using Unsigned = common::HostUnsignedIntType<8 * KIND>;
+  Unsigned un{static_cast<Unsigned>(n)};
   int signChars{0};
   switch (edit.descriptor) {
   case DataEdit::ListDirected:
   case 'G':
   case 'I':
+    if (isNegative) {
+      un = -n;
+    }
     if (isNegative || (edit.modes.editingFlags & signPlus)) {
       signChars = 1; // '-' or '+'
     }
     while (un > 0) {
       auto quotient{un / 10u};
-      *--p = '0' + static_cast<int>(un - UINT{10} * quotient);
+      *--p = '0' + static_cast<int>(un - 10u * quotient);
       un = quotient;
     }
     break;
@@ -382,8 +382,7 @@ bool RealOutputEditing<binaryPrecision>::EditEXOutput(const DataEdit &) {
       "EX output editing is not yet implemented"); // TODO
 }
 
-template <int binaryPrecision>
-bool RealOutputEditing<binaryPrecision>::Edit(const DataEdit &edit) {
+template <int KIND> bool RealOutputEditing<KIND>::Edit(const DataEdit &edit) {
   switch (edit.descriptor) {
   case 'D':
     return EditEorDOutput(edit);
@@ -398,7 +397,7 @@ bool RealOutputEditing<binaryPrecision>::Edit(const DataEdit &edit) {
   case 'B':
   case 'O':
   case 'Z':
-    return EditIntegerOutput(io_, edit,
+    return EditIntegerOutput<KIND>(io_, edit,
         decimal::BinaryFloatingPointNumber<binaryPrecision>{x_}.raw());
   case 'G':
     return Edit(EditForGOutput(edit));
@@ -503,10 +502,16 @@ bool EditDefaultCharacterOutput(IoStatementState &io, const DataEdit &edit,
       io.Emit(x, std::min(width, len));
 }
 
-template bool EditIntegerOutput<std::int64_t, std::uint64_t>(
+template bool EditIntegerOutput<1>(
+    IoStatementState &, const DataEdit &, std::int8_t);
+template bool EditIntegerOutput<2>(
+    IoStatementState &, const DataEdit &, std::int16_t);
+template bool EditIntegerOutput<4>(
+    IoStatementState &, const DataEdit &, std::int32_t);
+template bool EditIntegerOutput<8>(
     IoStatementState &, const DataEdit &, std::int64_t);
-template bool EditIntegerOutput<common::uint128_t, common::uint128_t>(
-    IoStatementState &, const DataEdit &, common::uint128_t);
+template bool EditIntegerOutput<16>(
+    IoStatementState &, const DataEdit &, common::int128_t);
 
 template class RealOutputEditing<2>;
 template class RealOutputEditing<3>;

diff  --git a/flang/runtime/edit-output.h b/flang/runtime/edit-output.h
index 251dc797f5e09..843310e992bc9 100644
--- a/flang/runtime/edit-output.h
+++ b/flang/runtime/edit-output.h
@@ -29,8 +29,9 @@ namespace Fortran::runtime::io {
 // The DataEdit reference is const here (and elsewhere in this header) so that
 // one edit descriptor with a repeat factor may safely serve to edit
 // multiple elements of an array.
-template <typename INT = std::int64_t, typename UINT = std::uint64_t>
-bool EditIntegerOutput(IoStatementState &, const DataEdit &, INT);
+template <int KIND>
+bool EditIntegerOutput(
+    IoStatementState &, const DataEdit &, common::HostSignedIntType<8 * KIND>);
 
 // Encapsulates the state of a REAL output conversion.
 class RealOutputEditingBase {
@@ -98,10 +99,16 @@ bool ListDirectedDefaultCharacterOutput(IoStatementState &,
 bool EditDefaultCharacterOutput(
     IoStatementState &, const DataEdit &, const char *, std::size_t);
 
-extern template bool EditIntegerOutput<std::int64_t, std::uint64_t>(
+extern template bool EditIntegerOutput<1>(
+    IoStatementState &, const DataEdit &, std::int8_t);
+extern template bool EditIntegerOutput<2>(
+    IoStatementState &, const DataEdit &, std::int16_t);
+extern template bool EditIntegerOutput<4>(
+    IoStatementState &, const DataEdit &, std::int32_t);
+extern template bool EditIntegerOutput<8>(
     IoStatementState &, const DataEdit &, std::int64_t);
-extern template bool EditIntegerOutput<common::uint128_t, common::uint128_t>(
-    IoStatementState &, const DataEdit &, common::uint128_t);
+extern template bool EditIntegerOutput<16>(
+    IoStatementState &, const DataEdit &, common::int128_t);
 
 extern template class RealOutputEditing<2>;
 extern template class RealOutputEditing<3>;

diff  --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index bfef8a4e5ae2c..061f1552270f0 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -949,14 +949,52 @@ bool IONAME(InputUnformattedBlock)(
   return false;
 }
 
+bool IONAME(OutputInteger8)(Cookie cookie, std::int8_t n) {
+  cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger8");
+  StaticDescriptor staticDescriptor;
+  Descriptor &descriptor{staticDescriptor.descriptor()};
+  descriptor.Establish(
+      TypeCategory::Integer, 1, reinterpret_cast<void *>(&n), 0);
+  return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
+}
+
+bool IONAME(OutputInteger16)(Cookie cookie, std::int16_t n) {
+  cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger16");
+  StaticDescriptor staticDescriptor;
+  Descriptor &descriptor{staticDescriptor.descriptor()};
+  descriptor.Establish(
+      TypeCategory::Integer, 2, reinterpret_cast<void *>(&n), 0);
+  return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
+}
+
+bool IONAME(OutputInteger32)(Cookie cookie, std::int32_t n) {
+  cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger32");
+  StaticDescriptor staticDescriptor;
+  Descriptor &descriptor{staticDescriptor.descriptor()};
+  descriptor.Establish(
+      TypeCategory::Integer, 4, reinterpret_cast<void *>(&n), 0);
+  return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
+}
+
 bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) {
   cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger64");
   StaticDescriptor staticDescriptor;
   Descriptor &descriptor{staticDescriptor.descriptor()};
   descriptor.Establish(
-      TypeCategory::Integer, sizeof n, reinterpret_cast<void *>(&n), 0);
+      TypeCategory::Integer, 8, reinterpret_cast<void *>(&n), 0);
+  return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
+}
+
+#ifdef __SIZEOF_INT128__
+bool IONAME(OutputInteger128)(Cookie cookie, common::int128_t n) {
+  cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger128");
+  StaticDescriptor staticDescriptor;
+  Descriptor &descriptor{staticDescriptor.descriptor()};
+  descriptor.Establish(
+      TypeCategory::Integer, 16, reinterpret_cast<void *>(&n), 0);
   return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
 }
+#endif
 
 bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
   cookie->CheckFormattedStmtType<Direction::Input>("InputInteger");

diff  --git a/flang/unittests/Runtime/NumericalFormatTest.cpp b/flang/unittests/Runtime/NumericalFormatTest.cpp
index e2cad5ff80755..0dd274713696a 100644
--- a/flang/unittests/Runtime/NumericalFormatTest.cpp
+++ b/flang/unittests/Runtime/NumericalFormatTest.cpp
@@ -66,7 +66,7 @@ TEST(IOApiTests, HelloWorldOutputTest) {
   // Write string, integer, and logical values to buffer
   IONAME(OutputAscii)(cookie, "WORLD", 5);
   IONAME(OutputInteger64)(cookie, 678);
-  IONAME(OutputInteger64)(cookie, 0xfeedface);
+  IONAME(OutputInteger32)(cookie, 0xfeedface);
   IONAME(OutputLogical)(cookie, true);
 
   // Ensure IO succeeded


        


More information about the flang-commits mailing list