1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
|
# Polymorphic Entities
A polymorphic entity is a data entity that can be of different type during the
execution of a program.
This document aims to give insights at the representation of polymorphic
entities in FIR and how polymorphic related constructs and features are lowered
to FIR.
## Fortran standard
Here is a list of the sections and constraints of the Fortran standard involved
for polymorphic entities.
- 7.3.2.1 - 7.3.2.2: TYPE specifier (TYPE(*))
- C708
- C709
- C710
- C711
- 7.3.2.3: CLASS specifier
- 7.5.4.5: The passed-object dummy argument
- C760
- 9.7.1: ALLOCATE statement
- C933
- 9.7.2: NULLIFY statement
- When a NULLIFY statement is applied to a polymorphic pointer (7.3.2.3),
its dynamic type becomes the same as its declared type.
- 10.2.2.3: Data pointer assignment
- 11.1.3: ASSOCIATE construct
- 11.1.11: SELECT TYPE construct
- C1157
- C1158
- C1159
- C1160
- C1161
- C1162
- C1163
- C1164
- C1165
- 16.9.76 EXTENDS_TYPE_OF (A, MOLD)
- 16.9.165 SAME_TYPE_AS (A, B)
- 16.9.184 STORAGE_SIZE (A [, KIND])
- C.10.5 Polymorphic Argument Association (15.5.2.9)
---
## Representation in FIR
### Polymorphic entities `CLASS(type1)`
A polymorphic entity is represented as a class type in FIR. In the example below
the dummy argument `p` is passed to the subroutine `foo` as a polymorphic entity
with the extensible type `point`. The type information captured in the class is
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.
`!fir.class` and `!fir.box` are based on a same `BaseBoxType` similar to the
`BaseMemRefType` done for MemRef.
**Fortran**
```fortran
type point
real :: x, y
end type point
type, extends(point) :: point_3d
real :: z
end type
subroutine foo(p)
class(point) :: p
! code of the subroutine
end subroutine
```
**FIR**
```c
func.func @foo(%p : !fir.class<!fir.type<_QTpoint{x:f32,y:f32}>>)
```
### Unlimited polymorphic entities `CLASS(*)`
The unlimited polymorphic entity is represented as a class type with `none` as
element type.
**Fortran**
```fortran
subroutine bar(x)
class(*) :: x
! code of the subroutine
end subroutine
```
**FIR**
```c
func.func @bar(%x : !fir.class<none>)
```
### Assumed-type `TYPE(*)`
Assumed type is added in Fortran 2018 and it is available only for dummy
arguments. It's mainly used for interfaces to non-Fortran code and is similar
to C's `void`.
An entity that is declared using the `TYPE(*)` type specifier is assumed-type
and is an unlimited polymorphic entity. It is not declared to have a type, and
is not considered to have the same declared type as any other entity,
including another unlimited polymorphic entity. Its dynamic type and type
parameters are assumed from its effective argument (7.3.2.2 - 3).
Assumed-type is represented in FIR as `!fir.box<none>`.
### SELECT TYPE construct
The `SELECT TYPE` construct select for execution at most one of its constituent
block. The selection is based on the dynamic type of the selector.
**Fortran**
```fortran
type point
real :: x, y
end type point
type, extends(point) :: point_3d
real :: z
end type point_3d
type, extends(point) :: color_point
integer :: color
end type color_point
type(point), target :: p
type(point_3d), target :: p3
type(color_point), target :: c
class(point), pointer :: p_or_c
p_or_c => c
select type ( a => p_or_c )
class is (point)
print*, a%x, a%y
type is (point_3d)
print*, a%x, a%y, a%z
class default
print*,'default'
end select
```
From the Fortran standard:
> A `TYPE IS` type guard statement matches the selector if the dynamic type
and kind type parameter values of the selector are the same as those specified
by the statement. A `CLASS IS` type guard statement matches the selector if the
dynamic type of the selector is an extension of the type specified by the
statement and the kind type parameter values specified by the statement are the
same as the corresponding type parameter values of the dynamic type of the
selector.
In the example above the `CLASS IS` type guard is matched.
The construct is lowered to a specific FIR operation `fir.select_type`. It is
similar to other FIR "select" operations such as `fir.select` and
`fir.select_rank`. The dynamic type of the selector value is matched against a
list of type descriptor. The `TYPE IS` type guard statement is represented by a
`#fir.type_is` attribute and the `CLASS IS` type guard statement is represented
by a `#fir.class_is` attribute.
The `CLASS DEFAULT` type guard statement is represented by a `unit` attribute.
**FIR**
```
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]
```
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 these two
functions _ignore_ the values of `KIND` type parameters.
The functions for the `EXTENDS_TYPE_OF` and `SAME_TYPE_AS`
intrinsics (`flang/include/flang/Runtime/derived-api.h`).
```cpp
// 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<!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.func private @_FortranAClassIs(!fir.box<none>, !fir.ref<none>) -> i1
}
```
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
```
---
## Dynamic dispatch
Dynamic dispatch is the process of selecting which implementation of a
polymorphic procedure to call at runtime. The runtime already has information
to be used in this process (more information can be found here:
[RuntimeTypeInfo.md](RuntimeTypeInfo.md)).
The declaration of the data structures are present in
`flang/runtime/type-info.h`.
In the example below, there is a basic type `shape` with two type extensions
`triangle` and `rectangle`.
The two type extensions override the `get_area` type-bound procedure.
**UML**
```
|---------------------|
| Shape |
|---------------------|
| + color:integer |
| + isFilled:logical |
|---------------------|
| + init() |
| + get_area():real |
|---------------------|
/\
/__\
|
|---------------------------------------------------|
| |
| |
|---------------------| |---------------------|
| triangle | | rectangle |
|---------------------| |---------------------|
| + base:real | | + length:real |
| + height:real | | + width:real |
|---------------------| |---------------------|
| + get_area():real | | + get_area():real |
|---------------------| |---------------------|
```
**Fortran**
```fortran
module geometry
type :: shape
integer :: color
logical :: isFilled
contains
procedure :: get_area => get_area_shape
procedure :: init => init_shape
end type shape
type, extends(shape) :: triangle
real :: base
real :: height
contains
procedure :: get_area => get_area_triangle
end type triangle
type, extends(shape) :: rectangle
real :: length
real :: width
contains
procedure :: get_area => get_area_rectangle
end type rectangle
type shape_array
class(shape), allocatable :: item
end type
contains
function get_area_shape(this)
real :: get_area_shape
class(shape) :: this
get_area_shape = 0.0
end function
subroutine init_shape(this, color)
class(shape) :: this
integer :: color
this%color = color
this%isFilled = .false.
end subroutine
function get_area_triangle(this)
real :: get_area_triangle
class(triangle) :: this
get_area_triangle = (this%base * this%height) / 2
end function
function get_area_rectangle(this)
real :: get_area_rectangle
class(rectangle) :: this
get_area_rectangle = this%length * this%width
end function
function get_all_area(shapes)
real :: get_all_area
type(shape_array) :: shapes(:)
real :: sum
integer :: i
get_all_area = 0.0
do i = 1, size(shapes)
get_all_area = get_all_area + shapes(i)%item%get_area()
end do
end function
subroutine set_base_values(sh, v1, v2)
class(shape) :: sh
real, intent(in) :: v1, v2
select type (sh)
type is (triangle)
sh%base = v1
sh%height = v2
type is (rectangle)
sh%length = v1
sh%width = v2
class default
print*,'Cannot set values'
end select
end subroutine
end module
program foo
use geometry
real :: area
type(shape_array), dimension(2) :: shapes
allocate (triangle::shapes(1)%item)
allocate (rectangle::shapes(2)%item)
do i = 1, size(shapes)
call shapes(i)%item%init(i)
end do
call set_base_values(shapes(1)%item, 2.0, 1.5)
call set_base_values(shapes(2)%item, 5.0, 4.5)
area = get_all_area(shapes)
print*, area
deallocate(shapes(1)%item)
deallocate(shapes(2)%item)
end program
```
The `fir.dispatch` operation is used to perform a dynamic dispatch. This
operation is comparable to the `fir.call` operation but for polymorphic
entities.
Call to `NON_OVERRIDABLE` type-bound procedure are resolved at compile time and
a `fir.call` operation is emitted instead of a `fir.dispatch`.
When the type of a polymorphic entity can be fully determined at compile
time, a `fir.dispatch` op can even be converted to a `fir.call` op. This will
be discussed in more detailed later in the document in the devirtualization
section.
**FIR**
Here is simple example of the `fir.dispatch` operation. The operation specify
the binding name of the type-bound procedure to be called and pass the
descriptor as argument. If the `NOPASS` attribute is set then the descriptor is
not passed as argument when lowered. If `PASS(arg-name)` is specified, the
`fir.pass` attribute is added to point to the PASS argument in the
`fir.dispatch` operation. `fir.nopass` attribute is added for the `NOPASS`. The
descriptor still need to be present in the `fir.dispatch` operation for the
dynamic dispatch. The CodeGen will then omit the descriptor in the argument
of the generated call.
The dispatch explanation focus only on the call to `get_area()` as seen in the
example.
**Fortran**
```fortran
get_all_area = get_all_area + shapes(i)%item%get_area()
```
**FIR**
```c
%1 = fir.convert %0 : !fir.ref<!fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>>
%2 = fir.dispatch "get_area"(%1 : !fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>) -> f32
```
The type information is stored in the `f18Addendum` of the descriptor. The
format is defined in `flang/runtime/type-info.h` and part of its representation
in LLVM IR is shown below. The binding is comparable to a vtable. Each derived
type has a complete type-bound procedure table in which all of the bindings of
its ancestor types appear first.
**LLVMIR**
Representation of the derived type information with the bindings.
```c
%_QM__fortran_type_infoTderivedtype = type { { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8 }, i64, { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, i32, i8, i8, i8, i8, [4 x i8] }
%_QM__fortran_type_infoTbinding = type { %_QM__fortran_builtinsT__builtin_c_funptr, { ptr, i64, i32, i8, i8, i8, i8 } }
%_QM__fortran_builtinsT__builtin_c_funptr = type { i64 }
```
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 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
call void %22(ptr %0)
```
### Passing polymorphic entities as argument
**Fortran**
```fortran
TYPE t1
END TYPE
TYPE, EXTENDS(t1) :: t2
END TYPE
```
1) Dummy argument is fixed type and actual argument is fixed type.
- `TYPE(t1)` to `TYPE(t1)`: Nothing special to take into consideration.
2) Dummy argument is polymorphic and actual argument is fixed type. In these
cases, the actual argument need to be boxed to be passed to the
subroutine/function since those are expecting a descriptor.
```c
func.func @_QMmod1Ps(%arg0: !fir.class<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>)
func.func @_QQmain() {
%0 = fir.alloca !fir.type<_QMmod1Tshape{x:i32,y:i32}> {uniq_name = "_QFEsh"}
%1 = fir.embox %0 : (!fir.ref<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>) -> !fir.class<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>
fir.call @_QMmod1Ps(%1) : (!fir.class<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>) -> ()
return
}
```
- `TYPE(t1)` to `CLASS(t1)`
- `TYPE(t2)` to `CLASS(t1)`
- `TYPE(t1)` to `CLASS(t2)` - Invalid
- `TYPE(t2)` to `CLASS(t2)`
3) Actual argument is polymorphic and dummy argument is fixed type. These case
are restricted to the declared type of the polymorphic entities.
- The simple case is when the actual argument is a scalar
polymorphic entity passed to a non-PDT. The caller just extract the
base address from the descriptor and pass it to the function.
- In other cases, the caller needs to perform a copyin/copyout since it
cannot just extract the base address of the `CLASS(T)` because it is
likely not contiguous.
- `CLASS(t1)` to `TYPE(t1)`
- `CLASS(t2)` to `TYPE(t1)` - Invalid
- `CLASS(t1)` to `TYPE(t2)` - Invalid
- `CLASS(t2)` to `TYPE(t2)`
4) Both actual and dummy arguments are polymorphic. These particular cases are
straight forward. The function expect polymorphic entities already.
The boxed type is passed without change.
- `CLASS(t1)` to `CLASS(t1)`
- `CLASS(t2)` to `CLASS(t1)`
- `CLASS(t1)` to `CLASS(t2)` - Invalid
- `CLASS(t2)` to `CLASS(t2)`
### User-Defined Derived Type Input/Output
User-Defined Derived Type Input/Output allows to define how a derived-type
is read or written from/to a file.
There are 4 basic subroutines that can be defined:
- Formatted READ
- Formatted WRITE
- Unformatted READ
- Unformatted WRITE
Here are their respective interfaces:
**Fortran**
```fortran
subroutine read_formatted(dtv, unit, iotype, v_list, iostat, iomsg)
subroutine write_formatted(dtv, unit, iotype, v_list, iostat, iomsg)
subroutine read_unformatted(dtv, unit, iotype, v_list, iostat, iomsg)
subroutine write_unformatted(dtv, unit, iotype, v_list, iostat, iomsg)
```
When defined on a derived-type, these specific type-bound procedures are stored
as special bindings in the type descriptor (see `SpecialBinding` in
`flang/runtime/type-info.h`).
With a derived-type the function call to `@_FortranAioOutputDescriptor` from IO
runtime will be emitted in lowering.
**Fortran**
```fortran
type(t) :: x
write(10), x
```
**FIR**
```c
%5 = fir.call @_FortranAioBeginUnformattedOutput(%c10_i32, %4, %c56_i32) : (i32, !fir.ref<i8>, i32) -> !fir.ref<i8>
%6 = fir.embox %2 : (!fir.ref<!fir.type<_QTt>>) -> !fir.class<!fir.type<_QTt>>
%7 = fir.convert %6 : (!fir.class<!fir.type<_QTt>>) -> !fir.box<none>
%8 = fir.call @_FortranAioOutputDescriptor(%5, %7) : (!fir.ref<i8>, !fir.box<none>) -> i1
%9 = fir.call @_FortranAioEndIoStatement(%5) : (!fir.ref<i8>) -> i32
```
When dealing with polymorphic entities the call to IO runtime can stay
unchanged. The runtime function `OutputDescriptor` can make the dynamic dispatch
to the correct binding stored in the descriptor.
### Finalization
The `FINAL` specifies a final subroutine that might be executed when a data
entity of that type is finalized. Section 7.5.6.3 defines when finalization
occurs.
Final subroutines like User-Defined Derived Type Input/Output are stored as
special bindings in the type descriptor. The runtime is able to handle the
finalization with a call the the `@_FortranADestroy` function
(`flang/include/flang/Runtime/derived-api.h`).
**FIR**
```c
%5 = fir.call @_FortranADestroy(%desc) : (!fir.box<none>) -> none
```
The `@_FortranADestroy` function will take care to call the final subroutines
and the ones from the parent type.
Appropriate call to finalization have to be lowered at the right places (7.5.6.3
When finalization occurs).
### Devirtualization
Sometimes there is enough information at compile-time to avoid going through
a dynamic dispatch for a type-bound procedure call on a polymorphic entity. To
be able to perform this optimization directly in FIR the dispatch table is also
present statically with the `fir.dispatch_table` and `fir.dt_entry` operations.
Here is an example of these operations representing the dispatch tables for the
same example than for the dynamic dispatch.
**FIR**
```
fir.dispatch_table @_QMgeometryE.dt.shape {
fir.dt_entry init, @_QMgeometryPinit_shape
fir.dt_entry get_area, @_QMgeometryPget_area_shape
}
fir.dispatch_table @_QMgeometryE.dt.rectangle {
fir.dt_entry init, @_QMgeometryPinit_shape
fir.dt_entry get_area, @_QMgeometryPget_area_rectangle
}
fir.dispatch_table @_QMgeometryE.dt.triangle {
fir.dt_entry init, @_QMgeometryPinit_shape
fir.dt_entry get_area, @_QMgeometryPget_area_triangle
}
```
With this information, an optimization pass can replace `fir.dispatch`
operations with `fir.call` operations to the correct functions when the type is
know at compile time.
This is the case in a `type is` type-guard block as illustrated below.
**Fortran**
```fortran
subroutine get_only_triangle_area(sh)
class(shape) :: sh
real :: area
select type (sh)
type is (triangle)
area = sh%get_area()
class default
area = 0.0
end select
end subroutine
```
**FIR**
The call to `get_area` in the `type is (triangle)` guard can be replaced.
```c
%3 = fir.dispatch "get_area"(%desc)
// Replaced by
%3 = fir.call @get_area_triangle(%desc)
```
Another example would be the one below. In this case as well, a dynamic dispatch
is not necessary and a `fir.call` can be emitted instead.
**Fortran**
```fortran
real :: area
class(shape), pointer :: sh
type(triangle), target :: tr
sh => tr
area = sh%get_area()
```
Note that the frontend is already replacing some of the dynamic dispatch calls
with the correct static ones. The optimization pass is useful for cases not
handled by the frontend and especially cases showing up after some other
optimizations are applied.
### `ALLOCATE`/`DEALLOCATE` statements
The allocation and deallocation of polymorphic entities are delegated to the
runtime.
The corresponding function signatures can be found in
`flang/include/flang/Runtime/allocatable.h` and in
`flang/include/flang/Runtime/pointer.h` for pointer allocation.
`ALLOCATE`
The `ALLOCATE` statement is lowered to runtime calls as shown in the example
below.
**Fortran**
```fortran
allocate(triangle::shapes(1)%item)
allocate(rectangle::shapes(2)%item)
```
**FIR**
```c
%0 = fir.address_of(@_QMgeometryE.dt.triangle) : !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype>>
%1 = fir.convert %item1 : (!fir.ref<!fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>>) -> !fir.ref<!fir.box<none>>
%2 = fir.call @_FortranAAllocatableInitDerived(%1, %0)
%3 = fir.call @_FortranAAllocatableAllocate(%1, ...)
%4 = fir.address_of(@_QMgeometryE.dt.rectangle) : !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype>>
%5 = fir.convert %item2 : (!fir.ref<!fir.class<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32}>>>) -> !fir.ref<!fir.box<none>>
%6 = fir.call @_FortranAAllocatableInitDerived(%5, %4)
%7 = fir.call @_FortranAAllocatableAllocate(%5, ...)
```
For pointer allocation, the `PointerAllocate` function is used.
`DEALLOCATE`
The `DEALLOCATE` statement is lowered to a runtime call to
`AllocatableDeallocate` and `PointerDeallocate` for pointers.
**Fortran**
```fortran
deallocate(shapes(1)%item)
deallocate(shapes(2)%item)
```
**FIR**
```c
%8 = fir.call @_FortranAAllocatableDeallocate(%desc1)
%9 = fir.call @_FortranAAllocatableDeallocate(%desc2)
```
### `EXTENDS_TYPE_OF`/`SAME_TYPE_AS` intrinsics
`EXTENDS_TYPE_OF` and `SAME_TYPE_AS` intrinsics have implementation in the
runtime. Respectively `SameTypeAs` and `ExtendsTypeOf` in
`flang/include/flang/Evaluate/type.h`.
Both intrinsic functions are lowered to their respective runtime calls.
### Assignment / Pointer assignment
Intrinsic assignment of an object to another is already implemented in the
runtime. The function `@_FortranAAsssign` performs the correct operations.
Available in `flang/include/flang/Runtime/assign.h`.
### User defined assignment and operator
**Fortran**
```fortran
module mod1
type t1
contains
procedure :: assign_t1
generic :: assignment(=) => assign_t1
end type t1
type, extends(t1) :: t2
end type
contains
subroutine assign_t1(to, from)
class(t1), intent(inout) :: to
class(t1), intent(in) :: from
! Custom code for the assignment
end subroutine
subroutine assign_t2(to, from)
class(t2), intent(inout) :: to
class(t2), intent(in) :: from
! Custom code for the assignment
end subroutine
end module
program main
use mod
class(t1), allocatable :: v1
class(t1), allocatable :: v2
allocate(t2::v1)
allocate(t2::v2)
v2 = v1
end program
```
In the example above the assignment `v2 = v1` is done by a call to `assign_t1`.
This is resolved at compile time since `t2` could not have a generic type-bound
procedure for assignment with an interface that is not distinguishable. This
is the same for user defined operators.
### `NULLIFY`
When a `NULLIFY` statement is applied to a polymorphic pointer (7.3.2.3), its
dynamic type becomes the same as its declared type.
The `NULLIFY` statement is lowered to a call to the corresponding runtime
function `PointerNullifyDerived` in `flang/include/flang/Runtime/pointer.h`.
### Impact on existing FIR operations dealing with descriptors
Currently, FIR has a couple of operations taking descriptors as inputs or
producing descriptors as outputs. These operations might need to deal with the
dynamic type of polymorphic entities.
- `fir.load`/`fir.store`
- Currently a `fir.load` of a `fir.box` is a special case. In the code
generation no copy is made. This could be problematic with polymorphic
entities. When a `fir.load` is performed on a `fir.class` type, the dynamic
can be copied.
**Fortran**
```fortran
module mod1
class(shape), pointer :: a
contains
subroutine sub1(a, b)
class(shape) :: b
associate (b => a)
! Some more code
end associate
end subroutine
end module
```
In the example above, the dynamic type of `a` and `b` might be different. The
dynamic type of `a` must be copied when it is associated on `b`.
**FIR**
```c
// fir.load must copy the dynamic type from the pointer `a`
%0 = fir.address_of(@_QMmod1Ea) : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>>>
%1 = fir.load %0 : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>>>
```
- `fir.embox`
- The embox operation is used to create a descriptor from a reference. With
polymorphic entities, it is used to create a polymorphic descriptor from
a derived type. The declared type of the descriptor and the derived type
are identical. The dynamic type of the descriptor must be set when it is
created. This is already handled by lowering.
- `fir.rebox`
- The rebox operation is used to create a new descriptor from a another
descriptor with new optional dimension. If the original descriptor is a
polymorphic entities its dynamic type must be propagated to the new
descriptor.
```
%0 = fir.slice %c10, %c33, %c2 : (index, index, index) -> !fir.slice<1>
%1 = fir.shift %c0 : (index) -> !fir.shift<1>
%2 = fir.rebox %x(%1)[%0] : (!fir.class<!fir.array<?x!fir.type<>>>, !fir.shift<1>, !fir.slice<1>) -> !fir.class<!fir.array<?x!fir.type<>>>
```
---
# Testing
- Lowering part is tested with LIT tests in tree
- Polymorphic entities involved a lot of runtime information so executable
tests will be useful for full testing.
---
# Current TODOs
Current list of TODOs in lowering:
- `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
---
Resources:
- [1] https://www.pgroup.com/blogs/posts/f03-oop-part1.htm
- [2] https://www.pgroup.com/blogs/posts/f03-oop-part2.htm
- [3] https://www.pgroup.com/blogs/posts/f03-oop-part3.htm
- [4] https://www.pgroup.com/blogs/posts/f03-oop-part4.htm
- [5] Modern Fortran explained
|