backend_x64: Split emit_x64
This commit is contained in:
parent
2a493f8b50
commit
a554e4a329
7 changed files with 2829 additions and 2691 deletions
|
@ -127,6 +127,11 @@ if (ARCHITECTURE_x86_64)
|
||||||
backend_x64/constant_pool.h
|
backend_x64/constant_pool.h
|
||||||
backend_x64/emit_x64.cpp
|
backend_x64/emit_x64.cpp
|
||||||
backend_x64/emit_x64.h
|
backend_x64/emit_x64.h
|
||||||
|
backend_x64/emit_x64_data_processing.cpp
|
||||||
|
backend_x64/emit_x64_floating_point.cpp
|
||||||
|
backend_x64/emit_x64_packed.cpp
|
||||||
|
backend_x64/emit_x64_saturation.cpp
|
||||||
|
backend_x64/emit_x64_vector.cpp
|
||||||
backend_x64/hostloc.cpp
|
backend_x64/hostloc.cpp
|
||||||
backend_x64/hostloc.h
|
backend_x64/hostloc.h
|
||||||
backend_x64/jitstate_info.h
|
backend_x64/jitstate_info.h
|
||||||
|
|
File diff suppressed because it is too large
Load diff
1219
src/backend_x64/emit_x64_data_processing.cpp
Normal file
1219
src/backend_x64/emit_x64_data_processing.cpp
Normal file
File diff suppressed because it is too large
Load diff
645
src/backend_x64/emit_x64_floating_point.cpp
Normal file
645
src/backend_x64/emit_x64_floating_point.cpp
Normal file
|
@ -0,0 +1,645 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2016 MerryMage
|
||||||
|
* This software may be used and distributed according to the terms of the GNU
|
||||||
|
* General Public License version 2 or any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "backend_x64/block_of_code.h"
|
||||||
|
#include "backend_x64/emit_x64.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/ir/microinstruction.h"
|
||||||
|
#include "frontend/ir/opcodes.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace BackendX64 {
|
||||||
|
|
||||||
|
using namespace Xbyak::util;
|
||||||
|
|
||||||
|
constexpr u64 f32_negative_zero = 0x80000000u;
|
||||||
|
constexpr u64 f32_nan = 0x7fc00000u;
|
||||||
|
constexpr u64 f32_non_sign_mask = 0x7fffffffu;
|
||||||
|
|
||||||
|
constexpr u64 f64_negative_zero = 0x8000000000000000u;
|
||||||
|
constexpr u64 f64_nan = 0x7ff8000000000000u;
|
||||||
|
constexpr u64 f64_non_sign_mask = 0x7fffffffffffffffu;
|
||||||
|
|
||||||
|
constexpr u64 f64_penultimate_positive_denormal = 0x000ffffffffffffeu;
|
||||||
|
constexpr u64 f64_min_s32 = 0xc1e0000000000000u; // -2147483648 as a double
|
||||||
|
constexpr u64 f64_max_s32 = 0x41dfffffffc00000u; // 2147483647 as a double
|
||||||
|
constexpr u64 f64_min_u32 = 0x0000000000000000u; // 0 as a double
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void DenormalsAreZero32(BlockOfCode* code, Xbyak::Xmm xmm_value, Xbyak::Reg32 gpr_scratch) {
|
||||||
|
Xbyak::Label end;
|
||||||
|
|
||||||
|
// We need to report back whether we've found a denormal on input.
|
||||||
|
// SSE doesn't do this for us when SSE's DAZ is enabled.
|
||||||
|
|
||||||
|
code->movd(gpr_scratch, xmm_value);
|
||||||
|
code->and_(gpr_scratch, u32(0x7FFFFFFF));
|
||||||
|
code->sub(gpr_scratch, u32(1));
|
||||||
|
code->cmp(gpr_scratch, u32(0x007FFFFE));
|
||||||
|
code->ja(end);
|
||||||
|
code->pxor(xmm_value, xmm_value);
|
||||||
|
code->mov(dword[r15 + offsetof(JST, FPSCR_IDC)], u32(1 << 7));
|
||||||
|
code->L(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void DenormalsAreZero64(BlockOfCode* code, Xbyak::Xmm xmm_value, Xbyak::Reg64 gpr_scratch) {
|
||||||
|
Xbyak::Label end;
|
||||||
|
|
||||||
|
auto mask = code->MConst(f64_non_sign_mask);
|
||||||
|
mask.setBit(64);
|
||||||
|
auto penult_denormal = code->MConst(f64_penultimate_positive_denormal);
|
||||||
|
penult_denormal.setBit(64);
|
||||||
|
|
||||||
|
code->movq(gpr_scratch, xmm_value);
|
||||||
|
code->and_(gpr_scratch, mask);
|
||||||
|
code->sub(gpr_scratch, u32(1));
|
||||||
|
code->cmp(gpr_scratch, penult_denormal);
|
||||||
|
code->ja(end);
|
||||||
|
code->pxor(xmm_value, xmm_value);
|
||||||
|
code->mov(dword[r15 + offsetof(JST, FPSCR_IDC)], u32(1 << 7));
|
||||||
|
code->L(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void FlushToZero32(BlockOfCode* code, Xbyak::Xmm xmm_value, Xbyak::Reg32 gpr_scratch) {
|
||||||
|
Xbyak::Label end;
|
||||||
|
|
||||||
|
code->movd(gpr_scratch, xmm_value);
|
||||||
|
code->and_(gpr_scratch, u32(0x7FFFFFFF));
|
||||||
|
code->sub(gpr_scratch, u32(1));
|
||||||
|
code->cmp(gpr_scratch, u32(0x007FFFFE));
|
||||||
|
code->ja(end);
|
||||||
|
code->pxor(xmm_value, xmm_value);
|
||||||
|
code->mov(dword[r15 + offsetof(JST, FPSCR_UFC)], u32(1 << 3));
|
||||||
|
code->L(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void FlushToZero64(BlockOfCode* code, Xbyak::Xmm xmm_value, Xbyak::Reg64 gpr_scratch) {
|
||||||
|
Xbyak::Label end;
|
||||||
|
|
||||||
|
auto mask = code->MConst(f64_non_sign_mask);
|
||||||
|
mask.setBit(64);
|
||||||
|
auto penult_denormal = code->MConst(f64_penultimate_positive_denormal);
|
||||||
|
penult_denormal.setBit(64);
|
||||||
|
|
||||||
|
code->movq(gpr_scratch, xmm_value);
|
||||||
|
code->and_(gpr_scratch, mask);
|
||||||
|
code->sub(gpr_scratch, u32(1));
|
||||||
|
code->cmp(gpr_scratch, penult_denormal);
|
||||||
|
code->ja(end);
|
||||||
|
code->pxor(xmm_value, xmm_value);
|
||||||
|
code->mov(dword[r15 + offsetof(JST, FPSCR_UFC)], u32(1 << 3));
|
||||||
|
code->L(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DefaultNaN32(BlockOfCode* code, Xbyak::Xmm xmm_value) {
|
||||||
|
Xbyak::Label end;
|
||||||
|
|
||||||
|
code->ucomiss(xmm_value, xmm_value);
|
||||||
|
code->jnp(end);
|
||||||
|
code->movaps(xmm_value, code->MConst(f32_nan));
|
||||||
|
code->L(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DefaultNaN64(BlockOfCode* code, Xbyak::Xmm xmm_value) {
|
||||||
|
Xbyak::Label end;
|
||||||
|
|
||||||
|
code->ucomisd(xmm_value, xmm_value);
|
||||||
|
code->jnp(end);
|
||||||
|
code->movaps(xmm_value, code->MConst(f64_nan));
|
||||||
|
code->L(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ZeroIfNaN64(BlockOfCode* code, Xbyak::Xmm xmm_value, Xbyak::Xmm xmm_scratch) {
|
||||||
|
code->pxor(xmm_scratch, xmm_scratch);
|
||||||
|
code->cmpordsd(xmm_scratch, xmm_value); // true mask when ordered (i.e.: when not an NaN)
|
||||||
|
code->pand(xmm_value, xmm_scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void FPThreeOp32(BlockOfCode* code, EmitContext& ctx, IR::Inst* inst, void (Xbyak::CodeGenerator::*fn)(const Xbyak::Xmm&, const Xbyak::Operand&)) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm operand = ctx.reg_alloc.UseScratchXmm(args[1]);
|
||||||
|
Xbyak::Reg32 gpr_scratch = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero32<JST>(code, result, gpr_scratch);
|
||||||
|
DenormalsAreZero32<JST>(code, operand, gpr_scratch);
|
||||||
|
}
|
||||||
|
(code->*fn)(result, operand);
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
FlushToZero32<JST>(code, result, gpr_scratch);
|
||||||
|
}
|
||||||
|
if (ctx.FPSCR_DN()) {
|
||||||
|
DefaultNaN32(code, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void FPThreeOp64(BlockOfCode* code, EmitContext& ctx, IR::Inst* inst, void (Xbyak::CodeGenerator::*fn)(const Xbyak::Xmm&, const Xbyak::Operand&)) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm operand = ctx.reg_alloc.UseScratchXmm(args[1]);
|
||||||
|
Xbyak::Reg64 gpr_scratch = ctx.reg_alloc.ScratchGpr();
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero64<JST>(code, result, gpr_scratch);
|
||||||
|
DenormalsAreZero64<JST>(code, operand, gpr_scratch);
|
||||||
|
}
|
||||||
|
(code->*fn)(result, operand);
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
FlushToZero64<JST>(code, result, gpr_scratch);
|
||||||
|
}
|
||||||
|
if (ctx.FPSCR_DN()) {
|
||||||
|
DefaultNaN64(code, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void FPTwoOp32(BlockOfCode* code, EmitContext& ctx, IR::Inst* inst, void (Xbyak::CodeGenerator::*fn)(const Xbyak::Xmm&, const Xbyak::Operand&)) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Reg32 gpr_scratch = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero32<JST>(code, result, gpr_scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
(code->*fn)(result, result);
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
FlushToZero32<JST>(code, result, gpr_scratch);
|
||||||
|
}
|
||||||
|
if (ctx.FPSCR_DN()) {
|
||||||
|
DefaultNaN32(code, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void FPTwoOp64(BlockOfCode* code, EmitContext& ctx, IR::Inst* inst, void (Xbyak::CodeGenerator::*fn)(const Xbyak::Xmm&, const Xbyak::Operand&)) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Reg64 gpr_scratch = ctx.reg_alloc.ScratchGpr();
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero64<JST>(code, result, gpr_scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
(code->*fn)(result, result);
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
FlushToZero64<JST>(code, result, gpr_scratch);
|
||||||
|
}
|
||||||
|
if (ctx.FPSCR_DN()) {
|
||||||
|
DefaultNaN64(code, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPAbs32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
|
||||||
|
code->pand(result, code->MConst(f32_non_sign_mask));
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPAbs64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
|
||||||
|
code->pand(result, code->MConst(f64_non_sign_mask));
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPNeg32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
|
||||||
|
code->pxor(result, code->MConst(f32_negative_zero));
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPNeg64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
|
||||||
|
code->pxor(result, code->MConst(f64_negative_zero));
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPThreeOp32<JST>(code, ctx, inst, &Xbyak::CodeGenerator::addss);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPThreeOp64<JST>(code, ctx, inst, &Xbyak::CodeGenerator::addsd);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPDiv32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPThreeOp32<JST>(code, ctx, inst, &Xbyak::CodeGenerator::divss);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPDiv64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPThreeOp64<JST>(code, ctx, inst, &Xbyak::CodeGenerator::divsd);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPMul32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPThreeOp32<JST>(code, ctx, inst, &Xbyak::CodeGenerator::mulss);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPMul64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPThreeOp64<JST>(code, ctx, inst, &Xbyak::CodeGenerator::mulsd);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPSqrt32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPTwoOp32<JST>(code, ctx, inst, &Xbyak::CodeGenerator::sqrtss);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPSqrt64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPTwoOp64<JST>(code, ctx, inst, &Xbyak::CodeGenerator::sqrtsd);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPThreeOp32<JST>(code, ctx, inst, &Xbyak::CodeGenerator::subss);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
FPThreeOp64<JST>(code, ctx, inst, &Xbyak::CodeGenerator::subsd);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
static void SetFpscrNzcvFromFlags(BlockOfCode* code, EmitContext& ctx) {
|
||||||
|
ctx.reg_alloc.ScratchGpr({HostLoc::RCX}); // shifting requires use of cl
|
||||||
|
Xbyak::Reg32 nzcv = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
code->mov(nzcv, 0x28630000);
|
||||||
|
code->sete(cl);
|
||||||
|
code->rcl(cl, 3);
|
||||||
|
code->shl(nzcv, cl);
|
||||||
|
code->and_(nzcv, 0xF0000000);
|
||||||
|
code->mov(dword[r15 + offsetof(JST, FPSCR_nzcv)], nzcv);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPCompare32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm reg_a = ctx.reg_alloc.UseXmm(args[0]);
|
||||||
|
Xbyak::Xmm reg_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
bool exc_on_qnan = args[2].GetImmediateU1();
|
||||||
|
|
||||||
|
if (exc_on_qnan) {
|
||||||
|
code->comiss(reg_a, reg_b);
|
||||||
|
} else {
|
||||||
|
code->ucomiss(reg_a, reg_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetFpscrNzcvFromFlags<JST>(code, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPCompare64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm reg_a = ctx.reg_alloc.UseXmm(args[0]);
|
||||||
|
Xbyak::Xmm reg_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
bool exc_on_qnan = args[2].GetImmediateU1();
|
||||||
|
|
||||||
|
if (exc_on_qnan) {
|
||||||
|
code->comisd(reg_a, reg_b);
|
||||||
|
} else {
|
||||||
|
code->ucomisd(reg_a, reg_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetFpscrNzcvFromFlags<JST>(code, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPSingleToDouble(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Reg64 gpr_scratch = ctx.reg_alloc.ScratchGpr();
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero32<JST>(code, result, gpr_scratch.cvt32());
|
||||||
|
}
|
||||||
|
code->cvtss2sd(result, result);
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
FlushToZero64<JST>(code, result, gpr_scratch);
|
||||||
|
}
|
||||||
|
if (ctx.FPSCR_DN()) {
|
||||||
|
DefaultNaN64(code, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPDoubleToSingle(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Reg64 gpr_scratch = ctx.reg_alloc.ScratchGpr();
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero64<JST>(code, result, gpr_scratch);
|
||||||
|
}
|
||||||
|
code->cvtsd2ss(result, result);
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
FlushToZero32<JST>(code, result, gpr_scratch.cvt32());
|
||||||
|
}
|
||||||
|
if (ctx.FPSCR_DN()) {
|
||||||
|
DefaultNaN32(code, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPSingleToS32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm from = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Reg32 to = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Xmm xmm_scratch = ctx.reg_alloc.ScratchXmm();
|
||||||
|
bool round_towards_zero = args[1].GetImmediateU1();
|
||||||
|
|
||||||
|
// ARM saturates on conversion; this differs from x64 which returns a sentinel value.
|
||||||
|
// Conversion to double is lossless, and allows for clamping.
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero32<JST>(code, from, to);
|
||||||
|
}
|
||||||
|
code->cvtss2sd(from, from);
|
||||||
|
// First time is to set flags
|
||||||
|
if (round_towards_zero) {
|
||||||
|
code->cvttsd2si(to, from); // 32 bit gpr
|
||||||
|
} else {
|
||||||
|
code->cvtsd2si(to, from); // 32 bit gpr
|
||||||
|
}
|
||||||
|
// Clamp to output range
|
||||||
|
ZeroIfNaN64(code, from, xmm_scratch);
|
||||||
|
code->minsd(from, code->MConst(f64_max_s32));
|
||||||
|
code->maxsd(from, code->MConst(f64_min_s32));
|
||||||
|
// Second time is for real
|
||||||
|
if (round_towards_zero) {
|
||||||
|
code->cvttsd2si(to, from); // 32 bit gpr
|
||||||
|
} else {
|
||||||
|
code->cvtsd2si(to, from); // 32 bit gpr
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPSingleToU32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm from = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Reg32 to = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Xmm xmm_scratch = ctx.reg_alloc.ScratchXmm();
|
||||||
|
bool round_towards_zero = args[1].GetImmediateU1();
|
||||||
|
|
||||||
|
// ARM saturates on conversion; this differs from x64 which returns a sentinel value.
|
||||||
|
// Conversion to double is lossless, and allows for accurate clamping.
|
||||||
|
//
|
||||||
|
// Since SSE2 doesn't provide an unsigned conversion, we shift the range as appropriate.
|
||||||
|
//
|
||||||
|
// FIXME: Inexact exception not correctly signalled with the below code
|
||||||
|
|
||||||
|
if (!ctx.FPSCR_RoundTowardsZero() && !round_towards_zero) {
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero32<JST>(code, from, to);
|
||||||
|
}
|
||||||
|
code->cvtss2sd(from, from);
|
||||||
|
ZeroIfNaN64(code, from, xmm_scratch);
|
||||||
|
// Bring into SSE range
|
||||||
|
code->addsd(from, code->MConst(f64_min_s32));
|
||||||
|
// First time is to set flags
|
||||||
|
code->cvtsd2si(to, from); // 32 bit gpr
|
||||||
|
// Clamp to output range
|
||||||
|
code->minsd(from, code->MConst(f64_max_s32));
|
||||||
|
code->maxsd(from, code->MConst(f64_min_s32));
|
||||||
|
// Actually convert
|
||||||
|
code->cvtsd2si(to, from); // 32 bit gpr
|
||||||
|
// Bring back into original range
|
||||||
|
code->add(to, u32(2147483648u));
|
||||||
|
} else {
|
||||||
|
Xbyak::Xmm xmm_mask = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Reg32 gpr_mask = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero32<JST>(code, from, to);
|
||||||
|
}
|
||||||
|
code->cvtss2sd(from, from);
|
||||||
|
ZeroIfNaN64(code, from, xmm_scratch);
|
||||||
|
// Generate masks if out-of-signed-range
|
||||||
|
code->movaps(xmm_mask, code->MConst(f64_max_s32));
|
||||||
|
code->cmpltsd(xmm_mask, from);
|
||||||
|
code->movd(gpr_mask, xmm_mask);
|
||||||
|
code->pand(xmm_mask, code->MConst(f64_min_s32));
|
||||||
|
code->and_(gpr_mask, u32(2147483648u));
|
||||||
|
// Bring into range if necessary
|
||||||
|
code->addsd(from, xmm_mask);
|
||||||
|
// First time is to set flags
|
||||||
|
code->cvttsd2si(to, from); // 32 bit gpr
|
||||||
|
// Clamp to output range
|
||||||
|
code->minsd(from, code->MConst(f64_max_s32));
|
||||||
|
code->maxsd(from, code->MConst(f64_min_u32));
|
||||||
|
// Actually convert
|
||||||
|
code->cvttsd2si(to, from); // 32 bit gpr
|
||||||
|
// Bring back into original range if necessary
|
||||||
|
code->add(to, gpr_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPDoubleToS32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm from = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Reg32 to = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Xmm xmm_scratch = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Reg32 gpr_scratch = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
bool round_towards_zero = args[1].GetImmediateU1();
|
||||||
|
|
||||||
|
// ARM saturates on conversion; this differs from x64 which returns a sentinel value.
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero64<JST>(code, from, gpr_scratch.cvt64());
|
||||||
|
}
|
||||||
|
// First time is to set flags
|
||||||
|
if (round_towards_zero) {
|
||||||
|
code->cvttsd2si(gpr_scratch, from); // 32 bit gpr
|
||||||
|
} else {
|
||||||
|
code->cvtsd2si(gpr_scratch, from); // 32 bit gpr
|
||||||
|
}
|
||||||
|
// Clamp to output range
|
||||||
|
ZeroIfNaN64(code, from, xmm_scratch);
|
||||||
|
code->minsd(from, code->MConst(f64_max_s32));
|
||||||
|
code->maxsd(from, code->MConst(f64_min_s32));
|
||||||
|
// Second time is for real
|
||||||
|
if (round_towards_zero) {
|
||||||
|
code->cvttsd2si(to, from); // 32 bit gpr
|
||||||
|
} else {
|
||||||
|
code->cvtsd2si(to, from); // 32 bit gpr
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPDoubleToU32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Xmm from = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Reg32 to = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Xmm xmm_scratch = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Reg32 gpr_scratch = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
bool round_towards_zero = args[1].GetImmediateU1();
|
||||||
|
|
||||||
|
// ARM saturates on conversion; this differs from x64 which returns a sentinel value.
|
||||||
|
// TODO: Use VCVTPD2UDQ when AVX512VL is available.
|
||||||
|
// FIXME: Inexact exception not correctly signalled with the below code
|
||||||
|
|
||||||
|
if (!ctx.FPSCR_RoundTowardsZero() && !round_towards_zero) {
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero64<JST>(code, from, gpr_scratch.cvt64());
|
||||||
|
}
|
||||||
|
ZeroIfNaN64(code, from, xmm_scratch);
|
||||||
|
// Bring into SSE range
|
||||||
|
code->addsd(from, code->MConst(f64_min_s32));
|
||||||
|
// First time is to set flags
|
||||||
|
code->cvtsd2si(gpr_scratch, from); // 32 bit gpr
|
||||||
|
// Clamp to output range
|
||||||
|
code->minsd(from, code->MConst(f64_max_s32));
|
||||||
|
code->maxsd(from, code->MConst(f64_min_s32));
|
||||||
|
// Actually convert
|
||||||
|
code->cvtsd2si(to, from); // 32 bit gpr
|
||||||
|
// Bring back into original range
|
||||||
|
code->add(to, u32(2147483648u));
|
||||||
|
} else {
|
||||||
|
Xbyak::Xmm xmm_mask = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Reg32 gpr_mask = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
if (ctx.FPSCR_FTZ()) {
|
||||||
|
DenormalsAreZero64<JST>(code, from, gpr_scratch.cvt64());
|
||||||
|
}
|
||||||
|
ZeroIfNaN64(code, from, xmm_scratch);
|
||||||
|
// Generate masks if out-of-signed-range
|
||||||
|
code->movaps(xmm_mask, code->MConst(f64_max_s32));
|
||||||
|
code->cmpltsd(xmm_mask, from);
|
||||||
|
code->movd(gpr_mask, xmm_mask);
|
||||||
|
code->pand(xmm_mask, code->MConst(f64_min_s32));
|
||||||
|
code->and_(gpr_mask, u32(2147483648u));
|
||||||
|
// Bring into range if necessary
|
||||||
|
code->addsd(from, xmm_mask);
|
||||||
|
// First time is to set flags
|
||||||
|
code->cvttsd2si(gpr_scratch, from); // 32 bit gpr
|
||||||
|
// Clamp to output range
|
||||||
|
code->minsd(from, code->MConst(f64_max_s32));
|
||||||
|
code->maxsd(from, code->MConst(f64_min_u32));
|
||||||
|
// Actually convert
|
||||||
|
code->cvttsd2si(to, from); // 32 bit gpr
|
||||||
|
// Bring back into original range if necessary
|
||||||
|
code->add(to, gpr_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPS32ToSingle(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Reg32 from = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Xmm to = ctx.reg_alloc.ScratchXmm();
|
||||||
|
bool round_to_nearest = args[1].GetImmediateU1();
|
||||||
|
ASSERT_MSG(!round_to_nearest, "round_to_nearest unimplemented");
|
||||||
|
|
||||||
|
code->cvtsi2ss(to, from);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPU32ToSingle(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Reg64 from = ctx.reg_alloc.UseGpr(args[0]);
|
||||||
|
Xbyak::Xmm to = ctx.reg_alloc.ScratchXmm();
|
||||||
|
bool round_to_nearest = args[1].GetImmediateU1();
|
||||||
|
ASSERT_MSG(!round_to_nearest, "round_to_nearest unimplemented");
|
||||||
|
|
||||||
|
// We are using a 64-bit GPR register to ensure we don't end up treating the input as signed
|
||||||
|
code->mov(from.cvt32(), from.cvt32()); // TODO: Verify if this is necessary
|
||||||
|
code->cvtsi2ss(to, from);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPS32ToDouble(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Reg32 from = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Xmm to = ctx.reg_alloc.ScratchXmm();
|
||||||
|
bool round_to_nearest = args[1].GetImmediateU1();
|
||||||
|
ASSERT_MSG(!round_to_nearest, "round_to_nearest unimplemented");
|
||||||
|
|
||||||
|
code->cvtsi2sd(to, from);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitFPU32ToDouble(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
Xbyak::Reg64 from = ctx.reg_alloc.UseGpr(args[0]);
|
||||||
|
Xbyak::Xmm to = ctx.reg_alloc.ScratchXmm();
|
||||||
|
bool round_to_nearest = args[1].GetImmediateU1();
|
||||||
|
ASSERT_MSG(!round_to_nearest, "round_to_nearest unimplemented");
|
||||||
|
|
||||||
|
// We are using a 64-bit GPR register to ensure we don't end up treating the input as signed
|
||||||
|
code->mov(from.cvt32(), from.cvt32()); // TODO: Verify if this is necessary
|
||||||
|
code->cvtsi2sd(to, from);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace BackendX64
|
||||||
|
} // namespace Dynarmic
|
||||||
|
|
||||||
|
#include "backend_x64/a32_jitstate.h"
|
||||||
|
#include "backend_x64/a64_jitstate.h"
|
||||||
|
|
||||||
|
template class Dynarmic::BackendX64::EmitX64<Dynarmic::BackendX64::A32JitState>;
|
||||||
|
template class Dynarmic::BackendX64::EmitX64<Dynarmic::BackendX64::A64JitState>;
|
734
src/backend_x64/emit_x64_packed.cpp
Normal file
734
src/backend_x64/emit_x64_packed.cpp
Normal file
|
@ -0,0 +1,734 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2016 MerryMage
|
||||||
|
* This software may be used and distributed according to the terms of the GNU
|
||||||
|
* General Public License version 2 or any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "backend_x64/block_of_code.h"
|
||||||
|
#include "backend_x64/emit_x64.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/ir/microinstruction.h"
|
||||||
|
#include "frontend/ir/opcodes.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace BackendX64 {
|
||||||
|
|
||||||
|
using namespace Xbyak::util;
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedAddU8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto ge_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetGEFromOp);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
code->paddb(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
if (ge_inst) {
|
||||||
|
Xbyak::Xmm xmm_ge = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Xmm ones = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
code->pcmpeqb(ones, ones);
|
||||||
|
|
||||||
|
code->movdqa(xmm_ge, xmm_a);
|
||||||
|
code->pminub(xmm_ge, xmm_b);
|
||||||
|
code->pcmpeqb(xmm_ge, xmm_b);
|
||||||
|
code->pxor(xmm_ge, ones);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, xmm_ge);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedAddS8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto ge_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetGEFromOp);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
if (ge_inst) {
|
||||||
|
Xbyak::Xmm saturated_sum = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Xmm xmm_ge = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
code->pxor(xmm_ge, xmm_ge);
|
||||||
|
code->movdqa(saturated_sum, xmm_a);
|
||||||
|
code->paddsb(saturated_sum, xmm_b);
|
||||||
|
code->pcmpgtb(xmm_ge, saturated_sum);
|
||||||
|
code->pcmpeqb(saturated_sum, saturated_sum);
|
||||||
|
code->pxor(xmm_ge, saturated_sum);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, xmm_ge);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
code->paddb(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedAddU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto ge_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetGEFromOp);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
code->paddw(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
if (ge_inst) {
|
||||||
|
if (code->DoesCpuSupport(Xbyak::util::Cpu::tSSE41)) {
|
||||||
|
Xbyak::Xmm xmm_ge = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Xmm ones = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
code->pcmpeqb(ones, ones);
|
||||||
|
|
||||||
|
code->movdqa(xmm_ge, xmm_a);
|
||||||
|
code->pminuw(xmm_ge, xmm_b);
|
||||||
|
code->pcmpeqw(xmm_ge, xmm_b);
|
||||||
|
code->pxor(xmm_ge, ones);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, xmm_ge);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
} else {
|
||||||
|
Xbyak::Xmm tmp_a = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Xmm tmp_b = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
// !(b <= a+b) == b > a+b
|
||||||
|
code->movdqa(tmp_a, xmm_a);
|
||||||
|
code->movdqa(tmp_b, xmm_b);
|
||||||
|
code->paddw(tmp_a, code->MConst(0x80008000));
|
||||||
|
code->paddw(tmp_b, code->MConst(0x80008000));
|
||||||
|
code->pcmpgtw(tmp_b, tmp_a); // *Signed* comparison!
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, tmp_b);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedAddS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto ge_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetGEFromOp);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
if (ge_inst) {
|
||||||
|
Xbyak::Xmm saturated_sum = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Xmm xmm_ge = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
code->pxor(xmm_ge, xmm_ge);
|
||||||
|
code->movdqa(saturated_sum, xmm_a);
|
||||||
|
code->paddsw(saturated_sum, xmm_b);
|
||||||
|
code->pcmpgtw(xmm_ge, saturated_sum);
|
||||||
|
code->pcmpeqw(saturated_sum, saturated_sum);
|
||||||
|
code->pxor(xmm_ge, saturated_sum);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, xmm_ge);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
code->paddw(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSubU8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto ge_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetGEFromOp);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
if (ge_inst) {
|
||||||
|
Xbyak::Xmm xmm_ge = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
code->movdqa(xmm_ge, xmm_a);
|
||||||
|
code->pmaxub(xmm_ge, xmm_b);
|
||||||
|
code->pcmpeqb(xmm_ge, xmm_a);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, xmm_ge);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
code->psubb(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSubS8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto ge_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetGEFromOp);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
if (ge_inst) {
|
||||||
|
Xbyak::Xmm saturated_sum = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Xmm xmm_ge = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
code->pxor(xmm_ge, xmm_ge);
|
||||||
|
code->movdqa(saturated_sum, xmm_a);
|
||||||
|
code->psubsb(saturated_sum, xmm_b);
|
||||||
|
code->pcmpgtb(xmm_ge, saturated_sum);
|
||||||
|
code->pcmpeqb(saturated_sum, saturated_sum);
|
||||||
|
code->pxor(xmm_ge, saturated_sum);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, xmm_ge);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
code->psubb(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSubU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto ge_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetGEFromOp);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
if (ge_inst) {
|
||||||
|
if (code->DoesCpuSupport(Xbyak::util::Cpu::tSSE41)) {
|
||||||
|
Xbyak::Xmm xmm_ge = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
code->movdqa(xmm_ge, xmm_a);
|
||||||
|
code->pmaxuw(xmm_ge, xmm_b); // Requires SSE 4.1
|
||||||
|
code->pcmpeqw(xmm_ge, xmm_a);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, xmm_ge);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
} else {
|
||||||
|
Xbyak::Xmm xmm_ge = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Xmm ones = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
// (a >= b) == !(b > a)
|
||||||
|
code->pcmpeqb(ones, ones);
|
||||||
|
code->paddw(xmm_a, code->MConst(0x80008000));
|
||||||
|
code->paddw(xmm_b, code->MConst(0x80008000));
|
||||||
|
code->movdqa(xmm_ge, xmm_b);
|
||||||
|
code->pcmpgtw(xmm_ge, xmm_a); // *Signed* comparison!
|
||||||
|
code->pxor(xmm_ge, ones);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, xmm_ge);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code->psubw(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSubS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto ge_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetGEFromOp);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
if (ge_inst) {
|
||||||
|
Xbyak::Xmm saturated_diff = ctx.reg_alloc.ScratchXmm();
|
||||||
|
Xbyak::Xmm xmm_ge = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
code->pxor(xmm_ge, xmm_ge);
|
||||||
|
code->movdqa(saturated_diff, xmm_a);
|
||||||
|
code->psubsw(saturated_diff, xmm_b);
|
||||||
|
code->pcmpgtw(xmm_ge, saturated_diff);
|
||||||
|
code->pcmpeqw(saturated_diff, saturated_diff);
|
||||||
|
code->pxor(xmm_ge, saturated_diff);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, xmm_ge);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
code->psubw(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingAddU8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
if (args[0].IsInXmm() || args[1].IsInXmm()) {
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseScratchXmm(args[1]);
|
||||||
|
Xbyak::Xmm ones = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
// Since,
|
||||||
|
// pavg(a, b) == (a + b + 1) >> 1
|
||||||
|
// Therefore,
|
||||||
|
// ~pavg(~a, ~b) == (a + b) >> 1
|
||||||
|
|
||||||
|
code->pcmpeqb(ones, ones);
|
||||||
|
code->pxor(xmm_a, ones);
|
||||||
|
code->pxor(xmm_b, ones);
|
||||||
|
code->pavgb(xmm_a, xmm_b);
|
||||||
|
code->pxor(xmm_a, ones);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
} else {
|
||||||
|
Xbyak::Reg32 reg_a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 reg_b = ctx.reg_alloc.UseGpr(args[1]).cvt32();
|
||||||
|
Xbyak::Reg32 xor_a_b = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Reg32 and_a_b = reg_a;
|
||||||
|
Xbyak::Reg32 result = reg_a;
|
||||||
|
|
||||||
|
// This relies on the equality x+y == ((x&y) << 1) + (x^y).
|
||||||
|
// Note that x^y always contains the LSB of the result.
|
||||||
|
// Since we want to calculate (x+y)/2, we can instead calculate (x&y) + ((x^y)>>1).
|
||||||
|
// We mask by 0x7F to remove the LSB so that it doesn't leak into the field below.
|
||||||
|
|
||||||
|
code->mov(xor_a_b, reg_a);
|
||||||
|
code->and_(and_a_b, reg_b);
|
||||||
|
code->xor_(xor_a_b, reg_b);
|
||||||
|
code->shr(xor_a_b, 1);
|
||||||
|
code->and_(xor_a_b, 0x7F7F7F7F);
|
||||||
|
code->add(result, xor_a_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingAddU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
if (args[0].IsInXmm() || args[1].IsInXmm()) {
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
code->movdqa(tmp, xmm_a);
|
||||||
|
code->pand(xmm_a, xmm_b);
|
||||||
|
code->pxor(tmp, xmm_b);
|
||||||
|
code->psrlw(tmp, 1);
|
||||||
|
code->paddw(xmm_a, tmp);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
} else {
|
||||||
|
Xbyak::Reg32 reg_a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 reg_b = ctx.reg_alloc.UseGpr(args[1]).cvt32();
|
||||||
|
Xbyak::Reg32 xor_a_b = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Reg32 and_a_b = reg_a;
|
||||||
|
Xbyak::Reg32 result = reg_a;
|
||||||
|
|
||||||
|
// This relies on the equality x+y == ((x&y) << 1) + (x^y).
|
||||||
|
// Note that x^y always contains the LSB of the result.
|
||||||
|
// Since we want to calculate (x+y)/2, we can instead calculate (x&y) + ((x^y)>>1).
|
||||||
|
// We mask by 0x7FFF to remove the LSB so that it doesn't leak into the field below.
|
||||||
|
|
||||||
|
code->mov(xor_a_b, reg_a);
|
||||||
|
code->and_(and_a_b, reg_b);
|
||||||
|
code->xor_(xor_a_b, reg_b);
|
||||||
|
code->shr(xor_a_b, 1);
|
||||||
|
code->and_(xor_a_b, 0x7FFF7FFF);
|
||||||
|
code->add(result, xor_a_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingAddS8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Reg32 reg_a = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 reg_b = ctx.reg_alloc.UseGpr(args[1]).cvt32();
|
||||||
|
Xbyak::Reg32 xor_a_b = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Reg32 and_a_b = reg_a;
|
||||||
|
Xbyak::Reg32 result = reg_a;
|
||||||
|
Xbyak::Reg32 carry = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
// This relies on the equality x+y == ((x&y) << 1) + (x^y).
|
||||||
|
// Note that x^y always contains the LSB of the result.
|
||||||
|
// Since we want to calculate (x+y)/2, we can instead calculate (x&y) + ((x^y)>>1).
|
||||||
|
// We mask by 0x7F to remove the LSB so that it doesn't leak into the field below.
|
||||||
|
// carry propagates the sign bit from (x^y)>>1 upwards by one.
|
||||||
|
|
||||||
|
code->mov(xor_a_b, reg_a);
|
||||||
|
code->and_(and_a_b, reg_b);
|
||||||
|
code->xor_(xor_a_b, reg_b);
|
||||||
|
code->mov(carry, xor_a_b);
|
||||||
|
code->and_(carry, 0x80808080);
|
||||||
|
code->shr(xor_a_b, 1);
|
||||||
|
code->and_(xor_a_b, 0x7F7F7F7F);
|
||||||
|
code->add(result, xor_a_b);
|
||||||
|
code->xor_(result, carry);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingAddS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm();
|
||||||
|
|
||||||
|
// This relies on the equality x+y == ((x&y) << 1) + (x^y).
|
||||||
|
// Note that x^y always contains the LSB of the result.
|
||||||
|
// Since we want to calculate (x+y)/2, we can instead calculate (x&y) + ((x^y)>>>1).
|
||||||
|
// The arithmetic shift right makes this signed.
|
||||||
|
|
||||||
|
code->movdqa(tmp, xmm_a);
|
||||||
|
code->pand(xmm_a, xmm_b);
|
||||||
|
code->pxor(tmp, xmm_b);
|
||||||
|
code->psraw(tmp, 1);
|
||||||
|
code->paddw(xmm_a, tmp);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingSubU8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Reg32 minuend = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 subtrahend = ctx.reg_alloc.UseScratchGpr(args[1]).cvt32();
|
||||||
|
|
||||||
|
// This relies on the equality x-y == (x^y) - (((x^y)&y) << 1).
|
||||||
|
// Note that x^y always contains the LSB of the result.
|
||||||
|
// Since we want to calculate (x+y)/2, we can instead calculate ((x^y)>>1) - ((x^y)&y).
|
||||||
|
|
||||||
|
code->xor_(minuend, subtrahend);
|
||||||
|
code->and_(subtrahend, minuend);
|
||||||
|
code->shr(minuend, 1);
|
||||||
|
|
||||||
|
// At this point,
|
||||||
|
// minuend := (a^b) >> 1
|
||||||
|
// subtrahend := (a^b) & b
|
||||||
|
|
||||||
|
// We must now perform a partitioned subtraction.
|
||||||
|
// We can do this because minuend contains 7 bit fields.
|
||||||
|
// We use the extra bit in minuend as a bit to borrow from; we set this bit.
|
||||||
|
// We invert this bit at the end as this tells us if that bit was borrowed from.
|
||||||
|
code->or_(minuend, 0x80808080);
|
||||||
|
code->sub(minuend, subtrahend);
|
||||||
|
code->xor_(minuend, 0x80808080);
|
||||||
|
|
||||||
|
// minuend now contains the desired result.
|
||||||
|
ctx.reg_alloc.DefineValue(inst, minuend);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingSubS8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Reg32 minuend = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 subtrahend = ctx.reg_alloc.UseScratchGpr(args[1]).cvt32();
|
||||||
|
|
||||||
|
Xbyak::Reg32 carry = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
// This relies on the equality x-y == (x^y) - (((x^y)&y) << 1).
|
||||||
|
// Note that x^y always contains the LSB of the result.
|
||||||
|
// Since we want to calculate (x-y)/2, we can instead calculate ((x^y)>>1) - ((x^y)&y).
|
||||||
|
|
||||||
|
code->xor_(minuend, subtrahend);
|
||||||
|
code->and_(subtrahend, minuend);
|
||||||
|
code->mov(carry, minuend);
|
||||||
|
code->and_(carry, 0x80808080);
|
||||||
|
code->shr(minuend, 1);
|
||||||
|
|
||||||
|
// At this point,
|
||||||
|
// minuend := (a^b) >> 1
|
||||||
|
// subtrahend := (a^b) & b
|
||||||
|
// carry := (a^b) & 0x80808080
|
||||||
|
|
||||||
|
// We must now perform a partitioned subtraction.
|
||||||
|
// We can do this because minuend contains 7 bit fields.
|
||||||
|
// We use the extra bit in minuend as a bit to borrow from; we set this bit.
|
||||||
|
// We invert this bit at the end as this tells us if that bit was borrowed from.
|
||||||
|
// We then sign extend the result into this bit.
|
||||||
|
code->or_(minuend, 0x80808080);
|
||||||
|
code->sub(minuend, subtrahend);
|
||||||
|
code->xor_(minuend, 0x80808080);
|
||||||
|
code->xor_(minuend, carry);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, minuend);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingSubU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Xmm minuend = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm subtrahend = ctx.reg_alloc.UseScratchXmm(args[1]);
|
||||||
|
|
||||||
|
// This relies on the equality x-y == (x^y) - (((x^y)&y) << 1).
|
||||||
|
// Note that x^y always contains the LSB of the result.
|
||||||
|
// Since we want to calculate (x-y)/2, we can instead calculate ((x^y)>>1) - ((x^y)&y).
|
||||||
|
|
||||||
|
code->pxor(minuend, subtrahend);
|
||||||
|
code->pand(subtrahend, minuend);
|
||||||
|
code->psrlw(minuend, 1);
|
||||||
|
|
||||||
|
// At this point,
|
||||||
|
// minuend := (a^b) >> 1
|
||||||
|
// subtrahend := (a^b) & b
|
||||||
|
|
||||||
|
code->psubw(minuend, subtrahend);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, minuend);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingSubS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Xmm minuend = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm subtrahend = ctx.reg_alloc.UseScratchXmm(args[1]);
|
||||||
|
|
||||||
|
// This relies on the equality x-y == (x^y) - (((x^y)&y) << 1).
|
||||||
|
// Note that x^y always contains the LSB of the result.
|
||||||
|
// Since we want to calculate (x-y)/2, we can instead calculate ((x^y)>>>1) - ((x^y)&y).
|
||||||
|
|
||||||
|
code->pxor(minuend, subtrahend);
|
||||||
|
code->pand(subtrahend, minuend);
|
||||||
|
code->psraw(minuend, 1);
|
||||||
|
|
||||||
|
// At this point,
|
||||||
|
// minuend := (a^b) >>> 1
|
||||||
|
// subtrahend := (a^b) & b
|
||||||
|
|
||||||
|
code->psubw(minuend, subtrahend);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, minuend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitPackedSubAdd(BlockOfCode* code, EmitContext& ctx, IR::Inst* inst, bool hi_is_sum, bool is_signed, bool is_halving) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
auto ge_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetGEFromOp);
|
||||||
|
|
||||||
|
Xbyak::Reg32 reg_a_hi = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 reg_b_hi = ctx.reg_alloc.UseScratchGpr(args[1]).cvt32();
|
||||||
|
Xbyak::Reg32 reg_a_lo = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Reg32 reg_b_lo = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Reg32 reg_sum, reg_diff;
|
||||||
|
|
||||||
|
if (is_signed) {
|
||||||
|
code->movsx(reg_a_lo, reg_a_hi.cvt16());
|
||||||
|
code->movsx(reg_b_lo, reg_b_hi.cvt16());
|
||||||
|
code->sar(reg_a_hi, 16);
|
||||||
|
code->sar(reg_b_hi, 16);
|
||||||
|
} else {
|
||||||
|
code->movzx(reg_a_lo, reg_a_hi.cvt16());
|
||||||
|
code->movzx(reg_b_lo, reg_b_hi.cvt16());
|
||||||
|
code->shr(reg_a_hi, 16);
|
||||||
|
code->shr(reg_b_hi, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hi_is_sum) {
|
||||||
|
code->sub(reg_a_lo, reg_b_hi);
|
||||||
|
code->add(reg_a_hi, reg_b_lo);
|
||||||
|
reg_diff = reg_a_lo;
|
||||||
|
reg_sum = reg_a_hi;
|
||||||
|
} else {
|
||||||
|
code->add(reg_a_lo, reg_b_hi);
|
||||||
|
code->sub(reg_a_hi, reg_b_lo);
|
||||||
|
reg_diff = reg_a_hi;
|
||||||
|
reg_sum = reg_a_lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ge_inst) {
|
||||||
|
// The reg_b registers are no longer required.
|
||||||
|
Xbyak::Reg32 ge_sum = reg_b_hi;
|
||||||
|
Xbyak::Reg32 ge_diff = reg_b_lo;
|
||||||
|
|
||||||
|
code->mov(ge_sum, reg_sum);
|
||||||
|
code->mov(ge_diff, reg_diff);
|
||||||
|
|
||||||
|
if (!is_signed) {
|
||||||
|
code->shl(ge_sum, 15);
|
||||||
|
code->sar(ge_sum, 31);
|
||||||
|
} else {
|
||||||
|
code->not_(ge_sum);
|
||||||
|
code->sar(ge_sum, 31);
|
||||||
|
}
|
||||||
|
code->not_(ge_diff);
|
||||||
|
code->sar(ge_diff, 31);
|
||||||
|
code->and_(ge_sum, hi_is_sum ? 0xFFFF0000 : 0x0000FFFF);
|
||||||
|
code->and_(ge_diff, hi_is_sum ? 0x0000FFFF : 0xFFFF0000);
|
||||||
|
code->or_(ge_sum, ge_diff);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(ge_inst, ge_sum);
|
||||||
|
ctx.EraseInstruction(ge_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_halving) {
|
||||||
|
code->shl(reg_a_lo, 15);
|
||||||
|
code->shr(reg_a_hi, 1);
|
||||||
|
} else {
|
||||||
|
code->shl(reg_a_lo, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reg_a_lo now contains the low word and reg_a_hi now contains the high word.
|
||||||
|
// Merge them.
|
||||||
|
code->shld(reg_a_hi, reg_a_lo, 16);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, reg_a_hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedAddSubU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedSubAdd(code, ctx, inst, true, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedAddSubS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedSubAdd(code, ctx, inst, true, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSubAddU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedSubAdd(code, ctx, inst, false, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSubAddS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedSubAdd(code, ctx, inst, false, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingAddSubU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedSubAdd(code, ctx, inst, true, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingAddSubS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedSubAdd(code, ctx, inst, true, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingSubAddU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedSubAdd(code, ctx, inst, false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedHalvingSubAddS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedSubAdd(code, ctx, inst, false, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EmitPackedOperation(BlockOfCode* code, EmitContext& ctx, IR::Inst* inst, void (Xbyak::CodeGenerator::*fn)(const Xbyak::Mmx& mmx, const Xbyak::Operand&)) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
(code->*fn)(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSaturatedAddU8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedOperation(code, ctx, inst, &Xbyak::CodeGenerator::paddusb);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSaturatedAddS8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedOperation(code, ctx, inst, &Xbyak::CodeGenerator::paddsb);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSaturatedSubU8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedOperation(code, ctx, inst, &Xbyak::CodeGenerator::psubusb);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSaturatedSubS8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedOperation(code, ctx, inst, &Xbyak::CodeGenerator::psubsb);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSaturatedAddU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedOperation(code, ctx, inst, &Xbyak::CodeGenerator::paddusw);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSaturatedAddS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedOperation(code, ctx, inst, &Xbyak::CodeGenerator::paddsw);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSaturatedSubU16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedOperation(code, ctx, inst, &Xbyak::CodeGenerator::psubusw);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSaturatedSubS16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedOperation(code, ctx, inst, &Xbyak::CodeGenerator::psubsw);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedAbsDiffSumS8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitPackedOperation(code, ctx, inst, &Xbyak::CodeGenerator::psadbw);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitPackedSelect(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
size_t num_args_in_xmm = args[0].IsInXmm() + args[1].IsInXmm() + args[2].IsInXmm();
|
||||||
|
|
||||||
|
if (num_args_in_xmm >= 2) {
|
||||||
|
Xbyak::Xmm ge = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm to = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
Xbyak::Xmm from = ctx.reg_alloc.UseScratchXmm(args[2]);
|
||||||
|
|
||||||
|
code->pand(from, ge);
|
||||||
|
code->pandn(ge, to);
|
||||||
|
code->por(from, ge);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, from);
|
||||||
|
} else if (code->DoesCpuSupport(Xbyak::util::Cpu::tBMI1)) {
|
||||||
|
Xbyak::Reg32 ge = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 to = ctx.reg_alloc.UseScratchGpr(args[1]).cvt32();
|
||||||
|
Xbyak::Reg32 from = ctx.reg_alloc.UseScratchGpr(args[2]).cvt32();
|
||||||
|
|
||||||
|
code->and_(from, ge);
|
||||||
|
code->andn(to, ge, to);
|
||||||
|
code->or_(from, to);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, from);
|
||||||
|
} else {
|
||||||
|
Xbyak::Reg32 ge = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 to = ctx.reg_alloc.UseGpr(args[1]).cvt32();
|
||||||
|
Xbyak::Reg32 from = ctx.reg_alloc.UseScratchGpr(args[2]).cvt32();
|
||||||
|
|
||||||
|
code->and_(from, ge);
|
||||||
|
code->not_(ge);
|
||||||
|
code->and_(ge, to);
|
||||||
|
code->or_(from, ge);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace BackendX64
|
||||||
|
} // namespace Dynarmic
|
||||||
|
|
||||||
|
#include "backend_x64/a32_jitstate.h"
|
||||||
|
#include "backend_x64/a64_jitstate.h"
|
||||||
|
|
||||||
|
template class Dynarmic::BackendX64::EmitX64<Dynarmic::BackendX64::A32JitState>;
|
||||||
|
template class Dynarmic::BackendX64::EmitX64<Dynarmic::BackendX64::A64JitState>;
|
163
src/backend_x64/emit_x64_saturation.cpp
Normal file
163
src/backend_x64/emit_x64_saturation.cpp
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2016 MerryMage
|
||||||
|
* This software may be used and distributed according to the terms of the GNU
|
||||||
|
* General Public License version 2 or any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "backend_x64/block_of_code.h"
|
||||||
|
#include "backend_x64/emit_x64.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/bit_util.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/ir/microinstruction.h"
|
||||||
|
#include "frontend/ir/opcodes.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace BackendX64 {
|
||||||
|
|
||||||
|
using namespace Xbyak::util;
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitSignedSaturatedAdd(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp);
|
||||||
|
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 addend = ctx.reg_alloc.UseGpr(args[1]).cvt32();
|
||||||
|
Xbyak::Reg32 overflow = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
code->mov(overflow, result);
|
||||||
|
code->shr(overflow, 31);
|
||||||
|
code->add(overflow, 0x7FFFFFFF);
|
||||||
|
// overflow now contains 0x7FFFFFFF if a was positive, or 0x80000000 if a was negative
|
||||||
|
code->add(result, addend);
|
||||||
|
code->cmovo(result, overflow);
|
||||||
|
|
||||||
|
if (overflow_inst) {
|
||||||
|
code->seto(overflow.cvt8());
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(overflow_inst, overflow);
|
||||||
|
ctx.EraseInstruction(overflow_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitSignedSaturatedSub(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp);
|
||||||
|
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 subend = ctx.reg_alloc.UseGpr(args[1]).cvt32();
|
||||||
|
Xbyak::Reg32 overflow = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
code->mov(overflow, result);
|
||||||
|
code->shr(overflow, 31);
|
||||||
|
code->add(overflow, 0x7FFFFFFF);
|
||||||
|
// overflow now contains 0x7FFFFFFF if a was positive, or 0x80000000 if a was negative
|
||||||
|
code->sub(result, subend);
|
||||||
|
code->cmovo(result, overflow);
|
||||||
|
|
||||||
|
if (overflow_inst) {
|
||||||
|
code->seto(overflow.cvt8());
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(overflow_inst, overflow);
|
||||||
|
ctx.EraseInstruction(overflow_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitUnsignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp);
|
||||||
|
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
size_t N = args[1].GetImmediateU8();
|
||||||
|
ASSERT(N <= 31);
|
||||||
|
|
||||||
|
u32 saturated_value = (1u << N) - 1;
|
||||||
|
|
||||||
|
Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Reg32 reg_a = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 overflow = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
// Pseudocode: result = clamp(reg_a, 0, saturated_value);
|
||||||
|
code->xor_(overflow, overflow);
|
||||||
|
code->cmp(reg_a, saturated_value);
|
||||||
|
code->mov(result, saturated_value);
|
||||||
|
code->cmovle(result, overflow);
|
||||||
|
code->cmovbe(result, reg_a);
|
||||||
|
|
||||||
|
if (overflow_inst) {
|
||||||
|
code->seta(overflow.cvt8());
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(overflow_inst, overflow);
|
||||||
|
ctx.EraseInstruction(overflow_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitSignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto overflow_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp);
|
||||||
|
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
size_t N = args[1].GetImmediateU8();
|
||||||
|
ASSERT(N >= 1 && N <= 32);
|
||||||
|
|
||||||
|
if (N == 32) {
|
||||||
|
if (overflow_inst) {
|
||||||
|
auto no_overflow = IR::Value(false);
|
||||||
|
overflow_inst->ReplaceUsesWith(no_overflow);
|
||||||
|
}
|
||||||
|
ctx.reg_alloc.DefineValue(inst, args[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 mask = (1u << N) - 1;
|
||||||
|
u32 positive_saturated_value = (1u << (N - 1)) - 1;
|
||||||
|
u32 negative_saturated_value = 1u << (N - 1);
|
||||||
|
u32 sext_negative_satured_value = Common::SignExtend(N, negative_saturated_value);
|
||||||
|
|
||||||
|
Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Reg32 reg_a = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
||||||
|
Xbyak::Reg32 overflow = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
Xbyak::Reg32 tmp = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
|
|
||||||
|
// overflow now contains a value between 0 and mask if it was originally between {negative,positive}_saturated_value.
|
||||||
|
code->lea(overflow, code->ptr[reg_a.cvt64() + negative_saturated_value]);
|
||||||
|
|
||||||
|
// Put the appropriate saturated value in result
|
||||||
|
code->cmp(reg_a, positive_saturated_value);
|
||||||
|
code->mov(tmp, positive_saturated_value);
|
||||||
|
code->mov(result, sext_negative_satured_value);
|
||||||
|
code->cmovg(result, tmp);
|
||||||
|
|
||||||
|
// Do the saturation
|
||||||
|
code->cmp(overflow, mask);
|
||||||
|
code->cmovbe(result, reg_a);
|
||||||
|
|
||||||
|
if (overflow_inst) {
|
||||||
|
code->seta(overflow.cvt8());
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(overflow_inst, overflow);
|
||||||
|
ctx.EraseInstruction(overflow_inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace BackendX64
|
||||||
|
} // namespace Dynarmic
|
||||||
|
|
||||||
|
#include "backend_x64/a32_jitstate.h"
|
||||||
|
#include "backend_x64/a64_jitstate.h"
|
||||||
|
|
||||||
|
template class Dynarmic::BackendX64::EmitX64<Dynarmic::BackendX64::A32JitState>;
|
||||||
|
template class Dynarmic::BackendX64::EmitX64<Dynarmic::BackendX64::A64JitState>;
|
63
src/backend_x64/emit_x64_vector.cpp
Normal file
63
src/backend_x64/emit_x64_vector.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2016 MerryMage
|
||||||
|
* This software may be used and distributed according to the terms of the GNU
|
||||||
|
* General Public License version 2 or any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "backend_x64/block_of_code.h"
|
||||||
|
#include "backend_x64/emit_x64.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/ir/basic_block.h"
|
||||||
|
#include "frontend/ir/microinstruction.h"
|
||||||
|
#include "frontend/ir/opcodes.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
namespace BackendX64 {
|
||||||
|
|
||||||
|
using namespace Xbyak::util;
|
||||||
|
|
||||||
|
static void EmitVectorOperation(BlockOfCode* code, EmitContext& ctx, IR::Inst* inst, void (Xbyak::CodeGenerator::*fn)(const Xbyak::Mmx& mmx, const Xbyak::Operand&)) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
Xbyak::Xmm xmm_a = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||||
|
Xbyak::Xmm xmm_b = ctx.reg_alloc.UseXmm(args[1]);
|
||||||
|
|
||||||
|
(code->*fn)(xmm_a, xmm_b);
|
||||||
|
|
||||||
|
ctx.reg_alloc.DefineValue(inst, xmm_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitVectorAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitVectorOperation(code, ctx, inst, &Xbyak::CodeGenerator::paddb);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitVectorAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitVectorOperation(code, ctx, inst, &Xbyak::CodeGenerator::paddw);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitVectorAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitVectorOperation(code, ctx, inst, &Xbyak::CodeGenerator::paddd);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitVectorAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitVectorOperation(code, ctx, inst, &Xbyak::CodeGenerator::paddq);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename JST>
|
||||||
|
void EmitX64<JST>::EmitVectorAnd(EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
EmitVectorOperation(code, ctx, inst, &Xbyak::CodeGenerator::pand);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace BackendX64
|
||||||
|
} // namespace Dynarmic
|
||||||
|
|
||||||
|
#include "backend_x64/a32_jitstate.h"
|
||||||
|
#include "backend_x64/a64_jitstate.h"
|
||||||
|
|
||||||
|
template class Dynarmic::BackendX64::EmitX64<Dynarmic::BackendX64::A32JitState>;
|
||||||
|
template class Dynarmic::BackendX64::EmitX64<Dynarmic::BackendX64::A64JitState>;
|
Loading…
Reference in a new issue