Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions dnn-providers/hip-kernel-provider/rocke/platform/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ circular. torch is therefore **optional** and the only hard dep is `numpy`

1. the `torch.fx` fusion frontend (`torch_backend.py`, `helpers/fuse.py`
graph capture);
2. torch-tensor launch integration (`runtime/torch_module.py`);
2. torch-tensor launch integration (`runtime/torch_interop.py`);
3. on-GPU numeric verification against torch eager.

Everything else runs torch-free: IR build, lower, `comgr` compile, numpy launch,
Expand All @@ -125,8 +125,8 @@ with neither torch nor a GPU installed.
### ROCm library discovery (libamd_comgr / libamdhip64)

The runtime resolves the ROCm shared libs WITHOUT importing torch
(`runtime/hip_module._candidate_lib_paths` / `_rocm_root_libdirs`), in priority
order:
(`runtime/runtime_coexistence._candidate_lib_paths` / `_rocm_root_libdirs`), in
priority order:

1. explicit full-path override env var (`ROCKE_COMGR_LIB`, `ROCKE_HIP_LIB`);
2. torch-bundled `<torch>/lib/lib*.so` — opportunistic fast-path **only if torch
Expand Down
6 changes: 3 additions & 3 deletions dnn-providers/hip-kernel-provider/rocke/platform/BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ Optional sanitizer build for diagnostics (not for shipping): `-DROCKE_SANITIZE=O

In-process compile + launch needs the ROCm shared libs at runtime. The Python
runtime resolves them WITHOUT importing torch
(`Python/rocke/runtime/hip_module._candidate_lib_paths` / `_rocm_root_libdirs`),
in priority order:
(`Python/rocke/runtime/runtime_coexistence._candidate_lib_paths` /
`_rocm_root_libdirs`), in priority order:

1. explicit full-path override env var: `ROCKE_COMGR_LIB`, `ROCKE_HIP_LIB`;
2. torch-bundled `<torch>/lib/lib*.so` — only if torch is already imported (the
Expand All @@ -87,7 +87,7 @@ in priority order:
4. bare `lib<name>.so` on the dynamic linker's search path (last resort).

torch is therefore **optional** — required only for the `torch.fx` fusion
frontend, torch-tensor launch (`runtime/torch_module.py`), and on-GPU torch-eager
frontend, torch-tensor launch (`runtime/torch_interop.py`), and on-GPU torch-eager
numeric checks. Building the engine, lowering, `comgr` compile, numpy launch, and
the byte-identity gate need no torch. If a torch-less process reports `cannot load
libamd_comgr.so`, set `ROCM_PATH` (or `ROCKE_COMGR_LIB`) to your ROCm install.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Conventional anchors:
- COMGR: `python/rocke/runtime/comgr.py`.
- HIP runtime: `python/rocke/runtime/hip_module.py`.
- Launcher / workspace / timing: `python/rocke/runtime/launcher.py`.
- Torch arg packing: `python/rocke/runtime/torch_module.py`.
- Kernel arg packing: `python/rocke/runtime/packing.py`.
- High-level compile: `python/rocke/helpers/compile.py`.
- Manifest schema: `python/rocke/helpers/manifest.py`.
- Optimization runbook: `gpu-op-optimization-runbook` Cursor skill.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ core/lower_llvm.py
runtime/comgr.py
ctypes over libamd_comgr; LLVM IR -> bitcode -> relocatable -> HSACO.

runtime/hip_module.py, runtime/launcher.py, runtime/torch_module.py
runtime/hip_module.py, runtime/launcher.py, runtime/torch_interop.py
Load HSACO, pack args, resolve streams, launch, time, manage workspace
and per-stream pending-args lifetime.
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1988,8 +1988,8 @@ moved measured throughput by under 1 % on direct-conv kernels.
| `no_fence` context manager | `runtime/launcher.py` | Skip per-call sync inside an event-timed loop (graph-style) |
| `time_launches(fn, warmup, iters, stream)` | `runtime/launcher.py` | The canonical HIP-event timer |
| `StreamConfig` | `runtime/launcher.py` | Mirror of CK Tile `stream_config` |
| `resolve_stream(stream=0)` | `runtime/torch_module.py` | Substitute torch's current stream to keep allocator coherent |
| `pack_args` vs `pack_args_kernelparams` | `runtime/torch_module.py` | AMDGPU kernarg buffer vs the safer `kernelParams` path |
| `resolve_stream(stream=0)` | `runtime/torch_interop.py` | Substitute torch's current stream to keep allocator coherent |
| `pack_args` vs `pack_args_kernelparams` | `runtime/packing.py` | AMDGPU kernarg buffer vs the safer `kernelParams` path |
| HIP graph capture | torch | Amortizes launch overhead — pair with `no_fence` and many iters |
| `rocm-smi --setperflevel high && --setsclk 7` | shell | Lock clocks to avoid thermal / DVFS noise during measurement |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,15 @@ wait_stream_and_release
release_retained_for_stream
```

## `from rocke.runtime.torch_module import ...`
## `from rocke.runtime.packing import ...`

```text
pack_args, pack_args_kernelparams # torch-agnostic kernarg packing
```

## `from rocke.runtime.torch_interop import ...`

```text
pack_args, pack_args_kernelparams
resolve_stream
empty_workspace
launch_torch_kernel # back-compat shim
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File Index

A by-file map of the `rocke` package. Symbols listed are the primary public exports — see each file for the rest.
A by-file map of the `rocke` package. Symbols listed are the primary contents (public exports, or the key internal helpers for whole-module-private files) — see each file for the rest.

## Top Level

Expand Down Expand Up @@ -32,9 +32,12 @@ A by-file map of the `rocke` package. Symbols listed are the primary public expo

| Path | Primary contents |
|-------------------------------|--------------------------------------------------------------------------------------------------------|
| `runtime/runtime_coexistence.py` | `_candidate_lib_paths`, `_rocm_root_libdirs`, `_torch_bundled_lib`, ... . Which ROCm runtime we bind to, and whose (torch-bundled vs system). Shared by `comgr` + `hip_module`. |
| `runtime/_ctypes_bind.py` | `_LazyFn`. Lazy ctypes function binder shared by `comgr` + `hip_module`. |
| `runtime/comgr.py` | `build_hsaco_from_llvm_ir(ir_text, isa=..., options=...) -> (bytes, ComgrTimings)`. Ctypes over `libamd_comgr`. |
| `runtime/hip_module.py` | `Runtime`, `Module`, `Event`, `HipError`. Ctypes over `libamdhip64`. Per-stream pending-args queue. |
| `runtime/torch_module.py` | `pack_args`, `pack_args_kernelparams`, `resolve_stream`, `empty_workspace`, `launch_torch_kernel`. |
| `runtime/packing.py` | `pack_args`, `pack_args_kernelparams`, `_as_ptr`. Torch-agnostic AMDGPU kernarg packing. |
| `runtime/torch_interop.py` | `resolve_stream`, `empty_workspace`, `launch_torch_kernel`, `TorchLaunchSummary`. Torch-tensor launch glue. |
| `runtime/launcher.py` | `KernelLauncher`, `PipelineLauncher`, `LaunchConfig`, `LaunchSummary`, `WorkspaceSpec`, `WorkspacePool`, `DeviceMem`, `time_launches`, `no_fence`, `synchronize_and_release`, `wait_stream_and_release`, `release_retained_for_stream`. |

## `helpers/` — Authoring Layer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ extra[] = {
}
```

`args_bytes` is the packed arg buffer (built by `runtime/torch_module.py::pack_args` from the signature dict list). For an `(A, B, C, M, N, K)` GEMM:
`args_bytes` is the packed arg buffer (built by `runtime/packing.py::pack_args` from the signature dict list). For an `(A, B, C, M, N, K)` GEMM:

```text
struct.pack("<QQQiii", A_dev, B_dev, C_dev, M, N, K)
Expand All @@ -193,7 +193,7 @@ For everyday DSL use, `KernelLauncher` and `compile_kernel` are the right abstra
- You need fine-grained event control across multiple streams.
- You're debugging a launch failure and want to inspect packed args + grid + block + stream manually.

Numpy-only paths (the manifest runner) go through `Runtime.alloc` / `memcpy_h2d` / `launch` / `memcpy_d2h` directly with `struct.pack`-built args. Torch paths go through `runtime/torch_module.pack_args` + `KernelLauncher`. Both end up at the same `hipModuleLaunchKernel`.
Numpy-only paths (the manifest runner) go through `Runtime.alloc` / `memcpy_h2d` / `launch` / `memcpy_d2h` directly with `struct.pack`-built args. Torch paths go through `runtime/packing.pack_args` + `KernelLauncher`. Both end up at the same `hipModuleLaunchKernel`.

## Common Failure Modes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ Both fixes are automatic when launches go through `KernelLauncher`.

## Torch Runtime Layer

File: `runtime/torch_module.py`.
Files: `runtime/packing.py` (torch-agnostic arg packing) and
`runtime/torch_interop.py` (stream resolution + torch-tensor launch).

```text
pack_args(signature, values) -> bytes # packs kernel args in declaration order
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ What does work for multi-GPU usage of the DSL:

This pattern is verified end-to-end by `dsl_docs/development/verify_dsl_docs.py` on the GPU available to this box; the per-device launcher constructed under `torch.cuda.device(d)` correctly launches on that device.

- **Torch-stream-aware launches**: `runtime/torch_module.py::resolve_stream(stream, device=None)` honors `torch.cuda.current_stream(device).cuda_stream`, so the standard pattern of allocating tensors and launching all under `torch.cuda.device(d):` interoperates with torch's caching allocator on the right device.
- **Torch-stream-aware launches**: `runtime/torch_interop.py::resolve_stream(stream, device=None)` honors `torch.cuda.current_stream(device).cuda_stream`, so the standard pattern of allocating tensors and launching all under `torch.cuda.device(d):` interoperates with torch's caching allocator on the right device.

- **Composing with `torch.distributed` (RCCL on ROCm)**: the user runs `torch.distributed.init_process_group(backend="nccl")` (PyTorch's "nccl" backend on ROCm is implemented by RCCL — `librccl.so`). DSL kernels then execute on each rank's local device; collectives (`torch.distributed.all_reduce`, `all_gather`, etc.) execute outside the DSL on a separate communication stream. The DSL never invokes RCCL itself.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ The runner uses `default_shape` to allocate `X`, `Gamma`/`Beta` (norms), and `Y`
"f32" # 4-byte scalar
```

`size_bytes` is for the host arg-packer (`runtime/torch_module.py::pack_args`) and the runner. Pointers are always 8 bytes; scalars match the canonical width.
`size_bytes` is for the host arg-packer (`runtime/packing.py::pack_args`) and the runner. Pointers are always 8 bytes; scalars match the canonical width.

## Standard Signatures

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def _torch_hip_version() -> Optional[Tuple[int, int]]:
bundle their own ``libamd_comgr.so`` whose LLVM version follows the
wheel's ROCm release, not the system ``/opt/rocm`` one. When rocke
is paired with a torch-bundled comgr (see
:func:`runtime.hip_module._torch_bundled_lib`), the flavor must
:func:`runtime.runtime_coexistence._torch_bundled_lib`), the flavor must
match torch's ROCm vintage or comgr will reject the IR or
silently auto-upgrade declares the lowerer didn't intend.
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
proxy, since the streaming trio has no GEMM FLOP) plus ``is_valid``.

No torch dependency: device buffers come from ``Runtime.alloc`` (hipMalloc),
args are packed with ``runtime.torch_module.pack_args`` (which accepts integer
args are packed with ``runtime.packing.pack_args`` (which accepts integer
device pointers directly).
"""

Expand All @@ -29,7 +29,7 @@
from rocke.core.lower_llvm import lower_kernel_to_llvm
from rocke.runtime.comgr import build_hsaco_from_llvm_ir
from rocke.runtime.hip_module import Runtime
from rocke.runtime.torch_module import pack_args
from rocke.runtime.packing import pack_args

_DTYPE_BYTES = {"f16": 2, "fp16": 2, "bf16": 2}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@
in-process pipeline that turns AMDGPU LLVM IR text into a running
kernel.

Layered modules (bottom-up):
Layered modules (bottom-up). The first five run torch-free -- the
hip-only core; ``torch_interop`` and ``launcher`` are the torch-aware
edge:

- ``runtime_coexistence`` : which ROCm runtime we bind to, and whose
(torch-bundled vs a system ROCm install). Shared
library resolution used by both ``comgr`` and
``hip_module``.

- ``_ctypes_bind``: the lazy ctypes function binder (`_LazyFn`) shared
by ``comgr`` and ``hip_module``.

- ``comgr`` : ctypes wrapper over `libamd_comgr.so`. Implements
`LLVM IR (text) -> BC -> relocatable ELF -> HSA
Expand All @@ -23,10 +33,14 @@
`HIP_LAUNCH_PARAM_BUFFER_POINTER` arg-buffer
lifetime race.

- ``torch_module``: torch-tensor arg packing + `resolve_stream`
(which collapses ``stream=0`` to torch's current
stream so the caching allocator sees our
launches).
- ``packing`` : torch-agnostic kernel-arg packing (`pack_args`,
`pack_args_kernelparams`) for the AMDGPU kernarg
ABI. Used by both the numpy and torch paths.

- ``torch_interop``: torch-tensor launch glue -- `resolve_stream`
(collapses ``stream=0`` to torch's current stream
so the caching allocator sees our launches),
`empty_workspace`, `launch_torch_kernel`.

- ``launcher`` : long-lived launch abstractions (CK Tile / FlyDSL /
Triton inspired). The recommended entry point
Expand Down Expand Up @@ -84,11 +98,11 @@
time_launches,
wait_stream_and_release,
)
from .torch_module import (
from .packing import pack_args
from .torch_interop import (
TorchLaunchSummary,
empty_workspace,
launch_torch_kernel,
pack_args,
resolve_stream,
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT

"""Lazy ctypes function binding shared by the HIP / comgr wrappers.

A tiny ergonomics helper: defer ``getattr`` on the underlying ``CDLL``
until the first call so the runtime wrappers (`hip_module`, `comgr`) can
be imported before their shared library is resolved -- which lets rocke
and torch be imported in any order and still bind the same loaded HIP /
comgr runtime instance (see :mod:`rocke.runtime.runtime_coexistence`).
"""

from __future__ import annotations

import ctypes
from typing import Any, Callable, List, Optional


class _LazyFn:
"""Lazy ctypes function wrapper.

Defers ``getattr`` on the underlying CDLL until the first call so
that rocke and torch can be imported in any order without ending
up with two HIP runtimes (see
``runtime_coexistence._torch_bundled_lib``). Resolved on
first use; subsequent calls dispatch directly through the cached
function pointer.

``lib_resolver`` returns the shared ``ctypes.CDLL`` for this lib
family (HIP runtime / comgr / ...). It is invoked exactly once per
function on first call.
"""

__slots__ = ("_name", "_argtypes", "_restype", "_lib_resolver", "_fn")

def __init__(
self,
name: str,
argtypes: List[Any],
restype: Any,
lib_resolver: "Callable[[], ctypes.CDLL]",
) -> None:
self._name = name
self._argtypes = argtypes
self._restype = restype
self._lib_resolver = lib_resolver
self._fn: Optional[Any] = None

def _resolve(self) -> Any:
fn = getattr(self._lib_resolver(), self._name)
fn.argtypes = self._argtypes
fn.restype = self._restype
self._fn = fn
return fn

def __call__(self, *args: Any) -> Any:
fn = self._fn or self._resolve()
return fn(*args)
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
-> AMD_COMGR_ACTION_LINK_RELOCATABLE_TO_EXECUTABLE -> HSA code object

The resulting HSACO bytes are returned to Python and can be handed
straight to `hipModuleLoadData` (see `_hip_module.py`). No subprocesses,
straight to `hipModuleLoadData` (see `hip_module.py`). No subprocesses,
no `<hip/hip_runtime.h>` parsing, no clang spawn.

The library is loaded from the default ROCm library locations or the dynamic
Expand All @@ -28,7 +28,8 @@
from dataclasses import dataclass
from typing import List, Optional, Tuple

from .hip_module import _IS_WINDOWS, _LazyFn, _add_dll_dir, _candidate_lib_paths
from ._ctypes_bind import _LazyFn
from .runtime_coexistence import _IS_WINDOWS, _add_dll_dir, _candidate_lib_paths


# Status codes.
Expand Down Expand Up @@ -58,9 +59,9 @@ class ComgrError(RuntimeError):
def _load_lib() -> ctypes.CDLL:
# Pair the loader with ``hip_module._load_lib`` so the two halves of
# the process always share a single HIP/comgr runtime instance. See
# ``_torch_bundled_lib`` in ``hip_module`` for why a torch-shipped
# libamd_comgr is preferred over /opt/rocm when torch is in the
# process.
# ``_torch_bundled_lib`` in ``runtime_coexistence`` for why a
# torch-shipped libamd_comgr is preferred over /opt/rocm when torch is
# in the process.
err = None
for p in _candidate_lib_paths("amd_comgr", "ROCKE_COMGR_LIB", ["3"]):
try:
Expand All @@ -73,7 +74,7 @@ def _load_lib() -> ctypes.CDLL:


# Lazy: resolved on first call so that rocke and torch can be imported
# in any order. See ``hip_module._torch_bundled_lib`` for context.
# in any order. See ``runtime_coexistence._torch_bundled_lib`` for context.
_lib: Optional[ctypes.CDLL] = None


Expand All @@ -86,7 +87,7 @@ def _resolve_lib() -> ctypes.CDLL:

def resolved_lib_path() -> Optional[str]:
"""Path of the ``libamd_comgr`` this module will load (torch-bundled
preferred over ``/opt/rocm``; see :func:`hip_module._torch_bundled_lib`).
preferred over ``/opt/rocm``; see :func:`runtime_coexistence._torch_bundled_lib`).

Returns the already-loaded lib's path once :func:`_resolve_lib` has run,
else the first existing candidate. Pure lookup -- does NOT ``dlopen``, so it
Expand Down Expand Up @@ -196,7 +197,7 @@ def prefer_bundled_lib() -> Optional[Tuple[int, int]]:

The resolver never imports torch as a side effect (a library must not), so it
only prefers torch's bundled (newest) ``libamd_comgr`` when torch is ALREADY
in the process -- see :func:`hip_module._torch_bundled_lib`. A CLI / runner
in the process -- see :func:`runtime_coexistence._torch_bundled_lib`. A CLI / runner
that lowers IR should call this ONCE at startup, BEFORE the first lowering, so
the bundled comgr (e.g. ROCm 7.2 / llvm22) is in the process and the LLVM
flavor cannot be locked to a stale ``/opt/rocm`` by import order.
Expand Down
Loading
Loading