Harden bitset access

This commit is contained in:
Megamouse
2026-05-13 23:27:39 +02:00
parent 6f50f0854f
commit c5ee48f54a
14 changed files with 264 additions and 139 deletions
+5 -4
View File
@@ -2,7 +2,6 @@
#include "Emu/System.h" #include "Emu/System.h"
#include "Emu/Cell/PPUModule.h" #include "Emu/Cell/PPUModule.h"
#include <bitset>
#include "cellPamf.h" #include "cellPamf.h"
LOG_CHANNEL(cellPamf); LOG_CHANNEL(cellPamf);
@@ -500,7 +499,7 @@ error_code pamfVerify(vm::cptr<PamfHeader> pAddr, u64 fileSize, vm::ptr<CellPamf
const auto streams = &pAddr->seq_info.grouping_periods.groups.streams; const auto streams = &pAddr->seq_info.grouping_periods.groups.streams;
std::bitset<16> channels_used[6]{}; std::array<bit_set<16>, 6> channels_used{};
u32 end_of_streams_addr = 0; u32 end_of_streams_addr = 0;
u32 next_ep_table_addr = 0; u32 next_ep_table_addr = 0;
@@ -516,14 +515,16 @@ error_code pamfVerify(vm::cptr<PamfHeader> pAddr, u64 fileSize, vm::ptr<CellPamf
return CELL_PAMF_ERROR_UNKNOWN_STREAM; return CELL_PAMF_ERROR_UNKNOWN_STREAM;
} }
bit_set<16>& used_channels = ::at32(channels_used, *type);
// Every channel may only be used once per type // Every channel may only be used once per type
if (channels_used[*type].test(*ch)) if (used_channels.test(*ch))
{ {
return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid channel" }; return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid channel" };
} }
// Mark channel as used // Mark channel as used
channels_used[*type].set(*ch); used_channels.set(*ch, true);
const u32 ep_offset = streams[stream_idx].ep_offset; const u32 ep_offset = streams[stream_idx].ep_offset;
const u32 ep_num = streams[stream_idx].ep_num; const u32 ep_num = streams[stream_idx].ep_num;
+10 -9
View File
@@ -1,6 +1,5 @@
#include "stdafx.h" #include "stdafx.h"
#include <bitset>
#include <string> #include <string>
#include "cellSsl.h" #include "cellSsl.h"
@@ -92,7 +91,7 @@ std::string getCert(const std::string& certPath, const int certID, const bool is
newID = certID - 1; newID = certID - 1;
} }
std::string filePath = fmt::format("%sCA%02d.cer", certPath, newID); const std::string filePath = fmt::format("%sCA%02d.cer", certPath, newID);
if (!fs::exists(filePath)) if (!fs::exists(filePath))
{ {
@@ -106,7 +105,7 @@ error_code cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm
{ {
cellSsl.trace("cellSslCertificateLoader(flag=%llu, buffer=*0x%x, size=%zu, required=*0x%x)", flag, buffer, size, required); cellSsl.trace("cellSslCertificateLoader(flag=%llu, buffer=*0x%x, size=%zu, required=*0x%x)", flag, buffer, size, required);
const std::bitset<58> flagBits(flag); const bit_set<58> flagBits(flag);
const std::string certPath = vfs::get("/dev_flash/data/cert/"); const std::string certPath = vfs::get("/dev_flash/data/cert/");
if (required) if (required)
@@ -114,10 +113,11 @@ error_code cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm
*required = 0; *required = 0;
for (uint i = 1; i <= flagBits.size(); i++) for (uint i = 1; i <= flagBits.size(); i++)
{ {
if (!flagBits[i-1]) if (!flagBits.test(i - 1))
continue; continue;
// If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs. // If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs.
*required += ::size32(getCert(certPath, i, flagBits[BaltimoreCert-1])); *required += ::size32(getCert(certPath, i, flagBits.test(BaltimoreCert - 1)));
} }
} }
else else
@@ -125,14 +125,15 @@ error_code cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm
std::string final; std::string final;
for (uint i = 1; i <= flagBits.size(); i++) for (uint i = 1; i <= flagBits.size(); i++)
{ {
if (!flagBits[i-1]) if (!flagBits.test(i - 1))
continue; continue;
// If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs. // If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs.
final.append(getCert(certPath, i, flagBits[BaltimoreCert-1])); final.append(getCert(certPath, i, flagBits.test(BaltimoreCert - 1)));
} }
memset(buffer.get_ptr(), '\0', size - 1); std::memset(buffer.get_ptr(), '\0', size - 1);
memcpy(buffer.get_ptr(), final.c_str(), final.size()); std::memcpy(buffer.get_ptr(), final.c_str(), final.size());
} }
return CELL_OK; return CELL_OK;
+58 -58
View File
@@ -2905,7 +2905,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
// Check for redundancy // Check for redundancy
if (!m_block_info[target / 4]) if (!m_block_info[target / 4])
{ {
m_block_info[target / 4] = true; m_block_info.set(target / 4, true);
workload.push_back(target); workload.push_back(target);
} }
@@ -3080,7 +3080,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
else else
{ {
m_entry_info[target / 4] = true; m_entry_info.set(target / 4, true);
add_block(target); add_block(target);
} }
} }
@@ -3091,8 +3091,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (!is_no_return && sl && g_cfg.core.spu_block_size != spu_block_size_type::safe) if (!is_no_return && sl && g_cfg.core.spu_block_size != spu_block_size_type::safe)
{ {
m_ret_info[pos / 4 + 1] = true; m_ret_info.set(pos / 4 + 1, true);
m_entry_info[pos / 4 + 1] = true; m_entry_info.set(pos / 4 + 1, true);
m_targets[pos].push_back(pos + 4); m_targets[pos].push_back(pos + 4);
add_block(pos + 4); add_block(pos + 4);
} }
@@ -3307,8 +3307,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
else else
{ {
m_ret_info[pos / 4 + 1] = true; m_ret_info.set(pos / 4 + 1, true);
m_entry_info[pos / 4 + 1] = true; m_entry_info.set(pos / 4 + 1, true);
m_targets[pos].push_back(pos + 4); m_targets[pos].push_back(pos + 4);
add_block(pos + 4); add_block(pos + 4);
} }
@@ -3375,15 +3375,15 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (!is_no_return && g_cfg.core.spu_block_size != spu_block_size_type::safe) if (!is_no_return && g_cfg.core.spu_block_size != spu_block_size_type::safe)
{ {
m_ret_info[pos / 4 + 1] = true; m_ret_info.set(pos / 4 + 1, true);
m_entry_info[pos / 4 + 1] = true; m_entry_info.set(pos / 4 + 1, true);
m_targets[pos].push_back(pos + 4); m_targets[pos].push_back(pos + 4);
add_block(pos + 4); add_block(pos + 4);
} }
if (!is_no_return && g_cfg.core.spu_block_size == spu_block_size_type::giga && !sync) if (!is_no_return && g_cfg.core.spu_block_size == spu_block_size_type::giga && !sync)
{ {
m_entry_info[target / 4] = true; m_entry_info.set(target / 4, true);
add_block(target); add_block(target);
} }
else else
@@ -3409,7 +3409,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (g_cfg.core.spu_block_size == spu_block_size_type::giga && !sync) if (g_cfg.core.spu_block_size == spu_block_size_type::giga && !sync)
{ {
m_entry_info[target / 4] = true; m_entry_info.set(target / 4, true);
} }
else else
{ {
@@ -3714,7 +3714,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
// Bit array used to deduplicate workload list // Bit array used to deduplicate workload list
workload.push_back(pair.first); workload.push_back(pair.first);
m_bits[pair.first / 4] = true; m_bits.set(pair.first / 4, true);
for (usz i = 0; !reachable && i < workload.size(); i++) for (usz i = 0; !reachable && i < workload.size(); i++)
{ {
@@ -3746,7 +3746,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (new_pred >= lsa && new_pred < limit && !m_bits[new_pred / 4]) if (new_pred >= lsa && new_pred < limit && !m_bits[new_pred / 4])
{ {
workload.push_back(new_pred); workload.push_back(new_pred);
m_bits[new_pred / 4] = true; m_bits.set(new_pred / 4, true);
} }
} }
} }
@@ -3769,7 +3769,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
for (u32 pred : workload) for (u32 pred : workload)
{ {
m_bits[pred / 4] = false; m_bits.set(pred / 4, false);
} }
if (!reachable && pair.first < limit) if (!reachable && pair.first < limit)
@@ -3828,9 +3828,9 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (addr < lsa || addr >= limit || !result.data[(addr - lsa) / 4]) if (addr < lsa || addr >= limit || !result.data[(addr - lsa) / 4])
{ {
m_block_info[addr / 4] = false; m_block_info.set(addr / 4, false);
m_entry_info[addr / 4] = false; m_entry_info.set(addr / 4, false);
m_ret_info[addr / 4] = false; m_ret_info.set(addr / 4, false);
m_preds.erase(addr); m_preds.erase(addr);
} }
} }
@@ -3856,7 +3856,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (it->second.empty() && !m_entry_info[it->first / 4]) if (it->second.empty() && !m_entry_info[it->first / 4])
{ {
// If not an entry point, remove the block completely // If not an entry point, remove the block completely
m_block_info[it->first / 4] = false; m_block_info.set(it->first / 4, false);
it = m_preds.erase(it); it = m_preds.erase(it);
continue; continue;
} }
@@ -3964,7 +3964,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (type == spu_itype::STQD && op.ra == s_reg_sp && !block.reg_mod[op.rt] && !block.reg_use[op.rt]) if (type == spu_itype::STQD && op.ra == s_reg_sp && !block.reg_mod[op.rt] && !block.reg_use[op.rt])
{ {
// Register saved onto the stack before use // Register saved onto the stack before use
block.reg_save_dom[op.rt] = true; block.reg_save_dom.set(op.rt, true);
reg_save = op.rt; reg_save = op.rt;
} }
@@ -3994,7 +3994,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (reg_save != reg && block.reg_save_dom[reg]) if (reg_save != reg && block.reg_save_dom[reg])
{ {
// Register is still used after saving; probably not eligible for optimization // Register is still used after saving; probably not eligible for optimization
block.reg_save_dom[reg] = false; block.reg_save_dom.set(reg, false);
} }
} }
} }
@@ -4065,7 +4065,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (!is_tail) if (!is_tail)
{ {
block.reg_mod.set(i); block.reg_mod.set(i);
block.reg_mod_xf[i] = false; block.reg_mod_xf.set(i, false);
} }
} }
} }
@@ -4106,8 +4106,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
{ {
const u32 addr = emp.first->first; const u32 addr = emp.first->first;
spu_log.error("[0x%05x] Fixed first function at 0x%05x", entry_point, addr); spu_log.error("[0x%05x] Fixed first function at 0x%05x", entry_point, addr);
m_entry_info[addr / 4] = true; m_entry_info.set(addr / 4, true);
m_ret_info[addr / 4] = false; m_ret_info.set(addr / 4, false);
} }
} }
@@ -4146,9 +4146,9 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (!m_entry_info[target / 4] || m_ret_info[target / 4]) if (!m_entry_info[target / 4] || m_ret_info[target / 4])
{ {
// Create new function entry (likely a tail call) // Create new function entry (likely a tail call)
m_entry_info[target / 4] = true; m_entry_info.set(target / 4, true);
m_ret_info[target / 4] = false; m_ret_info.set(target / 4, false);
m_funcs.try_emplace(target); m_funcs.try_emplace(target);
@@ -4246,10 +4246,10 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
for (u32 entry : new_entries) for (u32 entry : new_entries)
{ {
m_entry_info[entry / 4] = true; m_entry_info.set(entry / 4, true);
// Acknowledge artificial (reversible) chunk entry point // Acknowledge artificial (reversible) chunk entry point
m_ret_info[entry / 4] = true; m_ret_info.set(entry / 4, true);
} }
for (auto& bb : m_bbs) for (auto& bb : m_bbs)
@@ -4435,7 +4435,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
if (orig < 0x40000) if (orig < 0x40000)
{ {
auto& src = ::at32(m_bbs, orig); auto& src = ::at32(m_bbs, orig);
bb.reg_const[i] = src.reg_const[i]; bb.reg_const.set(i, src.reg_const[i]);
bb.reg_val32[i] = src.reg_val32[i]; bb.reg_val32[i] = src.reg_val32[i];
} }
@@ -4474,25 +4474,25 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
{ {
case spu_itype::IL: case spu_itype::IL:
{ {
bb.reg_const[op.rt] = true; bb.reg_const.set(op.rt, true);
bb.reg_val32[op.rt] = op.si16; bb.reg_val32[op.rt] = op.si16;
break; break;
} }
case spu_itype::ILA: case spu_itype::ILA:
{ {
bb.reg_const[op.rt] = true; bb.reg_const.set(op.rt, true);
bb.reg_val32[op.rt] = op.i18; bb.reg_val32[op.rt] = op.i18;
break; break;
} }
case spu_itype::ILHU: case spu_itype::ILHU:
{ {
bb.reg_const[op.rt] = true; bb.reg_const.set(op.rt, true);
bb.reg_val32[op.rt] = op.i16 << 16; bb.reg_val32[op.rt] = op.i16 << 16;
break; break;
} }
case spu_itype::ILH: case spu_itype::ILH:
{ {
bb.reg_const[op.rt] = true; bb.reg_const.set(op.rt, true);
bb.reg_val32[op.rt] = op.i16 << 16 | op.i16; bb.reg_val32[op.rt] = op.i16 << 16 | op.i16;
break; break;
} }
@@ -4503,37 +4503,37 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
case spu_itype::ORI: case spu_itype::ORI:
{ {
bb.reg_const[op.rt] = bb.reg_const[op.ra]; bb.reg_const.set(op.rt, bb.reg_const[op.ra]);
bb.reg_val32[op.rt] = bb.reg_val32[op.ra] | op.si10; bb.reg_val32[op.rt] = bb.reg_val32[op.ra] | op.si10;
break; break;
} }
case spu_itype::OR: case spu_itype::OR:
{ {
bb.reg_const[op.rt] = bb.reg_const[op.ra] && bb.reg_const[op.rb]; bb.reg_const.set(op.rt, bb.reg_const[op.ra] && bb.reg_const[op.rb]);
bb.reg_val32[op.rt] = bb.reg_val32[op.ra] | bb.reg_val32[op.rb]; bb.reg_val32[op.rt] = bb.reg_val32[op.ra] | bb.reg_val32[op.rb];
break; break;
} }
case spu_itype::AI: case spu_itype::AI:
{ {
bb.reg_const[op.rt] = bb.reg_const[op.ra]; bb.reg_const.set(op.rt, bb.reg_const[op.ra]);
bb.reg_val32[op.rt] = bb.reg_val32[op.ra] + op.si10; bb.reg_val32[op.rt] = bb.reg_val32[op.ra] + op.si10;
break; break;
} }
case spu_itype::A: case spu_itype::A:
{ {
bb.reg_const[op.rt] = bb.reg_const[op.ra] && bb.reg_const[op.rb]; bb.reg_const.set(op.rt, bb.reg_const[op.ra] && bb.reg_const[op.rb]);
bb.reg_val32[op.rt] = bb.reg_val32[op.ra] + bb.reg_val32[op.rb]; bb.reg_val32[op.rt] = bb.reg_val32[op.ra] + bb.reg_val32[op.rb];
break; break;
} }
case spu_itype::SFI: case spu_itype::SFI:
{ {
bb.reg_const[op.rt] = bb.reg_const[op.ra]; bb.reg_const.set(op.rt, bb.reg_const[op.ra]);
bb.reg_val32[op.rt] = op.si10 - bb.reg_val32[op.ra]; bb.reg_val32[op.rt] = op.si10 - bb.reg_val32[op.ra];
break; break;
} }
case spu_itype::SF: case spu_itype::SF:
{ {
bb.reg_const[op.rt] = bb.reg_const[op.ra] && bb.reg_const[op.rb]; bb.reg_const.set(op.rt, bb.reg_const[op.ra] && bb.reg_const[op.rb]);
bb.reg_val32[op.rt] = bb.reg_val32[op.rb] - bb.reg_val32[op.ra]; bb.reg_val32[op.rt] = bb.reg_val32[op.rb] - bb.reg_val32[op.ra];
break; break;
} }
@@ -4566,14 +4566,14 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
// Clear const // Clear const
bb.reg_const[op.rt] = false; bb.reg_const.set(op.rt, false);
break; break;
} }
default: default:
{ {
// Clear const if reg is modified here // Clear const if reg is modified here
if (u8 reg = m_regmod[ia / 4]; reg < s_reg_max) if (u8 reg = m_regmod[ia / 4]; reg < s_reg_max)
bb.reg_const[reg] = false; bb.reg_const.set(reg, false);
break; break;
} }
} }
@@ -6103,17 +6103,17 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
u32 ra = s_reg_max, rb = s_reg_max, rc = s_reg_max; u32 ra = s_reg_max, rb = s_reg_max, rc = s_reg_max;
if (::at32(m_use_ra, pos / 4)) if (m_use_ra.test(pos / 4))
{ {
ra = op.ra; ra = op.ra;
} }
if (::at32(m_use_rb, pos / 4)) if (m_use_rb.test(pos / 4))
{ {
rb = op.rb; rb = op.rb;
} }
if (::at32(m_use_rc, pos / 4)) if (m_use_rc.test(pos / 4))
{ {
rc = op.rc; rc = op.rc;
} }
@@ -6373,8 +6373,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
std::array<u32, s_reg_max> reg_use{}; std::array<u32, s_reg_max> reg_use{};
std::bitset<s_reg_max> reg_maybe_float{}; bit_set<s_reg_max> reg_maybe_float{};
std::bitset<s_reg_max> reg_mod{}; bit_set<s_reg_max> reg_mod{};
for (auto it = m_bbs.find(reduced_loop->loop_pc); it != m_bbs.end() && it->first <= bpc; it++) for (auto it = m_bbs.find(reduced_loop->loop_pc); it != m_bbs.end() && it->first <= bpc; it++)
{ {
@@ -6398,7 +6398,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
for (u32 i = 0; i < s_reg_max; i++) for (u32 i = 0; i < s_reg_max; i++)
{ {
if (!::at32(reduced_loop->loop_dicts, i)) if (!reduced_loop->loop_dicts.test(i))
{ {
if (reg_use[i] && reg_mod[i]) if (reg_use[i] && reg_mod[i])
{ {
@@ -6662,7 +6662,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
for (u32 i = 0; i < reg->regs.size() && reduced_loop->active; i++) for (u32 i = 0; i < reg->regs.size() && reduced_loop->active; i++)
{ {
if (::at32(reg->regs, i)) if (reg->regs.test(i))
{ {
if (0) if (i == op_rt || reg->modified == 0) if (0) if (i == op_rt || reg->modified == 0)
{ {
@@ -6708,11 +6708,11 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
auto reg_org = reduced_loop->find_reg(i); auto reg_org = reduced_loop->find_reg(i);
u32 reg_index = i; u32 reg_index = i;
if (reg_org && !cond_val_incr_before_cond && reg_org->modified == 0 && reg_org->regs.count() - 1u <= 1u && !::at32(reg_org->regs, i)) if (reg_org && !cond_val_incr_before_cond && reg_org->modified == 0 && reg_org->regs.count() - 1u <= 1u && !reg_org->regs.test(i))
{ {
for (u32 j = 0; j <= s_reg_127; j++) for (u32 j = 0; j <= s_reg_127; j++)
{ {
if (::at32(reg_org->regs, j)) if (reg_org->regs.test(j))
{ {
if (const auto reg_found = reduced_loop->find_reg(j)) if (const auto reg_found = reduced_loop->find_reg(j))
{ {
@@ -6781,7 +6781,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
break; break;
} }
if (reg_index != i && ::at32(reg->regs, reg_index)) if (reg_index != i && reg->regs.test(reg_index))
{ {
// Unimplemented // Unimplemented
break_reduced_loop_pattern(30, reduced_loop->discard()); break_reduced_loop_pattern(30, reduced_loop->discard());
@@ -7104,8 +7104,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
} }
std::array<u32, s_reg_max> reg_use{}; std::array<u32, s_reg_max> reg_use{};
std::bitset<s_reg_max> reg_maybe_float{}; bit_set<s_reg_max> reg_maybe_float{};
std::bitset<s_reg_max> reg_mod{}; bit_set<s_reg_max> reg_mod{};
for (auto it = m_bbs.find(reduced_loop->loop_pc); it != m_bbs.end() && it->first <= bpc; it++) for (auto it = m_bbs.find(reduced_loop->loop_pc); it != m_bbs.end() && it->first <= bpc; it++)
{ {
@@ -7129,7 +7129,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
for (u32 i = 0; i < s_reg_max; i++) for (u32 i = 0; i < s_reg_max; i++)
{ {
if (!::at32(reduced_loop->loop_dicts, i)) if (!reduced_loop->loop_dicts.test(i))
{ {
if (reg_use[i] && reg_mod[i]) if (reg_use[i] && reg_mod[i])
{ {
@@ -8412,17 +8412,17 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
u32 ra = s_reg_max, rb = s_reg_max, rc = s_reg_max; u32 ra = s_reg_max, rb = s_reg_max, rc = s_reg_max;
if (::at32(m_use_ra, pos / 4)) if (m_use_ra.test(pos / 4))
{ {
ra = op.ra; ra = op.ra;
} }
if (::at32(m_use_rb, pos / 4)) if (m_use_rb.test(pos / 4))
{ {
rb = op.rb; rb = op.rb;
} }
if (::at32(m_use_rc, pos / 4)) if (m_use_rc.test(pos / 4))
{ {
rc = op.rc; rc = op.rc;
} }
@@ -8693,7 +8693,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
for (u32 i = 0; i < s_reg_max; i++) for (u32 i = 0; i < s_reg_max; i++)
{ {
if (::at32(pattern.loop_writes, i)) if (pattern.loop_writes.test(i))
{ {
if (regs.size() != 1) if (regs.size() != 1)
{ {
@@ -8703,7 +8703,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
fmt::append(regs, " r%u-w", i); fmt::append(regs, " r%u-w", i);
} }
if (::at32(pattern.loop_args, i)) if (pattern.loop_args.test(i))
{ {
if (regs.size() != 1) if (regs.size() != 1)
{ {
@@ -8713,7 +8713,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
fmt::append(regs, " r%u-r", i); fmt::append(regs, " r%u-r", i);
} }
if (::at32(pattern.loop_may_update, i)) if (pattern.loop_may_update.test(i))
{ {
if (regs.size() != 1) if (regs.size() != 1)
{ {
+9 -9
View File
@@ -7148,8 +7148,8 @@ public:
{ {
const value_t<f32[4]> ab[2]{a, b}; const value_t<f32[4]> ab[2]{a, b};
std::bitset<2> safe_int_compare(0); bit_set<2> safe_int_compare(0);
std::bitset<2> safe_finite_compare(0); bit_set<2> safe_finite_compare(0);
for (u32 i = 0; i < 2; i++) for (u32 i = 0; i < 2; i++)
{ {
@@ -7192,12 +7192,12 @@ public:
return eval(sext<s32[4]>(bitcast<s32[4]>(a) > bitcast<s32[4]>(b))); return eval(sext<s32[4]>(bitcast<s32[4]>(a) > bitcast<s32[4]>(b)));
} }
if (safe_finite_compare.test(1)) if (safe_finite_compare.test(1u))
{ {
return eval(sext<s32[4]>(fcmp_uno(clamp_negative_smax(a) > b))); return eval(sext<s32[4]>(fcmp_uno(clamp_negative_smax(a) > b)));
} }
if (safe_finite_compare.test(0)) if (safe_finite_compare.test(0u))
{ {
return eval(sext<s32[4]>(fcmp_ord(a > clamp_smax(b)))); return eval(sext<s32[4]>(fcmp_ord(a > clamp_smax(b))));
} }
@@ -7247,7 +7247,7 @@ public:
const value_t<f32[4]> ab[2]{a, b}; const value_t<f32[4]> ab[2]{a, b};
std::bitset<2> safe_int_compare(0); bit_set<2> safe_int_compare(0);
for (u32 i = 0; i < 2; i++) for (u32 i = 0; i < 2; i++)
{ {
@@ -7521,8 +7521,8 @@ public:
const value_t<f32[4]> ab[2]{a, b}; const value_t<f32[4]> ab[2]{a, b};
std::bitset<2> safe_float_compare(0); bit_set<2> safe_float_compare(0);
std::bitset<2> safe_int_compare(0); bit_set<2> safe_int_compare(0);
for (u32 i = 0; i < 2; i++) for (u32 i = 0; i < 2; i++)
{ {
@@ -7595,8 +7595,8 @@ public:
const value_t<f32[4]> ab[2]{a, b}; const value_t<f32[4]> ab[2]{a, b};
std::bitset<2> safe_float_compare(0); bit_set<2> safe_float_compare(0);
std::bitset<2> safe_int_compare(0); bit_set<2> safe_int_compare(0);
for (u32 i = 0; i < 2; i++) for (u32 i = 0; i < 2; i++)
{ {
+24 -36
View File
@@ -6,22 +6,10 @@
#include "SPUThread.h" #include "SPUThread.h"
#include "SPUAnalyser.h" #include "SPUAnalyser.h"
#include <vector> #include <vector>
#include <bitset>
#include <memory> #include <memory>
#include <string> #include <string>
#include <deque> #include <deque>
// std::bitset
template <typename CT, typename T>
requires requires(std::remove_cvref_t<CT>& x, T&& y) { x.count(); x.test(y); x.flip(y); }
[[nodiscard]] constexpr bool at32(CT&& container, T&& index, std::source_location src_loc = std::source_location::current())
{
const usz csv = container.size();
if (csv <= std::forward<T>(index)) [[unlikely]]
fmt::raw_range_error(src_loc, format_object_simplified(index), csv);
return container[std::forward<T>(index)];
}
// Helper class // Helper class
class spu_cache class spu_cache
{ {
@@ -358,15 +346,15 @@ public:
} }
} }
std::bitset<s_reg_max> loop_args; bit_set<s_reg_max> loop_args;
std::bitset<s_reg_max> loop_dicts; bit_set<s_reg_max> loop_dicts;
std::bitset<s_reg_max> loop_writes; bit_set<s_reg_max> loop_writes;
std::bitset<s_reg_max> loop_may_update; bit_set<s_reg_max> loop_may_update;
std::bitset<s_reg_max> gpr_not_nans; bit_set<s_reg_max> gpr_not_nans;
struct origin_t struct origin_t
{ {
std::bitset<s_reg_max + 1> regs{}; bit_set<s_reg_max + 1> regs{};
u32 modified = 0; u32 modified = 0;
spu_itype_t mod1_type = spu_itype::UNK; spu_itype_t mod1_type = spu_itype::UNK;
spu_itype_t mod2_type = spu_itype::UNK; spu_itype_t mod2_type = spu_itype::UNK;
@@ -434,7 +422,7 @@ public:
return true; return true;
} }
return regs.count() == 1 && ::at32(regs, reg_val); return regs.count() == 1 && regs.test(reg_val);
} }
bool is_loop_dictator(u32 reg_val, bool test_predictable = false, bool should_predictable = true) const bool is_loop_dictator(u32 reg_val, bool test_predictable = false, bool should_predictable = true) const
@@ -444,7 +432,7 @@ public:
return false; return false;
} }
if (regs.count() >= 1 && ::at32(regs, reg_val)) if (regs.count() >= 1 && regs.test(reg_val))
{ {
if (!test_predictable) if (!test_predictable)
{ {
@@ -503,7 +491,7 @@ public:
return false; return false;
} }
if (regs.count() - (::at32(regs, reg_val) ? 1 : 0)) if (regs.count() - (regs.test(reg_val) ? 1 : 0))
{ {
return false; return false;
} }
@@ -686,7 +674,7 @@ public:
bool is_gpr_not_NaN_hint(u32 i) const noexcept bool is_gpr_not_NaN_hint(u32 i) const noexcept
{ {
return ::at32(gpr_not_nans, i); return gpr_not_nans.test(i);
} }
origin_t get_reg(u32 reg_val) noexcept origin_t get_reg(u32 reg_val) noexcept
@@ -714,14 +702,14 @@ protected:
u64 m_hash_start; u64 m_hash_start;
// Bit indicating start of the block // Bit indicating start of the block
std::bitset<SPU_LS_SIZE / 4> m_block_info; bit_set<SPU_LS_SIZE / 4> m_block_info;
// GPR modified by the instruction (-1 = not set) // GPR modified by the instruction (-1 = not set)
std::array<u8, SPU_LS_SIZE / 4> m_regmod; std::array<u8, SPU_LS_SIZE / 4> m_regmod;
std::bitset<SPU_LS_SIZE / 4> m_use_ra; bit_set<SPU_LS_SIZE / 4> m_use_ra;
std::bitset<SPU_LS_SIZE / 4> m_use_rb; bit_set<SPU_LS_SIZE / 4> m_use_rb;
std::bitset<SPU_LS_SIZE / 4> m_use_rc; bit_set<SPU_LS_SIZE / 4> m_use_rc;
// List of possible targets for the instruction (entry shouldn't exist for simple instructions) // List of possible targets for the instruction (entry shouldn't exist for simple instructions)
std::unordered_map<u32, std::vector<u32>, value_hash<u32, 2>> m_targets; std::unordered_map<u32, std::vector<u32>, value_hash<u32, 2>> m_targets;
@@ -730,10 +718,10 @@ protected:
std::unordered_map<u32, std::vector<u32>, value_hash<u32, 2>> m_preds; std::unordered_map<u32, std::vector<u32>, value_hash<u32, 2>> m_preds;
// List of function entry points and return points (set after BRSL, BRASL, BISL, BISLED) // List of function entry points and return points (set after BRSL, BRASL, BISL, BISLED)
std::bitset<SPU_LS_SIZE / 4> m_entry_info; bit_set<SPU_LS_SIZE / 4> m_entry_info;
// Set after return points and disjoint chunks // Set after return points and disjoint chunks
std::bitset<SPU_LS_SIZE / 4> m_ret_info; bit_set<SPU_LS_SIZE / 4> m_ret_info;
// Basic block information // Basic block information
struct block_info struct block_info
@@ -751,28 +739,28 @@ protected:
term_type terminator; term_type terminator;
// Bit mask of the registers modified in the block // Bit mask of the registers modified in the block
std::bitset<s_reg_max> reg_mod{}; bit_set<s_reg_max> reg_mod{};
// Set if last modifying instruction produces xfloat // Set if last modifying instruction produces xfloat
std::bitset<s_reg_max> reg_mod_xf{}; bit_set<s_reg_max> reg_mod_xf{};
// Set if the initial register value in this block may be xfloat // Set if the initial register value in this block may be xfloat
std::bitset<s_reg_max> reg_maybe_xf{}; bit_set<s_reg_max> reg_maybe_xf{};
// Set if register is used in floating pont instruction // Set if register is used in floating pont instruction
std::bitset<s_reg_max> reg_maybe_float{}; bit_set<s_reg_max> reg_maybe_float{};
// Set if register is used as shuffle mask // Set if register is used as shuffle mask
std::bitset<s_reg_max> reg_maybe_shuffle_mask{}; bit_set<s_reg_max> reg_maybe_shuffle_mask{};
// Number of times registers are used (before modified) // Number of times registers are used (before modified)
std::array<u32, s_reg_max> reg_use{}; std::array<u32, s_reg_max> reg_use{};
// Bit mask of the trivial (u32 x 4) constant value resulting in this block // Bit mask of the trivial (u32 x 4) constant value resulting in this block
std::bitset<s_reg_max> reg_const{}; bit_set<s_reg_max> reg_const{};
// Bit mask of register saved onto the stack before use // Bit mask of register saved onto the stack before use
std::bitset<s_reg_max> reg_save_dom{}; bit_set<s_reg_max> reg_save_dom{};
// Address of the function // Address of the function
u32 func = 0x40000; u32 func = 0x40000;
@@ -850,7 +838,7 @@ protected:
private: private:
// For private use // For private use
std::bitset<0x10000> m_bits; bit_set<0x10000> m_bits;
// For private use // For private use
std::vector<u32> workload; std::vector<u32> workload;
+7 -8
View File
@@ -9,7 +9,6 @@
#include "sys_rsxaudio.h" #include "sys_rsxaudio.h"
#include <cmath> #include <cmath>
#include <bitset>
#include <optional> #include <optional>
#ifdef __linux__ #ifdef __linux__
@@ -1651,13 +1650,13 @@ void rsxaudio_backend_thread::set_mute_state(avport_bit muted_avports)
u8 rsxaudio_backend_thread::gen_mute_state(avport_bit avports) u8 rsxaudio_backend_thread::gen_mute_state(avport_bit avports)
{ {
std::bitset<SYS_RSXAUDIO_AVPORT_CNT> mute_state{0}; bit_set<SYS_RSXAUDIO_AVPORT_CNT> mute_state{0};
if (avports.hdmi_0) mute_state[static_cast<u8>(RsxaudioAvportIdx::HDMI_0)] = true; if (avports.hdmi_0) mute_state.set(static_cast<u8>(RsxaudioAvportIdx::HDMI_0), true);
if (avports.hdmi_1) mute_state[static_cast<u8>(RsxaudioAvportIdx::HDMI_1)] = true; if (avports.hdmi_1) mute_state.set(static_cast<u8>(RsxaudioAvportIdx::HDMI_1), true);
if (avports.avmulti) mute_state[static_cast<u8>(RsxaudioAvportIdx::AVMULTI)] = true; if (avports.avmulti) mute_state.set(static_cast<u8>(RsxaudioAvportIdx::AVMULTI), true);
if (avports.spdif_0) mute_state[static_cast<u8>(RsxaudioAvportIdx::SPDIF_0)] = true; if (avports.spdif_0) mute_state.set(static_cast<u8>(RsxaudioAvportIdx::SPDIF_0), true);
if (avports.spdif_1) mute_state[static_cast<u8>(RsxaudioAvportIdx::SPDIF_1)] = true; if (avports.spdif_1) mute_state.set(static_cast<u8>(RsxaudioAvportIdx::SPDIF_1), true);
return static_cast<u8>(mute_state.to_ulong()); return static_cast<u8>(mute_state.to_ulong());
} }
@@ -1832,7 +1831,7 @@ u32 rsxaudio_backend_thread::write_data_callback(u32 bytes, void* buf)
return val; return val;
}); });
const std::bitset<SYS_RSXAUDIO_AVPORT_CNT> mute_state{cb_cfg.mute_state}; const bit_set<SYS_RSXAUDIO_AVPORT_CNT> mute_state{cb_cfg.mute_state};
if (cb_cfg.ready && !mute_state[static_cast<u8>(cb_cfg.avport_idx)] && Emu.IsRunning()) if (cb_cfg.ready && !mute_state[static_cast<u8>(cb_cfg.avport_idx)] && Emu.IsRunning())
{ {
+3 -4
View File
@@ -2,12 +2,11 @@
#include "util/atomic.hpp" #include "util/atomic.hpp"
#include <util/types.hpp> #include <util/types.hpp>
#include <bitset>
namespace rsx namespace rsx
{ {
template <int N> template <int N>
void unpack_bitset(const std::bitset<N>& block, u64* values) void unpack_bitset(const bit_set<N>& block, u64* values)
{ {
for (int bit = 0, n = -1, shift = 0; bit < N; ++bit, ++shift) for (int bit = 0, n = -1, shift = 0; bit < N; ++bit, ++shift)
{ {
@@ -25,11 +24,11 @@ namespace rsx
} }
template <int N> template <int N>
void pack_bitset(std::bitset<N>& block, u64* values) void pack_bitset(bit_set<N>& block, u64* values)
{ {
for (int n = 0, shift = 0; shift < N; ++n, shift += 64) for (int n = 0, shift = 0; shift < N; ++n, shift += 64)
{ {
std::bitset<N> tmp = values[n]; bit_set<N> tmp = values[n];
tmp <<= shift; tmp <<= shift;
block |= tmp; block |= tmp;
} }
+3 -3
View File
@@ -140,7 +140,7 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert
//u32 last_instruction_address = 0; //u32 last_instruction_address = 0;
//u32 first_instruction_address = entry; //u32 first_instruction_address = entry;
std::bitset<rsx::max_vertex_program_instructions> instructions_to_patch; bit_set<rsx::max_vertex_program_instructions> instructions_to_patch;
std::pair<u32, u32> instruction_range{ umax, 0 }; std::pair<u32, u32> instruction_range{ umax, 0 };
bool has_branch_instruction = false; bool has_branch_instruction = false;
std::stack<u32> call_stack; std::stack<u32> call_stack;
@@ -178,7 +178,7 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert
d3.HEX = instruction._u32[3]; d3.HEX = instruction._u32[3];
// Touch current instruction // Touch current instruction
result.instruction_mask[current_instruction] = true; result.instruction_mask.set(current_instruction, true);
instruction_range.first = std::min(current_instruction, instruction_range.first); instruction_range.first = std::min(current_instruction, instruction_range.first);
instruction_range.second = std::max(current_instruction, instruction_range.second); instruction_range.second = std::max(current_instruction, instruction_range.second);
@@ -258,7 +258,7 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert
case RSX_SCA_OPCODE_CLB: case RSX_SCA_OPCODE_CLB:
{ {
// Need to patch the jump address to be consistent wherever the program is located // Need to patch the jump address to be consistent wherever the program is located
instructions_to_patch[current_instruction] = true; instructions_to_patch.set(current_instruction, true);
has_branch_instruction = true; has_branch_instruction = true;
d0.HEX = instruction._u32[0]; d0.HEX = instruction._u32[0];
+1 -1
View File
@@ -24,7 +24,7 @@ namespace program_hash_util
{ {
struct vertex_program_metadata struct vertex_program_metadata
{ {
std::bitset<rsx::max_vertex_program_instructions> instruction_mask; bit_set<rsx::max_vertex_program_instructions> instruction_mask;
u32 ucode_length; u32 ucode_length;
u32 referenced_textures_mask; u32 referenced_textures_mask;
u16 referenced_inputs_mask; u16 referenced_inputs_mask;
+1 -2
View File
@@ -3,7 +3,6 @@
#include "program_util.h" #include "program_util.h"
#include <vector> #include <vector>
#include <bitset>
#include <set> #include <set>
enum vp_reg_type enum vp_reg_type
@@ -227,7 +226,7 @@ struct RSXVertexProgram
u32 output_mask = 0; u32 output_mask = 0;
u32 base_address = 0; u32 base_address = 0;
u32 entry = 0; u32 entry = 0;
std::bitset<rsx::max_vertex_program_instructions> instruction_mask; bit_set<rsx::max_vertex_program_instructions> instruction_mask;
std::set<u32> jump_table; std::set<u32> jump_table;
rsx::texture_dimension_extended get_texture_dimension(u8 id) const rsx::texture_dimension_extended get_texture_dimension(u8 id) const
+1 -2
View File
@@ -12,7 +12,6 @@
#include "util/asm.hpp" #include "util/asm.hpp"
#include <thread> #include <thread>
#include <bitset>
using spu_rdata_t = std::byte[128]; using spu_rdata_t = std::byte[128];
@@ -689,7 +688,7 @@ namespace rsx
} }
// Check for flow control // Check for flow control
if (std::bitset<2> jump_type; jump_type if (bit_set<2> jump_type; jump_type
.set(0, (cmd & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD) .set(0, (cmd & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD)
.set(1, (cmd & RSX_METHOD_NEW_JUMP_CMD_MASK) == RSX_METHOD_NEW_JUMP_CMD) .set(1, (cmd & RSX_METHOD_NEW_JUMP_CMD_MASK) == RSX_METHOD_NEW_JUMP_CMD)
.any()) .any())
+1 -1
View File
@@ -40,7 +40,7 @@ log_viewer::log_viewer(std::shared_ptr<gui_settings> gui_settings)
m_path_last = m_gui_settings->GetValue(gui::fd_log_viewer).toString(); m_path_last = m_gui_settings->GetValue(gui::fd_log_viewer).toString();
m_show_timestamps = m_gui_settings->GetValue(gui::lv_show_timestamps).toBool(); m_show_timestamps = m_gui_settings->GetValue(gui::lv_show_timestamps).toBool();
m_show_threads = m_gui_settings->GetValue(gui::lv_show_threads).toBool(); m_show_threads = m_gui_settings->GetValue(gui::lv_show_threads).toBool();
m_log_levels = std::bitset<32>(m_gui_settings->GetValue(gui::lv_log_levels).toUInt()); m_log_levels = bit_set<32>(m_gui_settings->GetValue(gui::lv_log_levels).toUInt());
m_log_text = new QPlainTextEdit(this); m_log_text = new QPlainTextEdit(this);
m_log_text->setReadOnly(true); m_log_text->setReadOnly(true);
+2 -2
View File
@@ -1,11 +1,11 @@
#pragma once #pragma once
#include "find_dialog.h" #include "find_dialog.h"
#include "util/types.hpp"
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QDropEvent> #include <QDropEvent>
#include <bitset>
#include <memory> #include <memory>
class LogHighlighter; class LogHighlighter;
@@ -37,7 +37,7 @@ private:
QPlainTextEdit* m_log_text; QPlainTextEdit* m_log_text;
LogHighlighter* m_log_highlighter; LogHighlighter* m_log_highlighter;
std::unique_ptr<find_dialog> m_find_dialog; std::unique_ptr<find_dialog> m_find_dialog;
std::bitset<32> m_log_levels = std::bitset<32>(0b11111111u); bit_set<32> m_log_levels = bit_set<32>(0b11111111u);
bool m_show_timestamps = true; bool m_show_timestamps = true;
bool m_show_threads = true; bool m_show_threads = true;
bool m_last_actions_only = false; bool m_last_actions_only = false;
+139
View File
@@ -11,6 +11,7 @@
#include <compare> #include <compare>
#include <memory> #include <memory>
#include <bit> #include <bit>
#include <bitset>
#include <string> #include <string>
#include <source_location> #include <source_location>
#include <new> #include <new>
@@ -1064,6 +1065,144 @@ template <typename CT, typename T> requires requires (CT&& x, T&& y) { x.count(y
return found->second; return found->second;
} }
// Exception friendly std::bitset
template <usz Bits>
struct bit_set
{
public:
constexpr bit_set() noexcept : m_bitset() {}
constexpr bit_set(usz val) noexcept : m_bitset(val) {}
[[nodiscard]] bool test(usz pos, std::source_location src_loc = std::source_location::current()) const
{
if (pos >= Bits) [[unlikely]]
fmt::raw_range_error(src_loc, format_object_simplified(pos), Bits);
return m_bitset[pos];
}
bit_set& set(usz pos, bool val = true, std::source_location src_loc = std::source_location::current())
{
if (pos >= Bits) [[unlikely]]
fmt::raw_range_error(src_loc, format_object_simplified(pos), Bits);
m_bitset[pos] = val;
return *this;
}
bit_set& reset(usz pos, std::source_location src_loc = std::source_location::current())
{
if (pos >= Bits) [[unlikely]]
fmt::raw_range_error(src_loc, format_object_simplified(pos), Bits);
m_bitset[pos] = false;
return *this;
}
constexpr bit_set& reset() noexcept
{
m_bitset.reset();
return *this;
}
[[nodiscard]] constexpr unsigned long to_ulong() const noexcept requires(Bits <= 32)
{
return m_bitset.to_ulong();
}
[[nodiscard]] constexpr unsigned long long to_ullong() const noexcept requires(Bits <= 64)
{
return m_bitset.to_ullong();
}
[[nodiscard]] constexpr bool any() const noexcept
{
return m_bitset.any();
}
[[nodiscard]] constexpr bool none() const noexcept
{
return m_bitset.none();
}
[[nodiscard]] constexpr bool all() const noexcept
{
return m_bitset.all();
}
[[nodiscard]] constexpr usz count() const noexcept
{
return m_bitset.count();
}
[[nodiscard]] constexpr usz size() const noexcept
{
return Bits;
}
// Helps us getting the source location when using the [] operator
struct location_index
{
template <typename T> requires std::convertible_to<T, usz>
location_index(T&& pos, std::source_location src_loc = std::source_location::current())
: index(static_cast<usz>(std::forward<T>(pos))), loc(src_loc)
{}
usz index;
std::source_location loc;
};
[[nodiscard]] constexpr bool operator[](location_index index) const
{
return test(index.index, index.loc);
}
constexpr bit_set& operator&=(const bit_set& r) noexcept
{
m_bitset &= r.m_bitset;
return *this;
}
constexpr bit_set& operator|=(const bit_set& r) noexcept
{
m_bitset |= r.m_bitset;
return *this;
}
constexpr bit_set& operator^=(const bit_set& r) noexcept
{
m_bitset ^= r.m_bitset;
return *this;
}
constexpr bit_set& operator<<=(usz pos) noexcept
{
m_bitset <<= pos;
return *this;
}
constexpr bit_set& operator>>=(usz pos) noexcept
{
m_bitset >>= pos;
return *this;
}
[[nodiscard]] constexpr bit_set operator<<(const usz pos) const noexcept
{
return m_bitset << pos;
}
[[nodiscard]] constexpr bit_set operator>>(const usz pos) const noexcept
{
return m_bitset >> pos;
}
private:
constexpr bit_set(std::bitset<Bits>&& set) noexcept : m_bitset(set) {}
std::bitset<Bits> m_bitset;
};
// Simplified hash algorithm. May be used in std::unordered_(map|set). // Simplified hash algorithm. May be used in std::unordered_(map|set).
template <typename T, usz Shift = 0> template <typename T, usz Shift = 0>
struct value_hash struct value_hash