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

Reply via email to