See merge request ryubing/ryujinx!71
This commit is contained in:
Coxxs
2025-08-04 20:45:15 -05:00
committed by LotP
parent 324f18aa5f
commit d22756f1bd
58 changed files with 3394 additions and 25 deletions

View File

@@ -3,6 +3,7 @@ using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Runtime.InteropServices;
using ExecutionContext = ARMeilleure.State.ExecutionContext;
namespace ARMeilleure.Instructions
{
@@ -200,7 +201,11 @@ namespace ARMeilleure.Instructions
ExecutionContext context = GetContext();
context.CheckInterrupt();
// If debugging, we'll handle interrupts outside
if (!Optimizations.EnableDebugging)
{
context.CheckInterrupt();
}
Statistics.ResumeTimer();

View File

@@ -12,6 +12,7 @@ namespace ARMeilleure
public static bool AllowLcqInFunctionTable { get; set; } = true;
public static bool UseUnmanagedDispatchLoop { get; set; } = true;
public static bool EnableDebugging { get; set; } = false;
public static bool UseAdvSimdIfAvailable { get; set; } = true;
public static bool UseArm64AesIfAvailable { get; set; } = true;

View File

@@ -1,4 +1,5 @@
using ARMeilleure.Memory;
using System.Threading;
namespace ARMeilleure.State
{
@@ -10,7 +11,7 @@ namespace ARMeilleure.State
internal nint NativeContextPtr => _nativeContext.BasePtr;
private bool _interrupted;
internal bool Interrupted { get; private set; }
private readonly ICounter _counter;
@@ -65,6 +66,8 @@ namespace ARMeilleure.State
public bool IsAarch32 { get; set; }
public ulong ThreadUid { get; set; }
internal ExecutionMode ExecutionMode
{
get
@@ -90,14 +93,19 @@ namespace ARMeilleure.State
private readonly ExceptionCallbackNoArgs _interruptCallback;
private readonly ExceptionCallback _breakCallback;
private readonly ExceptionCallbackNoArgs _stepCallback;
private readonly ExceptionCallback _supervisorCallback;
private readonly ExceptionCallback _undefinedCallback;
internal int ShouldStep;
public ulong DebugPc { get; set; }
public ExecutionContext(
IJitMemoryAllocator allocator,
ICounter counter,
ExceptionCallbackNoArgs interruptCallback = null,
ExceptionCallback breakCallback = null,
ExceptionCallbackNoArgs stepCallback = null,
ExceptionCallback supervisorCallback = null,
ExceptionCallback undefinedCallback = null)
{
@@ -105,6 +113,7 @@ namespace ARMeilleure.State
_counter = counter;
_interruptCallback = interruptCallback;
_breakCallback = breakCallback;
_stepCallback = stepCallback;
_supervisorCallback = supervisorCallback;
_undefinedCallback = undefinedCallback;
@@ -127,9 +136,9 @@ namespace ARMeilleure.State
internal void CheckInterrupt()
{
if (_interrupted)
if (Interrupted)
{
_interrupted = false;
Interrupted = false;
_interruptCallback?.Invoke(this);
}
@@ -139,16 +148,37 @@ namespace ARMeilleure.State
public void RequestInterrupt()
{
_interrupted = true;
Interrupted = true;
}
public void StepHandler()
{
_stepCallback?.Invoke(this);
}
public void RequestDebugStep()
{
Interlocked.Exchange(ref ShouldStep, 1);
RequestInterrupt();
}
internal void OnBreak(ulong address, int imm)
{
if (Optimizations.EnableDebugging)
{
DebugPc = Pc;
}
_breakCallback?.Invoke(this, address, imm);
}
internal void OnSupervisorCall(ulong address, int imm)
{
if (Optimizations.EnableDebugging)
{
DebugPc = Pc;
}
_supervisorCallback?.Invoke(this, address, imm);
}

View File

@@ -22,6 +22,12 @@ namespace ARMeilleure.State
public ulong ExclusiveValueHigh;
public int Running;
public long Tpidr2El0;
/// <summary>
/// Precise PC value used for debugging.
/// This will only be set when Optimizations.EnableDebugging is true.
/// </summary>
public ulong DebugPrecisePc;
}
private static NativeCtxStorage _dummyStorage = new();
@@ -39,6 +45,11 @@ namespace ARMeilleure.State
public ulong GetPc()
{
if (Optimizations.EnableDebugging)
{
return GetStorage().DebugPrecisePc;
}
// TODO: More precise tracking of PC value.
return GetStorage().DispatchAddress;
}
@@ -268,6 +279,11 @@ namespace ARMeilleure.State
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
}
public static int GetDebugPrecisePcOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DebugPrecisePc);
}
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
{
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);

View File

@@ -33,7 +33,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 7008; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 7009; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";
@@ -303,6 +303,13 @@ namespace ARMeilleure.Translation.PTC
return false;
}
if (outerHeader.DebuggerMode != Optimizations.EnableDebugging)
{
InvalidateCompressedStream(compressedStream);
return false;
}
nint intPtr = nint.Zero;
try
@@ -479,6 +486,7 @@ namespace ARMeilleure.Translation.PTC
MemoryManagerMode = GetMemoryManagerMode(),
OSPlatform = GetOSPlatform(),
Architecture = (uint)RuntimeInformation.ProcessArchitecture,
DebuggerMode = Optimizations.EnableDebugging,
UncompressedStreamSize =
(long)Unsafe.SizeOf<InnerHeader>() +
@@ -1068,7 +1076,7 @@ namespace ARMeilleure.Translation.PTC
return osPlatform;
}
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 86*/)]
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 87*/)]
private struct OuterHeader
{
public ulong Magic;
@@ -1080,6 +1088,7 @@ namespace ARMeilleure.Translation.PTC
public byte MemoryManagerMode;
public uint OSPlatform;
public uint Architecture;
public bool DebuggerMode;
public long UncompressedStreamSize;

View File

@@ -119,7 +119,25 @@ namespace ARMeilleure.Translation
NativeInterface.RegisterThread(context, Memory, this);
if (Optimizations.UseUnmanagedDispatchLoop)
if (Optimizations.EnableDebugging)
{
context.DebugPc = address;
do
{
if (Interlocked.CompareExchange(ref context.ShouldStep, 0, 1) == 1)
{
context.DebugPc = Step(context, context.DebugPc);
context.StepHandler();
}
else
{
context.DebugPc = ExecuteSingle(context, context.DebugPc);
}
context.CheckInterrupt();
}
while (context.Running && context.DebugPc != 0);
}
else if (Optimizations.UseUnmanagedDispatchLoop)
{
Stubs.DispatchLoop(context.NativeContextPtr, address);
}
@@ -175,8 +193,24 @@ namespace ARMeilleure.Translation
return nextAddr;
}
public ulong Step(State.ExecutionContext context, ulong address)
private ulong Step(State.ExecutionContext context, ulong address)
{
try
{
OpCode opCode = Decoder.DecodeOpCode(Memory, address, context.ExecutionMode);
// For branch instructions during single-stepping, we handle them manually
// func.Execute() will sometimes execute the entire function call, which is not what we want
if (opCode.Instruction.Name is InstName.Bl or InstName.Blr or InstName.Blx or InstName.Br)
{
return ExecuteBranchInstructionForStepping(context, address, opCode);
}
}
catch
{
// ignore
}
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
address = func.Execute(Stubs.ContextWrapper, context);
@@ -186,6 +220,94 @@ namespace ARMeilleure.Translation
return address;
}
private static ulong ExecuteBranchInstructionForStepping(State.ExecutionContext context, ulong address, OpCode opCode)
{
switch (opCode.Instruction.Name)
{
case InstName.Bl:
if (opCode is IOpCodeBImm opBImm)
{
// Set link register
if (context.ExecutionMode == ExecutionMode.Aarch64)
{
context.SetX(30, address + (ulong)opCode.OpCodeSizeInBytes); // LR = X30
}
else
{
// For ARM32, need to set the appropriate return address
uint returnAddr = opCode is OpCode32 op32 && op32.IsThumb
? (uint)address + (uint)opCode.OpCodeSizeInBytes | 1u // Thumb bit set
: (uint)address + (uint)opCode.OpCodeSizeInBytes;
context.SetX(14, returnAddr); // LR = R14
}
return (ulong)opBImm.Immediate;
}
break;
case InstName.Blr:
if (opCode is OpCodeBReg opBReg)
{
// Set link register
if (context.ExecutionMode == ExecutionMode.Aarch64)
{
context.SetX(30, address + (ulong)opCode.OpCodeSizeInBytes); // LR = X30
}
else
{
uint returnAddr = opCode is OpCode32 op32 && op32.IsThumb
? (uint)address + (uint)opCode.OpCodeSizeInBytes | 1u // Thumb bit set
: (uint)address + (uint)opCode.OpCodeSizeInBytes;
context.SetX(14, returnAddr); // LR = R14
}
return context.GetX(opBReg.Rn);
}
break;
case InstName.Blx:
if (opCode is IOpCodeBImm opBlxImm)
{
// Handle mode switching for BLX
if (opCode is OpCode32 op32)
{
uint returnAddr = op32.IsThumb
? (uint)address + (uint)opCode.OpCodeSizeInBytes | 1u
: (uint)address + (uint)opCode.OpCodeSizeInBytes;
context.SetX(14, returnAddr);
// BLX switches between ARM and Thumb modes
context.SetPstateFlag(PState.TFlag, !op32.IsThumb);
}
return (ulong)opBlxImm.Immediate;
}
else if (opCode is IOpCode32BReg opBlxReg)
{
if (opCode is OpCode32 op32)
{
uint returnAddr = op32.IsThumb
? (uint)address + (uint)opCode.OpCodeSizeInBytes | 1u
: (uint)address + (uint)opCode.OpCodeSizeInBytes;
context.SetX(14, returnAddr);
// For BLX register, the target address determines the mode
ulong targetAddr = context.GetX(opBlxReg.Rm);
context.SetPstateFlag(PState.TFlag, (targetAddr & 1) != 0);
return targetAddr & ~1UL; // Clear the Thumb bit for the actual address
}
}
break;
case InstName.Br:
if (opCode is OpCodeBReg opBr)
{
// BR doesn't set link register, just branches to the target
return context.GetX(opBr.Rn);
}
break;
}
throw new InvalidOperationException($"Unhandled branch instruction: {opCode.Instruction.Name}");
}
internal TranslatedFunction GetOrTranslate(ulong address, ExecutionMode mode)
{
if (!Functions.TryGetValue(address, out TranslatedFunction func))
@@ -367,9 +489,13 @@ namespace ARMeilleure.Translation
if (block.Exit)
{
// Left option here as it may be useful if we need to return to managed rather than tail call in
// future. (eg. for debug)
bool useReturns = false;
// Return to managed rather than tail call.
bool useReturns = Optimizations.EnableDebugging;
if (Optimizations.EnableDebugging)
{
EmitDebugPrecisePcUpdate(context, block.Address);
}
InstEmitFlowHelper.EmitVirtualJump(context, Const(block.Address), isReturn: useReturns);
}
@@ -393,6 +519,11 @@ namespace ARMeilleure.Translation
}
}
if (Optimizations.EnableDebugging)
{
EmitDebugPrecisePcUpdate(context, opCode.Address);
}
Operand lblPredicateSkip = default;
if (context.IsInIfThenBlock && context.CurrentIfThenBlockCond != Condition.Al)
@@ -489,6 +620,14 @@ namespace ARMeilleure.Translation
context.MarkLabel(lblExit);
}
internal static void EmitDebugPrecisePcUpdate(EmitterContext context, ulong address)
{
long debugPrecisePcOffs = NativeContext.GetDebugPrecisePcOffset();
Operand debugPrecisePcAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(debugPrecisePcOffs));
context.Store(debugPrecisePcAddr, Const(address));
}
public void InvalidateJitCacheRegion(ulong address, ulong size)
{
ulong[] overlapAddresses = [];