diff --git a/src/shader_recompiler/ir/microinstruction.cpp b/src/shader_recompiler/ir/microinstruction.cpp index e663f5eea..b3dc6b25c 100644 --- a/src/shader_recompiler/ir/microinstruction.cpp +++ b/src/shader_recompiler/ir/microinstruction.cpp @@ -147,7 +147,7 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) { } void Inst::Invalidate() { - ASSERT(uses.empty()); + ASSERT(users.list.empty()); ClearArgs(); ReplaceOpcode(Opcode::Void); } @@ -175,13 +175,52 @@ void Inst::ClearArgs() { } } +UseIterator Inst::UserList::UseBegin() { + return UseIterator(list.begin(), list.end()); +} + +UseIterator Inst::UserList::UseEnd() { + return UseIterator(list.end(), list.end()); +} + +boost::iterator_range Inst::UserList::Uses() { + return boost::make_iterator_range(UseBegin(), UseEnd()); +} + +void Inst::UserList::AddUse(IR::Inst* user, u32 operand) { + DEBUG_ASSERT(operand < 31); + auto it = std::find_if(list.begin(), list.end(), + [&](const UserNode& user_node) { return user_node.user == user; }); + u32 operand_pos = 1 << operand; + if (it == list.end()) { + list.emplace_front(user, operand_pos); + } else { + DEBUG_ASSERT((it->operand_mask & operand_pos) == 0); + it->operand_mask |= operand_pos; + } + ++num_uses; +} + +void Inst::UserList::RemoveUse(IR::Inst* user, u32 operand) { + auto it = std::find_if(list.begin(), list.end(), + [&](const UserNode& user_node) { return user_node.user == user; }); + DEBUG_ASSERT(it != list.end()); + u32 operand_pos = 1 << operand; + DEBUG_ASSERT((it->operand_mask & operand_pos) != 0); + it->operand_mask &= ~operand_pos; + if (it->operand_mask == 0) { + list.erase(it); + } + --num_uses; +} + void Inst::ReplaceUsesWith(Value replacement, bool preserve) { // Copy since user->SetArg will mutate this->uses // Could also do temp_uses = std::move(uses) but more readable - boost::container::list temp_uses = uses; - for (auto& [user, operand] : temp_uses) { - DEBUG_ASSERT(user->Arg(operand).Inst() == this); - user->SetArg(operand, replacement); + UserList temp_users = users; + for (IR::Use use : temp_users.Uses()) { + DEBUG_ASSERT(use.user->Arg(use.operand).Inst() == this); + use.user->SetArg(use.operand, replacement); } Invalidate(); if (preserve) { @@ -205,14 +244,23 @@ void Inst::ReplaceOpcode(IR::Opcode opcode) { } void Inst::Use(Inst* used, u32 operand) { - DEBUG_ASSERT(0 == std::count(used->uses.begin(), used->uses.end(), IR::Use(this, operand))); - used->uses.emplace_front(this, operand); + used->users.AddUse(this, operand); } void Inst::UndoUse(Inst* used, u32 operand) { - IR::Use use(this, operand); - DEBUG_ASSERT(1 == std::count(used->uses.begin(), used->uses.end(), use)); - used->uses.remove(use); + used->users.RemoveUse(this, operand); +} + +UseIterator Inst::UseBegin() { + return users.UseBegin(); +} + +UseIterator Inst::UseEnd() { + return users.UseEnd(); +} + +boost::iterator_range Inst::Uses() { + return boost::make_iterator_range(UseBegin(), UseEnd()); } } // namespace Shader::IR diff --git a/src/shader_recompiler/ir/value.h b/src/shader_recompiler/ir/value.h index c239226a9..3ca361873 100644 --- a/src/shader_recompiler/ir/value.h +++ b/src/shader_recompiler/ir/value.h @@ -110,13 +110,10 @@ public: struct Use { Inst* user; u32 operand; - - Use() = default; - Use(Inst* user_, u32 operand_) : user(user_), operand(operand_) {} - Use(const Use&) = default; - bool operator==(const Use&) const noexcept = default; }; +class UseIterator; + class Inst : public boost::intrusive::list_base_hook<> { public: explicit Inst(IR::Opcode op_, u32 flags_) noexcept; @@ -130,12 +127,12 @@ public: /// Get the number of uses this instruction has. [[nodiscard]] int UseCount() const noexcept { - return uses.size(); + return users.num_uses; } /// Determines whether this instruction has uses or not. [[nodiscard]] bool HasUses() const noexcept { - return uses.size() > 0; + return users.num_uses > 0; } /// Get the opcode this microinstruction represents. @@ -213,20 +210,22 @@ public: return std::bit_cast(definition); } - auto users() { + auto Users() { + return boost::adaptors::transform(users.list, + [](UserList::UserNode& user) { return user.user; }); + } + + const auto Users() const { return boost::adaptors::transform( - boost::unique(uses, - [](const IR::Use& a, const IR::Use& b) { return a.user == b.user; }), - [](const IR::Use& use) { return use.user; }); + users.list, + [](const UserList::UserNode& user) -> const IR::Inst* { return user.user; }); } - auto user_begin() { - return users().begin(); - } + UseIterator UseBegin(); + UseIterator UseEnd(); - auto user_end() { - return users().end(); - } + boost::iterator_range Uses(); + const boost::iterator_range Uses() const; private: struct NonTriviallyDummy { @@ -246,9 +245,73 @@ private: std::array args; }; - boost::container::list uses; + struct UserList { + struct UserNode { + IR::Inst* user; + u32 operand_mask; + }; + + boost::container::list list; + using UserIterator = boost::container::list::iterator; + u32 num_uses{}; + + void AddUse(IR::Inst* user, u32 operand); + void RemoveUse(IR::Inst* user, u32 operand); + + UseIterator UseBegin(); + UseIterator UseEnd(); + boost::iterator_range Uses(); + } users; + + friend class UseIterator; }; -static_assert(sizeof(Inst) <= 152, "Inst size unintentionally increased"); +static_assert(sizeof(Inst) <= 160, "Inst size unintentionally increased"); + +class UseIterator + : public boost::iterator_facade { +public: + UseIterator() : user_it(), user_end(), bitmask_pos(0) {}; + + explicit UseIterator(IR::Inst::UserList::UserIterator user_begin_, + IR::Inst::UserList::UserIterator user_end_) + : user_it(user_begin_), user_end(user_end_), bitmask_pos(0) { + if (user_it != user_end) { + bitmask_pos = std::countr_zero(user_it->operand_mask); + DEBUG_ASSERT(user_it->operand_mask != 0); + } + }; + +private: + friend class boost::iterator_core_access; + + void increment() { + // Assumes inst has less than 31 operands + u32 mask = 1 << (bitmask_pos + 1); + u32 use_mask = user_it->operand_mask & ~(mask - 1); + if (use_mask == 0) { + ++user_it; + if (user_it == user_end) { + bitmask_pos = 0; + return; + } + use_mask = user_it->operand_mask; + ASSERT(use_mask != 0); + } + bitmask_pos = std::countr_zero(use_mask); + }; + + bool equal(UseIterator const& other) const { + return user_it == other.user_it && bitmask_pos == other.bitmask_pos; + }; + + IR::Use dereference() const { + return IR::Use(user_it->user, bitmask_pos); + }; + + IR::Inst::UserList::UserIterator user_it; + IR::Inst::UserList::UserIterator user_end; + u32 bitmask_pos; +}; // class UseIterator using U1 = TypedValue; using U8 = TypedValue;