IR: Implement Conditional Select
This commit is contained in:
parent
7992a319ba
commit
6395f09f94
9 changed files with 126 additions and 0 deletions
|
@ -250,6 +250,87 @@ void EmitX64<JST>::EmitTestBit(EmitContext& ctx, IR::Inst* inst) {
|
||||||
ctx.reg_alloc.DefineValue(inst, result);
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void EmitConditionalSelect(BlockOfCode* code, EmitContext& ctx, IR::Inst* inst, int bitsize) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Reg32 nzcv = ctx.reg_alloc.ScratchGpr({HostLoc::RAX}).cvt32();
|
||||||
|
Xbyak::Reg then_ = ctx.reg_alloc.UseGpr(args[1]).changeBit(bitsize);
|
||||||
|
Xbyak::Reg else_ = ctx.reg_alloc.UseScratchGpr(args[2]).changeBit(bitsize);
|
||||||
|
|
||||||
|
code->mov(nzcv, dword[r15 + offsetof(JST, CPSR_nzcv)]);
|
||||||
|
// TODO: Flag optimization
|
||||||
|
code->shr(nzcv, 28);
|
||||||
|
code->imul(nzcv, nzcv, 0b00010000'10000001);
|
||||||
|
code->and_(nzcv.cvt8(), 1);
|
||||||
|
code->add(nzcv.cvt8(), 0x7F); // restore OF
|
||||||
|
code->sahf(); // restore SF, ZF, CF
|
||||||
|
|
||||||
|
switch (args[0].GetImmediateCond()) {
|
||||||
|
case IR::Cond::EQ: //z
|
||||||
|
code->cmovz(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::NE: //!z
|
||||||
|
code->cmovnz(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::CS: //c
|
||||||
|
code->cmovc(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::CC: //!c
|
||||||
|
code->cmovnc(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::MI: //n
|
||||||
|
code->cmovs(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::PL: //!n
|
||||||
|
code->cmovns(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::VS: //v
|
||||||
|
code->cmovo(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::VC: //!v
|
||||||
|
code->cmovno(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::HI: //c & !z
|
||||||
|
code->cmc();
|
||||||
|
code->cmova(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::LS: //!c | z
|
||||||
|
code->cmc();
|
||||||
|
code->cmovna(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::GE: // n == v
|
||||||
|
code->cmovge(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::LT: // n != v
|
||||||
|
code->cmovl(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::GT: // !z & (n == v)
|
||||||
|
code->cmovg(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::LE: // z | (n != v)
|
||||||
|
code->cmovle(else_, then_);
|
||||||
|
break;
|
||||||
|
case IR::Cond::AL:
|
||||||
|
case IR::Cond::NV:
|
||||||
|
code->mov(else_, then_);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_MSG(false, "Invalid cond %zu", static_cast<size_t>(args[0].GetImmediateCond()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, else_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitConditionalSelect32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitConditionalSelect<JST>(code, ctx, inst, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitConditionalSelect64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitConditionalSelect<JST>(code, ctx, inst, 64);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename JST>
|
template <typename JST>
|
||||||
void EmitX64<JST>::EmitLogicalShiftLeft32(EmitContext& ctx, IR::Inst* inst) {
|
void EmitX64<JST>::EmitLogicalShiftLeft32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
||||||
|
|
|
@ -146,6 +146,11 @@ u64 Argument::GetImmediateU64() const {
|
||||||
return ImmediateToU64(value);
|
return ImmediateToU64(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IR::Cond Argument::GetImmediateCond() const {
|
||||||
|
ASSERT(IsImmediate() && GetType() == IR::Type::Cond);
|
||||||
|
return value.GetCond();
|
||||||
|
}
|
||||||
|
|
||||||
bool Argument::IsInGpr() const {
|
bool Argument::IsInGpr() const {
|
||||||
if (IsImmediate())
|
if (IsImmediate())
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "backend_x64/hostloc.h"
|
#include "backend_x64/hostloc.h"
|
||||||
#include "backend_x64/oparg.h"
|
#include "backend_x64/oparg.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/ir/cond.h"
|
||||||
#include "frontend/ir/microinstruction.h"
|
#include "frontend/ir/microinstruction.h"
|
||||||
#include "frontend/ir/value.h"
|
#include "frontend/ir/value.h"
|
||||||
|
|
||||||
|
@ -65,6 +66,7 @@ public:
|
||||||
u32 GetImmediateU32() const;
|
u32 GetImmediateU32() const;
|
||||||
u64 GetImmediateS32() const;
|
u64 GetImmediateS32() const;
|
||||||
u64 GetImmediateU64() const;
|
u64 GetImmediateU64() const;
|
||||||
|
IR::Cond GetImmediateCond() const;
|
||||||
|
|
||||||
/// Is this value currently in a GPR?
|
/// Is this value currently in a GPR?
|
||||||
bool IsInGpr() const;
|
bool IsInGpr() const;
|
||||||
|
|
|
@ -91,6 +91,23 @@ U1 IREmitter::TestBit(const U32U64& value, const U8& bit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
U32 IREmitter::ConditionalSelect(Cond cond, const U32& a, const U32& b) {
|
||||||
|
return Inst<U32>(Opcode::ConditionalSelect32, Value{cond}, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
U64 IREmitter::ConditionalSelect(Cond cond, const U64& a, const U64& b) {
|
||||||
|
return Inst<U64>(Opcode::ConditionalSelect64, Value{cond}, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
U32U64 IREmitter::ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b) {
|
||||||
|
ASSERT(a.GetType() == b.GetType());
|
||||||
|
if (a.GetType() == Type::U32) {
|
||||||
|
return Inst<U32>(Opcode::ConditionalSelect32, Value{cond}, a, b);
|
||||||
|
} else {
|
||||||
|
return Inst<U64>(Opcode::ConditionalSelect64, Value{cond}, a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NZCV IREmitter::NZCVFrom(const Value& value) {
|
NZCV IREmitter::NZCVFrom(const Value& value) {
|
||||||
return Inst<NZCV>(Opcode::GetNZCVFromOp, value);
|
return Inst<NZCV>(Opcode::GetNZCVFromOp, value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,9 @@ public:
|
||||||
U1 IsZero(const U64& value);
|
U1 IsZero(const U64& value);
|
||||||
U1 IsZero(const U32U64& value);
|
U1 IsZero(const U32U64& value);
|
||||||
U1 TestBit(const U32U64& value, const U8& bit);
|
U1 TestBit(const U32U64& value, const U8& bit);
|
||||||
|
U32 ConditionalSelect(Cond cond, const U32& a, const U32& b);
|
||||||
|
U64 ConditionalSelect(Cond cond, const U64& a, const U64& b);
|
||||||
|
U32U64 ConditionalSelect(Cond cond, const U32U64& a, const U32U64& b);
|
||||||
|
|
||||||
// This pseudo-instruction may only be added to instructions that support it.
|
// This pseudo-instruction may only be added to instructions that support it.
|
||||||
NZCV NZCVFrom(const Value& value);
|
NZCV NZCVFrom(const Value& value);
|
||||||
|
|
|
@ -48,6 +48,7 @@ enum class Type {
|
||||||
F128 = 1 << 12,
|
F128 = 1 << 12,
|
||||||
CoprocInfo = 1 << 13,
|
CoprocInfo = 1 << 13,
|
||||||
NZCVFlags = 1 << 14,
|
NZCVFlags = 1 << 14,
|
||||||
|
Cond = 1 << 15,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr Type operator|(Type a, Type b) {
|
constexpr Type operator|(Type a, Type b) {
|
||||||
|
|
|
@ -67,6 +67,8 @@ OPCODE(MostSignificantBit, T::U1, T::U32
|
||||||
OPCODE(IsZero32, T::U1, T::U32 )
|
OPCODE(IsZero32, T::U1, T::U32 )
|
||||||
OPCODE(IsZero64, T::U1, T::U64 )
|
OPCODE(IsZero64, T::U1, T::U64 )
|
||||||
OPCODE(TestBit, T::U1, T::U64, T::U8 )
|
OPCODE(TestBit, T::U1, T::U64, T::U8 )
|
||||||
|
OPCODE(ConditionalSelect32, T::U32, T::Cond, T::U32, T::U32 )
|
||||||
|
OPCODE(ConditionalSelect64, T::U64, T::Cond, T::U64, T::U64 )
|
||||||
OPCODE(LogicalShiftLeft32, T::U32, T::U32, T::U8, T::U1 )
|
OPCODE(LogicalShiftLeft32, T::U32, T::U32, T::U8, T::U1 )
|
||||||
OPCODE(LogicalShiftLeft64, T::U64, T::U64, T::U8 )
|
OPCODE(LogicalShiftLeft64, T::U64, T::U64, T::U8 )
|
||||||
OPCODE(LogicalShiftRight32, T::U32, T::U32, T::U8, T::U1 )
|
OPCODE(LogicalShiftRight32, T::U32, T::U32, T::U8, T::U1 )
|
||||||
|
|
|
@ -55,6 +55,10 @@ Value::Value(std::array<u8, 8> value) : type(Type::CoprocInfo) {
|
||||||
inner.imm_coproc = value;
|
inner.imm_coproc = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value::Value(Cond value) : type(Type::Cond) {
|
||||||
|
inner.imm_cond = value;
|
||||||
|
}
|
||||||
|
|
||||||
bool Value::IsImmediate() const {
|
bool Value::IsImmediate() const {
|
||||||
if (type == Type::Opaque)
|
if (type == Type::Opaque)
|
||||||
return inner.inst->GetOpcode() == Opcode::Identity ? inner.inst->GetArg(0).IsImmediate() : false;
|
return inner.inst->GetOpcode() == Opcode::Identity ? inner.inst->GetArg(0).IsImmediate() : false;
|
||||||
|
@ -143,5 +147,12 @@ std::array<u8, 8> Value::GetCoprocInfo() const {
|
||||||
return inner.imm_coproc;
|
return inner.imm_coproc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cond Value::GetCond() const {
|
||||||
|
if (type == Type::Opaque && inner.inst->GetOpcode() == Opcode::Identity)
|
||||||
|
return inner.inst->GetArg(0).GetCond();
|
||||||
|
ASSERT(type == Type::Cond);
|
||||||
|
return inner.imm_cond;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace IR
|
} // namespace IR
|
||||||
} // namespace Dynarmic
|
} // namespace Dynarmic
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "frontend/A32/types.h"
|
#include "frontend/A32/types.h"
|
||||||
#include "frontend/A64/types.h"
|
#include "frontend/A64/types.h"
|
||||||
|
#include "frontend/ir/cond.h"
|
||||||
#include "frontend/ir/opcodes.h"
|
#include "frontend/ir/opcodes.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
@ -37,6 +38,7 @@ public:
|
||||||
explicit Value(u32 value);
|
explicit Value(u32 value);
|
||||||
explicit Value(u64 value);
|
explicit Value(u64 value);
|
||||||
explicit Value(std::array<u8, 8> value);
|
explicit Value(std::array<u8, 8> value);
|
||||||
|
explicit Value(Cond value);
|
||||||
|
|
||||||
bool IsEmpty() const;
|
bool IsEmpty() const;
|
||||||
bool IsImmediate() const;
|
bool IsImmediate() const;
|
||||||
|
@ -53,6 +55,7 @@ public:
|
||||||
u32 GetU32() const;
|
u32 GetU32() const;
|
||||||
u64 GetU64() const;
|
u64 GetU64() const;
|
||||||
std::array<u8, 8> GetCoprocInfo() const;
|
std::array<u8, 8> GetCoprocInfo() const;
|
||||||
|
Cond GetCond() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type type;
|
Type type;
|
||||||
|
@ -69,6 +72,7 @@ private:
|
||||||
u32 imm_u32;
|
u32 imm_u32;
|
||||||
u64 imm_u64;
|
u64 imm_u64;
|
||||||
std::array<u8, 8> imm_coproc;
|
std::array<u8, 8> imm_coproc;
|
||||||
|
Cond imm_cond;
|
||||||
} inner;
|
} inner;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Value) <= 2 * sizeof(u64), "IR::Value should be kept small in size");
|
static_assert(sizeof(Value) <= 2 * sizeof(u64), "IR::Value should be kept small in size");
|
||||||
|
|
Loading…
Reference in a new issue