1353 lines
35 KiB
C++
1353 lines
35 KiB
C++
/*
|
||
******************************************************************************
|
||
* 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;
|
||
}
|
||
|
||
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);
|
||
|
||
// (x||x)
|
||
v = f.Or(f.Shl(v, 32), f.ZeroExtend(f.Truncate(v, INT32_TYPE), INT64_TYPE));
|
||
|
||
// 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(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));
|
||
}
|
||
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
|