Merge pull request #401 from lioncash/folding
constant_propagation_pass: Fold &, |, ^, and ~ operations where applicable
This commit is contained in:
commit
c649f11c0a
3 changed files with 156 additions and 1 deletions
|
@ -174,4 +174,24 @@ u64 Value::GetImmediateAsU64() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Value::HasAllBitsSet() const {
|
||||||
|
ASSERT(IsImmediate());
|
||||||
|
|
||||||
|
switch (GetType()) {
|
||||||
|
case IR::Type::U1:
|
||||||
|
return GetU1();
|
||||||
|
case IR::Type::U8:
|
||||||
|
return GetU8() == 0xFF;
|
||||||
|
case IR::Type::U16:
|
||||||
|
return GetU16() == 0xFFFF;
|
||||||
|
case IR::Type::U32:
|
||||||
|
return GetU32() == 0xFFFFFFFF;
|
||||||
|
case IR::Type::U64:
|
||||||
|
return GetU64() == 0xFFFFFFFFFFFFFFFF;
|
||||||
|
default:
|
||||||
|
ASSERT_MSG(false, "HasAllBitsSet called on an incompatible Value type.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::IR
|
} // namespace Dynarmic::IR
|
||||||
|
|
|
@ -74,6 +74,14 @@ public:
|
||||||
*/
|
*/
|
||||||
u64 GetImmediateAsU64() const;
|
u64 GetImmediateAsU64() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not the contained constant value has all bits set.
|
||||||
|
*
|
||||||
|
* @pre The value contains either a U1, U8, U16, U32, or U64 value.
|
||||||
|
* Breaking this precondition will cause an assertion to be invoked.
|
||||||
|
*/
|
||||||
|
bool HasAllBitsSet() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type type;
|
Type type;
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,121 @@
|
||||||
#include "ir_opt/passes.h"
|
#include "ir_opt/passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::Optimization {
|
namespace Dynarmic::Optimization {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Folds AND operations based on the following:
|
||||||
|
//
|
||||||
|
// 1. imm_x & imm_y -> result
|
||||||
|
// 2. x & 0 -> 0
|
||||||
|
// 3. 0 & y -> 0
|
||||||
|
// 4. x & y -> y (where x has all bits set to 1)
|
||||||
|
// 5. x & y -> x (where y has all bits set to 1)
|
||||||
|
//
|
||||||
|
void FoldAND(IR::Inst& inst, bool is_32_bit) {
|
||||||
|
const auto lhs = inst.GetArg(0);
|
||||||
|
const auto rhs = inst.GetArg(1);
|
||||||
|
|
||||||
|
const bool is_lhs_immediate = lhs.IsImmediate();
|
||||||
|
const bool is_rhs_immediate = rhs.IsImmediate();
|
||||||
|
|
||||||
|
if (is_lhs_immediate && is_rhs_immediate) {
|
||||||
|
const u64 result = lhs.GetImmediateAsU64() & rhs.GetImmediateAsU64();
|
||||||
|
|
||||||
|
if (is_32_bit) {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(result)});
|
||||||
|
} else {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{result});
|
||||||
|
}
|
||||||
|
} else if ((is_lhs_immediate && lhs.GetImmediateAsU64() == 0) || (is_rhs_immediate && rhs.GetImmediateAsU64() == 0)) {
|
||||||
|
if (is_32_bit) {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{u32{0}});
|
||||||
|
} else {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{u64{0}});
|
||||||
|
}
|
||||||
|
} else if (is_lhs_immediate && lhs.HasAllBitsSet()) {
|
||||||
|
inst.ReplaceUsesWith(rhs);
|
||||||
|
} else if (is_rhs_immediate && rhs.HasAllBitsSet()) {
|
||||||
|
inst.ReplaceUsesWith(lhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Folds EOR operations based on the following:
|
||||||
|
//
|
||||||
|
// 1. imm_x ^ imm_y -> result
|
||||||
|
// 2. x ^ 0 -> x
|
||||||
|
// 3. 0 ^ y -> y
|
||||||
|
//
|
||||||
|
void FoldEOR(IR::Inst& inst, bool is_32_bit) {
|
||||||
|
const auto lhs = inst.GetArg(0);
|
||||||
|
const auto rhs = inst.GetArg(1);
|
||||||
|
|
||||||
|
const bool is_lhs_immediate = lhs.IsImmediate();
|
||||||
|
const bool is_rhs_immediate = rhs.IsImmediate();
|
||||||
|
|
||||||
|
if (is_lhs_immediate && is_rhs_immediate) {
|
||||||
|
const u64 result = lhs.GetImmediateAsU64() ^ rhs.GetImmediateAsU64();
|
||||||
|
|
||||||
|
if (is_32_bit) {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(result)});
|
||||||
|
} else {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{result});
|
||||||
|
}
|
||||||
|
} else if (is_lhs_immediate && lhs.GetImmediateAsU64() == 0) {
|
||||||
|
inst.ReplaceUsesWith(rhs);
|
||||||
|
} else if (is_rhs_immediate && rhs.GetImmediateAsU64() == 0) {
|
||||||
|
inst.ReplaceUsesWith(lhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Folds NOT operations if the contained value is an immediate.
|
||||||
|
void FoldNOT(IR::Inst& inst, bool is_32_bit) {
|
||||||
|
const auto operand = inst.GetArg(0);
|
||||||
|
|
||||||
|
if (operand.IsImmediate()) {
|
||||||
|
const u64 result = ~operand.GetImmediateAsU64();
|
||||||
|
|
||||||
|
if (is_32_bit) {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(result)});
|
||||||
|
} else {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{result});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Folds OR operations based on the following:
|
||||||
|
//
|
||||||
|
// 1. imm_x | imm_y -> result
|
||||||
|
// 2. x | 0 -> x
|
||||||
|
// 3. 0 | y -> y
|
||||||
|
//
|
||||||
|
void FoldOR(IR::Inst& inst, bool is_32_bit) {
|
||||||
|
const auto lhs = inst.GetArg(0);
|
||||||
|
const auto rhs = inst.GetArg(1);
|
||||||
|
|
||||||
|
const bool is_lhs_immediate = lhs.IsImmediate();
|
||||||
|
const bool is_rhs_immediate = rhs.IsImmediate();
|
||||||
|
|
||||||
|
if (is_lhs_immediate && is_rhs_immediate) {
|
||||||
|
const u64 result = lhs.GetImmediateAsU64() | rhs.GetImmediateAsU64();
|
||||||
|
|
||||||
|
if (is_32_bit) {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{static_cast<u32>(result)});
|
||||||
|
} else {
|
||||||
|
inst.ReplaceUsesWith(IR::Value{result});
|
||||||
|
}
|
||||||
|
} else if (is_lhs_immediate && lhs.GetImmediateAsU64() == 0) {
|
||||||
|
inst.ReplaceUsesWith(rhs);
|
||||||
|
} else if (is_rhs_immediate && rhs.GetImmediateAsU64() == 0) {
|
||||||
|
inst.ReplaceUsesWith(lhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
void ConstantPropagation(IR::Block& block) {
|
void ConstantPropagation(IR::Block& block) {
|
||||||
for (auto& inst : block) {
|
for (auto& inst : block) {
|
||||||
switch (inst.GetOpcode()) {
|
const auto opcode = inst.GetOpcode();
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
case IR::Opcode::LogicalShiftLeft32:
|
case IR::Opcode::LogicalShiftLeft32:
|
||||||
case IR::Opcode::LogicalShiftRight32:
|
case IR::Opcode::LogicalShiftRight32:
|
||||||
case IR::Opcode::ArithmeticShiftRight32:
|
case IR::Opcode::ArithmeticShiftRight32:
|
||||||
|
@ -33,6 +144,22 @@ void ConstantPropagation(IR::Block& block) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case IR::Opcode::And32:
|
||||||
|
case IR::Opcode::And64:
|
||||||
|
FoldAND(inst, opcode == IR::Opcode::And32);
|
||||||
|
break;
|
||||||
|
case IR::Opcode::Eor32:
|
||||||
|
case IR::Opcode::Eor64:
|
||||||
|
FoldEOR(inst, opcode == IR::Opcode::Eor32);
|
||||||
|
break;
|
||||||
|
case IR::Opcode::Or32:
|
||||||
|
case IR::Opcode::Or64:
|
||||||
|
FoldOR(inst, opcode == IR::Opcode::Or32);
|
||||||
|
break;
|
||||||
|
case IR::Opcode::Not32:
|
||||||
|
case IR::Opcode::Not64:
|
||||||
|
FoldNOT(inst, opcode == IR::Opcode::Not32);
|
||||||
|
break;
|
||||||
case IR::Opcode::ZeroExtendByteToWord: {
|
case IR::Opcode::ZeroExtendByteToWord: {
|
||||||
if (!inst.AreAllArgsImmediates())
|
if (!inst.AreAllArgsImmediates())
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue