Repository navigation
HIP (ROCm) backend for LTS-C++17, C++17-only, plus LTS bug fixes - #401
Merged
Merged
Conversation
Add ENABLE_HIP (ON by default, disabled with a status message when no HIP
compiler is found) and cmake/libs/hip/{hip_init,hip,archs,target_generation,
debug}.cmake, modelled on main's HIP support:
- CMake's HIP language (amdclang++). HIP_STANDARD 17, HIP_STANDARD_REQUIRED,
HIP_EXTENSIONS OFF, so HIP sources compile with -std=c++17 like the CXX and
CUDA ones. TEMPLATE_DEPTH is applied as for CUDA.
- HIP_ARCHITECTURES follows CMAKE_HIP_ARCHITECTURES (the local GPU by
default); -DHIP_ARCH seeds it on the first configure.
- Targets link hip::host, not hip::device: hip::device adds
"-x hip --offload-arch" to the CXX sources of a target, which breaks
tests/main.cpp when the CXX compiler is not amdclang++.
- find_package(hip) also looks in the ROCm root of the HIP compiler found
by CMake, so HIP_PATH/ROCM_PATH need not be set.
Test, utest and benchmark discovery generate a _hip target next to _cpp and
_cu (sources marked __ONLY_CU__ or __ONLY_CPU__ are skipped), plus the
utest_saturate HIP libraries. New marker __ONLY_GPU__: CUDA and HIP targets
only, no _cpp target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
Stream_<ParArch::GPU_NVIDIA> never destroyed the cudaStream_t it created. Its destructor destroyed the stream only when getRefCount() == 0, but the Ref base class is destroyed after the body of the derived destructor, so the last copy always saw a count of 1 (a CPU mock of the same logic created 4 streams and destroyed 0). In addition, Ref::operator= took the other counter without releasing its own, so assigned-over counters leaked and the counts were wrong. Each fk::Stream leaked about 160 KB of device memory. Port main's fix (fcfe515 for Ref, fe3e17c for the stream), which is already C++17: - Ref: atomic counter, operator= releases the previous reference. - Stream_<GPU_NVIDIA>: the cudaStream_t is owned by a shared_ptr shared by the copies. The last copy destroys it with cudaStreamDestroy, which releases it when its pending work has finished (no synchronization, which would invalidate CUDA graph captures running in other threads). A stream given by the user is never destroyed. The class is a verbatim copy of main's. - utests/core/execution_model/utest_stream_ownership.h from main: copies share the stream, a copy keeps it usable after the original is gone, a user stream is not destroyed, and no device memory is lost. The _cpp target has nothing to test. Not compiled with nvcc on this machine (no CUDA toolkit); it must be verified on a CUDA machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
HIP declares its own vector types (::float3, ::uchar3...) in the global namespace (hip/hip_runtime.h). They can not be FKL's vector types: they are not aggregates, they bring their own operators, and some layouts differ from CUDA's and FKL's (::double4 is 32/32 under HIP). As on main (943895c), under HIP FKL uses its own types, the fk::Float3... structs the CPU backend already uses, and leaves the global names to HIP. So that one source compiles for every backend, the FKL vector types are now also named fk::float3, fk::uchar3... (and fk::bool1-4): - with nvcc and on the CPU they are using-declarations of the existing global types (the CUDA vector types, or the CPU aliases of fk::Float3...). They name the same types, so nothing changes for CPU and CUDA code, and code with "using namespace fk;" can keep using the global names there; - under HIP they are aliases of FKL's structs (fk::float3 = fk::Float3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The GPU_AMD backend, compiled by amdclang++ (__HIPCC__), modelled on main's HIP support and written in C++17: - utils.h: hip_runtime.h, the FK_* host/device macros, gpuAssert(hipError_t) and gpuErrchk, __grid_constant__ as clang's attribute. - parallel_architectures.h: defaultParArch is GPU_AMD under HIP. - stream.h: Stream_<ParArch::GPU_AMD>, main's class (the hipStream_t is owned by a shared_ptr: copies share it, the last one destroys it, a user stream is never destroyed). - ptr_nd.h, ptr_utils.h, image.h: hipMalloc/hipMallocPitch/hipHostMalloc, hipFree/hipHostFree, hipMemcpyAsync/hipMemcpy2DAsync, uploadTo/downloadTo and upload/download on Stream_<GPU_AMD>; defaultMemType is DeviceAndPinned, as with CUDA; setTo runs on the GPU stream as with CUDA. - data_parallel_patterns.h: TransformDPP and DivergentBatchTransformDPP for GPU_AMD, using blockIdx/blockDim/threadIdx instead of cooperative groups. DivergentBatchTransformDPPBase gets the GPU ParArch of the TransformDPP it runs as a template parameter (GPU_NVIDIA by default, as before). - executors.h, executor_kernels.h: Executor<TransformDPP<GPU_AMD>> and Executor<DivergentBatchTransformDPP<GPU_AMD>>, generated from the CUDA executors with the HIP runtime API. Main selects these with "requires (ARCH == ParArch::GPU_AMD)" (C++20); here they are partial specializations for ParArch::GPU_AMD. The host-only helpers (CtxDim3, getDefaultBlockSize) are shared with CUDA. - interpolation.h, color_conversion.h: the device intrinsics (__float2int_rd...) also under __HIP_DEVICE_COMPILE__. The CUDA code is unchanged: HIP code is in __HIPCC__ branches or specializations next to the CUDA ones, and only guards were widened to "defined(__NVCC__) || defined(__HIPCC__)" where code is API independent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The global names (float3, uchar3...) are HIP's own vector types in the _hip targets, so tests and utests use the fk:: names, which are the same types as before in the _cpp and _cu targets. Mechanical change (as main's 943895c), also in the token-pasting launch macros of test_thread_fusion.h. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
- fkBenchmarksCommon.h: TimeMarkerOne/TimeMarkerTwo<ParArch::GPU_AMD> with hipEvent_t, generated from the CUDA ones. The CUDA timers were guarded with "defined(__CUDACC__) || defined(__HIP__)", which would compile them, with the CUDA API, under HIP: they are now __CUDACC__ only (same as before with nvcc, which never defines __HIP__). - benchmark_thread_fusion.h: fk::-qualified vector types. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
Clang (-std=c++17) reports C++20 extensions in LTS code, which a strict
C++17 build (-Werror=c++20-extensions -pedantic-errors) rejects:
- array.h: the constexpr ArrayVector(const T& initValue) constructors of
the unions initialize no member ("constexpr union constructor that does
not initialize any member is a C++20 extension"). They now value-
initialize "at" before assigning its elements: same result.
- ptr_nd.h: uninitialized local variables in constexpr functions
("uninitialized variable in a constexpr function is a C++20 extension"):
size_t pitch in PtrImpl<_2D>::d_malloc and int currentDevice in
Ptr::allocDevice/allocHostPinned/allocDeviceAndPinned are initialized to
0 before the runtime calls set them.
- fkBenchmarksCommon.h: a stray ';' in BenchmarkResultsNumbersOne
(rejected by -pedantic-errors).
g++ does not report them, which is why they went unnoticed. No functional
change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
- test_ptr_nd.h: the uploadTo/downloadTo tests with Device and HostPinned pointers also run under HIP. - test_divergent_hf_executor.h: __ONLY_GPU__ instead of __ONLY_CU__, and defaultParArch instead of GPU_NVIDIA, so it runs on CUDA and HIP (there is no CPU executor for DivergentBatchTransformDPP). It now also checks the output: plane 0 must be imgA * 2 and plane 1 imgB + 100 (it only checked that the call compiled and ran). With nvcc it is the same test as before plus that check. - utest_VBase.h: under HIP, fk::float3 is FKL's aggregate vector type and HIP's ::float3 is not an FKL vector type (as main's utest). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
README: CPU, CUDA and HIP backends, how to configure a HIP build, and why code that compiles for HIP uses fk::float3... The build-and-test skill lists ENABLE_HIP, HIP_ARCH, the _hip suffix and the __ONLY_GPU__ marker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The CUDA build compares Horizontal Fusion with the same batch launched as a CUDA graph (benchmark_Horizontal_Fusion_NO_CPU_OVERHEAD_CUDAGraphs). The HIP build now runs the same comparison with HIP graphs (benchmark_Horizontal_Fusion_NO_CPU_OVERHEAD_HIPGraphs, written to ..._HIPGraphs_GPU_AMD.csv): the CUDA function generated with the hipGraph* API (hipStreamBeginCapture, hipGraphAddChildGraphNode, hipGraphInstantiate, hipGraphLaunch...). The CUDA version is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
With a GPU stream and a Ptr in MemType::Host or MemType::HostPinned, setTo launched a GPU kernel that wrote ptr(). For Host memory that pointer comes from malloc, so the kernel faults (HSA_STATUS_ERROR_MEMORY_FAULT on the RX 9060). Non-device memory is now filled by the CPU executor, as on main (fabbb80); the GPU branch is main's text. test_ptr_nd checks setTo on Host memory in every backend and on HostPinned memory in the GPU builds (the CPU backend can not allocate pinned memory). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
launch() ignored the results of the fused-vs-iterated comparisons and always returned 0, so ctest and the paper runs passed with wrong results. It now ANDs every launch_benchmark_* result, prints a Passed!!!/Failed!!! line as benchmark_thread_fusion does, and returns -1 on any failure. The CSV files are unchanged. benchmark_thread_fusion already returned -1 on a mismatch. benchmark_latency_hiding and benchmark_numinstructions_x_operation only time their executions and have no comparison to fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
… data Executor<TransformDPP<ParArch::GPU_AMD>> gets the alignment guard of the CUDA executor (28101e3): when the pointers or pitches of a thread fused read or write are not aligned to the thread fusion vector types, it launches the TF::DISABLED kernel through launchNonThreadFused. The HIP executor is again the CUDA one with the HIP runtime API names. On the RX 9060 (gfx1200) misaligned thread fused accesses do not fault and give the same results, so this keeps the backends consistent rather than fixing a crash there. In test_thread_fusion_alignment_hip the guard moves 147 of 434 launches to the TF::DISABLED kernel. hipMallocPitch pitches are 256-byte aligned, so FKL-allocated device Ptr2Ds keep thread fusion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
Like the other tests (dfd946e): under HIP the unqualified uchar2 and float2 are HIP's own vector types, not FKL's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
- Ptr::crop and Ptr::getPlane incremented the count and then built the result with a constructor that increments it again, so the allocation of every cropped Ptr (and Image) was never freed. - freePtr() only cleared ref when it released the last reference. Alloc and allocTensor on a Ptr that shared its allocation then threw, and the destructor released the same reference again: a use-after-free of the other owners' memory. test_ptr_nd checks the counts of crops, planes, a crop that outlives its parent, and Alloc/allocTensor on a shared Ptr. Closes #377 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HaJGJQnKer8xAT5P66U9Dz (cherry picked from commit d0588f8) LTS-C++17: test_ptr_nd keeps test_setTo_host and its launch() (no launch_impl or namespace fk wrapper), adds test_shared_allocation_ref_counts before them and ANDs its result into the return value; the new test uses fk::uchar3 so that the _hip target builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
Configure every LTS workflow with -DENABLE_BENCHMARK=ON so that CI compiles the benchmarks (fkBenchmarksCommon.h, the CUDA graphs variant of benchmark_horizontal_fusion...) with every host compiler and CUDA version of the matrix. They were never built in CI before. The benchmarks register as ctest tests (benchmark_*_cpp and _cu); benchmark_thread_fusion_cpp alone takes more than 10 minutes, so the Test step excludes them with -E "^benchmark_". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The Linux x64, ARM64 and Windows jobs asked for [self-hosted, linux, x64] (or arm64/windows) only. Those labels also match runners without a CUDA toolkit: main's CPU job runs on asusb550mlinux, the ROCm machine, which has the self-hosted, linux, x64 labels and no nvcc. A CUDA job scheduled there finds no CUDA compiler, disables CUDA and passes without building or testing a single _cu target (or fails because the host compiler of the matrix is not installed there). Ask for the cuda label too, as main's CUDA workflows do (minisforumwsl, jetsonagxorin and MINISFORUM carry it). The Ubuntu 24.04 job already asks for its own ubuntu2404 label. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
0e56a81 (main's fabbb80) sent every non-Device Ptr to a synchronous CPU fill when the stream is a GPU stream. That fixed MemType::Host, which the GPU can not write, but HostPinned memory was filled by a kernel on the stream before, and the CPU fill is not ordered with the work already enqueued: a download into the Ptr that is still pending overwrites the values, an upload from it that is still pending copies the new values, and a graph capture does not record the fill. The pinned copy of DeviceAndPinned memory had the same problem (also before 0e56a81). With a GPU stream, setTo now: - fills Device and HostPinned memory with a kernel on the stream, as LTS-C++17 did before 0e56a81; - fills both copies of DeviceAndPinned memory with kernels on the stream; - fills Host memory from the CPU after stream.sync(), so that the work enqueued before setTo has finished. test_ptr_nd checks the order with copies that wait behind a busy GPU (a clock64() spin kernel): a download into HostPinned and into Host memory, an upload from HostPinned memory, and a download of DeviceAndPinned memory, each followed by setTo. On the RX 9060 the old code fails the HostPinned download, HostPinned upload and DeviceAndPinned cases (3/3 runs); the new code passes them. Reported by the NVIDIA-side review of PR #401 (verified on the RTX 4090) and by the review of this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
(cherry picked from commit 1838493) LTS-C++17: CircularTensor::update launches a DivergentBatchTransformDPP whose plane space is the sum of the planes of its sequences. The copy sequence declared BATCH planes (CircularTensorRead::num_elems_z returned BATCH) instead of BATCH - 1, so a last row of blocks wrote plane BATCH, past the end of the output Tensor, in every update, on CUDA and HIP. On the RX 9060 that write lands in the update's temporary tensor: with BATCH 4 and 5 the oldest plane ends up holding a newer input. The library change is main's commit as is. Main's test_circular_tensor.h does not exist on LTS: tests/data/test_circular_tensor.h is its GPU test (__ONLY_GPU__, since LTS has no CPU executor for DivergentBatchTransformDPP) for BATCH 2, 3, 4, 5, 8 and 15. Without the fix it fails for BATCH 4 and 5 on HIP; with it, all pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
update throws std::runtime_error when the CircularTensor is not in Device or DeviceAndPinned memory and the stream is a GPU stream, but it only checked ParArch::GPU_NVIDIA. With the GPU_AMD DivergentBatch executor a Host-memory CircularTensor was launched on the GPU: on the RX 9060 it aborts with "an illegal memory access was encountered". The check now covers GPU_NVIDIA and GPU_AMD, as setTo does. test_circular_tensor checks that the update of a Host-memory CircularTensor with a GPU stream throws. Reported by the NVIDIA-side review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The HIP code paths (ParArch::GPU_AMD streams, memory, DPPs and executors, the fk:: vector type aliases, the HIP timers) were selected with __HIPCC__. ROCm's hip/hip_common.h also defines __HIPCC__ in nvcc and clang CUDA compilations that include a HIP header (HIP on NVIDIA), so a .cu file that included one before FKL got both backends: fk::float3 became fk::Float3 instead of ::float3, ptr_nd.h allocated with hipMalloc and copied with cudaMemcpyAsync, and Stream_<GPU_AMD> was defined with the CUDA one. LTS-C++17 before the HIP backend compiled such files. compiler_macros.h defines FK_HIP_BACKEND for HIP compilations by clang (__HIP__, which only clang predefines, in HIP mode) that are not CUDA ones, and every former use of __HIPCC__ in include/, tests/, utests/ and benchmarks/ tests FK_HIP_BACKEND instead. The stale "__HIP__" #endif comments of ptr_nd.h and image.h name the new macro. No change for real HIP, CUDA or CPU builds: the preprocessed output of all 190 translation units of the HIP + CPU build (241 host, device and CPU passes) is byte-identical before and after. A CPU compilation of test_ptr_nd with -D__HIPCC__ failed before and is now identical to one without it. Reported by the NVIDIA-side review of PR #401 and by the review of this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
…nd CUDA b8752fd made fk::float3, fk::uchar3... using-declarations of the global vector types with nvcc and on the CPU, so that CPU and CUDA code keeps the same types, but only the HIP side was checked. utest_VBase now also asserts, outside HIP, that fk::float3, fk::uchar1, fk::uchar3, fk::int2, fk::double4 and fk::bool3 are the global types. Suggested by the NVIDIA-side review of PR #401 and by the review of this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
… only - The _cpp target only printed "nothing to test" and passed: the source is now marked __ONLY_GPU__, as test_divergent_hf_executor.h, and the CPU stub is gone. - The "no device memory lost" check can not fail under HIP: ROCm maps the streams onto a few hardware queues and reuses their memory. On the RX 9060, 4000 streams created after a few others and never destroyed cost 0.00 MB of free device memory, so the check passed with a Stream_ that leaks every stream. It now runs with nvcc only, where each CUDA stream holds about 160 KB; the HIP target keeps the sharing, lifetime and user stream checks. Reported by the NVIDIA-side review of PR #401 and by the review of this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
- hip_init.cmake set ENABLE_HIP OFF in the cache with FORCE when no HIP compiler was found, so configuring the same build directory again with -DCMAKE_HIP_COMPILER=... left HIP disabled. CMakeLists.txt now runs check_language(HIP) and defines the ENABLE_HIP option only when a compiler is found, as it does for CUDA; otherwise ENABLE_HIP is a plain OFF variable for that configure, and an ENABLE_HIP the user put in the cache is left alone. - On a machine with HIP for NVIDIA GPUs, check_language(HIP) could pick nvcc as the HIP compiler and the _hip targets would then fail to build. FKL's HIP backend is ParArch::GPU_AMD (CUDA is the NVIDIA backend), so CMAKE_HIP_PLATFORM defaults to "amd" before the check, which then only considers compilers for AMD GPUs, and HIP is enabled only for that platform. Checked: the build-hip and build-verify trees reconfigure unchanged; a new build directory with HIPCXX=g++ disables HIP (no _hip targets, no ENABLE_HIP in the cache) and enables it when configured again with -DCMAKE_HIP_COMPILER=amdclang++; -DCMAKE_HIP_PLATFORM=nvidia -DENABLE_HIP=ON disables HIP without errors. Reported by the NVIDIA-side review of PR #401 (FORCE) and by the review of this branch (HIP platform). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The CUDA and HIP executors computed their grids as ceil(activeThreads.x / static_cast<float>(block.x)) (and .y), and TransformDPP::build_details the thread fusion thread space as ceil(width / static_cast<float>(elems_per_thread)). The uint is rounded to a float first, so above 2^24 the result can be one too small and the last elements are silently never written: a 1D Ptr of 2^24 + 1 elements lost its last element without thread fusion, and one of 2^26 + 4 uchar its last 4 with it, on the GPU and, for thread fusion, on the CPU too. - active_threads.h: ceilDiv(a, b), a / b rounded up with integers. - executors.h: computeGrid(activeThreads, block), shared by the CUDA and HIP executors (TransformDPP and DivergentBatchTransformDPP, 6 grids), replaces the float ceil. - data_parallel_patterns.h: build_details uses ceilDiv. Host code only: the kernels are unchanged, and below 2^24 the sizes are the same as before. tests/thread_fusion/test_large_grids.h writes 2^24 + 1 elements without thread fusion and 2^26 + 4 (and 2^24) with it and counts the elements not written. On the RX 9060 the old code misses 1 and 4 elements in test_large_grids_hip and 4 in test_large_grids_cpp; the new code misses none. Reported by the NVIDIA-side review of PR #401 (also on main and on LTS-C++17 before this branch). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
- The deleter of the cudaStream_t discarded the result of
cudaStreamDestroy, but a failed destroy leaves its error pending, and
the next cudaGetLastError() reports it for unrelated work: after
"{ fk::Stream s; cudaDeviceReset(); }" the next launch threw "invalid
device context" in its gpuErrchk (it did not on 1a9bfe8). The deleter
now clears the error of the destroy, unless an error was already
pending before it, which is left for its owner. The HIP deleter does
the same with hipPeekAtLastError/hipGetLastError. On ROCm 10.1 that
case leaves no error (hipStreamDestroy after hipDeviceReset succeeds),
so for HIP this keeps the backends alike.
- The comment of Stream_<GPU_AMD>, copied from main, said that
hipStreamDestroy releases the stream when its pending work has
finished. On ROCm it blocks the host until that work has finished
(93.6 ms for a pending 94 ms kernel on the RX 9060); the comment says
so.
Reported by the NVIDIA-side review of PR #401 (error) and by the review
of this branch (comment).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
In a CUDA or HIP build, setTo with a Stream_<ParArch::CPU> on a Ptr in Device or DeviceAndPinned memory ran the CPU executor on ptr(), a device pointer, from the CPU: a segfault with CUDA. It now throws std::runtime_error, as main's CPU_MULTITHREAD setTo does for those memory types. Host and HostPinned memory are filled by the CPU as before. test_ptr_nd checks that it throws for Device and DeviceAndPinned memory (without the check, the _hip target fails it). Reported by the NVIDIA-side review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
raw_image.h names uchar3, ushort3 and float3 (ColorDepthPixelTypes) but did not include vector_types.h. Under HIP, when it was the first FKL header, those names found HIP's global vector types (hip_runtime.h comes in through rawptr.h), not fk::uchar3..., and a ReadYUV into a Ptr2D<fk::uchar3> failed with "no known conversion from HIP_vector_type<unsigned char,3> to fk::Uchar3". On the CPU the same include order did not compile. With the include, a translation unit that includes raw_image.h first compiles in both, and ColorDepthPixelType<p8bit> is fk::uchar3. Reported by the review of this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
…ribute Under HIP, __grid_constant__ was defined only when clang supports the grid_constant attribute; with a clang that does not, the kernels in executor_kernels.h failed with "unknown type name __grid_constant__". On AMD GPUs kernel parameters are always in the constant kernel argument segment, so it is now defined empty there. No change with ROCm 10.1, whose clang supports the attribute. Reported by the review of this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
clang accepts some C++20 features in -std=c++17 mode as extensions, with a warning (designated initializers, uninitialized variables in constexpr functions...); e354f0e removed the ones LTS had, but nothing kept new ones out. HIP sources are now compiled with -Werror=c++20-extensions (which covers -Wc++20-designator), so the HIP build keeps the code C++17. The CUDA and CPU flags are unchanged. Also drops the stale "#todo: add hip support" in benchmarks/CMakeLists.txt: the benchmarks have _hip targets. Suggested by the review of this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
- README: the sample and the snippets that explain it used uchar3 and float3 with "using namespace fk;", which is ambiguous under HIP (HIP's global types and fk::uchar3...). The code now uses the fk:: names, like tests/examples/readme_test_code.h. The prose that names the resulting types is unchanged. - README, Backends and building: the __ONLY_* markers and ENABLE_BENCHMARK decide which targets are built; the HIP build uses the default C++ compiler (it said g++ but did not select it); the example no longer enables the benchmarks, and says how to skip them in ctest (benchmark_thread_fusion_cpp alone takes more than 10 minutes). - fkl-build-and-test skill: the loop over *_cu *_hip *_cpp printed "*_hip FAIL" in every build without HIP (an unmatched glob stays literal). It sets nullglob first. Reported by the review of this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
In the first CI run of PR #401 only 2 of the 12 jobs (ARM64, CUDA 12.9) built CUDA: the others pointed CUDACXX at toolkits the runners no longer have (/usr/local/cuda-13.3 on minisforumwsl and jetsonagxorin, 12.9 and 13.3 on MINISFORUM), so check_language(CUDA) found nothing, CUDA was disabled, and the jobs passed with the CPU targets only ("CUDA compiler not found, CUDA support will be disabled", 34 tests instead of 73). - The Linux x64 and ARM64 jobs use CUDA 13.4, the version main's CUDA workflows use on the same runners, instead of 13.3 (ARM64 keeps its CUDA 12.9 jobs, which work). - The Linux workflows pass -DCMAKE_CUDA_COMPILER, as main's do, so a toolkit that is not installed fails the configure step ("Compiler requires the CUDA toolkit") instead of silently testing the CPU backend only. The Windows workflow is not changed here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
…stPinned - The Host memory case of test_setTo_stream_order downloaded into pageable memory, but a copy into pageable memory returns only when it has finished (CUDA documents it, and on the RX 9060 downloadTo blocked until the spin kernel and the copy were done), so the case passed without the stream.sync() that c5ada13 added to setTo. The earlier work is now a host function (cudaLaunchHostFunc/hipLaunchHostFunc) enqueued behind the spin kernel that writes the memory. On the RX 9060 a setTo without the sync fails the case 3/3; the current one passes. - setTo with a CPU stream fills HostPinned memory from the CPU in the GPU builds: checked next to the Device and DeviceAndPinned cases that throw. Found by the round 3 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
…error 25c8559 makes the Stream_ deleter clear the error of a failed cudaStreamDestroy (unless one was already pending), so that the next cudaGetLastError() does not report it for other work. With nvcc the utest now checks it last: a GPUStream destroyed after cudaDeviceReset must leave no error pending. Under HIP there is nothing to check: hipStreamDestroy after hipDeviceReset succeeds on ROCm 10.1. Compiled with nvcc 13.4 (g++-13 and g++-15 as host compilers); it runs only on a CUDA machine. Found by the round 3 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
A compile time test for 75411b2: a translation unit whose first FKL header is raw_image.h must see FKL's vector types, so ColorDepthPixelType<p8bit/p10bit/f24bit> are fk::uchar3, fk::ushort3 and fk::float3, also under HIP, where hip_runtime.h declares its own global uchar3... With the raw_image.h of before 75411b2 neither target compiles (5 errors in the _hip one, 25 in the _cpp one). Found by the round 3 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
- README and the build-and-test skill presented -DHIP_ARCH as a way to choose the HIP architectures, but hip_init.cmake uses it only when CMAKE_HIP_ARCHITECTURES is not cached yet: they now say "on the first configure of a build directory", and -DCMAKE_HIP_ARCHITECTURES to change it later. - The skill's CI section said CUDA 12.9 and 13.3: since 0a0ed04 the Linux workflows use 12.9 and 13.4. It also says that CI builds the benchmarks without running them, and that no workflow builds HIP. Found by the round 3 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
7309118 replaced __HIPCC__ with FK_HIP_BACKEND in every header, including the new comment that explains why __HIPCC__ can not select the HIP backend, which then said that FK_HIP_BACKEND does not identify it. Comment only. Found by the round 3 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The DivergentBatch executors build their block as min(activeThreads.x, 32) x min(activeThreads.y, 8), which is 0 wide (or high) for an empty thread space. 2ce1f46 computes the grid with ceilDiv(activeThreads.x, block.x), an integer division by zero there: on x86-64 the process died with SIGFPE (both CUDA and HIP), where the float formula before it gave a grid the launch rejected, so gpuErrchk threw "invalid configuration argument". ceilDiv(a, 0) is now 0, so the launch fails and throws as before. The TransformDPP executors are not affected (getDefaultBlockSize never returns 0). test_circular_tensor updates a CircularTensor that is not allocated with an empty input and checks that it throws: without the fix the _hip target dies with SIGFPE (exit 136). Found by the round 3 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
ENABLE_HIP_LINE_INFO (ON by default) added -gline-tables-only to every HIP compile, after the -g of CMAKE_HIP_FLAGS_DEBUG and of add_hip_debug_support_to_target. clang keeps the last -g* flag, so a Debug or ENABLE_DEBUG build of a _hip target only had line tables (no variables in rocgdb). As add_cuda_lineinfo_to_target does for -lineinfo, the line tables are now added only without ENABLE_DEBUG, and only for configurations other than Debug: a Debug test_ptr_nd_hip compiles with -g, a Release one with -gline-tables-only as before. ENABLE_DEBUG was declared only by cmake/libs/cuda/debug.cmake, so a configure without CUDA did not offer it: hip/debug.cmake declares it too. Found by the round 3 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The LTS workflows test CPU and CUDA, but ENABLE_HIP defaults to ON when check_language(HIP) finds a HIP compiler for AMD GPUs: a runner that also has ROCm installed would build every _hip target and ctest would run them. The three Linux workflows now pass -DENABLE_HIP=OFF, as main's CUDA workflows do. On this machine (ROCm 10.1 installed) a configure with it has no _hip targets. Found by the round 3 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
…aviour, segfaults on CUDA 13.4) 5e4c0ea checked that a GPUStream destroyed after cudaDeviceReset leaves no pending error. On the RTX 4090 (CUDA 13.4, driver R610) utest_stream_ownership_cu then segfaults (exit 139, 3/3 runs): gdb shows SIGSEGV in libcuda under cuStreamDestroy_v2, from cudaStreamDestroy in the owned-stream deleter, on the destructor of that GPUStream. cudaDeviceReset destroys all the resources of the context, and passing a stream from before the reset to a later CUDA call is undefined: whether the destroy returns an error or crashes depends on the state of the process, so no ordering makes the check reliable. The check is removed, with a comment that says why; the deleter of 25c8559 is unchanged. With the patch the test prints "GPU stream ownership OK" 3/3 and is compute-sanitizer clean on the 4090. Found by the NVIDIA agent's verification of a6ae66c (patch from it, through fkl-paper). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The e1a094b assert static_assert(std::is_same_v<fk::double4, ::double4>) was the only new warning site under nvcc: CUDA 13 deprecates ::double4 ("use double4_16a or double4_32a"). It checks fk::double3 instead, which shows the same thing (a using-declaration of the global double vector type) without the warning. Reported by the NVIDIA agent's verification of 0a0ed04. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
6724ed1 fixed Ref::operator=, which took the counter of the other object without releasing its own (and made the counter atomic), but no test checked the counts: utest_stream_ownership only checks the native GPU streams, now owned by a shared_ptr. Every Stream_ derives from Ref, so the new utest checks the counts through Stream_<ParArch::CPU> in all three backends: copies share a counter, an assignment leaves the old counter (c drops to 1) and shares the new one, assigning the same counter or the object itself changes nothing, and a copy that goes away releases its count. With the ref_class.h of origin/LTS-C++17 the _cpp target reports "a 3, c 2 WRONG" and exits -1; with the fix all pass (_cpp and _hip run here, _cu compiled with nvcc 13.4). Found by the round 4 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
- test_setTo_host said the device can not write the memory and the values must be written from the CPU; since c5ada13 that holds for Host memory only, and HostPinned memory is filled by a kernel on the GPU stream. Comment only. - README: the __ONLY_* markers apply to test and utest sources; benchmark discovery does not read them. Found by the round 4 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
42b4f0f dropped -gline-tables-only both in Debug and whenever ENABLE_DEBUG is ON, but add_hip_debug_support_to_target adds -g only in the Debug configuration: a Release configure with ENABLE_DEBUG=ON compiled the HIP objects with neither -g nor line tables, less than a plain Release build. The line tables now depend only on the configuration: Debug (with or without ENABLE_DEBUG) gets -g, every other configuration -gline-tables-only. The comment said ENABLE_DEBUG gives -g outside Debug; it does not. Found by the round 4 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
hip_init.cmake put $HIP_PATH and $ROCM_PATH before CMAKE_HIP_COMPILER_ROCM_ROOT in CMAKE_PREFIX_PATH. With two ROCm installations, the environment pointing at one and the HIP compiler coming from the other, find_package(hip) took the environment's package, and hip::host linked a libamdhip64 other than the one the HIP language links for the compiler's ROCm. The compiler's ROCm root now comes first. No change with a single ROCm installation (on this machine both resolve to /opt/rocm/core-10.1/lib/cmake/hip). Found by the round 4 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
With -DENABLE_BENCHMARK=ON, the configure that CI and the README use, the loop over *_cu *_hip *_cpp also ran the benchmark_* binaries, and benchmark_thread_fusion_cpp alone takes more than 10 minutes, not the "few minutes" the skill promises. The loop skips them, as ctest -E "^benchmark_" does. Found by the round 5 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
- 0373c8a added -gline-tables-only in every configuration but Debug, and said Debug was the only one with -g. RelWithDebInfo has it too (CMAKE_HIP_FLAGS_RELWITHDEBINFO is -O2 -g -DNDEBUG, and cmake_init.cmake lists RelWithDebInfo in CMAKE_CONFIGURATION_TYPES), so a RelWithDebInfo HIP build only had line tables. The line tables are now added outside Debug and RelWithDebInfo: Debug -g, RelWithDebInfo -O2 -g, Release -O3 and MinSizeRel -Os with -gline-tables-only. - The comment of the ENABLE_DEBUG declaration in hip/debug.cmake says that the option changes nothing for HIP: Debug builds already compile host and device code with -g and without optimizations. Found by the round 5 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
c5ada13 fills Host (pageable) memory from the CPU after stream.sync() when the stream is a GPU stream. On a stream that is being captured into a graph that sync is not allowed: gpuErrchk threw "operation not permitted when stream is capturing", and the capture was invalidated, so the work captured before it was lost too (hipStreamEndCapture: "operation failed due to a previous error during capture"; CUDA documents the same for cudaStreamSynchronize during capture). setTo now checks cudaStreamIsCapturing/hipStreamIsCapturing first and throws std::runtime_error, leaving the capture as it was. The comment also says that the GPU can write pageable memory on systems that give GPUs access to it (HMM/ATS). On those, LTS-C++17 before this branch filled Host memory with a kernel, also in a capture; now the CPU fills it, with the same result outside a capture. test_ptr_nd captures a setTo on Device memory and then a setTo on Host memory: the second must throw, and the capture must end with a valid graph of 1 node. Without the check the _hip target gets "capture valid 0, 0 nodes" and fails; with it, "threw 1, capture valid 1, 1 nodes". The _cu target compiles with nvcc 13.4 (g++-13 and g++-15). Found by the round 5 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
FKL is header only, and its templates and inline functions get a different definition in each backend under the same name: a program that links a .cpp translation unit compiled for the CPU and a .hip one that both instantiate Ptr2D<fk::uchar3> keeps one of the definitions (the round 5 reviewer's probe aborted with "Device allocation not supported in non-CUDA compilation" or worked, depending on the link order). With CUDA this already held for scalar element types; under HIP the fk:: vector types are the same types as on the CPU, so it also holds for them. Every FKL target is built for one backend; the README now says not to mix them in one program. Found by the round 5 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
The spin kernel of the stream order tests waited for 50M clock64() cycles. Under HIP clock64() is __builtin_readcyclecounter(), which on gfx11 (RDNA3: RX 7000, W7000, Phoenix/Strix APUs) reads hwreg(HW_REG_SHADER_CYCLES, 0, 20), a 20 bit counter that wraps: the difference never reaches 50M, so test_ptr_nd_hip would never finish there (gfx1200 reads the 64 bit SHADER_CYCLES_HI/LO, so it passed on the RX 9060). Under HIP the kernel now waits for 20 ms of wall_clock64() ticks (hipDeviceAttributeWallClockRate, in kHz), which on gfx1100 and gfx1200 is the 64 bit realtime counter (s_sendmsg_rtn_b64 MSG_RTN_GET_REALTIME). The CUDA kernel is unchanged: clock64() is 64 bit there. On the RX 9060 all the cases pass 3/3, and with the non-Device memory filled from the CPU without a sync (as before c5ada13) the HostPinned download, Host host function and HostPinned upload cases still fail 3/3. The gfx1100 code of the kernel has no SHADER_CYCLES read. The _cu target compiles with nvcc 13.4. Found by the round 6 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
.github/copilot-instructions.md still described the branch before this PR: CUDA and CPU backends only, vector types outside the fk namespace, only the ONLY_CU and ONLY_CPU markers and .cpp/.cu targets, CI runners without the cuda label, CUDA 12.9/13.3 and compilers "not as CMake -D flags". An agent following it would write tests with the global float3 (HIP's own type in the _hip targets) or mark a GPU test ONLY_CU. It now matches the README and the fkl-build-and-test skill: three backends and the fk:: vector type names, the HIP requirements and options (ENABLE_HIP, HIP_ARCH), the _hip targets and the ONLY_GPU marker, FK_HIP_BACKEND, the HIP stream wrapper, and the CI workflows as they are now (cuda label, 12.9/13.4, -DCMAKE_CUDA_COMPILER, -DENABLE_HIP=OFF, benchmarks built but not run, Windows CPU only, no HIP job). Found by the round 6 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
divergent_operate passed the function pointer launchTransformDPP<IOps...> to apply_d. Without optimizations, as in HIP Debug builds (-g, no -O), clang keeps that as an indirect call (a readfirstlane loop and s_swappc_b64), and the static scratch size of the kernel does not include the callee's frame: the runtime gives it the default hipLimitStackSize of 1024 bytes per lane, while the CircularTensor update kernel needs about 3256 and the test_divergent_hf_executor one about 2120. In a Debug build test_circular_tensor_hip aborted with HSA_STATUS_ERROR_MEMORY_FAULT on its first update (private_seg_size 1024), and test_divergent_hf_executor_hip passed only by luck; at -O1 and above both pass. The line is older than the HIP backend; nvcc -G resolves the call statically. A generic lambda that calls launchTransformDPP is a direct call the compiler can see through, as executors.h already does with apply. In a Debug HIP build both tests now pass. In the Release build the gfx1200 device code of both tests is unchanged (the same instructions; only debug line info differs), and nvcc 13.4 compiles both with -O3 and -G without stack size warnings. Found by the round 7 review of PR #401. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
From the NVIDIA agent's capture probes on fd04605, documented in the setTo comment (comment only): - the stream.sync() of a Host fill on a stream that is not capturing still invalidates a capture the same thread makes on another stream in global mode, as any synchronization does (relaxed mode works); - with an fk::Stream wrapping the legacy default stream while another stream captures, cudaStreamIsCapturing returns cudaErrorStreamCaptureImplicit and gpuErrchk throws (the other capture survives). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n
This was referenced Oct 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an AMD GPU backend (HIP/ROCm,
ParArch::GPU_AMD) to LTS-C++17. It is written in strict C++17 and modelled on main's HIP support. It also fixes LTS bugs found while porting and in review, most of which affect CPU and CUDA too. Apart from those fixes, CPU and CUDA behaviour is unchanged: the HIP code is behindENABLE_HIP, theFK_HIP_BACKENDmacro andParArch::GPU_AMD.What it adds
ENABLE_HIPexists when CMake finds a HIP compiler for AMD GPUs (amdclang++) and is then ON by default.HIP_STANDARD 17, with-Werror=c++20-extensions._hiptarget next to_cpp/_cu. A new__ONLY_GPU__marker limits a test to the CUDA and HIP targets.FK_HIP_BACKENDselects the HIP code paths, not__HIPCC__. The HIP headers also define__HIPCC__in nvcc compilations that include them, so that macro can't tell the two apart.Ptrmemory,TransformDPP/DivergentBatchTransformDPPand executors. Main selects these withrequires; here they are partial specializations. The HIP executors are the CUDA executors with the HIP API names, including the thread fusion alignment fallback of 28101e3/1a9bfe8.fk::float3,fk::uchar3... in every backend (as main's 943895c). On CPU and CUDA they are the existing global types. Under HIP they are FKL's own structs, because HIP's global vector types are not FKL vector types.Stream_owns its native stream through ashared_ptr, for both GPU_NVIDIA and GPU_AMD (from main's fcfe515/fe3e17c). The LTSStream_never destroyed thecudaStream_tit created.benchmark_horizontal_fusion._hiptargets and thefk::vector types. They also say not to link FKL code compiled for different backends into one program.LTS bug fixes (CPU and CUDA too)
setTosetToon Host memory throws and leaves the capture intact.Memory and resource bugs
Ptr::crop/getPlanereference counts: backport of main d0588f8 (Ptr::crop and Ptr::getPlane leak the parent allocation #377).CircularTensor::updatewrote one plane past the end of the tensor on every update: backport of main 1838493. On the RTX 4090, without it test_circular_tensor fails BATCH 2, 4 and 5 and compute-sanitizer reports 3041 out-of-bounds writes. Its Device-memory check now also covers HIP streams.Ref::operator=kept its old counter (6724ed1).cudaGetLastError().Grid sizes
ceilDivinstead ofceil(n / float(d)). With the float version, sizes above 2^24 lost their last block, and the last elements were never written.HIP Debug builds
DivergentBatchTransformDPPcalls each sequence through a lambda, not a function pointer.test_circular_tensor_hipthen crashed with a memory fault in a Debug build.Headers
raw_image.his self-contained.array.h,ptr_nd.handfkBenchmarksCommon.h.Benchmarks
benchmark_horizontal_fusionreturns non-zero when a comparison fails. The CSV output is unchanged.Many of these also exist on main: the
setToordering, grid sizes,__HIPCC__selection, theCircularTensorcheck, the stream destroy error,__grid_constant__and the HIP utest checks.CI changes
-DENABLE_BENCHMARK=ON). ctest skips them (-E "^benchmark_").-DCMAKE_CUDA_COMPILER, so a missing toolkit fails the job.-DENABLE_HIP=OFF.cudarunner label, so they can't run on the ROCm runner.cmake-linux-rocm-amd64.yml) follows in a separate PR.Verification at the final tip
_hipand 39_cppctest entries pass, benchmarks included (benchmark_thread_fusion_cppis left out as too slow).-pedantic-errors -Werror=c++20-extensions: 38/38_hipand 36/36_cpptests.ENABLE_CPU=OFF: 38/38._cutargets compile with nvcc 13.4.-GDebug runs identical to the previous tip.test_circular_tensor_cu'sDivergentBatchTransformDPPkernel for BATCH 4, after the lambda change above. It has the same 80 instructions and the same loads and stores; only the circular-index wrap computation moved from uniform to per-thread registers. That is a codegen variation, not a behaviour change._cutarget was compiled with nvcc 13.4.Review
Known issue (ROCm, not FKL)
HIP_FORCE_DEV_KERNARG=0and in RelWithDebInfo. Run alone, every Debug test passes.Not ported (intentionally)
ParArch::CPU_MULTITHREAD.🤖 Generated with Claude Code
https://claude.ai/code/session_019iTqfS8rWiAyRMrM4uU64n