using System; using System.Collections.Generic; using System.Reflection; using Translator.Core.Disassembly; using Translator.Core.Ir; using Translator.Core.Lifting; using Xunit; namespace Translator.Tests; public class PpcLifterHelperCoverageTests { private static readonly Type LifterType = typeof(PpcLifter); private static T InvokePrivate(string name, params object?[] args) { var method = LifterType.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static); Assert.NotNull(method); return (T)method!.Invoke(null, args)!; } private static PpcRegisterOperand Gpr(int index) => new($"r{index}", index); [Fact] public void HelperMethods_ReadParseAndNormalizeRegisters() { var instruction = PpcInstruction.Synthetic(0x80000000, 0x12345678, "addi", new PpcOperand[] { Gpr(3), Gpr(4), new PpcImmediateOperand(1) }); Assert.Equal(0x12345678u, InvokePrivate("ReadRawInstruction", instruction)); // PpcInstruction stores the raw big-endian word itself, so an instruction // can no longer be missing its encoding; a zero word still reads back. var zeroWord = new PpcInstruction( 0x80000000, 0u, "nop", Array.Empty(), Array.Empty(), isReturn: false, isCall: false, isConditionalBranch: false); Assert.Equal(0u, InvokePrivate("ReadRawInstruction", zeroWord)); Assert.Equal(0, InvokePrivate("ParseCrFieldIndex", string.Empty)); Assert.Equal(0, InvokePrivate("ParseCrFieldIndex", "cr")); Assert.Equal(7, InvokePrivate("ParseCrFieldIndex", "cr7_3")); Assert.Equal(3, InvokePrivate("ParseCrFieldIndex", "cr3")); Assert.Equal(0, InvokePrivate("ParseCrFieldIndex", "bad")); var zeroBase = InvokePrivate("BaseOrZero", "r0"); Assert.Equal("const", zeroBase.Kind); Assert.Equal(0, zeroBase.Constant); var regBase = InvokePrivate("BaseOrZero", "r5"); Assert.Equal("register", regBase.Kind); Assert.Equal("r5", regBase.RegisterName); Assert.Equal(31, InvokePrivate("ParseRegisterNumber", "r31")); var badRegister = Assert.Throws(() => InvokePrivate("ParseRegisterNumber", "ctr")); Assert.IsType(badRegister.InnerException); } [Fact] public void HelperMethods_EmitDFormAndPairedSingleMemoryAccesses() { var zeroBasedStore = InvokePrivate>("DFormStore", "r0", 24, IrValue.Register("r7"), 4); Assert.Collection( zeroBasedStore, ins => { var assign = Assert.IsType(ins); Assert.Equal("r7_ea", assign.Destination); Assert.Equal(24, assign.Value.Constant); }, ins => { var store = Assert.IsType(ins); Assert.Equal("r7_ea", store.Address.Base); Assert.Equal("r7", store.Source.RegisterName); Assert.Equal(4, store.SizeBytes); }); var normalStore = InvokePrivate>("DFormStore", "r4", -8, IrValue.Register("r6"), 8); var storeIns = Assert.IsType(Assert.Single(normalStore)); Assert.Equal("r4", storeIns.Address.Base); Assert.Equal(-8, storeIns.Address.Offset); Assert.Equal(8, storeIns.SizeBytes); var zeroBasedLoad = InvokePrivate>("EmitPairedSingleLoad", "f1", "r0", 32, 1, 5, false, 0x80001000u); Assert.Collection( zeroBasedLoad, ins => Assert.Contains("psq_load", Assert.IsType(ins).Text, StringComparison.Ordinal), ins => { var assign = Assert.IsType(ins); Assert.Equal("f1_psq_ea", assign.Destination); Assert.Equal(32, assign.Value.Constant); }, ins => { var call = Assert.IsType(ins); Assert.Equal("PPC_PsqL", call.Target); Assert.Equal("f1_psq_ea", call.Arguments[0].RegisterName); }); var updatingLoad = InvokePrivate>("EmitPairedSingleLoad", "f2", "r9", 12, 0, 3, true, 0x80001004u); Assert.Equal("r9_psq_addr", Assert.IsType(updatingLoad[1]).Destination); Assert.Equal("PPC_PsqL", Assert.IsType(updatingLoad[2]).Target); Assert.Equal("r9", Assert.IsType(updatingLoad[3]).Destination); var zeroOffsetLoad = InvokePrivate>("EmitPairedSingleLoad", "f3", "r8", 0, 0, 2, false, 0x80001008u); var directLoadCall = Assert.IsType(zeroOffsetLoad[1]); Assert.Equal("r8", directLoadCall.Arguments[0].RegisterName); var invalidLoad = Assert.Throws(() => InvokePrivate>("EmitPairedSingleLoad", "f4", "r0", 4, 0, 0, true, 0x8000100Cu)); Assert.IsType(invalidLoad.InnerException); var zeroBasedStorePs = InvokePrivate>("EmitPairedSingleStore", "f5", "r0", 48, 1, 6, false, 0x80001010u); Assert.Collection( zeroBasedStorePs, ins => Assert.Contains("psq_store", Assert.IsType(ins).Text, StringComparison.Ordinal), ins => { var assign = Assert.IsType(ins); Assert.Equal("f5_psq_ea", assign.Destination); Assert.Equal(48, assign.Value.Constant); }, ins => { var call = Assert.IsType(ins); Assert.Equal("PPC_PsqSt", call.Target); Assert.Equal("f5", call.Arguments[1].RegisterName); }); var updatingStore = InvokePrivate>("EmitPairedSingleStore", "f6", "r10", 16, 0, 7, true, 0x80001014u); Assert.Equal("r10_psq_ea", Assert.IsType(updatingStore[1]).Destination); Assert.Equal("PPC_PsqSt", Assert.IsType(updatingStore[2]).Target); Assert.Equal("r10", Assert.IsType(updatingStore[3]).Destination); var zeroOffsetStore = InvokePrivate>("EmitPairedSingleStore", "f7", "r11", 0, 0, 1, false, 0x80001018u); var directStoreCall = Assert.IsType(zeroOffsetStore[1]); Assert.Equal("r11", directStoreCall.Arguments[0].RegisterName); var invalidStore = Assert.Throws(() => InvokePrivate>("EmitPairedSingleStore", "f8", "r0", 4, 0, 0, true, 0x8000101Cu)); Assert.IsType(invalidStore.InnerException); } [Fact] public void HelperMethods_ParseCrBitsBuildTargetLabelsAndLiftUndefined() { Assert.Equal(0, InvokePrivate("ParseCrBitIndex", string.Empty)); Assert.Equal(30, InvokePrivate("ParseCrBitIndex", "cr7eq")); Assert.Equal(31, InvokePrivate("ParseCrBitIndex", "crb40")); Assert.Equal(13, InvokePrivate("ParseCrBitIndex", "cr3gt")); Assert.Equal(29, InvokePrivate("ParseCrBitIndex", "29")); Assert.Equal(0, InvokePrivate("ParseCrBitIndex", "bogus")); Assert.Equal(0, InvokePrivate("ParseCrFieldName", string.Empty)); Assert.Equal(7, InvokePrivate("ParseCrFieldName", "crf7")); Assert.Equal(3, InvokePrivate("ParseCrFieldName", "cr3")); Assert.Equal(7, InvokePrivate("ParseCrFieldName", "cr99")); Assert.Equal(0, InvokePrivate("ParseCrFieldName", "bogus")); var noTarget = PpcInstruction.Synthetic(0x80002000, 0x48000000, "b", Array.Empty()); var noTargetEx = Assert.Throws(() => InvokePrivate("TargetLabel", noTarget, new HashSet(), true)); Assert.IsType(noTargetEx.InnerException); var branch = PpcInstruction.Synthetic(0x80002010, 0x48000000, "b", Array.Empty(), branchTargets: new[] { 0x80003000u }); Assert.Equal("0x80002014", InvokePrivate("TargetLabel", branch, new HashSet(), true)); Assert.Equal("0x80003000", InvokePrivate("TargetLabel", branch, new HashSet(), false)); Assert.Equal("0x80003000", InvokePrivate("TargetLabel", branch, new HashSet { 0x80003000u }, true)); var withOperands = PpcInstruction.Synthetic(0x80003000, 0x38630001, "addi", new PpcOperand[] { Gpr(3), Gpr(3), new PpcImmediateOperand(1) }); var undefinedWithOperands = Assert.IsType(Assert.Single(InvokePrivate>("LiftUndefined", withOperands, "unsupported"))); Assert.Equal("addi r3, r3, 1", undefinedWithOperands.Disassembly); Assert.Equal("unsupported", undefinedWithOperands.Reason); var withoutOperands = PpcInstruction.Synthetic(0x80003004, 0x60000000, "nop", Array.Empty()); var undefinedWithoutOperands = Assert.IsType(Assert.Single(InvokePrivate>("LiftUndefined", withoutOperands, "no-op"))); Assert.Equal("nop", undefinedWithoutOperands.Disassembly); } [Fact] public void HelperMethods_LiftCrLogicalAndIndexedPairedSingles() { var creqv = Assert.IsType(Assert.Single(InvokePrivate>("LiftCrLogical", "creqv", "crb1", "crb2", "crb3"))); Assert.Equal("PPC_CrLogical", creqv.Target); Assert.Equal(new long?[] { 5, 1, 2, 3 }, new[] { creqv.Arguments[0].Constant, creqv.Arguments[1].Constant, creqv.Arguments[2].Constant, creqv.Arguments[3].Constant }); var unknownLogical = Assert.IsType(Assert.Single(InvokePrivate>("LiftCrLogical", "unknown", "cr0lt", "cr0gt", "cr0eq"))); Assert.Equal(0, unknownLogical.Arguments[0].Constant); var indexedLoad = PpcInstruction.Synthetic(0x80004000, 0, "psq_lx", Array.Empty()); var indexedLoadOps = InvokePrivate>("LiftPairedSingleIndexedLoad", indexedLoad, (3u << 21) | (4u << 16) | (5u << 11) | (1u << 10) | (2u << 7), false); Assert.Equal("addr_psqx_80004000_loc", Assert.IsType(indexedLoadOps[0]).Destination); Assert.Equal("PPC_PsqL", Assert.IsType(indexedLoadOps[1]).Target); var indexedLoadZeroBase = InvokePrivate>("LiftPairedSingleIndexedLoad", indexedLoad, (3u << 21) | (0u << 16) | (5u << 11), false); Assert.Equal("addr_psqx_80004000_loc", Assert.IsType(indexedLoadZeroBase[0]).Destination); var indexedLoadUpdate = InvokePrivate>("LiftPairedSingleIndexedLoad", indexedLoad, (3u << 21) | (4u << 16) | (5u << 11), true); Assert.Equal("r4", Assert.IsType(indexedLoadUpdate[2]).Destination); var badIndexedLoad = Assert.Throws(() => InvokePrivate>("LiftPairedSingleIndexedLoad", indexedLoad, (3u << 21) | (0u << 16) | (5u << 11), true)); Assert.IsType(badIndexedLoad.InnerException); var indexedStore = PpcInstruction.Synthetic(0x80004004, 0, "psq_stx", Array.Empty()); var indexedStoreOps = InvokePrivate>("LiftPairedSingleIndexedStore", indexedStore, (6u << 21) | (7u << 16) | (8u << 11) | (1u << 10) | (3u << 7), false); Assert.Equal("addr_psqx_80004004_loc", Assert.IsType(indexedStoreOps[0]).Destination); Assert.Equal("PPC_PsqSt", Assert.IsType(indexedStoreOps[1]).Target); var indexedStoreZeroBase = InvokePrivate>("LiftPairedSingleIndexedStore", indexedStore, (6u << 21) | (0u << 16) | (8u << 11), false); Assert.Equal("addr_psqx_80004004_loc", Assert.IsType(indexedStoreZeroBase[0]).Destination); var indexedStoreUpdate = InvokePrivate>("LiftPairedSingleIndexedStore", indexedStore, (6u << 21) | (7u << 16) | (8u << 11), true); Assert.Equal("r7", Assert.IsType(indexedStoreUpdate[2]).Destination); var badIndexedStore = Assert.Throws(() => InvokePrivate>("LiftPairedSingleIndexedStore", indexedStore, (6u << 21) | (0u << 16) | (8u << 11), true)); Assert.IsType(badIndexedStore.InnerException); } }