summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/abstract_hardware_model.cc1
-rw-r--r--src/abstract_hardware_model.h30
-rw-r--r--src/gpgpu-sim/mem_fetch.h6
-rw-r--r--src/gpgpu-sim/shader.cc349
-rw-r--r--src/gpgpu-sim/shader.h15
-rw-r--r--src/gpgpusim_entrypoint.cc6
6 files changed, 336 insertions, 71 deletions
diff --git a/src/abstract_hardware_model.cc b/src/abstract_hardware_model.cc
index ed1be65..68efd4d 100644
--- a/src/abstract_hardware_model.cc
+++ b/src/abstract_hardware_model.cc
@@ -50,6 +50,7 @@ void mem_access_t::init(gpgpu_context *ctx) {
m_req_size = 0;
m_source_wid = (unsigned)-1;
m_source_split_id = (unsigned)-1;
+ m_source_list.clear();
}
void warp_inst_t::issue(const active_mask_t &mask, unsigned warp_id,
diff --git a/src/abstract_hardware_model.h b/src/abstract_hardware_model.h
index 1a42657..713c51a 100644
--- a/src/abstract_hardware_model.h
+++ b/src/abstract_hardware_model.h
@@ -867,6 +867,22 @@ class mem_access_t {
void set_source_wid(unsigned w) { m_source_wid = w; }
void set_source_split_id(unsigned s) { m_source_split_id = s; }
+ // MEMCO v3 inter-set coalescing: list of (wid, split_id) pairs that
+ // contributed lanes to this access after cross-set merging. Empty for
+ // unmerged / legacy (non-co-issue) accesses. On writeback, each entry's
+ // source_inst out[] regs are decremented independently.
+ void add_source(unsigned w, unsigned s) {
+ for (unsigned i = 0; i < m_source_list.size(); i++) {
+ if (m_source_list[i].first == w && m_source_list[i].second == s) return;
+ }
+ m_source_list.push_back(std::make_pair(w, s));
+ }
+ const std::vector<std::pair<unsigned, unsigned> > &get_source_list() const {
+ return m_source_list;
+ }
+ bool has_source_list() const { return !m_source_list.empty(); }
+ void clear_source_list() { m_source_list.clear(); }
+
void print(FILE *fp) const {
fprintf(fp, "addr=0x%llx, %s, size=%u, ", m_addr,
m_write ? "store" : "load ", m_req_size);
@@ -919,6 +935,8 @@ class mem_access_t {
mem_access_sector_mask_t m_sector_mask;
unsigned m_source_wid; // MEM co-issue: originating warp (-1 = unset)
unsigned m_source_split_id; // MEM co-issue: originating split (-1 = unset)
+ // MEMCO v3: populated by coalesce_accessq_across_sets; empty otherwise.
+ std::vector<std::pair<unsigned, unsigned> > m_source_list;
};
class mem_fetch;
@@ -1432,6 +1450,18 @@ class warp_inst_t : public inst_t {
std::list<mem_access_t>::const_iterator accessq_end() const {
return m_accessq.end();
}
+ // MEMCO v3: mutable iteration for coalesce_accessq_across_sets (populating
+ // per-access source_list + erasing merged duplicates).
+ std::list<mem_access_t>::iterator accessq_mut_begin() {
+ return m_accessq.begin();
+ }
+ std::list<mem_access_t>::iterator accessq_mut_end() {
+ return m_accessq.end();
+ }
+ std::list<mem_access_t>::iterator accessq_mut_erase(
+ std::list<mem_access_t>::iterator it) {
+ return m_accessq.erase(it);
+ }
bool dispatch_delay() {
if (cycles > 0) cycles--;
diff --git a/src/gpgpu-sim/mem_fetch.h b/src/gpgpu-sim/mem_fetch.h
index add4792..cdeeac2 100644
--- a/src/gpgpu-sim/mem_fetch.h
+++ b/src/gpgpu-sim/mem_fetch.h
@@ -102,6 +102,12 @@ class mem_fetch {
unsigned get_access_source_split_id() const {
return m_access.get_source_split_id();
}
+ // MEMCO v3 inter-set coalescing: list of (wid, split_id) pairs whose lanes
+ // were merged into this mf's access. Empty for legacy / unmerged accesses.
+ const std::vector<std::pair<unsigned, unsigned> > &
+ get_access_source_list() const {
+ return m_access.get_source_list();
+ }
bool istexture() const;
bool isconst() const;
enum mf_type get_type() const { return m_type; }
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc
index 191beaf..3b2b17c 100644
--- a/src/gpgpu-sim/shader.cc
+++ b/src/gpgpu-sim/shader.cc
@@ -56,6 +56,11 @@
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+// MEMCO v3: diagnostic globals aggregating inter-set merge activity across
+// all ldst_units. Printed at gpgpu termination.
+unsigned long long g_memcov3_n_merges_global = 0;
+unsigned long long g_memcov3_n_merged_sources_global = 0;
+
mem_fetch *shader_core_mem_fetch_allocator::alloc(
new_addr_type addr, mem_access_type type, unsigned size, bool wr,
unsigned long long cycle, unsigned long long streamID) const {
@@ -3515,40 +3520,53 @@ void ldst_unit::L1_latency_queue_cycle() {
// Bug #2 fix: per-access dispatch via mf's own write bit, not
// composite primary op.
if (!mf_next->get_is_write()) {
- // MEM co-issue: the access belongs to mf_next's stamped source
- // warp. Resolve which instruction's out[] registers should be
- // decremented (primary's or a co-issued set's).
- unsigned src_split_mf = mf_next->get_access_source_split_id();
- mem_src_t src = resolve_source(
- mf_next->get_inst(), mf_next->get_wid(), src_split_mf);
- // "intra" = coissuer came from a secondary ibuffer slot (secondary
- // scoreboard map). Not tied to warp equality with primary.
- bool src_is_intra = (src_split_mf != (unsigned)-1);
+ // MEMCO v3: iterate mf's access source_list — a merged access has
+ // multiple contributing sources, each with its own out[] regs to
+ // decrement. Falls back to single-entry [(stamp_wid, stamp_split)]
+ // when the access wasn't normalized (legacy / non-coissue path).
+ std::vector<std::pair<unsigned, unsigned> > l1_srcs;
+ const std::vector<std::pair<unsigned, unsigned> > &mf_srcs =
+ mf_next->get_access_source_list();
+ if (!mf_srcs.empty()) {
+ l1_srcs = mf_srcs;
+ } else {
+ l1_srcs.push_back(std::make_pair(
+ mf_next->get_wid(), mf_next->get_access_source_split_id()));
+ }
bool any_completed = false;
- for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++)
- if (src.out_inst->out[r] > 0) {
- unsigned reg = src.out_inst->out[r];
- if (src_is_intra) {
- assert(m_pending_writes_secondary[src.wid][src_split_mf]
- [reg] > 0);
- unsigned still_pending = --m_pending_writes_secondary
- [src.wid][src_split_mf][reg];
- if (!still_pending) {
- m_pending_writes_secondary[src.wid][src_split_mf].erase(
- reg);
- m_scoreboard->releaseRegisterSecondary(src.wid, reg);
- any_completed = true;
- }
- } else {
- assert(m_pending_writes[src.wid][reg] > 0);
- unsigned still_pending = --m_pending_writes[src.wid][reg];
- if (!still_pending) {
- m_pending_writes[src.wid].erase(reg);
- m_scoreboard->releaseRegister(src.wid, reg);
- any_completed = true;
+ for (unsigned si = 0; si < l1_srcs.size(); si++) {
+ unsigned src_wid_i = l1_srcs[si].first;
+ unsigned src_split_mf = l1_srcs[si].second;
+ mem_src_t src = resolve_source(
+ mf_next->get_inst(), src_wid_i, src_split_mf);
+ // "intra" = coissuer came from a secondary ibuffer slot (secondary
+ // scoreboard map). Not tied to warp equality with primary.
+ bool src_is_intra = (src_split_mf != (unsigned)-1);
+ for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++)
+ if (src.out_inst->out[r] > 0) {
+ unsigned reg = src.out_inst->out[r];
+ if (src_is_intra) {
+ assert(m_pending_writes_secondary[src.wid][src_split_mf]
+ [reg] > 0);
+ unsigned still_pending = --m_pending_writes_secondary
+ [src.wid][src_split_mf][reg];
+ if (!still_pending) {
+ m_pending_writes_secondary[src.wid][src_split_mf].erase(
+ reg);
+ m_scoreboard->releaseRegisterSecondary(src.wid, reg);
+ any_completed = true;
+ }
+ } else {
+ assert(m_pending_writes[src.wid][reg] > 0);
+ unsigned still_pending = --m_pending_writes[src.wid][reg];
+ if (!still_pending) {
+ m_pending_writes[src.wid].erase(reg);
+ m_scoreboard->releaseRegister(src.wid, reg);
+ any_completed = true;
+ }
}
}
- }
+ }
if (any_completed) m_core->warp_inst_complete(mf_next->get_inst());
// release LDGSTS (primary-only; LDGSTS excluded from MEM co-issue)
@@ -4050,6 +4068,9 @@ void ldst_unit::init(mem_fetch_interface *icnt,
m_next_global = NULL;
m_next_wb_src_wid = (unsigned)-1;
m_next_wb_src_split_id = (unsigned)-1;
+ m_next_wb_src_list.clear();
+ m_n_interset_merges = 0;
+ m_n_interset_merged_sources = 0;
m_last_inst_gpu_sim_cycle = 0;
m_last_inst_gpu_tot_sim_cycle = 0;
}
@@ -4123,6 +4144,144 @@ ldst_unit::mem_src_t ldst_unit::resolve_source(
return r;
}
+// MEMCO v3: inter-set coalescing pass. Single post-splice walk of the
+// composite's accessq that:
+// (1) Normalizes each eligible access's source_list to contain the
+// access's originating (wid, split_id) (from its single stamp).
+// (2) Merges segment-equal accesses (same access_type + block_address +
+// is_write) by unioning their warp/byte/sector masks into the first
+// occurrence and appending the later source to the first's source_list,
+// then erasing the merged duplicate.
+//
+// Provenance: each merged access carries the list of contributing sources.
+// Writeback (ldst_unit::writeback) and L1 HIT path (L1_latency_queue_cycle)
+// iterate this list to decrement pending_writes for each source.
+//
+// Exclusions: atomics (per-thread semantic), LDGSTS (excluded from co-issue),
+// access types other than GLOBAL/LOCAL (shared doesn't populate accessq;
+// const/texture take other pipe stages).
+//
+// Env vars:
+// MEMCOV3_DISABLE_INTERSET_COALESCE=1 → skip pass entirely (Stage A behavior)
+// MEMCOV3_DISABLE_MERGE=1 → normalize but skip merge (Stage B)
+// MEMCOV3_TRACE_MERGES=1 → print one line per merge event
+void ldst_unit::coalesce_accessq_across_sets(warp_inst_t &inst) {
+ static const bool disable = (getenv("MEMCOV3_DISABLE_INTERSET_COALESCE") !=
+ NULL);
+ static const bool disable_merge = (getenv("MEMCOV3_DISABLE_MERGE") != NULL);
+ static const bool trace = (getenv("MEMCOV3_TRACE_MERGES") != NULL);
+ if (disable) return;
+ if (inst.isatomic()) return;
+ if (inst.m_is_ldgsts) return;
+ unsigned primary_wid = inst.warp_id();
+
+ // Pass 1: normalize source_list on every eligible access.
+ for (std::list<mem_access_t>::iterator it = inst.accessq_mut_begin();
+ it != inst.accessq_mut_end(); ++it) {
+ mem_access_type at = it->get_type();
+ bool eligible = (at == GLOBAL_ACC_R || at == GLOBAL_ACC_W ||
+ at == LOCAL_ACC_R || at == LOCAL_ACC_W);
+ if (!eligible) continue;
+ if (it->has_source_list()) continue;
+ unsigned w = it->get_source_wid();
+ unsigned sp = it->get_source_split_id();
+ if (w == (unsigned)-1) { w = primary_wid; sp = (unsigned)-1; }
+ it->add_source(w, sp);
+ }
+
+ if (disable_merge) return;
+
+ // Pass 2: merge segment-equal accesses. Hash key = (type, addr, size, write).
+ // Include size to avoid merging accesses of different sector sizes (e.g.
+ // 32B atomic sector vs 128B line). Merging is safe only when every field
+ // the downstream cache/coalescer inspects is identical.
+ struct merge_key {
+ mem_access_type type;
+ new_addr_type addr;
+ unsigned size;
+ bool write;
+ bool operator<(const merge_key &o) const {
+ if (type != o.type) return type < o.type;
+ if (addr != o.addr) return addr < o.addr;
+ if (size != o.size) return size < o.size;
+ return write < o.write;
+ }
+ };
+ std::map<merge_key, std::list<mem_access_t>::iterator> first_occ;
+
+ unsigned merges_fired = 0;
+ std::list<mem_access_t>::iterator it = inst.accessq_mut_begin();
+ while (it != inst.accessq_mut_end()) {
+ mem_access_type at = it->get_type();
+ bool eligible = (at == GLOBAL_ACC_R || at == GLOBAL_ACC_W ||
+ at == LOCAL_ACC_R || at == LOCAL_ACC_W);
+ if (!eligible) { ++it; continue; }
+ merge_key k;
+ k.type = at;
+ k.addr = it->get_addr();
+ k.size = it->get_size();
+ k.write = it->is_write();
+ std::map<merge_key, std::list<mem_access_t>::iterator>::iterator
+ fit = first_occ.find(k);
+ if (fit == first_occ.end()) {
+ first_occ[k] = it;
+ ++it;
+ } else {
+ mem_access_t &existing = *(fit->second);
+ const mem_access_t &dup = *it;
+ // Union masks.
+ active_mask_t merged_active = existing.get_warp_mask() |
+ dup.get_warp_mask();
+ mem_access_byte_mask_t merged_byte = existing.get_byte_mask() |
+ dup.get_byte_mask();
+ mem_access_sector_mask_t merged_sector =
+ existing.get_sector_mask() | dup.get_sector_mask();
+ // Mutate existing via a rebuilt mem_access_t (no in-place setters for
+ // masks). Preserve source_list from existing + append dup's sources.
+ std::vector<std::pair<unsigned, unsigned> > merged_srcs =
+ existing.get_source_list();
+ const std::vector<std::pair<unsigned, unsigned> > &dup_srcs =
+ dup.get_source_list();
+ for (unsigned i = 0; i < dup_srcs.size(); i++) {
+ bool already = false;
+ for (unsigned j = 0; j < merged_srcs.size(); j++) {
+ if (merged_srcs[j] == dup_srcs[i]) { already = true; break; }
+ }
+ if (!already) merged_srcs.push_back(dup_srcs[i]);
+ }
+ mem_access_t rebuilt(k.type, k.addr, k.size, k.write, merged_active,
+ merged_byte, merged_sector,
+ m_core->get_gpu()->gpgpu_ctx);
+ // Preserve single-stamp from existing (first-arrival source), so
+ // downstream code that still reads the single stamp sees a consistent
+ // first source; source_list carries the full truth for iteration.
+ rebuilt.set_source_wid(existing.get_source_wid());
+ rebuilt.set_source_split_id(existing.get_source_split_id());
+ for (unsigned i = 0; i < merged_srcs.size(); i++) {
+ rebuilt.add_source(merged_srcs[i].first, merged_srcs[i].second);
+ }
+ *(fit->second) = rebuilt;
+ it = inst.accessq_mut_erase(it);
+ merges_fired++;
+ m_n_interset_merges++;
+ m_n_interset_merged_sources += dup_srcs.size();
+ extern unsigned long long g_memcov3_n_merges_global;
+ extern unsigned long long g_memcov3_n_merged_sources_global;
+ g_memcov3_n_merges_global++;
+ g_memcov3_n_merged_sources_global += dup_srcs.size();
+ if (trace) {
+ fprintf(stdout,
+ "[MEMCOV3 merge] sid=%u pc=0x%llx type=%d addr=0x%llx "
+ "size=%u wr=%d sources_now=%u\n",
+ m_sid, (unsigned long long)inst.pc, (int)k.type,
+ (unsigned long long)k.addr, k.size, k.write ? 1 : 0,
+ (unsigned)merged_srcs.size());
+ }
+ }
+ }
+ (void)merges_fired;
+}
+
void ldst_unit::issue(register_set &reg_set) {
warp_inst_t *inst = *(reg_set.get_ready());
@@ -4138,21 +4297,40 @@ void ldst_unit::issue(register_set &reg_set) {
bool primary_is_tracked_load = inst->is_load() &&
inst->space.get_type() != shared_space;
- if (inst->has_simd_sets() &&
- m_config->gpgpu_simd_partitioning &&
- m_config->gpgpu_mem_coissue) {
+ // MEMCO v3: for co-issue composites, run the inter-set coalescing /
+ // provenance-normalization pass before pending_writes accounting. Stage B:
+ // populate each access's source_list with [(wid, split)] derived from the
+ // access's single-stamp — no merging yet, same access count as before.
+ bool is_coissue_composite = inst->has_simd_sets() &&
+ m_config->gpgpu_simd_partitioning &&
+ m_config->gpgpu_mem_coissue;
+ if (is_coissue_composite && !inst->accessq_empty()) {
+ coalesce_accessq_across_sets(*inst);
+ }
+
+ if (is_coissue_composite) {
// MEM co-issue: accesses in the composite's queue may originate from
// different source warps (inter-warp) or different splits of the same
// warp (intra-warp). Group them by (source_wid, source_split_id) to
// charge pending_writes to the correct source's destination registers.
- // Unstamped accesses (source_wid == -1) belong to the primary.
+ // Post-MEMCO-v3: each access has a source_list (one entry for unmerged,
+ // multiple for merged). Iterate the list so merged accesses count
+ // toward EACH contributing source.
std::map<std::pair<unsigned, unsigned>, unsigned> n_acc_per_src;
for (std::list<mem_access_t>::const_iterator it = inst->accessq_begin();
it != inst->accessq_end(); ++it) {
- unsigned w = it->get_source_wid();
- unsigned sp = it->get_source_split_id();
- if (w == (unsigned)-1) { w = primary_wid; sp = (unsigned)-1; }
- n_acc_per_src[std::make_pair(w, sp)] += 1;
+ if (it->has_source_list()) {
+ const std::vector<std::pair<unsigned, unsigned> > &srcs =
+ it->get_source_list();
+ for (unsigned i = 0; i < srcs.size(); i++) {
+ n_acc_per_src[srcs[i]] += 1;
+ }
+ } else {
+ unsigned w = it->get_source_wid();
+ unsigned sp = it->get_source_split_id();
+ if (w == (unsigned)-1) { w = primary_wid; sp = (unsigned)-1; }
+ n_acc_per_src[std::make_pair(w, sp)] += 1;
+ }
}
// Primary's own registers — gated on primary being a tracked load
@@ -4277,46 +4455,69 @@ void ldst_unit::writeback() {
unsigned src_split = m_next_wb_src_split_id;
// is_intra = secondary-slot coissuer (not warp equality with primary).
bool is_intra_coissued = (src_split != (unsigned)-1);
- mem_src_t src = resolve_source(m_next_wb, src_wid, src_split);
- for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
- if (src.out_inst->out[r] > 0) {
- if (!is_shared) {
- unsigned reg = src.out_inst->out[r];
- if (is_intra_coissued) {
- // Intra-warp co-issued: pending count lives in the secondary
- // map (keyed by (wid, split_id, reg)). Decrement there; when
- // zero, release from secondary scoreboard.
- assert(m_pending_writes_secondary[src.wid][src_split][reg] > 0);
- unsigned still_pending =
- --m_pending_writes_secondary[src.wid][src_split][reg];
- if (!still_pending) {
- m_pending_writes_secondary[src.wid][src_split].erase(reg);
- m_scoreboard->releaseRegisterSecondary(src.wid, reg);
- insn_completed = true;
+ if (!is_shared) {
+ // MEMCO v3: iterate m_next_wb_src_list — each contributing source
+ // gets its out[] regs decremented independently. Falls back to
+ // single-entry [(src_wid, src_split)] when the access wasn't
+ // normalized (legacy / non-coissue path).
+ std::vector<std::pair<unsigned, unsigned> > wb_srcs;
+ if (!m_next_wb_src_list.empty()) {
+ wb_srcs = m_next_wb_src_list;
+ } else {
+ wb_srcs.push_back(std::make_pair(src_wid, src_split));
+ }
+ for (unsigned si = 0; si < wb_srcs.size(); si++) {
+ unsigned src_wid_i = wb_srcs[si].first;
+ unsigned src_split_i = wb_srcs[si].second;
+ bool is_intra_i = (src_split_i != (unsigned)-1);
+ mem_src_t src_i = resolve_source(m_next_wb, src_wid_i, src_split_i);
+ for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
+ if (src_i.out_inst->out[r] > 0) {
+ unsigned reg = src_i.out_inst->out[r];
+ if (is_intra_i) {
+ assert(m_pending_writes_secondary[src_i.wid][src_split_i]
+ [reg] > 0);
+ unsigned still_pending =
+ --m_pending_writes_secondary[src_i.wid][src_split_i][reg];
+ if (!still_pending) {
+ m_pending_writes_secondary[src_i.wid][src_split_i]
+ .erase(reg);
+ m_scoreboard->releaseRegisterSecondary(src_i.wid, reg);
+ insn_completed = true;
+ }
+ } else {
+ assert(m_pending_writes[src_i.wid][reg] > 0);
+ unsigned still_pending =
+ --m_pending_writes[src_i.wid][reg];
+ if (!still_pending) {
+ m_pending_writes[src_i.wid].erase(reg);
+ m_scoreboard->releaseRegister(src_i.wid, reg);
+ insn_completed = true;
+ }
}
- } else {
- assert(m_pending_writes[src.wid][reg] > 0);
- unsigned still_pending = --m_pending_writes[src.wid][reg];
- if (!still_pending) {
- m_pending_writes[src.wid].erase(reg);
- m_scoreboard->releaseRegister(src.wid, reg);
+ } else if (m_next_wb.m_is_ldgsts) {
+ // LDGSTS excluded from co-issue; source_list always size<=1
+ m_pending_ldgsts[primary_wid][m_next_wb.pc]
+ [m_next_wb.get_addr(0)]--;
+ if (m_pending_ldgsts[primary_wid][m_next_wb.pc]
+ [m_next_wb.get_addr(0)] == 0) {
insn_completed = true;
}
+ break;
}
- } else { // shared — release primary scoreboard only here;
- // composite per-set releases happen below.
- m_scoreboard->releaseRegister(src.wid, src.out_inst->out[r]);
- insn_completed = true;
}
- } else if (m_next_wb.m_is_ldgsts) { // LDGSTS excluded from co-issue
- m_pending_ldgsts[primary_wid][m_next_wb.pc]
- [m_next_wb.get_addr(0)]--;
- if (m_pending_ldgsts[primary_wid][m_next_wb.pc]
- [m_next_wb.get_addr(0)] == 0) {
+ }
+ } else {
+ // shared — release primary scoreboard only here; composite per-set
+ // releases happen below.
+ mem_src_t src = resolve_source(m_next_wb, src_wid, src_split);
+ (void)is_intra_coissued; // unused on shared path
+ for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
+ if (src.out_inst->out[r] > 0) {
+ m_scoreboard->releaseRegister(src.wid, src.out_inst->out[r]);
insn_completed = true;
}
- break;
}
}
@@ -4359,6 +4560,7 @@ void ldst_unit::writeback() {
m_next_wb.clear();
m_next_wb_src_wid = (unsigned)-1;
m_next_wb_src_split_id = (unsigned)-1;
+ m_next_wb_src_list.clear();
m_last_inst_gpu_sim_cycle = m_core->get_gpu()->gpu_sim_cycle;
m_last_inst_gpu_tot_sim_cycle = m_core->get_gpu()->gpu_tot_sim_cycle;
} // close inner block opened at line ~4176
@@ -4381,6 +4583,7 @@ void ldst_unit::writeback() {
// handles the primary portion.
m_next_wb_src_wid = (unsigned)-1;
m_next_wb_src_split_id = (unsigned)-1;
+ m_next_wb_src_list.clear();
if (m_next_wb.isatomic()) {
m_next_wb.do_atomic();
m_core->decrement_atomic_count(m_next_wb.warp_id(),
@@ -4419,6 +4622,7 @@ void ldst_unit::writeback() {
m_next_wb = mf->get_inst();
m_next_wb_src_wid = mf->get_wid();
m_next_wb_src_split_id = mf->get_access_source_split_id();
+ m_next_wb_src_list = mf->get_access_source_list();
delete mf;
serviced_client = next_client;
}
@@ -4429,6 +4633,7 @@ void ldst_unit::writeback() {
m_next_wb = mf->get_inst();
m_next_wb_src_wid = mf->get_wid();
m_next_wb_src_split_id = mf->get_access_source_split_id();
+ m_next_wb_src_list = mf->get_access_source_list();
delete mf;
serviced_client = next_client;
}
@@ -4439,6 +4644,7 @@ void ldst_unit::writeback() {
m_next_wb_src_wid = m_next_global->get_wid();
m_next_wb_src_split_id =
m_next_global->get_access_source_split_id();
+ m_next_wb_src_list = m_next_global->get_access_source_list();
if (m_next_global->isatomic()) {
m_core->decrement_atomic_count(
m_next_global->get_wid(),
@@ -4455,6 +4661,7 @@ void ldst_unit::writeback() {
m_next_wb = mf->get_inst();
m_next_wb_src_wid = mf->get_wid();
m_next_wb_src_split_id = mf->get_access_source_split_id();
+ m_next_wb_src_list = mf->get_access_source_list();
delete mf;
serviced_client = next_client;
}
diff --git a/src/gpgpu-sim/shader.h b/src/gpgpu-sim/shader.h
index cd03aec..1fff5e5 100644
--- a/src/gpgpu-sim/shader.h
+++ b/src/gpgpu-sim/shader.h
@@ -1666,6 +1666,12 @@ class ldst_unit : public pipelined_simd_unit {
mem_src_t resolve_source(const warp_inst_t &inst,
unsigned access_src_wid,
unsigned access_src_split_id) const;
+ // MEMCO v3: inter-set coalescing pass on a composite's accessq.
+ // Stage B: normalize each access's source_list to a single-entry list
+ // derived from its single (source_wid, source_split_id) stamp. No merging
+ // performed at this stage. Stage C will add the segment-equal merge step.
+ // Behavior gated off: env var MEMCOV3_DISABLE_INTERSET_COALESCE=1 skips.
+ void coalesce_accessq_across_sets(warp_inst_t &inst);
gpgpu_sim *m_gpu;
const memory_config *m_memory_config;
@@ -1693,6 +1699,15 @@ class ldst_unit : public pipelined_simd_unit {
// Set at each "m_next_wb = ..." assignment; defaults to composite primary.
unsigned m_next_wb_src_wid;
unsigned m_next_wb_src_split_id;
+ // MEMCO v3: list of all (wid, split_id) sources that contributed to the
+ // access feeding m_next_wb. Populated from mf->get_access().get_source_list()
+ // at arb time. Falls back to single-element [(m_next_wb_src_wid,
+ // m_next_wb_src_split_id)] when the access has no explicit source_list.
+ std::vector<std::pair<unsigned, unsigned> > m_next_wb_src_list;
+ // MEMCO v3: diagnostic counters. n_merges = inter-set merge events;
+ // n_merged_sources = total sources absorbed by merges (0 => no merging).
+ unsigned long long m_n_interset_merges;
+ unsigned long long m_n_interset_merged_sources;
// MEM co-issue intra-warp pending writes: primary and intra-warp-coissued
// may write the same register number (R5) for disjoint thread subsets.
// The existing m_pending_writes[wid][reg] is single-counter per (wid,reg)
diff --git a/src/gpgpusim_entrypoint.cc b/src/gpgpusim_entrypoint.cc
index 4462dc3..5ae7682 100644
--- a/src/gpgpusim_entrypoint.cc
+++ b/src/gpgpusim_entrypoint.cc
@@ -86,7 +86,13 @@ void *gpgpu_sim_thread_sequential(void *ctx_ptr) {
return NULL;
}
+extern unsigned long long g_memcov3_n_merges_global;
+extern unsigned long long g_memcov3_n_merged_sources_global;
+
static void termination_callback() {
+ printf(
+ "MEMCOV3: interset_merges = %llu, merged_sources_absorbed = %llu\n",
+ g_memcov3_n_merges_global, g_memcov3_n_merged_sources_global);
printf("GPGPU-Sim: *** exit detected ***\n");
fflush(stdout);
}