[flang] Document and initiate development of run time descriptors.
authorpeter klausler <pklausler@nvidia.com>
Wed, 16 May 2018 17:22:33 +0000 (10:22 -0700)
committerpeter klausler <pklausler@nvidia.com>
Wed, 16 May 2018 17:22:33 +0000 (10:22 -0700)
Original-commit: flang-compiler/f18@79edea528f2f27deee424cfe967ed71bb523891d
Reviewed-on: https://github.com/flang-compiler/f18/pull/92
Tree-same-pre-rewrite: false

flang/.gitignore
flang/CMakeLists.txt
flang/documentation/C++style.md
flang/documentation/RuntimeDescriptor.md [new file with mode: 0644]
flang/include/flang/ISO_Fortran_binding.h [new file with mode: 0644]
flang/runtime/CMakeLists.txt [new file with mode: 0644]
flang/runtime/ISO_Fortran_binding.cc [new file with mode: 0644]
flang/runtime/descriptor.cc [new file with mode: 0644]
flang/runtime/descriptor.h [new file with mode: 0644]

index 81f373a..0a9c736 100644 (file)
@@ -2,11 +2,13 @@ Debug
 Release
 MinSizeRel
 tags
-./f18
 *.o
 *~
 *#
 CMakeCache.txt
-CMakeFiles/*
+*/CMakeFiles/*
+*/*/CMakeFiles/*
+*/Makefile
+*/*/Makefile
 cmake_install.cmake
 formatted
index c53cbd1..d8d2de0 100644 (file)
@@ -96,6 +96,7 @@ include_directories(BEFORE
 
 add_subdirectory(include/flang)
 add_subdirectory(lib)
+add_subdirectory(runtime)
 add_subdirectory(tools)
 
 configure_file(
index 34bc454..2ffdae0 100644 (file)
@@ -3,7 +3,9 @@ Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
 -->
 
 ## In brief:
-* Use *clang-format* to resolve all layout questions.
+* Use *clang-format* on all C++ source and header files before
+  every merge to master.  All code layout should be determined
+  by means of clang-format.
 * Where LLVM's C++ style guide is clear on usage, follow it.
 * Otherwise, where a clear precedent exists in the project, follow it.
 * Otherwise, where a good public C++ style guide is relevant and clear,
@@ -26,10 +28,11 @@ file actually uses directly.  (Exception: when foo.cc starts, as it should,
 with `#include "foo.h"`, and foo.h includes bar.h in order to define the
 interface to the module foo, you don't have to redundantly `#include "bar.h"`
 in foo.cc.)
-1. In the source file "foo.cc", put the `#include "foo.h"` first.
+1. In the source file "foo.cc", put its corresponding `#include "foo.h"`
+first in the sequence of inclusions.
 Then `#include` other project headers in alphabetic order; then C++ standard
 headers, also alphabetically; then C and system headers.
-1. Don't use `#include <iostream>`.  If you need it for debugging,
+1. Don't use `#include <iostream>`.  If you need it for temporary debugging,
 remove the inclusion before committing.
 ### Naming
 1. C++ names that correspond to STL names should look like those STL names
diff --git a/flang/documentation/RuntimeDescriptor.md b/flang/documentation/RuntimeDescriptor.md
new file mode 100644 (file)
index 0000000..bea9488
--- /dev/null
@@ -0,0 +1,432 @@
+<!--
+Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+-->
+
+## Concept
+The properties that characterize data values and objects in Fortran
+programs must sometimes be materialized when the program runs.
+
+Some properties are known during compilation and constant during
+execution, yet must be reified anyway for execution in order to
+drive the interfaces of a language support library or the mandated
+interfaces of interoperable (i.e., C) procedure calls.
+
+Note that many Fortran intrinsic subprograms have interfaces
+that are more flexible and generic than actual Fortran subprograms
+can be, so properties that must be known during compilation and
+are constant during execution may still need to be materialized
+for calls to the library, even if only by modifying names to
+distinguish types or their kind specializations.
+
+Other properties are deferred to execution, and need to be represented
+to serve the needs of compiled code and the run time support library.
+
+Previous implementations of Fortran have typically defined a small
+sheaf of _descriptor_ data structures for this purpose, and attached
+these descriptors as additional hidden arguments, type components,
+and local variables so as to convey dynamic characteristics between
+subprograms and between user code and the run-time support library.
+
+### References
+References are to the 12-2017 draft of the Fortran 2018 standard
+(N2146).
+
+Section 15.4.2.2 can be interpreted as a decent list of things that
+might need descriptors or other hidden state passed across a
+subprogram call, since such features (apart from assumed-length
+`CHARACTER` function results) trigger a requirement for the
+subprogram to have an explicit interface visible to their callers.
+
+Section 15.5.2 has good laundry lists of situations that can arise
+across subprogram call boundaries.
+
+## A survey of dynamic characteristics
+
+### Length of assumed-length `CHARACTER` function results (B.3.6)
+```
+CHARACTER*8 :: FOO
+PRINT *, FOO('abcdefghijklmnopqrstuvwxyz')
+...
+CHARACTER*(*) FUNCTION FOO(STR)
+  CHARACTER*26 STR
+  FOO=STR
+END
+```
+
+prints `abcdefgh` because the length parameter of the character type
+of the result of `FOO` is passed across the call -- even in the absence
+of an explicit interface!
+
+### Assumed length type parameters (7.2)
+Dummy arguments and associate names for `SELECT TYPE` can have assumed length
+type parameters, which are denoted by asterisks (not colons).
+Their values come from actual arguments or the associated expression (resp.).
+
+### Explicit-shape arrays (8.5.8.2)
+The expressions used for lower and upper bounds must be captured and remain
+invariant over the scope of an array, even if they contain references to
+variables that are later modified.
+
+Explicit-shape arrays can be dummy arguments, "adjustable" local variables,
+and components of derived type (using specification expressions in terms
+of constants and KIND type parameters).
+
+### Leading dimensions of assumed-size arrays (8.5.8.5)
+```
+SUBROUTINE BAR(A)
+  REAL A(2,3,*)
+END
+```
+The total size and final dimension's extent do not constitute dynamic
+properties.
+The called subprogram has no means to extract the extent of the
+last (major) dimension, and may not depend upon it implicitly by using
+the array in any context that demands a known shape.
+
+The values of the expressions used as the bounds of the dimensions
+that appear prior to
+the last dimension are, however, effectively captured on entry to the
+subprogram, and remain invariant even if the variables that appear in
+those expressions have their values modified later.
+This is similar to the requirements for an explicit-shape array.
+
+### Some function results
+1. Deferred-shape
+2. Deferred length type parameter values
+3. Stride information for `POINTER` results
+
+Note that while function result variables can have the `ALLOCATABLE`
+attribute, the function itself and the value returned to the caller
+do not possess the attribute.
+
+### Assumed-shape arrays
+The extents of the dimensions of assumed-shape dummy argument arrays
+are conveyed from those of the actual effective arguments.
+The bounds, however, are not.  The called subprogram can define the
+lower bound to be a value other than 1, but that is a local effect
+only.
+
+### Deferred-shape arrays
+The extents and bounds of `POINTER` and `ALLOCATABLE` arrays are
+established by pointer assignments and `ALLOCATE` statements.
+Note that dummy arguments and function results that are `POINTER`
+or `ALLOCATABLE` can be deferred-shape, not assumed-shape -- one cannot
+supply a lower bound expression as a local effect.
+
+### Strides
+Some arrays can have discontiguous (or negative) strides.
+These include assumed-shape dummy arguments and deferred-shape
+`POINTER` variables, components, and function results.
+
+Fortran disallows some conceivable cases that might otherwise
+require implied strides, such as passing an array of an extended
+derived type as an actual argument that corresponds to a
+nonpolymorphic dummy array of a base type, or the similar
+case of pointer assignment to a base of an extended derived type.
+
+Other arrays, including `ALLOCATABLE`, can be assured to
+be contiguous, and do not necessarily need to manage or
+convey dynamic stride information.
+`CONTIGUOUS` dummy arguments and `POINTER` arrays need not
+record stride information either.
+(The standard notes that a `CONTIGUOUS POINTER` occupies a
+number of storage units that is distinct from that required
+to hold a non-`CONTIGUOUS` pointer.)
+
+Note that Fortran distinguishes the `CONTIGUOUS` attribute from
+the concept of being known or required to be _simply contiguous_ (9.5.4),
+which includes `CONTIGUOUS` entities as well as many others, and
+the concept of actually _being_ contiguous (8.5.7) during execution.
+I believe that the property of being simply contiguous implies
+that an entity is known at compilation time to not require the
+use or maintenance of hidden stride values.
+
+### Derived type component initializers
+Fortran allows components of derived types to be declared with
+initial values that are to be assigned to the components when an
+instance of the derived type is created.
+These include `ALLOCATABLE` components, which are always initialized
+to a deallocated state.
+
+These can be implemented with constructor subroutines, inline
+stores or block copies from static initializer blocks, or a sequence
+of sparse offset/size/value component initializers to be emplaced
+by the run-time library.
+
+N.B. Fortran allows kind type parameters to appear in component
+initialization constant expressions, but not length type parameters,
+so the initialization values are constants.
+
+N.B. Initialization is not assignment, and cannot be implemented
+with assignments to uninitialized derived type instances from
+static constant initializers.
+
+### Polymorphic `CLASS()`, `CLASS(*)`, and `TYPE(*)`
+Type identification for `SELECT TYPE`.
+Default initializers (see above).
+Offset locations of `ALLOCATABLE` and polymorphic components.
+Presence of `FINAL` procedures.
+Mappings to overridable type-bound specific procedures.
+
+### Deferred length type parameters
+Derived types with length type parameters, and `CHARACTER`, may be used
+with the values of those parameters deferred to execution.
+Their actual values must be maintained as characteristics of the dynamic
+type that is associated with a value or object
+.
+A single copy of the deferred length type parameters suffices for
+all of the elements of an array of that parameterized derived type.
+
+### Components whose types and/or shape depends on length type parameters
+Non-pointer, non-allocatable components whose types or shapes are expressed
+in terms of length type parameters will probably have to be implemented as
+if they had deferred type and/or shape and were `ALLOCATABLE`.
+The derived type instance constructor must allocate them and possibly
+initialize them; the instance destructor must deallocate them.
+
+### Assumed rank arrays
+Rank is almost always known at compilation time and would be redundant
+in most circumstances if also managed dynamically.
+`DIMENSION(..)` dummy arguments (8.5.8.7), however, are a recent feature
+with which the rank of a whole array is dynamic outside the cases of
+a `SELECT RANK` construct.
+
+The lower bounds of the dimensions of assumed rank arrays
+are always 1.
+
+### Cached invariant subexpressions for addressing
+Implementations of Fortran have often maintained precalculated integer
+values to accelerate subscript computations.
+For example, given `REAL*8 :: A(2:4,3:5)`, the data reference `A(I,J)`
+resolves to something like `&A + 8*((I-2)+3*(J-3))`, and this can be
+effectively reassociated to `&A - 88 + 8*I + 24*J`
+or `&A - 88 + 8*(I + 3*J)`.
+When the offset term and coefficients are not compile-time constants,
+they are at least invariant and can be precomputed.
+
+In the cases of dummy argument arrays, `POINTER`, and `ALLOCATABLE`,
+these addressing invariants could be managed alongside other dynamic
+information like deferred extents and lower bounds to avoid their
+recalculation.
+It's not clear that it's worth the trouble to do so, since the
+expressions are invariant and cheap.
+
+### Coarray state (8.5.6)
+A _coarray_ is an `ALLOCATABLE` variable or component, or statically
+allocated variable (`SAVE` attribute explicit or implied), or dummy
+argument whose ultimate effective argument is one of such things.
+
+Each image in a team maintains its portion of each coarray and can
+access those portions of the coarray that are maintained by other images
+in the team.
+Allocations and deallocations are synchronization events at which
+the several images can exchange whatever information is needed by
+the underlying intercommunication interface to access the data
+of their peers.
+(Strictly speaking, an implementation could synchronize
+images at allocations and deallocations with simple barriers, and defer
+the communication of remote access information until it is needed for a
+given coarray on a given image, so long as it could be acquired in a
+"one-sided" fashion.)
+
+### Presence of `OPTIONAL` dummy arguments
+Typically indicated with null argument addresses.
+Note that `POINTER` and `ALLOCATABLE` objects can be passed to
+non-`POINTER` non-`ALLOCATABLE` dummy arguments, and their
+association or allocation status (resp.) determines the presence
+of the dummy argument.
+
+### Stronger contiguity enforcement or indication
+Some implementations of Fortran guarantee that dummy argument arrays
+are, or have been made to be, contiguous on one or more dimensions
+when the language does not require them to be so (8.5.7 p2).
+Others pass a flag to identify contiguous arrays (or could pass the
+number of contiguous leading dimensions, although I know of no such
+implementation) so that optimizing transformations that depend on
+contiguity can be made conditional with multiple-version code generation
+and selected during execution.
+
+In the absence of a contiguity guarantee or flag, the called side
+would have to determine contiguity dynamically, if it cares,
+by calculating addresses of elements in the array whose subscripts
+differ by exactly 1 on exactly 1 dimension of interest, and checking
+whether that difference exactly matches the byte size of the type times
+the product of the extents of any prior dimensions.
+
+### Host instances for dummy procedures and procedure pointers
+A static link or other means of accessing the imported state of the
+host procedure must be available when an internal procedure is
+used as an actual argument or as a pointer assignment target.
+
+### Alternate returns
+Subroutines (only) with alternate return arguments need a
+means, such as the otherwise unused function return value, by which
+to distinguish and identify the use of an alternate `RETURN` statement.
+The protocol can be a simple nonzero integer that drives a switch
+in the caller, or the caller can pass multiple return addresses as
+arguments for the callee to substitute on the stack for the original
+return address in the event of an alternate `RETURN`.
+
+## Implementation options
+
+### A note on array descriptions
+Some arrays require dynamic management of distinct combinations of
+values per dimension.
+
+One can extract the extent on a dimension from its bounds, or extract
+the upper bound from the extent and the lower bound.  Having distinct
+extent and upper bound would be redundant.
+
+Contiguous arrays can assume a stride of 1 on each dimension.
+
+Assumed-shape and assumed-size dummy argument arrays need not convey
+lower bounds.
+
+So there are examples of dimensions with
+ * extent only (== upper bound): `CONTIGUOUS` assumed-shape, explict shape and multidimensional assumed-size with constant lower bound
+ * lower bound and either extent or upper bound: `ALLOCATABLE`, `CONTIGUOUS` `POINTER`, general explicit-shape and multidimensional assumed-size
+ * extent (== upper bound) and stride: general (non-`CONTIGUOUS`) assumed-shape
+ * lower bound, stride, and either extent or upper bound: general (non-`CONTIGUOUS`) `POINTER`, assumed-rank
+
+and these cases could be accompanied by precomputed invariant
+addressing subexpressions to accelerate indexing calculations.
+
+### Interoperability requirements
+
+Fortran 2018 requires that a Fortran implementation supply a header file
+`ISO_Fortran_binding.h` for use in C and C++ programs that defines and
+implements an interface to Fortran objects from the _interoperable_
+subset of Fortran objects and their types suitable for use when those
+objects are passed to C functions.
+This interface mandates a fat descriptor that is passed by address,
+containing (at least)
+ * a data base address
+ * explicit rank and type
+ * flags to distinguish `POINTER` and `ALLOCATABLE`
+ * elemental byte size, and
+ * (per-dimension) lower bound, extent, and byte stride
+
+The requirements on the interoperability API do not mandate any
+support for features like derived type component initialization,
+automatic deallocation of `ALLOCATABLE` components, finalization,
+derived type parameters, data contiguity flags, &c.
+But neither does the Standard preclude inclusion of additional
+interfaces to describe and support such things.
+
+Given a desire to fully support the Fortran 2018 language, we need
+to either support the interoperability requirements as a distinct
+specialization of the procedure call protocol, or use the
+`ISO_Fortran_binding.h` header file requirements as a subset basis for a
+complete implementation that adds representations for all the
+missing capabilities, which would be isolated and named so as
+to prevent user C code from relying upon them.
+
+### Design space
+There is a range of possible options for representing the
+properties of values and objects during the execution of Fortran
+programs.
+
+At one extreme, the amount of dynamic information is minimized,
+and is packaged in custom data structures or additional arguments
+for each situation to convey only the values that are unknown at
+compilation time and actually needed at execution time.
+
+At the other extreme, data values and objects are described completely,
+including even the values of properties are known at compilation time.
+This is not as silly as it sounds -- e.g., Fortran array descriptors
+have historically materialized the number of dimensions they cover, even
+though rank will be (nearly) always be a known constant during compilation.
+
+When data are packaged, their containers can be self-describing to
+some degree.
+Description records can have tag values or strings.
+Their fields can have presence flags or identifying tags, and fields
+need not have fixed offsets or ordering.
+This flexibility can increase binary compatibility across revisions
+of the run-time support library, and is convenient for debugging
+that library.
+However, it is not free.
+
+Further, the requirements of the representation of dynamic
+properties of values and objects depend on the execution model:
+specifically, are the complicated semantics of intrinsic assignment,
+deallocation, and finalization of allocatables implemented entirely
+in the support library, in generated code for non-recursive cases,
+or by means of a combination of the two approaches?
+
+Consider how to implement the following:
+```
+TYPE :: LIST
+  REAL :: HEAD
+  TYPE(LIST), ALLOCATABLE :: REST
+END TYPE LIST
+TYPE(LIST), ALLOCATABLE :: A, B
+...
+A = B
+```
+
+Fortran requires that `A`'s arbitrary-length linked list be deleted and
+replaced with a "deep copy" of `B`'s.
+So either a complicated pair of loops must be generated by the compiler,
+or a sophisticated run time support library needs to be driven with
+an expressive representation of type information.
+
+## Proposal
+We need to write `ISO_Fortran_binding.h` in any event.
+It is a header that is published for use in user C code for interoperation
+with compiled Fortran and the Fortran run time support library.
+
+There is a sole descriptor structure defined in `ISO_Fortran_binding.h`.
+It is suitable for characterizing scalars and array sections of intrinsic
+types.
+It is essentially a "fat" data pointer that encapsulates a raw data pointer,
+a type code, rank, elemental byte size, and per-dimension bounds and stride.
+
+Please note that the mandated interoperable descriptor includes the data
+pointer.
+This design in the Standard precludes the use of static descriptors that
+could be associated with dynamic base addresses.
+
+The F18 runtime cannot use just the mandated interoperable
+`struct CFI_cdesc_t` argument descriptor structure as its
+all-purpose data descriptor.
+It has no information about derived type components, overridable
+type-bound procedure bindings, type parameters, &c.
+
+However, we could extend the standard interoperable argument descriptor.
+The `struct CFI_cdesc_t` structure is not of fixed size, but we
+can efficiently locate the first address after an instance of the
+standard descriptor and attach our own data record there to
+hold what we need.
+There's at least one unused padding byte in the standard argument
+descriptor that can be used to hold a flag indicating the presence
+of the addenda.
+
+The definitions of our additional run time data structures must
+appear in a header file that is distinct from `ISO_Fortran_binding.h`,
+and they should never be used by user applications.
+
+This expanded descriptor structure can serve, at least initially for
+simplicity, as the sole representation of `POINTER` variables and
+components, `ALLOCATABLE` variables and components, and derived type
+instances, including length parameter values.
+
+An immediate concern with this concept is the amount of space and
+initialization time that would be wasted when derived type components
+needing a descriptor would have to be accompanied by an instance
+of the general descriptor.
+(In the linked list example close above, what could be done with a
+single pointer for the `REST` component would become at least
+a four-word dynamic structure.)
+This concern is amplified when derived type instances
+are allocated as arrays, since the overhead is per-element.
+
+We can reduce this wastage in two ways.
+First, when the content of the component's descriptor is constant
+at compilation apart from its base address, a static descriptor
+can be placed in read-only storage and attached to the description
+of the derived type's components.
+Second, we could eventually optimize the storage requirements by
+omitting all static fields from the dynamic descriptor, and
+expand the compressed dynamic descriptor during execution when
+needed.
diff --git a/flang/include/flang/ISO_Fortran_binding.h b/flang/include/flang/ISO_Fortran_binding.h
new file mode 100644 (file)
index 0000000..65b1a67
--- /dev/null
@@ -0,0 +1,155 @@
+/* Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ISO_FORTRAN_BINDING_H_
+#define ISO_FORTRAN_BINDING_H_
+
+#include <stddef.h>
+
+/* Standard interface to Fortran from C and C++.
+ * These interfaces are named in section 18.5 of the Fortran 2018
+ * standard, with most of the actual details being left to the
+ * implementation.
+ */
+
+#ifdef __cplusplus
+// C++ does not support flexible array members, so they have to be
+// declared with single elements.
+# define ISO_FORTRAN_BINDING_FLEXIBLE_ARRAY 1
+namespace Fortran::ISO {
+inline namespace Fortran_2018 {
+#else
+# define ISO_FORTRAN_BINDING_FLEXIBLE_ARRAY
+#endif
+
+/* 18.5.4 */
+#define CFI_VERSION 20180515
+
+#define CFI_MAX_RANK 15
+typedef unsigned char CFI_rank_t;
+
+// This type is probably larger than a default Fortran INTEGER
+// and should be used for all array indexing and loop bound calculations.
+typedef ptrdiff_t CFI_index_t;
+
+#define CFI_DESC_T(rank) struct { \
+    CFI_cdesc_t cdesc;  /* must be first */ \
+    CFI_dim_t dim[rank]; \
+  };
+
+typedef unsigned short CFI_attribute_t;
+#define CFI_attribute_pointer 1
+#define CFI_attribute_allocatable 2
+#define CFI_attribute_other 0  /* neither pointer nor allocatable */
+
+typedef signed char CFI_type_t;
+/* These codes are required to be macros (i.e., #ifdef will work).
+ * They are not required to be either distinct nor to have had their
+ * synonyms combined.
+ */
+#define CFI_type_signed_char 1
+#define CFI_type_short 2
+#define CFI_type_int 3
+#define CFI_type_long 4
+#define CFI_type_long_long 5
+#define CFI_type_size_t 6
+#define CFI_type_int8_t 7
+#define CFI_type_int16_t 8
+#define CFI_type_int32_t 9
+#define CFI_type_int64_t 10
+#define CFI_type_int_least8_t 11
+#define CFI_type_int_least16_t 12
+#define CFI_type_int_least32_t 13
+#define CFI_type_int_least64_t 14
+#define CFI_type_int_fast8_t 15
+#define CFI_type_int_fast16_t 16
+#define CFI_type_int_fast32_t 17
+#define CFI_type_int_fast64_t 18
+#define CFI_type_intmax_t 19
+#define CFI_type_intptr_t 20
+#define CFI_type_ptrdiff_t 21
+#define CFI_type_float 22
+#define CFI_type_double 23
+#define CFI_type_long_double 24
+#define CFI_type_float_Complex 25
+#define CFI_type_double_Complex 26
+#define CFI_type_long_double_Complex 27
+#define CFI_type_Bool 28
+#define CFI_type_char 29
+#define CFI_type_cptr 30
+#define CFI_type_struct 31
+#define CFI_type_other (-1)  /* must be negative */
+
+/* Error code macros */
+#define CFI_SUCCESS 0
+#define CFI_ERROR_BASE_ADDR_NULL 1
+#define CFI_ERROR_BASE_ADDR_NOT_NULL 2
+#define CFI_INVALID_ELEM_LEN 3
+#define CFI_INVALID_RANK 4
+#define CFI_INVALID_TYPE 5
+#define CFI_INVALID_ATTRIBUTE 6
+#define CFI_INVALID_EXTENT 7
+#define CFI_INVALID_DESCRIPTOR 8
+#define CFI_ERROR_MEM_ALLOCATION 9
+#define CFI_ERROR_OUT_OF_BOUNDS 10
+
+/* 18.5.2 per-dimension information */
+typedef struct CFI_dim_t {
+  CFI_index_t lower_bound;
+  CFI_index_t extent;  /* == -1 for assumed size */
+  CFI_index_t sm;  /* memory stride in bytes */
+} CFI_dim_t;
+
+/* 18.5.3 generic data descriptor */
+typedef struct CFI_cdesc_t {
+  /* These three members must be appear first, in exactly this order. */
+  void *base_addr;
+  size_t elem_len;  /* element size in bytes */
+  int version;  /* == CFI_VERSION */
+  CFI_rank_t rank;  /* [0 .. CFI_MAX_RANK] */
+  CFI_type_t type;
+  CFI_attribute_t attribute;
+  CFI_dim_t dim[ISO_FORTRAN_BINDING_FLEXIBLE_ARRAY];  /* must appear last */
+} CFI_cdesc_t;
+
+/* 18.5.5 procedural interfaces*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *CFI_address(const CFI_cdesc_t *, const CFI_index_t subscripts[]);
+int CFI_allocate(CFI_cdesc_t *, const CFI_index_t lower_bounds[],
+                 const CFI_index_t upper_bounds[], size_t elem_len);
+int CFI_deallocate(CFI_cdesc_t *);
+int CFI_establish(CFI_cdesc_t *, void *base_addr, CFI_attribute_t,
+                  CFI_type_t, size_t elem_len, CFI_rank_t,
+                  const CFI_index_t extents[]);
+int CFI_is_contiguous(const CFI_cdesc_t *);
+int CFI_section(CFI_cdesc_t *, const CFI_cdesc_t *source,
+                const CFI_index_t lower_bounds[],
+                const CFI_index_t upper_bounds[],
+                const CFI_index_t strides[]);
+int CFI_select_part(CFI_cdesc_t *, const CFI_cdesc_t *source,
+                    size_t displacement, size_t elem_len);
+int CFI_setpointer(CFI_cdesc_t *, const CFI_cdesc_t *source,
+                   const CFI_index_t lower_bounds[]);
+#ifdef __cplusplus
+}  // extern "C"
+}  // namespace Fortran_2018
+}  // namespace Fortran::ISO
+#endif
+
+#undef ISO_FORTRAN_BINDING_FLEXIBLE_ARRAY
+
+#endif  /* ISO_FORTRAN_BINDING_H_ */
diff --git a/flang/runtime/CMakeLists.txt b/flang/runtime/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5a4276a
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_library(FortranRuntime
+  ISO_Fortran_binding.cc
+)
diff --git a/flang/runtime/ISO_Fortran_binding.cc b/flang/runtime/ISO_Fortran_binding.cc
new file mode 100644 (file)
index 0000000..a63943f
--- /dev/null
@@ -0,0 +1,152 @@
+// Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Implements the required interoperability API from ISO_Fortran_binding.h
+// as specified in section 18.5.5 of Fortran 2018.
+
+#include "descriptor.h"
+#include <cstdlib>
+
+namespace Fortran::ISO {
+extern "C" {
+
+void *CFI_address(const CFI_cdesc_t *descriptor,
+                  const CFI_index_t subscripts[]) {
+  auto p = reinterpret_cast<char *>(descriptor->base_addr);
+  std::size_t rank{descriptor->rank};
+  const CFI_dim_t *dim{descriptor->dim};
+  for (std::size_t j{0}; j < rank; ++j, ++dim) {
+    p += (subscripts[j] - dim->lower_bound) * dim->sm;
+  }
+  return reinterpret_cast<void *>(p);
+}
+
+int CFI_allocate(CFI_cdesc_t *descriptor, const CFI_index_t lower_bounds[],
+                 const CFI_index_t upper_bounds[], std::size_t elem_len) {
+  if (descriptor->version != CFI_VERSION) {
+    return CFI_INVALID_DESCRIPTOR;
+  }
+  if ((descriptor->attribute &
+      ~(CFI_attribute_pointer | CFI_attribute_allocatable)) != 0) {
+    // Non-interoperable object
+    return CFI_INVALID_DESCRIPTOR;
+  }
+  if (descriptor->base_addr != nullptr) {
+    return CFI_ERROR_BASE_ADDR_NOT_NULL;
+  }
+  if (descriptor->rank > CFI_MAX_RANK) {
+    return CFI_INVALID_RANK;
+  }
+  // TODO: CFI_INVALID_TYPE?
+  if (descriptor->type != CFI_type_cptr) {
+    elem_len = descriptor->elem_len;
+    if (elem_len <= 0) {
+      return CFI_INVALID_ELEM_LEN;
+    }
+  }
+  std::size_t rank{descriptor->rank};
+  CFI_dim_t *dim{descriptor->dim};
+  std::size_t byteSize{elem_len};
+  for (std::size_t j{0}; j < rank; ++j, ++dim) {
+    CFI_index_t lb{lower_bounds[j]};
+    CFI_index_t ub{upper_bounds[j]};
+    CFI_index_t extent{ub >= lb ? ub - lb + 1 : 0};
+    dim->lower_bound = lb;
+    dim->extent = extent;
+    dim->sm = byteSize;
+    byteSize *= extent;
+  }
+  void *p{std::malloc(byteSize)};
+  if (p == nullptr) {
+    return CFI_ERROR_MEM_ALLOCATION;
+  }
+  descriptor->base_addr = p;
+  return CFI_SUCCESS;
+}
+
+int CFI_deallocate(CFI_cdesc_t *descriptor) {
+  if (descriptor->version != CFI_VERSION) {
+    return CFI_INVALID_DESCRIPTOR;
+  }
+  if ((descriptor->attribute &
+      ~(CFI_attribute_pointer | CFI_attribute_allocatable)) != 0) {
+    // Non-interoperable object
+    return CFI_INVALID_DESCRIPTOR;
+  }
+  if (descriptor->base_addr == nullptr) {
+    return CFI_ERROR_BASE_ADDR_NULL;
+  }
+  std::free(descriptor->base_addr);
+  descriptor->base_addr = nullptr;
+  return CFI_SUCCESS;
+}
+
+int CFI_establish(CFI_cdesc_t *descriptor, void *base_addr,
+                  CFI_attribute_t attribute, CFI_type_t type,
+                  std::size_t elem_len, CFI_rank_t rank,
+                  const CFI_index_t extents[]) {
+  if ((attribute & ~(CFI_attribute_pointer | CFI_attribute_allocatable)) != 0) {
+    return CFI_INVALID_ATTRIBUTE;
+  }
+  if ((attribute & CFI_attribute_allocatable) != 0 &&
+      base_addr != nullptr) {
+    return CFI_ERROR_BASE_ADDR_NOT_NULL;
+  }
+  if (rank > CFI_MAX_RANK) {
+    return CFI_INVALID_RANK;
+  }
+  if (rank > 0 && base_addr != nullptr && extents == nullptr) {
+    return CFI_INVALID_EXTENT;
+  }
+  if (type != CFI_type_struct && type != CFI_type_other &&
+      type != CFI_type_cptr) {
+    // TODO: force value of elem_len
+  }
+  descriptor->base_addr = base_addr;
+  descriptor->elem_len = elem_len;
+  descriptor->version = CFI_VERSION;
+  descriptor->rank = rank;
+  descriptor->attribute = attribute;
+  std::size_t byteSize{elem_len};
+  for (std::size_t j{0}; j < rank; ++j) {
+    descriptor->dim[j].lower_bound = 1;
+    descriptor->dim[j].extent = extents[j];
+    descriptor->dim[j].sm = byteSize;
+    byteSize *= extents[j];
+  }
+  return CFI_SUCCESS;
+}
+
+int CFI_is_contiguous(const CFI_cdesc_t *descriptor) {
+  return 0;  // TODO
+}
+
+int CFI_section(CFI_cdesc_t *result, const CFI_cdesc_t *source,
+                const CFI_index_t lower_bounds[],
+                const CFI_index_t upper_bounds[],
+                const CFI_index_t strides[]) {
+  return CFI_INVALID_DESCRIPTOR;  // TODO
+}
+
+int CFI_select_part(CFI_cdesc_t *result, const CFI_cdesc_t *source,
+                    std::size_t displacement, std::size_t elem_len) {
+  return CFI_INVALID_DESCRIPTOR;  // TODO
+}
+
+int CFI_setpointer(CFI_cdesc_t *result, const CFI_cdesc_t *source,
+                   const CFI_index_t lower_bounds[]) {
+  return CFI_INVALID_DESCRIPTOR;  // TODO
+}
+}  // extern "C"
+}  // namespace Fortran::ISO
diff --git a/flang/runtime/descriptor.cc b/flang/runtime/descriptor.cc
new file mode 100644 (file)
index 0000000..e28bde0
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// TODO: Not complete; exists to check compilability of descriptor.h
+
+#include "descriptor.h"
+
+namespace Fortran::runtime {
+
+Descriptor::Descriptor(const DerivedType &t, int rank = 0) {
+  raw_.base_addr = nullptr;
+  raw_.elem_len = t.SizeInBytes();
+  raw_.version = CFI_VERSION;
+  raw_.rank = rank;
+  raw_.type = CFI_type_struct;
+  raw_.attribute = ADDENDUM;
+  new(GetAddendum()) DescriptorAddendum{t};
+}
+
+std::size_t Descriptor::SizeInBytes() const {
+  const DescriptorAddendum *addendum{GetAddendum()};
+  return sizeof *this + raw_.rank * sizeof(Dimension) +
+         (addendum ? addendum->AddendumSizeInBytes() : 0);
+}
+
+std::int64_t DerivedTypeParameter::Value(const DescriptorAddendum *addendum) const {
+  if (isLenTypeParameter_) {
+    return addendum->GetLenParameterValue(value_);
+  } else {
+    return value_;
+  }
+}
+
+std::int64_t DerivedTypeParameter::Value(const Descriptor *descriptor) const {
+  if (isLenTypeParameter_) {
+    return descriptor->GetAddendum()->GetLenTypeParameterValue(value_);
+  } else {
+    return value_;
+  }
+}
+}  // namespace Fortran::runtime
diff --git a/flang/runtime/descriptor.h b/flang/runtime/descriptor.h
new file mode 100644 (file)
index 0000000..8766cba
--- /dev/null
@@ -0,0 +1,323 @@
+// Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef FORTRAN_RUNTIME_DESCRIPTOR_H_
+#define FORTRAN_RUNTIME_DESCRIPTOR_H_
+
+// Defines data structures used during execution of a Fortran program
+// to implement pointers, allocatables, arguments, function results,
+// and the special behaviors of instances of derived types.
+// This header file includes and extends the published language
+// interoperability header that is required by the Fortran 2018 standard
+// as a subset of definitions suitable for exposure to user C/C++ code.
+// User C code can depend on that ISO_Fortran_binding.h file, but should
+// never reference this internal header.
+
+#include "../include/flang/ISO_Fortran_binding.h"
+#include <cinttypes>
+#include <cstddef>
+
+namespace Fortran::runtime {
+
+// Fortran requires that default INTEGER values occupy a single numeric
+// storage unit, just like default REAL.  So the default INTEGER type,
+// which is what the type of an intrinsic type's KIND type parameter has,
+// is basically forced to be a 32-bit int.
+using DefaultKindInteger = std::int32_t;
+
+class DerivedType;
+class DescriptorAddendum;
+
+// A C++ view of the ISO descriptor and its type and per-dimension information.
+
+class TypeCode {
+public:
+  enum class Form {
+    Integer, Real, Complex, Logical, Character, DerivedType
+  };
+
+  TypeCode() {}
+  explicit TypeCode(ISO::CFI_type_t t) : raw_{t} {}
+  int raw() const { return raw_; }
+
+  constexpr bool IsValid() const {
+    return raw_ >= CFI_type_signed_char && raw_ <= CFI_type_struct;
+  }
+  constexpr bool IsInteger() const {
+    return raw_ >= CFI_type_signed_char && raw_ <= CFI_type_ptrdiff_t;
+  }
+  constexpr bool IsReal() const {
+    return raw_ >= CFI_type_float && raw_ <= CFI_type_long_double;
+  }
+  constexpr bool IsComplex() const {
+    return raw_ >= CFI_type_float_Complex && raw_ <= CFI_type_long_double_Complex;
+  }
+  constexpr bool IsLogical() const {
+    return raw_ == CFI_type_Bool;
+  }
+  constexpr bool IsCharacter() const {
+    return raw_ == CFI_type_cptr;
+  }
+  constexpr bool IsDerivedType() const { return raw_ == CFI_type_struct; }
+
+  constexpr bool IsIntrinsic() const {
+    return raw_ >= CFI_type_signed_char && raw_ <= CFI_type_cptr;
+  }
+
+  constexpr Form GetForm() const {
+    if (IsInteger()) { return Form::Integer; }
+    if (IsReal()) { return Form::Real; }
+    if (IsComplex()) { return Form::Complex; }
+    if (IsLogical()) { return Form::Logical; }
+    if (IsCharacter()) { return Form::Character; }
+    return Form::DerivedType;
+  }
+
+private:
+  ISO::CFI_type_t raw_{CFI_type_other};
+};
+
+class Dimension {
+public:
+  std::int64_t LowerBound() const { return raw_.lower_bound; }
+  std::int64_t Extent() const { return raw_.extent; }
+  std::int64_t UpperBound() const { return LowerBound() + Extent() - 1; }
+  std::int64_t ByteStride() const { return raw_.sm; }
+private:
+  ISO::CFI_dim_t raw_;  // must be first and only member
+};
+
+class Descriptor {
+public:
+  Descriptor(TypeCode t, std::size_t elementBytes, int rank = 0) {
+    raw_.base_addr = nullptr;
+    raw_.elem_len = elementBytes;
+    raw_.version = CFI_VERSION;
+    raw_.rank = rank;
+    raw_.type = t.raw();
+    raw_.attribute = 0;
+  }
+  Descriptor(const DerivedType &, int rank = 0);
+
+  void Check() const;
+
+  template<typename A> A &Element(std::size_t offset = 0) const {
+    auto p = reinterpret_cast<char *>(raw_.base_addr);
+    return *reinterpret_cast<A *>(p + offset);
+  }
+
+  std::size_t ElementBytes() const { return raw_.elem_len; }
+  int rank() const { return raw_.rank; }
+  TypeCode type() const { return TypeCode{raw_.type}; }
+
+  bool IsPointer() const {
+    return (raw_.attribute & CFI_attribute_pointer) != 0;
+  }
+  bool IsAllocatable() const {
+    return (raw_.attribute & CFI_attribute_allocatable) != 0;
+  }
+  bool IsLenParameterDependent() const {
+    return (raw_.attribute & LEN_PARAMETER_DEPENDENT) != 0;
+  }
+  bool IsStaticDescriptor() const {
+    return (raw_.attribute & STATIC_DESCRIPTOR) != 0;
+  }
+  bool IsTarget() const {
+    return (raw_.attribute & (CFI_attribute_pointer | TARGET)) != 0;
+  }
+  bool IsContiguous() const {
+    return (raw_.attribute & CONTIGUOUS) != 0;
+  }
+  bool IsNotFinalizable() const {
+    return (raw_.attribute & NOT_FINALIZABLE) != 0;
+  }
+
+  const Dimension &GetDimension(int dim) const {
+    return *reinterpret_cast<const Dimension *>(&raw_.dim[dim]);
+  }
+
+  const DescriptorAddendum *GetAddendum() const {
+    if ((raw_.attribute & ADDENDUM) != 0) {
+      return reinterpret_cast<const DescriptorAddendum *>(&GetDimension(rank()));
+    } else {
+      return nullptr;
+    }
+  }
+
+  std::size_t SizeInBytes() const;
+
+private:
+  // These values must coexist with the ISO_Fortran_binding.h definitions
+  // for CFI_attribute_...
+  enum AdditionalAttributes {
+    // non-pointer nonallocatable derived type component implemented as
+    // an implicit allocatable due to dependence on LEN type parameters
+    LEN_PARAMETER_DEPENDENT = 0x4,  // implicitly allocated object
+    ADDENDUM = 0x8,  // last dim[] entry is followed by DescriptorAddendum
+    STATIC_DESCRIPTOR = 0x10,  // base_addr is null, get base address elsewhere
+    TARGET = 0x20,  // TARGET attribute; also implied by CFI_attribute_pointer
+    CONTIGUOUS = 0x40,
+    NOT_FINALIZABLE = 0x80,  // do not finalize, this is a compiler temp
+  };
+
+  ISO::CFI_cdesc_t raw_;  // must be first and only member
+};
+
+// Static type information resides in a read-only section.
+// Information about intrinsic types is inferable from raw CFI_type_t
+// type codes (packaged as TypeCode above).
+// Information about derived types and their KIND parameter specializations
+// appears in the compiled program units that define or specialize the types.
+
+class DerivedTypeParameter {
+public:
+  const char *name() const { return name_; }
+  const TypeCode type() const { return typeCode_; }
+  std::int64_t Value(const DescriptorAddendum *) const;
+  std::int64_t Value(const Descriptor *) const;
+private:
+  const char *name_;
+  TypeCode typeCode_;  // not necessarily default INTEGER
+  bool isLenTypeParameter_;  // if true, value is in dynamic descriptor
+  std::int64_t value_;  // truncated to type then sign-extended
+};
+
+// Components that have any need for a descriptor will either reference
+// a static descriptor that applies to all instances, or will *be* a
+// descriptor.  Be advised: the base addresses in static descriptors
+// are null.  Most runtime interfaces separate the data address from that
+// of the descriptor, and ignore the encapsulated base address in the
+// descriptor.  Some interfaces, e.g. calls to interoperable procedures,
+// cannot pass a separate data address, and any static descriptor being used
+// in that kind of situation must be copied and customized.
+// Static descriptors are flagged in their attributes.
+class Component {
+public:
+  const char *name() const { return name_; }
+  template<typename A> A *Locate(char *instance) const {
+    return reinterpret_cast<A *>(instance + offset_);
+  }
+  template<typename A> const A *Locate(const char *instance) const {
+    return reinterpret_cast<const A *>(instance + offset_);
+  }
+  TypeCode typeCode() const { return typeCode_; }
+  bool IsPrivate() const { return (flags_ & PRIVATE) != 0; }
+  const Descriptor *GetDescriptor(const char *instance) const {
+    if (staticDescriptor_ != nullptr) {
+      return staticDescriptor_;
+    } else if ((flags_ & IS_DESCRIPTOR) != 0) {
+      return Locate<const Descriptor>(instance);
+    } else {
+      return nullptr;
+    }
+  }
+
+private:
+  enum Flag { PRIVATE=1, IS_DESCRIPTOR=2 };
+
+  const char *name_{nullptr};
+  std::size_t offset_{0};  // relative to start of derived type instance
+  std::uint32_t flags_{0};
+  TypeCode typeCode_{CFI_type_other};
+  const Descriptor *staticDescriptor_{nullptr};
+};
+
+struct ExecutableCode {
+  void (*host)(Descriptor *);
+  void (*device)(Descriptor *);
+};
+
+struct TypeBoundProcedure {
+  const char *name;
+  ExecutableCode code;
+};
+
+struct ProcedurePointer {
+  ExecutableCode entryAddresses;
+  void *staticLink;
+};
+
+// This static representation of a derived type specialization includes
+// the values of all its KIND type parameters, and reflects those values
+// in the values of array bounds and static derived type descriptors that
+// appear in the static descriptors of the components.
+// Extended derived types have the EXTENDS flag set and place their base
+// component first in the component descriptions, which is significant for
+// the execution of FINAL subroutines.
+class DerivedType {
+public:
+  const char *name() const { return name_; }
+  std::size_t SizeInBytes() const { return bytes_; }
+  const char *initializer() const { return initializer_; }
+  std::size_t kindParameters() const { return kindParameters_; }
+  const DerivedTypeParameter &kindParameter(int n) const {
+    return kindParameter_[n];
+  }
+  std::size_t lenParameters() const { return lenParameters_; }
+  std::size_t components() const { return components_; }
+  const Component &component(int n) const { return component_[n]; }
+  std::size_t typeBoundProcedures() const { return typeBoundProcedures_; }
+  const ExecutableCode &typeBoundProcedure(int n) const {
+    return typeBoundProcedure_[n];
+  }
+  const ExecutableCode &finalSubroutine() const { return finalSubroutine_; }
+
+private:
+  const char *name_;  // NUL-terminated constant text
+  std::size_t bytes_;  // allocation size of one scalar instance, w/ alignment
+  enum Flag { EXTENDS=1, SEQUENCE=2, BIND=4, ANY_PRIVATE=8 };
+
+  std::uint64_t flags_;  // needed for TYPE IS comparison
+  const char *initializer_;  // can be null; includes base components
+  std::size_t kindParameters_;
+  const DerivedTypeParameter *kindParameter_;  // array
+  std::size_t lenParameters_;  // count only; values are in descriptor
+  std::size_t components_;  // *not* including type parameters
+  const Component *component_;  // array
+  std::size_t typeBoundProcedures_;
+  const ExecutableCode *typeBoundProcedure_;  // array of overridable TBP bindings
+  ExecutableCode finalSubroutine_;  // resolved at compilation, can be null
+};
+
+// The storage for this object follows the last used dim[] entry in a
+// Descriptor (CFI_cdesc_t) generic descriptor; that is why this class
+// cannot be defined as a derivation or encapsulation of the standard
+// argument descriptor.  Space matters here, since dynamic descriptors
+// can serve as components of derived type instances.  The presence of
+// this structure is implied by (CFI_cdesc_t.attribute & ADDENDUM) != 0,
+// and the number of elements in the len_[] array is determined by
+// derivedType_->lenParameters().
+class DescriptorAddendum {
+public:
+  explicit DescriptorAddendum(const DerivedType *dt) : derivedType_{dt} {}
+
+  const DerivedType *derivedType() const { return derivedType_; }
+  std::int64_t GetLenParameterValue(std::size_t n) const {
+    return len_[n];
+  }
+  std::size_t SizeOfAddendumInBytes() const {
+    return sizeof *this - sizeof len_[0] +
+           derivedType_->lenParameters() * sizeof len_[0];
+  }
+
+private:
+  const DerivedType *derivedType_{nullptr};
+  std::int64_t len_[1];  // must be the last component
+  // The LEN type parameter values can also include captured values of
+  // specification expressions that were used for bounds and for LEN type
+  // parameters of components.  The values have been truncated to the LEN
+  // type parameter's type, if shorter than 64 bits, then sign-extended.
+};
+}  // namespace Fortran::runtime
+#endif  // __cplusplus#endif  // FORTRAN_RUNTIME_DESCRIPTOR_H_