Transpiler: constant-fold arr.Length and sizeof(T)#540
Open
Copilot wants to merge 2 commits into
Open
Conversation
5 tasks
Agent-Logs-Url: https://github.com/jonathanpeppers/dotnes/sessions/a97fcdbc-cc6f-40e0-b6e3-c1b88a0941b7 Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Add transpiler support for arr.Length and sizeof(T)
Transpiler: constant-fold May 22, 2026
arr.Length and sizeof(T)
Contributor
There was a problem hiding this comment.
Pull request overview
This PR extends the dotnes IL→6502 pipeline to constant-fold array length (ldlen / .Length) and verifies Roslyn’s compile-time folding of sizeof(T) for primitive types, unblocking ports that rely heavily on array-length loop bounds.
Changes:
- Add
ldlensupport inIL2NESWriterby folding.Lengthto an immediate constant when the array size is known at transpile time (locals, static field tables, andnew T[N].LengthRelease elision). - Fix
.cctorscanning for static array sizes by tracking array-initialization state acrossdup/ldtoken/call InitializeArraypatterns. - Add Roslyn-based regression tests for
.Lengthfolding and primitivesizeof()folding; update related unsupported-opcode guidance/docs.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/dotnes.tests/IL2NESWriterTests.cs | Updates the Ldlen unsupported-opcode message expectation to match new guidance. |
| src/dotnes.tests/ArraysTests.cs | Adds Roslyn tests covering .Length folding patterns and sizeof(byte/ushort) folding. |
| src/dotnes.tasks/Utilities/Transpiler.StructAnalysis.cs | Improves .cctor scanning so static array sizes are discovered for .Length folding. |
| src/dotnes.tasks/Utilities/IL2NESWriter.ILDispatch.cs | Implements Ldlen folding in IL dispatch and adds/duplicates Sizeof handling. |
| src/dotnes.tasks/Utilities/IL2NESWriter.Helpers.cs | Adds helper lookahead logic to detect ldlen and suppress redundant array loads. |
| src/dotnes.tasks/Utilities/IL2NESWriter.cs | Adds _pendingLdlenSource state used to fold .Length at ldlen. |
| docs/8bitworkshop-samples.md | Removes an outdated sizeof() limitation note. |
Comment on lines
171
to
175
| int lastLdc = 0; | ||
| ILOpCode prevOp = ILOpCode.Nop; | ||
| bool inArrayInit = false; // True after Newarr until Stsfld/Stloc/etc. consumes the array | ||
|
|
||
| while (il.Offset < il.Length) |
Comment on lines
+1168
to
+1176
| else if (previous == ILOpCode.Newarr && Stack.Count > 0) | ||
| { | ||
| // Roslyn (Release) elides the local for `new T[N].Length`, emitting | ||
| // ldc; newarr; ldlen directly. The element count is on the Stack | ||
| // (the Newarr handler left it there after removing the size LDA). | ||
| // For byte[] the count IS the length; for ushort[] it was already | ||
| // popped during newarr processing, so this path only handles byte[]. | ||
| count = Stack.Pop(); | ||
| } |
Comment on lines
+1187
to
+1193
| case ILOpCode.Sizeof: | ||
| // Native integer size (nint/IntPtr) on the 6502 is 1 byte (8-bit CPU). | ||
| // Fixed-size primitive types (byte, ushort, int, etc.) are folded by Roslyn at compile time | ||
| // and typically never reach this opcode; this implementation only handles platform-dependent | ||
| // native integer sizeof values and will always push 1 here. | ||
| WriteLdc(1); | ||
| break; |
Comment on lines
+1200
to
+1201
| // Use a local copy of the array for indexing to focus the test on | ||
| // the .Length constant-folding for the static field. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Roslyn emits
arr.Lengthasldlenand the transpiler rejected it, forcing samples to maintain a parallelconst byte LEN = N. This blocks the From Below port (#378), which uses array-length loop bounds 26 times.Changes
IL2NESWriter—Ldlenhandler. When the precedingLdloc.X/Ldsfldreferences a known arrayLocal, suppress the load and emitLDA #count(orLDX/LDApair for >255 elements) atLdlen. UsesLocal.Value, which is element count for byte, ushort, and struct arrays alike.byte n = (byte)new byte[8].Length, emittingldc; newarr; ldlendirectly.Ldlenfalls back to popping the size off the IL stack left byNewarr.Transpiler.ScanCctorArraySizes. Replaced theprevOp is Newarr or Dupcheck atStsfldwith aninArrayInitflag so the standardnewarr; dup; ldtoken; call InitializeArray; stsfldshape is recognized. Without this,static readonly byte[]fields were sized as 0 and.Lengthfolded to the wrong value.sizeof(T). No code change required for primitives — Roslyn folds these toLdcbefore IL emission. Regression test added.Ldlenunsupported-opcode message to describe which forms are now supported; dropped thesizeof()caveat fromtranstable.cindocs/8bitworkshop-samples.md.Now transpiles
Tests
Five new
RoslynTestsinArraysTestscover: localbyte[]loop bound (byte-identical to a hard-codedN),static readonly byte[]loop bound, standalone.Lengthassignment under Release elision,ushort[].Lengthreturning element count (not byte count), andsizeof(byte)/sizeof(ushort)Roslyn-folding. ExistingGetUnsupportedOpcodeMessagerow forLdlenupdated to match the new wording.