master: Add sb-vm:%atomic-exchange

snuglas via Sbcl-commits <[email protected]>
Newsgroups gmane.lisp.steel-bank.cvs
Message-ID <[email protected]>
The branch "master" has been updated in SBCL:
       via  341713c776046393dfe3dc15031c7ca3fde8488c (commit)
      from  0a17885e2733eea227cf5b5d404cd5bb8bbd8c2e (commit)

- Log -----------------------------------------------------------------
commit 341713c776046393dfe3dc15031c7ca3fde8488c
Author: Douglas Katzman <[email protected]>
Date:   Wed Aug 5 20:53:48 2026 +0000

    Add sb-vm:%atomic-exchange
---
 src/code/setf-funs.lisp       | 40 ++++++++++++++++++++++++++++++++++++++++
 src/compiler/arm64/insts.lisp |  5 +++++
 tests/atomic-xchg.pure.lisp   | 28 ++++++++++++++++++++++++++++
 3 files changed, 73 insertions(+)

diff --git a/src/code/setf-funs.lisp b/src/code/setf-funs.lisp
index 1750e76bf..ab49e56f0 100644
--- a/src/code/setf-funs.lisp
+++ b/src/code/setf-funs.lisp
@@ -78,3 +78,43 @@
   get subseq
   ;; Have explicit redundant definitions...
   bit sbit aref gethash)
+
+(in-package "SB-VM")
+(export '%atomic-exchange)
+(defmacro %atomic-exchange (place value)
+  ;; Given PLACE which is of a very restricted kind - (STRUCT-SLOT-ACCESSOR instance)
+  ;; atomically swap VALUE and return the old, unconditionally.
+  (destructuring-bind (accessor instance) place
+    (let* ((info (the (cons defstruct-description defstruct-slot-description)
+                      (info :function :source-transform accessor)))
+           (dsd (cdr info))
+           (disp (- (ash (+ instance-slots-offset (dsd-index dsd)) word-shift)
+                    instance-pointer-lowtag))
+           (type (dsd-type dsd)))
+      (declare (ignorable disp type))
+      #+(or arm64 riscv64 x86-64)
+      (multiple-value-bind (sc primtype)
+          (ecase (dsd-raw-type dsd)
+            (t (if (eq type 'fixnum)
+                   (values 'any-reg 'tagged-num)
+                   (values 'descriptor-reg 't)))
+            (signed-word (values 'signed-reg 'signed-num))
+            (word (values 'unsigned-reg 'unsigned-num)))
+        `(truly-the ,type
+          (inline-vop (((instance descriptor-reg t) (the ,(dd-name (car info)) ,instance))
+                       ((val ,sc ,primtype) (the ,type ,value))
+                       ((temp)))
+                      ((res ,sc ,primtype))
+           #+arm64 (progn (inst add temp instance ,disp)
+                          (inst swpal val res temp))
+           #+riscv (progn (inst addi temp instance ,disp)
+                          (inst amoswap res val temp :aq :rl))
+           #+x86-64 (progn (move temp val)
+                           (inst xchg temp (ea ,disp instance)) ; LOCK is implicit
+                           (move res temp)))))
+      #-(or arm64 riscv64 x86-64)
+      `(let* ((.instance. ,instance) ; GET-CAS-EXPANSION exists. I don't care
+              (.new. ,value)
+              (.old. (,accessor .instance.)))
+         (loop until (eq .old. (setf .old. (cas ,place .old. .new.))))
+         .old.))))
diff --git a/src/compiler/arm64/insts.lisp b/src/compiler/arm64/insts.lisp
index 21d736741..4fcee845d 100644
--- a/src/compiler/arm64/insts.lisp
+++ b/src/compiler/arm64/insts.lisp
@@ -2032,6 +2032,11 @@
 (def-ldatomic ldseta #b001100 1 0)
 (def-ldatomic ldsetal #b001100 1 1)
 
+(def-ldatomic swp #b100000 0 0)
+(def-ldatomic swpa #b100000 1 0)
+(def-ldatomic swpal #b100000 1 1)
+(def-ldatomic swpl #b100000 0 1)
+
 (define-instruction-format (ldaddb 32)
   (size :field (byte 2 30))
   (op2 :field (byte 6 24) :value #b111000)
diff --git a/tests/atomic-xchg.pure.lisp b/tests/atomic-xchg.pure.lisp
new file mode 100644
index 000000000..53c04b8d7
--- /dev/null
+++ b/tests/atomic-xchg.pure.lisp
@@ -0,0 +1,28 @@
+(defstruct s (f 0 :type fixnum) (w 0 :type word) (sw 0 :type signed-word))
+
+(defun swapw (mystruct v) (sb-vm:%atomic-exchange (s-w mystruct) v))
+(defun swapsw (mystruct v) (sb-vm:%atomic-exchange (s-sw mystruct) v))
+(defun swapf (mystruct v) (sb-vm:%atomic-exchange (s-f mystruct) v))
+
+(mapc 'compile '(swapw swapsw swapf))
+
+(with-test (:name :atomic-exchange-word)
+  (let ((s (make-s)))
+    (assert (= (swapsw s 5) 0))
+    (assert (= (s-sw s) 5))
+    (assert (= (swapsw s 1000) 5))
+    (assert (= (s-sw s) 1000))))
+
+(with-test (:name :atomic-exchange-signed-word)
+  (let ((s (make-s)))
+    (assert (= (swapsw s 5) 0))
+    (assert (= (s-sw s) 5))
+    (assert (= (swapsw s -100) 5))
+    (assert (= (s-sw s) -100))))
+
+(with-test (:name :atomic-exchange-fixnum)
+  (let ((s (make-s)))
+    (assert (= (swapf s 5) 0))
+    (assert (= (s-f s) 5))
+    (assert (= (swapf s -100) 5))
+    (assert (= (s-f s) -100))))

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


hooks/post-receive
-- 
SBCL
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.