Skip to content

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

Open
Vineet1101 wants to merge 5 commits intop4lang:mainfrom
Vineet1101:feature/bmv2-forstatement-support
Open

[Feat] Add partial support for loops (ForStatement) inside action bodies#5552
Vineet1101 wants to merge 5 commits intop4lang:mainfrom
Vineet1101:feature/bmv2-forstatement-support

Conversation

@Vineet1101
Copy link
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.

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>
@Vineet1101
Copy link
Contributor Author

@fruffy @jafingerhut can you please review it

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>
@fruffy
Copy link
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
Contributor

Do these changes support break or continue statements?

@Vineet1101
Copy link
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
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
Contributor Author

Do these changes support break or continue statements?

now it supports break and continue as well

@Vineet1101
Copy link
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
Contributor

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

@jafingerhut
Copy link
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
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
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.

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