From 46a398744042bb4a1bbe0eb068775861033c6761 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 13 Sep 2026 19:29:41 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20bounds=20che?= =?UTF-8?q?cking=20in=20VirtualMemory=20hot=20loops?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored `while` loops in `TryValidateRange`, `CopyFromRegions`, and `CopyToRegions` to use standard `for` loops bounded by `span.Length`. This pattern allows RyuJIT to statically verify loop boundaries and elide runtime array bounds checks, enhancing performance and safety during sequential Guest MMU access. Included required performance optimization inline XML documentation. All relevant tests pass and `dotnet format` verifies compliance. Co-authored-by: manupawickramasinghe <73810867+manupawickramasinghe@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ src/SharpEmu.Core/Memory/VirtualMemory.cs | 24 +++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index e86e382be..975f6fb95 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -30,3 +30,7 @@ **Context:** `src/SharpEmu.Core/Memory/VirtualMemory.cs` (`FindInsertionIndex`) **Learning:** Standard C# `List` accesses inside high-frequency binary searches introduce unnecessary overhead via indexer property access and bounds checking. The same optimization pattern recently used in `PhysicalVirtualMemory.cs` (commit 980b47b) applies directly to `VirtualMemory.cs`. Bypassing this via `CollectionsMarshal.AsSpan(list)` completely elides these checks, turning the operation into direct O(1) span memory access. **Action:** When optimizing binary search loops or hot paths over `List`, immediately refactor to use `CollectionsMarshal.AsSpan()` to access elements and `span.Length` for bounds, alongside the `>>> 1` operator for division. +## 2026-09-13 - Bounds Check Elision in Guest MMU Read/Write Loops +**Context:** Guest MMU Address Translation / `VirtualMemory.cs` +**Learning:** The C# JIT compiler cannot elide array bounds checks for `Span` or arrays when using a `while` loop with a manually managed index. This introduces silent branching overhead in memory-intensive hot paths (like `TryValidateRange`, `CopyFromRegions`). +**Action:** Always replace `while` loops iterating over sequential buffers with standard `for (var i = start; i < span.Length; i++)` loops to guarantee RyuJIT bounds check elimination in high-frequency emulation paths. diff --git a/src/SharpEmu.Core/Memory/VirtualMemory.cs b/src/SharpEmu.Core/Memory/VirtualMemory.cs index 7496dc42f..72e574a4b 100644 --- a/src/SharpEmu.Core/Memory/VirtualMemory.cs +++ b/src/SharpEmu.Core/Memory/VirtualMemory.cs @@ -127,6 +127,7 @@ public bool TryWrite(ulong virtualAddress, ReadOnlySpan source) return true; } + /// Performance optimization: Uses a standard for-loop to allow the JIT compiler to elide array bounds checks during hot-path sequential span access. private bool TryValidateRange( ulong virtualAddress, int length, @@ -142,15 +143,9 @@ private bool TryValidateRange( var span = System.Runtime.InteropServices.CollectionsMarshal.AsSpan(_regions); var currentAddress = virtualAddress; var remaining = length; - var currentIndex = regionIndex; - while (true) + for (var i = regionIndex; i < span.Length; i++) { - if (currentIndex >= span.Length) - { - return false; - } - - ref var region = ref span[currentIndex]; + ref var region = ref span[i]; if (currentAddress < region.Region.VirtualAddress || currentAddress >= region.EndAddress || (region.Region.Protection & requiredProtection) == 0) @@ -172,8 +167,9 @@ private bool TryValidateRange( } currentAddress += (ulong)chunkLength; - currentIndex++; } + + return false; } private int FindContainingRegionIndex(ulong virtualAddress) @@ -192,14 +188,15 @@ private int FindContainingRegionIndex(ulong virtualAddress) : -1; } + /// Performance optimization: Uses a standard for-loop to allow the JIT compiler to elide array bounds checks during hot-path sequential span access. private void CopyFromRegions(ulong virtualAddress, Span destination, int regionIndex) { var span = System.Runtime.InteropServices.CollectionsMarshal.AsSpan(_regions); var copied = 0; var currentAddress = virtualAddress; - while (copied < destination.Length) + for (var i = regionIndex; i < span.Length && copied < destination.Length; i++) { - ref var region = ref span[regionIndex++]; + ref var region = ref span[i]; var regionOffset = checked((int)(currentAddress - region.Region.VirtualAddress)); var chunkLength = Math.Min(destination.Length - copied, region.BackingMemory.Length - regionOffset); region.BackingMemory.AsSpan(regionOffset, chunkLength).CopyTo(destination[copied..]); @@ -208,14 +205,15 @@ private void CopyFromRegions(ulong virtualAddress, Span destination, int r } } + /// Performance optimization: Uses a standard for-loop to allow the JIT compiler to elide array bounds checks during hot-path sequential span access. private void CopyToRegions(ulong virtualAddress, ReadOnlySpan source, int regionIndex) { var span = System.Runtime.InteropServices.CollectionsMarshal.AsSpan(_regions); var copied = 0; var currentAddress = virtualAddress; - while (copied < source.Length) + for (var i = regionIndex; i < span.Length && copied < source.Length; i++) { - ref var region = ref span[regionIndex++]; + ref var region = ref span[i]; var regionOffset = checked((int)(currentAddress - region.Region.VirtualAddress)); var chunkLength = Math.Min(source.Length - copied, region.BackingMemory.Length - regionOffset); source.Slice(copied, chunkLength).CopyTo(region.BackingMemory.AsSpan(regionOffset, chunkLength)); From b72e8d82eb4de467cc3fe5b9ba6bbb7e392ecc3d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 13 Sep 2026 19:39:09 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Fix=20ExportModel=20ref?= =?UTF-8?q?lection=20signature=20in=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated `ExportModelEqualityTests` in `SysAbiExportGeneratorTests.cs` to pass the newly added `bool preferLle` parameter to the `ExportModel` constructor via `Activator.CreateInstance`. This resolves the `System.MissingMethodException` encountered during GitHub CI test runs. Co-authored-by: manupawickramasinghe <73810867+manupawickramasinghe@users.noreply.github.com> --- .jules/bolt.md | 5 ++++ .../SysAbiExportGeneratorTests.cs | 24 +++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 975f6fb95..6720a80d1 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -34,3 +34,8 @@ **Context:** Guest MMU Address Translation / `VirtualMemory.cs` **Learning:** The C# JIT compiler cannot elide array bounds checks for `Span` or arrays when using a `while` loop with a manually managed index. This introduces silent branching overhead in memory-intensive hot paths (like `TryValidateRange`, `CopyFromRegions`). **Action:** Always replace `while` loops iterating over sequential buffers with standard `for (var i = start; i < span.Length; i++)` loops to guarantee RyuJIT bounds check elimination in high-frequency emulation paths. + +## 2026-09-13 - SysAbiExportGenerator.ExportModel Constructor Signature Updates +**Context:** Unit Tests / `SysAbiExportGeneratorTests.cs` +**Learning:** When internal models like `ExportModel` are modified with new parameters (e.g., adding `bool preferLle`), reflection-based unit tests creating instances via `Activator.CreateInstance` will fail with `MissingMethodException` if the test arguments are not updated to match the new constructor signature. +**Action:** Always verify and update reflection-based test factories whenever internal constructor signatures change to maintain test suite stability. diff --git a/tests/SharpEmu.SourceGenerators.Tests/SysAbiExportGeneratorTests.cs b/tests/SharpEmu.SourceGenerators.Tests/SysAbiExportGeneratorTests.cs index 93a2658f7..fd448c44f 100644 --- a/tests/SharpEmu.SourceGenerators.Tests/SysAbiExportGeneratorTests.cs +++ b/tests/SharpEmu.SourceGenerators.Tests/SysAbiExportGeneratorTests.cs @@ -191,15 +191,15 @@ public void ExportModelEqualityTests() var modelType = generatorType.GetNestedType("ExportModel", System.Reflection.BindingFlags.NonPublic); Assert.NotNull(modelType); - object Create(string containingType, string methodName, SysAbiExportShape.HandlerShape shape, string typedParameterKinds, string libraryName, string nid, string exportName, int target) + object Create(string containingType, string methodName, SysAbiExportShape.HandlerShape shape, string typedParameterKinds, string libraryName, string nid, string exportName, int target, bool preferLle) { - return Activator.CreateInstance(modelType, containingType, methodName, shape, typedParameterKinds, libraryName, nid, exportName, target)!; + return Activator.CreateInstance(modelType, containingType, methodName, shape, typedParameterKinds, libraryName, nid, exportName, target, preferLle)!; } - var baseModel = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 1); + var baseModel = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 1, false); // Equals(object? obj) and Equals(ExportModel? other) identical - var identical = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 1); + var identical = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 1, false); Assert.True(baseModel.Equals(identical)); Assert.True(baseModel.Equals((object)identical)); @@ -214,28 +214,28 @@ object Create(string containingType, string methodName, SysAbiExportShape.Handle Assert.False(baseModel.Equals(new object())); // Different fields - var diffContainingType = Create("TypeB", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 1); + var diffContainingType = Create("TypeB", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 1, false); Assert.False(baseModel.Equals(diffContainingType)); - var diffMethodName = Create("TypeA", "MethodB", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 1); + var diffMethodName = Create("TypeA", "MethodB", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 1, false); Assert.False(baseModel.Equals(diffMethodName)); - var diffShape = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.Parameterless, "uint", "libA", "nidA", "expA", 1); + var diffShape = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.Parameterless, "uint", "libA", "nidA", "expA", 1, false); Assert.False(baseModel.Equals(diffShape)); - var diffTypedKinds = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "int", "libA", "nidA", "expA", 1); + var diffTypedKinds = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "int", "libA", "nidA", "expA", 1, false); Assert.False(baseModel.Equals(diffTypedKinds)); - var diffLibrary = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libB", "nidA", "expA", 1); + var diffLibrary = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libB", "nidA", "expA", 1, false); Assert.False(baseModel.Equals(diffLibrary)); - var diffNid = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidB", "expA", 1); + var diffNid = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidB", "expA", 1, false); Assert.False(baseModel.Equals(diffNid)); - var diffExportName = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expB", 1); + var diffExportName = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expB", 1, false); Assert.False(baseModel.Equals(diffExportName)); - var diffTarget = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 2); + var diffTarget = Create("TypeA", "MethodA", SysAbiExportShape.HandlerShape.ContextOnly, "uint", "libA", "nidA", "expA", 2, false); Assert.False(baseModel.Equals(diffTarget)); } }