/*
 ******************************************************************************
 * 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);
  f.StoreCA(AddWithCarryDidCarry(f, ra, rb, f.LoadCA()));
  if (i.XO.Rc) {
    f.UpdateCR(0, v);
  }
  if (i.XO.OE) {
    // Stub implementation.
    // TODO: Handle overflow flag.
    // NOTE: 535507D4 (Raiden Fighters Aces) never seems to rely on this
    //   behavior either, despite OE being set.
  }
  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();
  } 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();
  }
  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();
  }
  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();
  }
  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();
  }
  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();
  }
  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();
  }
  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();
  }
  Value* ratrunc =
      f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), INT64_TYPE);

  Value* rbtrunc =
      f.SignExtend(f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), INT64_TYPE);

  Value* v = f.Sha(f.Mul(ratrunc, rbtrunc), 32);

  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();
  }

  Value* ratrunc =
      f.ZeroExtend(f.Truncate(f.LoadGPR(i.XO.RA), INT32_TYPE), INT64_TYPE);

  Value* rbtrunc =
      f.ZeroExtend(f.Truncate(f.LoadGPR(i.XO.RB), INT32_TYPE), INT64_TYPE);

  Value* v = f.Shr(f.Mul(ratrunc, rbtrunc, ARITHMETIC_UNSIGNED), 32);
  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();
  }
  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();
  }
  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) {
    // Stub implementation.
    // TODO: Handle overflow flag for XER.
    // NOTE: 535507D4 (Raiden Fighters Aces) never seems to rely on this
    //   behavior, despite having OE set.
    Value* v = f.LoadGPR(i.XO.RA);
    if (v->AsUint64() == 0x8000000000000000) {
      f.StoreGPR(i.XO.RT, v);
      return 0;
    }
  }
  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();
    // 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();
    // 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();
    // 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();
    // 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();
    // 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) {
    // chrispy: this special version of orx is db16cyc and is heavily used in
    // spinlocks. since we do not emit any code for this we end up wasting a ton
    // of power
    if (i.code == 0x7FFFFB78) {
      f.DelayExecution();
    } else {
      // 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) {
  // n <- sh[5] || sh[0:4]
  // r <- ROTL64(RS, n)
  // b <- mb[5] || mb[0:4]
  // m <- MASK(b, ~n)
  // RA <- r & m
  // TODO: Check if this makes any sense. It works in 535507D4,
  //   but it's the only title I could find that uses this instruction.
  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));
  }
  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_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
