[flang-commits] [flang] 8f2c5c4 - [flang] Implement byte-swapped external unformatted I/O in runtime

peter klausler via flang-commits flang-commits at lists.llvm.org
Tue Jul 21 18:15:30 PDT 2020


Author: peter klausler
Date: 2020-07-21T18:14:46-07:00
New Revision: 8f2c5c4314f2360bf35d54507a54a5b612a41082

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

LOG: [flang] Implement byte-swapped external unformatted I/O in runtime

Add SetConvert() to the OPEN statement's runtime API.
Add ByteswapOption() to the main program's runtime API.
Check a $FORT_CONVERT environment variable, too, for
a swapping specifier.

Reviewed By: sscalpone

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

Added: 
    

Modified: 
    flang/runtime/environment.cpp
    flang/runtime/environment.h
    flang/runtime/format.cpp
    flang/runtime/format.h
    flang/runtime/io-api.cpp
    flang/runtime/io-api.h
    flang/runtime/io-stmt.cpp
    flang/runtime/io-stmt.h
    flang/runtime/main.cpp
    flang/runtime/main.h
    flang/runtime/unit.cpp
    flang/runtime/unit.h
    flang/tools/f18/f18.cpp
    flang/unittests/Runtime/external-io.cpp

Removed: 
    


################################################################################
diff  --git a/flang/runtime/environment.cpp b/flang/runtime/environment.cpp
index e05baa3323cd..2b62e8a729a5 100644
--- a/flang/runtime/environment.cpp
+++ b/flang/runtime/environment.cpp
@@ -7,13 +7,35 @@
 //===----------------------------------------------------------------------===//
 
 #include "environment.h"
+#include "tools.h"
 #include <cstdio>
 #include <cstdlib>
+#include <cstring>
 #include <limits>
 
 namespace Fortran::runtime {
+
 ExecutionEnvironment executionEnvironment;
 
+std::optional<Convert> GetConvertFromString(const char *x, std::size_t n) {
+  static const char *keywords[]{
+      "UNKNOWN", "NATIVE", "LITTLE_ENDIAN", "BIG_ENDIAN", "SWAP", nullptr};
+  switch (IdentifyValue(x, n, keywords)) {
+  case 0:
+    return Convert::Unknown;
+  case 1:
+    return Convert::Native;
+  case 2:
+    return Convert::LittleEndian;
+  case 3:
+    return Convert::BigEndian;
+  case 4:
+    return Convert::Swap;
+  default:
+    return std::nullopt;
+  }
+}
+
 void ExecutionEnvironment::Configure(
     int ac, const char *av[], const char *env[]) {
   argc = ac;
@@ -22,6 +44,7 @@ void ExecutionEnvironment::Configure(
   listDirectedOutputLineLengthLimit = 79; // PGI default
   defaultOutputRoundingMode =
       decimal::FortranRounding::RoundNearest; // RP(==RN)
+  conversion = Convert::Unknown;
 
   if (auto *x{std::getenv("FORT_FMT_RECL")}) {
     char *end;
@@ -34,6 +57,15 @@ void ExecutionEnvironment::Configure(
     }
   }
 
+  if (auto *x{std::getenv("FORT_CONVERT")}) {
+    if (auto convert{GetConvertFromString(x, std::strlen(x))}) {
+      conversion = *convert;
+    } else {
+      std::fprintf(
+          stderr, "Fortran runtime: FORT_CONVERT=%s is invalid; ignored\n", x);
+    }
+  }
+
   // TODO: Set RP/ROUND='PROCESSOR_DEFINED' from environment
 }
 } // namespace Fortran::runtime

diff  --git a/flang/runtime/environment.h b/flang/runtime/environment.h
index 0d9d318915cd..c04c3133f02a 100644
--- a/flang/runtime/environment.h
+++ b/flang/runtime/environment.h
@@ -10,8 +10,23 @@
 #define FORTRAN_RUNTIME_ENVIRONMENT_H_
 
 #include "flang/Decimal/decimal.h"
+#include <optional>
 
 namespace Fortran::runtime {
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+constexpr bool isHostLittleEndian{false};
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+constexpr bool isHostLittleEndian{true};
+#else
+#error host endianness is not known
+#endif
+
+// External unformatted I/O data conversions
+enum class Convert { Unknown, Native, LittleEndian, BigEndian, Swap };
+
+std::optional<Convert> GetConvertFromString(const char *, std::size_t);
+
 struct ExecutionEnvironment {
   void Configure(int argc, const char *argv[], const char *envp[]);
 
@@ -20,6 +35,7 @@ struct ExecutionEnvironment {
   const char **envp;
   int listDirectedOutputLineLengthLimit;
   enum decimal::FortranRounding defaultOutputRoundingMode;
+  Convert conversion;
 };
 extern ExecutionEnvironment executionEnvironment;
 } // namespace Fortran::runtime

diff  --git a/flang/runtime/format.cpp b/flang/runtime/format.cpp
index 2c259b1f7b83..65ed12447bb5 100644
--- a/flang/runtime/format.cpp
+++ b/flang/runtime/format.cpp
@@ -15,7 +15,8 @@ DataEdit DefaultFormatControlCallbacks::GetNextDataEdit(int) {
         "non-formatted I/O statement");
   return {};
 }
-bool DefaultFormatControlCallbacks::Emit(const char *, std::size_t) {
+bool DefaultFormatControlCallbacks::Emit(
+    const char *, std::size_t, std::size_t) {
   Crash("DefaultFormatControlCallbacks::Emit(char) called for non-output I/O "
         "statement");
   return {};

diff  --git a/flang/runtime/format.h b/flang/runtime/format.h
index 07eff97a7048..7d33e0ffbd14 100644
--- a/flang/runtime/format.h
+++ b/flang/runtime/format.h
@@ -63,7 +63,7 @@ struct DataEdit {
 struct DefaultFormatControlCallbacks : public IoErrorHandler {
   using IoErrorHandler::IoErrorHandler;
   DataEdit GetNextDataEdit(int = 1);
-  bool Emit(const char *, std::size_t);
+  bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
   bool Emit(const char16_t *, std::size_t);
   bool Emit(const char32_t *, std::size_t);
   std::optional<char32_t> GetCurrentChar();

diff  --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp
index 2f077e1f9ff8..5e595a93ce05 100644
--- a/flang/runtime/io-api.cpp
+++ b/flang/runtime/io-api.cpp
@@ -614,6 +614,24 @@ bool IONAME(SetAsynchronous)(
   }
 }
 
+bool IONAME(SetConvert)(
+    Cookie cookie, const char *keyword, std::size_t length) {
+  IoStatementState &io{*cookie};
+  auto *open{io.get_if<OpenStatementState>()};
+  if (!open) {
+    io.GetIoErrorHandler().Crash(
+        "SetConvert() called when not in an OPEN statement");
+  }
+  if (auto convert{GetConvertFromString(keyword, length)}) {
+    open->set_convert(*convert);
+    return true;
+  } else {
+    open->SignalError(IostatErrorInKeyword, "Invalid CONVERT='%.*s'",
+        static_cast<int>(length), keyword);
+    return false;
+  }
+}
+
 bool IONAME(SetEncoding)(
     Cookie cookie, const char *keyword, std::size_t length) {
   IoStatementState &io{*cookie};
@@ -818,21 +836,27 @@ bool IONAME(OutputDescriptor)(Cookie cookie, const Descriptor &) {
   io.GetIoErrorHandler().Crash("OutputDescriptor: not yet implemented"); // TODO
 }
 
-bool IONAME(OutputUnformattedBlock)(
-    Cookie cookie, const char *x, std::size_t length) {
+bool IONAME(InputDescriptor)(Cookie cookie, const Descriptor &) {
+  IoStatementState &io{*cookie};
+  io.GetIoErrorHandler().Crash("InputDescriptor: not yet implemented"); // TODO
+}
+
+bool IONAME(OutputUnformattedBlock)(Cookie cookie, const char *x,
+    std::size_t length, std::size_t elementBytes) {
   IoStatementState &io{*cookie};
   if (auto *unf{io.get_if<UnformattedIoStatementState<Direction::Output>>()}) {
-    return unf->Emit(x, length);
+    return unf->Emit(x, length, elementBytes);
   }
   io.GetIoErrorHandler().Crash("OutputUnformattedBlock() called for an I/O "
                                "statement that is not unformatted output");
   return false;
 }
 
-bool IONAME(InputUnformattedBlock)(Cookie cookie, char *x, std::size_t length) {
+bool IONAME(InputUnformattedBlock)(
+    Cookie cookie, char *x, std::size_t length, std::size_t elementBytes) {
   IoStatementState &io{*cookie};
   if (auto *unf{io.get_if<UnformattedIoStatementState<Direction::Input>>()}) {
-    return unf->Receive(x, length);
+    return unf->Receive(x, length, elementBytes);
   }
   io.GetIoErrorHandler().Crash("InputUnformattedBlock() called for an I/O "
                                "statement that is not unformatted output");

diff  --git a/flang/runtime/io-api.h b/flang/runtime/io-api.h
index 6b394dbdec02..a540076c2d15 100644
--- a/flang/runtime/io-api.h
+++ b/flang/runtime/io-api.h
@@ -211,8 +211,10 @@ bool IONAME(SetSign)(Cookie, const char *, std::size_t);
 // and avoid the following items when they might crash.
 bool IONAME(OutputDescriptor)(Cookie, const Descriptor &);
 bool IONAME(InputDescriptor)(Cookie, const Descriptor &);
-bool IONAME(OutputUnformattedBlock)(Cookie, const char *, std::size_t);
-bool IONAME(InputUnformattedBlock)(Cookie, char *, std::size_t);
+bool IONAME(OutputUnformattedBlock)(
+    Cookie, const char *, std::size_t, std::size_t elementBytes);
+bool IONAME(InputUnformattedBlock)(
+    Cookie, char *, std::size_t, std::size_t elementBytes);
 bool IONAME(OutputInteger64)(Cookie, std::int64_t);
 bool IONAME(InputInteger)(Cookie, std::int64_t &, int kind = 8);
 bool IONAME(OutputReal32)(Cookie, float);
@@ -236,6 +238,8 @@ bool IONAME(SetAccess)(Cookie, const char *, std::size_t);
 bool IONAME(SetAction)(Cookie, const char *, std::size_t);
 // ASYNCHRONOUS=YES, NO
 bool IONAME(SetAsynchronous)(Cookie, const char *, std::size_t);
+// CONVERT=NATIVE, LITTLE_ENDIAN, BIG_ENDIAN, or SWAP
+bool IONAME(SetConvert)(Cookie, const char *, std::size_t);
 // ENCODING=UTF-8, DEFAULT
 bool IONAME(SetEncoding)(Cookie, const char *, std::size_t);
 // FORM=FORMATTED, UNFORMATTED

diff  --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp
index 9e89e0c28816..95c0b9b8e166 100644
--- a/flang/runtime/io-stmt.cpp
+++ b/flang/runtime/io-stmt.cpp
@@ -38,7 +38,7 @@ InternalIoStatementState<DIR, CHAR>::InternalIoStatementState(
 
 template <Direction DIR, typename CHAR>
 bool InternalIoStatementState<DIR, CHAR>::Emit(
-    const CharType *data, std::size_t chars) {
+    const CharType *data, std::size_t chars, std::size_t /*elementBytes*/) {
   if constexpr (DIR == Direction::Input) {
     Crash("InternalIoStatementState<Direction::Input>::Emit() called");
     return false;
@@ -167,7 +167,7 @@ int OpenStatementState::EndIoStatement() {
                 "than 'OLD'");
   }
   unit().OpenUnit(status_.value_or(OpenStatus::Unknown), action_, position_,
-      std::move(path_), pathLength_, *this);
+      std::move(path_), pathLength_, convert_, *this);
   return ExternalIoStatementBase::EndIoStatement();
 }
 
@@ -195,11 +195,12 @@ template <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {
 }
 
 template <Direction DIR>
-bool ExternalIoStatementState<DIR>::Emit(const char *data, std::size_t chars) {
+bool ExternalIoStatementState<DIR>::Emit(
+    const char *data, std::size_t bytes, std::size_t elementBytes) {
   if constexpr (DIR == Direction::Input) {
     Crash("ExternalIoStatementState::Emit(char) called for input statement");
   }
-  return unit().Emit(data, chars * sizeof(*data), *this);
+  return unit().Emit(data, bytes, elementBytes, *this);
 }
 
 template <Direction DIR>
@@ -210,8 +211,8 @@ bool ExternalIoStatementState<DIR>::Emit(
         "ExternalIoStatementState::Emit(char16_t) called for input statement");
   }
   // TODO: UTF-8 encoding
-  return unit().Emit(
-      reinterpret_cast<const char *>(data), chars * sizeof(*data), *this);
+  return unit().Emit(reinterpret_cast<const char *>(data), chars * sizeof *data,
+      static_cast<int>(sizeof *data), *this);
 }
 
 template <Direction DIR>
@@ -222,8 +223,8 @@ bool ExternalIoStatementState<DIR>::Emit(
         "ExternalIoStatementState::Emit(char32_t) called for input statement");
   }
   // TODO: UTF-8 encoding
-  return unit().Emit(
-      reinterpret_cast<const char *>(data), chars * sizeof(*data), *this);
+  return unit().Emit(reinterpret_cast<const char *>(data), chars * sizeof *data,
+      static_cast<int>(sizeof *data), *this);
 }
 
 template <Direction DIR>
@@ -277,8 +278,10 @@ std::optional<DataEdit> IoStatementState::GetNextDataEdit(int n) {
       [&](auto &x) { return x.get().GetNextDataEdit(*this, n); }, u_);
 }
 
-bool IoStatementState::Emit(const char *data, std::size_t n) {
-  return std::visit([=](auto &x) { return x.get().Emit(data, n); }, u_);
+bool IoStatementState::Emit(
+    const char *data, std::size_t n, std::size_t elementBytes) {
+  return std::visit(
+      [=](auto &x) { return x.get().Emit(data, n, elementBytes); }, u_);
 }
 
 std::optional<char32_t> IoStatementState::GetCurrentChar() {
@@ -576,12 +579,23 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
 }
 
 template <Direction DIR>
-bool UnformattedIoStatementState<DIR>::Receive(char *data, std::size_t bytes) {
+bool UnformattedIoStatementState<DIR>::Receive(
+    char *data, std::size_t bytes, std::size_t elementBytes) {
   if constexpr (DIR == Direction::Output) {
     this->Crash(
         "UnformattedIoStatementState::Receive() called for output statement");
   }
-  return this->unit().Receive(data, bytes, *this);
+  return this->unit().Receive(data, bytes, elementBytes, *this);
+}
+
+template <Direction DIR>
+bool UnformattedIoStatementState<DIR>::Emit(
+    const char *data, std::size_t bytes, std::size_t elementBytes) {
+  if constexpr (DIR == Direction::Input) {
+    this->Crash(
+        "UnformattedIoStatementState::Emit() called for input statement");
+  }
+  return ExternalIoStatementState<DIR>::Emit(data, bytes, elementBytes);
 }
 
 template <Direction DIR>

diff  --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h
index da58769ef114..755e5946ff3b 100644
--- a/flang/runtime/io-stmt.h
+++ b/flang/runtime/io-stmt.h
@@ -49,7 +49,7 @@ class IoStatementState {
   // This design avoids virtual member functions and function pointers,
   // which may not have good support in some runtime environments.
   std::optional<DataEdit> GetNextDataEdit(int = 1);
-  bool Emit(const char *, std::size_t);
+  bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
   std::optional<char32_t> GetCurrentChar(); // vacant after end of record
   bool AdvanceRecord(int = 1);
   void BackspaceRecord();
@@ -159,7 +159,8 @@ class InternalIoStatementState : public IoStatementBase,
   InternalIoStatementState(
       const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0);
   int EndIoStatement();
-  bool Emit(const CharType *, std::size_t chars /* not bytes */);
+  bool Emit(const CharType *, std::size_t chars /* not necessarily bytes */,
+      std::size_t elementBytes = 0);
   std::optional<char32_t> GetCurrentChar();
   bool AdvanceRecord(int = 1);
   void BackspaceRecord();
@@ -238,7 +239,7 @@ class ExternalIoStatementState : public ExternalIoStatementBase,
 public:
   using ExternalIoStatementBase::ExternalIoStatementBase;
   int EndIoStatement();
-  bool Emit(const char *, std::size_t);
+  bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
   bool Emit(const char16_t *, std::size_t chars /* not bytes */);
   bool Emit(const char32_t *, std::size_t chars /* not bytes */);
   std::optional<char32_t> GetCurrentChar();
@@ -283,7 +284,8 @@ template <Direction DIR>
 class UnformattedIoStatementState : public ExternalIoStatementState<DIR> {
 public:
   using ExternalIoStatementState<DIR>::ExternalIoStatementState;
-  bool Receive(char *, std::size_t);
+  bool Receive(char *, std::size_t, std::size_t elementBytes = 0);
+  bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
   int EndIoStatement();
 };
 
@@ -298,6 +300,7 @@ class OpenStatementState : public ExternalIoStatementBase {
   void set_path(const char *, std::size_t, int kind); // FILE=
   void set_position(Position position) { position_ = position; } // POSITION=
   void set_action(Action action) { action_ = action; } // ACTION=
+  void set_convert(Convert convert) { convert_ = convert; } // CONVERT=
   int EndIoStatement();
 
 private:
@@ -305,6 +308,7 @@ class OpenStatementState : public ExternalIoStatementBase {
   std::optional<OpenStatus> status_;
   Position position_{Position::AsIs};
   std::optional<Action> action_;
+  Convert convert_{Convert::Native};
   OwningPtr<char> path_;
   std::size_t pathLength_;
 };

diff  --git a/flang/runtime/main.cpp b/flang/runtime/main.cpp
index 5de2e6434abb..d01a8bffc7f2 100644
--- a/flang/runtime/main.cpp
+++ b/flang/runtime/main.cpp
@@ -33,4 +33,14 @@ void RTNAME(ProgramStart)(int argc, const char *argv[], const char *envp[]) {
   ConfigureFloatingPoint();
   // I/O is initialized on demand so that it works for non-Fortran main().
 }
+
+void RTNAME(ByteswapOption)() {
+  if (Fortran::runtime::executionEnvironment.conversion ==
+      Fortran::runtime::Convert::Unknown) {
+    // The environment variable overrides the command-line option;
+    // either of them take precedence over explicit OPEN(CONVERT=) specifiers.
+    Fortran::runtime::executionEnvironment.conversion =
+        Fortran::runtime::Convert::Swap;
+  }
+}
 }

diff  --git a/flang/runtime/main.h b/flang/runtime/main.h
index 31ef8f8f4446..a69eead6bb96 100644
--- a/flang/runtime/main.h
+++ b/flang/runtime/main.h
@@ -14,6 +14,7 @@
 
 FORTRAN_EXTERN_C_BEGIN
 void RTNAME(ProgramStart)(int, const char *[], const char *[]);
+void RTNAME(ByteswapOption)(); // -byteswapio
 FORTRAN_EXTERN_C_END
 
 #endif // FORTRAN_RUNTIME_MAIN_H_

diff  --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp
index c6af53e6ec22..a4c69df8d6a9 100644
--- a/flang/runtime/unit.cpp
+++ b/flang/runtime/unit.cpp
@@ -7,10 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "unit.h"
+#include "environment.h"
 #include "io-error.h"
 #include "lock.h"
 #include "unit-map.h"
 #include <cstdio>
+#include <utility>
 
 namespace Fortran::runtime::io {
 
@@ -65,7 +67,7 @@ ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(
     result.OpenUnit(
         dir == Direction::Input ? OpenStatus::Old : OpenStatus::Replace,
         Action::ReadWrite, Position::Rewind, std::move(path),
-        std::strlen(path.get()), handler);
+        std::strlen(path.get()), Convert::Native, handler);
     result.isUnformatted = isUnformatted;
   }
   return result;
@@ -90,7 +92,13 @@ int ExternalFileUnit::NewUnit(const Terminator &terminator) {
 
 void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional<Action> action,
     Position position, OwningPtr<char> &&newPath, std::size_t newPathLength,
-    IoErrorHandler &handler) {
+    Convert convert, IoErrorHandler &handler) {
+  if (executionEnvironment.conversion != Convert::Unknown) {
+    convert = executionEnvironment.conversion;
+  }
+  swapEndianness_ = convert == Convert::Swap ||
+      (convert == Convert::LittleEndian && !isHostLittleEndian) ||
+      (convert == Convert::BigEndian && isHostLittleEndian);
   if (IsOpen()) {
     if (status == OpenStatus::Old &&
         (!newPath.get() ||
@@ -213,8 +221,20 @@ void ExternalFileUnit::FlushAll(IoErrorHandler &handler) {
   }
 }
 
-bool ExternalFileUnit::Emit(
-    const char *data, std::size_t bytes, IoErrorHandler &handler) {
+static void SwapEndianness(
+    char *data, std::size_t bytes, std::size_t elementBytes) {
+  if (elementBytes > 1) {
+    auto half{elementBytes >> 1};
+    for (std::size_t j{0}; j + elementBytes <= bytes; j += elementBytes) {
+      for (std::size_t k{0}; k < half; ++k) {
+        std::swap(data[j + k], data[j + elementBytes - 1 - k]);
+      }
+    }
+  }
+}
+
+bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
+    std::size_t elementBytes, IoErrorHandler &handler) {
   auto furthestAfter{std::max(furthestPositionInRecord,
       positionInRecord + static_cast<std::int64_t>(bytes))};
   if (furthestAfter > recordLength.value_or(furthestAfter)) {
@@ -230,14 +250,18 @@ bool ExternalFileUnit::Emit(
     std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ',
         positionInRecord - furthestPositionInRecord);
   }
-  std::memcpy(Frame() + recordOffsetInFrame_ + positionInRecord, data, bytes);
+  char *to{Frame() + recordOffsetInFrame_ + positionInRecord};
+  std::memcpy(to, data, bytes);
+  if (swapEndianness_) {
+    SwapEndianness(to, bytes, elementBytes);
+  }
   positionInRecord += bytes;
   furthestPositionInRecord = furthestAfter;
   return true;
 }
 
-bool ExternalFileUnit::Receive(
-    char *data, std::size_t bytes, IoErrorHandler &handler) {
+bool ExternalFileUnit::Receive(char *data, std::size_t bytes,
+    std::size_t elementBytes, IoErrorHandler &handler) {
   RUNTIME_CHECK(handler, direction_ == Direction::Input);
   auto furthestAfter{std::max(furthestPositionInRecord,
       positionInRecord + static_cast<std::int64_t>(bytes))};
@@ -252,6 +276,9 @@ bool ExternalFileUnit::Receive(
   auto got{ReadFrame(frameOffsetInFile_, need, handler)};
   if (got >= need) {
     std::memcpy(data, Frame() + recordOffsetInFrame_ + positionInRecord, bytes);
+    if (swapEndianness_) {
+      SwapEndianness(data, bytes, elementBytes);
+    }
     positionInRecord += bytes;
     furthestPositionInRecord = furthestAfter;
     return true;
@@ -365,7 +392,7 @@ bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
         }
       } else {
         positionInRecord = furthestPositionInRecord;
-        ok &= Emit("\n", 1, handler); // TODO: Windows CR+LF
+        ok &= Emit("\n", 1, 1, handler); // TODO: Windows CR+LF
       }
     }
     frameOffsetInFile_ +=

diff  --git a/flang/runtime/unit.h b/flang/runtime/unit.h
index d2d2dce035f1..f94e4229cd4c 100644
--- a/flang/runtime/unit.h
+++ b/flang/runtime/unit.h
@@ -49,7 +49,8 @@ class ExternalFileUnit : public ConnectionState,
   static void FlushAll(IoErrorHandler &);
 
   void OpenUnit(OpenStatus, std::optional<Action>, Position,
-      OwningPtr<char> &&path, std::size_t pathLength, IoErrorHandler &);
+      OwningPtr<char> &&path, std::size_t pathLength, Convert,
+      IoErrorHandler &);
   void CloseUnit(CloseStatus, IoErrorHandler &);
   void DestroyClosed();
 
@@ -67,8 +68,9 @@ class ExternalFileUnit : public ConnectionState,
     return *io_;
   }
 
-  bool Emit(const char *, std::size_t, IoErrorHandler &);
-  bool Receive(char *, std::size_t, IoErrorHandler &);
+  bool Emit(
+      const char *, std::size_t, std::size_t elementBytes, IoErrorHandler &);
+  bool Receive(char *, std::size_t, std::size_t elementBytes, IoErrorHandler &);
   std::optional<char32_t> GetCurrentChar(IoErrorHandler &);
   void SetLeftTabLimit();
   void BeginReadingRecord(IoErrorHandler &);
@@ -122,6 +124,8 @@ class ExternalFileUnit : public ConnectionState,
   // manage the frame and the current record therein separately.
   std::int64_t frameOffsetInFile_{0};
   std::size_t recordOffsetInFrame_{0}; // of currentRecordNumber
+
+  bool swapEndianness_{false};
 };
 
 } // namespace Fortran::runtime::io

diff  --git a/flang/tools/f18/f18.cpp b/flang/tools/f18/f18.cpp
index 574a37074e52..bcafb0d53cc7 100644
--- a/flang/tools/f18/f18.cpp
+++ b/flang/tools/f18/f18.cpp
@@ -88,6 +88,7 @@ struct DriverOptions {
   bool forcedForm{false}; // -Mfixed or -Mfree appeared
   bool warnOnNonstandardUsage{false}; // -Mstandard
   bool warningsAreErrors{false}; // -Werror
+  bool byteswapio{false}; // -byteswapio
   Fortran::parser::Encoding encoding{Fortran::parser::Encoding::UTF_8};
   bool parseOnly{false};
   bool dumpProvenance{false};
@@ -585,6 +586,8 @@ int main(int argc, char *const argv[]) {
       driver.getDefinitionArgs = {arguments[0], arguments[1], arguments[2]};
     } else if (arg == "-fget-symbols-sources") {
       driver.getSymbolsSources = true;
+    } else if (arg == "-byteswapio") {
+      driver.byteswapio = true; // TODO: Pass to lowering, generate call
     } else if (arg == "-help" || arg == "--help" || arg == "-?") {
       llvm::errs()
           << "f18 options:\n"

diff  --git a/flang/unittests/Runtime/external-io.cpp b/flang/unittests/Runtime/external-io.cpp
index 63b910b23956..6806ce94ae1e 100644
--- a/flang/unittests/Runtime/external-io.cpp
+++ b/flang/unittests/Runtime/external-io.cpp
@@ -35,7 +35,7 @@ void TestDirectUnformatted() {
     IONAME(SetRec)(io, j) || (Fail() << "SetRec(" << j << ')', 0);
     buffer = j;
     IONAME(OutputUnformattedBlock)
-    (io, reinterpret_cast<const char *>(&buffer), recl) ||
+    (io, reinterpret_cast<const char *>(&buffer), recl, recl) ||
         (Fail() << "OutputUnformattedBlock()", 0);
     IONAME(EndIoStatement)
     (io) == IostatOk ||
@@ -47,7 +47,7 @@ void TestDirectUnformatted() {
     IONAME(SetRec)
     (io, j) || (Fail() << "SetRec(" << j << ')', 0);
     IONAME(InputUnformattedBlock)
-    (io, reinterpret_cast<char *>(&buffer), recl) ||
+    (io, reinterpret_cast<char *>(&buffer), recl, recl) ||
         (Fail() << "InputUnformattedBlock()", 0);
     IONAME(EndIoStatement)
     (io) == IostatOk ||
@@ -65,6 +65,72 @@ void TestDirectUnformatted() {
   llvm::errs() << "end TestDirectUnformatted()\n";
 }
 
+void TestDirectUnformattedSwapped() {
+  llvm::errs() << "begin TestDirectUnformattedSwapped()\n";
+  // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',&
+  //   FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE')
+  auto io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)};
+  IONAME(SetAccess)(io, "DIRECT", 6) || (Fail() << "SetAccess(DIRECT)", 0);
+  IONAME(SetAction)
+  (io, "READWRITE", 9) || (Fail() << "SetAction(READWRITE)", 0);
+  IONAME(SetForm)
+  (io, "UNFORMATTED", 11) || (Fail() << "SetForm(UNFORMATTED)", 0);
+  IONAME(SetConvert)
+  (io, "NATIVE", 6) || (Fail() << "SetConvert(NATIVE)", 0);
+  std::int64_t buffer;
+  static constexpr std::size_t recl{sizeof buffer};
+  IONAME(SetRecl)(io, recl) || (Fail() << "SetRecl()", 0);
+  IONAME(SetStatus)(io, "SCRATCH", 7) || (Fail() << "SetStatus(SCRATCH)", 0);
+  int unit{-1};
+  IONAME(GetNewUnit)(io, unit) || (Fail() << "GetNewUnit()", 0);
+  llvm::errs() << "unit=" << unit << '\n';
+  IONAME(EndIoStatement)
+  (io) == IostatOk || (Fail() << "EndIoStatement() for OpenNewUnit", 0);
+  static constexpr int records{10};
+  for (int j{1}; j <= records; ++j) {
+    // WRITE(UNIT=unit,REC=j) j
+    io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
+    IONAME(SetRec)(io, j) || (Fail() << "SetRec(" << j << ')', 0);
+    buffer = j;
+    IONAME(OutputUnformattedBlock)
+    (io, reinterpret_cast<const char *>(&buffer), recl, recl) ||
+        (Fail() << "OutputUnformattedBlock()", 0);
+    IONAME(EndIoStatement)
+    (io) == IostatOk ||
+        (Fail() << "EndIoStatement() for OutputUnformattedBlock", 0);
+  }
+  // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP')
+  io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__);
+  IONAME(SetStatus)(io, "OLD", 3) || (Fail() << "SetStatus(OLD)", 0);
+  IONAME(SetConvert)
+  (io, "SWAP", 4) || (Fail() << "SetConvert(SWAP)", 0);
+  IONAME(EndIoStatement)
+  (io) == IostatOk || (Fail() << "EndIoStatement() for OpenUnit", 0);
+  for (int j{records}; j >= 1; --j) {
+    // READ(UNIT=unit,REC=j) n
+    io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
+    IONAME(SetRec)
+    (io, j) || (Fail() << "SetRec(" << j << ')', 0);
+    IONAME(InputUnformattedBlock)
+    (io, reinterpret_cast<char *>(&buffer), recl, recl) ||
+        (Fail() << "InputUnformattedBlock()", 0);
+    IONAME(EndIoStatement)
+    (io) == IostatOk ||
+        (Fail() << "EndIoStatement() for InputUnformattedBlock", 0);
+    if (buffer >> 56 != j) {
+      Fail() << "Read back " << (buffer >> 56)
+             << " from direct unformatted record " << j << ", expected " << j
+             << '\n';
+    }
+  }
+  // CLOSE(UNIT=unit,STATUS='DELETE')
+  io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
+  IONAME(SetStatus)(io, "DELETE", 6) || (Fail() << "SetStatus(DELETE)", 0);
+  IONAME(EndIoStatement)
+  (io) == IostatOk || (Fail() << "EndIoStatement() for Close", 0);
+  llvm::errs() << "end TestDirectUnformatted()\n";
+}
+
 void TestSequentialFixedUnformatted() {
   llvm::errs() << "begin TestSequentialFixedUnformatted()\n";
   // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',&
@@ -91,7 +157,7 @@ void TestSequentialFixedUnformatted() {
     io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
     buffer = j;
     IONAME(OutputUnformattedBlock)
-    (io, reinterpret_cast<const char *>(&buffer), recl) ||
+    (io, reinterpret_cast<const char *>(&buffer), recl, recl) ||
         (Fail() << "OutputUnformattedBlock()", 0);
     IONAME(EndIoStatement)
     (io) == IostatOk ||
@@ -105,7 +171,7 @@ void TestSequentialFixedUnformatted() {
     // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
     IONAME(InputUnformattedBlock)
-    (io, reinterpret_cast<char *>(&buffer), recl) ||
+    (io, reinterpret_cast<char *>(&buffer), recl, recl) ||
         (Fail() << "InputUnformattedBlock()", 0);
     IONAME(EndIoStatement)
     (io) == IostatOk ||
@@ -125,7 +191,7 @@ void TestSequentialFixedUnformatted() {
     // READ(UNIT=unit) n
     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
     IONAME(InputUnformattedBlock)
-    (io, reinterpret_cast<char *>(&buffer), recl) ||
+    (io, reinterpret_cast<char *>(&buffer), recl, recl) ||
         (Fail() << "InputUnformattedBlock()", 0);
     IONAME(EndIoStatement)
     (io) == IostatOk ||
@@ -175,7 +241,8 @@ void TestSequentialVariableUnformatted() {
     // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO
     io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__);
     IONAME(OutputUnformattedBlock)
-    (io, reinterpret_cast<const char *>(&buffer), j * sizeof *buffer) ||
+    (io, reinterpret_cast<const char *>(&buffer), j * sizeof *buffer,
+        sizeof *buffer) ||
         (Fail() << "OutputUnformattedBlock()", 0);
     IONAME(EndIoStatement)
     (io) == IostatOk ||
@@ -189,7 +256,8 @@ void TestSequentialVariableUnformatted() {
     // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO
     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
     IONAME(InputUnformattedBlock)
-    (io, reinterpret_cast<char *>(&buffer), j * sizeof *buffer) ||
+    (io, reinterpret_cast<char *>(&buffer), j * sizeof *buffer,
+        sizeof *buffer) ||
         (Fail() << "InputUnformattedBlock()", 0);
     IONAME(EndIoStatement)
     (io) == IostatOk ||
@@ -211,7 +279,8 @@ void TestSequentialVariableUnformatted() {
     // READ(unit=unit) n; check
     io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__);
     IONAME(InputUnformattedBlock)
-    (io, reinterpret_cast<char *>(&buffer), j * sizeof *buffer) ||
+    (io, reinterpret_cast<char *>(&buffer), j * sizeof *buffer,
+        sizeof *buffer) ||
         (Fail() << "InputUnformattedBlock()", 0);
     IONAME(EndIoStatement)
     (io) == IostatOk ||
@@ -390,6 +459,7 @@ void TestStreamUnformatted() {
 int main() {
   StartTests();
   TestDirectUnformatted();
+  TestDirectUnformattedSwapped();
   TestSequentialFixedUnformatted();
   TestSequentialVariableUnformatted();
   TestDirectFormatted();


        


More information about the flang-commits mailing list