Ryujinx/ChocolArm64/Instruction/AInstEmitMemoryEx.cs
gdkchan b8133c1997
Thread scheduler rewrite (#393)
* Started to rewrite the thread scheduler

* Add a single core-like scheduling mode, enabled by default

* Clear exclusive monitor on context switch

* Add SetThreadActivity, misc fixes

* Implement WaitForAddress and SignalToAddress svcs, misc fixes

* Misc fixes (on SetActivity and Arbiter), other tweaks

* Rebased

* Add missing null check

* Rename multicore key on config, fix UpdatePriorityInheritance

* Make scheduling data MLQs private

* nit: Ordering
2018-09-18 20:36:43 -03:00

192 lines
6 KiB
C#

using ChocolArm64.Decoder;
using ChocolArm64.Memory;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
using System.Threading;
using static ChocolArm64.Instruction.AInstEmitMemoryHelper;
namespace ChocolArm64.Instruction
{
static partial class AInstEmit
{
[Flags]
private enum AccessType
{
None = 0,
Ordered = 1,
Exclusive = 2,
OrderedEx = Ordered | Exclusive
}
public static void Clrex(AILEmitterCtx Context)
{
EmitMemoryCall(Context, nameof(AMemory.ClearExclusive));
}
public static void Dmb(AILEmitterCtx Context) => EmitBarrier(Context);
public static void Dsb(AILEmitterCtx Context) => EmitBarrier(Context);
public static void Ldar(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Ordered);
public static void Ldaxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.OrderedEx);
public static void Ldxr(AILEmitterCtx Context) => EmitLdr(Context, AccessType.Exclusive);
public static void Ldxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.Exclusive);
public static void Ldaxp(AILEmitterCtx Context) => EmitLdp(Context, AccessType.OrderedEx);
private static void EmitLdr(AILEmitterCtx Context, AccessType AccType)
{
EmitLoad(Context, AccType, false);
}
private static void EmitLdp(AILEmitterCtx Context, AccessType AccType)
{
EmitLoad(Context, AccType, true);
}
private static void EmitLoad(AILEmitterCtx Context, AccessType AccType, bool Pair)
{
AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp;
bool Ordered = (AccType & AccessType.Ordered) != 0;
bool Exclusive = (AccType & AccessType.Exclusive) != 0;
if (Ordered)
{
EmitBarrier(Context);
}
if (Exclusive)
{
EmitMemoryCall(Context, nameof(AMemory.SetExclusive), Op.Rn);
}
Context.EmitLdint(Op.Rn);
Context.EmitSttmp();
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
Context.EmitLdtmp();
EmitReadZxCall(Context, Op.Size);
Context.EmitStintzr(Op.Rt);
if (Pair)
{
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
Context.EmitLdtmp();
Context.EmitLdc_I8(1 << Op.Size);
Context.Emit(OpCodes.Add);
EmitReadZxCall(Context, Op.Size);
Context.EmitStintzr(Op.Rt2);
}
}
public static void Pfrm(AILEmitterCtx Context)
{
//Memory Prefetch, execute as no-op.
}
public static void Stlr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Ordered);
public static void Stlxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.OrderedEx);
public static void Stxr(AILEmitterCtx Context) => EmitStr(Context, AccessType.Exclusive);
public static void Stxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.Exclusive);
public static void Stlxp(AILEmitterCtx Context) => EmitStp(Context, AccessType.OrderedEx);
private static void EmitStr(AILEmitterCtx Context, AccessType AccType)
{
EmitStore(Context, AccType, false);
}
private static void EmitStp(AILEmitterCtx Context, AccessType AccType)
{
EmitStore(Context, AccType, true);
}
private static void EmitStore(AILEmitterCtx Context, AccessType AccType, bool Pair)
{
AOpCodeMemEx Op = (AOpCodeMemEx)Context.CurrOp;
bool Ordered = (AccType & AccessType.Ordered) != 0;
bool Exclusive = (AccType & AccessType.Exclusive) != 0;
if (Ordered)
{
EmitBarrier(Context);
}
AILLabel LblEx = new AILLabel();
AILLabel LblEnd = new AILLabel();
if (Exclusive)
{
EmitMemoryCall(Context, nameof(AMemory.TestExclusive), Op.Rn);
Context.Emit(OpCodes.Brtrue_S, LblEx);
Context.EmitLdc_I8(1);
Context.EmitStintzr(Op.Rs);
Context.Emit(OpCodes.Br_S, LblEnd);
}
Context.MarkLabel(LblEx);
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
Context.EmitLdint(Op.Rn);
Context.EmitLdintzr(Op.Rt);
EmitWriteCall(Context, Op.Size);
if (Pair)
{
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
Context.EmitLdint(Op.Rn);
Context.EmitLdc_I8(1 << Op.Size);
Context.Emit(OpCodes.Add);
Context.EmitLdintzr(Op.Rt2);
EmitWriteCall(Context, Op.Size);
}
if (Exclusive)
{
Context.EmitLdc_I8(0);
Context.EmitStintzr(Op.Rs);
EmitMemoryCall(Context, nameof(AMemory.ClearExclusiveForStore));
}
Context.MarkLabel(LblEnd);
}
private static void EmitMemoryCall(AILEmitterCtx Context, string Name, int Rn = -1)
{
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Core));
if (Rn != -1)
{
Context.EmitLdint(Rn);
}
Context.EmitCall(typeof(AMemory), Name);
}
private static void EmitBarrier(AILEmitterCtx Context)
{
//Note: This barrier is most likely not necessary, and probably
//doesn't make any difference since we need to do a ton of stuff
//(software MMU emulation) to read or write anything anyway.
Context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier));
}
}
}