From 0d71376678778ea7f62556d997c71baa43fb2fbe Mon Sep 17 00:00:00 2001 From: Davit Grigoryan Date: Fri, 24 Apr 2026 22:53:58 +0000 Subject: fix global mem reqs coalescing --- src/abstract_hardware_model.cc | 1 + src/abstract_hardware_model.h | 30 ++++ src/gpgpu-sim/mem_fetch.h | 6 + src/gpgpu-sim/shader.cc | 349 ++++++++++++++++++++++++++++++++--------- src/gpgpu-sim/shader.h | 15 ++ src/gpgpusim_entrypoint.cc | 6 + 6 files changed, 336 insertions(+), 71 deletions(-) (limited to 'src') 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 > &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 > m_source_list; }; class mem_fetch; @@ -1432,6 +1450,18 @@ class warp_inst_t : public inst_t { std::list::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::iterator accessq_mut_begin() { + return m_accessq.begin(); + } + std::list::iterator accessq_mut_end() { + return m_accessq.end(); + } + std::list::iterator accessq_mut_erase( + std::list::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 > & + 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 > l1_srcs; + const std::vector > &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::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::iterator> first_occ; + + unsigned merges_fired = 0; + std::list::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::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 > merged_srcs = + existing.get_source_list(); + const std::vector > &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 ®_set) { warp_inst_t *inst = *(reg_set.get_ready()); @@ -4138,21 +4297,40 @@ void ldst_unit::issue(register_set ®_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, unsigned> n_acc_per_src; for (std::list::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 > &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 > 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 > 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); } -- cgit v1.3