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:
Ficture Seven 2020-05-04 06:06:22 +04:00 committed by GitHub
parent 53369e79bd
commit 180ad8605d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 333 additions and 128 deletions

View file

@ -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()
{
indentation = indentation.Substring(0, indentation.Length - Indentation.Length);
}
void AppendLine(string text)
{
sb.AppendLine(indentation + text);
}
IncreaseIndentation();
for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
{
string blockName = GetBlockName(block);
if (block.Next != null)
{
blockName += $" (next {GetBlockName(block.Next)})";
}
if (block.Branch != null)
{
blockName += $" (branch {GetBlockName(block.Branch)})";
}
blockName += ":";
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";
}
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) private void Indent()
{ {
return $"block{block.Index}"; _builder.EnsureCapacity(_builder.Capacity + _indentLevel * Indentation.Length);
for (int index = 0; index < _indentLevel; index++)
{
_builder.Append(Indentation);
}
} }
private static string GetOperandName(Operand operand, Dictionary<Operand, string> localNames) private void IncreaseIndentation()
{
_indentLevel++;
}
private void DecreaseIndentation()
{
_indentLevel--;
}
private void DumpBlockName(BasicBlock block)
{
_builder.Append("block").Append(block.Index);
}
private void DumpBlockHeader(BasicBlock block)
{
DumpBlockName(block);
if (block.Next != null)
{
_builder.Append(" (next ");
DumpBlockName(block.Next);
_builder.Append(')');
}
if (block.Branch != null)
{
_builder.Append(" (branch ");
DumpBlockName(block.Branch);
_builder.Append(')');
}
_builder.Append(':');
}
private void DumpOperand(Operand operand)
{ {
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);
}
_builder.Append(localName);
break;
case OperandKind.Register:
Register reg = operand.GetRegister();
switch (reg.Type)
{
case RegisterType.Flag: _builder.Append('b'); break;
case RegisterType.FpFlag: _builder.Append('f'); break;
case RegisterType.Integer: _builder.Append('r'); break;
case RegisterType.Vector: _builder.Append('v'); break;
}
_builder.Append(reg.Index);
break;
case OperandKind.Constant:
string symbolName = Symbols.Get(operand.Value);
if (symbolName != null && !_symbolNames.ContainsKey(operand.Value))
{
_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
name = localName;
}
else if (operand.Kind == OperandKind.Register)
{
Register reg = operand.GetRegister();
switch (reg.Type)
{ {
case RegisterType.Flag: name = "b" + reg.Index; break; _builder.Append(", ");
case RegisterType.FpFlag: name = "f" + reg.Index; break;
case RegisterType.Integer: name = "r" + reg.Index; break;
case RegisterType.Vector: name = "v" + reg.Index; break;
} }
} }
else if (operand.Kind == OperandKind.Constant)
switch (node)
{ {
name = "0x" + operand.Value.ToString("X"); case PhiNode phi:
} _builder.Append("Phi ");
else
{ for (int index = 0; index < phi.SourcesCount; index++)
name = operand.Kind.ToString().ToLower(); {
_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;
} }
return GetTypeName(operand.Type) + " " + name; 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}\".");
} }
} }
} }

View 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));
}
}
}
}

View file

@ -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);

View file

@ -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)