Implementation of a parametrized objective function without using module variables or internal subroutines

Then let us use “recursion-safe”. I do believe “recursion” is a subject of Fortran standards. Module variables are not recursion-safe. I have observed this in real applications.

Or let me put it in this way: how to implement the code in my question without using module variables or internal procedures?

We want to avoid these two constructs for what so ever reason that is unspecified.

Thanks.

1 Like

@zaikunzhang I think the answer to your question is that of @RonShepard, which posted it at the same time as you posted.
Therefore, I think you might miss it.

1 Like

Thank you @chchanvit. But I hope the implementation of such a basic use case will not depend on external libraries like OpenMP.

Why is it basic? It is essentially an example from the Fortran standard, which implements it using internal procedures.

I have been hoping that it can be implemented using derived types (this does not mean to change the interface of solver to accept a derived type as an input), but my knowledge about derived types is not sufficient, and nobody has suggest such a solution yet. The LLMs have attempted in this direction but none of them has suggested a valid implementation.

At this level you need to distinguish, the Fortran standard from a Fortran processor implementation.

Older versions of GCC (pre 13) chose a particular design implementations (executable stack) which has become problematic in recent years due to increased cyber-security concerns. It seems the initial effort to introduce internal procedures as arguments into gfortran started back in 2010: Tobias Burnus - RFC: Function pointer to internal function

Flang for instance chose a different approach, also using trampolines: Trampolines for pointers to internal procedures. — The Flang Compiler

I think currently you have to use internal procedures. For example the NAG compiler implements them without executable stack and it does support recursion (I am not sure about thread safety, but I think it might be related).

How would you do this in C? Isn’t this exactly the same problem as in Fortran? For this reason most 3rd party libraries allow you to pass user data. If they don’t, that’s a deficiency of the 3rd party library I think. Then you have to be careful about recursion/threads, as you pointed out.

2 Likes

I mean the Fortran standard itself. See the example in Note 12.18 on page 290 of WD 1539-1 J3/10-007r1, F2008 Working Document.

All I’m trying to say is, this isn’t an issue of the Fortran standard, but a particular version of the compiler you are using (potentially indirectly through the MATLAB MEX tools). Other compiler-implementors have found ways to handle this problem in a way that doesn’t require an executable stack.

I hope Fortran is more capable than C in this respect, because Fortran is supposed to be the language for scientific computing.

In modern/popular languages such as C++, Python, Julia, and MATLAB, this can be implemented easily using closures, lambda expressions, anonymous functions, …

1 Like

I am not sure you can use a lambda function to pass to a C library that only expects a pointer, without user data. Here is an example:

https://grok.com/share/bGVnYWN5_f8024618-48cd-41c0-b0ed-102f3cfbf8cc

Can you provide C++ code that does what you want? Then we can brainstorm how to do it in Fortran.

1 Like

There are some fundamental problems here for languages like C and Fortran, more details here: Funarg problem - Wikipedia.

With C++ the situation is a bit different; lambdas and std::function work well when used within the confines of C++, but as @certik has just alluded, it faces the same limitations when trying to pass code and data across language boundaries.

For example a lambda function can be converted to a C compatible function pointer, only under certain restrictions, namely that it doesn’t capture any variables from the enclosing scope.

As @themos has written before,

Personal take: The essence of Fortran is that all executable code is present at program start. Fortran will not compose executable code at runtime, and if it ever does it shouldn’t be called Fortran anymore. In Fortran, data comes and goes but code is written in stone.

To tie back to what @certik said,

To achieve the parameterized function while preserving reentrancy, the solver would need to “open” a way to pass data (in C this would be the void * omni-parameter).

1 Like

I think this might be overstating the case a little, but basically I agree with the idea. In a von Neumann machine, both machine instructions and data are treated the same, both modifiable at runtime, and even in a self-modifying way. Prior to that development, the instructions were typically encoded on, for example, paper tape and could not be modified by the program at run time. In a similar way, in a Harvard machine the instructions and the data are kept separate, and for both security reasons and performance reasons, the instructions are usually compiled before execution and kept fixed, while the data can grow, shrink, and of course be modified at run time.

On machines that allow it, I have seen self-modifying code written in fortran (although not exactly within the standard, so it is nonportable and of course very machine-specific). The idea is to take a simple function that requires only a few instructions. An example might be a function that squares it argument. When that function is called the first time, it places the result in the return register in the usual way, but then it goes on and looks at the return address and modifies the calling sequence at that point of the code to eliminate the branch-store-branch-retrieve function operations and to place the function evaluation operations inline. If the calling code ever executes the instructions at that address again, then the function operation is effectively evaluated inline with no overhead associated with the branch-store-branch-retrieve function call. When the calling overhead associated with functions and subroutines is slow, as it was for early computers, this is a significant performance improvement, and it allows the high-level code to be written in a way that requires the function evaluation to be written only once and used any number of times by the calling program. Of course, in modern times with multiuser machines on networks it is easy to see how such a programming approach could be abused, so this kind of self-modifying code is not allowed by most modern operating systems.

This also brings up the difference in compiled and interpreted languages. In an interpreted language, the compilation steps, turning human readable character sequences into machine instructions, is done at run time, and often only a single instruction or a small group of instructions at a time. In the languages that work this way, the instructions are often discarded immediately after they are used, and if that section of code is executed again, they are regenerated, used, and discarded repeatedly. I have wondered how the efforts related to interpreted versions of fortran fit with this model.

The other way is to use internal functions. For example the NAG compiler implements them without executable stack by using global context, and the user callback examines this global context and finds the appropriate data (parent scope variables) so that it works correctly with recursion etc. I am not sure if it is thread safe, but I think it could be made thread safe as well. (LFortran also uses this technique, but so far we only implemented one level global context, so it’s not thread safe yet.)

2 Likes

There is a way to have executable code not present at program start: with execute_command_line(command) where command is a character string evaluated at run time. The problem with it is of course that it’s different in different operating systems.

I don’t program in C++. So I refer to Lambdas and Closures in C++. A lambda function is a function that… | by Pranay Kumar | Medium :

A closure is any function that closes over the environment in which it was defined. This means that it can access variables, not in its parameter list.

My understanding may be wrong.

This is very interesting. Then what do you think about the examples mentioned below? @ivanpribec

Fortran internal procedures are not real closures (since the scope in which they are defined does not survive the parent’s call).

GCC has nested functions for C, but it’s just an extension (that was probably used as a basis for gfortran’s internal procedures).

Since newer versions of gfortran seem to fix the issue of executable stack, you may want to try those…

…Or combine @ivanpribec 's and @RonShepard 's answers for a module-based approach.

2 Likes

This is correct. But you cannot pass the C++ lambda function into some function call that expects a function pointer. To be specific, I think you can pass it as long as the lambda function does not capture any variables, but that’s not what you were asking above. Thus it seems what you are asking is possible in neither C++ nor Fortran.

However, maybe you are asking about the following (please correct me if I am wrong): within C++, you can indeed use lambda functions with std::function, as @ivanpribec mentioned above, and it will pass in the context correctly. And you are asking for an equivalent feature in Fortran: so that you can write a library in Fortran that expects just a function call (not just a C function pointer, but an equivalent of std::function, that can accept a context implicitly as a language feature) and you want to be able to pass in a context, just like lambda functions allow in C++. Is this what you are asking?

1 Like

The example in Note 12.18 (omitting the interface for procedure integrate) is,

REAL FUNCTION MY_INTEGRATION(N, A, B) RESULT(INTEGRAL)
   ! Integrate f(x)=x^n over [a,b]
   USE ISO_C_BINDING
   INTEGER, INTENT(IN) :: N
   REAL, INTENT(IN) :: A, B

   INTEGRAL = INTEGRATE(MY_F, REAL(A, C_FLOAT), REAL(B, C_FLOAT))
      ! This will call the internal function MY_F to calculate f(x).
      ! The above interface of INTEGRATE must be explicit and available.

CONTAINS

   REAL(C_FLOAT) FUNCTION MY_F(X) BIND(C) ! Integrand
      REAL(C_FLOAT), VALUE :: X
      MY_F = X**N ! N is taken from the host instance of MY_INTEGRATION.
   END FUNCTION

END FUNCTION MY_INTEGRATION

The internal procedure is compiled into the executable: https://godbolt.org/z/fq6ofsb4E

1 Like

MATLAB plays the least role in the topic under discussion. Whether it works or not does not affect how Fortran should fix this issue. I mentioned it just in case it is useful to someone. Please ignore it.

Excuse me for being frank, but this is not more than wishful thinking as of today. Even gfortran has not fixed it yet up to version 14.2.0, if ever (see below). I have not tested ifx. I will not see this as being fixed until three years after both ifx and gfortran have fixed it. Why three years? Because major operating systems do not adopt the latest versions of compilers for stability. For example, the latest LTS of Ubuntu, namely 24.40, takes gfortran-13, which was released in 2023.

$ gfortran-14 --version && gfortran-14 -std=f2008 -Wall optim.f90 -o optimize
GNU Fortran (Ubuntu 14.2.0-4ubuntu2~24.04) 14.2.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

/usr/bin/ld: warning: /tmp/ccT3VNhU.o: requires executable stack (because the .note.GNU-stack section is executable)

And what about with this option: Code Gen Options (Using the GNU Compiler Collection (GCC)) ?

The software supply chain can only move at the rate of the slowest link. The longer you stick with old tools, the longer they’ll need to be around, and the longer you’ll be subject to the security vulnerability.

Edit: I just checked on SLES 15

> gfortran --version
GNU Fortran (Spack GCC) 14.2.0
...
> gfortran optim.f90
/usr/bin/ld: warning: /tmp/cccpkks1.o: requires executable stack (because the .note.GNU-stack section is executable)
> gfortran -ftrampoline-impl=heap optim.f90
>

One can verify that the routines to allocate the trampoline on the heap (i.e. __gcc_nested_func_ptr_*) are now used:

> gfortran -ftrampoline-impl=stack optim.f90
/usr/bin/ld: warning: /tmp/ccxoR5pl.o: requires executable stack (because the .note.GNU-stack section is executable)
> nm -u a.out
                 U _gfortran_set_args@GFORTRAN_8
                 U _gfortran_set_options@GFORTRAN_8
                 w __gmon_start__
                 U __libc_start_main@GLIBC_2.34
> gfortran -ftrampoline-impl=heap optim.f90
> nm -u a.out
                 U __gcc_nested_func_ptr_created@GCC_14.0.0
                 U __gcc_nested_func_ptr_deleted@GCC_14.0.0
                 U _gfortran_set_args@GFORTRAN_8
                 U _gfortran_set_options@GFORTRAN_8
                 w __gmon_start__
                 U __libc_start_main@GLIBC_2.34

To verify you can also look into the ELF binary with the command readelf -l <binary>,

With gfortran -ftrampoline-impl=stack:

  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    0x10

RWE for Read,Write,Execute

With gfortran -ftrampoline-impl=heap

  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10

The E is gone now.

With ifx I get no linker warning, and readelf -l shows:

  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10

Same for nagfor, no linker warning, and only read,write:

  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
2 Likes