Browse Source

Merge pull request #107 from Akeit0/fix-conditionals

Fix: `or` doesn't work well
Akeit0 8 months ago
parent
commit
7f868dc47d

+ 27 - 14
src/Lua/CodeAnalysis/Compilation/FunctionCompilationContext.cs

@@ -130,7 +130,7 @@ public class FunctionCompilationContext : IDisposable
     /// Push or merge the new instruction.
     /// Push or merge the new instruction.
     /// </summary>
     /// </summary>
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public void PushOrMergeInstruction(int lastLocal, in Instruction instruction, in SourcePosition position, ref bool incrementStackPosition)
+    public void PushOrMergeInstruction(in Instruction instruction, in SourcePosition position, ref bool incrementStackPosition)
     {
     {
         if (instructions.Length == 0)
         if (instructions.Length == 0)
         {
         {
@@ -139,27 +139,40 @@ public class FunctionCompilationContext : IDisposable
             return;
             return;
         }
         }
 
 
+        var activeLocals = Scope.ActiveLocalVariables;
+
         ref var lastInstruction = ref instructions.AsSpan()[^1];
         ref var lastInstruction = ref instructions.AsSpan()[^1];
         var opcode = instruction.OpCode;
         var opcode = instruction.OpCode;
         switch (opcode)
         switch (opcode)
         {
         {
             case OpCode.Move:
             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)
+
+                if (
+                    // available to merge and  last A is not local variable
+                    lastInstruction.A == instruction.B && !activeLocals[lastInstruction.A])
                 {
                 {
                     switch (lastInstruction.OpCode)
                     switch (lastInstruction.OpCode)
                     {
                     {
-                        case OpCode.GetTable:
+                        case OpCode.LoadK:
+                        case OpCode.LoadBool when lastInstruction.C == 0:
+                        case OpCode.LoadNil when lastInstruction.B == 0:
+                        case OpCode.GetUpVal:
+                        case OpCode.GetTabUp:
+                        case OpCode.GetTable  when !activeLocals[lastInstruction.B]:
+                        case OpCode.SetTabUp:
+                        case OpCode.SetUpVal:
+                        case OpCode.SetTable:
+                        case OpCode.NewTable:
+                        case OpCode.Self:
                         case OpCode.Add:
                         case OpCode.Add:
                         case OpCode.Sub:
                         case OpCode.Sub:
                         case OpCode.Mul:
                         case OpCode.Mul:
                         case OpCode.Div:
                         case OpCode.Div:
                         case OpCode.Mod:
                         case OpCode.Mod:
                         case OpCode.Pow:
                         case OpCode.Pow:
+                        case OpCode.Unm:
+                        case OpCode.Not:
+                        case OpCode.Len:
                         case OpCode.Concat:
                         case OpCode.Concat:
                             {
                             {
                                 lastInstruction.A = instruction.A;
                                 lastInstruction.A = instruction.A;
@@ -173,7 +186,7 @@ public class FunctionCompilationContext : IDisposable
             case OpCode.GetTable:
             case OpCode.GetTable:
                 {
                 {
                     // Merge MOVE GetTable
                     // Merge MOVE GetTable
-                    if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A)
+                    if (lastInstruction.OpCode == OpCode.Move && !activeLocals[lastInstruction.A])
                     {
                     {
                         if (lastInstruction.A == instruction.B)
                         if (lastInstruction.A == instruction.B)
                         {
                         {
@@ -189,7 +202,7 @@ public class FunctionCompilationContext : IDisposable
             case OpCode.SetTable:
             case OpCode.SetTable:
                 {
                 {
                     // Merge MOVE SETTABLE
                     // Merge MOVE SETTABLE
-                    if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A)
+                    if (lastInstruction.OpCode == OpCode.Move && !activeLocals[lastInstruction.A])
                     {
                     {
                         var lastB = lastInstruction.B;
                         var lastB = lastInstruction.B;
                         var lastA = lastInstruction.A;
                         var lastA = lastInstruction.A;
@@ -200,7 +213,7 @@ public class FunctionCompilationContext : IDisposable
                             {
                             {
                                 ref var last2Instruction = ref instructions.AsSpan()[^2];
                                 ref var last2Instruction = ref instructions.AsSpan()[^2];
                                 var last2A = last2Instruction.A;
                                 var last2A = last2Instruction.A;
-                                if (last2Instruction.OpCode == OpCode.Move && lastLocal != last2A && instruction.C == last2A)
+                                if (last2Instruction.OpCode == OpCode.Move && !activeLocals[last2A] && instruction.C == last2A)
                                 {
                                 {
                                     last2Instruction = Instruction.SetTable((byte)(lastB), instruction.B, last2Instruction.B);
                                     last2Instruction = Instruction.SetTable((byte)(lastB), instruction.B, last2Instruction.B);
                                     instructions.RemoveAtSwapback(instructions.Length - 1);
                                     instructions.RemoveAtSwapback(instructions.Length - 1);
@@ -232,7 +245,7 @@ public class FunctionCompilationContext : IDisposable
                         if (last2OpCode is OpCode.LoadK or OpCode.Move)
                         if (last2OpCode is OpCode.LoadK or OpCode.Move)
                         {
                         {
                             var last2A = last2Instruction.A;
                             var last2A = last2Instruction.A;
-                            if (last2A != lastLocal && instruction.C == last2A)
+                            if (!activeLocals[last2A] && instruction.C == last2A)
                             {
                             {
                                 var c = last2OpCode == OpCode.LoadK ? last2Instruction.Bx + 256 : last2Instruction.B;
                                 var c = last2OpCode == OpCode.LoadK ? last2Instruction.Bx + 256 : last2Instruction.B;
                                 last2Instruction = lastInstruction;
                                 last2Instruction = lastInstruction;
@@ -250,10 +263,9 @@ public class FunctionCompilationContext : IDisposable
             case OpCode.Unm:
             case OpCode.Unm:
             case OpCode.Not:
             case OpCode.Not:
             case OpCode.Len:
             case OpCode.Len:
-                if (lastInstruction.OpCode == OpCode.Move && lastLocal != lastInstruction.A && lastInstruction.A == instruction.B)
+                if (lastInstruction.OpCode == OpCode.Move && !activeLocals[lastInstruction.A] && lastInstruction.A == instruction.B)
                 {
                 {
                     lastInstruction = instruction with { B = lastInstruction.B };
                     lastInstruction = instruction with { B = lastInstruction.B };
-                    ;
                     instructionPositions[^1] = position;
                     instructionPositions[^1] = position;
                     incrementStackPosition = false;
                     incrementStackPosition = false;
                     return;
                     return;
@@ -264,6 +276,7 @@ public class FunctionCompilationContext : IDisposable
                 if (lastInstruction.OpCode == OpCode.Move && instruction.B == 2 && lastInstruction.B < 256)
                 if (lastInstruction.OpCode == OpCode.Move && instruction.B == 2 && lastInstruction.B < 256)
                 {
                 {
                     lastInstruction = instruction with { A = (byte)lastInstruction.B };
                     lastInstruction = instruction with { A = (byte)lastInstruction.B };
+
                     instructionPositions[^1] = position;
                     instructionPositions[^1] = position;
                     incrementStackPosition = false;
                     incrementStackPosition = false;
                     return;
                     return;

+ 7 - 1
src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs

@@ -146,7 +146,13 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
                 a = context.StackTopPosition;
                 a = context.StackTopPosition;
             }
             }
 
 
-            context.PushInstruction(Instruction.Test(a, (byte)(node.OperatorType is BinaryOperator.And ? 0 : 1)), node.Position);
+            context.PushInstruction(Instruction.Test(a, 0), node.Position);
+            if (node.OperatorType is BinaryOperator.Or)
+            {
+                context.PushInstruction(Instruction.Jmp(0, 2), node.Position);
+                context.PushInstruction(Instruction.Move(r, a), node.Position);
+            }
+
             var testJmpIndex = context.Function.Instructions.Length;
             var testJmpIndex = context.Function.Instructions.Length;
             context.PushInstruction(Instruction.Jmp(0, 0), node.Position);
             context.PushInstruction(Instruction.Jmp(0, 0), node.Position);
 
 

+ 4 - 4
src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs

@@ -33,7 +33,7 @@ public class ScopeCompilationContext : IDisposable
     readonly Dictionary<ReadOnlyMemory<char>, LocalVariableDescription> localVariables = new(256, Utf16StringMemoryComparer.Default);
     readonly Dictionary<ReadOnlyMemory<char>, LocalVariableDescription> localVariables = new(256, Utf16StringMemoryComparer.Default);
     readonly Dictionary<ReadOnlyMemory<char>, LabelDescription> labels = new(32, Utf16StringMemoryComparer.Default);
     readonly Dictionary<ReadOnlyMemory<char>, LabelDescription> labels = new(32, Utf16StringMemoryComparer.Default);
 
 
-    byte lastLocalVariableIndex;
+    internal BitFlags256 ActiveLocalVariables = default;
 
 
     public byte StackStartPosition { get; private set; }
     public byte StackStartPosition { get; private set; }
     public byte StackPosition { get; set; }
     public byte StackPosition { get; set; }
@@ -74,7 +74,7 @@ public class ScopeCompilationContext : IDisposable
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public void PushInstruction(in Instruction instruction, SourcePosition position, bool incrementStackPosition = false)
     public void PushInstruction(in Instruction instruction, SourcePosition position, bool incrementStackPosition = false)
     {
     {
-        Function.PushOrMergeInstruction(lastLocalVariableIndex, instruction, position, ref incrementStackPosition);
+        Function.PushOrMergeInstruction(instruction, position, ref incrementStackPosition);
         if (incrementStackPosition)
         if (incrementStackPosition)
         {
         {
             StackPosition++;
             StackPosition++;
@@ -98,7 +98,7 @@ public class ScopeCompilationContext : IDisposable
     public void AddLocalVariable(ReadOnlyMemory<char> name, LocalVariableDescription description, bool markAsLastLocalVariable = true)
     public void AddLocalVariable(ReadOnlyMemory<char> name, LocalVariableDescription description, bool markAsLastLocalVariable = true)
     {
     {
         localVariables[name] = description;
         localVariables[name] = description;
-        lastLocalVariableIndex = description.RegisterIndex;
+        ActiveLocalVariables.Set(description.RegisterIndex);
     }
     }
 
 
 
 
@@ -173,7 +173,7 @@ public class ScopeCompilationContext : IDisposable
         HasCapturedLocalVariables = false;
         HasCapturedLocalVariables = false;
         localVariables.Clear();
         localVariables.Clear();
         labels.Clear();
         labels.Clear();
-        lastLocalVariableIndex = 0;
+        ActiveLocalVariables = default;
     }
     }
 
 
     /// <summary>
     /// <summary>

+ 23 - 0
src/Lua/Internal/BitFlags256.cs

@@ -0,0 +1,23 @@
+namespace Lua.Internal;
+
+internal unsafe struct BitFlags256
+{
+    internal fixed long Data[4];
+    
+    public bool this[int index]
+    {
+        get => (Data[index >> 6] & (1L << (index & 63))) != 0;
+        set
+        {
+            if (value)
+            {
+                Data[index >> 6] |= 1L << (index & 63);
+            }
+            else
+            {
+                Data[index >> 6] &= ~(1L << (index & 63));
+            }
+        }
+    }
+    public void Set(int index) => Data[index >> 6] |= 1L << (index & 63);
+}

+ 22 - 0
tests/Lua.Tests/ConditionalsTests.cs

@@ -0,0 +1,22 @@
+namespace Lua.Tests;
+
+public class ConditionalsTests
+{
+    [Test]
+    public async Task Test_Clamp()
+    {
+        var source = @"
+function clamp(x, min, max)
+    return x < min and min or (x > max and max or x)
+end
+
+return clamp(0, 1, 25), clamp(10, 1, 25), clamp(30, 1, 25)
+";
+        var result = await LuaState.Create().DoStringAsync(source);
+
+        Assert.That(result, Has.Length.EqualTo(3));
+        Assert.That(result[0], Is.EqualTo(new LuaValue(1)));
+        Assert.That(result[1], Is.EqualTo(new LuaValue(10)));
+        Assert.That(result[2], Is.EqualTo(new LuaValue(25)));
+    }
+}