P3.1: extract FFT code from rt snap (extract_fft.py); decode cplx_mul 0x8440 = in-place elementwise double mult, twiddle loader 0x39b00 stride copy, dispatcher 0x535a70->0x140a10/70->jumptable[0x1826159a0]=4; implement cplx_mul_scalar_inplace

This commit is contained in:
2026-08-20 02:25:59 +03:00
parent 22e4599e0a
commit 88422034f5
4 changed files with 101 additions and 0 deletions
+7
View File
@@ -3,6 +3,13 @@
namespace fft_stage {
// 0x180008440: dst[i] *= src[i] (double, in-place elementwise; kernel 0x18003fa20)
void cplx_mul_scalar_inplace(double* dst, const double* src, uint32_t n) {
for (uint32_t i = 0; i < n; i++) {
dst[i] *= src[i];
}
}
// cplx_mul — complex elementwise multiply: out = in1 * in2 (conjugated 2nd)
void cplx_mul(double* out, const double* in1, const double* in2, uint32_t n) {
for (uint32_t i = 0; i < n * 2; i += 2) {
+6
View File
@@ -4,6 +4,12 @@
namespace fft_stage {
// 0x180008440 (kernel 0x18003fa20): IN-PLACE elementwise double multiply
// dst[i] *= src[i]. NOT a complex multiply — the complex op is done via separate
// re/im passes on the interleaved layout.
void cplx_mul_scalar_inplace(double* dst, const double* src, uint32_t n);
// complex elementwise multiply (interleaved re,im): out = in1 * in2
void cplx_mul(double* out, const double* in1, const double* in2, uint32_t n);
void stage_complex(double* out, const double* in, const double* tw, uint32_t n);
void stage_double(double* out, const double* in, const double* tw, uint32_t n);
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""extract_fft.py — extract FFT-related code regions from the rt snap into raw bins."""
import struct, mmap, os
SNAP = '/tmp/snap_rt.bin'
OUT = '/tmp/fft/'
# (name, addr, size)
TARGETS = [
('cplx_mul_8440', 0x180008440, 0x80),
('cplx_mul_kernel_c440', 0x18000c440, 0x100),
('stage_bfc0', 0x18000bfc0, 0x600),
('stage_c5e0', 0x18000c5e0, 0x600),
('twiddle_loader_39b00', 0x180039b00, 0x400),
('plan_gen_2f980', 0x18002f980, 0x1200),
('dispatcher_535a70', 0x180535a70, 0x100),
('scalar_140a10', 0x180140a10, 0x200),
('vector_140a70', 0x180140a70, 0x200),
('fft_kernel_a5a0', 0x18001a5a0, 0x100),
]
def main():
os.makedirs(OUT, exist_ok=True)
fd = os.open(SNAP, os.O_RDONLY)
sz = os.fstat(fd).st_size
mm = mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
# parse region index once
regs = []
i = 0
while i + 16 <= sz:
lo, n = struct.unpack_from('<QQ', mm, i)
regs.append((lo, i + 16, n)) # (addr, data_off, size)
i += 16 + n
regs.sort()
def readabs(addr, n):
for lo, off, rn in regs:
if lo <= addr < lo + rn and addr - lo + n <= rn:
return mm[off + (addr - lo): off + (addr - lo) + n]
return None
for name, addr, n in TARGETS:
b = readabs(addr, n)
if b:
with open(os.path.join(OUT, name + '.bin'), 'wb') as f:
f.write(b)
print('%-28s 0x%x %d bytes OK' % (name, addr, len(b)))
else:
print('%-28s 0x%x MISSING' % (name, addr))
mm.close()
os.close(fd)
if __name__ == '__main__':
main()
+32
View File
@@ -55,3 +55,35 @@ outer while (uVar24 < (iVar25-1+iVar33)/iVar33)
- 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).