Skip to content

Conversation

@mtsamis
Copy link
Collaborator

@mtsamis mtsamis commented Aug 18, 2025

This PR implements the equivalent of P4C's RemoveParserControlFlow pass. It replaces if and switch statements inside parser states with newly introduced states and transitions.

Some infrastructure for this pass is generally useful and will be helpful in implementing subparser/control inlining. As such, it has been placed in a separate IRUtils namespace.

(This is very similar to #220, which I accidentally closed by deleting the branch. This new branch contains few more helper functions and a different TransitionSelectBuilder::addCases implementation).

@mtsamis mtsamis force-pushed the mtsamis/remove-parser-cf branch from ceda822 to f2e7092 Compare August 20, 2025 11:02
@asl asl force-pushed the mtsamis/remove-parser-cf branch from f2e7092 to cab6581 Compare August 20, 2025 17:15
@asl asl self-requested a review August 20, 2025 17:15
@asl asl linked an issue Aug 20, 2025 that may be closed by this pull request
Copy link
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 (mostly minor) here


// Helper to create a new empty sub-state for `state`.
P4HIR::ParserStateOp createSubState(mlir::RewriterBase &rewriter, P4HIR::ParserStateOp state,
llvm::StringRef suffix);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use Twine for suffix.

auto getNextTransition() { return getBody().back().getTerminator(); }
auto getNextTransition() { return getBlock()->getTerminator(); }

/// Get a SymbolRefAttr for this parser state.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we be a bit more specific here. Essentially it is a fully qualified state name, right? So this should be explained here :)

// Inline `scopeOp`'s body to its parent.
void inlineScope(mlir::RewriterBase &rewriter, P4HIR::ScopeOp scopeOp);

// Return true if it's valid to call `splitBlockAt` for the given arguments.
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would help to explain better the invariants / conditions here.

// Split resulting block.
mlir::Block *before = op->getBlock();
mlir::Block *middle = rewriter.splitBlock(before, op->getIterator());
mlir::Block *after = rewriter.splitBlock(middle, ++op->getIterator());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
mlir::Block *after = rewriter.splitBlock(middle, ++op->getIterator());
mlir::Block *after = rewriter.splitBlock(middle, std::next(op->getIterator()));

// This is needed to properly handle operands after `copyOp` calls.
for (mlir::Operation &op : llvm::make_early_inc_range(llvm::reverse(*block))) {
if (mlir::isa<P4HIR::VariableOp>(op)) {
// Promote variables to a parser locals.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Promote variables to a parser locals.
// Move variables to top-level. Rewriter position is expected to point where we can create such locals (e.g. parser or control locals

Note that for control locals we'd need to be a bit more elaborated as actions are isolated from above, so they are exported via "symbol references"

std::string name = basename;

size_t counter = 0;
while (parser.lookupSymbol(name)) name = basename + "_" + std::to_string(counter++);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you ask symbol table to generate unique symbol for you?

E.g.:

        auto getUniqueName = [&](mlir::StringAttr toRename) {
            unsigned counter = 0;
            return mlir::SymbolTable::generateSymbolName<256>(
                toRename,
                [&](llvm::StringRef candidate) {
                    return parser.lookupSymbol(StringAttr::get(candidate)) != nullptr;
                },
                counter);
        };

        auto nameAttr = getUniqueName(StringAttr::get(llvm::Twine(state.getSymName()) + "_" + suffix)));

You might want to tune strings types here though :)


// Create new intermediate state that transitions to `transitionTo` and move `ops` in it.
// If `ops` is non-null the terminator of the block is erased once moved.
P4HIR::ParserStateOp createSubState(llvm::StringRef suffix,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use twines here and below

@mtsamis mtsamis force-pushed the mtsamis/remove-parser-cf branch from cab6581 to e6c0ed9 Compare August 21, 2025 10:14
@mtsamis
Copy link
Collaborator Author

mtsamis commented Aug 21, 2025

Thanks for the review! I have addressed your comments in the latest version.

In addition, I made adjustBlockUses to only move variables if they escape, and simplified ParserStateOp::getSymbolRef by using more appropriate get functions.

Note that for control locals we'd need to be a bit more elaborated as actions are isolated from above, so they are exported via "symbol references"

Indeed, the current version probably won't work for control locals. It looks like it would need some work to get them right, but we don't currently need that somewhere. Should we add an assertion for now and implement in the future if needed?

@asl
Copy link
Collaborator

asl commented Aug 21, 2025

Indeed, the current version probably won't work for control locals. It looks like it would need some work to get them right, but we don't currently need that somewhere. Should we add an assertion for now and implement in the future if needed?

Yeah, let's do an assert for now.

@mtsamis mtsamis force-pushed the mtsamis/remove-parser-cf branch from e6c0ed9 to 55a6e4e Compare August 21, 2025 16:23
@mtsamis
Copy link
Collaborator Author

mtsamis commented Aug 21, 2025

Added assert for ControlOp in adjustBlockUses


// Create new intermediate state that transitions to `transitionTo` and move `ops` in it.
// If `ops` is non-null the terminator of the block is erased once moved.
P4HIR::ParserStateOp createSubState(llvm::Twine suffix, P4HIR::ParserStateOp transitionTo = {},
Copy link
Collaborator

@asl asl Aug 21, 2025

Choose a reason for hiding this comment

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

This is a common problem :)

Twines should only be used as const references in arguments

See https://github.com/llvm/llvm-project/blob/bce9b6d1771bbcf6d250935fdaab1dfa0922fd72/llvm/include/llvm/ADT/Twine.h#L38

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I see, thanks for pointing out. I force pushed the fix.

@mtsamis mtsamis force-pushed the mtsamis/remove-parser-cf branch from 55a6e4e to 9f491a8 Compare August 22, 2025 13:45
@asl asl force-pushed the mtsamis/remove-parser-cf branch 2 times, most recently from 0b4b930 to 596ac64 Compare September 23, 2025 06:46
@asl
Copy link
Collaborator

asl commented Sep 23, 2025

@mtsamis Looks like it does not compile anymore

@mtsamis
Copy link
Collaborator Author

mtsamis commented Sep 23, 2025

@mtsamis Looks like it does not compile anymore

Thanks for reporting, I'll have a look.

@asl asl force-pushed the mtsamis/remove-parser-cf branch from 2671cb6 to e29f8c5 Compare October 1, 2025 22:00
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.

Remove conditional control flow from parsers

2 participants