diff --git a/dispatcher/codegen/arch_filter.py b/dispatcher/codegen/arch_filter.py index a6299b0b3f..64fb5b2863 100644 --- a/dispatcher/codegen/arch_filter.py +++ b/dispatcher/codegen/arch_filter.py @@ -88,6 +88,13 @@ OPERATOR_TILE_CONSTRAINTS = { "tile_k_alignment": 8, }, OperatorType.GEMM_GROUPED: { + "min_tile_m": 16, + "min_tile_n": 16, + "min_tile_k": 8, + "tile_m_alignment": 16, + "tile_n_alignment": 16, + "tile_k_alignment": 8, + }, # NOTE: these are copied from plain GEMM and only gate tile *shape* validity. # They do NOT express Stream-K's real feasibility requirement -- that a problem # has enough output tiles to partition K-work across the CUs. That gate is diff --git a/dispatcher/tests/CMakeLists.txt b/dispatcher/tests/CMakeLists.txt index 3976988ab0..bd19859e5f 100644 --- a/dispatcher/tests/CMakeLists.txt +++ b/dispatcher/tests/CMakeLists.txt @@ -27,6 +27,19 @@ set_tests_properties(dispatcher_test_autocorrect PROPERTIES ENVIRONMENT "PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/../python:${CMAKE_CURRENT_SOURCE_DIR}/../codegen:${CMAKE_CURRENT_SOURCE_DIR}/../scripts" ) +# Operator tile-constraints table regression test +add_test( + NAME dispatcher_test_arch_filter_constraints + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_arch_filter_constraints.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +set_tests_properties(dispatcher_test_arch_filter_constraints PROPERTIES + LABELS "dispatcher;python;validation" + TIMEOUT 60 + ENVIRONMENT "PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/../python:${CMAKE_CURRENT_SOURCE_DIR}/../codegen:${CMAKE_CURRENT_SOURCE_DIR}/../scripts" +) + # Verbose version of the test add_test( NAME dispatcher_test_autocorrect_verbose diff --git a/dispatcher/tests/test_arch_filter_constraints.py b/dispatcher/tests/test_arch_filter_constraints.py new file mode 100644 index 0000000000..333721a1a8 --- /dev/null +++ b/dispatcher/tests/test_arch_filter_constraints.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +# Copyright (c) Advanced Micro Devices, Inc., or its affiliates. +# SPDX-License-Identifier: MIT + +""" +Regression tests for OPERATOR_TILE_CONSTRAINTS in arch_filter.py. + +Guards against a malformed constraints table (e.g. an operator entry left +without a body/closing brace), which raises a SyntaxError on import and +breaks all GEMM codegen -- and therefore every downstream build/test that +imports unified_gemm_codegen. + +Can be run as: + python3 tests/test_arch_filter_constraints.py + ctest -R test_arch_filter_constraints +""" + +import sys +import unittest +from pathlib import Path + +# Setup paths +SCRIPT_DIR = Path(__file__).parent.resolve() +DISPATCHER_DIR = SCRIPT_DIR.parent +sys.path.insert(0, str(DISPATCHER_DIR / "codegen")) + +# Importing at all fails if arch_filter.py has a syntax error. +from arch_filter import OPERATOR_TILE_CONSTRAINTS, OperatorType # noqa: E402 + +REQUIRED_KEYS = { + "min_tile_m", + "min_tile_n", + "min_tile_k", + "tile_m_alignment", + "tile_n_alignment", + "tile_k_alignment", +} + + +class TestOperatorTileConstraints(unittest.TestCase): + """Validate the OPERATOR_TILE_CONSTRAINTS table is well-formed.""" + + def test_every_operator_has_an_entry(self): + """Every OperatorType must have a constraints entry.""" + for op in OperatorType: + self.assertIn( + op, + OPERATOR_TILE_CONSTRAINTS, + f"{op} is missing from OPERATOR_TILE_CONSTRAINTS", + ) + + def test_every_entry_is_a_well_formed_dict(self): + """Each entry must be a dict with the required integer keys.""" + for op, constraints in OPERATOR_TILE_CONSTRAINTS.items(): + self.assertIsInstance( + constraints, dict, f"{op} constraints should be a dict" + ) + self.assertEqual( + REQUIRED_KEYS, + set(constraints.keys()), + f"{op} constraints keys mismatch", + ) + for key, value in constraints.items(): + self.assertIsInstance( + value, int, f"{op}[{key}] should be an int, got {type(value)}" + ) + self.assertGreater(value, 0, f"{op}[{key}] should be positive") + + def test_gemm_grouped_entry_present(self): + """Regression: GEMM_GROUPED must have its own distinct constraints dict.""" + self.assertIn(OperatorType.GEMM_GROUPED, OPERATOR_TILE_CONSTRAINTS) + grouped = OPERATOR_TILE_CONSTRAINTS[OperatorType.GEMM_GROUPED] + self.assertEqual(REQUIRED_KEYS, set(grouped.keys())) + # GEMM_GROUPED and GEMM_STREAMK are separate entries, not a merged one. + self.assertIn(OperatorType.GEMM_STREAMK, OPERATOR_TILE_CONSTRAINTS) + + +if __name__ == "__main__": + unittest.main(verbosity=2)