Fix strict weak ordering in dependency autoexpand graph In case of cyclic dependencies, sorting our graph with a simple `DependsOn()` results in issues, as we don't have a strict weak ordering anymore. Instead, prepare a rank look-up table that contains the counts of how many other modules in the buffer depend on it. In any DAG, a dependency has strictly more dependents than the module depending on it. The look-up table also provides stable ordering even if we have multiple disconnected graphs as we just compare a topological rank. Use that rank in the comparator and use the name as tie-breaker in case of cyclic dependencies. This was discovered by enabling debug mode stdlib which triggered strict weak ordering complaints in tests in `verible/verilog/tools/ls/autoexpand_test.cc` `AUTO_ExpandPortsMultipleConnectionsWithAUTO_TEMPLATE` `AUTO_ExpandPorts_DependencyLoop`
diff --git a/verible/verilog/tools/ls/autoexpand.cc b/verible/verilog/tools/ls/autoexpand.cc index 0f9d5c4..82f7de0 100644 --- a/verible/verilog/tools/ls/autoexpand.cc +++ b/verible/verilog/tools/ls/autoexpand.cc
@@ -1464,10 +1464,26 @@ module->RetrieveDependencies(modules_); } // Sort modules in the buffer based on a dependency graph, so that AUTOs are - // expanded in order + // expanded in order. Counting dependents and breaking ties by name induces a + // strict weak ordering even with dependency loops or independent modules. + absl::flat_hash_map<const Module *, int> dependents_count; + for (const Module *module : buffer_modules) { + int count = 0; + for (const Module *other : buffer_modules) { + if (other != module && other->DependsOn(module)) { + ++count; + } + } + dependents_count[module] = count; + } std::sort(buffer_modules.begin(), buffer_modules.end(), - [](const Module *left, const Module *right) { - return right->DependsOn(left); + [&dependents_count](const Module *left, const Module *right) { + const int left_count = dependents_count.at(left); + const int right_count = dependents_count.at(right); + if (left_count != right_count) { + return left_count > right_count; + } + return left->Name() < right->Name(); }); for (Module *const module : buffer_modules) { // Ports declared in AUTOINPUT/AUTOINOUT/AUTOOUTPUT must be removed from