Merge pull request #401 from lioncash/folding

constant_propagation_pass: Fold &, |, ^, and ~ operations where applicable
This commit is contained in:
Merry 2018-10-02 21:22:58 +01:00 committed by MerryMage
commit c649f11c0a
3 changed files with 156 additions and 1 deletions

View file

@ -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

View file

@ -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;

View file

@ -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;