Build CK on Windows (#3458)

* CMakeLists.txt hack for Windows.

* Add Windows build instructions.

* Fix  type issue with variadic min function.

* Use std::common_type to fix the variadic min/max functions.

* Enable CPU guard compilation on Windows.

* Suppress warnings related to std::getenv on Windows platform.

* Git ignore the output directory on Windows platform.

* Powershell script for running tests and generating reports.

* Improve test logging.

* Disable non-conv tests.

* Fix Debug build on Windows.

* More debug build changes.

* Update Windows build instructions.

* Enable all tests.

* Test fixes.

* Suppress not found linker options warning.

* Update unsigned long literals and format specifiers to work correctly in Windows

* Fix conv 3D bwd weight bilinear tests on Windows.

* Revert changes on .gitignore.

* Clean-up CMake project file for Windows builds.

* clang-format

* Fix definition of CMAKE_PREFIX_PATH on both Linux and Windows platforms.

* Fix building examples on Windows.

* Update Readme.

* Remove the suppression of the deprecated warnings.

* Remove Windows specific min/max implementations from CK Tile math core.

* Remove unnecessary no-op on Windows.

---------

Co-authored-by: User <user@example.com>
Co-authored-by: Ville Pietilä <none>
Co-authored-by: John Afaganis <john.afaganis@amd.com>
Co-authored-by: Ville Pietilä <>

[ROCm/composable_kernel commit: 1fc5a3f3ac]
This commit is contained in:
Ville Pietilä
2026-01-14 17:31:45 +02:00
committed by GitHub
parent b313b8eaea
commit 2eb573a0e2
7 changed files with 342 additions and 28 deletions

View File

@@ -31,11 +31,12 @@ endif()
# Default installation path
if(NOT WIN32)
set(CMAKE_INSTALL_PREFIX "/opt/rocm" CACHE PATH "")
else()
set(CMAKE_INSTALL_PREFIX "C:/dist/TheRock" CACHE PATH "")
endif()
set(version 1.2.0)
# Check support for CUDA/HIP in Cmake
project(composable_kernel VERSION ${version} LANGUAGES CXX HIP)
project(composable_kernel VERSION ${version} LANGUAGES CXX)
include(CTest)
option(ENABLE_CLANG_CPP_CHECKS "Enables clang tidy, cppcheck" ON)
@@ -162,7 +163,13 @@ execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD OUTPUT_VARIABLE COMMI
configure_file(include/ck/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/ck/version.h)
set(ROCM_SYMLINK_LIBS OFF)
find_package(ROCM REQUIRED PATHS /opt/rocm)
if (WIN32)
find_package(ROCmCMakeBuildTools REQUIRED PATHS C:/dist/TheRock)
set(HIP_PLATFORM "amd" CACHE STRING "HIP platform")
else()
find_package(ROCM REQUIRED PATHS /opt/rocm)
endif()
include(ROCMInstallTargets)
include(ROCMPackageConfigHelpers)
@@ -189,7 +196,10 @@ if(GPU_TARGETS)
else()
set(USER_GPU_TARGETS 0)
endif()
find_package(hip REQUIRED)
enable_language(HIP)
# No assumption that HIP kernels are launched with uniform block size for backward compatibility
# SWDEV-413293 and https://reviews.llvm.org/D155213
math(EXPR hip_VERSION_FLAT "(${hip_VERSION_MAJOR} * 1000 + ${hip_VERSION_MINOR}) * 100000 + ${hip_VERSION_PATCH}")

View File

@@ -137,6 +137,22 @@ Docker images are available on [DockerHub](https://hub.docker.com/r/rocm/composa
```
**[See Note on -j](#notes)**
### Building for Windows
Install TheRock and run CMake configure as
```bash
cmake \
-D CMAKE_PREFIX_PATH="C:/dist/TheRock" \
-D CMAKE_CXX_COMPILER="C:/dist/TheRock/bin/hipcc.exe" \
-D CMAKE_BUILD_TYPE=Release \
-D GPU_TARGETS="gfx1151" \
-G Ninja \
..
```
Use Ninja to build either the whole library or individual targets.
## Optional post-install steps
* Build examples and tests:

View File

@@ -6,6 +6,35 @@ include_directories(BEFORE
${PROJECT_SOURCE_DIR}/library/include
)
if(WIN32)
# On Windows, HIP uses -nostdlib which prevents C runtime linking
# We need legacy_stdio_definitions.lib to provide vfprintf and other legacy C functions
# This is mainly needed for the getopt library.
set(LEGACY_STDIO_SEARCH_PATHS)
# Try to use Visual C++ Tools environment variable (if build executes from Visual Studio Developer Command Prompt)
if(DEFINED ENV{VCToolsInstallDir})
list(APPEND LEGACY_STDIO_SEARCH_PATHS "$ENV{VCToolsInstallDir}/lib/x64")
endif()
# Fallback: Search common Visual Studio installation locations
file(GLOB MSVC_LIB_PATHS "C:/Program Files/Microsoft Visual Studio/*/*/VC/Tools/MSVC/*/lib/x64")
list(APPEND LEGACY_STDIO_SEARCH_PATHS ${MSVC_LIB_PATHS})
# Use find_library to locate the library
find_library(LEGACY_STDIO_LIB legacy_stdio_definitions
PATHS ${LEGACY_STDIO_SEARCH_PATHS}
NO_DEFAULT_PATH
)
if(LEGACY_STDIO_LIB)
message(STATUS "Found legacy_stdio_definitions.lib: ${LEGACY_STDIO_LIB}")
add_link_options("SHELL:-Xlinker \"${LEGACY_STDIO_LIB}\"")
else()
message(WARNING "Could not find legacy_stdio_definitions.lib - examples may fail to link.")
endif()
endif()
add_custom_target(examples)
@@ -216,6 +245,7 @@ function(add_example_executable_no_testing EXAMPLE_NAME FILE_NAME)
set_source_files_properties(${FILE_NAME} PROPERTIES LANGUAGE HIP)
add_executable(${EXAMPLE_NAME} ${FILE_NAME})
target_link_libraries(${EXAMPLE_NAME} PRIVATE utility)
target_link_libraries(${EXAMPLE_NAME} PRIVATE getopt::getopt)
add_dependencies(examples ${EXAMPLE_NAME})
set_property(TARGET ${EXAMPLE_NAME} PROPERTY HIP_ARCHITECTURES ${EX_TARGETS})
rocm_install(TARGETS ${EXAMPLE_NAME} COMPONENT examples)

254
script/run-tests.ps1 Normal file
View File

@@ -0,0 +1,254 @@
<#
.SYNOPSIS
Runs GTest executables and generates a markdown test report.
.DESCRIPTION
This script searches for GTest executables in a specified binary directory,
runs them, and generates a comprehensive markdown report with test results.
.PARAMETER BinaryDirectory
The directory containing the GTest executables.
.PARAMETER TestName
The name pattern of the GTest executable(s). Supports wildcards (e.g., "*test*.exe").
.PARAMETER OutputReport
Optional. The path to the output markdown report file.
Defaults to "test-report.md" in the current directory.
.PARAMETER FullTestOutput
Optional. If specified, includes the full test output for failed tests instead of just error lines.
Defaults to false (only error lines are included).
.PARAMETER ExcludeTests
Optional. Pattern to exclude specific test executables. Supports wildcards (e.g., "*large_cases*").
Test executables matching this pattern will be filtered out and not executed.
.EXAMPLE
.\run-tests.ps1 -BinaryDirectory "C:\build\bin" -TestName "test_*.exe"
.EXAMPLE
.\run-tests.ps1 -BinaryDirectory ".\build" -TestName "*test.exe" -OutputReport "test-results.md"
.EXAMPLE
.\run-tests.ps1 -BinaryDirectory ".\build" -TestName "*test.exe" -FullTestOutput
.EXAMPLE
.\run-tests.ps1 -BinaryDirectory ".\build" -TestName "*test.exe" -ExcludeTests "*large_cases*"
#>
param(
[Parameter(Mandatory=$true)]
[string]$BinaryDirectory,
[Parameter(Mandatory=$true)]
[string]$TestName,
[Parameter(Mandatory=$false)]
[string]$OutputReport = "test-report.md",
[Parameter(Mandatory=$false)]
[switch]$FullTestOutput,
[Parameter(Mandatory=$false)]
[string]$ExcludeTests = ""
)
# Validate binary directory exists
if (-not (Test-Path -Path $BinaryDirectory -PathType Container)) {
Write-Error "Binary directory does not exist: $BinaryDirectory"
exit 1
}
# Find all matching executables
$executables = Get-ChildItem -Path $BinaryDirectory -Filter $TestName -File -Recurse -ErrorAction SilentlyContinue
# Filter out excluded executables if ExcludeTests is specified
if ($ExcludeTests) {
$originalCount = $executables.Count
$executables = $executables | Where-Object { $_.Name -notlike $ExcludeTests }
$excludedCount = $originalCount - $executables.Count
if ($excludedCount -gt 0) {
Write-Host "Excluded $excludedCount executable(s) matching pattern '$ExcludeTests'"
}
}
if ($executables.Count -eq 0) {
Write-Error "No executables found matching pattern '$TestName' (after exclusions) in directory '$BinaryDirectory'"
exit 1
}
Write-Host "Found $($executables.Count) executable(s) to run"
# Initialize counters
$totalTests = 0
$totalPassed = 0
$totalFailed = 0
$failedTestDetails = @()
$executionResults = @()
# Process each executable
foreach ($exe in $executables) {
Write-Host "Running: $($exe.FullName)"
$exeResult = @{
Name = $exe.Name
Path = $exe.FullName
Tests = 0
Passed = 0
Failed = 0
Output = ""
FailedTests = @()
}
try {
# Run the GTest executable
$output = & $exe.FullName --gtest_color=no 2>&1 | Out-String
$exeResult.Output = $output
# Extract total tests run
if ($output -match '\[==========\] Running (\d+) test') {
$exeResult.Tests = [int]$matches[1]
$totalTests += $exeResult.Tests
}
# Extract passed tests
if ($output -match '\[ PASSED \] (\d+) test') {
$exeResult.Passed = [int]$matches[1]
$totalPassed += $exeResult.Passed
}
# Extract failed tests count
if ($output -match '\[ FAILED \] (\d+) test') {
$exeResult.Failed = [int]$matches[1]
$totalFailed += $exeResult.Failed
}
$failedTestPattern = '\[ FAILED \] ([^\r\n\(]+)'
$failedMatches = [regex]::Matches($output, $failedTestPattern)
foreach ($match in $failedMatches) {
if ($match.Groups[1].Value -notmatch '^\d+ test') {
$failedTestName = $match.Groups[1].Value.Trim()
$exeResult.FailedTests += $failedTestName
$parts = $failedTestName -split ", where "
$escapedName = $parts[0]
$runPattern = "\[\s+RUN\s+\]\s+$escapedName\s*[\r\n]+([\s\S]*?)\[\s+FAILED\s+\]\s+$escapedName.*"
$detailsText = ""
if ($output -match $runPattern) {
$testSection = $matches[1]
if ($FullTestOutput) {
$detailsText = $testSection.Trim()
} else {
# Extract only lines containing "error" (case-insensitive)
$errorLines = @()
$lines = $testSection -split "`r?`n"
foreach ($line in $lines) {
if ($line -match 'error') {
$errorLines += $line.Trim()
}
}
if ($errorLines.Count -gt 0) {
$detailsText = $errorLines -join "`n"
} else {
# If no error lines found, show the full section (might contain other useful info)
$detailsText = $testSection.Trim()
if ($detailsText.Length -lt 10) {
$detailsText = "Test failed without detailed error output."
}
}
}
} else {
# If pattern doesn't match, provide a helpful message
$detailsText = "Test failed without detailed error output."
}
$failedTestDetails += @{
Executable = $exe.Name
TestName = $failedTestName
Details = $detailsText
}
}
}
} catch {
Write-Warning "Error running $($exe.Name): $_"
$exeResult.Output = "Error: $_"
}
$executionResults += $exeResult
}
# Generate Markdown Report
$reportContent = @"
# GTest Execution Report
**Generated:** $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
## Summary
| Metric | Count |
|--------|-------|
| **Total Tests Executed** | $totalTests |
| **Tests Passed** | $totalPassed |
| **Tests Failed** | $totalFailed |
| **Success Rate** | $(if ($totalTests -gt 0) { [math]::Round(($totalPassed / $totalTests) * 100, 2) } else { 0 })% |
## Executable Results
"@
foreach ($result in $executionResults) {
# Use emoji/symbols via string concatenation to avoid encoding issues
$passSymbol = [char]0x2705 # ✅ white heavy check mark
$failSymbol = [char]0x274C # ❌ cross mark
$status = if ($result.Failed -eq 0) { "$passSymbol PASSED" } else { "$failSymbol FAILED" }
$reportContent += "`n`n### $($result.Name) $status`n`n"
$reportContent += "- **Tests Run:** $($result.Tests)`n"
$reportContent += "- **Passed:** $($result.Passed)`n"
$reportContent += "- **Failed:** $($result.Failed)`n"
$reportContent += "- **Path:** ``$($result.Path)```n"
}
# Add failed test details section if there are failures
if ($failedTestDetails.Count -gt 0) {
$failSymbol = [char]0x274C # ❌ cross mark
$reportContent += "`n`n---`n`n## Failed Test Details`n"
foreach ($failure in $failedTestDetails) {
$reportContent += "`n`n$failSymbol $($failure.TestName)`n`n"
$reportContent += "$($failure.Details)`n"
}
} else {
$celebrationSymbol = [char]0x1F389 # 🎉 party popper
$reportContent += "`n`n---`n`n$celebrationSymbol All Tests Passed!`n`n"
$reportContent += "No test failures detected.`n"
}
# Add footer
$reportContent += "`n"
# Write report to file
$reportContent | Out-File -FilePath $OutputReport -Encoding UTF8
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Test Execution Complete" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Total Tests: $totalTests" -ForegroundColor White
Write-Host "Passed: $totalPassed" -ForegroundColor Green
Write-Host "Failed: $totalFailed" -ForegroundColor $(if ($totalFailed -gt 0) { "Red" } else { "Green" })
Write-Host "Report saved: $((Get-Item $OutputReport).FullName)" -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Cyan
# Exit with appropriate code
if ($totalFailed -gt 0) {
exit 1
} else {
exit 0
}

View File

@@ -66,20 +66,24 @@ class TestGroupedConvndBwdWeight : public ::testing::Test
auto& param = conv_params[i];
if(!skip_case(split_k))
{
pass = pass && ck::profiler::profile_grouped_conv_bwd_weight_impl<NDimSpatial{},
InLayout,
WeiLayout,
OutLayout,
InDataType,
WeiDataType,
OutDataType>(
2, // do_verification
1, // init_method: integer value
false, // do_log
false, // time_kernel
param,
std::to_string(split_k),
instance_index);
const bool success =
ck::profiler::profile_grouped_conv_bwd_weight_impl<NDimSpatial{},
InLayout,
WeiLayout,
OutLayout,
InDataType,
WeiDataType,
OutDataType>(
2, // do_verification
1, // init_method: integer value
false, // do_log
false, // time_kernel
param,
std::to_string(split_k),
instance_index);
pass = pass && success;
if(!success)
std::cout << "Case " << param << " failed!" << std::endl;
}
}
}
@@ -186,11 +190,11 @@ TYPED_TEST(TestGroupedConvndBwdWeight3d, Test3D)
this->conv_params.push_back(
{3, 2, 32, 128, 256, {1, 1, 1}, {3, 3, 3}, {1, 1, 1}, {1, 1, 1}, {0, 0, 0}, {0, 0, 0}});
this->conv_params.push_back(
{3, 1, 1, 1, 32, {3, 3, 3}, {32, 32, 32}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 1, 32, {3, 3, 3}, {16, 16, 16}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->conv_params.push_back(
{3, 1, 1, 64, 3, {3, 3, 3}, {32, 32, 32}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 64, 3, {3, 3, 3}, {14, 14, 14}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->conv_params.push_back(
{3, 1, 1, 1, 1, {3, 3, 3}, {32, 32, 32}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 1, 1, {3, 3, 3}, {18, 18, 18}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->conv_params.push_back(
{3, 16, 16, 1, 1, {3, 3, 3}, {28, 28, 28}, {2, 2, 2}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->Run();

View File

@@ -311,13 +311,13 @@ TYPED_TEST(TestGroupedConvndBwdWeight3d, Test3D)
this->conv_params.push_back(
{3, 2, 32, 128, 128, {1, 1, 1}, {3, 3, 3}, {1, 1, 1}, {1, 1, 1}, {0, 0, 0}, {0, 0, 0}});
this->conv_params.push_back(
{3, 1, 1, 1, 32, {3, 3, 3}, {32, 32, 32}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 1, 32, {3, 3, 3}, {12, 12, 12}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->conv_params.push_back(
{3, 1, 1, 64, 3, {3, 3, 3}, {32, 32, 32}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 64, 3, {3, 3, 3}, {10, 10, 10}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->conv_params.push_back(
{3, 1, 1, 1, 1, {3, 3, 3}, {32, 32, 32}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 1, 1, {3, 3, 3}, {14, 14, 14}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->conv_params.push_back(
{3, 1, 1, 4, 4, {3, 3, 3}, {14, 28, 28}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 4, 4, {3, 3, 3}, {12, 14, 14}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->Run();
}

View File

@@ -284,12 +284,12 @@ TYPED_TEST(TestGroupedConvndBwdWeight3d, Test3D)
this->conv_params.push_back(
{3, 2, 32, 128, 128, {1, 1, 1}, {3, 3, 3}, {1, 1, 1}, {1, 1, 1}, {0, 0, 0}, {0, 0, 0}});
this->conv_params.push_back(
{3, 1, 1, 1, 32, {3, 3, 3}, {32, 32, 32}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 1, 32, {3, 3, 3}, {16, 16, 16}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->conv_params.push_back(
{3, 1, 1, 64, 3, {3, 3, 3}, {32, 32, 32}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 64, 3, {3, 3, 3}, {14, 14, 14}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->conv_params.push_back(
{3, 1, 1, 1, 1, {3, 3, 3}, {32, 32, 32}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 1, 1, {3, 3, 3}, {18, 18, 18}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->conv_params.push_back(
{3, 1, 1, 4, 4, {3, 3, 3}, {14, 28, 28}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
{3, 1, 1, 4, 4, {3, 3, 3}, {14, 16, 16}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 1, 1}});
this->Run();
}