diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2019-01-29 13:06:11 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-01-29 13:06:11 -0300 |
| commit | c1bdf19061ec679aa3c69eda2a41337e3e809014 (patch) | |
| tree | f3813b8df8ff8dd1fbf73fd085893b0df21850dc /ChocolArm64/Instructions | |
| parent | 8f7fcede7fa98c605925dc7b9316940960543bf1 (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.cs | 46 | ||||
| -rw-r--r-- | ChocolArm64/Instructions/InstEmitAlu32.cs | 26 | ||||
| -rw-r--r-- | ChocolArm64/Instructions/InstEmitAluHelper.cs | 12 | ||||
| -rw-r--r-- | ChocolArm64/Instructions/InstEmitFlow32.cs | 23 | ||||
| -rw-r--r-- | ChocolArm64/Instructions/InstEmitMemory32.cs | 325 |
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 |
