Files
soothe2-re/notes_giant_fft.md
T

125 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<<iVar19)*0x10 + 0x3f & ~0x3f // size budget
```
## Stage iteration
```
iVar25 = 1 << iVar19
uVar3 = DAT_181c5e15c[log2] // 2nd factor table (stage exponent bundle)
iVar33 = 1 << (uVar3 - iVar2) // butterfly count/base
loop groups:
for (uVar26=1; ...) {
iVar44 = 1 << (iVar16+2); iVar32 = 1 << (iVar16+2 + dirflag)
if (iVar12 == log2) -> 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<<log2; if <9 return; else select radix2/3
for log2==4 || >10 -> 2 else 3; loops similarly, returns param_4 (plan tail).
- Return: puVar10 + (1<<log2)*2 (aligned plan tail after current stage block).
## Notes for reimplementation
- Exact ordering must be transcribed from the index algebra (the branches accumulate a
combination of ± vs complemented indices; XOR selects conjugate/negate for 2x recalc).
- The 7 giants are per-(data type, direct/inverse, maybe r2c/c2r) variants (identical
structure; different base-call FUN_18002e360 vs FUN_180023860).
- Stage kernels executed at runtime are the dispatched vector kernels (see roadmap),
driven from this plan's ordering; the transform itself therefore reproduces a
specific FFT: split-radix 2/4/8-style in-place butterflies with fused reorder.
## Twiddle source (DAT_182616800 = sin-table)
- 1024 doubles = exact sin(k*2*pi/1024) (double precision), first half-period 0..512 (rest garbage of neighboring data).
- Copied to scratch by FUN_180039b00( log2N, dst ): step lVar18 = 1 << (10 - log2N) for N<=1024.
- So for N = 2^m: twiddle angles sampled from table with stride 2^(10-m). For N>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<<log2N; count = N/4 (sar $2 after N/2 sar).
if log2N > 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.
### CORRECTION (2026-08-20b): the "final kernels" are vectorized ln(x), NOT FFT
The 6 addresses 0x1802a24c0..0x1802ce4a0 are the plugin's OWN vectorized **natural log**,
not split-radix FFT butterflies (verified by subagent numeric simulation: matches std::log
to float precision). The dispatch chain 0x535a70→…→[0x1826181d8] reaches the plugin's
runtime math-function table, not the FFT. Three ln variants live around 0x1802a24c0:
- 0x1802a24c0 = AVX2 float ln (minimax poly, range-reduction via 2/3 magic 0x3f2aaaab, ln2)
- 0x1802a2fc0 = scalar double ln (Cody-Waite table 128×3 + Taylor), slow path
- 0x1802a3260 = second ln variant (9-term, split ln2 hi/lo)
The earlier "own vectorized sin/cos" reading was WRONG — the constants (0.333366, 0.250047,
…, 0.693147=ln2, 2/3, 0.75) are the minimax coefficients of ln(1+x), not sin/cos.
Transcribed to `dsp/vlog.{hpp,cpp}` (namespace vlog, `log_f32(src,dst,n)`), check ALL OK
(max rel err 2.4e-7 vs std::log). This is the exact log used by level-path (logf·8.6859
for dB, log/gamma in LUT curve), so it feeds bit-exact level→dB.
### REAL FFT pieces (still to transcribe):
- stage kernels FUN_18000bfc0 / 18000c5e0 (butterfly) — see above
- plan generator FUN_18002f980 (split-radix index algebra)
- actual twiddle usage for small N via FUN_180039b00 sin-table
So P3 scope is unchanged (butterfly + plan gen), NOT the ln kernels.
## STATUS / scope
Full bit-exact FFT = plan generator (0x2f980 split-radix index algebra) + stage kernels
(0xbfc0/0xc5e0 butterfly) + twiddle. ln (vlog) now done. FFT butterfly + plan gen remain.