Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29529 Discussions

porting ifort -> ifx: acess violation when returning a struct to c

james-thunes
Beginner
1,962 Views

Hello all,

 

I have a c application that runs on both windows and linux. The application includes a number of fortran functions which are called from the c code. On windows (windows 11 24H2), we have been using ifort to compile the fortran code and msvc to compile the c code.

 

The fortran functions return a struct that contain error information: a return code and a message.

 

When compiling the same code with ifx instead of ifort, my code fails with the following error:
forrtl: severe (157): Program Exception - access violation

I've been able to narrow it down to a minimal reproduceable error. If I define as a function with a return value (get_struct_byval), the memory address of the struct is different in fortran than it is in the calling c function. However, if I defined a subroutine which returns the struct as an intent(out) argument (get_struct_byref), the memory addresses are the same in both the c and fortran sides. See the attached files.

 

I can see that the return is different for (get_struct_byval) when comparing the asm generated by ifort and ifx. ifort is correctly setting the address of the struct in RAX (which appears to be to be the expected behavior for the windows x64 ABI) while ifx is not.

 

Could somebody who's more familiar with the language and ifx confirm whether I'm doing anything incorrect in the attached code? Perhaps there's a compiler flag I'm missing? I'm not seeing anything obviously wrong about the code.

 

The compiler versions are:

msvc: Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35723 for x64
ifort: Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.9.0 Build 20230302_000000
ifx: Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2025.3.2 Build 20260112

all code is compiled with optimization O3 (although the same failure is seen at other optimization levels). Fortran standard 03 is used (again, same error is seen with newer standard versions).

 

 

0 Kudos
15 Replies
james-thunes
Beginner
1,868 Views

The windows ABI is described on this page x64 calling conventions. The relevant section:

User-defined types can be returned by value from global functions and static member functions. To return a user-defined type by value in RAX, it must have a length of 1, 2, 4, 8, 16, 32, or 64 bits. ...[snip]... Otherwise, the caller must allocate memory for the return value and pass a pointer to it as the first argument. The remaining arguments are then shifted one argument to the right. The same pointer must be returned by the callee in RAX.

 

Specifically, in ifort, this is the return from the function:

        mov       BYTE PTR [19+r14], 0                          ;39.9
        mov       rax, r14                                      ;40.3
        lea       rsp, QWORD PTR [112+rbp]                      ;40.3
        pop       r14                                           ;40.3
        pop       r13                                           ;40.3
        pop       rsi                                           ;40.3
        pop       rbx                                           ;40.3
        pop       rbp                                           ;40.3
        ret                                                     ;40.3

 note that rax is set to r14 before the return (line 2).

 

While in ifx:

	movups	%xmm0, 4(%rsi)
	.seh_startepilogue
	addq	$136, %rsp
	popq	%rbx
	popq	%rdi
	popq	%rsi
	popq	%r14
	.seh_endepilogue
	retq

 note that there's no equivalent mov call. 

0 Kudos
james-thunes
Beginner
1,926 Views

apologies, the memory location are a red herring. However the difference in the generated assembly is still relevant I think. The resultant code does return the expected data and run without error when compiled with ifort while it crashes with ifx.

0 Kudos
FortranFan
Honored Contributor III
1,853 Views

@Igor_V_Intel , this looks like a bug in IFX.  May you please quickly review this with Intel Fortran team?

A colleague pointed me to this thread for they are seeing an issue in their code for which they haven't reduced to reproducer but the characteristics are similar to this case by @James_Thunes about a function result that is a derived type with bind(C) attribute.

0 Kudos
james-thunes
Beginner
1,852 Views

Glad to hear I'm not the only one @FortranFan. My default reaction to these things is always that I'm making a mistake, but I'm tentatively leaning towards a ifx bug as well. I'd be very happy to hear that it's on my end though.

 

I do have a workaround (pass the object as an intent(out) argument) but that does require modification to the code. A solution that doesn't require code changes would be preferable.

0 Kudos
FortranFan
Honored Contributor III
1,822 Views

@Igor_V_Intel ,

For whatever it's worth, the behavior using gfortran and IFORT is as expected.

Intel Fortran team may also consider an added reproducer in this review:

  • C main, say file c.c
#include <stdint.h>
#include <stdio.h>

enum { N = 256 };

typedef struct _foo_t {
   int32_t i;
   char s[N];
} foo_t;

foo_t f();

void main() {
   foo_t x;
   x = f();
   printf("C main: x.i = %d; x.s = %s\n", x.i, x.s);
   return;
}
  • Fortran module, say m.f
module m
   use, intrinsic :: iso_c_binding, only : c_int32_t, c_char, c_f_pointer, c_loc, c_null_char
   enum, bind(C)
      enumerator :: N = 256
   end enum
   type, bind(C) :: foo_t
      integer(c_int32_t) :: i
      character(kind=c_char, len=1) :: s(N)
   end type
contains
   function f() result(r) bind(C, name="f")
      ! Function result
      type(foo_t), target :: r
      r%i = 42
      block
         character(kind=c_char, len=N), pointer :: s
         call c_f_pointer( cptr=c_loc(r%s), fptr=s )
         s = repeat( c_char_" ", ncopies=N )
         s = c_char_"Hello World!" // c_null_char
         s => null()
      end block
      return
   end function f
end module
  • gfortran response with GCC as the companion C processor
C:\Temp>c:\gcc\bin\gcc -c c.c

C:\Temp>c:\gcc\bin\gfortran -c -ffree-form m.f

C:\Temp>c:\gcc\bin\gfortran c.o m.o -o c.exe

C:\Temp>c.exe
C main: x.i = 42; x.s = Hello World!

C:\Temp>
  • IFX response with Microsoft C/C++ as the companion C processor
C:\Temp>cl /c /W3 /EHsc c.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.50.35721 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

c.c

C:\Temp>ifx /c /free /standard-semantics m.f
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2025.3.2 Build 20260112
Copyright (C) 1985-2026 Intel Corporation. All rights reserved.


C:\Temp>link c.obj m.obj /subsystem:console /out:c.exe
Microsoft (R) Incremental Linker Version 14.50.35721.0
Copyright (C) Microsoft Corporation.  All rights reserved.


C:\Temp>c.exe
C main: x.i = 1819043144; x.s = o World!

C:\Temp>

 Intel team may try this example with the value of enum set to 3.  Please look into struct data alignment and bind(C) considerations.

0 Kudos
Igor_V_Intel
Moderator
1,717 Views

Thank you.  I am not sure that this code is standard conforming, e.g. 

character(kind=c_char, len=N), pointer :: s

The rule for c_f_pointer is that the Fortran target must be an interoperable data entity. I guess that in this case it is not an interoperable  unless N=1. However, I believe there is a bug in the ifx after making this example conform to the standard.

0 Kudos
witwald
New Contributor II
1,699 Views

I think that you are correct that "only character variables with a length of one (1) are interoperable". That makes sense from the C perspective, as the character array (pointer to an array of characters) needs a spot for the null character at the end. Hence it has a length of 1 when it is created. I'm not very clued up on all of this, but am finding the discussion interesting as it relates to calling/using C libraries from Fortran.

0 Kudos
FortranFan
Honored Contributor III
1,666 Views

@Igor_V_Intel wrote:

Thank you.  I am not sure that this code is standard conforming, e.g. 

character(kind=c_char, len=N), pointer :: s

The rule for c_f_pointer is that the Fortran target must be an interoperable data entity. I guess that in this case it is not an interoperable  unless N=1. However, I believe there is a bug in the ifx after making this example conform to the standard.


@Igor_V_Intel ,

For the sake of the issue with IFX on hand, please ignore the above distraction.  Please consider the following variant with IFX review.

module m
   use, intrinsic :: iso_c_binding, only : c_int32_t, c_char, c_f_pointer, c_loc, c_null_char
   enum, bind(C)
      enumerator :: N = 256
   end enum
   type, bind(C) :: foo_t
      integer(c_int32_t) :: i
      character(kind=c_char, len=1) :: s(N)
   end type
contains
   function f() result(r) bind(C, name="f")
      ! Function result
      type(foo_t), target :: r
      ! Local entities
      integer :: i
      character(kind=c_char, len=*), parameter :: s = c_char_"Hello World!" // c_null_char
      r%i = 42
      r%s = repeat( c_char_" ", ncopies=N )
      do i = 1, len(s)
         r%s(i) = s(i:i)
      end do
      return
   end function f
end module
0 Kudos
james-thunes
Beginner
1,519 Views

Yes, the above is a more concise representation of the problem I believe. Thanks

 

0 Kudos
Igor_V_Intel
Moderator
1,502 Views

Thank you, @FortranFan . I have escalated it to be fixed (CMPLRLLVM-74139).

0 Kudos
james-thunes
Beginner
1,478 Views

thanks @Igor_V_Intel. Is the above bug (CMPLRLLVM-74139) publicly available so I can track its progress?

0 Kudos
Igor_V_Intel
Moderator
1,463 Views

Hi @james-thunes . Unfortunately not. It is more for your reference if you contact Intel support to ask for an update. I will notify you and the community here once the bug is fixed. 

0 Kudos
james-thunes
Beginner
1,461 Views

Not a problem. That'll be sufficient for my purposes.

0 Kudos
Steve_Lionel
Honored Contributor III
1,612 Views

The language has a carve-out for passing character values with length greater than 1 to character array dummy arguments.

"Fortran’s rules of sequence association (15.5.2.12) permit a character scalar actual argument to correspond to
a dummy argument array. This makes it possible to argument associate a Fortran character string with a C
string."

james-thunes
Beginner
1,519 Views

Thanks @Steve_Lionel good to know. Dealing with character arrays in c is always a minefield. Glad that there's some guidance on how to do so in the fortran standard.

 

This is, however, perhaps a little orthogonal to my actual issue. The use of a string in the object I'm returning from the function was in retrospect a bad choice. It was done since it was representative of the simplest use-case that was failing in my ported code, but the same result can be seen with other objects as well. Of note is that the opposite case, passing the same data from c to fortran, does not exhibit the same behavior. 

0 Kudos
Reply