mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
shader_recompiler: add SSA
This commit is contained in:
@@ -0,0 +1,612 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
namespace {
|
||||
|
||||
Value Arg(const Inst& inst, size_t index) {
|
||||
return inst.Arg(index).Resolve();
|
||||
}
|
||||
|
||||
bool IsImmediate(Value value, Type type) {
|
||||
return value.IsImmediate() && value.GetType() == type;
|
||||
}
|
||||
|
||||
void Replace(Inst& inst, Value value) {
|
||||
inst.ReplaceUsesWith(value.Resolve());
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
bool FoldU32(Inst& inst, Function function) {
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (!IsImmediate(lhs, Type::U32) || !IsImmediate(rhs, Type::U32)) {
|
||||
return false;
|
||||
}
|
||||
Replace(inst, Value(static_cast<uint32_t>(function(lhs.U32(), rhs.U32()))));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
bool FoldU64(Inst& inst, Function function) {
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (!IsImmediate(lhs, Type::U64) || !IsImmediate(rhs, Type::U64)) {
|
||||
return false;
|
||||
}
|
||||
Replace(inst, Value(static_cast<uint64_t>(function(lhs.U64(), rhs.U64()))));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
bool FoldU64Shift(Inst& inst, Function function) {
|
||||
const auto value = Arg(inst, 0);
|
||||
const auto shift = Arg(inst, 1);
|
||||
if (!IsImmediate(value, Type::U64) || !IsImmediate(shift, Type::U32)) {
|
||||
return false;
|
||||
}
|
||||
Replace(inst, Value(static_cast<uint64_t>(function(value.U64(), shift.U32()))));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
bool FoldU32Compare(Inst& inst, Function function) {
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (!IsImmediate(lhs, Type::U32) || !IsImmediate(rhs, Type::U32)) {
|
||||
return false;
|
||||
}
|
||||
Replace(inst, Value(function(lhs.U32(), rhs.U32())));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
bool FoldU64Compare(Inst& inst, Function function) {
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (!IsImmediate(lhs, Type::U64) || !IsImmediate(rhs, Type::U64)) {
|
||||
return false;
|
||||
}
|
||||
Replace(inst, Value(function(lhs.U64(), rhs.U64())));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
bool FoldLogical(Inst& inst, Function function) {
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (!IsImmediate(lhs, Type::U1) || !IsImmediate(rhs, Type::U1)) {
|
||||
return false;
|
||||
}
|
||||
Replace(inst, Value(function(lhs.U1(), rhs.U1())));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReplaceBinaryIdentity(Inst& inst, Type type, uint64_t identity) {
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (IsImmediate(lhs, type) &&
|
||||
(type == Type::U32 ? lhs.U32() == identity : lhs.U64() == identity)) {
|
||||
Replace(inst, rhs);
|
||||
return true;
|
||||
}
|
||||
if (IsImmediate(rhs, type) &&
|
||||
(type == Type::U32 ? rhs.U32() == identity : rhs.U64() == identity)) {
|
||||
Replace(inst, lhs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FoldSelect(Inst& inst) {
|
||||
const auto condition = Arg(inst, 0);
|
||||
const auto true_value = Arg(inst, 1);
|
||||
const auto false_value = Arg(inst, 2);
|
||||
if (IsImmediate(condition, Type::U1)) {
|
||||
Replace(inst, condition.U1() ? true_value : false_value);
|
||||
return true;
|
||||
}
|
||||
if (true_value == false_value) {
|
||||
Replace(inst, true_value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FoldPhi(Inst& inst) {
|
||||
Value same;
|
||||
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
||||
const auto value = Arg(inst, index);
|
||||
if (value.TryInstruction() == &inst) {
|
||||
continue;
|
||||
}
|
||||
if (same.IsEmpty()) {
|
||||
same = value;
|
||||
} else if (same != value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (same.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
Replace(inst, same);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FoldBitCast(Inst& inst, ValueOpcode reverse) {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::F32) && inst.GetOpcode() == ValueOpcode::BitCastU32F32) {
|
||||
Replace(inst, Value(std::bit_cast<uint32_t>(value.F32Value())));
|
||||
return true;
|
||||
}
|
||||
if (IsImmediate(value, Type::U32) && inst.GetOpcode() == ValueOpcode::BitCastF32U32) {
|
||||
Replace(inst, Value::F32(std::bit_cast<float>(value.U32())));
|
||||
return true;
|
||||
}
|
||||
if (IsImmediate(value, Type::F16) && inst.GetOpcode() == ValueOpcode::BitCastU16F16) {
|
||||
Replace(inst, Value(value.F16Bits()));
|
||||
return true;
|
||||
}
|
||||
if (IsImmediate(value, Type::U16) && inst.GetOpcode() == ValueOpcode::BitCastF16U16) {
|
||||
Replace(inst, Value::F16(value.U16()));
|
||||
return true;
|
||||
}
|
||||
if (auto* producer = value.TryInstruction();
|
||||
producer != nullptr && producer->GetOpcode() == reverse) {
|
||||
Replace(inst, producer->Arg(0));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FoldCompositeExtract(Inst& inst, ValueOpcode construct, size_t components) {
|
||||
const auto composite = Arg(inst, 0);
|
||||
const auto index = Arg(inst, 1);
|
||||
if (!IsImmediate(index, Type::U32) || index.U32() >= components) {
|
||||
return false;
|
||||
}
|
||||
const auto component = index.U32();
|
||||
const auto* producer = composite.TryInstruction();
|
||||
if (producer != nullptr && producer->GetOpcode() == construct) {
|
||||
Replace(inst, producer->Arg(component));
|
||||
return true;
|
||||
}
|
||||
if (component < 2u && producer != nullptr &&
|
||||
producer->GetOpcode() == ValueOpcode::IAddCarry32) {
|
||||
const auto lhs = producer->Arg(0).Resolve();
|
||||
const auto rhs = producer->Arg(1).Resolve();
|
||||
if (IsImmediate(lhs, Type::U32) && IsImmediate(rhs, Type::U32)) {
|
||||
const auto sum = static_cast<uint64_t>(lhs.U32()) + rhs.U32();
|
||||
Replace(inst, Value(component == 0u ? static_cast<uint32_t>(sum)
|
||||
: static_cast<uint32_t>(sum >> 32u)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (component < 2u && producer != nullptr &&
|
||||
producer->GetOpcode() == ValueOpcode::UnpackUint2x32) {
|
||||
const auto packed = producer->Arg(0).Resolve();
|
||||
if (IsImmediate(packed, Type::U64)) {
|
||||
Replace(inst, Value(static_cast<uint32_t>(packed.U64() >> (component * 32u))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FoldInstruction(Inst& inst) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case ValueOpcode::Phi: FoldPhi(inst); return;
|
||||
case ValueOpcode::SelectU1:
|
||||
case ValueOpcode::SelectU32:
|
||||
case ValueOpcode::SelectF32: FoldSelect(inst); return;
|
||||
case ValueOpcode::BitFieldInsert: {
|
||||
const auto base = Arg(inst, 0);
|
||||
const auto insert = Arg(inst, 1);
|
||||
const auto offset = Arg(inst, 2);
|
||||
const auto count = Arg(inst, 3);
|
||||
if (IsImmediate(base, Type::U32) && IsImmediate(insert, Type::U32) &&
|
||||
IsImmediate(offset, Type::U32) && IsImmediate(count, Type::U32) &&
|
||||
offset.U32() <= 32u && count.U32() <= 32u - offset.U32()) {
|
||||
if (count.U32() == 0u) {
|
||||
Replace(inst, base);
|
||||
return;
|
||||
}
|
||||
const auto mask = count.U32() == 32u
|
||||
? UINT32_MAX
|
||||
: ((uint32_t {1} << count.U32()) - 1u) << offset.U32();
|
||||
Replace(inst,
|
||||
Value((base.U32() & ~mask) | ((insert.U32() << offset.U32()) & mask)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::BitFieldUExtract:
|
||||
case ValueOpcode::BitFieldSExtract: {
|
||||
const auto value = Arg(inst, 0);
|
||||
const auto offset = Arg(inst, 1);
|
||||
const auto count = Arg(inst, 2);
|
||||
if (!IsImmediate(value, Type::U32) || !IsImmediate(offset, Type::U32) ||
|
||||
!IsImmediate(count, Type::U32) || offset.U32() > 32u ||
|
||||
count.U32() > 32u - offset.U32()) {
|
||||
return;
|
||||
}
|
||||
if (count.U32() == 0u) {
|
||||
Replace(inst, Value(0u));
|
||||
} else if (inst.GetOpcode() == ValueOpcode::BitFieldUExtract) {
|
||||
const auto mask =
|
||||
count.U32() == 32u ? UINT32_MAX : (uint32_t {1} << count.U32()) - 1u;
|
||||
Replace(inst, Value((value.U32() >> offset.U32()) & mask));
|
||||
} else {
|
||||
const auto left = 32u - offset.U32() - count.U32();
|
||||
const auto bits = value.U32() << left;
|
||||
Replace(inst, Value(static_cast<uint32_t>(std::bit_cast<int32_t>(bits) >>
|
||||
(left + offset.U32()))));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::BitCastU16F16: FoldBitCast(inst, ValueOpcode::BitCastF16U16); return;
|
||||
case ValueOpcode::BitCastF16U16: FoldBitCast(inst, ValueOpcode::BitCastU16F16); return;
|
||||
case ValueOpcode::BitCastU32F32: FoldBitCast(inst, ValueOpcode::BitCastF32U32); return;
|
||||
case ValueOpcode::BitCastF32U32: FoldBitCast(inst, ValueOpcode::BitCastU32F32); return;
|
||||
case ValueOpcode::ConvertU16U32: {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::U32)) {
|
||||
Replace(inst, Value(static_cast<uint16_t>(value.U32())));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::ConvertU32U16: {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::U16)) {
|
||||
Replace(inst, Value(static_cast<uint32_t>(value.U16())));
|
||||
} else if (auto* producer = value.TryInstruction();
|
||||
producer != nullptr && producer->GetOpcode() == ValueOpcode::ConvertU16U32) {
|
||||
Replace(inst, producer->Arg(0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::ConvertU8U32: {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::U32)) {
|
||||
Replace(inst, Value(static_cast<uint8_t>(value.U32())));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::ConvertU32U8: {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::U8)) {
|
||||
Replace(inst, Value(static_cast<uint32_t>(value.U8())));
|
||||
} else if (auto* producer = value.TryInstruction();
|
||||
producer != nullptr && producer->GetOpcode() == ValueOpcode::ConvertU8U32) {
|
||||
Replace(inst, producer->Arg(0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::CompositeExtractU32x2:
|
||||
FoldCompositeExtract(inst, ValueOpcode::CompositeConstructU32x2, 2);
|
||||
return;
|
||||
case ValueOpcode::CompositeExtractU32x4:
|
||||
FoldCompositeExtract(inst, ValueOpcode::CompositeConstructU32x4, 4);
|
||||
return;
|
||||
case ValueOpcode::PackUint2x32: {
|
||||
const auto value = Arg(inst, 0);
|
||||
const auto* producer = value.TryInstruction();
|
||||
if (producer != nullptr && producer->GetOpcode() == ValueOpcode::UnpackUint2x32) {
|
||||
Replace(inst, producer->Arg(0));
|
||||
} else if (producer != nullptr &&
|
||||
producer->GetOpcode() == ValueOpcode::CompositeConstructU32x2) {
|
||||
const auto low = producer->Arg(0).Resolve();
|
||||
const auto high = producer->Arg(1).Resolve();
|
||||
if (IsImmediate(low, Type::U32) && IsImmediate(high, Type::U32)) {
|
||||
Replace(inst, Value(static_cast<uint64_t>(low.U32()) |
|
||||
(static_cast<uint64_t>(high.U32()) << 32u)));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::UnpackUint2x32: {
|
||||
const auto value = Arg(inst, 0);
|
||||
const auto* producer = value.TryInstruction();
|
||||
if (producer != nullptr && producer->GetOpcode() == ValueOpcode::PackUint2x32) {
|
||||
Replace(inst, producer->Arg(0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::IAdd32:
|
||||
if (!FoldU32(inst, [](uint32_t a, uint32_t b) { return a + b; })) {
|
||||
ReplaceBinaryIdentity(inst, Type::U32, 0u);
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::IAdd64:
|
||||
if (!FoldU64(inst, [](uint64_t a, uint64_t b) { return a + b; })) {
|
||||
ReplaceBinaryIdentity(inst, Type::U64, 0u);
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::ISub32: {
|
||||
if (FoldU32(inst, [](uint32_t a, uint32_t b) { return a - b; })) {
|
||||
return;
|
||||
}
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (IsImmediate(rhs, Type::U32) && rhs.U32() == 0u) {
|
||||
Replace(inst, Arg(inst, 0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::ISub64: {
|
||||
if (FoldU64(inst, [](uint64_t a, uint64_t b) { return a - b; })) {
|
||||
return;
|
||||
}
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (IsImmediate(rhs, Type::U64) && rhs.U64() == 0u) {
|
||||
Replace(inst, Arg(inst, 0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::IMul32:
|
||||
if (!FoldU32(inst, [](uint32_t a, uint32_t b) { return a * b; })) {
|
||||
ReplaceBinaryIdentity(inst, Type::U32, 1u);
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::IMul64:
|
||||
if (!FoldU64(inst, [](uint64_t a, uint64_t b) { return a * b; })) {
|
||||
ReplaceBinaryIdentity(inst, Type::U64, 1u);
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::UDiv32: {
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (IsImmediate(rhs, Type::U32) && rhs.U32() == 1u) {
|
||||
Replace(inst, Arg(inst, 0));
|
||||
} else if (IsImmediate(rhs, Type::U32) && rhs.U32() != 0u) {
|
||||
FoldU32(inst, [](uint32_t a, uint32_t b) { return a / b; });
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::SMulHi:
|
||||
FoldU32(inst, [](uint32_t a, uint32_t b) {
|
||||
const auto product = static_cast<int64_t>(std::bit_cast<int32_t>(a)) *
|
||||
static_cast<int64_t>(std::bit_cast<int32_t>(b));
|
||||
return static_cast<uint32_t>(static_cast<uint64_t>(product) >> 32u);
|
||||
});
|
||||
return;
|
||||
case ValueOpcode::UMulHi:
|
||||
FoldU32(inst, [](uint32_t a, uint32_t b) {
|
||||
return static_cast<uint32_t>((static_cast<uint64_t>(a) * b) >> 32u);
|
||||
});
|
||||
return;
|
||||
case ValueOpcode::IAbs32: {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::U32)) {
|
||||
Replace(inst,
|
||||
Value((value.U32() & 0x80000000u) != 0u ? 0u - value.U32() : value.U32()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::ShiftLeftLogical32:
|
||||
if (!FoldU32(inst, [](uint32_t a, uint32_t b) { return a << (b & 31u); })) {
|
||||
const auto shift = Arg(inst, 1);
|
||||
if (IsImmediate(shift, Type::U32) && (shift.U32() & 31u) == 0u) {
|
||||
Replace(inst, Arg(inst, 0));
|
||||
}
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::ShiftRightLogical32:
|
||||
if (!FoldU32(inst, [](uint32_t a, uint32_t b) { return a >> (b & 31u); })) {
|
||||
const auto shift = Arg(inst, 1);
|
||||
if (IsImmediate(shift, Type::U32) && (shift.U32() & 31u) == 0u) {
|
||||
Replace(inst, Arg(inst, 0));
|
||||
}
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::ShiftRightArithmetic32:
|
||||
FoldU32(inst, [](uint32_t a, uint32_t b) {
|
||||
return static_cast<uint32_t>(std::bit_cast<int32_t>(a) >> (b & 31u));
|
||||
});
|
||||
return;
|
||||
case ValueOpcode::ShiftLeftLogical64:
|
||||
if (!FoldU64Shift(inst, [](uint64_t a, uint32_t b) { return a << (b & 63u); })) {
|
||||
const auto shift = Arg(inst, 1);
|
||||
if (IsImmediate(shift, Type::U32) && (shift.U32() & 63u) == 0u) {
|
||||
Replace(inst, Arg(inst, 0));
|
||||
}
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::ShiftRightLogical64:
|
||||
if (!FoldU64Shift(inst, [](uint64_t a, uint32_t b) { return a >> (b & 63u); })) {
|
||||
const auto shift = Arg(inst, 1);
|
||||
if (IsImmediate(shift, Type::U32) && (shift.U32() & 63u) == 0u) {
|
||||
Replace(inst, Arg(inst, 0));
|
||||
}
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::ShiftRightArithmetic64:
|
||||
FoldU64Shift(inst, [](uint64_t a, uint32_t b) {
|
||||
return static_cast<uint64_t>(std::bit_cast<int64_t>(a) >> (b & 63u));
|
||||
});
|
||||
return;
|
||||
case ValueOpcode::BitwiseAnd32:
|
||||
if (!FoldU32(inst, [](uint32_t a, uint32_t b) { return a & b; })) {
|
||||
ReplaceBinaryIdentity(inst, Type::U32, 0xffffffffu);
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::BitwiseAnd64:
|
||||
if (!FoldU64(inst, [](uint64_t a, uint64_t b) { return a & b; })) {
|
||||
ReplaceBinaryIdentity(inst, Type::U64, UINT64_MAX);
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::BitwiseOr32:
|
||||
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);
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::BitwiseOr64:
|
||||
if (!FoldU64(inst, [](uint64_t a, uint64_t b) { return a | b; })) {
|
||||
ReplaceBinaryIdentity(inst, Type::U64, 0u);
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::BitwiseNot32: {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::U32)) {
|
||||
Replace(inst, Value(~value.U32()));
|
||||
} else if (auto* producer = value.TryInstruction();
|
||||
producer != nullptr && producer->GetOpcode() == ValueOpcode::BitwiseNot32) {
|
||||
Replace(inst, producer->Arg(0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::BitCount32: {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::U32)) {
|
||||
Replace(inst, Value(static_cast<uint32_t>(std::popcount(value.U32()))));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::BitCount64: {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::U64)) {
|
||||
Replace(inst, Value(static_cast<uint32_t>(std::popcount(value.U64()))));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ValueOpcode::SMin32:
|
||||
case ValueOpcode::SMax32:
|
||||
FoldU32(inst, [opcode = inst.GetOpcode()](uint32_t a, uint32_t b) {
|
||||
const auto lhs = std::bit_cast<int32_t>(a);
|
||||
const auto rhs = std::bit_cast<int32_t>(b);
|
||||
return opcode == ValueOpcode::SMin32 ? (lhs < rhs ? a : b) : (lhs > rhs ? a : b);
|
||||
});
|
||||
return;
|
||||
case ValueOpcode::UMin32:
|
||||
FoldU32(inst, [](uint32_t a, uint32_t b) { return std::min(a, b); });
|
||||
return;
|
||||
case ValueOpcode::UMax32:
|
||||
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; });
|
||||
return;
|
||||
case ValueOpcode::INotEqual32:
|
||||
FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a != b; });
|
||||
return;
|
||||
case ValueOpcode::ULessThan32:
|
||||
FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a < b; });
|
||||
return;
|
||||
case ValueOpcode::ULessThanEqual32:
|
||||
FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a <= b; });
|
||||
return;
|
||||
case ValueOpcode::UGreaterThan32:
|
||||
FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a > b; });
|
||||
return;
|
||||
case ValueOpcode::UGreaterThanEqual32:
|
||||
FoldU32Compare(inst, [](uint32_t a, uint32_t b) { return a >= b; });
|
||||
return;
|
||||
case ValueOpcode::SLessThan32:
|
||||
case ValueOpcode::SLessThanEqual32:
|
||||
case ValueOpcode::SGreaterThan32:
|
||||
case ValueOpcode::SGreaterThanEqual32:
|
||||
FoldU32Compare(inst, [opcode = inst.GetOpcode()](uint32_t a, uint32_t b) {
|
||||
const auto lhs = std::bit_cast<int32_t>(a);
|
||||
const auto rhs = std::bit_cast<int32_t>(b);
|
||||
switch (opcode) {
|
||||
case ValueOpcode::SLessThan32: return lhs < rhs;
|
||||
case ValueOpcode::SLessThanEqual32: return lhs <= rhs;
|
||||
case ValueOpcode::SGreaterThan32: return lhs > rhs;
|
||||
default: return lhs >= rhs;
|
||||
}
|
||||
});
|
||||
return;
|
||||
case ValueOpcode::IEqual64:
|
||||
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a == b; });
|
||||
return;
|
||||
case ValueOpcode::INotEqual64:
|
||||
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a != b; });
|
||||
return;
|
||||
case ValueOpcode::ULessThan64:
|
||||
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a < b; });
|
||||
return;
|
||||
case ValueOpcode::ULessThanEqual64:
|
||||
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a <= b; });
|
||||
return;
|
||||
case ValueOpcode::UGreaterThan64:
|
||||
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a > b; });
|
||||
return;
|
||||
case ValueOpcode::UGreaterThanEqual64:
|
||||
FoldU64Compare(inst, [](uint64_t a, uint64_t b) { return a >= b; });
|
||||
return;
|
||||
case ValueOpcode::SLessThan64:
|
||||
case ValueOpcode::SLessThanEqual64:
|
||||
case ValueOpcode::SGreaterThan64:
|
||||
case ValueOpcode::SGreaterThanEqual64:
|
||||
FoldU64Compare(inst, [opcode = inst.GetOpcode()](uint64_t a, uint64_t b) {
|
||||
const auto lhs = std::bit_cast<int64_t>(a);
|
||||
const auto rhs = std::bit_cast<int64_t>(b);
|
||||
switch (opcode) {
|
||||
case ValueOpcode::SLessThan64: return lhs < rhs;
|
||||
case ValueOpcode::SLessThanEqual64: return lhs <= rhs;
|
||||
case ValueOpcode::SGreaterThan64: return lhs > rhs;
|
||||
default: return lhs >= rhs;
|
||||
}
|
||||
});
|
||||
return;
|
||||
case ValueOpcode::LogicalAnd:
|
||||
if (!FoldLogical(inst, [](bool a, bool b) { return a && b; })) {
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (IsImmediate(lhs, Type::U1)) {
|
||||
Replace(inst, lhs.U1() ? rhs : lhs);
|
||||
} else if (IsImmediate(rhs, Type::U1)) {
|
||||
Replace(inst, rhs.U1() ? lhs : rhs);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::LogicalOr:
|
||||
if (!FoldLogical(inst, [](bool a, bool b) { return a || b; })) {
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (IsImmediate(lhs, Type::U1)) {
|
||||
Replace(inst, lhs.U1() ? lhs : rhs);
|
||||
} else if (IsImmediate(rhs, Type::U1)) {
|
||||
Replace(inst, rhs.U1() ? rhs : lhs);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::LogicalXor:
|
||||
if (!FoldLogical(inst, [](bool a, bool b) { return a != b; })) {
|
||||
const auto lhs = Arg(inst, 0);
|
||||
const auto rhs = Arg(inst, 1);
|
||||
if (IsImmediate(lhs, Type::U1) && !lhs.U1()) {
|
||||
Replace(inst, rhs);
|
||||
} else if (IsImmediate(rhs, Type::U1) && !rhs.U1()) {
|
||||
Replace(inst, lhs);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case ValueOpcode::LogicalNot: {
|
||||
const auto value = Arg(inst, 0);
|
||||
if (IsImmediate(value, Type::U1)) {
|
||||
Replace(inst, Value(!value.U1()));
|
||||
} else if (auto* producer = value.TryInstruction();
|
||||
producer != nullptr && producer->GetOpcode() == ValueOpcode::LogicalNot) {
|
||||
Replace(inst, producer->Arg(0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ConstantPropagationPass(const BlockList& blocks) {
|
||||
for (auto* block: blocks) {
|
||||
for (auto& inst: block->Instructions()) {
|
||||
FoldInstruction(inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
Reference in New Issue
Block a user