summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/abstract_hardware_model.cc18
-rw-r--r--src/abstract_hardware_model.h11
-rw-r--r--src/gpgpu-sim/scoreboard.cc144
-rw-r--r--src/gpgpu-sim/scoreboard.h49
-rw-r--r--src/gpgpu-sim/shader.cc251
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);
}