Add a faster linear scan allocator, unwinding support on windows, and other changes
This commit is contained in:
parent
4f38a65509
commit
4069fa8e33
39 changed files with 1734 additions and 579 deletions
17
ARMeilleure/CodeGen/CompiledFunction.cs
Normal file
17
ARMeilleure/CodeGen/CompiledFunction.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using ARMeilleure.CodeGen.Unwinding;
|
||||
|
||||
namespace ARMeilleure.CodeGen
|
||||
{
|
||||
struct CompiledFunction
|
||||
{
|
||||
public byte[] Code { get; }
|
||||
|
||||
public UnwindInfo UnwindInfo { get; }
|
||||
|
||||
public CompiledFunction(byte[] code, UnwindInfo unwindInfo)
|
||||
{
|
||||
Code = code;
|
||||
UnwindInfo = unwindInfo;
|
||||
}
|
||||
}
|
||||
}
|
568
ARMeilleure/CodeGen/RegisterAllocators/FastLinearScan.cs
Normal file
568
ARMeilleure/CodeGen/RegisterAllocators/FastLinearScan.cs
Normal file
|
@ -0,0 +1,568 @@
|
|||
using ARMeilleure.Common;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
using static ARMeilleure.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
class FastLinearScan
|
||||
{
|
||||
private const int InstructionGap = 2;
|
||||
|
||||
private const int RegistersCount = 16;
|
||||
|
||||
private const int MaxIROperands = 4;
|
||||
|
||||
private class OperationInfo
|
||||
{
|
||||
public LinkedListNode<Node> Node { get; }
|
||||
|
||||
public int IntSpillUsedRegisters { get; set; }
|
||||
public int VecSpillUsedRegisters { get; set; }
|
||||
|
||||
public OperationInfo(LinkedListNode<Node> node)
|
||||
{
|
||||
Node = node;
|
||||
}
|
||||
}
|
||||
|
||||
private List<OperationInfo> _operationNodes;
|
||||
|
||||
private int _intSpillTemps;
|
||||
private int _vecSpillTemps;
|
||||
|
||||
private List<LiveInterval> _intervals;
|
||||
|
||||
private class CompareIntervalsEnd : IComparer<LiveInterval>
|
||||
{
|
||||
public int Compare(LiveInterval lhs, LiveInterval rhs)
|
||||
{
|
||||
if (lhs.GetEnd() == rhs.GetEnd())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (lhs.GetEnd() < rhs.GetEnd())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AllocationResult RunPass(ControlFlowGraph cfg, StackAllocator stackAlloc, RegisterMasks regMasks)
|
||||
{
|
||||
PhiFunctions.Remove(cfg);
|
||||
|
||||
BuildIntervals(cfg, regMasks);
|
||||
|
||||
List<LiveInterval>[] fixedIntervals = new List<LiveInterval>[2];
|
||||
|
||||
fixedIntervals[0] = new List<LiveInterval>();
|
||||
fixedIntervals[1] = new List<LiveInterval>();
|
||||
|
||||
for (int index = 0; index < RegistersCount * 2; index++)
|
||||
{
|
||||
LiveInterval interval = _intervals[index];
|
||||
|
||||
if (!interval.IsEmpty)
|
||||
{
|
||||
InsertSorted(fixedIntervals[index & 1], interval);
|
||||
}
|
||||
}
|
||||
|
||||
List<LiveInterval> activeIntervals = new List<LiveInterval>();
|
||||
|
||||
CompareIntervalsEnd comparer = new CompareIntervalsEnd();
|
||||
|
||||
int intFreeRegisters = regMasks.IntAvailableRegisters;
|
||||
int vecFreeRegisters = regMasks.VecAvailableRegisters;
|
||||
|
||||
int intUsedRegisters = 0;
|
||||
int vecUsedRegisters = 0;
|
||||
|
||||
intFreeRegisters = ReserveSpillTemps(ref _intSpillTemps, intFreeRegisters);
|
||||
vecFreeRegisters = ReserveSpillTemps(ref _vecSpillTemps, vecFreeRegisters);
|
||||
|
||||
for (int index = RegistersCount * 2; index < _intervals.Count; index++)
|
||||
{
|
||||
LiveInterval current = _intervals[index];
|
||||
|
||||
while (activeIntervals.Count != 0 &&
|
||||
activeIntervals[activeIntervals.Count - 1].GetEnd() < current.GetStart())
|
||||
{
|
||||
int iIndex = activeIntervals.Count - 1;
|
||||
|
||||
LiveInterval interval = activeIntervals[iIndex];
|
||||
|
||||
if (interval.Register.Type == RegisterType.Integer)
|
||||
{
|
||||
intFreeRegisters |= 1 << interval.Register.Index;
|
||||
}
|
||||
else /* if (interval.Register.Type == RegisterType.Vector) */
|
||||
{
|
||||
vecFreeRegisters |= 1 << interval.Register.Index;
|
||||
}
|
||||
|
||||
activeIntervals.RemoveAt(iIndex);
|
||||
}
|
||||
|
||||
Operand local = current.Local;
|
||||
|
||||
bool localIsInteger = local.Type.IsInteger();
|
||||
|
||||
int freeMask = localIsInteger ? intFreeRegisters : vecFreeRegisters;
|
||||
|
||||
if (freeMask != 0)
|
||||
{
|
||||
List<LiveInterval> fixedIntervalsForType = fixedIntervals[localIsInteger ? 0 : 1];
|
||||
|
||||
for (int iIndex = 0; iIndex < fixedIntervalsForType.Count; iIndex++)
|
||||
{
|
||||
LiveInterval interval = fixedIntervalsForType[iIndex];
|
||||
|
||||
if (interval.GetStart() >= current.GetEnd())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (interval.Overlaps(current))
|
||||
{
|
||||
freeMask &= ~(1 << interval.Register.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (freeMask != 0)
|
||||
{
|
||||
int selectedReg = BitUtils.LowestBitSet(freeMask);
|
||||
|
||||
current.Register = new Register(selectedReg, local.Type.ToRegisterType());
|
||||
|
||||
int regMask = 1 << selectedReg;
|
||||
|
||||
if (localIsInteger)
|
||||
{
|
||||
intUsedRegisters |= regMask;
|
||||
intFreeRegisters &= ~regMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
vecUsedRegisters |= regMask;
|
||||
vecFreeRegisters &= ~regMask;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Spill the interval that will free the register for the longest
|
||||
//amount of time, as long there's no interference of the current
|
||||
//interval with a fixed interval using the same register.
|
||||
bool hasRegisterSelected = false;
|
||||
|
||||
RegisterType regType = current.Local.Type.ToRegisterType();
|
||||
|
||||
for (int iIndex = 0; iIndex < activeIntervals.Count; iIndex++)
|
||||
{
|
||||
LiveInterval spillCandidate = activeIntervals[iIndex];
|
||||
|
||||
if (spillCandidate.Register.Type != regType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
LiveInterval fixedInterval = _intervals[GetRegisterId(spillCandidate.Register)];
|
||||
|
||||
if (fixedInterval.IsEmpty || !fixedInterval.Overlaps(current))
|
||||
{
|
||||
current.Register = spillCandidate.Register;
|
||||
|
||||
spillCandidate.Spill(stackAlloc.Allocate(spillCandidate.Local.Type));
|
||||
|
||||
activeIntervals.RemoveAt(iIndex);
|
||||
|
||||
hasRegisterSelected = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(hasRegisterSelected, "Failure allocating register with spill.");
|
||||
}
|
||||
|
||||
InsertSorted(activeIntervals, current, comparer);
|
||||
}
|
||||
|
||||
for (int index = RegistersCount * 2; index < _intervals.Count; index++)
|
||||
{
|
||||
LiveInterval interval = _intervals[index];
|
||||
|
||||
if (interval.IsSpilled)
|
||||
{
|
||||
ReplaceLocalWithSpill(interval, ref intUsedRegisters, ref vecUsedRegisters);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReplaceLocalWithRegister(interval);
|
||||
}
|
||||
}
|
||||
|
||||
return new AllocationResult(intUsedRegisters, vecUsedRegisters, stackAlloc.TotalSize);
|
||||
}
|
||||
|
||||
private int ReserveSpillTemps(ref int tempsMask, int availableRegs)
|
||||
{
|
||||
for (int index = 0; index < MaxIROperands; index++)
|
||||
{
|
||||
int selectedReg = BitUtils.HighestBitSet(availableRegs);
|
||||
|
||||
tempsMask |= 1 << selectedReg;
|
||||
|
||||
availableRegs &= ~(1 << selectedReg);
|
||||
}
|
||||
|
||||
return availableRegs;
|
||||
}
|
||||
|
||||
private void ReplaceLocalWithRegister(LiveInterval interval)
|
||||
{
|
||||
Operand register = GetRegister(interval);
|
||||
|
||||
foreach (int usePosition in interval.UsePositions())
|
||||
{
|
||||
Node operation = GetOperationInfo(usePosition).Node.Value;
|
||||
|
||||
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
|
||||
{
|
||||
Operand source = operation.GetSource(srcIndex);
|
||||
|
||||
if (source == interval.Local)
|
||||
{
|
||||
operation.SetSource(srcIndex, register);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation.Dest == interval.Local)
|
||||
{
|
||||
operation.Dest = register;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand GetRegister(LiveInterval interval)
|
||||
{
|
||||
Debug.Assert(!interval.IsSpilled, "Spilled intervals are not allowed.");
|
||||
|
||||
return new Operand(
|
||||
interval.Register.Index,
|
||||
interval.Register.Type,
|
||||
interval.Local.Type);
|
||||
}
|
||||
|
||||
private void ReplaceLocalWithSpill(
|
||||
LiveInterval interval,
|
||||
ref int intUsedRegisters,
|
||||
ref int vecUsedRegisters)
|
||||
{
|
||||
Operand local = interval.Local;
|
||||
|
||||
int spillOffset = interval.SpillOffset;
|
||||
|
||||
foreach (int usePosition in interval.UsePositions())
|
||||
{
|
||||
OperationInfo info = GetOperationInfo(usePosition);
|
||||
|
||||
int tempReg = GetSpillTemp(info, local.Type);
|
||||
|
||||
if (local.Type.IsInteger())
|
||||
{
|
||||
intUsedRegisters |= 1 << tempReg;
|
||||
}
|
||||
else
|
||||
{
|
||||
vecUsedRegisters |= 1 << tempReg;
|
||||
}
|
||||
|
||||
Operand temp = new Operand(tempReg, local.Type.ToRegisterType(), local.Type);
|
||||
|
||||
LinkedListNode<Node> node = info.Node;
|
||||
|
||||
Node operation = node.Value;
|
||||
|
||||
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
|
||||
{
|
||||
Operand source = operation.GetSource(srcIndex);
|
||||
|
||||
if (source == local)
|
||||
{
|
||||
Operation fillOp = new Operation(Instruction.Fill, temp, Const(spillOffset));
|
||||
|
||||
node.List.AddBefore(node, fillOp);
|
||||
|
||||
operation.SetSource(srcIndex, temp);
|
||||
}
|
||||
}
|
||||
|
||||
if (operation.Dest == local)
|
||||
{
|
||||
Operation spillOp = new Operation(Instruction.Spill, null, Const(spillOffset), temp);
|
||||
|
||||
node.List.AddAfter(node, spillOp);
|
||||
|
||||
operation.Dest = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OperationInfo GetOperationInfo(int position)
|
||||
{
|
||||
return _operationNodes[position / InstructionGap];
|
||||
}
|
||||
|
||||
private int GetSpillTemp(OperationInfo info, OperandType type)
|
||||
{
|
||||
int selectedReg;
|
||||
|
||||
if (type.IsInteger())
|
||||
{
|
||||
selectedReg = BitUtils.LowestBitSet(_intSpillTemps & ~info.IntSpillUsedRegisters);
|
||||
|
||||
info.IntSpillUsedRegisters |= 1 << selectedReg;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedReg = BitUtils.LowestBitSet(_vecSpillTemps & ~info.VecSpillUsedRegisters);
|
||||
|
||||
info.VecSpillUsedRegisters |= 1 << selectedReg;
|
||||
}
|
||||
|
||||
Debug.Assert(selectedReg != -1, "Out of spill temporary registers. " + (info.Node.Value as Operation).Inst);
|
||||
|
||||
return selectedReg;
|
||||
}
|
||||
|
||||
private static void InsertSorted(
|
||||
List<LiveInterval> list,
|
||||
LiveInterval interval,
|
||||
IComparer<LiveInterval> comparer = null)
|
||||
{
|
||||
int insertIndex = list.BinarySearch(interval, comparer);
|
||||
|
||||
if (insertIndex < 0)
|
||||
{
|
||||
insertIndex = ~insertIndex;
|
||||
}
|
||||
|
||||
list.Insert(insertIndex, interval);
|
||||
}
|
||||
|
||||
private void BuildIntervals(ControlFlowGraph cfg, RegisterMasks masks)
|
||||
{
|
||||
_operationNodes = new List<OperationInfo>();
|
||||
|
||||
_intervals = new List<LiveInterval>();
|
||||
|
||||
for (int index = 0; index < RegistersCount; index++)
|
||||
{
|
||||
_intervals.Add(new LiveInterval(new Register(index, RegisterType.Integer)));
|
||||
_intervals.Add(new LiveInterval(new Register(index, RegisterType.Vector)));
|
||||
}
|
||||
|
||||
HashSet<Operand> visited = new HashSet<Operand>();
|
||||
|
||||
LiveInterval GetOrAddInterval(Operand operand)
|
||||
{
|
||||
LiveInterval interval;
|
||||
|
||||
if (visited.Add(operand))
|
||||
{
|
||||
operand.NumberLocal(_intervals.Count);
|
||||
|
||||
interval = new LiveInterval(operand);
|
||||
|
||||
_intervals.Add(interval);
|
||||
}
|
||||
else
|
||||
{
|
||||
interval = _intervals[GetOperandId(operand)];
|
||||
}
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
int[] blockStarts = new int[cfg.Blocks.Count];
|
||||
|
||||
int operationPos = 0;
|
||||
|
||||
List<LiveRange> backwardsBranches = new List<LiveRange>();
|
||||
|
||||
for (int index = cfg.PostOrderBlocks.Length - 1; index >= 0; index--)
|
||||
{
|
||||
BasicBlock block = cfg.PostOrderBlocks[index];
|
||||
|
||||
blockStarts[block.Index] = operationPos;
|
||||
|
||||
for (LinkedListNode<Node> node = block.Operations.First; node != null; node = node.Next)
|
||||
{
|
||||
_operationNodes.Add(new OperationInfo(node));
|
||||
|
||||
Operation operation = node.Value as Operation;
|
||||
|
||||
Operand dest = operation.Dest;
|
||||
|
||||
if (dest != null)
|
||||
{
|
||||
if (dest.Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
LiveInterval interval = GetOrAddInterval(dest);
|
||||
|
||||
if (interval.IsEmpty)
|
||||
{
|
||||
interval.SetStart(operationPos + 1);
|
||||
}
|
||||
|
||||
interval.AddUsePosition(operationPos);
|
||||
}
|
||||
else if (dest.Kind == OperandKind.Register)
|
||||
{
|
||||
int iIndex = GetRegisterId(dest.GetRegister());
|
||||
|
||||
_intervals[iIndex].AddRange(operationPos + 1, operationPos + InstructionGap);
|
||||
}
|
||||
}
|
||||
|
||||
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
|
||||
{
|
||||
Operand source = operation.GetSource(srcIndex);
|
||||
|
||||
if (source.Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
LiveInterval interval = GetOrAddInterval(source);
|
||||
|
||||
Debug.Assert(!interval.IsEmpty, "Interval is empty.");
|
||||
|
||||
interval.SetEnd(operationPos + 1);
|
||||
interval.AddUsePosition(operationPos);
|
||||
}
|
||||
else if (source.Kind == OperandKind.Register)
|
||||
{
|
||||
int iIndex = GetRegisterId(source.GetRegister());
|
||||
|
||||
LiveInterval interval = _intervals[iIndex];
|
||||
|
||||
if (interval.IsEmpty)
|
||||
{
|
||||
interval.SetStart(operationPos + 1);
|
||||
}
|
||||
else if (interval.GetEnd() < operationPos + 1)
|
||||
{
|
||||
interval.SetEnd(operationPos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (operation.Inst == Instruction.Call)
|
||||
{
|
||||
AddIntervalCallerSavedReg(masks.IntCallerSavedRegisters, operationPos, RegisterType.Integer);
|
||||
AddIntervalCallerSavedReg(masks.VecCallerSavedRegisters, operationPos, RegisterType.Vector);
|
||||
}
|
||||
|
||||
operationPos += InstructionGap;
|
||||
}
|
||||
|
||||
foreach (BasicBlock successor in Successors(block))
|
||||
{
|
||||
int branchIndex = cfg.PostOrderMap[block.Index];
|
||||
int targetIndex = cfg.PostOrderMap[successor.Index];
|
||||
|
||||
//Is the branch jumping backwards?
|
||||
if (targetIndex >= branchIndex)
|
||||
{
|
||||
int targetPos = blockStarts[successor.Index];
|
||||
|
||||
backwardsBranches.Add(new LiveRange(targetPos, operationPos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (LiveRange backwardBranch in backwardsBranches)
|
||||
{
|
||||
for (int iIndex = RegistersCount * 2; iIndex < _intervals.Count; iIndex++)
|
||||
{
|
||||
LiveInterval interval = _intervals[iIndex];
|
||||
|
||||
int start = interval.GetStart();
|
||||
int end = interval.GetEnd();
|
||||
|
||||
if (backwardBranch.Start >= start && backwardBranch.Start < end)
|
||||
{
|
||||
if (interval.GetEnd() < backwardBranch.End)
|
||||
{
|
||||
interval.SetEnd(backwardBranch.End);
|
||||
}
|
||||
}
|
||||
|
||||
if (start > backwardBranch.Start)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddIntervalCallerSavedReg(int mask, int operationPos, RegisterType regType)
|
||||
{
|
||||
while (mask != 0)
|
||||
{
|
||||
int regIndex = BitUtils.LowestBitSet(mask);
|
||||
|
||||
Register callerSavedReg = new Register(regIndex, regType);
|
||||
|
||||
int rIndex = GetRegisterId(callerSavedReg);
|
||||
|
||||
_intervals[rIndex].AddRange(operationPos + 1, operationPos + InstructionGap);
|
||||
|
||||
mask &= ~(1 << regIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetOperandId(Operand operand)
|
||||
{
|
||||
if (operand.Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
return operand.AsInt32();
|
||||
}
|
||||
else if (operand.Kind == OperandKind.Register)
|
||||
{
|
||||
return GetRegisterId(operand.GetRegister());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Invalid operand kind \"{operand.Kind}\".");
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetRegisterId(Register register)
|
||||
{
|
||||
return (register.Index << 1) | (register.Type == RegisterType.Vector ? 1 : 0);
|
||||
}
|
||||
|
||||
private static IEnumerable<BasicBlock> Successors(BasicBlock block)
|
||||
{
|
||||
if (block.Next != null)
|
||||
{
|
||||
yield return block.Next;
|
||||
}
|
||||
|
||||
if (block.Branch != null)
|
||||
{
|
||||
yield return block.Branch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,11 +41,10 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||
public int IntUsedRegisters { get; set; }
|
||||
public int VecUsedRegisters { get; set; }
|
||||
|
||||
public AllocationContext(RegisterMasks masks, int intervalsCount)
|
||||
public AllocationContext(StackAllocator stackAlloc, RegisterMasks masks, int intervalsCount)
|
||||
{
|
||||
Masks = masks;
|
||||
|
||||
StackAlloc = new StackAllocator();
|
||||
StackAlloc = stackAlloc;
|
||||
Masks = masks;
|
||||
|
||||
Active = new BitMap(intervalsCount);
|
||||
Inactive = new BitMap(intervalsCount);
|
||||
|
@ -69,13 +68,13 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||
}
|
||||
}
|
||||
|
||||
public AllocationResult RunPass(ControlFlowGraph cfg, RegisterMasks regMasks)
|
||||
public AllocationResult RunPass(ControlFlowGraph cfg, StackAllocator stackAlloc, RegisterMasks regMasks)
|
||||
{
|
||||
PhiFunctions.Remove(cfg);
|
||||
|
||||
NumberLocals(cfg);
|
||||
|
||||
AllocationContext context = new AllocationContext(regMasks, _intervals.Count);
|
||||
AllocationContext context = new AllocationContext(stackAlloc, regMasks, _intervals.Count);
|
||||
|
||||
BuildIntervals(cfg, context);
|
||||
|
||||
|
@ -95,10 +94,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!current.HasRegister)
|
||||
{
|
||||
AllocateInterval(context, current, index);
|
||||
}
|
||||
AllocateInterval(context, current, index);
|
||||
}
|
||||
|
||||
for (int index = RegistersCount * 2; index < _intervals.Count; index++)
|
||||
|
@ -202,7 +198,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||
int selectedNextUse = freePositions[selectedReg];
|
||||
|
||||
//Intervals starts and ends at odd positions, unless they span an entire
|
||||
//block, in this case they will have ranges at even position.
|
||||
//block, in this case they will have ranges at a even position.
|
||||
//When a interval is loaded from the stack to a register, we can only
|
||||
//do the split at a odd position, because otherwise the split interval
|
||||
//that is inserted on the list to be processed may clobber a register
|
||||
|
@ -907,23 +903,10 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||
interval.AddUsePosition(operationPos);
|
||||
}
|
||||
|
||||
if (node is Operation operation)
|
||||
if (node is Operation operation && operation.Inst == Instruction.Call)
|
||||
{
|
||||
if (operation.Inst == Instruction.Call)
|
||||
{
|
||||
AddIntervalCallerSavedReg(context.Masks.IntCallerSavedRegisters, operationPos, RegisterType.Integer);
|
||||
AddIntervalCallerSavedReg(context.Masks.VecCallerSavedRegisters, operationPos, RegisterType.Vector);
|
||||
}
|
||||
else if (operation.Inst == Instruction.StackAlloc)
|
||||
{
|
||||
Operand offset = operation.GetSource(0);
|
||||
|
||||
Debug.Assert(offset.Kind == OperandKind.Constant, "StackAlloc has non-constant size.");
|
||||
|
||||
int spillOffset = context.StackAlloc.Allocate(offset.AsInt32());
|
||||
|
||||
operation.SetSource(0, new Operand(spillOffset));
|
||||
}
|
||||
AddIntervalCallerSavedReg(context.Masks.IntCallerSavedRegisters, operationPos, RegisterType.Integer);
|
||||
AddIntervalCallerSavedReg(context.Masks.VecCallerSavedRegisters, operationPos, RegisterType.Vector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -935,8 +918,6 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||
{
|
||||
int regIndex = BitUtils.LowestBitSet(mask);
|
||||
|
||||
Debug.Assert(regIndex < RegistersCount, "Invalid register index.");
|
||||
|
||||
Register callerSavedReg = new Register(regIndex, regType);
|
||||
|
||||
LiveInterval interval = _intervals[GetRegisterId(callerSavedReg)];
|
||||
|
|
|
@ -24,23 +24,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||
|
||||
public Operand Local { get; }
|
||||
|
||||
private Register _register;
|
||||
|
||||
public bool HasRegister { get; private set; }
|
||||
|
||||
public Register Register
|
||||
{
|
||||
get
|
||||
{
|
||||
return _register;
|
||||
}
|
||||
set
|
||||
{
|
||||
_register = value;
|
||||
|
||||
HasRegister = true;
|
||||
}
|
||||
}
|
||||
public Register Register { get; set; }
|
||||
|
||||
public int SpillOffset { get; private set; }
|
||||
|
||||
|
@ -93,6 +77,22 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||
return _ranges[0].Start;
|
||||
}
|
||||
|
||||
public void SetEnd(int position)
|
||||
{
|
||||
if (_ranges.Count != 0)
|
||||
{
|
||||
int lastIdx = _ranges.Count - 1;
|
||||
|
||||
Debug.Assert(position != _ranges[lastIdx].Start);
|
||||
|
||||
_ranges[lastIdx] = new LiveRange(_ranges[lastIdx].Start, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ranges.Add(new LiveRange(position, position + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public int GetEnd()
|
||||
{
|
||||
if (_ranges.Count == 0)
|
||||
|
@ -103,6 +103,35 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||
return _ranges[_ranges.Count - 1].End;
|
||||
}
|
||||
|
||||
public void Expand(int position)
|
||||
{
|
||||
Expand(position, position + 1);
|
||||
|
||||
_usePositions.Add(position);
|
||||
}
|
||||
|
||||
public void Expand(int start, int end)
|
||||
{
|
||||
if (_ranges.Count == 0)
|
||||
{
|
||||
_ranges.Add(new LiveRange(start, end));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int lastIdx = _ranges.Count - 1;
|
||||
|
||||
if (_ranges[0].Start > start)
|
||||
{
|
||||
_ranges[0] = new LiveRange(start, _ranges[0].End);
|
||||
}
|
||||
|
||||
if (_ranges[lastIdx].End < end)
|
||||
{
|
||||
_ranges[lastIdx] = new LiveRange(_ranges[lastIdx].Start, end);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(int start, int end)
|
||||
{
|
||||
if (start >= end)
|
||||
|
|
18
ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs
Normal file
18
ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace ARMeilleure.CodeGen.Unwinding
|
||||
{
|
||||
struct UnwindInfo
|
||||
{
|
||||
public UnwindPushEntry[] PushEntries { get; }
|
||||
|
||||
public int PrologueSize { get; }
|
||||
|
||||
public int FixedAllocSize { get; }
|
||||
|
||||
public UnwindInfo(UnwindPushEntry[] pushEntries, int prologueSize, int fixedAllocSize)
|
||||
{
|
||||
PushEntries = pushEntries;
|
||||
PrologueSize = prologueSize;
|
||||
FixedAllocSize = fixedAllocSize;
|
||||
}
|
||||
}
|
||||
}
|
20
ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs
Normal file
20
ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
|
||||
namespace ARMeilleure.CodeGen.Unwinding
|
||||
{
|
||||
struct UnwindPushEntry
|
||||
{
|
||||
public int Index { get; }
|
||||
|
||||
public RegisterType Type { get; }
|
||||
|
||||
public int StreamEndOffset { get; }
|
||||
|
||||
public UnwindPushEntry(int index, RegisterType type, int streamEndOffset)
|
||||
{
|
||||
Index = index;
|
||||
Type = type;
|
||||
StreamEndOffset = streamEndOffset;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
private const int OpModRMBits = 24;
|
||||
|
||||
private const byte LockPrefix = 0xf0;
|
||||
|
||||
[Flags]
|
||||
private enum InstFlags
|
||||
{
|
||||
|
@ -82,6 +84,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Add(X86Instruction.Cmpps, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstFlags.Vex | InstFlags.NoRexW));
|
||||
Add(X86Instruction.Cmpsd, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstFlags.Vex | InstFlags.NoRexW | InstFlags.PrefixF2));
|
||||
Add(X86Instruction.Cmpss, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fc2, InstFlags.Vex | InstFlags.NoRexW | InstFlags.PrefixF3));
|
||||
Add(X86Instruction.Cmpxchg8b, new InstInfo(0x01000fc7, BadOp, BadOp, BadOp, BadOp, InstFlags.NoRexW));
|
||||
Add(X86Instruction.Cmpxchg16b, new InstInfo(0x01000fc7, BadOp, BadOp, BadOp, BadOp, InstFlags.RexW));
|
||||
Add(X86Instruction.Comisd, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2f, InstFlags.Vex | InstFlags.NoRexW | InstFlags.Prefix66));
|
||||
Add(X86Instruction.Comiss, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x00000f2f, InstFlags.Vex | InstFlags.NoRexW));
|
||||
Add(X86Instruction.Cvtdq2pd, new InstInfo(BadOp, BadOp, BadOp, BadOp, 0x00000fe6, InstFlags.Vex | InstFlags.NoRexW | InstFlags.PrefixF3));
|
||||
|
@ -360,6 +364,20 @@ namespace ARMeilleure.CodeGen.X86
|
|||
WriteByte(imm);
|
||||
}
|
||||
|
||||
public void Cmpxchg16b(X86MemoryOperand memOp)
|
||||
{
|
||||
WriteByte(LockPrefix);
|
||||
|
||||
WriteInstruction(memOp, null, X86Instruction.Cmpxchg16b);
|
||||
}
|
||||
|
||||
public void Cmpxchg8b(X86MemoryOperand memOp)
|
||||
{
|
||||
WriteByte(LockPrefix);
|
||||
|
||||
WriteInstruction(memOp, null, X86Instruction.Cmpxchg8b);
|
||||
}
|
||||
|
||||
public void Comisd(Operand source1, Operand source2)
|
||||
{
|
||||
WriteInstruction(source1, source2, X86Instruction.Comisd);
|
||||
|
|
|
@ -8,12 +8,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
public static int GetIntAvailableRegisters()
|
||||
{
|
||||
int mask = RegistersMask;
|
||||
|
||||
mask &= ~(1 << (int)X86Register.Rbp);
|
||||
mask &= ~(1 << (int)X86Register.Rsp);
|
||||
|
||||
return mask;
|
||||
return RegistersMask & ~(1 << (int)X86Register.Rsp);
|
||||
}
|
||||
|
||||
public static int GetVecAvailableRegisters()
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
private Stream _stream;
|
||||
|
||||
public int StreamOffset => (int)_stream.Length;
|
||||
|
||||
public AllocationResult AllocResult { get; }
|
||||
|
||||
public Assembler Assembler { get; }
|
||||
|
@ -23,6 +25,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
public int VecCalleeSaveSize { get; }
|
||||
|
||||
private long[] _blockOffsets;
|
||||
|
||||
private struct Jump
|
||||
{
|
||||
public bool IsConditional { get; }
|
||||
|
@ -62,8 +66,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
private long[] _blockOffsets;
|
||||
|
||||
private List<Jump> _jumps;
|
||||
|
||||
private X86Condition _jNearCondition;
|
||||
|
@ -97,16 +99,20 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
vecCalleeSaveSize = BitUtils.CountBits(vecMask) * 16;
|
||||
|
||||
intMask |= 1 << (int)X86Register.Rbp;
|
||||
|
||||
int calleeSaveRegionSize = BitUtils.CountBits(intMask) * 8 + vecCalleeSaveSize + 8;
|
||||
|
||||
int argsCount = maxCallArgs;
|
||||
|
||||
//The ABI mandates that the space for at least 4 arguments
|
||||
//is reserved on the stack (this is called shadow space).
|
||||
if (argsCount < 4 && maxCallArgs != -1)
|
||||
if (argsCount < 0)
|
||||
{
|
||||
//When the function has no calls, argsCount is -1.
|
||||
//In this case, we don't need to allocate the shadow space.
|
||||
argsCount = 0;
|
||||
}
|
||||
else if (argsCount < 4)
|
||||
{
|
||||
//The ABI mandates that the space for at least 4 arguments
|
||||
//is reserved on the stack (this is called shadow space).
|
||||
argsCount = 4;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
using ARMeilleure.CodeGen.Optimizations;
|
||||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.CodeGen.Unwinding;
|
||||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Diagnostics;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
|
@ -29,6 +30,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Add(Instruction.BranchIfTrue, GenerateBranchIfTrue);
|
||||
Add(Instruction.ByteSwap, GenerateByteSwap);
|
||||
Add(Instruction.Call, GenerateCall);
|
||||
Add(Instruction.CompareAndSwap128, GenerateCompareAndSwap128);
|
||||
Add(Instruction.CompareEqual, GenerateCompareEqual);
|
||||
Add(Instruction.CompareGreater, GenerateCompareGreater);
|
||||
Add(Instruction.CompareGreaterOrEqual, GenerateCompareGreaterOrEqual);
|
||||
|
@ -48,12 +50,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Add(Instruction.DivideUI, GenerateDivideUI);
|
||||
Add(Instruction.Fill, GenerateFill);
|
||||
Add(Instruction.Load, GenerateLoad);
|
||||
Add(Instruction.LoadFromContext, GenerateLoadFromContext);
|
||||
Add(Instruction.LoadSx16, GenerateLoadSx16);
|
||||
Add(Instruction.LoadSx32, GenerateLoadSx32);
|
||||
Add(Instruction.LoadSx8, GenerateLoadSx8);
|
||||
Add(Instruction.LoadZx16, GenerateLoadZx16);
|
||||
Add(Instruction.LoadZx8, GenerateLoadZx8);
|
||||
Add(Instruction.Load16, GenerateLoad16);
|
||||
Add(Instruction.Load8, GenerateLoad8);
|
||||
Add(Instruction.Multiply, GenerateMultiply);
|
||||
Add(Instruction.Multiply64HighSI, GenerateMultiply64HighSI);
|
||||
Add(Instruction.Multiply64HighUI, GenerateMultiply64HighUI);
|
||||
|
@ -72,7 +70,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Add(Instruction.Store, GenerateStore);
|
||||
Add(Instruction.Store16, GenerateStore16);
|
||||
Add(Instruction.Store8, GenerateStore8);
|
||||
Add(Instruction.StoreToContext, GenerateStoreToContext);
|
||||
Add(Instruction.Subtract, GenerateSubtract);
|
||||
Add(Instruction.VectorExtract, GenerateVectorExtract);
|
||||
Add(Instruction.VectorExtract16, GenerateVectorExtract16);
|
||||
|
@ -226,8 +223,10 @@ namespace ARMeilleure.CodeGen.X86
|
|||
_instTable[(int)inst] = func;
|
||||
}
|
||||
|
||||
public static byte[] Generate(ControlFlowGraph cfg, MemoryManager memory)
|
||||
public static CompiledFunction Generate(CompilerContext cctx)
|
||||
{
|
||||
ControlFlowGraph cfg = cctx.Cfg;
|
||||
|
||||
Logger.StartPass(PassName.Optimization);
|
||||
|
||||
Optimizer.RunPass(cfg);
|
||||
|
@ -236,13 +235,15 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
Logger.StartPass(PassName.PreAllocation);
|
||||
|
||||
PreAllocator.RunPass(cfg, memory, out int maxCallArgs);
|
||||
StackAllocator stackAlloc = new StackAllocator();
|
||||
|
||||
PreAllocator.RunPass(cctx, stackAlloc, out int maxCallArgs);
|
||||
|
||||
Logger.EndPass(PassName.PreAllocation, cfg);
|
||||
|
||||
Logger.StartPass(PassName.RegisterAllocation);
|
||||
|
||||
LinearScan regAlloc = new LinearScan();
|
||||
FastLinearScan regAlloc = new FastLinearScan();
|
||||
|
||||
RegisterMasks regMasks = new RegisterMasks(
|
||||
CallingConvention.GetIntAvailableRegisters(),
|
||||
|
@ -252,17 +253,17 @@ namespace ARMeilleure.CodeGen.X86
|
|||
CallingConvention.GetIntCalleeSavedRegisters(),
|
||||
CallingConvention.GetVecCalleeSavedRegisters());
|
||||
|
||||
AllocationResult allocResult = regAlloc.RunPass(cfg, regMasks);
|
||||
AllocationResult allocResult = regAlloc.RunPass(cfg, stackAlloc, regMasks);
|
||||
|
||||
Logger.EndPass(PassName.RegisterAllocation, cfg);
|
||||
|
||||
Logger.StartPass(PassName.CodeGeneration);
|
||||
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
CodeGenContext context = new CodeGenContext(stream, allocResult, maxCallArgs, cfg.Blocks.Count);
|
||||
|
||||
WritePrologue(context);
|
||||
|
||||
context.Assembler.Mov(Register(X86Register.Rbp), Register(X86Register.Rcx));
|
||||
UnwindInfo unwindInfo = WritePrologue(context);
|
||||
|
||||
foreach (BasicBlock block in cfg.Blocks)
|
||||
{
|
||||
|
@ -277,7 +278,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
return context.GetCode();
|
||||
Logger.EndPass(PassName.CodeGeneration);
|
||||
|
||||
return new CompiledFunction(context.GetCode(), unwindInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,6 +382,16 @@ namespace ARMeilleure.CodeGen.X86
|
|||
context.Assembler.Call(operation.GetSource(0));
|
||||
}
|
||||
|
||||
private static void GenerateCompareAndSwap128(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand dest = operation.Dest;
|
||||
Operand src1 = operation.GetSource(0);
|
||||
|
||||
X86MemoryOperand memOp = new X86MemoryOperand(OperandType.I64, src1, null, Scale.x1, 0);
|
||||
|
||||
context.Assembler.Cmpxchg16b(memOp);
|
||||
}
|
||||
|
||||
private static void GenerateCompareEqual(CodeGenContext context, Operation operation)
|
||||
{
|
||||
GenerateCompare(context, operation, X86Condition.Equal);
|
||||
|
@ -645,55 +658,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
private static void GenerateLoadFromContext(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand dest = operation.Dest;
|
||||
Operand offset = operation.GetSource(0);
|
||||
|
||||
if (offset.Kind != OperandKind.Constant)
|
||||
{
|
||||
throw new InvalidOperationException("LoadFromContext has non-constant context offset.");
|
||||
}
|
||||
|
||||
Operand rbp = Register(X86Register.Rbp);
|
||||
|
||||
X86MemoryOperand memOp = new X86MemoryOperand(dest.Type, rbp, null, Scale.x1, offset.AsInt32());
|
||||
|
||||
if (dest.GetRegister().Type == RegisterType.Vector)
|
||||
{
|
||||
context.Assembler.Movdqu(dest, memOp);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Assembler.Mov(dest, memOp);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateLoadSx16(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand value = operation.Dest;
|
||||
Operand address = GetMemoryOperand(operation.GetSource(0), value.Type);
|
||||
|
||||
context.Assembler.Movsx16(operation.Dest, address);
|
||||
}
|
||||
|
||||
private static void GenerateLoadSx32(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand value = operation.Dest;
|
||||
Operand address = GetMemoryOperand(operation.GetSource(0), value.Type);
|
||||
|
||||
context.Assembler.Movsx32(operation.Dest, address);
|
||||
}
|
||||
|
||||
private static void GenerateLoadSx8(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand value = operation.Dest;
|
||||
Operand address = GetMemoryOperand(operation.GetSource(0), value.Type);
|
||||
|
||||
context.Assembler.Movsx8(operation.Dest, address);
|
||||
}
|
||||
|
||||
private static void GenerateLoadZx16(CodeGenContext context, Operation operation)
|
||||
private static void GenerateLoad16(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand value = operation.Dest;
|
||||
Operand address = GetMemoryOperand(operation.GetSource(0), value.Type);
|
||||
|
@ -701,7 +666,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
context.Assembler.Movzx16(operation.Dest, address);
|
||||
}
|
||||
|
||||
private static void GenerateLoadZx8(CodeGenContext context, Operation operation)
|
||||
private static void GenerateLoad8(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand value = operation.Dest;
|
||||
Operand address = GetMemoryOperand(operation.GetSource(0), value.Type);
|
||||
|
@ -753,18 +718,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
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();
|
||||
|
@ -872,30 +825,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||
context.Assembler.Mov8(address, value);
|
||||
}
|
||||
|
||||
private static void GenerateStoreToContext(CodeGenContext context, Operation operation)
|
||||
{
|
||||
Operand offset = operation.GetSource(0);
|
||||
Operand source = operation.GetSource(1);
|
||||
|
||||
if (offset.Kind != OperandKind.Constant)
|
||||
{
|
||||
throw new InvalidOperationException("StoreToContext has non-constant context offset.");
|
||||
}
|
||||
|
||||
Operand rbp = Register(X86Register.Rbp);
|
||||
|
||||
X86MemoryOperand memOp = new X86MemoryOperand(source.Type, rbp, null, Scale.x1, offset.AsInt32());
|
||||
|
||||
if (source.GetRegister().Type == RegisterType.Vector)
|
||||
{
|
||||
context.Assembler.Movdqu(memOp, source);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Assembler.Mov(memOp, source);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateSubtract(CodeGenContext context, Operation operation)
|
||||
{
|
||||
ValidateDestSrc1(operation);
|
||||
|
@ -1157,36 +1086,42 @@ namespace ARMeilleure.CodeGen.X86
|
|||
{
|
||||
context.Assembler.Comisd(operation.GetSource(0), operation.GetSource(1));
|
||||
context.Assembler.Setcc(operation.Dest, X86Condition.Equal);
|
||||
context.Assembler.Movzx8(operation.Dest, operation.Dest);
|
||||
}
|
||||
|
||||
private static void GenerateX86Comisdge(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Comisd(operation.GetSource(0), operation.GetSource(1));
|
||||
context.Assembler.Setcc(operation.Dest, X86Condition.AboveOrEqual);
|
||||
context.Assembler.Movzx8(operation.Dest, operation.Dest);
|
||||
}
|
||||
|
||||
private static void GenerateX86Comisdlt(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Comisd(operation.GetSource(0), operation.GetSource(1));
|
||||
context.Assembler.Setcc(operation.Dest, X86Condition.Below);
|
||||
context.Assembler.Movzx8(operation.Dest, operation.Dest);
|
||||
}
|
||||
|
||||
private static void GenerateX86Comisseq(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Comiss(operation.GetSource(0), operation.GetSource(1));
|
||||
context.Assembler.Setcc(operation.Dest, X86Condition.Equal);
|
||||
context.Assembler.Movzx8(operation.Dest, operation.Dest);
|
||||
}
|
||||
|
||||
private static void GenerateX86Comissge(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Comiss(operation.GetSource(0), operation.GetSource(1));
|
||||
context.Assembler.Setcc(operation.Dest, X86Condition.AboveOrEqual);
|
||||
context.Assembler.Movzx8(operation.Dest, operation.Dest);
|
||||
}
|
||||
|
||||
private static void GenerateX86Comisslt(CodeGenContext context, Operation operation)
|
||||
{
|
||||
context.Assembler.Comiss(operation.GetSource(0), operation.GetSource(1));
|
||||
context.Assembler.Setcc(operation.Dest, X86Condition.Below);
|
||||
context.Assembler.Movzx8(operation.Dest, operation.Dest);
|
||||
}
|
||||
|
||||
private static void GenerateX86Cvtdq2pd(CodeGenContext context, Operation operation)
|
||||
|
@ -1785,6 +1720,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
{
|
||||
context.Assembler.Cmp(operation.GetSource(0), operation.GetSource(1));
|
||||
context.Assembler.Setcc(operation.Dest, condition);
|
||||
context.Assembler.Movzx8(operation.Dest, operation.Dest);
|
||||
}
|
||||
|
||||
private static void GenerateSpill(CodeGenContext context, Operation operation, int baseOffset)
|
||||
|
@ -1840,11 +1776,11 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
private static void WritePrologue(CodeGenContext context)
|
||||
private static UnwindInfo WritePrologue(CodeGenContext context)
|
||||
{
|
||||
int mask = CallingConvention.GetIntCalleeSavedRegisters() & context.AllocResult.IntUsedRegisters;
|
||||
List<UnwindPushEntry> pushEntries = new List<UnwindPushEntry>();
|
||||
|
||||
mask |= 1 << (int)X86Register.Rbp;
|
||||
int mask = CallingConvention.GetIntCalleeSavedRegisters() & context.AllocResult.IntUsedRegisters;
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
|
@ -1852,6 +1788,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
context.Assembler.Push(Register((X86Register)bit));
|
||||
|
||||
pushEntries.Add(new UnwindPushEntry(bit, RegisterType.Integer, context.StreamOffset));
|
||||
|
||||
mask &= ~(1 << bit);
|
||||
}
|
||||
|
||||
|
@ -1869,6 +1807,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
context.Assembler.Movdqu(memOp, Xmm((X86Register)bit));
|
||||
|
||||
pushEntries.Add(new UnwindPushEntry(bit, RegisterType.Vector, context.StreamOffset));
|
||||
|
||||
mask &= ~(1 << bit);
|
||||
}
|
||||
|
||||
|
@ -1880,6 +1820,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
{
|
||||
context.Assembler.Sub(Register(X86Register.Rsp), new Operand(reservedStackSize));
|
||||
}
|
||||
|
||||
return new UnwindInfo(pushEntries.ToArray(), context.StreamOffset, reservedStackSize);
|
||||
}
|
||||
|
||||
private static void WriteEpilogue(CodeGenContext context)
|
||||
|
@ -1912,8 +1854,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
mask = CallingConvention.GetIntCalleeSavedRegisters() & context.AllocResult.IntUsedRegisters;
|
||||
|
||||
mask |= 1 << (int)X86Register.Rbp;
|
||||
|
||||
while (mask != 0)
|
||||
{
|
||||
int bit = BitUtils.HighestBitSet(mask);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using ARMeilleure.CodeGen.RegisterAllocators;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
@ -10,11 +10,13 @@ namespace ARMeilleure.CodeGen.X86
|
|||
{
|
||||
static class PreAllocator
|
||||
{
|
||||
public static void RunPass(ControlFlowGraph cfg, MemoryManager memory, out int maxCallArgs)
|
||||
public static void RunPass(CompilerContext cctx, StackAllocator stackAlloc, out int maxCallArgs)
|
||||
{
|
||||
maxCallArgs = -1;
|
||||
|
||||
foreach (BasicBlock block in cfg.Blocks)
|
||||
Operand[] preservedArgs = new Operand[CallingConvention.GetArgumentsOnRegsCount()];
|
||||
|
||||
foreach (BasicBlock block in cctx.Cfg.Blocks)
|
||||
{
|
||||
LinkedListNode<Node> nextNode;
|
||||
|
||||
|
@ -31,15 +33,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
HandleConstantCopy(node, operation);
|
||||
|
||||
//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() || IsComparisonIntrinsic(inst))
|
||||
{
|
||||
Operation copyOp = new Operation(Instruction.Copy, operation.Dest, Const(0));
|
||||
HandleFixedRegisterCopy(node, operation);
|
||||
|
||||
block.Operations.AddBefore(node, copyOp);
|
||||
}
|
||||
HandleSameDestSrc1Copy(node, operation);
|
||||
|
||||
//Unsigned integer to FP conversions are not supported on X86.
|
||||
//We need to turn them into signed integer to FP conversions, and
|
||||
|
@ -73,11 +69,19 @@ namespace ARMeilleure.CodeGen.X86
|
|||
{
|
||||
maxCallArgs = argsCount;
|
||||
}
|
||||
|
||||
//Copy values to registers expected by the function being called,
|
||||
//as mandated by the ABI.
|
||||
HandleCallWindowsAbi(stackAlloc, node, operation);
|
||||
}
|
||||
else if (inst == Instruction.Return)
|
||||
{
|
||||
HandleReturnWindowsAbi(cctx, node, preservedArgs, operation);
|
||||
}
|
||||
else if (inst == Instruction.LoadArgument)
|
||||
{
|
||||
HandleLoadArgumentWindowsAbi(cctx, node, preservedArgs, operation);
|
||||
}
|
||||
|
||||
HandleFixedRegisterCopy(node, operation);
|
||||
|
||||
HandleSameDestSrc1Copy(node, operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +188,6 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Operand zex = Local(OperandType.I64);
|
||||
|
||||
temp = nodes.AddAfter(temp, new Operation(Instruction.Copy, zex, source));
|
||||
|
||||
temp = nodes.AddAfter(temp, new Operation(Instruction.ConvertToFP, dest, zex));
|
||||
}
|
||||
else /* if (source.Type == OperandType.I64) */
|
||||
|
@ -315,7 +318,44 @@ namespace ARMeilleure.CodeGen.X86
|
|||
operation.Dest = rdx;
|
||||
}
|
||||
|
||||
//The only allowed shift register is CL.
|
||||
//Handle the many restrictions of the compare and exchange (16 bytes) instruction:
|
||||
//- 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.
|
||||
if (inst == Instruction.CompareAndSwap128)
|
||||
{
|
||||
void SplitOperand(Operand source, X86Register lowReg, X86Register highReg)
|
||||
{
|
||||
Operand lr = Gpr(lowReg, OperandType.I64);
|
||||
Operand hr = Gpr(highReg, OperandType.I64);
|
||||
|
||||
Operation extrL = new Operation(Instruction.VectorExtract, lr, source, Const(0));
|
||||
Operation extrH = new Operation(Instruction.VectorExtract, hr, source, Const(1));
|
||||
|
||||
node.List.AddBefore(node, extrL);
|
||||
node.List.AddBefore(node, extrH);
|
||||
}
|
||||
|
||||
Operand src2 = operation.GetSource(1);
|
||||
Operand src3 = operation.GetSource(2);
|
||||
|
||||
SplitOperand(src2, X86Register.Rax, X86Register.Rdx);
|
||||
SplitOperand(src3, X86Register.Rbx, X86Register.Rcx);
|
||||
|
||||
Operand rax = Gpr(X86Register.Rax, OperandType.I64);
|
||||
Operand rdx = Gpr(X86Register.Rdx, OperandType.I64);
|
||||
|
||||
Operation insL = new Operation(Instruction.Copy, dest, rax);
|
||||
Operation insH = new Operation(Instruction.VectorInsert, dest, dest, rdx, Const(1));
|
||||
|
||||
node.List.AddAfter(node, insH);
|
||||
node.List.AddAfter(node, insL);
|
||||
|
||||
operation.SetSource(1, Undef());
|
||||
operation.SetSource(2, Undef());
|
||||
}
|
||||
|
||||
//The shift register is always implied to be CL (low 8-bits of RCX or ECX).
|
||||
if (inst.IsShift() && operation.GetSource(1).Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
Operand rcx = Gpr(X86Register.Rcx, OperandType.I32);
|
||||
|
@ -326,16 +366,12 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
operation.SetSource(1, rcx);
|
||||
}
|
||||
|
||||
//Copy values to registers expected by the function being called,
|
||||
//as mandated by the ABI.
|
||||
if (inst == Instruction.Call)
|
||||
{
|
||||
HandleCallWindowsAbi(node, operation);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleCallWindowsAbi(LinkedListNode<Node> node, Operation operation)
|
||||
private static void HandleCallWindowsAbi(
|
||||
StackAllocator stackAlloc,
|
||||
LinkedListNode<Node> node,
|
||||
Operation operation)
|
||||
{
|
||||
Operand dest = operation.Dest;
|
||||
|
||||
|
@ -344,11 +380,33 @@ namespace ARMeilleure.CodeGen.X86
|
|||
|
||||
Operand retValueAddr = null;
|
||||
|
||||
int stackAllocOffset = 0;
|
||||
|
||||
int AllocateOnStack(int size)
|
||||
{
|
||||
//We assume that the stack allocator is initially empty (TotalSize = 0).
|
||||
//Taking that into account, we can reuse the space allocated for other
|
||||
//calls by keeping track of our own allocated size (stackAllocOffset).
|
||||
//If the space allocated is not big enough, then we just expand it.
|
||||
int offset = stackAllocOffset;
|
||||
|
||||
if (stackAllocOffset + size > stackAlloc.TotalSize)
|
||||
{
|
||||
stackAlloc.Allocate((stackAllocOffset + size) - stackAlloc.TotalSize);
|
||||
}
|
||||
|
||||
stackAllocOffset += size;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
if (dest != null && dest.Type == OperandType.V128)
|
||||
{
|
||||
retValueAddr = Local(OperandType.I64);
|
||||
|
||||
Operation allocOp = new Operation(Instruction.StackAlloc, retValueAddr, Const(dest.Type.GetSizeInBytes()));
|
||||
int stackOffset = AllocateOnStack(dest.Type.GetSizeInBytes());
|
||||
|
||||
Operation allocOp = new Operation(Instruction.StackAlloc, retValueAddr, Const(stackOffset));
|
||||
|
||||
node.List.AddBefore(node, allocOp);
|
||||
|
||||
|
@ -369,7 +427,9 @@ namespace ARMeilleure.CodeGen.X86
|
|||
{
|
||||
Operand stackAddr = Local(OperandType.I64);
|
||||
|
||||
Operation allocOp = new Operation(Instruction.StackAlloc, stackAddr, Const(source.Type.GetSizeInBytes()));
|
||||
int stackOffset = AllocateOnStack(source.Type.GetSizeInBytes());
|
||||
|
||||
Operation allocOp = new Operation(Instruction.StackAlloc, stackAddr, Const(stackOffset));
|
||||
|
||||
node.List.AddBefore(node, allocOp);
|
||||
|
||||
|
@ -466,6 +526,121 @@ namespace ARMeilleure.CodeGen.X86
|
|||
}
|
||||
}
|
||||
|
||||
private static void HandleReturnWindowsAbi(
|
||||
CompilerContext cctx,
|
||||
LinkedListNode<Node> node,
|
||||
Operand[] preservedArgs,
|
||||
Operation operation)
|
||||
{
|
||||
if (operation.SourcesCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Operand source = operation.GetSource(0);
|
||||
|
||||
Operand retReg;
|
||||
|
||||
if (source.Type.IsInteger())
|
||||
{
|
||||
retReg = Gpr(CallingConvention.GetIntReturnRegister(), source.Type);
|
||||
}
|
||||
else if (source.Type == OperandType.V128)
|
||||
{
|
||||
if (preservedArgs[0] == null)
|
||||
{
|
||||
Operand preservedArg = Local(OperandType.I64);
|
||||
|
||||
Operand arg0 = Gpr(CallingConvention.GetIntArgumentRegister(0), OperandType.I64);
|
||||
|
||||
Operation copyOp = new Operation(Instruction.Copy, preservedArg, arg0);
|
||||
|
||||
cctx.Cfg.Entry.Operations.AddFirst(copyOp);
|
||||
|
||||
preservedArgs[0] = preservedArg;
|
||||
}
|
||||
|
||||
retReg = preservedArgs[0];
|
||||
}
|
||||
else /* if (regType == RegisterType.Vector) */
|
||||
{
|
||||
retReg = Xmm(CallingConvention.GetVecReturnRegister(), source.Type);
|
||||
}
|
||||
|
||||
if (source.Type == OperandType.V128)
|
||||
{
|
||||
Operation retStoreOp = new Operation(Instruction.Store, null, retReg, source);
|
||||
|
||||
node.List.AddBefore(node, retStoreOp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operation retCopyOp = new Operation(Instruction.Copy, retReg, source);
|
||||
|
||||
node.List.AddBefore(node, retCopyOp);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleLoadArgumentWindowsAbi(
|
||||
CompilerContext cctx,
|
||||
LinkedListNode<Node> node,
|
||||
Operand[] preservedArgs,
|
||||
Operation operation)
|
||||
{
|
||||
Operand source = operation.GetSource(0);
|
||||
|
||||
Debug.Assert(source.Kind == OperandKind.Constant, "Non-constant LoadArgument source kind.");
|
||||
|
||||
int retArgs = cctx.FuncReturnType == OperandType.V128 ? 1 : 0;
|
||||
|
||||
int index = source.AsInt32() + retArgs;
|
||||
|
||||
if (index < CallingConvention.GetArgumentsOnRegsCount())
|
||||
{
|
||||
Operand dest = operation.Dest;
|
||||
|
||||
if (preservedArgs[index] == null)
|
||||
{
|
||||
Operand preservedArg;
|
||||
|
||||
Operand argReg;
|
||||
|
||||
if (dest.Type.IsInteger())
|
||||
{
|
||||
argReg = Gpr(CallingConvention.GetIntArgumentRegister(index), dest.Type);
|
||||
|
||||
preservedArg = Local(dest.Type);
|
||||
}
|
||||
else if (dest.Type == OperandType.V128)
|
||||
{
|
||||
argReg = Gpr(CallingConvention.GetIntArgumentRegister(index), OperandType.I64);
|
||||
|
||||
preservedArg = Local(OperandType.I64);
|
||||
}
|
||||
else /* if (regType == RegisterType.Vector) */
|
||||
{
|
||||
argReg = Xmm(CallingConvention.GetVecArgumentRegister(index), dest.Type);
|
||||
|
||||
preservedArg = Local(dest.Type);
|
||||
}
|
||||
|
||||
Operation copyOp = new Operation(Instruction.Copy, preservedArg, argReg);
|
||||
|
||||
cctx.Cfg.Entry.Operations.AddFirst(copyOp);
|
||||
|
||||
preservedArgs[index] = preservedArg;
|
||||
}
|
||||
|
||||
Operation loadArgOp = new Operation(dest.Type == OperandType.V128
|
||||
? Instruction.Load
|
||||
: Instruction.Copy, dest, preservedArgs[index]);
|
||||
|
||||
node.List.AddBefore(node, loadArgOp);
|
||||
|
||||
Delete(node, operation);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleSameDestSrc1Copy(LinkedListNode<Node> node, Operation operation)
|
||||
{
|
||||
if (operation.Dest == null || operation.SourcesCount == 0)
|
||||
|
@ -618,6 +793,7 @@ namespace ARMeilleure.CodeGen.X86
|
|||
switch (inst)
|
||||
{
|
||||
case Instruction.Copy:
|
||||
case Instruction.LoadArgument:
|
||||
case Instruction.LoadFromContext:
|
||||
case Instruction.Spill:
|
||||
case Instruction.SpillArg:
|
||||
|
@ -683,21 +859,5 @@ namespace ARMeilleure.CodeGen.X86
|
|||
return inst > Instruction.X86Intrinsic_Start &&
|
||||
inst < Instruction.X86Intrinsic_End;
|
||||
}
|
||||
|
||||
private static bool IsComparisonIntrinsic(Instruction inst)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.X86Comisdeq:
|
||||
case Instruction.X86Comisdge:
|
||||
case Instruction.X86Comisdlt:
|
||||
case Instruction.X86Comisseq:
|
||||
case Instruction.X86Comissge:
|
||||
case Instruction.X86Comisslt:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,8 @@ namespace ARMeilleure.CodeGen.X86
|
|||
Cmpps,
|
||||
Cmpsd,
|
||||
Cmpss,
|
||||
Cmpxchg16b,
|
||||
Cmpxchg8b,
|
||||
Comisd,
|
||||
Comiss,
|
||||
Cvtdq2pd,
|
||||
|
|
|
@ -1,20 +1,34 @@
|
|||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ARMeilleure.Diagnostics
|
||||
{
|
||||
static class Logger
|
||||
{
|
||||
private static long _startTime;
|
||||
|
||||
private static long[] _accumulatedTime;
|
||||
|
||||
static Logger()
|
||||
{
|
||||
_accumulatedTime = new long[(int)PassName.Count];
|
||||
}
|
||||
|
||||
public static bool DoLog;
|
||||
|
||||
public static void StartPass(PassName name)
|
||||
{
|
||||
#if DEBUG
|
||||
#if M_DEBUG
|
||||
WriteOutput(name + " pass started...");
|
||||
|
||||
_startTime = Stopwatch.GetTimestamp();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void EndPass(PassName name, ControlFlowGraph cfg)
|
||||
{
|
||||
#if DEBUG
|
||||
#if M_DEBUG
|
||||
EndPass(name);
|
||||
|
||||
WriteOutput("IR after " + name + " pass:");
|
||||
|
@ -25,11 +39,20 @@ namespace ARMeilleure.Diagnostics
|
|||
|
||||
public static void EndPass(PassName name)
|
||||
{
|
||||
#if DEBUG
|
||||
WriteOutput(name + " pass ended...");
|
||||
#if M_DEBUG
|
||||
long elapsedTime = Stopwatch.GetTimestamp() - _startTime;
|
||||
|
||||
_accumulatedTime[(int)name] += elapsedTime;
|
||||
|
||||
WriteOutput($"{name} pass ended after {GetMilliseconds(_accumulatedTime[(int)name])} ms...");
|
||||
#endif
|
||||
}
|
||||
|
||||
private static long GetMilliseconds(long ticks)
|
||||
{
|
||||
return (long)(((double)ticks / Stopwatch.Frequency) * 1000);
|
||||
}
|
||||
|
||||
private static void WriteOutput(string text)
|
||||
{
|
||||
Console.WriteLine(text);
|
||||
|
|
|
@ -2,10 +2,16 @@ namespace ARMeilleure.Diagnostics
|
|||
{
|
||||
enum PassName
|
||||
{
|
||||
Decoding,
|
||||
Translation,
|
||||
RegisterUsage,
|
||||
Dominance,
|
||||
SsaConstruction,
|
||||
Optimization,
|
||||
PreAllocation,
|
||||
RegisterAllocation
|
||||
RegisterAllocation,
|
||||
CodeGeneration,
|
||||
|
||||
Count
|
||||
}
|
||||
}
|
|
@ -84,8 +84,8 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
Operand value = EmitLoad(context, address, exclusive, 4);
|
||||
|
||||
Operand valueLow = context.VectorExtract(value, Local(OperandType.I64), 0);
|
||||
Operand valueHigh = context.VectorExtract(value, Local(OperandType.I64), 1);
|
||||
Operand valueLow = context.VectorExtract(OperandType.I64, value, 0);
|
||||
Operand valueHigh = context.VectorExtract(OperandType.I64, value, 1);
|
||||
|
||||
SetIntOrZR(context, op.Rt, valueLow);
|
||||
SetIntOrZR(context, op.Rt2, valueHigh);
|
||||
|
|
|
@ -144,10 +144,21 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
switch (size)
|
||||
{
|
||||
case 0: value = context.LoadZx8 (Local(OperandType.I32), physAddr); break;
|
||||
case 1: value = context.LoadZx16(Local(OperandType.I32), physAddr); break;
|
||||
case 2: value = context.Load (Local(OperandType.I32), physAddr); break;
|
||||
case 3: value = context.Load (Local(OperandType.I64), physAddr); break;
|
||||
case 0:
|
||||
value = context.Load8(physAddr);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value = context.Load16(physAddr);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value = context.Load(OperandType.I32, physAddr);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
value = context.Load(OperandType.I64, physAddr);
|
||||
break;
|
||||
}
|
||||
|
||||
SetIntOrZR(context, rt, value);
|
||||
|
@ -186,23 +197,23 @@ namespace ARMeilleure.Instructions
|
|||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
value = context.VectorInsert8(vector, context.LoadZx8(Local(OperandType.I32), physAddr), elem);
|
||||
value = context.VectorInsert8(vector, context.Load8(physAddr), elem);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value = context.VectorInsert16(vector, context.LoadZx16(Local(OperandType.I32), physAddr), elem);
|
||||
value = context.VectorInsert16(vector, context.Load16(physAddr), elem);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value = context.VectorInsert(vector, context.Load(Local(OperandType.I32), physAddr), elem);
|
||||
value = context.VectorInsert(vector, context.Load(OperandType.I32, physAddr), elem);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
value = context.VectorInsert(vector, context.Load(Local(OperandType.I64), physAddr), elem);
|
||||
value = context.VectorInsert(vector, context.Load(OperandType.I64, physAddr), elem);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
value = context.Load(Local(OperandType.V128), physAddr);
|
||||
value = context.Load(OperandType.V128, physAddr);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -284,19 +295,19 @@ namespace ARMeilleure.Instructions
|
|||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
context.Store8(physAddr, context.VectorExtract8(value, Local(OperandType.I32), elem));
|
||||
context.Store8(physAddr, context.VectorExtract8(value, elem));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
context.Store16(physAddr, context.VectorExtract16(value, Local(OperandType.I32), elem));
|
||||
context.Store16(physAddr, context.VectorExtract16(value, elem));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
context.Store(physAddr, context.VectorExtract(value, Local(OperandType.FP32), elem));
|
||||
context.Store(physAddr, context.VectorExtract(OperandType.FP32, value, elem));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
context.Store(physAddr, context.VectorExtract(value, Local(OperandType.FP64), elem));
|
||||
context.Store(physAddr, context.VectorExtract(OperandType.FP64, value, elem));
|
||||
break;
|
||||
|
||||
case 4:
|
||||
|
@ -337,7 +348,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
Operand pteAddress = context.Add(pte, pteOffset);
|
||||
|
||||
pte = context.Load(Local(OperandType.I64), pteAddress);
|
||||
pte = context.Load(OperandType.I64, pteAddress);
|
||||
}
|
||||
while (bit < context.Memory.AddressSpaceBits);
|
||||
|
||||
|
@ -455,18 +466,27 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
MethodInfo info = typeof(NativeInterface).GetMethod(fallbackMethodName);
|
||||
|
||||
Operand value;
|
||||
Operand value = null;
|
||||
|
||||
if (size < 4)
|
||||
{
|
||||
value = Local(size == 3 ? OperandType.I64 : OperandType.I32);
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: context.VectorExtract8 (GetVec(rt), value, elem); break;
|
||||
case 1: context.VectorExtract16(GetVec(rt), value, elem); break;
|
||||
case 2: context.VectorExtract (GetVec(rt), value, elem); break;
|
||||
case 3: context.VectorExtract (GetVec(rt), value, elem); break;
|
||||
case 0:
|
||||
value = context.VectorExtract8(GetVec(rt), elem);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
value = context.VectorExtract16(GetVec(rt), elem);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
value = context.VectorExtract(OperandType.I32, GetVec(rt), elem);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
value = context.VectorExtract(OperandType.I64, GetVec(rt), elem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -387,8 +387,8 @@ namespace ARMeilleure.Instructions
|
|||
OperandType type = sizeF != 0 ? OperandType.FP64
|
||||
: OperandType.FP32;
|
||||
|
||||
Operand ne0 = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand ne1 = context.VectorExtract(GetVec(op.Rn), Local(type), 1);
|
||||
Operand ne0 = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand ne1 = context.VectorExtract(type, GetVec(op.Rn), 1);
|
||||
|
||||
Operand res = EmitSoftFloatCall(context, nameof(SoftFloat32.FPAdd), ne0, ne1);
|
||||
|
||||
|
@ -1022,9 +1022,9 @@ namespace ARMeilleure.Instructions
|
|||
OperandType type = sizeF != 0 ? OperandType.FP64
|
||||
: OperandType.FP32;
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand me = context.VectorExtract(GetVec(op.Rm), Local(type), 0);
|
||||
Operand ae = context.VectorExtract(GetVec(op.Ra), Local(type), 0);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand me = context.VectorExtract(type, GetVec(op.Rm), 0);
|
||||
Operand ae = context.VectorExtract(type, GetVec(op.Ra), 0);
|
||||
|
||||
Operand res = context.Subtract(context.Multiply(context.Negate(ne), me), ae);
|
||||
|
||||
|
@ -1040,9 +1040,9 @@ namespace ARMeilleure.Instructions
|
|||
OperandType type = sizeF != 0 ? OperandType.FP64
|
||||
: OperandType.FP32;
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand me = context.VectorExtract(GetVec(op.Rm), Local(type), 0);
|
||||
Operand ae = context.VectorExtract(GetVec(op.Ra), Local(type), 0);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand me = context.VectorExtract(type, GetVec(op.Rm), 0);
|
||||
Operand ae = context.VectorExtract(type, GetVec(op.Ra), 0);
|
||||
|
||||
Operand res = context.Subtract(context.Multiply(ne, me), ae);
|
||||
|
||||
|
|
|
@ -466,7 +466,7 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
Operand ordMask = context.AddIntrinsic(Instruction.X86Cmpss, n, m, Const(cmpOrdered));
|
||||
|
||||
Operand isOrdered = context.VectorExtract16(ordMask, Local(OperandType.I32), 0);
|
||||
Operand isOrdered = context.VectorExtract16(ordMask, 0);
|
||||
|
||||
context.BranchIfFalse(lblNaN, isOrdered);
|
||||
|
||||
|
@ -483,7 +483,7 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
Operand ordMask = context.AddIntrinsic(Instruction.X86Cmpsd, n, m, Const(cmpOrdered));
|
||||
|
||||
Operand isOrdered = context.VectorExtract16(ordMask, Local(OperandType.I32), 0);
|
||||
Operand isOrdered = context.VectorExtract16(ordMask, 0);
|
||||
|
||||
context.BranchIfFalse(lblNaN, isOrdered);
|
||||
|
||||
|
@ -512,7 +512,7 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
OperandType type = op.Size != 0 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand me;
|
||||
|
||||
if (cmpWithZero)
|
||||
|
@ -521,7 +521,7 @@ namespace ARMeilleure.Instructions
|
|||
}
|
||||
else
|
||||
{
|
||||
me = context.VectorExtract(GetVec(op.Rm), Local(type), 0);
|
||||
me = context.VectorExtract(type, GetVec(op.Rm), 0);
|
||||
}
|
||||
|
||||
Operand nzcv = EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare), ne, me, Const(signalNaNs));
|
||||
|
@ -625,12 +625,12 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), index);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
|
||||
Operand me;
|
||||
|
||||
if (op is OpCodeSimdReg binOp)
|
||||
{
|
||||
me = context.VectorExtract(GetVec(binOp.Rm), Local(type), index);
|
||||
me = context.VectorExtract(type, GetVec(binOp.Rm), index);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace ARMeilleure.Instructions
|
|||
}
|
||||
else
|
||||
{
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(OperandType.FP32), 0);
|
||||
Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), 0);
|
||||
|
||||
Operand res = context.ConvertToFP(OperandType.FP64, ne);
|
||||
|
||||
|
@ -51,7 +51,7 @@ namespace ARMeilleure.Instructions
|
|||
}
|
||||
else
|
||||
{
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(OperandType.FP64), 0);
|
||||
Operand ne = context.VectorExtract(OperandType.FP64, GetVec(op.Rn), 0);
|
||||
|
||||
Operand res = context.ConvertToFP(OperandType.FP32, ne);
|
||||
|
||||
|
@ -60,7 +60,7 @@ namespace ARMeilleure.Instructions
|
|||
}
|
||||
else if (op.Size == 0 && op.Opc == 3) // Single -> Half.
|
||||
{
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(OperandType.FP32), 0);
|
||||
Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), 0);
|
||||
|
||||
MethodInfo info = typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
|
||||
|
||||
|
@ -150,7 +150,7 @@ namespace ARMeilleure.Instructions
|
|||
}
|
||||
else /* if (sizeF == 1) */
|
||||
{
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(OperandType.FP32), part + index);
|
||||
Operand ne = context.VectorExtract(OperandType.FP32, GetVec(op.Rn), part + index);
|
||||
|
||||
Operand e = context.ConvertToFP(OperandType.FP64, ne);
|
||||
|
||||
|
@ -209,7 +209,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
|
||||
if (sizeF == 0)
|
||||
{
|
||||
|
@ -389,6 +389,11 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
Operand res = GetIntOrZR(context, op.Rn);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
res = context.SignExtend32(OperandType.I64, res);
|
||||
}
|
||||
|
||||
res = EmitFPConvert(context, res, op.Size, signed: true);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), res, 0));
|
||||
|
@ -400,6 +405,11 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
Operand res = GetIntOrZR(context, op.Rn);
|
||||
|
||||
if (op.RegisterSize == RegisterSize.Int32)
|
||||
{
|
||||
res = context.SignExtend32(OperandType.I64, res);
|
||||
}
|
||||
|
||||
res = EmitFPConvert(context, res, op.Size, signed: true);
|
||||
|
||||
res = EmitI2fFBitsMul(context, res, op.FBits);
|
||||
|
@ -554,7 +564,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = context.VectorExtract(n, Local(type), index);
|
||||
Operand ne = context.VectorExtract(type, n, index);
|
||||
|
||||
Operand e = EmitRoundMathCall(context, MidpointRounding.ToEven, ne);
|
||||
|
||||
|
@ -606,7 +616,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = context.VectorExtract(n, Local(type), index);
|
||||
Operand ne = context.VectorExtract(type, n, index);
|
||||
|
||||
Operand e = EmitF2iFBitsMul(context, ne, fBits);
|
||||
|
||||
|
@ -655,7 +665,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
OperandType type = op.Size == 0 ? OperandType.FP32 : OperandType.FP64;
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
|
||||
Operand res = signed
|
||||
? EmitScalarFcvts(context, emit(ne), 0)
|
||||
|
@ -680,7 +690,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
OperandType type = op.Size == 0 ? OperandType.FP32 : OperandType.FP64;
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
|
||||
Operand res = signed
|
||||
? EmitScalarFcvts(context, ne, op.FBits)
|
||||
|
@ -1156,9 +1166,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
private static Operand EmitVectorLongExtract(EmitterContext context, int reg, int index, int size)
|
||||
{
|
||||
Operand res = Local(size == 3 ? OperandType.I64 : OperandType.I32);
|
||||
OperandType type = size == 3 ? OperandType.I64 : OperandType.I32;
|
||||
|
||||
return context.VectorExtract(GetVec(reg), res, index);
|
||||
return context.VectorExtract(type, GetVec(reg), index);
|
||||
}
|
||||
|
||||
private static Operand EmitVectorLongCreate(EmitterContext context, Operand low, Operand high)
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(OperandType.I32), 0);
|
||||
Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0);
|
||||
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(OperandType.I32), 0);
|
||||
Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0);
|
||||
|
||||
MethodInfo info = typeof(SoftFallback).GetMethod(nameof(SoftFallback.FixedRotate));
|
||||
|
||||
|
@ -47,7 +47,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(OperandType.I32), 0);
|
||||
Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0);
|
||||
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
|
@ -64,7 +64,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
Operand d = GetVec(op.Rd);
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(OperandType.I32), 0);
|
||||
Operand ne = context.VectorExtract(OperandType.I32, GetVec(op.Rn), 0);
|
||||
|
||||
Operand m = GetVec(op.Rm);
|
||||
|
||||
|
|
|
@ -375,8 +375,8 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
Operand n = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand m = context.VectorExtract(GetVec(op.Rm), Local(type), op.Index);
|
||||
Operand n = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand m = context.VectorExtract(type, GetVec(op.Rm), op.Index);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), emit(n, m), 0));
|
||||
}
|
||||
|
@ -387,9 +387,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
Operand d = context.VectorExtract(GetVec(op.Rd), Local(type), 0);
|
||||
Operand n = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand m = context.VectorExtract(GetVec(op.Rm), Local(type), op.Index);
|
||||
Operand d = context.VectorExtract(type, GetVec(op.Rd), 0);
|
||||
Operand n = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand m = context.VectorExtract(type, GetVec(op.Rm), op.Index);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), emit(d, n, m), 0));
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
Operand n = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand n = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), emit(n), 0));
|
||||
}
|
||||
|
@ -470,8 +470,8 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
Operand n = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand m = context.VectorExtract(GetVec(op.Rm), Local(type), 0);
|
||||
Operand n = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand m = context.VectorExtract(type, GetVec(op.Rm), 0);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), emit(n, m), 0));
|
||||
}
|
||||
|
@ -482,9 +482,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
OperandType type = (op.Size & 1) != 0 ? OperandType.FP64 : OperandType.FP32;
|
||||
|
||||
Operand a = context.VectorExtract(GetVec(op.Ra), Local(type), 0);
|
||||
Operand n = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand m = context.VectorExtract(GetVec(op.Rm), Local(type), 0);
|
||||
Operand a = context.VectorExtract(type, GetVec(op.Ra), 0);
|
||||
Operand n = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand m = context.VectorExtract(type, GetVec(op.Rm), 0);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), emit(a, n, m), 0));
|
||||
}
|
||||
|
@ -503,7 +503,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), index);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
|
||||
|
||||
res = context.VectorInsert(res, emit(ne), index);
|
||||
}
|
||||
|
@ -525,8 +525,8 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), index);
|
||||
Operand me = context.VectorExtract(GetVec(op.Rm), Local(type), index);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
|
||||
Operand me = context.VectorExtract(type, GetVec(op.Rm), index);
|
||||
|
||||
res = context.VectorInsert(res, emit(ne, me), index);
|
||||
}
|
||||
|
@ -548,9 +548,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand de = context.VectorExtract(GetVec(op.Rd), Local(type), index);
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), index);
|
||||
Operand me = context.VectorExtract(GetVec(op.Rm), Local(type), index);
|
||||
Operand de = context.VectorExtract(type, GetVec(op.Rd), index);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
|
||||
Operand me = context.VectorExtract(type, GetVec(op.Rm), index);
|
||||
|
||||
res = context.VectorInsert(res, emit(de, ne, me), index);
|
||||
}
|
||||
|
@ -572,8 +572,8 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), index);
|
||||
Operand me = context.VectorExtract(GetVec(op.Rm), Local(type), op.Index);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
|
||||
Operand me = context.VectorExtract(type, GetVec(op.Rm), op.Index);
|
||||
|
||||
res = context.VectorInsert(res, emit(ne, me), index);
|
||||
}
|
||||
|
@ -595,9 +595,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand de = context.VectorExtract(GetVec(op.Rd), Local(type), index);
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), index);
|
||||
Operand me = context.VectorExtract(GetVec(op.Rm), Local(type), op.Index);
|
||||
Operand de = context.VectorExtract(type, GetVec(op.Rd), index);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
|
||||
Operand me = context.VectorExtract(type, GetVec(op.Rm), op.Index);
|
||||
|
||||
res = context.VectorInsert(res, emit(de, ne, me), index);
|
||||
}
|
||||
|
@ -1074,11 +1074,11 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
int pairIndex = index << 1;
|
||||
|
||||
Operand n0 = context.VectorExtract(GetVec(op.Rn), Local(type), pairIndex);
|
||||
Operand n1 = context.VectorExtract(GetVec(op.Rn), Local(type), pairIndex + 1);
|
||||
Operand n0 = context.VectorExtract(type, GetVec(op.Rn), pairIndex);
|
||||
Operand n1 = context.VectorExtract(type, GetVec(op.Rn), pairIndex + 1);
|
||||
|
||||
Operand m0 = context.VectorExtract(GetVec(op.Rm), Local(type), pairIndex);
|
||||
Operand m1 = context.VectorExtract(GetVec(op.Rm), Local(type), pairIndex + 1);
|
||||
Operand m0 = context.VectorExtract(type, GetVec(op.Rm), pairIndex);
|
||||
Operand m1 = context.VectorExtract(type, GetVec(op.Rm), pairIndex + 1);
|
||||
|
||||
res = context.VectorInsert(res, emit(n0, n1), index);
|
||||
res = context.VectorInsert(res, emit(m0, m1), pairs + index);
|
||||
|
@ -1416,14 +1416,25 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
ThrowIfInvalid(index, size);
|
||||
|
||||
Operand res = Local(size == 3 ? OperandType.I64 : OperandType.I32);
|
||||
Operand res = null;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 0: context.VectorExtract8 (GetVec(reg), res, index); break;
|
||||
case 1: context.VectorExtract16(GetVec(reg), res, index); break;
|
||||
case 2: context.VectorExtract (GetVec(reg), res, index); break;
|
||||
case 3: context.VectorExtract (GetVec(reg), res, index); break;
|
||||
case 0:
|
||||
res = context.VectorExtract8(GetVec(reg), index);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
res = context.VectorExtract16(GetVec(reg), index);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
res = context.VectorExtract(OperandType.I32, GetVec(reg), index);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
res = context.VectorExtract(OperandType.I64, GetVec(reg), index);
|
||||
break;
|
||||
}
|
||||
|
||||
if (signed)
|
||||
|
|
|
@ -229,7 +229,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
OperandType type = op.Size == 0 ? OperandType.FP32 : OperandType.FP64;
|
||||
|
||||
Operand me = context.VectorExtract(GetVec(op.Rm), Local(type), 0);
|
||||
Operand me = context.VectorExtract(type, GetVec(op.Rm), 0);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), me, 0));
|
||||
|
||||
|
@ -237,7 +237,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
context.MarkLabel(lblTrue);
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), ne, 0));
|
||||
|
||||
|
@ -286,7 +286,7 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
OperandType type = op.Size == 0 ? OperandType.FP32 : OperandType.FP64;
|
||||
|
||||
Operand ne = context.VectorExtract(GetVec(op.Rn), Local(type), 0);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
|
||||
context.Copy(GetVec(op.Rd), context.VectorInsert(context.VectorZero(), ne, 0));
|
||||
}
|
||||
|
|
|
@ -201,7 +201,7 @@ namespace ARMeilleure.Instructions
|
|||
{
|
||||
ThreadContext context = GetCurrentContext();
|
||||
|
||||
V128 value = context.Memory.ReadVector128((long)address);
|
||||
V128 value = context.Memory.AtomicLoadInt128((long)address);
|
||||
|
||||
context.ExclusiveAddress = GetMaskedExclusiveAddress(address);
|
||||
context.ExclusiveValueLow = value.GetUInt64(0);
|
||||
|
@ -335,12 +335,9 @@ namespace ARMeilleure.Instructions
|
|||
|
||||
if (success)
|
||||
{
|
||||
success = context.Memory.AtomicCompareExchangeInt128(
|
||||
(long)address,
|
||||
context.ExclusiveValueLow,
|
||||
context.ExclusiveValueHigh,
|
||||
value.GetUInt64(0),
|
||||
value.GetUInt64(1));
|
||||
V128 expected = new V128(context.ExclusiveValueLow, context.ExclusiveValueHigh);
|
||||
|
||||
success = context.Memory.AtomicCompareExchangeInt128((long)address, expected, value);
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||
BranchIfTrue,
|
||||
ByteSwap,
|
||||
Call,
|
||||
CompareAndSwap128,
|
||||
CompareEqual,
|
||||
CompareGreater,
|
||||
CompareGreaterOrEqual,
|
||||
|
@ -32,12 +33,10 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||
DivideUI,
|
||||
Fill,
|
||||
Load,
|
||||
Load16,
|
||||
Load8,
|
||||
LoadArgument,
|
||||
LoadFromContext,
|
||||
LoadSx16,
|
||||
LoadSx32,
|
||||
LoadSx8,
|
||||
LoadZx16,
|
||||
LoadZx8,
|
||||
Multiply,
|
||||
Multiply64HighSI,
|
||||
Multiply64HighUI,
|
||||
|
@ -213,26 +212,6 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||
|
||||
static class InstructionExtensions
|
||||
{
|
||||
public static bool IsComparison(this Instruction inst)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.CompareEqual:
|
||||
case Instruction.CompareGreater:
|
||||
case Instruction.CompareGreaterOrEqual:
|
||||
case Instruction.CompareGreaterOrEqualUI:
|
||||
case Instruction.CompareGreaterUI:
|
||||
case Instruction.CompareLess:
|
||||
case Instruction.CompareLessOrEqual:
|
||||
case Instruction.CompareLessOrEqualUI:
|
||||
case Instruction.CompareLessUI:
|
||||
case Instruction.CompareNotEqual:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsShift(this Instruction inst)
|
||||
{
|
||||
switch (inst)
|
||||
|
|
|
@ -41,11 +41,12 @@ namespace ARMeilleure.Memory
|
|||
{
|
||||
switch (protection)
|
||||
{
|
||||
case Memory.MemoryProtection.None: return MmapProts.PROT_NONE;
|
||||
case Memory.MemoryProtection.Read: return MmapProts.PROT_READ;
|
||||
case Memory.MemoryProtection.ReadAndWrite: return MmapProts.PROT_READ | MmapProts.PROT_WRITE;
|
||||
case Memory.MemoryProtection.ReadAndExecute: return MmapProts.PROT_READ | MmapProts.PROT_EXEC;
|
||||
case Memory.MemoryProtection.Execute: return MmapProts.PROT_EXEC;
|
||||
case Memory.MemoryProtection.None: return MmapProts.PROT_NONE;
|
||||
case Memory.MemoryProtection.Read: return MmapProts.PROT_READ;
|
||||
case Memory.MemoryProtection.ReadAndWrite: return MmapProts.PROT_READ | MmapProts.PROT_WRITE;
|
||||
case Memory.MemoryProtection.ReadAndExecute: return MmapProts.PROT_READ | MmapProts.PROT_EXEC;
|
||||
case Memory.MemoryProtection.ReadWriteExecute: return MmapProts.PROT_READ | MmapProts.PROT_WRITE | MmapProts.PROT_EXEC;
|
||||
case Memory.MemoryProtection.Execute: return MmapProts.PROT_EXEC;
|
||||
|
||||
default: throw new ArgumentException($"Invalid permission \"{protection}\".");
|
||||
}
|
||||
|
|
|
@ -115,11 +115,12 @@ namespace ARMeilleure.Memory
|
|||
{
|
||||
switch (protection)
|
||||
{
|
||||
case Memory.MemoryProtection.None: return MemoryProtection.NoAccess;
|
||||
case Memory.MemoryProtection.Read: return MemoryProtection.ReadOnly;
|
||||
case Memory.MemoryProtection.ReadAndWrite: return MemoryProtection.ReadWrite;
|
||||
case Memory.MemoryProtection.ReadAndExecute: return MemoryProtection.ExecuteRead;
|
||||
case Memory.MemoryProtection.Execute: return MemoryProtection.Execute;
|
||||
case Memory.MemoryProtection.None: return MemoryProtection.NoAccess;
|
||||
case Memory.MemoryProtection.Read: return MemoryProtection.ReadOnly;
|
||||
case Memory.MemoryProtection.ReadAndWrite: return MemoryProtection.ReadWrite;
|
||||
case Memory.MemoryProtection.ReadAndExecute: return MemoryProtection.ExecuteRead;
|
||||
case Memory.MemoryProtection.ReadWriteExecute: return MemoryProtection.ExecuteReadWrite;
|
||||
case Memory.MemoryProtection.Execute: return MemoryProtection.Execute;
|
||||
|
||||
default: throw new ArgumentException($"Invalid permission \"{protection}\".");
|
||||
}
|
||||
|
|
|
@ -398,23 +398,19 @@ namespace ARMeilleure.Memory
|
|||
return (ulong)position < (ulong)AddressSpaceSize;
|
||||
}
|
||||
|
||||
internal bool AtomicCompareExchange2xInt32(
|
||||
long position,
|
||||
int expectedLow,
|
||||
int expectedHigh,
|
||||
int desiredLow,
|
||||
int desiredHigh)
|
||||
internal V128 AtomicLoadInt128(long position)
|
||||
{
|
||||
long expected = (uint)expectedLow;
|
||||
long desired = (uint)desiredLow;
|
||||
if ((position & 0xf) != 0)
|
||||
{
|
||||
AbortWithAlignmentFault(position);
|
||||
}
|
||||
|
||||
expected |= (long)expectedHigh << 32;
|
||||
desired |= (long)desiredHigh << 32;
|
||||
IntPtr ptr = TranslateWrite(position);
|
||||
|
||||
return AtomicCompareExchangeInt64(position, expected, desired);
|
||||
return MemoryManagerPal.AtomicLoad128(ptr);
|
||||
}
|
||||
|
||||
public bool AtomicCompareExchangeByte(long position, byte expected, byte desired)
|
||||
internal bool AtomicCompareExchangeByte(long position, byte expected, byte desired)
|
||||
{
|
||||
int* ptr = (int*)Translate(position);
|
||||
|
||||
|
@ -426,7 +422,7 @@ namespace ARMeilleure.Memory
|
|||
return Interlocked.CompareExchange(ref *ptr, desired32, expected32) == expected32;
|
||||
}
|
||||
|
||||
public bool AtomicCompareExchangeInt16(long position, short expected, short desired)
|
||||
internal bool AtomicCompareExchangeInt16(long position, short expected, short desired)
|
||||
{
|
||||
if ((position & 1) != 0)
|
||||
{
|
||||
|
@ -455,7 +451,7 @@ namespace ARMeilleure.Memory
|
|||
return Interlocked.CompareExchange(ref *ptr, desired, expected) == expected;
|
||||
}
|
||||
|
||||
public bool AtomicCompareExchangeInt64(long position, long expected, long desired)
|
||||
internal bool AtomicCompareExchangeInt64(long position, long expected, long desired)
|
||||
{
|
||||
if ((position & 7) != 0)
|
||||
{
|
||||
|
@ -467,12 +463,7 @@ namespace ARMeilleure.Memory
|
|||
return Interlocked.CompareExchange(ref *ptr, desired, expected) == expected;
|
||||
}
|
||||
|
||||
internal bool AtomicCompareExchangeInt128(
|
||||
long position,
|
||||
ulong expectedLow,
|
||||
ulong expectedHigh,
|
||||
ulong desiredLow,
|
||||
ulong desiredHigh)
|
||||
internal bool AtomicCompareExchangeInt128(long position, V128 expected, V128 desired)
|
||||
{
|
||||
if ((position & 0xf) != 0)
|
||||
{
|
||||
|
@ -481,7 +472,7 @@ namespace ARMeilleure.Memory
|
|||
|
||||
IntPtr ptr = TranslateWrite(position);
|
||||
|
||||
throw new NotImplementedException();
|
||||
return MemoryManagerPal.CompareAndSwap128(ptr, expected, desired) == expected;
|
||||
}
|
||||
|
||||
public int AtomicIncrementInt32(long position)
|
||||
|
|
66
ARMeilleure/Memory/MemoryManagerPal.cs
Normal file
66
ARMeilleure/Memory/MemoryManagerPal.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Memory
|
||||
{
|
||||
static class MemoryManagerPal
|
||||
{
|
||||
private delegate V128 CompareExchange128(IntPtr address, V128 expected, V128 desired);
|
||||
|
||||
private static CompareExchange128 _compareExchange128;
|
||||
|
||||
private static object _lock;
|
||||
|
||||
static MemoryManagerPal()
|
||||
{
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public static V128 AtomicLoad128(IntPtr address)
|
||||
{
|
||||
return GetCompareAndSwap128()(address, V128.Zero, V128.Zero);
|
||||
}
|
||||
|
||||
public static V128 CompareAndSwap128(IntPtr address, V128 expected, V128 desired)
|
||||
{
|
||||
return GetCompareAndSwap128()(address, expected, desired);
|
||||
}
|
||||
|
||||
private static CompareExchange128 GetCompareAndSwap128()
|
||||
{
|
||||
if (_compareExchange128 == null)
|
||||
{
|
||||
GenerateCompareAndSwap128();
|
||||
}
|
||||
|
||||
return _compareExchange128;
|
||||
}
|
||||
|
||||
private static void GenerateCompareAndSwap128()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_compareExchange128 != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EmitterContext context = new EmitterContext();
|
||||
|
||||
Operand address = context.LoadArgument(OperandType.I64, 0);
|
||||
Operand expected = context.LoadArgument(OperandType.V128, 1);
|
||||
Operand desired = context.LoadArgument(OperandType.V128, 2);
|
||||
|
||||
Operand result = context.CompareAndSwap128(address, expected, desired);
|
||||
|
||||
context.Return(result);
|
||||
|
||||
ControlFlowGraph cfg = context.GetControlFlowGraph();
|
||||
|
||||
_compareExchange128 = Compiler.Compile<CompareExchange128>(cfg, OperandType.V128);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,8 @@ namespace ARMeilleure.Memory
|
|||
Write = 1 << 1,
|
||||
Execute = 1 << 2,
|
||||
|
||||
ReadAndWrite = Read | Write,
|
||||
ReadAndExecute = Read | Execute
|
||||
ReadAndWrite = Read | Write,
|
||||
ReadAndExecute = Read | Execute,
|
||||
ReadWriteExecute = Read | Write | Execute
|
||||
}
|
||||
}
|
|
@ -7,6 +7,10 @@ namespace ARMeilleure.State
|
|||
private ulong _e0;
|
||||
private ulong _e1;
|
||||
|
||||
private static V128 _zero = new V128(0, 0);
|
||||
|
||||
public static V128 Zero => _zero;
|
||||
|
||||
public V128(float value) : this(value, 0, 0, 0) { }
|
||||
|
||||
public V128(double value) : this(value, 0) { }
|
||||
|
|
39
ARMeilleure/Translation/Compiler.cs
Normal file
39
ARMeilleure/Translation/Compiler.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using ARMeilleure.CodeGen;
|
||||
using ARMeilleure.CodeGen.X86;
|
||||
using ARMeilleure.Diagnostics;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class Compiler
|
||||
{
|
||||
public static T Compile<T>(ControlFlowGraph cfg, OperandType funcReturnType)
|
||||
{
|
||||
IntPtr codePtr = JitCache.Map(Compile(cfg, funcReturnType));
|
||||
|
||||
return Marshal.GetDelegateForFunctionPointer<T>(codePtr);
|
||||
}
|
||||
|
||||
public static CompiledFunction Compile(ControlFlowGraph cfg, OperandType funcReturnType)
|
||||
{
|
||||
Logger.StartPass(PassName.Dominance);
|
||||
|
||||
Dominance.FindDominators(cfg);
|
||||
Dominance.FindDominanceFrontiers(cfg);
|
||||
|
||||
Logger.EndPass(PassName.Dominance);
|
||||
|
||||
Logger.StartPass(PassName.SsaConstruction);
|
||||
|
||||
Ssa.Rename(cfg);
|
||||
|
||||
Logger.EndPass(PassName.SsaConstruction, cfg);
|
||||
|
||||
CompilerContext cctx = new CompilerContext(cfg, funcReturnType);
|
||||
|
||||
return CodeGenerator.Generate(cctx);
|
||||
}
|
||||
}
|
||||
}
|
17
ARMeilleure/Translation/CompilerContext.cs
Normal file
17
ARMeilleure/Translation/CompilerContext.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
struct CompilerContext
|
||||
{
|
||||
public ControlFlowGraph Cfg { get; }
|
||||
|
||||
public OperandType FuncReturnType { get; }
|
||||
|
||||
public CompilerContext(ControlFlowGraph cfg, OperandType funcReturnType)
|
||||
{
|
||||
Cfg = cfg;
|
||||
FuncReturnType = funcReturnType;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,29 +41,29 @@ namespace ARMeilleure.Translation
|
|||
_needsNewBlock = true;
|
||||
}
|
||||
|
||||
public Operand Add(Operand a, Operand b)
|
||||
public Operand Add(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.Add, Local(a.Type), a, b);
|
||||
return Add(Instruction.Add, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand BitwiseAnd(Operand a, Operand b)
|
||||
public Operand BitwiseAnd(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.BitwiseAnd, Local(a.Type), a, b);
|
||||
return Add(Instruction.BitwiseAnd, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand BitwiseExclusiveOr(Operand a, Operand b)
|
||||
public Operand BitwiseExclusiveOr(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.BitwiseExclusiveOr, Local(a.Type), a, b);
|
||||
return Add(Instruction.BitwiseExclusiveOr, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand BitwiseNot(Operand a)
|
||||
public Operand BitwiseNot(Operand op1)
|
||||
{
|
||||
return Add(Instruction.BitwiseNot, Local(a.Type), a);
|
||||
return Add(Instruction.BitwiseNot, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public Operand BitwiseOr(Operand a, Operand b)
|
||||
public Operand BitwiseOr(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.BitwiseOr, Local(a.Type), a, b);
|
||||
return Add(Instruction.BitwiseOr, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public void Branch(Operand label)
|
||||
|
@ -73,23 +73,23 @@ namespace ARMeilleure.Translation
|
|||
BranchToLabel(label);
|
||||
}
|
||||
|
||||
public void BranchIfFalse(Operand label, Operand a)
|
||||
public void BranchIfFalse(Operand label, Operand op1)
|
||||
{
|
||||
Add(Instruction.BranchIfFalse, null, a);
|
||||
Add(Instruction.BranchIfFalse, null, op1);
|
||||
|
||||
BranchToLabel(label);
|
||||
}
|
||||
|
||||
public void BranchIfTrue(Operand label, Operand a)
|
||||
public void BranchIfTrue(Operand label, Operand op1)
|
||||
{
|
||||
Add(Instruction.BranchIfTrue, null, a);
|
||||
Add(Instruction.BranchIfTrue, null, op1);
|
||||
|
||||
BranchToLabel(label);
|
||||
}
|
||||
|
||||
public Operand ByteSwap(Operand a)
|
||||
public Operand ByteSwap(Operand op1)
|
||||
{
|
||||
return Add(Instruction.ByteSwap, Local(a.Type), a);
|
||||
return Add(Instruction.ByteSwap, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public Operand Call(MethodInfo info, params Operand[] callArgs)
|
||||
|
@ -154,109 +154,124 @@ namespace ARMeilleure.Translation
|
|||
return Add(Instruction.Call, Local(returnType), callArgs);
|
||||
}
|
||||
|
||||
public Operand ConditionalSelect(Operand a, Operand b, Operand c)
|
||||
public Operand CompareAndSwap128(Operand address, Operand expected, Operand desired)
|
||||
{
|
||||
return Add(Instruction.ConditionalSelect, Local(b.Type), a, b, c);
|
||||
return Add(Instruction.CompareAndSwap128, Local(OperandType.V128), address, expected, desired);
|
||||
}
|
||||
|
||||
public Operand ConvertI64ToI32(Operand a)
|
||||
public Operand ConditionalSelect(Operand op1, Operand op2, Operand op3)
|
||||
{
|
||||
return Add(Instruction.ConvertI64ToI32, Local(OperandType.I32), a);
|
||||
return Add(Instruction.ConditionalSelect, Local(op2.Type), op1, op2, op3);
|
||||
}
|
||||
|
||||
public Operand ConvertToFP(OperandType type, Operand a)
|
||||
public Operand ConvertI64ToI32(Operand op1)
|
||||
{
|
||||
return Add(Instruction.ConvertToFP, Local(type), a);
|
||||
return Add(Instruction.ConvertI64ToI32, Local(OperandType.I32), op1);
|
||||
}
|
||||
|
||||
public Operand ConvertToFPUI(OperandType type, Operand a)
|
||||
public Operand ConvertToFP(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ConvertToFPUI, Local(type), a);
|
||||
return Add(Instruction.ConvertToFP, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand Copy(Operand a)
|
||||
public Operand ConvertToFPUI(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.Copy, Local(a.Type), a);
|
||||
return Add(Instruction.ConvertToFPUI, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand Copy(Operand d, Operand a)
|
||||
public Operand Copy(Operand op1)
|
||||
{
|
||||
return Add(Instruction.Copy, d, a);
|
||||
return Add(Instruction.Copy, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public Operand CountLeadingZeros(Operand a)
|
||||
public Operand Copy(Operand dest, Operand op1)
|
||||
{
|
||||
return Add(Instruction.CountLeadingZeros, Local(a.Type), a);
|
||||
return Add(Instruction.Copy, dest, op1);
|
||||
}
|
||||
|
||||
public Operand Divide(Operand a, Operand b)
|
||||
public Operand CountLeadingZeros(Operand op1)
|
||||
{
|
||||
return Add(Instruction.Divide, Local(a.Type), a, b);
|
||||
return Add(Instruction.CountLeadingZeros, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public Operand DivideUI(Operand a, Operand b)
|
||||
public Operand Divide(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.DivideUI, Local(a.Type), a, b);
|
||||
return Add(Instruction.Divide, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareEqual(Operand a, Operand b)
|
||||
public Operand DivideUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareEqual, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.DivideUI, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareGreater(Operand a, Operand b)
|
||||
public Operand ICompareEqual(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareGreater, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.CompareEqual, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareGreaterOrEqual(Operand a, Operand b)
|
||||
public Operand ICompareGreater(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareGreaterOrEqual, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.CompareGreater, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareGreaterOrEqualUI(Operand a, Operand b)
|
||||
public Operand ICompareGreaterOrEqual(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareGreaterOrEqualUI, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.CompareGreaterOrEqual, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareGreaterUI(Operand a, Operand b)
|
||||
public Operand ICompareGreaterOrEqualUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareGreaterUI, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.CompareGreaterOrEqualUI, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareLess(Operand a, Operand b)
|
||||
public Operand ICompareGreaterUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareLess, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.CompareGreaterUI, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareLessOrEqual(Operand a, Operand b)
|
||||
public Operand ICompareLess(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareLessOrEqual, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.CompareLess, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareLessOrEqualUI(Operand a, Operand b)
|
||||
public Operand ICompareLessOrEqual(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareLessOrEqualUI, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.CompareLessOrEqual, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareLessUI(Operand a, Operand b)
|
||||
public Operand ICompareLessOrEqualUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareLessUI, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.CompareLessOrEqualUI, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ICompareNotEqual(Operand a, Operand b)
|
||||
public Operand ICompareLessUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.CompareNotEqual, Local(OperandType.I32), a, b);
|
||||
return Add(Instruction.CompareLessUI, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand Load(Operand value, Operand address)
|
||||
public Operand ICompareNotEqual(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.Load, value, address);
|
||||
return Add(Instruction.CompareNotEqual, Local(OperandType.I32), op1, op2);
|
||||
}
|
||||
|
||||
public Operand LoadFromContext(int offset)
|
||||
public Operand Load(OperandType type, Operand address)
|
||||
{
|
||||
return Add(Instruction.LoadFromContext, Local(OperandType.I32), Const(offset));
|
||||
return Add(Instruction.Load, Local(type), address);
|
||||
}
|
||||
|
||||
public Operand Load16(Operand address)
|
||||
{
|
||||
return Add(Instruction.Load16, Local(OperandType.I32), address);
|
||||
}
|
||||
|
||||
public Operand Load8(Operand address)
|
||||
{
|
||||
return Add(Instruction.Load8, Local(OperandType.I32), address);
|
||||
}
|
||||
|
||||
public Operand LoadArgument(OperandType type, int index)
|
||||
{
|
||||
return Add(Instruction.LoadArgument, Local(type), Const(index));
|
||||
}
|
||||
|
||||
public void LoadFromContext()
|
||||
|
@ -266,114 +281,73 @@ namespace ARMeilleure.Translation
|
|||
Add(Instruction.LoadFromContext);
|
||||
}
|
||||
|
||||
public void StoreToContext(int offset, Operand value)
|
||||
public Operand Multiply(Operand op1, Operand op2)
|
||||
{
|
||||
Add(Instruction.StoreToContext, null, Const(offset), value);
|
||||
return Add(Instruction.Multiply, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public void StoreToContext()
|
||||
public Operand Multiply64HighSI(Operand op1, Operand op2)
|
||||
{
|
||||
Add(Instruction.StoreToContext);
|
||||
return Add(Instruction.Multiply64HighSI, Local(OperandType.I64), op1, op2);
|
||||
}
|
||||
|
||||
public Operand Multiply64HighUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.Multiply64HighUI, Local(OperandType.I64), op1, op2);
|
||||
}
|
||||
|
||||
public Operand Negate(Operand op1)
|
||||
{
|
||||
return Add(Instruction.Negate, Local(op1.Type), op1);
|
||||
}
|
||||
|
||||
public void Return()
|
||||
{
|
||||
Add(Instruction.Return);
|
||||
|
||||
_needsNewBlock = true;
|
||||
}
|
||||
|
||||
public Operand LoadSx16(Operand value, Operand address)
|
||||
public void Return(Operand op1)
|
||||
{
|
||||
return Add(Instruction.LoadSx16, value, address);
|
||||
}
|
||||
|
||||
public Operand LoadSx32(Operand value, Operand address)
|
||||
{
|
||||
return Add(Instruction.LoadSx32, value, address);
|
||||
}
|
||||
|
||||
public Operand LoadSx8(Operand value, Operand address)
|
||||
{
|
||||
return Add(Instruction.LoadSx8, value, address);
|
||||
}
|
||||
|
||||
public Operand LoadZx16(Operand value, Operand address)
|
||||
{
|
||||
return Add(Instruction.LoadZx16, value, address);
|
||||
}
|
||||
|
||||
public Operand LoadZx8(Operand value, Operand address)
|
||||
{
|
||||
return Add(Instruction.LoadZx8, value, address);
|
||||
}
|
||||
|
||||
public Operand Multiply(Operand a, Operand b)
|
||||
{
|
||||
return Add(Instruction.Multiply, Local(a.Type), a, b);
|
||||
}
|
||||
|
||||
public Operand Multiply64HighSI(Operand a, Operand b)
|
||||
{
|
||||
return Add(Instruction.Multiply64HighSI, Local(OperandType.I64), a, b);
|
||||
}
|
||||
|
||||
public Operand Multiply64HighUI(Operand a, Operand b)
|
||||
{
|
||||
return Add(Instruction.Multiply64HighUI, Local(OperandType.I64), a, b);
|
||||
}
|
||||
|
||||
public Operand Negate(Operand a)
|
||||
{
|
||||
return Add(Instruction.Negate, Local(a.Type), a);
|
||||
}
|
||||
|
||||
public Operand Return()
|
||||
{
|
||||
Operand returnValue = Add(Instruction.Return);
|
||||
Add(Instruction.Return, null, op1);
|
||||
|
||||
_needsNewBlock = true;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public Operand Return(Operand a)
|
||||
public Operand RotateRight(Operand op1, Operand op2)
|
||||
{
|
||||
Operand returnValue = Add(Instruction.Return, null, a);
|
||||
|
||||
_needsNewBlock = true;
|
||||
|
||||
return returnValue;
|
||||
return Add(Instruction.RotateRight, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand RotateRight(Operand a, Operand b)
|
||||
public Operand ShiftLeft(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.RotateRight, Local(a.Type), a, b);
|
||||
return Add(Instruction.ShiftLeft, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ShiftLeft(Operand a, Operand b)
|
||||
public Operand ShiftRightSI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.ShiftLeft, Local(a.Type), a, b);
|
||||
return Add(Instruction.ShiftRightSI, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ShiftRightSI(Operand a, Operand b)
|
||||
public Operand ShiftRightUI(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.ShiftRightSI, Local(a.Type), a, b);
|
||||
return Add(Instruction.ShiftRightUI, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand ShiftRightUI(Operand a, Operand b)
|
||||
public Operand SignExtend16(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ShiftRightUI, Local(a.Type), a, b);
|
||||
return Add(Instruction.SignExtend16, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand SignExtend16(OperandType type, Operand a)
|
||||
public Operand SignExtend32(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.SignExtend16, Local(type), a);
|
||||
return Add(Instruction.SignExtend32, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand SignExtend32(OperandType type, Operand a)
|
||||
public Operand SignExtend8(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.SignExtend32, Local(type), a);
|
||||
}
|
||||
|
||||
public Operand SignExtend8(OperandType type, Operand a)
|
||||
{
|
||||
return Add(Instruction.SignExtend8, Local(type), a);
|
||||
return Add(Instruction.SignExtend8, Local(type), op1);
|
||||
}
|
||||
|
||||
public void Store(Operand address, Operand value)
|
||||
|
@ -391,24 +365,31 @@ namespace ARMeilleure.Translation
|
|||
Add(Instruction.Store8, null, address, value);
|
||||
}
|
||||
|
||||
public Operand Subtract(Operand a, Operand b)
|
||||
public void StoreToContext()
|
||||
{
|
||||
return Add(Instruction.Subtract, Local(a.Type), a, b);
|
||||
Add(Instruction.StoreToContext);
|
||||
|
||||
_needsNewBlock = true;
|
||||
}
|
||||
|
||||
public Operand VectorExtract(Operand vector, Operand value, int index)
|
||||
public Operand Subtract(Operand op1, Operand op2)
|
||||
{
|
||||
return Add(Instruction.VectorExtract, value, vector, Const(index));
|
||||
return Add(Instruction.Subtract, Local(op1.Type), op1, op2);
|
||||
}
|
||||
|
||||
public Operand VectorExtract16(Operand vector, Operand value, int index)
|
||||
public Operand VectorExtract(OperandType type, Operand vector, int index)
|
||||
{
|
||||
return Add(Instruction.VectorExtract16, value, vector, Const(index));
|
||||
return Add(Instruction.VectorExtract, Local(type), vector, Const(index));
|
||||
}
|
||||
|
||||
public Operand VectorExtract8(Operand vector, Operand value, int index)
|
||||
public Operand VectorExtract16(Operand vector, int index)
|
||||
{
|
||||
return Add(Instruction.VectorExtract8, value, vector, Const(index));
|
||||
return Add(Instruction.VectorExtract16, Local(OperandType.I32), vector, Const(index));
|
||||
}
|
||||
|
||||
public Operand VectorExtract8(Operand vector, int index)
|
||||
{
|
||||
return Add(Instruction.VectorExtract8, Local(OperandType.I32), vector, Const(index));
|
||||
}
|
||||
|
||||
public Operand VectorInsert(Operand vector, Operand value, int index)
|
||||
|
@ -441,19 +422,19 @@ namespace ARMeilleure.Translation
|
|||
return Add(Instruction.VectorZeroUpper96, Local(OperandType.V128), vector);
|
||||
}
|
||||
|
||||
public Operand ZeroExtend16(OperandType type, Operand a)
|
||||
public Operand ZeroExtend16(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ZeroExtend16, Local(type), a);
|
||||
return Add(Instruction.ZeroExtend16, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand ZeroExtend32(OperandType type, Operand a)
|
||||
public Operand ZeroExtend32(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ZeroExtend32, Local(type), a);
|
||||
return Add(Instruction.ZeroExtend32, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand ZeroExtend8(OperandType type, Operand a)
|
||||
public Operand ZeroExtend8(OperandType type, Operand op1)
|
||||
{
|
||||
return Add(Instruction.ZeroExtend8, Local(type), a);
|
||||
return Add(Instruction.ZeroExtend8, Local(type), op1);
|
||||
}
|
||||
|
||||
public Operand AddIntrinsic(Instruction inst, params Operand[] args)
|
||||
|
|
100
ARMeilleure/Translation/JitCache.cs
Normal file
100
ARMeilleure/Translation/JitCache.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
using ARMeilleure.CodeGen;
|
||||
using ARMeilleure.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class JitCache
|
||||
{
|
||||
private const int PageSize = 4 * 1024;
|
||||
private const int PageMask = PageSize - 1;
|
||||
|
||||
private static uint CacheSize = 512 * 1024 * 1024;
|
||||
|
||||
private static IntPtr _basePointer;
|
||||
|
||||
private static int _offset;
|
||||
|
||||
private static List<JitCacheEntry> _cacheEntries;
|
||||
|
||||
static JitCache()
|
||||
{
|
||||
_basePointer = MemoryManagement.Allocate(CacheSize);
|
||||
|
||||
JitUnwindWindows.InstallFunctionTableHandler(_basePointer, CacheSize);
|
||||
|
||||
//The first page is used for the table based SEH structs.
|
||||
_offset = PageSize;
|
||||
|
||||
_cacheEntries = new List<JitCacheEntry>();
|
||||
}
|
||||
|
||||
public static IntPtr Map(CompiledFunction func)
|
||||
{
|
||||
byte[] code = func.Code;
|
||||
|
||||
int funcOffset = Allocate(code.Length);
|
||||
|
||||
IntPtr funcPtr = _basePointer + funcOffset;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* codePtr = code)
|
||||
{
|
||||
byte* dest = (byte*)funcPtr;
|
||||
|
||||
long size = (long)code.Length;
|
||||
|
||||
Buffer.MemoryCopy(codePtr, dest, size, size);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: W^X.
|
||||
MemoryManagement.Reprotect(funcPtr, (ulong)code.Length, MemoryProtection.ReadWriteExecute);
|
||||
|
||||
Add(new JitCacheEntry(funcOffset, code.Length, func.UnwindInfo));
|
||||
|
||||
return funcPtr;
|
||||
}
|
||||
|
||||
private static int Allocate(int codeSize)
|
||||
{
|
||||
int allocOffset = _offset;
|
||||
|
||||
_offset += codeSize;
|
||||
|
||||
if ((ulong)(uint)_offset > CacheSize)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
|
||||
return allocOffset;
|
||||
}
|
||||
|
||||
private static void Add(JitCacheEntry entry)
|
||||
{
|
||||
//TODO: Use concurrent collection.
|
||||
_cacheEntries.Add(entry);
|
||||
}
|
||||
|
||||
public static bool TryFind(int offset, out JitCacheEntry entry)
|
||||
{
|
||||
foreach (JitCacheEntry cacheEntry in _cacheEntries)
|
||||
{
|
||||
int endOffset = cacheEntry.Offset + cacheEntry.Size;
|
||||
|
||||
if (offset >= cacheEntry.Offset && offset < endOffset)
|
||||
{
|
||||
entry = cacheEntry;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
entry = default(JitCacheEntry);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
19
ARMeilleure/Translation/JitCacheEntry.cs
Normal file
19
ARMeilleure/Translation/JitCacheEntry.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using ARMeilleure.CodeGen.Unwinding;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
struct JitCacheEntry
|
||||
{
|
||||
public int Offset { get; }
|
||||
public int Size { get; }
|
||||
|
||||
public UnwindInfo UnwindInfo { get; }
|
||||
|
||||
public JitCacheEntry(int offset, int size, UnwindInfo unwindInfo)
|
||||
{
|
||||
Offset = offset;
|
||||
Size = size;
|
||||
UnwindInfo = unwindInfo;
|
||||
}
|
||||
}
|
||||
}
|
148
ARMeilleure/Translation/JitUnwindWindows.cs
Normal file
148
ARMeilleure/Translation/JitUnwindWindows.cs
Normal file
|
@ -0,0 +1,148 @@
|
|||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
static class JitUnwindWindows
|
||||
{
|
||||
private const int MaxUnwindCodesArraySize = 9 + 10 * 2 + 3;
|
||||
|
||||
private struct RuntimeFunction
|
||||
{
|
||||
public uint BeginAddress;
|
||||
public uint EndAddress;
|
||||
public uint UnwindData;
|
||||
}
|
||||
|
||||
private struct UnwindInfo
|
||||
{
|
||||
public byte VersionAndFlags;
|
||||
public byte SizeOfProlog;
|
||||
public byte CountOfUnwindCodes;
|
||||
public byte FrameRegister;
|
||||
public unsafe fixed ushort UnwindCodes[MaxUnwindCodesArraySize];
|
||||
}
|
||||
|
||||
private enum UnwindOperation
|
||||
{
|
||||
PushNonvol = 0,
|
||||
AllocLarge = 1,
|
||||
AllocSmall = 2,
|
||||
SetFpreg = 3,
|
||||
SaveNonvol = 4,
|
||||
SaveNonvolFar = 5,
|
||||
SaveXmm128 = 8,
|
||||
SaveXmm128Far = 9,
|
||||
PushMachframe = 10
|
||||
}
|
||||
|
||||
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
private static unsafe extern bool RtlInstallFunctionTableCallback(
|
||||
ulong tableIdentifier,
|
||||
ulong baseAddress,
|
||||
uint length,
|
||||
GetRuntimeFunctionCallback callback,
|
||||
IntPtr context,
|
||||
string outOfProcessCallbackDll);
|
||||
|
||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||
|
||||
private static int _sizeOfRuntimeFunction;
|
||||
|
||||
private unsafe static RuntimeFunction* _runtimeFunction;
|
||||
|
||||
private unsafe static UnwindInfo* _unwindInfo;
|
||||
|
||||
public static void InstallFunctionTableHandler(IntPtr codeCachePointer, uint codeCacheLength)
|
||||
{
|
||||
ulong codeCachePtr = (ulong)codeCachePointer.ToInt64();
|
||||
|
||||
_sizeOfRuntimeFunction = Marshal.SizeOf<RuntimeFunction>();
|
||||
|
||||
bool result;
|
||||
|
||||
unsafe
|
||||
{
|
||||
_runtimeFunction = (RuntimeFunction*)codeCachePointer;
|
||||
|
||||
_unwindInfo = (UnwindInfo*)(codeCachePointer + _sizeOfRuntimeFunction);
|
||||
|
||||
_getRuntimeFunctionCallback = new GetRuntimeFunctionCallback(FunctionTableHandler);
|
||||
|
||||
result = RtlInstallFunctionTableCallback(
|
||||
codeCachePtr | 3,
|
||||
codeCachePtr,
|
||||
codeCacheLength,
|
||||
_getRuntimeFunctionCallback,
|
||||
codeCachePointer,
|
||||
null);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
throw new InvalidOperationException("Failure installing function table callback.");
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, IntPtr context)
|
||||
{
|
||||
int offset = (int)((long)controlPc - context.ToInt64());
|
||||
|
||||
if (!JitCache.TryFind(offset, out JitCacheEntry funcEntry))
|
||||
{
|
||||
//Not found.
|
||||
return null;
|
||||
}
|
||||
|
||||
var unwindInfo = funcEntry.UnwindInfo;
|
||||
|
||||
_unwindInfo->UnwindCodes[0] = PackUwop(UnwindOperation.AllocLarge, unwindInfo.PrologueSize, 1);
|
||||
_unwindInfo->UnwindCodes[1] = (ushort)(unwindInfo.FixedAllocSize >> 0);
|
||||
_unwindInfo->UnwindCodes[2] = (ushort)(unwindInfo.FixedAllocSize >> 16);
|
||||
|
||||
int codeIndex = 3;
|
||||
|
||||
int spOffset = 0;
|
||||
|
||||
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
|
||||
{
|
||||
var entry = unwindInfo.PushEntries[index];
|
||||
|
||||
bool isInteger = entry.Type == RegisterType.Integer;
|
||||
|
||||
UnwindOperation uwop = isInteger
|
||||
? UnwindOperation.PushNonvol
|
||||
: UnwindOperation.SaveXmm128;
|
||||
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = PackUwop(uwop, entry.StreamEndOffset, entry.Index);
|
||||
|
||||
if (!isInteger)
|
||||
{
|
||||
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)spOffset;
|
||||
|
||||
spOffset -= 16;
|
||||
}
|
||||
}
|
||||
|
||||
_unwindInfo->VersionAndFlags = 1;
|
||||
_unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologueSize;
|
||||
_unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
|
||||
_unwindInfo->FrameRegister = 0;
|
||||
|
||||
_runtimeFunction->BeginAddress = (uint)funcEntry.Offset;
|
||||
_runtimeFunction->EndAddress = (uint)(funcEntry.Offset + funcEntry.Size);
|
||||
_runtimeFunction->UnwindData = (uint)_sizeOfRuntimeFunction;
|
||||
|
||||
return _runtimeFunction;
|
||||
}
|
||||
|
||||
private static ushort PackUwop(UnwindOperation uwop, int prologOffset, int opInfo)
|
||||
{
|
||||
return (ushort)(prologOffset | ((int)uwop << 8) | (opInfo << 12));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -280,6 +280,8 @@ namespace ARMeilleure.Translation
|
|||
|
||||
private static void LoadLocals(BasicBlock block, long inputs, RegisterType baseType)
|
||||
{
|
||||
Operand arg0 = Local(OperandType.I64);
|
||||
|
||||
for (int bit = 63; bit >= 0; bit--)
|
||||
{
|
||||
long mask = 1L << bit;
|
||||
|
@ -291,12 +293,22 @@ namespace ARMeilleure.Translation
|
|||
|
||||
Operand dest = GetRegFromBit(bit, baseType);
|
||||
|
||||
int offset = NativeContext.GetRegisterOffset(dest.GetRegister());
|
||||
long offset = NativeContext.GetRegisterOffset(dest.GetRegister());
|
||||
|
||||
Operation operation = new Operation(Instruction.LoadFromContext, dest, Const(offset));
|
||||
Operand addr = Local(OperandType.I64);
|
||||
|
||||
block.Operations.AddFirst(operation);
|
||||
Operation loadOp = new Operation(Instruction.Load, dest, addr);
|
||||
|
||||
block.Operations.AddFirst(loadOp);
|
||||
|
||||
Operation calcOffsOp = new Operation(Instruction.Add, addr, arg0, Const(offset));
|
||||
|
||||
block.Operations.AddFirst(calcOffsOp);
|
||||
}
|
||||
|
||||
Operation loadArg0 = new Operation(Instruction.LoadArgument, arg0, Const(0));
|
||||
|
||||
block.Operations.AddFirst(loadArg0);
|
||||
}
|
||||
|
||||
private static void StoreLocals(BasicBlock block, long outputs, RegisterType baseType)
|
||||
|
@ -313,6 +325,12 @@ namespace ARMeilleure.Translation
|
|||
}
|
||||
}
|
||||
|
||||
Operand arg0 = Local(OperandType.I64);
|
||||
|
||||
Operation loadArg0 = new Operation(Instruction.LoadArgument, arg0, Const(0));
|
||||
|
||||
block.Append(loadArg0);
|
||||
|
||||
for (int bit = 0; bit < 64; bit++)
|
||||
{
|
||||
long mask = 1L << bit;
|
||||
|
@ -324,11 +342,17 @@ namespace ARMeilleure.Translation
|
|||
|
||||
Operand source = GetRegFromBit(bit, baseType);
|
||||
|
||||
int offset = NativeContext.GetRegisterOffset(source.GetRegister());
|
||||
long offset = NativeContext.GetRegisterOffset(source.GetRegister());
|
||||
|
||||
Operation operation = new Operation(Instruction.StoreToContext, null, Const(offset), source);
|
||||
Operand addr = Local(OperandType.I64);
|
||||
|
||||
block.Append(operation);
|
||||
Operation calcOffsOp = new Operation(Instruction.Add, addr, arg0, Const(offset));
|
||||
|
||||
block.Append(calcOffsOp);
|
||||
|
||||
Operation storeOp = new Operation(Instruction.Store, null, addr, source);
|
||||
|
||||
block.Append(storeOp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using ARMeilleure.CodeGen.X86;
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.Diagnostics;
|
||||
using ARMeilleure.Instructions;
|
||||
|
@ -17,16 +16,12 @@ namespace ARMeilleure.Translation
|
|||
{
|
||||
private MemoryManager _memory;
|
||||
|
||||
private TranslatedCache _cache;
|
||||
|
||||
private ConcurrentDictionary<ulong, TranslatedFunction> _funcs;
|
||||
|
||||
public Translator(MemoryManager memory)
|
||||
{
|
||||
_memory = memory;
|
||||
|
||||
_cache = new TranslatedCache();
|
||||
|
||||
_funcs = new ConcurrentDictionary<ulong, TranslatedFunction>();
|
||||
}
|
||||
|
||||
|
@ -73,8 +68,14 @@ namespace ARMeilleure.Translation
|
|||
|
||||
context.Memory = _memory;
|
||||
|
||||
Logger.StartPass(PassName.Decoding);
|
||||
|
||||
Block[] blocks = Decoder.DecodeFunction(_memory, address, ExecutionMode.Aarch64);
|
||||
|
||||
Logger.EndPass(PassName.Decoding);
|
||||
|
||||
Logger.StartPass(PassName.Translation);
|
||||
|
||||
EmitSynchronization(context);
|
||||
|
||||
if (blocks[0].Address != address)
|
||||
|
@ -84,21 +85,17 @@ namespace ARMeilleure.Translation
|
|||
|
||||
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks);
|
||||
|
||||
Logger.EndPass(PassName.Translation);
|
||||
|
||||
Logger.StartPass(PassName.RegisterUsage);
|
||||
|
||||
RegisterUsage.RunPass(cfg);
|
||||
|
||||
Dominance.FindDominators(cfg);
|
||||
Logger.EndPass(PassName.RegisterUsage);
|
||||
|
||||
Dominance.FindDominanceFrontiers(cfg);
|
||||
GuestFunction func = Compiler.Compile<GuestFunction>(cfg, OperandType.I64);
|
||||
|
||||
Logger.StartPass(PassName.SsaConstruction);
|
||||
|
||||
Ssa.Rename(cfg);
|
||||
|
||||
Logger.EndPass(PassName.SsaConstruction, cfg);
|
||||
|
||||
byte[] code = CodeGenerator.Generate(cfg, _memory);
|
||||
|
||||
return _cache.CreateFunction(code);
|
||||
return new TranslatedFunction(func);
|
||||
}
|
||||
|
||||
private static ControlFlowGraph EmitAndGetCFG(EmitterContext context, Block[] blocks)
|
||||
|
@ -164,11 +161,14 @@ namespace ARMeilleure.Translation
|
|||
|
||||
private static void EmitSynchronization(EmitterContext context)
|
||||
{
|
||||
int cntOffset = NativeContext.GetCounterOffset();
|
||||
long countOffs = NativeContext.GetCounterOffset();
|
||||
|
||||
Operand count = context.LoadFromContext(cntOffset);
|
||||
Operand countAddr = context.Add(context.LoadArgument(OperandType.I64, 0), Const(countOffs));
|
||||
|
||||
Operand count = context.Load(OperandType.I32, countAddr);
|
||||
|
||||
Operand lblNonZero = Label();
|
||||
Operand lblExit = Label();
|
||||
|
||||
context.BranchIfTrue(lblNonZero, count);
|
||||
|
||||
|
@ -176,11 +176,15 @@ namespace ARMeilleure.Translation
|
|||
|
||||
context.Call(info);
|
||||
|
||||
context.Branch(lblExit);
|
||||
|
||||
context.MarkLabel(lblNonZero);
|
||||
|
||||
count = context.Subtract(count, Const(1));
|
||||
|
||||
context.StoreToContext(cntOffset, count);
|
||||
context.Store(countAddr, count);
|
||||
|
||||
context.MarkLabel(lblExit);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
using ARMeilleure.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
class TranslatedCache
|
||||
{
|
||||
public TranslatedFunction CreateFunction(byte[] code)
|
||||
{
|
||||
IntPtr funcPtr = MapCodeAsExecutable(code);
|
||||
|
||||
GuestFunction func = Marshal.GetDelegateForFunctionPointer<GuestFunction>(funcPtr);
|
||||
|
||||
return new TranslatedFunction(func);
|
||||
}
|
||||
|
||||
private static IntPtr MapCodeAsExecutable(byte[] code)
|
||||
{
|
||||
ulong codeLength = (ulong)code.Length;
|
||||
|
||||
IntPtr funcPtr = MemoryManagement.Allocate(codeLength);
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* codePtr = code)
|
||||
{
|
||||
byte* dest = (byte*)funcPtr;
|
||||
|
||||
long size = (long)codeLength;
|
||||
|
||||
Buffer.MemoryCopy(codePtr, dest, size, size);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryManagement.Reprotect(funcPtr, codeLength, MemoryProtection.Execute);
|
||||
|
||||
return funcPtr;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue