From 6395f09f94243f070325e8d77f53a76eb036bc83 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Thu, 18 Jan 2018 11:36:48 +0000 Subject: [PATCH] IR: Implement Conditional Select --- src/backend_x64/emit_x64.cpp | 81 ++++++++++++++++++++++++++++++++++ src/backend_x64/reg_alloc.cpp | 5 +++ src/backend_x64/reg_alloc.h | 2 + src/frontend/ir/ir_emitter.cpp | 17 +++++++ src/frontend/ir/ir_emitter.h | 3 ++ src/frontend/ir/opcodes.h | 1 + src/frontend/ir/opcodes.inc | 2 + src/frontend/ir/value.cpp | 11 +++++ src/frontend/ir/value.h | 4 ++ 9 files changed, 126 insertions(+) diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 09d5cf6c..0dadd6c5 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -250,6 +250,87 @@ void EmitX64::EmitTestBit(EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, result); } +template +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(args[0].GetImmediateCond())); + } + + ctx.reg_alloc.DefineValue(inst, else_); +} + +template +void EmitX64::EmitConditionalSelect32(EmitContext& ctx, IR::Inst* inst) { + EmitConditionalSelect(code, ctx, inst, 32); +} + +template +void EmitX64::EmitConditionalSelect64(EmitContext& ctx, IR::Inst* inst) { + EmitConditionalSelect(code, ctx, inst, 64); +} + template void EmitX64::EmitLogicalShiftLeft32(EmitContext& ctx, IR::Inst* inst) { auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); diff --git a/src/backend_x64/reg_alloc.cpp b/src/backend_x64/reg_alloc.cpp index 8104e665..e7ddf9e9 100644 --- a/src/backend_x64/reg_alloc.cpp +++ b/src/backend_x64/reg_alloc.cpp @@ -146,6 +146,11 @@ u64 Argument::GetImmediateU64() const { return ImmediateToU64(value); } +IR::Cond Argument::GetImmediateCond() const { + ASSERT(IsImmediate() && GetType() == IR::Type::Cond); + return value.GetCond(); +} + bool Argument::IsInGpr() const { if (IsImmediate()) return false; diff --git a/src/backend_x64/reg_alloc.h b/src/backend_x64/reg_alloc.h index e56584aa..0e9fff00 100644 --- a/src/backend_x64/reg_alloc.h +++ b/src/backend_x64/reg_alloc.h @@ -17,6 +17,7 @@ #include "backend_x64/hostloc.h" #include "backend_x64/oparg.h" #include "common/common_types.h" +#include "frontend/ir/cond.h" #include "frontend/ir/microinstruction.h" #include "frontend/ir/value.h" @@ -65,6 +66,7 @@ public: u32 GetImmediateU32() const; u64 GetImmediateS32() const; u64 GetImmediateU64() const; + IR::Cond GetImmediateCond() const; /// Is this value currently in a GPR? bool IsInGpr() const; diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index 9268ce08..df3f574d 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -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(Opcode::ConditionalSelect32, Value{cond}, a, b); +} + +U64 IREmitter::ConditionalSelect(Cond cond, const U64& a, const U64& b) { + return Inst(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(Opcode::ConditionalSelect32, Value{cond}, a, b); + } else { + return Inst(Opcode::ConditionalSelect64, Value{cond}, a, b); + } +} + NZCV IREmitter::NZCVFrom(const Value& value) { return Inst(Opcode::GetNZCVFromOp, value); } diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index ca7490ec..dbb20e51 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -83,6 +83,9 @@ public: U1 IsZero(const U64& value); U1 IsZero(const U32U64& value); 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. NZCV NZCVFrom(const Value& value); diff --git a/src/frontend/ir/opcodes.h b/src/frontend/ir/opcodes.h index e58abcbb..71f49995 100644 --- a/src/frontend/ir/opcodes.h +++ b/src/frontend/ir/opcodes.h @@ -48,6 +48,7 @@ enum class Type { F128 = 1 << 12, CoprocInfo = 1 << 13, NZCVFlags = 1 << 14, + Cond = 1 << 15, }; constexpr Type operator|(Type a, Type b) { diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index fbb9f46b..f8a59eb7 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -67,6 +67,8 @@ OPCODE(MostSignificantBit, T::U1, T::U32 OPCODE(IsZero32, T::U1, T::U32 ) OPCODE(IsZero64, T::U1, T::U64 ) 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(LogicalShiftLeft64, T::U64, T::U64, T::U8 ) OPCODE(LogicalShiftRight32, T::U32, T::U32, T::U8, T::U1 ) diff --git a/src/frontend/ir/value.cpp b/src/frontend/ir/value.cpp index 7203e33f..c9cc8080 100644 --- a/src/frontend/ir/value.cpp +++ b/src/frontend/ir/value.cpp @@ -55,6 +55,10 @@ Value::Value(std::array value) : type(Type::CoprocInfo) { inner.imm_coproc = value; } +Value::Value(Cond value) : type(Type::Cond) { + inner.imm_cond = value; +} + bool Value::IsImmediate() const { if (type == Type::Opaque) return inner.inst->GetOpcode() == Opcode::Identity ? inner.inst->GetArg(0).IsImmediate() : false; @@ -143,5 +147,12 @@ std::array Value::GetCoprocInfo() const { 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 Dynarmic diff --git a/src/frontend/ir/value.h b/src/frontend/ir/value.h index 4b93e4fd..b27d69a0 100644 --- a/src/frontend/ir/value.h +++ b/src/frontend/ir/value.h @@ -12,6 +12,7 @@ #include "common/common_types.h" #include "frontend/A32/types.h" #include "frontend/A64/types.h" +#include "frontend/ir/cond.h" #include "frontend/ir/opcodes.h" namespace Dynarmic { @@ -37,6 +38,7 @@ public: explicit Value(u32 value); explicit Value(u64 value); explicit Value(std::array value); + explicit Value(Cond value); bool IsEmpty() const; bool IsImmediate() const; @@ -53,6 +55,7 @@ public: u32 GetU32() const; u64 GetU64() const; std::array GetCoprocInfo() const; + Cond GetCond() const; private: Type type; @@ -69,6 +72,7 @@ private: u32 imm_u32; u64 imm_u64; std::array imm_coproc; + Cond imm_cond; } inner; }; static_assert(sizeof(Value) <= 2 * sizeof(u64), "IR::Value should be kept small in size");