This is an archive of the discontinued LLVM Phabricator instance.

[mlir][affine] Normalize constant valued bound loop
ClosedPublic

Authored by Lewuathe on Jan 18 2023, 10:29 PM.

Details

Summary

This change aims to resolve the issue reported in https://github.com/llvm/llvm-project/issues/59994.

After calling AffineForOp#setUpperBound and setLowerBound, it makes the original affine map structure inconsistent. It's necessary to keep the original map to build the new induction vars.

Diff Detail

Event Timeline

Lewuathe created this revision.Jan 18 2023, 10:29 PM
Lewuathe requested review of this revision.Jan 18 2023, 10:29 PM
Lewuathe edited the summary of this revision. (Show Details)Jan 18 2023, 10:30 PM
Lewuathe updated this revision to Diff 490378.Jan 18 2023, 10:34 PM

Remove unused debug code.

Lewuathe edited the summary of this revision. (Show Details)Jan 18 2023, 10:35 PM
bondhugula requested changes to this revision.Jan 23 2023, 7:31 PM

Thank you for reporting and attempting to fix https://github.com/llvm/llvm-project/issues/59994. Some comments.

mlir/lib/Dialect/Affine/Utils/Utils.cpp
568–569

Use the range-based ctor or use llvm::append_range to write this more compactly.

573

Likewise.

579–580

You can move these lines up closer to declaration to keep related things together or move the declaration below.

Group the canonicalize calls below with the code creating origLb/UbOperands.

582–583

origLb/UbMap aren't good names since they aren't really the original maps anymore.

This revision now requires changes to proceed.Jan 23 2023, 7:31 PM
bondhugula added inline comments.Jan 23 2023, 7:31 PM
mlir/test/Dialect/Affine/affine-loop-normalize.mlir
216

Drop extra line.

Lewuathe updated this revision to Diff 491997.Jan 24 2023, 10:11 PM

Cleanup the code based on the review comments.

Lewuathe marked 5 inline comments as done.Jan 24 2023, 10:11 PM
Lewuathe updated this revision to Diff 492297.Jan 25 2023, 5:02 PM

Apply clang-format.

Comments on test cases.

mlir/test/Dialect/Affine/affine-loop-normalize.mlir
226

#map -> #map{{.*}} or #{{.*}}
Avoid map in CHECK line since its suffix would change when the test case is updated.

239

Both of these test cases really don't exercise all the changes. The fact that the other test cases didn't have to change indicates those aren't impacted by the canonicalization added. Can you include a couple more test cases where the bounds have maps + operands (with symbols) that would get simplified. Here is one.

affine.for %i = 0 to affine_map<(d0, d1) -> (d0)>(%N, %M) step 4 {
   ...
}

(%N, %M) can be func arguments. d1 will be dropped, d0 will become s0.

239

Does this test case exercise anything more than the first one?

240

Capturing IV is unnecessary, you aren't using it.

Lewuathe added inline comments.Jan 25 2023, 8:25 PM
mlir/test/Dialect/Affine/affine-loop-normalize.mlir
239

The first one is identical to the case in the issue ticket. The second one aimed to just focus on the canonicalization of a constant value in affine.for but we may not need this one.

I'll remove the second one.

Lewuathe updated this revision to Diff 492334.Jan 25 2023, 10:24 PM

Add test cases.

Lewuathe marked 4 inline comments as done.Jan 25 2023, 10:24 PM
bondhugula added inline comments.Jan 29 2023, 4:19 AM
mlir/lib/Dialect/Affine/Utils/Utils.cpp
588–592

How are we ensuring that in newUbExprs, the symbols appear in this order? I couldn't immediately see this.

Overall, I feel adding this canonicalization is unnecessarily complicating this code. From the commit summary, it isn't clear why canonicalization is needed here on the fly. s0 - s0 will simplify to 0 by construction. Something being zero can be readily checked if needed.

598

Please move this line to right above 619.

mlir/test/Dialect/Affine/affine-loop-normalize.mlir
239

Remove numbered values from CHECK lines. Either %arg{{.*}} or capture and match.

295

Drop extra new line.

Lewuathe added inline comments.Jan 29 2023, 5:20 PM
mlir/lib/Dialect/Affine/Utils/Utils.cpp
588–592

When we use arith.constant value for the lower and upper bound, it's translated into the affine map ()[s0] -> (s0) irrelevant to the actual value. And it always induces 0 in L.613 ~ L.616 as origUbExprs[i] and origLbExprs[0] are same.

for (unsigned i = 0, e = origUbExprs.size(); i < e; ++i) {
  newUbExprs.push_back(
      (origUbExprs[i] - origLbExprs[0]).ceilDiv(origLoopStep));
}

Without this canonicalization, the first test case here

func.func @constant_lower_bound() {
  %c0 = arith.constant 0 : index
  %c1 = arith.constant 1 : index
  scf.for %j = %c0 to %c1 step %c1 {
    affine.for %i = %c0 to %c1 {
    }
  }
  return
}

went to 0 to 0 as follows.

func.func @constant_lower_bound() {
  %c0 = arith.constant 0 : index
  %c1 = arith.constant 1 : index
  scf.for %j = %c0 to %c1 step %c1 {
    affine.for %i = 0 to 0 {
    }
  }
  return
}

So I thought we did not care about the constant valued bounds properly.

598

We need to use this map in the coming lines L.613 - L.616. So do we need to keep this position?

bondhugula added inline comments.Jan 30 2023, 5:40 PM
mlir/lib/Dialect/Affine/Utils/Utils.cpp
588–592

It appears that canonicalization isn't the right fix for the bug you are fixing -- the bug would still be there in other cases I believe when you don't have constants. Also, we shouldn't need to canonicalize bounds, especially if it makes the code complex here. The -canonicalize pass takes care of it.

For eg., does the following work:

affine.for %i = %M to %N
Lewuathe added inline comments.Jan 31 2023, 4:27 PM
mlir/lib/Dialect/Affine/Utils/Utils.cpp
588–592

I see. Thinking the case again, we do not need to make sure to canonicalize the bound if we can assume -canonicalize pass. I'm going to apply only the fix for the issue reported originally.

Lewuathe updated this revision to Diff 494502.Feb 2 2023, 9:12 PM

Stop canonicalization in the middle of normalization pass.

Lewuathe marked 5 inline comments as done.Feb 2 2023, 9:13 PM
Lewuathe updated this revision to Diff 494503.Feb 2 2023, 9:16 PM

Rename variables.

bondhugula added inline comments.Feb 10 2023, 1:36 PM
mlir/lib/Dialect/Affine/Utils/Utils.cpp
588–592

Thanks - this would be the right approach instead of combining orthogonal things that are clouding this patch. I'll take another look tomorrow.

Can you please update the commit summary? It's no longer reflective of the change.

Lewuathe edited the summary of this revision. (Show Details)Feb 12 2023, 7:52 PM

@bondhugula Thanks. I updated the commit message accordingly.

bondhugula accepted this revision.Feb 13 2023, 3:00 AM

LGTM - thanks.

mlir/lib/Dialect/Affine/Utils/Utils.cpp
616–619

You can do: opBuilder.getAffineMap(...). (No need of context arg then.)

This revision is now accepted and ready to land.Feb 13 2023, 3:00 AM
Lewuathe added inline comments.Feb 13 2023, 7:08 PM
mlir/lib/Dialect/Affine/Utils/Utils.cpp
616–619

Thanks for letting me know. But that utility is not prepared yet. OpBuilder seems to have some methods for building purpose specific AffineMaps (e.g. getEmptyAffineMap )

/llvm-project/mlir/lib/Dialect/Affine/Utils/Utils.cpp:613:34: error: no member named 'getAffineMap' in 'mlir::OpBuilder'
  AffineMap newUbMap = opBuilder.getAffineMap(
This revision was automatically updated to reflect the committed changes.