Skip to content

Add AMDGPU execution provider - #2165

Open
Aditya Lohia (aditya-dl) wants to merge 18 commits into
microsoft:mainfrom
aditya-dl:amd/dev/adilohia/migraphx_support
Open

Add AMDGPU execution provider#2165
Aditya Lohia (aditya-dl) wants to merge 18 commits into
microsoft:mainfrom
aditya-dl:amd/dev/adilohia/migraphx_support

Conversation

@aditya-dl

@aditya-dl Aditya Lohia (aditya-dl) commented May 14, 2026

Copy link
Copy Markdown

This PR adds the AMDGPU execution provider to ONNX Runtime GenAI. The AMDGPU EP picks a concrete backend at runtime and OGA drives it as a single DeviceType::AMDGPU, so callers never manage the backend directly. It supersedes the earlier MIGraphX-only framing (#2093 and the previous version of this PR).

Users select it as "amdgpu". We also accept "AMDGPUExecutionProvider", the catalog name the AMD Windows ML EP MSIX advertises, so harnesses that match against WinML-discovered names work without special-casing. Both normalize to "AMDGPU".

What's in the three commits

1. AMDGPU EP with GPU-resident KV cache. Adds the EP wiring (DeviceType::AMDGPU, the dispatch-table entry, provider-name normalization, and the src/amdgpu/ sources) plus a GPU-resident DeviceInterface that keeps the KV cache on the device, removing the per-token CPU/GPU roundtrip. Cross-device copies go through backend-agnostic opaque DeviceBuffer handles. It also emits the static-shape prefill config (ep.migraphx.static_pad_* and hip_graph_enable) so the backend compiles the prefill once and reuses a captured graph.

2. Host-accessible decode inputs. The small decode inputs (input_ids, position_ids, attention_mask) are allocated from a host-accessible allocator that the CPU can write and the GPU can read, so the CPU updates them in place with no per-step copy. The KV cache and scoring stay on the default device interface. If no host-accessible allocator is available, it falls back to the default input path.

3. Logits on CPU. Logits are GPU-written and CPU-read, the opposite of the decode inputs. On AMDGPU those inputs live in a host-accessible heap that is not coherent for CPU reads, so reading logits from it would return stale data. This commit routes logits to the CPU interface through a new p_logits_ member, leaving only the decode inputs pinned.

Configuration

"provider_options": [{ "amdgpu": {} }]

or

config.append_provider("amdgpu")

"AMDGPUExecutionProvider" also works (the catalog form).

Design notes

Graph capture is always on for AMDGPU, so OGA sizes the attention mask and KV cache itself. The EP is told to pad the prefill token axis through the static-pad session-config entries, while OGA keeps ownership of mask and KV sizing. The host-accessible and logits routing are both backend-aware and fall back safely to the default path when the allocator isn't there.

Known limitations

Beam search isn't supported (it needs past_present_share_buffer=true, which needs num_beams=1). The host-accessible allocator currently assumes device_id=0, so multi-GPU is a follow-up.

Copilot AI review requested due to automatic review settings May 14, 2026 19:37
@aditya-dl
Aditya Lohia (aditya-dl) requested a review from a team as a code owner May 14, 2026 19:37
@aditya-dl

Copy link
Copy Markdown
Author

kunal-vaishnavi Baiju Meswani (@baijumeswani) could you help review this PR?

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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds MIGraphX (AMD GPU) execution provider integration to ONNX Runtime GenAI, including provider name normalization and runtime behaviors needed to avoid recompilation during prompt processing.

Changes:

  • Register MIGraphX in the session-options dispatch table and add a MIGraphX EP implementation (V2 plugin path with V1 fallback).
  • Normalize provider names so "migraphx" and "MIGraphXExecutionProvider" map to "MIGraphX", and enable graph capture for MIGraphX.
  • Add “static input shape” prompt-time padding and update position/logits shapes to support padded prompt lengths.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/models/session_options.cpp Adds MIGraphX EP registration in the provider dispatch map.
src/models/position_inputs.h Updates CreateAndInitializePositionIDs signature to accept actual seq_length.
src/models/position_inputs.cpp Implements position id initialization w/ padded shapes and safe indexing using seq_length.
src/models/model.h Adds State::prompt_gen_ flag to control prompt-time padding behavior.
src/models/model.cpp Pads input_ids to max_length during prompt generation for static-shape EPs.
src/models/logits.cpp Forces logits output shape to max_length during prompt generation.
src/migraphx/session_options.h Declares MIGraphX EP append entrypoint.
src/migraphx/session_options.cpp Implements MIGraphX EP append with V2 plugin-first and V1 fallback.
src/generators.h Adds use_static_input_shapes to generator params.
src/generators.cpp Toggles prompt_gen_ around prompt vs token-generation runs.
src/config.h Declares NeedsStaticInputShapes.
src/config.cpp Adds MIGraphX name normalization, enables graph capture for MIGraphX, and implements NeedsStaticInputShapes.
cmake/global_variables.cmake Adds MIGraphX sources to the CMake glob.

Comment thread src/models/position_inputs.h
Comment thread src/models/model.cpp Outdated
Comment thread src/models/model.cpp Outdated
Comment thread src/models/model.cpp Outdated
Comment thread src/models/model.cpp Outdated
Comment thread src/models/model.cpp Outdated
Comment thread src/models/model.cpp Outdated
Comment thread src/models/position_inputs.cpp Outdated
Comment thread src/models/position_inputs.cpp Outdated
Comment thread src/models/position_inputs.cpp Outdated
Comment thread src/models/logits.cpp Outdated
Comment thread src/models/logits.cpp Outdated
Comment thread src/models/model.cpp Outdated
Comment thread src/migraphx/session_options.cpp Outdated
@aditya-dl

Copy link
Copy Markdown
Author

Hi kunal-vaishnavi. Apologies for the long delay getting back to you. In the meantime, we've been reworking the architecture of the AMD GPU integration, and your comments on this PR shaped that direction.
The short version: we're dropping MIGraphX as a user-facing execution provider. Going forward, the umbrella "AMDGPU" EP is the single user-facing EP for all AMD GPUs; it selects the backend internally, so users target one EP regardless of the underlying stack.

This resolves the concerns you raised here:

  • "Why can't the MIGraphX EP handle static shapes through its own mechanism?" / the logits.cpp hackiness: Agreed, and that's now the design. Static input-shape padding has moved out of OGA's shared paths and into the EP (the EP pads/slices internally and reuses the compiled program). OGA no longer special-cases it.
  • "Don't introduce EP-specific if/else logic for how inputs are updated": Removed. The umbrella EP owns the backend-specific behavior; OGA drives it through generic provider options instead of branching on the EP in shared code. The NeedsStaticInputShapes special-case goes away.
  • session_options.cpp "why is this done?": Superseded by the umbrella architecture; it won't carry forward in its current form.

We'll push the new-architecture updates to this same PR rather than opening a new one retitling/rescoping it to the umbrella EP as we go. Thanks for the patience and for the feedback that pushed us here.

@aditya-dl
Aditya Lohia (aditya-dl) force-pushed the amd/dev/adilohia/migraphx_support branch from d563801 to 66e496a Compare July 21, 2026 22:00
@aditya-dl Aditya Lohia (aditya-dl) changed the title Add MIGraphX execution provider support Add AMDGPU execution provider Jul 21, 2026
@microsoft-github-policy-service

Copy link
Copy Markdown
Contributor

Aditya Lohia (@aditya-dl) please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"
Contributor License Agreement

Contribution License Agreement

This Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
and conveys certain license rights to Microsoft Corporation and its affiliates (“Microsoft”) for Your
contributions to Microsoft open source projects. This Agreement is effective as of the latest signature
date below.

  1. Definitions.
    “Code” means the computer software code, whether in human-readable or machine-executable form,
    that is delivered by You to Microsoft under this Agreement.
    “Project” means any of the projects owned or managed by Microsoft and offered under a license
    approved by the Open Source Initiative (www.opensource.org).
    “Submit” is the act of uploading, submitting, transmitting, or distributing code or other content to any
    Project, including but not limited to communication on electronic mailing lists, source code control
    systems, and issue tracking systems that are managed by, or on behalf of, the Project for the purpose of
    discussing and improving that Project, but excluding communication that is conspicuously marked or
    otherwise designated in writing by You as “Not a Submission.”
    “Submission” means the Code and any other copyrightable material Submitted by You, including any
    associated comments and documentation.
  2. Your Submission. You must agree to the terms of this Agreement before making a Submission to any
    Project. This Agreement covers any and all Submissions that You, now or in the future (except as
    described in Section 4 below), Submit to any Project.
  3. Originality of Work. You represent that each of Your Submissions is entirely Your original work.
    Should You wish to Submit materials that are not Your original work, You may Submit them separately
    to the Project if You (a) retain all copyright and license information that was in the materials as You
    received them, (b) in the description accompanying Your Submission, include the phrase “Submission
    containing materials of a third party:” followed by the names of the third party and any licenses or other
    restrictions of which You are aware, and (c) follow any other instructions in the Project’s written
    guidelines concerning Submissions.
  4. Your Employer. References to “employer” in this Agreement include Your employer or anyone else
    for whom You are acting in making Your Submission, e.g. as a contractor, vendor, or agent. If Your
    Submission is made in the course of Your work for an employer or Your employer has intellectual
    property rights in Your Submission by contract or applicable law, You must secure permission from Your
    employer to make the Submission before signing this Agreement. In that case, the term “You” in this
    Agreement will refer to You and the employer collectively. If You change employers in the future and
    desire to Submit additional Submissions for the new employer, then You agree to sign a new Agreement
    and secure permission from the new employer before Submitting those Submissions.
  5. Licenses.
  • Copyright License. You grant Microsoft, and those who receive the Submission directly or
    indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license in the
    Submission to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute
    the Submission and such derivative works, and to sublicense any or all of the foregoing rights to third
    parties.
  • Patent License. You grant Microsoft, and those who receive the Submission directly or
    indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license under
    Your patent claims that are necessarily infringed by the Submission or the combination of the
    Submission with the Project to which it was Submitted to make, have made, use, offer to sell, sell and
    import or otherwise dispose of the Submission alone or with the Project.
  • Other Rights Reserved. Each party reserves all rights not expressly granted in this Agreement.
    No additional licenses or rights whatsoever (including, without limitation, any implied licenses) are
    granted by implication, exhaustion, estoppel or otherwise.
  1. Representations and Warranties. You represent that You are legally entitled to grant the above
    licenses. You represent that each of Your Submissions is entirely Your original work (except as You may
    have disclosed under Section 3). You represent that You have secured permission from Your employer to
    make the Submission in cases where Your Submission is made in the course of Your work for Your
    employer or Your employer has intellectual property rights in Your Submission by contract or applicable
    law. If You are signing this Agreement on behalf of Your employer, You represent and warrant that You
    have the necessary authority to bind the listed employer to the obligations contained in this Agreement.
    You are not expected to provide support for Your Submission, unless You choose to do so. UNLESS
    REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, AND EXCEPT FOR THE WARRANTIES
    EXPRESSLY STATED IN SECTIONS 3, 4, AND 6, THE SUBMISSION PROVIDED UNDER THIS AGREEMENT IS
    PROVIDED WITHOUT WARRANTY OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF
    NONINFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
  2. Notice to Microsoft. You agree to notify Microsoft in writing of any facts or circumstances of which
    You later become aware that would make Your representations in this Agreement inaccurate in any
    respect.
  3. Information about Submissions. You agree that contributions to Projects and information about
    contributions may be maintained indefinitely and disclosed publicly, including Your name and other
    information that You submit with Your Submission.
  4. Governing Law/Jurisdiction. This Agreement is governed by the laws of the State of Washington, and
    the parties consent to exclusive jurisdiction and venue in the federal courts sitting in King County,
    Washington, unless no federal subject matter jurisdiction exists, in which case the parties consent to
    exclusive jurisdiction and venue in the Superior Court of King County, Washington. The parties waive all
    defenses of lack of personal jurisdiction and forum non-conveniens.
  5. Entire Agreement/Assignment. This Agreement is the entire agreement between the parties, and
    supersedes any and all prior agreements, understandings or communications, written or oral, between
    the parties relating to the subject matter hereof. This Agreement may be assigned by Microsoft.

@aditya-dl

Copy link
Copy Markdown
Author

Hi kunal-vaishnavi. The new-architecture updates are now pushed to this PR (three commits on top of latest main), and I've updated the description to match. The EP-specific if/else logic and the logits.cpp hackiness from the previous version are both gone. Whenever you have a chance, could you take another look? Happy to walk through any of it. Thanks again for the earlier feedback.

Comment thread src/models/kv_cache.cpp Outdated
Comment thread src/models/model.cpp Outdated
Comment thread src/models/session_options.cpp
Comment thread src/models/logits.h Outdated
Aditya Lohia (aditya-dl) added a commit to aditya-dl/onnxruntime-genai that referenced this pull request Jul 22, 2026
Aditya Lohia (aditya-dl) added a commit to aditya-dl/onnxruntime-genai that referenced this pull request Jul 22, 2026
Aditya Lohia (aditya-dl) added a commit to aditya-dl/onnxruntime-genai that referenced this pull request Jul 22, 2026
Aditya Lohia (aditya-dl) added a commit to aditya-dl/onnxruntime-genai that referenced this pull request Jul 22, 2026
…ice (review microsoft#2165)

Resolve the AMDGPU device id from the filtered EP device instead of hardcoding 0. The EP keys its allocator on this id, so a hardcoded 0 pinned compute to device 0 regardless of the selected device. Correlate the host-accessible pool to the same id so pinned decode inputs live on the device that runs the model. Single-GPU resolves to id 0, unchanged.
Comment thread src/models/model.cpp Outdated
Comment thread src/models/model.cpp Outdated
Owen Zhang (Zhaeong) pushed a commit to AMD-GPU/onnxruntime-genai that referenced this pull request Aug 4, 2026
Owen Zhang (Zhaeong) pushed a commit to AMD-GPU/onnxruntime-genai that referenced this pull request Aug 4, 2026
…ice (review microsoft#2165)

Resolve the AMDGPU device id from the filtered EP device instead of hardcoding 0. The EP keys its allocator on this id, so a hardcoded 0 pinned compute to device 0 regardless of the selected device. Correlate the host-accessible pool to the same id so pinned decode inputs live on the device that runs the model. Single-GPU resolves to id 0, unchanged.
Owen Zhang (Zhaeong) added a commit to AMD-GPU/onnxruntime-genai that referenced this pull request Aug 4, 2026
Add AMDGPU execution provider (port of microsoft#2165 to rel-0.15.1)
Add the AMDGPU execution provider to ONNX Runtime GenAI. The AMDGPU EP
resolves a profile to a concrete backend (MIGraphX or DML) at runtime;
OGA drives it as a single DeviceType::AMDGPU.

Provider naming: exposed as "amdgpu"; OGA also accepts
"AMDGPUExecutionProvider" (the catalog form used by the AMD-shipped
Windows ML EP MSIX) so test harnesses that match config strings against
WinML-discovered names work without bypass hacks. Both normalize to
"AMDGPU".

GPU-resident KV cache: a GPU-resident DeviceInterface keeps the KV cache
on the device (no per-token CPU-to-GPU roundtrip), with backend-agnostic
opaque DeviceBuffer copies that dispatch to the active backend.

Static-shape prefill: emit ep.migraphx.static_pad_* and hip_graph_enable
session-config entries so the EP pads the prefill token axis and reuses a
captured graph. DML ignores the migraphx-namespaced keys.

Known limitations:
- Beam search not supported (needs past_present_share_buffer=true, which
  requires num_beams=1)
Route the small decode inputs (input_ids/position_ids/attention_mask) through a host-accessible (CPU-writable, GPU-readable) allocator so the CPU updates them in place with no per-step copy. Resolved via GetSharedAllocator; KV cache and scoring stay on the default device interface, and the path falls back to default inputs if no host-accessible allocator is available. Single-GPU only for now (device_id 0).
Logits is GPU-written and CPU-read (the sampler), the opposite of the pinned decode inputs. On AMDGPU the inputs use a host-accessible allocator whose heap is not CPU-read-coherent, so reading logits from it returns stale data. Route logits to the CPU interface instead via a new p_logits_ member; only the decode inputs stay on the host-accessible allocator.
…ice (review microsoft#2165)

Resolve the AMDGPU device id from the filtered EP device instead of hardcoding 0. The EP keys its allocator on this id, so a hardcoded 0 pinned compute to device 0 regardless of the selected device. Correlate the host-accessible pool to the same id so pinned decode inputs live on the device that runs the model. Single-GPU resolves to id 0, unchanged.
The umbrella EP routes backends by model architecture but OGA never sent it, so every model routed as non-LLM. Emit config.model.type as the ep.amdgpuexecutionprovider.model_arch provider option alongside the existing static-padding hints.
Trailing-comment spacing and argument-continuation alignment flagged by the lint-cpp CI check (clang-format 20.1.0). Formatting only, no behavior change.
Emit the ep.directml.enable_host_accessible provider option so the DirectML backend uses host-accessible decode inputs. Sits alongside the existing static-padding and model_arch config entries.
…ft#2165)

Applies the reviewer's suggested wording: the exclusion is about WebGPU, so
naming the backends that do zero-init adds nothing.
…(review microsoft#2165)

The host-accessible interface was selected in a separate if that reassigned
p_device_inputs_ after the chain had already set it. It is now an else if in
that chain, so the field is assigned once.

The interface getter already returns null when no host-accessible allocator
exists, so the extra guard on it went away with the restructure.
…rosoft#2165)

EnsureDeviceOrtInit carried the AMD-specific device-id lookup and the
host-accessible allocator acquisition inline, including the provider name as a
literal. Both now sit behind DeviceInterface virtuals with no-op defaults,
following the existing ShapeInitSessionProviderOptions pattern, so the shared
path no longer branches on the device:

  GetDeviceId          - id the allocators bind to, 0 unless the device resolves
                         one from EP metadata
  InitDeviceAllocators - lets a device set up any additional allocators it
                         offers once the device allocator exists

The AMDGPU implementations move to src/amdgpu/interface.cpp, which also drops
the SetAMDGPUDeviceId free function that existed only to reach back into the
interface from the shared path.
…ft#2165)

Comments in model.cpp, model.h, smartptrs.h, onnxruntime_api.h and
generators.cpp named AMD backends while describing device-agnostic code. The
behaviour they document is not vendor-specific, so the names are dropped and
the wording shortened.
The reviewer asked for this to be an overload of Create rather than a
separately named CreateV2. It turned out to have no callers left: the
allocator setup now reads the memory-info the EP advertises instead of
building one, so the wrapper is dropped rather than renamed.
…icrosoft#2165)

The name sits next to the interface that uses it, so the comment restates
what the code already shows.
@aditya-dl
Aditya Lohia (aditya-dl) force-pushed the amd/dev/adilohia/migraphx_support branch from 1e76280 to 05297cc Compare August 7, 2026 00:13
A plugin EP is only discoverable once its library is registered on the OrtEnv,
and the C model_benchmark has no option to pass a path. Resolve it the way the
RyzenAI interface does and register it, skipping entirely when the EP is
already registered or the library is not found.

Also accept AMDGPU in the benchmark's execution-provider list.
Wang, Zhenze (zz002) added a commit to ROCm/hip-ep that referenced this pull request Aug 7, 2026
The AMDGPU umbrella integration for OGA lives in upstream
microsoft/onnxruntime-genai#2165, which is cut against a base newer than the 0.14
line, so it cannot be applied on the current v0.14.0 pin. Move both workflows to
the v0.15.0 tag and patch it with that PR.

The PR is a draft that gets rebased in place, so its number no longer identifies
its content: a new step resolves each patch PR's head SHA and folds it into the
OGA cache key, replacing the hand-bumped mm<N> token that had to be remembered
whenever a patch PR moved.

0.15 enables 1DS telemetry by default, which pulls cpp_client_telemetry (plus
curl and mbedtls on Linux) into a cold build and would ship Microsoft telemetry
inside AMD artifacts, so OGA now builds with --no_telemetry.

0.15's model_benchmark prints "Peak working set size: <n> bytes (<x> GB)" where
0.14 printed "Peak working set size (bytes): <n>", so the OGA benchmark summary
and the two perf-report tools would report "-" for peak memory. Match the current
format, which is what the perf_test parser in the same workflow already expects.

Verified locally on gfx1151 against ORT 1.27: v0.15.0 + pull/2165.patch applies
with git am, builds (Ninja, Release), and runs Llama-3.1-8B AWQ int4 through the
AMD GPU umbrella at 287 tok/s prefill and 30.0 tok/s decode.
@kunal-vaishnavi

Copy link
Copy Markdown
Contributor

Approving this PR. We should update the main README to show that AMD GPU is supported instead of on the roadmap. Looks like there is still a merge conflict to resolve as well.

Comment thread src/models/model.h

DeviceInterface* p_device_{}; // The device we're running on (matches device_type_) used for things that work the same on all devices
DeviceInterface* p_device_inputs_{}; // For some model inputs, the device might be the CPU device (all but KV cache currently for WebGPU and DML)
DeviceInterface* p_device_logits_{}; // Logits are read back on the CPU every step

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This comment is misleading. Logits can be on the device for cuda and nvtensorrtrtx eps.

Comment thread src/models/model.cpp

// Inputs-only interface backed by a host-accessible allocation, so the CPU updates the small
// decode inputs in place with no per-step roundtrip. Null if the device offers no such allocator.
DeviceInterface* p_host_accessible_inputs = GetAMDGPUPinnedInputsInterface();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The variable reads p_host_accessible_inputs but queries a EP specialized function GetAMDGPUPinnedInputsInterface. Could we move this behind an abstraction such as p_device->GetHostAccessibleDevice()?

Now, that we have so many device interface variables, we could consider moving all behind p_device->GetInputDevice(), p_device->GetLogitsDevice() so the code can have ep specific overrides instead of us having to add EP specialized functions at the top level.

@baijumeswani

Baiju Meswani (baijumeswani) commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

A few thoughts:

  1. Could you explain how the different EPs are to be thought of. Right now, we have 5 AMD EPs: VitisAI, RyzenAI, MigraphX, rocm, AMDGPU. How are they related? And if they are not related, what mental model should we have on which scenario each ep specializes in.
  2. It would be good if we can keep EP abstractions away from the main code flow and push them behind device interfaces as much as possible.
  3. Do we expect new models to show up in foundry-local for amdgpu ep? Is there a timeline? Is there anything needed from us to enable those models?

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.

5 participants