-
Notifications
You must be signed in to change notification settings - Fork 751
odb: Fixed a bug where _dbModule::modbterm_hash_ is emtpy after read_db is executed
#9142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
- Previously, dbModules in a child block (not top block) suffers from empty dbModule::*_hash_ containers because the deserialization logic always searches top block even for the child block. - Corrected the module search logic to find the child blocks correctly Signed-off-by: Jaehyun Kim <[email protected]>
Signed-off-by: Jaehyun Kim <[email protected]>
Signed-off-by: Jaehyun Kim <[email protected]>
Signed-off-by: Jaehyun Kim <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request correctly centralizes the logic for reconstructing module-level hash tables during database deserialization, fixing a bug related to hierarchical designs in child blocks. The approach of moving this logic into _dbBlock::operator>> is sound.
My review includes two main points:
- A suggestion to improve robustness by adding null checks to prevent potential crashes if module lookups fail.
- A recommendation to refactor duplicated code into a template function to enhance maintainability.
Additionally, I've pointed out that one of the new tests doesn't fully validate the fix for dbinst_hash_ and suggested a correction.
Overall, the changes are in the right direction to solve the issue.
| if (db->isSchema(kSchemaDbRemoveHash)) { | ||
| // Construct dbinst_hash_ | ||
| dbSet<dbInst> insts((dbBlock*) &block, block.inst_tbl_); | ||
| for (dbInst* obj : insts) { | ||
| _dbInst* _obj = (_dbInst*) obj; | ||
| _dbModule* module = nullptr; | ||
| if (_obj->module_ == 0) { | ||
| module = block.module_tbl_->getPtr(block.top_module_); | ||
| } else { | ||
| module = block.module_tbl_->getPtr(_obj->module_); | ||
| } | ||
| if (_obj->name_) { | ||
| module->dbinst_hash_[_obj->name_] = _obj->getId(); | ||
| } | ||
| } | ||
| } | ||
| if (db->isSchema(kSchemaBlockOwnsScanInsts)) { | ||
| stream >> *block.scan_inst_tbl_; | ||
| } | ||
| stream >> *block.modinst_tbl_; | ||
| if (db->isSchema(kSchemaDbRemoveHash)) { | ||
| // Construct modinst_hash_ | ||
| dbSet<dbModInst> modinsts((dbBlock*) &block, block.modinst_tbl_); | ||
| for (dbModInst* obj : modinsts) { | ||
| _dbModInst* _obj = (_dbModInst*) obj; | ||
| _dbModule* module = block.module_tbl_->getPtr(_obj->parent_); | ||
| if (_obj->name_) { | ||
| module->modinst_hash_[_obj->name_] = _obj->getId(); | ||
| } | ||
| } | ||
| } | ||
| if (db->isSchema(kSchemaUpdateHierarchy)) { | ||
| stream >> *block.modbterm_tbl_; | ||
| if (db->isSchema(kSchemaDbRemoveHash)) { | ||
| // Construct modbterm_hash_ | ||
| dbSet<dbModBTerm> modbterms((dbBlock*) &block, block.modbterm_tbl_); | ||
| for (dbModBTerm* obj : modbterms) { | ||
| _dbModBTerm* _obj = (_dbModBTerm*) obj; | ||
| _dbModule* module = block.module_tbl_->getPtr(_obj->parent_); | ||
| if (_obj->name_) { | ||
| module->modbterm_hash_[_obj->name_] = _obj->getId(); | ||
| } | ||
| } | ||
| } | ||
| if (db->isSchema(kSchemaDbRemoveHash)) { | ||
| stream >> *block.busport_tbl_; | ||
| } | ||
| stream >> *block.moditerm_tbl_; | ||
| stream >> *block.modnet_tbl_; | ||
| if (db->isSchema(kSchemaDbRemoveHash)) { | ||
| // Construct modnet_hash_ | ||
| dbSet<dbModNet> modnets((dbBlock*) &block, block.modnet_tbl_); | ||
| for (dbModNet* obj : modnets) { | ||
| _dbModNet* _obj = (_dbModNet*) obj; | ||
| _dbModule* module = block.module_tbl_->getPtr(_obj->parent_); | ||
| if (_obj->name_) { | ||
| module->modnet_hash_[_obj->name_] = _obj->getId(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple of issues with the new hash reconstruction logic:
- Potential Null Pointer Dereference: In all four loops,
block.module_tbl_->getPtr()could returnnullptr, which would lead to a crash whenmoduleis dereferenced. A null check is needed. - Code Duplication: The loops for
modinst_hash_,modbterm_hash_, andmodnet_hash_are nearly identical and can be refactored into a template function to improve maintainability.
I'd suggest refactoring the three similar loops into a template helper function that also includes the null check. The loop for dbinst_hash_ can be a separate function or left as is, but also needs the null check.
Example refactoring for the three similar loops:
template <typename T, typename T_impl>
void constructParentBasedHash(
_dbBlock& block,
dbTable<T_impl>* table,
dbHashTable<T> _dbModule::* hash_member)
{
dbSet<T> items((dbBlock*)&block, table);
for (T* obj : items) {
T_impl* _obj = (T_impl*)obj;
_dbModule* module = block.module_tbl_->getPtr(_obj->parent_);
if (module && _obj->name_) {
(module->*hash_member)[_obj->name_] = _obj->getId();
}
}
}And for dbinst_hash_, you should add a null check like this:
// ...
_dbModule* module = ...; // logic to get module
if (module && _obj->name_) {
module->dbinst_hash_[_obj->name_] = _obj->getId();
}| dbInst* child_inst_2 = child_block2->findInst("child_inst"); | ||
| ASSERT_NE(child_inst_2, nullptr); | ||
| EXPECT_EQ(child_inst_2->getName(), "child_inst"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test uses child_block2->findInst(), which checks the _dbBlock::inst_hash_. However, the fix in this PR is for populating _dbModule::dbinst_hash_. To correctly verify the fix, you should use mod0_2->findDbInst() to check the module-level hash table.
| dbInst* child_inst_2 = child_block2->findInst("child_inst"); | |
| ASSERT_NE(child_inst_2, nullptr); | |
| EXPECT_EQ(child_inst_2->getName(), "child_inst"); | |
| dbInst* child_inst_2 = mod0_2->findDbInst("child_inst"); | |
| ASSERT_NE(child_inst_2, nullptr); | |
| EXPECT_EQ(child_inst_2->getName(), "child_inst"); |
|
clang-tidy review says "All clean, LGTM! 👍" |
…ate/OpenROAD into secure-fix-restore-odb-hash-in-module
|
clang-tidy review says "All clean, LGTM! 👍" |
Signed-off-by: Jaehyun Kim <[email protected]>
Signed-off-by: Jaehyun Kim <[email protected]>
Signed-off-by: Jaehyun Kim <[email protected]>
Signed-off-by: Jaehyun Kim <[email protected]>
|
clang-tidy review says "All clean, LGTM! 👍" |
|
@maliberty @precisionmoon |
Fixes #9123
Problem
The following data structures are empty after
read_dbif the module is in a child block (non-top block).The issue blocks operator swapping.
Root-cause
When it deserializes the hash table during
read_db, the deserialize logic searches the module in the top block always, instead of the correct child block.Solution
Fixed the deserialize logic to correctly find the right child block.