|
Forth generated code is basically a long series of "assembler macros", always doing the same maximally-generic thing for each primitive operation. Even a very simple-minded compiler of the 1980s could already beat that. VAR1 @ VAR2 @ + VAR3 !
will execute this at run time: ; push address of VAR1
inc bx
inc bx
push bx
; fetch and jump to next primitive
lodsw
mov bx,ax
jmp [bx]
; push contents of variable
pop bx
push [bx]
; next primitive...
; push address of VAR2, next...
; push contents of variable, next...
; add top two stack elements, push sum, next...
; push address of VAR3, next...
; store to address, next...
There are some "low-hanging fruits", like keeping top-of-stack in a register, which the Forth used here doesn't do though. Or direct threading.Still, an incredibly stupid compiler could do better (for execution speed, definitely not size) by outputting similar code fragments - including all the pushes and pops, who needs a register allocator? - just without the interpreter overhead (lodsw etc.) in between them. A compiler producing worse code likely didn't exist before today's glorious world of vibe coding ;) A slightly better compiler would directly load the variables instead of first pushing their addresses, etc. You don't need to be an expert in compiler theory to come up with enough ideas for boiling it down to the same three instructions that a competent assembly programmer would write for this operation. And at least for this case, Forth doesn't even have the size advantage anymore, the code would only take 10 bytes instead of 14. |