/* ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2013 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/cpu/ppc/ppc_emit-private.h" #include "xenia/base/assert.h" #include "xenia/cpu/ppc/ppc_context.h" #include "xenia/cpu/ppc/ppc_hir_builder.h" #include namespace xe { namespace cpu { namespace ppc { // TODO(benvanik): remove when enums redefined. using namespace xe::cpu::hir; using xe::cpu::hir::Value; Value* CalculateEA_0(PPCHIRBuilder& f, uint32_t ra, uint32_t rb); #define SHUFPS_SWAP_DWORDS 0x1B // Most of this file comes from: // http://biallas.net/doc/vmx128/vmx128.txt // https://github.com/kakaroto/ps3ida/blob/master/plugins/PPCAltivec/src/main.cpp // https://sannybuilder.com/forums/viewtopic.php?id=190 #define OP(x) ((((uint32_t)(x)) & 0x3f) << 26) #define VX128(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x3d0)) #define VX128_1(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x7f3)) #define VX128_2(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x210)) #define VX128_3(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x7f0)) #define VX128_4(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x730)) #define VX128_5(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x10)) #define VX128_P(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x630)) #define VX128_VD128 (i.VX128.VD128l | (i.VX128.VD128h << 5)) #define VX128_VA128 \ (i.VX128.VA128l | (i.VX128.VA128h << 5) | (i.VX128.VA128H << 6)) #define VX128_VB128 (i.VX128.VB128l | (i.VX128.VB128h << 5)) #define VX128_1_VD128 (i.VX128_1.VD128l | (i.VX128_1.VD128h << 5)) #define VX128_2_VD128 (i.VX128_2.VD128l | (i.VX128_2.VD128h << 5)) #define VX128_2_VA128 \ (i.VX128_2.VA128l | (i.VX128_2.VA128h << 5) | (i.VX128_2.VA128H << 6)) #define VX128_2_VB128 (i.VX128_2.VB128l | (i.VX128_2.VB128h << 5)) #define VX128_2_VC (i.VX128_2.VC) #define VX128_3_VD128 (i.VX128_3.VD128l | (i.VX128_3.VD128h << 5)) #define VX128_3_VB128 (i.VX128_3.VB128l | (i.VX128_3.VB128h << 5)) #define VX128_3_IMM (i.VX128_3.IMM) #define VX128_5_VD128 (i.VX128_5.VD128l | (i.VX128_5.VD128h << 5)) #define VX128_5_VA128 \ (i.VX128_5.VA128l | (i.VX128_5.VA128h << 5)) | (i.VX128_5.VA128H << 6) #define VX128_5_VB128 (i.VX128_5.VB128l | (i.VX128_5.VB128h << 5)) #define VX128_5_SH (i.VX128_5.SH) #define VX128_R_VD128 (i.VX128_R.VD128l | (i.VX128_R.VD128h << 5)) #define VX128_R_VA128 \ (i.VX128_R.VA128l | (i.VX128_R.VA128h << 5) | (i.VX128_R.VA128H << 6)) #define VX128_R_VB128 (i.VX128_R.VB128l | (i.VX128_R.VB128h << 5)) unsigned int xerotl(unsigned int value, unsigned int shift) { assert_true(shift < 32); return shift == 0 ? value : ((value << shift) | (value >> (32 - shift))); } int InstrEmit_lvebx(PPCHIRBuilder& f, const InstrData& i) { // Same as lvx. Value* ea = f.And(CalculateEA_0(f, i.X.RA, i.X.RB), f.LoadConstantUint64(~0xFull)); f.StoreVR(i.X.RT, f.ByteSwap(f.Load(ea, VEC128_TYPE))); return 0; } int InstrEmit_lvehx(PPCHIRBuilder& f, const InstrData& i) { // Same as lvx. Value* ea = f.And(CalculateEA_0(f, i.X.RA, i.X.RB), f.LoadConstantUint64(~0xFull)); f.StoreVR(i.X.RT, f.ByteSwap(f.Load(ea, VEC128_TYPE))); return 0; } int InstrEmit_lvewx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { // Same as lvx. Value* ea = f.And(CalculateEA_0(f, ra, rb), f.LoadConstantUint64(~0xFull)); f.StoreVR(vd, f.ByteSwap(f.Load(ea, VEC128_TYPE))); return 0; } int InstrEmit_lvewx(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvewx_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_lvewx128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvewx_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_lvsl_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { Value* ea = CalculateEA_0(f, ra, rb); Value* sh = f.Truncate(f.And(ea, f.LoadConstantInt64(0xF)), INT8_TYPE); Value* v = f.LoadVectorShl(sh); f.StoreVR(vd, v); return 0; } int InstrEmit_lvsl(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvsl_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_lvsl128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvsl_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_lvsr_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { Value* ea = CalculateEA_0(f, ra, rb); Value* sh = f.Truncate(f.And(ea, f.LoadConstantInt64(0xF)), INT8_TYPE); Value* v = f.LoadVectorShr(sh); f.StoreVR(vd, v); return 0; } int InstrEmit_lvsr(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvsr_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_lvsr128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvsr_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_lvx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { Value* ea = f.And(CalculateEA_0(f, ra, rb), f.LoadConstantInt64(~0xFull)); f.StoreVR(vd, f.ByteSwap(f.Load(ea, VEC128_TYPE))); return 0; } int InstrEmit_lvx(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvx_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_lvx128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvx_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_lvxl(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvx(f, i); } int InstrEmit_lvxl128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvx128(f, i); } int InstrEmit_stvebx(PPCHIRBuilder& f, const InstrData& i) { Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB); Value* el = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantUint8(0xF)); Value* v = f.Extract(f.LoadVR(i.X.RT), el, INT8_TYPE); f.Store(ea, v); return 0; } int InstrEmit_stvehx(PPCHIRBuilder& f, const InstrData& i) { Value* ea = CalculateEA_0(f, i.X.RA, i.X.RB); ea = f.And(ea, f.LoadConstantUint64(~0x1ull)); Value* el = f.Shr(f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantUint8(0xF)), 1); Value* v = f.Extract(f.LoadVR(i.X.RT), el, INT16_TYPE); f.Store(ea, f.ByteSwap(v)); return 0; } int InstrEmit_stvewx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { Value* ea = CalculateEA_0(f, ra, rb); ea = f.And(ea, f.LoadConstantUint64(~0x3ull)); Value* el = f.Shr(f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantUint8(0xF)), 2); Value* v = f.Extract(f.LoadVR(vd), el, INT32_TYPE); f.Store(ea, f.ByteSwap(v)); return 0; } int InstrEmit_stvewx(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvewx_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_stvewx128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvewx_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_stvx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { Value* ea = f.And(CalculateEA_0(f, ra, rb), f.LoadConstantUint64(~0xFull)); f.Store(ea, f.ByteSwap(f.LoadVR(vd))); return 0; } int InstrEmit_stvx(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvx_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_stvx128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvx_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_stvxl(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvx(f, i); } int InstrEmit_stvxl128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvx128(f, i); } // The lvlx/lvrx/etc instructions are in Cell docs only: // https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/C40E4C6133B31EE8872570B500791108/$file/vector_simd_pem_v_2.07c_26Oct2006_cell.pdf int InstrEmit_lvlx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { Value* ea = CalculateEA_0(f, ra, rb); Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF)); // ea &= ~0xF ea = f.And(ea, f.LoadConstantUint64(~0xFull)); // v = (new << eb) Value* v = f.Permute(f.LoadVectorShl(eb), f.ByteSwap(f.Load(ea, VEC128_TYPE)), f.LoadZeroVec128(), INT8_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_lvlx(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvlx_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_lvlx128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvlx_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_lvlxl(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvlx(f, i); } int InstrEmit_lvlxl128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvlx128(f, i); } int InstrEmit_lvrx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { // NOTE: if eb == 0 (so 16b aligned) then no data is loaded. This is important // as often times memcpy's will use this to handle the remaining <=16b of a // buffer, which sometimes may be nothing and hang off the end of the valid // page area. We still need to zero the resulting register, though. Value* ea = CalculateEA_0(f, ra, rb); Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF)); // Skip if %16=0 (just load zero). auto load_label = f.NewLabel(); auto end_label = f.NewLabel(); f.BranchTrue(eb, load_label); f.StoreVR(vd, f.LoadZeroVec128()); f.Branch(end_label); f.MarkLabel(load_label); // ea &= ~0xF // NOTE: need to recalculate ea and eb because after Branch we start a new // block and we can't use their previous instantiation in the new block ea = CalculateEA_0(f, ra, rb); eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF)); ea = f.And(ea, f.LoadConstantUint64(~0xFull)); // v = (new >> (16 - eb)) Value* v = f.Permute(f.LoadVectorShl(eb), f.LoadZeroVec128(), f.ByteSwap(f.Load(ea, VEC128_TYPE)), INT8_TYPE); f.StoreVR(vd, v); f.MarkLabel(end_label); return 0; } int InstrEmit_lvrx(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvrx_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_lvrx128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvrx_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_lvrxl(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvrx(f, i); } int InstrEmit_lvrxl128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_lvrx128(f, i); } int InstrEmit_stvlx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { // NOTE: if eb == 0 (so 16b aligned) this equals new_value // we could optimize this to prevent the other load/mask, in that case. Value* ea = CalculateEA_0(f, ra, rb); Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF)); // ea &= ~0xF ea = f.And(ea, f.LoadConstantUint64(~0xFull)); // v = (old & ~mask) | ((new >> eb) & mask) Value* new_value = f.Permute(f.LoadVectorShr(eb), f.LoadZeroVec128(), f.LoadVR(vd), INT8_TYPE); Value* old_value = f.ByteSwap(f.Load(ea, VEC128_TYPE)); // mask = FFFF... >> eb Value* mask = f.Permute(f.LoadVectorShr(eb), f.LoadZeroVec128(), f.Not(f.LoadZeroVec128()), INT8_TYPE); Value* v = f.Or(f.AndNot(old_value, mask), f.And(new_value, mask)); // ea &= ~0xF (handled above) f.Store(ea, f.ByteSwap(v)); return 0; } int InstrEmit_stvlx(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvlx_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_stvlx128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvlx_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_stvlxl(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvlx(f, i); } int InstrEmit_stvlxl128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvlx128(f, i); } int InstrEmit_stvrx_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t ra, uint32_t rb) { // NOTE: if eb == 0 (so 16b aligned) then no data is loaded. This is important // as often times memcpy's will use this to handle the remaining <=16b of a // buffer, which sometimes may be nothing and hang off the end of the valid // page area. Value* ea = CalculateEA_0(f, ra, rb); Value* eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF)); // Skip if %16=0 (no data to store). auto skip_label = f.NewLabel(); f.BranchFalse(eb, skip_label); // ea &= ~0xF // NOTE: need to recalculate ea and eb because after Branch we start a new // block and we can't use their previous instantiation in the new block ea = CalculateEA_0(f, ra, rb); eb = f.And(f.Truncate(ea, INT8_TYPE), f.LoadConstantInt8(0xF)); ea = f.And(ea, f.LoadConstantUint64(~0xFull)); // v = (old & ~mask) | ((new << eb) & mask) Value* new_value = f.Permute(f.LoadVectorShr(eb), f.LoadVR(vd), f.LoadZeroVec128(), INT8_TYPE); Value* old_value = f.ByteSwap(f.Load(ea, VEC128_TYPE)); // mask = ~FFFF... >> eb Value* mask = f.Permute(f.LoadVectorShr(eb), f.Not(f.LoadZeroVec128()), f.LoadZeroVec128(), INT8_TYPE); Value* v = f.Or(f.AndNot(old_value, mask), f.And(new_value, mask)); // ea &= ~0xF (handled above) f.Store(ea, f.ByteSwap(v)); f.MarkLabel(skip_label); return 0; } int InstrEmit_stvrx(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvrx_(f, i, i.X.RT, i.X.RA, i.X.RB); } int InstrEmit_stvrx128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvrx_(f, i, VX128_1_VD128, i.VX128_1.RA, i.VX128_1.RB); } int InstrEmit_stvrxl(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvrx(f, i); } int InstrEmit_stvrxl128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_stvrx128(f, i); } int InstrEmit_mfvscr(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_mtvscr(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vaddcuw(PPCHIRBuilder& f, const InstrData& i) { Value* sum = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_UNSIGNED); Value* overflow = f.VectorCompareUGT(f.LoadVR(i.VX.VA), sum, INT32_TYPE); Value* carry = f.VectorShr(overflow, f.LoadConstantVec128(vec128i(31)), INT32_TYPE); f.StoreVR(i.VX.VD, carry); return 0; } int InstrEmit_vaddfp_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD) <- (VA) + (VB) (4 x fp) Value* v = f.VectorAdd(f.LoadVR(va), f.LoadVR(vb), FLOAT32_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vaddfp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vaddfp_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vaddfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vaddfp_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vaddsbs(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, ARITHMETIC_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vaddshs(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, ARITHMETIC_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vaddsws(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vaddubm(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vaddubs(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, ARITHMETIC_UNSIGNED | ARITHMETIC_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vadduhm(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vadduhs(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, ARITHMETIC_UNSIGNED | ARITHMETIC_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vadduwm(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vadduws(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAdd(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_UNSIGNED | ARITHMETIC_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vand_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // VD <- (VA) & (VB) Value* v = f.And(f.LoadVR(va), f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vand(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vand_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vand128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vand_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vandc_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // VD <- (VA) & ¬(VB) Value* v = f.AndNot(f.LoadVR(va), f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vandc(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vandc_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vandc128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vandc_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vavgsb(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAverage(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, 0); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vavgsh(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAverage(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, 0); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vavgsw(PPCHIRBuilder& f, const InstrData& i) { // do i = 0 to 127 by 32 // aop = EXTS((VRA)i:i + 31) // bop = EXTS((VRB)i:i + 31) // VRTi:i + 31 = Chop((aop + int bop + int 1) >> 1, 32) Value* v = f.VectorAverage(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, 0); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vavgub(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAverage(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vavguh(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAverage(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vavguw(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorAverage(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vcfsx_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb, uint32_t uimm) { // (VD) <- float(VB as signed) / 2^uimm Value* v = f.VectorConvertI2F(f.LoadVR(vb)); if (uimm) { float fuimm = std::ldexp(1.0f, -int(uimm)); v = f.Mul(v, f.Splat(f.LoadConstantFloat32(fuimm), VEC128_TYPE)); } f.StoreVR(vd, v); return 0; } int InstrEmit_vcfsx(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcfsx_(f, i.VX.VD, i.VX.VB, i.VX.VA); } int InstrEmit_vcsxwfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcfsx_(f, VX128_3_VD128, VX128_3_VB128, VX128_3_IMM); } int InstrEmit_vcfux_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb, uint32_t uimm) { // (VD) <- float(VB as unsigned) / 2^uimm Value* v = f.VectorConvertI2F(f.LoadVR(vb), ARITHMETIC_UNSIGNED); if (uimm) { float fuimm = std::ldexp(1.0f, -int(uimm)); v = f.Mul(v, f.Splat(f.LoadConstantFloat32(fuimm), VEC128_TYPE)); } f.StoreVR(vd, v); return 0; } int InstrEmit_vcfux(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcfux_(f, i.VX.VD, i.VX.VB, i.VX.VA); } int InstrEmit_vcuxwfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcfux_(f, VX128_3_VD128, VX128_3_VB128, VX128_3_IMM); } int InstrEmit_vctsxs_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb, uint32_t uimm) { // (VD) <- int_sat(VB as signed * 2^uimm) float fuimm = static_cast(std::exp2(uimm)); Value* v = f.Mul(f.LoadVR(vb), f.Splat(f.LoadConstantFloat32(fuimm), VEC128_TYPE)); v = f.VectorConvertF2I(v); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(vd, v); return 0; } int InstrEmit_vctsxs(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vctsxs_(f, i.VX.VD, i.VX.VB, i.VX.VA); } int InstrEmit_vcfpsxws128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vctsxs_(f, VX128_3_VD128, VX128_3_VB128, VX128_3_IMM); } int InstrEmit_vctuxs_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb, uint32_t uimm) { // (VD) <- int_sat(VB as unsigned * 2^uimm) float fuimm = static_cast(std::exp2(uimm)); Value* v = f.Mul(f.LoadVR(vb), f.Splat(f.LoadConstantFloat32(fuimm), VEC128_TYPE)); v = f.VectorConvertF2I(v, ARITHMETIC_UNSIGNED); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(vd, v); return 0; } int InstrEmit_vctuxs(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vctuxs_(f, i.VX.VD, i.VX.VB, i.VX.VA); } int InstrEmit_vcfpuxws128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vctuxs_(f, VX128_3_VD128, VX128_3_VB128, VX128_3_IMM); } int InstrEmit_vcmpbfp_(PPCHIRBuilder& f, const InstrData& i, uint32_t vd, uint32_t va, uint32_t vb, uint32_t rc) { // if vA or vB are NaN, the 2 high-order bits are set (0xC0000000) Value* va_value = f.LoadVR(va); Value* vb_value = f.LoadVR(vb); Value* gt = f.VectorCompareSGT(va_value, vb_value, FLOAT32_TYPE); Value* lt = f.Not(f.VectorCompareSGE(va_value, f.Neg(vb_value), FLOAT32_TYPE)); Value* v = f.Or(f.And(gt, f.LoadConstantVec128(vec128i(0x80000000, 0x80000000, 0x80000000, 0x80000000))), f.And(lt, f.LoadConstantVec128(vec128i(0x40000000, 0x40000000, 0x40000000, 0x40000000)))); f.StoreVR(vd, v); if (rc) { // CR0:4 = 0; CR0:5 = VT == 0; CR0:6 = CR0:7 = 0; // If all of the elements are within bounds, CR6[2] is set // FIXME: Does not affect CR6[0], but the following function does. f.UpdateCR6(f.Or(gt, lt)); } return 0; } int InstrEmit_vcmpbfp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpbfp_(f, i, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpbfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpbfp_(f, i, VX128_R_VD128, VX128_R_VA128, VX128_R_VB128, i.VX128_R.Rc); } enum vcmpxxfp_op { vcmpxxfp_eq, vcmpxxfp_gt, vcmpxxfp_ge, }; int InstrEmit_vcmpxxfp_(PPCHIRBuilder& f, const InstrData& i, vcmpxxfp_op cmpop, uint32_t vd, uint32_t va, uint32_t vb, uint32_t rc) { // (VD.xyzw) = (VA.xyzw) OP (VB.xyzw) ? 0xFFFFFFFF : 0x00000000 // if (Rc) CR6 = all_equal | 0 | none_equal | 0 // If an element in either VA or VB is NaN the result will be 0x00000000 Value* v; switch (cmpop) { case vcmpxxfp_eq: v = f.VectorCompareEQ(f.LoadVR(va), f.LoadVR(vb), FLOAT32_TYPE); break; case vcmpxxfp_gt: v = f.VectorCompareSGT(f.LoadVR(va), f.LoadVR(vb), FLOAT32_TYPE); break; case vcmpxxfp_ge: v = f.VectorCompareSGE(f.LoadVR(va), f.LoadVR(vb), FLOAT32_TYPE); break; default: assert_unhandled_case(cmpop); return 1; } if (rc) { f.UpdateCR6(v); } f.StoreVR(vd, v); return 0; } int InstrEmit_vcmpeqfp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxfp_(f, i, vcmpxxfp_eq, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpeqfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxfp_(f, i, vcmpxxfp_eq, VX128_R_VD128, VX128_R_VA128, VX128_R_VB128, i.VX128_R.Rc); } int InstrEmit_vcmpgefp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxfp_(f, i, vcmpxxfp_ge, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpgefp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxfp_(f, i, vcmpxxfp_ge, VX128_R_VD128, VX128_R_VA128, VX128_R_VB128, i.VX128_R.Rc); } int InstrEmit_vcmpgtfp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxfp_(f, i, vcmpxxfp_gt, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpgtfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxfp_(f, i, vcmpxxfp_gt, VX128_R_VD128, VX128_R_VA128, VX128_R_VB128, i.VX128_R.Rc); } enum vcmpxxi_op { vcmpxxi_eq, vcmpxxi_gt_signed, vcmpxxi_gt_unsigned, }; int InstrEmit_vcmpxxi_(PPCHIRBuilder& f, const InstrData& i, vcmpxxi_op cmpop, uint32_t width, uint32_t vd, uint32_t va, uint32_t vb, uint32_t rc) { // (VD.xyzw) = (VA.xyzw) OP (VB.xyzw) ? 0xFFFFFFFF : 0x00000000 // if (Rc) CR6 = all_equal | 0 | none_equal | 0 // If an element in either VA or VB is NaN the result will be 0x00000000 Value* v; switch (cmpop) { case vcmpxxi_eq: switch (width) { case 1: v = f.VectorCompareEQ(f.LoadVR(va), f.LoadVR(vb), INT8_TYPE); break; case 2: v = f.VectorCompareEQ(f.LoadVR(va), f.LoadVR(vb), INT16_TYPE); break; case 4: v = f.VectorCompareEQ(f.LoadVR(va), f.LoadVR(vb), INT32_TYPE); break; default: assert_unhandled_case(width); return 1; } break; case vcmpxxi_gt_signed: switch (width) { case 1: v = f.VectorCompareSGT(f.LoadVR(va), f.LoadVR(vb), INT8_TYPE); break; case 2: v = f.VectorCompareSGT(f.LoadVR(va), f.LoadVR(vb), INT16_TYPE); break; case 4: v = f.VectorCompareSGT(f.LoadVR(va), f.LoadVR(vb), INT32_TYPE); break; default: assert_unhandled_case(width); return 1; } break; case vcmpxxi_gt_unsigned: switch (width) { case 1: v = f.VectorCompareUGT(f.LoadVR(va), f.LoadVR(vb), INT8_TYPE); break; case 2: v = f.VectorCompareUGT(f.LoadVR(va), f.LoadVR(vb), INT16_TYPE); break; case 4: v = f.VectorCompareUGT(f.LoadVR(va), f.LoadVR(vb), INT32_TYPE); break; default: assert_unhandled_case(width); return 1; } break; default: assert_unhandled_case(cmpop); return 1; } if (rc) { f.UpdateCR6(v); } f.StoreVR(vd, v); return 0; } int InstrEmit_vcmpequb(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_eq, 1, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpequh(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_eq, 2, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpequw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_eq, 4, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpequw128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_eq, 4, VX128_R_VD128, VX128_R_VA128, VX128_R_VB128, i.VX128_R.Rc); } int InstrEmit_vcmpgtsb(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_gt_signed, 1, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpgtsh(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_gt_signed, 2, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpgtsw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_gt_signed, 4, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpgtub(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_gt_unsigned, 1, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpgtuh(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_gt_unsigned, 2, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vcmpgtuw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vcmpxxi_(f, i, vcmpxxi_gt_unsigned, 4, i.VXR.VD, i.VXR.VA, i.VXR.VB, i.VXR.Rc); } int InstrEmit_vexptefp_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // (VD) <- pow2(VB) Value* v = f.Pow2(f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vexptefp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vexptefp_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vexptefp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vexptefp_(f, VX128_3_VD128, VX128_3_VB128); } int InstrEmit_vlogefp_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // (VD) <- log2(VB) Value* v = f.Log2(f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vlogefp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vlogefp_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vlogefp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vlogefp_(f, VX128_3_VD128, VX128_3_VB128); } int InstrEmit_vmaddfp_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb, uint32_t vc) { // (VD) <- ((VA) * (VC)) + (VB) Value* v = f.MulAdd(f.LoadVR(va), f.LoadVR(vc), f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vmaddfp(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- ((VA) * (VC)) + (VB) return InstrEmit_vmaddfp_(f, i.VXA.VD, i.VXA.VA, i.VXA.VB, i.VXA.VC); } int InstrEmit_vmaddfp128(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- ((VA) * (VB)) + (VD) // NOTE: this resuses VD and swaps the arg order! return InstrEmit_vmaddfp_(f, VX128_VD128, VX128_VA128, VX128_VD128, VX128_VB128); } int InstrEmit_vmaddcfp128(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- ((VA) * (VD)) + (VB) Value* v = f.MulAdd(f.LoadVR(VX128_VA128), f.LoadVR(VX128_VD128), f.LoadVR(VX128_VB128)); f.StoreVR(VX128_VD128, v); return 0; } int InstrEmit_vmaxfp_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD) <- max((VA), (VB)) Value* v = f.Max(f.LoadVR(va), f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vmaxfp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vmaxfp_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vmaxfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vmaxfp_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vmaxsb(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- max((VA), (VB)) (signed int8) Value* v = f.VectorMax(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmaxsh(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- max((VA), (VB)) (signed int16) Value* v = f.VectorMax(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmaxsw(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- max((VA), (VB)) (signed int32) Value* v = f.VectorMax(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmaxub(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- max((VA), (VB)) (unsigned int8) Value* v = f.VectorMax(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmaxuh(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- max((VA), (VB)) (unsigned int16) Value* v = f.VectorMax(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmaxuw(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- max((VA), (VB)) (unsigned int32) Value* v = f.VectorMax(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmhaddshs(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmhraddshs(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vminfp_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD) <- min((VA), (VB)) Value* v = f.Min(f.LoadVR(va), f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vminfp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vminfp_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vminfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vminfp_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vminsb(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- min((VA), (VB)) (signed int8) Value* v = f.VectorMin(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vminsh(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- min((VA), (VB)) (signed int16) Value* v = f.VectorMin(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vminsw(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- min((VA), (VB)) (signed int32) Value* v = f.VectorMin(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vminub(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- min((VA), (VB)) (unsigned int8) Value* v = f.VectorMin(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vminuh(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- min((VA), (VB)) (unsigned int16) Value* v = f.VectorMin(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vminuw(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- min((VA), (VB)) (unsigned int32) Value* v = f.VectorMin(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmladduhm(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmrghb(PPCHIRBuilder& f, const InstrData& i) { // (VD.b[i]) = (VA.b[i]) // (VD.b[i+1]) = (VB.b[i+1]) // ... Value* v = f.Permute(f.LoadConstantVec128(vec128b(0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23)), f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmrghh(PPCHIRBuilder& f, const InstrData& i) { // (VD.w[i]) = (VA.w[i]) // (VD.w[i+1]) = (VB.w[i+1]) // ... Value* v = f.Permute(f.LoadConstantVec128(vec128s(0, 8, 1, 9, 2, 10, 3, 11)), f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmrghw_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD.x) = (VA.x) // (VD.y) = (VB.x) // (VD.z) = (VA.y) // (VD.w) = (VB.y) Value* v = f.Permute(f.LoadConstantUint32(MakePermuteMask(0, 0, 1, 0, 0, 1, 1, 1)), f.LoadVR(va), f.LoadVR(vb), INT32_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vmrghw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vmrghw_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vmrghw128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vmrghw_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vmrglb(PPCHIRBuilder& f, const InstrData& i) { // (VD.b[i]) = (VA.b[i]) // (VD.b[i+1]) = (VB.b[i+1]) // ... Value* v = f.Permute(f.LoadConstantVec128(vec128b(8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31)), f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmrglh(PPCHIRBuilder& f, const InstrData& i) { // (VD.w[i]) = (VA.w[i]) // (VD.w[i+1]) = (VB.w[i+1]) // ... Value* v = f.Permute(f.LoadConstantVec128(vec128s(4, 12, 5, 13, 6, 14, 7, 15)), f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vmrglw_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD.x) = (VA.z) // (VD.y) = (VB.z) // (VD.z) = (VA.w) // (VD.w) = (VB.w) Value* v = f.Permute(f.LoadConstantUint32(MakePermuteMask(0, 2, 1, 2, 0, 3, 1, 3)), f.LoadVR(va), f.LoadVR(vb), INT32_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vmrglw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vmrglw_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vmrglw128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vmrglw_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vmsummbm(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmsumshm(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmsumshs(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmsumubm(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmsumuhm(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmsumuhs(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmsum3fp128(PPCHIRBuilder& f, const InstrData& i) { // Dot product XYZ. // (VD.xyzw) = (VA.x * VB.x) + (VA.y * VB.y) + (VA.z * VB.z) Value* v = f.DotProduct3(f.LoadVR(VX128_VA128), f.LoadVR(VX128_VB128)); v = f.Splat(v, VEC128_TYPE); f.StoreVR(VX128_VD128, v); return 0; } int InstrEmit_vmsum4fp128(PPCHIRBuilder& f, const InstrData& i) { // Dot product XYZW. // (VD.xyzw) = (VA.x * VB.x) + (VA.y * VB.y) + (VA.z * VB.z) + (VA.w * VB.w) Value* v = f.DotProduct4(f.LoadVR(VX128_VA128), f.LoadVR(VX128_VB128)); v = f.Splat(v, VEC128_TYPE); f.StoreVR(VX128_VD128, v); return 0; } int InstrEmit_vmulesb(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmulesh(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmuleub(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmuleuh(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmulosb(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmulosh(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmuloub(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmulouh(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vmulfp128(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- (VA) * (VB) (4 x fp) Value* v = f.Mul(f.LoadVR(VX128_VA128), f.LoadVR(VX128_VB128)); f.StoreVR(VX128_VD128, v); return 0; } int InstrEmit_vnmsubfp_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb, uint32_t vc) { // (VD) <- -(((VA) * (VC)) - (VB)) // NOTE: only one rounding should take place, but that's hard... // This really needs VFNMSUB132PS/VFNMSUB213PS/VFNMSUB231PS but that's AVX. Value* v = f.Neg(f.MulSub(f.LoadVR(va), f.LoadVR(vc), f.LoadVR(vb))); f.StoreVR(vd, v); return 0; } int InstrEmit_vnmsubfp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vnmsubfp_(f, i.VXA.VD, i.VXA.VA, i.VXA.VB, i.VXA.VC); } int InstrEmit_vnmsubfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vnmsubfp_(f, VX128_VD128, VX128_VA128, VX128_VD128, VX128_VB128); } int InstrEmit_vnor_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // VD <- ¬((VA) | (VB)) Value* v = f.Not(f.Or(f.LoadVR(va), f.LoadVR(vb))); f.StoreVR(vd, v); return 0; } int InstrEmit_vnor(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vnor_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vnor128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vnor_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vor_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // VD <- (VA) | (VB) if (va == vb) { // Copy VA==VB into VD. f.StoreVR(vd, f.LoadVR(va)); } else { Value* v = f.Or(f.LoadVR(va), f.LoadVR(vb)); f.StoreVR(vd, v); } return 0; } int InstrEmit_vor(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vor_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vor128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vor_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vperm_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb, uint32_t vc) { Value* v = f.Permute(f.LoadVR(vc), f.LoadVR(va), f.LoadVR(vb), INT8_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vperm(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vperm_(f, i.VXA.VD, i.VXA.VA, i.VXA.VB, i.VXA.VC); } int InstrEmit_vperm128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vperm_(f, VX128_2_VD128, VX128_2_VA128, VX128_2_VB128, VX128_2_VC); } int InstrEmit_vpermwi128(PPCHIRBuilder& f, const InstrData& i) { // (VD.x) = (VB.uimm[6-7]) // (VD.y) = (VB.uimm[4-5]) // (VD.z) = (VB.uimm[2-3]) // (VD.w) = (VB.uimm[0-1]) const uint32_t vd = i.VX128_P.VD128l | (i.VX128_P.VD128h << 5); const uint32_t vb = i.VX128_P.VB128l | (i.VX128_P.VB128h << 5); uint32_t uimm = i.VX128_P.PERMl | (i.VX128_P.PERMh << 5); uint32_t mask = MakeSwizzleMask(uimm >> 6, uimm >> 4, uimm >> 2, uimm >> 0); Value* v = f.Swizzle(f.LoadVR(vb), INT32_TYPE, mask); f.StoreVR(vd, v); return 0; } int InstrEmit_vrefp_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // (VD) <- 1/(VB) Value* v = f.Recip(f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vrefp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrefp_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vrefp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrefp_(f, VX128_3_VD128, VX128_3_VB128); } int InstrEmit_vrfim_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // (VD) <- RndToFPInt32Floor(VB) Value* v = f.Round(f.LoadVR(vb), ROUND_TO_MINUS_INFINITY); f.StoreVR(vd, v); return 0; } int InstrEmit_vrfim(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrfim_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vrfim128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrfim_(f, VX128_3_VD128, VX128_3_VB128); } int InstrEmit_vrfin_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // (VD) <- RoundToNearest(VB) Value* v = f.Round(f.LoadVR(vb), ROUND_TO_NEAREST); f.StoreVR(vd, v); return 0; } int InstrEmit_vrfin(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrfin_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vrfin128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrfin_(f, VX128_3_VD128, VX128_3_VB128); } int InstrEmit_vrfip_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // (VD) <- RndToFPInt32Ceil(VB) Value* v = f.Round(f.LoadVR(vb), ROUND_TO_POSITIVE_INFINITY); f.StoreVR(vd, v); return 0; } int InstrEmit_vrfip(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrfip_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vrfip128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrfip_(f, VX128_3_VD128, VX128_3_VB128); } int InstrEmit_vrfiz_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // (VD) <- RndToFPInt32Trunc(VB) Value* v = f.Round(f.LoadVR(vb), ROUND_TO_ZERO); f.StoreVR(vd, v); return 0; } int InstrEmit_vrfiz(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrfiz_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vrfiz128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrfiz_(f, VX128_3_VD128, VX128_3_VB128); } int InstrEmit_vrlb(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- ROTL((VA), (VB)&0x3) Value* v = f.VectorRotateLeft(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vrlh(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- ROTL((VA), (VB)&0xF) Value* v = f.VectorRotateLeft(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vrlw_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD) <- ROTL((VA), (VB)&0x1F) Value* v = f.VectorRotateLeft(f.LoadVR(va), f.LoadVR(vb), INT32_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vrlw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrlw_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vrlw128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrlw_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vrlimi128(PPCHIRBuilder& f, const InstrData& i) { const uint32_t vd = i.VX128_4.VD128l | (i.VX128_4.VD128h << 5); const uint32_t vb = i.VX128_4.VB128l | (i.VX128_4.VB128h << 5); uint32_t blend_mask_src = i.VX128_4.IMM; uint32_t blend_mask = 0; blend_mask |= (((blend_mask_src >> 3) & 0x1) ? 0 : 4) << 0; blend_mask |= (((blend_mask_src >> 2) & 0x1) ? 1 : 5) << 8; blend_mask |= (((blend_mask_src >> 1) & 0x1) ? 2 : 6) << 16; blend_mask |= (((blend_mask_src >> 0) & 0x1) ? 3 : 7) << 24; uint32_t rotate = i.VX128_4.z; // This is just a fancy permute. // X Y Z W, rotated left by 2 = Z W X Y // Then mask select the results into the dest. // Sometimes rotation is zero, so fast path. Value* v; if (rotate) { // TODO(benvanik): constants need conversion. uint32_t swizzle_mask; switch (rotate) { case 1: // X Y Z W -> Y Z W X swizzle_mask = SWIZZLE_XYZW_TO_YZWX; break; case 2: // X Y Z W -> Z W X Y swizzle_mask = SWIZZLE_XYZW_TO_ZWXY; break; case 3: // X Y Z W -> W X Y Z swizzle_mask = SWIZZLE_XYZW_TO_WXYZ; break; default: XEINSTRNOTIMPLEMENTED(); return 1; } v = f.Swizzle(f.LoadVR(vb), FLOAT32_TYPE, swizzle_mask); } else { v = f.LoadVR(vb); } if (blend_mask != kIdentityPermuteMask) { v = f.Permute(f.LoadConstantUint32(blend_mask), v, f.LoadVR(vd), INT32_TYPE); } f.StoreVR(vd, v); return 0; } int InstrEmit_vrsqrtefp_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // (VD) <- 1 / sqrt(VB) // There are a lot of rules in the Altivec_PEM docs for handlings that // result in nan/infinity/etc. They are ignored here. I hope games would // never rely on them. Value* v = f.RSqrt(f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vrsqrtefp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrsqrtefp_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vrsqrtefp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vrsqrtefp_(f, VX128_3_VD128, VX128_3_VB128); } int InstrEmit_vsel_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb, uint32_t vc) { // For each bit: // VRTi <- ((VRC)i=0) ? (VRA)i : (VRB)i Value* v = f.Select(f.LoadVR(vc), f.LoadVR(va), f.LoadVR(vb)); f.StoreVR(vd, v); return 0; } int InstrEmit_vsel(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsel_(f, i.VXA.VD, i.VXA.VA, i.VXA.VB, i.VXA.VC); } int InstrEmit_vsel128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsel_(f, VX128_VD128, VX128_VA128, VX128_VB128, VX128_VD128); } int InstrEmit_vsl(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.Shl(f.LoadVR(i.VX.VA), f.And(f.Extract(f.LoadVR(i.VX.VB), 15, INT8_TYPE), f.LoadConstantInt8(0b111))); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vslb(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorShl(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vslh(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.VectorShl(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vslw_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // VA = |xxxxx|yyyyy|zzzzz|wwwww| // VB = |...sh|...sh|...sh|...sh| // VD = |x<> (16 - SH)) Value* control = f.LoadConstantVec128(__vsldoi_table[sh]); Value* v = f.Permute(control, f.LoadVR(va), f.LoadVR(vb), INT8_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vsldoi(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsldoi_(f, i.VXA.VD, i.VXA.VA, i.VXA.VB, i.VXA.VC & 0xF); } int InstrEmit_vsldoi128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsldoi_(f, VX128_5_VD128, VX128_5_VA128, VX128_5_VB128, VX128_5_SH); } int InstrEmit_vslo_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD) <- (VA) << (VB.b[F] & 0x78) (by octet) // TODO(benvanik): flag for shift-by-octet as optimization. Value* sh = f.Shr( f.And(f.Extract(f.LoadVR(vb), 15, INT8_TYPE), f.LoadConstantInt8(0x78)), 3); Value* v = f.Permute(f.LoadVectorShl(sh), f.LoadVR(va), f.LoadZeroVec128(), INT8_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vslo(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vslo_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vslo128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vslo_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vspltb(PPCHIRBuilder& f, const InstrData& i) { // b <- UIMM*8 // do i = 0 to 127 by 8 // (VD)[i:i+7] <- (VB)[b:b+7] Value* b = f.Extract(f.LoadVR(i.VX.VB), i.VX.VA & 0xF, INT8_TYPE); Value* v = f.Splat(b, VEC128_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsplth(PPCHIRBuilder& f, const InstrData& i) { // (VD.xyzw) <- (VB.uimm) Value* h = f.Extract(f.LoadVR(i.VX.VB), i.VX.VA & 0x7, INT16_TYPE); Value* v = f.Splat(h, VEC128_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vspltw_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb, uint32_t uimm) { // (VD.xyzw) <- (VB.uimm) Value* w = f.Extract(f.LoadVR(vb), uimm & 0x3, INT32_TYPE); Value* v = f.Splat(w, VEC128_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vspltw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vspltw_(f, i.VX.VD, i.VX.VB, i.VX.VA); } int InstrEmit_vspltw128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vspltw_(f, VX128_3_VD128, VX128_3_VB128, VX128_3_IMM); } int InstrEmit_vspltisb(PPCHIRBuilder& f, const InstrData& i) { // (VD.xyzw) <- sign_extend(uimm) Value* v; if (i.VX.VA) { // Sign extend from 5bits -> 8 and load. int8_t simm = (i.VX.VA & 0x10) ? (i.VX.VA | 0xF0) : i.VX.VA; v = f.Splat(f.LoadConstantInt8(simm), VEC128_TYPE); } else { // Zero out the register. v = f.LoadZeroVec128(); } f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vspltish(PPCHIRBuilder& f, const InstrData& i) { // (VD.xyzw) <- sign_extend(uimm) Value* v; if (i.VX.VA) { // Sign extend from 5bits -> 16 and load. int16_t simm = (i.VX.VA & 0x10) ? (i.VX.VA | 0xFFF0) : i.VX.VA; v = f.Splat(f.LoadConstantInt16(simm), VEC128_TYPE); } else { // Zero out the register. v = f.LoadZeroVec128(); } f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vspltisw_(PPCHIRBuilder& f, uint32_t vd, uint32_t uimm) { // (VD.xyzw) <- sign_extend(uimm) Value* v; if (uimm) { // Sign extend from 5bits -> 32 and load. int32_t simm = (uimm & 0x10) ? (uimm | 0xFFFFFFF0) : uimm; v = f.Splat(f.LoadConstantInt32(simm), VEC128_TYPE); } else { // Zero out the register. v = f.LoadZeroVec128(); } f.StoreVR(vd, v); return 0; } int InstrEmit_vspltisw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vspltisw_(f, i.VX.VD, i.VX.VA); } int InstrEmit_vspltisw128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vspltisw_(f, VX128_3_VD128, VX128_3_IMM); } int InstrEmit_vsr(PPCHIRBuilder& f, const InstrData& i) { Value* v = f.Shr(f.LoadVR(i.VX.VA), f.And(f.Extract(f.LoadVR(i.VX.VB), 15, INT8_TYPE), f.LoadConstantInt8(0b111))); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsrab(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- (VA) >>a (VB) by bytes Value* v = f.VectorSha(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsrah(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- (VA) >>a (VB) by halfwords Value* v = f.VectorSha(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsraw_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD) <- (VA) >>a (VB) by words Value* v = f.VectorSha(f.LoadVR(va), f.LoadVR(vb), INT32_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vsraw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsraw_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vsraw128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsraw_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vsrb(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- (VA) >> (VB) by bytes Value* v = f.VectorShr(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsrh(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- (VA) >> (VB) by halfwords Value* v = f.VectorShr(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsro_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD) <- (VA) >> (VB.b[F] & 0x78) (by octet) // TODO(benvanik): flag for shift-by-octet as optimization. Value* sh = f.Shr( f.And(f.Extract(f.LoadVR(vb), 15, INT8_TYPE), f.LoadConstantInt8(0x78)), 3); Value* v = f.Permute(f.LoadVectorShr(sh), f.LoadZeroVec128(), f.LoadVR(va), INT8_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vsro(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsro_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vsro128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsro_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vsrw_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD) <- (VA) >> (VB) by words Value* v = f.VectorShr(f.LoadVR(va), f.LoadVR(vb), INT32_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vsrw(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsrw_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vsrw128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsrw_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vsubcuw(PPCHIRBuilder& f, const InstrData& i) { Value* underflow = f.VectorCompareUGE(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE); Value* borrow = f.VectorShr(underflow, f.LoadConstantVec128(vec128i(31)), INT32_TYPE); f.StoreVR(i.VX.VD, borrow); return 1; } int InstrEmit_vsubfp_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // (VD) <- (VA) - (VB) (4 x fp) Value* v = f.VectorSub(f.LoadVR(va), f.LoadVR(vb), FLOAT32_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vsubfp(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsubfp_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vsubfp128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vsubfp_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vsubsbs(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- clamp(EXTS(VA) + ¬EXTS(VB) + 1, -128, 127) Value* v = f.VectorSub(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, ARITHMETIC_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsubshs(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- clamp(EXTS(VA) + ¬EXTS(VB) + 1, -2^15, 2^15-1) Value* v = f.VectorSub(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, ARITHMETIC_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsubsws(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- clamp(EXTS(VA) + ¬EXTS(VB) + 1, -2^31, 2^31-1) Value* v = f.VectorSub(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsububm(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- (EXTZ(VA) + ¬EXTZ(VB) + 1) % 256 Value* v = f.VectorSub(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsubuhm(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- (EXTZ(VA) + ¬EXTZ(VB) + 1) % 2^16 Value* v = f.VectorSub(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsubuwm(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- (EXTZ(VA) + ¬EXTZ(VB) + 1) % 2^32 Value* v = f.VectorSub(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_UNSIGNED); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsububs(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- clamp(EXTZ(VA) + ¬EXTZ(VB) + 1, 0, 256) Value* v = f.VectorSub(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT8_TYPE, ARITHMETIC_SATURATE | ARITHMETIC_UNSIGNED); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsubuhs(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- clamp(EXTZ(VA) + ¬EXTZ(VB) + 1, 0, 2^16-1) Value* v = f.VectorSub(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT16_TYPE, ARITHMETIC_SATURATE | ARITHMETIC_UNSIGNED); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsubuws(PPCHIRBuilder& f, const InstrData& i) { // (VD) <- clamp(EXTZ(VA) + ¬EXTZ(VB) + 1, 0, 2^32-1) Value* v = f.VectorSub(f.LoadVR(i.VX.VA), f.LoadVR(i.VX.VB), INT32_TYPE, ARITHMETIC_SATURATE | ARITHMETIC_UNSIGNED); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(i.VX.VD, v); return 0; } int InstrEmit_vsumsws(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vsum2sws(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vsum4sbs(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vsum4shs(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vsum4ubs(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vpkpx(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vpkshss_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // Vector Pack Signed Halfword Signed Saturate // Convert VA and VB from signed words to signed saturated bytes then // concat: // for each i in VA + VB: // i = int8_t(Clamp(EXTS(int16_t(t)), -128, 127)) // dest = VA | VB (lower 8bit values) Value* v = f.Pack(f.LoadVR(va), f.LoadVR(vb), PACK_TYPE_8_IN_16 | PACK_TYPE_IN_SIGNED | PACK_TYPE_OUT_SIGNED | PACK_TYPE_OUT_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(vd, v); return 0; } int InstrEmit_vpkshss(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkshss_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vpkshss128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkshss_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vpkswss_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // Vector Pack Signed Word Signed Saturate // Convert VA and VB from signed int words to signed saturated shorts then // concat: // for each i in VA + VB: // i = int16_t(Clamp(EXTS(int32_t(t)), -2^15, 2^15-1)) // dest = VA | VB (lower 16bit values) Value* v = f.Pack(f.LoadVR(va), f.LoadVR(vb), PACK_TYPE_16_IN_32 | PACK_TYPE_IN_SIGNED | PACK_TYPE_OUT_SIGNED | PACK_TYPE_OUT_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(vd, v); return 0; } int InstrEmit_vpkswss(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkswss_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vpkswss128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkswss_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vpkswus_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // Vector Pack Signed Word Unsigned Saturate // Convert VA and VB from signed int words to unsigned saturated shorts then // concat: // for each i in VA + VB: // i = uint16_t(Clamp(EXTS(int32_t(t)), 0, 2^16-1)) // dest = VA | VB (lower 16bit values) Value* v = f.Pack(f.LoadVR(va), f.LoadVR(vb), PACK_TYPE_16_IN_32 | PACK_TYPE_IN_SIGNED | PACK_TYPE_OUT_UNSIGNED | PACK_TYPE_OUT_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(vd, v); return 0; } int InstrEmit_vpkswus(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkswus_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vpkswus128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkswus_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vpkuhum_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // Vector Pack Unsigned Halfword Unsigned Modulo // Convert VA and VB from unsigned shorts to unsigned bytes then concat: // for each i in VA + VB: // i = uint8_t(uint16_t(i)) // dest = VA | VB (lower 8bit values) Value* v = f.Pack(f.LoadVR(va), f.LoadVR(vb), PACK_TYPE_8_IN_16 | PACK_TYPE_IN_UNSIGNED | PACK_TYPE_OUT_UNSIGNED | PACK_TYPE_OUT_UNSATURATE); f.StoreVR(vd, v); return 0; } int InstrEmit_vpkuhum(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkuhum_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vpkuhum128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkuhum_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vpkuhus_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // Vector Pack Unsigned Halfword Unsigned Saturate // Convert VA and VB from unsigned shorts to unsigned saturated bytes then // concat: // for each i in VA + VB: // i = uint8_t(Clamp(EXTZ(uint16_t(i)), 0, 255)) // dest = VA | VB (lower 8bit values) Value* v = f.Pack(f.LoadVR(va), f.LoadVR(vb), PACK_TYPE_8_IN_16 | PACK_TYPE_IN_UNSIGNED | PACK_TYPE_OUT_UNSIGNED | PACK_TYPE_OUT_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(vd, v); return 0; } int InstrEmit_vpkuhus(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkuhus_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vpkuhus128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkuhus_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vpkshus_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // Vector Pack Signed Halfword Unsigned Saturate // Convert VA and VB from signed shorts to unsigned saturated bytes then // concat: // for each i in VA + VB: // i = uint8_t(Clamp(EXTS(int16_t(i)), 0, 255)) // dest = VA | VB (lower 8bit values) Value* v = f.Pack(f.LoadVR(va), f.LoadVR(vb), PACK_TYPE_8_IN_16 | PACK_TYPE_IN_SIGNED | PACK_TYPE_OUT_UNSIGNED | PACK_TYPE_OUT_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(vd, v); return 0; } int InstrEmit_vpkshus(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkshus_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vpkshus128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkshus_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vpkuwum_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // Vector Pack Unsigned Word Unsigned Modulo // Concat low shorts from VA + VB: // for each i in VA + VB: // i = uint16_t(uint32_t(i)) // dest = VA | VB (lower 16bit values) Value* v = f.Pack(f.LoadVR(va), f.LoadVR(vb), PACK_TYPE_16_IN_32 | PACK_TYPE_IN_UNSIGNED | PACK_TYPE_OUT_UNSIGNED | PACK_TYPE_OUT_UNSATURATE); f.StoreVR(vd, v); return 0; } int InstrEmit_vpkuwum(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkuwum_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vpkuwum128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkuwum_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vpkuwus_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // Vector Pack Unsigned Word Unsigned Saturate // Convert VA and VB from unsigned int words to unsigned saturated shorts then // concat: // for each i in VA + VB: // i = uint16_t(Clamp(EXTZ(uint32_t(t)), 0, 2^16-1)) // dest = VA | VB (lower 16bit values) Value* v = f.Pack(f.LoadVR(va), f.LoadVR(vb), PACK_TYPE_16_IN_32 | PACK_TYPE_IN_UNSIGNED | PACK_TYPE_OUT_UNSIGNED | PACK_TYPE_OUT_SATURATE); f.StoreSAT(f.DidSaturate(v)); f.StoreVR(vd, v); return 0; } int InstrEmit_vpkuwus(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkuwus_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vpkuwus128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vpkuwus_(f, VX128_VD128, VX128_VA128, VX128_VB128); } int InstrEmit_vupkhpx(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vupklpx(PPCHIRBuilder& f, const InstrData& i) { XEINSTRNOTIMPLEMENTED(); return 1; } int InstrEmit_vupkhsh_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // Vector Unpack High Signed Halfword // halfwords 0-3 expanded to words 0-3 and sign extended Value* v = f.Unpack(f.LoadVR(vb), PACK_TYPE_TO_HI | PACK_TYPE_16_IN_32 | PACK_TYPE_IN_SIGNED | PACK_TYPE_OUT_SIGNED); f.StoreVR(vd, v); return 0; } int InstrEmit_vupkhsh(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vupkhsh_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vupkhsh128(PPCHIRBuilder& f, const InstrData& i) { uint32_t va = VX128_VA128; assert_zero(va); return InstrEmit_vupkhsh_(f, VX128_VD128, VX128_VB128); } int InstrEmit_vupklsh_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // Vector Unpack Low Signed Halfword // halfwords 4-7 expanded to words 0-3 and sign extended Value* v = f.Unpack(f.LoadVR(vb), PACK_TYPE_TO_LO | PACK_TYPE_16_IN_32 | PACK_TYPE_IN_SIGNED | PACK_TYPE_OUT_SIGNED); f.StoreVR(vd, v); return 0; } int InstrEmit_vupklsh(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vupklsh_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vupklsh128(PPCHIRBuilder& f, const InstrData& i) { uint32_t va = VX128_VA128; assert_zero(va); return InstrEmit_vupklsh_(f, VX128_VD128, VX128_VB128); } int InstrEmit_vupkhsb_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // Vector Unpack High Signed Byte // bytes 0-7 expanded to halfwords 0-7 and sign extended Value* v = f.Unpack(f.LoadVR(vb), PACK_TYPE_TO_HI | PACK_TYPE_8_IN_16 | PACK_TYPE_IN_SIGNED | PACK_TYPE_OUT_SIGNED); f.StoreVR(vd, v); return 0; } int InstrEmit_vupkhsb(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vupkhsb_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vupkhsb128(PPCHIRBuilder& f, const InstrData& i) { uint32_t va = VX128_VA128; if (va == 0x60) { // Hrm, my instruction tables suck. return InstrEmit_vupkhsh_(f, VX128_VD128, VX128_VB128); } return InstrEmit_vupkhsb_(f, VX128_VD128, VX128_VB128); } int InstrEmit_vupklsb_(PPCHIRBuilder& f, uint32_t vd, uint32_t vb) { // Vector Unpack Low Signed Byte // bytes 8-15 expanded to halfwords 0-7 and sign extended Value* v = f.Unpack(f.LoadVR(vb), PACK_TYPE_TO_LO | PACK_TYPE_8_IN_16 | PACK_TYPE_IN_SIGNED | PACK_TYPE_OUT_SIGNED); f.StoreVR(vd, v); return 0; } int InstrEmit_vupklsb(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vupklsb_(f, i.VX.VD, i.VX.VB); } int InstrEmit_vupklsb128(PPCHIRBuilder& f, const InstrData& i) { uint32_t va = VX128_VA128; if (va == 0x60) { // Hrm, my instruction tables suck. return InstrEmit_vupklsh_(f, VX128_VD128, VX128_VB128); } return InstrEmit_vupklsb_(f, VX128_VD128, VX128_VB128); } int InstrEmit_vpkd3d128(PPCHIRBuilder& f, const InstrData& i) { const uint32_t vd = i.VX128_4.VD128l | (i.VX128_4.VD128h << 5); const uint32_t vb = i.VX128_4.VB128l | (i.VX128_4.VB128h << 5); uint32_t type = i.VX128_4.IMM >> 2; uint32_t pack = i.VX128_4.IMM & 0x3; uint32_t shift = i.VX128_4.z; Value* v = f.LoadVR(vb); switch (type) { case 0: // VPACK_D3DCOLOR v = f.Pack(v, PACK_TYPE_D3DCOLOR); break; case 1: // VPACK_NORMSHORT2 v = f.Pack(v, PACK_TYPE_SHORT_2); break; case 2: // VPACK_NORMPACKED32 2_10_10_10 w_z_y_x v = f.Pack(v, PACK_TYPE_UINT_2101010); break; case 3: // VPACK_FLOAT16_2 DXGI_FORMAT_R16G16_FLOAT v = f.Pack(v, PACK_TYPE_FLOAT16_2); break; case 4: // VPACK_NORMSHORT4 v = f.Pack(v, PACK_TYPE_SHORT_4); break; case 5: // VPACK_FLOAT16_4 DXGI_FORMAT_R16G16B16A16_FLOAT v = f.Pack(v, PACK_TYPE_FLOAT16_4); break; case 6: // VPACK_NORMPACKED64 4_20_20_20 w_z_y_x // Used in 54540829 and other installments in the series, pretty rarely in // general. v = f.Pack(v, PACK_TYPE_ULONG_4202020); break; default: assert_unhandled_case(type); return 1; } // https://hlssmod.net/he_code/public/pixelwriter.h // control = prev:0123 | new:4567 uint32_t control = kIdentityPermuteMask; // original switch (pack) { case 1: // VPACK_32 // VPACK_32 & shift = 3 puts lower 32 bits in x (leftmost slot). switch (shift) { case 0: control = MakePermuteMask(0, 0, 0, 1, 0, 2, 1, 3); break; case 1: control = MakePermuteMask(0, 0, 0, 1, 1, 3, 0, 3); break; case 2: control = MakePermuteMask(0, 0, 1, 3, 0, 2, 0, 3); break; case 3: control = MakePermuteMask(1, 3, 0, 1, 0, 2, 0, 3); break; default: assert_unhandled_case(shift); return 1; } break; case 2: // 64bit switch (shift) { case 0: control = MakePermuteMask(0, 0, 0, 1, 1, 2, 1, 3); break; case 1: control = MakePermuteMask(0, 0, 1, 2, 1, 3, 0, 3); break; case 2: control = MakePermuteMask(1, 2, 1, 3, 0, 2, 0, 3); break; case 3: control = MakePermuteMask(1, 3, 0, 1, 0, 2, 0, 3); break; default: assert_unhandled_case(shift); return 1; } break; case 3: // 64bit switch (shift) { case 0: control = MakePermuteMask(0, 0, 0, 1, 1, 2, 1, 3); break; case 1: control = MakePermuteMask(0, 0, 1, 2, 1, 3, 0, 3); break; case 2: control = MakePermuteMask(1, 2, 1, 3, 0, 2, 0, 3); break; case 3: control = MakePermuteMask(0, 0, 0, 1, 0, 2, 1, 2); break; default: assert_unhandled_case(shift); return 1; } break; default: assert_unhandled_case(pack); return 1; } v = f.Permute(f.LoadConstantUint32(control), f.LoadVR(vd), v, INT32_TYPE); f.StoreVR(vd, v); return 0; } int InstrEmit_vupkd3d128(PPCHIRBuilder& f, const InstrData& i) { // Can't find many docs on this. Best reference is // https://code.google.com/archive/p/worldcraft/source/default/source?page=4 // (qylib/math/xmmatrix.inl) // which shows how it's used in some cases. Since it's all intrinsics, // finding it in code is pretty easy. const uint32_t vd = i.VX128_3.VD128l | (i.VX128_3.VD128h << 5); const uint32_t vb = i.VX128_3.VB128l | (i.VX128_3.VB128h << 5); const uint32_t type = i.VX128_3.IMM >> 2; Value* v = f.LoadVR(vb); switch (type) { case 0: // VPACK_D3DCOLOR v = f.Unpack(v, PACK_TYPE_D3DCOLOR); break; case 1: // VPACK_NORMSHORT2 v = f.Unpack(v, PACK_TYPE_SHORT_2); break; case 2: // VPACK_NORMPACKED32 2_10_10_10 w_z_y_x v = f.Unpack(v, PACK_TYPE_UINT_2101010); break; case 3: // VPACK_FLOAT16_2 DXGI_FORMAT_R16G16_FLOAT v = f.Unpack(v, PACK_TYPE_FLOAT16_2); break; case 4: // VPACK_NORMSHORT4 v = f.Unpack(v, PACK_TYPE_SHORT_4); break; case 5: // VPACK_FLOAT16_4 DXGI_FORMAT_R16G16B16A16_FLOAT v = f.Unpack(v, PACK_TYPE_FLOAT16_4); break; case 6: // VPACK_NORMPACKED64 4_20_20_20 w_z_y_x v = f.Unpack(v, PACK_TYPE_ULONG_4202020); break; default: assert_unhandled_case(type); return 1; } f.StoreVR(vd, v); return 0; } int InstrEmit_vxor_(PPCHIRBuilder& f, uint32_t vd, uint32_t va, uint32_t vb) { // VD <- (VA) ^ (VB) Value* v; if (va == vb) { // Fast clear. v = f.LoadZeroVec128(); } else { v = f.Xor(f.LoadVR(va), f.LoadVR(vb)); } f.StoreVR(vd, v); return 0; } int InstrEmit_vxor(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vxor_(f, i.VX.VD, i.VX.VA, i.VX.VB); } int InstrEmit_vxor128(PPCHIRBuilder& f, const InstrData& i) { return InstrEmit_vxor_(f, VX128_VD128, VX128_VA128, VX128_VB128); } void RegisterEmitCategoryAltivec() { XEREGISTERINSTR(lvebx); XEREGISTERINSTR(lvehx); XEREGISTERINSTR(lvewx); XEREGISTERINSTR(lvewx128); XEREGISTERINSTR(lvsl); XEREGISTERINSTR(lvsl128); XEREGISTERINSTR(lvsr); XEREGISTERINSTR(lvsr128); XEREGISTERINSTR(lvx); XEREGISTERINSTR(lvx128); XEREGISTERINSTR(lvxl); XEREGISTERINSTR(lvxl128); XEREGISTERINSTR(stvebx); XEREGISTERINSTR(stvehx); XEREGISTERINSTR(stvewx); XEREGISTERINSTR(stvewx128); XEREGISTERINSTR(stvx); XEREGISTERINSTR(stvx128); XEREGISTERINSTR(stvxl); XEREGISTERINSTR(stvxl128); XEREGISTERINSTR(lvlx); XEREGISTERINSTR(lvlx128); XEREGISTERINSTR(lvlxl); XEREGISTERINSTR(lvlxl128); XEREGISTERINSTR(lvrx); XEREGISTERINSTR(lvrx128); XEREGISTERINSTR(lvrxl); XEREGISTERINSTR(lvrxl128); XEREGISTERINSTR(stvlx); XEREGISTERINSTR(stvlx128); XEREGISTERINSTR(stvlxl); XEREGISTERINSTR(stvlxl128); XEREGISTERINSTR(stvrx); XEREGISTERINSTR(stvrx128); XEREGISTERINSTR(stvrxl); XEREGISTERINSTR(stvrxl128); XEREGISTERINSTR(mfvscr); XEREGISTERINSTR(mtvscr); XEREGISTERINSTR(vaddcuw); XEREGISTERINSTR(vaddfp); XEREGISTERINSTR(vaddfp128); XEREGISTERINSTR(vaddsbs); XEREGISTERINSTR(vaddshs); XEREGISTERINSTR(vaddsws); XEREGISTERINSTR(vaddubm); XEREGISTERINSTR(vaddubs); XEREGISTERINSTR(vadduhm); XEREGISTERINSTR(vadduhs); XEREGISTERINSTR(vadduwm); XEREGISTERINSTR(vadduws); XEREGISTERINSTR(vand); XEREGISTERINSTR(vand128); XEREGISTERINSTR(vandc); XEREGISTERINSTR(vandc128); XEREGISTERINSTR(vavgsb); XEREGISTERINSTR(vavgsh); XEREGISTERINSTR(vavgsw); XEREGISTERINSTR(vavgub); XEREGISTERINSTR(vavguh); XEREGISTERINSTR(vavguw); XEREGISTERINSTR(vcfsx); XEREGISTERINSTR(vcsxwfp128); XEREGISTERINSTR(vcfpsxws128); XEREGISTERINSTR(vcfux); XEREGISTERINSTR(vcuxwfp128); XEREGISTERINSTR(vcfpuxws128); XEREGISTERINSTR(vcmpbfp); XEREGISTERINSTR(vcmpbfp128); XEREGISTERINSTR(vcmpeqfp); XEREGISTERINSTR(vcmpeqfp128); XEREGISTERINSTR(vcmpgefp); XEREGISTERINSTR(vcmpgefp128); XEREGISTERINSTR(vcmpgtfp); XEREGISTERINSTR(vcmpgtfp128); XEREGISTERINSTR(vcmpgtsb); XEREGISTERINSTR(vcmpgtsh); XEREGISTERINSTR(vcmpgtsw); XEREGISTERINSTR(vcmpequb); XEREGISTERINSTR(vcmpgtub); XEREGISTERINSTR(vcmpequh); XEREGISTERINSTR(vcmpgtuh); XEREGISTERINSTR(vcmpequw); XEREGISTERINSTR(vcmpequw128); XEREGISTERINSTR(vcmpgtuw); XEREGISTERINSTR(vctsxs); XEREGISTERINSTR(vctuxs); XEREGISTERINSTR(vexptefp); XEREGISTERINSTR(vexptefp128); XEREGISTERINSTR(vlogefp); XEREGISTERINSTR(vlogefp128); XEREGISTERINSTR(vmaddfp); XEREGISTERINSTR(vmaddfp128); XEREGISTERINSTR(vmaddcfp128); XEREGISTERINSTR(vmaxfp); XEREGISTERINSTR(vmaxfp128); XEREGISTERINSTR(vmaxsb); XEREGISTERINSTR(vmaxsh); XEREGISTERINSTR(vmaxsw); XEREGISTERINSTR(vmaxub); XEREGISTERINSTR(vmaxuh); XEREGISTERINSTR(vmaxuw); XEREGISTERINSTR(vmhaddshs); XEREGISTERINSTR(vmhraddshs); XEREGISTERINSTR(vminfp); XEREGISTERINSTR(vminfp128); XEREGISTERINSTR(vminsb); XEREGISTERINSTR(vminsh); XEREGISTERINSTR(vminsw); XEREGISTERINSTR(vminub); XEREGISTERINSTR(vminuh); XEREGISTERINSTR(vminuw); XEREGISTERINSTR(vmladduhm); XEREGISTERINSTR(vmrghb); XEREGISTERINSTR(vmrghh); XEREGISTERINSTR(vmrghw); XEREGISTERINSTR(vmrghw128); XEREGISTERINSTR(vmrglb); XEREGISTERINSTR(vmrglh); XEREGISTERINSTR(vmrglw); XEREGISTERINSTR(vmrglw128); XEREGISTERINSTR(vmsummbm); XEREGISTERINSTR(vmsumshm); XEREGISTERINSTR(vmsumshs); XEREGISTERINSTR(vmsumubm); XEREGISTERINSTR(vmsumuhm); XEREGISTERINSTR(vmsumuhs); XEREGISTERINSTR(vmsum3fp128); XEREGISTERINSTR(vmsum4fp128); XEREGISTERINSTR(vmulesb); XEREGISTERINSTR(vmulesh); XEREGISTERINSTR(vmuleub); XEREGISTERINSTR(vmuleuh); XEREGISTERINSTR(vmulosb); XEREGISTERINSTR(vmulosh); XEREGISTERINSTR(vmuloub); XEREGISTERINSTR(vmulouh); XEREGISTERINSTR(vmulfp128); XEREGISTERINSTR(vnmsubfp); XEREGISTERINSTR(vnmsubfp128); XEREGISTERINSTR(vnor); XEREGISTERINSTR(vnor128); XEREGISTERINSTR(vor); XEREGISTERINSTR(vor128); XEREGISTERINSTR(vperm); XEREGISTERINSTR(vperm128); XEREGISTERINSTR(vpermwi128); XEREGISTERINSTR(vpkpx); XEREGISTERINSTR(vpkshss); XEREGISTERINSTR(vpkshss128); XEREGISTERINSTR(vpkshus); XEREGISTERINSTR(vpkshus128); XEREGISTERINSTR(vpkswss); XEREGISTERINSTR(vpkswss128); XEREGISTERINSTR(vpkswus); XEREGISTERINSTR(vpkswus128); XEREGISTERINSTR(vpkuhum); XEREGISTERINSTR(vpkuhum128); XEREGISTERINSTR(vpkuhus); XEREGISTERINSTR(vpkuhus128); XEREGISTERINSTR(vpkuwum); XEREGISTERINSTR(vpkuwum128); XEREGISTERINSTR(vpkuwus); XEREGISTERINSTR(vpkuwus128); XEREGISTERINSTR(vpkd3d128); XEREGISTERINSTR(vrefp); XEREGISTERINSTR(vrefp128); XEREGISTERINSTR(vrfim); XEREGISTERINSTR(vrfim128); XEREGISTERINSTR(vrfin); XEREGISTERINSTR(vrfin128); XEREGISTERINSTR(vrfip); XEREGISTERINSTR(vrfip128); XEREGISTERINSTR(vrfiz); XEREGISTERINSTR(vrfiz128); XEREGISTERINSTR(vrlb); XEREGISTERINSTR(vrlh); XEREGISTERINSTR(vrlw); XEREGISTERINSTR(vrlw128); XEREGISTERINSTR(vrlimi128); XEREGISTERINSTR(vrsqrtefp); XEREGISTERINSTR(vrsqrtefp128); XEREGISTERINSTR(vsel); XEREGISTERINSTR(vsel128); XEREGISTERINSTR(vsl); XEREGISTERINSTR(vslb); XEREGISTERINSTR(vslh); XEREGISTERINSTR(vslo); XEREGISTERINSTR(vslo128); XEREGISTERINSTR(vslw); XEREGISTERINSTR(vslw128); XEREGISTERINSTR(vsldoi); XEREGISTERINSTR(vsldoi128); XEREGISTERINSTR(vspltb); XEREGISTERINSTR(vsplth); XEREGISTERINSTR(vspltw); XEREGISTERINSTR(vspltw128); XEREGISTERINSTR(vspltisb); XEREGISTERINSTR(vspltish); XEREGISTERINSTR(vspltisw); XEREGISTERINSTR(vspltisw128); XEREGISTERINSTR(vsr); XEREGISTERINSTR(vsrab); XEREGISTERINSTR(vsrah); XEREGISTERINSTR(vsraw); XEREGISTERINSTR(vsraw128); XEREGISTERINSTR(vsrb); XEREGISTERINSTR(vsrh); XEREGISTERINSTR(vsro); XEREGISTERINSTR(vsro128); XEREGISTERINSTR(vsrw); XEREGISTERINSTR(vsrw128); XEREGISTERINSTR(vsubcuw); XEREGISTERINSTR(vsubfp); XEREGISTERINSTR(vsubfp128); XEREGISTERINSTR(vsubsbs); XEREGISTERINSTR(vsubshs); XEREGISTERINSTR(vsubsws); XEREGISTERINSTR(vsububm); XEREGISTERINSTR(vsubuhm); XEREGISTERINSTR(vsubuwm); XEREGISTERINSTR(vsububs); XEREGISTERINSTR(vsubuhs); XEREGISTERINSTR(vsubuws); XEREGISTERINSTR(vsumsws); XEREGISTERINSTR(vsum2sws); XEREGISTERINSTR(vsum4sbs); XEREGISTERINSTR(vsum4shs); XEREGISTERINSTR(vsum4ubs); XEREGISTERINSTR(vupkhpx); XEREGISTERINSTR(vupkhsb); XEREGISTERINSTR(vupkhsb128); XEREGISTERINSTR(vupkhsh); XEREGISTERINSTR(vupklpx); XEREGISTERINSTR(vupklsb); XEREGISTERINSTR(vupklsb128); XEREGISTERINSTR(vupklsh); XEREGISTERINSTR(vupkd3d128); XEREGISTERINSTR(vxor); XEREGISTERINSTR(vxor128); } } // namespace ppc } // namespace cpu } // namespace xe