Index: flang/docs/ProcedurePointer.md =================================================================== --- /dev/null +++ flang/docs/ProcedurePointer.md @@ -0,0 +1,299 @@ + + +# 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. + +## 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. +- 10.2.2.4 Procedure pointer assignment +- 15.4.3.6 Procedure declaration statement +- 15.5.2.9(5) Actual arguments associated with dummy procedure entities +- 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>` + +A procedure pointer may have an explicit or implicit interface. T in +`!fir.ref>` 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 intrinsic NULL, 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 argument; 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) -> !fir.ref>) +func.func private @foo2(!fir.ref) -> !fir.ref>>) + +func.func @proc_pointer_dummy_argument(%0 : !fir.ref) -> !fir.ref>>) { + %1 = fir.load %0 : !fir.ref) -> !fir.ref>> + fir.call @foo1(%1) : ((!fir.ref) -> !fir.ref) -> () + fir.call @foo2(%0) : (!fir.ref) -> !fir.ref>>) -> () + 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) -> !fir.ref>) +func.func private @foo2(!fir.ref) -> !fir.ref>>) + +fir.global internal @ProcedurePointer : !fir.boxproc<(!fir.ref) -> !fir.ref> { + %0 = fir.zero_bits (!fir.ref) -> !fir.ref + %1 = fir.emboxproc %0 : ((!fir.ref) -> !fir.ref) -> !fir.boxproc<(!fir.ref) -> !fir.ref> + fir.has_value %1 : !fir.boxproc<(!fir.ref) -> !fir.ref> +} + +func.func @proc_pointer_global() { + %0 = fir.address_of(@ProcedurePointer) : !fir.ref) -> !fir.ref>> + %1 = fir.load %0 : !fir.ref) -> !fir.ref>> + fir.call @foo1(%1) : (!fir.boxproc<(!fir.ref) -> !fir.ref>) -> () + fir.call @foo2(%0) : (!fir.ref) -> !fir.ref>>) -> () + 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) -> !fir.ref>) +func.func private @foo2(!fir.ref) -> !fir.ref>>) + +func.func @proc_pointer_local() { + %0 = fir.alloca !fir.boxproc<(!fir.ref) -> !fir.ref> + %1 = fir.alloca (!fir.ref) -> !fir.ref + %2 = fir.zero_bits (!fir.ref) -> !fir.ref + fir.store %2 to %1 : !fir.ref<(!fir.ref) -> !fir.ref> + %3 = fir.load %1 : !fir.ref<(!fir.ref) -> !fir.ref> + %4 = fir.emboxproc %3 : ((!fir.ref) -> !fir.ref) -> !fir.boxproc<(!fir.ref) -> !fir.ref> + fir.store %4 to %0 : !fir.ref) -> !fir.ref>> + fir.call @foo2(%0) : (!fir.ref) -> !fir.ref>>) -> () + %5 = fir.load %0 : !fir.ref) -> !fir.ref>> + %6 = fir.box_addr %5 : (!fir.boxproc<(!fir.ref) -> !fir.ref>) -> ((!fir.ref) -> !fir.ref) + fir.store %6 to %1 : !fir.ref<(!fir.ref) -> !fir.ref> + %7 = fir.load %1 : !fir.ref<(!fir.ref) -> !fir.ref> + %8 = fir.emboxproc %7 : ((!fir.ref) -> !fir.ref) -> !fir.boxproc<(!fir.ref) -> !fir.ref> + fir.call @foo1(%8) : (!fir.boxproc<(!fir.ref) -> !fir.ref>) -> () + return +} +``` + +### 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. + +**FIR** +``` +func.func @Procedure(%arg0 : !fir.ref) -> !fir.ref { + %1 = fir.load %arg0 : !fir.ref + %2 = fir.convert %1 : (i32) -> f32 + return %2 : f32 +} + +func.func @Reference2Function() -> !fir.boxproc<(!fir.ref) -> !fir.ref> { + %0 = fir.alloca !fir.boxproc<(!fir.ref) -> !fir.ref> + %1 = fir.load %0 : !fir.ref) -> !fir.ref>> + return %1 : !fir.boxproc<(!fir.ref) -> !fir.ref> +} + +func.func @proc_pointer_assignment(%arg0 : !fir.ref) -> !fir.ref>>, %arg1 : !fir.ref) -> !fir.ref>>) { + %0 = fir.alloca !fir.boxproc<(!fir.ref) -> !fir.ref> {bindc_name = ".result"} + // assignment from external procedure, internal procedure may need host context + %1 = fir.address_of(@Procedure) : (!fir.ref) -> !fir.ref + %2 = fir.emboxproc %1 : ((!fir.ref) -> !fir.ref) -> !fir.boxproc<(!fir.ref) -> !fir.ref> + fir.store %2 to %arg0 : !fir.ref) -> !fir.ref>> + // assignment from procdure pointer + %3 = fir.load %arg1 : !fir.ref) -> !fir.ref>> + fir.store %3 to %arg0 : !fir.ref) -> !fir.ref>> + // assignment from a reference to a function whose result is a procedure pointer + %4 = fir.call @Reference2Function() : () -> !fir.boxproc<(!fir.ref) -> !fir.ref> + fir.store %4 to %0 : !fir.ref) -> !fir.ref>> + %5 = fir.load %0 : !fir.ref) -> !fir.ref>> + fir.store %5 to %arg0 : !fir.ref) -> !fir.ref>> + return +} +``` + +### Procedure pointer component + +Having procedure pointers in derived types permits `methods` dynamically binded +to objects. The procedure pointer component is to have the type !fir.boxproc. + +**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.ref) -> !fir.ref, %arg1: !fir.ref) { + %0 = fir.alloca !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<(!fir.ref) -> !fir.ref>}> + %1 = fir.field_index solve, !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<(!fir.ref) -> !fir.ref>}> + %2 = fir.coordinate_of %0, %1 : (!fir.ref,solve:!fir.boxproc<(!fir.ref) -> !fir.ref>}>>, !fir.field) -> !fir.ref) -> !fir.ref>> + %3 = fir.emboxproc %arg0 : ((!fir.ref) -> !fir.ref) -> !fir.boxproc<(!fir.ref) -> !fir.ref> + fir.store %3 to %2 : !fir.ref) -> !fir.ref>> + %4 = fir.field_index solve, !fir.type<_QFtestTmatrix{element:!fir.array<2x2xf32>,solve:!fir.boxproc<(!fir.ref) -> !fir.ref>}> + %5 = fir.coordinate_of %0, %4 : (!fir.ref,solve:!fir.boxproc<(!fir.ref) -> !fir.ref>}>>, !fir.field) -> !fir.ref) -> !fir.ref>> + %6 = fir.load %5 : !fir.ref) -> !fir.ref>> + %7 = fir.box_addr %6 : (!fir.boxproc<(!fir.ref) -> !fir.ref>) -> ((!fir.ref) -> !fir.ref) + %8 = fir.call %7(%arg1) : (!fir.ref) -> !fir.ref + return +} +``` + +--- + +# 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 + + +Current list of TODOs in code generation: + +NOTE: There are any number of possible implementations possible. + +- `flang/lib/Optimizer/CodeGen/TypeConverter.h:64` TODO: BoxProcType type conversion +- `flang/lib/Optimizer/CodeGen/BoxedProcedure.cpp:136` not yet implemented: record type with a boxproc type + +or + +- `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