Skip to content

Refactor rnn ops to op builders#4606

Open
pfultz2 wants to merge 15 commits into
developfrom
rnn-op-builder
Open

Refactor rnn ops to op builders#4606
pfultz2 wants to merge 15 commits into
developfrom
rnn-op-builder

Conversation

@pfultz2

@pfultz2 pfultz2 commented Feb 12, 2026

Copy link
Copy Markdown
Collaborator

Motivation

Technical Details

Changelog Category

Add a CHANGELOG.md entry for any option other than Not Applicable

    • Added: New functionality.
    • Changed: Changes to existing functionality.
    • Removed: Functionality or support that has been removed. (Compared to a previous release)
    • Optimized: Component performance that has been optimized or improved.
    • Resolved Issues: Known issues from a previous version that have been resolved.
    • Not Applicable: This PR is not to be included in the changelog.

auto und = mm->add_instruction(migraphx::make_op("undefined"));
mm->add_instruction(
migraphx::make_op(
"gru",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This was a mistake, the test is for lstm, but we were using gru.

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

This PR migrates RNN-family ops (RNN/LSTM/GRU) from dedicated operators + rewrite_rnn pass to OpBuilder-based graph construction, updating ONNX parsing and verification tests accordingly and removing the legacy ops/pass.

Changes:

  • Add OpBuilder implementations for rnn, lstm, and gru (plus shared utilities) that directly expand into lower-level ops.
  • Update ONNX parsers and many verify tests to use migraphx::op::builder::add(...) results instead of make_op("rnn"/"lstm"/"gru") + rnn_last_* ops.
  • Remove rewrite_rnn from target pass pipelines and remove legacy RNN-family operators/headers/registrations; add new OpBuilder-focused tests.

Reviewed changes

Copilot reviewed 96 out of 97 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/verify/test_var_sl_gru_forward.cpp Switch GRU construction to OpBuilder and adjust return ordering to match builder outputs.
test/verify/test_var_sl_gru_bidirct.cpp Switch bidirectional GRU construction to OpBuilder.
test/verify/test_rnn_sql_2.cpp Switch RNN construction to OpBuilder; remove unused serialize include.
test/verify/test_rnn_sql_1_layout.cpp Switch RNN construction to OpBuilder and adapt layout handling to builder outputs.
test/verify/test_rnn_sql_1.cpp Switch RNN construction to OpBuilder; remove unused serialize include.
test/verify/test_rnn_reverse_layout.cpp Switch reverse RNN construction to OpBuilder and reuse builder output for layout ops.
test/verify/test_rnn_reverse2.cpp Switch reverse RNN construction to OpBuilder (no explicit return).
test/verify/test_rnn_reverse.cpp Switch reverse RNN construction to OpBuilder (no explicit return).
test/verify/test_rnn_forward_layout.cpp Switch forward RNN construction to OpBuilder and adapt layout handling.
test/verify/test_rnn_forward10.cpp Switch forward RNN construction to OpBuilder.
test/verify/test_rnn_forward.cpp Switch forward RNN construction to OpBuilder.
test/verify/test_rnn_bidirectional_layout.cpp Switch bidirectional RNN construction to OpBuilder and adapt last-output usage.
test/verify/test_rnn_bidirectional10.cpp Switch bidirectional RNN construction to OpBuilder (no explicit return).
test/verify/test_rnn_bidirectional.cpp Switch bidirectional RNN construction to OpBuilder (no explicit return).
test/verify/test_rnn_bi_3args_layout.cpp Switch 3-arg bidirectional RNN construction to OpBuilder and adapt last-output usage.
test/verify/test_rnn_bi_3args.cpp Switch 3-arg bidirectional RNN construction to OpBuilder.
test/verify/test_rnn_5args.cpp Switch 5-arg RNN construction to OpBuilder.
test/verify/test_rnn_4args_layout.cpp Switch 4-arg RNN construction to OpBuilder and adapt layout handling.
test/verify/test_rnn_4args.cpp Switch 4-arg RNN construction to OpBuilder.
test/verify/test_rnn_3args.cpp Switch 3-arg RNN construction to OpBuilder.
test/verify/test_lstm_two_outputs.cpp Switch LSTM construction to OpBuilder and return builder outputs.
test/verify/test_lstm_three_outputs_layout.cpp Switch LSTM construction to OpBuilder and adapt layout handling for 3 outputs.
test/verify/test_lstm_three_outputs.cpp Switch LSTM construction to OpBuilder and return 3 builder outputs.
test/verify/test_lstm_reverse_last.cpp Switch reverse LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_reverse_3args_layout.cpp Switch reverse LSTM construction to OpBuilder and adapt layout handling.
test/verify/test_lstm_reverse_3args_cell_output_layout.cpp Switch reverse LSTM construction to OpBuilder and adapt cell-output usage.
test/verify/test_lstm_reverse_3args_cell_output.cpp Switch reverse LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_reverse_3args.cpp Switch reverse LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_forward_seq1.cpp Switch forward LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_forward_last_layout.cpp Switch forward LSTM construction to OpBuilder and adapt layout handling for last output.
test/verify/test_lstm_forward_last.cpp Switch forward LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_forward_hs_layout.cpp Switch forward LSTM construction to OpBuilder and adapt layout handling for hidden states.
test/verify/test_lstm_forward_hs.cpp Switch forward LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_forward_default_actv1.cpp Switch forward LSTM construction to OpBuilder with explicit default activation list.
test/verify/test_lstm_forward_default_actv.cpp Switch forward LSTM construction to OpBuilder with empty activation list.
test/verify/test_lstm_forward_3args_und.cpp Switch forward LSTM construction to OpBuilder with undefined placeholders.
test/verify/test_lstm_forward_3args.cpp Switch forward LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_bidirct_seq1.cpp Switch bidirectional LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_bidirct_last_layout.cpp Switch bidirectional LSTM construction to OpBuilder and adapt layout handling for last output.
test/verify/test_lstm_bidirct_last.cpp Switch bidirectional LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_bidirct_hs.cpp Switch bidirectional LSTM construction to OpBuilder (no explicit return).
test/verify/test_lstm_bidirct_default_actv2.cpp Switch bidirectional LSTM construction to OpBuilder with 2 default activations.
test/verify/test_lstm_bidirct_default_actv1.cpp Switch bidirectional LSTM construction to OpBuilder with 1 default activation.
test/verify/test_lstm_bidirct_default_actv.cpp Switch bidirectional LSTM construction to OpBuilder with empty activation list.
test/verify/test_lstm_bidirct_3args_und.cpp Switch GRU (file name indicates LSTM) to OpBuilder with undefined placeholders.
test/verify/test_lstm_bidirct_3args_layout.cpp Switch bidirectional LSTM construction to OpBuilder and adapt layout handling.
test/verify/test_lstm_bidirct_3args.cpp Switch bidirectional LSTM construction to OpBuilder (no explicit return).
test/verify/test_gru_two_outputs.cpp Switch GRU construction to OpBuilder and return builder outputs.
test/verify/test_gru_reverse_last_layout.cpp Switch reverse GRU construction to OpBuilder and adapt layout handling for last output.
test/verify/test_gru_reverse_last.cpp Switch reverse GRU construction to OpBuilder and return last output directly.
test/verify/test_gru_reverse_3args_layout.cpp Switch reverse GRU construction to OpBuilder and adapt layout handling.
test/verify/test_gru_reverse_3args.cpp Switch reverse GRU construction to OpBuilder and return hidden states.
test/verify/test_gru_forward_seq1.cpp Switch forward GRU construction to OpBuilder and return hidden states.
test/verify/test_gru_forward_layout.cpp Switch forward GRU construction to OpBuilder and adapt layout handling.
test/verify/test_gru_forward_default_actv1.cpp Switch forward GRU construction to OpBuilder and return hidden states.
test/verify/test_gru_forward_default_actv.cpp Switch forward GRU construction to OpBuilder and return hidden states.
test/verify/test_gru_forward_3args_und.cpp Switch forward GRU construction to OpBuilder with undefined placeholders.
test/verify/test_gru_forward_3args_layout.cpp Switch forward GRU construction to OpBuilder and adapt layout handling.
test/verify/test_gru_forward_3args.cpp Switch forward GRU construction to OpBuilder and return hidden states.
test/verify/test_gru_forward.cpp Switch forward GRU construction to OpBuilder and preserve return ordering.
test/verify/test_gru_bidirct_seq1.cpp Switch bidirectional GRU construction to OpBuilder and return hidden states.
test/verify/test_gru_bidirct_layout.cpp Switch bidirectional GRU construction to OpBuilder and adapt layout handling.
test/verify/test_gru_bidirct_default_actv1.cpp Switch bidirectional GRU construction to OpBuilder and return hidden states.
test/verify/test_gru_bidirct_default_actv.cpp Switch bidirectional GRU construction to OpBuilder and return hidden states.
test/verify/test_gru_bidirct_3args_und.cpp Switch bidirectional GRU construction to OpBuilder with undefined placeholders.
test/verify/test_gru_bidirct_3args_layout.cpp Switch bidirectional GRU construction to OpBuilder and adapt layout handling.
test/verify/test_gru_bidirct_3args.cpp Switch bidirectional GRU construction to OpBuilder and return hidden states.
test/verify/test_gru_bidirct.cpp Switch bidirectional GRU construction to OpBuilder and return both outputs.
test/operators.cpp Remove legacy rnn operator test case (operator no longer present).
test/op/builder/rnn_builder_test.cpp Add OpBuilder tests for RNN output arity, shapes, and basic eval.
test/op/builder/lstm_builder_test.cpp Add OpBuilder tests for LSTM output arity, shapes, and basic eval.
test/op/builder/gru_builder_test.cpp Add OpBuilder tests for GRU output arity, shapes, and basic eval.
src/targets/ref/target.cpp Remove rewrite_rnn pass from ref target pipeline.
src/targets/gpu/target.cpp Remove rewrite_rnn pass from GPU target pipeline.
src/targets/fpga/target.cpp Remove rewrite_rnn pass from FPGA target pipeline.
src/targets/cpu/target.cpp Remove rewrite_rnn pass from CPU target pipeline.
src/quantization.cpp Remove rewrite_rnn from quantization pre-pass pipeline.
src/op/builder/rnn.cpp Add OpBuilder expansion for vanilla RNN into lower-level ops + var-seq-len handling.
src/op/builder/lstm.cpp Add OpBuilder expansion for LSTM into lower-level ops + var-seq-len handling.
src/op/builder/gru.cpp Add OpBuilder expansion for GRU into lower-level ops + var-seq-len handling.
src/op/builder/include/migraphx/op/builder/rnn_utils.hpp Add shared utilities for seq-len handling, padding, and var-seq-len shifting.
src/onnx/parse_rnn.cpp Parse ONNX RNN via OpBuilder and consume returned outputs.
src/onnx/parse_lstm.cpp Parse ONNX LSTM via OpBuilder and consume returned outputs.
src/onnx/parse_gru.cpp Parse ONNX GRU via OpBuilder and consume returned outputs.
src/include/migraphx/rewrite_rnn.hpp Remove legacy rewrite pass API header.
src/include/migraphx/operators.hpp Stop including legacy rnn/gru/lstm + last-output operator headers.
src/include/migraphx/op/rnn_last_hs_output.hpp Remove legacy rnn_last_hs_output operator.
src/include/migraphx/op/rnn_last_cell_output.hpp Remove legacy rnn_last_cell_output operator.
src/include/migraphx/op/rnn.hpp Remove legacy rnn operator.
src/include/migraphx/op/lstm.hpp Remove legacy lstm operator.
src/include/migraphx/op/gru.hpp Remove legacy gru operator.
src/driver/passes.cpp Remove rewrite_rnn from driver pass lookup.
src/CMakeLists.txt Stop building/registering legacy rnn/gru/lstm operators and rewrite_rnn.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/op/builder/lstm.cpp
Comment on lines +293 to +311
struct lstm_builder : op_builder<lstm_builder>
{
static std::vector<std::string> names() { return {"lstm"}; }

std::size_t hidden_size = 1;
std::vector<operation> actv_funcs{};
op::rnn_direction direction = op::rnn_direction::forward;
float clip = 0.0f;
int input_forget = 0;

template <class Self, class F>
static auto reflect(Self& self, F f)
{
return pack(f(self.hidden_size, "hidden_size"),
f(self.actv_funcs, "actv_func"),
f(self.direction, "direction"),
f(self.clip, "clip"),
f(self.input_forget, "input_forget"));
}

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

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

hidden_size and clip are reflected on lstm_builder, but hidden_size is not validated/used (the implementation derives hs from R), and clip is also unused. This can mask invalid inputs (attribute/weight mismatch) and makes the public builder API misleading. Consider validating these attributes against the provided tensors and either applying clip/hidden_size semantics or throwing when they are inconsistent/non-default.

Copilot uses AI. Check for mistakes.
Comment thread src/op/builder/lstm.cpp Outdated
Comment on lines +297 to +311
std::size_t hidden_size = 1;
std::vector<operation> actv_funcs{};
op::rnn_direction direction = op::rnn_direction::forward;
float clip = 0.0f;
int input_forget = 0;

template <class Self, class F>
static auto reflect(Self& self, F f)
{
return pack(f(self.hidden_size, "hidden_size"),
f(self.actv_funcs, "actv_func"),
f(self.direction, "direction"),
f(self.clip, "clip"),
f(self.input_forget, "input_forget"));
}

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

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

input_forget is parsed and reflected on lstm_builder, but it is never used in the LSTM computation. As a result, ONNX models that set input_forget=1 will behave the same as 0. Consider either implementing the input-forget coupling in the cell logic, or explicitly rejecting/ignoring non-zero values with a clear error to avoid silent misbehavior.

Copilot uses AI. Check for mistakes.
Comment thread src/op/builder/rnn.cpp
Comment on lines +152 to +168
struct rnn_builder : op_builder<rnn_builder>
{
static std::vector<std::string> names() { return {"rnn"}; }

std::size_t hidden_size = 1;
std::vector<operation> actv_funcs{};
op::rnn_direction direction = op::rnn_direction::forward;
float clip = 0.0f;

template <class Self, class F>
static auto reflect(Self& self, F f)
{
return pack(f(self.hidden_size, "hidden_size"),
f(self.actv_funcs, "actv_func"),
f(self.direction, "direction"),
f(self.clip, "clip"));
}

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

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

hidden_size (and clip) are accepted as attributes on rnn_builder, but the implementation never uses them or validates them against the provided weight shapes. This can hide malformed inputs (e.g., ONNX attribute/weight mismatch) that previously would have been caught. Consider validating hidden_size and direction against W/R shapes and either using hidden_size for shape decisions or throwing on mismatch.

Copilot uses AI. Check for mistakes.
Comment thread src/op/builder/gru.cpp
Comment on lines +235 to +253
struct gru_builder : op_builder<gru_builder>
{
static std::vector<std::string> names() { return {"gru"}; }

std::size_t hidden_size = 1;
std::vector<operation> actv_funcs{};
op::rnn_direction direction = op::rnn_direction::forward;
float clip = 0.0f;
int linear_before_reset = 0;

template <class Self, class F>
static auto reflect(Self& self, F f)
{
return pack(f(self.hidden_size, "hidden_size"),
f(self.actv_funcs, "actv_func"),
f(self.direction, "direction"),
f(self.clip, "clip"),
f(self.linear_before_reset, "linear_before_reset"));
}

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

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

hidden_size (and clip) are reflected on gru_builder, but the builder derives hs from the input shapes and does not validate/consume the hidden_size attribute. This makes invalid graphs harder to diagnose (attribute/weight mismatch) and can lead to silent shape inconsistencies. Consider validating hidden_size against R/W and erroring on mismatch (or removing the unused attribute).

Copilot uses AI. Check for mistakes.
@codecov

codecov Bot commented Feb 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.15849% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/onnx/parse_lstm.cpp 83.33% 2 Missing ⚠️
src/op/builder/lstm.cpp 99.24% 2 Missing ⚠️
src/onnx/parse_gru.cpp 87.50% 1 Missing ⚠️
src/onnx/parse_rnn.cpp 87.50% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #4606      +/-   ##
===========================================
- Coverage    92.89%   92.88%   -0.01%     
===========================================
  Files          603      600       -3     
  Lines        32448    32431      -17     
===========================================
- Hits         30140    30122      -18     
- Misses        2308     2309       +1     
Files with missing lines Coverage Δ
src/include/migraphx/serialize.hpp 92.45% <100.00%> (+0.15%) ⬆️
src/op/builder/gru.cpp 100.00% <100.00%> (ø)
.../builder/include/migraphx/op/builder/rnn_utils.hpp 100.00% <100.00%> (ø)
src/op/builder/rnn.cpp 100.00% <100.00%> (ø)
src/op/builder/torch_kit.cpp 100.00% <100.00%> (ø)
src/quantization.cpp 85.33% <100.00%> (ø)
src/targets/ref/target.cpp 100.00% <ø> (ø)
src/onnx/parse_gru.cpp 94.03% <87.50%> (-1.68%) ⬇️
src/onnx/parse_rnn.cpp 93.75% <87.50%> (-1.63%) ⬇️
src/onnx/parse_lstm.cpp 92.31% <83.33%> (-2.63%) ⬇️
... and 1 more

... and 9 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pfultz2
pfultz2 marked this pull request as ready for review July 10, 2026 22:10
@pfultz2
pfultz2 requested a review from causten as a code owner July 10, 2026 22:10
@gh-app-migraphx-bot-pr-write

gh-app-migraphx-bot-pr-write Bot commented Jul 11, 2026

Copy link
Copy Markdown
Test Batch New Rate (d98ae7) Old Rate (55ca93) Diff Status
torchvision-resnet50 64 3,138.27 3,169.20 -0.98%
torchvision-resnet50_fp16 64 6,649.73 6,721.88 -1.07%
torchvision-densenet121 32 2,693.38 2,712.99 -0.72%
torchvision-densenet121_fp16 32 4,549.86 4,578.90 -0.63%
torchvision-inceptionv3 32 1,795.71 1,805.02 -0.52%
torchvision-inceptionv3_fp16 32 2,844.62 2,855.31 -0.37%
cadene-inceptionv4 16 822.14 824.13 -0.24%
cadene-resnext64x4 16 783.48 785.46 -0.25%
slim-mobilenet 64 8,382.94 8,444.46 -0.73%
slim-nasnetalarge 64 229.06 229.50 -0.19%
slim-resnet50v2 64 3,161.10 3,185.35 -0.76%
bert-mrpc-onnx 8 1,169.75 1,173.56 -0.32%
bert-mrpc-tf 1 486.69 484.20 0.51%
pytorch-examples-wlang-gru 1 484.40 472.27 2.57%
pytorch-examples-wlang-lstm 1 384.51 386.76 -0.58%
torchvision-resnet50_1 1 771.31 754.58 2.22%
cadene-dpn92_1 1 451.57 444.58 1.57%
cadene-resnext101_1 1 364.64 366.32 -0.46%
onnx-taau-downsample 1 401.84 402.99 -0.29%
dlrm-criteoterabyte 1 32.42 32.58 -0.50%
dlrm-criteoterabyte_fp16 1 51.84 52.65 -1.53%
agentmodel 1 9,438.24 7,784.58 21.24% 🔆
unet_fp16 2 57.07 57.48 -0.72%
resnet50v1_fp16 1 1,016.59 932.89 8.97% 🔆
resnet50v1_int8 1 935.03 934.74 0.03%
bert_base_cased_fp16 64 1,097.88 1,105.09 -0.65%
bert_large_uncased_fp16 32 344.67 347.51 -0.82%
bert_large_fp16 1 203.23 205.61 -1.16%
distilgpt2_fp16 16 2,092.27 2,104.89 -0.60%
yolov5s 1 558.33 559.63 -0.23%
tinyllama 1 45.79 46.13 -0.74%
vicuna-fastchat 1 44.02 44.16 -0.31%
whisper-tiny-encoder 1 412.33 415.45 -0.75%
whisper-tiny-decoder 1 409.58 412.20 -0.64%
llama2_7b 1 20.84 21.00 -0.76%
qwen1.5-7b 1 23.58 23.73 -0.63%
phi3-3.8b 1 26.69 26.83 -0.50%
llama3-8b 1 21.80 21.87 -0.36%
whisper-large-encoder 1 10.17 10.23 -0.57%
whisper-large-decoder 1 105.04 104.89 0.14%
mistral-7b 1 23.75 23.89 -0.61%
FLUX.1-schnell 1 766.18 760.54 0.74%

Check flagged results 🔆

@gh-app-migraphx-bot-pr-write

gh-app-migraphx-bot-pr-write Bot commented Jul 11, 2026

Copy link
Copy Markdown
Test Status Result
bert-mrpc-onnx PASSED: MIGraphX meets tolerance
bert-mrpc-tf PASSED: MIGraphX meets tolerance
pytorch-examples-wlang-gru PASSED: MIGraphX meets tolerance
pytorch-examples-wlang-lstm PASSED: MIGraphX meets tolerance
dlrm-criteoterabyte PASSED: MIGraphX meets tolerance
agentmodel PASSED: MIGraphX meets tolerance
unet PASSED: MIGraphX meets tolerance
resnet50v1 PASSED: MIGraphX meets tolerance
bert_base_cased_fp16 PASSED: MIGraphX meets tolerance
bert_large_uncased_fp16 🔴 FAILED: MIGraphX is not within tolerance - check verbose output
bert_large PASSED: MIGraphX meets tolerance
yolov5s PASSED: MIGraphX meets tolerance
tinyllama PASSED: MIGraphX meets tolerance
vicuna-fastchat PASSED: MIGraphX meets tolerance
whisper-tiny-encoder PASSED: MIGraphX meets tolerance
whisper-tiny-decoder PASSED: MIGraphX meets tolerance
distilgpt2_fp16 PASSED: MIGraphX meets tolerance
llama2_7b PASSED: MIGraphX meets tolerance
qwen1.5-7b PASSED: MIGraphX meets tolerance
phi3-3.8b PASSED: MIGraphX meets tolerance
llama3-8b PASSED: MIGraphX meets tolerance
whisper-large-encoder PASSED: MIGraphX meets tolerance
whisper-large-decoder PASSED: MIGraphX meets tolerance
mistral-7b PASSED: MIGraphX meets tolerance
FLUX.1-schnell PASSED: MIGraphX meets tolerance

@pfultz2
pfultz2 requested a review from shivadbhavsar July 13, 2026 20:19
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.

3 participants