diff --git a/CHANGELOG.md b/CHANGELOG.md index 0281daf..91a5e2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## v3.3.X - 2020.05.23 +- Support `Localloc` operation + ## v3.3.1 - 2020.04.01 - Sign GrEmit assembly with strong name diff --git a/GrEmit.Tests/Test.cs b/GrEmit.Tests/Test.cs index ad0d4bd..5d47de4 100644 --- a/GrEmit.Tests/Test.cs +++ b/GrEmit.Tests/Test.cs @@ -18,6 +18,91 @@ namespace GrEmit.Tests [TestFixture] public class Test { + [Test] + public void TestLocalloc() + { + // private static unsafe int GetSum(byte length) + // { + // byte* bytes = stackalloc byte[length]; + // for (byte i = 0; i < length; ++i) + // { + // bytes[i] = i; + // } + // int sum = 0; + // for (byte i = 0; i < length; ++i) + // { + // sum += bytes[i]; + // } + // return sum; + // } + var method = new DynamicMethod(Guid.NewGuid().ToString(), typeof(int), new[] {typeof(byte)}, typeof(Test)); + using (var il = new GroboIL(method)) + { + il.Ldarg(0); // stack: [length] + il.Conv(); + il.Localloc(); // stack: [*pointer] + var pointer = il.DeclareLocal(typeof(UIntPtr)); + il.Stloc(pointer); // pointer = value; stack: [] + + il.Ldc_I4(0); // stack: [0] + var i = il.DeclareLocal(typeof(byte)); + il.Stloc(i); // i = 0; stack: [] + var loop1Start = il.DefineLabel("loop1_start"); + var loop1End = il.DefineLabel("loop1_end"); + il.MarkLabel(loop1Start); + { + il.Ldloc(i); // stack: [i] + il.Ldarg(0); // stack: [i, length] + il.Bge(loop1End, unsigned : true); // if (i >= length) goto end; stack: [] + il.Ldloc(pointer); //stack: [pointer] + il.Ldloc(i); // stack: [pointer, i] + il.Add(); // stack: [pointer + i] + il.Ldloc(i); // stack: [pointer + i, i] + il.Stind(typeof(byte)); // *(pointer + i) = i; stack: [] + il.Ldloc(i); // stack: [i] + il.Ldc_I4(1); // stack: [i, 1] + il.Add(); // stack: [i + 1] + il.Conv(); + il.Stloc(i); // i = i + 1; stack: [] + il.Br(loop1Start); + } + il.MarkLabel(loop1End); + + il.Ldc_I4(0); // stack: [0] + il.Dup(); // stack: [0, 0] + var sum = il.DeclareLocal(typeof(int)); + il.Stloc(sum); // sum = 0; stack: [0] + il.Stloc(i); // i = 0; stack: [] + var loop2Start = il.DefineLabel("loop2_start"); + var loop2End = il.DefineLabel("loop2_end"); + il.MarkLabel(loop2Start); + { + il.Ldloc(i); // stack: [i] + il.Ldarg(0); // stack: [i, length] + il.Bge(loop2End, unsigned : true); // if i >= length goto end; stack:[] + il.Ldloc(pointer); // stack: [pointer] + il.Ldloc(i); // stack: [pointer, i] + il.Add(); // stack: [pointer + i] + il.Ldind(typeof(byte)); // stack: [*(pointer + i)] + il.Ldloc(sum); // stack: [*(pointer + i), sum] + il.Add(); // stack: [*(pointer + i) + sum] + il.Stloc(sum); // sum = *(pointer + i) + sum; stack: [] + il.Ldloc(i); // stack: [i] + il.Ldc_I4(1); // stack: [i, 1] + il.Add(); // stack: [i + 1] + il.Conv(); + il.Stloc(i); // i = (i + 1); // stack: [] + il.Br(loop2Start); + } + il.MarkLabel(loop2End); + + il.Ldloc(sum); // stack: [sum] + il.Ret(); + } + var func = (Func)method.CreateDelegate(typeof(Func)); + Assert.That(func(6), Is.EqualTo(15)); + } + [Test] public void TestDifferentStructs() { diff --git a/GrEmit/GroboIL.cs b/GrEmit/GroboIL.cs index 6200214..e25f5e1 100644 --- a/GrEmit/GroboIL.cs +++ b/GrEmit/GroboIL.cs @@ -925,6 +925,11 @@ public void Ldc_IntPtr(IntPtr value) Conv(); } + public void Localloc() + { + Emit(OpCodes.Localloc); + } + /// /// Clears the specified pinned local by setting it to null /// diff --git a/GrEmit/StackMutatorCollection.cs b/GrEmit/StackMutatorCollection.cs index 49b934a..a1f8074 100644 --- a/GrEmit/StackMutatorCollection.cs +++ b/GrEmit/StackMutatorCollection.cs @@ -210,6 +210,7 @@ public static void Mutate(OpCode opCode, GroboIL il, ILInstructionParameter para {OpCodes.Stelem_R8, new StelemStackMutator()}, {OpCodes.Stelem_Ref, new StelemStackMutator()}, {OpCodes.Ckfinite, new CkfiniteStackMutator()}, + {OpCodes.Localloc, new LocallocStackMutator()}, }; private static readonly MarkLabelStackMutator markLabelStackMutator = new MarkLabelStackMutator(); diff --git a/GrEmit/StackMutators/LocallocStackMutator.cs b/GrEmit/StackMutators/LocallocStackMutator.cs new file mode 100644 index 0000000..1aff620 --- /dev/null +++ b/GrEmit/StackMutators/LocallocStackMutator.cs @@ -0,0 +1,16 @@ +using System; + +namespace GrEmit.StackMutators +{ + internal class LocallocStackMutator : StackMutator + { + public override void Mutate(GroboIL il, ILInstructionParameter parameter, ref EvaluationStack stack) + { + CheckNotEmpty(il, stack, () => $"In order to perform the instruction 'localloc' size must be loaded onto the evaluation stack"); + var type = stack.Pop(); + if (ToCLIType(type) != CLIType.NativeInt) + ThrowError(il, $"Unable to perform the instruction 'localloc' on type '{type}'"); + stack.Push(typeof(UIntPtr)); + } + } +} \ No newline at end of file