diff options
Diffstat (limited to 'src/gpgpu-sim/shader.cc')
| -rw-r--r-- | src/gpgpu-sim/shader.cc | 277 |
1 files changed, 206 insertions, 71 deletions
diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc index a2a77a7..ec764aa 100644 --- a/src/gpgpu-sim/shader.cc +++ b/src/gpgpu-sim/shader.cc @@ -191,7 +191,8 @@ void shader_core_ctx::create_front_pipeline() { } void shader_core_ctx::create_schedulers() { - m_scoreboard = new Scoreboard(m_sid, m_config->max_warps_per_shader, m_gpu); + m_scoreboard = new Scoreboard(m_sid, m_config->max_warps_per_shader, m_gpu, + m_config->gpgpu_scoreboard_mode); // scedulers // must currently occur after all inputs have been initialized. @@ -1528,21 +1529,31 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite, } // Reserve scoreboard for the co-issued warp. - // For intra-warp co-issue (same warp_id), skip reservation — the primary - // instruction already reserved for this warp, and the scoreboard tracks at - // warp granularity. Both splits may write to the same register number - // (which is safe since they operate on exclusive threads), but the - // scoreboard would abort on a duplicate reservation. - if (split_id == (unsigned)-1) { - // Inter-warp co-issue: different warp_id, always safe to reserve + 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. m_scoreboard->reserveRegisters(&temp_inst); } else { - // Intra-warp co-issue: reserve on secondary scoreboard to track - // dependencies within the secondary split's instruction stream. - // Cannot use primary scoreboard (would abort on duplicate register names). - for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { - if (temp_inst.out[r] > 0) { - m_scoreboard->reserveRegisterSecondary(warp_id, temp_inst.out[r]); + // Mode 0 (legacy): + // For intra-warp co-issue (same warp_id), skip primary reservation — + // the primary instruction already reserved for this warp, and the + // scoreboard tracks at warp granularity. Both splits may write to the + // same register number (which is safe since they operate on exclusive + // threads), but the scoreboard would abort on a duplicate reservation. + if (split_id == (unsigned)-1) { + // Inter-warp co-issue: different warp_id, always safe to reserve + m_scoreboard->reserveRegisters(&temp_inst); + } else { + // Intra-warp co-issue: reserve on secondary scoreboard to track + // dependencies within the secondary split's instruction stream. + // Cannot use primary scoreboard (would abort on duplicate register + // names). + for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { + if (temp_inst.out[r] > 0) { + m_scoreboard->reserveRegisterSecondary(warp_id, temp_inst.out[r]); + } } } } @@ -1555,6 +1566,10 @@ void shader_core_ctx::co_issue_warp(warp_inst_t *composite, // between primary and secondary-split sets that share warp_id). temp_inst.set_source_inst_on_sets(next_inst); temp_inst.set_split_id_on_sets(split_id); + // Mode 1: stamp the source's full issue mask so per-set writeback can + // 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); // Mixed-space MEM co-issue (v2): for SHARED coissuers, capture per-lane // memreqaddr + temp_inst.cycles into each valid set BEFORE merge. The @@ -1948,7 +1963,20 @@ void scheduler_unit::try_inter_warp_coissue( } // Scoreboard check. - if (m_scoreboard->checkCollision(cand_warp_id, cand_inst)) { + // Mode 1 needs the candidate's active mask for hazard intersection; + // hoist its lookup before the legacy/mode-1 dispatch. + bool sb_collision_inter; + if (m_shader->m_config->gpgpu_scoreboard_mode == 1) { + const active_mask_t &cand_mask_pre = + m_shader->get_active_mask(cand_warp_id, cand_inst); + sb_collision_inter = m_scoreboard->checkCollisionMask(cand_warp_id, + cand_inst, + cand_mask_pre); + } else { + sb_collision_inter = m_scoreboard->checkCollision(cand_warp_id, + cand_inst); + } + if (sb_collision_inter) { m_stats->coissue_denied_by_scoreboard[get_sid()]++; continue; } @@ -2091,21 +2119,30 @@ void scheduler_unit::try_intra_warp_coissue( } } - if (m_scoreboard->checkCollisionSecondary(primary_warp_id, sec_inst)) { - m_stats->coissue_denied_by_scoreboard[get_sid()]++; - continue; - } - // Stale-mask guard: see commentary on the same fix in // try_utilization_max_coissue. Cached `sec_mask` may diverge from // current `split_mask` (lane re-bucketing or split-ID reuse). Dispatch // only the intersection — lanes in BOTH masks are guaranteed to have - // their per-thread PC at sec_inst->pc. + // their per-thread PC at sec_inst->pc. Hoisted before the scoreboard + // check so mode 1 can use the effective mask for hazard detection. active_mask_t sec_mask_eff; for (unsigned t = 0; t < MAX_WARP_SIZE; t++) { if (sec_mask.test(t) && split_mask.test(t)) sec_mask_eff.set(t); } if (!sec_mask_eff.any()) continue; + + bool sb_collision; + if (m_shader->m_config->gpgpu_scoreboard_mode == 1) { + sb_collision = m_scoreboard->checkCollisionMask(primary_warp_id, + sec_inst, sec_mask_eff); + } else { + sb_collision = m_scoreboard->checkCollisionSecondary(primary_warp_id, + sec_inst); + } + if (sb_collision) { + m_stats->coissue_denied_by_scoreboard[get_sid()]++; + continue; + } unsigned sec_active = sec_mask_eff.count(); unsigned sec_sets_needed = (sec_active + set_width - 1) / set_width; if (sec_sets_needed > available_sets) { @@ -2190,7 +2227,18 @@ void scheduler_unit::try_utilization_max_coissue( &cand_rpc); if (cand_pc != cand_inst->pc) continue; - if (m_scoreboard->checkCollision(cand_warp_id, cand_inst)) { + bool sb_collision_inter2; + if (m_shader->m_config->gpgpu_scoreboard_mode == 1) { + const active_mask_t &cand_mask_pre = + m_shader->get_active_mask(cand_warp_id, cand_inst); + sb_collision_inter2 = m_scoreboard->checkCollisionMask(cand_warp_id, + cand_inst, + cand_mask_pre); + } else { + sb_collision_inter2 = m_scoreboard->checkCollision(cand_warp_id, + cand_inst); + } + if (sb_collision_inter2) { m_stats->coissue_denied_by_scoreboard[get_sid()]++; continue; } @@ -2326,11 +2374,6 @@ void scheduler_unit::try_utilization_max_coissue( continue; } } - if (m_scoreboard->checkCollisionSecondary(cand_warp_id, sec_inst)) { - m_stats->coissue_denied_by_scoreboard[get_sid()]++; - continue; - } - // Stale-mask guard: the cached `sec_mask` is from secondary-fetch // time and may not match the current splits-table mask. Two // failure modes: @@ -2344,12 +2387,27 @@ void scheduler_unit::try_utilization_max_coissue( // cached mask is stale. // Both reduce to: trust only lanes present in BOTH masks. The // ibuffer slot ownership invariant guarantees lanes in `split_mask` - // are at `split_pc == sec_inst->pc`. + // are at `split_pc == sec_inst->pc`. Hoisted before the scoreboard + // check so mode 1 can use the effective mask for hazard detection. active_mask_t sec_mask_eff; for (unsigned t = 0; t < MAX_WARP_SIZE; t++) { if (sec_mask.test(t) && split_mask.test(t)) sec_mask_eff.set(t); } if (!sec_mask_eff.any()) continue; + + bool sb_collision; + if (m_shader->m_config->gpgpu_scoreboard_mode == 1) { + sb_collision = m_scoreboard->checkCollisionMask(cand_warp_id, + sec_inst, + sec_mask_eff); + } else { + sb_collision = m_scoreboard->checkCollisionSecondary(cand_warp_id, + sec_inst); + } + if (sb_collision) { + m_stats->coissue_denied_by_scoreboard[get_sid()]++; + continue; + } unsigned sec_active = sec_mask_eff.count(); unsigned sec_needed = (sec_active + set_width - 1) / set_width; if (sec_needed > available_sets) { @@ -2548,15 +2606,23 @@ void scheduler_unit::cycle() { m_scoreboard->clearSecondary(warp_id); } else { valid_inst = true; - if (!m_scoreboard->checkCollision(warp_id, pI)) { + // Mode 1 needs the warp's active mask for hazard intersection; + // hoist it before the scoreboard check. + const active_mask_t &active_mask = + m_shader->get_active_mask(warp_id, pI); + bool sb_collision_primary; + if (m_shader->m_config->gpgpu_scoreboard_mode == 1) { + sb_collision_primary = + m_scoreboard->checkCollisionMask(warp_id, pI, active_mask); + } else { + sb_collision_primary = m_scoreboard->checkCollision(warp_id, pI); + } + if (!sb_collision_primary) { SCHED_DPRINTF( "Warp (warp_id %u, dynamic_warp_id %u) passes scoreboard\n", (*iter)->get_warp_id(), (*iter)->get_dynamic_warp_id()); ready_inst = true; - const active_mask_t &active_mask = - m_shader->get_active_mask(warp_id, pI); - assert(warp(warp_id).inst_in_pipeline()); if ((pI->op == LOAD_OP) || (pI->op == STORE_OP) || @@ -3330,14 +3396,19 @@ void shader_core_ctx::writeback() { } // Release scoreboard for primary instruction (covers the outer warp_id) m_scoreboard->releaseRegisters(pipe_reg); - // Release scoreboard for ALL co-issued sets (inter-warp AND intra-warp) + // Release scoreboard for ALL co-issued sets (inter-warp AND intra-warp). + // Dispatcher: mode 0 -> primary release; mode 1 -> mask-aware release + // using the set's stamped source_mask. Idempotent; safe to call once + // per set even when multiple sets share the same source_inst. 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; for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { if (sets[s].source_inst->out[r] > 0) { - m_scoreboard->releaseRegister(set_wid, - sets[s].source_inst->out[r]); + m_scoreboard->releaseSetReg(set_wid, + sets[s].source_inst->out[r], + sets[s].source_mask, + /*is_intra_legacy=*/false); } } } @@ -3359,12 +3430,14 @@ void shader_core_ctx::writeback() { co_issued_warps_decremented.insert(sets[s].warp_id); } } else { - // Intra-warp: release from secondary scoreboard and dec once + // Intra-warp: release from secondary scoreboard (mode 0) or + // mask-aware (mode 1) and dec once. if (!intra_warp_decremented) { for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { if (sets[s].source_inst->out[r] > 0) { - m_scoreboard->releaseRegisterSecondary( - sets[s].warp_id, sets[s].source_inst->out[r]); + m_scoreboard->releaseSetReg( + sets[s].warp_id, sets[s].source_inst->out[r], + sets[s].source_mask, /*is_intra_legacy=*/true); } } m_warp[warp_id]->dec_inst_in_pipeline(); @@ -3532,6 +3605,11 @@ mem_stage_stall_type ldst_unit::process_cache_access( [src.out_inst->out[r]]--; else m_pending_writes[src.wid][src.out_inst->out[r]]--; + // Mode 1: per-mask decrement + release when the inst's own + // accesses fully drain (independent of other in-flight insts' + // contributions to the aggregate counter). + dec_mask_pw_and_maybe_release(src.wid, src.out_inst->out[r], + src.source_mask); } // release LDGSTS (primary-only: LDGSTS is excluded from MEM co-issue) @@ -3695,7 +3773,8 @@ void ldst_unit::L1_latency_queue_cycle() { if (!still_pending) { m_pending_writes_secondary[src.wid][src_split_mf].erase( reg); - m_scoreboard->releaseRegisterSecondary(src.wid, reg); + m_scoreboard->releaseSetReg(src.wid, reg, src.source_mask, + /*is_intra_legacy=*/true); any_completed = true; } } else { @@ -3703,10 +3782,14 @@ void ldst_unit::L1_latency_queue_cycle() { unsigned still_pending = --m_pending_writes[src.wid][reg]; if (!still_pending) { m_pending_writes[src.wid].erase(reg); - m_scoreboard->releaseRegister(src.wid, reg); + m_scoreboard->releaseSetReg(src.wid, reg, src.source_mask, + /*is_intra_legacy=*/false); any_completed = true; } } + // Mode 1: per-mask release fires per inst, decoupled from + // the aggregate counter above. + dec_mask_pw_and_maybe_release(src.wid, reg, src.source_mask); } } if (any_completed) m_core->warp_inst_complete(mf_next->get_inst()); @@ -4259,17 +4342,41 @@ ldst_unit::ldst_unit(mem_fetch_interface *icnt, mem_config, stats, sid, tpc); } +void ldst_unit::dec_mask_pw_and_maybe_release(unsigned wid, unsigned reg, + const active_mask_t &mask) { + if (m_core->get_config()->gpgpu_scoreboard_mode != 1) return; + unsigned long mk = mask.to_ulong(); + auto wit = m_pending_writes_mask.find(wid); + if (wit == m_pending_writes_mask.end()) return; + auto rit = wit->second.find(reg); + if (rit == wit->second.end()) return; + auto mit = rit->second.find(mk); + if (mit == rit->second.end()) return; + assert(mit->second > 0); + if (--(mit->second) == 0) { + rit->second.erase(mit); + if (rit->second.empty()) { + wit->second.erase(rit); + if (wit->second.empty()) m_pending_writes_mask.erase(wit); + } + m_scoreboard->releaseRegisterMask(wid, reg, mask); + } +} + ldst_unit::mem_src_t ldst_unit::resolve_source( const warp_inst_t &inst, unsigned access_src_wid, unsigned access_src_split_id) const { mem_src_t r; r.wid = (access_src_wid == (unsigned)-1) ? inst.warp_id() : access_src_wid; r.out_inst = &inst; + r.source_mask.reset(); // 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 // it came from the primary warp's own secondary ibuffer slot. 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(); return r; } if (!inst.has_simd_sets()) return r; @@ -4280,6 +4387,7 @@ ldst_unit::mem_src_t ldst_unit::resolve_source( if (sets[s].warp_id == r.wid && sets[s].split_id == access_src_split_id) { r.out_inst = sets[s].source_inst; + r.source_mask = sets[s].source_mask; break; } } @@ -4487,9 +4595,19 @@ void ldst_unit::issue(register_set ®_set) { unsigned n_primary = n_acc_per_src[std::make_pair(primary_wid, (unsigned)-1)]; if (n_primary > 0) { + const bool mode1 = + (m_core->get_config()->gpgpu_scoreboard_mode == 1); + unsigned long pri_mask_key = + mode1 ? inst->get_active_mask().to_ulong() : 0; for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { unsigned reg_id = inst->out[r]; - if (reg_id > 0) m_pending_writes[primary_wid][reg_id] += n_primary; + if (reg_id > 0) { + m_pending_writes[primary_wid][reg_id] += n_primary; + if (mode1) { + m_pending_writes_mask[primary_wid][reg_id][pri_mask_key] += + n_primary; + } + } } } } @@ -4513,6 +4631,10 @@ void ldst_unit::issue(register_set ®_set) { // is_intra = "uses secondary scoreboard map" = split came from a // secondary ibuffer slot. Not tied to warp_id == primary_wid. bool is_intra = (sets[s].split_id != (unsigned)-1); + const bool mode1 = + (m_core->get_config()->gpgpu_scoreboard_mode == 1); + unsigned long set_mask_key = + mode1 ? sets[s].source_mask.to_ulong() : 0; for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { unsigned reg_id = sets[s].source_inst->out[r]; if (reg_id > 0) { @@ -4522,15 +4644,29 @@ void ldst_unit::issue(register_set ®_set) { } else { m_pending_writes[sets[s].warp_id][reg_id] += n_src; } + if (mode1) { + m_pending_writes_mask[sets[s].warp_id][reg_id][set_mask_key] += + n_src; + } } } } } else if (primary_is_tracked_load) { // Legacy (non-co-issue) path: single-warp pending_writes. unsigned n_accesses = inst->accessq_count(); + const bool mode1 = + (m_core->get_config()->gpgpu_scoreboard_mode == 1); + unsigned long pri_mask_key = + mode1 ? inst->get_active_mask().to_ulong() : 0; for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { unsigned reg_id = inst->out[r]; - if (reg_id > 0) m_pending_writes[primary_wid][reg_id] += n_accesses; + if (reg_id > 0) { + m_pending_writes[primary_wid][reg_id] += n_accesses; + if (mode1) { + m_pending_writes_mask[primary_wid][reg_id][pri_mask_key] += + n_accesses; + } + } } } @@ -4631,7 +4767,9 @@ void ldst_unit::writeback() { if (!still_pending) { m_pending_writes_secondary[src_i.wid][src_split_i] .erase(reg); - m_scoreboard->releaseRegisterSecondary(src_i.wid, reg); + m_scoreboard->releaseSetReg(src_i.wid, reg, + src_i.source_mask, + /*is_intra_legacy=*/true); insn_completed = true; } } else { @@ -4640,10 +4778,14 @@ void ldst_unit::writeback() { --m_pending_writes[src_i.wid][reg]; if (!still_pending) { m_pending_writes[src_i.wid].erase(reg); - m_scoreboard->releaseRegister(src_i.wid, reg); + m_scoreboard->releaseSetReg(src_i.wid, reg, + src_i.source_mask, + /*is_intra_legacy=*/false); insn_completed = true; } } + // Mode 1: per-mask release fires per inst. + dec_mask_pw_and_maybe_release(src_i.wid, reg, src_i.source_mask); } else if (m_next_wb.m_is_ldgsts) { // LDGSTS excluded from co-issue; source_list always size<=1 m_pending_ldgsts[primary_wid][m_next_wb.pc] @@ -4663,7 +4805,9 @@ void ldst_unit::writeback() { (void)is_intra_coissued; // unused on shared path for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { if (src.out_inst->out[r] > 0) { - m_scoreboard->releaseRegister(src.wid, src.out_inst->out[r]); + m_scoreboard->releaseSetReg(src.wid, src.out_inst->out[r], + src.source_mask, + /*is_intra_legacy=*/false); insn_completed = true; } } @@ -4682,16 +4826,13 @@ void ldst_unit::writeback() { sets[s].split_id); if (released.count(key)) continue; released.insert(key); - // Secondary slot coissuer → secondary scoreboard map. + // Secondary slot coissuer → secondary scoreboard map (mode 0). bool coissued_intra = (sets[s].split_id != (unsigned)-1); for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { if (sets[s].source_inst->out[r] > 0) { - if (coissued_intra) - m_scoreboard->releaseRegisterSecondary( - sets[s].warp_id, sets[s].source_inst->out[r]); - else - m_scoreboard->releaseRegister( - sets[s].warp_id, sets[s].source_inst->out[r]); + m_scoreboard->releaseSetReg( + sets[s].warp_id, sets[s].source_inst->out[r], + sets[s].source_mask, /*is_intra_legacy=*/coissued_intra); insn_completed = true; } } @@ -5013,10 +5154,9 @@ void ldst_unit::cycle() { for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { unsigned reg = sets[s].source_inst->out[r]; if (reg == 0) continue; - if (is_intra) - m_scoreboard->releaseRegisterSecondary(sets[s].warp_id, reg); - else - m_scoreboard->releaseRegister(sets[s].warp_id, reg); + m_scoreboard->releaseSetReg(sets[s].warp_id, reg, + sets[s].source_mask, + /*is_intra_legacy=*/is_intra); } } pipe_reg.set_shared_side_released(true); @@ -5188,10 +5328,9 @@ void ldst_unit::cycle() { for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { unsigned reg_id = sets[s].source_inst->out[r]; if (reg_id == 0) continue; - if (is_intra) - m_scoreboard->releaseRegisterSecondary(sets[s].warp_id, reg_id); - else - m_scoreboard->releaseRegister(sets[s].warp_id, reg_id); + m_scoreboard->releaseSetReg(sets[s].warp_id, reg_id, + sets[s].source_mask, + /*is_intra_legacy=*/is_intra); } } } @@ -5331,16 +5470,14 @@ void ldst_unit::cycle() { sets[s].split_id); if (released.count(key)) continue; released.insert(key); - // Secondary slot → secondary scoreboard map. + // Secondary slot → secondary scoreboard map (mode 0). bool is_intra = (sets[s].split_id != (unsigned)-1); for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { unsigned reg_id = sets[s].source_inst->out[r]; if (reg_id == 0) continue; - if (is_intra) - m_scoreboard->releaseRegisterSecondary(sets[s].warp_id, - reg_id); - else - m_scoreboard->releaseRegister(sets[s].warp_id, reg_id); + m_scoreboard->releaseSetReg(sets[s].warp_id, reg_id, + sets[s].source_mask, + /*is_intra_legacy=*/is_intra); } } } @@ -5423,11 +5560,9 @@ void ldst_unit::cycle() { for (unsigned r = 0; r < MAX_OUTPUT_VALUES; r++) { unsigned reg_id = sets[s].source_inst->out[r]; if (reg_id == 0) continue; - if (is_intra) - m_scoreboard->releaseRegisterSecondary(sets[s].warp_id, - reg_id); - else - m_scoreboard->releaseRegister(sets[s].warp_id, reg_id); + m_scoreboard->releaseSetReg(sets[s].warp_id, reg_id, + sets[s].source_mask, + /*is_intra_legacy=*/is_intra); } } } |
