From 314fb652d8a27a38599eeeec121d45327a405bb0 Mon Sep 17 00:00:00 2001 From: Davit Grigoryan Date: Wed, 29 Apr 2026 00:02:46 +0000 Subject: impl option w/ only 2 SBs; stall so SBs are cleared before div or reconv --- src/abstract_hardware_model.cc | 8 +++ src/abstract_hardware_model.h | 22 +++++- src/gpgpu-sim/scoreboard.cc | 63 ++++++++++++++++- src/gpgpu-sim/scoreboard.h | 28 +++++++- src/gpgpu-sim/shader.cc | 150 +++++++++++++++++++++++++++++++++++------ src/gpgpu-sim/shader.h | 3 + 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 &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 &a, const std::vector &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 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 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 #include #include +#include #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 > sec_reg_table; + // Mode 2: per-warp, per-slot register sets. + // slot_reg_table[wid][slot] = set of reserved register numbers. + std::vector, 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, -- cgit v1.3