in src/tir/transforms/thread_storage_sync.cc [52:192]
std::vector<AccessEntry> Summarize(std::vector<StmtEntry> seq, const ForNode* loop) final {
// Redirect all "shared.dyn" buffer access to the same buffer var
// so that the accesses can be planned together.
Var shared_dyn_buf;
for (StmtEntry& entry : seq) {
for (AccessEntry& access : entry.access) {
if (access.scope.rank == StorageRank::kShared && access.scope.tag == ".dyn" &&
access.buffer.defined()) {
if (!shared_dyn_buf.defined()) {
shared_dyn_buf = access.buffer;
} else {
access.buffer = shared_dyn_buf;
}
}
}
}
// Unsynced reads and writes
std::vector<AccessEntry> reads;
std::vector<AccessEntry> writes;
// if it is a loop, rotate two times to consider effect of loop.
// simulation based approach to find dependencies
for (size_t i = 0; i < seq.size(); ++i) {
const StmtEntry& s = seq[i];
// check if sync before statement is needed.
bool sync_before_stmt = (syncs_inserted_.count(s.stmt) != 0);
// Apply the syncs added already.
if (sync_before_stmt) {
reads.clear();
writes.clear();
}
for (const AccessEntry& acc : s.access) {
if (acc.type == kRead) {
if (FindConflict(writes, acc, false)) {
sync_before_stmt = true;
break;
}
} else if (acc.type == kWrite) {
if (FindConflict(reads, acc, false)) {
sync_before_stmt = true;
break;
}
} else if (acc.type == kSync) {
reads.clear();
writes.clear();
}
}
// If sync is inserted. remove the irrelevant things.
if (sync_before_stmt) {
reads.clear();
writes.clear();
}
// Add the read/write of current statement
for (const AccessEntry& acc : s.access) {
if (acc.type == kRead) {
reads.push_back(acc);
} else if (acc.type == kWrite) {
writes.push_back(acc);
} else if (acc.type == kSync) {
reads.clear();
writes.clear();
}
}
if (sync_before_stmt) {
ICHECK_EQ(condition_counter(), 0) << "Cannot insert syncs inside condition";
syncs_inserted_.insert(s.stmt);
}
}
if (loop != nullptr) {
for (size_t i = 0; i < seq.size(); ++i) {
const StmtEntry& s = seq[i];
if (syncs_inserted_.count(s.stmt) != 0) break;
if (reads.empty() && writes.empty()) break;
bool sync_before_stmt = false;
for (const AccessEntry& acc : s.access) {
if (acc.type == kRead) {
if (FindConflict(writes, acc, true)) {
sync_before_stmt = true;
break;
}
} else if (acc.type == kWrite) {
if (FindConflict(reads, acc, true)) {
sync_before_stmt = true;
break;
}
} else if (acc.type == kSync) {
reads.clear();
writes.clear();
}
}
if (sync_before_stmt) {
ICHECK_EQ(condition_counter(), 0) << "Cannot insert syncs inside condition";
syncs_inserted_.insert(s.stmt);
break;
}
}
}
// return the exposed entries, remove unecessary ones.
int sync_count = 0;
// head are before first sync, tail are after last sync
std::vector<AccessEntry> head, tail;
AccessEntry esync;
esync.threads = this->env_threads();
esync.type = kSync;
esync.scope = sync_scope_;
for (const StmtEntry& s : seq) {
if (syncs_inserted_.count(s.stmt)) {
if (sync_count != 0) {
tail.clear();
} else {
head.push_back(esync);
}
++sync_count;
}
for (const AccessEntry& acc : s.access) {
if (acc.type == kSync) {
if (sync_count != 0) {
tail.clear();
} else {
head.push_back(esync);
}
++sync_count;
} else {
if (sync_count != 0) {
tail.push_back(acc);
} else {
head.push_back(acc);
}
}
}
}
head.insert(head.end(), tail.begin(), tail.end());
if (loop != nullptr) {
// clear double buffer flag after a loop is finished.
for (AccessEntry& e : head) {
e.double_buffer_write = false;
}
}
return head;
}