Shop OBEX P1 Docs P2 Docs Learn Events
Lifetime of Parameters and Local Variables — Parallax Forums

Lifetime of Parameters and Local Variables

pjvpjv Posts: 1,903
edited 2012-11-09 10:56 in Propeller 1
Hello All;

I've been buried in PASM for much too long, and am now doing some things in Spin, but I do not know much about higher level language concepts, so I'm experimenting.

A question that I am looking to have answered, is: what is the lifetime of the values in parameters passed to a method as well as the lifetime of local variables in the method ?

More precisely, when I invoke MethodA which calls MethodB, are MethodA's parameters and locals valid in MethodB, and are MethodB's values still valid in MethodA after exiting MethodB.

And what about their values after exiting both methods ?

So far most of my work has been software employing a single cog. Does mutiple cogs affect my observations ?

Insight into this would be much appreciated.

Cheers,

Peter (pjv)

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2012-11-08 11:59
    The parameter storage and local variables are created when the method is entered (called) and they go away when the method exits (returns) ... simple. The call to the method pushes the parameters on the stack (including the return address and result variable (RESULT), then increments the stack pointer to allocate space for the local variables. When the method returns, the only thing left is the result variable which is discarded if not needed where the method is called.

    If method A calls method B, method A's parameters and local variables are still on the stack, but their names are known only within method A, so you can't really access them from method B.

    Using multiple cogs doesn't change this. Each cog has its own stack and any methods executing refer to the copy of their variables on their (private) stack.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-11-08 12:02
    A local variable actually is the same as a parameter. Both are created on the stack when the function is called. The lifetime is from function start until function end. But subfunctions don't know anything about the variables of the calling function. If you try to use a local variable or a parameter from MethodA in MethodB you'll get an compile error.

    As they live on the stack there is in theory a possibility to access the variables, but in SPIN there is no stack-pointer available, so there is no easy way to do this. And to make this very clear: It would also be a very bad idea to do this! One function should not assume that it's called from ONE other function! In case both functions need the same variable you have several options:
    Make the variable global
    Pass a pointer to the function
    Use getter and setter functions

    When the function returns parameters and local variables are not cleaned up. So the values are still available on the stack until there is the next function call. Writing about that I have an interesting idea. You can for example call
    MethodB(par1, par2, par3)
    And Method B returns the address of par1. Then the calling function can access all parameters which allows to return more than one value!
    But you need to be aware that these have to be rescued before calling the next function!

    Multiple COGs work with multiple stacks. So, as long as you do not exceed the limit of the stacks there is no affect. The speciality of the first COG (which starts your code) is that this COG has the whole unused HUB-RAM as stack-space. So, with small amount of beginners code you'll never run into stack-problems.
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-11-08 12:09
    PS: I lied! The parameter is not exactly the same as a local variable! Parameters are initialized with the values that you pass whereas local variables are uninitialized and contain any remains that might have been on the stack before. So, don't forget to initialize the local variables in the function!
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-11-08 14:05
    Here's a simple program that illustrates what can and can't be done:
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
    OBJ
    
      pst   :       "Parallax Serial Terminal"
    
    PUB  start | i
    
      pst.start(9600)
      i := 12345
      print_i
      pst.str(string("j = "))
      pst.dec(long[@i + 16])
      pst.char(13)
      repeat
    
    PUB print_i | j
    
      pst.str(string("i = "))
      pst.dec(long[@j - 16])
      pst.char(13)
      j := 67890
    

    print_i is able to output the value of start local variable i because it's still on the stack -- behind the return address and the (unused) result variable. But by the time start attempts to retrieve the value of print_i's local variable j, it's already been clobbered by intervening stack manipulations for expression evaluation.

    -Phil
  • pjvpjv Posts: 1,903
    edited 2012-11-08 15:27
    Mike, MagIO and Phil;

    Thank you so much for your prompt responses.

    So, if I distill down what I interpret from these answers (and my experimentation) is that the values on the stack will not be affected by activity within other cogs, and the value will remain valid while in each respective method. And still valid after exiting each method until some further method is called with some parameters.

    In my particular case, speed is everything, and transferring parameters into global variables consumes 36 microseconds, so I would prefer to just pass the address of the parameter list an additional method and saving the transfer time. My methods are very skinny, and I believe I would not mess up the stack between consecutive operations. At least that is what my experimentation indicates.

    Thank you again for your comments.

    Cheers,

    Peter (pjv)
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-11-08 16:30
    pjv wrote:
    ... And still valid after exiting each method until some further method is called with some parameters.
    No. The stack is also used for expression evaluation. In my demo program above, even though no further methods were called after the call to print_i, it's value of j got clobbered due to the interpreter's other, intervening stack manipulations.

    -Phil
  • pjvpjv Posts: 1,903
    edited 2012-11-08 20:09
    Phil... yes I see that now. Thanks for making that clear.

    Cheers

    Peter (pjv)
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-11-09 01:16
    Sorry Phil, but that's wrong!

    You can't expect that the pst function-calls don't clobber the stack. So the right test would look like this:
    CON
    
      _clkmode      = xtal1 + pll16x
      _xinfreq      = 5_000_000
    
    OBJ
    
      pst   :       "Parallax Serial Terminal"
    
    PUB  start | i
    
      pst.start(9600)
      i := 12345
      print_i
      i := long[@i + 16]
    
      pst.str(string("j = "))
      pst.dec(i)
      pst.char(13)
      repeat
    
    PUB print_i | j
    
      pst.str(string("i = "))
      pst.dec(long[@j - 16])
      pst.char(13)
      j := 67890
    

    and this prooves that only function calls destroy the stack and not expressions! Makes sense, because I guess that there is a register inside the COG which is used to calculate expressions. And it does not make sense to write back intermediate results. And end results propably don't need to be stored at all. And if so, they will be stored with an assignment directly in the right location and not on the stack.

    But thank you phil, now I took the time to also try out the multi value passback and it works:
    PUB  start | i2,i3,i
    
      pst.start(9600)
      i := 12345
      print_i
      i := long[@i + 16]
      i2 := long[@i + 20]
      i3 := long[@i + 24]
    
      pst.str(string("j = "))
      pst.dec(i)
      pst.str(string(" k = "))
      pst.dec(i2)
      pst.str(string(" l = "))
      pst.dec(i3)
      pst.char(13)
      repeat
    
    PUB print_i | j,k,l
    
      pst.str(string("i = "))
      pst.dec(long[@j - 16])
      pst.char(13)
      j := 67890
      k:= 11111
      l:= 22222
    
    Not sure whether this is usefull, but it works!
  • kuronekokuroneko Posts: 3,623
    edited 2012-11-09 02:07
    MagIO2 wrote: »
    ... and this prooves that only function calls destroy the stack and not expressions!
    Would a case construct count as function call or expression then? I have one ready which clobbers the stack quite sufficiently without calling any method.
    case result
        0: case frqb
             6: result := ?vscl
             POSX: result := ||cnt
    
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2012-11-09 08:39
    MagIO2 wrote:
    So the right test would look like this:
    Oops! Yes, of course you're right; in my haste to cobble an example together, I neglected to consider the pst call.

    -Phil
  • MagIO2MagIO2 Posts: 2,243
    edited 2012-11-09 10:56
    Ok, Phil, I have to apologize!

    Out of curiosity I wrote some small tests! It looks like the SPIN language makes heavy usage of the stack! Repeat loops, case statements and EXPRESSIONs use the stack! So, why does the example (post #9) work? It's because the calculation only needs 2 longs regardless of how complex the expression is (when no function call is involved!). This means that the calculation of [@i+16] only overwrites the return-address and the return-value of the previous function call. All parameters and local variables are untouched.

    Learning never ends! :o)
Sign In or Register to comment.