LiveInterval.cs
1 using ARMeilleure.IntermediateRepresentation; 2 using System; 3 using System.Collections.Generic; 4 using System.Diagnostics; 5 6 namespace ARMeilleure.CodeGen.RegisterAllocators 7 { 8 unsafe readonly struct LiveInterval : IComparable<LiveInterval> 9 { 10 public const int NotFound = -1; 11 12 private struct Data 13 { 14 public int End; 15 public int SpillOffset; 16 17 public LiveRange FirstRange; 18 public LiveRange PrevRange; 19 public LiveRange CurrRange; 20 21 public LiveInterval Parent; 22 public LiveInterval CopySource; 23 24 public UseList Uses; 25 public LiveIntervalList Children; 26 27 public Operand Local; 28 public Register Register; 29 30 public bool IsFixed; 31 public bool IsFixedAndUsed; 32 } 33 34 private readonly Data* _data; 35 36 private ref int End => ref _data->End; 37 private ref LiveRange FirstRange => ref _data->FirstRange; 38 private ref LiveRange CurrRange => ref _data->CurrRange; 39 private ref LiveRange PrevRange => ref _data->PrevRange; 40 private ref LiveInterval Parent => ref _data->Parent; 41 private ref LiveInterval CopySource => ref _data->CopySource; 42 private ref UseList Uses => ref _data->Uses; 43 private ref LiveIntervalList Children => ref _data->Children; 44 45 public Operand Local => _data->Local; 46 public ref Register Register => ref _data->Register; 47 public ref int SpillOffset => ref _data->SpillOffset; 48 49 public bool IsFixed => _data->IsFixed; 50 public ref bool IsFixedAndUsed => ref _data->IsFixedAndUsed; 51 public bool IsEmpty => FirstRange == default; 52 public bool IsSplit => Children.Count != 0; 53 public bool IsSpilled => SpillOffset != -1; 54 55 public int UsesCount => Uses.Count; 56 57 public LiveInterval(Operand local = default, LiveInterval parent = default) 58 { 59 _data = Allocators.LiveIntervals.Allocate<Data>(); 60 *_data = default; 61 62 _data->IsFixed = false; 63 _data->Local = local; 64 65 Parent = parent == default ? this : parent; 66 Uses = new UseList(); 67 Children = new LiveIntervalList(); 68 69 FirstRange = default; 70 CurrRange = default; 71 PrevRange = default; 72 73 SpillOffset = -1; 74 } 75 76 public LiveInterval(Register register) : this(local: default, parent: default) 77 { 78 _data->IsFixed = true; 79 80 Register = register; 81 } 82 83 public void SetCopySource(LiveInterval copySource) 84 { 85 CopySource = copySource; 86 } 87 88 public bool TryGetCopySourceRegister(out int copySourceRegIndex) 89 { 90 if (CopySource._data != null) 91 { 92 copySourceRegIndex = CopySource.Register.Index; 93 94 return true; 95 } 96 97 copySourceRegIndex = 0; 98 99 return false; 100 } 101 102 public void Reset() 103 { 104 PrevRange = default; 105 CurrRange = FirstRange; 106 } 107 108 public void Forward(int position) 109 { 110 LiveRange prev = PrevRange; 111 LiveRange curr = CurrRange; 112 113 while (curr != default && curr.Start < position && !curr.Overlaps(position)) 114 { 115 prev = curr; 116 curr = curr.Next; 117 } 118 119 PrevRange = prev; 120 CurrRange = curr; 121 } 122 123 public int GetStart() 124 { 125 Debug.Assert(!IsEmpty, "Empty LiveInterval cannot have a start position."); 126 127 return FirstRange.Start; 128 } 129 130 public void SetStart(int position) 131 { 132 if (FirstRange != default) 133 { 134 Debug.Assert(position != FirstRange.End); 135 136 FirstRange.Start = position; 137 } 138 else 139 { 140 FirstRange = new LiveRange(position, position + 1); 141 End = position + 1; 142 } 143 } 144 145 public int GetEnd() 146 { 147 Debug.Assert(!IsEmpty, "Empty LiveInterval cannot have an end position."); 148 149 return End; 150 } 151 152 public void AddRange(int start, int end) 153 { 154 Debug.Assert(start < end, $"Invalid range start position {start}, {end}"); 155 156 if (FirstRange != default) 157 { 158 // If the new range ends exactly where the first range start, then coalesce together. 159 if (end == FirstRange.Start) 160 { 161 FirstRange.Start = start; 162 163 return; 164 } 165 // If the new range is already contained, then coalesce together. 166 else if (FirstRange.Overlaps(start, end)) 167 { 168 FirstRange.Start = Math.Min(FirstRange.Start, start); 169 FirstRange.End = Math.Max(FirstRange.End, end); 170 End = Math.Max(End, end); 171 172 Debug.Assert(FirstRange.Next == default || !FirstRange.Overlaps(FirstRange.Next)); 173 return; 174 } 175 } 176 177 FirstRange = new LiveRange(start, end, FirstRange); 178 End = Math.Max(End, end); 179 180 Debug.Assert(FirstRange.Next == default || !FirstRange.Overlaps(FirstRange.Next)); 181 } 182 183 public void AddUsePosition(int position) 184 { 185 Uses.Add(position); 186 } 187 188 public bool Overlaps(int position) 189 { 190 LiveRange curr = CurrRange; 191 192 while (curr != default && curr.Start <= position) 193 { 194 if (curr.Overlaps(position)) 195 { 196 return true; 197 } 198 199 curr = curr.Next; 200 } 201 202 return false; 203 } 204 205 public bool Overlaps(LiveInterval other) 206 { 207 return GetOverlapPosition(other) != NotFound; 208 } 209 210 public int GetOverlapPosition(LiveInterval other) 211 { 212 LiveRange a = CurrRange; 213 LiveRange b = other.CurrRange; 214 215 while (a != default) 216 { 217 while (b != default && b.Start < a.Start) 218 { 219 if (a.Overlaps(b)) 220 { 221 return a.Start; 222 } 223 224 b = b.Next; 225 } 226 227 if (b == default) 228 { 229 break; 230 } 231 else if (a.Overlaps(b)) 232 { 233 return a.Start; 234 } 235 236 a = a.Next; 237 } 238 239 return NotFound; 240 } 241 242 public ReadOnlySpan<LiveInterval> SplitChildren() 243 { 244 return Parent.Children.Span; 245 } 246 247 public ReadOnlySpan<int> UsePositions() 248 { 249 return Uses.Span; 250 } 251 252 public int FirstUse() 253 { 254 return Uses.FirstUse; 255 } 256 257 public int NextUseAfter(int position) 258 { 259 return Uses.NextUse(position); 260 } 261 262 public LiveInterval Split(int position) 263 { 264 LiveInterval result = new(Local, Parent) 265 { 266 End = End, 267 }; 268 269 LiveRange prev = PrevRange; 270 LiveRange curr = CurrRange; 271 272 while (curr != default && curr.Start < position && !curr.Overlaps(position)) 273 { 274 prev = curr; 275 curr = curr.Next; 276 } 277 278 if (curr.Start >= position) 279 { 280 prev.Next = default; 281 282 result.FirstRange = curr; 283 284 End = prev.End; 285 } 286 else 287 { 288 result.FirstRange = new LiveRange(position, curr.End, curr.Next); 289 290 curr.End = position; 291 curr.Next = default; 292 293 End = curr.End; 294 } 295 296 result.Uses = Uses.Split(position); 297 298 AddSplitChild(result); 299 300 Debug.Assert(!IsEmpty, "Left interval is empty after split."); 301 Debug.Assert(!result.IsEmpty, "Right interval is empty after split."); 302 303 // Make sure the iterator in the new split is pointing to the start. 304 result.Reset(); 305 306 return result; 307 } 308 309 private void AddSplitChild(LiveInterval child) 310 { 311 Debug.Assert(!child.IsEmpty, "Trying to insert an empty interval."); 312 313 Parent.Children.Add(child); 314 } 315 316 public LiveInterval GetSplitChild(int position) 317 { 318 if (Overlaps(position)) 319 { 320 return this; 321 } 322 323 foreach (LiveInterval splitChild in SplitChildren()) 324 { 325 if (splitChild.Overlaps(position)) 326 { 327 return splitChild; 328 } 329 else if (splitChild.GetStart() > position) 330 { 331 break; 332 } 333 } 334 335 return default; 336 } 337 338 public bool TrySpillWithSiblingOffset() 339 { 340 foreach (LiveInterval splitChild in SplitChildren()) 341 { 342 if (splitChild.IsSpilled) 343 { 344 Spill(splitChild.SpillOffset); 345 346 return true; 347 } 348 } 349 350 return false; 351 } 352 353 public void Spill(int offset) 354 { 355 SpillOffset = offset; 356 } 357 358 public int CompareTo(LiveInterval interval) 359 { 360 if (FirstRange == default || interval.FirstRange == default) 361 { 362 return 0; 363 } 364 365 return GetStart().CompareTo(interval.GetStart()); 366 } 367 368 public bool Equals(LiveInterval interval) 369 { 370 return interval._data == _data; 371 } 372 373 public override bool Equals(object obj) 374 { 375 return obj is LiveInterval interval && Equals(interval); 376 } 377 378 public static bool operator ==(LiveInterval a, LiveInterval b) 379 { 380 return a.Equals(b); 381 } 382 383 public static bool operator !=(LiveInterval a, LiveInterval b) 384 { 385 return !a.Equals(b); 386 } 387 388 public override int GetHashCode() 389 { 390 return HashCode.Combine((IntPtr)_data); 391 } 392 393 public override string ToString() 394 { 395 LiveInterval self = this; 396 397 IEnumerable<string> GetRanges() 398 { 399 LiveRange curr = self.CurrRange; 400 401 while (curr != default) 402 { 403 if (curr == self.CurrRange) 404 { 405 yield return "*" + curr; 406 } 407 else 408 { 409 yield return curr.ToString(); 410 } 411 412 curr = curr.Next; 413 } 414 } 415 416 return string.Join(", ", GetRanges()); 417 } 418 } 419 }