
What is “the stack”? - jor-el
http://jvns.ca/blog/2016/03/01/a-few-notes-on-the-stack/
======
Animats
It started with the x86 and PDP-11 thing. Some CPU architectures have a
specific stack pointer register, and some don't. IBM mainframes don't have a
dedicated stack pointer; the stack is a software construct. Neither do SPARC
machines.

There are also real stack machines, where instructions take operands from the
stack and push results on the stack. The Burroughs 5000 and the English
Electric LEO-Marconi KDF9 introduced that architecture around 1958. It lives
on in the x86 FPU register stack.

FORTRAN and COBOL originally forbade recursion. Compilers could allocate all
temporary memory at compile time. Stack overflow is impossible. Some real-time
systems still work that way.

~~~
weinzierl
Some microcontrollers (e.g. some PICs) even have a hardware stack for storing
return addresses. This stack is on the processor die like the registers and
not in the RAM which makes it even more _fundamental_.

~~~
Animats
Yes, and the PIC's return point stack is tiny. 2 (!) on the smallest machines,
8 on most, 31 on the "high end" PICs.[1]

[1] [http://embeddedgurus.com/stack-overflow/2009/04/pic-stack-
ov...](http://embeddedgurus.com/stack-overflow/2009/04/pic-stack-overflow/)

------
js2
This only scratches the surface of the topic. Here's a fine article that talks
about implementing a crash handler for JNI that goes into some of the more
complex issues of multiple stacks, signal handling, stack unwinding, and
calling across languages:

[http://blog.httrack.com/blog/2013/08/23/catching-posix-
signa...](http://blog.httrack.com/blog/2013/08/23/catching-posix-signals-on-
android/)

Aside: it's interesting how complex things which I learned in my younger years
have become. From understanding how a car operates and being able to perform
maintenance on it (compare rebuilding a carburetor or adjusting the timing to
fuel injection and computer controlled distributorless electronic ignition,
etc), to this topic, computer architecture. I'm glad I first learned these
things when they were simpler. I think they might seem daunting to start
learning about today. Though I guess like anything else, you start at the
surface and keep digging till you're satisfied you understand the topic well
enough. It's just that if you like to get to the bottom of those rabbit holes,
they're all so much deeper now. Shrug.

p.s. I really enjoy reading the author's posts. They are often about something
I'm familiar with, but it's neat to read someone writing about learning these
things through fresh eyes. I also sometimes realize I don't know something as
well as I thought. So thank you for sharing.

~~~
verbify
Conversely, I often think how lucky I am to have entered the industry at this
point.

I learnt HTML by initially downloading websites using ctrl-s, and then editing
them in notepad. I learnt HTML/CSS, and then JavaScript, and then other
languages.

I had the advantage that I had a GUI text editor. While I'm now quite
comfortable using VIM and command-line editors, I sometimes think if I had
tried to become a developer before the advent of GUI editors the learning
curve would have been too steep.

Perhaps under the hood things are more complex, but the interfaces have
improved.

~~~
Spearchucker
Interfaces have improved because the scope of the thing has grown.

I learnt HTML in 1995, I think shortly before HTML 2 was released. It was tiny
back then. Using anything other than a text editor was inconceivable (I doubt
there was any tooling back then). CSS and JavaScript didn't even exist. We had
CGI for emailing form content, but most of the web was static.

By 1998 I was convinced that every app ever would be online. Today I've come
full circle, avoiding web dev like the plague (too _many_ tools) and stick
with desktop and phone apps.

------
eddyb
"This isn't a theoretical question at all -- Rust actually has a different
calling convention from C"

Technically correct, but not at the register/stack management level: LLVM
still controls all of that, Rust only chooses the order of the arguments and
whether they are passed "directly" (in registers or on the stack, when
registers are exhausted) or "indirectly" (pointer to the value, whether it's
on the stack or somewhere else).

So the following statement, "which means it sets up its stack differently" is
incorrect: even if Rust wanted to, it couldn't do so with the LLVM backend.

~~~
gshrikant
In turn, isn't LLVM's stack management and argument passing dictated by the
hardware's ABI? ARM, for one, publishes its calling convention in AAPCS (ARM
Architecture Procedure Call Standard) which establishes the calling convention
on the C stack.

~~~
derefr
ARM and modern x64 have pretty rigorously-defined calling conventions, yeah.
x86, on the other hand, has six or seven different ones, and it was up to you
as a C programmer to choose which one to use for each given function, by
sticking e.g. a __stdcall or a __fastcall in your typespec.

~~~
atilaneves
x64 on Linux has a different calling convention than x64 on Windows.

------
kmm
`push` _decrements_ the stack pointer on x86 architecture, unlike on ARM. The
stack is strange in that it grows downwards. That is also why in the
disassembly of the `blah` function the argument is temporarily stored in `ebp
- 4` as ebp points to the top of the stack (which is at the lowest address).

I also wouldn't really say assembly follows the C stack pointer. The C
language makes no assumptions about stacks, stack pointer or return addresses.
It just describes "functions" to which the execution can be transferred and
from which can be returned. How this is implemented, is up to the compiler
designer and is platform dependent. I wouldn't be surprised to find out there
were platforms without a stack/stack pointer but with a C compiler

~~~
danellis
> `push` decrements the stack pointer on x86 architecture, unlike on ARM.

Why do you say "unlike on ARM"? In 25 years I've never seen anything but a
full, descending stack used as the program stack. You _can_ have ascending
stacks, but that's more an artifact of the fact that `pop` and `push` (which
didn't exist in the assembly language until Thumb) are just specific uses of
the general multiple load/store instructions `LDM` and `STM`, which can "write
back" an incremented or decremented base register.

------
__david__
And just wait until you find out about Harvard architecture[1] CPUs that don't
have a stack in the same sense that C has a stack—they generally have a call
stack that is only manipulated implicitly by JSR and RET style instructions.
It doesn't co-mingle return addresses with stack variables, like x86 (and von
Neumann architecture[2] CPUs).

[1]
[https://en.wikipedia.org/wiki/Harvard_architecture](https://en.wikipedia.org/wiki/Harvard_architecture)

[2]
[https://en.wikipedia.org/wiki/Von_Neumann_architecture](https://en.wikipedia.org/wiki/Von_Neumann_architecture)

~~~
dkarapetyan
At some point I was messing around with writing a toy VM and the choice of co-
mingling return addresses and what where the functions arguments felt weird to
me so I split them into two different stacks. It actually worked out. I mean
it meant I diverged from the book I was using to do this but that was part of
the fun to see which parts were simpler and which parts were more complicated
in my design.

This was the book I used in case folks are curious
[http://www.springer.com/us/book/9783642149085](http://www.springer.com/us/book/9783642149085).

~~~
jonsen
See Forth, Factor a.o. which are "harvard" languages.

------
melloclello
This is great - I'm self taught and have been working for years with no idea
what the stack is - I'd love one for this "heap" thing I keep hearing about,
as well.

~~~
mikekchar
Things like the stack and heap and many other things (like memory mapped I/O,
interrupts, etc) are often taught in first year university courses under a
name like "introduction to computer organization". There are many online
courses you can do for free, or you can simply go to a book store and pick up
a text book. Although none of it is something that you desperately need in
order to work as a programmer these days, it's one of those things that will
undoubtedly be useful almost every day, so I highly recommend it.

As for the heap, I noticed another description, but I'll have a go at
describing it in a slightly different way. Back in the old days you had a big
chunk of contiguous memory. It started at address 0 and ended at address 65535
(if you were lucky enough to have 64K of RAM!).

As you saw in the article, you need to set aside some of that memory for the
stack. In most languages that are compiled to machine language, the stack
holds storage for variables local to a function as well as the function return
address (the object that holds all the information for one function is called
a "stack frame"). As others have said, other organizations are possible, but
this is the most common.

Allocating memory on the stack is ideal because it is really easy to allocate
and deallocate. When you enter a new function, you look at how big each of the
local variables are and you simply move the "stack pointer" that many bytes
(either up or down, depending on the architecture of the machine). When the
function is finished, you simply move the stack pointer back to the start of
that stack frame. Super easy and super fast.

The problem is that as soon as your function finishes, the variable is
deallocated. So let's say you have a function that loads a picture from disk.
You make a big array in your picture loading function, put the picture data in
it and return it from the function... uh oh... As soon as the function ends,
the memory is deallocated :-(. That data is "gone" (what's worse is that the
data is still there, so it will appear to work until the stack grows big
enough to overwrite that data -- at which point your picture will be
corrupted). So we need a way to allocate memory that will stick around until
we tell the system that it is not needed any more.

That's what the heap is. I can call a system function (usually called
"malloc", which stands for "memory allocate") and say "I want 4K of memory".
It finds a contiguous chunk of 4K of memory, makes a note that the memory is
in use and then gives me a pointer to it. I can then use that pointer to write
into the memory. The system will not allocate any memory in that area unless I
tell it to "free" the memory (usually with a call to something called "free").
After that it can be used again.

The place the system looks for in the memory for this kind of allocation is
called the heap. So you can imagine the big block of memory in a computer is
divided up into at least 2 sections: the stack and the heap. Often a computer
system will put them at the opposite sides of the memory so that you can grow
the stack as much as possible. So I might start the stack at 65535 and grow
downward. And I might start allocating the heap from 0 upwards. In practice
there are other things you need to reserve memory/addresses for, so this is
not how it really works, but you get the idea.

Many programmers like allocating from the heap because it is easy. You don't
have to worry about the scope of your program. You allocate a variable and it
is there until you tell it to go away. But there are a couple of problems.
First, if you forget to free the memory, it _never_ goes away. This is called
a "memory leak". Also, you might accidentally try to use the memory after you
freed it, resulting in the same corruption noted earlier. But there is yet
another problem.

Let's imagine that I allocated 1024 40 byte strings. That will take up 40K
(almost all of my memory on this hypothetical old machine). Let's say that
every second string is only needed at the beginning of the program, so let's
free them all. That frees up 20K. However, the heap is now "fragmented". It
has 40 bytes allocated, 40 bytes unallocated, 40 bytes allocated, 40 bytes
unallocated... If I want to allocate 45 bytes, I will have to walk through 512
unallocated blocks, noting that each is too small, before I decide to allocate
at the end. This is very slow. If there isn't a 45 byte chunk available, then
my memory allocation will fail, even though I have 20K of free memory.

So, for many old timers like me, we favoured allocating on the stack. This
leads to fast, efficient, safe code that is also surprisingly elegant.
Unfortunately, it is also hard to design ;-)

These days, operating systems do a lot behind the scenes to make sure that you
don't fragment your heap too much, etc, etc. Modern interpreted languages use
garbage collection and allocate on the heap incrementally -- as if it were
stack allocation. Occasionally they wander through and deallocate unused
variables and compact the heap. Some of the algorithms used are very
sophisticated and quick.

I keep thinking about designing a functional language with stack allocation,
though... It appeals to my roots ;-).

Hope that description helps. Keep in mind that many details will be incorrect
for any specific system. I mostly just tried to make the description simple,
not accurate.

~~~
gmfawcett
I don't know if it's still the case, but past versions of the Chicken Scheme
system [1] implemented a remarkable stack-based allocation method from Henry
Baker, fondly called "Cheney on the MTA" [2]. If you like thinking about how
functional languages interplay with stack allocation, you would probably enjoy
bending your head around this. :)

[1] [http://www.call-cc.org/](http://www.call-cc.org/)

[2]
[http://home.pipeline.com/~hbaker1/CheneyMTA.html](http://home.pipeline.com/~hbaker1/CheneyMTA.html)

~~~
mikekchar
Sorry for the slow response. I'm reading this now and enjoying it very much.
Thank you!

------
cbd1984
What came first in modern systems was the ABI standards documentation, which
for Linux is the System V ABI, which is apparently followed even on BSD.

[http://wiki.osdev.org/System_V_ABI](http://wiki.osdev.org/System_V_ABI)

So System V came out in 1983, which postdates the introduction of the x86
architecture because the IBM PC was x86 from day one and it first came out in
1981. (I'm sure the 8086 is older, but it doesn't really matter here.)

Of course, it isn't the only standard out there for x86. Microsoft exists,
after all.

[https://en.wikipedia.org/wiki/X86_calling_conventions](https://en.wikipedia.org/wiki/X86_calling_conventions)

Anyway, all of these standards have to specify which registers get saved by
the called function and which get clobbered (and, therefore, must be saved by
the calling function if their values are important) and you can't know which
registers will exist on a given ISA until it exists, so the specific System V
ABI for the x86 couldn't possibly have existed until the chips did. Ditto the
System V ABI for the x86-64, the MIPS, and all of the other ISAs for which
there is such a document.

However, chip designers aren't stupid. They know that their chips will be used
to run binaries generated by C compilers, and that C compilers like stack
frames and registers. Therefore, modern ISAs are designed with those things in
mind, and I wouldn't be surprised if at least a few chip designers worked with
one eye on some ABI documentation.

(Compare modern chips with older minicomputer and mainframe ISAs, where call
stacks weren't always available and where subroutine calls sometimes involved
self-modifying code. The PDP-8, for example, did subroutine calls by writing
the the return address to the first (12-bit) word of memory in the subroutine,
and then jumping to the address just past it; returning was a simple matter of
jumping to a memory location loaded from a known address, but recursion was
effectively impossible. The PDP-8 was a fine machine for assembly language
programmers, but almost perversely unfriendly to modern mid-level languages,
such as C and Pascal.)

~~~
pjmlp
Burroughs, IBM mainframes, Ada Machines (by Rational), Xerox Parc and ETHZ
workstations are probably the best examples of more higher level friendly
architectures.

Or at least the better documented ones.

------
microcolonel
Bit of a nitpick, but ELF binaries are not used "only in Linux". OS X (Mach)
and Windows (PE) are really quite exceptional here.

System V R4, HP-UX, NetBSD, Linux, Solaris, AIX, IRIX, FreeBSD, OpenBSD,
DragonFly BSD, OpenVMS, QNX, Syllable, MINIX, BeOS/Haiku, RISC OS, GP2X,
Dreamcast, Gamecube, PSX, PS2, PS3 (maybe NetBSD or FreeBSD ish), PS4(based on
FreeBSD), AmigaOS 4, Morph OS, AROS, Open Firmware, and probably a whole bunch
of other systems use ELF binaries.

~~~
pjmlp
I think you can had existing mainframe OSes to the exceptions list.

~~~
wglb
I would have thought so, but this
[https://www-01.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/...](https://www-01.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.cbcdu01/cons_sf_1.htm)
suggest maybe otherwise.

~~~
pjmlp
So it seems, thanks for the hint.

------
vram22
This question reminds me of this Twitter thread, started by @raymondh:

Programmer survey: Do visualize consecutive memory locations as running left-
to-right, right-to-left, top-to-bottom, or bottom-to-top?

[https://mobile.twitter.com/raymondh/status/69510373753937510...](https://mobile.twitter.com/raymondh/status/695103737539375104)

~~~
gnuvince
Left-to-right for me; vertical is confusing because I like to put address 0 at
the top and address MAX at the bottom, but then the stack "grows down" and I
get confused.

~~~
mcguire
Strangely, on the processors I'm familiar with, the stack would grow up in
that orientation.

------
gcc_programmer
> I found it pretty surprising that the x86 assembly instruction set knows
> about the C stack pointer and how the stack in C is laid out. Of course, you
> could invent your own new smart stack format, but you wouldn't be able to
> use the callq and retq instructions to call functions.

I find it pretty amazing how modern "programmers" are lacking in knowledge of
basic computer science concepts. Let me nit-pick this statement as it is
obviously wrong.

The x86 assembly language knows _ABSOLUTELY_ nothing about the "C stack
pointer and how the stack in C is laid out". I will now illustrate why the
first part of this statement is wrong.

You can have a bare-metal x86 device that is only programmed though assembly
and push/pop would still be available. You are clearly confusing the roles of
the compiler, operating system, and the language your program is written in.
On a modern operating system like Linux, each program, when started, runs in a
new process, which has its own view of memory due to the virtual memory
system. Each program has its view of memory split into several sections, and
one of them is allocated as "stack space" (it's usually the high memory
addresses but this is architecture and/or OS dependent). When the program is
executed, %rsp would be set-up appropriately by the OS, and on context
switches it would save/restore it. Moreover, the actual address of your stack
in real memory is different for every program since each process/program has
its own mapping of virtual to physical memory, again provided by the OS.

Now, the second part about the "layout of the stack". The language you write
your program in (in this case C) knows _ABSOLUTELY_ nothing about the layout
of "the stack". However, when you give your source code to the compiler, it
will analyse it and, because C is low-level and staticly typed, will infer
_HOW_ it actually needs to lay out the stack _FOR you_. The output of the
compiler is assembly code, and the assembly code produced "knows" about the
layout of the stack because it has been written that way, generated by the
compiler. It is, actually, the compiler that knows about all of this, not the
source or assembly language.

Also, the example with python was a bad one - python is an interpreted
language, and AFAIK it has no concept of a stack, and even if it does, it is
the interpreter that allocates frames for it's own computations, not the
program that you are running. And, moreover, even if python does allocate
stack frames, they are most probably on the heap since it needs to do so
dynamically (again, since it's not compiled but interpreted).

~~~
vm_programmer
> python is an interpreted language, and AFAIK it has no concept of a stack,

Here, I could say, "I find it pretty amazing that old-timey[1] 'programmers'
fail to understand modern VM architecture". In fact, Python's interpreter
(cpython) is partly based on the concept of a stack.

But instead, I'll just explain that most modern VM's are either stack-based or
register-based. Stack-based VM's, like Python's, are (not surprisingly) based
on a virtual machine that uses a stack as it interprets Python's bytecode.
Yes, not the same level of abstraction as the call stack, but it's still a
stack.

So instead of expressing dismay at how little modern programmers know about
your little corner of the programming world (low-level systems programming),
why not spend some time pondering about how so much programming has moved up
the stack, and how abstractions have grown multifold.

In the 1970s and 1980s, it used to be that the typical self-taught programmer
had to know all the details low-level C and assembly, because that was almost
all there was _to_ know, unless you were a high-level Lisp hacker.

I mean, I'm a little surprised at how little some C kernel hackers know about
how the modern Web works (particularly regarding async programming patterns;
eg, been asked by one why I didn't just use a mutex somewhere in JS program,
wasn't trolling.). But then I realize that there's just too much for any but
the _most_ exceptional programmers to truly understand the full modern stack,
from system architecture, to GPU, to networking, to Web, on so on.

Why not instead welcome how this particular web-focused programmer is
interested in these low-level concepts? Is it that it wouldn't give you as
much of a chance to show off your knowledge?

1\. Said by another old-timey programmer.

~~~
TheOtherHobbes
VMs, and VM-based languages, are applications that run on top of all that
kernel programming.

Applications come and go so there's no reason to feel amazed that not everyone
know how the Python VM works - especially considering the details aren't
considered a core part of the Python curriculum, and they're almost completely
inaccessible to a typical Python user.

But not understanding the heap or the stack has real implications for
application performance and stability in whatever language or VM you're
working in. The abstractions have gotten more abstract, but developers who no
longer understand what happens when you run out of memory, or have a buffer
under/overflow, or your stack explodes because you're using recursion without
tail calls, are definitely a _bad thing._

You seem to be arguing that anyone who does web or VM dev doesn't really need
to know what happens inside a computer. I think that's an unhelpful view,
because it implies that all those abstractions "just work" and no one who uses
them needs to care how.

Do they really "just work"? I think the evidence suggests they don't. Perhaps
if developers understood hardware better that could be improved.

~~~
vm_programmer
> VMs, and VM-based languages, are applications that run on top of all that
> kernel programming.

I'm guessing the vast majority of all modern programming is done using VMs:
Python, Java, C#, Lua^, JavaScript^, etc. Basically, if your language uses a
runtime, it's likely using a documented or undocumented bytecode and VM.
They're pretty important to understand.

> You seem to be arguing that anyone who does web or VM dev doesn't really
> need to know what happens inside a computer.

That's not my argument. I highly recommend new programmers to understand as
much of machine architecture as reasonable. I'm just arguing that there's no
place for a systems programmer to be so dismissive toward a web programmer
because he/she doesn't know his part of the stack as well as he does. There is
always going to be a gap in knowledge about low-level programming between
these two specializations. That gap is no reflection on the intelligence or
competence of the higher-level programmer.

In this particular case, I believe that Julie has only been programming for a
few years. If that's the case, and assuming she only retains a fraction of
what she writes about, she's further along than most of us were at that point
(me included). Just because many of us bluff and bluster our way through
interactions with our peers, and Julie is up-front about her state of
knowledge, doesn't mean that she's an inferior programmer (at all). I suspect
her honest curiosity will carry her along to the top of our profession in due
time.

So if the parent had written something like, "it's _so_ important for new
programmers to work on this type of material, and it's heartening to see these
kinds of blog posts from younger programmers", then I would have applauded.

That also would have been more in line with the HN guidelines ("avoid
gratuitous negativity"), rather than the approach that was actually taken.

^ If you want to advance the "Lua, Python, and JS" aren't VMs, but rather
interpreters, you'll need to convince the likes of Mike Pall and Lars Bak that
they're not working on a VM (both have explicitly referred to their creations
as "VMs").

------
ajdlinux
I have a lot of fun explaining to people that the architecture I work on (IBM
Power aka PowerPC) uses calling conventions that for many function calls
doesn't touch the stack whatsoever. :)

~~~
CrystalGamma
How does it work? Pass the return address in a register and have enough
caller-saved registers that leaf functions can often do without spilling? Or
is there a more sophisticated mechanism?

~~~
ajdlinux
a) Return address is passed in the "link register" \- our call instruction is
"bl" (branch and link) and return is "blr" (branch link register)

b) We have 32 general purpose registers - the ABI defines a certain range of
registers (8 general purpose registers + a bunch of floating point and vector
registers) for passing parameters

c) The register range allocated for passing parameters is designated as
"volatile", so the caller can't rely on them coming back unmodified. This
gives you a few registers to play with.

(NB: I'm not hugely experienced in PPC assembly programming, I've just had to
touch it a couple of times and so I've read bits and pieces of the ABI, don't
trust what I say here...)

~~~
RealityVoid
Aha, but if you have at least function 2 calls, assuming none is inlined, you
HAVE to use the stack to save the prev LR. I actually think it's one of the
default processes at the beginning of a function, but maybe the compiler
optimizes in many cases.. I'm really curious if you can find some assembly
examples of functions where you don't use the stack. I've seen it on Renesas
MCU's and on PPC but only for veery simple functions.

Also, the architecture isn't exactly the one to blame for this, it's more
dependant on compiler calling conventions. I see things in the arch that
facillitate this, but I see it possible to implement this mechanism with or
without this arch support. I've only seen certain registers being reserved for
something in the compiler docs not the MCU docs.

Eh, maybe I'm rambling.

~~~
ajdlinux
Oh, yeah, only for leaf functions. Obviously not for multiple levels of calls.

My understanding is that PPC encourages register based parameter passing just
by virtue of having a tonne of registers.

------
nickpsecurity
"I found it pretty surprising that the x86 assembly instruction set knows
about the C stack pointer and how the stack in C is laid out. Of course, you
could invent your own new smart stack format, but you wouldn't be able to use
the callq and retq instructions to call functions."

Exactly the problem that everyone runs into coming up with more secure
alternatives to C or existing stack. One example was a 10+% penalty for
MULTICS-style, reverse stack due to indirection necessary. It's why I tell
people to use a RISC system if they want to build security on. Ideally,
microcode will come back with another HLL-to-microcode compiler so we can
implement secure alternatives directly as instructions. One could also
implement abstract machines like JVM's that way with no attacks through
abstraction gaps. Not to mention efficiency and arbitrary atomic actions.

------
agentgt
This sort of reminds me of stack virtual machines vs register based virtual
machines.

That is the low level code (assembly/bytecode) of function calls in a
programming language is very different depending on whether its stack or
register based.

------
lazyant
Usually I browse quickly the HN comments before reading (or skipping) an
article, in this case I browse through the blog post first and then I thouhgt,
"now the HN comments are going to teach me a lot, pointing out all the errors
in this post", wasn't dissapointed.

------
ChemicalWarfare
"I was really surprised that assembly instructions make assumptions about how
memory is organized and what information is in which registers."

This layout is a part of the specific architecture the compiler is generating
the assembly code for so it better know where the stack pointer is stored on
your platform :)

It's basically the same way as if you were manually writing Assembly code
yourself - you would need to know what registers are used for what purpose on
your platform.

------
vram22
Partly off-topic, but interesting, IMO:

This thread (the word "stack") triggered a memory of stack machines, which I
had read about much earlier, in computer mags such as BYTE. Looked it up just
now, and saw:

[https://en.wikipedia.org/wiki/Stack_machine#Virtual_stack_ma...](https://en.wikipedia.org/wiki/Stack_machine#Virtual_stack_machines)

which says that the JVM, CPython and a couple of Ruby VMs are virtual stack
machines.

------
vram22
Yet another wrinkle or dimension to this is the C vs Pascal calling
conventions [1]. I remember from Win 32 SDK days (MS VC, Borland C, etc.),
e.g.:

"long far pascal" as part of the type decl of functions.

[1] which was about the order in which function args get pushed onto the stack
before the jump to the function is taken - L to R or R to L.

Used to wonder what non-tech people would think if they heard a programmer
saying such stuff out aloud :)

------
yomism
Great post!

If you liked this kind of descent to the low-level realm, the post author
(Julia Evans) did a great presentation on Pycon 2015:

[https://www.youtube.com/watch?v=5v6o-VsLAew](https://www.youtube.com/watch?v=5v6o-VsLAew)

Sometimes when shit hits the fan it's nice to know what is going on behind all
those layers of abstraction.

------
sklogic
Many ISAs do not provide any specific stack handling instructions, and yet a C
ABI is based on a stack model very similar to x86 one. Instructions are
irrelevant, ABI is only a convention.

~~~
lmm
Sure. But I think it's fair to say that a structure that's part of the ABI is
more distinguished/fundamental than an ordinary datastructure defined in the
language.

------
jtchang
If a stack grows down how do you generally draw it on paper? Do you start with
some arbitrarily high address like 8000000 and just subtract from it? What
happens when you get to 0?

------
throweway
FPGAs are a way to avoid stacks too. E.g use c\ash to convert Haskell to a
circuit.

~~~
sklogic
Most modern FPGAs got two-port block rams. Ideal for stacks and FIFOs. Many
non-trivial designs are using stacks one way or another.

------
agumonkey
After reading about FP, TCO, the question shifted from `what` to `why`.

~~~
titanomachy
In case anyone is as confused as I was at first, agumonkey is referring to the
fact that Functional Programming languages are capable of Tail Call
Optimization, which allows functions to be composed without adding a new frame
to the stack.

~~~
abecedarius
It's kind of interesting that TCO goes together with FP, because logically it
makes at least as much sense for OOP, as Guy Steele pointed out at
[http://www.eighty-twenty.org/2011/10/01/oo-tail-
calls.html](http://www.eighty-twenty.org/2011/10/01/oo-tail-calls.html)

~~~
lmm
The concept of combining many values to get another value _of the same type_
is the heart of FP - function composition is just the most prominent example
of it. That definition of IntSet is effectively a cons list, a very FP-
oriented data structure. It's not an example of OOP needing tail calls because
it's not an example of OOP style (at least, if we believe that's something
disjoint from functional style) at all.

~~~
abecedarius
I don't consider OO to be disjoint from functional; the first OO language was
the untyped lambda calculus (as William Cook's said). In the taxonomy at
[http://www.paulgraham.com/reesoo.html](http://www.paulgraham.com/reesoo.html)
we're looking at 2. protection and 6. "all you can do (with an object) is send
a message", like how all you can do with a function is call it.

I don't agree that FP owns compositionality. In the 80s and 90s I used to have
to argue for the value of functional programming; nowadays the closeminded
attitudes come more from the "other side", and from one POV that's heartening
progress, but from another it's silly to be running culture wars around
technical ideas.

~~~
agumonkey
I've never seen an OO article / book that managed to demonstrate composition
even at 1% that the average Haskell book does. I'm trying not to take sides
too much, but I grew up in OO-land, left and keep getting mesmerized by what
the FP guys have been doing. Most recently monadic thinking (I know, monads)
but bind as an abstract composition operator is maddeningly beautiful.
Although I've heard that Monads themselve don't compose ~_~;

~~~
abecedarius
It's great how Haskell and friends keep spreading, isn't it? Here is an OO
book I like:
[http://www.erights.org/talks/thesis/](http://www.erights.org/talks/thesis/)

------
marssaxman
Well done.

I feel old, though.

------
kdkooo
Very informative, thanks for the summary!

------
tiedmann
I hoped to find something about "the stack" as in "full stack development"
(DB, middleware, frontend etc) because _that_ stack also needs some sorting
out.

~~~
sklogic
That hipstor buzzword hardly deserves "THE" in front of it.

