Files
TheRock/rocm-libraries/projects/hipblaslt/tensilelite/Tensile/Tests/unit/test_perArchFallbackRename.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

241 lines
11 KiB
Python

################################################################################
#
# Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
################################################################################
"""Producer-side tests for per-arch fallback rename and Mapping completeness.
Each test is constructed so it fails on the *un*patched producer for the
specific behavior the patch changes. Discrimination is the point: a test that
passes on either implementation does not protect against this regression class.
"""
import pytest
from Tensile.SolutionLibrary import (
MasterSolutionLibrary,
PlaceholderLibrary,
PredicateLibrary,
ProblemMapLibrary,
)
from Tensile.TensileCreateLibrary.Run import renameFallbacksPerArch
def _placeholder(name: str) -> PlaceholderLibrary:
return PlaceholderLibrary(name)
def _wrap_in_predicate(child) -> PredicateLibrary:
"""Wrap a single child in a PredicateLibrary row, mirroring the
structure produced by the real library builder."""
pred = PredicateLibrary(tag="Hardware")
pred.rows = [{"predicate": object(), "library": child}]
return pred
def _wrap_in_problem_map(child) -> ProblemMapLibrary:
pmap = ProblemMapLibrary(mappingProperty=object(), mapping={"key": child})
return pmap
def _make_master_with_fallback(lazyName: str, treeName: str) -> MasterSolutionLibrary:
"""Build a minimal master library that has a single fallback lazy library
and a matching PlaceholderLibrary buried inside the master.library tree.
Solutions dicts are left empty; the rename code does not need real
solutions, only attribute shape (.lazyLibraries dict, .library tree).
"""
placeholder = _placeholder(treeName)
tree = _wrap_in_predicate(_wrap_in_problem_map(placeholder))
master = MasterSolutionLibrary({}, tree)
# `merge()` would have populated lazyLibraries from the fallback master;
# we plant the entry directly to avoid needing real solution objects.
master.lazyLibraries[lazyName] = object()
return master
# ---------------------------------------------------------------------------
# 1. lazyLibraries dict keys get arch-suffixed for fallback entries.
# ---------------------------------------------------------------------------
def test_renameFallbacksPerArch_archSuffixesLazyKeys():
master = _make_master_with_fallback(
lazyName="TensileLibrary_Type_Hi_fallback",
treeName="TensileLibrary_Type_Hi_fallback",
)
masterLibraries = {"gfx942": master}
renameFallbacksPerArch(masterLibraries)
keys = list(masterLibraries["gfx942"].lazyLibraries.keys())
assert "TensileLibrary_Type_Hi_fallback_gfx942" in keys, (
"Patched producer must arch-suffix fallback lazy-library keys; "
"without the rename the key stays at 'TensileLibrary_Type_Hi_fallback' "
"and the per-arch Mapping filter (`name.endswith(\"_gfx942\")`) drops it."
)
assert "TensileLibrary_Type_Hi_fallback" not in keys, (
"Old (unsuffixed) key must be removed; leaving both around would "
"double-count solutions in the per-arch Mapping."
)
# ---------------------------------------------------------------------------
# 2. PlaceholderLibrary nodes inside master.library tree are renamed too.
# ---------------------------------------------------------------------------
def test_renameFallbacksPerArch_walksTreeAndRenamesPlaceholders():
master = _make_master_with_fallback(
lazyName="TensileLibrary_Type_Hi_fallback",
treeName="TensileLibrary_Type_Hi_fallback",
)
masterLibraries = {"gfx942": master}
renameFallbacksPerArch(masterLibraries)
# Re-walk to find the PlaceholderLibrary the helper produced:
placeholder = (
masterLibraries["gfx942"]
.library.rows[0]["library"] # PredicateLibrary -> ProblemMapLibrary
.mapping["key"] # ProblemMapLibrary -> PlaceholderLibrary
)
assert isinstance(placeholder, PlaceholderLibrary)
assert placeholder.filenamePrefix == "TensileLibrary_Type_Hi_fallback_gfx942", (
"The PlaceholderLibrary in the master library tree drives the runtime "
"load path. If only the lazyLibraries key is renamed but the tree node "
"is not, the runtime asks for the un-suffixed file and finds nothing."
)
# ---------------------------------------------------------------------------
# 3. Multi-arch safety: deepcopy must isolate per-arch changes.
# ---------------------------------------------------------------------------
def test_renameFallbacksPerArch_deepcopiesPerArchToAvoidAliasing():
"""`MasterSolutionLibrary.merge` aliases the same fallback lazy library
across every arch's master. Renaming without a per-arch deep copy would
suffix the shared object once per arch, producing
`..._fallback_gfx942_gfx1100` and corrupting both arches.
"""
master_gfx942 = _make_master_with_fallback(
lazyName="TensileLibrary_Type_Hi_fallback",
treeName="TensileLibrary_Type_Hi_fallback",
)
master_gfx1100 = _make_master_with_fallback(
lazyName="TensileLibrary_Type_Hi_fallback",
treeName="TensileLibrary_Type_Hi_fallback",
)
# Mimic the post-merge alias: both arches reference the same tree node.
shared_placeholder = master_gfx942.library.rows[0]["library"].mapping["key"]
master_gfx1100.library.rows[0]["library"].mapping["key"] = shared_placeholder
masterLibraries = {"gfx942": master_gfx942, "gfx1100": master_gfx1100}
renameFallbacksPerArch(masterLibraries)
p942 = (
masterLibraries["gfx942"]
.library.rows[0]["library"]
.mapping["key"]
)
p1100 = (
masterLibraries["gfx1100"]
.library.rows[0]["library"]
.mapping["key"]
)
assert p942.filenamePrefix == "TensileLibrary_Type_Hi_fallback_gfx942"
assert p1100.filenamePrefix == "TensileLibrary_Type_Hi_fallback_gfx1100"
# Without the deepcopy these would point to the same mutated object and
# one of the asserts above would fail with the other arch's suffix.
assert p942 is not p1100, (
"renameFallbacksPerArch must deep-copy each arch's master library; "
"without it, the shared fallback PlaceholderLibrary is mutated twice "
"and one arch ends up with the other arch's filename."
)
# ---------------------------------------------------------------------------
# 4. Idempotency: a second pass must not double-suffix.
# ---------------------------------------------------------------------------
def test_renameFallbacksPerArch_isIdempotent():
master = _make_master_with_fallback(
lazyName="TensileLibrary_Type_Hi_fallback",
treeName="TensileLibrary_Type_Hi_fallback",
)
masterLibraries = {"gfx942": master}
renameFallbacksPerArch(masterLibraries)
# Capture the post-first-pass state and re-run.
keys_after_first = set(masterLibraries["gfx942"].lazyLibraries.keys())
renameFallbacksPerArch(masterLibraries)
keys_after_second = set(masterLibraries["gfx942"].lazyLibraries.keys())
assert keys_after_first == keys_after_second, (
"A naive implementation without the `endswith('_<arch>')` guard "
"would suffix again, producing '..._fallback_gfx942_gfx942'."
)
assert "TensileLibrary_Type_Hi_fallback_gfx942_gfx942" not in keys_after_second
# ---------------------------------------------------------------------------
# 5. Round-trip discrimination: the per-arch Mapping filter the producer uses
# must keep every fallback entry once the rename has run.
#
# This is the test that would have caught the CI failure: it asserts the
# completeness invariant the runtime depends on.
# ---------------------------------------------------------------------------
def _per_arch_mapping_keys(libraryMapping, arch):
"""Mirror the producer's per-arch filter logic from Run.py."""
return {
idx
for idx, name in libraryMapping.items()
if name.endswith("_" + arch)
}
def test_perArchMapping_keepsFallbackEntries_afterRename():
"""Build a synthetic post-merge libraryMapping (the dict that
`generateLogicDataAndSolutions` returns) and verify that after rename
the per-arch filter retains every entry the runtime needs."""
# Pre-rename: lazyLibraries has both a tuned-for-arch entry and a fallback
# entry that came in via merge. The producer's libraryMapping would map
# solution indexes to these names.
libraryMapping_pre = {
0: "TensileLibrary_Type_Hi_gfx942", # tuned solution
1: "TensileLibrary_Type_Hi_fallback", # fallback solution (e.g. fp16)
}
# Apply the per-arch filter as the unpatched producer would:
unpatched_kept = _per_arch_mapping_keys(libraryMapping_pre, "gfx942")
assert unpatched_kept == {0}, (
"Sanity check: under the unpatched producer the fallback entry is "
"filtered out because its name ends in '_fallback' not '_gfx942'. "
"This is the bug that causes 'NO solution found!' for fp16 at runtime."
)
# Post-rename libraryMapping (what the patched producer feeds to the filter):
libraryMapping_post = {
0: "TensileLibrary_Type_Hi_gfx942",
1: "TensileLibrary_Type_Hi_fallback_gfx942",
}
patched_kept = _per_arch_mapping_keys(libraryMapping_post, "gfx942")
assert patched_kept == {0, 1}, (
"Patched producer must surface every solution index in the per-arch "
"Mapping. Missing fallback entries here means the runtime cannot "
"resolve solutions whose dtype lives only in the fallback library."
)