mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-10 17:16:47 -04:00
163 lines
7.5 KiB
C#
163 lines
7.5 KiB
C#
using Translator.Core.Disassembly;
|
|
using Xunit;
|
|
|
|
namespace Translator.Tests;
|
|
|
|
public sealed class PpcWordAnalysisTests
|
|
{
|
|
[Fact]
|
|
public void FieldsExposeSplitSprAndImmediateForms()
|
|
{
|
|
var fields = new PpcWordFields(0x7D8903A6u); // mtctr r12
|
|
|
|
Assert.Equal(31, fields.PrimaryOpcode);
|
|
Assert.Equal(12, fields.GprField0);
|
|
Assert.Equal(9, fields.Spr);
|
|
Assert.Equal(467, fields.ExtendedOpcode);
|
|
|
|
var immediateFields = new PpcWordFields(0x398CFFF0u); // addi r12,r12,-0x10
|
|
Assert.Equal(-16, immediateFields.SignedImmediate16);
|
|
Assert.Equal(0xFFF0u, immediateFields.UnsignedImmediate16);
|
|
}
|
|
|
|
[Fact]
|
|
public void ImmediateAndRegisterPatternsPreserveArchitecturalFieldOrder()
|
|
{
|
|
Assert.True(PpcInstructionPatterns.TryGetLis(0x3D80807Eu, out var lisDestination, out var high));
|
|
Assert.Equal(12, lisDestination);
|
|
Assert.Equal(0x807Eu, high);
|
|
|
|
Assert.True(PpcInstructionPatterns.TryGetAddi(0x398CFFF0u, out var addiDestination, out var addiSource, out var addiImmediate));
|
|
Assert.Equal(12, addiDestination);
|
|
Assert.Equal(12, addiSource);
|
|
Assert.Equal(-16, addiImmediate);
|
|
|
|
Assert.True(PpcInstructionPatterns.TryGetOri(0x618C3064u, out var oriSource, out var oriDestination, out var oriImmediate));
|
|
Assert.Equal(12, oriSource);
|
|
Assert.Equal(12, oriDestination);
|
|
Assert.Equal(0x3064u, oriImmediate);
|
|
|
|
var oris = EncodeImmediate(25, source: 12, destination: 9, immediate: 0x1234);
|
|
Assert.True(PpcInstructionPatterns.TryGetOris(oris, out var orisSource, out var orisDestination, out var orisImmediate));
|
|
Assert.Equal(12, orisSource);
|
|
Assert.Equal(9, orisDestination);
|
|
Assert.Equal(0x1234u, orisImmediate);
|
|
|
|
var orWord = EncodeXForm(source: 12, destination: 9, otherSource: 12, extendedOpcode: 444);
|
|
Assert.True(PpcInstructionPatterns.TryGetOr(orWord, out var source, out var destination, out var otherSource));
|
|
Assert.Equal(12, source);
|
|
Assert.Equal(9, destination);
|
|
Assert.Equal(12, otherSource);
|
|
|
|
var rlwinm = EncodeRlwinm(source: 3, destination: 4, shift: 0, maskBegin: 16, maskEnd: 31);
|
|
Assert.True(PpcInstructionPatterns.TryGetRlwinm(rlwinm, out var rotateSource, out var rotateDestination, out var shift, out var maskBegin, out var maskEnd));
|
|
Assert.Equal(3, rotateSource);
|
|
Assert.Equal(4, rotateDestination);
|
|
Assert.Equal(0, shift);
|
|
Assert.Equal(16, maskBegin);
|
|
Assert.Equal(31, maskEnd);
|
|
}
|
|
|
|
[Fact]
|
|
public void SprPatternsRecognizeOnlyTheRequestedSpecialRegister()
|
|
{
|
|
Assert.True(PpcInstructionPatterns.TryGetMtspr(0x7D8903A6u, 9, out var ctrSource));
|
|
Assert.Equal(12, ctrSource);
|
|
Assert.False(PpcInstructionPatterns.TryGetMtspr(0x7D8903A6u, 8, out _));
|
|
|
|
Assert.True(PpcInstructionPatterns.TryGetMtspr(0x7C0803A6u, 8, out var lrSource));
|
|
Assert.Equal(0, lrSource);
|
|
}
|
|
|
|
[Fact]
|
|
public void BranchPatternsPreserveLinkAbsoluteAndConditionalDistinctions()
|
|
{
|
|
const uint address = 0x80000100u;
|
|
|
|
Assert.True(PpcControlFlow.IsRelativeUnlinkedBranch(0x48000008u));
|
|
Assert.True(PpcControlFlow.TryDecodeRelativeBranchTarget(address, 0x48000008u, out var forward));
|
|
Assert.Equal(0x80000108u, forward);
|
|
|
|
Assert.True(PpcControlFlow.IsRelativeLinkedBranch(0x48000001u));
|
|
Assert.True(PpcControlFlow.TryDecodeRelativeBranchLinkTarget(address, 0x48000001u, out var linked));
|
|
Assert.Equal(address, linked);
|
|
|
|
Assert.False(PpcControlFlow.IsRelativeUnlinkedBranch(0x48000002u)); // absolute b
|
|
Assert.False(PpcControlFlow.TryDecodeRelativeBranchTarget(address, 0x48000001u, out _)); // bl
|
|
|
|
Assert.True(PpcControlFlow.TryDecodeConditionalRelativeBranchTarget(address, 0x4182FFFCu, out var conditional));
|
|
Assert.Equal(0x800000FCu, conditional);
|
|
Assert.True(PpcControlFlow.MayChangeControlFlow(0x4182FFFCu));
|
|
Assert.True(PpcControlFlow.MayChangeControlFlow(0x48000000u));
|
|
Assert.True(PpcControlFlow.MayChangeControlFlow(0x4E800020u));
|
|
Assert.False(PpcControlFlow.MayChangeControlFlow(0x398CFFF0u));
|
|
}
|
|
|
|
[Fact]
|
|
public void ConservativeWritePolicyUsesTheArchitecturalDestinationRegister()
|
|
{
|
|
// ori RA, RS, UIMM writes RA. Two of the three scanners used to key this
|
|
// on RS, so they evicted the source and kept a stale value in the
|
|
// register the instruction actually overwrote.
|
|
var ori = EncodeImmediate(24, source: 3, destination: 4, immediate: 1);
|
|
Assert.True(PpcRegisterEffects.MayWriteGpr(ori, 4));
|
|
Assert.False(PpcRegisterEffects.MayWriteGpr(ori, 3));
|
|
|
|
// addi RT, RA, SIMM writes RT, which this encoder puts in the source field.
|
|
var addi = EncodeImmediate(14, source: 5, destination: 6, immediate: 8);
|
|
Assert.True(PpcRegisterEffects.MayWriteGpr(addi, 5));
|
|
Assert.False(PpcRegisterEffects.MayWriteGpr(addi, 6));
|
|
|
|
// lwzu RT, d(RA) writes both RT and the RA base.
|
|
var lwzu = EncodeImmediate(33, source: 7, destination: 8, immediate: 4);
|
|
Assert.True(PpcRegisterEffects.MayWriteGpr(lwzu, 7));
|
|
Assert.True(PpcRegisterEffects.MayWriteGpr(lwzu, 8));
|
|
|
|
// stwu RS, d(RA) writes the RA base back; stw writes no register.
|
|
var stwu = EncodeImmediate(37, source: 1, destination: 1, immediate: 0xFFF0);
|
|
Assert.True(PpcRegisterEffects.MayWriteGpr(stwu, 1));
|
|
var stw = EncodeImmediate(36, source: 9, destination: 10, immediate: 0);
|
|
Assert.False(PpcRegisterEffects.MayWriteGpr(stw, 9));
|
|
Assert.False(PpcRegisterEffects.MayWriteGpr(stw, 10));
|
|
|
|
// lfd writes an FPR, so no GPR dies; lfdu still writes its RA base.
|
|
var lfd = EncodeImmediate(50, source: 11, destination: 12, immediate: 0);
|
|
Assert.False(PpcRegisterEffects.MayWriteGpr(lfd, 11));
|
|
Assert.False(PpcRegisterEffects.MayWriteGpr(lfd, 12));
|
|
var lfdu = EncodeImmediate(51, source: 11, destination: 12, immediate: 8);
|
|
Assert.True(PpcRegisterEffects.MayWriteGpr(lfdu, 12));
|
|
|
|
// cmpi writes a CR field, not a GPR.
|
|
var cmpi = EncodeImmediate(11, source: 0, destination: 13, immediate: 0);
|
|
Assert.False(PpcRegisterEffects.MayWriteGpr(cmpi, 0));
|
|
Assert.False(PpcRegisterEffects.MayWriteGpr(cmpi, 13));
|
|
|
|
// X-form is treated as clobbering either candidate: `add r5,r5,r6` and
|
|
// `or r5,r6,r6` share a primary opcode but not a destination field.
|
|
var xform = EncodeImmediate(31, source: 14, destination: 15, immediate: 0);
|
|
Assert.True(PpcRegisterEffects.MayWriteGpr(xform, 14));
|
|
Assert.True(PpcRegisterEffects.MayWriteGpr(xform, 15));
|
|
}
|
|
|
|
private static uint EncodeImmediate(int opcode, int source, int destination, int immediate) =>
|
|
((uint)opcode << 26) |
|
|
((uint)source << 21) |
|
|
((uint)destination << 16) |
|
|
(uint)(immediate & 0xFFFF);
|
|
|
|
private static uint EncodeXForm(int source, int destination, int otherSource, int extendedOpcode) =>
|
|
(31u << 26) |
|
|
((uint)source << 21) |
|
|
((uint)destination << 16) |
|
|
((uint)otherSource << 11) |
|
|
((uint)extendedOpcode << 1);
|
|
|
|
private static uint EncodeRlwinm(int source, int destination, int shift, int maskBegin, int maskEnd) =>
|
|
(21u << 26) |
|
|
((uint)source << 21) |
|
|
((uint)destination << 16) |
|
|
((uint)shift << 11) |
|
|
((uint)maskBegin << 6) |
|
|
((uint)maskEnd << 1);
|
|
}
|