1
votes

I have to call a C function from Fortran, but I want to do this in a vectorised loop. I am working with Intel 16.0.3 compilers on Linux.

So the options are: I can try and get the function to inline or I can use a SIMD function (I want to use OpenMP SIMD for this, I want it to be portable and I already use OpenMP).

If I call Fortran from Fortran it works both ways. For passing arguments I use the linear/ref clause to pass a reference to a vector of values rather than a vector of references and this seems to work efficiently. But in C the linear/ref clause is not recognised.

I can get the function to nominally vectorise but it is inserting gathers and scatters and performance is no better than scalar (at least for my small test function).

If I put linear(ref(r,s)) in the Fortran interface block I get the message

The UVAL or REF modifiers must not be used on a dummy argument with the VALUE attribute.

I can get the performance using the trick of passing by value from Fortran and returning a value as the function return. This produces a vectorised function, and performance is good, but unfortunately my real function needs to return more than one value.

If I try to inline the C function, it just won't work. The opt-report just tells me the callsite cannot be inlined. This is true even with a scalar function. I cannot get C functions to inline into Fortran at all. I am using ipo to try and do this. I have wondered if the inlining problem could be Fortran passing a pointer to a pointer? But then as the code gives the right answers it seems that it somehow acceptable.

The test code (passing pointers) is essentially...

Fortran caller

Use, intrinsic :: ISO_C_BINDING
….
real*8, allocatable :: r(:),s(:)  .
….

interface
 integer simd_c_func(r,s) bind(c, name="simd_c_func")
 !$OMP DECLARE SIMD(simd_c_func)
 import :: C_DOUBLE
 real(kind=C_DOUBLE), intent(inout):: r,s
end interface

allocate.... 

!$OMP SIMD

do i=1,N
  ierror=simd_c_func(r(i),s(i))
enddo

C callee

#pragma omp declare simd
int simd_c_func(double *r, double *s) {
 (*r)+=(*s);
 return 0;
}
2
Can you make a test program that compiles? - Chiel
These things are very new, the support is patchy and it varies from compiler version to version. Include the actual compiler output and try to make a MCVE stackoverflow.com/help/mcve - Vladimir F
OK, I will paste a working example as soon as possible. I have the code on a standalone machine with the relevant compiler package. Waiting for a transfer. - plonker13

2 Answers

2
votes

The LINEAR(REF()) is pretty new, so one can only hope that some LINEAR using OMP and CONTIGUOUS can get you joy.

The message:

The UVAL or REF modifiers must not be used on a dummy argument with the VALUE attribute."

indicates that it is doing value and on the stack. I thought C arrays were in heap? but maybe you need a -heap switch?

C-side:

I would suggest you add linear to you #pragma

#pragma omp declare simd linear(r,s)

Have you looked at using #pragma vector aligned? and also #pragma ivdep? I am assuming that (*r) is a reference (in heap)

F90-Side

I would suggest you replace:

real(8), allocatable :: r(:), s(:)

with

!$DIR ATTRIBUTES ALIGN:64                           :: R, S
REAL(KIND=8), DIMENSION(:), ALLOCATABLE, CONTIGUOUS :: R, S

The only real change here is the 64bit alignment and the contiguous. One can also use a switch -align array64byte

If ierror is not an array then do you want one? or do you perhaps need to use a first/last private or a reduction clause on ierror? I would probably use reduction.

!$OMP SIMD then goes to:

!$OMP DO SIMD REDUCTION(ierror)

But as you mention that you need more than one value then maybe you need to allocate ierror?

And I am interested in how the interface knows that it gets a return value of zero. Seem like you have to have INTEGER FUNCTION somewhere in the interface.

Linking-Compiling side:

If C produced an F90- MODULE then the USE would allow it to see deep enough into the C code to inline or otherwise help. So you may need the -IPO on the compiler/link to allow the F90 to understand what it can do with the C callee.

0
votes

After a sleep, I am wondering if you should just have a void, and use subroutine on your interface specification?

The other option seems like it would be: INTEGER FUNCTION simd_c_func and then use a REDUCTION in the SIMD on the FORTRAN Side.

But neither of those address the actual compiler message you got. And I see you already used -IPO.

You may try replacing: LINEAR(REF(R,S)) with LINEAR(REF(R), REF(S))