Conversation
andrej
left a comment
There was a problem hiding this comment.
A nicer "Kernel" abstraction that encodes (i) the function name (extern C), (ii) the argument specification expected (MLIR func.func signature), (iii) how to compile and link it in would be good at some point in the future. If we have to list every single activation function in every GEMV and GEMM kernel as a custom prologue/epilogue, it will get old quick.
But for now this is good. Thanks!
| # per output tile would be. Both kernels take separate in/out pointers (restrict on | ||
| # both), so normalizing needs a second L1 buffer rather than an in-place call. |
There was a problem hiding this comment.
Should we add an in-place activation kernel in the future? Seems wasteful ...
CI Test Results13e23a1 (2026_09_11_21_46_21) IRON - CI SummaryExamplesiron/applications/llama_3.2_1b
Smalliron/operators/axpy
iron/operators/dequant
iron/operators/elementwise_add
iron/operators/elementwise_mul
iron/operators/gelu
iron/operators/gemm
iron/operators/gemv
iron/operators/layer_norm
iron/operators/leaky_relu
iron/operators/mem_copy
iron/operators/mha
iron/operators/relu
iron/operators/repeat
iron/operators/rms_norm
iron/operators/rope
iron/operators/sigmoid
iron/operators/silu
iron/operators/softmax
iron/operators/strided_copy
iron/operators/swiglu_decode
iron/operators/swiglu_prefill
iron/operators/tanh
iron/operators/transpose
Krackan - SmallIRONTested on iron/operators/axpy
iron/operators/dequant
iron/operators/elementwise_add
iron/operators/elementwise_mul
iron/operators/gelu
iron/operators/gemm
iron/operators/gemv
iron/operators/layer_norm
iron/operators/leaky_relu
iron/operators/mem_copy
iron/operators/mha
iron/operators/relu
iron/operators/repeat
iron/operators/rms_norm
iron/operators/rope
iron/operators/sigmoid
iron/operators/silu
iron/operators/softmax
iron/operators/strided_copy
iron/operators/swiglu_decode
iron/operators/swiglu_prefill
iron/operators/tanh
iron/operators/transpose
Krackan - ExamplesIRONTested on iron/applications/llama_3.2_1b
Phoenix - SmallIRONTested on iron/operators/axpy
iron/operators/dequant
iron/operators/elementwise_add
iron/operators/elementwise_mul
iron/operators/gelu
iron/operators/gemm
iron/operators/gemv
iron/operators/layer_norm
iron/operators/leaky_relu
iron/operators/mem_copy
iron/operators/relu
iron/operators/repeat
iron/operators/rms_norm
iron/operators/rope
iron/operators/sigmoid
iron/operators/silu
iron/operators/softmax
iron/operators/strided_copy
iron/operators/swiglu_decode
iron/operators/swiglu_prefill
iron/operators/tanh
iron/operators/transpose
Phoenix - ExamplesIRONTested on Trend tables omitted, the comment hit GitHub's size limit. Full report in the workflow run. |
CI Test Resultsb9b371d (2026_09_08_21_23_32) IRON - CI SummaryExamplesiron/applications/llama_3.2_1b
Smalliron/operators/axpy
iron/operators/dequant
iron/operators/elementwise_add
iron/operators/elementwise_mul
iron/operators/gelu
iron/operators/gemm
iron/operators/gemv
iron/operators/layer_norm
iron/operators/leaky_relu
iron/operators/mem_copy
iron/operators/relu
iron/operators/repeat
iron/operators/rms_norm
iron/operators/rope
iron/operators/sigmoid
iron/operators/silu
iron/operators/softmax
iron/operators/strided_copy
iron/operators/swiglu_decode
iron/operators/swiglu_prefill
iron/operators/tanh
iron/operators/transpose
Krackan - ExamplesIRONTested on iron/applications/llama_3.2_1b
Trends: IRON Trendsiron/applications/llama_3.2_1btest_llama_3_2_1b[llama_3.2_1b_prompt_1024_tokens_1]
test_llama_3_2_1b[llama_3.2_1b_prompt_1024_tokens_40]
test_llama_3_2_1b[llama_3.2_1b_prompt_13_tokens_1]
test_llama_3_2_1b[llama_3.2_1b_prompt_13_tokens_40]
Phoenix - SmallIRONTested on iron/operators/axpy
iron/operators/dequant
iron/operators/elementwise_add
iron/operators/elementwise_mul
iron/operators/gelu
iron/operators/gemm
iron/operators/gemv
iron/operators/layer_norm
iron/operators/leaky_relu
iron/operators/mem_copy
iron/operators/relu
iron/operators/repeat
iron/operators/rms_norm
iron/operators/rope
iron/operators/sigmoid
iron/operators/silu
iron/operators/softmax
iron/operators/strided_copy
iron/operators/swiglu_decode
iron/operators/swiglu_prefill
iron/operators/tanh
iron/operators/transpose
Trends: IRON Trendsiron/operators/axpytest_axpy[input_length_2048-num_aie_columns_1-tile_size_2048-scalar_factor_3.0]
test_axpy[input_length_2048-num_aie_columns_2-tile_size_1024-scalar_factor_3.0]
test_axpy[input_length_2048-num_aie_columns_4-tile_size_512-scalar_factor_3.0]
iron/operators/dequanttest_dequant[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-group_size_32]
test_dequant[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-group_size_32]
test_dequant[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-group_size_32]
test_dequant[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-group_size_32]
test_dequant[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-group_size_32]
test_dequant[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-group_size_32]
iron/operators/elementwise_addtest_elementwise_add[input_length_2048-num_aie_columns_1-tile_size_2048]
test_elementwise_add[input_length_2048-num_aie_columns_2-tile_size_1024]
test_elementwise_add[input_length_2048-num_aie_columns_4-tile_size_512]
iron/operators/elementwise_multest_elementwise_mul[input_length_2048-num_aie_columns_1-tile_size_2048]
test_elementwise_mul[input_length_2048-num_aie_columns_2-tile_size_1024]
test_elementwise_mul[input_length_2048-num_aie_columns_4-tile_size_512]
iron/operators/gelutest_gelu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]
test_gelu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]
test_gelu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]
test_gelu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]
test_gelu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]
test_gelu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]
iron/operators/gemmtest_gemm[M_192-K_384-N_64-num_aie_columns_4-b_col_maj_False-c_col_maj_False-m_48-k_96-n_16-trace_size_0-partition_N_1]
test_gemm[M_192-K_384-N_64-num_aie_columns_4-b_col_maj_True-c_col_maj_True-m_48-k_96-n_16-trace_size_0-partition_N_1]
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_1-b_col_maj_False-c_col_maj_False-m_64-k_64-n_64-trace_size_0-partition_N_1]
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_2-b_col_maj_True-c_col_maj_False-m_64-k_64-n_64-trace_size_0-partition_N_1]
test_gemm[M_384-K_1536-N_1792-num_aie_columns_4-b_col_maj_True-c_col_maj_False-m_32-k_48-n_64-trace_size_0-partition_N_1]
test_gemm[M_64-K_512-N_256-num_aie_columns_4-b_col_maj_True-c_col_maj_False-m_16-k_64-n_64-trace_size_0-partition_N_4]
iron/operators/gemvtest_gemv[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128]
test_gemv[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048]
test_gemv[M_2048-K_8192-num_aie_columns_2-tile_size_input_1-tile_size_output_1024]
test_gemv[M_2048-K_8192-num_aie_columns_4-tile_size_input_1-tile_size_output_512]
test_gemv[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024]
test_gemv[M_8192-K_2048-num_aie_columns_2-tile_size_input_4-tile_size_output_1024]
test_gemv[M_8192-K_2048-num_aie_columns_4-tile_size_input_4-tile_size_output_1024]
test_gemv_batched[M_1024-K_1024-num_aie_columns_1-tile_size_input_1-tile_size_output_64-num_batches_2]
test_gemv_batched[M_1026-K_64-num_aie_columns_1-tile_size_input_1-tile_size_output_2-num_batches_2]
test_gemv_batched[M_256-K_128-num_aie_columns_1-tile_size_input_1-tile_size_output_256-num_batches_4]
test_gemv_batched[M_64-K_1536-num_aie_columns_1-tile_size_input_1-tile_size_output_64-num_batches_8]
test_gemv_gelu[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128]No metrics available. test_gemv_gelu[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048]No metrics available. test_gemv_gelu[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024]No metrics available. test_gemv_norm_prologue[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128-prologue_ln-norm_ref_<function layer_norm_ref at 0x796408e4ab60>]
test_gemv_norm_prologue[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128-prologue_rms-norm_ref_<function rms_norm_ref at 0x796408e4aac0>]
test_gemv_norm_prologue[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048-prologue_ln-norm_ref_<function layer_norm_ref at 0x796408e4ab60>]No metrics available. test_gemv_norm_prologue[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048-prologue_rms-norm_ref_<function rms_norm_ref at 0x796408e4aac0>]No metrics available. test_gemv_norm_prologue[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024-prologue_ln-norm_ref_<function layer_norm_ref at 0x796408e4ab60>]
test_gemv_norm_prologue[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024-prologue_rms-norm_ref_<function rms_norm_ref at 0x796408e4aac0>]
iron/operators/layer_normtest_layer_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]
test_layer_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]
test_layer_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]
test_layer_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]
test_layer_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]
test_layer_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]
iron/operators/leaky_relutest_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.01]
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.1]
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.25]
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-alpha_0.01]
test_leaky_relu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-alpha_0.01]
test_leaky_relu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-alpha_0.01]
test_leaky_relu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-alpha_0.01]
test_leaky_relu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-alpha_0.01]
iron/operators/mem_copytest_mem_copy[input_length_2048-num_cores_1-num_channels_1-bypass_False-tile_size_2048]
test_mem_copy[input_length_2048-num_cores_2-num_channels_1-bypass_False-tile_size_1024]
test_mem_copy[input_length_2048-num_cores_2-num_channels_2-bypass_False-tile_size_1024]
test_mem_copy[input_length_2048-num_cores_4-num_channels_1-bypass_False-tile_size_512]
test_mem_copy[input_length_2048-num_cores_4-num_channels_2-bypass_False-tile_size_512]
test_mem_copy[input_length_2048-num_cores_8-num_channels_2-bypass_False-tile_size_256]
iron/operators/repeattest_cols_without_a_legal_split_is_rejected[cols_1031-why_prime > 1023: the only divisors are 1 and cols, neither legal]No metrics available. test_cols_without_a_legal_split_is_rejected[cols_2062-why_2 x 1031: the only word-aligned chunk leaves a 1031-wide chunk count]No metrics available. test_cols_without_a_legal_split_is_rejected[cols_513-why_odd: every divisor is odd, so no chunk is a whole 32-bit word]No metrics available. test_repeat[rows_4-cols_1024-repeat_2-transfer_size_None]
test_repeat[rows_8-cols_512-repeat_4-transfer_size_64]
test_repeat[rows_8-cols_64-repeat_4-transfer_size_None]
iron/operators/rms_normtest_rms_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-weighted_False]
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-weighted_True]
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-weighted_False]
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-weighted_True]
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-weighted_False]
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-weighted_True]
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-weighted_False]
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-weighted_True]
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-weighted_False]
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-weighted_True]
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-weighted_False]
iron/operators/ropetest_rope[rows_32-cols_512-angle_rows_32-aie_columns_1-method_type_0]
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_2-method_type_0]
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_4-method_type_0]
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_1-method_type_0]
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_2-method_type_0]
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_4-method_type_0]
iron/operators/softmaxtest_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_1024]
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_2048]
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_512]
iron/operators/strided_copytest_strided_copy[chunked_transfer]
test_strided_copy[contiguous]
test_strided_copy[four_channels]
test_strided_copy[kv_slot0]
test_strided_copy[kv_slot5]
test_strided_copy[kv_slot5_four_channels]
test_strided_copy[kv_slot5_two_channels]
test_strided_copy[kv_slot_last]
test_strided_copy[two_channels]
test_strided_copy[two_channels_chunked]
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter0]No metrics available. test_transfer_size_not_dividing_per_channel_share_is_rejected[iter1]No metrics available. test_transfer_size_not_dividing_per_channel_share_is_rejected[iter2]No metrics available. test_transfer_size_not_dividing_per_channel_share_is_rejected[iter3]No metrics available. test_transfer_size_not_dividing_per_channel_share_is_rejected[iter4]No metrics available. iron/operators/swiglu_decodetest_swiglu_decode[embedding_dim_1024-hidden_dim_3584]
test_swiglu_decode[embedding_dim_2048-hidden_dim_2048]
iron/operators/swiglu_prefilltest_swiglu_prefill[seq_len_256-embedding_dim_2048-hidden_dim_2048-prio_accuracy_False]
iron/operators/transposetest_transpose[M_2048-N_64-aie_columns_1-channels_1-m_64-n_64-s_8-num_batches_1]
test_transpose[M_2048-N_64-aie_columns_1-channels_1-m_64-n_64-s_8-num_batches_2]
test_transpose[M_2048-N_64-aie_columns_1-channels_1-m_64-n_64-s_8]
test_transpose[M_2048-N_64-aie_columns_1-channels_2-m_64-n_64-s_8-num_batches_1]
test_transpose[M_2048-N_64-aie_columns_1-channels_2-m_64-n_64-s_8]
|
Adds an opt-in `prologue` parameter to GEMV. `prologue="none"` (default) is byte-identical to before; `prologue="rms"`/`"ln"` normalizes the shared B vector once per acquire, before it feeds every matvec call in that batch, so a decode-time norm+GEMV becomes one dispatch instead of a separate elementwise pass. Affine-free (gamma/beta fold into the weight matrix host-side), matching the existing rms_norm.cc/layer_norm.cc kernels' own convention. Both kernels take separate restrict-qualified in/out pointers, so normalizing b in place would violate their aliasing contract (the fix amd#135 used for gelu, an in-place wrapper, isn't available here without touching two kernel files for one call site). Instead each core gets a second L1 Buffer for the normalized vector; matvec reads from that instead of the raw acquire. Reuses rms_norm.cc/layer_norm.cc as-is, no kernel changes. rms_norm.cc and layer_norm.cc exist under both aie2 and aie2p with the same extern-C signature, so unlike the gelu epilogue this prologue is not NPU2-only. Test: test_gemv_norm_prologue compares against an A @ norm(B) golden across three shapes for each of rms/ln, with the standard latency/bandwidth metrics. rms_norm_ref/layer_norm_ref reproduce the kernels' f32 reduction math; checked against a hand-derived formula at rel-L2 < 1e-7 (pure numpy, no device). Not device-verified: I don't have hardware access in this session. Tolerances (rel_tol=0.05, abs_tol=1e-2) follow the gelu epilogue's precedent, not a measurement.
Head branch was pushed to by a user without write access
1b7d479 to
9858d9b
Compare
|
One warning: And these errors: |
GEMV can now fold an RMSNorm or LayerNorm over its shared B vector, so a caller that
normalizes before a matvec stops paying a separate round trip for it.
Added
prologue={"none","rms","ln"}on the GEMV operator, applied to the shared B vector onceper acquire, before it feeds every matvec call in the batch — so the cost is O(K) per
batch, not O(M) as re-normalizing per output tile would be.
rms_norm_ref/layer_norm_refingemv/reference.py, affine-free and computed infloat32 to match the kernels' own reduction.
test_gemv_norm_prologue, checking the fused result against anA @ norm(B)goldenacross both norms and three shapes.
layer_norm_f32inaie_kernels/aie2{,p}/layer_norm.cc: anextern "C"entry point forthe
layer_norm_bf16_f32_calculationimplementation that was already in the file but notexported.
Changed
layer_norm_f32rather thanlayer_norm.layer_normaccumulates the sum in bfloat16 (
::aie::vector<T, N> sum_acc) while accumulating thesum of squares in f32, so the mean it subtracts loses accuracy as
colsgrows. That isvisible here and not in RMSNorm, which needs no mean: against the f32 reference,
lnatK=2048 missed on ~180 of 8192 outputs and at K=128 on one, while
rmsmatched at thesame shapes.
layer_normitself is untouched, so nothing else changes behavior.Removed
Known gap
The prologue reads and writes separate pointers, so it needs a second K-element L1 buffer
on top of the B fifo's. At K=8192 with
m_input=1that buffer alone overflows one core'sdata memory, and the failure surfaces as
Basic sequential allocation also failedfromaiecc rather than as a precondition. The sweep is bounded to K=4096 accordingly; a real
up-front check wants an L1 size the device model exposes, which IRON does not carry today.
PR Merge Checklist
develcommit and pointing todevel.