diff --git a/CMakeLists.txt b/CMakeLists.txt
index b71b3352..c777da70 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,9 +1,12 @@
# select backend
-set(PSZ_BACKEND "CUDA" CACHE STRING "Select the desired backend: CUDA/cuda and ONEAPI/1api.")
+set(PSZ_BACKEND "CUDA" CACHE STRING "Select the desired backend: CUDA/cuda, HIP/hip, ONEAPI/1api.")
if(PSZ_BACKEND STREQUAL "CUDA" OR PSZ_BACKEND STREQUAL "cuda")
cmake_minimum_required(VERSION 3.18)
project(CUSZ LANGUAGES CUDA CXX C VERSION 0.10.0)
+elseif(PSZ_BACKEND STREQUAL "HIP" OR PSZ_BACKEND STREQUAL "hip")
+ cmake_minimum_required(VERSION 3.21)
+ project(HIPSZ LANGUAGES HIP CXX C VERSION 0.10.0)
elseif(PSZ_BACKEND STREQUAL "ONEAPI" OR PSZ_BACKEND STREQUAL "oneapi" OR PSZ_BACKEND STREQUAL "1api")
cmake_minimum_required(VERSION 3.23)
project(DPSZ LANGUAGES CXX C VERSION 0.6.0)
@@ -14,7 +17,15 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
option(BUILD_SHARED_LIBS "prefer shared libaries" ON)
# conditionally enable functionality
-option(PSZ_ACTIVATE_LC "build LC as a submodule" ON)
+# The bundled LC-framework (third_party/lc) is an external CUDA codebase that is
+# not yet ported to HIP; default it OFF on the HIP backend so the core
+# Lorenzo/spline + Huffman/HFR + FZG pipeline builds. It can be force-enabled
+# once LC-framework gains HIP support.
+if(PSZ_BACKEND STREQUAL "HIP" OR PSZ_BACKEND STREQUAL "hip")
+ option(PSZ_ACTIVATE_LC "build LC as a submodule" OFF)
+else()
+ option(PSZ_ACTIVATE_LC "build LC as a submodule" ON)
+endif()
# example and testing
option(PSZ_BUILD_EXAMPLES "build example codes" ON)
@@ -25,6 +36,9 @@ option(PSZ_BUILD_PYBINDING "enable Python binding for specific backend" OFF)
if(PSZ_BACKEND STREQUAL "CUDA" OR PSZ_BACKEND STREQUAL "cuda")
message("[psz::info] CUDA backend has been selected.")
include(cmake/cuda.cmake)
+elseif(PSZ_BACKEND STREQUAL "HIP" OR PSZ_BACKEND STREQUAL "hip")
+ message("[psz::info] HIP (ROCm) backend has been selected.")
+ include(cmake/hip.cmake)
elseif(PSZ_BACKEND STREQUAL "ONEAPI" OR PSZ_BACKEND STREQUAL "oneapi" OR PSZ_BACKEND STREQUAL "1api")
message("[psz::info] oneAPI backend has been selected.")
include(cmake/sycl.cmake)
diff --git a/README.md b/README.md
index fa80da1a..4a773af2 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ pSZ/cuSZ: A GPU-Based Error-Bounded Lossy Compressor for Scientific Data
-pSZ/cuSZ (cuSZ for short) is a GPU implementation of the seminal [SZ algorithm](https://github.com/szcompressor/SZ). It is the *first* GPU-practical framework of error-bounded lossy compression on GPU for scientific data (circa 2020), aiming to improve SZ's throughput on heterogeneous HPC systems. pSZ/cuSZ primarily focuses on CUDA backend support, with other GPU-parallel backends in development. pSZ/cuSZ is formerly known as cuSZ, which is also the short form of its current name.
+pSZ/cuSZ (cuSZ for short) is a GPU implementation of the seminal [SZ algorithm](https://github.com/szcompressor/SZ). It is the *first* GPU-practical framework of error-bounded lossy compression on GPU for scientific data (circa 2020), aiming to improve SZ's throughput on heterogeneous HPC systems. pSZ/cuSZ primarily focuses on CUDA backend support, with other GPU-parallel backends in development, including an AMD GPU (ROCm/HIP) backend selected at build time with `-DPSZ_BACKEND=HIP`. pSZ/cuSZ is formerly known as cuSZ, which is also the short form of its current name.
(c) 2025 by Argonne National Laboratory and Oakland University. See [COPYRIGHT](https://github.com/szcompressor/cuSZ/blob/master/LICENSE) in top-level directory.
diff --git a/cmake/hip-compat/cooperative_groups.h b/cmake/hip-compat/cooperative_groups.h
new file mode 100644
index 00000000..85beb491
--- /dev/null
+++ b/cmake/hip-compat/cooperative_groups.h
@@ -0,0 +1,8 @@
+// Copyright (c) 2026 Advanced Micro Devices, Inc.
+//
+// Shim placed on the HIP include path so sources that #include
+// resolve against HIP's cooperative-groups header.
+#ifndef PSZ_HIP_COMPAT_COOPERATIVE_GROUPS_H
+#define PSZ_HIP_COMPAT_COOPERATIVE_GROUPS_H
+#include
+#endif
diff --git a/cmake/hip-compat/cooperative_groups/memcpy_async.h b/cmake/hip-compat/cooperative_groups/memcpy_async.h
new file mode 100644
index 00000000..5477bc76
--- /dev/null
+++ b/cmake/hip-compat/cooperative_groups/memcpy_async.h
@@ -0,0 +1,9 @@
+// Copyright (c) 2026 Advanced Micro Devices, Inc.
+//
+// Shim for sources that #include . HIP's
+// cooperative-groups support lives in a single header and does not split out a
+// memcpy_async sub-header; redirect to it so the include resolves.
+#ifndef PSZ_HIP_COMPAT_CG_MEMCPY_ASYNC_H
+#define PSZ_HIP_COMPAT_CG_MEMCPY_ASYNC_H
+#include
+#endif
diff --git a/cmake/hip-compat/cuda.h b/cmake/hip-compat/cuda.h
new file mode 100644
index 00000000..cfc02aa1
--- /dev/null
+++ b/cmake/hip-compat/cuda.h
@@ -0,0 +1,9 @@
+// Copyright (c) 2026 Advanced Micro Devices, Inc.
+//
+// Shim placed on the HIP include path so sources that #include resolve
+// against the HIP runtime; the CUDA->HIP translation macros are supplied by the
+// force-included psz_hip_compat.h prelude / the cuda_runtime.h shim.
+#ifndef PSZ_HIP_COMPAT_CUDA_H
+#define PSZ_HIP_COMPAT_CUDA_H
+#include
+#endif
diff --git a/cmake/hip-compat/cuda_fp16.h b/cmake/hip-compat/cuda_fp16.h
new file mode 100644
index 00000000..6c359b2a
--- /dev/null
+++ b/cmake/hip-compat/cuda_fp16.h
@@ -0,0 +1,7 @@
+// Copyright (c) 2026 Advanced Micro Devices, Inc.
+//
+// Shim so sources that #include resolve against HIP's half type.
+#ifndef PSZ_HIP_COMPAT_CUDA_FP16_H
+#define PSZ_HIP_COMPAT_CUDA_FP16_H
+#include
+#endif
diff --git a/cmake/hip-compat/cuda_runtime.h b/cmake/hip-compat/cuda_runtime.h
new file mode 100644
index 00000000..7c38de7f
--- /dev/null
+++ b/cmake/hip-compat/cuda_runtime.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2026 Advanced Micro Devices, Inc.
+//
+// Shim placed on the HIP include path so sources that #include
+// (kernel .cu TUs and host .cc TUs alike) resolve against the HIP runtime and
+// pick up the CUDA->HIP translation macros. Device TUs additionally get the
+// force-included psz_hip_compat.h prelude, but a plain host .cc that only
+// includes is fully covered by this shim alone.
+#ifndef PSZ_HIP_COMPAT_CUDA_RUNTIME_H
+#define PSZ_HIP_COMPAT_CUDA_RUNTIME_H
+
+#include
+
+#include "macro/c_cu2hip_0_translation.h"
+#include "macro/c_cu2hip_1_fix_primitives.h"
+#include "macro/c_cu2hip_2_suppress_warning.h"
+
+#endif
diff --git a/cmake/hip-compat/cuda_runtime_api.h b/cmake/hip-compat/cuda_runtime_api.h
new file mode 100644
index 00000000..f0218248
--- /dev/null
+++ b/cmake/hip-compat/cuda_runtime_api.h
@@ -0,0 +1,9 @@
+// Copyright (c) 2026 Advanced Micro Devices, Inc.
+//
+// Shim so sources that #include resolve against the HIP
+// runtime. The CUDA->HIP translation macros are supplied by the cuda_runtime.h
+// shim / the force-included psz_hip_compat.h prelude.
+#ifndef PSZ_HIP_COMPAT_CUDA_RUNTIME_API_H
+#define PSZ_HIP_COMPAT_CUDA_RUNTIME_API_H
+#include
+#endif
diff --git a/cmake/hip-compat/curand.h b/cmake/hip-compat/curand.h
new file mode 100644
index 00000000..26636c16
--- /dev/null
+++ b/cmake/hip-compat/curand.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2026 Advanced Micro Devices, Inc.
+//
+// Shim placed on the HIP include path so sources that #include
+// resolve against hipRAND. The cuRAND host-generator API used by cuSZ
+// (portable/src/utils/rand.cu.cc) maps 1:1 onto hipRAND.
+#ifndef PSZ_HIP_COMPAT_CURAND_H
+#define PSZ_HIP_COMPAT_CURAND_H
+
+#include
+
+#define curandGenerator_t hiprandGenerator_t
+#define curandCreateGenerator hiprandCreateGenerator
+#define curandSetPseudoRandomGeneratorSeed hiprandSetPseudoRandomGeneratorSeed
+#define curandGenerateUniform hiprandGenerateUniform
+#define curandGenerateUniformDouble hiprandGenerateUniformDouble
+#define CURAND_RNG_PSEUDO_DEFAULT HIPRAND_RNG_PSEUDO_DEFAULT
+
+#endif
diff --git a/cmake/hip-compat/psz_hip_compat.h b/cmake/hip-compat/psz_hip_compat.h
new file mode 100644
index 00000000..3dcf02ff
--- /dev/null
+++ b/cmake/hip-compat/psz_hip_compat.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2026 Advanced Micro Devices, Inc.
+//
+// HIP/ROCm backend compatibility prelude for cuSZ.
+//
+// This header is force-included (compiler -include) into every translation
+// unit compiled for the HIP backend. The kernel and host sources are written
+// in CUDA spelling and reused as-is for HIP (single-source, no .hip mirrors);
+// this prelude pulls in the HIP runtime and the CUDA->HIP translation macros so
+// those sources compile unchanged against ROCm. It is never on the CUDA or SYCL
+// include path, so those backends are unaffected.
+
+#ifndef PSZ_HIP_COMPAT_H
+#define PSZ_HIP_COMPAT_H
+
+#include
+#include
+
+#include "macro/c_cu2hip_0_translation.h"
+#include "macro/c_cu2hip_1_fix_primitives.h"
+#include "macro/c_cu2hip_2_suppress_warning.h"
+
+#endif /* PSZ_HIP_COMPAT_H */
diff --git a/cmake/hip.cmake b/cmake/hip.cmake
new file mode 100644
index 00000000..91622d33
--- /dev/null
+++ b/cmake/hip.cmake
@@ -0,0 +1,349 @@
+# ------------------------------------------------------------------------------
+# HIP (ROCm) backend. Mirrors cmake/cuda.cmake: the same single-source
+# .cu/.cu.inl kernel and host files are reused and marked LANGUAGE HIP, with the
+# CUDA->HIP translation supplied by the force-included cmake/hip-compat prelude
+# and the portable c_cu2hip macros. Library targets keep their _cu names so the
+# shared install/test plumbing is backend-agnostic.
+# ------------------------------------------------------------------------------
+
+add_compile_definitions(
+ PSZ_USE_HIP
+ _PORTABLE_USE_HIP
+)
+
+find_package(hip REQUIRED)
+find_package(hiprand REQUIRED)
+find_package(rocthrust REQUIRED)
+find_package(rocprim REQUIRED)
+
+include(GNUInstallDirs)
+include(CTest)
+
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/psz/src/cusz_version.h.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/psz/include/cusz_version.h"
+ @ONLY
+)
+
+# All .cu/.cu.inl sources are compiled as HIP without renaming. A single helper
+# marks a list of sources LANGUAGE HIP.
+function(psz_mark_hip)
+ set_source_files_properties(${ARGN} PROPERTIES LANGUAGE HIP)
+endfunction()
+
+# ------------------------------------------------------------------------------
+# Common compile settings (interface target)
+# ------------------------------------------------------------------------------
+
+add_library(psz_cu_compile_settings INTERFACE)
+
+target_compile_features(psz_cu_compile_settings
+ INTERFACE
+ cxx_std_17
+)
+
+target_compile_definitions(psz_cu_compile_settings
+ INTERFACE
+ $<$:__STRICT_ANSI__>
+ # ROCm/TheRock amd_hip_bf16.h defines __shfl_*_sync bf16 overloads that
+ # clash with the templated forms from amd_warp_sync_functions.h on Windows;
+ # the c_cu2hip macros redirect __shfl_*_sync to __shfl_* so suppressing the
+ # bf16 overloads is safe.
+ $<$:HIP_DISABLE_WARP_SYNC_BUILTINS>
+)
+
+# Force-include the HIP compatibility prelude (hip runtime + cooperative groups
+# + CUDA->HIP translation macros) and put the include shims (cuda_runtime.h,
+# cooperative_groups.h, curand.h) ahead of the rest of the include path.
+target_compile_options(psz_cu_compile_settings
+ INTERFACE
+ $<$:-Wno-deprecated-declarations>
+ $<$:-include;${CMAKE_CURRENT_SOURCE_DIR}/cmake/hip-compat/psz_hip_compat.h>
+)
+
+target_include_directories(psz_cu_compile_settings
+ INTERFACE
+ $
+ $
+ $
+ $
+ $
+ $
+ $
+ $
+ $
+ $
+ $
+ $
+ $
+)
+
+# ------------------------------------------------------------------------------
+# Dependencies (installed or fallback)
+# ------------------------------------------------------------------------------
+
+find_package(PORTABLE QUIET)
+if(NOT TARGET PORTABLE::PORTABLE AND NOT TARGET PORTABLE)
+ add_subdirectory(portable)
+endif()
+
+set(_PORTABLE_TARGET "")
+if(TARGET PORTABLE::PORTABLE)
+ set(_PORTABLE_TARGET PORTABLE::PORTABLE)
+elseif(TARGET PORTABLE)
+ set(_PORTABLE_TARGET PORTABLE)
+else()
+ message(FATAL_ERROR
+ "PORTABLE target not available. Provide PORTABLE or add the portable subdirectory."
+ )
+endif()
+
+if(NOT TARGET DEPS::deps)
+ add_library(DEPS::deps ALIAS "${_PORTABLE_TARGET}")
+endif()
+
+target_link_libraries(psz_cu_compile_settings
+ INTERFACE
+ DEPS::deps
+)
+
+find_package(FZG QUIET)
+if(NOT TARGET FZG::fzg_cu AND NOT FZG_FOUND)
+ add_subdirectory(codec/fzg)
+endif()
+
+find_package(EVAL QUIET)
+if(NOT TARGET EVAL::utils_headers AND NOT EVAL_FOUND)
+ add_subdirectory(utils)
+endif()
+
+# ------------------------------------------------------------------------------
+# Libraries
+# ------------------------------------------------------------------------------
+
+find_package(PHF QUIET)
+if(NOT TARGET PHF::phf_cu AND NOT PHF_FOUND)
+ add_subdirectory(codec/hf)
+endif()
+if(TARGET phf_cu AND NOT TARGET PSZ::CUDA::phf)
+ add_library(PSZ::CUDA::phf ALIAS phf_cu)
+ add_library(CUSZ::phf ALIAS phf_cu)
+elseif(TARGET PHF::phf_cu AND NOT TARGET PSZ::CUDA::phf)
+ add_library(PSZ::CUDA::phf ALIAS PHF::phf_cu)
+ add_library(CUSZ::phf ALIAS PHF::phf_cu)
+endif()
+
+add_library(psz_seq_core
+ psz/src/kernel/lrz.seq.cc
+ psz/src/kernel/hist_generic.seq.cc
+ psz/src/kernel/histsp.seq.cc
+ psz/src/kernel/spvn.seq.cc
+)
+target_link_libraries(psz_seq_core
+ PUBLIC
+ psz_cu_compile_settings
+)
+
+set(psz_cu_mem_sources
+ psz/src/buf_comp.cc
+ psz/src/buf_comp_dummy.cu
+)
+psz_mark_hip(psz/src/buf_comp_dummy.cu)
+add_library(psz_cu_mem ${psz_cu_mem_sources})
+target_link_libraries(psz_cu_mem
+ PUBLIC
+ psz_cu_compile_settings
+ EVAL::stat_cu
+ DEPS::deps
+ PHF::phf_cu
+ hip::device
+)
+
+set(psz_cu_core_sources
+ psz/src/compile/hist_generic.cu
+ psz/src/compile/histsp.cu
+ psz/src/compile/proto_lrz_c.cu
+ psz/src/compile/proto_lrz_x.cu
+ psz/src/compile/spvn.cu
+ psz/src/compile/lrz_c.cu
+ psz/src/compile/lrz_x.cu
+ psz/src/compile/spl_y24_c_u1.cu
+ psz/src/compile/spl_y24_c_u2.cu
+ psz/src/compile/spl_y24_x_u1.cu
+ psz/src/compile/spl_y24_x_u2.cu
+ psz/src/compile/spl_y25_c_u1.cu
+ psz/src/compile/spl_y25_c_u2.cu
+ psz/src/compile/spl_y25_x_u1.cu
+ psz/src/compile/spl_y25_x_u2.cu
+)
+psz_mark_hip(${psz_cu_core_sources})
+add_library(psz_cu_core ${psz_cu_core_sources})
+target_link_libraries(psz_cu_core
+ PUBLIC
+ psz_cu_compile_settings
+ psz_cu_mem
+ PHF::phf_cu
+ hip::device
+)
+
+# verinfo.cu (NVML) and verinfo_nv.cu (CUDA driver-API deviceQuery) are replaced
+# by verinfo_hip.cu, which reports the HIP/ROCm toolchain and device properties.
+set(psz_cu_utils_sources
+ psz/src/cli/verinfo.cc
+ psz/src/cli/verinfo_hip.cu
+ psz/src/cli/context.cc
+ psz/src/header.c
+)
+psz_mark_hip(psz/src/cli/verinfo_hip.cu)
+add_library(psz_cu_utils ${psz_cu_utils_sources})
+target_link_libraries(psz_cu_utils
+ PUBLIC
+ psz_cu_compile_settings
+ PHF::phf_cu
+ EVAL::stat_seq
+ EVAL::viewer_cu
+ hip::device
+)
+
+if(PSZ_ACTIVATE_LC)
+ add_compile_definitions(PSZ_USE_LC_FIXED)
+ set(lc_gen_sources
+ third_party/lc_gen/lc_connector.cu
+ third_party/lc_gen/comp-tcms.cu third_party/lc_gen/decomp-tcms.cu
+ third_party/lc_gen/comp-bitr.cu third_party/lc_gen/decomp-bitr.cu
+ third_party/lc_gen/comp-rtr.cu third_party/lc_gen/decomp-rtr.cu
+ )
+ psz_mark_hip(${lc_gen_sources})
+ add_library(lc_gen ${lc_gen_sources})
+ target_compile_options(lc_gen PRIVATE
+ $<$:-O3 -ffp-contract=off>
+ $<$:-O3 -march=native -mno-fma>
+ )
+ target_link_libraries(lc_gen PUBLIC psz_cu_compile_settings hip::device)
+endif()
+
+add_library(cusz
+ psz/src/compressor.cc
+ psz/src/libcusz.cc
+)
+target_link_libraries(cusz
+ PUBLIC
+ psz_cu_compile_settings
+ psz_cu_core
+ psz_cu_mem
+ psz_cu_utils
+ EVAL::stat_cu
+ PHF::phf_cu
+ FZG::fzg_cu
+ hip::device
+)
+if(PSZ_ACTIVATE_LC)
+ target_link_libraries(cusz PUBLIC lc_gen)
+endif()
+
+# ------------------------------------------------------------------------------
+# Executable
+# ------------------------------------------------------------------------------
+
+add_executable(cusz-bin psz/src/cli/cli.cc psz/src/cli/executor.cc)
+psz_mark_hip(psz/src/cli/cli.cc psz/src/cli/executor.cc)
+target_link_libraries(cusz-bin PRIVATE cusz)
+set_target_properties(cusz-bin PROPERTIES OUTPUT_NAME cusz)
+
+# ------------------------------------------------------------------------------
+# Examples / Tests
+# ------------------------------------------------------------------------------
+
+if(PSZ_BUILD_EXAMPLES)
+ add_subdirectory(example)
+endif()
+
+if(BUILD_TESTING)
+ add_subdirectory(test)
+
+ include(ProcessorCount)
+ ProcessorCount(N)
+ if(N EQUAL 0)
+ set(N 8)
+ endif()
+ add_custom_target(check
+ COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure --parallel ${N}
+ USES_TERMINAL
+ )
+endif()
+
+# ------------------------------------------------------------------------------
+# Installation (CUSZ:: namespace, back compat)
+# ------------------------------------------------------------------------------
+
+install(TARGETS psz_cu_compile_settings EXPORT CUSZTargets)
+
+install(TARGETS
+ psz_seq_core
+ psz_cu_core
+ psz_cu_mem
+ psz_cu_utils
+ cusz
+ eval_cu
+ eval_seq
+ eval_viewer_cu
+ EXPORT CUSZTargets
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+)
+if(PSZ_ACTIVATE_LC)
+ install(TARGETS
+ lc_gen
+ EXPORT CUSZTargets
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+ )
+endif()
+
+install(TARGETS
+ cusz-bin
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
+
+install(
+ EXPORT CUSZTargets
+ NAMESPACE CUSZ::
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CUSZ
+)
+
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CUSZConfig.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/CUSZConfig.cmake"
+ INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CUSZ
+)
+
+write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/CUSZConfigVersion.cmake"
+ VERSION "${PROJECT_VERSION}"
+ COMPATIBILITY AnyNewerVersion
+)
+
+install(FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/CUSZConfig.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/CUSZConfigVersion.cmake"
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CUSZ
+)
+
+install(DIRECTORY
+ portable/include
+ psz/include
+ codec/hf/include
+ codec/fzg/include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cusz
+)
+
+install(FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/psz/include/cusz_version.h"
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cusz
+)
diff --git a/cmake/probe.cmake b/cmake/probe.cmake
index 892f3231..4598ed35 100644
--- a/cmake/probe.cmake
+++ b/cmake/probe.cmake
@@ -14,6 +14,10 @@ if(PSZ_BACKEND STREQUAL "CUDA" OR PSZ_BACKEND STREQUAL "cuda")
message("[psz::info] $\{CMAKE_CUDA_COMPILER_ID\}: " ${CMAKE_CUDA_COMPILER_ID})
message("[psz::info] $\{CMAKE_CUDA_COMPILER_VERSION\}: " ${CMAKE_CUDA_COMPILER_VERSION})
message("[psz::info] $\{CMAKE_CUDA_ARCHITECTURES\}: " ${CMAKE_CUDA_ARCHITECTURES})
+elseif(PSZ_BACKEND STREQUAL "HIP" OR PSZ_BACKEND STREQUAL "hip")
+ message("[psz::info] $\{CMAKE_HIP_COMPILER\}: " ${CMAKE_HIP_COMPILER})
+ message("[psz::info] $\{CMAKE_HIP_COMPILER_VERSION\}: " ${CMAKE_HIP_COMPILER_VERSION})
+ message("[psz::info] $\{CMAKE_HIP_ARCHITECTURES\}: " ${CMAKE_HIP_ARCHITECTURES})
elseif(PSZ_BACKEND STREQUAL "ONEAPI" OR PSZ_BACKEND STREQUAL "oneapi" OR PSZ_BACKEND STREQUAL "1api")
else()
message(FATAL_ERROR "Unsupported PSZ_BACKEND selected: ${PSZ_BACKEND}")
diff --git a/codec/fzg/CMakeLists.txt b/codec/fzg/CMakeLists.txt
index 5dd3a485..4a27b4ef 100644
--- a/codec/fzg/CMakeLists.txt
+++ b/codec/fzg/CMakeLists.txt
@@ -1,12 +1,20 @@
cmake_minimum_required(VERSION 3.18)
-project(FZG
- VERSION 0.1.0
- LANGUAGES CXX CUDA
-)
-
-find_package(CUDAToolkit REQUIRED)
+if(NOT PSZ_BACKEND)
+ set(PSZ_BACKEND "CUDA")
+endif()
+string(TOUPPER "${PSZ_BACKEND}" FZG_BACKEND_UPPER)
+
+if(FZG_BACKEND_UPPER STREQUAL "HIP")
+ project(FZG VERSION 0.1.0 LANGUAGES CXX HIP)
+ find_package(hip REQUIRED)
+ set(_FZG_GPU_RT hip::device)
+else()
+ project(FZG VERSION 0.1.0 LANGUAGES CXX CUDA)
+ find_package(CUDAToolkit REQUIRED)
+ set(_FZG_GPU_RT CUDA::cudart)
+endif()
# ------------------------------------------------------------------------------
# Dependency: PORTABLE (installed or fallback)
@@ -39,6 +47,16 @@ add_library(fzg_cu
add_library(FZG::fzg_cu ALIAS fzg_cu)
+if(FZG_BACKEND_UPPER STREQUAL "HIP")
+ set_source_files_properties(src/fzg_kernel.cu PROPERTIES LANGUAGE HIP)
+ target_compile_definitions(fzg_cu PRIVATE FZG_USE_HIP _PORTABLE_USE_HIP)
+ # Host .cc TUs (fzg_hl.cc, fzg_buf.cc) and the HIP kernel TU all need the
+ # CUDA->HIP translation prelude and the cuda_runtime.h shim.
+ target_include_directories(fzg_cu PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/hip-compat)
+ target_compile_options(fzg_cu PRIVATE
+ -include ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/hip-compat/psz_hip_compat.h)
+endif()
+
target_include_directories(fzg_cu
PUBLIC
$
@@ -47,7 +65,7 @@ target_include_directories(fzg_cu
target_link_libraries(fzg_cu
PUBLIC
- CUDA::cudart
+ ${_FZG_GPU_RT}
PORTABLE
)
diff --git a/codec/hf/CMakeLists.txt b/codec/hf/CMakeLists.txt
index 097a609c..175f3b5b 100644
--- a/codec/hf/CMakeLists.txt
+++ b/codec/hf/CMakeLists.txt
@@ -1,12 +1,22 @@
cmake_minimum_required(VERSION 3.18)
-project(PHF
- VERSION 0.1.0
- LANGUAGES CXX CUDA
-)
-
-find_package(CUDAToolkit REQUIRED)
+if(NOT PSZ_BACKEND)
+ set(PSZ_BACKEND "CUDA")
+endif()
+string(TOUPPER "${PSZ_BACKEND}" PHF_BACKEND_UPPER)
+
+if(PHF_BACKEND_UPPER STREQUAL "HIP")
+ project(PHF VERSION 0.1.0 LANGUAGES CXX HIP)
+ find_package(hip REQUIRED)
+ set(_PHF_GPU_RT hip::device)
+ set(_PHF_GPU_DEFINE PHF_USE_HIP)
+else()
+ project(PHF VERSION 0.1.0 LANGUAGES CXX CUDA)
+ find_package(CUDAToolkit REQUIRED)
+ set(_PHF_GPU_RT CUDA::cudart)
+ set(_PHF_GPU_DEFINE PHF_USE_CUDA)
+endif()
find_package(PORTABLE QUIET)
if(NOT TARGET PORTABLE AND NOT TARGET PORTABLE::PORTABLE)
@@ -35,7 +45,7 @@ endif()
target_compile_definitions(${PHF_PORTABLE_TARGET}
INTERFACE
- PHF_USE_CUDA
+ ${_PHF_GPU_DEFINE}
)
# ------------------------------------------------------------------------------
@@ -80,6 +90,19 @@ add_library(phf_cu
)
add_library(PHF::phf_cu ALIAS phf_cu)
+if(PHF_BACKEND_UPPER STREQUAL "HIP")
+ get_target_property(_phf_sources phf_cu SOURCES)
+ foreach(_src ${_phf_sources})
+ if(_src MATCHES "\\.cu$")
+ set_source_files_properties(${_src} PROPERTIES LANGUAGE HIP)
+ endif()
+ endforeach()
+ target_compile_definitions(phf_cu PRIVATE PHF_USE_HIP _PORTABLE_USE_HIP)
+ target_include_directories(phf_cu PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/hip-compat)
+ target_compile_options(phf_cu PRIVATE
+ -include ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/hip-compat/psz_hip_compat.h)
+endif()
+
target_include_directories(phf_cu
PUBLIC
$
@@ -91,7 +114,7 @@ target_include_directories(phf_cu
target_link_libraries(phf_cu
PUBLIC
- CUDA::cudart
+ ${_PHF_GPU_RT}
${PHF_PORTABLE_TARGET}
)
diff --git a/codec/hf/src/hf_buf.cc b/codec/hf/src/hf_buf.cc
index f7eaa1b5..7561b0a0 100644
--- a/codec/hf/src/hf_buf.cc
+++ b/codec/hf/src/hf_buf.cc
@@ -14,7 +14,14 @@
namespace {
struct phf_eager_module_loading_init {
- phf_eager_module_loading_init() { setenv("CUDA_MODULE_LOADING", "EAGER", /*overwrite=*/0); }
+ phf_eager_module_loading_init()
+ {
+#if defined(_WIN32)
+ if (not std::getenv("CUDA_MODULE_LOADING")) _putenv_s("CUDA_MODULE_LOADING", "EAGER");
+#else
+ setenv("CUDA_MODULE_LOADING", "EAGER", /*overwrite=*/0);
+#endif
+ }
};
phf_eager_module_loading_init _phf_eager_module_loading_init_singleton;
} // namespace
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
index dbed245a..08687925 100644
--- a/example/CMakeLists.txt
+++ b/example/CMakeLists.txt
@@ -1,5 +1,7 @@
if(PSZ_BACKEND STREQUAL "CUDA" OR PSZ_BACKEND STREQUAL "cuda")
include(cmake/cuda-example.cmake)
+elseif(PSZ_BACKEND STREQUAL "HIP" OR PSZ_BACKEND STREQUAL "hip")
+ include(cmake/hip-example.cmake)
elseif(PSZ_BACKEND STREQUAL "ONEAPI" OR PSZ_BACKEND STREQUAL "oneapi" OR PSZ_BACKEND STREQUAL "1api")
include(cmake/sycl-example.cmake)
endif()
diff --git a/example/cmake/hip-example.cmake b/example/cmake/hip-example.cmake
new file mode 100644
index 00000000..2092284b
--- /dev/null
+++ b/example/cmake/hip-example.cmake
@@ -0,0 +1,25 @@
+# HIP (ROCm) example/driver binaries. Mirrors cuda-example.cmake; the bin_hf
+# binary backs the bin_hf ctest matrix. CUDA::cupti (profiling-only) is dropped
+# on the HIP path. The .cc sources are reused unchanged; the cuda_runtime.h
+# shim and c_cu2hip macros (carried by cusz's psz_cu_compile_settings) translate
+# their CUDA API calls.
+#
+# bin_pred2 is omitted: pred_run.hh uses cudaFree as a std::unique_ptr deleter
+# (decltype(&cudaFree)), but cudaFree is a macro on the HIP path, not a function
+# symbol. bin_pred2 is not used by any HIP ctest (its tests are in
+# cuda-test-bin_pred.cmake, which is CUDA-only).
+
+add_library(example_utils2 src/ex_utils2.cc)
+target_link_libraries(example_utils2 PRIVATE cusz)
+
+add_executable(bin_pred1 src/bin_pred1.cc)
+target_link_libraries(bin_pred1 PRIVATE cusz)
+
+add_executable(bin_hf src/bin_phf.cc)
+target_link_libraries(bin_hf PRIVATE cusz hip::device PORTABLE::testutils)
+
+add_executable(bin_hist src/bin_hist.cc)
+target_link_libraries(bin_hist PRIVATE cusz hip::device)
+
+add_executable(batch_run src/batch_run.cc)
+target_link_libraries(batch_run PRIVATE cusz example_utils2 hip::device)
diff --git a/portable/CMakeLists.txt b/portable/CMakeLists.txt
index 508ba8f8..9a2b9e62 100644
--- a/portable/CMakeLists.txt
+++ b/portable/CMakeLists.txt
@@ -1,12 +1,25 @@
cmake_minimum_required(VERSION 3.18)
-project(PORTABLE
- VERSION 0.1.0
- LANGUAGES CXX CUDA
-)
-
-find_package(CUDAToolkit REQUIRED)
+if(NOT PSZ_BACKEND)
+ set(PSZ_BACKEND "CUDA")
+endif()
+string(TOUPPER "${PSZ_BACKEND}" PORTABLE_BACKEND_UPPER)
+
+if(PORTABLE_BACKEND_UPPER STREQUAL "HIP")
+ project(PORTABLE VERSION 0.1.0 LANGUAGES CXX HIP)
+ find_package(hip REQUIRED)
+ find_package(hiprand REQUIRED)
+ set(_PORTABLE_GPU_DEFINE _PORTABLE_USE_HIP)
+ set(_PORTABLE_GPU_RT hip::host)
+ set(_PORTABLE_GPU_RAND hip::hiprand)
+else()
+ project(PORTABLE VERSION 0.1.0 LANGUAGES CXX CUDA)
+ find_package(CUDAToolkit REQUIRED)
+ set(_PORTABLE_GPU_DEFINE _PORTABLE_USE_CUDA)
+ set(_PORTABLE_GPU_RT CUDA::cudart)
+ set(_PORTABLE_GPU_RAND CUDA::curand)
+endif()
# ------------------------------------------------------------------------------
# Header-only interface target
@@ -21,7 +34,7 @@ target_compile_features(PORTABLE
target_compile_definitions(PORTABLE
INTERFACE
- _PORTABLE_USE_CUDA
+ ${_PORTABLE_GPU_DEFINE}
)
target_include_directories(PORTABLE
@@ -41,11 +54,17 @@ add_library(portable_testutils
src/utils/synth.cc
)
add_library(PORTABLE::testutils ALIAS portable_testutils)
+if(PORTABLE_BACKEND_UPPER STREQUAL "HIP")
+ # rand.cu.cc is compiled as host C++ and includes ; the hip-compat
+ # shim maps it onto hipRAND.
+ target_include_directories(portable_testutils
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/hip-compat)
+endif()
target_link_libraries(portable_testutils
PUBLIC
PORTABLE
- CUDA::cudart
- CUDA::curand
+ ${_PORTABLE_GPU_RT}
+ ${_PORTABLE_GPU_RAND}
)
# ------------------------------------------------------------------------------
diff --git a/portable/include/c_type.h b/portable/include/c_type.h
index 9ee4f8cb..c87aefc7 100644
--- a/portable/include/c_type.h
+++ b/portable/include/c_type.h
@@ -12,7 +12,7 @@ extern "C" {
// enum for device type
typedef enum { CPU, NVGPU, INTELGPU } _ptb_device;
// enum for runtime type
-typedef enum { SEQ, SIMD, OPENMP, CUDA, SYCL, THRUST_DPL } _ptb_runtime;
+typedef enum { SEQ, SIMD, OPENMP, CUDA, HIP, SYCL, THRUST_DPL } _ptb_runtime;
// enum for toolkit type
typedef enum { VENDOR_NATIVE, KOKKOS, ONEAPI } _ptb_toolkit;
diff --git a/portable/include/cxx_typing.h b/portable/include/cxx_typing.h
index ac8fc416..d8cc286e 100644
--- a/portable/include/cxx_typing.h
+++ b/portable/include/cxx_typing.h
@@ -49,7 +49,11 @@ template <> struct TypeSym { static const _ptb_dtype type = U1; static const
template <> struct TypeSym { static const _ptb_dtype type = U2; static const int width = sizeof(u2); };
template <> struct TypeSym { static const _ptb_dtype type = U4; static const int width = sizeof(u4); };
template <> struct TypeSym { static const _ptb_dtype type = U8; static const int width = sizeof(u8); };
+#if !defined(_WIN32)
+// On Windows (MSVC/LLP64) uint64_t and unsigned long long are the same type, so a
+// separate TypeSym would redefine TypeSym; on LP64 they are distinct.
template <> struct TypeSym { static const _ptb_dtype type = ULL; static const int width = sizeof(ull); };
+#endif
// clang-format on
} // namespace _ptb
diff --git a/portable/include/macro/c_cu2hip_0_translation.h b/portable/include/macro/c_cu2hip_0_translation.h
index 8ff4891b..1286dacf 100644
--- a/portable/include/macro/c_cu2hip_0_translation.h
+++ b/portable/include/macro/c_cu2hip_0_translation.h
@@ -44,4 +44,16 @@
#define cudaGetErrorString(...) hipGetErrorString(__VA_ARGS__)
#define cudaGetLastError(...) hipGetLastError(__VA_ARGS__)
-#define cudaDeviceReset(...) hipDeviceReset(__VA_ARGS__)
\ No newline at end of file
+#define cudaDeviceReset(...) hipDeviceReset(__VA_ARGS__)
+
+#define cudaMemsetAsync(...) hipMemsetAsync(__VA_ARGS__)
+#define cudaGetSymbolAddress(...) hipGetSymbolAddress(__VA_ARGS__)
+#define cudaOccupancyMaxActiveBlocksPerMultiprocessor(...) \
+ hipOccupancyMaxActiveBlocksPerMultiprocessor(__VA_ARGS__)
+
+#define cudaEvent_t hipEvent_t
+#define cudaEventCreate(...) hipEventCreate(__VA_ARGS__)
+#define cudaEventDestroy(...) hipEventDestroy(__VA_ARGS__)
+#define cudaEventRecord(...) hipEventRecord(__VA_ARGS__)
+#define cudaEventSynchronize(...) hipEventSynchronize(__VA_ARGS__)
+#define cudaEventElapsedTime(...) hipEventElapsedTime(__VA_ARGS__)
\ No newline at end of file
diff --git a/portable/include/macro/c_cu2hip_1_fix_primitives.h b/portable/include/macro/c_cu2hip_1_fix_primitives.h
index 5a2182db..3ce3e14d 100644
--- a/portable/include/macro/c_cu2hip_1_fix_primitives.h
+++ b/portable/include/macro/c_cu2hip_1_fix_primitives.h
@@ -1,6 +1,25 @@
// clang-format off
-#define __shfl_sync(MASK, VAR, SRC_LANE, WIDTH) __shfl(VAR, SRC_LANE, WIDTH)
-#define __shfl_up_sync(MASK, VAR, DELTA, WIDTH) __shfl_up(VAR, DELTA, WIDTH)
-#define __shfl_down_sync(MASK, VAR, DELTA, WIDTH) __shfl_down(VAR, DELTA, WIDTH)
-#define __shfl_xor_sync(MASK, VAR, LANE_MASK, WIDTH) __shfl_xor(VAR, LANE_MASK, WIDTH)
-// clang-format on
\ No newline at end of file
+
+// Warp shuffle intrinsics: HIP's __shfl_*_sync require a 64-bit lane mask, but
+// the CUDA-spelled call sites pass a 32-bit mask (0xffffffff). Redirect to the
+// maskless __shfl_* forms instead. Variadic so both the 3-arg (default-width)
+// and 4-arg (explicit-width) call forms map through.
+#define __shfl_sync(MASK, VAR, SRC_LANE, ...) __shfl(VAR, SRC_LANE, ##__VA_ARGS__)
+#define __shfl_up_sync(MASK, VAR, DELTA, ...) __shfl_up(VAR, DELTA, ##__VA_ARGS__)
+#define __shfl_down_sync(MASK, VAR, DELTA, ...) __shfl_down(VAR, DELTA, ##__VA_ARGS__)
+#define __shfl_xor_sync(MASK, VAR, LANE_MASK, ...) __shfl_xor(VAR, LANE_MASK, ##__VA_ARGS__)
+
+// Ballot intrinsic: every call site here uses a logical 32-lane warp (mask
+// 0xffffffff) and consumes the result as a 32-bit mask. HIP's __ballot returns
+// the full wavefront mask (32 bits on wave32, 64 bits on wave64). On wave64 a
+// 32-lane logical warp occupies one of the two 32-lane halves of the wavefront,
+// so extract the 32 bits for the current thread's half by shifting down by the
+// half-aligned base lane (__lane_id() & ~31). This is correct on both wave32
+// (shift is always 0) and wave64, and matches CUDA's uint32_t semantics.
+#define __ballot_sync(MASK, PRED) \
+ ((unsigned int)(__ballot(PRED) >> (__lane_id() & ~(unsigned)31)))
+
+// Population count and __activemask: HIP uses the same intrinsic name as CUDA
+// for __popc; no translation needed.
+
+// clang-format on
diff --git a/portable/include/mem/cxx_backends.h b/portable/include/mem/cxx_backends.h
index 9121a732..9ac99e53 100644
--- a/portable/include/mem/cxx_backends.h
+++ b/portable/include/mem/cxx_backends.h
@@ -14,6 +14,8 @@
#if defined(_PORTABLE_USE_CUDA)
#include
+#elif defined(_PORTABLE_USE_HIP)
+#include
#elif defined(_PORTABLE_USE_1API)
#include
#include
@@ -34,6 +36,14 @@
#define GPU_EVENT cudaEvent_t
#define GPU_EVENT_CREATE(e) cudaEventCreate(e);
+#elif defined(_PORTABLE_USE_HIP)
+
+#define GPULEN3 dim3
+#define MAKE_GPULEN3(X, Y, Z) dim3(X, Y, Z)
+#define GPU_BACKEND_SPECIFIC_STREAM hipStream_t
+#define GPU_EVENT hipEvent_t
+#define GPU_EVENT_CREATE(e) hipEventCreate(e);
+
#elif defined(_PORTABLE_USE_1API)
#define GPULEN3 sycl::range<3>
@@ -62,6 +72,24 @@
cudaEventSynchronize(E2);
#define event_time_elapsed(start, end, p_millisec) cudaEventElapsedTime(p_millisec, start, end);
+#elif defined(_PORTABLE_USE_HIP)
+
+#define event_create_pair(...) \
+ ([]() -> std::pair { \
+ hipEvent_t a, b; \
+ hipEventCreate(&a); \
+ hipEventCreate(&b); \
+ return {a, b}; \
+ })(__VA_ARGS__);
+#define event_destroy_pair(a, b) \
+ hipEventDestroy(a); \
+ hipEventDestroy(b);
+#define event_recording_start(E1, STREAM) hipEventRecord(E1, (hipStream_t)STREAM);
+#define event_recording_stop(E2, STREAM) \
+ hipEventRecord(E2, (hipStream_t)STREAM); \
+ hipEventSynchronize(E2);
+#define event_time_elapsed(start, end, p_millisec) hipEventElapsedTime(p_millisec, start, end);
+
#elif defined(_PORTABLE_USE_1API)
#define event_create_pair(...) \
@@ -96,6 +124,16 @@
})(__VA_ARGS__);
#define destroy_stream(stream) ([](void* s) { cudaStreamDestroy((cudaStream_t)s); })(stream);
+#elif defined(_PORTABLE_USE_HIP)
+
+#define create_stream(...) \
+ ([]() -> hipStream_t { \
+ hipStream_t stream; \
+ hipStreamCreate(&stream); \
+ return stream; \
+ })(__VA_ARGS__);
+#define destroy_stream(stream) ([](void* s) { hipStreamDestroy((hipStream_t)s); })(stream);
+
#elif defined(_PORTABLE_USE_1API)
#define create_stream(...) \
@@ -112,6 +150,11 @@
#define sync_by_stream(stream) cudaStreamSynchronize((cudaStream_t)stream);
#define sync_device cudaDeviceSynchronize();
+#elif defined(_PORTABLE_USE_HIP)
+
+#define sync_by_stream(stream) hipStreamSynchronize((hipStream_t)stream);
+#define sync_device hipDeviceSynchronize();
+
#elif defined(_PORTABLE_USE_1API)
#define sync_by_stream(stream) ((dpct::queue_ptr)stream)->wait();
diff --git a/portable/include/mem/cxx_mem_ops.h b/portable/include/mem/cxx_mem_ops.h
index 53dbeb53..e89176b8 100644
--- a/portable/include/mem/cxx_mem_ops.h
+++ b/portable/include/mem/cxx_mem_ops.h
@@ -5,6 +5,8 @@ template <>
struct _memcpy_direcion {
#if defined(_PORTABLE_USE_CUDA)
static const cudaMemcpyKind direction = cudaMemcpyHostToHost;
+#elif defined(_PORTABLE_USE_HIP)
+ static const hipMemcpyKind direction = hipMemcpyHostToHost;
#elif defined(_PORTABLE_USE_1API)
// no need
#endif
@@ -14,6 +16,8 @@ template <>
struct _memcpy_direcion {
#if defined(_PORTABLE_USE_CUDA)
static const cudaMemcpyKind direction = cudaMemcpyDeviceToHost;
+#elif defined(_PORTABLE_USE_HIP)
+ static const hipMemcpyKind direction = hipMemcpyDeviceToHost;
#elif defined(_PORTABLE_USE_1API)
// no need
#endif
diff --git a/portable/include/mem/gpu_event.hh b/portable/include/mem/gpu_event.hh
index 1e912bf2..8206f254 100644
--- a/portable/include/mem/gpu_event.hh
+++ b/portable/include/mem/gpu_event.hh
@@ -2,7 +2,9 @@
#define _PORTABLE_MEM_GPU_EVENT_HH
#include
+#if !defined(_PORTABLE_USE_HIP)
#include
+#endif
#include
#include
@@ -10,19 +12,22 @@
#include
#include
#include
+#include
namespace _ptb {
// CUDA event zone: RAII ///////////////////////////////////////////////////////
+using _gpu_event_elem = std::remove_pointer::type;
+
struct _gpu_event_deleter {
- void operator()(CUevent_st* e) const noexcept
+ void operator()(_gpu_event_elem* e) const noexcept
{
if (e) cudaEventDestroy(e);
}
};
-using gpu_event = std::unique_ptr;
+using gpu_event = std::unique_ptr<_gpu_event_elem, _gpu_event_deleter>;
inline gpu_event make_gpu_event()
{
@@ -51,6 +56,24 @@ struct timer_cuevent {
// CUPTI zone //////////////////////////////////////////////////////////////////
+#if defined(_PORTABLE_USE_HIP)
+
+// ROCm has no CUPTI; the optional per-kernel profiling timer is unavailable, so
+// provide an inert stub with the same interface. It never activates, so
+// gpu_timer always falls back to the hipEvent wall-clock path.
+struct timer_cupti {
+ static inline bool active = false;
+ static void enable() {}
+ void start(cudaStream_t) {}
+ double stop_ms(cudaStream_t s)
+ {
+ cudaStreamSynchronize(s);
+ return 0.0;
+ }
+};
+
+#else
+
// CUPTI kernel timing: sum of on-device kernel durations (vs cudaEvent wall-clock).
// Buffers are app-owned (request=malloc, complete=parse+free).
struct timer_cupti {
@@ -112,6 +135,8 @@ struct timer_cupti {
}
};
+#endif // _PORTABLE_USE_HIP
+
// user zone //////////////////////////////////////////////////////////////////
struct gpu_timer {
diff --git a/portable/include/mem/gpu_stream.hh b/portable/include/mem/gpu_stream.hh
index e19620e0..c8f965b7 100644
--- a/portable/include/mem/gpu_stream.hh
+++ b/portable/include/mem/gpu_stream.hh
@@ -21,17 +21,20 @@
#include
#include
+#include
namespace _ptb {
+using _gpu_stream_elem = std::remove_pointer::type;
+
struct gpu_stream_deleter {
- void operator()(CUstream_st* s) const noexcept
+ void operator()(_gpu_stream_elem* s) const noexcept
{
if (s) cudaStreamDestroy(s);
}
};
-using gpu_stream = std::unique_ptr;
+using gpu_stream = std::unique_ptr<_gpu_stream_elem, gpu_stream_deleter>;
inline gpu_stream make_gpu_stream()
{
diff --git a/portable/include/utils/err.hh b/portable/include/utils/err.hh
index ad15ad29..8f02ea2b 100644
--- a/portable/include/utils/err.hh
+++ b/portable/include/utils/err.hh
@@ -1,8 +1,12 @@
#ifndef AE6DCA2E_F19B_41DB_80CB_11230E548F92
#define AE6DCA2E_F19B_41DB_80CB_11230E548F92
+#if defined(_PORTABLE_USE_CUDA) || defined(_PORTABLE_USE_HIP)
#if defined(_PORTABLE_USE_CUDA)
#include
+#else
+#include
+#endif
#include
#include
diff --git a/psz/src/cli/context.cc b/psz/src/cli/context.cc
index 39b4cce6..27242708 100644
--- a/psz/src/cli/context.cc
+++ b/psz/src/cli/context.cc
@@ -32,6 +32,12 @@ const char* BACKEND_TEXT = "cuSZ";
const char* VERSION_TEXT = "2025-02-05 (0.16)";
const int VERSION = 20241218;
+#elif defined(PSZ_USE_HIP)
+
+const char* BACKEND_TEXT = "hipSZ";
+const char* VERSION_TEXT = "2025-02-05 (0.16)";
+const int VERSION = 20241218;
+
#elif defined(PSZ_USE_1API)
const char* BACKEND_TEXT = "dpSZ";
diff --git a/psz/src/cli/query/query_cpu.hh b/psz/src/cli/query/query_cpu.hh
index 6ad9efb8..46041374 100644
--- a/psz/src/cli/query/query_cpu.hh
+++ b/psz/src/cli/query/query_cpu.hh
@@ -5,9 +5,15 @@
#define E8CDEF97_5136_45C6_A6F2_3FECD549F8A4
#include
+#include
#include "cusz/type.h"
+#if defined(_WIN32)
+#define popen _popen
+#define pclose _pclose
+#endif
+
struct cpu_diagnostics {
static std::string exec_shellcmd(const char* cmd)
{
diff --git a/psz/src/cli/verinfo_hip.cu b/psz/src/cli/verinfo_hip.cu
new file mode 100644
index 00000000..a2d19743
--- /dev/null
+++ b/psz/src/cli/verinfo_hip.cu
@@ -0,0 +1,92 @@
+// Copyright (c) 2026 Advanced Micro Devices, Inc.
+//
+// \file verinfo_hip.cu
+// \author Jeff Daily
+// \brief HIP/ROCm implementation of the CLI version-info queries.
+//
+// On the HIP backend this replaces verinfo.cu (NVML driver query) and
+// verinfo_nv.cu (CUDA driver-API deviceQuery). The function names mirror the
+// CUDA backend (print_NVCC_ver, print_CUDA_driver, print_NVIDIA_driver,
+// CUDA_devices) so the shared context.cc version banner is unchanged; here they
+// report the HIP/ROCm toolchain, driver, and device properties.
+
+#include
+
+#include
+
+#include "cli/verinfo.h"
+
+void print_NVCC_ver()
+{
+ printf(
+ "- HIP: %s.%s.%s\n", //
+ STRINGIZE_VALUE_OF(HIP_VERSION_MAJOR), STRINGIZE_VALUE_OF(HIP_VERSION_MINOR),
+ STRINGIZE_VALUE_OF(HIP_VERSION_PATCH));
+}
+
+int print_CUDA_driver()
+{
+ int driver_version = 0;
+ std::printf("- HIP driver: ");
+ if (hipDriverGetVersion(&driver_version) != hipSuccess) {
+ std::printf("(failed to get driver version)\n");
+ return 1;
+ }
+ int major_version = driver_version / 10000000;
+ int minor_version = (driver_version % 10000000) / 100000;
+ std::printf("%d.%d\n", major_version, minor_version);
+ return 0;
+}
+
+int print_NVIDIA_driver()
+{
+ int runtime_version = 0;
+ if (hipRuntimeGetVersion(&runtime_version) != hipSuccess) {
+ std::printf("- ROCm runtime: (failed to query)\n");
+ return 1;
+ }
+ int major_version = runtime_version / 10000000;
+ int minor_version = (runtime_version % 10000000) / 100000;
+ std::printf("- ROCm runtime: %d.%d\n", major_version, minor_version);
+ return 0;
+}
+
+void CUDA_devices()
+{
+ int device_count = 0;
+ hipGetDeviceCount(&device_count);
+
+ if (device_count == 0) { printf("0 devices detected\n"); }
+ else {
+ printf("%d HIP device(s):\n", device_count);
+ }
+
+ for (auto dev = 0; dev < device_count; ++dev) {
+ hipDeviceProp_t deviceProp;
+ hipGetDeviceProperties(&deviceProp, dev);
+
+ int memClockKHz = deviceProp.memoryClockRate;
+ auto membw_GiBps = membw_base1024(deviceProp.memoryBusWidth, memClockKHz * 1e3);
+ auto membw_GBps = membw_base1000(deviceProp.memoryBusWidth, memClockKHz * 1e3);
+
+ printf("- %s (%s)\n", deviceProp.name, deviceProp.gcnArchName);
+ printf(
+ " - %d compute units; warp size: %d\n", deviceProp.multiProcessorCount,
+ deviceProp.warpSize);
+ printf(
+ " - global VRAM: %.0f MB (theoretically) at \n",
+ (float)deviceProp.totalGlobalMem / 1048576.0f);
+ printf(" %.1f GiB/s (base-1024) or %.1f GB/s (base-1000)\n", membw_GiBps, membw_GBps);
+ printf(" - L2 cache: %d bytes\n", deviceProp.l2CacheSize);
+ printf(
+ " - per-block/CU total shared memory: %zu/%zu bytes\n", deviceProp.sharedMemPerBlock,
+ deviceProp.sharedMemPerMultiprocessor);
+ printf(
+ " - per-block/CU max thread count: %d/%d\n", deviceProp.maxThreadsPerBlock,
+ deviceProp.maxThreadsPerMultiProcessor);
+ printf(
+ " - max thread-block dim (x,y,z): (%d, %d, %d)\n", deviceProp.maxThreadsDim[0],
+ deviceProp.maxThreadsDim[1], deviceProp.maxThreadsDim[2]);
+ printf(" - per-block total registers count: %d\n", deviceProp.regsPerBlock);
+ }
+}
diff --git a/psz/src/compressor.inl b/psz/src/compressor.inl
index 009af9c1..cf64c110 100644
--- a/psz/src/compressor.inl
+++ b/psz/src/compressor.inl
@@ -24,7 +24,7 @@ template , psz::PredictorFeature>;
-#if defined(PSZ_USE_CUDA)
+#if defined(PSZ_USE_CUDA) || defined(PSZ_USE_HIP)
#define CONCAT_ON_DEVICE(dst, src, nbyte, stream) \
if (nbyte != 0) cudaMemcpyAsync(dst, src, nbyte, cudaMemcpyDeviceToDevice, (cudaStream_t)stream);
diff --git a/psz/src/kernel/histsp.cu.inl b/psz/src/kernel/histsp.cu.inl
index 9617f43b..6dd46303 100644
--- a/psz/src/kernel/histsp.cu.inl
+++ b/psz/src/kernel/histsp.cu.inl
@@ -58,10 +58,15 @@ __global__ void KCU_histogram_sparse_multiwarp(
__syncthreads();
#ifdef __HIP_PLATFORM_AMD__
+ // 32-lane logical-warp inclusive scan; the result is consumed below at the
+ // lane-31 leader (threadIdx.x % 32 == 31). Pin the shuffle width to 32 so the
+ // scan domain matches that leader on both wave32 (RDNA) and wave64 (CDNA): the
+ // earlier wave64-only form (width 64, threadIdx.x % 64) lost the upper half of
+ // the scan on a 32-lane wavefront, corrupting the per-warp histogram.
for (auto& sum : p_hist) {
- for (auto d = 1; d < 64; d *= 2) {
- auto n = __shfl_up(sum, d, 64);
- if (threadIdx.x % 64 >= d) sum += n;
+ for (auto d = 1; d < 32; d *= 2) {
+ auto n = __shfl_up(sum, d, 32);
+ if (threadIdx.x % 32 >= d) sum += n;
}
}
#else
diff --git a/psz/src/kernel/spl_y24.cuh b/psz/src/kernel/spl_y24.cuh
index 39872697..bfc7b5e3 100644
--- a/psz/src/kernel/spl_y24.cuh
+++ b/psz/src/kernel/spl_y24.cuh
@@ -54,7 +54,8 @@ template <
typename T1, typename T2, typename FP, int LINEAR_BLOCK_SIZE, bool WORKFLOW = SPLINE3_COMPR,
bool PROBE_PRED_ERROR = false>
__device__ void spline3d_layout2_interpolate(
- volatile T1 s_data[9][9][33], volatile T2 s_eq[9][9][33], FP eb_r, FP ebx2, int radius);
+ volatile T1 s_data[9][9][33], volatile T2 s_eq[9][9][33], dim3 data_size, FP eb_r, FP ebx2,
+ int radius);
} // namespace psz
namespace {
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 34958120..323f274c 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,8 +1,16 @@
-if(PSZ_BACKEND STREQUAL "CUDA" OR PSZ_BACKEND STREQUAL "cuda")
+string(TOUPPER "${PSZ_BACKEND}" _PSZ_TEST_BACKEND_UPPER)
+if(_PSZ_TEST_BACKEND_UPPER STREQUAL "CUDA")
+ set(PSZ_TEST_GPU_RT CUDA::cudart)
include(cmake/cuda-test.cmake) # legacy unit-test binaries
include(cmake/cuda-test-bin_pred.cmake) # bin_pred microbench matrix (v1-only on develop)
include(cmake/cuda-test-bin_hf.cmake) # bin_hf-driven ctest matrix
include(cmake/cuda-test-cusz.cmake) # cusz CLI-driven ctest matrix
+elseif(_PSZ_TEST_BACKEND_UPPER STREQUAL "HIP")
+ set(PSZ_TEST_GPU_RT hip::device)
+ set(PSZ_TEST_HIP ON)
+ include(cmake/cuda-test.cmake) # same sources, marked LANGUAGE HIP
+ include(cmake/cuda-test-bin_hf.cmake) # bin_hf-driven ctest matrix (binary-agnostic)
+ include(cmake/cuda-test-cusz.cmake) # cusz CLI-driven ctest matrix (binary-agnostic)
elseif(PSZ_BACKEND STREQUAL "ONEAPI" OR PSZ_BACKEND STREQUAL "oneapi" OR PSZ_BACKEND STREQUAL "1api")
include(cmake/sycl-test.cmake)
else()
diff --git a/test/cmake/cuda-test.cmake b/test/cmake/cuda-test.cmake
index c87bf960..0ab19dff 100644
--- a/test/cmake/cuda-test.cmake
+++ b/test/cmake/cuda-test.cmake
@@ -14,6 +14,36 @@ target_include_directories(
$
)
+# On the HIP backend, test TUs (host .cc and .cu alike) need the CUDA->HIP
+# translation prelude and the cuda_runtime.h shim; carry both here so tests that
+# do not link psz_cu_compile_settings still resolve.
+if(PSZ_TEST_HIP)
+ find_package(hip REQUIRED)
+ target_compile_definitions(psz_cu_test_compile_settings
+ INTERFACE PSZ_USE_HIP _PORTABLE_USE_HIP)
+ # hip-compat first (cuda_runtime.h shim + macros), then the ROCm include dir so
+ # the shims' #include resolve in host C++ test TUs too.
+ target_include_directories(psz_cu_test_compile_settings
+ INTERFACE
+ ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/hip-compat
+ ${hip_INCLUDE_DIRS})
+ # HIP-language test TUs get the force-included prelude via
+ # psz_cu_compile_settings; host .cc test TUs pick up translation via the
+ # cuda_runtime.h shim on demand. No -include here, to avoid a duplicate
+ # force-include when both interface targets are linked.
+endif()
+
+# On the HIP backend the same .cu test sources are compiled as HIP; the
+# CUDA->HIP translation prelude is carried by psz_cu_compile_settings.
+if(PSZ_TEST_HIP)
+ set_source_files_properties(
+ src/test_l1_compact.cu
+ src/tune_histsp.cu
+ src/test_identical2.cu
+ src/test_mem_unique.cu
+ PROPERTIES LANGUAGE HIP)
+endif()
+
# functionality
add_executable(zigzag src/test_zigzag_codec.cc)
target_link_libraries(zigzag PRIVATE psz_cu_test_compile_settings)
@@ -26,13 +56,19 @@ target_link_libraries(l1_compact PRIVATE psz_cu_compile_settings
add_test(test_l1_compact l1_compact)
# Level-2 kernel (template; unit tests)
-add_executable(histsp_cu src/tune_histsp.cu)
-target_link_libraries(histsp_cu
- PRIVATE psz_cu_compile_settings
- psz_seq_core
- cusz
-)
-add_test(test_histsp_cu histsp_cu)
+# tune_histsp.cu.inl calls GPU_histogram_generic(d_in,...) as a constructor
+# but the struct only exposes static ::init/::kernel; signature drifted upstream.
+# Pre-existing CUDA build failure; excluded on the HIP path to avoid blocking
+# the build. The target is a tuning/perf test only, not a correctness gate.
+if(NOT PSZ_TEST_HIP)
+ add_executable(histsp_cu src/tune_histsp.cu)
+ target_link_libraries(histsp_cu
+ PRIVATE psz_cu_compile_settings
+ psz_seq_core
+ cusz
+ )
+ add_test(test_histsp_cu histsp_cu)
+endif()
# Level-3 kernel with configuration (low-level API)
add_executable(lrz_seq src/test_lrz.seq.cc)
@@ -55,7 +91,7 @@ target_link_libraries(stat_identical1
PORTABLE::testutils
EVAL::stat_cu
EVAL::stat_seq
- CUDA::cudart
+ ${PSZ_TEST_GPU_RT}
)
add_test(test_stat_identical1 stat_identical1)
@@ -67,7 +103,7 @@ target_link_libraries(stat_identical2
PORTABLE::testutils
EVAL::stat_cu
EVAL::stat_seq
- CUDA::cudart
+ ${PSZ_TEST_GPU_RT}
)
add_test(test_stat_identical2 stat_identical2)
@@ -79,7 +115,7 @@ target_link_libraries(stat_max_error
PORTABLE::testutils
EVAL::stat_cu
EVAL::stat_seq
- CUDA::cudart
+ ${PSZ_TEST_GPU_RT}
)
add_test(test_stat_max_error stat_max_error)
@@ -89,7 +125,7 @@ target_link_libraries(mem_unique
psz_cu_compile_settings
psz_cu_test_compile_settings
psz_cu_mem
- CUDA::cudart
+ ${PSZ_TEST_GPU_RT}
)
add_test(test_mem_unique mem_unique)
@@ -98,7 +134,7 @@ target_link_libraries(test_hfr
PRIVATE
psz_cu_test_compile_settings
PSZ::CUDA::phf
- CUDA::cudart
+ ${PSZ_TEST_GPU_RT}
)
add_test(test_hf_revisit_altcode test_hfr)
@@ -113,13 +149,15 @@ add_test(test_hf_cpu_serial_codebook test_hfserial)
# GPU tests serialize via a named resource lock so `ctest -j N` doesn't
# oversubscribe the device. Tests that touch only the host (zigzag, lrz_seq,
# test_hf_cpu_serial_codebook) are intentionally omitted and run in parallel.
-set_tests_properties(
+set(psz_gpu_tests
test_l1_compact
- test_histsp_cu
test_stat_identical1
test_stat_identical2
test_stat_max_error
test_mem_unique
test_hf_revisit_altcode
- PROPERTIES RESOURCE_LOCK gpu
)
+if(NOT PSZ_TEST_HIP)
+ list(APPEND psz_gpu_tests test_histsp_cu)
+endif()
+set_tests_properties(${psz_gpu_tests} PROPERTIES RESOURCE_LOCK gpu)
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
index cbdbb940..4194a010 100644
--- a/utils/CMakeLists.txt
+++ b/utils/CMakeLists.txt
@@ -40,9 +40,11 @@ else()
option(EVAL "build CUDA eval implementations (requires full psz tree)" ON)
endif()
-# Find CUDA if needed
+# Find GPU toolkit if needed
if(PSZ_BACKEND_UPPER STREQUAL "CUDA")
find_package(CUDAToolkit REQUIRED)
+elseif(PSZ_BACKEND_UPPER STREQUAL "HIP")
+ find_package(hip REQUIRED)
endif()
# Find portable headers
@@ -73,6 +75,17 @@ if(EVAL_STANDALONE)
INTERFACE
$<$:--extended-lambda;--expt-relaxed-constexpr>
)
+ elseif(PSZ_BACKEND_UPPER STREQUAL "HIP")
+ target_compile_definitions(eval_compile_settings
+ INTERFACE
+ PSZ_USE_HIP
+ _PORTABLE_USE_HIP
+ )
+ target_include_directories(eval_compile_settings
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/hip-compat)
+ target_compile_options(eval_compile_settings
+ INTERFACE
+ $<$:-include;${CMAKE_CURRENT_SOURCE_DIR}/../cmake/hip-compat/psz_hip_compat.h>)
elseif(PSZ_BACKEND STREQUAL "ONEAPI" OR PSZ_BACKEND STREQUAL "oneapi" OR PSZ_BACKEND STREQUAL "1api")
target_compile_definitions(eval_compile_settings
INTERFACE
@@ -93,6 +106,11 @@ if(PSZ_BACKEND_UPPER STREQUAL "CUDA")
enable_language(CUDA)
set(EVAL_BACKEND "CUDA")
+elseif(PSZ_BACKEND_UPPER STREQUAL "HIP")
+
+ enable_language(HIP)
+ set(EVAL_BACKEND "HIP")
+
elseif(PSZ_BACKEND STREQUAL "ONEAPI" OR PSZ_BACKEND STREQUAL "oneapi" OR PSZ_BACKEND STREQUAL "1api")
set(EVAL_BACKEND "SYCL")
@@ -173,7 +191,51 @@ if(EVAL_BACKEND STREQUAL "CUDA")
else()
message("[eval::info] EVAL is OFF: CUDA eval implementations skipped (require full psz tree).")
endif()
-
+
+elseif(EVAL_BACKEND STREQUAL "HIP")
+ # HIP reuses the same .cu/.cc sources as CUDA, marked LANGUAGE HIP; the
+ # CUDA->HIP translation prelude is supplied by ${eval_compile_settings}
+ # (psz_cu_compile_settings in the in-tree build).
+ if(EVAL)
+ set(_eval_hip_sources
+ src/identical.cu
+ src/compile/extrema-f4.cu
+ src/compile/extrema-f8.cu
+ src/compile/calc_err-f4.cu
+ src/compile/calc_err-f8.cu
+ src/compile/assess-f4.cu
+ src/compile/assess-f8.cu
+ src/compile/maxerr-all.cu
+ )
+ set_source_files_properties(${_eval_hip_sources} PROPERTIES LANGUAGE HIP)
+ add_library(eval_cu ${_eval_hip_sources})
+ target_link_libraries(eval_cu
+ PUBLIC
+ utils_headers
+ ${eval_compile_settings}
+ hip::device
+ )
+ add_library(EVAL::stat_cu ALIAS eval_cu)
+
+ set(_eval_viewer_hip_sources
+ src/viewer.cc
+ src/compile/viewer-all.cu
+ )
+ set_source_files_properties(src/compile/viewer-all.cu PROPERTIES LANGUAGE HIP)
+ add_library(eval_viewer_cu ${_eval_viewer_hip_sources})
+ target_link_libraries(eval_viewer_cu
+ PUBLIC
+ utils_headers
+ eval_seq
+ eval_cu
+ ${eval_compile_settings}
+ hip::device
+ )
+ add_library(EVAL::viewer_cu ALIAS eval_viewer_cu)
+ else()
+ message("[eval::info] EVAL is OFF: HIP eval implementations skipped (require full psz tree).")
+ endif()
+
elseif(EVAL_BACKEND STREQUAL "SYCL")
# SYCL/DPC++ backend - TODO: add SYCL implementations
# add_library(eval_dp ...)
diff --git a/utils/include/compare.hh b/utils/include/compare.hh
index 0899bd37..e6f14c66 100644
--- a/utils/include/compare.hh
+++ b/utils/include/compare.hh
@@ -53,12 +53,13 @@ template
std::tuple GPU_probe_extrema(T* in, size_t len)
{
T r[4];
- if constexpr (R == CUDA)
+ if constexpr (R == CUDA or R == HIP)
cuda::GPU_extrema(in, len, r);
else if constexpr (R == SYCL)
dpcpp::GPU_extrema(in, len, r);
else
- static_assert(R == CUDA or R == SYCL, "GPU_probe_extrema supports CUDA / SYCL.");
+ static_assert(
+ R == CUDA or R == HIP or R == SYCL, "GPU_probe_extrema supports CUDA / HIP / SYCL.");
return {r[0], r[1], r[2], r[3]};
}
@@ -67,12 +68,13 @@ void assess_quality(psz_stats* s, T* xdata, T* odata, size_t const len)
{
if constexpr (P == SEQ)
cppstl::CPU_assess_quality(s, xdata, odata, len);
- else if constexpr (P == CUDA)
+ else if constexpr (P == CUDA or P == HIP)
cuda::GPU_assess_quality(s, xdata, odata, len);
else if constexpr (P == SYCL)
dpcpp::GPU_assess_quality(s, xdata, odata, len);
else
- static_assert(P == SEQ or P == CUDA or P == SYCL, "assess_quality: unsupported backend.");
+ static_assert(
+ P == SEQ or P == CUDA or P == HIP or P == SYCL, "assess_quality: unsupported backend.");
}
} // namespace psz::analysis
diff --git a/utils/src/atomics.cu.inl b/utils/src/atomics.cu.inl
index 34429c81..8cfa69db 100644
--- a/utils/src/atomics.cu.inl
+++ b/utils/src/atomics.cu.inl
@@ -64,7 +64,9 @@ __device__ __forceinline__ T atomicAddFp(T* addr, T value)
{
if constexpr (std::is_same::value) { return atomicAdd(addr, value); }
else if constexpr (std::is_same::value) {
-#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 600)
+#if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 600)) || defined(__HIP_DEVICE_COMPILE__)
+ // CUDA SM6.0+ and HIP/ROCm provide a native double atomicAdd; the CAS loop
+ // below is the portable fallback for older CUDA device targets only.
return atomicAdd(addr, value);
#else
auto addr_as_ull = reinterpret_cast(addr);
diff --git a/utils/src/extrema.cu.inl b/utils/src/extrema.cu.inl
index 6c60c347..e04f8664 100644
--- a/utils/src/extrema.cu.inl
+++ b/utils/src/extrema.cu.inl
@@ -101,21 +101,9 @@ void GPU_extrema(T* in, size_t len, T res[4])
cudaMemcpy(d_minel, in, sizeof(T), cudaMemcpyDeviceToDevice); // init min el
cudaMemcpy(d_maxel, in, sizeof(T), cudaMemcpyDeviceToDevice); // init max el
-// launch
-#if defined(PSZ_USE_CUDA)
+ // launch: the same KCU_extrema kernel is used on CUDA and HIP.
psz::KCU_extrema<<>>(
in, len, d_minel, d_maxel, d_sum, failsafe, R);
-#elif defined(PSZ_USE_HIP)
- if constexpr (std::is_same
::value) {
- psz::extrema_kernel<<>>(
- in, len, d_minel, d_maxel, d_sum, failsafe, R);
- }
- else {
- throw std::runtime_error(
- "As of now (5.5.30202), HIP does not support 64-bit integer atomic "
- "operation.");
- }
-#endif
cudaStreamSynchronize(stream);
diff --git a/utils/src/viewer.cc b/utils/src/viewer.cc
index 83a5a04d..70da8f50 100644
--- a/utils/src/viewer.cc
+++ b/utils/src/viewer.cc
@@ -19,6 +19,16 @@ using std::cout;
using std::endl;
using std::to_string;
+// MSVC STL has no std::hash
specialization, so a const-qualified
+// unordered_map key fails to compile; on libstdc++ the const form is accepted.
+// Keep the Linux key type byte-identical (so device objects and exported symbols
+// are unchanged there) and drop the const only on Windows.
+#if defined(_WIN32)
+#define PSZ_MAP_KEY(T) T
+#else
+#define PSZ_MAP_KEY(T) T const
+#endif
+
void psz_review_comp_time_breakdown(void* _r, psz_header* h)
{
std::runtime_error("psz_review_comp_time_breakdown is to be updated.");
@@ -26,7 +36,7 @@ void psz_review_comp_time_breakdown(void* _r, psz_header* h)
string const psz_report_query_pred(psz_predictor const p)
{
- const std::unordered_map lut = {
+ const std::unordered_map lut = {
{psz_predictor::Lorenzo, "Lorenzo"},
{psz_predictor::LorenzoZigZag, "Lrz-ZigZag"},
{psz_predictor::LorenzoProto, "Lrz-Proto"},
@@ -37,7 +47,7 @@ string const psz_report_query_pred(psz_predictor const p)
string const psz_report_query_hist(psz_hist const h)
{
- const std::unordered_map lut = {
+ const std::unordered_map lut = {
{psz_hist::HistGeneric, "Hist"},
{psz_hist::HistSp, "Hist-Sparse"},
{psz_hist::HistNull, "Null"},
@@ -47,7 +57,7 @@ string const psz_report_query_hist(psz_hist const h)
string const psz_report_query_codec1(psz_codec const c)
{
- const std::unordered_map lut = {
+ const std::unordered_map lut = {
{psz_codec::HF, "HF"},
{psz_codec::HFr1, "Huffman-rev1"},
{psz_codec::HFr2, "Huffman-rev2"},