diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/abstract_hardware_model.cc | 18 | ||||
| -rw-r--r-- | src/abstract_hardware_model.h | 11 | ||||
| -rw-r--r-- | src/gpgpu-sim/scoreboard.cc | 144 | ||||
| -rw-r--r-- | src/gpgpu-sim/scoreboard.h | 49 | ||||
| -rw-r--r-- | src/gpgpu-sim/shader.cc | 251 |
5 files changed, 445 insertions, 28 deletions
diff --git a/src/abstract_hardware_model.cc b/src/abstract_hardware_model.cc index b164c73..970cc62 100644 --- a/src/abstract_hardware_model.cc +++ b/src/abstract_hardware_model.cc @@ -2792,6 +2792,12 @@ void simt_tables::update(simt_mask_t &thread_done, addr_vector_t &next_pc, std::map<address_type, simt_mask_t> divergent_paths; bool do_invalidate = false; + // Mode 2 (slot-pinned scoreboard) drain gate: set true if this update + // actually mutates FIFO membership (divergence, reconvergence, CALL, + // RET). Plain linear advance / branch-taken with no divergence does NOT + // set this. Co-issue's move_split_to_front does not go through update() + // at all, so it cannot set this. + bool fifo_membership_changed = false; while (top_active_mask.any()) { address_type tmp_next_pc = null_pc; @@ -2860,12 +2866,14 @@ void simt_tables::update(simt_mask_t &thread_done, addr_vector_t &next_pc, new_st_entry.m_branch_div_cycle = gpgpusim_total_cycles; new_st_entry.m_type = SPLITS_TABLE_TYPE_CALL; m_simt_splits_table->insert_new_entry(new_st_entry); + m_div_recv_drain_pending = true; // CALL changes FIFO membership return; } else if (next_inst_op == RET_OPS && top_type == SPLITS_TABLE_TYPE_CALL) { assert(num_divergent_paths == 1); top_recvg_entry = m_simt_splits_table->get_rpc_entry(); m_simt_splits_table->invalidate(); + fifo_membership_changed = true; // RET changes FIFO membership simt_mask_t active_mask = m_simt_recvg_table->get_active_mask(new_recvg_entry); @@ -2901,6 +2909,7 @@ void simt_tables::update(simt_mask_t &thread_done, addr_vector_t &next_pc, m_simt_recvg_table->get_type(top_recvg_entry); m_simt_splits_table->insert_new_entry(pc, rpc, rpc_entry, active_mask, type, true); + fifo_membership_changed = true; // reconvergence inserts merged split } if (num_divergent_paths == 1) { if (!do_invalidate) { @@ -2936,6 +2945,7 @@ void simt_tables::update(simt_mask_t &thread_done, addr_vector_t &next_pc, m_simt_recvg_table->get_type(new_recvg_entry); m_simt_splits_table->insert_new_entry(pc, rpc, rpc_entry, active_mask, type); + fifo_membership_changed = true; // reconvergence inserts merged split } continue; } @@ -2957,6 +2967,14 @@ void simt_tables::update(simt_mask_t &thread_done, addr_vector_t &next_pc, if (do_invalidate) m_simt_splits_table->update_active_entry(); + // Mode 2 drain gate: divergence (>1 paths) or any reconvergence/CALL/RET + // path observed above sets the flag so the next fetch into either ibuffer + // half for this warp waits for both slot scoreboards to drain. CALL_OPS + // already returned with the flag set; reach here only for normal paths. + if (warp_diverged || fifo_membership_changed) { + m_div_recv_drain_pending = true; + } + check_simt_tables(); m_shader->update_st_size(m_simt_splits_table->num_entries()); diff --git a/src/abstract_hardware_model.h b/src/abstract_hardware_model.h index 09d59f9..8d1e0e8 100644 --- a/src/abstract_hardware_model.h +++ b/src/abstract_hardware_model.h @@ -1934,6 +1934,16 @@ class simt_tables { return m_simt_splits_table->move_split_to_front(split_id); } + // Mode 2 (slot-pinned scoreboard) cross-slot drain gate. Set by + // simt_tables::update() when the FIFO membership changes due to actual + // divergence (num_divergent_paths > 1) or reconvergence (active mask + // grows on RPC merge). NOT set by co-issue's move_split_to_front + // (FIFO ordering change without membership change). Cleared by the + // fetch-stage gate once both slot scoreboards are observed clean. + bool div_recv_drain_pending() const { return m_div_recv_drain_pending; } + void set_div_recv_drain_pending() { m_div_recv_drain_pending = true; } + void clear_div_recv_drain_pending() { m_div_recv_drain_pending = false; } + private: unsigned m_warp_id; unsigned m_warp_size; @@ -1942,6 +1952,7 @@ class simt_tables { const shader_core_config *m_config; const struct memory_config *m_mem_config; shader_core_ctx *m_shader; + bool m_div_recv_drain_pending = false; }; // ── End ITS data structures ───────────────────────────────────────────────── diff --git a/src/gpgpu-sim/scoreboard.cc b/src/gpgpu-sim/scoreboard.cc index 09bb5b7..4c270b9 100644 --- a/src/gpgpu-sim/scoreboard.cc +++ b/src/gpgpu-sim/scoreboard.cc @@ -44,6 +44,7 @@ Scoreboard::Scoreboard(unsigned sid, unsigned n_warps, class gpgpu_t* gpu, sec_reg_table.resize(n_warps); mask_reg_table.resize(n_warps); slot_reg_table.resize(n_warps); + m_xslot_shadow_mask_table.resize(n_warps); m_gpu = gpu; @@ -132,9 +133,12 @@ void Scoreboard::reserveRegisters(const class warp_inst_t* inst) { // Slot-pinned: insert into the inst's owning ibuffer half's table. unsigned slot = inst->get_ibuffer_half_id(); assert(slot < NUM_SLOTS); + const active_mask_t &shadow_mask = inst->get_active_mask(); for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { if (inst->out[r] > 0) { reserveRegisterSlot(inst->warp_id(), slot, inst->out[r]); + // Mask-aware shadow for diag (sb=1-equivalent ground truth). + reserveShadowMask(inst->warp_id(), inst->out[r], shadow_mask); } } } else { @@ -178,9 +182,12 @@ void Scoreboard::releaseRegisters(const class warp_inst_t* inst) { } else if (m_mode == 2) { unsigned slot = inst->get_ibuffer_half_id(); assert(slot < NUM_SLOTS); + const active_mask_t &shadow_mask = inst->get_active_mask(); for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { if (inst->out[r] > 0) { releaseRegisterSlot(inst->warp_id(), slot, inst->out[r]); + // Mask-aware shadow release. + releaseShadowMask(inst->warp_id(), inst->out[r], shadow_mask); longopregs[inst->warp_id()].erase(inst->out[r]); } } @@ -350,6 +357,8 @@ void Scoreboard::releaseSetReg(unsigned wid, unsigned reg, releaseRegisterMask(wid, reg, mask); } else if (m_mode == 2) { releaseRegisterSlot(wid, slot_id, reg); + // Mask-aware shadow release (per-set writeback path). + releaseShadowMask(wid, reg, mask); } else if (is_intra_legacy) { releaseRegisterSecondary(wid, reg); } else { @@ -364,17 +373,118 @@ void Scoreboard::releaseSetReg(unsigned wid, unsigned reg, void Scoreboard::reserveRegisterSlot(unsigned wid, unsigned slot, unsigned regnum) { assert(slot < NUM_SLOTS); - // Idempotent insert: same slot may try to reserve same reg if a - // back-to-back write to the same dest is co-issued by the same split - // through this slot. Mode 2 keeps it as a flat set (not ref-counted) - // for simplicity — relies on writeback's idempotent erase semantics. - slot_reg_table[wid][slot].insert(regnum); + // Ref-counted: each pending write to (slot, reg) increments. Two + // back-to-back writes to the same reg from the same slot must both + // be tracked — the prior set-based impl's idempotent insert lost the + // second write's pending state when the first retired (same-slot + // RAW miss). Each releaseRegisterSlot decrements; entry erased at 0. + slot_reg_table[wid][slot][regnum]++; } void Scoreboard::releaseRegisterSlot(unsigned wid, unsigned slot, unsigned regnum) { assert(slot < NUM_SLOTS); - slot_reg_table[wid][slot].erase(regnum); + auto it = slot_reg_table[wid][slot].find(regnum); + if (it == slot_reg_table[wid][slot].end()) return; // idempotent + if (it->second > 1) { + it->second--; + } else { + slot_reg_table[wid][slot].erase(it); + } +} + +// ===================================================================== +// Mode 2 mask-aware shadow table (diagnostic only — not used for any +// hazard-detection or stalling decision). Mirrors mask_reg_table's +// semantics so we can compare slot-pinned check vs sb=1 ground truth +// at issue eligibility sites. +// ===================================================================== +void Scoreboard::reserveShadowMask(unsigned wid, unsigned reg, + const active_mask_t &mask) { + if (m_mode != 2) return; + for (auto &e : m_xslot_shadow_mask_table[wid]) { + if (e.reg == reg && e.mask == mask) { + e.ref_count++; + return; + } + } + mask_resv e; + e.reg = reg; + e.mask = mask; + e.ref_count = 1; + e.resv_cycle = m_gpu->gpu_sim_cycle + m_gpu->gpu_tot_sim_cycle; + m_xslot_shadow_mask_table[wid].push_back(e); +} + +void Scoreboard::releaseShadowMask(unsigned wid, unsigned reg, + const active_mask_t &mask) { + if (m_mode != 2) return; + for (auto it = m_xslot_shadow_mask_table[wid].begin(); + it != m_xslot_shadow_mask_table[wid].end(); ++it) { + if (it->reg == reg && it->mask == mask) { + assert(it->ref_count > 0); + it->ref_count--; + if (it->ref_count == 0) { + m_xslot_shadow_mask_table[wid].erase(it); + } + return; + } + } + // Idempotent no-op on no match — mirrors releaseRegisterMask. +} + +bool Scoreboard::checkCollisionShadow(unsigned wid, + const class inst_t *inst, + const active_mask_t &mask) const { + if (m_mode != 2) return false; + std::set<int> inst_regs; + for (unsigned i = 0; i < inst->outcount; i++) inst_regs.insert(inst->out[i]); + for (unsigned j = 0; j < inst->incount; j++) inst_regs.insert(inst->in[j]); + if (inst->pred > 0) inst_regs.insert(inst->pred); + if (inst->ar1 > 0) inst_regs.insert(inst->ar1); + if (inst->ar2 > 0) inst_regs.insert(inst->ar2); + for (auto reg : inst_regs) { + if (reg <= 0) continue; + for (const auto &e : m_xslot_shadow_mask_table[wid]) { + if (e.reg == (unsigned)reg && (e.mask & mask).any()) { + return true; + } + } + } + return false; +} + +void Scoreboard::dumpShadowOverlap(unsigned wid, const class inst_t *inst, + const active_mask_t &mask, FILE *out, + const char *site, + unsigned long long cyc) const { + if (m_mode != 2) return; + std::set<int> inst_regs; + for (unsigned i = 0; i < inst->outcount; i++) inst_regs.insert(inst->out[i]); + for (unsigned j = 0; j < inst->incount; j++) inst_regs.insert(inst->in[j]); + if (inst->pred > 0) inst_regs.insert(inst->pred); + if (inst->ar1 > 0) inst_regs.insert(inst->ar1); + if (inst->ar2 > 0) inst_regs.insert(inst->ar2); + for (auto reg : inst_regs) { + if (reg <= 0) continue; + for (const auto &e : m_xslot_shadow_mask_table[wid]) { + if (e.reg == (unsigned)reg && (e.mask & mask).any()) { + // Lookup which slot has the reg (or both / neither). + auto it0 = slot_reg_table[wid][0].find((unsigned)reg); + auto it1 = slot_reg_table[wid][1].find((unsigned)reg); + unsigned cnt0 = (it0 == slot_reg_table[wid][0].end()) ? 0u : it0->second; + unsigned cnt1 = (it1 == slot_reg_table[wid][1].end()) ? 0u : it1->second; + fprintf(out, + "[XSLOT_RAW] cyc=%llu site=%s sid=%u wid=%u reg=%d " + "issue_mask=%s entry_mask=%s entry_resv_cyc=%llu " + "issue_count=%lu entry_count=%lu intersection_count=%lu " + "slot0_cnt=%u slot1_cnt=%u\n", + cyc, site, m_sid, wid, reg, mask.to_string().c_str(), + e.mask.to_string().c_str(), e.resv_cycle, mask.count(), + e.mask.count(), (e.mask & mask).count(), cnt0, cnt1); + } + } + } } bool Scoreboard::checkCollisionSlot(unsigned wid, unsigned slot, @@ -459,6 +569,28 @@ void Scoreboard::dumpAccounting(FILE* out) const { m_sid, m_mask_reserve_inserts, m_mask_reserve_inc_refcount, m_mask_release_match, m_mask_release_nomatch, m_mask_release_erase, mask_remaining_entries, mask_remaining_refcount); + // Mode 2 (slot-pinned) cross-slot hazard diagnostics. + // _would_stall : unmasked slot=1 reg-name match (over-counts; lane-disjoint + // cross-slot writes count too). + // _real_would_stall : mask-aware shadow-table check (sb=1 ground truth); + // counts only true RAWs that sb=2 missed. + unsigned long long shadow_remaining = 0; + for (unsigned w = 0; w < m_xslot_shadow_mask_table.size(); w++) { + shadow_remaining += m_xslot_shadow_mask_table[w].size(); + } + fprintf(out, + "xslot_hazard_diag sid=%u primary_checks=%llu primary_would_stall=%llu " + "primary_real_would_stall=%llu " + "inter_checks=%llu inter_would_stall=%llu inter_real_would_stall=%llu " + "intra_checks=%llu intra_would_stall=%llu " + "shadow_remaining_entries=%llu\n", + m_sid, m_xslot_primary_issue_checks, + m_xslot_primary_issue_would_stall, + m_xslot_primary_issue_real_would_stall, + m_xslot_inter_coissue_checks, m_xslot_inter_coissue_would_stall, + m_xslot_inter_coissue_real_would_stall, + m_xslot_intra_coissue_checks, m_xslot_intra_coissue_would_stall, + shadow_remaining); // If anything is leaked, dump per-warp leak details. if (mask_remaining_entries > 0) { for (unsigned w = 0; w < mask_reg_table.size(); w++) { diff --git a/src/gpgpu-sim/scoreboard.h b/src/gpgpu-sim/scoreboard.h index da34af5..8f736d7 100644 --- a/src/gpgpu-sim/scoreboard.h +++ b/src/gpgpu-sim/scoreboard.h @@ -136,8 +136,15 @@ class Scoreboard { std::vector<std::set<unsigned> > sec_reg_table; // Mode 2: per-warp, per-slot register sets. - // slot_reg_table[wid][slot] = set of reserved register numbers. - std::vector<std::array<std::set<unsigned>, NUM_SLOTS> > slot_reg_table; + // slot_reg_table[wid][slot][reg] = refcount of pending writes to reg + // from this slot. Idempotent insert (set semantics) is *wrong* — two + // consecutive issues from the same slot writing the same reg would + // collapse to a single entry, and the first retire would erase it + // before the second retires, losing the second's pending state. + // Ref-counted map fixes that: each reserve ++, each release --, entry + // erased when count hits 0. + std::vector<std::array<std::map<unsigned, unsigned>, NUM_SLOTS> > + slot_reg_table; // Mode 1: per-warp list of mask-aware reservations. struct mask_resv { @@ -180,6 +187,44 @@ class Scoreboard { m_primary_resv_cycle; std::map<std::pair<unsigned, unsigned>, unsigned long long> m_sec_resv_cycle; + + public: + // Mode 2 (slot-pinned) cross-slot hazard diagnostics. Increments when an + // issue from one slot would have collided with the *opposite* slot's + // pending writes — i.e. a hazard the slot-pinned design currently misses. + // Updated from shader.cc at issue-eligibility check sites. + // The unmasked variant counts any reg-name overlap (including + // lane-disjoint cases that aren't real RAWs). The "_real" variant uses + // a mask-aware shadow table to count only true RAWs. + unsigned long long m_xslot_primary_issue_would_stall = 0; + unsigned long long m_xslot_primary_issue_real_would_stall = 0; + unsigned long long m_xslot_primary_issue_checks = 0; + unsigned long long m_xslot_inter_coissue_would_stall = 0; + unsigned long long m_xslot_inter_coissue_real_would_stall = 0; + unsigned long long m_xslot_inter_coissue_checks = 0; + unsigned long long m_xslot_intra_coissue_would_stall = 0; + unsigned long long m_xslot_intra_coissue_checks = 0; + + // Mask-aware shadow table maintained alongside the slot tables (mode 2 + // only) for diagnostic comparison vs sb=1 ground truth. Each reservation + // pushes (reg, inst's active mask, refcount); each release pops the + // matching (reg, mask) entry. checkCollisionShadow performs the + // sb=1-style mask intersection across ALL entries for the warp. + void reserveShadowMask(unsigned wid, unsigned reg, + const active_mask_t &mask); + void releaseShadowMask(unsigned wid, unsigned reg, + const active_mask_t &mask); + bool checkCollisionShadow(unsigned wid, const class inst_t *inst, + const active_mask_t &mask) const; + // Dump shadow entries that intersect (mask) for any of inst's regs. + // Used by diag tracing to attribute real_would_stall events. + void dumpShadowOverlap(unsigned wid, const class inst_t *inst, + const active_mask_t &mask, FILE *out, + const char *site, unsigned long long cyc) const; + + private: + std::vector<std::vector<mask_resv> > m_xslot_shadow_mask_table; + public: }; #endif /* SCOREBOARD_H_ */ diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc index 7b208e8..ac5cd09 100644 --- a/src/gpgpu-sim/shader.cc +++ b/src/gpgpu-sim/shader.cc @@ -1119,18 +1119,35 @@ void shader_core_ctx::fetch() { if (simt_conditions && !m_warp[warp_id]->functional_done() && !m_warp[warp_id]->imiss_pending() && m_warp[warp_id]->ibuffer_half_empty(0)) { - // Mode 2 (slot-pinned): if the next split to fetch from differs - // from half 0's previously assigned owner, wait for the slot's - // scoreboard to drain before reassigning. This is the "wait - // for clean at divergence/reconvergence/split-rotation" gate. + // Mode 2 (slot-pinned): drain gate at fetch time. Two trigger + // conditions: (1) FIFO membership change set by + // simt_tables::update() (divergence/reconvergence/CALL/RET); + // (2) slot 0 about to be reassigned to a different split than + // its previous owner — this catches co-issue's + // move_split_to_front rotations, where the secondary split + // (with pending writes in slot 1) becomes slot 0's new owner. + // Without (2), the same split's writes are stranded in slot 1 + // while slot 0 issues reads of those same regs (real RAW). + // Both conditions require BOTH slots clean before fetch + // proceeds, so cross-slot pending writes from prior owners + // drain regardless of which slot is being reassigned. if (m_config->gpgpu_scoreboard_mode == 2 && m_config->model == AWARE_RECONVERGENCE) { unsigned new_split_id = m_simt_tables[warp_id]->get_active_split_id(); - if (m_warp[warp_id]->ibuffer_half_assigned(0) && - m_warp[warp_id]->ibuffer_half_split_id(0) != new_split_id && - !m_scoreboard->slotClean(warp_id, 0)) { - continue; // drain pending — try next warp + bool slot0_reassigned = + m_warp[warp_id]->ibuffer_half_assigned(0) && + m_warp[warp_id]->ibuffer_half_split_id(0) != new_split_id; + bool fifo_changed = + m_simt_tables[warp_id]->div_recv_drain_pending(); + if (slot0_reassigned || fifo_changed) { + if (!m_scoreboard->slotClean(warp_id, 0) || + !m_scoreboard->slotClean(warp_id, 1)) { + continue; // drain pending — try next warp + } + if (fifo_changed) { + m_simt_tables[warp_id]->clear_div_recv_drain_pending(); + } } } address_type pc; @@ -1225,13 +1242,25 @@ void shader_core_ctx::fetch() { if ((sec_mask & half0_mask).any()) continue; // overlapping, skip } - // Mode 2: if half 1's previous owner differs from this candidate, - // wait for SB[1] to drain before reassigning. - if (m_config->gpgpu_scoreboard_mode == 2 && - m_warp[warp_id]->ibuffer_half_assigned(1) && - m_warp[warp_id]->ibuffer_half_split_id(1) != sec_split_id && - !m_scoreboard->slotClean(warp_id, 1)) { - continue; // drain pending — try next warp + // Mode 2: drain gate at secondary fetch (mirrors the primary-fetch + // gate above). Two trigger conditions: (1) FIFO membership change + // (div_recv_drain_pending); (2) slot 1 reassignment to a different + // split than its previous owner. Both require BOTH slots clean. + if (m_config->gpgpu_scoreboard_mode == 2) { + bool slot1_reassigned = + m_warp[warp_id]->ibuffer_half_assigned(1) && + m_warp[warp_id]->ibuffer_half_split_id(1) != sec_split_id; + bool fifo_changed = + m_simt_tables[warp_id]->div_recv_drain_pending(); + if (slot1_reassigned || fifo_changed) { + if (!m_scoreboard->slotClean(warp_id, 0) || + !m_scoreboard->slotClean(warp_id, 1)) { + continue; // drain pending — try next warp + } + if (fifo_changed) { + m_simt_tables[warp_id]->clear_div_recv_drain_pending(); + } + } } // Attempt I-Cache access (HIT-only for secondary) @@ -1996,6 +2025,24 @@ void scheduler_unit::try_inter_warp_coissue( m_stats->coissue_skipped_simt_blocked[get_sid()]++; continue; } + // Mode 2 (slot-pinned scoreboard): if the candidate warp has a + // pending FIFO-membership-change drain, refuse to inter-coissue + // with it. Otherwise an instruction fetched pre-divergence (with + // a now-stale broader mask) could issue with the post-divergence + // narrower active mask, hitting cross-slot RAWs against pending + // writes from the broader mask. Same gate as the primary issue + // path's drain check, applied to the candidate. + if (m_shader->m_config->gpgpu_scoreboard_mode == 2 && + m_shader->m_config->model == AWARE_RECONVERGENCE && + m_shader->get_simt_tables(cand_warp_id) + ->div_recv_drain_pending()) { + if (!m_scoreboard->slotClean(cand_warp_id, 0) || + !m_scoreboard->slotClean(cand_warp_id, 1)) { + continue; + } + m_shader->get_simt_tables(cand_warp_id) + ->clear_div_recv_drain_pending(); + } const warp_inst_t *cand_inst = warp(cand_warp_id).ibuffer_next_inst(); if (!cand_inst) { @@ -2288,6 +2335,18 @@ void scheduler_unit::try_utilization_max_coissue( !warp(cand_warp_id).virtualized(); } if (!simt_ok) continue; + // Mode 2 sb drain gate (mirrors try_inter_warp_coissue). + if (m_shader->m_config->gpgpu_scoreboard_mode == 2 && + m_shader->m_config->model == AWARE_RECONVERGENCE && + m_shader->get_simt_tables(cand_warp_id) + ->div_recv_drain_pending()) { + if (!m_scoreboard->slotClean(cand_warp_id, 0) || + !m_scoreboard->slotClean(cand_warp_id, 1)) { + continue; + } + m_shader->get_simt_tables(cand_warp_id) + ->clear_div_recv_drain_pending(); + } const warp_inst_t *cand_inst = warp(cand_warp_id).ibuffer_next_inst(); if (!cand_inst) continue; @@ -2306,8 +2365,24 @@ void scheduler_unit::try_utilization_max_coissue( cand_inst, cand_mask_pre); } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) { - sb_collision_inter2 = m_scoreboard->checkCollisionSlot( + // OR check: stall on either slot — see commentary at primary + // issue path. + bool s0c = m_scoreboard->checkCollisionSlot( cand_warp_id, /*slot=*/0, cand_inst); + bool s1c = m_scoreboard->checkCollisionSlot( + cand_warp_id, /*slot=*/1, cand_inst); + sb_collision_inter2 = s0c || s1c; + const active_mask_t &cand_mask_for_diag = + m_shader->get_active_mask(cand_warp_id, cand_inst); + m_scoreboard->m_xslot_inter_coissue_checks++; + if (!s0c && s1c) { + m_scoreboard->m_xslot_inter_coissue_would_stall++; + } + if (!s0c && !s1c && + m_scoreboard->checkCollisionShadow(cand_warp_id, cand_inst, + cand_mask_for_diag)) { + m_scoreboard->m_xslot_inter_coissue_real_would_stall++; + } } else { sb_collision_inter2 = m_scoreboard->checkCollision(cand_warp_id, cand_inst); @@ -2656,6 +2731,18 @@ void scheduler_unit::try_utilization_max_coissue_window( !warp(cand_warp_id).virtualized(); } if (!simt_ok) continue; + // Mode 2 sb drain gate (mirrors try_inter_warp_coissue). + if (m_shader->m_config->gpgpu_scoreboard_mode == 2 && + m_shader->m_config->model == AWARE_RECONVERGENCE && + m_shader->get_simt_tables(cand_warp_id) + ->div_recv_drain_pending()) { + if (!m_scoreboard->slotClean(cand_warp_id, 0) || + !m_scoreboard->slotClean(cand_warp_id, 1)) { + continue; + } + m_shader->get_simt_tables(cand_warp_id) + ->clear_div_recv_drain_pending(); + } const warp_inst_t *cand_inst = warp(cand_warp_id).ibuffer_next_inst(); if (!cand_inst) continue; @@ -2674,8 +2761,24 @@ void scheduler_unit::try_utilization_max_coissue_window( cand_inst, cand_mask_pre); } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) { - sb_collision_inter2 = m_scoreboard->checkCollisionSlot( + // OR check: stall on either slot — see commentary at primary + // issue path. + bool s0c = m_scoreboard->checkCollisionSlot( cand_warp_id, /*slot=*/0, cand_inst); + bool s1c = m_scoreboard->checkCollisionSlot( + cand_warp_id, /*slot=*/1, cand_inst); + sb_collision_inter2 = s0c || s1c; + const active_mask_t &cand_mask_for_diag = + m_shader->get_active_mask(cand_warp_id, cand_inst); + m_scoreboard->m_xslot_inter_coissue_checks++; + if (!s0c && s1c) { + m_scoreboard->m_xslot_inter_coissue_would_stall++; + } + if (!s0c && !s1c && + m_scoreboard->checkCollisionShadow(cand_warp_id, cand_inst, + cand_mask_for_diag)) { + m_scoreboard->m_xslot_inter_coissue_real_would_stall++; + } } else { sb_collision_inter2 = m_scoreboard->checkCollision(cand_warp_id, cand_inst); @@ -2989,6 +3092,18 @@ void scheduler_unit::try_utilization_max_coissue_window_centered( !warp(cand_warp_id).virtualized(); } if (!simt_ok) continue; + // Mode 2 sb drain gate (mirrors try_inter_warp_coissue). + if (m_shader->m_config->gpgpu_scoreboard_mode == 2 && + m_shader->m_config->model == AWARE_RECONVERGENCE && + m_shader->get_simt_tables(cand_warp_id) + ->div_recv_drain_pending()) { + if (!m_scoreboard->slotClean(cand_warp_id, 0) || + !m_scoreboard->slotClean(cand_warp_id, 1)) { + continue; + } + m_shader->get_simt_tables(cand_warp_id) + ->clear_div_recv_drain_pending(); + } const warp_inst_t *cand_inst = warp(cand_warp_id).ibuffer_next_inst(); if (!cand_inst) continue; @@ -3007,8 +3122,24 @@ void scheduler_unit::try_utilization_max_coissue_window_centered( cand_inst, cand_mask_pre); } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) { - sb_collision_inter2 = m_scoreboard->checkCollisionSlot( + // OR check: stall on either slot — see commentary at primary + // issue path. + bool s0c = m_scoreboard->checkCollisionSlot( cand_warp_id, /*slot=*/0, cand_inst); + bool s1c = m_scoreboard->checkCollisionSlot( + cand_warp_id, /*slot=*/1, cand_inst); + sb_collision_inter2 = s0c || s1c; + const active_mask_t &cand_mask_for_diag = + m_shader->get_active_mask(cand_warp_id, cand_inst); + m_scoreboard->m_xslot_inter_coissue_checks++; + if (!s0c && s1c) { + m_scoreboard->m_xslot_inter_coissue_would_stall++; + } + if (!s0c && !s1c && + m_scoreboard->checkCollisionShadow(cand_warp_id, cand_inst, + cand_mask_for_diag)) { + m_scoreboard->m_xslot_inter_coissue_real_would_stall++; + } } else { sb_collision_inter2 = m_scoreboard->checkCollision(cand_warp_id, cand_inst); @@ -3314,6 +3445,18 @@ void scheduler_unit::try_utilization_max_coissue_window_before( !warp(cand_warp_id).virtualized(); } if (!simt_ok) continue; + // Mode 2 sb drain gate (mirrors try_inter_warp_coissue). + if (m_shader->m_config->gpgpu_scoreboard_mode == 2 && + m_shader->m_config->model == AWARE_RECONVERGENCE && + m_shader->get_simt_tables(cand_warp_id) + ->div_recv_drain_pending()) { + if (!m_scoreboard->slotClean(cand_warp_id, 0) || + !m_scoreboard->slotClean(cand_warp_id, 1)) { + continue; + } + m_shader->get_simt_tables(cand_warp_id) + ->clear_div_recv_drain_pending(); + } const warp_inst_t *cand_inst = warp(cand_warp_id).ibuffer_next_inst(); if (!cand_inst) continue; @@ -3332,8 +3475,24 @@ void scheduler_unit::try_utilization_max_coissue_window_before( cand_inst, cand_mask_pre); } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) { - sb_collision_inter2 = m_scoreboard->checkCollisionSlot( + // OR check: stall on either slot — see commentary at primary + // issue path. + bool s0c = m_scoreboard->checkCollisionSlot( cand_warp_id, /*slot=*/0, cand_inst); + bool s1c = m_scoreboard->checkCollisionSlot( + cand_warp_id, /*slot=*/1, cand_inst); + sb_collision_inter2 = s0c || s1c; + const active_mask_t &cand_mask_for_diag = + m_shader->get_active_mask(cand_warp_id, cand_inst); + m_scoreboard->m_xslot_inter_coissue_checks++; + if (!s0c && s1c) { + m_scoreboard->m_xslot_inter_coissue_would_stall++; + } + if (!s0c && !s1c && + m_scoreboard->checkCollisionShadow(cand_warp_id, cand_inst, + cand_mask_for_diag)) { + m_scoreboard->m_xslot_inter_coissue_real_would_stall++; + } } else { sb_collision_inter2 = m_scoreboard->checkCollision(cand_warp_id, cand_inst); @@ -3640,6 +3799,23 @@ void scheduler_unit::cycle() { !warp(warp_id).virtualized(); } + // Mode 2 (slot-pinned scoreboard) drain gate at issue time. + // Fetch-only drain leaks: ibuffer entries already-fetched pre- + // divergence may match the post-divergence active PC and issue + // with the new (subset) active mask, reading regs whose pending + // writes carry the FULL pre-divergence mask → real cross-slot + // RAW. Block all issues from this warp until both slot + // scoreboards drain. Flag clears here once observed clean. + if (m_shader->m_config->gpgpu_scoreboard_mode == 2 && + m_shader->m_config->model == AWARE_RECONVERGENCE && + m_shader->get_simt_tables(warp_id)->div_recv_drain_pending()) { + if (!m_scoreboard->slotClean(warp_id, 0) || + !m_scoreboard->slotClean(warp_id, 1)) { + continue; // drain pending — try next warp + } + m_shader->get_simt_tables(warp_id)->clear_div_recv_drain_pending(); + } + while (simt_conditions && !warp(warp_id).waiting() && !warp(warp_id).ibuffer_empty() && (checked < max_issue) && (checked <= issued) && (issued < max_issue)) { @@ -3682,8 +3858,43 @@ void scheduler_unit::cycle() { sb_collision_primary = m_scoreboard->checkCollisionMask(warp_id, pI, active_mask); } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) { - sb_collision_primary = m_scoreboard->checkCollisionSlot( + // Slot-pinned: issue stalls if the inst's regs have pending + // writes in EITHER slot. The rotation case (split moves + // from slot 1 to slot 0 via co-issue's move_split_to_front) + // strands prior writes in slot 1 while the same split's + // next instructions issue from slot 0 — same-split + // cross-slot RAW. Drain gates at fetch reduce this but + // can't catch all rotation+pre-fetched-ibuffer interactions. + // The OR check is mask-blind (no false positives from + // lane-disjoint splits in steady-state divergent + // execution? — yes there are: see m_xslot_primary_issue_would_stall). + bool slot0_coll = m_scoreboard->checkCollisionSlot( warp_id, /*slot=*/0, pI); + bool slot1_coll = m_scoreboard->checkCollisionSlot( + warp_id, /*slot=*/1, pI); + sb_collision_primary = slot0_coll || slot1_coll; + // Diagnostic kept for now: count would_stall (unmasked) + // and real_would_stall (mask-aware shadow). With OR check + // active, _real_would_stall should drop to 0 — any real + // RAW would also fire one of slot0/slot1 collision. + m_scoreboard->m_xslot_primary_issue_checks++; + if (!slot0_coll && slot1_coll) { + m_scoreboard->m_xslot_primary_issue_would_stall++; + } + if (!slot0_coll && !slot1_coll && + m_scoreboard->checkCollisionShadow(warp_id, pI, + active_mask)) { + m_scoreboard->m_xslot_primary_issue_real_would_stall++; + static unsigned long long s_traced = 0; + if (s_traced < 50 && getenv("XSLOT_TRACE")) { + unsigned long long cyc = + m_shader->get_gpu()->gpu_sim_cycle + + m_shader->get_gpu()->gpu_tot_sim_cycle; + m_scoreboard->dumpShadowOverlap(warp_id, pI, active_mask, + stderr, "primary", cyc); + s_traced++; + } + } } else { sb_collision_primary = m_scoreboard->checkCollision(warp_id, pI); } |
