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