From 8b3b5bbf710302ad3a835626477dc1bf2d317952 Mon Sep 17 00:00:00 2001 From: Davit Grigoryan Date: Sat, 11 Apr 2026 03:49:00 +0000 Subject: initial impl of mimd timing model --- src/abstract_hardware_model.cc | 27 +++++ src/abstract_hardware_model.h | 8 +- src/gpgpu-sim/shader.cc | 269 ++++++++++++++++++++++++++++++++++++++--- src/gpgpu-sim/shader.h | 9 +- 4 files changed, 291 insertions(+), 22 deletions(-) diff --git a/src/abstract_hardware_model.cc b/src/abstract_hardware_model.cc index 316b02b..ed91a44 100644 --- a/src/abstract_hardware_model.cc +++ b/src/abstract_hardware_model.cc @@ -96,6 +96,33 @@ unsigned warp_inst_t::num_active_simd_sets() const { return count; } +void warp_inst_t::set_source_inst_on_sets(const inst_t *src) { + for (unsigned s = 0; s < m_simd_sets.size(); s++) { + if (m_simd_sets[s].valid) { + m_simd_sets[s].source_inst = src; + } + } +} + +void warp_inst_t::merge_simd_sets( + const std::vector &other_sets) { + assert(m_simd_sets.size() == other_sets.size()); + for (unsigned s = 0; s < other_sets.size(); s++) { + if (other_sets[s].valid && !m_simd_sets[s].valid) { + m_simd_sets[s] = other_sets[s]; + } + } +} + +bool warp_inst_t::simd_sets_overlap(const std::vector &a, + const std::vector &b) { + assert(a.size() == b.size()); + for (unsigned s = 0; s < a.size(); s++) { + if (a[s].valid && b[s].valid) return true; + } + return false; +} + checkpoint::checkpoint() { struct stat st = {0}; diff --git a/src/abstract_hardware_model.h b/src/abstract_hardware_model.h index 948e226..f191ab9 100644 --- a/src/abstract_hardware_model.h +++ b/src/abstract_hardware_model.h @@ -1118,9 +1118,11 @@ struct simd_set_info { unsigned thread_ids[MAX_WARP_SIZE]; // Original thread IDs for each lane (max set_width used) unsigned num_active_threads; // popcount of set_active_mask bool valid; // Whether this set has any active threads + const inst_t *source_inst; // Pointer to static decoded instruction (for MIMD) simd_set_info() - : set_id(0), warp_id(0), num_active_threads(0), valid(false) { + : set_id(0), warp_id(0), num_active_threads(0), valid(false), + source_inst(NULL) { set_active_mask.reset(); active_mask_in_warp.reset(); for (unsigned i = 0; i < MAX_WARP_SIZE; i++) thread_ids[i] = 0; @@ -1186,6 +1188,10 @@ class warp_inst_t : public inst_t { } unsigned num_active_simd_sets() const; bool has_simd_sets() const { return !m_simd_sets.empty(); } + void merge_simd_sets(const std::vector &other_sets); + void set_source_inst_on_sets(const inst_t *src); + static bool simd_sets_overlap(const std::vector &a, + const std::vector &b); const active_mask_t &get_active_mask() const { return m_warp_active_mask; } void completed(unsigned long long cycle) diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc index 9f92215..0897107 100644 --- a/src/gpgpu-sim/shader.cc +++ b/src/gpgpu-sim/shader.cc @@ -1087,10 +1087,10 @@ void exec_shader_core_ctx::func_exec_inst(warp_inst_t &inst) { } } -void shader_core_ctx::issue_warp(register_set &pipe_reg_set, - const warp_inst_t *next_inst, - const active_mask_t &active_mask, - unsigned warp_id, unsigned sch_id) { +warp_inst_t *shader_core_ctx::issue_warp(register_set &pipe_reg_set, + const warp_inst_t *next_inst, + const active_mask_t &active_mask, + unsigned warp_id, unsigned sch_id) { warp_inst_t **pipe_reg = pipe_reg_set.get_free(m_config->sub_core_model, sch_id); assert(pipe_reg); @@ -1203,6 +1203,45 @@ void shader_core_ctx::issue_warp(register_set &pipe_reg_set, m_scoreboard->reserveRegisters(*pipe_reg); m_warp[warp_id]->set_next_pc(next_inst->pc + next_inst->isize); if (split_reaches_barrier) m_simt_tables[warp_id]->push_back(); + return *pipe_reg; +} + +void shader_core_ctx::co_issue_warp(warp_inst_t *composite, + const warp_inst_t *next_inst, + const active_mask_t &active_mask, + unsigned warp_id, unsigned sch_id) { + // Free the co-issued warp's I-buffer entry + m_warp[warp_id]->ibuffer_free(); + assert(next_inst->valid()); + + // Create a temporary instruction for functional execution + warp_inst_t temp_inst = *next_inst; + temp_inst.issue(active_mask, warp_id, + m_gpu->gpu_tot_sim_cycle + m_gpu->gpu_sim_cycle, + m_warp[warp_id]->get_dynamic_warp_id(), sch_id, + m_warp[warp_id]->get_streamID()); + + // Compute SIMD sets for the co-issued instruction + temp_inst.compute_simd_sets(m_config->gpgpu_num_simd_sets, + m_config->simd_set_width); + + // Functional execution for the co-issued warp's threads + func_exec_inst(temp_inst); + + // Update SIMT divergence structures for the co-issued warp + updateSIMTDivergenceStructures(warp_id, &temp_inst); + + // Reserve scoreboard for the co-issued warp + m_scoreboard->reserveRegisters(&temp_inst); + + // Set next PC for the co-issued warp + m_warp[warp_id]->set_next_pc(next_inst->pc + next_inst->isize); + + // Tag the co-issued sets with their source instruction for writeback + temp_inst.set_source_inst_on_sets(next_inst); + + // Merge the co-issued instruction's valid sets into the composite + composite->merge_simd_sets(temp_inst.get_simd_sets()); } void shader_core_ctx::issue() { @@ -1345,6 +1384,12 @@ void scheduler_unit::cycle() { // waiting for pending register writes bool issued_inst = false; // of these we issued one + // SIMD set co-issue tracking + warp_inst_t *co_issue_composite = NULL; + exec_unit_type_t co_issue_fu_type = exec_unit_type_t::NONE; + register_set *co_issue_reg_set = NULL; + unsigned co_issue_primary_warp_id = (unsigned)-1; + order_warps(); for (std::vector::const_iterator iter = m_next_cycle_prioritized_warps.begin(); @@ -1507,16 +1552,22 @@ void scheduler_unit::cycle() { pI->op == BRANCH_OP) bru_avail = m_shader->branch_unit_avail(warp_id); if (bru_avail) { - m_shader->issue_warp(*m_sp_out, pI, active_mask, warp_id, - m_id); + co_issue_composite = m_shader->issue_warp( + *m_sp_out, pI, active_mask, warp_id, m_id); + co_issue_fu_type = exec_unit_type_t::SP; + co_issue_reg_set = m_sp_out; + co_issue_primary_warp_id = warp_id; issued++; issued_inst = true; warp_inst_issued = true; previous_issued_inst_exec_type = exec_unit_type_t::SP; } } else if (execute_on_INT) { - m_shader->issue_warp(*m_int_out, pI, active_mask, warp_id, - m_id); + co_issue_composite = m_shader->issue_warp( + *m_int_out, pI, active_mask, warp_id, m_id); + co_issue_fu_type = exec_unit_type_t::INT; + co_issue_reg_set = m_int_out; + co_issue_primary_warp_id = warp_id; issued++; issued_inst = true; warp_inst_issued = true; @@ -1532,8 +1583,11 @@ void scheduler_unit::cycle() { m_id); if (dp_pipe_avail) { - m_shader->issue_warp(*m_dp_out, pI, active_mask, warp_id, - m_id); + co_issue_composite = m_shader->issue_warp( + *m_dp_out, pI, active_mask, warp_id, m_id); + co_issue_fu_type = exec_unit_type_t::DP; + co_issue_reg_set = m_dp_out; + co_issue_primary_warp_id = warp_id; issued++; issued_inst = true; warp_inst_issued = true; @@ -1552,8 +1606,11 @@ void scheduler_unit::cycle() { m_id); if (sfu_pipe_avail) { - m_shader->issue_warp(*m_sfu_out, pI, active_mask, warp_id, - m_id); + co_issue_composite = m_shader->issue_warp( + *m_sfu_out, pI, active_mask, warp_id, m_id); + co_issue_fu_type = exec_unit_type_t::SFU; + co_issue_reg_set = m_sfu_out; + co_issue_primary_warp_id = warp_id; issued++; issued_inst = true; warp_inst_issued = true; @@ -1568,8 +1625,11 @@ void scheduler_unit::cycle() { m_shader->m_config->sub_core_model, m_id); if (tensor_core_pipe_avail) { - m_shader->issue_warp(*m_tensor_core_out, pI, active_mask, - warp_id, m_id); + co_issue_composite = m_shader->issue_warp( + *m_tensor_core_out, pI, active_mask, warp_id, m_id); + co_issue_fu_type = exec_unit_type_t::TENSOR; + co_issue_reg_set = m_tensor_core_out; + co_issue_primary_warp_id = warp_id; issued++; issued_inst = true; warp_inst_issued = true; @@ -1589,8 +1649,11 @@ void scheduler_unit::cycle() { m_id); if (spec_pipe_avail) { - m_shader->issue_warp(*spec_reg_set, pI, active_mask, warp_id, - m_id); + co_issue_composite = m_shader->issue_warp( + *spec_reg_set, pI, active_mask, warp_id, m_id); + co_issue_fu_type = exec_unit_type_t::SPECIALIZED; + co_issue_reg_set = spec_reg_set; + co_issue_primary_warp_id = warp_id; issued++; issued_inst = true; warp_inst_issued = true; @@ -1649,6 +1712,136 @@ void scheduler_unit::cycle() { } } + // SIMD set co-issue pass: after issuing primary instruction, try to pack + // instructions from other warps into unused SIMD sets (same FU type). + // Only for non-MEM FUs (LDST excluded from partitioning). + if (m_shader->m_config->gpgpu_simd_partitioning && co_issue_composite != NULL && + co_issue_fu_type != exec_unit_type_t::MEM && + co_issue_fu_type != exec_unit_type_t::NONE) { + // Count available (unused) sets in the composite + unsigned available_sets = 0; + const std::vector &primary_sets = + co_issue_composite->get_simd_sets(); + for (unsigned s = 0; s < primary_sets.size(); s++) { + if (!primary_sets[s].valid) available_sets++; + } + + if (available_sets > 0) { + printf("SIMD_SETS: cycle %llu, core %u, sched %u: %u sets available " + "after primary warp %u\n", + m_shader->get_gpu()->gpu_sim_cycle + + m_shader->get_gpu()->gpu_tot_sim_cycle, + get_sid(), m_id, available_sets, co_issue_primary_warp_id); + + // Scan other warps for co-issue candidates + for (std::vector::const_iterator iter2 = + m_next_cycle_prioritized_warps.begin(); + iter2 != m_next_cycle_prioritized_warps.end() && available_sets > 0; + iter2++) { + if ((*iter2) == NULL || (*iter2)->done_exit()) continue; + unsigned cand_warp_id = (*iter2)->get_warp_id(); + + // Skip the primary warp (already issued) + if (cand_warp_id == co_issue_primary_warp_id) continue; + + // Check basic eligibility + if (warp(cand_warp_id).ibuffer_empty()) continue; + if (warp(cand_warp_id).waiting()) continue; + + // AWARE reconvergence eligibility + bool simt_ok = true; + if (m_shader->m_config->model == AWARE_RECONVERGENCE) { + simt_ok = warp(cand_warp_id).valid() && + !warp(cand_warp_id).blocked() && + !warp(cand_warp_id).pending_reconvergence() && + !warp(cand_warp_id).virtualized(); + } + if (!simt_ok) continue; + + const warp_inst_t *cand_inst = warp(cand_warp_id).ibuffer_next_inst(); + if (!cand_inst) continue; + if (!warp(cand_warp_id).ibuffer_next_valid()) continue; + + // Control hazard check + unsigned cand_pc, cand_rpc; + m_shader->get_pdom_stack_top_info(cand_warp_id, cand_inst, &cand_pc, + &cand_rpc); + if (cand_pc != cand_inst->pc) continue; + + // Scoreboard check for the candidate warp + if (m_scoreboard->checkCollision(cand_warp_id, cand_inst)) continue; + + // Check candidate targets the same FU type as the primary + exec_unit_type_t cand_fu_type = exec_unit_type_t::NONE; + if ((cand_inst->op == LOAD_OP) || (cand_inst->op == STORE_OP) || + (cand_inst->op == MEMORY_BARRIER_OP) || + (cand_inst->op == TENSOR_CORE_LOAD_OP) || + (cand_inst->op == TENSOR_CORE_STORE_OP)) { + cand_fu_type = exec_unit_type_t::MEM; + } else if (cand_inst->op == SP_OP || + (cand_inst->op != DP_OP && cand_inst->op != SFU_OP && + cand_inst->op != ALU_SFU_OP && + cand_inst->op != TENSOR_CORE_OP && + cand_inst->op < SPEC_UNIT_START_ID && + m_shader->m_config->gpgpu_num_int_units == 0)) { + cand_fu_type = exec_unit_type_t::SP; + } else if (cand_inst->op != SP_OP && cand_inst->op != DP_OP && + cand_inst->op != SFU_OP && cand_inst->op != ALU_SFU_OP && + cand_inst->op != TENSOR_CORE_OP && + cand_inst->op < SPEC_UNIT_START_ID && + m_shader->m_config->gpgpu_num_int_units > 0) { + cand_fu_type = exec_unit_type_t::INT; + } else if (cand_inst->op == DP_OP) { + cand_fu_type = exec_unit_type_t::DP; + } else if (cand_inst->op == SFU_OP || cand_inst->op == ALU_SFU_OP) { + cand_fu_type = exec_unit_type_t::SFU; + } else if (cand_inst->op == TENSOR_CORE_OP) { + cand_fu_type = exec_unit_type_t::TENSOR; + } else if (cand_inst->op >= SPEC_UNIT_START_ID) { + cand_fu_type = exec_unit_type_t::SPECIALIZED; + } + if (cand_fu_type != co_issue_fu_type) continue; + + // Get candidate's active mask and compute its SIMD sets + const active_mask_t &cand_mask = + m_shader->get_active_mask(cand_warp_id, cand_inst); + + // Build candidate's SIMD set info to check for overlap + warp_inst_t cand_temp(m_shader->m_config); + cand_temp = *cand_inst; + cand_temp.issue(cand_mask, cand_warp_id, 0, 0, 0, 0); + cand_temp.compute_simd_sets(m_shader->m_config->gpgpu_num_simd_sets, + m_shader->m_config->simd_set_width); + + // Check set overlap with the current composite + if (warp_inst_t::simd_sets_overlap(co_issue_composite->get_simd_sets(), + cand_temp.get_simd_sets())) { + continue; // sets conflict, can't co-issue + } + + // Count candidate's sets to verify they fit + unsigned cand_sets_needed = cand_temp.num_active_simd_sets(); + if (cand_sets_needed > available_sets) continue; + + printf("SIMD_SETS: cycle %llu, core %u, sched %u: warp %u " + "CO-ISSUED with primary warp %u (%u sets)\n", + m_shader->get_gpu()->gpu_sim_cycle + + m_shader->get_gpu()->gpu_tot_sim_cycle, + get_sid(), m_id, cand_warp_id, co_issue_primary_warp_id, + cand_sets_needed); + + // Co-issue: functional execution, SIMT update, scoreboard, merge + m_shader->co_issue_warp(co_issue_composite, cand_inst, cand_mask, + cand_warp_id, m_id); + + available_sets -= cand_sets_needed; + + // Advance candidate warp's ibuffer + warp(cand_warp_id).ibuffer_step(); + } + } + } + // issue stall statistics: if (!valid_inst) m_stats->shader_cycle_distro[0]++; // idle or control hazard @@ -2063,8 +2256,48 @@ void shader_core_ctx::writeback() { m_operand_collector.writeback(*pipe_reg); unsigned warp_id = pipe_reg->warp_id(); - m_scoreboard->releaseRegisters(pipe_reg); - m_warp[warp_id]->dec_inst_in_pipeline(); + + // Per-set writeback: release scoreboard and dec pipeline counter + // for each unique warp participating in this composite instruction + if (m_config->gpgpu_simd_partitioning && pipe_reg->has_simd_sets()) { + std::set unique_warp_ids; + const std::vector &sets = pipe_reg->get_simd_sets(); + for (unsigned s = 0; s < sets.size(); s++) { + if (!sets[s].valid) continue; + unsigned set_warp_id = sets[s].warp_id; + unique_warp_ids.insert(set_warp_id); + } + // Release scoreboard for primary instruction (covers the outer warp_id) + m_scoreboard->releaseRegisters(pipe_reg); + // Release scoreboard for any co-issued warps (non-primary) + for (unsigned wid : unique_warp_ids) { + if (wid == warp_id) continue; // already released above + // For co-issued warps, release their destination registers + // which were reserved in co_issue_warp() via reserveRegisters + // on the temporary instruction. The temp used the co-issued + // warp's out[] registers, so we need to release those. + // Since the composite's outer out[] belongs to the primary warp, + // we look at the sets to find co-issued warp registers. + for (unsigned s = 0; s < sets.size(); s++) { + if (sets[s].valid && sets[s].warp_id == wid && + sets[s].source_inst != NULL) { + for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { + if (sets[s].source_inst->out[r] > 0) { + m_scoreboard->releaseRegister(wid, sets[s].source_inst->out[r]); + } + } + } + } + } + // dec_inst_in_pipeline for each unique warp + for (unsigned wid : unique_warp_ids) { + m_warp[wid]->dec_inst_in_pipeline(); + } + } else { + m_scoreboard->releaseRegisters(pipe_reg); + m_warp[warp_id]->dec_inst_in_pipeline(); + } + warp_inst_complete(*pipe_reg); m_gpu->gpu_sim_insn_last_update_sid = m_sid; m_gpu->gpu_sim_insn_last_update = m_gpu->gpu_sim_cycle; diff --git a/src/gpgpu-sim/shader.h b/src/gpgpu-sim/shader.h index 01dd92c..c25f185 100644 --- a/src/gpgpu-sim/shader.h +++ b/src/gpgpu-sim/shader.h @@ -2496,9 +2496,12 @@ class shader_core_ctx : public core_t { friend class scheduler_unit; // this is needed to use private issue warp. friend class TwoLevelScheduler; friend class LooseRoundRobbinScheduler; - virtual void issue_warp(register_set &warp, const warp_inst_t *pI, - const active_mask_t &active_mask, unsigned warp_id, - unsigned sch_id); + virtual warp_inst_t *issue_warp(register_set &warp, const warp_inst_t *pI, + const active_mask_t &active_mask, + unsigned warp_id, unsigned sch_id); + void co_issue_warp(warp_inst_t *composite, const warp_inst_t *next_inst, + const active_mask_t &active_mask, unsigned warp_id, + unsigned sch_id); void create_front_pipeline(); void create_schedulers(); -- cgit v1.3