1 //===-- include/flang/Runtime/io-api.h --------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // Defines API between compiled code and I/O runtime library.
10 
11 #ifndef FORTRAN_RUNTIME_IO_API_H_
12 #define FORTRAN_RUNTIME_IO_API_H_
13 
14 #include "flang/Common/uint128.h"
15 #include "flang/Runtime/entry-names.h"
16 #include "flang/Runtime/iostat.h"
17 #include <cinttypes>
18 #include <cstddef>
19 
20 namespace Fortran::runtime {
21 class Descriptor;
22 } // namespace Fortran::runtime
23 
24 namespace Fortran::runtime::io {
25 
26 class NamelistGroup;
27 class IoStatementState;
28 using Cookie = IoStatementState *;
29 using ExternalUnit = int;
30 using AsynchronousId = int;
31 static constexpr ExternalUnit DefaultUnit{-1}; // READ(*), WRITE(*), PRINT
32 
33 // INQUIRE specifiers are encoded as simple base-26 packings of
34 // the spellings of their keywords.
35 using InquiryKeywordHash = std::uint64_t;
HashInquiryKeyword(const char * p)36 constexpr InquiryKeywordHash HashInquiryKeyword(const char *p) {
37   InquiryKeywordHash hash{1};
38   while (char ch{*p++}) {
39     std::uint64_t letter{0};
40     if (ch >= 'a' && ch <= 'z') {
41       letter = ch - 'a';
42     } else {
43       letter = ch - 'A';
44     }
45     hash = 26 * hash + letter;
46   }
47   return hash;
48 }
49 
50 const char *InquiryKeywordHashDecode(
51     char *buffer, std::size_t, InquiryKeywordHash);
52 
53 extern "C" {
54 
55 #define IONAME(name) RTNAME(io##name)
56 
57 // These functions initiate data transfer statements (READ, WRITE, PRINT).
58 // Example: PRINT *, 666 is implemented as the series of calls:
59 //   Cookie cookie{BeginExternalListOutput(DefaultUnit,__FILE__,__LINE__)};
60 //   OutputInteger32(cookie, 666);
61 //   EndIoStatement(cookie);
62 
63 // Internal I/O initiation
64 // Internal I/O can loan the runtime library an optional block of memory
65 // in which the library can maintain state across the calls that implement
66 // the internal transfer; use of these blocks can reduce the need for dynamic
67 // memory allocation &/or thread-local storage.  The block must be sufficiently
68 // aligned to hold a pointer.
RecommendedInternalIoScratchAreaBytes(int maxFormatParenthesesNestingDepth)69 constexpr std::size_t RecommendedInternalIoScratchAreaBytes(
70     int maxFormatParenthesesNestingDepth) {
71   return 32 + 8 * maxFormatParenthesesNestingDepth;
72 }
73 
74 // For NAMELIST I/O, use the API for the appropriate form of list-directed
75 // I/O initiation and configuration, then call OutputNamelist/InputNamelist
76 // below.
77 
78 // Internal I/O to/from character arrays &/or non-default-kind character
79 // requires a descriptor, which is copied.
80 Cookie IONAME(BeginInternalArrayListOutput)(const Descriptor &,
81     void **scratchArea = nullptr, std::size_t scratchBytes = 0,
82     const char *sourceFile = nullptr, int sourceLine = 0);
83 Cookie IONAME(BeginInternalArrayListInput)(const Descriptor &,
84     void **scratchArea = nullptr, std::size_t scratchBytes = 0,
85     const char *sourceFile = nullptr, int sourceLine = 0);
86 Cookie IONAME(BeginInternalArrayFormattedOutput)(const Descriptor &,
87     const char *format, std::size_t formatLength, void **scratchArea = nullptr,
88     std::size_t scratchBytes = 0, const char *sourceFile = nullptr,
89     int sourceLine = 0);
90 Cookie IONAME(BeginInternalArrayFormattedInput)(const Descriptor &,
91     const char *format, std::size_t formatLength, void **scratchArea = nullptr,
92     std::size_t scratchBytes = 0, const char *sourceFile = nullptr,
93     int sourceLine = 0);
94 
95 // Internal I/O to/from a default-kind character scalar can avoid a
96 // descriptor.
97 Cookie IONAME(BeginInternalListOutput)(char *internal,
98     std::size_t internalLength, void **scratchArea = nullptr,
99     std::size_t scratchBytes = 0, const char *sourceFile = nullptr,
100     int sourceLine = 0);
101 Cookie IONAME(BeginInternalListInput)(const char *internal,
102     std::size_t internalLength, void **scratchArea = nullptr,
103     std::size_t scratchBytes = 0, const char *sourceFile = nullptr,
104     int sourceLine = 0);
105 Cookie IONAME(BeginInternalFormattedOutput)(char *internal,
106     std::size_t internalLength, const char *format, std::size_t formatLength,
107     void **scratchArea = nullptr, std::size_t scratchBytes = 0,
108     const char *sourceFile = nullptr, int sourceLine = 0);
109 Cookie IONAME(BeginInternalFormattedInput)(const char *internal,
110     std::size_t internalLength, const char *format, std::size_t formatLength,
111     void **scratchArea = nullptr, std::size_t scratchBytes = 0,
112     const char *sourceFile = nullptr, int sourceLine = 0);
113 
114 // External unit numbers must fit in default integers. When the integer
115 // provided as UNIT is of a wider type than the default integer, it could
116 // overflow when converted to a default integer.
117 // CheckUnitNumberInRange should be called to verify that a unit number of a
118 // wide integer type can fit in a default integer. Since it should be called
119 // before the BeginXXX(unit, ...) call, it has its own error handling interface.
120 // If handleError is false, and the unit number is out of range, the program
121 // will be terminated. Otherwise, if unit is out of range, a nonzero Iostat
122 // code is returned and ioMsg is set if it is not a nullptr.
123 enum Iostat IONAME(CheckUnitNumberInRange64)(std::int64_t unit,
124     bool handleError, char *ioMsg = nullptr, std::size_t ioMsgLength = 0,
125     const char *sourceFile = nullptr, int sourceLine = 0);
126 enum Iostat IONAME(CheckUnitNumberInRange128)(common::int128_t unit,
127     bool handleError, char *ioMsg = nullptr, std::size_t ioMsgLength = 0,
128     const char *sourceFile = nullptr, int sourceLine = 0);
129 
130 // External synchronous I/O initiation
131 Cookie IONAME(BeginExternalListOutput)(ExternalUnit = DefaultUnit,
132     const char *sourceFile = nullptr, int sourceLine = 0);
133 Cookie IONAME(BeginExternalListInput)(ExternalUnit = DefaultUnit,
134     const char *sourceFile = nullptr, int sourceLine = 0);
135 Cookie IONAME(BeginExternalFormattedOutput)(const char *format, std::size_t,
136     ExternalUnit = DefaultUnit, const char *sourceFile = nullptr,
137     int sourceLine = 0);
138 Cookie IONAME(BeginExternalFormattedInput)(const char *format, std::size_t,
139     ExternalUnit = DefaultUnit, const char *sourceFile = nullptr,
140     int sourceLine = 0);
141 Cookie IONAME(BeginUnformattedOutput)(ExternalUnit = DefaultUnit,
142     const char *sourceFile = nullptr, int sourceLine = 0);
143 Cookie IONAME(BeginUnformattedInput)(ExternalUnit = DefaultUnit,
144     const char *sourceFile = nullptr, int sourceLine = 0);
145 
146 // WAIT(ID=)
147 Cookie IONAME(BeginWait)(ExternalUnit, AsynchronousId,
148     const char *sourceFile = nullptr, int sourceLine = 0);
149 // WAIT(no ID=)
150 Cookie IONAME(BeginWaitAll)(
151     ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
152 
153 // Other I/O statements
154 Cookie IONAME(BeginClose)(
155     ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
156 Cookie IONAME(BeginFlush)(
157     ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
158 Cookie IONAME(BeginBackspace)(
159     ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
160 Cookie IONAME(BeginEndfile)(
161     ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
162 Cookie IONAME(BeginRewind)(
163     ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
164 
165 // OPEN(UNIT=) and OPEN(NEWUNIT=) have distinct interfaces.
166 Cookie IONAME(BeginOpenUnit)(
167     ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
168 Cookie IONAME(BeginOpenNewUnit)(
169     const char *sourceFile = nullptr, int sourceLine = 0);
170 
171 // The variant forms of INQUIRE() statements have distinct interfaces.
172 // BeginInquireIoLength() is basically a no-op output statement.
173 Cookie IONAME(BeginInquireUnit)(
174     ExternalUnit, const char *sourceFile = nullptr, int sourceLine = 0);
175 Cookie IONAME(BeginInquireFile)(const char *, std::size_t,
176     const char *sourceFile = nullptr, int sourceLine = 0);
177 Cookie IONAME(BeginInquireIoLength)(
178     const char *sourceFile = nullptr, int sourceLine = 0);
179 
180 // If an I/O statement has any IOSTAT=, ERR=, END=, or EOR= specifiers,
181 // call EnableHandlers() immediately after the Begin...() call.
182 // An output or OPEN statement may not enable HasEnd or HasEor.
183 // This call makes the runtime library defer those particular error/end
184 // conditions to the EndIoStatement() call rather than terminating
185 // the image.  E.g., for READ(*,*,END=666) A, B, (C(J),J=1,N)
186 //   Cookie cookie{BeginExternalListInput(DefaultUnit,__FILE__,__LINE__)};
187 //   EnableHandlers(cookie, false, false, true /*END=*/, false);
188 //   if (InputReal64(cookie, &A)) {
189 //     if (InputReal64(cookie, &B)) {
190 //       for (int J{1}; J<=N; ++J) {
191 //         if (!InputReal64(cookie, &C[J])) break;
192 //       }
193 //     }
194 //   }
195 //   if (EndIoStatement(cookie) == FORTRAN_RUTIME_IOSTAT_END) goto label666;
196 void IONAME(EnableHandlers)(Cookie, bool hasIoStat = false, bool hasErr = false,
197     bool hasEnd = false, bool hasEor = false, bool hasIoMsg = false);
198 
199 // ASYNCHRONOUS='YES' or 'NO' on READ/WRITE/OPEN
200 // Use GetAsynchronousId() to handle ID=.
201 bool IONAME(SetAsynchronous)(Cookie, const char *, std::size_t);
202 
203 // Control list options.  These return false on a error that the
204 // Begin...() call has specified will be handled by the caller.
205 // The interfaces that pass a default-kind CHARACTER argument
206 // are limited to passing specific case-insensitive keyword values.
207 // ADVANCE=YES, NO
208 bool IONAME(SetAdvance)(Cookie, const char *, std::size_t);
209 // BLANK=NULL, ZERO
210 bool IONAME(SetBlank)(Cookie, const char *, std::size_t);
211 // DECIMAL=COMMA, POINT
212 bool IONAME(SetDecimal)(Cookie, const char *, std::size_t);
213 // DELIM=APOSTROPHE, QUOTE, NONE
214 bool IONAME(SetDelim)(Cookie, const char *, std::size_t);
215 // PAD=YES, NO
216 bool IONAME(SetPad)(Cookie, const char *, std::size_t);
217 bool IONAME(SetPos)(Cookie, std::int64_t);
218 bool IONAME(SetRec)(Cookie, std::int64_t);
219 // ROUND=UP, DOWN, ZERO, NEAREST, COMPATIBLE, PROCESSOR_DEFINED
220 bool IONAME(SetRound)(Cookie, const char *, std::size_t);
221 // SIGN=PLUS, SUPPRESS, PROCESSOR_DEFINED
222 bool IONAME(SetSign)(Cookie, const char *, std::size_t);
223 
224 // Data item transfer for modes other than NAMELIST:
225 // Any data object that can be passed as an actual argument without the
226 // use of a temporary can be transferred by means of a descriptor;
227 // vector-valued subscripts and coindexing will require elementwise
228 // transfers &/or data copies.  Unformatted transfers to/from contiguous
229 // blocks of local image memory can avoid the descriptor, and there
230 // are specializations for the most common scalar types.
231 //
232 // These functions return false when the I/O statement has encountered an
233 // error or end-of-file/record condition that the caller has indicated
234 // should not cause termination of the image by the runtime library.
235 // Once the statement has encountered an error, all following items will be
236 // ignored and also return false; but compiled code should check for errors
237 // and avoid the following items when they might crash.
238 bool IONAME(OutputDescriptor)(Cookie, const Descriptor &);
239 bool IONAME(InputDescriptor)(Cookie, const Descriptor &);
240 // Contiguous transfers for unformatted I/O
241 bool IONAME(OutputUnformattedBlock)(
242     Cookie, const char *, std::size_t, std::size_t elementBytes);
243 bool IONAME(InputUnformattedBlock)(
244     Cookie, char *, std::size_t, std::size_t elementBytes);
245 // Formatted (including list directed) I/O data items
246 bool IONAME(OutputInteger8)(Cookie, std::int8_t);
247 bool IONAME(OutputInteger16)(Cookie, std::int16_t);
248 bool IONAME(OutputInteger32)(Cookie, std::int32_t);
249 bool IONAME(OutputInteger64)(Cookie, std::int64_t);
250 bool IONAME(OutputInteger128)(Cookie, common::int128_t);
251 bool IONAME(InputInteger)(Cookie, std::int64_t &, int kind = 8);
252 bool IONAME(OutputReal32)(Cookie, float);
253 bool IONAME(InputReal32)(Cookie, float &);
254 bool IONAME(OutputReal64)(Cookie, double);
255 bool IONAME(InputReal64)(Cookie, double &);
256 bool IONAME(OutputComplex32)(Cookie, float, float);
257 bool IONAME(InputComplex32)(Cookie, float[2]);
258 bool IONAME(OutputComplex64)(Cookie, double, double);
259 bool IONAME(InputComplex64)(Cookie, double[2]);
260 bool IONAME(OutputCharacter)(Cookie, const char *, std::size_t, int kind = 1);
261 bool IONAME(OutputAscii)(Cookie, const char *, std::size_t);
262 bool IONAME(InputCharacter)(Cookie, char *, std::size_t, int kind = 1);
263 bool IONAME(InputAscii)(Cookie, char *, std::size_t);
264 bool IONAME(OutputLogical)(Cookie, bool);
265 bool IONAME(InputLogical)(Cookie, bool &);
266 
267 // NAMELIST I/O must be the only data item in an (otherwise)
268 // list-directed I/O statement.
269 bool IONAME(OutputNamelist)(Cookie, const NamelistGroup &);
270 bool IONAME(InputNamelist)(Cookie, const NamelistGroup &);
271 
272 // Additional specifier interfaces for the connection-list of
273 // on OPEN statement (only).  SetBlank(), SetDecimal(),
274 // SetDelim(), GetIoMsg(), SetPad(), SetRound(), SetSign(),
275 // & SetAsynchronous() are also acceptable for OPEN.
276 // ACCESS=SEQUENTIAL, DIRECT, STREAM
277 bool IONAME(SetAccess)(Cookie, const char *, std::size_t);
278 // ACTION=READ, WRITE, or READWRITE
279 bool IONAME(SetAction)(Cookie, const char *, std::size_t);
280 // CARRIAGECONTROL=LIST, FORTRAN, NONE
281 bool IONAME(SetCarriagecontrol)(Cookie, const char *, std::size_t);
282 // CONVERT=NATIVE, LITTLE_ENDIAN, BIG_ENDIAN, or SWAP
283 bool IONAME(SetConvert)(Cookie, const char *, std::size_t);
284 // ENCODING=UTF-8, DEFAULT
285 bool IONAME(SetEncoding)(Cookie, const char *, std::size_t);
286 // FORM=FORMATTED, UNFORMATTED
287 bool IONAME(SetForm)(Cookie, const char *, std::size_t);
288 // POSITION=ASIS, REWIND, APPEND
289 bool IONAME(SetPosition)(Cookie, const char *, std::size_t);
290 bool IONAME(SetRecl)(Cookie, std::size_t); // RECL=
291 
292 // STATUS can be set during an OPEN or CLOSE statement.
293 // For OPEN: STATUS=OLD, NEW, SCRATCH, REPLACE, UNKNOWN
294 // For CLOSE: STATUS=KEEP, DELETE
295 bool IONAME(SetStatus)(Cookie, const char *, std::size_t);
296 
297 bool IONAME(SetFile)(Cookie, const char *, std::size_t chars);
298 
299 // Acquires the runtime-created unit number for OPEN(NEWUNIT=)
300 bool IONAME(GetNewUnit)(Cookie, int &, int kind = 4);
301 
302 // READ(SIZE=), after all input items
303 std::size_t IONAME(GetSize)(Cookie);
304 
305 // INQUIRE(IOLENGTH=), after all output items
306 std::size_t IONAME(GetIoLength)(Cookie);
307 
308 // GetIoMsg() does not modify its argument unless an error or
309 // end-of-record/file condition is present.
310 void IONAME(GetIoMsg)(Cookie, char *, std::size_t); // IOMSG=
311 
312 // Defines ID= on READ/WRITE(ASYNCHRONOUS='YES')
313 int IONAME(GetAsynchronousId)(Cookie);
314 
315 // INQUIRE() specifiers are mostly identified by their NUL-terminated
316 // case-insensitive names.
317 // ACCESS, ACTION, ASYNCHRONOUS, BLANK, CONVERT, DECIMAL, DELIM, DIRECT,
318 // ENCODING, FORM, FORMATTED, NAME, PAD, POSITION, READ, READWRITE, ROUND,
319 // SEQUENTIAL, SIGN, STREAM, UNFORMATTED, WRITE:
320 bool IONAME(InquireCharacter)(Cookie, InquiryKeywordHash, char *, std::size_t);
321 // EXIST, NAMED, OPENED, and PENDING (without ID):
322 bool IONAME(InquireLogical)(Cookie, InquiryKeywordHash, bool &);
323 // PENDING with ID
324 bool IONAME(InquirePendingId)(Cookie, std::int64_t, bool &);
325 // NEXTREC, NUMBER, POS, RECL, SIZE
326 bool IONAME(InquireInteger64)(
327     Cookie, InquiryKeywordHash, std::int64_t &, int kind = 8);
328 
329 // This function must be called to end an I/O statement, and its
330 // cookie value may not be used afterwards unless it is recycled
331 // by the runtime library to serve a later I/O statement.
332 // The return value can be used to implement IOSTAT=, ERR=, END=, & EOR=;
333 // store it into the IOSTAT= variable if there is one, and test
334 // it to implement the various branches.  The error condition
335 // returned is guaranteed to only be one of the problems that the
336 // EnableHandlers() call has indicated should be handled in compiled code
337 // rather than by terminating the image.
338 enum Iostat IONAME(EndIoStatement)(Cookie);
339 
340 } // extern "C"
341 } // namespace Fortran::runtime::io
342 #endif
343