diff --git a/flang/runtime/environment.h b/flang/runtime/environment.h --- 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 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 GetConvertFromString(const char *, std::size_t); + struct ExecutionEnvironment { void Configure(int argc, const char *argv[], const char *envp[]); @@ -20,6 +35,7 @@ const char **envp; int listDirectedOutputLineLengthLimit; enum decimal::FortranRounding defaultOutputRoundingMode; + Convert conversion; }; extern ExecutionEnvironment executionEnvironment; } // namespace Fortran::runtime diff --git a/flang/runtime/environment.cpp b/flang/runtime/environment.cpp --- a/flang/runtime/environment.cpp +++ b/flang/runtime/environment.cpp @@ -7,13 +7,35 @@ //===----------------------------------------------------------------------===// #include "environment.h" +#include "tools.h" #include #include +#include #include namespace Fortran::runtime { + ExecutionEnvironment executionEnvironment; +std::optional 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 @@ 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 @@ } } + 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/format.h b/flang/runtime/format.h --- a/flang/runtime/format.h +++ b/flang/runtime/format.h @@ -63,7 +63,7 @@ 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 GetCurrentChar(); diff --git a/flang/runtime/format.cpp b/flang/runtime/format.cpp --- a/flang/runtime/format.cpp +++ b/flang/runtime/format.cpp @@ -15,7 +15,8 @@ "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/io-api.h b/flang/runtime/io-api.h --- a/flang/runtime/io-api.h +++ b/flang/runtime/io-api.h @@ -211,8 +211,10 @@ // 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(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-api.cpp b/flang/runtime/io-api.cpp --- a/flang/runtime/io-api.cpp +++ b/flang/runtime/io-api.cpp @@ -614,6 +614,24 @@ } } +bool IONAME(SetConvert)( + Cookie cookie, const char *keyword, std::size_t length) { + IoStatementState &io{*cookie}; + auto *open{io.get_if()}; + 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(length), keyword); + return false; + } +} + bool IONAME(SetEncoding)( Cookie cookie, const char *keyword, std::size_t length) { IoStatementState &io{*cookie}; @@ -818,21 +836,27 @@ 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>()}) { - 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>()}) { - 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-stmt.h b/flang/runtime/io-stmt.h --- a/flang/runtime/io-stmt.h +++ b/flang/runtime/io-stmt.h @@ -49,7 +49,7 @@ // This design avoids virtual member functions and function pointers, // which may not have good support in some runtime environments. std::optional GetNextDataEdit(int = 1); - bool Emit(const char *, std::size_t); + bool Emit(const char *, std::size_t, std::size_t elementBytes = 0); std::optional GetCurrentChar(); // vacant after end of record bool AdvanceRecord(int = 1); void BackspaceRecord(); @@ -159,7 +159,8 @@ 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 GetCurrentChar(); bool AdvanceRecord(int = 1); void BackspaceRecord(); @@ -238,7 +239,7 @@ 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 GetCurrentChar(); @@ -283,7 +284,8 @@ class UnformattedIoStatementState : public ExternalIoStatementState { public: using ExternalIoStatementState::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 @@ 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 @@ std::optional status_; Position position_{Position::AsIs}; std::optional action_; + Convert convert_{Convert::Native}; OwningPtr path_; std::size_t pathLength_; }; diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -38,7 +38,7 @@ template bool InternalIoStatementState::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::Emit() called"); return false; @@ -167,7 +167,7 @@ "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 -bool ExternalIoStatementState::Emit(const char *data, std::size_t chars) { +bool ExternalIoStatementState::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 @@ -210,8 +211,8 @@ "ExternalIoStatementState::Emit(char16_t) called for input statement"); } // TODO: UTF-8 encoding - return unit().Emit( - reinterpret_cast(data), chars * sizeof(*data), *this); + return unit().Emit(reinterpret_cast(data), chars * sizeof *data, + static_cast(sizeof *data), *this); } template @@ -222,8 +223,8 @@ "ExternalIoStatementState::Emit(char32_t) called for input statement"); } // TODO: UTF-8 encoding - return unit().Emit( - reinterpret_cast(data), chars * sizeof(*data), *this); + return unit().Emit(reinterpret_cast(data), chars * sizeof *data, + static_cast(sizeof *data), *this); } template @@ -277,8 +278,10 @@ [&](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 IoStatementState::GetCurrentChar() { @@ -576,12 +579,23 @@ } template -bool UnformattedIoStatementState::Receive(char *data, std::size_t bytes) { +bool UnformattedIoStatementState::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 +bool UnformattedIoStatementState::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::Emit(data, bytes, elementBytes); } template diff --git a/flang/runtime/main.h b/flang/runtime/main.h --- 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/main.cpp b/flang/runtime/main.cpp --- a/flang/runtime/main.cpp +++ b/flang/runtime/main.cpp @@ -33,4 +33,14 @@ 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/unit.h b/flang/runtime/unit.h --- a/flang/runtime/unit.h +++ b/flang/runtime/unit.h @@ -49,7 +49,8 @@ static void FlushAll(IoErrorHandler &); void OpenUnit(OpenStatus, std::optional, Position, - OwningPtr &&path, std::size_t pathLength, IoErrorHandler &); + OwningPtr &&path, std::size_t pathLength, Convert, + IoErrorHandler &); void CloseUnit(CloseStatus, IoErrorHandler &); void DestroyClosed(); @@ -67,8 +68,9 @@ 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 GetCurrentChar(IoErrorHandler &); void SetLeftTabLimit(); void BeginReadingRecord(IoErrorHandler &); @@ -122,6 +124,8 @@ // 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/runtime/unit.cpp b/flang/runtime/unit.cpp --- 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 +#include namespace Fortran::runtime::io { @@ -65,7 +67,7 @@ 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 @@ void ExternalFileUnit::OpenUnit(OpenStatus status, std::optional action, Position position, OwningPtr &&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 @@ } } -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(bytes))}; if (furthestAfter > recordLength.value_or(furthestAfter)) { @@ -230,14 +250,18 @@ 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(bytes))}; @@ -252,6 +276,9 @@ 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 @@ } } 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/tools/f18/f18.cpp b/flang/tools/f18/f18.cpp --- a/flang/tools/f18/f18.cpp +++ b/flang/tools/f18/f18.cpp @@ -88,6 +88,7 @@ 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 @@ 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 --- a/flang/unittests/Runtime/external-io.cpp +++ b/flang/unittests/Runtime/external-io.cpp @@ -35,7 +35,7 @@ IONAME(SetRec)(io, j) || (Fail() << "SetRec(" << j << ')', 0); buffer = j; IONAME(OutputUnformattedBlock) - (io, reinterpret_cast(&buffer), recl) || + (io, reinterpret_cast(&buffer), recl, recl) || (Fail() << "OutputUnformattedBlock()", 0); IONAME(EndIoStatement) (io) == IostatOk || @@ -47,7 +47,7 @@ IONAME(SetRec) (io, j) || (Fail() << "SetRec(" << j << ')', 0); IONAME(InputUnformattedBlock) - (io, reinterpret_cast(&buffer), recl) || + (io, reinterpret_cast(&buffer), recl, recl) || (Fail() << "InputUnformattedBlock()", 0); IONAME(EndIoStatement) (io) == IostatOk || @@ -65,6 +65,72 @@ 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(&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(&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 @@ io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); buffer = j; IONAME(OutputUnformattedBlock) - (io, reinterpret_cast(&buffer), recl) || + (io, reinterpret_cast(&buffer), recl, recl) || (Fail() << "OutputUnformattedBlock()", 0); IONAME(EndIoStatement) (io) == IostatOk || @@ -105,7 +171,7 @@ // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); IONAME(InputUnformattedBlock) - (io, reinterpret_cast(&buffer), recl) || + (io, reinterpret_cast(&buffer), recl, recl) || (Fail() << "InputUnformattedBlock()", 0); IONAME(EndIoStatement) (io) == IostatOk || @@ -125,7 +191,7 @@ // READ(UNIT=unit) n io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); IONAME(InputUnformattedBlock) - (io, reinterpret_cast(&buffer), recl) || + (io, reinterpret_cast(&buffer), recl, recl) || (Fail() << "InputUnformattedBlock()", 0); IONAME(EndIoStatement) (io) == IostatOk || @@ -175,7 +241,8 @@ // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); IONAME(OutputUnformattedBlock) - (io, reinterpret_cast(&buffer), j * sizeof *buffer) || + (io, reinterpret_cast(&buffer), j * sizeof *buffer, + sizeof *buffer) || (Fail() << "OutputUnformattedBlock()", 0); IONAME(EndIoStatement) (io) == IostatOk || @@ -189,7 +256,8 @@ // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); IONAME(InputUnformattedBlock) - (io, reinterpret_cast(&buffer), j * sizeof *buffer) || + (io, reinterpret_cast(&buffer), j * sizeof *buffer, + sizeof *buffer) || (Fail() << "InputUnformattedBlock()", 0); IONAME(EndIoStatement) (io) == IostatOk || @@ -211,7 +279,8 @@ // READ(unit=unit) n; check io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); IONAME(InputUnformattedBlock) - (io, reinterpret_cast(&buffer), j * sizeof *buffer) || + (io, reinterpret_cast(&buffer), j * sizeof *buffer, + sizeof *buffer) || (Fail() << "InputUnformattedBlock()", 0); IONAME(EndIoStatement) (io) == IostatOk || @@ -390,6 +459,7 @@ int main() { StartTests(); TestDirectUnformatted(); + TestDirectUnformattedSwapped(); TestSequentialFixedUnformatted(); TestSequentialVariableUnformatted(); TestDirectFormatted();