It works for me because I have distinct moments in time when the whole stack is no longer needed. I’m working on a CAM/CAE app where work is organized as a sequence of optimization passes. I only need to keep the data within 1 iteration. At the end of every optimization pass I simply reset the stack pointer to the very bottom, and on the next iteration the stack starts growing again from zero reusing these physical memory pages.
BTW I’m using multiple of these stacks, one per thread. Helps with performance because no locks are needed, also L1 and L2 caches are inside cores, only the L3 is shared across the whole CPU.
The approach is pretty close to arena allocation https://en.wikipedia.org/wiki/Region-based_memory_management... The only twist, with modern 64-bit processors there’s no need for linked lists or multiple blocks, can instead add new physical pages into pre-allocated address space.