Operation.cs
  1  using System;
  2  using System.Diagnostics;
  3  using System.Runtime.CompilerServices;
  4  
  5  namespace ARMeilleure.IntermediateRepresentation
  6  {
  7      unsafe struct Operation : IEquatable<Operation>, IIntrusiveListNode<Operation>
  8      {
  9          internal struct Data
 10          {
 11              public ushort Instruction;
 12              public ushort Intrinsic;
 13              public ushort SourcesCount;
 14              public ushort DestinationsCount;
 15              public Operation ListPrevious;
 16              public Operation ListNext;
 17              public Operand* Destinations;
 18              public Operand* Sources;
 19          }
 20  
 21          private Data* _data;
 22  
 23          public readonly Instruction Instruction
 24          {
 25              get => (Instruction)_data->Instruction;
 26              private set => _data->Instruction = (ushort)value;
 27          }
 28  
 29          public readonly Intrinsic Intrinsic
 30          {
 31              get => (Intrinsic)_data->Intrinsic;
 32              private set => _data->Intrinsic = (ushort)value;
 33          }
 34  
 35          public readonly Operation ListPrevious
 36          {
 37              get => _data->ListPrevious;
 38              set => _data->ListPrevious = value;
 39          }
 40  
 41          public readonly Operation ListNext
 42          {
 43              get => _data->ListNext;
 44              set => _data->ListNext = value;
 45          }
 46  
 47          public readonly Operand Destination
 48          {
 49              get => _data->DestinationsCount != 0 ? GetDestination(0) : default;
 50              set => SetDestination(value);
 51          }
 52  
 53          public readonly int DestinationsCount => _data->DestinationsCount;
 54          public readonly int SourcesCount => _data->SourcesCount;
 55  
 56          internal readonly Span<Operand> DestinationsUnsafe => new(_data->Destinations, _data->DestinationsCount);
 57          internal readonly Span<Operand> SourcesUnsafe => new(_data->Sources, _data->SourcesCount);
 58  
 59          public readonly PhiOperation AsPhi()
 60          {
 61              Debug.Assert(Instruction == Instruction.Phi);
 62  
 63              return new PhiOperation(this);
 64          }
 65  
 66          public readonly Operand GetDestination(int index)
 67          {
 68              return DestinationsUnsafe[index];
 69          }
 70  
 71          public readonly Operand GetSource(int index)
 72          {
 73              return SourcesUnsafe[index];
 74          }
 75  
 76          public readonly void SetDestination(int index, Operand dest)
 77          {
 78              ref Operand curDest = ref DestinationsUnsafe[index];
 79  
 80              RemoveAssignment(curDest);
 81              AddAssignment(dest);
 82  
 83              curDest = dest;
 84          }
 85  
 86          public readonly void SetSource(int index, Operand src)
 87          {
 88              ref Operand curSrc = ref SourcesUnsafe[index];
 89  
 90              RemoveUse(curSrc);
 91              AddUse(src);
 92  
 93              curSrc = src;
 94          }
 95  
 96          private readonly void RemoveOldDestinations()
 97          {
 98              for (int i = 0; i < _data->DestinationsCount; i++)
 99              {
100                  RemoveAssignment(_data->Destinations[i]);
101              }
102          }
103  
104          public readonly void SetDestination(Operand dest)
105          {
106              RemoveOldDestinations();
107  
108              if (dest == default)
109              {
110                  _data->DestinationsCount = 0;
111              }
112              else
113              {
114                  EnsureCapacity(ref _data->Destinations, ref _data->DestinationsCount, 1);
115  
116                  _data->Destinations[0] = dest;
117  
118                  AddAssignment(dest);
119              }
120          }
121  
122          public readonly void SetDestinations(Operand[] dests)
123          {
124              RemoveOldDestinations();
125  
126              EnsureCapacity(ref _data->Destinations, ref _data->DestinationsCount, dests.Length);
127  
128              for (int index = 0; index < dests.Length; index++)
129              {
130                  Operand newOp = dests[index];
131  
132                  _data->Destinations[index] = newOp;
133  
134                  AddAssignment(newOp);
135              }
136          }
137  
138          private readonly void RemoveOldSources()
139          {
140              for (int index = 0; index < _data->SourcesCount; index++)
141              {
142                  RemoveUse(_data->Sources[index]);
143              }
144          }
145  
146          public readonly void SetSource(Operand src)
147          {
148              RemoveOldSources();
149  
150              if (src == default)
151              {
152                  _data->SourcesCount = 0;
153              }
154              else
155              {
156                  EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, 1);
157  
158                  _data->Sources[0] = src;
159  
160                  AddUse(src);
161              }
162          }
163  
164          public readonly void SetSources(Operand[] srcs)
165          {
166              RemoveOldSources();
167  
168              EnsureCapacity(ref _data->Sources, ref _data->SourcesCount, srcs.Length);
169  
170              for (int index = 0; index < srcs.Length; index++)
171              {
172                  Operand newOp = srcs[index];
173  
174                  _data->Sources[index] = newOp;
175  
176                  AddUse(newOp);
177              }
178          }
179  
180          public void TurnIntoCopy(Operand source)
181          {
182              Instruction = Instruction.Copy;
183  
184              SetSource(source);
185          }
186  
187          private readonly void AddAssignment(Operand op)
188          {
189              if (op != default)
190              {
191                  op.AddAssignment(this);
192              }
193          }
194  
195          private readonly void RemoveAssignment(Operand op)
196          {
197              if (op != default)
198              {
199                  op.RemoveAssignment(this);
200              }
201          }
202  
203          private readonly void AddUse(Operand op)
204          {
205              if (op != default)
206              {
207                  op.AddUse(this);
208              }
209          }
210  
211          private readonly void RemoveUse(Operand op)
212          {
213              if (op != default)
214              {
215                  op.RemoveUse(this);
216              }
217          }
218  
219          public readonly bool Equals(Operation operation)
220          {
221              return operation._data == _data;
222          }
223  
224          public readonly override bool Equals(object obj)
225          {
226              return obj is Operation operation && Equals(operation);
227          }
228  
229          public readonly override int GetHashCode()
230          {
231              return HashCode.Combine((IntPtr)_data);
232          }
233  
234          public static bool operator ==(Operation a, Operation b)
235          {
236              return a.Equals(b);
237          }
238  
239          public static bool operator !=(Operation a, Operation b)
240          {
241              return !a.Equals(b);
242          }
243  
244          [MethodImpl(MethodImplOptions.AggressiveInlining)]
245          private static void EnsureCapacity(ref Operand* list, ref ushort capacity, int newCapacity)
246          {
247              if (newCapacity > ushort.MaxValue)
248              {
249                  ThrowOverflow(newCapacity);
250              }
251              // We only need to allocate a new buffer if we're increasing the size.
252              else if (newCapacity > capacity)
253              {
254                  list = Allocators.References.Allocate<Operand>((uint)newCapacity);
255              }
256  
257              capacity = (ushort)newCapacity;
258          }
259  
260          private static void ThrowOverflow(int count) =>
261              throw new OverflowException($"Exceeded maximum size for Source or Destinations. Required {count}.");
262  
263          public static class Factory
264          {
265              private static Operation Make(Instruction inst, int destCount, int srcCount)
266              {
267                  Data* data = Allocators.Operations.Allocate<Data>();
268                  *data = default;
269  
270                  Operation result = new()
271                  {
272                      _data = data,
273                      Instruction = inst,
274                  };
275  
276                  EnsureCapacity(ref result._data->Destinations, ref result._data->DestinationsCount, destCount);
277                  EnsureCapacity(ref result._data->Sources, ref result._data->SourcesCount, srcCount);
278  
279                  result.DestinationsUnsafe.Clear();
280                  result.SourcesUnsafe.Clear();
281  
282                  return result;
283              }
284  
285              public static Operation Operation(Instruction inst, Operand dest)
286              {
287                  Operation result = Make(inst, 0, 0);
288                  result.SetDestination(dest);
289                  return result;
290              }
291  
292              public static Operation Operation(Instruction inst, Operand dest, Operand src0)
293              {
294                  Operation result = Make(inst, 0, 1);
295                  result.SetDestination(dest);
296                  result.SetSource(0, src0);
297                  return result;
298              }
299  
300              public static Operation Operation(Instruction inst, Operand dest, Operand src0, Operand src1)
301              {
302                  Operation result = Make(inst, 0, 2);
303                  result.SetDestination(dest);
304                  result.SetSource(0, src0);
305                  result.SetSource(1, src1);
306                  return result;
307              }
308  
309              public static Operation Operation(Instruction inst, Operand dest, Operand src0, Operand src1, Operand src2)
310              {
311                  Operation result = Make(inst, 0, 3);
312                  result.SetDestination(dest);
313                  result.SetSource(0, src0);
314                  result.SetSource(1, src1);
315                  result.SetSource(2, src2);
316                  return result;
317              }
318  
319              public static Operation Operation(Instruction inst, Operand dest, int srcCount)
320              {
321                  Operation result = Make(inst, 0, srcCount);
322                  result.SetDestination(dest);
323                  return result;
324              }
325  
326              public static Operation Operation(Instruction inst, Operand dest, Operand[] srcs)
327              {
328                  Operation result = Make(inst, 0, srcs.Length);
329  
330                  result.SetDestination(dest);
331  
332                  for (int index = 0; index < srcs.Length; index++)
333                  {
334                      result.SetSource(index, srcs[index]);
335                  }
336  
337                  return result;
338              }
339  
340              public static Operation Operation(Intrinsic intrin, Operand dest, params Operand[] srcs)
341              {
342                  Operation result = Make(Instruction.Extended, 0, srcs.Length);
343  
344                  result.Intrinsic = intrin;
345                  result.SetDestination(dest);
346  
347                  for (int index = 0; index < srcs.Length; index++)
348                  {
349                      result.SetSource(index, srcs[index]);
350                  }
351  
352                  return result;
353              }
354  
355              public static Operation Operation(Instruction inst, Operand[] dests, Operand[] srcs)
356              {
357                  Operation result = Make(inst, dests.Length, srcs.Length);
358  
359                  for (int index = 0; index < dests.Length; index++)
360                  {
361                      result.SetDestination(index, dests[index]);
362                  }
363  
364                  for (int index = 0; index < srcs.Length; index++)
365                  {
366                      result.SetSource(index, srcs[index]);
367                  }
368  
369                  return result;
370              }
371  
372              public static Operation PhiOperation(Operand dest, int srcCount)
373              {
374                  return Operation(Instruction.Phi, dest, srcCount * 2);
375              }
376          }
377      }
378  }