1<!--===- docs/ProcedurePointer.md 2 3 Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 See https://llvm.org/LICENSE.txt for license information. 5 SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 7--> 8 9# Procedure Pointer 10 11A procedure pointer is a procedure that has the EXTERNAL and POINTER attributes. 12 13This document summarizes what of context the procedure pointers should appear, 14and how they are lowered to FIR. 15 16The current plan is to use/extend the `BoxedProcedure` pass for the conversion 17to LLVM IR, and thus will not be lowering the procedure-pointer-related 18operations to LLVM IR in `CodeGen.cpp`. 19 20## Fortran standard 21 22Here is a list of the sections and constraints of the Fortran standard involved 23for procedure pointers. 24 25- 8.5.4 Components 26 - C757 27 - C758 28 - C759 29- 8.5.9: EXTERNAL attribute 30- 8.5.14: POINTER attribute 31 - C853 32 - A procedure pointer shall not be referenced unless it is pointer associated 33 with a target procedure. 34- 8.5.15 PROTECTED attribute 35 - C855 36- 8.5.16 SAVE attribute 37 - (4) A procedure pointer declared in the scoping unit of a main program, 38 module, or submodule implicitly has the SAVE attribute. 39- 8.10.2.1 COMMON statement 40 - C8119 41- 10.2.2.2 Pointer assignment statement 42 - C1028 43 - C1029 44- 10.2.2.4 Procedure pointer assignment 45- 11.1.3 ASSOCIATE construct 46 - C1005 47- 12.6.3 Data transfer input/output list 48 - C1233 49- 15.2.2.4 Procedure pointers 50 - A procedure pointer may be pointer associated with an external procedure, an 51 internal procedure, an intrinsic procedure, a module procedure, or a dummy 52 procedure that is not a procedure pointer. 53- 15.4.3.6 Procedure declaration statement 54- 15.5.2.9(5) Actual arguments associated with dummy procedure entities 55- 16.9.16 ASSOCIATED(POINTER [, TARGET]) 56 - POINTER may be a procedure pointer, and TARGET may be proc-target in a 57 pointer assignment statement (10.2.2). 58- 16.9.144 NULL([MOLD]) 59 - MOLD may be a procedure pointer. 60- 18.2.3.4 C_F_PROCPOINTER(CPTR, FPTR) 61 - FPTR shall be a procedure pointer, and not be a component of a coindexed 62 object. 63- C.1.1 A procedure that is not a procedure pointer can be an actual argument 64 that corresponds to a procedure pointer dummy argument with the INTENT(IN) 65 attribute. 66 67--- 68 69## Representation in FIR 70 71### Procedure pointer `!fir.ref<!fir.boxproc<T>>` 72 73A procedure pointer may have an explicit or implicit interface. T in 74`!fir.ref<!fir.boxproc<T>>` is the function type, which is `() -> ()` if the 75procedure pointer has the implicit interface declared as 76`procedure(), pointer :: p`. 77 78A procedure declaration statement specifies EXTERNAL attribute (8.5.9) for all 79entities for all entities in the procedure declaration list. 80 81### Actual arguments associated with dummy procedure entities 82 83The actual argument may be a procedure pointer, a valid target for the dummy 84pointer, a reference to the NULL() intrinsic, or a reference to a function that 85returns a procedure pointer. 86 87If the interface is explicit, and the dummy argument is procedure pointer, the 88reference is resolved as the pointer to the procedure; otherwise, the reference 89is resolved as the pointer target. 90 91**Fortran case 1** 92```fortran 93subroutine proc_pointer_dummy_argument(p) 94 interface 95 function func(x) 96 integer :: x 97 end function func 98 end interface 99 procedure(func), pointer :: p 100 call foo1(p) 101 call foo2(p) 102contains 103 subroutine foo2(q) 104 interface 105 function func(x) 106 integer :: x 107 end function func 108 end interface 109 procedure(func), pointer :: q 110 end subroutine foo2 111end subroutine proc_pointer_dummy_argument 112``` 113 114**FIR for case 1** 115``` 116func.func private @foo1(!fir.boxproc<(!fir.ref<i32>) -> f32>) 117func.func private @foo2(!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) 118 119func.func @proc_pointer_dummy_argument(%0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) { 120 %1 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 121 fir.call @foo1(%1) : (!fir.boxproc<(!fir.ref<i32>) -> f32>) -> () 122 fir.call @foo2(%0) : (!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) -> () 123 return 124} 125``` 126 127**Fortran case 2** 128```fortran 129subroutine proc_pointer_global() 130 interface 131 function func(x) 132 integer :: x 133 end function func 134 end interface 135 procedure(func), pointer, save :: p 136 call foo1(p) 137 call foo2(p) 138contains 139 subroutine foo2(q) 140 interface 141 function func(x) 142 integer :: x 143 end function func 144 end interface 145 procedure(func), pointer :: q 146 end subroutine foo2 147end subroutine proc_pointer_global 148``` 149 150**FIR for case 2** 151``` 152func.func private @foo1(!fir.boxproc<(!fir.ref<i32>) -> f32>) 153func.func private @foo2(!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) 154 155fir.global internal @ProcedurePointer : !fir.boxproc<(!fir.ref<i32>) -> f32> { 156 %0 = fir.zero_bits (!fir.ref<i32>) -> f32 157 %1 = fir.emboxproc %0 : ((!fir.ref<i32>) -> f32) -> !fir.boxproc<(!fir.ref<i32>) -> f32> 158 fir.has_value %1 : !fir.boxproc<(!fir.ref<i32>) -> f32> 159} 160 161func.func @proc_pointer_global() { 162 %0 = fir.address_of(@ProcedurePointer) : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 163 %1 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 164 fir.call @foo1(%1) : (!fir.boxproc<(!fir.ref<i32>) -> f32>) -> () 165 fir.call @foo2(%0) : (!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) -> () 166 return 167} 168``` 169 170**Fortran case 3** 171```fortran 172subroutine proc_pointer_local() 173 interface 174 function func(x) 175 integer :: x 176 end function func 177 end interface 178 procedure(func), pointer :: p 179 call foo1(p) 180 call foo2(p) 181contains 182 subroutine foo2(q) 183 interface 184 function func(x) 185 integer :: x 186 end function func 187 end interface 188 procedure(func), pointer :: q 189 end subroutine foo2 190end subroutine proc_pointer_local 191``` 192 193**FIR for case 3** 194``` 195func.func private @foo1(!fir.boxproc<(!fir.ref<i32>) -> f32>) 196func.func private @foo2(!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) 197 198func.func @proc_pointer_local() { 199 %0 = fir.alloca !fir.boxproc<(!fir.ref<i32>) -> f32> 200 %1 = fir.zero_bits (!fir.ref<i32>) -> f32 201 %2 = fir.emboxproc %1 : ((!fir.ref<i32>) -> f32) -> !fir.boxproc<(!fir.ref<i32>) -> f32> 202 fir.store %2 to %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 203 %4 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 204 fir.call @foo1(%4) : (!fir.boxproc<(!fir.ref<i32>) -> f32>) -> () 205 fir.call @foo2(%0) : (!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) -> () 206 return 207} 208``` 209 210It is possible to pass procedure pointers to a C function. If the C function has 211an explicit interface in fortran code, and the dummy argument is a procedure 212pointer, the code passes a pointer to the procedure as the actual argument 213(see Case 5); Otherwise, the code passes the procedure pointer target as the 214actual argument (see Case 4). 215 216**Case 4** 217```c 218void func_(void (*foo)(int *)) { 219 int *x, y = 1; 220 x = &y; 221 foo(x); 222} 223``` 224```fortran 225program main 226 procedure(), pointer :: pp 227 pp=>print_x 228 call func(pp) 229contains 230 subroutine print_x(x) 231 integer :: x 232 print *, x 233 end 234end 235``` 236 237Note that the internal procedure is not one good usage, but it works in 238implementation. It is better to use BIND(C) external or module procedure as 239right-hand side proc-target. 240 241**Case 5** 242```c 243void func_(void (**foo)(int *)) { 244 int *x, y = 1; 245 x = &y; 246 (*foo)(x); 247} 248``` 249```fortran 250program main 251 interface 252 subroutine func(p) 253 procedure(), pointer :: p 254 end 255 end interface 256 procedure(), pointer :: pp 257 pp=>print_x 258 call func(pp) 259contains 260 subroutine print_x(x) 261 integer :: x 262 print *, x 263 end 264end 265``` 266 267Case 4 and Case 5 are not recommended from Fortran 2003 standard, which provides 268the feature of interoperability with C to handle this. Specifically, 269C_F_PROCPOINTER is used to associate a procedure pointer with the target of a C 270function pointer. C_FUNPTR is also designed for interoperability with any C 271function pointer type. 272 273### Procedure pointer to function returning a character type 274 275The dummy procedure pointer may not have a function type with an assumed length 276due to C721 and C723. 277 278### Procedure pointer to internal procedure 279 280Initially the current plan is to implement pointers to internal procedures 281using the LLVM Trampoline intrinsics. This has the drawback of requiring the 282stack to be executable, which is a security hole. To avoid this, we will need 283[improve the implementation](InternalProcedureTrampolines.md) to use heap-resident thunks. 284 285### Procedure pointer assignment `p => proc` 286 287The right-hand side may be a procedure, a procedure pointer, or a function whose 288result is a procedure pointer. 289 290The procedure could be a BIND(C) procedure. The lowering of it is the same as 291that of an external or module procedure. The case of internal procedure has been 292discussed above. 293 294```c 295#include<stdio.h> 296void func_(int *x) { 297 printf("%d\n", *x); 298} 299``` 300```fortran 301program main 302 interface 303 subroutine func(x) bind(C) 304 integer :: x 305 end 306 end interface 307 procedure(func), bind(C, name="func_") :: proc 308 procedure(func), pointer :: pp 309 integer :: x = 5 310 pp=>proc 311 call pp(x) 312end 313``` 314 315**Fortran case** 316```fortran 317subroutine proc_pointer_assignment(arg0, arg1) 318 interface 319 function func(x) 320 integer :: x 321 end 322 end interface 323 procedure(func), pointer :: arg0, arg1 324 real, external, bind(C, name="Procedure") :: proc 325 arg0=>proc ! case 1 326 arg0=>arg1 ! case 2 327 arg0=>reffunc ! case 3 328contains 329 function reffunc() result(pp) 330 interface 331 function func(x) 332 integer :: x 333 end 334 end interface 335 procedure(func), pointer :: pp 336 end 337end 338function proc(x) bind(C, name="Procedure") 339 integer :: x 340 proc = real(x) 341end 342``` 343 344**FIR** 345``` 346func.func @Procedure(%arg0 : !fir.ref<i32>) -> f32 { 347 %0 = fir.alloca f32 {bindc_name = "res", uniq_name = "_QFfuncEres"} 348 %1 = fir.load %arg0 : !fir.ref<i32> 349 %2 = fir.convert %1 : (i32) -> f32 350 fir.store %2 to %0 : !fir.ref<f32> 351 %3 = fir.load %0 : !fir.ref<f32> 352 return %3 : f32 353} 354 355func.func @Reference2Function() -> !fir.boxproc<(!fir.ref<i32>) -> f32> { 356 %0 = fir.alloca !fir.boxproc<(!fir.ref<i32>) -> f32> 357 %1 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 358 return %1 : !fir.boxproc<(!fir.ref<i32>) -> f32> 359} 360 361func.func @proc_pointer_assignment(%arg0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>, %arg1 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>>) { 362 %0 = fir.alloca !fir.boxproc<(!fir.ref<i32>) -> f32> {bindc_name = ".result"} 363 // case 1: assignment from external procedure 364 %1 = fir.address_of(@Procedure) : (!fir.ref<i32>) -> f32 365 %2 = fir.emboxproc %1 : ((!fir.ref<i32>) -> f32) -> !fir.boxproc<(!fir.ref<i32>) -> f32> 366 fir.store %2 to %arg0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 367 // case2: assignment from procdure pointer 368 %3 = fir.load %arg1 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 369 fir.store %3 to %arg0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 370 // case3: assignment from a reference to a function whose result is a procedure pointer 371 %4 = fir.call @Reference2Function() : () -> !fir.boxproc<(!fir.ref<i32>) -> f32> 372 fir.store %4 to %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 373 %5 = fir.load %0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 374 fir.store %5 to %arg0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> f32>> 375 return 376} 377``` 378 379### Procedure pointer components 380 381Having procedure pointers in derived types permits `methods` to be dynamically 382bound to objects. Such procedure pointer components will have the type 383!fir.boxproc<T>. 384 385**Fortran** 386```fortran 387subroutine proc_pointer_component(a, i, f) 388 interface 389 function func(x) 390 integer :: x 391 end 392 end interface 393 type matrix 394 real :: element(2,2) 395 procedure(func), pointer, nopass :: solve 396 end type 397 integer :: i 398 procedure(func) :: f 399 type(matrix) :: a 400 a%solve=>f 401 r = a%solve(i) 402end subroutine proc_pointer_component 403``` 404 405**FIR** 406``` 407func.func @proc_pointer_component(%arg0 : !fir.boxproc<(!fir.ref<i32>) -> f32>, %arg1: !fir.ref<i32>) { 408 %0 = fir.alloca !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}> 409 %1 = fir.field_index solve, !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}> 410 %2 = fir.coordinate_of %0, %1 : (!fir.ref<!fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>>, !fir.field) -> !fir.ref<!fir.boxproc<() -> ()>> 411 %3 = fir.convert %arg0 : (!fir.boxproc<(!fir.ref<i32>) -> f32>) -> !fir.boxproc<() -> ()> 412 fir.store %3 to %2 : !fir.ref<!fir.boxproc<() -> ()>> 413 %4 = fir.field_index solve, !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}> 414 %5 = fir.coordinate_of %0, %4 : (!fir.ref<!fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<() -> ()>}>>, !fir.field) -> !fir.ref<!fir.boxproc<() -> ()>> 415 %6 = fir.load %5 : !fir.ref<!fir.boxproc<() -> ()>> 416 %7 = fir.convert %6 : (!fir.boxproc<() -> ()>) -> !fir.boxproc<(!fir.ref<i32>) -> f32> 417 %8 = fir.box_addr %7 : (!fir.boxproc<(!fir.ref<i32>) -> f32>) -> ((!fir.ref<i32>) -> f32) 418 %9 = fir.call %8(%arg1) : (!fir.ref<i32>) -> f32 419 return 420} 421``` 422 423--- 424 425## Testing 426 427The lowering part is tested with LIT tests in tree, but the execution tests are 428useful for full testing. 429 430LLVM IR testing is also helpful with the initial check. A C function pointer is 431semantically equivalent to a Fortran procedure in LLVM IR level, and a pointer 432to a C function pointer is semantically equivalent to a Fortran procedure 433pointer in LLVM IR level. That is, a Fortran procedure will be converted to a 434opaque pointer in LLVM IR level, which is the same for a C function pointer; 435a Fortran procedure pointer will be converted to a opaque pointer pointing to 436a opaque pointer, which is the same for a pointer to a C function pointer. 437 438The tests should include the following 439- function result, subroutine/function arguments with varying types 440 - non-character scalar 441 - character (assumed-length and non-assumed-length) 442 - array (static and dynamic) 443 - character array 444 - derived type 445 - ... (polymorphic?) 446- internal/external/module procedure or a C function as the target 447 - procedure pointer initialization 448 - procedure pointer assignment 449- procedure pointer, procedure pointer target passed to a C function 450- procedure pointer, procedure pointer target passed to a Fortran procedure 451- procedure pointer component in derived types 452 453--- 454 455## Current TODOs 456Current list of TODOs in lowering: 457- `flang/lib/Lower/CallInterface.cpp:708`: not yet implemented: procedure pointer result not yet handled 458- `flang/lib/Lower/CallInterface.cpp:961`: not yet implemented: procedure pointer arguments 459- `flang/lib/Lower/CallInterface.cpp:993`: not yet implemented: procedure pointer results 460- `flang/lib/Lower/ConvertExpr.cpp:1119`: not yet implemented: procedure pointer component in derived type assignment 461- `flang/lib/Lower/ConvertType.cpp:228`: not yet implemented: procedure pointers 462- `flang/lib/Lower/Bridge.cpp:2438`: not yet implemented: procedure pointer assignment 463- `flang/lib/Lower/ConvertVariable.cpp:348`: not yet implemented: procedure pointer component default initialization 464- `flang/lib/Lower/ConvertVariable.cpp:416`: not yet implemented: procedure pointer globals 465- `flang/lib/Lower/ConvertVariable.cpp:1459`: not yet implemented: procedure pointers 466- `flang/lib/Lower/HostAssociations.cpp:162`: not yet implemented: capture procedure pointer in internal procedure 467- lowering of procedure pointers in ASSOCIATED, NULL, and C_F_PROCPOINTER 468 469Current list of TODOs in code generation: 470 471NOTE: There are any number of possible implementations. 472 473BoxedProcedure pass 474 475or 476 477- `flang/lib/Optimizer/CodeGen/TypeConverter.h:64` TODO: BoxProcType type conversion 478- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:2080` not yet implemented: fir.emboxproc codegen 479- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:629` not yet implemented: fir.boxproc_host codegen 480- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:1078` not yet implemented: fir.len_param_index codegen 481- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:3166` not yet implemented: fir.unboxproc codegen 482 483--- 484 485Resources: 486- [1] Fortran standard 487