Re: master: Fix immediate construction on loongarch, riscv
Stas Boukarev <[email protected]> Mon, 23 Feb 2026 04:07:11 +0300
| Newsgroups | gmane.lisp.steel-bank.cvs,gmane.lisp.steel-bank.devel |
|---|---|
| Message-ID | <CAF63=12BmVA-D5EEt2HdJ2ki_cLq0OD=t_yejx_WFxdjO00SaQ@mail.gmail.com> |
Some other options: regalloc might be forced to always assign non-descriptor regs to constants. But using two registers is attractive, maybe regalloc could be convinced to allocate two load-tns, the second one can be short lived. Maybe if nothing has to be spilled. On Sun, Feb 22, 2026 at 6:07 PM Charles Zhang <[email protected]> wrote: > > Depends on the microarch. But almost all immediates will require less than 6-8 instructions to materialize, hence why the gcc backend does it this way. The pattern matcher I implemented for sbcl is very stupid, so it’s going to be suboptimal in some cases. > > On Sunday, February 22, 2026, 3:55 PM, Stas Boukarev <[email protected]> wrote: > > But are 6/8 instructions better than a memory load? > > On Sun, Feb 22, 2026 at 5:54 PM Charles Zhang <[email protected]> wrote: > > > > clang moves from memory, but there’s a todo that’s been there since at least my port in 2019 that they should materialize large immediates without going through memory. GCC will do memory less instruction sequences. > > > > On Sunday, February 22, 2026, 3:52 PM, Stas Boukarev <[email protected]> wrote: > > > > And loongarch has lu32i.d lu52i.d instructions. > > > > On Sun, Feb 22, 2026 at 4:56 PM Stas Boukarev <[email protected]> wrote: > > > > > > I just want to get things moving, not optimize them. > > > clang just loads things from memory. > > > > > > On Sun, Feb 22, 2026 at 1:27 PM Charles Zhang <[email protected]> wrote: > > > > > > > > Just a note: If a temp register is allocated anyway, the generic case can just be done by loading the high 32 bits into the temp register, 32 bits into the target register, and adding. > > > > > > > > On Sunday, February 22, 2026, 3:59 AM, stassats via Sbcl-commits <[email protected]> wrote: > > > > > > > > The branch "master" has been updated in SBCL: > > > > via fcc91d51a57bc79921d0bbe4ff7b4f2abc372317 (commit) > > > > from 5bf2decc30b259ac8dfb3ac1d052fd90e14a4e84 (commit) > > > > > > > > - Log ----------------------------------------------------------------- > > > > commit fcc91d51a57bc79921d0bbe4ff7b4f2abc372317 > > > > Author: Stas Boukarev <[email protected]> > > > > Date: Sun Feb 22 03:26:59 2026 +0300 > > > > > > > > Fix immediate construction on loongarch, riscv > > > > > > > > Where large immediates are constructed by adding and shifting left. > > > > Intermediate values might not be properly tagged for a descriptor > > > > register. > > > > --- > > > > src/assembly/riscv/assem-rtns.lisp | 4 +- > > > > src/compiler/loongarch64/insts.lisp | 90 +++++++++++++++++++++---------------- > > > > src/compiler/riscv/insts.lisp | 87 +++++++++++++++++++---------------- > > > > src/compiler/riscv/vm.lisp | 9 ++-- > > > > 4 files changed, 109 insertions(+), 81 deletions(-) > > > > > > > > diff --git a/src/assembly/riscv/assem-rtns.lisp b/src/assembly/riscv/assem-rtns.lisp > > > > index f61171d1e..5a1b023b2 100644 > > > > --- a/src/assembly/riscv/assem-rtns.lisp > > > > +++ b/src/assembly/riscv/assem-rtns.lisp > > > > @@ -266,7 +266,7 @@ > > > > > > > > ;; Don't want these to overlap with C args. > > > > (:temp pa-temp non-descriptor-reg nl6-offset) > > > > - (:temp temp non-descriptor-reg nl7-offset)) > > > > + (:temp temp non-descriptor-reg tmp-offset)) > > > > (save-c-registers) > > > > > > > > (initialize-boxed-regs (list function arg-ptr nargs #+sb-thread tls-ptr)) > > > > @@ -313,7 +313,7 @@ > > > > (:temp value0-pass (any-reg) (result-reg-offset 0)) > > > > (:temp value1-pass (any-reg) (result-reg-offset 1)) > > > > (:temp pa-temp (any-reg) nl6-offset) > > > > - (:temp temp (any-reg) nl7-offset)) > > > > + (:temp temp (unsigned-reg) tmp-offset)) > > > > ;; The C stack frame and argument registers should have already been > > > > ;; set up in Lisp. > > > > > > > > diff --git a/src/compiler/loongarch64/insts.lisp b/src/compiler/loongarch64/insts.lisp > > > > index b74cc318d..df4f1c44c 100644 > > > > --- a/src/compiler/loongarch64/insts.lisp > > > > +++ b/src/compiler/loongarch64/insts.lisp > > > > @@ -783,10 +783,11 @@ > > > > (u-and-i-inst-immediate immediate) > > > > (cond ((zerop hi) > > > > (inst addi.d rd zero-tn immediate)) > > > > + ((zerop lo) > > > > + (inst lu12i.w rd hi)) > > > > (t > > > > - (inst lu12i.w rd hi) > > > > - (unless (zerop lo) > > > > - (inst addi.d rd rd lo)))))) > > > > + (inst lu12i.w t7-tn hi) > > > > + (inst addi.d rd t7-tn lo))))) > > > > > > > > ;;;; li should optimization by lu32i, lu52i > > > > (defun %li (reg value) > > > > @@ -799,63 +800,76 @@ > > > > (integer-length (integer-length value)) > > > > (2^k (ash 1 integer-length)) > > > > (2^.k-1 (ash 1 (1- integer-length))) > > > > - (complement (mod (lognot value) (ash 1 64)))) > > > > + (complement (mod (lognot value) (ash 1 64))) > > > > + ;; Intermediate unboxed values might be inappropriate for a > > > > + ;; boxed target register > > > > + (tmp t7-tn)) > > > > (cond ((zerop (logand (1+ value) value)) > > > > ;; Common special case: the immediate is of the form #xfff... > > > > - (inst addi.d reg zero-tn -1) > > > > - (unless (= integer-length 64) > > > > - (inst srli.d reg reg (- 64 integer-length)))) > > > > + (inst addi.d tmp zero-tn -1) > > > > + (aver (/= integer-length 64)) > > > > + (inst srli.d reg tmp (- 64 integer-length))) > > > > ((let ((delta (- 2^k value))) > > > > (and (typep (ash delta (- 64 integer-length)) 'short-immediate) > > > > (logand delta (1- delta)))) > > > > ;; Common special case: the immediate is of the form > > > > ;; #x00fff...00, where there are a small number of > > > > ;; zeroes at the end. > > > > - (inst addi.d reg zero-tn (ash (- value 2^k) (- 64 integer-length))) > > > > - (inst srli.d reg reg (- 64 integer-length))) > > > > + (inst addi.d tmp zero-tn (ash (- value 2^k) (- 64 integer-length))) > > > > + (inst srli.d reg tmp (- 64 integer-length))) > > > > ((zerop (logand complement (1+ complement))) > > > > ;; #xfffffff...00000 > > > > - (inst addi.d reg zero-tn -1) > > > > - (inst slli.d reg reg (integer-length complement))) > > > > + (inst addi.d tmp zero-tn -1) > > > > + (inst slli.d reg tmp (integer-length complement))) > > > > ((typep (- value 2^k) 'short-immediate) > > > > ;; Common special case: loading an immediate which is a > > > > ;; signed 12 bit constant away from a power of 2. > > > > (cond ((= integer-length 64) > > > > (inst addi.d reg zero-tn (- value 2^k))) > > > > (t > > > > - (inst addi.d reg zero-tn 1) > > > > - (inst slli.d reg reg integer-length) > > > > - (inst addi.d reg reg (- value 2^k))))) > > > > + (inst addi.d tmp zero-tn 1) > > > > + (inst slli.d tmp tmp integer-length) > > > > + (inst addi.d reg tmp (- value 2^k))))) > > > > ((typep (- value 2^.k-1) 'short-immediate) > > > > ;; Common special case: loading an immediate which is a > > > > ;; signed 12 bit constant away from a power of 2. > > > > - (inst addi.d reg zero-tn 1) > > > > - (inst slli.d reg reg (1- integer-length)) > > > > - (unless (= value 2^.k-1) > > > > - (inst addi.d reg reg (- value 2^.k-1)))) > > > > + (inst addi.d tmp zero-tn 1) > > > > + (cond ((= value 2^.k-1) > > > > + (inst slli.d reg tmp (1- integer-length))) > > > > + (t > > > > + (inst slli.d tmp tmp (1- integer-length)) > > > > + (inst addi.d reg tmp (- value 2^.k-1))))) > > > > (t > > > > ;; The "generic" case. > > > > ;; Load in the first 31 non zero most significant bits. > > > > - (let ((chunk (ldb (byte 12 (- integer-length 31)) value))) > > > > - (inst lu12i.w reg (ldb (byte 20 (- integer-length 19)) value)) > > > > - (cond ((= (1- (ash 1 12)) chunk) > > > > - (inst addi.d reg reg (1- (ash 1 11))) > > > > - (inst addi.d reg reg (1- (ash 1 11))) > > > > - (inst addi.d reg reg 1)) > > > > - ((logbitp 11 chunk) > > > > - (inst addi.d reg reg (1- (ash 1 11))) > > > > - (inst addi.d reg reg (- chunk (1- (ash 1 11))))) > > > > - (t > > > > - (inst addi.d reg reg chunk)))) > > > > - ;; Now we need to load in the rest of the bits properly, in > > > > - ;; chunks of 11 to avoid sign extension. > > > > - (do ((i (- integer-length 31) (- i 11))) > > > > - ((< i 11) > > > > - (inst slli.d reg reg i) > > > > - (unless (zerop (ldb (byte i 0) value)) > > > > - (inst addi.d reg reg (ldb (byte i 0) value)))) > > > > - (inst slli.d reg reg 11) > > > > - (inst addi.d reg reg (ldb (byte 11 (- i 11)) value))))))) > > > > + (let ((chunk (ldb (byte 12 (- integer-length 31)) value)) > > > > + instructions) > > > > + (flet ((add-inst (m d o1 o2) > > > > + ;; Gotta know which instruction is the last one > > > > + ;; to put REG as its destination > > > > + (push (list m d o1 o2) instructions))) > > > > + (inst lu12i.w tmp (ldb (byte 20 (- integer-length 19)) value)) > > > > + (cond ((= (1- (ash 1 12)) chunk) > > > > + (inst addi.d tmp tmp (1- (ash 1 11))) > > > > + (inst addi.d tmp tmp (1- (ash 1 11))) > > > > + (add-inst 'addi.d tmp tmp 1)) > > > > + ((logbitp 11 chunk) > > > > + (inst addi.d tmp tmp (1- (ash 1 11))) > > > > + (add-inst 'addi.d tmp tmp (- chunk (1- (ash 1 11))))) > > > > + (t > > > > + (add-inst 'addi.d tmp tmp chunk))) > > > > + ;; Now we need to load in the rest of the bits properly, in > > > > + ;; chunks of 11 to avoid sign extension. > > > > + (do ((i (- integer-length 31) (- i 11))) > > > > + ((< i 11) > > > > + (add-inst 'slli.d tmp tmp i) > > > > + (unless (zerop (ldb (byte i 0) value)) > > > > + (add-inst 'addi.d tmp tmp (ldb (byte i 0) value)))) > > > > + (add-inst 'slli.d tmp tmp 11) > > > > + (add-inst 'addi.d tmp tmp (ldb (byte 11 (- i 11)) value))) > > > > + (setf (second (car instructions)) reg) > > > > + (loop for (m d o1 o2) in (reverse instructions) > > > > + do (inst* m d o1 o2)))))))) > > > > (fixup > > > > (inst lu12i.w reg value) > > > > (inst addi.d reg reg value)))) > > > > diff --git a/src/compiler/riscv/insts.lisp b/src/compiler/riscv/insts.lisp > > > > index 04ff8098c..e7135c811 100644 > > > > --- a/src/compiler/riscv/insts.lisp > > > > +++ b/src/compiler/riscv/insts.lisp > > > > @@ -19,7 +19,7 @@ > > > > sb-vm::registers sb-vm::float-registers > > > > sb-vm::zero > > > > sb-vm::zero-offset > > > > - sb-vm::lip-tn sb-vm::zero-tn > > > > + sb-vm::lip-tn sb-vm::zero-tn sb-vm::tmp-tn > > > > sb-vm::code-tn > > > > sb-vm::tn-byte-offset > > > > ;; Types > > > > @@ -441,10 +441,11 @@ > > > > (u-and-i-inst-immediate immediate) > > > > (cond ((zerop hi) > > > > (inst addi rd zero-tn immediate)) > > > > + ((zerop lo) > > > > + (inst lui rd hi)) > > > > (t > > > > - (inst lui rd hi) > > > > - (unless (zerop lo) > > > > - (inst addi rd rd lo)))))) > > > > + (inst lui tmp-tn hi) > > > > + (inst addi rd tmp-tn lo))))) > > > > > > > > (defun %li (reg value) > > > > (etypecase value > > > > @@ -457,63 +458,73 @@ > > > > (integer-length (integer-length value)) > > > > (2^k (ash 1 integer-length)) > > > > (2^.k-1 (ash 1 (1- integer-length))) > > > > - (complement (mod (lognot value) (ash 1 64)))) > > > > + (complement (mod (lognot value) (ash 1 64))) > > > > + (tmp tmp-tn)) > > > > (cond ((zerop (logand (1+ value) value)) > > > > ;; Common special case: the immediate is of the form #xfff... > > > > (inst addi reg zero-tn -1) > > > > - (unless (= integer-length 64) > > > > - (inst srli reg reg (- 64 integer-length)))) > > > > + (inst srli reg reg (- 64 integer-length))) > > > > ((let ((delta (- 2^k value))) > > > > (and (typep (ash delta (- 64 integer-length)) 'short-immediate) > > > > (logand delta (1- delta)))) > > > > ;; Common special case: the immediate is of the form > > > > ;; #x00fff...00, where there are a small number of > > > > ;; zeroes at the end. > > > > - (inst addi reg zero-tn (ash (- value 2^k) (- 64 integer-length))) > > > > - (inst srli reg reg (- 64 integer-length))) > > > > + (inst addi tmp zero-tn (ash (- value 2^k) (- 64 integer-length))) > > > > + (inst srli reg tmp (- 64 integer-length))) > > > > ((zerop (logand complement (1+ complement))) > > > > ;; #xfffffff...00000 > > > > - (inst addi reg zero-tn -1) > > > > - (inst slli reg reg (integer-length complement))) > > > > + (inst addi tmp zero-tn -1) > > > > + (inst slli reg tmp (integer-length complement))) > > > > ((typep (- value 2^k) 'short-immediate) > > > > ;; Common special case: loading an immediate which is a > > > > ;; signed 12 bit constant away from a power of 2. > > > > (cond ((= integer-length 64) > > > > (inst addi reg zero-tn (- value 2^k))) > > > > (t > > > > - (inst addi reg zero-tn 1) > > > > - (inst slli reg reg integer-length) > > > > - (inst addi reg reg (- value 2^k))))) > > > > + (inst addi tmp zero-tn 1) > > > > + (inst slli tmp tmp integer-length) > > > > + (inst addi reg tmp (- value 2^k))))) > > > > ((typep (- value 2^.k-1) 'short-immediate) > > > > ;; Common special case: loading an immediate which is a > > > > ;; signed 12 bit constant away from a power of 2. > > > > - (inst addi reg zero-tn 1) > > > > - (inst slli reg reg (1- integer-length)) > > > > - (unless (= value 2^.k-1) > > > > - (inst addi reg reg (- value 2^.k-1)))) > > > > + (inst addi tmp zero-tn 1) > > > > + (cond ((= value 2^.k-1) > > > > + (inst slli reg tmp (1- integer-length))) > > > > + (t > > > > + (inst slli tmp tmp (1- integer-length)) > > > > + (inst addi reg tmp (- value 2^.k-1))))) > > > > (t > > > > ;; The "generic" case. > > > > ;; Load in the first 31 non zero most significant bits. > > > > - (let ((chunk (ldb (byte 12 (- integer-length 31)) value))) > > > > - (inst lui reg (ldb (byte 20 (- integer-length 19)) value)) > > > > - (cond ((= (1- (ash 1 12)) chunk) > > > > - (inst addi reg reg (1- (ash 1 11))) > > > > - (inst addi reg reg (1- (ash 1 11))) > > > > - (inst addi reg reg 1)) > > > > - ((logbitp 11 chunk) > > > > - (inst addi reg reg (1- (ash 1 11))) > > > > - (inst addi reg reg (- chunk (1- (ash 1 11))))) > > > > - (t > > > > - (inst addi reg reg chunk)))) > > > > - ;; Now we need to load in the rest of the bits properly, in > > > > - ;; chunks of 11 to avoid sign extension. > > > > - (do ((i (- integer-length 31) (- i 11))) > > > > - ((< i 11) > > > > - (inst slli reg reg i) > > > > - (unless (zerop (ldb (byte i 0) value)) > > > > - (inst addi reg reg (ldb (byte i 0) value)))) > > > > - (inst slli reg reg 11) > > > > - (inst addi reg reg (ldb (byte 11 (- i 11)) value))))))) > > > > + (let ((chunk (ldb (byte 12 (- integer-length 31)) value)) > > > > + instructions) > > > > + (flet ((add-inst (m d o1 o2) > > > > + ;; Gotta know which instruction is the last one > > > > + ;; to put REG as its destination > > > > + (push (list m d o1 o2) instructions))) > > > > + (inst lui tmp (ldb (byte 20 (- integer-length 19)) value)) > > > > + (cond ((= (1- (ash 1 12)) chunk) > > > > + (inst addi tmp tmp (1- (ash 1 11))) > > > > + (inst addi tmp tmp (1- (ash 1 11))) > > > > + (add-inst 'addi tmp tmp 1)) > > > > + ((logbitp 11 chunk) > > > > + (inst addi tmp tmp (1- (ash 1 11))) > > > > + (add-inst 'addi tmp tmp (- chunk (1- (ash 1 11))))) > > > > + (t > > > > + (add-inst 'addi tmp tmp chunk))) > > > > + ;; Now we need to load in the rest of the bits properly, in > > > > + ;; chunks of 11 to avoid sign extension. > > > > + (do ((i (- integer-length 31) (- i 11))) > > > > + ((< i 11) > > > > + (add-inst 'slli tmp tmp i) > > > > + (unless (zerop (ldb (byte i 0) value)) > > > > + (add-inst 'addi tmp tmp (ldb (byte i 0) value)))) > > > > + (add-inst 'slli tmp tmp 11) > > > > + (add-inst 'addi tmp tmp (ldb (byte 11 (- i 11)) value))) > > > > + (setf (second (car instructions)) reg) > > > > + (loop for (m d o1 o2) in (reverse instructions) > > > > + do (inst* m d o1 o2)))))))) > > > > (fixup > > > > (inst lui reg value) > > > > (inst addi reg reg value)))) > > > > diff --git a/src/compiler/riscv/vm.lisp b/src/compiler/riscv/vm.lisp > > > > index c0a58f0ae..3950e56ec 100644 > > > > --- a/src/compiler/riscv/vm.lisp > > > > +++ b/src/compiler/riscv/vm.lisp > > > > @@ -65,7 +65,8 @@ > > > > (defreg l0 22) ; s6 > > > > (defreg nl6 23) ; s7 > > > > (defreg l1 24) ; s8 > > > > - (defreg nl7 25) ; s9 > > > > + ;; A register needed to load constants into descriptor-regs > > > > + (defreg tmp 25) ; s9 > > > > (defreg #-sb-thread l2 #+sb-thread thread 26) ; s10 > > > > > > > > (defreg cfunc 27) ; s11 > > > > @@ -74,7 +75,7 @@ > > > > (defreg code 30) ; t5 > > > > (defreg nargs 31) ; t6 > > > > > > > > - (defregset non-descriptor-regs nl0 nl1 nl2 ra nl3 nl4 nl5 nl6 nl7 nargs nfp cfunc) > > > > + (defregset non-descriptor-regs nl0 nl1 nl2 ra nl3 nl4 nl5 nl6 nargs nfp cfunc) > > > > (defregset descriptor-regs a0 a1 a2 a3 a4 a5 l0 l1 #-sb-thread l2 ocfp lexenv) > > > > (defregset reserve-descriptor-regs lexenv) > > > > (defregset reserve-non-descriptor-regs cfunc) > > > > @@ -210,7 +211,9 @@ > > > > (defregtn ocfp any-reg) > > > > (defregtn nfp any-reg) > > > > > > > > - (defregtn ra any-reg)) > > > > + (defregtn ra any-reg) > > > > + > > > > + (defregtn tmp unsigned-reg)) > > > > > > > > ;;; If VALUE can be represented as an immediate constant, then return the > > > > ;;; appropriate SC number, otherwise return NIL. > > > > > > > > ----------------------------------------------------------------------- > > > > > > > > > > > > hooks/post-receive > > > > -- > > > > SBCL > > > > > > > > > > > > _______________________________________________ > > > > Sbcl-commits mailing list > > > > [email protected] > > > > https://lists.sourceforge.net/lists/listinfo/sbcl-commits _______________________________________________ Sbcl-commits mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/sbcl-commits