forked from Mirror/Ryujinx
36ec1bc6c0
* Relax block ordering constraints Before `block.Next` had to follow `block.ListNext`, now it does not. Instead `CodeGenerator` will now emit the necessary jump instructions to ensure control flow. This makes control flow and block order modifications easier. It also eliminates some simple cases of redundant branches. * Set PPTC version
303 lines
No EOL
9 KiB
C#
303 lines
No EOL
9 KiB
C#
using ARMeilleure.IntermediateRepresentation;
|
|
using ARMeilleure.Translation;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace ARMeilleure.Diagnostics
|
|
{
|
|
class IRDumper
|
|
{
|
|
private const string Indentation = " ";
|
|
|
|
private int _indentLevel;
|
|
|
|
private readonly StringBuilder _builder;
|
|
|
|
private readonly Dictionary<Operand, string> _localNames;
|
|
private readonly Dictionary<ulong, string> _symbolNames;
|
|
|
|
private IRDumper(int indent)
|
|
{
|
|
_indentLevel = indent;
|
|
|
|
_builder = new StringBuilder();
|
|
|
|
_localNames = new Dictionary<Operand, string>();
|
|
_symbolNames = new Dictionary<ulong, string>();
|
|
}
|
|
|
|
private void Indent()
|
|
{
|
|
_builder.EnsureCapacity(_builder.Capacity + _indentLevel * Indentation.Length);
|
|
|
|
for (int index = 0; index < _indentLevel; index++)
|
|
{
|
|
_builder.Append(Indentation);
|
|
}
|
|
}
|
|
|
|
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.SuccessorCount > 0)
|
|
{
|
|
_builder.Append(" (");
|
|
|
|
for (int i = 0; i < block.SuccessorCount; i++)
|
|
{
|
|
DumpBlockName(block.GetSuccessor(i));
|
|
|
|
if (i < block.SuccessorCount - 1)
|
|
{
|
|
_builder.Append(", ");
|
|
}
|
|
}
|
|
|
|
_builder.Append(')');
|
|
}
|
|
|
|
_builder.Append(':');
|
|
}
|
|
|
|
private void DumpOperand(Operand operand)
|
|
{
|
|
if (operand == null)
|
|
{
|
|
_builder.Append("<NULL>");
|
|
return;
|
|
}
|
|
|
|
_builder.Append(GetTypeName(operand.Type)).Append(' ');
|
|
|
|
switch (operand.Kind)
|
|
{
|
|
case OperandKind.LocalVariable:
|
|
if (!_localNames.TryGetValue(operand, out string localName))
|
|
{
|
|
localName = $"%{_localNames.Count}";
|
|
|
|
_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
|
|
{
|
|
_builder.Append(", ");
|
|
}
|
|
}
|
|
|
|
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:
|
|
bool comparison = false;
|
|
|
|
_builder.Append(operation.Instruction);
|
|
|
|
if (operation.Instruction == Instruction.Extended)
|
|
{
|
|
var intrinOp = (IntrinsicOperation)operation;
|
|
|
|
_builder.Append('.').Append(intrinOp.Intrinsic);
|
|
}
|
|
else if (operation.Instruction == Instruction.BranchIf ||
|
|
operation.Instruction == Instruction.Compare)
|
|
{
|
|
comparison = true;
|
|
}
|
|
|
|
_builder.Append(' ');
|
|
|
|
for (int index = 0; index < operation.SourcesCount; index++)
|
|
{
|
|
Operand source = operation.GetSource(index);
|
|
|
|
if (index < operation.SourcesCount - 1)
|
|
{
|
|
DumpOperand(source);
|
|
|
|
_builder.Append(", ");
|
|
}
|
|
else if (comparison)
|
|
{
|
|
_builder.Append((Comparison)source.AsInt32());
|
|
}
|
|
else
|
|
{
|
|
DumpOperand(source);
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
return type switch
|
|
{
|
|
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}\"."),
|
|
};
|
|
}
|
|
}
|
|
} |