summaryrefslogtreecommitdiff
path: root/src/gpgpu-sim
diff options
context:
space:
mode:
authorDavit Grigoryan <[email protected]>2026-04-27 06:39:20 +0000
committerDavit Grigoryan <[email protected]>2026-04-27 06:39:20 +0000
commit2e2f1c89a6b296c9cb28c8e8b0fabb670b4ba3f7 (patch)
tree9b6e18eb4e7182ed1fa0f0f46fc3c57d40c8021d /src/gpgpu-sim
parent2f190971f5f952f55197d385c688667439bc6649 (diff)
fix co-issue scheduling pc assertions
Diffstat (limited to 'src/gpgpu-sim')
-rw-r--r--src/gpgpu-sim/shader.cc139
-rw-r--r--src/gpgpu-sim/shader.h3
2 files changed, 138 insertions, 4 deletions
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc
index a113ddb..9a1658d 100644
--- a/src/gpgpu-sim/shader.cc
+++ b/src/gpgpu-sim/shader.cc
@@ -1219,6 +1219,17 @@ void shader_core_ctx::fetch() {
sec_pc, nbytes, warp_id, 1, sec_split_id, sec_mask);
// Assign I-Buffer half 1 to this split
m_warp[warp_id]->ibuffer_assign_half(1, sec_split_id, sec_mask);
+ static const bool dbg_pc_enabled_sec = (getenv("MEMCO_DBG_PC") != NULL);
+ if (dbg_pc_enabled_sec && warp_id == 0) {
+ fprintf(stderr,
+ "[SEC_FETCH_DBG] cycle=%llu warp=%u sec_split_id=%u "
+ "sec_pc=0x%lx sec_mask=%s\n",
+ (unsigned long long)(m_gpu->gpu_sim_cycle +
+ m_gpu->gpu_tot_sim_cycle),
+ warp_id, sec_split_id, (unsigned long)sec_pc,
+ sec_mask.to_string().c_str());
+ fflush(stderr);
+ }
delete mf;
break; // one secondary fetch per cycle
} else {
@@ -1282,6 +1293,21 @@ warp_inst_t *shader_core_ctx::issue_warp(register_set &pipe_reg_set,
active_mask, warp_id, m_gpu->gpu_tot_sim_cycle + m_gpu->gpu_sim_cycle,
m_warp[warp_id]->get_dynamic_warp_id(), sch_id,
m_warp[warp_id]->get_streamID()); // dynamic instruction information
+ (*pipe_reg)->set_dbg_path(0);
+ (*pipe_reg)->set_dbg_split_id(-1);
+ (*pipe_reg)->set_dbg_source_inst(next_inst);
+ static const bool dbg_pc_enabled_pri = (getenv("MEMCO_DBG_PC") != NULL);
+ if (dbg_pc_enabled_pri && warp_id == 0 &&
+ m_config->model == AWARE_RECONVERGENCE) {
+ unsigned active_split_id = m_simt_tables[warp_id]->get_active_split_id();
+ fprintf(stderr,
+ "[PRI_ISSUE_DBG] cycle=%llu warp=%u front_split_id=%u "
+ "inst_pc=0x%lx active_mask=%s\n",
+ (unsigned long long)(m_gpu->gpu_sim_cycle + m_gpu->gpu_tot_sim_cycle),
+ warp_id, active_split_id, (unsigned long)next_inst->pc,
+ active_mask.to_string().c_str());
+ fflush(stderr);
+ }
m_stats->shader_cycle_distro[2 + (*pipe_reg)->active_count()]++;
// Compute SIMD set assignments before functional execution
if (m_config->gpgpu_simd_partitioning) {
@@ -1416,6 +1442,55 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite,
m_gpu->gpu_tot_sim_cycle + m_gpu->gpu_sim_cycle,
m_warp[warp_id]->get_dynamic_warp_id(), sch_id,
m_warp[warp_id]->get_streamID());
+ // Debug path tag: 1=intra (split_id != -1), 2=inter (split_id == -1).
+ // MEM-coissue (path=3) is set by caller after this if applicable; here
+ // we use intra/inter based on split_id which the caller passes.
+ temp_inst.set_dbg_path(split_id != (unsigned)-1 ? 1u : 2u);
+ temp_inst.set_dbg_split_id(split_id != (unsigned)-1 ? (int)split_id : -1);
+ temp_inst.set_dbg_source_inst(next_inst);
+
+ // Debug: for intra co-issue, compare cached active_mask (from
+ // ibuffer slot) against current splits-table mask + warp's
+ // m_active_threads. Logs only when MEMCO_DBG_PC env var is set,
+ // and when the cached mask differs from the current split mask
+ // OR includes any thread no longer in m_active_threads.
+ static const bool dbg_pc_enabled = (getenv("MEMCO_DBG_PC") != NULL);
+ if (dbg_pc_enabled && m_config->model == AWARE_RECONVERGENCE &&
+ split_id != (unsigned)-1) {
+ if (m_simt_tables[warp_id]->is_split_valid(split_id)) {
+ unsigned cur_pc;
+ simt_mask_t cur_mask;
+ m_simt_tables[warp_id]->get_split_info(split_id, &cur_pc, &cur_mask);
+ bool mask_drift = false;
+ for (unsigned t = 0; t < MAX_WARP_SIZE; t++) {
+ if (active_mask.test(t) != cur_mask.test(t)) mask_drift = true;
+ }
+ if (mask_drift || cur_pc != next_inst->pc) {
+ fprintf(stderr,
+ "[CO_ISSUE_DRIFT] cycle=%llu warp=%u INTRA split=%u "
+ "next_inst_pc=0x%lx cur_pc=0x%lx cached_mask=%s "
+ "cur_split_mask=%s drift=%d pc_drift=%d\n",
+ (unsigned long long)(m_gpu->gpu_sim_cycle +
+ m_gpu->gpu_tot_sim_cycle),
+ warp_id, split_id, (unsigned long)next_inst->pc,
+ (unsigned long)cur_pc,
+ active_mask.to_string().c_str(),
+ cur_mask.to_string().c_str(),
+ mask_drift ? 1 : 0,
+ (cur_pc != next_inst->pc) ? 1 : 0);
+ fflush(stderr);
+ }
+ } else {
+ fprintf(stderr,
+ "[CO_ISSUE_INVALID] cycle=%llu warp=%u INTRA split=%u "
+ "next_inst_pc=0x%lx cached_mask=%s\n",
+ (unsigned long long)(m_gpu->gpu_sim_cycle +
+ m_gpu->gpu_tot_sim_cycle),
+ warp_id, split_id, (unsigned long)next_inst->pc,
+ active_mask.to_string().c_str());
+ fflush(stderr);
+ }
+ }
// Compute SIMD sets for the co-issued instruction
switch (m_config->gpgpu_compaction_mode) {
@@ -2021,7 +2096,17 @@ void scheduler_unit::try_intra_warp_coissue(
continue;
}
- unsigned sec_active = sec_mask.count();
+ // Stale-mask guard: see commentary on the same fix in
+ // try_utilization_max_coissue. Cached `sec_mask` may diverge from
+ // current `split_mask` (lane re-bucketing or split-ID reuse). Dispatch
+ // only the intersection — lanes in BOTH masks are guaranteed to have
+ // their per-thread PC at sec_inst->pc.
+ active_mask_t sec_mask_eff;
+ for (unsigned t = 0; t < MAX_WARP_SIZE; t++) {
+ if (sec_mask.test(t) && split_mask.test(t)) sec_mask_eff.set(t);
+ }
+ if (!sec_mask_eff.any()) continue;
+ unsigned sec_active = sec_mask_eff.count();
unsigned sec_sets_needed = (sec_active + set_width - 1) / set_width;
if (sec_sets_needed > available_sets) {
m_stats->coissue_denied_by_no_sets[get_sid()]++;
@@ -2037,7 +2122,7 @@ void scheduler_unit::try_intra_warp_coissue(
get_sid(), m_id, primary_warp_id, sec_split_id, sec_sets_needed);
}
- m_shader->co_issue_warp(co_issue_composite, sec_inst, sec_mask,
+ m_shader->co_issue_warp(co_issue_composite, sec_inst, sec_mask_eff,
primary_warp_id, m_id, next_free_set,
sec_split_id);
@@ -2189,6 +2274,33 @@ void scheduler_unit::try_utilization_max_coissue(
m_shader->get_split_info(cand_warp_id, sec_split_id, &split_pc,
&split_mask);
if (split_pc != sec_inst->pc) continue;
+
+ // At most one greedy-pool candidate per warp. Two reasons:
+ // (a) Dup with primary half: when the FIFO front split moves to
+ // match a split already cached in this warp's secondary half
+ // (slots 2/3), primary fetch refills slot 0 with the same
+ // (split_id, pc) that the secondary slot already holds. The
+ // inter scan above would have admitted slot 0; admitting the
+ // secondary slot here too double-issues the same warp/split/pc,
+ // advancing per-thread PCs twice and tripping
+ // `pc == inst.pc` in cuda-sim.cc:1797.
+ // (b) Order-of-operations within the pool: when multiple
+ // candidates target the same warp, the earlier one's
+ // co_issue_warp call mutates the warp's splits-table (move,
+ // update, possibly split-id reuse). The later candidate's
+ // cached split_id can refer to a different split by then.
+ // simt_tables::update on the second candidate then asserts
+ // `top_pc == next_inst_pc` because FRONT.pc has changed.
+ // Restricting to one candidate per warp keeps the scheduler from
+ // issuing two AWARE updates against the same warp in one cycle.
+ bool dup_with_warp = false;
+ for (unsigned k = 0; k < pool.size(); k++) {
+ if (pool[k].warp_id == cand_warp_id) {
+ dup_with_warp = true;
+ break;
+ }
+ }
+ if (dup_with_warp) continue;
if (classify_fu_type(sec_inst) != co_issue_fu_type) {
m_stats->coissue_denied_by_fu_mismatch[get_sid()]++;
continue;
@@ -2219,7 +2331,26 @@ void scheduler_unit::try_utilization_max_coissue(
continue;
}
- unsigned sec_active = sec_mask.count();
+ // Stale-mask guard: the cached `sec_mask` is from secondary-fetch
+ // time and may not match the current splits-table mask. Two
+ // failure modes:
+ // 1. AWARE re-bucketing moves lanes out of this split (mask
+ // shrinks) while keeping the split's PC stable. Cached lanes
+ // that left have advanced their per-thread PC; dispatching
+ // them at the cached PC trips cuda-sim.cc:1797.
+ // 2. Split-ID reuse: the original split was invalidated and the
+ // ID slot got recycled for a new split that happens to be at
+ // the same PC. The current mask is from the *new* split; the
+ // cached mask is stale.
+ // Both reduce to: trust only lanes present in BOTH masks. The
+ // ibuffer slot ownership invariant guarantees lanes in `split_mask`
+ // are at `split_pc == sec_inst->pc`.
+ active_mask_t sec_mask_eff;
+ for (unsigned t = 0; t < MAX_WARP_SIZE; t++) {
+ if (sec_mask.test(t) && split_mask.test(t)) sec_mask_eff.set(t);
+ }
+ if (!sec_mask_eff.any()) continue;
+ unsigned sec_active = sec_mask_eff.count();
unsigned sec_needed = (sec_active + set_width - 1) / set_width;
if (sec_needed > available_sets) {
m_stats->coissue_denied_by_no_sets[get_sid()]++;
@@ -2231,7 +2362,7 @@ void scheduler_unit::try_utilization_max_coissue(
c.warp_id = cand_warp_id;
c.split_id = sec_split_id;
c.inst = sec_inst;
- c.mask = sec_mask;
+ c.mask = sec_mask_eff;
c.sec_slot = sec_slot;
c.active_count = sec_active;
c.sets_needed = sec_needed;
diff --git a/src/gpgpu-sim/shader.h b/src/gpgpu-sim/shader.h
index ce0c8c4..f25ba0e 100644
--- a/src/gpgpu-sim/shader.h
+++ b/src/gpgpu-sim/shader.h
@@ -196,6 +196,9 @@ class shd_warp_t {
n_completed++;
}
bool test_active(unsigned lane) { return m_active_threads.test(lane); }
+ const std::bitset<MAX_WARP_SIZE> &get_active_threads() const {
+ return m_active_threads;
+ }
void set_last_fetch(unsigned long long sim_cycle) {
m_last_fetch = sim_cycle;