Skip to content

Extend DotGeneralSimplify to handle broadcasted scalars#2637

Open
binoygeorge97 wants to merge 3 commits into
EnzymeAD:mainfrom
binoygeorge97:fix-issue-1475
Open

Extend DotGeneralSimplify to handle broadcasted scalars#2637
binoygeorge97 wants to merge 3 commits into
EnzymeAD:mainfrom
binoygeorge97:fix-issue-1475

Conversation

@binoygeorge97

Copy link
Copy Markdown

Fixes #1475.

Problem

DotGeneralSimplify rewrites dot_general(splat, X) into a reduce over the
contracting dims, scaled by the splat value and broadcast to the result shape.
Detection matched a splat operand only via m_Constant, so a
broadcast_in_dim of a rank-0 runtime scalar (e.g. a function argument)
was missed — no canonicalizer can fold it into a splat constant, so the full
dot_general survived even though it is still s * reduce(X).

Change

  • Add extractBroadcastScalar, which looks through a rank-0 broadcast_in_dim
    (empty broadcast_dimensions) to its scalar operand Value. Mirrors the
    look-through in extractSplatInt, but returns a Value since the runtime
    case has no compile-time attribute.
  • Detection now accepts either a splat constant or such a broadcast, on either
    operand. The constant path is unchanged; the runtime path scales the reduce
    by a broadcast of the scalar Value and always emits the multiply, relying
    on the existing downstream canonicalizer to drop it when the value is 1.
  • The dot_general element-type constraint guarantees the scalar's type
    matches the reduce result, so no convert is needed.

Tests

test/lit_tests/dot_general_bcast_scalar.mlir covers a broadcast of a constant
scalar (already folded by the pipeline) plus runtime scalars on the LHS and RHS
operands, both of which now simplify away the dot_general.

Before (runtime scalar, LHS):

func.func @bcast_rank0_runtime(%s: tensor<f32>, %arg1: tensor<1024x32xf32>) -> tensor<24x32xf32> {
    %ones = stablehlo.broadcast_in_dim %s, dims = [] : (tensor<f32>) -> tensor<24x1024xf32>
    %r = stablehlo.dot_general %ones, %arg1, contracting_dims = [1] x [0], precision = [DEFAULT, DEFAULT] : (tensor<24x1024xf32>, tensor<1024x32xf32>) -> tensor<24x32xf32>
    return %r : tensor<24x32xf32>
}

After:

func.func @bcast_rank0_runtime(%arg0: tensor<f32>, %arg1: tensor<1024x32xf32>) -> tensor<24x32xf32> {
    %cst = stablehlo.constant dense<0.000000e+00> : tensor<f32>
    %0 = stablehlo.reduce(%arg1 init: %cst) applies stablehlo.add across dimensions = [0] : (tensor<1024x32xf32>, tensor<f32>) -> tensor<32xf32>
    %1 = stablehlo.broadcast_in_dim %0, dims = [1] : (tensor<32xf32>) -> tensor<24x32xf32>
    %2 = stablehlo.broadcast_in_dim %arg0, dims = [] : (tensor<f32>) -> tensor<24x32xf32>
    %3 = stablehlo.multiply %1, %2 : tensor<24x32xf32>
    return %3 : tensor<24x32xf32>
}

binoygeorge97 and others added 3 commits July 18, 2026 11:34
DotGeneralSimplify rewrites dot_general(splat, X) into a reduce over the
contracting dims scaled by the splat value. Detection only matched splat
constants via m_Constant, so a broadcast_in_dim of a rank-0 runtime scalar
(which no canonicalizer can fold to a constant) was left as a full
dot_general.

Add an extractBroadcastScalar helper that looks through a rank-0
broadcast_in_dim to its scalar operand (mirroring extractSplatInt's
empty-broadcast_dimensions look-through, but returning the Value since the
runtime case has no compile-time attribute). Detection now accepts either a
splat constant or such a broadcast, on either operand. The constant path is
unchanged; the runtime path scales the reduce by a broadcast of the scalar
Value and always emits the multiply, relying on the existing canonicalizer
to drop it when the value is 1.

Fixes EnzymeAD#1475
Covers three cases: broadcast of a constant scalar (already folded by the
pipeline), and broadcast of a runtime scalar on the LHS and RHS operands,
both of which now simplify away the dot_general.
@wsmoses
wsmoses requested a review from avik-pal July 18, 2026 20:54
@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 31.11%. Comparing base (94c1d96) to head (8a206cb).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2637      +/-   ##
==========================================
- Coverage   31.73%   31.11%   -0.63%     
==========================================
  Files         227      227              
  Lines       44908    44875      -33     
==========================================
- Hits        14253    13963     -290     
- Misses      30655    30912     +257     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

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

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.

Extend DotGeneralSimplify to handle Broadcasted Scalars

1 participant