
Stack Free Recursion (1995) - luu
http://home.olympus.net/~7seas/recurse.html
======
barrkel
The "spaghetti code" conversion - using labels instead of calls - only works
if the original recursion didn't actually need to be recursive, i.e. it didn't
require storage proportional to its maximum stack depth, and could already
have been rewritten as a loop.

Other than that, the approach trades code (time) for data (space). Instead of
making a copy of activation record variables at each level of the recursion,
the mutation of the activation record variables is implicitly encoded in the
return address pushed onto the call stack, because the code at the return
address undoes the mutation.

------
icarot
I'm so confused by this. His solutions end up using recursion, yet he says
it's stack-free. Which doesn't make any sense at all considering deficient
compilers allocate stack frames on each call for languages like C. This is all
chapter 1 SICP-grade stuff. Unless you're going to do a technique (read: hack)
such as trampolining (certainly not done here) I don't see how it's possible
to have recursion without a stack. The runtime should keep pushing frames.
Notice there's still a pointer defined in the Hanoi().

I don't think this can be computed on a register machine. What do you
girls/guys think?

~~~
waterhouse
The title ("Stack Free Recursion") is certainly misleading, as are the labels
of several examples ("Stackless Factorial", "This Form of Routine Can Be
Changed to Stack Free Recursion", the function stackfree(x), "Stackless Towers
of Hanoi"), in that all these examples, and all but one example in the paper,
implicitly use a stack for return addresses.

However, in the abstract, the definition of the bet, and throughout much of
the article, the author does distinguish between the concepts of a "data
stack" and a "return stack", and he illustrates _one_ case in which the return
stack is in fact eliminated entirely (and says, at the bottom, that it is
"sometimes possible" to remove the return stack). Overall, 19/48 instances of
the string "stack" are preceded by either "data" or "return".

Is it fair to use "stackless" to mean "data-stack-less", without saying so
explicitly? I would say no. The stated advantage is that less memory is used
(and that the result "sometimes is faster", presumably due to fewer push/pop
operations). However, "stackless" implies that you've eliminated the problem
completely, when you've achieved less, perhaps significantly less than that.
For example, in the case of factorial, the naive approach stores two CPU words
per level of recursion (the value of x and the return address), and the
(data-)stackless approach stores one CPU word per recurse (the return
address); this fixes _half_ the problem.

Also, see this in the abstract: "We also present a method that uses no return
stack or data stack and we derive a simple line drawing function using the
information presented herein." This wording strongly suggests that the line
drawing function doesn't use a return stack, when it does. A comma after "data
stack" would make it a _little_ less non-obvious that the two statements in
the sentence are not related to each other. I would have to call this paper
"oversold".

~~~
icarot
Definitely agree on the "oversold".

I notice you said "half the problem" at the end of your third paragraph there.
I'm assuming the other half is that it seems foolish to save the return
address for a recurse. Only the first call should save a return. I think one
problem with compiler writers is that oddly they don't separate how to treat a
procedure and its recursions as different cases. They just fit every procedure
into the same bucket-list algorithm of 1. allocate frames 2. allocate return
address.

------
rogerbraun
Isn't this just rewriting tail calls
([http://en.wikipedia.org/wiki/Tail_call](http://en.wikipedia.org/wiki/Tail_call))?
Am I missing something?

~~~
segmondy
This was 1995, Outside of Prolog, LISP, Scheme, and other exotic languages,
what popular languages of that era offered TC? (C, Pascal, C++, BASIC,
FORTRAN, etc) didn't.

~~~
dragonwriter
Tail call optimization, strictly speaking, isn't a language feature (though a
language standard can require it in conforming implementations, as Scheme
standards have. Which brings me to:

> (C, Pascal, C++, BASIC, FORTRAN, etc) didn't.

I'm not sure that's true. gcc, in optimizing modes, could do at least tail
recursion elimination at least as far back as 1999, and does more now. (The
earliest reference I can find refers to gcc 2.95.3, but its not clear to me if
that's the first version that did it, or just the earliest version on which
the writer tested and confirmed the behavior.)

Outside of a handful of languages, tail call elimination in either restrictive
(e.g., tail recursion only) or general forms is an optional optimization
rather than an essential behavior.

------
andreasvc
This seems to me like the sort of optimization a compiler should do for you.
Does GCC do this?

------
hughes
As a dev who has never really got into the low-level parts of programming, I
don't really understand yet how this is stackless. I thought the stack had
more to do with the depth of function calls than with the passing of function
parameters. Can anyone here explain it?

~~~
ekr
Haven't read all of it, but the author assumes the availability of a return
stack, which somehow makes this approach not really stack-free. Most modern
calling conventions (x86) use a register(eax/rax) for returning values.

But his approach does in fact conserve stack space.

~~~
LukeShu
A little bit further down, he shows how to avoid using the return stack also,
to use no stack at all.

Also, by "return stack", he isn't referring to the mechanism for returning
values, he is referring to the mechanism for knowing which address to jump
(return) to when a function is finished. In x86, this is still done with a
stack, but the stack modifications are done "behind the scenes" by the
call/ret instructions.

------
mqsiuser
Hoare writes in his (famous) paper on quicksort (in 1962): "We'd implement our
own recursion schema for perf reasons"

Recursion is just a tree.

Calling a function/procedure is expensive (in memory and run-time).

Create a tree (e.g. linked list), base your recursion on that!

You are not at university anymore, where you look at naive implementations for
the sake of learning.

Spoiler: I (actually _do_ ) resolve (function-call) recursion to (tree-based)
recursion when I feel like I want to.

Downvoters pls explain.

