Search code examples
c#clr

Where is return value from function stored


I've read few articles about stack, heap and how they are used in program execution. Here is one of them. It is always said that when calling a function, it's parameters are placed to stack, as well as local variables (strictly saying that's not correct, as Eric Lippert described in his post, but that's not my question now).

My question is where the return value from function is stored and how is it passed to the caller? Nobody says it's placed on stack, but still nobody says it's not. Can anybody clarify that?

For example, consider the following function:

public DateTime GetTomorrowDate()
{
    return DateTime.Now.AddDays(1).Date;
}

Is my understanding that it will declare a local variable for return value correct? If so, why it isn't destroyed when function returns and it's stack frame destroyed? Is it declared in the caller's stack frame (even if it is not used by a caller)? Or maybe it is stored somewhere in a register (I don't believe that, because there may be not suitable-size register for some custom structure).


Solution

  • There are several strategies (that I have used).

    1. The return value is small enough to fit into a register, and is returned from the function as a value in a register reserved for that purpose. This can be extended to 2 or more registers if needed.
    2. The return value is created as a temporary variable on the stack frame of the caller. Then a reference (pointer) to the temporary is pushed on the stack so that the return value acts as an extra, out parameter. In some languages that variable appears inside the called function as a named variable ('result'), in others the compiler generates a move to that parameter.
    3. Two local variables are created, one inside the function and one outside. The value is copied from one to the other on function exit.
    4. The return value is simply created as an extra argument. The value found in that argument is pulled out by the caller before it unwinds the stack.
    5. The value is returned in a 'special' register, such as a floating point accumulator.
    6. The value is placed in a known location (such as a task frame), from which it can be retrieved later.

    There are probably others, but those are a good start.


    My answer was based on reading the question as wanting a summary of common techniques. In the context of C# it would apply to JIT-generated code, but not the CIL itself.

    Typical stack-oriented VM languages (including CIL) return values from functions primarily by leaving them on the stack when the function returns. The arguments sit below that, so some stack cleanup is required after function return.

    As @eric says, it's hard to see when this information might be useful. Obviously returning large value types from functions could have performance implications, but that is only as expected.

    The CIL is rather well documented, but JIT compilation and possible other mechanisms are not, which would further reduce the usefulness of any such insights.