mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-22 09:03:36 +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;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Spv.Generator
|
namespace Spv.Generator
|
||||||
{
|
{
|
||||||
|
@ -12,7 +10,7 @@ namespace Spv.Generator
|
||||||
|
|
||||||
public Specification.Op Opcode { get; private set; }
|
public Specification.Op Opcode { get; private set; }
|
||||||
private Instruction _resultType;
|
private Instruction _resultType;
|
||||||
public List<Operand> _operands;
|
private InstructionOperands _operands;
|
||||||
|
|
||||||
public uint Id { get; set; }
|
public uint Id { get; set; }
|
||||||
|
|
||||||
|
@ -22,7 +20,7 @@ namespace Spv.Generator
|
||||||
Id = id;
|
Id = id;
|
||||||
_resultType = resultType;
|
_resultType = resultType;
|
||||||
|
|
||||||
_operands = new List<Operand>();
|
_operands = new InstructionOperands();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetId(uint id)
|
public void SetId(uint id)
|
||||||
|
@ -46,9 +44,10 @@ namespace Spv.Generator
|
||||||
result += _resultType.WordCount;
|
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;
|
return result;
|
||||||
|
@ -101,7 +100,7 @@ namespace Spv.Generator
|
||||||
AddOperand(new LiteralString(value));
|
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)
|
if (!typeof(T).IsPrimitive && !typeof(T).IsEnum)
|
||||||
{
|
{
|
||||||
|
@ -124,9 +123,10 @@ namespace Spv.Generator
|
||||||
writer.Write(Id);
|
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)
|
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)
|
public bool EqualsResultType(Instruction cmpObj)
|
||||||
|
@ -196,9 +212,19 @@ namespace Spv.Generator
|
||||||
return _resultType.Opcode == cmpObj._resultType.Opcode && _resultType.EqualsContent(cmpObj._resultType);
|
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()
|
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)
|
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;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Spv.Generator
|
namespace Spv.Generator
|
||||||
{
|
{
|
||||||
|
@ -20,43 +18,45 @@ namespace Spv.Generator
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntegerType _integerType;
|
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;
|
_data = data;
|
||||||
_integerType = integerType;
|
_integerType = integerType;
|
||||||
|
|
||||||
|
WordCount = wordCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator LiteralInteger(int value) => Create(value, IntegerType.Int32);
|
public static implicit operator LiteralInteger(int value) => new LiteralInteger((ulong)value, IntegerType.Int32, 1);
|
||||||
public static implicit operator LiteralInteger(uint value) => Create(value, IntegerType.UInt32);
|
public static implicit operator LiteralInteger(uint value) => new LiteralInteger(value, IntegerType.UInt32, 1);
|
||||||
public static implicit operator LiteralInteger(long value) => Create(value, IntegerType.Int64);
|
public static implicit operator LiteralInteger(long value) => new LiteralInteger((ulong)value, IntegerType.Int64, 2);
|
||||||
public static implicit operator LiteralInteger(ulong value) => Create(value, IntegerType.UInt64);
|
public static implicit operator LiteralInteger(ulong value) => new LiteralInteger(value, IntegerType.UInt64, 2);
|
||||||
public static implicit operator LiteralInteger(float value) => Create(value, IntegerType.Float32);
|
public static implicit operator LiteralInteger(float value) => new LiteralInteger(BitConverter.SingleToUInt32Bits(value), IntegerType.Float32, 1);
|
||||||
public static implicit operator LiteralInteger(double value) => Create(value, IntegerType.Float64);
|
public static implicit operator LiteralInteger(double value) => new LiteralInteger(BitConverter.DoubleToUInt64Bits(value), IntegerType.Float64, 2);
|
||||||
public static implicit operator LiteralInteger(Enum value) => Create((int)Convert.ChangeType(value, typeof(int)), IntegerType.Int32);
|
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)
|
// 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 : Enum
|
||||||
public static LiteralInteger CreateForEnum<T>(T value) where T : struct
|
|
||||||
{
|
{
|
||||||
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)
|
public void WriteOperand(BinaryWriter writer)
|
||||||
|
{
|
||||||
|
if (WordCount == 1)
|
||||||
|
{
|
||||||
|
writer.Write((uint)_data);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
writer.Write(_data);
|
writer.Write(_data);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
|
@ -65,12 +65,12 @@ namespace Spv.Generator
|
||||||
|
|
||||||
public bool Equals(LiteralInteger cmpObj)
|
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()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine(Type, _data);
|
return DeterministicHashCode.Combine(Type, _data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(Operand obj)
|
public bool Equals(Operand obj)
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace Spv.Generator
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine(Type, _value);
|
return DeterministicHashCode.Combine(Type, DeterministicHashCode.GetHashCode(_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(Operand obj)
|
public bool Equals(Operand obj)
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
|
|
||||||
namespace Spv.Generator
|
namespace Spv.Generator
|
||||||
|
@ -19,7 +17,7 @@ namespace Spv.Generator
|
||||||
// Follow spec order here why keeping it as dumb as possible.
|
// Follow spec order here why keeping it as dumb as possible.
|
||||||
private List<Capability> _capabilities;
|
private List<Capability> _capabilities;
|
||||||
private List<string> _extensions;
|
private List<string> _extensions;
|
||||||
private List<Instruction> _extInstImports;
|
private Dictionary<DeterministicStringKey, Instruction> _extInstImports;
|
||||||
private AddressingModel _addressingModel;
|
private AddressingModel _addressingModel;
|
||||||
private MemoryModel _memoryModel;
|
private MemoryModel _memoryModel;
|
||||||
|
|
||||||
|
@ -29,9 +27,11 @@ namespace Spv.Generator
|
||||||
private List<Instruction> _annotations;
|
private List<Instruction> _annotations;
|
||||||
|
|
||||||
// In the declaration block.
|
// In the declaration block.
|
||||||
private List<Instruction> _typeDeclarations;
|
private Dictionary<TypeDeclarationKey, Instruction> _typeDeclarations;
|
||||||
// In the declaration block.
|
// In the declaration block.
|
||||||
private List<Instruction> _globals;
|
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.
|
// In the declaration block, for function that aren't defined in the module.
|
||||||
private List<Instruction> _functionsDeclarations;
|
private List<Instruction> _functionsDeclarations;
|
||||||
|
|
||||||
|
@ -43,14 +43,15 @@ namespace Spv.Generator
|
||||||
_bound = 1;
|
_bound = 1;
|
||||||
_capabilities = new List<Capability>();
|
_capabilities = new List<Capability>();
|
||||||
_extensions = new List<string>();
|
_extensions = new List<string>();
|
||||||
_extInstImports = new List<Instruction>();
|
_extInstImports = new Dictionary<DeterministicStringKey, Instruction>();
|
||||||
_addressingModel = AddressingModel.Logical;
|
_addressingModel = AddressingModel.Logical;
|
||||||
_memoryModel = MemoryModel.Simple;
|
_memoryModel = MemoryModel.Simple;
|
||||||
_entrypoints = new List<Instruction>();
|
_entrypoints = new List<Instruction>();
|
||||||
_executionModes = new List<Instruction>();
|
_executionModes = new List<Instruction>();
|
||||||
_debug = new List<Instruction>();
|
_debug = new List<Instruction>();
|
||||||
_annotations = 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>();
|
_globals = new List<Instruction>();
|
||||||
_functionsDeclarations = new List<Instruction>();
|
_functionsDeclarations = new List<Instruction>();
|
||||||
_functionsDefinitions = new List<Instruction>();
|
_functionsDefinitions = new List<Instruction>();
|
||||||
|
@ -73,44 +74,43 @@ namespace Spv.Generator
|
||||||
|
|
||||||
public Instruction AddExtInstImport(string import)
|
public Instruction AddExtInstImport(string import)
|
||||||
{
|
{
|
||||||
Instruction instruction = new Instruction(Op.OpExtInstImport);
|
var key = new DeterministicStringKey(import);
|
||||||
instruction.AddOperand(import);
|
|
||||||
|
|
||||||
foreach (Instruction extInstImport in _extInstImports)
|
if (_extInstImports.TryGetValue(key, out Instruction extInstImport))
|
||||||
{
|
|
||||||
if (extInstImport.Opcode == Op.OpExtInstImport && extInstImport.EqualsContent(instruction))
|
|
||||||
{
|
{
|
||||||
// update the duplicate instance to use the good id so it ends up being encoded right.
|
// update the duplicate instance to use the good id so it ends up being encoded right.
|
||||||
return extInstImport;
|
return extInstImport;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Instruction instruction = new Instruction(Op.OpExtInstImport);
|
||||||
|
instruction.AddOperand(import);
|
||||||
|
|
||||||
instruction.SetId(GetNewId());
|
instruction.SetId(GetNewId());
|
||||||
|
|
||||||
_extInstImports.Add(instruction);
|
_extInstImports.Add(key, instruction);
|
||||||
|
|
||||||
return instruction;
|
return instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddTypeDeclaration(Instruction instruction, bool forceIdAllocation)
|
private void AddTypeDeclaration(Instruction instruction, bool forceIdAllocation)
|
||||||
{
|
{
|
||||||
|
var key = new TypeDeclarationKey(instruction);
|
||||||
|
|
||||||
if (!forceIdAllocation)
|
if (!forceIdAllocation)
|
||||||
{
|
{
|
||||||
foreach (Instruction typeDeclaration in _typeDeclarations)
|
if (_typeDeclarations.TryGetValue(key, out Instruction typeDeclaration))
|
||||||
{
|
|
||||||
if (typeDeclaration.Opcode == instruction.Opcode && typeDeclaration.EqualsContent(instruction))
|
|
||||||
{
|
{
|
||||||
// update the duplicate instance to use the good id so it ends up being encoded right.
|
// update the duplicate instance to use the good id so it ends up being encoded right.
|
||||||
|
|
||||||
instruction.SetId(typeDeclaration.Id);
|
instruction.SetId(typeDeclaration.Id);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
instruction.SetId(GetNewId());
|
instruction.SetId(GetNewId());
|
||||||
|
|
||||||
_typeDeclarations.Add(instruction);
|
_typeDeclarations.Add(key, instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEntryPoint(ExecutionModel executionModel, Instruction function, string name, params Instruction[] interfaces)
|
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.OpConstantNull ||
|
||||||
constant.Opcode == Op.OpConstantComposite);
|
constant.Opcode == Op.OpConstantComposite);
|
||||||
|
|
||||||
foreach (Instruction global in _globals)
|
var key = new ConstantKey(constant);
|
||||||
{
|
|
||||||
if (global.Opcode == constant.Opcode && global.EqualsContent(constant) && global.EqualsResultType(constant))
|
if (_constants.TryGetValue(key, out Instruction global))
|
||||||
{
|
{
|
||||||
// update the duplicate instance to use the good id so it ends up being encoded right.
|
// update the duplicate instance to use the good id so it ends up being encoded right.
|
||||||
constant.SetId(global.Id);
|
constant.SetId(global.Id);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
constant.SetId(GetNewId());
|
constant.SetId(GetNewId());
|
||||||
|
|
||||||
_globals.Add(constant);
|
_constants.Add(key, constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instruction ExtInst(Instruction resultType, Instruction set, LiteralInteger instruction, params Operand[] parameters)
|
public Instruction ExtInst(Instruction resultType, Instruction set, LiteralInteger instruction, params Operand[] parameters)
|
||||||
|
@ -275,7 +274,7 @@ namespace Spv.Generator
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.
|
// 3.
|
||||||
foreach (Instruction extInstImport in _extInstImports)
|
foreach (Instruction extInstImport in _extInstImports.Values)
|
||||||
{
|
{
|
||||||
extInstImport.Write(writer);
|
extInstImport.Write(writer);
|
||||||
}
|
}
|
||||||
|
@ -313,8 +312,9 @@ namespace Spv.Generator
|
||||||
|
|
||||||
// Ensure that everything is in the right order in the declarations section
|
// Ensure that everything is in the right order in the declarations section
|
||||||
List<Instruction> declarations = new List<Instruction>();
|
List<Instruction> declarations = new List<Instruction>();
|
||||||
declarations.AddRange(_typeDeclarations);
|
declarations.AddRange(_typeDeclarations.Values);
|
||||||
declarations.AddRange(_globals);
|
declarations.AddRange(_globals);
|
||||||
|
declarations.AddRange(_constants.Values);
|
||||||
declarations.Sort((Instruction x, Instruction y) => x.Id.CompareTo(y.Id));
|
declarations.Sort((Instruction x, Instruction y) => x.Id.CompareTo(y.Id));
|
||||||
|
|
||||||
// 9.
|
// 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