[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