Improve IRDumper (#1135)
* Improve IRDumper * Make Symbols.Add(ulong, ulong, ulong, string) thread safe * Use a StringBuilder for MemoryOperand * Add #if M_DEBUG guards * Fix JMP_TABLE typo * Fix using in Symbols * Use Conditional("M_DEBUG") instead Address gdkchan's feedback * Use a struct instead of 4-tuple Address gdkchan's feedback * Place symbols in comments instead Address gdkchan's feedback * Use StringBuilder throughout * Handle offsetted symbols * Fix naming convention of Builder * Avoid ArgumentException * Remove unnecessary using * Use switch expression instead * Turn into a class * Clean up * Remove unnecessary using
This commit is contained in:
parent
53369e79bd
commit
180ad8605d
4 changed files with 333 additions and 128 deletions
|
@ -2,168 +2,282 @@ using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace ARMeilleure.Diagnostics
|
namespace ARMeilleure.Diagnostics
|
||||||
{
|
{
|
||||||
static class IRDumper
|
class IRDumper
|
||||||
{
|
{
|
||||||
private const string Indentation = " ";
|
private const string Indentation = " ";
|
||||||
|
|
||||||
public static string GetDump(ControlFlowGraph cfg)
|
private int _indentLevel;
|
||||||
|
|
||||||
|
private readonly StringBuilder _builder;
|
||||||
|
|
||||||
|
private readonly Dictionary<Operand, string> _localNames;
|
||||||
|
private readonly Dictionary<ulong, string> _symbolNames;
|
||||||
|
|
||||||
|
private IRDumper(int indent)
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
_indentLevel = indent;
|
||||||
|
|
||||||
Dictionary<Operand, string> localNames = new Dictionary<Operand, string>();
|
_builder = new StringBuilder();
|
||||||
|
|
||||||
string indentation = string.Empty;
|
_localNames = new Dictionary<Operand, string>();
|
||||||
|
_symbolNames = new Dictionary<ulong, string>();
|
||||||
void IncreaseIndentation()
|
|
||||||
{
|
|
||||||
indentation += Indentation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecreaseIndentation()
|
private void Indent()
|
||||||
{
|
{
|
||||||
indentation = indentation.Substring(0, indentation.Length - Indentation.Length);
|
_builder.EnsureCapacity(_builder.Capacity + _indentLevel * Indentation.Length);
|
||||||
|
|
||||||
|
for (int index = 0; index < _indentLevel; index++)
|
||||||
|
{
|
||||||
|
_builder.Append(Indentation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppendLine(string text)
|
private void IncreaseIndentation()
|
||||||
{
|
{
|
||||||
sb.AppendLine(indentation + text);
|
_indentLevel++;
|
||||||
}
|
}
|
||||||
|
|
||||||
IncreaseIndentation();
|
private void DecreaseIndentation()
|
||||||
|
|
||||||
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
|
||||||
{
|
{
|
||||||
string blockName = GetBlockName(block);
|
_indentLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DumpBlockName(BasicBlock block)
|
||||||
|
{
|
||||||
|
_builder.Append("block").Append(block.Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DumpBlockHeader(BasicBlock block)
|
||||||
|
{
|
||||||
|
DumpBlockName(block);
|
||||||
|
|
||||||
if (block.Next != null)
|
if (block.Next != null)
|
||||||
{
|
{
|
||||||
blockName += $" (next {GetBlockName(block.Next)})";
|
_builder.Append(" (next ");
|
||||||
|
DumpBlockName(block.Next);
|
||||||
|
_builder.Append(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block.Branch != null)
|
if (block.Branch != null)
|
||||||
{
|
{
|
||||||
blockName += $" (branch {GetBlockName(block.Branch)})";
|
_builder.Append(" (branch ");
|
||||||
|
DumpBlockName(block.Branch);
|
||||||
|
_builder.Append(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
blockName += ":";
|
_builder.Append(':');
|
||||||
|
|
||||||
AppendLine(blockName);
|
|
||||||
|
|
||||||
IncreaseIndentation();
|
|
||||||
|
|
||||||
for (Node node = block.Operations.First; node != null; node = node.ListNext)
|
|
||||||
{
|
|
||||||
string[] sources = new string[node.SourcesCount];
|
|
||||||
|
|
||||||
string instName = string.Empty;
|
|
||||||
|
|
||||||
if (node is PhiNode phi)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < sources.Length; index++)
|
|
||||||
{
|
|
||||||
string phiBlockName = GetBlockName(phi.GetBlock(index));
|
|
||||||
|
|
||||||
string operName = GetOperandName(phi.GetSource(index), localNames);
|
|
||||||
|
|
||||||
sources[index] = $"({phiBlockName}: {operName})";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instName = "Phi";
|
private void DumpOperand(Operand operand)
|
||||||
}
|
|
||||||
else if (node is Operation operation)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < sources.Length; index++)
|
|
||||||
{
|
|
||||||
sources[index] = GetOperandName(operation.GetSource(index), localNames);
|
|
||||||
}
|
|
||||||
|
|
||||||
instName = operation.Instruction.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
string allSources = string.Join(", ", sources);
|
|
||||||
|
|
||||||
string line = instName + " " + allSources;
|
|
||||||
|
|
||||||
if (node.Destination != null)
|
|
||||||
{
|
|
||||||
line = GetOperandName(node.Destination, localNames) + " = " + line;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppendLine(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
DecreaseIndentation();
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetBlockName(BasicBlock block)
|
|
||||||
{
|
|
||||||
return $"block{block.Index}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetOperandName(Operand operand, Dictionary<Operand, string> localNames)
|
|
||||||
{
|
{
|
||||||
if (operand == null)
|
if (operand == null)
|
||||||
{
|
{
|
||||||
return "<NULL>";
|
_builder.Append("<NULL>");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string name = string.Empty;
|
_builder.Append(GetTypeName(operand.Type)).Append(' ');
|
||||||
|
|
||||||
if (operand.Kind == OperandKind.LocalVariable)
|
switch (operand.Kind)
|
||||||
{
|
{
|
||||||
if (!localNames.TryGetValue(operand, out string localName))
|
case OperandKind.LocalVariable:
|
||||||
|
if (!_localNames.TryGetValue(operand, out string localName))
|
||||||
{
|
{
|
||||||
localName = "%" + localNames.Count;
|
localName = $"%{_localNames.Count}";
|
||||||
|
|
||||||
localNames.Add(operand, localName);
|
_localNames.Add(operand, localName);
|
||||||
}
|
}
|
||||||
|
|
||||||
name = localName;
|
_builder.Append(localName);
|
||||||
}
|
break;
|
||||||
else if (operand.Kind == OperandKind.Register)
|
|
||||||
{
|
case OperandKind.Register:
|
||||||
Register reg = operand.GetRegister();
|
Register reg = operand.GetRegister();
|
||||||
|
|
||||||
switch (reg.Type)
|
switch (reg.Type)
|
||||||
{
|
{
|
||||||
case RegisterType.Flag: name = "b" + reg.Index; break;
|
case RegisterType.Flag: _builder.Append('b'); break;
|
||||||
case RegisterType.FpFlag: name = "f" + reg.Index; break;
|
case RegisterType.FpFlag: _builder.Append('f'); break;
|
||||||
case RegisterType.Integer: name = "r" + reg.Index; break;
|
case RegisterType.Integer: _builder.Append('r'); break;
|
||||||
case RegisterType.Vector: name = "v" + reg.Index; break;
|
case RegisterType.Vector: _builder.Append('v'); break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (operand.Kind == OperandKind.Constant)
|
_builder.Append(reg.Index);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandKind.Constant:
|
||||||
|
string symbolName = Symbols.Get(operand.Value);
|
||||||
|
|
||||||
|
if (symbolName != null && !_symbolNames.ContainsKey(operand.Value))
|
||||||
{
|
{
|
||||||
name = "0x" + operand.Value.ToString("X");
|
_symbolNames.Add(operand.Value, symbolName);
|
||||||
|
}
|
||||||
|
|
||||||
|
_builder.Append("0x").Append(operand.Value.ToString("X"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OperandKind.Memory:
|
||||||
|
var memOp = (MemoryOperand)operand;
|
||||||
|
|
||||||
|
_builder.Append('[');
|
||||||
|
|
||||||
|
DumpOperand(memOp.BaseAddress);
|
||||||
|
|
||||||
|
if (memOp.Index != null)
|
||||||
|
{
|
||||||
|
_builder.Append(" + ");
|
||||||
|
|
||||||
|
DumpOperand(memOp.Index);
|
||||||
|
|
||||||
|
switch (memOp.Scale)
|
||||||
|
{
|
||||||
|
case Multiplier.x2: _builder.Append("*2"); break;
|
||||||
|
case Multiplier.x4: _builder.Append("*4"); break;
|
||||||
|
case Multiplier.x8: _builder.Append("*8"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memOp.Displacement != 0)
|
||||||
|
{
|
||||||
|
_builder.Append(" + 0x").Append(memOp.Displacement.ToString("X"));
|
||||||
|
}
|
||||||
|
|
||||||
|
_builder.Append(']');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_builder.Append(operand.Type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DumpNode(Node node)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < node.DestinationsCount; index++)
|
||||||
|
{
|
||||||
|
DumpOperand(node.GetDestination(index));
|
||||||
|
|
||||||
|
if (index == node.DestinationsCount - 1)
|
||||||
|
{
|
||||||
|
_builder.Append(" = ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
name = operand.Kind.ToString().ToLower();
|
_builder.Append(", ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetTypeName(operand.Type) + " " + name;
|
switch (node)
|
||||||
|
{
|
||||||
|
case PhiNode phi:
|
||||||
|
_builder.Append("Phi ");
|
||||||
|
|
||||||
|
for (int index = 0; index < phi.SourcesCount; index++)
|
||||||
|
{
|
||||||
|
_builder.Append('(');
|
||||||
|
|
||||||
|
DumpBlockName(phi.GetBlock(index));
|
||||||
|
|
||||||
|
_builder.Append(": ");
|
||||||
|
|
||||||
|
DumpOperand(phi.GetSource(index));
|
||||||
|
|
||||||
|
_builder.Append(')');
|
||||||
|
|
||||||
|
if (index < phi.SourcesCount - 1)
|
||||||
|
{
|
||||||
|
_builder.Append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Operation operation:
|
||||||
|
_builder.Append(operation.Instruction);
|
||||||
|
|
||||||
|
if (operation.Instruction == Instruction.Extended)
|
||||||
|
{
|
||||||
|
var intrinOp = (IntrinsicOperation)operation;
|
||||||
|
|
||||||
|
_builder.Append('.').Append(intrinOp.Intrinsic);
|
||||||
|
}
|
||||||
|
|
||||||
|
_builder.Append(' ');
|
||||||
|
|
||||||
|
for (int index = 0; index < operation.SourcesCount; index++)
|
||||||
|
{
|
||||||
|
DumpOperand(operation.GetSource(index));
|
||||||
|
|
||||||
|
if (index < operation.SourcesCount - 1)
|
||||||
|
{
|
||||||
|
_builder.Append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_symbolNames.Count == 1)
|
||||||
|
{
|
||||||
|
_builder.Append(" ;; ").Append(_symbolNames.First().Value);
|
||||||
|
}
|
||||||
|
else if (_symbolNames.Count > 1)
|
||||||
|
{
|
||||||
|
_builder.Append(" ;;");
|
||||||
|
|
||||||
|
foreach ((ulong value, string name) in _symbolNames)
|
||||||
|
{
|
||||||
|
_builder.Append(" 0x").Append(value.ToString("X")).Append(" = ").Append(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the set of symbols for the next Node we're going to dump.
|
||||||
|
_symbolNames.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetDump(ControlFlowGraph cfg)
|
||||||
|
{
|
||||||
|
var dumper = new IRDumper(1);
|
||||||
|
|
||||||
|
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
|
||||||
|
{
|
||||||
|
dumper.Indent();
|
||||||
|
dumper.DumpBlockHeader(block);
|
||||||
|
|
||||||
|
dumper._builder.AppendLine();
|
||||||
|
|
||||||
|
dumper.IncreaseIndentation();
|
||||||
|
|
||||||
|
for (Node node = block.Operations.First; node != null; node = node.ListNext)
|
||||||
|
{
|
||||||
|
dumper.Indent();
|
||||||
|
dumper.DumpNode(node);
|
||||||
|
|
||||||
|
dumper._builder.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
dumper.DecreaseIndentation();
|
||||||
|
}
|
||||||
|
|
||||||
|
return dumper._builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetTypeName(OperandType type)
|
private static string GetTypeName(OperandType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
return type switch
|
||||||
{
|
{
|
||||||
case OperandType.FP32: return "f32";
|
OperandType.None => "none",
|
||||||
case OperandType.FP64: return "f64";
|
OperandType.I32 => "i32",
|
||||||
case OperandType.I32: return "i32";
|
OperandType.I64 => "i64",
|
||||||
case OperandType.I64: return "i64";
|
OperandType.FP32 => "f32",
|
||||||
case OperandType.None: return "none";
|
OperandType.FP64 => "f64",
|
||||||
case OperandType.V128: return "v128";
|
OperandType.V128 => "v128",
|
||||||
}
|
_ => throw new ArgumentException($"Invalid operand type \"{type}\"."),
|
||||||
|
};
|
||||||
throw new ArgumentException($"Invalid operand type \"{type}\".");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
84
ARMeilleure/Diagnostics/Symbols.cs
Normal file
84
ARMeilleure/Diagnostics/Symbols.cs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Diagnostics
|
||||||
|
{
|
||||||
|
static class Symbols
|
||||||
|
{
|
||||||
|
private struct RangedSymbol
|
||||||
|
{
|
||||||
|
public readonly ulong Start;
|
||||||
|
public readonly ulong End;
|
||||||
|
public readonly ulong ElementSize;
|
||||||
|
public readonly string Name;
|
||||||
|
|
||||||
|
public RangedSymbol(ulong start, ulong end, ulong elemSize, string name)
|
||||||
|
{
|
||||||
|
Start = start;
|
||||||
|
End = end;
|
||||||
|
ElementSize = elemSize;
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<ulong, string> _symbols;
|
||||||
|
private static readonly List<RangedSymbol> _rangedSymbols;
|
||||||
|
|
||||||
|
static Symbols()
|
||||||
|
{
|
||||||
|
_symbols = new ConcurrentDictionary<ulong, string>();
|
||||||
|
_rangedSymbols = new List<RangedSymbol>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Get(ulong address)
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
|
||||||
|
if (_symbols.TryGetValue(address, out result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_rangedSymbols)
|
||||||
|
{
|
||||||
|
foreach (RangedSymbol symbol in _rangedSymbols)
|
||||||
|
{
|
||||||
|
if (address >= symbol.Start && address <= symbol.End)
|
||||||
|
{
|
||||||
|
ulong diff = address - symbol.Start;
|
||||||
|
ulong rem = diff % symbol.ElementSize;
|
||||||
|
|
||||||
|
result = symbol.Name + "_" + diff / symbol.ElementSize;
|
||||||
|
|
||||||
|
if (rem != 0)
|
||||||
|
{
|
||||||
|
result += "+" + rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
_symbols.TryAdd(address, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("M_DEBUG")]
|
||||||
|
public static void Add(ulong address, string name)
|
||||||
|
{
|
||||||
|
_symbols.TryAdd(address, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("M_DEBUG")]
|
||||||
|
public static void Add(ulong address, ulong size, ulong elemSize, string name)
|
||||||
|
{
|
||||||
|
lock (_rangedSymbols)
|
||||||
|
{
|
||||||
|
_rangedSymbols.Add(new RangedSymbol(address, address + size, elemSize, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using ARMeilleure.Diagnostics;
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using System;
|
using System;
|
||||||
|
@ -85,6 +86,8 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
IntPtr ptr = Marshal.GetFunctionPointerForDelegate<Delegate>(func);
|
IntPtr ptr = Marshal.GetFunctionPointerForDelegate<Delegate>(func);
|
||||||
|
|
||||||
|
Symbols.Add((ulong)ptr.ToInt64(), func.Method.Name);
|
||||||
|
|
||||||
OperandType returnType = GetOperandType(func.Method.ReturnType);
|
OperandType returnType = GetOperandType(func.Method.ReturnType);
|
||||||
|
|
||||||
return Call(Const(ptr.ToInt64()), returnType, callArgs);
|
return Call(Const(ptr.ToInt64()), returnType, callArgs);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Diagnostics;
|
||||||
|
using ARMeilleure.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -60,6 +61,9 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
_targets = new ConcurrentDictionary<ulong, TranslatedFunction>();
|
_targets = new ConcurrentDictionary<ulong, TranslatedFunction>();
|
||||||
_dependants = new ConcurrentDictionary<ulong, LinkedList<int>>();
|
_dependants = new ConcurrentDictionary<ulong, LinkedList<int>>();
|
||||||
|
|
||||||
|
Symbols.Add((ulong)_jumpRegion.Pointer.ToInt64(), JumpTableByteSize, JumpTableStride, "JMP_TABLE");
|
||||||
|
Symbols.Add((ulong)_dynamicRegion.Pointer.ToInt64(), DynamicTableByteSize, DynamicTableStride, "DYN_TABLE");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterFunction(ulong address, TranslatedFunction func)
|
public void RegisterFunction(ulong address, TranslatedFunction func)
|
||||||
|
|
Reference in a new issue