Skip to content

Fix MLIR conv-pointwise-layout fusion splitting#5064

Open
justinrosner wants to merge 6 commits into
developfrom
1067-cached-tuning-db-error
Open

Fix MLIR conv-pointwise-layout fusion splitting#5064
justinrosner wants to merge 6 commits into
developfrom
1067-cached-tuning-db-error

Conversation

@justinrosner

@justinrosner justinrosner commented Jul 14, 2026

Copy link
Copy Markdown

Motivation

Some gpu::mlir_op convolution + pointwise fusions can fail to compile when the fused module is followed by layout-only operations such as reshape_lazy or transpose. In these cases, MIGraphX was unable to use the selected perfConfig for the fused module, and was also failing to properly split the module into separate conv + pointwise kernels because of the layout tail.

Technical Details

Update the GPU JIT MLIR compiler to detect layout-only tails after pointwise fusion and split the fused op into separate convolution MLIR, pointwise, and layout-copy kernels when needed. This keeps the MLIR portion compatible with the selected perf config while preserving the final output layout.

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.

Follow the LLVM AI Tool Use Policy for contributions using AI.

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 prevents gpu::compile_ops from blindly reusing cached MLIR tuning solutions (perfConfig strings) that no longer apply to a fused MLIR submodule, by validating cached solutions against the current fused module before reuse. This fits into the GPU backend’s tuning/benchmarking pipeline and improves robustness when problem-cache keys collide across different MLIR module structures.

Changes:

  • Add an applicability check for cached gpu::mlir_op solutions using is_module_fusible() before reusing a cached perfConfig.
  • Fall back to configured tuning candidates (and benchmarking when enabled) when a cached solution is null or inapplicable.
  • Add a verify regression that seeds MIGRAPHX_PROBLEM_CACHE with a known-bad MLIR perfConfig for a conv+pointwise fused path.

Reviewed changes

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

File Description
test/verify/test_conv_add_tune.cpp Adds a regression that seeds the problem cache with a bad MLIR perfConfig and exercises a fused conv/add MLIR path.
src/targets/gpu/compile_ops.cpp Validates cached MLIR solutions against the fused submodule before reusing them; falls back to candidate selection/benchmarking otherwise.
CHANGELOG.md Documents the fix for cached gpu::mlir_op perfConfig reuse causing compilation failures.

Comment thread test/verify/test_conv_add_tune.cpp Outdated
Comment thread test/verify/test_conv_add_tune.cpp Outdated
Comment thread src/targets/gpu/compile_ops.cpp Outdated
Comment thread src/targets/gpu/compile_ops.cpp Outdated

@umangyadav umangyadav left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Changes look good to me but let's wait on someone from MIGraphX team to review

@pfultz2 pfultz2 left a comment

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.

I dont think this is a good approach. I think it would be better to just add is_module_fusible to the problem config.

Comment thread test/verify/test_conv_add_tune.cpp Outdated
Comment thread src/targets/gpu/compile_ops.cpp Outdated
@justinrosner

Copy link
Copy Markdown
Author

I dont think this is a good approach. I think it would be better to just add is_module_fusible to the problem config.

I'm not sure that I follow. How would we add is_module_fusible to the problem config?

@pfultz2

pfultz2 commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

I saw this specifically with a splitK perfConfig cached for a standalone convolution problem that was later reused for a fused convolution plus pointwise MLIR module

That shouldn't fail, we split the module when is_module_fusible is false: https://github.com/ROCm/AMDMIGraphX/blob/develop/src/targets/gpu/jit/mlir.cpp#L215

@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #5064   +/-   ##
========================================
  Coverage    92.89%   92.89%           
========================================
  Files          603      603           
  Lines        32448    32448           
========================================
  Hits         30140    30140           
  Misses        2308     2308           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

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

Copy link
Copy Markdown
Test Batch New Rate (7df825) Old Rate (55ca93) Diff Status
torchvision-resnet50 64 3,103.80 3,169.20 -2.06%
torchvision-resnet50_fp16 64 6,724.24 6,721.88 0.04%
torchvision-densenet121 32 699.34 2,712.99 -74.22% 🔴
torchvision-densenet121_fp16 32 434.22 4,578.90 -90.52% 🔴
torchvision-inceptionv3 32 634.48 1,805.02 -64.85% 🔴
torchvision-inceptionv3_fp16 32 1,662.07 2,855.31 -41.79% 🔴
cadene-inceptionv4 16 716.84 824.13 -13.02% 🔴
cadene-resnext64x4 16 756.21 785.46 -3.72%
slim-mobilenet 64 8,451.19 8,444.46 0.08%
slim-nasnetalarge 64 171.20 229.50 -25.40% 🔴
slim-resnet50v2 64 1,045.58 3,185.35 -67.18% 🔴
bert-mrpc-onnx 8 1,175.05 1,173.56 0.13%
bert-mrpc-tf 1 73.17 484.20 -84.89% 🔴
pytorch-examples-wlang-gru 1 116.49 472.27 -75.33% 🔴
pytorch-examples-wlang-lstm 1 99.78 386.76 -74.20% 🔴
torchvision-resnet50_1 1 154.25 754.58 -79.56% 🔴
cadene-dpn92_1 1 62.67 444.58 -85.90% 🔴
cadene-resnext101_1 1 89.50 366.32 -75.57% 🔴
onnx-taau-downsample 1 237.31 402.99 -41.11% 🔴
dlrm-criteoterabyte 1 26.02 32.58 -20.14% 🔴
dlrm-criteoterabyte_fp16 1 52.71 52.65 0.11%
agentmodel 1 9,735.87 7,784.58 25.07% 🔆
unet_fp16 2 55.59 57.48 -3.29%
resnet50v1_fp16 1 755.86 932.89 -18.98% 🔴
resnet50v1_int8 1 929.89 934.74 -0.52%
bert_base_cased_fp16 64 707.82 1,105.09 -35.95% 🔴
bert_large_uncased_fp16 32 347.56 347.51 0.01%
bert_large_fp16 1 91.76 205.61 -55.37% 🔴
distilgpt2_fp16 16 1,321.36 2,104.89 -37.22% 🔴
yolov5s 1 568.28 559.63 1.55%
tinyllama 1 25.15 46.13 -45.48% 🔴
vicuna-fastchat 1 44.24 44.16 0.18%
whisper-tiny-encoder 1 415.39 415.45 -0.02%
whisper-tiny-decoder 1 411.12 412.20 -0.26%
llama2_7b 1 20.85 21.00 -0.71%
qwen1.5-7b 1 19.32 23.73 -18.56% 🔴
phi3-3.8b 1 26.85 26.83 0.06%
llama3-8b 1 7.89 21.87 -63.94% 🔴
whisper-large-encoder 1 4.00 10.23 -60.92% 🔴
whisper-large-decoder 1 25.95 104.89 -75.26% 🔴
mistral-7b 1 23.76 23.89 -0.56%
FLUX.1-schnell 1 747.85 760.54 -1.67%

Regressions detected 🔴

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

gh-app-migraphx-bot-pr-write Bot commented Jul 14, 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

@justinrosner justinrosner changed the title Validate cached MLIR tuning solutions before reuse Fix MLIR conv-pointwise-layout fusion splitting Jul 15, 2026
@justinrosner
justinrosner requested a review from pfultz2 July 15, 2026 01:07
@justinrosner

Copy link
Copy Markdown
Author

I saw this specifically with a splitK perfConfig cached for a standalone convolution problem that was later reused for a fused convolution plus pointwise MLIR module

That shouldn't fail, we split the module when is_module_fusible is false: https://github.com/ROCm/AMDMIGraphX/blob/develop/src/targets/gpu/jit/mlir.cpp#L215

@pfultz2 I believe I tracked down the root cause to the selected MLIR perfConfig rejecting the full conv + pointwise + layout fused module, and then the existing fallback only knew how to split into MLIR kernel + pointwise kernel. That two-way fallback assumes the pointwise portion can write directly into the original final output buffer. Once there is a layout-only tail, that assumption breaks, because the pointwise output and final output layout/shape are separate stages.

I am still worried about overall performance when trying to use the same perfConfigs for problemConfigs that index back to both fused and non-fused kernels. I know this is something that the rocMLIR team has talked about in the past before, so maybe we can try to come up with a better solution for this now. @dhernandez0 @umangyadav @pabloantoniom

@justinrosner
justinrosner requested a review from umangyadav July 15, 2026 01:31
Comment thread test/verify/test_conv_add_tune.cpp Outdated
migraphx::make_op("convolution", {{"padding", {1, 1, 1, 0}}, {"stride", {2, 2}}}),
x2,
w2);
auto add = mm->add_instruction(migraphx::make_op("add"), pooling, conv2);

@dhernandez0 dhernandez0 Jul 17, 2026

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.

this one is doing conv + add + reshape + transpose, right? I guess if you do (same conv) conv + relu it'd still be the same problem config but it will not work with split-k. Does the code take that into account as well? It'd be nice to have tests as well

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It should. Added a new test for this.

return *it;
}

static optional<instruction_ref> find_layout_tail_split(instruction_ref pointwise_ins)

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.

I dont think you need to write another function for this. reshape_lazy just needs to be added to the list in find_final_split.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I saw your comment down here: #5064 (comment). Is the current function approach that I have the proper way to go about solving this? Or is this a different underlying root cause where there should never be a reshape_lazy in the first place?

Comment thread test/verify/test_conv_add_tune.cpp Outdated
template struct test_conv_add_tune<migraphx::shape::fp8e4m3fn_type>;
template struct test_conv_add_tune<migraphx::shape::fp8e5m2_type>;

struct test_conv_add_layout_tune : verify_program<test_conv_add_layout_tune>

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 should be named test_conv_add_reshape_lazy_transpose. It also be added to it own .cpp file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Updated.

Comment thread test/verify/test_conv_add_tune.cpp Outdated
migraphx::make_op("convolution", {{"padding", {1, 1, 1, 0}}, {"stride", {2, 2}}}),
x1,
w1);
auto pooling =

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.

I dont think this pooling op is needed for this test.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed.

Comment thread test/verify/test_conv_add_tune.cpp Outdated
auto w2 = mm->add_literal(
migraphx::generate_literal({migraphx::shape::half_type, {1, 256, 3, 2}}, 1));

auto conv1 = mm->add_instruction(

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.

I dont think the conv1 is needed for this test.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Also removed.

@pfultz2

pfultz2 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

I saw this specifically with a splitK perfConfig cached for a standalone convolution problem that was later reused for a fused convolution plus pointwise MLIR module

That shouldn't fail, we split the module when is_module_fusible is false: https://github.com/ROCm/AMDMIGraphX/blob/develop/src/targets/gpu/jit/mlir.cpp#L215

@pfultz2 I believe I tracked down the root cause to the selected MLIR perfConfig rejecting the full conv + pointwise + layout fused module, and then the existing fallback only knew how to split into MLIR kernel + pointwise kernel. That two-way fallback assumes the pointwise portion can write directly into the original final output buffer. Once there is a layout-only tail, that assumption breaks, because the pointwise output and final output layout/shape are separate stages.

We already check for these "layout"-ops(which it more accurately called shape transforms as these ops do not change the memory layout) when we do the split, but we missed the check for reshape_lazy. We do check for reshape though.

I am still worried about overall performance when trying to use the same perfConfigs for problemConfigs that index back to both fused and non-fused kernels. I know this is something that the rocMLIR team has talked about in the past before, so maybe we can try to come up with a better solution for this now. @dhernandez0 @umangyadav @pabloantoniom

It needs to add is_module_fusible to the problem key so we treat them as different problems to tune for.

@pfultz2

pfultz2 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Actually, we shouldn't be adding reshape_lazy to the mlir submodule in the first place. This can cause problems if the layout changes. I wonder where the reshape_lazy is coming from as we dont insert reshape_lazy until lowering which happens after fuse_mlir.

@justinrosner

Copy link
Copy Markdown
Author

It needs to add is_module_fusible to the problem key so we treat them as different problems to tune for.

I think we should be careful with that because is_module_fusible depends on the selected perfConfig (the function’s solution param), not just the problem key/problem config. The same problem key can return either true or false depending on which perfConfig is being tested. For example, one config may fit the fused op into LDS while another does not. So adding that boolean to the problem key would encode a perfConfig-level property as if it were a problemConfig-level property (i.e., a property of the kernel itself).

I do see the issue you’re trying to solve, though. Would it make more sense to add a problemConfig-level capability, like supportsSplitK, instead of using is_module_fusible directly? That check is independent of the chosen perfConfig, so it seems much safer to include in the problem key than the result of is_module_fusible.

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