diff --git a/xls/dslx/ir_convert/extract_conversion_order.cc b/xls/dslx/ir_convert/extract_conversion_order.cc index 1f87fafb0a..2739c1857b 100644 --- a/xls/dslx/ir_convert/extract_conversion_order.cc +++ b/xls/dslx/ir_convert/extract_conversion_order.cc @@ -69,6 +69,20 @@ std::string ConversionRecordsToString( } // namespace +// -- class ProcIdFactory + +ProcId ProcIdFactory::CreateProcId(const ProcId& parent, Proc* spawnee, + bool count_as_new_instance) { + std::vector> new_stack = parent.proc_instance_stack; + int& instance_count = + instance_counts_[std::make_pair(parent, spawnee->identifier())]; + new_stack.push_back(std::make_pair(spawnee, instance_count)); + if (count_as_new_instance) { + ++instance_count; + } + return ProcId{.proc_instance_stack = std::move(new_stack)}; +} + // -- class Callee /* static */ absl::StatusOr Callee::Make( @@ -295,15 +309,13 @@ class InvocationVisitor : public ExprVisitor { if (maybe_proc.has_value()) { XLS_RET_CHECK(proc_id_.has_value()) << "Functions cannot spawn procs."; - std::vector proc_stack = proc_id_.value().proc_stack; - proc_stack.push_back(maybe_proc.value()); - proc_id = ProcId{proc_stack, proc_instances_[proc_stack]}; - // Only increment on next so that Config & Next have the same ID. - // This assumes that we call a Proc's config & next calls in that order, - // which is indeed the case. - if (callee_info->callee->tag() == FunctionTag::kProcNext) { - proc_instances_[proc_stack]++; - } + // Only count `next` as a new instance, so that `config` and `next` have + // the same ID. This assumes that we call a proc's `config` and `next` in + // that order, which is indeed the case. + const bool count_as_new_instance = + callee_info->callee->tag() == FunctionTag::kProcNext; + proc_id = proc_id_factory_.CreateProcId(*proc_id_, maybe_proc.value(), + count_as_new_instance); } // See if there are parametric bindings to use in the callee for this @@ -438,7 +450,8 @@ class InvocationVisitor : public ExprVisitor { Module* module = (*info)->module; XLS_ASSIGN_OR_RETURN(Function * f, module->GetMemberOrError(colon_ref->attr())); - return CalleeInfo{module, f, (*info)->type_info}; + return CalleeInfo{ + .module = module, .callee = f, .type_info = (*info)->type_info}; } // Helper for invocations of NameRef callees. @@ -488,14 +501,15 @@ class InvocationVisitor : public ExprVisitor { identifier); } - return CalleeInfo{this_m, f, callee_type_info}; + return CalleeInfo{ + .module = this_m, .callee = f, .type_info = callee_type_info}; } Module* module_; TypeInfo* type_info_; const ParametricEnv& bindings_; std::optional proc_id_; - absl::flat_hash_map, int> proc_instances_; + ProcIdFactory proc_id_factory_; // Built up list of callee records discovered during traversal. std::vector callees_; @@ -717,13 +731,14 @@ static absl::StatusOr> GetOrderForProc( // The next function of a proc is the entry function when converting a proc to // IR. - XLS_RETURN_IF_ERROR(AddToReady(&p->next(), - /*invocation=*/nullptr, p->owner(), type_info, - ParametricEnv(), &ready, ProcId{{p}, 0}, - is_top)); + XLS_RETURN_IF_ERROR( + AddToReady(&p->next(), + /*invocation=*/nullptr, p->owner(), type_info, ParametricEnv(), + &ready, ProcId{.proc_instance_stack = {{p, 0}}}, is_top)); XLS_RETURN_IF_ERROR(AddToReady(&p->config(), /*invocation=*/nullptr, p->owner(), type_info, - ParametricEnv(), &ready, ProcId{{p}, 0})); + ParametricEnv(), &ready, + ProcId{.proc_instance_stack = {{p, 0}}})); // Constants and "member" vars are assigned and defined in Procs' "config'" // functions, so we need to execute those before their "next" functions. diff --git a/xls/dslx/ir_convert/extract_conversion_order.h b/xls/dslx/ir_convert/extract_conversion_order.h index 9edba4d448..4fe26b1b08 100644 --- a/xls/dslx/ir_convert/extract_conversion_order.h +++ b/xls/dslx/ir_convert/extract_conversion_order.h @@ -21,9 +21,12 @@ #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/log/check.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include "xls/dslx/frontend/ast.h" #include "xls/dslx/frontend/proc.h" @@ -40,39 +43,72 @@ namespace xls::dslx { struct ProcId { // Contains the "spawn chain": the series of Procs through which this Proc was // spawned, with the oldest/"root" proc as element 0. Contains the current - // proc, as well. - std::vector proc_stack; - - // The index of this Proc in the proc stack. If a Proc is spawned > 1 inside - // the same Proc config function, this index differentiates the spawnees. - // Each unique proc stack will have its count start at 0 - in other words, - // the sequence: - // spawn foo()(c0) - // spawn bar()(c1) - // spawn foo()(c2) - // Would result in IDs of: - // foo:0, bar:0, and foo:1, respectively. - int instance; + // proc, as well. The second element of each pair is a zero-based spawn index + // of that same proc by the spawning proc. For example, with a spawn chain + // like: + // A |-> D |-> B -> C + // |-> B -> C + // |-> B -> C + // |-> E |-> B -> C + // + // the `proc_instance_stack` for each `C` would look like: + // [{A, 0}, {D, 0}, {B, 0}, {C, 0}] + // [{A, 0}, {B, 0}, {C, 0}] + // [{A, 0}, {B, 1}, {C, 0}] + // [{A, 0}, {E, 0}, {B, 0}, {C, 0}] + std::vector> proc_instance_stack; std::string ToString() const { - return absl::StrCat(absl::StrJoin(proc_stack, "->", - [](std::string* out, const Proc* p) { - out->append(p->identifier()); - }), - ":", instance); - return ""; + if (proc_instance_stack.empty()) { + return ""; + } + // The first proc in a chain never needs an instance count. Leaving it out + // specifically when the chain length is more than 1 gets us the historical + // output in most cases (where there was only an instance count at the end + // of the chain). + CHECK_EQ(proc_instance_stack[0].second, 0); + const bool omit_first_instance_count = proc_instance_stack.size() > 1; + std::string part_with_instance_counts = absl::StrJoin( + proc_instance_stack.begin() + (omit_first_instance_count ? 1 : 0), + proc_instance_stack.end(), "->", + [](std::string* out, const std::pair p) { + absl::StrAppendFormat(out, "%s:%d", p.first->identifier(), p.second); + }); + return omit_first_instance_count + ? absl::StrCat(proc_instance_stack[0].first->identifier(), "->", + part_with_instance_counts) + : part_with_instance_counts; } bool operator==(const ProcId& other) const { - return proc_stack == other.proc_stack && instance == other.instance; + return proc_instance_stack == other.proc_instance_stack; } template friend H AbslHashValue(H h, const ProcId& pid) { - return H::combine(std::move(h), pid.proc_stack, pid.instance); + return H::combine(std::move(h), pid.proc_instance_stack); } }; +// An object that deals out `ProcId` instances. +class ProcIdFactory { + public: + // Creates a `ProcId` representing the given `spawnee` spawned by the given + // `parent` context. If `count_as_new_instance` is true, then subsequent calls + // with the same `parent` and `spawnee` will get a new instance count value. + // Otherwise, subsequent calls will get an equivalent `ProcId` to the one + // returned by this call. + ProcId CreateProcId(const ProcId& parent, Proc* spawnee, + bool count_as_new_instance = true); + + private: + // Maps each `parent` and `spawnee` identifier passed to `CreateProcId` to the + // number of instances of that pairing, i.e., the number of times that + // `parent` and `spawnee` have been passed in with `true` for + // `count_as_new_instance`. + absl::flat_hash_map, int> instance_counts_; +}; + // Describes a callee function in the conversion order (see // ConversionRecord::callees). class Callee { diff --git a/xls/dslx/ir_convert/extract_conversion_order_test.cc b/xls/dslx/ir_convert/extract_conversion_order_test.cc index 017cb6305c..ad1b973151 100644 --- a/xls/dslx/ir_convert/extract_conversion_order_test.cc +++ b/xls/dslx/ir_convert/extract_conversion_order_test.cc @@ -19,6 +19,7 @@ #include #include +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/container/flat_hash_map.h" #include "xls/common/status/matchers.h" @@ -32,6 +33,18 @@ namespace xls::dslx { namespace { +using ::testing::ElementsAre; + +MATCHER_P2(IdentifierAndProcId, identifier_matcher, proc_id_matcher, "") { + if (!arg.proc_id().has_value()) { + return false; + } + return ExplainMatchResult(identifier_matcher, arg.f()->identifier(), + result_listener) && + ExplainMatchResult(proc_id_matcher, arg.proc_id()->ToString(), + result_listener); +} + TEST(ExtractConversionOrderTest, SimpleLinearCallgraph) { constexpr std::string_view kProgram = R"( fn g() -> u32 { u32:42 } @@ -410,11 +423,11 @@ proc main { ASSERT_TRUE(order[20].proc_id().has_value()); EXPECT_EQ(order[0].f()->identifier(), "p2.init"); EXPECT_FALSE(order[0].IsTop()); - EXPECT_EQ(order[0].proc_id().value().ToString(), "main->p0->p2:0"); + EXPECT_EQ(order[0].proc_id().value().ToString(), "main->p0:0->p2:0"); EXPECT_EQ(order[1].f()->identifier(), "f0"); EXPECT_FALSE(order[1].IsTop()); EXPECT_EQ(order[2].f()->identifier(), "p1.init"); - EXPECT_EQ(order[2].proc_id().value().ToString(), "main->p0->p1:0"); + EXPECT_EQ(order[2].proc_id().value().ToString(), "main->p0:0->p1:0"); EXPECT_FALSE(order[2].IsTop()); EXPECT_EQ(order[3].f()->identifier(), "p0.init"); EXPECT_EQ(order[3].proc_id().value().ToString(), "main->p0:0"); @@ -431,31 +444,31 @@ proc main { EXPECT_EQ(order[7].proc_id().value().ToString(), "main->p1:0"); EXPECT_FALSE(order[7].IsTop()); EXPECT_EQ(order[8].f()->identifier(), "p2.config"); - EXPECT_EQ(order[8].proc_id().value().ToString(), "main->p1->p2:0"); + EXPECT_EQ(order[8].proc_id().value().ToString(), "main->p1:0->p2:0"); EXPECT_FALSE(order[8].IsTop()); EXPECT_EQ(order[9].f()->identifier(), "p0.config"); EXPECT_EQ(order[9].proc_id().value().ToString(), "main->p0:0"); EXPECT_FALSE(order[9].IsTop()); EXPECT_EQ(order[10].f()->identifier(), "p1.config"); - EXPECT_EQ(order[10].proc_id().value().ToString(), "main->p0->p1:0"); + EXPECT_EQ(order[10].proc_id().value().ToString(), "main->p0:0->p1:0"); EXPECT_FALSE(order[10].IsTop()); EXPECT_EQ(order[11].f()->identifier(), "p2.config"); - EXPECT_EQ(order[11].proc_id().value().ToString(), "main->p0->p1->p2:0"); + EXPECT_EQ(order[11].proc_id().value().ToString(), "main->p0:0->p1:0->p2:0"); EXPECT_FALSE(order[11].IsTop()); EXPECT_EQ(order[12].f()->identifier(), "p2.config"); - EXPECT_EQ(order[12].proc_id().value().ToString(), "main->p0->p2:0"); + EXPECT_EQ(order[12].proc_id().value().ToString(), "main->p0:0->p2:0"); EXPECT_FALSE(order[12].IsTop()); EXPECT_EQ(order[13].f()->identifier(), "main.next"); EXPECT_EQ(order[13].proc_id().value().ToString(), "main:0"); EXPECT_TRUE(order[13].IsTop()); EXPECT_EQ(order[14].f()->identifier(), "p2.next"); - EXPECT_EQ(order[14].proc_id().value().ToString(), "main->p0->p2:0"); + EXPECT_EQ(order[14].proc_id().value().ToString(), "main->p0:0->p2:0"); EXPECT_FALSE(order[14].IsTop()); EXPECT_EQ(order[15].f()->identifier(), "p2.next"); - EXPECT_EQ(order[15].proc_id().value().ToString(), "main->p0->p1->p2:0"); + EXPECT_EQ(order[15].proc_id().value().ToString(), "main->p0:0->p1:0->p2:0"); EXPECT_FALSE(order[15].IsTop()); EXPECT_EQ(order[16].f()->identifier(), "p1.next"); - EXPECT_EQ(order[16].proc_id().value().ToString(), "main->p0->p1:0"); + EXPECT_EQ(order[16].proc_id().value().ToString(), "main->p0:0->p1:0"); EXPECT_FALSE(order[16].IsTop()); EXPECT_EQ(order[17].f()->identifier(), "p0.next"); EXPECT_EQ(order[17].proc_id().value().ToString(), "main->p0:0"); @@ -548,9 +561,9 @@ proc main { EXPECT_EQ(order[0].f()->identifier(), "f0"); EXPECT_EQ(order[1].f()->identifier(), "f1"); EXPECT_EQ(order[2].f()->identifier(), "p2.init"); - EXPECT_EQ(order[2].proc_id().value().ToString(), "main->p0->p2:0"); + EXPECT_EQ(order[2].proc_id().value().ToString(), "main->p0:0->p2:0"); EXPECT_EQ(order[3].f()->identifier(), "p1.init"); - EXPECT_EQ(order[3].proc_id().value().ToString(), "main->p0->p1:0"); + EXPECT_EQ(order[3].proc_id().value().ToString(), "main->p0:0->p1:0"); EXPECT_EQ(order[4].f()->identifier(), "p0.init"); EXPECT_EQ(order[4].proc_id().value().ToString(), "main->p0:0"); EXPECT_EQ(order[5].f()->identifier(), "main.config"); @@ -560,27 +573,27 @@ proc main { EXPECT_EQ(order[7].f()->identifier(), "p1.config"); EXPECT_EQ(order[7].proc_id().value().ToString(), "main->p1:0"); EXPECT_EQ(order[8].f()->identifier(), "p2.config"); - EXPECT_EQ(order[8].proc_id().value().ToString(), "main->p1->p2:0"); + EXPECT_EQ(order[8].proc_id().value().ToString(), "main->p1:0->p2:0"); EXPECT_EQ(order[9].f()->identifier(), "p0.config"); EXPECT_EQ(order[9].proc_id().value().ToString(), "main->p0:0"); EXPECT_EQ(order[10].f()->identifier(), "p1.config"); - EXPECT_EQ(order[10].proc_id().value().ToString(), "main->p0->p1:0"); + EXPECT_EQ(order[10].proc_id().value().ToString(), "main->p0:0->p1:0"); EXPECT_EQ(order[11].f()->identifier(), "p2.config"); - EXPECT_EQ(order[11].proc_id().value().ToString(), "main->p0->p1->p2:0"); + EXPECT_EQ(order[11].proc_id().value().ToString(), "main->p0:0->p1:0->p2:0"); EXPECT_EQ(order[12].f()->identifier(), "p2.config"); - EXPECT_EQ(order[12].proc_id().value().ToString(), "main->p0->p2:0"); + EXPECT_EQ(order[12].proc_id().value().ToString(), "main->p0:0->p2:0"); EXPECT_EQ(order[13].f()->identifier(), "main.next"); EXPECT_EQ(order[13].proc_id().value().ToString(), "main:0"); EXPECT_EQ(order[14].f()->identifier(), "p2.next"); - EXPECT_EQ(order[14].proc_id().value().ToString(), "main->p0->p2:0"); + EXPECT_EQ(order[14].proc_id().value().ToString(), "main->p0:0->p2:0"); EXPECT_EQ(order[15].f()->identifier(), "p2.next"); - EXPECT_EQ(order[15].proc_id().value().ToString(), "main->p0->p1->p2:0"); + EXPECT_EQ(order[15].proc_id().value().ToString(), "main->p0:0->p1:0->p2:0"); EXPECT_EQ(order[16].f()->identifier(), "p1.next"); - EXPECT_EQ(order[16].proc_id().value().ToString(), "main->p0->p1:0"); + EXPECT_EQ(order[16].proc_id().value().ToString(), "main->p0:0->p1:0"); EXPECT_EQ(order[17].f()->identifier(), "p0.next"); EXPECT_EQ(order[17].proc_id().value().ToString(), "main->p0:0"); EXPECT_EQ(order[18].f()->identifier(), "p2.next"); - EXPECT_EQ(order[18].proc_id().value().ToString(), "main->p1->p2:0"); + EXPECT_EQ(order[18].proc_id().value().ToString(), "main->p1:0->p2:0"); EXPECT_EQ(order[19].f()->identifier(), "p1.next"); EXPECT_EQ(order[19].proc_id().value().ToString(), "main->p1:0"); EXPECT_EQ(order[20].f()->identifier(), "p2.next"); @@ -673,6 +686,88 @@ proc main { EXPECT_EQ(order[13].proc_id().value().ToString(), "main->p2:0"); } +TEST(ExtractConversionOrderTest, ProcChainWithMultiLevelMultiInstanceCounts) { + constexpr std::string_view kProgram = R"( +proc C { + init { () } + config() { () } + next(state: ()) { () } +} + +proc B { + init { () } + config() { + spawn C(); + () + } + next(state: ()) { () } +} + +proc E { + init { () } + config() { + spawn B(); + () + } + next(state: ()) { () } +} + +proc D { + init { () } + config() { + spawn B(); + () + } + next(state: ()) { () } +} + +proc A { + init { () } + config() { + spawn D(); + spawn B(); + spawn B(); + spawn E(); + () + } + next(state: ()) { () } +} +)"; + auto import_data = CreateImportDataForTest(); + XLS_ASSERT_OK_AND_ASSIGN( + TypecheckedModule tm, + ParseAndTypecheck(kProgram, "test.x", "test", &import_data)); + XLS_ASSERT_OK_AND_ASSIGN(std::vector order, + GetOrder(tm.module, tm.type_info)); + EXPECT_THAT(order, + ElementsAre(IdentifierAndProcId("C.init", "A->D:0->B:0->C:0"), + IdentifierAndProcId("B.init", "A->D:0->B:0"), + IdentifierAndProcId("D.init", "A->D:0"), + IdentifierAndProcId("E.init", "A->E:0"), + IdentifierAndProcId("A.config", "A:0"), + IdentifierAndProcId("E.config", "A->E:0"), + IdentifierAndProcId("B.config", "A->E:0->B:0"), + IdentifierAndProcId("C.config", "A->E:0->B:0->C:0"), + IdentifierAndProcId("B.config", "A->B:1"), + IdentifierAndProcId("C.config", "A->B:1->C:0"), + IdentifierAndProcId("B.config", "A->B:0"), + IdentifierAndProcId("C.config", "A->B:0->C:0"), + IdentifierAndProcId("D.config", "A->D:0"), + IdentifierAndProcId("B.config", "A->D:0->B:0"), + IdentifierAndProcId("C.config", "A->D:0->B:0->C:0"), + IdentifierAndProcId("A.next", "A:0"), + IdentifierAndProcId("C.next", "A->D:0->B:0->C:0"), + IdentifierAndProcId("B.next", "A->D:0->B:0"), + IdentifierAndProcId("D.next", "A->D:0"), + IdentifierAndProcId("C.next", "A->B:0->C:0"), + IdentifierAndProcId("B.next", "A->B:0"), + IdentifierAndProcId("C.next", "A->B:1->C:0"), + IdentifierAndProcId("B.next", "A->B:1"), + IdentifierAndProcId("C.next", "A->E:0->B:0->C:0"), + IdentifierAndProcId("B.next", "A->E:0->B:0"), + IdentifierAndProcId("E.next", "A->E:0"))); +} + TEST(GetTopLevelProcsTest, OnlyOneParametricProc) { constexpr std::string_view kProgram = R"( proc np { diff --git a/xls/dslx/ir_convert/ir_converter_test.cc b/xls/dslx/ir_convert/ir_converter_test.cc index 9f47e0f6e3..95540b223e 100644 --- a/xls/dslx/ir_convert/ir_converter_test.cc +++ b/xls/dslx/ir_convert/ir_converter_test.cc @@ -1768,33 +1768,16 @@ proc A { } )"; - { - ConvertOptions options; - options.emit_fail_as_assert = false; - options.emit_positions = false; - // TODO(b/357942810) - Turn on verification once it can pass. - options.verify_ir = false; - auto import_data = CreateImportDataForTest(); - - XLS_ASSERT_OK_AND_ASSIGN( - std::string converted, - ConvertOneFunctionForTest(kProgram, "A", import_data, options)); - ExpectIr(converted, TestName()); - } + ConvertOptions options; + options.emit_fail_as_assert = false; + options.emit_positions = false; + options.verify_ir = true; + auto import_data = CreateImportDataForTest(); - // TODO(b/357942810) - Remove once verification error is fixed. - { - ConvertOptions options; - options.emit_fail_as_assert = false; - options.emit_positions = false; - options.verify_ir = true; - auto import_data = CreateImportDataForTest(); - - EXPECT_THAT(ConvertOneFunctionForTest(kProgram, "A", import_data, options), - StatusIs(absl::StatusCode::kInternal, - HasSubstr(" __test_module__A__B__C_0_next is not " - "unique within package"))); - } + XLS_ASSERT_OK_AND_ASSIGN( + std::string converted, + ConvertOneFunctionForTest(kProgram, "A", import_data, options)); + ExpectIr(converted, TestName()); } TEST(IrConverterTest, SendIfRecvIf) { diff --git a/xls/dslx/ir_convert/proc_config_ir_converter.cc b/xls/dslx/ir_convert/proc_config_ir_converter.cc index a3ddba8163..271557f990 100644 --- a/xls/dslx/ir_convert/proc_config_ir_converter.cc +++ b/xls/dslx/ir_convert/proc_config_ir_converter.cc @@ -46,15 +46,6 @@ #include "xls/ir/value.h" namespace xls::dslx { -namespace { - -std::string ProcStackToId(const std::vector& stack) { - return absl::StrJoin(stack, "->", [](std::string* out, const Proc* p) { - out->append(p->identifier()); - }); -} - -} // namespace ProcConfigIrConverter::ProcConfigIrConverter( PackageConversionData* conversion_info, Function* f, TypeInfo* type_info, @@ -266,9 +257,7 @@ absl::Status ProcConfigIrConverter::HandleSpawn(const Spawn* node) { VLOG(4) << "ProcConfigIrConverter::HandleSpawn : " << node->ToString(); std::vector config_args; XLS_ASSIGN_OR_RETURN(Proc * p, ResolveProc(node->callee(), type_info_)); - std::vector new_stack = proc_id_.proc_stack; - new_stack.push_back(p); - ProcId new_id{new_stack, instances_[new_stack]++}; + ProcId new_id = proc_id_factory_.CreateProcId(proc_id_, p); for (const auto& arg : node->config()->args()) { XLS_RETURN_IF_ERROR(arg->Accept(this)); config_args.push_back(node_to_ir_.at(arg)); diff --git a/xls/dslx/ir_convert/proc_config_ir_converter.h b/xls/dslx/ir_convert/proc_config_ir_converter.h index 9c1f3210b7..37bd3e04f3 100644 --- a/xls/dslx/ir_convert/proc_config_ir_converter.h +++ b/xls/dslx/ir_convert/proc_config_ir_converter.h @@ -88,7 +88,7 @@ class ProcConfigIrConverter : public AstNodeVisitorWithDefault { NameUniquer channel_name_uniquer_; ProcConversionData* proc_data_; - absl::flat_hash_map, int> instances_; + ProcIdFactory proc_id_factory_; const ParametricEnv& bindings_; ProcId proc_id_; diff --git a/xls/dslx/ir_convert/proc_config_ir_converter_test.cc b/xls/dslx/ir_convert/proc_config_ir_converter_test.cc index d5e9a26fbb..8e349e2ee1 100644 --- a/xls/dslx/ir_convert/proc_config_ir_converter_test.cc +++ b/xls/dslx/ir_convert/proc_config_ir_converter_test.cc @@ -86,7 +86,7 @@ proc main { auto import_data = CreateImportDataForTest(); ParametricEnv bindings; - ProcId proc_id{/*proc_stack=*/{}, /*instance=*/0}; + ProcId proc_id; XLS_ASSERT_OK_AND_ASSIGN( TypecheckedModule tm, @@ -142,7 +142,7 @@ proc main { auto import_data = CreateImportDataForTest(); ParametricEnv bindings; - ProcId proc_id{/*proc_stack=*/{}, /*instance=*/0}; + ProcId proc_id; XLS_ASSERT_OK_AND_ASSIGN( TypecheckedModule tm, @@ -269,6 +269,51 @@ proc main { Contains(m::Channel("test_module__my_chan__1")))); } +TEST(ProcConfigIrConverterTest, MultipleNonLeafSpawnsOfSameProc) { + constexpr std::string_view kModule = R"( +proc C { + init { () } + config() { () } + next(state: ()) { () } +} + +proc B { + init { () } + config() { + spawn C(); + () + } + next(state: ()) { () } +} + +proc A { + init { () } + config() { + spawn B(); + spawn B(); + () + } + next(state: ()) { () } +} +)"; + + auto import_data = CreateImportDataForTest(); + + XLS_ASSERT_OK_AND_ASSIGN( + TypecheckedModule tm, + ParseAndTypecheck(kModule, "test_module.x", "test_module", &import_data)); + XLS_ASSERT_OK_AND_ASSIGN( + PackageConversionData conv, + ConvertModuleToPackage(tm.module, &import_data, ConvertOptions{})); + + EXPECT_THAT(conv.package->procs(), + UnorderedElementsAre(m::Proc("__test_module__A_0_next"), + m::Proc("__test_module__A__B_0__C_0_next"), + m::Proc("__test_module__A__B_0_next"), + m::Proc("__test_module__A__B_1__C_0_next"), + m::Proc("__test_module__A__B_1_next"))); +} + TEST(ProcConfigIrConverterTest, MultipleInternalChannelsWithSameNameInDifferentProcs) { constexpr std::string_view kModule = R"( diff --git a/xls/dslx/ir_convert/testdata/ir_converter_test_HandlesProcWithMultipleSpawn.ir b/xls/dslx/ir_convert/testdata/ir_converter_test_HandlesProcWithMultipleSpawn.ir index 219f326066..98d0a5b0b9 100644 --- a/xls/dslx/ir_convert/testdata/ir_converter_test_HandlesProcWithMultipleSpawn.ir +++ b/xls/dslx/ir_convert/testdata/ir_converter_test_HandlesProcWithMultipleSpawn.ir @@ -30,7 +30,7 @@ top proc __test_module__A_0_next(__state: (), init={()}) { next (tuple.13) } -proc __test_module__A__B__C_0_next(__state: bits[32], init={0}) { +proc __test_module__A__B_0__C_0_next(__state: bits[32], init={0}) { after_all.17: token = after_all(id=17) literal.16: bits[1] = literal(value=1, id=16) receive.18: (token, bits[32]) = receive(after_all.17, predicate=literal.16, channel=test_module__s0__2, id=18) @@ -55,10 +55,10 @@ proc __test_module__A__B_0_next(__state: bits[32], init={0}) { next (add.32) } -proc __test_module__A__B__C_0_next(__state: bits[32], init={0}) { +proc __test_module__A__B_1__C_0_next(__state: bits[32], init={0}) { after_all.36: token = after_all(id=36) literal.35: bits[1] = literal(value=1, id=35) - receive.37: (token, bits[32]) = receive(after_all.36, predicate=literal.35, channel=test_module__s0__2, id=37) + receive.37: (token, bits[32]) = receive(after_all.36, predicate=literal.35, channel=test_module__s0__1, id=37) data: bits[32] = tuple_index(receive.37, index=1, id=40) __token: token = literal(value=token, id=33) tuple_index.38: token = tuple_index(receive.37, index=0, id=38) diff --git a/xls/examples/BUILD b/xls/examples/BUILD index edae773178..d00e0de7da 100644 --- a/xls/examples/BUILD +++ b/xls/examples/BUILD @@ -708,7 +708,7 @@ xls_dslx_ir( xls_ir_opt_ir( name = "delay_opt_ir", src = "delay.ir", - top = "__delay__Delay32x2048_init3__Delay__DelayInternal_0__10_32_2048_64_1024_3_next", + top = "__delay__Delay32x2048_init3__Delay_0__DelayInternal_0__10_32_2048_64_1024_3_next", ) xls_ir_verilog(