Files
wiicompiled/translator/tests/Translator.Tests/PpcLifterCoverageTests.cs
T
patchzyy ec226e8348 init
2026-08-23 17:10:50 +02:00

679 lines
34 KiB
C#

using System;
using System.Linq;
using System.Reflection;
using Translator.Core.Disassembly;
using Translator.Core.Ir;
using Translator.Core.Lifting;
using Xunit;
namespace Translator.Tests;
public class PpcLifterCoverageTests
{
private static PpcRegisterOperand Gpr(int index) => new($"r{index}", index);
private static readonly Type LifterType = typeof(PpcLifter);
private static PpcInstruction Instruction(uint raw, string mnemonic, params PpcOperand[] operands)
=> PpcInstruction.Synthetic(0x80000000, raw, mnemonic, operands);
private static T InvokePrivate<T>(string name, params object?[] args)
{
var method = LifterType.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static);
Assert.NotNull(method);
return (T)method!.Invoke(null, args)!;
}
[Fact]
public void NormalizesCrBitStyleConditionRegistersDuringCompare()
{
var instruction = Instruction(
0x7C032800,
"cmpw",
new PpcOperand[]
{
new PpcConditionRegisterOperand("cr28", 28),
Gpr(3),
Gpr(5)
});
var ir = Assert.Single(new PpcLifter().Lift(new[] { instruction })).Ir;
var setCr = Assert.IsType<IrSetCrField>(Assert.Single(ir));
Assert.Equal(7, setCr.FieldIndex);
Assert.Equal("r3", setCr.Left.RegisterName);
Assert.Equal("r5", setCr.Right.RegisterName);
}
[Fact]
public void LiftsXoriDotAndBarrierInstructions()
{
var lifter = new PpcLifter();
var nop = Instruction(0, "nop");
Assert.Empty(Assert.Single(lifter.Lift(new[] { nop })).Ir);
var xori = Instruction(0, "xori.", Gpr(4), Gpr(5), new PpcImmediateOperand(-1));
var xoriIr = Assert.Single(lifter.Lift(new[] { xori })).Ir;
var xoriBinary = Assert.IsType<IrBinary>(xoriIr[0]);
Assert.Equal("r4", xoriBinary.Destination);
Assert.Equal("xor", xoriBinary.Op);
Assert.Equal(0xFFFF, xoriBinary.Right.Constant);
Assert.IsType<IrSetCrField>(xoriIr[1]);
var sync = Instruction(0, "sync");
var syncIr = Assert.Single(lifter.Lift(new[] { sync })).Ir;
Assert.Contains("sync", Assert.IsType<IrComment>(Assert.Single(syncIr)).Text, StringComparison.Ordinal);
}
[Fact]
public void LiftsAliasingCarryInstructions()
{
var lifter = new PpcLifter();
var addc = Instruction(0, "addc.", Gpr(3), Gpr(3), Gpr(4));
var addcIr = Assert.Single(lifter.Lift(new[] { addc })).Ir;
var addcTemp = Assert.IsType<IrAssign>(addcIr[0]);
Assert.Equal("r3_addc_left", addcTemp.Destination);
Assert.Equal("r3", addcTemp.Value.RegisterName);
Assert.Equal("add", Assert.IsType<IrBinary>(addcIr[1]).Op);
Assert.Equal("PPC_UpdateCarryAdd", Assert.IsType<IrCall>(addcIr[2]).Target);
Assert.IsType<IrSetCrField>(addcIr[3]);
var subfc = Instruction(0, "subfc.", Gpr(5), Gpr(1), Gpr(5));
var subfcIr = Assert.Single(lifter.Lift(new[] { subfc })).Ir;
var subfcTemp = Assert.IsType<IrAssign>(subfcIr[0]);
Assert.Equal("r5_subfc_min", subfcTemp.Destination);
Assert.Equal("r5", subfcTemp.Value.RegisterName);
Assert.Equal("sub", Assert.IsType<IrBinary>(subfcIr[1]).Op);
Assert.Equal("PPC_UpdateCarrySub", Assert.IsType<IrCall>(subfcIr[2]).Target);
Assert.IsType<IrSetCrField>(subfcIr[3]);
var lmw = Instruction(0, "lmw", Gpr(30), new PpcDisplacementOperand(8, "r4", 4));
var lmwIr = Assert.Single(lifter.Lift(new[] { lmw })).Ir;
Assert.Collection(
lmwIr,
op => Assert.Equal("r30", Assert.IsType<IrLoad>(op).Destination),
op => Assert.Equal("r31", Assert.IsType<IrLoad>(op).Destination));
}
[Fact]
public void LiftsIndexedByteUpdateFormsAndRejectsZeroBase()
{
var lifter = new PpcLifter();
var lbzux = Instruction((3u << 21) | (4u << 16) | (5u << 11), "lbzux");
var lbzuxIr = Assert.Single(lifter.Lift(new[] { lbzux })).Ir;
Assert.Equal(3, lbzuxIr.Count);
Assert.Equal("addr_lbzux_80000000_loc", Assert.IsType<IrBinary>(lbzuxIr[0]).Destination);
Assert.Equal("r3", Assert.IsType<IrLoad>(lbzuxIr[1]).Destination);
Assert.Equal("r4", Assert.IsType<IrAssign>(lbzuxIr[2]).Destination);
var stbux = Instruction((2u << 21) | (4u << 16) | (5u << 11), "stbux");
var stbuxIr = Assert.Single(lifter.Lift(new[] { stbux })).Ir;
Assert.Equal(3, stbuxIr.Count);
Assert.Equal("addr_stbux_80000000_loc", Assert.IsType<IrBinary>(stbuxIr[0]).Destination);
Assert.IsType<IrStore>(stbuxIr[1]);
Assert.Equal("r4", Assert.IsType<IrAssign>(stbuxIr[2]).Destination);
var invalidLbzux = Instruction((1u << 21) | (0u << 16) | (5u << 11), "lbzux");
Assert.Throws<InvalidOperationException>(() => lifter.Lift(new[] { invalidLbzux }));
var invalidStbux = Instruction((1u << 21) | (0u << 16) | (5u << 11), "stbux");
Assert.Throws<InvalidOperationException>(() => lifter.Lift(new[] { invalidStbux }));
}
[Fact]
public void LiftsChainedCarryAndZeroingVariants()
{
var lifter = new PpcLifter();
var adde = Instruction(0, "adde.", Gpr(7), Gpr(7), Gpr(8));
var addeIr = Assert.Single(lifter.Lift(new[] { adde })).Ir;
Assert.Equal("r7_adde_left", Assert.IsType<IrAssign>(addeIr[0]).Destination);
Assert.Equal("PPC_GetCarry", Assert.IsType<IrCall>(addeIr[1]).Target);
Assert.Equal("add", Assert.IsType<IrBinary>(addeIr[2]).Op);
Assert.Equal("add", Assert.IsType<IrBinary>(addeIr[3]).Op);
Assert.Equal("PPC_UpdateCarryAdd", Assert.IsType<IrCall>(addeIr[4]).Target);
Assert.IsType<IrSetCrField>(addeIr[5]);
var addze = Instruction(0, "addze.", Gpr(9), Gpr(9));
var addzeIr = Assert.Single(lifter.Lift(new[] { addze })).Ir;
Assert.Equal("r9_addze_src", Assert.IsType<IrAssign>(addzeIr[0]).Destination);
Assert.Equal("PPC_GetCarry", Assert.IsType<IrCall>(addzeIr[1]).Target);
Assert.Equal("PPC_UpdateCarryAdd", Assert.IsType<IrCall>(addzeIr[3]).Target);
Assert.IsType<IrSetCrField>(addzeIr[4]);
var addme = Instruction(0, "addme.", Gpr(11), Gpr(11));
var addmeIr = Assert.Single(lifter.Lift(new[] { addme })).Ir;
Assert.Equal("r11_addme_src", Assert.IsType<IrAssign>(addmeIr[0]).Destination);
Assert.Equal("PPC_GetCarry", Assert.IsType<IrCall>(addmeIr[1]).Target);
Assert.Equal(-1, Assert.IsType<IrBinary>(addmeIr[2]).Right.Constant);
Assert.Equal("PPC_UpdateCarryAdd", Assert.IsType<IrCall>(addmeIr[4]).Target);
Assert.IsType<IrSetCrField>(addmeIr[5]);
var subfze = Instruction(0, "subfze.", Gpr(10), Gpr(10));
var subfzeIr = Assert.Single(lifter.Lift(new[] { subfze })).Ir;
Assert.Equal("r10_subfze_src", Assert.IsType<IrAssign>(subfzeIr[0]).Destination);
Assert.Equal("not", Assert.IsType<IrBinary>(subfzeIr[1]).Op);
Assert.Equal("PPC_GetCarry", Assert.IsType<IrCall>(subfzeIr[2]).Target);
Assert.Equal("PPC_UpdateCarryAdd", Assert.IsType<IrCall>(subfzeIr[4]).Target);
Assert.IsType<IrSetCrField>(subfzeIr[5]);
var subfme = Instruction(0, "subfme.", Gpr(12), Gpr(12));
var subfmeIr = Assert.Single(lifter.Lift(new[] { subfme })).Ir;
Assert.Equal("r12_subfme_src", Assert.IsType<IrAssign>(subfmeIr[0]).Destination);
Assert.Equal("not", Assert.IsType<IrBinary>(subfmeIr[1]).Op);
Assert.Equal("PPC_GetCarry", Assert.IsType<IrCall>(subfmeIr[2]).Target);
Assert.Equal(-1, Assert.IsType<IrBinary>(subfmeIr[3]).Right.Constant);
Assert.Equal("PPC_UpdateCarryAdd", Assert.IsType<IrCall>(subfmeIr[5]).Target);
Assert.IsType<IrSetCrField>(subfmeIr[6]);
}
[Fact]
public void LiftsBranchCtrCrAndMaskedRegisterForms()
{
var lifter = new PpcLifter();
var bcctr = Instruction((4u << 21) | (9u << 16), "bcctr");
var bcctrIr = Assert.Single(lifter.Lift(new[] { bcctr })).Ir;
var bcctrBranch = Assert.IsType<IrBranch>(Assert.Single(bcctrIr));
Assert.Equal("raw", bcctrBranch.Condition);
Assert.Equal("indirect_ctr_80000000", bcctrBranch.TrueLabel);
Assert.Equal("0x80000004", bcctrBranch.FalseLabel);
Assert.Contains("GetCRBit(ctx, 2, 1)", bcctrBranch.ConditionRegister, StringComparison.Ordinal);
var bctr = PpcInstruction.Synthetic(
0x80000010,
0,
"bctr",
Array.Empty<PpcOperand>(),
branchTargets: new[] { 0x80000030u, 0x80000020u, 0x80000030u });
var bctrIr = Assert.Single(lifter.Lift(new[] { bctr })).Ir;
var jumpTable = Assert.IsType<IrJumpTable>(Assert.Single(bctrIr));
Assert.Equal("ctr", jumpTable.Selector);
Assert.Equal(new uint[] { 0x80000030u, 0x80000020u }, jumpTable.Cases.Select(c => c.TargetAddress));
var directBctr = Instruction(0, "bctr");
var directBctrIr = Assert.Single(lifter.Lift(new[] { directBctr })).Ir;
Assert.Equal("ctr", Assert.IsType<IrIndirectJump>(Assert.Single(directBctrIr)).Target.RegisterName);
var crclr = Instruction(0, "crclr", new PpcConditionRegisterOperand("cr7eq", 30));
var crclrIr = Assert.Single(lifter.Lift(new[] { crclr })).Ir;
var crclrCall = Assert.IsType<IrCall>(Assert.Single(crclrIr));
Assert.Equal("PPC_CrSetBit", crclrCall.Target);
Assert.Equal(new long?[] { 30, 0 }, crclrCall.Arguments.Select(a => a.Constant).ToArray());
var cror = Instruction(
0,
"cror",
new PpcOperand[]
{
new PpcConditionRegisterOperand("crb1", 1),
new PpcConditionRegisterOperand("crb2", 2),
new PpcConditionRegisterOperand("crb3", 3)
});
var crorIr = Assert.Single(lifter.Lift(new[] { cror })).Ir;
var crorCall = Assert.IsType<IrCall>(Assert.Single(crorIr));
Assert.Equal("PPC_CrLogical", crorCall.Target);
Assert.Equal(new long?[] { 7, 1, 2, 3 }, crorCall.Arguments.Select(a => a.Constant).ToArray());
var mtcrfNoOp = Instruction(0, "mtcrf", new PpcImmediateOperand(0), Gpr(3));
var mtcrfNoOpIr = Assert.Single(lifter.Lift(new[] { mtcrfNoOp })).Ir;
Assert.Contains("no-op", Assert.IsType<IrComment>(Assert.Single(mtcrfNoOpIr)).Text, StringComparison.Ordinal);
var mtcrfMasked = Instruction(0, "mtcrf", new PpcImmediateOperand(0x80), Gpr(4));
var mtcrfMaskedIr = Assert.Single(lifter.Lift(new[] { mtcrfMasked })).Ir;
Assert.Equal(3, mtcrfMaskedIr.Count);
var maskedSrc = Assert.IsType<IrBinary>(mtcrfMaskedIr[0]);
Assert.Equal("r4_mtcrf_src", maskedSrc.Destination);
Assert.Equal(unchecked((int)0xF0000000), maskedSrc.Right.Constant);
Assert.Equal("cr", Assert.IsType<IrBinary>(mtcrfMaskedIr[2]).Destination);
}
[Fact]
public void LiftsComparisonShiftAndUpdateMemoryForms()
{
var lifter = new PpcLifter();
var cmplwi = Instruction(0, "cmplwi", Gpr(6), new PpcImmediateOperand(0x1234));
var cmplwiIr = Assert.Single(lifter.Lift(new[] { cmplwi })).Ir;
var cmplwiSetCr = Assert.IsType<IrSetCrField>(Assert.Single(cmplwiIr));
Assert.True(cmplwiSetCr.IsUnsigned);
Assert.Equal("r6", cmplwiSetCr.Left.RegisterName);
Assert.Equal(0x1234, cmplwiSetCr.Right.Constant);
var cmpwi = Instruction(0, "cmpwi", new PpcConditionRegisterOperand("cr2", 8), Gpr(6), new PpcImmediateOperand(-1));
var cmpwiIr = Assert.Single(lifter.Lift(new[] { cmpwi })).Ir;
var cmpwiSetCr = Assert.IsType<IrSetCrField>(Assert.Single(cmpwiIr));
Assert.Equal(2, cmpwiSetCr.FieldIndex);
Assert.False(cmpwiSetCr.IsUnsigned);
Assert.Equal("r6", cmpwiSetCr.Left.RegisterName);
Assert.Equal(-1, cmpwiSetCr.Right.Constant);
var sraw = Instruction(0, "sraw.", Gpr(11), Gpr(12), Gpr(13));
var srawIr = Assert.Single(lifter.Lift(new[] { sraw })).Ir;
Assert.Equal("PPC_UpdateCarryShiftRight", Assert.IsType<IrCall>(srawIr[0]).Target);
Assert.Equal("r13", Assert.IsType<IrCall>(srawIr[0]).Arguments[1].RegisterName);
Assert.Equal("ppc_sraw", Assert.IsType<IrBinary>(srawIr[1]).Op);
Assert.IsType<IrSetCrField>(srawIr[2]);
var srw = Instruction(0, "srw.", Gpr(7), Gpr(8), Gpr(9));
var srwIr = Assert.Single(lifter.Lift(new[] { srw })).Ir;
var srwBinary = Assert.IsType<IrBinary>(srwIr[0]);
Assert.Equal("ppc_srw", srwBinary.Op);
Assert.Equal("r9", srwBinary.Right.RegisterName);
Assert.IsType<IrSetCrField>(srwIr[1]);
var slw = Instruction(0, "slw.", Gpr(10), Gpr(11), Gpr(12));
var slwIr = Assert.Single(lifter.Lift(new[] { slw })).Ir;
var slwBinary = Assert.IsType<IrBinary>(slwIr[0]);
Assert.Equal("ppc_slw", slwBinary.Op);
Assert.Equal("r12", slwBinary.Right.RegisterName);
Assert.IsType<IrSetCrField>(slwIr[1]);
var lhau = Instruction((3u << 21) | (4u << 16) | 0x0010u, "lhau");
var lhauIr = Assert.Single(lifter.Lift(new[] { lhau })).Ir;
Assert.Equal("r4", Assert.IsType<IrBinary>(lhauIr[0]).Destination);
Assert.Equal("r3", Assert.IsType<IrLoad>(lhauIr[1]).Destination);
Assert.Equal("sar", Assert.IsType<IrBinary>(lhauIr[3]).Op);
var invalidLhau = Instruction((3u << 21) | (0u << 16) | 0x0010u, "lhau");
Assert.Throws<InvalidOperationException>(() => lifter.Lift(new[] { invalidLhau }));
var stbu = Instruction(0, "stbu", Gpr(7), new PpcDisplacementOperand(4, "r8", 8));
var stbuIr = Assert.Single(lifter.Lift(new[] { stbu })).Ir;
Assert.Equal("r8_stbu_ea", Assert.IsType<IrBinary>(stbuIr[0]).Destination);
Assert.IsType<IrStore>(stbuIr[1]);
Assert.Equal("r8", Assert.IsType<IrAssign>(stbuIr[2]).Destination);
var sthu = Instruction(0, "sthu", Gpr(7), new PpcDisplacementOperand(6, "r8", 8));
var sthuIr = Assert.Single(lifter.Lift(new[] { sthu })).Ir;
Assert.Equal("r8_sthu_ea", Assert.IsType<IrBinary>(sthuIr[0]).Destination);
Assert.IsType<IrStore>(sthuIr[1]);
Assert.Equal("r8", Assert.IsType<IrAssign>(sthuIr[2]).Destination);
var addic = Instruction(0, "addic.", Gpr(15), Gpr(15), new PpcImmediateOperand(4));
var addicIr = Assert.Single(lifter.Lift(new[] { addic })).Ir;
Assert.Equal("r15_addic_src", Assert.IsType<IrAssign>(addicIr[0]).Destination);
Assert.Equal("PPC_UpdateCarryAdd", Assert.IsType<IrCall>(addicIr[2]).Target);
Assert.IsType<IrSetCrField>(addicIr[3]);
var subfe = Instruction(0, "subfe.", Gpr(16), Gpr(1), Gpr(16));
var subfeIr = Assert.Single(lifter.Lift(new[] { subfe })).Ir;
Assert.Equal("r16_subfe_rb", Assert.IsType<IrAssign>(subfeIr[0]).Destination);
Assert.Equal("not", Assert.IsType<IrBinary>(subfeIr[1]).Op);
Assert.Equal("PPC_GetCarry", Assert.IsType<IrCall>(subfeIr[2]).Target);
Assert.Equal("PPC_UpdateCarryAdd", Assert.IsType<IrCall>(subfeIr[5]).Target);
Assert.IsType<IrSetCrField>(subfeIr[6]);
}
[Fact]
public void LiftsMiscUnaryStatusAndLogicalForms()
{
var lifter = new PpcLifter();
var neg = Instruction(0, "neg.", Gpr(3), Gpr(4));
var negIr = Assert.Single(lifter.Lift(new[] { neg })).Ir;
var negBinary = Assert.IsType<IrBinary>(negIr[0]);
Assert.Equal("sub", negBinary.Op);
Assert.Equal(0, negBinary.Left.Constant);
Assert.Equal("r4", negBinary.Right.RegisterName);
Assert.IsType<IrSetCrField>(negIr[1]);
var nor = Instruction(0, "nor", Gpr(5), Gpr(6), Gpr(7));
var norIr = Assert.Single(lifter.Lift(new[] { nor })).Ir;
Assert.Equal("nor", Assert.IsType<IrBinary>(Assert.Single(norIr)).Op);
var nand = Instruction(0, "nand.", Gpr(8), Gpr(9), Gpr(10));
var nandIr = Assert.Single(lifter.Lift(new[] { nand })).Ir;
Assert.Equal("nand", Assert.IsType<IrBinary>(nandIr[0]).Op);
Assert.IsType<IrSetCrField>(nandIr[1]);
var mfdar = Instruction(0, "mfdar", Gpr(11));
Assert.Equal(0, Assert.IsType<IrAssign>(Assert.Single(Assert.Single(lifter.Lift(new[] { mfdar })).Ir)).Value.Constant);
var mfmsr = Instruction(0, "mfmsr", Gpr(12));
Assert.Equal("msr", Assert.IsType<IrAssign>(Assert.Single(Assert.Single(lifter.Lift(new[] { mfmsr })).Ir)).Value.RegisterName);
var mfpvr = Instruction(0, "mfpvr", Gpr(13));
Assert.Equal(0, Assert.IsType<IrAssign>(Assert.Single(Assert.Single(lifter.Lift(new[] { mfpvr })).Ir)).Value.Constant);
var mfdsisr = Instruction(0, "mfdsisr", Gpr(14));
Assert.Equal(0, Assert.IsType<IrAssign>(Assert.Single(Assert.Single(lifter.Lift(new[] { mfdsisr })).Ir)).Value.Constant);
var blelr = Instruction(0, "blelr");
var blelrBranch = Assert.IsType<IrBranch>(Assert.Single(Assert.Single(lifter.Lift(new[] { blelr })).Ir));
Assert.Equal("ble", blelrBranch.Condition);
Assert.Equal("return", blelrBranch.TrueLabel);
}
[Fact]
public void LiftsUpdateLoadFormsAndCacheNoOps()
{
var lifter = new PpcLifter();
var dcbzR0 = Instruction(0, "dcbz", Gpr(0), Gpr(5));
var dcbzR0Ir = Assert.Single(lifter.Lift(new[] { dcbzR0 })).Ir;
Assert.Equal("r5_addr_dcbz", Assert.IsType<IrAssign>(dcbzR0Ir[0]).Destination);
Assert.Equal("memset_zero_32", Assert.IsType<IrCall>(dcbzR0Ir[2]).Target);
var dcbz = Instruction(0, "dcbz_l", Gpr(4), Gpr(5));
var dcbzIr = Assert.Single(lifter.Lift(new[] { dcbz })).Ir;
Assert.Equal("add", Assert.IsType<IrBinary>(dcbzIr[0]).Op);
Assert.Equal("and", Assert.IsType<IrBinary>(dcbzIr[1]).Op);
foreach (var mnemonic in new[] { "icbi", "dcbi", "dcbf" })
{
var ins = Instruction(0, mnemonic);
var ir = Assert.Single(lifter.Lift(new[] { ins })).Ir;
Assert.Contains(mnemonic, Assert.IsType<IrComment>(Assert.Single(ir)).Text, StringComparison.Ordinal);
}
var lfsu = Instruction(0, "lfsu", Gpr(1), new PpcDisplacementOperand(4, "r3", 3));
var lfsuIr = Assert.Single(lifter.Lift(new[] { lfsu })).Ir;
Assert.Equal("r3", Assert.IsType<IrBinary>(lfsuIr[0]).Destination);
Assert.Equal("r1", Assert.IsType<IrLoad>(lfsuIr[1]).Destination);
var invalidLfsu = Instruction(0, "lfsu", Gpr(1), new PpcDisplacementOperand(4, "r0", 0));
Assert.Throws<InvalidOperationException>(() => lifter.Lift(new[] { invalidLfsu }));
var lhzu = Instruction(0, "lhzu", Gpr(2), new PpcDisplacementOperand(6, "r4", 4));
var lhzuIr = Assert.Single(lifter.Lift(new[] { lhzu })).Ir;
Assert.Equal("r4", Assert.IsType<IrBinary>(lhzuIr[0]).Destination);
Assert.Equal("r2", Assert.IsType<IrLoad>(lhzuIr[1]).Destination);
var invalidLhzu = Instruction(0, "lhzu", Gpr(2), new PpcDisplacementOperand(6, "r0", 0));
Assert.Throws<InvalidOperationException>(() => lifter.Lift(new[] { invalidLhzu }));
var lbzu = Instruction(0, "lbzu", Gpr(3), new PpcDisplacementOperand(8, "r5", 5));
var lbzuIr = Assert.Single(lifter.Lift(new[] { lbzu })).Ir;
Assert.Equal("r5", Assert.IsType<IrBinary>(lbzuIr[0]).Destination);
Assert.Equal("r3", Assert.IsType<IrLoad>(lbzuIr[1]).Destination);
var invalidLbzu = Instruction(0, "lbzu", Gpr(3), new PpcDisplacementOperand(8, "r0", 0));
Assert.Throws<InvalidOperationException>(() => lifter.Lift(new[] { invalidLbzu }));
var stwu = Instruction(0, "stwu", Gpr(6), new PpcDisplacementOperand(0x10, "r7", 7));
var stwuIr = Assert.Single(lifter.Lift(new[] { stwu })).Ir;
Assert.IsType<IrStore>(stwuIr[0]);
Assert.Equal("r7", Assert.IsType<IrBinary>(stwuIr[1]).Destination);
var invalidStwu = Instruction(0, "stwu", Gpr(6), new PpcDisplacementOperand(0x10, "r0", 0));
Assert.Throws<InvalidOperationException>(() => lifter.Lift(new[] { invalidStwu }));
}
[Fact]
public void LiftsControlRegisterTableAndDivisionForms()
{
var lifter = new PpcLifter();
var cmplw = Instruction(0, "cmplw", new PpcConditionRegisterOperand("cr4", 16), Gpr(8), Gpr(9));
var cmplwIr = Assert.Single(lifter.Lift(new[] { cmplw })).Ir;
var cmplwSetCr = Assert.IsType<IrSetCrField>(Assert.Single(cmplwIr));
Assert.Equal(4, cmplwSetCr.FieldIndex);
Assert.True(cmplwSetCr.IsUnsigned);
Assert.Equal("r8", cmplwSetCr.Left.RegisterName);
Assert.Equal("r9", cmplwSetCr.Right.RegisterName);
foreach (var mnemonic in new[] { "mticcr", "mttbu", "mttbl", "mtdar", "mtdsisr" })
{
var ins = Instruction(0, mnemonic, Gpr(3));
Assert.Throws<NotImplementedException>(() => lifter.Lift(new[] { ins }));
}
var mfibatu = Instruction(0, "mfibatu", Gpr(4), Gpr(5));
Assert.Equal(0, Assert.IsType<IrAssign>(Assert.Single(Assert.Single(lifter.Lift(new[] { mfibatu })).Ir)).Value.Constant);
foreach (var mnemonic in new[] { "mtibatu", "mtibatl", "mtdbatu", "mtdbatl" })
{
var ins = Instruction(0, mnemonic, Gpr(4), Gpr(5));
Assert.Throws<NotImplementedException>(() => lifter.Lift(new[] { ins }));
}
foreach (var mnemonic in new[] { "mfibatl", "mfdbatu", "mfdbatl" })
{
var ins = Instruction(0, mnemonic, Gpr(4), Gpr(5));
var ir = Assert.Single(lifter.Lift(new[] { ins })).Ir;
Assert.Equal(0, Assert.IsType<IrAssign>(Assert.Single(ir)).Value.Constant);
}
var mtxer = Instruction(0, "mtxer", Gpr(6));
Assert.Equal("xer", Assert.IsType<IrAssign>(Assert.Single(Assert.Single(lifter.Lift(new[] { mtxer })).Ir)).Destination);
var mfxer = Instruction(0, "mfxer", Gpr(7));
Assert.Equal("xer", Assert.IsType<IrAssign>(Assert.Single(Assert.Single(lifter.Lift(new[] { mfxer })).Ir)).Value.RegisterName);
var divw = Instruction(0, "divw.", Gpr(10), Gpr(11), Gpr(12));
var divwIr = Assert.Single(lifter.Lift(new[] { divw })).Ir;
Assert.Equal("div", Assert.IsType<IrBinary>(divwIr[0]).Op);
Assert.IsType<IrSetCrField>(divwIr[1]);
}
[Fact]
public void LiftsExtraCrShiftAndUnsupportedFallbackForms()
{
var lifter = new PpcLifter();
var srw = Instruction(0, "srw.", Gpr(3), Gpr(4), Gpr(5));
var srwIr = Assert.Single(lifter.Lift(new[] { srw })).Ir;
Assert.Equal("r3", Assert.IsType<IrBinary>(srwIr[0]).Destination);
Assert.Equal("ppc_srw", Assert.IsType<IrBinary>(srwIr[0]).Op);
Assert.IsType<IrSetCrField>(srwIr[1]);
var divwu = Instruction(0, "divwu.", Gpr(6), Gpr(7), Gpr(8));
var divwuIr = Assert.Single(lifter.Lift(new[] { divwu })).Ir;
Assert.Equal("divu", Assert.IsType<IrBinary>(divwuIr[0]).Op);
Assert.IsType<IrSetCrField>(divwuIr[1]);
var extsb = Instruction(0, "extsb.", Gpr(9), Gpr(10));
var extsbIr = Assert.Single(lifter.Lift(new[] { extsb })).Ir;
var extsbBinary = Assert.IsType<IrBinary>(extsbIr[0]);
Assert.Equal("sext", extsbBinary.Op);
Assert.Equal(8, extsbBinary.Right.Constant);
Assert.IsType<IrSetCrField>(extsbIr[1]);
var mcrf = Instruction(0, "mcrf", new PpcConditionRegisterOperand("cr7", 28), new PpcConditionRegisterOperand("cr2", 8));
var mcrfCall = Assert.IsType<IrCall>(Assert.Single(Assert.Single(lifter.Lift(new[] { mcrf })).Ir));
Assert.Equal("PPC_Mcrf", mcrfCall.Target);
Assert.Equal(new long?[] { 7, 2 }, mcrfCall.Arguments.Select(a => a.Constant).ToArray());
var mcrxr = Instruction(0, "mcrxr", new PpcConditionRegisterOperand("cr7", 28));
var mcrxrCall = Assert.IsType<IrCall>(Assert.Single(Assert.Single(lifter.Lift(new[] { mcrxr })).Ir));
Assert.Equal("PPC_Mcrxr", mcrxrCall.Target);
Assert.Equal(7, Assert.Single(mcrxrCall.Arguments).Constant);
var crxor = Instruction(
0,
"crxor",
new PpcConditionRegisterOperand("cr7eq", 30),
new PpcConditionRegisterOperand("crb2", 2),
new PpcConditionRegisterOperand("31", 31));
var crxorCall = Assert.IsType<IrCall>(Assert.Single(Assert.Single(lifter.Lift(new[] { crxor })).Ir));
Assert.Equal("PPC_CrLogical", crxorCall.Target);
Assert.Equal(new long?[] { 2, 30, 2, 31 }, crxorCall.Arguments.Select(a => a.Constant).ToArray());
var slwi = Instruction(0, "slwi.", Gpr(11), Gpr(12), new PpcImmediateOperand(5));
var slwiIr = Assert.Single(lifter.Lift(new[] { slwi })).Ir;
Assert.Equal("shl", Assert.IsType<IrBinary>(slwiIr[0]).Op);
Assert.IsType<IrSetCrField>(slwiIr[1]);
var unknown = Instruction(0x12345678, "totally_unknown", Gpr(3));
var undefined = Assert.IsType<IrUndefined>(Assert.Single(Assert.Single(lifter.Lift(new[] { unknown }, allowUnsupported: true)).Ir));
Assert.Contains("totally_unknown", undefined.Disassembly, StringComparison.Ordinal);
Assert.Equal(0x12345678u, undefined.RawInstruction);
var unsupported = Instruction(0, "mticcr", Gpr(3));
var unsupportedUndefined = Assert.IsType<IrUndefined>(Assert.Single(Assert.Single(lifter.Lift(new[] { unsupported }, allowUnsupported: true)).Ir));
Assert.Contains("UNIMPLEMENTED", unsupportedUndefined.Reason ?? string.Empty, StringComparison.Ordinal);
}
[Fact]
public void PrivateHelpersParseDisplacementsCrBitsAndBranchConditions()
{
Assert.Equal((0, "r3"), InvokePrivate<(int offset, string @base)>("ParseDisplacement", "r3"));
Assert.Equal((0x1234, "0"), InvokePrivate<(int offset, string @base)>("ParseDisplacement", "0x1234"));
Assert.Equal((0, "r4"), InvokePrivate<(int offset, string @base)>("ParseDisplacement", "(r4)"));
Assert.Throws<TargetInvocationException>(() => InvokePrivate<(int offset, string @base)>("ParseDisplacement", "broken("));
Assert.Equal(30, InvokePrivate<int>("ParseCrBitIndex", "cr7eq"));
Assert.Equal(2, InvokePrivate<int>("ParseCrBitIndex", "crb2"));
Assert.Equal(25, InvokePrivate<int>("ParseCrBitIndex", "cr6gt"));
Assert.Equal(31, InvokePrivate<int>("ParseCrBitIndex", "31"));
Assert.Equal(0, InvokePrivate<int>("ParseCrBitIndex", "mystery"));
Assert.Equal(7, InvokePrivate<int>("ParseCrFieldName", "crf7"));
Assert.Equal(3, InvokePrivate<int>("ParseCrFieldName", "cr3"));
Assert.Equal(0, InvokePrivate<int>("ParseCrFieldName", "bogus"));
var absoluteLoad = InvokePrivate<IReadOnlyList<IrInstruction>>("DFormLoad", "r5", "0", 12, 4);
Assert.Equal("r5_ea", Assert.IsType<IrAssign>(absoluteLoad[0]).Destination);
Assert.Equal("r5", Assert.IsType<IrLoad>(absoluteLoad[1]).Destination);
var registerLoad = InvokePrivate<IReadOnlyList<IrInstruction>>("DFormLoad", "r6", "r7", 16, 2);
var registerLoadIr = Assert.Single(registerLoad);
var load = Assert.IsType<IrLoad>(registerLoadIr);
Assert.Equal("r6", load.Destination);
Assert.Equal("r7", load.Address.Base);
Assert.Equal(16, load.Address.Offset);
}
[Fact]
public void PrivateCrLogicalHelperMapsAllRemainingOpcodes()
{
foreach (var (mnemonic, opcode) in new[]
{
("crnor", 0L),
("crandc", 1L),
("crnand", 3L),
("crand", 4L),
("creqv", 5L),
("crorc", 6L)
})
{
var ir = Assert.Single(InvokePrivate<IReadOnlyList<IrInstruction>>("LiftCrLogical", mnemonic, "cr7eq", "crb2", "31"));
var call = Assert.IsType<IrCall>(ir);
Assert.Equal("PPC_CrLogical", call.Target);
Assert.Equal(opcode, call.Arguments[0].Constant);
Assert.Equal(30, call.Arguments[1].Constant);
Assert.Equal(2, call.Arguments[2].Constant);
Assert.Equal(31, call.Arguments[3].Constant);
}
}
[Fact]
public void LiftsLinkRegisterBranchFamiliesAndGenericBcFallbacks()
{
var lifter = new PpcLifter();
var beqlrl = Instruction(0, "beqlrl", new PpcConditionRegisterOperand("cr3", 12));
var beqlrlBranch = Assert.IsType<IrBranch>(Assert.Single(Assert.Single(lifter.Lift(new[] { beqlrl })).Ir));
Assert.Equal("beq", beqlrlBranch.Condition);
Assert.Equal("cr3", beqlrlBranch.ConditionRegister);
Assert.Contains("call_lr_80000000_80000004", beqlrlBranch.TrueLabel, StringComparison.Ordinal);
var bgtlrl = Instruction(0, "bgtlrl");
var bgtlrlBranch = Assert.IsType<IrBranch>(Assert.Single(Assert.Single(lifter.Lift(new[] { bgtlrl })).Ir));
Assert.Equal("bgt", bgtlrlBranch.Condition);
Assert.Equal("cr0", bgtlrlBranch.ConditionRegister);
var bnelrCr6 = Instruction(0, "bnelr", new PpcConditionRegisterOperand("cr6", 24));
var bnelrCr6Branch = Assert.IsType<IrBranch>(Assert.Single(Assert.Single(lifter.Lift(new[] { bnelrCr6 })).Ir));
Assert.Equal("bne", bnelrCr6Branch.Condition);
Assert.Equal("return", bnelrCr6Branch.TrueLabel);
Assert.Equal("cr6", bnelrCr6Branch.ConditionRegister);
var bnelrCr7 = Instruction(0, "bnelr", new PpcConditionRegisterOperand("cr7", 28));
var bnelrCr7Branch = Assert.IsType<IrBranch>(Assert.Single(Assert.Single(lifter.Lift(new[] { bnelrCr7 })).Ir));
Assert.Equal("bne", bnelrCr7Branch.Condition);
Assert.Equal("cr7", bnelrCr7Branch.ConditionRegister);
var bcImmediate = Instruction(
0,
"bc",
new PpcImmediateOperand(4),
new PpcImmediateOperand(3),
new PpcImmediateOperand(unchecked((int)0x80000020)));
var bcImmediateBranch = Assert.IsType<IrBranch>(Assert.Single(Assert.Single(lifter.Lift(new[] { bcImmediate })).Ir));
Assert.Equal("raw", bcImmediateBranch.Condition);
Assert.Contains("GetCRBit(ctx, 0, 3)", bcImmediateBranch.ConditionRegister, StringComparison.Ordinal);
var bcFallback = PpcInstruction.Synthetic(
0x80000000,
0,
"bc",
new PpcOperand[] { new PpcConditionRegisterOperand("cr4", 16) },
new[] { 0x80000010u },
isConditional: true);
var bcFallbackBranch = Assert.IsType<IrBranch>(Assert.Single(Assert.Single(lifter.Lift(new[] { bcFallback })).Ir));
Assert.Equal("bc", bcFallbackBranch.Condition);
Assert.Equal("cr4", bcFallbackBranch.ConditionRegister);
var ctrBc = Assert.Single(lifter.Lift(new[] { Instruction(0, "bc", new PpcImmediateOperand(0), new PpcImmediateOperand(0), new PpcImmediateOperand(4)) })).Ir;
Assert.Equal("ctr", Assert.IsType<IrBinary>(ctrBc[0]).Destination);
Assert.Equal("raw", Assert.IsType<IrBranch>(ctrBc[1]).Condition);
var bclrReturn = Instruction((20u << 21), "bclr");
var bclrReturnBranch = Assert.IsType<IrBranch>(Assert.Single(Assert.Single(lifter.Lift(new[] { bclrReturn })).Ir));
Assert.Equal("raw", bclrReturnBranch.Condition);
Assert.Equal("return", bclrReturnBranch.TrueLabel);
var bclrCr = Instruction((12u << 21) | (2u << 16), "bclr");
var bclrCrBranch = Assert.IsType<IrBranch>(Assert.Single(Assert.Single(lifter.Lift(new[] { bclrCr })).Ir));
Assert.Equal("raw", bclrCrBranch.Condition);
Assert.Equal("return", bclrCrBranch.TrueLabel);
var bclrCtr = Instruction((16u << 21), "bclr");
var bclrCtrIr = Assert.Single(lifter.Lift(new[] { bclrCtr })).Ir;
Assert.Equal("ctr", Assert.IsType<IrBinary>(bclrCtrIr[0]).Destination);
Assert.Equal("raw", Assert.IsType<IrBranch>(bclrCtrIr[1]).Condition);
var combinedBclr = Assert.Single(lifter.Lift(new[] { Instruction(0, "bclr") })).Ir;
Assert.Equal("ctr", Assert.IsType<IrBinary>(combinedBclr[0]).Destination);
Assert.Equal("raw", Assert.IsType<IrBranch>(combinedBclr[1]).Condition);
}
[Fact]
public void LiftsCacheHintsByteReverseAndRawXoFallbacks()
{
var lifter = new PpcLifter();
foreach (var mnemonic in new[] { "dcbst", "xo_54", "xo_470", "dcbt", "xo_278", "dcbtst", "xo_246", "dcba", "xo_758", "tlbie", "xo_306", "tlbsync", "xo_566" })
{
var ir = Assert.Single(lifter.Lift(new[] { Instruction(0, mnemonic) })).Ir;
Assert.IsType<IrComment>(Assert.Single(ir));
}
var stwbrx = Assert.Single(lifter.Lift(new[] { Instruction((3u << 21) | (4u << 16) | (5u << 11), "stwbrx") })).Ir;
Assert.Equal("PPC_StoreWordByteReverse", Assert.IsType<IrCall>(stwbrx[1]).Target);
var lwbrx = Assert.Single(lifter.Lift(new[] { Instruction((6u << 21) | (7u << 16) | (8u << 11), "lwbrx") })).Ir;
Assert.Equal("PPC_LoadWordByteReverse", Assert.IsType<IrCall>(lwbrx[1]).Target);
var lhbrx = Assert.Single(lifter.Lift(new[] { Instruction((9u << 21) | (10u << 16) | (11u << 11), "lhbrx") })).Ir;
Assert.Equal("PPC_LoadHalfwordByteReverse", Assert.IsType<IrCall>(lhbrx[1]).Target);
var sthbrx = Assert.Single(lifter.Lift(new[] { Instruction((12u << 21) | (13u << 16) | (14u << 11), "sthbrx") })).Ir;
Assert.Equal("PPC_StoreHalfwordByteReverse", Assert.IsType<IrCall>(sthbrx[1]).Target);
var xoNand = Assert.Single(lifter.Lift(new[] { Instruction((4u << 21) | (5u << 16) | (6u << 11) | 1u, "xo_476") })).Ir;
Assert.Equal("nand", Assert.IsType<IrBinary>(xoNand[0]).Op);
Assert.IsType<IrSetCrField>(xoNand[1]);
var xoMulhwu = Assert.Single(lifter.Lift(new[] { Instruction((7u << 21) | (8u << 16) | (9u << 11), "xo_11") })).Ir;
Assert.Equal("mulhwu", Assert.IsType<IrBinary>(Assert.Single(xoMulhwu)).Op);
var xoStwx = Assert.Single(lifter.Lift(new[] { Instruction((10u << 21) | (11u << 16) | (12u << 11), "xo_151") })).Ir;
Assert.Equal(4, Assert.IsType<IrStore>(xoStwx[1]).SizeBytes);
var xoStbx = Assert.Single(lifter.Lift(new[] { Instruction((13u << 21) | (14u << 16) | (15u << 11), "xo_215") })).Ir;
Assert.Equal(1, Assert.IsType<IrStore>(xoStbx[1]).SizeBytes);
var xoAddze = Assert.Single(lifter.Lift(new[] { Instruction((16u << 21) | (17u << 16) | 1u, "xo_202") })).Ir;
Assert.Equal("PPC_GetCarry", Assert.IsType<IrCall>(xoAddze[0]).Target);
Assert.IsType<IrSetCrField>(xoAddze[^1]);
var xoCmp = Assert.Single(lifter.Lift(new[] { Instruction((3u << 23) | (18u << 16) | (19u << 11), "xo_0") })).Ir;
var xoCmpSetCr = Assert.IsType<IrSetCrField>(Assert.Single(xoCmp));
Assert.Equal(3, xoCmpSetCr.FieldIndex);
Assert.Equal("r18", xoCmpSetCr.Left.RegisterName);
Assert.Equal("r19", xoCmpSetCr.Right.RegisterName);
var xoMfsr = Instruction((20u << 21), "xo_595");
Assert.Throws<NotImplementedException>(() => lifter.Lift(new[] { xoMfsr }));
}
}