diff --git a/src/singeli/README.md b/src/singeli/README.md index 5766850c..71b0c37b 100644 --- a/src/singeli/README.md +++ b/src/singeli/README.md @@ -143,16 +143,19 @@ Some may also support one scalar argument or arguments with different widths. - `half{a:V, n} : count_half{V}` - extract either the low (`n==0`) or high (`n==1`) half of `a` - `pair{a:V, b:V} : count_dbl{V}` - make a vector from halves - `undefPromote{V2, a:V} : V` - promote `a` to a wider type, leaving the upper part undefined -- `vshl{a:V, b:V, n} : V` - `vshl{[0,1,2,3], [4,5,6,7], 1} → [1,2,3,4]` +- `vshl{a:V, b:V, n} : V` - `vshl{[0,1,2,3], [4,5,6,7], 1}` → `[1,2,3,4]` - `sel{VI1, a:V, b:VI2} : V2` - shuffle `a` by indices `b` in `VI1`-long lanes; arch-specific behavior on out-of-bounds values - -- `zip{a:V, b:V} : tup{V, V}` - `zip{[0,1,2,3], [4,5,6,7]} → tup{[0,4,1,5], [2,6,3,7]}` -- `mzip{a:V, b:V} : tup{el_dbl{V}, el_dbl{V}}` - reinterpreted `zip{a, b}` -- `pack{a:V, b:V} : tup{}` -- `zip{a:V, b:V, k} : V` - `select{zip{a, b}, k}` -- `mzip{a:V, b:V, k} : V` - `select{mzip{a, b}, k}` -- `pack{a:V, b:V, k} : V` - `select{pack{a, b}, k}` -- `zip128`, `mzip128` - within-128-bit-lane versions of the above + +## Zip & unzip + +All of these optionally accept a third argument of a constant `0` or `1` indicating which result element to give. + +Some of them define variants which operate within 128-bit lanes, via a `128` postfix to the name (e.g. `zip128`). + +- `zip{a:V, b:V} : tup{V, V}` - `zip{[0,1,2,3], [4,5,6,7]}` → `tup{[0,4,1,5], [2,6,3,7]}` +- `mzip{a:V, b:V} : tup{el_m{V}, el_m{V}}` - reinterpreted `zip{a, b}` +- `unzip{a:V, b:V} : tup{V, V}` - `unzip{[0,1,2,3], [4,5,6,7]}` → `tup{[0,2,4,6], [1,3,5,7]}` +- `pack{a:V, b:V} : tup{el_s{V}, el_s{V}}` - `unzip{el_s{V}~~a, el_s{V}~~b}`; i.e. low half of each element element in first result, high in second ## Mask stuff diff --git a/src/singeli/src/avx2.singeli b/src/singeli/src/avx2.singeli index 839ed815..8f33d4d1 100644 --- a/src/singeli/src/avx2.singeli +++ b/src/singeli/src/avx2.singeli @@ -1,7 +1,7 @@ def zip128{a:T, b:T, 0 if w256{T}} = emit{T, merge{'_mm256_unpacklo',x86_postfix{eltype{T}}}, a, b} def zip128{a:T, b:T, 1 if w256{T}} = emit{T, merge{'_mm256_unpackhi',x86_postfix{eltype{T}}}, a, b} -# questionable pack; these saturate the argument +# questionable pack; these work in 128-bit lanes and saturate elements def packQ{a:T,b:T==[16]i16} = emit{[32]i8, '_mm256_packs_epi16', a, b} def packQ{a:T,b:T==[ 8]i32} = emit{[16]i16, '_mm256_packs_epi32', a, b} def packQ{a:T,b:T==[16]u16} = emit{[32]u8, '_mm256_packus_epi16', a, b} diff --git a/src/singeli/src/base.singeli b/src/singeli/src/base.singeli index f0592377..3d11f9ce 100644 --- a/src/singeli/src/base.singeli +++ b/src/singeli/src/base.singeli @@ -31,8 +31,8 @@ oper &- ({v:T,m:(u1)} => v & -promote{T,m}) infix left 35 -def load {p:*[_]_, n } = assert{0} -def store{p:*[_]_, n, v} = assert{0} +def load {p:*[_]_, n } = assert{0,'bad load',p,n} +def store{p:*[_]_, n, v} = assert{0,'bad store',p,n,v} def load{p:*_} = load{p, 0} # def store{p:*_, v} = store{p, 0, v} def loadu {p:*T if isunsigned{T}} = emit{T, merge{'loadu_u', fmtnat{width{T}}}, p} @@ -163,7 +163,7 @@ def { homAll,homAny,bitAll,bitAny,homBlend,homMask,homMaskStore,homMaskStoreF,loadBatchBit, loadLow,make,maskStore,maskToHom,mulw,mulh,narrow,narrowTrunc,narrowPair,packQ,pair,pdep, pext,popcRand,sel,shl,shr,shuf,shuf16Hi,shuf16Lo,shufHalves,shufInd,storeLow, - topBlend,topMask,topMaskStore,topMaskStoreF,unord,vfold,widen,widenUpper, + topBlend,topMask,topMaskStore,topMaskStoreF,unord,unzip,vfold,widen,widenUpper, } def homMaskX{a:T} = tup{1, homMask{a}} # tup{n,mask}; mask with each bit repeated n times @@ -196,6 +196,8 @@ def zip128{a:T, b:T, k if width{T}==128} = zip{a, b, k} def mzip {a:T, b:T, k} = el_m{T} ~~ zip {a, b, k} def mzip128{a:T, b:T, k} = el_m{T} ~~ zip128{a, b, k} +def pack {a:T, b:T, k} = unzip{el_s{T}~~a, el_s{T}~~b, k} + local def extend kpair{op} = { def op{a:T, b:T} = tup{op{a,b,0}, op{a,b,1}} } @@ -326,6 +328,7 @@ def makeOptBranch{enable, Ts, F} = if (enable) makeBranch{Ts, F} else 'not defin def tree_fold{F, x} = { def h = length{x}>>1 + assert{h>0, 'tree_fold of empty'} F{tree_fold{F, slice{x,0,h}}, tree_fold{F, slice{x,h,length{x}}}} } def tree_fold{F, {x}} = x diff --git a/src/singeli/src/dyarith.singeli b/src/singeli/src/dyarith.singeli index ed6934c2..21e52ef7 100644 --- a/src/singeli/src/dyarith.singeli +++ b/src/singeli/src/dyarith.singeli @@ -69,9 +69,7 @@ def arithChk2{F=(__mul), M, w:T=[_](i32), x:T if hasarch{'X86_64'}} = { } def arithChk2{F=(__mul), M, w:T=[_]E, x:T if hasarch{'AARCH64'}} = { - def r12 = mulw{w, x} - rl:= pack{...r12, 0} - rh:= pack{...r12, 1} + def {rl, rh} = pack{...mulw{w, x}} tup{rl, tup{'homAny', M{rh != (rl >> (width{E}-1))}}} } diff --git a/src/singeli/src/neon.singeli b/src/singeli/src/neon.singeli index e3fe485e..d35571a4 100644 --- a/src/singeli/src/neon.singeli +++ b/src/singeli/src/neon.singeli @@ -75,8 +75,6 @@ def zip{a:T, b:T, 1 if nvec{T}} = emit{T, ntyp{'vzip2', T}, a, b} def unzip{x:T, y:T, 0 if nvec{T}} = emit{T, ntyp{'vuzp1', T}, T~~x, T~~y} def unzip{x:T, y:T, 1 if nvec{T}} = emit{T, ntyp{'vuzp2', T}, T~~x, T~~y} -def pack{x:T, y:T, 0 if nvec{T}} = unzip{el_s{T}~~x, el_s{T}~~y, 0} -def pack{x:T, y:T, 1 if nvec{T}} = unzip{el_s{T}~~x, el_s{T}~~y, 1} def shufInd{x:T, y:T, {...is} if nvec{T,32} and same{is, 2*range{vcount{T}}}} = T~~unzip{x,y,0} def shufInd{x:T, y:T, {...is} if nvec{T,32} and same{is, 1+2*range{vcount{T}}}} = T~~unzip{x,y,1}