aboutsummaryrefslogtreecommitdiff
path: root/flang/docs/ProcedurePointer.md
blob: 77e54bb505c7b0f373f3731b5e4e799e27888af2 (plain)
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
<!--===- docs/ProcedurePointer.md

   Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
   See https://llvm.org/LICENSE.txt for license information.
   SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

-->

# Procedure Pointer

A procedure pointer is a procedure that has the EXTERNAL and POINTER attributes.

This document summarizes what of context the procedure pointers should appear,
and how they are lowered to FIR.

The current plan is to use/extend the `BoxedProcedure` pass for the conversion
to LLVM IR, and thus will not be lowering the procedure-pointer-related
operations to LLVM IR in `CodeGen.cpp`.

## Fortran standard

Here is a list of the sections and constraints of the Fortran standard involved
for procedure pointers.

- 8.5.4 Components
  - C757
  - C758
  - C759
- 8.5.9: EXTERNAL attribute
- 8.5.14: POINTER attribute
  - C853
  - A procedure pointer shall not be referenced unless it is pointer associated
    with a target procedure.
- 8.5.15 PROTECTED attribute
  - C855
- 8.5.16 SAVE attribute
  - (4) A procedure pointer declared in the scoping unit of a main program,
        module, or submodule implicitly has the SAVE attribute.
- 8.10.2.1 COMMON statement
  - C8119
- 10.2.2.2 Pointer assignment statement
  - C1028
  - C1029
- 10.2.2.4 Procedure pointer assignment
- 11.1.3 ASSOCIATE construct
  - C1005
- 12.6.3 Data transfer input/output list
  - C1233
- 15.2.2.4 Procedure pointers
  - A procedure pointer may be pointer associated with an external procedure, an
    internal procedure, an intrinsic procedure, a module procedure, or a dummy
    procedure that is not a procedure pointer.
- 15.4.3.6 Procedure declaration statement
- 15.5.2.9(5) Actual arguments associated with dummy procedure entities
- 16.9.16 ASSOCIATED(POINTER [, TARGET])
  - POINTER may be a procedure pointer, and TARGET may be proc-target in a
    pointer assignment statement (10.2.2).
- 16.9.144 NULL([MOLD])
  - MOLD may be a procedure pointer.
- 18.2.3.4 C_F_PROCPOINTER(CPTR, FPTR)
  - FPTR shall be a procedure pointer, and not be a component of a coindexed
    object.
- C.1.1 A procedure that is not a procedure pointer can be an actual argument
  that corresponds to a procedure pointer dummy argument with the INTENT(IN)
  attribute.

---

## Representation in FIR

### Procedure pointer `!fir.ref<!fir.boxproc<T>>`

A procedure pointer may have an explicit or implicit interface. T in
`!fir.ref<!fir.boxproc<T>>` is the function type, which is `() -> ()` if the
procedure pointer has the implicit interface declared as
`procedure(), pointer :: p`.

A procedure declaration statement specifies EXTERNAL attribute (8.5.9) for all
entities for all entities in the procedure declaration list.

### Actual arguments associated with dummy procedure entities

The actual argument may be a procedure pointer, a valid target for the dummy
pointer, a reference to the NULL() intrinsic, or a reference to a function that
returns a procedure pointer.

If the interface is explicit, and the dummy argument is procedure pointer, the
reference is resolved as the pointer to the procedure; otherwise, the reference
is resolved as the pointer target.

**Fortran case 1**
```fortran
subroutine proc_pointer_dummy_argument(p)
  interface
    function func(x)
      integer :: x
    end function func
  end interface
  procedure(func), pointer :: p
  call foo1(p)
  call foo2(p)
contains
  subroutine foo2(q)
    interface
      function func(x)
        integer :: x
      end function func
    end interface
    procedure(func), pointer :: q
  end subroutine foo2
end subroutine proc_pointer_dummy_argument
```

**FIR for case 1**
```
func.func private @foo1(!fir.boxproc<(!fir.ref<i32>) -> f32>)
func.func private @foo2(!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>)

func.func @proc_pointer_dummy_argument(%0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) {
  %1 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  fir.call @foo1(%1) : (!fir.boxproc<(!fir.ref<i32>) -> f32>) -> ()
  fir.call @foo2(%0) : (!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) -> ()
  return
}
```

**Fortran case 2**
```fortran
subroutine proc_pointer_global()
  interface
    function func(x)
      integer :: x
    end function func
  end interface
  procedure(func), pointer, save :: p
  call foo1(p)
  call foo2(p)
contains
  subroutine foo2(q)
    interface
      function func(x)
        integer :: x
      end function func
    end interface
    procedure(func), pointer :: q
  end subroutine foo2
end subroutine proc_pointer_global
```

**FIR for case 2**
```
func.func private @foo1(!fir.boxproc<(!fir.ref<i32>) -> f32>)
func.func private @foo2(!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>)

fir.global internal @ProcedurePointer : !fir.boxproc<(!fir.ref<i32>) -> f32> {
  %0 = fir.zero_bits (!fir.ref<i32>) -> f32
  %1 = fir.emboxproc %0 : ((!fir.ref<i32>) -> f32) -> !fir.boxproc<(!fir.ref<i32>) -> f32>
  fir.has_value %1 : !fir.boxproc<(!fir.ref<i32>) -> f32>
}

func.func @proc_pointer_global() {
  %0 = fir.address_of(@ProcedurePointer) : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  %1 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  fir.call @foo1(%1) : (!fir.boxproc<(!fir.ref<i32>) -> f32>) -> ()
  fir.call @foo2(%0) : (!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) -> ()
  return
}
```

**Fortran case 3**
```fortran
subroutine proc_pointer_local()
  interface
    function func(x)
      integer :: x
    end function func
  end interface
  procedure(func), pointer :: p
  call foo1(p)
  call foo2(p)
contains
  subroutine foo2(q)
    interface
      function func(x)
        integer :: x
      end function func
    end interface
    procedure(func), pointer :: q
  end subroutine foo2
end subroutine proc_pointer_local
```

**FIR for case 3**
```
func.func private @foo1(!fir.boxproc<(!fir.ref<i32>) -> f32>)
func.func private @foo2(!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>)

func.func @proc_pointer_local() {
  %0 = fir.alloca !fir.boxproc<(!fir.ref<i32>) -> f32>
  %1 = fir.zero_bits (!fir.ref<i32>) -> f32
  %2 = fir.emboxproc %1 : ((!fir.ref<i32>) -> f32) -> !fir.boxproc<(!fir.ref<i32>) -> f32>
  fir.store %2 to %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  %4 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  fir.call @foo1(%4) : (!fir.boxproc<(!fir.ref<i32>) -> f32>) -> ()
  fir.call @foo2(%0) : (!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) -> ()
  return
}
```

It is possible to pass procedure pointers to a C function. If the C function has
an explicit interface in fortran code, and the dummy argument is a procedure
pointer, the code passes a pointer to the procedure as the actual argument
(see Case 5); Otherwise, the code passes the procedure pointer target as the
actual argument (see Case 4).

**Case 4**
```c
void func_(void (*foo)(int *)) {
  int *x, y = 1;
  x = &y;
  foo(x);
}
```
```fortran
program main
  procedure(), pointer :: pp
  pp=>print_x
  call func(pp)
contains
  subroutine print_x(x)
    integer :: x
    print *, x
  end
end
```

Note that the internal procedure is not one good usage, but it works in
implementation. It is better to use BIND(C) external or module procedure as
right-hand side proc-target.

**Case 5**
```c
void func_(void (**foo)(int *)) {
  int *x, y = 1;
  x = &y;
  (*foo)(x);
}
```
```fortran
program main
  interface
    subroutine func(p)
      procedure(), pointer :: p
    end
  end interface
  procedure(), pointer :: pp
  pp=>print_x
  call func(pp)
contains
  subroutine print_x(x)
    integer :: x
    print *, x
  end
end
```

Case 4 and Case 5 are not recommended from Fortran 2003 standard, which provides
the feature of interoperability with C to handle this. Specifically,
C_F_PROCPOINTER is used to associate a procedure pointer with the target of a C
function pointer. C_FUNPTR is also designed for interoperability with any C
function pointer type.

### Procedure pointer to function returning a character type

The dummy procedure pointer may not have a function type with an assumed length
due to C721 and C723.

### Procedure pointer to internal procedure

Initially the current plan is to implement pointers to internal procedures
using the LLVM Trampoline intrinsics. This has the drawback of requiring the
stack to be executable, which is a security hole. To avoid this, we will need
[improve the implementation](InternalProcedureTrampolines.md) to use heap-resident thunks.

### Procedure pointer assignment `p => proc`

The right-hand side may be a procedure, a procedure pointer, or a function whose
result is a procedure pointer.

The procedure could be a BIND(C) procedure. The lowering of it is the same as
that of an external or module procedure. The case of internal procedure has been
discussed above.

```c
#include<stdio.h>
void func_(int *x) {
  printf("%d\n", *x);
}
```
```fortran
program main
  interface
    subroutine func(x) bind(C)
      integer :: x
    end
  end interface
  procedure(func), bind(C, name="func_") :: proc
  procedure(func), pointer :: pp
  integer :: x = 5
  pp=>proc
  call pp(x)
end
```

**Fortran case**
```fortran
subroutine proc_pointer_assignment(arg0, arg1)
  interface
    function func(x)
      integer :: x
    end
  end interface
  procedure(func), pointer :: arg0, arg1
  real, external, bind(C, name="Procedure") :: proc
  arg0=>proc    ! case 1
  arg0=>arg1    ! case 2
  arg0=>reffunc ! case 3
contains
  function reffunc() result(pp)
    interface
      function func(x)
        integer :: x
      end
    end interface
    procedure(func), pointer :: pp
  end
end
function proc(x) bind(C, name="Procedure")
  integer :: x
  proc = real(x)
end
```

**FIR**
```
func.func @Procedure(%arg0 : !fir.ref<i32>) -> f32 {
  %0 = fir.alloca f32 {bindc_name = "res", uniq_name = "_QFfuncEres"}
  %1 = fir.load %arg0 : !fir.ref<i32>
  %2 = fir.convert %1 : (i32) -> f32
  fir.store %2 to %0 : !fir.ref<f32>
  %3 = fir.load %0 : !fir.ref<f32>
  return %3 : f32
}

func.func @Reference2Function() -> !fir.boxproc<(!fir.ref<i32>) -> f32> {
  %0 = fir.alloca !fir.boxproc<(!fir.ref<i32>) -> f32>
  %1 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  return %1 : !fir.boxproc<(!fir.ref<i32>) -> f32>
}

func.func @proc_pointer_assignment(%arg0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>, %arg1 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) {
  %0 = fir.alloca !fir.boxproc<(!fir.ref<i32>) -> f32> {bindc_name = ".result"}
  // case 1: assignment from external procedure
  %1 = fir.address_of(@Procedure) : (!fir.ref<i32>) -> f32
  %2 = fir.emboxproc %1 : ((!fir.ref<i32>) -> f32) -> !fir.boxproc<(!fir.ref<i32>) -> f32>
  fir.store %2 to %arg0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  // case2: assignment from procdure pointer
  %3 = fir.load %arg1 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  fir.store %3 to %arg0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  // case3: assignment from a reference to a function whose result is a procedure pointer
  %4 = fir.call @Reference2Function() : () -> !fir.boxproc<(!fir.ref<i32>) -> f32>
  fir.store %4 to %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  %5 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  fir.store %5 to %arg0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>
  return
}
```

### Procedure pointer components

Having procedure pointers in derived types permits `methods` to be dynamically
bound to objects. Such procedure pointer components will have the type
!fir.boxproc<T>.

**Fortran**
```fortran
subroutine proc_pointer_component(a, i, f)
  interface
    function func(x)
      integer :: x
    end
  end interface
  type matrix
    real :: element(2,2)
    procedure(func), pointer, nopass :: solve
  end type
  integer :: i
  procedure(func) :: f
  type(matrix) :: a
  a%solve=>f
  r = a%solve(i)
end subroutine proc_pointer_component
```

**FIR**
```
func.func @proc_pointer_component(%arg0 : !fir.boxproc<(!fir.ref<i32>) -> f32>, %arg1: !fir.ref<i32>) {
  %0 = fir.alloca !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>
  %1 = fir.field_index solve, !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>
  %2 = fir.coordinate_of %0, %1 : (!fir.ref<!fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>>, !fir.field) -> !fir.ref<!fir.boxproc<() -> ()>>
  %3 = fir.convert %arg0 : (!fir.boxproc<(!fir.ref<i32>) -> f32>) ->  !fir.boxproc<() -> ()>
  fir.store %3 to %2 : !fir.ref<!fir.boxproc<() -> ()>>
  %4 = fir.field_index solve, !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>
  %5 = fir.coordinate_of %0, %4 : (!fir.ref<!fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>>, !fir.field) -> !fir.ref<!fir.boxproc<() -> ()>>
  %6 = fir.load %5 : !fir.ref<!fir.boxproc<() -> ()>>
  %7 = fir.convert %6 : (!fir.boxproc<() -> ()>) -> !fir.boxproc<(!fir.ref<i32>) -> f32>
  %8 = fir.box_addr %7 : (!fir.boxproc<(!fir.ref<i32>) -> f32>) -> ((!fir.ref<i32>) -> f32)
  %9 = fir.call %8(%arg1) : (!fir.ref<i32>) -> f32
  return
}
```

---

## Testing

The lowering part is tested with LIT tests in tree, but the execution tests are
useful for full testing.

LLVM IR testing is also helpful with the initial check. A C function pointer is
semantically equivalent to a Fortran procedure in LLVM IR level, and a pointer
to a C function pointer is semantically equivalent to a Fortran procedure
pointer in LLVM IR level. That is, a Fortran procedure will be converted to a
opaque pointer in LLVM IR level, which is the same for a C function pointer;
a Fortran procedure pointer will be converted to a opaque pointer pointing to
a opaque pointer, which is the same for a pointer to a C function pointer.

The tests should include the following
- function result, subroutine/function arguments with varying types
  - non-character scalar
  - character (assumed-length and non-assumed-length)
  - array (static and dynamic)
  - character array
  - derived type
  - ... (polymorphic?)
- internal/external/module procedure or a C function as the target
  - procedure pointer initialization
  - procedure pointer assignment
- procedure pointer, procedure pointer target passed to a C function
- procedure pointer, procedure pointer target passed to a Fortran procedure
- procedure pointer component in derived types

---

## Current TODOs
Current list of TODOs in lowering:
- `flang/lib/Lower/CallInterface.cpp:708`: not yet implemented: procedure pointer result not yet handled
- `flang/lib/Lower/CallInterface.cpp:961`: not yet implemented: procedure pointer arguments
- `flang/lib/Lower/CallInterface.cpp:993`: not yet implemented: procedure pointer results
- `flang/lib/Lower/ConvertExpr.cpp:1119`: not yet implemented: procedure pointer component in derived type assignment
- `flang/lib/Lower/ConvertType.cpp:228`: not yet implemented: procedure pointers
- `flang/lib/Lower/Bridge.cpp:2438`: not yet implemented: procedure pointer assignment
- `flang/lib/Lower/ConvertVariable.cpp:348`: not yet implemented: procedure pointer component default initialization
- `flang/lib/Lower/ConvertVariable.cpp:416`: not yet implemented: procedure pointer globals
- `flang/lib/Lower/ConvertVariable.cpp:1459`: not yet implemented: procedure pointers
- `flang/lib/Lower/HostAssociations.cpp:162`: not yet implemented: capture procedure pointer in internal procedure
- lowering of procedure pointers in ASSOCIATED, NULL, and C_F_PROCPOINTER

Current list of TODOs in code generation:

NOTE: There are any number of possible implementations.

BoxedProcedure pass

or

- `flang/lib/Optimizer/CodeGen/TypeConverter.h:64` TODO: BoxProcType type conversion
- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:2080` not yet implemented: fir.emboxproc codegen
- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:629` not yet implemented: fir.boxproc_host codegen
- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:1078` not yet implemented: fir.len_param_index codegen
- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:3166` not yet implemented: fir.unboxproc codegen

---

Resources:
- [1] Fortran standard