Browse Source

Merge pull request #30 from AnnulusGames/compiler-optimization

Optimization
Annulus Games 1 year ago
parent
commit
b0110e96ed

+ 45 - 2
sandbox/Benchmark/AddBenchmark.cs

@@ -1,17 +1,48 @@
+using System.Reflection;
 using BenchmarkDotNet.Attributes;
 using Lua;
+using Lua.Standard;
 using MoonSharp.Interpreter;
 
+sealed class AddFunction : Lua.LuaFunction
+{
+    protected override ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        buffer.Span[0] = context.GetArgument<double>(0) + context.GetArgument<double>(1);
+        return new(1);
+    }
+}
+
 [Config(typeof(BenchmarkConfig))]
 public class AddBenchmark
 {
     BenchmarkCore core = new();
     LuaValue[] buffer = new LuaValue[1];
 
-    [GlobalSetup]
-    public void GlobalSetup()
+    public static double Add(double x, double y)
     {
+        return x + y;
+    }
+
+    [IterationSetup]
+    public void Setup()
+    {
+        core = new();
         core.Setup("add.lua");
+        core.LuaCSharpState.OpenStandardLibraries();
+
+        core.LuaCSharpState.Environment["add"] = new AddFunction();
+        core.MoonSharpState.Globals["add"] = (Func<double, double, double>)Add;
+        core.NLuaState.RegisterFunction("add", typeof(AddBenchmark).GetMethod(nameof(Add), BindingFlags.Static | BindingFlags.Public));
+        core.NeoLuaEnvironment.SetValue("add", (Func<double, double, double>)Add);
+    }
+
+    [IterationCleanup]
+    public void Cleanup()
+    {
+        core.Dispose();
+        core = default!;
+        GC.Collect();
     }
 
     [Benchmark(Description = "MoonSharp (RunString)")]
@@ -38,6 +69,18 @@ public class AddBenchmark
         return core.NLuaState.DoFile(core.FilePath);
     }
 
+    [Benchmark(Description = "NeoLua (DoChunk(code))")]
+    public Neo.IronLua.LuaResult Benchmark_NeoLua_String()
+    {
+        return core.NeoLuaEnvironment.DoChunk(core.SourceText, "chunk");
+    }
+
+    [Benchmark(Description = "NeoLua (DoChunk(fileName))")]
+    public Neo.IronLua.LuaResult Benchmark_NeoLua_File()
+    {
+        return core.NeoLuaEnvironment.DoChunk(core.FilePath);
+    }
+
     [Benchmark(Description = "Lua-CSharp (DoString)")]
     public async Task<LuaValue> Benchmark_LuaCSharp_String()
     {

+ 1 - 0
sandbox/Benchmark/Benchmark.csproj

@@ -10,6 +10,7 @@
   <ItemGroup>
     <PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
     <PackageReference Include="MoonSharp" Version="2.0.0" />
+    <PackageReference Include="NeoLua" Version="1.3.14" />
     <PackageReference Include="NLua" Version="1.7.3" />
   </ItemGroup>
 

+ 14 - 1
sandbox/Benchmark/BenchmarkCore.cs

@@ -1,15 +1,18 @@
 using Lua;
 using MoonSharp.Interpreter;
 
-public class BenchmarkCore
+public class BenchmarkCore : IDisposable
 {
     public NLua.Lua NLuaState => nLuaState;
+    public Neo.IronLua.LuaGlobal NeoLuaEnvironment => neoLuaEnvironment;
     public Script MoonSharpState => moonSharpState;
     public LuaState LuaCSharpState => luaCSharpState;
     public string FilePath => filePath;
     public string SourceText => sourceText;
 
     NLua.Lua nLuaState = default!;
+    Neo.IronLua.Lua neoLuaState = default!;
+    Neo.IronLua.LuaGlobal neoLuaEnvironment = default!;
     Script moonSharpState = default!;
     LuaState luaCSharpState = default!;
     string filePath = default!;
@@ -24,10 +27,20 @@ public class BenchmarkCore
         // NLua
         nLuaState = new();
 
+        // NeoLua
+        neoLuaState = new();
+        neoLuaEnvironment = neoLuaState.CreateEnvironment();
+
         // Lua-CSharp
         luaCSharpState = LuaState.Create();
 
         filePath = FileHelper.GetAbsolutePath(fileName);
         sourceText = File.ReadAllText(filePath);
     }
+
+    public void Dispose()
+    {
+        nLuaState.Dispose();
+        neoLuaState.Dispose();
+    }
 }

+ 12 - 3
sandbox/Benchmark/InterpreterSteps.cs

@@ -3,6 +3,7 @@ using Lua;
 using Lua.CodeAnalysis.Compilation;
 using Lua.CodeAnalysis.Syntax;
 using Lua.Runtime;
+using Lua.Standard;
 
 [Config(typeof(BenchmarkConfig))]
 public class InterpreterSteps
@@ -17,11 +18,9 @@ public class InterpreterSteps
     [GlobalSetup]
     public void GlobalSetup()
     {
-        var filePath = FileHelper.GetAbsolutePath("add.lua");
+        var filePath = FileHelper.GetAbsolutePath("n-body.lua");
         sourceText = File.ReadAllText(filePath);
 
-        state = LuaState.Create();
-
         var lexer = new Lexer
         {
             Source = sourceText.AsMemory()
@@ -45,6 +44,16 @@ public class InterpreterSteps
         chunk = LuaCompiler.Default.Compile(ast);
     }
 
+    [IterationSetup]
+    public void Setup()
+    {
+        state = default!;
+        GC.Collect();
+
+        state = LuaState.Create();
+        state.OpenStandardLibraries();
+    }
+
     [Benchmark]
     public void CreateState()
     {

+ 77 - 0
sandbox/Benchmark/NBodyBenchmark.cs

@@ -0,0 +1,77 @@
+using BenchmarkDotNet.Attributes;
+using Lua;
+using Lua.Standard;
+using MoonSharp.Interpreter;
+
+[Config(typeof(BenchmarkConfig))]
+public class NBodyBenchmark
+{
+    BenchmarkCore core = default!;
+    LuaValue[] buffer = new LuaValue[1];
+
+    [IterationSetup]
+    public void Setup()
+    {
+        core = new();
+        core.Setup("n-body.lua");
+        core.LuaCSharpState.OpenStandardLibraries();
+    }
+
+    [IterationCleanup]
+    public void Cleanup()
+    {
+        core.Dispose();
+        core = default!;
+        GC.Collect();
+    }
+
+    [Benchmark(Description = "MoonSharp (RunString)")]
+    public DynValue Benchmark_MoonSharp_String()
+    {
+        return core.MoonSharpState.DoString(core.SourceText);
+    }
+
+    [Benchmark(Description = "MoonSharp (RunFile)")]
+    public DynValue Benchmark_MoonSharp_File()
+    {
+        return core.MoonSharpState.DoFile(core.FilePath);
+    }
+
+    [Benchmark(Description = "NLua (DoString)")]
+    public object[] Benchmark_NLua_String()
+    {
+        return core.NLuaState.DoString(core.SourceText);
+    }
+
+    [Benchmark(Description = "NLua (DoFile)")]
+    public object[] Benchmark_NLua_File()
+    {
+        return core.NLuaState.DoFile(core.FilePath);
+    }
+
+    [Benchmark(Description = "NeoLua (DoChunk(code))")]
+    public Neo.IronLua.LuaResult Benchmark_NeoLua_String()
+    {
+        return core.NeoLuaEnvironment.DoChunk(core.SourceText, "chunk");
+    }
+
+    [Benchmark(Description = "NeoLua (DoChunk(fileName))")]
+    public Neo.IronLua.LuaResult Benchmark_NeoLua_File()
+    {
+        return core.NeoLuaEnvironment.DoChunk(core.FilePath);
+    }
+
+    [Benchmark(Description = "Lua-CSharp (DoString)")]
+    public async Task<LuaValue> Benchmark_LuaCSharp_String()
+    {
+        await core.LuaCSharpState.DoStringAsync(core.SourceText, buffer);
+        return buffer[0];
+    }
+
+    [Benchmark(Description = "Lua-CSharp (DoFileAsync)")]
+    public async Task<LuaValue> Benchmark_LuaCSharp_File()
+    {
+        await core.LuaCSharpState.DoFileAsync(core.FilePath, buffer);
+        return buffer[0];
+    }
+}

+ 2 - 2
sandbox/Benchmark/add.lua

@@ -1,7 +1,7 @@
 local x = 0
 
-for i = 0, 10000 do
-    x = x + i;
+for _ = 0, 10000 do
+    x = add(x, 1)
 end
 
 return x

+ 115 - 0
sandbox/Benchmark/n-body.lua

@@ -0,0 +1,115 @@
+sun = {}
+jupiter = {}
+saturn = {}
+uranus = {}
+neptune = {}
+
+local sqrt = math.sqrt
+
+local PI = 3.141592653589793
+local SOLAR_MASS = 4 * PI * PI
+local DAYS_PER_YEAR = 365.24
+sun.x = 0.0
+sun.y = 0.0
+sun.z = 0.0
+sun.vx = 0.0
+sun.vy = 0.0
+sun.vz = 0.0
+sun.mass = SOLAR_MASS
+jupiter.x = 4.84143144246472090e+00
+jupiter.y = -1.16032004402742839e+00
+jupiter.z = -1.03622044471123109e-01
+jupiter.vx = 1.66007664274403694e-03 * DAYS_PER_YEAR
+jupiter.vy = 7.69901118419740425e-03 * DAYS_PER_YEAR
+jupiter.vz = -6.90460016972063023e-05 * DAYS_PER_YEAR
+jupiter.mass = 9.54791938424326609e-04 * SOLAR_MASS
+saturn.x = 8.34336671824457987e+00
+saturn.y = 4.12479856412430479e+00
+saturn.z = -4.03523417114321381e-01
+saturn.vx = -2.76742510726862411e-03 * DAYS_PER_YEAR
+saturn.vy = 4.99852801234917238e-03 * DAYS_PER_YEAR
+saturn.vz = 2.30417297573763929e-05 * DAYS_PER_YEAR
+saturn.mass = 2.85885980666130812e-04 * SOLAR_MASS
+uranus.x = 1.28943695621391310e+01
+uranus.y = -1.51111514016986312e+01
+uranus.z = -2.23307578892655734e-01
+uranus.vx = 2.96460137564761618e-03 * DAYS_PER_YEAR
+uranus.vy = 2.37847173959480950e-03 * DAYS_PER_YEAR
+uranus.vz = -2.96589568540237556e-05 * DAYS_PER_YEAR
+uranus.mass = 4.36624404335156298e-05 * SOLAR_MASS
+neptune.x = 1.53796971148509165e+01
+neptune.y = -2.59193146099879641e+01
+neptune.z = 1.79258772950371181e-01
+neptune.vx = 2.68067772490389322e-03 * DAYS_PER_YEAR
+neptune.vy = 1.62824170038242295e-03 * DAYS_PER_YEAR
+neptune.vz = -9.51592254519715870e-05 * DAYS_PER_YEAR
+neptune.mass = 5.15138902046611451e-05 * SOLAR_MASS
+
+local bodies = { sun, jupiter, saturn, uranus, neptune }
+
+local function advance(bodies, nbody, dt)
+    for i = 1, nbody do
+        local bi = bodies[i]
+        local bix, biy, biz, bimass = bi.x, bi.y, bi.z, bi.mass
+        local bivx, bivy, bivz = bi.vx, bi.vy, bi.vz
+        for j = i + 1, nbody do
+            local bj = bodies[j]
+            local dx, dy, dz = bix - bj.x, biy - bj.y, biz - bj.z
+            local dist2 = dx * dx + dy * dy + dz * dz
+            local mag = sqrt(dist2)
+            mag = dt / (mag * dist2)
+            local bm = bj.mass * mag
+            bivx = bivx - (dx * bm)
+            bivy = bivy - (dy * bm)
+            bivz = bivz - (dz * bm)
+            bm = bimass * mag
+            bj.vx = bj.vx + (dx * bm)
+            bj.vy = bj.vy + (dy * bm)
+            bj.vz = bj.vz + (dz * bm)
+        end
+        bi.vx = bivx
+        bi.vy = bivy
+        bi.vz = bivz
+        bi.x = bix + dt * bivx
+        bi.y = biy + dt * bivy
+        bi.z = biz + dt * bivz
+    end
+end
+
+local function energy(bodies, nbody)
+    local e = 0
+    for i = 1, nbody do
+        local bi = bodies[i]
+        local vx, vy, vz, bim = bi.vx, bi.vy, bi.vz, bi.mass
+        e = e + (0.5 * bim * (vx * vx + vy * vy + vz * vz))
+        for j = i + 1, nbody do
+            local bj = bodies[j]
+            local dx, dy, dz = bi.x - bj.x, bi.y - bj.y, bi.z - bj.z
+            local distance = sqrt(dx * dx + dy * dy + dz * dz)
+            e = e - ((bim * bj.mass) / distance)
+        end
+    end
+    return e
+end
+
+local function offsetMomentum(b, nbody)
+    local px, py, pz = 0, 0, 0
+    for i = 1, nbody do
+        local bi = b[i]
+        local bim = bi.mass
+        px = px + (bi.vx * bim)
+        py = py + (bi.vy * bim)
+        pz = pz + (bi.vz * bim)
+    end
+    b[1].vx = -px / SOLAR_MASS
+    b[1].vy = -py / SOLAR_MASS
+    b[1].vz = -pz / SOLAR_MASS
+end
+
+local N = tonumber(arg and arg[1]) or 1000
+local nbody = #bodies
+
+offsetMomentum(bodies, nbody)
+energy(bodies, nbody)
+for i = 1, N do advance(bodies, nbody, 0.01) end
+energy(bodies, nbody)

+ 115 - 2
sandbox/ConsoleApp1/test.lua

@@ -1,2 +1,115 @@
-local math = require "math"
-return math.pi;
+sun = {}
+jupiter = {}
+saturn = {}
+uranus = {}
+neptune = {}
+
+local sqrt = math.sqrt
+
+local PI = 3.141592653589793
+local SOLAR_MASS = 4 * PI * PI
+local DAYS_PER_YEAR = 365.24
+sun.x = 0.0
+sun.y = 0.0
+sun.z = 0.0
+sun.vx = 0.0
+sun.vy = 0.0
+sun.vz = 0.0
+sun.mass = SOLAR_MASS
+jupiter.x = 4.84143144246472090e+00
+jupiter.y = -1.16032004402742839e+00
+jupiter.z = -1.03622044471123109e-01
+jupiter.vx = 1.66007664274403694e-03 * DAYS_PER_YEAR
+jupiter.vy = 7.69901118419740425e-03 * DAYS_PER_YEAR
+jupiter.vz = -6.90460016972063023e-05 * DAYS_PER_YEAR
+jupiter.mass = 9.54791938424326609e-04 * SOLAR_MASS
+saturn.x = 8.34336671824457987e+00
+saturn.y = 4.12479856412430479e+00
+saturn.z = -4.03523417114321381e-01
+saturn.vx = -2.76742510726862411e-03 * DAYS_PER_YEAR
+saturn.vy = 4.99852801234917238e-03 * DAYS_PER_YEAR
+saturn.vz = 2.30417297573763929e-05 * DAYS_PER_YEAR
+saturn.mass = 2.85885980666130812e-04 * SOLAR_MASS
+uranus.x = 1.28943695621391310e+01
+uranus.y = -1.51111514016986312e+01
+uranus.z = -2.23307578892655734e-01
+uranus.vx = 2.96460137564761618e-03 * DAYS_PER_YEAR
+uranus.vy = 2.37847173959480950e-03 * DAYS_PER_YEAR
+uranus.vz = -2.96589568540237556e-05 * DAYS_PER_YEAR
+uranus.mass = 4.36624404335156298e-05 * SOLAR_MASS
+neptune.x = 1.53796971148509165e+01
+neptune.y = -2.59193146099879641e+01
+neptune.z = 1.79258772950371181e-01
+neptune.vx = 2.68067772490389322e-03 * DAYS_PER_YEAR
+neptune.vy = 1.62824170038242295e-03 * DAYS_PER_YEAR
+neptune.vz = -9.51592254519715870e-05 * DAYS_PER_YEAR
+neptune.mass = 5.15138902046611451e-05 * SOLAR_MASS
+
+local bodies = { sun, jupiter, saturn, uranus, neptune }
+
+local function advance(bodies, nbody, dt)
+    for i = 1, nbody do
+        local bi = bodies[i]
+        local bix, biy, biz, bimass = bi.x, bi.y, bi.z, bi.mass
+        local bivx, bivy, bivz = bi.vx, bi.vy, bi.vz
+        for j = i + 1, nbody do
+            local bj = bodies[j]
+            local dx, dy, dz = bix - bj.x, biy - bj.y, biz - bj.z
+            local dist2 = dx * dx + dy * dy + dz * dz
+            local mag = sqrt(dist2)
+            mag = dt / (mag * dist2)
+            local bm = bj.mass * mag
+            bivx = bivx - (dx * bm)
+            bivy = bivy - (dy * bm)
+            bivz = bivz - (dz * bm)
+            bm = bimass * mag
+            bj.vx = bj.vx + (dx * bm)
+            bj.vy = bj.vy + (dy * bm)
+            bj.vz = bj.vz + (dz * bm)
+        end
+        bi.vx = bivx
+        bi.vy = bivy
+        bi.vz = bivz
+        bi.x = bix + dt * bivx
+        bi.y = biy + dt * bivy
+        bi.z = biz + dt * bivz
+    end
+end
+
+local function energy(bodies, nbody)
+    local e = 0
+    for i = 1, nbody do
+        local bi = bodies[i]
+        local vx, vy, vz, bim = bi.vx, bi.vy, bi.vz, bi.mass
+        e = e + (0.5 * bim * (vx * vx + vy * vy + vz * vz))
+        for j = i + 1, nbody do
+            local bj = bodies[j]
+            local dx, dy, dz = bi.x - bj.x, bi.y - bj.y, bi.z - bj.z
+            local distance = sqrt(dx * dx + dy * dy + dz * dz)
+            e = e - ((bim * bj.mass) / distance)
+        end
+    end
+    return e
+end
+
+local function offsetMomentum(b, nbody)
+    local px, py, pz = 0, 0, 0
+    for i = 1, nbody do
+        local bi = b[i]
+        local bim = bi.mass
+        px = px + (bi.vx * bim)
+        py = py + (bi.vy * bim)
+        pz = pz + (bi.vz * bim)
+    end
+    b[1].vx = -px / SOLAR_MASS
+    b[1].vy = -py / SOLAR_MASS
+    b[1].vz = -pz / SOLAR_MASS
+end
+
+local N = tonumber(arg and arg[1]) or 1000
+local nbody = #bodies
+
+offsetMomentum(bodies, nbody)
+energy(bodies, nbody)
+for i = 1, N do advance(bodies, nbody, 0.01) end
+energy(bodies, nbody)

+ 144 - 38
src/Lua/CodeAnalysis/Compilation/LuaCompiler.cs

@@ -90,7 +90,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
     // identifier
     public bool VisitIdentifierNode(IdentifierNode node, ScopeCompilationContext context)
     {
-        LoadIdentifier(node.Name, context, node.Position, false);
+        GetOrLoadIdentifier(node.Name, context, node.Position, false);
         return true;
     }
 
@@ -136,7 +136,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             byte a;
             if (node.LeftNode is IdentifierNode leftIdentifier)
             {
-                a = LoadIdentifier(leftIdentifier.Name, context, leftIdentifier.Position, true);
+                a = GetOrLoadIdentifier(leftIdentifier.Name, context, leftIdentifier.Position, true);
             }
             else
             {
@@ -155,7 +155,8 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         }
         else
         {
-            (var b, var c) = GetBAndC(node, context);
+            var b = (ushort)GetRKIndex(node.LeftNode, context);
+            var c = (ushort)GetRKIndex(node.RightNode, context);
 
             switch (node.OperatorType)
             {
@@ -332,10 +333,10 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         node.TableNode.Accept(this, context);
 
         // load key
-        node.KeyNode.Accept(this, context);
+        var keyPosition = (ushort)GetRKIndex(node.KeyNode, context);
 
         // push interuction
-        context.PushInstruction(Instruction.GetTable(tablePosition, tablePosition, (ushort)(context.StackPosition - 1)), node.Position);
+        context.PushInstruction(Instruction.GetTable(tablePosition, tablePosition, keyPosition), node.Position);
         context.StackPosition = (byte)(tablePosition + 1);
 
         return true;
@@ -633,7 +634,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
 
         // assign global variable
         var first = node.MemberPath[0];
-        var tableIndex = LoadIdentifier(first.Name, context, first.Position, true);
+        var tableIndex = GetOrLoadIdentifier(first.Name, context, first.Position, true);
         
         for (int i = 1; i < node.MemberPath.Length - 1; i++)
         {
@@ -1004,7 +1005,7 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         return true;
     }
 
-    static byte LoadIdentifier(ReadOnlyMemory<char> name, ScopeCompilationContext context, SourcePosition sourcePosition, bool dontLoadLocalVariable)
+    static byte GetOrLoadIdentifier(ReadOnlyMemory<char> name, ScopeCompilationContext context, SourcePosition sourcePosition, bool dontLoadLocalVariable)
     {
         var p = context.StackPosition;
 
@@ -1045,6 +1046,124 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
         }
     }
 
+    uint GetRKIndex(ExpressionNode node, ScopeCompilationContext context)
+    {
+        if (node is IdentifierNode identifier)
+        {
+            return GetOrLoadIdentifier(identifier.Name, context, identifier.Position, true);
+        }
+        else if (TryGetConstant(node, context, out var constant))
+        {
+            return context.Function.GetConstantIndex(constant) + 256;
+        }
+        else
+        {
+            node.Accept(this, context);
+            return context.StackTopPosition;
+        }
+    }
+
+    static bool TryGetConstant(ExpressionNode node, ScopeCompilationContext context, out LuaValue value)
+    {
+        switch (node)
+        {
+            case NilLiteralNode:
+                value = LuaValue.Nil;
+                return true;
+            case BooleanLiteralNode booleanLiteral:
+                value = booleanLiteral.Value;
+                return true;
+            case NumericLiteralNode numericLiteral:
+                value = numericLiteral.Value;
+                return true;
+            case StringLiteralNode stringLiteral:
+                if (stringLiteral.IsShortLiteral)
+                {
+                    if (!StringHelper.TryFromStringLiteral(stringLiteral.Text.Span, out var str))
+                    {
+                        throw new LuaParseException(context.Function.ChunkName, stringLiteral.Position, $"invalid escape sequence near '{stringLiteral.Text}'");
+                    }
+
+                    value = str;
+                }
+                else
+                {
+                    value = stringLiteral.Text.ToString();
+                }
+                return true;
+            case UnaryExpressionNode unaryExpression:
+                if (TryGetConstant(unaryExpression.Node, context, out var unaryNodeValue))
+                {
+                    switch (unaryExpression.Operator)
+                    {
+                        case UnaryOperator.Negate:
+                            if (unaryNodeValue.TryRead<double>(out var d1))
+                            {
+                                value = -d1;
+                                return true;
+                            }
+                            break;
+                        case UnaryOperator.Not:
+                            if (unaryNodeValue.TryRead<bool>(out var b))
+                            {
+                                value = !b;
+                                return true;
+                            }
+                            break;
+                    }
+                }
+                break;
+            case BinaryExpressionNode binaryExpression:
+                if (TryGetConstant(binaryExpression.LeftNode, context, out var leftValue) &&
+                    TryGetConstant(binaryExpression.RightNode, context, out var rightValue))
+                {
+                    switch (binaryExpression.OperatorType)
+                    {
+                        case BinaryOperator.Addition:
+                            {
+                                if (leftValue.TryRead<double>(out var d1) && rightValue.TryRead<double>(out var d2))
+                                {
+                                    value = d1 + d2;
+                                    return true;
+                                }
+                            }
+                            break;
+                        case BinaryOperator.Subtraction:
+                            {
+                                if (leftValue.TryRead<double>(out var d1) && rightValue.TryRead<double>(out var d2))
+                                {
+                                    value = d1 - d2;
+                                    return true;
+                                }
+                            }
+                            break;
+                        case BinaryOperator.Multiplication:
+                            {
+                                if (leftValue.TryRead<double>(out var d1) && rightValue.TryRead<double>(out var d2))
+                                {
+                                    value = d1 * d2;
+                                    return true;
+                                }
+                            }
+                            break;
+                        case BinaryOperator.Division:
+                            {
+                                if (leftValue.TryRead<double>(out var d1) && rightValue.TryRead<double>(out var d2) && d2 != 0)
+                                {
+                                    value = d1 / d2;
+                                    return true;
+                                }
+                            }
+                            break;
+                    }
+                }
+                break;
+        }
+
+        value = default;
+        return false;
+    }
+
     static bool IsFixedNumberOfReturnValues(ExpressionNode node)
     {
         return node is not (CallFunctionExpressionNode or CallTableMethodExpressionNode or VariableArgumentsExpressionNode);
@@ -1064,37 +1183,43 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             {
                 case BinaryOperator.Equality:
                     {
-                        (var b, var c) = GetBAndC(binaryExpression, context);
+                        var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
+                        var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
                         context.PushInstruction(Instruction.Eq(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position);
                         return;
                     }
                 case BinaryOperator.Inequality:
                     {
-                        (var b, var c) = GetBAndC(binaryExpression, context);
+                        var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
+                        var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
                         context.PushInstruction(Instruction.Eq(falseIsSkip ? (byte)1 : (byte)0, b, c), node.Position);
                         return;
                     }
                 case BinaryOperator.LessThan:
                     {
-                        (var b, var c) = GetBAndC(binaryExpression, context);
+                        var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
+                        var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
                         context.PushInstruction(Instruction.Lt(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position);
                         return;
                     }
                 case BinaryOperator.LessThanOrEqual:
                     {
-                        (var b, var c) = GetBAndC(binaryExpression, context);
+                        var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
+                        var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
                         context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)0 : (byte)1, b, c), node.Position);
                         return;
                     }
                 case BinaryOperator.GreaterThan:
                     {
-                        (var b, var c) = GetBAndC(binaryExpression, context);
+                        var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
+                        var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
                         context.PushInstruction(Instruction.Lt(falseIsSkip ? (byte)0 : (byte)1, c, b), node.Position);
                         return;
                     }
                 case BinaryOperator.GreaterThanOrEqual:
                     {
-                        (var b, var c) = GetBAndC(binaryExpression, context);
+                        var b = (ushort)GetRKIndex(binaryExpression.LeftNode, context);
+                        var c = (ushort)GetRKIndex(binaryExpression.RightNode, context);
                         context.PushInstruction(Instruction.Le(falseIsSkip ? (byte)0 : (byte)1, c, b), node.Position);
                         return;
                     }
@@ -1129,6 +1254,12 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
                 CompileVariableArgumentsExpression(varArg, context, resultCount);
                 isLastFunction = isLast;
             }
+            else if (TryGetConstant(expression, context, out var constant))
+            {
+                var index = context.Function.GetConstantIndex(constant);
+                context.PushInstruction(Instruction.LoadK(context.StackPosition, index), expression.Position, true);
+                isLastFunction = false;
+            }
             else
             {
                 expression.Accept(this, context);
@@ -1144,29 +1275,4 @@ public sealed class LuaCompiler : ISyntaxNodeVisitor<ScopeCompilationContext, bo
             context.StackPosition = (byte)(context.StackPosition + varCount);
         }
     }
-
-    (byte b, byte c) GetBAndC(BinaryExpressionNode node, ScopeCompilationContext context)
-    {
-        byte b, c;
-        if (node.LeftNode is IdentifierNode leftIdentifier)
-        {
-            b = LoadIdentifier(leftIdentifier.Name, context, leftIdentifier.Position, true);
-        }
-        else
-        {
-            node.LeftNode.Accept(this, context);
-            b = (byte)(context.StackPosition - 1);
-        }
-        if (node.RightNode is IdentifierNode rightIdentifier)
-        {
-            c = LoadIdentifier(rightIdentifier.Name, context, rightIdentifier.Position, true);
-        }
-        else
-        {
-            node.RightNode.Accept(this, context);
-            c = (byte)(context.StackPosition - 1);
-        }
-
-        return (b, c);
-    }
 }

+ 30 - 0
src/Lua/Internal/MemoryMarshalEx.cs

@@ -0,0 +1,30 @@
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Lua.Internal;
+
+internal static class MemoryMarshalEx
+{
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static ref T UnsafeElementAt<T>(T[] array, int index)
+    {
+#if NET6_0_OR_GREATER
+        return ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index);
+#else
+        ref var reference = ref MemoryMarshal.GetReference(array.AsSpan());
+        return ref Unsafe.Add(ref reference, index);
+#endif
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static ref T UnsafeElementAt<T>(Span<T> array, int index)
+    {
+        return ref Unsafe.Add(ref MemoryMarshal.GetReference(array), index);
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static ref T UnsafeElementAt<T>(ReadOnlySpan<T> array, int index)
+    {
+        return ref Unsafe.Add(ref MemoryMarshal.GetReference(array), index);
+    }
+}

+ 7 - 0
src/Lua/LuaFunction.cs

@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using Lua.Runtime;
 
 namespace Lua;
@@ -30,4 +31,10 @@ public abstract partial class LuaFunction
     }
 
     protected abstract ValueTask<int> InvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken);
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal ValueTask<int> InternalInvokeAsyncCore(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
+    {
+        return InvokeAsyncCore(context, buffer, cancellationToken);
+    }
 }

+ 2 - 0
src/Lua/LuaFunctionExecutionContext.cs

@@ -1,8 +1,10 @@
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using Lua.CodeAnalysis;
 
 namespace Lua;
 
+[StructLayout(LayoutKind.Auto)]
 public readonly record struct LuaFunctionExecutionContext
 {
     public required LuaState State { get; init; }

+ 9 - 6
src/Lua/LuaState.cs

@@ -1,4 +1,5 @@
 using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
 using Lua.Internal;
 using Lua.Loaders;
 using Lua.Runtime;
@@ -70,8 +71,8 @@ public sealed class LuaState
                 ArgumentCount = 0,
                 FrameBase = 0,
                 SourcePosition = null,
-                RootChunkName = chunk.Name ?? DefaultChunkName,
-                ChunkName = chunk.Name ?? DefaultChunkName,
+                RootChunkName = chunk.Name,
+                ChunkName = chunk.Name,
             }, buffer, cancellationToken);
         }
         finally
@@ -97,6 +98,7 @@ public sealed class LuaState
         };
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal bool TryGetMetatable(LuaValue value, [NotNullWhen(true)] out LuaTable? result)
     {
         result = value.Type switch
@@ -107,14 +109,15 @@ public sealed class LuaState
             LuaValueType.Number => numberMetatable,
             LuaValueType.Function => functionMetatable,
             LuaValueType.Thread => threadMetatable,
-            LuaValueType.UserData => value.Read<LuaUserData>().Metatable,
-            LuaValueType.Table => value.Read<LuaTable>().Metatable,
+            LuaValueType.UserData => value.UnsafeRead<LuaUserData>().Metatable,
+            LuaValueType.Table => value.UnsafeRead<LuaTable>().Metatable,
             _ => null
         };
 
         return result != null;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal void SetMetatable(LuaValue value, LuaTable metatable)
     {
         switch (value.Type)
@@ -138,10 +141,10 @@ public sealed class LuaState
                 threadMetatable = metatable;
                 break;
             case LuaValueType.UserData:
-                value.Read<LuaUserData>().Metatable = metatable;
+                value.UnsafeRead<LuaUserData>().Metatable = metatable;
                 break;
             case LuaValueType.Table:
-                value.Read<LuaTable>().Metatable = metatable;
+                value.UnsafeRead<LuaTable>().Metatable = metatable;
                 break;
         }
     }

+ 23 - 12
src/Lua/LuaTable.cs

@@ -21,30 +21,29 @@ public sealed class LuaTable
 
     public LuaValue this[LuaValue key]
     {
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         get
         {
-            if (key.Type is LuaValueType.Nil)
-            {
-                throw new ArgumentException("table index is nil");
-            }
+            if (key.Type is LuaValueType.Nil) ThrowIndexIsNil();
 
             if (TryGetInteger(key, out var index))
             {
                 if (index > 0 && index <= array.Length)
                 {
                     // Arrays in Lua are 1-origin...
-                    return array[index - 1];
+                    return MemoryMarshalEx.UnsafeElementAt(array, index - 1);
                 }
             }
 
             if (dictionary.TryGetValue(key, out var value)) return value;
             return LuaValue.Nil;
         }
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         set
         {
-            if (key.Type is LuaValueType.Number && double.IsNaN(key.Read<double>()))
+            if (key.Type is LuaValueType.Number && double.IsNaN(key.UnsafeRead<double>()))
             {
-                throw new ArgumentException("table index is NaN");
+                ThrowIndexIsNaN();
             }
 
             if (TryGetInteger(key, out var index))
@@ -52,7 +51,7 @@ public sealed class LuaTable
                 if (0 < index && index <= Math.Max(array.Length * 2, 8))
                 {
                     EnsureArrayCapacity(index);
-                    array[index - 1] = value;
+                    MemoryMarshalEx.UnsafeElementAt(array, index - 1) = value;
                     return;
                 }
             }
@@ -96,7 +95,7 @@ public sealed class LuaTable
         {
             if (index > 0 && index <= array.Length)
             {
-                value = array[index - 1];
+                value = MemoryMarshalEx.UnsafeElementAt(array, index - 1);
                 return value.Type is not LuaValueType.Nil;
             }
         }
@@ -113,7 +112,8 @@ public sealed class LuaTable
 
         if (TryGetInteger(key, out var index))
         {
-            return index > 0 && index <= array.Length && array[index - 1].Type != LuaValueType.Nil;
+            return index > 0 && index <= array.Length &&
+                MemoryMarshalEx.UnsafeElementAt(array, index - 1).Type != LuaValueType.Nil;
         }
 
         return dictionary.TryGetValue(key, out var value) && value.Type is not LuaValueType.Nil;
@@ -127,13 +127,14 @@ public sealed class LuaTable
         }
 
         var arrayIndex = index - 1;
-        var value = array[arrayIndex];
+        var value = MemoryMarshalEx.UnsafeElementAt(array, arrayIndex);
 
         if (arrayIndex < array.Length - 1)
         {
             array.AsSpan(arrayIndex + 1).CopyTo(array.AsSpan(arrayIndex));
         }
-        array[^1] = default;
+        
+        MemoryMarshalEx.UnsafeElementAt(array, array.Length - 1) = default;
 
         return value;
     }
@@ -275,4 +276,14 @@ public sealed class LuaTable
         integer = default;
         return false;
     }
+
+    static void ThrowIndexIsNil()
+    {
+        throw new ArgumentException("the table index is nil");
+    }
+
+    static void ThrowIndexIsNaN()
+    {
+        throw new ArgumentException("the table index is NaN");
+    }
 }

+ 42 - 29
src/Lua/LuaValue.cs

@@ -75,8 +75,8 @@ public readonly struct LuaValue : IEquatable<LuaValue>
             case LuaValueType.String:
                 if (t == typeof(string))
                 {
-                    var v = (string)referenceValue!;
-                    result = Unsafe.As<string, T>(ref v);
+                    var v = referenceValue!;
+                    result = Unsafe.As<object, T>(ref v);
                     return true;
                 }
                 else if (t == typeof(double))
@@ -137,8 +137,8 @@ public readonly struct LuaValue : IEquatable<LuaValue>
             case LuaValueType.Function:
                 if (t == typeof(LuaFunction) || t.IsSubclassOf(typeof(LuaFunction)))
                 {
-                    var v = (LuaFunction)referenceValue!;
-                    result = Unsafe.As<LuaFunction, T>(ref v);
+                    var v = referenceValue!;
+                    result = Unsafe.As<object, T>(ref v);
                     return true;
                 }
                 else if (t == typeof(object))
@@ -153,8 +153,8 @@ public readonly struct LuaValue : IEquatable<LuaValue>
             case LuaValueType.Thread:
                 if (t == typeof(LuaThread))
                 {
-                    var v = (LuaThread)referenceValue!;
-                    result = Unsafe.As<LuaThread, T>(ref v);
+                    var v = referenceValue!;
+                    result = Unsafe.As<object, T>(ref v);
                     return true;
                 }
                 else if (t == typeof(object))
@@ -169,8 +169,8 @@ public readonly struct LuaValue : IEquatable<LuaValue>
             case LuaValueType.UserData:
                 if (t == typeof(LuaUserData) || t.IsSubclassOf(typeof(LuaUserData)))
                 {
-                    var v = (LuaUserData)referenceValue!;
-                    result = Unsafe.As<LuaUserData, T>(ref v);
+                    var v = referenceValue!;
+                    result = Unsafe.As<object, T>(ref v);
                     return true;
                 }
                 else if (t == typeof(object))
@@ -185,8 +185,8 @@ public readonly struct LuaValue : IEquatable<LuaValue>
             case LuaValueType.Table:
                 if (t == typeof(LuaTable))
                 {
-                    var v = (LuaTable)referenceValue!;
-                    result = Unsafe.As<LuaTable, T>(ref v);
+                    var v = referenceValue!;
+                    result = Unsafe.As<object, T>(ref v);
                     return true;
                 }
                 else if (t == typeof(object))
@@ -210,6 +210,35 @@ public readonly struct LuaValue : IEquatable<LuaValue>
         return result;
     }
 
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal T UnsafeRead<T>()
+    {
+        switch (type)
+        {
+            case LuaValueType.Boolean:
+                {
+                    var v = value == 1;
+                    return Unsafe.As<bool, T>(ref v);
+                }
+            case LuaValueType.Number:
+                {
+                    var v = value;
+                    return Unsafe.As<double, T>(ref v);
+                }
+            case LuaValueType.String:
+            case LuaValueType.Thread:
+            case LuaValueType.Function:
+            case LuaValueType.Table:
+            case LuaValueType.UserData:
+                {
+                    var v = referenceValue!;
+                    return Unsafe.As<object, T>(ref v);
+                }
+        }
+
+        return default!;
+    }
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public bool ToBoolean()
     {
@@ -297,17 +326,7 @@ public readonly struct LuaValue : IEquatable<LuaValue>
 
     public override int GetHashCode()
     {
-        var valueHash = type switch
-        {
-            LuaValueType.Nil => 0,
-            LuaValueType.Boolean => Read<bool>().GetHashCode(),
-            LuaValueType.String => Read<string>().GetHashCode(),
-            LuaValueType.Number => Read<double>().GetHashCode(),
-            LuaValueType.Function or LuaValueType.Thread or LuaValueType.Table or LuaValueType.UserData => referenceValue!.GetHashCode(),
-            _ => 0,
-        };
-
-        return HashCode.Combine(type, valueHash);
+        return HashCode.Combine(type, value, referenceValue);
     }
 
     public bool Equals(LuaValue other)
@@ -317,14 +336,8 @@ public readonly struct LuaValue : IEquatable<LuaValue>
         return type switch
         {
             LuaValueType.Nil => true,
-            LuaValueType.Boolean => Read<bool>().Equals(other.Read<bool>()),
-            LuaValueType.String => Read<string>().Equals(other.Read<string>()),
-            LuaValueType.Number => Read<double>() == other.Read<double>(),
-            LuaValueType.Function => Read<LuaFunction>().Equals(other.Read<LuaFunction>()),
-            LuaValueType.Thread => Read<LuaThread>().Equals(other.Read<LuaThread>()),
-            LuaValueType.Table => Read<LuaTable>().Equals(other.Read<LuaTable>()),
-            LuaValueType.UserData => Read<LuaUserData>().Equals(other.Read<LuaUserData>()),
-            _ => false,
+            LuaValueType.Boolean or LuaValueType.Number => other.value == value,
+            _ => other.referenceValue!.Equals(referenceValue)
         };
     }
 

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

@@ -1,7 +1,9 @@
+using System.Runtime.InteropServices;
 using Lua.CodeAnalysis;
 
 namespace Lua.Runtime;
 
+[StructLayout(LayoutKind.Auto)]
 public record struct CallStackFrame
 {
     public required int Base;

+ 5 - 12
src/Lua/Runtime/LuaStack.cs

@@ -1,5 +1,5 @@
 using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
+using Lua.Internal;
 
 namespace Lua.Runtime;
 
@@ -34,7 +34,7 @@ public class LuaStack(int initialSize = 256)
     public void Push(LuaValue value)
     {
         EnsureCapacity(top + 1);
-        array[top] = value;
+        UnsafeGet(top) = value;
         top++;
     }
 
@@ -43,8 +43,8 @@ public class LuaStack(int initialSize = 256)
     {
         if (top == 0) ThrowEmptyStack();
         top--;
-        var item = array[top];
-        array[top] = default;
+        var item = UnsafeGet(top);
+        UnsafeGet(top) = default;
         return item;
     }
 
@@ -91,14 +91,7 @@ public class LuaStack(int initialSize = 256)
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public ref LuaValue UnsafeGet(int index)
     {
-        // if (index < 0 || index >= array.Length) throw new IndexOutOfRangeException();
-
-#if NET6_0_OR_GREATER
-        return ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index);
-#else
-        ref var reference = ref MemoryMarshal.GetReference(array.AsSpan());
-        return ref Unsafe.Add(ref reference, index);
-#endif
+        return ref MemoryMarshalEx.UnsafeElementAt(array, index);
     }
 
     static void ThrowEmptyStack()

+ 0 - 18
src/Lua/Runtime/LuaValueRuntimeExtensions.cs

@@ -1,4 +1,3 @@
-using System.Buffers;
 using System.Runtime.CompilerServices;
 
 namespace Lua.Runtime;
@@ -20,21 +19,4 @@ internal static class LuaRuntimeExtensions
             ? argumentCount - luaClosure.Proto.ParameterCount
             : 0;
     }
-
-#if NET6_0_OR_GREATER
-    [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]
-#endif
-    public static async ValueTask<int> InvokeAsync(this LuaFunction function, LuaFunctionExecutionContext context, CancellationToken cancellationToken)
-    {
-        var buffer = ArrayPool<LuaValue>.Shared.Rent(1024);
-        buffer.AsSpan().Clear();
-        try
-        {
-            return await function.InvokeAsync(context, buffer, cancellationToken);
-        }
-        finally
-        {
-            ArrayPool<LuaValue>.Shared.Return(buffer);
-        }
-    }
 }

+ 236 - 249
src/Lua/Runtime/LuaVirtualMachine.cs

@@ -11,6 +11,9 @@ public static partial class LuaVirtualMachine
         var thread = state.CurrentThread;
         var stack = thread.Stack;
         var chunk = closure.Proto;
+        var rootChunk = chunk.GetRoot();
+
+        var resultBuffer = ArrayPool<LuaValue>.Shared.Rent(1024);
 
         try
         {
@@ -49,7 +52,7 @@ public static partial class LuaVirtualMachine
                     case OpCode.GetUpVal:
                         {
                             stack.EnsureCapacity(RA + 1);
-                            var upValue = closure.UpValues[instruction.B];
+                            var upValue = MemoryMarshalEx.UnsafeElementAt(closure.UpValues, instruction.B);
                             stack.UnsafeGet(RA) = upValue.GetValue();
                             stack.NotifyTop(RA + 1);
                             break;
@@ -58,9 +61,10 @@ public static partial class LuaVirtualMachine
                         {
                             stack.EnsureCapacity(RA + 1);
                             var vc = RK(stack, chunk, instruction.C, frame.Base);
-                            var upValue = closure.UpValues[instruction.B];
+                            var upValue = MemoryMarshalEx.UnsafeElementAt(closure.UpValues, instruction.B);
                             var table = upValue.GetValue();
-                            var value = await GetTableValue(state, chunk, pc, table, vc, cancellationToken);
+                            await GetTableValue(state, thread, chunk, rootChunk, pc, table, vc, resultBuffer.AsMemory(), cancellationToken);
+                            var value = resultBuffer[0];
                             stack.UnsafeGet(RA) = value;
                             stack.NotifyTop(RA + 1);
                             break;
@@ -70,7 +74,8 @@ public static partial class LuaVirtualMachine
                             stack.EnsureCapacity(RA + 1);
                             var table = stack.UnsafeGet(RB);
                             var vc = RK(stack, chunk, instruction.C, frame.Base);
-                            var value = await GetTableValue(state, chunk, pc, table, vc, cancellationToken);
+                            await GetTableValue(state, thread, chunk, rootChunk, pc, table, vc, resultBuffer.AsMemory(), cancellationToken);
+                            var value = resultBuffer[0];
                             stack.UnsafeGet(RA) = value;
                             stack.NotifyTop(RA + 1);
                         }
@@ -80,14 +85,14 @@ public static partial class LuaVirtualMachine
                             var vb = RK(stack, chunk, instruction.B, frame.Base);
                             var vc = RK(stack, chunk, instruction.C, frame.Base);
 
-                            var upValue = closure.UpValues[instruction.A];
+                            var upValue = MemoryMarshalEx.UnsafeElementAt(closure.UpValues, instruction.A);
                             var table = upValue.GetValue();
-                            await SetTableValue(state, chunk, pc, table, vb, vc, cancellationToken);
+                            await SetTableValue(state, thread, chunk, rootChunk, pc, table, vb, vc, resultBuffer.AsMemory(), cancellationToken);
                             break;
                         }
                     case OpCode.SetUpVal:
                         {
-                            var upValue = closure.UpValues[instruction.B];
+                            var upValue = MemoryMarshalEx.UnsafeElementAt(closure.UpValues, instruction.B);
                             upValue.SetValue(stack.UnsafeGet(RA));
                             break;
                         }
@@ -96,7 +101,7 @@ public static partial class LuaVirtualMachine
                             var table = stack.UnsafeGet(RA);
                             var vb = RK(stack, chunk, instruction.B, frame.Base);
                             var vc = RK(stack, chunk, instruction.C, frame.Base);
-                            await SetTableValue(state, chunk, pc, table, vb, vc, cancellationToken);
+                            await SetTableValue(state, thread, chunk, rootChunk, pc, table, vb, vc, resultBuffer.AsMemory(), cancellationToken);
                         }
                         break;
                     case OpCode.NewTable:
@@ -109,7 +114,9 @@ public static partial class LuaVirtualMachine
                             stack.EnsureCapacity(RA + 2);
                             var table = stack.UnsafeGet(RB);
                             var vc = RK(stack, chunk, instruction.C, frame.Base);
-                            var value = await GetTableValue(state, chunk, pc, table, vc, cancellationToken);
+
+                            await GetTableValue(state, thread, chunk, rootChunk, pc, table, vc, resultBuffer.AsMemory(), cancellationToken);
+                            var value = resultBuffer[0];
 
                             stack.UnsafeGet(RA + 1) = table;
                             stack.UnsafeGet(RA) = value;
@@ -137,19 +144,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                using var methodBuffer = new PooledArray<LuaValue>(1024);
                                 await func.InvokeAsync(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = 2,
                                     FrameBase = stack.Count - 2,
-                                    SourcePosition = chunk.SourcePositions[pc],
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                     ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, methodBuffer.AsMemory(), cancellationToken);
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.UnsafeGet(RA) = methodBuffer[0];
+                                stack.UnsafeGet(RA) = resultBuffer[0];
                             }
                             else
                             {
@@ -180,19 +186,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                using var methodBuffer = new PooledArray<LuaValue>(1024);
                                 await func.InvokeAsync(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = 2,
                                     FrameBase = stack.Count - 2,
-                                    SourcePosition = chunk.SourcePositions[pc],
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                     ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, methodBuffer.AsMemory(), cancellationToken);
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.UnsafeGet(RA) = methodBuffer[0];
+                                stack.UnsafeGet(RA) = resultBuffer[0];
                             }
                             else
                             {
@@ -223,19 +228,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                using var methodBuffer = new PooledArray<LuaValue>(1024);
                                 await func.InvokeAsync(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = 2,
                                     FrameBase = stack.Count - 2,
-                                    SourcePosition = chunk.SourcePositions[pc],
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                     ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, methodBuffer.AsMemory(), cancellationToken);
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.UnsafeGet(RA) = methodBuffer[0];
+                                stack.UnsafeGet(RA) = resultBuffer[0];
                             }
                             else
                             {
@@ -266,19 +270,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                using var methodBuffer = new PooledArray<LuaValue>(1024);
                                 await func.InvokeAsync(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = 2,
                                     FrameBase = stack.Count - 2,
-                                    SourcePosition = chunk.SourcePositions[pc],
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                     ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, methodBuffer.AsMemory(), cancellationToken);
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.UnsafeGet(RA) = methodBuffer[0];
+                                stack.UnsafeGet(RA) = resultBuffer[0];
                             }
                             else
                             {
@@ -314,19 +317,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                using var methodBuffer = new PooledArray<LuaValue>(1024);
                                 await func.InvokeAsync(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = 2,
                                     FrameBase = stack.Count - 2,
-                                    SourcePosition = chunk.SourcePositions[pc],
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                     ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, methodBuffer.AsMemory(), cancellationToken);
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.UnsafeGet(RA) = methodBuffer[0];
+                                stack.UnsafeGet(RA) = resultBuffer[0];
                             }
                             else
                             {
@@ -357,19 +359,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                using var methodBuffer = new PooledArray<LuaValue>(1024);
                                 await func.InvokeAsync(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = 2,
                                     FrameBase = stack.Count - 2,
-                                    SourcePosition = chunk.SourcePositions[pc],
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                     ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, methodBuffer.AsMemory(), cancellationToken);
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.UnsafeGet(RA) = methodBuffer[0];
+                                stack.UnsafeGet(RA) = resultBuffer[0];
                             }
                             else
                             {
@@ -398,19 +399,18 @@ public static partial class LuaVirtualMachine
 
                                 stack.Push(vb);
 
-                                using var methodBuffer = new PooledArray<LuaValue>(1024);
                                 await func.InvokeAsync(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = 1,
                                     FrameBase = stack.Count - 1,
-                                    SourcePosition = chunk.SourcePositions[pc],
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                     ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, methodBuffer.AsMemory(), cancellationToken);
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.UnsafeGet(RA) = methodBuffer[0];
+                                stack.UnsafeGet(RA) = resultBuffer[0];
                             }
                             else
                             {
@@ -446,19 +446,18 @@ public static partial class LuaVirtualMachine
 
                                 stack.Push(vb);
 
-                                using var methodBuffer = new PooledArray<LuaValue>(1024);
                                 await func.InvokeAsync(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = 1,
                                     FrameBase = stack.Count - 1,
-                                    SourcePosition = chunk.SourcePositions[pc],
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                     ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, methodBuffer.AsMemory(), cancellationToken);
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.UnsafeGet(RA) = methodBuffer[0];
+                                stack.UnsafeGet(RA) = resultBuffer[0];
                             }
                             else if (vb.TryRead<LuaTable>(out var table))
                             {
@@ -507,19 +506,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                using var methodBuffer = new PooledArray<LuaValue>(1024);
                                 await func.InvokeAsync(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = 2,
                                     FrameBase = stack.Count - 2,
-                                    SourcePosition = chunk.SourcePositions[pc],
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                     ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, methodBuffer.AsMemory(), cancellationToken);
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.UnsafeGet(RA) = methodBuffer[0];
+                                stack.UnsafeGet(RA) = resultBuffer[0];
                             }
                             else
                             {
@@ -552,27 +550,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                var methodBuffer = ArrayPool<LuaValue>.Shared.Rent(1);
-                                methodBuffer.AsSpan().Clear();
-                                try
-                                {
-                                    await func.InvokeAsync(new()
-                                    {
-                                        State = state,
-                                        Thread = thread,
-                                        ArgumentCount = 2,
-                                        FrameBase = stack.Count - 2,
-                                        SourcePosition = chunk.SourcePositions[pc],
-                                        ChunkName = chunk.Name,
-                                        RootChunkName = chunk.GetRoot().Name,
-                                    }, methodBuffer, cancellationToken);
-
-                                    compareResult = methodBuffer[0].ToBoolean();
-                                }
-                                finally
+                                await func.InvokeAsync(new()
                                 {
-                                    ArrayPool<LuaValue>.Shared.Return(methodBuffer);
-                                }
+                                    State = state,
+                                    Thread = thread,
+                                    ArgumentCount = 2,
+                                    FrameBase = stack.Count - 2,
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
+                                    ChunkName = chunk.Name,
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
+
+                                compareResult = resultBuffer[0].ToBoolean();
                             }
 
                             if (compareResult != (instruction.A == 1))
@@ -605,27 +594,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                var methodBuffer = ArrayPool<LuaValue>.Shared.Rent(1);
-                                methodBuffer.AsSpan().Clear();
-                                try
-                                {
-                                    await func.InvokeAsync(new()
-                                    {
-                                        State = state,
-                                        Thread = thread,
-                                        ArgumentCount = 2,
-                                        FrameBase = stack.Count - 2,
-                                        SourcePosition = chunk.SourcePositions[pc],
-                                        ChunkName = chunk.Name,
-                                        RootChunkName = chunk.GetRoot().Name,
-                                    }, methodBuffer, cancellationToken);
-
-                                    compareResult = methodBuffer[0].ToBoolean();
-                                }
-                                finally
+                                await func.InvokeAsync(new()
                                 {
-                                    ArrayPool<LuaValue>.Shared.Return(methodBuffer);
-                                }
+                                    State = state,
+                                    Thread = thread,
+                                    ArgumentCount = 2,
+                                    FrameBase = stack.Count - 2,
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
+                                    ChunkName = chunk.Name,
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
+
+                                compareResult = resultBuffer[0].ToBoolean();
                             }
                             else
                             {
@@ -662,27 +642,18 @@ public static partial class LuaVirtualMachine
                                 stack.Push(vb);
                                 stack.Push(vc);
 
-                                var methodBuffer = ArrayPool<LuaValue>.Shared.Rent(1);
-                                methodBuffer.AsSpan().Clear();
-                                try
-                                {
-                                    await func.InvokeAsync(new()
-                                    {
-                                        State = state,
-                                        Thread = thread,
-                                        ArgumentCount = 2,
-                                        FrameBase = stack.Count - 2,
-                                        SourcePosition = chunk.SourcePositions[pc],
-                                        ChunkName = chunk.Name,
-                                        RootChunkName = chunk.GetRoot().Name,
-                                    }, methodBuffer, cancellationToken);
-
-                                    compareResult = methodBuffer[0].ToBoolean();
-                                }
-                                finally
+                                await func.InvokeAsync(new()
                                 {
-                                    ArrayPool<LuaValue>.Shared.Return(methodBuffer);
-                                }
+                                    State = state,
+                                    Thread = thread,
+                                    ArgumentCount = 2,
+                                    FrameBase = stack.Count - 2,
+                                    SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
+                                    ChunkName = chunk.Name,
+                                    RootChunkName = rootChunk.Name,
+                                }, resultBuffer.AsMemory(), cancellationToken);
+
+                                compareResult = resultBuffer[0].ToBoolean();
                             }
                             else
                             {
@@ -730,45 +701,62 @@ public static partial class LuaVirtualMachine
                                 }
                             }
 
-                            (var newBase, var argumentCount) = PrepareForFunctionCall(thread, func, instruction, RA, false);
+                            (var newBase, var argumentCount) = PrepareForFunctionCall(thread, func, instruction, RA, resultBuffer.AsSpan(), false);
+                            
+                            var callPosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc);
+                            var chunkName = chunk.Name ?? LuaState.DefaultChunkName;
+                            var rootChunkName = rootChunk.Name ?? LuaState.DefaultChunkName;
 
-                            var resultBuffer = ArrayPool<LuaValue>.Shared.Rent(1024);
-                            resultBuffer.AsSpan().Clear();
+                            thread.PushCallStackFrame(new CallStackFrame
+                            {
+                                Base = newBase,
+                                CallPosition = callPosition,
+                                ChunkName = chunkName,
+                                RootChunkName = rootChunkName,
+                                VariableArgumentCount = func is Closure cl ? Math.Max(argumentCount - cl.Proto.ParameterCount, 0) : 0,
+                                Function = func,
+                            });
+
+                            int rawResultCount;
                             try
                             {
-                                var resultCount = await func.InvokeAsync(new()
+                                rawResultCount = await func.InternalInvokeAsyncCore(new()
                                 {
                                     State = state,
                                     Thread = thread,
                                     ArgumentCount = argumentCount,
                                     FrameBase = newBase,
-                                    SourcePosition = chunk.SourcePositions[pc],
-                                    ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
+                                    SourcePosition = callPosition,
+                                    ChunkName = chunkName,
+                                    RootChunkName = rootChunkName,
                                 }, resultBuffer.AsMemory(), cancellationToken);
+                            }
+                            finally
+                            {
+                                thread.PopCallStackFrame();
+                            }
 
-                                if (instruction.C != 0)
-                                {
-                                    resultCount = instruction.C - 1;
-                                }
+                            var resultCount = rawResultCount;
 
-                                if (resultCount == 0)
-                                {
-                                    stack.Pop();
-                                }
-                                else
-                                {
-                                    stack.EnsureCapacity(RA + resultCount);
-                                    for (int i = 0; i < resultCount; i++)
-                                    {
-                                        stack.UnsafeGet(RA + i) = resultBuffer[i];
-                                    }
-                                    stack.NotifyTop(RA + resultCount);
-                                }
+                            if (instruction.C != 0)
+                            {
+                                resultCount = instruction.C - 1;
                             }
-                            finally
+
+                            if (resultCount == 0)
+                            {
+                                stack.Pop();
+                            }
+                            else
                             {
-                                ArrayPool<LuaValue>.Shared.Return(resultBuffer);
+                                stack.EnsureCapacity(RA + resultCount);
+                                for (int i = 0; i < resultCount; i++)
+                                {
+                                    stack.UnsafeGet(RA + i) = i >= rawResultCount
+                                        ? LuaValue.Nil
+                                        : resultBuffer[i];
+                                }
+                                stack.NotifyTop(RA + resultCount);
                             }
                         }
                         break;
@@ -785,7 +773,7 @@ public static partial class LuaVirtualMachine
                                 }
                             }
 
-                            (var newBase, var argumentCount) = PrepareForFunctionCall(thread, func, instruction, RA, true);
+                            (var newBase, var argumentCount) = PrepareForFunctionCall(thread, func, instruction, RA, resultBuffer.AsSpan(), true);
 
                             return await func.InvokeAsync(new()
                             {
@@ -793,9 +781,9 @@ public static partial class LuaVirtualMachine
                                 Thread = thread,
                                 ArgumentCount = argumentCount,
                                 FrameBase = newBase,
-                                SourcePosition = chunk.SourcePositions[pc],
+                                SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
                                 ChunkName = chunk.Name,
-                                RootChunkName = chunk.GetRoot().Name,
+                                RootChunkName = rootChunk.Name,
                             }, buffer, cancellationToken);
                         }
                     case OpCode.Return:
@@ -820,12 +808,22 @@ public static partial class LuaVirtualMachine
                         {
                             stack.EnsureCapacity(RA + 4);
 
-                            // TODO: add error message
-                            var variable = stack.UnsafeGet(RA).Read<double>();
-                            var limit = stack.UnsafeGet(RA + 1).Read<double>();
-                            var step = stack.UnsafeGet(RA + 2).Read<double>();
+                            if (!stack.UnsafeGet(RA).TryRead<double>(out var init))
+                            {
+                                throw new LuaRuntimeException(state.GetTraceback(), "'for' initial value must be a number");
+                            }
+
+                            if (!stack.UnsafeGet(RA + 1).TryRead<double>(out var limit))
+                            {
+                                throw new LuaRuntimeException(state.GetTraceback(), "'for' limit must be a number");
+                            }
+
+                            if (!stack.UnsafeGet(RA + 2).TryRead<double>(out var step))
+                            {
+                                throw new LuaRuntimeException(state.GetTraceback(), "'for' step must be a number");
+                            }
 
-                            var va = variable + step;
+                            var va = init + step;
                             stack.UnsafeGet(RA) = va;
 
                             if (step >= 0 ? va <= limit : va >= limit)
@@ -842,47 +840,54 @@ public static partial class LuaVirtualMachine
                         break;
                     case OpCode.ForPrep:
                         {
-                            // TODO: add error message
-                            stack.UnsafeGet(RA) = stack.UnsafeGet(RA).Read<double>() - stack.UnsafeGet(RA + 2).Read<double>();
+                            if (!stack.UnsafeGet(RA).TryRead<double>(out var init))
+                            {
+                                throw new LuaRuntimeException(state.GetTraceback(), "'for' initial value must be a number");
+                            }
+
+                            if (!stack.UnsafeGet(RA + 2).TryRead<double>(out var step))
+                            {
+                                throw new LuaRuntimeException(state.GetTraceback(), "'for' step must be a number");
+                            }
+
+                            stack.UnsafeGet(RA) = init - step;
                             stack.NotifyTop(RA + 1);
                             pc += instruction.SBx;
                         }
                         break;
                     case OpCode.TForCall:
                         {
-                            var iterator = stack.UnsafeGet(RA).Read<LuaFunction>();
+                            var iteratorRaw = stack.UnsafeGet(RA);
+                            if (!iteratorRaw.TryRead<LuaFunction>(out var iterator))
+                            {
+                                LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "call", iteratorRaw);
+                            }
 
                             var nextBase = RA + 3 + instruction.C;
                             stack.UnsafeGet(nextBase) = stack.UnsafeGet(RA + 1);
                             stack.UnsafeGet(nextBase + 1) = stack.UnsafeGet(RA + 2);
                             stack.NotifyTop(nextBase + 2);
 
-                            var resultBuffer = ArrayPool<LuaValue>.Shared.Rent(1024);
-                            resultBuffer.AsSpan().Clear();
-                            try
+                            var resultCount = await iterator.InvokeAsync(new()
                             {
-                                await iterator.InvokeAsync(new()
-                                {
-                                    State = state,
-                                    Thread = thread,
-                                    ArgumentCount = 2,
-                                    FrameBase = nextBase,
-                                    SourcePosition = chunk.SourcePositions[pc],
-                                    ChunkName = chunk.Name,
-                                    RootChunkName = chunk.GetRoot().Name,
-                                }, resultBuffer.AsMemory(), cancellationToken);
+                                State = state,
+                                Thread = thread,
+                                ArgumentCount = 2,
+                                FrameBase = nextBase,
+                                SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
+                                ChunkName = chunk.Name,
+                                RootChunkName = rootChunk.Name,
+                            }, resultBuffer.AsMemory(), cancellationToken);
 
-                                stack.EnsureCapacity(RA + instruction.C + 3);
-                                for (int i = 1; i <= instruction.C; i++)
-                                {
-                                    stack.UnsafeGet(RA + 2 + i) = resultBuffer[i - 1];
-                                }
-                                stack.NotifyTop(RA + instruction.C + 3);
-                            }
-                            finally
+                            stack.EnsureCapacity(RA + instruction.C + 3);
+                            for (int i = 1; i <= instruction.C; i++)
                             {
-                                ArrayPool<LuaValue>.Shared.Return(resultBuffer);
+                                var index = i - 1;
+                                stack.UnsafeGet(RA + 2 + i) = index >= resultCount
+                                    ? LuaValue.Nil
+                                    : resultBuffer[i - 1];
                             }
+                            stack.NotifyTop(RA + instruction.C + 3);
                         }
                         break;
                     case OpCode.TForLoop:
@@ -897,7 +902,10 @@ public static partial class LuaVirtualMachine
                         break;
                     case OpCode.SetList:
                         {
-                            var table = stack.UnsafeGet(RA).Read<LuaTable>();
+                            if (!stack.UnsafeGet(RA).TryRead<LuaTable>(out var table))
+                            {
+                                throw new LuaException("internal error");
+                            }
 
                             var count = instruction.B == 0
                                 ? stack.Count - (RA + 1)
@@ -941,28 +949,36 @@ public static partial class LuaVirtualMachine
             state.CloseUpValues(thread, frame.Base);
             throw;
         }
+        finally
+        {
+            ArrayPool<LuaValue>.Shared.Return(resultBuffer);
+        }
 
         return 0;
     }
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    static LuaValue RK(LuaStack stack, Chunk chunk, ushort index, int frameBase)
+    static ref LuaValue RK(LuaStack stack, Chunk chunk, ushort index, int frameBase)
     {
-        return index >= 256 ? chunk.Constants[index - 256] : stack.UnsafeGet(index + frameBase);
+        if (index >= 256)
+        {
+            return ref MemoryMarshalEx.UnsafeElementAt(chunk.Constants, index - 256);
+        }
+        else
+        {
+            return ref stack.UnsafeGet(index + frameBase);
+        }
     }
 
-#if NET6_0_OR_GREATER
-    [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]
-#endif
-    static async ValueTask<LuaValue> GetTableValue(LuaState state, Chunk chunk, int pc, LuaValue table, LuaValue key, CancellationToken cancellationToken)
+    static ValueTask<int> GetTableValue(LuaState state, LuaThread thread, Chunk chunk, Chunk rootChunk, int pc, LuaValue table, LuaValue key, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var thread = state.CurrentThread;
         var stack = thread.Stack;
         var isTable = table.TryRead<LuaTable>(out var t);
 
         if (isTable && t.TryGetValue(key, out var result))
         {
-            return result;
+            buffer.Span[0] = result;
+            return new(1);
         }
         else if (table.TryGetMetamethod(state, Metamethods.Index, out var metamethod))
         {
@@ -974,31 +990,21 @@ public static partial class LuaVirtualMachine
             stack.Push(table);
             stack.Push(key);
 
-            var methodBuffer = ArrayPool<LuaValue>.Shared.Rent(1024);
-            methodBuffer.AsSpan().Clear();
-            try
-            {
-                await indexTable.InvokeAsync(new()
-                {
-                    State = state,
-                    Thread = thread,
-                    ArgumentCount = 2,
-                    SourcePosition = chunk.SourcePositions[pc],
-                    FrameBase = stack.Count - 2,
-                    ChunkName = chunk.Name,
-                    RootChunkName = chunk.GetRoot().Name,
-                }, methodBuffer, cancellationToken);
-
-                return methodBuffer[0];
-            }
-            finally
+            return indexTable.InvokeAsync(new()
             {
-                ArrayPool<LuaValue>.Shared.Return(methodBuffer);
-            }
+                State = state,
+                Thread = thread,
+                ArgumentCount = 2,
+                SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
+                FrameBase = stack.Count - 2,
+                ChunkName = chunk.Name,
+                RootChunkName = rootChunk.Name,
+            }, buffer, cancellationToken);
         }
         else if (isTable)
         {
-            return LuaValue.Nil;
+            buffer.Span[0] = LuaValue.Nil;
+            return new(1);
         }
         else
         {
@@ -1007,23 +1013,24 @@ public static partial class LuaVirtualMachine
         }
     }
 
-#if NET6_0_OR_GREATER
-    [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))]
-#endif
-    static async ValueTask SetTableValue(LuaState state, Chunk chunk, int pc, LuaValue table, LuaValue key, LuaValue value, CancellationToken cancellationToken)
+    static ValueTask<int> SetTableValue(LuaState state, LuaThread thread, Chunk chunk, Chunk rootChunk, int pc, LuaValue table, LuaValue key, LuaValue value, Memory<LuaValue> buffer, CancellationToken cancellationToken)
     {
-        var thread = state.CurrentThread;
         var stack = thread.Stack;
         var isTable = table.TryRead<LuaTable>(out var t);
 
-        if (key.Type is LuaValueType.Number && key.TryRead<double>(out var d) && double.IsNaN(d))
+        if (key.Type is LuaValueType.Number)
         {
-            throw new LuaRuntimeException(GetTracebacks(state, chunk, pc), "table index is NaN");
+            var d = key.UnsafeRead<double>();
+            if (double.IsNaN(d))
+            {
+                throw new LuaRuntimeException(GetTracebacks(state, chunk, pc), "table index is NaN");
+            }
         }
 
-        if (isTable && t.ContainsKey(key))
+        if (isTable)
         {
             t[key] = value;
+            return new(1);
         }
         else if (table.TryGetMetamethod(state, Metamethods.NewIndex, out var metamethod))
         {
@@ -1036,37 +1043,25 @@ public static partial class LuaVirtualMachine
             stack.Push(key);
             stack.Push(value);
 
-            var methodBuffer = ArrayPool<LuaValue>.Shared.Rent(1024);
-            methodBuffer.AsSpan().Clear();
-            try
-            {
-                await indexTable.InvokeAsync(new()
-                {
-                    State = state,
-                    Thread = thread,
-                    ArgumentCount = 3,
-                    FrameBase = stack.Count - 3,
-                    SourcePosition = chunk.SourcePositions[pc],
-                    ChunkName = chunk.Name,
-                    RootChunkName = chunk.GetRoot().Name,
-                }, methodBuffer, cancellationToken);
-            }
-            finally
+            return indexTable.InvokeAsync(new()
             {
-                ArrayPool<LuaValue>.Shared.Return(methodBuffer);
-            }
-        }
-        else if (isTable)
-        {
-            t[key] = value;
+                State = state,
+                Thread = thread,
+                ArgumentCount = 3,
+                FrameBase = stack.Count - 3,
+                SourcePosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
+                ChunkName = chunk.Name,
+                RootChunkName = rootChunk.Name,
+            }, buffer, cancellationToken);
         }
         else
         {
             LuaRuntimeException.AttemptInvalidOperation(GetTracebacks(state, chunk, pc), "index", table);
+            return default; // dummy
         }
     }
 
-    static (int FrameBase, int ArgumentCount) PrepareForFunctionCall(LuaThread thread, LuaFunction function, Instruction instruction, int RA, bool isTailCall)
+    static (int FrameBase, int ArgumentCount) PrepareForFunctionCall(LuaThread thread, LuaFunction function, Instruction instruction, int RA, Span<LuaValue> buffer, bool isTailCall)
     {
         var stack = thread.Stack;
 
@@ -1097,22 +1092,14 @@ public static partial class LuaVirtualMachine
             var temp = newBase;
             newBase += variableArgumentCount;
 
-            var buffer = ArrayPool<LuaValue>.Shared.Rent(argumentCount);
-            try
-            {
-                stack.EnsureCapacity(newBase + argumentCount);
-                stack.NotifyTop(newBase + argumentCount);
+            stack.EnsureCapacity(newBase + argumentCount);
+            stack.NotifyTop(newBase + argumentCount);
 
-                var stackBuffer = stack.GetBuffer();
-                stackBuffer.Slice(temp, argumentCount).CopyTo(buffer);
-                buffer.AsSpan(0, argumentCount).CopyTo(stackBuffer[newBase..]);
+            var stackBuffer = stack.GetBuffer();
+            stackBuffer.Slice(temp, argumentCount).CopyTo(buffer);
+            buffer.Slice(0, argumentCount).CopyTo(stackBuffer[newBase..]);
 
-                buffer.AsSpan(argumentCount - variableArgumentCount, variableArgumentCount).CopyTo(stackBuffer[temp..]);
-            }
-            finally
-            {
-                ArrayPool<LuaValue>.Shared.Return(buffer);
-            }
+            buffer.Slice(argumentCount - variableArgumentCount, variableArgumentCount).CopyTo(stackBuffer[temp..]);
         }
 
         return (newBase, argumentCount);
@@ -1123,7 +1110,7 @@ public static partial class LuaVirtualMachine
         var frame = state.CurrentThread.GetCurrentFrame();
         state.CurrentThread.PushCallStackFrame(frame with
         {
-            CallPosition = chunk.SourcePositions[pc],
+            CallPosition = MemoryMarshalEx.UnsafeElementAt(chunk.SourcePositions, pc),
             ChunkName = chunk.Name,
             RootChunkName = chunk.GetRoot().Name,
         });

+ 1 - 1
src/Lua/Standard/Basic/ToNumberFunction.cs

@@ -22,7 +22,7 @@ public sealed class ToNumberFunction : LuaFunction
         double? value = null;
         if (e.Type is LuaValueType.Number)
         {
-            value = e.Read<double>();
+            value = e.UnsafeRead<double>();
         }
         else if (e.TryRead<string>(out var str))
         {