Skip to content

[Feat] Add partial support for loops (ForStatement) inside action bodies#5552

Merged
jafingerhut merged 9 commits intop4lang:mainfrom
Vineet1101:feature/bmv2-forstatement-support
Mar 30, 2026
Merged

[Feat] Add partial support for loops (ForStatement) inside action bodies#5552
jafingerhut merged 9 commits intop4lang:mainfrom
Vineet1101:feature/bmv2-forstatement-support

Conversation

@Vineet1101
Copy link
Copy Markdown
Contributor

This PR provides partial implementation for p4lang/project-ideas#33 by adding support for ForStatement execution inside BMv2 action bodies.

Proposed Changes

  1. backends/bmv2/common/check_unsupported.h:
    • Removed the ForStatement rejection block.
    • Updated the ForInStatement error message to be more specific (since for-in loops still rely on midend lowering that is not yet fully supported).
  2. backends/bmv2/common/action.cpp:
    • Added ForStatement preorder handling to convertActionBody().
    • Loops compile cleanly to JSON using a condition label check and a backward unconditional _jump.
  3. testdata/p4_16_samples/forloop-bmv2.p4:
    • Added a rigorous test asserting the behavior of standard for-loops, nested for-loops, and conditionals wrapped inside loops.

Verification & Proof of Compilation

The changes successfully lower P4 loop statements into valid BMv2 JSON structures. Below is the generated primitive stack from our test program forloop-bmv2.p4:

1. Simple Loop (action sum_loop)

for (bit<8> i = 1; i <= h.h.count; i = i + 1) {
    h.h.sum = h.h.sum + (bit<32>)i;
}

Compiled BMv2 JSON Primitives:

  [0] assign(h.sum, 0)
  [1] assign(scalars.i_0, 1)          <--- Init
  [2] _jump_if_zero(expr, jump_to=6)  <--- Condition (if false, jump to end)
  [3] assign(h.sum, expr)             <--- Body
  [4] assign(scalars.i_0, expr)       <--- Update
  [5] _jump(jump_to=2)                <--- Jump back to Condition
  [6] assign(standard_metadata.egress_spec, 0)

2. Nested Loops (action nested_loop)

for (bit<8> i = 0; i < h.h.count; i = i + 1) {
    for (bit<8> j = 0; j < h.h.count; j = j + 1) {
        h.h.product = h.h.product + 1;
    }
}

Compiled BMv2 JSON Primitives:

  [0] assign(h.product, 0)
  [1] assign(scalars.i_1, 0)          <--- Outer Init
  [2] _jump_if_zero(expr, jump_to=10) <--- Outer Condition
  [3] assign(scalars.j_0, 0)          <--- Inner Init
  [4] _jump_if_zero(expr, jump_to=8)  <--- Inner Condition
  [5] assign(h.product, expr)         <--- Inner Body
  [6] assign(scalars.j_0, expr)       <--- Inner Update
  [7] _jump(jump_to=4)                <--- Inner Jump back
  [8] assign(scalars.i_1, expr)       <--- Outer Update
  [9] _jump(jump_to=2)                <--- Outer Jump back

All existing BMv2 tests and validations pass with P4TEST_REPLACE=1.

@Vineet1101
Copy link
Copy Markdown
Contributor Author

@fruffy @jafingerhut can you please review it

@fruffy
Copy link
Copy Markdown
Collaborator

fruffy commented Mar 20, 2026

Could you supply some stf test cases with the program to demonstrate various kind of packet input/output scenarios? Your example calculates sums based on the table entries and input header values, we can use that

@jafingerhut
Copy link
Copy Markdown
Contributor

Do these changes support break or continue statements?

@Vineet1101
Copy link
Copy Markdown
Contributor Author

Vineet1101 commented Mar 20, 2026

Do these changes support break or continue statements?

I was trying to implement forstatement first if this gets merged I will implement other two statements. If you want I can implement them as well in this PR

@Vineet1101
Copy link
Copy Markdown
Contributor Author

Could you supply some stf test cases with the program to demonstrate various kind of packet input/output scenarios? Your example calculates sums based on the table entries and input header values, we can use that

ok let me try

@Vineet1101
Copy link
Copy Markdown
Contributor Author

Do these changes support break or continue statements?

now it supports break and continue as well

@Vineet1101
Copy link
Copy Markdown
Contributor Author

Could you supply some stf test cases with the program to demonstrate various kind of packet input/output scenarios? Your example calculates sums based on the table entries and input header values, we can use that

done can you review it

@jafingerhut
Copy link
Copy Markdown
Contributor

Looks like several test failures in the latest version of the changes.

@jafingerhut
Copy link
Copy Markdown
Contributor

jafingerhut commented Mar 20, 2026

Looks like several test failures in the latest version of the changes.

OK, the C++ lint / formatting checks should be something you can fix with clang-format, but the testgen failures might be due to p4testgen not supporting for loops?

@fruffy - I am not here asking you to support for loops in p4testgen any time soon, as that sounds like too big of a request. Would you recommend marking test programs using for loops with XFAIL for now?

@jafingerhut
Copy link
Copy Markdown
Contributor

Looks like several test failures in the latest version of the changes.

OK, the C++ lint / formatting checks should be something you can fix with clang-format, but the testgen failures might be due to p4testgen not supporting for loops?

@fruffy - I am not here asking you to support for loops in p4testgen any time soon, as that sounds like too big of a request. Would you recommend marking test programs using for loops with XFAIL for now?

FYI, there are already P4 test programs that have for loops in them that are exercised by p4testgen, but I believe all of the existing ones are ones that a p4c pass written by Chris Dodd completely unroll at compile time, so there are no for lops in the IR by the time the back end sees it. Perhapt p4testgen can unroll or otherwise handle the same loops that p4c pass unrolls?

I suspect this PR has P4 test programs that are not unrolled by that pass, because the number of iterations is not possible (or not easy enough) to determine at compile time.

@fruffy fruffy added the core Topics concerning the core segments of the compiler (frontend, midend, parser) label Mar 21, 2026
@fruffy
Copy link
Copy Markdown
Collaborator

fruffy commented Mar 21, 2026

Perhapt p4testgen can unroll or otherwise handle the same loops that p4c pass unrolls?

Yes, the easiest approach is to just add the pass that unrolls loops and things should work as expected. At some point, if we want to support differential testing, we can support stepping through loops in the interpreter.

@Vineet1101 You can either try to add the loops unrolling pass to P4Testgen's BMv2 mid end here: https://github.com/p4lang/p4c/blob/main/backends/p4tools/modules/testgen/targets/bmv2/target.cpp#L112

Or add the failing tests to XFails.

@jafingerhut
Copy link
Copy Markdown
Contributor

@Vineet1101 Do you know how to add the failing tests to XFAILs, so that they are expected to fail, and when they do, CI tests overall succeed? Let me know if you need help with that.

@Vineet1101 Vineet1101 force-pushed the feature/bmv2-forstatement-support branch from 01c5dee to 86a4218 Compare March 24, 2026 21:06
@Vineet1101
Copy link
Copy Markdown
Contributor Author

@fruffy @jafingerhut I have addressed your reviews in recent commits. For now, I have added tests to XFAILs

@Vineet1101
Copy link
Copy Markdown
Contributor Author

Vineet1101 commented Mar 24, 2026

@Vineet1101 Do you know how to add the failing tests to XFAILs, so that they are expected to fail, and when they do, CI tests overall succeed? Let me know if you need help with that.

Not needed just went through the docs. Thanks for asking @jafingerhut :)

Copy link
Copy Markdown
Collaborator

@fruffy fruffy left a comment

Choose a reason for hiding this comment

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

Great! Which model are you using if I may ask :)

int labelIdEndOfLoop = jumpInfo->numLabels;
jumpInfo->numLabels += 1;

// Step 1: Emit init statements
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.

Nit: Gemini likes to do this for some reason. These comments go out of sync quite quickly. Would remove the Step n.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

so should I remove all these comments??

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.

The general convention of "nit" is that it's a "nice-to-have". Not required for merging and you can use your own judgement whether you want to address it or not.

https://onetwobytes.com/2021/11/09/nit-code-review/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for sharing this, I didn't know it before. I have removed the unnecessary steps comment

@Vineet1101
Copy link
Copy Markdown
Contributor Author

Great! Which model are you using if I may ask :)

I use Antigravity as an IDE and Claude Opus 4.6 for difficult tasks. To be clear, can one use AI as long as they know what it is doing?.

@fruffy
Copy link
Copy Markdown
Collaborator

fruffy commented Mar 26, 2026

To be clear, can one use AI as long as they know what it is doing?.

Yes! But always question what the LLM does. https://neilkakkar.com/agentic-debt.html

@Vineet1101 Vineet1101 force-pushed the feature/bmv2-forstatement-support branch from 86a4218 to 0991080 Compare March 28, 2026 14:56
Add ForStatement compilation to convertActionBody() in the BMv2
backend. The loop structure is compiled as:

  [init statements]
  labelCondition:
    _jump_if_zero(cond, labelEndOfLoop)
    [body]
    [update statements]
    _jump(labelCondition)
  labelEndOfLoop:

This leverages the existing JumpLabelInfo infrastructure and the
_jump/_jump_if_zero BMv2 primitives that already support backward
jumps.

Also remove the ForStatement block from CheckUnsupported and update
the ForInStatement error message to be more specific.

Signed-off-by: Vineet1101 <vineetgoel692@gmail.com>
Adds a test program that exercises a simple for-loop,
nested for-loops, and conditionals within a for-loop inside
a BMv2 action. Also includes the expected frontend, midend, and
JSON compiler outputs.

Signed-off-by: Vineet1101 <vineetgoel692@gmail.com>
Add four STF scenarios exercising for-loop actions in forloop-bmv2.p4:

1. Default action (sum_loop): count=5, expects sum=15
2. Nested loop (nested_loop): count=4, expects product=16
3. Conditional loop (conditional_loop): count=10, expects sum=5
4. Zero iterations (sum_loop): count=0, expects sum=0

Tests cover simple loops, nested loops, loops with conditionals,
and the edge case of zero iterations.

Signed-off-by: Vineet1101 <vineetgoel692@gmail.com>
Extend the ForStatement handler in action body compilation to support
break and continue statements:

- Add labelIdBreak and labelIdContinue fields to JumpLabelInfo to carry
  the innermost loop's label context through recursive calls.
- Handle BreakStatement by emitting _jump to labelIdBreak (end of loop).
- Handle ContinueStatement by emitting _jump to labelIdContinue (update
  section of the loop).
- Add a separate labelIdUpdate to the ForStatement handler so continue
  jumps to the update statements rather than the condition check.
- Save/restore loop context around body compilation to support nested
  loops correctly.

Signed-off-by: Vineet1101 <vineetgoel692@gmail.com>
Add break_loop and continue_loop actions to forloop-bmv2.p4:
- break_loop: loop from 0..count, break at i==5, verifies early exit
- continue_loop: loop 0..9, skip even values via continue, sum odd

Add corresponding STF scenarios 5 and 6, and update expected outputs.

Signed-off-by: Vineet1101 <vineetgoel692@gmail.com>
Signed-off-by: Vineet1101 <vineetgoel692@gmail.com>
The forloop-bmv2.p4 test program uses runtime-dependent loop
bounds (h.h.count) that cannot be unrolled at compile time by
the UnrollLoops pass. Mark it as XFAIL across all 5 BMv2
testgen test suites until p4testgen adds ForStatement support.

Signed-off-by: Vineet1101 <vineetgoel692@gmail.com>
@Vineet1101
Copy link
Copy Markdown
Contributor Author

To be clear, can one use AI as long as they know what it is doing?.

Yes! But always question what the LLM does. https://neilkakkar.com/agentic-debt.html

Will remember this from now on, and also thanks for sharing this insightful resource.

Vineet1101 and others added 2 commits March 28, 2026 08:02
@fruffy
Copy link
Copy Markdown
Collaborator

fruffy commented Mar 29, 2026

@jafingerhut Any more comments?

Copy link
Copy Markdown
Contributor

@jafingerhut jafingerhut left a comment

Choose a reason for hiding this comment

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

LGTM. I created my own new test action that had nested loops, and break and continue statements in both outer and inner loops, and examined the BMv2 JSON data file produced for that action, and it looks correct. All STF test cases in this PR also look correct. Nice!

@jafingerhut jafingerhut added this pull request to the merge queue Mar 30, 2026
Merged via the queue into p4lang:main with commit 4536e01 Mar 30, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Topics concerning the core segments of the compiler (frontend, midend, parser)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants