From b3c0b7debcf120a1af34c0c525217440f315e8d2 Mon Sep 17 00:00:00 2001 From: hadashiA Date: Tue, 16 Jun 2026 10:42:28 +0900 Subject: [PATCH 1/2] Optimize __send__ --- src/ChibiRuby/MRubyState.Vm.cs | 54 ++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/src/ChibiRuby/MRubyState.Vm.cs b/src/ChibiRuby/MRubyState.Vm.cs index d836d26..fbcbb63 100644 --- a/src/ChibiRuby/MRubyState.Vm.cs +++ b/src/ChibiRuby/MRubyState.Vm.cs @@ -394,17 +394,59 @@ internal MRubyValue SendMeta(MRubyValue self) if (callInfo.ArgumentPacked) { var packedArgv = registers[0].As(); - registers[0] = packedArgv.SubSequence(1, packedArgv.Length - 1); + var nextArgumentCount = packedArgv.Length - 1; + if (callInfo.KeywordArgumentCount == 0 && nextArgumentCount < MRubyCallInfo.CallMaxArgs) + { + var block = Context.Stack[callInfo.StackPointer + callInfo.BlockArgumentOffset]; + Context.ExtendStack( + callInfo.StackPointer + + MRubyCallInfo.CalculateBlockArgumentOffset(nextArgumentCount, 0) + 1); + registers = Context.Stack.AsSpan(callInfo.StackPointer + 1); + packedArgv.AsSpan(1, nextArgumentCount).CopyTo(registers); + callInfo.ArgumentCount = (byte)nextArgumentCount; + registers[callInfo.ArgumentCount] = block; + } + else + { + registers[0] = packedArgv.SubSequence(1, nextArgumentCount); + } } else { - registers[1..].CopyTo(registers); // copy args - registers[callInfo.ArgumentCount] = registers[callInfo.ArgumentCount + 1]; // copy kargs or blocka - if (callInfo.KeywordArgumentCount > 0) + var argumentCount = callInfo.ArgumentCount; + if (callInfo.KeywordArgumentCount == 0) + { + switch (argumentCount) + { + case 1: + registers[0] = registers[1]; + break; + case 2: + registers[0] = registers[1]; + registers[1] = registers[2]; + break; + case 3: + registers[0] = registers[1]; + registers[1] = registers[2]; + registers[2] = registers[3]; + break; + case 4: + registers[0] = registers[1]; + registers[1] = registers[2]; + registers[2] = registers[3]; + registers[3] = registers[4]; + break; + default: + registers.Slice(1, argumentCount).CopyTo(registers); + break; + } + } + else { - registers[callInfo.ArgumentCount + 1] = registers[callInfo.ArgumentCount + 2]; // copy block + var slotsToMove = callInfo.BlockArgumentOffset - 1; + registers.Slice(1, slotsToMove).CopyTo(registers); } - callInfo.ArgumentCount--; // remove + callInfo.ArgumentCount = (byte)(argumentCount - 1); // remove method name } // var block = stack[blockArgumentOffset]; From 5b4871bf442953226b77d57e131a21d6c8de216e Mon Sep 17 00:00:00 2001 From: hadashiA Date: Tue, 16 Jun 2026 10:52:44 +0900 Subject: [PATCH 2/2] Optmize register copies --- src/ChibiRuby/MRubyState.Vm.cs | 37 +++++++--------------------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/src/ChibiRuby/MRubyState.Vm.cs b/src/ChibiRuby/MRubyState.Vm.cs index fbcbb63..8a1603c 100644 --- a/src/ChibiRuby/MRubyState.Vm.cs +++ b/src/ChibiRuby/MRubyState.Vm.cs @@ -413,38 +413,15 @@ internal MRubyValue SendMeta(MRubyValue self) } else { + // __send__/send: drop the method name in register[0] by shifting the remaining args var argumentCount = callInfo.ArgumentCount; - if (callInfo.KeywordArgumentCount == 0) + var slots = callInfo.KeywordArgumentCount == 0 + ? argumentCount + : callInfo.BlockArgumentOffset - 1; + ref var head = ref MemoryMarshal.GetReference(registers); + for (var i = 0; i < slots; i++) { - switch (argumentCount) - { - case 1: - registers[0] = registers[1]; - break; - case 2: - registers[0] = registers[1]; - registers[1] = registers[2]; - break; - case 3: - registers[0] = registers[1]; - registers[1] = registers[2]; - registers[2] = registers[3]; - break; - case 4: - registers[0] = registers[1]; - registers[1] = registers[2]; - registers[2] = registers[3]; - registers[3] = registers[4]; - break; - default: - registers.Slice(1, argumentCount).CopyTo(registers); - break; - } - } - else - { - var slotsToMove = callInfo.BlockArgumentOffset - 1; - registers.Slice(1, slotsToMove).CopyTo(registers); + Unsafe.Add(ref head, i) = Unsafe.Add(ref head, i + 1); } callInfo.ArgumentCount = (byte)(argumentCount - 1); // remove method name }