1 <!--===- docs/IORuntimeInternals.md
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
9 # Fortran I/O Runtime Library Internal Design
16 This note is meant to be an overview of the design of the *implementation*
17 of the f18 Fortran compiler's runtime support library for I/O statements.
19 The *interface* to the I/O runtime support library is defined in the
20 C++ header file `runtime/io-api.h`.
21 This interface was designed to minimize the amount of complexity exposed
22 to its clients, which are of course the sequences of calls generated by
23 the compiler to implement each I/O statement.
24 By keeping this interface as simple as possible, we hope that we have
25 lowered the risk of future incompatible changes that would necessitate
26 recompilation of Fortran codes in order to link with later versions of
28 As one will see in `io-api.h`, the interface is also directly callable
29 from C and C++ programs.
31 The I/O facilities of the Fortran 2018 language are specified in the
32 language standard in its clauses 12 (I/O statements) and 13 (`FORMAT`).
33 It's a complicated collection of language features:
34 * Files can comprise *records* or *streams*.
35 * Records can be fixed-length or variable-length.
36 * Record files can be accessed sequentially or directly (random access).
37 * Files can be *formatted*, or *unformatted* raw bits.
38 * `CHARACTER` scalars and arrays can be used as if they were
39 fixed-length formatted sequential record files.
40 * Formatted I/O can be under control of a `FORMAT` statement
41 or `FMT=` specifier, *list-directed* with default formatting chosen
42 by the runtime, or `NAMELIST`, in which a collection of variables
43 can be given a name and passed as a group to the runtime library.
44 * Sequential records of a file can be partially processed by one
45 or more *non-advancing* I/O statements and eventually completed by
47 * `FORMAT` strings can manipulate the position in the current
48 record arbitrarily, causing re-reading or overwriting.
49 * Floating-point output formatting supports more rounding modes
50 than the IEEE standard for floating-point arithmetic.
52 The Fortran I/O runtime support library is written in C++17, and
53 uses some C++17 standard library facilities, but it is intended
54 to not have any link-time dependences on the C++ runtime support
55 library or any LLVM libraries.
56 This is important because there are at least two C++ runtime support
57 libraries, and we don't want Fortran application builders to have to
58 build multiple versions of their codes; neither do we want to require
59 them to ship LLVM libraries along with their products.
61 Consequently, dynamic memory allocation in the Fortran runtime
62 uses only C's `malloc()` and `free()` functions, and the few
63 C++ standard class templates that we instantiate in the library have been
64 modified with optional template arguments that override their
65 allocators and deallocators.
67 Conversions between the many binary floating-point formats supported
68 by f18 and their decimal representations are performed with the same
69 template library of fast conversion algorithms used to interpret
70 floating-point values in Fortran source programs and to emit them
73 ## Overview of Classes
75 A suite of C++ classes and class templates are composed to construct
76 the Fortran I/O runtime support library.
77 They (mostly) reside in the C++ namespace `Fortran::runtime::io`.
78 They are summarized here in a bottom-up order of dependence.
80 The header and C++ implementation source file names of these
81 classes are in the process of being vigorously rearranged and
82 modified; use `grep` or an IDE to discover these classes in
83 the source for now. (Sorry!)
87 A general facility for the entire library, `Terminator` latches a
88 source program statement location in terms of an unowned pointer to
89 its source file path name and line number and uses them to construct
90 a fatal error message if needed.
91 It is used for both user program errors and internal runtime library crashes.
95 When I/O error conditions arise at runtime that the Fortran program
96 might have the privilege to handle itself via `ERR=`, `END=`, or
97 `EOR=` labels and/or by an `IOSTAT=` variable, this subclass of
98 `Terminator` is used to either latch the error indication or to crash.
99 It sorts out priorities in the case of multiple errors and determines
100 the final `IOSTAT=` value at the end of an I/O statement.
104 Fortran's formatted I/O statements are affected by a suite of
105 modes that can be configured by `OPEN` statements, overridden by
106 data transfer I/O statement control lists, and further overridden
107 between data items with control edit descriptors in a `FORMAT` string.
108 These modes are represented with a `MutableModes` instance, and these
109 are instantiated and copied where one would expect them to be in
110 order to properly isolate their modifications.
111 The modes in force at the time each data item is processed constitute
112 a member of each `DataEdit`.
116 Represents a single data edit descriptor from a `FORMAT` statement
117 or `FMT=` character value, with some hidden extensions to also
118 support formatting of list-directed transfers.
119 It holds an instance of `MutableModes`, and also has a repetition
120 count for when an array appears as a data item in the *io-list*.
121 For simplicity and efficiency, each data edit descriptor is
122 encoded in the `DataEdit` as a simple capitalized character
123 (or two) and some optional field widths.
125 ### `FormatControl<>`
127 This class template traverses a `FORMAT` statement's contents (or `FMT=`
128 character value) to extract data edit descriptors like `E20.14` to
129 serve each item in an I/O data transfer statement's *io-list*,
130 making callbacks to an instance of its class template argument
131 along the way to effect character literal output and record
133 The Fortran language standard defines formatted I/O as if the `FORMAT`
134 string were driving the traversal of the data items in the *io-list*,
135 but our implementation reverses that perspective to allow a more
136 convenient (for the compiler) I/O runtime support library API design
137 in which each data item is presented to the library with a distinct
140 Clients of `FormatControl` instantiations call its `GetNextDataEdit()`
141 member function to acquire the next data edit descriptor to be processed
142 from the format, and `FinishOutput()` to flush out any remaining
143 output strings or record positionings at the end of the *io-list*.
145 The `DefaultFormatControlCallbacks` structure summarizes the API
146 expected by `FormatControl` from its class template actual arguments.
150 This class encapsulates all (I hope) the operating system interfaces
151 used to interact with the host's filesystems for operations on
153 Asynchronous I/O interfaces are faked for now with synchronous
154 operations and deferred results.
156 ### `ConnectionState`
158 An active connection to an external or internal unit maintains
159 the common parts of its state in this subclass of `ConnectionAttributes`.
160 The base class holds state that should not change during the
161 lifetime of the connection, while the subclass maintains state
162 that may change during I/O statement execution.
164 ### `InternalDescriptorUnit`
166 When I/O is being performed from/to a Fortran `CHARACTER` array
167 rather than an external file, this class manages the standard
168 interoperable descriptor used to access its elements as records.
169 It has the necessary interfaces to serve as an actual argument
170 to the `FormatControl` class template.
174 This CRTP class template isolates all of the complexity involved between
175 an external unit's `OpenFile` and the buffering requirements
176 imposed by the capabilities of Fortran `FORMAT` control edit
177 descriptors that allow repositioning within the current record.
178 Its interface enables its clients to define a "frame" (my term,
179 not Fortran's) that is a contiguous range of bytes that are
180 or may soon be in the file.
181 This frame is defined as a file offset and a byte size.
182 The `FileFrame` instance manages an internal circular buffer
183 with two essential guarantees:
185 1. The most recently requested frame is present in the buffer
186 and contiguous in memory.
187 1. Any extra data after the frame that may have been read from
188 the external unit will be preserved, so that it's safe to
189 read from a socket, pipe, or tape and not have to worry about
190 repositioning and rereading.
192 In end-of-file situations, it's possible that a request to read
193 a frame may come up short.
195 As a CRTP class template, `FileFrame` accesses the raw filesystem
196 facilities it needs from `*this`.
198 ### `ExternalFileUnit`
200 This class mixes in `ConnectionState`, `OpenFile`, and
201 `FileFrame<ExternalFileUnit>` to represent the state of an open
202 (or soon to be opened) external file descriptor as a Fortran
204 It has the contextual APIs required to serve as a template actual
205 argument to `FormatControl`.
206 And it contains a `std::variant<>` suitable for holding the
207 state of the active I/O statement in progress on the unit
210 `ExternalFileUnit` instances reside in a `Map` that is allocated
211 as a static variable and indexed by Fortran unit number.
212 Static member functions `LookUp()`, `LookUpOrCrash()`, and `LookUpOrCreate()`
213 probe the map to convert Fortran `UNIT=` numbers from I/O statements
214 into references to active units.
216 ### `IoStatementBase`
218 The subclasses of `IoStatementBase` each encapsulate and maintain
219 the state of one active Fortran I/O statement across the several
220 I/O runtime library API function calls it may comprise.
221 The subclasses handle the distinctions between internal vs. external I/O,
222 formatted vs. list-directed vs. unformatted I/O, input vs. output,
225 `IoStatementBase` inherits default `FORMAT` processing callbacks and
227 Each of the `IoStatementBase` classes that pertain to formatted I/O
228 support the contextual callback interfaces needed by `FormatControl`,
229 overriding the default callbacks of the base class, which crash if
230 called inappropriately (e.g., if a `CLOSE` statement somehow
231 passes a data item from an *io-list*).
233 The lifetimes of these subclasses' instances each begin with a user
234 program call to an I/O API routine with a name like `BeginExternalListOutput()`
235 and persist until `EndIoStatement()` is called.
237 To reduce dynamic memory allocation, *external* I/O statements allocate
238 their per-statement state class instances in space reserved in the
239 `ExternalFileUnit` instance.
240 Internal I/O statements currently use dynamic allocation, but
241 the I/O API supports a means whereby the code generated for the Fortran
242 program may supply stack space to the I/O runtime support library
245 ### `IoStatementState`
247 F18's Fortran I/O runtime support library defines and implements an API
248 that uses a sequence of function calls to implement each Fortran I/O
250 The state of each I/O statement in progress is maintained in some
251 subclass of `IoStatementBase`, as noted above.
252 The purpose of `IoStatementState` is to provide generic access
253 to the specific state classes without recourse to C++ `virtual`
254 functions or function pointers, language features that may not be
255 available to us in some important execution environments.
256 `IoStatementState` comprises a `std::variant<>` of wrapped references
257 to the various possibilities, and uses `std::visit()` to
258 access them as needed by the I/O API calls that process each specifier
259 in the I/O *control-list* and each item in the *io-list*.
261 Pointers to `IoStatementState` instances are the `Cookie` type returned
262 in the I/O API for `Begin...` I/O statement calls, passed back for
263 the *control-list* specifiers and *io-list* data items, and consumed
264 by the `EndIoStatement()` call at the end of the statement.
266 Storage for `IoStatementState` is reserved in `ExternalFileUnit` for
267 external I/O units, and in the various final subclasses for internal
268 I/O statement states otherwise.
270 Since Fortran permits a `CLOSE` statement to reference a nonexistent
271 unit, the library has to treat that (expected to be rare) situation
272 as a weird variation of internal I/O since there's no `ExternalFileUnit`
273 available to hold its `IoStatementBase` subclass or `IoStatementState`.
275 ## A Narrative Overview Of `PRINT *, 'HELLO, WORLD'`
277 1. When the compiled Fortran program begins execution at the `main()`
278 entry point exported from its main program, it calls `ProgramStart()`
279 with its arguments and environment.
280 1. The generated code calls `BeginExternalListOutput()` to
281 start the sequence of calls that implement the `PRINT` statement.
282 Since the Fortran runtime I/O library has not yet been used in
283 this process, its data structures are initialized on this
284 first call, and Fortran I/O units 5 and 6 are connected with
285 the stadard input and output file descriptors (respectively).
286 The default unit code is converted to 6 and passed to
287 `ExternalFileUnit::LookUpOrCrash()`, which returns a reference to
289 1. We check that the unit was opened for formatted I/O.
290 1. `ExternalFileUnit::BeginIoStatement<>()` is called to initialize
291 an instance of `ExternalListIoStatementState<false>` in the unit,
292 point to it with an `IoStatementState`, and return a reference to
293 that object whose address will be the `Cookie` for this statement.
294 1. The generated code calls `OutputAscii()` with that cookie and the
295 address and length of the string.
296 1. `OutputAscii()` confirms that the cookie corresponds to an output
297 statement and determines that it's list-directed.
298 1. `ListDirectedStatementState<false>::EmitLeadingSpaceOrAdvance()`
299 emits the required initial space on the new current output record
300 by calling `IoStatementState::GetConnectionState()` to locate
301 the connection state, determining from the record position state
302 that the space is necessary, and calling `IoStatementState::Emit()`
303 to cough it out. That call is redirected to `ExternalFileUnit::Emit()`,
304 which calls `FileFrame<ExternalFileUnit>::WriteFrame()` to extend
305 the frame of the current record and then `memcpy()` to fill its
306 first byte with the space.
307 1. Back in `OutputAscii()`, the mutable modes and connection state
308 of the `IoStatementState` are queried to see whether we're in an
309 `WRITE(UNIT=,FMT=,DELIM=)` statement with a delimited specifier.
310 If we were, the library would emit the appropriate quote marks,
311 double up any instances of that character in the text, and split the
312 text over multiple records if it's long.
313 1. But we don't have a delimiter, so `OutputAscii()` just carves
314 up the text into record-sized chunks and emits them. There's just
315 one chunk for our short `CHARACTER` string value in this example.
316 It's passed to `IoStatementState::Emit()`, which (as above) is
317 redirected to `ExternalFileUnit::Emit()`, which interacts with the
318 frame to extend the frame and `memcpy` data into the buffer.
319 1. A flag is set in `ListDirectedStatementState<false>` to remember
320 that the last item emitted in this list-directed output statement
321 was an undelimited `CHARACTER` value, so that if the next item is
322 also an undelimited `CHARACTER`, no interposing space will be emitted
324 1. `OutputAscii()` return `true` to its caller.
325 1. The generated code calls `EndIoStatement()`, which is redirected to
326 `ExternalIoStatementState<false>`'s override of that function.
327 As this is not a non-advancing I/O statement, `ExternalFileUnit::AdvanceRecord()`
328 is called to end the record. Since this is a sequential formatted
329 file, a newline is emitted.
330 1. If unit 6 is connected to a terminal, the buffer is flushed.
331 `FileFrame<ExternalFileUnit>::Flush()` drives `ExternalFileUnit::Write()`
332 to push out the data in maximal contiguous chunks, dealing with any
333 short writes that might occur, and collecting I/O errors along the way.
334 This statement has no `ERR=` label or `IOSTAT=` specifier, so errors
335 arriving at `IoErrorHandler::SignalErrno()` will cause an immediate
337 1. `ExternalIoStatementBase::EndIoStatement()` is called.
338 It gets the final `IOSTAT=` value from `IoStatementBase::EndIoStatement()`,
339 tells the `ExternalFileUnit` that no I/O statement remains active, and
340 returns the I/O status value back to the program.
341 1. Eventually, the program calls `ProgramEndStatement()`, which
342 calls `ExternalFileUnit::CloseAll()`, which flushes and closes all
343 open files. If the standard output were not a terminal, the output
344 would be written now with the same sequence of calls as above.
345 1. `exit(EXIT_SUCCESS)`.