Browse Source

Merge pull request #48 from Akeit0/reduce-move

Optimize: Reduce and merge unnecessary move instructions
Annulus Games 11 months ago
parent
commit
55d8f1a45a

+ 144 - 0
src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs

@@ -110,6 +110,150 @@ public class FunctionCompilationContext : IDisposable
         instructionPositions.Add(position);
     }
 
+    /// <summary>
+    /// Push or merge the new instruction.
+    /// </summary>
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in SourcePosition position, ref bool incrementStackPosition)
+    {
+        if (instructions.Length == 0)
+        {
+            instructions.Add(instruction);
+            instructionPositions.Add(position);
+            return;
+        }
+        ref var lastInstruction = ref instructions.AsSpan()[^1];
+        var opcode = instruction.OpCode;
+        switch (opcode)
+        {
+            case OpCode.Move:
+                // last A is not local variable
+                if (lastInstruction.A != lastLocal &&
+                    // available to merge
+                    lastInstruction.A == instruction.B &&
+                    // not already merged
+                    lastInstruction.A != lastInstruction.B)
+                {
+                    switch (lastInstruction.OpCode)
+                    {
+                        case OpCode.GetTable:
+                        case OpCode.Add:
+                        case OpCode.Sub:
+                        case OpCode.Mul:
+                        case OpCode.Div:
+                        case OpCode.Mod:
+                        case OpCode.Pow:
+                        case OpCode.Concat:
+                            {
+                                lastInstruction.A = instruction.A;
+                                incrementStackPosition = false;
+                                return;
+                            }
+                    }
+                }
+                break;
+            case OpCode.GetTable:
+                {
+                    // Merge MOVE GetTable
+                    if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A)
+                    {
+                        if (lastInstruction.A == instruction.B)
+                        {
+                            lastInstruction = Instruction.GetTable(instruction.A, lastInstruction.B, instruction.C);
+                            instructionPositions[^1] = position;
+                            incrementStackPosition = false;
+                            return;
+                        }
+
+                    }
+                    break;
+                }
+            case OpCode.SetTable:
+                {
+                    // Merge MOVE SETTABLE
+                    if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A)
+                    {
+                        var lastB = lastInstruction.B;
+                        var lastA = lastInstruction.A;
+                        if (lastB < 255 && lastA == instruction.A)
+                        {
+                            // Merge MOVE MOVE SETTABLE
+                            if (instructions.Length > 2)
+                            {
+                                ref var last2Instruction = ref instructions.AsSpan()[^2];
+                                var last2A = last2Instruction.A;
+                                if (last2Instruction.OpCode == OpCode.Move && lastLocal != last2A && instruction.C == last2A)
+                                {
+                                    last2Instruction = Instruction.SetTable((byte)(lastB), instruction.B, last2Instruction.B);
+                                    instructions.RemoveAtSwapback(instructions.Length - 1);
+                                    instructionPositions.RemoveAtSwapback(instructionPositions.Length - 1);
+                                    instructionPositions[^1] = position;
+                                    incrementStackPosition = false;
+                                    return;
+                                }
+                            }
+                            lastInstruction = Instruction.SetTable((byte)(lastB), instruction.B, instruction.C);
+                            instructionPositions[^1] = position;
+                            incrementStackPosition = false;
+                            return;
+                        }
+
+                        if (lastA == instruction.C)
+                        {
+                            lastInstruction = Instruction.SetTable(instruction.A, instruction.B, lastB);
+                            instructionPositions[^1] = position;
+                            incrementStackPosition = false;
+                            return;
+                        }
+                    }
+                    else if (lastInstruction.OpCode == OpCode.GetTabUp && instructions.Length >= 2)
+                    {
+                        ref var last2Instruction = ref instructions[^2];
+                        var last2OpCode = last2Instruction.OpCode;
+                        if (last2OpCode is OpCode.LoadK or OpCode.Move)
+                        {
+
+                            var last2A = last2Instruction.A;
+                            if (last2A != lastLocal && instruction.C == last2A)
+                            {
+                                var c = last2OpCode == OpCode.LoadK ? last2Instruction.Bx + 256 : last2Instruction.B;
+                                last2Instruction = lastInstruction;
+                                lastInstruction = instruction with { C = (ushort)c };
+                                instructionPositions[^2] = instructionPositions[^1];
+                                instructionPositions[^1] = position;
+                                incrementStackPosition = false;
+                                return;
+                            }
+                        }
+                    }
+                    break;
+                }
+            case OpCode.Unm:
+            case OpCode.Not:
+            case OpCode.Len:
+                if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A && lastInstruction.A == instruction.B)
+                {
+                    lastInstruction = instruction with { B = lastInstruction.B }; ;
+                    instructionPositions[^1] = position;
+                    incrementStackPosition = false;
+                    return;
+                }
+                break;
+            case OpCode.Return:
+                if (lastInstruction.OpCode == OpCode.Move && instruction.B == 2 && lastInstruction.B < 256)
+                {
+                    lastInstruction = instruction with { A = (byte)lastInstruction.B };
+                    instructionPositions[^1] = position;
+                    incrementStackPosition = false;
+                    return;
+                }
+                break;
+        }
+
+        instructions.Add(instruction);
+        instructionPositions.Add(position);
+    }
+
     /// <summary>
     /// Gets the index of the constant from the value, or if the constant is not registered it is added and its index is returned.
     /// </summary>

+ 11 - 3
src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs

@@ -33,6 +33,8 @@ public class ScopeCompilationContext : IDisposable
     readonly Dictionary<ReadOnlyMemory<char>, LocalVariableDescription> localVariables = new(256, Utf16StringMemoryComparer.Default);
     readonly Dictionary<ReadOnlyMemory<char>, LabelDescription> labels = new(32, Utf16StringMemoryComparer.Default);
 
+    byte lastLocalVariableIndex;
+
     public byte StackStartPosition { get; private set; }
     public byte StackPosition { get; set; }
 
@@ -72,8 +74,11 @@ public class ScopeCompilationContext : IDisposable
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void PushInstruction(in Instruction instruction, SourcePosition position, bool incrementStackPosition = false)
     {
-        Function.PushInstruction(instruction, position);
-        if (incrementStackPosition) StackPosition++;
+        Function.PushOrMergeInstruction(lastLocalVariableIndex, instruction, position, ref incrementStackPosition);
+        if (incrementStackPosition)
+        {
+            StackPosition++;
+        }
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -89,11 +94,13 @@ public class ScopeCompilationContext : IDisposable
     /// Add new local variable.
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void AddLocalVariable(ReadOnlyMemory<char> name, LocalVariableDescription description)
+    public void AddLocalVariable(ReadOnlyMemory<char> name, LocalVariableDescription description, bool markAsLastLocalVariable = true)
     {
         localVariables[name] = description;
+        lastLocalVariableIndex = description.RegisterIndex;
     }
 
+
     /// <summary>
     /// Gets the local variable in scope.
     /// </summary>
@@ -157,6 +164,7 @@ public class ScopeCompilationContext : IDisposable
         HasCapturedLocalVariables = false;
         localVariables.Clear();
         labels.Clear();
+        lastLocalVariableIndex = 0;
     }
 
     /// <summary>