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
-->
# Trampolines for pointers to internal procedures.
## Overview
```fortran
subroutine host()
integer :: local
local = 10
call internal()
return
contains
subroutine internal()
print *, local
end subroutine internal
end subroutine host
```
Procedure code generated for subprogram `internal()` must have access to the scope of
its host procedure, e.g. to access `local` variable. Flang achieves this by passing
an extra argument to `internal()` that is a tuple of references to all variables
used via host association inside `internal()`. We will call this extra argument
a static chain link.
Fortran standard 2008 allowed using internal procedures as actual arguments for
procedure pointer targets:
> Fortran 2008 contains several extensions to Fortran 2003; some of these are listed below.
>
> * An internal procedure can be used as an actual argument or procedure pointer target.
>
> NOTE 12.18
>
> An internal procedure cannot be invoked using a procedure pointer from either Fortran or C after the host instance completes execution, because the pointer is then undefined. While the host instance is active, however, the internal procedure may be invoked from outside of the host procedure scoping unit if that internal procedure was passed as an actual argument or is the target of a procedure pointer.
Special handling is required for the internal procedures that might be invoked
via an argument association or via pointer.
This document describes Flang implementation to support it.
> NOTE: in some languages/extensions the static chain may contain links
to more than one stack frame, while Fortra's static chain only ever
has a link to a single host procedure.
## Flang current implementation
### Examples
Internal procedure as procedure pointer target:
```fortran
module other
abstract interface
function callback()
integer :: callback
end function callback
end interface
contains
subroutine foo(fptr)
procedure(callback), pointer :: fptr
! `fptr` is pointing to `callee`, which needs the static chain link.
print *, fptr()
end subroutine foo
end module other
subroutine host(local)
use other
integer :: local
procedure(callback), pointer :: fptr
fptr => callee
call foo(fptr)
return
contains
function callee()
integer :: callee
callee = local
end function callee
end subroutine host
program main
call host(10)
end program main
```
Internal procedure as actual argument (F90 style):
```fortran
module other
contains
subroutine foo(fptr)
interface
integer function fptr()
end function
end interface
! `fptr` is pointing to `callee`, which needs the static chain link.
print *, fptr()
end subroutine foo
end module other
subroutine host(local)
use other
integer :: local
call foo(callee)
return
contains
function callee()
integer :: callee
callee = local
end function callee
end subroutine host
program main
call host(10)
end program main
```
Internal procedure as actual argument (F77 style):
```fortran
module other
contains
subroutine foo(fptr)
integer :: fptr
! `fptr` is pointing to `callee`, which needs the static chain link.
print *, fptr()
end subroutine foo
end module other
subroutine host(local)
use other
integer :: local
call foo(callee)
return
contains
function callee()
integer :: callee
callee = local
end function callee
end subroutine host
program main
call host(10)
end program main
```
In all cases, the call sequence implementing `fptr()` call site inside `foo()`
must pass the stack chain link to the actual function `callee()`.
### Usage of trampolines in Flang
`BoxedProcedure` pass recognizes `fir.emboxproc` operations that
embox a subroutine address together with the static chain link,
and transforms them into a sequence of operations that replace
the result of `fir.emboxproc` with an address of a trampoline.
Eventually, it is the address of the trampoline that is passed
Note that value of `%p` is equal to `%tramp1` in most cases, but this is not
a requirement - this is partly [why](https://lists.llvm.org/pipermail/llvm-dev/2011-August/042845.html)
the second intrinsic was introduced:
> ```
> By the way an example of adjust_trampoline is ARM, which or's a 1 into the address of the trampoline. When the pointer is called the processor sees the 1 and puts itself into thumb mode.
Currently, the trampolines are allocated on the stack of `host()` subroutine,
so that they are available throughout the life span of `host()` and are
automatically deallocated at the end of `host()` invocation.
Unfortunately, this requires the program stack to be writeable and executable
at the same time, which might be a security concern.
> NOTE: LLVM's AArch64 backend supports `nest` attribute, but it does not seem to support trampoline intrinsics.
## Alternative implementation(s)
To address the security risk we may consider managing the trampoline memory
in a way that it is not writeable and executable at the same time.
One of the options is to use separate allocations for the trampoline code
and the trampoline "data".
The trampolines may be located in non-writeable executable memory: