Skip to content

reflectlite: use less stack when reading struct fields - #5884

Merged
davecheney merged 1 commit into
tinygo-org:devfrom
davecheney:dfc/reflectlite-field-stack
Oct 17, 2026
Merged

davecheney merged 1 commit into
tinygo-org:devfrom
davecheney:dfc/reflectlite-field-stack

Conversation

@davecheney

@davecheney davecheney commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Reflection on struct fields used too much stack. RawType.Field and Value.Field each built a complete rawStructField in an extra stack frame, and that frame was the deepest part of the call. On small goroutine stacks this is a problem: simavr has a 384-byte default stack, and its value went up and down in the past because tests overflowed it (32a7b8c, a94e03e, and the 512-byte setting for math.go in main_test.go). Code that calls Value.Field in a recursive walk, for example struct equality in the runtime (runtime.reflectValueEqual), reaches the limit quickly.

Stack use for a four-field struct, dev compared to this PR. Built at -opt=z with LLVM 22.1.8. The values come from the disassembly of complete programs: pushed registers, stack adjustment and return address of each frame in the deepest chain. runtime.alloc is not included, because it is the same before and after.

Below runtime.reflectValueEqual (Value.Field and its callees):

Target dev This PR
cortex-m-qemu 112 B 48 B
riscv-qemu 128 B 80 B
simavr 65 B 45 B

Below the caller of reflect.Type.Field, with Field called from several locations so that it is not inlined into one caller:

Target dev This PR
cortex-m-qemu 48 B 40 B
riscv-qemu 48 B 48 B
simavr 68 B 31 B

The gain is largest on AVR. On cortex-m and riscv, LLVM already inlined most of the old Field path, so the gain there is small.

@davecheney

Copy link
Copy Markdown
Contributor Author

@jakebailey @dgryski gentle ping

@dgryski dgryski left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Had a discussion with by bot about this and forgot to come back here with a response.

@davecheney
davecheney force-pushed the dfc/reflectlite-field-stack branch from f142461 to aa8a1bd Compare October 16, 2026 21:58
@davecheney
davecheney marked this pull request as draft October 16, 2026 23:57
@davecheney davecheney changed the title reflectlite: use less stack in RawType.Field reflectlite: use less stack when reading struct fields Oct 17, 2026
@davecheney
davecheney force-pushed the dfc/reflectlite-field-stack branch from 0b4a558 to a8706a0 Compare October 17, 2026 01:02
Reflection on struct fields used too much stack. RawType.Field and Value.Field each built a complete rawStructField in an extra stack frame, and that frame was the deepest part of the call. On small goroutine stacks this is a problem: simavr has a 384-byte default stack, and its value went up and down in the past because tests overflowed it (32a7b8c, a94e03e, and the 512-byte setting for math.go in main_test.go). Code that calls Value.Field in a recursive walk, for example struct equality in the runtime (runtime.reflectValueEqual), reaches the limit quickly.

Stack use for a four-field struct, dev compared to this PR. Built at -opt=z with LLVM 22.1.8. The values come from the disassembly of complete programs: pushed registers, stack adjustment and return address of each frame in the deepest chain. runtime.alloc is not included, because it is the same before and after.

Below runtime.reflectValueEqual (Value.Field and its callees):

Target | dev | This PR
--- | ---: | ---:
cortex-m-qemu | 112 B | 48 B
riscv-qemu | 128 B | 80 B
simavr | 65 B | 45 B

Below the caller of reflect.Type.Field, with Field called from several locations so that it is not inlined into one caller:

Target | dev | This PR
--- | ---: | ---:
cortex-m-qemu | 48 B | 40 B
riscv-qemu | 48 B | 48 B
simavr | 68 B | 31 B

The gain is largest on AVR. On cortex-m and riscv, LLVM already inlined most of the old Field path, so the gain there is small.
@davecheney
davecheney force-pushed the dfc/reflectlite-field-stack branch from a8706a0 to 6a230d1 Compare October 17, 2026 01:05
@davecheney
davecheney marked this pull request as ready for review October 17, 2026 01:06
@davecheney

Copy link
Copy Markdown
Contributor Author

@dgryski @jakebailey PTAL

@davecheney
davecheney merged commit 5a84515 into tinygo-org:dev Oct 17, 2026
33 checks passed
@davecheney
davecheney deleted the dfc/reflectlite-field-stack branch October 17, 2026 04:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants