master: Get rid of a redundant sign-extend in SB-ALIEN:DEREF

snuglas via Sbcl-commits <[email protected]> Wed, 08 Jul 2026 02:22:34 +0000
Newsgroups gmane.lisp.steel-bank.cvs
Message-ID <[email protected]>
The branch "master" has been updated in SBCL:
       via  0f19605f3d851c128e78baa623362b1baaa53018 (commit)
      from  da33c975a63c82ff3161e801dec285d67a791d51 (commit)

- Log -----------------------------------------------------------------
commit 0f19605f3d851c128e78baa623362b1baaa53018
Author: Douglas Katzman <[email protected]>
Date:   Wed Jul 8 02:22:23 2026 +0000

    Get rid of a redundant sign-extend in SB-ALIEN:DEREF
---
 src/compiler/aliencomp.lisp      | 50 +++++++++++++++++++++++++++++++++++++++-
 tests/x86-64-codegen.impure.lisp |  7 ++++++
 2 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/src/compiler/aliencomp.lisp b/src/compiler/aliencomp.lisp
index 02c4a6d7d..4222f975b 100644
--- a/src/compiler/aliencomp.lisp
+++ b/src/compiler/aliencomp.lisp
@@ -534,6 +534,38 @@
 
 ;;;; NATURALIZE/DEPORT/EXTRACT/DEPOSIT magic
 
+;;; The implementation of (integer :naturalize-gen) in {arm64,x86-64}/c-call
+;;; deals with function return convention where a result smaller than a full register
+;;; can leave trash in all the unused bits. Naive application of that unfortunately
+;;; pessimizes (DEREF x) where X is a pointer to integer, because memory loads all
+;;; performs sign-extension, then naturalize sign-extends again as shown:
+;;;     4C0FBE30         MOVSX R14, BYTE PTR [RAX]
+;;;     490FBED6         MOVSX RDX, R14B
+;;;
+;;;     4C0FBF30         MOVSX R14, WORD PTR [RAX]
+;;;     490FBFD6         MOVSX RDX, R14W
+;;;
+;;;     4C6330           MOVSX R14, DWORD PTR [RAX]
+;;;     4963D6           MOVSX RDX, R14D
+;;; And similarly for the arm64 disassembly.
+;;;
+;;; There is no way to undo the bad asm code (other than perhaps a peephole optimization)
+;;; because the NATURALIZE method receives zero semantic information - it does not know
+;;; that the underlying alien value came from SAP-REF. Its lexical var named ALIEN receives
+;;; the literal symbol ALIEN, and not the form which generated it. So the only place to infer
+;;; that naturalize is effectively a no-op is in the naturalize transform.
+#+(or arm64 x86-64)
+(defun is-sign-extending-load-p (object type)
+  (let ((bits (sb-alien::alien-integer-type-bits type)))
+    (eq (case bits
+          (8  'sb-sys:signed-sap-ref-8)
+          (16 'sb-sys:signed-sap-ref-16)
+          (32 'sb-sys:signed-sap-ref-32))
+        (combination-is (lvar-use object)
+                        '(sb-sys:signed-sap-ref-8
+                          sb-sys:signed-sap-ref-16
+                          sb-sys:signed-sap-ref-32)))))
+
 (flet ((%computed-lambda (compute-lambda type)
          (declare (type function compute-lambda))
          (unless (constant-lvar-p type)
@@ -546,7 +578,23 @@
            (error (condition)
                   (compiler-error "~A" condition)))))
   (deftransform naturalize ((object type))
-    (%computed-lambda #'compute-naturalize-lambda type))
+    ;; The problem detailed at IS-SIGN-EXTENDING-LOAD-P is probably true for any ABI with
+    ;; the same non-requirement to clear upper bits of smaller-than-register results.
+    ;; And I suspect that this logic is generally sound and need not be guarded
+    ;; by a reader conditional, however I am not that brave.
+    ;; Arguably this could go into the DEREF transform, but as it happens I have other
+    ;; diffs affecting DEREF and I want to keep them separate for reasons.
+    ;; (Maybe not good reasons. We'll see after they're both done.)
+    (cond #+(or arm64 x86-64)
+          ((and (combination-p (lvar-use object))
+                (constant-lvar-p type)
+                (let ((type (lvar-value type)))
+                  (and (alien-integer-type-p type)
+                       (alien-integer-type-signed type)
+                       (is-sign-extending-load-p object type))))
+           '(lambda (alien ignore) (declare (ignore ignore)) alien))
+          (t
+           (%computed-lambda #'compute-naturalize-lambda type))))
   (deftransform deport ((alien type))
     (%computed-lambda #'compute-deport-lambda type))
   (deftransform deport-alloc ((alien type))
diff --git a/tests/x86-64-codegen.impure.lisp b/tests/x86-64-codegen.impure.lisp
index 95c26b6e1..e8672ecaa 100644
--- a/tests/x86-64-codegen.impure.lisp
+++ b/tests/x86-64-codegen.impure.lisp
@@ -1476,3 +1476,10 @@
                    (search "FROZENTHING" line))
           (setq saw-layout t))))
     (assert (and saw-layout (eql comparisons 1)))))
+
+(with-test (:name :alien-deref-sign-extend-once-only)
+  (let ((lines (disassembly-lines
+                '(lambda ()
+                  (declare (optimize (debug 0)))
+                  (deref (alien-funcall (extern-alien "f" (function (* char)))) 0)))))
+    (assert (eql (loop for line in lines count (search "MOVSX" line)) 1))))

-----------------------------------------------------------------------


hooks/post-receive
-- 
SBCL