Files
Xenia-Canary/src/xenia/cpu/ppc/ppc_emit_alu.cc
chss95cs@gmail.com 33a6cfc0a7 Add special cases to DOT_PRODUCT_3/4 that detect whether they're calculating lengthsquared
Add alternate path to DOT_PRODUCT_3/4 for use_fast_dot_product that skips all the status register stuff and just remaps inf to qnan
Add OPCODE_TO_SINGLE to replace the CONVERT_F32_F64 - CONVERT_F64_F32 sequence we used to emit with the idea that a backend could implement a more correct rounding behavior if possible on its arch
Remove some impossible sequences like MUL_HI_I8/I16, MUL_ADD_F32, DIV_V128. These instructions have no equivalent in PPC. Many other instructions are unused/dead code and should be removed to make the x64 backend a better reference for future ones
Add backend_flags to Instr. Basically, flags field that a backend can use for whatever it wants when generating code.
Add backend instr flag to x64 that tells it to not generate code for an instruction. this allows sequences to consume subsequent instructions
Generate actual x64 code for VSL instruction instead of using callnativesafe
Detect repeated COMPARE instructions w/ identical operands and reuse the results in FLAGS if so. this eliminates a ton of garbage compare/set instructions.
If a COMPARE instructions destination is stored to context with no intervening instruction and no additional uses besides the store, do setx [ctx address]
Detect prefetchw and use it in CACHE_CONTROL if prefetch for write is requested instead of doing prefetch to all cache levels
Fixed an accident in an earlier commit by me, VECTOR_DENORMFLUSH was not being emitted at all, so denormal inputs to MUL_ADD_V128 were not becoming zero and outputs from DOT_PRODUCT_X were not either. I believe this introduced a bug into RDR where a wagon wouldnt spawn? (https://discord.com/channels/308194948048486401/308207592482668545/1000443975817252874)
Compute fresx in double precision using RECIP_F64 and then round to single instead of doing (double)(1.0f / (float)value), matching original behavior better
Refactor some of ppc_emit_fpu, much of the InstrEmit function are identical except for whether they round to single or not
Added "tail emitters" to X64Emitter. These are callbacks that get invoked with their label and the X64Emitter after the epilog code. This allows us to move cold code out of the critical path and in the future place constant pools near functions
guest_to_host_thunk/host_to_guest_thunk now gets directly rel32 called, instead of doing a mov
Add X64BackendContext structure, represents data before the start of the PPCContext
Instead of doing branchless sequence, do a compare and jump to tail emitted code for address translation. This makes converting addresses a 3 uop affair in most cases.
Do qnan move for dot product in a tail emitter
Detect whether EFLAGS bits are independent variables for the current cpu (not really detecting it ehe, just checking if zen) and if so generate inc/dec for add/sub 1
Detect whether low 32 bits of membase are 0. If they are then we can use membasereg.cvt32() in place of immediate 0 in many places, particularly in stores
Detect LOAD MODIFY STORE pattern for context variables (currently only done for 64 bit ones) and turn them into modify [context ptr]. This is done for add, sub, and, or, xor, not, neg
Tail emit error handling for TRAP opcodes
Stub out unused trap opcodes like TRAP_TRUE_I32, TRAP_TRUE_I64, TRAP_TRUE_I16 (the call_true/return_true opcodes for these types are also probably unused)
Remove BackpropTruncations. It was poorly written and causes crashes on the game Viva pinata (https://discord.com/channels/308194948048486401/701111856600711208/1000249460451983420)
2022-07-23 12:10:07 -07:00

1399 lines
37 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
******************************************************************************
* 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"
namespace xe {
namespace cpu {
namespace ppc {
// TODO(benvanik): remove when enums redefined.
using namespace xe::cpu::hir;
using xe::cpu::hir::Value;
// Integer arithmetic (A-3)
Value* AddDidCarry(PPCHIRBuilder& f, Value* v1, Value* v2) {
return f.CompareUGT(f.Truncate(v2, INT32_TYPE),
f.Not(f.Truncate(v1, INT32_TYPE)));
}
Value* SubDidCarry(PPCHIRBuilder& f, Value* v1, Value* v2) {
Value* trunc_v2 = f.Truncate(v2, INT32_TYPE);
return f.Or(f.CompareUGT(f.Truncate(v1, INT32_TYPE),
f.Sub(trunc_v2, f.LoadConstantInt32(1))),
f.IsFalse(trunc_v2));
}
// https://github.com/sebastianbiallas/pearpc/blob/0b3c823f61456faa677f6209545a7b906e797421/src/cpu/cpu_generic/ppc_tools.h#L26
Value* AddWithCarryDidCarry(PPCHIRBuilder& f, Value* v1, Value* v2, Value* v3) {
v1 = f.Truncate(v1, INT32_TYPE);
v2 = f.Truncate(v2, INT32_TYPE);
assert_true(v3->type == INT8_TYPE);
v3 = f.ZeroExtend(v3, INT32_TYPE);
return f.Or(f.CompareULT(f.Add(f.Add(v1, v2), v3), v3),
f.CompareULT(f.Add(v1, v2), v1));
}
int InstrEmit_addx(PPCHIRBuilder& f, const InstrData& i) {
// RD <- (RA) + (RB)
Value* v = f.Add(f.LoadGPR(i.XO.RA), f.LoadGPR(i.XO.RB));
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
XEINSTRNOTIMPLEMENTED();
// e.update_xer_with_overflow(EFLAGS OF?);
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_addcx(PPCHIRBuilder& f, const InstrData& i) {
// RD <- (RA) + (RB)
// CA <- carry bit
Value* ra = f.LoadGPR(i.XO.RA);
Value* rb = f.LoadGPR(i.XO.RB);
Value* v = f.Add(ra, rb);
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
XEINSTRNOTIMPLEMENTED();
// e.update_xer_with_overflow(EFLAGS OF?);
} else {
f.StoreCA(AddDidCarry(f, ra, rb));
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_addex(PPCHIRBuilder& f, const InstrData& i) {
// RD <- (RA) + (RB) + XER[CA]
// CA <- carry bit
Value* ra = f.LoadGPR(i.XO.RA);
Value* rb = f.LoadGPR(i.XO.RB);
Value* v = f.AddWithCarry(ra, rb, f.LoadCA());
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
XEINSTRNOTIMPLEMENTED();
// e.update_xer_with_overflow(EFLAGS OF?);
} else {
f.StoreCA(AddWithCarryDidCarry(f, ra, rb, f.LoadCA()));
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_addi(PPCHIRBuilder& f, const InstrData& i) {
// if RA = 0 then
// RT <- EXTS(SI)
// else
// RT <- (RA) + EXTS(SI)
Value* si = f.LoadConstantInt64(XEEXTS16(i.D.DS));
Value* v = si;
if (i.D.RA) {
v = f.Add(f.LoadGPR(i.D.RA), si);
}
f.StoreGPR(i.D.RT, v);
return 0;
}
int InstrEmit_addic(PPCHIRBuilder& f, const InstrData& i) {
// RT <- (RA) + EXTS(SI)
// CA <- carry bit
Value* ra = f.LoadGPR(i.D.RA);
Value* v = f.Add(ra, f.LoadConstantInt64(XEEXTS16(i.D.DS)));
f.StoreGPR(i.D.RT, v);
f.StoreCA(AddDidCarry(f, ra, f.LoadConstantInt64(XEEXTS16(i.D.DS))));
return 0;
}
int InstrEmit_addicx(PPCHIRBuilder& f, const InstrData& i) {
// RT <- (RA) + EXTS(SI)
// CA <- carry bit
Value* ra = f.LoadGPR(i.D.RA);
Value* v = f.Add(f.LoadGPR(i.D.RA), f.LoadConstantInt64(XEEXTS16(i.D.DS)));
f.StoreGPR(i.D.RT, v);
f.StoreCA(AddDidCarry(f, ra, f.LoadConstantInt64(XEEXTS16(i.D.DS))));
f.UpdateCR(0, v);
return 0;
}
int InstrEmit_addis(PPCHIRBuilder& f, const InstrData& i) {
// if RA = 0 then
// RT <- EXTS(SI) || i16.0
// else
// RT <- (RA) + EXTS(SI) || i16.0
Value* si = f.LoadConstantInt64(XEEXTS16(i.D.DS) << 16);
Value* v = si;
if (i.D.RA) {
v = f.Add(f.LoadGPR(i.D.RA), si);
}
f.StoreGPR(i.D.RT, v);
return 0;
}
int InstrEmit_addmex(PPCHIRBuilder& f, const InstrData& i) {
// RT <- (RA) + CA - 1
// CA <- carry bit
Value* ra = f.LoadGPR(i.XO.RA);
Value* v = f.AddWithCarry(ra, f.LoadConstantInt64(-1), f.LoadCA());
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
// With XER[SO] update too.
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
XEINSTRNOTIMPLEMENTED();
} else {
// Just CA update.
f.StoreCA(AddWithCarryDidCarry(f, ra, f.LoadConstantInt64(-1), f.LoadCA()));
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_addzex(PPCHIRBuilder& f, const InstrData& i) {
// RT <- (RA) + CA
// CA <- carry bit
Value* ra = f.LoadGPR(i.XO.RA);
Value* v = f.AddWithCarry(ra, f.LoadZeroInt64(), f.LoadCA());
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
// With XER[SO] update too.
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
XEINSTRNOTIMPLEMENTED();
return 1;
} else {
// Just CA update.
f.StoreCA(AddWithCarryDidCarry(f, ra, f.LoadZeroInt64(), f.LoadCA()));
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_divdx(PPCHIRBuilder& f, const InstrData& i) {
// dividend <- (RA)
// divisor <- (RB)
// if divisor = 0 then
// if OE = 1 then
// XER[OV] <- 1
// return
// RT <- dividend ÷ divisor
Value* divisor = f.LoadGPR(i.XO.RB);
// TODO(benvanik): check if zero
// if OE=1, set XER[OV] = 1
// else skip the divide
Value* v = f.Div(f.LoadGPR(i.XO.RA), divisor);
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
// If we are OE=1 we need to clear the overflow bit.
// e.update_xer_with_overflow(e.get_uint64(0));
XEINSTRNOTIMPLEMENTED();
return 1;
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_divdux(PPCHIRBuilder& f, const InstrData& i) {
// dividend <- (RA)
// divisor <- (RB)
// if divisor = 0 then
// if OE = 1 then
// XER[OV] <- 1
// return
// RT <- dividend ÷ divisor
Value* divisor = f.LoadGPR(i.XO.RB);
// TODO(benvanik): check if zero
// if OE=1, set XER[OV] = 1
// else skip the divide
Value* v = f.Div(f.LoadGPR(i.XO.RA), divisor, ARITHMETIC_UNSIGNED);
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
// If we are OE=1 we need to clear the overflow bit.
// e.update_xer_with_overflow(e.get_uint64(0));
XEINSTRNOTIMPLEMENTED();
return 1;
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_divwx(PPCHIRBuilder& f, const InstrData& i) {
// dividend[0:31] <- (RA)[32:63]
// divisor[0:31] <- (RB)[32:63]
// if divisor = 0 then
// if OE = 1 then
// XER[OV] <- 1
// return
// RT[32:63] <- dividend ÷ divisor
// RT[0:31] <- undefined
Value* divisor = f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE);
// TODO(benvanik): check if zero
// if OE=1, set XER[OV] = 1
// else skip the divide
Value* v = f.Div(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), divisor);
v = f.ZeroExtend(v, INT64_TYPE);
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
// If we are OE=1 we need to clear the overflow bit.
// e.update_xer_with_overflow(e.get_uint64(0));
XEINSTRNOTIMPLEMENTED();
return 1;
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_divwux(PPCHIRBuilder& f, const InstrData& i) {
// dividend[0:31] <- (RA)[32:63]
// divisor[0:31] <- (RB)[32:63]
// if divisor = 0 then
// if OE = 1 then
// XER[OV] <- 1
// return
// RT[32:63] <- dividend ÷ divisor
// RT[0:31] <- undefined
Value* divisor = f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE);
// TODO(benvanik): check if zero
// if OE=1, set XER[OV] = 1
// else skip the divide
Value* v = f.Div(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), divisor,
ARITHMETIC_UNSIGNED);
v = f.ZeroExtend(v, INT64_TYPE);
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
// If we are OE=1 we need to clear the overflow bit.
// e.update_xer_with_overflow(e.get_uint64(0));
XEINSTRNOTIMPLEMENTED();
return 1;
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_mulhdx(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ((RA) × (RB) as 128)[0:63]
if (i.XO.OE) {
// With XER update.
XEINSTRNOTIMPLEMENTED();
return 1;
}
Value* v = f.MulHi(f.LoadGPR(i.XO.RA), f.LoadGPR(i.XO.RB));
f.StoreGPR(i.XO.RT, v);
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_mulhdux(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ((RA) × (RB) as 128)[0:63]
if (i.XO.OE) {
// With XER update.
XEINSTRNOTIMPLEMENTED();
return 1;
}
Value* v =
f.MulHi(f.LoadGPR(i.XO.RA), f.LoadGPR(i.XO.RB), ARITHMETIC_UNSIGNED);
f.StoreGPR(i.XO.RT, v);
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_mulhwx(PPCHIRBuilder& f, const InstrData& i) {
// RT[32:64] <- ((RA)[32:63] × (RB)[32:63])[0:31]
if (i.XO.OE) {
// With XER update.
XEINSTRNOTIMPLEMENTED();
return 1;
}
Value* v = f.SignExtend(f.MulHi(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE),
f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE)),
INT64_TYPE);
f.StoreGPR(i.XO.RT, v);
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_mulhwux(PPCHIRBuilder& f, const InstrData& i) {
// RT[32:64] <- ((RA)[32:63] × (RB)[32:63])[0:31]
if (i.XO.OE) {
// With XER update.
XEINSTRNOTIMPLEMENTED();
return 1;
}
Value* v = f.ZeroExtend(
f.MulHi(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE),
f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), ARITHMETIC_UNSIGNED),
INT64_TYPE);
f.StoreGPR(i.XO.RT, v);
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_mulldx(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ((RA) × (RB))[64:127]
if (i.XO.OE) {
// With XER update.
XEINSTRNOTIMPLEMENTED();
return 1;
}
Value* v = f.Mul(f.LoadGPR(i.XO.RA), f.LoadGPR(i.XO.RB));
f.StoreGPR(i.XO.RT, v);
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_mulli(PPCHIRBuilder& f, const InstrData& i) {
// prod[0:127] <- (RA) × EXTS(SI)
// RT <- prod[64:127]
Value* v = f.Mul(f.LoadGPR(i.D.RA), f.LoadConstantInt64(XEEXTS16(i.D.DS)));
f.StoreGPR(i.D.RT, v);
return 0;
}
int InstrEmit_mullwx(PPCHIRBuilder& f, const InstrData& i) {
// RT <- (RA)[32:63] × (RB)[32:63]
if (i.XO.OE) {
// With XER update.
XEINSTRNOTIMPLEMENTED();
return 1;
}
Value* v = f.Mul(
f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), INT64_TYPE),
f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), INT64_TYPE));
f.StoreGPR(i.XO.RT, v);
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_negx(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ¬(RA) + 1
if (i.XO.OE) {
// With XER update.
// This is a different codepath as we need to use llvm.ssub.with.overflow.
// if RA == 0x8000000000000000 then no-op and set OV=1
// This may just magically do that...
XEINSTRNOTIMPLEMENTED();
return 1;
// Function* ssub_with_overflow = Intrinsic::getDeclaration(
// e.gen_module(), Intrinsic::ssub_with_overflow, jit_type_nint);
// jit_value_t v = b.CreateCall2(ssub_with_overflow,
// e.get_int64(0), f.LoadGPR(i.XO.RA));
// jit_value_t v0 = b.CreateExtractValue(v, 0);
// f.StoreGPR(i.XO.RT, v0);
// e.update_xer_with_overflow(b.CreateExtractValue(v, 1));
// if (i.XO.Rc) {
// // With cr0 update.
// f.UpdateCR(0, v0, e.get_int64(0), true);
//}
} else {
// No OE bit setting.
Value* v = f.Neg(f.LoadGPR(i.XO.RA));
f.StoreGPR(i.XO.RT, v);
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
}
return 0;
}
int InstrEmit_subfx(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ¬(RA) + (RB) + 1
Value* v = f.Sub(f.LoadGPR(i.XO.RB), f.LoadGPR(i.XO.RA));
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
XEINSTRNOTIMPLEMENTED();
return 1;
// e.update_xer_with_overflow(EFLAGS??);
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_subfcx(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ¬(RA) + (RB) + 1
Value* ra = f.LoadGPR(i.XO.RA);
Value* rb = f.LoadGPR(i.XO.RB);
Value* v = f.Sub(rb, ra);
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
XEINSTRNOTIMPLEMENTED();
return 1;
// e.update_xer_with_overflow(EFLAGS??);
} else {
f.StoreCA(SubDidCarry(f, rb, ra));
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_subficx(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ¬(RA) + EXTS(SI) + 1
Value* ra = f.LoadGPR(i.D.RA);
Value* v = f.Sub(f.LoadConstantInt64(XEEXTS16(i.D.DS)), ra);
f.StoreGPR(i.D.RT, v);
f.StoreCA(SubDidCarry(f, f.LoadConstantInt64(XEEXTS16(i.D.DS)), ra));
return 0;
}
int InstrEmit_subfex(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ¬(RA) + (RB) + CA
Value* not_ra = f.Not(f.LoadGPR(i.XO.RA));
Value* rb = f.LoadGPR(i.XO.RB);
Value* v = f.AddWithCarry(not_ra, rb, f.LoadCA());
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
XEINSTRNOTIMPLEMENTED();
return 1;
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
} else {
f.StoreCA(AddWithCarryDidCarry(f, not_ra, rb, f.LoadCA()));
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_subfmex(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ¬(RA) + CA - 1
Value* not_ra = f.Not(f.LoadGPR(i.XO.RA));
Value* v = f.AddWithCarry(not_ra, f.LoadConstantInt64(-1), f.LoadCA());
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
XEINSTRNOTIMPLEMENTED();
return 1;
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
} else {
f.StoreCA(
AddWithCarryDidCarry(f, not_ra, f.LoadConstantInt64(-1), f.LoadCA()));
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_subfzex(PPCHIRBuilder& f, const InstrData& i) {
// RT <- ¬(RA) + CA
Value* not_ra = f.Not(f.LoadGPR(i.XO.RA));
Value* v = f.AddWithCarry(not_ra, f.LoadZeroInt64(), f.LoadCA());
f.StoreGPR(i.XO.RT, v);
if (i.XO.OE) {
XEINSTRNOTIMPLEMENTED();
return 1;
// e.update_xer_with_overflow_and_carry(b.CreateExtractValue(v, 1));
} else {
f.StoreCA(AddWithCarryDidCarry(f, not_ra, f.LoadZeroInt64(), f.LoadCA()));
}
if (i.XO.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
// Integer compare (A-4)
int InstrEmit_cmp(PPCHIRBuilder& f, const InstrData& i) {
// if L = 0 then
// a <- EXTS((RA)[32:63])
// b <- EXTS((RB)[32:63])
// else
// a <- (RA)
// b <- (RB)
// if a < b then
// c <- 0b100
// else if a > b then
// c <- 0b010
// else
// c <- 0b001
// CR[4×BF+32:4×BF+35] <- c || XER[SO]
uint32_t BF = i.X.RT >> 2;
uint32_t L = i.X.RT & 1;
Value* lhs;
Value* rhs;
if (L) {
lhs = f.LoadGPR(i.X.RA);
rhs = f.LoadGPR(i.X.RB);
} else {
lhs = f.Truncate(f.LoadGPR(i.X.RA), INT32_TYPE);
rhs = f.Truncate(f.LoadGPR(i.X.RB), INT32_TYPE);
}
f.UpdateCR(BF, lhs, rhs);
return 0;
}
int InstrEmit_cmpi(PPCHIRBuilder& f, const InstrData& i) {
// if L = 0 then
// a <- EXTS((RA)[32:63])
// else
// a <- (RA)
// if a < EXTS(SI) then
// c <- 0b100
// else if a > EXTS(SI) then
// c <- 0b010
// else
// c <- 0b001
// CR[4×BF+32:4×BF+35] <- c || XER[SO]
uint32_t BF = i.D.RT >> 2;
uint32_t L = i.D.RT & 1;
Value* lhs;
Value* rhs;
if (L) {
lhs = f.LoadGPR(i.D.RA);
rhs = f.LoadConstantInt64(XEEXTS16(i.D.DS));
} else {
lhs = f.Truncate(f.LoadGPR(i.D.RA), INT32_TYPE);
rhs = f.LoadConstantInt32(int32_t(XEEXTS16(i.D.DS)));
}
f.UpdateCR(BF, lhs, rhs);
return 0;
}
int InstrEmit_cmpl(PPCHIRBuilder& f, const InstrData& i) {
// if L = 0 then
// a <- i32.0 || (RA)[32:63]
// b <- i32.0 || (RB)[32:63]
// else
// a <- (RA)
// b <- (RB)
// if a <u b then
// c <- 0b100
// else if a >u b then
// c <- 0b010
// else
// c <- 0b001
// CR[4×BF+32:4×BF+35] <- c || XER[SO]
uint32_t BF = i.X.RT >> 2;
uint32_t L = i.X.RT & 1;
Value* lhs;
Value* rhs;
if (L) {
lhs = f.LoadGPR(i.X.RA);
rhs = f.LoadGPR(i.X.RB);
} else {
lhs = f.Truncate(f.LoadGPR(i.X.RA), INT32_TYPE);
rhs = f.Truncate(f.LoadGPR(i.X.RB), INT32_TYPE);
}
f.UpdateCR(BF, lhs, rhs, false);
return 0;
}
int InstrEmit_cmpli(PPCHIRBuilder& f, const InstrData& i) {
// if L = 0 then
// a <- i32.0 || (RA)[32:63]
// else
// a <- (RA)
// if a <u i48.0 || SI then
// c <- 0b100
// else if a >u i48.0 || SI then
// c <- 0b010
// else
// c <- 0b001
// CR[4×BF+32:4×BF+35] <- c || XER[SO]
uint32_t BF = i.D.RT >> 2;
uint32_t L = i.D.RT & 1;
Value* lhs;
Value* rhs;
if (L) {
lhs = f.LoadGPR(i.D.RA);
rhs = f.LoadConstantUint64(i.D.DS);
} else {
lhs = f.Truncate(f.LoadGPR(i.D.RA), INT32_TYPE);
rhs = f.LoadConstantUint32(i.D.DS);
}
f.UpdateCR(BF, lhs, rhs, false);
return 0;
}
// Integer logical (A-5)
int InstrEmit_andx(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) & (RB)
Value* ra = f.And(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB));
f.StoreGPR(i.X.RA, ra);
if (i.X.Rc) {
f.UpdateCR(0, ra);
}
return 0;
}
int InstrEmit_andcx(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) & ¬(RB)
Value* ra = f.AndNot(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB));
f.StoreGPR(i.X.RA, ra);
if (i.X.Rc) {
f.UpdateCR(0, ra);
}
return 0;
}
int InstrEmit_andix(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) & (i48.0 || UI)
Value* ra = f.And(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS)));
f.StoreGPR(i.D.RA, ra);
f.UpdateCR(0, ra);
return 0;
}
int InstrEmit_andisx(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) & (i32.0 || UI || i16.0)
Value* ra =
f.And(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS) << 16));
f.StoreGPR(i.D.RA, ra);
f.UpdateCR(0, ra);
return 0;
}
int InstrEmit_cntlzdx(PPCHIRBuilder& f, const InstrData& i) {
// n <- 0
// do while n < 64
// if (RS)[n] = 1 then leave n
// n <- n + 1
// RA <- n
Value* v = f.CountLeadingZeros(f.LoadGPR(i.X.RT));
v = f.ZeroExtend(v, INT64_TYPE);
f.StoreGPR(i.X.RA, v);
if (i.X.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_cntlzwx(PPCHIRBuilder& f, const InstrData& i) {
// n <- 32
// do while n < 64
// if (RS)[n] = 1 then leave n
// n <- n + 1
// RA <- n - 32
Value* v = f.CountLeadingZeros(f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE));
v = f.ZeroExtend(v, INT64_TYPE);
f.StoreGPR(i.X.RA, v);
if (i.X.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_eqvx(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) == (RB)
Value* ra = f.Not(f.Xor(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB)));
f.StoreGPR(i.X.RA, ra);
if (i.X.Rc) {
f.UpdateCR(0, ra);
}
return 0;
}
int InstrEmit_extsbx(PPCHIRBuilder& f, const InstrData& i) {
// s <- (RS)[56]
// RA[56:63] <- (RS)[56:63]
// RA[0:55] <- i56.s
Value* rt = f.LoadGPR(i.X.RT);
rt = f.SignExtend(f.Truncate(rt, INT8_TYPE), INT64_TYPE);
f.StoreGPR(i.X.RA, rt);
if (i.X.Rc) {
f.UpdateCR(0, rt);
}
return 0;
}
int InstrEmit_extshx(PPCHIRBuilder& f, const InstrData& i) {
// s <- (RS)[48]
// RA[48:63] <- (RS)[48:63]
// RA[0:47] <- 48.s
Value* rt = f.LoadGPR(i.X.RT);
rt = f.SignExtend(f.Truncate(rt, INT16_TYPE), INT64_TYPE);
f.StoreGPR(i.X.RA, rt);
if (i.X.Rc) {
f.UpdateCR(0, rt);
}
return 0;
}
int InstrEmit_extswx(PPCHIRBuilder& f, const InstrData& i) {
// s <- (RS)[32]
// RA[32:63] <- (RS)[32:63]
// RA[0:31] <- i32.s
Value* rt = f.LoadGPR(i.X.RT);
rt = f.SignExtend(f.Truncate(rt, INT32_TYPE), INT64_TYPE);
f.StoreGPR(i.X.RA, rt);
if (i.X.Rc) {
f.UpdateCR(0, rt);
}
return 0;
}
int InstrEmit_nandx(PPCHIRBuilder& f, const InstrData& i) {
// RA <- ¬((RS) & (RB))
Value* ra = f.Not(f.And(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB)));
f.StoreGPR(i.X.RA, ra);
if (i.X.Rc) {
f.UpdateCR(0, ra);
}
return 0;
}
int InstrEmit_norx(PPCHIRBuilder& f, const InstrData& i) {
// RA <- ¬((RS) | (RB))
Value* ra = f.Not(f.Or(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB)));
f.StoreGPR(i.X.RA, ra);
if (i.X.Rc) {
f.UpdateCR(0, ra);
}
return 0;
}
int InstrEmit_orx(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) | (RB)
if (i.X.RT == i.X.RB && i.X.RT == i.X.RA && !i.X.Rc) {
// Sometimes used as no-op.
f.Nop();
return 0;
}
Value* ra;
if (i.X.RT == i.X.RB) {
ra = f.LoadGPR(i.X.RT);
} else {
ra = f.Or(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB));
}
f.StoreGPR(i.X.RA, ra);
if (i.X.Rc) {
f.UpdateCR(0, ra);
}
return 0;
}
int InstrEmit_orcx(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) | ¬(RB)
Value* ra = f.Or(f.LoadGPR(i.X.RT), f.Not(f.LoadGPR(i.X.RB)));
f.StoreGPR(i.X.RA, ra);
if (i.X.Rc) {
f.UpdateCR(0, ra);
}
return 0;
}
int InstrEmit_ori(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) | (i48.0 || UI)
if (!i.D.RA && !i.D.RT && !i.D.DS) {
f.Nop();
return 0;
}
Value* ra = f.Or(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS)));
f.StoreGPR(i.D.RA, ra);
return 0;
}
int InstrEmit_oris(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) | (i32.0 || UI || i16.0)
Value* ra =
f.Or(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS) << 16));
f.StoreGPR(i.D.RA, ra);
return 0;
}
int InstrEmit_xorx(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) XOR (RB)
Value* ra = f.Xor(f.LoadGPR(i.X.RT), f.LoadGPR(i.X.RB));
f.StoreGPR(i.X.RA, ra);
if (i.X.Rc) {
f.UpdateCR(0, ra);
}
return 0;
}
int InstrEmit_xori(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) XOR (i48.0 || UI)
Value* ra = f.Xor(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS)));
f.StoreGPR(i.D.RA, ra);
return 0;
}
int InstrEmit_xoris(PPCHIRBuilder& f, const InstrData& i) {
// RA <- (RS) XOR (i32.0 || UI || i16.0)
Value* ra =
f.Xor(f.LoadGPR(i.D.RT), f.LoadConstantUint64(XEEXTZ16(i.D.DS) << 16));
f.StoreGPR(i.D.RA, ra);
return 0;
}
// Integer rotate (A-6)
int InstrEmit_rldclx(PPCHIRBuilder& f, const InstrData& i) {
// n <- rB[58:63]
// r <- ROTL[64](rS, n)
// b <- mb[5] || mb[0:4]
// m <- MASK(b, 63)
// rA <- r & m
Value* n = f.And(f.Truncate(f.LoadGPR(i.MDS.RB), INT8_TYPE),
f.LoadConstantInt8(0x3F));
uint32_t mb = (i.MDS.MB5 << 5) | i.MDS.MB;
uint64_t m = XEMASK(mb, 63);
Value* v = f.LoadGPR(i.MDS.RT);
v = f.RotateLeft(v, n);
if (m != 0xFFFFFFFFFFFFFFFF) {
v = f.And(v, f.LoadConstantUint64(m));
}
f.StoreGPR(i.MDS.RA, v);
if (i.MDS.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_rldcrx(PPCHIRBuilder& f, const InstrData& i) {
// n <- rB[58:63]
// r <- ROTL[64](rS, n)
// b <- mb[5] || mb[0:4]
// m <- MASK(0, b)
// rA <- r & m
Value* n = f.And(f.Truncate(f.LoadGPR(i.MDS.RB), INT8_TYPE),
f.LoadConstantInt8(0x3F));
uint32_t mb = (i.MDS.MB5 << 5) | i.MDS.MB;
uint64_t m = XEMASK(0, mb);
Value* v = f.LoadGPR(i.MDS.RT);
v = f.RotateLeft(v, n);
if (m != 0xFFFFFFFFFFFFFFFF) {
v = f.And(v, f.LoadConstantUint64(m));
}
f.StoreGPR(i.MDS.RA, v);
if (i.MDS.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_rldicx(PPCHIRBuilder& f, const InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
int InstrEmit_rldiclx(PPCHIRBuilder& f, const InstrData& i) {
// n <- sh[5] || sh[0:4]
// r <- ROTL64((RS), n)
// b <- mb[5] || mb[0:4]
// m <- MASK(b, 63)
// RA <- r & m
uint32_t sh = (i.MD.SH5 << 5) | i.MD.SH;
uint32_t mb = (i.MD.MB5 << 5) | i.MD.MB;
uint64_t m = XEMASK(mb, 63);
Value* v = f.LoadGPR(i.MD.RT);
if (sh == 64 - mb) {
// srdi == rldicl ra,rs,64-n,n
v = f.Shr(v, int8_t(mb));
} else {
if (sh) {
v = f.RotateLeft(v, f.LoadConstantInt8(sh));
}
if (m != 0xFFFFFFFFFFFFFFFF) {
v = f.And(v, f.LoadConstantUint64(m));
}
}
f.StoreGPR(i.MD.RA, v);
if (i.MD.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_rldicrx(PPCHIRBuilder& f, const InstrData& i) {
// n <- sh[5] || sh[0:4]
// r <- ROTL64((RS), n)
// e <- me[5] || me[0:4]
// m <- MASK(0, e)
// RA <- r & m
uint32_t sh = (i.MD.SH5 << 5) | i.MD.SH;
uint32_t mb = (i.MD.MB5 << 5) | i.MD.MB;
uint64_t m = XEMASK(0, mb);
Value* v = f.LoadGPR(i.MD.RT);
if (mb == 63 - sh) {
// sldi == rldicr ra,rs,n,63-n
v = f.Shl(v, int8_t(sh));
} else {
if (sh) {
v = f.RotateLeft(v, f.LoadConstantInt8(sh));
}
if (m != 0xFFFFFFFFFFFFFFFF) {
v = f.And(v, f.LoadConstantUint64(m));
}
}
f.StoreGPR(i.MD.RA, v);
if (i.MD.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_rldimix(PPCHIRBuilder& f, const InstrData& i) {
// n <- sh[5] || sh[0:4]
// r <- ROTL64((RS), n)
// b <- me[5] || me[0:4]
// m <- MASK(b, ¬n)
// RA <- (r & m) | ((RA)&¬m)
uint32_t sh = (i.MD.SH5 << 5) | i.MD.SH;
uint32_t mb = (i.MD.MB5 << 5) | i.MD.MB;
uint64_t m = XEMASK(mb, ~sh);
Value* v = f.LoadGPR(i.MD.RT);
if (sh) {
v = f.RotateLeft(v, f.LoadConstantInt8(sh));
}
if (m != 0xFFFFFFFFFFFFFFFF) {
Value* ra = f.LoadGPR(i.MD.RA);
v = f.Or(f.And(v, f.LoadConstantUint64(m)),
f.And(ra, f.LoadConstantUint64(~m)));
}
f.StoreGPR(i.MD.RA, v);
if (i.MD.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_rlwimix(PPCHIRBuilder& f, const InstrData& i) {
// n <- SH
// r <- ROTL32((RS)[32:63], n)
// m <- MASK(MB+32, ME+32)
// RA <- r&m | (RA)&¬m
Value* v = f.LoadGPR(i.M.RT);
// (x||x)
v = f.Or(f.Shl(v, 32), f.ZeroExtend(f.Truncate(v, INT32_TYPE), INT64_TYPE));
if (i.M.SH) {
v = f.RotateLeft(v, f.LoadConstantInt8(i.M.SH));
}
// Compiler sometimes masks with 0xFFFFFFFF (identity) - avoid the work here
// as our truncation/zero-extend does it for us.
uint64_t m = XEMASK(i.M.MB + 32, i.M.ME + 32);
if (m != 0xFFFFFFFFFFFFFFFFull) {
v = f.And(v, f.LoadConstantUint64(m));
}
v = f.Or(v, f.And(f.LoadGPR(i.M.RA), f.LoadConstantUint64(~m)));
f.StoreGPR(i.M.RA, v);
if (i.M.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
static bool InstrCheck_rlx_only_needs_low(unsigned rotation, uint64_t mask) {
uint32_t mask32 = static_cast<uint32_t>(mask);
if (static_cast<uint64_t>(mask32) != mask) {
return false;
}
uint32_t all_ones_32 = ~0U;
all_ones_32 <<= rotation;
return all_ones_32 == mask32; // mask is only 32 bits and all bits from the
// rotation are discarded
}
int InstrEmit_rlwinmx(PPCHIRBuilder& f, const InstrData& i) {
// n <- SH
// r <- ROTL32((RS)[32:63], n)
// m <- MASK(MB+32, ME+32)
// RA <- r & m
Value* v = f.LoadGPR(i.M.RT);
unsigned rotation = i.M.SH;
uint64_t m = XEMASK(i.M.MB + 32, i.M.ME + 32);
// in uint32 range (so no register concat/truncate/zx needed) and no rotation
if (m < (1ULL << 32) && (rotation == 0)) {
v = f.And(v, f.LoadConstantUint64(m));
}
// masks out all the bits that are rotated in from the right, so just do a
// shift + and. the and with 0xFFFFFFFF is done instead of a truncate/zx
// because we have a special case for it in the emitters that will just do a
// single insn (mov reg32, lowpartofreg64), otherwise we generate
// significantly more code from setting up the opnds of the truncate/zx
else if (InstrCheck_rlx_only_needs_low(rotation, m)) {
// this path is taken for like 90% of all rlwinms
v = f.And(f.Shl(v, rotation), f.LoadConstantUint64(0xFFFFFFFF));
}
else {
// (x||x)
// cs: changed this to mask with UINT32_MAX instead of doing the
// truncate/extend, this generates better code in the backend and is easier
// to do analysis on
v = f.And(v, f.LoadConstantUint64(0xFFFFFFFF));
v = f.Or(f.Shl(v, 32), v);
// TODO(benvanik): optimize srwi
// TODO(benvanik): optimize slwi
// The compiler will generate a bunch of these for the special case of SH=0.
// Which seems to just select some bits and set cr0 for use with a branch.
// We can detect this and do less work.
if (i.M.SH) {
v = f.RotateLeft(v, f.LoadConstantInt8(rotation));
}
// Compiler sometimes masks with 0xFFFFFFFF (identity) - avoid the work here
// as our truncation/zero-extend does it for us.
if (m != 0xFFFFFFFFFFFFFFFFull) {
v = f.And(v, f.LoadConstantUint64(m));
}
}
f.StoreGPR(i.M.RA, v);
if (i.M.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_rlwnmx(PPCHIRBuilder& f, const InstrData& i) {
// n <- (RB)[59:63]
// r <- ROTL32((RS)[32:63], n)
// m <- MASK(MB+32, ME+32)
// RA <- r & m
Value* sh =
f.And(f.Truncate(f.LoadGPR(i.M.SH), INT8_TYPE), f.LoadConstantInt8(0x1F));
Value* v = f.LoadGPR(i.M.RT);
// (x||x)
v = f.Or(f.Shl(v, 32), f.ZeroExtend(f.Truncate(v, INT32_TYPE), INT64_TYPE));
v = f.RotateLeft(v, sh);
v = f.And(v, f.LoadConstantUint64(XEMASK(i.M.MB + 32, i.M.ME + 32)));
f.StoreGPR(i.M.RA, v);
if (i.M.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
// Integer shift (A-7)
int InstrEmit_sldx(PPCHIRBuilder& f, const InstrData& i) {
// n <- (RB)[58:63]
// r <- ROTL64((RS), n)
// if (RB)[57] = 0 then
// m <- MASK(0, 63-n)
// else
// m <- i64.0
// RA <- r & m
Value* sh =
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x7F));
Value* v = f.Select(f.IsTrue(f.Shr(sh, 6)), f.LoadZeroInt64(),
f.Shl(f.LoadGPR(i.X.RT), sh));
f.StoreGPR(i.X.RA, v);
if (i.X.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_slwx(PPCHIRBuilder& f, const InstrData& i) {
// n <- (RB)[59:63]
// r <- ROTL32((RS)[32:63], n)
// if (RB)[58] = 0 then
// m <- MASK(32, 63-n)
// else
// m <- i64.0
// RA <- r & m
Value* sh =
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x3F));
Value* v = f.Select(f.IsTrue(f.Shr(sh, 5)), f.LoadZeroInt32(),
f.Shl(f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE), sh));
v = f.ZeroExtend(v, INT64_TYPE);
f.StoreGPR(i.X.RA, v);
if (i.X.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_srdx(PPCHIRBuilder& f, const InstrData& i) {
// n <- (RB)[58:63]
// r <- ROTL64((RS), 64-n)
// if (RB)[57] = 0 then
// m <- MASK(n, 63)
// else
// m <- i64.0
// RA <- r & m
Value* sh =
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x7F));
Value* v = f.Select(f.IsTrue(f.And(sh, f.LoadConstantInt8(0x40))),
f.LoadZeroInt64(), f.Shr(f.LoadGPR(i.X.RT), sh));
f.StoreGPR(i.X.RA, v);
if (i.X.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_srwx(PPCHIRBuilder& f, const InstrData& i) {
// n <- (RB)[59:63]
// r <- ROTL32((RS)[32:63], 64-n)
// if (RB)[58] = 0 then
// m <- MASK(n+32, 63)
// else
// m <- i64.0
// RA <- r & m
Value* sh =
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x3F));
Value* v =
f.Select(f.IsTrue(f.And(sh, f.LoadConstantInt8(0x20))), f.LoadZeroInt32(),
f.Shr(f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE), sh));
v = f.ZeroExtend(v, INT64_TYPE);
f.StoreGPR(i.X.RA, v);
if (i.X.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_sradx(PPCHIRBuilder& f, const InstrData& i) {
// n <- rB[58-63]
// r <- ROTL[64](rS, 64 - n)
// if rB[57] = 0 then m ← MASK(n, 63)
// else m ← (64)0
// S ← rS[0]
// rA <- (r & m) | (((64)S) & ¬ m)
// XER[CA] <- S & ((r & ¬ m) ¦ 0)
// if n == 0: rA <- rS, XER[CA] = 0
// if n >= 64: rA <- 64 sign bits of rS, XER[CA] = sign bit of rS
Value* rt = f.LoadGPR(i.X.RT);
Value* sh =
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x7F));
Value* clamp_sh = f.Min(sh, f.LoadConstantInt8(0x3F));
Value* v = f.Sha(rt, clamp_sh);
// CA is set if any bits are shifted out of the right and if the result
// is negative.
Value* ca =
f.And(f.IsTrue(f.Shr(rt, 63)), f.CompareNE(f.Shl(v, clamp_sh), rt));
f.StoreCA(ca);
f.StoreGPR(i.X.RA, v);
if (i.X.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_sradix(PPCHIRBuilder& f, const InstrData& i) {
// n <- sh[5] || sh[0-4]
// r <- ROTL[64](rS, 64 - n)
// m ← MASK(n, 63)
// S ← rS[0]
// rA <- (r & m) | (((64)S) & ¬ m)
// XER[CA] <- S & ((r & ¬ m) ¦ 0)
// if n == 0: rA <- rS, XER[CA] = 0
// if n >= 64: rA <- 64 sign bits of rS, XER[CA] = sign bit of rS
Value* v = f.LoadGPR(i.XS.RT);
int8_t sh = (i.XS.SH5 << 5) | i.XS.SH;
// CA is set if any bits are shifted out of the right and if the result
// is negative.
if (sh) {
uint64_t mask = XEMASK(64 - sh, 63);
Value* ca = f.And(f.Truncate(f.Shr(v, 63), INT8_TYPE),
f.IsTrue(f.And(v, f.LoadConstantUint64(mask))));
f.StoreCA(ca);
v = f.Sha(v, sh);
} else {
f.StoreCA(f.LoadZeroInt8());
}
f.StoreGPR(i.XS.RA, v);
if (i.XS.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_srawx(PPCHIRBuilder& f, const InstrData& i) {
// n <- rB[59-63]
// r <- ROTL32((RS)[32:63], 64-n)
// m <- MASK(n+32, 63)
// s <- (RS)[32]
// RA <- r&m | (i64.s)&¬m
// CA <- s & ((r&¬m)[32:63]≠0)
// if n == 0: rA <- sign_extend(rS), XER[CA] = 0
// if n >= 32: rA <- 64 sign bits of rS, XER[CA] = sign bit of lo_32(rS)
Value* rt = f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE);
Value* sh =
f.And(f.Truncate(f.LoadGPR(i.X.RB), INT8_TYPE), f.LoadConstantInt8(0x3F));
Value* clamp_sh = f.Min(sh, f.LoadConstantInt8(0x1F));
Value* v = f.Sha(rt, f.Min(sh, clamp_sh));
// CA is set if any bits are shifted out of the right and if the result
// is negative.
Value* ca =
f.And(f.IsTrue(f.Shr(rt, 31)), f.CompareNE(f.Shl(v, clamp_sh), rt));
f.StoreCA(ca);
v = f.SignExtend(v, INT64_TYPE);
f.StoreGPR(i.X.RA, v);
if (i.X.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
int InstrEmit_srawix(PPCHIRBuilder& f, const InstrData& i) {
// n <- SH
// r <- ROTL32((RS)[32:63], 64-n)
// m <- MASK(n+32, 63)
// s <- (RS)[32]
// RA <- r&m | (i64.s)&¬m
// CA <- s & ((r&¬m)[32:63]≠0)
// if n == 0: rA <- sign_extend(rS), XER[CA] = 0
// if n >= 32: rA <- 64 sign bits of rS, XER[CA] = sign bit of lo_32(rS)
Value* v = f.Truncate(f.LoadGPR(i.X.RT), INT32_TYPE);
Value* ca;
if (!i.X.RB) {
// No shift, just a fancy sign extend and CA clearer.
v = f.SignExtend(v, INT64_TYPE);
ca = f.LoadZeroInt8();
} else {
// CA is set if any bits are shifted out of the right and if the result
// is negative.
uint32_t mask = (uint32_t)XEMASK(64 - i.X.RB, 63);
if (mask == 1) {
ca = f.And(f.CompareSLT(v, f.LoadConstantInt32(0)),
f.Truncate(v, INT8_TYPE));
} else {
ca = f.And(f.CompareSLT(v, f.LoadConstantInt32(0)),
f.IsTrue(f.And(v, f.LoadConstantUint32(mask))));
}
v = f.Sha(v, (int8_t)i.X.RB), v = f.SignExtend(v, INT64_TYPE);
}
f.StoreCA(ca);
f.StoreGPR(i.X.RA, v);
if (i.X.Rc) {
f.UpdateCR(0, v);
}
return 0;
}
void RegisterEmitCategoryALU() {
XEREGISTERINSTR(addx);
XEREGISTERINSTR(addcx);
XEREGISTERINSTR(addex);
XEREGISTERINSTR(addi);
XEREGISTERINSTR(addic);
XEREGISTERINSTR(addicx);
XEREGISTERINSTR(addis);
XEREGISTERINSTR(addmex);
XEREGISTERINSTR(addzex);
XEREGISTERINSTR(divdx);
XEREGISTERINSTR(divdux);
XEREGISTERINSTR(divwx);
XEREGISTERINSTR(divwux);
XEREGISTERINSTR(mulhdx);
XEREGISTERINSTR(mulhdux);
XEREGISTERINSTR(mulhwx);
XEREGISTERINSTR(mulhwux);
XEREGISTERINSTR(mulldx);
XEREGISTERINSTR(mulli);
XEREGISTERINSTR(mullwx);
XEREGISTERINSTR(negx);
XEREGISTERINSTR(subfx);
XEREGISTERINSTR(subfcx);
XEREGISTERINSTR(subficx);
XEREGISTERINSTR(subfex);
XEREGISTERINSTR(subfmex);
XEREGISTERINSTR(subfzex);
XEREGISTERINSTR(cmp);
XEREGISTERINSTR(cmpi);
XEREGISTERINSTR(cmpl);
XEREGISTERINSTR(cmpli);
XEREGISTERINSTR(andx);
XEREGISTERINSTR(andcx);
XEREGISTERINSTR(andix);
XEREGISTERINSTR(andisx);
XEREGISTERINSTR(cntlzdx);
XEREGISTERINSTR(cntlzwx);
XEREGISTERINSTR(eqvx);
XEREGISTERINSTR(extsbx);
XEREGISTERINSTR(extshx);
XEREGISTERINSTR(extswx);
XEREGISTERINSTR(nandx);
XEREGISTERINSTR(norx);
XEREGISTERINSTR(orx);
XEREGISTERINSTR(orcx);
XEREGISTERINSTR(ori);
XEREGISTERINSTR(oris);
XEREGISTERINSTR(xorx);
XEREGISTERINSTR(xori);
XEREGISTERINSTR(xoris);
XEREGISTERINSTR(rldclx);
XEREGISTERINSTR(rldcrx);
XEREGISTERINSTR(rldicx);
XEREGISTERINSTR(rldiclx);
XEREGISTERINSTR(rldicrx);
XEREGISTERINSTR(rldimix);
XEREGISTERINSTR(rlwimix);
XEREGISTERINSTR(rlwinmx);
XEREGISTERINSTR(rlwnmx);
XEREGISTERINSTR(sldx);
XEREGISTERINSTR(slwx);
XEREGISTERINSTR(srdx);
XEREGISTERINSTR(srwx);
XEREGISTERINSTR(sradx);
XEREGISTERINSTR(sradix);
XEREGISTERINSTR(srawx);
XEREGISTERINSTR(srawix);
}
} // namespace ppc
} // namespace cpu
} // namespace xe