;; Generated by tools/gen_matvec_wat.mjs. y[j] = sum_i x[i] * W[i*nout + j] (module (import "env" "memory" (memory 1)) (func (export "matvec") (param $w i32) (param $x i32) (param $y i32) (param $nin i32) (param $nout i32) (local $j i32) (local $i i32) (local $wp i32) (local $xp i32) (local $row i32) (local $row2 i32) (local $x0 v128) (local $x1 v128) (local $a0 v128) (local $a1 v128) (local $a2 v128) (local $a3 v128) (local $a4 v128) (local $a5 v128) (local $a6 v128) (local $a7 v128) (local.set $row (i32.shl (local.get $nout) (i32.const 2))) (local.set $row2 (i32.shl (local.get $nout) (i32.const 3))) (local.set $j (i32.const 0)) (block $done (loop $blocks (br_if $done (i32.gt_u (i32.add (local.get $j) (i32.const 32)) (local.get $nout))) (local.set $a0 (v128.const i32x4 0 0 0 0)) (local.set $a1 (v128.const i32x4 0 0 0 0)) (local.set $a2 (v128.const i32x4 0 0 0 0)) (local.set $a3 (v128.const i32x4 0 0 0 0)) (local.set $a4 (v128.const i32x4 0 0 0 0)) (local.set $a5 (v128.const i32x4 0 0 0 0)) (local.set $a6 (v128.const i32x4 0 0 0 0)) (local.set $a7 (v128.const i32x4 0 0 0 0)) (local.set $wp (i32.add (local.get $w) (i32.shl (local.get $j) (i32.const 2)))) (local.set $xp (local.get $x)) (local.set $i (i32.shr_u (local.get $nin) (i32.const 1))) (loop $rows (local.set $x0 (v128.load32_splat (local.get $xp))) (local.set $x1 (v128.load32_splat offset=4 (local.get $xp))) (local.set $a0 (f32x4.add (local.get $a0) (f32x4.mul (local.get $x0) (v128.load offset=0 (local.get $wp))))) (local.set $a1 (f32x4.add (local.get $a1) (f32x4.mul (local.get $x0) (v128.load offset=16 (local.get $wp))))) (local.set $a2 (f32x4.add (local.get $a2) (f32x4.mul (local.get $x0) (v128.load offset=32 (local.get $wp))))) (local.set $a3 (f32x4.add (local.get $a3) (f32x4.mul (local.get $x0) (v128.load offset=48 (local.get $wp))))) (local.set $a4 (f32x4.add (local.get $a4) (f32x4.mul (local.get $x0) (v128.load offset=64 (local.get $wp))))) (local.set $a5 (f32x4.add (local.get $a5) (f32x4.mul (local.get $x0) (v128.load offset=80 (local.get $wp))))) (local.set $a6 (f32x4.add (local.get $a6) (f32x4.mul (local.get $x0) (v128.load offset=96 (local.get $wp))))) (local.set $a7 (f32x4.add (local.get $a7) (f32x4.mul (local.get $x0) (v128.load offset=112 (local.get $wp))))) (local.set $a0 (f32x4.add (local.get $a0) (f32x4.mul (local.get $x1) (v128.load offset=0 (i32.add (local.get $wp) (local.get $row)))))) (local.set $a1 (f32x4.add (local.get $a1) (f32x4.mul (local.get $x1) (v128.load offset=16 (i32.add (local.get $wp) (local.get $row)))))) (local.set $a2 (f32x4.add (local.get $a2) (f32x4.mul (local.get $x1) (v128.load offset=32 (i32.add (local.get $wp) (local.get $row)))))) (local.set $a3 (f32x4.add (local.get $a3) (f32x4.mul (local.get $x1) (v128.load offset=48 (i32.add (local.get $wp) (local.get $row)))))) (local.set $a4 (f32x4.add (local.get $a4) (f32x4.mul (local.get $x1) (v128.load offset=64 (i32.add (local.get $wp) (local.get $row)))))) (local.set $a5 (f32x4.add (local.get $a5) (f32x4.mul (local.get $x1) (v128.load offset=80 (i32.add (local.get $wp) (local.get $row)))))) (local.set $a6 (f32x4.add (local.get $a6) (f32x4.mul (local.get $x1) (v128.load offset=96 (i32.add (local.get $wp) (local.get $row)))))) (local.set $a7 (f32x4.add (local.get $a7) (f32x4.mul (local.get $x1) (v128.load offset=112 (i32.add (local.get $wp) (local.get $row)))))) (local.set $wp (i32.add (local.get $wp) (local.get $row2))) (local.set $xp (i32.add (local.get $xp) (i32.const 8))) (local.set $i (i32.sub (local.get $i) (i32.const 1))) (br_if $rows (local.get $i))) (local.set $wp (i32.add (local.get $y) (i32.shl (local.get $j) (i32.const 2)))) (v128.store offset=0 (local.get $wp) (local.get $a0)) (v128.store offset=16 (local.get $wp) (local.get $a1)) (v128.store offset=32 (local.get $wp) (local.get $a2)) (v128.store offset=48 (local.get $wp) (local.get $a3)) (v128.store offset=64 (local.get $wp) (local.get $a4)) (v128.store offset=80 (local.get $wp) (local.get $a5)) (v128.store offset=96 (local.get $wp) (local.get $a6)) (v128.store offset=112 (local.get $wp) (local.get $a7)) (local.set $j (i32.add (local.get $j) (i32.const 32))) (br $blocks))) (block $done4 (loop $blocks4 (br_if $done4 (i32.ge_u (local.get $j) (local.get $nout))) (local.set $a0 (v128.const i32x4 0 0 0 0)) (local.set $wp (i32.add (local.get $w) (i32.shl (local.get $j) (i32.const 2)))) (local.set $xp (local.get $x)) (local.set $i (local.get $nin)) (loop $rows4 (local.set $a0 (f32x4.add (local.get $a0) (f32x4.mul (v128.load32_splat (local.get $xp)) (v128.load (local.get $wp))))) (local.set $wp (i32.add (local.get $wp) (local.get $row))) (local.set $xp (i32.add (local.get $xp) (i32.const 4))) (local.set $i (i32.sub (local.get $i) (i32.const 1))) (br_if $rows4 (local.get $i))) (v128.store (i32.add (local.get $y) (i32.shl (local.get $j) (i32.const 2))) (local.get $a0)) (local.set $j (i32.add (local.get $j) (i32.const 4))) (br $blocks4))) ) ;; Y[m, nout] = X[m, nin] @ W: one matvec per row (weights stay hot in cache). (func (export "gemm") (param $w i32) (param $x i32) (param $y i32) (param $m i32) (param $nin i32) (param $nout i32) (block $done (loop $rows (br_if $done (i32.eqz (local.get $m))) (call 0 (local.get $w) (local.get $x) (local.get $y) (local.get $nin) (local.get $nout)) (local.set $x (i32.add (local.get $x) (i32.shl (local.get $nin) (i32.const 2)))) (local.set $y (i32.add (local.get $y) (i32.shl (local.get $nout) (i32.const 2)))) (local.set $m (i32.sub (local.get $m) (i32.const 1))) (br $rows))) ) )