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

Stack access

Ken_Woolridge
Beginner
3,560 Views

I am trying to access the arguments passed by Intel's F90 compiler to an assembly routine.  The first four, passed in registers, I have no trouble accessing.  However, I cannot access the remaining four, which should be on the stack.  POP does not seem to work.  Here is the test code:

PROGRAM FF
   IMPLICIT NONE
   INTEGER(4),EXTERNAL :: AAA
   INTEGER(4) :: W1, W2, W3, W4, W5, W6, W7, W8, R

   W1 = 11
   W2 = 12
   W3 = 13
   W4 = 14
   W5 = 15
   W6 = 16
   W7 = 17
   W8 = 18
   R1 = 0
   R1 = AAA(W1, W2, W3, W4, W5, W6, W7, W8)
   PRINT *, R1
END PROGRAM FF

 

        TITLE      AAA
       .CODE
       PUBLIC AAA

AAA:
      MOV RAX,0
      POP RAX
      RET
      END

 

What am I missing?

0 Kudos
8 Replies
jimdempseyatthecove
Honored Contributor III
3,147 Views

Do not POP! You will crash upon return

Use rbp

Example:

RECURSIVE SUBROUTINE VECNRM_r_r (VA, VC)
00007FF6E8795D90  push        rbp  
00007FF6E8795D91  push        r14  
00007FF6E8795D93  push        r15  
00007FF6E8795D95  sub         rsp,80h  
00007FF6E8795D9C  lea         rbp,[rsp+30h]  
00007FF6E8795DA1  mov         r15,rdx  
00007FF6E8795DA4  vmovups     xmmword ptr [rbp+40h],xmm13  
00007FF6E8795DA9  mov         r14,rcx  

...

      RETURN
END SUBROUTINE VECNRM_r_r
00007FF6E8795DF6  lea         rsp,[rbp+50h]  
00007FF6E8795DFA  pop         r15  
00007FF6E8795DFC  pop         r14  
00007FF6E8795DFE  pop         rbp  
00007FF6E8795DFF  ret  

Your subroutine will have different arguments than that above.

*** caution, if you modify rsp, unmodify it upon return

Suggestion,

Make a Fortran subroutine with the same arguments, call it with break point at entry.

Open a Dissassembly window when at break point, then model your assembly code after the Fortran entry and exit of procedure.

Jim Dempsey

 

 

Ken_Woolridge
Beginner
1,764 Views

Thank you for this prompt response.  However, I am not sure of what is going on in your example.  All I require is the way to access the 5th through the last arguments which, according to the documentation, are on the stack.  I believe RSP points to the stack.  I have changed my assembly routine to:

 

        TITLE      AAA
       .CODE
       PUBLIC AAA

AAA:
      MOV RAX,0
      MOV RAX,{RSP]
      RET
      END

 

and I get the value 1481445554 instead of the expected value 15.  Obviously, I am doing something wrong.  My question is still:

 

How do I access the arguments on the stack?

0 Kudos
mecej4O
Novice
1,418 Views

You must study and understand the calling conventions of the Fortran compiler that you are using. The assembler code of the function must conform to those conventions. In addition, certain registers need to be saved on entry to the function routine, and must be restored before returning to the instruction following the function invocation point. 

Note that, as soon as the function code is entered, the stack pointer has no longer the same value as before the CALL to the function. Thus, the arguments are no longer in the same memory locations relative to RSP in comparison to where they were before the CALL. Note that the RBP register is dedicated to accessing arguments on the stack. Before a CALL, some register values are also pushed to the stack. Arguments can be passed by value or by address.

You must understand all these conventions in order to write an assembler routine and get it to work with calls from Fortran code.

0 Kudos
GVautier
New Contributor III
1,649 Views

How is declared your AAA function on fortran side? The value may indicate a pointer to the value rather than the value itself.

0 Kudos
Steve_Lionel
Honored Contributor III
1,589 Views

Yes, the arguments on the stack are by reference (unless modified by the interface.)

0 Kudos
mecej4O
Novice
1,102 Views

When your assembly routine AAA is entered, the stack top has the return address. Therefore, your POP RAX instruction puts that address into RAX, in contrast to your wish to move one of the other routine arguments into RAX. Furthermore, your zeroing RAX in the previous instruction makes no sense, given that the next instruction is going to overwrite the zero value in RAX. Furthermore, once you POP the return address into a register, the subsequent RET instruction will not work at all, since the return address is no longer on the stack in its expected position. Returning to a garbage address on the stack is almost certain to cause your program to crash.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,069 Views
program Console23
    call foo(1, 2, 3, 4, 5, 6)
end program Console23

subroutine foo(one, two, three, four, five, six)
    integer :: one, two, three, four, five, six
    integer :: test
    test = one
    test = two
    test = three
    test = four
    test = five
    test = six
    print *,"break here"
end subroutine foo
subroutine foo(one, two, three, four, five, six)
00007FF652441002  sub         esp,0A0h  ! reserve stack for foo
00007FF652441008  lea         rbp,[rsp+80h]  ! Set base pointer
00007FF652441010  mov         rax,r9  
00007FF652441013  mov         r9,qword ptr [rbp+58h]  
00007FF652441017  mov         r9,qword ptr [rbp+50h]  
00007FF65244101B  mov         qword ptr [rbp-18h],rcx  ! Store addrs of one arg
00007FF65244101F  mov         r10,qword ptr [rbp-18h]  ! r10 [rbp-18h]  = addr one
00007FF652441023  mov         qword ptr [rbp-20h],rdx ! store addre of two 
00007FF652441027  mov         r9,qword ptr [rbp-18h]  ! r9 [rbp-18h] = addre two
00007FF65244102B  mov         qword ptr [rbp-28h],r8  ! store addr three
00007FF65244102F  mov         r8,qword ptr [rbp-28h]  ! r8 [rbp-28h] = addr three
00007FF652441033  mov         qword ptr [rbp-30h],rax  ! store addr four
00007FF652441037  mov         rdx,qword ptr [rbp-30h]  ! rdx [rbp-30h] = addr four
00007FF65244103B  mov         rcx,qword ptr [rbp+50h] ! rcx [rbp+50h] = addr five
00007FF65244103F  mov         rax,qword ptr [rbp+58h] ! rax [rbp+58h] = addr six
    integer :: one, two, three, four, five, six
    integer :: test
    test = one
00007FF652441043  mov         r10d,dword ptr [r10]  
00007FF652441046  mov         dword ptr [TEST],r10d  
    test = two
00007FF65244104A  mov         r9d,dword ptr [r9]  
00007FF65244104D  mov         dword ptr [TEST],r9d  
    test = three
00007FF652441051  mov         r8d,dword ptr [r8]  
00007FF652441054  mov         dword ptr [TEST],r8d  
    test = four
00007FF652441058  mov         edx,dword ptr [rdx]  
00007FF65244105A  mov         dword ptr [TEST],edx  
    test = five
00007FF65244105D  mov         ecx,dword ptr [rcx]  
00007FF65244105F  mov         dword ptr [TEST],ecx  
    test = six
00007FF652441062  mov         eax,dword ptr [rax]  
00007FF652441064  mov         dword ptr [TEST],eax  
    print *,"break here"

Your disassembly code may not set up its stack frame the way Fortran Debug build does.

The asm listing for release foo

	.def	FOO;
	.scl	2;
	.type	32;
	.endef
	.globl	FOO
	.p2align	4, 0x90
FOO:
.Lfunc_begin1:
	.cv_func_id 1
	.cv_loc	1 1 19 0
.seh_proc FOO
	subq	$56, %rsp
	.seh_stackalloc 56
	.seh_endprologue
.Ltmp3:
	.cv_loc	1 1 21 0
	cmpl	$0, (%rcx) ; one
	je	.LBB1_7
.Ltmp4:
	.cv_loc	1 1 22 0
	cmpl	$0, (%rdx) ; two
	je	.LBB1_7
.Ltmp5:
	.cv_loc	1 1 23 0
	cmpl	$0, (%r8) ; three
	je	.LBB1_7
.Ltmp6:
	.cv_loc	1 1 24 0
	cmpl	$0, (%r9) ; four
	je	.LBB1_7
.Ltmp7:
	.cv_loc	1 1 25 0
	movq	96(%rsp), %rax ; five
	cmpl	$0, (%rax)
	je	.LBB1_7
.Ltmp8:
	.cv_loc	1 1 26 0
	movq	104(%rsp), %rax ; six
	cmpl	$0, (%rax)
	je	.LBB1_7
.Ltmp9:
	.cv_loc	1 1 27 0
	addq	$56, %rsp
	retq

Note, the Release build of this FOO did not use rbp.

 

Jim

0 Kudos
jimdempseyatthecove
Honored Contributor III
808 Views

A hack that I've successfully used in the past is to write a shell function with same arguments in Fortran, compile it with the assembly listing, then strip out all the unnecessary assembly statements leaving an assembly shell function to which you insert your code.

 

Jim Dempsey

Reply