This patch adds support for saturated shift left of all the
fixed-point modes -- except for the Reduced Code 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.

Ok for trunk?

Johann

--

gcc/
        * config/avr/avr-fixed.md (ALL12QA): New mode 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-s.u: New test.
diff --git a/gcc/config/avr/avr-fixed.md b/gcc/config/avr/avr-fixed.md
index 581afb035b0..1220304a5e7 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 xxashl  [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}")
+        (xxashl: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)
+        (xxashl: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}")
+        (xxashl: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)
+        (xxashl: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 142e9d16008..7fa07622c91 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 00000000000..d1354876e91
--- /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 00000000000..dcd0d03dcfe
--- /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 00000000000..1adf3bfc5a8
--- /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 d7a297064b4..142b832c2a1 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 0b5dd42506e..947666269f7 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