[midend]修复replaceAllUsesWith中由于setOperand 间接调用 removeUse 或 addUse导致的迭代器失效问题

This commit is contained in:
rain2133
2025-08-04 00:59:39 +08:00
parent 446a6a6fcb
commit f24cc7ec88
2 changed files with 38 additions and 6 deletions

View File

@ -232,7 +232,7 @@ class Value {
void replaceAllUsesWith(Value *value); ///< 将原来使用该value的使用者全变为使用给定参数value并修改相应use关系
void removeUse(const std::shared_ptr<Use> &use) {
assert(use != nullptr && "Use cannot be null");
assert(use->getValue() != this && "Use does not belong to this Value");
assert(use->getValue() == this && "Use being removed does NOT point to this Value!");
auto it = std::find(uses.begin(), uses.end(), use);
assert(it != uses.end() && "Use not found in Value's uses");
uses.remove(use);

View File

@ -118,13 +118,45 @@ ArrayType *ArrayType::get(Type *elementType, unsigned numElements) {
return result.first->get();
}
// void Value::replaceAllUsesWith(Value *value) {
// for (auto &use : uses) {
// auto user = use->getUser();
// assert(user && "Use's user cannot be null");
// user->setOperand(use->getIndex(), value);
// }
// uses.clear();
// }
void Value::replaceAllUsesWith(Value *value) {
for (auto &use : uses) {
use->getUser()->setOperand(use->getIndex(), value);
}
uses.clear();
}
// 1. 创建 uses 列表的副本进行迭代。
// 这样做是为了避免在迭代过程中,由于 setOperand 间接调用 removeUse 或 addUse
// 导致原列表被修改,从而引发迭代器失效问题。
std::list<std::shared_ptr<Use>> uses_copy = uses;
for (auto &use_ptr : uses_copy) { // 遍历副本
// 2. 检查 shared_ptr<Use> 本身是否为空。这是最常见的崩溃原因之一。
if (use_ptr == nullptr) {
std::cerr << "Warning: Encountered a null std::shared_ptr<Use> in Value::uses list. Skipping this entry." << std::endl;
// 在一个健康的 IR 中,这种情况不应该发生。如果经常出现,说明你的 Use 创建或管理有问题。
continue; // 跳过空的智能指针
}
// 3. 检查 Use 对象内部的 User* 是否为空。
User* user_val = use_ptr->getUser();
if (user_val == nullptr) {
std::cerr << "Warning: Use object (" << use_ptr.get() << ") has a null User* in replaceAllUsesWith. Skipping this entry. This indicates IR corruption." << std::endl;
// 同样,在一个健康的 IR 中Use 对象的 User* 不应该为空。
continue; // 跳过用户指针为空的 Use 对象
}
// 如果走到这里use_ptr 和 user_val 都是有效的,可以安全调用 setOperand
user_val->setOperand(use_ptr->getIndex(), value);
}
// 4. 处理完所有 use 之后,清空原始的 uses 列表。
// replaceAllUsesWith 的目的就是将所有使用关系从当前 Value 转移走,
// 所以最后清空列表是正确的。
uses.clear();
}
// Implementations for static members