c-variadic: fix i128 argument being misread on arm64ec
diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs
index 3ca920e..a64452d 100644
--- a/compiler/rustc_codegen_llvm/src/va_arg.rs
+++ b/compiler/rustc_codegen_llvm/src/va_arg.rs
@@ -1085,9 +1085,15 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
             bx,
             addr,
             target_ty,
-            PassMode::Direct,
+            // MS x64 ABI requirement: "Any argument that doesn't fit in 8 bytes, or is
+            // not 1, 2, 4, or 8 bytes, must be passed by reference."
+            if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
+                PassMode::Indirect
+            } else {
+                PassMode::Direct
+            },
             SlotSize::Bytes8,
-            if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes },
+            AllowHigherAlign::No,
             ForceRightAdjust::No,
         ),
         Arch::AArch64 if target.is_like_windows || target.is_like_darwin => emit_ptr_va_arg(
diff --git a/tests/assembly-llvm/c-variadic/aarch64.rs b/tests/assembly-llvm/c-variadic/aarch64.rs
index a5dbc8a..fcb44d0 100644
--- a/tests/assembly-llvm/c-variadic/aarch64.rs
+++ b/tests/assembly-llvm/c-variadic/aarch64.rs
@@ -287,11 +287,13 @@ pub struct VaList<'a> {
     // AARCH64_BE-NEXT: ldp     x0, x1, [x8]
     // AARCH64_BE-NEXT: ret
 
+    // NOTE: matches x86_64 Windows: an `i128` is passed indirectly, the slot holds a pointer.
+    //
     // ARM64EC_MSVC-LABEL: read_i128 = "#read_i128"
     // ARM64EC_MSVC: ldr x9, [x0]
-    // ARM64EC_MSVC-NEXT: mov x8, x0
-    // ARM64EC_MSVC-NEXT: ldp x0, x1, [x9], #16
-    // ARM64EC_MSVC-NEXT: str x9, [x8]
+    // ARM64EC_MSVC-NEXT: ldr x10, [x9], #8
+    // ARM64EC_MSVC-NEXT: str x9, [x0]
+    // ARM64EC_MSVC-NEXT: ldp x0, x1, [x10]
     // ARM64EC_MSVC-NEXT: ret
 
     // AARCH64_DARWIN-LABEL: _read_i128: