summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavit Grigoryan <[email protected]>2026-04-29 00:02:46 +0000
committerDavit Grigoryan <[email protected]>2026-04-29 00:02:46 +0000
commit314fb652d8a27a38599eeeec121d45327a405bb0 (patch)
treed3fa263f05e8b40c766b68eb1771b619b847802f
parentfbb90ef3a76cd6c469782c84668d25c5f3bfdd22 (diff)
impl option w/ only 2 SBs; stall so SBs are cleared before div or reconv
-rw-r--r--src/abstract_hardware_model.cc8
-rw-r--r--src/abstract_hardware_model.h22
-rw-r--r--src/gpgpu-sim/scoreboard.cc63
-rw-r--r--src/gpgpu-sim/scoreboard.h28
-rw-r--r--src/gpgpu-sim/shader.cc150
-rw-r--r--src/gpgpu-sim/shader.h3
6 files changed, 252 insertions, 22 deletions
diff --git a/src/abstract_hardware_model.cc b/src/abstract_hardware_model.cc
index 5d95db6..1010d00 100644
--- a/src/abstract_hardware_model.cc
+++ b/src/abstract_hardware_model.cc
@@ -313,6 +313,14 @@ void warp_inst_t::set_source_mask_on_sets(const active_mask_t &mask) {
}
}
+void warp_inst_t::set_source_slot_on_sets(unsigned slot_id) {
+ for (unsigned s = 0; s < m_simd_sets.size(); s++) {
+ if (m_simd_sets[s].valid) {
+ m_simd_sets[s].source_slot_id = slot_id;
+ }
+ }
+}
+
void warp_inst_t::merge_simd_sets(
const std::vector<simd_set_info> &other_sets) {
assert(m_simd_sets.size() == other_sets.size());
diff --git a/src/abstract_hardware_model.h b/src/abstract_hardware_model.h
index 6331d6b..09d59f9 100644
--- a/src/abstract_hardware_model.h
+++ b/src/abstract_hardware_model.h
@@ -1166,6 +1166,11 @@ struct simd_set_info {
// source_inst that owns this set. Used at writeback to release the
// matching (reg, mask) entry. Zero/empty when source_inst is NULL.
active_mask_t source_mask;
+ // Mode 2 (slot-pinned scoreboard): which ibuffer half (0 or 1) the
+ // source_inst issued from in its own warp. Used at writeback to release
+ // the matching slot's scoreboard. (unsigned)-1 when source_inst is NULL
+ // (i.e., the set carries primary's own work).
+ unsigned source_slot_id;
// Mixed-space MEM co-issue (v2): per-set shared state.
// Populated only when source_inst's space is shared/sstarr; otherwise
@@ -1177,7 +1182,8 @@ struct simd_set_info {
simd_set_info()
: set_id(0), warp_id(0), split_id((unsigned)-1),
- num_active_threads(0), valid(false), source_inst(NULL), cycles(0) {
+ num_active_threads(0), valid(false), source_inst(NULL),
+ source_slot_id((unsigned)-1), cycles(0) {
set_active_mask.reset();
active_mask_in_warp.reset();
source_mask.reset();
@@ -1337,6 +1343,16 @@ class warp_inst_t : public inst_t {
// Mode 1: stamp the source_inst's full active_mask onto each valid set
// for use at writeback (releaseRegisterMask).
void set_source_mask_on_sets(const active_mask_t &mask);
+ // Mode 2: stamp the source_inst's owning ibuffer half (0 or 1) onto
+ // each valid set for use at writeback (releaseRegisterSlot).
+ void set_source_slot_on_sets(unsigned slot_id);
+
+ // Mode 2: which ibuffer half (0 or 1) this inst was issued from. Set
+ // by the issue path; read by the scoreboard reservation/release
+ // dispatcher to pick the correct per-slot scoreboard. (unsigned)-1
+ // means "not from a slot" (modes 0/1 don't use this).
+ unsigned get_ibuffer_half_id() const { return m_ibuffer_half_id; }
+ void set_ibuffer_half_id(unsigned h) { m_ibuffer_half_id = h; }
static bool simd_sets_overlap(const std::vector<simd_set_info> &a,
const std::vector<simd_set_info> &b);
@@ -1575,6 +1591,10 @@ class warp_inst_t : public inst_t {
int m_dbg_split_id = -1;
const void *m_dbg_source_inst = NULL;
+ // Mode 2 (slot-pinned scoreboard): which ibuffer half (0 or 1) this
+ // inst was issued from. (unsigned)-1 = unset / N/A in modes 0,1.
+ unsigned m_ibuffer_half_id = (unsigned)-1;
+
// Jin: cdp support
public:
int m_is_cdp;
diff --git a/src/gpgpu-sim/scoreboard.cc b/src/gpgpu-sim/scoreboard.cc
index 70e6ac1..09bb5b7 100644
--- a/src/gpgpu-sim/scoreboard.cc
+++ b/src/gpgpu-sim/scoreboard.cc
@@ -43,6 +43,7 @@ Scoreboard::Scoreboard(unsigned sid, unsigned n_warps, class gpgpu_t* gpu,
longopregs.resize(n_warps);
sec_reg_table.resize(n_warps);
mask_reg_table.resize(n_warps);
+ slot_reg_table.resize(n_warps);
m_gpu = gpu;
@@ -127,6 +128,15 @@ void Scoreboard::reserveRegisters(const class warp_inst_t* inst) {
reserveRegisterMask(inst->warp_id(), inst->out[r], mask);
}
}
+ } else if (m_mode == 2) {
+ // Slot-pinned: insert into the inst's owning ibuffer half's table.
+ unsigned slot = inst->get_ibuffer_half_id();
+ assert(slot < NUM_SLOTS);
+ for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
+ if (inst->out[r] > 0) {
+ reserveRegisterSlot(inst->warp_id(), slot, inst->out[r]);
+ }
+ }
} else {
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
if (inst->out[r] > 0) {
@@ -165,6 +175,15 @@ void Scoreboard::releaseRegisters(const class warp_inst_t* inst) {
longopregs[inst->warp_id()].erase(inst->out[r]);
}
}
+ } else if (m_mode == 2) {
+ unsigned slot = inst->get_ibuffer_half_id();
+ assert(slot < NUM_SLOTS);
+ for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
+ if (inst->out[r] > 0) {
+ releaseRegisterSlot(inst->warp_id(), slot, inst->out[r]);
+ longopregs[inst->warp_id()].erase(inst->out[r]);
+ }
+ }
} else {
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
if (inst->out[r] > 0) {
@@ -210,6 +229,7 @@ bool Scoreboard::checkCollision(unsigned wid, const class inst_t* inst) const {
bool Scoreboard::pendingWrites(unsigned wid) const {
if (m_mode == 1) return !mask_reg_table[wid].empty();
+ if (m_mode == 2) return !bothSlotsClean(wid);
return !reg_table[wid].empty();
}
@@ -325,9 +345,11 @@ void Scoreboard::releaseRegisterMask(unsigned wid, unsigned reg,
void Scoreboard::releaseSetReg(unsigned wid, unsigned reg,
const active_mask_t &mask,
- bool is_intra_legacy) {
+ bool is_intra_legacy, unsigned slot_id) {
if (m_mode == 1) {
releaseRegisterMask(wid, reg, mask);
+ } else if (m_mode == 2) {
+ releaseRegisterSlot(wid, slot_id, reg);
} else if (is_intra_legacy) {
releaseRegisterSecondary(wid, reg);
} else {
@@ -335,6 +357,45 @@ void Scoreboard::releaseSetReg(unsigned wid, unsigned reg,
}
}
+// =====================================================================
+// Mode 2: slot-pinned scoreboards
+// =====================================================================
+
+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);
+}
+
+void Scoreboard::releaseRegisterSlot(unsigned wid, unsigned slot,
+ unsigned regnum) {
+ assert(slot < NUM_SLOTS);
+ slot_reg_table[wid][slot].erase(regnum);
+}
+
+bool Scoreboard::checkCollisionSlot(unsigned wid, unsigned slot,
+ const class inst_t *inst) const {
+ assert(slot < NUM_SLOTS);
+ 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;
+ if (slot_reg_table[wid][slot].find((unsigned)reg) !=
+ slot_reg_table[wid][slot].end()) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool Scoreboard::checkCollisionMask(unsigned wid, const class inst_t *inst,
const active_mask_t &mask) const {
std::set<int> inst_regs;
diff --git a/src/gpgpu-sim/scoreboard.h b/src/gpgpu-sim/scoreboard.h
index 660587e..da34af5 100644
--- a/src/gpgpu-sim/scoreboard.h
+++ b/src/gpgpu-sim/scoreboard.h
@@ -32,6 +32,7 @@
#include <set>
#include <utility>
#include <vector>
+#include <array>
#include "assert.h"
#ifndef SCOREBOARD_H_
@@ -83,8 +84,29 @@ class Scoreboard {
// Per-set release dispatcher used at writeback. In mode 0, picks
// primary vs secondary release based on `is_intra_legacy`. In mode 1,
// releases (reg, mask) from mask_reg_table; `is_intra_legacy` ignored.
+ // In mode 2, releases (reg) from slot_reg_table[wid][slot_id].
void releaseSetReg(unsigned wid, unsigned reg, const active_mask_t &mask,
- bool is_intra_legacy);
+ bool is_intra_legacy, unsigned slot_id = 0);
+
+ // Mode 2: slot-pinned per-half scoreboards. Each warp has two
+ // independent scoreboards (one per ibuffer half). Hazard tracking
+ // ignores masks since slot ownership guarantees lane-disjoint
+ // operation between the two halves. See
+ // for_claude/docs/scoreboard_redesign_plan.md.
+ static const unsigned NUM_SLOTS = 2;
+ void reserveRegisterSlot(unsigned wid, unsigned slot, unsigned regnum);
+ void releaseRegisterSlot(unsigned wid, unsigned slot, unsigned regnum);
+ bool checkCollisionSlot(unsigned wid, unsigned slot,
+ const inst_t *inst) const;
+ bool pendingWritesSlot(unsigned wid, unsigned slot) const {
+ return !slot_reg_table[wid][slot].empty();
+ }
+ bool slotClean(unsigned wid, unsigned slot) const {
+ return slot_reg_table[wid][slot].empty();
+ }
+ bool bothSlotsClean(unsigned wid) const {
+ return slot_reg_table[wid][0].empty() && slot_reg_table[wid][1].empty();
+ }
public:
// Accounting / leak-detection (called from gpu-sim end-of-run dump).
@@ -113,6 +135,10 @@ class Scoreboard {
// Secondary path register tracking (for I-Buffer half 1, intra-warp co-issue)
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;
+
// Mode 1: per-warp list of mask-aware reservations.
struct mask_resv {
unsigned reg;
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc
index ec764aa..9a47996 100644
--- a/src/gpgpu-sim/shader.cc
+++ b/src/gpgpu-sim/shader.cc
@@ -1063,6 +1063,15 @@ void shader_core_ctx::fetch() {
m_simt_tables[wid]->get_active_split_id();
m_inst_fetch_buffer.m_split_mask =
m_simt_tables[wid]->get_active_mask();
+ // Mode 2: record half 0's owner.
+ if (m_config->gpgpu_scoreboard_mode == 2) {
+ active_mask_t mask_aw;
+ const simt_mask_t &m = m_simt_tables[wid]->get_active_mask();
+ for (unsigned t = 0; t < MAX_WARP_SIZE; t++)
+ if (m.test(t)) mask_aw.set(t);
+ m_warp[wid]->ibuffer_assign_half(0, m_inst_fetch_buffer.m_split_id,
+ mask_aw);
+ }
}
m_inst_fetch_buffer.m_ibuffer_half = 0;
m_warp[mf->get_wid()]->set_last_fetch(m_gpu->gpu_sim_cycle);
@@ -1110,6 +1119,20 @@ 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.
+ 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
+ }
+ }
address_type pc;
pc = m_warp[warp_id]->get_pc();
address_type ppc = pc + PROGRAM_MEM_START;
@@ -1147,6 +1170,16 @@ void shader_core_ctx::fetch() {
m_simt_tables[warp_id]->get_active_split_id();
m_inst_fetch_buffer.m_split_mask =
m_simt_tables[warp_id]->get_active_mask();
+ // Mode 2: record half 0's owner for the wait-for-clean gate.
+ if (m_config->gpgpu_scoreboard_mode == 2) {
+ active_mask_t mask_aw;
+ const simt_mask_t &m =
+ m_simt_tables[warp_id]->get_active_mask();
+ for (unsigned t = 0; t < MAX_WARP_SIZE; t++)
+ if (m.test(t)) mask_aw.set(t);
+ m_warp[warp_id]->ibuffer_assign_half(
+ 0, m_inst_fetch_buffer.m_split_id, mask_aw);
+ }
}
m_inst_fetch_buffer.m_ibuffer_half = 0;
m_warp[warp_id]->set_last_fetch(m_gpu->gpu_sim_cycle);
@@ -1192,6 +1225,15 @@ 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
+ }
+
// Attempt I-Cache access (HIT-only for secondary)
address_type ppc = sec_pc + PROGRAM_MEM_START;
unsigned nbytes = 16;
@@ -1297,6 +1339,8 @@ warp_inst_t *shader_core_ctx::issue_warp(register_set &pipe_reg_set,
(*pipe_reg)->set_dbg_path(0);
(*pipe_reg)->set_dbg_split_id(-1);
(*pipe_reg)->set_dbg_source_inst(next_inst);
+ // Mode 2: primary issue always comes through ibuffer half 0.
+ (*pipe_reg)->set_ibuffer_half_id(0);
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) {
@@ -1449,6 +1493,10 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite,
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);
+ // Mode 2: stamp the source's owning ibuffer half. Intra-warp co-issue
+ // came from primary's slot 1 (secondary half); inter-warp from the
+ // donor's slot 0 (donor's own primary half).
+ temp_inst.set_ibuffer_half_id(split_id != (unsigned)-1 ? 1u : 0u);
// Debug: for intra co-issue, compare cached active_mask (from
// ibuffer slot) against current splits-table mask + warp's
@@ -1529,11 +1577,13 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite,
}
// Reserve scoreboard for the co-issued warp.
- if (m_config->gpgpu_scoreboard_mode == 1) {
- // Mode 1 (mask-aware): both inter-warp and intra-warp use the unified
- // mask-aware scoreboard. The active_mask carried in temp_inst already
- // differentiates the two splits' reservations — disjoint-lane writes
- // get distinct (reg, mask) entries even when they share warp_id.
+ if (m_config->gpgpu_scoreboard_mode == 1 ||
+ m_config->gpgpu_scoreboard_mode == 2) {
+ // Mode 1 (mask-aware): both inter and intra use unified mask-aware
+ // scoreboard; mask differentiates entries.
+ // Mode 2 (slot-pinned): both inter and intra reserve on their
+ // respective slot's scoreboard via temp_inst's set ibuffer_half_id
+ // (set above to 0 for inter, 1 for intra).
m_scoreboard->reserveRegisters(&temp_inst);
} else {
// Mode 0 (legacy):
@@ -1570,6 +1620,10 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite,
// release the matching mask-aware reservation. Cheap and harmless in
// mode 0 (field is unused there).
temp_inst.set_source_mask_on_sets(active_mask);
+ // Mode 2: stamp the source's owning ibuffer half (0 for inter-warp
+ // donor's primary half, 1 for intra-warp's secondary half) so per-set
+ // writeback can pick the right slot scoreboard.
+ temp_inst.set_source_slot_on_sets(split_id != (unsigned)-1 ? 1u : 0u);
// Mixed-space MEM co-issue (v2): for SHARED coissuers, capture per-lane
// memreqaddr + temp_inst.cycles into each valid set BEFORE merge. The
@@ -1963,8 +2017,8 @@ void scheduler_unit::try_inter_warp_coissue(
}
// Scoreboard check.
- // Mode 1 needs the candidate's active mask for hazard intersection;
- // hoist its lookup before the legacy/mode-1 dispatch.
+ // Mode 1 needs the candidate's active mask for hazard intersection.
+ // Mode 2 checks the donor warp's slot 0 (its primary half).
bool sb_collision_inter;
if (m_shader->m_config->gpgpu_scoreboard_mode == 1) {
const active_mask_t &cand_mask_pre =
@@ -1972,6 +2026,9 @@ void scheduler_unit::try_inter_warp_coissue(
sb_collision_inter = m_scoreboard->checkCollisionMask(cand_warp_id,
cand_inst,
cand_mask_pre);
+ } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) {
+ sb_collision_inter = m_scoreboard->checkCollisionSlot(
+ cand_warp_id, /*slot=*/0, cand_inst);
} else {
sb_collision_inter = m_scoreboard->checkCollision(cand_warp_id,
cand_inst);
@@ -2135,6 +2192,10 @@ void scheduler_unit::try_intra_warp_coissue(
if (m_shader->m_config->gpgpu_scoreboard_mode == 1) {
sb_collision = m_scoreboard->checkCollisionMask(primary_warp_id,
sec_inst, sec_mask_eff);
+ } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) {
+ // Intra-warp candidates check primary warp's slot 1 (secondary half).
+ sb_collision = m_scoreboard->checkCollisionSlot(primary_warp_id,
+ /*slot=*/1, sec_inst);
} else {
sb_collision = m_scoreboard->checkCollisionSecondary(primary_warp_id,
sec_inst);
@@ -2234,6 +2295,9 @@ void scheduler_unit::try_utilization_max_coissue(
sb_collision_inter2 = m_scoreboard->checkCollisionMask(cand_warp_id,
cand_inst,
cand_mask_pre);
+ } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) {
+ sb_collision_inter2 = m_scoreboard->checkCollisionSlot(
+ cand_warp_id, /*slot=*/0, cand_inst);
} else {
sb_collision_inter2 = m_scoreboard->checkCollision(cand_warp_id,
cand_inst);
@@ -2400,6 +2464,10 @@ void scheduler_unit::try_utilization_max_coissue(
sb_collision = m_scoreboard->checkCollisionMask(cand_warp_id,
sec_inst,
sec_mask_eff);
+ } else if (m_shader->m_config->gpgpu_scoreboard_mode == 2) {
+ sb_collision = m_scoreboard->checkCollisionSlot(cand_warp_id,
+ /*slot=*/1,
+ sec_inst);
} else {
sb_collision = m_scoreboard->checkCollisionSecondary(cand_warp_id,
sec_inst);
@@ -2614,6 +2682,9 @@ void scheduler_unit::cycle() {
if (m_shader->m_config->gpgpu_scoreboard_mode == 1) {
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(
+ warp_id, /*slot=*/0, pI);
} else {
sb_collision_primary = m_scoreboard->checkCollision(warp_id, pI);
}
@@ -3403,12 +3474,15 @@ void shader_core_ctx::writeback() {
for (unsigned s = 0; s < sets.size(); s++) {
if (!sets[s].valid || sets[s].source_inst == NULL) continue;
unsigned set_wid = sets[s].warp_id;
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1) ? sets[s].source_slot_id
+ : 0u;
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
if (sets[s].source_inst->out[r] > 0) {
m_scoreboard->releaseSetReg(set_wid,
sets[s].source_inst->out[r],
sets[s].source_mask,
- /*is_intra_legacy=*/false);
+ /*is_intra_legacy=*/false, set_slot);
}
}
}
@@ -3433,11 +3507,15 @@ void shader_core_ctx::writeback() {
// Intra-warp: release from secondary scoreboard (mode 0) or
// mask-aware (mode 1) and dec once.
if (!intra_warp_decremented) {
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : 1u; // intra defaults to slot 1
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
if (sets[s].source_inst->out[r] > 0) {
m_scoreboard->releaseSetReg(
sets[s].warp_id, sets[s].source_inst->out[r],
- sets[s].source_mask, /*is_intra_legacy=*/true);
+ sets[s].source_mask, /*is_intra_legacy=*/true, set_slot);
}
}
m_warp[warp_id]->dec_inst_in_pipeline();
@@ -3774,7 +3852,8 @@ void ldst_unit::L1_latency_queue_cycle() {
m_pending_writes_secondary[src.wid][src_split_mf].erase(
reg);
m_scoreboard->releaseSetReg(src.wid, reg, src.source_mask,
- /*is_intra_legacy=*/true);
+ /*is_intra_legacy=*/true,
+ src.source_slot_id);
any_completed = true;
}
} else {
@@ -3783,7 +3862,8 @@ void ldst_unit::L1_latency_queue_cycle() {
if (!still_pending) {
m_pending_writes[src.wid].erase(reg);
m_scoreboard->releaseSetReg(src.wid, reg, src.source_mask,
- /*is_intra_legacy=*/false);
+ /*is_intra_legacy=*/false,
+ src.source_slot_id);
any_completed = true;
}
}
@@ -4370,6 +4450,7 @@ ldst_unit::mem_src_t ldst_unit::resolve_source(
r.wid = (access_src_wid == (unsigned)-1) ? inst.warp_id() : access_src_wid;
r.out_inst = &inst;
r.source_mask.reset();
+ r.source_slot_id = 0; // primary's own slot
// Primary access: (wid == primary AND split_id == -1). Return early with
// out_inst = composite (primary's out[]). Note: split_id is the
// discriminator — a coissuer can have the same warp_id as primary when
@@ -4377,6 +4458,8 @@ ldst_unit::mem_src_t ldst_unit::resolve_source(
if (r.wid == inst.warp_id() && access_src_split_id == (unsigned)-1) {
// Primary's own mask is on the composite itself.
r.source_mask = inst.get_active_mask();
+ r.source_slot_id = inst.get_ibuffer_half_id();
+ if (r.source_slot_id == (unsigned)-1) r.source_slot_id = 0;
return r;
}
if (!inst.has_simd_sets()) return r;
@@ -4388,6 +4471,9 @@ ldst_unit::mem_src_t ldst_unit::resolve_source(
sets[s].split_id == access_src_split_id) {
r.out_inst = sets[s].source_inst;
r.source_mask = sets[s].source_mask;
+ if (sets[s].source_slot_id != (unsigned)-1) {
+ r.source_slot_id = sets[s].source_slot_id;
+ }
break;
}
}
@@ -4769,7 +4855,8 @@ void ldst_unit::writeback() {
.erase(reg);
m_scoreboard->releaseSetReg(src_i.wid, reg,
src_i.source_mask,
- /*is_intra_legacy=*/true);
+ /*is_intra_legacy=*/true,
+ src_i.source_slot_id);
insn_completed = true;
}
} else {
@@ -4780,7 +4867,8 @@ void ldst_unit::writeback() {
m_pending_writes[src_i.wid].erase(reg);
m_scoreboard->releaseSetReg(src_i.wid, reg,
src_i.source_mask,
- /*is_intra_legacy=*/false);
+ /*is_intra_legacy=*/false,
+ src_i.source_slot_id);
insn_completed = true;
}
}
@@ -4807,7 +4895,8 @@ void ldst_unit::writeback() {
if (src.out_inst->out[r] > 0) {
m_scoreboard->releaseSetReg(src.wid, src.out_inst->out[r],
src.source_mask,
- /*is_intra_legacy=*/false);
+ /*is_intra_legacy=*/false,
+ src.source_slot_id);
insn_completed = true;
}
}
@@ -4828,11 +4917,16 @@ void ldst_unit::writeback() {
released.insert(key);
// Secondary slot coissuer → secondary scoreboard map (mode 0).
bool coissued_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (coissued_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
if (sets[s].source_inst->out[r] > 0) {
m_scoreboard->releaseSetReg(
sets[s].warp_id, sets[s].source_inst->out[r],
- sets[s].source_mask, /*is_intra_legacy=*/coissued_intra);
+ sets[s].source_mask, /*is_intra_legacy=*/coissued_intra,
+ set_slot);
insn_completed = true;
}
}
@@ -5151,12 +5245,16 @@ void ldst_unit::cycle() {
if (released.count(key)) continue;
released.insert(key);
bool is_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (is_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
unsigned reg = sets[s].source_inst->out[r];
if (reg == 0) continue;
m_scoreboard->releaseSetReg(sets[s].warp_id, reg,
sets[s].source_mask,
- /*is_intra_legacy=*/is_intra);
+ /*is_intra_legacy=*/is_intra, set_slot);
}
}
pipe_reg.set_shared_side_released(true);
@@ -5325,12 +5423,16 @@ void ldst_unit::cycle() {
if (released_shared.count(key)) continue;
released_shared.insert(key);
bool is_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (is_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
unsigned reg_id = sets[s].source_inst->out[r];
if (reg_id == 0) continue;
m_scoreboard->releaseSetReg(sets[s].warp_id, reg_id,
sets[s].source_mask,
- /*is_intra_legacy=*/is_intra);
+ /*is_intra_legacy=*/is_intra, set_slot);
}
}
}
@@ -5472,12 +5574,17 @@ void ldst_unit::cycle() {
released.insert(key);
// Secondary slot → secondary scoreboard map (mode 0).
bool is_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (is_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
unsigned reg_id = sets[s].source_inst->out[r];
if (reg_id == 0) continue;
m_scoreboard->releaseSetReg(sets[s].warp_id, reg_id,
sets[s].source_mask,
- /*is_intra_legacy=*/is_intra);
+ /*is_intra_legacy=*/is_intra,
+ set_slot);
}
}
}
@@ -5557,12 +5664,17 @@ void ldst_unit::cycle() {
if (released.count(key)) continue;
released.insert(key);
bool is_intra = (sets[s].split_id != (unsigned)-1);
+ unsigned set_slot =
+ (sets[s].source_slot_id != (unsigned)-1)
+ ? sets[s].source_slot_id
+ : (is_intra ? 1u : 0u);
for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) {
unsigned reg_id = sets[s].source_inst->out[r];
if (reg_id == 0) continue;
m_scoreboard->releaseSetReg(sets[s].warp_id, reg_id,
sets[s].source_mask,
- /*is_intra_legacy=*/is_intra);
+ /*is_intra_legacy=*/is_intra,
+ set_slot);
}
}
}
diff --git a/src/gpgpu-sim/shader.h b/src/gpgpu-sim/shader.h
index ebcfc8d..92d6d33 100644
--- a/src/gpgpu-sim/shader.h
+++ b/src/gpgpu-sim/shader.h
@@ -1679,6 +1679,9 @@ class ldst_unit : public pipelined_simd_unit {
// matching simd_set's source_mask. Empty for primary access (which
// uses pipe_reg's own get_active_mask() at release time).
active_mask_t source_mask;
+ // Mode 2: source_inst's owning ibuffer half (0 or 1). For primary
+ // access (non-co-issue), defaults to 0 (composite IS the primary).
+ unsigned source_slot_id;
};
mem_src_t resolve_source(const warp_inst_t &inst,
unsigned access_src_wid,