# FUN_18002f980 (giant) — recursive FFT plan/permuation generator Signature: `ulonglong *FUN_18002f980(plan*, log2, src, flags, depth, uVar2, scratch)` Role: builds the FFT stage plan (bit-reverse ordering + sign-flip masks), NOT a numeric executor (no FP math inside; all integer indexing). ## Factor split (top) ``` iVar2 = DAT_181c5e0fc[log2] // per-log2 factor (4/8/9/13... from static table) iVar19 = log2 - iVar2 // reduced log2 for subproblem if iVar19 < 0x12: pu = FUN_18002e360(iVar19, scr) // small-plan path (radix2/3 generator) else: pu = FUN_18002f980(plan, iVar19, scr, ..., depth+1) // recurse *(plan + 0x78 + depth*8) = pu // store sub-plan ptr per depth *(plan + 0x38) = param_6 // (entry stores callback/context) *(plan + 0x1c) = (1< radix-8 permute block local_900[k]=src[±idx*8] (and ^= DAT_181c5e1e0 for negative/reflected) processed 16 qwords = 8 complex pairs; groups x butterflies next stage: uVar26 <<= 2; iVar16 -= 2 // radix-4 sweep else branch radix-3: uVar26 <<= 3; iVar16 -= 3 ... } while (iVar12 < log2) outer while (uVar24 < (iVar25-1+iVar33)/iVar33) ``` ## Key masive consts - DAT_181c5e1e0 = sign-flip XOR mask (bit for im part) applied to mirrored indices. - DAT_181c5e0fc / 181c5e15c / 181c5e3fc = per-log2 factor tables (read as int, index log2). - Base small planner FUN_18002e360: iVar6=1<10 -> 2 else 3; loops similarly, returns param_4 (plan tail). - Return: puVar10 + (1<1024 separate (two-pass) branch (unresolved). - Stage kernels (FUN_18000bfc0/18000c5e0) consume these with 0x40-complex chunks, cplx-mul=180008440, acc=FUN_1800437c0/180044700. ## ============ UPDATE 2026-08-20 (P3: code extracted from rt snap, objdump) ============ Raw code extracted from /tmp/snap_rt.bin via handoff/extract_fft.py → /tmp/fft/*.bin. ### cplx_mul 0x180008440 = IN-PLACE ELEMENTWISE DOUBLE MULTIPLY (not complex!): signature (rcx=dst, rdx=src, r8d=count): dst[i] *= src[i] (double). kernel 0x18003fa20: scalar path mulsd, vector path mulpd (4-wide). Corrects the "cplx-mul=180008440" label — the complex multiply is done via SEPARATE re/im passes (interleaved layout), 0x8440 is the scalar elementwise multiply. ### twiddle loader FUN_180039b00 (log2N=ecx, dst=rdx): N = 1< 10: separate two-pass branch (0x180039dc9). else stride = 1 << (10-log2N); copy sin_table[0x182616800 + k*stride*8] -> dst[k], k=0..N/4-1. sin_table DAT_182616800 = sin(k·2π/1024) for k=0..256 (257 valid doubles, then garbage). => twiddle[k] = sin(k·stride·2π/1024), the quarter-period sample set. ### stage kernels FUN_18000bfc0 / 18000c5e0 (butterfly): prologue stack 0x470 (scratch 0x200×2 doubles). Per 0x40-complex chunk: 0x180140c40(src, scratch, scratch+0x200, 0x40) — load/format twiddle into scratch 0x8440(dst, scratch, 0x40) — dst *= scratch (elementwise) 0x8440(dst, scratch+0x200, 0x40) — dst *= scratch+0x200 then butterfly arithmetic + acc (FUN_1800437c0/044700). Full loop still to transcribe. ### dispatcher 0x535a70 → 0x140a10 (scalar) / 0x140a70 (vector) (per NOTES:166). Second-level dispatch: 0x140a10/140a70 read global index [0x1826159a0] (=4 at capture) then jump through tables: scalar 0x182617548 = {0x180141380, 0x1801413a0, ..., 0x1801414a0} (12 entries, [4]=0x180141400) vector 0x182617588 = {0x180141440, ..., 0x180141560} ([4]=0x1801414c0) These are the per-size/per-type stage kernels (0x1801413xx/1415xx family). ### FOURTH level — final kernels (runtime-filled pointer tables): 0x180141400 = jmp *[0x1826181d8]; the tables hold (at capture): 0x1826181d8=0x1802a24c0, 0x182618200=0x1802a4d80, 0x182618228=0x1802a7540, 0x182618250=0x1802b4540, 0x182618278=0x1802c1540, 0x1826182a0=0x1802ce4a0. These 6 are the final split-radix FFT kernels (stack frames 0x328/0x350/0x7c8/0x7c8/0x7f8/0xd48). - **CRITICAL for bit-exact**: kernel 0x1802a24c0 prologue sets MXCSR rounding mode (vstmxcsr/ vldmxcsr, round-to-nearest 0x1f80) AND x87 control word (fnstcw/fldcw) before the FP loop, then restores. So the transform runs under an explicitly-forced rounding mode. ### STATUS / scope Full bit-exact FFT = plan generator (0x2f980 split-radix index algebra) + runtime dispatch (3-level tables) + 6 final kernels (each 0x800-0x1200 bytes of vectorized split-radix butterfly) + twiddle. This is the P3 2-4 week body. Architecture fully mapped; transcription not yet done. ### Final kernel 0x1802a24c0 — own vectorized sin/cos, NOT twiddle table The big-N kernels compute trig ON THE FLY via an AVX polynomial (vfmadd231ps/213ps chain) with range reduction done by float-bit tricks (vpaddd/vpsubd/vpsrad $0x17 = 23-bit shift = exponent extract, vpcmpgtd, vblendvps). Polynomial constants (rodata, float, broadcast x8): 0x181f81fa0 = 0.333366 0x181f81fc0 = -0.250047 0x181f81fe0 = 0.198225 0x181f82000 = -0.164625 0x181f82020 = 0.169649 0x181f82040 = -0.151772 0x181f82100 = 0.666667 0x181f821c0 = 0.693147 (= ln 2) 0x181f822c0 = 0.75 (0.693147 = ln 2 ⇒ exp-based sin/cos, SVML-like vector math library.) => Small-N path uses the twiddle sin-table (0x39b00); big-N path uses this own polynomial. Transcribing this bit-exactly = reimplementing a vector math sin/cos (Cody-Waite + poly + exp), plus the split-radix butterfly + integer reorder — the multi-week P3 body.