From cf8373644768d0addb959400585efedfb2460268 Mon Sep 17 00:00:00 2001 From: Eric Astor Date: Fri, 26 Dec 2025 06:42:03 -0800 Subject: [PATCH] [codegen 1.5] Enable the simplest proc test for new block conversion This required small patches to channel & state lowering, as well as to how we transfer Nodes into ScheduledBlocks: 1. Channel lowering now handles single-value channnels without crashing in some circumstances. (Whoops.) 2. State lowering now cleans up after itself. 3. ScheduledBlock conversion now takes ownership of the node name as well as the node. We also add dataflow simplification to the pipeline (matching codegen 1.0). PiperOrigin-RevId: 849163225 --- xls/codegen_v_1_5/BUILD | 65 ++++++ .../block_conversion_pass_pipeline.cc | 21 +- .../block_conversion_pass_pipeline.h | 5 +- .../block_conversion_pass_pipeline_test.cc | 62 ++++-- .../block_conversion_wrapper_pass.cc | 32 +++ .../block_conversion_wrapper_pass.h | 74 +++++++ .../block_conversion_wrapper_pass_test.cc | 76 +++++++ .../channel_to_port_io_lowering_pass.cc | 61 +++++- .../channel_to_port_io_lowering_pass_test.cc | 145 ++++++++++++++ xls/codegen_v_1_5/convert_to_block.cc | 4 +- .../flow_control_insertion_pass.cc | 28 +++ .../function_io_lowering_pass.cc | 11 +- .../function_io_lowering_pass_test.cc | 31 ++- xls/codegen_v_1_5/register_cleanup_pass.cc | 188 ++++++++++++++++++ xls/codegen_v_1_5/register_cleanup_pass.h | 61 ++++++ .../state_to_register_io_lowering_pass.cc | 154 +++++++------- xls/ir/BUILD | 1 + xls/ir/function_base.cc | 6 + xls/ir/function_base.h | 4 +- xls/ir/name_uniquer.cc | 10 + xls/ir/name_uniquer.h | 12 ++ xls/ir/proc.cc | 4 + 22 files changed, 936 insertions(+), 119 deletions(-) create mode 100644 xls/codegen_v_1_5/block_conversion_wrapper_pass.cc create mode 100644 xls/codegen_v_1_5/block_conversion_wrapper_pass.h create mode 100644 xls/codegen_v_1_5/block_conversion_wrapper_pass_test.cc create mode 100644 xls/codegen_v_1_5/register_cleanup_pass.cc create mode 100644 xls/codegen_v_1_5/register_cleanup_pass.h diff --git a/xls/codegen_v_1_5/BUILD b/xls/codegen_v_1_5/BUILD index 2cceef3c91..5eb74e6a0f 100644 --- a/xls/codegen_v_1_5/BUILD +++ b/xls/codegen_v_1_5/BUILD @@ -41,15 +41,20 @@ cc_library( hdrs = ["block_conversion_pass_pipeline.h"], deps = [ ":block_conversion_pass", + ":block_conversion_wrapper_pass", ":block_finalization_pass", ":channel_to_port_io_lowering_pass", ":flow_control_insertion_pass", ":function_io_lowering_pass", ":idle_insertion_pass", ":pipeline_register_insertion_pass", + ":register_cleanup_pass", ":scheduled_block_conversion_pass", ":scheduling_pass", ":state_to_register_io_lowering_pass", + "//xls/passes:dataflow_simplification_pass", + "//xls/passes:dce_pass", + "//xls/passes:optimization_pass", ], ) @@ -93,6 +98,65 @@ cc_test( ], ) +cc_library( + name = "register_cleanup_pass", + srcs = ["register_cleanup_pass.cc"], + hdrs = ["register_cleanup_pass.h"], + deps = [ + ":block_conversion_pass", + "//xls/common/status:status_macros", + "//xls/data_structures:transitive_closure", + "//xls/ir", + "//xls/ir:register", + "//xls/ir:value", + "//xls/ir:value_utils", + "//xls/passes:node_dependency_analysis", + "//xls/passes:partial_info_query_engine", + "//xls/passes:pass_base", + "//xls/passes:query_engine", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/types:span", + ], +) + +cc_library( + name = "block_conversion_wrapper_pass", + srcs = ["block_conversion_wrapper_pass.cc"], + hdrs = ["block_conversion_wrapper_pass.h"], + deps = [ + ":block_conversion_pass", + "//xls/ir", + "//xls/passes:optimization_pass", + "//xls/passes:pass_base", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", + ], +) + +cc_test( + name = "block_conversion_wrapper_pass_test", + srcs = ["block_conversion_wrapper_pass_test.cc"], + deps = [ + ":block_conversion_pass", + ":block_conversion_wrapper_pass", + "//xls/common:xls_gunit_main", + "//xls/common/status:matchers", + "//xls/ir", + "//xls/ir:function_builder", + "//xls/ir:ir_matcher", + "//xls/ir:ir_test_base", + "//xls/passes:dce_pass", + "//xls/passes:optimization_pass", + "//xls/passes:pass_base", + "@com_google_absl//absl/status:statusor", + "@googletest//:gtest", + ], +) + cc_library( name = "channel_to_port_io_lowering_pass", srcs = ["channel_to_port_io_lowering_pass.cc"], @@ -170,6 +234,7 @@ cc_library( "//xls/common/status:status_macros", "//xls/estimators/delay_model:delay_estimator", "//xls/ir", + "//xls/passes:optimization_pass", "//xls/passes:pass_base", "//xls/scheduling:pipeline_schedule_cc_proto", "//xls/scheduling:scheduling_options", diff --git a/xls/codegen_v_1_5/block_conversion_pass_pipeline.cc b/xls/codegen_v_1_5/block_conversion_pass_pipeline.cc index ea3f42c842..52d6eaa968 100644 --- a/xls/codegen_v_1_5/block_conversion_pass_pipeline.cc +++ b/xls/codegen_v_1_5/block_conversion_pass_pipeline.cc @@ -20,20 +20,25 @@ #include #include "xls/codegen_v_1_5/block_conversion_pass.h" +#include "xls/codegen_v_1_5/block_conversion_wrapper_pass.h" #include "xls/codegen_v_1_5/block_finalization_pass.h" #include "xls/codegen_v_1_5/channel_to_port_io_lowering_pass.h" #include "xls/codegen_v_1_5/flow_control_insertion_pass.h" #include "xls/codegen_v_1_5/function_io_lowering_pass.h" #include "xls/codegen_v_1_5/idle_insertion_pass.h" #include "xls/codegen_v_1_5/pipeline_register_insertion_pass.h" +#include "xls/codegen_v_1_5/register_cleanup_pass.h" #include "xls/codegen_v_1_5/scheduled_block_conversion_pass.h" #include "xls/codegen_v_1_5/scheduling_pass.h" #include "xls/codegen_v_1_5/state_to_register_io_lowering_pass.h" +#include "xls/passes/dataflow_simplification_pass.h" +#include "xls/passes/dce_pass.h" +#include "xls/passes/optimization_pass.h" namespace xls::codegen { -std::unique_ptr -CreateBlockConversionPassPipeline() { +std::unique_ptr CreateBlockConversionPassPipeline( + OptimizationContext& opt_context) { auto top = std::make_unique( "block_conversion", "Top level codegen v1.5 block conversion pipeline"); @@ -64,6 +69,18 @@ CreateBlockConversionPassPipeline() { // Lower scheduled block to standard block, inlining each stage. top->Add(); + // Clean up unused registers & load-enable bits (including flow-control + // registers). + top->Add(); + + // Clean up unnecessary array/tuple manipulation. + top->Add( + std::make_unique(), opt_context); + + // Remove anything we created & then left dead. + top->Add( + std::make_unique(), opt_context); + return top; } diff --git a/xls/codegen_v_1_5/block_conversion_pass_pipeline.h b/xls/codegen_v_1_5/block_conversion_pass_pipeline.h index 7541593a34..37d20dcbeb 100644 --- a/xls/codegen_v_1_5/block_conversion_pass_pipeline.h +++ b/xls/codegen_v_1_5/block_conversion_pass_pipeline.h @@ -18,13 +18,14 @@ #include #include "xls/codegen_v_1_5/block_conversion_pass.h" +#include "xls/passes/optimization_pass.h" namespace xls::codegen { // Returns a pipeline which converts an unscheduled IR package into a standard // block. -std::unique_ptr -CreateBlockConversionPassPipeline(); +std::unique_ptr CreateBlockConversionPassPipeline( + OptimizationContext& opt_context); } // namespace xls::codegen diff --git a/xls/codegen_v_1_5/block_conversion_pass_pipeline_test.cc b/xls/codegen_v_1_5/block_conversion_pass_pipeline_test.cc index 5bff3c171c..12f7b3e567 100644 --- a/xls/codegen_v_1_5/block_conversion_pass_pipeline_test.cc +++ b/xls/codegen_v_1_5/block_conversion_pass_pipeline_test.cc @@ -54,7 +54,6 @@ #include "xls/common/status/matchers.h" #include "xls/common/status/ret_check.h" #include "xls/common/status/status_macros.h" -#include "xls/estimators/delay_model/delay_estimator.h" #include "xls/interpreter/block_evaluator.h" #include "xls/interpreter/block_interpreter.h" #include "xls/ir/bits.h" @@ -97,6 +96,7 @@ using ::testing::ElementsAre; using ::testing::Eq; using ::testing::Ge; using ::testing::HasSubstr; +using ::testing::IsEmpty; using ::testing::Optional; using ::testing::Pair; using ::testing::SizeIs; @@ -282,7 +282,7 @@ TEST_F(BlockConversionTest, SimpleFunction) { m::OutputPort("out", m::Add(m::InputPort("x"), m::InputPort("y")))); } -TEST_F(BlockConversionTest, DISABLED_SimpleFunctionWithNamedOutput) { +TEST_F(BlockConversionTest, SimpleFunctionWithNamedOutput) { auto p = CreatePackage(); FunctionBuilder fb(TestName(), p.get()); BValue x = fb.Param("x", p->GetBitsType(32)); @@ -331,7 +331,7 @@ TEST_F(BlockConversionTest, ZeroWidthInputsAndOutput) { EXPECT_EQ(block->GetPorts().size(), 4); } -TEST_F(BlockConversionTest, DISABLED_SimplePipelinedFunction) { +TEST_F(BlockConversionTest, SimplePipelinedFunction) { auto p = CreatePackage(); FunctionBuilder fb(TestName(), p.get()); BValue x = fb.Param("x", p->GetBitsType(32)); @@ -348,11 +348,13 @@ TEST_F(BlockConversionTest, DISABLED_SimplePipelinedFunction) { SchedulingOptions().pipeline_stages(3))); EXPECT_THAT(GetOutputPort(block), - m::OutputPort(m::Neg(m::Register(m::Not(m::Register( - m::Add(m::InputPort("x"), m::InputPort("y")))))))); + m::OutputPort(m::Neg(m::Register(m::Not( + m::Register(m::Add(m::InputPort("x"), m::InputPort("y")))))))) + << "\n\nIR:\n" + << block->DumpIr(); } -TEST_F(BlockConversionTest, DISABLED_TrivialPipelinedFunctionNoFlopping) { +TEST_F(BlockConversionTest, TrivialPipelinedFunctionNoFlopping) { auto p = CreatePackage(); FunctionBuilder fb(TestName(), p.get()); BValue x = fb.Param("x", p->GetBitsType(32)); @@ -373,7 +375,7 @@ TEST_F(BlockConversionTest, DISABLED_TrivialPipelinedFunctionNoFlopping) { m::Add(m::InputPort("x"), m::InputPort("y")))))))); } -TEST_F(BlockConversionTest, DISABLED_TrivialPipelinedFunctionFloppedInputs) { +TEST_F(BlockConversionTest, TrivialPipelinedFunctionFloppedInputs) { auto p = CreatePackage(); FunctionBuilder fb(TestName(), p.get()); BValue x = fb.Param("x", p->GetBitsType(32)); @@ -395,7 +397,7 @@ TEST_F(BlockConversionTest, DISABLED_TrivialPipelinedFunctionFloppedInputs) { m::Register(m::InputPort("x")), m::Register(m::InputPort("y"))))))))); } -TEST_F(BlockConversionTest, DISABLED_TrivialPipelinedFunctionFloppedOutputs) { +TEST_F(BlockConversionTest, TrivialPipelinedFunctionFloppedOutputs) { auto p = CreatePackage(); FunctionBuilder fb(TestName(), p.get()); BValue x = fb.Param("x", p->GetBitsType(32)); @@ -416,7 +418,7 @@ TEST_F(BlockConversionTest, DISABLED_TrivialPipelinedFunctionFloppedOutputs) { m::Add(m::InputPort("x"), m::InputPort("y"))))))))); } -TEST_F(BlockConversionTest, DISABLED_TrivialPipelinedFunctionFloppedIo) { +TEST_F(BlockConversionTest, TrivialPipelinedFunctionFloppedIo) { auto p = CreatePackage(); FunctionBuilder fb(TestName(), p.get()); BValue x = fb.Param("x", p->GetBitsType(32)); @@ -438,7 +440,7 @@ TEST_F(BlockConversionTest, DISABLED_TrivialPipelinedFunctionFloppedIo) { m::Register(m::InputPort("y")))))))))); } -TEST_F(BlockConversionTest, DISABLED_ZeroWidthPipeline) { +TEST_F(BlockConversionTest, ZeroWidthPipeline) { auto p = CreatePackage(); FunctionBuilder fb(TestName(), p.get()); BValue x = fb.Param("x", p->GetTupleType({})); @@ -454,7 +456,28 @@ TEST_F(BlockConversionTest, DISABLED_ZeroWidthPipeline) { "clk"), SchedulingOptions().pipeline_stages(3))); - EXPECT_EQ(block->GetRegisters().size(), 4); + EXPECT_THAT(block->GetRegisters(), IsEmpty()); +} + +TEST_F(BlockConversionTest, ZeroWidthPipelineWithValidControl) { + auto p = CreatePackage(); + FunctionBuilder fb(TestName(), p.get()); + BValue x = fb.Param("x", p->GetTupleType({})); + BValue y = fb.Param("y", p->GetBitsType(0)); + XLS_ASSERT_OK_AND_ASSIGN(Function * f, + fb.BuildWithReturnValue(fb.Tuple({x, y}))); + + XLS_ASSERT_OK_AND_ASSIGN( + Block * block, + ConvertToBlock(f, + CodegenOptions() + .flop_inputs(false) + .flop_outputs(false) + .clock_name("clk") + .valid_control("inputs_valid", "output_valid"), + SchedulingOptions().pipeline_stages(3))); + + EXPECT_THAT(block->GetRegisters(), SizeIs(2)); } // Verifies that an implicit token, as generated by the DSLX IR converter, is @@ -473,11 +496,10 @@ fn __itok__implicit_token__main(__token: token, __activated: bits[1]) -> fn __implicit_token__main() -> () { after_all.9: token = after_all(id=9) literal.10: bits[1] = literal(value=1, id=10) - invoke.11: (token, ()) = invoke(after_all.9, literal.10, - to_apply=__itok__implicit_token__main, id=11) tuple_index.12: token = - tuple_index(invoke.11, index=0, id=12) invoke.13: (token, ()) = - invoke(tuple_index.12, literal.10, to_apply=__itok__implicit_token__main, - id=13) ret tuple_index.14: () = tuple_index(invoke.13, index=1, id=14) + invoke.11: (token, ()) = invoke(after_all.9, literal.10, to_apply=__itok__implicit_token__main, id=11) + tuple_index.12: token = tuple_index(invoke.11, index=0, id=12) + invoke.13: (token, ()) = invoke(tuple_index.12, literal.10, to_apply=__itok__implicit_token__main, id=13) + ret tuple_index.14: () = tuple_index(invoke.13, index=1, id=14) } )"; XLS_ASSERT_OK_AND_ASSIGN(auto p, Parser::ParsePackage(kIrText)); @@ -489,7 +511,7 @@ fn __implicit_token__main() -> () { XLS_ASSERT_OK(VerifyBlock(block)); } -TEST_F(BlockConversionTest, DISABLED_SimpleProc) { +TEST_F(BlockConversionTest, SimpleProc) { const std::string ir_text = R"(package test chan in(bits[32], id=0, kind=single_value, ops=receive_only) @@ -513,7 +535,9 @@ proc my_proc(my_state: (), init={()}) { Block * block, ConvertToBlock(proc, codegen_options().generate_combinational(true))); EXPECT_THAT(FindNode("out", block), - m::OutputPort("out", m::Neg(m::InputPort("in")))); + m::OutputPort("out", m::Neg(m::InputPort("in")))) + << "\n\nIR:\n" + << block->DumpIr(); } TEST_F(BlockConversionTest, DISABLED_StreamingChannelMetadataForSimpleProc) { @@ -6394,7 +6418,7 @@ TEST_F(ProcConversionTestFixture, } } -TEST_F(ProcConversionTestFixture, DISABLED_SimpleFunctionWithProcsPresent) { +TEST_F(ProcConversionTestFixture, SimpleFunctionWithProcsPresent) { XLS_ASSERT_OK_AND_ASSIGN(std::unique_ptr p, CreateMultiProcPackage(/*with_functions=*/true)); XLS_ASSERT_OK_AND_ASSIGN(Function * f0, p->GetFunction("f0")); diff --git a/xls/codegen_v_1_5/block_conversion_wrapper_pass.cc b/xls/codegen_v_1_5/block_conversion_wrapper_pass.cc new file mode 100644 index 0000000000..a3ce2b6054 --- /dev/null +++ b/xls/codegen_v_1_5/block_conversion_wrapper_pass.cc @@ -0,0 +1,32 @@ +// Copyright 2021 The XLS Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "xls/codegen_v_1_5/block_conversion_wrapper_pass.h" + +#include "absl/status/statusor.h" +#include "xls/codegen_v_1_5/block_conversion_pass.h" +#include "xls/ir/package.h" +#include "xls/passes/optimization_pass.h" +#include "xls/passes/pass_base.h" + +namespace xls::codegen { + +absl::StatusOr BlockConversionWrapperPass::RunInternal( + Package* package, const BlockConversionPassOptions& options, + PassResults* results) const { + return wrapped_pass_->Run(package, OptimizationPassOptions(options), results, + opt_context_); +} + +} // namespace xls::codegen diff --git a/xls/codegen_v_1_5/block_conversion_wrapper_pass.h b/xls/codegen_v_1_5/block_conversion_wrapper_pass.h new file mode 100644 index 0000000000..caa9025638 --- /dev/null +++ b/xls/codegen_v_1_5/block_conversion_wrapper_pass.h @@ -0,0 +1,74 @@ +// Copyright 2021 The XLS Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef XLS_CODEGEN_V_1_5_BLOCK_CONVERSION_WRAPPER_PASS_H_ +#define XLS_CODEGEN_V_1_5_BLOCK_CONVERSION_WRAPPER_PASS_H_ + +#include +#include + +#include "absl/status/statusor.h" +#include "absl/strings/str_format.h" +#include "absl/types/span.h" +#include "xls/codegen_v_1_5/block_conversion_pass.h" +#include "xls/ir/package.h" +#include "xls/passes/optimization_pass.h" +#include "xls/passes/pass_base.h" + +namespace xls::codegen { + +// A codegen pass wrapper which wraps a OptimizationFunctionBasePass. This is +// useful for adding an optimization or transformation pass (most passes in +// xls/passes are OptimizationFunctionBasePasses) to the codegen pipeline. The +// wrapped pass is run on the block being lowered to Verilog. +// +// Takes the OptimizationContext object at construction, since it's specific to +// optimization passes & cannot be passed via a codegen pass's Run function. +class BlockConversionWrapperPass : public BlockConversionPass { + public: + explicit BlockConversionWrapperPass( + std::unique_ptr wrapped_pass, + OptimizationContext& opt_context) + : BlockConversionPass( + absl::StrFormat("codegen_%s", wrapped_pass->short_name()), + absl::StrFormat("%s (codegen)", wrapped_pass->long_name())), + wrapped_pass_(std::move(wrapped_pass)), + opt_context_(opt_context) {} + ~BlockConversionWrapperPass() override = default; + + bool IsCompound() const override { return wrapped_pass_->IsCompound(); } + + absl::StatusOr RunInternal(Package* package, + const BlockConversionPassOptions& options, + PassResults* results) const final; + + absl::StatusOr RunNested(Package* package, + const BlockConversionPassOptions& options, + PassResults* results, + PassInvocation& invocation, + absl::Span + invariant_checkers) const override { + return wrapped_pass_->RunNested(package, OptimizationPassOptions(options), + results, opt_context_, invocation, + /*invariant_checkers=*/{}); + } + + private: + std::unique_ptr wrapped_pass_; + OptimizationContext& opt_context_; +}; + +} // namespace xls::codegen + +#endif // XLS_CODEGEN_V_1_5_BLOCK_CONVERSION_WRAPPER_PASS_H_ diff --git a/xls/codegen_v_1_5/block_conversion_wrapper_pass_test.cc b/xls/codegen_v_1_5/block_conversion_wrapper_pass_test.cc new file mode 100644 index 0000000000..45600011a9 --- /dev/null +++ b/xls/codegen_v_1_5/block_conversion_wrapper_pass_test.cc @@ -0,0 +1,76 @@ +// Copyright 2021 The XLS Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "xls/codegen_v_1_5/block_conversion_wrapper_pass.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/statusor.h" +#include "xls/codegen_v_1_5/block_conversion_pass.h" +#include "xls/common/status/matchers.h" +#include "xls/ir/block.h" +#include "xls/ir/function_builder.h" +#include "xls/ir/ir_matcher.h" +#include "xls/ir/ir_test_base.h" +#include "xls/passes/dce_pass.h" +#include "xls/passes/optimization_pass.h" +#include "xls/passes/pass_base.h" + +namespace m = ::xls::op_matchers; + +namespace xls::codegen { +namespace { + +using ::absl_testing::IsOkAndHolds; + +class BlockConversionWrapperPassTest : public IrTestBase { + protected: + absl::StatusOr Run(Block* block) { + PassResults results; + OptimizationContext optimization_context; + return BlockConversionWrapperPass( + std::make_unique(), + optimization_context) + .Run(block->package(), BlockConversionPassOptions(), &results); + } +}; + +TEST_F(BlockConversionWrapperPassTest, WrappedDce) { + auto p = CreatePackage(); + + BlockBuilder bb(TestName(), p.get()); + XLS_ASSERT_OK(bb.block()->AddClockPort("clk")); + BValue a = bb.InputPort("a", p->GetBitsType(32)); + BValue b = bb.InputPort("b", p->GetBitsType(32)); + bb.Add(a, b); + bb.OutputPort("c", bb.Subtract(a, b)); + XLS_ASSERT_OK_AND_ASSIGN(Block * block, bb.Build()); + + // The add should be eliminated. + EXPECT_EQ(block->node_count(), 5); + EXPECT_THAT(Run(block), IsOkAndHolds(true)); + EXPECT_EQ(block->node_count(), 4); + + EXPECT_THAT(FindNode("c", block), + m::OutputPort(m::Sub(m::InputPort("a"), m::InputPort("b")))); + + // Pass should be idempotent. + EXPECT_THAT(Run(block), IsOkAndHolds(false)); + EXPECT_EQ(block->node_count(), 4); +} + +} // namespace +} // namespace xls::codegen diff --git a/xls/codegen_v_1_5/channel_to_port_io_lowering_pass.cc b/xls/codegen_v_1_5/channel_to_port_io_lowering_pass.cc index 243894a632..bcc330c488 100644 --- a/xls/codegen_v_1_5/channel_to_port_io_lowering_pass.cc +++ b/xls/codegen_v_1_5/channel_to_port_io_lowering_pass.cc @@ -641,6 +641,7 @@ absl::Status ConnectReceivesToConnector( Stage& stage = block->stages()[stage_index]; Node* token = receive->As()->token(); std::optional predicate = receive->As()->predicate(); + bool is_blocking = receive->As()->is_blocking(); if (connector.ready.has_value()) { // The ready signal from this receive is: @@ -679,11 +680,54 @@ absl::Status ConnectReceivesToConnector( .status()); } - XLS_RETURN_IF_ERROR( - receive - ->ReplaceUsesWithNewInStage( - stage_index, absl::MakeConstSpan({token, connector.data})) - .status()); + Node* data = connector.data; + if (options.codegen_options.gate_recvs()) { + std::vector gate_conditions; + gate_conditions.reserve(2); + if (predicate.has_value()) { + gate_conditions.push_back(*predicate); + } + if (!is_blocking && connector.valid.has_value()) { + gate_conditions.push_back(*connector.valid); + } + + Node* gate_condition = nullptr; + if (gate_conditions.size() == 1) { + gate_condition = gate_conditions.front(); + } else if (gate_conditions.size() > 1) { + XLS_ASSIGN_OR_RETURN(gate_condition, block->MakeNodeInStage( + stage_index, receive->loc(), + gate_conditions, Op::kAnd)); + } + + if (gate_condition != nullptr) { + XLS_ASSIGN_OR_RETURN(Node * zero, + block->MakeNodeInStage( + stage_index, receive->loc(), + ZeroOfType(connector.data->GetType()))); + XLS_ASSIGN_OR_RETURN(data, + block->MakeNodeInStage