/ src / ARMeilleure / Diagnostics / IRDumper.cs
IRDumper.cs
  1  using ARMeilleure.IntermediateRepresentation;
  2  using ARMeilleure.Translation;
  3  using System;
  4  using System.Collections.Generic;
  5  using System.Linq;
  6  using System.Text;
  7  
  8  namespace ARMeilleure.Diagnostics
  9  {
 10      class IRDumper
 11      {
 12          private const string Indentation = " ";
 13  
 14          private int _indentLevel;
 15  
 16          private readonly StringBuilder _builder;
 17  
 18          private readonly Dictionary<Operand, string> _localNames;
 19          private readonly Dictionary<ulong, string> _symbolNames;
 20  
 21          public IRDumper(int indent)
 22          {
 23              _indentLevel = indent;
 24  
 25              _builder = new StringBuilder();
 26  
 27              _localNames = new Dictionary<Operand, string>();
 28              _symbolNames = new Dictionary<ulong, string>();
 29          }
 30  
 31          private void Indent()
 32          {
 33              _builder.EnsureCapacity(_builder.Capacity + _indentLevel * Indentation.Length);
 34  
 35              for (int index = 0; index < _indentLevel; index++)
 36              {
 37  #pragma warning disable CA1834 // Use StringBuilder.Append(char) for single character strings
 38                  _builder.Append(Indentation);
 39  #pragma warning restore CA1834
 40              }
 41          }
 42  
 43          private void IncreaseIndentation()
 44          {
 45              _indentLevel++;
 46          }
 47  
 48          private void DecreaseIndentation()
 49          {
 50              _indentLevel--;
 51          }
 52  
 53          private void DumpBlockName(BasicBlock block)
 54          {
 55              _builder.Append("block").Append(block.Index);
 56          }
 57  
 58          private void DumpBlockHeader(BasicBlock block)
 59          {
 60              DumpBlockName(block);
 61  
 62              if (block.Frequency == BasicBlockFrequency.Cold)
 63              {
 64                  _builder.Append(" cold");
 65              }
 66  
 67              if (block.SuccessorsCount > 0)
 68              {
 69                  _builder.Append(" (");
 70  
 71                  for (int i = 0; i < block.SuccessorsCount; i++)
 72                  {
 73                      DumpBlockName(block.GetSuccessor(i));
 74  
 75                      if (i < block.SuccessorsCount - 1)
 76                      {
 77                          _builder.Append(", ");
 78                      }
 79                  }
 80  
 81                  _builder.Append(')');
 82              }
 83  
 84              _builder.Append(':');
 85          }
 86  
 87          private void DumpOperand(Operand operand)
 88          {
 89              if (operand == default)
 90              {
 91                  _builder.Append("<NULL>");
 92                  return;
 93              }
 94  
 95              _builder.Append(GetTypeName(operand.Type)).Append(' ');
 96  
 97              switch (operand.Kind)
 98              {
 99                  case OperandKind.LocalVariable:
100                      if (!_localNames.TryGetValue(operand, out string localName))
101                      {
102                          localName = $"%{_localNames.Count}";
103  
104                          _localNames.Add(operand, localName);
105                      }
106  
107                      _builder.Append(localName);
108                      break;
109  
110                  case OperandKind.Register:
111                      Register reg = operand.GetRegister();
112  
113                      switch (reg.Type)
114                      {
115                          case RegisterType.Flag:
116                              _builder.Append('b');
117                              break;
118                          case RegisterType.FpFlag:
119                              _builder.Append('f');
120                              break;
121                          case RegisterType.Integer:
122                              _builder.Append('r');
123                              break;
124                          case RegisterType.Vector:
125                              _builder.Append('v');
126                              break;
127                      }
128  
129                      _builder.Append(reg.Index);
130                      break;
131  
132                  case OperandKind.Constant:
133                      string symbolName = Symbols.Get(operand.Value);
134  
135                      if (symbolName != null && !_symbolNames.ContainsKey(operand.Value))
136                      {
137                          _symbolNames.Add(operand.Value, symbolName);
138                      }
139  
140                      _builder.Append("0x").Append(operand.Value.ToString("X"));
141                      break;
142  
143                  case OperandKind.Memory:
144                      var memOp = operand.GetMemory();
145  
146                      _builder.Append('[');
147  
148                      DumpOperand(memOp.BaseAddress);
149  
150                      if (memOp.Index != default)
151                      {
152                          _builder.Append(" + ");
153  
154                          DumpOperand(memOp.Index);
155  
156                          switch (memOp.Scale)
157                          {
158                              case Multiplier.x2:
159                                  _builder.Append("*2");
160                                  break;
161                              case Multiplier.x4:
162                                  _builder.Append("*4");
163                                  break;
164                              case Multiplier.x8:
165                                  _builder.Append("*8");
166                                  break;
167                          }
168                      }
169  
170                      if (memOp.Displacement != 0)
171                      {
172                          _builder.Append(" + 0x").Append(memOp.Displacement.ToString("X"));
173                      }
174  
175                      _builder.Append(']');
176                      break;
177  
178                  default:
179                      _builder.Append(operand.Type);
180                      break;
181              }
182          }
183  
184          private void DumpNode(ControlFlowGraph cfg, Operation node)
185          {
186              for (int index = 0; index < node.DestinationsCount; index++)
187              {
188                  DumpOperand(node.GetDestination(index));
189  
190                  if (index == node.DestinationsCount - 1)
191                  {
192                      _builder.Append(" = ");
193                  }
194                  else
195                  {
196                      _builder.Append(", ");
197                  }
198              }
199  
200              switch (node)
201              {
202                  case Operation operation:
203                      if (operation.Instruction == Instruction.Phi)
204                      {
205                          PhiOperation phi = operation.AsPhi();
206  
207                          _builder.Append("Phi ");
208  
209                          for (int index = 0; index < phi.SourcesCount; index++)
210                          {
211                              _builder.Append('(');
212  
213                              DumpBlockName(phi.GetBlock(cfg, index));
214  
215                              _builder.Append(": ");
216  
217                              DumpOperand(phi.GetSource(index));
218  
219                              _builder.Append(')');
220  
221                              if (index < phi.SourcesCount - 1)
222                              {
223                                  _builder.Append(", ");
224                              }
225                          }
226  
227                          break;
228                      }
229  
230                      bool comparison = false;
231  
232                      _builder.Append(operation.Instruction);
233  
234                      if (operation.Instruction == Instruction.Extended)
235                      {
236                          _builder.Append('.').Append(operation.Intrinsic);
237                      }
238                      else if (operation.Instruction == Instruction.BranchIf ||
239                               operation.Instruction == Instruction.Compare)
240                      {
241                          comparison = true;
242                      }
243  
244                      _builder.Append(' ');
245  
246                      for (int index = 0; index < operation.SourcesCount; index++)
247                      {
248                          Operand source = operation.GetSource(index);
249  
250                          if (index < operation.SourcesCount - 1)
251                          {
252                              DumpOperand(source);
253  
254                              _builder.Append(", ");
255                          }
256                          else if (comparison)
257                          {
258                              _builder.Append((Comparison)source.AsInt32());
259                          }
260                          else
261                          {
262                              DumpOperand(source);
263                          }
264                      }
265                      break;
266              }
267  
268              if (_symbolNames.Count == 1)
269              {
270                  _builder.Append(" ;; ").Append(_symbolNames.First().Value);
271              }
272              else if (_symbolNames.Count > 1)
273              {
274                  _builder.Append(" ;;");
275  
276                  foreach ((ulong value, string name) in _symbolNames)
277                  {
278                      _builder.Append(" 0x").Append(value.ToString("X")).Append(" = ").Append(name);
279                  }
280              }
281  
282              // Reset the set of symbols for the next Node we're going to dump.
283              _symbolNames.Clear();
284          }
285  
286          public static string GetDump(ControlFlowGraph cfg)
287          {
288              var dumper = new IRDumper(1);
289  
290              for (BasicBlock block = cfg.Blocks.First; block != null; block = block.ListNext)
291              {
292                  dumper.Indent();
293                  dumper.DumpBlockHeader(block);
294  
295                  dumper._builder.AppendLine();
296  
297                  dumper.IncreaseIndentation();
298  
299                  for (Operation node = block.Operations.First; node != default; node = node.ListNext)
300                  {
301                      dumper.Indent();
302                      dumper.DumpNode(cfg, node);
303  
304                      dumper._builder.AppendLine();
305                  }
306  
307                  dumper.DecreaseIndentation();
308              }
309  
310              return dumper._builder.ToString();
311          }
312  
313          private static string GetTypeName(OperandType type)
314          {
315              return type switch
316              {
317                  OperandType.None => "none",
318                  OperandType.I32 => "i32",
319                  OperandType.I64 => "i64",
320                  OperandType.FP32 => "f32",
321                  OperandType.FP64 => "f64",
322                  OperandType.V128 => "v128",
323                  _ => throw new ArgumentException($"Invalid operand type \"{type}\"."),
324              };
325          }
326      }
327  }