Add most of the A32 instruction set to ARMeilleure (#897)

* Implement TEQ and MOV (Imm16)

* Initial work on A32 instructions + SVC. No tests yet, hangs in rtld.

* Implement CLZ, fix BFI and BFC

Now stops on SIMD initialization.

* Exclusive access instructions, fix to mul, system instructions.

Now gets to a break after SignalProcessWideKey64.

* Better impl of UBFX, add UDIV and SDIV

Now boots way further - now stuck on VMOV instruction.

* Many more instructions, start on SIMD and testing framework.

* Fix build issues

* svc: Rework 32 bit codepath

Fixing once and for all argument ordering issues.

* Fix 32 bits stacktrace

* hle debug: Add 32 bits dynamic section parsing

* Fix highCq mode, add many tests, fix some instruction bugs

Still suffers from critical malloc failure 😩

* Fix incorrect opcode decoders and a few more instructions.

* Add a few instructions and fix others. re-disable highCq for now.

Disabled the svc memory clear since i'm not sure about it.

* Fix build

* Fix typo in ordered/exclusive stores.

* Implement some more instructions, fix others.

Uxtab16/Sxtab16 are untested.

* Begin impl of pairwise, some other instructions.

* Add a few more instructions, a quick hack to fix svcs for now.

* Add tests and fix issues with VTRN, VZIP, VUZP

* Add a few more instructions, fix Vmul_1 encoding.

* Fix way too many instruction bugs, add tests for some of the more important ones.

* Fix HighCq, enable FastFP paths for some floating point instructions

(not entirely sure why these were disabled, so important to note this
commit exists)

Branching has been removed in A32 shifts until I figure out if it's
worth it

* Cleanup Part 1

There should be no functional change between these next few commits.
Should is the key word. (except for removing break handler)

* Implement 32 bits syscalls

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

Implement all 32 bits counterparts of the 64 bits syscalls we currently
have.

* Refactor part 2: Move index/subindex logic to Operand

May have inadvertently fixed one (1) bug

* Add FlushProcessDataCache32

* Address jd's comments

* Remove 16 bit encodings from OpCodeTable

Still need to catch some edge cases (operands that use the "F" flag) and
make Q encodings with non-even indexes undefined.

* Correct Fpscr handling for FP vector slow paths

WIP

* Add StandardFPSCRValue behaviour for all Arithmetic instructions

* Add StandardFPSCRValue behaviour to compare instructions.

* Force passing of fpcr to FPProcessException and FPUnpack.

Reduces potential for code error significantly

* OpCode cleanup

* Remove urgency from DMB comment in MRRC

DMB is currently a no-op via the instruction, so it should likely still
be a no-op here.

* Test Cleanup

* Fix FPDefaultNaN on Ryzen CPUs

* Improve some tests, fix some shift instructions, add slow path for Vadd

* Fix Typo

* More test cleanup

* Flip order of Fx and index, to indicate that the operand's is the "base"

* Remove Simd32 register type, use Int32 and Int64 for scalars like A64 does.

* Reintroduce alignment to DecoderHelper (removed by accident)

* One more realign as reading diffs is hard

* Use I32 registers in A32 (part 2)

Swap default integer register type based on current execution mode.

* FPSCR flags as Registers (part 1)

Still need to change NativeContext and ExecutionContext to allow
getting/setting with the flag values.

* Use I32 registers in A32 (part 1)

* FPSCR flags as registers (part 2)

Only CMP flags are on the registers right now. It could be useful to use
more of the space in non-fast-float when implementing A32 flags
accurately in the fast path.

* Address Feedback

* Correct FP->Int behaviour (should saturate)

* Make branches made by writing to PC eligible for Rejit

Greatly improves performance in most games.

* Remove unused branching for Vtbl

* RejitRequest as a class rather than a tuple

Makes a lot more sense than storing tuples on a dictionary.

* Add VMOVN, VSHR (imm), VSHRN (imm) and related tests

* Re-order InstEmitSystem32

Alphabetical sorting.

* Address Feedback

Feedback from Ac_K, remove and sort usings.

* Address Feedback 2

* Address Feedback from LDj3SNuD

Opcode table reordered to have alphabetical sorting within groups,
Vmaxnm and Vminnm have split names to be less ambiguous, SoftFloat nits,
Test nits and Test simplification with ValueSource.

* Add Debug Asserts to A32 helpers

Mainly to prevent the shift ones from being used on I64 operands, as
they expect I32 input for most operations (eg. carry flag setting), and
expect I32 input for shift and boolean amounts. Most other helper
functions don't take Operands, throw on out of range values, and take
specific types of OpCode, so didn't need any asserts.

* Use ConstF rather than creating an operand.

(useful for pooling in future)

* Move exclusive load to helper, reference call flag rather than literal 1.

* Address LDj feedback (minus table flatten)

one final look before it's all gone. the world is so beautiful.

* Flatten OpCodeTable

oh no

* Address more table ordering

* Call Flag as int on A32

Co-authored-by: Natalie C. <cyuubiapps@gmail.com>
Co-authored-by: Thog <thog@protonmail.com>
This commit is contained in:
riperiperi 2020-02-23 21:20:40 +00:00 committed by GitHub
parent 165e658f02
commit b1b6f294f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
104 changed files with 9426 additions and 479 deletions

View file

@ -292,15 +292,16 @@ namespace ARMeilleure.Decoders
private static bool IsCall(OpCode opCode)
{
// TODO (CQ): ARM32 support.
return opCode.Instruction.Name == InstName.Bl ||
opCode.Instruction.Name == InstName.Blr;
opCode.Instruction.Name == InstName.Blr ||
opCode.Instruction.Name == InstName.Blx;
}
private static bool IsException(OpCode opCode)
{
return opCode.Instruction.Name == InstName.Brk ||
opCode.Instruction.Name == InstName.Svc ||
opCode.Instruction.Name == InstName.Trap ||
opCode.Instruction.Name == InstName.Und;
}

View file

@ -148,5 +148,20 @@ namespace ARMeilleure.Decoders
{
return (((long)opCode << 45) >> 48) & ~3;
}
public static bool VectorArgumentsInvalid(bool q, params int[] args)
{
if (q)
{
for (int i = 0; i < args.Length; i++)
{
if ((args[i] & 1) == 1)
{
return true;
}
}
}
return false;
}
}
}

View file

@ -0,0 +1,11 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32AluBf
{
int Rd { get; }
int Rn { get; }
int Msb { get; }
int Lsb { get; }
}
}

View file

@ -0,0 +1,7 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32AluReg : IOpCode32Alu
{
int Rm { get; }
}
}

View file

@ -0,0 +1,8 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32AluUx : IOpCode32AluReg
{
int RotateBits { get; }
bool Add { get; }
}
}

View file

@ -6,7 +6,6 @@ namespace ARMeilleure.Decoders
int Rn { get; }
bool WBack { get; }
bool IsLoad { get; }
}
}

View file

@ -0,0 +1,7 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32MemEx : IOpCode32Mem
{
int Rd { get; }
}
}

View file

@ -0,0 +1,4 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32Simd : IOpCode32, IOpCodeSimd { }
}

View file

@ -0,0 +1,9 @@
namespace ARMeilleure.Decoders
{
interface IOpCode32SimdImm : IOpCode32Simd
{
int Vd { get; }
long Immediate { get; }
int Elems { get; }
}
}

View file

@ -0,0 +1,24 @@
namespace ARMeilleure.Decoders
{
class OpCode32AluBf : OpCode32, IOpCode32AluBf
{
public int Rd { get; private set; }
public int Rn { get; private set; }
public int Msb { get; private set; }
public int Lsb { get; private set; }
public int SourceMask => (int)(0xFFFFFFFF >> (31 - Msb));
public int DestMask => SourceMask & (int)(0xFFFFFFFF << Lsb);
public OpCode32AluBf(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 12) & 0xf;
Rn = (opCode >> 0) & 0xf;
Msb = (opCode >> 16) & 0x1f;
Lsb = (opCode >> 7) & 0x1f;
}
}
}

View file

@ -0,0 +1,15 @@
namespace ARMeilleure.Decoders
{
class OpCode32AluImm16 : OpCode32Alu
{
public int Immediate { get; private set; }
public OpCode32AluImm16(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
int imm12 = opCode & 0xfff;
int imm4 = (opCode >> 16) & 0xf;
Immediate = (imm4 << 12) | imm12;
}
}
}

View file

@ -0,0 +1,28 @@
namespace ARMeilleure.Decoders
{
class OpCode32AluMla : OpCode32, IOpCode32AluReg
{
public int Rn { get; private set; }
public int Rm { get; private set; }
public int Ra { get; private set; }
public int Rd { get; private set; }
public bool NHigh { get; private set; }
public bool MHigh { get; private set; }
public bool R { get; private set; }
public bool SetFlags { get; private set; }
public OpCode32AluMla(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rn = (opCode >> 0) & 0xf;
Rm = (opCode >> 8) & 0xf;
Ra = (opCode >> 12) & 0xf;
Rd = (opCode >> 16) & 0xf;
R = (opCode & (1 << 5)) != 0;
NHigh = ((opCode >> 5) & 0x1) == 1;
MHigh = ((opCode >> 6) & 0x1) == 1;
SetFlags = ((opCode >> 20) & 1) != 0;
}
}
}

View file

@ -0,0 +1,12 @@
namespace ARMeilleure.Decoders
{
class OpCode32AluReg : OpCode32Alu, IOpCode32AluReg
{
public int Rm { get; private set; }
public OpCode32AluReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rm = (opCode >> 0) & 0xf;
}
}
}

View file

@ -3,14 +3,14 @@ namespace ARMeilleure.Decoders
class OpCode32AluRsImm : OpCode32Alu
{
public int Rm { get; private set; }
public int Imm { get; private set; }
public int Immediate { get; private set; }
public ShiftType ShiftType { get; private set; }
public OpCode32AluRsImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rm = (opCode >> 0) & 0xf;
Imm = (opCode >> 7) & 0x1f;
Immediate = (opCode >> 7) & 0x1f;
ShiftType = (ShiftType)((opCode >> 5) & 3);
}

View file

@ -0,0 +1,18 @@
namespace ARMeilleure.Decoders
{
class OpCode32AluRsReg : OpCode32Alu
{
public int Rm { get; private set; }
public int Rs { get; private set; }
public ShiftType ShiftType { get; private set; }
public OpCode32AluRsReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rm = (opCode >> 0) & 0xf;
Rs = (opCode >> 8) & 0xf;
ShiftType = (ShiftType)((opCode >> 5) & 3);
}
}
}

View file

@ -0,0 +1,30 @@
namespace ARMeilleure.Decoders
{
class OpCode32AluUmull : OpCode32
{
public int RdLo { get; private set; }
public int RdHi { get; private set; }
public int Rn { get; private set; }
public int Rm { get; private set; }
public bool NHigh { get; private set; }
public bool MHigh { get; private set; }
public bool SetFlags { get; private set; }
public DataOp DataOp { get; private set; }
public OpCode32AluUmull(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
RdLo = (opCode >> 12) & 0xf;
RdHi = (opCode >> 16) & 0xf;
Rm = (opCode >> 8) & 0xf;
Rn = (opCode >> 0) & 0xf;
NHigh = ((opCode >> 5) & 0x1) == 1;
MHigh = ((opCode >> 6) & 0x1) == 1;
SetFlags = ((opCode >> 20) & 0x1) != 0;
DataOp = DataOp.Arithmetic;
}
}
}

View file

@ -0,0 +1,16 @@
using ARMeilleure.State;
namespace ARMeilleure.Decoders
{
class OpCode32AluUx : OpCode32AluReg, IOpCode32AluUx
{
public int Rotate { get; private set; }
public int RotateBits => Rotate * 8;
public bool Add => Rn != RegisterAlias.Aarch32Pc;
public OpCode32AluUx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rotate = (opCode >> 10) & 0x3;
}
}
}

View file

@ -0,0 +1,12 @@
namespace ARMeilleure.Decoders
{
class OpCode32Exception : OpCode32
{
public int Id { get; private set; }
public OpCode32Exception(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Id = opCode & 0xFFFFFF;
}
}
}

View file

@ -4,7 +4,7 @@ namespace ARMeilleure.Decoders
{
class OpCode32Mem : OpCode32, IOpCode32Mem
{
public int Rt { get; private set; }
public int Rt { get; protected set; }
public int Rn { get; private set; }
public int Immediate { get; protected set; }

View file

@ -0,0 +1,12 @@
namespace ARMeilleure.Decoders
{
class OpCode32MemLdEx : OpCode32Mem, IOpCode32MemEx
{
public int Rd { get; private set; }
public OpCode32MemLdEx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = opCode & 0xf;
}
}
}

View file

@ -0,0 +1,12 @@
namespace ARMeilleure.Decoders
{
class OpCode32MemReg : OpCode32Mem
{
public int Rm { get; private set; }
public OpCode32MemReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rm = (opCode >> 0) & 0xf;
}
}
}

View file

@ -0,0 +1,16 @@
namespace ARMeilleure.Decoders
{
class OpCode32MemRsImm : OpCode32Mem
{
public int Rm { get; private set; }
public ShiftType ShiftType { get; private set; }
public OpCode32MemRsImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rm = (opCode >> 0) & 0xf;
Immediate = (opCode >> 7) & 0x1f;
ShiftType = (ShiftType)((opCode >> 5) & 3);
}
}
}

View file

@ -0,0 +1,13 @@
namespace ARMeilleure.Decoders
{
class OpCode32MemStEx : OpCode32Mem, IOpCode32MemEx
{
public int Rd { get; private set; }
public OpCode32MemStEx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rd = (opCode >> 12) & 0xf;
Rt = (opCode >> 0) & 0xf;
}
}
}

View file

@ -0,0 +1,30 @@
namespace ARMeilleure.Decoders
{
class OpCode32Simd : OpCode32SimdBase
{
public int Opc { get; protected set; }
public bool Q { get; protected set; }
public bool F { get; protected set; }
public bool U { get; private set; }
public OpCode32Simd(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Size = (opCode >> 20) & 0x3;
Q = ((opCode >> 6) & 0x1) != 0;
F = ((opCode >> 10) & 0x1) != 0;
U = ((opCode >> 24) & 0x1) != 0;
Opc = (opCode >> 7) & 0x3;
RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64;
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
// Subclasses have their own handling of Vx to account for before checking.
if (GetType() == typeof(OpCode32Simd) && DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,52 @@
using System;
namespace ARMeilleure.Decoders
{
abstract class OpCode32SimdBase : OpCode32, IOpCode32Simd
{
public int Vd { get; protected set; }
public int Vm { get; protected set; }
public int Size { get; protected set; }
// Helpers to index doublewords within quad words. Essentially, looping over the vector starts at quadword Q and index Fx or Ix within it,
// depending on instruction type.
//
// Qx: The quadword register that the target vector is contained in.
// Ix: The starting index of the target vector within the quadword, with size treated as integer.
// Fx: The starting index of the target vector within the quadword, with size treated as floating point. (16 or 32)
public int Qd => GetQuadwordIndex(Vd);
public int Id => GetQuadwordSubindex(Vd) << (3 - Size);
public int Fd => GetQuadwordSubindex(Vd) << (1 - (Size & 1)); // When the top bit is truncated, 1 is fp16 which is an optional extension in ARMv8.2. We always assume 64.
public int Qm => GetQuadwordIndex(Vm);
public int Im => GetQuadwordSubindex(Vm) << (3 - Size);
public int Fm => GetQuadwordSubindex(Vm) << (1 - (Size & 1));
protected int GetQuadwordIndex(int index)
{
switch (RegisterSize)
{
case RegisterSize.Simd128:
case RegisterSize.Simd64:
return index >> 1;
}
throw new InvalidOperationException();
}
protected int GetQuadwordSubindex(int index)
{
switch (RegisterSize)
{
case RegisterSize.Simd128:
return 0;
case RegisterSize.Simd64:
return index & 1;
}
throw new InvalidOperationException();
}
public OpCode32SimdBase(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { }
}
}

View file

@ -0,0 +1,18 @@
namespace ARMeilleure.Decoders
{
/// <summary>
/// A special alias that always runs in 64 bit int, to speed up binary ops a little.
/// </summary>
class OpCode32SimdBinary : OpCode32SimdReg
{
public OpCode32SimdBinary(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Size = 3;
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm, Vn))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,15 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdCmpZ : OpCode32Simd
{
public OpCode32SimdCmpZ(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Size = (opCode >> 18) & 0x3;
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,13 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdCvtFI : OpCode32SimdS
{
public int Opc2 { get; private set; }
public OpCode32SimdCvtFI(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Opc2 = (opCode >> 16) & 0x7;
Opc = (opCode >> 7) & 0x1;
}
}
}

View file

@ -0,0 +1,40 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdDupElem : OpCode32Simd
{
public int Index { get; private set; }
public OpCode32SimdDupElem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
var opc = (opCode >> 16) & 0xf;
if ((opc & 0b1) == 1)
{
Size = 0;
Index = (opc >> 1) & 0x7;
}
else if ((opc & 0b11) == 0b10)
{
Size = 1;
Index = (opc >> 2) & 0x3;
}
else if ((opc & 0b111) == 0b100)
{
Size = 2;
Index = (opc >> 3) & 0x1;
}
else
{
Instruction = InstDescriptor.Undefined;
}
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,31 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdDupGP : OpCode32, IOpCode32Simd
{
public int Size { get; private set; }
public int Vd { get; private set; }
public int Rt { get; private set; }
public bool Q { get; private set; }
public OpCode32SimdDupGP(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Size = 2 - (((opCode >> 21) & 0x2) | ((opCode >> 5) & 0x1)); // B:E - 0 for 32, 16 then 8.
if (Size == -1)
{
Instruction = InstDescriptor.Undefined;
return;
}
Q = ((opCode >> 21) & 0x1) != 0;
RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64;
Vd = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf);
Rt = ((opCode >> 12) & 0xf);
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,17 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdExt : OpCode32SimdReg
{
public int Immediate { get; private set; }
public OpCode32SimdExt(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Immediate = (opCode >> 8) & 0xf;
Size = 0;
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm, Vn) || (!Q && Immediate > 7))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,37 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdImm : OpCode32, IOpCode32SimdImm
{
public int Vd { get; private set; }
public bool Q { get; private set; }
public long Immediate { get; private set; }
public int Size { get; private set; }
public int Elems => GetBytesCount() >> Size;
public OpCode32SimdImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Vd = (opCode >> 12) & 0xf;
Vd |= (opCode >> 18) & 0x10;
Q = ((opCode >> 6) & 0x1) > 0;
int cMode = (opCode >> 8) & 0xf;
int op = (opCode >> 5) & 0x1;
long imm;
imm = ((uint)opCode >> 0) & 0xf;
imm |= ((uint)opCode >> 12) & 0x70;
imm |= ((uint)opCode >> 17) & 0x80;
(Immediate, Size) = OpCodeSimdHelper.GetSimdImmediateAndSize(cMode, op, imm, fpBaseSize: 2);
RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64;
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,36 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdImm44 : OpCode32, IOpCode32SimdImm
{
public int Vd { get; private set; }
public long Immediate { get; private set; }
public int Size { get; private set; }
public int Elems { get; private set; }
public OpCode32SimdImm44(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Size = (opCode >> 8) & 0x3;
bool single = Size != 3;
if (single)
{
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
}
else
{
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
}
long imm;
imm = ((uint)opCode >> 0) & 0xf;
imm |= ((uint)opCode >> 12) & 0xf0;
Immediate = (Size == 3) ? (long)DecoderHelper.Imm8ToFP64Table[(int)imm] : DecoderHelper.Imm8ToFP32Table[(int)imm];
RegisterSize = (!single) ? RegisterSize.Int64 : RegisterSize.Int32;
Elems = 1;
}
}
}

View file

@ -0,0 +1,35 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdMemImm : OpCode32, IOpCode32Simd
{
public int Vd { get; private set; }
public int Rn { get; private set; }
public int Size { get; private set; }
public bool Add { get; private set; }
public int Immediate { get; private set; }
public OpCode32SimdMemImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Immediate = opCode & 0xff;
Rn = (opCode >> 16) & 0xf;
Size = (opCode >> 8) & 0x3;
Immediate <<= (Size == 1) ? 1 : 2;
bool u = (opCode & (1 << 23)) != 0;
Add = u;
bool single = Size != 3;
if (single)
{
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
}
else
{
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
}
}
}
}

View file

@ -0,0 +1,71 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdMemMult : OpCode32
{
public int Rn { get; private set; }
public int Vd { get; private set; }
public int RegisterRange { get; private set; }
public int Offset { get; private set; }
public int PostOffset { get; private set; }
public bool IsLoad { get; private set; }
public bool DoubleWidth { get; private set; }
public bool Add { get; private set; }
public OpCode32SimdMemMult(InstDescriptor inst, ulong address, int opCode) : base(inst, address, 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;
if (p == u && w)
{
Instruction = InstDescriptor.Undefined;
return;
}
DoubleWidth = (opCode & (1 << 8)) != 0;
if (!DoubleWidth)
{
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
}
else
{
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
}
Add = u;
RegisterRange = opCode & 0xff;
int regsSize = RegisterRange * 4; // Double mode is still measured in single register size.
if (!u)
{
Offset -= regsSize;
}
if (w)
{
PostOffset = u ? regsSize : -regsSize;
}
else
{
PostOffset = 0;
}
IsLoad = isLoad;
int regs = DoubleWidth ? RegisterRange / 2 : RegisterRange;
if (RegisterRange == 0 || RegisterRange > 32 || Vd + regs > 32)
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,46 @@
using ARMeilleure.State;
using System;
namespace ARMeilleure.Decoders
{
class OpCode32SimdMemPair : OpCode32, IOpCode32Simd
{
private static int[] RegsMap =
{
1, 1, 4, 2,
1, 1, 3, 1,
1, 1, 2, 1,
1, 1, 1, 1
};
public int Vd { get; private set; }
public int Rn { get; private set; }
public int Rm { get; private set; }
public int Align { get; private set; }
public bool WBack { get; private set; }
public bool RegisterIndex { get; private set; }
public int Size { get; private set; }
public int Elems => 8 >> Size;
public int Regs { get; private set; }
public int Increment { get; private set; }
public OpCode32SimdMemPair(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Vd = (opCode >> 12) & 0xf;
Vd |= (opCode >> 18) & 0x10;
Size = (opCode >> 6) & 0x3;
Align = (opCode >> 4) & 0x3;
Rm = (opCode >> 0) & 0xf;
Rn = (opCode >> 16) & 0xf;
WBack = Rm != RegisterAlias.Aarch32Pc;
RegisterIndex = Rm != RegisterAlias.Aarch32Pc && Rm != RegisterAlias.Aarch32Sp;
Regs = RegsMap[(opCode >> 8) & 0xf];
Increment = Math.Min(Regs, ((opCode >> 8) & 0x1) + 1);
}
}
}

View file

@ -0,0 +1,46 @@
using ARMeilleure.State;
namespace ARMeilleure.Decoders
{
class OpCode32SimdMemSingle : OpCode32, IOpCode32Simd
{
public int Vd { get; private set; }
public int Rn { get; private set; }
public int Rm { get; private set; }
public int IndexAlign { get; private set; }
public int Index { get; private set; }
public bool WBack { get; private set; }
public bool RegisterIndex { get; private set; }
public int Size { get; private set; }
public bool Replicate { get; private set; }
public int Increment { get; private set; }
public OpCode32SimdMemSingle(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Vd = (opCode >> 12) & 0xf;
Vd |= (opCode >> 18) & 0x10;
IndexAlign = (opCode >> 4) & 0xf;
Size = (opCode >> 10) & 0x3;
Replicate = Size == 3;
if (Replicate)
{
Size = (opCode >> 6) & 0x3;
Increment = ((opCode >> 5) & 1) + 1;
Index = 0;
}
else
{
Increment = (((IndexAlign >> Size) & 1) == 0) ? 1 : 2;
Index = IndexAlign >> (1 + Size);
}
Rm = (opCode >> 0) & 0xf;
Rn = (opCode >> 16) & 0xf;
WBack = Rm != RegisterAlias.Aarch32Pc;
RegisterIndex = Rm != RegisterAlias.Aarch32Pc && Rm != RegisterAlias.Aarch32Sp;
}
}
}

View file

@ -0,0 +1,26 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdMovGp : OpCode32, IOpCode32Simd
{
public int Size => 2;
public int Vn { get; private set; }
public int Rt { get; private set; }
public int Op { get; private set; }
public int Opc1 { get; private set; }
public int Opc2 { get; private set; }
public OpCode32SimdMovGp(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
// Which one is used is instruction dependant.
Op = (opCode >> 20) & 0x1;
Opc1 = (opCode >> 21) & 0x3;
Opc2 = (opCode >> 5) & 0x3;
Vn = ((opCode >> 7) & 0x1) | ((opCode >> 15) & 0x1e);
Rt = (opCode >> 12) & 0xf;
}
}
}

View file

@ -0,0 +1,31 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdMovGpDouble : OpCode32, IOpCode32Simd
{
public int Size => 3;
public int Vm { get; private set; }
public int Rt { get; private set; }
public int Rt2 { get; private set; }
public int Op { get; private set; }
public OpCode32SimdMovGpDouble(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
// Which one is used is instruction dependant.
Op = (opCode >> 20) & 0x1;
Rt = (opCode >> 12) & 0xf;
Rt2 = (opCode >> 16) & 0xf;
bool single = (opCode & (1 << 8)) == 0;
if (single)
{
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
}
else
{
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
}
}
}
}

View file

@ -0,0 +1,46 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdMovGpElem : OpCode32, IOpCode32Simd
{
public int Size { get; private set; }
public int Vd { get; private set; }
public int Rt { get; private set; }
public int Op { get; private set; }
public bool U { get; private set; }
public int Index { get; private set; }
public OpCode32SimdMovGpElem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Op = (opCode >> 20) & 0x1;
U = ((opCode >> 23) & 1) != 0;
var opc = (((opCode >> 23) & 1) << 4) | (((opCode >> 21) & 0x3) << 2) | ((opCode >> 5) & 0x3);
if ((opc & 0b01000) == 0b01000)
{
Size = 0;
Index = opc & 0x7;
}
else if ((opc & 0b01001) == 0b00001)
{
Size = 1;
Index = (opc >> 1) & 0x3;
}
else if ((opc & 0b11011) == 0)
{
Size = 2;
Index = (opc >> 2) & 0x1;
}
else
{
Instruction = InstDescriptor.Undefined;
return;
}
Vd = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf);
Rt = (opCode >> 12) & 0xf;
}
}
}

View file

@ -0,0 +1,22 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdReg : OpCode32Simd
{
public int Vn { get; private set; }
public int Qn => GetQuadwordIndex(Vn);
public int In => GetQuadwordSubindex(Vn) << (3 - Size);
public int Fn => GetQuadwordSubindex(Vn) << (1 - (Size & 1));
public OpCode32SimdReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Vn = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf);
// Subclasses have their own handling of Vx to account for before checking.
if (GetType() == typeof(OpCode32SimdReg) && DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm, Vn))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,21 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdRegElem : OpCode32SimdReg
{
public OpCode32SimdRegElem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Q = ((opCode >> 24) & 0x1) != 0;
F = ((opCode >> 8) & 0x1) != 0;
Size = ((opCode >> 20) & 0x3);
RegisterSize = Q ? RegisterSize.Simd128 : RegisterSize.Simd64;
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vn) || Size == 0 || (Size == 1 && F))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,20 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdRegS : OpCode32SimdS
{
public int Vn { get; private set; }
public OpCode32SimdRegS(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
bool single = Size != 3;
if (single)
{
Vn = ((opCode >> 7) & 0x1) | ((opCode >> 15) & 0x1e);
}
else
{
Vn = ((opCode >> 3) & 0x10) | ((opCode >> 16) & 0xf);
}
}
}
}

View file

@ -0,0 +1,14 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdRev : OpCode32SimdCmpZ
{
public OpCode32SimdRev(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
// Currently, this instruction is treated as though it's OPCODE is the true size,
// which lets us deal with reversing vectors on a single element basis (eg. math magic an I64 rather than insert lots of I8s).
int tempSize = Size;
Size = 3 - Opc; // Op 0 is 64 bit, 1 is 32 and so on.
Opc = tempSize;
}
}
}

View file

@ -0,0 +1,31 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdS : OpCode32, IOpCode32Simd
{
public int Vd { get; private set; }
public int Vm { get; private set; }
public int Opc { get; protected set; }
public int Size { get; protected set; }
public OpCode32SimdS(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Opc = (opCode >> 15) & 0x3;
Size = (opCode >> 8) & 0x3;
bool single = Size != 3;
RegisterSize = single ? RegisterSize.Int32 : RegisterSize.Int64;
if (single)
{
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
}
else
{
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
}
}
}
}

View file

@ -0,0 +1,20 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdSel : OpCode32SimdRegS
{
public OpCode32SimdSelMode Cc { get; private set; }
public OpCode32SimdSel(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Cc = (OpCode32SimdSelMode)((opCode >> 20) & 3);
}
}
enum OpCode32SimdSelMode : int
{
Eq = 0,
Vs,
Ge,
Gt
}
}

View file

@ -0,0 +1,44 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdShImm : OpCode32Simd
{
public int Immediate { get; private set; }
public int Shift { get; private set; }
public OpCode32SimdShImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Immediate = (opCode >> 16) & 0x3f;
var limm = ((opCode >> 1) & 0x40) | Immediate;
if ((limm & 0x40) == 0b1000000)
{
Size = 3;
Shift = Immediate;
}
else if ((limm & 0x60) == 0b0100000)
{
Size = 2;
Shift = Immediate - 32;
}
else if ((limm & 0x70) == 0b0010000)
{
Size = 1;
Shift = Immediate - 16;
}
else if ((limm & 0x78) == 0b0001000)
{
Size = 0;
Shift = Immediate - 8;
}
else
{
Instruction = InstDescriptor.Undefined;
}
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,14 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdSpecial : OpCode32
{
public int Rt { get; private set; }
public int Sreg { get; private set; }
public OpCode32SimdSpecial(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Rt = (opCode >> 12) & 0xf;
Sreg = (opCode >> 16) & 0xf;
}
}
}

View file

@ -0,0 +1,16 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdSqrte : OpCode32Simd
{
public OpCode32SimdSqrte(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Size = (opCode >> 18) & 0x1;
F = ((opCode >> 8) & 0x1) != 0;
if (DecoderHelper.VectorArgumentsInvalid(Q, Vd, Vm))
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,21 @@
namespace ARMeilleure.Decoders
{
class OpCode32SimdTbl : OpCode32SimdReg
{
public int Length { get; private set; }
public OpCode32SimdTbl(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Length = (opCode >> 8) & 3;
Size = 0;
Opc = Q ? 1 : 0;
Q = false;
RegisterSize = RegisterSize.Simd64;
if (Vn + Length + 1 > 32)
{
Instruction = InstDescriptor.Undefined;
}
}
}
}

View file

@ -0,0 +1,26 @@
namespace ARMeilleure.Decoders
{
class OpCode32System : OpCode32
{
public int Opc1 { get; private set; }
public int CRn { get; private set; }
public int Rt { get; private set; }
public int Opc2 { get; private set; }
public int CRm { get; private set; }
public int MrrcOp { get; private set; }
public int Coproc { get; private set; }
public OpCode32System(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
Opc1 = (opCode >> 21) & 0x7;
CRn = (opCode >> 16) & 0xf;
Rt = (opCode >> 12) & 0xf;
Opc2 = (opCode >> 5) & 0x7;
CRm = (opCode >> 0) & 0xf;
MrrcOp = (opCode >> 4) & 0xf;
Coproc = (opCode >> 8) & 0xf;
}
}
}

View file

@ -0,0 +1,88 @@
namespace ARMeilleure.Decoders
{
public static class OpCodeSimdHelper
{
public static (long Immediate, int Size) GetSimdImmediateAndSize(int cMode, int op, long imm, int fpBaseSize = 0)
{
int modeLow = cMode & 1;
int modeHigh = cMode >> 1;
int size = 0;
if (modeHigh == 0b111)
{
switch (op | (modeLow << 1))
{
case 0:
// 64-bits Immediate.
// Transform abcd efgh into abcd efgh abcd efgh ...
size = 3;
imm = (long)((ulong)imm * 0x0101010101010101);
break;
case 1:
// 64-bits Immediate.
// Transform abcd efgh into aaaa aaaa bbbb bbbb ...
size = 3;
imm = (imm & 0xf0) >> 4 | (imm & 0x0f) << 4;
imm = (imm & 0xcc) >> 2 | (imm & 0x33) << 2;
imm = (imm & 0xaa) >> 1 | (imm & 0x55) << 1;
imm = (long)((ulong)imm * 0x8040201008040201);
imm = (long)((ulong)imm & 0x8080808080808080);
imm |= imm >> 4;
imm |= imm >> 2;
imm |= imm >> 1;
break;
case 2:
// 2 x 32-bits floating point Immediate.
size = 3;
imm = (long)DecoderHelper.Imm8ToFP32Table[(int)imm];
imm |= imm << 32;
break;
case 3:
// 64-bits floating point Immediate.
size = 3;
imm = (long)DecoderHelper.Imm8ToFP64Table[(int)imm];
break;
}
}
else if ((modeHigh & 0b110) == 0b100)
{
// 16-bits shifted Immediate.
size = 1; imm <<= (modeHigh & 1) << 3;
}
else if ((modeHigh & 0b100) == 0b000)
{
// 32-bits shifted Immediate.
size = 2; imm <<= modeHigh << 3;
}
else if ((modeHigh & 0b111) == 0b110)
{
// 32-bits shifted Immediate (fill with ones).
size = 2; imm = ShlOnes(imm, 8 << modeLow);
}
else
{
// 8-bits without shift.
size = 0;
}
return (imm, size);
}
private static long ShlOnes(long value, int shift)
{
if (shift != 0)
{
return value << shift | (long)(ulong.MaxValue >> (64 - shift));
}
else
{
return value;
}
}
}
}

View file

@ -599,32 +599,283 @@ namespace ARMeilleure.Decoders
#region "OpCode Table (AArch32)"
// Base
SetA32("<<<<0010101xxxxxxxxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, typeof(OpCode32AluImm));
SetA32("<<<<0000101xxxxxxxxxxxxxxxx0xxxx", InstName.Adc, InstEmit32.Adc, typeof(OpCode32AluRsImm));
SetA32("<<<<0000101xxxxxxxxxxxxx0xx1xxxx", InstName.Adc, InstEmit32.Adc, typeof(OpCode32AluRsReg));
SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluImm));
SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluRsImm));
SetA32("<<<<0000100xxxxxxxxxxxxx0xx1xxxx", InstName.Add, InstEmit32.Add, typeof(OpCode32AluRsReg));
SetA32("<<<<0010000xxxxxxxxxxxxxxxxxxxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluImm));
SetA32("<<<<0000000xxxxxxxxxxxxxxxx0xxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluRsImm));
SetA32("<<<<0000000xxxxxxxxxxxxx0xx1xxxx", InstName.And, InstEmit32.And, typeof(OpCode32AluRsReg));
SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstName.B, InstEmit32.B, typeof(OpCode32BImm));
SetA32("<<<<0111110xxxxxxxxxxxxxx0011111", InstName.Bfc, InstEmit32.Bfc, typeof(OpCode32AluBf));
SetA32("<<<<0111110xxxxxxxxxxxxxx001xxxx", InstName.Bfi, InstEmit32.Bfi, typeof(OpCode32AluBf));
SetA32("<<<<0011110xxxxxxxxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluImm));
SetA32("<<<<0001110xxxxxxxxxxxxxxxx0xxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluRsImm));
SetA32("<<<<0001110xxxxxxxxxxxxx0xx1xxxx", InstName.Bic, InstEmit32.Bic, typeof(OpCode32AluRsReg));
SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Bl, InstEmit32.Bl, typeof(OpCode32BImm));
SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstName.Blx, InstEmit32.Blx, typeof(OpCode32BImm));
SetA32("<<<<000100101111111111110011xxxx", InstName.Blx, InstEmit32.Blxr, typeof(OpCode32BReg));
SetA32("<<<<000100101111111111110001xxxx", InstName.Bx, InstEmit32.Bx, typeof(OpCode32BReg));
SetT32("xxxxxxxxxxxxxxxx010001110xxxx000", InstName.Bx, InstEmit32.Bx, typeof(OpCodeT16BReg));
SetA32("11110101011111111111000000011111", InstName.Clrex, InstEmit32.Clrex, typeof(OpCode32));
SetA32("<<<<000101101111xxxx11110001xxxx", InstName.Clz, InstEmit32.Clz, typeof(OpCode32AluReg));
SetA32("<<<<00110111xxxx0000xxxxxxxxxxxx", InstName.Cmn, InstEmit32.Cmn, typeof(OpCode32AluImm));
SetA32("<<<<00010111xxxx0000xxxxxxx0xxxx", InstName.Cmn, InstEmit32.Cmn, typeof(OpCode32AluRsImm));
SetA32("<<<<00110101xxxx0000xxxxxxxxxxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluImm));
SetA32("<<<<00010101xxxx0000xxxxxxx0xxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluRsImm));
SetA32("<<<<00010101xxxx0000xxxx0xx1xxxx", InstName.Cmp, InstEmit32.Cmp, typeof(OpCode32AluRsReg));
SetA32("1111010101111111111100000101xxxx", InstName.Dmb, InstEmit32.Dmb, typeof(OpCode32));
SetA32("1111010101111111111100000100xxxx", InstName.Dsb, InstEmit32.Dsb, typeof(OpCode32));
SetA32("<<<<0010001xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluImm));
SetA32("<<<<0000001xxxxxxxxxxxxxxxx0xxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluRsImm));
SetA32("<<<<0000001xxxxxxxxxxxxx0xx1xxxx", InstName.Eor, InstEmit32.Eor, typeof(OpCode32AluRsReg));
SetA32("1111010101111111111100000110xxxx", InstName.Isb, InstEmit32.Nop, typeof(OpCode32));
SetA32("<<<<00011001xxxxxxxx110010011111", InstName.Lda, InstEmit32.Lda, typeof(OpCode32MemLdEx));
SetA32("<<<<00011101xxxxxxxx110010011111", InstName.Ldab, InstEmit32.Ldab, typeof(OpCode32MemLdEx));
SetA32("<<<<00011001xxxxxxxx111010011111", InstName.Ldaex, InstEmit32.Ldaex, typeof(OpCode32MemLdEx));
SetA32("<<<<00011101xxxxxxxx111010011111", InstName.Ldaexb, InstEmit32.Ldaexb, typeof(OpCode32MemLdEx));
SetA32("<<<<00011011xxxxxxxx111010011111", InstName.Ldaexd, InstEmit32.Ldaexd, typeof(OpCode32MemLdEx));
SetA32("<<<<00011111xxxxxxxx111010011111", InstName.Ldaexh, InstEmit32.Ldaexh, typeof(OpCode32MemLdEx));
SetA32("<<<<00011111xxxxxxxx110010011111", InstName.Ldah, InstEmit32.Ldah, typeof(OpCode32MemLdEx));
SetA32("<<<<100xx0x1xxxxxxxxxxxxxxxxxxxx", InstName.Ldm, InstEmit32.Ldm, typeof(OpCode32MemMult));
SetA32("<<<<010xx0x1xxxxxxxxxxxxxxxxxxxx", InstName.Ldr, InstEmit32.Ldr, typeof(OpCode32MemImm));
SetA32("<<<<011xx0x1xxxxxxxxxxxxxxx0xxxx", InstName.Ldr, InstEmit32.Ldr, typeof(OpCode32MemRsImm));
SetA32("<<<<010xx1x1xxxxxxxxxxxxxxxxxxxx", InstName.Ldrb, InstEmit32.Ldrb, typeof(OpCode32MemImm));
SetA32("<<<<011xx1x1xxxxxxxxxxxxxxx0xxxx", InstName.Ldrb, InstEmit32.Ldrb, typeof(OpCode32MemRsImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1101xxxx", InstName.Ldrd, InstEmit32.Ldrd, typeof(OpCode32MemImm8));
SetA32("<<<<000xx0x0xxxxxxxx00001101xxxx", InstName.Ldrd, InstEmit32.Ldrd, typeof(OpCode32MemReg));
SetA32("<<<<00011001xxxxxxxx111110011111", InstName.Ldrex, InstEmit32.Ldrex, typeof(OpCode32MemLdEx));
SetA32("<<<<00011101xxxxxxxx111110011111", InstName.Ldrexb, InstEmit32.Ldrexb, typeof(OpCode32MemLdEx));
SetA32("<<<<00011011xxxxxxxx111110011111", InstName.Ldrexd, InstEmit32.Ldrexd, typeof(OpCode32MemLdEx));
SetA32("<<<<00011111xxxxxxxx111110011111", InstName.Ldrexh, InstEmit32.Ldrexh, typeof(OpCode32MemLdEx));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1011xxxx", InstName.Ldrh, InstEmit32.Ldrh, typeof(OpCode32MemImm8));
SetA32("<<<<000xx0x1xxxxxxxx00001011xxxx", InstName.Ldrh, InstEmit32.Ldrh, typeof(OpCode32MemReg));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1101xxxx", InstName.Ldrsb, InstEmit32.Ldrsb, typeof(OpCode32MemImm8));
SetA32("<<<<000xx0x1xxxxxxxx00001101xxxx", InstName.Ldrsb, InstEmit32.Ldrsb, typeof(OpCode32MemReg));
SetA32("<<<<000xx1x1xxxxxxxxxxxx1111xxxx", InstName.Ldrsh, InstEmit32.Ldrsh, typeof(OpCode32MemImm8));
SetA32("<<<<000xx0x1xxxxxxxx00001111xxxx", InstName.Ldrsh, InstEmit32.Ldrsh, typeof(OpCode32MemReg));
SetA32("<<<<1110xxx0xxxxxxxx111xxxx1xxxx", InstName.Mcr, InstEmit32.Mcr, typeof(OpCode32System));
SetA32("<<<<0000001xxxxxxxxxxxxx1001xxxx", InstName.Mla, InstEmit32.Mla, typeof(OpCode32AluMla));
SetA32("<<<<00000110xxxxxxxxxxxx1001xxxx", InstName.Mls, InstEmit32.Mls, typeof(OpCode32AluMla));
SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluImm));
SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluRsImm));
SetA32("<<<<0001101x0000xxxxxxxx0xx1xxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluRsReg));
SetA32("<<<<00110000xxxxxxxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCode32AluImm16));
SetT32("xxxxxxxxxxxxxxxx00100xxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, typeof(OpCodeT16AluImm8));
SetA32("<<<<00110100xxxxxxxxxxxxxxxxxxxx", InstName.Movt, InstEmit32.Movt, typeof(OpCode32AluImm16));
SetA32("<<<<1110xxx1xxxxxxxx111xxxx1xxxx", InstName.Mrc, InstEmit32.Mrc, typeof(OpCode32System));
SetA32("<<<<11000101xxxxxxxx111xxxxxxxxx", InstName.Mrrc, InstEmit32.Mrrc, typeof(OpCode32System));
SetA32("<<<<0000000xxxxx0000xxxx1001xxxx", InstName.Mul, InstEmit32.Mul, typeof(OpCode32AluMla));
SetA32("<<<<0011111x0000xxxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluImm));
SetA32("<<<<0001111x0000xxxxxxxxxxx0xxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluRsImm));
SetA32("<<<<0001111x0000xxxxxxxx0xx1xxxx", InstName.Mvn, InstEmit32.Mvn, typeof(OpCode32AluRsReg));
SetA32("<<<<0011100xxxxxxxxxxxxxxxxxxxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluImm));
SetA32("<<<<0001100xxxxxxxxxxxxxxxx0xxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluRsImm));
SetA32("<<<<0001100xxxxxxxxxxxxx0xx1xxxx", InstName.Orr, InstEmit32.Orr, typeof(OpCode32AluRsReg));
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, typeof(OpCode32AluRsImm));
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, typeof(OpCode32));
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, typeof(OpCode32));
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, typeof(OpCode32AluReg));
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, typeof(OpCode32AluReg));
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, typeof(OpCode32AluReg));
SetA32("<<<<011011111111xxxx11111011xxxx", InstName.Revsh, InstEmit32.Revsh, typeof(OpCode32AluReg));
SetA32("<<<<0010011xxxxxxxxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluImm));
SetA32("<<<<0000011xxxxxxxxxxxxxxxx0xxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluRsImm));
SetA32("<<<<0000011xxxxxxxxxxxxx0xx1xxxx", InstName.Rsb, InstEmit32.Rsb, typeof(OpCode32AluRsReg));
SetA32("<<<<0010111xxxxxxxxxxxxxxxxxxxxx", InstName.Rsc, InstEmit32.Rsc, typeof(OpCode32AluImm));
SetA32("<<<<0000111xxxxxxxxxxxxxxxx0xxxx", InstName.Rsc, InstEmit32.Rsc, typeof(OpCode32AluRsImm));
SetA32("<<<<0000111xxxxxxxxxxxxx0xx1xxxx", InstName.Rsc, InstEmit32.Rsc, typeof(OpCode32AluRsReg));
SetA32("<<<<0010110xxxxxxxxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluImm));
SetA32("<<<<0000110xxxxxxxxxxxxxxxx0xxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluRsImm));
SetA32("<<<<0000110xxxxxxxxxxxxx0xx1xxxx", InstName.Sbc, InstEmit32.Sbc, typeof(OpCode32AluRsReg));
SetA32("<<<<0111101xxxxxxxxxxxxxx101xxxx", InstName.Sbfx, InstEmit32.Sbfx, typeof(OpCode32AluBf));
SetA32("<<<<01110001xxxx1111xxxx0001xxxx", InstName.Sdiv, InstEmit32.Sdiv, typeof(OpCode32AluMla));
SetA32("<<<<00010000xxxxxxxxxxxx1xx0xxxx", InstName.Smlab, InstEmit32.Smlab, typeof(OpCode32AluMla));
SetA32("<<<<0000111xxxxxxxxxxxxx1001xxxx", InstName.Smlal, InstEmit32.Smlal, typeof(OpCode32AluUmull));
SetA32("<<<<00010100xxxxxxxxxxxx1xx0xxxx", InstName.Smlalh, InstEmit32.Smlalh, typeof(OpCode32AluUmull));
SetA32("<<<<01110101xxxxxxxxxxxx00x1xxxx", InstName.Smmla, InstEmit32.Smmla, typeof(OpCode32AluMla));
SetA32("<<<<01110101xxxxxxxxxxxx11x1xxxx", InstName.Smmls, InstEmit32.Smmls, typeof(OpCode32AluMla));
SetA32("<<<<00010110xxxxxxxxxxxx1xx0xxxx", InstName.Smulh, InstEmit32.Smulh, typeof(OpCode32AluMla));
SetA32("<<<<0000110xxxxxxxxxxxxx1001xxxx", InstName.Smull, InstEmit32.Smull, typeof(OpCode32AluUmull));
SetA32("<<<<00011000xxxx111111001001xxxx", InstName.Stl, InstEmit32.Stl, typeof(OpCode32MemStEx));
SetA32("<<<<00011100xxxx111111001001xxxx", InstName.Stlb, InstEmit32.Stlb, typeof(OpCode32MemStEx));
SetA32("<<<<00011000xxxxxxxx11101001xxxx", InstName.Stlex, InstEmit32.Stlex, typeof(OpCode32MemStEx));
SetA32("<<<<00011100xxxxxxxx11101001xxxx", InstName.Stlexb, InstEmit32.Stlexb, typeof(OpCode32MemStEx));
SetA32("<<<<00011010xxxxxxxx11101001xxxx", InstName.Stlexd, InstEmit32.Stlexd, typeof(OpCode32MemStEx));
SetA32("<<<<00011110xxxxxxxx11101001xxxx", InstName.Stlexh, InstEmit32.Stlexh, typeof(OpCode32MemStEx));
SetA32("<<<<00011110xxxx111111001001xxxx", InstName.Stlh, InstEmit32.Stlh, typeof(OpCode32MemStEx));
SetA32("<<<<100xx0x0xxxxxxxxxxxxxxxxxxxx", InstName.Stm, InstEmit32.Stm, typeof(OpCode32MemMult));
SetA32("<<<<010xx0x0xxxxxxxxxxxxxxxxxxxx", InstName.Str, InstEmit32.Str, typeof(OpCode32MemImm));
SetA32("<<<<011xx0x0xxxxxxxxxxxxxxx0xxxx", InstName.Str, InstEmit32.Str, typeof(OpCode32MemRsImm));
SetA32("<<<<010xx1x0xxxxxxxxxxxxxxxxxxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemImm));
SetA32("<<<<011xx1x0xxxxxxxxxxxxxxx0xxxx", InstName.Strb, InstEmit32.Strb, typeof(OpCode32MemRsImm));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1111xxxx", InstName.Strd, InstEmit32.Strd, typeof(OpCode32MemImm8));
SetA32("<<<<000xx0x0xxxxxxxx00001111xxxx", InstName.Strd, InstEmit32.Strd, typeof(OpCode32MemReg));
SetA32("<<<<00011000xxxxxxxx11111001xxxx", InstName.Strex, InstEmit32.Strex, typeof(OpCode32MemStEx));
SetA32("<<<<00011100xxxxxxxx11111001xxxx", InstName.Strexb, InstEmit32.Strexb, typeof(OpCode32MemStEx));
SetA32("<<<<00011010xxxxxxxx11111001xxxx", InstName.Strexd, InstEmit32.Strexd, typeof(OpCode32MemStEx));
SetA32("<<<<00011110xxxxxxxx11111001xxxx", InstName.Strexh, InstEmit32.Strexh, typeof(OpCode32MemStEx));
SetA32("<<<<000xx1x0xxxxxxxxxxxx1011xxxx", InstName.Strh, InstEmit32.Strh, typeof(OpCode32MemImm8));
SetA32("<<<<000xx0x0xxxxxxxx00001011xxxx", InstName.Strh, InstEmit32.Strh, typeof(OpCode32MemReg));
SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluImm));
SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluRsImm));
SetA32("<<<<0000010xxxxxxxxxxxxx0xx1xxxx", InstName.Sub, InstEmit32.Sub, typeof(OpCode32AluRsReg));
SetA32("<<<<1111xxxxxxxxxxxxxxxxxxxxxxxx", InstName.Svc, InstEmit32.Svc, typeof(OpCode32Exception));
SetA32("<<<<01101010xxxxxxxxxx000111xxxx", InstName.Sxtb, InstEmit32.Sxtb, typeof(OpCode32AluUx));
SetA32("<<<<01101000xxxxxxxxxx000111xxxx", InstName.Sxtb16, InstEmit32.Sxtb16, typeof(OpCode32AluUx));
SetA32("<<<<01101011xxxxxxxxxx000111xxxx", InstName.Sxth, InstEmit32.Sxth, typeof(OpCode32AluUx));
SetA32("<<<<00110011xxxx0000xxxxxxxxxxxx", InstName.Teq, InstEmit32.Teq, typeof(OpCode32AluImm));
SetA32("<<<<00010011xxxx0000xxxxxxx0xxxx", InstName.Teq, InstEmit32.Teq, typeof(OpCode32AluRsImm));
SetA32("<<<<00010011xxxx0000xxxx0xx1xxxx", InstName.Teq, InstEmit32.Teq, typeof(OpCode32AluRsReg));
SetA32("<<<<0111111111111101111011111110", InstName.Trap, InstEmit32.Trap, typeof(OpCode32Exception));
SetA32("<<<<00110001xxxx0000xxxxxxxxxxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluImm));
SetA32("<<<<00010001xxxx0000xxxxxxx0xxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluRsImm));
SetA32("<<<<00010001xxxx0000xxxx0xx1xxxx", InstName.Tst, InstEmit32.Tst, typeof(OpCode32AluRsReg));
SetA32("<<<<0111111xxxxxxxxxxxxxx101xxxx", InstName.Ubfx, InstEmit32.Ubfx, typeof(OpCode32AluBf));
SetA32("<<<<01110011xxxx1111xxxx0001xxxx", InstName.Udiv, InstEmit32.Udiv, typeof(OpCode32AluMla));
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, typeof(OpCode32AluUmull));
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, typeof(OpCode32AluUmull));
SetA32("<<<<01101110xxxxxxxxxx000111xxxx", InstName.Uxtb, InstEmit32.Uxtb, typeof(OpCode32AluUx));
SetA32("<<<<01101100xxxxxxxxxx000111xxxx", InstName.Uxtb16, InstEmit32.Uxtb16, typeof(OpCode32AluUx));
SetA32("<<<<01101111xxxxxxxxxx000111xxxx", InstName.Uxth, InstEmit32.Uxth, typeof(OpCode32AluUx));
// FP & SIMD
SetA32("<<<<11101x110000xxxx10xx11x0xxxx", InstName.Vabs, InstEmit32.Vabs_S, typeof(OpCode32SimdRegS));
SetA32("111100111x11xx01xxxx0x110xx0xxxx", InstName.Vabs, InstEmit32.Vabs_V, typeof(OpCode32SimdReg));
SetA32("111100100xxxxxxxxxxx1000xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_I, typeof(OpCode32SimdReg));
SetA32("<<<<11100x11xxxxxxxx101xx0x0xxxx", InstName.Vadd, InstEmit32.Vadd_S, typeof(OpCode32SimdRegS));
SetA32("111100100x00xxxxxxxx1101xxx0xxxx", InstName.Vadd, InstEmit32.Vadd_V, typeof(OpCode32SimdReg));
SetA32("111100100x00xxxxxxxx0001xxx1xxxx", InstName.Vand, InstEmit32.Vand_I, typeof(OpCode32SimdBinary));
SetA32("111100110x11xxxxxxxx0001xxx1xxxx", InstName.Vbif, InstEmit32.Vbif, typeof(OpCode32SimdBinary));
SetA32("111100110x10xxxxxxxx0001xxx1xxxx", InstName.Vbit, InstEmit32.Vbit, typeof(OpCode32SimdBinary));
SetA32("111100110x01xxxxxxxx0001xxx1xxxx", InstName.Vbsl, InstEmit32.Vbsl, typeof(OpCode32SimdBinary));
SetA32("111100110x<<xxxxxxxx1000xxx1xxxx", InstName.Vceq, InstEmit32.Vceq_I, typeof(OpCode32SimdReg));
SetA32("111100100x00xxxxxxxx1110xxx0xxxx", InstName.Vceq, InstEmit32.Vceq_V, typeof(OpCode32SimdReg));
SetA32("111100111x11xx01xxxx0x010xx0xxxx", InstName.Vceq, InstEmit32.Vceq_Z, typeof(OpCode32SimdCmpZ));
SetA32("1111001x0x<<xxxxxxxx0011xxx1xxxx", InstName.Vcge, InstEmit32.Vcge_I, typeof(OpCode32SimdReg));
SetA32("111100110x00xxxxxxxx1110xxx0xxxx", InstName.Vcge, InstEmit32.Vcge_V, typeof(OpCode32SimdReg));
SetA32("111100111x11xx01xxxx0x001xx0xxxx", InstName.Vcge, InstEmit32.Vcge_Z, typeof(OpCode32SimdCmpZ));
SetA32("1111001x0x<<xxxxxxxx0011xxx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_I, typeof(OpCode32SimdReg));
SetA32("111100110x10xxxxxxxx1110xxx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_V, typeof(OpCode32SimdReg));
SetA32("111100111x11xx01xxxx0x000xx0xxxx", InstName.Vcgt, InstEmit32.Vcgt_Z, typeof(OpCode32SimdCmpZ));
SetA32("111100111x11xx01xxxx0x011xx0xxxx", InstName.Vcle, InstEmit32.Vcle_Z, typeof(OpCode32SimdCmpZ));
SetA32("111100111x11xx01xxxx0x100xx0xxxx", InstName.Vclt, InstEmit32.Vclt_Z, typeof(OpCode32SimdCmpZ));
SetA32("<<<<11101x11010xxxxx101x01x0xxxx", InstName.Vcmp, InstEmit32.Vcmp, typeof(OpCode32SimdS));
SetA32("<<<<11101x11010xxxxx101x11x0xxxx", InstName.Vcmpe, InstEmit32.Vcmpe, typeof(OpCode32SimdS));
SetA32("<<<<11101x110111xxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FD, typeof(OpCode32SimdS)); // FP 32 and 64, scalar.
SetA32("<<<<11101x11110xxxxx10xx11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); // FP32 to int.
SetA32("<<<<11101x111000xxxx10xxx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, typeof(OpCode32SimdCvtFI)); // Int to FP32.
SetA32("111111101x1111xxxxxx10>>x1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_R, typeof(OpCode32SimdCvtFI)); // The many FP32 to int encodings (fp).
SetA32("111100111x111011xxxx011xxxx0xxxx", InstName.Vcvt, InstEmit32.Vcvt_V, typeof(OpCode32SimdCmpZ)); // FP and integer, vector.
SetA32("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, typeof(OpCode32SimdRegS));
SetA32("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, typeof(OpCode32SimdDupGP));
SetA32("111100111x11xxxxxxxx11000xx0xxxx", InstName.Vdup, InstEmit32.Vdup_1, typeof(OpCode32SimdDupElem));
SetA32("111100101x11xxxxxxxxxxxxxxx0xxxx", InstName.Vext, InstEmit32.Vext, typeof(OpCode32SimdExt));
SetA32("111101001x10xxxxxxxxxx00xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemSingle));
SetA32("111101000x10xxxxxxxx0111xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); // Regs = 1.
SetA32("111101000x10xxxxxxxx1010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); // Regs = 2.
SetA32("111101000x10xxxxxxxx0110xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); // Regs = 3.
SetA32("111101000x10xxxxxxxx0010xxxxxxxx", InstName.Vld1, InstEmit32.Vld1, typeof(OpCode32SimdMemPair)); // Regs = 4.
SetA32("111101001x10xxxxxxxxxx01xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemSingle));
SetA32("111101000x10xxxxxxxx100xxxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemPair)); // Regs = 1, inc = 1/2 (itype).
SetA32("111101000x10xxxxxxxx0011xxxxxxxx", InstName.Vld2, InstEmit32.Vld2, typeof(OpCode32SimdMemPair)); // Regs = 2, inc = 2.
SetA32("111101001x10xxxxxxxxxx10xxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemSingle));
SetA32("111101000x10xxxxxxxx010xxxxxxxxx", InstName.Vld3, InstEmit32.Vld3, typeof(OpCode32SimdMemPair)); // Inc = 1/2 (itype).
SetA32("111101001x10xxxxxxxxxx11xxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemSingle));
SetA32("111101000x10xxxxxxxx000xxxxxxxxx", InstName.Vld4, InstEmit32.Vld4, typeof(OpCode32SimdMemPair)); // Inc = 1/2 (itype).
SetA32("<<<<11001x01xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11001x11xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11010x11xxxxxxxx1011xxxxxxx0", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11001x01xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11001x11xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11010x11xxxxxxxx1010xxxxxxxx", InstName.Vldm, InstEmit32.Vldm, typeof(OpCode32SimdMemMult));
SetA32("<<<<1101xx01xxxxxxxx101xxxxxxxxx", InstName.Vldr, InstEmit32.Vldr, typeof(OpCode32SimdMemImm));
SetA32("1111001x0x<<xxxxxxxx0110xxx0xxxx", InstName.Vmax, InstEmit32.Vmax_I, typeof(OpCode32SimdReg));
SetA32("111100100x00xxxxxxxx1111xxx0xxxx", InstName.Vmax, InstEmit32.Vmax_V, typeof(OpCode32SimdReg));
SetA32("1111001x0x<<xxxxxxxx0110xxx1xxxx", InstName.Vmin, InstEmit32.Vmin_I, typeof(OpCode32SimdReg));
SetA32("111100100x10xxxxxxxx1111xxx0xxxx", InstName.Vmin, InstEmit32.Vmin_V, typeof(OpCode32SimdReg));
SetA32("111111101x00xxxxxxxx10>>x0x0xxxx", InstName.Vmaxnm, InstEmit32.Vmaxnm_S, typeof(OpCode32SimdRegS));
SetA32("111100110x0xxxxxxxxx1111xxx1xxxx", InstName.Vmaxnm, InstEmit32.Vmaxnm_V, typeof(OpCode32SimdReg));
SetA32("111111101x00xxxxxxxx10>>x1x0xxxx", InstName.Vminnm, InstEmit32.Vminnm_S, typeof(OpCode32SimdRegS));
SetA32("111100110x1xxxxxxxxx1111xxx1xxxx", InstName.Vminnm, InstEmit32.Vminnm_V, typeof(OpCode32SimdReg));
SetA32("1111001x1x<<xxxxxxxx000xx1x0xxxx", InstName.Vmla, InstEmit32.Vmla_1, typeof(OpCode32SimdRegElem));
SetA32("111100100xxxxxxxxxxx1001xxx0xxxx", InstName.Vmla, InstEmit32.Vmla_I, typeof(OpCode32SimdReg));
SetA32("<<<<11100x00xxxxxxxx101xx0x0xxxx", InstName.Vmla, InstEmit32.Vmla_S, typeof(OpCode32SimdRegS));
SetA32("111100100x00xxxxxxxx1101xxx1xxxx", InstName.Vmla, InstEmit32.Vmla_V, typeof(OpCode32SimdReg));
SetA32("1111001x1x<<xxxxxxxx010xx1x0xxxx", InstName.Vmls, InstEmit32.Vmls_1, typeof(OpCode32SimdRegElem));
SetA32("<<<<11100x00xxxxxxxx101xx1x0xxxx", InstName.Vmls, InstEmit32.Vmls_S, typeof(OpCode32SimdRegS));
SetA32("111100100x10xxxxxxxx1101xxx1xxxx", InstName.Vmls, InstEmit32.Vmls_V, typeof(OpCode32SimdReg));
SetA32("111100110xxxxxxxxxxx1001xxx0xxxx", InstName.Vmls, InstEmit32.Vmls_I, typeof(OpCode32SimdReg));
SetA32("<<<<11100xx0xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov_G1, typeof(OpCode32SimdMovGpElem)); // From gen purpose.
SetA32("<<<<1110xxx1xxxxxxxx1011xxx10000", InstName.Vmov, InstEmit32.Vmov_G1, typeof(OpCode32SimdMovGpElem)); // To gen purpose.
SetA32("<<<<1100010xxxxxxxxx101000x1xxxx", InstName.Vmov, InstEmit32.Vmov_G2, typeof(OpCode32SimdMovGpDouble)); // To/from gen purpose x2 and single precision x2.
SetA32("<<<<1100010xxxxxxxxx101100x1xxxx", InstName.Vmov, InstEmit32.Vmov_GD, typeof(OpCode32SimdMovGpDouble)); // To/from gen purpose x2 and double precision.
SetA32("<<<<1110000xxxxxxxxx1010x0010000", InstName.Vmov, InstEmit32.Vmov_GS, typeof(OpCode32SimdMovGp)); // To/from gen purpose and single precision.
SetA32("1111001x1x000xxxxxxx0xx00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); // D/Q vector I32.
SetA32("<<<<11101x11xxxxxxxx101x0000xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm44)); // Scalar f16/32/64 based on size 01 10 11.
SetA32("1111001x1x000xxxxxxx10x00x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); // D/Q I16.
SetA32("1111001x1x000xxxxxxx11xx0x01xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); // D/Q (dt - from cmode).
SetA32("1111001x1x000xxxxxxx11100x11xxxx", InstName.Vmov, InstEmit32.Vmov_I, typeof(OpCode32SimdImm)); // D/Q I64.
SetA32("<<<<11101x110000xxxx101x01x0xxxx", InstName.Vmov, InstEmit32.Vmov_S, typeof(OpCode32SimdS));
SetA32("111100111x11xx10xxxx001000x0xxx0", InstName.Vmovn, InstEmit32.Vmovn, typeof(OpCode32SimdCmpZ));
SetA32("<<<<11101111xxxxxxxx101000010000", InstName.Vmrs, InstEmit32.Vmrs, typeof(OpCode32SimdSpecial));
SetA32("<<<<11101110xxxxxxxx101000010000", InstName.Vmsr, InstEmit32.Vmsr, typeof(OpCode32SimdSpecial));
SetA32("1111001x1x<<xxxxxxxx100xx1x0xxxx", InstName.Vmul, InstEmit32.Vmul_1, typeof(OpCode32SimdRegElem));
SetA32("1111001x0xxxxxxxxxxx1001xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_I, typeof(OpCode32SimdReg));
SetA32("<<<<11100x10xxxxxxxx101xx0x0xxxx", InstName.Vmul, InstEmit32.Vmul_S, typeof(OpCode32SimdRegS));
SetA32("111100110x00xxxxxxxx1101xxx1xxxx", InstName.Vmul, InstEmit32.Vmul_V, typeof(OpCode32SimdReg));
SetA32("1111001x1x000xxxxxxx0xx00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_I, typeof(OpCode32SimdImm)); // D/Q vector I32.
SetA32("1111001x1x000xxxxxxx10x00x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_I, typeof(OpCode32SimdImm));
SetA32("1111001x1x000xxxxxxx110x0x11xxxx", InstName.Vmvn, InstEmit32.Vmvn_I, typeof(OpCode32SimdImm));
SetA32("<<<<11101x110001xxxx101x01x0xxxx", InstName.Vneg, InstEmit32.Vneg_S, typeof(OpCode32SimdS));
SetA32("111100111x11xx01xxxx0x111xx0xxxx", InstName.Vneg, InstEmit32.Vneg_V, typeof(OpCode32Simd));
SetA32("<<<<11100x01xxxxxxxx101xx1x0xxxx", InstName.Vnmla, InstEmit32.Vnmla_S, typeof(OpCode32SimdRegS));
SetA32("<<<<11100x01xxxxxxxx101xx0x0xxxx", InstName.Vnmls, InstEmit32.Vnmls_S, typeof(OpCode32SimdRegS));
SetA32("<<<<11100x10xxxxxxxx101xx1x0xxxx", InstName.Vnmul, InstEmit32.Vnmul_S, typeof(OpCode32SimdRegS));
SetA32("111100100x10xxxxxxxx0001xxx1xxxx", InstName.Vorr, InstEmit32.Vorr_I, typeof(OpCode32SimdBinary));
SetA32("111100100x<<xxxxxxxx1011x0x1xxxx", InstName.Vpadd, InstEmit32.Vpadd_I, typeof(OpCode32SimdReg));
SetA32("111100110x00xxxxxxxx1101x0x0xxxx", InstName.Vpadd, InstEmit32.Vpadd_V, typeof(OpCode32SimdReg));
SetA32("111100111x111011xxxx010x0xx0xxxx", InstName.Vrecpe, InstEmit32.Vrecpe, typeof(OpCode32SimdSqrte));
SetA32("111100100x00xxxxxxxx1111xxx1xxxx", InstName.Vrecps, InstEmit32.Vrecps, typeof(OpCode32SimdReg));
SetA32("111100111x11xx00xxxx000<<xx0xxxx", InstName.Vrev, InstEmit32.Vrev, typeof(OpCode32SimdRev));
SetA32("111111101x1110xxxxxx101x01x0xxxx", InstName.Vrint, InstEmit32.Vrint_RM, typeof(OpCode32SimdCvtFI));
SetA32("<<<<11101x110110xxxx101x11x0xxxx", InstName.Vrint, InstEmit32.Vrint_Z, typeof(OpCode32SimdCvtFI));
SetA32("111100111x111011xxxx010x1xx0xxxx", InstName.Vrsqrte, InstEmit32.Vrsqrte, typeof(OpCode32SimdSqrte));
SetA32("111100100x10xxxxxxxx1111xxx1xxxx", InstName.Vrsqrts, InstEmit32.Vrsqrts, typeof(OpCode32SimdReg));
SetA32("111111100xxxxxxxxxxx101xx0x0xxxx", InstName.Vsel, InstEmit32.Vsel, typeof(OpCode32SimdSel));
SetA32("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, typeof(OpCode32SimdShImm));
SetA32("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, typeof(OpCode32SimdReg));
SetA32("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, typeof(OpCode32SimdShImm));
SetA32("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, typeof(OpCode32SimdShImm));
SetA32("<<<<11101x110001xxxx101x11x0xxxx", InstName.Vsqrt, InstEmit32.Vsqrt_S, typeof(OpCode32SimdS));
SetA32("111101001x00xxxxxxxx<<00xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemSingle));
SetA32("111101000x00xxxxxxxx0111xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); // Regs = 1.
SetA32("111101000x00xxxxxxxx1010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); // Regs = 2.
SetA32("111101000x00xxxxxxxx0110xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); // Regs = 3.
SetA32("111101000x00xxxxxxxx0010xxxxxxxx", InstName.Vst1, InstEmit32.Vst1, typeof(OpCode32SimdMemPair)); // Regs = 4.
SetA32("111101001x00xxxxxxxx<<01xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemSingle));
SetA32("111101000x00xxxxxxxx100xxxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemPair)); // Regs = 1, inc = 1/2 (itype).
SetA32("111101000x00xxxxxxxx0011xxxxxxxx", InstName.Vst2, InstEmit32.Vst2, typeof(OpCode32SimdMemPair)); // Regs = 2, inc = 2.
SetA32("111101001x00xxxxxxxx<<10xxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemSingle));
SetA32("111101000x00xxxxxxxx010xxxxxxxxx", InstName.Vst3, InstEmit32.Vst3, typeof(OpCode32SimdMemPair)); // Inc = 1/2 (itype).
SetA32("111101001x00xxxxxxxx<<11xxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemSingle));
SetA32("111101000x00xxxxxxxx000xxxxxxxxx", InstName.Vst4, InstEmit32.Vst4, typeof(OpCode32SimdMemPair)); // Inc = 1/2 (itype).
SetA32("<<<<11001x00xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11001x10xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11010x10xxxxxxxx1011xxxxxxx0", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11001x00xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11001x10xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));
SetA32("<<<<11010x10xxxxxxxx1010xxxxxxxx", InstName.Vstm, InstEmit32.Vstm, typeof(OpCode32SimdMemMult));
SetA32("<<<<1101xx00xxxxxxxx101xxxxxxxxx", InstName.Vstr, InstEmit32.Vstr, typeof(OpCode32SimdMemImm));
SetA32("111100110xxxxxxxxxxx1000xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_I, typeof(OpCode32SimdReg));
SetA32("<<<<11100x11xxxxxxxx101xx1x0xxxx", InstName.Vsub, InstEmit32.Vsub_S, typeof(OpCode32SimdRegS));
SetA32("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, typeof(OpCode32SimdReg));
SetA32("111100111x11xxxxxxxx10xxxxx0xxxx", InstName.Vtbl, InstEmit32.Vtbl, typeof(OpCode32SimdTbl));
SetA32("111100111x11<<10xxxx00001xx0xxxx", InstName.Vtrn, InstEmit32.Vtrn, typeof(OpCode32SimdCmpZ));
SetA32("111100111x11<<10xxxx00010xx0xxxx", InstName.Vuzp, InstEmit32.Vuzp, typeof(OpCode32SimdCmpZ));
SetA32("111100111x11<<10xxxx00011xx0xxxx", InstName.Vzip, InstEmit32.Vzip, typeof(OpCode32SimdCmpZ));
#endregion
FillFastLookupTable(_instA32FastLookup, _allInstA32);

View file

@ -134,6 +134,7 @@ namespace ARMeilleure.Diagnostics
switch (reg.Type)
{
case RegisterType.Flag: name = "b" + reg.Index; break;
case RegisterType.FpFlag: name = "f" + reg.Index; break;
case RegisterType.Integer: name = "r" + reg.Index; break;
case RegisterType.Vector: name = "v" + reg.Index; break;
}

View file

@ -4,13 +4,19 @@ using System;
namespace ARMeilleure.Instructions
{
delegate double _F64_F64(double a1);
delegate double _F64_F64_Bool(double a1, bool a2);
delegate double _F64_F64_F64(double a1, double a2);
delegate double _F64_F64_F64_Bool(double a1, double a2, bool a3);
delegate double _F64_F64_F64_F64(double a1, double a2, double a3);
delegate double _F64_F64_F64_F64_Bool(double a1, double a2, double a3, bool a4);
delegate double _F64_F64_MidpointRounding(double a1, MidpointRounding a2);
delegate float _F32_F32(float a1);
delegate float _F32_F32_Bool(float a1, bool a2);
delegate float _F32_F32_F32(float a1, float a2);
delegate float _F32_F32_F32_Bool(float a1, float a2, bool a3);
delegate float _F32_F32_F32_F32(float a1, float a2, float a3);
delegate float _F32_F32_F32_F32_Bool(float a1, float a2, float a3, bool a4);
delegate float _F32_F32_MidpointRounding(float a1, MidpointRounding a2);
delegate float _F32_U16(ushort a1);
@ -37,6 +43,7 @@ namespace ARMeilleure.Instructions
delegate ushort _U16_F32(float a1);
delegate ushort _U16_U64(ulong a1);
delegate uint _U32();
delegate uint _U32_F32(float a1);
delegate uint _U32_F64(double a1);
delegate uint _U32_U32(uint a1);
@ -74,6 +81,7 @@ namespace ARMeilleure.Instructions
delegate V128 _V128_V128_V128_V128(V128 a1, V128 a2, V128 a3);
delegate void _Void();
delegate void _Void_U32(uint a1);
delegate void _Void_U64(ulong a1);
delegate void _Void_U64_S32(ulong a1, int a2);
delegate void _Void_U64_U16(ulong a1, ushort a2);

View file

@ -276,23 +276,6 @@ namespace ARMeilleure.Instructions
SetAluDOrZR(context, d);
}
private static Operand EmitReverseBits32Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I32);
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaau)), Const(1)),
context.ShiftLeft (context.BitwiseAnd(op, Const(0x55555555u)), Const(1)));
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccu)), Const(2)),
context.ShiftLeft (context.BitwiseAnd(val, Const(0x33333333u)), Const(2)));
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0u)), Const(4)),
context.ShiftLeft (context.BitwiseAnd(val, Const(0x0f0f0f0fu)), Const(4)));
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00u)), Const(8)),
context.ShiftLeft (context.BitwiseAnd(val, Const(0x00ff00ffu)), Const(8)));
return context.BitwiseOr(context.ShiftRightUI(val, Const(16)), context.ShiftLeft(val, Const(16)));
}
private static Operand EmitReverseBits64Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I64);
@ -331,23 +314,6 @@ namespace ARMeilleure.Instructions
SetAluDOrZR(context, d);
}
private static Operand EmitReverseBytes16_32Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I32);
Operand val = EmitReverseBytes16_64Op(context, context.ZeroExtend32(OperandType.I64, op));
return context.ConvertI64ToI32(val);
}
private static Operand EmitReverseBytes16_64Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I64);
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xff00ff00ff00ff00ul)), Const(8)),
context.ShiftLeft (context.BitwiseAnd(op, Const(0x00ff00ff00ff00fful)), Const(8)));
}
public static void Rev32(ArmEmitterContext context)
{
OpCodeAlu op = (OpCodeAlu)context.CurrOp;

View file

@ -3,8 +3,8 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitAluHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
@ -31,6 +31,101 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res);
}
public static void Adc(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context, setCarry: false);
Operand res = context.Add(n, m);
Operand carry = GetFlag(PState.CFlag);
res = context.Add(res, carry);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
EmitAdcsCCheck(context, n, res);
EmitAddsVCheck(context, n, m, res);
}
EmitAluStore(context, res);
}
public static void And(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand res = context.BitwiseAnd(n, m);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitAluStore(context, res);
}
public static void Bfc(ArmEmitterContext context)
{
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
Operand d = GetIntA32(context, op.Rd);
Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
SetIntA32(context, op.Rd, res);
}
public static void Bfi(ArmEmitterContext context)
{
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand d = GetIntA32(context, op.Rd);
Operand part = context.BitwiseAnd(n, Const(op.SourceMask));
if (op.Lsb != 0)
{
part = context.ShiftLeft(part, Const(op.Lsb));
}
Operand res = context.BitwiseAnd(d, Const(~op.DestMask));
res = context.BitwiseOr(res, context.BitwiseAnd(part, Const(op.DestMask)));
SetIntA32(context, op.Rd, res);
}
public static void Bic(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand res = context.BitwiseAnd(n, context.BitwiseNot(m));
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitAluStore(context, res);
}
public static void Clz(ArmEmitterContext context)
{
Operand m = GetAluM(context, setCarry: false);
Operand res = context.CountLeadingZeros(m);
EmitAluStore(context, res);
}
public static void Cmp(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
@ -46,6 +141,36 @@ namespace ARMeilleure.Instructions
EmitSubsVCheck(context, n, m, res);
}
public static void Cmn(ArmEmitterContext context)
{
Operand n = GetAluN(context);
Operand m = GetAluM(context, setCarry: false);
Operand res = context.Add(n, m);
EmitNZFlagsCheck(context, res);
EmitAddsCCheck(context, n, res);
EmitAddsVCheck(context, n, m, res);
}
public static void Eor(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand res = context.BitwiseExclusiveOr(n, m);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitAluStore(context, res);
}
public static void Mov(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
@ -60,6 +185,210 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, m);
}
public static void Movt(ArmEmitterContext context)
{
OpCode32AluImm16 op = (OpCode32AluImm16)context.CurrOp;
Operand d = GetIntA32(context, op.Rd);
Operand imm = Const(op.Immediate << 16); // Immeditate value as top halfword.
Operand res = context.BitwiseAnd(d, Const(0x0000ffff));
res = context.BitwiseOr(res, imm);
EmitAluStore(context, res);
}
public static void Mul(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand res = context.Multiply(n, m);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitAluStore(context, res);
}
public static void Mvn(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand m = GetAluM(context);
Operand res = context.BitwiseNot(m);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitAluStore(context, res);
}
public static void Orr(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand res = context.BitwiseOr(n, m);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitAluStore(context, res);
}
public static void Pkh(ArmEmitterContext context)
{
OpCode32AluRsImm op = (OpCode32AluRsImm)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand res;
bool tbform = op.ShiftType == ShiftType.Asr;
if (tbform)
{
res = context.BitwiseOr(context.BitwiseAnd(n, Const(0xFFFF0000)), context.BitwiseAnd(m, Const(0xFFFF)));
}
else
{
res = context.BitwiseOr(context.BitwiseAnd(m, Const(0xFFFF0000)), context.BitwiseAnd(n, Const(0xFFFF)));
}
EmitAluStore(context, res);
}
public static void Rbit(ArmEmitterContext context)
{
Operand m = GetAluM(context);
Operand res = EmitReverseBits32Op(context, m);
EmitAluStore(context, res);
}
public static void Rev(ArmEmitterContext context)
{
Operand m = GetAluM(context);
Operand res = context.ByteSwap(m);
EmitAluStore(context, res);
}
public static void Rev16(ArmEmitterContext context)
{
Operand m = GetAluM(context);
Operand res = EmitReverseBytes16_32Op(context, m);
EmitAluStore(context, res);
}
public static void Revsh(ArmEmitterContext context)
{
Operand m = GetAluM(context);
Operand res = EmitReverseBytes16_32Op(context, m);
EmitAluStore(context, context.SignExtend16(OperandType.I32, res));
}
public static void Rsc(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context, setCarry: false);
Operand res = context.Subtract(m, n);
Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
res = context.Subtract(res, borrow);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
EmitSbcsCCheck(context, m, n);
EmitSubsVCheck(context, m, n, res);
}
EmitAluStore(context, res);
}
public static void Rsb(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context, setCarry: false);
Operand res = context.Subtract(m, n);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
EmitSubsCCheck(context, m, res);
EmitSubsVCheck(context, m, n, res);
}
EmitAluStore(context, res);
}
public static void Sbc(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context, setCarry: false);
Operand res = context.Subtract(n, m);
Operand borrow = context.BitwiseExclusiveOr(GetFlag(PState.CFlag), Const(1));
res = context.Subtract(res, borrow);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
EmitSbcsCCheck(context, n, m);
EmitSubsVCheck(context, n, m, res);
}
EmitAluStore(context, res);
}
public static void Sbfx(ArmEmitterContext context)
{
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
Operand n = GetIntA32(context, op.Rn);
Operand res = context.ShiftRightSI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
SetIntA32(context, op.Rd, res);
}
public static void Sdiv(ArmEmitterContext context)
{
EmitDiv(context, false);
}
public static void Sub(ArmEmitterContext context)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
@ -80,50 +409,216 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res);
}
public static void Sxtb(ArmEmitterContext context)
{
EmitSignExtend(context, true, 8);
}
public static void Sxtb16(ArmEmitterContext context)
{
EmitExtend16(context, true);
}
public static void Sxth(ArmEmitterContext context)
{
EmitSignExtend(context, true, 16);
}
public static void Teq(ArmEmitterContext context)
{
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand res = context.BitwiseExclusiveOr(n, m);
EmitNZFlagsCheck(context, res);
}
public static void Tst(ArmEmitterContext context)
{
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand res = context.BitwiseAnd(n, m);
EmitNZFlagsCheck(context, res);
}
public static void Ubfx(ArmEmitterContext context)
{
OpCode32AluBf op = (OpCode32AluBf)context.CurrOp;
var msb = op.Lsb + op.Msb; // For this instruction, the msb is actually a width.
Operand n = GetIntA32(context, op.Rn);
Operand res = context.ShiftRightUI(context.ShiftLeft(n, Const(31 - msb)), Const(31 - op.Msb));
SetIntA32(context, op.Rd, res);
}
public static void Udiv(ArmEmitterContext context)
{
EmitDiv(context, true);
}
public static void Uxtb(ArmEmitterContext context)
{
EmitSignExtend(context, false, 8);
}
public static void Uxtb16(ArmEmitterContext context)
{
EmitExtend16(context, false);
}
public static void Uxth(ArmEmitterContext context)
{
EmitSignExtend(context, false, 16);
}
private static void EmitSignExtend(ArmEmitterContext context, bool signed, int bits)
{
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
Operand m = GetAluM(context);
Operand res;
if (op.RotateBits == 0)
{
res = m;
}
else
{
Operand rotate = Const(op.RotateBits);
res = context.RotateRight(m, rotate);
}
switch (bits)
{
case 8:
res = (signed) ? context.SignExtend8(OperandType.I32, res) : context.ZeroExtend8(OperandType.I32, res);
break;
case 16:
res = (signed) ? context.SignExtend16(OperandType.I32, res) : context.ZeroExtend16(OperandType.I32, res);
break;
}
if (op.Add)
{
res = context.Add(res, GetAluN(context));
}
EmitAluStore(context, res);
}
private static void EmitExtend16(ArmEmitterContext context, bool signed)
{
IOpCode32AluUx op = (IOpCode32AluUx)context.CurrOp;
Operand m = GetAluM(context);
Operand res;
if (op.RotateBits == 0)
{
res = m;
}
else
{
Operand rotate = Const(op.RotateBits);
res = context.RotateRight(m, rotate);
}
Operand low16, high16;
if (signed)
{
low16 = context.SignExtend8(OperandType.I32, res);
high16 = context.SignExtend8(OperandType.I32, context.ShiftRightUI(res, Const(16)));
}
else
{
low16 = context.ZeroExtend8(OperandType.I32, res);
high16 = context.ZeroExtend8(OperandType.I32, context.ShiftRightUI(res, Const(16)));
}
if (op.Add)
{
Operand n = GetAluN(context);
Operand lowAdd, highAdd;
if (signed)
{
lowAdd = context.SignExtend16(OperandType.I32, n);
highAdd = context.SignExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
}
else
{
lowAdd = context.ZeroExtend16(OperandType.I32, n);
highAdd = context.ZeroExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
}
low16 = context.Add(low16, lowAdd);
high16 = context.Add(high16, highAdd);
}
res = context.BitwiseOr(
context.ZeroExtend16(OperandType.I32, low16),
context.ShiftLeft(context.ZeroExtend16(OperandType.I32, high16), Const(16)));
EmitAluStore(context, res);
}
public static void EmitDiv(ArmEmitterContext context, bool unsigned)
{
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand zero = Const(m.Type, 0);
Operand divisorIsZero = context.ICompareEqual(m, zero);
Operand lblBadDiv = Label();
Operand lblEnd = Label();
context.BranchIfTrue(lblBadDiv, divisorIsZero);
if (!unsigned)
{
// ARM64 behaviour: If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
// TODO: tests to ensure A32 works the same
Operand intMin = Const(int.MinValue);
Operand minus1 = Const(-1);
Operand nIsIntMin = context.ICompareEqual(n, intMin);
Operand mIsMinus1 = context.ICompareEqual(m, minus1);
Operand lblGoodDiv = Label();
context.BranchIfFalse(lblGoodDiv, context.BitwiseAnd(nIsIntMin, mIsMinus1));
EmitAluStore(context, intMin);
context.Branch(lblEnd);
context.MarkLabel(lblGoodDiv);
}
Operand res = unsigned
? context.DivideUI(n, m)
: context.Divide(n, m);
EmitAluStore(context, res);
context.Branch(lblEnd);
context.MarkLabel(lblBadDiv);
EmitAluStore(context, zero);
context.MarkLabel(lblEnd);
}
private static void EmitAluStore(ArmEmitterContext context, Operand value)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
if (op.Rd == RegisterAlias.Aarch32Pc)
{
if (op.SetFlags)
{
// TODO: Load SPSR etc.
Operand isThumb = GetFlag(PState.TFlag);
Operand lblThumb = Label();
context.BranchIfTrue(lblThumb, isThumb);
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~3))));
context.MarkLabel(lblThumb);
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~1))));
}
else
{
EmitAluWritePc(context, value);
}
}
else
{
SetIntA32(context, op.Rd, value);
}
}
private static void EmitAluWritePc(ArmEmitterContext context, Operand value)
{
context.StoreToContext();
if (IsThumb(context.CurrOp))
{
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(value, Const(~1))));
}
else
{
EmitBxWritePc(context, value);
}
EmitGenericAluStoreA32(context, op.Rd, op.SetFlags, value);
}
}
}

View file

@ -3,6 +3,7 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -77,6 +78,89 @@ namespace ARMeilleure.Instructions
SetFlag(context, PState.VFlag, vOut);
}
public static Operand EmitReverseBits32Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I32);
Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaau)), Const(1)),
context.ShiftLeft(context.BitwiseAnd(op, Const(0x55555555u)), Const(1)));
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccu)), Const(2)),
context.ShiftLeft(context.BitwiseAnd(val, Const(0x33333333u)), Const(2)));
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0u)), Const(4)),
context.ShiftLeft(context.BitwiseAnd(val, Const(0x0f0f0f0fu)), Const(4)));
val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00u)), Const(8)),
context.ShiftLeft(context.BitwiseAnd(val, Const(0x00ff00ffu)), Const(8)));
return context.BitwiseOr(context.ShiftRightUI(val, Const(16)), context.ShiftLeft(val, Const(16)));
}
public static Operand EmitReverseBytes16_64Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I64);
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xff00ff00ff00ff00ul)), Const(8)),
context.ShiftLeft(context.BitwiseAnd(op, Const(0x00ff00ff00ff00fful)), Const(8)));
}
public static Operand EmitReverseBytes16_32Op(ArmEmitterContext context, Operand op)
{
Debug.Assert(op.Type == OperandType.I32);
Operand val = EmitReverseBytes16_64Op(context, context.ZeroExtend32(OperandType.I64, op));
return context.ConvertI64ToI32(val);
}
private static void EmitAluWritePc(ArmEmitterContext context, Operand value)
{
Debug.Assert(value.Type == OperandType.I32);
context.StoreToContext();
if (IsThumb(context.CurrOp))
{
// Make this count as a call, the translator will ignore the low bit for the address.
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(value, Const(1))));
}
else
{
EmitBxWritePc(context, value);
}
}
public static void EmitGenericAluStoreA32(ArmEmitterContext context, int rd, bool setFlags, Operand value)
{
Debug.Assert(value.Type == OperandType.I32);
if (rd == RegisterAlias.Aarch32Pc && setFlags)
{
if (setFlags)
{
// TODO: Load SPSR etc.
Operand isThumb = GetFlag(PState.TFlag);
Operand lblThumb = Label();
context.BranchIfTrue(lblThumb, isThumb);
// Make this count as a call, the translator will ignore the low bit for the address.
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(context.BitwiseAnd(value, Const(~3)), Const(1))));
context.MarkLabel(lblThumb);
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(value, Const(1))));
}
else
{
EmitAluWritePc(context, value);
}
}
else
{
SetIntA32(context, rd, value);
}
}
public static Operand GetAluN(ArmEmitterContext context)
{
@ -116,10 +200,15 @@ namespace ARMeilleure.Instructions
return Const(op.Immediate);
}
case OpCode32AluImm16 op: return Const(op.Immediate);
case OpCode32AluRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
case OpCode32AluRsReg op: return GetMShiftedByReg(context, op, setCarry);
case OpCodeT16AluImm8 op: return Const(op.Immediate);
case IOpCode32AluReg op: return GetIntA32(context, op.Rm);
// ARM64.
case IOpCodeAluImm op:
{
@ -167,11 +256,11 @@ namespace ARMeilleure.Instructions
}
// ARM32 helpers.
private static Operand GetMShiftedByImmediate(ArmEmitterContext context, OpCode32AluRsImm op, bool setCarry)
public static Operand GetMShiftedByImmediate(ArmEmitterContext context, OpCode32AluRsImm op, bool setCarry)
{
Operand m = GetIntA32(context, op.Rm);
int shift = op.Imm;
int shift = op.Immediate;
if (shift == 0)
{
@ -193,7 +282,7 @@ namespace ARMeilleure.Instructions
case ShiftType.Lsr: m = GetLsrC(context, m, setCarry, shift); break;
case ShiftType.Asr: m = GetAsrC(context, m, setCarry, shift); break;
case ShiftType.Ror:
if (op.Imm != 0)
if (op.Immediate != 0)
{
m = GetRorC(context, m, setCarry, shift);
}
@ -208,8 +297,74 @@ namespace ARMeilleure.Instructions
return m;
}
private static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
public static Operand GetMShiftedByReg(ArmEmitterContext context, OpCode32AluRsReg op, bool setCarry)
{
Operand m = GetIntA32(context, op.Rm);
Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs));
Operand shiftIsZero = context.ICompareEqual(s, Const(0));
Operand zeroResult = m;
Operand shiftResult = m;
setCarry &= op.SetFlags;
switch (op.ShiftType)
{
case ShiftType.Lsl: shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); break;
case ShiftType.Lsr: shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); break;
case ShiftType.Asr: shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); break;
case ShiftType.Ror: shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); break;
}
return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult);
}
public static void EmitIfHelper(ArmEmitterContext context, Operand boolValue, Action action, bool expected = true)
{
Debug.Assert(boolValue.Type == OperandType.I32);
Operand endLabel = Label();
if (expected)
{
context.BranchIfFalse(endLabel, boolValue);
}
else
{
context.BranchIfTrue(endLabel, boolValue);
}
action();
context.MarkLabel(endLabel);
}
public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
Operand result = context.ShiftLeft(m, shift);
if (setCarry)
{
EmitIfHelper(context, shiftIsZero, () =>
{
Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift));
cOut = context.BitwiseAnd(cOut, Const(1));
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
SetFlag(context, PState.CFlag, cOut);
}, false);
}
return context.ConditionalSelect(shiftLarge, Const(0), result);
}
public static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
{
Debug.Assert(m.Type == OperandType.I32);
if ((uint)shift > 32)
{
return GetShiftByMoreThan32(context, setCarry);
@ -238,8 +393,32 @@ namespace ARMeilleure.Instructions
}
}
private static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32));
Operand result = context.ShiftRightUI(m, shift);
if (setCarry)
{
EmitIfHelper(context, shiftIsZero, () =>
{
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
cOut = context.BitwiseAnd(cOut, Const(1));
cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut);
SetFlag(context, PState.CFlag, cOut);
}, false);
}
return context.ConditionalSelect(shiftLarge, Const(0), result);
}
public static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
{
Debug.Assert(m.Type == OperandType.I32);
if ((uint)shift > 32)
{
return GetShiftByMoreThan32(context, setCarry);
@ -274,8 +453,45 @@ namespace ARMeilleure.Instructions
return Const(0);
}
private static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
Operand l32Result;
Operand ge32Result;
Operand less32 = context.ICompareLess(shift, Const(32));
ge32Result = context.ShiftRightSI(m, Const(31));
if (setCarry)
{
EmitIfHelper(context, context.BitwiseOr(less32, shiftIsZero), () =>
{
SetCarryMLsb(context, ge32Result);
}, false);
}
l32Result = context.ShiftRightSI(m, shift);
if (setCarry)
{
EmitIfHelper(context, context.BitwiseAnd(less32, context.BitwiseNot(shiftIsZero)), () =>
{
Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1)));
cOut = context.BitwiseAnd(cOut, Const(1));
SetFlag(context, PState.CFlag, cOut);
});
}
return context.ConditionalSelect(less32, l32Result, ge32Result);
}
public static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
{
Debug.Assert(m.Type == OperandType.I32);
if ((uint)shift >= 32)
{
m = context.ShiftRightSI(m, Const(31));
@ -298,8 +514,28 @@ namespace ARMeilleure.Instructions
}
}
private static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero)
{
Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32);
shift = context.BitwiseAnd(shift, Const(0x1f));
m = context.RotateRight(m, shift);
if (setCarry)
{
EmitIfHelper(context, shiftIsZero, () =>
{
SetCarryMMsb(context, m);
}, false);
}
return m;
}
public static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift)
{
Debug.Assert(m.Type == OperandType.I32);
shift &= 0x1f;
m = context.RotateRight(m, Const(shift));
@ -312,8 +548,10 @@ namespace ARMeilleure.Instructions
return m;
}
private static Operand GetRrxC(ArmEmitterContext context, Operand m, bool setCarry)
public static Operand GetRrxC(ArmEmitterContext context, Operand m, bool setCarry)
{
Debug.Assert(m.Type == OperandType.I32);
// Rotate right by 1 with carry.
Operand cIn = context.Copy(GetFlag(PState.CFlag));
@ -331,16 +569,22 @@ namespace ARMeilleure.Instructions
private static void SetCarryMLsb(ArmEmitterContext context, Operand m)
{
Debug.Assert(m.Type == OperandType.I32);
SetFlag(context, PState.CFlag, context.BitwiseAnd(m, Const(1)));
}
private static void SetCarryMMsb(ArmEmitterContext context, Operand m)
{
Debug.Assert(m.Type == OperandType.I32);
SetFlag(context, PState.CFlag, context.ShiftRightUI(m, Const(31)));
}
private static void SetCarryMShrOut(ArmEmitterContext context, Operand m, int shift)
{
Debug.Assert(m.Type == OperandType.I32);
Operand cOut = context.ShiftRightUI(m, Const(shift - 1));
cOut = context.BitwiseAnd(cOut, Const(1));

View file

@ -0,0 +1,36 @@
using ARMeilleure.Decoders;
using ARMeilleure.Translation;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Svc(ArmEmitterContext context)
{
EmitExceptionCall(context, NativeInterface.SupervisorCall);
}
public static void Trap(ArmEmitterContext context)
{
EmitExceptionCall(context, NativeInterface.Break);
}
private static void EmitExceptionCall(ArmEmitterContext context, _Void_U64_S32 func)
{
OpCode32Exception op = (OpCode32Exception)context.CurrOp;
context.StoreToContext();
context.Call(func, Const(op.Address), Const(op.Id));
context.LoadFromContext();
if (context.CurrBlock.Next == null)
{
context.Return(Const(op.Address + 4));
}
}
}
}

View file

@ -1,7 +1,9 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitFlowHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -20,7 +22,6 @@ namespace ARMeilleure.Instructions
else
{
context.StoreToContext();
context.Return(Const(op.Immediate));
}
}
@ -35,15 +36,6 @@ namespace ARMeilleure.Instructions
Blx(context, x: true);
}
public static void Bx(ArmEmitterContext context)
{
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
context.StoreToContext();
EmitBxWritePc(context, GetIntA32(context, op.Rm));
}
private static void Blx(ArmEmitterContext context, bool x)
{
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
@ -53,10 +45,10 @@ namespace ARMeilleure.Instructions
bool isThumb = IsThumb(context.CurrOp);
uint currentPc = isThumb
? op.GetPc() | 1
: op.GetPc() - 4;
? pc | 1
: pc - 4;
SetIntOrSP(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
// 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.
@ -67,5 +59,37 @@ namespace ARMeilleure.Instructions
InstEmitFlowHelper.EmitCall(context, (ulong)op.Immediate);
}
public static void Blxr(ArmEmitterContext context)
{
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
uint pc = op.GetPc();
Operand addr = GetIntA32(context, op.Rm);
Operand bitOne = context.BitwiseAnd(addr, Const(1));
addr = context.BitwiseOr(addr, Const((int)CallFlag)); // Set call flag.
bool isThumb = IsThumb(context.CurrOp);
uint currentPc = isThumb
? pc | 1
: pc - 4;
SetIntA32(context, GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr), Const(currentPc));
SetFlag(context, PState.TFlag, bitOne);
context.Return(addr); // Call.
}
public static void Bx(ArmEmitterContext context)
{
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
context.StoreToContext();
EmitBxWritePc(context, GetIntA32(context, op.Rm));
}
}
}

View file

@ -43,10 +43,15 @@ namespace ARMeilleure.Instructions
}
else
{
return GetIntOrSP(context, GetRegisterAlias(context.Mode, regIndex));
return Register(GetRegisterAlias(context.Mode, regIndex), RegisterType.Integer, OperandType.I32);
}
}
public static Operand GetVecA32(int regIndex)
{
return Register(regIndex, RegisterType.Vector, OperandType.V128);
}
public static void SetIntA32(ArmEmitterContext context, int regIndex, Operand value)
{
if (regIndex == RegisterAlias.Aarch32Pc)
@ -57,7 +62,13 @@ namespace ARMeilleure.Instructions
}
else
{
SetIntOrSP(context, GetRegisterAlias(context.Mode, regIndex), value);
if (value.Type == OperandType.I64)
{
value = context.ConvertI64ToI32(value);
}
Operand reg = Register(GetRegisterAlias(context.Mode, regIndex), RegisterType.Integer, OperandType.I32);
context.Copy(reg, value);
}
}
@ -143,11 +154,12 @@ namespace ARMeilleure.Instructions
context.BranchIfTrue(lblArmMode, mode);
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(pc, Const(~1))));
// Make this count as a call, the translator will ignore the low bit for the address.
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(pc, Const((int)InstEmitFlowHelper.CallFlag))));
context.MarkLabel(lblArmMode);
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseAnd(pc, Const(~3))));
context.Return(context.ZeroExtend32(OperandType.I64, context.BitwiseOr(context.BitwiseAnd(pc, Const(~3)), Const((int)InstEmitFlowHelper.CallFlag))));
}
public static Operand GetIntOrZR(ArmEmitterContext context, int regIndex)
@ -208,11 +220,21 @@ namespace ARMeilleure.Instructions
return Register((int)stateFlag, RegisterType.Flag, OperandType.I32);
}
public static Operand GetFpFlag(FPState stateFlag)
{
return Register((int)stateFlag, RegisterType.FpFlag, OperandType.I32);
}
public static void SetFlag(ArmEmitterContext context, PState stateFlag, Operand value)
{
context.Copy(GetFlag(stateFlag), value);
context.MarkFlagSet(stateFlag);
}
public static void SetFpFlag(ArmEmitterContext context, FPState stateFlag, Operand value)
{
context.Copy(GetFpFlag(stateFlag), value);
}
}
}

View file

@ -23,6 +23,8 @@ namespace ARMeilleure.Instructions
Store = 0,
Signed = 1,
Load = 2,
Ordered = 4,
Exclusive = 8,
LoadZx = Load,
LoadSx = Load | Signed,
@ -95,7 +97,7 @@ namespace ARMeilleure.Instructions
{
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand n = context.Copy(GetIntA32(context, op.Rn));
Operand baseAddress = context.Add(n, Const(op.Offset));
@ -152,14 +154,15 @@ namespace ARMeilleure.Instructions
OpCode32Mem op = (OpCode32Mem)context.CurrOp;
Operand n = context.Copy(GetIntA32(context, op.Rn));
Operand m = GetMemM(context, setCarry: false);
Operand temp = null;
if (op.Index || op.WBack)
{
temp = op.Add
? context.Add (n, Const(op.Immediate))
: context.Subtract(n, Const(op.Immediate));
? context.Add (n, m)
: context.Subtract(n, m);
}
if (op.WBack)

View file

@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitMemoryExHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
@ -66,7 +67,7 @@ namespace ARMeilleure.Instructions
// method to read 128-bits atomically.
if (op.Size == 2)
{
Operand value = EmitLoad(context, address, exclusive, 3);
Operand value = EmitLoadExclusive(context, address, exclusive, 3);
Operand valueLow = context.ConvertI64ToI32(value);
@ -79,7 +80,7 @@ namespace ARMeilleure.Instructions
}
else if (op.Size == 3)
{
Operand value = EmitLoad(context, address, exclusive, 4);
Operand value = EmitLoadExclusive(context, address, exclusive, 4);
Operand valueLow = context.VectorExtract(OperandType.I64, value, 0);
Operand valueHigh = context.VectorExtract(OperandType.I64, value, 1);
@ -95,46 +96,11 @@ namespace ARMeilleure.Instructions
else
{
// 8, 16, 32 or 64-bits (non-pairwise) load.
Operand value = EmitLoad(context, address, exclusive, op.Size);
Operand value = EmitLoadExclusive(context, address, exclusive, op.Size);
SetIntOrZR(context, op.Rt, value);
}
}
private static Operand EmitLoad(
ArmEmitterContext context,
Operand address,
bool exclusive,
int size)
{
Delegate fallbackMethodDlg = null;
if (exclusive)
{
switch (size)
{
case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByteExclusive); break;
case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16Exclusive); break;
case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32Exclusive); break;
case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64Exclusive); break;
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128Exclusive); break;
}
}
else
{
switch (size)
{
case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break;
case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16); break;
case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32); break;
case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64); break;
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break;
}
}
return context.Call(fallbackMethodDlg, address);
}
public static void Pfrm(ArmEmitterContext context)
{
// Memory Prefetch, execute as no-op.
@ -192,11 +158,11 @@ namespace ARMeilleure.Instructions
value = context.VectorInsert(value, t2, 1);
}
s = EmitStore(context, address, value, exclusive, op.Size + 1);
s = EmitStoreExclusive(context, address, value, exclusive, op.Size + 1);
}
else
{
s = EmitStore(context, address, t, exclusive, op.Size);
s = EmitStoreExclusive(context, address, t, exclusive, op.Size);
}
if (s != null)
@ -207,50 +173,6 @@ namespace ARMeilleure.Instructions
}
}
private static Operand EmitStore(
ArmEmitterContext context,
Operand address,
Operand value,
bool exclusive,
int size)
{
if (size < 3)
{
value = context.ConvertI64ToI32(value);
}
Delegate fallbackMethodDlg = null;
if (exclusive)
{
switch (size)
{
case 0: fallbackMethodDlg = new _S32_U64_U8 (NativeInterface.WriteByteExclusive); break;
case 1: fallbackMethodDlg = new _S32_U64_U16 (NativeInterface.WriteUInt16Exclusive); break;
case 2: fallbackMethodDlg = new _S32_U64_U32 (NativeInterface.WriteUInt32Exclusive); break;
case 3: fallbackMethodDlg = new _S32_U64_U64 (NativeInterface.WriteUInt64Exclusive); break;
case 4: fallbackMethodDlg = new _S32_U64_V128(NativeInterface.WriteVector128Exclusive); break;
}
return context.Call(fallbackMethodDlg, address, value);
}
else
{
switch (size)
{
case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break;
case 1: fallbackMethodDlg = new _Void_U64_U16 (NativeInterface.WriteUInt16); break;
case 2: fallbackMethodDlg = new _Void_U64_U32 (NativeInterface.WriteUInt32); break;
case 3: fallbackMethodDlg = new _Void_U64_U64 (NativeInterface.WriteUInt64); break;
case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break;
}
context.Call(fallbackMethodDlg, address, value);
return null;
}
}
private static void EmitBarrier(ArmEmitterContext context)
{
// Note: This barrier is most likely not necessary, and probably

View file

@ -0,0 +1,240 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitMemoryExHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Clrex(ArmEmitterContext context)
{
context.Call(new _Void(NativeInterface.ClearExclusive));
}
public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);
public static void Dsb(ArmEmitterContext context) => EmitBarrier(context);
public static void Ldrex(ArmEmitterContext context)
{
EmitExLoadOrStore(context, WordSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
}
public static void Ldrexb(ArmEmitterContext context)
{
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
}
public static void Ldrexd(ArmEmitterContext context)
{
EmitExLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
}
public static void Ldrexh(ArmEmitterContext context)
{
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive);
}
public static void Lda(ArmEmitterContext context)
{
EmitExLoadOrStore(context, WordSizeLog2, AccessType.LoadZx | AccessType.Ordered);
}
public static void Ldab(ArmEmitterContext context)
{
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx | AccessType.Ordered);
}
public static void Ldaex(ArmEmitterContext context)
{
EmitExLoadOrStore(context, WordSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
}
public static void Ldaexb(ArmEmitterContext context)
{
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
}
public static void Ldaexd(ArmEmitterContext context)
{
EmitExLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
}
public static void Ldaexh(ArmEmitterContext context)
{
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx | AccessType.Exclusive | AccessType.Ordered);
}
public static void Ldah(ArmEmitterContext context)
{
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx | AccessType.Ordered);
}
// Stores.
public static void Strex(ArmEmitterContext context)
{
EmitExLoadOrStore(context, WordSizeLog2, AccessType.Store | AccessType.Exclusive);
}
public static void Strexb(ArmEmitterContext context)
{
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.Store | AccessType.Exclusive);
}
public static void Strexd(ArmEmitterContext context)
{
EmitExLoadOrStore(context, DWordSizeLog2, AccessType.Store | AccessType.Exclusive);
}
public static void Strexh(ArmEmitterContext context)
{
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Exclusive);
}
public static void Stl(ArmEmitterContext context)
{
EmitExLoadOrStore(context, WordSizeLog2, AccessType.Store | AccessType.Ordered);
}
public static void Stlb(ArmEmitterContext context)
{
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.Store | AccessType.Ordered);
}
public static void Stlex(ArmEmitterContext context)
{
EmitExLoadOrStore(context, WordSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
}
public static void Stlexb(ArmEmitterContext context)
{
EmitExLoadOrStore(context, ByteSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
}
public static void Stlexd(ArmEmitterContext context)
{
EmitExLoadOrStore(context, DWordSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
}
public static void Stlexh(ArmEmitterContext context)
{
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Exclusive | AccessType.Ordered);
}
public static void Stlh(ArmEmitterContext context)
{
EmitExLoadOrStore(context, HWordSizeLog2, AccessType.Store | AccessType.Ordered);
}
private static void EmitExLoadOrStore(ArmEmitterContext context, int size, AccessType accType)
{
IOpCode32MemEx op = (IOpCode32MemEx)context.CurrOp;
Operand address = context.Copy(GetIntA32(context, op.Rn));
var exclusive = (accType & AccessType.Exclusive) != 0;
var ordered = (accType & AccessType.Ordered) != 0;
if (ordered)
{
EmitBarrier(context);
}
if ((accType & AccessType.Load) != 0)
{
if (size == DWordSizeLog2)
{
// Keep loads atomic - make the call to get the whole region and then decompose it into parts
// for the registers.
Operand value = EmitLoadExclusive(context, address, exclusive, size);
Operand valueLow = context.ConvertI64ToI32(value);
valueLow = context.ZeroExtend32(OperandType.I64, valueLow);
Operand valueHigh = context.ShiftRightUI(value, Const(32));
Operand lblBigEndian = Label();
Operand lblEnd = Label();
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
SetIntA32(context, op.Rt, valueLow);
SetIntA32(context, op.Rt | 1, valueHigh);
context.Branch(lblEnd);
context.MarkLabel(lblBigEndian);
SetIntA32(context, op.Rt | 1, valueLow);
SetIntA32(context, op.Rt, valueHigh);
context.MarkLabel(lblEnd);
}
else
{
SetIntA32(context, op.Rt, EmitLoadExclusive(context, address, exclusive, size));
}
}
else
{
if (size == DWordSizeLog2)
{
// Split the result into 2 words (based on endianness)
Operand lo = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt));
Operand hi = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt | 1));
Operand lblBigEndian = Label();
Operand lblEnd = Label();
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
Operand leResult = context.BitwiseOr(lo, context.ShiftLeft(hi, Const(32)));
Operand leS = EmitStoreExclusive(context, address, leResult, exclusive, size);
if (exclusive)
{
SetIntA32(context, op.Rd, leS);
}
context.Branch(lblEnd);
context.MarkLabel(lblBigEndian);
Operand beResult = context.BitwiseOr(hi, context.ShiftLeft(lo, Const(32)));
Operand beS = EmitStoreExclusive(context, address, beResult, exclusive, size);
if (exclusive)
{
SetIntA32(context, op.Rd, beS);
}
context.MarkLabel(lblEnd);
}
else
{
Operand s = EmitStoreExclusive(context, address, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt)), exclusive, size);
// This is only needed for exclusive stores. The function returns 0
// when the store is successful, and 1 otherwise.
if (exclusive)
{
SetIntA32(context, op.Rd, s);
}
}
}
}
private static void EmitBarrier(ArmEmitterContext context)
{
// Note: This barrier is most likely not necessary, and probably
// doesn't make any difference since we need to do a ton of stuff
// (software MMU emulation) to read or write anything anyway.
}
}
}

View file

@ -0,0 +1,87 @@
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
namespace ARMeilleure.Instructions
{
static class InstEmitMemoryExHelper
{
public static Operand EmitLoadExclusive(
ArmEmitterContext context,
Operand address,
bool exclusive,
int size)
{
Delegate fallbackMethodDlg = null;
if (exclusive)
{
switch (size)
{
case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByteExclusive); break;
case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16Exclusive); break;
case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32Exclusive); break;
case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64Exclusive); break;
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128Exclusive); break;
}
}
else
{
switch (size)
{
case 0: fallbackMethodDlg = new _U8_U64(NativeInterface.ReadByte); break;
case 1: fallbackMethodDlg = new _U16_U64(NativeInterface.ReadUInt16); break;
case 2: fallbackMethodDlg = new _U32_U64(NativeInterface.ReadUInt32); break;
case 3: fallbackMethodDlg = new _U64_U64(NativeInterface.ReadUInt64); break;
case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break;
}
}
return context.Call(fallbackMethodDlg, address);
}
public static Operand EmitStoreExclusive(
ArmEmitterContext context,
Operand address,
Operand value,
bool exclusive,
int size)
{
if (size < 3)
{
value = context.ConvertI64ToI32(value);
}
Delegate fallbackMethodDlg = null;
if (exclusive)
{
switch (size)
{
case 0: fallbackMethodDlg = new _S32_U64_U8(NativeInterface.WriteByteExclusive); break;
case 1: fallbackMethodDlg = new _S32_U64_U16(NativeInterface.WriteUInt16Exclusive); break;
case 2: fallbackMethodDlg = new _S32_U64_U32(NativeInterface.WriteUInt32Exclusive); break;
case 3: fallbackMethodDlg = new _S32_U64_U64(NativeInterface.WriteUInt64Exclusive); break;
case 4: fallbackMethodDlg = new _S32_U64_V128(NativeInterface.WriteVector128Exclusive); break;
}
return context.Call(fallbackMethodDlg, address, value);
}
else
{
switch (size)
{
case 0: fallbackMethodDlg = new _Void_U64_U8(NativeInterface.WriteByte); break;
case 1: fallbackMethodDlg = new _Void_U64_U16(NativeInterface.WriteUInt16); break;
case 2: fallbackMethodDlg = new _Void_U64_U32(NativeInterface.WriteUInt32); break;
case 3: fallbackMethodDlg = new _Void_U64_U64(NativeInterface.WriteUInt64); break;
case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break;
}
context.Call(fallbackMethodDlg, address, value);
return null;
}
}
}
}

View file

@ -53,7 +53,7 @@ namespace ARMeilleure.Instructions
if (!isSimd)
{
Operand value = GetIntOrZR(context, rt);
Operand value = GetInt(context, rt);
if (ext == Extension.Sx32 || ext == Extension.Sx64)
{
@ -67,7 +67,7 @@ namespace ARMeilleure.Instructions
}
}
SetIntOrZR(context, rt, value);
SetInt(context, rt, value);
}
}
@ -505,5 +505,68 @@ namespace ARMeilleure.Instructions
SetIntOrZR(context, rt, value);
}
}
// ARM32 helpers.
public static Operand GetMemM(ArmEmitterContext context, bool setCarry = true)
{
switch (context.CurrOp)
{
case OpCode32MemRsImm op: return GetMShiftedByImmediate(context, op, setCarry);
case OpCode32MemReg op: return GetIntA32(context, op.Rm);
case OpCode32Mem op: return Const(op.Immediate);
case OpCode32SimdMemImm op: return Const(op.Immediate);
default: throw InvalidOpCodeType(context.CurrOp);
}
}
private static Exception InvalidOpCodeType(OpCode opCode)
{
return new InvalidOperationException($"Invalid OpCode type \"{opCode?.GetType().Name ?? "null"}\".");
}
public static Operand GetMShiftedByImmediate(ArmEmitterContext context, OpCode32MemRsImm op, bool setCarry)
{
Operand m = GetIntA32(context, op.Rm);
int shift = op.Immediate;
if (shift == 0)
{
switch (op.ShiftType)
{
case ShiftType.Lsr: shift = 32; break;
case ShiftType.Asr: shift = 32; break;
case ShiftType.Ror: shift = 1; break;
}
}
if (shift != 0)
{
setCarry &= false;
switch (op.ShiftType)
{
case ShiftType.Lsl: m = InstEmitAluHelper.GetLslC(context, m, setCarry, shift); break;
case ShiftType.Lsr: m = InstEmitAluHelper.GetLsrC(context, m, setCarry, shift); break;
case ShiftType.Asr: m = InstEmitAluHelper.GetAsrC(context, m, setCarry, shift); break;
case ShiftType.Ror:
if (op.Immediate != 0)
{
m = InstEmitAluHelper.GetRorC(context, m, setCarry, shift);
}
else
{
m = InstEmitAluHelper.GetRrxC(context, m, setCarry);
}
break;
}
}
return m;
}
}
}

View file

@ -0,0 +1,290 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitAluHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
[Flags]
private enum MullFlags
{
Subtract = 1,
Add = 1 << 1,
Signed = 1 << 2,
SignedAdd = Signed | Add,
SignedSubtract = Signed | Subtract
}
public static void Mla(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand a = GetIntA32(context, op.Ra);
Operand res = context.Add(a, context.Multiply(n, m));
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitAluStore(context, res);
}
public static void Mls(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetAluN(context);
Operand m = GetAluM(context);
Operand a = GetIntA32(context, op.Ra);
Operand res = context.Subtract(a, context.Multiply(n, m));
EmitAluStore(context, res);
}
public static void Smull(ArmEmitterContext context)
{
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
Operand res = context.Multiply(n, m);
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo);
}
public static void Smmla(ArmEmitterContext context)
{
EmitSmmul(context, MullFlags.SignedAdd);
}
public static void Smmls(ArmEmitterContext context)
{
EmitSmmul(context, MullFlags.SignedSubtract);
}
public static void Smmul(ArmEmitterContext context)
{
EmitSmmul(context, MullFlags.Signed);
}
private static void EmitSmmul(ArmEmitterContext context, MullFlags flags)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rn));
Operand m = context.SignExtend32(OperandType.I64, GetIntA32(context, op.Rm));
Operand res = context.Multiply(n, m);
if (flags.HasFlag(MullFlags.Add) && op.Ra != 0xf)
{
res = context.Add(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
}
else if (flags.HasFlag(MullFlags.Subtract))
{
res = context.Subtract(context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Ra)), Const(32)), res);
}
if (op.R)
{
res = context.Add(res, Const(0x80000000L));
}
Operand hi = context.ConvertI64ToI32(context.ShiftRightSI(res, Const(32)));
EmitGenericAluStoreA32(context, op.Rd, false, hi);
}
public static void Smlab(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
if (op.NHigh)
{
n = context.SignExtend16(OperandType.I32, context.ShiftRightUI(n, Const(16)));
}
else
{
n = context.SignExtend16(OperandType.I32, n);
}
if (op.MHigh)
{
m = context.SignExtend16(OperandType.I32, context.ShiftRightUI(m, Const(16)));
}
else
{
m = context.SignExtend16(OperandType.I32, m);
}
Operand res = context.Multiply(n, m);
Operand a = GetIntA32(context, op.Ra);
res = context.Add(res, a);
// TODO: set Q flag when last addition overflows (saturation)?
EmitGenericAluStoreA32(context, op.Rd, false, res);
}
public static void Smlal(ArmEmitterContext context)
{
EmitMlal(context, true);
}
public static void Smlalh(ArmEmitterContext context)
{
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
if (op.NHigh)
{
n = context.SignExtend16(OperandType.I64, context.ShiftRightUI(n, Const(16)));
}
else
{
n = context.SignExtend16(OperandType.I64, n);
}
if (op.MHigh)
{
m = context.SignExtend16(OperandType.I64, context.ShiftRightUI(m, Const(16)));
}
else
{
m = context.SignExtend16(OperandType.I64, m);
}
Operand res = context.Multiply(n, m);
Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
res = context.Add(res, toAdd);
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res);
EmitGenericAluStoreA32(context, op.RdHi, false, hi);
EmitGenericAluStoreA32(context, op.RdLo, false, lo);
}
public static void Smulh(ArmEmitterContext context)
{
OpCode32AluMla op = (OpCode32AluMla)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
if (op.NHigh)
{
n = context.ShiftRightSI(n, Const(16));
}
else
{
n = context.SignExtend16(OperandType.I32, n);
}
if (op.MHigh)
{
m = context.ShiftRightSI(m, Const(16));
}
else
{
m = context.SignExtend16(OperandType.I32, m);
}
Operand res = context.Multiply(n, m);
EmitGenericAluStoreA32(context, op.Rd, false, res);
}
public static void Umlal(ArmEmitterContext context)
{
EmitMlal(context, false);
}
public static void Umull(ArmEmitterContext context)
{
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
Operand n = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rn));
Operand m = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rm));
Operand res = context.Multiply(n, m);
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo);
}
public static void EmitMlal(ArmEmitterContext context, bool signed)
{
OpCode32AluUmull op = (OpCode32AluUmull)context.CurrOp;
Operand n = GetIntA32(context, op.Rn);
Operand m = GetIntA32(context, op.Rm);
if (signed)
{
n = context.SignExtend32(OperandType.I64, n);
m = context.SignExtend32(OperandType.I64, m);
}
else
{
n = context.ZeroExtend32(OperandType.I64, n);
m = context.ZeroExtend32(OperandType.I64, m);
}
Operand res = context.Multiply(n, m);
Operand toAdd = context.ShiftLeft(context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdHi)), Const(32));
toAdd = context.BitwiseOr(toAdd, context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.RdLo)));
res = context.Add(res, toAdd);
Operand hi = context.ConvertI64ToI32(context.ShiftRightUI(res, Const(32)));
Operand lo = context.ConvertI64ToI32(res);
if (op.SetFlags)
{
EmitNZFlagsCheck(context, res);
}
EmitGenericAluStoreA32(context, op.RdHi, op.SetFlags, hi);
EmitGenericAluStoreA32(context, op.RdLo, op.SetFlags, lo);
}
}
}

View file

@ -0,0 +1,634 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitFlowHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Vabs_S(ArmEmitterContext context)
{
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Abs, Math.Abs, op1));
}
public static void Vabs_V(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
EmitVectorUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Abs, Math.Abs, op1));
}
else
{
EmitVectorUnaryOpSx32(context, (op1) => EmitAbs(context, op1));
}
}
private static Operand EmitAbs(ArmEmitterContext context, Operand value)
{
Operand isPositive = context.ICompareGreaterOrEqual(value, Const(value.Type, 0));
return context.ConditionalSelect(isPositive, value, context.Negate(value));
}
public static void Vadd_S(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitScalarBinaryOpF32(context, (op1, op2) => context.Add(op1, op2));
}
else
{
EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPAdd, SoftFloat64.FPAdd, op1, op2));
}
}
public static void Vadd_V(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitVectorBinaryOpF32(context, (op1, op2) => context.Add(op1, op2));
}
else
{
EmitVectorBinaryOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPAddFpscr, SoftFloat64.FPAddFpscr, op1, op2));
}
}
public static void Vadd_I(ArmEmitterContext context)
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.Add(op1, op2));
}
public static void Vdup(ArmEmitterContext context)
{
OpCode32SimdDupGP op = (OpCode32SimdDupGP)context.CurrOp;
Operand insert = GetIntA32(context, op.Rt);
// Zero extend into an I64, then replicate. Saves the most time over elementwise inserts.
switch (op.Size)
{
case 2:
insert = context.Multiply(context.ZeroExtend32(OperandType.I64, insert), Const(0x0000000100000001u));
break;
case 1:
insert = context.Multiply(context.ZeroExtend16(OperandType.I64, insert), Const(0x0001000100010001u));
break;
case 0:
insert = context.Multiply(context.ZeroExtend8(OperandType.I64, insert), Const(0x0101010101010101u));
break;
default:
throw new InvalidOperationException("Unknown Vdup Size.");
}
InsertScalar(context, op.Vd, insert);
if (op.Q)
{
InsertScalar(context, op.Vd + 1, insert);
}
}
public static void Vdup_1(ArmEmitterContext context)
{
OpCode32SimdDupElem op = (OpCode32SimdDupElem)context.CurrOp;
Operand insert = EmitVectorExtractZx32(context, op.Vm >> 1, ((op.Vm & 1) << (3 - op.Size)) + op.Index, op.Size);
// Zero extend into an I64, then replicate. Saves the most time over elementwise inserts.
switch (op.Size)
{
case 2:
insert = context.Multiply(context.ZeroExtend32(OperandType.I64, insert), Const(0x0000000100000001u));
break;
case 1:
insert = context.Multiply(context.ZeroExtend16(OperandType.I64, insert), Const(0x0001000100010001u));
break;
case 0:
insert = context.Multiply(context.ZeroExtend8(OperandType.I64, insert), Const(0x0101010101010101u));
break;
default:
throw new InvalidOperationException("Unknown Vdup Size.");
}
InsertScalar(context, op.Vd, insert);
if (op.Q)
{
InsertScalar(context, op.Vd | 1, insert);
}
}
public static void Vext(ArmEmitterContext context)
{
OpCode32SimdExt op = (OpCode32SimdExt)context.CurrOp;
int elems = op.GetBytesCount();
int byteOff = op.Immediate;
Operand res = GetVecA32(op.Qd);
for (int index = 0; index < elems; index++)
{
Operand extract;
if (byteOff >= elems)
{
extract = EmitVectorExtractZx32(context, op.Qm, op.Im + (byteOff - elems), op.Size);
}
else
{
extract = EmitVectorExtractZx32(context, op.Qn, op.In + byteOff, op.Size);
}
byteOff++;
res = EmitVectorInsert(context, res, extract, op.Id + index, op.Size);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vmov_S(ArmEmitterContext context)
{
EmitScalarUnaryOpF32(context, (op1) => op1);
}
public static void Vmovn(ArmEmitterContext context)
{
EmitVectorUnaryNarrowOp32(context, (op1) => op1);
}
public static void Vneg_S(ArmEmitterContext context)
{
EmitScalarUnaryOpF32(context, (op1) => context.Negate(op1));
}
public static void Vnmul_S(ArmEmitterContext context)
{
EmitScalarBinaryOpF32(context, (op1, op2) => context.Negate(context.Multiply(op1, op2)));
}
public static void Vnmla_S(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
{
return context.Negate(context.Add(op1, context.Multiply(op2, op3)));
});
}
else
{
EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
{
return EmitSoftFloatCall(context, SoftFloat32.FPNegMulAdd, SoftFloat64.FPNegMulAdd, op1, op2, op3);
});
}
}
public static void Vnmls_S(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
{
return context.Add(context.Negate(op1), context.Multiply(op2, op3));
});
}
else
{
EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
{
return EmitSoftFloatCall(context, SoftFloat32.FPNegMulSub, SoftFloat64.FPNegMulSub, op1, op2, op3);
});
}
}
public static void Vneg_V(ArmEmitterContext context)
{
if ((context.CurrOp as OpCode32Simd).F)
{
EmitVectorUnaryOpF32(context, (op1) => context.Negate(op1));
}
else
{
EmitVectorUnaryOpSx32(context, (op1) => context.Negate(op1));
}
}
public static void Vdiv_S(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitScalarBinaryOpF32(context, (op1, op2) => context.Divide(op1, op2));
}
else
{
EmitScalarBinaryOpF32(context, (op1, op2) =>
{
return EmitSoftFloatCall(context, SoftFloat32.FPDiv, SoftFloat64.FPDiv, op1, op2);
});
}
}
public static void Vmaxnm_S(ArmEmitterContext context)
{
EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPMaxNum, SoftFloat64.FPMaxNum, op1, op2));
}
public static void Vmaxnm_V(ArmEmitterContext context)
{
EmitVectorBinaryOpSx32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMaxNumFpscr, SoftFloat64.FPMaxNumFpscr, op1, op2));
}
public static void Vminnm_S(ArmEmitterContext context)
{
EmitScalarBinaryOpF32(context, (op1, op2) => EmitSoftFloatCall(context, SoftFloat32.FPMinNum, SoftFloat64.FPMinNum, op1, op2));
}
public static void Vminnm_V(ArmEmitterContext context)
{
EmitVectorBinaryOpSx32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMinNumFpscr, SoftFloat64.FPMinNumFpscr, op1, op2));
}
public static void Vmax_V(ArmEmitterContext context)
{
EmitVectorBinaryOpF32(context, (op1, op2) =>
{
return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMaxFpscr, SoftFloat64.FPMaxFpscr, op1, op2);
});
}
public static void Vmax_I(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
if (op.U)
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.ConditionalSelect(context.ICompareGreaterUI(op1, op2), op1, op2));
}
else
{
EmitVectorBinaryOpSx32(context, (op1, op2) => context.ConditionalSelect(context.ICompareGreater(op1, op2), op1, op2));
}
}
public static void Vmin_V(ArmEmitterContext context)
{
EmitVectorBinaryOpF32(context, (op1, op2) =>
{
return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMinFpscr, SoftFloat64.FPMinFpscr, op1, op2);
});
}
public static void Vmin_I(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
if (op.U)
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.ConditionalSelect(context.ICompareLessUI(op1, op2), op1, op2));
}
else
{
EmitVectorBinaryOpSx32(context, (op1, op2) => context.ConditionalSelect(context.ICompareLess(op1, op2), op1, op2));
}
}
public static void Vmul_S(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitScalarBinaryOpF32(context, (op1, op2) => context.Multiply(op1, op2));
}
else
{
EmitScalarBinaryOpF32(context, (op1, op2) =>
{
return EmitSoftFloatCall(context, SoftFloat32.FPMul, SoftFloat64.FPMul, op1, op2);
});
}
}
public static void Vmul_V(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitVectorBinaryOpF32(context, (op1, op2) => context.Multiply(op1, op2));
}
else
{
EmitVectorBinaryOpF32(context, (op1, op2) =>
{
return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulFpscr, SoftFloat64.FPMulFpscr, op1, op2);
});
}
}
public static void Vmul_I(ArmEmitterContext context)
{
if ((context.CurrOp as OpCode32SimdReg).U) throw new NotImplementedException("Polynomial mode not implemented");
EmitVectorBinaryOpSx32(context, (op1, op2) => context.Multiply(op1, op2));
}
public static void Vmul_1(ArmEmitterContext context)
{
OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
if (op.F)
{
if (Optimizations.FastFP)
{
EmitVectorByScalarOpF32(context, (op1, op2) => context.Multiply(op1, op2));
}
else
{
EmitVectorByScalarOpF32(context, (op1, op2) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulFpscr, SoftFloat64.FPMulFpscr, op1, op2));
}
}
else
{
EmitVectorByScalarOpI32(context, (op1, op2) => context.Multiply(op1, op2), false);
}
}
public static void Vmla_S(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
{
return context.Add(op1, context.Multiply(op2, op3));
});
}
else
{
EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
{
return EmitSoftFloatCall(context, SoftFloat32.FPMulAdd, SoftFloat64.FPMulAdd, op1, op2, op3);
});
}
}
public static void Vmla_V(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitVectorTernaryOpF32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3)));
}
else
{
EmitVectorTernaryOpF32(context, (op1, op2, op3) =>
{
return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulAddFpscr, SoftFloat64.FPMulAddFpscr, op1, op2, op3);
});
}
}
public static void Vmla_I(ArmEmitterContext context)
{
EmitVectorTernaryOpZx32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3)));
}
public static void Vmla_1(ArmEmitterContext context)
{
OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
if (op.F)
{
if (Optimizations.FastFP)
{
EmitVectorsByScalarOpF32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3)));
}
else
{
EmitVectorsByScalarOpF32(context, (op1, op2, op3) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulAddFpscr, SoftFloat64.FPMulAddFpscr, op1, op2, op3));
}
}
else
{
EmitVectorsByScalarOpI32(context, (op1, op2, op3) => context.Add(op1, context.Multiply(op2, op3)), false);
}
}
public static void Vmls_S(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
{
return context.Subtract(op1, context.Multiply(op2, op3));
});
}
else
{
EmitScalarTernaryOpF32(context, (op1, op2, op3) =>
{
return EmitSoftFloatCall(context, SoftFloat32.FPMulSub, SoftFloat64.FPMulSub, op1, op2, op3);
});
}
}
public static void Vmls_V(ArmEmitterContext context)
{
if (Optimizations.FastFP)
{
EmitVectorTernaryOpF32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3)));
}
else
{
EmitVectorTernaryOpF32(context, (op1, op2, op3) =>
{
return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulSubFpscr, SoftFloat64.FPMulSubFpscr, op1, op2, op3);
});
}
}
public static void Vmls_I(ArmEmitterContext context)
{
EmitVectorTernaryOpZx32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3)));
}
public static void Vmls_1(ArmEmitterContext context)
{
OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
if (op.F)
{
if (Optimizations.FastFP)
{
EmitVectorsByScalarOpF32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3)));
}
else
{
EmitVectorsByScalarOpF32(context, (op1, op2, op3) => EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPMulSubFpscr, SoftFloat64.FPMulSubFpscr, op1, op2, op3));
}
}
else
{
EmitVectorsByScalarOpI32(context, (op1, op2, op3) => context.Subtract(op1, context.Multiply(op2, op3)), false);
}
}
public static void Vpadd_V(ArmEmitterContext context)
{
EmitVectorPairwiseOpF32(context, (op1, op2) => context.Add(op1, op2));
}
public static void Vpadd_I(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
EmitVectorPairwiseOpI32(context, (op1, op2) => context.Add(op1, op2), !op.U);
}
public static void Vrev(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
EmitVectorUnaryOpZx32(context, (op1) =>
{
switch (op.Opc)
{
case 0:
switch (op.Size) // Swap bytes.
{
default:
return op1;
case 1:
return InstEmitAluHelper.EmitReverseBytes16_32Op(context, op1);
case 2:
case 3:
return context.ByteSwap(op1);
}
case 1:
switch (op.Size)
{
default:
return op1;
case 2:
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0xffff0000)), Const(16)),
context.ShiftLeft(context.BitwiseAnd(op1, Const(0x0000ffff)), Const(16)));
case 3:
return context.BitwiseOr(
context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0xffff000000000000ul)), Const(48)),
context.ShiftLeft(context.BitwiseAnd(op1, Const(0x000000000000fffful)), Const(48))),
context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0x0000ffff00000000ul)), Const(16)),
context.ShiftLeft(context.BitwiseAnd(op1, Const(0x00000000ffff0000ul)), Const(16))));
}
case 2:
// Swap upper and lower halves.
return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op1, Const(0xffffffff00000000ul)), Const(32)),
context.ShiftLeft(context.BitwiseAnd(op1, Const(0x00000000fffffffful)), Const(32)));
}
return op1;
});
}
public static void Vrecpe(ArmEmitterContext context)
{
OpCode32SimdSqrte op = (OpCode32SimdSqrte)context.CurrOp;
if (op.F)
{
EmitVectorUnaryOpF32(context, (op1) =>
{
return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPRecipEstimateFpscr, SoftFloat64.FPRecipEstimateFpscr, op1);
});
}
else
{
throw new NotImplementedException("Integer Vrecpe not currently implemented.");
}
}
public static void Vrecps(ArmEmitterContext context)
{
EmitVectorBinaryOpF32(context, (op1, op2) =>
{
return EmitSoftFloatCall(context, SoftFloat32.FPRecipStep, SoftFloat64.FPRecipStep, op1, op2);
});
}
public static void Vrsqrte(ArmEmitterContext context)
{
OpCode32SimdSqrte op = (OpCode32SimdSqrte)context.CurrOp;
if (op.F)
{
EmitVectorUnaryOpF32(context, (op1) =>
{
return EmitSoftFloatCallDefaultFpscr(context, SoftFloat32.FPRSqrtEstimateFpscr, SoftFloat64.FPRSqrtEstimateFpscr, op1);
});
}
else
{
throw new NotImplementedException("Integer Vrsqrte not currently implemented.");
}
}
public static void Vrsqrts(ArmEmitterContext context)
{
EmitVectorBinaryOpF32(context, (op1, op2) =>
{
return EmitSoftFloatCall(context, SoftFloat32.FPRSqrtStep, SoftFloat64.FPRSqrtStep, op1, op2);
});
}
public static void Vsel(ArmEmitterContext context)
{
OpCode32SimdSel op = (OpCode32SimdSel)context.CurrOp;
Operand condition = null;
switch (op.Cc)
{
case OpCode32SimdSelMode.Eq:
condition = GetCondTrue(context, Condition.Eq);
break;
case OpCode32SimdSelMode.Ge:
condition = GetCondTrue(context, Condition.Ge);
break;
case OpCode32SimdSelMode.Gt:
condition = GetCondTrue(context, Condition.Gt);
break;
case OpCode32SimdSelMode.Vs:
condition = GetCondTrue(context, Condition.Vs);
break;
}
EmitScalarBinaryOpI32(context, (op1, op2) =>
{
return context.ConditionalSelect(condition, op1, op2);
});
}
public static void Vsqrt_S(ArmEmitterContext context)
{
EmitScalarUnaryOpF32(context, (op1) =>
{
return EmitSoftFloatCall(context, SoftFloat32.FPSqrt, SoftFloat64.FPSqrt, op1);
});
}
public static void Vsub_S(ArmEmitterContext context)
{
EmitScalarBinaryOpF32(context, (op1, op2) => context.Subtract(op1, op2));
}
public static void Vsub_V(ArmEmitterContext context)
{
EmitVectorBinaryOpF32(context, (op1, op2) => context.Subtract(op1, op2));
}
public static void Vsub_I(ArmEmitterContext context)
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.Subtract(op1, op2));
}
}
}

View file

@ -0,0 +1,273 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
using Func2I = Func<Operand, Operand, Operand>;
static partial class InstEmit32
{
public static void Vceq_V(ArmEmitterContext context)
{
EmitCmpOpF32(context, SoftFloat32.FPCompareEQFpscr, SoftFloat64.FPCompareEQFpscr, false);
}
public static void Vceq_I(ArmEmitterContext context)
{
EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, false, false);
}
public static void Vceq_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
EmitCmpOpF32(context, SoftFloat32.FPCompareEQFpscr, SoftFloat64.FPCompareEQFpscr, true);
}
else
{
EmitCmpOpI32(context, context.ICompareEqual, context.ICompareEqual, true, false);
}
}
public static void Vcge_V(ArmEmitterContext context)
{
EmitCmpOpF32(context, SoftFloat32.FPCompareGEFpscr, SoftFloat64.FPCompareGEFpscr, false);
}
public static void Vcge_I(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, false, !op.U);
}
public static void Vcge_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
EmitCmpOpF32(context, SoftFloat32.FPCompareGEFpscr, SoftFloat64.FPCompareGEFpscr, true);
}
else
{
EmitCmpOpI32(context, context.ICompareGreaterOrEqual, context.ICompareGreaterOrEqualUI, true, true);
}
}
public static void Vcgt_V(ArmEmitterContext context)
{
EmitCmpOpF32(context, SoftFloat32.FPCompareGTFpscr, SoftFloat64.FPCompareGTFpscr, false);
}
public static void Vcgt_I(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, false, !op.U);
}
public static void Vcgt_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
EmitCmpOpF32(context, SoftFloat32.FPCompareGTFpscr, SoftFloat64.FPCompareGTFpscr, true);
}
else
{
EmitCmpOpI32(context, context.ICompareGreater, context.ICompareGreaterUI, true, true);
}
}
public static void Vcle_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
EmitCmpOpF32(context, SoftFloat32.FPCompareLEFpscr, SoftFloat64.FPCompareLEFpscr, true);
}
else
{
EmitCmpOpI32(context, context.ICompareLessOrEqual, context.ICompareLessOrEqualUI, true, true);
}
}
public static void Vclt_Z(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
if (op.F)
{
EmitCmpOpF32(context, SoftFloat32.FPCompareLTFpscr, SoftFloat64.FPCompareLTFpscr, true);
}
else
{
EmitCmpOpI32(context, context.ICompareLess, context.ICompareLessUI, true, true);
}
}
private static void EmitCmpOpF32(
ArmEmitterContext context,
_F32_F32_F32_Bool f32,
_F64_F64_F64_Bool f64,
bool zero)
{
Operand one = Const(1);
if (zero)
{
EmitVectorUnaryOpF32(context, (m) =>
{
OperandType type = m.Type;
if (type == OperandType.FP64)
{
return context.Call(f64, m, ConstF(0.0), one);
}
else
{
return context.Call(f32, m, ConstF(0.0f), one);
}
});
}
else
{
EmitVectorBinaryOpF32(context, (n, m) =>
{
OperandType type = n.Type;
if (type == OperandType.FP64)
{
return context.Call(f64, n, m, one);
}
else
{
return context.Call(f32, n, m, one);
}
});
}
}
private static Operand ZerosOrOnes(ArmEmitterContext context, Operand fromBool, OperandType baseType)
{
var ones = (baseType == OperandType.I64) ? Const(-1L) : Const(-1);
return context.ConditionalSelect(fromBool, ones, Const(baseType, 0L));
}
private static void EmitCmpOpI32(
ArmEmitterContext context,
Func2I signedOp,
Func2I unsignedOp,
bool zero,
bool signed)
{
if (zero)
{
if (signed)
{
EmitVectorUnaryOpSx32(context, (m) =>
{
OperandType type = m.Type;
Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
return ZerosOrOnes(context, signedOp(m, zeroV), type);
});
}
else
{
EmitVectorUnaryOpZx32(context, (m) =>
{
OperandType type = m.Type;
Operand zeroV = (type == OperandType.I64) ? Const(0L) : Const(0);
return ZerosOrOnes(context, unsignedOp(m, zeroV), type);
});
}
}
else
{
if (signed)
{
EmitVectorBinaryOpSx32(context, (n, m) => ZerosOrOnes(context, signedOp(n, m), n.Type));
}
else
{
EmitVectorBinaryOpZx32(context, (n, m) => ZerosOrOnes(context, unsignedOp(n, m), n.Type));
}
}
}
public static void Vcmp(ArmEmitterContext context)
{
EmitVcmpOrVcmpe(context, false);
}
public static void Vcmpe(ArmEmitterContext context)
{
EmitVcmpOrVcmpe(context, true);
}
private static void EmitVcmpOrVcmpe(ArmEmitterContext context, bool signalNaNs)
{
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
bool cmpWithZero = (op.Opc & 2) != 0;
{
int fSize = op.Size & 1;
OperandType type = fSize != 0 ? OperandType.FP64 : OperandType.FP32;
Operand ne = ExtractScalar(context, type, op.Vd);
Operand me;
if (cmpWithZero)
{
me = fSize == 0 ? ConstF(0f) : ConstF(0d);
}
else
{
me = ExtractScalar(context, type, op.Vm);
}
Delegate dlg = fSize != 0
? (Delegate)new _S32_F64_F64_Bool(SoftFloat64.FPCompare)
: (Delegate)new _S32_F32_F32_Bool(SoftFloat32.FPCompare);
Operand nzcv = context.Call(dlg, ne, me, Const(signalNaNs));
EmitSetFPSCRFlags(context, nzcv);
}
}
private static void EmitSetFPSCRFlags(ArmEmitterContext context, Operand nzcv)
{
Operand Extract(Operand value, int bit)
{
if (bit != 0)
{
value = context.ShiftRightUI(value, Const(bit));
}
value = context.BitwiseAnd(value, Const(1));
return value;
}
SetFpFlag(context, FPState.VFlag, Extract(nzcv, 0));
SetFpFlag(context, FPState.CFlag, Extract(nzcv, 1));
SetFpFlag(context, FPState.ZFlag, Extract(nzcv, 2));
SetFpFlag(context, FPState.NFlag, Extract(nzcv, 3));
}
}
}

View file

@ -0,0 +1,274 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
private static int FlipVdBits(int vd, bool lowBit)
{
if (lowBit)
{
// Move the low bit to the top.
return ((vd & 0x1) << 4) | (vd >> 1);
}
else
{
// Move the high bit to the bottom.
return ((vd & 0xf) << 1) | (vd >> 4);
}
}
private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned)
{
if (op1.Type == OperandType.FP64)
{
if (unsigned)
{
return context.Call(new _U32_F64(SoftFallback.SatF64ToU32), op1);
}
else
{
return context.Call(new _S32_F64(SoftFallback.SatF64ToS32), op1);
}
}
else
{
if (unsigned)
{
return context.Call(new _U32_F32(SoftFallback.SatF32ToU32), op1);
}
else
{
return context.Call(new _S32_F32(SoftFallback.SatF32ToS32), op1);
}
}
}
public static void Vcvt_V(ArmEmitterContext context)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
bool unsigned = (op.Opc & 1) != 0;
bool toInteger = (op.Opc & 2) != 0;
OperandType floatSize = (op.Size == 2) ? OperandType.FP32 : OperandType.FP64;
if (toInteger)
{
EmitVectorUnaryOpF32(context, (op1) =>
{
return EmitSaturateFloatToInt(context, op1, unsigned);
});
}
else
{
if (unsigned)
{
EmitVectorUnaryOpZx32(context, (op1) => EmitFPConvert(context, op1, floatSize, false));
}
else
{
EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true));
}
}
}
public static void Vcvt_FD(ArmEmitterContext context)
{
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
int vm = op.Vm;
int vd;
if (op.Size == 3)
{
vd = FlipVdBits(op.Vd, false);
// Double to single.
Operand fp = ExtractScalar(context, OperandType.FP64, vm);
Operand res = context.ConvertToFP(OperandType.FP32, fp);
InsertScalar(context, vd, res);
}
else
{
vd = FlipVdBits(op.Vd, true);
// Single to double.
Operand fp = ExtractScalar(context, OperandType.FP32, vm);
Operand res = context.ConvertToFP(OperandType.FP64, fp);
InsertScalar(context, vd, res);
}
}
public static void Vcvt_FI(ArmEmitterContext context)
{
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
bool toInteger = (op.Opc2 & 0b100) != 0;
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
if (toInteger)
{
bool unsigned = (op.Opc2 & 1) == 0;
bool roundWithFpscr = op.Opc != 1;
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
Operand asInteger;
// TODO: Fast Path.
if (roundWithFpscr)
{
// These need to get the FPSCR value, so it's worth noting we'd need to do a c# call at some point.
if (floatSize == OperandType.FP64)
{
if (unsigned)
{
asInteger = context.Call(new _U32_F64(SoftFallback.DoubleToUInt32), toConvert);
}
else
{
asInteger = context.Call(new _S32_F64(SoftFallback.DoubleToInt32), toConvert);
}
}
else
{
if (unsigned)
{
asInteger = context.Call(new _U32_F32(SoftFallback.FloatToUInt32), toConvert);
}
else
{
asInteger = context.Call(new _S32_F32(SoftFallback.FloatToInt32), toConvert);
}
}
}
else
{
// Round towards zero.
asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
}
InsertScalar(context, op.Vd, asInteger);
}
else
{
bool unsigned = op.Opc == 0;
Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm);
Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned);
InsertScalar(context, op.Vd, asFloat);
}
}
public static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n)
{
IOpCode32Simd op = (IOpCode32Simd)context.CurrOp;
Delegate dlg;
if ((op.Size & 1) == 0)
{
dlg = new _F32_F32_MidpointRounding(MathF.Round);
}
else /* if ((op.Size & 1) == 1) */
{
dlg = new _F64_F64_MidpointRounding(Math.Round);
}
return context.Call(dlg, n, Const((int)roundMode));
}
public static void Vcvt_R(ArmEmitterContext context)
{
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
bool unsigned = (op.Opc & 1) == 0;
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
switch (op.Opc2)
{
case 0b00: // Away
toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
break;
case 0b01: // Nearest
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break;
case 0b10: // Towards positive infinity
toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert);
break;
case 0b11: // Towards negative infinity
toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert);
break;
}
Operand asInteger;
asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
InsertScalar(context, op.Vd, asInteger);
}
public static void Vrint_RM(ArmEmitterContext context)
{
OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
switch (op.Opc2)
{
case 0b00: // Away
toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
break;
case 0b01: // Nearest
toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
break;
case 0b10: // Towards positive infinity
toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert);
break;
case 0b11: // Towards negative infinity
toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert);
break;
}
InsertScalar(context, op.Vd, toConvert);
}
public static void Vrint_Z(ArmEmitterContext context)
{
EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Truncate, Math.Truncate, op1));
}
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
{
Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
if (signed)
{
return context.ConvertToFP(type, value);
}
else
{
return context.ConvertToFPUI(type, value);
}
}
}
}

View file

@ -1528,7 +1528,7 @@ namespace ARMeilleure.Instructions
{
ThrowIfInvalid(index, size);
if (size < 3)
if (size < 3 && value.Type == OperandType.I64)
{
value = context.ConvertI64ToI32(value);
}
@ -1544,7 +1544,7 @@ namespace ARMeilleure.Instructions
return vector;
}
private static void ThrowIfInvalid(int index, int size)
public static void ThrowIfInvalid(int index, int size)
{
if ((uint)size > 3u)
{

View file

@ -0,0 +1,581 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
using Func1I = Func<Operand, Operand>;
using Func2I = Func<Operand, Operand, Operand>;
using Func3I = Func<Operand, Operand, Operand, Operand>;
static class InstEmitSimdHelper32
{
public static (int, int) GetQuadwordAndSubindex(int index, RegisterSize size)
{
switch (size)
{
case RegisterSize.Simd128:
return (index >> 1, 0);
case RegisterSize.Simd64:
case RegisterSize.Int64:
return (index >> 1, index & 1);
case RegisterSize.Int32:
return (index >> 2, index & 3);
}
throw new ArgumentException("Unrecognized Vector Register Size.");
}
public static Operand ExtractScalar(ArmEmitterContext context, OperandType type, int reg)
{
Debug.Assert(type != OperandType.V128);
if (type == OperandType.FP64 || type == OperandType.I64)
{
// From dreg.
return context.VectorExtract(type, GetVecA32(reg >> 1), reg & 1);
}
else
{
// From sreg.
return context.VectorExtract(type, GetVecA32(reg >> 2), reg & 3);
}
}
public static void InsertScalar(ArmEmitterContext context, int reg, Operand value)
{
Debug.Assert(value.Type != OperandType.V128);
Operand vec, insert;
if (value.Type == OperandType.FP64 || value.Type == OperandType.I64)
{
// From dreg.
vec = GetVecA32(reg >> 1);
insert = context.VectorInsert(vec, value, reg & 1);
}
else
{
// From sreg.
vec = GetVecA32(reg >> 2);
insert = context.VectorInsert(vec, value, reg & 3);
}
context.Copy(vec, insert);
}
public static void EmitVectorImmUnaryOp32(ArmEmitterContext context, Func1I emit)
{
IOpCode32SimdImm op = (IOpCode32SimdImm)context.CurrOp;
Operand imm = Const(op.Immediate);
int elems = op.Elems;
(int index, int subIndex) = GetQuadwordAndSubindex(op.Vd, op.RegisterSize);
Operand vec = GetVecA32(index);
Operand res = vec;
for (int item = 0; item < elems; item++)
{
res = EmitVectorInsert(context, res, emit(imm), item + subIndex * elems, op.Size);
}
context.Copy(vec, res);
}
public static void EmitScalarUnaryOpF32(ArmEmitterContext context, Func1I emit)
{
OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
Operand m = ExtractScalar(context, type, op.Vm);
InsertScalar(context, op.Vd, emit(m));
}
public static void EmitScalarBinaryOpF32(ArmEmitterContext context, Func2I emit)
{
OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
Operand n = ExtractScalar(context, type, op.Vn);
Operand m = ExtractScalar(context, type, op.Vm);
InsertScalar(context, op.Vd, emit(n, m));
}
public static void EmitScalarBinaryOpI32(ArmEmitterContext context, Func2I emit)
{
OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
OperandType type = (op.Size & 1) != 0 ? OperandType.I64 : OperandType.I32;
if (op.Size < 2)
{
throw new NotSupportedException("Cannot perform a scalar SIMD operation on integers smaller than 32 bits.");
}
Operand n = ExtractScalar(context, type, op.Vn);
Operand m = ExtractScalar(context, type, op.Vm);
InsertScalar(context, op.Vd, emit(n, m));
}
public static void EmitScalarTernaryOpF32(ArmEmitterContext context, Func3I emit)
{
OpCode32SimdRegS op = (OpCode32SimdRegS)context.CurrOp;
OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
Operand a = ExtractScalar(context, type, op.Vd);
Operand n = ExtractScalar(context, type, op.Vn);
Operand m = ExtractScalar(context, type, op.Vm);
InsertScalar(context, op.Vd, emit(a, n, m));
}
public static void EmitVectorUnaryOpF32(ArmEmitterContext context, Func1I emit)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
int sizeF = op.Size & 1;
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
int elems = op.GetBytesCount() >> sizeF + 2;
Operand res = GetVecA32(op.Qd);
for (int index = 0; index < elems; index++)
{
Operand me = context.VectorExtract(type, GetVecA32(op.Qm), op.Fm + index);
res = context.VectorInsert(res, emit(me), op.Fd + index);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void EmitVectorBinaryOpF32(ArmEmitterContext context, Func2I emit)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
int sizeF = op.Size & 1;
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
int elems = op.GetBytesCount() >> (sizeF + 2);
Operand res = GetVecA32(op.Qd);
for (int index = 0; index < elems; index++)
{
Operand ne = context.VectorExtract(type, GetVecA32(op.Qn), op.Fn + index);
Operand me = context.VectorExtract(type, GetVecA32(op.Qm), op.Fm + index);
res = context.VectorInsert(res, emit(ne, me), op.Fd + index);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void EmitVectorTernaryOpF32(ArmEmitterContext context, Func3I emit)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
int sizeF = op.Size & 1;
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
int elems = op.GetBytesCount() >> sizeF + 2;
Operand res = GetVecA32(op.Qd);
for (int index = 0; index < elems; index++)
{
Operand de = context.VectorExtract(type, GetVecA32(op.Qd), op.Fd + index);
Operand ne = context.VectorExtract(type, GetVecA32(op.Qn), op.Fn + index);
Operand me = context.VectorExtract(type, GetVecA32(op.Qm), op.Fm + index);
res = context.VectorInsert(res, emit(de, ne, me), op.Fd + index);
}
context.Copy(GetVecA32(op.Qd), res);
}
// Integer
public static void EmitVectorUnaryOpI32(ArmEmitterContext context, Func1I emit, bool signed)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
Operand res = GetVecA32(op.Qd);
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed);
res = EmitVectorInsert(context, res, emit(me), op.Id + index, op.Size);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void EmitVectorBinaryOpI32(ArmEmitterContext context, Func2I emit, bool signed)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
Operand res = GetVecA32(op.Qd);
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed);
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed);
res = EmitVectorInsert(context, res, emit(ne, me), op.Id + index, op.Size);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void EmitVectorTernaryOpI32(ArmEmitterContext context, Func3I emit, bool signed)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
Operand res = GetVecA32(op.Qd);
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand de = EmitVectorExtract32(context, op.Qd, op.Id + index, op.Size, signed);
Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed);
Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, signed);
res = EmitVectorInsert(context, res, emit(de, ne, me), op.Id + index, op.Size);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void EmitVectorUnaryOpSx32(ArmEmitterContext context, Func1I emit)
{
EmitVectorUnaryOpI32(context, emit, true);
}
public static void EmitVectorBinaryOpSx32(ArmEmitterContext context, Func2I emit)
{
EmitVectorBinaryOpI32(context, emit, true);
}
public static void EmitVectorTernaryOpSx32(ArmEmitterContext context, Func3I emit)
{
EmitVectorTernaryOpI32(context, emit, true);
}
public static void EmitVectorUnaryOpZx32(ArmEmitterContext context, Func1I emit)
{
EmitVectorUnaryOpI32(context, emit, false);
}
public static void EmitVectorBinaryOpZx32(ArmEmitterContext context, Func2I emit)
{
EmitVectorBinaryOpI32(context, emit, false);
}
public static void EmitVectorTernaryOpZx32(ArmEmitterContext context, Func3I emit)
{
EmitVectorTernaryOpI32(context, emit, false);
}
// Vector by scalar
public static void EmitVectorByScalarOpF32(ArmEmitterContext context, Func2I emit)
{
OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
int sizeF = op.Size & 1;
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
int elems = op.GetBytesCount() >> sizeF + 2;
Operand m = ExtractScalar(context, type, op.Vm);
Operand res = GetVecA32(op.Qd);
for (int index = 0; index < elems; index++)
{
Operand ne = context.VectorExtract(type, GetVecA32(op.Qn), op.Fn + index);
res = context.VectorInsert(res, emit(ne, m), op.Fd + index);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void EmitVectorByScalarOpI32(ArmEmitterContext context, Func2I emit, bool signed)
{
OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
Operand m = EmitVectorExtract32(context, op.Vm >> (4 - op.Size), op.Vm & ((1 << (4 - op.Size)) - 1), op.Size, signed);
Operand res = GetVecA32(op.Qd);
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed);
res = EmitVectorInsert(context, res, emit(ne, m), op.In + index, op.Size);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void EmitVectorsByScalarOpF32(ArmEmitterContext context, Func3I emit)
{
OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
int sizeF = op.Size & 1;
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
int elems = op.GetBytesCount() >> sizeF + 2;
Operand m = ExtractScalar(context, type, op.Vm);
Operand res = GetVecA32(op.Qd);
for (int index = 0; index < elems; index++)
{
Operand de = context.VectorExtract(type, GetVecA32(op.Qd), op.Fd + index);
Operand ne = context.VectorExtract(type, GetVecA32(op.Qn), op.Fn + index);
res = context.VectorInsert(res, emit(de, ne, m), op.Fd + index);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void EmitVectorsByScalarOpI32(ArmEmitterContext context, Func3I emit, bool signed)
{
OpCode32SimdRegElem op = (OpCode32SimdRegElem)context.CurrOp;
Operand m = EmitVectorExtract32(context, op.Vm >> (4 - op.Size), op.Vm & ((1 << (4 - op.Size)) - 1), op.Size, signed);
Operand res = GetVecA32(op.Qd);
int elems = op.GetBytesCount() >> op.Size;
for (int index = 0; index < elems; index++)
{
Operand de = EmitVectorExtract32(context, op.Qd, op.Id + index, op.Size, signed);
Operand ne = EmitVectorExtract32(context, op.Qn, op.In + index, op.Size, signed);
res = EmitVectorInsert(context, res, emit(de, ne, m), op.Id + index, op.Size);
}
context.Copy(GetVecA32(op.Qd), res);
}
// Pairwise
public static void EmitVectorPairwiseOpF32(ArmEmitterContext context, Func2I emit)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
int sizeF = op.Size & 1;
OperandType type = sizeF != 0 ? OperandType.FP64 : OperandType.FP32;
int elems = op.GetBytesCount() >> (sizeF + 2);
int pairs = elems >> 1;
Operand res = GetVecA32(op.Qd);
Operand mvec = GetVecA32(op.Qm);
Operand nvec = GetVecA32(op.Qn);
for (int index = 0; index < pairs; index++)
{
int pairIndex = index << 1;
Operand n1 = context.VectorExtract(type, nvec, op.Fn + pairIndex);
Operand n2 = context.VectorExtract(type, nvec, op.Fn + pairIndex + 1);
res = context.VectorInsert(res, emit(n1, n2), op.Fd + index);
Operand m1 = context.VectorExtract(type, mvec, op.Fm + pairIndex);
Operand m2 = context.VectorExtract(type, mvec, op.Fm + pairIndex + 1);
res = context.VectorInsert(res, emit(m1, m2), op.Fd + index + pairs);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void EmitVectorPairwiseOpI32(ArmEmitterContext context, Func2I emit, bool signed)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
int elems = op.GetBytesCount() >> op.Size;
int pairs = elems >> 1;
Operand res = GetVecA32(op.Qd);
for (int index = 0; index < pairs; index++)
{
int pairIndex = index << 1;
Operand n1 = EmitVectorExtract32(context, op.Qn, op.In + pairIndex, op.Size, signed);
Operand n2 = EmitVectorExtract32(context, op.Qn, op.In + pairIndex + 1, op.Size, signed);
Operand m1 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex, op.Size, signed);
Operand m2 = EmitVectorExtract32(context, op.Qm, op.Im + pairIndex + 1, op.Size, signed);
res = EmitVectorInsert(context, res, emit(n1, n2), op.Id + index, op.Size);
res = EmitVectorInsert(context, res, emit(m1, m2), op.Id + index + pairs, op.Size);
}
context.Copy(GetVecA32(op.Qd), res);
}
// Narrow
public static void EmitVectorUnaryNarrowOp32(ArmEmitterContext context, Func1I emit)
{
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
int elems = 8 >> op.Size; // Size contains the target element size. (for when it becomes a doubleword)
Operand res = GetVecA32(op.Qd);
int id = (op.Vd & 1) << (3 - op.Size); // Target doubleword base.
for (int index = 0; index < elems; index++)
{
Operand m = EmitVectorExtract32(context, op.Qm, index, op.Size + 1, false);
res = EmitVectorInsert(context, res, emit(m), id + index, op.Size);
}
context.Copy(GetVecA32(op.Qd), res);
}
// Generic Functions
public static Operand EmitSoftFloatCallDefaultFpscr(
ArmEmitterContext context,
_F32_F32_Bool f32,
_F64_F64_Bool f64,
params Operand[] callArgs)
{
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64;
Array.Resize(ref callArgs, callArgs.Length + 1);
callArgs[callArgs.Length - 1] = Const(1);
return context.Call(dlg, callArgs);
}
public static Operand EmitSoftFloatCallDefaultFpscr(
ArmEmitterContext context,
_F32_F32_F32_Bool f32,
_F64_F64_F64_Bool f64,
params Operand[] callArgs)
{
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64;
Array.Resize(ref callArgs, callArgs.Length + 1);
callArgs[callArgs.Length - 1] = Const(1);
return context.Call(dlg, callArgs);
}
public static Operand EmitSoftFloatCallDefaultFpscr(
ArmEmitterContext context,
_F32_F32_F32_F32_Bool f32,
_F64_F64_F64_F64_Bool f64,
params Operand[] callArgs)
{
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
Delegate dlg = (op.Size & 1) == 0 ? (Delegate)f32 : (Delegate)f64;
Array.Resize(ref callArgs, callArgs.Length + 1);
callArgs[callArgs.Length - 1] = Const(1);
return context.Call(dlg, callArgs);
}
public static Operand EmitVectorExtractSx32(ArmEmitterContext context, int reg, int index, int size)
{
return EmitVectorExtract32(context, reg, index, size, true);
}
public static Operand EmitVectorExtractZx32(ArmEmitterContext context, int reg, int index, int size)
{
return EmitVectorExtract32(context, reg, index, size, false);
}
public static Operand EmitVectorExtract32(ArmEmitterContext context, int reg, int index, int size, bool signed)
{
ThrowIfInvalid(index, size);
Operand res = null;
switch (size)
{
case 0:
res = context.VectorExtract8(GetVec(reg), index);
break;
case 1:
res = context.VectorExtract16(GetVec(reg), index);
break;
case 2:
res = context.VectorExtract(OperandType.I32, GetVec(reg), index);
break;
case 3:
res = context.VectorExtract(OperandType.I64, GetVec(reg), index);
break;
}
if (signed)
{
switch (size)
{
case 0: res = context.SignExtend8(OperandType.I32, res); break;
case 1: res = context.SignExtend16(OperandType.I32, res); break;
}
}
else
{
switch (size)
{
case 0: res = context.ZeroExtend8(OperandType.I32, res); break;
case 1: res = context.ZeroExtend16(OperandType.I32, res); break;
}
}
return res;
}
}
}

View file

@ -0,0 +1,56 @@
using ARMeilleure.Decoders;
using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Vand_I(ArmEmitterContext context)
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseAnd(op1, op2));
}
public static void Vbif(ArmEmitterContext context)
{
EmitBifBit(context, true);
}
public static void Vbit(ArmEmitterContext context)
{
EmitBifBit(context, false);
}
public static void Vbsl(ArmEmitterContext context)
{
EmitVectorTernaryOpZx32(context, (op1, op2, op3) =>
{
return context.BitwiseExclusiveOr(
context.BitwiseAnd(op1,
context.BitwiseExclusiveOr(op2, op3)), op3);
});
}
public static void Vorr_I(ArmEmitterContext context)
{
EmitVectorBinaryOpZx32(context, (op1, op2) => context.BitwiseOr(op1, op2));
}
private static void EmitBifBit(ArmEmitterContext context, bool notRm)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
EmitVectorTernaryOpZx32(context, (d, n, m) =>
{
if (notRm)
{
m = context.BitwiseNot(m);
}
return context.BitwiseExclusiveOr(
context.BitwiseAnd(m,
context.BitwiseExclusiveOr(d, n)), d);
});
}
}
}

View file

@ -0,0 +1,352 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitMemoryHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Vld1(ArmEmitterContext context)
{
EmitVStoreOrLoadN(context, 1, true);
}
public static void Vld2(ArmEmitterContext context)
{
EmitVStoreOrLoadN(context, 2, true);
}
public static void Vld3(ArmEmitterContext context)
{
EmitVStoreOrLoadN(context, 3, true);
}
public static void Vld4(ArmEmitterContext context)
{
EmitVStoreOrLoadN(context, 4, true);
}
public static void Vst1(ArmEmitterContext context)
{
EmitVStoreOrLoadN(context, 1, false);
}
public static void Vst2(ArmEmitterContext context)
{
EmitVStoreOrLoadN(context, 2, false);
}
public static void Vst3(ArmEmitterContext context)
{
EmitVStoreOrLoadN(context, 3, false);
}
public static void Vst4(ArmEmitterContext context)
{
EmitVStoreOrLoadN(context, 4, false);
}
public static void EmitVStoreOrLoadN(ArmEmitterContext context, int count, bool load)
{
if (context.CurrOp is OpCode32SimdMemSingle)
{
OpCode32SimdMemSingle op = (OpCode32SimdMemSingle)context.CurrOp;
int eBytes = 1 << op.Size;
Operand n = context.Copy(GetIntA32(context, op.Rn));
// TODO: Check alignment.
int offset = 0;
int d = op.Vd;
for (int i = 0; i < count; i++)
{
// Write an element from a double simd register.
Operand address = context.Add(n, Const(offset));
if (eBytes == 8)
{
if (load)
{
EmitDVectorLoad(context, address, d);
}
else
{
EmitDVectorStore(context, address, d);
}
}
else
{
int index = ((d & 1) << (3 - op.Size)) + op.Index;
if (load)
{
if (op.Replicate)
{
var regs = (count > 1) ? 1 : op.Increment;
for (int reg = 0; reg < regs; reg++)
{
int dreg = reg + d;
int rIndex = ((dreg & 1) << (3 - op.Size));
int limit = rIndex + (1 << (3 - op.Size));
while (rIndex < limit)
{
EmitLoadSimd(context, address, GetVecA32(dreg >> 1), dreg >> 1, rIndex++, op.Size);
}
}
}
else
{
EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
}
}
else
{
EmitStoreSimd(context, address, d >> 1, index, op.Size);
}
}
offset += eBytes;
d += op.Increment;
}
if (op.WBack)
{
if (op.RegisterIndex)
{
Operand m = GetIntA32(context, op.Rm);
SetIntA32(context, op.Rn, context.Add(n, m));
}
else
{
SetIntA32(context, op.Rn, context.Add(n, Const(count * eBytes)));
}
}
}
else
{
OpCode32SimdMemPair op = (OpCode32SimdMemPair)context.CurrOp;
int eBytes = 1 << op.Size;
Operand n = context.Copy(GetIntA32(context, op.Rn));
int offset = 0;
int d = op.Vd;
for (int reg = 0; reg < op.Regs; reg++)
{
for (int elem = 0; elem < op.Elems; elem++)
{
int elemD = d + reg;
for (int i = 0; i < count; i++)
{
// Write an element from a double simd register
// add ebytes for each element.
Operand address = context.Add(n, Const(offset));
int index = ((elemD & 1) << (3 - op.Size)) + elem;
if (eBytes == 8)
{
if (load)
{
EmitDVectorLoad(context, address, elemD);
}
else
{
EmitDVectorStore(context, address, elemD);
}
}
else
{
if (load)
{
EmitLoadSimd(context, address, GetVecA32(elemD >> 1), elemD >> 1, index, op.Size);
}
else
{
EmitStoreSimd(context, address, elemD >> 1, index, op.Size);
}
}
offset += eBytes;
elemD += op.Increment;
}
}
}
if (op.WBack)
{
if (op.RegisterIndex)
{
Operand m = GetIntA32(context, op.Rm);
SetIntA32(context, op.Rn, context.Add(n, m));
}
else
{
SetIntA32(context, op.Rn, context.Add(n, Const(count * 8 * op.Regs)));
}
}
}
}
public static void Vldm(ArmEmitterContext context)
{
OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
Operand n = context.Copy(GetIntA32(context, op.Rn));
Operand baseAddress = context.Add(n, Const(op.Offset));
bool writeBack = op.PostOffset != 0;
if (writeBack)
{
SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
}
int range = op.RegisterRange;
int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
int offset = 0;
int byteSize = 4;
for (int num = 0; num < range; num++, sReg++)
{
Operand address = context.Add(baseAddress, Const(offset));
Operand vec = GetVecA32(sReg >> 2);
EmitLoadSimd(context, address, vec, sReg >> 2, sReg & 3, WordSizeLog2);
offset += byteSize;
}
}
public static void Vstm(ArmEmitterContext context)
{
OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
Operand n = context.Copy(GetIntA32(context, op.Rn));
Operand baseAddress = context.Add(n, Const(op.Offset));
bool writeBack = op.PostOffset != 0;
if (writeBack)
{
SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
}
int offset = 0;
int range = op.RegisterRange;
int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
int byteSize = 4;
for (int num = 0; num < range; num++, sReg++)
{
Operand address = context.Add(baseAddress, Const(offset));
EmitStoreSimd(context, address, sReg >> 2, sReg & 3, WordSizeLog2);
offset += byteSize;
}
}
public static void Vldr(ArmEmitterContext context)
{
EmitVLoadOrStore(context, AccessType.Load);
}
public static void Vstr(ArmEmitterContext context)
{
EmitVLoadOrStore(context, AccessType.Store);
}
private static void EmitDVectorStore(ArmEmitterContext context, Operand address, int vecD)
{
int vecQ = vecD >> 1;
int vecSElem = (vecD & 1) << 1;
Operand lblBigEndian = Label();
Operand lblEnd = Label();
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
EmitStoreSimd(context, address, vecQ, vecSElem, WordSizeLog2);
EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem | 1, WordSizeLog2);
context.Branch(lblEnd);
context.MarkLabel(lblBigEndian);
EmitStoreSimd(context, address, vecQ, vecSElem | 1, WordSizeLog2);
EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem, WordSizeLog2);
context.MarkLabel(lblEnd);
}
private static void EmitDVectorLoad(ArmEmitterContext context, Operand address, int vecD)
{
int vecQ = vecD >> 1;
int vecSElem = (vecD & 1) << 1;
Operand vec = GetVecA32(vecQ);
Operand lblBigEndian = Label();
Operand lblEnd = Label();
context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
EmitLoadSimd(context, address, vec, vecQ, vecSElem, WordSizeLog2);
EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem | 1, WordSizeLog2);
context.Branch(lblEnd);
context.MarkLabel(lblBigEndian);
EmitLoadSimd(context, address, vec, vecQ, vecSElem | 1, WordSizeLog2);
EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem, WordSizeLog2);
context.MarkLabel(lblEnd);
}
private static void EmitVLoadOrStore(ArmEmitterContext context, AccessType accType)
{
OpCode32SimdMemImm op = (OpCode32SimdMemImm)context.CurrOp;
Operand n = context.Copy(GetIntA32(context, op.Rn));
Operand m = GetMemM(context, setCarry: false);
Operand address = op.Add
? context.Add(n, m)
: context.Subtract(n, m);
int size = op.Size;
if ((accType & AccessType.Load) != 0)
{
if (size == DWordSizeLog2)
{
EmitDVectorLoad(context, address, op.Vd);
}
else
{
Operand vec = GetVecA32(op.Vd >> 2);
EmitLoadSimd(context, address, vec, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
}
}
else
{
if (size == DWordSizeLog2)
{
EmitDVectorStore(context, address, op.Vd);
}
else
{
EmitStoreSimd(context, address, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
}
}
}
}
}

View file

@ -0,0 +1,336 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Vmov_I(ArmEmitterContext context)
{
EmitVectorImmUnaryOp32(context, (op1) => op1);
}
public static void Vmvn_I(ArmEmitterContext context)
{
EmitVectorImmUnaryOp32(context, (op1) => context.BitwiseExclusiveOr(op1, op1));
}
public static void Vmov_GS(ArmEmitterContext context)
{
OpCode32SimdMovGp op = (OpCode32SimdMovGp)context.CurrOp;
Operand vec = GetVecA32(op.Vn >> 2);
if (op.Op == 1)
{
// To general purpose.
Operand value = context.VectorExtract(OperandType.I32, vec, op.Vn & 0x3);
SetIntA32(context, op.Rt, value);
}
else
{
// From general purpose.
Operand value = GetIntA32(context, op.Rt);
context.Copy(vec, context.VectorInsert(vec, value, op.Vn & 0x3));
}
}
public static void Vmov_G1(ArmEmitterContext context)
{
OpCode32SimdMovGpElem op = (OpCode32SimdMovGpElem)context.CurrOp;
int index = op.Index + ((op.Vd & 1) << (3 - op.Size));
if (op.Op == 1)
{
// To general purpose.
Operand value = EmitVectorExtract32(context, op.Vd >> 1, index, op.Size, !op.U);
SetIntA32(context, op.Rt, value);
}
else
{
// From general purpose.
Operand vec = GetVecA32(op.Vd >> 1);
Operand value = GetIntA32(context, op.Rt);
context.Copy(vec, EmitVectorInsert(context, vec, value, index, op.Size));
}
}
public static void Vmov_G2(ArmEmitterContext context)
{
OpCode32SimdMovGpDouble op = (OpCode32SimdMovGpDouble)context.CurrOp;
Operand vec = GetVecA32(op.Vm >> 2);
int vm1 = op.Vm + 1;
bool sameOwnerVec = (op.Vm >> 2) == (vm1 >> 2);
Operand vec2 = sameOwnerVec ? vec : GetVecA32(vm1 >> 2);
if (op.Op == 1)
{
// To general purpose.
Operand lowValue = context.VectorExtract(OperandType.I32, vec, op.Vm & 3);
SetIntA32(context, op.Rt, lowValue);
Operand highValue = context.VectorExtract(OperandType.I32, vec2, vm1 & 3);
SetIntA32(context, op.Rt2, highValue);
}
else
{
// From general purpose.
Operand lowValue = GetIntA32(context, op.Rt);
Operand resultVec = context.VectorInsert(vec, lowValue, op.Vm & 3);
Operand highValue = GetIntA32(context, op.Rt2);
if (sameOwnerVec)
{
context.Copy(vec, context.VectorInsert(resultVec, highValue, vm1 & 3));
}
else
{
context.Copy(vec, resultVec);
context.Copy(vec2, context.VectorInsert(vec2, highValue, vm1 & 3));
}
}
}
public static void Vmov_GD(ArmEmitterContext context)
{
OpCode32SimdMovGpDouble op = (OpCode32SimdMovGpDouble)context.CurrOp;
Operand vec = GetVecA32(op.Vm >> 1);
if (op.Op == 1)
{
// To general purpose.
Operand value = context.VectorExtract(OperandType.I64, vec, op.Vm & 1);
SetIntA32(context, op.Rt, context.ConvertI64ToI32(value));
SetIntA32(context, op.Rt2, context.ConvertI64ToI32(context.ShiftRightUI(value, Const(32))));
}
else
{
// From general purpose.
Operand lowValue = GetIntA32(context, op.Rt);
Operand highValue = GetIntA32(context, op.Rt2);
Operand value = context.BitwiseOr(
context.ZeroExtend32(OperandType.I64, lowValue),
context.ShiftLeft(context.ZeroExtend32(OperandType.I64, highValue), Const(32)));
context.Copy(vec, context.VectorInsert(vec, value, op.Vm & 1));
}
}
public static void Vtbl(ArmEmitterContext context)
{
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
bool extension = op.Opc == 1;
int elems = op.GetBytesCount() >> op.Size;
int length = op.Length + 1;
(int Qx, int Ix)[] tableTuples = new (int, int)[length];
for (int i = 0; i < length; i++)
{
(int vn, int en) = GetQuadwordAndSubindex(op.Vn + i, op.RegisterSize);
tableTuples[i] = (vn, en);
}
int byteLength = length * 8;
Operand res = GetVecA32(op.Qd);
Operand m = GetVecA32(op.Qm);
for (int index = 0; index < elems; index++)
{
Operand selectedIndex = context.ZeroExtend8(OperandType.I32, context.VectorExtract8(m, index + op.Im));
Operand inRange = context.ICompareLess(selectedIndex, Const(byteLength));
Operand elemRes = null; // Note: This is I64 for ease of calculation.
// TODO: Branching rather than conditional select.
// Get indexed byte.
// To simplify (ha) the il, we get bytes from every vector and use a nested conditional select to choose the right result.
// This does have to extract `length` times for every element but certainly not as bad as it could be.
// Which vector number is the index on.
Operand vecIndex = context.ShiftRightUI(selectedIndex, Const(3));
// What should we shift by to extract it.
Operand subVecIndexShift = context.ShiftLeft(context.BitwiseAnd(selectedIndex, Const(7)), Const(3));
for (int i = 0; i < length; i++)
{
(int qx, int ix) = tableTuples[i];
// Get the whole vector, we'll get a byte out of it.
Operand lookupResult;
if (qx == op.Qd)
{
// Result contains the current state of the vector.
lookupResult = context.VectorExtract(OperandType.I64, res, ix);
}
else
{
lookupResult = EmitVectorExtract32(context, qx, ix, 3, false); // I64
}
lookupResult = context.ShiftRightUI(lookupResult, subVecIndexShift); // Get the relevant byte from this vector.
if (i == 0)
{
elemRes = lookupResult; // First result is always default.
}
else
{
Operand isThisElem = context.ICompareEqual(vecIndex, Const(i));
elemRes = context.ConditionalSelect(isThisElem, lookupResult, elemRes);
}
}
Operand fallback = (extension) ? context.ZeroExtend32(OperandType.I64, EmitVectorExtract32(context, op.Qd, index + op.Id, 0, false)) : Const(0L);
res = EmitVectorInsert(context, res, context.ConditionalSelect(inRange, elemRes, fallback), index + op.Id, 0);
}
context.Copy(GetVecA32(op.Qd), res);
}
public static void Vtrn(ArmEmitterContext context)
{
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
int elems = op.GetBytesCount() >> op.Size;
int pairs = elems >> 1;
bool overlap = op.Qm == op.Qd;
Operand resD = GetVecA32(op.Qd);
Operand resM = GetVecA32(op.Qm);
for (int index = 0; index < pairs; index++)
{
int pairIndex = index << 1;
Operand d2 = EmitVectorExtract32(context, op.Qd, pairIndex + 1 + op.Id, op.Size, false);
Operand m1 = EmitVectorExtract32(context, op.Qm, pairIndex + op.Im, op.Size, false);
resD = EmitVectorInsert(context, resD, m1, pairIndex + 1 + op.Id, op.Size);
if (overlap)
{
resM = resD;
}
resM = EmitVectorInsert(context, resM, d2, pairIndex + op.Im, op.Size);
if (overlap)
{
resD = resM;
}
}
context.Copy(GetVecA32(op.Qd), resD);
if (!overlap)
{
context.Copy(GetVecA32(op.Qm), resM);
}
}
public static void Vzip(ArmEmitterContext context)
{
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
int elems = op.GetBytesCount() >> op.Size;
int pairs = elems >> 1;
bool overlap = op.Qm == op.Qd;
Operand resD = GetVecA32(op.Qd);
Operand resM = GetVecA32(op.Qm);
for (int index = 0; index < pairs; index++)
{
int pairIndex = index << 1;
Operand dRowD = EmitVectorExtract32(context, op.Qd, index + op.Id, op.Size, false);
Operand mRowD = EmitVectorExtract32(context, op.Qm, index + op.Im, op.Size, false);
Operand dRowM = EmitVectorExtract32(context, op.Qd, index + op.Id + pairs, op.Size, false);
Operand mRowM = EmitVectorExtract32(context, op.Qm, index + op.Im + pairs, op.Size, false);
resD = EmitVectorInsert(context, resD, dRowD, pairIndex + op.Id, op.Size);
resD = EmitVectorInsert(context, resD, mRowD, pairIndex + 1 + op.Id, op.Size);
if (overlap)
{
resM = resD;
}
resM = EmitVectorInsert(context, resM, dRowM, pairIndex + op.Im, op.Size);
resM = EmitVectorInsert(context, resM, mRowM, pairIndex + 1 + op.Im, op.Size);
if (overlap)
{
resD = resM;
}
}
context.Copy(GetVecA32(op.Qd), resD);
if (!overlap)
{
context.Copy(GetVecA32(op.Qm), resM);
}
}
public static void Vuzp(ArmEmitterContext context)
{
OpCode32SimdCmpZ op = (OpCode32SimdCmpZ)context.CurrOp;
int elems = op.GetBytesCount() >> op.Size;
int pairs = elems >> 1;
bool overlap = op.Qm == op.Qd;
Operand resD = GetVecA32(op.Qd);
Operand resM = GetVecA32(op.Qm);
for (int index = 0; index < elems; index++)
{
Operand dIns, mIns;
if (index >= pairs)
{
int pind = index - pairs;
dIns = EmitVectorExtract32(context, op.Qm, (pind << 1) + op.Im, op.Size, false);
mIns = EmitVectorExtract32(context, op.Qm, ((pind << 1) | 1) + op.Im, op.Size, false);
}
else
{
dIns = EmitVectorExtract32(context, op.Qd, (index << 1) + op.Id, op.Size, false);
mIns = EmitVectorExtract32(context, op.Qd, ((index << 1) | 1) + op.Id, op.Size, false);
}
resD = EmitVectorInsert(context, resD, dIns, index + op.Id, op.Size);
if (overlap)
{
resM = resD;
}
resM = EmitVectorInsert(context, resM, mIns, index + op.Im, op.Size);
if (overlap)
{
resD = resM;
}
}
context.Copy(GetVecA32(op.Qd), resD);
if (!overlap)
{
context.Copy(GetVecA32(op.Qm), resM);
}
}
}
}

View file

@ -0,0 +1,100 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using static ARMeilleure.Instructions.InstEmitSimdHelper32;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Vshl(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
EmitVectorUnaryOpZx32(context, (op1) => context.ShiftLeft(op1, Const(op.Shift)));
}
public static void Vshl_I(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
if (op.U)
{
EmitVectorBinaryOpZx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, true));
}
else
{
EmitVectorBinaryOpSx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, false));
}
}
public static void Vshr(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
int shift = (8 << op.Size) - op.Shift; // Shr amount is flipped.
int maxShift = (8 << op.Size) - 1;
if (op.U)
{
EmitVectorUnaryOpZx32(context, (op1) => (shift > maxShift) ? Const(op1.Type, 0) : context.ShiftRightUI(op1, Const(shift)));
}
else
{
EmitVectorUnaryOpSx32(context, (op1) => context.ShiftRightSI(op1, Const(Math.Min(maxShift, shift))));
}
}
public static void Vshrn(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
int shift = (8 << op.Size) - op.Shift; // Shr amount is flipped.
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
}
private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool unsigned)
{
if (shiftLsB.Type == OperandType.I64)
{
shiftLsB = context.ConvertI64ToI32(shiftLsB);
}
shiftLsB = context.SignExtend8(OperandType.I32, shiftLsB);
Debug.Assert((uint)size < 4u);
Operand negShiftLsB = context.Negate(shiftLsB);
Operand isPositive = context.ICompareGreaterOrEqual(shiftLsB, Const(0));
Operand shl = context.ShiftLeft(op, shiftLsB);
Operand shr = unsigned ? context.ShiftRightUI(op, negShiftLsB) : context.ShiftRightSI(op, negShiftLsB);
Operand res = context.ConditionalSelect(isPositive, shl, shr);
if (unsigned)
{
Operand isOutOfRange = context.BitwiseOr(
context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size)),
context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size)));
return context.ConditionalSelect(isOutOfRange, Const(op.Type, 0), res);
}
else
{
Operand isOutOfRange0 = context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size));
Operand isOutOfRangeN = context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size));
// Also zero if shift is too negative, but value was positive.
isOutOfRange0 = context.BitwiseOr(isOutOfRange0, context.BitwiseAnd(isOutOfRangeN, context.ICompareGreaterOrEqual(op, Const(op.Type, 0))));
Operand min = (op.Type == OperandType.I64) ? Const(-1L) : Const(-1);
return context.ConditionalSelect(isOutOfRange0, Const(op.Type, 0), context.ConditionalSelect(isOutOfRangeN, min, res));
}
}
}
}

View file

@ -0,0 +1,233 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
namespace ARMeilleure.Instructions
{
static partial class InstEmit32
{
public static void Mcr(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
if (op.Coproc != 15)
{
throw new NotImplementedException($"Unknown MRC Coprocessor ID 0x{op.Coproc:X16} at 0x{op.Address:X16}.");
}
if (op.Opc1 != 0)
{
throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
}
Delegate dlg;
switch (op.CRn)
{
case 13: // Process and Thread Info.
if (op.CRm != 0)
{
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
}
switch (op.Opc2)
{
case 2:
dlg = new _Void_U32(NativeInterface.SetTpidrEl032); break;
default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
}
break;
case 7:
switch (op.CRm) // Cache and Memory barrier.
{
case 10:
switch (op.Opc2)
{
case 5: // Data Memory Barrier Register.
return; // No-op.
default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
}
default:
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
}
default:
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
}
context.Call(dlg, GetIntA32(context, op.Rt));
}
public static void Mrc(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
if (op.Coproc != 15)
{
throw new NotImplementedException($"Unknown MRC Coprocessor ID 0x{op.Coproc:X16} at 0x{op.Address:X16}.");
}
if (op.Opc1 != 0)
{
throw new NotImplementedException($"Unknown MRC Opc1 0x{op.Opc1:X16} at 0x{op.Address:X16}.");
}
Delegate dlg;
switch (op.CRn)
{
case 13: // Process and Thread Info.
if (op.CRm != 0)
{
throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16}.");
}
switch (op.Opc2)
{
case 2:
dlg = new _U32(NativeInterface.GetTpidrEl032); break;
case 3:
dlg = new _U32(NativeInterface.GetTpidr32); break;
default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16}.");
}
break;
default:
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
}
if (op.Rt == RegisterAlias.Aarch32Pc)
{
// Special behavior: copy NZCV flags into APSR.
EmitSetNzcv(context, context.Call(dlg));
return;
}
else
{
SetIntA32(context, op.Rt, context.Call(dlg));
}
}
public static void Mrrc(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
if (op.Coproc != 15)
{
throw new NotImplementedException($"Unknown MRC Coprocessor ID 0x{op.Coproc:X16} at 0x{op.Address:X16}.");
}
var opc = op.MrrcOp;
Delegate dlg;
switch (op.CRm)
{
case 14: // Timer.
switch (opc)
{
case 0:
dlg = new _U64(NativeInterface.GetCntpctEl0); break;
default:
throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X16} at 0x{op.Address:X16}.");
}
break;
default:
throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
}
Operand result = context.Call(dlg);
SetIntA32(context, op.Rt, context.ConvertI64ToI32(result));
SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32))));
}
public static void Nop(ArmEmitterContext context) { }
public static void Vmrs(ArmEmitterContext context)
{
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
if (op.Rt == RegisterAlias.Aarch32Pc && op.Sreg == 0b0001)
{
// Special behavior: copy NZCV flags into APSR.
SetFlag(context, PState.VFlag, GetFpFlag(FPState.VFlag));
SetFlag(context, PState.CFlag, GetFpFlag(FPState.CFlag));
SetFlag(context, PState.ZFlag, GetFpFlag(FPState.ZFlag));
SetFlag(context, PState.NFlag, GetFpFlag(FPState.NFlag));
return;
}
Delegate dlg;
switch (op.Sreg)
{
case 0b0000: // FPSID
throw new NotImplementedException("Supervisor Only");
case 0b0001: // FPSCR
dlg = new _U32(NativeInterface.GetFpscr); break;
case 0b0101: // MVFR2
throw new NotImplementedException("MVFR2");
case 0b0110: // MVFR1
throw new NotImplementedException("MVFR1");
case 0b0111: // MVFR0
throw new NotImplementedException("MVFR0");
case 0b1000: // FPEXC
throw new NotImplementedException("Supervisor Only");
default:
throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
}
SetIntA32(context, op.Rt, context.Call(dlg));
}
public static void Vmsr(ArmEmitterContext context)
{
OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
Delegate dlg;
switch (op.Sreg)
{
case 0b0000: // FPSID
throw new NotImplementedException("Supervisor Only");
case 0b0001: // FPSCR
dlg = new _Void_U32(NativeInterface.SetFpscr); break;
case 0b0101: // MVFR2
throw new NotImplementedException("MVFR2");
case 0b0110: // MVFR1
throw new NotImplementedException("MVFR1");
case 0b0111: // MVFR0
throw new NotImplementedException("MVFR0");
case 0b1000: // FPEXC
throw new NotImplementedException("Supervisor Only");
default:
throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
}
context.Call(dlg, GetIntA32(context, op.Rt));
}
private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
{
Operand v = context.ShiftRightUI(t, Const((int)PState.VFlag));
v = context.BitwiseAnd(v, Const(1));
Operand c = context.ShiftRightUI(t, Const((int)PState.CFlag));
c = context.BitwiseAnd(c, Const(1));
Operand z = context.ShiftRightUI(t, Const((int)PState.ZFlag));
z = context.BitwiseAnd(z, Const(1));
Operand n = context.ShiftRightUI(t, Const((int)PState.NFlag));
n = context.BitwiseAnd(n, Const(1));
SetFlag(context, PState.VFlag, v);
SetFlag(context, PState.CFlag, c);
SetFlag(context, PState.ZFlag, z);
SetFlag(context, PState.NFlag, n);
}
}
}

View file

@ -82,6 +82,7 @@ namespace ARMeilleure.Instructions
Smaddl,
Smsubl,
Smulh,
Smull,
Stlr,
Stlxp,
Stlxr,
@ -92,6 +93,8 @@ namespace ARMeilleure.Instructions
Sub,
Subs,
Svc,
Sxtb,
Sxth,
Sys,
Tbnz,
Tbz,
@ -445,19 +448,140 @@ namespace ARMeilleure.Instructions
Zip2_V,
// Base (AArch32)
Bfc,
Bfi,
Blx,
Bx,
Cmp,
Cmn,
Movt,
Mul,
Lda,
Ldab,
Ldaex,
Ldaexb,
Ldaexd,
Ldaexh,
Ldah,
Ldm,
Ldrb,
Ldrd,
Ldrex,
Ldrexb,
Ldrexd,
Ldrexh,
Ldrh,
Ldrsb,
Ldrsh,
Mcr,
Mla,
Mls,
Mov,
Mrc,
Mrrc,
Mvn,
Pkh,
Pld,
Rev,
Revsh,
Rsb,
Rsc,
Sbfx,
Smlab,
Smlal,
Smlalh,
Smmla,
Smmls,
Smmul,
Stl,
Stlb,
Stlex,
Stlexb,
Stlexd,
Stlexh,
Stlh,
Stm,
Strb,
Strd,
Strh
Strex,
Strexb,
Strexd,
Strexh,
Strh,
Sxtb16,
Teq,
Trap,
Tst,
Ubfx,
Umlal,
Umull,
Uxtb,
Uxtb16,
Uxth,
// FP & SIMD (AArch32)
Vabs,
Vadd,
Vand,
Vbif,
Vbit,
Vbsl,
Vceq,
Vcge,
Vcgt,
Vcle,
Vclt,
Vcmp,
Vcmpe,
Vcvt,
Vdiv,
Vdup,
Vext,
Vld1,
Vld2,
Vld3,
Vld4,
Vldm,
Vldr,
Vmax,
Vmaxnm,
Vmin,
Vminnm,
Vmla,
Vmls,
Vmov,
Vmovn,
Vmrs,
Vmsr,
Vmul,
Vmvn,
Vneg,
Vnmul,
Vnmla,
Vnmls,
Vorr,
Vpadd,
Vrev,
Vrint,
Vsel,
Vshl,
Vshr,
Vshrn,
Vst1,
Vst2,
Vst3,
Vst4,
Vstm,
Vstr,
Vsqrt,
Vrecpe,
Vrecps,
Vrsqrte,
Vrsqrts,
Vsub,
Vtbl,
Vtrn,
Vuzp,
Vzip,
}
}

View file

@ -87,16 +87,39 @@ namespace ARMeilleure.Instructions
return (ulong)GetContext().Fpsr;
}
public static uint GetFpscr()
{
ExecutionContext context = GetContext();
uint result = (uint)(context.Fpsr & FPSR.A32Mask) | (uint)(context.Fpcr & FPCR.A32Mask);
result |= context.GetFPstateFlag(FPState.NFlag) ? (1u << 31) : 0;
result |= context.GetFPstateFlag(FPState.ZFlag) ? (1u << 30) : 0;
result |= context.GetFPstateFlag(FPState.CFlag) ? (1u << 29) : 0;
result |= context.GetFPstateFlag(FPState.VFlag) ? (1u << 28) : 0;
return result;
}
public static ulong GetTpidrEl0()
{
return (ulong)GetContext().TpidrEl0;
}
public static uint GetTpidrEl032()
{
return (uint)GetContext().TpidrEl0;
}
public static ulong GetTpidr()
{
return (ulong)GetContext().Tpidr;
}
public static uint GetTpidr32()
{
return (uint)GetContext().Tpidr;
}
public static ulong GetCntfrqEl0()
{
return GetContext().CntfrqEl0;
@ -117,13 +140,31 @@ namespace ARMeilleure.Instructions
GetContext().Fpsr = (FPSR)value;
}
public static void SetFpscr(uint value)
{
ExecutionContext context = GetContext();
context.SetFPstateFlag(FPState.NFlag, (value & (1u << 31)) != 0);
context.SetFPstateFlag(FPState.ZFlag, (value & (1u << 30)) != 0);
context.SetFPstateFlag(FPState.CFlag, (value & (1u << 29)) != 0);
context.SetFPstateFlag(FPState.VFlag, (value & (1u << 28)) != 0);
context.Fpsr = FPSR.A32Mask & (FPSR)value;
context.Fpcr = FPCR.A32Mask & (FPCR)value;
}
public static void SetTpidrEl0(ulong value)
{
GetContext().TpidrEl0 = (long)value;
}
#endregion
#region "Read"
public static void SetTpidrEl032(uint value)
{
GetContext().TpidrEl0 = (long)value;
}
#endregion
#region "Read"
public static byte ReadByte(ulong address)
{
return GetMemoryManager().ReadByte((long)address);

View file

@ -420,6 +420,26 @@ namespace ARMeilleure.Instructions
return MathF.Truncate(value);
}
}
public static int FloatToInt32(float value)
{
return SatF32ToS32(RoundF(value));
}
public static int DoubleToInt32(double value)
{
return SatF64ToS32(Round(value));
}
public static uint FloatToUInt32(float value)
{
return SatF32ToU32(RoundF(value));
}
public static uint DoubleToUInt32(double value)
{
return SatF64ToU32(Round(value));
}
#endregion
#region "Saturation"

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,7 @@ namespace ARMeilleure.IntermediateRepresentation
{
Integer,
Vector,
Flag
Flag,
FpFlag
}
}

View file

@ -36,6 +36,7 @@ namespace ARMeilleure.State
public FPCR Fpcr { get; set; }
public FPSR Fpsr { get; set; }
public FPCR StandardFpcrValue => (Fpcr & (FPCR.Ahp)) | FPCR.Dn | FPCR.Fz;
public bool IsAarch32 { get; set; }
@ -90,6 +91,9 @@ namespace ARMeilleure.State
public bool GetPstateFlag(PState flag) => _nativeContext.GetPstateFlag(flag);
public void SetPstateFlag(PState flag, bool value) => _nativeContext.SetPstateFlag(flag, value);
public bool GetFPstateFlag(FPState flag) => _nativeContext.GetFPStateFlag(flag);
public void SetFPstateFlag(FPState flag, bool value) => _nativeContext.SetFPStateFlag(flag, value);
internal void CheckInterrupt()
{
if (_interrupted)

View file

@ -3,12 +3,14 @@ using System;
namespace ARMeilleure.State
{
[Flags]
public enum FPCR
public enum FPCR : uint
{
Ufe = 1 << 11,
Fz = 1 << 24,
Dn = 1 << 25,
Ahp = 1 << 26
Ahp = 1 << 26,
A32Mask = 0x07ffff00
}
public static class FPCRExtensions

View file

@ -3,9 +3,11 @@ using System;
namespace ARMeilleure.State
{
[Flags]
public enum FPSR
public enum FPSR : uint
{
Ufc = 1 << 3,
Qc = 1 << 27
Qc = 1 << 27,
A32Mask = 0xf800000f
}
}

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ARMeilleure.State
{
[Flags]
public enum FPState
{
VFlag = 28,
CFlag = 29,
ZFlag = 30,
NFlag = 31
}
}

View file

@ -14,7 +14,8 @@ namespace ARMeilleure.State
private const int TotalSize = RegisterConsts.IntRegsCount * IntSize +
RegisterConsts.VecRegsCount * VecSize +
RegisterConsts.FlagsCount * FlagSize + ExtraSize;
RegisterConsts.FlagsCount * FlagSize +
RegisterConsts.FpFlagsCount * FlagSize + ExtraSize;
public IntPtr BasePtr { get; }
@ -100,6 +101,38 @@ namespace ARMeilleure.State
Marshal.WriteInt32(BasePtr, offset, value ? 1 : 0);
}
public bool GetFPStateFlag(FPState flag)
{
if ((uint)flag >= RegisterConsts.FlagsCount)
{
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
}
int offset =
RegisterConsts.IntRegsCount * IntSize +
RegisterConsts.VecRegsCount * VecSize +
RegisterConsts.FlagsCount * FlagSize + (int)flag * FlagSize;
int value = Marshal.ReadInt32(BasePtr, offset);
return value != 0;
}
public void SetFPStateFlag(FPState flag, bool value)
{
if ((uint)flag >= RegisterConsts.FlagsCount)
{
throw new ArgumentException($"Invalid flag \"{flag}\" specified.");
}
int offset =
RegisterConsts.IntRegsCount * IntSize +
RegisterConsts.VecRegsCount * VecSize +
RegisterConsts.FlagsCount * FlagSize + (int)flag * FlagSize;
Marshal.WriteInt32(BasePtr, offset, value ? 1 : 0);
}
public int GetCounter()
{
return Marshal.ReadInt32(BasePtr, GetCounterOffset());
@ -146,7 +179,8 @@ namespace ARMeilleure.State
{
return RegisterConsts.IntRegsCount * IntSize +
RegisterConsts.VecRegsCount * VecSize +
RegisterConsts.FlagsCount * FlagSize;
RegisterConsts.FlagsCount * FlagSize +
RegisterConsts.FpFlagsCount * FlagSize;
}
public void Dispose()

View file

@ -32,6 +32,7 @@ namespace ARMeilleure.State
public const int SpFiq = 29;
public const int LrFiq = 30;
public const int Aarch32Sp = 13;
public const int Aarch32Lr = 14;
public const int Aarch32Pc = 15;

View file

@ -5,8 +5,10 @@ namespace ARMeilleure.State
public const int IntRegsCount = 32;
public const int VecRegsCount = 32;
public const int FlagsCount = 32;
public const int FpFlagsCount = 32;
public const int IntAndVecRegsCount = IntRegsCount + VecRegsCount;
public const int TotalCount = IntRegsCount + VecRegsCount + FlagsCount;
public const int FpFlagsOffset = IntRegsCount + VecRegsCount + FlagsCount;
public const int TotalCount = IntRegsCount + VecRegsCount + FlagsCount + FpFlagsCount;
public const int ZeroIndex = 31;
}

View file

@ -10,6 +10,7 @@ namespace ARMeilleure.Translation
{
private const long CallerSavedIntRegistersMask = 0x7fL << 9;
private const long PStateNzcvFlagsMask = 0xfL << 60;
private const long FpStateNzcvFlagsMask = 0xfL << 60;
private const long CallerSavedVecRegistersMask = 0xffffL << 16;
@ -68,7 +69,7 @@ namespace ARMeilleure.Translation
}
}
public static void RunPass(ControlFlowGraph cfg, bool isCompleteFunction)
public static void RunPass(ControlFlowGraph cfg, ExecutionMode mode, bool isCompleteFunction)
{
// Compute local register inputs and outputs used inside blocks.
RegisterMask[] localInputs = new RegisterMask[cfg.Blocks.Count];
@ -205,8 +206,8 @@ namespace ARMeilleure.Translation
// It always needs a context load as it is the first block to run.
if (block.Predecessors.Count == 0 || hasContextLoad)
{
LoadLocals(block, globalInputs[block.Index].VecMask, RegisterType.Vector);
LoadLocals(block, globalInputs[block.Index].IntMask, RegisterType.Integer);
LoadLocals(block, globalInputs[block.Index].VecMask, RegisterType.Vector, mode);
LoadLocals(block, globalInputs[block.Index].IntMask, RegisterType.Integer, mode);
}
bool hasContextStore = HasContextStore(block);
@ -218,8 +219,8 @@ namespace ARMeilleure.Translation
if (EndsWithReturn(block) || hasContextStore)
{
StoreLocals(block, globalOutputs[block.Index].IntMask, RegisterType.Integer, isCompleteFunction);
StoreLocals(block, globalOutputs[block.Index].VecMask, RegisterType.Vector, isCompleteFunction);
StoreLocals(block, globalOutputs[block.Index].IntMask, RegisterType.Integer, mode, isCompleteFunction);
StoreLocals(block, globalOutputs[block.Index].VecMask, RegisterType.Vector, mode, isCompleteFunction);
}
}
}
@ -263,6 +264,7 @@ namespace ARMeilleure.Translation
{
case RegisterType.Flag: intMask = (1L << RegsCount) << register.Index; break;
case RegisterType.Integer: intMask = 1L << register.Index; break;
case RegisterType.FpFlag: vecMask = (1L << RegsCount) << register.Index; break;
case RegisterType.Vector: vecMask = 1L << register.Index; break;
}
@ -278,7 +280,7 @@ namespace ARMeilleure.Translation
return oldValue != value;
}
private static void LoadLocals(BasicBlock block, long inputs, RegisterType baseType)
private static void LoadLocals(BasicBlock block, long inputs, RegisterType baseType, ExecutionMode mode)
{
Operand arg0 = Local(OperandType.I64);
@ -291,7 +293,7 @@ namespace ARMeilleure.Translation
continue;
}
Operand dest = GetRegFromBit(bit, baseType);
Operand dest = GetRegFromBit(bit, baseType, mode);
long offset = NativeContext.GetRegisterOffset(dest.GetRegister());
@ -311,7 +313,7 @@ namespace ARMeilleure.Translation
block.Operations.AddFirst(loadArg0);
}
private static void StoreLocals(BasicBlock block, long outputs, RegisterType baseType, bool isCompleteFunction)
private static void StoreLocals(BasicBlock block, long outputs, RegisterType baseType, ExecutionMode mode, bool isCompleteFunction)
{
if (Optimizations.AssumeStrictAbiCompliance && isCompleteFunction)
{
@ -319,7 +321,7 @@ namespace ARMeilleure.Translation
{
outputs = ClearCallerSavedIntRegs(outputs);
}
else /* if (baseType == RegisterType.Vector) */
else /* if (baseType == RegisterType.Vector || baseType == RegisterType.FpFlag) */
{
outputs = ClearCallerSavedVecRegs(outputs);
}
@ -340,7 +342,7 @@ namespace ARMeilleure.Translation
continue;
}
Operand source = GetRegFromBit(bit, baseType);
Operand source = GetRegFromBit(bit, baseType, mode);
long offset = NativeContext.GetRegisterOffset(source.GetRegister());
@ -356,28 +358,33 @@ namespace ARMeilleure.Translation
}
}
private static Operand GetRegFromBit(int bit, RegisterType baseType)
private static Operand GetRegFromBit(int bit, RegisterType baseType, ExecutionMode mode)
{
if (bit < RegsCount)
{
return new Operand(bit, baseType, GetOperandType(baseType));
return new Operand(bit, baseType, GetOperandType(baseType, mode));
}
else if (baseType == RegisterType.Integer)
{
return new Operand(bit & RegsMask, RegisterType.Flag, OperandType.I32);
}
else if (baseType == RegisterType.Vector)
{
return new Operand(bit & RegsMask, RegisterType.FpFlag, OperandType.I32);
}
else
{
throw new ArgumentOutOfRangeException(nameof(bit));
}
}
private static OperandType GetOperandType(RegisterType type)
private static OperandType GetOperandType(RegisterType type, ExecutionMode mode)
{
switch (type)
{
case RegisterType.Flag: return OperandType.I32;
case RegisterType.Integer: return OperandType.I64;
case RegisterType.FpFlag: return OperandType.I32;
case RegisterType.Integer: return (mode == ExecutionMode.Aarch64) ? OperandType.I64 : OperandType.I32;
case RegisterType.Vector: return OperandType.V128;
}
@ -405,7 +412,7 @@ namespace ARMeilleure.Translation
private static long ClearCallerSavedVecRegs(long mask)
{
// TODO: ARM32 support.
mask &= ~CallerSavedVecRegistersMask;
mask &= ~(CallerSavedVecRegistersMask | FpStateNzcvFlagsMask);
return mask;
}

View file

@ -0,0 +1,16 @@
using ARMeilleure.State;
namespace ARMeilleure.Translation
{
struct RejitRequest
{
public ulong Address;
public ExecutionMode Mode;
public RejitRequest(ulong address, ExecutionMode mode)
{
Address = address;
Mode = mode;
}
}
}

View file

@ -268,10 +268,14 @@ namespace ARMeilleure.Translation
{
return RegisterConsts.IntRegsCount + reg.Index;
}
else /* if (reg.Type == RegisterType.Flag) */
else if (reg.Type == RegisterType.Flag)
{
return RegisterConsts.IntAndVecRegsCount + reg.Index;
}
else /* if (reg.Type == RegisterType.FpFlag) */
{
return RegisterConsts.FpFlagsOffset + reg.Index;
}
}
private static Register GetRegisterFromId(int id)
@ -284,10 +288,14 @@ namespace ARMeilleure.Translation
{
return new Register(id - RegisterConsts.IntRegsCount, RegisterType.Vector);
}
else /* if (id < RegisterConsts.TotalCount) */
else if (id < RegisterConsts.FpFlagsOffset)
{
return new Register(id - RegisterConsts.IntAndVecRegsCount, RegisterType.Flag);
}
else /* if (id < RegisterConsts.TotalCount) */
{
return new Register(id - RegisterConsts.FpFlagsOffset, RegisterType.FpFlag);
}
}
}
}

View file

@ -20,7 +20,7 @@ namespace ARMeilleure.Translation
private ConcurrentDictionary<ulong, TranslatedFunction> _funcs;
private PriorityQueue<ulong> _backgroundQueue;
private PriorityQueue<RejitRequest> _backgroundQueue;
private AutoResetEvent _backgroundTranslatorEvent;
@ -32,7 +32,7 @@ namespace ARMeilleure.Translation
_funcs = new ConcurrentDictionary<ulong, TranslatedFunction>();
_backgroundQueue = new PriorityQueue<ulong>(2);
_backgroundQueue = new PriorityQueue<RejitRequest>(2);
_backgroundTranslatorEvent = new AutoResetEvent(false);
}
@ -41,11 +41,11 @@ namespace ARMeilleure.Translation
{
while (_threadCount != 0)
{
if (_backgroundQueue.TryDequeue(out ulong address))
if (_backgroundQueue.TryDequeue(out RejitRequest request))
{
TranslatedFunction func = Translate(address, ExecutionMode.Aarch64, highCq: true);
TranslatedFunction func = Translate(request.Address, request.Mode, highCq: true);
_funcs.AddOrUpdate(address, func, (key, oldFunc) => func);
_funcs.AddOrUpdate(request.Address, func, (key, oldFunc) => func);
}
else
{
@ -114,7 +114,7 @@ namespace ARMeilleure.Translation
}
else if (isCallTarget && func.ShouldRejit())
{
_backgroundQueue.Enqueue(0, address);
_backgroundQueue.Enqueue(0, new RejitRequest(address, mode));
_backgroundTranslatorEvent.Set();
}
@ -149,7 +149,7 @@ namespace ARMeilleure.Translation
Logger.StartPass(PassName.RegisterUsage);
RegisterUsage.RunPass(cfg, isCompleteFunction: false);
RegisterUsage.RunPass(cfg, mode, isCompleteFunction: false);
Logger.EndPass(PassName.RegisterUsage);

View file

@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.Tests.Unicorn.Native
{
public enum Arm32Register
{
INVALID = 0,
APSR,
APSR_NZCV,
CPSR,
FPEXC,
FPINST,
FPSCR,
FPSCR_NZCV,
FPSID,
ITSTATE,
LR,
PC,
SP,
SPSR,
D0,
D1,
D2,
D3,
D4,
D5,
D6,
D7,
D8,
D9,
D10,
D11,
D12,
D13,
D14,
D15,
D16,
D17,
D18,
D19,
D20,
D21,
D22,
D23,
D24,
D25,
D26,
D27,
D28,
D29,
D30,
D31,
FPINST2,
MVFR0,
MVFR1,
MVFR2,
Q0,
Q1,
Q2,
Q3,
Q4,
Q5,
Q6,
Q7,
Q8,
Q9,
Q10,
Q11,
Q12,
Q13,
Q14,
Q15,
R0,
R1,
R2,
R3,
R4,
R5,
R6,
R7,
R8,
R9,
R10,
R11,
R12,
S0,
S1,
S2,
S3,
S4,
S5,
S6,
S7,
S8,
S9,
S10,
S11,
S12,
S13,
S14,
S15,
S16,
S17,
S18,
S19,
S20,
S21,
S22,
S23,
S24,
S25,
S26,
S27,
S28,
S29,
S30,
S31,
C1_C0_2,
C13_C0_2,
C13_C0_3,
IPSR,
MSP,
PSP,
CONTROL,
ENDING,
// Alias registers.
R13 = SP,
R14 = LR,
R15 = PC,
SB = R9,
SL = R10,
FP = R11,
IP = R12,
}
}

View file

@ -0,0 +1,280 @@
using Ryujinx.Tests.Unicorn.Native;
using System;
namespace Ryujinx.Tests.Unicorn
{
public class UnicornAArch32
{
internal readonly IntPtr uc;
public IndexedProperty<int, uint> R
{
get
{
return new IndexedProperty<int, uint>(
(int i) => GetX(i),
(int i, uint value) => SetX(i, value));
}
}
public IndexedProperty<int, SimdValue> Q
{
get
{
return new IndexedProperty<int, SimdValue>(
(int i) => GetQ(i),
(int i, SimdValue value) => SetQ(i, value));
}
}
public uint LR
{
get => GetRegister(Arm32Register.LR);
set => SetRegister(Arm32Register.LR, value);
}
public uint SP
{
get => GetRegister(Arm32Register.SP);
set => SetRegister(Arm32Register.SP, value);
}
public uint PC
{
get => GetRegister(Arm32Register.PC);
set => SetRegister(Arm32Register.PC, value);
}
public uint APSR
{
get => (uint)GetRegister(Arm32Register.APSR);
set => SetRegister(Arm32Register.APSR, (uint)value);
}
public int Fpscr
{
get => (int)GetRegister(Arm32Register.FPSCR) | ((int)GetRegister(Arm32Register.FPSCR_NZCV));
set => SetRegister(Arm32Register.FPSCR, (uint)value);
}
public bool OverflowFlag
{
get => (APSR & 0x10000000u) != 0;
set => APSR = (APSR & ~0x10000000u) | (value ? 0x10000000u : 0u);
}
public bool CarryFlag
{
get => (APSR & 0x20000000u) != 0;
set => APSR = (APSR & ~0x20000000u) | (value ? 0x20000000u : 0u);
}
public bool ZeroFlag
{
get => (APSR & 0x40000000u) != 0;
set => APSR = (APSR & ~0x40000000u) | (value ? 0x40000000u : 0u);
}
public bool NegativeFlag
{
get => (APSR & 0x80000000u) != 0;
set => APSR = (APSR & ~0x80000000u) | (value ? 0x80000000u : 0u);
}
public UnicornAArch32()
{
Interface.Checked(Interface.uc_open(UnicornArch.UC_ARCH_ARM, UnicornMode.UC_MODE_LITTLE_ENDIAN, out uc));
SetRegister(Arm32Register.C1_C0_2, GetRegister(Arm32Register.C1_C0_2) | 0xf00000);
SetRegister(Arm32Register.FPEXC, 0x40000000);
}
~UnicornAArch32()
{
Interface.Checked(Native.Interface.uc_close(uc));
}
public void RunForCount(ulong count)
{
Interface.Checked(Native.Interface.uc_emu_start(uc, this.PC, 0xFFFFFFFFFFFFFFFFu, 0, count));
}
public void Step()
{
RunForCount(1);
}
private static Arm32Register[] XRegisters = new Arm32Register[16]
{
Arm32Register.R0,
Arm32Register.R1,
Arm32Register.R2,
Arm32Register.R3,
Arm32Register.R4,
Arm32Register.R5,
Arm32Register.R6,
Arm32Register.R7,
Arm32Register.R8,
Arm32Register.R9,
Arm32Register.R10,
Arm32Register.R11,
Arm32Register.R12,
Arm32Register.R13,
Arm32Register.R14,
Arm32Register.R15,
};
private static Arm32Register[] QRegisters = new Arm32Register[16]
{
Arm32Register.Q0,
Arm32Register.Q1,
Arm32Register.Q2,
Arm32Register.Q3,
Arm32Register.Q4,
Arm32Register.Q5,
Arm32Register.Q6,
Arm32Register.Q7,
Arm32Register.Q8,
Arm32Register.Q9,
Arm32Register.Q10,
Arm32Register.Q11,
Arm32Register.Q12,
Arm32Register.Q13,
Arm32Register.Q14,
Arm32Register.Q15
};
public uint GetX(int index)
{
if ((uint)index > 15)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return GetRegister(XRegisters[index]);
}
public void SetX(int index, uint value)
{
if ((uint)index > 15)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
SetRegister(XRegisters[index], value);
}
public SimdValue GetQ(int index)
{
if ((uint)index > 15)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
// Getting quadword registers from Unicorn A32 seems to be broken, so we combine its 2 doubleword registers instead.
return GetVector((Arm32Register)((int)Arm32Register.D0 + index * 2));
}
public void SetQ(int index, SimdValue value)
{
if ((uint)index > 15)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
SetVector((Arm32Register)((int)Arm32Register.D0 + index * 2), value);
}
public uint GetRegister(Arm32Register register)
{
byte[] data = new byte[4];
Interface.Checked(Native.Interface.uc_reg_read(uc, (int)register, data));
return (uint)BitConverter.ToInt32(data, 0);
}
public void SetRegister(Arm32Register register, uint value)
{
byte[] data = BitConverter.GetBytes(value);
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
}
public SimdValue GetVector(Arm32Register register)
{
byte[] data = new byte[8];
Interface.Checked(Interface.uc_reg_read(uc, (int)register, data));
ulong lo = BitConverter.ToUInt64(data, 0);
Interface.Checked(Interface.uc_reg_read(uc, (int)register + 1, data));
ulong hi = BitConverter.ToUInt64(data, 0);
return new SimdValue(lo, hi);
}
private void SetVector(Arm32Register register, SimdValue value)
{
byte[] data = BitConverter.GetBytes(value.GetUInt64(0));
Interface.Checked(Interface.uc_reg_write(uc, (int)register, data));
data = BitConverter.GetBytes(value.GetUInt64(1));
Interface.Checked(Interface.uc_reg_write(uc, (int)register + 1, data));
}
public byte[] MemoryRead(ulong address, ulong size)
{
byte[] value = new byte[size];
Interface.Checked(Interface.uc_mem_read(uc, address, value, size));
return value;
}
public byte MemoryRead8(ulong address) => MemoryRead(address, 1)[0];
public UInt16 MemoryRead16(ulong address) => (UInt16)BitConverter.ToInt16(MemoryRead(address, 2), 0);
public UInt32 MemoryRead32(ulong address) => (UInt32)BitConverter.ToInt32(MemoryRead(address, 4), 0);
public UInt64 MemoryRead64(ulong address) => (UInt64)BitConverter.ToInt64(MemoryRead(address, 8), 0);
public void MemoryWrite(ulong address, byte[] value)
{
Interface.Checked(Interface.uc_mem_write(uc, address, value, (ulong)value.Length));
}
public void MemoryWrite8(ulong address, byte value) => MemoryWrite(address, new byte[] { value });
public void MemoryWrite16(ulong address, Int16 value) => MemoryWrite(address, BitConverter.GetBytes(value));
public void MemoryWrite16(ulong address, UInt16 value) => MemoryWrite(address, BitConverter.GetBytes(value));
public void MemoryWrite32(ulong address, Int32 value) => MemoryWrite(address, BitConverter.GetBytes(value));
public void MemoryWrite32(ulong address, UInt32 value) => MemoryWrite(address, BitConverter.GetBytes(value));
public void MemoryWrite64(ulong address, Int64 value) => MemoryWrite(address, BitConverter.GetBytes(value));
public void MemoryWrite64(ulong address, UInt64 value) => MemoryWrite(address, BitConverter.GetBytes(value));
public void MemoryMap(ulong address, ulong size, MemoryPermission permissions)
{
Interface.Checked(Interface.uc_mem_map(uc, address, size, (uint)permissions));
}
public void MemoryUnmap(ulong address, ulong size)
{
Interface.Checked(Interface.uc_mem_unmap(uc, address, size));
}
public void MemoryProtect(ulong address, ulong size, MemoryPermission permissions)
{
Interface.Checked(Interface.uc_mem_protect(uc, address, size, (uint)permissions));
}
public static bool IsAvailable()
{
try
{
Interface.uc_version(out _, out _);
return true;
}
catch (DllNotFoundException)
{
return false;
}
}
}
}

View file

@ -0,0 +1,530 @@
using ARMeilleure.Memory;
using ARMeilleure.State;
using ARMeilleure.Translation;
using NUnit.Framework;
using Ryujinx.Tests.Unicorn;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Tests.Cpu
{
[TestFixture]
public class CpuTest32
{
private uint _currAddress;
private long _size;
private uint _entryPoint;
private IntPtr _ramPointer;
private MemoryManager _memory;
private ExecutionContext _context;
private Translator _translator;
private static bool _unicornAvailable;
private UnicornAArch32 _unicornEmu;
private bool usingMemory;
static CpuTest32()
{
_unicornAvailable = UnicornAArch32.IsAvailable();
if (!_unicornAvailable)
{
Console.WriteLine("WARNING: Could not find Unicorn.");
}
}
[SetUp]
public void Setup()
{
_currAddress = 0x1000;
_size = 0x1000;
_entryPoint = _currAddress;
_ramPointer = Marshal.AllocHGlobal(new IntPtr(_size * 2));
_memory = new MemoryManager(_ramPointer, addressSpaceBits: 16, useFlatPageTable: true);
_memory.Map((long)_currAddress, 0, _size*2);
_context = new ExecutionContext();
_context.IsAarch32 = true;
_translator = new Translator(_memory);
if (_unicornAvailable)
{
_unicornEmu = new UnicornAArch32();
_unicornEmu.MemoryMap(_currAddress, (ulong)_size, MemoryPermission.READ | MemoryPermission.EXEC);
_unicornEmu.MemoryMap((ulong)(_currAddress + _size), (ulong)_size, MemoryPermission.READ | MemoryPermission.WRITE);
_unicornEmu.PC = _entryPoint;
}
}
[TearDown]
public void Teardown()
{
Marshal.FreeHGlobal(_ramPointer);
_memory = null;
_context = null;
_translator = null;
_unicornEmu = null;
}
protected void Reset()
{
Teardown();
Setup();
}
protected void Opcode(uint opcode)
{
_memory.WriteUInt32((long)_currAddress, opcode);
if (_unicornAvailable)
{
_unicornEmu.MemoryWrite32((ulong)_currAddress, opcode);
}
_currAddress += 4;
}
protected ExecutionContext GetContext() => _context;
protected void SetContext(uint r0 = 0,
uint r1 = 0,
uint r2 = 0,
uint r3 = 0,
uint sp = 0,
V128 v0 = default,
V128 v1 = default,
V128 v2 = default,
V128 v3 = default,
V128 v4 = default,
V128 v5 = default,
V128 v14 = default,
V128 v15 = default,
bool overflow = false,
bool carry = false,
bool zero = false,
bool negative = false,
int fpscr = 0)
{
_context.SetX(0, r0);
_context.SetX(1, r1);
_context.SetX(2, r2);
_context.SetX(3, r3);
_context.SetX(0xd, sp);
_context.SetV(0, v0);
_context.SetV(1, v1);
_context.SetV(2, v2);
_context.SetV(3, v3);
_context.SetV(4, v4);
_context.SetV(5, v5);
_context.SetV(14, v14);
_context.SetV(15, v15);
_context.SetPstateFlag(PState.VFlag, overflow);
_context.SetPstateFlag(PState.CFlag, carry);
_context.SetPstateFlag(PState.ZFlag, zero);
_context.SetPstateFlag(PState.NFlag, negative);
_context.Fpsr = FPSR.A32Mask & (FPSR)fpscr;
_context.Fpcr = FPCR.A32Mask & (FPCR)fpscr;
if (_unicornAvailable)
{
_unicornEmu.R[0] = r0;
_unicornEmu.R[1] = r1;
_unicornEmu.R[2] = r2;
_unicornEmu.R[3] = r3;
_unicornEmu.SP = sp;
_unicornEmu.Q[0] = V128ToSimdValue(v0);
_unicornEmu.Q[1] = V128ToSimdValue(v1);
_unicornEmu.Q[2] = V128ToSimdValue(v2);
_unicornEmu.Q[3] = V128ToSimdValue(v3);
_unicornEmu.Q[4] = V128ToSimdValue(v4);
_unicornEmu.Q[5] = V128ToSimdValue(v5);
_unicornEmu.Q[14] = V128ToSimdValue(v14);
_unicornEmu.Q[15] = V128ToSimdValue(v15);
_unicornEmu.OverflowFlag = overflow;
_unicornEmu.CarryFlag = carry;
_unicornEmu.ZeroFlag = zero;
_unicornEmu.NegativeFlag = negative;
_unicornEmu.Fpscr = fpscr;
}
}
protected void ExecuteOpcodes()
{
_translator.Execute(_context, _entryPoint);
if (_unicornAvailable)
{
_unicornEmu.RunForCount((ulong)(_currAddress - _entryPoint - 4) / 4);
}
}
protected ExecutionContext SingleOpcode(uint opcode,
uint r0 = 0,
uint r1 = 0,
uint r2 = 0,
uint r3 = 0,
uint sp = 0,
V128 v0 = default,
V128 v1 = default,
V128 v2 = default,
V128 v3 = default,
V128 v4 = default,
V128 v5 = default,
V128 v14 = default,
V128 v15 = default,
bool overflow = false,
bool carry = false,
bool zero = false,
bool negative = false,
int fpscr = 0,
bool copyFpFlags = false)
{
Opcode(opcode);
if (copyFpFlags)
{
Opcode(0xeef1fa10);
}
Opcode(0xe12fff1e); // BX LR
SetContext(r0, r1, r2, r3, sp, v0, v1, v2, v3, v4, v5, v14, v15, overflow, carry, zero, negative, fpscr);
ExecuteOpcodes();
return GetContext();
}
protected void SetWorkingMemory(byte[] data)
{
_memory.WriteBytes(0x2000, data);
if (_unicornAvailable)
{
_unicornEmu.MemoryWrite((ulong)(0x2000), data);
}
usingMemory = true; // When true, CompareAgainstUnicorn checks the working memory for equality too.
}
/// <summary>Rounding Mode control field.</summary>
public enum RMode
{
/// <summary>Round to Nearest mode.</summary>
Rn,
/// <summary>Round towards Plus Infinity mode.</summary>
Rp,
/// <summary>Round towards Minus Infinity mode.</summary>
Rm,
/// <summary>Round towards Zero mode.</summary>
Rz
};
/// <summary>Floating-point Control Register.</summary>
protected enum Fpcr
{
/// <summary>Rounding Mode control field.</summary>
RMode = 22,
/// <summary>Flush-to-zero mode control bit.</summary>
Fz = 24,
/// <summary>Default NaN mode control bit.</summary>
Dn = 25,
/// <summary>Alternative half-precision control bit.</summary>
Ahp = 26
}
/// <summary>Floating-point Status Register.</summary>
[Flags]
protected enum Fpsr
{
None = 0,
/// <summary>Invalid Operation cumulative floating-point exception bit.</summary>
Ioc = 1 << 0,
/// <summary>Divide by Zero cumulative floating-point exception bit.</summary>
Dzc = 1 << 1,
/// <summary>Overflow cumulative floating-point exception bit.</summary>
Ofc = 1 << 2,
/// <summary>Underflow cumulative floating-point exception bit.</summary>
Ufc = 1 << 3,
/// <summary>Inexact cumulative floating-point exception bit.</summary>
Ixc = 1 << 4,
/// <summary>Input Denormal cumulative floating-point exception bit.</summary>
Idc = 1 << 7,
/// <summary>Cumulative saturation bit.</summary>
Qc = 1 << 27,
/// <summary>NZCV flags</summary>
Nzcv = (1 << 28) | (1 << 29) | (1 << 30) | (1 << 31)
}
[Flags]
protected enum FpSkips
{
None = 0,
IfNaNS = 1,
IfNaND = 2,
IfUnderflow = 4,
IfOverflow = 8
}
protected enum FpTolerances
{
None,
UpToOneUlpsS,
UpToOneUlpsD
}
protected void CompareAgainstUnicorn(
Fpsr fpsrMask = Fpsr.None,
FpSkips fpSkips = FpSkips.None,
FpTolerances fpTolerances = FpTolerances.None)
{
if (!_unicornAvailable)
{
return;
}
if (fpSkips != FpSkips.None)
{
ManageFpSkips(fpSkips);
}
Assert.That(_context.GetX(0), Is.EqualTo(_unicornEmu.R[0]));
Assert.That(_context.GetX(1), Is.EqualTo(_unicornEmu.R[1]));
Assert.That(_context.GetX(2), Is.EqualTo(_unicornEmu.R[2]));
Assert.That(_context.GetX(3), Is.EqualTo(_unicornEmu.R[3]));
Assert.That(_context.GetX(4), Is.EqualTo(_unicornEmu.R[4]));
Assert.That(_context.GetX(5), Is.EqualTo(_unicornEmu.R[5]));
Assert.That(_context.GetX(6), Is.EqualTo(_unicornEmu.R[6]));
Assert.That(_context.GetX(7), Is.EqualTo(_unicornEmu.R[7]));
Assert.That(_context.GetX(8), Is.EqualTo(_unicornEmu.R[8]));
Assert.That(_context.GetX(9), Is.EqualTo(_unicornEmu.R[9]));
Assert.That(_context.GetX(10), Is.EqualTo(_unicornEmu.R[10]));
Assert.That(_context.GetX(11), Is.EqualTo(_unicornEmu.R[11]));
Assert.That(_context.GetX(12), Is.EqualTo(_unicornEmu.R[12]));
Assert.That(_context.GetX(13), Is.EqualTo(_unicornEmu.R[13]));
Assert.That(_context.GetX(14), Is.EqualTo(_unicornEmu.R[14]));
if (fpTolerances == FpTolerances.None)
{
Assert.That(V128ToSimdValue(_context.GetV(0)), Is.EqualTo(_unicornEmu.Q[0]));
}
else
{
ManageFpTolerances(fpTolerances);
}
Assert.That(V128ToSimdValue(_context.GetV(1)), Is.EqualTo(_unicornEmu.Q[1]));
Assert.That(V128ToSimdValue(_context.GetV(2)), Is.EqualTo(_unicornEmu.Q[2]));
Assert.That(V128ToSimdValue(_context.GetV(3)), Is.EqualTo(_unicornEmu.Q[3]));
Assert.That(V128ToSimdValue(_context.GetV(4)), Is.EqualTo(_unicornEmu.Q[4]));
Assert.That(V128ToSimdValue(_context.GetV(5)), Is.EqualTo(_unicornEmu.Q[5]));
Assert.That(V128ToSimdValue(_context.GetV(6)), Is.EqualTo(_unicornEmu.Q[6]));
Assert.That(V128ToSimdValue(_context.GetV(7)), Is.EqualTo(_unicornEmu.Q[7]));
Assert.That(V128ToSimdValue(_context.GetV(8)), Is.EqualTo(_unicornEmu.Q[8]));
Assert.That(V128ToSimdValue(_context.GetV(9)), Is.EqualTo(_unicornEmu.Q[9]));
Assert.That(V128ToSimdValue(_context.GetV(10)), Is.EqualTo(_unicornEmu.Q[10]));
Assert.That(V128ToSimdValue(_context.GetV(11)), Is.EqualTo(_unicornEmu.Q[11]));
Assert.That(V128ToSimdValue(_context.GetV(12)), Is.EqualTo(_unicornEmu.Q[12]));
Assert.That(V128ToSimdValue(_context.GetV(13)), Is.EqualTo(_unicornEmu.Q[13]));
Assert.That(V128ToSimdValue(_context.GetV(14)), Is.EqualTo(_unicornEmu.Q[14]));
Assert.That(V128ToSimdValue(_context.GetV(15)), Is.EqualTo(_unicornEmu.Q[15]));
Assert.That((int)_context.Fpcr | ((int)_context.Fpsr & (int)fpsrMask), Is.EqualTo(_unicornEmu.Fpscr));
Assert.That(_context.GetPstateFlag(PState.VFlag), Is.EqualTo(_unicornEmu.OverflowFlag));
Assert.That(_context.GetPstateFlag(PState.CFlag), Is.EqualTo(_unicornEmu.CarryFlag));
Assert.That(_context.GetPstateFlag(PState.ZFlag), Is.EqualTo(_unicornEmu.ZeroFlag));
Assert.That(_context.GetPstateFlag(PState.NFlag), Is.EqualTo(_unicornEmu.NegativeFlag));
if (usingMemory)
{
byte[] meilleureMem = _memory.ReadBytes((long)(0x2000), _size);
byte[] unicornMem = _unicornEmu.MemoryRead((ulong)(0x2000), (ulong)_size);
for (int i = 0; i < _size; i++)
{
Assert.AreEqual(meilleureMem[i], unicornMem[i]);
}
}
}
private void ManageFpSkips(FpSkips fpSkips)
{
if (fpSkips.HasFlag(FpSkips.IfNaNS))
{
if (float.IsNaN(_unicornEmu.Q[0].AsFloat()))
{
Assert.Ignore("NaN test.");
}
}
else if (fpSkips.HasFlag(FpSkips.IfNaND))
{
if (double.IsNaN(_unicornEmu.Q[0].AsDouble()))
{
Assert.Ignore("NaN test.");
}
}
if (fpSkips.HasFlag(FpSkips.IfUnderflow))
{
if ((_unicornEmu.Fpscr & (int)Fpsr.Ufc) != 0)
{
Assert.Ignore("Underflow test.");
}
}
if (fpSkips.HasFlag(FpSkips.IfOverflow))
{
if ((_unicornEmu.Fpscr & (int)Fpsr.Ofc) != 0)
{
Assert.Ignore("Overflow test.");
}
}
}
private void ManageFpTolerances(FpTolerances fpTolerances)
{
bool IsNormalOrSubnormalS(float f) => float.IsNormal(f) || float.IsSubnormal(f);
bool IsNormalOrSubnormalD(double d) => double.IsNormal(d) || double.IsSubnormal(d);
if (!Is.EqualTo(_unicornEmu.Q[0]).ApplyTo(V128ToSimdValue(_context.GetV(0))).IsSuccess)
{
if (fpTolerances == FpTolerances.UpToOneUlpsS)
{
if (IsNormalOrSubnormalS(_unicornEmu.Q[0].AsFloat()) &&
IsNormalOrSubnormalS(_context.GetV(0).AsFloat()))
{
Assert.That(_context.GetV(0).GetFloat(0),
Is.EqualTo(_unicornEmu.Q[0].GetFloat(0)).Within(1).Ulps);
Assert.That(_context.GetV(0).GetFloat(1),
Is.EqualTo(_unicornEmu.Q[0].GetFloat(1)).Within(1).Ulps);
Assert.That(_context.GetV(0).GetFloat(2),
Is.EqualTo(_unicornEmu.Q[0].GetFloat(2)).Within(1).Ulps);
Assert.That(_context.GetV(0).GetFloat(3),
Is.EqualTo(_unicornEmu.Q[0].GetFloat(3)).Within(1).Ulps);
Console.WriteLine(fpTolerances);
}
else
{
Assert.That(V128ToSimdValue(_context.GetV(0)), Is.EqualTo(_unicornEmu.Q[0]));
}
}
if (fpTolerances == FpTolerances.UpToOneUlpsD)
{
if (IsNormalOrSubnormalD(_unicornEmu.Q[0].AsDouble()) &&
IsNormalOrSubnormalD(_context.GetV(0).AsDouble()))
{
Assert.That(_context.GetV(0).GetDouble(0),
Is.EqualTo(_unicornEmu.Q[0].GetDouble(0)).Within(1).Ulps);
Assert.That(_context.GetV(0).GetDouble(1),
Is.EqualTo(_unicornEmu.Q[0].GetDouble(1)).Within(1).Ulps);
Console.WriteLine(fpTolerances);
}
else
{
Assert.That(V128ToSimdValue(_context.GetV(0)), Is.EqualTo(_unicornEmu.Q[0]));
}
}
}
}
private static SimdValue V128ToSimdValue(V128 value)
{
return new SimdValue(value.GetUInt64(0), value.GetUInt64(1));
}
protected static V128 MakeVectorScalar(float value) => new V128(value);
protected static V128 MakeVectorScalar(double value) => new V128(value);
protected static V128 MakeVectorE0(ulong e0) => new V128(e0, 0);
protected static V128 MakeVectorE1(ulong e1) => new V128(0, e1);
protected static V128 MakeVectorE0E1(ulong e0, ulong e1) => new V128(e0, e1);
protected static ulong GetVectorE0(V128 vector) => vector.GetUInt64(0);
protected static ulong GetVectorE1(V128 vector) => vector.GetUInt64(1);
protected static ushort GenNormalH()
{
uint rnd;
do rnd = TestContext.CurrentContext.Random.NextUShort();
while ((rnd & 0x7C00u) == 0u ||
(~rnd & 0x7C00u) == 0u);
return (ushort)rnd;
}
protected static ushort GenSubnormalH()
{
uint rnd;
do rnd = TestContext.CurrentContext.Random.NextUShort();
while ((rnd & 0x03FFu) == 0u);
return (ushort)(rnd & 0x83FFu);
}
protected static uint GenNormalS()
{
uint rnd;
do rnd = TestContext.CurrentContext.Random.NextUInt();
while ((rnd & 0x7F800000u) == 0u ||
(~rnd & 0x7F800000u) == 0u);
return rnd;
}
protected static uint GenSubnormalS()
{
uint rnd;
do rnd = TestContext.CurrentContext.Random.NextUInt();
while ((rnd & 0x007FFFFFu) == 0u);
return rnd & 0x807FFFFFu;
}
protected static ulong GenNormalD()
{
ulong rnd;
do rnd = TestContext.CurrentContext.Random.NextULong();
while ((rnd & 0x7FF0000000000000ul) == 0ul ||
(~rnd & 0x7FF0000000000000ul) == 0ul);
return rnd;
}
protected static ulong GenSubnormalD()
{
ulong rnd;
do rnd = TestContext.CurrentContext.Random.NextULong();
while ((rnd & 0x000FFFFFFFFFFFFFul) == 0ul);
return rnd & 0x800FFFFFFFFFFFFFul;
}
}
}

View file

@ -0,0 +1,61 @@
#define Alu32
using NUnit.Framework;
using System;
namespace Ryujinx.Tests.Cpu
{
[Category("Alu32")]
public sealed class CpuTestAlu32 : CpuTest32
{
#if Alu32
#region "ValueSource (Opcodes)"
private static uint[] _Lsr_Lsl_Asr_Ror_()
{
return new uint[]
{
0xe1b00030u, // LSRS R0, R0, R0
0xe1b00010u, // LSLS R0, R0, R0
0xe1b00050u, // ASRS R0, R0, R0
0xe1b00070u // RORS R0, R0, R0
};
}
#endregion
private const int RndCnt = 2;
[Test, Pairwise, Description("RBIT <Rd>, <Rn>")]
public void Rbit_32bit([Values(0u, 0xdu)] uint rd,
[Values(1u, 0xdu)] uint rm,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn)
{
uint opcode = 0xe6ff0f30u; // RBIT R0, R0
opcode |= ((rm & 15) << 0) | ((rd & 15) << 12);
uint w31 = TestContext.CurrentContext.Random.NextUInt();
SingleOpcode(opcode, r1: wn, sp: w31);
CompareAgainstUnicorn();
}
[Test, Pairwise]
public void Lsr_Lsl_Asr_Ror([ValueSource("_Lsr_Lsl_Asr_Ror_")] uint opcode,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint shiftValue,
[Range(0, 31)] [Values(32, 256, 768, -1, -23)] int shiftAmount)
{
uint rd = 0;
uint rm = 1;
uint rs = 2;
opcode |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rs & 15) << 8);
SingleOpcode(opcode, r1: shiftValue, r2: (uint)shiftAmount);
CompareAgainstUnicorn();
}
#endif
}
}

View file

@ -0,0 +1,84 @@
#define AluRs32
using NUnit.Framework;
namespace Ryujinx.Tests.Cpu
{
[Category("AluRs32")]
public sealed class CpuTestAluRs32 : CpuTest32
{
#if AluRs32
#region "ValueSource (Opcodes)"
private static uint[] _Add_Adds_Rsb_Rsbs_()
{
return new uint[]
{
0xe0800000u, // ADD R0, R0, R0, LSL #0
0xe0900000u, // ADDS R0, R0, R0, LSL #0
0xe0600000u, // RSB R0, R0, R0, LSL #0
0xe0700000u // RSBS R0, R0, R0, LSL #0
};
}
private static uint[] _Adc_Adcs_Rsc_Rscs_Sbc_Sbcs_()
{
return new uint[]
{
0xe0a00000u, // ADC R0, R0, R0
0xe0b00000u, // ADCS R0, R0, R0
0xe0e00000u, // RSC R0, R0, R0
0xe0f00000u, // RSCS R0, R0, R0
0xe0c00000u, // SBC R0, R0, R0
0xe0d00000u // SBCS R0, R0, R0
};
}
#endregion
private const int RndCnt = 2;
private const int RndCntAmount = 2;
[Test, Pairwise]
public void Adc_Adcs_Rsc_Rscs_Sbc_Sbcs([ValueSource("_Adc_Adcs_Rsc_Rscs_Sbc_Sbcs_")] uint opcode,
[Values(0u, 13u)] uint rd,
[Values(1u, 13u)] uint rn,
[Values(2u, 13u)] uint rm,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm,
[Values] bool carryIn)
{
opcode |= ((rm & 15) << 0) | ((rn & 15) << 16) | ((rd & 15) << 12);
uint sp = TestContext.CurrentContext.Random.NextUInt();
SingleOpcode(opcode, r1: wn, r2: wm, sp: sp, carry: carryIn);
CompareAgainstUnicorn();
}
[Test, Pairwise]
public void Add_Adds_Rsb_Rsbs([ValueSource("_Add_Adds_Rsb_Rsbs_")] uint opcode,
[Values(0u, 13u)] uint rd,
[Values(1u, 13u)] uint rn,
[Values(2u, 13u)] uint rm,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wm,
[Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
[Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntAmount)] uint amount)
{
opcode |= ((rm & 15) << 0) | ((rn & 15) << 16) | ((rd & 15) << 12);
opcode |= ((shift & 3) << 5) | ((amount & 31) << 7);
uint sp = TestContext.CurrentContext.Random.NextUInt();
SingleOpcode(opcode, r1: wn, r2: wm, sp: sp);
CompareAgainstUnicorn();
}
#endif
}
}

View file

@ -0,0 +1,108 @@
#define Bf32
using NUnit.Framework;
using System;
namespace Ryujinx.Tests.Cpu
{
[Category("Bf32")]
public sealed class CpuTestBf32 : CpuTest32
{
#if Bf32
private const int RndCnt = 2;
private const int RndCntImmr = 2;
private const int RndCntImms = 2;
[Test, Pairwise, Description("BFC <Rd>, #<lsb>, #<width>")]
public void Bfc([Values(0u, 0xdu)] uint rd,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wd,
[Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint lsb,
[Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint msb)
{
msb = Math.Max(lsb, msb); // Don't test unpredictable for now.
uint opcode = 0xe7c0001fu; // BFC R0, #0, #1
opcode |= ((rd & 0xf) << 12);
opcode |= ((msb & 31) << 16) | ((lsb & 31) << 7);
uint sp = TestContext.CurrentContext.Random.NextUInt();
SingleOpcode(opcode, r0: wd, sp: sp);
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("BFI <Rd>, <Rn>, #<lsb>, #<width>")]
public void Bfi([Values(0u, 0xdu)] uint rd,
[Values(1u, 0xdu)] uint rn,
[Random(RndCnt)] uint wd,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
[Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint lsb,
[Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint msb)
{
msb = Math.Max(lsb, msb); // Don't test unpredictable for now.
uint opcode = 0xe7c00010u; // BFI R0, R0, #0, #1
opcode |= ((rd & 0xf) << 12);
opcode |= ((rn & 0xf) << 0);
opcode |= ((msb & 31) << 16) | ((lsb & 31) << 7);
uint sp = TestContext.CurrentContext.Random.NextUInt();
SingleOpcode(opcode, r0: wd, r1: wn, sp: sp);
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("UBFX <Rd>, <Rn>, #<lsb>, #<width>")]
public void Ubfx([Values(0u, 0xdu)] uint rd,
[Values(1u, 0xdu)] uint rn,
[Random(RndCnt)] uint wd,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
[Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint lsb,
[Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint widthm1)
{
if (lsb + widthm1 > 31)
{
widthm1 -= (lsb + widthm1) - 31;
}
uint opcode = 0xe7e00050u; // UBFX R0, R0, #0, #1
opcode |= ((rd & 0xf) << 12);
opcode |= ((rn & 0xf) << 0);
opcode |= ((widthm1 & 31) << 16) | ((lsb & 31) << 7);
uint sp = TestContext.CurrentContext.Random.NextUInt();
SingleOpcode(opcode, r0: wd, r1: wn, sp: sp);
CompareAgainstUnicorn();
}
[Test, Pairwise, Description("SBFX <Rd>, <Rn>, #<lsb>, #<width>")]
public void Sbfx([Values(0u, 0xdu)] uint rd,
[Values(1u, 0xdu)] uint rn,
[Random(RndCnt)] uint wd,
[Values(0x00000000u, 0x7FFFFFFFu,
0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn,
[Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImmr)] uint lsb,
[Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, RndCntImms)] uint widthm1)
{
if (lsb + widthm1 > 31)
{
widthm1 -= (lsb + widthm1) - 31;
}
uint opcode = 0xe7a00050u; // SBFX R0, R0, #0, #1
opcode |= ((rd & 0xf) << 12);
opcode |= ((rn & 0xf) << 0);
opcode |= ((widthm1 & 31) << 16) | ((lsb & 31) << 7);
uint sp = TestContext.CurrentContext.Random.NextUInt();
SingleOpcode(opcode, r0: wd, r1: wn, sp: sp);
CompareAgainstUnicorn();
}
#endif
}
}

View file

@ -0,0 +1,61 @@
#define SimdLogical32
using ARMeilleure.State;
using NUnit.Framework;
using System;
namespace Ryujinx.Tests.Cpu
{
[Category("SimdLogical32")]
public sealed class CpuTestSimdLogical32 : CpuTest32
{
#if SimdLogical32
#region "ValueSource (Opcodes)"
private static uint[] _Vbif_Vbit_Vbsl_Vand_()
{
return new uint[]
{
0xf3300110u, // VBIF D0, D0, D0
0xf3200110u, // VBIT D0, D0, D0
0xf3100110u, // VBSL D0, D0, D0
0xf2000110u // VAND D0, D0, D0
};
}
#endregion
private const int RndCnt = 2;
[Test, Pairwise]
public void Vbif_Vbit_Vbsl_Vand([ValueSource("_Vbif_Vbit_Vbsl_Vand_")] uint opcode,
[Range(0u, 4u)] uint rd,
[Range(0u, 4u)] uint rn,
[Range(0u, 4u)] uint rm,
[Random(RndCnt)] ulong z,
[Random(RndCnt)] ulong a,
[Random(RndCnt)] ulong b,
[Values] bool q)
{
if (q)
{
opcode |= 1 << 6;
rm <<= 1;
rn <<= 1;
rd <<= 1;
}
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
V128 v0 = MakeVectorE0E1(z, z);
V128 v1 = MakeVectorE0E1(a, z);
V128 v2 = MakeVectorE0E1(b, z);
SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
CompareAgainstUnicorn();
}
#endif
}
}

Some files were not shown because too many files have changed in this diff Show more