[flang-commits] [flang] [llvm] [flang][docs] Update runtime trampoline documentation (PR #215233)

Sairudra More via flang-commits flang-commits at lists.llvm.org
Tue Aug 11 07:49:24 PDT 2026


================
@@ -259,153 +264,93 @@ trampoline1:
 ...
 ```
 
-The `TDATA` memory is writeable and contains *<static chain address, function address>*
-for each of the trampolines.
-
-A runtime support library may provide APIs for initializing/accessing/deallocating
-the trampolines that can be used by `BoxedProcedure` pass.
-
-### Implementation considerations
-
-* The static chain address still has to be passed in fixed target-specific register,
-  and the implementations that rely on LLVM back-ends can use `nest` attribute for this.
-
-* The trampoline area must be able to grow, because there can be a trampoline
-  for each internal procedure per host invocation, and an internal procedure can call
-  the host recursively. This means that the amount of trampolines in one thread
-  may grow pretty quickly.
-
-  ```fortran
-  recursive subroutine host(local)
-    use other
-    integer :: local
-    call foo(callee)
-    return
-
-    contains
-
-    function callee()
-      integer :: callee
-      if (local .le. CONST_N) then
-         call host(local + 1)
-      endif
-    end function callee
-  end subroutine host
-  ```
-
-* On the other hand, putting a hard limit on the number of trampolines live at the same time
-  allows putting the trampolines into the static code segment.
-
-* Each thread may have its own dynamic trampoline area to reduce the number
-  of required locks.
+Each `TDATA` entry stores the callee and static-chain addresses for one
+trampoline. The generated stub loads both values, places the static-chain
+address in the target-specific register, and jumps to the callee.
 
-* Some support is required for the offload devices.
+### Implementation characteristics
 
-* Each trampoline invocation implies two indirect accesses with this approach.
+* The pool contains 1024 slots by default. `FLANG_TRAMPOLINE_POOL_SIZE` can set
+  a different capacity, as described in the
+  [runtime environment documentation](RuntimeEnvironment.md).
+* Allocation from a full pool terminates the program with a diagnostic. The
+  pool does not grow dynamically.
+* `TrampolineFree` returns a slot to the synchronized global pool for reuse.
+  The implementation does not use a dynamic trampoline area per thread.
+* Each trampoline invocation loads the static chain and callee addresses from
+  its paired data entry.
 
-### Fortran runtime support
+### Fortran runtime API
 
-The following APIs are suggested:
+The `BoxedProcedure` pass uses these runtime APIs:
 
 ```c++
 /**
- * \brief Initializes new trampoline and returns its internal handle.
+ * \brief Initializes a new trampoline and returns its internal handle.
  *
- * Initializes new trampoline with the given \p callee_address
- * and \p static_chain_address, and returns the new trampoline's
+ * Initializes a new trampoline with the given \p callee_address
+ * and \p static_chain_address, and returns the trampoline's
  * internal handle. The compiler calls this method once per host
  * invocation for each internal procedure that will need its address
  * passed around.
  *
- * The initialization is reserving a new entry in TDATA and
- * initializes the entry with the given \p callee_address and
- * \p static_chain_address; it is also reserving a new entry
- * in the trampoline area that is using the corresponding TDATA entry.
- *
- * Optional:
- *   \p scratch may be used to switch between the trampoline pool
- *   and llvm.init.trampoline implementation, e.g. if compiler passes
- *   non-null \p scratch it will be used as a writeable/executable
- *   memory for the new trampoline.
+ * \p scratch is reserved and currently ignored. The lowering passes
+ * a null pointer; this argument does not select the default
+ * stack-trampoline implementation.
  */
-const void *InitTrampoline([[maybe_unused]] void *scratch,
-                           const void *callee_address,
-                           const void *static_chain_address);
+void *TrampolineInit(void *scratch, const void *callee_address,
+                     const void *static_chain_address);
 
 /**
  * \brief Returns the trampoline's address for the given handle.
  *
- * \p handle is a value returned by InitTrampoline().
- * The result of AdjustTrampoline() is the actual callable
+ * \p handle is a value returned by TrampolineInit().
+ * The result of TrampolineAdjust() is the actual callable
  * trampoline's address.
- *
- * Optional: may be implemented via llvm.adjust.trampoline.
  */
-const void *AdjustTrampoline(const void *handle);
+void *TrampolineAdjust(void *handle);
 
 /**
  * \brief Frees internal resources occupied for the given trampoline.
  *
  * The compiler must call this API at every exit from the host function.
- *
- * Optional: may be no-op, if LLVM trampolines are used underneath.
  */
-void FreeTrampoline(void *handle);
+void TrampolineFree(void *handle);
 ```
 
-`InitTrampoline` will do the initial allocation of the TDATA memory
-and the trampoline area followed by the initialization of the trampoline
-area with the binary code to "link" the trampolines with the corresponding
-TDATA entries. After the initial allocation the trampoline area is made
-executable and not writeable.
-
-If there is an available entry in the TDATA/trampoline area, then the function
-will initialized the TDATA entry with the given arguments and return
-a handle to the trampoline entry.
-
-`FreeTrampoline` will free the reserved entry.
-
-> NOTE: `FreeTrampoline` may reset the `callee_address` in the trampoline
-being freed to a runtime library function that complains about a dead
-internal procedure being called. This provides some runtime diagnostics
-of dangling procedure pointer usage. Such freed trampolines may still
-have to be reclaimed, if new trampoline is requested and the trampoline
-area is all used.
+`TrampolineInit` initializes the pool on first use, reserves an available slot,
+stores the callee and static chain addresses in its data entry, and returns an
+opaque handle. `TrampolineAdjust` returns the executable code address for that
+slot. `TrampolineFree` invalidates the data entry and returns the slot to the
+pool.
 
 #### Sample IR
 
 ```
     // Init the trampoline once per host procedure invocation
     // (i.e. when the procedure address is emboxed).
-    %handle = llvm.call @_FortranAInitTrampoline(%nullptr, %9, %7) : (!llvm.ptr<i8>, !llvm.ptr<i8>, !llvm.ptr<i8>) -> !llvm.ptr<i8>
+    %handle = llvm.call @_FortranATrampolineInit(%nullptr, %9, %7) : (!llvm.ptr<i8>, !llvm.ptr<i8>, !llvm.ptr<i8>) -> !llvm.ptr<i8>
     // Get the actual internal procedure address once per host procedure invocation.
-    %10 = llvm.call @_FortranAAdjustTrampoline(%handle) : (!llvm.ptr<i8>) -> !llvm.ptr<i8>
+    %10 = llvm.call @_FortranATrampolineAdjust(%handle) : (!llvm.ptr<i8>) -> !llvm.ptr<i8>
     %11 = llvm.bitcast %10 : !llvm.ptr<i8> to !llvm.ptr<func<void ()>>
     llvm.call @_QMotherPfoo(%11) {fastmathFlags = #llvm.fastmath<fast>} : (!llvm.ptr<func<void ()>>) -> ()
     // The trampoline deallocation must be done only at the exits from the host procedure.
-    llvm.call @_FortranAFreeTrampoline(%handle) : (!llvm.ptr<i8>) -> ()
+    llvm.call @_FortranATrampolineFree(%handle) : (!llvm.ptr<i8>) -> ()
 ```
 
-### Implementation options
-
-We may try to reuse [libffi](https://github.com/libffi/libffi) implementation for __static trampolines__:
-* Initial implementation added support for x64, i386, aarch64 and arm on Linux: https://github.com/libffi/libffi/pull/624
-* Follow-up patches:
-  * Added support for Cygwin: https://github.com/libffi/libffi/commit/a1130f37712c03957c9b0adf316cd006fa92a60b
-  * Added support for LoongArch: https://github.com/libffi/libffi/pull/723
-  * Page protection for iOS devices: https://github.com/libffi/libffi/pull/718
-  * Fix for trampoline code for x32: https://github.com/libffi/libffi/pull/657
-* The author (@madvenka786) initially [proposed](https://sourceware.org/pipermail/libffi-discuss/2021/002587.html) to make the trampoline APIs public,
-  but this was not a requirement at the time and the APIs were made private.
-  If we want to rely on `libffi`, the APIs have to be made public.
-* We may also try to extract the static trampolines implementation from `libffi`
-  into separate library (e.g. `libstatictramp` as mentioned [here](https://sourceware.org/pipermail/libffi-discuss/2021/002592.html)).
-
-Flang's own implementation for trampolines have an advantage that,
-having to support the only Fortran/C interoperable calling convention,
-the implementation may reduce the trampoline overhead. For example,
-it may avoid saving/restoring the scratch registers used by the trampoline code,
-and just clobber some of them according to the particular ABI.
-
-At this point, the recommended approach is to implement the trampoline
-support in Flang runtime.
+The current implementation is self-contained in the Flang runtime.
+Because it needs to support only the Fortran/C interoperable calling convention,
+the implementation may reduce trampoline overhead by clobbering ABI-permitted
+scratch registers rather than saving and restoring them.
+
+### Implementations that were considered
+
+Alternative implementations were considered, but not pursued. Some of these are
+described in this section.
+
+Reusing the
+[libffi](https://github.com/libffi/libffi) implementation for __static
+trampolines__ was one option. Extracting the static-trampoline implementation
+from `libffi` into a separate library was another alternative that was considered
+(e.g. `libstatictramp`, as
+mentioned [here](https://sourceware.org/pipermail/libffi-discuss/2021/002592.html)).
----------------
Saieiei wrote:

Sure not an issue. Will work on this and push it back.

https://github.com/llvm/llvm-project/pull/215233


More information about the flang-commits mailing list