From 80a879cb447507822e19acb0e51e123b768acd61 Mon Sep 17 00:00:00 2001
From: Nicholas Rodine <halfofastaple@gmail.com>
Date: Wed, 17 Aug 2022 18:49:43 -0500
Subject: [PATCH] Fix SpirV parse failure (#3597)

* Added .ToString overrides, to help diagnose and debug SpirV generated code.

* Added Spirv to team shared dictionary, so the word will not show up as a warning.

* Fixed bug where we were creating invalid constants (bool 0i and float 0i)

* Update Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update Spv.Generator/Instruction.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Adjusted spacing to match style of the rest of the code.

* Added handler for FP64(double) as well, for undefined aggregate types.

* Made the operand labels a static dictionary, to avoid re-allocation on each call.
Replaced Contains/Get with a TryGetValue, to reduce the number of dictionary lookups.

* Added newline between AllOperands and ToString().

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
---
 .../CodeGen/Spirv/CodeGenContext.cs           | 13 ++++++++++++-
 Ryujinx.sln.DotSettings                       |  1 +
 Spv.Generator/Instruction.cs                  | 15 +++++++++++++++
 Spv.Generator/InstructionOperands.cs          | 19 +++++++++++++++++++
 Spv.Generator/LiteralInteger.cs               |  2 ++
 Spv.Generator/LiteralString.cs                |  2 ++
 6 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
index bdd92553a..eeae45768 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
@@ -234,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
                     IrOperandType.Constant => GetConstant(type, operand),
                     IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
                     IrOperandType.LocalVariable => GetLocal(type, operand),
-                    IrOperandType.Undefined => Constant(GetType(type), 0),
+                    IrOperandType.Undefined => GetUndefined(type),
                     _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
                 };
             }
@@ -242,6 +242,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             throw new NotImplementedException(node.GetType().Name);
         }
 
+        private Instruction GetUndefined(AggregateType type)
+        {
+            return type switch
+            {
+                AggregateType.Bool => ConstantFalse(TypeBool()),
+                AggregateType.FP32 => Constant(TypeFP32(), 0f),
+                AggregateType.FP64 => Constant(TypeFP64(), 0d),
+                _ => Constant(GetType(type), 0)
+            };
+        }
+
         public Instruction GetAttributeElemPointer(int attr, bool isOutAttr, Instruction index, out AggregateType elemType)
         {
             var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
diff --git a/Ryujinx.sln.DotSettings b/Ryujinx.sln.DotSettings
index ed3582549..049bdaf69 100644
--- a/Ryujinx.sln.DotSettings
+++ b/Ryujinx.sln.DotSettings
@@ -15,6 +15,7 @@
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Ryujinx/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Sint/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Snorm/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=Spirv/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Srgb/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Unorm/@EntryIndexedValue">True</s:Boolean>
 </wpf:ResourceDictionary>
\ No newline at end of file
diff --git a/Spv.Generator/Instruction.cs b/Spv.Generator/Instruction.cs
index 27190f050..8ecfe6839 100644
--- a/Spv.Generator/Instruction.cs
+++ b/Spv.Generator/Instruction.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 
@@ -228,5 +229,19 @@ namespace Spv.Generator
         {
             return obj is Instruction instruction && Equals(instruction);
         }
+        
+        private static readonly Dictionary<Specification.Op, string[]> _operandLabels = new()
+        {
+            { Specification.Op.OpConstant, new [] { "Value" } },
+            { Specification.Op.OpTypeInt, new [] { "Width", "Signed" } },
+            { Specification.Op.OpTypeFloat, new [] { "Width" } }
+        };
+
+        public override string ToString()
+        {
+            var labels = _operandLabels.TryGetValue(Opcode, out var opLabels) ? opLabels : Array.Empty<string>();
+            var result = _resultType == null ? string.Empty : $"{_resultType} ";
+            return $"{result}{Opcode}{_operands.ToString(labels)}";
+        }
     }
 }
diff --git a/Spv.Generator/InstructionOperands.cs b/Spv.Generator/InstructionOperands.cs
index a349827a0..c48b004fa 100644
--- a/Spv.Generator/InstructionOperands.cs
+++ b/Spv.Generator/InstructionOperands.cs
@@ -1,4 +1,6 @@
 using System;
+using System.Collections.Generic;
+using System.Linq;
 using System.Runtime.InteropServices;
 
 namespace Spv.Generator
@@ -49,5 +51,22 @@ namespace Spv.Generator
                 Overflow[Count++] = operand;
             }
         }
+
+        private IEnumerable<Operand> AllOperands => new[] { Operand1, Operand2, Operand3, Operand4, Operand5 }
+            .Concat(Overflow ?? Array.Empty<Operand>())
+            .Take(Count);
+
+        public override string ToString()
+        {
+            return $"({string.Join(", ", AllOperands)})";
+        }
+
+        public string ToString(string[] labels)
+        {
+            var labeledParams = AllOperands.Zip(labels, (op, label) => $"{label}: {op}");
+            var unlabeledParams = AllOperands.Skip(labels.Length).Select(op => op.ToString());
+            var paramsToPrint = labeledParams.Concat(unlabeledParams);
+            return $"({string.Join(", ", paramsToPrint)})";
+        }
     }
 }
diff --git a/Spv.Generator/LiteralInteger.cs b/Spv.Generator/LiteralInteger.cs
index 3193ed6e5..22cb5484b 100644
--- a/Spv.Generator/LiteralInteger.cs
+++ b/Spv.Generator/LiteralInteger.cs
@@ -99,5 +99,7 @@ namespace Spv.Generator
         {
             return obj is LiteralInteger literalInteger && Equals(literalInteger);
         }
+
+        public override string ToString() => $"{_integerType} {_data}";
     }
 }
diff --git a/Spv.Generator/LiteralString.cs b/Spv.Generator/LiteralString.cs
index 4ed9e52bf..629ff7bf6 100644
--- a/Spv.Generator/LiteralString.cs
+++ b/Spv.Generator/LiteralString.cs
@@ -48,5 +48,7 @@ namespace Spv.Generator
         {
             return obj is LiteralString literalString && Equals(literalString);
         }
+
+        public override string ToString() => _value;
     }
 }