[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