Skip to content

Split symbolic dimension pass - #5123

Draft
shivadbhavsar wants to merge 23 commits into
developfrom
split_sym_dim
Draft

Split symbolic dimension pass#5123
shivadbhavsar wants to merge 23 commits into
developfrom
split_sym_dim

Conversation

@shivadbhavsar

Copy link
Copy Markdown
Contributor

Motivation

Currently we only handle the dynamic batch case for using select modules, this creates the framework for generalizing for all axes.

Technical Details

The padding and slicing semantics are defined in terms of op-families where each family of ops (ie. convolution, pointwise, gemm, reduce, etc.) defines the characteristics of its input and output axes. These characteristics are then use to materialize pad-slice wrappings for each operator and then used to coalesce chains of operations that have compatible padding/masking characteristics.

See tests for examples of currently supported blocks.

WIP

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.

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

gh-app-migraphx-bot-pr-write Bot commented Aug 8, 2026

Copy link
Copy Markdown
Test Batch New Rate (141546) Old Rate (3a503c)* Diff Status
torchvision-resnet50 64 3,328.25 3,264.92 1.94%
torchvision-resnet50_fp16 64 7,879.97 7,548.67 4.39%
torchvision-densenet121 32 2,491.01 2,483.99 0.28%
torchvision-densenet121_fp16 32 5,034.94 5,004.24 0.61%
torchvision-inceptionv3 32 2,077.81 2,058.51 0.94%
torchvision-inceptionv3_fp16 32 4,496.58 4,416.99 1.80%
cadene-inceptionv4 16 820.80 820.61 0.02%
cadene-resnext64x4 16 783.23 782.78 0.06%
slim-mobilenet 64 8,386.71 8,386.36 0.00%
slim-nasnetalarge 64 228.82 228.86 -0.02%
slim-resnet50v2 64 3,239.26 3,180.91 1.83%
bert-mrpc-onnx 8 1,168.95 1,168.84 0.01%
bert-mrpc-tf 1 497.92 498.63 -0.14%
pytorch-examples-wlang-gru 1 476.16 473.35 0.59%
pytorch-examples-wlang-lstm 1 436.43 384.83 13.41% 🔆
torchvision-resnet50_1 1 1,054.48 1,046.63 0.75%
cadene-dpn92_1 1 459.76 437.32 5.13% 🔆
cadene-resnext101_1 1 364.94 365.89 -0.26%
onnx-taau-downsample 1 845.97 844.09 0.22%
dlrm-criteoterabyte 1 32.41 32.42 -0.03%
dlrm-criteoterabyte_fp16 1 51.79 51.80 -0.03%
agentmodel 1 9,620.56 9,209.12 4.47%
unet_fp16 2 58.74 58.80 -0.10%
resnet50v1_fp16 1 1,377.47 1,366.11 0.83%
resnet50v1_int8 1 1,654.15 1,883.96 -12.20% 🔴
bert_base_cased_fp16 64 1,097.61 1,098.16 -0.05%
bert_large_uncased_fp16 32 345.66 345.59 0.02%
bert_large_fp16 1 204.79 206.59 -0.87%
distilgpt2_fp16 16 2,094.27 2,092.89 0.07%
yolov5s 1 560.94 558.33 0.47%
tinyllama 1 45.85 45.83 0.04%
vicuna-fastchat 1 44.16 44.20 -0.09%
whisper-tiny-encoder 1 412.90 411.87 0.25%
whisper-tiny-decoder 1 409.79 408.48 0.32%
llama2_7b 1 20.83 20.84 -0.03%
qwen1.5-7b 1 23.60 23.58 0.07%
phi3-3.8b 1 26.80 26.72 0.33%
llama3-8b 1 21.77 21.80 -0.17%
whisper-large-encoder 1 10.18 10.18 0.01%
whisper-large-decoder 1 107.33 105.30 1.93%
mistral-7b 1 23.79 23.78 0.06%
FLUX.1-schnell 1 798.05 755.22 5.67% 🔆

Regressions detected 🔴

* No develop baseline was found for this PR's branch point; compared against the latest available develop run instead.

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

gh-app-migraphx-bot-pr-write Bot commented Aug 8, 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 🔴 FAILED: MIGraphX is not within tolerance - check verbose output
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

@CharlieL7 CharlieL7 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 would like to see an explanation of the steps in the split_sym_dim pass. Especially what the collect_roots() , discover_blocks(), materialize(), coalesce() and specialize_blocks() functions are trying to do.

I also see that this is using broadcast and multibroadcast with symbolic output_dyn_dims but without 2 inputs. Which is why they then have to be materialized to using the out_lens attribute. Our original idea was to use a symbolic map to evaluate at runtime, but that plan changed.

I don't think the multibroadcast or broadcast with symbolic output_dyn_dim with only 1 input would be able to run compute(). So it's an op that has to be rewritten. That's the same issue as the TopK redesign I had. To avoid that, multibroadcast and broadcast should be handled like dyn_slice.

unsupported_ops};
}

static void materialize_returned_slice(module& m)

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.

What is the purpose of this function?

{
return {
enable_pass(disabled(MIGRAPHX_ENABLE_FULL_DYNAMIC{}), split_single_dyn_dim{}),
split_sym_dim{},

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 think this was the only place we use split_single_dyn_dim{}. So should probably just remove the pass rather than have dead code?

"scatternd_none",
"select_module"};
"select_module",
"slice"};

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.

Why add slice to this list?

Comment thread src/instruction.cpp
Comment on lines +382 to +383
if(has_finalize(op))
return false;

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.

What was finalize() doing before these symbolic changes? As in, are there operators with a finalize() that can be evaluated?

Comment thread src/promote_literals.cpp
return root_ins.name() == "@literal" and root_ins.get_literal() == literal;
});
auto new_lit =
existing == root_module->end() ? root_module->add_literal(literal) : existing;

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.

Is this so we don't have a literal at the end of a module? Why is that an issue?

Comment thread src/split_sym_dim.cpp
unknown // no registered semantics -> forces a pass boundary
};

// Sentinel the padded region must hold so a sensitive op's result is unchanged.

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.

Suggested change
// Sentinel the padded region must hold so a sensitive op's result is unchanged.
// Sentinel value the padded region must hold so a sensitive op's result is unchanged.

Comment thread src/split_sym_dim.cpp
// Sentinel the padded region must hold so a sensitive op's result is unchanged.
enum class fill_kind
{
none, // op is insensitive on this axis (parallel); fill is a don't-care

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.

What does "(parallel)" mean?

Comment thread src/split_sym_dim.cpp
Comment on lines +142 to +153
std::optional<fill_kind> reduce_identity(const std::string& name)
{
if(name == "reduce_max")
return fill_kind::lowest;
if(name == "reduce_min")
return fill_kind::highest;
if(name == "reduce_prod" or name == "reduce_all")
return fill_kind::one;
if(name == "reduce_sum" or name == "reduce_any")
return fill_kind::zero;
return std::nullopt;
}

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 could be a map

Comment thread src/split_sym_dim.cpp
Comment on lines +156 to +167
std::vector<int64_t> reduce_axes(const operation& op, std::size_t ndim)
{
auto v = op.to_value();
std::vector<int64_t> axes;
if(v.contains("axes"))
axes = v.at("axes").to_vector<int64_t>();
int64_t rank = ndim;
for(auto& a : axes)
if(a < 0)
a += rank;
return axes;
}

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 is redoing what normalize_attributes does because it's happening before the normalize_ops pass. Maybe we should just run normalize_ops before this pass?

Comment thread src/split_sym_dim.cpp
axis_policy axes = unsupported_policy();
std::optional<symbolic_target_policy> symbolic_target;
};

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.

There are a lot of structs in this pass. Can you add descriptions how they are used?

shivadbhavsar and others added 10 commits August 15, 2026 22:03
Bring the PR #5112-based parser prerequisite branch onto the current development baseline before applying its remaining changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the merged resolver regression compatible with the dynamic-slice interface introduced by PR #5112.

Co-authored-by: Cursor <cursoragent@cursor.com>
Track exact integral shape values through parser operations so dynamic consumers retain symbolic output relationships without changing runtime dataflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the symbolic-value change focused by restoring existing resolver diagnostics and simplifying the signed Gather size declaration.

Co-authored-by: Cursor <cursoragent@cursor.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.

2 participants