Ryujinx/ARMeilleure/IntermediateRepresentation/Operation.cs
FICTURE7 312be74861
Optimize HybridAllocator (#2637)
* Store constant `Operand`s in the `LocalInfo`

Since the spill slot and register assigned is fixed, we can just store
the `Operand` reference in the `LocalInfo` struct. This allows skipping
hitting the intern-table for a look up.

* Skip `Uses`/`Assignments` management

Since the `HybridAllocator` is the last pass and we do not care about
uses/assignments we can skip managing that when setting destinations or
sources.

* Make `GetLocalInfo` inlineable

Also fix a possible issue where with numbered locals. See or-assignment
operator in `SetVisited(local)` before patch.

* Do not run `BlockPlacement` in LCQ

With the host mapped memory manager, there is a lot less cold code to
split from hot code. So disabling this in LCQ gives some extra
throughput - where we need it.

* Address Mou-Ikkai's feedback

* Apply suggestions from code review

Co-authored-by: VocalFan <45863583+Mou-Ikkai@users.noreply.github.com>

* Move check to an assert

Co-authored-by: VocalFan <45863583+Mou-Ikkai@users.noreply.github.com>
2021-09-29 01:38:37 +02:00

376 lines
10 KiB
C#

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace ARMeilleure.IntermediateRepresentation
{
unsafe struct Operation : IEquatable<Operation>, IIntrusiveListNode<Operation>
{
internal struct Data
{
public ushort Instruction;
public ushort Intrinsic;
public ushort SourcesCount;
public ushort DestinationsCount;
public Operation ListPrevious;
public Operation ListNext;
public Operand* Destinations;
public Operand* Sources;
}
private Data* _data;
public Instruction Instruction
{
get => (Instruction)_data->Instruction;
private set => _data->Instruction = (ushort)value;
}
public Intrinsic Intrinsic
{
get => (Intrinsic)_data->Intrinsic;
private set => _data->Intrinsic = (ushort)value;
}
public Operation ListPrevious
{
get => _data->ListPrevious;
set => _data->ListPrevious = value;
}
public Operation ListNext
{
get => _data->ListNext;
set => _data->ListNext = value;
}
public Operand Destination
{
get => _data->DestinationsCount != 0 ? GetDestination(0) : default;
set => SetDestination(value);
}
public int DestinationsCount => _data->DestinationsCount;
public int SourcesCount => _data->SourcesCount;
internal Span<Operand> DestinationsUnsafe => new(_data->Destinations, _data->DestinationsCount);
internal Span<Operand> SourcesUnsafe => new(_data->Sources, _data->SourcesCount);
public PhiOperation AsPhi()
{
Debug.Assert(Instruction == Instruction.Phi);
return new PhiOperation(this);
}
public Operand GetDestination(int index)
{
return DestinationsUnsafe[index];
}
public Operand GetSource(int index)
{
return SourcesUnsafe[index];
}
public void SetDestination(int index, Operand dest)
{
ref Operand curDest = ref DestinationsUnsafe[index];
RemoveAssignment(curDest);
AddAssignment(dest);
curDest = dest;
}
public void SetSource(int index, Operand src)
{
ref Operand curSrc = ref SourcesUnsafe[index];
RemoveUse(curSrc);
AddUse(src);
curSrc = src;
}
private void RemoveOldDestinations()
{
for (int i = 0; i < _data->DestinationsCount; i++)
{
RemoveAssignment(_data->Destinations[i]);
}
}
public void SetDestination(Operand dest)
{
RemoveOldDestinations();
if (dest == default)
{
_data->DestinationsCount = 0;
}
else
{
EnsureCapacity(ref _data->Destinations, ref _data->DestinationsCount, 1);
_data->Destinations[0] = dest;
AddAssignment(dest);
}
}
public void SetDestinations(Operand[] dests)
{
RemoveOldDestinations();
EnsureCapacity(ref _data->Destinations, ref _data->DestinationsCount, dests.Length);
for (int index = 0; index < dests.Length; index++)
{
Operand newOp = dests[index];
_data->Destinations[index] = newOp;
AddAssignment(newOp);
}
}
private void RemoveOldSources()
{
for (int index = 0; index < _data->SourcesCount; index++)
{
RemoveUse(_data->Sources[index]);
}
}
public void SetSource(Operand src)
{
RemoveOldSources();
if (src == default)
{
_data->SourcesCount = 0;
}
else
{
EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, 1);
_data->Sources[0] = src;
AddUse(src);
}
}
public void SetSources(Operand[] srcs)
{
RemoveOldSources();
EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, srcs.Length);
for (int index = 0; index < srcs.Length; index++)
{
Operand newOp = srcs[index];
_data->Sources[index] = newOp;
AddUse(newOp);
}
}
public void TurnIntoCopy(Operand source)
{
Instruction = Instruction.Copy;
SetSource(source);
}
private void AddAssignment(Operand op)
{
if (op != default)
{
op.AddAssignment(this);
}
}
private void RemoveAssignment(Operand op)
{
if (op != default)
{
op.RemoveAssignment(this);
}
}
private void AddUse(Operand op)
{
if (op != default)
{
op.AddUse(this);
}
}
private void RemoveUse(Operand op)
{
if (op != default)
{
op.RemoveUse(this);
}
}
public bool Equals(Operation operation)
{
return operation._data == _data;
}
public override bool Equals(object obj)
{
return obj is Operation operation && Equals(operation);
}
public override int GetHashCode()
{
return HashCode.Combine((IntPtr)_data);
}
public static bool operator ==(Operation a, Operation b)
{
return a.Equals(b);
}
public static bool operator !=(Operation a, Operation b)
{
return !a.Equals(b);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void EnsureCapacity(ref Operand* list, ref ushort capacity, int newCapacity)
{
if (newCapacity > ushort.MaxValue)
{
ThrowOverflow(newCapacity);
}
// We only need to allocate a new buffer if we're increasing the size.
else if (newCapacity > capacity)
{
list = Allocators.References.Allocate<Operand>((uint)newCapacity);
}
capacity = (ushort)newCapacity;
}
private static void ThrowOverflow(int count) =>
throw new OverflowException($"Exceeded maximum size for Source or Destinations. Required {count}.");
public static class Factory
{
private static Operation Make(Instruction inst, int destCount, int srcCount)
{
Data* data = Allocators.Operations.Allocate<Data>();
*data = default;
Operation result = new();
result._data = data;
result.Instruction = inst;
EnsureCapacity(ref result._data->Destinations, ref result._data->DestinationsCount, destCount);
EnsureCapacity(ref result._data->Sources, ref result._data->SourcesCount, srcCount);
result.DestinationsUnsafe.Clear();
result.SourcesUnsafe.Clear();
return result;
}
public static Operation Operation(Instruction inst, Operand dest)
{
Operation result = Make(inst, 0, 0);
result.SetDestination(dest);
return result;
}
public static Operation Operation(Instruction inst, Operand dest, Operand src0)
{
Operation result = Make(inst, 0, 1);
result.SetDestination(dest);
result.SetSource(0, src0);
return result;
}
public static Operation Operation(Instruction inst, Operand dest, Operand src0, Operand src1)
{
Operation result = Make(inst, 0, 2);
result.SetDestination(dest);
result.SetSource(0, src0);
result.SetSource(1, src1);
return result;
}
public static Operation Operation(Instruction inst, Operand dest, Operand src0, Operand src1, Operand src2)
{
Operation result = Make(inst, 0, 3);
result.SetDestination(dest);
result.SetSource(0, src0);
result.SetSource(1, src1);
result.SetSource(2, src2);
return result;
}
public static Operation Operation(Instruction inst, Operand dest, int srcCount)
{
Operation result = Make(inst, 0, srcCount);
result.SetDestination(dest);
return result;
}
public static Operation Operation(Instruction inst, Operand dest, Operand[] srcs)
{
Operation result = Make(inst, 0, srcs.Length);
result.SetDestination(dest);
for (int index = 0; index < srcs.Length; index++)
{
result.SetSource(index, srcs[index]);
}
return result;
}
public static Operation Operation(Intrinsic intrin, Operand dest, params Operand[] srcs)
{
Operation result = Make(Instruction.Extended, 0, srcs.Length);
result.Intrinsic = intrin;
result.SetDestination(dest);
for (int index = 0; index < srcs.Length; index++)
{
result.SetSource(index, srcs[index]);
}
return result;
}
public static Operation Operation(Instruction inst, Operand[] dests, Operand[] srcs)
{
Operation result = Make(inst, dests.Length, srcs.Length);
for (int index = 0; index < dests.Length; index++)
{
result.SetDestination(index, dests[index]);
}
for (int index = 0; index < srcs.Length; index++)
{
result.SetSource(index, srcs[index]);
}
return result;
}
public static Operation PhiOperation(Operand dest, int srcCount)
{
return Operation(Instruction.Phi, dest, srcCount * 2);
}
}
}
}