From 9a2c80d30fb4f8afa41bd804d02d33cbf4160612 Mon Sep 17 00:00:00 2001 From: "assistant-librarian[bot]" Date: Wed, 14 Jan 2026 16:16:26 +0000 Subject: [PATCH] Merge commit '1fc5a3f3ac6bf204305c089f77a898b1f8765903' into develop --- CMakeLists.txt | 16 +- README.md | 16 ++ example/CMakeLists.txt | 30 +++ script/run-tests.ps1 | 254 ++++++++++++++++++ .../test_grouped_convnd_bwd_weight.cpp | 38 +-- ...est_grouped_convnd_bwd_weight_bilinear.cpp | 8 +- .../test_grouped_convnd_bwd_weight_scale.cpp | 8 +- 7 files changed, 342 insertions(+), 28 deletions(-) create mode 100644 script/run-tests.ps1 diff --git a/CMakeLists.txt b/CMakeLists.txt index eaed7d3509..121c663f64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}") diff --git a/README.md b/README.md index 8a5258bab6..09540ff245 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index aed19c083a..c39f89fcaf 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -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) diff --git a/script/run-tests.ps1 b/script/run-tests.ps1 new file mode 100644 index 0000000000..86cd9f69cf --- /dev/null +++ b/script/run-tests.ps1 @@ -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 +} diff --git a/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight.cpp b/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight.cpp index 5d56615834..0a62091039 100644 --- a/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight.cpp +++ b/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight.cpp @@ -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( - 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( + 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(); diff --git a/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight_bilinear.cpp b/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight_bilinear.cpp index ff025e2dba..80f046a714 100644 --- a/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight_bilinear.cpp +++ b/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight_bilinear.cpp @@ -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(); } diff --git a/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight_scale.cpp b/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight_scale.cpp index dba2fbd5d4..19e1bd7b0f 100644 --- a/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight_scale.cpp +++ b/test/grouped_convnd_bwd_weight/test_grouped_convnd_bwd_weight_scale.cpp @@ -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(); }