Merge branch 'profiling' into actually_profiling

This commit is contained in:
Andy Adshead 2019-02-02 13:15:30 +00:00
commit fad35bd2b5
91 changed files with 2727 additions and 769 deletions

View file

@ -25,8 +25,6 @@ namespace ChocolArm64
ThreadState = new CpuThreadState();
ThreadState.ExecutionMode = ExecutionMode.AArch64;
ThreadState.Running = true;
Work = new Thread(delegate()

View file

@ -1,4 +1,4 @@
namespace ChocolArm64
namespace ChocolArm64.Decoders
{
static class BitUtils
{
@ -36,6 +36,16 @@ namespace ChocolArm64
return bits == 64 ? -1L : (1L << bits) - 1;
}
public static int RotateRight(int bits, int shift, int size)
{
return (int)RotateRight((uint)bits, shift, size);
}
public static uint RotateRight(uint bits, int shift, int size)
{
return (bits >> shift) | (bits << (size - shift));
}
public static long RotateRight(long bits, int shift, int size)
{
return (long)RotateRight((ulong)bits, shift, size);

View file

@ -1,6 +1,6 @@
namespace ChocolArm64.Decoders
{
enum Cond
enum Condition
{
Eq = 0,
Ne = 1,

View file

@ -19,20 +19,20 @@ namespace ChocolArm64.Decoders
_opActivators = new ConcurrentDictionary<Type, OpActivator>();
}
public static Block DecodeBasicBlock(CpuThreadState state, MemoryManager memory, long start)
public static Block DecodeBasicBlock(MemoryManager memory, long start, ExecutionMode mode)
{
Block block = new Block(start);
FillBlock(state, memory, block);
FillBlock(memory, mode, block);
return block;
}
public static Block DecodeSubroutine(
TranslatorCache cache,
CpuThreadState state,
MemoryManager memory,
long start)
long start,
ExecutionMode mode)
{
Dictionary<long, Block> visited = new Dictionary<long, Block>();
Dictionary<long, Block> visitedEnd = new Dictionary<long, Block>();
@ -59,7 +59,7 @@ namespace ChocolArm64.Decoders
{
Block current = blocks.Dequeue();
FillBlock(state, memory, current);
FillBlock(memory, mode, current);
//Set child blocks. "Branch" is the block the branch instruction
//points to (when taken), "Next" is the block at the next address,
@ -71,7 +71,7 @@ namespace ChocolArm64.Decoders
OpCode64 lastOp = current.GetLastOp();
if (lastOp is OpCodeBImm64 op)
if (lastOp is IOpCodeBImm op)
{
if (op.Emitter == InstEmit.Bl)
{
@ -83,8 +83,7 @@ namespace ChocolArm64.Decoders
}
}
if (!((lastOp is OpCodeBImmAl64) ||
(lastOp is OpCodeBReg64)) || hasCachedSub)
if (!IsUnconditionalBranch(lastOp) || hasCachedSub)
{
current.Next = Enqueue(current.EndPosition);
}
@ -121,7 +120,7 @@ namespace ChocolArm64.Decoders
return entry;
}
private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block)
private static void FillBlock(MemoryManager memory, ExecutionMode mode, Block block)
{
long position = block.Position;
@ -129,13 +128,11 @@ namespace ChocolArm64.Decoders
do
{
//TODO: This needs to be changed to support both AArch32 and AArch64,
//once JIT support is introduced on AArch32 aswell.
opCode = DecodeOpCode(state, memory, position);
opCode = DecodeOpCode(memory, position, mode);
block.OpCodes.Add(opCode);
position += 4;
position += opCode.OpCodeSizeInBytes;
}
while (!(IsBranch(opCode) || IsException(opCode)));
@ -145,7 +142,85 @@ namespace ChocolArm64.Decoders
private static bool IsBranch(OpCode64 opCode)
{
return opCode is OpCodeBImm64 ||
opCode is OpCodeBReg64;
opCode is OpCodeBReg64 || IsAarch32Branch(opCode);
}
private static bool IsUnconditionalBranch(OpCode64 opCode)
{
return opCode is OpCodeBImmAl64 ||
opCode is OpCodeBReg64 || IsAarch32UnconditionalBranch(opCode);
}
private static bool IsAarch32UnconditionalBranch(OpCode64 opCode)
{
if (!(opCode is OpCode32 op))
{
return false;
}
//Note: On ARM32, most instructions have conditional execution,
//so there's no "Always" (unconditional) branch like on ARM64.
//We need to check if the condition is "Always" instead.
return IsAarch32Branch(op) && op.Cond >= Condition.Al;
}
private static bool IsAarch32Branch(OpCode64 opCode)
{
//Note: On ARM32, most ALU operations can write to R15 (PC),
//so we must consider such operations as a branch in potential aswell.
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
{
return true;
}
//Same thing for memory operations. We have the cases where PC is a target
//register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
//a write back to PC (wback == true && Rn == 15), however the later may
//be "undefined" depending on the CPU, so compilers should not produce that.
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
{
int rt, rn;
bool wBack, isLoad;
if (opCode is IOpCode32Mem opMem)
{
rt = opMem.Rt;
rn = opMem.Rn;
wBack = opMem.WBack;
isLoad = opMem.IsLoad;
//For the dual load, we also need to take into account the
//case were Rt2 == 15 (PC).
if (rt == 14 && opMem.Emitter == InstEmit32.Ldrd)
{
rt = RegisterAlias.Aarch32Pc;
}
}
else if (opCode is IOpCode32MemMult opMemMult)
{
const int pcMask = 1 << RegisterAlias.Aarch32Pc;
rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0;
rn = opMemMult.Rn;
wBack = opMemMult.PostOffset != 0;
isLoad = opMemMult.IsLoad;
}
else
{
throw new NotImplementedException($"The type \"{opCode.GetType().Name}\" is not implemented on the decoder.");
}
if ((rt == RegisterAlias.Aarch32Pc && isLoad) ||
(rn == RegisterAlias.Aarch32Pc && wBack))
{
return true;
}
}
//Explicit branch instructions.
return opCode is IOpCode32BImm ||
opCode is IOpCode32BReg;
}
private static bool IsException(OpCode64 opCode)
@ -155,20 +230,26 @@ namespace ChocolArm64.Decoders
opCode.Emitter == InstEmit.Und;
}
public static OpCode64 DecodeOpCode(CpuThreadState state, MemoryManager memory, long position)
public static OpCode64 DecodeOpCode(MemoryManager memory, long position, ExecutionMode mode)
{
int opCode = memory.ReadInt32(position);
Inst inst;
if (state.ExecutionMode == ExecutionMode.AArch64)
if (mode == ExecutionMode.Aarch64)
{
inst = OpCodeTable.GetInstA64(opCode);
}
else
{
//TODO: Thumb support.
inst = OpCodeTable.GetInstA32(opCode);
if (mode == ExecutionMode.Aarch32Arm)
{
inst = OpCodeTable.GetInstA32(opCode);
}
else /* if (mode == ExecutionMode.Aarch32Thumb) */
{
inst = OpCodeTable.GetInstT32(opCode);
}
}
OpCode64 decodedOpCode = new OpCode64(Inst.Undefined, position, opCode);

View file

@ -89,6 +89,11 @@ namespace ChocolArm64.Decoders
return value;
}
public static long DecodeImm24_2(int opCode)
{
return ((long)opCode << 40) >> 38;
}
public static long DecodeImm26_2(int opCode)
{
return ((long)opCode << 38) >> 36;

View file

@ -0,0 +1,9 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32 : IOpCode64
{
Condition Cond { get; }
uint GetPc();
}
}

View file

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32Alu : IOpCode32
{
int Rd { get; }
int Rn { get; }
bool SetFlags { get; }
}
}

View file

@ -0,0 +1,4 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32BImm : IOpCode32, IOpCodeBImm { }
}

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32BReg : IOpCode32
{
int Rm { get; }
}
}

View file

@ -0,0 +1,12 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32Mem : IOpCode32
{
int Rt { get; }
int Rn { get; }
bool WBack { get; }
bool IsLoad { get; }
}
}

View file

@ -0,0 +1,13 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32MemMult : IOpCode32
{
int Rn { get; }
int RegisterMask { get; }
int PostOffset { get; }
bool IsLoad { get; }
}
}

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoders
{
interface IOpCodeBImm : IOpCode64
{
long Imm { get; }
}
}

View file

@ -2,6 +2,6 @@ namespace ChocolArm64.Decoders
{
interface IOpCodeCond64 : IOpCode64
{
Cond Cond { get; }
Condition Cond { get; }
}
}

View file

@ -0,0 +1,24 @@
using ChocolArm64.Instructions;
using ChocolArm64.State;
namespace ChocolArm64.Decoders
{
class OpCode32 : OpCode64
{
public Condition Cond { get; protected set; }
public OpCode32(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
RegisterSize = RegisterSize.Int32;
Cond = (Condition)((uint)opCode >> 28);
}
public uint GetPc()
{
//Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
//the PC actually points 2 instructions ahead.
return (uint)Position + (uint)OpCodeSizeInBytes * 2;
}
}
}

View file

@ -0,0 +1,20 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32Alu : OpCode32, IOpCode32Alu
{
public int Rd { get; private set; }
public int Rn { get; private set; }
public bool SetFlags { get; private set; }
public OpCode32Alu(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rd = (opCode >> 12) & 0xf;
Rn = (opCode >> 16) & 0xf;
SetFlags = ((opCode >> 20) & 1) != 0;
}
}
}

View file

@ -0,0 +1,21 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32AluImm : OpCode32Alu
{
public int Imm { get; private set; }
public bool IsRotated { get; private set; }
public OpCode32AluImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
int value = (opCode >> 0) & 0xff;
int shift = (opCode >> 8) & 0xf;
Imm = BitUtils.RotateRight(value, shift * 2, 32);
IsRotated = shift != 0;
}
}
}

View file

@ -0,0 +1,20 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32AluRsImm : OpCode32Alu
{
public int Rm { get; private set; }
public int Imm { get; private set; }
public ShiftType ShiftType { get; private set; }
public OpCode32AluRsImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rm = (opCode >> 0) & 0xf;
Imm = (opCode >> 7) & 0x1f;
ShiftType = (ShiftType)((opCode >> 5) & 3);
}
}
}

View file

@ -0,0 +1,29 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32BImm : OpCode32, IOpCode32BImm
{
public long Imm { get; private set; }
public OpCode32BImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
uint pc = GetPc();
//When the codition is never, the instruction is BLX to Thumb mode.
if (Cond != Condition.Nv)
{
pc &= ~3u;
}
Imm = pc + DecoderHelper.DecodeImm24_2(opCode);
if (Cond == Condition.Nv)
{
long H = (opCode >> 23) & 2;
Imm |= H;
}
}
}
}

View file

@ -0,0 +1,14 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32BReg : OpCode32, IOpCode32BReg
{
public int Rm { get; private set; }
public OpCode32BReg(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rm = opCode & 0xf;
}
}
}

View file

@ -0,0 +1,37 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32Mem : OpCode32, IOpCode32Mem
{
public int Rt { get; private set; }
public int Rn { get; private set; }
public int Imm { get; protected set; }
public bool Index { get; private set; }
public bool Add { get; private set; }
public bool WBack { get; private set; }
public bool Unprivileged { get; private set; }
public bool IsLoad { get; private set; }
public OpCode32Mem(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rt = (opCode >> 12) & 0xf;
Rn = (opCode >> 16) & 0xf;
bool isLoad = (opCode & (1 << 20)) != 0;
bool w = (opCode & (1 << 21)) != 0;
bool u = (opCode & (1 << 23)) != 0;
bool p = (opCode & (1 << 24)) != 0;
Index = p;
Add = u;
WBack = !p || w;
Unprivileged = !p && w;
IsLoad = isLoad || inst.Emitter == InstEmit32.Ldrd;
}
}
}

View file

@ -0,0 +1,12 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32MemImm : OpCode32Mem
{
public OpCode32MemImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Imm = opCode & 0xfff;
}
}
}

View file

@ -0,0 +1,15 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32MemImm8 : OpCode32Mem
{
public OpCode32MemImm8(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
int imm4L = (opCode >> 0) & 0xf;
int imm4H = (opCode >> 8) & 0xf;
Imm = imm4L | (imm4H << 4);
}
}
}

View file

@ -0,0 +1,57 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCode32MemMult : OpCode32, IOpCode32MemMult
{
public int Rn { get; private set; }
public int RegisterMask { get; private set; }
public int Offset { get; private set; }
public int PostOffset { get; private set; }
public bool IsLoad { get; private set; }
public OpCode32MemMult(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rn = (opCode >> 16) & 0xf;
bool isLoad = (opCode & (1 << 20)) != 0;
bool w = (opCode & (1 << 21)) != 0;
bool u = (opCode & (1 << 23)) != 0;
bool p = (opCode & (1 << 24)) != 0;
RegisterMask = opCode & 0xffff;
int regsSize = 0;
for (int index = 0; index < 16; index++)
{
regsSize += (RegisterMask >> index) & 1;
}
regsSize *= 4;
if (!u)
{
Offset -= regsSize;
}
if (u == p)
{
Offset += 4;
}
if (w)
{
PostOffset = u ? regsSize : -regsSize;
}
else
{
PostOffset = 0;
}
IsLoad = isLoad;
}
}
}

View file

@ -9,9 +9,10 @@ namespace ChocolArm64.Decoders
public long Position { get; private set; }
public int RawOpCode { get; private set; }
public InstEmitter Emitter { get; protected set; }
public InstInterpreter Interpreter { get; protected set; }
public RegisterSize RegisterSize { get; protected set; }
public int OpCodeSizeInBytes { get; protected set; } = 4;
public InstEmitter Emitter { get; protected set; }
public RegisterSize RegisterSize { get; protected set; }
public OpCode64(Inst inst, long position, int opCode)
{
@ -20,8 +21,7 @@ namespace ChocolArm64.Decoders
RegisterSize = RegisterSize.Int64;
Emitter = inst.Emitter;
Interpreter = inst.Interpreter;
Emitter = inst.Emitter;
}
public int GetBitsCount()

View file

@ -2,7 +2,7 @@ using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeBImm64 : OpCode64
class OpCodeBImm64 : OpCode64, IOpCodeBImm
{
public long Imm { get; protected set; }

View file

@ -4,7 +4,7 @@ namespace ChocolArm64.Decoders
{
class OpCodeBImmCond64 : OpCodeBImm64, IOpCodeCond64
{
public Cond Cond { get; private set; }
public Condition Cond { get; private set; }
public OpCodeBImmCond64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
@ -17,7 +17,7 @@ namespace ChocolArm64.Decoders
return;
}
Cond = (Cond)(opCode & 0xf);
Cond = (Condition)(opCode & 0xf);
Imm = position + DecoderHelper.DecodeImmS19_2(opCode);
}

View file

@ -8,7 +8,7 @@ namespace ChocolArm64.Decoders
public int Nzcv { get; private set; }
protected int RmImm;
public Cond Cond { get; private set; }
public Condition Cond { get; private set; }
public OpCodeCcmp64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
@ -21,11 +21,11 @@ namespace ChocolArm64.Decoders
return;
}
Nzcv = (opCode >> 0) & 0xf;
Cond = (Cond)((opCode >> 12) & 0xf);
RmImm = (opCode >> 16) & 0x1f;
Nzcv = (opCode >> 0) & 0xf;
Cond = (Condition)((opCode >> 12) & 0xf);
RmImm = (opCode >> 16) & 0x1f;
Rd = CpuThreadState.ZrIndex;
Rd = RegisterAlias.Zr;
}
}
}

View file

@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders
{
public int Rm { get; private set; }
public Cond Cond { get; private set; }
public Condition Cond { get; private set; }
public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rm = (opCode >> 16) & 0x1f;
Cond = (Cond)((opCode >> 12) & 0xf);
Rm = (opCode >> 16) & 0x1f;
Cond = (Condition)((opCode >> 12) & 0xf);
}
}
}

View file

@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders
{
public int Nzcv { get; private set; }
public Cond Cond { get; private set; }
public Condition Cond { get; private set; }
public OpCodeSimdFcond64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Nzcv = (opCode >> 0) & 0xf;
Cond = (Cond)((opCode >> 12) & 0xf);
Nzcv = (opCode >> 0) & 0xf;
Cond = (Condition)((opCode >> 12) & 0xf);
}
}
}

View file

@ -0,0 +1,14 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeT16 : OpCode32
{
public OpCodeT16(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Cond = Condition.Al;
OpCodeSizeInBytes = 2;
}
}
}

View file

@ -0,0 +1,22 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeT16AluImm8 : OpCodeT16, IOpCode32Alu
{
private int _rdn;
public int Rd => _rdn;
public int Rn => _rdn;
public bool SetFlags => false;
public int Imm { get; private set; }
public OpCodeT16AluImm8(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Imm = (opCode >> 0) & 0xff;
_rdn = (opCode >> 8) & 0x7;
}
}
}

View file

@ -0,0 +1,14 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeT16BReg : OpCodeT16, IOpCode32BReg
{
public int Rm { get; private set; }
public OpCodeT16BReg(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rm = (opCode >> 3) & 0xf;
}
}
}

View file

@ -1,15 +0,0 @@
using ChocolArm64.Decoders;
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders32
{
class A32OpCode : OpCode64
{
public Cond Cond { get; private set; }
public A32OpCode(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Cond = (Cond)((uint)opCode >> 28);
}
}
}

View file

@ -1,16 +0,0 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders32
{
class A32OpCodeBImmAl : A32OpCode
{
public int Imm;
public int H;
public A32OpCodeBImmAl(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Imm = (opCode << 8) >> 6;
H = (opCode >> 23) & 2;
}
}
}

View file

@ -9,7 +9,7 @@ namespace ChocolArm64.Instructions
static class CryptoHelper
{
#region "LookUp Tables"
private static byte[] _sBox =
private static readonly byte[] _sBox = new byte[]
{
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
@ -29,7 +29,7 @@ namespace ChocolArm64.Instructions
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
private static byte[] _invSBox =
private static readonly byte[] _invSBox = new byte[]
{
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
@ -49,7 +49,7 @@ namespace ChocolArm64.Instructions
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};
private static byte[] _gfMul02 =
private static readonly byte[] _gfMul02 = new byte[]
{
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
@ -69,7 +69,7 @@ namespace ChocolArm64.Instructions
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
};
private static byte[] _gfMul03 =
private static readonly byte[] _gfMul03 = new byte[]
{
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
@ -89,7 +89,7 @@ namespace ChocolArm64.Instructions
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
};
private static byte[] _gfMul09 =
private static readonly byte[] _gfMul09 = new byte[]
{
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
@ -109,7 +109,7 @@ namespace ChocolArm64.Instructions
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
};
private static byte[] _gfMul0B =
private static readonly byte[] _gfMul0B = new byte[]
{
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
@ -129,7 +129,7 @@ namespace ChocolArm64.Instructions
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
};
private static byte[] _gfMul0D =
private static readonly byte[] _gfMul0D = new byte[]
{
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
@ -149,7 +149,7 @@ namespace ChocolArm64.Instructions
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
};
private static byte[] _gfMul0E =
private static readonly byte[] _gfMul0E = new byte[]
{
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
@ -169,9 +169,15 @@ namespace ChocolArm64.Instructions
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
};
private static byte[] _srPerm = { 0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3 };
private static readonly byte[] _srPerm = new byte[]
{
0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3
};
private static byte[] _isrPerm = { 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11 };
private static readonly byte[] _isrPerm = new byte[]
{
0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11
};
#endregion
public static Vector128<float> AesInvMixColumns(Vector128<float> op)
@ -179,7 +185,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16];
byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op);
FromVectorToByteArray(op, inState);
for (int columns = 0; columns <= 3; columns++)
{
@ -206,7 +212,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16];
byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op);
FromVectorToByteArray(op, inState);
for (int idx = 0; idx <= 15; idx++)
{
@ -223,7 +229,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16];
byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op);
FromVectorToByteArray(op, inState);
for (int idx = 0; idx <= 15; idx++)
{
@ -240,7 +246,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16];
byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op);
FromVectorToByteArray(op, inState);
for (int columns = 0; columns <= 3; columns++)
{
@ -267,7 +273,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16];
byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op);
FromVectorToByteArray(op, inState);
for (int idx = 0; idx <= 15; idx++)
{
@ -284,7 +290,7 @@ namespace ChocolArm64.Instructions
byte[] inState = new byte[16];
byte[] outState = new byte[16];
FromVectorToByteArray(inState, ref op);
FromVectorToByteArray(op, inState);
for (int idx = 0; idx <= 15; idx++)
{
@ -296,33 +302,30 @@ namespace ChocolArm64.Instructions
return op;
}
private static void FromVectorToByteArray(byte[] state, ref Vector128<float> op)
{
ulong uLongLow = VectorHelper.VectorExtractIntZx((op), (byte)0, 3);
ulong uLongHigh = VectorHelper.VectorExtractIntZx((op), (byte)1, 3);
for (int idx = 0; idx <= 7; idx++)
{
state[idx + 0] = (byte)(uLongLow & 0xFFUL);
state[idx + 8] = (byte)(uLongHigh & 0xFFUL);
uLongLow >>= 8;
uLongHigh >>= 8;
}
}
private static void FromByteArrayToVector(byte[] state, ref Vector128<float> op)
private unsafe static void FromVectorToByteArray(Vector128<float> op, byte[] state)
{
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
op = Sse.StaticCast<byte, float>(Sse2.SetVector128(
state[15], state[14], state[13], state[12],
state[11], state[10], state[9], state[8],
state[7], state[6], state[5], state[4],
state[3], state[2], state[1], state[0]));
fixed (byte* ptr = &state[0])
{
Sse2.Store(ptr, Sse.StaticCast<float, byte>(op));
}
}
private unsafe static void FromByteArrayToVector(byte[] state, ref Vector128<float> op)
{
if (!Sse2.IsSupported)
{
throw new PlatformNotSupportedException();
}
fixed (byte* ptr = &state[0])
{
op = Sse.StaticCast<byte, float>(Sse2.LoadVector128(ptr));
}
}
}
}

View file

@ -4,17 +4,15 @@ namespace ChocolArm64.Instructions
{
struct Inst
{
public InstInterpreter Interpreter { get; private set; }
public InstEmitter Emitter { get; private set; }
public Type Type { get; private set; }
public InstEmitter Emitter { get; }
public Type Type { get; }
public static Inst Undefined => new Inst(null, InstEmit.Und, null);
public static Inst Undefined => new Inst(InstEmit.Und, null);
public Inst(InstInterpreter interpreter, InstEmitter emitter, Type type)
public Inst(InstEmitter emitter, Type type)
{
Interpreter = interpreter;
Emitter = emitter;
Type = type;
Emitter = emitter;
Type = type;
}
}
}

View file

@ -0,0 +1,145 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
namespace ChocolArm64.Instructions
{
static class InstEmit32Helper
{
public static bool IsThumb(OpCode64 op)
{
return op is OpCodeT16;
}
public static void EmitLoadFromRegister(ILEmitterCtx context, int register)
{
if (register == RegisterAlias.Aarch32Pc)
{
OpCode32 op = (OpCode32)context.CurrOp;
context.EmitLdc_I4((int)op.GetPc());
}
else
{
context.EmitLdint(InstEmit32Helper.GetRegisterAlias(context.Mode, register));
}
}
public static void EmitStoreToRegister(ILEmitterCtx context, int register)
{
if (register == RegisterAlias.Aarch32Pc)
{
context.EmitStoreState();
EmitBxWritePc(context);
}
else
{
context.EmitStint(GetRegisterAlias(context.Mode, register));
}
}
public static void EmitBxWritePc(ILEmitterCtx context)
{
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Dup);
context.EmitStflg((int)PState.TBit);
ILLabel lblArmMode = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brtrue_S, lblArmMode);
context.EmitLdc_I4(~1);
context.Emit(OpCodes.Br_S, lblEnd);
context.MarkLabel(lblArmMode);
context.EmitLdc_I4(~3);
context.MarkLabel(lblEnd);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Ret);
}
public static int GetRegisterAlias(Aarch32Mode mode, int register)
{
//Only registers >= 8 are banked, with registers in the range [8, 12] being
//banked for the FIQ mode, and registers 13 and 14 being banked for all modes.
if ((uint)register < 8)
{
return register;
}
return GetBankedRegisterAlias(mode, register);
}
public static int GetBankedRegisterAlias(Aarch32Mode mode, int register)
{
switch (register)
{
case 8: return mode == Aarch32Mode.Fiq
? RegisterAlias.R8Fiq
: RegisterAlias.R8Usr;
case 9: return mode == Aarch32Mode.Fiq
? RegisterAlias.R9Fiq
: RegisterAlias.R9Usr;
case 10: return mode == Aarch32Mode.Fiq
? RegisterAlias.R10Fiq
: RegisterAlias.R10Usr;
case 11: return mode == Aarch32Mode.Fiq
? RegisterAlias.R11Fiq
: RegisterAlias.R11Usr;
case 12: return mode == Aarch32Mode.Fiq
? RegisterAlias.R12Fiq
: RegisterAlias.R12Usr;
case 13:
switch (mode)
{
case Aarch32Mode.User:
case Aarch32Mode.System: return RegisterAlias.SpUsr;
case Aarch32Mode.Fiq: return RegisterAlias.SpFiq;
case Aarch32Mode.Irq: return RegisterAlias.SpIrq;
case Aarch32Mode.Supervisor: return RegisterAlias.SpSvc;
case Aarch32Mode.Abort: return RegisterAlias.SpAbt;
case Aarch32Mode.Hypervisor: return RegisterAlias.SpHyp;
case Aarch32Mode.Undefined: return RegisterAlias.SpUnd;
default: throw new ArgumentException(nameof(mode));
}
case 14:
switch (mode)
{
case Aarch32Mode.User:
case Aarch32Mode.Hypervisor:
case Aarch32Mode.System: return RegisterAlias.LrUsr;
case Aarch32Mode.Fiq: return RegisterAlias.LrFiq;
case Aarch32Mode.Irq: return RegisterAlias.LrIrq;
case Aarch32Mode.Supervisor: return RegisterAlias.LrSvc;
case Aarch32Mode.Abort: return RegisterAlias.LrAbt;
case Aarch32Mode.Undefined: return RegisterAlias.LrUnd;
default: throw new ArgumentException(nameof(mode));
}
default: throw new ArgumentOutOfRangeException(nameof(register));
}
}
}
}

View file

@ -17,7 +17,7 @@ namespace ChocolArm64.Instructions
private static void EmitAdc(ILEmitterCtx context, bool setFlags)
{
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Add);
@ -44,14 +44,14 @@ namespace ChocolArm64.Instructions
EmitAddsVCheck(context);
}
EmitDataStore(context);
EmitAluStore(context);
}
public static void Add(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Add);
public static void Add(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Add);
public static void Adds(ILEmitterCtx context)
{
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Add);
@ -59,14 +59,14 @@ namespace ChocolArm64.Instructions
EmitAddsCCheck(context);
EmitAddsVCheck(context);
EmitDataStoreS(context);
EmitAluStoreS(context);
}
public static void And(ILEmitterCtx context) => EmitDataOp(context, OpCodes.And);
public static void And(ILEmitterCtx context) => EmitAluOp(context, OpCodes.And);
public static void Ands(ILEmitterCtx context)
{
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.And);
@ -74,17 +74,17 @@ namespace ChocolArm64.Instructions
context.EmitZnFlagCheck();
EmitDataStoreS(context);
EmitAluStoreS(context);
}
public static void Asrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr);
public static void Asrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr);
public static void Bic(ILEmitterCtx context) => EmitBic(context, false);
public static void Bics(ILEmitterCtx context) => EmitBic(context, true);
private static void EmitBic(ILEmitterCtx context, bool setFlags)
{
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Not);
context.Emit(OpCodes.And);
@ -96,7 +96,7 @@ namespace ChocolArm64.Instructions
context.EmitZnFlagCheck();
}
EmitDataStore(context, setFlags);
EmitAluStore(context, setFlags);
}
public static void Cls(ILEmitterCtx context)
@ -136,15 +136,15 @@ namespace ChocolArm64.Instructions
public static void Eon(ILEmitterCtx context)
{
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Not);
context.Emit(OpCodes.Xor);
EmitDataStore(context);
EmitAluStore(context);
}
public static void Eor(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Xor);
public static void Eor(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Xor);
public static void Extr(ILEmitterCtx context)
{
@ -166,18 +166,18 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.Or);
}
EmitDataStore(context);
EmitAluStore(context);
}
public static void Lslv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shl);
public static void Lsrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr_Un);
public static void Lslv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shl);
public static void Lsrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr_Un);
public static void Sbc(ILEmitterCtx context) => EmitSbc(context, false);
public static void Sbcs(ILEmitterCtx context) => EmitSbc(context, true);
private static void EmitSbc(ILEmitterCtx context, bool setFlags)
{
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Sub);
@ -208,16 +208,16 @@ namespace ChocolArm64.Instructions
EmitSubsVCheck(context);
}
EmitDataStore(context);
EmitAluStore(context);
}
public static void Sub(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Sub);
public static void Sub(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Sub);
public static void Subs(ILEmitterCtx context)
{
context.TryOptMarkCondWithoutCmp();
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Sub);
@ -225,20 +225,20 @@ namespace ChocolArm64.Instructions
EmitSubsCCheck(context);
EmitSubsVCheck(context);
EmitDataStoreS(context);
EmitAluStoreS(context);
}
public static void Orn(ILEmitterCtx context)
{
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Not);
context.Emit(OpCodes.Or);
EmitDataStore(context);
EmitAluStore(context);
}
public static void Orr(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Or);
public static void Orr(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Or);
public static void Rbit(ILEmitterCtx context) => EmitFallback32_64(context,
nameof(SoftFallback.ReverseBits32),
@ -283,22 +283,22 @@ namespace ChocolArm64.Instructions
public static void Rorv(ILEmitterCtx context)
{
EmitDataLoadRn(context);
EmitDataLoadShift(context);
EmitAluLoadRn(context);
EmitAluLoadShift(context);
context.Emit(OpCodes.Shr_Un);
EmitDataLoadRn(context);
EmitAluLoadRn(context);
context.EmitLdc_I4(context.CurrOp.GetBitsCount());
EmitDataLoadShift(context);
EmitAluLoadShift(context);
context.Emit(OpCodes.Sub);
context.Emit(OpCodes.Shl);
context.Emit(OpCodes.Or);
EmitDataStore(context);
EmitAluStore(context);
}
public static void Sdiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div);
@ -309,7 +309,7 @@ namespace ChocolArm64.Instructions
//If Rm == 0, Rd = 0 (division by zero).
context.EmitLdc_I(0);
EmitDataLoadRm(context);
EmitAluLoadRm(context);
context.EmitLdc_I(0);
@ -325,13 +325,13 @@ namespace ChocolArm64.Instructions
context.EmitLdc_I(intMin);
EmitDataLoadRn(context);
EmitAluLoadRn(context);
context.EmitLdc_I(intMin);
context.Emit(OpCodes.Ceq);
EmitDataLoadRm(context);
EmitAluLoadRm(context);
context.EmitLdc_I(-1);
@ -341,38 +341,38 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.Pop);
}
EmitDataLoadRn(context);
EmitDataLoadRm(context);
EmitAluLoadRn(context);
EmitAluLoadRm(context);
context.Emit(ilOp);
context.MarkLabel(badDiv);
EmitDataStore(context);
EmitAluStore(context);
}
private static void EmitDataOp(ILEmitterCtx context, OpCode ilOp)
private static void EmitAluOp(ILEmitterCtx context, OpCode ilOp)
{
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(ilOp);
EmitDataStore(context);
EmitAluStore(context);
}
private static void EmitDataOpShift(ILEmitterCtx context, OpCode ilOp)
private static void EmitAluOpShift(ILEmitterCtx context, OpCode ilOp)
{
EmitDataLoadRn(context);
EmitDataLoadShift(context);
EmitAluLoadRn(context);
EmitAluLoadShift(context);
context.Emit(ilOp);
EmitDataStore(context);
EmitAluStore(context);
}
private static void EmitDataLoadShift(ILEmitterCtx context)
private static void EmitAluLoadShift(ILEmitterCtx context)
{
EmitDataLoadRm(context);
EmitAluLoadRm(context);
context.EmitLdc_I(context.CurrOp.GetBitsCount() - 1);
@ -398,5 +398,22 @@ namespace ChocolArm64.Instructions
context.EmitStflg((int)PState.CBit);
}
public static void EmitAluStore(ILEmitterCtx context) => EmitAluStore(context, false);
public static void EmitAluStoreS(ILEmitterCtx context) => EmitAluStore(context, true);
public static void EmitAluStore(ILEmitterCtx context, bool setFlags)
{
IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
if (setFlags || op is IOpCodeAluRs64)
{
context.EmitStintzr(op.Rd);
}
else
{
context.EmitStint(op.Rd);
}
}
}
}

View file

@ -0,0 +1,141 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System.Reflection.Emit;
using static ChocolArm64.Instructions.InstEmit32Helper;
using static ChocolArm64.Instructions.InstEmitAluHelper;
namespace ChocolArm64.Instructions
{
static partial class InstEmit32
{
public static void Add(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false);
context.Emit(OpCodes.Add);
if (op.SetFlags)
{
context.EmitZnFlagCheck();
EmitAddsCCheck(context);
EmitAddsVCheck(context);
}
EmitAluStore(context);
}
public static void Cmp(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false);
context.Emit(OpCodes.Sub);
context.EmitZnFlagCheck();
EmitSubsCCheck(context);
EmitSubsVCheck(context);
context.Emit(OpCodes.Pop);
}
public static void Mov(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOper2(context);
if (op.SetFlags)
{
context.EmitZnFlagCheck();
}
EmitAluStore(context);
}
public static void Sub(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
EmitAluLoadOpers(context, setCarry: false);
context.Emit(OpCodes.Sub);
if (op.SetFlags)
{
context.EmitZnFlagCheck();
EmitSubsCCheck(context);
EmitSubsVCheck(context);
}
EmitAluStore(context);
}
private static void EmitAluStore(ILEmitterCtx context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
if (op.Rd == RegisterAlias.Aarch32Pc)
{
if (op.SetFlags)
{
//TODO: Load SPSR etc.
context.EmitLdflg((int)PState.TBit);
ILLabel lblThumb = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brtrue_S, lblThumb);
context.EmitLdc_I4(~3);
context.Emit(OpCodes.Br_S, lblEnd);
context.MarkLabel(lblThumb);
context.EmitLdc_I4(~1);
context.MarkLabel(lblEnd);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Ret);
}
else
{
EmitAluWritePc(context);
}
}
else
{
context.EmitStint(GetRegisterAlias(context.Mode, op.Rd));
}
}
private static void EmitAluWritePc(ILEmitterCtx context)
{
context.EmitStoreState();
if (IsThumb(context.CurrOp))
{
context.EmitLdc_I4(~1);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Ret);
}
else
{
EmitBxWritePc(context);
}
}
}
}

View file

@ -1,6 +1,7 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
namespace ChocolArm64.Instructions
@ -14,7 +15,7 @@ namespace ChocolArm64.Instructions
context.EmitLdtmp();
context.EmitLdtmp();
EmitDataLoadRn(context);
EmitAluLoadRn(context);
context.Emit(OpCodes.Ceq);
@ -24,7 +25,7 @@ namespace ChocolArm64.Instructions
context.EmitLdtmp();
EmitDataLoadRn(context);
EmitAluLoadRn(context);
context.Emit(OpCodes.Clt_Un);
context.Emit(OpCodes.Or);
@ -37,7 +38,7 @@ namespace ChocolArm64.Instructions
//C = Rd < Rn
context.Emit(OpCodes.Dup);
EmitDataLoadRn(context);
EmitAluLoadRn(context);
context.Emit(OpCodes.Clt_Un);
@ -49,11 +50,11 @@ namespace ChocolArm64.Instructions
//V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
context.Emit(OpCodes.Dup);
EmitDataLoadRn(context);
EmitAluLoadRn(context);
context.Emit(OpCodes.Xor);
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Xor);
context.Emit(OpCodes.Not);
@ -69,7 +70,7 @@ namespace ChocolArm64.Instructions
public static void EmitSbcsCCheck(ILEmitterCtx context)
{
//C = (Rn == Rm && CIn) || Rn > Rm
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Ceq);
@ -77,7 +78,7 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.And);
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Cgt_Un);
context.Emit(OpCodes.Or);
@ -88,7 +89,7 @@ namespace ChocolArm64.Instructions
public static void EmitSubsCCheck(ILEmitterCtx context)
{
//C = Rn == Rm || Rn > Rm = !(Rn < Rm)
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Clt_Un);
@ -104,11 +105,11 @@ namespace ChocolArm64.Instructions
//V = (Rd ^ Rn) & (Rn ^ Rm) < 0
context.Emit(OpCodes.Dup);
EmitDataLoadRn(context);
EmitAluLoadRn(context);
context.Emit(OpCodes.Xor);
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
context.Emit(OpCodes.Xor);
context.Emit(OpCodes.And);
@ -120,35 +121,76 @@ namespace ChocolArm64.Instructions
context.EmitStflg((int)PState.VBit);
}
public static void EmitDataLoadRm(ILEmitterCtx context)
public static void EmitAluLoadRm(ILEmitterCtx context)
{
context.EmitLdintzr(((IOpCodeAluRs64)context.CurrOp).Rm);
}
public static void EmitDataLoadOpers(ILEmitterCtx context)
{
EmitDataLoadRn(context);
EmitDataLoadOper2(context);
}
public static void EmitDataLoadRn(ILEmitterCtx context)
{
IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64)
if (context.CurrOp is IOpCodeAluRs64 op)
{
context.EmitLdintzr(op.Rn);
context.EmitLdintzr(op.Rm);
}
else if (context.CurrOp is OpCode32AluRsImm op32)
{
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm);
}
else
{
context.EmitLdint(op.Rn);
throw new InvalidOperationException();
}
}
public static void EmitDataLoadOper2(ILEmitterCtx context)
public static void EmitAluLoadOpers(ILEmitterCtx context, bool setCarry = true)
{
EmitAluLoadRn(context);
EmitAluLoadOper2(context, setCarry);
}
public static void EmitAluLoadRn(ILEmitterCtx context)
{
if (context.CurrOp is IOpCodeAlu64 op)
{
if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64)
{
context.EmitLdintzr(op.Rn);
}
else
{
context.EmitLdint(op.Rn);
}
}
else if (context.CurrOp is IOpCode32Alu op32)
{
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn);
}
else
{
throw new InvalidOperationException();
}
}
public static void EmitAluLoadOper2(ILEmitterCtx context, bool setCarry = true)
{
switch (context.CurrOp)
{
//ARM32.
case OpCode32AluImm op:
context.EmitLdc_I4(op.Imm);
if (op.SetFlags && op.IsRotated)
{
context.EmitLdc_I4((int)((uint)op.Imm >> 31));
context.EmitStflg((int)PState.CBit);
}
break;
case OpCode32AluRsImm op:
EmitLoadRmShiftedByImmediate(context, op, setCarry);
break;
case OpCodeT16AluImm8 op:
context.EmitLdc_I4(op.Imm);
break;
//ARM64.
case IOpCodeAluImm64 op:
context.EmitLdc_I(op.Imm);
break;
@ -170,23 +212,8 @@ namespace ChocolArm64.Instructions
context.EmitCast(op.IntType);
context.EmitLsl(op.Shift);
break;
}
}
public static void EmitDataStore(ILEmitterCtx context) => EmitDataStore(context, false);
public static void EmitDataStoreS(ILEmitterCtx context) => EmitDataStore(context, true);
public static void EmitDataStore(ILEmitterCtx context, bool setFlags)
{
IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
if (setFlags || op is IOpCodeAluRs64)
{
context.EmitStintzr(op.Rd);
}
else
{
context.EmitStint(op.Rd);
default: throw new InvalidOperationException();
}
}
@ -217,5 +244,219 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.NBit);
}
//ARM32 helpers.
private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCode32AluRsImm op, bool setCarry)
{
int shift = op.Imm;
if (shift == 0)
{
switch (op.ShiftType)
{
case ShiftType.Lsr: shift = 32; break;
case ShiftType.Asr: shift = 32; break;
case ShiftType.Ror: shift = 1; break;
}
}
context.EmitLdint(op.Rm);
if (shift != 0)
{
setCarry &= op.SetFlags;
switch (op.ShiftType)
{
case ShiftType.Lsl: EmitLslC(context, setCarry, shift); break;
case ShiftType.Lsr: EmitLsrC(context, setCarry, shift); break;
case ShiftType.Asr: EmitAsrC(context, setCarry, shift); break;
case ShiftType.Ror:
if (op.Imm != 0)
{
EmitRorC(context, setCarry, shift);
}
else
{
EmitRrxC(context, setCarry);
}
break;
}
}
}
private static void EmitLslC(ILEmitterCtx context, bool setCarry, int shift)
{
if ((uint)shift > 32)
{
EmitShiftByMoreThan32(context, setCarry);
}
else if (shift == 32)
{
if (setCarry)
{
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
}
else
{
context.Emit(OpCodes.Pop);
}
context.EmitLdc_I4(0);
}
else
{
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLsr(32 - shift);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
}
context.EmitLsl(shift);
}
}
private static void EmitLsrC(ILEmitterCtx context, bool setCarry, int shift)
{
if ((uint)shift > 32)
{
EmitShiftByMoreThan32(context, setCarry);
}
else if (shift == 32)
{
if (setCarry)
{
context.EmitLsr(31);
context.EmitStflg((int)PState.CBit);
}
else
{
context.Emit(OpCodes.Pop);
}
context.EmitLdc_I4(0);
}
else
{
context.Emit(OpCodes.Dup);
context.EmitLsr(shift - 1);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
context.EmitLsr(shift);
}
}
private static void EmitShiftByMoreThan32(ILEmitterCtx context, bool setCarry)
{
context.Emit(OpCodes.Pop);
context.EmitLdc_I4(0);
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitStflg((int)PState.CBit);
}
}
private static void EmitAsrC(ILEmitterCtx context, bool setCarry, int shift)
{
if ((uint)shift >= 32)
{
context.EmitAsr(31);
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
}
}
else
{
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLsr(shift - 1);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitStflg((int)PState.CBit);
}
context.EmitAsr(shift);
}
}
private static void EmitRorC(ILEmitterCtx context, bool setCarry, int shift)
{
shift &= 0x1f;
context.EmitRor(shift);
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLsr(31);
context.EmitStflg((int)PState.CBit);
}
}
private static void EmitRrxC(ILEmitterCtx context, bool setCarry)
{
//Rotate right by 1 with carry.
if (setCarry)
{
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(1);
context.Emit(OpCodes.And);
context.EmitSttmp();
}
context.EmitLsr(1);
context.EmitLdflg((int)PState.CBit);
context.EmitLsl(31);
context.Emit(OpCodes.Or);
if (setCarry)
{
context.EmitLdtmp();
context.EmitStflg((int)PState.CBit);
}
}
}
}

View file

@ -48,7 +48,7 @@ namespace ChocolArm64.Instructions
context.MarkLabel(lblTrue);
EmitDataLoadOpers(context);
EmitAluLoadOpers(context);
if (cmpOp == CcmpOp.Cmp)
{

View file

@ -36,36 +36,10 @@ namespace ChocolArm64.Instructions
OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp;
context.EmitLdc_I(op.Position + 4);
context.EmitStint(CpuThreadState.LrIndex);
context.EmitStint(RegisterAlias.Lr);
context.EmitStoreState();
if (context.TryOptEmitSubroutineCall())
{
//Note: the return value of the called method will be placed
//at the Stack, the return value is always a Int64 with the
//return address of the function. We check if the address is
//correct, if it isn't we keep returning until we reach the dispatcher.
context.Emit(OpCodes.Dup);
context.EmitLdc_I8(op.Position + 4);
ILLabel lblContinue = new ILLabel();
context.Emit(OpCodes.Beq_S, lblContinue);
context.Emit(OpCodes.Ret);
context.MarkLabel(lblContinue);
context.Emit(OpCodes.Pop);
context.EmitLoadState();
}
else
{
context.EmitLdc_I8(op.Imm);
context.Emit(OpCodes.Ret);
}
InstEmitFlowHelper.EmitCall(context, op.Imm);
}
public static void Blr(ILEmitterCtx context)
@ -74,7 +48,7 @@ namespace ChocolArm64.Instructions
context.EmitLdintzr(op.Rn);
context.EmitLdc_I(op.Position + 4);
context.EmitStint(CpuThreadState.LrIndex);
context.EmitStint(RegisterAlias.Lr);
context.EmitStoreState();
context.Emit(OpCodes.Ret);
@ -106,7 +80,7 @@ namespace ChocolArm64.Instructions
public static void Ret(ILEmitterCtx context)
{
context.EmitStoreState();
context.EmitLdint(CpuThreadState.LrIndex);
context.EmitLdint(RegisterAlias.Lr);
context.Emit(OpCodes.Ret);
}
@ -128,7 +102,7 @@ namespace ChocolArm64.Instructions
EmitBranch(context, ilOp);
}
private static void EmitBranch(ILEmitterCtx context, Cond cond)
private static void EmitBranch(ILEmitterCtx context, Condition cond)
{
OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp;

View file

@ -0,0 +1,82 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System.Reflection.Emit;
using static ChocolArm64.Instructions.InstEmit32Helper;
namespace ChocolArm64.Instructions
{
static partial class InstEmit32
{
public static void B(ILEmitterCtx context)
{
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
if (context.CurrBlock.Branch != null)
{
context.Emit(OpCodes.Br, context.GetLabel(op.Imm));
}
else
{
context.EmitStoreState();
context.EmitLdc_I8(op.Imm);
context.Emit(OpCodes.Ret);
}
}
public static void Bl(ILEmitterCtx context)
{
Blx(context, x: false);
}
public static void Blx(ILEmitterCtx context)
{
Blx(context, x: true);
}
public static void Bx(ILEmitterCtx context)
{
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
context.EmitStoreState();
EmitLoadFromRegister(context, op.Rm);
EmitBxWritePc(context);
}
private static void Blx(ILEmitterCtx context, bool x)
{
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
uint pc = op.GetPc();
bool isThumb = IsThumb(context.CurrOp);
if (!isThumb)
{
context.EmitLdc_I(op.GetPc() - 4);
}
else
{
context.EmitLdc_I(op.GetPc() | 1);
}
context.EmitStint(GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr));
context.EmitStoreState();
//If x is true, then this is a branch with link and exchange.
//In this case we need to swap the mode between Arm <-> Thumb.
if (x)
{
context.EmitLdc_I4(isThumb ? 0 : 1);
context.EmitStflg((int)PState.TBit);
}
InstEmitFlowHelper.EmitCall(context, op.Imm);
}
}
}

View file

@ -0,0 +1,39 @@
using ChocolArm64.Translation;
using System.Reflection.Emit;
namespace ChocolArm64.Instructions
{
static class InstEmitFlowHelper
{
public static void EmitCall(ILEmitterCtx context, long imm)
{
if (context.TryOptEmitSubroutineCall())
{
//Note: the return value of the called method will be placed
//at the Stack, the return value is always a Int64 with the
//return address of the function. We check if the address is
//correct, if it isn't we keep returning until we reach the dispatcher.
context.Emit(OpCodes.Dup);
context.EmitLdc_I8(context.CurrOp.Position + 4);
ILLabel lblContinue = new ILLabel();
context.Emit(OpCodes.Beq_S, lblContinue);
context.Emit(OpCodes.Ret);
context.MarkLabel(lblContinue);
context.Emit(OpCodes.Pop);
context.EmitLoadState();
}
else
{
context.EmitLdc_I8(imm);
context.Emit(OpCodes.Ret);
}
}
}
}

View file

@ -0,0 +1,325 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
using static ChocolArm64.Instructions.InstEmit32Helper;
using static ChocolArm64.Instructions.InstEmitMemoryHelper;
namespace ChocolArm64.Instructions
{
static partial class InstEmit32
{
private const int ByteSizeLog2 = 0;
private const int HWordSizeLog2 = 1;
private const int WordSizeLog2 = 2;
private const int DWordSizeLog2 = 3;
[Flags]
enum AccessType
{
Store = 0,
Signed = 1,
Load = 2,
LoadZx = Load,
LoadSx = Load | Signed,
}
public static void Ldm(ILEmitterCtx context)
{
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
EmitLoadFromRegister(context, op.Rn);
bool writesToPc = (op.RegisterMask & (1 << RegisterAlias.Aarch32Pc)) != 0;
bool writeBack = op.PostOffset != 0 && (op.Rn != RegisterAlias.Aarch32Pc || !writesToPc);
if (writeBack)
{
context.Emit(OpCodes.Dup);
}
context.EmitLdc_I4(op.Offset);
context.Emit(OpCodes.Add);
context.EmitSttmp();
if (writeBack)
{
context.EmitLdc_I4(op.PostOffset);
context.Emit(OpCodes.Add);
EmitStoreToRegister(context, op.Rn);
}
int mask = op.RegisterMask;
int offset = 0;
for (int register = 0; mask != 0; mask >>= 1, register++)
{
if ((mask & 1) != 0)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitLdc_I4(offset);
context.Emit(OpCodes.Add);
EmitReadZxCall(context, WordSizeLog2);
EmitStoreToRegister(context, register);
offset += 4;
}
}
}
public static void Ldr(ILEmitterCtx context)
{
EmitLoadOrStore(context, WordSizeLog2, AccessType.LoadZx);
}
public static void Ldrb(ILEmitterCtx context)
{
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx);
}
public static void Ldrd(ILEmitterCtx context)
{
EmitLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx);
}
public static void Ldrh(ILEmitterCtx context)
{
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx);
}
public static void Ldrsb(ILEmitterCtx context)
{
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadSx);
}
public static void Ldrsh(ILEmitterCtx context)
{
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadSx);
}
public static void Stm(ILEmitterCtx context)
{
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
EmitLoadFromRegister(context, op.Rn);
context.EmitLdc_I4(op.Offset);
context.Emit(OpCodes.Add);
context.EmitSttmp();
int mask = op.RegisterMask;
int offset = 0;
for (int register = 0; mask != 0; mask >>= 1, register++)
{
if ((mask & 1) != 0)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitLdc_I4(offset);
context.Emit(OpCodes.Add);
EmitLoadFromRegister(context, register);
EmitWriteCall(context, WordSizeLog2);
//Note: If Rn is also specified on the register list,
//and Rn is the first register on this list, then the
//value that is written to memory is the unmodified value,
//before the write back. If it is on the list, but it's
//not the first one, then the value written to memory
//varies between CPUs.
if (offset == 0 && op.PostOffset != 0)
{
//Emit write back after the first write.
EmitLoadFromRegister(context, op.Rn);
context.EmitLdc_I4(op.PostOffset);
context.Emit(OpCodes.Add);
EmitStoreToRegister(context, op.Rn);
}
offset += 4;
}
}
}
public static void Str(ILEmitterCtx context)
{
EmitLoadOrStore(context, WordSizeLog2, AccessType.Store);
}
public static void Strb(ILEmitterCtx context)
{
EmitLoadOrStore(context, ByteSizeLog2, AccessType.Store);
}
public static void Strd(ILEmitterCtx context)
{
EmitLoadOrStore(context, DWordSizeLog2, AccessType.Store);
}
public static void Strh(ILEmitterCtx context)
{
EmitLoadOrStore(context, HWordSizeLog2, AccessType.Store);
}
private static void EmitLoadOrStore(ILEmitterCtx context, int size, AccessType accType)
{
OpCode32Mem op = (OpCode32Mem)context.CurrOp;
if (op.Index || op.WBack)
{
EmitLoadFromRegister(context, op.Rn);
context.EmitLdc_I4(op.Imm);
context.Emit(op.Add ? OpCodes.Add : OpCodes.Sub);
context.EmitSttmp();
}
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
if (op.Index)
{
context.EmitLdtmp();
}
else
{
EmitLoadFromRegister(context, op.Rn);
}
if ((accType & AccessType.Load) != 0)
{
if ((accType & AccessType.Signed) != 0)
{
EmitReadSx32Call(context, size);
}
else
{
EmitReadZxCall(context, size);
}
if (op.WBack)
{
context.EmitLdtmp();
EmitStoreToRegister(context, op.Rn);
}
if (size == DWordSizeLog2)
{
context.Emit(OpCodes.Dup);
context.EmitLdflg((int)PState.EBit);
ILLabel lblBigEndian = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
//Little endian mode.
context.Emit(OpCodes.Conv_U4);
EmitStoreToRegister(context, op.Rt);
context.EmitLsr(32);
context.Emit(OpCodes.Conv_U4);
EmitStoreToRegister(context, op.Rt | 1);
context.Emit(OpCodes.Br_S, lblEnd);
//Big endian mode.
context.MarkLabel(lblBigEndian);
context.EmitLsr(32);
context.Emit(OpCodes.Conv_U4);
EmitStoreToRegister(context, op.Rt);
context.Emit(OpCodes.Conv_U4);
EmitStoreToRegister(context, op.Rt | 1);
context.MarkLabel(lblEnd);
}
else
{
EmitStoreToRegister(context, op.Rt);
}
}
else
{
if (op.WBack)
{
context.EmitLdtmp();
EmitStoreToRegister(context, op.Rn);
}
EmitLoadFromRegister(context, op.Rt);
if (size == DWordSizeLog2)
{
context.Emit(OpCodes.Conv_U8);
context.EmitLdflg((int)PState.EBit);
ILLabel lblBigEndian = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
//Little endian mode.
EmitLoadFromRegister(context, op.Rt | 1);
context.Emit(OpCodes.Conv_U8);
context.EmitLsl(32);
context.Emit(OpCodes.Or);
context.Emit(OpCodes.Br_S, lblEnd);
//Big endian mode.
context.MarkLabel(lblBigEndian);
context.EmitLsl(32);
EmitLoadFromRegister(context, op.Rt | 1);
context.Emit(OpCodes.Conv_U8);
context.Emit(OpCodes.Or);
context.MarkLabel(lblEnd);
}
EmitWriteCall(context, size);
}
}
}
}

View file

@ -392,8 +392,7 @@ namespace ChocolArm64.Instructions
public static void Fadd_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitScalarSseOrSse2OpF(context, nameof(Sse.AddScalar));
}
@ -408,8 +407,7 @@ namespace ChocolArm64.Instructions
public static void Fadd_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorSseOrSse2OpF(context, nameof(Sse.Add));
}
@ -470,8 +468,7 @@ namespace ChocolArm64.Instructions
public static void Faddp_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Add));
}
@ -486,8 +483,7 @@ namespace ChocolArm64.Instructions
public static void Fdiv_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitScalarSseOrSse2OpF(context, nameof(Sse.DivideScalar));
}
@ -502,8 +498,7 @@ namespace ChocolArm64.Instructions
public static void Fdiv_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorSseOrSse2OpF(context, nameof(Sse.Divide));
}
@ -564,8 +559,7 @@ namespace ChocolArm64.Instructions
public static void Fmax_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitScalarSseOrSse2OpF(context, nameof(Sse.MaxScalar));
}
@ -580,8 +574,7 @@ namespace ChocolArm64.Instructions
public static void Fmax_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorSseOrSse2OpF(context, nameof(Sse.Max));
}
@ -612,8 +605,7 @@ namespace ChocolArm64.Instructions
public static void Fmaxp_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Max));
}
@ -628,8 +620,7 @@ namespace ChocolArm64.Instructions
public static void Fmin_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitScalarSseOrSse2OpF(context, nameof(Sse.MinScalar));
}
@ -644,8 +635,7 @@ namespace ChocolArm64.Instructions
public static void Fmin_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorSseOrSse2OpF(context, nameof(Sse.Min));
}
@ -676,8 +666,7 @@ namespace ChocolArm64.Instructions
public static void Fminp_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorPairwiseSseOrSse2OpF(context, nameof(Sse.Min));
}
@ -984,8 +973,7 @@ namespace ChocolArm64.Instructions
public static void Fmul_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitScalarSseOrSse2OpF(context, nameof(Sse.MultiplyScalar));
}
@ -1005,8 +993,7 @@ namespace ChocolArm64.Instructions
public static void Fmul_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorSseOrSse2OpF(context, nameof(Sse.Multiply));
}
@ -1753,8 +1740,7 @@ namespace ChocolArm64.Instructions
public static void Fsqrt_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitScalarSseOrSse2OpF(context, nameof(Sse.SqrtScalar));
}
@ -1769,8 +1755,7 @@ namespace ChocolArm64.Instructions
public static void Fsqrt_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorSseOrSse2OpF(context, nameof(Sse.Sqrt));
}
@ -1785,8 +1770,7 @@ namespace ChocolArm64.Instructions
public static void Fsub_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitScalarSseOrSse2OpF(context, nameof(Sse.SubtractScalar));
}
@ -1801,8 +1785,7 @@ namespace ChocolArm64.Instructions
public static void Fsub_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitVectorSseOrSse2OpF(context, nameof(Sse.Subtract));
}
@ -2268,6 +2251,15 @@ namespace ChocolArm64.Instructions
}
}
public static void Smlal_Ve(ILEmitterCtx context)
{
EmitVectorWidenTernaryOpByElemSx(context, () =>
{
context.Emit(OpCodes.Mul);
context.Emit(OpCodes.Add);
});
}
public static void Smlsl_V(ILEmitterCtx context)
{
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
@ -2319,11 +2311,25 @@ namespace ChocolArm64.Instructions
}
}
public static void Smlsl_Ve(ILEmitterCtx context)
{
EmitVectorWidenTernaryOpByElemSx(context, () =>
{
context.Emit(OpCodes.Mul);
context.Emit(OpCodes.Sub);
});
}
public static void Smull_V(ILEmitterCtx context)
{
EmitVectorWidenRnRmBinaryOpSx(context, () => context.Emit(OpCodes.Mul));
}
public static void Smull_Ve(ILEmitterCtx context)
{
EmitVectorWidenBinaryOpByElemSx(context, () => context.Emit(OpCodes.Mul));
}
public static void Sqabs_S(ILEmitterCtx context)
{
EmitScalarSaturatingUnaryOpSx(context, () => EmitAbs(context));
@ -2929,6 +2935,15 @@ namespace ChocolArm64.Instructions
}
}
public static void Umlal_Ve(ILEmitterCtx context)
{
EmitVectorWidenTernaryOpByElemZx(context, () =>
{
context.Emit(OpCodes.Mul);
context.Emit(OpCodes.Add);
});
}
public static void Umlsl_V(ILEmitterCtx context)
{
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
@ -2980,11 +2995,25 @@ namespace ChocolArm64.Instructions
}
}
public static void Umlsl_Ve(ILEmitterCtx context)
{
EmitVectorWidenTernaryOpByElemZx(context, () =>
{
context.Emit(OpCodes.Mul);
context.Emit(OpCodes.Sub);
});
}
public static void Umull_V(ILEmitterCtx context)
{
EmitVectorWidenRnRmBinaryOpZx(context, () => context.Emit(OpCodes.Mul));
}
public static void Umull_Ve(ILEmitterCtx context)
{
EmitVectorWidenBinaryOpByElemZx(context, () => context.Emit(OpCodes.Mul));
}
public static void Uqadd_S(ILEmitterCtx context)
{
EmitScalarSaturatingBinaryOpZx(context, SaturatingFlags.Add);

View file

@ -173,8 +173,7 @@ namespace ChocolArm64.Instructions
public static void Fcmeq_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareEqualScalar), scalar: true);
}
@ -186,8 +185,7 @@ namespace ChocolArm64.Instructions
public static void Fcmeq_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareEqual), scalar: false);
}
@ -199,8 +197,7 @@ namespace ChocolArm64.Instructions
public static void Fcmge_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar), scalar: true);
}
@ -212,8 +209,7 @@ namespace ChocolArm64.Instructions
public static void Fcmge_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual), scalar: false);
}
@ -225,8 +221,7 @@ namespace ChocolArm64.Instructions
public static void Fcmgt_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar), scalar: true);
}
@ -238,8 +233,7 @@ namespace ChocolArm64.Instructions
public static void Fcmgt_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan), scalar: false);
}
@ -251,8 +245,7 @@ namespace ChocolArm64.Instructions
public static void Fcmle_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar), scalar: true, isLeOrLt: true);
}
@ -264,8 +257,7 @@ namespace ChocolArm64.Instructions
public static void Fcmle_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual), scalar: false, isLeOrLt: true);
}
@ -277,8 +269,7 @@ namespace ChocolArm64.Instructions
public static void Fcmlt_S(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar), scalar: true, isLeOrLt: true);
}
@ -290,8 +281,7 @@ namespace ChocolArm64.Instructions
public static void Fcmlt_V(ILEmitterCtx context)
{
if (Optimizations.FastFP && Optimizations.UseSse
&& Optimizations.UseSse2)
if (Optimizations.FastFP && Optimizations.UseSse2)
{
EmitCmpSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan), scalar: false, isLeOrLt: true);
}

View file

@ -78,7 +78,6 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSse2 && sizeF == 1)
{
Type[] typesMov = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
Type[] typesCvt = new Type[] { typeof(Vector128<float>) };
string nameMov = op.RegisterSize == RegisterSize.Simd128
@ -88,7 +87,7 @@ namespace ChocolArm64.Instructions
context.EmitLdvec(op.Rn);
context.Emit(OpCodes.Dup);
context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov));
context.EmitCall(typeof(Sse).GetMethod(nameMov));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Double), typesCvt));
@ -144,7 +143,6 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSse2 && sizeF == 1)
{
Type[] typesMov = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
Type[] typesCvt = new Type[] { typeof(Vector128<double>) };
string nameMov = op.RegisterSize == RegisterSize.Simd128
@ -154,15 +152,15 @@ namespace ChocolArm64.Instructions
context.EmitLdvec(op.Rd);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov));
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh)));
EmitLdvecWithCastToDouble(context, op.Rn);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt));
context.Emit(OpCodes.Dup);
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov));
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh)));
context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov));
context.EmitCall(typeof(Sse).GetMethod(nameMov));
context.EmitStvec(op.Rd);
}

View file

@ -642,21 +642,21 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorOpByElem(context, emit, op.Index, false, true);
EmitVectorOpByElem(context, emit, op.Index, ternary: false, signed: true);
}
public static void EmitVectorBinaryOpByElemZx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorOpByElem(context, emit, op.Index, false, false);
EmitVectorOpByElem(context, emit, op.Index, ternary: false, signed: false);
}
public static void EmitVectorTernaryOpByElemZx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorOpByElem(context, emit, op.Index, true, false);
EmitVectorOpByElem(context, emit, op.Index, ternary: true, signed: false);
}
public static void EmitVectorOpByElem(ILEmitterCtx context, Action emit, int elem, bool ternary, bool signed)
@ -809,6 +809,64 @@ namespace ChocolArm64.Instructions
context.EmitStvec(op.Rd);
}
public static void EmitVectorWidenBinaryOpByElemSx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorWidenOpByElem(context, emit, op.Index, ternary: false, signed: true);
}
public static void EmitVectorWidenBinaryOpByElemZx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorWidenOpByElem(context, emit, op.Index, ternary: false, signed: false);
}
public static void EmitVectorWidenTernaryOpByElemSx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorWidenOpByElem(context, emit, op.Index, ternary: true, signed: true);
}
public static void EmitVectorWidenTernaryOpByElemZx(ILEmitterCtx context, Action emit)
{
OpCodeSimdRegElem64 op = (OpCodeSimdRegElem64)context.CurrOp;
EmitVectorWidenOpByElem(context, emit, op.Index, ternary: true, signed: false);
}
public static void EmitVectorWidenOpByElem(ILEmitterCtx context, Action emit, int elem, bool ternary, bool signed)
{
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
int elems = 8 >> op.Size;
int part = op.RegisterSize == RegisterSize.Simd128 ? elems : 0;
EmitVectorExtract(context, op.Rm, elem, op.Size, signed);
context.EmitSttmp();
for (int index = 0; index < elems; index++)
{
if (ternary)
{
EmitVectorExtract(context, op.Rd, index, op.Size + 1, signed);
}
EmitVectorExtract(context, op.Rn, part + index, op.Size, signed);
context.EmitLdtmp();
emit();
EmitVectorInsertTmp(context, index, op.Size + 1);
}
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
}
public static void EmitVectorPairwiseOpSx(ILEmitterCtx context, Action emit)
{
EmitVectorPairwiseOp(context, emit, true);
@ -1416,7 +1474,7 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSse)
{
//TODO: Use Sse2.MoveScalar once it is fixed,
//as of the time of writing it just crashes the JIT (SDK 2.1.500).
//as of the time of writing it just crashes the JIT (SDK 2.1.503).
/*Type[] typesMov = new Type[] { typeof(Vector128<ulong>) };

View file

@ -168,7 +168,7 @@ namespace ChocolArm64.Instructions
context.EmitLdint(op.Rn);
if (op.Rm != CpuThreadState.ZrIndex)
if (op.Rm != RegisterAlias.Zr)
{
context.EmitLdint(op.Rm);
}

View file

@ -12,6 +12,34 @@ namespace ChocolArm64.Instructions
{
static partial class InstEmit
{
#region "Masks"
private static readonly long[] _masksE0_TrnUzpXtn = new long[]
{
14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0,
13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0,
11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0
};
private static readonly long[] _masksE1_TrnUzp = new long[]
{
15L << 56 | 13L << 48 | 11L << 40 | 09L << 32 | 07L << 24 | 05L << 16 | 03L << 8 | 01L << 0,
15L << 56 | 14L << 48 | 11L << 40 | 10L << 32 | 07L << 24 | 06L << 16 | 03L << 8 | 02L << 0,
15L << 56 | 14L << 48 | 13L << 40 | 12L << 32 | 07L << 24 | 06L << 16 | 05L << 8 | 04L << 0
};
private static readonly long[] _masksE0_Uzp = new long[]
{
13L << 56 | 09L << 48 | 05L << 40 | 01L << 32 | 12L << 24 | 08L << 16 | 04L << 8 | 00L << 0,
11L << 56 | 10L << 48 | 03L << 40 | 02L << 32 | 09L << 24 | 08L << 16 | 01L << 8 | 00L << 0
};
private static readonly long[] _masksE1_Uzp = new long[]
{
15L << 56 | 11L << 48 | 07L << 40 | 03L << 32 | 14L << 24 | 10L << 16 | 06L << 8 | 02L << 0,
15L << 56 | 14L << 48 | 07L << 40 | 06L << 32 | 13L << 24 | 12L << 16 | 05L << 8 | 04L << 0
};
#endregion
public static void Dup_Gp(ILEmitterCtx context)
{
OpCodeSimdIns64 op = (OpCodeSimdIns64)context.CurrOp;
@ -379,15 +407,6 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSsse3)
{
long[] masks = new long[]
{
14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0,
13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0,
11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0
};
Type[] typesMov = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
Type[] typesSfl = new Type[] { typeof(Vector128<sbyte>), typeof(Vector128<sbyte>) };
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
string nameMov = op.RegisterSize == RegisterSize.Simd128
@ -397,18 +416,18 @@ namespace ChocolArm64.Instructions
context.EmitLdvec(op.Rd);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh), typesMov));
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh)));
EmitLdvecWithSignedCast(context, op.Rn, 0);
EmitLdvecWithSignedCast(context, op.Rn, 0); // value
context.EmitLdc_I8(masks[op.Size]);
context.Emit(OpCodes.Dup);
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // mask
context.Emit(OpCodes.Dup); // mask
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesSfl));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
context.EmitCall(typeof(Sse).GetMethod(nameMov, typesMov));
context.EmitCall(typeof(Sse).GetMethod(nameMov));
context.EmitStvec(op.Rd);
}
@ -465,22 +484,61 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
int words = op.GetBitsCount() >> 4;
int pairs = words >> op.Size;
for (int index = 0; index < pairs; index++)
if (Optimizations.UseSsse3)
{
int idx = index << 1;
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
EmitVectorExtractZx(context, op.Rn, idx + part, op.Size);
EmitVectorExtractZx(context, op.Rm, idx + part, op.Size);
string nameUpk = part == 0
? nameof(Sse2.UnpackLow)
: nameof(Sse2.UnpackHigh);
EmitVectorInsertTmp(context, idx + 1, op.Size);
EmitVectorInsertTmp(context, idx, op.Size);
EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value
if (op.Size < 3)
{
context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
EmitLdvecWithSignedCast(context, op.Rm, op.Size); // value
if (op.Size < 3)
{
context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(op.Size)));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
}
else
{
int words = op.GetBitsCount() >> 4;
int pairs = words >> op.Size;
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
for (int index = 0; index < pairs; index++)
{
int idx = index << 1;
EmitVectorExtractZx(context, op.Rn, idx + part, op.Size);
EmitVectorExtractZx(context, op.Rm, idx + part, op.Size);
EmitVectorInsertTmp(context, idx + 1, op.Size);
EmitVectorInsertTmp(context, idx, op.Size);
}
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
}
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -492,26 +550,91 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
int words = op.GetBitsCount() >> 4;
int pairs = words >> op.Size;
for (int index = 0; index < pairs; index++)
if (Optimizations.UseSsse3)
{
int idx = index << 1;
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
EmitVectorExtractZx(context, op.Rn, idx + part, op.Size);
EmitVectorExtractZx(context, op.Rm, idx + part, op.Size);
string nameUpk = part == 0
? nameof(Sse2.UnpackLow)
: nameof(Sse2.UnpackHigh);
EmitVectorInsertTmp(context, pairs + index, op.Size);
EmitVectorInsertTmp(context, index, op.Size);
if (op.RegisterSize == RegisterSize.Simd128)
{
EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value
if (op.Size < 3)
{
context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
EmitLdvecWithSignedCast(context, op.Rm, op.Size); // value
if (op.Size < 3)
{
context.EmitLdc_I8(_masksE1_TrnUzp [op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3)));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
}
else
{
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
EmitLdvecWithSignedCast(context, op.Rm, op.Size);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackLow), GetTypesSflUpk(op.Size))); // value
if (op.Size < 2)
{
context.EmitLdc_I8(_masksE1_Uzp[op.Size]); // maskE1
context.EmitLdc_I8(_masksE0_Uzp[op.Size]); // maskE0
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64Zero));
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3)));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
}
}
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
else
{
EmitVectorZeroUpper(context, op.Rd);
int words = op.GetBitsCount() >> 4;
int pairs = words >> op.Size;
for (int index = 0; index < pairs; index++)
{
int idx = index << 1;
EmitVectorExtractZx(context, op.Rn, idx + part, op.Size);
EmitVectorExtractZx(context, op.Rm, idx + part, op.Size);
EmitVectorInsertTmp(context, pairs + index, op.Size);
EmitVectorInsertTmp(context, index, op.Size);
}
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
}
@ -521,36 +644,26 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSse2)
{
EmitLdvecWithUnsignedCast(context, op.Rn, op.Size);
EmitLdvecWithUnsignedCast(context, op.Rm, op.Size);
Type[] types = new Type[]
{
VectorUIntTypesPerSizeLog2[op.Size],
VectorUIntTypesPerSizeLog2[op.Size]
};
string name = part == 0 || (part != 0 && op.RegisterSize == RegisterSize.Simd64)
string nameUpk = part == 0
? nameof(Sse2.UnpackLow)
: nameof(Sse2.UnpackHigh);
context.EmitCall(typeof(Sse2).GetMethod(name, types));
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
EmitLdvecWithSignedCast(context, op.Rm, op.Size);
if (op.RegisterSize == RegisterSize.Simd64 && part != 0)
if (op.RegisterSize == RegisterSize.Simd128)
{
context.EmitLdc_I4(8);
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(op.Size)));
}
else
{
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackLow), GetTypesSflUpk(op.Size)));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64Zero));
Type[] shTypes = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) };
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), shTypes));
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3)));
}
EmitStvecWithUnsignedCast(context, op.Rd, op.Size);
if (op.RegisterSize == RegisterSize.Simd64 && part == 0)
{
EmitVectorZeroUpper(context, op.Rd);
}
EmitStvecWithSignedCast(context, op.Rd, op.Size);
}
else
{
@ -579,5 +692,10 @@ namespace ChocolArm64.Instructions
}
}
}
private static Type[] GetTypesSflUpk(int size)
{
return new Type[] { VectorIntTypesPerSizeLog2[size], VectorIntTypesPerSizeLog2[size] };
}
}
}

View file

@ -1,8 +0,0 @@
using ChocolArm64.Decoders;
using ChocolArm64.Memory;
using ChocolArm64.State;
namespace ChocolArm64.Instructions
{
delegate void InstInterpreter(CpuThreadState state, MemoryManager memory, OpCode64 opCode);
}

View file

@ -664,7 +664,7 @@ namespace ChocolArm64.Instructions
for (int bit = highBit; bit >= 0; bit--)
{
if (((value >> bit) & 0b1) != 0)
if (((int)(value >> bit) & 0b1) != 0)
{
return (ulong)(highBit - bit);
}
@ -688,7 +688,7 @@ namespace ChocolArm64.Instructions
do
{
nibbleIdx -= 4;
preCount = ClzNibbleTbl[(value >> nibbleIdx) & 0b1111];
preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111];
count += preCount;
}
while (preCount == 4);
@ -698,11 +698,6 @@ namespace ChocolArm64.Instructions
public static ulong CountSetBits8(ulong value) // "size" is 8 (SIMD&FP Inst.).
{
if (value == 0xfful)
{
return 8ul;
}
value = ((value >> 1) & 0x55ul) + (value & 0x55ul);
value = ((value >> 2) & 0x33ul) + (value & 0x33ul);

View file

@ -1545,9 +1545,9 @@ namespace ChocolArm64.Instructions
return -value;
}
private static float ZerosOrOnes(bool zeros)
private static float ZerosOrOnes(bool ones)
{
return BitConverter.Int32BitsToSingle(!zeros ? 0 : -1);
return BitConverter.Int32BitsToSingle(ones ? -1 : 0);
}
private static float FPUnpack(
@ -2629,9 +2629,9 @@ namespace ChocolArm64.Instructions
return -value;
}
private static double ZerosOrOnes(bool zeros)
private static double ZerosOrOnes(bool ones)
{
return BitConverter.Int64BitsToDouble(!zeros ? 0L : -1L);
return BitConverter.Int64BitsToDouble(ones ? -1L : 0L);
}
private static double FPUnpack(

View file

@ -1,7 +0,0 @@
namespace ChocolArm64.Instructions32
{
static partial class A32InstInterpret
{
}
}

View file

@ -1,70 +0,0 @@
using ChocolArm64.Decoders;
using ChocolArm64.Decoders32;
using ChocolArm64.Memory;
using ChocolArm64.State;
using static ChocolArm64.Instructions32.A32InstInterpretHelper;
namespace ChocolArm64.Instructions32
{
static partial class A32InstInterpret
{
public static void B(CpuThreadState state, MemoryManager memory, OpCode64 opCode)
{
A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode;
if (IsConditionTrue(state, op.Cond))
{
BranchWritePc(state, GetPc(state) + (uint)op.Imm);
}
}
public static void Bl(CpuThreadState state, MemoryManager memory, OpCode64 opCode)
{
Blx(state, memory, opCode, false);
}
public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode)
{
Blx(state, memory, opCode, true);
}
public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode, bool x)
{
A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode;
if (IsConditionTrue(state, op.Cond))
{
uint pc = GetPc(state);
if (state.Thumb)
{
state.R14 = pc | 1;
}
else
{
state.R14 = pc - 4U;
}
if (x)
{
state.Thumb = !state.Thumb;
}
if (!state.Thumb)
{
pc &= ~3U;
}
BranchWritePc(state, pc + (uint)op.Imm);
}
}
private static void BranchWritePc(CpuThreadState state, uint pc)
{
state.R15 = state.Thumb
? pc & ~1U
: pc & ~3U;
}
}
}

View file

@ -1,65 +0,0 @@
using ChocolArm64.Decoders;
using ChocolArm64.State;
using System;
namespace ChocolArm64.Instructions32
{
static class A32InstInterpretHelper
{
public static bool IsConditionTrue(CpuThreadState state, Cond cond)
{
switch (cond)
{
case Cond.Eq: return state.Zero;
case Cond.Ne: return !state.Zero;
case Cond.GeUn: return state.Carry;
case Cond.LtUn: return !state.Carry;
case Cond.Mi: return state.Negative;
case Cond.Pl: return !state.Negative;
case Cond.Vs: return state.Overflow;
case Cond.Vc: return !state.Overflow;
case Cond.GtUn: return state.Carry && !state.Zero;
case Cond.LeUn: return !state.Carry && state.Zero;
case Cond.Ge: return state.Negative == state.Overflow;
case Cond.Lt: return state.Negative != state.Overflow;
case Cond.Gt: return state.Negative == state.Overflow && !state.Zero;
case Cond.Le: return state.Negative != state.Overflow && state.Zero;
}
return true;
}
public unsafe static uint GetReg(CpuThreadState state, int reg)
{
if ((uint)reg > 15)
{
throw new ArgumentOutOfRangeException(nameof(reg));
}
fixed (uint* ptr = &state.R0)
{
return *(ptr + reg);
}
}
public unsafe static void SetReg(CpuThreadState state, int reg, uint value)
{
if ((uint)reg > 15)
{
throw new ArgumentOutOfRangeException(nameof(reg));
}
fixed (uint* ptr = &state.R0)
{
*(ptr + reg) = value;
}
}
public static uint GetPc(CpuThreadState state)
{
//Due to the old fetch-decode-execute pipeline of old ARM CPUs,
//the PC is 4 or 8 bytes (2 instructions) ahead of the current instruction.
return state.R15 + (state.Thumb ? 2U : 4U);
}
}
}

View file

@ -1,7 +1,5 @@
using ChocolArm64.Decoders;
using ChocolArm64.Decoders32;
using ChocolArm64.Instructions;
using ChocolArm64.Instructions32;
using ChocolArm64.State;
using System;
using System.Collections.Generic;
@ -10,13 +8,61 @@ namespace ChocolArm64
{
static class OpCodeTable
{
private const int FastLookupSize = 0x1000;
private class InstInfo
{
public int Mask;
public int Value;
public Inst Inst;
public InstInfo(int mask, int value, Inst inst)
{
Mask = mask;
Value = value;
Inst = inst;
}
}
private static List<InstInfo> _allInstA32 = new List<InstInfo>();
private static List<InstInfo> _allInstT32 = new List<InstInfo>();
private static List<InstInfo> _allInstA64 = new List<InstInfo>();
private static InstInfo[][] _instA32FastLookup = new InstInfo[FastLookupSize][];
private static InstInfo[][] _instT32FastLookup = new InstInfo[FastLookupSize][];
private static InstInfo[][] _instA64FastLookup = new InstInfo[FastLookupSize][];
static OpCodeTable()
{
#region "OpCode Table (AArch32)"
//Integer
SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.B, typeof(A32OpCodeBImmAl));
SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Bl, typeof(A32OpCodeBImmAl));
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Blx, typeof(A32OpCodeBImmAl));
SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Add, typeof(OpCode32AluImm));
SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCode32AluRsImm));
SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B, typeof(OpCode32BImm));
SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Bl, typeof(OpCode32BImm));
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Blx, typeof(OpCode32BImm));
SetA32("<<<<000100101111111111110001xxxx", InstEmit32.Bx, typeof(OpCode32BReg));
SetT32("010001110xxxx000", InstEmit32.Bx, typeof(OpCodeT16BReg));
SetA32("<<<<00110101xxxx0000xxxxxxxxxxxx", InstEmit32.Cmp, typeof(OpCode32AluImm));
SetA32("<<<<00010101xxxx0000xxxxxxx0xxxx", InstEmit32.Cmp, typeof(OpCode32AluRsImm));
SetA32("<<<<100xx0x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldm, typeof(OpCode32MemMult));
SetA32("<<<<010xx0x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldr, typeof(OpCode32MemImm));
SetA32("<<<<010xx1x1xxxxxxxxxxxxxxxxxxxx", InstEmit32.Ldrb, typeof(OpCode32MemImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1101xxxx", InstEmit32.Ldrd, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1011xxxx", InstEmit32.Ldrh, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1101xxxx", InstEmit32.Ldrsb, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1111xxxx", InstEmit32.Ldrsh, typeof(OpCode32MemImm8));
SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstEmit32.Mov, typeof(OpCode32AluImm));
SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstEmit32.Mov, typeof(OpCode32AluRsImm));
SetT32("00100xxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeT16AluImm8));
SetA32("<<<<100xx0x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Stm, typeof(OpCode32MemMult));
SetA32("<<<<010xx0x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Str, typeof(OpCode32MemImm));
SetA32("<<<<010xx1x0xxxxxxxxxxxxxxxxxxxx", InstEmit32.Strb, typeof(OpCode32MemImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1111xxxx", InstEmit32.Strd, typeof(OpCode32MemImm8));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1011xxxx", InstEmit32.Strh, typeof(OpCode32MemImm8));
SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Sub, typeof(OpCode32AluImm));
SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Sub, typeof(OpCode32AluRsImm));
#endregion
#region "OpCode Table (AArch64)"
@ -413,9 +459,12 @@ namespace ChocolArm64
SetA64("0x001110<<1xxxxx011011xxxxxxxxxx", InstEmit.Smin_V, typeof(OpCodeSimdReg64));
SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", InstEmit.Sminp_V, typeof(OpCodeSimdReg64));
SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", InstEmit.Smlal_V, typeof(OpCodeSimdReg64));
SetA64("0x001111xxxxxxxx0010x0xxxxxxxxxx", InstEmit.Smlal_Ve, typeof(OpCodeSimdRegElem64));
SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", InstEmit.Smlsl_V, typeof(OpCodeSimdReg64));
SetA64("0x001111xxxxxxxx0110x0xxxxxxxxxx", InstEmit.Smlsl_Ve, typeof(OpCodeSimdRegElem64));
SetA64("0x001110000xxxxx001011xxxxxxxxxx", InstEmit.Smov_S, typeof(OpCodeSimdIns64));
SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", InstEmit.Smull_V, typeof(OpCodeSimdReg64));
SetA64("0x001111xxxxxxxx1010x0xxxxxxxxxx", InstEmit.Smull_Ve, typeof(OpCodeSimdRegElem64));
SetA64("01011110xx100000011110xxxxxxxxxx", InstEmit.Sqabs_S, typeof(OpCodeSimd64));
SetA64("0>001110<<100000011110xxxxxxxxxx", InstEmit.Sqabs_V, typeof(OpCodeSimd64));
SetA64("01011110xx1xxxxx000011xxxxxxxxxx", InstEmit.Sqadd_S, typeof(OpCodeSimdReg64));
@ -502,9 +551,12 @@ namespace ChocolArm64
SetA64("0x101110<<1xxxxx011011xxxxxxxxxx", InstEmit.Umin_V, typeof(OpCodeSimdReg64));
SetA64("0x101110<<1xxxxx101011xxxxxxxxxx", InstEmit.Uminp_V, typeof(OpCodeSimdReg64));
SetA64("0x101110<<1xxxxx100000xxxxxxxxxx", InstEmit.Umlal_V, typeof(OpCodeSimdReg64));
SetA64("0x101111xxxxxxxx0010x0xxxxxxxxxx", InstEmit.Umlal_Ve, typeof(OpCodeSimdRegElem64));
SetA64("0x101110<<1xxxxx101000xxxxxxxxxx", InstEmit.Umlsl_V, typeof(OpCodeSimdReg64));
SetA64("0x101111xxxxxxxx0110x0xxxxxxxxxx", InstEmit.Umlsl_Ve, typeof(OpCodeSimdRegElem64));
SetA64("0x001110000xxxxx001111xxxxxxxxxx", InstEmit.Umov_S, typeof(OpCodeSimdIns64));
SetA64("0x101110<<1xxxxx110000xxxxxxxxxx", InstEmit.Umull_V, typeof(OpCodeSimdReg64));
SetA64("0x101111xxxxxxxx1010x0xxxxxxxxxx", InstEmit.Umull_Ve, typeof(OpCodeSimdRegElem64));
SetA64("01111110xx1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_S, typeof(OpCodeSimdReg64));
SetA64("0>101110<<1xxxxx000011xxxxxxxxxx", InstEmit.Uqadd_V, typeof(OpCodeSimdReg64));
SetA64("0>101110<<1xxxxx010111xxxxxxxxxx", InstEmit.Uqrshl_V, typeof(OpCodeSimdReg64));
@ -544,63 +596,29 @@ namespace ChocolArm64
SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstEmit.Zip2_V, typeof(OpCodeSimdReg64));
#endregion
#region "Generate InstA64FastLookup Table (AArch64)"
var tmp = new List<InstInfo>[_fastLookupSize];
for (int i = 0; i < _fastLookupSize; i++)
{
tmp[i] = new List<InstInfo>();
}
foreach (var inst in _allInstA64)
{
int mask = ToFastLookupIndex(inst.Mask);
int value = ToFastLookupIndex(inst.Value);
for (int i = 0; i < _fastLookupSize; i++)
{
if ((i & mask) == value)
{
tmp[i].Add(inst);
}
}
}
for (int i = 0; i < _fastLookupSize; i++)
{
_instA64FastLookup[i] = tmp[i].ToArray();
}
#endregion
FillFastLookupTable(_instA32FastLookup, _allInstA32);
FillFastLookupTable(_instT32FastLookup, _allInstT32);
FillFastLookupTable(_instA64FastLookup, _allInstA64);
}
private class InstInfo
private static void SetA32(string encoding, InstEmitter emitter, Type type)
{
public int Mask;
public int Value;
public Inst Inst;
public InstInfo(int mask, int value, Inst inst)
{
Mask = mask;
Value = value;
Inst = inst;
}
Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Arm);
}
private static List<InstInfo> _allInstA32 = new List<InstInfo>();
private static List<InstInfo> _allInstA64 = new List<InstInfo>();
private static int _fastLookupSize = 0x1000;
private static InstInfo[][] _instA64FastLookup = new InstInfo[_fastLookupSize][];
private static void SetA32(string encoding, InstInterpreter interpreter, Type type)
private static void SetT32(string encoding, InstEmitter emitter, Type type)
{
Set(encoding, new Inst(interpreter, null, type), ExecutionMode.AArch32);
if (encoding.Length == 16)
{
encoding = "xxxxxxxxxxxxxxxx" + encoding;
}
Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Thumb);
}
private static void SetA64(string encoding, InstEmitter emitter, Type type)
{
Set(encoding, new Inst(null, emitter, type), ExecutionMode.AArch64);
Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch64);
}
private static void Set(string encoding, Inst inst, ExecutionMode mode)
@ -673,27 +691,55 @@ namespace ChocolArm64
}
}
private static void InsertInst(
int xMask,
int value,
Inst inst,
ExecutionMode mode)
private static void InsertInst(int xMask, int value, Inst inst, ExecutionMode mode)
{
InstInfo info = new InstInfo(xMask, value, inst);
if (mode == ExecutionMode.AArch64)
switch (mode)
{
_allInstA64.Add(info);
case ExecutionMode.Aarch32Arm: _allInstA32.Add(info); break;
case ExecutionMode.Aarch32Thumb: _allInstT32.Add(info); break;
case ExecutionMode.Aarch64: _allInstA64.Add(info); break;
}
else
}
private static void FillFastLookupTable(InstInfo[][] table, List<InstInfo> allInsts)
{
List<InstInfo>[] tmp = new List<InstInfo>[FastLookupSize];
for (int i = 0; i < FastLookupSize; i++)
{
_allInstA32.Add(info);
tmp[i] = new List<InstInfo>();
}
foreach (InstInfo inst in allInsts)
{
int mask = ToFastLookupIndex(inst.Mask);
int value = ToFastLookupIndex(inst.Value);
for (int i = 0; i < FastLookupSize; i++)
{
if ((i & mask) == value)
{
tmp[i].Add(inst);
}
}
}
for (int i = 0; i < FastLookupSize; i++)
{
table[i] = tmp[i].ToArray();
}
}
public static Inst GetInstA32(int opCode)
{
return GetInstFromList(_allInstA32, opCode);
return GetInstFromList(_instA32FastLookup[ToFastLookupIndex(opCode)], opCode);
}
public static Inst GetInstT32(int opCode)
{
return GetInstFromList(_instT32FastLookup[ToFastLookupIndex(opCode)], opCode);
}
public static Inst GetInstA64(int opCode)
@ -708,7 +754,7 @@ namespace ChocolArm64
private static Inst GetInstFromList(IEnumerable<InstInfo> instList, int opCode)
{
foreach (var node in instList)
foreach (InstInfo node in instList)
{
if ((opCode & node.Mask) == node.Value)
{

View file

@ -0,0 +1,15 @@
namespace ChocolArm64.State
{
enum Aarch32Mode
{
User = 0b10000,
Fiq = 0b10001,
Irq = 0b10010,
Supervisor = 0b10011,
Monitor = 0b10110,
Abort = 0b10111,
Hypervisor = 0b11010,
Undefined = 0b11011,
System = 0b11111
}
}

View file

@ -8,25 +8,11 @@ namespace ChocolArm64.State
{
public class CpuThreadState
{
internal const int LrIndex = 30;
internal const int ZrIndex = 31;
internal const int ErgSizeLog2 = 4;
internal const int DczSizeLog2 = 4;
private const int MinInstForCheck = 4000000;
internal ExecutionMode ExecutionMode;
//AArch32 state.
public uint R0, R1, R2, R3,
R4, R5, R6, R7,
R8, R9, R10, R11,
R12, R13, R14, R15;
public bool Thumb;
//AArch64 state.
public ulong X0, X1, X2, X3, X4, X5, X6, X7,
X8, X9, X10, X11, X12, X13, X14, X15,
X16, X17, X18, X19, X20, X21, X22, X23,
@ -37,11 +23,18 @@ namespace ChocolArm64.State
V16, V17, V18, V19, V20, V21, V22, V23,
V24, V25, V26, V27, V28, V29, V30, V31;
public bool Aarch32;
public bool Thumb;
public bool BigEndian;
public bool Overflow;
public bool Carry;
public bool Zero;
public bool Negative;
public int ElrHyp;
public bool Running { get; set; }
public int Core { get; set; }
@ -59,10 +52,10 @@ namespace ChocolArm64.State
{
get
{
return (Negative ? (int)PState.N : 0) |
(Zero ? (int)PState.Z : 0) |
(Carry ? (int)PState.C : 0) |
(Overflow ? (int)PState.V : 0);
return (Negative ? (int)PState.NMask : 0) |
(Zero ? (int)PState.ZMask : 0) |
(Carry ? (int)PState.CMask : 0) |
(Overflow ? (int)PState.VMask : 0);
}
}
@ -146,6 +139,18 @@ namespace ChocolArm64.State
Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode));
}
internal ExecutionMode GetExecutionMode()
{
if (!Aarch32)
{
return ExecutionMode.Aarch64;
}
else
{
return Thumb ? ExecutionMode.Aarch32Thumb : ExecutionMode.Aarch32Arm;
}
}
internal bool GetFpcrFlag(Fpcr flag)
{
return (Fpcr & (1 << (int)flag)) != 0;

View file

@ -2,7 +2,8 @@ namespace ChocolArm64.State
{
enum ExecutionMode
{
AArch32,
AArch64
Aarch64,
Aarch32Arm,
Aarch32Thumb
}
}

View file

@ -5,19 +5,20 @@ namespace ChocolArm64.State
[Flags]
enum PState
{
TBit = 5,
EBit = 9,
VBit = 28,
CBit = 29,
ZBit = 30,
NBit = 31,
V = 1 << VBit,
C = 1 << CBit,
Z = 1 << ZBit,
N = 1 << NBit,
TMask = 1 << TBit,
EMask = 1 << EBit,
Nz = N | Z,
Cv = C | V,
Nzcv = Nz | Cv
VMask = 1 << VBit,
CMask = 1 << CBit,
ZMask = 1 << ZBit,
NMask = 1 << NBit
}
}

View file

@ -43,6 +43,9 @@ namespace ChocolArm64.State
{
switch ((PState)Index)
{
case PState.TBit: return GetField(nameof(CpuThreadState.Thumb));
case PState.EBit: return GetField(nameof(CpuThreadState.BigEndian));
case PState.VBit: return GetField(nameof(CpuThreadState.Overflow));
case PState.CBit: return GetField(nameof(CpuThreadState.Carry));
case PState.ZBit: return GetField(nameof(CpuThreadState.Zero));

View file

@ -0,0 +1,41 @@
namespace ChocolArm64.State
{
static class RegisterAlias
{
public const int R8Usr = 8;
public const int R9Usr = 9;
public const int R10Usr = 10;
public const int R11Usr = 11;
public const int R12Usr = 12;
public const int SpUsr = 13;
public const int LrUsr = 14;
public const int SpHyp = 15;
public const int LrIrq = 16;
public const int SpIrq = 17;
public const int LrSvc = 18;
public const int SpSvc = 19;
public const int LrAbt = 20;
public const int SpAbt = 21;
public const int LrUnd = 22;
public const int SpUnd = 23;
public const int R8Fiq = 24;
public const int R9Fiq = 25;
public const int R10Fiq = 26;
public const int R11Fiq = 27;
public const int R12Fiq = 28;
public const int SpFiq = 29;
public const int LrFiq = 30;
public const int Aarch32Lr = 14;
public const int Aarch32Pc = 15;
public const int Lr = 30;
public const int Zr = 31;
}
}

View file

@ -23,6 +23,8 @@ namespace ChocolArm64.Translation
public Block CurrBlock => _currBlock;
public OpCode64 CurrOp => _currBlock?.OpCodes[_opcIndex];
public Aarch32Mode Mode { get; } = Aarch32Mode.User; //TODO
private Dictionary<Block, ILBlock> _visitedBlocks;
private Queue<Block> _branchTargets;
@ -97,11 +99,52 @@ namespace ChocolArm64.Translation
EmitSynchronization();
}
//On AARCH32 mode, (almost) all instruction can be conditionally
//executed, and the required condition is encoded on the opcode.
//We handle that here, skipping the instruction if the condition
//is not met. We can just ignore it when the condition is "Always",
//because in this case the instruction is always going to be executed.
//Condition "Never" is also ignored because this is a special encoding
//used by some unconditional instructions.
ILLabel lblSkip = null;
if (CurrOp is OpCode32 op && op.Cond < Condition.Al)
{
lblSkip = new ILLabel();
EmitCondBranch(lblSkip, GetInverseCond(op.Cond));
}
CurrOp.Emitter(this);
if (lblSkip != null)
{
MarkLabel(lblSkip);
//If this is the last op on the block, and there's no "next" block
//after this one, then we have to return right now, with the address
//of the next instruction to be executed (in the case that the condition
//is false, and the branch was not taken, as all basic blocks should end with
//some kind of branch).
if (CurrOp == CurrBlock.GetLastOp() && CurrBlock.Next == null)
{
EmitStoreState();
EmitLdc_I8(CurrOp.Position + CurrOp.OpCodeSizeInBytes);
Emit(OpCodes.Ret);
}
}
_ilBlock.Add(new ILBarrier());
}
private Condition GetInverseCond(Condition cond)
{
//Bit 0 of all conditions is basically a negation bit, so
//inverting this bit has the effect of inverting the condition.
return (Condition)((int)cond ^ 1);
}
private void EmitSynchronization()
{
EmitLdarg(TranslatedSub.StateArgIdx);
@ -243,27 +286,27 @@ namespace ChocolArm64.Translation
{
_optOpLastCompare = CurrOp;
InstEmitAluHelper.EmitDataLoadOpers(this);
InstEmitAluHelper.EmitAluLoadOpers(this);
Stloc(CmpOptTmp2Index, IoType.Int);
Stloc(CmpOptTmp1Index, IoType.Int);
}
private Dictionary<Cond, OpCode> _branchOps = new Dictionary<Cond, OpCode>()
private Dictionary<Condition, OpCode> _branchOps = new Dictionary<Condition, OpCode>()
{
{ Cond.Eq, OpCodes.Beq },
{ Cond.Ne, OpCodes.Bne_Un },
{ Cond.GeUn, OpCodes.Bge_Un },
{ Cond.LtUn, OpCodes.Blt_Un },
{ Cond.GtUn, OpCodes.Bgt_Un },
{ Cond.LeUn, OpCodes.Ble_Un },
{ Cond.Ge, OpCodes.Bge },
{ Cond.Lt, OpCodes.Blt },
{ Cond.Gt, OpCodes.Bgt },
{ Cond.Le, OpCodes.Ble }
{ Condition.Eq, OpCodes.Beq },
{ Condition.Ne, OpCodes.Bne_Un },
{ Condition.GeUn, OpCodes.Bge_Un },
{ Condition.LtUn, OpCodes.Blt_Un },
{ Condition.GtUn, OpCodes.Bgt_Un },
{ Condition.LeUn, OpCodes.Ble_Un },
{ Condition.Ge, OpCodes.Bge },
{ Condition.Lt, OpCodes.Blt },
{ Condition.Gt, OpCodes.Bgt },
{ Condition.Le, OpCodes.Ble }
};
public void EmitCondBranch(ILLabel target, Cond cond)
public void EmitCondBranch(ILLabel target, Condition cond)
{
OpCode ilOp;
@ -432,7 +475,7 @@ namespace ChocolArm64.Translation
public void EmitLdintzr(int index)
{
if (index != CpuThreadState.ZrIndex)
if (index != RegisterAlias.Zr)
{
EmitLdint(index);
}
@ -444,7 +487,7 @@ namespace ChocolArm64.Translation
public void EmitStintzr(int index)
{
if (index != CpuThreadState.ZrIndex)
if (index != RegisterAlias.Zr)
{
EmitStint(index);
}
@ -487,7 +530,15 @@ namespace ChocolArm64.Translation
public void EmitLdflg(int index) => Ldloc(index, IoType.Flag);
public void EmitStflg(int index)
{
_optOpLastFlagSet = CurrOp;
//Set this only if any of the NZCV flag bits were modified.
//This is used to ensure that, when emiting a direct IL branch
//instruction for compare + branch sequences, we're not expecting
//to use comparison values from an old instruction, when in fact
//the flags were already overwritten by another instruction further along.
if (index >= (int)PState.VBit)
{
_optOpLastFlagSet = CurrOp;
}
Stloc(index, IoType.Flag);
}

View file

@ -24,34 +24,10 @@ namespace ChocolArm64
internal void ExecuteSubroutine(CpuThread thread, long position)
{
//TODO: Both the execute A32/A64 methods should be merged on the future,
//when both ISAs are implemented with the interpreter and JIT.
//As of now, A32 only has a interpreter and A64 a JIT.
CpuThreadState state = thread.ThreadState;
MemoryManager memory = thread.Memory;
if (state.ExecutionMode == ExecutionMode.AArch32)
{
ExecuteSubroutineA32(state, memory);
}
else
{
ExecuteSubroutineA64(state, memory, position);
}
ExecuteSubroutine(thread.ThreadState, thread.Memory, position);
}
private void ExecuteSubroutineA32(CpuThreadState state, MemoryManager memory)
{
do
{
OpCode64 opCode = Decoder.DecodeOpCode(state, memory, state.R15);
opCode.Interpreter(state, memory, opCode);
}
while (state.R15 != 0 && state.Running);
}
private void ExecuteSubroutineA64(CpuThreadState state, MemoryManager memory, long position)
private void ExecuteSubroutine(CpuThreadState state, MemoryManager memory, long position)
{
ProfileConfig tier0 = Profiles.CPU.TranslateTier0;
ProfileConfig tier1 = Profiles.CPU.TranslateTier1;
@ -69,14 +45,14 @@ namespace ChocolArm64
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
{
Profile.Begin(tier0);
sub = TranslateTier0(state, memory, position);
sub = TranslateTier0(memory, position, state.GetExecutionMode());
Profile.End(tier0);
}
if (sub.ShouldReJit())
{
Profile.Begin(tier1);
TranslateTier1(state, memory, position);
TranslateTier1(memory, position, state.GetExecutionMode());
Profile.End(tier1);
}
@ -90,9 +66,9 @@ namespace ChocolArm64
return _cache.HasSubroutine(position);
}
private TranslatedSub TranslateTier0(CpuThreadState state, MemoryManager memory, long position)
private TranslatedSub TranslateTier0(MemoryManager memory, long position, ExecutionMode mode)
{
Block block = Decoder.DecodeBasicBlock(state, memory, position);
Block block = Decoder.DecodeBasicBlock(memory, position, mode);
ILEmitterCtx context = new ILEmitterCtx(_cache, block);
@ -109,9 +85,9 @@ namespace ChocolArm64
return subroutine;
}
private void TranslateTier1(CpuThreadState state, MemoryManager memory, long position)
private void TranslateTier1(MemoryManager memory, long position, ExecutionMode mode)
{
Block graph = Decoder.DecodeSubroutine(_cache, state, memory, position);
Block graph = Decoder.DecodeSubroutine(_cache, memory, position, mode);
ILEmitterCtx context = new ILEmitterCtx(_cache, graph);

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;

View file

@ -5,6 +5,7 @@ namespace Ryujinx.Common.Logging
Audio,
Cpu,
Font,
Emulation,
Gpu,
Hid,
Kernel,

View file

@ -16,6 +16,8 @@ namespace Ryujinx.Common.Logging
public static event EventHandler<LogEventArgs> Updated;
public static bool EnableFileLog { get; set; }
static Logger()
{
m_EnabledLevels = new bool[Enum.GetNames(typeof(LogLevel)).Length];

View file

@ -278,25 +278,22 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
forceUpdate = false;
if (New.ScissorTestEnabled[Index] != Old.ScissorTestEnabled[Index])
if (New.ScissorTestEnabled[Index])
{
if (New.ScissorTestEnabled[Index])
// If there is only 1 scissor test, geometry shaders are disabled so the scissor test applies to all viewports
if (New.ScissorTestCount == 1)
{
// If there is only 1 scissor test geometry shaders are disables so the scissor test applies to all viewports
if (New.ScissorTestCount == 1)
{
GL.Enable(EnableCap.ScissorTest);
}
else
{
GL.Enable(IndexedEnableCap.ScissorTest, Index);
}
forceUpdate = true;
GL.Enable(EnableCap.ScissorTest);
}
else
{
GL.Disable(IndexedEnableCap.ScissorTest, Index);
GL.Enable(IndexedEnableCap.ScissorTest, Index);
}
forceUpdate = true;
}
else
{
GL.Disable(IndexedEnableCap.ScissorTest, Index);
}
if (New.ScissorTestEnabled[Index] &&

View file

@ -367,6 +367,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Disable(EnableCap.FramebufferSrgb);
// Will be re-enabled if needed while binding, called before any game GL calls
GL.Disable(EnableCap.ScissorTest);
GL.BlitFramebuffer(
SrcX0,
SrcY0,

View file

@ -63,6 +63,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private Dictionary<int, ShaderDeclInfo> m_OutAttributes;
private Dictionary<int, ShaderDeclInfo> m_Gprs;
private Dictionary<int, ShaderDeclInfo> m_GprsHalf;
private Dictionary<int, ShaderDeclInfo> m_Preds;
public IReadOnlyDictionary<ShaderIrOp, ShaderDeclInfo> CbTextures => m_CbTextures;
@ -74,8 +75,9 @@ namespace Ryujinx.Graphics.Gal.Shader
public IReadOnlyDictionary<int, ShaderDeclInfo> InAttributes => m_InAttributes;
public IReadOnlyDictionary<int, ShaderDeclInfo> OutAttributes => m_OutAttributes;
public IReadOnlyDictionary<int, ShaderDeclInfo> Gprs => m_Gprs;
public IReadOnlyDictionary<int, ShaderDeclInfo> Preds => m_Preds;
public IReadOnlyDictionary<int, ShaderDeclInfo> Gprs => m_Gprs;
public IReadOnlyDictionary<int, ShaderDeclInfo> GprsHalf => m_GprsHalf;
public IReadOnlyDictionary<int, ShaderDeclInfo> Preds => m_Preds;
public GalShaderType ShaderType { get; private set; }
@ -92,8 +94,9 @@ namespace Ryujinx.Graphics.Gal.Shader
m_InAttributes = new Dictionary<int, ShaderDeclInfo>();
m_OutAttributes = new Dictionary<int, ShaderDeclInfo>();
m_Gprs = new Dictionary<int, ShaderDeclInfo>();
m_Preds = new Dictionary<int, ShaderDeclInfo>();
m_Gprs = new Dictionary<int, ShaderDeclInfo>();
m_GprsHalf = new Dictionary<int, ShaderDeclInfo>();
m_Preds = new Dictionary<int, ShaderDeclInfo>();
}
public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType, ShaderHeader Header) : this(ShaderType)
@ -146,8 +149,9 @@ namespace Ryujinx.Graphics.Gal.Shader
Merge(Combined.m_Attributes, VpA.m_Attributes, VpB.m_Attributes);
Merge(Combined.m_OutAttributes, VpA.m_OutAttributes, VpB.m_OutAttributes);
Merge(Combined.m_Gprs, VpA.m_Gprs, VpB.m_Gprs);
Merge(Combined.m_Preds, VpA.m_Preds, VpB.m_Preds);
Merge(Combined.m_Gprs, VpA.m_Gprs, VpB.m_Gprs);
Merge(Combined.m_GprsHalf, VpA.m_GprsHalf, VpB.m_GprsHalf);
Merge(Combined.m_Preds, VpA.m_Preds, VpB.m_Preds);
//Merge input attributes.
foreach (KeyValuePair<int, ShaderDeclInfo> KV in VpA.m_InAttributes)
@ -343,7 +347,20 @@ namespace Ryujinx.Graphics.Gal.Shader
{
string Name = GetGprName(Gpr.Index);
m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index));
if (Gpr.RegisterSize == ShaderRegisterSize.Single)
{
m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index));
}
else if (Gpr.RegisterSize == ShaderRegisterSize.Half)
{
Name += "_h" + Gpr.HalfPart;
m_GprsHalf.TryAdd((Gpr.Index << 1) | Gpr.HalfPart, new ShaderDeclInfo(Name, Gpr.Index));
}
else /* if (Gpr.RegisterSize == ShaderRegisterSize.Double) */
{
throw new NotImplementedException("Double types are not supported.");
}
}
break;
}

View file

@ -377,6 +377,7 @@ namespace Ryujinx.Graphics.Gal.Shader
private void PrintDeclGprs()
{
PrintDecls(Decl.Gprs);
PrintDecls(Decl.GprsHalf);
}
private void PrintDeclPreds()
@ -910,7 +911,23 @@ namespace Ryujinx.Graphics.Gal.Shader
private string GetName(ShaderIrOperGpr Gpr)
{
return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index);
if (Gpr.IsConst)
{
return "0";
}
if (Gpr.RegisterSize == ShaderRegisterSize.Single)
{
return GetNameWithSwizzle(Decl.Gprs, Gpr.Index);
}
else if (Gpr.RegisterSize == ShaderRegisterSize.Half)
{
return GetNameWithSwizzle(Decl.GprsHalf, (Gpr.Index << 1) | Gpr.HalfPart);
}
else /* if (Gpr.RegisterSize == ShaderRegisterSize.Double) */
{
throw new NotImplementedException("Double types are not supported.");
}
}
private string GetValue(ShaderIrOperImm Imm)

View file

@ -6,6 +6,14 @@ namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
private enum HalfOutputType
{
PackedFp16,
Fp32,
MergeH0,
MergeH1
}
public static void Bfe_C(ShaderIrBlock Block, long OpCode, int Position)
{
EmitBfe(Block, OpCode, ShaderOper.CR);
@ -144,6 +152,16 @@ namespace Ryujinx.Graphics.Gal.Shader
EmitFsetp(Block, OpCode, ShaderOper.RR);
}
public static void Hadd2_R(ShaderIrBlock Block, long OpCode, int Position)
{
EmitBinaryHalfOp(Block, OpCode, ShaderIrInst.Fadd);
}
public static void Hmul2_R(ShaderIrBlock Block, long OpCode, int Position)
{
EmitBinaryHalfOp(Block, OpCode, ShaderIrInst.Fmul);
}
public static void Iadd_C(ShaderIrBlock Block, long OpCode, int Position)
{
EmitIadd(Block, OpCode, ShaderOper.CR);
@ -1041,6 +1059,47 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op)));
}
private static void EmitBinaryHalfOp(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
{
bool AbsB = OpCode.Read(30);
bool NegB = OpCode.Read(31);
bool Sat = OpCode.Read(32);
bool AbsA = OpCode.Read(44);
ShaderIrOperGpr[] VecA = OpCode.GprHalfVec8();
ShaderIrOperGpr[] VecB = OpCode.GprHalfVec20();
HalfOutputType OutputType = (HalfOutputType)OpCode.Read(49, 3);
int Elems = OutputType == HalfOutputType.PackedFp16 ? 2 : 1;
int First = OutputType == HalfOutputType.MergeH1 ? 1 : 0;
for (int Index = First; Index < Elems; Index++)
{
ShaderIrNode OperA = GetAluFabs (VecA[Index], AbsA);
ShaderIrNode OperB = GetAluFabsFneg(VecB[Index], AbsB, NegB);
ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
ShaderIrOperGpr Dst = GetHalfDst(OpCode, OutputType, Index);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, GetAluFsat(Op, Sat))));
}
}
private static ShaderIrOperGpr GetHalfDst(long OpCode, HalfOutputType OutputType, int Index)
{
switch (OutputType)
{
case HalfOutputType.PackedFp16: return OpCode.GprHalf0(Index);
case HalfOutputType.Fp32: return OpCode.Gpr0();
case HalfOutputType.MergeH0: return OpCode.GprHalf0(0);
case HalfOutputType.MergeH1: return OpCode.GprHalf0(1);
}
throw new ArgumentException(nameof(OutputType));
}
private static void EmitLop(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
{
int SubOp = OpCode.Read(41, 3);

View file

@ -6,8 +6,6 @@ namespace Ryujinx.Graphics.Gal.Shader
{
static partial class ShaderDecode
{
private const int TempRegStart = 0x100;
private const int ____ = 0x0;
private const int R___ = 0x1;
private const int _G__ = 0x2;
@ -149,14 +147,18 @@ namespace Ryujinx.Graphics.Gal.Shader
for (int Index = 0; Index < Coords.Length; Index++)
{
Coords[Index] = OpCode.Gpr8();
ShaderIrOperGpr CoordReg = OpCode.Gpr8();
Coords[Index].Index += Index;
CoordReg.Index += Index;
if (Coords[Index].Index > ShaderIrOperGpr.ZRIndex)
if (!CoordReg.IsValidRegister)
{
Coords[Index].Index = ShaderIrOperGpr.ZRIndex;
CoordReg.Index = ShaderIrOperGpr.ZRIndex;
}
Coords[Index] = ShaderIrOperGpr.MakeTemporary(Index);
Block.AddNode(new ShaderIrAsg(Coords[Index], CoordReg));
}
int ChMask = OpCode.Read(31, 0xf);
@ -167,17 +169,6 @@ namespace Ryujinx.Graphics.Gal.Shader
ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs;
for (int Ch = 0; Ch < 4; Ch++)
{
ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch);
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
}
int RegInc = 0;
for (int Ch = 0; Ch < 4; Ch++)
@ -187,18 +178,20 @@ namespace Ryujinx.Graphics.Gal.Shader
continue;
}
ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch);
ShaderIrOperGpr Dst = OpCode.Gpr0();
Dst.Index += RegInc++;
if (Dst.Index >= ShaderIrOperGpr.ZRIndex)
if (!Dst.IsValidRegister || Dst.IsConst)
{
continue;
}
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Src)));
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
}
}
@ -215,57 +208,81 @@ namespace Ryujinx.Graphics.Gal.Shader
private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
{
//TODO: Support other formats.
ShaderIrNode OperA = OpCode.Gpr8();
ShaderIrNode OperB = OpCode.Gpr20();
ShaderIrNode OperC = OpCode.Imm13_36();
int LutIndex;
LutIndex = OpCode.Gpr0 ().Index != ShaderIrOperGpr.ZRIndex ? 1 : 0;
LutIndex |= OpCode.Gpr28().Index != ShaderIrOperGpr.ZRIndex ? 2 : 0;
LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0;
LutIndex |= !OpCode.Gpr28().IsConst ? 2 : 0;
if (LutIndex == 0)
{
//Both registers are RZ, color is not written anywhere.
//So, the intruction is basically a no-op.
//Both destination registers are RZ, do nothing.
return;
}
int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)];
bool Fp16 = !OpCode.Read(59);
for (int Ch = 0; Ch < 4; Ch++)
{
ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch);
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta);
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
}
int RegInc = 0;
int DstIncrement = 0;
ShaderIrOperGpr GetDst()
{
ShaderIrOperGpr Dst;
switch (LutIndex)
if (Fp16)
{
case 1: Dst = OpCode.Gpr0(); break;
case 2: Dst = OpCode.Gpr28(); break;
case 3: Dst = (RegInc >> 1) != 0
? OpCode.Gpr28()
: OpCode.Gpr0 (); break;
//FP16 mode, two components are packed on the two
//halfs of a 32-bits register, as two half-float values.
int HalfPart = DstIncrement & 1;
default: throw new InvalidOperationException();
switch (LutIndex)
{
case 1: Dst = OpCode.GprHalf0(HalfPart); break;
case 2: Dst = OpCode.GprHalf28(HalfPart); break;
case 3: Dst = (DstIncrement >> 1) != 0
? OpCode.GprHalf28(HalfPart)
: OpCode.GprHalf0(HalfPart); break;
default: throw new InvalidOperationException();
}
}
else
{
//32-bits mode, each component uses one register.
//Two components uses two consecutive registers.
switch (LutIndex)
{
case 1: Dst = OpCode.Gpr0(); break;
case 2: Dst = OpCode.Gpr28(); break;
case 3: Dst = (DstIncrement >> 1) != 0
? OpCode.Gpr28()
: OpCode.Gpr0(); break;
default: throw new InvalidOperationException();
}
Dst.Index += DstIncrement & 1;
}
Dst.Index += RegInc++ & 1;
DstIncrement++;
return Dst;
}
int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)];
if (ChMask == 0)
{
//All channels are disabled, do nothing.
return;
}
ShaderIrNode OperC = OpCode.Imm13_36();
ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0);
ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1);
Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8()));
Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20()));
for (int Ch = 0; Ch < 4; Ch++)
{
if (!IsChannelUsed(ChMask, Ch))
@ -273,13 +290,15 @@ namespace Ryujinx.Graphics.Gal.Shader
continue;
}
ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch);
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta);
ShaderIrOperGpr Dst = GetDst();
if (Dst.Index != ShaderIrOperGpr.ZRIndex)
if (Dst.IsValidRegister && !Dst.IsConst)
{
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Src)));
Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op)));
}
}
}

View file

@ -75,6 +75,49 @@ namespace Ryujinx.Graphics.Gal.Shader
return new ShaderIrOperGpr(OpCode.Read(28, 0xff));
}
private static ShaderIrOperGpr[] GprHalfVec8(this long OpCode)
{
return GetGprHalfVec2(OpCode.Read(8, 0xff), OpCode.Read(47, 3));
}
private static ShaderIrOperGpr[] GprHalfVec20(this long OpCode)
{
return GetGprHalfVec2(OpCode.Read(20, 0xff), OpCode.Read(28, 3));
}
private static ShaderIrOperGpr[] GetGprHalfVec2(int Gpr, int Mask)
{
if (Mask == 1)
{
//This value is used for FP32, the whole 32-bits register
//is used as each element on the vector.
return new ShaderIrOperGpr[]
{
new ShaderIrOperGpr(Gpr),
new ShaderIrOperGpr(Gpr)
};
}
ShaderIrOperGpr Low = new ShaderIrOperGpr(Gpr, 0);
ShaderIrOperGpr High = new ShaderIrOperGpr(Gpr, 1);
return new ShaderIrOperGpr[]
{
(Mask & 1) != 0 ? High : Low,
(Mask & 2) != 0 ? High : Low
};
}
private static ShaderIrOperGpr GprHalf0(this long OpCode, int HalfPart)
{
return new ShaderIrOperGpr(OpCode.Read(0, 0xff), HalfPart);
}
private static ShaderIrOperGpr GprHalf28(this long OpCode, int HalfPart)
{
return new ShaderIrOperGpr(OpCode.Read(28, 0xff), HalfPart);
}
private static ShaderIrOperImm Imm5_39(this long OpCode)
{
return new ShaderIrOperImm(OpCode.Read(39, 0x1f));

View file

@ -6,13 +6,26 @@ namespace Ryujinx.Graphics.Gal.Shader
public bool IsConst => Index == ZRIndex;
public bool IsValidRegister => (Index <= ZRIndex);
public bool IsValidRegister => (uint)Index <= ZRIndex;
public int Index { get; set; }
public int Index { get; set; }
public int HalfPart { get; set; }
public ShaderRegisterSize RegisterSize { get; private set; }
public ShaderIrOperGpr(int Index)
{
this.Index = Index;
RegisterSize = ShaderRegisterSize.Single;
}
public ShaderIrOperGpr(int Index, int HalfPart)
{
this.Index = Index;
this.HalfPart = HalfPart;
RegisterSize = ShaderRegisterSize.Half;
}
public static ShaderIrOperGpr MakeTemporary(int Index = 0)

View file

@ -58,6 +58,8 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("010010111011xx", ShaderDecode.Fsetp_C);
Set("0011011x1011xx", ShaderDecode.Fsetp_I);
Set("010110111011xx", ShaderDecode.Fsetp_R);
Set("0101110100010x", ShaderDecode.Hadd2_R);
Set("0101110100001x", ShaderDecode.Hmul2_R);
Set("0100110010111x", ShaderDecode.I2f_C);
Set("0011100x10111x", ShaderDecode.I2f_I);
Set("0101110010111x", ShaderDecode.I2f_R);
@ -118,7 +120,7 @@ namespace Ryujinx.Graphics.Gal.Shader
Set("110000xxxx111x", ShaderDecode.Tex);
Set("1101111010111x", ShaderDecode.Tex_B);
Set("1101111101001x", ShaderDecode.Texq);
Set("1101100xxxxxxx", ShaderDecode.Texs);
Set("1101x00xxxxxxx", ShaderDecode.Texs);
Set("1101101xxxxxxx", ShaderDecode.Tlds);
Set("01011111xxxxxx", ShaderDecode.Vmad);
Set("0100111xxxxxxx", ShaderDecode.Xmad_CR);

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.Gal.Shader
{
enum ShaderRegisterSize
{
Half,
Single,
Double
}
}

View file

@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gal
ulong Instruction = 0;
//Dump until a NOP instruction is found
while ((Instruction >> 52 & 0xfff8) != 0x50b0)
while ((Instruction >> 48 & 0xfff8) != 0x50b0)
{
uint Word0 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 0);
uint Word1 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 4);

View file

@ -454,7 +454,7 @@ namespace Ryujinx.Graphics.Graphics3d
// Once geometry shaders are fixed it should be equal to GalPipelineState.RenderTargetCount when shader loaded, otherwise equal to 1
State.ScissorTestCount = 1;
for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
for (int Index = 0; Index < State.ScissorTestCount; Index++)
{
State.ScissorTestEnabled[Index] = ReadRegisterBool(NvGpuEngine3dReg.ScissorEnable + Index * 4);
@ -473,6 +473,12 @@ namespace Ryujinx.Graphics.Graphics3d
if ((int)State.FlipY == -1)
{
State.ScissorTestY[Index] = ViewportHeight - State.ScissorTestY[Index] - State.ScissorTestHeight[Index];
// Handle negative viewpont coordinate
if (State.ScissorTestY[Index] < 0)
{
State.ScissorTestY[Index] = 0;
}
}
}
}

View file

@ -225,11 +225,6 @@ namespace Ryujinx.HLE.HOS
}
}
if (!metaData.Is64Bits)
{
throw new NotImplementedException("32-bit titles are unsupported!");
}
CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
LoadNso("rtld");
@ -394,7 +389,57 @@ namespace Ryujinx.HLE.HOS
return;
}
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file");
// This is not a normal NSP, it's actually a ExeFS as a NSP
Npdm metaData = null;
PfsFileEntry npdmFile = nsp.Files.FirstOrDefault(x => x.Name.Equals("main.npdm"));
if (npdmFile != null)
{
Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm...");
metaData = new Npdm(nsp.OpenFile(npdmFile).AsStream());
}
else
{
Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
metaData = GetDefaultNpdm();
}
List<IExecutable> staticObjects = new List<IExecutable>();
void LoadNso(string searchPattern)
{
PfsFileEntry entry = nsp.Files.FirstOrDefault(x => x.Name.Equals(searchPattern));
if (entry != null)
{
Logger.PrintInfo(LogClass.Loader, $"Loading {entry.Name}...");
NxStaticObject staticObject = new NxStaticObject(nsp.OpenFile(entry).AsStream());
staticObjects.Add(staticObject);
}
}
CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
LoadNso("rtld");
LoadNso("main");
LoadNso("subsdk*");
LoadNso("sdk");
ContentManager.LoadEntries();
if (staticObjects.Count == 0)
{
Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file");
}
else
{
ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray());
}
}
public void LoadNca(Nca mainNca, Nca controlNca)
@ -488,11 +533,6 @@ namespace Ryujinx.HLE.HOS
CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
}
if (!metaData.Is64Bits)
{
throw new NotImplementedException("32-bit titles are not supported!");
}
LoadNso("rtld");
LoadNso("main");
LoadNso("subsdk");

View file

@ -78,6 +78,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
}
}
//TODO: ARM32.
long framePointer = (long)threadState.X29;
while (framePointer != 0)
@ -245,6 +246,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
long ehHdrEndOffset = memory.ReadInt32(mod0Offset + 0x14) + mod0Offset;
long modObjOffset = memory.ReadInt32(mod0Offset + 0x18) + mod0Offset;
//TODO: Elf32.
while (true)
{
long tagVal = memory.ReadInt64(dynamicOffset + 0);

View file

@ -3,6 +3,7 @@ using ChocolArm64.Events;
using ChocolArm64.Memory;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
@ -797,6 +798,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
context.ThreadState.Interrupt += InterruptHandler;
context.ThreadState.SvcCall += _svcHandler.SvcCall;
context.ThreadState.Undefined += UndefinedInstructionHandler;
}
private void InterruptHandler(object sender, EventArgs e)
@ -1021,5 +1023,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}.");
}
private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
{
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
}
}
}

View file

@ -152,8 +152,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
Context = new CpuThread(owner.Translator, owner.CpuMemory, (long)entrypoint);
bool isAarch32 = (Owner.MmuFlags & 1) == 0;
Context.ThreadState.Aarch32 = isAarch32;
Context.ThreadState.X0 = argsPtr;
Context.ThreadState.X31 = stackTop;
if (isAarch32)
{
Context.ThreadState.X13 = (uint)stackTop;
}
else
{
Context.ThreadState.X31 = stackTop;
}
Context.ThreadState.CntfrqEl0 = 19200000;
Context.ThreadState.Tpidr = (long)_tlsAddress;

View file

@ -125,9 +125,14 @@ namespace Ryujinx.HLE.HOS
IExecutable[] staticObjects,
byte[] arguments = null)
{
if (!metaData.Is64Bits)
{
Logger.PrintWarning(LogClass.Loader, "32-bits application detected!");
}
ulong argsStart = 0;
int argsSize = 0;
ulong codeStart = 0x8000000;
ulong codeStart = metaData.Is64Bits ? 0x8000000UL : 0x200000UL;
int codeSize = 0;
ulong[] nsoBase = new ulong[staticObjects.Length];

View file

@ -45,6 +45,32 @@ namespace Ryujinx.Tests.Cpu
0x0F808000u // MUL V0.2S, V0.2S, V0.S[0]
};
}
private static uint[] _SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_()
{
return new uint[]
{
0x0F402000u, // SMLAL V0.4S, V0.4H, V0.H[0]
0x0F406000u, // SMLSL V0.4S, V0.4H, V0.H[0]
0x0F40A000u, // SMULL V0.4S, V0.4H, V0.H[0]
0x2F402000u, // UMLAL V0.4S, V0.4H, V0.H[0]
0x2F406000u, // UMLSL V0.4S, V0.4H, V0.H[0]
0x2F40A000u // UMULL V0.4S, V0.4H, V0.H[0]
};
}
private static uint[] _SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_()
{
return new uint[]
{
0x0F802000u, // SMLAL V0.2D, V0.2S, V0.S[0]
0x0F806000u, // SMLSL V0.2D, V0.2S, V0.S[0]
0x0F80A000u, // SMULL V0.2D, V0.2S, V0.S[0]
0x2F802000u, // UMLAL V0.2D, V0.2S, V0.S[0]
0x2F806000u, // UMLSL V0.2D, V0.2S, V0.S[0]
0x2F80A000u // UMULL V0.2D, V0.2S, V0.S[0]
};
}
#endregion
private const int RndCnt = 2;
@ -103,6 +129,61 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
[Test, Pairwise]
public void SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S([ValueSource("_SU_Mlal_Mlsl_Mull_Ve_4H4S_8H4S_")] uint opcodes,
[Values(0u)] uint rd,
[Values(1u, 0u)] uint rn,
[Values(2u, 0u)] uint rm,
[ValueSource("_4H_")] [Random(RndCnt)] ulong z,
[ValueSource("_4H_")] [Random(RndCnt)] ulong a,
[ValueSource("_4H_")] [Random(RndCnt)] ulong b,
[Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint index,
[Values(0b0u, 0b1u)] uint q) // <4H4S, 8H4S>
{
uint h = (index >> 2) & 1;
uint l = (index >> 1) & 1;
uint m = index & 1;
opcodes |= ((rm & 15) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
opcodes |= (l << 21) | (m << 20) | (h << 11);
opcodes |= ((q & 1) << 30);
Vector128<float> v0 = MakeVectorE0E1(z, z);
Vector128<float> v1 = MakeVectorE0E1(q == 0u ? a : 0ul, q == 1u ? a : 0ul);
Vector128<float> v2 = MakeVectorE0E1(b, b * h);
SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn();
}
[Test, Pairwise]
public void SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D([ValueSource("_SU_Mlal_Mlsl_Mull_Ve_2S2D_4S2D_")] uint opcodes,
[Values(0u)] uint rd,
[Values(1u, 0u)] uint rn,
[Values(2u, 0u)] uint rm,
[ValueSource("_2S_")] [Random(RndCnt)] ulong z,
[ValueSource("_2S_")] [Random(RndCnt)] ulong a,
[ValueSource("_2S_")] [Random(RndCnt)] ulong b,
[Values(0u, 1u, 2u, 3u)] uint index,
[Values(0b0u, 0b1u)] uint q) // <2S2D, 4S2D>
{
uint h = (index >> 1) & 1;
uint l = index & 1;
opcodes |= ((rm & 15) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0);
opcodes |= (l << 21) | (h << 11);
opcodes |= ((q & 1) << 30);
Vector128<float> v0 = MakeVectorE0E1(z, z);
Vector128<float> v1 = MakeVectorE0E1(q == 0u ? a : 0ul, q == 1u ? a : 0ul);
Vector128<float> v2 = MakeVectorE0E1(b, b * h);
SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn();
}
#endif
}
}

View file

@ -78,6 +78,8 @@ namespace Ryujinx
MaxLevel = Convert.ToInt32(parser.Value("Profiling_Max_Level")),
});
Logger.EnableFileLog = Convert.ToBoolean(parser.Value("Enable_File_Log"));
SystemLanguage SetLanguage = Enum.Parse<SystemLanguage>(parser.Value("System_Language"));
device.System.State.SetLanguage(SetLanguage);
@ -134,7 +136,6 @@ namespace Ryujinx
});
NpadController = new NpadController(
Convert.ToBoolean(parser.Value("GamePad_Enable")),
Convert.ToInt32 (parser.Value("GamePad_Index")),
(float)Convert.ToDouble (parser.Value("GamePad_Deadzone"), CultureInfo.InvariantCulture),

View file

@ -23,7 +23,10 @@ namespace Ryujinx
Config.Read(device);
Logger.Updated += ConsoleLog.Log;
Logger.Updated += Log.LogMessage;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
if (args.Length == 1)
{
@ -62,6 +65,7 @@ namespace Ryujinx
device.LoadNca(args[0]);
break;
case ".nsp":
case ".pfs0":
Console.WriteLine("Loading as NSP.");
device.LoadNsp(args[0]);
break;
@ -89,6 +93,23 @@ namespace Ryujinx
audioOut.Dispose();
}
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
Log.Close();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
if (e.IsTerminating)
{
Log.Close();
}
}
/// <summary>
/// Picks an <see cref="IAalOutput"/> audio output renderer supported on this machine
/// </summary>

View file

@ -37,6 +37,9 @@ Profiling_History = 5
#Set the maximum profiling level. Higher values may cause a heavy load on your system but will allow you to profile in more detail.
Profiling_Max_Level = 0
#Enable file logging
Enable_File_Log = false
#System Language list: https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b
#Change System Language
System_Language = AmericanEnglish

View file

@ -1,22 +1,27 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
namespace Ryujinx
{
static class ConsoleLog
static class Log
{
private static readonly string _path;
private static StreamWriter _logWriter;
private static Thread _messageThread;
private static BlockingCollection<LogEventArgs> _messageQueue;
private static Dictionary<LogLevel, ConsoleColor> _logColors;
static ConsoleLog()
static Log()
{
_logColors = new Dictionary<LogLevel, ConsoleColor>()
{
@ -47,6 +52,13 @@ namespace Ryujinx
}
});
_path = Path.Combine(Environment.CurrentDirectory, "Ryujinx.log");
if (Logger.EnableFileLog)
{
_logWriter = new StreamWriter(File.Open(_path,FileMode.Create, FileAccess.Write));
}
_messageThread.IsBackground = true;
_messageThread.Start();
}
@ -82,26 +94,47 @@ namespace Ryujinx
}
}
string message = sb.ToString();
if (_logColors.TryGetValue(e.Level, out ConsoleColor color))
{
Console.ForegroundColor = color;
Console.WriteLine(sb.ToString());
Console.WriteLine(message);
Console.ResetColor();
}
else
{
Console.WriteLine(sb.ToString());
Console.WriteLine(message);
}
if (Logger.EnableFileLog)
{
_logWriter.WriteLine(message);
}
}
public static void Log(object sender, LogEventArgs e)
public static void LogMessage(object sender, LogEventArgs e)
{
if (!_messageQueue.IsAddingCompleted)
{
_messageQueue.Add(e);
}
}
public static void Close()
{
_messageQueue.CompleteAdding();
_messageThread.Join();
if (Logger.EnableFileLog)
{
_logWriter.Flush();
_logWriter.Close();
_logWriter.Dispose();
}
}
}
}
}