Menu

#1006 Different behavior of gas and gcc/gas64 on a same unsafe code, but is it a bug?

open
nobody
None
compiler
2024-08-15
2024-08-15
No

What bothers me a little to fill out such a bug report is that the code used is not safe when successive returns of a same function provide different arguments to a same procedure:
- Indeed, there is no constraint on the order of evaluation of the arguments passed to a procedure compared to its declaration (from the last parameter to the first or vice versa?).
- Which means that when the value returned by the function varies with each call, a precise behavior is not ensured seen from the procedure body with its received parameters.

In addition, gas and gcc/gas64 have a different behavior on this unsafe code when the function returns by reference (or by pointeur):
- For gcc/gas64, the same value is received for the different arguments,.
It is as if the arguments were actually passed by reference (or pointer) instead of by value.

Test program:

Function ReturnByVal() As Integer
    Static As Integer I
    I+= 1
    Return I
End Function

Function ReturnByRef() Byref As Integer
    Static As Integer I
    I+= 1
    Return I
End Function

Function ReturnByPtr() As Integer Ptr
    Static As Integer I
    I+= 1
    Return @I
End Function

Sub Test(Byval I1 As Integer, Byval I2 As Integer, Byval I3 As Integer)
    Print "   First received parameter  : " & I1
    Print "   Second received parameter : " & I2
    Print "   Third received parameter  : " & I3
End Sub

Print "Arguments are function returns by value)
Test(ReturnByVal(), ReturnByVal(), ReturnByVal())
Print
Print "Arguments are function returns by reference)
Test(ReturnByRef(), ReturnByRef(), ReturnByRef())
Print
Print "Arguments are function returns by pointer)
Test(*ReturnByPtr(), *ReturnByPtr(), *ReturnByPtr())

Sleep

Output with gas(32):

Arguments are function returns by value)
   First received parameter  : 3
   Second received parameter : 2
   Third received parameter  : 1

Arguments are function returns by reference)
   First received parameter  : 3
   Second received parameter : 2
   Third received parameter  : 1

Arguments are function returns by pointer)
   First received parameter  : 3
   Second received parameter : 2
   Third received parameter  : 1

Output with gcc(32/64) or gas64:

Arguments are function returns by value)
   First received parameter  : 3
   Second received parameter : 2
   Third received parameter  : 1

Arguments are function returns by reference)
   First received parameter  : 3
   Second received parameter : 3
   Third received parameter  : 3

Arguments are function returns by pointer)
   First received parameter  : 3
   Second received parameter : 3
   Third received parameter  : 3

Discovered by dodicat.
See also the forum, from this post : https://www.freebasic.net/forum/viewtopic.php?p=304456#p304456

Discussion

  • fxm (freebasic.net)

    The calling convention (stdcall, pascal, cdecl) imposes the order of passing the parameters (in the stack) relative to the order in which they are listed in the declaration, but nothing specifically imposes that the corresponding arguments are evaluated (for passing by value) in the same order.

    Otherwise, I think gas(32) is right, and it is not because the arguments provided are references that they should not still be passed by value as stipulated in the declaration.

     
  • Jeff Marshall

    Jeff Marshall - 2024-08-15

    Related: byref function result chained with additional methods

    The byref result is passed as an instance parameter to the next method call; under the hood, methods have method(this, args) as parameters, so at the very least we should expect arguments to the method to be evaluated before the method call.

    However, as can see in this example, all arguments are evaluated before any method call which is surely incorrect.

    type Q
        x as single
        declare function d( byval arg as single ) as single
        declare function a( byval arg as single ) byref as Q
        declare constructor()
    end type
    
    constructor Q()
    end constructor
    
    function Q.d( byval arg as single ) as single
        print "Q.d(" & arg & ")"
        return arg
    end function
    
    function Q.a( byval arg as single ) byref as Q
        print "Q.a(" & arg & ")"
        return this
    end function
    
    var t = Q()
    t.a( t.d(1) ).a( t.d(2) ).a( t.d(3) )
    
    sleep
    

    Execution order

    Q.d(3)
    Q.d(2)
    Q.d(1)
    Q.a(1)
    Q.a(2)
    Q.a(3)
    

    Some investigation shows that this is related to how fbc tracks the list of nested arguments to calls within other calls within the AST.

    So, while the arguments are evaluated ahead of the procedure call that uses them we should nonetheless like to have arguments further down the chain evaluated after method calls earlier in the chain.

    Expected execution order

    Q.d(1)
    Q.a(1)
    Q.d(2)
    Q.a(2)
    Q.d(3)
    Q.a(3)
    
     

Log in to post a comment.

MongoDB Logo MongoDB