Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 71 additions & 2 deletions .github/workflows/hipdnn-superbuild-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ on:
permissions:
contents: read

env:
# Pinned rocm-kpack commit installed into the build venv. Keep in sync with
# HIPKERNELPROVIDER_KPACK_GIT_REF in
# dnn-providers/hip-kernel-provider/cmake/KpackPython.cmake.
KPACK_REF: e3483286e751060b3a70b792792cc122632c66e8

concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
cancel-in-progress: true
Expand Down Expand Up @@ -121,6 +127,27 @@ jobs:
--deps-dir $env:THIRD_PARTY_DEPS_DIR
if ($LASTEXITCODE -ne 0) { throw "Failed to download third-party dependency sources" }

- name: Install rocm_kpack
shell: pwsh
run: |
$python = Join-Path $env:VENV_PATH "Scripts\python.exe"
# --force-reinstall: rocm-kpack is version 0.1.0 at every commit, so
# pip would treat the pinned requirement as already satisfied in a
# warm venv. Its deps are installed separately because the pinned
# commit's pyproject omits msgpack.
& $python -m pip install --force-reinstall --no-deps "rocm-kpack @ git+https://github.com/ROCm/rocm-kpack@${{ env.KPACK_REF }}"
if ($LASTEXITCODE -ne 0) { throw "Failed to install rocm_kpack" }
& $python -m pip install "zstandard>=0.20.0" "msgpack>=1.0.0"
if ($LASTEXITCODE -ne 0) { throw "Failed to install rocm_kpack dependencies" }
$installed = (& $python -c "import importlib.metadata as m, json; print(json.loads(m.distribution('rocm-kpack').read_text('direct_url.json'))['vcs_info']['commit_id'])")
if ($installed -ne "${{ env.KPACK_REF }}") { throw "rocm_kpack is $installed, expected ${{ env.KPACK_REF }}" }
# From the installed module: site.getsitepackages()[0] is the venv
# root on Windows.
$sitePackages = (& $python -c "import pathlib, rocm_kpack; print(pathlib.Path(rocm_kpack.__file__).parent.parent.as_posix())")
if ($LASTEXITCODE -ne 0) { throw "rocm_kpack is not importable after install" }
if ([string]::IsNullOrWhiteSpace($sitePackages)) { throw "empty rocm_kpack python dir" }
echo "KPACK_PYTHON_DIR=$sitePackages" >> $env:GITHUB_ENV

- name: Configure superbuild
run: |
cmake --preset hipdnn-dev-all -GNinja \
Expand All @@ -132,14 +159,27 @@ jobs:
-DHIPKERNELPROVIDER_ENABLE_ROCKE=ON \
-DHIPDNN_ENABLE_CUDNN_COMPATIBILITY=ON \
-DHIPDNN_ENABLE_KERNEL_INGESTOR=ON \
-DHIPKERNELPROVIDER_KPACK_PYTHON_DIR="$KPACK_PYTHON_DIR" \
-DENABLE_CLANG_TIDY=OFF \
-DFETCHCONTENT_SOURCE_DIR_NANOBIND="$THIRD_PARTY_DEPS_DIR/nanobind-2.12.0" \
-DFETCHCONTENT_SOURCE_DIR_TSL_ROBIN_MAP="$THIRD_PARTY_DEPS_DIR/robin-map-1.4.1" \
-DPython_EXECUTABLE="$VENV_PATH/Scripts/python.exe"
-DPython_EXECUTABLE="$VENV_PATH/Scripts/python.exe" \
-DPython3_EXECUTABLE="$VENV_PATH/Scripts/python.exe"

- name: Build
run: cmake --build build

# Packing is skipped with a STATUS message, so a resolution regression
# would otherwise pass CI while producing no archives.
- name: Verify kpack archives were produced
shell: bash
run: |
set -euo pipefail
shopt -s nullglob
kpacks=(build/dnn-providers/hip-kernel-provider/src/engines/asm_sdpa_engine/.kpack/*.kpack)
test "${#kpacks[@]}" -gt 0 || { echo "no .kpack archives produced" >&2; exit 1; }
printf '%s\n' "${kpacks[@]}"

- name: Add ROCm bin to PATH
run: echo "$ROCM_SDK_PATH/bin" >> "$GITHUB_PATH"

Expand Down Expand Up @@ -269,6 +309,23 @@ jobs:
"$VENV_PATH/bin/python" projects/hipdnn/python/download_third_party_deps.py \
--deps-dir "$THIRD_PARTY_DEPS_DIR"

- name: Install rocm_kpack
run: |
set -euo pipefail
# --force-reinstall: rocm-kpack is version 0.1.0 at every commit, so
# pip would treat the pinned requirement as already satisfied in a
# warm venv. Its deps are installed separately because the pinned
# commit's pyproject omits msgpack.
"$VENV_PATH/bin/pip" install --force-reinstall --no-deps \
"rocm-kpack @ git+https://github.com/ROCm/rocm-kpack@${{ env.KPACK_REF }}"
"$VENV_PATH/bin/pip" install "zstandard>=0.20.0" "msgpack>=1.0.0"
installed=$("$VENV_PATH/bin/python" -c "import importlib.metadata as m, json; print(json.loads(m.distribution('rocm-kpack').read_text('direct_url.json'))['vcs_info']['commit_id'])")
test "$installed" = "${{ env.KPACK_REF }}" || { echo "rocm_kpack is $installed, expected ${{ env.KPACK_REF }}" >&2; exit 1; }
# Derived from the installed module, which also proves it imports.
KPACK_PYTHON_DIR=$("$VENV_PATH/bin/python" -c "import pathlib, rocm_kpack; print(pathlib.Path(rocm_kpack.__file__).parent.parent.as_posix())")
test -n "$KPACK_PYTHON_DIR" || { echo "empty rocm_kpack python dir" >&2; exit 1; }
echo "KPACK_PYTHON_DIR=$KPACK_PYTHON_DIR" >> "$GITHUB_ENV"

- name: Configure superbuild
run: |
cmake --preset hipdnn-dev-all -GNinja \
Expand All @@ -281,14 +338,26 @@ jobs:
-DHIPKERNELPROVIDER_ENABLE_ROCKE=ON \
-DHIPDNN_ENABLE_CUDNN_COMPATIBILITY=ON \
-DHIPDNN_ENABLE_KERNEL_INGESTOR=ON \
-DHIPKERNELPROVIDER_KPACK_PYTHON_DIR="$KPACK_PYTHON_DIR" \
-DCMAKE_CXX_CLANG_TIDY=/usr/bin/clang-tidy-20 \
-DFETCHCONTENT_SOURCE_DIR_NANOBIND="$THIRD_PARTY_DEPS_DIR/nanobind-2.12.0" \
-DFETCHCONTENT_SOURCE_DIR_TSL_ROBIN_MAP="$THIRD_PARTY_DEPS_DIR/robin-map-1.4.1" \
-DPython_EXECUTABLE="$VENV_PATH/bin/python"
-DPython_EXECUTABLE="$VENV_PATH/bin/python" \
-DPython3_EXECUTABLE="$VENV_PATH/bin/python"

- name: Build
run: cmake --build build

# Packing is skipped with a STATUS message, so a resolution regression
# would otherwise pass CI while producing no archives.
- name: Verify kpack archives were produced
run: |
set -euo pipefail
shopt -s nullglob
kpacks=(build/dnn-providers/hip-kernel-provider/src/engines/asm_sdpa_engine/.kpack/*.kpack)
test "${#kpacks[@]}" -gt 0 || { echo "no .kpack archives produced" >&2; exit 1; }
printf '%s\n' "${kpacks[@]}"

- name: Run tests
run: ctest --test-dir build --output-on-failure

Expand Down
134 changes: 134 additions & 0 deletions dnn-providers/hip-kernel-provider/cmake/KpackPython.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Copyright © Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT
#
# Resolves the rocm_kpack Python source for this provider's kpack consumers.
# The build never reaches the network unless HIPKERNELPROVIDER_KPACK_ALLOW_FETCH
# is set, and that fetch is pinned.
#
# Order: HIPKERNELPROVIDER_KPACK_PYTHON_DIR, the deprecated
# ROCKE_KPACK_PYTHON_DIR, a pinned fetch when allowed, else empty. Callers
# decide whether empty is fatal.

include_guard(GLOBAL)

set(HIPKERNELPROVIDER_KPACK_PYTHON_DIR "" CACHE PATH
"Path to the rocm_kpack Python source (the parent of rocm_kpack/): \
<rocm-systems>/shared/kpack/python or <rocm-kpack>/python.")

# Superprojects still pass ROCKE_KPACK_PYTHON_DIR; accept it until they migrate.
# Absolutized here because the alias is untyped and the consumers include this
# module from different directories. The canonical flag wins: the alias writes
# the cache entry only when it is empty or still holds a previously seeded
# value, so a warm build dir tracks a changed alias without overwriting an
# explicit path.
if(ROCKE_KPACK_PYTHON_DIR AND
(NOT HIPKERNELPROVIDER_KPACK_PYTHON_DIR OR
"${HIPKERNELPROVIDER_KPACK_PYTHON_DIR}" STREQUAL "${_KPACK_SEEDED_KPACK_DIR}"))
get_filename_component(_rocke_abs "${ROCKE_KPACK_PYTHON_DIR}"
ABSOLUTE BASE_DIR "${CMAKE_SOURCE_DIR}")
if(NOT "${_rocke_abs}" STREQUAL "${HIPKERNELPROVIDER_KPACK_PYTHON_DIR}")
set(HIPKERNELPROVIDER_KPACK_PYTHON_DIR "${_rocke_abs}" CACHE PATH
"Path to the rocm_kpack Python source (the parent of rocm_kpack/): \
<rocm-systems>/shared/kpack/python or <rocm-kpack>/python." FORCE)
endif()
set(_KPACK_SEEDED_KPACK_DIR "${_rocke_abs}" CACHE INTERNAL
"Canonical kpack path this module last seeded from the deprecated alias.")
message(STATUS "kpack: ROCKE_KPACK_PYTHON_DIR is deprecated; "
"pass HIPKERNELPROVIDER_KPACK_PYTHON_DIR instead.")
endif()

option(HIPKERNELPROVIDER_KPACK_ALLOW_FETCH
"Fetch rocm_kpack when no local source is configured." OFF)

# FetchContent re-fetches a moving ref only on a clean populate (a wiped
# _deps/rocm_kpack-*), so prefer a SHA over a branch.
set(HIPKERNELPROVIDER_KPACK_GIT_REPO "https://github.com/ROCm/rocm-kpack.git"
CACHE STRING "rocm-kpack git repository to fetch (override for a fork).")
set(HIPKERNELPROVIDER_KPACK_GIT_REF "e3483286e751060b3a70b792792cc122632c66e8"
CACHE STRING "rocm-kpack git ref (SHA, tag, or branch) to fetch.")

# kpack_resolve_python_dir(<out_var>)
# Sets <out_var> to a directory containing rocm_kpack/kpack.py, or empty when
# kpack is unavailable and fetching is not permitted. A configured-but-wrong
# path is fatal rather than a fallback to fetching.
function(kpack_resolve_python_dir out_var)
if(HIPKERNELPROVIDER_KPACK_PYTHON_DIR)
get_filename_component(_dir "${HIPKERNELPROVIDER_KPACK_PYTHON_DIR}" ABSOLUTE)
if(NOT EXISTS "${_dir}/rocm_kpack/kpack.py")
message(FATAL_ERROR
"kpack: HIPKERNELPROVIDER_KPACK_PYTHON_DIR is '${_dir}' but "
"'${_dir}/rocm_kpack/kpack.py' does not exist. Point it at the "
"directory containing rocm_kpack/.")
endif()
set(${out_var} "${_dir}" PARENT_SCOPE)
message(STATUS "kpack: using rocm_kpack from ${_dir}")
return()
endif()

if(NOT HIPKERNELPROVIDER_KPACK_ALLOW_FETCH)
set(${out_var} "" PARENT_SCOPE)
return()
endif()

message(STATUS "kpack: fetching "
"${HIPKERNELPROVIDER_KPACK_GIT_REPO}@${HIPKERNELPROVIDER_KPACK_GIT_REF}")
include(FetchContent)
FetchContent_Declare(
rocm_kpack
GIT_REPOSITORY "${HIPKERNELPROVIDER_KPACK_GIT_REPO}"
GIT_TAG "${HIPKERNELPROVIDER_KPACK_GIT_REF}"
)
# Only the source tree is needed, so a bare populate is used. CMP0169
# (CMake >= 3.30) deprecates the single-argument form. PUSH/POP keeps the
# OLD setting from leaking into the including directory scope.
cmake_policy(PUSH)
if(POLICY CMP0169)
cmake_policy(SET CMP0169 OLD)
endif()
FetchContent_GetProperties(rocm_kpack)
if(NOT rocm_kpack_POPULATED)
FetchContent_Populate(rocm_kpack)
endif()
cmake_policy(POP)
if(NOT EXISTS "${rocm_kpack_SOURCE_DIR}/python/rocm_kpack/kpack.py")
message(FATAL_ERROR "kpack: fetched "
"${HIPKERNELPROVIDER_KPACK_GIT_REPO}@${HIPKERNELPROVIDER_KPACK_GIT_REF} "
"but it has no python/rocm_kpack/kpack.py.")
endif()
set(${out_var} "${rocm_kpack_SOURCE_DIR}/python" PARENT_SCOPE)
message(STATUS "kpack: fetched into ${rocm_kpack_SOURCE_DIR}/python")
endfunction()

# kpack_unset_reason(<out_var>)
# The remediation message callers print when resolution returns empty.
function(kpack_unset_reason out_var)
# One argument: multiple set() values build a ;-joined list.
set(${out_var}
"no rocm_kpack source configured. Pass -DHIPKERNELPROVIDER_KPACK_PYTHON_DIR=<rocm-systems>/shared/kpack/python (or <rocm-kpack>/python), or set -DHIPKERNELPROVIDER_KPACK_ALLOW_FETCH=ON to fetch the pinned commit"
PARENT_SCOPE)
endfunction()

# kpack_check_python_deps(<python_exe> <pythonpath> <out_missing>)
# Reports which of pack.py's imports are unavailable, under the same
# interpreter and PYTHONPATH the pack command uses.
#
# Importing the rocm_kpack modules covers their third-party dependencies
# transitively (kpack imports msgpack, compression imports zstandard) and
# catches a rocm_kpack that resolves on disk but fails to import.
#
# Never installs: doing so at configure time would mutate the host
# environment from an unpinned index.
function(kpack_check_python_deps python_exe pythonpath out_missing)
set(_missing "")
foreach(_mod rocm_kpack.compression rocm_kpack.kpack)
execute_process(
COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=${pythonpath}" --
"${python_exe}" -c "import ${_mod}"
RESULT_VARIABLE _rc
OUTPUT_QUIET ERROR_QUIET)
if(NOT _rc EQUAL 0)
list(APPEND _missing "${_mod}")
endif()
endforeach()
set(${out_missing} "${_missing}" PARENT_SCOPE)
endfunction()
Original file line number Diff line number Diff line change
Expand Up @@ -16,68 +16,21 @@ set(HKP_PYTHON_ROOT "${HKP_PKG_DIR}/python")
set(HKP_TOOL "${HKP_PKG_DIR}/tools/hkp_pack.py")
set(HKP_FIXTURES "${HKP_PKG_DIR}/tests/fixtures")

# rocm-kpack source for the FetchContent tiers of hkp_resolve_kpack. The default
# ref is pinned to a known-good SHA for reproducible builds (the tool depends on
# rocm_kpack's prepare_kernel/get_kernel API). Override the ref to test a newer
# kpack: set HIPKERNELPROVIDER_KPACK_GIT_REF to any SHA, tag, or branch (e.g.
# "main"), and HIPKERNELPROVIDER_KPACK_GIT_REPO to fetch from a fork. A branch
# ref is a moving target: FetchContent re-fetches it only on a clean populate
# (a wiped _deps/rocm_kpack-* or build dir) — `cmake --fresh` is NOT sufficient,
# as it clears the cache but leaves _deps/ intact. Pin to a specific newer SHA
# for a deterministic re-fetch.
set(HIPKERNELPROVIDER_KPACK_GIT_REPO "https://github.com/ROCm/rocm-kpack.git"
CACHE STRING "rocm-kpack git repository to fetch (override for a fork).")
set(HIPKERNELPROVIDER_KPACK_GIT_REF "e3483286e751060b3a70b792792cc122632c66e8"
CACHE STRING "rocm-kpack git ref (SHA, tag, or branch) to fetch. Defaults \
to a pinned SHA for reproducibility; set to a branch or newer SHA to test the \
latest tool.")
include(KpackPython)

# ---------------------------------------------------------------------------
# hkp_resolve_kpack(<out_var>)
# 3-tier resolution of the rocm-kpack 'python' directory:
# (1) -DHIPKERNELPROVIDER_KPACK_PYTHON_DIR override,
# (2)/(3) FetchContent of a pinned rocm-kpack commit. Sets <out_var> to the
# resolved python dir. rocm_kpack is load-bearing (the tool cannot pack
# without it), so an unresolvable dependency is a hard error.
# Resolve the rocm_kpack python dir, or hard-fail: this pipeline cannot pack
# without it, so there is no skip path.
# ---------------------------------------------------------------------------
function(hkp_resolve_kpack out_var)
if(DEFINED HIPKERNELPROVIDER_KPACK_PYTHON_DIR AND EXISTS "${HIPKERNELPROVIDER_KPACK_PYTHON_DIR}")
set(${out_var} "${HIPKERNELPROVIDER_KPACK_PYTHON_DIR}" PARENT_SCOPE)
message(STATUS "hkp: using rocm_kpack from HIPKERNELPROVIDER_KPACK_PYTHON_DIR=${HIPKERNELPROVIDER_KPACK_PYTHON_DIR}")
return()
endif()

# Tiers 2/3: FetchContent of rocm-kpack at the repo/ref configured at the top
# of this module (HIPKERNELPROVIDER_KPACK_GIT_REPO/REF). Only the python/ tree
# is consumed (download-only, never configured), so a bare populate is used.
set(_kpack_repo "${HIPKERNELPROVIDER_KPACK_GIT_REPO}")
set(_kpack_tag "${HIPKERNELPROVIDER_KPACK_GIT_REF}")
message(STATUS "hkp: fetching rocm_kpack ${_kpack_repo}@${_kpack_tag}")
include(FetchContent)
FetchContent_Declare(
rocm_kpack
GIT_REPOSITORY "${_kpack_repo}"
GIT_TAG "${_kpack_tag}"
)
# CMP0169 (CMake >= 3.30) deprecates the single-arg FetchContent_Populate;
# keep it valid since only the source tree is needed, not a configured build.
if(POLICY CMP0169)
cmake_policy(SET CMP0169 OLD)
endif()
FetchContent_GetProperties(rocm_kpack)
if(NOT rocm_kpack_POPULATED)
FetchContent_Populate(rocm_kpack)
kpack_resolve_python_dir(_python_dir)
if(NOT _python_dir)
kpack_unset_reason(_reason)
message(FATAL_ERROR "hkp: ${_reason}. rocm_kpack is required to pack "
"descriptors; there is no skip path.")
endif()
if(EXISTS "${rocm_kpack_SOURCE_DIR}/python/rocm_kpack/kpack.py")
set(${out_var} "${rocm_kpack_SOURCE_DIR}/python" PARENT_SCOPE)
message(STATUS "hkp: fetched rocm_kpack into ${rocm_kpack_SOURCE_DIR}/python")
return()
endif()

message(FATAL_ERROR
"hkp: rocm_kpack could not be resolved (override with "
"HIPKERNELPROVIDER_KPACK_PYTHON_DIR or ensure the pinned commit is fetchable). "
"rocm_kpack is required to pack; there is no skip path.")
set(${out_var} "${_python_dir}" PARENT_SCOPE)
endfunction()

# ---------------------------------------------------------------------------
Expand Down
Loading
Loading