Intel® Integrated Performance Primitives
Deliberate problems developing high-performance vision, signal, security, and storage applications.

Calling IPP from Fortran

eos_pengwern
Beginner
968 Views
I'd like to access a few IPP (Intel Performance Primitives) functions from my Fortran code, and as there doesn't seem to be a standard Fortran interface to them I began to generate my own. As for the time being I'm only interested in two functions, my first attempt at writing the required module is short enough to reproduce here in full:

[fortran]module IPP

    use, intrinsic :: iso_c_binding
    implicit none
    
    integer, parameter :: IPPI_INTER_NN      = 1
    integer, parameter :: IPPI_INTER_LINEAR  = 2
    integer, parameter :: IPPI_INTER_CUBIC   = 4
    integer, parameter :: IPPI_INTER_SUPER   = 8
    integer, parameter :: IPPI_INTER_LANCZOS = 16
    
  ! --------------------------------------------------
  
    type, bind(c) :: IppiRect
    
        integer :: x
        integer :: y
        integer :: width
        integer :: height
    
    end type IppiRect

  ! ---------------------------------------------------

    type, bind(c) :: IppiSize
    
        integer :: width
        integer :: height
    
    end type IppiSize

  ! --------------------------------------------------

    interface
        function ippiResizeGetBufSize(srcROI, dstROI, nChannels, interpolation, bufsize) bind(c)
                                                   ! N.B. 'bind(c)' implies 'external'
                                                                                     
            type(IppiRect), intent(in) :: srcROI, dstROI
            integer, value :: nChannels, interpolation
            integer, intent(out) :: bufsize
            
            integer :: ippiResizeGetBufSize
            
        end function ippiResizeGetBufSize
    end interface
      
  ! --------------------------------------------------
interface function ippiResizeSqrPixel_64f_C1R(pSrc, srcSize, srcStep, srcROI, pDst, dstStep, dstROI, & xFactor, yFactor, xShift, yShift, interpolation, pBuffer) bind(c) ! N.B. 'bind(c)' implies 'external' real(c_double), intent(in), dimension(srcROI%width, srcROI%height) :: pSrc type(IppiSize), value :: srcSize integer, value :: srcStep type(IppiRect), value :: srcROI real(kind(1d0)), intent(out), dimension(dstROI%width, dstROI%height) :: pDst integer, value :: dstStep type(IppiRect), value :: dstROI real(c_double), value :: xFactor, yFactor, xShift, yShift integer, value :: interpolation character, intent(inout), dimension(1) :: pBuffer integer :: ippiResizeSqrPixel_64f_C1R end function ippiResizeSqrPixel_64f_C1R end interface end module IPP[/fortran]

The reasons that this doesn't work have surprised and, so far, baffled me. The interface blocks don't seem to be able to make use of the derived types which are defined immediately above them in the same module. Specifically, here are my compilation error messages:


[bash]IPP.f90(38): error #6457: This derived type name has not been declared.   [IPPIRECT]
IPP.f90(35): warning #6717: This name has not been given an explicit type.   [SRCROI]
IPP.f90(35): warning #6717: This name has not been given an explicit type.   [DSTROI]
IPP.f90(54): warning #6717: This name has not been given an explicit type.   [SRCROI]
IPP.f90(54): error #6535: This variable or component must be of a derived or structure type   [SRCROI]
IPP.f90(54): error #6460: This is not a field name that is defined in the encompassing structure.   [WIDTH]
IPP.f90(54): error #6223: A specification expression is invalid.   [WIDTH]
IPP.f90(54): error #6460: This is not a field name that is defined in the encompassing structure.   [HEIGHT]
IPP.f90(54): error #6223: A specification expression is invalid.   [HEIGHT]
IPP.f90(55): error #6457: This derived type name has not been declared.   [IPPISIZE]
IPP.f90(57): error #6457: This derived type name has not been declared.   [IPPIRECT]
IPP.f90(58): warning #6717: This name has not been given an explicit type.   [DSTROI]
IPP.f90(58): error #6535: This variable or component must be of a derived or structure type   [DSTROI]
IPP.f90(58): error #6223: A specification expression is invalid.   [WIDTH]
IPP.f90(58): error #6223: A specification expression is invalid.   [HEIGHT]
IPP.f90(60): error #6457: This derived type name has not been declared.   [IPPIRECT]
IPP.f90(54): warning #6717: This name has not been given an explicit type.   [WIDTH]
IPP.f90(54): warning #6717: This name has not been given an explicit type.   [HEIGHT]
IPP.f90(50): warning #6717: This name has not been given an explicit type.   [SRCSIZE][/bash]

So far there's nothing IPP-specific in any of this - I haven't tried linking the libraries themselves in yet - so these are entirely generic Fortran errors. I would expect this to compile though, so what have I missed?

Thanks,

Stephen.

0 Kudos
1 Solution
IanH
Honored Contributor II
968 Views
Interface blocks are a totally different scope to the program unit that hosts them. Unlike internal procedures or module procedures, they don't inherit by host association any of the things that are defined by the host. (They also don't inherit the implicit typing rules of the host - if you don't specify IMPLICIT NONE inside an interface body in an interface block then ye-olde fortran implicit typing is in effect in that interface body.)

To bring a type (or parameter, etc) in from the parent scope you can either repeat the definition of the the type definition (only if it is a BIND(C) or SEQUENCE type); or put the type definition in a separate module and then USE that module inside the interface body (also applicable to the entities from ISO_C_BINDING) and the parent scope; or your can use the IMPORT statement introduced with Fortran 2003.

For example:

[fortran]   interface  
        function ippiResizeGetBufSize(srcROI, dstROI, nChannels, interpolation, bufsize) bind(c)  
                                                   ! N.B. 'bind(c)' implies 'external'  
            import :: IppiRect                                              
            implicit none
            type(IppiRect), intent(in) :: srcROI, dstROI  
            integer, value :: nChannels, interpolation  
            integer, intent(out) :: bufsize                
            integer :: ippiResizeGetBufSize                
        end function ippiResizeGetBufSize  
    end interface  
[/fortran]
On a separate note - be mindful that without a NAME= clause the binding label for a procedure will be an all lower version of your procedure name - not the mixed case that you've used in your function statements. I don't know whether you want the mixed or lower case variant.

View solution in original post

0 Kudos
10 Replies
IanH
Honored Contributor II
969 Views
Interface blocks are a totally different scope to the program unit that hosts them. Unlike internal procedures or module procedures, they don't inherit by host association any of the things that are defined by the host. (They also don't inherit the implicit typing rules of the host - if you don't specify IMPLICIT NONE inside an interface body in an interface block then ye-olde fortran implicit typing is in effect in that interface body.)

To bring a type (or parameter, etc) in from the parent scope you can either repeat the definition of the the type definition (only if it is a BIND(C) or SEQUENCE type); or put the type definition in a separate module and then USE that module inside the interface body (also applicable to the entities from ISO_C_BINDING) and the parent scope; or your can use the IMPORT statement introduced with Fortran 2003.

For example:

[fortran]   interface  
        function ippiResizeGetBufSize(srcROI, dstROI, nChannels, interpolation, bufsize) bind(c)  
                                                   ! N.B. 'bind(c)' implies 'external'  
            import :: IppiRect                                              
            implicit none
            type(IppiRect), intent(in) :: srcROI, dstROI  
            integer, value :: nChannels, interpolation  
            integer, intent(out) :: bufsize                
            integer :: ippiResizeGetBufSize                
        end function ippiResizeGetBufSize  
    end interface  
[/fortran]
On a separate note - be mindful that without a NAME= clause the binding label for a procedure will be an all lower version of your procedure name - not the mixed case that you've used in your function statements. I don't know whether you want the mixed or lower case variant.
0 Kudos
eos_pengwern
Beginner
968 Views
Thank you Ian. That was the key to it.

Here is the version that compiles without error:

[fortran]odule IPP

    use, intrinsic :: iso_c_binding
    implicit none
    
    integer, parameter :: IPPI_INTER_NN      = 1
    integer, parameter :: IPPI_INTER_LINEAR  = 2
    integer, parameter :: IPPI_INTER_CUBIC   = 4
    integer, parameter :: IPPI_INTER_SUPER   = 8
    integer, parameter :: IPPI_INTER_LANCZOS = 16
    
  ! -----------------------------------------------
  
    type, bind(c) :: IppiRect
    
        integer :: x
        integer :: y
        integer :: width
        integer :: height
    
    end type IppiRect

  ! -----------------------------------------------

    type, bind(c) :: IppiSize
    
        integer :: width
        integer :: height
    
    end type IppiSize

  ! -----------------------------------------------

    interface
        function ippiResizeGetBufSize(srcROI, dstROI, nChannels, interpolation, bufsize)             &
                                      bind(c, name='ippiResizeGetBufSize')
                                      ! N.B. 'bind(c)' implies 'external'
                                                        
            use, intrinsic :: iso_c_binding
            import :: IppiRect
            implicit none
                                                                                                 
            type(IppiRect), intent(in) :: srcROI, dstROI
            integer, value :: nChannels, interpolation
            integer, intent(out) :: bufsize
            
            integer :: ippiResizeGetBufSize
            
        end function ippiResizeGetBufSize
    end interface
      
  ! -----------------------------------------------

    interface
        function ippiResizeSqrPixel_64f_C1R(pSrc, srcSize, srcStep, srcROI, pDst, dstStep, dstROI,    &
                                            xFactor, yFactor, xShift, yShift, interpolation, pBuffer) &
                                            bind(c, name='ippiResizeSqrPixel_64f_C1R')
                                            ! N.B. 'bind(c)' implies 'external'    

            use, intrinsic :: iso_c_binding
            import :: IppiRect, IppiSize
            implicit none

 
            integer, value :: srcStep
            type(IppiRect), value :: srcROI
            type(IppiSize), value :: srcSize
            real(c_double), intent(in), dimension(srcROI%width, srcROI%height) :: pSrc
            integer, value :: dstStep
            type(IppiRect), value :: dstROI
            real(c_double), intent(out), dimension(dstROI%width, dstROI%height) :: pDst
            real(kind(1d0)), value :: xFactor, yFactor, xShift, yShift
            integer, value :: interpolation
            character, intent(inout), dimension(1) :: pBuffer
            
            integer :: ippiResizeSqrPixel_64f_C1R
            
        end function ippiResizeSqrPixel_64f_C1R
    end interface
            
end module IPP[/fortran]


All I have to do now is to get the linking right, and check that it all works.

Regards,
Stephen.
0 Kudos
eos_pengwern
Beginner
968 Views
"All I have to do now is to get the linking right, and check that it all works."

(hollow laughter)

Sometimes, you just know you're on a hiding to nothing. Nothing I've tried so far has enabled my Fortran linker to find the IPP functions, and even Intel Premier Support say "sorry, we don't support this". There are in existence some examples of IPP cryptography functions being called from Fortran without ISO_C_BINDING but with many lines of !DEC$ ATTRIBUTES and !MS$ATRIBUTES declarations(which I've never seen before), but I haven't been able to imitate these successfully.

I guess it's time to find another solution, without the IPP.
0 Kudos
TimP
Honored Contributor III
968 Views

ipp is an optional component of icc installation or icc redistributable library. If you have the same version of icc and ifort, the environment script should put the ipp libraries on the search path, so -lipp..... should find them (in the path under /ipp/lib in the directory which contains the ifort /lib/).

There's no move to support .mod files to take care of the ISO_C INTERFACE or automatic choice of the relevant ipp libraries under ifort, so this involves significant hassle.

0 Kudos
eos_pengwern
Beginner
968 Views
Sadly yes... I know where the IPP .lib and .dll files are in the path, as I have another C++ application that uses them successfully. I think the problem from Fortran is entirely one of interface and/or calling convention, so that the linker doesn't recognise the IPP function in the library on the basis of the description provided in the Fortran interface block.
0 Kudos
Wendy_Doerner__Intel
Valued Contributor I
968 Views
You should in theory be able to call ipp functions from Fortran just like you make an interlanguage call from Fortran to C routine with parameters. I have checked with the premier support manager for IPP and she suggested to send you to their forum in case they have some more concrete suggestions now that you have working Fortran code.

There is a cryptography example that shows how to call IPP from Fortran (you may have seen this).

But ultimately you need to figure out based on the IPP definition files for the parameters how to specify them in the FORTRAN for MIxed Language calls. The section of the Fortran Documentation that discusses this is:

Mixed Language Programming

------

Wendy

Attaching or including files in a post

0 Kudos
eos_pengwern
Beginner
968 Views
I tried writing a separate C DLL with simplified interfaces to the IPP, and although I could link to the IPP from the C library I still couldn't link to the C library from Fortran, so I don't know what I did wrong. I must be getting my _cdecls and _stdcalls in a twist.

I have come up with a solution that works for me, for now, though. As my Fortran code is a library that is called from a C++ application which itself makes use of IPP routines, I have put the C functions with simplified interfaces into my main application code, and then passed pointers to them as 'callbacks' alongside the other parameters being passed to the Fortran routines. When the Fortran routines need to access the IPP, they just call the callback functions they've been provided with. This works fine. Although it makes the source code a little more convoluted than I'd like, ithas the big advantage thatI don't need to touch my makefiles.This is useful as this suite gets compiled on various different platforms. It also confirms that my C interfaces are fine, so there's obviously some other reason why I can't seem to do the linking from Fortran.

Stephen.
0 Kudos
IanH
Honored Contributor II
968 Views
Quoting eos pengwern
I tried writing a separate C DLL with simplified interfaces to the IPP, and although I could link to the IPP from the C library I still couldn't link to the C library from Fortran, so I don't know what I did wrong. I must be getting my _cdecls and _stdcalls in a twist.
...

I know nothing of IPP, but this C interface layer seems a reasonable approach. Getting Fortran code that you control to talk to C code that you control should be relatively straight forward. Perhaps post the C function declaration and C compiler options along with what you used to define the interface on the Fortran side.

0 Kudos
Lorri_M_Intel
Employee
968 Views
Yeah, you're going to have to use C++ as the intermediary.

The IPP routines all have C++ interfaces, and that's not covered by ISO_C_BINDING, because of the name
mangling among other things.

-- Lorri
0 Kudos
eos_pengwern
Beginner
968 Views
Ah yes, that would explain the difficulty with the linking!

Stephen.
0 Kudos
Reply