Implement a new JIT for Arm devices

This commit is contained in:
Gabriel A 2023-12-24 01:01:08 -03:00
parent 9864675a0b
commit 597710a522
127 changed files with 42538 additions and 25 deletions

View file

@ -0,0 +1,148 @@
using ARMeilleure.Memory;
using ARMeilleure.State;
using System;
namespace Ryujinx.Cpu.LightningJit.State
{
public class ExecutionContext : IExecutionContext
{
private const int MinCountForCheck = 4000;
private readonly NativeContext _nativeContext;
internal IntPtr NativeContextPtr => _nativeContext.BasePtr;
private bool _interrupted;
private readonly ICounter _counter;
public ulong Pc => _nativeContext.GetPc();
public ulong CntfrqEl0 => _counter.Frequency;
public ulong CntpctEl0 => _counter.Counter;
public long TpidrEl0
{
get => _nativeContext.GetTpidrEl0();
set => _nativeContext.SetTpidrEl0(value);
}
public long TpidrroEl0
{
get => _nativeContext.GetTpidrroEl0();
set => _nativeContext.SetTpidrroEl0(value);
}
public uint Pstate
{
get => _nativeContext.GetPstate();
set => _nativeContext.SetPstate(value);
}
public uint Fpsr
{
get => _nativeContext.GetFPState((uint)FPSR.Mask);
set => _nativeContext.SetFPState(value, (uint)FPSR.Mask);
}
public uint Fpcr
{
get => _nativeContext.GetFPState((uint)FPCR.Mask);
set => _nativeContext.SetFPState(value, (uint)FPCR.Mask);
}
public bool IsAarch32 { get; set; }
internal ExecutionMode ExecutionMode
{
get
{
if (IsAarch32)
{
return (Pstate & (1u << 5)) != 0
? ExecutionMode.Aarch32Thumb
: ExecutionMode.Aarch32Arm;
}
else
{
return ExecutionMode.Aarch64;
}
}
}
public bool Running
{
get => _nativeContext.GetRunning();
private set => _nativeContext.SetRunning(value);
}
private readonly ExceptionCallbackNoArgs _interruptCallback;
private readonly ExceptionCallback _breakCallback;
private readonly ExceptionCallback _supervisorCallback;
private readonly ExceptionCallback _undefinedCallback;
public ExecutionContext(IJitMemoryAllocator allocator, ICounter counter, ExceptionCallbacks exceptionCallbacks)
{
_nativeContext = new NativeContext(allocator);
_counter = counter;
_interruptCallback = exceptionCallbacks.InterruptCallback;
_breakCallback = exceptionCallbacks.BreakCallback;
_supervisorCallback = exceptionCallbacks.SupervisorCallback;
_undefinedCallback = exceptionCallbacks.UndefinedCallback;
Running = true;
_nativeContext.SetCounter(MinCountForCheck);
}
public ulong GetX(int index) => _nativeContext.GetX(index);
public void SetX(int index, ulong value) => _nativeContext.SetX(index, value);
public V128 GetV(int index) => _nativeContext.GetV(index);
public void SetV(int index, V128 value) => _nativeContext.SetV(index, value);
internal void CheckInterrupt()
{
if (_interrupted)
{
_interrupted = false;
_interruptCallback?.Invoke(this);
}
_nativeContext.SetCounter(MinCountForCheck);
}
public void RequestInterrupt()
{
_interrupted = true;
}
internal void OnBreak(ulong address, int imm)
{
_breakCallback?.Invoke(this, address, imm);
}
internal void OnSupervisorCall(ulong address, int imm)
{
_supervisorCallback?.Invoke(this, address, imm);
}
internal void OnUndefined(ulong address, int opCode)
{
_undefinedCallback?.Invoke(this, address, opCode);
}
public void StopRunning()
{
Running = false;
_nativeContext.SetCounter(0);
}
public void Dispose()
{
_nativeContext.Dispose();
}
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Cpu.LightningJit.State
{
enum ExecutionMode
{
Aarch32Arm = 0,
Aarch32Thumb = 1,
Aarch64 = 2,
}
}

View file

@ -0,0 +1,173 @@
using ARMeilleure.Memory;
using ARMeilleure.State;
using System;
using System.Runtime.CompilerServices;
namespace Ryujinx.Cpu.LightningJit.State
{
class NativeContext : IDisposable
{
private unsafe struct NativeCtxStorage
{
public fixed ulong X[32];
public fixed ulong V[64];
public uint Flags;
public uint FpFlags;
public long TpidrEl0;
public long TpidrroEl0;
public int Counter;
public uint HostFpFlags;
public ulong DispatchAddress;
public int Running;
}
private static NativeCtxStorage _dummyStorage = new();
private readonly IJitMemoryBlock _block;
public IntPtr BasePtr => _block.Pointer;
public NativeContext(IJitMemoryAllocator allocator)
{
_block = allocator.Allocate((ulong)Unsafe.SizeOf<NativeCtxStorage>());
}
public ulong GetPc()
{
// TODO: More precise tracking of PC value.
return GetStorage().DispatchAddress;
}
public unsafe ulong GetX(int index)
{
if ((uint)index >= 32)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return GetStorage().X[index];
}
public unsafe void SetX(int index, ulong value)
{
if ((uint)index >= 32)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
GetStorage().X[index] = value;
}
public unsafe V128 GetV(int index)
{
if ((uint)index >= 32)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return new V128(GetStorage().V[index * 2 + 0], GetStorage().V[index * 2 + 1]);
}
public unsafe void SetV(int index, V128 value)
{
if ((uint)index >= 32)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
GetStorage().V[index * 2 + 0] = value.Extract<ulong>(0);
GetStorage().V[index * 2 + 1] = value.Extract<ulong>(1);
}
public unsafe uint GetPstate()
{
return GetStorage().Flags;
}
public unsafe void SetPstate(uint value)
{
GetStorage().Flags = value;
}
public unsafe uint GetFPState(uint mask = uint.MaxValue)
{
return GetStorage().FpFlags & mask;
}
public unsafe void SetFPState(uint value, uint mask = uint.MaxValue)
{
GetStorage().FpFlags = (value & mask) | (GetStorage().FpFlags & ~mask);
}
public long GetTpidrEl0() => GetStorage().TpidrEl0;
public void SetTpidrEl0(long value) => GetStorage().TpidrEl0 = value;
public long GetTpidrroEl0() => GetStorage().TpidrroEl0;
public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value;
public int GetCounter() => GetStorage().Counter;
public void SetCounter(int value) => GetStorage().Counter = value;
public bool GetRunning() => GetStorage().Running != 0;
public void SetRunning(bool value) => GetStorage().Running = value ? 1 : 0;
public unsafe static int GetXOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.X[0]);
}
public unsafe static int GetVOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.V[0]);
}
public static int GetFlagsOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Flags);
}
public static int GetFpFlagsOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.FpFlags);
}
public static int GetTpidrEl0Offset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrEl0);
}
public static int GetTpidrroEl0Offset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0);
}
public static int GetCounterOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);
}
public static int GetHostFpFlagsOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.HostFpFlags);
}
public static int GetDispatchAddressOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.DispatchAddress);
}
public static int GetRunningOffset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Running);
}
private static int StorageOffset<T>(ref NativeCtxStorage storage, ref T target)
{
return (int)Unsafe.ByteOffset(ref Unsafe.As<NativeCtxStorage, T>(ref storage), ref target);
}
private unsafe ref NativeCtxStorage GetStorage() => ref Unsafe.AsRef<NativeCtxStorage>((void*)_block.Pointer);
public void Dispose() => _block.Dispose();
}
}