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

Problem on passing contained procedure as an argument

Yaqi_Wang
Beginner
1,694 Views
I got a 'Debug Assertion Failed' error when one module subroutine of my working code tries tocalla procedure passed in as an argument at run-time.

I can not findany codingproblem, so I decide to create a smaller test case, see if I canreproduce the error. I followed the exact logic in myworking code, but I can not get the error in the test.

For your info, I attached my test here:

file1:
------------

program testproc

use test

call testmesh

stop

end program testproc


file2:
-----------

module mesh

integer, parameter :: ikd = 4

contains

subroutine visit_two_mesh(proc1)

INTERFACE

SUBROUTINE proc1(iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history)

!< [in] subroutine to be called a new element start on either mesh

integer, parameter :: ikd = 4

INTEGER(IKD), INTENT(IN) :: iel

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER, INTENT(IN) :: lvl

INTEGER, INTENT(IN) :: lvl_dest

INTEGER , INTENT(IN) :: history(:)

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

END SUBROUTINE proc1

END INTERFACE

integer(IKD) :: iel, iel_dest

integer :: lvl, lvl_dest

integer :: hp, hc, ha

logical :: inc

integer :: history(1)

iel = 1

iel_dest = 1

lvl = 0

lvl_dest = 0

hp = 1

hc = 1

ha = 1

inc = .true.

history = 0

call proc1(iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history)

end subroutine visit_two_mesh

end module


file3:
-------------------

module test

use mesh

contains

subroutine testmesh

call visit_two_mesh(myproc)

contains

subroutine myproc(iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history)

INTEGER(IKD), INTENT(IN) :: iel

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER, INTENT(IN) :: lvl

INTEGER, INTENT(IN) :: lvl_dest

INTEGER , INTENT(IN) :: history(:)

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

print *, iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history

end subroutine myproc

end subroutine testmesh

end module test

==============

The real error in my larger codehappenswhen visit_two_meshis calling proc1. The debugger says 'history' has Undefined address. Starting from 'lvl_dest', all arguments are wrong. But everything is fine in the test. The compilor options:
/nologo /debug:full /Od /fpp /I"C:\\Program Files\\MPICH2\\include" /I"C:\\works\\Phisics\\INSTANT\\lib\\instant2d\\\\..\\..\\src" /I"C:\\works\\Phisics\\INSTANT\\lib\\instant2d\\\\..\\..\\src\\mesh" /Ddim=2 /warn:all /module:"x64\\Debug\\\\" /object:"x64\\Debug\\\\" /Fd"x64\\Debug\\vc90.pdb" /traceback /check:all /libs:static /threads /dbglibs /c

The options used for the test:
/nologo /debug:full /Od /warn:interfaces /module:"x64\\Debug\\\\" /object:"x64\\Debug\\\\" /Fd"x64\\Debug\\vc90.pdb" /traceback /check:bounds /libs:static /threads /dbglibs /c

In my working code, 'visit_two_mesh' accepts more arguments, but I think it should be irrelavent.
When I set a breakpoint at the print line in the third file in the test, I see the disassembly:

subroutine myproc(iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history)

00000001400011F6 push rbp

00000001400011F7 sub rsp,150h

00000001400011FE lea rbp,[rsp+30h]

0000000140001203 mov qword ptr [rbp+110h],rsi

000000014000120A mov qword ptr [rbp+108h],rbx

0000000140001211 mov qword ptr [rbp+130h],rcx

0000000140001218 mov qword ptr [rbp+138h],rdx

000000014000121F mov qword ptr [rbp+140h],r8

0000000140001226 mov qword ptr [rbp+148h],r9

000000014000122D mov eax,40h

0000000140001232 add rax,qword ptr [rbp+170h]

0000000140001239 mov edx,30h

000000014000123E add rdx,qword ptr [rbp+170h]

0000000140001245 mov rdx,qword ptr [rdx]

0000000140001248 add rdx,qword ptr [rax]

000000014000124B dec rdx

000000014000124E mov qword ptr [rbp+58h],rdx

0000000140001252 mov rax,qword ptr [rbp+58h]

0000000140001256 mov qword ptr [rbp+60h],rax

INTEGER(IKD), INTENT(IN) :: iel

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER, INTENT(IN) :: lvl

INTEGER, INTENT(IN) :: lvl_dest

INTEGER , INTENT(IN) :: history(:)

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

print *, iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history



The disassembly in my working code however:

SUBROUTINE proc11(iell,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history)

000000014096A022 push rbp

000000014096A023 sub rsp,0F0h

000000014096A02A lea rbp,[rsp+30h]

000000014096A02F mov qword ptr [rbp+0B0h],rsi

000000014096A036 mov qword ptr [rbp+0A8h],rbx

000000014096A03D mov qword ptr [rbp+0D0h],r10

000000014096A044 mov qword ptr [rbp+0D8h],rcx

000000014096A04B mov qword ptr [rbp+0E0h],rdx

000000014096A052 mov qword ptr [rbp+0E8h],r8

000000014096A059 mov rax,qword ptr [rbp+0D0h]

000000014096A060 mov qword ptr [rbp+70h],rax

000000014096A064 mov eax,40h

000000014096A069 add rax,qword ptr [rbp+118h]

000000014096A070 mov edx,30h

000000014096A075 add rdx,qword ptr [rbp+118h]

000000014096A07C mov rdx,qword ptr [rdx]

000000014096A07F add rdx,qword ptr [rax]

000000014096A082 dec rdx

000000014096A085 mov qword ptr [rbp+78h],rdx

000000014096A089 mov rax,qword ptr [rbp+78h]

000000014096A08D mov qword ptr [rbp+80h],rax

IMPLICIT NONE

INTEGER(IKD), INTENT(IN) :: iell

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER , INTENT(IN) :: lvl

INTEGER , INTENT(IN) :: lvl_dest

INTEGER , INTENT(IN) :: history(:)

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

print *, history


, which is different from the one of the test. Is this normal? Any suggestions for me to remove this run-time error?

Many thanks.

0 Kudos
25 Replies
Steven_L_Intel1
Employee
1,475 Views
When you get the Debug Assertion Failed error, look at the application's console window, which may be hidden behind Visual Studio, to see if there is a more descriptive error. If you can't figure it out, consider contacting Intel Premier Support and supplying the complete application.
0 Kudos
Yaqi_Wang
Beginner
1,475 Views

I noticed the following assembly lines are messed up:

000000014096A03D mov qword ptr [rbp+0D0h],r10

000000014096A044 mov qword ptr [rbp+0D8h],rcx

000000014096A04B mov qword ptr [rbp+0E0h],rdx

000000014096A052 mov qword ptr [rbp+0E8h],r8

rcx, rdx, r8 and r9 are storing the addresses of iel, iel_dest, lvl, lvl_dest, but the subroutine is using r8 and r10. What can be the reason for this?

0 Kudos
Steven_L_Intel1
Employee
1,475 Views
Sorry, this isn't the sort of thing we can diagnose from portions of assembly code. If you can provide us with a test case that fails, we can work with that,
0 Kudos
Yaqi_Wang
Beginner
1,475 Views

I finally reproduce the error. Change the previous file 2 with the following:

----------------------------

module test

use mesh

implicit none

contains

subroutine testmesh

real(8) :: a(10,10)

call visit_two_mesh(myproc, myproc1)

contains

SUBROUTINE proc0()

IMPLICIT NONE

a = 0.0_8

RETURN

END SUBROUTINE proc0

subroutine myproc(iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history)

INTEGER(IKD), INTENT(IN) :: iel

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER, INTENT(IN) :: lvl

INTEGER, INTENT(IN) :: lvl_dest

INTEGER , INTENT(IN) :: history(:)

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

print *, iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history

end subroutine myproc

subroutine myproc1(iel,iel_dest, hp, hc, ha, inc)

INTEGER(IKD), INTENT(IN) :: iel

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

print *, iel,iel_dest,hp, hc, ha, inc

end subroutine myproc1

end subroutine testmesh

end module test

=============
You will see the error with 12.0.

0 Kudos
Yaqi_Wang
Beginner
1,475 Views

Essentially, the allocated array in stack caused the error. If I use /Qsave, then the error is gone. I am not sure if this is considered a compiler bug or not.

0 Kudos
Steven_L_Intel1
Employee
1,475 Views
Thanks - I will look at this.
0 Kudos
Steven_L_Intel1
Employee
1,475 Views
I'm a bit confused here. The new source you provide would seem to replace file 3, not file 2. Then you have the call to visit_two_mesh passing two arguments, but the routine accepts only one.

Would you please ZIP a folder containing the test sources and the solution/project files, or whatever tells me how you build the sources, and attach it to a reply? That way I can be certain of seeing what you are seeing.
0 Kudos
Yaqi_Wang
Beginner
1,475 Views
sorry, Steve. I tried out different ways to fix the problem, the test code is messed up. Let me reattach them here.

file 1:
===

program testproc

use test

call testmesh

stop

end program testproc

file 2:
====

module mesh

integer, parameter :: ikd = 4

contains

subroutine visit_two_mesh(proc1, proc2)

INTERFACE

SUBROUTINE proc1(iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history)

!< [in] subroutine to be called a new element start on either mesh

integer, parameter :: ikd = 4

INTEGER(IKD), INTENT(IN) :: iel

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER, INTENT(IN) :: lvl

INTEGER, INTENT(IN) :: lvl_dest

INTEGER , INTENT(IN) :: history(:)

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

END SUBROUTINE proc1

SUBROUTINE proc2(iel,iel_dest, hp, hc, ha, inc)

!< [in] subroutine to be called a new element start on either mesh

integer, parameter :: ikd = 4

INTEGER(IKD), INTENT(IN) :: iel

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

END SUBROUTINE proc2

END INTERFACE

integer(IKD) :: iel, iel_dest

integer :: lvl, lvl_dest

integer :: hp, hc, ha

logical :: inc

integer :: history(1)

iel = 1

iel_dest = 1

lvl = 0

lvl_dest = 0

hp = 1

hc = 1

ha = 1

inc = .true.

history = 0

call proc2(iel,iel_dest, hp, hc, ha, inc)

call proc1(iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history)

end subroutine visit_two_mesh

end module

file 3:
====

module test

use mesh

implicit none

contains

subroutine testmesh

real(8) :: a(10,10)

!real(8), save :: a(10,10)

call visit_two_mesh(myproc, myproc1)

contains

SUBROUTINE proc0()

IMPLICIT NONE

a = 0.0_8

RETURN

END SUBROUTINE proc0

subroutine myproc(iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history)

INTEGER(IKD), INTENT(IN) :: iel

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER, INTENT(IN) :: lvl

INTEGER, INTENT(IN) :: lvl_dest

INTEGER , INTENT(IN) :: history(:)

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

print *, iel,iel_dest,lvl,lvl_dest, hp, hc, ha, inc,history

end subroutine myproc

subroutine myproc1(iel,iel_dest, hp, hc, ha, inc)

INTEGER(IKD), INTENT(IN) :: iel

INTEGER(IKD), INTENT(IN) :: iel_dest

INTEGER , INTENT(IN) :: hp

INTEGER , INTENT(IN) :: hc

INTEGER , INTENT(IN) :: ha !

LOGICAL , INTENT(IN) :: inc ! true to indicate a new element on the first mesh starts

print *, iel,iel_dest,hp, hc, ha, inc

end subroutine myproc1

end subroutine testmesh

end module test

If I put save attribute on the auto array a, then the error is fixed. But this does not work for my big code. I also tried to allocate a dynamically, it does not work.

Thanks.

0 Kudos
Steven_L_Intel1
Employee
1,475 Views
Ok, I can see a problem with x64. Investigating.
0 Kudos
Steven_L_Intel1
Employee
1,475 Views
Ok, I can see a problem with x64. Investigating.
0 Kudos
Steven_L_Intel1
Employee
1,475 Views
What I can see so far is that when "proc1" is called, arguments hc and afterward are shifted one place so that the called routine picks up garbage for the address of the last argument triggering the access violation. I haven't yet figured out if the error is on the caller or called side.

This is an interesting case - it may take me a while to unravel. Changing the declaration of the array simply changes the layout of memory causing random other data to be used for the address which may or may not result in an error. It does not fail on IA-32.
0 Kudos
Yaqi_Wang
Beginner
1,475 Views
Steve,

When "proc1" is called back, "proc1" need to be able to see the local varialbes declared in its containing subroutine. I guess the error happens whilethecompiler handles this.

In my working code, when I comment out all statements in "proc1" and use save attribute for a, I can see the arguments are passed correctly with debugger (r8 and r9 are used). Once I add one statement accessing the local data in the containing subroutine, arguments are no longer passed correctly (in this cass, r9 and r10 are used in the assembly).I am desparately wanting a fix of this.

I remembered the code works longbefore, I was using Win32 anyway. I may had a bad memory. I will try Win32 and Linux tomorrow see what will happen.

Best.
0 Kudos
Steven_L_Intel1
Employee
1,475 Views
Yaqi,

I found that there was no problem accessing the host-associated variable. The problem is solely in how the actual arguments are passed to the subroutine, where the first three arguments are passed correctly but after that, positions are offset by one so that the last one picks up garbage. I'm pretty sure that this is related to the way the argument list is "split" on x64 with the first few arguments passed in registers and the rest on the stack. I have escalated this as issue DPD200168169.

I verified that it also fails with the 11.1 compiler on Windows - I haven't tried Linux. It works fine on IA-32 - it's only Intel 64 where the problem appears.
0 Kudos
Yaqi_Wang
Beginner
1,475 Views
Thanks Steve.

I am assigned to work on something else. Please do let me know when you have a fix.

-Yaqi
0 Kudos
Yaqi_Wang
Beginner
1,475 Views
Steve,

Has this issue been fixed in update 5 of Composer XE 2011?

Best,
Yaqi
0 Kudos
Steven_L_Intel1
Employee
1,475 Views
No, it has not.
0 Kudos
jimdempseyatthecove
Honored Contributor III
1,475 Views
There was a related forumreport of passing a procedure (subroutine/function) contains subroutine/function to an "external" routine (via call/dummy argument)and where the Host associated (stack)variables were not (properly) accessible (IOW compiler didn't complain but references pointed to incorrect address). I think this is an issue were any "host" declared contains subroutine/function may only be called (directly) from the "host" declaring said contains routine. Note the "SAVE" variables are accessible because they are globally named. Other than lack of warning message this might not be an error. In the IVF documentation:

Description

Internal procedures are the same as external procedures, except for the following:

  • Only the host program unit can use an internal procedure.

  • An internal procedure has access to host entities by host association; that is, names declared in the host program unit are useable within the internal procedure.

  • In Fortran 95/90, the name of an internal procedure must not be passed as an argument to another procedure. However, Intel Fortran allows an internal procedure name to be passed as an actual argument to another procedure. (J.D. I assume this means the procedure address can be saved/restored but not called)

  • An internal procedure must not contain an ENTRY statement.

    Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
1,475 Views
Jim,

Your interpretation is not correct. Intel Fortran supports passing an internal proxedure as an actual argument and calling the procedure via the argument. For some background on how we do that, see my Doctor Fortran post Think, Thank, Thunk.

I will note that this feature is now standard in Fortran 2008 - we were just ahead of the curve this time...
0 Kudos
jimdempseyatthecove
Honored Contributor III
1,475 Views
Neet - so you cook the address of the internal subroutine/function to point to a section of code created on the stack which can fetch, as an immediate value, the host context pointer (likely the savedebp or rbp of the host). This would place a caviate on the programmer such that the context of the host proceedure must remain for the duration of time of all subsequent calls to the contained routine. Meaning a host cannot "post" the thunk for later use, exit the host context (RETURN), and some time later something else uses the savedthunk for a call. A second caviate is: should an O/S mark the stack segment to Execute Disable then the thunk techniquewould not work.

This is loosly reminicient of a C++0x lambda function.

Can you clarify in the IVF documentation the requirement of the host context to persist through the duration of any subsequent use of a contains procedure. Also clarify when (if) a contains procedure recursively calls the host (new context or same context).

Jim Dempsey
0 Kudos
mecej4
Honored Contributor III
1,332 Views
Comment: one would need to read Steve's "Think, Thank, Thunk" before reading Jim's response.

It seems to me that it will be tough to document the requirements and limitations as described in Jim's last paragraph, since Fortran is supposed to be agnostic to concepts such as registers, thunks and stacks, and so the documentation will have to avoid using these concepts.
0 Kudos
Reply