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