mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-26 21:06:53 +01:00
shader: fold complementary masks so wave reductions see full EXEC
Co-authored-by: Senaxx <michelbeld@gmail.com> Co-authored-by: Mac <me@itsmemac.com>
This commit is contained in:
@@ -167,6 +167,16 @@ bool ReplaceBinaryIdentity(Inst& inst, Type type, uint64_t identity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Whether one operand is the other's negation.
|
||||
bool IsComplementPair(const Inst& inst, ValueOpcode negation) {
|
||||
for (uint32_t arg = 0; arg < 2u; ++arg) {
|
||||
const auto* negate = Arg(inst, arg).TryInstruction();
|
||||
if (negate != nullptr && negate->GetOpcode() == negation &&
|
||||
Arg(*negate, 0) == Arg(inst, arg ^ 1u)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FoldSelect(Inst& inst) {
|
||||
const auto condition = Arg(inst, 0);
|
||||
const auto true_value = Arg(inst, 1);
|
||||
@@ -626,8 +636,9 @@ void FoldInstruction(Block& block, Block::iterator instruction,
|
||||
case ValueOpcode::BitwiseXor32:
|
||||
if (!FoldU32(inst, [opcode = inst.GetOpcode()](uint32_t a, uint32_t b) {
|
||||
return opcode == ValueOpcode::BitwiseOr32 ? a | b : a ^ b;
|
||||
})) {
|
||||
ReplaceBinaryIdentity(inst, Type::U32, 0u);
|
||||
}) && !ReplaceBinaryIdentity(inst, Type::U32, 0u) &&
|
||||
IsComplementPair(inst, ValueOpcode::BitwiseNot32)) {
|
||||
Replace(inst, Value(UINT32_MAX));
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::BitwiseNot32: {
|
||||
@@ -802,6 +813,8 @@ void FoldInstruction(Block& block, Block::iterator instruction,
|
||||
Replace(inst, lhs.U1() ? lhs : rhs);
|
||||
} else if (IsImmediate(rhs, Type::U1)) {
|
||||
Replace(inst, rhs.U1() ? rhs : lhs);
|
||||
} else if (IsComplementPair(inst, ValueOpcode::LogicalNot)) {
|
||||
Replace(inst, Value(true));
|
||||
} else {
|
||||
const auto* left = lhs.TryInstruction();
|
||||
const auto* right = rhs.TryInstruction();
|
||||
|
||||
@@ -104,11 +104,10 @@ Value GetRealValue(PhiMap& phi_map, Value source, uint32_t lane, uint32_t wave_s
|
||||
// Fragment subgroups may have no physical invocation at either endpoint.
|
||||
class WaveReductionMatcher {
|
||||
public:
|
||||
WaveReductionMatcher(Block* block, ValueOpcode operation)
|
||||
: block(block), operation(operation),
|
||||
identity(operation == ValueOpcode::UMin32 ? UINT32_MAX : 0u) {}
|
||||
WaveReductionMatcher(const Block* block, ValueOpcode operation)
|
||||
: block(block), operation(operation) {}
|
||||
|
||||
Value Match(const Inst& reduction, uint32_t wave_size) {
|
||||
Value Match(const Inst& reduction, uint32_t wave_size) const {
|
||||
const auto* low = Get(reduction.Arg(0), ValueOpcode::ReadLane);
|
||||
const auto* high = Get(reduction.Arg(1), ValueOpcode::ReadLane);
|
||||
if (low == nullptr || high == nullptr || low->Arg(0).Resolve() != high->Arg(0).Resolve()) {
|
||||
@@ -124,45 +123,32 @@ public:
|
||||
(lane(low->Arg(1), wave_size - 1u) && lane(high->Arg(1), half - 1u)))) {
|
||||
return {};
|
||||
}
|
||||
if (wave_size == 32u) return MatchScan(Unwrap(low->Arg(0)));
|
||||
const auto* joined = Get(Unwrap(low->Arg(0)), operation);
|
||||
if (wave_size == 32u) return MatchScan(low->Arg(0));
|
||||
const auto* joined = Get(low->Arg(0), operation);
|
||||
if (joined == nullptr) return {};
|
||||
for (size_t index = 0; index < 2; index++) {
|
||||
auto attempt = *this;
|
||||
const auto* cross = attempt.Get(attempt.Unwrap(joined->Arg(index)), ValueOpcode::Permlane16U32);
|
||||
const auto* cross = Get(joined->Arg(index), ValueOpcode::Permlane16U32);
|
||||
if (cross == nullptr) continue;
|
||||
const auto flags = cross->Flags<PermlaneFlags>();
|
||||
auto prefix = attempt.Unwrap(joined->Arg(1 - index));
|
||||
const auto flags = cross->Flags<PermlaneFlags>();
|
||||
const auto prefix = joined->Arg(1 - index).Resolve();
|
||||
if (!flags.x16 || flags.fetch_inactive || !flags.bound_control ||
|
||||
cross->Arg(1).Resolve() != Value(UINT32_MAX) ||
|
||||
cross->Arg(2).Resolve() != Value(UINT32_MAX) ||
|
||||
!attempt.FullExec(cross->Arg(3)) || attempt.Unwrap(cross->Arg(0)) != prefix) {
|
||||
cross->Arg(2).Resolve() != Value(UINT32_MAX) || !FullExec(cross->Arg(3)) ||
|
||||
cross->Arg(0).Resolve() != prefix) {
|
||||
continue;
|
||||
}
|
||||
if (const auto seed = attempt.MatchScan(prefix); !seed.IsEmpty()) return seed;
|
||||
if (const auto seed = MatchScan(prefix); !seed.IsEmpty()) return seed;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
// The row scan's input, whose lanes outside the saved EXEC hold the operation's identity.
|
||||
Value MatchScan(Value prefix) {
|
||||
// The row scan's input.
|
||||
Value MatchScan(Value prefix) const {
|
||||
for (const uint32_t shift: {8u, 4u, 2u, 1u}) {
|
||||
prefix = MatchRow(prefix, shift);
|
||||
}
|
||||
prefix = Unwrap(prefix);
|
||||
if (prefix.IsEmpty() || saved_exec.IsEmpty()) return prefix;
|
||||
const auto* seed = Get(prefix, ValueOpcode::SelectU32);
|
||||
if (seed != nullptr && seed->Arg(2).Resolve() == Value(identity)) {
|
||||
const auto predicate = seed->Arg(0).Resolve();
|
||||
const auto* subset = Get(predicate, ValueOpcode::LogicalAnd);
|
||||
if (predicate == saved_exec ||
|
||||
(subset != nullptr && (subset->Arg(0).Resolve() == saved_exec ||
|
||||
subset->Arg(1).Resolve() == saved_exec))) {
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
return prefix;
|
||||
}
|
||||
|
||||
Inst* Get(Value value, ValueOpcode opcode) const {
|
||||
@@ -171,28 +157,8 @@ private:
|
||||
? inst : nullptr;
|
||||
}
|
||||
|
||||
bool FullExec(Value predicate) {
|
||||
predicate = predicate.Resolve();
|
||||
if (predicate == Value(true)) return true;
|
||||
const auto* either = Get(predicate, ValueOpcode::LogicalOr);
|
||||
if (either == nullptr) return false;
|
||||
for (size_t index = 0; index < 2; index++) {
|
||||
const auto* negate = Get(either->Arg(index), ValueOpcode::LogicalNot);
|
||||
if (negate == nullptr || negate->Arg(0).Resolve() != either->Arg(1 - index).Resolve()) continue;
|
||||
const auto original = negate->Arg(0).Resolve();
|
||||
if (saved_exec.IsEmpty()) saved_exec = original;
|
||||
return saved_exec == original;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Value Unwrap(Value value) {
|
||||
while (const auto* select = Get(value, ValueOpcode::SelectU32)) {
|
||||
if (!FullExec(select->Arg(0))) break;
|
||||
value = select->Arg(1);
|
||||
}
|
||||
return value.Resolve();
|
||||
}
|
||||
// Constant propagation folds the x | ~x EXEC of s_orn2_saveexec to true.
|
||||
static bool FullExec(Value predicate) { return predicate.Resolve() == Value(true); }
|
||||
|
||||
static bool IsRowShift(const Inst& inst, uint32_t shift) {
|
||||
const auto flags = inst.Flags<DppMoveFlags>();
|
||||
@@ -200,24 +166,24 @@ private:
|
||||
flags.bank_mask == 0xf && !flags.fetch_inactive && !flags.bound_control && !flags.dpp8;
|
||||
}
|
||||
|
||||
Value MatchRow(Value value, uint32_t shift) {
|
||||
const auto* update = Get(Unwrap(value), ValueOpcode::DppUpdateU32);
|
||||
Value MatchRow(Value value, uint32_t shift) const {
|
||||
const auto* update = Get(value, ValueOpcode::DppUpdateU32);
|
||||
if (update == nullptr || !IsRowShift(*update, shift) || !FullExec(update->Arg(2))) return {};
|
||||
const auto* combined = Get(update->Arg(0), operation);
|
||||
if (combined == nullptr) return {};
|
||||
for (size_t index = 0; index < 2; index++) {
|
||||
const auto* move = Get(combined->Arg(index), ValueOpcode::DppMoveU32);
|
||||
if (move == nullptr || !IsRowShift(*move, shift) || !FullExec(move->Arg(1))) continue;
|
||||
const auto previous = Unwrap(combined->Arg(1 - index));
|
||||
if (Unwrap(move->Arg(0)) == previous && Unwrap(update->Arg(1)) == previous) return previous;
|
||||
const auto previous = combined->Arg(1 - index).Resolve();
|
||||
if (move->Arg(0).Resolve() == previous && update->Arg(1).Resolve() == previous) {
|
||||
return previous;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Block* block;
|
||||
ValueOpcode operation;
|
||||
uint32_t identity;
|
||||
Value saved_exec;
|
||||
const Block* block;
|
||||
ValueOpcode operation;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user