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:
parent
8f7fcede7f
commit
c1bdf19061
29 changed files with 686 additions and 87 deletions
|
@ -2,6 +2,7 @@ using ChocolArm64.Decoders;
|
|||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ChocolArm64.Instructions
|
||||
{
|
||||
|
@ -26,6 +27,51 @@ namespace ChocolArm64.Instructions
|
|||
}
|
||||
}
|
||||
|
||||
public static void EmitStoreToRegister(ILEmitterCtx context, int register)
|
||||
{
|
||||
if (register == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
context.EmitStoreState();
|
||||
|
||||
EmitBxWritePc(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.EmitStint(GetRegisterAlias(context.Mode, register));
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitBxWritePc(ILEmitterCtx context)
|
||||
{
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
context.EmitLdc_I4(1);
|
||||
|
||||
context.Emit(OpCodes.And);
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
context.EmitStflg((int)PState.TBit);
|
||||
|
||||
ILLabel lblArmMode = new ILLabel();
|
||||
ILLabel lblEnd = new ILLabel();
|
||||
|
||||
context.Emit(OpCodes.Brtrue_S, lblArmMode);
|
||||
|
||||
context.EmitLdc_I4(~1);
|
||||
|
||||
context.Emit(OpCodes.Br_S, lblEnd);
|
||||
|
||||
context.MarkLabel(lblArmMode);
|
||||
|
||||
context.EmitLdc_I4(~3);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
|
||||
context.Emit(OpCodes.And);
|
||||
context.Emit(OpCodes.Conv_U8);
|
||||
context.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static int GetRegisterAlias(Aarch32Mode mode, int register)
|
||||
{
|
||||
//Only registers >= 8 are banked, with registers in the range [8, 12] being
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace ChocolArm64.Instructions
|
|||
{
|
||||
public static void Add(ILEmitterCtx context)
|
||||
{
|
||||
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
EmitAluLoadOpers(context, setCarry: false);
|
||||
|
||||
|
@ -29,9 +29,25 @@ namespace ChocolArm64.Instructions
|
|||
EmitAluStore(context);
|
||||
}
|
||||
|
||||
public static void Cmp(ILEmitterCtx context)
|
||||
{
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
EmitAluLoadOpers(context, setCarry: false);
|
||||
|
||||
context.Emit(OpCodes.Sub);
|
||||
|
||||
context.EmitZnFlagCheck();
|
||||
|
||||
EmitSubsCCheck(context);
|
||||
EmitSubsVCheck(context);
|
||||
|
||||
context.Emit(OpCodes.Pop);
|
||||
}
|
||||
|
||||
public static void Mov(ILEmitterCtx context)
|
||||
{
|
||||
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
EmitAluLoadOper2(context);
|
||||
|
||||
|
@ -45,7 +61,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
public static void Sub(ILEmitterCtx context)
|
||||
{
|
||||
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
EmitAluLoadOpers(context, setCarry: false);
|
||||
|
||||
|
@ -64,7 +80,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
private static void EmitAluStore(ILEmitterCtx context)
|
||||
{
|
||||
IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
|
||||
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
|
||||
|
||||
if (op.Rd == RegisterAlias.Aarch32Pc)
|
||||
{
|
||||
|
@ -106,6 +122,8 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
private static void EmitAluWritePc(ILEmitterCtx context)
|
||||
{
|
||||
context.EmitStoreState();
|
||||
|
||||
if (IsThumb(context.CurrOp))
|
||||
{
|
||||
context.EmitLdc_I4(~1);
|
||||
|
|
|
@ -127,7 +127,7 @@ namespace ChocolArm64.Instructions
|
|||
{
|
||||
context.EmitLdintzr(op.Rm);
|
||||
}
|
||||
else if (context.CurrOp is OpCodeAluRsImm32 op32)
|
||||
else if (context.CurrOp is OpCode32AluRsImm op32)
|
||||
{
|
||||
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm);
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ namespace ChocolArm64.Instructions
|
|||
context.EmitLdint(op.Rn);
|
||||
}
|
||||
}
|
||||
else if (context.CurrOp is IOpCodeAlu32 op32)
|
||||
else if (context.CurrOp is IOpCode32Alu op32)
|
||||
{
|
||||
InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn);
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ namespace ChocolArm64.Instructions
|
|||
switch (context.CurrOp)
|
||||
{
|
||||
//ARM32.
|
||||
case OpCodeAluImm32 op:
|
||||
case OpCode32AluImm op:
|
||||
context.EmitLdc_I4(op.Imm);
|
||||
|
||||
if (op.SetFlags && op.IsRotated)
|
||||
|
@ -182,11 +182,11 @@ namespace ChocolArm64.Instructions
|
|||
}
|
||||
break;
|
||||
|
||||
case OpCodeAluRsImm32 op:
|
||||
case OpCode32AluRsImm op:
|
||||
EmitLoadRmShiftedByImmediate(context, op, setCarry);
|
||||
break;
|
||||
|
||||
case OpCodeAluImm8T16 op:
|
||||
case OpCodeT16AluImm8 op:
|
||||
context.EmitLdc_I4(op.Imm);
|
||||
break;
|
||||
|
||||
|
@ -246,7 +246,7 @@ namespace ChocolArm64.Instructions
|
|||
}
|
||||
|
||||
//ARM32 helpers.
|
||||
private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCodeAluRsImm32 op, bool setCarry)
|
||||
private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCode32AluRsImm op, bool setCarry)
|
||||
{
|
||||
int shift = op.Imm;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace ChocolArm64.Instructions
|
|||
{
|
||||
public static void B(ILEmitterCtx context)
|
||||
{
|
||||
IOpCodeBImm32 op = (IOpCodeBImm32)context.CurrOp;
|
||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
||||
|
||||
if (context.CurrBlock.Branch != null)
|
||||
{
|
||||
|
@ -38,7 +38,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
public static void Bx(ILEmitterCtx context)
|
||||
{
|
||||
IOpCodeBReg32 op = (IOpCodeBReg32)context.CurrOp;
|
||||
IOpCode32BReg op = (IOpCode32BReg)context.CurrOp;
|
||||
|
||||
context.EmitStoreState();
|
||||
|
||||
|
@ -49,7 +49,7 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
private static void Blx(ILEmitterCtx context, bool x)
|
||||
{
|
||||
IOpCodeBImm32 op = (IOpCodeBImm32)context.CurrOp;
|
||||
IOpCode32BImm op = (IOpCode32BImm)context.CurrOp;
|
||||
|
||||
uint pc = op.GetPc();
|
||||
|
||||
|
@ -78,22 +78,5 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
InstEmitFlowHelper.EmitCall(context, op.Imm);
|
||||
}
|
||||
|
||||
private static void EmitBxWritePc(ILEmitterCtx context)
|
||||
{
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
context.EmitLdc_I4(1);
|
||||
|
||||
context.Emit(OpCodes.And);
|
||||
|
||||
context.EmitStflg((int)PState.TBit);
|
||||
|
||||
context.EmitLdc_I4(~1);
|
||||
|
||||
context.Emit(OpCodes.And);
|
||||
context.Emit(OpCodes.Conv_U8);
|
||||
context.Emit(OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
}
|
325
ChocolArm64/Instructions/InstEmitMemory32.cs
Normal file
325
ChocolArm64/Instructions/InstEmitMemory32.cs
Normal file
|
@ -0,0 +1,325 @@
|
|||
using ChocolArm64.Decoders;
|
||||
using ChocolArm64.State;
|
||||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
using static ChocolArm64.Instructions.InstEmit32Helper;
|
||||
using static ChocolArm64.Instructions.InstEmitMemoryHelper;
|
||||
|
||||
namespace ChocolArm64.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
private const int ByteSizeLog2 = 0;
|
||||
private const int HWordSizeLog2 = 1;
|
||||
private const int WordSizeLog2 = 2;
|
||||
private const int DWordSizeLog2 = 3;
|
||||
|
||||
[Flags]
|
||||
enum AccessType
|
||||
{
|
||||
Store = 0,
|
||||
Signed = 1,
|
||||
Load = 2,
|
||||
|
||||
LoadZx = Load,
|
||||
LoadSx = Load | Signed,
|
||||
}
|
||||
|
||||
public static void Ldm(ILEmitterCtx context)
|
||||
{
|
||||
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
|
||||
|
||||
EmitLoadFromRegister(context, op.Rn);
|
||||
|
||||
bool writesToPc = (op.RegisterMask & (1 << RegisterAlias.Aarch32Pc)) != 0;
|
||||
|
||||
bool writeBack = op.PostOffset != 0 && (op.Rn != RegisterAlias.Aarch32Pc || !writesToPc);
|
||||
|
||||
if (writeBack)
|
||||
{
|
||||
context.Emit(OpCodes.Dup);
|
||||
}
|
||||
|
||||
context.EmitLdc_I4(op.Offset);
|
||||
|
||||
context.Emit(OpCodes.Add);
|
||||
|
||||
context.EmitSttmp();
|
||||
|
||||
if (writeBack)
|
||||
{
|
||||
context.EmitLdc_I4(op.PostOffset);
|
||||
|
||||
context.Emit(OpCodes.Add);
|
||||
|
||||
EmitStoreToRegister(context, op.Rn);
|
||||
}
|
||||
|
||||
int mask = op.RegisterMask;
|
||||
int offset = 0;
|
||||
|
||||
for (int register = 0; mask != 0; mask >>= 1, register++)
|
||||
{
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
||||
context.EmitLdtmp();
|
||||
|
||||
context.EmitLdc_I4(offset);
|
||||
|
||||
context.Emit(OpCodes.Add);
|
||||
|
||||
EmitReadZxCall(context, WordSizeLog2);
|
||||
|
||||
EmitStoreToRegister(context, register);
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Ldr(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, WordSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrb(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrd(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrh(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx);
|
||||
}
|
||||
|
||||
public static void Ldrsb(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadSx);
|
||||
}
|
||||
|
||||
public static void Ldrsh(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadSx);
|
||||
}
|
||||
|
||||
public static void Stm(ILEmitterCtx context)
|
||||
{
|
||||
OpCode32MemMult op = (OpCode32MemMult)context.CurrOp;
|
||||
|
||||
EmitLoadFromRegister(context, op.Rn);
|
||||
|
||||
context.EmitLdc_I4(op.Offset);
|
||||
|
||||
context.Emit(OpCodes.Add);
|
||||
|
||||
context.EmitSttmp();
|
||||
|
||||
int mask = op.RegisterMask;
|
||||
int offset = 0;
|
||||
|
||||
for (int register = 0; mask != 0; mask >>= 1, register++)
|
||||
{
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
||||
context.EmitLdtmp();
|
||||
|
||||
context.EmitLdc_I4(offset);
|
||||
|
||||
context.Emit(OpCodes.Add);
|
||||
|
||||
EmitLoadFromRegister(context, register);
|
||||
|
||||
EmitWriteCall(context, WordSizeLog2);
|
||||
|
||||
//Note: If Rn is also specified on the register list,
|
||||
//and Rn is the first register on this list, then the
|
||||
//value that is written to memory is the unmodified value,
|
||||
//before the write back. If it is on the list, but it's
|
||||
//not the first one, then the value written to memory
|
||||
//varies between CPUs.
|
||||
if (offset == 0 && op.PostOffset != 0)
|
||||
{
|
||||
//Emit write back after the first write.
|
||||
EmitLoadFromRegister(context, op.Rn);
|
||||
|
||||
context.EmitLdc_I4(op.PostOffset);
|
||||
|
||||
context.Emit(OpCodes.Add);
|
||||
|
||||
EmitStoreToRegister(context, op.Rn);
|
||||
}
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Str(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, WordSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
public static void Strb(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, ByteSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
public static void Strd(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, DWordSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
public static void Strh(ILEmitterCtx context)
|
||||
{
|
||||
EmitLoadOrStore(context, HWordSizeLog2, AccessType.Store);
|
||||
}
|
||||
|
||||
private static void EmitLoadOrStore(ILEmitterCtx context, int size, AccessType accType)
|
||||
{
|
||||
OpCode32Mem op = (OpCode32Mem)context.CurrOp;
|
||||
|
||||
if (op.Index || op.WBack)
|
||||
{
|
||||
EmitLoadFromRegister(context, op.Rn);
|
||||
|
||||
context.EmitLdc_I4(op.Imm);
|
||||
|
||||
context.Emit(op.Add ? OpCodes.Add : OpCodes.Sub);
|
||||
|
||||
context.EmitSttmp();
|
||||
}
|
||||
|
||||
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
|
||||
|
||||
if (op.Index)
|
||||
{
|
||||
context.EmitLdtmp();
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLoadFromRegister(context, op.Rn);
|
||||
}
|
||||
|
||||
if ((accType & AccessType.Load) != 0)
|
||||
{
|
||||
if ((accType & AccessType.Signed) != 0)
|
||||
{
|
||||
EmitReadSx32Call(context, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitReadZxCall(context, size);
|
||||
}
|
||||
|
||||
if (op.WBack)
|
||||
{
|
||||
context.EmitLdtmp();
|
||||
|
||||
EmitStoreToRegister(context, op.Rn);
|
||||
}
|
||||
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
context.Emit(OpCodes.Dup);
|
||||
|
||||
context.EmitLdflg((int)PState.EBit);
|
||||
|
||||
ILLabel lblBigEndian = new ILLabel();
|
||||
ILLabel lblEnd = new ILLabel();
|
||||
|
||||
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
|
||||
|
||||
//Little endian mode.
|
||||
context.Emit(OpCodes.Conv_U4);
|
||||
|
||||
EmitStoreToRegister(context, op.Rt);
|
||||
|
||||
context.EmitLsr(32);
|
||||
|
||||
context.Emit(OpCodes.Conv_U4);
|
||||
|
||||
EmitStoreToRegister(context, op.Rt | 1);
|
||||
|
||||
context.Emit(OpCodes.Br_S, lblEnd);
|
||||
|
||||
//Big endian mode.
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
context.EmitLsr(32);
|
||||
|
||||
context.Emit(OpCodes.Conv_U4);
|
||||
|
||||
EmitStoreToRegister(context, op.Rt);
|
||||
|
||||
context.Emit(OpCodes.Conv_U4);
|
||||
|
||||
EmitStoreToRegister(context, op.Rt | 1);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitStoreToRegister(context, op.Rt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op.WBack)
|
||||
{
|
||||
context.EmitLdtmp();
|
||||
|
||||
EmitStoreToRegister(context, op.Rn);
|
||||
}
|
||||
|
||||
EmitLoadFromRegister(context, op.Rt);
|
||||
|
||||
if (size == DWordSizeLog2)
|
||||
{
|
||||
context.Emit(OpCodes.Conv_U8);
|
||||
|
||||
context.EmitLdflg((int)PState.EBit);
|
||||
|
||||
ILLabel lblBigEndian = new ILLabel();
|
||||
ILLabel lblEnd = new ILLabel();
|
||||
|
||||
context.Emit(OpCodes.Brtrue_S, lblBigEndian);
|
||||
|
||||
//Little endian mode.
|
||||
EmitLoadFromRegister(context, op.Rt | 1);
|
||||
|
||||
context.Emit(OpCodes.Conv_U8);
|
||||
|
||||
context.EmitLsl(32);
|
||||
|
||||
context.Emit(OpCodes.Or);
|
||||
|
||||
context.Emit(OpCodes.Br_S, lblEnd);
|
||||
|
||||
//Big endian mode.
|
||||
context.MarkLabel(lblBigEndian);
|
||||
|
||||
context.EmitLsl(32);
|
||||
|
||||
EmitLoadFromRegister(context, op.Rt | 1);
|
||||
|
||||
context.Emit(OpCodes.Conv_U8);
|
||||
|
||||
context.Emit(OpCodes.Or);
|
||||
|
||||
context.MarkLabel(lblEnd);
|
||||
}
|
||||
|
||||
EmitWriteCall(context, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue