From 180ad8605d09ff0ca754a8e5efcce78844701444 Mon Sep 17 00:00:00 2001 From: Ficture Seven Date: Mon, 4 May 2020 06:06:22 +0400 Subject: [PATCH] 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 --- ARMeilleure/Diagnostics/IRDumper.cs | 368 ++++++++++++++-------- ARMeilleure/Diagnostics/Symbols.cs | 84 +++++ ARMeilleure/Translation/EmitterContext.cs | 3 + ARMeilleure/Translation/JumpTable.cs | 6 +- 4 files changed, 333 insertions(+), 128 deletions(-) create mode 100644 ARMeilleure/Diagnostics/Symbols.cs diff --git a/ARMeilleure/Diagnostics/IRDumper.cs b/ARMeilleure/Diagnostics/IRDumper.cs index 100a9b11..7ff76077 100644 --- a/ARMeilleure/Diagnostics/IRDumper.cs +++ b/ARMeilleure/Diagnostics/IRDumper.cs @@ -2,168 +2,282 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace ARMeilleure.Diagnostics { - static class IRDumper + class IRDumper { private const string Indentation = " "; - public static string GetDump(ControlFlowGraph cfg) + private int _indentLevel; + + private readonly StringBuilder _builder; + + private readonly Dictionary _localNames; + private readonly Dictionary _symbolNames; + + private IRDumper(int indent) { - StringBuilder sb = new StringBuilder(); + _indentLevel = indent; - Dictionary localNames = new Dictionary(); + _builder = new StringBuilder(); - string indentation = string.Empty; - - 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(); + _localNames = new Dictionary(); + _symbolNames = new Dictionary(); } - 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 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) { - return ""; + _builder.Append(""); + 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)) - { - localName = "%" + localNames.Count; + case OperandKind.LocalVariable: + if (!_localNames.TryGetValue(operand, out string localName)) + { + 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(" = "); } - - name = localName; - } - else if (operand.Kind == OperandKind.Register) - { - Register reg = operand.GetRegister(); - - switch (reg.Type) + else { - case RegisterType.Flag: name = "b" + reg.Index; break; - case RegisterType.FpFlag: name = "f" + reg.Index; break; - case RegisterType.Integer: name = "r" + reg.Index; break; - case RegisterType.Vector: name = "v" + reg.Index; break; + _builder.Append(", "); } } - else if (operand.Kind == OperandKind.Constant) + + switch (node) { - name = "0x" + operand.Value.ToString("X"); - } - else - { - name = operand.Kind.ToString().ToLower(); + 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; } - 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) { - switch (type) + return type switch { - case OperandType.FP32: return "f32"; - case OperandType.FP64: return "f64"; - case OperandType.I32: return "i32"; - case OperandType.I64: return "i64"; - case OperandType.None: return "none"; - case OperandType.V128: return "v128"; - } - - throw new ArgumentException($"Invalid operand type \"{type}\"."); + OperandType.None => "none", + OperandType.I32 => "i32", + OperandType.I64 => "i64", + OperandType.FP32 => "f32", + OperandType.FP64 => "f64", + OperandType.V128 => "v128", + _ => throw new ArgumentException($"Invalid operand type \"{type}\"."), + }; } } } \ No newline at end of file diff --git a/ARMeilleure/Diagnostics/Symbols.cs b/ARMeilleure/Diagnostics/Symbols.cs new file mode 100644 index 00000000..17764f7e --- /dev/null +++ b/ARMeilleure/Diagnostics/Symbols.cs @@ -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 _symbols; + private static readonly List _rangedSymbols; + + static Symbols() + { + _symbols = new ConcurrentDictionary(); + _rangedSymbols = new List(); + } + + 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)); + } + } + } +} diff --git a/ARMeilleure/Translation/EmitterContext.cs b/ARMeilleure/Translation/EmitterContext.cs index d9e0a4ed..7abda1a7 100644 --- a/ARMeilleure/Translation/EmitterContext.cs +++ b/ARMeilleure/Translation/EmitterContext.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Diagnostics; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.State; using System; @@ -85,6 +86,8 @@ namespace ARMeilleure.Translation IntPtr ptr = Marshal.GetFunctionPointerForDelegate(func); + Symbols.Add((ulong)ptr.ToInt64(), func.Method.Name); + OperandType returnType = GetOperandType(func.Method.ReturnType); return Call(Const(ptr.ToInt64()), returnType, callArgs); diff --git a/ARMeilleure/Translation/JumpTable.cs b/ARMeilleure/Translation/JumpTable.cs index e444e96d..40ea0fce 100644 --- a/ARMeilleure/Translation/JumpTable.cs +++ b/ARMeilleure/Translation/JumpTable.cs @@ -1,4 +1,5 @@ -using ARMeilleure.Memory; +using ARMeilleure.Diagnostics; +using ARMeilleure.Memory; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -60,6 +61,9 @@ namespace ARMeilleure.Translation _targets = new ConcurrentDictionary(); _dependants = new ConcurrentDictionary>(); + + 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)