summaryrefslogtreecommitdiffstats
path: root/flang/include/flang/Runtime/io-api.h
blob: 328afc715a3f1e05d5a4c1ab7dd393f55cb8c2ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
//===-- include/flang/Runtime/io-api.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
//
//===----------------------------------------------------------------------===//

// Defines API between compiled code and I/O runtime library.

#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/iostat.h"
#include "flang/Runtime/magic-numbers.h"
#include <cinttypes>
#include <cstddef>

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;
}

RT_API_ATTRS const char *InquiryKeywordHashDecode(
    char *buffer, std::size_t, InquiryKeywordHash);

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