From dba354a85ad112f7d16f0f58526a32c57d3074dd Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 15 Sep 2026 20:01:43 +0200 Subject: [PATCH] [mono][llvm] Fix malformed uaddlp/saddlp intrinsic declarations (#132744) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `add_intrinsic ()` computes the source type of a `Widen`-kind intrinsic as `intrin_types [vw][ew - 1]`, but SADDLP/UADDLP were declared with `I1` in their overload spec, so `ew == 0` was reached and `intrin_types [vw][-1]` was read out of bounds. That reads the last element of the previous row, which is a floating point type, so two of the eight registered declarations were malformed: | overload spec | declaration | |----------------|----------------------------------------------------------------| | `V64 \| I1` | `declare <8 x i8> @llvm.aarch64.neon.uaddlp.v8i8.f64(double)` ❌ | | `V64 \| I2` | `declare <4 x i16> @llvm.aarch64.neon.uaddlp.v4i16.v8i8(<8 x i8>)` | | `V64 \| I4` | `declare <2 x i32> @llvm.aarch64.neon.uaddlp.v2i32.v4i16(<4 x i16>)` | | `V64 \| I8` | `declare <1 x i64> @llvm.aarch64.neon.uaddlp.v1i64.v2i32(<2 x i32>)` | | `V128 \| I1` | `declare <16 x i8> @llvm.aarch64.neon.uaddlp.v16i8.v1f64(<1 x double>)` ❌ | | `V128 \| I2` | `declare <8 x i16> @llvm.aarch64.neon.uaddlp.v8i16.v16i8(<16 x i8>)` | | `V128 \| I4` | `declare <4 x i32> @llvm.aarch64.neon.uaddlp.v4i32.v8i16(<8 x i16>)` | | `V128 \| I8` | `declare <2 x i64> @llvm.aarch64.neon.uaddlp.v2i64.v4i32(<4 x i32>)` | The element width in these specs is that of the widened *result*, and there is no `uaddlp`/`saddlp` form with an 8-bit result element, so `I1` doesn't belong there in the first place. SADDLP and UADDLP are the only two `Widen`-kind entries in `llvm-intrinsics.h`, so no other intrinsic is affected. Call sites are unaffected too — `ovr_tag_from_mono_vector_class (ins->klass)` uses the result vector class, which is never an 8-bit element vector for these. The malformed declarations were never called, but starting with **LLVM 23** the IR verifier rejects them, so AOT compiling any assembly that references `AdvSimd.AddPairwiseWidening` fails: ``` intrinsic argument 0 type (overload type 1) expected any vector type, but got double declare <8 x i8> @llvm.aarch64.neon.uaddlp.v8i8.f64(double) intrinsic argument 0 type (overload type 1) expected any vector type, but got double declare <8 x i8> @llvm.aarch64.neon.saddlp.v8i8.f64(double) LLVM ERROR: Broken module found, compilation aborted! ``` The out-of-bounds read has been there since #51993 (2021); it only started failing now because of the LLVM 23 bump. It was found in dotnet/macios, where the untrimmed `dont link` iOS test started failing to AOT compile `System.Private.CoreLib.dll`. Fixes https://github.com/dotnet/runtime/issues/132743 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b1fe653-aecb-469b-b96d-85e0691818de --- src/mono/mono/mini/llvm-intrinsics.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/mini/llvm-intrinsics.h b/src/mono/mono/mini/llvm-intrinsics.h index 848ef32a64bf82..103fe71a8ed217 100644 --- a/src/mono/mono/mini/llvm-intrinsics.h +++ b/src/mono/mono/mini/llvm-intrinsics.h @@ -426,8 +426,11 @@ INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_UMINV, aarch64_neon_uminv, Arm64, Across, INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_FMAXV, aarch64_neon_fmaxv, Arm64, Across, V64 | V128 | R4 | R8) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_FMINV, aarch64_neon_fminv, Arm64, Across, V64 | V128 | R4 | R8) -INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_SADDLP, aarch64_neon_saddlp, Arm64, Widen, V64 | V128 | I1 | I2 | I4 | I8) -INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_UADDLP, aarch64_neon_uaddlp, Arm64, Widen, V64 | V128 | I1 | I2 | I4 | I8) +/* The element width here is that of the (widened) result, so I1 is not a valid + * combination: the narrowest result element width is 16 bits. Listing it would + * also make add_intrinsic () index intrin_types out of bounds. */ +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_SADDLP, aarch64_neon_saddlp, Arm64, Widen, V64 | V128 | I2 | I4 | I8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_UADDLP, aarch64_neon_uaddlp, Arm64, Widen, V64 | V128 | I2 | I4 | I8) INTRINS_OVR_2_ARG(AARCH64_ADV_SIMD_FCVTXN, aarch64_neon_fcvtxn, Arm64, v64_r4_t, v128_r8_t)