[PATCH] Fix 128-bit alien value truncation on x86-64

Andreas Franke via Sbcl-devel <[email protected]> Fri, 27 Feb 2026 00:23:58 +0000
Newsgroups gmane.lisp.steel-bank.devel
Message-ID <trinity-b649d32a-2bd5-489c-97fa-4bbedbd695af-1772151838646@trinity-msg-rest-gmx-gmx-live-756cfcdf5c-tbvpt>
Here's another one (co-authored by Claude).
Validated on sbcl-2.6.1-233-g122d48ff6 master.

_______________________________________________
Sbcl-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sbcl-devel
0001-Fix-128-bit-alien-value-truncation-on-x86-64.patch (text/x-patch, 3.2 KB)
From 2074ae00c19007b862e0e03001defc059e9e6c77 Mon Sep 17 00:00:00 2001
From: Andreas Franke <[email protected]>
Date: Thu, 26 Feb 2026 21:05:12 +0000
Subject: [PATCH] Fix 128-bit alien value truncation on x86-64

---
 src/compiler/x86-64/c-call.lisp |  6 +-----
 tests/alien-128.c               |  6 ++++++
 tests/alien.impure.lisp         | 23 +++++++++++++++++++++++
 3 files changed, 30 insertions(+), 5 deletions(-)
 create mode 100644 tests/alien-128.c

diff --git a/src/compiler/x86-64/c-call.lisp b/src/compiler/x86-64/c-call.lisp
index bf89c8d1d..22b400876 100644
--- a/src/compiler/x86-64/c-call.lisp
+++ b/src/compiler/x86-64/c-call.lisp
@@ -512,11 +512,7 @@ Floats are passed in integer registers."
               (lambda-vars arg)
               (cond ((and (alien-integer-type-p type)
                           (> (sb-alien::alien-integer-type-bits type) 64))
-                     ;; CLH: FIXME! This should really be
-                     ;; #xffffffffffffffff. nyef says: "Passing
-                     ;; 128-bit integers to ALIEN functions on x86-64
-                     ;; believed to be broken."
-                     (new-args `(logand ,arg #xffffffff))
+                     (new-args `(logand ,arg #xffffffffffffffff))
                      (new-args `(ash ,arg -64))
                      (new-arg-types (parse-alien-type '(unsigned 64) env))
                      (if (alien-integer-type-signed type)
diff --git a/tests/alien-128.c b/tests/alien-128.c
new file mode 100644
index 000000000..45804ad55
--- /dev/null
+++ b/tests/alien-128.c
@@ -0,0 +1,6 @@
+#include <stdint.h>
+
+typedef unsigned __int128 uint128;
+
+uint64_t echo_low_64(uint128 x) { return (uint64_t)x; }
+uint64_t echo_high_64(uint128 x) { return (uint64_t)(x >> 64); }
diff --git a/tests/alien.impure.lisp b/tests/alien.impure.lisp
index 1ba7e58ab..c73d88017 100644
--- a/tests/alien.impure.lisp
+++ b/tests/alien.impure.lisp
@@ -585,6 +585,29 @@
                 (alien-funcall (extern-alien (something) (function c-string c-string)) s))))))
     (assert (string= (funcall f "SBCL_HOME") (sb-ext:posix-getenv "SBCL_HOME")))))
 
+(unless (probe-file "alien-128.so")
+  (sb-ext:run-program "/bin/sh"
+                      '("run-compiler.sh" "-sbcl-pic" "-sbcl-shared"
+                        "-o" "alien-128.so" "alien-128.c")))
+
+(with-test (:name :alien-128bit-value-passing
+            :skipped-on (not :x86-64))
+  ;; Verify that both halves of a 128-bit alien value survive the FFI call.
+  ;; This guards against using a 32-bit mask instead of 64-bit when splitting
+  ;; the value into two 64-bit halves.
+  (load-shared-object (truename "alien-128.so"))
+  (let ((val (+ (ash #xDEADBEEFCAFEBABE 64) #x0123456789ABCDEF)))
+    (assert (= (alien-funcall
+                (extern-alien "echo_low_64"
+                              (function (unsigned 64) (unsigned 128)))
+                val)
+               #x0123456789ABCDEF))
+    (assert (= (alien-funcall
+                (extern-alien "echo_high_64"
+                              (function (unsigned 64) (unsigned 128)))
+                val)
+               #xDEADBEEFCAFEBABE))))
+
 (cl:in-package "SB-KERNEL")
 (test-util:with-test (:name :hash-consing)
   (assert (eq (parse-alien-type '(integer 9) nil)
-- 
2.43.0