diff --git a/ARMeilleure/Diagnostics/IRDumper.cs b/ARMeilleure/Diagnostics/IRDumper.cs
index 100a9b1130..7ff7607726 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<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;
-
-            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<Operand, string>();
+            _symbolNames = new Dictionary<ulong, string>();
         }
 
-        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)
             {
-                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))
-                {
-                    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 0000000000..17764f7e3a
--- /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<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));
+            }
+        }
+    }
+}
diff --git a/ARMeilleure/Translation/EmitterContext.cs b/ARMeilleure/Translation/EmitterContext.cs
index d9e0a4edee..7abda1a7b5 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<Delegate>(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 e444e96de1..40ea0fcee7 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<ulong, TranslatedFunction>();
             _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)