aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Instructions
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2019-01-29 13:06:11 -0300
committerGitHub <noreply@github.com>2019-01-29 13:06:11 -0300
commitc1bdf19061ec679aa3c69eda2a41337e3e809014 (patch)
treef3813b8df8ff8dd1fbf73fd085893b0df21850dc /ChocolArm64/Instructions
parent8f7fcede7fa98c605925dc7b9316940960543bf1 (diff)
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
Diffstat (limited to 'ChocolArm64/Instructions')
-rw-r--r--ChocolArm64/Instructions/InstEmit32Helper.cs46
-rw-r--r--ChocolArm64/Instructions/InstEmitAlu32.cs26
-rw-r--r--ChocolArm64/Instructions/InstEmitAluHelper.cs12
-rw-r--r--ChocolArm64/Instructions/InstEmitFlow32.cs23
-rw-r--r--ChocolArm64/Instructions/InstEmitMemory32.cs325
5 files changed, 402 insertions, 30 deletions
diff --git a/ChocolArm64/Instructions/InstEmit32Helper.cs b/ChocolArm64/Instructions/InstEmit32Helper.cs
index d3ff8138..792e96f5 100644
--- a/ChocolArm64/Instructions/InstEmit32Helper.cs
+++ b/ChocolArm64/Instructions/InstEmit32Helper.cs
@@ -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
diff --git a/ChocolArm64/Instructions/InstEmitAlu32.cs b/ChocolArm64/Instructions/InstEmitAlu32.cs
index 2ebb8073..539e7c43 100644
--- a/ChocolArm64/Instructions/InstEmitAlu32.cs
+++ b/ChocolArm64/Instructions/InstEmitAlu32.cs
@@ -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);
diff --git a/ChocolArm64/Instructions/InstEmitAluHelper.cs b/ChocolArm64/Instructions/InstEmitAluHelper.cs
index db8fd0e5..181f645a 100644
--- a/ChocolArm64/Instructions/InstEmitAluHelper.cs
+++ b/ChocolArm64/Instructions/InstEmitAluHelper.cs
@@ -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;
diff --git a/ChocolArm64/Instructions/InstEmitFlow32.cs b/ChocolArm64/Instructions/InstEmitFlow32.cs
index 03b39936..61f1d34c 100644
--- a/ChocolArm64/Instructions/InstEmitFlow32.cs
+++ b/ChocolArm64/Instructions/InstEmitFlow32.cs
@@ -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);
- }
}
} \ No newline at end of file
diff --git a/ChocolArm64/Instructions/InstEmitMemory32.cs b/ChocolArm64/Instructions/InstEmitMemory32.cs
new file mode 100644
index 00000000..4d6a57a4
--- /dev/null
+++ b/ChocolArm64/Instructions/InstEmitMemory32.cs
@@ -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);
+ }
+ }
+ }
+} \ No newline at end of file