diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessArray.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessArray.ir index 44476bb0ef..ff7cefe6cd 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessArray.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessArray.ir @@ -28,7 +28,7 @@ top proc __test_module__Array_0_next<>(__state: bits[32][4], init={[0, 1, 2, 3]} tuple.3: () = tuple(id=3) tuple_index.20: token = tuple_index(counted_for.19, index=0, id=20) assert.24: token = assert(__token, or.23, message="State element written before read in same activation.", id=24) - next_value.25: () = next_value(param=__state, value=new_array, predicate=literal.2, id=25) + next_value.25: () = next_value(state_element=__state, value=new_array, predicate=literal.2, id=25) tuple.26: () = tuple(id=26) tuple.27: () = tuple(id=27) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessBool.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessBool.ir index aaf8b0d28c..b16ece00db 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessBool.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessBool.ir @@ -12,7 +12,7 @@ top proc __test_module__Bool_0_next<>(__state: bits[1], init={0}) { tuple.3: () = tuple(id=3) false_val: bits[1] = identity(__state, id=5) assert.9: token = assert(__token, or.8, message="State element written before read in same activation.", id=9) - next_value.10: () = next_value(param=__state, value=literal.6, predicate=literal.2, id=10) + next_value.10: () = next_value(state_element=__state, value=literal.6, predicate=literal.2, id=10) tuple.11: () = tuple(id=11) tuple.12: () = tuple(id=12) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessConditionalWrite.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessConditionalWrite.ir index c9999512af..fa3d8adddf 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessConditionalWrite.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessConditionalWrite.ir @@ -39,10 +39,10 @@ top proc __test_module__main_0_next<>(__state: (bits[32], bits[1]), init={(0, 0) tuple.3: () = tuple(id=3) literal.6: bits[32] = literal(value=0, id=6) literal.12: bits[32] = literal(value=0, id=12) - next_value.22: () = next_value(param=__state, value=tuple.17, predicate=and.18, id=22) + next_value.22: () = next_value(state_element=__state, value=tuple.17, predicate=and.18, id=22) literal.24: bits[32] = literal(value=0, id=24) assert.38: token = assert(assert.34, or.37, message="State element written after write in same activation.", id=38) or.39: bits[1] = or(and.18, and.31, id=39) - next_value.40: () = next_value(param=__state, value=tuple.29, predicate=and.31, id=40) + next_value.40: () = next_value(state_element=__state, value=tuple.29, predicate=and.31, id=40) sel.42: () = sel(eq.11, cases=[tuple.41, tuple.23], id=42) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessLabeledReadAndWrite.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessLabeledReadAndWrite.ir index 94da6f910c..56f27d957d 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessLabeledReadAndWrite.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessLabeledReadAndWrite.ir @@ -13,7 +13,7 @@ top proc __test_module__main_0_next<>(__state: bits[32], init={0}) { y: bits[32] = add(x, literal.6, id=7) tuple.3: () = tuple(id=3) assert.10: token = assert(__token, or.9, message="State element written before read in same activation.", id=10) - next_value.11: () = next_value(param=__state, value=y, predicate=literal.2, label="main_write", id=11) + next_value.11: () = next_value(state_element=__state, value=y, predicate=literal.2, label="main_write", id=11) tuple.12: () = tuple(id=12) tuple.13: () = tuple(id=13) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMatch.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMatch.ir index 5fca02c1bb..16bbbd3391 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMatch.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMatch.ir @@ -45,14 +45,14 @@ top proc __test_module__main_0_next<>(__state: bits[32], init={0}) { tuple.36: () = tuple(id=36) tuple.50: () = tuple(id=50) tuple.3: () = tuple(id=3) - next_value.17: () = next_value(param=__state, value=add.11, predicate=and.13, id=17) + next_value.17: () = next_value(state_element=__state, value=add.11, predicate=and.13, id=17) tuple.18: () = tuple(id=18) - next_value.34: () = next_value(param=__state, value=umul.23, predicate=and.25, id=34) + next_value.34: () = next_value(state_element=__state, value=umul.23, predicate=and.25, id=34) tuple.35: () = tuple(id=35) literal.37: bits[1] = literal(value=1, id=37) assert.46: token = assert(assert.42, or.45, message="State element written after write in same activation.", id=46) or.47: bits[1] = or(or.33, and.39, id=47) - next_value.48: () = next_value(param=__state, value=current, predicate=and.39, id=48) + next_value.48: () = next_value(state_element=__state, value=current, predicate=and.39, id=48) tuple.49: () = tuple(id=49) priority_sel.52: () = priority_sel(concat.51, cases=[tuple.19, tuple.36], default=tuple.50, id=52) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMatchMultipleWrites.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMatchMultipleWrites.ir index 69b7d139d3..d3749677b9 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMatchMultipleWrites.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMatchMultipleWrites.ir @@ -51,16 +51,16 @@ top proc __test_module__main_0_next<>(__state: bits[1], init={1}) { tuple.29: () = tuple(id=29) tuple.58: () = tuple(id=58) tuple.3: () = tuple(id=3) - next_value.14: () = next_value(param=__state, value=literal.8, predicate=and.10, id=14) + next_value.14: () = next_value(state_element=__state, value=literal.8, predicate=and.10, id=14) tuple.15: () = tuple(id=15) - next_value.27: () = next_value(param=__state, value=literal.16, predicate=and.18, id=27) + next_value.27: () = next_value(state_element=__state, value=literal.16, predicate=and.18, id=27) tuple.28: () = tuple(id=28) eq.31: bits[1] = eq(literal.30, val, id=31) - next_value.43: () = next_value(param=__state, value=literal.32, predicate=and.34, id=43) + next_value.43: () = next_value(state_element=__state, value=literal.32, predicate=and.34, id=43) tuple.44: () = tuple(id=44) assert.54: token = assert(assert.50, or.53, message="State element written after write in same activation.", id=54) or.55: bits[1] = or(or.42, and.47, id=55) - next_value.56: () = next_value(param=__state, value=literal.45, predicate=and.47, id=56) + next_value.56: () = next_value(state_element=__state, value=literal.45, predicate=and.47, id=56) tuple.57: () = tuple(id=57) priority_sel.60: () = priority_sel(concat.59, cases=[tuple.29], default=tuple.58, id=60) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleBranchingLabeledReads.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleBranchingLabeledReads.ir index 1b7f7b3827..ba8f63672b 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleBranchingLabeledReads.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleBranchingLabeledReads.ir @@ -42,15 +42,15 @@ top proc __test_module__main_0_next<>(__val: bits[32], __switch: bits[1], init={ or.46: bits[1] = or(not.45, literal.2, id=46) not.44: bits[1] = not(switch_val, id=44) tuple.3: () = tuple(id=3) - next_value.15: () = next_value(param=__val, value=add.10, predicate=and.11, label="EvenWrite", id=15) + next_value.15: () = next_value(state_element=__val, value=add.10, predicate=and.11, label="EvenWrite", id=15) tuple.16: () = tuple(id=16) or.24: bits[1] = or(and.6, and.19, id=24) or.39: bits[1] = or(and.11, and.31, id=39) - next_value.40: () = next_value(param=__val, value=add.29, predicate=and.31, label="OddWrite", id=40) + next_value.40: () = next_value(state_element=__val, value=add.29, predicate=and.31, label="OddWrite", id=40) tuple.41: () = tuple(id=41) sel.43: () = sel(switch_val, cases=[tuple.42, tuple.17], id=43) assert.47: token = assert(assert.38, or.46, message="State element written before read in same activation.", id=47) - next_value.48: () = next_value(param=__switch, value=not.44, predicate=literal.2, id=48) + next_value.48: () = next_value(state_element=__switch, value=not.44, predicate=literal.2, id=48) tuple.49: () = tuple(id=49) tuple.50: () = tuple(id=50) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStates.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStates.ir index ca827df6c7..e1f9e7e856 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStates.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStates.ir @@ -36,12 +36,12 @@ top proc __test_module__main_0_next<>(__state_0: (bits[32], bits[32]), __state_1 tuple.3: () = tuple(id=3) literal.13: bits[32] = literal(value=1, id=13) literal.17: bits[32] = literal(value=0, id=17) - next_value.27: () = next_value(param=__state_0, value=new_a, predicate=literal.2, id=27) + next_value.27: () = next_value(state_element=__state_0, value=new_a, predicate=literal.2, id=27) tuple.28: () = tuple(id=28) - next_value.32: () = next_value(param=__state_1, value=new_b, predicate=literal.2, id=32) + next_value.32: () = next_value(state_element=__state_1, value=new_b, predicate=literal.2, id=32) tuple.33: () = tuple(id=33) assert.38: token = assert(assert.31, or.37, message="State element written before read in same activation.", id=38) - next_value.39: () = next_value(param=__state_2, value=add.35, predicate=literal.2, id=39) + next_value.39: () = next_value(state_element=__state_2, value=add.35, predicate=literal.2, id=39) tuple.40: () = tuple(id=40) tuple.41: () = tuple(id=41) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStatesMultipleWrites.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStatesMultipleWrites.ir index c7734662f5..1bad303da9 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStatesMultipleWrites.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStatesMultipleWrites.ir @@ -22,11 +22,11 @@ top proc __test_module__main_0_next<>(__state_0: bits[32], __state_1: bits[32], or.21: bits[1] = or(not.19, not.20, id=21) add.15: bits[32] = add(a, literal.14, id=15) tuple.3: () = tuple(id=3) - next_value.12: () = next_value(param=__state_1, value=add.8, predicate=literal.2, id=12) + next_value.12: () = next_value(state_element=__state_1, value=add.8, predicate=literal.2, id=12) tuple.13: () = tuple(id=13) assert.22: token = assert(assert.18, or.21, message="State element written after write in same activation.", id=22) or.23: bits[1] = or(literal.2, literal.2, id=23) - next_value.24: () = next_value(param=__state_1, value=add.15, predicate=literal.2, id=24) + next_value.24: () = next_value(state_element=__state_1, value=add.15, predicate=literal.2, id=24) tuple.25: () = tuple(id=25) tuple.26: () = tuple(id=26) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStatesWriteBeforeRead.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStatesWriteBeforeRead.ir index 948e497744..6d0b2a19d6 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStatesWriteBeforeRead.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleStatesWriteBeforeRead.ir @@ -10,7 +10,7 @@ top proc __test_module__main_0_next<>(__state_0: bits[32], __state_1: bits[32], a: bits[32] = identity(__state_0, id=5) tuple.3: () = tuple(id=3) assert.7: token = assert(__token, not.6, message="State element written before read in same activation.", id=7) - next_value.8: () = next_value(param=__state_1, value=a, predicate=literal.2, id=8) + next_value.8: () = next_value(state_element=__state_1, value=a, predicate=literal.2, id=8) tuple.9: () = tuple(id=9) tuple.10: () = tuple(id=10) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleWrites.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleWrites.ir index d84a108cb6..d09a8183ae 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleWrites.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessMultipleWrites.ir @@ -21,11 +21,11 @@ top proc __test_module__main_0_next<>(__state: bits[32], init={0}) { or.20: bits[1] = or(not.18, not.19, id=20) accum__1: bits[32] = add(accum, literal.13, id=14) tuple.3: () = tuple(id=3) - next_value.11: () = next_value(param=__state, value=accum, predicate=literal.2, id=11) + next_value.11: () = next_value(state_element=__state, value=accum, predicate=literal.2, id=11) tuple.12: () = tuple(id=12) assert.21: token = assert(assert.17, or.20, message="State element written after write in same activation.", id=21) or.22: bits[1] = or(literal.2, literal.2, id=22) - next_value.23: () = next_value(param=__state, value=accum__1, predicate=literal.2, id=23) + next_value.23: () = next_value(state_element=__state, value=accum__1, predicate=literal.2, id=23) tuple.24: () = tuple(id=24) tuple.25: () = tuple(id=25) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessReadWithLabeledRead.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessReadWithLabeledRead.ir index cd1fea41be..2aec8fa163 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessReadWithLabeledRead.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessReadWithLabeledRead.ir @@ -22,7 +22,7 @@ top proc __test_module__main_0_next<>(__state: bits[32], init={0}) { tuple.3: () = tuple(id=3) or.10: bits[1] = or(literal.2, literal.2, id=10) assert.19: token = assert(assert.9, or.18, message="State element written before read in same activation.", id=19) - next_value.20: () = next_value(param=__state, value=y, predicate=literal.2, label="main_write", id=20) + next_value.20: () = next_value(state_element=__state, value=y, predicate=literal.2, label="main_write", id=20) tuple.21: () = tuple(id=21) tuple.22: () = tuple(id=22) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessS32.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessS32.ir index 7365ca13a7..ffa6ff638d 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessS32.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessS32.ir @@ -13,7 +13,7 @@ top proc __test_module__Counter_0_next<>(__state: bits[32], init={4294967291}) { add.7: bits[32] = add(current, literal.6, id=7) tuple.3: () = tuple(id=3) assert.10: token = assert(__token, or.9, message="State element written before read in same activation.", id=10) - next_value.11: () = next_value(param=__state, value=add.7, predicate=literal.2, id=11) + next_value.11: () = next_value(state_element=__state, value=add.7, predicate=literal.2, id=11) tuple.12: () = tuple(id=12) tuple.13: () = tuple(id=13) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessString.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessString.ir index 7b15cbad5e..38ea3dbb2d 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessString.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessString.ir @@ -12,7 +12,7 @@ top proc __test_module__String_0_next<>(__state: bits[8][5], init={[104, 101, 10 tuple.3: () = tuple(id=3) current: bits[8][5] = identity(__state, id=5) assert.9: token = assert(__token, or.8, message="State element written before read in same activation.", id=9) - next_value.10: () = next_value(param=__state, value=literal.6, predicate=literal.2, id=10) + next_value.10: () = next_value(state_element=__state, value=literal.6, predicate=literal.2, id=10) tuple.11: () = tuple(id=11) tuple.12: () = tuple(id=12) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessStruct.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessStruct.ir index 269c4f14cf..14e32a998f 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessStruct.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessStruct.ir @@ -18,6 +18,6 @@ top proc __test_module__Struct_0_next<>(__state: (bits[32], bits[32]), init={(0, shift: (bits[32], bits[32]) = tuple(add.8, add.11, id=12) tuple.3: () = tuple(id=3) assert.15: token = assert(__token, or.14, message="State element written before read in same activation.", id=15) - next_value.16: () = next_value(param=__state, value=shift, predicate=literal.2, id=16) + next_value.16: () = next_value(state_element=__state, value=shift, predicate=literal.2, id=16) tuple.17: () = tuple(id=17) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessU32.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessU32.ir index 301564b35f..56eee15288 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessU32.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessU32.ir @@ -13,7 +13,7 @@ top proc __test_module__Counter_0_next<>(__state: bits[32], init={0}) { y: bits[32] = add(x, literal.6, id=7) tuple.3: () = tuple(id=3) assert.10: token = assert(__token, or.9, message="State element written before read in same activation.", id=10) - next_value.11: () = next_value(param=__state, value=y, predicate=literal.2, id=11) + next_value.11: () = next_value(state_element=__state, value=y, predicate=literal.2, id=11) tuple.12: () = tuple(id=12) tuple.13: () = tuple(id=13) } diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessWriteBeforeRead.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessWriteBeforeRead.ir index cd75f578a5..eacf9b2111 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessWriteBeforeRead.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_ExplicitStateAccessWriteBeforeRead.ir @@ -9,7 +9,7 @@ top proc __test_module__main_0_next<>(__state: bits[32], init={0}) { literal.4: bits[32] = literal(value=1, id=4) tuple.3: () = tuple(id=3) assert.6: token = assert(__token, not.5, message="State element written before read in same activation.", id=6) - next_value.7: () = next_value(param=__state, value=literal.4, predicate=literal.2, id=7) + next_value.7: () = next_value(state_element=__state, value=literal.4, predicate=literal.2, id=7) tuple.8: () = tuple(id=8) tuple.9: () = tuple(id=9) } diff --git a/xls/interpreter/proc_evaluator_test_base.cc b/xls/interpreter/proc_evaluator_test_base.cc index cdb40f7e79..7d89b82b7a 100644 --- a/xls/interpreter/proc_evaluator_test_base.cc +++ b/xls/interpreter/proc_evaluator_test_base.cc @@ -780,6 +780,34 @@ TEST_P(ProcEvaluatorTestBase, ConditionalNextProc) { EXPECT_THAT(queue.Read(), Optional(Value(UBits(2, 32)))); } +TEST_P(ProcEvaluatorTestBase, DecoupledNextProc) { + Package package(TestName()); + ProcBuilder pb("dec_next", &package); + XLS_ASSERT_OK_AND_ASSIGN( + StateElement * se, + pb.UnreadStateElement("state_element", Value(UBits(42, 32)), + /*non_synthesizable=*/false)); + pb.StateRead(se); + pb.Next(se, pb.Literal(UBits(56, 32))); + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc, pb.Build()); + XLS_ASSERT_OK(package.SetTop(proc)); + + std::unique_ptr queue_manager = + GetParam().CreateQueueManager(&package); + std::unique_ptr evaluator = + GetParam().CreateEvaluator(proc, queue_manager.get()); + + std::unique_ptr continuation = evaluator->NewContinuation( + queue_manager->elaboration().GetUniqueInstance(proc).value()); + + EXPECT_THAT(continuation->GetState(), ElementsAre(Value(UBits(42, 32)))); + EXPECT_THAT( + evaluator->Tick(*continuation), + IsOkAndHolds(TickResult{.execution_state = TickExecutionState::kCompleted, + .channel_instance = std::nullopt, + .progress_made = true})); + EXPECT_THAT(continuation->GetState(), ElementsAre(Value(UBits(56, 32)))); +} TEST_P(ProcEvaluatorTestBase, CollidingNextValuesProc) { // Create an output-only proc which increments its counter value only every // other iteration - but also tries to set the counter value to a different diff --git a/xls/interpreter/proc_interpreter.cc b/xls/interpreter/proc_interpreter.cc index 8359e17a92..4175d78fb4 100644 --- a/xls/interpreter/proc_interpreter.cc +++ b/xls/interpreter/proc_interpreter.cc @@ -212,7 +212,10 @@ class ProcIrInterpreter : public IrInterpreter { return SetValueResult(next, Value::Tuple({})); } } - (*active_next_values_)[next->state_read()->As()->state_element()] + (*active_next_values_)[next->has_state_read() ? next->state_read() + ->As() + ->state_element() + : next->state_element()] .push_back(next); return SetValueResult(next, Value::Tuple({})); } diff --git a/xls/ir/ir_parser.cc b/xls/ir/ir_parser.cc index fce222be0e..cfbfff05a5 100644 --- a/xls/ir/ir_parser.cc +++ b/xls/ir/ir_parser.cc @@ -858,7 +858,10 @@ absl::StatusOr Parser::ParseNode( GetEffectiveProcOrError(fb, "next operations only supported in procs", op_token.pos()) .status()); - BValue* state_read = arg_parser.AddKeywordArg("param"); + std::optional* state_read = + arg_parser.AddOptionalKeywordArg("param"); + std::optional* state_element = + arg_parser.AddOptionalKeywordArg("state_element"); BValue* value = arg_parser.AddKeywordArg("value"); std::optional* predicate = arg_parser.AddOptionalKeywordArg("predicate"); @@ -869,8 +872,32 @@ absl::StatusOr Parser::ParseNode( label_string = label->value().value; } XLS_ASSIGN_OR_RETURN(operands, arg_parser.Run(/*arity=*/0)); - bvalue = fb->Next(*state_read, *value, *predicate, label_string, *loc, - node_name); + if (state_read->has_value() && state_element->has_value()) { + return absl::InvalidArgumentError( + "next_value cannot specify both 'param' (StateRead) and " + "'state_element'"); + } + if (!state_read->has_value() && !state_element->has_value()) { + return absl::InvalidArgumentError( + "next_value must specify either 'param' (StateRead) or " + "'state_element'"); + } + if (state_read->has_value()) { + bvalue = fb->Next(state_read->value(), *value, *predicate, label_string, + *loc, node_name); + } else { + Proc* proc = fb->function()->AsProcOrDie(); + if (!proc->HasStateElement(state_element->value().value)) { + return absl::InvalidArgumentError(absl::StrFormat( + "State element '%s' not found for next_value @ %s", + state_element->value().value, op_token.pos().ToHumanString())); + } + XLS_ASSIGN_OR_RETURN( + StateElement * state_element, + proc->GetStateElementByName(state_element->value().value)); + bvalue = fb->Next(state_element, *value, *predicate, label_string, *loc, + node_name); + } break; } case Op::kCountedFor: { diff --git a/xls/ir/ir_parser_test.cc b/xls/ir/ir_parser_test.cc index 8430622b86..0da72e07b4 100644 --- a/xls/ir/ir_parser_test.cc +++ b/xls/ir/ir_parser_test.cc @@ -619,6 +619,27 @@ proc foo( x: bits[32], y: (), z: bits[32], init={42, (), 123}) { EXPECT_THAT(reads.front()->predicate(), std::nullopt); } +TEST(IrParserTest, DecoupledNextProc) { + std::string program = R"( +package test + +proc decoupled_proc(__state: bits[32], init={0}) { + sr: bits[32] = state_read(state_element=__state) + one: bits[32] = literal(value=1) + add: bits[32] = add(sr, one) + next_state: () = next_value(state_element=__state, value=add) +} +)"; + XLS_ASSERT_OK_AND_ASSIGN(auto package, Parser::ParsePackage(program)); + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc, package->GetProc("decoupled_proc")); + EXPECT_EQ(proc->GetStateElementCount(), 1); + XLS_ASSERT_OK_AND_ASSIGN(StateElement * state, + proc->GetStateElementByName("__state")); + absl::Span reads = proc->GetStateReadsByStateElement(state); + ASSERT_EQ(reads.size(), 1); + EXPECT_THAT(reads.front()->predicate(), std::nullopt); +} + TEST(IrParserTest, ProcWithPredicatedStateRead) { std::string program = R"( package test diff --git a/xls/ir/node.cc b/xls/ir/node.cc index f652fe1a5e..af287b23e3 100644 --- a/xls/ir/node.cc +++ b/xls/ir/node.cc @@ -641,10 +641,12 @@ std::string Node::ToStringInternal(bool include_operand_types) const { } case Op::kNext: { const Next* next = As(); - std::string param_name = next->has_state_read() - ? next->state_read()->GetName() - : next->state_element()->name(); - args = {absl::StrFormat("param=%s", param_name), + std::string target_arg = + next->has_state_read() + ? absl::StrFormat("param=%s", next->state_read()->GetName()) + : absl::StrFormat("state_element=%s", + next->state_element()->name()); + args = {target_arg, absl::StrFormat("value=%s", next->value()->GetName())}; std::optional predicate = next->predicate(); if (predicate.has_value()) { diff --git a/xls/ir/nodes.cc b/xls/ir/nodes.cc index acb4447d71..ec21bfc2d2 100755 --- a/xls/ir/nodes.cc +++ b/xls/ir/nodes.cc @@ -985,9 +985,16 @@ bool StateRead::IsDefinitelyEqualTo(const Node* other) const { std::vector StateRead::GetNextValues() const { std::vector next_values; - for (Node* user : users()) { - if (user->Is() && user->As()->state_read() == this) { - next_values.push_back(user->As()); + if (function_base()->AsProcOrDie()->uses_decoupled_next()) { + const auto& next_values_se = + function_base()->AsProcOrDie()->next_values(state_element_); + next_values.insert(next_values.end(), next_values_se.begin(), + next_values_se.end()); + } else { + for (Node* user : users()) { + if (user->Is() && user->As()->state_read() == this) { + next_values.push_back(user->As()); + } } } return next_values; diff --git a/xls/ir/proc_test.cc b/xls/ir/proc_test.cc index a8df186c98..45f709e871 100644 --- a/xls/ir/proc_test.cc +++ b/xls/ir/proc_test.cc @@ -288,6 +288,41 @@ TEST_F(ProcTest, RemoveStateThatStillHasUse) { HasSubstr("state read st has uses"))); } +TEST_F(ProcTest, GetNextStateReadDecoupled) { + auto p = CreatePackage(); + TokenlessProcBuilder pb(TestName(), "tkn", p.get()); + XLS_ASSERT_OK_AND_ASSIGN(StateElement * state_elem, + pb.UnreadStateElement("x", Value(UBits(42, 32)), + /*non_synthesizable=*/false)); + BValue read = pb.StateRead(state_elem); + BValue next_val = pb.Add(read, pb.Literal(Value(UBits(1, 32)))); + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc, pb.Build({})); + + StateRead* state_read = proc->GetStateReadByStateElement(state_elem); + EXPECT_EQ(state_read->GetNextValues().size(), 0); + + XLS_ASSERT_OK(proc->MakeNodeWithName(SourceInfo(), state_elem, + next_val.node(), + /*predicate=*/std::nullopt, + /*label=*/std::nullopt, "x_next") + .status()); + EXPECT_EQ(state_read->GetNextValues().size(), 1); + + XLS_ASSERT_OK_AND_ASSIGN( + Literal * next_value, + proc->MakeNode(SourceInfo(), Value(UBits(43, 32)))); + XLS_ASSERT_OK( + proc->MakeNodeWithName(SourceInfo(), state_elem, next_value, + /*predicate=*/std::nullopt, + /*label=*/std::nullopt, "second_next") + .status()); + EXPECT_EQ(state_read->GetNextValues().size(), 2); + EXPECT_THAT(state_read->GetNextValues(), + UnorderedElementsAre( + m::NextWithStateElement(state_elem, m::Add()), + m::NextWithStateElement(state_elem, m::Literal(43)))); +} + TEST_F(ProcTest, InsertStateElementDecoupled) { auto p = CreatePackage(); ProcBuilder pb("p", p.get()); diff --git a/xls/jit/function_base_jit.cc b/xls/jit/function_base_jit.cc index 212cb5f3f2..95326f38e3 100644 --- a/xls/jit/function_base_jit.cc +++ b/xls/jit/function_base_jit.cc @@ -593,7 +593,13 @@ absl::StatusOr BuildPartitionFunction( if (node->Is()) { // next_value nodes store their output in the state-read's location, and // return nothing themselves. - StateRead* state_read = node->As()->state_read()->As(); + StateRead* state_read = + node->As()->has_state_read() + ? node->As()->state_read()->As() + : node->function_base() + ->AsProcOrDie() + ->GetStateReadByStateElement( + node->As()->state_element()); XLS_RET_CHECK(allocator.GetAllocationKind(state_read) == AllocationKind::kNone); output_buffers = wrapper.GetOutputBuffers(state_read, b); diff --git a/xls/jit/ir_builder_visitor.cc b/xls/jit/ir_builder_visitor.cc index 41692e8739..1bd42a0511 100644 --- a/xls/jit/ir_builder_visitor.cc +++ b/xls/jit/ir_builder_visitor.cc @@ -670,9 +670,13 @@ absl::Status InvokeAssertCallback(llvm::IRBuilder<>* builder, absl::Status InvokeNextValueCallback(llvm::IRBuilder<>* builder, Next* next, llvm::Value* instance_ctx) { llvm::Type* void_type = llvm::Type::getVoidTy(builder->getContext()); + StateElement* state_element = + next->has_state_read() + ? next->state_read()->As()->state_element() + : next->state_element(); llvm::Value* state_element_idx = builder->getInt64( *next->function_base()->AsProcOrDie()->GetStateElementIndex( - next->state_read()->As()->state_element())); + state_element)); llvm::Value* next_value = builder->getInt64(next->id()); InvokeCallback( builder, void_type, instance_ctx, {state_element_idx, next_value}); @@ -2851,7 +2855,11 @@ absl::Status IrBuilderVisitor::HandleNeg(UnOp* neg) { } absl::Status IrBuilderVisitor::HandleNext(Next* next) { - std::vector param_names({"param", "value"}); + std::vector param_names; + if (next->has_state_read()) { + param_names.push_back("param"); + } + param_names.push_back("value"); if (next->predicate().has_value()) { param_names.push_back("predicate"); } @@ -2860,7 +2868,8 @@ absl::Status IrBuilderVisitor::HandleNext(Next* next) { NewNodeIrContext(next, param_names, /*include_wrapper_args=*/true)); llvm::IRBuilder<>& b = node_context.entry_builder(); - llvm::Value* value_ptr = node_context.GetOperandPtr(Next::kValueOperand); + llvm::Value* value_ptr = + node_context.GetOperandPtr(next->value_operand_number()); if (!next->predicate().has_value()) { LlvmMemcpy(node_context.GetOutputPtr(0), value_ptr, @@ -2875,7 +2884,8 @@ absl::Status IrBuilderVisitor::HandleNext(Next* next) { } // If the predicate is true, emulate the `next_value` node's effects. - llvm::Value* predicate = Truthiness(node_context.LoadOperand(2), b); + llvm::Value* predicate = Truthiness( + node_context.LoadOperand(next->predicate_operand_number().value()), b); LlvmIfThen if_then = CreateIfThen(predicate, b, next->GetName()); LlvmMemcpy(node_context.GetOutputPtr(0), value_ptr, diff --git a/xls/passes/proc_state_narrowing_pass.cc b/xls/passes/proc_state_narrowing_pass.cc index e0a6d32111..009e0fcedf 100644 --- a/xls/passes/proc_state_narrowing_pass.cc +++ b/xls/passes/proc_state_narrowing_pass.cc @@ -59,7 +59,9 @@ struct ProcStateNarrowTransform : public Proc::StateElementTransformer { Next* old_next) final { XLS_RET_CHECK_EQ( new_state_read->GetType()->GetFlatBitCount() + known_leading_, - old_next->state_read()->GetType()->GetFlatBitCount()); + proc->GetStateReadByStateElement(old_next->state_element()) + ->GetType() + ->GetFlatBitCount()); return proc->MakeNodeWithName( old_next->loc(), old_next->value(), /*start=*/0, /*width=*/new_state_read->GetType()->GetFlatBitCount(), diff --git a/xls/passes/proc_state_narrowing_pass_test.cc b/xls/passes/proc_state_narrowing_pass_test.cc index 2bf858e732..aad25e4bb2 100644 --- a/xls/passes/proc_state_narrowing_pass_test.cc +++ b/xls/passes/proc_state_narrowing_pass_test.cc @@ -164,6 +164,40 @@ TEST_F(ProcStateNarrowingPassTest, BasicLoop) { EXPECT_THAT(proc->StateElements(), UnorderedElementsAre(m::StateElement( "the_state", p->GetBitsType(3)))); } + +TEST_F(ProcStateNarrowingPassTest, BasicLoopDecoupledNext) { + auto p = CreatePackage(); + XLS_ASSERT_OK_AND_ASSIGN( + auto* chan, p->CreateStreamingChannel("test_chan", ChannelOps::kSendOnly, + p->GetBitsType(32))); + ProcBuilder pb(TestName(), p.get()); + XLS_ASSERT_OK_AND_ASSIGN( + auto* state_element, + pb.UnreadStateElement("the_state", Value(UBits(1, 32)), + /*non_synthesizable=*/false)); + BValue state = pb.StateRead(state_element); + // State just counts up 1 to 6 then resets to 1. + // NB Limit is exactly 6 and comparison is LT so that however the transform is + // done the state fits in 3 bits. + auto in_loop = pb.ULt(state, pb.Literal(UBits(6, 32))); + pb.Send(chan, pb.Literal(Value::Token()), state); + pb.Next(state, pb.Add(state, pb.Literal(UBits(1, 32))), in_loop); + // Reset value is intentionally not something that could be removed by + // exploiting overflow + pb.Next(state, pb.Literal(UBits(1, 32)), pb.Not(in_loop)); + + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc, pb.Build()); + + solvers::ScopedVerifyProcEquivalence svpe(proc, /*activation_count=*/16, + /*include_state=*/false); + ScopedRecordIr sri(p.get()); + EXPECT_THAT(RunPass(proc), IsOkAndHolds(true)); + EXPECT_THAT(RunProcStateCleanup(proc), IsOkAndHolds(true)); + + EXPECT_THAT(proc->StateElements(), UnorderedElementsAre(m::StateElement( + "the_state", p->GetBitsType(3)))); +} + TEST_F(ProcStateNarrowingPassTest, BasicHalt) { auto p = CreatePackage(); XLS_ASSERT_OK_AND_ASSIGN( diff --git a/xls/passes/proc_state_range_query_engine.cc b/xls/passes/proc_state_range_query_engine.cc index f65623a14f..37651fcee4 100644 --- a/xls/passes/proc_state_range_query_engine.cc +++ b/xls/passes/proc_state_range_query_engine.cc @@ -539,7 +539,7 @@ absl::StatusOr> NarrowUsingSegments( for (Next* n : proc->next_values(state_element)) { // Nexts which don't update anything (either due to just being passthrough // or having a known-false predicate) don't need to be taken into account. - if (n->value() == n->state_read() || + if (n->value() == proc->GetStateReadByStateElement(state_element) || (n->predicate() && piqe.IsAllZeros(*n->predicate()))) { continue; } @@ -606,7 +606,7 @@ FindContextualRanges(Proc* proc, const QueryEngine& qe, // TODO(allight): We might want to use data-flow to better track whether // things have changed. This should probably be good enough in practice // however. - if (n->state_read() != n->value()) { + if (proc->GetStateReadByStateElement(state_element) != n->value()) { nexts.push_back(n); } } diff --git a/xls/passes/proc_state_range_query_engine_test.cc b/xls/passes/proc_state_range_query_engine_test.cc index cb5a57479e..5b262b22fb 100644 --- a/xls/passes/proc_state_range_query_engine_test.cc +++ b/xls/passes/proc_state_range_query_engine_test.cc @@ -76,6 +76,37 @@ TEST_F(ProcStateRangeQueryEngineTest, Negatives) { "[[0, 14]]"); } +TEST_F(ProcStateRangeQueryEngineTest, NegativesDecoupledNext) { + auto p = CreatePackage(); + ProcBuilder fb(TestName(), p.get()); + + XLS_ASSERT_OK_AND_ASSIGN(StateElement * state_element, + fb.UnreadStateElement("foo", Value(SBits(0, 32)), + /*non_synthesizable=*/false)); + + BValue st = fb.StateRead(state_element); + BValue res = fb.Add(st, fb.Literal(UBits(3, 32))); + BValue res_pos = fb.Add(st, fb.Literal(UBits(7, 32))); + + // Count -7 to 7 + auto in_loop = fb.SLt(st, fb.Literal(UBits(7, 32))); + fb.Next(state_element, fb.Add(st, fb.Literal(UBits(1, 32))), in_loop); + // If we aren't looping the value goes to -7 + fb.Next(state_element, fb.Literal(SBits(-7, 32)), fb.Not(in_loop)); + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc, fb.Build()); + + ProcStateRangeQueryEngine qe; + XLS_ASSERT_OK(qe.Populate(proc).status()); + EXPECT_EQ( + IntervalSetTreeToString(qe.GetIntervals(st.node())), + absl::StrFormat("[[0, 7], [%v, %v]]", SBits(-7, 32), SBits(-1, 32))); + EXPECT_EQ( + IntervalSetTreeToString(qe.GetIntervals(res.node())), + absl::StrFormat("[[0, 10], [%v, %v]]", SBits(-7 + 3, 32), SBits(-1, 32))); + EXPECT_EQ(IntervalSetTreeToString(qe.GetIntervals(res_pos.node())), + "[[0, 14]]"); +} + TEST_F(ProcStateRangeQueryEngineTest, NegativesWithEq) { auto p = CreatePackage(); ProcBuilder fb(TestName(), p.get()); diff --git a/xls/scheduling/pipeline_schedule.cc b/xls/scheduling/pipeline_schedule.cc index 3e0111a2f0..43fed07797 100644 --- a/xls/scheduling/pipeline_schedule.cc +++ b/xls/scheduling/pipeline_schedule.cc @@ -207,7 +207,8 @@ bool PipelineSchedule::IsLiveOutOfCycle(Node* node, int64_t c) const { } if (user->Is()) { Next* user_next = user->As(); - if (user_next->predicate() != node && user_next->value() != node) { + if (user_next->predicate() != node && user_next->value() != node && + user_next->has_state_read()) { CHECK_EQ(user_next->state_read(), node); // This Next node only uses this StateRead node to target the state // register it needs to write to; it doesn't actually need the value diff --git a/xls/scheduling/pipeline_schedule_test.cc b/xls/scheduling/pipeline_schedule_test.cc index 350c9b8f04..36c6e4e502 100644 --- a/xls/scheduling/pipeline_schedule_test.cc +++ b/xls/scheduling/pipeline_schedule_test.cc @@ -2372,6 +2372,44 @@ TEST_P(PipelineScheduleTest, ProcWithMultipleStateReads) { EXPECT_EQ(schedule.length(), 1); } +TEST_P(PipelineScheduleTest, ProcDecoupledFindMinimumCaseThroughput) { + Package p(TestName()); + Type* u32 = p.GetBitsType(32); + XLS_ASSERT_OK_AND_ASSIGN( + Channel * out_ch, + p.CreateStreamingChannel("out_ch", ChannelOps::kSendOnly, u32)); + + TokenlessProcBuilder pb("the_proc", "tkn", &p); + XLS_ASSERT_OK_AND_ASSIGN(StateElement * se, + pb.UnreadStateElement("state", Value(UBits(0, 32)), + /*non_synthesizable=*/false)); + BValue current = pb.StateRead(se); + BValue first_add = pb.Add(current, pb.Literal(UBits(1, 32))); + BValue second_add = pb.Add(first_add, pb.Literal(UBits(2, 32))); + BValue third_add = pb.Add(second_add, pb.Literal(UBits(3, 32))); + + pb.Send(out_ch, third_add); + BValue next = pb.Next(se, third_add); + + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc, pb.Build()); + + SchedulingOptions options = this->options(); + options.clock_period_ps(1); + options.minimize_worst_case_throughput(true); + + XLS_ASSERT_OK_AND_ASSIGN( + PipelineSchedule schedule, + RunPipelineSchedule(proc, TestDelayEstimator(), options)); + EXPECT_EQ(schedule.cycle(current.node()), 0); + EXPECT_EQ(schedule.cycle(next.node()), 2); + + ASSERT_TRUE(proc->GetInitiationInterval().has_value()); + EXPECT_EQ(proc->GetInitiationInterval().value(), 3); + + EXPECT_LE(schedule.cycle(next.node()) - schedule.cycle(current.node()), + *proc->GetInitiationInterval() - 1); +} + TEST_P(PipelineScheduleErrorTest, ProcWithZeroReadsErrors) { Package p(TestName()); TokenlessProcBuilder pb("the_proc", "tkn", &p); @@ -2816,7 +2854,36 @@ TEST_F(PipelineScheduleTest, EXPECT_EQ(schedule.cycle(next.node()), 1); EXPECT_EQ(schedule.cycle(read_mutually_exclusive.node()), 2); } +TEST_F(PipelineScheduleTest, DecoupledNextIsOutOfCycle) { + Package p(TestName()); + ProcBuilder pb("the_proc", &p); + XLS_ASSERT_OK_AND_ASSIGN(StateElement * se, + pb.UnreadStateElement("state", Value(UBits(0, 32)), + /*non_synthesizable=*/false)); + BValue read = pb.StateRead(se); + BValue next = pb.Next(se, read); + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc, pb.Build()); + + // Run scheduler to get a valid base schedule first. + SchedulingOptions options; + options.clock_period_ps(1); + options.pipeline_stages(2); + XLS_ASSERT_OK_AND_ASSIGN( + PipelineSchedule schedule, + RunPipelineSchedule(proc, TestDelayEstimator(), options)); + ScheduleCycleMap cycle_map = schedule.GetCycleMap(); + cycle_map[read.node()] = 0; + cycle_map[next.node()] = 1; + XLS_ASSERT_OK_AND_ASSIGN(schedule, PipelineSchedule::Create(proc, cycle_map)); + EXPECT_EQ(schedule.cycle(read.node()), 0); + EXPECT_EQ(schedule.cycle(next.node()), 1); + // Read is consumed by Next in cycle 1, so read IS live out of cycle 0 + EXPECT_TRUE(schedule.IsLiveOutOfCycle(read.node(), 0)); + // By cycle 1 (where Next is), read no longer requires a pipeline register + // and isn't live out of cycle 1. + EXPECT_FALSE(schedule.IsLiveOutOfCycle(read.node(), 1)); +} TEST_F(PipelineScheduleTest, ProcWriteBeforeReadFailsVerification) { Package p(TestName()); ProcBuilder pb("the_proc", &p); diff --git a/xls/scheduling/run_pipeline_schedule.cc b/xls/scheduling/run_pipeline_schedule.cc index 094ef63029..e4ccef535b 100644 --- a/xls/scheduling/run_pipeline_schedule.cc +++ b/xls/scheduling/run_pipeline_schedule.cc @@ -232,7 +232,10 @@ absl::StatusOr FindMinimumWorstCaseThroughput( // Extract the worst-case throughput from this schedule as an upper bound. int64_t pessimistic_worst_case_throughput = 1; for (Next* next : proc->next_values()) { - Node* state_read = next->state_read(); + Node* state_read = + next->has_state_read() + ? next->state_read() + : proc->GetStateReadByStateElement(next->state_element()); const int64_t backedge_length = schedule_cycle_map[next] - schedule_cycle_map[state_read]; pessimistic_worst_case_throughput =