@@ -102,35 +102,43 @@ void check_s(Module& m, const std::string& prefix, int s) {
102102 << " golden.size=" << golden.size () << " )" ;
103103}
104104
105- // Dynamic quantized linear: input [M, kLinK] -> output [M, kLinN].
105+ // Dynamic quantized linear: input [M, kLinK] -> output [M, n]. kLinN is the
106+ // register-tiled/bicol config; kLinNShmem (N>=2048) routes to the shmem GEMM.
106107constexpr int kLinK = 64 ;
107108constexpr int kLinN = 128 ;
108- // Run dyn_linear at [m_rows, kLinK] on an already-loaded module (so it can be
109+ constexpr int kLinNShmem = 2048 ;
110+ // Run <prefix> at [m_rows, kLinK] on an already-loaded module (so it can be
109111// reused across M without a fresh load), and compare to the golden.
110- void run_linear (Module& m, int m_rows) {
111- const std::string base = g_dir + " /dyn_linear .S" + std::to_string (m_rows);
112+ void run_linear (Module& m, int m_rows, const char * prefix, int n ) {
113+ const std::string base = g_dir + " /" + prefix + " .S" + std::to_string (m_rows);
112114 auto input = read_bin (base + " .input.bin" );
113115 auto golden = read_bin (base + " .golden.bin" );
114- ASSERT_FALSE (input.empty ()) << " missing dyn_linear .S" << m_rows;
116+ ASSERT_FALSE (input.empty ()) << " missing " << prefix << " .S" << m_rows;
115117 auto t = make_tensor_ptr ({m_rows, kLinK }, std::move (input));
116118 auto r = m.forward ({EValue (t)});
117119 ASSERT_TRUE (r.ok () && !r.get ().empty () && r.get ()[0 ].isTensor ())
118- << " dyn_linear M=" << m_rows << " forward failed" ;
120+ << prefix << " M=" << m_rows << " forward failed" ;
119121 const auto & out = r.get ()[0 ].toTensor ();
120- const size_t numel = static_cast <size_t >(m_rows) * kLinN ;
122+ const size_t numel = static_cast <size_t >(m_rows) * n ;
121123 ASSERT_EQ (static_cast <size_t >(out.numel ()), numel)
122- << " dyn_linear M=" << m_rows << " output numel mismatch" ;
124+ << prefix << " M=" << m_rows << " output numel mismatch" ;
123125 std::vector<float > got (
124126 out.const_data_ptr <float >(), out.const_data_ptr <float >() + numel);
125127 const float e = max_err (got, golden);
126128 // 4-bit quant: looser tol (the kernel mirrors the dequant-matmul reference).
127- EXPECT_LT (e, 5e-3f ) << " dyn_linear M=" << m_rows << " max_err=" << e;
129+ EXPECT_LT (e, 5e-3f ) << prefix << " M=" << m_rows << " max_err=" << e;
128130}
129131
130132void check_linear (int m_rows) {
131133 Module m (g_dir + " /dyn_linear.pte" );
132134 ASSERT_EQ (m.load_forward (), Error::Ok) << " load dyn_linear.pte" ;
133- run_linear (m, m_rows);
135+ run_linear (m, m_rows, " dyn_linear" , kLinN );
136+ }
137+
138+ void check_linear_shmem (int m_rows) {
139+ Module m (g_dir + " /dyn_linear_shmem.pte" );
140+ ASSERT_EQ (m.load_forward (), Error::Ok) << " load dyn_linear_shmem.pte" ;
141+ run_linear (m, m_rows, " dyn_linear_shmem" , kLinNShmem );
134142}
135143
136144// Dynamic SDPA (GQA prefill, input_pos=0): q[1,s,hq,d] k/v[1,s,hkv,d]
@@ -395,7 +403,25 @@ TEST(DynamicShape, QuantizedLinearReusedGraph) {
395403 Module m (g_dir + " /dyn_linear.pte" );
396404 ASSERT_EQ (m.load_forward (), Error::Ok) << " load dyn_linear.pte" ;
397405 for (int m_rows : {128 , 32 , 1 , 128 }) {
398- run_linear (m, m_rows);
406+ run_linear (m, m_rows, " dyn_linear" , kLinN );
407+ }
408+ }
409+
410+ // I3: dynamic linear at N=2048 -> the shmem-GEMM route (K>=4096||N>=2048); the
411+ // resize hook recomputes the shmem tile count for the live M on the fixed shmem
412+ // pipeline (M=1 exercises a partial row-tile).
413+ TEST (DynamicShape, QuantizedLinearShmem) {
414+ for (int m_rows : {128 , 32 , 1 }) {
415+ check_linear_shmem (m_rows);
416+ }
417+ }
418+
419+ // I4: shmem-routed linear reusing ONE loaded graph across M.
420+ TEST (DynamicShape, QuantizedLinearShmemReusedGraph) {
421+ Module m (g_dir + " /dyn_linear_shmem.pte" );
422+ ASSERT_EQ (m.load_forward (), Error::Ok) << " load dyn_linear_shmem.pte" ;
423+ for (int m_rows : {128 , 32 , 1 , 128 }) {
424+ run_linear (m, m_rows, " dyn_linear_shmem" , kLinNShmem );
399425 }
400426}
401427
0 commit comments