diff --git a/xls/ir/BUILD b/xls/ir/BUILD index 574c1659cb..b3af8c96bf 100644 --- a/xls/ir/BUILD +++ b/xls/ir/BUILD @@ -1603,11 +1603,11 @@ cc_library( ":channel_ops", ":ir", ":value", + "//xls/common/logging", "//xls/common/status:ret_check", "//xls/common/status:status_macros", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", @@ -1621,6 +1621,7 @@ cc_test( srcs = ["elaboration_test.cc"], deps = [ ":channel", + ":channel_ops", ":elaboration", ":function_builder", ":ir", diff --git a/xls/ir/elaboration.cc b/xls/ir/elaboration.cc index 63b88e0bd0..7577d21fbd 100644 --- a/xls/ir/elaboration.cc +++ b/xls/ir/elaboration.cc @@ -25,30 +25,31 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" -#include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" #include "absl/types/span.h" +#include "xls/common/logging/logging.h" #include "xls/common/status/ret_check.h" #include "xls/common/status/status_macros.h" #include "xls/ir/channel.h" #include "xls/ir/channel_ops.h" +#include "xls/ir/package.h" #include "xls/ir/proc.h" #include "xls/ir/proc_instantiation.h" #include "xls/ir/value.h" namespace xls { -/*static*/ -absl::StatusOr> ProcInstance::Create( +namespace { + +absl::StatusOr> CreateNewStyleProcInstance( Proc* proc, std::optional proc_instantiation, const InstantiationPath& path, absl::Span interface) { XLS_RET_CHECK(proc->is_new_style_proc()); - auto instance = absl::WrapUnique( - new ProcInstance(proc, proc_instantiation, path, interface)); // Map from channel reference name to ChannelInstance. absl::flat_hash_map channel_instances; @@ -56,11 +57,14 @@ absl::StatusOr> ProcInstance::Create( for (int64_t i = 0; i < interface.size(); ++i) { channel_instances[proc->interface()[i]->name()] = interface[i]; } + std::vector> declared_channels; for (Channel* channel : proc->channels()) { - instance->channels_.push_back(std::make_unique( + declared_channels.push_back(std::make_unique( ChannelInstance{.channel = channel, .path = path})); - channel_instances[channel->name()] = instance->channels_.back().get(); + channel_instances[channel->name()] = declared_channels.back().get(); } + + std::vector> instantiated_procs; for (const std::unique_ptr& instantiation : proc->proc_instantiations()) { InstantiationPath instantiation_path = path; @@ -84,12 +88,47 @@ absl::StatusOr> ProcInstance::Create( channel_instances.at(channel_ref->name())); } XLS_ASSIGN_OR_RETURN(std::unique_ptr instantiation_instance, - Create(instantiation->proc(), instantiation.get(), - instantiation_path, instantiation_interface)); - instance->instantiated_procs_.push_back(std::move(instantiation_instance)); + CreateNewStyleProcInstance( + instantiation->proc(), instantiation.get(), + instantiation_path, instantiation_interface)); + instantiated_procs.push_back(std::move(instantiation_instance)); + } + return std::make_unique(proc, proc_instantiation, path, + interface, std::move(declared_channels), + std::move(instantiated_procs)); +} + +} // namespace + +std::string ChannelInstance::ToString() const { + if (path.has_value()) { + return absl::StrFormat("%s [%s]", channel->name(), path->ToString()); + } + return std::string{channel->name()}; +} + +absl::StatusOr ProcInstance::GetChannelInstance( + std::string_view channel_name) const { + for (const std::unique_ptr& channel : channels_) { + if (channel->channel->name() == channel_name) { + return channel.get(); + } + } + for (ChannelInstance* channel : interface_) { + if (channel->channel->name() == channel_name) { + return channel; + } } + return absl::NotFoundError(absl::StrFormat( + "No channel instance with name `%s` in instance of proc `%s`", + channel_name, proc()->name())); +} - return std::move(instance); +std::string ProcInstance::GetName() const { + if (!path().has_value()) { + return proc()->name(); + } + return absl::StrFormat("%s [%s]", proc()->name(), path()->ToString()); } std::string ProcInstance::ToString(int64_t indent_amount) const { @@ -118,20 +157,33 @@ std::string ProcInstance::ToString(int64_t indent_amount) const { return absl::StrJoin(pieces, "\n"); } -static void BuildInstanceMaps( - ProcInstance* proc_instance, - absl::flat_hash_map& proc_map, - absl::flat_hash_map, - ChannelInstance*>& channel_map) { - proc_map[proc_instance->path()] = proc_instance; +void Elaboration::BuildInstanceMaps(ProcInstance* proc_instance) { + XLS_CHECK(proc_instance->path().has_value()); + + proc_instance_ptrs_.push_back(proc_instance); + instances_of_proc_[proc_instance->proc()].push_back(proc_instance); + + proc_instances_by_path_[proc_instance->path().value()] = proc_instance; for (const std::unique_ptr& channel_instance : proc_instance->channels()) { - channel_map[{std::string{channel_instance->channel->name()}, - proc_instance->path()}] = channel_instance.get(); + channel_instances_by_path_[{std::string{channel_instance->channel->name()}, + proc_instance->path().value()}] = + channel_instance.get(); + instances_of_channel_[channel_instance->channel].push_back( + channel_instance.get()); + channel_instance_ptrs_.push_back(channel_instance.get()); } + for (const std::unique_ptr& subinstance : proc_instance->instantiated_procs()) { - BuildInstanceMaps(subinstance.get(), proc_map, channel_map); + BuildInstanceMaps(subinstance.get()); + } + + XLS_CHECK_EQ(proc_instance->interface().size(), + proc_instance->proc()->interface().size()); + for (int64_t i = 0; i < proc_instance->interface().size(); ++i) { + instances_of_channel_reference_[proc_instance->proc()->interface()[i]] + .push_back(proc_instance->interface()[i]); } } @@ -143,6 +195,8 @@ absl::StatusOr Elaboration::Elaborate(Proc* top) { } Elaboration elaboration; + elaboration.package_ = top->package(); + // Create top-level channels. These are required because there are no // xls::Channels in the IR corresponding to the ChannelReferences forming the // interface of `top`. @@ -168,20 +222,25 @@ absl::StatusOr Elaboration::Elaborate(Proc* top) { elaboration.interface_channel_instances_.push_back( std::make_unique(ChannelInstance{ .channel = elaboration.interface_channels_.back().get(), - .path = path})); + .path = std::nullopt})); channel_instance_ptrs.push_back( elaboration.interface_channel_instances_.back().get()); } XLS_ASSIGN_OR_RETURN( elaboration.top_, - ProcInstance::Create(top, /*proc_instantiation=*/std::nullopt, path, - channel_instance_ptrs)); - BuildInstanceMaps(elaboration.top_.get(), elaboration.proc_instances_by_path_, - elaboration.channel_instances_by_path_); + CreateNewStyleProcInstance(top, /*proc_instantiation=*/std::nullopt, path, + channel_instance_ptrs)); + + for (const std::unique_ptr& channel_instance : + elaboration.interface_channel_instances_) { + elaboration.channel_instance_ptrs_.push_back(channel_instance.get()); + } + elaboration.BuildInstanceMaps(elaboration.top_.get()); + return elaboration; } -std::string Elaboration::ToString() const { return top().ToString(); } +std::string Elaboration::ToString() const { return top()->ToString(); } absl::StatusOr Elaboration::GetProcInstance( const InstantiationPath& path) const { @@ -189,11 +248,20 @@ absl::StatusOr Elaboration::GetProcInstance( if (it == proc_instances_by_path_.end()) { return absl::NotFoundError(absl::StrFormat( "Instantiation path `%s` does not exist in elaboration from proc `%s`", - path.ToString(), top().proc()->name())); + path.ToString(), top()->proc()->name())); } return it->second; } +absl::StatusOr Elaboration::GetProcInstance( + std::string_view path_str) const { + XLS_ASSIGN_OR_RETURN(InstantiationPath path, CreatePath(path_str)); + if (path.path.empty()) { + return top(); + } + return GetProcInstance(path); +} + absl::StatusOr Elaboration::GetChannelInstance( std::string_view channel_name, const InstantiationPath& path) const { auto it = channel_instances_by_path_.find({std::string{channel_name}, path}); @@ -201,11 +269,18 @@ absl::StatusOr Elaboration::GetChannelInstance( return absl::NotFoundError( absl::StrFormat("No channel `%s` at instantiation path `%s` in " "elaboration from proc `%s`", - channel_name, path.ToString(), top().proc()->name())); + channel_name, path.ToString(), top()->proc()->name())); } return it->second; } +absl::StatusOr Elaboration::GetChannelInstance( + std::string_view channel_name, std::string_view path_str) const { + XLS_ASSIGN_OR_RETURN(InstantiationPath path, CreatePath(path_str)); + XLS_ASSIGN_OR_RETURN(ProcInstance * proc_instance, GetProcInstance(path)); + return proc_instance->GetChannelInstance(channel_name); +} + std::string InstantiationPath::ToString() const { if (path.empty()) { return top->name(); @@ -213,9 +288,123 @@ std::string InstantiationPath::ToString() const { return absl::StrFormat( "%s::%s", top->name(), absl::StrJoin( - path, "->", [](std::string* s, const ProcInstantiation* pi) { - absl::StrAppendFormat(s, "%s::%s", pi->name(), pi->proc()->name()); + path, "::", [](std::string* s, const ProcInstantiation* pi) { + absl::StrAppendFormat(s, "%s->%s", pi->name(), pi->proc()->name()); })); } +/*static*/ absl::StatusOr Elaboration::ElaborateOldStylePackage( + Package* package) { + // Iterate through every proc and channel and create a single instance for + // each. + Elaboration elaboration; + elaboration.package_ = package; + for (const std::unique_ptr& proc : package->procs()) { + XLS_RET_CHECK(!proc->is_new_style_proc()); + elaboration.proc_instances_.push_back(std::make_unique( + proc.get(), /*proc_instantiation=*/std::nullopt, + /*path=*/std::nullopt, + /*interface=*/absl::Span(), + /*channel_instances=*/std::vector>(), + /*instantiated_procs=*/std::vector>())); + elaboration.proc_instance_ptrs_.push_back( + elaboration.proc_instances_.back().get()); + + elaboration.instances_of_proc_[proc.get()] = { + elaboration.proc_instance_ptrs_.back()}; + } + for (Channel* channel : package->channels()) { + elaboration.channel_instances_.push_back(std::make_unique( + ChannelInstance{.channel = channel, .path = std::nullopt})); + elaboration.channel_instance_ptrs_.push_back( + elaboration.channel_instances_.back().get()); + + elaboration.instances_of_channel_[channel] = { + elaboration.channel_instance_ptrs_.back()}; + } + return std::move(elaboration); +} + +absl::Span Elaboration::GetInstances(Proc* proc) const { + if (!instances_of_proc_.contains(proc)) { + return {}; + } + return instances_of_proc_.at(proc); +} + +absl::Span Elaboration::GetInstances( + Channel* channel) const { + if (!instances_of_channel_.contains(channel)) { + return {}; + } + return instances_of_channel_.at(channel); +} + +absl::Span Elaboration::GetInstancesOfChannelReference( + ChannelReference* channel_reference) const { + if (!instances_of_channel_reference_.contains(channel_reference)) { + return {}; + } + return instances_of_channel_reference_.at(channel_reference); +} + +absl::StatusOr Elaboration::GetUniqueInstance(Proc* proc) const { + absl::Span instances = GetInstances(proc); + if (instances.size() != 1) { + return absl::InvalidArgumentError(absl::StrFormat( + "There is not exactly 1 instance of proc `%s`, instance count: %d", + proc->name(), instances.size())); + } + return instances.front(); +} + +absl::StatusOr Elaboration::GetUniqueInstance( + Channel* channel) const { + absl::Span instances = GetInstances(channel); + if (instances.size() != 1) { + return absl::InvalidArgumentError(absl::StrFormat( + "There is not exactly 1 instance of channel `%s`, instance count: %d", + channel->name(), instances.size())); + } + return instances.front(); +} + +absl::StatusOr Elaboration::CreatePath( + std::string_view path_str) const { + std::vector pieces = absl::StrSplit(path_str, "::"); + if (pieces.front() != top()->proc()->name()) { + return absl::InvalidArgumentError( + absl::StrFormat("Path top `%s` does not match name of top proc `%s`", + pieces.front(), top()->proc()->name())); + } + InstantiationPath path; + path.top = top()->proc(); + Proc* proc = path.top; + for (std::string_view piece : absl::MakeSpan(pieces).subspan(1)) { + std::vector parts = absl::StrSplit(piece, "->"); + if (parts.size() != 2) { + return absl::InvalidArgumentError( + absl::StrFormat("Invalid component of path `%s`. Expected form: " + "`instantiation->proc`.", + piece)); + } + absl::StatusOr instantiation = + proc->GetProcInstantiation(parts[0]); + if (!instantiation.ok()) { + return absl::InvalidArgumentError( + absl::StrFormat("Proc `%s` does not have an instantiation named `%s`", + proc->name(), parts[0])); + } + if (parts[1] != (*instantiation)->proc()->name()) { + return absl::InvalidArgumentError(absl::StrFormat( + "Instantiation `%s` in proc `%s` instantiates proc `%s`, but path " + "element is `%s`", + parts[0], proc->name(), (*instantiation)->proc()->name(), parts[1])); + } + path.path.push_back(*instantiation); + proc = (*instantiation)->proc(); + } + return path; +} + } // namespace xls diff --git a/xls/ir/elaboration.h b/xls/ir/elaboration.h index 28b52a1b64..0e140c7c7d 100644 --- a/xls/ir/elaboration.h +++ b/xls/ir/elaboration.h @@ -28,6 +28,7 @@ #include "absl/status/statusor.h" #include "absl/types/span.h" #include "xls/ir/channel.h" +#include "xls/ir/package.h" #include "xls/ir/proc.h" #include "xls/ir/proc_instantiation.h" @@ -104,7 +105,12 @@ struct InstantiationPath { struct ChannelInstance { Channel* channel; - InstantiationPath path; + + // Instantiation path of the proc instance in which this channel is + // defined. Is nullopt for old-style channels. + std::optional path; + + std::string ToString() const; }; // Representation of an instance of a proc. This is a recursive data structure @@ -112,12 +118,17 @@ struct ChannelInstance { // instance including recursively. class ProcInstance { public: - // Creates and returns a ProcInstance for the given proc. Walks and constructs - // the tree of proc instances beneath this one. - static absl::StatusOr> Create( - Proc* proc, std::optional proc_instantiation, - const InstantiationPath& path, - absl::Span interface); + ProcInstance(Proc* proc, std::optional proc_instantiation, + std::optional path, + absl::Span interface, + std::vector> channel_instances, + std::vector> instantiated_procs) + : proc_(proc), + proc_instantiation_(proc_instantiation), + path_(std::move(path)), + interface_(interface.begin(), interface.end()), + channels_(std::move(channel_instances)), + instantiated_procs_(std::move(instantiated_procs)) {} Proc* proc() const { return proc_; } @@ -128,8 +139,9 @@ class ProcInstance { return proc_instantiation_; } - // The path to this proc instance through the proc hierarchy/ - const InstantiationPath& path() const { return path_; } + // The path to this proc instance through the proc hierarchy. This is nullopt + // for old-style procs. + const std::optional& path() const { return path_; } // The ChannelInstances comprising the interface of this proc instance. absl::Span interface() const { return interface_; } @@ -145,21 +157,24 @@ class ProcInstance { return instantiated_procs_; } + // Returns the ChannelInstance with the given name in this proc instance. The + // channel instance can refer to an interface channel or a channel defined in + // the proc. + absl::StatusOr GetChannelInstance( + std::string_view channel_name) const; + + // Returns a unique name for this proc instantiation. For new-style procs this + // includes the proc name and the instantiation path. For old-style procs this + // is simply the proc name. + std::string GetName() const; + // Return a nested representation of the proc instance. std::string ToString(int64_t indent_amount = 0) const; private: - ProcInstance(Proc* proc, std::optional proc_instantiation, - const InstantiationPath& path, - absl::Span interface) - : proc_(proc), - proc_instantiation_(proc_instantiation), - path_(path), - interface_(interface.begin(), interface.end()) {} - Proc* proc_; std::optional proc_instantiation_; - InstantiationPath path_; + std::optional path_; std::vector interface_; // Channel and proc instances in this proc instance. Unique pointers are used @@ -173,7 +188,15 @@ class Elaboration { public: static absl::StatusOr Elaborate(Proc* top); - const ProcInstance& top() const { return *top_; } + // Elaborate the package of old style procs. This generates a single instance + // for each proc and channel in the package. The instance paths of each object + // are std::nullopt. + + // TODO(https://github.com/google/xls/issues/869): Remove when all procs are + // new style. + static absl::StatusOr ElaborateOldStylePackage(Package* package); + + ProcInstance* top() const { return top_.get(); } std::string ToString() const; @@ -183,9 +206,68 @@ class Elaboration { absl::StatusOr GetChannelInstance( std::string_view channel_name, const InstantiationPath& path) const; + // Returns the proc/channel instance at the given path where the path is given + // as a serialization (e.g., `top_proc::inst->other_proc`). + absl::StatusOr GetProcInstance( + std::string_view path_str) const; + absl::StatusOr GetChannelInstance( + std::string_view channel_name, std::string_view path_str) const; + + // Return a vector of all proc or channel instances in the elaboration. + absl::Span proc_instances() const { + return proc_instance_ptrs_; + } + absl::Span channel_instances() const { + return channel_instance_ptrs_; + } + + // Return all instances of a particular channel/proc. + absl::Span GetInstances(Proc* proc) const; + absl::Span GetInstances(Channel* channel) const; + + // Return all channel instances which the given channel reference is bound to + // in the elaboration. + absl::Span GetInstancesOfChannelReference( + ChannelReference* channel_reference) const; + + // Return the unique instance of the given proc/channel. Returns an error if + // there is not exactly one instance associated with the IR object. + absl::StatusOr GetUniqueInstance(Proc* proc) const; + absl::StatusOr GetUniqueInstance(Channel* channel) const; + + Package* package() const { return package_; } + + // Create path from the given path string serialization. Example input: + // + // top_proc::inst1->other_proc::inst2->that_proc + // + // The return path will have the Proc pointer to `top_proc` as the top of the + // path, with an instantiation path containing the ProcInstantiation pointers: + // {inst1, inst2}. + // + // Returns an error if the path does not exist in the elaboration. + absl::StatusOr CreatePath(std::string_view path_str) const; + private: + // Walks the hierarchy and builds the data member maps of instances. Only + // should be called for new-style procs. + void BuildInstanceMaps(ProcInstance* proc_instance); + + Package* package_; + + // For a new style procs this is the top-level instantiation. All other + // ProcInstances are contained within this instance. std::unique_ptr top_; + // For non-new-style procs, this is the list of proc/channel instantiations, + // one per proc in the package. + std::vector> proc_instances_; + std::vector> channel_instances_; + + // Vectors of all proc/channel instances in the elaboration. + std::vector proc_instance_ptrs_; + std::vector channel_instance_ptrs_; + // Channel object for the interface of the top-level proc. This is necessary // as there are no associated Channel objects in the IR. // TODO(https://github.com/google/xls/issues/869): An IR object should @@ -204,6 +286,15 @@ class Elaboration { absl::flat_hash_map, ChannelInstance*> channel_instances_by_path_; + + // List of instances of each Proc/Channel. + absl::flat_hash_map> instances_of_proc_; + absl::flat_hash_map> + instances_of_channel_; + + // List of channel instances for each channel reference. + absl::flat_hash_map> + instances_of_channel_reference_; }; } // namespace xls diff --git a/xls/ir/elaboration_test.cc b/xls/ir/elaboration_test.cc index 245731a9dc..4434c41104 100644 --- a/xls/ir/elaboration_test.cc +++ b/xls/ir/elaboration_test.cc @@ -27,6 +27,7 @@ #include "xls/common/status/matchers.h" #include "xls/common/status/status_macros.h" #include "xls/ir/channel.h" +#include "xls/ir/channel_ops.h" #include "xls/ir/function_builder.h" #include "xls/ir/ir_test_base.h" #include "xls/ir/package.h" @@ -42,20 +43,6 @@ using ::testing::HasSubstr; class ElaborationTest : public IrTestBase {}; -static InstantiationPath MakeInstantiationPath( - Proc* top, absl::Span instantiations) { - InstantiationPath path; - path.top = top; - Proc* proc = top; - for (std::string_view instantiation_name : instantiations) { - ProcInstantiation* instantiation = - proc->GetProcInstantiation(instantiation_name).value(); - path.path.push_back(instantiation); - proc = instantiation->proc(); - } - return path; -} - absl::StatusOr CreateLeafProc(std::string_view name, int64_t input_channel_count, Package* package) { @@ -116,9 +103,18 @@ TEST_F(ElaborationTest, SingleProcNoChannels) { Proc * proc, CreateLeafProc("foo", /*input_channel_count=*/0, p.get())); XLS_ASSERT_OK_AND_ASSIGN(Elaboration elab, Elaboration::Elaborate(proc)); - EXPECT_EQ(elab.top().proc(), proc); - EXPECT_EQ(elab.top().path().ToString(), "foo"); - EXPECT_TRUE(elab.top().interface().empty()); + EXPECT_EQ(elab.top()->proc(), proc); + EXPECT_TRUE(elab.top()->path().has_value()); + EXPECT_EQ(elab.top()->path()->ToString(), "foo"); + EXPECT_TRUE(elab.top()->interface().empty()); + + ASSERT_EQ(elab.proc_instances().size(), 1); + EXPECT_EQ(elab.proc_instances().front()->proc(), proc); + + EXPECT_TRUE(elab.channel_instances().empty()); + + EXPECT_EQ(elab.GetInstances(proc).size(), 1); + EXPECT_EQ(elab.GetInstances(proc).front(), elab.proc_instances().front()); EXPECT_EQ(elab.ToString(), "foo<>"); } @@ -129,16 +125,33 @@ TEST_F(ElaborationTest, SingleProcMultipleChannels) { Proc * proc, CreateLeafProc("foo", /*input_channel_count=*/3, p.get())); XLS_ASSERT_OK_AND_ASSIGN(Elaboration elab, Elaboration::Elaborate(proc)); - EXPECT_EQ(elab.top().proc(), proc); - EXPECT_FALSE(elab.top().proc_instantiation().has_value()); - EXPECT_EQ(elab.top().path().ToString(), "foo"); - EXPECT_EQ(elab.top().interface().size(), 3); - EXPECT_EQ(elab.top().interface()[0]->channel->name(), "ch0"); - EXPECT_EQ(elab.top().interface()[0]->path.ToString(), "foo"); - EXPECT_EQ(elab.top().interface()[1]->channel->name(), "ch1"); - EXPECT_EQ(elab.top().interface()[1]->path.ToString(), "foo"); - EXPECT_EQ(elab.top().interface()[2]->channel->name(), "ch2"); - EXPECT_EQ(elab.top().interface()[2]->path.ToString(), "foo"); + EXPECT_EQ(elab.top()->proc(), proc); + EXPECT_FALSE(elab.top()->proc_instantiation().has_value()); + EXPECT_TRUE(elab.top()->path().has_value()); + EXPECT_EQ(elab.top()->path()->ToString(), "foo"); + EXPECT_EQ(elab.top()->interface().size(), 3); + + ASSERT_EQ(elab.proc_instances().size(), 1); + EXPECT_EQ(elab.proc_instances().front()->proc(), proc); + + EXPECT_EQ(elab.channel_instances().size(), 3); + EXPECT_EQ(elab.channel_instances()[0]->channel->name(), "ch0"); + EXPECT_THAT(elab.top()->GetChannelInstance("ch0"), + IsOkAndHolds(elab.channel_instances()[0])); + EXPECT_EQ(elab.channel_instances()[1]->channel->name(), "ch1"); + EXPECT_THAT(elab.top()->GetChannelInstance("ch1"), + IsOkAndHolds(elab.channel_instances()[1])); + EXPECT_EQ(elab.channel_instances()[2]->channel->name(), "ch2"); + EXPECT_THAT(elab.top()->GetChannelInstance("ch2"), + IsOkAndHolds(elab.channel_instances()[2])); + + EXPECT_EQ(elab.top()->interface()[0]->channel->name(), "ch0"); + EXPECT_FALSE(elab.top()->interface()[0]->path.has_value()); + EXPECT_EQ(elab.top()->interface()[1]->channel->name(), "ch1"); + EXPECT_FALSE(elab.top()->interface()[1]->path.has_value()); + EXPECT_EQ(elab.top()->interface()[2]->channel->name(), "ch2"); + EXPECT_FALSE(elab.top()->interface()[2]->path.has_value()); + EXPECT_EQ(elab.ToString(), "foo"); } @@ -158,18 +171,17 @@ TEST_F(ElaborationTest, ProcInstantiatingProc) { XLS_ASSERT_OK_AND_ASSIGN(Elaboration elab, Elaboration::Elaborate(top)); - EXPECT_EQ(elab.top().proc(), top); - EXPECT_EQ(elab.top().path().ToString(), "top_proc"); - EXPECT_EQ(elab.top().instantiated_procs().size(), 1); - EXPECT_EQ(elab.top().channels().size(), 1); + EXPECT_EQ(elab.top()->proc(), top); + EXPECT_EQ(elab.top()->path()->ToString(), "top_proc"); + EXPECT_EQ(elab.top()->instantiated_procs().size(), 1); + EXPECT_EQ(elab.top()->channels().size(), 1); - EXPECT_THAT(elab.GetProcInstance(MakeInstantiationPath(top, {})), - IsOkAndHolds(&elab.top())); - EXPECT_THAT(elab.GetChannelInstance("the_ch", MakeInstantiationPath(top, {})), - IsOkAndHolds(elab.top().channels().front().get())); + EXPECT_THAT(elab.GetProcInstance("top_proc"), IsOkAndHolds(elab.top())); + EXPECT_THAT(elab.GetChannelInstance("the_ch", "top_proc"), + IsOkAndHolds(elab.top()->channels().front().get())); - ProcInstance* leaf_instance = elab.top().instantiated_procs().front().get(); - EXPECT_THAT(elab.GetProcInstance(MakeInstantiationPath(top, {"leaf_inst"})), + ProcInstance* leaf_instance = elab.top()->instantiated_procs().front().get(); + EXPECT_THAT(elab.GetProcInstance("top_proc::leaf_inst->leaf"), IsOkAndHolds(leaf_instance)); EXPECT_EQ(elab.ToString(), R"(top_proc @@ -199,13 +211,14 @@ TEST_F(ElaborationTest, ProcInstantiatingProcInstantiatedProcEtc) { XLS_ASSERT_OK_AND_ASSIGN(Elaboration elab, Elaboration::Elaborate(top)); - XLS_ASSERT_OK_AND_ASSIGN(ProcInstance * leaf_inst, - elab.GetProcInstance(MakeInstantiationPath( - top, {"inst1", "inst_proc0", "inst_foo"}))); + XLS_ASSERT_OK_AND_ASSIGN( + ProcInstance * leaf_inst, + elab.GetProcInstance( + "top_proc::inst1->proc1::inst_proc0->proc0::inst_foo->foo")); EXPECT_EQ(leaf_inst->proc(), leaf_proc); - EXPECT_EQ(elab.top().proc(), top); - EXPECT_EQ(elab.top().path().ToString(), "top_proc"); + EXPECT_EQ(elab.top()->proc(), top); + EXPECT_EQ(elab.top()->path()->ToString(), "top_proc"); EXPECT_EQ(elab.ToString(), R"(top_proc proc1 [inst1] proc0 [inst_proc0] @@ -232,8 +245,48 @@ TEST_F(ElaborationTest, MultipleInstantiations) { XLS_ASSERT_OK_AND_ASSIGN(Elaboration elab, Elaboration::Elaborate(top)); - EXPECT_EQ(elab.top().proc(), top); - EXPECT_EQ(elab.top().path().ToString(), "top_proc"); + EXPECT_EQ(elab.GetInstances(leaf_proc).size(), 7); + + EXPECT_EQ(elab.GetInstances(leaf_proc)[0]->proc(), leaf_proc); + EXPECT_EQ(elab.GetInstances(leaf_proc)[0]->path()->ToString(), + "top_proc::inst0->middle::inst0->leaf"); + + EXPECT_EQ(elab.GetInstances(leaf_proc)[1]->proc(), leaf_proc); + EXPECT_EQ(elab.GetInstances(leaf_proc)[1]->path()->ToString(), + "top_proc::inst0->middle::inst1->leaf"); + + EXPECT_EQ(elab.GetInstances(leaf_proc)[2]->proc(), leaf_proc); + EXPECT_EQ(elab.GetInstances(leaf_proc)[2]->path()->ToString(), + "top_proc::inst0->middle::inst2->leaf"); + + EXPECT_EQ(elab.GetInstances(leaf_proc)[3]->proc(), leaf_proc); + EXPECT_EQ(elab.GetInstances(leaf_proc)[3]->path()->ToString(), + "top_proc::inst1->middle::inst0->leaf"); + + EXPECT_EQ(elab.GetInstances(leaf_proc)[6]->proc(), leaf_proc); + EXPECT_EQ(elab.GetInstances(leaf_proc)[6]->path()->ToString(), + "top_proc::inst2->leaf"); + + EXPECT_EQ(elab.GetInstances(middle_proc->channels()[0]).size(), 2); + EXPECT_EQ(elab.GetInstances(middle_proc->channels()[1]).size(), 2); + + EXPECT_EQ( + elab.GetInstancesOfChannelReference(leaf_proc->interface()[0]).size(), 7); + EXPECT_EQ( + elab.GetInstancesOfChannelReference(leaf_proc->interface()[1]).size(), 7); + + EXPECT_EQ(elab.GetInstancesOfChannelReference(leaf_proc->interface()[0])[0] + ->path->ToString(), + "top_proc::inst0->middle"); + EXPECT_EQ(elab.GetInstancesOfChannelReference(leaf_proc->interface()[0])[1] + ->path->ToString(), + "top_proc::inst0->middle"); + EXPECT_EQ(elab.GetInstancesOfChannelReference(leaf_proc->interface()[0])[6] + ->path->ToString(), + "top_proc"); + + EXPECT_EQ(elab.top()->proc(), top); + EXPECT_EQ(elab.top()->path()->ToString(), "top_proc"); EXPECT_EQ(elab.ToString(), R"(top_proc chan ch0 chan ch1 @@ -263,13 +316,13 @@ TEST_F(ElaborationTest, ProcInstantiatingProcWithNoChannels) { XLS_ASSERT_OK_AND_ASSIGN(Elaboration elab, Elaboration::Elaborate(top)); - EXPECT_EQ(elab.top().proc(), top); - EXPECT_EQ(elab.top().path().ToString(), "top_proc"); + EXPECT_EQ(elab.top()->proc(), top); + EXPECT_EQ(elab.top()->path()->ToString(), "top_proc"); EXPECT_EQ(elab.ToString(), R"(top_proc<> foo<> [foo_inst])"); } -TEST_F(ElaborationTest, ElaborateOldStyleProc) { +TEST_F(ElaborationTest, ElaborateOldStyleProcWithWrongMethod) { auto p = CreatePackage(); TokenlessProcBuilder pb("old_style_proc", "tkn", p.get()); XLS_ASSERT_OK_AND_ASSIGN(Proc * top, pb.Build({})); @@ -280,5 +333,62 @@ TEST_F(ElaborationTest, ElaborateOldStyleProc) { HasSubstr("Cannot elaborate old-style proc `old_style_proc`"))); } +TEST_F(ElaborationTest, ElaborateOldStyleProc) { + auto p = CreatePackage(); + TokenlessProcBuilder pb("old_style_proc", "tkn", p.get()); + XLS_ASSERT_OK_AND_ASSIGN(Proc * top, pb.Build({})); + + XLS_ASSERT_OK_AND_ASSIGN(Elaboration elab, + Elaboration::ElaborateOldStylePackage(p.get())); + + ASSERT_EQ(elab.proc_instances().size(), 1); + absl::Span proc_instances = elab.GetInstances(top); + EXPECT_EQ(proc_instances, elab.proc_instances()); + EXPECT_EQ(top, proc_instances.front()->proc()); + + EXPECT_TRUE(elab.channel_instances().empty()); +} + +TEST_F(ElaborationTest, ElaborateOldStyleMultiprocNetwork) { + Package p("package"); + + Type* u32 = p.GetBitsType(32); + XLS_ASSERT_OK_AND_ASSIGN( + StreamingChannel * ch1, + p.CreateStreamingChannel("ch1", ChannelOps::kSendReceive, u32)); + XLS_ASSERT_OK_AND_ASSIGN( + StreamingChannel * ch2, + p.CreateStreamingChannel("ch2", ChannelOps::kSendReceive, u32)); + XLS_ASSERT_OK_AND_ASSIGN( + StreamingChannel * ch3, + p.CreateStreamingChannel("ch3", ChannelOps::kSendReceive, u32)); + + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc1, + TokenlessProcBuilder("proc1", "tkn", &p).Build({})); + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc2, + TokenlessProcBuilder("proc2", "tkn", &p).Build({})); + XLS_ASSERT_OK_AND_ASSIGN(Proc * proc3, + TokenlessProcBuilder("proc3", "tkn", &p).Build({})); + + XLS_ASSERT_OK_AND_ASSIGN(Elaboration elab, + Elaboration::ElaborateOldStylePackage(&p)); + + ASSERT_EQ(elab.proc_instances().size(), 3); + EXPECT_EQ(elab.GetInstances(proc1).size(), 1); + EXPECT_EQ(elab.GetInstances(proc1).front()->proc(), proc1); + EXPECT_EQ(elab.GetInstances(proc2).size(), 1); + EXPECT_EQ(elab.GetInstances(proc2).front()->proc(), proc2); + EXPECT_EQ(elab.GetInstances(proc3).size(), 1); + EXPECT_EQ(elab.GetInstances(proc3).front()->proc(), proc3); + + EXPECT_EQ(elab.channel_instances().size(), 3); + EXPECT_EQ(elab.GetInstances(ch1).size(), 1); + EXPECT_EQ(elab.GetInstances(ch1).front()->channel, ch1); + EXPECT_EQ(elab.GetInstances(ch2).size(), 1); + EXPECT_EQ(elab.GetInstances(ch2).front()->channel, ch2); + EXPECT_EQ(elab.GetInstances(ch3).size(), 1); + EXPECT_EQ(elab.GetInstances(ch3).front()->channel, ch3); +} + } // namespace } // namespace xls