Skip to content

feat(rocr/aie): resolve a full ELF's external symbols against defined variables - #14004

Merged
erwei-xilinx merged 3 commits into
developfrom
users/erwei-xilinx/aie-external-variables
Oct 25, 2026
Merged

erwei-xilinx merged 3 commits into
developfrom
users/erwei-xilinx/aie-external-variables

Conversation

@erwei-xilinx

@erwei-xilinx erwei-xilinx commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

A full ELF can relocate the address of a buffer it does not carry. The common case is the control scratchpad (scratch-pad-ctrl), which holds the runtime parameters that a design reads in its preamble. MLIR-AIR's LLM decode uses it to pass the context length to one ELF for every token.

The AIE hsaco loader refused any such relocation ("unsupported relocation symbol"). So a design with scratchpad parameters could not be loaded. Applications worked around this: they allocated the scratchpad, wrote its device address into the ELF, pointed the relocation at symbol 0 so that the loader skipped it, and repacked the hsaco for each allocation (for example amd/Triton-XDNA#158).

Technical Details

A relocation whose symbol is neither a PDI nor an argument index is now an external variable. It is resolved the same way as a GPU code object's undefined symbols: the application allocates the buffer and defines it before the load.

hsa_amd_memory_pool_allocate(dev_pool, size, 0, &scratchpad);
hsa_executable_agent_global_variable_define(exe, aie_agent, "scratch-pad-ctrl", scratchpad);
hsa_executable_load_agent_code_object(exe, aie_agent, reader, nullptr, nullptr);
  • Parse (amd_aie_elf.cpp): the parser records each external symbol's patch sites and st_size in Kernel::external_symbols. Only Address64 is accepted, as for PDIs. A site must hold 8 bytes inside the control code. All the symbols that name one variable must have the same st_size.
  • Load (executable.cpp): the loader looks up each name in agent_symbols_, the table that hsa_executable_agent_global_variable_define fills. If the variable is not defined, the load fails with HSA_STATUS_ERROR_VARIABLE_UNDEFINED. If its st_size bytes are not inside one allocation that the AIE agent can reach, the load fails with HSA_STATUS_ERROR_INVALID_ALLOCATION. Otherwise the loader writes the variable's agent address plus the addend into the control code, next to the PDI addresses, and records its BO handle.
  • Dispatch (amd_xdna_driver.cpp): BuildFullElfCommand flushes each external variable from the CPU cache and adds its BO to the command. So the host can change a parameter between dispatches with an ordinary store, and the application does not flush. The packet still lists only the kernel's arguments.
  • Docs: docs/aie-hsaco-format.md sections 7.4, 7.5, 10 and 11, and a new section 9.3.

No change to the hsaco format, the packer, or the packet ABI.

Two points for reviewers:

  1. The command processor copies the scratchpad itself. On aie2p it does so only from the device-SVM pool (dev heap). A scratchpad in the data pool loads, but the dispatch fails. This PR documents the requirement but does not enforce it, because enforcement needs an allocation-to-region lookup that Runtime does not have. Should the loader refuse the load?
  2. The new test design follows MLIR-AIR's sequence: load the empty device, then the design, then sync_parameters(), run, and load the empty device again. Without the final empty load, aie2p firmware gives each dispatch the scratchpad value of the dispatch before it. XRT gives the same result for one ELF run through a new xrt::run for each dispatch, so this comes from the firmware, not from this change. It is noted in the design file.

Issue Tracking

Follow-up to #12846 (AIE hsaco support). Independent of #13908.

Test Plan

  • elf_parse_test: 3 new tests on a real scratchpad artifact (vector_scalar_add_param.py, a new IRON design that is built only as a full ELF):
    • The scratchpad is parsed as an external symbol, with the correct sites and size.
    • Any scheme other than Address64 is refused.
    • An out-of-range site is refused.
  • test_dispatch: new ExternalVariableTest cases on hardware:
    • The load refuses an undefined scratchpad, a scratchpad in host memory, and a scratchpad that runs past the end of its allocation or of a VMM mapping.
    • One loaded kernel is dispatched 5 times, and the host changes the parameter with a plain store before each dispatch. Each output is checked against that dispatch's value.
  • The full AIE suite, as a regression check.
  • TryLoad/LoadAndFreeze take an optional list of variables to define before the load. add_aie_kernel's full-ELF half is split out as add_aie_full_elf so that a full-ELF-only design can use it.

Test Result

On an aie2p (Krackan) NPU with hsa-runtime64 built from this branch:

  • elf_parse_test: 16/16 (13 before this PR).
  • aie_code_test: 12/12.
  • test_dispatch: 56/56, run several times.
  • test_memory: 40/40.
  • test_error_callback: 2/2.
  • ExternalVariableTest* and the existing FullElf* tests: 20 consecutive runs each, with no failures.

During development, one run of ExternalVariableTest* hung, right after the artifacts were rebuilt. It did not happen again in the 30 runs after it.

Submission Checklist

🤖 Generated with Claude Code

erwei-xilinx and others added 2 commits October 21, 2026 21:40
… variables

A full ELF can relocate the address of a buffer it does not carry, most
notably the control scratchpad (`scratch-pad-ctrl`) that a design reads its
runtime parameters from. The hsaco loader refused any such relocation, so
a design with scratchpad parameters could not be loaded at all, and
applications worked around it by writing the address into the ELF
themselves before packing.

Treat a relocation whose symbol is neither a PDI nor an argument index as
an external variable, resolved the way a GPU code object's undefined
symbols are: the application allocates the buffer and defines it with
hsa_executable_agent_global_variable_define before loading. The loader
writes the variable's device address into the control code, alongside the
PDI addresses, and fails the load with HSA_STATUS_ERROR_VARIABLE_UNDEFINED
if it was not defined, or HSA_STATUS_ERROR_INVALID_ALLOCATION if it does
not fit in AIE-reachable memory. Each dispatch flushes the variable from
the CPU cache and lists its BO, so the host can change a parameter
between dispatches with an ordinary store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 22, 2026 05:36
@github-actions github-actions Bot added documentation Improvements or additions to documentation project: rocr-runtime labels Oct 22, 2026
@therock-pr-bot

Copy link
Copy Markdown

✅ All Policy Checks Passed

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —

🎉 All policy checks passed!

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The allocation check accepts external-variable ranges that extend beyond virtual-memory mappings, risking invalid memory access during dispatch.

1 open issue
What changed in this PR

Adds external-variable support to the AIE full-ELF loading path, allowing applications to define buffers such as the control scratchpad without rewriting and repackaging code objects.

Changes:

  • Parses external-symbol relocations and resolves them against application-defined variables.
  • Records external buffers for per-dispatch cache flushing and residency.
  • Adds scratchpad tests, build integration, and usage documentation without changing the hsaco format or packet ABI.
File Description
projects/​rocr-runtime/​runtime/​hsa-runtime/​loader/​executable.cpp Resolves external variables, validates allocations, and patches addresses.
projects/​rocr-runtime/​runtime/​hsa-runtime/​core/​runtime/​amd_aie_elf.cpp Parses and validates external-symbol relocation sites.
projects/​rocr-runtime/​runtime/​hsa-runtime/​core/​inc/​amd_aie_section.h Records external buffers in kernel descriptors.
projects/​rocr-runtime/​runtime/​hsa-runtime/​core/​inc/​amd_aie_elf.h Defines parsed external-symbol metadata.
projects/​rocr-runtime/​runtime/​hsa-runtime/​core/​driver/​xdna/​amd_xdna_driver.cpp Flushes external buffers and includes their handles in commands.
projects/​rocr-runtime/​rocrtst/​suites/​aie/​vector_scalar_add_param.py Adds a scratchpad-driven vector-add design.
projects/​rocr-runtime/​rocrtst/​suites/​aie/​test_dispatch.cc Tests variable validation and parameter changes between dispatches.
projects/​rocr-runtime/​rocrtst/​suites/​aie/​elf_parse_test.cc Tests external-symbol parsing and invalid relocation rejection.
projects/​rocr-runtime/​rocrtst/​suites/​aie/​CMakeLists.txt Extracts full-ELF build support and integrates scratchpad artifacts.
projects/​rocr-runtime/​docs/​aie-hsaco-format.md Documents external-variable loading, memory requirements, and dispatch behavior.

Review effort: Balanced

@cfreeamd cfreeamd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI-assisted pre-review (for @cfreeamd)

Core loader (executable.cpp), ELF parser (amd_aie_elf.cpp), and driver dispatch path (amd_xdna_driver.cpp) look correct. Variable resolution follows the same agent_symbols_ lookup path as GPU undefined symbols; cleanup on failure destroys the executable and reader; size consistency across multiple relocations naming the same symbol is enforced. New tests cover the three refusal paths (undefined, non-AIE memory, out-of-range) and the parameter-change-between-dispatches scenario. rocrtst_yaml_missing flag is a false positive — elf_parse_test and test_dispatch are standalone GTest binaries, not entries in the rocrtst main runner, so test_categories.yaml / platform_config.yaml updates are not required.

For a VMM mapping, pointer info reports the queried pointer as the base
but the whole mapping as the size, so a variable near the end of a mapping
passed the extent check. Measure the extent from the base that
FindDriverMemoryHandle returns, and keep the pointer-info offset for the
device address. Also count external variables in the full-ELF chain's BO
capacity hint, and compare symbol names as strings in elf_parse_test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@erwei-xilinx
erwei-xilinx merged commit 7709cab into develop Oct 25, 2026
410 of 414 checks passed
@erwei-xilinx
erwei-xilinx deleted the users/erwei-xilinx/aie-external-variables branch October 25, 2026 17:55
@systems-assistant

Copy link
Copy Markdown
Contributor

TheRock Submodule Bump Activity

Newest first

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation organization: ROCm project: rocr-runtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants