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 }