Search Issue Tracker

Third Party Issue

Third Party Issue in 6000.6.X

Votes

33

Found in

6000.0.67f1

6000.3.8f1

6000.4.0b8

6000.5.0a6

6000.6.0a1

Issue ID

UUM-134192

Regression

Yes

[Android][IL2CPP][ARMv7] Struct field value corrupts when invoking a generic delegate with a large struct by value

Android

-

Reproduction steps:
1. Open the attached “IN-133974“ project
2. In the Android Player Settings, make sure that IL2CPP is selected as the Scripting backend and that only ARMv7 is selected as the Target Architecture (Project Settings > Player Settings > Other Settings)
3. Build and Run the Android Player
4. In the Player, click the “Test” button
5. Observe the result text field under the button

Actual result: The provided result is a random number, not equal to “300”
Expected result: The provided result is “300”

Reproducible with: 6000.0.67f1, 6000.2.0a2, 6000.3.8f1, 6000.4.0b8, 6000.5.0a6
Not reproducible with: 2022.3.71f1, 6000.2.0a1

Reproducible environments: macOS 26.2
Not reproducible environments: No other environments tested

Reproducible with these devices:
VLNQA00336 - HUAWEI Y6p (MED-LX9N), CPU: MediaTek MT6762R, GPU: PowerVR Rogue GE8320, OS: 10
VLNQA00583 - Xiaomi Redmi 9A (M2006C3LG), CPU: MediaTek Helio G25 (MT6762G), GPU: PowerVR Rogue GE8320, OS: 11
VLNQA00422 - Galaxy S21 Ultra 5G (SM-G998B), CPU: Exynos 2100, GPU: Mali-G78, OS: 11 (When only ARMv7 is enabled, when ARM64 is enabled, issue does not reproduce)

  1. Resolution Note:

    Thank you for reporting a bug to Unity.

    Our investigation indicates the issue originates from a third-party compiler issue in NDK 27. Because the problem lies outside our codebase, we're unable to address it directly at the moment, but it will be addressed in the future by an NDK 29 update.

    When Writer<TestStruct>.write = DebugStruct is assigned in C#, IL2CPP stores it as an open static delegate. The Action_2_Invoke system routes through a generated OpenStatic trampoline. In NDK 27, the compiler incorrectly reordered a dependent store-then-load pair.

    - Option 1: Pass by Reference: Change the delegate to use ref T. This avoids large-struct register splitting. delegate void RefAction<A, B>(A a, ref B b);
    - Option 2: Use Reference Types Wrap the large internal State struct in a class. This shrinks the parent struct size to 8 bytes, keeping it in registers and avoiding the buggy stack-split path.
    - Option 3: Avoid Generic Delegate Trampolines Call the method directly (DebugStruct(writer, item)) rather than through a generic Action<T> field.

  2. Resolution Note (6000.6.X):

    Thank you for reporting a bug to Unity.

    Our investigation indicates the issue originates from a third-party compiler issue in NDK 27. Because the problem lies outside our codebase, we're unable to address it directly at the moment, but it will be addressed in the future by an NDK 29 update.

    When Writer<TestStruct>.write = DebugStruct is assigned in C#, IL2CPP stores it as an open static delegate. The Action_2_Invoke system routes through a generated OpenStatic trampoline. In NDK 27, the compiler incorrectly reordered a dependent store-then-load pair.

    - Option 1: Pass by Reference: Change the delegate to use ref T. This avoids large-struct register splitting. delegate void RefAction<A, B>(A a, ref B b);
    - Option 2: Use Reference Types Wrap the large internal State struct in a class. This shrinks the parent struct size to 8 bytes, keeping it in registers and avoiding the buggy stack-split path.
    - Option 3: Avoid Generic Delegate Trampolines Call the method directly (DebugStruct(writer, item)) rather than through a generic Action<T> field.

Comments (1)

  1. unity_1401A9A0EA24A46A3B96

    Feb 10, 2026 16:46

    29 NDK fix this issue

Add comment

Log in to post comment

All about bugs

View bugs we have successfully reproduced, and vote for the bugs you want to see fixed most urgently.