using System; using System.Collections.Generic; using System.Text; namespace SpirV { public struct ModuleHeader { public Version Version { get; set; } public string GeneratorVendor { get; set; } public string GeneratorName { get; set; } public int GeneratorVersion { get; set; } public uint Bound { get; set; } public uint Reserved { get; set; } } [Flags] public enum DisassemblyOptions { None, ShowTypes, ShowNames, Default = ShowTypes | ShowNames } public class Disassembler { public string Disassemble (Module module) { return Disassemble(module, DisassemblyOptions.Default); } public string Disassemble(Module module, DisassemblyOptions options) { m_sb.AppendLine("; SPIR-V"); m_sb.Append("; Version: ").Append(module.Header.Version).AppendLine(); if (module.Header.GeneratorName == null) { m_sb.Append("; Generator: unknown; ").Append(module.Header.GeneratorVersion).AppendLine(); } else { m_sb.Append("; Generator: ").Append(module.Header.GeneratorVendor).Append(' '). Append(module.Header.GeneratorName).Append("; ").Append(module.Header.GeneratorVersion).AppendLine(); } m_sb.Append("; Bound: ").Append(module.Header.Bound).AppendLine(); m_sb.Append("; Schema: ").Append(module.Header.Reserved).AppendLine(); string[] lines = new string[module.Instructions.Count + 1]; lines[0] = m_sb.ToString(); m_sb.Clear(); for (int i = 0; i < module.Instructions.Count; i++) { ParsedInstruction instruction = module.Instructions[i]; PrintInstruction(m_sb, instruction, options); lines[i + 1] = m_sb.ToString(); m_sb.Clear(); } int longestPrefix = 0; for (int i = 0; i < lines.Length; i++) { string line = lines[i]; longestPrefix = Math.Max(longestPrefix, line.IndexOf('=')); if (longestPrefix > 50) { longestPrefix = 50; break; } } m_sb.Append(lines[0]); for (int i = 1; i < lines.Length; i++) { string line = lines[i]; int index = line.IndexOf('='); if (index == -1) { m_sb.Append(' ', longestPrefix + 4); m_sb.Append(line); } else { int pad = Math.Max(0, longestPrefix - index); m_sb.Append(' ', pad); m_sb.Append(line, 0, index); m_sb.Append('='); m_sb.Append(line, index + 1, line.Length - index - 1); } m_sb.AppendLine(); } string result = m_sb.ToString(); m_sb.Clear(); return result; } private static void PrintInstruction(StringBuilder sb, ParsedInstruction instruction, DisassemblyOptions options) { if (instruction.Operands.Count == 0) { sb.Append(instruction.Instruction.Name); return; } int currentOperand = 0; if (instruction.Instruction.Operands[currentOperand].Type is IdResultType) { if (options.HasFlag(DisassemblyOptions.ShowTypes)) { instruction.ResultType.ToString(sb).Append(' '); } ++currentOperand; } if (currentOperand < instruction.Operands.Count && instruction.Instruction.Operands[currentOperand].Type is IdResult) { if (!options.HasFlag(DisassemblyOptions.ShowNames) || string.IsNullOrWhiteSpace(instruction.Name)) { PrintOperandValue(sb, instruction.Operands[currentOperand].Value, options); } else { sb.Append(instruction.Name); } sb.Append(" = "); ++currentOperand; } sb.Append(instruction.Instruction.Name); sb.Append(' '); for (; currentOperand < instruction.Operands.Count; ++currentOperand) { PrintOperandValue(sb, instruction.Operands[currentOperand].Value, options); sb.Append(' '); } } private static void PrintOperandValue(StringBuilder sb, object value, DisassemblyOptions options) { switch (value) { case System.Type t: sb.Append(t.Name); break; case string s: { sb.Append('"'); sb.Append(s); sb.Append('"'); } break; case ObjectReference or: { if (options.HasFlag(DisassemblyOptions.ShowNames) && or.Reference != null && !string.IsNullOrWhiteSpace(or.Reference.Name)) { sb.Append(or.Reference.Name); } else { or.ToString(sb); } } break; case IBitEnumOperandValue beov: PrintBitEnumValue(sb, beov, options); break; case IValueEnumOperandValue veov: PrintValueEnumValue(sb, veov, options); break; case VaryingOperandValue varOpVal: varOpVal.ToString(sb); break; default: sb.Append(value); break; } } private static void PrintBitEnumValue(StringBuilder sb, IBitEnumOperandValue enumOperandValue, DisassemblyOptions options) { foreach (uint key in enumOperandValue.Values.Keys) { sb.Append(enumOperandValue.EnumerationType.GetEnumName(key)); IReadOnlyList value = enumOperandValue.Values[key]; if (value.Count != 0) { sb.Append(' '); foreach (object v in value) { PrintOperandValue(sb, v, options); } } } } private static void PrintValueEnumValue(StringBuilder sb, IValueEnumOperandValue valueOperandValue, DisassemblyOptions options) { sb.Append(valueOperandValue.Key); if (valueOperandValue.Value is IList valueList && valueList.Count > 0) { sb.Append(' '); foreach (object v in valueList) { PrintOperandValue(sb, v, options); } } } private readonly StringBuilder m_sb = new StringBuilder(); } }