Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@ paket-files/
#tools/**
#!tools/packages.config

# JitAsm uops.info database (110MB, download separately)
tools/JitAsm/instructions.xml
tools/JitAsm/instructions.db

# Tabs Studio
*.tss

Expand Down
112 changes: 112 additions & 0 deletions tools/JitAsm/DisassemblyParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
Comment thread
benaadams marked this conversation as resolved.
Outdated
// SPDX-License-Identifier: LGPL-3.0-only

using System.Text;
using System.Text.RegularExpressions;

namespace JitAsm;

internal static partial class DisassemblyParser
{
// Pattern to detect the start of a method's disassembly
// Example: ; Assembly listing for method Namespace.Type:Method(args)
[GeneratedRegex(@"^; Assembly listing for method (?<method>.+)$", RegexOptions.Compiled | RegexOptions.Multiline)]
private static partial Regex MethodHeaderPattern();

// Pattern to detect end of method disassembly (next method or end)
[GeneratedRegex(@"^; Total bytes of code", RegexOptions.Compiled | RegexOptions.Multiline)]
private static partial Regex MethodEndPattern();

public static string Parse(string jitOutput, bool lastOnly = false)
{
if (string.IsNullOrWhiteSpace(jitOutput))
{
return string.Empty;
}

var result = new StringBuilder();
var matches = MethodHeaderPattern().Matches(jitOutput);

if (matches.Count == 0)
{
// No method headers found, return raw output if it looks like assembly
if (jitOutput.Contains("mov") || jitOutput.Contains("call") || jitOutput.Contains("ret"))
{
return jitOutput.Trim();
}
return string.Empty;
}

// In tier1 mode, JitDisasm captures both Tier-0 and Tier-1 compilations.

Check warning on line 40 in tools/JitAsm/DisassemblyParser.cs

View workflow job for this annotation

GitHub Actions / Check whitespaces

Unknown word (Disasm)
// We want the LAST compilation (Tier-1 with full optimizations).
int startIdx = lastOnly ? matches.Count - 1 : 0;

for (int i = startIdx; i < matches.Count; i++)
{
var match = matches[i];
var startIndex = match.Index;

// Find the end of this method's disassembly
int endIndex = (i + 1 < matches.Count) ? matches[i + 1].Index : jitOutput.Length;

// Extract this method's disassembly
var methodAsm = jitOutput[startIndex..endIndex].TrimEnd();

// Find "Total bytes of code" line and include it
var totalBytesMatch = MethodEndPattern().Match(methodAsm);
if (totalBytesMatch.Success)
{
// Find end of line after "Total bytes of code"
var lineEnd = methodAsm.IndexOf('\n', totalBytesMatch.Index);
if (lineEnd > 0)
{
methodAsm = methodAsm[..(lineEnd + 1)].TrimEnd();
}
}

if (result.Length > 0)
{
result.AppendLine();
result.AppendLine(new string('-', 80));
result.AppendLine();
}

result.AppendLine(methodAsm);
}

return result.ToString().Trim();
}

public static IEnumerable<MethodDisassembly> ParseMethods(string jitOutput)
{
if (string.IsNullOrWhiteSpace(jitOutput))
{
yield break;
}

var matches = MethodHeaderPattern().Matches(jitOutput);

for (int i = 0; i < matches.Count; i++)
{
var match = matches[i];
var methodName = match.Groups["method"].Value;
var startIndex = match.Index;

int endIndex = (i + 1 < matches.Count) ? matches[i + 1].Index : jitOutput.Length;

var methodAsm = jitOutput[startIndex..endIndex].TrimEnd();

yield return new MethodDisassembly
{
MethodName = methodName,
Assembly = methodAsm
};
}
}
}

internal sealed class MethodDisassembly
{
public required string MethodName { get; init; }
public required string Assembly { get; init; }
}
Loading
Loading