using System; using System.Diagnostics; using System.Runtime.CompilerServices; namespace ARMeilleure.IntermediateRepresentation { unsafe struct Operation : IEquatable, IIntrusiveListNode { 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; private Span Destinations => new(_data->Destinations, _data->DestinationsCount); private Span 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++) { 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((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 = 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); } } } }