Report an array's RANK, not just its flat element count - #7
Merged
Conversation
…ount `variableShape()`'s array arm and `struct()`'s array members now carry `dims` — the per-dimension extents, outermost first. `length` is unchanged (still the product). Only `dims` answers the question a consumer spelling C has to answer: `g[i]` on a rank-2 array is a ROW, not an element. Knowing just the flat count you write one subscript, which against the project's own header is a type error — or, where the row address flows into an integer context, silently the wrong address. On a DW_AT_declaration a leading extent of 1 is GCC 2.95's unsized-outer-bound spelling (`extern T x[]`, `extern T x[][4]`) and reports null, mirroring the rule `length` already applies; the inner extents are written down and survive. A rank-1 array reports a one-entry `dims`, so an absent key always means "not an array", never "rank unknown". Fixtures: agbcc-min gains g_grid3 [2][3][4], the extern g_ext_grid[][4] idiom, and a 2-D struct member Grid.cells.
…st agbcc
`dims` was only exercised by agbcc-min. The facts it reads are producer- and
byte-order-sensitive — the dimensions come from a DW_TAG_subrange chain whose bounds are
read through the endian-aware reader, and DWARF 2 spells them differently from DWARF 3+ —
so one toolchain is not evidence.
mips-min, ppc-min and devkitarm-min gain the same two shapes agbcc-min already had:
`unsigned char g_grid3[2][3][4]` (a fully-bounded rank 3) and `struct Grid { int id;
unsigned short cells[2][3]; }` (the same fact on a member). All four now report
`dims: [2,3,4]` / `[2,3]` identically across GCC 2.95 (DWARF 2), GCC 14 (DWARF 3+), and
both byte orders.
They are wired into the EXISTING cross-project machinery rather than tested separately:
`Grid` joins CANDIDATE_TYPES and `g_grid3`/`g_grid` join CANDIDATE_GLOBALS, so the layout,
declaration-fact and variableShape comparisons all pick them up, and the "shares exactly
this declaration set" pin records the new membership. Mutation-checked: flattening
arrayDims to a single product turns the shared-shape test red on all four.
`g_ext_grid` is NOT shared, and is now listed in CANDIDATE_GLOBALS so `skipped` says so out
loud rather than leaving the asymmetry invisible: the unsized-outer-bound spelling
(`extern T x[][4]` encoded as upper_bound 0) is a GCC 2.95 ENCODING quirk, so agbcc-min is
the only place it can be pinned.
devkitarm-min's block is APPENDED, not inserted — debug-macro.spec.ts pins the macro
fixtures by exact line number and the file says so; inserting mid-file moved REG_DISPSTAT
from 157 to 173 and broke three tests.
222 tests green (was 218), typecheck + lint clean, all four ELF/oracle artifacts rebuilt.
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.
variableShape()'s array arm andstruct()'s array members now carrydims— the per-dimension extents, outermost first (u16 g[4][0x400]→[4, 1024]).lengthis unchanged: still the product, which is what sizes the object.Why
The two readings answer different questions, and only
dimsanswers the one a consumer spelling C has to answer:g[i]on a rank-2 array is a ROW, not an element. Knowing only the flat count you write one subscript, which against the project's own header is a type error — or, where the row address flows into an integer context, merely a warning and silently the wrong address.This is not hypothetical. It was found from the consumer side: asmlift emitted
gBgTilemapBufs[i]for the Klonoa decomp's ownextern u16 gBgTilemapBufs[4][0x400], which isincompatible types in assignment×4. Across six vendored GBA decomp projects the maps carry 444 rank>1 array globals; kleod's context alone declares 576 multi-dimensional arrays.Details
arrayDims()is the new primitive;arrayLength()is re-derived from it rather than kept as a parallel loop, so the flat count and the rank cannot drift.nullmarks an unbounded dimension.DW_AT_declaration, a leading extent of 1 is GCC 2.95's spelling of an unsized outer bound (extern T x[],extern T x[][4]) and is reported asnull— mirroring the rulelengthalready applies. The inner extents are written down and survive, and they are the ones an element access needs. A definition keeps its1, since the definition is the witness.dims, so an absent key always means "not an array", never "rank unknown" — which is what lets a consumer gate on key presence.Fixtures — all four test projects
The facts
dimsreads are producer- and byte-order-sensitive (aDW_TAG_subrangechain whose bounds go through the endian-aware reader; DWARF 2 spells them differently from DWARF 3+), so one toolchain is not evidence. All four projects gainunsigned char g_grid3[2][3][4]andstruct Grid { int id; unsigned short cells[2][3]; }, and all four reportdims: [2,3,4]/[2,3]identically across GCC 2.95 (DWARF 2), GCC 14 (DWARF 3+), and both byte orders.They ride the existing cross-project machinery rather than a separate test:
GridjoinsCANDIDATE_TYPESandg_grid3/g_gridjoinCANDIDATE_GLOBALS, so the layout, declaration-fact andvariableShapecomparisons all pick them up. Mutation-checked — flatteningarrayDimsto a single product turns the shared-shape test red on all four.agbcc-minadditionally carriesextern const short g_ext_grid[][4](defined incrt0.s, so only the inner extent is knowable). That one is deliberately not shared: the unsized-outer-bound encoding is a GCC 2.95 quirk, so it is listed inCANDIDATE_GLOBALSand recorded byskippedas agbcc-only rather than left as an invisible asymmetry.222 tests green,
tsc --noEmitandeslintclean. All four ELF/oracle artifacts rebuilt and byte-stable.Note on
StructMember.dimsReported for symmetry with
VariableShape— a member and a global are the same DWARF array type. It has no consumer in asmlift today, deliberately: a consumer synthesizing its own struct declaration may flatten the member, because its access and its declaration then agree by construction. Rank only becomes load-bearing where the declaration belongs to someone else. The docstring says so.Consumed by macabeus/asmlift#9, which requires
>= 0.5.0, so it cannotpnpm installuntil this is published.🤖 Generated with Claude Code