CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
426 lines
13 KiB
Plaintext
426 lines
13 KiB
Plaintext
---
|
|
globs: ["*.cpp", "*.hpp", "*.h", "*.cxx", "*.cc"]
|
|
---
|
|
|
|
# C++ Code Style
|
|
|
|
> **Note to AI Agents:** If you find code patterns that contradict these style guidelines, suggest rule updates to match actual practices.
|
|
|
|
## Naming Conventions
|
|
|
|
- Use snake_case for functions and variables (e.g., `rocblas_gemm`, `matrix_size`)
|
|
- Use SCREAMING_SNAKE_CASE for macros and constants (e.g., `ROCBLAS_API_CALL`)
|
|
- Use PascalCase for class and struct names when appropriate
|
|
- Prefix internal/private functions with underscore or place in detail namespace
|
|
- Follow BLAS naming conventions for API functions:
|
|
- Format: `rocblas_<precision><operation>`
|
|
- Examples: `rocblas_sgemm`, `rocblas_daxpy`, `rocblas_cgemv`
|
|
|
|
## File Headers
|
|
|
|
- Add copyright header to all source files, updating present year in text:
|
|
|
|
```cpp
|
|
/* ************************************************************************
|
|
* Copyright (C) 2016-[CURRENT YEAR] Advanced Micro Devices, Inc. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cop-
|
|
* ies of the Software, and to permit persons to whom the Software is furnished
|
|
* to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM-
|
|
* PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNE-
|
|
* CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* ************************************************************************ */
|
|
```
|
|
|
|
- For header files (.h, .hpp), use `#pragma once` for include guards
|
|
|
|
## Code Practices
|
|
|
|
- Use explicit type conversions with `static_cast<>` where needed
|
|
- Prefer const correctness for function parameters
|
|
- **Never use hipMalloc/hipFree** - use `handle->device_malloc()` for device memory (see Device Memory section)
|
|
- Use other HIP functions as needed (hipMemcpy, hipStreamSynchronize, etc.)
|
|
- Always check return codes from rocBLAS API calls in client code
|
|
- Use braces for all control flow statements, even single-line bodies
|
|
- Follow const-correctness: use `const` for read-only parameters and member functions
|
|
- Use RAII patterns for resource management (memory, pointer mode changes, etc.)
|
|
|
|
## Device Memory Allocation
|
|
|
|
**Critical Rule:** rocBLAS code must NOT call `hipMalloc()` or `hipFree()` directly - they are synchronizing APIs.
|
|
|
|
### Using rocBLAS Device Memory Manager
|
|
|
|
```cpp
|
|
// Allocate device memory using RAII wrapper
|
|
auto w_mem = handle->device_malloc(dev_bytes);
|
|
if(!w_mem)
|
|
return rocblas_status_memory_error;
|
|
|
|
// Use the memory
|
|
void* workspace = static_cast<void*>(w_mem);
|
|
// Memory automatically freed when w_mem goes out of scope
|
|
```
|
|
|
|
### Key Requirements
|
|
|
|
1. **Allocate upfront:** All device memory must be allocated at the start of the function
|
|
2. **RAII pattern:** Use `rocblas_device_malloc` for automatic cleanup
|
|
3. **Pass down:** Lower-level kernels receive pre-allocated memory from higher-level routines
|
|
4. **Naming:** Use `w_mem`, `workspace`, or `w_` prefix for workspace variables
|
|
5. **No nested allocation:** Don't allocate/deallocate at different kernel call levels
|
|
|
|
### Multiple Memory Blocks
|
|
|
|
```cpp
|
|
// Allocate multiple blocks
|
|
auto mem = handle->device_malloc(size1, size2, size3);
|
|
if(!mem)
|
|
return rocblas_status_memory_error;
|
|
|
|
// Access individual blocks
|
|
void* ptr1 = mem[0];
|
|
void* ptr2 = mem[1];
|
|
void* ptr3 = mem[2];
|
|
```
|
|
|
|
## Implementation Pattern (_impl/_launcher)
|
|
|
|
rocBLAS uses a two-tier pattern for functions needing device memory or error checking:
|
|
|
|
### Pattern A: Separate Templates (Preferred)
|
|
|
|
```cpp
|
|
// _launcher: Fast computation, no error checking or allocation
|
|
template <typename API_INT, typename T>
|
|
rocblas_status rocblas_<operation>_launcher(..., void* workspace)
|
|
{
|
|
// Launch device kernels
|
|
// No argument checking
|
|
// No logging
|
|
// No device memory allocation
|
|
// Can be called by other computational functions
|
|
return rocblas_status_success;
|
|
}
|
|
|
|
// _impl: Error checking, logging, memory allocation
|
|
template <typename API_INT, typename T>
|
|
rocblas_status rocblas_<operation>_impl(rocblas_handle handle, ...)
|
|
{
|
|
// Argument error checking
|
|
if(!handle) return rocblas_status_invalid_handle;
|
|
|
|
// Logging
|
|
log_trace(handle, ...);
|
|
|
|
// Allocate device memory
|
|
auto w_mem = handle->device_malloc(dev_bytes);
|
|
if(!w_mem)
|
|
return rocblas_status_memory_error;
|
|
|
|
// Call launcher with pre-allocated memory
|
|
return rocblas_<operation>_launcher<API_INT, T>(..., static_cast<void*>(w_mem));
|
|
}
|
|
|
|
// C API wrapper
|
|
extern "C" rocblas_status rocblas_<precision><operation>(...)
|
|
{
|
|
return rocblas_<operation>_impl<rocblas_int, <type>>(handle, ...);
|
|
}
|
|
```
|
|
|
|
### Pattern B: Template Bool Parameter
|
|
|
|
```cpp
|
|
template <bool full_function, typename API_INT, typename T>
|
|
rocblas_status rocblas_<operation>_launcher(..., void* workspace = nullptr)
|
|
{
|
|
if(full_function)
|
|
{
|
|
// Error checking, logging, memory allocation
|
|
auto w_mem = handle->device_malloc(dev_bytes);
|
|
return rocblas_<operation>_launcher<false, API_INT, T>(..., static_cast<void*>(w_mem));
|
|
}
|
|
// Fast computation path
|
|
}
|
|
```
|
|
|
|
Note: Pattern A is preferred for clarity. Pattern B is useful for large existing functions.
|
|
|
|
## Pointer Mode Patterns
|
|
|
|
rocBLAS supports scalar parameters in both host and device memory.
|
|
|
|
### Switching Pointer Mode
|
|
|
|
```cpp
|
|
// Temporarily switch to host pointer mode (RAII)
|
|
auto saved_pointer_mode = handle->push_pointer_mode(rocblas_pointer_mode_host);
|
|
|
|
// Get scalar value regardless of original mode
|
|
T alpha_h;
|
|
if(saved_pointer_mode == rocblas_pointer_mode_host)
|
|
alpha_h = *alpha; // Already on host
|
|
else
|
|
RETURN_IF_HIP_ERROR(hipMemcpy(&alpha_h, alpha, sizeof(T), hipMemcpyDeviceToHost));
|
|
|
|
// Use host constants in kernel calls
|
|
static constexpr T one = 1;
|
|
static constexpr T zero = 0;
|
|
rocblas_internal_gemm_template(handle, ..., &one, ..., &zero, ...);
|
|
|
|
// Pointer mode automatically restored when saved_pointer_mode goes out of scope
|
|
```
|
|
|
|
### load_scalar() for Kernels
|
|
|
|
```cpp
|
|
// HIP kernel that works with both host and device scalars
|
|
template <typename T, typename U>
|
|
__global__ void axpy_kernel(rocblas_int n, U alpha_device_host, const T* x, T* y)
|
|
{
|
|
auto alpha = load_scalar(alpha_device_host); // Handles both pointer and value
|
|
ptrdiff_t tid = blockIdx.x * blockDim.x + threadIdx.x;
|
|
if(tid < n)
|
|
y[tid] += alpha * x[tid];
|
|
}
|
|
|
|
// Launch with device pointer or host value
|
|
if(handle->pointer_mode == rocblas_pointer_mode_device)
|
|
ROCBLAS_LAUNCH_KERNEL(axpy_kernel, ..., alpha, ...); // Pass pointer
|
|
else if(*alpha)
|
|
ROCBLAS_LAUNCH_KERNEL(axpy_kernel, ..., *alpha, ...); // Pass value
|
|
```
|
|
|
|
## Template Ordering
|
|
|
|
**Rule:** Place non-type template parameters before type parameters.
|
|
|
|
```cpp
|
|
// Correct: Non-type (NB) before type (T)
|
|
template <rocblas_int NB, typename T>
|
|
void kernel_calc(const T* data, T* output, rocblas_int n)
|
|
{
|
|
// Kernel implementation - T is part of function signature
|
|
}
|
|
|
|
// Caller can omit T - it's deduced from the pointer types
|
|
template <rocblas_int NB, typename TPtr>
|
|
void kernel_wrapper(TPtr data, TPtr output, rocblas_int n)
|
|
{
|
|
// Only specify NB, T is automatically deduced from TPtr
|
|
kernel_calc<NB>(data, output, n); // T deduced from pointer arguments
|
|
}
|
|
|
|
// Example usage
|
|
float* d_data;
|
|
float* d_output;
|
|
kernel_wrapper<256>(d_data, d_output, n); // Only NB specified, TPtr=float* deduced
|
|
|
|
// Incorrect: Type before non-type
|
|
template <typename T, rocblas_int NB> // ❌ Wrong order
|
|
void bad_kernel(const T* data, T* output)
|
|
{
|
|
// Caller must specify: bad_kernel<float, 256>(data, output) // Both required!
|
|
}
|
|
```
|
|
|
|
**Rationale:** Type parameters can be automatically deduced from function arguments, but non-type parameters cannot. Placing non-type parameters first allows calling templates with only explicit non-type arguments while types are auto-deduced from function parameters.
|
|
|
|
## RAII Patterns
|
|
|
|
Use RAII for all resource management:
|
|
|
|
```cpp
|
|
// Memory allocation (automatic cleanup)
|
|
auto w_mem = handle->device_malloc(bytes);
|
|
|
|
// Pointer mode (automatic restore)
|
|
auto saved_mode = handle->push_pointer_mode(rocblas_pointer_mode_host);
|
|
|
|
// One-time initialization
|
|
void my_func()
|
|
{
|
|
static int dummy = (init_once_function(), 0);
|
|
// init_once_function() called exactly once, thread-safely
|
|
}
|
|
|
|
// Singleton pattern for static variables
|
|
static auto& get_table()
|
|
{
|
|
static std::unordered_map<std::string, size_t> table;
|
|
return table;
|
|
}
|
|
```
|
|
|
|
Never use explicit `new`/`delete`, `malloc`/`free`, or `hipMalloc`/`hipFree`.
|
|
|
|
## Constants and Macros
|
|
|
|
### Constants: Use static constexpr
|
|
|
|
**Use `static constexpr` for constants. Never use macros for constants.**
|
|
|
|
```cpp
|
|
// Template variable for constants
|
|
template <typename T>
|
|
static constexpr T negative_one = -1;
|
|
|
|
template <typename T>
|
|
static constexpr T zero = 0;
|
|
|
|
// Inside functions, use static constexpr (not just constexpr)
|
|
void my_function()
|
|
{
|
|
static constexpr int BLOCK_SIZE = 256;
|
|
}
|
|
```
|
|
|
|
### Macros: Only for Control Flow
|
|
|
|
**Only use macros for control flow constructs where necessary.** Macros with control flow must use `do-while(0)` wrapper.
|
|
|
|
```cpp
|
|
// Macro with control flow - wraps early return
|
|
#define RETURN_ZERO_IF_QUERIED(h) \
|
|
do \
|
|
{ \
|
|
if((h)->is_query()) \
|
|
return rocblas_status_success; \
|
|
} while(0)
|
|
|
|
// Usage
|
|
void some_function(rocblas_handle handle)
|
|
{
|
|
RETURN_ZERO_IF_QUERIED(handle); // No semicolon needed with do-while
|
|
// ... rest of function
|
|
}
|
|
```
|
|
|
|
**Why do-while(0)?** Ensures the macro behaves like a single statement in all contexts (if/else, etc.).
|
|
|
|
## API Function Signature Pattern
|
|
|
|
```cpp
|
|
// Standard BLAS API pattern
|
|
rocblas_status rocblas_<precision><operation>(
|
|
rocblas_handle handle,
|
|
// operation-specific parameters
|
|
const <type>* input_ptr,
|
|
<type>* output_ptr);
|
|
|
|
// Example: GEMM
|
|
rocblas_status rocblas_sgemm(
|
|
rocblas_handle handle,
|
|
rocblas_operation transA,
|
|
rocblas_operation transB,
|
|
rocblas_int m,
|
|
rocblas_int n,
|
|
rocblas_int k,
|
|
const float* alpha,
|
|
const float* A,
|
|
rocblas_int lda,
|
|
const float* B,
|
|
rocblas_int ldb,
|
|
const float* beta,
|
|
float* C,
|
|
rocblas_int ldc);
|
|
```
|
|
|
|
## Build System
|
|
|
|
- Use CMake for all build configuration
|
|
- Dependencies managed via `install.sh` (Linux) or `rdeps.py` (Windows)
|
|
- Tensile integration handled automatically during build
|
|
- For new dependencies, update `deps/CMakeLists.txt` and dependency scripts
|
|
|
|
## Example Code Structure
|
|
|
|
```cpp
|
|
/* ************************************************************************
|
|
* Copyright (C) 2016-2024 Advanced Micro Devices, Inc. All rights reserved.
|
|
* ... (full copyright header)
|
|
* ************************************************************************ */
|
|
|
|
#pragma once
|
|
|
|
#include "rocblas.h"
|
|
|
|
// API function following BLAS naming conventions
|
|
rocblas_status rocblas_sgemm(rocblas_handle handle,
|
|
rocblas_operation transA,
|
|
rocblas_operation transB,
|
|
rocblas_int m,
|
|
rocblas_int n,
|
|
rocblas_int k,
|
|
const float* alpha,
|
|
const float* A,
|
|
rocblas_int lda,
|
|
const float* B,
|
|
rocblas_int ldb,
|
|
const float* beta,
|
|
float* C,
|
|
rocblas_int ldc);
|
|
|
|
// Internal helper function
|
|
namespace detail
|
|
{
|
|
template<typename T>
|
|
rocblas_status gemm_template(rocblas_handle handle,
|
|
rocblas_operation transA,
|
|
rocblas_operation transB,
|
|
rocblas_int m,
|
|
rocblas_int n,
|
|
rocblas_int k,
|
|
const T* alpha,
|
|
const T* A,
|
|
rocblas_int lda,
|
|
const T* B,
|
|
rocblas_int ldb,
|
|
const T* beta,
|
|
T* C,
|
|
rocblas_int ldc);
|
|
}
|
|
```
|
|
|
|
## HIP/GPU Code Patterns
|
|
|
|
```cpp
|
|
// Device function
|
|
__device__ void kernel_helper(float* data, int idx)
|
|
{
|
|
data[idx] = data[idx] * 2.0f;
|
|
}
|
|
|
|
// Kernel launch
|
|
__global__ void my_kernel(float* data, int n)
|
|
{
|
|
int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
|
if(idx < n)
|
|
{
|
|
kernel_helper(data, idx);
|
|
}
|
|
}
|
|
|
|
// Host code launching kernel
|
|
hipLaunchKernelGGL(my_kernel,
|
|
dim3(blocks),
|
|
dim3(threads),
|
|
0, // shared memory
|
|
stream,
|
|
data,
|
|
n);
|
|
```
|