Support multiple destination operands

This commit is contained in:
gdkchan 2019-07-27 22:17:31 -03:00
parent ceb5c055a0
commit 36be15fe6c
9 changed files with 244 additions and 188 deletions

View file

@ -695,19 +695,24 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
Node operation = GetOperationNode(usePosition).Value;
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
for (int index = 0; index < operation.SourcesCount; index++)
{
Operand source = operation.GetSource(srcIndex);
Operand source = operation.GetSource(index);
if (source == current.Local)
{
operation.SetSource(srcIndex, register);
operation.SetSource(index, register);
}
}
if (operation.Dest == current.Local)
for (int index = 0; index < operation.DestinationsCount; index++)
{
operation.Dest = register;
Operand dest = operation.GetDestination(index);
if (dest == current.Local)
{
operation.SetDestination(index, register);
}
}
}
}
@ -753,13 +758,14 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
Node operation = node.Value;
Operand dest = operation.Dest;
if (dest != null && dest.Kind == OperandKind.LocalVariable && visited.Add(dest))
foreach (Operand dest in Destinations(operation))
{
dest.NumberLocal(_intervals.Count);
if (dest.Kind == OperandKind.LocalVariable && visited.Add(dest))
{
dest.NumberLocal(_intervals.Count);
_intervals.Add(new LiveInterval(dest));
_intervals.Add(new LiveInterval(dest));
}
}
}
@ -794,7 +800,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
foreach (Node node in block.Operations)
{
foreach (Operand source in Operands(node))
foreach (Operand source in Sources(node))
{
int id = GetOperandId(source);
@ -804,9 +810,9 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
}
}
if (node.Dest != null && IsLocalOrRegister(node.Dest.Kind))
foreach (Operand dest in Destinations(node))
{
liveKill.Set(GetOperandId(node.Dest));
liveKill.Set(GetOperandId(dest));
}
}
@ -894,9 +900,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
{
operationPos -= InstructionGap;
Operand dest = node.Dest;
if (dest != null && IsLocalOrRegister(dest.Kind))
foreach (Operand dest in Destinations(node))
{
LiveInterval interval = _intervals[GetOperandId(dest)];
@ -904,7 +908,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
interval.AddUsePosition(operationPos + 1);
}
foreach (Operand source in Operands(node))
foreach (Operand source in Sources(node))
{
LiveInterval interval = _intervals[GetOperandId(source)];
@ -983,7 +987,15 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
}
}
private static IEnumerable<Operand> Operands(Node node)
private static IEnumerable<Operand> Destinations(Node node)
{
for (int index = 0; index < node.DestinationsCount; index++)
{
yield return node.GetDestination(index);
}
}
private static IEnumerable<Operand> Sources(Node node)
{
for (int index = 0; index < node.SourcesCount; index++)
{

View file

@ -700,6 +700,10 @@ namespace ARMeilleure.CodeGen.X86
if (dest.Type.IsInteger())
{
divisor = operation.GetSource(2);
EnsureSameType(dest, divisor);
if (divisor.Type == OperandType.I32)
{
context.Assembler.Cdq();
@ -723,7 +727,7 @@ namespace ARMeilleure.CodeGen.X86
private static void GenerateDivideUI(CodeGenContext context, Operation operation)
{
Operand divisor = operation.GetSource(1);
Operand divisor = operation.GetSource(2);
Operand rdx = Register(X86Register.Rdx);

View file

@ -79,7 +79,7 @@ namespace ARMeilleure.CodeGen.X86
{
HandleLoadArgumentWindowsAbi(cctx, node, preservedArgs, operation);
}
else
else /* if (callConv == CallConvName.SystemV) */
{
HandleLoadArgumentSystemVAbi(cctx, node, preservedArgs, operation);
}
@ -209,28 +209,26 @@ namespace ARMeilleure.CodeGen.X86
// - The expected value should be in RDX:RAX.
// - The new value to be written should be in RCX:RBX.
// - The value at the memory location is loaded to RDX:RAX.
void SplitOperand(Operand source, X86Register lowReg, X86Register highReg)
void SplitOperand(Operand source, Operand lr, Operand hr)
{
Operand lr = Gpr(lowReg, OperandType.I64);
Operand hr = Gpr(highReg, OperandType.I64);
nodes.AddBefore(node, new Operation(Instruction.VectorExtract, lr, source, Const(0)));
nodes.AddBefore(node, new Operation(Instruction.VectorExtract, hr, source, Const(1)));
}
SplitOperand(operation.GetSource(1), X86Register.Rax, X86Register.Rdx);
SplitOperand(operation.GetSource(2), X86Register.Rbx, X86Register.Rcx);
Operand rax = Gpr(X86Register.Rax, OperandType.I64);
Operand rbx = Gpr(X86Register.Rbx, OperandType.I64);
Operand rcx = Gpr(X86Register.Rcx, OperandType.I64);
Operand rdx = Gpr(X86Register.Rdx, OperandType.I64);
SplitOperand(operation.GetSource(1), rax, rdx);
SplitOperand(operation.GetSource(2), rbx, rcx);
node = nodes.AddAfter(node, new Operation(Instruction.VectorCreateScalar, dest, rax));
node = nodes.AddAfter(node, new Operation(Instruction.VectorInsert, dest, dest, rdx, Const(1)));
operation.SetSource(1, Undef());
operation.SetSource(2, Undef());
operation.SetDestinations(new Operand[] { rdx, rax });
operation.Dest = null;
operation.SetSources(new Operand[] { operation.GetSource(0), rdx, rax, rcx, rbx });
break;
}
@ -245,25 +243,24 @@ namespace ARMeilleure.CodeGen.X86
Operand eax = Gpr(X86Register.Rax, OperandType.I32);
Operand ebx = Gpr(X86Register.Rbx, OperandType.I32);
Operand rcx = Gpr(X86Register.Rcx, OperandType.I64);
Operand ecx = Gpr(X86Register.Rcx, OperandType.I32);
Operand edx = Gpr(X86Register.Rdx, OperandType.I32);
// Value 0x01 = Version, family and feature information.
nodes.AddBefore(node, new Operation(Instruction.Copy, eax, Const(1)));
// We don't care about those two, but their values are overwritten,
// so we need to take that into account.
node = nodes.AddAfter(node, new Operation(Instruction.Clobber, eax));
node = nodes.AddAfter(node, new Operation(Instruction.Clobber, ebx));
// Copy results to the destination register.
// The values are split into 2 32-bits registers, we merge them
// into a single 64-bits register.
Operand rcx = Gpr(X86Register.Rcx, OperandType.I64);
node = nodes.AddAfter(node, new Operation(Instruction.ZeroExtend32, dest, edx));
node = nodes.AddAfter(node, new Operation(Instruction.ShiftLeft, dest, dest, Const(32)));
node = nodes.AddAfter(node, new Operation(Instruction.BitwiseOr, dest, dest, rcx));
operation.Dest = null;
operation.SetDestinations(new Operand[] { eax, ebx, ecx, edx });
operation.SetSources(new Operand[] { eax });
break;
}
@ -280,14 +277,15 @@ namespace ARMeilleure.CodeGen.X86
Operand rax = Gpr(X86Register.Rax, src1.Type);
Operand rdx = Gpr(X86Register.Rdx, src1.Type);
nodes.AddBefore(node, new Operation(Instruction.Copy, rax, src1));
operation.SetSource(0, rax);
nodes.AddBefore(node, new Operation(Instruction.Copy, rax, src1));
nodes.AddBefore(node, new Operation(Instruction.Clobber, rdx));
node = nodes.AddAfter(node, new Operation(Instruction.Copy, dest, rax));
operation.SetDestinations(new Operand[] { rdx, rax });
operation.SetSources(new Operand[] { rdx, rax, operation.GetSource(1) });
operation.Dest = rax;
break;
@ -328,7 +326,7 @@ namespace ARMeilleure.CodeGen.X86
node = nodes.AddAfter(node, new Operation(Instruction.Copy, dest, rdx));
operation.Dest = rdx;
operation.SetDestinations(new Operand[] { rdx, rax });
break;
}
@ -571,11 +569,13 @@ namespace ARMeilleure.CodeGen.X86
return offset;
}
Operand arg0Reg = null;
if (dest != null && dest.Type == OperandType.V128)
{
int stackOffset = AllocateOnStack(dest.Type.GetSizeInBytes());
Operand arg0Reg = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64);
arg0Reg = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64);
Operation allocOp = new Operation(Instruction.StackAlloc, arg0Reg, Const(stackOffset));
@ -584,6 +584,24 @@ namespace ARMeilleure.CodeGen.X86
retArgs = 1;
}
int argsCount = operation.SourcesCount - 1;
int maxArgs = CallingConvention.GetArgumentsOnRegsCount() - retArgs;
if (argsCount > maxArgs)
{
argsCount = maxArgs;
}
Operand[] sources = new Operand[1 + retArgs + argsCount];
sources[0] = operation.GetSource(0);
if (arg0Reg != null)
{
sources[1] = arg0Reg;
}
for (int index = 1; index < operation.SourcesCount; index++)
{
Operand source = operation.GetSource(index);
@ -602,35 +620,29 @@ namespace ARMeilleure.CodeGen.X86
HandleConstantCopy(nodes.AddBefore(node, storeOp), storeOp);
operation.SetSource(index, stackAddr);
sources[retArgs + index] = stackAddr;
}
}
// Handle arguments passed on registers.
int argsCount = operation.SourcesCount - 1;
int maxArgs = CallingConvention.GetArgumentsOnRegsCount() - retArgs;
if (argsCount > maxArgs)
{
argsCount = maxArgs;
}
for (int index = 0; index < argsCount; index++)
{
Operand source = operation.GetSource(index + 1);
RegisterType regType = source.Type.ToRegisterType();
if (source.Type == OperandType.V128)
{
source = sources[1 + retArgs + index];
}
Operand argReg;
int argIndex = index + retArgs;
if (regType == RegisterType.Integer)
if (source.Type.IsInteger())
{
argReg = Gpr(CallingConvention.GetIntArgumentRegister(argIndex), source.Type);
}
else /* if (regType == RegisterType.Vector) */
else
{
argReg = Xmm(CallingConvention.GetVecArgumentRegister(argIndex), source.Type);
}
@ -639,7 +651,7 @@ namespace ARMeilleure.CodeGen.X86
HandleConstantCopy(nodes.AddBefore(node, copyOp), copyOp);
operation.SetSource(index + 1, argReg);
sources[1 + retArgs + index] = argReg;
}
// The remaining arguments (those that are not passed on registers)
@ -653,8 +665,6 @@ namespace ARMeilleure.CodeGen.X86
Operation spillOp = new Operation(Instruction.SpillArg, null, offset, source);
HandleConstantCopy(nodes.AddBefore(node, spillOp), spillOp);
operation.SetSource(index + 1, new Operand(OperandKind.Undefined));
}
if (dest != null)
@ -663,8 +673,6 @@ namespace ARMeilleure.CodeGen.X86
{
Operand retValueAddr = Local(OperandType.I64);
Operand arg0Reg = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64);
Operation copyOp = new Operation(Instruction.Copy, retValueAddr, arg0Reg);
nodes.AddBefore(node, copyOp);
@ -677,9 +685,7 @@ namespace ARMeilleure.CodeGen.X86
}
else
{
RegisterType regType = dest.Type.ToRegisterType();
Operand retReg = regType == RegisterType.Integer
Operand retReg = dest.Type.IsInteger()
? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type)
: Xmm(CallingConvention.GetVecReturnRegister(), dest.Type);
@ -691,6 +697,8 @@ namespace ARMeilleure.CodeGen.X86
}
}
operation.SetSources(sources);
return node;
}
@ -700,7 +708,10 @@ namespace ARMeilleure.CodeGen.X86
LinkedList<Node> nodes = node.List;
// Handle arguments passed on registers.
List<Operand> sources = new List<Operand>();
sources.Add(operation.GetSource(0));
int argsCount = operation.SourcesCount - 1;
int intMax = CallingConvention.GetIntArgumentsOnRegsCount();
@ -739,8 +750,6 @@ namespace ARMeilleure.CodeGen.X86
nodes.AddBefore(node, new Operation(Instruction.VectorExtract, argReg, source, Const(0)));
nodes.AddBefore(node, new Operation(Instruction.VectorExtract, argReg2, source, Const(1)));
operation.SetSource(index + 1, Undef());
continue;
}
@ -754,7 +763,7 @@ namespace ARMeilleure.CodeGen.X86
HandleConstantCopy(nodes.AddBefore(node, copyOp), copyOp);
operation.SetSource(index + 1, argReg);
sources.Add(argReg);
}
else
{
@ -765,8 +774,6 @@ namespace ARMeilleure.CodeGen.X86
HandleConstantCopy(nodes.AddBefore(node, spillOp), spillOp);
stackOffset += source.Type.GetSizeInBytes();
operation.SetSource(index + 1, Undef());
}
}
@ -784,9 +791,7 @@ namespace ARMeilleure.CodeGen.X86
}
else
{
RegisterType regType = dest.Type.ToRegisterType();
Operand retReg = regType == RegisterType.Integer
Operand retReg = dest.Type.IsInteger()
? Gpr(CallingConvention.GetIntReturnRegister(), dest.Type)
: Xmm(CallingConvention.GetVecReturnRegister(), dest.Type);
@ -798,6 +803,8 @@ namespace ARMeilleure.CodeGen.X86
}
}
operation.SetSources(sources.ToArray());
return node;
}

View file

@ -76,21 +76,4 @@ namespace ARMeilleure.IntermediateRepresentation
SpillArg,
StoreToContext
}
static class InstructionExtensions
{
public static bool IsShift(this Instruction inst)
{
switch (inst)
{
case Instruction.RotateRight:
case Instruction.ShiftLeft:
case Instruction.ShiftRightSI:
case Instruction.ShiftRightUI:
return true;
}
return false;
}
}
}

View file

@ -4,63 +4,129 @@ namespace ARMeilleure.IntermediateRepresentation
{
class Node
{
private Operand _dest;
private LinkedListNode<Node> _asgUseNode;
public Operand Dest
{
get
{
return _dest;
return _destinations.Length != 0 ? GetDestination(0) : null;
}
set
{
if (_dest != null && _dest.Kind == OperandKind.LocalVariable)
if (value != null)
{
_dest.Assignments.Remove(_asgUseNode);
SetDestinations(new Operand[] { value });
}
if (value != null && value.Kind == OperandKind.LocalVariable)
else
{
_asgUseNode = value.Assignments.AddLast(this);
SetDestinations(new Operand[0]);
}
_dest = value;
}
}
protected Operand[] Sources;
private Operand[] _destinations;
private Operand[] _sources;
public int SourcesCount => Sources.Length;
private LinkedListNode<Node>[] _asgUseNodes;
private LinkedListNode<Node>[] _srcUseNodes;
protected LinkedListNode<Node>[] SrcUseNodes;
public int DestinationsCount => _destinations.Length;
public int SourcesCount => _sources.Length;
public Node(int sourcesCount)
public Node(Operand destination, int sourcesCount)
{
SrcUseNodes = new LinkedListNode<Node>[sourcesCount];
Dest = destination;
_sources = new Operand[sourcesCount];
_srcUseNodes = new LinkedListNode<Node>[sourcesCount];
}
public Node(Operand[] destinations, int sourcesCount)
{
SetDestinations(destinations);
_sources = new Operand[sourcesCount];
_srcUseNodes = new LinkedListNode<Node>[sourcesCount];
}
public Operand GetDestination(int index)
{
return _destinations[index];
}
public Operand GetSource(int index)
{
return Sources[index];
return _sources[index];
}
public void SetDestination(int index, Operand destination)
{
Set(_destinations, _asgUseNodes, index, destination);
}
public void SetSource(int index, Operand source)
{
Operand oldSrc = Sources[index];
Set(_sources, _srcUseNodes, index, source);
}
if (oldSrc != null && oldSrc.Kind == OperandKind.LocalVariable)
private void Set(Operand[] ops, LinkedListNode<Node>[] uses, int index, Operand newOp)
{
Operand oldOp = ops[index];
if (oldOp != null && oldOp.Kind == OperandKind.LocalVariable)
{
oldSrc.Uses.Remove(SrcUseNodes[index]);
oldOp.Uses.Remove(uses[index]);
}
if (source != null && source.Kind == OperandKind.LocalVariable)
if (newOp != null && newOp.Kind == OperandKind.LocalVariable)
{
SrcUseNodes[index] = source.Uses.AddLast(this);
uses[index] = newOp.Uses.AddLast(this);
}
Sources[index] = source;
ops[index] = newOp;
}
public void SetDestinations(Operand[] destinations)
{
Set(ref _destinations, ref _asgUseNodes, destinations);
}
public void SetSources(Operand[] sources)
{
Set(ref _sources, ref _srcUseNodes, sources);
}
private void Set(ref Operand[] ops, ref LinkedListNode<Node>[] uses, Operand[] newOps)
{
if (ops != null)
{
for (int index = 0; index < ops.Length; index++)
{
Operand oldOp = ops[index];
if (oldOp != null && oldOp.Kind == OperandKind.LocalVariable)
{
oldOp.Uses.Remove(uses[index]);
}
}
}
ops = new Operand[newOps.Length];
uses = new LinkedListNode<Node>[newOps.Length];
for (int index = 0; index < newOps.Length; index++)
{
Operand newOp = newOps[index];
ops[index] = newOp;
if (newOp.Kind == OperandKind.LocalVariable)
{
uses[index] = newOp.Uses.AddLast(this);
}
}
}
}
}

View file

@ -6,22 +6,29 @@ namespace ARMeilleure.IntermediateRepresentation
{
public Instruction Inst { get; private set; }
public Operation(Instruction inst, Operand dest, params Operand[] sources) : base(sources.Length)
public Operation(
Instruction inst,
Operand destination,
params Operand[] sources) : base(destination, sources.Length)
{
Inst = inst;
Dest = dest;
//The array may be modified externally, so we store a copy.
Sources = (Operand[])sources.Clone();
for (int index = 0; index < Sources.Length; index++)
for (int index = 0; index < sources.Length; index++)
{
Operand source = Sources[index];
SetSource(index, sources[index]);
}
}
if (source.Kind == OperandKind.LocalVariable)
{
SrcUseNodes[index] = source.Uses.AddLast(this);
}
public Operation(
Instruction inst,
Operand[] destinations,
Operand[] sources) : base(destinations, sources.Length)
{
Inst = inst;
for (int index = 0; index < sources.Length; index++)
{
SetSource(index, sources[index]);
}
}
@ -29,24 +36,7 @@ namespace ARMeilleure.IntermediateRepresentation
{
Inst = Instruction.Copy;
for (int index = 0; index < Sources.Length; index++)
{
if (Sources[index].Kind != OperandKind.LocalVariable)
{
continue;
}
Sources[index].Uses.Remove(SrcUseNodes[index]);
}
Sources = new Operand[] { source };
SrcUseNodes = new LinkedListNode<Node>[1];
if (source.Kind == OperandKind.LocalVariable)
{
SrcUseNodes[0] = source.Uses.AddLast(this);
}
SetSources(new Operand[] { source });
}
}
}

View file

@ -4,13 +4,9 @@ namespace ARMeilleure.IntermediateRepresentation
{
private BasicBlock[] _blocks;
public PhiNode(Operand dest, int predecessorsCount) : base(predecessorsCount)
public PhiNode(Operand destination, int predecessorsCount) : base(destination, predecessorsCount)
{
Sources = new Operand[predecessorsCount];
_blocks = new BasicBlock[predecessorsCount];
Dest = dest;
}
public BasicBlock GetBlock(int index)

View file

@ -3,6 +3,7 @@ using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory;
using ARMeilleure.State;
using System.Collections.Generic;
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
@ -10,6 +11,8 @@ namespace ARMeilleure.Translation
{
class ArmEmitterContext : EmitterContext
{
private Dictionary<ulong, Operand> _labels;
private OpCode _optOpLastCompare;
private OpCode _optOpLastFlagSet;
@ -42,6 +45,20 @@ namespace ARMeilleure.Translation
{
Memory = memory;
Mode = mode;
_labels = new Dictionary<ulong, Operand>();
}
public Operand GetLabel(ulong address)
{
if (!_labels.TryGetValue(address, out Operand label))
{
label = Label();
_labels.Add(address, label);
}
return label;
}
public void MarkComparison(Operand n, Operand m)

View file

@ -10,8 +10,6 @@ namespace ARMeilleure.Translation
{
class EmitterContext
{
private Dictionary<ulong, Operand> _labels;
private Dictionary<Operand, BasicBlock> _irLabels;
private LinkedList<BasicBlock> _irBlocks;
@ -22,8 +20,6 @@ namespace ARMeilleure.Translation
public EmitterContext()
{
_labels = new Dictionary<ulong, Operand>();
_irLabels = new Dictionary<Operand, BasicBlock>();
_irBlocks = new LinkedList<BasicBlock>();
@ -94,37 +90,34 @@ namespace ARMeilleure.Translation
return Call(Const(ptr.ToInt64()), returnType, callArgs);
}
private static Dictionary<TypeCode, OperandType> _typeCodeToOperandTypeMap =
new Dictionary<TypeCode, OperandType>()
{
{ TypeCode.Boolean, OperandType.I32 },
{ TypeCode.Byte, OperandType.I32 },
{ TypeCode.Char, OperandType.I32 },
{ TypeCode.Double, OperandType.FP64 },
{ TypeCode.Int16, OperandType.I32 },
{ TypeCode.Int32, OperandType.I32 },
{ TypeCode.Int64, OperandType.I64 },
{ TypeCode.SByte, OperandType.I32 },
{ TypeCode.Single, OperandType.FP32 },
{ TypeCode.UInt16, OperandType.I32 },
{ TypeCode.UInt32, OperandType.I32 },
{ TypeCode.UInt64, OperandType.I64 }
};
private static OperandType GetOperandType(Type type)
{
switch (Type.GetTypeCode(type))
if (_typeCodeToOperandTypeMap.TryGetValue(Type.GetTypeCode(type), out OperandType ot))
{
case TypeCode.Boolean:
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.UInt16:
case TypeCode.Int16:
case TypeCode.UInt32:
case TypeCode.Int32:
return OperandType.I32;
case TypeCode.UInt64:
case TypeCode.Int64:
return OperandType.I64;
case TypeCode.Single:
return OperandType.FP32;
case TypeCode.Double:
return OperandType.FP64;
return ot;
}
if (type == typeof(V128))
else if (type == typeof(V128))
{
return OperandType.V128;
}
if (type == typeof(void))
else if (type == typeof(void))
{
return OperandType.None;
}
@ -527,18 +520,6 @@ namespace ARMeilleure.Translation
}
}
public Operand GetLabel(ulong address)
{
if (!_labels.TryGetValue(address, out Operand label))
{
label = Label();
_labels.Add(address, label);
}
return label;
}
private void NewNextBlock()
{
BasicBlock block = new BasicBlock(_irBlocks.Count);