Skip to content

Implement select cases simplifications (#204)#217

Open
mtsamis wants to merge 7 commits intop4lang:mainfrom
mtsamis:mtsamis/transition-select-opt
Open

Implement select cases simplifications (#204)#217
mtsamis wants to merge 7 commits intop4lang:mainfrom
mtsamis:mtsamis/transition-select-opt

Conversation

@mtsamis
Copy link
Copy Markdown
Collaborator

@mtsamis mtsamis commented Jul 29, 2025

This PR implements the various transition_select optimizations from ticket #204.

Four of the five transformations (SimplifySelectList / ReplaceSelectRange / RemoveSelectBooleans / SingleArgumentSelect) are implemented as patterns of a new SimplifySelect optimization pass, while the equivalent of SimplifySelectCases is implemented as ParserTransitionSelectOp canonicalizations.

To aid implementing these optimizations and also simplify the IR, there is an initial commit that makes p4hir.transition_select and the corresponding yield statements have variadic arguments. This was done because having either a single argument or a tuple of arguments and set products made all passes more complicated and required code duplication to handle one vs many arguments.

There is also an additional commit that refactors CastOp constant folding and fixes various signedness bugs around it.

@asl asl self-requested a review July 30, 2025 21:35
@asl asl linked an issue Jul 30, 2025 that may be closed by this pull request
Copy link
Copy Markdown
Collaborator

@asl asl left a comment

Choose a reason for hiding this comment

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

See comments


// CHECK-COUNT-2: p4hir.select_case

p4hir.state @start {
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 probably suggest to do CHECK-LABEL: p4hir.state @start to make checks more structured. Same for other cases. You won't need CHECK-COUNT above then.

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.

Thanks for pointing out, done.
(I kept the check-count because it was meant to be only for the first state, not both)

%set_3 = p4hir.const #set_const_of_int2_i8i
%set_4 = p4hir.const #set_const_of_int1_b2i
%everything = p4hir.const #everything
p4hir.state @start {
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.

See above. I'd use CHECK-LABEL to narrow the check place

%false = p4hir.const #false
%w = p4hir.variable ["w", init] : <!p4hir.bool>
p4hir.assign %false, %w : <!p4hir.bool>
p4hir.state @start {
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.

see above

%false = p4hir.const #false
%w = p4hir.variable ["w", init] : <!p4hir.bool>
p4hir.assign %false, %w : <!p4hir.bool>
p4hir.state @start {
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.

ditto


// Collect operations to apply patterns.
llvm::SmallVector<mlir::Operation *, 16> ops;
getOperation()->walk<mlir::WalkOrder::PostOrder>([&](mlir::Operation *op) {
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 you can just do

    getOperation()->walk<mlir::WalkOrder::PostOrder>([&](P4HIR::ParserTransitionSelectOp op) {
      ops.push_back(op);
    }

What is the purpose of postorder traversal?

Even more, it seems we can explicitly apply to the ops, right? Something along https://mlir.llvm.org/docs/PassManagement/#operation-pass-static-filtering-by-op-type and corresponding tablegen bits.

Copy link
Copy Markdown
Collaborator Author

@mtsamis mtsamis Jul 31, 2025

Choose a reason for hiding this comment

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

Yes, thanks for pointing out, I copied this from another pass and didn't though about it, fixed now:

  1. Modified pass in tablegen to only apply on ParserOp.
  2. Removed WalkOrder and mlir::isa bits.
  3. Added strictMode = mlir::GreedyRewriteStrictness::ExistingOps config to avoid unnecessary iterations on other ops.

return false;

bool isShapePrimitive = (shape.size() == 1)
&& !shape.front().getDefiningOp<P4HIR::TupleOp>();
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 really how clang-format did? If not, will you please reformat the things? :)

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.

No, because I didn't use clang-format 😅
I now properly formatted all the code in the last push.

@mtsamis mtsamis force-pushed the mtsamis/transition-select-opt branch 2 times, most recently from 30dc9dc to 3eb26e9 Compare July 31, 2025 09:12
getOperation()->walk([&](P4HIR::ParserTransitionSelectOp op) { ops.push_back(op); });

mlir::GreedyRewriteConfig config;
config.strictMode = mlir::GreedyRewriteStrictness::ExistingOps;
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 if we'd have bool inside tuple as a key? Won't this prevent application of second pattern to the newly created op? Should we use ExistingAndNewOps here?

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.

It's always the same transition select op and we just modify the arguments / cases. So no, it doesn't prevent application of the pattern multiple times.

Copy link
Copy Markdown
Collaborator

@asl asl left a comment

Choose a reason for hiding this comment

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

See question above

@mtsamis mtsamis force-pushed the mtsamis/transition-select-opt branch from 3eb26e9 to 97acab0 Compare August 1, 2025 13:58
@mtsamis
Copy link
Copy Markdown
Collaborator Author

mtsamis commented Aug 1, 2025

Latest push fixes and improves the code for FlattenTuples:

Previously both shape and keys arguments in flattenValues were mlir::ValueRange and we unpacked shape by looking for TupleOp. This was incorrect, since a tuple can be something other than a TupleOp (e.g. a tuple valued argument). Apart from resulting in bugs it also didn't make sense for shape to be a ValueRange.

In the latest code shape is replaced by the appropriate TypeRange and we unpack the actual type. The function now creates p4hir.tuple_extract operations to unpack values of tuple type that are not the result of a TupleOp.

The test case was extended with the previously failing cases.

@asl
Copy link
Copy Markdown
Collaborator

asl commented Aug 1, 2025

In the latest code shape is replaced by the appropriate TypeRange and we unpack the actual type. The function now creates p4hir.tuple_extract operations to unpack values of tuple type that are not the result of a TupleOp.

Can you always do this? And let canonicalizer fold the extracts?

@mtsamis mtsamis force-pushed the mtsamis/transition-select-opt branch from 97acab0 to 752096c Compare August 5, 2025 12:59
@mtsamis
Copy link
Copy Markdown
Collaborator Author

mtsamis commented Aug 5, 2025

In the latest code shape is replaced by the appropriate TypeRange and we unpack the actual type. The function now creates p4hir.tuple_extract operations to unpack values of tuple type that are not the result of a TupleOp.

Can you always do this? And let canonicalizer fold the extracts?

Done by also implementing TupleExtractOp::fold (same with StructExtractOp).

@mtsamis
Copy link
Copy Markdown
Collaborator Author

mtsamis commented Aug 5, 2025

The last force-pushed revision of this also modified the SetAttr::getConstantValue.
Instead of a templated AttrT type it handles all Attr types and returns the appropriate constant value:

llvm::APInt getConstantValue() {
  assert(getKind() == SetKind::Constant && "Expected Constant kind set.");
  assert(getMembers().size() == 1 && "Expected single member set.");
  auto attr = getMembers()[0];
  if (auto intAttr = mlir::dyn_cast<P4HIR::IntAttr>(attr)) {
    return intAttr.getValue();
  } else if (auto boolAttr = mlir::dyn_cast<P4HIR::BoolAttr>(attr)) {
    return llvm::APInt(1, static_cast<uint64_t>(boolAttr.getValue()));
  } else {
    auto enumFieldAttr = mlir::cast<P4HIR::EnumFieldAttr>(attr);
    auto enumType = mlir::cast<P4HIR::SerEnumType>(enumFieldAttr.getType());
    auto field = enumFieldAttr.getField().getValue();
    return enumType.valueOf<P4HIR::IntAttr>(field).getValue();
  }
}

This fixed a crash when we had a SerEnum type in a select during the MakeSingleArgument pattern.

Signed-off-by: Manolis Tsamis <tsamismanolis@gmail.com>
Signed-off-by: Manolis Tsamis <tsamismanolis@gmail.com>
Signed-off-by: Manolis Tsamis <tsamismanolis@gmail.com>
Signed-off-by: Manolis Tsamis <tsamismanolis@gmail.com>
@mtsamis mtsamis force-pushed the mtsamis/transition-select-opt branch from 752096c to 92e6a98 Compare August 18, 2025 13:56
Signed-off-by: Manolis Tsamis <tsamismanolis@gmail.com>
Signed-off-by: Manolis Tsamis <tsamismanolis@gmail.com>
…f SimplifySelectCases

Signed-off-by: Manolis Tsamis <tsamismanolis@gmail.com>
@mtsamis mtsamis force-pushed the mtsamis/transition-select-opt branch from 92e6a98 to 7337dfe Compare August 18, 2025 14:10
@mtsamis
Copy link
Copy Markdown
Collaborator Author

mtsamis commented Aug 18, 2025

Changes in the last version:

  • Introduce new helper P4HIR::getConstantInt to convert an mlir::Attribute to llvm::APSInt when applicable.
  • foldCastConstant now fixes another upstream bug that resulted in a crash when casting a CtorParamAttr constant.
  • Add SetAttr::isEmptyRange to check for empty ranges (ranges [min, max] where min > max) and fix handling of empty ranges in ReplaceRanges.
  • Fix handling of select arguments that are not BitsType in ReplaceRanges (e.g. SerEnumType).
  • Fix mask types and improve code in MakeSingleArgument::createMaskOp

With these changes all samples from the P4C repository transform without crashes.


private:
// Create appropriate casts to convert `keyset` to a set with element type `newType`.
mlir::Value castKeysetValue(mlir::PatternRewriter &rewriter, mlir::Type newType,
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.

Can we utilize TypeConverter here? We're having bunch of helpers in p4mlir/Conversion/ConversionPatterns.h

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.

Add select cases simplification

2 participants