"self tail recursion" is only a simple case of the idea of tail calls (-> tail call optimization, TCO). In a Lisp variant/implementation with TCO (generally Scheme implementations and also a bunch of CL compilers, like SBCL), ANY tail call is automatically optimized to a jump, without growing the stacks, without the need to use special construct like Clojure's loops or trampolines.
for example SBCL on ARM64
CL-USER> (defun example (a b)
(if (> a b) (sin a) (cos b)))
EXAMPLE
In above CL function, the calls to SIN and COS are tail calls. Let's look at the machine code, which is automatically and by default created by above definition:
CL-USER> (disassemble #'example)
; disassembly for EXAMPLE
; Size: 100 bytes. Origin: #x7006948100 ; EXAMPLE
; 00: AA0A40F9 LDR R0, [THREAD, #16] ; binding-stack-pointer
; 04: 4A0B00F9 STR R0, [CFP, #16]
; 08: 7A0300F9 STR CFP, [CSP]
; 0C: EA030EAA MOV R0, R4
; 10: EB030DAA MOV R1, R3
; 14: FA031BAA MOV CFP, CSP
; 18: 29BE80D2 MOVZ TMP, #1521
; 1C: BE6B69F8 LDR LR, [NULL, TMP] ; SB-KERNEL:TWO-ARG->
; 20: C0033FD6 BLR LR
; 24: 4EB741A9 LDP R4, R3, [CFP, #24]
; 28: 5F011DEB CMP R0, NULL
; 2C: E1000054 BNE L0
; 30: EA030DAA MOV R0, R3
; 34: F6FBFF58 LDR LEXENV, #x70069480B0 ; #<SB-KERNEL:FDEFN COS>
; 38: 570080D2 MOVZ NARGS, #2
; 3C: DE9240F8 LDR LR, [LEXENV, #9]
; 40: DE130091 ADD LR, LR, #4
; 44: C0031FD6 BR LR
; 48: L0: EA030EAA MOV R0, R4
; 4C: 76FBFF58 LDR LEXENV, #x70069480B8 ; #<SB-KERNEL:FDEFN SIN>
; 50: 570080D2 MOVZ NARGS, #2
; 54: DE9240F8 LDR LR, [LEXENV, #9]
; 58: DE130091 ADD LR, LR, #4
; 5C: C0031FD6 BR LR
; 60: E00120D4 BRK #15 ; Invalid argument count trap
One can see in the assembler, that the > function is called as a subroutine with BLR (-> it is not a tail call), while SIN and COS are called with a BR, basically a jump, since those are tail calls. Thus in any piece of code, the compiler recognizes the tail calls and will create the corresponding machine code jmp instruction (minus any restriction a compiler will further have).
Generally Common Lisp doesn't require TCO, though many compilers provide it as an optimization. Required is a basic low-level lexical goto construct, which is used to implement various loop.
> but GraalVM really does help with that
In something like SBCL there is no special tool/implementation (restricting dynamism) needed to create an executable. It is a function call and one second execution time away. The resulting executable starts fast and is basically a full Lisp with all features, again.