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
Add a link
Reference in a new issue