diff --git a/core/libs/prx/libSceAgcDriver/tests/DenormalFlushElimination.cpp b/core/libs/prx/libSceAgcDriver/tests/DenormalFlushElimination.cpp index 47020acb0..3db6a8ce0 100644 --- a/core/libs/prx/libSceAgcDriver/tests/DenormalFlushElimination.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/DenormalFlushElimination.cpp @@ -150,6 +150,66 @@ int main() { return b.eliminate() == 1u && !clamped.HasUses() && product.HasUses(); }); + passed &= run("a min, max, min3, max3 or med3 of flushed values needs no flush, one with an unknown operand does", [] { + Builder b; + const auto value = [&](IrValue& bits) -> IrValue& { return b.emit(IrOpcode::BitCastF32U32, IrType::F32, {&bits}); }; + const auto flushedResult = [&](IrOpcode opcode, std::initializer_list arguments) -> IrValue& { + return b.flush(b.emit(IrOpcode::BitCastU32F32, IrType::U32, {&b.emit(opcode, IrType::F32, arguments)})); + }; + auto& x = value(b.flush(b.unknown())); + auto& y = value(b.flush(b.unknown())); + auto& z = value(b.flush(b.unknown())); + auto& raw = value(b.unknown()); + IrValue* removed[] = { + &flushedResult(IrOpcode::FPMin32, {&x, &y}), + &flushedResult(IrOpcode::FPMax32, {&y, &x}), + &flushedResult(IrOpcode::FPMinTri32, {&x, &y, &z}), + &flushedResult(IrOpcode::FPMaxTri32, {&z, &y, &x}), + &flushedResult(IrOpcode::FPMedTri32, {&x, &z, &y}), + }; + auto& mixedPair = flushedResult(IrOpcode::FPMax32, {&x, &raw}); + auto& mixedMedian = flushedResult(IrOpcode::FPMedTri32, {&x, &y, &raw}); + for (auto* flushed : removed) b.keep(*flushed); + b.keep(mixedPair); + b.keep(mixedMedian); + const bool counted = b.eliminate() == 5u; + bool kept = false; + for (auto* flushed : removed) kept |= flushed->HasUses(); + return counted && !kept && mixedPair.HasUses() && mixedMedian.HasUses(); + }); + + passed &= run("a floor or ceil needs no flush, even of an unknown value", [] { + Builder b; + auto& raw = b.emit(IrOpcode::BitCastF32U32, IrType::F32, {&b.unknown()}); + auto& floored = b.flush(b.emit(IrOpcode::BitCastU32F32, IrType::U32, {&b.emit(IrOpcode::FPFloor32, IrType::F32, {&raw})})); + auto& ceiled = b.flush(b.emit(IrOpcode::BitCastU32F32, IrType::U32, {&b.emit(IrOpcode::FPCeil32, IrType::F32, {&raw})})); + b.keep(floored); + b.keep(ceiled); + return b.eliminate() == 2u && !floored.HasUses() && !ceiled.HasUses(); + }); + + passed &= run("a max carried around a loop without a flush keeps its flush and the search ends", [] { + Builder b; + IrBlock& head = *b.block; + IrBlock& body = b.newBlock(); + IrBlock& exit = b.newBlock(); + head.AddBranch(&body); + body.AddBranch(&body); + body.AddBranch(&exit); + auto& initial = b.flush(b.unknown()); + b.block = &body; + auto& carried = b.program.CreateValue(IrOpcode::Phi, IrType::U32); + body.AppendInstruction(&carried); + auto& step = b.emit(IrOpcode::BitCastF32U32, IrType::F32, {&b.flush(b.unknown())}); + auto& maximum = b.emit(IrOpcode::BitCastU32F32, IrType::U32, {&b.emit(IrOpcode::FPMax32, IrType::F32, {&b.emit(IrOpcode::BitCastF32U32, IrType::F32, {&carried}), &step})}); + carried.AddPhiOperand(&head, &initial); + carried.AddPhiOperand(&body, &maximum); + auto& flushed = b.flush(maximum); + b.block = &exit; + b.keep(flushed); + return b.eliminate() == 0u && flushed.HasUses(); + }); + passed &= run("a phi of flushed values needs no flush", [] { Builder b; IrBlock& head = *b.block; diff --git a/core/shader/recompiler/Optimization/src/DenormalFlushEliminator.cpp b/core/shader/recompiler/Optimization/src/DenormalFlushEliminator.cpp index 7de56772e..d448da5e6 100644 --- a/core/shader/recompiler/Optimization/src/DenormalFlushEliminator.cpp +++ b/core/shader/recompiler/Optimization/src/DenormalFlushEliminator.cpp @@ -51,6 +51,8 @@ private: case IrOpcode::ConvertF32S32: case IrOpcode::ConvertF32U32: case IrOpcode::ConvertF32F16: + case IrOpcode::FPFloor32: + case IrOpcode::FPCeil32: return true; case IrOpcode::BitCastU32F32: case IrOpcode::BitCastF32U32: @@ -67,6 +69,11 @@ private: case IrOpcode::SelectU32: case IrOpcode::SelectF32: return Contains(value.Argument(1)) && Contains(value.Argument(2)); + case IrOpcode::FPMin32: + case IrOpcode::FPMax32: + case IrOpcode::FPMinTri32: + case IrOpcode::FPMaxTri32: + case IrOpcode::FPMedTri32: case IrOpcode::Phi: for (std::size_t index = 0; index < value.ArgumentCount(); ++index) { if (!Contains(value.Argument(index))) return false;