From 35e045f9f7e8ecdfde98b82321b1e2a9bc97fac2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 12:52:38 +0000 Subject: [PATCH] Port the runtime and translator fixes from upstream's KartPad batch From patchzyy/Wiicompiled 6f14bde (#244), runtime and translator parts only: - Keep KD and NCD devices available while networking is off; only IP and SSL need the network. - Run RFLiIsWorking's alarm pump in a private interrupt context so callback register writes don't clobber the caller, and release the alarm recursion guard before rescheduling. - Share one LR continuation dispatch per translated function when it has several continuation calls. The renderer half of that commit is left out: it conflicts with the stereo renderer, and upstream reverted part of it (e409d9f) for stutters. (cherry picked from part of commit 6f14bde26a4ed6390d4f18e1b3a9c1be6819d589) Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_011tcyLz63pXjoYEwsFjfg8F --- runtime/src/hle/net/network_core.cpp | 11 ++-- runtime/src/hle/os/os_alarm.cpp | 22 ++++---- .../CxxLinearCodeGenerator.EmitInstruction.cs | 20 ++++--- .../CodeGen/CxxLinearCodeGenerator.cs | 22 ++++++-- .../SharedLrContinuationCodeGenTests.cs | 52 +++++++++++++++++++ 5 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 translator/tests/Translator.Tests/SharedLrContinuationCodeGenTests.cs diff --git a/runtime/src/hle/net/network_core.cpp b/runtime/src/hle/net/network_core.cpp index dacbbe3..7c68222 100644 --- a/runtime/src/hle/net/network_core.cpp +++ b/runtime/src/hle/net/network_core.cpp @@ -648,9 +648,10 @@ extern "C" int32_t Network_HLE_OpenDevice(const char* path, uint32_t mode) { if (!path) { return -101; } - if (!RuntimeConfigFile::NetworkEnabled(true)) { - // The guest opens several /dev/net nodes at boot and retries; report the - // reason online will not work exactly once. + const bool isIpTop = std::strcmp(path, "/dev/net/ip/top") == 0; + const bool isSsl = std::strcmp(path, "/dev/net/ssl") == 0; + // KD and NCD provide local identity/configuration services even offline. + if ((isIpTop || isSsl) && !RuntimeConfigFile::NetworkEnabled(true)) { static bool reported = false; if (!reported) { reported = true; @@ -665,10 +666,10 @@ extern "C" int32_t Network_HLE_OpenDevice(const char* path, uint32_t mode) { kind = DeviceKind::KdTime; } else if (std::strcmp(path, "/dev/net/ncd/manage") == 0) { kind = DeviceKind::NcdManage; - } else if (std::strcmp(path, "/dev/net/ip/top") == 0) { + } else if (isIpTop) { kind = DeviceKind::IpTop; EnsureSocketRuntime(); - } else if (std::strcmp(path, "/dev/net/ssl") == 0) { + } else if (isSsl) { kind = DeviceKind::Ssl; EnsureSocketRuntime(); } else { diff --git a/runtime/src/hle/os/os_alarm.cpp b/runtime/src/hle/os/os_alarm.cpp index a60e076..49abda7 100644 --- a/runtime/src/hle/os/os_alarm.cpp +++ b/runtime/src/hle/os/os_alarm.cpp @@ -222,7 +222,6 @@ bool ProcessAlarmQueue(CpuContext* cpu, int maxToProcess) throw; } DecrementSchedulerDisableCount(); - RunDeferredReschedule(cpu); } } } catch (const ::Memory::AccessViolation& e) { @@ -233,7 +232,7 @@ bool ProcessAlarmQueue(CpuContext* cpu, int maxToProcess) // Host DNS workers never touch guest memory. Commit their output here on // the scheduler thread, waking synchronous IOS waiters or queuing async IOS // callbacks before the callback drain below. - bool completionNeedsReschedule = false; + bool completionNeedsReschedule = handledAny; if (Network_HLE_ProcessCompletions(cpu)) { handledAny = true; completionNeedsReschedule = true; @@ -446,15 +445,18 @@ PPC_NATIVE_OVERRIDE_VOID(801A08E0, OS__SetPeriodicAlarm_801a08e0, (CpuContext* c // returning 0 when the manager pointer (0x80386298) is null. extern "C" uint32_t RFLiIsWorking_HLE_800bd860() { - // Pump alarms/callbacks on the current guest thread when available. Using a - // detached persistent context here can leave the busy loop waiting on work - // that completed on the wrong scheduling context. - CpuContext* cpu = TryGetCpuContext(); - if (!cpu) { - cpu = &GetPersistentCpuContext(); - } + // Alarm callbacks interrupt the caller; keep their register writes private. + GuestInterruptCallbackContext interrupt; + CpuContext* cpu = interrupt.get(); EnsureSda1Base(cpu); - ProcessAlarmQueue(cpu, 32); + IncrementSchedulerDisableCount(); + try { + ProcessAlarmQueue(cpu, 32); + } catch (...) { + DecrementSchedulerDisableCount(); + throw; + } + DecrementSchedulerDisableCount(); // Now return the actual "working" status constexpr uint32_t kRflManagerPtrAddr = 0x80386298u; diff --git a/translator/src/Translator.Core/CodeGen/CxxLinearCodeGenerator.EmitInstruction.cs b/translator/src/Translator.Core/CodeGen/CxxLinearCodeGenerator.EmitInstruction.cs index 6c2e0eb..b4874b1 100644 --- a/translator/src/Translator.Core/CodeGen/CxxLinearCodeGenerator.EmitInstruction.cs +++ b/translator/src/Translator.Core/CodeGen/CxxLinearCodeGenerator.EmitInstruction.cs @@ -27,7 +27,8 @@ public sealed partial class CxxLinearCodeGenerator IReadOnlyDictionary stateFreeAbiContracts, IReadOnlyDictionary stateFreeCallSymbols, IReadOnlyDictionary stateFreeCallSiteVariants, - IReadOnlySet modOverridableCallTargets) + IReadOnlySet modOverridableCallTargets, + bool shareLrContinuationDispatch) { if (ins is IrPhi) { @@ -410,11 +411,18 @@ public sealed partial class CxxLinearCodeGenerator fallbackPad = IndentPad(indent + 1); } - EmitLocalLrContinuationDispatch(sb, fallbackPad, labelNames); - sb.AppendLine($"{fallbackPad}if (TranslatedFunctionRegistry::FindByAddressPtr(ctx->lr) != nullptr) {{"); - sb.AppendLine($"{fallbackPad} InvokeIndirectCpu(ctx->lr, ctx);"); - sb.AppendLine($"{fallbackPad}}}"); - sb.AppendLine($"{fallbackPad}return;"); + if (shareLrContinuationDispatch) + { + sb.AppendLine($"{fallbackPad}goto lr_continuation_dispatch;"); + } + else + { + EmitLocalLrContinuationDispatch(sb, fallbackPad, labelNames); + sb.AppendLine($"{fallbackPad}if (TranslatedFunctionRegistry::FindByAddressPtr(ctx->lr) != nullptr) {{"); + sb.AppendLine($"{fallbackPad} InvokeIndirectCpu(ctx->lr, ctx);"); + sb.AppendLine($"{fallbackPad}}}"); + sb.AppendLine($"{fallbackPad}return;"); + } if (localFallthroughLr.HasValue) { sb.AppendLine($"{pad}}}"); diff --git a/translator/src/Translator.Core/CodeGen/CxxLinearCodeGenerator.cs b/translator/src/Translator.Core/CodeGen/CxxLinearCodeGenerator.cs index aef844d..0fb955c 100644 --- a/translator/src/Translator.Core/CodeGen/CxxLinearCodeGenerator.cs +++ b/translator/src/Translator.Core/CodeGen/CxxLinearCodeGenerator.cs @@ -241,12 +241,14 @@ public sealed partial class CxxLinearCodeGenerator var labelNames = func.Blocks.ToDictionary(b => b.Label, b => SanitizeLabel(b.Label), StringComparer.OrdinalIgnoreCase); var instructionContinuationLabels = new Dictionary(); - var needsInstructionContinuationLabels = func.Blocks + var continuationCallCount = func.Blocks .SelectMany(static block => block.Instructions) .OfType() - .Any(call => + .Count(call => TryParseAddress(call.Target, out var target) && (nonReturningCallTargets.Contains(target) || lrContinuationCallTargets.Contains(target))); + var needsInstructionContinuationLabels = continuationCallCount > 0; + var shareLrContinuationDispatch = continuationCallCount > 1; if (needsInstructionContinuationLabels) { foreach (var trace in func.Blocks.SelectMany(static block => block.Instructions).OfType()) @@ -405,7 +407,7 @@ public sealed partial class CxxLinearCodeGenerator _activeGpuFifoBurstSlot = gpuFifoBurstPlan.Slot(block.Label, i); try { - EmitInstruction(block.Label, directCallOrdinal, block.Instructions[i], body, bufferBaseLength, 1, cfg, labelNames, types, signature, localPaired, _guestAbiProvider, knownConstants, localConstants, linkedAddressRemap, nonReturningCallTargets, lrContinuationCallTargets, stackFacts, inlineGuestThunkStackBase, localFallthroughLr, guestAbiContracts, stateFreeAbiContracts, stateFreeCallSymbols, stateFreeCallSiteVariants, modOverridableCallTargets); + EmitInstruction(block.Label, directCallOrdinal, block.Instructions[i], body, bufferBaseLength, 1, cfg, labelNames, types, signature, localPaired, _guestAbiProvider, knownConstants, localConstants, linkedAddressRemap, nonReturningCallTargets, lrContinuationCallTargets, stackFacts, inlineGuestThunkStackBase, localFallthroughLr, guestAbiContracts, stateFreeAbiContracts, stateFreeCallSymbols, stateFreeCallSiteVariants, modOverridableCallTargets, shareLrContinuationDispatch); } finally { @@ -423,7 +425,7 @@ public sealed partial class CxxLinearCodeGenerator switch (term) { case IrUndefined undef: - EmitInstruction(block.Label, -1, undef, body, bufferBaseLength, 1, cfg, labelNames, types, signature, localPaired, _guestAbiProvider, knownConstants, localConstants, linkedAddressRemap, nonReturningCallTargets, lrContinuationCallTargets, stackFacts, inlineGuestThunkStackBase: false, localFallthroughLr: null, guestAbiContracts, stateFreeAbiContracts, stateFreeCallSymbols, stateFreeCallSiteVariants, modOverridableCallTargets); + EmitInstruction(block.Label, -1, undef, body, bufferBaseLength, 1, cfg, labelNames, types, signature, localPaired, _guestAbiProvider, knownConstants, localConstants, linkedAddressRemap, nonReturningCallTargets, lrContinuationCallTargets, stackFacts, inlineGuestThunkStackBase: false, localFallthroughLr: null, guestAbiContracts, stateFreeAbiContracts, stateFreeCallSymbols, stateFreeCallSiteVariants, modOverridableCallTargets, shareLrContinuationDispatch); AppendFlush(body, " "); body.AppendLine(" return;"); break; @@ -509,6 +511,18 @@ public sealed partial class CxxLinearCodeGenerator body.AppendLine(); } + if (shareLrContinuationDispatch) + { + // Every call site has already reloaded the callee's state. + // Keep the complete local target set, but emit it only once. + body.AppendLine(" return;"); + body.AppendLine("[[maybe_unused]] lr_continuation_dispatch:"); + EmitLocalLrContinuationDispatch(body, " ", labelNames); + body.AppendLine(" if (TranslatedFunctionRegistry::FindByAddressPtr(ctx->lr) != nullptr) {"); + body.AppendLine(" InvokeIndirectCpu(ctx->lr, ctx);"); + body.AppendLine(" }"); + body.AppendLine(" return;"); + } body.Append('}'); // The residency discovery pass only exists for its side effects // on the recorder; materializing its text costs a full copy of diff --git a/translator/tests/Translator.Tests/SharedLrContinuationCodeGenTests.cs b/translator/tests/Translator.Tests/SharedLrContinuationCodeGenTests.cs new file mode 100644 index 0000000..7204423 --- /dev/null +++ b/translator/tests/Translator.Tests/SharedLrContinuationCodeGenTests.cs @@ -0,0 +1,52 @@ +using Translator.Core.Analysis.Ssa; +using Translator.Core.Analysis.Representation; +using Translator.Core.CodeGen; +using Translator.Core.Ir; +using Translator.Core.Representation; +using Xunit; + +namespace Translator.Tests; + +public class SharedLrContinuationCodeGenTests +{ + [Theory] + [InlineData(2)] + [InlineData(20)] + public void MultipleHooksShareOneCompleteDispatchAndKeepNormalFallthrough(int callCount) + { + var instructions = new List(); + for (var i = 0; i < callCount; i++) + { + instructions.Add(new IrAssign("lr", IrValue.Imm(unchecked((int)(0x80001004u + (uint)i * 4u))))); + instructions.Add(new IrCall(string.Empty, "0x81800000", Array.Empty())); + } + instructions.Add(new IrAssign("r3", IrValue.Imm(8))); + instructions.Add(new IrReturn(null)); + var function = new IrFunction("shared_lr_continuation", "0x80001000", new[] + { + new IrBasicBlock("0x80001000", instructions), + new IrBasicBlock("0x80001100", new IrInstruction[] + { + new IrAssign("r3", IrValue.Imm(1)), new IrReturn(null) + }) + }); + var types = new RepresentationEnvironment(new Dictionary + { + ["lr"] = ValueRepresentation.UInt32, ["r3"] = ValueRepresentation.UInt32 + }); + var code = new CxxLinearCodeGenerator().Emit(0x80001000, + new SsaTransformer().Convert(function), + new FunctionAbiClassification("shared_lr_continuation", ValueRepresentation.Void), types, + lrContinuationCallTargets: new HashSet { 0x81800000u }); + + Assert.Equal(1, code.Split("switch (ctx->lr)").Length - 1); + Assert.Equal(callCount, code.Split("goto lr_continuation_dispatch;").Length - 1); + Assert.Equal(callCount, code.Split("if (ctx->lr != ").Length - 1); + Assert.Contains("case 0x80001100u:", code); + Assert.Contains("goto loc_80001100;", code); + Assert.Contains("InvokeIndirectCpu(ctx->lr, ctx);", code); + var sharedLabel = code.IndexOf("lr_continuation_dispatch:", StringComparison.Ordinal); + Assert.True(code.IndexOf("r3 = 8;", StringComparison.Ordinal) < sharedLabel); + Assert.True(code.IndexOf("case 0x80001100u:", StringComparison.Ordinal) > sharedLabel); + } +}