Files
Xenia-Canary/src/xenia/cpu/ppc/ppc_emit_alu.cc
chss95cs@gmail.com 327cc9eff5 drastically reduce size of final generated code for rlwinm by adding special paths for rotations of 0, masks that discard the rotated bits and using And w/ UINT_MAX instead of truncate/zero extend
Add special case to TYPE_INT64's EmitAnd for UINT_MAX mask. Do mov32 to 32 if detected to take advantage of implicit zero xt/reg renaming

Add helper function for skipping assignment defs in instr.
Add helper function for checking if an opcode is binary value type
Add several new optimizations to simplificationpass, plus weak NZM calculation code (better full evaluation of Z/NZ will be done later) .
 List of optimizations:
  If a value is anded with a bitmask that it was already masked against, reuse the old value (this cuts out most FPSCR update garbage, although it does cause a local variable to be allocated for the masked FPSCR and it still repeatedly stores the masked value to the context)
  If masking a value that was or'ed against another check whether our mask only considers bits from one value or another. if so, change the operand to the OR input that actually matters
  If the only usage of a rotate left's output is an AND against a mask that discards the bits that were rotated in change the opcode to SHIFT_LEFT
  If masking against all ones, become an assign.
  If XOR or OR against 0, become an assign (additional FPSCR codegen cleanup)
  If XOR against all ones, become a NOT
Adding a direct CPUID check to x64_emitter for lzcnt, the version of xbyak we are using is skipping checking for lzcnt on all non-intel cpus, meaning we are generating the much slower bitscan path for AMD cpus.
2022-06-25 09:58:13 -07:00

1388 lines
36 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) {
return f.Or(f.CompareUGT(f.Truncate(v1, INT32_TYPE),
f.Not(f.Neg(f.Truncate(v2, INT32_TYPE)))),
f.IsFalse(f.Truncate(v2, INT32_TYPE)));
}
// 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);
ca = f.And(f.Truncate(f.Shr(v, 31), INT8_TYPE),
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