Skip to content

Correct integer switch table dispatch - #583

Open
jogibear9988 wants to merge 1 commit into
dadhi:masterfrom
jogibear9988:fix/integer-switch-dispatch
Open

jogibear9988 wants to merge 1 commit into
dadhi:masterfrom
jogibear9988:fix/integer-switch-dispatch

Conversation

@jogibear9988

@jogibear9988 jogibear9988 commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Integer switch-table normalization adds negative origins instead of subtracting them, and can feed 64-bit operands to the 32-bit IL switch instruction. Wide values may alias their low bits; negative or unsigned table origins can select the wrong/default branch.

Keep Int64/UInt64 operands (including enums) on the existing ordered-comparison path. Emit Int32 origins/outlier comparisons and subtract the actual table origin. Preserve upstream duplicate-test handling. The production diff is eight added/five removed lines.

Directly based on upstream master 750e1e8ea602a7e72a90072ee80b0c1376f5b72d; one commit (9d727f3b), independent of #558 and the other standalone PRs.

Correctness and validation

Four shared methods cover first-case duplicate selection, signed/unsigned 64-bit operands and enums, low-bit aliases, 32-bit origins at negative and unsigned boundaries, defaults, and single evaluation of the switch operand.

The unfixed .NET 10 regression fails the integer switch matrix in both APIs; the same matrix passes after the patch.

  • Focused Standard and LightExpression regressions pass on .NET 8, .NET 10 and .NET Framework 4.7.2 (4 registered methods per API).
  • Full Release solution build passes with zero warnings/errors.
  • Legacy native suites pass: 1,756 cases on .NET 10 and 1,755 on .NET 8.
  • Framework runner passes 198 TestX + 1,639 legacy cases.
  • The full .NET 8/10 runner still reports the existing nullable MultiplyChecked parity failure for (1,1) and (2,1) in both APIs. Unmodified master was rebuilt and rerun alongside this branch; the complete failure logs are byte-for-byte identical. No regression assertions were skipped or weakened. Legacy suites were invoked separately because the existing TestX failure gates the normal runner.

Performance checks

Compared only with the master revision above, using already-working expressions. The broken expressions establish correctness and are not treated as a valid before/after performance baseline. Both Standard and LightExpression are measured. Framework is correctness-tested; the timings below cover .NET 8.0.28 and .NET 10.0.9 on Windows ARM64, SDK 10.0.301, Release.

Prebuilt expression trees; Light conversion outside the timed section; strict FEC compilation, no System fallback. Tiering disabled, fixed CPU affinity, warmed compilation and invocation. Two fresh processes with opposite load order, nine alternating samples per side, GC before samples and a 256 MiB no-GC region where available. Compilation below uses the longer approximately 100 ms pair-calibration run; invocation uses the approximately 24 ms run. Times are geometric means of the two process medians.

All measured, already-working controls retain identical decoded emitted IL and the same managed allocation per compilation/invocation. The ref-box invocation control intentionally allocates its boxed int (24 B); other invocation controls allocate zero. No persistent compiler metadata, extra collection pass, or closure allocation is added by this fix.

Timing remains noisy, including the slower rows below and variation when comparing master with itself. These measurements do not prove zero CPU regression across inputs/machines; no speedup or universal performance-equivalence claim is made.

Runtime API Control Compile ns, master → fix Change Invoke ns, master → fix Compile B/op, both Invoke B/op, both
net10.0 Standard add_control 1162.6 → 1166.0 +0.3% 1.94 → 1.87 872 0
net10.0 Standard reference_constant 933.7 → 978.7 +4.8% 1.85 → 1.85 984 0
net10.0 Standard switch_int32 1808.3 → 1799.5 -0.5% 2.18 → 2.13 1176 0
net10.0 Standard switch_int64_two_cases 1614.1 → 1588.1 -1.6% 2.10 → 2.12 1160 0
net10.0 Light add_control 992.1 → 1103.6 +11.2% 1.94 → 1.88 872 0
net10.0 Light reference_constant 983.2 → 932.6 -5.2% 1.94 → 1.91 984 0
net10.0 Light switch_int32 1572.4 → 1559.3 -0.8% 2.27 → 2.23 1176 0
net10.0 Light switch_int64_two_cases 1472.2 → 1498.3 +1.8% 1.96 → 2.00 1160 0
net8.0 Standard add_control 1099.5 → 1065.5 -3.1% 1.86 → 1.88 872 0
net8.0 Standard reference_constant 1007.5 → 949.0 -5.8% 1.88 → 1.92 984 0
net8.0 Standard switch_int32 1474.6 → 1575.9 +6.9% 2.16 → 2.14 1176 0
net8.0 Standard switch_int64_two_cases 1540.6 → 1554.5 +0.9% 2.08 → 2.08 1160 0
net8.0 Light add_control 1074.2 → 1303.7 +21.4% 1.88 → 1.87 872 0
net8.0 Light reference_constant 903.7 → 854.5 -5.4% 1.89 → 1.90 984 0
net8.0 Light switch_int32 1471.1 → 1524.0 +3.6% 2.15 → 2.14 1176 0
net8.0 Light switch_int64_two_cases 1474.9 → 1424.8 -3.4% 2.02 → 2.06 1160 0

The initial short run included all 16 Standard/Light controls per runtime: add, reference constant, Int32→Int64 conversion, root ref boxing, built-in Not, method-based parameter Not, a positive-origin Int32 table switch, and a two-case Int64 comparison switch. IL and allocation checks passed across all of them. The longer run focuses on the affected operation family plus add/constant controls; all short-run timing ranges, including unselected controls, are summarized below.

Runtime Initial compile range Initial compile geometric mean change Longer master/master control range Longer master/master geometric mean change
net10.0 -12.4% to +12.7% +0.2% -5.3% to +5.8% +1.4%
net8.0 -9.4% to +9.2% -0.3% -5.4% to +9.7% +1.3%

Limit IL switch-table indices to 32-bit integer types. Keep long and ulong operands on ordered comparison dispatch. Normalize table origins by subtracting the actual Int32 origin and emit Int32 outlier comparisons. Preserve first-case selection for duplicate tests.

Standard and Light regressions cover signed/unsigned 32-bit origins, wide values and enums, low-bit aliases, duplicate tests and single operand evaluation. Focused tests pass on net8.0, net10.0 and net472; the unfixed compiler fails the switch matrix. Full solution builds, legacy suites and Framework runners pass. Existing master MultiplyChecked parity failures reproduce identically on net8.0/net10.0.
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.

1 participant