Memory management without stack? - recursion

To model the run-time semantics of procedures, it is known that a stack is generally needed.
If the language does not allow procedure recursion, do we have to have stacks?
And if the language does allow procedure recursion, but a recursive call can only happen at the end of a procedure, do we have to have stacks?

In Fortran, which you are probably interested in as an example, you do need stack for recursion. It is because you want local variables of recursive procedures to be independent for each invocation of the procedure. Not all have to be independent, but you generally want to have this possibility.
Without recursion, you have only one invocation of any procedure at any time so local variables can be static. Not so with recursion, you don't know how deep it will be so you need some dynamic data structure to store the data. You could emulate the stack on the heap if necessary, but you do need some dynamic memory.
Often, stack is also used for automatic (variable length) arrays, but that is not required, they can be on the heap depending on the compiler and its settings.

Stack is used to store return address to reach after a method ends execution.
Stack is also used to allocate objects with limited duration scope.
So, if your launguage does not permit automatic (C style) objects (or in other words, does not permit local scopes) or does not permit methods, I suppose language could omit stack implementation completely.
I think recursion doesn't have anything to do with requirement of stack.

Related

Is explicit stack better than recursion

We can print a linked list in reverse order with a stack as well as using recursion. My teacher said that using explicit stack is better since recursion also uses stack but has to maintain a lot of other parameters. Even if we use std::stack from stack, doesn't referring to an external library also take up time? How does using an explicit stack save time/space compared to using a recursive solution?
Recursion involves the use of implicit stacks. This is implemented in the background by the compiler being used to compile your code.
This background stack created by the compiler is known as a ‘Call stack’. Call Stack can be implemented using stack data structure which stores information about the active subroutines of a computer program.
Each subroutine call uses a frame inside the call stack called the stack frame. When a function returns a value, it’s stack frame is popped off the call stack.
Recursion's Call Stack vs Explicit Call Stack?
Stack overflow
The fundamental difference between the 2 stacks is that the space allocated by the compiler for the call stack of a program is fixed. This means there will be a stack overflow if you’re not sure about the maximum no. of recursive function calls expected and there are way too many calls than the space allocated to the stack can handle at a given point of time.
On the other hand, if you define an explicit stack, it’s implemented on the heap space allocated to the program by the compiler at run time. And guess what, the heap size is not fixed and can increase dynamically during run-time when required. You don’t really have to worry about the explicit stack overflowing.
Space and Time
Which one will be faster for a given situation?
Iterating on an explicit stack can be faster than recursion in languages that don’t support recursion related optimizations such as tail call optimization for tail recursion.
What’s Tail recursion?
Tail recursion is a special case of recursion where the recursive function doesn’t do any more computation after the recursive function call i.e. the last step of the function is a call to the recursive function.
What’s Tail-call optimization (TCO)?
Tail-call optimization is where you are able to avoid allocating a new stack frame for a function because the calling function will simply return the value that it gets from the called function.
So, compilers/languages that support tail-call optimizations implement the call to the recursive function with only a single stack frame in the call stack. If your compiler/language doesn’t support this, then using an explicit stack will save you a LOT of space and time.
Python doesn’t support tail call optimization. The main reason for this is to have a complete and clear stack trace which enables efficient debugging. Almost all C/C++ compilers support tail call optimization.
Sometimes explicitly controlling the stack helps simplify things when multiple parameters are being used.
whereas, a recursive solution makes the size of the source code a lot smaller and more maintainable.
Conclusion
In the end, There’s no fixed answer. For a particular scenario, many factors need to be considered such as scalability, code maintainability, language/compiler being used, etc.
The best way would be to implement the solution using both ways, time the 2 solutions on an input set and analyze peak space utilization before deploying it on a production setup.
See Wikipedia - Recursion versus Iteration

Does the processor use more than one stack to separate the call stack from the expression/register stack?

I was reading some basic articles about memory manipulation by the processor, and I was confused as to how the processor handles what comes next.
The concept of the call stack is clear, but I was wondering if the expression stack/register stack (used to make the calculations) is the same stack, or even if the stack for the local variables of a subroutine (a function) in a program is the same call stack.
If anyone could explain to me how the processor operates regarding its stack(s), that'd help me a lot.
All the processors I've worked on have just used a single stack for these.
If you think about what the processor is doing, you only need a single stack. During calculations you can use the same stack as the calling stack, as when the calculation is complete the stack will be 'clean' again. Same for local variables, just before you go out of the scope of the local variables your stack will be clean allowing the call to return correctly.
You can change the stack just set the SS:SP segment and pointer registers (just save the current values)
The procedure call parameters and local variables takes place in the stack. And the dynamically created objects take place in the heap (DS:DI). The SS:SP register pair shifted by the right amount of bytes to reserve the needed memory on the procedure call. And on the return the SS:SP sets back to the pre call state.

Is there any way to simulate recursion without implicit or explicit use of the stack ADT?

Usually functional recursion is simulated using the call stack ,but is there any way to simulate recursion without using the stack ADT?
Yes: one of the well-known approaches to implementing functional languages with first-class continuations is to heap allocate activation records, deallocation being handled by garbage collection. In this scheme call information is arranged in an immutable DAG, and a continuation has the particularly straightforward implementation of a pointer to an activation record.
Without the motivation of first-class continuations this arrangement isn't all that attractive for performance reasons.
You can simulate a stack using an array, for example (not using the stack ADT) ... But if you want an implementation of recursion without using stacks at all - implicit, explicit, or self-defined, go through this link:
http://home.olympus.net/~7seas/recurse.html

Procedure Recursion and Stacking

To model the run-time semantics of procedures, it is known that a stack is generally needed.
1. If the language does not allow procedure recursion, do we have to have stacks?
2. If the language does allow procedure recursion, but a recursive call can only happen at the end of a procedure, do we have to have stacks?
No. However if the language is turing complete you can simulate one if you want. Eg. Brainf**k doesn't have a stack and I have made a LISP1 interpreter with it that supports procedures and macros.
It depends. Traditionally you need a way to have multiple nested arguments and that is a stack by definition. If your procedures have no arguments and only can call something else in the end of it's code It's almost as if procedures give less value than a goto would. However if you mean tail instead of end then it would be just as good as a goto without arguments.

What affects stack size in recursion?

Exactly what parts of a recursive method call contributes to the stack--say, the returned object, arguments, local variables, etc.?
I'm trying to optimize the levels of recursion that an Android application can do on limited memory before running into a StackOverflowException.
Thanks in advance.
If you run out of stack space, don't optimize your stack usage. Doing that just means the same problem will come back later, with a slightly larger input set or called from somewhere else. And at some point you have reached the theoretical or practical minimum of space you can consume for the problem you're solving. Instead, convert the offending code to use a collection other than the machine stack (e.g. a heap-allocated stack or queue). Doing so sometimes results in very ugly code, but at least it won't crash.
But to answer the question: Generally all the things you name can take stack space, and temporary values take space too (so nesting expressions like crazy just to save local variables won't help). Some of these will be stored in registers, depending on the calling convention, but may have to be spilled(*) anyway. But regardless of the calling convention, this only saves you a few bytes, and everything will have to be spilled for calls as the callee is given usually free reign over registers during the call. So at the time your stack overflows, the stack is indeed crowded with parameters, local variables, and temporaries of earlier calls. Some may be optimized away altogether or share a stack slot if they aren't needed at the same time. Ultimately this is up to the JIT compiler.
(*) Spilling: Moving a value from a register to memory (i.e., the stack) because the register is needed for something else.
Each method has two stack frame sizes associated with it, the stack required for arguments and local variables, and the stack required for expression evaluation. The return value only counts as part of the stack required for expression evaluation. The JVM is able to verify that the method does not exceed these sizes as it executes.
Exactly how much stack is required for variables and expression evaluation is down to the bytecode compiler. For instance it is often able to share local variable slots among variables with non-overlapping lifetimes.

Resources