mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-10-22 14:15:12 -04:00
port fixes found in forks
This commit is contained in:
@@ -807,21 +807,17 @@ struct DlMayContainDrawVisitor {
|
||||
|
||||
bool OnNop(uint8_t) { return true; }
|
||||
bool OnBpReg(const uint8_t*, uint32_t) { return true; }
|
||||
// An array base/stride write means the list is setting up indexed geometry.
|
||||
bool OnCpReg(const uint8_t*, uint8_t reg, uint32_t) { return !(reg >= 0xA0 && reg <= 0xBF); }
|
||||
// CP writes must reach the HLE vertex state, and indexed XF loads need their
|
||||
// array resolved, so neither may be passed through raw.
|
||||
bool OnCpReg(const uint8_t*, uint8_t, uint32_t) { return false; }
|
||||
bool OnXfReg(const uint8_t*, uint32_t) { return true; }
|
||||
bool OnIndexedXf(const uint8_t*, uint8_t, uint32_t) { return true; }
|
||||
bool OnIndexedXf(const uint8_t*, uint8_t, uint32_t) { return false; }
|
||||
bool OnInvalidateVertexCache(uint8_t) { return true; }
|
||||
// Draw opcodes reach this too (kHandlesDraw is false): a draw opcode, or a
|
||||
// command byte this walker does not model, both mean "assume it draws".
|
||||
bool OnUnknownCommand(uint8_t) { return false; }
|
||||
bool OnCallDisplayList(uint32_t addr, uint32_t size, int depth) {
|
||||
if (addr == 0 || size == 0) return true;
|
||||
const uint8_t* nested = static_cast<const uint8_t*>(GuestToHostPtr(addr, size));
|
||||
if (!nested) return false;
|
||||
DlMayContainDrawVisitor child;
|
||||
return WalkDisplayList(nested, size, child, depth + 1);
|
||||
}
|
||||
// Aurora skips nested calls, so the child has to be flattened.
|
||||
bool OnCallDisplayList(uint32_t addr, uint32_t size, int) { return addr == 0 || size == 0; }
|
||||
};
|
||||
|
||||
static bool DisplayListMayContainDraw(const uint8_t* data, uint32_t nbytes) {
|
||||
@@ -1142,8 +1138,7 @@ static void ApplyAuroraVtxDesc() {
|
||||
// GXGetVtxDesc only decodes GX_VA_PNMTXIDX..GX_VA_TEX7; the XF array
|
||||
// pseudo-attributes above GX_VA_TEX7 have no CP descriptor bits, so
|
||||
// probing them would always report GX_NONE and republish a no-op.
|
||||
if (!needsPublish && !IsMatrixIndexAttr(gxAttr) && gxAttr <= GX_VA_TEX7 &&
|
||||
(type == GX_INDEX8 || type == GX_INDEX16)) {
|
||||
if (!needsPublish && gxAttr <= GX_VA_TEX7) {
|
||||
needsPublish = !AuroraVtxDescMatchesLive(gxAttr, type);
|
||||
}
|
||||
if (needsPublish) {
|
||||
|
||||
@@ -490,9 +490,16 @@ void HleFifoWrite(u32 val, uint32_t sizeBytes) {
|
||||
const uint16_t vtxCount = ReadBE16(data + 1);
|
||||
const GXVtxFmt vtxFmt = static_cast<GXVtxFmt>(cmd & GX_VAT_MASK_CMD);
|
||||
const GXPrimitive prim = OpcodeToGXPrimitive(cmd);
|
||||
if (vtxCount == 0) {
|
||||
if (!consumeBytes(3, sink)) break;
|
||||
continue;
|
||||
}
|
||||
uint32_t rawVertexSize = 0;
|
||||
if (TryGetRawDirectFifoVertexSize(vtxFmt, rawVertexSize)) {
|
||||
const uint32_t packetBytes = 3u + static_cast<uint32_t>(vtxCount) * rawVertexSize;
|
||||
const uint32_t packetBytes = TryGetRawDirectFifoVertexSize(vtxFmt, rawVertexSize)
|
||||
? 3u + static_cast<uint32_t>(vtxCount) * rawVertexSize
|
||||
: 0u;
|
||||
// A packet that cannot fit the buffer is streamed per vertex instead.
|
||||
if (packetBytes != 0 && packetBytes <= g_hleGxState.fifoBytes.size()) {
|
||||
if (g_hleGxState.fifoByteCount < packetBytes) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -703,8 +703,11 @@ extern "C" void GX__LoadTexObj_80170f2c(uint32_t oa, uint32_t tid) {
|
||||
}
|
||||
PPC_NATIVE_OVERRIDE_VOID(80170f2c, GX__LoadTexObj_80170f2c, (uint32_t oa, uint32_t tid), (oa, tid));
|
||||
|
||||
extern "C" void GX__LoadTexObjPreLoaded_80170dc8(uint32_t oa, uint32_t tid) { GX__LoadTexObj_80170f2c(oa, tid); }
|
||||
PPC_NATIVE_OVERRIDE_VOID(80170dc8, GX__LoadTexObjPreLoaded_80170dc8, (uint32_t oa, uint32_t tid), (oa, tid));
|
||||
extern "C" void GX__LoadTexObjPreLoaded_80170dc8(uint32_t oa, uint32_t region, uint32_t tid) {
|
||||
(void)region;
|
||||
GX__LoadTexObj_80170f2c(oa, tid);
|
||||
}
|
||||
PPC_NATIVE_OVERRIDE_VOID(80170dc8, GX__LoadTexObjPreLoaded_80170dc8, (uint32_t oa, uint32_t region, uint32_t tid), (oa, region, tid));
|
||||
|
||||
// ============================================================================
|
||||
// Texture Object Getters
|
||||
|
||||
@@ -150,7 +150,7 @@ extern "C" void GX__SetVtxAttrFmtv_8016de08(uint32_t vf, uint32_t la) {
|
||||
while(true){
|
||||
uint32_t a=Memory::Read32(p);
|
||||
if(a==0xFFu) break;
|
||||
list.push_back({a, Memory::Read32(p+4), Memory::Read32(p+8), Memory::Read32(p+12)&0xFFu});
|
||||
list.push_back({a, Memory::Read32(p+4), Memory::Read32(p+8), Memory::Read8(p+12)});
|
||||
p+=16;
|
||||
}
|
||||
GxWorker::Run([vf, list = std::move(list)] {
|
||||
@@ -326,7 +326,7 @@ extern "C" void GX__Begin_8016f0f0(uint32_t t, uint32_t vf, uint32_t nv) {
|
||||
static void GxBeginImpl(uint32_t t, uint32_t vf, uint32_t nv) {
|
||||
ApplyAuroraVtxStateForBegin(static_cast<GXVtxFmt>(vf));
|
||||
g_hleGxState.currentVtxFmt=(GXVtxFmt)vf; g_hleGxState.currentPrim=(GXPrimitive)t;
|
||||
g_hleGxState.vertsRemaining=nv; g_hleGxState.inBegin=true; g_hleGxState.auroraBeginCalled=false;
|
||||
g_hleGxState.vertsRemaining=nv; g_hleGxState.inBegin=nv!=0; g_hleGxState.auroraBeginCalled=false;
|
||||
g_hleGxState.fifoReadOffset = 0;
|
||||
g_hleGxState.fifoByteCount=0; g_hleGxState.ResetVertex();
|
||||
}
|
||||
|
||||
@@ -1031,16 +1031,17 @@ extern "C" int32_t DVDLowInit_80164848() {
|
||||
}
|
||||
PPC_NATIVE_OVERRIDE(80164848, DVDLowInit_80164848, int32_t, (), ());
|
||||
|
||||
extern "C" int32_t DVDLowInquiry_80165A30(uint32_t cmdBlockPtr, uint32_t callback)
|
||||
// DVDLowInquiry(DVDDriveInfo* info, DVDLowCallback cb); reports a retail drive.
|
||||
extern "C" int32_t DVDLowInquiry_80165A30(uint32_t driveInfoPtr, uint32_t callback)
|
||||
{
|
||||
// HLE: acknowledge the drive is present. Must mark the command block complete
|
||||
// (State = 0) or callers polling this address hang waiting for "Busy" to clear.
|
||||
if (cmdBlockPtr) {
|
||||
Memory::Write32(cmdBlockPtr + DVD_CB_OFFSET_STATE, DVD_STATE_END);
|
||||
if (driveInfoPtr && Memory::Contains(driveInfoPtr, 0x20)) {
|
||||
Memory::Write32(driveInfoPtr + 0x0, 0x00000002); // revision level, device code
|
||||
Memory::Write32(driveInfoPtr + 0x4, 0x20060526); // release date
|
||||
Memory::Write32(driveInfoPtr + 0x8, 0x41000000);
|
||||
}
|
||||
InvokeDvdLowCallback(callback, DvdReadContract::kInterruptTransferComplete);
|
||||
CompleteDvdCancelState();
|
||||
|
||||
return 1; // Return 1 to indicate the command was successfully issued.
|
||||
return 1;
|
||||
}
|
||||
PPC_NATIVE_OVERRIDE(80165A30, DVDLowInquiry_80165A30, int32_t, (uint32_t b, uint32_t c), (b, c));
|
||||
|
||||
@@ -1051,11 +1052,21 @@ extern "C" int32_t DVDLowReadDiskID_80164AAC(uint32_t diskIdPtr, uint32_t callba
|
||||
Memory::Write16(diskIdPtr + 0x04, 0x3031);
|
||||
Memory::Write8 (diskIdPtr + 0x06, 0x01);
|
||||
}
|
||||
InvokeDvdLowCallback(callback, DvdReadContract::kInterruptTransferComplete);
|
||||
CompleteDvdCancelState();
|
||||
return 1; // Success
|
||||
}
|
||||
PPC_NATIVE_OVERRIDE(80164AAC, DVDLowReadDiskID_80164AAC, int32_t, (uint32_t p, uint32_t c), (p, c));
|
||||
|
||||
// Host files need no head positioning.
|
||||
extern "C" int32_t DVDLowSeek_801664E0(uint32_t offset, uint32_t callback) {
|
||||
(void)offset;
|
||||
InvokeDvdLowCallback(callback, DvdReadContract::kInterruptTransferComplete);
|
||||
CompleteDvdCancelState();
|
||||
return 1;
|
||||
}
|
||||
PPC_NATIVE_OVERRIDE(801664E0, DVDLowSeek_801664E0, int32_t, (uint32_t o, uint32_t c), (o, c));
|
||||
|
||||
// 0x80166330 -> DVDLowRead (And 0x80165708 UnencryptedRead)
|
||||
// The game calls this to read the Disk Header (offset 0) or raw data.
|
||||
extern "C" int32_t DVDLowRead_80166330(uint32_t buffer, uint32_t length, uint32_t offset, uint32_t callback)
|
||||
|
||||
@@ -964,7 +964,7 @@ static int32_t HandleIsfsReadDir(uint32_t numIn, uint32_t numOut, uint32_t vecto
|
||||
}
|
||||
const std::filesystem::path hostPath = TranslateNandPath(wiiPath.c_str());
|
||||
if (!IsDirectory(hostPath)) {
|
||||
return ISFS_ENOENT;
|
||||
return PathExists(hostPath) ? ISFS_EINVAL : ISFS_ENOENT;
|
||||
}
|
||||
|
||||
// NAND names are at most 12 characters; longer host names cannot exist on
|
||||
|
||||
@@ -253,6 +253,12 @@ public sealed partial class CxxLinearCodeGenerator
|
||||
? index
|
||||
: -1;
|
||||
|
||||
private static uint ClearPairedBit(uint mask, string destination)
|
||||
{
|
||||
var bit = PairedFloatRegisterBit(destination);
|
||||
return bit >= 0 ? mask & ~(1u << bit) : mask;
|
||||
}
|
||||
|
||||
private static HashSet<string> MaterializePairedFloatSet(uint mask, StringComparer comparer)
|
||||
{
|
||||
var set = new HashSet<string>(comparer);
|
||||
@@ -454,11 +460,21 @@ public sealed partial class CxxLinearCodeGenerator
|
||||
break;
|
||||
}
|
||||
case IrLoad load:
|
||||
newOut = ClearPairedBit(newOut, load.Destination);
|
||||
break;
|
||||
case IrResolvedLoad load:
|
||||
newOut = ClearPairedBit(newOut, load.Destination);
|
||||
break;
|
||||
case IrResolvedLoadPair pair:
|
||||
newOut = ClearPairedBit(newOut, pair.FirstDestination);
|
||||
newOut = ClearPairedBit(newOut, pair.SecondDestination);
|
||||
break;
|
||||
case IrResolvedPsqLoad load:
|
||||
{
|
||||
var destBit = PairedFloatRegisterBit(load.Destination);
|
||||
if (destBit >= 0)
|
||||
{
|
||||
newOut &= ~(1u << destBit);
|
||||
newOut |= 1u << destBit;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ internal static class JumpTableDetector
|
||||
}
|
||||
entriesAreRelative = HasRelativeEntryAdd(ordered, loadIndex, mtctrIndex, targetRegister, baseReg.Name);
|
||||
|
||||
if (!TryFindUpperBound(ordered, loadIndex, indexReg.Name, out var upperBound) || upperBound < 0 || upperBound >= MaxEntryCount)
|
||||
if (!TryFindUpperBound(ordered, loadIndex, indexReg, out var upperBound) || upperBound < 0 || upperBound >= MaxEntryCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -381,47 +381,60 @@ internal static class JumpTableDetector
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryFindUpperBound(IReadOnlyList<PpcInstruction> ordered, int startIndex, string register, out int upperBound)
|
||||
private static bool TryFindUpperBound(IReadOnlyList<PpcInstruction> ordered, int startIndex, PpcRegisterOperand register, out int upperBound)
|
||||
{
|
||||
// Preferred: compare directly on the same register used for lwzx indexing.
|
||||
// Preferred: the compare on the lwzx index, followed back through the copies and
|
||||
// shifts that derived it. A compare older than the value's definition bounds
|
||||
// something else.
|
||||
var tracked = register.Number;
|
||||
for (var i = startIndex - 1; i >= 0 && startIndex - i <= MaxBacktrackInstructions; i--)
|
||||
{
|
||||
var ins = ordered[i];
|
||||
if (ins.Operands.Count < 2 || ins.Operands[0] is not PpcRegisterOperand reg ||
|
||||
!string.Equals(reg.Name, register, StringComparison.OrdinalIgnoreCase) ||
|
||||
ins.Operands[1] is not PpcImmediateOperand imm)
|
||||
if (TryGetCompareImmediate(ins, out var compared, out var bound))
|
||||
{
|
||||
if (compared == tracked)
|
||||
{
|
||||
upperBound = bound;
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ins.IsCall)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!DefinesGpr(ins, tracked))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ins.Mnemonic is "cmplwi" or "cmpwi")
|
||||
if (IsIndexDerivation(ins) &&
|
||||
ins.Operands[0] is PpcRegisterOperand dest && dest.Number == tracked &&
|
||||
ins.Operands[1] is PpcRegisterOperand source)
|
||||
{
|
||||
upperBound = imm.Value;
|
||||
return true;
|
||||
tracked = source.Number;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Fallback: some loops compare a logical counter (e.g. r18) distinct from the byte-offset
|
||||
// register (e.g. r30) that feeds lwzx, so the bound check may not target the index register.
|
||||
for (var i = startIndex - 1; i >= 0 && startIndex - i <= MaxBacktrackInstructions; i--)
|
||||
{
|
||||
var ins = ordered[i];
|
||||
if (ins.Operands.Count < 2 || ins.Operands[1] is not PpcImmediateOperand imm)
|
||||
if (ordered[i].Operands.Count > 0 && ordered[i].Operands[0] is PpcConditionRegisterOperand)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!TryGetCompareImmediate(ordered[i], out _, out var bound) || bound < 0 || bound >= MaxEntryCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ins.Mnemonic is not ("cmplwi" or "cmpwi"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (imm.Value < 0 || imm.Value >= MaxEntryCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
upperBound = imm.Value;
|
||||
upperBound = bound;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -429,6 +442,70 @@ internal static class JumpTableDetector
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copies, sign extensions and left shifts (slwi, clrlwi, clrlslwi) keep a bound on
|
||||
// the source a bound on the result's entry index.
|
||||
private static bool IsIndexDerivation(PpcInstruction ins) => ins.Mnemonic switch
|
||||
{
|
||||
"mr" or "extsb" or "extsh" => ins.Operands.Count >= 2,
|
||||
"rlwinm" => ins.Operands.Count == 5 &&
|
||||
ins.Operands[2] is PpcImmediateOperand sh &&
|
||||
ins.Operands[4] is PpcImmediateOperand me &&
|
||||
me.Value == 31 - sh.Value,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// MayWriteGpr treats both register fields of an X-form as written, which also
|
||||
// counts sources (or, extsb, stwx, cmpw); read the decoded destination instead.
|
||||
private static bool DefinesGpr(PpcInstruction ins, int register)
|
||||
{
|
||||
if (ins.Word >> 26 != 31)
|
||||
{
|
||||
return PpcRegisterEffects.MayWriteGpr(ins.Word, register);
|
||||
}
|
||||
|
||||
var mnemonic = ins.Mnemonic;
|
||||
if (mnemonic.EndsWith("ux", StringComparison.Ordinal) &&
|
||||
ins.Operands.Count > 1 && ins.Operands[1] is PpcRegisterOperand updated && updated.Number == register)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mnemonic.StartsWith("st", StringComparison.Ordinal) ||
|
||||
mnemonic.StartsWith("cmp", StringComparison.Ordinal) ||
|
||||
mnemonic.StartsWith("mt", StringComparison.Ordinal) ||
|
||||
mnemonic.StartsWith("dcb", StringComparison.Ordinal) ||
|
||||
mnemonic.StartsWith("icb", StringComparison.Ordinal) ||
|
||||
mnemonic.StartsWith("tw", StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ins.Operands.Count > 0 && ins.Operands[0] is PpcRegisterOperand dest && dest.Number == register;
|
||||
}
|
||||
|
||||
private static bool TryGetCompareImmediate(PpcInstruction ins, out int register, out int immediate)
|
||||
{
|
||||
register = -1;
|
||||
immediate = 0;
|
||||
if (ins.Mnemonic is not ("cmplwi" or "cmpwi"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var operands = ins.Operands;
|
||||
var first = operands.Count > 0 && operands[0] is PpcConditionRegisterOperand ? 1 : 0;
|
||||
if (operands.Count < first + 2 ||
|
||||
operands[first] is not PpcRegisterOperand reg ||
|
||||
operands[first + 1] is not PpcImmediateOperand imm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
register = reg.Number;
|
||||
immediate = imm.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadWord(ProgramImage image, uint address, out uint value)
|
||||
{
|
||||
if (!image.Contains(address, sizeof(uint)))
|
||||
|
||||
@@ -538,4 +538,122 @@ public class FunctionTranslatorCoverageTests
|
||||
Assert.Equal("// generated", result.CxxCode);
|
||||
Assert.Same(metrics, result.Metrics);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Translate_JumpTableBoundFollowsShiftedIndexPastStaleCompare()
|
||||
{
|
||||
const uint entry = 0x80000000;
|
||||
const uint table = 0x80000100;
|
||||
const uint case0 = entry + 0x20;
|
||||
const uint case1 = entry + 0x28;
|
||||
const uint case2 = entry + 0x30;
|
||||
|
||||
var image = TranslatorCppTestHarness.CreateImage(
|
||||
(entry + 0x00, 0x2C040001), // cmpwi r4, 1
|
||||
(entry + 0x04, 0x28030002), // cmplwi r3, 2
|
||||
(entry + 0x08, 0x5464103A), // slwi r4, r3, 2
|
||||
(entry + 0x0C, 0x3D808000), // lis r12, 0x8000
|
||||
(entry + 0x10, 0x398C0100), // addi r12, r12, 0x100
|
||||
(entry + 0x14, 0x7C0C202E), // lwzx r0, r12, r4
|
||||
(entry + 0x18, 0x7C0903A6), // mtctr r0
|
||||
(entry + 0x1C, 0x4E800420), // bctr
|
||||
(case0 + 0x00, 0x38600007), // li r3, 7
|
||||
(case0 + 0x04, 0x4E800020), // blr
|
||||
(case1 + 0x00, 0x38600009), // li r3, 9
|
||||
(case1 + 0x04, 0x4E800020), // blr
|
||||
(case2 + 0x00, 0x3860000B), // li r3, 11
|
||||
(case2 + 0x04, 0x4E800020), // blr
|
||||
(table + 0x00, case0),
|
||||
(table + 0x04, case1),
|
||||
(table + 0x08, case2));
|
||||
var result = new FunctionTranslator(image).Translate(
|
||||
entry,
|
||||
TranslationOptions.Default with
|
||||
{
|
||||
MaxBytes = 0x80,
|
||||
AllowUnsupportedInstructions = true,
|
||||
KnownFunctionEntryPoints = new HashSet<uint> { entry }
|
||||
});
|
||||
|
||||
Assert.Contains("case 0x80000030u:", result.CxxCode, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Translate_JumpTableBoundSurvivesCopyOfIndex()
|
||||
{
|
||||
const uint entry = 0x80000000;
|
||||
const uint table = 0x80000100;
|
||||
const uint case0 = entry + 0x28;
|
||||
const uint case1 = entry + 0x30;
|
||||
const uint case2 = entry + 0x38;
|
||||
|
||||
var image = TranslatorCppTestHarness.CreateImage(
|
||||
(entry + 0x00, 0x28030002), // cmplwi r3, 2
|
||||
(entry + 0x04, 0x7C7F1B78), // mr r31, r3
|
||||
(entry + 0x08, 0x2C040001), // cmpwi r4, 1
|
||||
(entry + 0x0C, 0x5460103A), // slwi r0, r3, 2
|
||||
(entry + 0x10, 0x3D808000), // lis r12, 0x8000
|
||||
(entry + 0x14, 0x398C0100), // addi r12, r12, 0x100
|
||||
(entry + 0x18, 0x7C0C002E), // lwzx r0, r12, r0
|
||||
(entry + 0x1C, 0x7C0903A6), // mtctr r0
|
||||
(entry + 0x20, 0x4E800420), // bctr
|
||||
(entry + 0x24, 0x4E800020), // blr
|
||||
(case0 + 0x00, 0x38600007), // li r3, 7
|
||||
(case0 + 0x04, 0x4E800020), // blr
|
||||
(case1 + 0x00, 0x38600009), // li r3, 9
|
||||
(case1 + 0x04, 0x4E800020), // blr
|
||||
(case2 + 0x00, 0x3860000B), // li r3, 11
|
||||
(case2 + 0x04, 0x4E800020), // blr
|
||||
(table + 0x00, case0),
|
||||
(table + 0x04, case1),
|
||||
(table + 0x08, case2));
|
||||
var result = new FunctionTranslator(image).Translate(
|
||||
entry,
|
||||
TranslationOptions.Default with
|
||||
{
|
||||
MaxBytes = 0x80,
|
||||
AllowUnsupportedInstructions = true,
|
||||
KnownFunctionEntryPoints = new HashSet<uint> { entry }
|
||||
});
|
||||
|
||||
Assert.Contains("case 0x80000038u:", result.CxxCode, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Translate_JumpTableBoundAcceptsNonCr0Compare()
|
||||
{
|
||||
const uint entry = 0x80000000;
|
||||
const uint table = 0x80000100;
|
||||
const uint case0 = entry + 0x1C;
|
||||
const uint case1 = entry + 0x24;
|
||||
const uint case2 = entry + 0x2C;
|
||||
|
||||
var image = TranslatorCppTestHarness.CreateImage(
|
||||
(entry + 0x00, 0x28830002), // cmplwi cr1, r3, 2
|
||||
(entry + 0x04, 0x5463103A), // slwi r3, r3, 2
|
||||
(entry + 0x08, 0x3D808000), // lis r12, 0x8000
|
||||
(entry + 0x0C, 0x398C0100), // addi r12, r12, 0x100
|
||||
(entry + 0x10, 0x7C0C182E), // lwzx r0, r12, r3
|
||||
(entry + 0x14, 0x7C0903A6), // mtctr r0
|
||||
(entry + 0x18, 0x4E800420), // bctr
|
||||
(case0 + 0x00, 0x38600007), // li r3, 7
|
||||
(case0 + 0x04, 0x4E800020), // blr
|
||||
(case1 + 0x00, 0x38600009), // li r3, 9
|
||||
(case1 + 0x04, 0x4E800020), // blr
|
||||
(case2 + 0x00, 0x3860000B), // li r3, 11
|
||||
(case2 + 0x04, 0x4E800020), // blr
|
||||
(table + 0x00, case0),
|
||||
(table + 0x04, case1),
|
||||
(table + 0x08, case2));
|
||||
var result = new FunctionTranslator(image).Translate(
|
||||
entry,
|
||||
TranslationOptions.Default with
|
||||
{
|
||||
MaxBytes = 0x80,
|
||||
AllowUnsupportedInstructions = true,
|
||||
KnownFunctionEntryPoints = new HashSet<uint> { entry }
|
||||
});
|
||||
|
||||
Assert.Contains("case 0x8000002Cu:", result.CxxCode, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,4 +152,69 @@ public sealed class PairedFlowCodeGenTests
|
||||
Assert.Contains("SetCRFloatResident(cr, 0, f31.d, f1.d);", code, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("SetCRFloatResident(cr, 0, PPC_PsToScalarInline(f31.d)", code, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvedScalarLoadEndsPairedStateAcrossEdge()
|
||||
{
|
||||
var function = new IrFunction("resolved_load_clears_paired", "entry", new[]
|
||||
{
|
||||
new IrBasicBlock("entry", new IrInstruction[]
|
||||
{
|
||||
new IrCall("f5", "PPC_PsMul", new[] { IrValue.Register("f1"), IrValue.Register("f2") }),
|
||||
new IrResolveGuestMemoryRange("range", IrValue.Register("r3"), 0, 8, true, false),
|
||||
new IrResolvedLoad("f5", "range", new IrAddress("r3", 0), 0, 8),
|
||||
new IrJump("next")
|
||||
}),
|
||||
new IrBasicBlock("next", new IrInstruction[]
|
||||
{
|
||||
new IrSetCrField(0, IrValue.Register("f5"), IrValue.Register("f1"), false),
|
||||
new IrReturn(null)
|
||||
})
|
||||
});
|
||||
var types = new RepresentationEnvironment(new Dictionary<string, ValueRepresentation>
|
||||
{
|
||||
["f1"] = ValueRepresentation.Float64,
|
||||
["f2"] = ValueRepresentation.Float64,
|
||||
["f5"] = ValueRepresentation.Float64,
|
||||
["r3"] = ValueRepresentation.UInt32
|
||||
});
|
||||
var signature = new FunctionAbiClassification("resolved_load_clears_paired", ValueRepresentation.Void);
|
||||
|
||||
var code = new CxxLinearCodeGenerator().Emit(
|
||||
0x80006008, new SsaTransformer().Convert(function), signature, types);
|
||||
|
||||
Assert.Contains("SetCRFloatResident(cr, 0, f5.d, f1.d);", code, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("PPC_PsToScalarInline(f5.d)", code, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolvedPsqLoadStaysPairedAcrossEdge()
|
||||
{
|
||||
var function = new IrFunction("resolved_psq_load_paired", "entry", new[]
|
||||
{
|
||||
new IrBasicBlock("entry", new IrInstruction[]
|
||||
{
|
||||
new IrResolveGuestMemoryRange("range", IrValue.Register("r3"), 0, 8, true, false),
|
||||
new IrResolvedPsqLoad("f5", "range", IrValue.Register("r3"), 0, 0, 0, 0u),
|
||||
new IrJump("next")
|
||||
}),
|
||||
new IrBasicBlock("next", new IrInstruction[]
|
||||
{
|
||||
new IrSetCrField(0, IrValue.Register("f5"), IrValue.Register("f1"), false),
|
||||
new IrReturn(null)
|
||||
})
|
||||
});
|
||||
var types = new RepresentationEnvironment(new Dictionary<string, ValueRepresentation>
|
||||
{
|
||||
["f1"] = ValueRepresentation.Float64,
|
||||
["f5"] = ValueRepresentation.Float64,
|
||||
["r3"] = ValueRepresentation.UInt32
|
||||
});
|
||||
var signature = new FunctionAbiClassification("resolved_psq_load_paired", ValueRepresentation.Void);
|
||||
|
||||
var code = new CxxLinearCodeGenerator().Emit(
|
||||
0x8000600C, new SsaTransformer().Convert(function), signature, types);
|
||||
|
||||
Assert.Contains("SetCRFloatResident(cr, 0, PPC_PsToScalarInline(f5.d), f1.d);", code, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user