;; ;; GCL supports limited tail call optimization in compiled functions. ;; In this log we will see how it works. ;; ;; Each DEFUN-ed function starts its life as a lambda block. Lambda ;; blocks are lists (literally, CONS cells) and are executed by being ;; interpreted. They all get stack frames created for them. Long recursive ;; evaluations of lambda blocks will run out of memory dedicated to stack ;; frames. We'll see how long it takes for FACT. ;; Switching to a to a tail-call form of a function while it's still a ;; evaluated as a lambda block will reduce the size of each frame and ;; will buy you more frames, but you will eventually run out. ;; Switching a tail-recursive form to the compiled function applies real ;; tail-call optimizations (so long as the function calls _itself_ in the ;; tail call position---more modern compilers can do better and will look ;; through a chain of calls). So we'll see loops without stack frames in ;; compiled tail-call functions. % gcl GCL (GNU Common Lisp) 2.6.14 Fri Jan 13 10:47:56 AM EST 2023 ANSI git: Version_2_6_14 [..skipped..] ;; Naive recursive form: >(defun fact (n) (if (= n 0) 1 (* n (fact (1- n))))) FACT ;; DEFUN makes a lambda block: >#'fact (SYSTEM:LAMBDA-BLOCK FACT (N) (IF (= N 0) 1 (* N (FACT (1- N))))) ;; So far so good: >(fact 1000) 4023872...000000 ;; ...but then we run out of stack: >(fact 2000) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by IF. Condition in IF [or a callee]: INTERNAL-SIMPLE-ERROR: Invocation history stack overflow. Broken at *. Type :H for Help. 1 Return to top level. >>n ;; <--- that's what N is at this moment, 637 calls left to go 637 >>1 Top level. >(- 2000 637) 1363 ;; So this is the last N for which the naive form will succeed: >(fact 1363) > 2191982503321451....000000 >(fact 1364) >> Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by IF. Condition in IF [or a callee]: INTERNAL-SIMPLE-ERROR: Invocation history stack overflow. Broken at *. Type :H for Help. 1 Return to top level. >>n 1 ;; <--- just one more frame was left to go before we ran out of memory >>1 Top level. ;; Let's try the tail-recursive form: >(defun fact1 (n acc) (if (= n 0) acc (fact1 (1- n) (* n acc)))) FACT1 ;; ...we make it past 2000: >(fact1 2000 1) 33162750924506332411753933...0000000 ;; ... but still run out: >(fact1 3000 1) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by IF. Condition in IF [or a callee]: INTERNAL-SIMPLE-ERROR: Invocation history stack overflow. Broken at IF. Type :H for Help. 1 Return to top level. >>n 955 ;; <<-- 955 frames left to go >>1 Top level. >(- 3000 955) 2045 ;; This will succeed, >(fact1 2045 1) 1950125394810..00000000 ;; ...this will run short by one frame: >(fact1 2046 1) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by IF. Condition in IF [or a callee]: INTERNAL-SIMPLE-ERROR: Invocation history stack overflow. Broken at IF. Type :H for Help. 1 Return to top level. >>n 1 >>1 ;; Let's see what this tells us about the relative stack frame sizes for these two ;; lambda blocks: Top level. >(- 2045 1363) 682 >(/ 1363 682) 1363/682 ;; very close to 2.0 ;;; So we see that the tail-recursive form FACT1 has frames twice as small as those of FACT, ;;; but still non-empty. Hence, they still exhaust GCL's call stack but fit twice ;;; as many frames, and allow to twice the range of FACT. ;;;;; ------------------- And now for the compiled optimized form ---------------- ;; The compiler tells you that it found and deployed a loop (see YES below) >(compile 'fact1) Compiling /var/folders/tp/17fq2kwj13bb2xj7s5hbr0ch0000gn/T/gazonk_53343_0.lsp. End of Pass 1. ;; Note: Tail-recursive call of FACT1 was replaced by iteration. //// <<--- YES! End of Pass 2. OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3 Finished compiling /private/var/folders/tp/17fq2kwj13bb2xj7s5hbr0ch0000gn/T/gazonk_53343_0.o. ;; Loading #p"/private/var/folders/tp/17fq2kwj13bb2xj7s5hbr0ch0000gn/T/gazonk_53343_0.o" start address -T 0x10b2f7330 ;; Finished loading #p"/private/var/folders/tp/17fq2kwj13bb2xj7s5hbr0ch0000gn/T/gazonk_53343_0.o" # NIL NIL ;; Fast & stackless: >(fact1 3000 1) 4149359603437854085.....0000 >(fact1 300000 1) 147739153173.....0000 ;; We can also do 3,000,000 but that'd take 3 hours to compute, see notes on GCL and LLDB ;;; -------------------------- A few side notes ------------------------ ;; Compilation will help the naively recursive FACT as well: >(compile 'fact) Compiling /var/folders/tp/17fq2kwj13bb2xj7s5hbr0ch0000gn/T/gazonk_53343_0.lsp. End of Pass 1. End of Pass 2. OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3 Finished compiling /private/var/folders/tp/17fq2kwj13bb2xj7s5hbr0ch0000gn/T/gazonk_53343_0.o. ;; Loading #p"/private/var/folders/tp/17fq2kwj13bb2xj7s5hbr0ch0000gn/T/gazonk_53343_0.o" start address -T 0x10b5838d0 ;; Finished loading #p"/private/var/folders/tp/17fq2kwj13bb2xj7s5hbr0ch0000gn/T/gazonk_53343_0.o" # NIL NIL ;; Compiled form will use C stack frames, which are much more compact: >(fact 100000) 28242294079.....0000000 ;; ...but are still using the stack, and we will run out of it: >(fact 300000) Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by FACT. Condition in FACT [or a callee]: INTERNAL-SIMPLE-ERROR: Value stack overflow. Broken at FACT. Type :H for Help. 1 Return to top level. ;; Sadly, we cannot look into compiled frames for N from GCL's debugger once the function ;; has been compiled: >>n Broken at FACT. Type :H for Help. Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by FACT. Condition in FACT [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on N: Unbound variable: Broken at FACT. 1 (abort) Return to debug level 1. 2 Return to top level. >>>2 Top level. ;;; But we'll still be able to catch the value of N that causes stack overflow, by ;;; attaching LLDB to GCL while FACT is running. ;;; First, we need to understand how the functions are compiled. ;;; -------------------- How GCL compiles functions ----------------- ;; GCL's compiler first converts the Lisp function to C then compiles C into ;; an object file. Then it loads the object file into GCL's memory and links ;; the compiled function in. ;; It's easiest to catch the compiler's intermediate steps by compiling a file: >(system "cat fact0.lsp") (defun fact (n) (if (= n 0) 1 (* n (fact (- n 1))))) 0 0 ;; :c-file is an optional ("key") argument, that asks the compiler to keep the C file, ;; rather than deleting it. ;; :NAME is Common Lisp's syntax for optionals. Look it up. >(COMPILE-FILE "fact0.lsp" :c-file t) > Compiling fact0.lsp. End of Pass 1. End of Pass 2. OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3 Finished compiling /Users/user/cs59/cosc59.gitlab.io/lisp/fact0.o. #p"/Users/user/cs59/cosc59.gitlab.io/lisp/fact0.o" NIL NIL ;;; The C file has a huge preamble with many C macro definitions. The actual C function ;;; created for FACT is at the end, and starts with a label L1. I use the AWK Unix ;;; command to show only lines after the pattern "L1" is seen. ;;; There are easier ways to show the tail end of the file (e.g., "tail" command) ;;; but I like sticking to the old Unix ways :) >(system "awk '/L1/{found=1} found' fact0.c") static void L1() {register object *base=vs_base; register object *sup=base+VM1; VC1 vs_check; {object V1; V1=(base[0]); vs_top=sup; goto TTL; TTL:; if(!(immnum_eq((V1),small_fixnum(0)))){ goto T2; } base[1]= small_fixnum(1); vs_top=(vs_base=base+1)+1; return; goto T2; T2:; base[1]= immnum_minus((V1),small_fixnum(1)); vs_top=(vs_base=base+1)+1; (void) (*Lnk0)(); ;; <--- recursive function call to FACT, see below vs_top=sup; V2= vs_base[0]; base[1]= immnum_times((V1),V2); vs_top=(vs_base=base+1)+1; return; } } static void LnkT0(){ call_or_link(VV[0],(void **)(void *)&Lnk0);} /* FACT */ 0 0 ;;; So we see the call, and it will be compiled as a CALL instruction, with a ;;; stack frame. ;;; Let's see the compiled tail call's C version: >(system "cat facta.lsp") (defun fact-acc (n acc) (if (= n 0) acc (fact-acc (- n 1) (* n acc)))) 0 0 >(COMPILE-FILE "facta.lsp" :c-file t) (defun fact-acc (n acc) (if (= n 0) acc (fact-acc (- n 1) (* n acc)))) Compiling facta.lsp. End of Pass 1. ;; Note: Tail-recursive call of FACT-ACC was replaced by iteration. ;; <-- YES! End of Pass 2. OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3 Finished compiling /Users/user/cs59/cosc59.gitlab.io/lisp/facta.o. #p"/Users/user/cs59/cosc59.gitlab.io/lisp/facta.o" NIL NIL >(system "awk '/L1/{found=1} found' fact0.c") static void L1() {register object *base=vs_base; register object *sup=base+VM1; VC1 vs_check; {object V1; object V2; V1=(base[0]); V2=(base[1]); vs_top=sup; goto TTL; TTL:; if(!(immnum_eq((V1),small_fixnum(0)))){ goto T2; } base[2]= (V2); vs_top=(vs_base=base+2)+1; return; goto T2; T2:; {object V3; V3= immnum_minus((V1),small_fixnum(1)); V2= immnum_times((V1),(V2)); V1= (V3);} goto TTL; // <--- no CALL, no indirection, just a JMP to the label TTL. It's a loop! } } 0 0 ;; The same difference shows in assembly: in FACT's disassembly, there is a CALL to self, ;; in FACT1 there is a JMP back into the loop. ;; One caveat is that fact0.o and facta.o are object files, not executables. This means ;; that addresses and offsets in the instructions are not yet filled in, and are ;; instead all 0s. Instead, the relevant targets are stored in the separate slots ;; called relocation entries, which hold information on how to find the right address ;; when linking the object file in. To show these entries in disassembly, objdump must ;; be given the "-r" option alongside the "-d" option. ;; Observe that most CALL instructions below lead to operations on integers: ;; comparison (_number_compare), multiplication (_number_times), subtraction (_number_minus). ;; For extra credit, you can figure out where N is stored and how it is passed around ;; into these arithmetic operations. ;; The most interesting CALL, however, is the recursive call to the function itself, at ;; the offset D3. This CALL will save its return address on the C call stack, and ;; this will eventually overflow the stack. Every little bit counts :) >(system "objdump -r -d fact0.o | awk '/L1/{found=1} found' ") 0000000000000010 <_L1>: 10: 55 pushq %rbp 11: 41 57 pushq %r15 13: 41 56 pushq %r14 15: 41 55 pushq %r13 17: 41 54 pushq %r12 19: 53 pushq %rbx 1a: 50 pushq %rax 1b: 48 8b 1d 00 00 00 00 movq (%rip), %rbx # 22 <_L1+0x12> 000000000000001e: X86_64_RELOC_GOT_LOAD _vs_base@GOTPCREL 22: 4c 8b 3b movq (%rbx), %r15 25: 4d 8d 67 10 leaq 16(%r15), %r12 29: 48 8b 2d 00 00 00 00 movq (%rip), %rbp # 30 <_L1+0x20> 000000000000002c: X86_64_RELOC_GOT_LOAD _vs_top@GOTPCREL 30: 48 8b 45 00 movq (%rbp), %rax 34: 48 8b 0d 00 00 00 00 movq (%rip), %rcx # 3b <_L1+0x2b> 0000000000000037: X86_64_RELOC_GOT_LOAD _vs_limit@GOTPCREL 3b: 48 3b 01 cmpq (%rcx), %rax 3e: 72 05 jb 0x45 <_L1+0x35> 40: e8 00 00 00 00 callq 0x45 <_L1+0x35> 0000000000000041: X86_64_RELOC_BRANCH _vs_overflow 45: 49 be 00 00 00 00 00 00 00 a0 movabsq $-6917529027641081856, %r14 4f: 4d 8b 2f movq (%r15), %r13 52: 4c 89 65 00 movq %r12, (%rbp) 56: 4d 85 ed testq %r13, %r13 59: 79 22 jns 0x7d <_L1+0x6d> 5b: 4d 39 f5 cmpq %r14, %r13 5e: 74 3a je 0x9a <_L1+0x8a> 60: 48 b8 00 00 00 00 00 00 00 80 movabsq $-9223372036854775808, %rax 6a: 4c 01 e8 addq %r13, %rax 6d: 48 83 c0 ff addq $-1, %rax 71: 48 c1 e8 3e shrq $62, %rax 75: 75 34 jne 0xab <_L1+0x9b> 77: 49 8d 45 ff leaq -1(%r13), %rax 7b: eb 45 jmp 0xc2 <_L1+0xb2> 7d: 4c 89 ef movq %r13, %rdi 80: 4c 89 f6 movq %r14, %rsi 83: e8 00 00 00 00 callq 0x88 <_L1+0x78> 0000000000000084: X86_64_RELOC_BRANCH _number_compare 88: 85 c0 testl %eax, %eax 8a: 74 0e je 0x9a <_L1+0x8a> 8c: 49 8d 76 01 leaq 1(%r14), %rsi 90: 4c 89 ef movq %r13, %rdi 93: e8 00 00 00 00 callq 0x98 <_L1+0x88> 0000000000000094: X86_64_RELOC_BRANCH _number_minus 98: eb 28 jmp 0xc2 <_L1+0xb2> 9a: 49 83 c6 01 addq $1, %r14 9e: 4d 89 77 08 movq %r14, 8(%r15) a2: 49 83 c7 08 addq $8, %r15 a6: e9 9b 01 00 00 jmp 0x246 <_L1+0x236> ab: 48 b8 00 00 00 00 00 00 00 60 movabsq $6917529027641081856, %rax b5: 4a 8d 3c 28 leaq (%rax,%r13), %rdi b9: 48 83 c7 ff addq $-1, %rdi bd: e8 00 00 00 00 callq 0xc2 <_L1+0xb2> 00000000000000be: X86_64_RELOC_BRANCH _make_fixnum1 c2: 49 89 47 08 movq %rax, 8(%r15) c6: 49 83 c7 08 addq $8, %r15 ca: 4c 89 3b movq %r15, (%rbx) cd: 4c 89 65 00 movq %r12, (%rbp) d1: 31 c0 xorl %eax, %eax d3: ff 15 00 00 00 00 callq *(%rip) # d9 <_L1+0xc9> // <--- recursive CALL 00000000000000d5: X86_64_RELOC_SIGNED _Lnk0 d9: 4c 89 65 00 movq %r12, (%rbp) dd: 48 8b 03 movq (%rbx), %rax e0: 48 8b 00 movq (%rax), %rax e3: 4c 85 e8 testq %r13, %rax e6: 78 10 js 0xf8 <_L1+0xe8> e8: 4c 89 ef movq %r13, %rdi eb: 48 89 c6 movq %rax, %rsi ee: e8 00 00 00 00 callq 0xf3 <_L1+0xe3> 00000000000000ef: X86_64_RELOC_BRANCH _number_times f3: e9 4b 01 00 00 jmp 0x243 <_L1+0x233> f8: 49 89 ea movq %rbp, %r10 fb: 49 89 d9 movq %rbx, %r9 fe: 48 bb 00 00 00 00 00 00 00 80 movabsq $-9223372036854775808, %rbx 108: 48 b9 00 00 00 00 00 00 00 60 movabsq $6917529027641081856, %rcx 112: 49 01 cd addq %rcx, %r13 115: 48 01 c8 addq %rcx, %rax 118: 4c 89 ed movq %r13, %rbp 11b: 48 f7 dd negq %rbp 11e: 49 0f 4c ed cmovlq %r13, %rbp 122: 48 89 c7 movq %rax, %rdi 125: 48 f7 df negq %rdi 128: 48 0f 4c f8 cmovlq %rax, %rdi 12c: 31 d2 xorl %edx, %edx 12e: 66 90 nop 130: 48 89 de movq %rbx, %rsi 133: 89 d1 movl %edx, %ecx 135: 48 d3 ee shrq %cl, %rsi 138: 48 85 ee testq %rbp, %rsi 13b: 75 4f jne 0x18c <_L1+0x17c> 13d: 8d 4a 01 leal 1(%rdx), %ecx 140: 48 89 de movq %rbx, %rsi 143: 48 d3 ee shrq %cl, %rsi 146: 48 85 ee testq %rbp, %rsi 149: 75 31 jne 0x17c <_L1+0x16c> 14b: 8d 4a 02 leal 2(%rdx), %ecx 14e: 48 89 de movq %rbx, %rsi 151: 48 d3 ee shrq %cl, %rsi 154: 48 85 ee testq %rbp, %rsi 157: 75 29 jne 0x182 <_L1+0x172> 159: 8d 4a 03 leal 3(%rdx), %ecx 15c: 48 89 de movq %rbx, %rsi 15f: 48 d3 ee shrq %cl, %rsi 162: 48 85 ee testq %rbp, %rsi 165: 75 21 jne 0x188 <_L1+0x178> 167: 48 83 c2 04 addq $4, %rdx 16b: 48 83 fa 40 cmpq $64, %rdx 16f: 75 bf jne 0x130 <_L1+0x120> 171: 41 b8 40 00 00 00 movl $64, %r8d 177: 4c 89 d5 movq %r10, %rbp 17a: eb 17 jmp 0x193 <_L1+0x183> 17c: 48 83 c2 01 addq $1, %rdx 180: eb 0a jmp 0x18c <_L1+0x17c> 182: 48 83 c2 02 addq $2, %rdx 186: eb 04 jmp 0x18c <_L1+0x17c> 188: 48 83 c2 03 addq $3, %rdx 18c: 4c 89 d5 movq %r10, %rbp 18f: 4c 0f be c2 movsbq %dl, %r8 193: 31 d2 xorl %edx, %edx 195: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:(%rax,%rax) 19f: 90 nop 1a0: 48 89 de movq %rbx, %rsi 1a3: 89 d1 movl %edx, %ecx 1a5: 48 d3 ee shrq %cl, %rsi 1a8: 48 85 fe testq %rdi, %rsi 1ab: 75 7b jne 0x228 <_L1+0x218> 1ad: 8d 4a 01 leal 1(%rdx), %ecx 1b0: 48 89 de movq %rbx, %rsi 1b3: 48 d3 ee shrq %cl, %rsi 1b6: 48 85 fe testq %rdi, %rsi 1b9: 75 5d jne 0x218 <_L1+0x208> 1bb: 8d 4a 02 leal 2(%rdx), %ecx 1be: 48 89 de movq %rbx, %rsi 1c1: 48 d3 ee shrq %cl, %rsi 1c4: 48 85 fe testq %rdi, %rsi 1c7: 75 55 jne 0x21e <_L1+0x20e> 1c9: 8d 4a 03 leal 3(%rdx), %ecx 1cc: 48 89 de movq %rbx, %rsi 1cf: 48 d3 ee shrq %cl, %rsi 1d2: 48 85 fe testq %rdi, %rsi 1d5: 75 4d jne 0x224 <_L1+0x214> 1d7: 48 83 c2 04 addq $4, %rdx 1db: 48 83 fa 40 cmpq $64, %rdx 1df: 75 bf jne 0x1a0 <_L1+0x190> 1e1: b9 40 00 00 00 movl $64, %ecx 1e6: 4c 89 cb movq %r9, %rbx 1e9: 4c 01 c1 addq %r8, %rcx 1ec: 48 83 f9 42 cmpq $66, %rcx 1f0: 72 46 jb 0x238 <_L1+0x228> 1f2: 49 0f af c5 imulq %r13, %rax 1f6: 48 b9 00 00 00 00 00 00 00 20 movabsq $2305843009213693952, %rcx 200: 48 01 c1 addq %rax, %rcx 203: 48 c1 e9 3e shrq $62, %rcx 207: 75 05 jne 0x20e <_L1+0x1fe> 209: 4c 01 f0 addq %r14, %rax 20c: eb 35 jmp 0x243 <_L1+0x233> 20e: 48 89 c7 movq %rax, %rdi 211: e8 00 00 00 00 callq 0x216 <_L1+0x206> 0000000000000212: X86_64_RELOC_BRANCH _make_fixnum1 216: eb 2b jmp 0x243 <_L1+0x233> 218: 48 83 c2 01 addq $1, %rdx 21c: eb 0a jmp 0x228 <_L1+0x218> 21e: 48 83 c2 02 addq $2, %rdx 222: eb 04 jmp 0x228 <_L1+0x218> 224: 48 83 c2 03 addq $3, %rdx 228: 4c 89 cb movq %r9, %rbx 22b: 48 0f be ca movsbq %dl, %rcx 22f: 4c 01 c1 addq %r8, %rcx 232: 48 83 f9 42 cmpq $66, %rcx 236: 73 ba jae 0x1f2 <_L1+0x1e2> 238: 4c 89 ef movq %r13, %rdi 23b: 48 89 c6 movq %rax, %rsi 23e: e8 00 00 00 00 callq 0x243 <_L1+0x233> 000000000000023f: X86_64_RELOC_BRANCH _fixnum_times 243: 49 89 07 movq %rax, (%r15) 246: 4c 89 3b movq %r15, (%rbx) 249: 4c 89 65 00 movq %r12, (%rbp) 24d: 48 83 c4 08 addq $8, %rsp 251: 5b popq %rbx 252: 41 5c popq %r12 254: 41 5d popq %r13 256: 41 5e popq %r14 258: 41 5f popq %r15 25a: 5d popq %rbp 25b: c3 retq 25c: 0f 1f 40 00 nopl (%rax) 0000000000000260 <_LnkT0>: // <--- This is a stub for calling FACT again, recursively 260: 48 8b 3d 00 00 00 00 movq (%rip), %rdi # 267 <_LnkT0+0x7> 0000000000000263: X86_64_RELOC_SIGNED _VVi 267: 48 8d 35 00 00 00 00 leaq (%rip), %rsi # 26e <_LnkT0+0xe> 000000000000026a: X86_64_RELOC_SIGNED _Lnk0 26e: e9 00 00 00 00 jmp 0x273 <_LnkT0+0x13> 000000000000026f: X86_64_RELOC_BRANCH _call_or_link 0 0 ;; Observe that all CALL instructions below lead to operations on integers: ;; comparison (_number_compare), multiplication (_number_times), subtraction (_number_minus). ;; There is no recursive call to FACT1, but there is a jump back to the loop. ;; For extra credit, you can figure out where and how the respective Lisp values of N ;; and ACC are stored. >(system "objdump -r -d facta.o | awk '/L1/{found=1} found' ") 0000000000000010 <_L1>: 10: 55 pushq %rbp 11: 41 57 pushq %r15 13: 41 56 pushq %r14 15: 41 55 pushq %r13 17: 41 54 pushq %r12 19: 53 pushq %rbx 1a: 48 83 ec 18 subq $24, %rsp 1e: 48 8b 05 00 00 00 00 movq (%rip), %rax # 25 <_L1+0x15> 0000000000000021: X86_64_RELOC_GOT_LOAD _vs_base@GOTPCREL 25: 4c 8b 38 movq (%rax), %r15 28: 49 8d 47 18 leaq 24(%r15), %rax 2c: 48 89 44 24 08 movq %rax, 8(%rsp) 31: 48 8b 05 00 00 00 00 movq (%rip), %rax # 38 <_L1+0x28> 0000000000000034: X86_64_RELOC_GOT_LOAD _vs_top@GOTPCREL 38: 48 8b 00 movq (%rax), %rax 3b: 48 8b 0d 00 00 00 00 movq (%rip), %rcx # 42 <_L1+0x32> 000000000000003e: X86_64_RELOC_GOT_LOAD _vs_limit@GOTPCREL 42: 48 3b 01 cmpq (%rcx), %rax 45: 72 05 jb 0x4c <_L1+0x3c> 47: e8 00 00 00 00 callq 0x4c <_L1+0x3c> 0000000000000048: X86_64_RELOC_BRANCH _vs_overflow 4c: 49 be 00 00 00 00 00 00 00 a0 movabsq $-6917529027641081856, %r14 56: 48 bd 00 00 00 00 00 00 00 80 movabsq $-9223372036854775808, %rbp 60: 49 bd 00 00 00 00 00 00 00 60 movabsq $6917529027641081856, %r13 6a: 49 8b 1f movq (%r15), %rbx 6d: 4c 89 7c 24 10 movq %r15, 16(%rsp) 72: 4d 8b 7f 08 movq 8(%r15), %r15 76: 48 8b 44 24 08 movq 8(%rsp), %rax 7b: 48 8b 0d 00 00 00 00 movq (%rip), %rcx # 82 <_L1+0x72> 000000000000007e: X86_64_RELOC_GOT_LOAD _vs_top@GOTPCREL 82: 48 89 01 movq %rax, (%rcx) 85: 48 85 db testq %rbx, %rbx 88: 79 76 jns 0x100 <_L1+0xf0> 8a: eb 1a jmp 0xa6 <_L1+0x96> 8c: 0f 1f 40 00 nopl (%rax) 90: 48 89 df movq %rbx, %rdi 93: 4c 89 fe movq %r15, %rsi 96: e8 00 00 00 00 callq 0x9b <_L1+0x8b> 0000000000000097: X86_64_RELOC_BRANCH _number_times 9b: 49 89 c7 movq %rax, %r15 // <--- top of the loop 9e: 4c 89 e3 movq %r12, %rbx a1: 48 85 db testq %rbx, %rbx a4: 79 5a jns 0x100 <_L1+0xf0> a6: 4c 39 f3 cmpq %r14, %rbx a9: 0f 84 d2 01 00 00 je 0x281 <_L1+0x271> af: 48 8d 04 2b leaq (%rbx,%rbp), %rax b3: 48 83 c0 ff addq $-1, %rax b7: 48 b9 00 00 00 00 00 00 00 40 movabsq $4611686018427387904, %rcx c1: 48 39 c8 cmpq %rcx, %rax c4: 73 1a jae 0xe0 <_L1+0xd0> c6: 4c 8d 63 ff leaq -1(%rbx), %r12 ca: 4c 85 fb testq %r15, %rbx cd: 79 c1 jns 0x90 <_L1+0x80> cf: eb 5a jmp 0x12b <_L1+0x11b> d1: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:(%rax,%rax) db: 0f 1f 44 00 00 nopl (%rax,%rax) e0: 4a 8d 3c 2b leaq (%rbx,%r13), %rdi e4: 48 83 c7 ff addq $-1, %rdi e8: e8 00 00 00 00 callq 0xed <_L1+0xdd> 00000000000000e9: X86_64_RELOC_BRANCH _make_fixnum1 ed: 49 89 c4 movq %rax, %r12 f0: 4c 85 fb testq %r15, %rbx f3: 79 9b jns 0x90 <_L1+0x80> f5: eb 34 jmp 0x12b <_L1+0x11b> f7: 66 0f 1f 84 00 00 00 00 00 nopw (%rax,%rax) 100: 48 89 df movq %rbx, %rdi 103: 4c 89 f6 movq %r14, %rsi 106: e8 00 00 00 00 callq 0x10b <_L1+0xfb> 0000000000000107: X86_64_RELOC_BRANCH _number_compare 10b: 85 c0 testl %eax, %eax 10d: 0f 84 6e 01 00 00 je 0x281 <_L1+0x271> 113: 49 8d 76 01 leaq 1(%r14), %rsi 117: 48 89 df movq %rbx, %rdi 11a: e8 00 00 00 00 callq 0x11f <_L1+0x10f> 000000000000011b: X86_64_RELOC_BRANCH _number_minus 11f: 49 89 c4 movq %rax, %r12 122: 4c 85 fb testq %r15, %rbx 125: 0f 89 65 ff ff ff jns 0x90 <_L1+0x80> 12b: 4c 01 eb addq %r13, %rbx 12e: 4d 01 ef addq %r13, %r15 131: 48 89 de movq %rbx, %rsi 134: 48 f7 de negq %rsi 137: 48 0f 4c f3 cmovlq %rbx, %rsi 13b: 4c 89 fa movq %r15, %rdx 13e: 48 f7 da negq %rdx 141: 49 0f 4c d7 cmovlq %r15, %rdx 145: 31 c0 xorl %eax, %eax 147: 66 0f 1f 84 00 00 00 00 00 nopw (%rax,%rax) 150: 48 89 ef movq %rbp, %rdi 153: 89 c1 movl %eax, %ecx 155: 48 d3 ef shrq %cl, %rdi 158: 48 85 f7 testq %rsi, %rdi 15b: 75 4b jne 0x1a8 <_L1+0x198> 15d: 8d 48 01 leal 1(%rax), %ecx 160: 48 89 ef movq %rbp, %rdi 163: 48 d3 ef shrq %cl, %rdi 166: 48 85 f7 testq %rsi, %rdi 169: 75 2d jne 0x198 <_L1+0x188> 16b: 8d 48 02 leal 2(%rax), %ecx 16e: 48 89 ef movq %rbp, %rdi 171: 48 d3 ef shrq %cl, %rdi 174: 48 85 f7 testq %rsi, %rdi 177: 75 25 jne 0x19e <_L1+0x18e> 179: 8d 48 03 leal 3(%rax), %ecx 17c: 48 89 ef movq %rbp, %rdi 17f: 48 d3 ef shrq %cl, %rdi 182: 48 85 f7 testq %rsi, %rdi 185: 75 1d jne 0x1a4 <_L1+0x194> 187: 48 83 c0 04 addq $4, %rax 18b: 48 83 f8 40 cmpq $64, %rax 18f: 75 bf jne 0x150 <_L1+0x140> 191: be 40 00 00 00 movl $64, %esi 196: eb 14 jmp 0x1ac <_L1+0x19c> 198: 48 83 c0 01 addq $1, %rax 19c: eb 0a jmp 0x1a8 <_L1+0x198> 19e: 48 83 c0 02 addq $2, %rax 1a2: eb 04 jmp 0x1a8 <_L1+0x198> 1a4: 48 83 c0 03 addq $3, %rax 1a8: 48 0f be f0 movsbq %al, %rsi 1ac: 31 c0 xorl %eax, %eax 1ae: 66 90 nop 1b0: 48 89 ef movq %rbp, %rdi 1b3: 89 c1 movl %eax, %ecx 1b5: 48 d3 ef shrq %cl, %rdi 1b8: 48 85 d7 testq %rdx, %rdi 1bb: 0f 85 a3 00 00 00 jne 0x264 <_L1+0x254> 1c1: 8d 48 01 leal 1(%rax), %ecx 1c4: 48 89 ef movq %rbp, %rdi 1c7: 48 d3 ef shrq %cl, %rdi 1ca: 48 85 d7 testq %rdx, %rdi 1cd: 0f 85 81 00 00 00 jne 0x254 <_L1+0x244> 1d3: 8d 48 02 leal 2(%rax), %ecx 1d6: 48 89 ef movq %rbp, %rdi 1d9: 48 d3 ef shrq %cl, %rdi 1dc: 48 85 d7 testq %rdx, %rdi 1df: 75 79 jne 0x25a <_L1+0x24a> 1e1: 8d 48 03 leal 3(%rax), %ecx 1e4: 48 89 ef movq %rbp, %rdi 1e7: 48 d3 ef shrq %cl, %rdi 1ea: 48 85 d7 testq %rdx, %rdi 1ed: 75 71 jne 0x260 <_L1+0x250> 1ef: 48 83 c0 04 addq $4, %rax 1f3: 48 83 f8 40 cmpq $64, %rax 1f7: 75 b7 jne 0x1b0 <_L1+0x1a0> 1f9: b8 40 00 00 00 movl $64, %eax 1fe: 48 01 f0 addq %rsi, %rax 201: 48 83 f8 42 cmpq $66, %rax 205: 72 6a jb 0x271 <_L1+0x261> 207: 66 0f 1f 84 00 00 00 00 00 nopw (%rax,%rax) 210: 49 0f af df imulq %r15, %rbx 214: 48 b8 00 00 00 00 00 00 00 20 movabsq $2305843009213693952, %rax 21e: 48 01 d8 addq %rbx, %rax 221: 48 b9 00 00 00 00 00 00 00 40 movabsq $4611686018427387904, %rcx 22b: 48 39 c8 cmpq %rcx, %rax 22e: 73 17 jae 0x247 <_L1+0x237> 230: 4c 01 f3 addq %r14, %rbx 233: 49 89 df movq %rbx, %r15 236: 4c 89 e3 movq %r12, %rbx 239: 48 85 db testq %rbx, %rbx 23c: 0f 89 be fe ff ff jns 0x100 <_L1+0xf0> 242: e9 5f fe ff ff jmp 0xa6 <_L1+0x96> 247: 48 89 df movq %rbx, %rdi 24a: e8 00 00 00 00 callq 0x24f <_L1+0x23f> 000000000000024b: X86_64_RELOC_BRANCH _make_fixnum1 24f: e9 47 fe ff ff jmp 0x9b <_L1+0x8b> 254: 48 83 c0 01 addq $1, %rax 258: eb 0a jmp 0x264 <_L1+0x254> 25a: 48 83 c0 02 addq $2, %rax 25e: eb 04 jmp 0x264 <_L1+0x254> 260: 48 83 c0 03 addq $3, %rax 264: 48 0f be c0 movsbq %al, %rax 268: 48 01 f0 addq %rsi, %rax 26b: 48 83 f8 42 cmpq $66, %rax 26f: 73 9f jae 0x210 <_L1+0x200> 271: 48 89 df movq %rbx, %rdi 274: 4c 89 fe movq %r15, %rsi 277: e8 00 00 00 00 callq 0x27c <_L1+0x26c> 0000000000000278: X86_64_RELOC_BRANCH _fixnum_times 27c: e9 1a fe ff ff jmp 0x9b <_L1+0x8b> // <-- just a JMP, loop created 281: 48 8b 4c 24 10 movq 16(%rsp), %rcx 286: 4c 89 79 10 movq %r15, 16(%rcx) 28a: 48 83 c1 10 addq $16, %rcx 28e: 48 8b 05 00 00 00 00 movq (%rip), %rax # 295 <_L1+0x285> 0000000000000291: X86_64_RELOC_GOT_LOAD _vs_base@GOTPCREL 295: 48 89 08 movq %rcx, (%rax) 298: 48 8b 44 24 08 movq 8(%rsp), %rax 29d: 48 8b 0d 00 00 00 00 movq (%rip), %rcx # 2a4 <_L1+0x294> 00000000000002a0: X86_64_RELOC_GOT_LOAD _vs_top@GOTPCREL 2a4: 48 89 01 movq %rax, (%rcx) 2a7: 48 83 c4 18 addq $24, %rsp 2ab: 5b popq %rbx 2ac: 41 5c popq %r12 2ae: 41 5d popq %r13 2b0: 41 5e popq %r14 2b2: 41 5f popq %r15 2b4: 5d popq %rbp 2b5: c3 retq 0 0 ;;; Read on for tracing the linked facta.o in LLDB, at ;;; https://cosc59.gitlab.io/lisp/lldb-attached-to-gcl.txt