implement optional compilation of inner loop to CIL, unrolling the loop into a number of functions we invoke
This commit is contained in:
parent
f7c3bde22f
commit
6822d8f896
786
Program.cs
786
Program.cs
|
|
@ -1,10 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -62,6 +65,7 @@ void Main()
|
||||||
}
|
}
|
||||||
|
|
||||||
// options??
|
// options??
|
||||||
|
bool shouldUseCompiler = false;
|
||||||
bool shouldUseParallelism = true;
|
bool shouldUseParallelism = true;
|
||||||
bool shouldUseSimd = true;
|
bool shouldUseSimd = true;
|
||||||
|
|
||||||
|
|
@ -88,12 +92,15 @@ void Main()
|
||||||
isEvaluating = true;
|
isEvaluating = true;
|
||||||
shouldUpdateTexture = true;
|
shouldUpdateTexture = true;
|
||||||
InterpreterOptions interpreterOptions = (shouldUseParallelism ? InterpreterOptions.Parallelism : default)
|
InterpreterOptions interpreterOptions = (shouldUseParallelism ? InterpreterOptions.Parallelism : default)
|
||||||
| (shouldUseSimd ? InterpreterOptions.Simd : default);
|
| (shouldUseSimd ? InterpreterOptions.Simd : default)
|
||||||
|
| (shouldUseCompiler
|
||||||
|
? InterpreterOptions.CompileInnerLoop
|
||||||
|
: default);
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
Instruction[] instructions = Parsing.Parse(programsProsperoVm);
|
EvaluationInstructions instructions = Parsing.Parse(programsProsperoVm);
|
||||||
currentOutputImageData.AsSpan()[..currentOutputImageData.Length].Clear();
|
currentOutputImageData.AsSpan()[..currentOutputImageData.Length].Clear();
|
||||||
|
|
||||||
if ((interpreterOptions & InterpreterOptions.Simd) != 0)
|
if ((interpreterOptions & InterpreterOptions.Simd) != 0)
|
||||||
|
|
@ -129,7 +136,7 @@ void Main()
|
||||||
Raylib.EndShaderMode();
|
Raylib.EndShaderMode();
|
||||||
|
|
||||||
Raylib.DrawText("Sharpero (press R to evaluate, O to output to file)", 12, 12, 20, Color.White);
|
Raylib.DrawText("Sharpero (press R to evaluate, O to output to file)", 12, 12, 20, Color.White);
|
||||||
Raylib.DrawText($" - parallelism {(shouldUseParallelism ? "enabled" : "disabled")} (P to toggle), simd {(shouldUseSimd ? "enabled" : "disabled")} (S to toggle)", 12, 32, 20, Color.White);
|
Raylib.DrawText($" - parallelism {(shouldUseParallelism ? "enabled" : "disabled")} (P to toggle), simd {(shouldUseSimd ? "enabled" : "disabled")} (S to toggle), compile {(shouldUseCompiler? "enabled" : "disabled")} (C to toggle)", 12, 32, 20, Color.White);
|
||||||
|
|
||||||
if (lastEvaluationTimeTook != 0.0f)
|
if (lastEvaluationTimeTook != 0.0f)
|
||||||
{
|
{
|
||||||
|
|
@ -160,6 +167,11 @@ void Main()
|
||||||
{
|
{
|
||||||
shouldUseSimd = !shouldUseSimd;
|
shouldUseSimd = !shouldUseSimd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Raylib.IsKeyPressed(KeyboardKey.C))
|
||||||
|
{
|
||||||
|
shouldUseCompiler = !shouldUseCompiler;
|
||||||
|
}
|
||||||
|
|
||||||
Raylib.EndDrawing();
|
Raylib.EndDrawing();
|
||||||
}
|
}
|
||||||
|
|
@ -177,7 +189,7 @@ float[] GenerateOutputImage(int currentImageSize, bool shouldWriteOutputImage =
|
||||||
{
|
{
|
||||||
(float[] result, double timeTakenSecondsEvaluate) = BenchmarkFunction(() =>
|
(float[] result, double timeTakenSecondsEvaluate) = BenchmarkFunction(() =>
|
||||||
{
|
{
|
||||||
Instruction[] instructions = Parsing.Parse(programsProsperoVm);
|
EvaluationInstructions instructions = Parsing.Parse(programsProsperoVm);
|
||||||
InterpreterOptions interpreterOptions = InterpreterOptions.Parallelism | InterpreterOptions.Simd;
|
InterpreterOptions interpreterOptions = InterpreterOptions.Parallelism | InterpreterOptions.Simd;
|
||||||
return Interpreter.Evaluate<Vector<float>>(instructions, imageSize: currentImageSize, interpreterOptions);
|
return Interpreter.Evaluate<Vector<float>>(instructions, imageSize: currentImageSize, interpreterOptions);
|
||||||
});
|
});
|
||||||
|
|
@ -264,6 +276,8 @@ internal readonly record struct Instruction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal record EvaluationInstructions(Instruction[] Instructions);
|
||||||
|
|
||||||
internal static class Parsing
|
internal static class Parsing
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -282,7 +296,18 @@ internal static class Parsing
|
||||||
return identifiers[id] = new Operand(Convert.ToInt32(id[1..], 16), isConstant);
|
return identifiers[id] = new Operand(Convert.ToInt32(id[1..], 16), isConstant);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction[] Parse(string filename)
|
private static float EvaluateExpression(OpCode opCode, float a, float b)
|
||||||
|
{
|
||||||
|
return opCode switch
|
||||||
|
{
|
||||||
|
OpCode.Add => a + b,
|
||||||
|
OpCode.Sub => a - b,
|
||||||
|
OpCode.Mul => a * b,
|
||||||
|
_ => 0.0f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EvaluationInstructions Parse(string filename)
|
||||||
{
|
{
|
||||||
var identifiers = new Dictionary<string, Operand>();
|
var identifiers = new Dictionary<string, Operand>();
|
||||||
foreach (string line in File.ReadAllLines(filename))
|
foreach (string line in File.ReadAllLines(filename))
|
||||||
|
|
@ -332,17 +357,6 @@ internal static class Parsing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float EvaluateExpression(OpCode opCode, float a, float b)
|
|
||||||
{
|
|
||||||
return opCode switch
|
|
||||||
{
|
|
||||||
OpCode.Add => a + b,
|
|
||||||
OpCode.Sub => a - b,
|
|
||||||
OpCode.Mul => a * b,
|
|
||||||
_ => 0.0f
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shouldEliminateConstants = true;
|
bool shouldEliminateConstants = true;
|
||||||
|
|
||||||
if (shouldEliminateConstants)
|
if (shouldEliminateConstants)
|
||||||
|
|
@ -416,15 +430,507 @@ internal static class Parsing
|
||||||
Console.WriteLine($"Sharpero eliminated {numberOfRemovedInstructions} constants from tape, {(float)numberOfRemovedInstructions / totalNumberOfInstructions * 100.0f:0.0} % of total");
|
Console.WriteLine($"Sharpero eliminated {numberOfRemovedInstructions} constants from tape, {(float)numberOfRemovedInstructions / totalNumberOfInstructions * 100.0f:0.0} % of total");
|
||||||
}
|
}
|
||||||
|
|
||||||
return instructions.ToArray();
|
return new EvaluationInstructions(instructions.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
internal enum InterpreterOptions
|
internal enum InterpreterOptions
|
||||||
{
|
{
|
||||||
Parallelism = 0x1,
|
CompileInnerLoop = 0x1,
|
||||||
Simd = 0x2
|
Parallelism = 0x2,
|
||||||
|
Simd = 0x4
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class Evaluation
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void Write<T>(Span<T> variables, T value, int offset)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
Unsafe.Add(ref MemoryMarshal.GetReference(variables), offset) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T Read<T>(Span<T> variables, int offset)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
return Unsafe.Add(ref MemoryMarshal.GetReference(variables), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T Add<T>(T a, T b)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(Unsafe.As<T, float>(ref a) + Unsafe.As<T, float>(ref b));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(Unsafe.As<T, Vector<float>>(ref a) + Unsafe.As<T, Vector<float>>(ref b));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T Sub<T>(T a, T b)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(Unsafe.As<T, float>(ref a) - Unsafe.As<T, float>(ref b));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(Unsafe.As<T, Vector<float>>(ref a) - Unsafe.As<T, Vector<float>>(ref b));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T Mul<T>(T a, T b)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(Unsafe.As<T, float>(ref a) * Unsafe.As<T, float>(ref b));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(Unsafe.As<T, Vector<float>>(ref a) * Unsafe.As<T, Vector<float>>(ref b));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T Neg<T>(T v)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(-Unsafe.As<T, float>(ref v));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(-Unsafe.As<T, Vector<float>>(ref v));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T Max<T>(T a, T b)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(MathF.Max(Unsafe.As<T, float>(ref a), Unsafe.As<T, float>(ref b)));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(Vector.Max(Unsafe.As<T, Vector<float>>(ref a), Unsafe.As<T, Vector<float>>(ref b)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T Min<T>(T a, T b)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(MathF.Min(Unsafe.As<T, float>(ref a), Unsafe.As<T, float>(ref b)));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(Vector.Min(Unsafe.As<T, Vector<float>>(ref a), Unsafe.As<T, Vector<float>>(ref b)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T SquareRoot<T>(T v)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(MathF.Sqrt(Unsafe.As<T, float>(ref v)));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(Vector.SquareRoot(Unsafe.As<T, Vector<float>>(ref v)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T Square<T>(T v)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(Unsafe.As<T, float>(ref v) * Unsafe.As<T, float>(ref v));
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(Vector.Multiply(Unsafe.As<T, Vector<float>>(ref v), Unsafe.As<T, Vector<float>>(ref v)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T EvaluateConstant<T>(float c)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(c);
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(new Vector<float>(c));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static T One<T>()
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(T) == typeof(float))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<float, T>(1.0f);
|
||||||
|
}
|
||||||
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
|
{
|
||||||
|
return Unsafe.BitCast<Vector<float>, T>(Vector<float>.One);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class Delegates<T>
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
public delegate void EvaluateDelegate(Span<T> variables, T xs, T ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class Compiler
|
||||||
|
{
|
||||||
|
internal static class CompilerCache<T>
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
internal static readonly ConcurrentDictionary<EvaluationInstructions, Delegates<T>.EvaluateDelegate> CachedPrograms =
|
||||||
|
new ConcurrentDictionary<EvaluationInstructions, Delegates<T>.EvaluateDelegate>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (AssemblyBuilder, MethodBuilder, TypeBuilder) CreateDynamicAssemblyWithMethodBuilder<T>(string assemblyName, string moduleName, string typeName, string methodName)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run);
|
||||||
|
var moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
|
||||||
|
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
|
||||||
|
var methodBuilder =
|
||||||
|
typeBuilder.DefineMethod(
|
||||||
|
methodName,
|
||||||
|
MethodAttributes.Public | MethodAttributes.Static,
|
||||||
|
typeof(void),
|
||||||
|
[typeof(Span<T>), typeof(T), typeof(T)]);
|
||||||
|
|
||||||
|
return (assemblyBuilder, methodBuilder, typeBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Delegates<T>.EvaluateDelegate Compile<T>(EvaluationInstructions evaluationInstructions)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
if (CompilerCache<T>.CachedPrograms.TryGetValue(evaluationInstructions, out var program))
|
||||||
|
{
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (CompilerCache<T>.CachedPrograms)
|
||||||
|
{
|
||||||
|
int maximumInstructionsPerChunk = 32;
|
||||||
|
|
||||||
|
Stopwatch totalSw = Stopwatch.StartNew();
|
||||||
|
var chunkDelegates = evaluationInstructions.Instructions
|
||||||
|
.Chunk(maximumInstructionsPerChunk)
|
||||||
|
.AsParallel()
|
||||||
|
.Select((instructionsChunk, instructionChunkIndex) =>
|
||||||
|
{
|
||||||
|
Stopwatch subTotalSw = Stopwatch.StartNew();
|
||||||
|
var evaluateDynamicMethod = new DynamicMethod(
|
||||||
|
$"EvaluateChunk_{instructionChunkIndex}",
|
||||||
|
typeof(void),
|
||||||
|
[typeof(Span<T>), typeof(T), typeof(T)],
|
||||||
|
typeof(Compiler).Module,
|
||||||
|
skipVisibility: true
|
||||||
|
);
|
||||||
|
|
||||||
|
var methodGenerator = evaluateDynamicMethod.GetILGenerator();
|
||||||
|
methodGenerator.DeclareLocal(typeof(T));
|
||||||
|
|
||||||
|
foreach (Instruction instruction in instructionsChunk)
|
||||||
|
{
|
||||||
|
CompileInstruction<T>(methodGenerator, in instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalize method, we need a return as our final bit
|
||||||
|
methodGenerator.Emit(OpCodes.Ret);
|
||||||
|
|
||||||
|
// create the delegate, but also force the JIT to compile our function :D
|
||||||
|
var newChunkDelegate = evaluateDynamicMethod.CreateDelegate<Delegates<T>.EvaluateDelegate>();
|
||||||
|
RuntimeHelpers.PrepareDelegate(newChunkDelegate);
|
||||||
|
|
||||||
|
Console.WriteLine($" - took: {subTotalSw.Elapsed.Milliseconds:0.0} ms to compile sub program ({instructionChunkIndex}) ({methodGenerator.ILOffset} bytes of instructions)!");
|
||||||
|
return newChunkDelegate;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
Console.WriteLine($"Sharpero took: {totalSw.Elapsed.Milliseconds:0.0} ms to compile program with {chunkDelegates.Count} parts (max instructions per chunk = {maximumInstructionsPerChunk})!");
|
||||||
|
CompilerCache<T>.CachedPrograms[evaluationInstructions] = (variables, xs, ys) =>
|
||||||
|
{
|
||||||
|
foreach (Delegates<T>.EvaluateDelegate chunkDelegate in chunkDelegates)
|
||||||
|
{
|
||||||
|
chunkDelegate(variables, xs, ys);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// force the JIT to compile our function :D
|
||||||
|
RuntimeHelpers.PrepareDelegate(CompilerCache<T>.CachedPrograms[evaluationInstructions]);
|
||||||
|
return CompilerCache<T>.CachedPrograms[evaluationInstructions];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CompileInstruction<T>(ILGenerator methodGenerator, in Instruction instruction)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
void EmitReadConstant(float constant)
|
||||||
|
{
|
||||||
|
methodGenerator.Emit(OpCodes.Ldc_R4, constant); // constant
|
||||||
|
methodGenerator.Emit(OpCodes.Call, typeof(Evaluation).GetMethod(nameof(Evaluation.EvaluateConstant))!.MakeGenericMethod(typeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitWriteConstant(int offset, float constant)
|
||||||
|
{
|
||||||
|
// write, but with constant value
|
||||||
|
methodGenerator.Emit(OpCodes.Ldarg_0); // Span<T>
|
||||||
|
methodGenerator.Emit(OpCodes.Ldc_R4, constant); // result of call
|
||||||
|
methodGenerator.Emit(OpCodes.Ldc_I4, offset); // offset
|
||||||
|
methodGenerator.Emit(OpCodes.Call, typeof(Evaluation).GetMethod(nameof(Evaluation.Write))!.MakeGenericMethod(typeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitWrite(int offset)
|
||||||
|
{
|
||||||
|
// write, previous result will be at loc 0 if all is well
|
||||||
|
methodGenerator.Emit(OpCodes.Ldarg_0); // Span<T>
|
||||||
|
methodGenerator.Emit(OpCodes.Ldloc_0); // result of call
|
||||||
|
methodGenerator.Emit(OpCodes.Ldc_I4, offset); // offset
|
||||||
|
methodGenerator.Emit(OpCodes.Call, typeof(Evaluation).GetMethod(nameof(Evaluation.Write))!.MakeGenericMethod(typeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitWriteXs(int offset)
|
||||||
|
{
|
||||||
|
// write, but xs specifically
|
||||||
|
methodGenerator.Emit(OpCodes.Ldarg_0); // Span<T>
|
||||||
|
methodGenerator.Emit(OpCodes.Ldarg_1); // xs
|
||||||
|
methodGenerator.Emit(OpCodes.Ldc_I4, offset); // offset
|
||||||
|
methodGenerator.Emit(OpCodes.Call, typeof(Evaluation).GetMethod(nameof(Evaluation.Write))!.MakeGenericMethod(typeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitWriteYs(int offset)
|
||||||
|
{
|
||||||
|
// write, but ys specifically
|
||||||
|
methodGenerator.Emit(OpCodes.Ldarg_0); // Span<T>
|
||||||
|
methodGenerator.Emit(OpCodes.Ldarg_2); // ys
|
||||||
|
methodGenerator.Emit(OpCodes.Ldc_I4, offset); // offset
|
||||||
|
methodGenerator.Emit(OpCodes.Call, typeof(Evaluation).GetMethod(nameof(Evaluation.Write))!.MakeGenericMethod(typeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitInvokeUnaryOperation(string methodName)
|
||||||
|
{
|
||||||
|
// call with Span<T>, pushed results of variables[a] and variables[b] reads
|
||||||
|
methodGenerator.Emit(OpCodes.Call, typeof(Evaluation).GetMethod(methodName)!.MakeGenericMethod(typeof(T)));
|
||||||
|
methodGenerator.Emit(OpCodes.Stloc_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitInvokeBinaryOperation(string methodName)
|
||||||
|
{
|
||||||
|
// call with Span<T>, pushed results of variables[a] and variables[b] reads
|
||||||
|
methodGenerator.Emit(OpCodes.Call, typeof(Evaluation).GetMethod(methodName)!.MakeGenericMethod(typeof(T)));
|
||||||
|
methodGenerator.Emit(OpCodes.Stloc_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitReadVariablesB(Operand b)
|
||||||
|
{
|
||||||
|
methodGenerator.Emit(OpCodes.Ldarg_0); // Span<T>
|
||||||
|
methodGenerator.Emit(OpCodes.Ldc_I4, b); // b
|
||||||
|
methodGenerator.Emit(OpCodes.Call, typeof(Evaluation).GetMethod(nameof(Evaluation.Read))!.MakeGenericMethod(typeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitReadVariablesA(Operand a)
|
||||||
|
{
|
||||||
|
methodGenerator.Emit(OpCodes.Ldarg_0); // Span<T>
|
||||||
|
methodGenerator.Emit(OpCodes.Ldc_I4, a); // a
|
||||||
|
methodGenerator.Emit(OpCodes.Call, typeof(Evaluation).GetMethod(nameof(Evaluation.Read))!.MakeGenericMethod(typeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (instruction)
|
||||||
|
{
|
||||||
|
case { OpCode: OpCode.VarX }: // => xs,
|
||||||
|
|
||||||
|
EmitWriteXs(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.VarY }: // => ys,
|
||||||
|
|
||||||
|
EmitWriteYs(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Add, A: { IsConstant: false } a, B: { IsConstant: false } b }: // => Add(variables[a], variables[b]),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitReadVariablesB(b);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Add));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Add, A.IsConstant: true, B: { IsConstant: false } b }: // => Add(EvaluateConstant<T>(instruction.C), variables[b]),
|
||||||
|
|
||||||
|
EmitReadConstant(instruction.C);
|
||||||
|
EmitReadVariablesB(b);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Add));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Add, A: { IsConstant: false } a, B.IsConstant: true }: // => Add(variables[a], EvaluateConstant<T>(instruction.C)),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitReadConstant(instruction.C);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Add));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Sub, A: { IsConstant: false } a, B: { IsConstant: false } b }: // => Sub(variables[a], variables[b]),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitReadVariablesB(b);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Sub));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Sub, A.IsConstant: true, B: { IsConstant: false } b }: // => Sub(EvaluateConstant<T>(instruction.C), variables[b]),
|
||||||
|
|
||||||
|
EmitReadConstant(instruction.C);
|
||||||
|
EmitReadVariablesB(b);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Sub));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Sub, A: { IsConstant: false } a, B.IsConstant: true }: // => Sub(variables[a], EvaluateConstant<T>(instruction.C)),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitReadConstant(instruction.C);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Sub));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Mul, A: { IsConstant: false } a, B: { IsConstant: false } b }: // => Mul(variables[a], variables[b]),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitReadVariablesB(b);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Mul));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Mul, A.IsConstant: true, B: { IsConstant: false } b }: // => Mul(EvaluateConstant<T>(instruction.C), variables[b]),
|
||||||
|
|
||||||
|
EmitReadConstant(instruction.C);
|
||||||
|
EmitReadVariablesB(b);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Mul));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Mul, A: { IsConstant: false } a, B.IsConstant: true }: // => Mul(variables[a], EvaluateConstant<T>(instruction.C)),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitReadConstant(instruction.C);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Mul));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Max, A: var a, B: var b }: // => Max(variables[a], variables[b]),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitReadVariablesB(b);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Max));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Min, A: var a, B: var b }: // => Min(variables[a], variables[b]),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitReadVariablesB(b);
|
||||||
|
EmitInvokeBinaryOperation(nameof(Evaluation.Min));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Neg, A: var a }: // => Neg(variables[a]),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitInvokeUnaryOperation(nameof(Evaluation.Neg));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Sqrt, A: var a }: // => SquareRoot(variables[a]),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitInvokeUnaryOperation(nameof(Evaluation.SquareRoot));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Square, A: var a }: // => Square(variables[a]),
|
||||||
|
|
||||||
|
EmitReadVariablesA(a);
|
||||||
|
EmitInvokeUnaryOperation(nameof(Evaluation.Square));
|
||||||
|
EmitWrite(instruction.Out);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case { OpCode: OpCode.Const, C: var v }: // => EvaluateConstant<T>(v),
|
||||||
|
|
||||||
|
EmitWriteConstant(instruction.Out, v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class Interpreter
|
internal static class Interpreter
|
||||||
|
|
@ -434,11 +940,11 @@ internal static class Interpreter
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(float))
|
if (typeof(T) == typeof(float))
|
||||||
{
|
{
|
||||||
return (T)(object)values[0];
|
return Unsafe.BitCast<float, T>(values[0]);
|
||||||
}
|
}
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
else if (typeof(T) == typeof(Vector<float>))
|
||||||
{
|
{
|
||||||
return (T)(object)new Vector<float>(values);
|
return Unsafe.BitCast<Vector<float>, T>(new Vector<float>(values));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -447,7 +953,7 @@ internal static class Interpreter
|
||||||
}
|
}
|
||||||
|
|
||||||
[SkipLocalsInit]
|
[SkipLocalsInit]
|
||||||
public static float[] Evaluate<T>(Instruction[] instructions, int imageSize, InterpreterOptions options = default, float[]? result = null)
|
public static float[] Evaluate<T>(EvaluationInstructions evaluationInstructions, int imageSize, InterpreterOptions options = default, float[]? result = null)
|
||||||
where T : unmanaged
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
result ??= new float[imageSize * imageSize];
|
result ??= new float[imageSize * imageSize];
|
||||||
|
|
@ -456,6 +962,13 @@ internal static class Interpreter
|
||||||
{
|
{
|
||||||
MaxDegreeOfParallelism = (options & InterpreterOptions.Parallelism) != 0 ? -1 : 1
|
MaxDegreeOfParallelism = (options & InterpreterOptions.Parallelism) != 0 ? -1 : 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool shouldCompileInnerLoop = (options & InterpreterOptions.CompileInnerLoop) != 0;
|
||||||
|
|
||||||
|
if (shouldCompileInnerLoop)
|
||||||
|
{
|
||||||
|
Compiler.Compile<T>(evaluationInstructions);
|
||||||
|
}
|
||||||
|
|
||||||
int chunkSize = typeof(T) == typeof(Vector<float>) ? Vector<float>.Count : 1;
|
int chunkSize = typeof(T) == typeof(Vector<float>) ? Vector<float>.Count : 1;
|
||||||
Parallel.For(0, (imageSize * imageSize) / chunkSize, parallelOptions, chunkIdx =>
|
Parallel.For(0, (imageSize * imageSize) / chunkSize, parallelOptions, chunkIdx =>
|
||||||
|
|
@ -475,236 +988,71 @@ internal static class Interpreter
|
||||||
(xs[idx], ys[idx]) = (vx, vy);
|
(xs[idx], ys[idx]) = (vx, vy);
|
||||||
}
|
}
|
||||||
|
|
||||||
T results = Evaluate(instructions, GetValues<T>(xs), GetValues<T>(ys));
|
T results = shouldCompileInnerLoop
|
||||||
|
? EvaluateCompiled(evaluationInstructions,GetValues<T>(xs), GetValues<T>(ys))
|
||||||
|
: EvaluateInterpreted(evaluationInstructions, GetValues<T>(xs), GetValues<T>(ys));
|
||||||
|
|
||||||
for (int idx = 0; idx < chunkSize; ++idx)
|
for (int idx = 0; idx < chunkSize; ++idx)
|
||||||
{
|
{
|
||||||
int currentIdx = chunkIdx * chunkSize + idx;
|
int currentIdx = chunkIdx * chunkSize + idx;
|
||||||
(int x, int y) = Parsing.IndexToCoord(currentIdx, width: imageSize);
|
(int x, int y) = Parsing.IndexToCoord(currentIdx, width: imageSize);
|
||||||
|
|
||||||
result[Parsing.CoordToIndex(x, y, width: imageSize)] = Unsafe.Add(ref Unsafe.As<T, float>(ref results), idx);
|
result[Parsing.CoordToIndex(x, y, width: imageSize)] = Unsafe.Add(ref Unsafe.As<T, float>(ref results), idx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T Add<T>(T a, T b)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(float))
|
|
||||||
{
|
|
||||||
return (T)(object)(Unsafe.As<T, float>(ref a) + Unsafe.As<T, float>(ref b));
|
|
||||||
}
|
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
|
||||||
{
|
|
||||||
return (T)(object)(Unsafe.As<T, Vector<float>>(ref a) + Unsafe.As<T, Vector<float>>(ref b));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T Sub<T>(T a, T b)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(float))
|
|
||||||
{
|
|
||||||
return (T)(object)(Unsafe.As<T, float>(ref a) - Unsafe.As<T, float>(ref b));
|
|
||||||
}
|
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
|
||||||
{
|
|
||||||
return (T)(object)(Unsafe.As<T, Vector<float>>(ref a) - Unsafe.As<T, Vector<float>>(ref b));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T Mul<T>(T a, T b)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(float))
|
|
||||||
{
|
|
||||||
return (T)(object)(Unsafe.As<T, float>(ref a) * Unsafe.As<T, float>(ref b));
|
|
||||||
}
|
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
|
||||||
{
|
|
||||||
return (T)(object)(Unsafe.As<T, Vector<float>>(ref a) * Unsafe.As<T, Vector<float>>(ref b));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T Mul<T>(T a, float b)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(float))
|
|
||||||
{
|
|
||||||
return (T)(object)(Unsafe.As<T, float>(ref a) * b);
|
|
||||||
}
|
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
|
||||||
{
|
|
||||||
return (T)(object)(Unsafe.As<T, Vector<float>>(ref a) * b);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T Neg<T>(T v)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(float))
|
|
||||||
{
|
|
||||||
return (T)(object)(-Unsafe.As<T, float>(ref v));
|
|
||||||
}
|
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
|
||||||
{
|
|
||||||
return (T)(object)(-Unsafe.As<T, Vector<float>>(ref v));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[SkipLocalsInit]
|
||||||
public static T Max<T>(T a, T b)
|
public static T EvaluateCompiled<T>(EvaluationInstructions evaluationInstructions, T xs, T ys)
|
||||||
where T : unmanaged
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(float))
|
// #TODO: this construction is just a little bit unhinged lol
|
||||||
{
|
Span<T> variables = stackalloc T[evaluationInstructions.Instructions.Length];
|
||||||
return (T)(object)MathF.Max(Unsafe.As<T, float>(ref a), Unsafe.As<T, float>(ref b));
|
|
||||||
}
|
// compile if we've not already cached this instruction set, and evaluate!
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
Compiler.Compile<T>(evaluationInstructions)?.Invoke(variables, xs, ys);
|
||||||
{
|
|
||||||
return (T)(object)Vector.Max(Unsafe.As<T, Vector<float>>(ref a), Unsafe.As<T, Vector<float>>(ref b));
|
return variables[evaluationInstructions.Instructions.Length - 1];
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T Min<T>(T a, T b)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(float))
|
|
||||||
{
|
|
||||||
return (T)(object)MathF.Min(Unsafe.As<T, float>(ref a), Unsafe.As<T, float>(ref b));
|
|
||||||
}
|
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
|
||||||
{
|
|
||||||
return (T)(object)Vector.Min(Unsafe.As<T, Vector<float>>(ref a), Unsafe.As<T, Vector<float>>(ref b));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T SquareRoot<T>(T v)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(float))
|
|
||||||
{
|
|
||||||
return (T)(object)MathF.Sqrt(Unsafe.As<T, float>(ref v));
|
|
||||||
}
|
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
|
||||||
{
|
|
||||||
return (T)(object)Vector.SquareRoot(Unsafe.As<T, Vector<float>>(ref v));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T EvaluateConstant<T>(float c)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(float))
|
|
||||||
{
|
|
||||||
return (T)(object)c;
|
|
||||||
}
|
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
|
||||||
{
|
|
||||||
return (T)(object)new Vector<float>(c);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T One<T>()
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(float))
|
|
||||||
{
|
|
||||||
return (T)(object)1.0f;
|
|
||||||
}
|
|
||||||
else if (typeof(T) == typeof(Vector<float>))
|
|
||||||
{
|
|
||||||
return (T)(object)Vector<float>.One;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[SkipLocalsInit]
|
[SkipLocalsInit]
|
||||||
public static T Evaluate<T>(Instruction[] instructions, T xs, T ys)
|
public static T EvaluateInterpreted<T>(EvaluationInstructions evaluationInstructions, T xs, T ys)
|
||||||
where T : unmanaged
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
// #TODO: this construction is just a little bit unhinged lol
|
// #TODO: this construction is just a little bit unhinged lol
|
||||||
Span<T> variables = stackalloc T[instructions.Length];
|
Span<T> variables = stackalloc T[evaluationInstructions.Instructions.Length];
|
||||||
|
|
||||||
foreach (ref Instruction instruction in instructions.AsSpan())
|
foreach (ref Instruction instruction in evaluationInstructions.Instructions.AsSpan())
|
||||||
{
|
{
|
||||||
variables[instruction.Out] = instruction switch
|
variables[instruction.Out] = instruction switch
|
||||||
{
|
{
|
||||||
{ OpCode: OpCode.VarX } => xs,
|
{ OpCode: OpCode.VarX } => xs,
|
||||||
{ OpCode: OpCode.VarY } => ys,
|
{ OpCode: OpCode.VarY } => ys,
|
||||||
|
|
||||||
{ OpCode: OpCode.Add, A: { IsConstant: false } a, B: { IsConstant: false } b } => Add(variables[a], variables[b]),
|
{ OpCode: OpCode.Add, A: { IsConstant: false } a, B: { IsConstant: false } b } => Evaluation.Add(variables[a], variables[b]),
|
||||||
{ OpCode: OpCode.Add, A.IsConstant: true, B: { IsConstant: false } b } => Add(EvaluateConstant<T>(instruction.C), variables[b]),
|
{ OpCode: OpCode.Add, A.IsConstant: true, B: { IsConstant: false } b } => Evaluation.Add(Evaluation.EvaluateConstant<T>(instruction.C), variables[b]),
|
||||||
{ OpCode: OpCode.Add, A: { IsConstant: false } a, B.IsConstant: true } => Add(variables[a], EvaluateConstant<T>(instruction.C)),
|
{ OpCode: OpCode.Add, A: { IsConstant: false } a, B.IsConstant: true } => Evaluation.Add(variables[a], Evaluation.EvaluateConstant<T>(instruction.C)),
|
||||||
|
|
||||||
{ OpCode: OpCode.Sub, A: { IsConstant: false } a, B: { IsConstant: false } b } => Sub(variables[a], variables[b]),
|
{ OpCode: OpCode.Sub, A: { IsConstant: false } a, B: { IsConstant: false } b } => Evaluation.Sub(variables[a], variables[b]),
|
||||||
{ OpCode: OpCode.Sub, A.IsConstant: true, B: { IsConstant: false } b } => Sub(EvaluateConstant<T>(instruction.C), variables[b]),
|
{ OpCode: OpCode.Sub, A.IsConstant: true, B: { IsConstant: false } b } => Evaluation.Sub(Evaluation.EvaluateConstant<T>(instruction.C), variables[b]),
|
||||||
{ OpCode: OpCode.Sub, A: { IsConstant: false } a, B.IsConstant: true } => Sub(variables[a], EvaluateConstant<T>(instruction.C)),
|
{ OpCode: OpCode.Sub, A: { IsConstant: false } a, B.IsConstant: true } => Evaluation.Sub(variables[a], Evaluation.EvaluateConstant<T>(instruction.C)),
|
||||||
|
|
||||||
{ OpCode: OpCode.Mul, A: { IsConstant: false } a, B: { IsConstant: false } b } => Mul(variables[a], variables[b]),
|
{ OpCode: OpCode.Mul, A: { IsConstant: false } a, B: { IsConstant: false } b } => Evaluation.Mul(variables[a], variables[b]),
|
||||||
{ OpCode: OpCode.Mul, A.IsConstant: true, B: { IsConstant: false } b } => Mul(EvaluateConstant<T>(instruction.C), variables[b]),
|
{ OpCode: OpCode.Mul, A.IsConstant: true, B: { IsConstant: false } b } => Evaluation.Mul(Evaluation.EvaluateConstant<T>(instruction.C), variables[b]),
|
||||||
{ OpCode: OpCode.Mul, A: { IsConstant: false } a, B.IsConstant: true } => Mul(variables[a], EvaluateConstant<T>(instruction.C)),
|
{ OpCode: OpCode.Mul, A: { IsConstant: false } a, B.IsConstant: true } => Evaluation.Mul(variables[a], Evaluation.EvaluateConstant<T>(instruction.C)),
|
||||||
|
|
||||||
{ OpCode: OpCode.Max, A: var a, B: var b } => Max(variables[a], variables[b]),
|
{ OpCode: OpCode.Max, A: var a, B: var b } => Evaluation.Max(variables[a], variables[b]),
|
||||||
{ OpCode: OpCode.Min, A: var a, B: var b } => Min(variables[a], variables[b]),
|
{ OpCode: OpCode.Min, A: var a, B: var b } => Evaluation.Min(variables[a], variables[b]),
|
||||||
{ OpCode: OpCode.Neg, A: var a } => Neg(variables[a]),
|
{ OpCode: OpCode.Neg, A: var a } => Evaluation.Neg(variables[a]),
|
||||||
{ OpCode: OpCode.Sqrt, A: var a } => SquareRoot(variables[a]),
|
{ OpCode: OpCode.Sqrt, A: var a } => Evaluation.SquareRoot(variables[a]),
|
||||||
{ OpCode: OpCode.Square, A: var a } => Mul(variables[a], variables[a]),
|
{ OpCode: OpCode.Square, A: var a } => Evaluation.Square(variables[a]),
|
||||||
|
|
||||||
{ OpCode: OpCode.Const, C: var v } => Mul(One<T>(), v),
|
{ OpCode: OpCode.Const, C: var v } => Evaluation.EvaluateConstant<T>(v),
|
||||||
_ => variables[instruction.Out]
|
_ => variables[instruction.Out]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return variables[instructions.Length - 1];
|
return variables[evaluationInstructions.Instructions.Length - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,18 +1,24 @@
|
||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArrayPool_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc8beccd33913594a240a8bed807798632452943eba5864a26bfec56b2e617_003FArrayPool_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArrayPool_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc8beccd33913594a240a8bed807798632452943eba5864a26bfec56b2e617_003FArrayPool_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AColor_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6a9fabe8949749a5835bf490db09a4cc272a00_003F66_003F1010c222_003FColor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AColor_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6a9fabe8949749a5835bf490db09a4cc272a00_003F66_003F1010c222_003FColor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConcurrentDictionary_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Faeddec7ccf3f1571cfd14ca2540abbf19f9846eb91bc3767e402298d4cbaa_003FConcurrentDictionary_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAdditionOperators_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F8c272ae9f746341d3142ee8e616ec652d57d3bf8faa768267b7c048c9a9e7d8_003FIAdditionOperators_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAdditionOperators_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F8c272ae9f746341d3142ee8e616ec652d57d3bf8faa768267b7c048c9a9e7d8_003FIAdditionOperators_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AImage_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1a3645a8e3569c886d75574e693cca03e1a49e8263ee2a1616650131c3b9e17_003FImage_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AISimdVector_005F2_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a7e82cbc39f2ce5352b03d8a8890a0dad9611ce64fbbba26f3e898898a6_003FISimdVector_005F2_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AISimdVector_005F2_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a7e82cbc39f2ce5352b03d8a8890a0dad9611ce64fbbba26f3e898898a6_003FISimdVector_005F2_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALocalBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F63ecc2c73bb9ce42eaf6244d09828fea6cdad70a4d4a5893180ef472d731b_003FLocalBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMethodInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F8f9a129b62e3d34c7deabb6d2a80a34a1ec0d4a1f6cccf89a3bd646658a8b43_003FMethodInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANumber_002EParsing_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fde8a243f75215d958afa80cf80a41b4981a44efea7db93bffcdaf7bd6ba378c0_003FNumber_002EParsing_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANumber_002EParsing_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fde8a243f75215d958afa80cf80a41b4981a44efea7db93bffcdaf7bd6ba378c0_003FNumber_002EParsing_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AParallel_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1e8c32362ff486e4e342292ccce957781165d4f6305c795c1d6b1b473e4c0a3_003FParallel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AParallel_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1e8c32362ff486e4e342292ccce957781165d4f6305c795c1d6b1b473e4c0a3_003FParallel_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APersistedAssemblyBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F479daf628d169411ac8526d497865727d69e6e7f4832a1ba6e65e18abf4f90_003FPersistedAssemblyBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APersistedAssemblyBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F479daf628d169411ac8526d497865727d69e6e7f4832a1ba6e65e18abf4f90_003FPersistedAssemblyBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARuntimeAssemblyBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F89f78f55baa267d67190b18dceab4e687d397e0573226d8f9d32ecc2271863e_003FRuntimeAssemblyBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARuntimeAssemblyBuilder_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F89f78f55baa267d67190b18dceab4e687d397e0573226d8f9d32ecc2271863e_003FRuntimeAssemblyBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARuntimeType_002ECoreCLR_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fa3874ce7f1ea55e4427b4e6216fee3d5a82d337cb291e07f7a9d047e9661384_003FRuntimeType_002ECoreCLR_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AScalar_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1a6f5a6d45979b9db8954e4e92811b8d7b853439d7953d79151da4586e57b3_003FScalar_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AScalar_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F1a6f5a6d45979b9db8954e4e92811b8d7b853439d7953d79151da4586e57b3_003FScalar_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASKBitmap_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F86313366d68bcb85ab793bdfd1df8614c6d587059aa7347837ac54be1c28_003FSKBitmap_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASKBitmap_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F86313366d68bcb85ab793bdfd1df8614c6d587059aa7347837ac54be1c28_003FSKBitmap_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASKCanvas_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F40c61ebf4349bc39224b986147986231e7ffd8b3b69ac47f370bd607e7a2_003FSKCanvas_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASKCanvas_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F40c61ebf4349bc39224b986147986231e7ffd8b3b69ac47f370bd607e7a2_003FSKCanvas_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASKPaint_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fb4d6b3f01617f13d8e24965ceb91a3b4bff76e3d61e49788aa782f68597af6a_003FSKPaint_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASKPaint_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fb4d6b3f01617f13d8e24965ceb91a3b4bff76e3d61e49788aa782f68597af6a_003FSKPaint_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASpan_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc24d475d83442e0a97ce97fdd4d43d6b5a3977b2a474c7996b895330c6b0_003FSpan_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASpan_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc24d475d83442e0a97ce97fdd4d43d6b5a3977b2a474c7996b895330c6b0_003FSpan_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F228bff957d5b9f32cfbedd8bf82a5d3e862ec3e051593a67913b95ae288287_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F228bff957d5b9f32cfbedd8bf82a5d3e862ec3e051593a67913b95ae288287_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUnsafe_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fdfaa73772eeb6b54d8782c43cbf9528a2397eadc311f5a327db22d7c9dd426_003FUnsafe_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AValueType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F3bf9ddbdca76837aff182b568ea612623121c0901c6c73de35e5c9b14a81a_003FValueType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AValueType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F3bf9ddbdca76837aff182b568ea612623121c0901c6c73de35e5c9b14a81a_003FValueType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVector_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F7b94acd1ee29851cd828afb28baacf1440add77e5870783fa3e5965f7536aaf_003FVector_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVector_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F7b94acd1ee29851cd828afb28baacf1440add77e5870783fa3e5965f7536aaf_003FVector_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVector_005F1_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F512c07e71314e149661d5edf86fb11fa7d0737832496882904c3c464b6ead_003FVector_005F1_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVector_005F1_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F512c07e71314e149661d5edf86fb11fa7d0737832496882904c3c464b6ead_003FVector_005F1_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||||
Loading…
Reference in New Issue