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