diff --git a/src/buddies/src/bd/bdConverterMain.cc b/src/buddies/src/bd/bdConverterMain.cc index d44f9b05e..d3f418b06 100644 --- a/src/buddies/src/bd/bdConverterMain.cc +++ b/src/buddies/src/bd/bdConverterMain.cc @@ -27,6 +27,7 @@ #include "dbReader.h" #include "dbWriter.h" #include "tlCommandLineParser.h" +#include "tlTimer.h" namespace bd { @@ -53,6 +54,8 @@ int converter_main (int argc, char *argv[], const std::string &format) db::Layout layout; + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + { db::LoadLayoutOptions load_options; generic_reader_options.configure (load_options); diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index 53133d373..752d20edd 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -120,7 +120,9 @@ GenericReaderOptions::GenericReaderOptions () m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool (); m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool (); m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string (); - m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int (); + // Don't take the default, as in practice, it's more common to substitute LEF macros by layouts + // m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int (); + m_lefdef_macro_resolution_mode = 2; // "assume FOREIGN always" } void diff --git a/src/buddies/src/bd/strmclip.cc b/src/buddies/src/bd/strmclip.cc index ca36b2507..917b5a24d 100644 --- a/src/buddies/src/bd/strmclip.cc +++ b/src/buddies/src/bd/strmclip.cc @@ -29,6 +29,7 @@ #include "dbSaveLayoutOptions.h" #include "tlLog.h" #include "tlCommandLineParser.h" +#include "tlTimer.h" struct ClipData @@ -200,6 +201,8 @@ BD_PUBLIC int strmclip (int argc, char *argv[]) cmd.parse (argc, argv); + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + clip (data); return 0; diff --git a/src/buddies/src/bd/strmcmp.cc b/src/buddies/src/bd/strmcmp.cc index 70cc712e9..ccc31c55c 100644 --- a/src/buddies/src/bd/strmcmp.cc +++ b/src/buddies/src/bd/strmcmp.cc @@ -25,6 +25,7 @@ #include "dbLayoutDiff.h" #include "dbReader.h" #include "tlCommandLineParser.h" +#include "tlTimer.h" BD_PUBLIC int strmcmp (int argc, char *argv[]) { @@ -141,6 +142,8 @@ BD_PUBLIC int strmcmp (int argc, char *argv[]) throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given"); } + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + db::Layout layout_a; db::Layout layout_b; diff --git a/src/buddies/src/bd/strmrun.cc b/src/buddies/src/bd/strmrun.cc index fa15cf018..d13abefe7 100644 --- a/src/buddies/src/bd/strmrun.cc +++ b/src/buddies/src/bd/strmrun.cc @@ -28,6 +28,7 @@ #include "tlLog.h" #include "tlCommandLineParser.h" #include "tlFileUtils.h" +#include "tlTimer.h" #include "rba.h" #include "pya.h" #include "gsi.h" @@ -97,5 +98,8 @@ BD_PUBLIC int strmrun (int argc, char *argv[]) lym::Macro macro; macro.load_from (script); macro.set_file_path (script); + + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + return macro.run (); } diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 94ff7da1d..1a5965e83 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -32,6 +32,8 @@ #include "gsiExpression.h" #include "tlCommandLineParser.h" #include "tlThreads.h" +#include "tlThreadedWorkers.h" +#include "tlTimer.h" namespace { @@ -319,7 +321,8 @@ struct XORData dont_summarize_missing_layers (false), silent (false), no_summary (false), threads (0), tile_size (0.0), heal_results (false), - output_layout (0), output_cell (0) + output_layout (0), output_cell (0), + layers_missing (0) { } db::Layout *layout_a, *layout_b; @@ -336,6 +339,8 @@ struct XORData db::cell_index_type output_cell; std::map, db::LPLogicalLessFunc> l2l_map; std::map, ResultDescriptor> *results; + mutable int layers_missing; + mutable tl::Mutex lock; }; } @@ -455,6 +460,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) } } + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + db::Layout layout_a; db::Layout layout_b; @@ -572,14 +579,22 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) if (! silent && ! no_summary) { if (result) { - tl::info << "No differences found"; + tl::info << tl::to_string (tr ("No differences found")); } else { const char *line_format = " %-10s %-12s %s"; - const char *sep = " -------------------------------------------------------"; - tl::info << "Result summary (layers without differences are not shown):" << tl::endl; - tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep; + std::string headline; + if (deep) { + headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (hierarchical shape count)"))); + } else { + headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)"))); + } + + const char *sep = " ----------------------------------------------------------------"; + + tl::info << tl::to_string (tr ("Result summary (layers without differences are not shown):")) << tl::endl; + tl::info << headline << tl::endl << sep; int ti = -1; for (std::map, ResultDescriptor>::const_iterator r = results.begin (); r != results.end (); ++r) { @@ -587,17 +602,17 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) if (r->first.first != ti) { ti = r->first.first; if (tolerances[ti] > db::epsilon) { - tl::info << tl::endl << "Tolerance " << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl; - tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep; + tl::info << tl::endl << tl::to_string (tr ("Tolerance ")) << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl; + tl::info << headline << tl::endl << sep; } } std::string out ("-"); std::string value; if (r->second.layer_a < 0 && ! dont_summarize_missing_layers) { - value = "(no such layer in first layout)"; + value = tl::to_string (tr ("(no such layer in first layout)")); } else if (r->second.layer_b < 0 && ! dont_summarize_missing_layers) { - value = "(no such layer in second layout)"; + value = tl::to_string (tr ("(no such layer in second layout)")); } else if (! r->second.is_empty ()) { if (r->second.layer_output >= 0 && r->second.layout) { out = r->second.layout->get_properties (r->second.layer_output).to_string (); @@ -758,15 +773,174 @@ bool run_tiled_xor (const XORData &xor_data) return result; } -bool run_deep_xor (const XORData &xor_data) -{ - db::DeepShapeStore dss; - dss.set_threads (xor_data.threads); +class XORJob + : public tl::JobBase +{ +public: + XORJob (int nworkers) + : tl::JobBase (nworkers) + { + } + + virtual tl::Worker *create_worker (); +}; + +class XORWorker + : public tl::Worker +{ +public: + XORWorker (XORJob *job); + void perform_task (tl::Task *task); + + db::DeepShapeStore &dss () + { + return m_dss; + } + +private: + XORJob *mp_job; + db::DeepShapeStore m_dss; +}; + +class XORTask + : public tl::Task +{ +public: + XORTask (const XORData *xor_data, const db::LayerProperties &layer_props, int la, int lb, double dbu) + : mp_xor_data (xor_data), m_layer_props (layer_props), m_la (la), m_lb (lb), m_dbu (dbu) + { + // .. nothing yet .. + } + + void run (XORWorker *worker) const + { + if ((m_la < 0 || m_lb < 0) && ! mp_xor_data->dont_summarize_missing_layers) { + + if (m_la < 0) { + (mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in first layout, but in second"; + } else { + (mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in second layout, but in first"; + } + + tl::MutexLocker locker (&mp_xor_data->lock); + + mp_xor_data->layers_missing += 1; + + int tol_index = 0; + for (std::vector::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) { + + ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second; + result.layer_a = m_la; + result.layer_b = m_lb; + result.layout = mp_xor_data->output_layout; + result.top_cell = mp_xor_data->output_cell; + + ++tol_index; + + } + + } else { + + tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + m_layer_props.to_string ()); + + db::RecursiveShapeIterator ri_a, ri_b; + + if (m_la >= 0) { + ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la); + } else { + ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), std::vector ()); + } + ri_a.set_for_merged_input (true); + + if (m_lb >= 0) { + ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb); + } else { + ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), std::vector ()); + } + ri_b.set_for_merged_input (true); + + db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu)); + db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu)); + + db::Region xor_res; + { + tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ()); + xor_res = in_a ^ in_b; + } + + int tol_index = 0; + for (std::vector::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) { + + db::LayerProperties lp = m_layer_props; + if (lp.layer >= 0) { + lp.layer += tol_index * mp_xor_data->tolerance_bump; + } + + if (*t > db::epsilon) { + tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + m_layer_props.to_string ()); + xor_res.size (-db::coord_traits::rounded (0.5 * *t / m_dbu)); + xor_res.size (db::coord_traits::rounded (0.5 * *t / m_dbu)); + } + + { + tl::MutexLocker locker (&mp_xor_data->lock); + + ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second; + result.layer_a = m_la; + result.layer_b = m_lb; + result.layout = mp_xor_data->output_layout; + result.top_cell = mp_xor_data->output_cell; + + if (mp_xor_data->output_layout) { + result.layer_output = result.layout->insert_layer (lp); + xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output); + } else { + result.shape_count = xor_res.hier_count (); + } + } + + ++tol_index; + + } + + } + } + +private: + const XORData *mp_xor_data; + const db::LayerProperties &m_layer_props; + int m_la; + int m_lb; + double m_dbu; +}; + +XORWorker::XORWorker (XORJob *job) + : tl::Worker (), mp_job (job) +{ // TODO: this conflicts with the "set_for_merged_input" optimization below. // It seems not to be very effective then. Why? - dss.set_wants_all_cells (true); // saves time for less cell mapping operations + m_dss.set_wants_all_cells (true); // saves time for less cell mapping operations +} +void +XORWorker::perform_task (tl::Task *task) +{ + XORTask *xor_task = dynamic_cast (task); + if (xor_task) { + xor_task->run (this); + } +} + +tl::Worker * +XORJob::create_worker () +{ + return new XORWorker (this); +} + + +bool run_deep_xor (const XORData &xor_data) +{ double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ()); if (tl::verbosity () >= 20) { @@ -779,98 +953,18 @@ bool run_deep_xor (const XORData &xor_data) xor_data.output_layout->dbu (dbu); } - bool result = true; - - int index = 1; + XORJob job (xor_data.threads); for (std::map >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) { - - if ((ll->second.first < 0 || ll->second.second < 0) && ! xor_data.dont_summarize_missing_layers) { - - if (ll->second.first < 0) { - (xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in first layout, but in second"; - } else { - (xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in second layout, but in first"; - } - - result = false; - - int tol_index = 0; - for (std::vector::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) { - - ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second; - result.layer_a = ll->second.first; - result.layer_b = ll->second.second; - result.layout = xor_data.output_layout; - result.top_cell = xor_data.output_cell; - - ++tol_index; - - } - - } else { - - tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ()); - - db::RecursiveShapeIterator ri_a, ri_b; - - if (ll->second.first >= 0) { - ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); - ri_a.set_for_merged_input (true); - } - - if (ll->second.second >= 0) { - ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); - ri_b.set_for_merged_input (true); - } - - db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu)); - db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu)); - - db::Region xor_res; - { - tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ()); - xor_res = in_a ^ in_b; - } - - int tol_index = 0; - for (std::vector::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) { - - db::LayerProperties lp = ll->first; - if (lp.layer >= 0) { - lp.layer += tol_index * xor_data.tolerance_bump; - } - - ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second; - result.layer_a = ll->second.first; - result.layer_b = ll->second.second; - result.layout = xor_data.output_layout; - result.top_cell = xor_data.output_cell; - - if (*t > db::epsilon) { - tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ()); - xor_res.size (-db::coord_traits::rounded (0.5 * *t / dbu)); - xor_res.size (db::coord_traits::rounded (0.5 * *t / dbu)); - } - - if (xor_data.output_layout) { - result.layer_output = result.layout->insert_layer (lp); - xor_res.insert_into (xor_data.output_layout, xor_data.output_cell, result.layer_output); - } else { - result.shape_count = xor_res.count (); - } - - ++tol_index; - - } - - } - - ++index; - + job.schedule (new XORTask (&xor_data, ll->first, ll->second.first, ll->second.second, dbu)); } - // Determines the output status + job.start (); + job.wait (); + + // Determine the output status + + bool result = (xor_data.layers_missing == 0); for (std::map, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) { result = r->second.is_empty (); } diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index cde4afde0..c065bbb43 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -105,7 +105,7 @@ TEST(1A_Flat) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 3/0 30\n" " 6/0 6/0 41\n" " 8/1 8/1 1\n" @@ -146,8 +146,8 @@ TEST(1A_Deep) "Layer 10/0 is not present in first layout, but in second\n" "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" " 3/0 3/0 3\n" " 6/0 6/0 314\n" " 8/1 8/1 1\n" @@ -177,7 +177,7 @@ TEST(1B_Flat) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 - 30\n" " 6/0 - 41\n" " 8/1 - 1\n" @@ -206,9 +206,9 @@ TEST(1B_Deep) "Layer 10/0 is not present in first layout, but in second\n" "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" - " 3/0 - 30\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" + " 3/0 - 3\n" " 6/0 - 314\n" " 8/1 - 1\n" " 10/0 - (no such layer in first layout)\n" @@ -417,7 +417,7 @@ TEST(3_FlatCount) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 - 31\n" " 6/0 - 217\n" " 8/1 - 168\n" @@ -483,7 +483,7 @@ TEST(3_FlatCountHeal) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 - 30\n" " 6/0 - 41\n" " 8/1 - 1\n" @@ -756,3 +756,42 @@ TEST(6_Deep) "Layer 10/0 is not present in first layout, but in second\n" ); } + +TEST(7_OptimizeDeep) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_covered1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_covered2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au7d.oas"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", "-u", input_a.c_str (), input_b.c_str (), output.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons)); + EXPECT_EQ (cap.captured_text (), + "Result summary (layers without differences are not shown):\n" + "\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" + " 2/0 2/0 1\n" + " 3/0 3/0 8\n" + "\n" + ); +} diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index a4f51760f..ec8d16ef6 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -1571,6 +1571,17 @@ struct array_iterator } } + /** + * @brief Gets a value indicating whether the iterator is a synthetic one + * + * "is_singular" is true, if the iterator was default-created or with a single + * transformation. + */ + bool is_singular () const + { + return mp_base == 0; + } + private: trans_type m_trans; basic_array_iterator *mp_base; diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index b3bdd4e6a..7bc3dc712 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -354,6 +354,13 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & std::vector &new_cells = *(new_cells_ptr ? new_cells_ptr : &new_cells_int); std::vector new_cells_b; + std::vector > all_a2b; + for (std::vector::const_iterator b = cell_index_b.begin (); b != cell_index_b.end (); ++b) { + auto m = m_b2a_mapping.find (*b); + tl_assert (m != m_b2a_mapping.end ()); + all_a2b.push_back (std::make_pair (m->second, *b)); + } + std::set called_b; for (std::vector::const_iterator i = cell_index_b.begin (); i != cell_index_b.end (); ++i) { layout_b.cell (*i).collect_called_cells (called_b); @@ -368,6 +375,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b); new_cells.push_back (new_cell); new_cells_b.push_back (*b); + all_a2b.push_back (std::make_pair (new_cell, *b)); if (mapped_pairs) { mapped_pairs->push_back (std::make_pair (*b, new_cell)); @@ -378,34 +386,34 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & } } - if (! new_cells.empty ()) { + if (all_a2b.empty ()) { + return; + } - // Note: this avoids frequent cell index table rebuilds if source and target layout are identical - db::LayoutLocker locker (&layout_a); + // Note: this avoids frequent cell index table rebuilds if source and target layout are identical + db::LayoutLocker locker (&layout_a); - // Create instances for the new cells in layout A according to their instantiation in layout B - double mag = layout_b.dbu () / layout_a.dbu (); - for (size_t i = 0; i < new_cells.size (); ++i) { + // Create instances for the new cells in layout A according to their instantiation in layout B + double mag = layout_b.dbu () / layout_a.dbu (); + for (auto i = all_a2b.begin (); i != all_a2b.end (); ++i) { - const db::Cell &b = layout_b.cell (new_cells_b [i]); - for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) { + const db::Cell &b = layout_b.cell (i->second); + for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) { - if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) { + if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) { - db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]); + db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]); - db::Instance bi = pb->child_inst (); + db::Instance bi = pb->child_inst (); - db::CellInstArray bci = bi.cell_inst (); - bci.object ().cell_index (new_cells [i]); - bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ()); - - if (bi.has_prop_id ()) { - pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ())); - } else { - pa.insert (bci); - } + db::CellInstArray bci = bi.cell_inst (); + bci.object ().cell_index (i->first); + bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ()); + if (bi.has_prop_id ()) { + pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ())); + } else { + pa.insert (bci); } } diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index a86e2ef4a..aaa6e5fb3 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1054,12 +1054,7 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const { const DeepRegion *other_deep = dynamic_cast (other.delegate ()); - if (empty ()) { - - // Nothing to do - return other.delegate ()->clone (); - - } else if (other.empty ()) { + if (other.empty ()) { // Nothing to do return clone (); @@ -1068,6 +1063,18 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const return AsIfFlatRegion::xor_with (other, property_constraint); + } else if (empty ()) { + + // Nothing to do, but to maintain the normal behavior, we have to map the other + // input to our layout if neccessary + if (&other_deep->deep_layer ().layout () == &deep_layer ().layout ()) { + return other.delegate ()->clone (); + } else { + std::unique_ptr other_deep_mapped (dynamic_cast (clone ())); + other_deep_mapped->deep_layer ().add_from (other_deep->deep_layer ()); + return other_deep_mapped.release (); + } + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { return new DeepRegion (deep_layer ().derived ()); diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index ac3a31116..b54354858 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -152,15 +152,17 @@ static std::pair > compute_clip_variant (const db::Box & } HierarchyBuilder::HierarchyBuilder (db::Layout *target, unsigned int target_layer, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) - : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans) + : mp_target (target), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans) { set_shape_receiver (pipe); + reset (); } HierarchyBuilder::HierarchyBuilder (db::Layout *target, const db::ICplxTrans &trans, HierarchyBuilderShapeReceiver *pipe) - : mp_target (target), m_initial_pass (true), m_cm_new_entry (false), m_target_layer (0), m_wants_all_cells (false), m_trans (trans) + : mp_target (target), m_target_layer (0), m_wants_all_cells (false), m_trans (trans) { set_shape_receiver (pipe); + reset (); } HierarchyBuilder::~HierarchyBuilder () @@ -178,6 +180,8 @@ void HierarchyBuilder::reset () { m_initial_pass = true; + m_cm_new_entry = false; + mp_initial_cell = 0; m_cells_to_be_filled.clear (); @@ -186,7 +190,6 @@ HierarchyBuilder::reset () m_cells_seen.clear (); m_cell_stack.clear (); m_cm_entry = null_iterator; - m_cm_new_entry = false; } const std::pair & @@ -351,7 +354,6 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co if (! key.clip_region.empty ()) { cn += "$CLIP_VAR"; description += "CLIP"; - } if (key.inactive) { cn += "$DIS"; @@ -383,7 +385,7 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co } HierarchyBuilder::new_inst_mode -HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) +HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes) { if (all) { @@ -402,7 +404,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. - return (m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; + return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; } else { @@ -413,7 +415,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } bool -HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) +HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { if (all) { @@ -441,7 +443,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } } - return (m_cells_seen.find (key) == m_cells_seen.end ()); + return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end (); } } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 31a125f44..d1d056718 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -294,8 +294,8 @@ public: virtual void end (const RecursiveShapeIterator *iter); virtual void enter_cell (const RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region); virtual void leave_cell (const RecursiveShapeIterator *iter, const db::Cell *cell); - virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all); - virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes); + virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes); virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); /** diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index c92cd4bc4..b3965f9ce 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -74,6 +74,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_layer = d.m_layer; mp_cell = d.mp_cell; m_current_layer = d.m_current_layer; + m_skip_shapes = d.m_skip_shapes; + m_skip_shapes_member = d.m_skip_shapes_member; m_shape = d.m_shape; m_trans = d.m_trans; m_global_trans = d.m_global_trans; @@ -85,6 +87,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_local_complex_region_stack = d.m_local_complex_region_stack; m_local_region_stack = d.m_local_region_stack; m_skip_shapes_stack = d.m_skip_shapes_stack; + m_skip_shapes_member_stack = d.m_skip_shapes_member_stack; m_needs_reinit = d.m_needs_reinit; m_inst_quad_id = d.m_inst_quad_id; m_inst_quad_id_stack = d.m_inst_quad_id_stack; @@ -469,6 +472,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const m_local_region_stack.push_back (m_global_trans.inverted () * m_region); m_skip_shapes_stack.clear (); m_skip_shapes_stack.push_back (false); + m_skip_shapes_member_stack.clear (); + m_skip_shapes_member_stack.push_back (false); m_local_complex_region_stack.clear (); if (mp_complex_region.get ()) { @@ -814,39 +819,6 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const bool RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { - bool skip_shapes = false; - - if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) { - - // Try some optimization: if the instance we're looking at is entirely covered - // by a rectangle (other objects are too expensive to check), then we skip it - // - // We check 10 shapes max. - - box_type inst_bx; - if (m_inst->size () == 1) { - inst_bx = m_inst->bbox (m_box_convert); - } else { - inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); - } - - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); - size_t nmax = 10; - while (! si.at_end () && nmax-- > 0) { - if (inst_bx.inside (si->rectangle ())) { - skip_shapes = true; - break; - } - ++si; - } - - } - - if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) { - return false; - } - tl_assert (mp_layout); m_trans_stack.push_back (m_trans); @@ -874,7 +846,8 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } m_local_region_stack.push_back (new_region); - m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes); + m_skip_shapes_stack.push_back (m_skip_shapes); + m_skip_shapes_member_stack.push_back (m_skip_shapes_member); if (! m_local_complex_region_stack.empty ()) { @@ -948,6 +921,8 @@ RecursiveShapeIterator::pop () const m_inst = m_inst_iterators.back (); m_inst_array = m_inst_array_iterators.back (); m_inst_quad_id = m_inst_quad_id_stack.back (); + m_skip_shapes = m_skip_shapes_stack.back (); + m_skip_shapes_member = m_skip_shapes_member_stack.back (); m_inst_iterators.pop_back (); m_inst_array_iterators.pop_back (); m_inst_quad_id_stack.pop_back (); @@ -958,6 +933,7 @@ RecursiveShapeIterator::pop () const m_cells.pop_back (); m_local_region_stack.pop_back (); m_skip_shapes_stack.pop_back (); + m_skip_shapes_member_stack.pop_back (); if (! m_local_complex_region_stack.empty ()) { m_local_complex_region_stack.pop_back (); } @@ -982,7 +958,7 @@ RecursiveShapeIterator::start_shapes () const void RecursiveShapeIterator::new_layer () const { - if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { + if (skip_shapes () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { m_shape = shape_iterator (); } else if (! m_overlapping) { m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); @@ -1029,6 +1005,32 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const new_inst (receiver); } +bool +RecursiveShapeIterator::instance_is_covered (const box_type &inst_bx, unsigned int layer) const +{ + // Try some optimization: if the instance we're looking at is entirely covered + // by a rectangle (other objects are too expensive to check), then we skip it + // + // We check 10 shapes max. + + auto si = cell ()->shapes (layer).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); + size_t nmax = 10; + while (! si.at_end () && nmax-- > 0) { + if (inst_bx.inside (si->rectangle ())) { + return true; + } + ++si; + } + + return false; +} + +bool +RecursiveShapeIterator::skip_shapes () const +{ + return m_skip_shapes_stack.back () || m_skip_shapes_member_stack.back (); +} + void RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const { @@ -1055,9 +1057,19 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const all_of_instance = m_local_complex_region_stack.empty (); } + m_skip_shapes = skip_shapes (); + m_skip_shapes_member = false; + + if (m_for_merged_input && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) { + box_type inst_bx = m_inst->bbox (m_box_convert); + m_skip_shapes = instance_is_covered (inst_bx, m_has_layers ? m_layers.front () : m_layer); + } + RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all; if (receiver) { - ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance, m_skip_shapes); + } else if (m_skip_shapes) { + ni = RecursiveShapeReceiver::NI_skip; } if (ni == RecursiveShapeReceiver::NI_skip) { @@ -1095,7 +1107,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const // skip instance array members not part of the complex region while (! m_inst_array.at_end ()) { - db::Box ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ()); + box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ()); if (! is_outside_complex_region (ia_box)) { break; } else { @@ -1105,12 +1117,31 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const } - while (! m_inst_array.at_end () && receiver) { - if (receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance ())) { - break; - } else { - ++m_inst_array; + m_skip_shapes_member = false; + + while (! m_inst_array.at_end () && (m_for_merged_input || receiver)) { + + m_skip_shapes_member = m_skip_shapes; + if (m_for_merged_input && ! m_inst_array.is_singular () && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) { + + box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ()); + m_skip_shapes_member = instance_is_covered (ia_box, m_has_layers ? m_layers.front () : m_layer); + } + + bool skip = false; + if (receiver) { + skip = ! receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), is_all_of_instance (), m_skip_shapes_member); + } else { + skip = m_skip_shapes_member; + } + + if (skip) { + ++m_inst_array; + } else { + break; + } + } } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 26ee32b7e..b0e685686 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -868,6 +868,7 @@ private: mutable unsigned int m_layer; mutable const cell_type *mp_cell; mutable size_t m_current_layer; + mutable bool m_skip_shapes, m_skip_shapes_member; mutable shape_iterator m_shape; mutable cplx_trans_type m_trans; mutable std::vector m_trans_stack; @@ -876,7 +877,7 @@ private: mutable std::vector m_cells; mutable std::vector m_local_complex_region_stack; mutable std::vector m_local_region_stack; - mutable std::vector m_skip_shapes_stack; + mutable std::vector m_skip_shapes_stack, m_skip_shapes_member_stack; mutable bool m_needs_reinit; mutable size_t m_inst_quad_id; mutable std::vector m_inst_quad_id_stack; @@ -899,6 +900,8 @@ private: bool down (RecursiveShapeReceiver *receiver) const; void pop () const; + bool instance_is_covered (const box_type &inst_bx, unsigned int layer) const; + bool skip_shapes () const; bool is_outside_complex_region (const db::Box &box) const; void set_inactive (bool a) const @@ -1013,8 +1016,11 @@ public: * - NI_all: iterate all members through "new_inst_member" * - NI_single: iterate a single member (the first one) * - NI_skip: skips the whole array (not a single instance is iterated) + * + * The "skip_shapes" parameter indicates that the instance is visited with the + * purpose of skipping all shapes. This is used to implement the "for_merged" optimization. */ - virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } + virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return NI_all; } /** * @brief Enters a new array member of the instance @@ -1026,8 +1032,11 @@ public: * "all" is true, if an instance array is iterated in "all" mode (see new_inst). * * If this method returns false, this array instance (but not the whole array) is skipped and the cell is not entered. + * + * The "skip_shapes" parameter indicates that the instance member is visited with the + * purpose of skipping all shapes. This is used to implement the "for_merged" optimization. */ - virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } + virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return true; } /** * @brief Delivers a shape diff --git a/src/db/unit_tests/dbCellMappingTests.cc b/src/db/unit_tests/dbCellMappingTests.cc index b20c8ec66..f6ec6a767 100644 --- a/src/db/unit_tests/dbCellMappingTests.cc +++ b/src/db/unit_tests/dbCellMappingTests.cc @@ -485,8 +485,41 @@ TEST(7) cib.push_back (b1.cell_index ()); cib.push_back (b2.cell_index ()); cm.create_multi_mapping_full (h, cib, *g, cia); - EXPECT_EQ (m2s (cm, *g, h), "a0->b0;a1->b1;a2->b2;a3->a3;a4->a4;a5->a5"); + EXPECT_EQ (m2s (cm, h, *g), "b0->a0;b1->a1;b2->a2;a3->a3;a4->a4;a5->a5"); EXPECT_EQ (l2s (h), "b0#0:;b1#1:cell_index=3 r0 0,0,cell_index=4 r0 0,0;b2#2:cell_index=4 r0 0,0;a3#3:cell_index=4 r0 0,0,cell_index=5 r0 0,0;a4#4:;a5#5:"); } +// Issue #2014 +TEST(8) +{ + std::unique_ptr g (new db::Layout ()); + db::Cell &a (g->cell (g->add_cell ("a"))); + db::Cell &b (g->cell (g->add_cell ("b"))); + db::Cell &b1 (g->cell (g->add_cell ("b1"))); + db::Cell &b2 (g->cell (g->add_cell ("b2"))); + db::Cell &c (g->cell (g->add_cell ("c"))); + + b.insert (db::CellInstArray (db::CellInst (a.cell_index ()), db::Trans ())); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans ())); + b.insert (db::CellInstArray (db::CellInst (b1.cell_index ()), db::Trans ())); + b.insert (db::CellInstArray (db::CellInst (b2.cell_index ()), db::Trans ())); + + db::Layout h; + db::Cell &ha (h.cell (h.add_cell ("a"))); + db::Cell &hb (h.cell (h.add_cell ("b"))); + db::Cell &hc (h.cell (h.add_cell ("c"))); + + db::CellMapping cm; + std::vector cib, cia; + cia.push_back (a.cell_index ()); + cia.push_back (b.cell_index ()); + cia.push_back (c.cell_index ()); + cib.push_back (ha.cell_index ()); + cib.push_back (hb.cell_index ()); + cib.push_back (hc.cell_index ()); + cm.create_multi_mapping_full (h, cib, *g, cia); + EXPECT_EQ (m2s (cm, h, *g), "a->a;b->b;b1->b1;b2->b2;c->c"); + + EXPECT_EQ (l2s (h), "a#0:;b#1:cell_index=0 r0 0,0,cell_index=2 r0 0,0,cell_index=3 r0 0,0,cell_index=4 r0 0,0;c#2:;b1#3:;b2#4:"); +} diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 7c386f011..c7205fbf3 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -756,15 +756,25 @@ namespace { : public db::RecursiveShapeReceiver { public: - FlatPusher (std::set *boxes) : mp_boxes (boxes) { } + FlatPusher (std::set *boxes = 0) : mp_boxes (boxes ? boxes : &m_boxes) { } void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { mp_boxes->insert (trans * shape.bbox ()); } + std::string to_string () const + { + std::vector s; + for (auto i = mp_boxes->begin (); i != mp_boxes->end (); ++i) { + s.push_back (i->to_string ()); + } + return tl::join (s.begin (), s.end (), ";"); + } + private: std::set *mp_boxes; + std::set m_boxes; }; } @@ -1038,7 +1048,7 @@ public: m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/) { m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); if (all) { @@ -1048,7 +1058,7 @@ public: return NI_all; } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/) { m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (always_apply * trans); if (all) { @@ -1073,9 +1083,9 @@ class ReceiverRejectingACellInstanceArray public: ReceiverRejectingACellInstanceArray (db::cell_index_type rejected) : m_rejected (rejected) { } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); + LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes); return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip; } @@ -1089,9 +1099,9 @@ class ReceiverRejectingACellInstanceArrayExceptOne public: ReceiverRejectingACellInstanceArrayExceptOne (db::cell_index_type rejected) : m_rejected (rejected) { } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); + LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes); return inst.object ().cell_index () != m_rejected ? NI_all : NI_single; } @@ -1105,9 +1115,9 @@ class ReceiverRejectingACellInstance public: ReceiverRejectingACellInstance (db::cell_index_type rejected, const db::ICplxTrans &trans_rejected) : m_rejected (rejected), m_trans_rejected (trans_rejected) { } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all); + LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all, skip_shapes); return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; } @@ -1586,49 +1596,174 @@ TEST(12_ForMerged) db::RecursiveShapeIterator i1 (*g, c0, 0); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); std::vector lv; lv.push_back (0); i1 = db::RecursiveShapeIterator (*g, c0, lv); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); lv.push_back (1); // empty, but kills "for merged" optimization i1 = db::RecursiveShapeIterator (*g, c0, lv); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)"); + } i1.set_for_merged_input (true); x = collect(i1, *g); // no longer optimized EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)"); + } i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50)); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100)"); + } + i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)"); + } i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50)); i1.set_overlapping (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)"); + } i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)"); + } } +TEST(12b_ForMerged) +{ + std::unique_ptr g (new db::Layout ()); + g->insert_layer (0); + g->insert_layer (1); + db::Cell &c0 (g->cell (g->add_cell ())); + db::Cell &c1 (g->cell (g->add_cell ())); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (db::Box (0, 0, 3000, 2200)); + c1.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (2000, 1000)), db::Vector (0, 2000), db::Vector (2000, 0), 2l, 2l)); + + std::string x; + + db::RecursiveShapeIterator i1 (*g, c0, 0); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } + + i1.set_for_merged_input (false); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 2000)))); + + db::RecursiveShapeIterator i2 (*g, c0, 0); + + x = collect(i2, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)"); + EXPECT_EQ (collect_with_copy(i2, *g), x); + + { + FlatPusher f; + i2.reset (); + i2.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } + + i2.set_for_merged_input (true); + x = collect(i2, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)"); + EXPECT_EQ (collect_with_copy(i2, *g), x); + + { + FlatPusher f; + i2.reset (); + i2.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } +} TEST(13_ForMergedPerformance) { diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index ff229aeb9..d95144c75 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -38,7 +38,7 @@ namespace db // ----------------------------------------------------------------------------------- // Path resolution utility -std::string correct_path (const std::string &fn_in, const db::Layout &layout, const std::string &base_path) +std::vector correct_path (const std::string &fn_in, const db::Layout &layout, const std::string &base_path, bool glob) { const db::Technology *tech = layout.technology (); @@ -64,19 +64,28 @@ std::string correct_path (const std::string &fn_in, const db::Layout &layout, co if (tech && ! tech->base_path ().empty ()) { std::string new_fn = tl::combine_path (tech->base_path (), fn); if (tl::file_exists (new_fn)) { - return new_fn; + std::vector res; + res.push_back (new_fn); + return res; + } else if (glob) { + return tl::glob_expand (new_fn); } } if (! base_path.empty ()) { - return tl::combine_path (base_path, fn); - } else { - return fn; + fn = tl::combine_path (base_path, fn); } - } else { - return fn; } + + if (tl::file_exists (fn) || ! glob) { + std::vector res; + res.push_back (fn); + return res; + } else { + return tl::glob_expand (fn); + } + } // ----------------------------------------------------------------------------------- @@ -1059,7 +1068,7 @@ LEFDEFReaderState::read_map_file (const std::string &filename, db::Layout &layou std::map, std::vector > layer_map; for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - read_single_map_file (correct_path (*p, layout, base_path), layer_map); + read_single_map_file (correct_path (*p, layout, base_path, false).front (), layer_map); } // build an explicit layer mapping now. diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index 607410b6a..2a5736f0e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -52,7 +52,7 @@ struct MacroDesc; * @brief Correct a path relative to the stream and technology */ DB_PLUGIN_PUBLIC -std::string correct_path (const std::string &fn, const db::Layout &layout, const std::string &base_path); +std::vector correct_path (const std::string &fn, const db::Layout &layout, const std::string &base_path, bool glob); /** * @brief Convers a string to a MASKSHIFT index list diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 8d37fa283..06e42a0c7 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -141,11 +141,12 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { - std::string lp = correct_path (*l, layout, base_path); - - tl::InputStream lef_stream (lp); - tl::log << tl::to_string (tr ("Reading")) << " " << lp; - importer.read (lef_stream, layout, state); + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { + tl::InputStream lef_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + importer.read (lef_stream, layout, state); + } } @@ -164,14 +165,20 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { - std::string lp = correct_path (*l, layout, base_path); - lef_files_read.insert (tl::normalize_path (lp)); + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + lp); + if (lef_files_read.insert (tl::normalize_path (*lp)).second) { - tl::InputStream lef_stream (lp); - tl::log << tl::to_string (tr ("Reading")) << " " << lp; - importer.read_lef (lef_stream, layout, state); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + *lp); + + tl::InputStream lef_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + importer.read_lef (lef_stream, layout, state); + + } + + } } @@ -223,22 +230,25 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti tl::shared_collection macro_layout_object_holder; for (std::vector::const_iterator l = effective_options.begin_macro_layout_files (); l != effective_options.end_macro_layout_files (); ++l) { - std::string lp = correct_path (*l, layout, base_path); + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + lp); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + *lp); - tl::InputStream macro_layout_stream (lp); - tl::log << tl::to_string (tr ("Reading")) << " " << lp; - db::Layout *new_layout = new db::Layout (false); - macro_layout_object_holder.push_back (new_layout); - macro_layouts.push_back (new_layout); + tl::InputStream macro_layout_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + db::Layout *new_layout = new db::Layout (false); + macro_layout_object_holder.push_back (new_layout); + macro_layouts.push_back (new_layout); - db::Reader reader (macro_layout_stream); - reader.read (*new_layout, options); + db::Reader reader (macro_layout_stream); + reader.read (*new_layout, options); + + if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { + importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), + *lp, new_layout->dbu (), layout.dbu ())); + } - if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { - importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), - lp, new_layout->dbu (), layout.dbu ())); } } @@ -266,6 +276,14 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti } + // Warn about cells that could not be resolved + for (std::map::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) { + if (f->second != seen && layout.cell (f->second).is_ghost_cell ()) { + importer.warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")), + f->first)); + } + } + } state.finish (layout); diff --git a/src/rdb/rdb/rdbUtils.cc b/src/rdb/rdb/rdbUtils.cc index 6d0181162..4692b35bd 100644 --- a/src/rdb/rdb/rdbUtils.cc +++ b/src/rdb/rdb/rdbUtils.cc @@ -146,7 +146,7 @@ public: m_cell_stack.pop_back (); } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { db::cell_index_type ci = inst.object ().cell_index (); if (m_id_to_cell.find (ci) != m_id_to_cell.end ()) { diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index 7debc3591..1735d853f 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -25,6 +25,7 @@ #include "tlLog.h" #include "tlInternational.h" #include "tlEnv.h" +#include "tlGlobPattern.h" #include #include @@ -430,6 +431,63 @@ std::vector dir_entries (const std::string &s, bool with_files, boo return ee; } +static void glob_partial (const std::string &where, std::vector::const_iterator pfrom, std::vector::const_iterator pto, std::vector &res) +{ + if (pfrom == pto) { + if (! is_dir (where)) { + res.push_back (where); + } + return; + } + + auto p = where + *pfrom; + if (file_exists (p)) { + glob_partial (p, pfrom + 1, pto, res); + return; + } + + if (tl::trimmed_part (*pfrom) == "**") { + if (pfrom + 1 == pto) { + // a glob pattern can't be "**" without anything after that + return; + } + auto subdirs = dir_entries (where, false, true, true); + for (auto s = subdirs.begin (); s != subdirs.end (); ++s) { + glob_partial (combine_path (where, *s), pfrom, pto, res); + } + ++pfrom; + } + +#if defined(_WIN32) + if (where.empty ()) { + // On Windows, we cannot iterate the drives + std::string root = *pfrom; + ++pfrom; + glob_partial (root, pfrom, pto, res); + return; + } +#endif + + tl::GlobPattern glob (tl::trimmed_part (*pfrom)); + ++pfrom; + auto entries = dir_entries (where, true, true, true); + for (auto e = entries.begin (); e != entries.end (); ++e) { + if (glob.match (*e)) { + glob_partial (combine_path (where, *e), pfrom, pto, res); + } + } +} + +std::vector glob_expand (const std::string &path) +{ + auto apath = absolute_file_path (path); + auto parts = split_path (apath); + + std::vector res; + glob_partial (std::string (), parts.begin (), parts.end (), res); + return res; +} + bool mkdir (const std::string &path) { #if defined(_WIN32) diff --git a/src/tl/tl/tlFileUtils.h b/src/tl/tl/tlFileUtils.h index 3456eb6da..4401ac41b 100644 --- a/src/tl/tl/tlFileUtils.h +++ b/src/tl/tl/tlFileUtils.h @@ -138,6 +138,14 @@ bool TL_PUBLIC is_dir (const std::string &s); */ std::vector TL_PUBLIC dir_entries (const std::string &s, bool with_files = true, bool with_dirs = true, bool without_dotfiles = false); +/** + * @brief Expands a glob pattern into a set of files + * + * This version supports "**" for recursive directory expansion. + * Apart from that the features of tl::GlobPattern are supported. + */ +std::vector TL_PUBLIC glob_expand (const std::string &path); + /** * @brief Rename the given file */ diff --git a/src/tl/unit_tests/tlFileUtilsTests.cc b/src/tl/unit_tests/tlFileUtilsTests.cc index c12776608..2c5e210cc 100644 --- a/src/tl/unit_tests/tlFileUtilsTests.cc +++ b/src/tl/unit_tests/tlFileUtilsTests.cc @@ -995,3 +995,94 @@ TEST (24) EXPECT_EQ (tl::file_exists (p), false); } +// glob_expand +TEST (25) +{ + tl::TemporaryDirectory tmpdir ("tl_tests"); + auto p = tmpdir.path (); + + auto ad = tl::combine_path (p, "a"); + tl::mkpath (ad); + auto aad = tl::combine_path (ad, "a"); + tl::mkpath (aad); + auto aaad = tl::combine_path (aad, "a"); + tl::mkpath (aaad); + auto bd = tl::combine_path (p, "b"); + tl::mkpath (bd); + + { + std::ofstream os (tl::combine_path (ad, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (aad, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (aaad, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (aaad, "test2.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (bd, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (p, "test2.txt")); + os << "A test"; + os.close (); + } + + std::vector au; + + auto res = tl::glob_expand (tl::combine_path (p, "*.txt")); + au.push_back (tl::combine_path (p, "test2.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); + + res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*.txt")); + au.clear (); + au.push_back (tl::combine_path (p, "test2.txt")); + au.push_back (tl::combine_path (ad, "test.txt")); + au.push_back (tl::combine_path (aad, "test.txt")); + au.push_back (tl::combine_path (aaad, "test.txt")); + au.push_back (tl::combine_path (aaad, "test2.txt")); + au.push_back (tl::combine_path (bd, "test.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); + + res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*2.txt")); + au.clear (); + au.push_back (tl::combine_path (p, "test2.txt")); + au.push_back (tl::combine_path (aaad, "test2.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); + + res = tl::glob_expand (tl::combine_path (tl::combine_path (tl::combine_path (p, "**"), "a"), "*2.txt")); + au.clear (); + au.push_back (tl::combine_path (aaad, "test2.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); +} + diff --git a/testdata/bd/strmxor_au7d.oas b/testdata/bd/strmxor_au7d.oas new file mode 100644 index 000000000..9d64f3f38 Binary files /dev/null and b/testdata/bd/strmxor_au7d.oas differ diff --git a/testdata/bd/strmxor_covered1.gds b/testdata/bd/strmxor_covered1.gds new file mode 100644 index 000000000..f3d6eb6c4 Binary files /dev/null and b/testdata/bd/strmxor_covered1.gds differ diff --git a/testdata/bd/strmxor_covered2.gds b/testdata/bd/strmxor_covered2.gds new file mode 100644 index 000000000..da6141b50 Binary files /dev/null and b/testdata/bd/strmxor_covered2.gds differ diff --git a/testdata/buddies/buddies.rb b/testdata/buddies/buddies.rb index bf7afb096..7e309ac27 100644 --- a/testdata/buddies/buddies.rb +++ b/testdata/buddies/buddies.rb @@ -113,7 +113,7 @@ Warning: Layer 2/0 is not present in first layout, but in second Result summary (layers without differences are not shown): Layer Output Differences (shape count) - ------------------------------------------------------- + ---------------------------------------------------------------- 1/0 - (no such layer in second layout) 2/0 - (no such layer in first layout)