Browse Source

Optimize: optimize and fix stacktrace

Akeit0 1 year ago
parent
commit
271cf48e28

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

@@ -64,6 +64,11 @@ public class FunctionCompilationContext : IDisposable
     // loop
     FastListCore<BreakDescription> breakQueue;
     FastListCore<GotoDescription> gotoQueue;
+    
+    /// <summary>
+    /// Maximum local stack size
+    /// </summary>
+    public byte MaxStackPosition { get; set; }
 
     /// <summary>
     /// Chunk name (for debug)
@@ -271,6 +276,7 @@ public class FunctionCompilationContext : IDisposable
             UpValues = upvalues.AsSpan().ToArray(),
             Functions = functions.AsSpan().ToArray(),
             ParameterCount = ParameterCount,
+            MaxStackPosition = MaxStackPosition,
         };
 
         foreach (var function in functions.AsSpan())
@@ -300,6 +306,7 @@ public class FunctionCompilationContext : IDisposable
         LoopLevel = 0;
         ParameterCount = 0;
         HasVariableArguments = false;
+        MaxStackPosition = 0;
     }
 
     /// <summary>

+ 1 - 0
src/Lua/CodeAnalysis/Compilation/ScopeCompilationContext.cs

@@ -74,6 +74,7 @@ public class ScopeCompilationContext : IDisposable
     {
         Function.PushInstruction(instruction, position);
         if (incrementStackPosition) StackPosition++;
+        Function.MaxStackPosition = Math.Max(Function.MaxStackPosition, StackPosition);
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 33 - 1
src/Lua/Internal/PooledList.cs

@@ -36,6 +36,32 @@ internal ref struct PooledList<T>
         tail++;
     }
 
+    public void AddRange(scoped ReadOnlySpan<T> items)
+    {
+        ThrowIfDisposed();
+
+        if (buffer == null)
+        {
+            buffer = ArrayPool<T>.Shared.Rent(items.Length);
+        }
+        else if (buffer.Length < tail + items.Length)
+        {
+            var newSize = buffer.Length * 2;
+            while (newSize < tail + items.Length)
+            {
+                newSize *= 2;
+            }
+            
+            var newArray = ArrayPool<T>.Shared.Rent(newSize);
+            buffer.AsSpan().CopyTo(newArray);
+            ArrayPool<T>.Shared.Return(buffer);
+            buffer = newArray;
+        }
+
+        items.CopyTo(buffer.AsSpan()[tail..]);
+        tail += items.Length;
+    }
+    
     public void Clear()
     {
         ThrowIfDisposed();
@@ -78,6 +104,12 @@ internal ref struct PooledList<T>
 
     void ThrowIfDisposed()
     {
-        if (tail == -1) throw new ObjectDisposedException(nameof(PooledList<T>));
+        if (tail == -1) ThrowDisposedException();
+    }
+    
+    void ThrowDisposedException()
+    {
+        throw new ObjectDisposedException(nameof(PooledList<T>));
     }
+    
 }

+ 0 - 5
src/Lua/LuaCoroutine.cs

@@ -63,11 +63,6 @@ public sealed class LuaCoroutine : LuaThread, IValueTaskSource<LuaCoroutine.Yiel
                         Stack.EnsureCapacity(baseThread.Stack.Count);
                         baseThread.Stack.AsSpan().CopyTo(Stack.GetBuffer());
                         Stack.NotifyTop(baseThread.Stack.Count);
-
-                        // copy callstack value
-                        CallStack.EnsureCapacity(baseThread.CallStack.Count);
-                        baseThread.CallStack.AsSpan().CopyTo(CallStack.GetBuffer());
-                        CallStack.NotifyTop(baseThread.CallStack.Count);
                     }
                     else
                     {

+ 0 - 3
src/Lua/LuaFunction.cs

@@ -23,9 +23,6 @@ public class LuaFunction(string name, Func<LuaFunctionExecutionContext, Memory<L
         var frame = new CallStackFrame
         {
             Base = context.FrameBase,
-            CallPosition = context.SourcePosition,
-            ChunkName = context.ChunkName ?? LuaState.DefaultChunkName,
-            RootChunkName = context.RootChunkName ?? LuaState.DefaultChunkName,
             VariableArgumentCount = this is Closure closure ? Math.Max(context.ArgumentCount - closure.Proto.ParameterCount, 0) : 0,
             Function = this,
         };

+ 25 - 5
src/Lua/LuaState.cs

@@ -88,13 +88,33 @@ public sealed class LuaState
 
     public Traceback GetTraceback()
     {
-        // TODO: optimize
+        if(threadStack.Count==0)
+        {
+            return new()
+            {
+                RootFunc = (Closure)MainThread.GetCallStackFrames()[0].Function,
+                StackFrames = MainThread.GetCallStackFrames()[1..]
+                    .ToArray()
+            };
+        }
+
+        using var list = new PooledList<CallStackFrame>(8);
+        foreach (var frame in MainThread.GetCallStackFrames()[1..])
+        {
+            list.Add(frame);
+        }
+        foreach (var thread in threadStack.AsSpan())
+        {
+            if(thread.CallStack.Count==0) continue;
+            foreach (var frame in thread.GetCallStackFrames()[1..])
+            {
+                list.Add(frame);
+            }
+        }
         return new()
         {
-            StackFrames = threadStack.AsSpan().ToArray()
-                .Append(MainThread)
-                .SelectMany(x => x.GetCallStackFrames()[1..].ToArray())
-                .ToArray()
+            RootFunc = (Closure)MainThread.GetCallStackFrames()[0].Function,
+            StackFrames = list.AsSpan().ToArray()
         };
     }
 

+ 8 - 4
src/Lua/Runtime/CallStackFrame.cs

@@ -7,10 +7,14 @@ namespace Lua.Runtime;
 public record struct CallStackFrame
 {
     public required int Base;
-    public required string ChunkName;
-    public required string RootChunkName;
     public required LuaFunction Function;
-    public required SourcePosition? CallPosition;
     public required int VariableArgumentCount;
-    public  int? CallerInstructionIndex;
+    public int CallerInstructionIndex;
+    internal CallStackFrameFlags Flags;
+}
+
+[Flags]
+public enum CallStackFrameFlags
+{
+    ReversedLe = 1,
 }

+ 2 - 0
src/Lua/Runtime/Chunk.cs

@@ -13,6 +13,8 @@ public sealed class Chunk
     public required UpValueInfo[] UpValues { get; init; }
     public required Chunk[] Functions { get; init; }
     public required int ParameterCount { get; init; }
+    
+    public required byte MaxStackPosition { get; init; }
 
     Chunk? rootCache;
 

+ 7 - 0
src/Lua/Runtime/LuaStack.cs

@@ -108,6 +108,13 @@ public sealed class LuaStack(int initialSize = 256)
     {
         return ref array[index];
     }
+    
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal ref LuaValue GetWithNotifyTop(int index)
+    {
+        if (this.top <= index) this.top = index + 1;
+        return ref array[index];
+    }
 
     static void ThrowEmptyStack()
     {

+ 204 - 164
src/Lua/Runtime/LuaVirtualMachine.cs

@@ -58,7 +58,7 @@ public static partial class LuaVirtualMachine
             var frames = callStack.AsSpan();
             if (frames.Length == BaseCallStackCount) return false;
             ref readonly var frame = ref frames[^1];
-            Pc = frame.CallerInstructionIndex!.Value;
+            Pc = frame.CallerInstructionIndex;
             ref readonly var lastFrame = ref frames[^2];
             Closure = Unsafe.As<Closure>(lastFrame.Function);
             var callInstruction = Chunk.Instructions[Pc];
@@ -74,6 +74,11 @@ public static partial class LuaVirtualMachine
             if (opCode is OpCode.Eq or OpCode.Lt or OpCode.Le)
             {
                 var compareResult = result.Length > 0 && result[0].ToBoolean();
+                if ((frame.Flags & CallStackFrameFlags.ReversedLe) != 0)
+                {
+                    compareResult = !compareResult;
+                }
+
                 if (compareResult != (callInstruction.A == 1))
                 {
                     Pc++;
@@ -85,32 +90,34 @@ public static partial class LuaVirtualMachine
 
             var target = callInstruction.A + FrameBase;
             var targetCount = result.Length;
-            if (opCode == OpCode.TForCall)
-            {
-                target += 3;
-                targetCount = callInstruction.C;
-            }
-            else if (opCode == OpCode.Call)
+            switch (opCode)
             {
-                var c = callInstruction.C;
-                if (c != 0)
+                case OpCode.Call:
                 {
-                    targetCount = c - 1;
+                    var c = callInstruction.C;
+                    if (c != 0)
+                    {
+                        targetCount = c - 1;
+                    }
+
+                    break;
                 }
-            }
-            else if (opCode == OpCode.Self)
-            {
-                Stack.Get(target) = result.Length == 0 ? LuaValue.Nil : result[0];
-                Thread.PopCallStackFrameUnsafe(target + 2);
-                return true;
-            }
-            else if (opCode is OpCode.SetTable or OpCode.SetTabUp)
-            {
-                targetCount = 0;
-            }
-            else // Other opcodes has one result
-            {
-                targetCount = 1;
+                case OpCode.TForCall:
+                    target += 3;
+                    targetCount = callInstruction.C;
+                    break;
+
+                case OpCode.Self:
+                    Stack.Get(target) = result.Length == 0 ? LuaValue.Nil : result[0];
+                    Thread.PopCallStackFrameUnsafe(target + 2);
+                    return true;
+                case OpCode.SetTable or OpCode.SetTabUp:
+                    targetCount = 0;
+                    break;
+                // Other opcodes has one result
+                default:
+                    targetCount = 1;
+                    break;
             }
 
             var count = Math.Min(result.Length, targetCount);
@@ -301,57 +308,54 @@ public static partial class LuaVirtualMachine
                 //This is a label to restart the execution when new function is called or restarted
                 Restart:
 
-                var instructions = context.Chunk.Instructions;
+                ref var instructionsHead = ref context.Chunk.Instructions[0];
                 var frameBase = context.FrameBase;
                 var stack = context.Stack;
+                stack.EnsureCapacity(frameBase + context.Chunk.MaxStackPosition);
                 ref var constHead = ref MemoryMarshalEx.UnsafeElementAt(context.Chunk.Constants, 0);
 
-                do
+                while (true)
                 {
-                    var instruction = instructions[++context.Pc];
-                    context.Instruction = instruction;
-                    var iA = (int)instruction.A;
-                    var ra1 = iA + frameBase + 1;
-                    switch (instruction.OpCode)
+                    var instructionRef = Unsafe.Add(ref instructionsHead, ++context.Pc);
+                    context.Instruction = instructionRef;
+                    switch (instructionRef.OpCode)
                     {
                         case OpCode.Move:
-                            stack.EnsureCapacity(ra1);
+                            var instruction = instructionRef;
                             ref var stackHead = ref stack.Get(frameBase);
+                            var iA = instruction.A;
                             Unsafe.Add(ref stackHead, iA) = Unsafe.Add(ref stackHead, instruction.UIntB);
-                            stack.NotifyTop(ra1);
+                            stack.NotifyTop(iA + frameBase + 1);
                             continue;
                         case OpCode.LoadK:
-                            stack.EnsureCapacity(ra1);
-                            stack.Get(ra1 - 1) = Unsafe.Add(ref constHead, instruction.Bx);
-                            stack.NotifyTop(ra1);
+                            instruction = instructionRef;
+                            stack.GetWithNotifyTop(instruction.A + frameBase) = Unsafe.Add(ref constHead, instruction.Bx);
                             continue;
                         case OpCode.LoadBool:
-                            stack.EnsureCapacity(ra1);
-                            stack.Get(ra1 - 1) = instruction.B != 0;
-                            stack.NotifyTop(ra1);
+                            instruction = instructionRef;
+                            stack.GetWithNotifyTop(instruction.A + frameBase) = instruction.B != 0;
                             if (instruction.C != 0) context.Pc++;
                             continue;
                         case OpCode.LoadNil:
+                            instruction = instructionRef;
+                            var ra1 = instruction.A + frameBase + 1;
                             var iB = instruction.B;
-                            stack.EnsureCapacity(ra1 + iB);
                             stack.GetBuffer().Slice(ra1 - 1, iB + 1).Clear();
                             stack.NotifyTop(ra1 + iB);
                             continue;
                         case OpCode.GetUpVal:
-                            stack.EnsureCapacity(ra1);
-                            stack.Get(ra1 - 1) = context.Closure.GetUpValue(instruction.B);
-                            stack.NotifyTop(ra1);
+                            instruction = instructionRef;
+                            stack.GetWithNotifyTop(instruction.A + frameBase) = context.Closure.GetUpValue(instruction.B);
                             continue;
                         case OpCode.GetTabUp:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
                             stackHead = ref stack.Get(frameBase);
                             ref readonly var vc = ref RKC(ref stackHead, ref constHead, instruction);
                             var table = context.Closure.GetUpValue(instruction.B);
 
                             if (table.TryReadTable(out var luaTable) && luaTable.TryGetValue(vc, out var resultValue))
                             {
-                                Unsafe.Add(ref stackHead, iA) = resultValue;
-                                stack.NotifyTop(ra1);
+                                stack.GetWithNotifyTop(instruction.A + frameBase) = resultValue;
                                 continue;
                             }
 
@@ -362,20 +366,16 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
+                            goto Await;
                         case OpCode.GetTable:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
                             stackHead = ref stack.Get(frameBase);
                             ref readonly var vb = ref Unsafe.Add(ref stackHead, instruction.UIntB);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
-                            if (vb.TryReadTable(out luaTable))
+                            if (vb.TryReadTable(out luaTable) && luaTable.TryGetValue(vc, out resultValue))
                             {
-                                if (luaTable.TryGetValue(vc, out resultValue))
-                                {
-                                    Unsafe.Add(ref stackHead, iA) = resultValue;
-                                    stack.NotifyTop(ra1);
-                                    continue;
-                                }
+                                stack.GetWithNotifyTop(instruction.A + frameBase) = resultValue;
+                                continue;
                             }
 
                             if (TryGetMetaTableValue(vb, vc, ref context, out doRestart))
@@ -385,9 +385,9 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
-
+                            goto Await;
                         case OpCode.SetTabUp:
+                            instruction = instructionRef;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             if (vb.TryReadNumber(out var numB))
@@ -415,12 +415,14 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.Nop;
-                            break;
+                            goto Await;
 
                         case OpCode.SetUpVal:
-                            context.Closure.SetUpValue(instruction.B, stack.Get(ra1 - 1));
+                            instruction = instructionRef;
+                            context.Closure.SetUpValue(instruction.B, stack.Get(instruction.A + frameBase));
                             continue;
                         case OpCode.SetTable:
+                            instruction = instructionRef;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             if (vb.TryReadNumber(out numB))
@@ -432,17 +434,11 @@ public static partial class LuaVirtualMachine
                                 }
                             }
 
-                            table = Unsafe.Add(ref stackHead, iA);
+                            table = Unsafe.Add(ref stackHead, instruction.A);
 
                             if (table.TryReadTable(out luaTable))
                             {
-                                if (luaTable.Metatable == null || !luaTable.Metatable!.ContainsKey(Metamethods.NewIndex))
-                                {
-                                    luaTable[vb] = RKC(ref stackHead, ref constHead, instruction);
-                                    continue;
-                                }
-
-                                if (luaTable.ContainsKey(vb))
+                                if (luaTable.Metatable == null || !luaTable.Metatable.ContainsKey(Metamethods.NewIndex) || luaTable.ContainsKey(vb))
                                 {
                                     luaTable[vb] = RKC(ref stackHead, ref constHead, instruction);
                                     continue;
@@ -457,28 +453,26 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.Nop;
-                            break;
-
-
+                            goto Await;
                         case OpCode.NewTable:
-                            stack.EnsureCapacity(ra1);
-                            stack.Get(ra1 - 1) = new LuaTable(instruction.B, instruction.C);
-                            stack.NotifyTop(ra1);
+                            instruction = instructionRef;
+                            stack.GetWithNotifyTop(instruction.A + frameBase) = new LuaTable(instruction.B, instruction.C);
                             continue;
                         case OpCode.Self:
-                            stack.EnsureCapacity(ra1 + 1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
                             table = Unsafe.Add(ref stackHead, instruction.UIntB);
-
                             if (table.TryReadTable(out luaTable) && luaTable.TryGetValue(vc, out resultValue))
                             {
                                 Unsafe.Add(ref stackHead, iA) = resultValue;
                                 Unsafe.Add(ref stackHead, iA + 1) = table;
-                                stack.NotifyTop(ra1 + 2);
+                                stack.NotifyTop(iA + frameBase + 2);
                                 continue;
                             }
 
+
                             if (TryGetMetaTableValue(table, vc, ref context, out doRestart))
                             {
                                 if (doRestart) goto Restart;
@@ -486,27 +480,27 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.Self;
-                            break;
+                            goto Await;
                         case OpCode.Add:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
-
-                            if(vb.Type  == LuaValueType.Number && vc.Type== LuaValueType.Number)
+                            if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number)
                             {
-                                Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() + vc.UnsafeReadDouble() ;
-                                stack.NotifyTop(ra1);
+                                Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() + vc.UnsafeReadDouble();
+                                stack.NotifyTop(iA + frameBase + 1);
                                 continue;
                             }
-                            
+
                             if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out var numC))
                             {
                                 Unsafe.Add(ref stackHead, iA) = numB + numC;
-                                stack.NotifyTop(ra1);
+                                stack.NotifyTop(iA + frameBase + 1);
                                 continue;
                             }
-                            
+
                             if (ExecuteBinaryOperationMetaMethod(vb, vc, ref context, Metamethods.Add, "add", out doRestart))
                             {
                                 if (doRestart) goto Restart;
@@ -514,22 +508,25 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
+                            goto Await;
                         case OpCode.Sub:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
-                            
-                            if(vb.Type  == LuaValueType.Number && vc.Type== LuaValueType.Number)
+
+                            if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number)
                             {
-                                Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() - vc.UnsafeReadDouble() ;
+                                ra1 = iA + frameBase + 1;
+                                Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() - vc.UnsafeReadDouble();
                                 stack.NotifyTop(ra1);
                                 continue;
                             }
 
                             if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC))
                             {
+                                ra1 = iA + frameBase + 1;
                                 Unsafe.Add(ref stackHead, iA) = numB - numC;
                                 stack.NotifyTop(ra1);
                                 continue;
@@ -542,23 +539,26 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
+                            goto Await;
 
                         case OpCode.Mul:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
-                            
-                            if(vb.Type  == LuaValueType.Number && vc.Type== LuaValueType.Number)
+
+                            if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number)
                             {
-                                Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() * vc.UnsafeReadDouble() ;
+                                ra1 = iA + frameBase + 1;
+                                Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() * vc.UnsafeReadDouble();
                                 stack.NotifyTop(ra1);
                                 continue;
                             }
 
                             if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC))
                             {
+                                ra1 = iA + frameBase + 1;
                                 Unsafe.Add(ref stackHead, iA) = numB * numC;
                                 stack.NotifyTop(ra1);
                                 continue;
@@ -571,23 +571,26 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
+                            goto Await;
 
                         case OpCode.Div:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
-                            
-                            if(vb.Type  == LuaValueType.Number && vc.Type== LuaValueType.Number)
+
+                            if (vb.Type == LuaValueType.Number && vc.Type == LuaValueType.Number)
                             {
-                                Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() / vc.UnsafeReadDouble() ;
+                                ra1 = iA + frameBase + 1;
+                                Unsafe.Add(ref stackHead, iA) = vb.UnsafeReadDouble() / vc.UnsafeReadDouble();
                                 stack.NotifyTop(ra1);
                                 continue;
                             }
 
                             if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC))
                             {
+                                ra1 = iA + frameBase + 1;
                                 Unsafe.Add(ref stackHead, iA) = numB / numC;
                                 stack.NotifyTop(ra1);
                                 continue;
@@ -600,9 +603,10 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
+                            goto Await;
                         case OpCode.Mod:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
@@ -615,7 +619,6 @@ public static partial class LuaVirtualMachine
                                 }
 
                                 Unsafe.Add(ref stackHead, iA) = mod;
-                                stack.NotifyTop(ra1);
                                 continue;
                             }
 
@@ -626,14 +629,16 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
+                            goto Await;
                         case OpCode.Pow:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
                             if (vb.TryReadDouble(out numB) && vc.TryReadDouble(out numC))
                             {
+                                ra1 = iA + frameBase + 1;
                                 Unsafe.Add(ref stackHead, iA) = Math.Pow(numB, numC);
                                 stack.NotifyTop(ra1);
                                 continue;
@@ -646,17 +651,18 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
-
+                            goto Await;
                         case OpCode.Unm:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref Unsafe.Add(ref stackHead, instruction.UIntB);
 
                             if (vb.TryReadDouble(out numB))
                             {
+                                //ra1 = iA + frameBase + 1;
                                 Unsafe.Add(ref stackHead, iA) = -numB;
-                                stack.NotifyTop(ra1);
+                                //stack.NotifyTop(ra1);
                                 continue;
                             }
 
@@ -667,25 +673,29 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
-
+                            goto Await;
                         case OpCode.Not:
+                            instruction = instructionRef;
+                            iA = instruction.A;
+                            ra1 = iA + frameBase + 1;
                             stack.EnsureCapacity(ra1);
                             stackHead = ref stack.Get(frameBase);
                             Unsafe.Add(ref stackHead, iA) = !Unsafe.Add(ref stackHead, instruction.UIntB).ToBoolean();
-                            stack.NotifyTop(ra1);
+                            //stack.NotifyTop(ra1);
                             continue;
 
                         case OpCode.Len:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
                             stackHead = ref stack.Get(frameBase);
 
                             vb = ref Unsafe.Add(ref stackHead, instruction.UIntB);
 
                             if (vb.TryReadString(out var str))
                             {
+                                iA = instruction.A;
+                                // ra1 = iA + frameBase + 1;
                                 Unsafe.Add(ref stackHead, iA) = str.Length;
-                                stack.NotifyTop(ra1);
+                                //stack.NotifyTop(ra1);
                                 continue;
                             }
 
@@ -696,7 +706,7 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
+                            goto Await;
                         case OpCode.Concat:
                             if (Concat(ref context, out doRestart))
                             {
@@ -705,9 +715,11 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.SetResult;
-                            break;
+                            goto Await;
                         case OpCode.Jmp:
+                            instruction = instructionRef;
                             context.Pc += instruction.SBx;
+                            iA = instruction.A;
                             if (iA != 0)
                             {
                                 context.State.CloseUpValues(context.Thread, frameBase + iA - 1);
@@ -715,7 +727,8 @@ public static partial class LuaVirtualMachine
 
                             continue;
                         case OpCode.Eq:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
@@ -736,9 +749,10 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.Compare;
-                            break;
+                            goto Await;
                         case OpCode.Lt:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
@@ -773,10 +787,11 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.Compare;
-                            break;
+                            goto Await;
 
                         case OpCode.Le:
-                            stack.EnsureCapacity(ra1);
+                            instruction = instructionRef;
+                            iA = instruction.A;
                             stackHead = ref stack.Get(frameBase);
                             vb = ref RKB(ref stackHead, ref constHead, instruction);
                             vc = ref RKC(ref stackHead, ref constHead, instruction);
@@ -810,17 +825,18 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.Compare;
-                            break;
+                            goto Await;
 
                         case OpCode.Test:
-                            if (stack.Get(ra1 - 1).ToBoolean() != (instruction.C == 1))
+                            instruction = instructionRef;
+                            if (stack.Get(instruction.A + frameBase).ToBoolean() != (instruction.C == 1))
                             {
                                 context.Pc++;
                             }
 
                             continue;
-
                         case OpCode.TestSet:
+                            instruction = instructionRef;
                             vb = ref stack.Get(instruction.B + frameBase);
                             if (vb.ToBoolean() != (instruction.C == 1))
                             {
@@ -828,8 +844,7 @@ public static partial class LuaVirtualMachine
                             }
                             else
                             {
-                                stack.Get(ra1 - 1) = vb;
-                                stack.NotifyTop(ra1);
+                                stack.GetWithNotifyTop(instruction.A + frameBase) = vb;
                             }
 
                             continue;
@@ -842,7 +857,7 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.Call;
-                            break;
+                            goto Await;
                         case OpCode.TailCall:
                             if (TailCall(ref context, out doRestart))
                             {
@@ -852,8 +867,11 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.TailCall;
-                            break;
+                            goto Await;
                         case OpCode.Return:
+                            instruction = instructionRef;
+                            iA = instruction.A;
+                            ra1 = iA + frameBase + 1;
                             context.State.CloseUpValues(context.Thread, frameBase);
                             if (context.Pop(instruction, frameBase)) goto Restart;
                             var retCount = instruction.B - 1;
@@ -871,26 +889,24 @@ public static partial class LuaVirtualMachine
                             context.ResultCount = retCount;
                             goto End;
                         case OpCode.ForLoop:
-                            stack.EnsureCapacity(ra1 + 3);
-                            ref var indexRef = ref stack.Get(ra1 - 1);
-
-                            var lastIndex = indexRef.UnsafeReadDouble();
-                            var step = Unsafe.Add(ref indexRef, 2).UnsafeReadDouble();
-                            var index = lastIndex + step;
+                            ref var indexRef = ref stack.Get(instructionRef.A + frameBase);
                             var limit = Unsafe.Add(ref indexRef, 1).UnsafeReadDouble();
+                            var step = Unsafe.Add(ref indexRef, 2).UnsafeReadDouble();
+                            var index = indexRef.UnsafeReadDouble() + step;
+
                             if (step >= 0 ? index <= limit : limit <= index)
                             {
-                                context.Pc += instruction.SBx;
+                                context.Pc += instructionRef.SBx;
                                 indexRef = index;
                                 Unsafe.Add(ref indexRef, 3) = index;
-                                stack.NotifyTop(ra1 + 3);
+                                stack.NotifyTop(instructionRef.A + frameBase + 4);
                                 continue;
                             }
 
-                            stack.NotifyTop(ra1);
+                            stack.NotifyTop(instructionRef.A + frameBase + 1);
                             continue;
                         case OpCode.ForPrep:
-                            indexRef = ref stack.Get(ra1 - 1);
+                            indexRef = ref stack.Get(instructionRef.A + frameBase);
 
                             if (!indexRef.TryReadDouble(out var init))
                             {
@@ -911,8 +927,8 @@ public static partial class LuaVirtualMachine
                             }
 
                             indexRef = init - step;
-                            stack.NotifyTop(ra1);
-                            context.Pc += instruction.SBx;
+                            //stack.NotifyTop(instructionRef.A + frameBase + 1);
+                            context.Pc += instructionRef.SBx;
                             continue;
                         case OpCode.TForCall:
                             if (TForCall(ref context, out doRestart))
@@ -922,9 +938,13 @@ public static partial class LuaVirtualMachine
                             }
 
                             postOperation = PostOperationType.TForCall;
-                            break;
+                            goto Await;
                         case OpCode.TForLoop:
+                            instruction = instructionRef;
+                            iA = instruction.A;
+                            ra1 = iA + frameBase + 1;
                             ref var forState = ref stack.Get(ra1);
+
                             if (forState.Type is not LuaValueType.Nil)
                             {
                                 Unsafe.Add(ref forState, -1) = forState;
@@ -936,11 +956,17 @@ public static partial class LuaVirtualMachine
                             SetList(ref context);
                             continue;
                         case OpCode.Closure:
+                            instruction = instructionRef;
+                            iA = instruction.A;
+                            ra1 = iA + frameBase + 1;
                             stack.EnsureCapacity(ra1);
                             stack.Get(ra1 - 1) = new Closure(context.State, context.Chunk.Functions[instruction.SBx]);
                             stack.NotifyTop(ra1);
                             continue;
                         case OpCode.VarArg:
+                            instruction = instructionRef;
+                            iA = instruction.A;
+                            ra1 = iA + frameBase + 1;
                             var frameVariableArgumentCount = context.VariableArgumentCount;
                             var count = instruction.B == 0
                                 ? frameVariableArgumentCount
@@ -950,8 +976,8 @@ public static partial class LuaVirtualMachine
                             stackHead = ref stack.Get(0);
                             for (int i = 0; i < count; i++)
                             {
-                                Unsafe.Add(ref stackHead,ra + i) = frameVariableArgumentCount > i
-                                    ? Unsafe.Add(ref stackHead,frameBase - (frameVariableArgumentCount - i))
+                                Unsafe.Add(ref stackHead, ra + i) = frameVariableArgumentCount > i
+                                    ? Unsafe.Add(ref stackHead, frameBase - (frameVariableArgumentCount - i))
                                     : default;
                             }
 
@@ -959,12 +985,12 @@ public static partial class LuaVirtualMachine
                             continue;
                         case OpCode.ExtraArg:
                         default:
-                            ThrowLuaNotImplementedException(ref context, instruction.OpCode);
+                            ThrowLuaNotImplementedException(ref context, context.Instruction.OpCode);
                             return;
                     }
-                } while (postOperation == PostOperationType.None);
-
+                }
 
+                Await:
                 //Set the state to await and return with setting this method as the task's continuation
                 state = State.Await;
                 Builder.AwaitOnCompleted(ref context.Awaiter, ref this);
@@ -980,7 +1006,7 @@ public static partial class LuaVirtualMachine
             {
                 if (e is not LuaRuntimeException)
                 {
-                    e = new LuaRuntimeCSharpException(context.State.GetTraceback(), e);
+                    e = new LuaRuntimeCSharpException(GetTracebacks(ref context), e);
                 }
 
                 context.PopOnTopCallStackFrames();
@@ -1074,7 +1100,7 @@ public static partial class LuaVirtualMachine
             }
             else
             {
-                LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "call", metamethod);
+                LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), "call", va);
             }
         }
 
@@ -1318,7 +1344,7 @@ public static partial class LuaVirtualMachine
             : instruction.B;
 
         table.EnsureArrayCapacity((instruction.C - 1) * 50 + count);
-        stack.AsSpan().Slice(RA + 1, count)
+        stack.GetBuffer().Slice(RA + 1, count)
             .CopyTo(table.GetArraySpan()[((instruction.C - 1) * 50)..]);
     }
 
@@ -1506,7 +1532,6 @@ public static partial class LuaVirtualMachine
                 context.Thread.PopCallStackFrame();
                 var RA = context.Instruction.A + context.FrameBase;
                 stack.Get(RA) = resultCount == 0 ? LuaValue.Nil : context.ResultsBuffer[0];
-                stack.NotifyTop(RA + 1);
                 context.ClearResultsBuffer(resultCount);
                 return true;
             }
@@ -1553,7 +1578,6 @@ public static partial class LuaVirtualMachine
                 var RA = context.Instruction.A + context.FrameBase;
                 var resultCount = context.Awaiter.GetResult();
                 stack.Get(RA) = resultCount == 0 ? LuaValue.Nil : context.ResultsBuffer[0];
-                stack.NotifyTop(RA + 1);
                 context.ClearResultsBuffer(resultCount);
                 return true;
             }
@@ -1565,7 +1589,6 @@ public static partial class LuaVirtualMachine
         {
             var RA = context.Instruction.A + context.FrameBase;
             stack.Get(RA) = table.ArrayLength;
-            stack.NotifyTop(RA + 1);
             return true;
         }
 
@@ -1573,11 +1596,14 @@ public static partial class LuaVirtualMachine
         return true;
     }
 
+
     [MethodImpl(MethodImplOptions.NoInlining)]
     static bool ExecuteCompareOperationMetaMethod(LuaValue vb, LuaValue vc,
         ref VirtualMachineExecutionContext context, string name, string? description, out bool doRestart)
     {
         doRestart = false;
+        bool reverseLe = false;
+        ReCheck:
         if (vb.TryGetMetamethod(context.State, name, out var metamethod) ||
             vc.TryGetMetamethod(context.State, name, out metamethod))
         {
@@ -1590,7 +1616,7 @@ public static partial class LuaVirtualMachine
             stack.Push(vb);
             stack.Push(vc);
             var newFrame = func.CreateNewFrame(ref context, stack.Count - 2);
-
+            if (reverseLe) newFrame.Flags |= CallStackFrameFlags.ReversedLe;
             context.Thread.PushCallStackFrame(newFrame);
 
             if (func.IsClosure)
@@ -1608,6 +1634,7 @@ public static partial class LuaVirtualMachine
                 context.Thread.PopCallStackFrame();
                 var resultCount = context.Awaiter.GetResult();
                 var compareResult = resultCount != 0 && context.ResultsBuffer[0].ToBoolean();
+                compareResult = reverseLe ? !compareResult : compareResult;
                 if (compareResult != (context.Instruction.A == 1))
                 {
                     context.Pc++;
@@ -1621,8 +1648,21 @@ public static partial class LuaVirtualMachine
             return false;
         }
 
+        if (name == Metamethods.Le)
+        {
+            reverseLe = true;
+            name = Metamethods.Lt;
+            (vb, vc) = (vc, vb);
+            goto ReCheck;
+        }
+
         if (description != null)
         {
+            if (reverseLe)
+            {
+                (vb, vc) = (vc, vb);
+            }
+
             LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(ref context), description, vb, vc);
         }
         else
@@ -1663,6 +1703,10 @@ public static partial class LuaVirtualMachine
         {
             argumentCount = (ushort)(thread.Stack.Count - (RA + 1));
         }
+        else
+        {
+            thread.Stack.NotifyTop(RA + 1 + argumentCount);
+        }
 
         var newBase = RA + 1;
         var variableArgumentCount = function.GetVariableArgumentCount(argumentCount);
@@ -1686,6 +1730,10 @@ public static partial class LuaVirtualMachine
         {
             argumentCount = (ushort)(stack.Count - (RA + 1));
         }
+        else
+        {
+            thread.Stack.NotifyTop(RA + 1 + argumentCount);
+        }
 
         var newBase = RA + 1;
 
@@ -1711,17 +1759,15 @@ public static partial class LuaVirtualMachine
 
     static Traceback GetTracebacks(ref VirtualMachineExecutionContext context)
     {
-        return GetTracebacks(context.State, context.Chunk, context.Pc);
+        return GetTracebacks(context.State, context.Pc);
     }
 
-    static Traceback GetTracebacks(LuaState state, Chunk chunk, int pc)
+    static Traceback GetTracebacks(LuaState state, int pc)
     {
         var frame = state.CurrentThread.GetCurrentFrame();
         state.CurrentThread.PushCallStackFrame(frame with
         {
-            CallPosition = chunk.SourcePositions[pc],
-            ChunkName = chunk.Name,
-            RootChunkName = chunk.GetRoot().Name,
+            CallerInstructionIndex = pc
         });
         var tracebacks = state.GetTraceback();
         state.CurrentThread.PopCallStackFrame();
@@ -1734,9 +1780,6 @@ public static partial class LuaVirtualMachine
         return new()
         {
             Base = newBase,
-            CallPosition = context.Chunk.SourcePositions[context.Pc],
-            ChunkName = context.Chunk.Name,
-            RootChunkName = context.Chunk.GetRoot().Name,
             Function = function,
             VariableArgumentCount = variableArgumentCount,
             CallerInstructionIndex = context.Pc,
@@ -1752,9 +1795,6 @@ public static partial class LuaVirtualMachine
             Thread = context.Thread,
             ArgumentCount = arguments,
             FrameBase = frame.Base,
-            SourcePosition = frame.CallPosition,
-            ChunkName = frame.ChunkName,
-            RootChunkName = frame.RootChunkName,
             CallerInstructionIndex = frame.CallerInstructionIndex,
         }, context.ResultsBuffer, context.CancellationToken);
     }

+ 54 - 9
src/Lua/Runtime/Tracebacks.cs

@@ -1,24 +1,69 @@
+using System.Globalization;
+using System.Runtime.CompilerServices;
 using Lua.CodeAnalysis;
+using Lua.Internal;
 
 namespace Lua.Runtime;
 
 public class Traceback
 {
+    public required Closure RootFunc { get; init; }
     public required CallStackFrame[] StackFrames { get; init; }
 
-    internal string RootChunkName => StackFrames.Length == 0 ? "" : StackFrames[^1].RootChunkName;
-    internal SourcePosition LastPosition => StackFrames.Length == 0 ? default : StackFrames[^1].CallPosition!.Value;
+    internal string RootChunkName => RootFunc.Proto.Name; //StackFrames.Length == 0 ? "" : StackFrames[^1].Function is Closure closure ? closure.Proto.GetRoot().Name : StackFrames[^2].Function.Name;
+
+    internal SourcePosition LastPosition
+    {
+        get
+        {
+            var stackFrames = StackFrames.AsSpan();
+            for (var index = stackFrames.Length - 1; index >= 0; index--)
+            {
+                LuaFunction lastFunc = index > 0 ? stackFrames[index - 1].Function : RootFunc;
+                var frame = stackFrames[index];
+                if (lastFunc is Closure closure)
+                {
+                    var p = closure.Proto;
+                    return p.SourcePositions[frame.CallerInstructionIndex];
+                }
+            }
+
+            return default;
+        }
+    }
+
 
     public override string ToString()
     {
-        var str = string.Join("\n   ", StackFrames
-            .Where(x => x.CallPosition != null)
-            .Select(x =>
+        using var list = new PooledList<char>(64);
+        list.AddRange("stack traceback:\n");
+        var stackFrames = StackFrames.AsSpan();
+        var intFormatBuffer = (stackalloc char[15]);
+        for (var index = stackFrames.Length - 1; index >= 0; index--)
+        {
+            LuaFunction lastFunc = index > 0 ? stackFrames[index - 1].Function : RootFunc;
+            var frame = stackFrames[index];
+            if (lastFunc is not null and not Closure)
+            {
+                list.AddRange("\t[C#]: in function '");
+                list.AddRange(lastFunc.Name);
+                list.AddRange("'\n");
+            }
+            else if (lastFunc is Closure closure)
             {
-                return $"{x.RootChunkName}:{x.CallPosition!.Value.Line}: {(string.IsNullOrEmpty(x.ChunkName) ? "" : $"in '{x.ChunkName}'")}";
-            })
-            .Reverse());
+                var p = closure.Proto;
+                var root = p.GetRoot();
+                list.AddRange("\t");
+                list.AddRange(root.Name);
+                list.AddRange(":");
+                p.SourcePositions[frame.CallerInstructionIndex].Line.TryFormat(intFormatBuffer, out var charsWritten,provider:CultureInfo.InvariantCulture);
+                list.AddRange(intFormatBuffer[..charsWritten]);
+                list.AddRange(root == p ? ": in '" : ": in function '");
+                list.AddRange(p.Name);
+                list.AddRange("'\n");
+            }
+        }
 
-        return $"stack traceback:\n   {str}";
+        return list.AsSpan().ToString();
     }
 }

+ 20 - 6
src/Lua/Standard/CoroutineLibrary.cs

@@ -64,15 +64,29 @@ public sealed class CoroutineLibrary
 
             stack.Push(thread);
             stack.PushRange(context.Arguments);
+            context.Thread.PushCallStackFrame(new()
+            {
+                Base = frameBase,
+                VariableArgumentCount = 0,
+                Function = arg0,
+            });
+            try
+            {
+                var resultCount = await thread.ResumeAsync(context with
+                {
+                    ArgumentCount = context.ArgumentCount + 1,
+                    FrameBase = frameBase,
+                }, buffer, cancellationToken);
 
-            var resultCount = await thread.ResumeAsync(context with
+                buffer.Span[1..].CopyTo(buffer.Span[0..]);
+                return resultCount - 1;
+            }
+            finally
             {
-                ArgumentCount = context.ArgumentCount + 1,
-                FrameBase = frameBase,
-            }, buffer, cancellationToken);
+                context.Thread.PopCallStackFrame();
+            }
 
-            buffer.Span[1..].CopyTo(buffer.Span[0..]);
-            return resultCount - 1;
+           
         });
 
         return new(1);

+ 18 - 3
src/Lua/Standard/TableLibrary.cs

@@ -8,6 +8,7 @@ namespace Lua.Standard;
 public sealed class TableLibrary
 {
     public static readonly TableLibrary Instance = new();
+    
 
     public TableLibrary()
     {
@@ -40,6 +41,7 @@ public sealed class TableLibrary
         ],
         ParameterCount = 2,
         UpValues = [],
+        MaxStackPosition = 2,
     };
 
     public ValueTask<int> Concat(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
@@ -158,9 +160,22 @@ public sealed class TableLibrary
         var arg1 = context.HasArgument(1)
             ? context.GetArgument<LuaFunction>(1)
             : new Closure(context.State, defaultComparer);
-
-        await QuickSortAsync(context, arg0.GetArrayMemory(), 0, arg0.ArrayLength - 1, arg1, cancellationToken);
-        return 0;
+        
+        context.Thread.PushCallStackFrame(new ()
+        {
+            Base = context.FrameBase,
+            VariableArgumentCount = 0,
+            Function = arg1
+        });
+        try
+        {
+            await QuickSortAsync(context, arg0.GetArrayMemory(), 0, arg0.ArrayLength - 1, arg1, cancellationToken);
+            return 0;
+        }
+        finally
+        {
+            context.Thread.PopCallStackFrameUnsafe(context.FrameBase);
+        }
     }
 
     async ValueTask QuickSortAsync(LuaFunctionExecutionContext context, Memory<LuaValue> memory, int low, int high, LuaFunction comparer, CancellationToken cancellationToken)