mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-21 16:43:35 +00:00
Some optimizations to Spv.Generator
- Dictionary for lookups of type declarations, constants, extinst - LiteralInteger internal data format -> ushort - Deterministic HashCode implementation to avoid spirv result not being the same between runs - Inline operand list instead of List<T>, falls back to array if many operands. (large performance boost) TODO: improve instruction allocation, structured program creator, ssa?
This commit is contained in:
parent
12dec18f39
commit
bf94f4c7d6
9 changed files with 356 additions and 73 deletions
30
Spv.Generator/ConstantKey.cs
Normal file
30
Spv.Generator/ConstantKey.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Spv.Generator
|
||||
{
|
||||
internal struct ConstantKey : IEquatable<ConstantKey>
|
||||
{
|
||||
private Instruction _constant;
|
||||
|
||||
public ConstantKey(Instruction constant)
|
||||
{
|
||||
_constant = constant;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_constant.Opcode, _constant.GetHashCodeContent(), _constant.GetHashCodeResultType());
|
||||
}
|
||||
|
||||
public bool Equals(ConstantKey other)
|
||||
{
|
||||
return _constant.Opcode == other._constant.Opcode && _constant.EqualsContent(other._constant) && _constant.EqualsResultType(other._constant);
|
||||
}
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object obj)
|
||||
{
|
||||
return obj is ConstantKey && Equals((ConstantKey)obj);
|
||||
}
|
||||
}
|
||||
}
|
110
Spv.Generator/DeterministicHashCode.cs
Normal file
110
Spv.Generator/DeterministicHashCode.cs
Normal file
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Spv.Generator
|
||||
{
|
||||
/// <summary>
|
||||
/// Similar to System.HashCode, but without introducing random values.
|
||||
/// The same primes and shifts are used.
|
||||
/// </summary>
|
||||
internal static class DeterministicHashCode
|
||||
{
|
||||
private const uint Prime1 = 2654435761U;
|
||||
private const uint Prime2 = 2246822519U;
|
||||
private const uint Prime3 = 3266489917U;
|
||||
private const uint Prime4 = 668265263U;
|
||||
|
||||
public static int GetHashCode(string value)
|
||||
{
|
||||
uint hash = (uint)value.Length + Prime1;
|
||||
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
hash += (hash << 7) ^ value[i];
|
||||
}
|
||||
|
||||
return (int)MixFinal(hash);
|
||||
}
|
||||
|
||||
public static int Combine<T>(ReadOnlySpan<T> values)
|
||||
{
|
||||
uint hashCode = Prime2;
|
||||
hashCode += 4 * (uint)values.Length;
|
||||
|
||||
foreach (T value in values)
|
||||
{
|
||||
uint hc = (uint)(value?.GetHashCode() ?? 0);
|
||||
hashCode = MixStep(hashCode, hc);
|
||||
}
|
||||
|
||||
return (int)MixFinal(hashCode);
|
||||
}
|
||||
|
||||
public static int Combine<T1, T2>(T1 value1, T2 value2)
|
||||
{
|
||||
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
||||
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
||||
|
||||
uint hash = Prime2;
|
||||
hash += 8;
|
||||
|
||||
hash = MixStep(hash, hc1);
|
||||
hash = MixStep(hash, hc2);
|
||||
|
||||
return (int)MixFinal(hash);
|
||||
}
|
||||
|
||||
public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3)
|
||||
{
|
||||
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
||||
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
||||
uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
|
||||
|
||||
uint hash = Prime2;
|
||||
hash += 12;
|
||||
|
||||
hash = MixStep(hash, hc1);
|
||||
hash = MixStep(hash, hc2);
|
||||
hash = MixStep(hash, hc3);
|
||||
|
||||
return (int)MixFinal(hash);
|
||||
}
|
||||
|
||||
public static int Combine<T1, T2, T3, T4>(T1 value1, T2 value2, T3 value3, T4 value4)
|
||||
{
|
||||
uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
|
||||
uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
|
||||
uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
|
||||
uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
|
||||
|
||||
uint hash = Prime2;
|
||||
hash += 16;
|
||||
|
||||
hash = MixStep(hash, hc1);
|
||||
hash = MixStep(hash, hc2);
|
||||
hash = MixStep(hash, hc3);
|
||||
hash = MixStep(hash, hc4);
|
||||
|
||||
return (int)MixFinal(hash);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint MixStep(uint hashCode, uint mixValue)
|
||||
{
|
||||
return BitOperations.RotateLeft(hashCode + mixValue * Prime3, 17) * Prime4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint MixFinal(uint hash)
|
||||
{
|
||||
hash ^= hash >> 15;
|
||||
hash *= Prime2;
|
||||
hash ^= hash >> 13;
|
||||
hash *= Prime3;
|
||||
hash ^= hash >> 16;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
30
Spv.Generator/DeterministicStringKey.cs
Normal file
30
Spv.Generator/DeterministicStringKey.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Spv.Generator
|
||||
{
|
||||
internal class DeterministicStringKey : IEquatable<DeterministicStringKey>
|
||||
{
|
||||
private string _value;
|
||||
|
||||
public DeterministicStringKey(string value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return DeterministicHashCode.GetHashCode(_value);
|
||||
}
|
||||
|
||||
public bool Equals(DeterministicStringKey other)
|
||||
{
|
||||
return _value == other._value;
|
||||
}
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object obj)
|
||||
{
|
||||
return obj is DeterministicStringKey && Equals((DeterministicStringKey)obj);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Spv.Generator
|
||||
{
|
||||
|
@ -12,7 +10,7 @@ namespace Spv.Generator
|
|||
|
||||
public Specification.Op Opcode { get; private set; }
|
||||
private Instruction _resultType;
|
||||
public List<Operand> _operands;
|
||||
private InstructionOperands _operands;
|
||||
|
||||
public uint Id { get; set; }
|
||||
|
||||
|
@ -22,7 +20,7 @@ namespace Spv.Generator
|
|||
Id = id;
|
||||
_resultType = resultType;
|
||||
|
||||
_operands = new List<Operand>();
|
||||
_operands = new InstructionOperands();
|
||||
}
|
||||
|
||||
public void SetId(uint id)
|
||||
|
@ -46,9 +44,10 @@ namespace Spv.Generator
|
|||
result += _resultType.WordCount;
|
||||
}
|
||||
|
||||
foreach (Operand operand in _operands)
|
||||
Span<Operand> operands = _operands.ToSpan();
|
||||
for (int i = 0; i < operands.Length; i++)
|
||||
{
|
||||
result += operand.WordCount;
|
||||
result += operands[i].WordCount;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -101,7 +100,7 @@ namespace Spv.Generator
|
|||
AddOperand(new LiteralString(value));
|
||||
}
|
||||
|
||||
public void AddOperand<T>(T value) where T: struct
|
||||
public void AddOperand<T>(T value) where T: Enum
|
||||
{
|
||||
if (!typeof(T).IsPrimitive && !typeof(T).IsEnum)
|
||||
{
|
||||
|
@ -124,9 +123,10 @@ namespace Spv.Generator
|
|||
writer.Write(Id);
|
||||
}
|
||||
|
||||
foreach (Operand operand in _operands)
|
||||
Span<Operand> operands = _operands.ToSpan();
|
||||
for (int i = 0; i < operands.Length; i++)
|
||||
{
|
||||
operand.WriteOperand(writer);
|
||||
operands[i].WriteOperand(writer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,23 @@ namespace Spv.Generator
|
|||
|
||||
public bool EqualsContent(Instruction cmpObj)
|
||||
{
|
||||
return _operands.SequenceEqual(cmpObj._operands);
|
||||
Span<Operand> thisOperands = _operands.ToSpan();
|
||||
Span<Operand> cmpOperands = cmpObj._operands.ToSpan();
|
||||
|
||||
if (thisOperands.Length != cmpOperands.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < thisOperands.Length; i++)
|
||||
{
|
||||
if (!thisOperands[i].Equals(cmpOperands[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool EqualsResultType(Instruction cmpObj)
|
||||
|
@ -196,9 +212,19 @@ namespace Spv.Generator
|
|||
return _resultType.Opcode == cmpObj._resultType.Opcode && _resultType.EqualsContent(cmpObj._resultType);
|
||||
}
|
||||
|
||||
public int GetHashCodeContent()
|
||||
{
|
||||
return DeterministicHashCode.Combine<Operand>(_operands.ToSpan());
|
||||
}
|
||||
|
||||
public int GetHashCodeResultType()
|
||||
{
|
||||
return DeterministicHashCode.Combine(_resultType.Opcode, _resultType.GetHashCodeContent());
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Opcode, Id, _resultType, _operands);
|
||||
return DeterministicHashCode.Combine(Opcode, Id, _resultType, DeterministicHashCode.Combine<Operand>(_operands.ToSpan()));
|
||||
}
|
||||
|
||||
public bool Equals(Operand obj)
|
||||
|
|
57
Spv.Generator/InstructionOperands.cs
Normal file
57
Spv.Generator/InstructionOperands.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spv.Generator
|
||||
{
|
||||
public struct InstructionOperands
|
||||
{
|
||||
private const int InternalCount = 5;
|
||||
|
||||
public int Count;
|
||||
public Operand Operand1;
|
||||
public Operand Operand2;
|
||||
public Operand Operand3;
|
||||
public Operand Operand4;
|
||||
public Operand Operand5;
|
||||
public Operand[] Overflow;
|
||||
|
||||
public Span<Operand> ToSpan()
|
||||
{
|
||||
if (Count > InternalCount)
|
||||
{
|
||||
return MemoryMarshal.CreateSpan(ref this.Overflow[0], Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return MemoryMarshal.CreateSpan(ref this.Operand1, Count);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(Operand operand)
|
||||
{
|
||||
if (Count < InternalCount)
|
||||
{
|
||||
MemoryMarshal.CreateSpan(ref this.Operand1, Count + 1)[Count] = operand;
|
||||
Count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Overflow == null)
|
||||
{
|
||||
Overflow = new Operand[InternalCount * 2];
|
||||
MemoryMarshal.CreateSpan(ref this.Operand1, InternalCount).CopyTo(Overflow.AsSpan());
|
||||
}
|
||||
else if (Count == Overflow.Length)
|
||||
{
|
||||
Array.Resize(ref Overflow, Overflow.Length * 2);
|
||||
}
|
||||
|
||||
Overflow[Count++] = operand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Spv.Generator
|
||||
{
|
||||
|
@ -20,43 +18,45 @@ namespace Spv.Generator
|
|||
}
|
||||
|
||||
private IntegerType _integerType;
|
||||
private ulong _data;
|
||||
|
||||
private byte[] _data;
|
||||
public ushort WordCount { get; }
|
||||
|
||||
private LiteralInteger(byte[] data, IntegerType integerType)
|
||||
private LiteralInteger(ulong data, IntegerType integerType, ushort wordCount)
|
||||
{
|
||||
_data = data;
|
||||
_integerType = integerType;
|
||||
|
||||
WordCount = wordCount;
|
||||
}
|
||||
|
||||
public static implicit operator LiteralInteger(int value) => Create(value, IntegerType.Int32);
|
||||
public static implicit operator LiteralInteger(uint value) => Create(value, IntegerType.UInt32);
|
||||
public static implicit operator LiteralInteger(long value) => Create(value, IntegerType.Int64);
|
||||
public static implicit operator LiteralInteger(ulong value) => Create(value, IntegerType.UInt64);
|
||||
public static implicit operator LiteralInteger(float value) => Create(value, IntegerType.Float32);
|
||||
public static implicit operator LiteralInteger(double value) => Create(value, IntegerType.Float64);
|
||||
public static implicit operator LiteralInteger(Enum value) => Create((int)Convert.ChangeType(value, typeof(int)), IntegerType.Int32);
|
||||
public static implicit operator LiteralInteger(int value) => new LiteralInteger((ulong)value, IntegerType.Int32, 1);
|
||||
public static implicit operator LiteralInteger(uint value) => new LiteralInteger(value, IntegerType.UInt32, 1);
|
||||
public static implicit operator LiteralInteger(long value) => new LiteralInteger((ulong)value, IntegerType.Int64, 2);
|
||||
public static implicit operator LiteralInteger(ulong value) => new LiteralInteger(value, IntegerType.UInt64, 2);
|
||||
public static implicit operator LiteralInteger(float value) => new LiteralInteger(BitConverter.SingleToUInt32Bits(value), IntegerType.Float32, 1);
|
||||
public static implicit operator LiteralInteger(double value) => new LiteralInteger(BitConverter.DoubleToUInt64Bits(value), IntegerType.Float64, 2);
|
||||
public static implicit operator LiteralInteger(Enum value) => new LiteralInteger((ulong)Convert.ChangeType(value, typeof(ulong)), IntegerType.Int32, 1);
|
||||
|
||||
// NOTE: this is not in the standard, but this is some syntax sugar useful in some instructions (TypeInt ect)
|
||||
public static implicit operator LiteralInteger(bool value) => Create(Convert.ToInt32(value), IntegerType.Int32);
|
||||
public static implicit operator LiteralInteger(bool value) => new LiteralInteger(Convert.ToUInt64(value), IntegerType.Int32, 1);
|
||||
|
||||
|
||||
public static LiteralInteger CreateForEnum<T>(T value) where T : struct
|
||||
public static LiteralInteger CreateForEnum<T>(T value) where T : Enum
|
||||
{
|
||||
return Create(value, IntegerType.Int32);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static LiteralInteger Create<T>(T value, IntegerType integerType) where T: struct
|
||||
{
|
||||
return new LiteralInteger(MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1)).ToArray(), integerType);
|
||||
}
|
||||
|
||||
public ushort WordCount => (ushort)(_data.Length / 4);
|
||||
|
||||
public void WriteOperand(BinaryWriter writer)
|
||||
{
|
||||
if (WordCount == 1)
|
||||
{
|
||||
writer.Write((uint)_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(_data);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
|
@ -65,12 +65,12 @@ namespace Spv.Generator
|
|||
|
||||
public bool Equals(LiteralInteger cmpObj)
|
||||
{
|
||||
return Type == cmpObj.Type && _integerType == cmpObj._integerType && _data.SequenceEqual(cmpObj._data);
|
||||
return Type == cmpObj.Type && _integerType == cmpObj._integerType && _data == cmpObj._data;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Type, _data);
|
||||
return DeterministicHashCode.Combine(Type, _data);
|
||||
}
|
||||
|
||||
public bool Equals(Operand obj)
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace Spv.Generator
|
|||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Type, _value);
|
||||
return DeterministicHashCode.Combine(Type, DeterministicHashCode.GetHashCode(_value));
|
||||
}
|
||||
|
||||
public bool Equals(Operand obj)
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Spv.Generator
|
||||
|
@ -19,7 +17,7 @@ namespace Spv.Generator
|
|||
// Follow spec order here why keeping it as dumb as possible.
|
||||
private List<Capability> _capabilities;
|
||||
private List<string> _extensions;
|
||||
private List<Instruction> _extInstImports;
|
||||
private Dictionary<DeterministicStringKey, Instruction> _extInstImports;
|
||||
private AddressingModel _addressingModel;
|
||||
private MemoryModel _memoryModel;
|
||||
|
||||
|
@ -29,9 +27,11 @@ namespace Spv.Generator
|
|||
private List<Instruction> _annotations;
|
||||
|
||||
// In the declaration block.
|
||||
private List<Instruction> _typeDeclarations;
|
||||
private Dictionary<TypeDeclarationKey, Instruction> _typeDeclarations;
|
||||
// In the declaration block.
|
||||
private List<Instruction> _globals;
|
||||
// In the declaration block.
|
||||
private Dictionary<ConstantKey, Instruction> _constants;
|
||||
// In the declaration block, for function that aren't defined in the module.
|
||||
private List<Instruction> _functionsDeclarations;
|
||||
|
||||
|
@ -43,14 +43,15 @@ namespace Spv.Generator
|
|||
_bound = 1;
|
||||
_capabilities = new List<Capability>();
|
||||
_extensions = new List<string>();
|
||||
_extInstImports = new List<Instruction>();
|
||||
_extInstImports = new Dictionary<DeterministicStringKey, Instruction>();
|
||||
_addressingModel = AddressingModel.Logical;
|
||||
_memoryModel = MemoryModel.Simple;
|
||||
_entrypoints = new List<Instruction>();
|
||||
_executionModes = new List<Instruction>();
|
||||
_debug = new List<Instruction>();
|
||||
_annotations = new List<Instruction>();
|
||||
_typeDeclarations = new List<Instruction>();
|
||||
_typeDeclarations = new Dictionary<TypeDeclarationKey, Instruction>();
|
||||
_constants = new Dictionary<ConstantKey, Instruction>();
|
||||
_globals = new List<Instruction>();
|
||||
_functionsDeclarations = new List<Instruction>();
|
||||
_functionsDefinitions = new List<Instruction>();
|
||||
|
@ -73,44 +74,43 @@ namespace Spv.Generator
|
|||
|
||||
public Instruction AddExtInstImport(string import)
|
||||
{
|
||||
Instruction instruction = new Instruction(Op.OpExtInstImport);
|
||||
instruction.AddOperand(import);
|
||||
var key = new DeterministicStringKey(import);
|
||||
|
||||
foreach (Instruction extInstImport in _extInstImports)
|
||||
{
|
||||
if (extInstImport.Opcode == Op.OpExtInstImport && extInstImport.EqualsContent(instruction))
|
||||
if (_extInstImports.TryGetValue(key, out Instruction extInstImport))
|
||||
{
|
||||
// update the duplicate instance to use the good id so it ends up being encoded right.
|
||||
return extInstImport;
|
||||
}
|
||||
}
|
||||
|
||||
Instruction instruction = new Instruction(Op.OpExtInstImport);
|
||||
instruction.AddOperand(import);
|
||||
|
||||
instruction.SetId(GetNewId());
|
||||
|
||||
_extInstImports.Add(instruction);
|
||||
_extInstImports.Add(key, instruction);
|
||||
|
||||
return instruction;
|
||||
}
|
||||
|
||||
private void AddTypeDeclaration(Instruction instruction, bool forceIdAllocation)
|
||||
{
|
||||
var key = new TypeDeclarationKey(instruction);
|
||||
|
||||
if (!forceIdAllocation)
|
||||
{
|
||||
foreach (Instruction typeDeclaration in _typeDeclarations)
|
||||
{
|
||||
if (typeDeclaration.Opcode == instruction.Opcode && typeDeclaration.EqualsContent(instruction))
|
||||
if (_typeDeclarations.TryGetValue(key, out Instruction typeDeclaration))
|
||||
{
|
||||
// update the duplicate instance to use the good id so it ends up being encoded right.
|
||||
|
||||
instruction.SetId(typeDeclaration.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instruction.SetId(GetNewId());
|
||||
|
||||
_typeDeclarations.Add(instruction);
|
||||
_typeDeclarations.Add(key, instruction);
|
||||
}
|
||||
|
||||
public void AddEntryPoint(ExecutionModel executionModel, Instruction function, string name, params Instruction[] interfaces)
|
||||
|
@ -195,20 +195,19 @@ namespace Spv.Generator
|
|||
constant.Opcode == Op.OpConstantNull ||
|
||||
constant.Opcode == Op.OpConstantComposite);
|
||||
|
||||
foreach (Instruction global in _globals)
|
||||
{
|
||||
if (global.Opcode == constant.Opcode && global.EqualsContent(constant) && global.EqualsResultType(constant))
|
||||
var key = new ConstantKey(constant);
|
||||
|
||||
if (_constants.TryGetValue(key, out Instruction global))
|
||||
{
|
||||
// update the duplicate instance to use the good id so it ends up being encoded right.
|
||||
constant.SetId(global.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
constant.SetId(GetNewId());
|
||||
|
||||
_globals.Add(constant);
|
||||
_constants.Add(key, constant);
|
||||
}
|
||||
|
||||
public Instruction ExtInst(Instruction resultType, Instruction set, LiteralInteger instruction, params Operand[] parameters)
|
||||
|
@ -275,7 +274,7 @@ namespace Spv.Generator
|
|||
}
|
||||
|
||||
// 3.
|
||||
foreach (Instruction extInstImport in _extInstImports)
|
||||
foreach (Instruction extInstImport in _extInstImports.Values)
|
||||
{
|
||||
extInstImport.Write(writer);
|
||||
}
|
||||
|
@ -313,8 +312,9 @@ namespace Spv.Generator
|
|||
|
||||
// Ensure that everything is in the right order in the declarations section
|
||||
List<Instruction> declarations = new List<Instruction>();
|
||||
declarations.AddRange(_typeDeclarations);
|
||||
declarations.AddRange(_typeDeclarations.Values);
|
||||
declarations.AddRange(_globals);
|
||||
declarations.AddRange(_constants.Values);
|
||||
declarations.Sort((Instruction x, Instruction y) => x.Id.CompareTo(y.Id));
|
||||
|
||||
// 9.
|
||||
|
|
30
Spv.Generator/TypeDeclarationKey.cs
Normal file
30
Spv.Generator/TypeDeclarationKey.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Spv.Generator
|
||||
{
|
||||
internal struct TypeDeclarationKey : IEquatable<TypeDeclarationKey>
|
||||
{
|
||||
private Instruction _typeDeclaration;
|
||||
|
||||
public TypeDeclarationKey(Instruction typeDeclaration)
|
||||
{
|
||||
_typeDeclaration = typeDeclaration;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return DeterministicHashCode.Combine(_typeDeclaration.Opcode, _typeDeclaration.GetHashCodeContent());
|
||||
}
|
||||
|
||||
public bool Equals(TypeDeclarationKey other)
|
||||
{
|
||||
return _typeDeclaration.Opcode == other._typeDeclaration.Opcode && _typeDeclaration.EqualsContent(other._typeDeclaration);
|
||||
}
|
||||
|
||||
public override bool Equals([NotNullWhen(true)] object obj)
|
||||
{
|
||||
return obj is TypeDeclarationKey && Equals((TypeDeclarationKey)obj);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue