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

Error in passing variable between subroutines in Fortran

RubensMigliore
Beginner
329 Views

I have been developing a VB .NET program in VisualStudio environment for some years, which manipulates an Intel Fortran IFX DLL. Until version IFX 2023.2, all programs presented correct results and were verified in debug mode both in VB and in Fortran. However, I recently updated the IFX compiler to version 2024.0 and now the results are being incorrectly presented. After some tests, I observed that the passing of individual variable results is wrong, which does not occur when the results are vectors. The following source codes illustrate the problem.

[VB Code]
Public Class Form1

'Public Declare Auto Sub ProcessArray Lib "TesteArrayFortran.dll" (
' ByRef n As Integer,
' <MarshalAs(UnmanagedType.LPArray)> arr As Double(),
' <MarshalAs(UnmanagedType.LPArray)> sai As Double(),
' <MarshalAs(UnmanagedType.LPArray)> sum1 As Double,
' <MarshalAs(UnmanagedType.LPArray)> sum2 As Double())

Public Declare Auto Sub ProcessArray Lib "TesteArrayFortran.dll" (
ByRef n As Integer,
arr As Double(), sai As Double(),
sum1 As Double, sum2 As Double())

Private Sub Button1_Click(
sender As Object, e As EventArgs
) Handles Button1.Click

Dim sum1 As Double
Dim sum2(0) As Double

Dim arr() As Double = {1.0, 2.0, 3.0, 4.0, 5.0}
Dim n As Integer = arr.Length
Dim sai(4) As Double

ProcessArray(n, arr, sai, sum1, sum2)

Console.WriteLine($"n = {n}")
Console.WriteLine($"sai(0) = {sai(0)}")
Console.WriteLine($"sai(1) = {sai(1)}")
Console.WriteLine($"sai(2) = {sai(3)}")
Console.WriteLine($"sai(3) = {sai(3)}")
Console.WriteLine($"sai(4) = {sai(4)}")
Console.WriteLine($"sum1 = {sum1}")
Console.WriteLine($"sum2 = {sum2(0)}")
End Sub
End Class


[Fortran Code]
SUBROUTINE ProcessArray(n, entra, sai, sum1, sum2)

!DEC$ ATTRIBUTES DLLEXPORT, ALIAS:'ProcessArray' :: ProcessArray
!DEC$ ATTRIBUTES REFERENCE :: n

IMPLICIT NONE
EXTERNAL :: Check

INTEGER, INTENT(IN) :: n
REAL*8, INTENT(IN) :: entra(n)
REAL*8, INTENT(OUT) :: sai(n), sum1, sum2(1)

INTEGER :: i
DO i = 1, n
sai(i) = entra(i) * entra(i)
END DO

CALL Check(n, entra, sum1, sum2)

END SUBROUTINE ProcessArray


SUBROUTINE Check(n, entra, sum1, sum2)

IMPLICIT NONE

INTEGER,INTENT(IN) :: n
REAL*8,INTENT(IN) :: entra(n)
REAL*8,INTENT(OUT) :: sum1, sum2(1)

INTEGER :: i

sum1 = 0
DO i = 1, n
sum1 = sum1 + entra(i)
END DO

sum2(1) = sum1

RETURN
ENDSUBROUTINE Check

I believe the error is related to some configuration of IFX 2024.0 which did not exist or had a different behavior until IFX 2023.2. Is it possible to analyze the issue and suggest some configuration that corrects the problem?

 

Some additional comments:

1) Is it necessary to use the command <MarshalAs(UnmanagedType.LPArray)>? In my tests, there is no difference at all between using it or not when IFX is configured with the STDCALL calling convention (/iface:stdcall).

2) Partial results are only correct when IFX is configured with a) Initialize Variables to Signaling NaN = Yes (/Qinit:snan); b) Initialize Variables to Zero = Yes (/Qinit:zero); c) Initialize Arrays as well as Scalars = Yes (/Qinit:arrays).

3) In my example, the elements of the "sai" array coincide with the elements of the "arr" array. The sum of these elements is 1+2+3+4+5=15, which should be correctly reported by the first element of the "sum2" array (sum2(0) = 15). However, the variable "sum1" is calculated in the same way but results incorrectly in sum1 = 0.

Labels (1)
0 Kudos
1 Reply
RubensMigliore
Beginner
302 Views

I would like to inform you that: a) my code is 64-bit; b) it is not necessary to use the attribute <MarshalAs(UnmanagedType.LPArray)>; c) after days of researching and combining instructions and settings in VB .NET and Fortran IFX, I managed to find the solution to my problem. Basically, the variable I want to pass between Fortran subroutines ('sum', in this case) must have the ByRef attribute on both the Fortran and VB sides to be presented to the user. The configuration of the IFX compiler should be: Calling Convention = STDCALL, REFERENCE (/iface:stdref).

In case it is helpful for anyone else, I am sharing the code snippets of my new test, leaving out only the important additional parts, as well as the output of the results.

***** VB .NET Code
Imports System.Diagnostics.Debug
Imports System.Runtime.InteropServices
Imports System.Environment

Public Class Form1

<DllImport("TesteArrayFortran.dll")>
Public Shared Sub ProcessArray(
ByRef n As Integer,
ByRef soma As Double,
arr As Double())
End Sub

Private Sub Button1_Click(
sender As Object, e As EventArgs
) Handles Button1.Click

Dim soma As Double
Dim arr() As Double = {1.1, 2.1, 3.1, 4.1, 5.1}

Dim n As Integer = arr.Length

ProcessArray(n, soma, arr)

WriteLine($"{NewLine}")
WriteLine($"***** {Now}")
For i = 0 To 4
WriteLine($"arr({i}) = {arr(i):#0.0}")
Next i
WriteLine($"soma = {soma:#0.0}")

End Sub
End Class

***** Fortran Code
SUBROUTINE ProcessArray(n, sum, entra)
!DEC$ ATTRIBUTES DLLEXPORT, ALIAS:'ProcessArray' :: ProcessArray
!DEC$ ATTRIBUTES REFERENCE :: n, sum
IMPLICIT NONE
INTEGER, INTENT(IN) :: n
REAL*8, INTENT(INOUT) :: sum
REAL*8, INTENT(IN) :: entra(n)
INTERFACE
SUBROUTINE Check(n, sum, entra)
IMPORT
INTEGER, INTENT(IN) :: n
REAL*8, INTENT(INOUT) :: sum
REAL*8, INTENT(IN) :: entra(n)
END SUBROUTINE Check
END INTERFACE
CALL Check(n, sum, entra)
END SUBROUTINE ProcessArray

SUBROUTINE Check(n, sum, entra)
IMPLICIT NONE
INTEGER, INTENT(IN) :: n
REAL*8, INTENT(INOUT) :: sum
REAL*8, INTENT(IN) :: entra(n)
INTEGER :: i
sum = 0
DO i = 1, n
sum = sum + entra(i)
END DO
END SUBROUTINE Check


***** Output Results
***** 06/03/2024 18:07:29
arr(0) = 1,1
arr(1) = 2,1
arr(2) = 3,1
arr(3) = 4,1
arr(4) = 5,1
soma = 15,5

Reply