y = a*x + foo(b);
Lisp: (setq y (+ (* a x) (foo b)))
Example stack instructions for the above (many variations possible): PUSH a
PUSH x
MUL
PUSH b
PUSH foo
CALL
ADD
PUSH y
SETQ ; or MOV or whatever your arch wants to call it
Dennis Ritchie added the register storage qualifier keyword to primeval C (and also auto, inherited from B) to make the earliest C compilers easier to write, because bug-free register allocation is a very hard problem and Ritchie's earliest PDP had only a few kilobytes of core to hold both the C compiler's code and the chunk of program text being currently translated, so a complicated register allocator was out of the question. $ wc `find src/core \( -name \*.c -o -name \*.h \) -print`
[...]
81604 249802 3001043 total
So 81,604 lines for src/core vs 17,263 for OpenRC. So why does systemd need 677,446 lines of C code in its entire tree if src/core is 81,604 lines? $ git clone git://git.musl-libc.org/musl; cd musl
$ wc `find . \( -name \*.c -o -name \*.h \) -print`
[...]
7 11 94 ./src/unistd/_exit.c
104637 330017 2883975 total
$ git clone https://sourceware.org/git/glibc.git; cd glibc
$ wc `find . \( -name \*.c -o -name \*.h \) -print`
[...]
49 216 1647 ./wctype/wctype_l.c
1389414 6823487 52867225 total
So 104,637 lines vs 1,389,414 lines. There are more human programmers on this planet who can audit 104,637 lines than can audit 1,389,414 lines. What is glibc doing that is so terribly useful that it needs to be 13 times the size of Musl? It's just standard GNU bloat. And why does GNU awk (gawk) need to add network socket programming support? Isn't this beyond the scope of what awk does well and does best? Keep adding features, keep increasing the attack surface.... $ wc `find . \( -name \*.c -o -name \*.h \) -print`
[ ... ]
37 89 1100 ./src/xdg-autostart-generator/xdg-autostart-service.h
677446 2077830 23953433 total
(Simple wc includes blank lines and comment lines and we could be fancier but wc will do.) Now compare the same thing with OpenRC (git clone https://github.com/OpenRC/openrc): $ wc `find . \( -name \*.c -o -name \*.h \) -print`
[ ... ]
56 242 2209 ./src/rc/_usage.h
17263 53243 419162 total
So 677,446 lines vs. 17,263 lines. $ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 9952 764 ? Ssl Jun15 1:06 /init
I'm sick of Microsoft telemetry and I would like to go back to native Linux, but non-systemd options are very limited:
I see where you are coming from now, and yes, you are correct these stack CPUs are not pure. The whole point though of trying to do a stack machine CPU (however impure) is to simplify register allocation, which is why the 1982 C Machine Stack Cache paper I cited has a title beginning with Register Allocation for Free.... Whether writing a compiler or writing assembler by hand, at some point you hit the problem that it's not easy to fit all your variables into registers r0 through r15 (or whatever) and you must then spill them onto the stack. You evidently have first-hand experience with the challenges of keeping performance critical variables in registers. So the essence of these stack processors, the whole point of trying to build them, is to just let you spill everything on the stack and let the CPU worry about how to map stack locations to fast registers.
Excerpt from the C Machine Stack Cache paper[1]:
The goal of the Stack Cache is to keep the top elements of the stack in high speed registers. The problem we solve is how to perform the allocation of these registers without placing the burden on the compiler, and at the same time retaining the efficiency of register accesses. Control of the hardware, the instruction set, and a disciplined use of the aforementioned calling sequence allows a memory-to-memory style architecture (i.e. registerless to the compiler) to perform an automatic binding of memory addresses to machine registers.
[1] https://www.eecg.utoronto.ca/~jzhu/csc467/readings/ra-for-fr...