A game about forced loneliness, made by TACStudios
at master 1597 lines 64 kB view raw
1#if UNITY_EDITOR || BURST_INTERNAL 2using System; 3using System.Collections.Generic; 4using System.Diagnostics; 5using System.Runtime.InteropServices; 6using System.Text; 7using Debug = UnityEngine.Debug; 8 9namespace Unity.Burst.Editor 10{ 11 /// <summary> 12 /// Disassembler for Intel and ARM 13 /// </summary> 14 internal partial class BurstDisassembler 15 { 16 // The following member need to be reset/clear on each Reset() 17 private readonly Dictionary<int, string> _fileName; 18 private readonly Dictionary<int, string[]> _fileList; 19 private readonly List<AsmToken> _tokens; 20 private readonly List<AsmBlock> _blocks; 21 private readonly List<string> _blockToString; 22 private readonly List<int> _columnIndices; 23 private readonly List<AsmLine> _lines; 24 internal UsedRegisters _registersUsedAtLine; 25 private readonly DictionaryGlobalLabel _globalLabels; 26 private readonly List<TempLabelRef> _tempLabelRefs; 27 private readonly Dictionary<int, StringSlice> _mapBlockIndexToGlobalLabel; 28 private DictionaryLocalLabel _currentDictLocalLabel; 29 public bool IsInitialized { get; private set; } 30 31 // ^^^ 32 private string _input; 33 private AsmKind _inputAsmKind; 34 internal readonly StringBuilder _output; 35 private bool _colored; 36 37 // This is used to aligned instructions and there operands so they look like this 38 // 39 // mulps x,x,x 40 // shufbps x,x,x 41 // 42 // instead of 43 // 44 // mulps x,x,x 45 // shufbps x,x,x 46 // 47 // Notice if instruction name is longer than this no alignment will be done. 48 private const int InstructionAlignment = 10; 49 50 private static readonly StringSlice CVLocDirective = new StringSlice(".cv_loc"); 51 52 // Colors used for the tokens 53 // TODO: Make this configurable via some editor settings? 54 private const string DarkColorLineDirective = "#FFFF00"; 55 private const string DarkColorDirective = "#CCCCCC"; 56 private const string DarkColorIdentifier = "#d4d4d4"; 57 private const string DarkColorQualifier = "#DCDCAA"; 58 private const string DarkColorInstruction = "#4EC9B0"; 59 internal const string DarkColorInstructionSIMD = "#C586C0"; 60 internal const string DarkColorInstructionSIMDPacked = "#A586C0"; 61 internal const string DarkColorInstructionSIMDScalar = "#E586C0"; 62 private const string DarkColorRegister = "#d7ba7d"; 63 private const string DarkColorNumber = "#9cdcfe"; 64 private const string DarkColorString = "#ce9178"; 65 private const string DarkColorComment = "#6A9955"; 66 67 private const string LightColorLineDirective = "#888800"; 68 private const string LightColorDirective = "#444444"; 69 private const string LightColorIdentifier = "#1c1c1c"; 70 private const string LightColorQualifier = "#267f99"; 71 private const string LightColorInstruction = "#0451a5"; 72 private const string LightColorInstructionSIMD = "#0000ff"; 73 private const string LightColorInstructionSIMDPacked = "#8000ff"; 74 private const string LightColorInstructionSIMDScalar = "#8050ff"; 75 private const string LightColorRegister = "#811f3f"; 76 private const string LightColorNumber = "#007ACC"; 77 private const string LightColorString = "#a31515"; 78 private const string LightColorComment = "#008000"; 79 80 private string ColorLineDirective; 81 private string ColorDirective; 82 private string ColorIdentifier; 83 private string ColorQualifier; 84 private string ColorInstruction; 85 private string ColorInstructionSIMD; 86 private string ColorInstructionSIMDPacked; 87 private string ColorInstructionSIMDScalar; 88 private string ColorRegister; 89 private string ColorNumber; 90 private string ColorString; 91 private string ColorComment; 92 93 private char _commentStart; 94 95 public BurstDisassembler() 96 { 97 _fileName = new Dictionary<int, string>(); 98 _fileList = new Dictionary<int, string[]>(); 99 _tokens = new List<AsmToken>(65536); 100 _blocks = new List<AsmBlock>(128); 101 _blockToString = new List<string>(128); 102 _columnIndices = new List<int>(65536); 103 _lines = new List<AsmLine>(4096); 104 _registersUsedAtLine = new UsedRegisters(4096); 105 _tempLabelRefs = new List<TempLabelRef>(4096); 106 _globalLabels = new DictionaryGlobalLabel(128); 107 _mapBlockIndexToGlobalLabel = new Dictionary<int, StringSlice>(128); 108 _output = new StringBuilder(); 109 } 110 111 internal List<int> ColumnIndices => _columnIndices; 112 113 /// <summary> 114 /// Gets all the blocks. 115 /// </summary> 116 public List<AsmBlock> Blocks => _blocks; 117 118 /// <summary> 119 /// Gets whether the disassembly is colored. 120 /// </summary> 121 public bool IsColored => _colored; 122 123 /// <summary> 124 /// Gets all the lines for all the blocks. 125 /// </summary> 126 public List<AsmLine> Lines => _lines; 127 128 /// <summary> 129 /// Gets all the tokens 130 /// </summary> 131 public List<AsmToken> Tokens => _tokens; 132 133 public int LineUsedReg(int lineIdx, string reg) => _registersUsedAtLine.RegisterMatch(lineIdx, reg); 134 public bool LineUsesRegs(int lineIdx, out List<string> usedRegs) => _registersUsedAtLine.LineContainsRegs(lineIdx, out usedRegs); 135 public List<string> CleanRegs(List<string> regs) => _registersUsedAtLine.CleanRegs(regs); 136 137 public int GetRegisterTokenIndex(AsmLine line, string reg, int startIndex = 0) 138 { 139 var idx = -1; 140 141 var i = Math.Max(line.TokenIndex, startIndex); 142 var len = line.TokenIndex + line.Length; 143 for (; i < len; i++) 144 { 145 var token = Tokens[i]; 146 if (_registersUsedAtLine.RegisterEquality(reg, GetTokenAsText(token))) 147 { 148 idx = i; 149 break; 150 } 151 } 152 153 return idx; 154 } 155 156 /// <summary> 157 /// Get a token index for a particular block, line number and column number. 158 /// </summary> 159 /// <param name="blockIndex"></param> 160 /// <param name="line"></param> 161 /// <param name="column"></param> 162 /// <param name="lineIndex">Returns the line index to query <see cref="Lines"/></param> 163 /// <returns>The token index to use with <see cref="GetToken"/> or -1 if the line, column was not found.</returns> 164 public int GetTokenIndexFromColumn(int blockIndex, int line, int column, out int lineIndex) 165 { 166 lineIndex = -1; 167 var block = _blocks[blockIndex]; 168 var lineStartIndex = block.LineIndex + line; 169 var asmLine = _lines[lineStartIndex]; 170 if (asmLine.Kind != AsmLineKind.SourceFileLocation) 171 { 172 var columnIndex = asmLine.ColumnIndex; 173 for (int j = 1; j < asmLine.Length; j++) 174 { 175 // _columnIndices doesn't have an index for the first token (because the column is always 0) 176 var tokenColumn = _columnIndices[columnIndex + j - 1]; 177 var token = GetToken(asmLine.TokenIndex + j); 178 179 if (tokenColumn <= column && column < tokenColumn + token.Length) 180 { 181 lineIndex = lineStartIndex; 182 return asmLine.TokenIndex + j; 183 } 184 } 185 } 186 return -1; 187 } 188 189 /// <summary> 190 /// Gets or renders a particular block to text without caching the result. 191 /// </summary> 192 /// <param name="blockIndex">The block to render.</param> 193 /// <param name="colored">Whether output should be colored.</param> 194 /// <returns>A string representation of the block.</returns> 195 public string GetOrRenderBlockToTextUncached(int blockIndex, bool colored) 196 { 197 return RenderBlock(blockIndex, colored); 198 } 199 200 /// <summary> 201 /// Gets or renders a particular block to text (colored if specified at <see cref="Initialize"/> time) 202 /// </summary> 203 /// <param name="blockIndex">The block to render.</param> 204 /// <returns>A string representation of the block.</returns> 205 public string GetOrRenderBlockToText(int blockIndex) 206 { 207 var str = _blockToString[blockIndex]; 208 if (str == null) 209 { 210 str = RenderBlock(blockIndex, _colored); 211 _blockToString[blockIndex] = str; 212 } 213 return str; 214 } 215 216 /// <summary> 217 /// Gets a token at the specified token index. 218 /// </summary> 219 /// <param name="tokenIndex">The token index</param> 220 /// <returns>The token available at the specified index</returns> 221 public AsmToken GetToken(int tokenIndex) 222 { 223 return _tokens[tokenIndex]; 224 } 225 226 /// <summary> 227 /// Returns the text representation of the token at the specified index 228 /// </summary> 229 /// <param name="tokenIndex"></param> 230 /// <returns></returns> 231 public StringSlice GetTokenAsTextSlice(int tokenIndex) 232 { 233 return _tokens[tokenIndex].Slice(_input); 234 } 235 236 /// <summary> 237 /// Returns the text representation of the specified token. 238 /// </summary> 239 public StringSlice GetTokenAsTextSlice(AsmToken token) 240 { 241 return token.Slice(_input); 242 } 243 244 /// <summary> 245 /// Returns the text representation of the specified token. 246 /// </summary> 247 public string GetTokenAsText(AsmToken token) 248 { 249 return token.ToString(_input); 250 } 251 252 /// <summary> 253 /// Try and get description of <see cref="instruction"/>. 254 /// </summary> 255 /// <param name="instruction">Instruction to query information about.</param> 256 /// <param name="info">If instruction present the queried information, else default string.</param> 257 /// <returns>Whether instruction was present in burst disassembler core.</returns> 258 internal bool GetInstructionInformation(string instruction, out string info) 259 { 260 switch (_inputAsmKind) 261 { 262 case AsmKind.Intel: 263 return X86AsmInstructionInfo.GetX86InstructionInfo(instruction, out info); 264 case AsmKind.ARM: 265 return ARM64InstructionInfo.GetARM64Info(instruction, out info); 266 case AsmKind.LLVMIR: 267 return LLVMIRInstructionInfo.GetLLVMIRInfo(instruction, out info); 268 case AsmKind.Wasm: 269 return WasmInstructionInfo.GetWasmInfo(instruction, out info); 270 default: 271 throw new InvalidOperationException($"No instruction information for {_inputAsmKind}"); 272 } 273 } 274 275 /// <summary> 276 /// Initialize the disassembler with the input and parametesr. 277 /// </summary> 278 /// <param name="input"></param> 279 /// <param name="asmKind"></param> 280 /// <param name="useDarkSkin"></param> 281 /// <param name="useSyntaxColoring"></param> 282 /// <param name="smellTest"></param> 283 /// <returns></returns> 284 public bool Initialize(string input, AsmKind asmKind, bool useDarkSkin = true, bool useSyntaxColoring = true, bool smellTest = false) 285 { 286 try 287 { 288 InitializeImpl(input, asmKind, useDarkSkin, useSyntaxColoring, smellTest); 289 IsInitialized = true; 290 } 291 catch (Exception ex) 292 { 293 Reset(); 294#if BURST_INTERNAL 295 throw new InvalidOperationException($"Error while trying to disassemble the input: {ex}"); 296#else 297 UnityEngine.Debug.Log($"Error while trying to disassemble the input: {ex}"); 298#endif 299 } 300 301 return IsInitialized; 302 } 303 304 /// <summary> 305 /// Helper method to output the full (colored) text as we did before. 306 /// 307 /// This method will be deprecated. Just here for testing during the transition. 308 /// </summary> 309 public string RenderFullText() 310 { 311 // If not initialized correctly (disassembly failed), return the input string as-is 312 if (!IsInitialized) return _input ?? string.Empty; 313 314 var builder = new StringBuilder(); 315 for (int i = 0; i < _blocks.Count; i++) 316 { 317 var text = GetOrRenderBlockToText(i); 318 builder.Append(text); 319 } 320 return builder.ToString(); 321 } 322 323 private void Reset() 324 { 325 _registersUsedAtLine.Clear(); 326 _fileList.Clear(); 327 _fileName.Clear(); 328 _tokens.Clear(); 329 _blocks.Clear(); 330 _blockTextIdxs.Clear(); 331 _blockToString.Clear(); 332 _columnIndices.Clear(); 333 _lines.Clear(); 334 _tempLabelRefs.Clear(); 335 _globalLabels.Clear(); 336 _mapBlockIndexToGlobalLabel.Clear(); 337 _currentDictLocalLabel = null; 338 IsInitialized = false; 339 } 340 341 private AsmTokenKindProvider _tokenProvider = null; 342 343 private void InitializeImpl(string input, AsmKind asmKind, bool useDarkSkin = true, bool useSyntaxColoring = true, bool smellTest=false) 344 { 345 _commentStart = (asmKind == AsmKind.Intel || asmKind == AsmKind.Wasm) ? '#' : ';'; 346 UseSkin(useDarkSkin, smellTest); 347 _colored = useSyntaxColoring; 348 _tokenProvider = InitializeInput(input, asmKind); 349 _registersUsedAtLine.AddTokenProvider(_tokenProvider); 350 ParseAndProcessTokens(_tokenProvider); 351 } 352 353 /// <summary> 354 /// Finds the block index encapsulating <see cref="textIdx"/>. 355 /// </summary> 356 /// <param name="textIdx">Text index relative to <see cref="_input"/>.</param> 357 /// <param name="start">Left-most block index to search within.</param> 358 /// <returns>(block index, blocks start index in <see cref="_input"/>)</returns> 359 public (int idx, int l) GetBlockIdxFromTextIdx(int textIdx) 360 { 361 return GetBlockIdxFromTextIdx(textIdx, 0); 362 } 363 364 365 /// <summary> 366 /// Finds the block index encapsulating <see cref="textIdx"/>. 367 /// </summary> 368 /// <param name="textIdx">Text index relative to <see cref="_input"/>.</param> 369 /// <param name="start">Left-most block index to search within.</param> 370 /// <returns>(block index, blocks start index in <see cref="_input"/>)</returns> 371 public (int idx, int l) GetBlockIdxFromTextIdx(int textIdx, int start) 372 { 373 int end = _blockTextIdxs.Count-1; 374 while (start <= end) 375 { 376 int mid = (end + start) / 2; 377 var (startIdx, endIdx) = _blockTextIdxs[mid]; 378 379 if (startIdx <= textIdx && textIdx <= endIdx) 380 { 381 return (mid, startIdx); 382 } 383 384 if (endIdx < textIdx) 385 { 386 start = mid + 1; 387 } 388 else 389 { 390 end = mid - 1; 391 } 392 } 393 return (-1, -1); 394 } 395 396 private bool _smellTest; 397 private void UseSkin(bool useDarkSkin, bool smellTest) 398 { 399 _smellTest = smellTest; 400 if (useDarkSkin) 401 { 402 ColorLineDirective = DarkColorLineDirective; 403 ColorDirective = DarkColorDirective; 404 ColorIdentifier = DarkColorIdentifier; 405 ColorQualifier = DarkColorQualifier; 406 ColorInstruction = DarkColorInstruction; 407 ColorInstructionSIMD = DarkColorInstructionSIMD; 408 ColorInstructionSIMDPacked = DarkColorInstructionSIMDPacked; 409 ColorInstructionSIMDScalar = DarkColorInstructionSIMDScalar; 410 ColorRegister = DarkColorRegister; 411 ColorNumber = DarkColorNumber; 412 ColorString = DarkColorString; 413 ColorComment = DarkColorComment; 414 } 415 else 416 { 417 ColorLineDirective = LightColorLineDirective; 418 ColorDirective = LightColorDirective; 419 ColorIdentifier = LightColorIdentifier; 420 ColorQualifier = LightColorQualifier; 421 ColorInstruction = LightColorInstruction; 422 ColorInstructionSIMD = LightColorInstructionSIMD; 423 ColorInstructionSIMDPacked = LightColorInstructionSIMDPacked; 424 ColorInstructionSIMDScalar = LightColorInstructionSIMDScalar; 425 ColorRegister = LightColorRegister; 426 ColorNumber = LightColorNumber; 427 ColorString = LightColorString; 428 ColorComment = LightColorComment; 429 } 430 } 431 432 private int AlignInstruction(StringBuilder output, int instructionLength, AsmKind asmKind) 433 { 434 // Only support Intel for now 435 if (instructionLength >= InstructionAlignment || asmKind != AsmKind.Intel) 436 return 0; 437 438 int align = InstructionAlignment - instructionLength; 439 output.Append(' ', align); 440 return align; 441 } 442 443 private AsmTokenKindProvider InitializeInput(string input, AsmKind asmKind) 444 { 445 AsmTokenKindProvider asmTokenProvider = null; 446 447 _input = input; 448 _inputAsmKind = asmKind; 449 450 switch (asmKind) 451 { 452 case AsmKind.Intel: 453 asmTokenProvider = (AsmTokenKindProvider)X86AsmTokenKindProvider.Instance; 454 break; 455 case AsmKind.ARM: 456 asmTokenProvider = (AsmTokenKindProvider)ARM64AsmTokenKindProvider.Instance; 457 break; 458 case AsmKind.Wasm: 459 asmTokenProvider = (AsmTokenKindProvider)WasmAsmTokenKindProvider.Instance; 460 break; 461 case AsmKind.LLVMIR: 462 asmTokenProvider = (AsmTokenKindProvider)LLVMIRAsmTokenKindProvider.Instance; 463 break; 464 default: 465 throw new InvalidOperationException($"No {nameof(AsmTokenKindProvider)} for {asmKind}"); 466 } 467 468 return asmTokenProvider; 469 } 470 471 private int GetLineLen(in AsmLine line) 472 { 473 int len = 0; 474 int offset = line.TokenIndex; 475 int numLineTokens = line.Length; 476 for (int i = 0; i < numLineTokens; i++) 477 { 478 AsmToken token = _tokens[offset + i]; 479 len += token.Kind != AsmTokenKind.NewLine 480 ? token.Length 481 : 1; // We don't use windows line endings, but internal token might, 482 } 483 484 return len; 485 } 486 487 private void ParseAndProcessTokens(AsmTokenKindProvider asmTokenProvider) 488 { 489 Reset(); 490 491 var tokenizer = new AsmTokenizer(_input, _inputAsmKind, asmTokenProvider, _commentStart); 492 493 // Adjust token size 494 var pseudoTokenSizeMax = _input.Length / 7; 495 if (pseudoTokenSizeMax > _tokens.Capacity) 496 { 497 _tokens.Capacity = pseudoTokenSizeMax; 498 } 499 500 // Start the top-block as a directive block 501 var block = new AsmBlock { Kind = AsmBlockKind.Block }; 502 AsmLine line = default; 503 var blockKindDetectFlags = BlockKindDetectFlags.None; 504 505 // Skip first line 506 // Don't tokenize the first line that contains e.g: 507 // While compiling job: System.Single BurstJobTester/MyJob::CheckFmaSlow(System.Single,System.Single,System.Single) 508 while (tokenizer.TryGetNextToken(out var token)) 509 { 510 if (token.Kind == AsmTokenKind.NewLine) 511 { 512 break; 513 } 514 } 515 516 // Read all tokens 517 // Create blocks and lines on the fly, record functions 518 int totalIdx = 0; 519 int blockStartIdx = 0; 520 bool newLine = false; 521 var (possiblyRemoveAlignment, addedAlignment) = (false, 0); 522 while (tokenizer.TryGetNextToken(out var token)) 523 { 524 var tokenIndex = _tokens.Count; 525 _tokens.Add(token); 526 527 if (newLine) 528 { 529 if (possiblyRemoveAlignment) 530 { 531 // Alignment was added just before a newline 532 totalIdx -= addedAlignment; 533 } 534 535 // Push new line 536 if (line.Kind == AsmLineKind.SourceFile) 537 { 538 // Have to remove the line from totalIdx, for proper block idx saving. 539 totalIdx -= GetLineLen(line); 540 ProcessSourceFile(ref line); 541 // We drop this line, we don't store SourceFile line as-is but just below as SourceFileLocation 542 } 543 else 544 { 545 var lineRef = new AsmLineRef(_blocks.Count, block.Length); 546 if (line.Kind == AsmLineKind.SourceLocation) 547 { 548 // Have to remove the line from totalIdx, for proper block idx saving. 549 totalIdx -= GetLineLen(line); 550 ProcessSourceLocation(ref line, ref totalIdx); 551 // after this, the line is now a SourceFileLocation 552 } 553 else if (line.Kind == AsmLineKind.LabelDeclaration) 554 { 555 // Record labels (global and locals) 556 ProcessLabelDeclaration(lineRef, line); 557 } 558 else if (line.Kind == AsmLineKind.CodeBranch || line.Kind == AsmLineKind.CodeJump) 559 { 560 // Record temp branch/jumps 561 ProcessJumpOrBranch(lineRef, ref line); 562 } 563 564 _lines.Add(line); 565 _registersUsedAtLine.PushLine(); 566 block.Length++; 567 } 568 569 bool previousLineWasBranch = line.Kind == AsmLineKind.CodeBranch; 570 571 // Reset the line 572 line = default; 573 line.Kind = AsmLineKind.Empty; 574 line.TokenIndex = tokenIndex; 575 // We create a new block when hitting a label declaration 576 // If the previous line was a conditional branch, it is like having an implicit label 577 if (previousLineWasBranch || token.Kind == AsmTokenKind.Label) 578 { 579 // Refine the kind of block before pushing it 580 if ((blockKindDetectFlags & BlockKindDetectFlags.Code) != 0) 581 { 582 block.Kind = AsmBlockKind.Code; 583 } 584 else if ((blockKindDetectFlags & BlockKindDetectFlags.Data) != 0) 585 { 586 block.Kind = AsmBlockKind.Data; 587 } 588 else if ((blockKindDetectFlags & BlockKindDetectFlags.Directive) != 0) 589 { 590 block.Kind = AsmBlockKind.Directive; 591 } 592 593 // Push the current block 594 _blocks.Add(block); 595 _blockTextIdxs.Add((blockStartIdx, totalIdx-1)); 596 _blockToString.Add(null); 597 598 // Create a new block 599 blockStartIdx = totalIdx; 600 block = new AsmBlock 601 { 602 Kind = AsmBlockKind.None, 603 LineIndex = _lines.Count, 604 Length = 0 605 }; 606 blockKindDetectFlags = BlockKindDetectFlags.None; 607 } 608 } 609 610 // If the current line is still undefined try to detect what kind of line we have 611 var lineKind = line.Kind; 612 if (lineKind == AsmLineKind.Empty) 613 { 614 switch (token.Kind) 615 { 616 case AsmTokenKind.Directive: 617 lineKind = AsmLineKind.Directive; 618 blockKindDetectFlags |= BlockKindDetectFlags.Directive; 619 break; 620 case AsmTokenKind.SourceFile: 621 lineKind = AsmLineKind.SourceFile; 622 break; 623 case AsmTokenKind.SourceLocation: 624 lineKind = AsmLineKind.SourceLocation; 625 blockKindDetectFlags |= BlockKindDetectFlags.Code; 626 break; 627 case AsmTokenKind.DataDirective: 628 lineKind = AsmLineKind.Data; 629 blockKindDetectFlags |= BlockKindDetectFlags.Data; 630 break; 631 case AsmTokenKind.Instruction: 632 case AsmTokenKind.InstructionSIMD: 633 lineKind = AsmLineKind.Code; 634 blockKindDetectFlags |= BlockKindDetectFlags.Code; 635 break; 636 case AsmTokenKind.BranchInstruction: 637 lineKind = AsmLineKind.CodeBranch; 638 blockKindDetectFlags |= BlockKindDetectFlags.Code; 639 break; 640 case AsmTokenKind.JumpInstruction: 641 lineKind = AsmLineKind.CodeJump; 642 blockKindDetectFlags |= BlockKindDetectFlags.Code; 643 break; 644 case AsmTokenKind.CallInstruction: 645 lineKind = AsmLineKind.CodeCall; 646 blockKindDetectFlags |= BlockKindDetectFlags.Code; 647 break; 648 case AsmTokenKind.ReturnInstruction: 649 lineKind = AsmLineKind.CodeReturn; 650 blockKindDetectFlags |= BlockKindDetectFlags.Code; 651 break; 652 case AsmTokenKind.Label: 653 lineKind = newLine ? AsmLineKind.LabelDeclaration : AsmLineKind.Empty; 654 break; 655 case AsmTokenKind.Comment: 656 lineKind = AsmLineKind.Comment; 657 break; 658 case AsmTokenKind.FunctionBegin: 659 lineKind = AsmLineKind.FunctionBegin; 660 break; 661 case AsmTokenKind.FunctionEnd: 662 lineKind = AsmLineKind.FunctionEnd; 663 break; 664 } 665 line.Kind = lineKind; 666 } 667 668 // Add alignment for it to match the output BurstDisassembler gives to the outside world 669 switch (token.Kind) 670 { 671 case AsmTokenKind.Instruction: 672 case AsmTokenKind.CallInstruction: 673 case AsmTokenKind.BranchInstruction: 674 case AsmTokenKind.JumpInstruction: 675 case AsmTokenKind.ReturnInstruction: 676 case AsmTokenKind.InstructionSIMD: 677 if (!(token.Length >= InstructionAlignment || _inputAsmKind != AsmKind.Intel)) 678 { 679 totalIdx += (InstructionAlignment - token.Length); 680 possiblyRemoveAlignment = true; 681 addedAlignment = InstructionAlignment - token.Length; 682 } 683 break; 684 // If new line is hit do not set to false, as to carry the information 685 // into the next iteration. 686 case AsmTokenKind.NewLine: 687 break; 688 default: 689 possiblyRemoveAlignment = false; 690 break; 691 } 692 693 // Add used registers to the index appropriate for specific line. 694 if (token.Kind == AsmTokenKind.Register) 695 { 696 _registersUsedAtLine.Add(_lines.Count, GetTokenAsText(token)); 697 } 698 699 line.Length++; 700 newLine = token.Kind == AsmTokenKind.NewLine; 701 totalIdx += newLine ? 1 : token.Length; 702 } 703 704 // Process the remaining line 705 if (line.Length > 0) 706 { 707 _lines.Add(line); 708 block.Length++; 709 710 _registersUsedAtLine.PushLine(); 711 } 712 713 if (block.Length > 0) 714 { 715 _blocks.Add(block); 716 _blockTextIdxs.Add((blockStartIdx, totalIdx - 1)); 717 _blockToString.Add(null); 718 } 719 720 ProcessLabelsAndCreateEdges(); 721 } 722 723 private void ProcessLabelDeclaration(in AsmLineRef lineRef, in AsmLine line) 724 { 725 var iterator = GetIterator(line); 726 727 iterator.TryGetNext(out var token); // label 728 var text = token.Slice(_input); 729 if (IsLabelLocal(text)) 730 { 731 // if ´_currentDictLocalLabel==null´ we just hit a local label prior to any global labels. 732 // So we simply create a empty global label, to hold this local: 733 if (_currentDictLocalLabel is null) 734 { 735 _currentDictLocalLabel = _globalLabels.GetOrCreate(new StringSlice(""), lineRef); 736 _mapBlockIndexToGlobalLabel[lineRef.BlockIndex] = text; 737 } 738 739 // Record local labels to the current global label dictionary 740 _currentDictLocalLabel.Add(text, lineRef); 741 } 742 else 743 { 744 // Create a local label dictionary per global label 745 _currentDictLocalLabel = _globalLabels.GetOrCreate(text, lineRef); 746 // Associate the current block index to this global index 747 _mapBlockIndexToGlobalLabel[lineRef.BlockIndex] = text; 748 } 749 } 750 751 private void ProcessJumpOrBranch(in AsmLineRef lineRef, ref AsmLine line) 752 { 753 var iterator = GetIterator(line); 754 iterator.TryGetNext(out _); // branch/jump instruction 755 756 if (iterator.TryGetNext(out var label, out var labelTokenIndex)) 757 { 758 if (label.Kind == AsmTokenKind.String || label.Kind == AsmTokenKind.Identifier || label.Kind == AsmTokenKind.Label) 759 { 760 // In case the token is not a label, convert it to a label after this 761 if (label.Kind != AsmTokenKind.Label) 762 { 763 var token = _tokens[labelTokenIndex]; 764 token = new AsmToken(AsmTokenKind.Label, token.Position, token.AlignedPosition, token.Length); 765 _tokens[labelTokenIndex] = token; 766 } 767 768 var currentGlobalBlockIndex = _currentDictLocalLabel.GlobalLabelLineRef.BlockIndex; 769 _tempLabelRefs.Add(new TempLabelRef(currentGlobalBlockIndex, lineRef, label.Position, label.Length)); 770 } 771 } 772 } 773 774 private void ProcessSourceFile(ref AsmLine line) 775 { 776 var it = GetIterator(line); 777 778 it.TryGetNext(out _); // skip .file or .cv_file 779 780 int index = 0; 781 if (it.TryGetNext(out var token) && token.Kind == AsmTokenKind.Number) 782 { 783 var numberAsStr = GetTokenAsText(token); 784 index = int.Parse(numberAsStr); 785 } 786 787 if (it.TryGetNext(out token) && token.Kind == AsmTokenKind.String) 788 { 789 var filename = GetTokenAsText(token).Trim('"').Replace('\\', '/'); 790 string[] fileLines = null; 791 792 //blockIdx += 4 + System.IO.Path.GetFileName(filename).Length;// ("=== " + filename).Length 793 try 794 { 795 if (System.IO.File.Exists(filename)) 796 { 797 fileLines = System.IO.File.ReadAllLines(filename); 798 } 799 } 800 catch 801 { 802 fileLines = null; 803 } 804 805 806 _fileName.Add(index, filename); 807 _fileList.Add(index, fileLines); 808 } 809 } 810 811 private void ProcessSourceLocation(ref AsmLine line, ref int blockIdx) 812 { 813 var it = GetIterator(line); 814 815 // .loc {fileno} {lineno} [column] [options] - 816 // .cv_loc funcid fileno lineno [column] 817 int fileno = 0; 818 int colno = 0; 819 int lineno = 0; // NB 0 indicates no information given 820 821 if (it.TryGetNext(out var token)) 822 { 823 var tokenSlice = GetTokenAsTextSlice(token); 824 if (tokenSlice == CVLocDirective) 825 { 826 // skip funcId 827 it.TryGetNext(out token); 828 } 829 } 830 831 if (it.TryGetNext(out token) && token.Kind == AsmTokenKind.Number) 832 { 833 var numberAsStr = GetTokenAsText(token); 834 fileno = int.Parse(numberAsStr); 835 } 836 837 if (it.TryGetNext(out token) && token.Kind == AsmTokenKind.Number) 838 { 839 var numberAsStr = GetTokenAsText(token); 840 lineno = int.Parse(numberAsStr); 841 } 842 843 if (it.TryGetNext(out token) && token.Kind == AsmTokenKind.Number) 844 { 845 var numberAsStr = GetTokenAsText(token); 846 colno = int.Parse(numberAsStr); 847 } 848 849 // Transform the SourceLocation into a SourceFileLocation 850 line.Kind = AsmLineKind.SourceFileLocation; 851 line.SourceFileNumber = fileno; 852 line.SourceLineNumber = lineno; 853 line.SourceColumnNumber = colno; 854 855 // Make sure blockTextIdxs are correct 856 if (fileno == 0) return; 857 blockIdx += 2 + System.IO.Path.GetFileName(_fileName[fileno]).Length; // ("; " + filename).length 858 859 if (lineno != 0) 860 { 861 blockIdx += 4 + lineno.ToString().Length + (colno + 1).ToString().Length;// "(x, y)" 862 863 if (_fileList.ContainsKey(fileno) && _fileList[fileno] != null && lineno - 1 < _fileList[fileno].Length) 864 { 865 blockIdx += _fileList[fileno][lineno - 1].Length; 866 } 867 } 868 blockIdx++; // \n 869 } 870 871 private static bool IsLabelLocal(in StringSlice slice) 872 { 873 return slice.StartsWith(".L"); 874 } 875 876 private void ProcessLabelsAndCreateEdges() 877 { 878 foreach (var tempLabelRef in _tempLabelRefs) 879 { 880 var globalBlockIndex = tempLabelRef.GlobalBlockIndex; 881 882 // Source Block + Line 883 var srcRef = tempLabelRef.LineRef; 884 var srcBlockIndex = srcRef.BlockIndex; 885 var srcLineIndex = srcRef.LineIndex; 886 var srcBlock = _blocks[srcBlockIndex]; 887 // Line where the edge occurs 888 var srcLine = _lines[srcBlock.LineIndex + srcLineIndex]; 889 890 var label = new StringSlice(_input, tempLabelRef.StringIndex, tempLabelRef.StringLength); 891 var isLocal = IsLabelLocal(label); 892 AsmLineRef destRef; 893 if (isLocal) 894 { 895 var globalLabel = _mapBlockIndexToGlobalLabel[globalBlockIndex]; 896 var localLabel = _globalLabels[globalLabel]; 897 destRef = localLabel[label]; 898 } 899 else 900 { 901 if (_globalLabels.TryGetValue(label, out var entry)) 902 { 903 destRef = entry.GlobalLabelLineRef; 904 } 905 else 906 { 907 continue; // Some global labels (at least on arm) e.g. __divsi3 are runtime library defined and not present at all in the source 908 } 909 } 910 911 // Destination Block + Line 912 var dstBlock = _blocks[destRef.BlockIndex]; 913 914 // Create edges 915 srcBlock.AddEdge(new AsmEdge(AsmEdgeKind.OutBound, srcRef, destRef)); 916 dstBlock.AddEdge(new AsmEdge(AsmEdgeKind.InBound, destRef, srcRef)); 917 918 // For conditional branches, add the false branch as well 919 // TODO: should we comment that in the meantime or? 920 if (srcLine.Kind == AsmLineKind.CodeBranch) 921 { 922 // The implicit destination block for the false branch is the next block of the source 923 // TODO: we pickup the line 0, while we might want to select the first code of line or first Label declaration 924 var blockFalseRef = new AsmLineRef(srcRef.BlockIndex + 1, 0); 925 dstBlock = _blocks[blockFalseRef.BlockIndex]; 926 927 srcBlock.AddEdge(new AsmEdge(AsmEdgeKind.OutBound, srcRef, blockFalseRef)); 928 dstBlock.AddEdge(new AsmEdge(AsmEdgeKind.InBound, blockFalseRef, srcRef)); 929 } 930 } 931 932 // Sort all edges 933 foreach (var block in Blocks) 934 { 935 block.SortEdges(); 936 } 937 } 938 939 private List<(int startIdx, int endIdx)> _blockTextIdxs = new List<(int startIdx, int endIdx)>(128); 940 941 public List<(int startIdx, int endIdx)> BlockIdxs => _blockTextIdxs; 942 943 944 private string RenderBlock(int blockIndex, bool colored) 945 { 946 var block = _blocks[blockIndex]; 947 _output.Clear(); 948 var lineStart = block.LineIndex; 949 var length = block.Length; 950 for (int i = 0; i < length; i++) 951 { 952 var line = _lines[lineStart + i]; 953 RenderLine(ref line, colored); 954 // write back the line that has been modified. But only if we run with the same color mode, 955 // that the disassembler was initialized with. 956 if (colored == _colored) _lines[lineStart + i] = line; 957 } 958 959 var str = _output.ToString(); 960 _output.Length = 0; 961 return str; 962 } 963 964 internal void RenderLine(ref AsmLine line, bool colored) 965 { 966 // Render this line with a specific renderer 967 if (line.Kind == AsmLineKind.SourceFileLocation) 968 { 969 RenderSourceFileLocation(ref line, colored); 970 return; 971 } 972 973 // Process all tokens 974 var length = line.Length; 975 int column = 0; 976 for (int i = 0; i < length; i++) 977 { 978 var token = _tokens[line.TokenIndex + i]; 979 var slice = token.Slice(_input); 980 981 // We don't record the first column because it is always 0 982 if (column > 0) 983 { 984 if (line.ColumnIndex == 0) 985 { 986 line.ColumnIndex = _columnIndices.Count; 987 } 988 _columnIndices.Add(column); 989 } 990 991 if (colored) 992 { 993 switch (token.Kind) 994 { 995 case AsmTokenKind.DataDirective: 996 case AsmTokenKind.Directive: 997 case AsmTokenKind.FunctionBegin: 998 case AsmTokenKind.FunctionEnd: 999 _output.Append("<color=").Append(ColorDirective).Append('>'); 1000 _output.Append(_input, slice.Position, slice.Length); 1001 column += slice.Length; 1002 _output.Append("</color>"); 1003 break; 1004 case AsmTokenKind.Label: 1005 case AsmTokenKind.Identifier: 1006 _output.Append("<color=").Append(ColorIdentifier).Append('>'); 1007 _output.Append(_input, slice.Position, slice.Length); 1008 column += slice.Length; 1009 _output.Append("</color>"); 1010 break; 1011 case AsmTokenKind.Qualifier: 1012 _output.Append("<color=").Append(ColorQualifier).Append('>'); 1013 _output.Append(_input, slice.Position, slice.Length); 1014 column += slice.Length; 1015 _output.Append("</color>"); 1016 break; 1017 case AsmTokenKind.Instruction: 1018 case AsmTokenKind.CallInstruction: 1019 case AsmTokenKind.BranchInstruction: 1020 case AsmTokenKind.JumpInstruction: 1021 case AsmTokenKind.ReturnInstruction: 1022 _output.Append("<color=").Append(ColorInstruction).Append('>'); 1023 _output.Append(_input, slice.Position, slice.Length); 1024 column += slice.Length; 1025 _output.Append("</color>"); 1026 if (i == length - 2) // last slice always a newline 1027 break; 1028 column += AlignInstruction(_output, slice.Length, _inputAsmKind); 1029 break; 1030 case AsmTokenKind.InstructionSIMD: 1031 // Perform smell test for simd instructions: 1032 var col = ColorInstructionSIMD; 1033 if (_smellTest) 1034 { 1035 switch (_tokenProvider.SimdKind(slice)) 1036 { 1037 case SIMDkind.Packed: 1038 col = ColorInstructionSIMDPacked; 1039 break; 1040 case SIMDkind.Scalar: 1041 col = ColorInstructionSIMDScalar; 1042 break; 1043 case SIMDkind.Infrastructure: 1044 break; 1045 } 1046 } 1047 1048 _output.Append("<color=").Append(col).Append('>'); 1049 _output.Append(_input, slice.Position, slice.Length); 1050 column += slice.Length; 1051 _output.Append("</color>"); 1052 if (i == length - 2) // last slice always newline 1053 break; 1054 column += AlignInstruction(_output, slice.Length, _inputAsmKind); 1055 break; 1056 case AsmTokenKind.Register: 1057 _output.Append("<color=").Append(ColorRegister).Append('>'); 1058 _output.Append(_input, slice.Position, slice.Length); 1059 column += slice.Length; 1060 _output.Append("</color>"); 1061 break; 1062 case AsmTokenKind.Number: 1063 _output.Append("<color=").Append(ColorNumber).Append('>'); 1064 _output.Append(_input, slice.Position, slice.Length); 1065 column += slice.Length; 1066 _output.Append("</color>"); 1067 break; 1068 case AsmTokenKind.String: 1069 _output.Append("<color=").Append(ColorString).Append('>'); 1070 _output.Append(_input, slice.Position, slice.Length); 1071 column += slice.Length; 1072 _output.Append("</color>"); 1073 break; 1074 case AsmTokenKind.Comment: 1075 _output.Append("<color=").Append(ColorComment).Append('>'); 1076 _output.Append(_input, slice.Position, slice.Length); 1077 column += slice.Length; 1078 _output.Append("</color>"); 1079 break; 1080 case AsmTokenKind.NewLine: 1081 _output.Append('\n'); 1082 break; 1083 default: 1084 _output.Append(_input, slice.Position, slice.Length); 1085 column += slice.Length; 1086 break; 1087 } 1088 } 1089 else 1090 { 1091 if (token.Kind == AsmTokenKind.NewLine) 1092 { 1093 _output.Append('\n'); 1094 } 1095 else 1096 { 1097 _output.Append(_input, slice.Position, slice.Length); 1098 column += slice.Length; 1099 } 1100 1101 // Also wants to align instructions in uncolored mode the same way as colored. 1102 switch (token.Kind) 1103 { 1104 case AsmTokenKind.Instruction: 1105 case AsmTokenKind.CallInstruction: 1106 case AsmTokenKind.BranchInstruction: 1107 case AsmTokenKind.JumpInstruction: 1108 case AsmTokenKind.ReturnInstruction: 1109 case AsmTokenKind.InstructionSIMD: 1110 // Do not add alignment to instruction with no arguments 1111 // last slice always a newline 1112 if (i == length - 2) break; 1113 column += AlignInstruction(_output, slice.Length, _inputAsmKind); 1114 break; 1115 } 1116 } 1117 } 1118 } 1119 1120 private void RenderSourceFileLocation(ref AsmLine line, bool colored) 1121 { 1122 char[] comment = {_commentStart, ' '}; 1123 var fileno = line.SourceFileNumber; 1124 var lineno = line.SourceLineNumber; 1125 var colno = line.SourceColumnNumber; 1126 1127 // If the file number is 0, skip the line 1128 if (fileno == 0) 1129 { 1130 } 1131 // If the line number is 0, then we can update the file tracking, but still not output a line 1132 else if (lineno == 0) 1133 { 1134 if (colored) _output.Append("<color=").Append(ColorLineDirective).Append('>'); 1135 _output.Append(comment).Append(System.IO.Path.GetFileName(_fileName[fileno])); 1136 if (colored) _output.Append("</color>"); 1137 } 1138 // We have a source line and number -- can we load file and extract this line? 1139 else 1140 { 1141 if (_fileList.ContainsKey(fileno) && _fileList[fileno] != null && lineno - 1 < _fileList[fileno].Length) 1142 { 1143 if (colored) _output.Append("<color=").Append(ColorLineDirective).Append('>'); 1144 _output.Append(comment).Append(System.IO.Path.GetFileName(_fileName[fileno])).Append('(').Append(lineno).Append(", ").Append(colno + 1).Append(')').Append(_fileList[fileno][lineno - 1]); 1145 if (colored) _output.Append("</color>"); 1146 } 1147 else 1148 { 1149 if (colored) _output.Append("<color=").Append(ColorLineDirective).Append('>'); 1150 _output.Append(comment).Append(System.IO.Path.GetFileName(_fileName[fileno])).Append('(').Append(lineno).Append(", ").Append(colno + 1).Append(')'); 1151 if (colored) _output.Append("</color>"); 1152 } 1153 } 1154 _output.Append('\n'); 1155 } 1156 private AsmTokenIterator GetIterator(in AsmLine line) 1157 { 1158 return new AsmTokenIterator(_tokens, line.TokenIndex, line.Length); 1159 } 1160 1161 public enum AsmKind 1162 { 1163 Intel, 1164 ARM, 1165 Wasm, 1166 LLVMIR 1167 } 1168 1169 [Flags] 1170 enum BlockKindDetectFlags 1171 { 1172 None = 0, 1173 Code = 1 << 0, 1174 Data = 1 << 1, 1175 Directive = 1 << 2, 1176 } 1177 1178 public enum AsmBlockKind 1179 { 1180 None, 1181 Block, 1182 Directive, 1183 Code, 1184 Data 1185 } 1186 1187 [DebuggerDisplay("Block {Kind} LineIndex = {LineIndex} Length = {Length}")] 1188 public class AsmBlock 1189 { 1190 public AsmBlockKind Kind; 1191 1192 public int LineIndex; 1193 1194 public int Length; 1195 1196 // Edges attached to this block, might be null if no edges 1197 public List<AsmEdge> Edges; 1198 1199 public void AddEdge(in AsmEdge edge) 1200 { 1201 var edges = Edges; 1202 if (edges == null) 1203 { 1204 edges = new List<AsmEdge>(); 1205 Edges = edges; 1206 } 1207 edges.Add(edge); 1208 } 1209 1210 /// <summary> 1211 /// Sort edges by in-bound first, block index, line index 1212 /// </summary> 1213 public void SortEdges() 1214 { 1215 var edges = Edges; 1216 if (edges == null) return; 1217 edges.Sort(EdgeComparer.Instance); 1218 } 1219 1220 private class EdgeComparer : IComparer<AsmEdge> 1221 { 1222 public static readonly EdgeComparer Instance = new EdgeComparer(); 1223 1224 public int Compare(AsmEdge x, AsmEdge y) 1225 { 1226 // Order by kind first (InBound first, outbound first) 1227 if (x.Kind != y.Kind) 1228 { 1229 return x.Kind == AsmEdgeKind.InBound ? -1 : 1; 1230 } 1231 1232 // Order by Block Index 1233 if (x.LineRef.BlockIndex != y.LineRef.BlockIndex) return x.LineRef.BlockIndex.CompareTo(y.LineRef.BlockIndex); 1234 1235 // Then order by Line Index 1236 return x.LineRef.LineIndex.CompareTo(y.LineRef.LineIndex); 1237 } 1238 } 1239 } 1240 1241 public enum AsmLineKind 1242 { 1243 Empty = 0, 1244 Comment, 1245 Directive, 1246 SourceFile, 1247 SourceLocation, 1248 SourceFileLocation, // computed line 1249 FunctionBegin, 1250 FunctionEnd, 1251 LabelDeclaration, 1252 Code, 1253 CodeCall, 1254 CodeBranch, 1255 CodeJump, 1256 CodeReturn, 1257 Data, 1258 } 1259 1260 /// <summary> 1261 /// An <see cref="AsmToken"/> iterator skipping spaces. 1262 /// </summary> 1263 struct AsmTokenIterator 1264 { 1265 private readonly List<AsmToken> _tokens; 1266 private readonly int _startIndex; 1267 private readonly int _endIndex; 1268 private int _index; 1269 1270 public AsmTokenIterator(List<AsmToken> tokens, int index, int length) 1271 { 1272 if (tokens == null) throw new ArgumentNullException(nameof(tokens)); 1273 _tokens = tokens; 1274 if (index < 0 || index >= tokens.Count) throw new ArgumentOutOfRangeException(nameof(index), $"Invalid index {index}. Must be >= 0 and < {tokens.Count}"); 1275 if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), $"Invalid length {length}. Must be >=0"); 1276 _startIndex = index; 1277 _endIndex = index + length - 1; 1278 if (_endIndex >= tokens.Count) throw new ArgumentOutOfRangeException(nameof(length), $"Invalid length {length}. The final index {_endIndex} cannot be >= {tokens.Count}"); 1279 _index = index; 1280 } 1281 1282 public void Reset() 1283 { 1284 _index = _startIndex; 1285 } 1286 1287 public bool TryGetNext(out AsmToken token) 1288 { 1289 while (_index <= _endIndex) 1290 { 1291 var nextToken = _tokens[_index++]; 1292 if (nextToken.Kind == AsmTokenKind.Misc) continue; 1293 token = nextToken; 1294 return true; 1295 } 1296 1297 token = default; 1298 return false; 1299 } 1300 1301 public bool TryGetNext(out AsmToken token, out int tokenIndex) 1302 { 1303 while (_index <= _endIndex) 1304 { 1305 tokenIndex = _index; 1306 var nextToken = _tokens[_index++]; 1307 if (nextToken.Kind == AsmTokenKind.Misc) continue; 1308 token = nextToken; 1309 return true; 1310 } 1311 1312 tokenIndex = -1; 1313 token = default; 1314 return false; 1315 } 1316 } 1317 1318 [DebuggerDisplay("{ToDebuggerDisplay(),nq}")] 1319 [StructLayout(LayoutKind.Explicit)] 1320 public struct AsmLine 1321 { 1322 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1323 // CAUTION: It is important to not put *any managed objects* 1324 // into this struct for GC efficiency 1325 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1326 1327 [FieldOffset(0)] public AsmLineKind Kind; 1328 1329 [FieldOffset(4)] public int TokenIndex; 1330 1331 // only valid when Kind == SourceFileLocation 1332 [FieldOffset(4)] public int SourceFileNumber; 1333 1334 [FieldOffset(8)] public int Length; 1335 1336 // only valid when Kind == SourceFileLocation 1337 [FieldOffset(8)] public int SourceLineNumber; 1338 1339 // only valid when Kind == SourceFileLocation 1340 [FieldOffset(12)] public int SourceColumnNumber; 1341 1342 /// <summary> 1343 /// Index into <see cref="_columnIndices"/>, the column indices will then contain <see cref="Length"/> minus 1 of column ints, 1344 /// each column corresponding the horizontal offset to a token. 1345 /// The first column is always 0 for the first token, hence the minus 1. 1346 /// Only get filled when asking for the text for a block. 1347 /// </summary> 1348 [FieldOffset(16)] public int ColumnIndex; 1349 1350 private string ToDebuggerDisplay() 1351 { 1352 if (Kind == AsmLineKind.SourceFileLocation) 1353 { 1354 return $"Line {Kind} File={SourceFileNumber} Line={SourceLineNumber} Column={SourceColumnNumber}"; 1355 } 1356 else 1357 { 1358 return $"Line {Kind} TokenIndex={TokenIndex} Length={Length} ColumnIndex={ColumnIndex}"; 1359 } 1360 } 1361 } 1362 1363 1364 public enum AsmEdgeKind 1365 { 1366 InBound, 1367 OutBound, 1368 } 1369 1370 /// <summary> 1371 /// An inbound or outbound connection for a block to another block+line 1372 /// </summary> 1373 [DebuggerDisplay("Edge {Kind} Origin: {OriginRef} LineRef: {LineRef}")] 1374 public struct AsmEdge : IEquatable<AsmEdge> 1375 { 1376 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1377 // CAUTION: It is important to not put *any managed objects* 1378 // into this struct for GC efficiency 1379 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1380 1381 public AsmEdge(AsmEdgeKind kind, AsmLineRef originRef, AsmLineRef lineRef) 1382 { 1383 Kind = kind; 1384 OriginRef = originRef; 1385 LineRef = lineRef; 1386 } 1387 1388 1389 public AsmEdgeKind Kind; 1390 1391 public AsmLineRef OriginRef; 1392 1393 public AsmLineRef LineRef; 1394 1395 public override string ToString() 1396 { 1397 return Kind == AsmEdgeKind.InBound ? 1398 $"Edge {Kind} {LineRef} => {OriginRef}" 1399 : $"Edge {Kind} {OriginRef} => {LineRef}"; 1400 } 1401 1402 public bool Equals(AsmEdge obj) => Kind == obj.Kind && OriginRef.Equals(obj.OriginRef) && LineRef.Equals(obj.LineRef); 1403 1404 public override bool Equals(object obj) => obj is AsmEdge other && Equals(other); 1405 1406 public override int GetHashCode() => base.GetHashCode(); 1407 } 1408 1409 public readonly struct AsmLineRef: IEquatable<AsmLineRef> 1410 { 1411 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1412 // CAUTION: It is important to not put *any managed objects* 1413 // into this struct for GC efficiency 1414 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1415 1416 1417 public AsmLineRef(int blockIndex, int lineIndex) 1418 { 1419 BlockIndex = blockIndex; 1420 LineIndex = lineIndex; 1421 } 1422 1423 public readonly int BlockIndex; 1424 1425 public readonly int LineIndex; 1426 1427 public override string ToString() 1428 { 1429 return $"Block: {BlockIndex}, Line: {LineIndex}"; 1430 } 1431 1432 public bool Equals(AsmLineRef obj) => BlockIndex == obj.BlockIndex && LineIndex == obj.LineIndex; 1433 1434 public override bool Equals(object obj) => obj is AsmLineRef other && Equals(other); 1435 1436 public override int GetHashCode() => base.GetHashCode(); 1437 } 1438 1439 /// <summary> 1440 /// Structure used to store all label references before they are getting fully resolved 1441 /// </summary> 1442 [DebuggerDisplay("TempLabelRef {LineRef} - String {StringIndex}, {StringLength}")] 1443 private readonly struct TempLabelRef 1444 { 1445 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1446 // CAUTION: It is important to not put *any managed objects* 1447 // into this struct for GC efficiency 1448 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1449 1450 public TempLabelRef(int globalBlockIndex, AsmLineRef lineRef, int stringIndex, int stringLength) 1451 { 1452 GlobalBlockIndex = globalBlockIndex; 1453 LineRef = lineRef; 1454 StringIndex = stringIndex; 1455 StringLength = stringLength; 1456 } 1457 1458 public readonly int GlobalBlockIndex; 1459 1460 public readonly AsmLineRef LineRef; 1461 1462 public readonly int StringIndex; 1463 1464 public readonly int StringLength; 1465 } 1466 1467 private class DictionaryLocalLabel : Dictionary<StringSlice, AsmLineRef> 1468 { 1469 public DictionaryLocalLabel() 1470 { 1471 } 1472 1473 public DictionaryLocalLabel(int capacity) : base(capacity) 1474 { 1475 } 1476 1477 public AsmLineRef GlobalLabelLineRef; 1478 } 1479 1480 private class DictionaryGlobalLabel : Dictionary<StringSlice, DictionaryLocalLabel> 1481 { 1482 public DictionaryGlobalLabel() 1483 { 1484 } 1485 1486 public DictionaryGlobalLabel(int capacity) : base(capacity) 1487 { 1488 } 1489 1490 public DictionaryLocalLabel GetOrCreate(StringSlice label, AsmLineRef globalLineRef) 1491 { 1492 if (!TryGetValue(label, out var dictLabel)) 1493 { 1494 dictLabel = new DictionaryLocalLabel(); 1495 Add(label, dictLabel); 1496 } 1497 dictLabel.GlobalLabelLineRef = globalLineRef; 1498 return dictLabel; 1499 } 1500 } 1501 1502 internal struct UsedRegisters 1503 { 1504 private AsmTokenKindProvider _tokenProvider; 1505 1506 /// <summary> 1507 /// Dictionary<lineNr, List<reg>> 1508 /// </summary> 1509 internal readonly Dictionary<int, List<string>> _linesRegisters; 1510 1511 private readonly List<string> _tmp; 1512 private int _currentLineIdx; 1513 1514 public UsedRegisters(int count) 1515 { 1516 _linesRegisters = new Dictionary<int, List<string>>(count); 1517 _tmp = new List<string>(2); 1518 _currentLineIdx = -1; 1519 _tokenProvider = null; 1520 } 1521 1522 public void AddTokenProvider(AsmTokenKindProvider provider) 1523 { 1524 _tokenProvider = provider; 1525 } 1526 1527 private int NumberOfOcurences(List<string> regs, string target) 1528 { 1529 var count = 0; 1530 foreach (var elm in regs) 1531 { 1532 if (_tokenProvider.RegisterEqual(elm, target)) 1533 { 1534 count++; 1535 } 1536 } 1537 return count; 1538 } 1539 1540 public int RegisterMatch(int lineIdx, string reg) 1541 { 1542 return LineContainsRegs(lineIdx, out var actualRegs) 1543 ? NumberOfOcurences(actualRegs, reg) 1544 : 0; 1545 } 1546 1547 public bool RegisterEquality(string regA, string regB) => _tokenProvider.RegisterEqual(regA, regB); 1548 1549 public List<string> CleanRegs(List<string> regs) 1550 { 1551 var tmpTokenProvider = _tokenProvider; 1552 var retVal = new List<string>(regs.Count); 1553 1554 foreach (var reg in regs) 1555 { 1556 if (!retVal.Exists(elm => tmpTokenProvider.RegisterEqual(reg, elm))) 1557 { 1558 retVal.Add(reg); 1559 } 1560 } 1561 return retVal; 1562 } 1563 1564 public bool LineContainsRegs(int lineIdx, out List<string> value) 1565 { 1566 return _linesRegisters.TryGetValue(lineIdx, out value); 1567 } 1568 1569 public void Add(int lineIdx, string reg) 1570 { 1571 _currentLineIdx = lineIdx; 1572 _tmp.Add(reg); 1573 } 1574 1575 public void PushLine() 1576 { 1577 if (_currentLineIdx == -1) 1578 { 1579 // We haven't actually tried to add anything. 1580 return; 1581 } 1582 _linesRegisters[_currentLineIdx] = new List<string>(_tmp); 1583 _tmp.Clear(); 1584 _currentLineIdx = -1; 1585 } 1586 1587 public int Count => _linesRegisters.Count; 1588 1589 public void Clear() 1590 { 1591 _linesRegisters.Clear(); 1592 _tmp.Clear(); 1593 } 1594 } 1595 } 1596} 1597#endif