aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/IntermediateRepresentation
diff options
context:
space:
mode:
authorFICTURE7 <FICTURE7@gmail.com>2021-08-17 22:08:34 +0400
committerGitHub <noreply@github.com>2021-08-17 15:08:34 -0300
commit22b2cb39af00fb8881e908fd671fbf57a6e2db2a (patch)
treea79e2df801d7f16a33ff50b3c5bfed303cb476e9 /ARMeilleure/IntermediateRepresentation
parentcd4530f29c6a4ffd1b023105350b0440fa63f47b (diff)
Reduce JIT GC allocations (#2515)
* Turn `MemoryOperand` into a struct * Remove `IntrinsicOperation` * Remove `PhiNode` * Remove `Node` * Turn `Operand` into a struct * Turn `Operation` into a struct * Clean up pool management methods * Add `Arena` allocator * Move `OperationHelper` to `Operation.Factory` * Move `OperandHelper` to `Operand.Factory` * Optimize `Operation` a bit * Fix `Arena` initialization * Rename `NativeList<T>` to `ArenaList<T>` * Reduce `Operand` size from 88 to 56 bytes * Reduce `Operation` size from 56 to 40 bytes * Add optimistic interning of Register & Constant operands * Optimize `RegisterUsage` pass a bit * Optimize `RemoveUnusedNodes` pass a bit Iterating in reverse-order allows killing dependency chains in a single pass. * Fix PPTC symbols * Optimize `BasicBlock` a bit Reduce allocations from `_successor` & `DominanceFrontiers` * Fix `Operation` resize * Make `Arena` expandable Change the arena allocator to be expandable by allocating in pages, with some of them being pooled. Currently 32 pages are pooled. An LRU removal mechanism should probably be added to it. Apparently MHR can allocate bitmaps large enough to exceed the 16MB limit for the type. * Move `Arena` & `ArenaList` to `Common` * Remove `ThreadStaticPool` & co * Add `PhiOperation` * Reduce `Operand` size from 56 from 48 bytes * Add linear-probing to `Operand` intern table * Optimize `HybridAllocator` a bit * Add `Allocators` class * Tune `ArenaAllocator` sizes * Add page removal mechanism to `ArenaAllocator` Remove pages which have not been used for more than 5s after each reset. I am on fence if this would be better using a Gen2 callback object like the one in System.Buffers.ArrayPool<T>, to trim the pool. Because right now if a large translation happens, the pages will be freed only after a reset. This reset may not happen for a while because no new translation is hit, but the arena base sizes are rather small. * Fix `OOM` when allocating larger than page size in `ArenaAllocator` Tweak resizing mechanism for Operand.Uses and Assignemnts. * Optimize `Optimizer` a bit * Optimize `Operand.Add<T>/Remove<T>` a bit * Clean up `PreAllocator` * Fix phi insertion order Reduce codegen diffs. * Fix code alignment * Use new heuristics for degree of parallelism * Suppress warnings * Address gdkchan's feedback Renamed `GetValue()` to `GetValueUnsafe()` to make it more clear that `Operand.Value` should usually not be modified directly. * Add fast path to `ArenaAllocator` * Assembly for `ArenaAllocator.Allocate(ulong)`: .L0: mov rax, [rcx+0x18] lea r8, [rax+rdx] cmp r8, [rcx+0x10] ja short .L2 .L1: mov rdx, [rcx+8] add rax, [rdx+8] mov [rcx+0x18], r8 ret .L2: jmp ArenaAllocator.AllocateSlow(UInt64) A few variable/field had to be changed to ulong so that RyuJIT avoids emitting zero-extends. * Implement a new heuristic to free pooled pages. If an arena is used often, it is more likely that its pages will be needed, so the pages are kept for longer (e.g: during PPTC rebuild or burst sof compilations). If is not used often, then it is more likely that its pages will not be needed (e.g: after PPTC rebuild or bursts of compilations). * Address riperiperi's feedback * Use `EqualityComparer<T>` in `IntrusiveList<T>` Avoids a potential GC hole in `Equals(T, T)`.
Diffstat (limited to 'ARMeilleure/IntermediateRepresentation')
-rw-r--r--ARMeilleure/IntermediateRepresentation/BasicBlock.cs120
-rw-r--r--ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs2
-rw-r--r--ARMeilleure/IntermediateRepresentation/Instruction.cs3
-rw-r--r--ARMeilleure/IntermediateRepresentation/Intrinsic.cs2
-rw-r--r--ARMeilleure/IntermediateRepresentation/IntrinsicOperation.cs12
-rw-r--r--ARMeilleure/IntermediateRepresentation/IntrusiveList.cs86
-rw-r--r--ARMeilleure/IntermediateRepresentation/MemoryOperand.cs61
-rw-r--r--ARMeilleure/IntermediateRepresentation/Node.cs309
-rw-r--r--ARMeilleure/IntermediateRepresentation/Operand.cs480
-rw-r--r--ARMeilleure/IntermediateRepresentation/OperandHelper.cs119
-rw-r--r--ARMeilleure/IntermediateRepresentation/OperandKind.cs1
-rw-r--r--ARMeilleure/IntermediateRepresentation/Operation.cs384
-rw-r--r--ARMeilleure/IntermediateRepresentation/OperationHelper.cs66
-rw-r--r--ARMeilleure/IntermediateRepresentation/PhiNode.cs22
-rw-r--r--ARMeilleure/IntermediateRepresentation/PhiOperation.cs37
15 files changed, 989 insertions, 715 deletions
diff --git a/ARMeilleure/IntermediateRepresentation/BasicBlock.cs b/ARMeilleure/IntermediateRepresentation/BasicBlock.cs
index 056a9d46..7cee52e5 100644
--- a/ARMeilleure/IntermediateRepresentation/BasicBlock.cs
+++ b/ARMeilleure/IntermediateRepresentation/BasicBlock.cs
@@ -1,37 +1,47 @@
using System;
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
namespace ARMeilleure.IntermediateRepresentation
{
- class BasicBlock : IIntrusiveListNode<BasicBlock>
+ class BasicBlock : IEquatable<BasicBlock>, IIntrusiveListNode<BasicBlock>
{
- private readonly List<BasicBlock> _successors;
+ private const uint MaxSuccessors = 2;
- public int Index { get; set; }
+ private int _succCount;
+ private BasicBlock _succ0;
+ private BasicBlock _succ1;
+ private HashSet<BasicBlock> _domFrontiers;
+ public int Index { get; set; }
public BasicBlockFrequency Frequency { get; set; }
-
public BasicBlock ListPrevious { get; set; }
public BasicBlock ListNext { get; set; }
-
- public IntrusiveList<Node> Operations { get; }
-
+ public IntrusiveList<Operation> Operations { get; }
public List<BasicBlock> Predecessors { get; }
-
- public HashSet<BasicBlock> DominanceFrontiers { get; }
public BasicBlock ImmediateDominator { get; set; }
- public int SuccessorCount => _successors.Count;
+ public int SuccessorsCount => _succCount;
+
+ public HashSet<BasicBlock> DominanceFrontiers
+ {
+ get
+ {
+ if (_domFrontiers == null)
+ {
+ _domFrontiers = new HashSet<BasicBlock>();
+ }
+
+ return _domFrontiers;
+ }
+ }
public BasicBlock() : this(index: -1) { }
public BasicBlock(int index)
{
- _successors = new List<BasicBlock>();
-
- Operations = new IntrusiveList<Node>();
+ Operations = new IntrusiveList<Operation>();
Predecessors = new List<BasicBlock>();
- DominanceFrontiers = new HashSet<BasicBlock>();
Index = index;
}
@@ -40,54 +50,92 @@ namespace ARMeilleure.IntermediateRepresentation
{
if (block == null)
{
- throw new ArgumentNullException(nameof(block));
+ ThrowNull(nameof(block));
+ }
+
+ if ((uint)_succCount + 1 > MaxSuccessors)
+ {
+ ThrowSuccessorOverflow();
}
block.Predecessors.Add(this);
- _successors.Add(block);
+ GetSuccessorUnsafe(_succCount++) = block;
}
public void RemoveSuccessor(int index)
{
- BasicBlock oldBlock = _successors[index];
+ if ((uint)index >= (uint)_succCount)
+ {
+ ThrowOutOfRange(nameof(index));
+ }
+
+ ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
oldBlock.Predecessors.Remove(this);
+ oldBlock = null;
+
+ if (index == 0)
+ {
+ _succ0 = _succ1;
+ }
- _successors.RemoveAt(index);
+ _succCount--;
}
public BasicBlock GetSuccessor(int index)
{
- return _successors[index];
+ if ((uint)index >= (uint)_succCount)
+ {
+ ThrowOutOfRange(nameof(index));
+ }
+
+ return GetSuccessorUnsafe(index);
+ }
+
+ private ref BasicBlock GetSuccessorUnsafe(int index)
+ {
+ return ref Unsafe.Add(ref _succ0, index);
}
public void SetSuccessor(int index, BasicBlock block)
{
if (block == null)
{
- throw new ArgumentNullException(nameof(block));
+ ThrowNull(nameof(block));
}
- BasicBlock oldBlock = _successors[index];
+ if ((uint)index >= (uint)_succCount)
+ {
+ ThrowOutOfRange(nameof(index));
+ }
+
+ ref BasicBlock oldBlock = ref GetSuccessorUnsafe(index);
oldBlock.Predecessors.Remove(this);
block.Predecessors.Add(this);
-
- _successors[index] = block;
+
+ oldBlock = block;
}
- public void Append(Node node)
+ public void Append(Operation node)
{
- var lastOp = Operations.Last as Operation;
+ Operation last = Operations.Last;
// Append node before terminal or to end if no terminal.
- switch (lastOp?.Instruction)
+ if (last == default)
+ {
+ Operations.AddLast(node);
+
+ return;
+ }
+
+ switch (last.Instruction)
{
case Instruction.Return:
case Instruction.Tailcall:
case Instruction.BranchIf:
- Operations.AddBefore(lastOp, node);
+ Operations.AddBefore(last, node);
break;
default:
@@ -96,9 +144,23 @@ namespace ARMeilleure.IntermediateRepresentation
}
}
- public Node GetLastOp()
+ private static void ThrowNull(string name) => throw new ArgumentNullException(name);
+ private static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
+ private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");
+
+ public bool Equals(BasicBlock other)
+ {
+ return other == this;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as BasicBlock);
+ }
+
+ public override int GetHashCode()
{
- return Operations.Last;
+ return base.GetHashCode();
}
}
} \ No newline at end of file
diff --git a/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs b/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs
index 797e7891..caa9b83f 100644
--- a/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs
+++ b/ARMeilleure/IntermediateRepresentation/IIntrusiveListNode.cs
@@ -1,6 +1,6 @@
namespace ARMeilleure.IntermediateRepresentation
{
- interface IIntrusiveListNode<T> where T : class
+ interface IIntrusiveListNode<T>
{
T ListPrevious { get; set; }
T ListNext { get; set; }
diff --git a/ARMeilleure/IntermediateRepresentation/Instruction.cs b/ARMeilleure/IntermediateRepresentation/Instruction.cs
index 938a528d..b675ed1c 100644
--- a/ARMeilleure/IntermediateRepresentation/Instruction.cs
+++ b/ARMeilleure/IntermediateRepresentation/Instruction.cs
@@ -1,6 +1,6 @@
namespace ARMeilleure.IntermediateRepresentation
{
- enum Instruction
+ enum Instruction : ushort
{
Add,
BitwiseAnd,
@@ -63,6 +63,7 @@ namespace ARMeilleure.IntermediateRepresentation
Extended,
Fill,
LoadFromContext,
+ Phi,
Spill,
SpillArg,
StoreToContext
diff --git a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
index 1ddf93e5..f5c5f3d7 100644
--- a/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
+++ b/ARMeilleure/IntermediateRepresentation/Intrinsic.cs
@@ -1,6 +1,6 @@
namespace ARMeilleure.IntermediateRepresentation
{
- enum Intrinsic
+ enum Intrinsic : ushort
{
X86Addpd,
X86Addps,
diff --git a/ARMeilleure/IntermediateRepresentation/IntrinsicOperation.cs b/ARMeilleure/IntermediateRepresentation/IntrinsicOperation.cs
deleted file mode 100644
index 34781b70..00000000
--- a/ARMeilleure/IntermediateRepresentation/IntrinsicOperation.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace ARMeilleure.IntermediateRepresentation
-{
- class IntrinsicOperation : Operation
- {
- public Intrinsic Intrinsic { get; }
-
- public IntrinsicOperation(Intrinsic intrin, Operand dest, params Operand[] sources) : base(Instruction.Extended, dest, sources)
- {
- Intrinsic = intrin;
- }
- }
-} \ No newline at end of file
diff --git a/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs b/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs
index a7b0f7a7..184df87c 100644
--- a/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs
+++ b/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs
@@ -1,4 +1,6 @@
-using System.Diagnostics;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace ARMeilleure.IntermediateRepresentation
@@ -7,7 +9,7 @@ namespace ARMeilleure.IntermediateRepresentation
/// Represents a efficient linked list that stores the pointer on the object directly and does not allocate.
/// </summary>
/// <typeparam name="T">Type of the list items</typeparam>
- class IntrusiveList<T> where T : class, IIntrusiveListNode<T>
+ class IntrusiveList<T> where T : IEquatable<T>, IIntrusiveListNode<T>
{
/// <summary>
/// First item of the list, or null if empty.
@@ -25,21 +27,33 @@ namespace ARMeilleure.IntermediateRepresentation
public int Count { get; private set; }
/// <summary>
+ /// Initializes a new instance of the <see cref="IntrusiveList{T}"/> class.
+ /// </summary>
+ /// <exception cref="ArgumentException"><typeparamref name="T"/> is not pointer sized.</exception>
+ public IntrusiveList()
+ {
+ if (Unsafe.SizeOf<T>() != IntPtr.Size)
+ {
+ throw new ArgumentException("T must be a reference type or a pointer sized struct.");
+ }
+ }
+
+ /// <summary>
/// Adds a item as the first item of the list.
/// </summary>
/// <param name="newNode">Item to be added</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void AddFirst(T newNode)
+ public T AddFirst(T newNode)
{
- if (First != null)
+ if (!EqualsNull(First))
{
- AddBefore(First, newNode);
+ return AddBefore(First, newNode);
}
else
{
- Debug.Assert(newNode.ListPrevious == null);
- Debug.Assert(newNode.ListNext == null);
- Debug.Assert(Last == null);
+ Debug.Assert(EqualsNull(newNode.ListPrevious));
+ Debug.Assert(EqualsNull(newNode.ListNext));
+ Debug.Assert(EqualsNull(Last));
First = newNode;
Last = newNode;
@@ -47,6 +61,8 @@ namespace ARMeilleure.IntermediateRepresentation
Debug.Assert(Count == 0);
Count = 1;
+
+ return newNode;
}
}
@@ -55,17 +71,17 @@ namespace ARMeilleure.IntermediateRepresentation
/// </summary>
/// <param name="newNode">Item to be added</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void AddLast(T newNode)
+ public T AddLast(T newNode)
{
- if (Last != null)
+ if (!EqualsNull(Last))
{
- AddAfter(Last, newNode);
+ return AddAfter(Last, newNode);
}
else
{
- Debug.Assert(newNode.ListPrevious == null);
- Debug.Assert(newNode.ListNext == null);
- Debug.Assert(First == null);
+ Debug.Assert(EqualsNull(newNode.ListPrevious));
+ Debug.Assert(EqualsNull(newNode.ListNext));
+ Debug.Assert(EqualsNull(First));
First = newNode;
Last = newNode;
@@ -73,6 +89,8 @@ namespace ARMeilleure.IntermediateRepresentation
Debug.Assert(Count == 0);
Count = 1;
+
+ return newNode;
}
}
@@ -85,20 +103,20 @@ namespace ARMeilleure.IntermediateRepresentation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T AddBefore(T node, T newNode)
{
- Debug.Assert(newNode.ListPrevious == null);
- Debug.Assert(newNode.ListNext == null);
+ Debug.Assert(EqualsNull(newNode.ListPrevious));
+ Debug.Assert(EqualsNull(newNode.ListNext));
newNode.ListPrevious = node.ListPrevious;
newNode.ListNext = node;
node.ListPrevious = newNode;
- if (newNode.ListPrevious != null)
+ if (!EqualsNull(newNode.ListPrevious))
{
newNode.ListPrevious.ListNext = newNode;
}
- if (First == node)
+ if (Equals(First, node))
{
First = newNode;
}
@@ -117,20 +135,20 @@ namespace ARMeilleure.IntermediateRepresentation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T AddAfter(T node, T newNode)
{
- Debug.Assert(newNode.ListPrevious == null);
- Debug.Assert(newNode.ListNext == null);
+ Debug.Assert(EqualsNull(newNode.ListPrevious));
+ Debug.Assert(EqualsNull(newNode.ListNext));
newNode.ListPrevious = node;
newNode.ListNext = node.ListNext;
node.ListNext = newNode;
- if (newNode.ListNext != null)
+ if (!EqualsNull(newNode.ListNext))
{
newNode.ListNext.ListPrevious = newNode;
}
- if (Last == node)
+ if (Equals(Last, node))
{
Last = newNode;
}
@@ -147,32 +165,44 @@ namespace ARMeilleure.IntermediateRepresentation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Remove(T node)
{
- if (node.ListPrevious != null)
+ if (!EqualsNull(node.ListPrevious))
{
node.ListPrevious.ListNext = node.ListNext;
}
else
{
- Debug.Assert(First == node);
+ Debug.Assert(Equals(First, node));
First = node.ListNext;
}
- if (node.ListNext != null)
+ if (!EqualsNull(node.ListNext))
{
node.ListNext.ListPrevious = node.ListPrevious;
}
else
{
- Debug.Assert(Last == node);
+ Debug.Assert(Equals(Last, node));
Last = node.ListPrevious;
}
- node.ListPrevious = null;
- node.ListNext = null;
+ node.ListPrevious = default;
+ node.ListNext = default;
Count--;
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool EqualsNull(T a)
+ {
+ return EqualityComparer<T>.Default.Equals(a, default);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool Equals(T a, T b)
+ {
+ return EqualityComparer<T>.Default.Equals(a, b);
+ }
}
}
diff --git a/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs b/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs
index 56d07288..07d2633b 100644
--- a/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs
+++ b/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs
@@ -1,29 +1,54 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
namespace ARMeilleure.IntermediateRepresentation
{
- class MemoryOperand : Operand
+ unsafe struct MemoryOperand
{
- public Operand BaseAddress { get; set; }
- public Operand Index { get; set; }
+ private struct Data
+ {
+#pragma warning disable CS0649
+ public byte Kind;
+ public byte Type;
+#pragma warning restore CS0649
+ public byte Scale;
+ public Operand BaseAddress;
+ public Operand Index;
+ public int Displacement;
+ }
+
+ private Data* _data;
+
+ public MemoryOperand(Operand operand)
+ {
+ Debug.Assert(operand.Kind == OperandKind.Memory);
+
+ _data = (Data*)Unsafe.As<Operand, IntPtr>(ref operand);
+ }
- public Multiplier Scale { get; private set; }
+ public Operand BaseAddress
+ {
+ get => _data->BaseAddress;
+ set => _data->BaseAddress = value;
+ }
- public int Displacement { get; private set; }
+ public Operand Index
+ {
+ get => _data->Index;
+ set => _data->Index = value;
+ }
- public MemoryOperand() { }
+ public Multiplier Scale
+ {
+ get => (Multiplier)_data->Scale;
+ set => _data->Scale = (byte)value;
+ }
- public MemoryOperand With(
- OperandType type,
- Operand baseAddress,
- Operand index = null,
- Multiplier scale = Multiplier.x1,
- int displacement = 0)
+ public int Displacement
{
- With(OperandKind.Memory, type);
- BaseAddress = baseAddress;
- Index = index;
- Scale = scale;
- Displacement = displacement;
- return this;
+ get => _data->Displacement;
+ set => _data->Displacement = value;
}
}
} \ No newline at end of file
diff --git a/ARMeilleure/IntermediateRepresentation/Node.cs b/ARMeilleure/IntermediateRepresentation/Node.cs
deleted file mode 100644
index 3f41d814..00000000
--- a/ARMeilleure/IntermediateRepresentation/Node.cs
+++ /dev/null
@@ -1,309 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace ARMeilleure.IntermediateRepresentation
-{
- class Node : IIntrusiveListNode<Node>
- {
- public Node ListPrevious { get; set; }
- public Node ListNext { get; set; }
-
- public Operand Destination
- {
- get => _destinations.Count != 0 ? GetDestination(0) : null;
- set => SetDestination(value);
- }
-
- private readonly List<Operand> _destinations;
- private readonly List<Operand> _sources;
- private bool _clearedDest;
-
- public int DestinationsCount => _destinations.Count;
- public int SourcesCount => _sources.Count;
-
- private void Resize(List<Operand> list, int size)
- {
- if (list.Count > size)
- {
- list.RemoveRange(size, list.Count - size);
- }
- else
- {
- while (list.Count < size)
- {
- list.Add(null);
- }
- }
- }
-
- public Node()
- {
- _destinations = new List<Operand>();
- _sources = new List<Operand>();
- }
-
- public Node(Operand destination, int sourcesCount) : this()
- {
- Destination = destination;
-
- Resize(_sources, sourcesCount);
- }
-
- private void Reset(int sourcesCount)
- {
- _clearedDest = true;
- _sources.Clear();
- ListPrevious = null;
- ListNext = null;
-
- Resize(_sources, sourcesCount);
- }
-
- public Node With(Operand destination, int sourcesCount)
- {
- Reset(sourcesCount);
- Destination = destination;
-
- return this;
- }
-
- public Node With(Operand[] destinations, int sourcesCount)
- {
- Reset(sourcesCount);
- SetDestinations(destinations ?? throw new ArgumentNullException(nameof(destinations)));
-
- return this;
- }
-
- public Operand GetDestination(int index)
- {
- return _destinations[index];
- }
-
- public Operand GetSource(int index)
- {
- return _sources[index];
- }
-
- public void SetDestination(int index, Operand destination)
- {
- if (!_clearedDest)
- {
- RemoveAssignment(_destinations[index]);
- }
-
- AddAssignment(destination);
-
- _clearedDest = false;
-
- _destinations[index] = destination;
- }
-
- public void SetSource(int index, Operand source)
- {
- RemoveUse(_sources[index]);
-
- AddUse(source);
-
- _sources[index] = source;
- }
-
- private void RemoveOldDestinations()
- {
- if (!_clearedDest)
- {
- for (int index = 0; index < _destinations.Count; index++)
- {
- RemoveAssignment(_destinations[index]);
- }
- }
-
- _clearedDest = false;
- }
-
- public void SetDestination(Operand destination)
- {
- RemoveOldDestinations();
-
- if (destination == null)
- {
- _destinations.Clear();
- _clearedDest = true;
- }
- else
- {
- Resize(_destinations, 1);
-
- _destinations[0] = destination;
-
- AddAssignment(destination);
- }
- }
-
- public void SetDestinations(Operand[] destinations)
- {
- RemoveOldDestinations();
-
- Resize(_destinations, destinations.Length);
-
- for (int index = 0; index < destinations.Length; index++)
- {
- Operand newOp = destinations[index];
-
- _destinations[index] = newOp;
-
- AddAssignment(newOp);
- }
- }
-
- private void RemoveOldSources()
- {
- for (int index = 0; index < _sources.Count; index++)
- {
- RemoveUse(_sources[index]);
- }
- }
-
- public void SetSource(Operand source)
- {
- RemoveOldSources();
-
- if (source == null)
- {
- _sources.Clear();
- }
- else
- {
- Resize(_sources, 1);
-
- _sources[0] = source;
-
- AddUse(source);
- }
- }
-
- public void SetSources(Operand[] sources)
- {
- RemoveOldSources();
-
- Resize(_sources, sources.Length);
-
- for (int index = 0; index < sources.Length; index++)
- {
- Operand newOp = sources[index];
-
- _sources[index] = newOp;
-
- AddUse(newOp);
- }
- }
-
- private void AddAssignment(Operand op)
- {
- if (op == null)
- {
- return;
- }
-
- if (op.Kind == OperandKind.LocalVariable)
- {
- op.Assignments.Add(this);
- }
- else if (op.Kind == OperandKind.Memory)
- {
- MemoryOperand memOp = (MemoryOperand)op;
-
- if (memOp.BaseAddress != null)
- {
- memOp.BaseAddress.Assignments.Add(this);
- }
-
- if (memOp.Index != null)
- {
- memOp.Index.Assignments.Add(this);
- }
- }
- }
-
- private void RemoveAssignment(Operand op)
- {
- if (op == null)
- {
- return;
- }
-
- if (op.Kind == OperandKind.LocalVariable)
- {
- op.Assignments.Remove(this);
- }
- else if (op.Kind == OperandKind.Memory)
- {
- MemoryOperand memOp = (MemoryOperand)op;
-
- if (memOp.BaseAddress != null)
- {
- memOp.BaseAddress.Assignments.Remove(this);
- }
-
- if (memOp.Index != null)
- {
- memOp.Index.Assignments.Remove(this);
- }
- }
- }
-
- private void AddUse(Operand op)
- {
- if (op == null)
- {
- return;
- }
-
- if (op.Kind == OperandKind.LocalVariable)
- {
- op.Uses.Add(this);
- }
- else if (op.Kind == OperandKind.Memory)
- {
- MemoryOperand memOp = (MemoryOperand)op;
-
- if (memOp.BaseAddress != null)
- {
- memOp.BaseAddress.Uses.Add(this);
- }
-
- if (memOp.Index != null)
- {
- memOp.Index.Uses.Add(this);
- }
- }
- }
-
- private void RemoveUse(Operand op)
- {
- if (op == null)
- {
- return;
- }
-
- if (op.Kind == OperandKind.LocalVariable)
- {
- op.Uses.Remove(this);
- }
- else if (op.Kind == OperandKind.Memory)
- {
- MemoryOperand memOp = (MemoryOperand)op;
-
- if (memOp.BaseAddress != null)
- {
- memOp.BaseAddress.Uses.Remove(this);
- }
-
- if (memOp.Index != null)
- {
- memOp.Index.Uses.Remove(this);
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/ARMeilleure/IntermediateRepresentation/Operand.cs b/ARMeilleure/IntermediateRepresentation/Operand.cs
index 64df416f..2e0b649c 100644
--- a/ARMeilleure/IntermediateRepresentation/Operand.cs
+++ b/ARMeilleure/IntermediateRepresentation/Operand.cs
@@ -1,3 +1,4 @@
+using ARMeilleure.Common;
using ARMeilleure.Translation.PTC;
using System;
using System.Collections.Generic;
@@ -6,94 +7,106 @@ using System.Runtime.CompilerServices;
namespace ARMeilleure.IntermediateRepresentation
{
- class Operand
+ unsafe struct Operand : IEquatable<Operand>
{
- public OperandKind Kind { get; private set; }
- public OperandType Type { get; private set; }
-
- public ulong Value { get; private set; }
-
- public List<Node> Assignments { get; }
- public List<Node> Uses { get; }
+ internal struct Data
+ {
+ public byte Kind;
+ public byte Type;
+ public byte SymbolType;
+ public ushort AssignmentsCount;
+ public ushort AssignmentsCapacity;
+ public ushort UsesCount;
+ public ushort UsesCapacity;
+ public Operation* Assignments;
+ public Operation* Uses;
+ public ulong Value;
+ public ulong SymbolValue;
+ }
- public Symbol Symbol { get; private set; }
- public bool Relocatable => Symbol.Type != SymbolType.None;
+ private Data* _data;
- public Operand()
+ public OperandKind Kind
{
- Assignments = new List<Node>();
- Uses = new List<Node>();
+ get => (OperandKind)_data->Kind;
+ private set => _data->Kind = (byte)value;
}
- public Operand(OperandKind kind, OperandType type = OperandType.None) : this()
+ public OperandType Type
{
- Kind = kind;
- Type = type;
+ get => (OperandType)_data->Type;
+ private set => _data->Type = (byte)value;
}
- public Operand With(
- OperandKind kind,
- OperandType type = OperandType.None,
- ulong value = 0,
- Symbol symbol = default)
+ public ulong Value
{
- Kind = kind;
- Type = type;
-
- Value = value;
+ get => _data->Value;
+ private set => _data->Value = value;
+ }
- Symbol = symbol;
+ public Symbol Symbol
+ {
+ get
+ {
+ Debug.Assert(Kind != OperandKind.Memory);
- Assignments.Clear();
- Uses.Clear();
+ return new Symbol((SymbolType)_data->SymbolType, _data->SymbolValue);
+ }
+ private set
+ {
+ Debug.Assert(Kind != OperandKind.Memory);
- return this;
+ if (value.Type == SymbolType.None)
+ {
+ _data->SymbolType = (byte)SymbolType.None;
+ }
+ else
+ {
+ _data->SymbolType = (byte)value.Type;
+ _data->SymbolValue = value.Value;
+ }
+ }
}
- public Operand With(int value)
+ public ReadOnlySpan<Operation> Assignments
{
- return With(OperandKind.Constant, OperandType.I32, (uint)value);
- }
+ get
+ {
+ Debug.Assert(Kind != OperandKind.Memory);
- public Operand With(uint value)
- {
- return With(OperandKind.Constant, OperandType.I32, value);
+ return new ReadOnlySpan<Operation>(_data->Assignments, _data->AssignmentsCount);
+ }
}
- public Operand With(long value)
+ public ReadOnlySpan<Operation> Uses
{
- return With(OperandKind.Constant, OperandType.I64, (ulong)value);
- }
+ get
+ {
+ Debug.Assert(Kind != OperandKind.Memory);
- public Operand With(long value, Symbol symbol)
- {
- return With(OperandKind.Constant, OperandType.I64, (ulong)value, symbol);
+ return new ReadOnlySpan<Operation>(_data->Uses, _data->UsesCount);
+ }
}
- public Operand With(ulong value)
- {
- return With(OperandKind.Constant, OperandType.I64, value);
- }
+ public int UsesCount => _data->UsesCount;
+ public int AssignmentsCount => _data->AssignmentsCount;
- public Operand With(float value)
- {
- return With(OperandKind.Constant, OperandType.FP32, (ulong)BitConverter.SingleToInt32Bits(value));
- }
+ public bool Relocatable => Symbol.Type != SymbolType.None;
- public Operand With(double value)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Register GetRegister()
{
- return With(OperandKind.Constant, OperandType.FP64, (ulong)BitConverter.DoubleToInt64Bits(value));
- }
+ Debug.Assert(Kind == OperandKind.Register);
- public Operand With(int index, RegisterType regType, OperandType type)
- {
- return With(OperandKind.Register, type, (ulong)((int)regType << 24 | index));
+ return new Register((int)Value & 0xffffff, (RegisterType)(Value >> 24));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Register GetRegister()
+ public MemoryOperand GetMemory()
{
- return new Register((int)Value & 0xffffff, (RegisterType)(Value >> 24));
+ Debug.Assert(Kind == OperandKind.Memory);
+
+ return new MemoryOperand(this);
}
public int GetLocalNumber()
@@ -133,6 +146,11 @@ namespace ARMeilleure.IntermediateRepresentation
return BitConverter.Int64BitsToDouble((long)Value);
}
+ internal ref ulong GetValueUnsafe()
+ {
+ return ref _data->Value;
+ }
+
internal void NumberLocal(int number)
{
if (Kind != OperandKind.LocalVariable)
@@ -143,6 +161,158 @@ namespace ARMeilleure.IntermediateRepresentation
Value = (ulong)number;
}
+ public void AddAssignment(Operation operation)
+ {
+ if (Kind == OperandKind.LocalVariable)
+ {
+ Add(operation, ref _data->Assignments, ref _data->AssignmentsCount, ref _data->AssignmentsCapacity);
+ }
+ else if (Kind == OperandKind.Memory)
+ {
+ MemoryOperand memOp = GetMemory();
+ Operand addr = memOp.BaseAddress;
+ Operand index = memOp.Index;
+
+ if (addr != default)
+ {
+ Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
+ }
+
+ if (index != default)
+ {
+ Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
+ }
+ }
+ }
+
+ public void RemoveAssignment(Operation operation)
+ {
+ if (Kind == OperandKind.LocalVariable)
+ {
+ Remove(operation, ref _data->Assignments, ref _data->AssignmentsCount);
+ }
+ else if (Kind == OperandKind.Memory)
+ {
+ MemoryOperand memOp = GetMemory();
+ Operand addr = memOp.BaseAddress;
+ Operand index = memOp.Index;
+
+ if (addr != default)
+ {
+ Remove(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount);
+ }
+
+ if (index != default)
+ {
+ Remove(operation, ref index._data->Assignments, ref index._data->AssignmentsCount);
+ }
+ }
+ }
+
+ public void AddUse(Operation operation)
+ {
+ if (Kind == OperandKind.LocalVariable)
+ {
+ Add(operation, ref _data->Uses, ref _data->UsesCount, ref _data->UsesCapacity);
+ }
+ else if (Kind == OperandKind.Memory)
+ {
+ MemoryOperand memOp = GetMemory();
+ Operand addr = memOp.BaseAddress;
+ Operand index = memOp.Index;
+
+ if (addr != default)
+ {
+ Add(operation, ref addr._data->Uses, ref addr._data->UsesCount, ref addr._data->UsesCapacity);
+ }
+
+ if (index != default)
+ {
+ Add(operation, ref index._data->Uses, ref index._data->UsesCount, ref index._data->UsesCapacity);
+ }
+ }
+ }
+
+ public void RemoveUse(Operation operation)
+ {
+ if (Kind == OperandKind.LocalVariable)
+ {
+ Remove(operation, ref _data->Uses, ref _data->UsesCount);
+ }
+ else if (Kind == OperandKind.Memory)
+ {
+ MemoryOperand memOp = GetMemory();
+ Operand addr = memOp.BaseAddress;
+ Operand index = memOp.Index;
+
+ if (addr != default)
+ {
+ Remove(operation, ref addr._data->Uses, ref addr._data->UsesCount);
+ }
+
+ if (index != default)
+ {
+ Remove(operation, ref index._data->Uses, ref index._data->UsesCount);
+ }
+ }
+ }
+
+ private static void New<T>(ref T* data, ref ushort count, ref ushort capacity, ushort initialCapacity) where T : unmanaged
+ {
+ count = 0;
+ capacity = initialCapacity;
+ data = Allocators.References.Allocate<T>(initialCapacity);
+ }
+
+ private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
+ {
+ if (count < capacity)
+ {
+ data[(uint)count++] = item;
+
+ return;
+ }
+
+ // Could not add item in the fast path, fallback onto the slow path.
+ ExpandAdd(item, ref data, ref count, ref capacity);
+
+ static void ExpandAdd(T item, ref T* data, ref ushort count, ref ushort capacity)
+ {
+ ushort newCount = checked((ushort)(count + 1));
+ ushort newCapacity = (ushort)Math.Min(capacity * 2, ushort.MaxValue);
+
+ var oldSpan = new Span<T>(data, count);
+
+ capacity = newCapacity;
+ data = Allocators.References.Allocate<T>(capacity);
+
+ oldSpan.CopyTo(new Span<T>(data, count));
+
+ data[count] = item;
+ count = newCount;
+ }
+ }
+
+ private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
+ {
+ var span = new Span<T>(data, count);
+
+ for (int i = 0; i < span.Length; i++)
+ {
+ if (EqualityComparer<T>.Default.Equals(span[i], item))
+ {
+ if (i + 1 < count)
+ {
+ span.Slice(i + 1).CopyTo(span.Slice(i));
+ }
+
+ count--;
+
+ return;
+ }
+ }
+ }
+
public override int GetHashCode()
{
if (Kind == OperandKind.LocalVariable)
@@ -154,5 +324,201 @@ namespace ARMeilleure.IntermediateRepresentation
return (int)Value ^ ((int)Kind << 16) ^ ((int)Type << 20);
}
}
+
+ public bool Equals(Operand operand)
+ {
+ return operand._data == _data;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is Operand operand && Equals(operand);
+ }
+
+ public static bool operator ==(Operand a, Operand b)
+ {
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(Operand a, Operand b)
+ {
+ return !a.Equals(b);
+ }
+
+ public static class Factory
+ {
+ private const int InternTableSize = 256;
+ private const int InternTableProbeLength = 8;
+
+ [ThreadStatic]
+ private static Data* _internTable;
+
+ private static Data* InternTable
+ {
+ get
+ {
+ if (_internTable == null)
+ {
+ _internTable = (Data*)NativeAllocator.Instance.Allocate((uint)sizeof(Data) * InternTableSize);
+
+ // Make sure the table is zeroed.
+ new Span<Data>(_internTable, InternTableSize).Clear();
+ }
+
+ return _internTable;
+ }
+ }
+
+ private static Operand Make(OperandKind kind, OperandType type, ulong value, Symbol symbol = default)
+ {
+ Debug.Assert(kind != OperandKind.None);
+
+ Data* data = null;
+
+ // If constant or register, then try to look up in the intern table before allocating.
+ if (kind == OperandKind.Constant || kind == OperandKind.Register)
+ {
+ uint hash = (uint)HashCode.Combine(kind, type, value);
+
+ // Look in the next InternTableProbeLength slots for a match.
+ for (uint i = 0; i < InternTableProbeLength; i++)
+ {
+ Operand interned = new();
+ interned._data = &InternTable[(hash + i) % InternTableSize];
+
+ // If slot matches the allocation request then return that slot.
+ if (interned.Kind == kind && interned.Type == type && interned.Value == value && interned.Symbol == symbol)
+ {
+ return interned;
+ }
+ // Otherwise if the slot is not occupied, we store in that slot.
+ else if (interned.Kind == OperandKind.None)
+ {
+ data = interned._data;
+
+ break;
+ }
+ }
+ }
+
+ // If we could not get a slot from the intern table, we allocate somewhere else and store there.
+ if (data == null)
+ {
+ data = Allocators.Operands.Allocate<Data>();
+ }
+
+ *data = default;
+
+ Operand result = new();
+ result._data = data;
+ result.Value = value;
+ result.Kind = kind;
+ result.Type = type;
+
+ if (kind != OperandKind.Memory)
+ {
+ result.Symbol = symbol;
+ }
+
+ // If local variable, then the use and def list is initialized with default sizes.
+ if (kind == OperandKind.LocalVariable)
+ {
+ New(ref result._data->Assignments, ref result._data->AssignmentsCount, ref result._data->AssignmentsCapacity, 1);
+ New(ref result._data->Uses, ref result._data->UsesCount, ref result._data->UsesCapacity, 4);
+ }
+
+ return result;
+ }
+
+ public static Operand Const(OperandType type, long value)
+ {
+ Debug.Assert(type is OperandType.I32 or OperandType.I64);
+
+ return type == OperandType.I32 ? Const((int)value) : Const(value);
+ }
+
+ public static Operand Const(bool value)
+ {
+ return Const(value ? 1 : 0);
+ }
+
+ public static Operand Const(int value)
+ {
+ return Const((uint)value);
+ }
+
+ public static Operand Const(uint value)
+ {
+ return Make(OperandKind.Constant, OperandType.I32, value);
+ }
+
+ public static Operand Const(long value)
+ {
+ return Const(value, symbol: default);
+ }
+
+ public static Operand Const<T>(ref T reference, Symbol symbol = default)
+ {
+ return Const((long)Unsafe.AsPointer(ref reference), symbol);
+ }
+
+ public static Operand Const(long value, Symbol symbol)
+ {
+ return Make(OperandKind.Constant, OperandType.I64, (ulong)value, symbol);
+ }
+
+ public static Operand Const(ulong value)
+ {
+ return Make(OperandKind.Constant, OperandType.I64, value);
+ }
+
+ public static Operand ConstF(float value)
+ {
+ return Make(OperandKind.Constant, OperandType.FP32, (ulong)BitConverter.SingleToInt32Bits(value));
+ }
+
+ public static Operand ConstF(double value)
+ {
+ return Make(OperandKind.Constant, OperandType.FP64, (ulong)BitConverter.DoubleToInt64Bits(value));
+ }
+
+ public static Operand Label()
+ {
+ return Make(OperandKind.Label, OperandType.None, 0);
+ }
+
+ public static Operand Local(OperandType type)
+ {
+ return Make(OperandKind.LocalVariable, type, 0);
+ }
+
+ public static Operand Register(int index, RegisterType regType, OperandType type)
+ {
+ return Make(OperandKind.Register, type, (ulong)((int)regType << 24 | index));
+ }
+
+ public static Operand Undef()
+ {
+ return Make(OperandKind.Undefined, OperandType.None, 0);
+ }
+
+ public static Operand MemoryOp(
+ OperandType type,
+ Operand baseAddress,
+ Operand index = default,
+ Multiplier scale = Multiplier.x1,
+ int displacement = 0)
+ {
+ Operand result = Make(OperandKind.Memory, type, 0);
+
+ MemoryOperand memory = result.GetMemory();
+ memory.BaseAddress = baseAddress;
+ memory.Index = index;
+ memory.Scale = scale;
+ memory.Displacement = displacement;
+
+ return result;
+ }
+ }
}
} \ No newline at end of file
diff --git a/ARMeilleure/IntermediateRepresentation/OperandHelper.cs b/ARMeilleure/IntermediateRepresentation/OperandHelper.cs
deleted file mode 100644
index 420555a7..00000000
--- a/ARMeilleure/IntermediateRepresentation/OperandHelper.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using ARMeilleure.Common;
-using ARMeilleure.Translation.PTC;
-using System.Runtime.CompilerServices;
-
-namespace ARMeilleure.IntermediateRepresentation
-{
- static class OperandHelper
- {
- public static Operand Const(OperandType type, long value)
- {
- return type == OperandType.I32 ? Operand().With((int)value) : Operand().With(value);
- }
-
- public static Operand Const(bool value)
- {
- return Operand().With(value ? 1 : 0);
- }
-
- public static Operand Const(int value)
- {
- return Operand().With(value);
- }
-
- public static Operand Const(uint value)
- {
- return Operand().With(value);
- }
-
- public static Operand Const(long value)
- {
- return Operand().With(value);
- }
-
- public static Operand Const(long value, Symbol symbol)
- {
- return Operand().With(value, symbol);
- }
-
- public static Operand Const(ulong value)
- {
- return Operand().With(value);
- }
-
- public static unsafe Operand Const<T>(ref T reference, Symbol symbol = default)
- {
- return Operand().With((long)Unsafe.AsPointer(ref reference), symbol);
- }
-
- public static Operand ConstF(float value)
- {
- return Operand().With(value);
- }
-
- public static Operand ConstF(double value)
- {
- return Operand().With(value);
- }
-
- public static Operand Label()
- {
- return Operand().With(OperandKind.Label);
- }
-
- public static Operand Local(OperandType type)
- {
- return Operand().With(OperandKind.LocalVariable, type);
- }
-
- public static Operand Register(int index, RegisterType regType, OperandType type)
- {
- return Operand().With(index, regType, type);
- }
-
- public static Operand Undef()
- {
- return Operand().With(OperandKind.Undefined);
- }
-
- public static MemoryOperand MemoryOp(
- OperandType type,
- Operand baseAddress,
- Operand index = null,
- Multiplier scale = Multiplier.x1,
- int displacement = 0)
- {
- return MemoryOperand().With(type, baseAddress, index, scale, displacement);
- }
-
- #region "ThreadStaticPool"
- public static void PrepareOperandPool(int groupId = 0)
- {
- ThreadStaticPool<Operand>.PreparePool(groupId, ChunkSizeLimit.Large);
- ThreadStaticPool<MemoryOperand>.PreparePool(groupId, ChunkSizeLimit.Small);
- }
-
- private static Operand Operand()
- {
- return ThreadStaticPool<Operand>.Instance.Allocate();
- }
-
- private static MemoryOperand MemoryOperand()
- {
- return ThreadStaticPool<MemoryOperand>.Instance.Allocate();
- }
-
- public static void ResetOperandPool(int groupId = 0)
- {
- ThreadStaticPool<MemoryOperand>.ResetPool(groupId);
- ThreadStaticPool<Operand>.ResetPool(groupId);
- }
-
- public static void DisposeOperandPools()
- {
- ThreadStaticPool<Operand>.DisposePools();
- ThreadStaticPool<MemoryOperand>.DisposePools();
- }
- #endregion
- }
-}
diff --git a/ARMeilleure/IntermediateRepresentation/OperandKind.cs b/ARMeilleure/IntermediateRepresentation/OperandKind.cs
index 57618353..adb83561 100644
--- a/ARMeilleure/IntermediateRepresentation/OperandKind.cs
+++ b/ARMeilleure/IntermediateRepresentation/OperandKind.cs
@@ -2,6 +2,7 @@ namespace ARMeilleure.IntermediateRepresentation
{
enum OperandKind
{
+ None,
Constant,
Label,
LocalVariable,
diff --git a/ARMeilleure/IntermediateRepresentation/Operation.cs b/ARMeilleure/IntermediateRepresentation/Operation.cs
index 4cdbe326..08b874cf 100644
--- a/ARMeilleure/IntermediateRepresentation/Operation.cs
+++ b/ARMeilleure/IntermediateRepresentation/Operation.cs
@@ -1,89 +1,180 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
namespace ARMeilleure.IntermediateRepresentation
{
- class Operation : Node
+ unsafe struct Operation : IEquatable<Operation>, IIntrusiveListNode<Operation>
{
- public Instruction Instruction { get; private set; }
+ 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;
+ }
- public Operation() : base() { }
+ private Data* _data;
- public Operation(
- Instruction instruction,
- Operand destination,
- Operand[] sources) : base(destination, sources.Length)
+ public Instruction Instruction
{
- Instruction = instruction;
+ get => (Instruction)_data->Instruction;
+ private set => _data->Instruction = (ushort)value;
+ }
- for (int index = 0; index < sources.Length; index++)
- {
- SetSource(index, sources[index]);
- }
+ public Intrinsic Intrinsic
+ {
+ get => (Intrinsic)_data->Intrinsic;
+ private set => _data->Intrinsic = (ushort)value;
}
- public Operation With(Instruction instruction, Operand destination)
+ public Operation ListPrevious
{
- With(destination, 0);
- Instruction = instruction;
- return this;
+ get => _data->ListPrevious;
+ set => _data->ListPrevious = value;
}
- public Operation With(Instruction instruction, Operand destination, Operand[] sources)
+ public Operation ListNext
{
- With(destination, sources.Length);
- Instruction = instruction;
+ get => _data->ListNext;
+ set => _data->ListNext = value;
+ }
- for (int index = 0; index < sources.Length; index++)
+ public Operand Destination
+ {
+ get => _data->DestinationsCount != 0 ? GetDestination(0) : default;
+ set => SetDestination(value);
+ }
+
+ public int DestinationsCount => _data->DestinationsCount;
+ public int SourcesCount => _data->SourcesCount;
+
+ private Span<Operand> Destinations => new(_data->Destinations, _data->DestinationsCount);
+ private Span<Operand> Sources => new(_data->Sources, _data->SourcesCount);
+
+ public PhiOperation AsPhi()
+ {
+ Debug.Assert(Instruction == Instruction.Phi);
+
+ return new PhiOperation(this);
+ }
+
+ public Operand GetDestination(int index)
+ {
+ return Destinations[index];
+ }
+
+ public Operand GetSource(int index)
+ {
+ return Sources[index];
+ }
+
+ public void SetDestination(int index, Operand dest)
+ {
+ ref Operand curDest = ref Destinations[index];
+
+ RemoveAssignment(curDest);
+ AddAssignment(dest);
+
+ curDest = dest;
+ }
+
+ public void SetSource(int index, Operand src)
+ {
+ ref Operand curSrc = ref Sources[index];
+
+ RemoveUse(curSrc);
+ AddUse(src);
+
+ curSrc = src;
+ }
+
+ private void RemoveOldDestinations()
+ {
+ for (int i = 0; i < _data->DestinationsCount; i++)
{
- SetSource(index, sources[index]);
+ RemoveAssignment(_data->Destinations[i]);
}
- return this;
}
- public Operation With(Instruction instruction, Operand destination,
- Operand source0)
+ public void SetDestination(Operand dest)
{
- With(destination, 1);
- Instruction = instruction;
+ RemoveOldDestinations();
+
+ if (dest == default)
+ {
+ _data->DestinationsCount = 0;
+ }
+ else
+ {
+ EnsureCapacity(ref _data->Destinations, ref _data->DestinationsCount, 1);
+
+ _data->Destinations[0] = dest;
- SetSource(0, source0);
- return this;
+ AddAssignment(dest);
+ }
}
- public Operation With(Instruction instruction, Operand destination,
- Operand source0, Operand source1)
+ public void SetDestinations(Operand[] dests)
{
- With(destination, 2);
- Instruction = instruction;
+ 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;
- SetSource(0, source0);
- SetSource(1, source1);
- return this;
+ AddAssignment(newOp);
+ }
}
- public Operation With(Instruction instruction, Operand destination,
- Operand source0, Operand source1, Operand source2)
+ private void RemoveOldSources()
{
- With(destination, 3);
- Instruction = instruction;
+ for (int index = 0; index < _data->SourcesCount; index++)
+ {
+ RemoveUse(_data->Sources[index]);
+ }
+ }
+
+ public void SetSource(Operand src)
+ {
+ RemoveOldSources();
- SetSource(0, source0);
- SetSource(1, source1);
- SetSource(2, source2);
- return this;
+ if (src == default)
+ {
+ _data->SourcesCount = 0;
+ }
+ else
+ {
+ EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, 1);
+
+ _data->Sources[0] = src;
+
+ AddUse(src);
+ }
}
- public Operation With(
- Instruction instruction,
- Operand[] destinations,
- Operand[] sources)
+ public void SetSources(Operand[] srcs)
{
- With(destinations, sources.Length);
- Instruction = instruction;
+ RemoveOldSources();
- for (int index = 0; index < sources.Length; index++)
+ EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, srcs.Length);
+
+ for (int index = 0; index < srcs.Length; index++)
{
- SetSource(index, sources[index]);
+ Operand newOp = srcs[index];
+
+ _data->Sources[index] = newOp;
+
+ AddUse(newOp);
}
- return this;
}
public void TurnIntoCopy(Operand source)
@@ -92,5 +183,194 @@ namespace ARMeilleure.IntermediateRepresentation
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.Destinations.Clear();
+ result.Sources.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);
+ }
+ }
}
} \ No newline at end of file
diff --git a/ARMeilleure/IntermediateRepresentation/OperationHelper.cs b/ARMeilleure/IntermediateRepresentation/OperationHelper.cs
deleted file mode 100644
index 0e560ee0..00000000
--- a/ARMeilleure/IntermediateRepresentation/OperationHelper.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using ARMeilleure.Common;
-
-namespace ARMeilleure.IntermediateRepresentation
-{
- static class OperationHelper
- {
- public static Operation Operation(Instruction instruction, Operand destination)
- {
- return Operation().With(instruction, destination);
- }
-
- public static Operation Operation(Instruction instruction, Operand destination,
- Operand[] sources)
- {
- return Operation().With(instruction, destination, sources);
- }
-
- public static Operation Operation(Instruction instruction, Operand destination,
- Operand source0)
- {
- return Operation().With(instruction, destination, source0);
- }
-
- public static Operation Operation(Instruction instruction, Operand destination,
- Operand source0, Operand source1)
- {
- return Operation().With(instruction, destination, source0, source1);
- }
-
- public static Operation Operation(Instruction instruction, Operand destination,
- Operand source0, Operand source1, Operand source2)
- {
- return Operation().With(instruction, destination, source0, source1, source2);
- }
-
- public static Operation Operation(
- Instruction instruction,
- Operand[] destinations,
- Operand[] sources)
- {
- return Operation().With(instruction, destinations, sources);
- }
-
- #region "ThreadStaticPool"
- public static void PrepareOperationPool(int groupId = 0)
- {
- ThreadStaticPool<Operation>.PreparePool(groupId, ChunkSizeLimit.Medium);
- }
-
- private static Operation Operation()
- {
- return ThreadStaticPool<Operation>.Instance.Allocate();
- }
-
- public static void ResetOperationPool(int groupId = 0)
- {
- ThreadStaticPool<Operation>.ResetPool(groupId);
- }
-
- public static void DisposeOperationPools()
- {
- ThreadStaticPool<Operation>.DisposePools();
- }
- #endregion
- }
-}
diff --git a/ARMeilleure/IntermediateRepresentation/PhiNode.cs b/ARMeilleure/IntermediateRepresentation/PhiNode.cs
deleted file mode 100644
index 30fc4d38..00000000
--- a/ARMeilleure/IntermediateRepresentation/PhiNode.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace ARMeilleure.IntermediateRepresentation
-{
- class PhiNode : Node
- {
- private BasicBlock[] _blocks;
-
- public PhiNode(Operand destination, int predecessorsCount) : base(destination, predecessorsCount)
- {
- _blocks = new BasicBlock[predecessorsCount];
- }
-
- public BasicBlock GetBlock(int index)
- {
- return _blocks[index];
- }
-
- public void SetBlock(int index, BasicBlock block)
- {
- _blocks[index] = block;
- }
- }
-} \ No newline at end of file
diff --git a/ARMeilleure/IntermediateRepresentation/PhiOperation.cs b/ARMeilleure/IntermediateRepresentation/PhiOperation.cs
new file mode 100644
index 00000000..f2430882
--- /dev/null
+++ b/ARMeilleure/IntermediateRepresentation/PhiOperation.cs
@@ -0,0 +1,37 @@
+using ARMeilleure.Translation;
+using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
+
+namespace ARMeilleure.IntermediateRepresentation
+{
+ struct PhiOperation
+ {
+ private readonly Operation _operation;
+
+ public PhiOperation(Operation operation)
+ {
+ _operation = operation;
+ }
+
+ public int SourcesCount => _operation.SourcesCount / 2;
+
+ public BasicBlock GetBlock(ControlFlowGraph cfg, int index)
+ {
+ return cfg.PostOrderBlocks[cfg.PostOrderMap[_operation.GetSource(index * 2).AsInt32()]];
+ }
+
+ public void SetBlock(int index, BasicBlock block)
+ {
+ _operation.SetSource(index * 2, Const(block.Index));
+ }
+
+ public Operand GetSource(int index)
+ {
+ return _operation.GetSource(index * 2 + 1);
+ }
+
+ public void SetSource(int index, Operand operand)
+ {
+ _operation.SetSource(index * 2 + 1, operand);
+ }
+ }
+}