mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
shader: prove descriptor bounds from simplified image EXEC predicates
This commit is contained in:
@@ -248,6 +248,40 @@ bool FoldCompositeExtract(Inst& inst, ValueOpcode construct, size_t components)
|
||||
return false;
|
||||
}
|
||||
|
||||
void FoldZeroComparison(Block& block, Block::iterator instruction, FoldWorklist& worklist) {
|
||||
auto& inst = *instruction;
|
||||
Value value;
|
||||
for (uint32_t arg = 0; arg < 2u; ++arg)
|
||||
if (Arg(inst, arg) == Value(0u)) value = Arg(inst, arg ^ 1u);
|
||||
const auto* source = value.TryInstruction();
|
||||
if (source == nullptr) return;
|
||||
const bool equal = inst.GetOpcode() == ValueOpcode::IEqual32;
|
||||
const auto boolean_select = [](const Inst* select) {
|
||||
return select != nullptr && select->GetOpcode() == ValueOpcode::SelectU32 &&
|
||||
IsImmediate(Arg(*select, 1), Type::U32) &&
|
||||
IsImmediate(Arg(*select, 2), Type::U32);
|
||||
};
|
||||
if (boolean_select(source)) {
|
||||
const bool when_true = (Arg(*source, 1).U32() == 0u) == equal;
|
||||
const bool when_false = (Arg(*source, 2).U32() == 0u) == equal;
|
||||
if (when_true == when_false) Replace(inst, Value(when_true));
|
||||
else if (when_true) Replace(inst, Arg(*source, 0));
|
||||
else Replace(inst, Value(&*worklist.Prepend(block, instruction, ValueOpcode::LogicalNot,
|
||||
{Arg(*source, 0)})));
|
||||
} else if (source->GetOpcode() == ValueOpcode::BitwiseOr32 &&
|
||||
(boolean_select(Arg(*source, 0).TryInstruction()) ||
|
||||
boolean_select(Arg(*source, 1).TryInstruction()))) {
|
||||
// SCC encoded as a DWORD needs no select or bitwise operation when only
|
||||
// its zero predicate is consumed. Keep arbitrary DWORDs as zero comparisons.
|
||||
const auto lhs = worklist.Prepend(block, instruction, inst.GetOpcode(),
|
||||
{Arg(*source, 0), Value(0u)});
|
||||
const auto rhs = worklist.Prepend(block, instruction, inst.GetOpcode(),
|
||||
{Arg(*source, 1), Value(0u)});
|
||||
Replace(inst, Value(&*worklist.Prepend(block, instruction,
|
||||
equal ? ValueOpcode::LogicalAnd : ValueOpcode::LogicalOr, {Value(&*lhs), Value(&*rhs)})));
|
||||
}
|
||||
}
|
||||
|
||||
void FoldInstruction(Block& block, Block::iterator instruction,
|
||||
FoldWorklist& worklist) {
|
||||
auto& inst = *instruction;
|
||||
@@ -630,10 +664,12 @@ void FoldInstruction(Block& block, Block::iterator instruction,
|
||||
FoldU32(inst, [](uint32_t a, uint32_t b) { return std::max(a, b); });
|
||||
return;
|
||||
case ValueOpcode::IEqual32:
|
||||
FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a == b; });
|
||||
if (!FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a == b; }))
|
||||
FoldZeroComparison(block, instruction, worklist);
|
||||
return;
|
||||
case ValueOpcode::INotEqual32:
|
||||
FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a != b; });
|
||||
if (!FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a != b; }))
|
||||
FoldZeroComparison(block, instruction, worklist);
|
||||
return;
|
||||
case ValueOpcode::ULessThan32:
|
||||
FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a < b; });
|
||||
@@ -695,19 +731,43 @@ void FoldInstruction(Block& block, Block::iterator instruction,
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
const auto simplify = [&](Value assumption, Value expression) {
|
||||
const auto* disjunction = expression.TryInstruction();
|
||||
if (disjunction == nullptr || disjunction->GetOpcode() != ValueOpcode::LogicalOr) {
|
||||
return false;
|
||||
const auto* test = expression.TryInstruction();
|
||||
if (test == nullptr) return false;
|
||||
const auto replace = [&](Value value) {
|
||||
inst.SetArg(assumption == lhs ? 1u : 0u, value);
|
||||
return true;
|
||||
};
|
||||
const auto negate = [&](Value value) {
|
||||
return Value(&*worklist.Prepend(block, instruction, ValueOpcode::LogicalNot, {value}));
|
||||
};
|
||||
const bool inverse = test->GetOpcode() == ValueOpcode::LogicalNot;
|
||||
if (inverse) {
|
||||
test = Arg(*test, 0).TryInstruction();
|
||||
if (test == nullptr) return false;
|
||||
if (test->GetOpcode() == ValueOpcode::LogicalAnd)
|
||||
for (uint32_t arg = 0; arg < 2u; ++arg)
|
||||
if (Arg(*test, arg) == assumption)
|
||||
return replace(negate(Arg(*test, arg ^ 1u)));
|
||||
}
|
||||
for (uint32_t i = 0; i < 2u; ++i) {
|
||||
const auto* inverse = disjunction->Arg(i).Resolve().TryInstruction();
|
||||
if (inverse != nullptr && inverse->GetOpcode() == ValueOpcode::LogicalNot &&
|
||||
inverse->Arg(0).Resolve() == assumption) {
|
||||
inst.SetArg(assumption == lhs ? 1u : 0u,
|
||||
disjunction->Arg(i ^ 1u));
|
||||
return true;
|
||||
if (test->GetOpcode() == ValueOpcode::IEqual32 ||
|
||||
test->GetOpcode() == ValueOpcode::INotEqual32) {
|
||||
for (uint32_t arg = 0; arg < 2u; ++arg) {
|
||||
if (Arg(*test, arg) != Value(0u)) continue;
|
||||
const auto original = Arg(*test, arg ^ 1u);
|
||||
const auto active = ResolveActiveU32(original, assumption);
|
||||
if (!active.IsEmpty() && active != original) {
|
||||
const auto comparison = Value(&*worklist.Prepend(block, instruction,
|
||||
test->GetOpcode(), {active, Value(0u)}));
|
||||
return replace(inverse ? negate(comparison) : comparison);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inverse || test->GetOpcode() != ValueOpcode::LogicalOr) return false;
|
||||
for (uint32_t arg = 0; arg < 2u; ++arg) {
|
||||
const auto* negation = Arg(*test, arg).TryInstruction();
|
||||
if (negation != nullptr && negation->GetOpcode() == ValueOpcode::LogicalNot &&
|
||||
Arg(*negation, 0) == assumption) return replace(Arg(*test, arg ^ 1u));
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (simplify(lhs, rhs) || simplify(rhs, lhs)) return;
|
||||
|
||||
@@ -1656,9 +1656,10 @@ private:
|
||||
const auto op = test->GetOpcode();
|
||||
if (op == ValueOpcode::LogicalNot)
|
||||
return UnsignedBound(test->Arg(0), value, !positive);
|
||||
if (op == ValueOpcode::LogicalAnd && positive) {
|
||||
const auto left = UnsignedBound(test->Arg(0), value);
|
||||
return left.IsEmpty() ? UnsignedBound(test->Arg(1), value) : left;
|
||||
if ((op == ValueOpcode::LogicalAnd && positive) ||
|
||||
(op == ValueOpcode::LogicalOr && !positive)) {
|
||||
const auto left = UnsignedBound(test->Arg(0), value, positive);
|
||||
return left.IsEmpty() ? UnsignedBound(test->Arg(1), value, positive) : left;
|
||||
}
|
||||
if ((positive ? op == ValueOpcode::ULessThan32 : op == ValueOpcode::UGreaterThanEqual32) &&
|
||||
EquivalentValue(m_program, test->Arg(0), value)) return test->Arg(1);
|
||||
@@ -1684,6 +1685,24 @@ private:
|
||||
return guarded ? bound : Value {};
|
||||
}
|
||||
|
||||
Value UnsignedImageBound(Value key, const Inst& handle) const {
|
||||
Value bound;
|
||||
for (const auto& use: handle.Uses()) {
|
||||
const auto& image = *use.user;
|
||||
const auto op = image.GetOpcode();
|
||||
if (use.operand != 0u || (op != ValueOpcode::ImageSampleRaw &&
|
||||
op != ValueOpcode::ImageGatherRaw && op != ValueOpcode::ImageRead)) return {};
|
||||
// The image's own EXEC predicate proves the range for every accessing lane;
|
||||
// an EXEC-nonzero branch alone only proves that some lane is active.
|
||||
const auto candidate = UnsignedBound(image.Arg(image.NumArgs() - 1u), key);
|
||||
if (candidate.IsEmpty() ||
|
||||
!ValidateRuntimeValue(m_program, candidate, RuntimeValueType::Integer)) return {};
|
||||
if (bound.IsEmpty()) bound = candidate;
|
||||
else if (!EquivalentValue(m_program, candidate, bound)) return {};
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
Value SharedLoadBound(Value value, Value active) {
|
||||
const auto* read = ActiveValue(value, active).TryInstruction();
|
||||
if (read == nullptr || read->GetOpcode() != ValueOpcode::LoadSharedU32 ||
|
||||
@@ -2417,7 +2436,8 @@ private:
|
||||
// the GPU key using the same plan as other indirect image reads.
|
||||
plan.retain_reads |= table_source.dword_count == 4u;
|
||||
} else if (table_source.dword_count == 2u) {
|
||||
const auto guarded_count = UnsignedBoundOnEntry(key, handle.Parent());
|
||||
auto guarded_count = UnsignedBoundOnEntry(key, handle.Parent());
|
||||
if (guarded_count.IsEmpty()) guarded_count = UnsignedImageBound(key, handle);
|
||||
const auto* selector = key.Resolve().TryInstruction();
|
||||
const bool bitscan = guarded_count.IsEmpty() && selector != nullptr &&
|
||||
selector->GetOpcode() == ValueOpcode::FindILsb32 &&
|
||||
|
||||
Reference in New Issue
Block a user