[flang-commits] [flang] 9b90700 - [flang][NFC] Update polymorphic design document to reflect implementation
Valentin Clement via flang-commits
flang-commits at lists.llvm.org
Thu Apr 6 08:43:17 PDT 2023
Author: Valentin Clement
Date: 2023-04-06T08:43:07-07:00
New Revision: 9b907001ab8cf88b9092e0ec6bf95ee37ccb9a38
URL: https://github.com/llvm/llvm-project/commit/9b907001ab8cf88b9092e0ec6bf95ee37ccb9a38
DIFF: https://github.com/llvm/llvm-project/commit/9b907001ab8cf88b9092e0ec6bf95ee37ccb9a38.diff
LOG: [flang][NFC] Update polymorphic design document to reflect implementation
Update the design document to reflect the actual implementation
of polymorphic entities.
Reviewed By: jeanPerier, PeteSteinfeld
Differential Revision: https://reviews.llvm.org/D147665
Added:
Modified:
flang/docs/PolymorphicEntities.md
Removed:
################################################################################
diff --git a/flang/docs/PolymorphicEntities.md b/flang/docs/PolymorphicEntities.md
index 1227bdce805d..3935861b6207 100644
--- a/flang/docs/PolymorphicEntities.md
+++ b/flang/docs/PolymorphicEntities.md
@@ -55,10 +55,8 @@ the best statically available at compile time.
`!fir.class` is a new type introduced for polymorphic entities. It's similar to
a box type but allows the distinction between a monomorphic and a polymorphic
descriptor.
-A specific `BoxTypeInterface` (TypeInterface) can be introduced to share the
-same API for both types where it is necessary. `!fir.class` and `!fir.box` can
-also be based on a same `BaseBoxType` similar to the `BaseMemRefType` done for
-MemRef.
+`!fir.class` and `!fir.box` are based on a same `BaseBoxType` similar to the
+`BaseMemRefType` done for MemRef.
**Fortran**
```fortran
@@ -140,7 +138,7 @@ class is (point)
type is (point_3d)
print*, a%x, a%y, a%z
class default
- print*,
+ print*,'default'
end select
```
@@ -165,9 +163,9 @@ The `CLASS DEFAULT` type guard statement is represented by a `unit` attribute.
**FIR**
```
-fir.select_type %p : !fir.class<!fir.type<_QTpoint{x:f32,y:f32}>> [
- #fir.class_is<!fir.type<_QTpoint{x:f32,y:f32}>>, ^bb1,
- #fir.type_is<!fir.type<_QTpoint_3d{x:f32,y:f32,z:f32}>>, ^bb2,
+fir.select_type %6 : !fir.class<!fir.ptr<!fir.type<_QFTpoint{x:f32,y:f32}>>> [
+ #fir.class_is<!fir.type<_QFTpoint{x:f32,y:f32}>>, ^bb1,
+ #fir.type_is<!fir.type<_QFTpoint_3d{x:f32,y:f32,z:f32}>>, ^bb2,
unit, ^bb3]
```
@@ -175,55 +173,81 @@ Lowering of the `fir.select_type` operation will produce a if-then-else ladder.
The testing of the dynamic type of the selector is done by calling runtime
functions.
-The runtime has two functions to compare dynamic types . Note that this two
-functions _ignore_ the values of `KIND` type parameters. A version of these
-functions that does not _ignore_ the value of the `KIND` type parameters will
-be implemented for the `SELECT TYPE` type guards testing.
+The runtime has two functions to compare dynamic types. Note that these two
+functions _ignore_ the values of `KIND` type parameters.
-Currently available functions for the `EXTENDS_TYPE_OF` and `SAME_TYPE_AS`
-intrinsics (`flang/include/flang/Evaluate/type.h`).
+The functions for the `EXTENDS_TYPE_OF` and `SAME_TYPE_AS`
+intrinsics (`flang/include/flang/Runtime/derived-api.h`).
```cpp
-std::optional<bool> ExtendsTypeOf(const DynamicType &) const;
-std::optional<bool> SameTypeAs(const DynamicType &) const;
+// Perform the test of the SAME_TYPE_AS intrinsic.
+bool RTNAME(SameTypeAs)(const Descriptor &, const Descriptor &);
+
+// Perform the test of the EXTENDS_TYPE_OF intrinsic.
+bool RTNAME(ExtendsTypeOf)(const Descriptor &, const Descriptor &);
+```
+
+For the `SELECT TYPE` construct, the `KIND` type parameter is not ignored. The
+`TYPE IS` type guard statement is lowered to an inlined comparison. The
+`CLASS IS` type guard statement is lowered to a runtime function call.
+
+The function `ClassIs` implements the dynamic type comparison.
+(`flang/include/flang/Runtime/derived-api.h`).
+```cpp
+// Perform the test of the CLASS IS type guard statement of the SELECT TYPE
+// construct.
+bool RTNAME(ClassIs)(const Descriptor &, const typeInfo::DerivedType &);
```
**FIR** (lower level FIR/MLIR after conversion to an if-then-else ladder)
```
module {
- func @f(%arg0: !fir.class<*>) -> i32 {
- %c4_i32 = arith.constant 4 : i32
- %c8_i32 = arith.constant 8 : i32
- %c16_i32 = arith.constant 16 : i32
- %0 = fir.gentypedesc !fir.tdesc<!fir.type<!fir.type<_QTpoint{x:f32,y:f32}>>>
- %1 = fir.convert %arg0 : (!fir.class<!fir.type<_QTpoint{x:f32,y:f32}>>) -> !fir.box<none>
- %2 = fir.convert %0 : (!fir.tdesc<!fir.type<!fir.type<_QTpoint{x:f32,y:f32}>>>) -> !fir.ref<none>
- %3 = fir.call @ExtendsTypeOfWithKind(%1, %2) : (!fir.box<none>, !fir.ref<none>) -> i1
- cond_br %3, ^bb2(%c4_i32 : i32), ^bb1
- ^bb1: // pred: ^bb0
- %4 = fir.gentypedesc !fir.type<_QTpoint_3d{x:f32,y:f32,z:f32}>
- %5 = fir.convert %arg0 : (!fir.class<!fir.type<_QTpoint{x:f32,y:f32}>>) -> !fir.box<none>
- %6 = fir.convert %4 : (!fir.tdesc<!fir.type<_QTpoint_3d{x:f32,y:f32,z:f32}>>) -> !fir.ref<none>
- %7 = fir.call @SameTypeAsWithKind(%5, %6) : (!fir.box<none>, !fir.ref<none>) -> i1
- cond_br %7, ^bb4(%c16_i32 : i32), ^bb3
- ^bb2(%8: i32): // pred: ^bb0
- return %8 : i32
- ^bb3: // pred: ^bb1
- br ^bb5(%c8_i32 : i32)
- ^bb4(%9: i32): // pred: ^bb1
- %10 = arith.addi %9, %9 : i32
- return %10 : i32
- ^bb5(%11: i32): // pred: ^bb3
- %12 = arith.muli %11, %11 : i32
- return %12 : i32
+ func @f(%arg0: !fir.class<!fir.ptr<!fir.type<_QFTpoint{x:f32,y:f32}>>>) -> () {
+ // TYPE IS comparison done inlined.
+ %0 = fir.address_of(@_QFE.dt.point_3d) : !fir.ref<!fir type<_QM__fortran_type_infoTderivedtype{}>>
+ %1 = fir.box_tdesc %arg0 : (!fir.class<!fir.ptr<!fir.type<_QFTpoint{x:f32,y:f32}>>>) -> !fir.tdesc<none>
+ %2 = fir.convert %0 : (!fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>) -> index
+ %3 = fir.convert %1 : (!fir.tdesc<none>) -> index
+ %4 = arith.cmpi eq, %2, %3 : index
+ cf.cond_br %4, ^bb4, ^bb3
+ ^bb1: // pred: ^bb3
+ cf.br ^bb5
+ ^bb2: // pred: ^bb3
+ // CLASS IS block.
+ cf.br ^bb6
+ ^bb3: // pred: ^bb0
+ // CLASS IS comparison done with a runtime function call.
+ %24 = fir.address_of(@_QFE.dt.point) : !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>
+ %25 = fir.convert %24 : (!fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>) -> !fir.ref<none>
+ %26 = fir.convert %6 : (!fir.class<!fir.ptr<!fir.type<_QFTpoint{x:f32,y:f32}>>>) -> !fir.box<none>
+ %27 = fir.call @_FortranAClassIs(%26, %25) : (!fir.box<none>, !fir.ref<none>) -> i1
+ cf.cond_br %27, ^bb2, ^bb1
+ ^bb4: // pred: ^bb0
+ // TYPE IS block
+ cf.br ^bb6
+ ^bb5: // pred: ^bb1
+ // CLASS DEFAULT block.
+ cf.br ^bb6
+ ^bb6: // 3 preds: ^bb2, ^bb4, ^bb5
+ return
}
- func private @ExactSameTypeAsWithKind(!fir.box<none>, !fir.ref<none>) -> i1
- func private @SameTypeAsWithKind(!fir.box<none>, !fir.ref<none>) -> i1
+ func.func private @_FortranAClassIs(!fir.box<none>, !fir.ref<none>) -> i1
}
```
-Note: some dynamic type checks can be inlined for performance. Type check with
-intrinsic types when dealing with unlimited polymorphic entities is an ideal
-candidate for inlined checks.
+Dynamic type comparisons are inlined for performance whenever possible.
+Dynamic type comparison for the `TYPE IS` type guard is inlined and
+intrinsic types comparison when dealing with unlimited polymorphic entities are
+also inlined.
+
+```fortran
+type is (integer(4))
+```
+
+```
+%i32typecode = arith.constant 9 : i8
+%typecode = fir.box_typecode %selector : (!fir.class<none>) -> i8
+%isi32 = arith.cmpi eq, %typecode, %i32typecode : i8
+```
---
@@ -433,40 +457,50 @@ Representation of the derived type information with the bindings.
%_QM__fortran_builtinsT__builtin_c_funptr = type { i64 }
```
-The `fir.dispatch` is then lowered to use the runtime information to extract the
+The `fir.dispatch` is lowered to FIR operations by the `PolymorphicOpConversion`
+pass. It uses the runtime information to extract the
correct function from the vtable and to perform the actual call. Here is
what it can look like in pseudo LLVM IR code.
+**FIR**
+```c
+ %2 = fir.box_tdesc %arg0 : (!fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>) -> !fir.tdesc<none>
+ %3 = fir.box_tdesc %arg0 : (!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>) -> !fir.tdesc<none>
+ %4 = fir.convert %3 : (!fir.tdesc<none>) -> !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>
+ %5 = fir.field_index binding, !fir.type<_QM__fortran_type_infoTderivedtype{}>
+ %6 = fir.coordinate_of %4, %5 : (!fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>>>>
+ %7 = fir.load %6 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>>>>
+ %8 = fir.box_addr %7 : (!fir.box<!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>>>) -> !fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>>
+ %c0 = arith.constant 0 : index
+ %9 = fir.coordinate_of %8, %c0 : (!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>
+ %10 = fir.field_index proc, !fir.type<_QM__fortran_type_infoTbinding{proc:!fir.type<_QM__fortran_builtinsT__builtin_c_funptr{__address:i64}>,name:!fir.box<!fir.ptr<!fir.char<1,?>>>}>
+ %11 = fir.coordinate_of %9, %10 : (!fir.ref<!fir.type<_QM__fortran_type_infoTbinding{}>>, !fir.field) -> !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_funptr{__address:i64}>>
+ %12 = fir.field_index __address, !fir.type<_QM__fortran_builtinsT__builtin_c_funptr{__address:i64}>
+ %13 = fir.coordinate_of %11, %12 : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_funptr{__address:i64}>>, !fir.field) -> !fir.ref<i64>
+ %14 = fir.load %13 : !fir.ref<i64>
+ %15 = fir.convert %14 : (i64) -> ((!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>) -> ())
+ fir.call %15(%arg0) : (!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>) -> ()
+```
+
**LLVMIR**
```c
-// Retrieve the bindings (vtable) from the type information from the descriptor
-%1 = call %_QM__fortran_type_infoTbinding* @_FortranAGetBindings(%desc)
-// Retrieve the position of the specific bindings in the table
-%2 = call i32 @_FortranAGetBindingOffset(%1, "get_area")
-// Get the binding from the table
-%3 = getelementptr %_QM__fortran_type_infoTbinding, %_QM__fortran_type_infoTbinding* %1, i32 0, i32 %2
-// Get the function pointer from the binding
-%4 = getelementptr %_QM__fortran_builtinsT__builtin_c_funptr, %_QM__fortran_type_infoTbinding %3, i32 0, i32 0
-// Cast func pointer
-%5 = inttoptr i64 %4 to <procedure pointer>
-// Load the function
-%6 = load f32(%_QMgeometryTshape*)*, %5
+// Retrieve the derived type runtime information and the vtable.
+%14 = getelementptr %_QM__fortran_type_infoTderivedtype, ptr %13, i32 0, i32 0
+%15 = load { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %14
+store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] } %15, ptr %8
+%16 = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %8, i32 0, i32 0
+%17 = load ptr, ptr %16
+%18 = getelementptr %_QM__fortran_type_infoTbinding, ptr %17, i64 0
+%19 = getelementptr %_QM__fortran_type_infoTbinding, ptr %18, i32 0, i32 0
+%20 = getelementptr %_QM__fortran_builtinsT__builtin_c_funptr, ptr %19, i32 0, i32 0
+// Load func address
+%21 = load i64, ptr %20
+// Cast to func pointer
+%22 = inttoptr i64 %21 to ptr
// Perform the actual function call
-%7 = call f32 %6(%_QMgeometryTshape* %shape)
+call void %22(ptr %0)
```
-_Note:_ functions `@_FortranAGetBindings` and `@_FortranAGetBindingOffset` are
-not available in the runtime and will need to be implemented.
-
-- `@_FortranAGetBindings` retrieves the bindings from the descriptor. The
- descriptor holds the type information that holds the bindings.
-- `@_FortranAGetBindingOffset` retrieves the procedure offset in the bindings
- based on the binding name provided.
-
-Retrieving the binding table and the offset are done separately so multiple
-dynamic dispatch on the same polymorphic entities can be optimized (the binding
-table is retrieved only once for multiple call).
-
### Passing polymorphic entities as argument
**Fortran**
@@ -850,16 +884,10 @@ dynamic type of polymorphic entities.
# Current TODOs
Current list of TODOs in lowering:
-- `flang/lib/Lower/Allocatable.cpp:465` not yet implemented: SOURCE allocation
-- `flang/lib/Lower/Allocatable.cpp:468` not yet implemented: MOLD allocation
- `flang/lib/Lower/Bridge.cpp:448` not yet implemented: create polymorphic host associated copy
- `flang/lib/Lower/CallInterface.cpp:795` not yet implemented: support for polymorphic types
- `flang/lib/Lower/ConvertType.cpp:237` not yet implemented: support for polymorphic types
-Current list of TODOs in code generation:
-
-- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:2651` not yet implemented: fir.gentypedesc codegen
-
---
Resources:
More information about the flang-commits
mailing list