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
Tip: Here, RK
is an alias to (or basically, an abbreviation for) the real kind real64
which is imported from the Fortran standard’s intrinsic module iso_fortran_env
. It is then used to declare the kinds of the real input number and the output result.
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,
- a DLL file named
Square_mod.dll
containing the library’s executable code and, - 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
Tip: For more information on how to create DLLs with Intel Fortran compiler, see this page.
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
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_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
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: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
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: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
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: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.
Tip: The Fortran intrinsic attribute bind(C, name="getSq")
in the function interface has nothing to do with DLL generation. It is an interoperation-with-C-language capability that exists in the Fortran standard, which tells the compiler to avoid name-mangling (changing the function name to something else) so that the Fortran code becomes more portable and the code, whether compiler by Intel or any other compiler, generates a DLL that contains a function with the exact global name as specified by name="getSq"
in the function interface.