Consider the following example Fortran function contained in Fortran module Square_mod which takes an input real number of type real64 and returns its square as output,

module Square_mod
implicit none
contains
function getSquare(val) result(valSquared)
use, intrinsic :: iso_fortran_env, only: RK => real64
real(RK), intent(in) :: val
real(RK)             :: valSquared
valSquared = val ** 2
end function getSquare
end module Square_mod


Modify this function and provide the Intel Fortran Compiler command ifort with the required flags to generate Fortran DLL file that can be called from Fortran programs compiled by the same compiler. Then, write a Fortran program that calls this function from the DLL library and computes getSquare(100._RK).

To export a Fortran DLL via Intel Fortran Compiler one needs to add the following comment to the function that has to appear in the line immediately after the function interface.

module Square_mod
implicit none
contains
function getSquare(val) result(valSquared)
!DEC$ATTRIBUTES DLLEXPORT :: getSquare use, intrinsic :: iso_fortran_env, only: RK => real64 real(RK), intent(in) :: val real(RK) :: valSquared valSquared = val ** 2 end function getSquare end module Square_mod  The exclamation mark in !DEC$ simply converts the entire line to a Fortran comment so that this line can only be parsed by the Fortran compiler when needed. The rest, DEC$ tells the Intel compiler to parse this line and export the function named getSquare to the DLL file. The name that appears after ATTRIBUTES DLLEXPORT ::  must be the exact name of the function that is being exported. By default, the DLL export comment line will be ignored by all Fortran compilers, including Intel’s. However, the DLL comment line is recognizable by the Intel Fortran compiler only if the ifort command is invoked with the following optional flags, ifort /dll Square_mod.f90  ifort /dll Square_mod.f90 Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.0.4.245 Build 20190417 Copyright (C) 1985-2019 Intel Corporation. All rights reserved. Microsoft (R) Incremental Linker Version 14.22.27905.0 Copyright (C) Microsoft Corporation. All rights reserved. -out:Square_mod.dll -dll -implib:Square_mod.lib Square_mod.obj Creating library Square_mod.lib and object Square_mod.exp  where Square_mod.f90 is the name of the file containing the Square_mod module. This command creates the following files, 1. a DLL file named Square_mod.dll containing the library’s executable code and, 2. an import library, Square_mod.lib which the linker uses to associate a main program with the DLL, and which one must link with applications that call the DLL. If you also specify /exe:filename, the filename you specify will be used instead of the default DLL name, ifort /dll Square_mod.f90 /exe:Square  ifort /dll Square_mod.f90 /exe:Square Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.0.4.245 Build 20190417 Copyright (C) 1985-2019 Intel Corporation. All rights reserved. Microsoft (R) Incremental Linker Version 14.22.27905.0 Copyright (C) Microsoft Corporation. All rights reserved. -out:Square.dll -dll -implib:Square.lib Square_mod.obj Creating library Square.lib and object Square.exp  To see the contents of the generated library file, one can use the Windows command, dumpbin -exports Square.lib  Microsoft (R) COFF/PE Dumper Version 14.22.27905.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file Square.lib File Type: LIBRARY Exports ordinal name SQUARE_MOD_mp_GETSQUARE Summary C0 .debug$S
14 .idata$2 14 .idata$3
8 .idata$4 8 .idata$5
C .idata$6  or, dumpbin /symbols Square.exp  Microsoft (R) COFF/PE Dumper Version 14.22.27905.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file Square.exp File Type: COFF OBJECT COFF SYMBOL TABLE 000 01006D01 ABS notype Static | @comp.id 001 00000810 ABS notype Static | @feat.00 002 00000000 SECT1 notype Static | .edata 003 00000000 SECT2 notype Static | .debug$S
004 00000032 SECT1  notype       Static       | szName
005 00000028 SECT1  notype       Static       | rgpv
006 0000002C SECT1  notype       Static       | rgszName
007 00000030 SECT1  notype       Static       | rgwOrd
008 0000003D SECT1  notype       Static       | $N00001 009 00000000 UNDEF notype External | SQUARE_MOD_mp_GETSQUARE String Table Size = 0x1C bytes Summary 151 .debug$S
55 .edata


Here is an example Fortran program that uses the DLL file,

program Square_prog
use, intrinsic :: iso_fortran_env, only: RK => real64
use Square_mod, only: getSquare
implicit none
real(RK) :: val = 100._RK
write(*,"(*(g0.13,:,' '))") "Square of", val, "is", getSquare(val)
end program Square_prog


Compile this program Square_prog.f90 with the following Intel ifort command to get an executable with a default name Square_prog.exe,

ifort Square_prog.f90 Square.lib

ifort Square_prog.f90 Square.lib
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.0.4.245 Build 20190417

Microsoft (R) Incremental Linker Version 14.22.27905.0

-out:Square_prog.exe
-subsystem:console
Square_prog.obj
Square.lib


Note that Square.lib has to appear as the last input argument to ifort. Now running the executable would yield,

Square_prog.exe

Square of 100.0000000000 is 10000.00000000


Alternatively, one could rename the executable via -o filename flag, or on the Windows-systems only, via /exe:filename, where filename is the name of the output executable by the ifort linker,

ifort Square_prog.f90 Square.lib -o main.exe

ifort Square_prog.f90 Square.lib -o main.exe
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.0.4.245 Build 20190417

Microsoft (R) Incremental Linker Version 14.22.27905.0

-out:main.exe
-subsystem:console
Square_prog.obj
Square.lib


The approach described above works only within the Intel Fortran compiler environment. That is because of a technique called name mangling that compilers utilize to encode function and variable names into unique names so that linkers can separate common names used within a program or the language. Since each Fortran compiler has its set of rules and conventions for name mangling, the above simplistic approach to DLL generation can be potentially non-portable, that is, not useful for mixed-language or mixed-compiler programming. Fortunately, the Fortran standard provides a method to request the compiler to generate a unique global identifier provided by the user for a function of interest. This can be done via Fortran’s intrinsic bind(C, name=globalName) in the function/subroutine interface, with globalName representing the global identifier specified by the user for the Fortran function.

Given this new information, modify the function getSquare to give it a global identifier getSq as well as exporting the original name getSquare via Intel’s DLLEXPORT. Also, provide the Intel Fortran Compiler command ifort with the required flags to generate Fortran DLL file that can be called from Fortran programs compiled by the same compiler. Then, write a Fortran program that calls this function from the DLL library in two different ways, one through use Square_mod, only: getSquare to compute getSquare(100._RK) and the other, by defining the required explicit function interface and DLLIMPORT to call the global identifier getSq directly from within the program to compute getSq(100._RK).

To make the generated DLL portable, or simply to call the function inside the DLL file with a different name from the function’s original name, for example getSq instead of getSquare, one can use Fortran’s intrinsic bind() in the function’s interface to define a global identifier for the function,

module Square_mod
implicit none
contains
function getSquare(val) result(valSquared) bind(C, name="getSq")
!DEC$ATTRIBUTES DLLEXPORT :: getSquare use, intrinsic :: iso_fortran_env, only: RK => real64 real(RK), intent(in) :: val real(RK) :: valSquared valSquared = val ** 2 end function getSquare end module Square_mod  and call the DLL function either in the old fashion via use Square_mod, only: getSquare or in the new approach by calling the global identifier getSq instead in the calling program, for example, program Square_prog use, intrinsic :: iso_fortran_env, only: RK => real64 use Square_mod, only: getSquare implicit none real(RK) :: val = 100._RK interface function getSq(val) result(valSquared) !DEC$ ATTRIBUTES DLLIMPORT, DECORATE, ALIAS: 'getSq' :: getSq
use, intrinsic :: iso_fortran_env, only: RK => real64
real(RK), intent(in) :: val
real(RK)             :: valSquared
end function getSq
end interface
write(*,"(*(g0.13,:,' '))") "getSquare(", val, ") =", getSquare(val)
write(*,"(*(g0.13,:,' '))") "    getSq(", val, ") =", getSq(val)
end program Square_prog

ifort /dll PortableSquare_mod.f90 /exe:PortableSquare

Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.0.4.245 Build 20190417

Microsoft (R) Incremental Linker Version 14.22.27905.0

-out:PortableSquare.dll
-dll
-implib:PortableSquare.lib
PortableSquare_mod.obj
Creating library PortableSquare.lib and object PortableSquare.exp

ifort PortableSquare_prog.f90 PortableSquare.lib -o main.exe

Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.0.4.245 Build 20190417

Microsoft (R) Incremental Linker Version 14.22.27905.0

-out:main.exe
-subsystem:console
PortableSquare_prog.obj
PortableSquare.lib

main.exe

getSquare( 100.0000000000 ) = 10000.00000000
getSq( 100.0000000000 ) = 10000.00000000


Note, however, that the use of the global identifier from the DLL inside the Fortran main program requires an explicit interface of the original function as defined in the above example.