Restore HIP feature parity with CUDA; make hipify.sh maintainable - #221
Restore HIP feature parity with CUDA; make hipify.sh maintainable#221susilehtola wants to merge 1 commit into
Conversation
The HIP backend has not built since mGGA, FXC contraction, EXC gradients
and sn-LinK screening landed on the CUDA side. scheme1_base.cxx is
compiled for both backends (device/CMakeLists.txt:16, before the
conditional cuda/ and hip/ subdirectories) and calls zmat_*_fxc,
increment_exc_grad_*, exx_ek_screening_bfn_stats and the shell-to-task
collocation, none of which had HIP definitions -- so any
-DGAUXC_ENABLE_HIP=ON build fails to link. The HIP files that did exist
were also stale: zmat_vxc.hip still had the pre-UKS/GKS kernel names,
and uvvars.hip had no mGGA path at all.
Root cause is that hipify.sh covered only 8 of the 24 current CUDA
kernel sources and was never updated. This rewrites it to cover all of
them, and replaces the hipify-perl invocations with explicit sed rules
so regeneration is reproducible without a ROCm installation and the
non-mechanical decisions are documented in one place:
* warp/wavefront size comes from GauXC::{cuda,hip}::warp_size (32 vs
64), so launch geometry adapts; no literal 32s were introduced.
* __syncwarp() has no HIP equivalent (lockstep wavefronts on AMD) and
is commented out, the convention the 2022 port already used here.
* __shfl_*_sync(mask, ...) -> __shfl_*(...) (HIP takes no mask).
* cub -> hipcub.
* CUDA's two-tier shared-memory limit has no AMD counterpart: both
cudaDevAttrMaxSharedMemoryPerBlock{,Optin} map to
hipDeviceAttributeMaxSharedMemoryPerBlock, leaving the overflow
guard intact and the opt-in branch a no-op.
* __stcs() and its inline-PTX fallback in pack_submat are lowered to a
plain store; CUDART_VERSION is undefined under HIP, so the
preprocessor would otherwise select the PTX branch.
* CUTLASS is deliberately not translated (CUDA-only dependent option).
Regenerated all 23 kernel files and added zmat_fxc, increment_exc_grad
and exx_ek_screening_bfn_stats to the HIP CMakeLists. Every kernel
symbol scheme1_base.cxx references now has a HIP definition, and no
compile-breaking CUDA token remains in the generated sources.
NOT COMPILE-TESTED: no ROCm or AMD hardware was available. Before this
is trusted for numerical results, it needs a build with hipcc and a
review of any intra-wavefront reduction whose trip count is a literal
rather than derived from hip::warp_size (see CHECK_WAVEFRONT in
hipify.sh).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0135evJ9zgNL1y8U6T9bQ3UT
|
Hey @susilehtola, just wanted to let you know that I am taking a look at this. Currently seeing if I can get access to some appropriate AMD GPUs to test this myself. I'll also look into some options for CI/CD if I can get my hands on some quota from one of the big cloud providers. |
There was a problem hiding this comment.
🟡 Changes recommended
Missing generated headers, untranslated CUDA dependencies, and multiple 32-to-64-lane indexing errors prevent a correct HIP build.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Restores HIP backend parity with CUDA by regenerating missing kernels and expanding HIP build coverage.
Changes:
- Reworks
hipify.shto translate current CUDA kernels. - Adds mGGA, FXC, gradient, screening, and shell-to-task support.
- Updates HIP build wiring and wavefront-dependent kernels.
File summaries
| File | Description |
|---|---|
hip/kernels/zmat_vxc.hip |
Adds UKS/GKS and mGGA VXC kernels. |
hip/kernels/zmat_fxc.hip |
Adds FXC contraction kernels. |
hip/kernels/uvvars.hip |
Dispatches split LDA/GGA/mGGA kernels. |
hip/kernels/uvvars_mgga.hpp |
Adds mGGA variable kernels. |
hip/kernels/uvvars_lda.hpp |
Adds LDA scheme variants. |
hip/kernels/uvvars_gga.hpp |
Adds GGA scheme variants. |
hip/kernels/symmetrize_mat.hip |
Updates matrix symmetrization. |
hip/kernels/pack_submat.hip |
Adds symmetric/asymmetric packing. |
hip/kernels/hipblas_extensions.hip |
Expands HIP BLAS helpers. |
hip/kernels/hip_ssf_1d.hpp |
Declares SSF gradient support. |
hip/kernels/hip_ssf_1d.hip |
Adds SSF weight derivatives. |
hip/kernels/hip_inc_potential.hip |
Adds symmetric/asymmetric increments. |
hip/kernels/hip_extensions.hpp |
Adds wavefront reductions. |
hip/kernels/grid_to_center.hpp |
Updates HIP declarations. |
hip/kernels/grid_to_center.hip |
Reworks distance computation. |
hip/kernels/exx_ek_screening_bfn_stats.hip |
Adds sn-LinK screening. |
hip/kernels/collocation/collocation_device_constants.hpp |
Expands angular constants. |
hip/kernels/collocation/collocation_angular_spherical_unnorm.hpp |
Adds angular momentum-4 support. |
hip/kernels/collocation/collocation_angular_cartesian.hpp |
Adds Cartesian momentum-4 support. |
hip/kernels/collocation_shell_to_task_kernels.hpp |
Aggregates shell-to-task kernels. |
hip/kernels/collocation_masked_combined_kernels.hpp |
Updates masked collocation. |
hip/kernels/collocation_device.hip |
Adds shell-to-task dispatch. |
hip/hipify.sh |
Rewrites HIP generation workflow. |
hip/CMakeLists.txt |
Registers new HIP sources. |
Review details
Suppressed comments (3)
src/xc_integrator/local_work_driver/device/hip/kernels/pack_submat.hip:226
- As in the symmetric path, this 32×32 launch is incompatible with
WARP_X=16and the 8×8 cut decomposition:tid_xy=1creates overlapping/out-of-range unrolled reads, while only four of eighttid_yyresidues run. Launch the logical 16×64 shape.
dim3 threads( hip::warp_size/2, hip::max_warps_per_thread_block * 2, 1 );
src/xc_integrator/local_work_driver/device/hip/kernels/hip_inc_potential.hip:212
- This asymmetric kernel has the same fixed 16×64 indexing contract, while the 32×32 HIP launch causes overlapping/out-of-bounds unrolled accesses and skips half of each cut block. Launch the dimensions represented by the kernel constants.
dim3 threads( hip::warp_size/2, hip::max_warps_per_thread_block * 2, 1 );
src/xc_integrator/local_work_driver/device/hip/kernels/exx_ek_screening_bfn_stats.hip:405
- This duplicated conversion kernel has the same 64-vs-32 mismatch: the load loop leaves many
collisions_buffer[threadIdx.y][threadIdx.x]rows uninitialized before they are consumed, yielding incorrect shell lists andnbecounts. Make its logical subgroup width and launch geometry consistent.
for (int buffer_loop = 0; buffer_loop < warp_size; buffer_loop += warp_size/buffer_size) {
const int t_id_x = threadIdx.x % buffer_size;
const int buffer_thread = threadIdx.x / buffer_size;
const int buffer_idx = buffer_thread + buffer_loop;
if (j_block * buffer_size_bits + t_id_x * element_size < nshells && i_base + buffer_idx < ntasks) {
- Files reviewed: 25/25 changed files
- Comments generated: 10
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| -e 's|cuda_extensions\.hpp|hip_extensions.hpp|g' \ | ||
| -e 's|cuda_aos_scheme1\.hpp|hip_aos_scheme1.hpp|g' \ | ||
| -e 's|#include <cub/cub\.cuh>|#include <hipcub/hipcub.hpp>|g' \ | ||
| -e 's|\bcub::|hipcub::|g' \ |
| hipify_file "$CUDA_PREFIX/collocation_shell_to_task_kernels.hpp" \ | ||
| "$HIP_PREFIX/collocation_shell_to_task_kernels.hpp" |
| } | ||
| // do the computation | ||
| #pragma unroll 2 | ||
| for (int k = threadIdx.y; k < hip::warp_size; k+=hip::warp_size/2) { |
| register double den_reg = den_shared[0][sm_y][threadIdx.x]; | ||
| register double dx_reg = den_shared[1][sm_y][threadIdx.x]; | ||
| register double dy_reg = den_shared[2][sm_y][threadIdx.x]; | ||
| register double dz_reg = den_shared[3][sm_y][threadIdx.x]; |
| register double tx_reg = den_shared[0][sm_y][threadIdx.x]; | ||
| register double ty_reg = den_shared[1][sm_y][threadIdx.x]; | ||
| register double tz_reg = den_shared[2][sm_y][threadIdx.x]; |
| const size_t num_blocks = ((N + hip::warp_size - 1) / hip::warp_size); | ||
| // Warp size must equal max_warps_per_thread_block must equal 32 | ||
| dim3 threads(hip::warp_size, hip::max_warps_per_thread_block), blocks(num_blocks); |
| hipStream_t stream = queue.queue_as<util::hip_stream>(); | ||
|
|
||
| j += delta_j; | ||
| dim3 threads( hip::warp_size/2, hip::max_warps_per_thread_block * 2, 1 ); |
|
|
||
| hipStream_t stream = queue.queue_as<util::hip_stream>(); | ||
|
|
||
| dim3 threads( hip::warp_size/2, hip::max_warps_per_thread_block * 2, 1 ); |
| __shared__ double bf_shared[32][32 + 1]; | ||
| __shared__ double bfn_sum_shared[32]; | ||
| bfn_sum_shared[warp_lane] = 0.0; |
| for (int buffer_loop = 0; buffer_loop < warp_size; buffer_loop += warp_size/buffer_size) { | ||
| const int t_id_x = threadIdx.x % buffer_size; | ||
| const int buffer_thread = threadIdx.x / buffer_size; | ||
| const int buffer_idx = buffer_thread + buffer_loop; | ||
| if (j_block * buffer_size_bits + t_id_x * element_size < nsp && i_base + buffer_idx < ntasks) { |
Problem
The HIP backend does not build.
scheme1_base.cxxis compiled for both backends — it is listed insrc/xc_integrator/local_work_driver/device/CMakeLists.txt:16, before the conditionalcuda/andhip/subdirectories — and it callszmat_*_fxc,increment_exc_grad_*,exx_ek_screening_bfn_statsand the shell-to-task collocation. None of those had HIP definitions, so any-DGAUXC_ENABLE_HIP=ONbuild fails to link.The HIP sources that did exist were stale as well:
kernels/zmat_vxc.hipstill had the pre-UKS/GKS kernel names (zmat_lda_vxc_kernelrather thanzmat_lda_vxc_{rks,uks,gks}_kernel);kernels/uvvars.hiphad no mGGA path at all (zerotaureferences against 19 on the CUDA side);collocation_shell_to_task_kernels.hppand the splituvvars_{lda,gga,mgga}.hppheaders were absent.In effect the HIP backend has been frozen at roughly its 2022 LDA/GGA-VXC feature level while mGGA, FXC contraction, EXC gradients and sn-LinK screening were added on the CUDA side.
Root cause
hip/hipify.shcovered only 8 of the 24 current files undercuda/kernels/and was never updated as kernels were added.What this PR does
Rewrites
hipify.shto cover all current CUDA kernel sources, regenerates the HIP tree from it, and addszmat_fxc,increment_exc_gradandexx_ek_screening_bfn_statstohip/CMakeLists.txt.The
hipify-perlinvocations are replaced with explicitsedrules. Two reasons: regeneration then works on machines without a ROCm installation, and the handful of decisions that are not mechanical end up documented in one place rather than in a maintainer's head:GauXC::cuda::warp_size→GauXC::hip::warp_size(32 vs 64), so launch geometry adapts. No literal 32s were introduced.__syncwarp()has no HIP equivalent (wavefronts are lockstep on AMD) and is commented out — the convention the existing 2022-era files in this directory already used.__shfl_*_sync(mask, …)→__shfl_*(…), since HIP shuffles take no mask argument.cub→hipcub(cub::DeviceScanin the EXX screening kernel).cudaDevAttrMaxSharedMemoryPerBlockand…Optinmap tohipDeviceAttributeMaxSharedMemoryPerBlock. The overflow guard inexx_ek_screeningis preserved; the opt-in branch becomes a no-op.__stcs()and its inline-PTX fallback inpack_submatare lowered to a plain store.CUDART_VERSIONis undefined under HIP, so the preprocessor would otherwise select the PTX branch and fail to compile. The cache hint is an optimization, not semantics.GAUXC_ENABLE_CUTLASSis already a CUDA-only dependent option.Verification
Static checks only (see caveat):
scheme1_base.cxxnow resolves against the HIP tree;hip_aos_scheme1{,_data}.cxxwere checked to have the same method sets as their CUDA counterparts (the line-count difference is CUTLASS-only code), so they did not need regeneration.Caveat — please read before merging
This has not been compiled. I have neither a ROCm installation nor AMD hardware, so the changes have never been through
hipcc. Expect a round of compile fixes.Beyond compilation, the one correctness risk worth a reviewer's eyes is wavefront width: nothing hardcodes 32 and launch geometry derives from
hip::warp_size, but any intra-wavefront reduction whose trip count is written as a literal rather than derived fromwarp_sizeshould be checked before the numbers are trusted on AMD.hipify.shprints aCHECK_WAVEFRONTreminder to that effect.I am happy to iterate on this if someone with an AMD GPU can run the test suite, or to split it into "script + build wiring" and "regenerated kernels" commits if that is easier to review.