diff options
| author | Davit Grigoryan <[email protected]> | 2026-04-27 06:39:20 +0000 |
|---|---|---|
| committer | Davit Grigoryan <[email protected]> | 2026-04-27 06:39:20 +0000 |
| commit | 2e2f1c89a6b296c9cb28c8e8b0fabb670b4ba3f7 (patch) | |
| tree | 9b6e18eb4e7182ed1fa0f0f46fc3c57d40c8021d /src/gpgpu-sim | |
| parent | 2f190971f5f952f55197d385c688667439bc6649 (diff) | |
fix co-issue scheduling pc assertions
Diffstat (limited to 'src/gpgpu-sim')
| -rw-r--r-- | src/gpgpu-sim/shader.cc | 139 | ||||
| -rw-r--r-- | src/gpgpu-sim/shader.h | 3 |
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; |
