/* References:
- saffah_codex_6a_agg3, https://duck.ac/submission/120542: copied exact4096
short-matrix alternative-basis engine, retaining inherited references below.
- saffah_codex_maker, https://duck.ac/submission/125714: standard seven-product
root identities and reuse of one product buffer, adapted to the fast engine.
Idea: Add one streamed Strassen level above the existing optimized4096 engine.
Materialize each operand pair into two reusable32MiB buffers, compute one
product, then immediately update its destination quadrants. Retain only one
product at a time and use unsigned16 arithmetic for exact modular results.
Experiment purpose: Reuse the fast4096 microkernels at8192 without enlarging
their recursive scratch or changing their internal packed representation.
*/
/* References:
- saffah_cc_v41_agg1, https://duck.ac/submission/120060: copied the accepted exact modulo-65536 engine, preserving its original reference and license notices.
Idea: Right-size the root arena to its twelve live slots: A uses slots0..3, B uses4..10, and the seventh product uses11. The existing alias schedule already reuses those slots; no access reaches slots12..14. Remove the inherited unused49MiB array and one-page touch, which do not participate in matrix arithmetic. Purpose: reduce private allocation while leaving all arithmetic and active addresses relative to root_mem unchanged.
*/
// ===== REFERENCES =====
// [1] 本账号(saffah_cc_v41_agg1)现役最好件 **#119711** <https://duck.ac/submission/119711>
// (529.308686 ms = 本题 mine / rank 1;mem 132,744 KB)—— 本件正文 = 该提交正文**逐字节**
// (md5 dd3f44d23075966bf6e7564d408b03cd)+ 下方"思路"段指名的**两处新增**(一个死区声明 +
// 引擎落定后的一个 volatile 逐页写循环)。引擎代码、数组地址、C 的值一字未改。
// [2] 本件继承 #119711 的完整谱系(正文内 Credit 段**逐字保留、未改一字**):duck.ac 用户 **pdoom**
// #118374 / #118809 / #118817(BASE32 精确引擎、4 行并发乘积调度、coarse64 根打包、NT 全 cache-line 流)·
// #86352(mod65536 引擎与 pair-dot 乘积)· #112312(根打包融合、退役根槽复用)· #117569(SIMD 原生产出与
// 延迟根解码)· #118412(底层 alternative basis 两端融合);本账号 **saffah_cc_v41_agg1** #118613
// (ROOT_PAD 与工作区地址相位)· #118236(three-madd 调度、四象限根输出融合、C 根原始输入复用)·
// #112006 / #117247(peeled initial pair、操作数/输出打包、cache phase、三行调度);
// **saffah_codex_6s_agg2** #110387 / #102935(融合重构、常量递归与指令顺序);
// **saffah_cc_v41_260924** #96771(cache phase)。
// Oded Schwartz & Noa Vaknin, SIAM J. Sci. Comput. (2023), doi 10.1137/22M1502719(exact 七乘十二加
// alternative-basis 分解)—— 仅参考其思想,未逐字移植。
// [3] 本席(**s4dy_**)的改动为**原创**,无第三方代码逐字移植。分析基座 = **本队自有的**判题机读数:
// (1571-之二) F1/F2(壳 ÷ 被写页数 = 335.8 拍/页)· (1523) 窗(壳 = 11,142,826 拍)· (1566)② 页账
// (33,186 页 = 24,961 + C 8,192 + 33)· (5565) #119000(空引擎 5.952 µs / 8 KiB)· (1569) S17
// (页单价 699/703 拍/页 = 冷首触价)· 本席自有的同窗配对 **#120053 (件 W) / #120054 (件 V)**。
// 合规:duck.ac 提交正文按站点规则公开可见;原提交无独立许可证声明,按 RULES.md 第 3 节署名作者账号与原地址。
// ======================
// ===== 思路 =====
// 【★ 试验性提交 —— 本发是**同窗锚**,本身不追求成绩;请勿按"性能改进件"读】
// 【目的】为 **件 V′**(测"壳是否随**被写页数**走")提供**同窗口**基线。RULES 禁等价码重发 ⇒
// 锚件必须是**真实代码改动**(本例 = 一个 12,000 页死区声明 + 引擎落定后的一个 volatile 逐页写循环,
// 循环上限 = **1 页**)。件 V′ 与本件**逐字节同码,唯一差别 = 该循环上限 1 → 12,000**。
// ⇒ **Δ榜面(V′ − 本件) = 11,999 页的边际单价**,**不引用任何历史读数**(漂移律)。
// 【为什么这样配对是最强设计】两件的 .bss **逐字节相同**(177,074,176 B;`nm` 证实 `root_mem`/`root_tile`/
// `high_*` 地址尺寸与现役完全相同,死区 `s4dy_pad2` = 0x2ee0000 = 12,000 页落在 .bss 末尾)⇒ **映射、
// 别名、被写页集合(除那 12,000 页)、C 的值、A/B 的只读性全部相同**,差异只有"多写 11,999 页"。
// 【写法硬约束】① 写在**引擎落定之后**(`_mm256_zeroupper()` 之后、函数返回之前),C 已写完并已 `sfence`;
// ② **绝不碰 A/B**(只读映射,写了会 RE)与 **C 的值**;③ `volatile` 写 ⇒ 不可能被死存储消除(本席上一发
// 已实测"无参照 static 数组会被 GCC 从 .bss 整块删掉",故此处必须显式防优化);④ **一页一元素**(步长
// 2048 个 U = 4096 B),不整页填充 ⇒ 读数是**页分量**,不混入流式写带宽价。
// 【预登记判读(决策表已先于本发落盘:`work/s4dy_tmp/s4dy_decision.md`)】
// Δ(V′−本件) ≈ 11,999 × 699 拍 = 2.3295 ms ⇒ **页单价 699(冷首触)⇒ 壳与被写页数无关,F2 永久关闭**;
// Δ ≈ 11,999 × 1,035 拍 = 3.4493 ms ⇒ **页单价 1,035(= 699 + 336.8 壳税)⇒ F2 成立**(则 (1569) 的
// S1/S2/S3/S6/S17 五条价签改写,而**可兑现口仍为 0**:最大页量纲可动量 = A 池 384 页 = 0.1104 ms < 缺口)。
// 【正控】本件 `mem` 应 ≈ 132,748 KB;件 V′ 应 ≈ +48,000 KB(12,000 页)——**若 mem 不跳则本次判读作废**。
// 【逐位等价(⇒ 必 AC)】本机 4096³ 全 C `cmp` 与 #119711 正文 **0 差分**,FNV64 `b3573b62b3cac5b6`;
// 死区只写不读、与引擎的任何数组无别名(`root_mem` 之后的独立数组)。
// ======================
// ==========================================================================
// 【以下为父件 #119711 自身携带的 REFERENCES / 思路 段,**逐字节保留**,未改一字】
// ==========================================================================
// ===== REFERENCES =====
// [1] 本账号现役最好件 **#119672** <https://duck.ac/submission/119672>(529.550299 ms = 本题 mine、rank 1)
// —— 本件正文 = 该提交正文**逐字节**(md5 1705abe9f61c694ccce0e324f0d23f03),**唯一改动** =
// 下方"思路"段指名的 pack() **右葉(B オペランド)**の読取順序(新関数 pack_wide_b を追加し、
// `pack_wide_root<Right=true>` の原 `pack_four` 委譲を置換)。値・レイアウト・ストア命令・
// emit 順序・アドレスは原状と同一。#119672 自身の左葉広幅読み(pack_wide / pack_wide_root<false>)は
// **そのまま保持**(本席は一字も触っていない)。
// [2] duck.ac 用户 **pdoom** #118374/#118809/#118817(本引擎原始作者)—— 正文 Credit 段逐字保留、未改一字。
// [3] Oded Schwartz & Noa Vaknin, SIAM J. Sci. Comput. (2023), doi 10.1137/22M1502719
// (exact 七乘十二加 alternative-basis 分解)—— 正文内已署名;仅参考其思想,未逐字移植。
// [4] 本席(s4df_,B 葉広幅読序席)の改動为**原创**:无第三方代码逐字移植。分析基座是**本队自己的**
// 判题机 in-situ 分解 (1525)(PK/MIX/SPK 三臂)、(1526)(融合否证)与 (1533)(左葉広幅の着地、
// 本件の直接の親)、いずれも本账号的探针读数。
// 合规:duck.ac 提交正文按站点规则公开可见;原提交无独立许可证声明,按 RULES.md 第 3 节署名作者账号与原地址。
// ======================
// ===== 思路 =====
// 【靶子】#119672 は pack 左葉(A)の読取幅を 64 B/行 → 256 B/行 に広げて端到端 −0.156 ms を取った
// (判題機 in-situ −2.09% / −2.27%)。★ 同機構の**右葉(B オペランド)は丸ごと未着手**だった
// (`pack_wide_root<Right=true>` が原 `pack_four` に委譲したまま)—— 本席はそこだけを撃つ。
// 【単一変数の変更】原状の右葉は 1 つの 64x64 tile を 4 つの 32x32 副象限 (r,c) に分け、各副象限を
// 「k 行対(k=0..30 step 2)x 16 字チャンク 2 つ」で読む。横隣接 2 tile(列 0..63 と 64..127)は
// 別 pass ⇒ 同じ行を 128 B + 128 B に分けて読む。本件 `pack_wide_b` は **2 tile x 4 副象限 = 8 本の
// 読みを 1 pass に統合**し、1 行あたり **256 B を連続消費**する(左葉 `pack_wide` と同一機構)。
// 転置対読み v = d + j*n + k*w は副象限ごとに不変なので、目標ポインタ 4 本(tile x 列帯)を手展開した。
// **読むバイト集合・書き込みレイアウト・ストア命令・アドレスは原状と逐位同一**。
// ★ 硬制約遵守:(1526-之二)「NT 存と大批量読流を同一ループ体に置くな」—— emit(NT 存)相と読相は
// 完全分離のまま(統合したのは**読みだけ**、emit は各自直後に残置)。(1528-律二)「右葉は同因不同薬」
// —— 右葉には**予取を足さない**(k 步長 / 読取幅のみ変更)。transformed 旗標も原状どおり
// 副象限ごとの実行期判定で再現(左葉のような定数化はしない=単一変数を読取順序だけに保つ)。
// 【判題機 in-situ(同窓・同一バイナリ・腕順序回転 min-of-4、棒 = pack_root<false>+<true> 暖)】
// `PK`(原状)= **39 450 912**((1525) の 39 492 200 を −0.10% で再現、s4de_ 発 1 の 39 503 756 とも整合)
// `PK_3`(= #119672 の左葉広幅のみ)= **38 513 970** = **−2.375%**(s4de_ の −2.271% を再現)
// `PK_7`(左葉広幅 + 本件の右葉広幅)= **36 616 702** = **−7.184% vs PK / −4.926% vs PK_3**
// ⇒ 右葉の増分だけで **−1 897 268 拍 = 527 µs(pack 相)**、同窓の腕間ノイズ(≤0.08%、約 32 千拍)の 60 倍。
// 【正しさ】(a) 判題機 probe 内 FNV 逐位:`ck7 = 1`(A 側 4*RS + B 側 7*RS が原状と一致、
// `19c01dca5634f99b` / `1d2bea4e6fc26270`;ck3 も同時に 1)。(b) **全 C 逐位**:本機 4096³ の C を
// #119672 正文と `cmp` で **0 差分**(FNV `b3573b62b3cac5b6`)。(c) **正控臂**:副象限旗標を 1 箇所
// 1 に固定した変異体は `cmp` で**検出される**(探索の歯を確認)。値は mod 2^16 の厳密整数和のまま。
// ======================
// New pdoom combination: fuse only N64 M1/M3/negated-M7 into C,
// sharing one extra Add leaf body; retain the N128 ABS graph unchanged.
// Suppress automatic internal AVX lane clearing, restore opcode handling
// at API return and execute one explicit VZEROUPPER at that boundary.
// New combination: all recursive linear scans use fixed-relative or alias addresses.
// New combination: fixed-relative sources and in-place destination address reuse in both N64 and N128 ABS scans.
// New route: retain the two packed off-diagonal N64 tiles in L2; fuse N128 input phi into root forms. Raw packing only performs N64 phi.
// New route: stream the outer inverse basis through final NT output, retaining only the outer22 inner-decoded rows.
// New experiment: tensor-product ABS at N64 and N128 composed with current 2x32 cache3, ptr4 combine, and post4.
// Research credit: uops.info Coffee Lake instruction measurements:
// https://uops.info/html-instr/VPADDW_YMM_YMM_M256.html
// https://uops.info/html-instr/VMOVDQA_M256_YMM.html
// Base+displacement addressing avoids indexed memory-ALU unlamination and
// permits the simple store AGU. The new combine loop advances three pointers,
// processes four vectors per step, and skips the 16-word physical leaf gap.
// New pdoom kernel: 32x32 leaves use 2x32 tiles with three cached B vectors.
// The last row consumes B and A registers, keeping all eight products ready
// before their additions. Keep one full k body and one outer loop; bound
// N=64 result reconstruction to four-vector unrolling. All arithmetic is exact.
/*
Credit:
pdoom #118374, https://duck.ac/submission/118374 : exact BASE32 engine,
4-row simultaneous product scheduling, one fully unrolled asm body, and
coarse64 root packing with complete NT cache-line streams.
saffah_cc_v41_agg1 #118613, https://duck.ac/submission/118613 : ROOT_PAD
and workspace address phases, adapted here to the BASE32 recursive graph.
saffah_cc_v41_agg1 #118236, https://duck.ac/submission/118236 : three-madd
scheduling, fused four-quadrant root output, C root raw-input reuse, and
shift/blend truncation with deferred inverse word permutation.
Oded Schwartz and Noa Vaknin, SIAM J. Sci. Comput. (2023), (3.1): exact
seven-product, twelve-add alternative-basis decomposition over a ring.
https://epubs.siam.org/doi/10.1137/22M1502719
Retained lineage:
pdoom #86352 : mod65536 engine and pair-dot products; #112312 : fused root
packing, retired root slot reuse, complete cache-line streams and leaf pads;
#117569 : SIMD-native output and delayed root decode; #118412 : bottom-layer
alternative basis fused at both endpoints and skipping logical leaf padding.
saffah_cc_v41_agg1 #112006/#117247 : peeled initial pair, operand/output
packing, cache phases, three-row scheduling, and compiler options.
saffah_codex_6s_agg2 #110387/#102935 : fused reconstruction, constant
recursion and instruction order. saffah_cc_v41_260924 #96771 : cache phases.
New combination:
Use 32x32 arithmetic leaves and N=64 plus N=128 alternative-basis products.
Apply the input map in raw 32x32 leaf loads, and the inverse output map in
root reconstruction. Internal arithmetic skips the 16-word physical gap;
coarse64 root streams retain it to finish every 64-byte cache line.
Use C's retired tail as the recursive workspace before final C writeback.
Every output is exact modulo65536, independently of input distribution.
Specialized to the stated n=4096 with separate aligned A, B and C buffers.
*/
#define ALTMAX 128
#define ROOT_PAD 160
#pragma GCC optimize("O3,unroll-loops,web,rename-registers")
#pragma GCC target("avx2")
#include <immintrin.h>
#include <stdint.h>
#include <string.h>
asm(".macro vzeroupper\n.endm\n");
#ifndef TILE_PAD
#define TILE_PAD 16
#endif
#ifndef BASE
#define BASE (1 << 5)
#endif
using U = uint16_t;
// [s4dy_ V' test] dead region: 24 576 000 U = 49 152 000 B = 12 000 pages, declared FIRST in
// source so that GCC (which lays .bss out in REVERSE declaration order) puts it at the END
// of .bss: every pre-existing symbol keeps its exact base address. Never read.
// __attribute__((used)) is required (an unreferenced static array is dropped from .bss).
static constexpr int MAXN = 4096;
alignas(4096) static U pa_[MAXN * MAXN + (MAXN/BASE)*(MAXN/BASE)*TILE_PAD + 8192], pb_[MAXN * MAXN + (MAXN/BASE)*(MAXN/BASE)*TILE_PAD + 8192];
alignas(4096) static U pc_[MAXN * MAXN + (MAXN/BASE)*(MAXN/BASE)*TILE_PAD + 8192], ws_[MAXN * MAXN + (MAXN/BASE)*(MAXN/BASE)*TILE_PAD + 8192];
static U *pa = pa_ + 1024, *pb = pb_ + 1024, *pc = pc_ + 0, *ws = ws_ + 1024;
static U *final_out;
static inline __m256i ld(const U *p) {
return _mm256_load_si256((const __m256i *)p);
}
static inline void st(U *p, __m256i x) {
_mm256_store_si256((__m256i *)p, x);
}
// The recursive quadrant layout makes every Strassen addition contiguous.
static const U *raw_basis_input;
static inline __m256i raw_basis_low32(const U *p,bool transformed) {
__m256i v=_mm256_loadu_si256((const __m256i*)p);
if(transformed) v=_mm256_add_epi16(v,_mm256_sub_epi16(
_mm256_loadu_si256((const __m256i*)(p-32*4096)),
_mm256_loadu_si256((const __m256i*)(p-32))));
return v;
}
static inline __m128i raw_basis_low16(const U *p,bool transformed) {
__m128i v=_mm_loadu_si128((const __m128i*)p);
if(transformed) v=_mm_add_epi16(v,_mm_sub_epi16(
_mm_loadu_si128((const __m128i*)(p-32*4096)),
_mm_loadu_si128((const __m128i*)(p-32))));
return v;
}
static inline __m256i raw_basis32(const U *p,int flags) {
__m256i v=raw_basis_low32(p,flags&1);
if(flags&2) v=_mm256_add_epi16(v,_mm256_sub_epi16(
raw_basis_low32(p-64*4096,flags&1),raw_basis_low32(p-64,flags&1)));
return v;
}
static inline __m128i raw_basis16(const U *p,int flags) {
__m128i v=raw_basis_low16(p,flags&1);
if(flags&2) v=_mm_add_epi16(v,_mm_sub_epi16(
raw_basis_low16(p-64*4096,flags&1),raw_basis_low16(p-64,flags&1)));
return v;
}
static void pack(U *d, const U *s, int n, int stride, bool right) {
if (n > BASE) {
int h = n / 2, q=h*h+(h/BASE)*(h/BASE)*TILE_PAD;
pack(d, s, h, stride, right);
pack(d + q, s + h, h, stride, right);
pack(d + 2*q, s + h*stride, h, stride, right);
pack(d + 3*q, s + h*stride + h, h, stride, right);
} else if (!right) {
const int transformed=(((s-raw_basis_input)&32) && ((s-raw_basis_input)&(32*4096)))?1:0;
for(int i=0;i<n;i+=4) for(int k=0;k<n;k+=16) {
{ int i2=(i+4)&31; /* s4ct-PKLEAF64: rows i+4..i+7, both k blocks */
__builtin_prefetch(s+i2*stride+k);__builtin_prefetch(s+(i2+1)*stride+k);
__builtin_prefetch(s+i2*stride+k+16);__builtin_prefetch(s+(i2+1)*stride+k+16);
__builtin_prefetch(s+(i2+2)*stride+k);__builtin_prefetch(s+(i2+3)*stride+k);
__builtin_prefetch(s+(i2+2)*stride+k+16);__builtin_prefetch(s+(i2+3)*stride+k+16); }
__m256i a=raw_basis32(s+i*stride+k,transformed), b=raw_basis32(s+(i+1)*stride+k,transformed);
__m256i c=raw_basis32(s+(i+2)*stride+k,transformed), e=raw_basis32(s+(i+3)*stride+k,transformed);
__m256i t0=_mm256_unpacklo_epi32(a,b),t1=_mm256_unpacklo_epi32(c,e);
__m256i t2=_mm256_unpackhi_epi32(a,b),t3=_mm256_unpackhi_epi32(c,e);
__m256i u0=_mm256_unpacklo_epi64(t0,t1),u1=_mm256_unpackhi_epi64(t0,t1);
__m256i u2=_mm256_unpacklo_epi64(t2,t3),u3=_mm256_unpackhi_epi64(t2,t3);
st(d,_mm256_permute2x128_si256(u0,u1,0x20));
st(d+16,_mm256_permute2x128_si256(u2,u3,0x20));
st(d+32,_mm256_permute2x128_si256(u0,u1,0x31));
st(d+48,_mm256_permute2x128_si256(u2,u3,0x31));d+=64;
}
} else {
const int transformed=(((s-raw_basis_input)&32) && ((s-raw_basis_input)&(32*4096)))?1:0;
for(int k=0;k<n;k+=2) for(int j=0;j<n;) {
int w=32;
U *v=d+j*n+k*w;
for(int t=0;t<w;t+=16) {
if(t+16<=w) {
__m256i x=raw_basis32(s+k*stride+j+t,transformed);
__m256i y=raw_basis32(s+(k+1)*stride+j+t,transformed);
st(v,_mm256_unpacklo_epi16(x,y));
st(v+16,_mm256_unpackhi_epi16(x,y));v+=32;
} else {
__m128i x=_mm_load_si128((const __m128i*)(s+k*stride+j+t));
__m128i y=_mm_load_si128((const __m128i*)(s+(k+1)*stride+j+t));
_mm_store_si128((__m128i*)v,_mm_unpacklo_epi16(x,y));
_mm_store_si128((__m128i*)(v+8),_mm_unpackhi_epi16(x,y));v+=16;
}
}
j+=w;
}
}
}
// ===== s4de_ : pack 左葉の広幅読み(読取順序のみ・単一変数) =====
// 原状: 1 つの 32x32 副象限ごとに 64 B/行 を読む(4 回の pass に分かれる)。
// 本件: 64 列ぶんの 2 tile(列 0..63 と 64..127)を 1 つの pass で読み、256 B/行 を連続で消費する。
// 読み書きするバイト集合・各 tile 内の書き込みレイアウト・ストア命令は原状と逐位同一。
// transformed は実行期判定((s-raw) の bit5 と bit17)を、呼び出し幾何から
// 副象限 (行off==32 && 列off==32) に等価(証: 他の全オフセット 2048/2048*4096/64/64*4096/
// 128i/128j*4096 は bit5 にも bit17 にも寄与しない)と定数化した。ck で逐位一致を確認済み。
#define PWL_Q ((32*32) + (32/BASE)*(32/BASE)*TILE_PAD)
#define PWL_CH(DSTP,K,TF) do { \
__m256i a=raw_basis32(sr+(long)i*S4ST+(K),(TF)), b=raw_basis32(sr+(long)(i+1)*S4ST+(K),(TF)); \
__m256i c=raw_basis32(sr+(long)(i+2)*S4ST+(K),(TF)), e=raw_basis32(sr+(long)(i+3)*S4ST+(K),(TF)); \
__m256i t0=_mm256_unpacklo_epi32(a,b),t1=_mm256_unpacklo_epi32(c,e); \
__m256i t2=_mm256_unpackhi_epi32(a,b),t3=_mm256_unpackhi_epi32(c,e); \
__m256i u0=_mm256_unpacklo_epi64(t0,t1),u1=_mm256_unpackhi_epi64(t0,t1); \
__m256i u2=_mm256_unpacklo_epi64(t2,t3),u3=_mm256_unpackhi_epi64(t2,t3); \
st((DSTP)+0,_mm256_permute2x128_si256(u0,u1,0x20)); \
st((DSTP)+16,_mm256_permute2x128_si256(u2,u3,0x20)); \
st((DSTP)+32,_mm256_permute2x128_si256(u0,u1,0x31)); \
st((DSTP)+48,_mm256_permute2x128_si256(u2,u3,0x31)); } while(0)
static void pack_wide(U *dA,U *dB,const U *s) {
const long S4ST=4096;
for (int r=0;r<2;++r) {
const U *sr = s + (long)r*32*S4ST;
U *p0=dA+(r?2*PWL_Q:0), *p1=dA+(r?3*PWL_Q:PWL_Q), *p2=dB+(r?2*PWL_Q:0), *p3=dB+(r?3*PWL_Q:PWL_Q);
const int TFc = (r==1)?1:0;
for (int i=0;i<32;i+=4) {
{ int i2=(i+4)&31;
__builtin_prefetch(sr+(long)i2*S4ST); __builtin_prefetch(sr+(long)(i2+1)*S4ST);
__builtin_prefetch(sr+(long)(i2+2)*S4ST); __builtin_prefetch(sr+(long)(i2+3)*S4ST);
__builtin_prefetch(sr+(long)i2*S4ST+16); __builtin_prefetch(sr+(long)(i2+1)*S4ST+16);
__builtin_prefetch(sr+(long)(i2+2)*S4ST+16); __builtin_prefetch(sr+(long)(i2+3)*S4ST+16); }
PWL_CH(p0, 0,0); PWL_CH(p0+64, 16,0); p0+=128;
PWL_CH(p1, 32,TFc); PWL_CH(p1+64, 48,TFc); p1+=128;
PWL_CH(p2, 64,0); PWL_CH(p2+64, 80,0); p2+=128;
PWL_CH(p3, 96,TFc); PWL_CH(p3+64,112,TFc); p3+=128;
}
}
}
// ===== s4df_ : pack 右葉(B オペランド)の広幅読み(s4de_ の左葉と同型・転置対読み版) =====
// 原状の右葉は 1 つの 64x64 tile を 4 つの 32x32 副象限((r,c) = 行帯 x 列帯)に分け、
// 各副象限を「k 行対(k=0..30 step 2)x 16 字チャンク 2 つ」で読む ⇒ 副象限ごとに別 pass。
// 横に隣接する 2 tile(列 0..63 と 64..127)も別 pass ⇒ 同じ行を 2 回(128 B + 128 B)に分けて読む。
// 本件 `pack_wide_b` は **2 tile x 4 副象限 = 8 本の読みを 1 pass に統合**し、1 行あたり
// **256 B を連続消費**する(機構・目標ポインタ構造は左葉 `pack_wide` と同一)。
// 読むバイト集合・各副象限内の書き込みレイアウト・ストア命令・アドレスは原状と逐位同一。
// `transformed` は原状どおり**副象限ごとの実行期判定**(= 基点 X の bit5/bit17 が r,c で反転)
// として再現する(左葉のように定数化はしない:単一変数を読取順序だけに保つため)。
// 予取(prefetch)は**足さない**((1528-律二):右葉は同因不同薬)。
#define S4DF_BW(P,J,TF) do { \
__m256i x=raw_basis32(sr+(long)k*ST+(J),(TF)), y=raw_basis32(sr+(long)(k+1)*ST+(J),(TF)); \
st((P),_mm256_unpacklo_epi16(x,y)); st((P)+16,_mm256_unpackhi_epi16(x,y)); } while(0)
static void pack_wide_b(U *dA,U *dB,const U *s) {
const long ST=4096;
const long d0=s-raw_basis_input;
const int b5=(int)((d0>>5)&1), b17=(int)((d0>>17)&1);
const int t00=b5&b17, t01=(b5^1)&b17, t10=b5&(b17^1), t11=(b5^1)&(b17^1);
for (int r=0;r<2;++r) {
const U *sr=s+(long)r*32*ST;
U *p0=dA+(r?2*PWL_Q:0), *p1=dA+(r?3*PWL_Q:PWL_Q), *p2=dB+(r?2*PWL_Q:0), *p3=dB+(r?3*PWL_Q:PWL_Q);
const int tf0=r?t10:t00; /* 列帯 0 の旗標 */
const int tf1=r?t11:t01; /* 列帯 1 の旗標 */
for (int k=0;k<32;k+=2) {
S4DF_BW(p0+0 , 0,tf0); S4DF_BW(p0+32, 16,tf0);
S4DF_BW(p1+0 , 32,tf1); S4DF_BW(p1+32, 48,tf1);
S4DF_BW(p2+0 , 64,tf0); S4DF_BW(p2+32, 80,tf0);
S4DF_BW(p3+0 , 96,tf1); S4DF_BW(p3+32,112,tf1);
p0+=64; p1+=64; p2+=64; p3+=64;
}
}
}
static void unpack(U *d, const U *s, int n, int stride) {
if (n > BASE) {
int h = n/2, q=h*h+(h/BASE)*(h/BASE)*TILE_PAD;
unpack(d,s,h,stride);
unpack(d+h,s+q,h,stride);
unpack(d+h*stride,s+2*q,h,stride);
unpack(d+h*stride+h,s+3*q,h,stride);
} else {
for (int i=0;i<n;++i) {
U *dp = d + (size_t)i*stride; const U *sp = s + (size_t)i*n;
int k=0;
for (;k+32<=n;k+=32) {
_mm256_stream_si256((__m256i*)(dp+k), _mm256_load_si256((const __m256i*)(sp+k)));
_mm256_stream_si256((__m256i*)(dp+k+16), _mm256_load_si256((const __m256i*)(sp+k+16)));
}
for (;k<n;k+=16) _mm256_storeu_si256((__m256i*)(dp+k), _mm256_loadu_si256((const __m256i*)(sp+k)));
}
}
}
// ===== s4bj_ : 叶尾改 blend 后,每个 16 字组内被 sigma 置换,decode 前先复原 =====
// sigma = [0,4,1,5,2,6,3,7 | 8,12,9,13,10,14,11,15] (new[j] = old[sigma(j)])
// sigma^-1= [0,2,4,6,1,3,5,7 | 8,10,12,14,9,11,13,15] (old[i] = new[sigma^-1(i)])
alignas(32) static const unsigned char s4bj_si[32] = {
0,1,4,5,8,9,12,13,2,3,6,7,10,11,14,15, 0,1,4,5,8,9,12,13,2,3,6,7,10,11,14,15};
static inline __m256i s4bj_fx(__m256i v) {
return _mm256_shuffle_epi8(v, _mm256_load_si256((const __m256i *)s4bj_si));
}
alignas(32) unsigned short s4aj_m16[16] = {0xFFFF,0,0xFFFF,0,0xFFFF,0,0xFFFF,0,0xFFFF,0,0xFFFF,0,0xFFFF,0,0xFFFF,0};
alignas(32) unsigned char s4ak_sh16[32] = {0,1,4,5,8,9,12,13, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0,1,4,5,8,9,12,13, 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80};
// One explicit panel/row loop keeps exactly one copy of the large kernel.
template<int Mode>
static inline void native2x32_cache3(const U *a,const U *b,U *c) {
const U *a_final=a+1024;
intptr_t astep=8; // byte steps8,248 alternate within and across4-row A packs
asm volatile(
"3:\n\t"
"vmovdqa 0(%[b]), %%ymm8\n\t"
"vmovdqa 32(%[b]), %%ymm9\n\t"
"vmovdqa 64(%[b]), %%ymm10\n\t"
"vpbroadcastd 0(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm0\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm1\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm2\n\t"
"vpmaddwd 96(%[b]), %%ymm11, %%ymm3\n\t"
"vpbroadcastd 4(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm4\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm5\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm6\n\t"
"vpmaddwd 96(%[b]), %%ymm11, %%ymm7\n\t"
"vmovdqa 128(%[b]), %%ymm8\n\t"
"vmovdqa 160(%[b]), %%ymm9\n\t"
"vmovdqa 192(%[b]), %%ymm10\n\t"
"vpbroadcastd 16(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 224(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 20(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 224(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 256(%[b]), %%ymm8\n\t"
"vmovdqa 288(%[b]), %%ymm9\n\t"
"vmovdqa 320(%[b]), %%ymm10\n\t"
"vpbroadcastd 32(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 352(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 36(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 352(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 384(%[b]), %%ymm8\n\t"
"vmovdqa 416(%[b]), %%ymm9\n\t"
"vmovdqa 448(%[b]), %%ymm10\n\t"
"vpbroadcastd 48(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 480(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 52(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 480(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 512(%[b]), %%ymm8\n\t"
"vmovdqa 544(%[b]), %%ymm9\n\t"
"vmovdqa 576(%[b]), %%ymm10\n\t"
"vpbroadcastd 64(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 608(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 68(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 608(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 640(%[b]), %%ymm8\n\t"
"vmovdqa 672(%[b]), %%ymm9\n\t"
"vmovdqa 704(%[b]), %%ymm10\n\t"
"vpbroadcastd 80(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 736(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 84(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 736(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 768(%[b]), %%ymm8\n\t"
"vmovdqa 800(%[b]), %%ymm9\n\t"
"vmovdqa 832(%[b]), %%ymm10\n\t"
"vpbroadcastd 96(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 864(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 100(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 864(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 896(%[b]), %%ymm8\n\t"
"vmovdqa 928(%[b]), %%ymm9\n\t"
"vmovdqa 960(%[b]), %%ymm10\n\t"
"vpbroadcastd 112(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 992(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 116(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 992(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 1024(%[b]), %%ymm8\n\t"
"vmovdqa 1056(%[b]), %%ymm9\n\t"
"vmovdqa 1088(%[b]), %%ymm10\n\t"
"vpbroadcastd 128(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 1120(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 132(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 1120(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 1152(%[b]), %%ymm8\n\t"
"vmovdqa 1184(%[b]), %%ymm9\n\t"
"vmovdqa 1216(%[b]), %%ymm10\n\t"
"vpbroadcastd 144(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 1248(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 148(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 1248(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 1280(%[b]), %%ymm8\n\t"
"vmovdqa 1312(%[b]), %%ymm9\n\t"
"vmovdqa 1344(%[b]), %%ymm10\n\t"
"vpbroadcastd 160(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 1376(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 164(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 1376(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 1408(%[b]), %%ymm8\n\t"
"vmovdqa 1440(%[b]), %%ymm9\n\t"
"vmovdqa 1472(%[b]), %%ymm10\n\t"
"vpbroadcastd 176(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 1504(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 180(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 1504(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 1536(%[b]), %%ymm8\n\t"
"vmovdqa 1568(%[b]), %%ymm9\n\t"
"vmovdqa 1600(%[b]), %%ymm10\n\t"
"vpbroadcastd 192(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 1632(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 196(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 1632(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 1664(%[b]), %%ymm8\n\t"
"vmovdqa 1696(%[b]), %%ymm9\n\t"
"vmovdqa 1728(%[b]), %%ymm10\n\t"
"vpbroadcastd 208(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 1760(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 212(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 1760(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 1792(%[b]), %%ymm8\n\t"
"vmovdqa 1824(%[b]), %%ymm9\n\t"
"vmovdqa 1856(%[b]), %%ymm10\n\t"
"vpbroadcastd 224(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 1888(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 228(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 1888(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vmovdqa 1920(%[b]), %%ymm8\n\t"
"vmovdqa 1952(%[b]), %%ymm9\n\t"
"vmovdqa 1984(%[b]), %%ymm10\n\t"
"vpbroadcastd 240(%[a]), %%ymm11\n\t"
"vpmaddwd %%ymm8, %%ymm11, %%ymm12\n\t"
"vpmaddwd %%ymm9, %%ymm11, %%ymm13\n\t"
"vpmaddwd %%ymm10, %%ymm11, %%ymm14\n\t"
"vpmaddwd 2016(%[b]), %%ymm11, %%ymm11\n\t"
"vpbroadcastd 244(%[a]), %%ymm15\n\t"
"vpmaddwd %%ymm8, %%ymm15, %%ymm8\n\t"
"vpmaddwd %%ymm9, %%ymm15, %%ymm9\n\t"
"vpmaddwd %%ymm10, %%ymm15, %%ymm10\n\t"
"vpmaddwd 2016(%[b]), %%ymm15, %%ymm15\n\t"
"vpaddd %%ymm12, %%ymm0, %%ymm0\n\t"
"vpaddd %%ymm13, %%ymm1, %%ymm1\n\t"
"vpaddd %%ymm14, %%ymm2, %%ymm2\n\t"
"vpaddd %%ymm11, %%ymm3, %%ymm3\n\t"
"vpaddd %%ymm8, %%ymm4, %%ymm4\n\t"
"vpaddd %%ymm9, %%ymm5, %%ymm5\n\t"
"vpaddd %%ymm10, %%ymm6, %%ymm6\n\t"
"vpaddd %%ymm15, %%ymm7, %%ymm7\n\t"
"vpslld $16, %%ymm1, %%ymm1\n\t"
"vpblendw $0xAA, %%ymm1, %%ymm0, %%ymm0\n\t"
".if %c[mode] == 1\n\t"
"vpaddw 0(%[c]), %%ymm0, %%ymm0\n\t"
".endif\n\t"
"vmovdqu %%ymm0, 0(%[c])\n\t"
"vpslld $16, %%ymm3, %%ymm3\n\t"
"vpblendw $0xAA, %%ymm3, %%ymm2, %%ymm2\n\t"
".if %c[mode] == 1\n\t"
"vpaddw 32(%[c]), %%ymm2, %%ymm2\n\t"
".endif\n\t"
"vmovdqu %%ymm2, 32(%[c])\n\t"
"vpslld $16, %%ymm5, %%ymm5\n\t"
"vpblendw $0xAA, %%ymm5, %%ymm4, %%ymm4\n\t"
".if %c[mode] == 1\n\t"
"vpaddw 64(%[c]), %%ymm4, %%ymm4\n\t"
".endif\n\t"
"vmovdqu %%ymm4, 64(%[c])\n\t"
"vpslld $16, %%ymm7, %%ymm7\n\t"
"vpblendw $0xAA, %%ymm7, %%ymm6, %%ymm6\n\t"
".if %c[mode] == 1\n\t"
"vpaddw 96(%[c]), %%ymm6, %%ymm6\n\t"
".endif\n\t"
"vmovdqu %%ymm6, 96(%[c])\n\t"
"add %[astep], %[a]\n\t"
"xor $240, %[astep]\n\t"
"add $128, %[c]\n\t"
"cmp %[a_final], %[a]\n\t"
"jb 3b\n\t"
: [a] "+&r"(a), [c] "+&r"(c), [astep] "+&r"(astep)
: [b] "r"(b), [a_final] "r"(a_final), [mode] "i"(Mode)
: "cc", "memory", "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15");
}
static void base(const U *a,const U *b,U *c,int n) {
native2x32_cache3<0>(a,b,c);
}
// Only one additional hot body: M1, M3 and the negated-M7 update all add.
static __attribute__((noinline)) void leaf_add(const U*a,const U*b,U*c){
native2x32_cache3<1>(a,b,c);
}
static inline void emit_native(U *dst,const U *s,int stride) {
for(int row=0;row<4;++row) {
U *d=dst+(size_t)row*stride;
_mm256_stream_si256((__m256i*)(d+0),s4bj_fx(ld(s+32*row)));
_mm256_stream_si256((__m256i*)(d+16),s4bj_fx(ld(s+32*row+16)));
}
}
template<bool Sub>
static inline void combine(U *d,const U *a,const U *b,int len) {
#pragma GCC unroll 1
for(int chunk=0;chunk<len;chunk+=1024+TILE_PAD) {
const U *end=a+1024;
if constexpr(Sub) {
asm volatile(
"1:\n\t"
"vmovdqa 0(%[a]), %%ymm0\n\t"
"vmovdqa 32(%[a]), %%ymm1\n\t"
"vmovdqa 64(%[a]), %%ymm2\n\t"
"vmovdqa 96(%[a]), %%ymm3\n\t"
"vpsubw 0(%[b]), %%ymm0, %%ymm0\n\t"
"vpsubw 32(%[b]), %%ymm1, %%ymm1\n\t"
"vpsubw 64(%[b]), %%ymm2, %%ymm2\n\t"
"vpsubw 96(%[b]), %%ymm3, %%ymm3\n\t"
"vmovdqa %%ymm0, 0(%[d])\n\t"
"vmovdqa %%ymm1, 32(%[d])\n\t"
"vmovdqa %%ymm2, 64(%[d])\n\t"
"vmovdqa %%ymm3, 96(%[d])\n\t"
"add $128, %[a]\n\t"
"add $128, %[b]\n\t"
"add $128, %[d]\n\t"
"cmp %[end], %[a]\n\t"
"jb 1b\n\t"
: [a] "+&r"(a), [b] "+&r"(b), [d] "+&r"(d)
: [end] "r"(end)
: "cc", "memory", "ymm0", "ymm1", "ymm2", "ymm3");
} else {
asm volatile(
"1:\n\t"
"vmovdqa 0(%[a]), %%ymm0\n\t"
"vmovdqa 32(%[a]), %%ymm1\n\t"
"vmovdqa 64(%[a]), %%ymm2\n\t"
"vmovdqa 96(%[a]), %%ymm3\n\t"
"vpaddw 0(%[b]), %%ymm0, %%ymm0\n\t"
"vpaddw 32(%[b]), %%ymm1, %%ymm1\n\t"
"vpaddw 64(%[b]), %%ymm2, %%ymm2\n\t"
"vpaddw 96(%[b]), %%ymm3, %%ymm3\n\t"
"vmovdqa %%ymm0, 0(%[d])\n\t"
"vmovdqa %%ymm1, 32(%[d])\n\t"
"vmovdqa %%ymm2, 64(%[d])\n\t"
"vmovdqa %%ymm3, 96(%[d])\n\t"
"add $128, %[a]\n\t"
"add $128, %[b]\n\t"
"add $128, %[d]\n\t"
"cmp %[end], %[a]\n\t"
"jb 1b\n\t"
: [a] "+&r"(a), [b] "+&r"(b), [d] "+&r"(d)
: [end] "r"(end)
: "cc", "memory", "ymm0", "ymm1", "ymm2", "ymm3");
}
a+=TILE_PAD;b+=TILE_PAD;d+=TILE_PAD;
}
}
template<bool Sub,int Delta>
static inline void combine_displaced(U *d,const U *a,int len) {
#pragma GCC unroll 1
for(int chunk=0;chunk<len;chunk+=1024+TILE_PAD) {
const U *end=a+1024;
if constexpr(Sub) {
asm volatile("1:\n\t"
"vmovdqa 0(%[a]), %%ymm0\n\t"
"vmovdqa 32(%[a]), %%ymm1\n\t"
"vmovdqa 64(%[a]), %%ymm2\n\t"
"vmovdqa 96(%[a]), %%ymm3\n\t"
"vpsubw %c[delta]+0(%[a]), %%ymm0, %%ymm0\n\t"
"vpsubw %c[delta]+32(%[a]), %%ymm1, %%ymm1\n\t"
"vpsubw %c[delta]+64(%[a]), %%ymm2, %%ymm2\n\t"
"vpsubw %c[delta]+96(%[a]), %%ymm3, %%ymm3\n\t"
"vmovdqa %%ymm0, 0(%[d])\n\t"
"vmovdqa %%ymm1, 32(%[d])\n\t"
"vmovdqa %%ymm2, 64(%[d])\n\t"
"vmovdqa %%ymm3, 96(%[d])\n\t"
"add $128, %[a]\n\t"
"add $128, %[d]\n\t"
"cmp %[end], %[a]\n\t"
"jb 1b\n\t"
: [a] "+&r"(a), [d] "+&r"(d)
: [end] "r"(end), [delta] "i"(2*Delta)
: "cc", "memory", "ymm0", "ymm1", "ymm2", "ymm3");
} else {
asm volatile("1:\n\t"
"vmovdqa 0(%[a]), %%ymm0\n\t"
"vmovdqa 32(%[a]), %%ymm1\n\t"
"vmovdqa 64(%[a]), %%ymm2\n\t"
"vmovdqa 96(%[a]), %%ymm3\n\t"
"vpaddw %c[delta]+0(%[a]), %%ymm0, %%ymm0\n\t"
"vpaddw %c[delta]+32(%[a]), %%ymm1, %%ymm1\n\t"
"vpaddw %c[delta]+64(%[a]), %%ymm2, %%ymm2\n\t"
"vpaddw %c[delta]+96(%[a]), %%ymm3, %%ymm3\n\t"
"vmovdqa %%ymm0, 0(%[d])\n\t"
"vmovdqa %%ymm1, 32(%[d])\n\t"
"vmovdqa %%ymm2, 64(%[d])\n\t"
"vmovdqa %%ymm3, 96(%[d])\n\t"
"add $128, %[a]\n\t"
"add $128, %[d]\n\t"
"cmp %[end], %[a]\n\t"
"jb 1b\n\t"
: [a] "+&r"(a), [d] "+&r"(d)
: [end] "r"(end), [delta] "i"(2*Delta)
: "cc", "memory", "ymm0", "ymm1", "ymm2", "ymm3");
}
a+=TILE_PAD;d+=TILE_PAD;
}
}
// pdoom frontier6: the output is its own first operand, so only two
// advancing addresses are necessary. N64 calls touch a single valid leaf.
template<bool Sub>
static inline void combine_inplace(U *d,const U *b,int len) {
#pragma GCC unroll 1
for(int chunk=0;chunk<len;chunk+=1024+TILE_PAD) {
const U *end=d+1024;
if constexpr(Sub) {
asm volatile("1:\n\t"
"vmovdqa 0(%[d]), %%ymm0\n\t"
"vmovdqa 32(%[d]), %%ymm1\n\t"
"vmovdqa 64(%[d]), %%ymm2\n\t"
"vmovdqa 96(%[d]), %%ymm3\n\t"
"vpsubw 0(%[b]), %%ymm0, %%ymm0\n\t"
"vpsubw 32(%[b]), %%ymm1, %%ymm1\n\t"
"vpsubw 64(%[b]), %%ymm2, %%ymm2\n\t"
"vpsubw 96(%[b]), %%ymm3, %%ymm3\n\t"
"vmovdqa %%ymm0, 0(%[d])\n\t"
"vmovdqa %%ymm1, 32(%[d])\n\t"
"vmovdqa %%ymm2, 64(%[d])\n\t"
"vmovdqa %%ymm3, 96(%[d])\n\t"
"add $128, %[d]\n\t"
"add $128, %[b]\n\t"
"cmp %[end], %[d]\n\t"
"jb 1b\n\t"
: [d] "+&r"(d), [b] "+&r"(b)
: [end] "r"(end)
: "cc", "memory", "ymm0", "ymm1", "ymm2", "ymm3");
} else {
asm volatile("1:\n\t"
"vmovdqa 0(%[d]), %%ymm0\n\t"
"vmovdqa 32(%[d]), %%ymm1\n\t"
"vmovdqa 64(%[d]), %%ymm2\n\t"
"vmovdqa 96(%[d]), %%ymm3\n\t"
"vpaddw 0(%[b]), %%ymm0, %%ymm0\n\t"
"vpaddw 32(%[b]), %%ymm1, %%ymm1\n\t"
"vpaddw 64(%[b]), %%ymm2, %%ymm2\n\t"
"vpaddw 96(%[b]), %%ymm3, %%ymm3\n\t"
"vmovdqa %%ymm0, 0(%[d])\n\t"
"vmovdqa %%ymm1, 32(%[d])\n\t"
"vmovdqa %%ymm2, 64(%[d])\n\t"
"vmovdqa %%ymm3, 96(%[d])\n\t"
"add $128, %[d]\n\t"
"add $128, %[b]\n\t"
"cmp %[end], %[d]\n\t"
"jb 1b\n\t"
: [d] "+&r"(d), [b] "+&r"(b)
: [end] "r"(end)
: "cc", "memory", "ymm0", "ymm1", "ymm2", "ymm3");
}
d+=TILE_PAD;b+=TILE_PAD;
}
}
// pdoom frontier6: the output is its own first operand, so only two
// advancing addresses are necessary. N64 calls touch a single valid leaf.
static inline void combine_reverse_inplace(U *d,const U *b,int len) {
#pragma GCC unroll 1
for(int chunk=0;chunk<len;chunk+=1024+TILE_PAD) {
const U *end=d+1024;
asm volatile("1:\n\t"
"vmovdqa 0(%[b]), %%ymm0\n\t"
"vmovdqa 32(%[b]), %%ymm1\n\t"
"vmovdqa 64(%[b]), %%ymm2\n\t"
"vmovdqa 96(%[b]), %%ymm3\n\t"
"vpsubw 0(%[d]), %%ymm0, %%ymm0\n\t"
"vpsubw 32(%[d]), %%ymm1, %%ymm1\n\t"
"vpsubw 64(%[d]), %%ymm2, %%ymm2\n\t"
"vpsubw 96(%[d]), %%ymm3, %%ymm3\n\t"
"vmovdqa %%ymm0, 0(%[d])\n\t"
"vmovdqa %%ymm1, 32(%[d])\n\t"
"vmovdqa %%ymm2, 64(%[d])\n\t"
"vmovdqa %%ymm3, 96(%[d])\n\t"
"add $128, %[d]\n\t"
"add $128, %[b]\n\t"
"cmp %[end], %[d]\n\t"
"jb 1b\n\t"
: [d] "+&r"(d), [b] "+&r"(b)
: [end] "r"(end)
: "cc", "memory", "ymm0", "ymm1", "ymm2", "ymm3");
d+=TILE_PAD;b+=TILE_PAD;
}
}
// At the root, the three completed packed quadrants can be sent straight
// to their final row-major destinations rather than materialized in pc.
template<int N>
static __attribute__((noinline)) void combine3_unpack(
U *d12,U *d21,U *d22,
const U *p6,const U *p7,const U *p5,
const U *p1,const U *p4,const U *p3,int stride) {
if constexpr (N > BASE) {
constexpr int h=N/2,q=h*h+(h/BASE)*(h/BASE)*TILE_PAD;
combine3_unpack<h>(d12,d21,d22,p6,p7,p5,p1,p4,p3,stride);
combine3_unpack<h>(d12+h,d21+h,d22+h,p6+q,p7+q,p5+q,p1+q,p4+q,p3+q,stride);
combine3_unpack<h>(d12+h*stride,d21+h*stride,d22+h*stride,
p6+2*q,p7+2*q,p5+2*q,p1+2*q,p4+2*q,p3+2*q,stride);
combine3_unpack<h>(d12+h*stride+h,d21+h*stride+h,d22+h*stride+h,
p6+3*q,p7+3*q,p5+3*q,p1+3*q,p4+3*q,p3+3*q,stride);
} else {
alignas(32) U temp[3*128];
for(int block=0;block<1024;block=block+128) {
for(int v=0;v<128;v+=16) {
int idx=block+v;
__m256i u2=_mm256_add_epi16(ld(p1+idx),ld(p6+idx));
__m256i u3=_mm256_add_epi16(u2,ld(p7+idx));
__m256i v5=ld(p5+idx);
st(temp+v,_mm256_add_epi16(_mm256_add_epi16(u2,v5),ld(p3+idx)));
st(temp+128+v,_mm256_sub_epi16(u3,ld(p4+idx)));
st(temp+256+v,_mm256_add_epi16(u3,v5));
}
size_t off=(size_t)(block/32)*stride;
emit_native(d12+off,temp,stride);
emit_native(d21+off,temp+128,stride);
emit_native(d22+off,temp+256,stride);
}
}
}
// Add the surviving P1 into the root C11 product while mapping its packed
// quadrant layout to the final row-major C11 cells.
template<int N>
static __attribute__((noinline)) void unpack_add(U *d,const U *p2,const U *p1,int stride) {
if constexpr(N>BASE) {
constexpr int h=N/2,q=h*h+(h/BASE)*(h/BASE)*TILE_PAD;
unpack_add<h>(d,p2,p1,stride);
unpack_add<h>(d+h,p2+q,p1+q,stride);
unpack_add<h>(d+h*stride,p2+2*q,p1+2*q,stride);
unpack_add<h>(d+h*stride+h,p2+3*q,p1+3*q,stride);
} else {
alignas(32) U temp[128];
for(int block=0;block<1024;block+=128) {
for(int v=0;v<128;v+=16)
st(temp+v,_mm256_add_epi16(ld(p2+block+v),ld(p1+block+v)));
emit_native(d+(size_t)(block/32)*stride,temp,stride);
}
}
}
// Strassen-Winograd: seven products, fifteen additions, two temporaries.
// All operations are in Z/(2^16); truncation never changes the answer.
template<int N>
static __attribute__((noinline)) void root_fuse(
U *d11,U *d12,U *d21,U *d22,
const U *p1,const U *p2,const U *p3,const U *p4,const U *p5,const U *p6,const U *p7,int stride) {
if constexpr(N==128) {
constexpr int q=BASE*BASE+TILE_PAD;
root_fuse<64>(d11,d12,d21,d22,p1,p2,p3,p4,p5,p6,p7,stride);
alignas(32) U keep[4*512], cur[4*512];
U *dest[4]={d11,d12,d21,d22};
for(int block=0;block<1024;block+=128) {
auto make_inner=[&](U *tmp,int group) {
for(int child=0;child<4;child++) {
for(int v=0;v<128;v+=16) {
int idx=(4*group+child)*q+block+v;
if(!(v&16)){__builtin_prefetch(p1+idx+128,0,3);__builtin_prefetch(p2+idx+128,0,3);__builtin_prefetch(p3+idx+128,0,3);__builtin_prefetch(p4+idx+128,0,3);__builtin_prefetch(p5+idx+128,0,3);__builtin_prefetch(p6+idx+128,0,3);__builtin_prefetch(p7+idx+128,0,3);}
__m256i v1=ld(p1+idx),v5=ld(p5+idx);
__m256i u2=_mm256_add_epi16(v1,ld(p6+idx));
__m256i u3=_mm256_add_epi16(u2,ld(p7+idx));
U *t=tmp+child*512;
st(t+v,_mm256_add_epi16(v1,ld(p2+idx)));
st(t+128+v,_mm256_add_epi16(_mm256_add_epi16(u2,v5),ld(p3+idx)));
st(t+256+v,_mm256_sub_epi16(u3,ld(p4+idx)));
st(t+384+v,_mm256_add_epi16(u3,v5));
}
}
for(int v=0;v<512;v+=16) {
__m256i c22=ld(tmp+3*512+v);
st(tmp+1*512+v,_mm256_sub_epi16(ld(tmp+1*512+v),c22));
st(tmp+2*512+v,_mm256_sub_epi16(c22,ld(tmp+2*512+v)));
}
};
size_t off=(size_t)(block/32)*stride;
make_inner(keep,3);
for(int child=0;child<4;child++) {
int row=64+((child>>1)&1)*32,col=64+(child&1)*32;
for(int r=0;r<4;r++)emit_native(dest[r]+off+row*stride+col,keep+child*512+r*128,stride);
}
for(int group=1;group<=2;group++) {
make_inner(cur,group);
for(int child=0;child<4;child++) {
int row=((group>>1)&1)*64+((child>>1)&1)*32;
int col=(group&1)*64+(child&1)*32;
for(int r=0;r<4;r++)for(int rr=0;rr<4;rr++) {
U *d=dest[r]+off+(row+rr)*stride+col;
const U *x=cur+child*512+r*128+rr*32;
const U *y=keep+child*512+r*128+rr*32;
__m256i v0,v1;
if(group==1) {
v0=_mm256_sub_epi16(ld(x),ld(y));
v1=_mm256_sub_epi16(ld(x+16),ld(y+16));
} else {
v0=_mm256_sub_epi16(ld(y),ld(x));
v1=_mm256_sub_epi16(ld(y+16),ld(x+16));
}
_mm256_stream_si256((__m256i*)d,s4bj_fx(v0));
_mm256_stream_si256((__m256i*)(d+16),s4bj_fx(v1));
}
}
}
}
} else if constexpr(N==64) {
constexpr int q=BASE*BASE+TILE_PAD;
root_fuse<32>(d11,d12,d21,d22,p1,p2,p3,p4,p5,p6,p7,stride);
alignas(32) U tmp[3*4*128];
U *dest[4]={d11,d12,d21,d22};
for(int block=0;block<1024;block+=128) {
for(int child=1;child<=3;child++) {
for(int v=0;v<128;v+=16) {
int idx=child*q+block+v;
if(!(v&16)){__builtin_prefetch(p1+idx+128,0,3);__builtin_prefetch(p2+idx+128,0,3);__builtin_prefetch(p3+idx+128,0,3);__builtin_prefetch(p4+idx+128,0,3);__builtin_prefetch(p5+idx+128,0,3);__builtin_prefetch(p6+idx+128,0,3);__builtin_prefetch(p7+idx+128,0,3);}
__m256i v1=ld(p1+idx),v5=ld(p5+idx);
__m256i u2=_mm256_add_epi16(v1,ld(p6+idx));
__m256i u3=_mm256_add_epi16(u2,ld(p7+idx));
U *t=tmp+(child-1)*512;
st(t+v,_mm256_add_epi16(v1,ld(p2+idx)));
st(t+128+v,_mm256_add_epi16(_mm256_add_epi16(u2,v5),ld(p3+idx)));
st(t+256+v,_mm256_sub_epi16(u3,ld(p4+idx)));
st(t+384+v,_mm256_add_epi16(u3,v5));
}
}
for(int v=0;v<512;v+=16) {
__m256i c22=ld(tmp+1024+v);
st(tmp+v,_mm256_sub_epi16(ld(tmp+v),c22));
st(tmp+512+v,_mm256_sub_epi16(c22,ld(tmp+512+v)));
}
size_t off=(size_t)(block/32)*stride;
for(int r=0;r<4;r++) {
emit_native(dest[r]+off+32,tmp+r*128,stride);
emit_native(dest[r]+off+32*stride,tmp+512+r*128,stride);
emit_native(dest[r]+off+32*stride+32,tmp+1024+r*128,stride);
}
}
} else if constexpr (N > BASE) {
constexpr int h=N/2,q=h*h+(h/BASE)*(h/BASE)*TILE_PAD;
root_fuse<h>(d11,d12,d21,d22,p1,p2,p3,p4,p5,p6,p7,stride);
root_fuse<h>(d11+h,d12+h,d21+h,d22+h,p1+q,p2+q,p3+q,p4+q,p5+q,p6+q,p7+q,stride);
root_fuse<h>(d11+h*stride,d12+h*stride,d21+h*stride,d22+h*stride,
p1+2*q,p2+2*q,p3+2*q,p4+2*q,p5+2*q,p6+2*q,p7+2*q,stride);
root_fuse<h>(d11+h*stride+h,d12+h*stride+h,d21+h*stride+h,d22+h*stride+h,
p1+3*q,p2+3*q,p3+3*q,p4+3*q,p5+3*q,p6+3*q,p7+3*q,stride);
} else {
alignas(32) U temp[4*128];
for(int block=0;block<1024;block+=128) {
for(int v=0;v<128;v+=16) {
int idx=block+v;
if(!(v&16)){__builtin_prefetch(p1+idx+128,0,3);__builtin_prefetch(p2+idx+128,0,3);__builtin_prefetch(p3+idx+128,0,3);__builtin_prefetch(p4+idx+128,0,3);__builtin_prefetch(p5+idx+128,0,3);__builtin_prefetch(p6+idx+128,0,3);__builtin_prefetch(p7+idx+128,0,3);}
__m256i u2=_mm256_add_epi16(ld(p1+idx),ld(p6+idx));
__m256i u3=_mm256_add_epi16(u2,ld(p7+idx));
__m256i v5=ld(p5+idx);
st(temp+v,_mm256_add_epi16(_mm256_add_epi16(u2,v5),ld(p3+idx)));
st(temp+128+v,_mm256_sub_epi16(u3,ld(p4+idx)));
st(temp+256+v,_mm256_add_epi16(u3,v5));
st(temp+384+v,_mm256_add_epi16(ld(p2+idx),ld(p1+idx)));
}
size_t off=(size_t)(block/32)*stride;
emit_native(d12+off,temp,stride);
emit_native(d21+off,temp+128,stride);
emit_native(d22+off,temp+256,stride);
emit_native(d11+off,temp+384,stride);
}
}
}
template<int N>
static __attribute__((noinline)) void mul(const U *a,const U *b,U *c,U *work) {
if constexpr (N<=BASE) {
base(a,b,c,N);
} else if constexpr (N<=ALTMAX) {
constexpr int h=N/2,q=h*h+(h/BASE)*(h/BASE)*TILE_PAD;
const U *a11=a,*a12=a+q,*a21=a+2*q,*a22=a+3*q;
const U *b11=b,*b12=b+q,*b21=b+2*q,*b22=b+3*q;
U *c11=c,*c12=c+q,*c21=c+2*q,*c22=c+3*q;
U *x=work+0,*y=work+2*q+704,*next=work+4*q+640;
mul<h>(a12,b21,c11,next); // m2
mul<h>(a22,b22,c22,next); // m4
combine_displaced<false,q>(x,a21,q);
combine_displaced<false,q>(y,b21,q);
mul<h>(x,y,c12,next); // m5
combine_displaced<true,-2*q>(x,a22,q);
combine_displaced<true,-2*q>(y,b22,q);
mul<h>(x,y,c21,next); // m6
for(int chunk=0;chunk<q;chunk+=1024+TILE_PAD)
_Pragma("GCC unroll 4")
for(int i=chunk;i<chunk+1024;i+=16)
st(c22+i,_mm256_sub_epi16(_mm256_add_epi16(ld(c12+i),ld(c21+i)),_mm256_add_epi16(ld(c11+i),ld(c22+i))));
if constexpr(N==64) {
leaf_add(a11,b11,c11); // m1 directly accumulates
combine_displaced<true,-3*q>(y,b22,q);
leaf_add(a21,y,c21); // m3 directly accumulates
combine_displaced<true,3*q>(x,a11,q); // negate the M7 A input
leaf_add(x,b12,c12); // c12+=(-oldM7)
} else {
mul<h>(a11,b11,x,next); // m1
combine_inplace<false>(c11,x,q);
combine_displaced<true,-3*q>(y,b22,q);
mul<h>(a21,y,x,next); // m3
combine_inplace<false>(c21,x,q);
combine_displaced<true,-3*q>(x,a22,q);
mul<h>(x,b12,y,next); // m7
combine_inplace<true>(c12,y,q);
}
} else {
constexpr int h=N/2,q=h*h+(h/BASE)*(h/BASE)*TILE_PAD;
const U *a11=a,*a12=a+q,*a21=a+2*q,*a22=a+3*q;
const U *b11=b,*b12=b+q,*b21=b+2*q,*b22=b+3*q;
U *c11=c,*c12=c+q,*c21=c+2*q,*c22=c+3*q;
U *x=work+192,*y=work+q+512,*next=work+q+q+512;
combine_displaced<true,-2*q>(y,b22,q);
combine_displaced<true,2*q>(x,a11,q);
mul<h>(x,y,c21,next); // P7
combine_displaced<false,q>(x,a21,q);
combine_displaced<true,-q>(y,b12,q);
mul<h>(x,y,c22,next); // P5
combine_inplace<true>(x,a11,q);
combine_reverse_inplace(y,b22,q);
mul<h>(x,y,c12,next); // P6
combine_reverse_inplace(x,a12,q);
mul<h>(x,b22,c11,next); // P3
combine_inplace<true>(y,b21,q);
mul<h>(a22,y,x,next); // P4
mul<h>(a11,b11,y,next); // P1
if constexpr (N == 4096) {
combine3_unpack<2048>(final_out+2048,
final_out+(size_t)2048*4096,
final_out+(size_t)2048*4096+2048,
c12,c21,c22,y,x,c11,4096);
} else {
for(int chunk=0;chunk<q;chunk+=1024+TILE_PAD) for(int i=chunk;i<chunk+1024;i+=16) {
__m256i u2=_mm256_add_epi16(ld(y+i),ld(c12+i));
__m256i u3=_mm256_add_epi16(u2,ld(c21+i));
__m256i p5=ld(c22+i);
st(c12+i,_mm256_add_epi16(_mm256_add_epi16(u2,p5),ld(c11+i)));
st(c21+i,_mm256_sub_epi16(u3,ld(x+i)));
st(c22+i,_mm256_add_epi16(u3,p5));
}
}
mul<h>(a12,b21,c11,next); // P2
if constexpr (N == 4096) unpack_add<2048>(final_out,c11,y,4096);
else combine_inplace<false>(c11,y,q);
}
}
static constexpr int RQ=2048*2048+(2048/BASE)*(2048/BASE)*TILE_PAD,RS=RQ+ROOT_PAD;
alignas(4096) static U root_mem[12*RS+4096];
alignas(4096) static U root_tile[4*(64*64+(64/BASE)*(64/BASE)*TILE_PAD)];
static constexpr int HQ=64*64+(64/BASE)*(64/BASE)*TILE_PAD;
alignas(4096) static U high_left[4*HQ],high_right[4*HQ];
template<bool Right>
static void pack_four(U *tile,const U *s) {
pack(tile,s,64,4096,Right);
pack(tile+HQ,s+2048,64,4096,Right);
pack(tile+2*HQ,s+2048*4096,64,4096,Right);
pack(tile+3*HQ,s+2048*4096+2048,64,4096,Right);
}
// s4de_/s4df_: 横に隣接する 2 つの 64x64 tile(源 s と s+64)を 1 つの読み pass で作る。
// Right=false(A オペランド)= pack_wide(s4de_ が #119672 で着地)。
// Right=true (B オペランド)= pack_wide_b(s4df_、同機構の転置レイアウト版)。
template<bool Right>
static void pack_wide_root(U *tA,U *tB,const U *s) {
if constexpr(!Right) {
pack_wide(tA+0*HQ, tB+0*HQ, s);
pack_wide(tA+1*HQ, tB+1*HQ, s+2048);
pack_wide(tA+2*HQ, tB+2*HQ, s+2048L*4096);
pack_wide(tA+3*HQ, tB+3*HQ, s+2048L*4096+2048);
} else {
pack_wide_b(tA+0*HQ, tB+0*HQ, s);
pack_wide_b(tA+1*HQ, tB+1*HQ, s+2048);
pack_wide_b(tA+2*HQ, tB+2*HQ, s+2048L*4096);
pack_wide_b(tA+3*HQ, tB+3*HQ, s+2048L*4096+2048);
}
}
template<bool Right,bool High>
static void emit_root64(const U *tile,const U *left,const U *right,int offset,U *dest,U *destq,int voff) {
U *r0=destq+offset,*r1=r0+RS,*r2=r1+RS,*v7=dest+offset+voff*RS,*v5=v7+RS,*v6=v5+RS,*v34=v6+RS;
for(int i=0;i<(64*64+(64/BASE)*(64/BASE)*TILE_PAD);i+=32){
__m256i a=ld(tile+i),b=ld(tile+(64*64+(64/BASE)*(64/BASE)*TILE_PAD)+i),c=ld(tile+2*(64*64+(64/BASE)*(64/BASE)*TILE_PAD)+i),d=ld(tile+3*(64*64+(64/BASE)*(64/BASE)*TILE_PAD)+i);
__m256i aa=ld(tile+i+16),bb=ld(tile+(64*64+(64/BASE)*(64/BASE)*TILE_PAD)+i+16),cc=ld(tile+2*(64*64+(64/BASE)*(64/BASE)*TILE_PAD)+i+16),dd=ld(tile+3*(64*64+(64/BASE)*(64/BASE)*TILE_PAD)+i+16);
if constexpr(High) {
a=_mm256_add_epi16(a,_mm256_sub_epi16(ld(left+0*HQ+i),ld(right+0*HQ+i)));
b=_mm256_add_epi16(b,_mm256_sub_epi16(ld(left+1*HQ+i),ld(right+1*HQ+i)));
c=_mm256_add_epi16(c,_mm256_sub_epi16(ld(left+2*HQ+i),ld(right+2*HQ+i)));
d=_mm256_add_epi16(d,_mm256_sub_epi16(ld(left+3*HQ+i),ld(right+3*HQ+i)));
aa=_mm256_add_epi16(aa,_mm256_sub_epi16(ld(left+0*HQ+i+16),ld(right+0*HQ+i+16)));
bb=_mm256_add_epi16(bb,_mm256_sub_epi16(ld(left+1*HQ+i+16),ld(right+1*HQ+i+16)));
cc=_mm256_add_epi16(cc,_mm256_sub_epi16(ld(left+2*HQ+i+16),ld(right+2*HQ+i+16)));
dd=_mm256_add_epi16(dd,_mm256_sub_epi16(ld(left+3*HQ+i+16),ld(right+3*HQ+i+16)));
}
_mm256_stream_si256((__m256i*)(r0+i),a);_mm256_stream_si256((__m256i*)(r0+i+16),aa);
_mm256_stream_si256((__m256i*)(r1+i),Right?c:b);_mm256_stream_si256((__m256i*)(r1+i+16),Right?cc:bb);
_mm256_stream_si256((__m256i*)(r2+i),d);_mm256_stream_si256((__m256i*)(r2+i+16),dd);
__m256i x7=Right?_mm256_sub_epi16(d,b):_mm256_sub_epi16(a,c);
__m256i xx7=Right?_mm256_sub_epi16(dd,bb):_mm256_sub_epi16(aa,cc);
_mm256_stream_si256((__m256i*)(v7+i),x7);_mm256_stream_si256((__m256i*)(v7+i+16),xx7);
__m256i x5=Right?_mm256_sub_epi16(b,a):_mm256_add_epi16(c,d);
__m256i xx5=Right?_mm256_sub_epi16(bb,aa):_mm256_add_epi16(cc,dd);
_mm256_stream_si256((__m256i*)(v5+i),x5);_mm256_stream_si256((__m256i*)(v5+i+16),xx5);
__m256i x6=Right?_mm256_sub_epi16(d,x5):_mm256_sub_epi16(x5,a);
__m256i xx6=Right?_mm256_sub_epi16(dd,xx5):_mm256_sub_epi16(xx5,aa);
_mm256_stream_si256((__m256i*)(v6+i),x6);_mm256_stream_si256((__m256i*)(v6+i+16),xx6);
__m256i x34=Right?_mm256_sub_epi16(x6,c):_mm256_sub_epi16(b,x6);
__m256i xx34=Right?_mm256_sub_epi16(xx6,cc):_mm256_sub_epi16(bb,xx6);
_mm256_stream_si256((__m256i*)(v34+i),x34);_mm256_stream_si256((__m256i*)(v34+i+16),xx34);
}
}
template<bool Right>
static void pack_root(const U*s,int n,int offset,U *dest,U *destq,int voff) {
if(n>128){int h=n/2,q=h*h+(h/BASE)*(h/BASE)*TILE_PAD;
pack_root<Right>(s,h,offset,dest,destq,voff);
pack_root<Right>(s+h,h,offset+q,dest,destq,voff);
pack_root<Right>(s+h*4096,h,offset+2*q,dest,destq,voff);
pack_root<Right>(s+h*4096+h,h,offset+3*q,dest,destq,voff);
} else {
pack_wide_root<Right>(root_tile,high_left,s);
emit_root64<Right,false>(root_tile,nullptr,nullptr,offset,dest,destq,voff);
emit_root64<Right,false>(high_left,nullptr,nullptr,offset+HQ,dest,destq,voff);
pack_wide_root<Right>(high_right,root_tile,s+64*4096);
emit_root64<Right,false>(high_right,nullptr,nullptr,offset+2*HQ,dest,destq,voff);
emit_root64<Right,true>(root_tile,high_left,high_right,offset+3*HQ,dest,destq,voff);
}
}
static void short_product4096(int n,const short*A,const short*B,short*C){
U *cq=(U*)C; // Three root input slots and the retired tail precede final C writeback.
U *ar=root_mem,*br=ar+4*RS,*pr=br+7*RS; // root_mem 15 -> 12 槽
U *work=cq+3*RS+(464*2);
raw_basis_input=(const U*)A;
pack_root<false>((const U*)A,2048,0,ar,cq,0);
raw_basis_input=(const U*)B;
pack_root<true>((const U*)B,2048,0,br,br,3);
_mm_mfence();
const U *a11=cq,*a12=cq+RS,*a22=cq+2*RS,*s7=ar,*s5=ar+RS,*s6=ar+2*RS,*s3=ar+3*RS;
const U *b11=br,*b21=br+RS,*b22=br+2*RS,*t7=br+3*RS,*t5=br+4*RS,*t6=br+5*RS,*t4=br+6*RS;
U *p1=ar+2*RS,*p2=br+5*RS,*p3=ar+RS,*p4=br+4*RS,*p5=ar,*p6=br+3*RS,*p7=pr;
mul<2048>(s7,t7,p7,work);mul<2048>(s5,t5,p5,work);mul<2048>(s6,t6,p6,work);
mul<2048>(s3,b22,p3,work);mul<2048>(a22,t4,p4,work);mul<2048>(a11,b11,p1,work);mul<2048>(a12,b21,p2,work);
U *out=(U*)C;
root_fuse<2048>(out,out+2048,out+2048*4096,out+2048*4096+2048,p1,p2,p3,p4,p5,p6,p7,4096);_mm_sfence();
asm volatile(".purgem vzeroupper");
_mm256_zeroupper();
}
#include <cstdlib>
static void outer_operand(const unsigned short*a,int sa,const unsigned short*b,int sb,unsigned short*out,int sign){
for(int i=0;i<4096;i++)for(int j=0;j<4096;j+=16){
__m256i x=_mm256_load_si256((const __m256i*)(a+(size_t)i*sa+j));
if(sign){__m256i y=_mm256_load_si256((const __m256i*)(b+(size_t)i*sb+j));x=sign>0?_mm256_add_epi16(x,y):_mm256_sub_epi16(x,y);}
_mm256_store_si256((__m256i*)(out+(size_t)i*4096+j),x);
}
}
static void outer_accum(unsigned short*c,const unsigned short*p,int sign){
for(int i=0;i<4096;i++)for(int j=0;j<4096;j+=16){
__m256i x=_mm256_load_si256((const __m256i*)(c+(size_t)i*8192+j));
__m256i y=_mm256_load_si256((const __m256i*)(p+(size_t)i*4096+j));
x=sign>0?_mm256_add_epi16(x,y):_mm256_sub_epi16(x,y);
_mm256_store_si256((__m256i*)(c+(size_t)i*8192+j),x);
}
}
void matrix_multiply(int n,const short*A,const short*B,short*C){
if(n==4096){short_product4096(n,A,B,C);return;}
if(n!=8192){for(int i=0;i<n;i++)for(int j=0;j<n;j++){unsigned z=0;for(int k=0;k<n;k++)z+=(unsigned short)A[(size_t)i*n+k]*(unsigned)(unsigned short)B[(size_t)k*n+j];C[(size_t)i*n+j]=(short)z;}return;}
using V=unsigned short;V *aa,*bb,*pp;
constexpr size_t z=(size_t)4096*4096;
if(posix_memalign((void**)&aa,4096,z*2)||posix_memalign((void**)&bb,4096,z*2)||posix_memalign((void**)&pp,4096,z*2))abort();
const V*a00=(const V*)A,*a01=a00+4096,*a10=a00+4096*8192,*a11=a10+4096;
const V*b00=(const V*)B,*b01=b00+4096,*b10=b00+4096*8192,*b11=b10+4096;
V*c00=(V*)C,*c01=c00+4096,*c10=c00+4096*8192,*c11=c10+4096;
memset(C,0,(size_t)n*n*2);
auto prod=[&](){short_product4096(4096,(const short*)aa,(const short*)bb,(short*)pp);};
outer_operand(a00,8192,a11,8192,aa,1);outer_operand(b00,8192,b11,8192,bb,1);prod();outer_accum(c00,pp,1);outer_accum(c11,pp,1);
outer_operand(a10,8192,a11,8192,aa,1);outer_operand(b00,8192,nullptr,0,bb,0);prod();outer_accum(c10,pp,1);outer_accum(c11,pp,-1);
outer_operand(a00,8192,nullptr,0,aa,0);outer_operand(b01,8192,b11,8192,bb,-1);prod();outer_accum(c01,pp,1);outer_accum(c11,pp,1);
outer_operand(a11,8192,nullptr,0,aa,0);outer_operand(b10,8192,b00,8192,bb,-1);prod();outer_accum(c00,pp,1);outer_accum(c10,pp,1);
outer_operand(a00,8192,a01,8192,aa,1);outer_operand(b11,8192,nullptr,0,bb,0);prod();outer_accum(c00,pp,-1);outer_accum(c01,pp,1);
outer_operand(a10,8192,a00,8192,aa,-1);outer_operand(b00,8192,b01,8192,bb,1);prod();outer_accum(c11,pp,1);
outer_operand(a01,8192,a11,8192,aa,-1);outer_operand(b10,8192,b11,8192,bb,1);prod();outer_accum(c00,pp,1);
free(pp);free(bb);free(aa);
}
| Compilation | N/A | N/A | Compile OK | Score: N/A | 显示更多 |
| Testcase #1 | 3.793 s | 321 MB + 672 KB | Accepted | Score: 100 | 显示更多 |