Skip to content

[AMDGPU] Emplace implicit kernargs per field to support unaligned/partial hidden blocks - #201

Draft
zjgarvey wants to merge 1 commit into
ROCm:mainfrom
zjgarvey:users/zjgarvey/fix/amdgpu-unaligned-implicit-kernarg-load
Draft

zjgarvey wants to merge 1 commit into
ROCm:mainfrom
zjgarvey:users/zjgarvey/fix/amdgpu-unaligned-implicit-kernarg-load

Conversation

@zjgarvey

Copy link
Copy Markdown
Collaborator

Problem

Hand-written GCN assembly kernels (for example MIOpen Winograd convolution) start their AMD hidden/implicit kernarg block at an unaligned base offset (observed at byte 201) and may declare only a partial subset of hidden fields. The AMDGPU kernarg layout rejected any implicit base that was not 8-byte aligned and wrote a fixed full implicit struct at the base offset, so these kernels failed to load — and the failed load surfaced downstream as a crash.

Fix

Record each declared implicit field's own native offset during code-object analysis; validate every declared field's bounds; require hidden block_count to be either omitted or a contiguous uint32[3] with a 4-byte-aligned anchor (the device performs uint32 dword stores); and emplace only the declared fields at their own offsets, zeroing the region first. The host dispatch path is rerouted so both the native and custom-direct paths source the native layout and anchor the indirect block_count write at the declared offset, skipping it when block_count is undeclared. Removes a device implicit-emplace helper left with no caller by the reroute.

Test

Adds unit coverage for per-field offset recording (catching a transposed mapping), unexpected-size rejection, and the out-of-bounds, non-contiguous, and misaligned block_count rejections, plus full and partial/unaligned emplacement.

Notes

Composes on top of the native-argument packing already on main. Opened as a draft: the amdgpu package builds under a pinned-ROCm-header configuration, and CI validates the full driver build and GPU dispatch path — a recommended gate before this leaves draft, as it was rebased across a recent rewrite of the dispatch-packet construction it interacts with.

🤖 Generated with Claude Code

…tial hidden blocks

The AMDGPU dispatch path synthesized the HIP/OpenCL implicit ("hidden")
kernarguments by memsetting a fixed iree_amdgpu_kernel_implicit_args_t
struct at a single base offset (the lowest hidden-argument offset) and
required that base to be 8-byte aligned. That model is wrong for
hand-written assembly kernels -- for example MIOpen Winograd convolution
kernels -- which declare only a partial hidden block that begins with
sub-8-byte hidden_none padding. For those kernels the region base is
unaligned, so layout construction rejected the kernel outright; and even
when a kernel was accepted, splatting a full fixed-size struct wrote
fields the kernel never declared and could run past the kernarg segment.

Replace the single-struct model with per-field emplacement:

- Record each synthesized hidden field's own native offset from the code
  object metadata value_kind (hidden_block_count_x/y/z,
  hidden_group_size_x/y/z, hidden_grid_dims, hidden_dynamic_lds_size) and
  reject any recognized field that declares an unexpected byte size.
  Fields the kernel does not declare stay marked absent and are never
  written.
- Drop the 8-byte base-alignment rejection. Instead validate that every
  declared implicit field lies within the kernarg segment, and require
  hidden_block_count_x/y/z to be either all omitted or declared as a
  contiguous uint32[3] block anchored at a 4-byte-aligned offset -- the
  contract the device indirect-parameter patch relies on when it rewrites
  block_count as a fixed uint32[3] store anchored at the x field. The
  4-byte anchor alignment replaces the removed 8-byte base reject, scoped
  to where the device actually performs aligned dword stores rather than
  to the (possibly unaligned) region base.
- Size the native kernarg segment as exactly the code object's
  kernarg_segment_size instead of reserving a fixed implicit suffix past
  it.
- At dispatch, zero the implicit region (so undeclared fields such as the
  global offset and printf/hostcall buffers are zero/NULL, matching the
  HIP defaults) and write only the declared fields, each at its own
  offset. Both the native and custom-direct argument paths now source the
  implicit offsets from the same native metadata layout, so the device
  block_count patch is anchored consistently regardless of path.

The device-side iree_hal_amdgpu_device_dispatch_emplace_implicit_args,
whose sole caller was the host dispatch reroute above, is orphaned by that
reroute and removed along with its declaration and unit test.

Adds regression tests covering: per-field offset recording at the ABI
offsets and rejection of a hidden field with an unexpected size; full-block
emplacement leaving every undeclared byte zero; partial-block emplacement
over an unaligned region with no field writes; and per-field rejection of
an out-of-bounds field, a non-contiguous block_count, and a misaligned
block_count anchor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant