Implement some ARM32 memory instructions and CMP (#565)

* Implement ARM32 memory instructions: LDM, LDR, LDRB, LDRD, LDRH, LDRSB, LDRSH, STM, STR, STRB, STRD, STRH (immediate and register + immediate variants), implement CMP (immediate and register shifted by immediate variants)

* Rename some opcode classes and flag masks for consistency

* Fix a few suboptimal ARM32 codegen issues, only loads should be considered on decoder when checking if Rt == PC, and only NZCV flags should be considered for comparison optimizations

* Take into account Rt2 for LDRD instructions aswell when checking if the instruction changes PC

* Re-align arm32 instructions on the opcode table
This commit is contained in:
gdkchan 2019-01-29 13:06:11 -03:00 committed by GitHub
parent 8f7fcede7f
commit c1bdf19061
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 686 additions and 87 deletions

View file

@ -168,9 +168,59 @@ namespace ChocolArm64.Decoders
{
//Note: On ARM32, most ALU operations can write to R15 (PC),
//so we must consider such operations as a branch in potential aswell.
return opCode is IOpCodeBImm32 ||
opCode is IOpCodeBReg32 ||
(opCode is IOpCodeAlu32 op && op.Rd == RegisterAlias.Aarch32Pc);
if (opCode is IOpCode32Alu opAlu && opAlu.Rd == RegisterAlias.Aarch32Pc)
{
return true;
}
//Same thing for memory operations. We have the cases where PC is a target
//register (Rt == 15 or (mask & (1 << 15)) != 0), and cases where there is
//a write back to PC (wback == true && Rn == 15), however the later may
//be "undefined" depending on the CPU, so compilers should not produce that.
if (opCode is IOpCode32Mem || opCode is IOpCode32MemMult)
{
int rt, rn;
bool wBack, isLoad;
if (opCode is IOpCode32Mem opMem)
{
rt = opMem.Rt;
rn = opMem.Rn;
wBack = opMem.WBack;
isLoad = opMem.IsLoad;
//For the dual load, we also need to take into account the
//case were Rt2 == 15 (PC).
if (rt == 14 && opMem.Emitter == InstEmit32.Ldrd)
{
rt = RegisterAlias.Aarch32Pc;
}
}
else if (opCode is IOpCode32MemMult opMemMult)
{
const int pcMask = 1 << RegisterAlias.Aarch32Pc;
rt = (opMemMult.RegisterMask & pcMask) != 0 ? RegisterAlias.Aarch32Pc : 0;
rn = opMemMult.Rn;
wBack = opMemMult.PostOffset != 0;
isLoad = opMemMult.IsLoad;
}
else
{
throw new NotImplementedException($"The type \"{opCode.GetType().Name}\" is not implemented on the decoder.");
}
if ((rt == RegisterAlias.Aarch32Pc && isLoad) ||
(rn == RegisterAlias.Aarch32Pc && wBack))
{
return true;
}
}
//Explicit branch instructions.
return opCode is IOpCode32BImm ||
opCode is IOpCode32BReg;
}
private static bool IsException(OpCode64 opCode)

View file

@ -1,6 +1,6 @@
namespace ChocolArm64.Decoders
{
interface IOpCodeAlu32 : IOpCode32
interface IOpCode32Alu : IOpCode32
{
int Rd { get; }
int Rn { get; }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,11 +2,11 @@ using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeBImm32 : OpCode32, IOpCodeBImm32
class OpCode32BImm : OpCode32, IOpCode32BImm
{
public long Imm { get; private set; }
public OpCodeBImm32(Inst inst, long position, int opCode) : base(inst, position, opCode)
public OpCode32BImm(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
uint pc = GetPc();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@ using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeAluImm8T16 : OpCodeT16, IOpCodeAlu32
class OpCodeT16AluImm8 : OpCodeT16, IOpCode32Alu
{
private int _rdn;
@ -13,7 +13,7 @@ namespace ChocolArm64.Decoders
public int Imm { get; private set; }
public OpCodeAluImm8T16(Inst inst, long position, int opCode) : base(inst, position, opCode)
public OpCodeT16AluImm8(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Imm = (opCode >> 0) & 0xff;
_rdn = (opCode >> 8) & 0x7;

View file

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