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

Problem Part 1: non-portable DLL

Fortran

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).

Solution Part 1: non-portable DLL

Fortran

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
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

Problem Part 2: portable DLL

Fortran

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).

Solution Part 2: portable DLL

Fortran

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.

Comments