Add ARM32 support on the translator (#561)

* Remove ARM32 interpreter and add ARM32 support on the translator

* Nits.

* Rename Cond -> Condition

* Align code again

* Rename Data to Alu

* Enable ARM32 support and handle undefined instructions

* Use the IsThumb method to check if its a thumb opcode

* Remove another 32-bits check
This commit is contained in:
gdkchan 2019-01-24 23:59:53 -02:00 committed by GitHub
parent 72157e03eb
commit 36b9ab0e48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 1274 additions and 495 deletions

View file

@ -0,0 +1,59 @@
namespace ChocolArm64.Decoders
{
static class BitUtils
{
public static int HighestBitSet32(int value)
{
for (int bit = 31; bit >= 0; bit--)
{
if (((value >> bit) & 1) != 0)
{
return bit;
}
}
return -1;
}
private static readonly sbyte[] HbsNibbleTbl = { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
public static int HighestBitSetNibble(int value) => HbsNibbleTbl[value & 0b1111];
public static long Replicate(long bits, int size)
{
long output = 0;
for (int bit = 0; bit < 64; bit += size)
{
output |= bits << bit;
}
return output;
}
public static long FillWithOnes(int bits)
{
return bits == 64 ? -1L : (1L << bits) - 1;
}
public static int RotateRight(int bits, int shift, int size)
{
return (int)RotateRight((uint)bits, shift, size);
}
public static uint RotateRight(uint bits, int shift, int size)
{
return (bits >> shift) | (bits << (size - shift));
}
public static long RotateRight(long bits, int shift, int size)
{
return (long)RotateRight((ulong)bits, shift, size);
}
public static ulong RotateRight(ulong bits, int shift, int size)
{
return (bits >> shift) | (bits << (size - shift));
}
}
}

View file

@ -1,6 +1,6 @@
namespace ChocolArm64.Decoders
{
enum Cond
enum Condition
{
Eq = 0,
Ne = 1,

View file

@ -19,20 +19,20 @@ namespace ChocolArm64.Decoders
_opActivators = new ConcurrentDictionary<Type, OpActivator>();
}
public static Block DecodeBasicBlock(CpuThreadState state, MemoryManager memory, long start)
public static Block DecodeBasicBlock(MemoryManager memory, long start, ExecutionMode mode)
{
Block block = new Block(start);
FillBlock(state, memory, block);
FillBlock(memory, mode, block);
return block;
}
public static Block DecodeSubroutine(
TranslatorCache cache,
CpuThreadState state,
MemoryManager memory,
long start)
long start,
ExecutionMode mode)
{
Dictionary<long, Block> visited = new Dictionary<long, Block>();
Dictionary<long, Block> visitedEnd = new Dictionary<long, Block>();
@ -59,7 +59,7 @@ namespace ChocolArm64.Decoders
{
Block current = blocks.Dequeue();
FillBlock(state, memory, current);
FillBlock(memory, mode, current);
//Set child blocks. "Branch" is the block the branch instruction
//points to (when taken), "Next" is the block at the next address,
@ -71,7 +71,7 @@ namespace ChocolArm64.Decoders
OpCode64 lastOp = current.GetLastOp();
if (lastOp is OpCodeBImm64 op)
if (lastOp is IOpCodeBImm op)
{
if (op.Emitter == InstEmit.Bl)
{
@ -83,8 +83,7 @@ namespace ChocolArm64.Decoders
}
}
if (!((lastOp is OpCodeBImmAl64) ||
(lastOp is OpCodeBReg64)) || hasCachedSub)
if (!IsUnconditionalBranch(lastOp) || hasCachedSub)
{
current.Next = Enqueue(current.EndPosition);
}
@ -121,7 +120,7 @@ namespace ChocolArm64.Decoders
return entry;
}
private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block)
private static void FillBlock(MemoryManager memory, ExecutionMode mode, Block block)
{
long position = block.Position;
@ -129,13 +128,11 @@ namespace ChocolArm64.Decoders
do
{
//TODO: This needs to be changed to support both AArch32 and AArch64,
//once JIT support is introduced on AArch32 aswell.
opCode = DecodeOpCode(state, memory, position);
opCode = DecodeOpCode(memory, position, mode);
block.OpCodes.Add(opCode);
position += 4;
position += opCode.OpCodeSizeInBytes;
}
while (!(IsBranch(opCode) || IsException(opCode)));
@ -145,7 +142,35 @@ namespace ChocolArm64.Decoders
private static bool IsBranch(OpCode64 opCode)
{
return opCode is OpCodeBImm64 ||
opCode is OpCodeBReg64;
opCode is OpCodeBReg64 || IsAarch32Branch(opCode);
}
private static bool IsUnconditionalBranch(OpCode64 opCode)
{
return opCode is OpCodeBImmAl64 ||
opCode is OpCodeBReg64 || IsAarch32UnconditionalBranch(opCode);
}
private static bool IsAarch32UnconditionalBranch(OpCode64 opCode)
{
if (!(opCode is OpCode32 op))
{
return false;
}
//Note: On ARM32, most instructions have conditional execution,
//so there's no "Always" (unconditional) branch like on ARM64.
//We need to check if the condition is "Always" instead.
return IsAarch32Branch(op) && op.Cond >= Condition.Al;
}
private static bool IsAarch32Branch(OpCode64 opCode)
{
//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);
}
private static bool IsException(OpCode64 opCode)
@ -155,20 +180,26 @@ namespace ChocolArm64.Decoders
opCode.Emitter == InstEmit.Und;
}
public static OpCode64 DecodeOpCode(CpuThreadState state, MemoryManager memory, long position)
public static OpCode64 DecodeOpCode(MemoryManager memory, long position, ExecutionMode mode)
{
int opCode = memory.ReadInt32(position);
Inst inst;
if (state.ExecutionMode == ExecutionMode.AArch64)
if (mode == ExecutionMode.Aarch64)
{
inst = OpCodeTable.GetInstA64(opCode);
}
else
{
//TODO: Thumb support.
inst = OpCodeTable.GetInstA32(opCode);
if (mode == ExecutionMode.Aarch32Arm)
{
inst = OpCodeTable.GetInstA32(opCode);
}
else /* if (mode == ExecutionMode.Aarch32Thumb) */
{
inst = OpCodeTable.GetInstT32(opCode);
}
}
OpCode64 decodedOpCode = new OpCode64(Inst.Undefined, position, opCode);

View file

@ -89,6 +89,11 @@ namespace ChocolArm64.Decoders
return value;
}
public static long DecodeImm24_2(int opCode)
{
return ((long)opCode << 40) >> 38;
}
public static long DecodeImm26_2(int opCode)
{
return ((long)opCode << 38) >> 36;

View file

@ -0,0 +1,9 @@
namespace ChocolArm64.Decoders
{
interface IOpCode32 : IOpCode64
{
Condition Cond { get; }
uint GetPc();
}
}

View file

@ -0,0 +1,10 @@
namespace ChocolArm64.Decoders
{
interface IOpCodeAlu32 : IOpCode32
{
int Rd { get; }
int Rn { get; }
bool SetFlags { get; }
}
}

View file

@ -0,0 +1,7 @@
namespace ChocolArm64.Decoders
{
interface IOpCodeBImm : IOpCode64
{
long Imm { get; }
}
}

View file

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

View file

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

View file

@ -2,6 +2,6 @@ namespace ChocolArm64.Decoders
{
interface IOpCodeCond64 : IOpCode64
{
Cond Cond { get; }
Condition Cond { get; }
}
}

View file

@ -0,0 +1,24 @@
using ChocolArm64.Instructions;
using ChocolArm64.State;
namespace ChocolArm64.Decoders
{
class OpCode32 : OpCode64
{
public Condition Cond { get; protected set; }
public OpCode32(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
RegisterSize = RegisterSize.Int32;
Cond = (Condition)((uint)opCode >> 28);
}
public uint GetPc()
{
//Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
//the PC actually points 2 instructions ahead.
return (uint)Position + (uint)OpCodeSizeInBytes * 2;
}
}
}

View file

@ -9,9 +9,10 @@ namespace ChocolArm64.Decoders
public long Position { get; private set; }
public int RawOpCode { get; private set; }
public InstEmitter Emitter { get; protected set; }
public InstInterpreter Interpreter { get; protected set; }
public RegisterSize RegisterSize { get; protected set; }
public int OpCodeSizeInBytes { get; protected set; } = 4;
public InstEmitter Emitter { get; protected set; }
public RegisterSize RegisterSize { get; protected set; }
public OpCode64(Inst inst, long position, int opCode)
{
@ -20,8 +21,7 @@ namespace ChocolArm64.Decoders
RegisterSize = RegisterSize.Int64;
Emitter = inst.Emitter;
Interpreter = inst.Interpreter;
Emitter = inst.Emitter;
}
public int GetBitsCount()

View file

@ -0,0 +1,20 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeAlu32 : OpCode32, IOpCodeAlu32
{
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)
{
Rd = (opCode >> 12) & 0xf;
Rn = (opCode >> 16) & 0xf;
SetFlags = ((opCode >> 20) & 1) != 0;
}
}
}

View file

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

View file

@ -0,0 +1,22 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeAluImm8T16 : OpCodeT16, IOpCodeAlu32
{
private int _rdn;
public int Rd => _rdn;
public int Rn => _rdn;
public bool SetFlags => false;
public int Imm { get; private set; }
public OpCodeAluImm8T16(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Imm = (opCode >> 0) & 0xff;
_rdn = (opCode >> 8) & 0x7;
}
}
}

View file

@ -0,0 +1,20 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeAluRsImm32 : OpCodeAlu32
{
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)
{
Rm = (opCode >> 0) & 0xf;
Imm = (opCode >> 7) & 0x1f;
ShiftType = (ShiftType)((opCode >> 5) & 3);
}
}
}

View file

@ -0,0 +1,29 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeBImm32 : OpCode32, IOpCodeBImm32
{
public long Imm { get; private set; }
public OpCodeBImm32(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
uint pc = GetPc();
//When the codition is never, the instruction is BLX to Thumb mode.
if (Cond != Condition.Nv)
{
pc &= ~3u;
}
Imm = pc + DecoderHelper.DecodeImm24_2(opCode);
if (Cond == Condition.Nv)
{
long H = (opCode >> 23) & 2;
Imm |= H;
}
}
}
}

View file

@ -2,7 +2,7 @@ using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeBImm64 : OpCode64
class OpCodeBImm64 : OpCode64, IOpCodeBImm
{
public long Imm { get; protected set; }

View file

@ -4,7 +4,7 @@ namespace ChocolArm64.Decoders
{
class OpCodeBImmCond64 : OpCodeBImm64, IOpCodeCond64
{
public Cond Cond { get; private set; }
public Condition Cond { get; private set; }
public OpCodeBImmCond64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
@ -17,7 +17,7 @@ namespace ChocolArm64.Decoders
return;
}
Cond = (Cond)(opCode & 0xf);
Cond = (Condition)(opCode & 0xf);
Imm = position + DecoderHelper.DecodeImmS19_2(opCode);
}

View file

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

View file

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

View file

@ -8,7 +8,7 @@ namespace ChocolArm64.Decoders
public int Nzcv { get; private set; }
protected int RmImm;
public Cond Cond { get; private set; }
public Condition Cond { get; private set; }
public OpCodeCcmp64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
@ -21,11 +21,11 @@ namespace ChocolArm64.Decoders
return;
}
Nzcv = (opCode >> 0) & 0xf;
Cond = (Cond)((opCode >> 12) & 0xf);
RmImm = (opCode >> 16) & 0x1f;
Nzcv = (opCode >> 0) & 0xf;
Cond = (Condition)((opCode >> 12) & 0xf);
RmImm = (opCode >> 16) & 0x1f;
Rd = CpuThreadState.ZrIndex;
Rd = RegisterAlias.Zr;
}
}
}

View file

@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders
{
public int Rm { get; private set; }
public Cond Cond { get; private set; }
public Condition Cond { get; private set; }
public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Rm = (opCode >> 16) & 0x1f;
Cond = (Cond)((opCode >> 12) & 0xf);
Rm = (opCode >> 16) & 0x1f;
Cond = (Condition)((opCode >> 12) & 0xf);
}
}
}

View file

@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders
{
public int Nzcv { get; private set; }
public Cond Cond { get; private set; }
public Condition Cond { get; private set; }
public OpCodeSimdFcond64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Nzcv = (opCode >> 0) & 0xf;
Cond = (Cond)((opCode >> 12) & 0xf);
Nzcv = (opCode >> 0) & 0xf;
Cond = (Condition)((opCode >> 12) & 0xf);
}
}
}

View file

@ -0,0 +1,14 @@
using ChocolArm64.Instructions;
namespace ChocolArm64.Decoders
{
class OpCodeT16 : OpCode32
{
public OpCodeT16(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
Cond = Condition.Al;
OpCodeSizeInBytes = 2;
}
}
}