[gcc r17-3236] AVR: Support saturating fixed-point shifts.

Georg-Johann Lay via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:5ad734dea0161726c74fb2c42a07f0fe7f8e1e02

commit r17-3236-g5ad734dea0161726c74fb2c42a07f0fe7f8e1e02
Author: Georg-Johann Lay <[email protected]>
Date:   Wed Aug 12 20:26:37 2026 +0200

    AVR: Support saturating fixed-point shifts.
    
    This patch adds support for saturated shift left of all the
    fixed-point modes -- except for the Reduced Core where no
    64-bit shifts are added.
    
    avr-fixed.md adds insns to model the reduced register foot
    prints of all the shifts up to 4 bytes.
    
    gcc/
            * config/avr/avr-fixed.md (ALL12QA): New mode iterator.
            (sat_ashl): New code iterator.
            (usashluqq3, ssashlqq3, usashluhq3, ssashlhq3, usashluha3)
            (ssashlha3, usashlusq3, ssashlsq3, usashlusa3, ssashlsa3)
            (*usashluqq3, *ssashlqq3, *usashluhq3, *ssashlhq3)
            (*usashluha3, *ssashlha3, *usashlusq3, *ssashlsq3, *usashlusa3)
            (*ssashlsa3): New insns.
            * config/avr/avr.md (code_stdname): Also map ss_ashift, us_ashift.
    libgcc/
            * config/avr/t-avr (LIB1ASMFUNCS): Add _usshift_1, _usshift_2,
            _usshift_4, _ssshift_1, _ssshift_2, _ssshift_4.
            (FUNCS_notiny): Add _usshift_8, _ssshift_8.
            (LIB2FUNCS_EXCLUDE): Add all the default libgcc saturated shifts.
            * config/avr/lib1funcs-fixed.S: Support saturated ashl for
            all fixed-point modes.
    gcc/testsuite/
            * gcc.target/avr/fx.h: New file.
            * gcc.target/avr/sat-shift-s.c: New test.
            * gcc.target/avr/sat-shift-u.c: New test.

Diff:
---
 gcc/config/avr/avr-fixed.md                |  65 ++++++
 gcc/config/avr/avr.md                      |   2 +-
 gcc/testsuite/gcc.target/avr/fx.h          |  56 +++++
 gcc/testsuite/gcc.target/avr/sat-shift-s.c |  25 +++
 gcc/testsuite/gcc.target/avr/sat-shift-u.c |  25 +++
 libgcc/config/avr/lib1funcs-fixed.S        | 339 +++++++++++++++++++++++++++++
 libgcc/config/avr/t-avr                    |   7 +
 7 files changed, 518 insertions(+), 1 deletion(-)

diff --git a/gcc/config/avr/avr-fixed.md b/gcc/config/avr/avr-fixed.md
index 581afb035b0c..e9813ccea6d4 100644
--- a/gcc/config/avr/avr-fixed.md
+++ b/gcc/config/avr/avr-fixed.md
@@ -26,6 +26,8 @@
 (define_mode_iterator ALL4A  [SA USA])
 (define_mode_iterator ALL2QA [HQ UHQ HA UHA])
 (define_mode_iterator ALL4QA [SQ USQ SA USA])
+(define_mode_iterator ALL12QA [ QQ   HQ  HA
+                               UQQ  UHQ UHA])
 (define_mode_iterator ALL124QA [ QQ   HQ  HA  SA  SQ
                                 UQQ  UHQ UHA USA USQ])
 
@@ -611,3 +613,66 @@
   "reload_completed"
   "%~call __round<mode>3"
   [(set_attr "type" "xcall")])
+
+
+;******************************************************************************
+;** Saturated Shift Left
+;******************************************************************************
+
+;; These functions are default ABI but are clobbering less registers.
+
+(define_code_iterator sat_ashl  [us_ashift ss_ashift])
+
+;; "usashluqq3"  "ssashlqq3"
+;; "usashluhq3"  "ssashlhq3"
+;; "usashluha3"  "ssashlha3"
+(define_insn_and_split "<code_stdname><mode>3"
+  [(set (match_operand:ALL12QA 0 "register_operand"                  "={r24}")
+        (sat_ashl:ALL12QA (match_operand:ALL12QA 1 "register_operand" "{r24}")
+                          (match_operand:QI 2 "register_operand"      "{r22}")))
+   (clobber (match_scratch:QI 3                                      "={r22}"))]
+  "SIGNED_FIXED_POINT_MODE_P (<MODE>mode) == (<CODE> == SS_ASHIFT)"
+  "#"
+  "&& reload_completed"
+  [(scratch)]
+  { DONE_ADD_CCC })
+
+;; "*usashluqq3"  "*ssashlqq3"
+;; "*usashluhq3"  "*ssashlhq3"
+;; "*usashluha3"  "*ssashlha3"
+(define_insn "*<code_stdname><mode>3"
+  [(set (reg:ALL12QA                   REG_24)
+        (sat_ashl:ALL12QA (reg:ALL12QA REG_24)
+                          (reg:QI      REG_22)))
+   (clobber (reg:QI REG_22))
+   (clobber (reg:CC REG_CC))]
+  "reload_completed
+   && SIGNED_FIXED_POINT_MODE_P (<MODE>mode) == (<CODE> == SS_ASHIFT)"
+  "%~call __<code_stdname><mode>3"
+  [(set_attr "type" "xcall")])
+
+;; "usashlusq3"  "ssashlsq3"
+;; "usashlusa3"  "ssashlsa3"
+(define_insn_and_split "<code_stdname><mode>3"
+  [(set (match_operand:ALL4QA 0 "register_operand"                 "={r22}")
+        (sat_ashl:ALL4QA (match_operand:ALL4QA 1 "register_operand" "{r22}")
+                         (match_operand:QI 2 "register_operand"     "{r20}")))
+   (clobber (match_scratch:QI 3                                    "={r20}"))]
+  "SIGNED_FIXED_POINT_MODE_P (<MODE>mode) == (<CODE> == SS_ASHIFT)"
+  "#"
+  "&& reload_completed"
+  [(scratch)]
+  { DONE_ADD_CCC })
+
+;; "*usashlusq3"  "*ssashlsq3"
+;; "*usashlusa3"  "*ssashlsa3"
+(define_insn "*<code_stdname><mode>3"
+  [(set (reg:ALL4QA                REG_22)
+        (sat_ashl:ALL4QA (reg:ALL4QA REG_22)
+                         (reg:QI     REG_20)))
+   (clobber (reg:QI REG_20))
+   (clobber (reg:CC REG_CC))]
+  "reload_completed
+   && SIGNED_FIXED_POINT_MODE_P (<MODE>mode) == (<CODE> == SS_ASHIFT)"
+  "%~call __<code_stdname><mode>3"
+  [(set_attr "type" "xcall")])
diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md
index 142e9d160080..7fa07622c91d 100644
--- a/gcc/config/avr/avr.md
+++ b/gcc/config/avr/avr.md
@@ -382,7 +382,7 @@
 
 ;; Map RTX code to its standard insn name
 (define_code_attr code_stdname
-  [(ashift   "ashl")
+  [(ashift   "ashl")  (ss_ashift "ssashl")  (us_ashift "usashl")
    (ashiftrt "ashr")
    (lshiftrt "lshr")
    (ior      "ior")
diff --git a/gcc/testsuite/gcc.target/avr/fx.h b/gcc/testsuite/gcc.target/avr/fx.h
new file mode 100644
index 000000000000..d1354876e911
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/fx.h
@@ -0,0 +1,56 @@
+#ifndef FX_H
+#define FX_H
+
+#include <stdfix.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <avr/pgmspace.h>
+
+#define NI __attribute((noipa))
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
+
+typedef short fract hr_t;
+typedef unsigned short fract uhr_t;
+typedef short accum hk_t;
+typedef unsigned short accum uhk_t;
+typedef fract r_t;
+typedef unsigned fract ur_t;
+
+typedef sat short fract sat_hr_t;
+typedef sat unsigned short fract sat_uhr_t;
+typedef sat short accum sat_hk_t;
+typedef sat short unsigned accum sat_uhk_t;
+typedef sat fract sat_r_t;
+typedef sat unsigned fract sat_ur_t;
+
+typedef accum k_t;
+typedef unsigned accum uk_t;
+typedef long fract lr_t;
+typedef long unsigned fract ulr_t;
+
+typedef sat accum sat_k_t;
+typedef sat unsigned accum sat_uk_t;
+typedef sat long fract sat_lr_t;
+typedef sat long unsigned fract sat_ulr_t;
+
+typedef long accum lk_t;
+typedef unsigned long accum ulk_t;
+typedef long long accum llk_t;
+typedef unsigned long long accum ullk_t;
+typedef long long fract llr_t;
+typedef long long unsigned fract ullr_t;
+
+typedef sat long accum sat_lk_t;
+typedef sat unsigned long accum sat_ulk_t;
+typedef sat long long accum sat_llk_t;
+typedef sat unsigned long long accum sat_ullk_t;
+typedef sat long long fract sat_llr_t;
+typedef sat long long unsigned fract sat_ullr_t;
+
+#define hk_1   (1u << __HA_FBIT__)
+#define uhk_1  (1u << __UHA_FBIT__)
+
+#define k_1   (1ul << __SA_FBIT__)
+#define uk_1  (1ul << __USA_FBIT__)
+
+#endif /* FX_H */
diff --git a/gcc/testsuite/gcc.target/avr/sat-shift-s.c b/gcc/testsuite/gcc.target/avr/sat-shift-s.c
new file mode 100644
index 000000000000..dcd0d03dcfea
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/sat-shift-s.c
@@ -0,0 +1,25 @@
+/* { dg-do link } */
+/* { dg-additional-options "-std=gnu99" } */
+
+#include "fx.h"
+
+#define MK_FUN(fx)					\
+  fx##_t sat_shift_##fx (sat_##fx##_t a, uint8_t x)	\
+  {							\
+    return a << x;					\
+  }
+
+MK_FUN (hk)
+MK_FUN (k)
+MK_FUN (lk)
+MK_FUN (llk)
+
+MK_FUN (hr)
+MK_FUN (r)
+MK_FUN (lr)
+MK_FUN (llr)
+
+int main (void)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/avr/sat-shift-u.c b/gcc/testsuite/gcc.target/avr/sat-shift-u.c
new file mode 100644
index 000000000000..1adf3bfc5a8b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/sat-shift-u.c
@@ -0,0 +1,25 @@
+/* { dg-do link } */
+/* { dg-additional-options "-std=gnu99" } */
+
+#include "fx.h"
+
+#define MK_FUN(fx)					\
+  fx##_t sat_shift_##fx (sat_##fx##_t a, uint8_t x)	\
+  {							\
+    return a << x;					\
+  }
+
+MK_FUN (uhk)
+MK_FUN (uk)
+MK_FUN (ulk)
+MK_FUN (ullk)
+
+MK_FUN (uhr)
+MK_FUN (ur)
+MK_FUN (ulr)
+MK_FUN (ullr)
+
+int main (void)
+{
+  return 0;
+}
diff --git a/libgcc/config/avr/lib1funcs-fixed.S b/libgcc/config/avr/lib1funcs-fixed.S
index d7a297064b4a..142b832c2a18 100644
--- a/libgcc/config/avr/lib1funcs-fixed.S
+++ b/libgcc/config/avr/lib1funcs-fixed.S
@@ -2632,3 +2632,342 @@ DEFUN __ret
     ret
 ENDF  __ret
 #endif /* L_ret */
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Saturated Shift Left, 1 Byte
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+#define A0 24
+#define Off r22
+
+#ifdef L_usshift_1
+;;; (set (reg:UQQ 24)
+;;;      (us_ashift:UQQ (reg:UQQ 24)
+;;;                     (reg:QI 22)))
+;;; T = T
+;;; Clobbers: R22.
+
+DEFUN __usashluqq3
+    tst     Off
+    breq 9f
+.Loop_bits:
+    lsl     A0
+    brcs .Lsat
+    dec     Off
+    brne .Loop_bits
+    ret
+
+.Lsat:
+    ldi     A0, 0xff
+9:  ret
+ENDF __usashluqq3
+#endif /* L_usshift_1 */
+
+#ifdef L_ssshift_1
+;;; (set (reg:QQ 24)
+;;;      (ss_ashift:QQ (reg:QQ 24)
+;;;                    (reg:QI 22)))
+;;; Clobbers: R22.
+DEFUN __ssashlqq3
+    bst     A0, 7
+    sbrc    A0, 7
+    neg     A0
+    XCALL   __usashluqq3
+    ;; In the negative result case, A = 0x80 is no overflow,
+    ;; but treating it as such keeps the value unchanged.
+    tst     A0
+    brmi .Lsat
+    brtc 9f
+    neg     A0
+    ret
+
+.Lsat:
+    ;; Saturate:
+    ;; T = 0 -> 0x7f
+    ;; T = 1 -> 0x80
+    ldi     A0, 0x7f
+    brtc 9f
+    inc     A0
+9:  ret
+ENDF __ssashlqq3
+#endif /* L_ssshift_1 */
+
+#undef A0
+#undef Off
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Saturated Shift Left, 2 Bytes
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+#define A0 24
+#define A1 A0+1
+#define Off r22
+
+#ifdef L_usshift_2
+;;; (set (reg:UH* 24)
+;;;      (us_ashift:UH* (reg:UH* 24)
+;;;                     (reg:QI 22)))
+;;; T = T
+;;; Clobbers: R22.
+
+DEFUN __usshift_2
+.Loop_bytes:
+    tst     A1
+    brne .Lsat
+    mov     A1, A0
+    clr     A0
+ENTRY __usashluha3
+ENTRY __usashluhq3
+    subi    Off, 8
+    brcc .Loop_bytes
+    ;; Undo the extra Off -= 8 from above.
+    subi    Off, -8
+    breq 9f
+.Loop_bits:
+    lsl     A0
+    rol     A1
+    brcs .Lsat
+    dec     Off
+    brne .Loop_bits
+    ret
+
+.Lsat:
+    ;; A = 0xff..
+    ldi     A0, lo8(0xffff)
+    ldi     A1, hi8(0xffff)
+9:  ret
+ENDF __usshift_2
+#endif /* L_usshift_2 */
+
+#ifdef L_ssshift_2
+;;; (set (reg:HA/HQ 24)
+;;;      (ss_ashift:HA/HQ (reg:HA/HQ 24)
+;;;                       (reg:QI 22)))
+;;; Clobbers: R22.
+DEFUN __ssashlha3
+ENTRY __ssashlhq3
+    bst     A1, 7
+    brtc 1f
+    NEG2    A0
+1:  XCALL   __usashluha3
+    ;; In the negative result case, A = 0x80.. is no overflow,
+    ;; but treating it as such keeps the value unchanged.
+    tst     A1
+    brmi .Lsat
+    brtc 9f
+    NEG2    A0
+    ret
+
+.Lsat:
+    ;; Saturate:
+    ;; T = 0 -> 0x7f..
+    ;; T = 1 -> 0x80..
+    ldi     A0, lo8(0x7fff)
+    ldi     A1, hi8(0x7fff)
+    brtc 9f
+    waddi   A0, 1
+9:  ret
+ENDF __ssashlha3
+#endif /* L_ssshift_2 */
+
+#undef A0
+#undef A1
+#undef Off
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Saturated Shift Left, 4 Bytes
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+#define A0 22
+#define A1 A0+1
+#define A2 A0+2
+#define A3 A0+3
+#define Off r20
+
+#ifdef L_usshift_4
+;;; (set (reg:US* 22)
+;;;      (us_ashift:US* (reg:US* 22)
+;;;                     (reg:QI 20)))
+;;; T = T
+;;; Clobbers: R20.
+DEFUN __usshift_4
+.Loop_bytes:
+    tst     A3
+    brne .Lsat
+    mov     A3, A2
+    mov     A2, A1
+    mov     A1, A0
+    clr     A0
+ENTRY __usashlusa3
+ENTRY __usashlusq3
+    subi    Off, 8
+    brcc .Loop_bytes
+    ;; Undo the extra Off -= 8 from above.
+    subi    Off, -8
+    breq 9f
+.Loop_bits:
+    lsl     A0
+    rol     A1
+    rol     A2
+    rol     A3
+    brcs .Lsat
+    dec     Off
+    brne .Loop_bits
+    ret
+
+.Lsat:
+    ldi     A0, 0xff
+    ldi     A1, 0xff
+    wmov    A2, A0
+9:  ret
+ENDF __usshift_4
+#endif /* L_usshift_4 */
+
+#ifdef L_ssshift_4
+;;; (set (reg:SA/SQ 22)
+;;;      (ss_ashift:SA/SQ (reg:SA/SQ 22)
+;;;                       (reg:QI 20)))
+;;; Clobbers: R20.
+DEFUN __ssashlsa3
+ENTRY __ssashlsq3
+    bst     A3, 7
+    brtc 1f
+    XCALL   __negsi2
+1:  XCALL   __usashlusa3
+    ;; In the negative result case, A = 0x80.. is no overflow,
+    ;; but treating it as such keeps the value unchanged.
+    tst     A3
+    brpl 2f
+    ;; Saturate:
+    ;; T = 0 -> 0x7f..
+    ;; T = 1 -> 0x80..
+    bld     A3, 7
+    cpi     A3, 0x80
+    sbc     A0, A0
+    sbc     A1, A1
+    wmov    A2, A0
+    subi    A3, 0x80
+9:  ret
+2:  brtc 9b
+    XJMP    __negsi2
+ENDF __ssashlsa3
+#endif /* L_ssshift_4 */
+
+#undef A0
+#undef A1
+#undef A2
+#undef A3
+#undef Off
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Saturated Shift Left, 8 Bytes
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+#ifndef __AVR_TINY__
+
+#define A0 18
+#define A1 A0+1
+#define A2 A0+2
+#define A3 A0+3
+#define A4 A0+4
+#define A5 A0+5
+#define A6 A0+6
+#define A7 A0+7
+#define Off r31
+
+#ifdef L_usshift_8
+;;; (set (reg:UD* 18)
+;;;      (us_ashift:UD* (reg:UD* 18)
+;;;                     (reg:QI 16)))
+;;; T = T
+;;; Clobbers: R31
+DEFUN __usashluda3
+ENTRY __usashluta3
+ENTRY __usashludq3
+    mov     Off, r16
+    rjmp    .Lstart
+.Loop_bytes:
+    tst     A7
+    brne .Lsat
+    mov     A7, A6
+    mov     A6, A5
+    mov     A5, A4
+    mov     A4, A3
+    mov     A3, A2
+    mov     A2, A1
+    mov     A1, A0
+    clr     A0
+.Lstart:
+    subi    Off, 8
+    brcc .Loop_bytes
+    ;; Undo the extra Off -= 8 from above.
+    subi    Off, -8
+    breq 9f
+.Loop_bits:
+    lsl     A0
+    rol     A1
+    rol     A2
+    rol     A3
+    rol     A4
+    rol     A5
+    rol     A6
+    rol     A7
+    brcs .Lsat
+    dec     Off
+    brne .Loop_bits
+    ret
+
+.Lsat:
+    ldi     A0, 0xff
+    ldi     A1, 0xff
+    wmov    A2, A0
+    wmov    A4, A0
+    wmov    A6, A0
+9:  ret
+ENDF __usashluda3
+#endif /* L_usshift_8 */
+
+#ifdef L_ssshift_8
+;;; (set (reg:DA/DQ 18)
+;;;      (ss_ashift:DA/DQ (reg:DA/DQ 18)
+;;;                       (reg:QI 16)))
+DEFUN __ssashlda3
+ENTRY __ssashldq3
+ENTRY __ssashlta3
+    bst     A7, 7
+    brtc 1f
+    XCALL   __negdi2
+1:  XCALL   __usashluda3
+    ;; In the negative result case, A = 0x80.. is no overflow,
+    ;; but treating it as such keeps the value unchanged.
+    tst     A7
+    brpl 2f
+    ;; Saturate:
+    ;; T = 0 -> 0x7f..
+    ;; T = 1 -> 0x80..
+    bld     A7, 7
+    cpi     A7, 0x80
+    sbc     A0, A0
+    sbc     A1, A1
+    wmov    A2, A0
+    wmov    A4, A0
+    wmov    A6, A0
+    subi    A7, 0x80
+9:  ret
+2:  brtc 9b
+    XJMP    __negdi2
+ENDF __ssashlda3
+#endif /* L_ssshift_8 */
+
+#undef A0
+#undef A1
+#undef A2
+#undef A3
+#undef A4
+#undef A5
+#undef A6
+#undef A7
+#undef Off
+
+#endif /* !__AVR_TINY__ */
diff --git a/libgcc/config/avr/t-avr b/libgcc/config/avr/t-avr
index 0b5dd42506e4..947666269f75 100644
--- a/libgcc/config/avr/t-avr
+++ b/libgcc/config/avr/t-avr
@@ -48,6 +48,8 @@ LIB1ASMFUNCS = \
 	_ssneg_2 \
 	_ssabs_1 _ssabs_2 \
 	_mask1 _ret \
+	_usshift_1 _usshift_2 _usshift_4 \
+	_ssshift_1 _ssshift_2 _ssshift_4 \
 	_roundqq3 _rounduqq3 \
 	_round_s2 _round_u2 _round_2_const _addmask_2 \
 
@@ -69,6 +71,7 @@ FUNCS_notiny = \
 	_divdi3 _udivdi3 \
 	_udivmod64 \
 	_negdi2 _negdi2_r10 \
+	_usshift_8 _ssshift_8 \
 	_prologue \
 	_epilogue \
 	_load_3 _load_4 \
@@ -273,6 +276,10 @@ LIB2FUNCS_EXCLUDE += \
 	$(foreach func,_lshr _ashl _ashr _cmp,\
 	$(foreach mode,$(allfix_modes),$(func_X)))
 
+LIB2FUNCS_EXCLUDE += \
+	$(foreach func,_ssashl _usashl _ashlhelper,\
+	$(foreach mode,$(allfix_modes),$(func_X)))
+
 
 usat_modes = UQQ UHQ UHA USQ USA UDQ UDA UTQ UTA
 ssat_modes =  QQ  HQ  HA  SQ  SA  DQ  DA  TQ  TA
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.