From 34c10ef47163d83cd06938d310db5e86736178e8 Mon Sep 17 00:00:00 2001 From: Peter Klausler Date: Fri, 21 Mar 2025 09:54:36 -0700 Subject: [PATCH] [flang][NFC] Restore I/O runtime API header name flang/include/flang/Runtime/io-api.h was changed into io-api-consts.h, then wrapped into a new io-api.h that includes io-api-consts.h, does some redundant includes and declarations, and then declares the prototype of one function, InquiryKeywordHashDecode. Make that function static in io-stmt.cpp prior to its sole call site, then undo the renaming, to reduce confusion and redundancy. --- flang-rt/lib/runtime/io-api.cpp | 17 - flang-rt/lib/runtime/io-stmt.cpp | 17 + flang-rt/unittests/Runtime/ExternalIOTest.cpp | 2 +- flang-rt/unittests/Runtime/ListInputTest.cpp | 2 +- .../unittests/Runtime/LogicalFormatTest.cpp | 2 +- flang-rt/unittests/Runtime/Namelist.cpp | 2 +- .../unittests/Runtime/NumericalFormatTest.cpp | 2 +- .../unittests/Runtime/RuntimeCrashTest.cpp | 2 +- .../Optimizer/Builder/Runtime/RTBuilder.h | 2 +- flang/include/flang/Runtime/io-api-consts.h | 368 ------------------ flang/include/flang/Runtime/io-api.h | 341 +++++++++++++++- flang/lib/Lower/IO.cpp | 2 +- .../Transforms/GenRuntimeCallsForTest.cpp | 2 +- .../Transforms/SetRuntimeCallAttributes.cpp | 2 +- 14 files changed, 363 insertions(+), 400 deletions(-) delete mode 100644 flang/include/flang/Runtime/io-api-consts.h diff --git a/flang-rt/lib/runtime/io-api.cpp b/flang-rt/lib/runtime/io-api.cpp index 0355734c67fcd..2b7ec9134689b 100644 --- a/flang-rt/lib/runtime/io-api.cpp +++ b/flang-rt/lib/runtime/io-api.cpp @@ -33,23 +33,6 @@ namespace Fortran::runtime::io { RT_EXT_API_GROUP_BEGIN -RT_API_ATTRS const char *InquiryKeywordHashDecode( - char *buffer, std::size_t n, InquiryKeywordHash hash) { - if (n < 1) { - return nullptr; - } - char *p{buffer + n}; - *--p = '\0'; - while (hash > 1) { - if (p < buffer) { - return nullptr; - } - *--p = 'A' + (hash % 26); - hash /= 26; - } - return hash == 1 ? p : nullptr; -} - template RT_API_ATTRS Cookie BeginInternalArrayListIO(const Descriptor &descriptor, void ** /*scratchArea*/, std::size_t /*scratchBytes*/, diff --git a/flang-rt/lib/runtime/io-stmt.cpp b/flang-rt/lib/runtime/io-stmt.cpp index b0823ffd9e703..636351f560b0a 100644 --- a/flang-rt/lib/runtime/io-stmt.cpp +++ b/flang-rt/lib/runtime/io-stmt.cpp @@ -79,6 +79,23 @@ bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t &) { return false; } +RT_API_ATTRS static const char *InquiryKeywordHashDecode( + char *buffer, std::size_t n, InquiryKeywordHash hash) { + if (n < 1) { + return nullptr; + } + char *p{buffer + n}; + *--p = '\0'; + while (hash > 1) { + if (p < buffer) { + return nullptr; + } + *--p = 'A' + (hash % 26); + hash /= 26; + } + return hash == 1 ? p : nullptr; +} + void IoStatementBase::BadInquiryKeywordHashCrash(InquiryKeywordHash inquiry) { char buffer[16]; const char *decode{InquiryKeywordHashDecode(buffer, sizeof buffer, inquiry)}; diff --git a/flang-rt/unittests/Runtime/ExternalIOTest.cpp b/flang-rt/unittests/Runtime/ExternalIOTest.cpp index c83535ca82bd3..3833e48be3dd6 100644 --- a/flang-rt/unittests/Runtime/ExternalIOTest.cpp +++ b/flang-rt/unittests/Runtime/ExternalIOTest.cpp @@ -13,7 +13,7 @@ #include "CrashHandlerFixture.h" #include "gtest/gtest.h" #include "flang-rt/runtime/descriptor.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" #include "flang/Runtime/main.h" #include "flang/Runtime/stop.h" #include "llvm/Support/raw_ostream.h" diff --git a/flang-rt/unittests/Runtime/ListInputTest.cpp b/flang-rt/unittests/Runtime/ListInputTest.cpp index 310c41a5c3fa5..a8f0d4516ceba 100644 --- a/flang-rt/unittests/Runtime/ListInputTest.cpp +++ b/flang-rt/unittests/Runtime/ListInputTest.cpp @@ -9,7 +9,7 @@ #include "CrashHandlerFixture.h" #include "flang-rt/runtime/descriptor.h" #include "flang-rt/runtime/io-error.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" using namespace Fortran::runtime; using namespace Fortran::runtime::io; diff --git a/flang-rt/unittests/Runtime/LogicalFormatTest.cpp b/flang-rt/unittests/Runtime/LogicalFormatTest.cpp index bc933292181c1..03da1f046eb94 100644 --- a/flang-rt/unittests/Runtime/LogicalFormatTest.cpp +++ b/flang-rt/unittests/Runtime/LogicalFormatTest.cpp @@ -8,7 +8,7 @@ #include "CrashHandlerFixture.h" #include "flang-rt/runtime/descriptor.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" #include #include #include diff --git a/flang-rt/unittests/Runtime/Namelist.cpp b/flang-rt/unittests/Runtime/Namelist.cpp index 040dedb8cd47c..ee4018e491c32 100644 --- a/flang-rt/unittests/Runtime/Namelist.cpp +++ b/flang-rt/unittests/Runtime/Namelist.cpp @@ -10,7 +10,7 @@ #include "CrashHandlerFixture.h" #include "tools.h" #include "flang-rt/runtime/descriptor.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" #include #include #include diff --git a/flang-rt/unittests/Runtime/NumericalFormatTest.cpp b/flang-rt/unittests/Runtime/NumericalFormatTest.cpp index 5a8ead48dcef9..36170ea1056fd 100644 --- a/flang-rt/unittests/Runtime/NumericalFormatTest.cpp +++ b/flang-rt/unittests/Runtime/NumericalFormatTest.cpp @@ -8,7 +8,7 @@ #include "CrashHandlerFixture.h" #include "flang-rt/runtime/descriptor.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" #include #include #include diff --git a/flang-rt/unittests/Runtime/RuntimeCrashTest.cpp b/flang-rt/unittests/Runtime/RuntimeCrashTest.cpp index e716dac2d1203..eaaf2a6e81b71 100644 --- a/flang-rt/unittests/Runtime/RuntimeCrashTest.cpp +++ b/flang-rt/unittests/Runtime/RuntimeCrashTest.cpp @@ -13,7 +13,7 @@ #include "CrashHandlerFixture.h" #include "tools.h" #include "flang-rt/runtime/terminator.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" #include "flang/Runtime/transformational.h" #include diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h index 722e9191be728..a93c98f223839 100644 --- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h +++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h @@ -21,7 +21,7 @@ #include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Dialect/FIRDialect.h" #include "flang/Optimizer/Dialect/FIRType.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" #include "flang/Runtime/reduce.h" #include "flang/Support/Fortran.h" #include "mlir/IR/BuiltinTypes.h" diff --git a/flang/include/flang/Runtime/io-api-consts.h b/flang/include/flang/Runtime/io-api-consts.h deleted file mode 100644 index 7ed8bf1489b3c..0000000000000 --- a/flang/include/flang/Runtime/io-api-consts.h +++ /dev/null @@ -1,368 +0,0 @@ -//===-- include/flang/Runtime/io-api-consts.h -------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef FORTRAN_RUNTIME_IO_API_CONSTS_H_ -#define FORTRAN_RUNTIME_IO_API_CONSTS_H_ - -#include "flang/Common/uint128.h" -#include "flang/Runtime/entry-names.h" -#include "flang/Runtime/iostat-consts.h" -#include "flang/Runtime/magic-numbers.h" -#include -#include - -namespace Fortran::runtime { -class Descriptor; -} // namespace Fortran::runtime - -namespace Fortran::runtime::io { - -struct NonTbpDefinedIoTable; -class NamelistGroup; -class IoStatementState; -using Cookie = IoStatementState *; -using ExternalUnit = int; -using AsynchronousId = int; - -static constexpr ExternalUnit DefaultOutputUnit{FORTRAN_DEFAULT_OUTPUT_UNIT}; -static constexpr ExternalUnit DefaultInputUnit{FORTRAN_DEFAULT_INPUT_UNIT}; - -// INQUIRE specifiers are encoded as simple base-26 packings of -// the spellings of their keywords. -using InquiryKeywordHash = std::uint64_t; -constexpr InquiryKeywordHash HashInquiryKeyword(const char *p) { - InquiryKeywordHash hash{1}; - while (char ch{*p++}) { - std::uint64_t letter{0}; - if (ch >= 'a' && ch <= 'z') { - letter = ch - 'a'; - } else { - letter = ch - 'A'; - } - hash = 26 * hash + letter; - } - return hash; -} - -extern "C" { - -#define IONAME(name) RTNAME(io##name) - -#ifndef IODECL -#define IODECL(name) RT_API_ATTRS IONAME(name) -#endif - -#ifndef IODEF -#define IODEF(name) RT_API_ATTRS IONAME(name) -#endif - -// These functions initiate data transfer statements (READ, WRITE, PRINT). -// Example: PRINT *, 666 is implemented as the series of calls: -// Cookie cookie{BeginExternalListOutput(DefaultOutputUnit, -// __FILE__, __LINE__)}; -// OutputInteger32(cookie, 666); -// EndIoStatement(cookie); -// Formatted I/O with explicit formats can supply the format as a -// const char * pointer with a length, or with a descriptor. - -// Internal I/O initiation -// Internal I/O can loan the runtime library an optional block of memory -// in which the library can maintain state across the calls that implement -// the internal transfer; use of these blocks can reduce the need for dynamic -// memory allocation &/or thread-local storage. The block must be sufficiently -// aligned to hold a pointer. -constexpr std::size_t RecommendedInternalIoScratchAreaBytes( - int maxFormatParenthesesNestingDepth) { - return 32 + 8 * maxFormatParenthesesNestingDepth; -} - -// For NAMELIST I/O, use the API for the appropriate form of list-directed -// I/O initiation and configuration, then call OutputNamelist/InputNamelist -// below. - -// Internal I/O to/from character arrays &/or non-default-kind character -// requires a descriptor, which is copied. -Cookie IODECL(BeginInternalArrayListOutput)(const Descriptor &, - void **scratchArea = nullptr, std::size_t scratchBytes = 0, - const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginInternalArrayListInput)(const Descriptor &, - void **scratchArea = nullptr, std::size_t scratchBytes = 0, - const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginInternalArrayFormattedOutput)(const Descriptor &, - const char *format, std::size_t formatLength, - const Descriptor *formatDescriptor = nullptr, void **scratchArea = nullptr, - std::size_t scratchBytes = 0, const char *sourceFile = nullptr, - int sourceLine = 0); -Cookie IODECL(BeginInternalArrayFormattedInput)(const Descriptor &, - const char *format, std::size_t formatLength, - const Descriptor *formatDescriptor = nullptr, void **scratchArea = nullptr, - std::size_t scratchBytes = 0, const char *sourceFile = nullptr, - int sourceLine = 0); - -// Internal I/O to/from a default-kind character scalar can avoid a -// descriptor. -Cookie IODECL(BeginInternalListOutput)(char *internal, - std::size_t internalLength, void **scratchArea = nullptr, - std::size_t scratchBytes = 0, const char *sourceFile = nullptr, - int sourceLine = 0); -Cookie IODECL(BeginInternalListInput)(const char *internal, - std::size_t internalLength, void **scratchArea = nullptr, - std::size_t scratchBytes = 0, const char *sourceFile = nullptr, - int sourceLine = 0); -Cookie IODECL(BeginInternalFormattedOutput)(char *internal, - std::size_t internalLength, const char *format, std::size_t formatLength, - const Descriptor *formatDescriptor = nullptr, void **scratchArea = nullptr, - std::size_t scratchBytes = 0, const char *sourceFile = nullptr, - int sourceLine = 0); -Cookie IODECL(BeginInternalFormattedInput)(const char *internal, - std::size_t internalLength, const char *format, std::size_t formatLength, - const Descriptor *formatDescriptor = nullptr, void **scratchArea = nullptr, - std::size_t scratchBytes = 0, const char *sourceFile = nullptr, - int sourceLine = 0); - -// External unit numbers must fit in default integers. When the integer -// provided as UNIT is of a wider type than the default integer, it could -// overflow when converted to a default integer. -// CheckUnitNumberInRange should be called to verify that a unit number of a -// wide integer type can fit in a default integer. Since it should be called -// before the BeginXXX(unit, ...) call, it has its own error handling interface. -// If handleError is false, and the unit number is out of range, the program -// will be terminated. Otherwise, if unit is out of range, a nonzero Iostat -// code is returned and ioMsg is set if it is not a nullptr. -enum Iostat IODECL(CheckUnitNumberInRange64)(std::int64_t unit, - bool handleError, char *ioMsg = nullptr, std::size_t ioMsgLength = 0, - const char *sourceFile = nullptr, int sourceLine = 0); -enum Iostat IODECL(CheckUnitNumberInRange128)(common::int128_t unit, - bool handleError, char *ioMsg = nullptr, std::size_t ioMsgLength = 0, - const char *sourceFile = nullptr, int sourceLine = 0); - -// External synchronous I/O initiation -Cookie IODECL(BeginExternalListOutput)(ExternalUnit = DefaultOutputUnit, - const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginExternalListInput)(ExternalUnit = DefaultInputUnit, - const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginExternalFormattedOutput)(const char *format, std::size_t, - const Descriptor *formatDescriptor = nullptr, - ExternalUnit = DefaultOutputUnit, const char *sourceFile = nullptr, - int sourceLine = 0); -Cookie IODECL(BeginExternalFormattedInput)(const char *format, std::size_t, - const Descriptor *formatDescriptor = nullptr, - ExternalUnit = DefaultInputUnit, const char *sourceFile = nullptr, - int sourceLine = 0); -Cookie IODECL(BeginUnformattedOutput)(ExternalUnit = DefaultOutputUnit, - const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginUnformattedInput)(ExternalUnit = DefaultInputUnit, - const char *sourceFile = nullptr, int sourceLine = 0); - -// WAIT(ID=) -Cookie IODECL(BeginWait)(ExternalUnit, AsynchronousId, - const char *sourceFile = nullptr, int sourceLine = 0); -// WAIT(no ID=) -Cookie IODECL(BeginWaitAll)( - ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); - -// Other I/O statements -Cookie IODECL(BeginClose)( - ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginFlush)( - ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginBackspace)( - ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginEndfile)( - ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginRewind)( - ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); - -// OPEN(UNIT=) and OPEN(NEWUNIT=) have distinct interfaces. -Cookie IODECL(BeginOpenUnit)( - ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginOpenNewUnit)( - const char *sourceFile = nullptr, int sourceLine = 0); - -// The variant forms of INQUIRE() statements have distinct interfaces. -// BeginInquireIoLength() is basically a no-op output statement. -Cookie IODECL(BeginInquireUnit)( - ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginInquireFile)(const char *, std::size_t, - const char *sourceFile = nullptr, int sourceLine = 0); -Cookie IODECL(BeginInquireIoLength)( - const char *sourceFile = nullptr, int sourceLine = 0); - -// If an I/O statement has any IOSTAT=, ERR=, END=, or EOR= specifiers, -// call EnableHandlers() immediately after the Begin...() call. -// An output or OPEN statement may not enable HasEnd or HasEor. -// This call makes the runtime library defer those particular error/end -// conditions to the EndIoStatement() call rather than terminating -// the image. E.g., for READ(*,*,END=666) A, B, (C(J),J=1,N) -// Cookie cookie{BeginExternalListInput(DefaultInputUnit,__FILE__,__LINE__)}; -// EnableHandlers(cookie, false, false, true /*END=*/, false); -// if (InputReal64(cookie, &A)) { -// if (InputReal64(cookie, &B)) { -// for (int J{1}; J<=N; ++J) { -// if (!InputReal64(cookie, &C[J])) break; -// } -// } -// } -// if (EndIoStatement(cookie) == FORTRAN_RUTIME_IOSTAT_END) goto label666; -void IODECL(EnableHandlers)(Cookie, bool hasIoStat = false, bool hasErr = false, - bool hasEnd = false, bool hasEor = false, bool hasIoMsg = false); - -// ASYNCHRONOUS='YES' or 'NO' on READ/WRITE/OPEN -// Use GetAsynchronousId() to handle ID=. -bool IODECL(SetAsynchronous)(Cookie, const char *, std::size_t); - -// Control list options. These return false on a error that the -// Begin...() call has specified will be handled by the caller. -// The interfaces that pass a default-kind CHARACTER argument -// are limited to passing specific case-insensitive keyword values. -// ADVANCE=YES, NO -bool IODECL(SetAdvance)(Cookie, const char *, std::size_t); -// BLANK=NULL, ZERO -bool IODECL(SetBlank)(Cookie, const char *, std::size_t); -// DECIMAL=COMMA, POINT -bool IODECL(SetDecimal)(Cookie, const char *, std::size_t); -// DELIM=APOSTROPHE, QUOTE, NONE -bool IODECL(SetDelim)(Cookie, const char *, std::size_t); -// PAD=YES, NO -bool IODECL(SetPad)(Cookie, const char *, std::size_t); -bool IODECL(SetPos)(Cookie, std::int64_t); -bool IODECL(SetRec)(Cookie, std::int64_t); -// ROUND=UP, DOWN, ZERO, NEAREST, COMPATIBLE, PROCESSOR_DEFINED -bool IODECL(SetRound)(Cookie, const char *, std::size_t); -// SIGN=PLUS, SUPPRESS, PROCESSOR_DEFINED -bool IODECL(SetSign)(Cookie, const char *, std::size_t); - -// Data item transfer for modes other than NAMELIST: -// Any data object that can be passed as an actual argument without the -// use of a temporary can be transferred by means of a descriptor; -// vector-valued subscripts and coindexing will require elementwise -// transfers &/or data copies. Unformatted transfers to/from contiguous -// blocks of local image memory can avoid the descriptor, and there -// are specializations for the most common scalar types. -// -// These functions return false when the I/O statement has encountered an -// error or end-of-file/record condition that the caller has indicated -// should not cause termination of the image by the runtime library. -// Once the statement has encountered an error, all following items will be -// ignored and also return false; but compiled code should check for errors -// and avoid the following items when they might crash. -bool IODECL(OutputDescriptor)(Cookie, const Descriptor &); -bool IODECL(InputDescriptor)(Cookie, const Descriptor &); -// Formatted (including list directed) I/O data items -bool IODECL(OutputInteger8)(Cookie, std::int8_t); -bool IODECL(OutputInteger16)(Cookie, std::int16_t); -bool IODECL(OutputInteger32)(Cookie, std::int32_t); -bool IODECL(OutputInteger64)(Cookie, std::int64_t); -bool IODECL(OutputInteger128)(Cookie, common::int128_t); -bool IODECL(InputInteger)(Cookie, std::int64_t &, int kind = 8); -bool IODECL(OutputReal32)(Cookie, float); -bool IODECL(InputReal32)(Cookie, float &); -bool IODECL(OutputReal64)(Cookie, double); -bool IODECL(InputReal64)(Cookie, double &); -bool IODECL(OutputComplex32)(Cookie, float, float); -bool IODECL(InputComplex32)(Cookie, float[2]); -bool IODECL(OutputComplex64)(Cookie, double, double); -bool IODECL(InputComplex64)(Cookie, double[2]); -bool IODECL(OutputCharacter)(Cookie, const char *, std::size_t, int kind = 1); -bool IODECL(OutputAscii)(Cookie, const char *, std::size_t); -bool IODECL(InputCharacter)(Cookie, char *, std::size_t, int kind = 1); -bool IODECL(InputAscii)(Cookie, char *, std::size_t); -bool IODECL(OutputLogical)(Cookie, bool); -bool IODECL(InputLogical)(Cookie, bool &); - -// NAMELIST I/O must be the only data item in an (otherwise) -// list-directed I/O statement. -bool IODECL(OutputNamelist)(Cookie, const NamelistGroup &); -bool IODECL(InputNamelist)(Cookie, const NamelistGroup &); - -// When an I/O list item has a derived type with a specific defined -// I/O subroutine of the appropriate generic kind for the active -// I/O data transfer statement (read/write, formatted/unformatted) -// that pertains to the type or its components, and those subroutines -// are dynamic or neither type-bound nor defined with interfaces -// in the same scope as the derived type (or an IMPORT statement has -// made such a generic interface inaccessible), these data item transfer -// APIs enable the I/O runtime to make the right calls to defined I/O -// subroutines. -bool IODECL(OutputDerivedType)( - Cookie, const Descriptor &, const NonTbpDefinedIoTable *); -bool IODECL(InputDerivedType)( - Cookie, const Descriptor &, const NonTbpDefinedIoTable *); - -// Additional specifier interfaces for the connection-list of -// on OPEN statement (only). SetBlank(), SetDecimal(), -// SetDelim(), GetIoMsg(), SetPad(), SetRound(), SetSign(), -// & SetAsynchronous() are also acceptable for OPEN. -// ACCESS=SEQUENTIAL, DIRECT, STREAM -bool IODECL(SetAccess)(Cookie, const char *, std::size_t); -// ACTION=READ, WRITE, or READWRITE -bool IODECL(SetAction)(Cookie, const char *, std::size_t); -// CARRIAGECONTROL=LIST, FORTRAN, NONE -bool IODECL(SetCarriagecontrol)(Cookie, const char *, std::size_t); -// CONVERT=NATIVE, LITTLE_ENDIAN, BIG_ENDIAN, or SWAP -bool IODECL(SetConvert)(Cookie, const char *, std::size_t); -// ENCODING=UTF-8, DEFAULT -bool IODECL(SetEncoding)(Cookie, const char *, std::size_t); -// FORM=FORMATTED, UNFORMATTED -bool IODECL(SetForm)(Cookie, const char *, std::size_t); -// POSITION=ASIS, REWIND, APPEND -bool IODECL(SetPosition)(Cookie, const char *, std::size_t); -bool IODECL(SetRecl)(Cookie, std::size_t); // RECL= - -// STATUS can be set during an OPEN or CLOSE statement. -// For OPEN: STATUS=OLD, NEW, SCRATCH, REPLACE, UNKNOWN -// For CLOSE: STATUS=KEEP, DELETE -bool IODECL(SetStatus)(Cookie, const char *, std::size_t); - -bool IODECL(SetFile)(Cookie, const char *, std::size_t chars); - -// Acquires the runtime-created unit number for OPEN(NEWUNIT=) -bool IODECL(GetNewUnit)(Cookie, int &, int kind = 4); - -// READ(SIZE=), after all input items -std::size_t IODECL(GetSize)(Cookie); - -// INQUIRE(IOLENGTH=), after all output items -std::size_t IODECL(GetIoLength)(Cookie); - -// GetIoMsg() does not modify its argument unless an error or -// end-of-record/file condition is present. -void IODECL(GetIoMsg)(Cookie, char *, std::size_t); // IOMSG= - -// Defines ID= on READ/WRITE(ASYNCHRONOUS='YES') -AsynchronousId IODECL(GetAsynchronousId)(Cookie); - -// INQUIRE() specifiers are mostly identified by their NUL-terminated -// case-insensitive names. -// ACCESS, ACTION, ASYNCHRONOUS, BLANK, CONVERT, DECIMAL, DELIM, DIRECT, -// ENCODING, FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND, -// SEQUENTIAL, SIGN, STREAM, UNFORMATTED, WRITE: -bool IODECL(InquireCharacter)(Cookie, InquiryKeywordHash, char *, std::size_t); -// EXIST, NAMED, OPENED, and PENDING (without ID): -bool IODECL(InquireLogical)(Cookie, InquiryKeywordHash, bool &); -// PENDING with ID -bool IODECL(InquirePendingId)(Cookie, AsynchronousId, bool &); -// NEXTREC, NUMBER, POS, RECL, SIZE -bool IODECL(InquireInteger64)( - Cookie, InquiryKeywordHash, std::int64_t &, int kind = 8); - -// This function must be called to end an I/O statement, and its -// cookie value may not be used afterwards unless it is recycled -// by the runtime library to serve a later I/O statement. -// The return value can be used to implement IOSTAT=, ERR=, END=, & EOR=; -// store it into the IOSTAT= variable if there is one, and test -// it to implement the various branches. The error condition -// returned is guaranteed to only be one of the problems that the -// EnableHandlers() call has indicated should be handled in compiled code -// rather than by terminating the image. -enum Iostat IODECL(EndIoStatement)(Cookie); - -} // extern "C" -} // namespace Fortran::runtime::io - -#endif /* FORTRAN_RUNTIME_IO_API_CONSTS_H_ */ diff --git a/flang/include/flang/Runtime/io-api.h b/flang/include/flang/Runtime/io-api.h index b86c3cecb32c5..988fe536705e6 100644 --- a/flang/include/flang/Runtime/io-api.h +++ b/flang/include/flang/Runtime/io-api.h @@ -6,14 +6,13 @@ // //===----------------------------------------------------------------------===// -// Defines API between compiled code and I/O runtime library. +// Defines the API of the I/O runtime support library for lowering. #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/io-api-consts.h" #include "flang/Runtime/iostat-consts.h" #include "flang/Runtime/magic-numbers.h" #include @@ -32,8 +31,340 @@ using Cookie = IoStatementState *; using ExternalUnit = int; using AsynchronousId = int; -RT_API_ATTRS const char *InquiryKeywordHashDecode( - char *buffer, std::size_t, InquiryKeywordHash); +static constexpr ExternalUnit DefaultOutputUnit{FORTRAN_DEFAULT_OUTPUT_UNIT}; +static constexpr ExternalUnit DefaultInputUnit{FORTRAN_DEFAULT_INPUT_UNIT}; -} // namespace Fortran::runtime::io +// INQUIRE specifiers are encoded as simple base-26 packings of +// the spellings of their keywords. +using InquiryKeywordHash = std::uint64_t; +constexpr InquiryKeywordHash HashInquiryKeyword(const char *p) { + InquiryKeywordHash hash{1}; + while (char ch{*p++}) { + std::uint64_t letter{0}; + if (ch >= 'a' && ch <= 'z') { + letter = ch - 'a'; + } else { + letter = ch - 'A'; + } + hash = 26 * hash + letter; + } + return hash; +} + +extern "C" { + +#define IONAME(name) RTNAME(io##name) + +#ifndef IODECL +#define IODECL(name) RT_API_ATTRS IONAME(name) +#endif + +#ifndef IODEF +#define IODEF(name) RT_API_ATTRS IONAME(name) #endif + +// These functions initiate data transfer statements (READ, WRITE, PRINT). +// Example: PRINT *, 666 is implemented as the series of calls: +// Cookie cookie{BeginExternalListOutput(DefaultOutputUnit, +// __FILE__, __LINE__)}; +// OutputInteger32(cookie, 666); +// EndIoStatement(cookie); +// Formatted I/O with explicit formats can supply the format as a +// const char * pointer with a length, or with a descriptor. + +// Internal I/O initiation +// Internal I/O can loan the runtime library an optional block of memory +// in which the library can maintain state across the calls that implement +// the internal transfer; use of these blocks can reduce the need for dynamic +// memory allocation &/or thread-local storage. The block must be sufficiently +// aligned to hold a pointer. +constexpr std::size_t RecommendedInternalIoScratchAreaBytes( + int maxFormatParenthesesNestingDepth) { + return 32 + 8 * maxFormatParenthesesNestingDepth; +} + +// For NAMELIST I/O, use the API for the appropriate form of list-directed +// I/O initiation and configuration, then call OutputNamelist/InputNamelist +// below. + +// Internal I/O to/from character arrays &/or non-default-kind character +// requires a descriptor, which is copied. +Cookie IODECL(BeginInternalArrayListOutput)(const Descriptor &, + void **scratchArea = nullptr, std::size_t scratchBytes = 0, + const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginInternalArrayListInput)(const Descriptor &, + void **scratchArea = nullptr, std::size_t scratchBytes = 0, + const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginInternalArrayFormattedOutput)(const Descriptor &, + const char *format, std::size_t formatLength, + const Descriptor *formatDescriptor = nullptr, void **scratchArea = nullptr, + std::size_t scratchBytes = 0, const char *sourceFile = nullptr, + int sourceLine = 0); +Cookie IODECL(BeginInternalArrayFormattedInput)(const Descriptor &, + const char *format, std::size_t formatLength, + const Descriptor *formatDescriptor = nullptr, void **scratchArea = nullptr, + std::size_t scratchBytes = 0, const char *sourceFile = nullptr, + int sourceLine = 0); + +// Internal I/O to/from a default-kind character scalar can avoid a +// descriptor. +Cookie IODECL(BeginInternalListOutput)(char *internal, + std::size_t internalLength, void **scratchArea = nullptr, + std::size_t scratchBytes = 0, const char *sourceFile = nullptr, + int sourceLine = 0); +Cookie IODECL(BeginInternalListInput)(const char *internal, + std::size_t internalLength, void **scratchArea = nullptr, + std::size_t scratchBytes = 0, const char *sourceFile = nullptr, + int sourceLine = 0); +Cookie IODECL(BeginInternalFormattedOutput)(char *internal, + std::size_t internalLength, const char *format, std::size_t formatLength, + const Descriptor *formatDescriptor = nullptr, void **scratchArea = nullptr, + std::size_t scratchBytes = 0, const char *sourceFile = nullptr, + int sourceLine = 0); +Cookie IODECL(BeginInternalFormattedInput)(const char *internal, + std::size_t internalLength, const char *format, std::size_t formatLength, + const Descriptor *formatDescriptor = nullptr, void **scratchArea = nullptr, + std::size_t scratchBytes = 0, const char *sourceFile = nullptr, + int sourceLine = 0); + +// External unit numbers must fit in default integers. When the integer +// provided as UNIT is of a wider type than the default integer, it could +// overflow when converted to a default integer. +// CheckUnitNumberInRange should be called to verify that a unit number of a +// wide integer type can fit in a default integer. Since it should be called +// before the BeginXXX(unit, ...) call, it has its own error handling interface. +// If handleError is false, and the unit number is out of range, the program +// will be terminated. Otherwise, if unit is out of range, a nonzero Iostat +// code is returned and ioMsg is set if it is not a nullptr. +enum Iostat IODECL(CheckUnitNumberInRange64)(std::int64_t unit, + bool handleError, char *ioMsg = nullptr, std::size_t ioMsgLength = 0, + const char *sourceFile = nullptr, int sourceLine = 0); +enum Iostat IODECL(CheckUnitNumberInRange128)(common::int128_t unit, + bool handleError, char *ioMsg = nullptr, std::size_t ioMsgLength = 0, + const char *sourceFile = nullptr, int sourceLine = 0); + +// External synchronous I/O initiation +Cookie IODECL(BeginExternalListOutput)(ExternalUnit = DefaultOutputUnit, + const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginExternalListInput)(ExternalUnit = DefaultInputUnit, + const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginExternalFormattedOutput)(const char *format, std::size_t, + const Descriptor *formatDescriptor = nullptr, + ExternalUnit = DefaultOutputUnit, const char *sourceFile = nullptr, + int sourceLine = 0); +Cookie IODECL(BeginExternalFormattedInput)(const char *format, std::size_t, + const Descriptor *formatDescriptor = nullptr, + ExternalUnit = DefaultInputUnit, const char *sourceFile = nullptr, + int sourceLine = 0); +Cookie IODECL(BeginUnformattedOutput)(ExternalUnit = DefaultOutputUnit, + const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginUnformattedInput)(ExternalUnit = DefaultInputUnit, + const char *sourceFile = nullptr, int sourceLine = 0); + +// WAIT(ID=) +Cookie IODECL(BeginWait)(ExternalUnit, AsynchronousId, + const char *sourceFile = nullptr, int sourceLine = 0); +// WAIT(no ID=) +Cookie IODECL(BeginWaitAll)( + ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); + +// Other I/O statements +Cookie IODECL(BeginClose)( + ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginFlush)( + ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginBackspace)( + ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginEndfile)( + ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginRewind)( + ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); + +// OPEN(UNIT=) and OPEN(NEWUNIT=) have distinct interfaces. +Cookie IODECL(BeginOpenUnit)( + ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginOpenNewUnit)( + const char *sourceFile = nullptr, int sourceLine = 0); + +// The variant forms of INQUIRE() statements have distinct interfaces. +// BeginInquireIoLength() is basically a no-op output statement. +Cookie IODECL(BeginInquireUnit)( + ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginInquireFile)(const char *, std::size_t, + const char *sourceFile = nullptr, int sourceLine = 0); +Cookie IODECL(BeginInquireIoLength)( + const char *sourceFile = nullptr, int sourceLine = 0); + +// If an I/O statement has any IOSTAT=, ERR=, END=, or EOR= specifiers, +// call EnableHandlers() immediately after the Begin...() call. +// An output or OPEN statement may not enable HasEnd or HasEor. +// This call makes the runtime library defer those particular error/end +// conditions to the EndIoStatement() call rather than terminating +// the image. E.g., for READ(*,*,END=666) A, B, (C(J),J=1,N) +// Cookie cookie{BeginExternalListInput(DefaultInputUnit,__FILE__,__LINE__)}; +// EnableHandlers(cookie, false, false, true /*END=*/, false); +// if (InputReal64(cookie, &A)) { +// if (InputReal64(cookie, &B)) { +// for (int J{1}; J<=N; ++J) { +// if (!InputReal64(cookie, &C[J])) break; +// } +// } +// } +// if (EndIoStatement(cookie) == FORTRAN_RUTIME_IOSTAT_END) goto label666; +void IODECL(EnableHandlers)(Cookie, bool hasIoStat = false, bool hasErr = false, + bool hasEnd = false, bool hasEor = false, bool hasIoMsg = false); + +// ASYNCHRONOUS='YES' or 'NO' on READ/WRITE/OPEN +// Use GetAsynchronousId() to handle ID=. +bool IODECL(SetAsynchronous)(Cookie, const char *, std::size_t); + +// Control list options. These return false on a error that the +// Begin...() call has specified will be handled by the caller. +// The interfaces that pass a default-kind CHARACTER argument +// are limited to passing specific case-insensitive keyword values. +// ADVANCE=YES, NO +bool IODECL(SetAdvance)(Cookie, const char *, std::size_t); +// BLANK=NULL, ZERO +bool IODECL(SetBlank)(Cookie, const char *, std::size_t); +// DECIMAL=COMMA, POINT +bool IODECL(SetDecimal)(Cookie, const char *, std::size_t); +// DELIM=APOSTROPHE, QUOTE, NONE +bool IODECL(SetDelim)(Cookie, const char *, std::size_t); +// PAD=YES, NO +bool IODECL(SetPad)(Cookie, const char *, std::size_t); +bool IODECL(SetPos)(Cookie, std::int64_t); +bool IODECL(SetRec)(Cookie, std::int64_t); +// ROUND=UP, DOWN, ZERO, NEAREST, COMPATIBLE, PROCESSOR_DEFINED +bool IODECL(SetRound)(Cookie, const char *, std::size_t); +// SIGN=PLUS, SUPPRESS, PROCESSOR_DEFINED +bool IODECL(SetSign)(Cookie, const char *, std::size_t); + +// Data item transfer for modes other than NAMELIST: +// Any data object that can be passed as an actual argument without the +// use of a temporary can be transferred by means of a descriptor; +// vector-valued subscripts and coindexing will require elementwise +// transfers &/or data copies. Unformatted transfers to/from contiguous +// blocks of local image memory can avoid the descriptor, and there +// are specializations for the most common scalar types. +// +// These functions return false when the I/O statement has encountered an +// error or end-of-file/record condition that the caller has indicated +// should not cause termination of the image by the runtime library. +// Once the statement has encountered an error, all following items will be +// ignored and also return false; but compiled code should check for errors +// and avoid the following items when they might crash. +bool IODECL(OutputDescriptor)(Cookie, const Descriptor &); +bool IODECL(InputDescriptor)(Cookie, const Descriptor &); +// Formatted (including list directed) I/O data items +bool IODECL(OutputInteger8)(Cookie, std::int8_t); +bool IODECL(OutputInteger16)(Cookie, std::int16_t); +bool IODECL(OutputInteger32)(Cookie, std::int32_t); +bool IODECL(OutputInteger64)(Cookie, std::int64_t); +bool IODECL(OutputInteger128)(Cookie, common::int128_t); +bool IODECL(InputInteger)(Cookie, std::int64_t &, int kind = 8); +bool IODECL(OutputReal32)(Cookie, float); +bool IODECL(InputReal32)(Cookie, float &); +bool IODECL(OutputReal64)(Cookie, double); +bool IODECL(InputReal64)(Cookie, double &); +bool IODECL(OutputComplex32)(Cookie, float, float); +bool IODECL(InputComplex32)(Cookie, float[2]); +bool IODECL(OutputComplex64)(Cookie, double, double); +bool IODECL(InputComplex64)(Cookie, double[2]); +bool IODECL(OutputCharacter)(Cookie, const char *, std::size_t, int kind = 1); +bool IODECL(OutputAscii)(Cookie, const char *, std::size_t); +bool IODECL(InputCharacter)(Cookie, char *, std::size_t, int kind = 1); +bool IODECL(InputAscii)(Cookie, char *, std::size_t); +bool IODECL(OutputLogical)(Cookie, bool); +bool IODECL(InputLogical)(Cookie, bool &); + +// NAMELIST I/O must be the only data item in an (otherwise) +// list-directed I/O statement. +bool IODECL(OutputNamelist)(Cookie, const NamelistGroup &); +bool IODECL(InputNamelist)(Cookie, const NamelistGroup &); + +// When an I/O list item has a derived type with a specific defined +// I/O subroutine of the appropriate generic kind for the active +// I/O data transfer statement (read/write, formatted/unformatted) +// that pertains to the type or its components, and those subroutines +// are dynamic or neither type-bound nor defined with interfaces +// in the same scope as the derived type (or an IMPORT statement has +// made such a generic interface inaccessible), these data item transfer +// APIs enable the I/O runtime to make the right calls to defined I/O +// subroutines. +bool IODECL(OutputDerivedType)( + Cookie, const Descriptor &, const NonTbpDefinedIoTable *); +bool IODECL(InputDerivedType)( + Cookie, const Descriptor &, const NonTbpDefinedIoTable *); + +// Additional specifier interfaces for the connection-list of +// on OPEN statement (only). SetBlank(), SetDecimal(), +// SetDelim(), GetIoMsg(), SetPad(), SetRound(), SetSign(), +// & SetAsynchronous() are also acceptable for OPEN. +// ACCESS=SEQUENTIAL, DIRECT, STREAM +bool IODECL(SetAccess)(Cookie, const char *, std::size_t); +// ACTION=READ, WRITE, or READWRITE +bool IODECL(SetAction)(Cookie, const char *, std::size_t); +// CARRIAGECONTROL=LIST, FORTRAN, NONE +bool IODECL(SetCarriagecontrol)(Cookie, const char *, std::size_t); +// CONVERT=NATIVE, LITTLE_ENDIAN, BIG_ENDIAN, or SWAP +bool IODECL(SetConvert)(Cookie, const char *, std::size_t); +// ENCODING=UTF-8, DEFAULT +bool IODECL(SetEncoding)(Cookie, const char *, std::size_t); +// FORM=FORMATTED, UNFORMATTED +bool IODECL(SetForm)(Cookie, const char *, std::size_t); +// POSITION=ASIS, REWIND, APPEND +bool IODECL(SetPosition)(Cookie, const char *, std::size_t); +bool IODECL(SetRecl)(Cookie, std::size_t); // RECL= + +// STATUS can be set during an OPEN or CLOSE statement. +// For OPEN: STATUS=OLD, NEW, SCRATCH, REPLACE, UNKNOWN +// For CLOSE: STATUS=KEEP, DELETE +bool IODECL(SetStatus)(Cookie, const char *, std::size_t); + +bool IODECL(SetFile)(Cookie, const char *, std::size_t chars); + +// Acquires the runtime-created unit number for OPEN(NEWUNIT=) +bool IODECL(GetNewUnit)(Cookie, int &, int kind = 4); + +// READ(SIZE=), after all input items +std::size_t IODECL(GetSize)(Cookie); + +// INQUIRE(IOLENGTH=), after all output items +std::size_t IODECL(GetIoLength)(Cookie); + +// GetIoMsg() does not modify its argument unless an error or +// end-of-record/file condition is present. +void IODECL(GetIoMsg)(Cookie, char *, std::size_t); // IOMSG= + +// Defines ID= on READ/WRITE(ASYNCHRONOUS='YES') +AsynchronousId IODECL(GetAsynchronousId)(Cookie); + +// INQUIRE() specifiers are mostly identified by their NUL-terminated +// case-insensitive names. +// ACCESS, ACTION, ASYNCHRONOUS, BLANK, CONVERT, DECIMAL, DELIM, DIRECT, +// ENCODING, FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND, +// SEQUENTIAL, SIGN, STREAM, UNFORMATTED, WRITE: +bool IODECL(InquireCharacter)(Cookie, InquiryKeywordHash, char *, std::size_t); +// EXIST, NAMED, OPENED, and PENDING (without ID): +bool IODECL(InquireLogical)(Cookie, InquiryKeywordHash, bool &); +// PENDING with ID +bool IODECL(InquirePendingId)(Cookie, AsynchronousId, bool &); +// NEXTREC, NUMBER, POS, RECL, SIZE +bool IODECL(InquireInteger64)( + Cookie, InquiryKeywordHash, std::int64_t &, int kind = 8); + +// This function must be called to end an I/O statement, and its +// cookie value may not be used afterwards unless it is recycled +// by the runtime library to serve a later I/O statement. +// The return value can be used to implement IOSTAT=, ERR=, END=, & EOR=; +// store it into the IOSTAT= variable if there is one, and test +// it to implement the various branches. The error condition +// returned is guaranteed to only be one of the problems that the +// EnableHandlers() call has indicated should be handled in compiled code +// rather than by terminating the image. +enum Iostat IODECL(EndIoStatement)(Cookie); + +} // extern "C" +} // namespace Fortran::runtime::io + +#endif /* FORTRAN_RUNTIME_IO_API_H_ */ diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp index 16e6d05bd3b10..07c9e6a1726bf 100644 --- a/flang/lib/Lower/IO.cpp +++ b/flang/lib/Lower/IO.cpp @@ -34,7 +34,7 @@ #include "flang/Optimizer/Dialect/Support/FIRContext.h" #include "flang/Optimizer/Support/InternalNames.h" #include "flang/Parser/parse-tree.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" #include "flang/Semantics/runtime-type-info.h" #include "flang/Semantics/tools.h" #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" diff --git a/flang/lib/Optimizer/Transforms/GenRuntimeCallsForTest.cpp b/flang/lib/Optimizer/Transforms/GenRuntimeCallsForTest.cpp index 8d1d62e334d8c..7ea3b9c670c69 100644 --- a/flang/lib/Optimizer/Transforms/GenRuntimeCallsForTest.cpp +++ b/flang/lib/Optimizer/Transforms/GenRuntimeCallsForTest.cpp @@ -29,7 +29,7 @@ #include "flang/Optimizer/Dialect/FIROpsSupport.h" #include "flang/Optimizer/Support/InternalNames.h" #include "flang/Optimizer/Transforms/Passes.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" #include "mlir/Dialect/LLVMIR/LLVMAttrs.h" namespace fir { diff --git a/flang/lib/Optimizer/Transforms/SetRuntimeCallAttributes.cpp b/flang/lib/Optimizer/Transforms/SetRuntimeCallAttributes.cpp index 1c33e81839970..378037e9494f4 100644 --- a/flang/lib/Optimizer/Transforms/SetRuntimeCallAttributes.cpp +++ b/flang/lib/Optimizer/Transforms/SetRuntimeCallAttributes.cpp @@ -23,7 +23,7 @@ #include "flang/Optimizer/Dialect/FIROpsSupport.h" #include "flang/Optimizer/Support/InternalNames.h" #include "flang/Optimizer/Transforms/Passes.h" -#include "flang/Runtime/io-api-consts.h" +#include "flang/Runtime/io-api.h" #include "mlir/Dialect/LLVMIR/LLVMAttrs.h" namespace fir {