Also in terms of stack, how do you think pretty much all modern CPUs call a subroutine? They all have a register that is used as a stack pointer and the instruction for returning from the subroutine requires the return pointer to be sitting on the stack.
Hardly ANY popular ISA designed since 1980 requires a subroutine return address to be sitting on the stack!
It's certainly not true for SPARC, MIPS, ARM, PA-RISC, PowerPC, Alpha, Itanium, RISC-V, SuperH which all store the return address for a function call into a register, and the return instruction takes it from the same register.
AVR and MSP430 do store/take return addresses on the stack. Exceptions to the rule.
x86, m68k, 8080, 8051, z80, 6800, 6502, 6809, VAX all store return addresses on the stack. They were all designed in the 1970s -- 40+ years ago!
Okay i did put it a bit broad. Yeah there are architectures that use things like a link register to hold the return address, but as i have explained later on even those eventually end up pushing it on the stack as soon as functions become complex enough.
Yes of course, but that is typically far fewer than half of all function calls. Often it will be 10% or less as most functions that call other functions either call more than one of them or else call the same one repeatedly. Functions that only call one other function are I'd say more likely than not to be able to do that as a tail call (i.e. just a jump, no return) as they're more likely to be fiddling with the arguments than fiddling with the return result.
You are typically required to restore registers to there previous state before finishing a subroutine.
Some of them. RISC-V, for example, gives you 15 registers (a0-a7, t0-t6) that a function can use without restoring them, which very often reduces the memory load of leaf functions (which is most functions, as pointed out above) to only the absolute essentials of explicitly accessing arrays or pointer structures in the actual source code.
The easiest way to preserve the link register is to push it on the stack, do your stuff (including call other subrutines), pop it back off the stack and return to it. So in more RISC like architectures you end up doing the same as CISC does, just doing it step by step instead of a magical single instruction. Yes you can avoid putting it on the stack if your subroutine never calls other subroutines but all practical programs do that at some point.
Absolutely! If you do need to, which isn't actually all that often, dynamically. It's true that if you have a link register then you need extra instructions to save and restore the link register, but an advantage is you can do those any time you like, when it's convenient, for example re-loading the link register a few instructions before the actual return, so that you already have it when you need it. You can also avoid the save and re-load on any execution paths through the function that don't actually call another function.
It's a bit of a optimization to skip stacking the return address on small simple functions, but its even more of an optimization to simple tell the compiler to inline the function rather than calling it, and CISC like systems that offer the magical stack instruction for returning can also be made to call and return in the RISC like way of using a link register.
Sure, but you have to be careful with inlining. On small systems total code size is often critical, and even on bigger ones excessive inlining destroys the effectiveness of the instruction cache.
Learning that avoiding the "magical" CISC instructions on VAX and IBM 370 actually made programs up to two or three times faster on the same machine was exactly how RISC got started :-)
You can write programs without using a stack at all, but then again you can also compute everything with only a single accumulator register and a few bitwise instructions. But just because it can be done in a simpler way does not automatically mean its a good way of doing it. And a stack is one of those things that makes life a lot easier.
I would never suggest that! The stack is a fantastic invention and the advent of instruction sets that made it natural and easy to use it was one of the biggest advances in computer instruction sets. Making PIC code natural and simple (which x86 *still* doesn't do!) is another. And the development of first index registers (full absolute memory address in the code, and small array index in a register) and then base registers (full memory address in a register, and small offset in the code) is the 3rd.
The trick is in finding the minimal instruction set that gives all the nice features you want (recursion, PIC, stack, heap, dynamic linking, virtual functions, switch statements) with maximum performance, minimum energy usage, and minimum code size.