Start of the ARMeilleure project
This commit is contained in:
parent
54b79dffa8
commit
c25bc79316
185 changed files with 15770 additions and 1352 deletions
19
ARMeilleure/ARMeilleure.csproj
Normal file
19
ARMeilleure/ARMeilleure.csproj
Normal file
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
258
ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs
Normal file
258
ARMeilleure/CodeGen/Optimizations/ConstantFolding.cs
Normal file
|
@ -0,0 +1,258 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Optimizations
|
||||
{
|
||||
static class ConstantFolding
|
||||
{
|
||||
public static void Fold(Operation operation)
|
||||
{
|
||||
if (operation.Dest == null || operation.SourcesCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AreAllSourcesConstant(operation))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OperandType type = operation.Dest.Type;
|
||||
|
||||
switch (operation.Inst)
|
||||
{
|
||||
case Instruction.Add:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x + y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x + y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseAnd:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x & y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x & y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x ^ y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x ^ y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseNot:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => ~x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => ~x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseOr:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x | y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x | y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Copy:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Divide:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => y != 0 ? x / y : 0);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => y != 0 ? x / y : 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.DivideUI:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => y != 0 ? (int)((uint)x / (uint)y) : 0);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => y != 0 ? (long)((ulong)x / (ulong)y) : 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Multiply:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x * y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x * y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Negate:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => -x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => -x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ShiftLeft:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x << y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x << (int)y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ShiftRightSI:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x >> y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x >> (int)y);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.ShiftRightUI:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => (int)((uint)x >> y));
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => (long)((ulong)x >> (int)y));
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.SignExtend16:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => (short)x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => (short)x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.SignExtend32:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => (long)x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.SignExtend8:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateUnaryI32(operation, (x) => (sbyte)x);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateUnaryI64(operation, (x) => (sbyte)x);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.Subtract:
|
||||
if (type == OperandType.I32)
|
||||
{
|
||||
EvaluateBinaryI32(operation, (x, y) => x - y);
|
||||
}
|
||||
else if (type == OperandType.I64)
|
||||
{
|
||||
EvaluateBinaryI64(operation, (x, y) => x - y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool AreAllSourcesConstant(Operation operation)
|
||||
{
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
if (operation.GetSource(index).Kind != OperandKind.Constant)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void EvaluateUnaryI32(Operation operation, Func<int, int> op)
|
||||
{
|
||||
int x = operation.GetSource(0).AsInt32();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x)));
|
||||
}
|
||||
|
||||
private static void EvaluateUnaryI64(Operation operation, Func<long, long> op)
|
||||
{
|
||||
long x = operation.GetSource(0).AsInt64();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x)));
|
||||
}
|
||||
|
||||
private static void EvaluateBinaryI32(Operation operation, Func<int, int, int> op)
|
||||
{
|
||||
int x = operation.GetSource(0).AsInt32();
|
||||
int y = operation.GetSource(1).AsInt32();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x, y)));
|
||||
}
|
||||
|
||||
private static void EvaluateBinaryI64(Operation operation, Func<long, long, long> op)
|
||||
{
|
||||
long x = operation.GetSource(0).AsInt64();
|
||||
long y = operation.GetSource(1).AsInt64();
|
||||
|
||||
operation.TurnIntoCopy(Const(op(x, y)));
|
||||
}
|
||||
}
|
||||
}
|
148
ARMeilleure/CodeGen/Optimizations/Optimizer.cs
Normal file
148
ARMeilleure/CodeGen/Optimizations/Optimizer.cs
Normal file
|
@ -0,0 +1,148 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Optimizations
|
||||
{
|
||||
static class Optimizer
|
||||
{
|
||||
public static void Optimize(ControlFlowGraph cfg)
|
||||
{
|
||||
bool modified;
|
||||
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
foreach (BasicBlock block in cfg.Blocks)
|
||||
{
|
||||
LinkedListNode<Node> node = block.Operations.First;
|
||||
|
||||
while (node != null)
|
||||
{
|
||||
LinkedListNode<Node> nextNode = node.Next;
|
||||
|
||||
bool isUnused = IsUnused(node.Value);
|
||||
|
||||
if (!(node.Value is Operation operation) || (isUnused && !IsMemoryStore(operation.Inst)))
|
||||
{
|
||||
if (isUnused)
|
||||
{
|
||||
RemoveNode(block, node);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
node = nextNode;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ConstantFolding.Fold(operation);
|
||||
|
||||
Simplification.Simplify(operation);
|
||||
|
||||
if (DestIsLocalVar(operation) && IsPropagableCopy(operation))
|
||||
{
|
||||
PropagateCopy(operation);
|
||||
|
||||
RemoveNode(block, node);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
node = nextNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (modified);
|
||||
}
|
||||
|
||||
private static void PropagateCopy(Operation copyOp)
|
||||
{
|
||||
//Propagate copy source operand to all uses of
|
||||
//the destination operand.
|
||||
Operand dest = copyOp.Dest;
|
||||
Operand source = copyOp.GetSource(0);
|
||||
|
||||
Node[] uses = dest.Uses.ToArray();
|
||||
|
||||
foreach (Node use in uses)
|
||||
{
|
||||
for (int index = 0; index < use.SourcesCount; index++)
|
||||
{
|
||||
if (use.GetSource(index) == dest)
|
||||
{
|
||||
use.SetSource(index, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveNode(BasicBlock block, LinkedListNode<Node> llNode)
|
||||
{
|
||||
//Remove a node from the nodes list, and also remove itself
|
||||
//from all the use lists on the operands that this node uses.
|
||||
block.Operations.Remove(llNode);
|
||||
|
||||
Queue<Node> nodes = new Queue<Node>();
|
||||
|
||||
nodes.Enqueue(llNode.Value);
|
||||
|
||||
while (nodes.TryDequeue(out Node node))
|
||||
{
|
||||
for (int index = 0; index < node.SourcesCount; index++)
|
||||
{
|
||||
Operand src = node.GetSource(index);
|
||||
|
||||
if (src.Kind != OperandKind.LocalVariable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (src.Uses.Remove(node) && src.Uses.Count == 0)
|
||||
{
|
||||
foreach (Node assignment in src.Assignments)
|
||||
{
|
||||
nodes.Enqueue(assignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsUnused(Node node)
|
||||
{
|
||||
return DestIsLocalVar(node) && node.Dest.Uses.Count == 0;
|
||||
}
|
||||
|
||||
private static bool DestIsLocalVar(Node node)
|
||||
{
|
||||
return node.Dest != null && node.Dest.Kind == OperandKind.LocalVariable;
|
||||
}
|
||||
|
||||
private static bool IsPropagableCopy(Operation operation)
|
||||
{
|
||||
if (operation.Inst != Instruction.Copy)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return operation.Dest.Type == operation.GetSource(0).Type;
|
||||
}
|
||||
|
||||
private static bool IsMemoryStore(Instruction inst)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.Store:
|
||||
case Instruction.Store16:
|
||||
case Instruction.Store8:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
157
ARMeilleure/CodeGen/Optimizations/Simplification.cs
Normal file
157
ARMeilleure/CodeGen/Optimizations/Simplification.cs
Normal file
|
@ -0,0 +1,157 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Optimizations
|
||||
{
|
||||
static class Simplification
|
||||
{
|
||||
public static void Simplify(Operation operation)
|
||||
{
|
||||
switch (operation.Inst)
|
||||
{
|
||||
case Instruction.Add:
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
TryEliminateBinaryOpComutative(operation, 0);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseAnd:
|
||||
TryEliminateBitwiseAnd(operation);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseOr:
|
||||
TryEliminateBitwiseOr(operation);
|
||||
break;
|
||||
|
||||
case Instruction.ConditionalSelect:
|
||||
TryEliminateConditionalSelect(operation);
|
||||
break;
|
||||
|
||||
case Instruction.Divide:
|
||||
TryEliminateBinaryOpY(operation, 1);
|
||||
break;
|
||||
|
||||
case Instruction.Multiply:
|
||||
TryEliminateBinaryOpComutative(operation, 1);
|
||||
break;
|
||||
|
||||
case Instruction.ShiftLeft:
|
||||
case Instruction.ShiftRightSI:
|
||||
case Instruction.ShiftRightUI:
|
||||
case Instruction.Subtract:
|
||||
TryEliminateBinaryOpY(operation, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBitwiseAnd(Operation operation)
|
||||
{
|
||||
//Try to recognize and optimize those 3 patterns (in order):
|
||||
//x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
|
||||
//x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(x, AllOnes(x.Type)))
|
||||
{
|
||||
operation.TurnIntoCopy(y);
|
||||
}
|
||||
else if (IsConstEqual(y, AllOnes(y.Type)))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
else if (IsConstEqual(x, 0) || IsConstEqual(y, 0))
|
||||
{
|
||||
operation.TurnIntoCopy(Const(0));
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBitwiseOr(Operation operation)
|
||||
{
|
||||
//Try to recognize and optimize those 3 patterns (in order):
|
||||
//x | 0x00000000 == x, 0x00000000 | y == y,
|
||||
//x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(x, 0))
|
||||
{
|
||||
operation.TurnIntoCopy(y);
|
||||
}
|
||||
else if (IsConstEqual(y, 0))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
else if (IsConstEqual(x, AllOnes(x.Type)) || IsConstEqual(y, AllOnes(y.Type)))
|
||||
{
|
||||
operation.TurnIntoCopy(Const(AllOnes(x.Type)));
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBinaryOpY(Operation operation, ulong comparand)
|
||||
{
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(y, comparand))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateBinaryOpComutative(Operation operation, ulong comparand)
|
||||
{
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
if (IsConstEqual(x, comparand))
|
||||
{
|
||||
operation.TurnIntoCopy(y);
|
||||
}
|
||||
else if (IsConstEqual(y, comparand))
|
||||
{
|
||||
operation.TurnIntoCopy(x);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryEliminateConditionalSelect(Operation operation)
|
||||
{
|
||||
Operand cond = operation.GetSource(0);
|
||||
|
||||
if (cond.Kind != OperandKind.Constant)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//The condition is constant, we can turn it into a copy, and select
|
||||
//the source based on the condition value.
|
||||
int srcIndex = cond.Value != 0 ? 1 : 2;
|
||||
|
||||
Operand source = operation.GetSource(srcIndex);
|
||||
|
||||
operation.TurnIntoCopy(source);
|
||||
}
|
||||
|
||||
private static bool IsConstEqual(Operand operand, ulong comparand)
|
||||
{
|
||||
if (operand.Kind != OperandKind.Constant || !operand.Type.IsInteger())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return operand.Value == comparand;
|
||||
}
|
||||
|
||||
private static ulong AllOnes(OperandType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OperandType.I32: return ~0U;
|
||||
case OperandType.I64: return ~0UL;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Invalid operand type \"" + type + "\".");
|
||||
}
|
||||
}
|
||||
}
|
248
ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs
Normal file
248
ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs
Normal file
|
@ -0,0 +1,248 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
class CopyResolver
|
||||
{
|
||||
private class ParallelCopy
|
||||
{
|
||||
private struct Copy
|
||||
{
|
||||
public Register Dest { get; }
|
||||
public Register Source { get; }
|
||||
|
||||
public OperandType Type { get; }
|
||||
|
||||
public Copy(Register dest, Register source, OperandType type)
|
||||
{
|
||||
Dest = dest;
|
||||
Source = source;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Copy> _copies;
|
||||
|
||||
public int Count => _copies.Count;
|
||||
|
||||
public ParallelCopy()
|
||||
{
|
||||
_copies = new List<Copy>();
|
||||
}
|
||||
|
||||
public void AddCopy(Register dest, Register source, OperandType type)
|
||||
{
|
||||
_copies.Add(new Copy(dest, source, type));
|
||||
}
|
||||
|
||||
public void Sequence(List<Operation> sequence)
|
||||
{
|
||||
Dictionary<Register, Register> locations = new Dictionary<Register, Register>();
|
||||
Dictionary<Register, Register> sources = new Dictionary<Register, Register>();
|
||||
|
||||
Dictionary<Register, OperandType> types = new Dictionary<Register, OperandType>();
|
||||
|
||||
Queue<Register> pendingQueue = new Queue<Register>();
|
||||
|
||||
Queue<Register> readyQueue = new Queue<Register>();
|
||||
|
||||
foreach (Copy copy in _copies)
|
||||
{
|
||||
locations[copy.Source] = copy.Source;
|
||||
|
||||
sources[copy.Dest] = copy.Source;
|
||||
|
||||
types[copy.Dest] = copy.Type;
|
||||
|
||||
pendingQueue.Enqueue(copy.Dest);
|
||||
}
|
||||
|
||||
foreach (Copy copy in _copies)
|
||||
{
|
||||
//If the destination is not used anywhere, we can assign it immediately.
|
||||
if (!locations.ContainsKey(copy.Dest))
|
||||
{
|
||||
readyQueue.Enqueue(copy.Dest);
|
||||
}
|
||||
}
|
||||
|
||||
while (pendingQueue.TryDequeue(out Register current))
|
||||
{
|
||||
Register copyDest;
|
||||
Register origSource;
|
||||
Register copySource;
|
||||
|
||||
while (readyQueue.TryDequeue(out copyDest))
|
||||
{
|
||||
origSource = sources[copyDest];
|
||||
copySource = locations[origSource];
|
||||
|
||||
OperandType type = types[copyDest];
|
||||
|
||||
EmitCopy(sequence, GetRegister(copyDest, type), GetRegister(copySource, type));
|
||||
|
||||
locations[origSource] = copyDest;
|
||||
|
||||
if (origSource == copySource && sources.ContainsKey(origSource))
|
||||
{
|
||||
readyQueue.Enqueue(origSource);
|
||||
}
|
||||
}
|
||||
|
||||
copyDest = current;
|
||||
origSource = sources[copyDest];
|
||||
copySource = locations[origSource];
|
||||
|
||||
if (copyDest != copySource)
|
||||
{
|
||||
OperandType type = types[copyDest];
|
||||
|
||||
EmitXorSwap(sequence, GetRegister(copyDest, type), GetRegister(copySource, type));
|
||||
|
||||
locations[origSource] = copyDest;
|
||||
|
||||
Register swapOther = copySource;
|
||||
|
||||
if (copyDest != locations[sources[copySource]])
|
||||
{
|
||||
//Find the other swap destination register.
|
||||
//To do that, we search all the pending registers, and pick
|
||||
//the one where the copy source register is equal to the
|
||||
//current destination register being processed (copyDest).
|
||||
foreach (Register pending in pendingQueue)
|
||||
{
|
||||
//Is this a copy of pending <- copyDest?
|
||||
if (copyDest == locations[sources[pending]])
|
||||
{
|
||||
swapOther = pending;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//The value that was previously at "copyDest" now lives on
|
||||
//"copySource" thanks to the swap, now we need to update the
|
||||
//location for the next copy that is supposed to copy the value
|
||||
//that used to live on "copyDest".
|
||||
locations[sources[swapOther]] = copySource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitCopy(List<Operation> sequence, Operand x, Operand y)
|
||||
{
|
||||
sequence.Add(new Operation(Instruction.Copy, x, y));
|
||||
}
|
||||
|
||||
private static void EmitXorSwap(List<Operation> sequence, Operand x, Operand y)
|
||||
{
|
||||
sequence.Add(new Operation(Instruction.BitwiseExclusiveOr, x, x, y));
|
||||
sequence.Add(new Operation(Instruction.BitwiseExclusiveOr, y, y, x));
|
||||
sequence.Add(new Operation(Instruction.BitwiseExclusiveOr, x, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<Operation> _fillQueue = new Queue<Operation>();
|
||||
private Queue<Operation> _spillQueue = new Queue<Operation>();
|
||||
|
||||
private ParallelCopy _parallelCopy;
|
||||
|
||||
public bool HasCopy { get; private set; }
|
||||
|
||||
public CopyResolver()
|
||||
{
|
||||
_fillQueue = new Queue<Operation>();
|
||||
_spillQueue = new Queue<Operation>();
|
||||
|
||||
_parallelCopy = new ParallelCopy();
|
||||
}
|
||||
|
||||
public void AddSplit(LiveInterval left, LiveInterval right)
|
||||
{
|
||||
if (left.Local != right.Local)
|
||||
{
|
||||
throw new ArgumentException("Intervals of different variables are not allowed.");
|
||||
}
|
||||
|
||||
OperandType type = left.Local.Type;
|
||||
|
||||
if (left.IsSpilled && !right.IsSpilled)
|
||||
{
|
||||
//Move from the stack to a register.
|
||||
AddSplitFill(left, right, type);
|
||||
}
|
||||
else if (!left.IsSpilled && right.IsSpilled)
|
||||
{
|
||||
//Move from a register to the stack.
|
||||
AddSplitSpill(left, right, type);
|
||||
}
|
||||
else if (!left.IsSpilled && !right.IsSpilled && left.Register != right.Register)
|
||||
{
|
||||
//Move from one register to another.
|
||||
AddSplitCopy(left, right, type);
|
||||
}
|
||||
else if (left.SpillOffset != right.SpillOffset)
|
||||
{
|
||||
//This would be the stack-to-stack move case, but this is not supported.
|
||||
throw new ArgumentException("Both intervals were spilled.");
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSplitFill(LiveInterval left, LiveInterval right, OperandType type)
|
||||
{
|
||||
Operand register = GetRegister(right.Register, type);
|
||||
|
||||
Operand offset = new Operand(left.SpillOffset);
|
||||
|
||||
_fillQueue.Enqueue(new Operation(Instruction.Fill, register, offset));
|
||||
|
||||
HasCopy = true;
|
||||
}
|
||||
|
||||
private void AddSplitSpill(LiveInterval left, LiveInterval right, OperandType type)
|
||||
{
|
||||
Operand offset = new Operand(right.SpillOffset);
|
||||
|
||||
Operand register = GetRegister(left.Register, type);
|
||||
|
||||
_spillQueue.Enqueue(new Operation(Instruction.Spill, null, offset, register));
|
||||
|
||||
HasCopy = true;
|
||||
}
|
||||
|
||||
private void AddSplitCopy(LiveInterval left, LiveInterval right, OperandType type)
|
||||
{
|
||||
_parallelCopy.AddCopy(right.Register, left.Register, type);
|
||||
|
||||
HasCopy = true;
|
||||
}
|
||||
|
||||
public Operation[] Sequence()
|
||||
{
|
||||
List<Operation> sequence = new List<Operation>();
|
||||
|
||||
while (_spillQueue.TryDequeue(out Operation spillOp))
|
||||
{
|
||||
sequence.Add(spillOp);
|
||||
}
|
||||
|
||||
_parallelCopy.Sequence(sequence);
|
||||
|
||||
while (_fillQueue.TryDequeue(out Operation fillOp))
|
||||
{
|
||||
sequence.Add(fillOp);
|
||||
}
|
||||
|
||||
return sequence.ToArray();
|
||||
}
|
||||
|
||||
private static Operand GetRegister(Register reg, OperandType type)
|
||||
{
|
||||
return new Operand(reg.Index, reg.Type, type);
|
||||
}
|
||||
}
|
||||
}
|
1084
ARMeilleure/CodeGen/RegisterAllocators/LinearScan.cs
Normal file
1084
ARMeilleure/CodeGen/RegisterAllocators/LinearScan.cs
Normal file
File diff suppressed because it is too large
Load diff
457
ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs
Normal file
457
ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs
Normal file
|
@ -0,0 +1,457 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
class LiveInterval
|
||||
{
|
||||
private const int NotFound = -1;
|
||||
|
||||
private LiveInterval _parent;
|
||||
|
||||
private SortedSet<int> _usePositions;
|
||||
|
||||
public int UsesCount => _usePositions.Count;
|
||||
|
||||
private List<LiveRange> _ranges;
|
||||
private List<LiveInterval> _childs;
|
||||
|
||||
public bool IsSplit => _childs.Count != 0;
|
||||
|
||||
public Operand Local { get; }
|
||||
|
||||
public LiveInterval Representative { get; private set; }
|
||||
|
||||
private Register _register;
|
||||
|
||||
public bool HasRegister { get; private set; }
|
||||
|
||||
public Register Register
|
||||
{
|
||||
get
|
||||
{
|
||||
return _register;
|
||||
}
|
||||
set
|
||||
{
|
||||
_register = value;
|
||||
|
||||
HasRegister = true;
|
||||
}
|
||||
}
|
||||
|
||||
public int SpillOffset { get; set; }
|
||||
|
||||
public bool IsSpilled => SpillOffset != -1;
|
||||
|
||||
public bool IsFixed { get; }
|
||||
|
||||
public bool IsEmpty => _ranges.Count == 0;
|
||||
|
||||
public int Start => _ranges[0].Start;
|
||||
public int End => _ranges[_ranges.Count - 1].End;
|
||||
|
||||
public LiveInterval(Operand local = null, LiveInterval parent = null)
|
||||
{
|
||||
Local = local;
|
||||
_parent = parent ?? this;
|
||||
|
||||
_usePositions = new SortedSet<int>();
|
||||
|
||||
_ranges = new List<LiveRange>();
|
||||
_childs = new List<LiveInterval>();
|
||||
|
||||
Representative = this;
|
||||
|
||||
SpillOffset = -1;
|
||||
}
|
||||
|
||||
public LiveInterval(Register register) : this()
|
||||
{
|
||||
IsFixed = true;
|
||||
Register = register;
|
||||
}
|
||||
|
||||
public void SetStart(int position)
|
||||
{
|
||||
if (_ranges.Count != 0)
|
||||
{
|
||||
_ranges[0] = new LiveRange(position, _ranges[0].End);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ranges.Add(new LiveRange(position, position));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(int start, int end)
|
||||
{
|
||||
if (BinarySearch(new LiveRange(start, end), out int index))
|
||||
{
|
||||
//New range insersects with an existing range, we need to remove
|
||||
//all the intersecting ranges before adding the new one.
|
||||
//We also extend the new range as needed, based on the values of
|
||||
//the existing ranges being removed.
|
||||
int lIndex = index;
|
||||
int rIndex = index;
|
||||
|
||||
while (lIndex > 0 && _ranges[lIndex - 1].End >= start)
|
||||
{
|
||||
lIndex--;
|
||||
}
|
||||
|
||||
while (rIndex + 1 < _ranges.Count && _ranges[rIndex + 1].Start <= end)
|
||||
{
|
||||
rIndex++;
|
||||
}
|
||||
|
||||
if (start > _ranges[lIndex].Start)
|
||||
{
|
||||
start = _ranges[lIndex].Start;
|
||||
}
|
||||
|
||||
if (end < _ranges[rIndex].End)
|
||||
{
|
||||
end = _ranges[rIndex].End;
|
||||
}
|
||||
|
||||
_ranges.RemoveRange(lIndex, (rIndex - lIndex) + 1);
|
||||
|
||||
InsertRange(lIndex, start, end);
|
||||
}
|
||||
else if (index < _ranges.Count && _ranges[index].Start < start)
|
||||
{
|
||||
InsertRange(index + 1, start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertRange(index, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
private void InsertRange(int index, int start, int end)
|
||||
{
|
||||
//Here we insert a new range on the ranges list.
|
||||
//If possible, we extend an existing range rather than inserting a new one.
|
||||
//We can extend an existing range if any of the following conditions are true:
|
||||
//- The new range starts right after the end of the previous range on the list.
|
||||
//- The new range ends right before the start of the next range on the list.
|
||||
//If both cases are true, we can extend either one. We prefer to extend the
|
||||
//previous range, and then remove the next one, but theres no specific reason
|
||||
//for that, extending either one will do.
|
||||
int? extIndex = null;
|
||||
|
||||
if (index > 0 && _ranges[index - 1].End == start)
|
||||
{
|
||||
start = _ranges[index - 1].Start;
|
||||
|
||||
extIndex = index - 1;
|
||||
}
|
||||
|
||||
if (index < _ranges.Count && _ranges[index].Start == end)
|
||||
{
|
||||
end = _ranges[index].End;
|
||||
|
||||
if (extIndex.HasValue)
|
||||
{
|
||||
_ranges.RemoveAt(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
extIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
if (extIndex.HasValue)
|
||||
{
|
||||
_ranges[extIndex.Value] = new LiveRange(start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ranges.Insert(index, new LiveRange(start, end));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddUsePosition(int position)
|
||||
{
|
||||
_usePositions.Add(position);
|
||||
}
|
||||
|
||||
public bool Overlaps(int position)
|
||||
{
|
||||
if (BinarySearch(new LiveRange(position, position + 1), out _))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Overlaps(LiveInterval other)
|
||||
{
|
||||
foreach (LiveRange range in other._ranges)
|
||||
{
|
||||
if (BinarySearch(range, out _))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<LiveInterval> SplitChilds()
|
||||
{
|
||||
return _childs;
|
||||
}
|
||||
|
||||
public IEnumerable<int> UsePositions()
|
||||
{
|
||||
return _usePositions;
|
||||
}
|
||||
|
||||
public int FirstUse()
|
||||
{
|
||||
if (_usePositions.Count == 0)
|
||||
{
|
||||
return NotFound;
|
||||
}
|
||||
|
||||
return _usePositions.First();
|
||||
}
|
||||
|
||||
public int NextUseAfter(int position)
|
||||
{
|
||||
foreach (int usePosition in _usePositions)
|
||||
{
|
||||
if (usePosition >= position)
|
||||
{
|
||||
return usePosition;
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound;
|
||||
}
|
||||
|
||||
public int NextOverlap(LiveInterval other)
|
||||
{
|
||||
foreach (LiveRange range in other._ranges)
|
||||
{
|
||||
if (BinarySearch(range, out int overlapIndex))
|
||||
{
|
||||
LiveRange overlappingRange = _ranges[overlapIndex];
|
||||
|
||||
if (range.Start > overlappingRange.Start)
|
||||
{
|
||||
return Math.Min(range.End, overlappingRange.End);
|
||||
}
|
||||
else
|
||||
{
|
||||
return overlappingRange.Start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound;
|
||||
}
|
||||
|
||||
public void Join(LiveInterval other)
|
||||
{
|
||||
foreach (LiveRange range in _ranges)
|
||||
{
|
||||
other.AddRange(range.Start, range.End);
|
||||
}
|
||||
|
||||
Representative = other;
|
||||
|
||||
_ranges.Clear();
|
||||
}
|
||||
|
||||
public LiveInterval Split(int position)
|
||||
{
|
||||
LiveInterval right = new LiveInterval(Local, _parent);
|
||||
|
||||
int splitIndex = 0;
|
||||
|
||||
for (; splitIndex < _ranges.Count; splitIndex++)
|
||||
{
|
||||
LiveRange range = _ranges[splitIndex];
|
||||
|
||||
if (position > range.Start && position <= range.End)
|
||||
{
|
||||
right._ranges.Add(new LiveRange(position, range.End));
|
||||
|
||||
range = new LiveRange(range.Start, position);
|
||||
|
||||
_ranges[splitIndex++] = range;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (range.Start >= position)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (splitIndex < _ranges.Count)
|
||||
{
|
||||
int count = _ranges.Count - splitIndex;
|
||||
|
||||
right._ranges.AddRange(_ranges.GetRange(splitIndex, count));
|
||||
|
||||
_ranges.RemoveRange(splitIndex, count);
|
||||
}
|
||||
|
||||
foreach (int usePosition in _usePositions.Where(x => x >= position))
|
||||
{
|
||||
right._usePositions.Add(usePosition);
|
||||
}
|
||||
|
||||
_usePositions.RemoveWhere(x => x >= position);
|
||||
|
||||
Debug.Assert(_ranges.Count != 0, "Left interval is empty after split.");
|
||||
|
||||
Debug.Assert(right._ranges.Count != 0, "Right interval is empty after split.");
|
||||
|
||||
AddSplitChild(right);
|
||||
|
||||
return right;
|
||||
}
|
||||
|
||||
private bool BinarySearch(LiveRange comparand, out int index)
|
||||
{
|
||||
index = 0;
|
||||
|
||||
int left = 0;
|
||||
int right = _ranges.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int size = right - left;
|
||||
|
||||
int middle = left + (size >> 1);
|
||||
|
||||
LiveRange range = _ranges[middle];
|
||||
|
||||
index = middle;
|
||||
|
||||
if (range.Start < comparand.End && comparand.Start < range.End)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (comparand.Start < range.Start)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddSplitChild(LiveInterval child)
|
||||
{
|
||||
Debug.Assert(!child.IsEmpty, "Trying to insert a empty interval.");
|
||||
|
||||
child.InsertSorted(_parent._childs);
|
||||
}
|
||||
|
||||
public LiveInterval GetSplitChild(int position)
|
||||
{
|
||||
//Try to find the interval among the split intervals that
|
||||
//contains the given position. The end is technically exclusive,
|
||||
//so if we have a interval where position == start, and other
|
||||
//where position == end, we should prefer the former.
|
||||
//To achieve that, we can just check the split childs backwards,
|
||||
//as they are sorted by start/end position, and there are no overlaps.
|
||||
for (int index = _childs.Count - 1; index >= 0; index--)
|
||||
{
|
||||
LiveInterval splitChild = _childs[index];
|
||||
|
||||
if (position >= splitChild.Start && position <= splitChild.End)
|
||||
{
|
||||
return splitChild;
|
||||
}
|
||||
}
|
||||
|
||||
if (position >= Start && position <= End)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool TrySpillWithSiblingOffset()
|
||||
{
|
||||
foreach (LiveInterval splitChild in _parent._childs)
|
||||
{
|
||||
if (splitChild.IsSpilled)
|
||||
{
|
||||
SpillOffset = splitChild.SpillOffset;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void InsertSorted(List<LiveInterval> list)
|
||||
{
|
||||
int insertIndex = 0;
|
||||
|
||||
int left = 0;
|
||||
int right = list.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int size = right - left;
|
||||
|
||||
int middle = left + (size >> 1);
|
||||
|
||||
LiveInterval current = list[middle];
|
||||
|
||||
insertIndex = middle;
|
||||
|
||||
if (Start == current.Start)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Start < current.Start)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
//If we have multiple intervals with the same start position, then the new one should
|
||||
//always be inserted after all the existing interval with the same position, in order
|
||||
//to ensure they will be processed (it works like a queue in this case).
|
||||
while (insertIndex < list.Count && list[insertIndex].Start <= Start)
|
||||
{
|
||||
insertIndex++;
|
||||
}
|
||||
|
||||
list.Insert(insertIndex, this);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Join("; ", _ranges);
|
||||
}
|
||||
}
|
||||
}
|
19
ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs
Normal file
19
ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
struct LiveRange
|
||||
{
|
||||
public int Start { get; }
|
||||
public int End { get; }
|
||||
|
||||
public LiveRange(int start, int end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{Start}, {End}[";
|
||||
}
|
||||
}
|
||||
}
|
143
ARMeilleure/CodeGen/RegisterAllocators/PhiFunctions.cs
Normal file
143
ARMeilleure/CodeGen/RegisterAllocators/PhiFunctions.cs
Normal file
|
@ -0,0 +1,143 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
static class PhiFunctions
|
||||
{
|
||||
private struct PhiEntry
|
||||
{
|
||||
public BasicBlock Predecessor { get; }
|
||||
public Operand Source { get; }
|
||||
|
||||
public PhiEntry(BasicBlock predecessor, Operand source)
|
||||
{
|
||||
Predecessor = predecessor;
|
||||
Source = source;
|
||||
}
|
||||
}
|
||||
public static void Remove(ControlFlowGraph cfg)
|
||||
{
|
||||
List<int> defBlocks = new List<int>();
|
||||
|
||||
HashSet<Operand> visited = new HashSet<Operand>();
|
||||
|
||||
//Build a list with the index of the block where each variable
|
||||
//is defined, and additionally number all the variables.
|
||||
foreach (BasicBlock block in cfg.Blocks)
|
||||
{
|
||||
foreach (Node node in block.Operations)
|
||||
{
|
||||
if (node.Dest != null && node.Dest.Kind == OperandKind.LocalVariable && visited.Add(node.Dest))
|
||||
{
|
||||
node.Dest.NumberLocal(defBlocks.Count);
|
||||
|
||||
defBlocks.Add(block.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (BasicBlock block in cfg.Blocks)
|
||||
{
|
||||
LinkedListNode<Node> node = block.Operations.First;
|
||||
|
||||
while (node?.Value is PhiNode phi)
|
||||
{
|
||||
LinkedListNode<Node> nextNode = node.Next;
|
||||
|
||||
Operand local = Local(phi.Dest.Type);
|
||||
|
||||
PhiEntry[] phiSources = GetPhiSources(phi);
|
||||
|
||||
for (int srcIndex = 0; srcIndex < phiSources.Length; srcIndex++)
|
||||
{
|
||||
BasicBlock predecessor = phiSources[srcIndex].Predecessor;
|
||||
|
||||
Operand source = phiSources[srcIndex].Source;
|
||||
|
||||
predecessor.Append(new Operation(Instruction.Copy, local, source));
|
||||
}
|
||||
|
||||
for (int index = 0; index < phi.SourcesCount; index++)
|
||||
{
|
||||
phi.SetSource(index, null);
|
||||
}
|
||||
|
||||
Operation copyOp = new Operation(Instruction.Copy, phi.Dest, local);
|
||||
|
||||
block.Operations.AddBefore(node, copyOp);
|
||||
|
||||
phi.Dest = null;
|
||||
|
||||
block.Operations.Remove(node);
|
||||
|
||||
node = nextNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PhiEntry[] GetPhiSources(PhiNode phi)
|
||||
{
|
||||
Dictionary<Operand, HashSet<BasicBlock>> defBlocksPerSrc = new Dictionary<Operand, HashSet<BasicBlock>>();
|
||||
|
||||
List<PhiEntry> phiSources = new List<PhiEntry>();
|
||||
|
||||
for (int index = 0; index < phi.SourcesCount; index++)
|
||||
{
|
||||
Operand source = phi.GetSource(index);
|
||||
|
||||
BasicBlock predecessor = phi.GetBlock(index);
|
||||
|
||||
BasicBlock defBlock = FindDefBlock(source, predecessor);
|
||||
|
||||
if (defBlock != null)
|
||||
{
|
||||
if (!defBlocksPerSrc.TryGetValue(source, out HashSet<BasicBlock> defBlocks))
|
||||
{
|
||||
defBlocks = new HashSet<BasicBlock>();
|
||||
|
||||
defBlocksPerSrc.Add(source, defBlocks);
|
||||
}
|
||||
|
||||
if (!defBlocks.Add(defBlock))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
phiSources.Add(new PhiEntry(defBlock ?? predecessor, source));
|
||||
}
|
||||
|
||||
return phiSources.ToArray();
|
||||
}
|
||||
|
||||
private static BasicBlock FindDefBlock(Operand source, BasicBlock predecessor)
|
||||
{
|
||||
if (source.Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
foreach (Node node in predecessor.Operations)
|
||||
{
|
||||
if (node.Dest == source)
|
||||
{
|
||||
return predecessor;
|
||||
}
|
||||
}
|
||||
|
||||
if (predecessor == predecessor.ImmediateDominator)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
predecessor = predecessor.ImmediateDominator;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
12
ARMeilleure/CodeGen/RegisterAllocators/RAReport.cs
Normal file
12
ARMeilleure/CodeGen/RegisterAllocators/RAReport.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
struct RAReport
|
||||
{
|
||||
public int UsedRegisters;
|
||||
|
||||
public RAReport(int usedRegisters)
|
||||
{
|
||||
UsedRegisters = usedRegisters;
|
||||
}
|
||||
}
|
||||
}
|
14
ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs
Normal file
14
ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
struct RegisterMasks
|
||||
{
|
||||
public int IntAvailableRegisters { get; }
|
||||
public int IntCalleeSavedRegisters { get; }
|
||||
|
||||
public RegisterMasks(int intAvailableRegisters, int intCalleeSavedRegisters)
|
||||
{
|
||||
IntAvailableRegisters = intAvailableRegisters;
|
||||
IntCalleeSavedRegisters = intCalleeSavedRegisters;
|
||||
}
|
||||
}
|
||||
}
|
139
ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs
Normal file
139
ARMeilleure/CodeGen/RegisterAllocators/StackAllocator.cs
Normal file
|
@ -0,0 +1,139 @@
|
|||
using ARMeilleure.Common;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
class StackAllocator
|
||||
{
|
||||
private List<ulong> _masks;
|
||||
|
||||
public StackAllocator()
|
||||
{
|
||||
_masks = new List<ulong>();
|
||||
}
|
||||
|
||||
public int Allocate(OperandType type)
|
||||
{
|
||||
return Allocate(GetSizeInWords(type));
|
||||
}
|
||||
|
||||
public int Free(int offset, OperandType type)
|
||||
{
|
||||
return Allocate(GetSizeInWords(type));
|
||||
}
|
||||
|
||||
private int Allocate(int sizeInWords)
|
||||
{
|
||||
ulong requiredMask = (1UL << sizeInWords) - 1;
|
||||
|
||||
for (int index = 0; ; index++)
|
||||
{
|
||||
ulong free = GetFreeMask(index);
|
||||
|
||||
while ((int)free != 0)
|
||||
{
|
||||
int freeBit = BitUtils.LowestBitSet((int)free);
|
||||
|
||||
ulong useMask = requiredMask << freeBit;
|
||||
|
||||
if ((free & useMask) == useMask)
|
||||
{
|
||||
free &= ~useMask;
|
||||
|
||||
SetFreeMask(index, free);
|
||||
|
||||
return -((index * 32 + freeBit) * 4 + sizeInWords * 4);
|
||||
}
|
||||
|
||||
free &= ~useMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Free(int offset, int sizeInWords)
|
||||
{
|
||||
int index = offset / 32;
|
||||
|
||||
ulong requiredMask = (1UL << sizeInWords) - 1;
|
||||
|
||||
ulong freeMask = (requiredMask << (offset & 31)) - 1;
|
||||
|
||||
SetFreeMask(index, GetFreeMask(index) & ~freeMask);
|
||||
}
|
||||
|
||||
private ulong GetFreeMask(int index)
|
||||
{
|
||||
int hi = index >> 1;
|
||||
|
||||
EnsureSize(hi);
|
||||
|
||||
ulong mask;
|
||||
|
||||
if ((index & 1) != 0)
|
||||
{
|
||||
EnsureSize(hi + 1);
|
||||
|
||||
mask = _masks[hi + 0] >> 32;
|
||||
mask |= _masks[hi + 1] << 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureSize(hi);
|
||||
|
||||
mask = _masks[hi];
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
private void SetFreeMask(int index, ulong mask)
|
||||
{
|
||||
int hi = index >> 1;
|
||||
|
||||
if ((index & 1) != 0)
|
||||
{
|
||||
EnsureSize(hi + 1);
|
||||
|
||||
_masks[hi + 0] &= 0x00000000ffffffffUL;
|
||||
_masks[hi + 1] &= 0xffffffff00000000UL;
|
||||
|
||||
_masks[hi + 0] |= mask << 32;
|
||||
_masks[hi + 1] |= mask >> 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureSize(hi);
|
||||
|
||||
_masks[hi] = mask;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureSize(int size)
|
||||
{
|
||||
while (size >= _masks.Count)
|
||||
{
|
||||
_masks.Add(ulong.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetSizeInWords(OperandType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case OperandType.I32:
|
||||
case OperandType.FP32:
|
||||
return 1;
|
||||
|
||||
case OperandType.I64:
|
||||
case OperandType.FP64:
|
||||
return 2;
|
||||
|
||||
case OperandType.V128: return 4;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid operand type \"{type}\".");
|
||||
}
|
||||
}
|
||||
}
|
738
ARMeilleure/CodeGen/X86/Assembler.cs
Normal file
738
ARMeilleure/CodeGen/X86/Assembler.cs
Normal file
|
@ -0,0 +1,738 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
class Assembler
|
||||
{
|
||||
private const int BadOp = 0;
|
||||
|
||||
private const int OpModRMBits = 16;
|
||||
|
||||
private const int R16Prefix = 0x66;
|
||||
|
||||
private struct InstInfo
|
||||
{
|
||||
public int OpRMR { get; }
|
||||
public int OpRMImm8 { get; }
|
||||
public int OpRMImm32 { get; }
|
||||
public int OpRImm64 { get; }
|
||||
public int OpRRM { get; }
|
||||
public int Opers { get; }
|
||||
|
||||
public InstInfo(
|
||||
int opRMR,
|
||||
int opRMImm8,
|
||||
int opRMImm32,
|
||||
int opRImm64,
|
||||
int opRRM,
|
||||
int opers)
|
||||
{
|
||||
OpRMR = opRMR;
|
||||
OpRMImm8 = opRMImm8;
|
||||
OpRMImm32 = opRMImm32;
|
||||
OpRImm64 = opRImm64;
|
||||
OpRRM = opRRM;
|
||||
Opers = opers;
|
||||
}
|
||||
}
|
||||
|
||||
private static InstInfo[] _instTable;
|
||||
|
||||
private Stream _stream;
|
||||
|
||||
static Assembler()
|
||||
{
|
||||
_instTable = new InstInfo[(int)X86Instruction.Count];
|
||||
|
||||
// Name RM/R RM/I8 RM/I32 R/I64 R/RM Opers
|
||||
Add(X86Instruction.Add, new InstInfo(0x000001, 0x000083, 0x000081, BadOp, 0x000003, 2));
|
||||
Add(X86Instruction.And, new InstInfo(0x000021, 0x040083, 0x040081, BadOp, 0x000023, 2));
|
||||
Add(X86Instruction.Cmp, new InstInfo(0x000039, 0x070083, 0x070081, BadOp, 0x00003b, 2));
|
||||
Add(X86Instruction.Idiv, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x0700f7, 1));
|
||||
Add(X86Instruction.Imul, new InstInfo(BadOp, 0x00006b, 0x000069, BadOp, 0x000faf, 2));
|
||||
Add(X86Instruction.Mov, new InstInfo(0x000089, BadOp, 0x0000c7, 0x0000b8, 0x00008b, 2));
|
||||
Add(X86Instruction.Mov16, new InstInfo(0x000089, BadOp, 0x0000c7, BadOp, 0x00008b, 2));
|
||||
Add(X86Instruction.Mov8, new InstInfo(0x000088, 0x0000c6, BadOp, BadOp, 0x00008a, 2));
|
||||
Add(X86Instruction.Movsx16, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x000fbf, 2));
|
||||
Add(X86Instruction.Movsx32, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x000063, 2));
|
||||
Add(X86Instruction.Movsx8, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x000fbe, 2));
|
||||
Add(X86Instruction.Movzx16, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x000fb7, 2));
|
||||
Add(X86Instruction.Movzx8, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x000fb6, 2));
|
||||
Add(X86Instruction.Neg, new InstInfo(0x0300f7, BadOp, BadOp, BadOp, BadOp, 1));
|
||||
Add(X86Instruction.Not, new InstInfo(0x0200f7, BadOp, BadOp, BadOp, BadOp, 1));
|
||||
Add(X86Instruction.Or, new InstInfo(0x000009, 0x010083, 0x010081, BadOp, 0x00000b, 2));
|
||||
Add(X86Instruction.Pop, new InstInfo(0x00008f, BadOp, BadOp, BadOp, BadOp, 1));
|
||||
Add(X86Instruction.Push, new InstInfo(BadOp, 0x00006a, 0x000068, BadOp, 0x0600ff, 1));
|
||||
Add(X86Instruction.Ror, new InstInfo(0x0100d3, 0x0100c1, BadOp, BadOp, BadOp, 2));
|
||||
Add(X86Instruction.Sar, new InstInfo(0x0700d3, 0x0700c1, BadOp, BadOp, BadOp, 2));
|
||||
Add(X86Instruction.Shl, new InstInfo(0x0400d3, 0x0400c1, BadOp, BadOp, BadOp, 2));
|
||||
Add(X86Instruction.Shr, new InstInfo(0x0500d3, 0x0500c1, BadOp, BadOp, BadOp, 2));
|
||||
Add(X86Instruction.Sub, new InstInfo(0x000029, 0x050083, 0x050081, BadOp, 0x00002b, 2));
|
||||
Add(X86Instruction.Test, new InstInfo(0x000085, BadOp, 0x0000f7, BadOp, BadOp, 2));
|
||||
Add(X86Instruction.Xor, new InstInfo(0x000031, 0x060083, 0x060081, BadOp, 0x000033, 2));
|
||||
}
|
||||
|
||||
private static void Add(X86Instruction inst, InstInfo info)
|
||||
{
|
||||
_instTable[(int)inst] = info;
|
||||
}
|
||||
|
||||
public Assembler(Stream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
}
|
||||
|
||||
public void Add(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Add);
|
||||
}
|
||||
|
||||
public void And(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.And);
|
||||
}
|
||||
|
||||
public void Cdq()
|
||||
{
|
||||
WriteByte(0x99);
|
||||
}
|
||||
|
||||
public void Cmovcc(Operand dest, Operand source, X86Condition condition)
|
||||
{
|
||||
WriteRRMOpCode(dest, source, 0x0f40 | (int)condition);
|
||||
}
|
||||
|
||||
public void Cmp(Operand src1, Operand src2)
|
||||
{
|
||||
WriteInstruction(src1, src2, X86Instruction.Cmp);
|
||||
}
|
||||
|
||||
public void Cqo()
|
||||
{
|
||||
WriteByte(0x48);
|
||||
WriteByte(0x99);
|
||||
}
|
||||
|
||||
public void Idiv(Operand source)
|
||||
{
|
||||
WriteInstruction(null, source, X86Instruction.Idiv);
|
||||
}
|
||||
|
||||
public void Imul(Operand dest, Operand source)
|
||||
{
|
||||
if (source.Kind != OperandKind.Register)
|
||||
{
|
||||
throw new ArgumentException($"Invalid source operand kind \"{source.Kind}\".");
|
||||
}
|
||||
|
||||
WriteInstruction(dest, source, X86Instruction.Imul);
|
||||
}
|
||||
|
||||
public void Imul(Operand dest, Operand src1, Operand src2)
|
||||
{
|
||||
InstInfo info = _instTable[(int)X86Instruction.Imul];
|
||||
|
||||
if (src2.Kind != OperandKind.Constant)
|
||||
{
|
||||
throw new ArgumentException($"Invalid source 2 operand kind \"{src2.Kind}\".");
|
||||
}
|
||||
|
||||
if (IsImm8(src2) && info.OpRMImm8 != BadOp)
|
||||
{
|
||||
WriteRRMOpCode(dest, src1, info.OpRMImm8);
|
||||
|
||||
WriteByte(src2.AsInt32());
|
||||
}
|
||||
else if (IsImm32(src2) && info.OpRMImm32 != BadOp)
|
||||
{
|
||||
WriteRRMOpCode(dest, src1, info.OpRMImm32);
|
||||
|
||||
WriteInt32(src2.AsInt32());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Failed to encode constant 0x{src2.Value:X}.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Jcc(X86Condition condition, long offset)
|
||||
{
|
||||
if (ConstFitsOnS8(offset))
|
||||
{
|
||||
WriteByte(0x70 | (int)condition);
|
||||
|
||||
WriteByte((byte)offset);
|
||||
}
|
||||
else if (ConstFitsOnS32(offset))
|
||||
{
|
||||
WriteByte(0x0f);
|
||||
WriteByte(0x80 | (int)condition);
|
||||
|
||||
WriteInt32((int)offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
}
|
||||
}
|
||||
|
||||
public void Jmp(long offset)
|
||||
{
|
||||
if (ConstFitsOnS8(offset))
|
||||
{
|
||||
WriteByte(0xeb);
|
||||
|
||||
WriteByte((byte)offset);
|
||||
}
|
||||
else if (ConstFitsOnS32(offset))
|
||||
{
|
||||
WriteByte(0xe9);
|
||||
|
||||
WriteInt32((int)offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
}
|
||||
}
|
||||
|
||||
public void Mov(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Mov);
|
||||
}
|
||||
|
||||
public void Mov16(Operand dest, Operand source)
|
||||
{
|
||||
WriteByte(R16Prefix);
|
||||
|
||||
WriteInstruction(dest, source, X86Instruction.Mov16);
|
||||
}
|
||||
|
||||
public void Mov8(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Mov8);
|
||||
}
|
||||
|
||||
public void Movsx16(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Movsx16);
|
||||
}
|
||||
|
||||
public void Movsx32(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Movsx32);
|
||||
}
|
||||
|
||||
public void Movsx8(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Movsx8);
|
||||
}
|
||||
|
||||
public void Movzx16(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Movzx16);
|
||||
}
|
||||
|
||||
public void Movzx8(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Movzx8);
|
||||
}
|
||||
|
||||
public void Neg(Operand dest)
|
||||
{
|
||||
WriteInstruction(dest, null, X86Instruction.Neg);
|
||||
}
|
||||
|
||||
public void Not(Operand dest)
|
||||
{
|
||||
WriteInstruction(dest, null, X86Instruction.Not);
|
||||
}
|
||||
|
||||
public void Or(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Or);
|
||||
}
|
||||
|
||||
public void Pop(Operand dest)
|
||||
{
|
||||
if (dest.Kind == OperandKind.Register)
|
||||
{
|
||||
WriteCompactInst(dest, 0x58);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteInstruction(dest, null, X86Instruction.Pop);
|
||||
}
|
||||
}
|
||||
|
||||
public void Push(Operand source)
|
||||
{
|
||||
if (source.Kind == OperandKind.Register)
|
||||
{
|
||||
WriteCompactInst(source, 0x50);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteInstruction(null, source, X86Instruction.Push);
|
||||
}
|
||||
}
|
||||
|
||||
public void Return()
|
||||
{
|
||||
WriteByte(0xc3);
|
||||
}
|
||||
|
||||
public void Ror(Operand dest, Operand source)
|
||||
{
|
||||
WriteShiftInst(dest, source, X86Instruction.Ror);
|
||||
}
|
||||
|
||||
public void Sar(Operand dest, Operand source)
|
||||
{
|
||||
WriteShiftInst(dest, source, X86Instruction.Sar);
|
||||
}
|
||||
|
||||
public void Shl(Operand dest, Operand source)
|
||||
{
|
||||
WriteShiftInst(dest, source, X86Instruction.Shl);
|
||||
}
|
||||
|
||||
public void Shr(Operand dest, Operand source)
|
||||
{
|
||||
WriteShiftInst(dest, source, X86Instruction.Shr);
|
||||
}
|
||||
|
||||
public void Setcc(Operand dest, X86Condition condition)
|
||||
{
|
||||
WriteOpCode(dest, null, 0x0f90 | (int)condition, rrm: false, r8h: true);
|
||||
}
|
||||
|
||||
public void Sub(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Sub);
|
||||
}
|
||||
|
||||
public void Test(Operand src1, Operand src2)
|
||||
{
|
||||
WriteInstruction(src1, src2, X86Instruction.Test);
|
||||
}
|
||||
|
||||
public void Xor(Operand dest, Operand source)
|
||||
{
|
||||
WriteInstruction(dest, source, X86Instruction.Xor);
|
||||
}
|
||||
|
||||
private void WriteShiftInst(Operand dest, Operand source, X86Instruction inst)
|
||||
{
|
||||
if (source.Kind == OperandKind.Register)
|
||||
{
|
||||
X86Register shiftReg = (X86Register)source.GetRegister().Index;
|
||||
|
||||
if (shiftReg != X86Register.Rcx)
|
||||
{
|
||||
throw new ArgumentException($"Invalid shift register \"{shiftReg}\".");
|
||||
}
|
||||
}
|
||||
|
||||
WriteInstruction(dest, source, inst);
|
||||
}
|
||||
|
||||
private void WriteInstruction(Operand dest, Operand source, X86Instruction inst)
|
||||
{
|
||||
InstInfo info = _instTable[(int)inst];
|
||||
|
||||
if (source != null)
|
||||
{
|
||||
if (source.Kind == OperandKind.Constant)
|
||||
{
|
||||
if (inst == X86Instruction.Mov8)
|
||||
{
|
||||
WriteOpCode(dest, null, info.OpRMImm8);
|
||||
|
||||
WriteByte(source.AsInt32());
|
||||
}
|
||||
else if (inst == X86Instruction.Mov16)
|
||||
{
|
||||
WriteOpCode(dest, null, info.OpRMImm32);
|
||||
|
||||
WriteInt16(source.AsInt16());
|
||||
}
|
||||
else if (IsImm8(source) && info.OpRMImm8 != BadOp)
|
||||
{
|
||||
WriteOpCode(dest, null, info.OpRMImm8);
|
||||
|
||||
WriteByte(source.AsInt32());
|
||||
}
|
||||
else if (IsImm32(source) && info.OpRMImm32 != BadOp)
|
||||
{
|
||||
WriteOpCode(dest, null, info.OpRMImm32);
|
||||
|
||||
WriteInt32(source.AsInt32());
|
||||
}
|
||||
else if (dest != null && IsR64(dest) && info.OpRImm64 != BadOp)
|
||||
{
|
||||
int rexPrefix = GetRexPrefix(dest, source, rrm: false);
|
||||
|
||||
if (rexPrefix != 0)
|
||||
{
|
||||
WriteByte(rexPrefix);
|
||||
}
|
||||
|
||||
WriteByte(info.OpRImm64 + (dest.GetRegister().Index & 0b111));
|
||||
|
||||
WriteUInt64(source.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Failed to encode constant 0x{source.Value:X}.");
|
||||
}
|
||||
}
|
||||
else if (source.Kind == OperandKind.Register && info.OpRMR != BadOp)
|
||||
{
|
||||
WriteOpCode(dest, source, info.OpRMR);
|
||||
}
|
||||
else if (info.OpRRM != BadOp)
|
||||
{
|
||||
WriteRRMOpCode(dest, source, info.OpRRM);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Invalid source operand kind \"{source.Kind}\".");
|
||||
}
|
||||
}
|
||||
else if (info.Opers == 1)
|
||||
{
|
||||
WriteOpCode(dest, source, info.OpRMR);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteRRMOpCode(Operand dest, Operand source, int opCode)
|
||||
{
|
||||
WriteOpCode(dest, source, opCode, rrm: true);
|
||||
}
|
||||
|
||||
private void WriteOpCode(
|
||||
Operand dest,
|
||||
Operand source,
|
||||
int opCode,
|
||||
bool rrm = false,
|
||||
bool r8h = false)
|
||||
{
|
||||
int rexPrefix = GetRexPrefix(dest, source, rrm);
|
||||
|
||||
int modRM = (opCode >> OpModRMBits) << 3;
|
||||
|
||||
X86MemoryOperand memOp = null;
|
||||
|
||||
if (dest != null)
|
||||
{
|
||||
if (dest.Kind == OperandKind.Register)
|
||||
{
|
||||
int regIndex = dest.GetRegister().Index;
|
||||
|
||||
modRM |= (regIndex & 0b111) << (rrm ? 3 : 0);
|
||||
|
||||
if (r8h && regIndex >= 4)
|
||||
{
|
||||
rexPrefix |= 0x40;
|
||||
}
|
||||
}
|
||||
else if (dest.Kind == OperandKind.Memory)
|
||||
{
|
||||
memOp = (X86MemoryOperand)dest;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid destination operand kind \"" + dest.Kind + "\".");
|
||||
}
|
||||
}
|
||||
|
||||
if (source != null)
|
||||
{
|
||||
if (source.Kind == OperandKind.Register)
|
||||
{
|
||||
modRM |= (source.GetRegister().Index & 0b111) << (rrm ? 0 : 3);
|
||||
}
|
||||
else if (source.Kind == OperandKind.Memory && memOp == null)
|
||||
{
|
||||
memOp = (X86MemoryOperand)source;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid source operand kind \"" + source.Kind + "\".");
|
||||
}
|
||||
}
|
||||
|
||||
bool needsSibByte = false;
|
||||
|
||||
bool needsDisplacement = false;
|
||||
|
||||
int sib = 0;
|
||||
|
||||
if (memOp != null)
|
||||
{
|
||||
//Either source or destination is a memory operand.
|
||||
Register baseReg = memOp.BaseAddress.GetRegister();
|
||||
|
||||
X86Register baseRegLow = (X86Register)(baseReg.Index & 0b111);
|
||||
|
||||
needsSibByte = memOp.Index != null || baseRegLow == X86Register.Rsp;
|
||||
|
||||
needsDisplacement = memOp.Displacement != 0 || baseRegLow == X86Register.Rbp;
|
||||
|
||||
if (needsDisplacement)
|
||||
{
|
||||
if (ConstFitsOnS8(memOp.Displacement))
|
||||
{
|
||||
modRM |= 0x40;
|
||||
}
|
||||
else /* if (ConstFitsOnS32(memOp.Displacement)) */
|
||||
{
|
||||
modRM |= 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsSibByte)
|
||||
{
|
||||
if (baseReg.Index >= 8)
|
||||
{
|
||||
rexPrefix |= 0x40 | (baseReg.Index >> 3);
|
||||
}
|
||||
|
||||
sib = (int)baseRegLow;
|
||||
|
||||
if (memOp.Index != null)
|
||||
{
|
||||
int indexReg = memOp.Index.GetRegister().Index;
|
||||
|
||||
if (indexReg == (int)X86Register.Rsp)
|
||||
{
|
||||
throw new ArgumentException("Using RSP as index register on the memory operand is not allowed.");
|
||||
}
|
||||
|
||||
if (indexReg >= 8)
|
||||
{
|
||||
rexPrefix |= 0x40 | (indexReg >> 3) << 1;
|
||||
}
|
||||
|
||||
sib |= (indexReg & 0b111) << 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
sib |= 0b100 << 3;
|
||||
}
|
||||
|
||||
sib |= (int)memOp.Scale << 6;
|
||||
|
||||
modRM |= 0b100;
|
||||
}
|
||||
else
|
||||
{
|
||||
modRM |= (int)baseRegLow;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Source and destination are registers.
|
||||
modRM |= 0xc0;
|
||||
}
|
||||
|
||||
if (rexPrefix != 0)
|
||||
{
|
||||
WriteByte(rexPrefix);
|
||||
}
|
||||
|
||||
if ((opCode & 0xff00) != 0)
|
||||
{
|
||||
WriteByte(opCode >> 8);
|
||||
}
|
||||
|
||||
WriteByte(opCode);
|
||||
WriteByte(modRM);
|
||||
|
||||
if (needsSibByte)
|
||||
{
|
||||
WriteByte(sib);
|
||||
}
|
||||
|
||||
if (needsDisplacement)
|
||||
{
|
||||
if (ConstFitsOnS8(memOp.Displacement))
|
||||
{
|
||||
WriteByte(memOp.Displacement);
|
||||
}
|
||||
else /* if (ConstFitsOnS32(memOp.Displacement)) */
|
||||
{
|
||||
WriteInt32(memOp.Displacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteCompactInst(Operand operand, int opCode)
|
||||
{
|
||||
int regIndex = operand.GetRegister().Index;
|
||||
|
||||
if (regIndex >= 8)
|
||||
{
|
||||
WriteByte(0x41);
|
||||
}
|
||||
|
||||
WriteByte(opCode + (regIndex & 0b111));
|
||||
}
|
||||
|
||||
private static int GetRexPrefix(Operand dest, Operand source, bool rrm)
|
||||
{
|
||||
int rexPrefix = 0;
|
||||
|
||||
void SetRegisterHighBit(Register reg, int bit)
|
||||
{
|
||||
if (reg.Index >= 8)
|
||||
{
|
||||
rexPrefix |= 0x40 | (reg.Index >> 3) << bit;
|
||||
}
|
||||
}
|
||||
|
||||
if (dest != null)
|
||||
{
|
||||
if (dest.Type == OperandType.I64)
|
||||
{
|
||||
rexPrefix = 0x48;
|
||||
}
|
||||
|
||||
if (dest.Kind == OperandKind.Register)
|
||||
{
|
||||
SetRegisterHighBit(dest.GetRegister(), (rrm ? 2 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (source != null && source.Kind == OperandKind.Register)
|
||||
{
|
||||
SetRegisterHighBit(source.GetRegister(), (rrm ? 0 : 2));
|
||||
}
|
||||
|
||||
return rexPrefix;
|
||||
}
|
||||
|
||||
private static bool IsR64(Operand operand)
|
||||
{
|
||||
return operand.Kind == OperandKind.Register &&
|
||||
operand.Type == OperandType.I64;
|
||||
}
|
||||
|
||||
private static bool IsImm8(Operand operand)
|
||||
{
|
||||
long value = operand.Type == OperandType.I32 ? operand.AsInt32()
|
||||
: operand.AsInt64();
|
||||
|
||||
return ConstFitsOnS8(value);
|
||||
}
|
||||
|
||||
private static bool IsImm32(Operand operand)
|
||||
{
|
||||
long value = operand.Type == OperandType.I32 ? operand.AsInt32()
|
||||
: operand.AsInt64();
|
||||
|
||||
return ConstFitsOnS32(value);
|
||||
}
|
||||
|
||||
public static int GetJccLength(long offset)
|
||||
{
|
||||
if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (ConstFitsOnS32(offset < 0 ? offset - 6 : offset))
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetJmpLength(long offset)
|
||||
{
|
||||
if (ConstFitsOnS8(offset < 0 ? offset - 2 : offset))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (ConstFitsOnS32(offset < 0 ? offset - 5 : offset))
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ConstFitsOnU32(long value)
|
||||
{
|
||||
return value >> 32 == 0;
|
||||
}
|
||||
|
||||
private static bool ConstFitsOnS8(long value)
|
||||
{
|
||||
return value == (sbyte)value;
|
||||
}
|
||||
|
||||
private static bool ConstFitsOnS32(long value)
|
||||
{
|
||||
return value == (int)value;
|
||||
}
|
||||
|
||||
private void WriteInt16(short value)
|
||||
{
|
||||
WriteUInt16((ushort)value);
|
||||
}
|
||||
|
||||
private void WriteInt32(int value)
|
||||
{
|
||||
WriteUInt32((uint)value);
|
||||
}
|
||||
|
||||
private void WriteInt64(long value)
|
||||
{
|
||||
WriteUInt64((ulong)value);
|
||||
}
|
||||
|
||||
private void WriteUInt16(ushort value)
|
||||
{
|
||||
_stream.WriteByte((byte)(value >> 0));
|
||||
_stream.WriteByte((byte)(value >> 8));
|
||||
}
|
||||
|
||||
private void WriteUInt32(uint value)
|
||||
{
|
||||
_stream.WriteByte((byte)(value >> 0));
|
||||
_stream.WriteByte((byte)(value >> 8));
|
||||
_stream.WriteByte((byte)(value >> 16));
|
||||
_stream.WriteByte((byte)(value >> 24));
|
||||
}
|
||||
|
||||
private void WriteUInt64(ulong value)
|
||||
{
|
||||
_stream.WriteByte((byte)(value >> 0));
|
||||
_stream.WriteByte((byte)(value >> 8));
|
||||
_stream.WriteByte((byte)(value >> 16));
|
||||
_stream.WriteByte((byte)(value >> 24));
|
||||
_stream.WriteByte((byte)(value >> 32));
|
||||
_stream.WriteByte((byte)(value >> 40));
|
||||
_stream.WriteByte((byte)(value >> 48));
|
||||
_stream.WriteByte((byte)(value >> 56));
|
||||
}
|
||||
|
||||
private void WriteByte(int value)
|
||||
{
|
||||
_stream.WriteByte((byte)value);
|
||||
}
|
||||
}
|
||||
}
|
33
ARMeilleure/CodeGen/X86/CallingConvention.cs
Normal file
33
ARMeilleure/CodeGen/X86/CallingConvention.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
static class CallingConvention
|
||||
{
|
||||
public static int GetIntAvailableRegisters()
|
||||
{
|
||||
int mask = 0xffff;
|
||||
|
||||
mask &= ~(1 << (int)X86Register.Rbp);
|
||||
mask &= ~(1 << (int)X86Register.Rsp);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
public static int GetIntCalleeSavedRegisters()
|
||||
{
|
||||
return (1 << (int)X86Register.Rbx) |
|
||||
(1 << (int)X86Register.Rbp) |
|
||||
(1 << (int)X86Register.Rdi) |
|
||||
(1 << (int)X86Register.Rsi) |
|
||||
(1 << (int)X86Register.Rsp) |
|
||||
(1 << (int)X86Register.R12) |
|
||||
(1 << (int)X86Register.R13) |
|
||||
(1 << (int)X86Register.R14) |
|
||||
(1 << (int)X86Register.R15);
|
||||
}
|
||||
|
||||
public static X86Register GetIntReturnRegister()
|
||||
{
|
||||
return X86Register.Rax;
|
||||
}
|
||||
}
|
||||
}
|
227
ARMeilleure/CodeGen/X86/CodeGenContext.cs
Normal file
227
ARMeilleure/CodeGen/X86/CodeGenContext.cs
Normal file
|
@ -0,0 +1,227 @@
|
|||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
class CodeGenContext
|
||||
{
|
||||
private const int ReservedBytesForJump = 1;
|
||||
|
||||
private Stream _stream;
|
||||
|
||||
public RAReport RAReport { get; }
|
||||
|
||||
public Assembler Assembler { get; }
|
||||
|
||||
public BasicBlock CurrBlock { get; private set; }
|
||||
|
||||
private struct Jump
|
||||
{
|
||||
public bool IsConditional { get; }
|
||||
|
||||
public X86Condition Condition { get; }
|
||||
|
||||
public BasicBlock Target { get; }
|
||||
|
||||
public long JumpPosition { get; }
|
||||
|
||||
public long RelativeOffset { get; set; }
|
||||
|
||||
public int InstSize { get; set; }
|
||||
|
||||
public Jump(BasicBlock target, long jumpPosition)
|
||||
{
|
||||
IsConditional = false;
|
||||
Condition = 0;
|
||||
Target = target;
|
||||
JumpPosition = jumpPosition;
|
||||
|
||||
RelativeOffset = 0;
|
||||
|
||||
InstSize = 0;
|
||||
}
|
||||
|
||||
public Jump(X86Condition condition, BasicBlock target, long jumpPosition)
|
||||
{
|
||||
IsConditional = true;
|
||||
Condition = condition;
|
||||
Target = target;
|
||||
JumpPosition = jumpPosition;
|
||||
|
||||
RelativeOffset = 0;
|
||||
|
||||
InstSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private long[] _blockOffsets;
|
||||
|
||||
private List<Jump> _jumps;
|
||||
|
||||
public CodeGenContext(Stream stream, RAReport raReport, int blocksCount)
|
||||
{
|
||||
_stream = stream;
|
||||
|
||||
RAReport = raReport;
|
||||
|
||||
Assembler = new Assembler(stream);
|
||||
|
||||
_blockOffsets = new long[blocksCount];
|
||||
|
||||
_jumps = new List<Jump>();
|
||||
}
|
||||
|
||||
public void EnterBlock(BasicBlock block)
|
||||
{
|
||||
_blockOffsets[block.Index] = _stream.Position;
|
||||
|
||||
CurrBlock = block;
|
||||
}
|
||||
|
||||
public void JumpTo(BasicBlock target)
|
||||
{
|
||||
_jumps.Add(new Jump(target, _stream.Position));
|
||||
|
||||
WritePadding(ReservedBytesForJump);
|
||||
}
|
||||
|
||||
public void JumpTo(X86Condition condition, BasicBlock target)
|
||||
{
|
||||
_jumps.Add(new Jump(condition, target, _stream.Position));
|
||||
|
||||
WritePadding(ReservedBytesForJump);
|
||||
}
|
||||
|
||||
private void WritePadding(int size)
|
||||
{
|
||||
while (size-- > 0)
|
||||
{
|
||||
_stream.WriteByte(0);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetCode()
|
||||
{
|
||||
//Write jump relative offsets.
|
||||
bool modified;
|
||||
|
||||
do
|
||||
{
|
||||
modified = false;
|
||||
|
||||
for (int index = 0; index < _jumps.Count; index++)
|
||||
{
|
||||
Jump jump = _jumps[index];
|
||||
|
||||
long jumpTarget = _blockOffsets[jump.Target.Index];
|
||||
|
||||
long offset = jumpTarget - jump.JumpPosition;
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
for (int index2 = index - 1; index2 >= 0; index2--)
|
||||
{
|
||||
Jump jump2 = _jumps[index2];
|
||||
|
||||
if (jump2.JumpPosition < jumpTarget)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
offset -= jump2.InstSize - ReservedBytesForJump;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index2 = index + 1; index2 < _jumps.Count; index2++)
|
||||
{
|
||||
Jump jump2 = _jumps[index2];
|
||||
|
||||
if (jump2.JumpPosition >= jumpTarget)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
offset += jump2.InstSize - ReservedBytesForJump;
|
||||
}
|
||||
|
||||
offset -= ReservedBytesForJump;
|
||||
}
|
||||
|
||||
if (jump.IsConditional)
|
||||
{
|
||||
jump.InstSize = Assembler.GetJccLength(offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
jump.InstSize = Assembler.GetJmpLength(offset);
|
||||
}
|
||||
|
||||
//The jump is relative to the next instruction, not the current one.
|
||||
//Since we didn't know the next instruction address when calculating
|
||||
//the offset (as the size of the current jump instruction was not know),
|
||||
//we now need to compensate the offset with the jump instruction size.
|
||||
//It's also worth to note that:
|
||||
//- This is only needed for backward jumps.
|
||||
//- The GetJmpLength and GetJccLength also compensates the offset
|
||||
//internally when computing the jump instruction size.
|
||||
if (offset < 0)
|
||||
{
|
||||
offset -= jump.InstSize;
|
||||
}
|
||||
|
||||
if (jump.RelativeOffset != offset)
|
||||
{
|
||||
modified = true;
|
||||
}
|
||||
|
||||
jump.RelativeOffset = offset;
|
||||
|
||||
_jumps[index] = jump;
|
||||
}
|
||||
}
|
||||
while (modified);
|
||||
|
||||
//Write the code, ignoring the dummy bytes after jumps, into a new stream.
|
||||
_stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using (MemoryStream codeStream = new MemoryStream())
|
||||
{
|
||||
Assembler assembler = new Assembler(codeStream);
|
||||
|
||||
byte[] buffer;
|
||||
|
||||
for (int index = 0; index < _jumps.Count; index++)
|
||||
{
|
||||
Jump jump = _jumps[index];
|
||||
|
||||
buffer = new byte[jump.JumpPosition - _stream.Position];
|
||||
|
||||
_stream.Read(buffer, 0, buffer.Length);
|
||||
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
|
||||
|
||||
codeStream.Write(buffer);
|
||||
|
||||
if (jump.IsConditional)
|
||||
{
|
||||
assembler.Jcc(jump.Condition, jump.RelativeOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
assembler.Jmp(jump.RelativeOffset);
|
||||
}
|
||||
}
|
||||
|
||||
buffer = new byte[_stream.Length - _stream.Position];
|
||||
|
||||
_stream.Read(buffer, 0, buffer.Length);
|
||||
|
||||
codeStream.Write(buffer);
|
||||
|
||||
return codeStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
523
ARMeilleure/CodeGen/X86/CodeGenerator.cs
Normal file
523
ARMeilleure/CodeGen/X86/CodeGenerator.cs
Normal file
|
@ -0,0 +1,523 @@
|
|||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
static class CodeGenerator
|
||||
{
|
||||
private static Action<CodeGenContext, Operation>[] _instTable;
|
||||
|
||||
static CodeGenerator()
|
||||
{
|
||||
_instTable = new Action<CodeGenContext, Operation>[(int)Instruction.Count];
|
||||
|
||||
Add(Instruction.Add, GenerateAdd);
|
||||
Add(Instruction.BitwiseAnd, GenerateBitwiseAnd);
|
||||
Add(Instruction.BitwiseExclusiveOr, GenerateBitwiseExclusiveOr);
|
||||
Add(Instruction.BitwiseNot, GenerateBitwiseNot);
|
||||
Add(Instruction.BitwiseOr, GenerateBitwiseOr);
|
||||
Add(Instruction.Branch, GenerateBranch);
|
||||
Add(Instruction.BranchIfFalse, GenerateBranchIfFalse);
|
||||
Add(Instruction.BranchIfTrue, GenerateBranchIfTrue);
|
||||
Add(Instruction.CompareEqual, GenerateCompareEqual);
|
||||
Add(Instruction.CompareGreater, GenerateCompareGreater);
|
||||
Add(Instruction.CompareGreaterOrEqual, GenerateCompareGreaterOrEqual);
|
||||
Add(Instruction.CompareGreaterOrEqualUI, GenerateCompareGreaterOrEqualUI);
|
||||
Add(Instruction.CompareGreaterUI, GenerateCompareGreaterUI);
|
||||
Add(Instruction.CompareLess, GenerateCompareLess);
|
||||
Add(Instruction.CompareLessOrEqual, GenerateCompareLessOrEqual);
|
||||
Add(Instruction.CompareLessOrEqualUI, GenerateCompareLessOrEqualUI);
|
||||
Add(Instruction.CompareLessUI, GenerateCompareLessUI);
|
||||
Add(Instruction.CompareNotEqual, GenerateCompareNotEqual);
|
||||
Add(Instruction.ConditionalSelect, GenerateConditionalSelect);
|
||||
Add(Instruction.Copy, GenerateCopy);
|
||||
Add(Instruction.Divide, GenerateDivide);
|
||||
Add(Instruction.Fill, GenerateFill);
|
||||
Add(Instruction.Load, GenerateLoad);
|
||||
Add(Instruction.LoadSx16, GenerateLoadSx16);
|
||||
Add(Instruction.LoadSx32, GenerateLoadSx32);
|
||||
Add(Instruction.LoadSx8, GenerateLoadSx8);
|
||||
Add(Instruction.LoadZx16, GenerateLoadZx16);
|
||||
Add(Instruction.LoadZx8, GenerateLoadZx8);
|
||||
Add(Instruction.Multiply, GenerateMultiply);
|
||||
Add(Instruction.Negate, GenerateNegate);
|
||||
Add(Instruction.Return, GenerateReturn);
|
||||
Add(Instruction.RotateRight, GenerateRotateRight);
|
||||
Add(Instruction.ShiftLeft, GenerateShiftLeft);
|
||||
Add(Instruction.ShiftRightSI, GenerateShiftRightSI);
|
||||
Add(Instruction.ShiftRightUI, GenerateShiftRightUI);
|
||||
Add(Instruction.SignExtend16, GenerateSignExtend16);
|
||||
Add(Instruction.SignExtend32, GenerateSignExtend32);
|
||||
Add(Instruction.SignExtend8, GenerateSignExtend8);
|
||||
Add(Instruction.Spill, GenerateSpill);
|
||||
Add(Instruction.Store, GenerateStore);
|
||||
Add(Instruction.Store16, GenerateStore16);
|
||||
Add(Instruction.Store8, GenerateStore8);
|
||||
Add(Instruction.Subtract, GenerateSubtract);
|
||||
}
|
||||
|
||||
private static void Add(Instruction inst, Action<CodeGenContext, Operation> func)
|
||||
{
|
||||
_instTable[(int)inst] = func;
|
||||
}
|
||||
|
||||
public static byte[] Generate(ControlFlowGraph cfg)
|
||||
{
|
||||
//IRAdapter.Adapt(cfg);
|
||||
|
||||
LinearScan regAlloc = new LinearScan();
|
||||
|
||||
RegisterMasks regMasks = new RegisterMasks(
|
||||
CallingConvention.GetIntAvailableRegisters(),
|
||||
CallingConvention.GetIntCalleeSavedRegisters());
|
||||
|
||||
RAReport raReport = regAlloc.Allocate(cfg, regMasks);
|
||||
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
CodeGenContext context = new CodeGenContext(stream, raReport, cfg.Blocks.Count);
|
||||
|
||||
WritePrologue(context);
|
||||
|
||||
context.Assembler.Mov(Register(X86Register.Rbp), Register(X86Register.Rcx));
|
||||
|
||||
foreach (BasicBlock block in cfg.Blocks)
|
||||
{
|
||||
context.EnterBlock(block);
|
||||
|
||||
foreach (Node node in block.Operations)
|
||||
{
|
||||
if (node is Operation operation)
|
||||
{
|
||||
GenerateOperation(context, operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return context.GetCode();
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateOperation(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Action<CodeGenContext, Operation> func = _instTable[(int)operation.Inst];
|
||||
|
||||
if (func != null)
|
||||
{
|
||||
func(context, operation);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Invalid operation instruction \"{operation.Inst}\".");
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateAdd(CodeGenContext context, Operation operation)
|
||||
{
|
||||
ValidateDestSrc1(operation);
|
||||
|
||||
context.Assembler.Add(operation.Dest, operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateBitwiseAnd(CodeGenContext context, Operation operation)
|
||||
{
|
||||
ValidateDestSrc1(operation);
|
||||
|
||||
context.Assembler.And(operation.Dest, operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateBitwiseExclusiveOr(CodeGenContext context, Operation operation)
|
||||
{
|
||||
ValidateDestSrc1(operation);
|
||||
|
||||
context.Assembler.Xor(operation.Dest, operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateBitwiseNot(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Not(operation.Dest);
|
||||
}
|
||||
|
||||
private static void GenerateBitwiseOr(CodeGenContext context, Operation operation)
|
||||
{
|
||||
ValidateDestSrc1(operation);
|
||||
|
||||
context.Assembler.Or(operation.Dest, operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateBranch(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.JumpTo(context.CurrBlock.Branch);
|
||||
}
|
||||
|
||||
private static void GenerateBranchIfFalse(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Test(operation.GetSource(0), operation.GetSource(0));
|
||||
|
||||
context.JumpTo(X86Condition.Equal, context.CurrBlock.Branch);
|
||||
}
|
||||
|
||||
private static void GenerateBranchIfTrue(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Test(operation.GetSource(0), operation.GetSource(0));
|
||||
|
||||
context.JumpTo(X86Condition.NotEqual, context.CurrBlock.Branch);
|
||||
}
|
||||
|
||||
private static void GenerateCompareEqual(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.Equal);
|
||||
}
|
||||
|
||||
private static void GenerateCompareGreater(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.Greater);
|
||||
}
|
||||
|
||||
private static void GenerateCompareGreaterOrEqual(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.GreaterOrEqual);
|
||||
}
|
||||
|
||||
private static void GenerateCompareGreaterOrEqualUI(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.AboveOrEqual);
|
||||
}
|
||||
|
||||
private static void GenerateCompareGreaterUI(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.Above);
|
||||
}
|
||||
|
||||
private static void GenerateCompareLess(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.Less);
|
||||
}
|
||||
|
||||
private static void GenerateCompareLessOrEqual(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.LessOrEqual);
|
||||
}
|
||||
|
||||
private static void GenerateCompareLessOrEqualUI(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.BelowOrEqual);
|
||||
}
|
||||
|
||||
private static void GenerateCompareLessUI(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.Below);
|
||||
}
|
||||
|
||||
private static void GenerateCompareNotEqual(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.NotEqual);
|
||||
}
|
||||
|
||||
private static void GenerateConditionalSelect(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Test(operation.GetSource(0), operation.GetSource(0));
|
||||
context.Assembler.Cmovcc(operation.Dest, operation.GetSource(1), X86Condition.NotEqual);
|
||||
}
|
||||
|
||||
private static void GenerateCopy(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand dest = operation.Dest;
|
||||
Operand source = operation.GetSource(0);
|
||||
|
||||
//Moves to the same register/memory location with the same type
|
||||
//are useless, we don't need to emit any code in this case.
|
||||
if (dest.Kind == source.Kind &&
|
||||
dest.Type == source.Type &&
|
||||
dest.Value == source.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (dest.Kind == OperandKind.Register &&
|
||||
source.Kind == OperandKind.Constant && source.Value == 0)
|
||||
{
|
||||
//Assemble "mov reg, 0" as "xor reg, reg" as the later is more efficient.
|
||||
dest = Get32BitsRegister(dest.GetRegister());
|
||||
|
||||
context.Assembler.Xor(dest, dest);
|
||||
}
|
||||
else if (dest.Type == OperandType.I64 && source.Type == OperandType.I32)
|
||||
{
|
||||
//I32 -> I64 zero-extension.
|
||||
if (dest.Kind == OperandKind.Register && source.Kind == OperandKind.Register)
|
||||
{
|
||||
dest = Get32BitsRegister(dest.GetRegister());
|
||||
}
|
||||
else if (source.Kind == OperandKind.Constant)
|
||||
{
|
||||
source = new Operand(source.Value);
|
||||
}
|
||||
|
||||
context.Assembler.Mov(dest, source);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Assembler.Mov(dest, source);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateDivide(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand divisor = operation.GetSource(1);
|
||||
|
||||
if (divisor.Type == OperandType.I32)
|
||||
{
|
||||
context.Assembler.Cdq();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Assembler.Cqo();
|
||||
}
|
||||
|
||||
context.Assembler.Idiv(divisor);
|
||||
}
|
||||
|
||||
private static void GenerateFill(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand dest = operation.Dest;
|
||||
Operand offset = operation.GetSource(0);
|
||||
|
||||
if (offset.Kind != OperandKind.Constant)
|
||||
{
|
||||
throw new InvalidOperationException("Fill has non-constant stack offset.");
|
||||
}
|
||||
|
||||
X86MemoryOperand memOp = new X86MemoryOperand(dest.Type, Register(X86Register.Rsp), null, Scale.x1, offset.AsInt32());
|
||||
|
||||
context.Assembler.Mov(dest, memOp);
|
||||
}
|
||||
|
||||
private static void GenerateLoad(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Mov(operation.Dest, operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateLoadSx16(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Movsx16(operation.Dest, operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateLoadSx32(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Movsx32(operation.Dest, operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateLoadSx8(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Movsx8(operation.Dest, operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateLoadZx16(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Movzx16(operation.Dest, operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateLoadZx8(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Movzx8(operation.Dest, operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateMultiply(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand dest = operation.Dest;
|
||||
Operand src1 = operation.GetSource(0);
|
||||
Operand src2 = operation.GetSource(1);
|
||||
|
||||
if (src2.Kind == OperandKind.Constant)
|
||||
{
|
||||
context.Assembler.Imul(dest, src1, src2);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Assembler.Imul(dest, src2);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateNegate(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Neg(operation.Dest);
|
||||
}
|
||||
|
||||
private static void GenerateReturn(CodeGenContext context, Operation operation)
|
||||
{
|
||||
if (operation.SourcesCount != 0)
|
||||
{
|
||||
Operand returnReg = Register(CallingConvention.GetIntReturnRegister());
|
||||
|
||||
Operand sourceReg = operation.GetSource(0);
|
||||
|
||||
if (returnReg.GetRegister() != sourceReg.GetRegister())
|
||||
{
|
||||
context.Assembler.Mov(returnReg, sourceReg);
|
||||
}
|
||||
}
|
||||
|
||||
WriteEpilogue(context);
|
||||
|
||||
context.Assembler.Return();
|
||||
}
|
||||
|
||||
private static void GenerateRotateRight(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Ror(operation.Dest, operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateShiftLeft(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Shl(operation.Dest, operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateShiftRightSI(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Sar(operation.Dest, operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateShiftRightUI(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Shr(operation.Dest, operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateSignExtend16(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Movsx16(operation.Dest, operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateSignExtend32(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Movsx32(operation.Dest, operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateSignExtend8(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Movsx8(operation.Dest, operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateSpill(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand offset = operation.GetSource(0);
|
||||
Operand source = operation.GetSource(1);
|
||||
|
||||
if (offset.Kind != OperandKind.Constant)
|
||||
{
|
||||
throw new InvalidOperationException("Spill has non-constant stack offset.");
|
||||
}
|
||||
|
||||
X86MemoryOperand memOp = new X86MemoryOperand(source.Type, Register(X86Register.Rsp), null, Scale.x1, offset.AsInt32());
|
||||
|
||||
context.Assembler.Mov(memOp, source);
|
||||
}
|
||||
|
||||
private static void GenerateStore(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Mov(operation.GetSource(0), operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateStore16(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Mov16(operation.GetSource(0), operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateStore8(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Mov8(operation.GetSource(0), operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateSubtract(CodeGenContext context, Operation operation)
|
||||
{
|
||||
ValidateDestSrc1(operation);
|
||||
|
||||
context.Assembler.Sub(operation.Dest, operation.GetSource(1));
|
||||
}
|
||||
|
||||
private static void GenerateCompare(CodeGenContext context, Operation operation, X86Condition condition)
|
||||
{
|
||||
context.Assembler.Cmp(operation.GetSource(0), operation.GetSource(1));
|
||||
context.Assembler.Setcc(operation.Dest, condition);
|
||||
}
|
||||
|
||||
private static void ValidateDestSrc1(Operation operation)
|
||||
{
|
||||
Operand dest = operation.Dest;
|
||||
Operand src1 = operation.GetSource(0);
|
||||
Operand src2 = operation.GetSource(1);
|
||||
|
||||
if (dest.Kind != OperandKind.Register)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid destination type \"{dest.Kind}\".");
|
||||
}
|
||||
|
||||
if (src1.Kind != OperandKind.Register)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid source 1 type \"{src1.Kind}\".");
|
||||
}
|
||||
|
||||
if (src2.Kind != OperandKind.Register && src2.Kind != OperandKind.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid source 2 type \"{src2.Kind}\".");
|
||||
}
|
||||
|
||||
if (dest.GetRegister() != src1.GetRegister())
|
||||
{
|
||||
throw new InvalidOperationException("Destination and source 1 register mismatch.");
|
||||
}
|
||||
|
||||
if (dest.Type != src1.Type || dest.Type != src2.Type)
|
||||
{
|
||||
throw new InvalidOperationException("Operand types mismatch.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void WritePrologue(CodeGenContext context)
|
||||
{
|
||||
int mask = CallingConvention.GetIntCalleeSavedRegisters() & context.RAReport.UsedRegisters;
|
||||
|
||||
mask |= 1 << (int)X86Register.Rbp;
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
int bit = BitUtils.LowestBitSet(mask);
|
||||
|
||||
context.Assembler.Push(Register((X86Register)bit));
|
||||
|
||||
mask &= ~(1 << bit);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteEpilogue(CodeGenContext context)
|
||||
{
|
||||
int mask = CallingConvention.GetIntCalleeSavedRegisters() & context.RAReport.UsedRegisters;
|
||||
|
||||
mask |= 1 << (int)X86Register.Rbp;
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
int bit = BitUtils.HighestBitSet(mask);
|
||||
|
||||
context.Assembler.Pop(Register((X86Register)bit));
|
||||
|
||||
mask &= ~(1 << bit);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand Get32BitsRegister(Register reg)
|
||||
{
|
||||
return new Operand(reg.Index, reg.Type, OperandType.I32);
|
||||
}
|
||||
|
||||
private static Operand Register(X86Register register)
|
||||
{
|
||||
return new Operand((int)register, RegisterType.Integer, OperandType.I64);
|
||||
}
|
||||
}
|
||||
}
|
419
ARMeilleure/CodeGen/X86/IRAdapter.cs
Normal file
419
ARMeilleure/CodeGen/X86/IRAdapter.cs
Normal file
|
@ -0,0 +1,419 @@
|
|||
using ARMeilleure.CodeGen.Optimizations;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
static class IRAdapter
|
||||
{
|
||||
private class IRModContext
|
||||
{
|
||||
private BasicBlock _block;
|
||||
private LinkedListNode<Node> _node;
|
||||
|
||||
public IRModContext(BasicBlock block, LinkedListNode<Node> node)
|
||||
{
|
||||
_block = block;
|
||||
_node = node.Previous;
|
||||
}
|
||||
|
||||
public Operand Append(Instruction inst, Operand src1, Operand src2)
|
||||
{
|
||||
Operand destSrc = AppendCopy(src1);
|
||||
|
||||
Operation operation = new Operation(inst, destSrc, destSrc, src2);
|
||||
|
||||
if (src2 is X86MemoryOperand memOp)
|
||||
{
|
||||
AddMemoryOperandUse(memOp, operation);
|
||||
}
|
||||
|
||||
Append(operation);
|
||||
|
||||
return destSrc;
|
||||
}
|
||||
|
||||
public Operand AppendCopy(Operand src)
|
||||
{
|
||||
Operation operation = new Operation(Instruction.Copy, Local(OperandType.I64), src);
|
||||
|
||||
if (src is X86MemoryOperand memOp)
|
||||
{
|
||||
AddMemoryOperandUse(memOp, operation);
|
||||
}
|
||||
|
||||
Append(operation);
|
||||
|
||||
return operation.Dest;
|
||||
}
|
||||
|
||||
private void Append(Operation operation)
|
||||
{
|
||||
if (_node != null)
|
||||
{
|
||||
_node = _block.Operations.AddAfter(_node, operation);
|
||||
}
|
||||
else
|
||||
{
|
||||
_node = _block.Operations.AddFirst(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Adapt(ControlFlowGraph cfg, MemoryManager memory)
|
||||
{
|
||||
Optimizer.Optimize(cfg);
|
||||
|
||||
foreach (BasicBlock block in cfg.Blocks)
|
||||
{
|
||||
for (LinkedListNode<Node> node = block.Operations.First; node != null; node = node.Next)
|
||||
{
|
||||
if (!(node.Value is Operation operation))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
void Clobber(X86Register register)
|
||||
{
|
||||
Operand reg = Gpr(register, OperandType.I32);
|
||||
|
||||
Operation copyOp = new Operation(Instruction.Copy, reg, reg);
|
||||
|
||||
block.Operations.AddBefore(node, copyOp);
|
||||
}
|
||||
|
||||
Operand AddCopy(Operand source)
|
||||
{
|
||||
Operand temp = Local(source.Type);
|
||||
|
||||
Operation copyOp = new Operation(Instruction.Copy, temp, source);
|
||||
|
||||
block.Operations.AddBefore(node, copyOp);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
Instruction inst = operation.Inst;
|
||||
|
||||
if (inst.IsMemory())
|
||||
{
|
||||
IRModContext context = new IRModContext(block, node);
|
||||
|
||||
Operand va = operation.GetSource(0);
|
||||
|
||||
OperandType valueType = inst == Instruction.Store ||
|
||||
inst == Instruction.Store16 ||
|
||||
inst == Instruction.Store8 ? operation.GetSource(1).Type : operation.Dest.Type;
|
||||
|
||||
X86MemoryOperand hostAddr = GuestToHostAddress(context, memory, valueType, va);
|
||||
|
||||
operation.SetSource(0, hostAddr);
|
||||
|
||||
AddMemoryOperandUse(hostAddr, operation);
|
||||
}
|
||||
|
||||
if (IsRMOnly(inst))
|
||||
{
|
||||
Operand src = operation.GetSource(0);
|
||||
|
||||
if (src.Kind == OperandKind.Constant)
|
||||
{
|
||||
src = AddCopy(src);
|
||||
|
||||
operation.SetSource(0, src);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation.Dest == null || operation.SourcesCount == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Operand dest = operation.Dest;
|
||||
Operand src1 = operation.GetSource(0);
|
||||
|
||||
bool isBinary = operation.SourcesCount == 2;
|
||||
|
||||
bool isRMOnly = IsRMOnly(inst);
|
||||
|
||||
if (operation.Inst != Instruction.Copy)
|
||||
{
|
||||
if ((src1.Kind == OperandKind.Constant && isBinary) || isRMOnly || IsLongConst(dest.Type, src1))
|
||||
{
|
||||
if (IsComutative(inst))
|
||||
{
|
||||
Operand src2 = operation.GetSource(1);
|
||||
Operand temp = src1;
|
||||
|
||||
src1 = src2;
|
||||
src2 = temp;
|
||||
|
||||
operation.SetSource(0, src1);
|
||||
operation.SetSource(1, src2);
|
||||
}
|
||||
|
||||
if (src1.Kind == OperandKind.Constant)
|
||||
{
|
||||
src1 = AddCopy(src1);
|
||||
|
||||
operation.SetSource(0, src1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isBinary)
|
||||
{
|
||||
Operand src2 = operation.GetSource(1);
|
||||
|
||||
//Comparison instructions uses CMOVcc, which does not zero the
|
||||
//upper bits of the register (since it's R8), we need to ensure it
|
||||
//is zero by zeroing it beforehand.
|
||||
if (inst.IsComparison())
|
||||
{
|
||||
Operation copyOp = new Operation(Instruction.Copy, dest, Const(0));
|
||||
|
||||
block.Operations.AddBefore(node, copyOp);
|
||||
}
|
||||
|
||||
//64-bits immediates are only supported by the MOV instruction.
|
||||
if (isRMOnly || IsLongConst(dest.Type, src2))
|
||||
{
|
||||
src2 = AddCopy(src2);
|
||||
|
||||
operation.SetSource(1, src2);
|
||||
}
|
||||
|
||||
//Handle the many restrictions of the division instructions:
|
||||
//- The dividend is always in RDX:RAX.
|
||||
//- The result is always in RAX.
|
||||
//- Additionally it also writes the remainder in RDX.
|
||||
if (inst == Instruction.Divide || inst == Instruction.DivideUI)
|
||||
{
|
||||
Operand rax = Gpr(X86Register.Rax, src1.Type);
|
||||
Operand rdx = Gpr(X86Register.Rdx, src1.Type);
|
||||
|
||||
Operation srcCopyOp = new Operation(Instruction.Copy, rax, src1);
|
||||
|
||||
block.Operations.AddBefore(node, srcCopyOp);
|
||||
|
||||
src1 = rax;
|
||||
|
||||
operation.SetSource(0, src1);
|
||||
|
||||
Clobber(X86Register.Rdx);
|
||||
|
||||
Operation destCopyOp = new Operation(Instruction.Copy, dest, rax);
|
||||
|
||||
block.Operations.AddAfter(node, destCopyOp);
|
||||
|
||||
dest = rax;
|
||||
|
||||
operation.Dest = dest;
|
||||
}
|
||||
|
||||
//The only allowed shift register is CL.
|
||||
if (inst.IsShift() && src2.Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
Operand rcx = Gpr(X86Register.Rcx, OperandType.I32);
|
||||
|
||||
Operation copyOp = new Operation(Instruction.Copy, rcx, src2);
|
||||
|
||||
block.Operations.AddBefore(node, copyOp);
|
||||
|
||||
src2 = rcx;
|
||||
|
||||
operation.SetSource(1, src2);
|
||||
}
|
||||
}
|
||||
|
||||
//The multiply instruction (that maps to IMUL) is somewhat special, it has
|
||||
//a three operand form where the second source is a immediate value.
|
||||
bool threeOperandForm = inst == Instruction.Multiply && operation.GetSource(1).Kind == OperandKind.Constant;
|
||||
|
||||
if (IsSameOperandDestSrc1(inst) && src1.Kind == OperandKind.LocalVariable && !threeOperandForm)
|
||||
{
|
||||
Operation copyOp = new Operation(Instruction.Copy, dest, src1);
|
||||
|
||||
block.Operations.AddBefore(node, copyOp);
|
||||
|
||||
operation.SetSource(0, dest);
|
||||
|
||||
src1 = dest;
|
||||
}
|
||||
else if (inst == Instruction.ConditionalSelect)
|
||||
{
|
||||
Operand src3 = operation.GetSource(2);
|
||||
|
||||
Operation copyOp = new Operation(Instruction.Copy, dest, src3);
|
||||
|
||||
block.Operations.AddBefore(node, copyOp);
|
||||
|
||||
operation.SetSource(2, dest);
|
||||
}
|
||||
|
||||
if (inst == Instruction.LoadFromContext ||
|
||||
inst == Instruction.StoreToContext)
|
||||
{
|
||||
if (inst == Instruction.LoadFromContext)
|
||||
{
|
||||
src1 = GetContextMemoryOperand(src1.GetRegister());
|
||||
|
||||
operation.Dest = null;
|
||||
}
|
||||
else /* if (inst == Instruction.StoreToContext) */
|
||||
{
|
||||
dest = GetContextMemoryOperand(dest.GetRegister());
|
||||
|
||||
operation.SetSource(0, null);
|
||||
}
|
||||
|
||||
operation = new Operation(Instruction.Copy, dest, src1);
|
||||
|
||||
LinkedListNode<Node> temp = block.Operations.AddBefore(node, operation);
|
||||
|
||||
block.Operations.Remove(node);
|
||||
|
||||
node = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsLongConst(OperandType destType, Operand operand)
|
||||
{
|
||||
if (operand.Kind != OperandKind.Constant)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (operand.Type == destType || destType == OperandType.I32)
|
||||
{
|
||||
return operand.AsInt32() != operand.AsInt64();
|
||||
}
|
||||
else
|
||||
{
|
||||
return operand.Value >> 31 != 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static X86MemoryOperand GuestToHostAddress(
|
||||
IRModContext context,
|
||||
MemoryManager memory,
|
||||
OperandType valueType,
|
||||
Operand va)
|
||||
{
|
||||
Operand vaPageOffs = context.Append(Instruction.BitwiseAnd, va, Const((ulong)MemoryManager.PageMask));
|
||||
|
||||
Operand ptBaseAddr = context.AppendCopy(Const(memory.PageTable.ToInt64()));
|
||||
|
||||
int bit = MemoryManager.PageBits;
|
||||
|
||||
do
|
||||
{
|
||||
va = context.Append(Instruction.ShiftRightUI, va, Const(bit));
|
||||
|
||||
Operand ptOffs = va;
|
||||
|
||||
bit += memory.PtLevelBits;
|
||||
|
||||
if (bit < memory.AddressSpaceBits)
|
||||
{
|
||||
ptOffs = context.Append(Instruction.BitwiseAnd, va, Const((ulong)memory.PtLevelMask));
|
||||
}
|
||||
|
||||
X86MemoryOperand memOp = new X86MemoryOperand(OperandType.I64, ptBaseAddr, ptOffs, Scale.x8, 0);
|
||||
|
||||
ptBaseAddr = context.AppendCopy(memOp);
|
||||
}
|
||||
while (bit < memory.AddressSpaceBits);
|
||||
|
||||
return new X86MemoryOperand(valueType, ptBaseAddr, vaPageOffs, Scale.x1, 0);
|
||||
}
|
||||
|
||||
private static X86MemoryOperand GetContextMemoryOperand(Register reg)
|
||||
{
|
||||
Operand baseReg = Register((int)X86Register.Rbp, RegisterType.Integer, OperandType.I64);
|
||||
|
||||
int offset = NativeContext.GetRegisterOffset(reg);
|
||||
|
||||
return new X86MemoryOperand(OperandType.I64, baseReg, null, Scale.x1, offset);
|
||||
}
|
||||
|
||||
private static void AddMemoryOperandUse(X86MemoryOperand memOp, Operation operation)
|
||||
{
|
||||
memOp.BaseAddress.Uses.AddLast(operation);
|
||||
|
||||
if (memOp.Index != null)
|
||||
{
|
||||
memOp.Index.Uses.AddLast(operation);
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand Gpr(X86Register register, OperandType type)
|
||||
{
|
||||
return Register((int)register, RegisterType.Integer, type);
|
||||
}
|
||||
|
||||
private static bool IsSameOperandDestSrc1(Instruction inst)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.Add:
|
||||
case Instruction.BitwiseAnd:
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
case Instruction.BitwiseNot:
|
||||
case Instruction.BitwiseOr:
|
||||
case Instruction.Multiply:
|
||||
case Instruction.Negate:
|
||||
case Instruction.RotateRight:
|
||||
case Instruction.ShiftLeft:
|
||||
case Instruction.ShiftRightSI:
|
||||
case Instruction.ShiftRightUI:
|
||||
case Instruction.Subtract:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsComutative(Instruction inst)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.Add:
|
||||
case Instruction.BitwiseAnd:
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
case Instruction.BitwiseOr:
|
||||
case Instruction.CompareEqual:
|
||||
case Instruction.CompareNotEqual:
|
||||
case Instruction.Multiply:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsRMOnly(Instruction inst)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.BranchIfFalse:
|
||||
case Instruction.BranchIfTrue:
|
||||
case Instruction.ConditionalSelect:
|
||||
case Instruction.Divide:
|
||||
case Instruction.DivideUI:
|
||||
case Instruction.SignExtend16:
|
||||
case Instruction.SignExtend32:
|
||||
case Instruction.SignExtend8:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
10
ARMeilleure/CodeGen/X86/Scale.cs
Normal file
10
ARMeilleure/CodeGen/X86/Scale.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
enum Scale
|
||||
{
|
||||
x1 = 0,
|
||||
x2 = 1,
|
||||
x4 = 2,
|
||||
x8 = 3
|
||||
}
|
||||
}
|
22
ARMeilleure/CodeGen/X86/X86Condition.cs
Normal file
22
ARMeilleure/CodeGen/X86/X86Condition.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
enum X86Condition
|
||||
{
|
||||
Overflow = 0x0,
|
||||
NotOverflow = 0x1,
|
||||
Below = 0x2,
|
||||
AboveOrEqual = 0x3,
|
||||
Equal = 0x4,
|
||||
NotEqual = 0x5,
|
||||
BelowOrEqual = 0x6,
|
||||
Above = 0x7,
|
||||
Sign = 0x8,
|
||||
NotSign = 0x9,
|
||||
ParityEven = 0xa,
|
||||
ParityOdd = 0xb,
|
||||
Less = 0xc,
|
||||
GreaterOrEqual = 0xd,
|
||||
LessOrEqual = 0xe,
|
||||
Greater = 0xf
|
||||
}
|
||||
}
|
33
ARMeilleure/CodeGen/X86/X86Instruction.cs
Normal file
33
ARMeilleure/CodeGen/X86/X86Instruction.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
enum X86Instruction
|
||||
{
|
||||
Add,
|
||||
And,
|
||||
Cmp,
|
||||
Idiv,
|
||||
Imul,
|
||||
Mov,
|
||||
Mov16,
|
||||
Mov8,
|
||||
Movsx16,
|
||||
Movsx32,
|
||||
Movsx8,
|
||||
Movzx16,
|
||||
Movzx8,
|
||||
Neg,
|
||||
Not,
|
||||
Or,
|
||||
Pop,
|
||||
Push,
|
||||
Ror,
|
||||
Sar,
|
||||
Shl,
|
||||
Shr,
|
||||
Sub,
|
||||
Test,
|
||||
Xor,
|
||||
|
||||
Count
|
||||
}
|
||||
}
|
26
ARMeilleure/CodeGen/X86/X86MemoryOperand.cs
Normal file
26
ARMeilleure/CodeGen/X86/X86MemoryOperand.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
class X86MemoryOperand : Operand
|
||||
{
|
||||
public Operand BaseAddress { get; set; }
|
||||
public Operand Index { get; set; }
|
||||
public Scale Scale { get; }
|
||||
|
||||
public int Displacement { get; }
|
||||
|
||||
public X86MemoryOperand(
|
||||
OperandType type,
|
||||
Operand baseAddress,
|
||||
Operand index,
|
||||
Scale scale,
|
||||
int displacement) : base(OperandKind.Memory, type)
|
||||
{
|
||||
BaseAddress = baseAddress;
|
||||
Index = index;
|
||||
Scale = scale;
|
||||
Displacement = displacement;
|
||||
}
|
||||
}
|
||||
}
|
22
ARMeilleure/CodeGen/X86/X86Register.cs
Normal file
22
ARMeilleure/CodeGen/X86/X86Register.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
enum X86Register
|
||||
{
|
||||
Rax = 0,
|
||||
Rcx = 1,
|
||||
Rdx = 2,
|
||||
Rbx = 3,
|
||||
Rsp = 4,
|
||||
Rbp = 5,
|
||||
Rsi = 6,
|
||||
Rdi = 7,
|
||||
R8 = 8,
|
||||
R9 = 9,
|
||||
R10 = 10,
|
||||
R11 = 11,
|
||||
R12 = 12,
|
||||
R13 = 13,
|
||||
R14 = 14,
|
||||
R15 = 15
|
||||
}
|
||||
}
|
138
ARMeilleure/Common/BitMap.cs
Normal file
138
ARMeilleure/Common/BitMap.cs
Normal file
|
@ -0,0 +1,138 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
class BitMap : IEnumerable<int>
|
||||
{
|
||||
private const int IntSize = 32;
|
||||
private const int IntMask = IntSize - 1;
|
||||
|
||||
private List<int> _masks;
|
||||
|
||||
public BitMap(int initialCapacity)
|
||||
{
|
||||
int count = (initialCapacity + IntMask) / IntSize;
|
||||
|
||||
_masks = new List<int>(count);
|
||||
|
||||
while (count-- > 0)
|
||||
{
|
||||
_masks.Add(0);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Set(int bit)
|
||||
{
|
||||
EnsureCapacity(bit + 1);
|
||||
|
||||
int wordIndex = bit / IntSize;
|
||||
int wordBit = bit & IntMask;
|
||||
|
||||
int wordMask = 1 << wordBit;
|
||||
|
||||
if ((_masks[wordIndex] & wordMask) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_masks[wordIndex] |= wordMask;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Clear(int bit)
|
||||
{
|
||||
EnsureCapacity(bit + 1);
|
||||
|
||||
int wordIndex = bit / IntSize;
|
||||
int wordBit = bit & IntMask;
|
||||
|
||||
int wordMask = 1 << wordBit;
|
||||
|
||||
_masks[wordIndex] &= ~wordMask;
|
||||
}
|
||||
|
||||
public bool IsSet(int bit)
|
||||
{
|
||||
EnsureCapacity(bit + 1);
|
||||
|
||||
int wordIndex = bit / IntSize;
|
||||
int wordBit = bit & IntMask;
|
||||
|
||||
return (_masks[wordIndex] & (1 << wordBit)) != 0;
|
||||
}
|
||||
|
||||
public bool Set(BitMap map)
|
||||
{
|
||||
EnsureCapacity(map._masks.Count * IntSize);
|
||||
|
||||
bool modified = false;
|
||||
|
||||
for (int index = 0; index < _masks.Count; index++)
|
||||
{
|
||||
int newValue = _masks[index] | map._masks[index];
|
||||
|
||||
if (_masks[index] != newValue)
|
||||
{
|
||||
_masks[index] = newValue;
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
public bool Clear(BitMap map)
|
||||
{
|
||||
EnsureCapacity(map._masks.Count * IntSize);
|
||||
|
||||
bool modified = false;
|
||||
|
||||
for (int index = 0; index < _masks.Count; index++)
|
||||
{
|
||||
int newValue = _masks[index] & ~map._masks[index];
|
||||
|
||||
if (_masks[index] != newValue)
|
||||
{
|
||||
_masks[index] = newValue;
|
||||
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
private void EnsureCapacity(int size)
|
||||
{
|
||||
while (_masks.Count * IntSize < size)
|
||||
{
|
||||
_masks.Add(0);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<int> GetEnumerator()
|
||||
{
|
||||
for (int index = 0; index < _masks.Count; index++)
|
||||
{
|
||||
int mask = _masks[index];
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
int bit = BitUtils.LowestBitSet(mask);
|
||||
|
||||
mask &= ~(1 << bit);
|
||||
|
||||
yield return index * IntSize + bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
72
ARMeilleure/Common/BitUtils.cs
Normal file
72
ARMeilleure/Common/BitUtils.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
namespace ARMeilleure.Common
|
||||
{
|
||||
static class BitUtils
|
||||
{
|
||||
public static int LowestBitSet(int value)
|
||||
{
|
||||
for (int bit = 0; bit < 32; bit++)
|
||||
{
|
||||
if (((value >> bit) & 1) != 0)
|
||||
{
|
||||
return bit;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int HighestBitSet(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));
|
||||
}
|
||||
}
|
||||
}
|
99
ARMeilleure/Decoders/Block.cs
Normal file
99
ARMeilleure/Decoders/Block.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class Block
|
||||
{
|
||||
public ulong Address { get; set; }
|
||||
public ulong EndAddress { get; set; }
|
||||
|
||||
public Block Next { get; set; }
|
||||
public Block Branch { get; set; }
|
||||
|
||||
public List<OpCode> OpCodes { get; private set; }
|
||||
|
||||
public Block()
|
||||
{
|
||||
OpCodes = new List<OpCode>();
|
||||
}
|
||||
|
||||
public Block(ulong address) : this()
|
||||
{
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public void Split(Block rightBlock)
|
||||
{
|
||||
int splitIndex = BinarySearch(OpCodes, rightBlock.Address);
|
||||
|
||||
if ((ulong)OpCodes[splitIndex].Address < rightBlock.Address)
|
||||
{
|
||||
splitIndex++;
|
||||
}
|
||||
|
||||
int splitCount = OpCodes.Count - splitIndex;
|
||||
|
||||
if (splitCount <= 0)
|
||||
{
|
||||
throw new ArgumentException("Can't split at right block address.");
|
||||
}
|
||||
|
||||
rightBlock.EndAddress = EndAddress;
|
||||
|
||||
rightBlock.Next = Next;
|
||||
rightBlock.Branch = Branch;
|
||||
|
||||
rightBlock.OpCodes.AddRange(OpCodes.GetRange(splitIndex, splitCount));
|
||||
|
||||
EndAddress = rightBlock.Address;
|
||||
|
||||
Next = rightBlock;
|
||||
Branch = null;
|
||||
|
||||
OpCodes.RemoveRange(splitIndex, splitCount);
|
||||
}
|
||||
|
||||
private static int BinarySearch(List<OpCode> opCodes, ulong address)
|
||||
{
|
||||
int left = 0;
|
||||
int middle = 0;
|
||||
int right = opCodes.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int size = right - left;
|
||||
|
||||
middle = left + (size >> 1);
|
||||
|
||||
OpCode opCode = opCodes[middle];
|
||||
|
||||
if (address == (ulong)opCode.Address)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (address < (ulong)opCode.Address)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return middle;
|
||||
}
|
||||
|
||||
public OpCode GetLastOp()
|
||||
{
|
||||
if (OpCodes.Count > 0)
|
||||
{
|
||||
return OpCodes[OpCodes.Count - 1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
32
ARMeilleure/Decoders/Condition.cs
Normal file
32
ARMeilleure/Decoders/Condition.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
enum Condition
|
||||
{
|
||||
Eq = 0,
|
||||
Ne = 1,
|
||||
GeUn = 2,
|
||||
LtUn = 3,
|
||||
Mi = 4,
|
||||
Pl = 5,
|
||||
Vs = 6,
|
||||
Vc = 7,
|
||||
GtUn = 8,
|
||||
LeUn = 9,
|
||||
Ge = 10,
|
||||
Lt = 11,
|
||||
Gt = 12,
|
||||
Le = 13,
|
||||
Al = 14,
|
||||
Nv = 15
|
||||
}
|
||||
|
||||
static class ConditionExtensions
|
||||
{
|
||||
public static Condition Invert(this Condition cond)
|
||||
{
|
||||
//Bit 0 of all conditions is basically a negation bit, so
|
||||
//inverting this bit has the effect of inverting the condition.
|
||||
return (Condition)((int)cond ^ 1);
|
||||
}
|
||||
}
|
||||
}
|
10
ARMeilleure/Decoders/DataOp.cs
Normal file
10
ARMeilleure/Decoders/DataOp.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
enum DataOp
|
||||
{
|
||||
Adr = 0,
|
||||
Arithmetic = 1,
|
||||
Logical = 2,
|
||||
BitField = 3
|
||||
}
|
||||
}
|
347
ARMeilleure/Decoders/Decoder.cs
Normal file
347
ARMeilleure/Decoders/Decoder.cs
Normal file
|
@ -0,0 +1,347 @@
|
|||
using ARMeilleure.Instructions;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
static class Decoder
|
||||
{
|
||||
private delegate object MakeOp(InstDescriptor inst, ulong address, int opCode);
|
||||
|
||||
private static ConcurrentDictionary<Type, MakeOp> _opActivators;
|
||||
|
||||
static Decoder()
|
||||
{
|
||||
_opActivators = new ConcurrentDictionary<Type, MakeOp>();
|
||||
}
|
||||
|
||||
public static Block[] DecodeFunction(MemoryManager memory, ulong address, ExecutionMode mode)
|
||||
{
|
||||
List<Block> blocks = new List<Block>();
|
||||
|
||||
Queue<Block> workQueue = new Queue<Block>();
|
||||
|
||||
Dictionary<ulong, Block> visited = new Dictionary<ulong, Block>();
|
||||
|
||||
Block GetBlock(ulong blkAddress)
|
||||
{
|
||||
if (!visited.TryGetValue(blkAddress, out Block block))
|
||||
{
|
||||
block = new Block(blkAddress);
|
||||
|
||||
workQueue.Enqueue(block);
|
||||
|
||||
visited.Add(blkAddress, block);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
GetBlock(address);
|
||||
|
||||
while (workQueue.TryDequeue(out Block currBlock))
|
||||
{
|
||||
//Check if the current block is inside another block.
|
||||
if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex))
|
||||
{
|
||||
Block nBlock = blocks[nBlkIndex];
|
||||
|
||||
if (nBlock.Address == currBlock.Address)
|
||||
{
|
||||
throw new InvalidOperationException("Found duplicate block address on the list.");
|
||||
}
|
||||
|
||||
nBlock.Split(currBlock);
|
||||
|
||||
blocks.Insert(nBlkIndex + 1, currBlock);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//If we have a block after the current one, set the limit address.
|
||||
ulong limitAddress = ulong.MaxValue;
|
||||
|
||||
if (nBlkIndex != blocks.Count)
|
||||
{
|
||||
Block nBlock = blocks[nBlkIndex];
|
||||
|
||||
int nextIndex = nBlkIndex + 1;
|
||||
|
||||
if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count)
|
||||
{
|
||||
limitAddress = blocks[nextIndex].Address;
|
||||
}
|
||||
else if (nBlock.Address > currBlock.Address)
|
||||
{
|
||||
limitAddress = blocks[nBlkIndex].Address;
|
||||
}
|
||||
}
|
||||
|
||||
FillBlock(memory, mode, currBlock, limitAddress);
|
||||
|
||||
if (currBlock.OpCodes.Count != 0)
|
||||
{
|
||||
//Set child blocks. "Branch" is the block the branch instruction
|
||||
//points to (when taken), "Next" is the block at the next address,
|
||||
//executed when the branch is not taken. For Unconditional Branches
|
||||
//(except BL/BLR that are sub calls) or end of executable, Next is null.
|
||||
OpCode lastOp = currBlock.GetLastOp();
|
||||
|
||||
bool isCall = IsCall(lastOp);
|
||||
|
||||
if (lastOp is IOpCodeBImm op && !isCall)
|
||||
{
|
||||
currBlock.Branch = GetBlock((ulong)op.Immediate);
|
||||
}
|
||||
|
||||
if (!IsUnconditionalBranch(lastOp) /*|| isCall*/)
|
||||
{
|
||||
currBlock.Next = GetBlock(currBlock.EndAddress);
|
||||
}
|
||||
}
|
||||
|
||||
//Insert the new block on the list (sorted by address).
|
||||
if (blocks.Count != 0)
|
||||
{
|
||||
Block nBlock = blocks[nBlkIndex];
|
||||
|
||||
blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
blocks.Add(currBlock);
|
||||
}
|
||||
}
|
||||
|
||||
return blocks.ToArray();
|
||||
}
|
||||
|
||||
private static bool BinarySearch(List<Block> blocks, ulong address, out int index)
|
||||
{
|
||||
index = 0;
|
||||
|
||||
int left = 0;
|
||||
int right = blocks.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int size = right - left;
|
||||
|
||||
int middle = left + (size >> 1);
|
||||
|
||||
Block block = blocks[middle];
|
||||
|
||||
index = middle;
|
||||
|
||||
if (address >= block.Address && address < block.EndAddress)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (address < block.Address)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void FillBlock(
|
||||
MemoryManager memory,
|
||||
ExecutionMode mode,
|
||||
Block block,
|
||||
ulong limitAddress)
|
||||
{
|
||||
ulong address = block.Address;
|
||||
|
||||
OpCode opCode;
|
||||
|
||||
do
|
||||
{
|
||||
if (address >= limitAddress)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
opCode = DecodeOpCode(memory, address, mode);
|
||||
|
||||
block.OpCodes.Add(opCode);
|
||||
|
||||
address += (ulong)opCode.OpCodeSizeInBytes;
|
||||
}
|
||||
while (!(IsBranch(opCode) || IsException(opCode)));
|
||||
|
||||
block.EndAddress = address;
|
||||
}
|
||||
|
||||
private static bool IsBranch(OpCode opCode)
|
||||
{
|
||||
return opCode is OpCodeBImm ||
|
||||
opCode is OpCodeBReg || IsAarch32Branch(opCode);
|
||||
}
|
||||
|
||||
private static bool IsUnconditionalBranch(OpCode opCode)
|
||||
{
|
||||
return opCode is OpCodeBImmAl ||
|
||||
opCode is OpCodeBReg || IsAarch32UnconditionalBranch(opCode);
|
||||
}
|
||||
|
||||
private static bool IsAarch32UnconditionalBranch(OpCode 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(OpCode opCode)
|
||||
{
|
||||
//Note: On ARM32, most ALU operations can write to R15 (PC),
|
||||
//so we must consider such operations as a branch in potential aswell.
|
||||
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.Instruction.Name == InstName.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 IsCall(OpCode opCode)
|
||||
{
|
||||
//TODO (CQ): ARM32 support.
|
||||
return opCode.Instruction.Name == InstName.Bl ||
|
||||
opCode.Instruction.Name == InstName.Blr;
|
||||
}
|
||||
|
||||
private static bool IsException(OpCode opCode)
|
||||
{
|
||||
return opCode.Instruction.Name == InstName.Brk ||
|
||||
opCode.Instruction.Name == InstName.Svc ||
|
||||
opCode.Instruction.Name == InstName.Und;
|
||||
}
|
||||
|
||||
public static OpCode DecodeOpCode(MemoryManager memory, ulong address, ExecutionMode mode)
|
||||
{
|
||||
int opCode = memory.ReadInt32((long)address);
|
||||
|
||||
InstDescriptor inst;
|
||||
|
||||
Type type;
|
||||
|
||||
if (mode == ExecutionMode.Aarch64)
|
||||
{
|
||||
(inst, type) = OpCodeTable.GetInstA64(opCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == ExecutionMode.Aarch32Arm)
|
||||
{
|
||||
(inst, type) = OpCodeTable.GetInstA32(opCode);
|
||||
}
|
||||
else /* if (mode == ExecutionMode.Aarch32Thumb) */
|
||||
{
|
||||
(inst, type) = OpCodeTable.GetInstT32(opCode);
|
||||
}
|
||||
}
|
||||
|
||||
OpCode decodedOpCode = new OpCode(inst, address, opCode);
|
||||
|
||||
if (type != null)
|
||||
{
|
||||
decodedOpCode = MakeOpCode(inst, type, address, opCode);
|
||||
}
|
||||
|
||||
return decodedOpCode;
|
||||
}
|
||||
|
||||
private static OpCode MakeOpCode(InstDescriptor inst, Type type, ulong address, int opCode)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
}
|
||||
|
||||
MakeOp createInstance = _opActivators.GetOrAdd(type, CacheOpActivator);
|
||||
|
||||
return (OpCode)createInstance(inst, address, opCode);
|
||||
}
|
||||
|
||||
private static MakeOp CacheOpActivator(Type type)
|
||||
{
|
||||
Type[] argTypes = new Type[] { typeof(InstDescriptor), typeof(ulong), typeof(int) };
|
||||
|
||||
DynamicMethod mthd = new DynamicMethod($"Make{type.Name}", type, argTypes);
|
||||
|
||||
ILGenerator generator = mthd.GetILGenerator();
|
||||
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Ldarg_1);
|
||||
generator.Emit(OpCodes.Ldarg_2);
|
||||
generator.Emit(OpCodes.Newobj, type.GetConstructor(argTypes));
|
||||
generator.Emit(OpCodes.Ret);
|
||||
|
||||
return (MakeOp)mthd.CreateDelegate(typeof(MakeOp));
|
||||
}
|
||||
}
|
||||
}
|
113
ARMeilleure/Decoders/DecoderHelper.cs
Normal file
113
ARMeilleure/Decoders/DecoderHelper.cs
Normal file
|
@ -0,0 +1,113 @@
|
|||
using ARMeilleure.Common;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
static class DecoderHelper
|
||||
{
|
||||
public struct BitMask
|
||||
{
|
||||
public long WMask;
|
||||
public long TMask;
|
||||
public int Pos;
|
||||
public int Shift;
|
||||
public bool IsUndefined;
|
||||
|
||||
public static BitMask Invalid => new BitMask { IsUndefined = true };
|
||||
}
|
||||
|
||||
public static BitMask DecodeBitMask(int opCode, bool immediate)
|
||||
{
|
||||
int immS = (opCode >> 10) & 0x3f;
|
||||
int immR = (opCode >> 16) & 0x3f;
|
||||
|
||||
int n = (opCode >> 22) & 1;
|
||||
int sf = (opCode >> 31) & 1;
|
||||
|
||||
int length = BitUtils.HighestBitSet((~immS & 0x3f) | (n << 6));
|
||||
|
||||
if (length < 1 || (sf == 0 && n != 0))
|
||||
{
|
||||
return BitMask.Invalid;
|
||||
}
|
||||
|
||||
int size = 1 << length;
|
||||
|
||||
int levels = size - 1;
|
||||
|
||||
int s = immS & levels;
|
||||
int r = immR & levels;
|
||||
|
||||
if (immediate && s == levels)
|
||||
{
|
||||
return BitMask.Invalid;
|
||||
}
|
||||
|
||||
long wMask = BitUtils.FillWithOnes(s + 1);
|
||||
long tMask = BitUtils.FillWithOnes(((s - r) & levels) + 1);
|
||||
|
||||
if (r > 0)
|
||||
{
|
||||
wMask = BitUtils.RotateRight(wMask, r, size);
|
||||
wMask &= BitUtils.FillWithOnes(size);
|
||||
}
|
||||
|
||||
return new BitMask()
|
||||
{
|
||||
WMask = BitUtils.Replicate(wMask, size),
|
||||
TMask = BitUtils.Replicate(tMask, size),
|
||||
|
||||
Pos = immS,
|
||||
Shift = immR
|
||||
};
|
||||
}
|
||||
|
||||
public static long DecodeImm8Float(long imm, int size)
|
||||
{
|
||||
int e = 0, f = 0;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: e = 8; f = 23; break;
|
||||
case 1: e = 11; f = 52; break;
|
||||
|
||||
default: throw new ArgumentOutOfRangeException(nameof(size));
|
||||
}
|
||||
|
||||
long value = (imm & 0x3f) << f - 4;
|
||||
|
||||
long eBit = (imm >> 6) & 1;
|
||||
long sBit = (imm >> 7) & 1;
|
||||
|
||||
if (eBit != 0)
|
||||
{
|
||||
value |= (1L << e - 3) - 1 << f + 2;
|
||||
}
|
||||
|
||||
value |= (eBit ^ 1) << f + e - 1;
|
||||
value |= sBit << f + e;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static long DecodeImmS19_2(int opCode)
|
||||
{
|
||||
return (((long)opCode << 40) >> 43) & ~3;
|
||||
}
|
||||
|
||||
public static long DecodeImmS14_2(int opCode)
|
||||
{
|
||||
return (((long)opCode << 45) >> 48) & ~3;
|
||||
}
|
||||
}
|
||||
}
|
17
ARMeilleure/Decoders/IOpCode.cs
Normal file
17
ARMeilleure/Decoders/IOpCode.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode
|
||||
{
|
||||
ulong Address { get; }
|
||||
|
||||
InstDescriptor Instruction { get; }
|
||||
|
||||
RegisterSize RegisterSize { get; }
|
||||
|
||||
int GetBitsCount();
|
||||
|
||||
OperandType GetOperandType();
|
||||
}
|
||||
}
|
9
ARMeilleure/Decoders/IOpCode32.cs
Normal file
9
ARMeilleure/Decoders/IOpCode32.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32 : IOpCode
|
||||
{
|
||||
Condition Cond { get; }
|
||||
|
||||
uint GetPc();
|
||||
}
|
||||
}
|
10
ARMeilleure/Decoders/IOpCode32Alu.cs
Normal file
10
ARMeilleure/Decoders/IOpCode32Alu.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32Alu : IOpCode32
|
||||
{
|
||||
int Rd { get; }
|
||||
int Rn { get; }
|
||||
|
||||
bool SetFlags { get; }
|
||||
}
|
||||
}
|
4
ARMeilleure/Decoders/IOpCode32BImm.cs
Normal file
4
ARMeilleure/Decoders/IOpCode32BImm.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32BImm : IOpCode32, IOpCodeBImm { }
|
||||
}
|
7
ARMeilleure/Decoders/IOpCode32BReg.cs
Normal file
7
ARMeilleure/Decoders/IOpCode32BReg.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32BReg : IOpCode32
|
||||
{
|
||||
int Rm { get; }
|
||||
}
|
||||
}
|
12
ARMeilleure/Decoders/IOpCode32Mem.cs
Normal file
12
ARMeilleure/Decoders/IOpCode32Mem.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32Mem : IOpCode32
|
||||
{
|
||||
int Rt { get; }
|
||||
int Rn { get; }
|
||||
|
||||
bool WBack { get; }
|
||||
|
||||
bool IsLoad { get; }
|
||||
}
|
||||
}
|
13
ARMeilleure/Decoders/IOpCode32MemMult.cs
Normal file
13
ARMeilleure/Decoders/IOpCode32MemMult.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCode32MemMult : IOpCode32
|
||||
{
|
||||
int Rn { get; }
|
||||
|
||||
int RegisterMask { get; }
|
||||
|
||||
int PostOffset { get; }
|
||||
|
||||
bool IsLoad { get; }
|
||||
}
|
||||
}
|
10
ARMeilleure/Decoders/IOpCodeAlu.cs
Normal file
10
ARMeilleure/Decoders/IOpCodeAlu.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCodeAlu : IOpCode
|
||||
{
|
||||
int Rd { get; }
|
||||
int Rn { get; }
|
||||
|
||||
DataOp DataOp { get; }
|
||||
}
|
||||
}
|
7
ARMeilleure/Decoders/IOpCodeAluImm.cs
Normal file
7
ARMeilleure/Decoders/IOpCodeAluImm.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCodeAluImm : IOpCodeAlu
|
||||
{
|
||||
long Immediate { get; }
|
||||
}
|
||||
}
|
10
ARMeilleure/Decoders/IOpCodeAluRs.cs
Normal file
10
ARMeilleure/Decoders/IOpCodeAluRs.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCodeAluRs : IOpCodeAlu
|
||||
{
|
||||
int Shift { get; }
|
||||
int Rm { get; }
|
||||
|
||||
ShiftType ShiftType { get; }
|
||||
}
|
||||
}
|
10
ARMeilleure/Decoders/IOpCodeAluRx.cs
Normal file
10
ARMeilleure/Decoders/IOpCodeAluRx.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCodeAluRx : IOpCodeAlu
|
||||
{
|
||||
int Shift { get; }
|
||||
int Rm { get; }
|
||||
|
||||
IntType IntType { get; }
|
||||
}
|
||||
}
|
7
ARMeilleure/Decoders/IOpCodeBImm.cs
Normal file
7
ARMeilleure/Decoders/IOpCodeBImm.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCodeBImm : IOpCode
|
||||
{
|
||||
long Immediate { get; }
|
||||
}
|
||||
}
|
7
ARMeilleure/Decoders/IOpCodeCond.cs
Normal file
7
ARMeilleure/Decoders/IOpCodeCond.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCodeCond : IOpCode
|
||||
{
|
||||
Condition Cond { get; }
|
||||
}
|
||||
}
|
11
ARMeilleure/Decoders/IOpCodeLit.cs
Normal file
11
ARMeilleure/Decoders/IOpCodeLit.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCodeLit : IOpCode
|
||||
{
|
||||
int Rt { get; }
|
||||
long Immediate { get; }
|
||||
int Size { get; }
|
||||
bool Signed { get; }
|
||||
bool Prefetch { get; }
|
||||
}
|
||||
}
|
7
ARMeilleure/Decoders/IOpCodeSimd.cs
Normal file
7
ARMeilleure/Decoders/IOpCodeSimd.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
interface IOpCodeSimd : IOpCode
|
||||
{
|
||||
int Size { get; }
|
||||
}
|
||||
}
|
18
ARMeilleure/Decoders/InstDescriptor.cs
Normal file
18
ARMeilleure/Decoders/InstDescriptor.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using ARMeilleure.Instructions;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
struct InstDescriptor
|
||||
{
|
||||
public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, null);
|
||||
|
||||
public InstName Name { get; }
|
||||
public InstEmitter Emitter { get; }
|
||||
|
||||
public InstDescriptor(InstName name, InstEmitter emitter)
|
||||
{
|
||||
Name = name;
|
||||
Emitter = emitter;
|
||||
}
|
||||
}
|
||||
}
|
6
ARMeilleure/Decoders/InstEmitter.cs
Normal file
6
ARMeilleure/Decoders/InstEmitter.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
using ARMeilleure.Translation;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
delegate void InstEmitter(EmitterContext context);
|
||||
}
|
14
ARMeilleure/Decoders/IntType.cs
Normal file
14
ARMeilleure/Decoders/IntType.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
enum IntType
|
||||
{
|
||||
UInt8 = 0,
|
||||
UInt16 = 1,
|
||||
UInt32 = 2,
|
||||
UInt64 = 3,
|
||||
Int8 = 4,
|
||||
Int16 = 5,
|
||||
Int32 = 6,
|
||||
Int64 = 7
|
||||
}
|
||||
}
|
45
ARMeilleure/Decoders/OpCode.cs
Normal file
45
ARMeilleure/Decoders/OpCode.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode : IOpCode
|
||||
{
|
||||
public ulong Address { get; private set; }
|
||||
public int RawOpCode { get; private set; }
|
||||
|
||||
public int OpCodeSizeInBytes { get; protected set; } = 4;
|
||||
|
||||
public InstDescriptor Instruction { get; protected set; }
|
||||
|
||||
public RegisterSize RegisterSize { get; protected set; }
|
||||
|
||||
public OpCode(InstDescriptor inst, ulong address, int opCode)
|
||||
{
|
||||
Address = address;
|
||||
RawOpCode = opCode;
|
||||
|
||||
Instruction = inst;
|
||||
|
||||
RegisterSize = RegisterSize.Int64;
|
||||
}
|
||||
|
||||
public int GetBitsCount()
|
||||
{
|
||||
switch (RegisterSize)
|
||||
{
|
||||
case RegisterSize.Int32: return 32;
|
||||
case RegisterSize.Int64: return 64;
|
||||
case RegisterSize.Simd64: return 64;
|
||||
case RegisterSize.Simd128: return 128;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public OperandType GetOperandType()
|
||||
{
|
||||
return RegisterSize == RegisterSize.Int32 ? OperandType.I32 : OperandType.I64;
|
||||
}
|
||||
}
|
||||
}
|
21
ARMeilleure/Decoders/OpCode32.cs
Normal file
21
ARMeilleure/Decoders/OpCode32.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32 : OpCode
|
||||
{
|
||||
public Condition Cond { get; protected set; }
|
||||
|
||||
public OpCode32(InstDescriptor inst, ulong address, int opCode) : base(inst, address, 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)Address + (uint)OpCodeSizeInBytes * 2;
|
||||
}
|
||||
}
|
||||
}
|
18
ARMeilleure/Decoders/OpCode32Alu.cs
Normal file
18
ARMeilleure/Decoders/OpCode32Alu.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32Alu : OpCode32, IOpCode32Alu
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public int Rn { get; private set; }
|
||||
|
||||
public bool SetFlags { get; private set; }
|
||||
|
||||
public OpCode32Alu(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 12) & 0xf;
|
||||
Rn = (opCode >> 16) & 0xf;
|
||||
|
||||
SetFlags = ((opCode >> 20) & 1) != 0;
|
||||
}
|
||||
}
|
||||
}
|
21
ARMeilleure/Decoders/OpCode32AluImm.cs
Normal file
21
ARMeilleure/Decoders/OpCode32AluImm.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using ARMeilleure.Common;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32AluImm : OpCode32Alu
|
||||
{
|
||||
public int Immediate { get; private set; }
|
||||
|
||||
public bool IsRotated { get; private set; }
|
||||
|
||||
public OpCode32AluImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int value = (opCode >> 0) & 0xff;
|
||||
int shift = (opCode >> 8) & 0xf;
|
||||
|
||||
Immediate = BitUtils.RotateRight(value, shift * 2, 32);
|
||||
|
||||
IsRotated = shift != 0;
|
||||
}
|
||||
}
|
||||
}
|
18
ARMeilleure/Decoders/OpCode32AluRsImm.cs
Normal file
18
ARMeilleure/Decoders/OpCode32AluRsImm.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32AluRsImm : OpCode32Alu
|
||||
{
|
||||
public int Rm { get; private set; }
|
||||
public int Imm { get; private set; }
|
||||
|
||||
public ShiftType ShiftType { get; private set; }
|
||||
|
||||
public OpCode32AluRsImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rm = (opCode >> 0) & 0xf;
|
||||
Imm = (opCode >> 7) & 0x1f;
|
||||
|
||||
ShiftType = (ShiftType)((opCode >> 5) & 3);
|
||||
}
|
||||
}
|
||||
}
|
27
ARMeilleure/Decoders/OpCode32BImm.cs
Normal file
27
ARMeilleure/Decoders/OpCode32BImm.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32BImm : OpCode32, IOpCode32BImm
|
||||
{
|
||||
public long Immediate { get; private set; }
|
||||
|
||||
public OpCode32BImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
uint pc = GetPc();
|
||||
|
||||
//When the codition is never, the instruction is BLX to Thumb mode.
|
||||
if (Cond != Condition.Nv)
|
||||
{
|
||||
pc &= ~3u;
|
||||
}
|
||||
|
||||
Immediate = pc + DecoderHelper.DecodeImm24_2(opCode);
|
||||
|
||||
if (Cond == Condition.Nv)
|
||||
{
|
||||
long H = (opCode >> 23) & 2;
|
||||
|
||||
Immediate |= H;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
ARMeilleure/Decoders/OpCode32BReg.cs
Normal file
12
ARMeilleure/Decoders/OpCode32BReg.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32BReg : OpCode32, IOpCode32BReg
|
||||
{
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public OpCode32BReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rm = opCode & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
37
ARMeilleure/Decoders/OpCode32Mem.cs
Normal file
37
ARMeilleure/Decoders/OpCode32Mem.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using ARMeilleure.Instructions;
|
||||
|
||||
namespace ARMeilleure.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(InstDescriptor inst, ulong address, int opCode) : base(inst, address, 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.Name == InstName.Ldrd;
|
||||
}
|
||||
}
|
||||
}
|
10
ARMeilleure/Decoders/OpCode32MemImm.cs
Normal file
10
ARMeilleure/Decoders/OpCode32MemImm.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32MemImm : OpCode32Mem
|
||||
{
|
||||
public OpCode32MemImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Imm = opCode & 0xfff;
|
||||
}
|
||||
}
|
||||
}
|
13
ARMeilleure/Decoders/OpCode32MemImm8.cs
Normal file
13
ARMeilleure/Decoders/OpCode32MemImm8.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32MemImm8 : OpCode32Mem
|
||||
{
|
||||
public OpCode32MemImm8(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int imm4L = (opCode >> 0) & 0xf;
|
||||
int imm4H = (opCode >> 8) & 0xf;
|
||||
|
||||
Imm = imm4L | (imm4H << 4);
|
||||
}
|
||||
}
|
||||
}
|
55
ARMeilleure/Decoders/OpCode32MemMult.cs
Normal file
55
ARMeilleure/Decoders/OpCode32MemMult.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
namespace ARMeilleure.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(InstDescriptor inst, ulong address, int opCode) : base(inst, address, 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;
|
||||
}
|
||||
}
|
||||
}
|
16
ARMeilleure/Decoders/OpCodeAdr.cs
Normal file
16
ARMeilleure/Decoders/OpCodeAdr.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeAdr : OpCode
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public long Immediate { get; private set; }
|
||||
|
||||
public OpCodeAdr(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = opCode & 0x1f;
|
||||
|
||||
Immediate = DecoderHelper.DecodeImmS19_2(opCode);
|
||||
Immediate |= ((long)opCode >> 29) & 3;
|
||||
}
|
||||
}
|
||||
}
|
21
ARMeilleure/Decoders/OpCodeAlu.cs
Normal file
21
ARMeilleure/Decoders/OpCodeAlu.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeAlu : OpCode, IOpCodeAlu
|
||||
{
|
||||
public int Rd { get; protected set; }
|
||||
public int Rn { get; private set; }
|
||||
|
||||
public DataOp DataOp { get; private set; }
|
||||
|
||||
public OpCodeAlu(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 0) & 0x1f;
|
||||
Rn = (opCode >> 5) & 0x1f;
|
||||
DataOp = (DataOp)((opCode >> 24) & 0x3);
|
||||
|
||||
RegisterSize = (opCode >> 31) != 0
|
||||
? RegisterSize.Int64
|
||||
: RegisterSize.Int32;
|
||||
}
|
||||
}
|
||||
}
|
38
ARMeilleure/Decoders/OpCodeAluImm.cs
Normal file
38
ARMeilleure/Decoders/OpCodeAluImm.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeAluImm : OpCodeAlu, IOpCodeAluImm
|
||||
{
|
||||
public long Immediate { get; private set; }
|
||||
|
||||
public OpCodeAluImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
if (DataOp == DataOp.Arithmetic)
|
||||
{
|
||||
Immediate = (opCode >> 10) & 0xfff;
|
||||
|
||||
int shift = (opCode >> 22) & 3;
|
||||
|
||||
Immediate <<= shift * 12;
|
||||
}
|
||||
else if (DataOp == DataOp.Logical)
|
||||
{
|
||||
var bm = DecoderHelper.DecodeBitMask(opCode, true);
|
||||
|
||||
if (bm.IsUndefined)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Immediate = bm.WMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(nameof(opCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
ARMeilleure/Decoders/OpCodeAluRs.cs
Normal file
27
ARMeilleure/Decoders/OpCodeAluRs.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeAluRs : OpCodeAlu, IOpCodeAluRs
|
||||
{
|
||||
public int Shift { get; private set; }
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public ShiftType ShiftType { get; private set; }
|
||||
|
||||
public OpCodeAluRs(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int shift = (opCode >> 10) & 0x3f;
|
||||
|
||||
if (shift >= GetBitsCount())
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Shift = shift;
|
||||
|
||||
Rm = (opCode >> 16) & 0x1f;
|
||||
ShiftType = (ShiftType)((opCode >> 22) & 0x3);
|
||||
}
|
||||
}
|
||||
}
|
17
ARMeilleure/Decoders/OpCodeAluRx.cs
Normal file
17
ARMeilleure/Decoders/OpCodeAluRx.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeAluRx : OpCodeAlu, IOpCodeAluRx
|
||||
{
|
||||
public int Shift { get; private set; }
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public IntType IntType { get; private set; }
|
||||
|
||||
public OpCodeAluRx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Shift = (opCode >> 10) & 0x7;
|
||||
IntType = (IntType)((opCode >> 13) & 0x7);
|
||||
Rm = (opCode >> 16) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
9
ARMeilleure/Decoders/OpCodeBImm.cs
Normal file
9
ARMeilleure/Decoders/OpCodeBImm.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeBImm : OpCode, IOpCodeBImm
|
||||
{
|
||||
public long Immediate { get; protected set; }
|
||||
|
||||
public OpCodeBImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { }
|
||||
}
|
||||
}
|
10
ARMeilleure/Decoders/OpCodeBImmAl.cs
Normal file
10
ARMeilleure/Decoders/OpCodeBImmAl.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeBImmAl : OpCodeBImm
|
||||
{
|
||||
public OpCodeBImmAl(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Immediate = (long)address + DecoderHelper.DecodeImm26_2(opCode);
|
||||
}
|
||||
}
|
||||
}
|
18
ARMeilleure/Decoders/OpCodeBImmCmp.cs
Normal file
18
ARMeilleure/Decoders/OpCodeBImmCmp.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeBImmCmp : OpCodeBImm
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
|
||||
public OpCodeBImmCmp(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = opCode & 0x1f;
|
||||
|
||||
Immediate = (long)address + DecoderHelper.DecodeImmS19_2(opCode);
|
||||
|
||||
RegisterSize = (opCode >> 31) != 0
|
||||
? RegisterSize.Int64
|
||||
: RegisterSize.Int32;
|
||||
}
|
||||
}
|
||||
}
|
23
ARMeilleure/Decoders/OpCodeBImmCond.cs
Normal file
23
ARMeilleure/Decoders/OpCodeBImmCond.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeBImmCond : OpCodeBImm, IOpCodeCond
|
||||
{
|
||||
public Condition Cond { get; private set; }
|
||||
|
||||
public OpCodeBImmCond(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int o0 = (opCode >> 4) & 1;
|
||||
|
||||
if (o0 != 0)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Cond = (Condition)(opCode & 0xf);
|
||||
|
||||
Immediate = (long)address + DecoderHelper.DecodeImmS19_2(opCode);
|
||||
}
|
||||
}
|
||||
}
|
18
ARMeilleure/Decoders/OpCodeBImmTest.cs
Normal file
18
ARMeilleure/Decoders/OpCodeBImmTest.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeBImmTest : OpCodeBImm
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
public int Bit { get; private set; }
|
||||
|
||||
public OpCodeBImmTest(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = opCode & 0x1f;
|
||||
|
||||
Immediate = (long)address + DecoderHelper.DecodeImmS14_2(opCode);
|
||||
|
||||
Bit = (opCode >> 19) & 0x1f;
|
||||
Bit |= (opCode >> 26) & 0x20;
|
||||
}
|
||||
}
|
||||
}
|
22
ARMeilleure/Decoders/OpCodeBReg.cs
Normal file
22
ARMeilleure/Decoders/OpCodeBReg.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeBReg : OpCode
|
||||
{
|
||||
public int Rn { get; private set; }
|
||||
|
||||
public OpCodeBReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int op4 = (opCode >> 0) & 0x1f;
|
||||
int op2 = (opCode >> 16) & 0x1f;
|
||||
|
||||
if (op2 != 0b11111 || op4 != 0b00000)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Rn = (opCode >> 5) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
27
ARMeilleure/Decoders/OpCodeBfm.cs
Normal file
27
ARMeilleure/Decoders/OpCodeBfm.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeBfm : OpCodeAlu
|
||||
{
|
||||
public long WMask { get; private set; }
|
||||
public long TMask { get; private set; }
|
||||
public int Pos { get; private set; }
|
||||
public int Shift { get; private set; }
|
||||
|
||||
public OpCodeBfm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
var bm = DecoderHelper.DecodeBitMask(opCode, false);
|
||||
|
||||
if (bm.IsUndefined)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
WMask = bm.WMask;
|
||||
TMask = bm.TMask;
|
||||
Pos = bm.Pos;
|
||||
Shift = bm.Shift;
|
||||
}
|
||||
}
|
||||
}
|
30
ARMeilleure/Decoders/OpCodeCcmp.cs
Normal file
30
ARMeilleure/Decoders/OpCodeCcmp.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using ARMeilleure.State;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeCcmp : OpCodeAlu, IOpCodeCond
|
||||
{
|
||||
public int Nzcv { get; private set; }
|
||||
protected int RmImm;
|
||||
|
||||
public Condition Cond { get; private set; }
|
||||
|
||||
public OpCodeCcmp(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int o3 = (opCode >> 4) & 1;
|
||||
|
||||
if (o3 != 0)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Nzcv = (opCode >> 0) & 0xf;
|
||||
Cond = (Condition)((opCode >> 12) & 0xf);
|
||||
RmImm = (opCode >> 16) & 0x1f;
|
||||
|
||||
Rd = RegisterAlias.Zr;
|
||||
}
|
||||
}
|
||||
}
|
9
ARMeilleure/Decoders/OpCodeCcmpImm.cs
Normal file
9
ARMeilleure/Decoders/OpCodeCcmpImm.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeCcmpImm : OpCodeCcmp, IOpCodeAluImm
|
||||
{
|
||||
public long Immediate => RmImm;
|
||||
|
||||
public OpCodeCcmpImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { }
|
||||
}
|
||||
}
|
13
ARMeilleure/Decoders/OpCodeCcmpReg.cs
Normal file
13
ARMeilleure/Decoders/OpCodeCcmpReg.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeCcmpReg : OpCodeCcmp, IOpCodeAluRs
|
||||
{
|
||||
public int Rm => RmImm;
|
||||
|
||||
public int Shift => 0;
|
||||
|
||||
public ShiftType ShiftType => ShiftType.Lsl;
|
||||
|
||||
public OpCodeCcmpReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode) { }
|
||||
}
|
||||
}
|
15
ARMeilleure/Decoders/OpCodeCsel.cs
Normal file
15
ARMeilleure/Decoders/OpCodeCsel.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeCsel : OpCodeAlu, IOpCodeCond
|
||||
{
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public Condition Cond { get; private set; }
|
||||
|
||||
public OpCodeCsel(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rm = (opCode >> 16) & 0x1f;
|
||||
Cond = (Condition)((opCode >> 12) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
12
ARMeilleure/Decoders/OpCodeException.cs
Normal file
12
ARMeilleure/Decoders/OpCodeException.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeException : OpCode
|
||||
{
|
||||
public int Id { get; private set; }
|
||||
|
||||
public OpCodeException(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Id = (opCode >> 5) & 0xffff;
|
||||
}
|
||||
}
|
||||
}
|
17
ARMeilleure/Decoders/OpCodeMem.cs
Normal file
17
ARMeilleure/Decoders/OpCodeMem.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMem : OpCode
|
||||
{
|
||||
public int Rt { get; protected set; }
|
||||
public int Rn { get; protected set; }
|
||||
public int Size { get; protected set; }
|
||||
public bool Extend64 { get; protected set; }
|
||||
|
||||
public OpCodeMem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = (opCode >> 0) & 0x1f;
|
||||
Rn = (opCode >> 5) & 0x1f;
|
||||
Size = (opCode >> 30) & 0x3;
|
||||
}
|
||||
}
|
||||
}
|
14
ARMeilleure/Decoders/OpCodeMemEx.cs
Normal file
14
ARMeilleure/Decoders/OpCodeMemEx.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMemEx : OpCodeMem
|
||||
{
|
||||
public int Rt2 { get; private set; }
|
||||
public int Rs { get; private set; }
|
||||
|
||||
public OpCodeMemEx(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt2 = (opCode >> 10) & 0x1f;
|
||||
Rs = (opCode >> 16) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
51
ARMeilleure/Decoders/OpCodeMemImm.cs
Normal file
51
ARMeilleure/Decoders/OpCodeMemImm.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMemImm : OpCodeMem
|
||||
{
|
||||
public long Immediate { get; protected set; }
|
||||
public bool WBack { get; protected set; }
|
||||
public bool PostIdx { get; protected set; }
|
||||
protected bool Unscaled { get; private set; }
|
||||
|
||||
private enum MemOp
|
||||
{
|
||||
Unscaled = 0,
|
||||
PostIndexed = 1,
|
||||
Unprivileged = 2,
|
||||
PreIndexed = 3,
|
||||
Unsigned
|
||||
}
|
||||
|
||||
public OpCodeMemImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Extend64 = ((opCode >> 22) & 3) == 2;
|
||||
WBack = ((opCode >> 24) & 1) == 0;
|
||||
|
||||
//The type is not valid for the Unsigned Immediate 12-bits encoding,
|
||||
//because the bits 11:10 are used for the larger Immediate offset.
|
||||
MemOp type = WBack ? (MemOp)((opCode >> 10) & 3) : MemOp.Unsigned;
|
||||
|
||||
PostIdx = type == MemOp.PostIndexed;
|
||||
Unscaled = type == MemOp.Unscaled ||
|
||||
type == MemOp.Unprivileged;
|
||||
|
||||
//Unscaled and Unprivileged doesn't write back,
|
||||
//but they do use the 9-bits Signed Immediate.
|
||||
if (Unscaled)
|
||||
{
|
||||
WBack = false;
|
||||
}
|
||||
|
||||
if (WBack || Unscaled)
|
||||
{
|
||||
//9-bits Signed Immediate.
|
||||
Immediate = (opCode << 11) >> 23;
|
||||
}
|
||||
else
|
||||
{
|
||||
//12-bits Unsigned Immediate.
|
||||
Immediate = ((opCode >> 10) & 0xfff) << Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
ARMeilleure/Decoders/OpCodeMemLit.cs
Normal file
26
ARMeilleure/Decoders/OpCodeMemLit.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMemLit : OpCode, IOpCodeLit
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
public long Immediate { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
public bool Signed { get; private set; }
|
||||
public bool Prefetch { get; private set; }
|
||||
|
||||
public OpCodeMemLit(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt = opCode & 0x1f;
|
||||
|
||||
Immediate = (long)address + DecoderHelper.DecodeImmS19_2(opCode);
|
||||
|
||||
switch ((opCode >> 30) & 3)
|
||||
{
|
||||
case 0: Size = 2; Signed = false; Prefetch = false; break;
|
||||
case 1: Size = 3; Signed = false; Prefetch = false; break;
|
||||
case 2: Size = 2; Signed = true; Prefetch = false; break;
|
||||
case 3: Size = 0; Signed = false; Prefetch = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
ARMeilleure/Decoders/OpCodeMemPair.cs
Normal file
23
ARMeilleure/Decoders/OpCodeMemPair.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMemPair : OpCodeMemImm
|
||||
{
|
||||
public int Rt2 { get; private set; }
|
||||
|
||||
public OpCodeMemPair(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rt2 = (opCode >> 10) & 0x1f;
|
||||
WBack = ((opCode >> 23) & 0x1) != 0;
|
||||
PostIdx = ((opCode >> 23) & 0x3) == 1;
|
||||
Extend64 = ((opCode >> 30) & 0x3) == 1;
|
||||
Size = ((opCode >> 31) & 0x1) | 2;
|
||||
|
||||
DecodeImm(opCode);
|
||||
}
|
||||
|
||||
protected void DecodeImm(int opCode)
|
||||
{
|
||||
Immediate = ((long)(opCode >> 15) << 57) >> (57 - Size);
|
||||
}
|
||||
}
|
||||
}
|
18
ARMeilleure/Decoders/OpCodeMemReg.cs
Normal file
18
ARMeilleure/Decoders/OpCodeMemReg.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMemReg : OpCodeMem
|
||||
{
|
||||
public bool Shift { get; private set; }
|
||||
public int Rm { get; private set; }
|
||||
|
||||
public IntType IntType { get; private set; }
|
||||
|
||||
public OpCodeMemReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Shift = ((opCode >> 12) & 0x1) != 0;
|
||||
IntType = (IntType)((opCode >> 13) & 0x7);
|
||||
Rm = (opCode >> 16) & 0x1f;
|
||||
Extend64 = ((opCode >> 22) & 0x3) == 2;
|
||||
}
|
||||
}
|
||||
}
|
33
ARMeilleure/Decoders/OpCodeMov.cs
Normal file
33
ARMeilleure/Decoders/OpCodeMov.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMov : OpCode
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public long Immediate { get; private set; }
|
||||
public int Bit { get; private set; }
|
||||
|
||||
public OpCodeMov(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int p1 = (opCode >> 22) & 1;
|
||||
int sf = (opCode >> 31) & 1;
|
||||
|
||||
if (sf == 0 && p1 != 0)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Rd = (opCode >> 0) & 0x1f;
|
||||
Immediate = (opCode >> 5) & 0xffff;
|
||||
Bit = (opCode >> 21) & 0x3;
|
||||
|
||||
Bit <<= 4;
|
||||
Immediate <<= Bit;
|
||||
|
||||
RegisterSize = (opCode >> 31) != 0
|
||||
? RegisterSize.Int64
|
||||
: RegisterSize.Int32;
|
||||
}
|
||||
}
|
||||
}
|
14
ARMeilleure/Decoders/OpCodeMul.cs
Normal file
14
ARMeilleure/Decoders/OpCodeMul.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeMul : OpCodeAlu
|
||||
{
|
||||
public int Rm { get; private set; }
|
||||
public int Ra { get; private set; }
|
||||
|
||||
public OpCodeMul(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Ra = (opCode >> 10) & 0x1f;
|
||||
Rm = (opCode >> 16) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
22
ARMeilleure/Decoders/OpCodeSimd.cs
Normal file
22
ARMeilleure/Decoders/OpCodeSimd.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimd : OpCode, IOpCodeSimd
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public int Rn { get; private set; }
|
||||
public int Opc { get; private set; }
|
||||
public int Size { get; protected set; }
|
||||
|
||||
public OpCodeSimd(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = (opCode >> 0) & 0x1f;
|
||||
Rn = (opCode >> 5) & 0x1f;
|
||||
Opc = (opCode >> 15) & 0x3;
|
||||
Size = (opCode >> 22) & 0x3;
|
||||
|
||||
RegisterSize = ((opCode >> 30) & 1) != 0
|
||||
? RegisterSize.Simd128
|
||||
: RegisterSize.Simd64;
|
||||
}
|
||||
}
|
||||
}
|
19
ARMeilleure/Decoders/OpCodeSimdCvt.cs
Normal file
19
ARMeilleure/Decoders/OpCodeSimdCvt.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdCvt : OpCodeSimd
|
||||
{
|
||||
public int FBits { get; private set; }
|
||||
|
||||
public OpCodeSimdCvt(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int scale = (opCode >> 10) & 0x3f;
|
||||
int sf = (opCode >> 31) & 0x1;
|
||||
|
||||
FBits = 64 - scale;
|
||||
|
||||
RegisterSize = sf != 0
|
||||
? RegisterSize.Int64
|
||||
: RegisterSize.Int32;
|
||||
}
|
||||
}
|
||||
}
|
12
ARMeilleure/Decoders/OpCodeSimdExt.cs
Normal file
12
ARMeilleure/Decoders/OpCodeSimdExt.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdExt : OpCodeSimdReg
|
||||
{
|
||||
public int Imm4 { get; private set; }
|
||||
|
||||
public OpCodeSimdExt(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Imm4 = (opCode >> 11) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
15
ARMeilleure/Decoders/OpCodeSimdFcond.cs
Normal file
15
ARMeilleure/Decoders/OpCodeSimdFcond.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdFcond : OpCodeSimdReg, IOpCodeCond
|
||||
{
|
||||
public int Nzcv { get; private set; }
|
||||
|
||||
public Condition Cond { get; private set; }
|
||||
|
||||
public OpCodeSimdFcond(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Nzcv = (opCode >> 0) & 0xf;
|
||||
Cond = (Condition)((opCode >> 12) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
31
ARMeilleure/Decoders/OpCodeSimdFmov.cs
Normal file
31
ARMeilleure/Decoders/OpCodeSimdFmov.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdFmov : OpCode, IOpCodeSimd
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public long Imm { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
|
||||
public OpCodeSimdFmov(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int imm5 = (opCode >> 5) & 0x1f;
|
||||
int type = (opCode >> 22) & 0x3;
|
||||
|
||||
if (imm5 != 0b00000 || type > 1)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Size = type;
|
||||
|
||||
long imm;
|
||||
|
||||
Rd = (opCode >> 0) & 0x1f;
|
||||
imm = (opCode >> 13) & 0xff;
|
||||
|
||||
Imm = DecoderHelper.DecodeImm8Float(imm, type);
|
||||
}
|
||||
}
|
||||
}
|
98
ARMeilleure/Decoders/OpCodeSimdImm.cs
Normal file
98
ARMeilleure/Decoders/OpCodeSimdImm.cs
Normal file
|
@ -0,0 +1,98 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdImm : OpCode, IOpCodeSimd
|
||||
{
|
||||
public int Rd { get; private set; }
|
||||
public long Imm { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
|
||||
public OpCodeSimdImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Rd = opCode & 0x1f;
|
||||
|
||||
int cMode = (opCode >> 12) & 0xf;
|
||||
int op = (opCode >> 29) & 0x1;
|
||||
|
||||
int modeLow = cMode & 1;
|
||||
int modeHigh = cMode >> 1;
|
||||
|
||||
long imm;
|
||||
|
||||
imm = ((uint)opCode >> 5) & 0x1f;
|
||||
imm |= ((uint)opCode >> 11) & 0xe0;
|
||||
|
||||
if (modeHigh == 0b111)
|
||||
{
|
||||
Size = modeLow != 0 ? op : 3;
|
||||
|
||||
switch (op | (modeLow << 1))
|
||||
{
|
||||
case 0:
|
||||
//64-bits Immediate.
|
||||
//Transform abcd efgh into abcd efgh abcd efgh ...
|
||||
imm = (long)((ulong)imm * 0x0101010101010101);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
//64-bits Immediate.
|
||||
//Transform abcd efgh into aaaa aaaa bbbb bbbb ...
|
||||
imm = (imm & 0xf0) >> 4 | (imm & 0x0f) << 4;
|
||||
imm = (imm & 0xcc) >> 2 | (imm & 0x33) << 2;
|
||||
imm = (imm & 0xaa) >> 1 | (imm & 0x55) << 1;
|
||||
|
||||
imm = (long)((ulong)imm * 0x8040201008040201);
|
||||
imm = (long)((ulong)imm & 0x8080808080808080);
|
||||
|
||||
imm |= imm >> 4;
|
||||
imm |= imm >> 2;
|
||||
imm |= imm >> 1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
//Floating point Immediate.
|
||||
imm = DecoderHelper.DecodeImm8Float(imm, Size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((modeHigh & 0b110) == 0b100)
|
||||
{
|
||||
//16-bits shifted Immediate.
|
||||
Size = 1; imm <<= (modeHigh & 1) << 3;
|
||||
}
|
||||
else if ((modeHigh & 0b100) == 0b000)
|
||||
{
|
||||
//32-bits shifted Immediate.
|
||||
Size = 2; imm <<= modeHigh << 3;
|
||||
}
|
||||
else if ((modeHigh & 0b111) == 0b110)
|
||||
{
|
||||
//32-bits shifted Immediate (fill with ones).
|
||||
Size = 2; imm = ShlOnes(imm, 8 << modeLow);
|
||||
}
|
||||
else
|
||||
{
|
||||
//8 bits without shift.
|
||||
Size = 0;
|
||||
}
|
||||
|
||||
Imm = imm;
|
||||
|
||||
RegisterSize = ((opCode >> 30) & 1) != 0
|
||||
? RegisterSize.Simd128
|
||||
: RegisterSize.Simd64;
|
||||
}
|
||||
|
||||
private static long ShlOnes(long value, int shift)
|
||||
{
|
||||
if (shift != 0)
|
||||
{
|
||||
return value << shift | (long)(ulong.MaxValue >> (64 - shift));
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
ARMeilleure/Decoders/OpCodeSimdIns.cs
Normal file
34
ARMeilleure/Decoders/OpCodeSimdIns.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdIns : OpCodeSimd
|
||||
{
|
||||
public int SrcIndex { get; private set; }
|
||||
public int DstIndex { get; private set; }
|
||||
|
||||
public OpCodeSimdIns(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int imm4 = (opCode >> 11) & 0xf;
|
||||
int imm5 = (opCode >> 16) & 0x1f;
|
||||
|
||||
if (imm5 == 0b10000)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Size = imm5 & -imm5;
|
||||
|
||||
switch (Size)
|
||||
{
|
||||
case 1: Size = 0; break;
|
||||
case 2: Size = 1; break;
|
||||
case 4: Size = 2; break;
|
||||
case 8: Size = 3; break;
|
||||
}
|
||||
|
||||
SrcIndex = imm4 >> Size;
|
||||
DstIndex = imm5 >> (Size + 1);
|
||||
}
|
||||
}
|
||||
}
|
17
ARMeilleure/Decoders/OpCodeSimdMemImm.cs
Normal file
17
ARMeilleure/Decoders/OpCodeSimdMemImm.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdMemImm : OpCodeMemImm, IOpCodeSimd
|
||||
{
|
||||
public OpCodeSimdMemImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size |= (opCode >> 21) & 4;
|
||||
|
||||
if (!WBack && !Unscaled && Size >= 4)
|
||||
{
|
||||
Immediate <<= 4;
|
||||
}
|
||||
|
||||
Extend64 = false;
|
||||
}
|
||||
}
|
||||
}
|
29
ARMeilleure/Decoders/OpCodeSimdMemLit.cs
Normal file
29
ARMeilleure/Decoders/OpCodeSimdMemLit.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdMemLit : OpCode, IOpCodeSimd, IOpCodeLit
|
||||
{
|
||||
public int Rt { get; private set; }
|
||||
public long Immediate { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
public bool Signed => false;
|
||||
public bool Prefetch => false;
|
||||
|
||||
public OpCodeSimdMemLit(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int opc = (opCode >> 30) & 3;
|
||||
|
||||
if (opc == 3)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Rt = opCode & 0x1f;
|
||||
|
||||
Immediate = (long)address + DecoderHelper.DecodeImmS19_2(opCode);
|
||||
|
||||
Size = opc + 2;
|
||||
}
|
||||
}
|
||||
}
|
46
ARMeilleure/Decoders/OpCodeSimdMemMs.cs
Normal file
46
ARMeilleure/Decoders/OpCodeSimdMemMs.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdMemMs : OpCodeMemReg, IOpCodeSimd
|
||||
{
|
||||
public int Reps { get; private set; }
|
||||
public int SElems { get; private set; }
|
||||
public int Elems { get; private set; }
|
||||
public bool WBack { get; private set; }
|
||||
|
||||
public OpCodeSimdMemMs(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
switch ((opCode >> 12) & 0xf)
|
||||
{
|
||||
case 0b0000: Reps = 1; SElems = 4; break;
|
||||
case 0b0010: Reps = 4; SElems = 1; break;
|
||||
case 0b0100: Reps = 1; SElems = 3; break;
|
||||
case 0b0110: Reps = 3; SElems = 1; break;
|
||||
case 0b0111: Reps = 1; SElems = 1; break;
|
||||
case 0b1000: Reps = 1; SElems = 2; break;
|
||||
case 0b1010: Reps = 2; SElems = 1; break;
|
||||
|
||||
default: Instruction = InstDescriptor.Undefined; return;
|
||||
}
|
||||
|
||||
Size = (opCode >> 10) & 3;
|
||||
WBack = ((opCode >> 23) & 1) != 0;
|
||||
|
||||
bool q = ((opCode >> 30) & 1) != 0;
|
||||
|
||||
if (!q && Size == 3 && SElems != 1)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Extend64 = false;
|
||||
|
||||
RegisterSize = q
|
||||
? RegisterSize.Simd128
|
||||
: RegisterSize.Simd64;
|
||||
|
||||
Elems = (GetBitsCount() >> 3) >> Size;
|
||||
}
|
||||
}
|
||||
}
|
14
ARMeilleure/Decoders/OpCodeSimdMemPair.cs
Normal file
14
ARMeilleure/Decoders/OpCodeSimdMemPair.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdMemPair : OpCodeMemPair, IOpCodeSimd
|
||||
{
|
||||
public OpCodeSimdMemPair(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size = ((opCode >> 30) & 3) + 2;
|
||||
|
||||
Extend64 = false;
|
||||
|
||||
DecodeImm(opCode);
|
||||
}
|
||||
}
|
||||
}
|
12
ARMeilleure/Decoders/OpCodeSimdMemReg.cs
Normal file
12
ARMeilleure/Decoders/OpCodeSimdMemReg.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdMemReg : OpCodeMemReg, IOpCodeSimd
|
||||
{
|
||||
public OpCodeSimdMemReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Size |= (opCode >> 21) & 4;
|
||||
|
||||
Extend64 = false;
|
||||
}
|
||||
}
|
||||
}
|
95
ARMeilleure/Decoders/OpCodeSimdMemSs.cs
Normal file
95
ARMeilleure/Decoders/OpCodeSimdMemSs.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdMemSs : OpCodeMemReg, IOpCodeSimd
|
||||
{
|
||||
public int SElems { get; private set; }
|
||||
public int Index { get; private set; }
|
||||
public bool Replicate { get; private set; }
|
||||
public bool WBack { get; private set; }
|
||||
|
||||
public OpCodeSimdMemSs(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
int size = (opCode >> 10) & 3;
|
||||
int s = (opCode >> 12) & 1;
|
||||
int sElems = (opCode >> 12) & 2;
|
||||
int scale = (opCode >> 14) & 3;
|
||||
int l = (opCode >> 22) & 1;
|
||||
int q = (opCode >> 30) & 1;
|
||||
|
||||
sElems |= (opCode >> 21) & 1;
|
||||
|
||||
sElems++;
|
||||
|
||||
int index = (q << 3) | (s << 2) | size;
|
||||
|
||||
switch (scale)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
if ((size & 1) != 0)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
index >>= 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
if ((size & 2) != 0 ||
|
||||
((size & 1) != 0 && s != 0))
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((size & 1) != 0)
|
||||
{
|
||||
index >>= 3;
|
||||
|
||||
scale = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
index >>= 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
if (l == 0 || s != 0)
|
||||
{
|
||||
Instruction = InstDescriptor.Undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
scale = size;
|
||||
|
||||
Replicate = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Index = index;
|
||||
SElems = sElems;
|
||||
Size = scale;
|
||||
|
||||
Extend64 = false;
|
||||
|
||||
WBack = ((opCode >> 23) & 1) != 0;
|
||||
|
||||
RegisterSize = q != 0
|
||||
? RegisterSize.Simd128
|
||||
: RegisterSize.Simd64;
|
||||
}
|
||||
}
|
||||
}
|
16
ARMeilleure/Decoders/OpCodeSimdReg.cs
Normal file
16
ARMeilleure/Decoders/OpCodeSimdReg.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdReg : OpCodeSimd
|
||||
{
|
||||
public bool Bit3 { get; private set; }
|
||||
public int Ra { get; private set; }
|
||||
public int Rm { get; protected set; }
|
||||
|
||||
public OpCodeSimdReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Bit3 = ((opCode >> 3) & 0x1) != 0;
|
||||
Ra = (opCode >> 10) & 0x1f;
|
||||
Rm = (opCode >> 16) & 0x1f;
|
||||
}
|
||||
}
|
||||
}
|
29
ARMeilleure/Decoders/OpCodeSimdRegElem.cs
Normal file
29
ARMeilleure/Decoders/OpCodeSimdRegElem.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdRegElem : OpCodeSimdReg
|
||||
{
|
||||
public int Index { get; private set; }
|
||||
|
||||
public OpCodeSimdRegElem(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
switch (Size)
|
||||
{
|
||||
case 1:
|
||||
Index = (opCode >> 20) & 3 |
|
||||
(opCode >> 9) & 4;
|
||||
|
||||
Rm &= 0xf;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Index = (opCode >> 21) & 1 |
|
||||
(opCode >> 10) & 2;
|
||||
|
||||
break;
|
||||
|
||||
default: Instruction = InstDescriptor.Undefined; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
ARMeilleure/Decoders/OpCodeSimdRegElemF.cs
Normal file
31
ARMeilleure/Decoders/OpCodeSimdRegElemF.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdRegElemF : OpCodeSimdReg
|
||||
{
|
||||
public int Index { get; private set; }
|
||||
|
||||
public OpCodeSimdRegElemF(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
switch ((opCode >> 21) & 3) // sz:L
|
||||
{
|
||||
case 0: // H:0
|
||||
Index = (opCode >> 10) & 2; // 0, 2
|
||||
|
||||
break;
|
||||
|
||||
case 1: // H:1
|
||||
Index = (opCode >> 10) & 2;
|
||||
Index++; // 1, 3
|
||||
|
||||
break;
|
||||
|
||||
case 2: // H
|
||||
Index = (opCode >> 11) & 1; // 0, 1
|
||||
|
||||
break;
|
||||
|
||||
default: Instruction = InstDescriptor.Undefined; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
ARMeilleure/Decoders/OpCodeSimdShImm.cs
Normal file
16
ARMeilleure/Decoders/OpCodeSimdShImm.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using ARMeilleure.Common;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCodeSimdShImm : OpCodeSimd
|
||||
{
|
||||
public int Imm { get; private set; }
|
||||
|
||||
public OpCodeSimdShImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
Imm = (opCode >> 16) & 0x7f;
|
||||
|
||||
Size = BitUtils.HighestBitSetNibble(Imm >> 3);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue