Patchwork [avr] ad PR54222: Support saturated +, -, ABS

login
register
mail settings
Submitter Georg-Johann Lay
Date Sept. 14, 2012, 3:43 p.m.
Message ID <505350A8.3000003@gjlay.de>
Download mbox | patch
Permalink /patch/183962/
State New
Headers show

Comments

Georg-Johann Lay - Sept. 14, 2012, 3:43 p.m.
This patch adds more fixed-point support, namely saturated operations:

SS_PLUS, SS_MINUS, SS_NEG, SS_ABS,
US_PLUS, US_MINUS, US_NEG

for all supported fixed-point modes:

[U]QQ,
[U]HQ, [U]HA,
[U]SQ, [U]SA,
[U]DQ, [U]DA, [U]TA.

Depending on their complexity, the functions are implemented in libgcc
or are natively supported by avr-gcc.

The bulk of code is in the avr_out_plus_1 routine which has been generalized
to perform saturation.

avr_out_plus has been rewritten and is now generic enough to handle all the
cases that were formerly treated by:
  avr_out_plus
  avr_out_plus_noclobber
  avr_out_minus
  avr_out_plus64
  avr_out_minus64

The latter 4 functions are removed and the md files are cleaned up to use
avr_out_plus.

There are no new regressions.

However, all new tests with "-Os -flto" fail because they trigger a
segmentation fault in lto1 at
   gcc/tree-streamer-in.c:unpack_ts_fixed_cst_value_fields()
while that function tries to deserialize TREE_FIXED_CST.

Thus, these FAILs are because of an LTO issue.

Except the "-Os -flto" cases, all other new tests PASS.

Ok for trunk?

Johann

gcc/
	PR target/54222
	* config/avr/avr-fixed.md (ALL2S, ALL4S, ALL24S, ALL124S,
	ALL124U): New mode iterators.
	(<code_stdname><mode>3): New insns for SS_PLUS, SS_MINUS.
	(<code_stdname><mode>3): New insns for US_PLUS, US_MINUS.
	(usneg<mode>2): New insns.
	(<code_stdname><mode>2): New expanders for SS_NEG, SS_ABS.
	(*<code_stdname><mode>2): New insns for SS_NEG, SS_ABS.
	* config/avr/avr-dimode.md (ALL8U, ALL8S): New mode iterators.
	(avr_out_plus64, avr_out_minus64): Use avr_out_plus instead.
	(<code_stdname><mode>3): New expanders for SS_PLUS, SS_MINUS.
	(<code_stdname><mode>3): New expanders for US_PLUS, US_MINUS.
	(<code_stdname><mode>3_insn): New insns.
	(<code_stdname><mode>3_const_insn): New insns.
	* config/avr/avr.md (cc): Add: plus. Remove: out_plus,
	out_plus_noclobber, minus.
	(length): Add: plus.  Remove: out_plus, out_plus_noclobber,
	plus64, minus, minus64.
	(abelian): New code_attr.
	(code_stdname): Handle: ss_plus, ss_minus, ss_neg, ss_abs,
	us_plus, us_minus, us_neg.
	(*add<mode>3, add<mode>3_clobber, add<mode>3, addpsi3, sub<mode>3):
	Use avr_out_plus to output.
	* config/avr/avr-protos.h (avr_out_plus): Change prototype.
	(avr_out_plus_noclobber, avr_out_minus): Remove.
	(avr_out_plus64, avr_out_minus64): Remove.
	* config/avr/avr.c (avr_out_plus_1): Add new default arguments
	code_sat, sign.  Saturate after operation if code_sat != UNKNOWN.
	(avr_out_plus_symbol): New static function.
	(avr_out_plus): Rewrite.
	(adjust_insn_length): Handle: ADJUST_LEN_PLUS.  Remove handling
	of: ADJUST_LEN_OUT_PLUS, ADJUST_LEN_PLUS64, ADJUST_LEN_MINUS,
	ADJUST_LEN_MINUS64, ADJUST_LEN_OUT_PLUS_NOCLOBBER.
	(notice_update_cc): Handle: CC_PLUS.  Remove handling of: CC_MINUS,
	CC_OUT_PLUS, CC_OUT_PLUS_NOCLOBBER
	(avr_out_plus_noclobber, avr_out_minus): Remove.
	(avr_out_plus64, avr_out_minus64): Remove.
	

libgcc/
	PR target/54222
	* config/avr/lib1funcs-fixed.S (__ssneg_2, __ssabs_2, __ssneg_4,
	__ssabs_4, __clr_8, __ssneg_8, __ssabs_8,
	__usadd_8, __ussub_8, __ssadd_8, __sssub_8): New functions.
	(__divsa3): Use __negsi2 to negate r_quoL.
	* config/avr/lib1funcs.S (FALIAS): New macro.
	(__divmodsi4): Break out and use __divmodsi4_neg1 as...
	(__negsi2): ...this new function.
	* config/avr/t-avr (LIB1ASMFUNCS): Add _negsi2, _clr_8,
	_ssneg_2, _ssneg_4, _ssneg_8, _ssabs_2, _ssabs_4,
	_ssabs_8, _ssadd_8, _sssub_8, _usadd_8, _ussub_8.
	(LIB2FUNCS_EXCLUDE): Fix typo for _add _sub.
	Add: _ssadd*, _sssub*, _ssneg*, _ssabs* for signed fixed modes.
	Add: _usadd*, _ussub*, _usneg* for unsigned fixed modes.

gcc/testsuite/
	PR target/54222
	* gcc.target/avr/torture/fix-types.h: New.
	* gcc.target/avr/torture/vals-hr.def: New.
	* gcc.target/avr/torture/vals-r.def: New.
	* gcc.target/avr/torture/vals-k.def: New.
	* gcc.target/avr/torture/vals-ur.def: New.
	* gcc.target/avr/torture/vals-uk.def: New.
	* gcc.target/avr/torture/vals-uhr.def: New.
	* gcc.target/avr/torture/vals-llk.def: New.
	* gcc.target/avr/torture/vals-ullk.def: New.
	* gcc.target/avr/torture/sat-hr-plus-minus.c: New.
	* gcc.target/avr/torture/sat-r-plus-minus.c: New.
	* gcc.target/avr/torture/sat-k-plus-minus.c: New.
	* gcc.target/avr/torture/sat-ur-plus-minus.c: New.
	* gcc.target/avr/torture/sat-uk-plus-minus.c: New.
	* gcc.target/avr/torture/sat-uhr-plus-minus.c: New.
	* gcc.target/avr/torture/sat-llk-plus-minus.c: New.
	* gcc.target/avr/torture/sat-ullk-plus-minus.c: New.
Denis Chertykov - Sept. 15, 2012, 6:22 a.m.
2012/9/14 Georg-Johann Lay <avr@gjlay.de>:
> This patch adds more fixed-point support, namely saturated operations:
>
> SS_PLUS, SS_MINUS, SS_NEG, SS_ABS,
> US_PLUS, US_MINUS, US_NEG
>
> for all supported fixed-point modes:
>
> [U]QQ,
> [U]HQ, [U]HA,
> [U]SQ, [U]SA,
> [U]DQ, [U]DA, [U]TA.
>
> Depending on their complexity, the functions are implemented in libgcc
> or are natively supported by avr-gcc.
>
> The bulk of code is in the avr_out_plus_1 routine which has been generalized
> to perform saturation.
>
> avr_out_plus has been rewritten and is now generic enough to handle all the
> cases that were formerly treated by:
>   avr_out_plus
>   avr_out_plus_noclobber
>   avr_out_minus
>   avr_out_plus64
>   avr_out_minus64
>
> The latter 4 functions are removed and the md files are cleaned up to use
> avr_out_plus.
>
> There are no new regressions.
>
> However, all new tests with "-Os -flto" fail because they trigger a
> segmentation fault in lto1 at
>    gcc/tree-streamer-in.c:unpack_ts_fixed_cst_value_fields()
> while that function tries to deserialize TREE_FIXED_CST.
>
> Thus, these FAILs are because of an LTO issue.
>
> Except the "-Os -flto" cases, all other new tests PASS.
>
> Ok for trunk?
>

Ok. Please apply.

Denis.

Patch

Index: gcc/testsuite/gcc.target/avr/torture/vals-llk.def
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/vals-llk.def	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/vals-llk.def	(revision 0)
@@ -0,0 +1,32 @@ 
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (05, -0x1)
+VAL (06, -0x3f)
+VAL (07, 0x4000000000000000)
+VAL (08,-0x4000000000000000)
+
+VAL (10,-0x7fffffffffffffff)
+VAL (11, 0x7fffffffffffffff)
+VAL (12, 0x7f80000000000000)
+VAL (13,-0x7f80000000000000)
+VAL (14, 0x7f80000000000001)
+VAL (15,-0x7f80000000000001)
+VAL (16, 0x7f7f7f7f7f7f7f7f)
+VAL (17,-0x7f7f7f7f7f7f7f7f)
+VAL (18, 0x7f80808080808000)
+VAL (19,-0x7f80808080808000)
+VAL (20, 0x3e80000000000000)
+VAL (21,-0x3e80000000000000)
+VAL (22, 0x3f80000000000000)
+VAL (23,-0x3f80000000000000)
+VAL (24, 0x40000000000000)
+VAL (25,-0x40000000000000)
+VAL (26, 0x3f000000000000)
+VAL (27,-0x3f000000000000)
+VAL (28, 0xffffff00)
+VAL (29,-0xffffff00)
+VAL (30, 0x00ff00ff00ff00ff)
+VAL (31,-0x00ff00ff00ff00ff)
+
Index: gcc/testsuite/gcc.target/avr/torture/vals-uhr.def
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/vals-uhr.def	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/vals-uhr.def	(revision 0)
@@ -0,0 +1,13 @@ 
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (07, 0x40)
+VAL (08, 0xc0)
+VAL (10, 0xc1)
+VAL (12, 0xff)
+VAL (14, 0x7f)
+VAL (16, 0x81)
+VAL (20, 0xbf)
+
+VAL (99, 0x80)
+
Index: gcc/testsuite/gcc.target/avr/torture/sat-uk-plus-minus.c
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/sat-uk-plus-minus.c	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/sat-uk-plus-minus.c	(revision 0)
@@ -0,0 +1,82 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fno-strict-overflow" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef unsigned _Accum fx_t;
+typedef unsigned _Sat _Accum satfx_t;
+typedef unsigned long intfx_t;
+
+US_LFUN (us_add, +, fx_t, uk, >)
+US_LFUN (us_sub, -, fx_t, uk, <)
+
+#define VAL(N, X)                               \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_add2_##N (satfx_t a)             \
+    {                                           \
+        return us_add_uk (a, X##P##-##16uk);    \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_add_##N (satfx_t a)              \
+    {                                           \
+        return a + X##P##-##16uk;               \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_sub2_##N (satfx_t a)             \
+    {                                           \
+        return us_sub_uk (a, X##P##-##16uk);    \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_sub_##N (satfx_t a)              \
+    {                                           \
+        return a - X##P##-##16uk;               \
+    }
+#include "vals-uk.def"
+#undef VAL
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  us_add_##N, us_add2_##N,                      \
+  us_sub_##N, us_sub2_##N,
+#include "vals-uk.def"
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x40000000, 0x3e800000, 0x3f800000,
+    0x7ffffffe, 0x7fffffff, 0x7f800000,
+    0x7f7f7f7f, 0x7f810080, 0x7f008000,
+    0x7f000001,
+    0x80000000, 0x80000001, 0x80808080,
+    0x80810000, 0x80ffffff, 0x80fffffe,
+    0x81000000, 0x81800000, 0x81800000,
+    0xff000000, 0xffffff01, 0xffffff80,
+    0xffffff7f, 0xff80ff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
Index: gcc/testsuite/gcc.target/avr/torture/sat-llk-plus-minus.c
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/sat-llk-plus-minus.c	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/sat-llk-plus-minus.c	(revision 0)
@@ -0,0 +1,108 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fno-strict-overflow" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef long long _Accum fx_t;
+typedef long long _Sat _Accum satfx_t;
+typedef long long intfx_t;
+
+SS_LFUN (ss_add, +, fx_t, llk, >)
+SS_LFUN (ss_sub, -, fx_t, llk, <)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add2_##N (satfx_t a)               \
+  {                                             \
+    return ss_add_llk (a, X##P##-##48llk);      \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##48llk;                  \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub2_##N (satfx_t a)               \
+  {                                             \
+    return ss_sub_llk (a, X##P##-##48llk);      \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##48llk;                  \
+  }
+#include "vals-llk.def"
+#undef VAL
+
+__attribute__((noinline,noclone))
+satfx_t ss_add2_99 (satfx_t a)
+{
+  return ss_add_llk (a, __LLACCUM_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_add_99 (satfx_t a)
+{
+  return a + __LLACCUM_MIN__;
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub2_99 (satfx_t a)
+{
+  return ss_sub_llk (a, __LLACCUM_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub_99 (satfx_t a)
+{
+  return a - __LLACCUM_MIN__;
+}
+
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  ss_add_##N, ss_add2_##N,                      \
+  ss_sub_##N, ss_sub2_##N,
+#include "vals-llk.def"
+  VAL (99,)
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x4000000000000000, 0x3e80000000000000, 0x3f80000000000000,
+    0x7ffffffffffffffe, 0x7fffffffffffffff, 0x7f80000000000000,
+    0x7f7f7f7f7f7f7f7f, 0x7f81000000000080, 0x7f00000080000000,
+    0x7f00000000000001,
+    0x8000000000000000, 0x8000000000000001, 0x8080808080808080,
+    0x8081000000000000, 0x80ffffffffffffff, 0x80fffffffffffffe,
+    0x8100000000000000, 0x8180000000000000, 0x818000000000000,
+    0xff00000000000000, 0xffffffffffffff01, 0xffffffffffffff80,
+    0xffffffffffffff7f, 0xff80ff80ff80ff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
Index: gcc/testsuite/gcc.target/avr/torture/sat-uhr-plus-minus.c
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/sat-uhr-plus-minus.c	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/sat-uhr-plus-minus.c	(revision 0)
@@ -0,0 +1,73 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fno-strict-overflow" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef unsigned short _Fract fx_t;
+typedef unsigned short _Sat _Fract satfx_t;
+typedef unsigned char intfx_t;
+
+US_LFUN (us_add, +, fx_t, uhr, >)
+US_LFUN (us_sub, -, fx_t, uhr, <)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_add2_##N (satfx_t a)               \
+  {                                             \
+    return us_add_uhr (a, X##P##-##8uhr);       \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##8uhr;                   \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_sub2_##N (satfx_t a)               \
+  {                                             \
+    return us_sub_uhr (a, X##P##-##8uhr);       \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##8uhr;                   \
+  }
+#include "vals-uhr.def"
+#undef VAL
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  us_add_##N, us_add2_##N,                      \
+  us_sub_##N, us_sub2_##N,
+#include "vals-uhr.def"
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, 1, 2, 0x7f, 0x80, 0x81, 0xff,
+    0x40, 0x3e, 0x3f, 0xbf, 0xc0, 0xc1
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
Index: gcc/testsuite/gcc.target/avr/torture/vals-uk.def
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/vals-uk.def	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/vals-uk.def	(revision 0)
@@ -0,0 +1,23 @@ 
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (07, 0x40000000)
+VAL (08, 0xc0000000)
+VAL (10, 0x7fffffff)
+VAL (12, 0x7f800000)
+VAL (14, 0x7f800001)
+VAL (16, 0x7f7f7f7f)
+VAL (18, 0x7f808000)
+VAL (20, 0x3e800000)
+VAL (22, 0x3f800000)
+VAL (24, 0x40000000)
+VAL (26, 0x3f000000)
+VAL (28, 0xffff00)
+VAL (30, 0x00ff00ff)
+VAL (31, 0xff00ff00)
+VAL (32, 0x10000000)
+VAL (33, 0xff000000)
+
+VAL (99, 0x80000000)
+
Index: gcc/testsuite/gcc.target/avr/torture/sat-r-plus-minus.c
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/sat-r-plus-minus.c	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/sat-r-plus-minus.c	(revision 0)
@@ -0,0 +1,107 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fno-strict-overflow" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef _Fract fx_t;
+typedef _Sat _Fract satfx_t;
+typedef int intfx_t;
+
+SS_FUN (ss_add, +, fx_t, r)
+SS_FUN (ss_sub, -, fx_t, r)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add2_##N (satfx_t a)               \
+  {                                             \
+    return ss_add_r (a, X##P##-##15r);          \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##15r;                    \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub2_##N (satfx_t a)               \
+  {                                             \
+    return ss_sub_r (a, X##P##-##15r);          \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##15r;                    \
+  }
+#include "vals-r.def"
+#undef VAL
+
+__attribute__((noinline,noclone))
+satfx_t ss_add2_99 (satfx_t a)
+{
+  return ss_add_r (a, __FRACT_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_add_99 (satfx_t a)
+{
+  return a + __FRACT_MIN__;
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub2_99 (satfx_t a)
+{
+  return ss_sub_r (a, __FRACT_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub_99 (satfx_t a)
+{
+  return a - __FRACT_MIN__;
+}
+
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  ss_add_##N, ss_add2_##N,                      \
+  ss_sub_##N, ss_sub2_##N,
+#include "vals-r.def"
+  VAL (99,)
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x4000, 0x3e80, 0x3f80,
+    0x7ffe, 0x7fff,
+    0x7f7f, 0x7f81, 0x7f80,
+    0x7f01,
+    0x8000, 0x8001, 0x8080,
+    0x8081, 0x80ff, 0x80fe,
+    0x8100, 0x8180, 0x817f,
+    0xff00, 0xff01, 0xff01,
+    0xff7f, 0xff80
+  };
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
Index: gcc/testsuite/gcc.target/avr/torture/vals-ur.def
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/vals-ur.def	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/vals-ur.def	(revision 0)
@@ -0,0 +1,17 @@ 
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (07, 0x4000)
+VAL (08, 0xc000)
+VAL (10, 0x7fff)
+VAL (12, 0x7f80)
+VAL (14, 0x7f81)
+VAL (16, 0x7f7f)
+VAL (20, 0x3e80)
+VAL (22, 0x3f80)
+VAL (26, 0x3f00)
+VAL (32, 0x100)
+
+VAL (99, 0x8000)
+
Index: gcc/testsuite/gcc.target/avr/torture/sat-ullk-plus-minus.c
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/sat-ullk-plus-minus.c	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/sat-ullk-plus-minus.c	(revision 0)
@@ -0,0 +1,82 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fno-strict-overflow" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef unsigned long long _Accum fx_t;
+typedef unsigned long long _Sat _Accum satfx_t;
+typedef unsigned long long intfx_t;
+
+US_LFUN (us_add, +, fx_t, ullk, >)
+US_LFUN (us_sub, -, fx_t, ullk, <)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_add2_##N (satfx_t a)               \
+  {                                             \
+    return us_add_ullk (a, X##P##-##48ullk);    \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##48ullk;                 \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_sub2_##N (satfx_t a)               \
+  {                                             \
+    return us_sub_ullk (a, X##P##-##48ullk);    \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t us_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##48ullk;                 \
+  }
+#include "vals-ullk.def"
+#undef VAL
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  us_add_##N, us_add2_##N,                      \
+  us_sub_##N, us_sub2_##N,
+#include "vals-ullk.def"
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x4000000000000000, 0x3e80000000000000, 0x3f80000000000000,
+    0x7ffffffffffffffe, 0x7fffffffffffffff, 0x7f80000000000000,
+    0x7f7f7f7f7f7f7f7f, 0x7f81000000000080, 0x7f00000080000000,
+    0x7f00000000000001,
+    0x8000000000000000, 0x8000000000000001, 0x8080808080808080,
+    0x8081000000000000, 0x80ffffffffffffff, 0x80fffffffffffffe,
+    0x8100000000000000, 0x8180000000000000, 0x818000000000000,
+    0xff00000000000000, 0xffffffffffffff01, 0xffffffffffffff80,
+    0xffffffffffffff7f, 0xff80ff80ff80ff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
Index: gcc/testsuite/gcc.target/avr/torture/sat-hr-plus-minus.c
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/sat-hr-plus-minus.c	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/sat-hr-plus-minus.c	(revision 0)
@@ -0,0 +1,98 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fno-strict-overflow" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef short _Fract fx_t;
+typedef short _Sat _Fract satfx_t;
+typedef char intfx_t;
+
+SS_FUN (ss_add, +, fx_t, hr)
+SS_FUN (ss_sub, -, fx_t, hr)
+
+#define VAL(N, X)                               \
+    __attribute__((noinline,noclone))           \
+    satfx_t ss_add2_##N (satfx_t a)             \
+    {                                           \
+        return ss_add_hr (a, X##P##-##7hr);     \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t ss_add_##N (satfx_t a)              \
+    {                                           \
+        return a + X##P##-##7hr;                \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t ss_sub2_##N (satfx_t a)             \
+    {                                           \
+        return ss_sub_hr (a, X##P##-##7hr);     \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t ss_sub_##N (satfx_t a)              \
+    {                                           \
+        return a - X##P##-##7hr;                \
+    }
+#include "vals-hr.def"
+#undef VAL
+
+__attribute__((noinline,noclone))
+satfx_t ss_add2_99 (satfx_t a)
+{
+  return ss_add_hr (a, __FRACT_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_add_99 (satfx_t a)
+{
+  return a + __FRACT_MIN__;
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub2_99 (satfx_t a)
+{
+  return ss_sub_hr (a, __FRACT_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub_99 (satfx_t a)
+{
+  return a - __FRACT_MIN__;
+}
+
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  ss_add_##N, ss_add2_##N,                      \
+  ss_sub_##N, ss_sub2_##N,
+#include "vals-hr.def"
+  VAL (99,)
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, 1, 2, 0x7f, 0x80, 0x81, 0xff,
+    0x40, 0x3e, 0x3f, 0xbf, 0xc0, 0xc1
+  };
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
Index: gcc/testsuite/gcc.target/avr/torture/vals-k.def
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/vals-k.def	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/vals-k.def	(revision 0)
@@ -0,0 +1,32 @@ 
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (05, -0x1)
+VAL (06, -0x3f)
+VAL (07, 0x40000000)
+VAL (08,-0x40000000)
+
+VAL (10,-0x7fffffff)
+VAL (11, 0x7fffffff)
+VAL (12, 0x7f800000)
+VAL (13,-0x7f800000)
+VAL (14, 0x7f800001)
+VAL (15,-0x7f800001)
+VAL (16, 0x7f7f7f7f)
+VAL (17,-0x7f7f7f7f)
+VAL (18, 0x7f808080)
+VAL (19,-0x7f808080)
+VAL (20, 0x3e800000)
+VAL (21,-0x3e800000)
+VAL (22, 0x3f800000)
+VAL (23,-0x3f800000)
+VAL (24, 0x400000)
+VAL (25,-0x400000)
+VAL (26, 0x3f000000)
+VAL (27,-0x3f000000)
+VAL (28, 0xffff00)
+VAL (29,-0xffff00)
+VAL (30, 0x00ff00ff)
+VAL (31,-0x00ff00ff)
+
Index: gcc/testsuite/gcc.target/avr/torture/vals-r.def
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/vals-r.def	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/vals-r.def	(revision 0)
@@ -0,0 +1,30 @@ 
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (05, -0x1)
+VAL (06, -0x3f)
+VAL (07, 0x4000)
+VAL (08,-0x4000)
+
+VAL (10,-0x7fff)
+VAL (11, 0x7fff)
+VAL (12, 0x7f80)
+VAL (13,-0x7f80)
+VAL (14, 0x7f81)
+VAL (15,-0x7f81)
+VAL (16, 0x7f7f)
+VAL (17,-0x7f7f)
+VAL (18, 0x7f80)
+VAL (19,-0x7f80)
+VAL (20, 0x3e80)
+VAL (21,-0x3e80)
+VAL (22, 0x3f80)
+VAL (23,-0x3f80)
+VAL (24, 0x40)
+VAL (25,-0x40)
+VAL (26, 0x3f00)
+VAL (27,-0x3f00)
+VAL (30, 0x00ff)
+VAL (31,-0x00ff)
+
Index: gcc/testsuite/gcc.target/avr/torture/sat-ur-plus-minus.c
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/sat-ur-plus-minus.c	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/sat-ur-plus-minus.c	(revision 0)
@@ -0,0 +1,82 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fno-strict-overflow" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef unsigned _Fract fx_t;
+typedef unsigned _Sat _Fract satfx_t;
+typedef unsigned int intfx_t;
+
+US_LFUN (us_add, +, fx_t, ur, >)
+US_LFUN (us_sub, -, fx_t, ur, <)
+
+#define VAL(N, X)                               \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_add2_##N (satfx_t a)             \
+    {                                           \
+        return us_add_ur (a, X##P##-##16ur);    \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_add_##N (satfx_t a)              \
+    {                                           \
+        return a + X##P##-##16ur;               \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_sub2_##N (satfx_t a)             \
+    {                                           \
+        return us_sub_ur (a, X##P##-##16ur);    \
+    }                                           \
+    __attribute__((noinline,noclone))           \
+    satfx_t us_sub_##N (satfx_t a)              \
+    {                                           \
+        return a - X##P##-##16ur;               \
+    }
+#include "vals-ur.def"
+#undef VAL
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  us_add_##N, us_add2_##N,                      \
+  us_sub_##N, us_sub2_##N,
+#include "vals-ur.def"
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x4000, 0x3e80, 0x3f80,
+    0x7ffe, 0x7fff,
+    0x7f7f, 0x7f81, 0x7f80,
+    0x7f01,
+    0x8000, 0x8001, 0x8080,
+    0x8081, 0x80ff, 0x80fe,
+    0x8100, 0x8180, 0x817f,
+    0xff00, 0xff01, 0xff01,
+    0xff7f, 0xff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
Index: gcc/testsuite/gcc.target/avr/torture/vals-ullk.def
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/vals-ullk.def	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/vals-ullk.def	(revision 0)
@@ -0,0 +1,20 @@ 
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04, 0x80)
+VAL (07, 0x4000000000000000)
+VAL (08, 0x4000000000000000)
+VAL (10, 0x7fffffffffffffff)
+VAL (12, 0x7f80000000000000)
+VAL (14, 0x7f80000000000001)
+VAL (16, 0x7f7f7f7f7f7f7f7f)
+VAL (18, 0x7f80808080808000)
+VAL (20, 0x3e80000000000000)
+VAL (22, 0x3f80000000000000)
+VAL (24, 0x40000000000000)
+VAL (26, 0x3f000000000000)
+VAL (28, 0xffffff00)
+VAL (30, 0x00ff00ff00ff00ff)
+
+VAL (99, 0x8000000000000000)
+
Index: gcc/testsuite/gcc.target/avr/torture/vals-hr.def
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/vals-hr.def	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/vals-hr.def	(revision 0)
@@ -0,0 +1,12 @@ 
+VAL (01, 0x0)
+VAL (02, 0x1)
+VAL (03, 0x3f)
+VAL (04,-0x3f)
+VAL (07, 0x40)
+VAL (08,-0x40)
+VAL (10,-0x1)
+VAL (12, 0x3f)
+VAL (13,-0x3f)
+VAL (14, 0x7f)
+VAL (15,-0x7f)
+
Index: gcc/testsuite/gcc.target/avr/torture/sat-k-plus-minus.c
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/sat-k-plus-minus.c	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/sat-k-plus-minus.c	(revision 0)
@@ -0,0 +1,108 @@ 
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -fno-strict-overflow" } */
+
+#include "fix-types.h"
+
+extern void abort (void);
+extern void exit (int);
+
+typedef _Accum fx_t;
+typedef _Sat _Accum satfx_t;
+typedef long intfx_t;
+
+SS_FUN (ss_add, +, fx_t, k)
+SS_FUN (ss_sub, -, fx_t, k)
+
+#define VAL(N, X)                               \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add2_##N (satfx_t a)               \
+  {                                             \
+    return ss_add_k (a, X##P##-##16k);          \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_add_##N (satfx_t a)                \
+  {                                             \
+    return a + X##P##-##16k;                    \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub2_##N (satfx_t a)               \
+  {                                             \
+    return ss_sub_k (a, X##P##-##16k);          \
+  }                                             \
+  __attribute__((noinline,noclone))             \
+  satfx_t ss_sub_##N (satfx_t a)                \
+  {                                             \
+    return a - X##P##-##16k;                    \
+  }
+#include "vals-k.def"
+#undef VAL
+
+__attribute__((noinline,noclone))
+satfx_t ss_add2_99 (satfx_t a)
+{
+  return ss_add_k (a, __ACCUM_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_add_99 (satfx_t a)
+{
+  return a + __ACCUM_MIN__;
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub2_99 (satfx_t a)
+{
+  return ss_sub_k (a, __ACCUM_MIN__);
+}
+
+__attribute__((noinline,noclone))
+satfx_t ss_sub_99 (satfx_t a)
+{
+  return a - __ACCUM_MIN__;
+}
+
+
+satfx_t (* __flash const fun[])(satfx_t) =
+{
+#define VAL(N, X)                               \
+  ss_add_##N, ss_add2_##N,                      \
+  ss_sub_##N, ss_sub2_##N,
+#include "vals-k.def"
+  VAL (99,)
+#undef VAL
+};
+
+
+const volatile __flash intfx_t vals[] =
+  {
+    0, -1, 1, -2, 2, -127, -128, -129,
+    0x7f, 0x80, 0x81, 0x100,
+    0x40000000, 0x3e800000, 0x3f800000,
+    0x7ffffffe, 0x7fffffff, 0x7f800000,
+    0x7f7f7f7f, 0x7f810080, 0x7f008000,
+    0x7f000001,
+    0x80000000, 0x80000001, 0x80808080,
+    0x80810000, 0x80ffffff, 0x80fffffe,
+    0x81000000, 0x81800000, 0x81800000,
+    0xff000000, 0xffffff01, 0xffffff80,
+    0xffffff7f, 0xff80ff80
+  };
+
+
+int main (void)
+{
+  for (unsigned int i = 0; i < sizeof (vals) / sizeof (*vals); i++)
+    {
+      satfx_t a, f1, f2;
+      intfx_t val = vals[i];
+      __builtin_memcpy (&a, &val, sizeof (satfx_t));
+      for (unsigned int f = 0; f < sizeof (fun) / sizeof (*fun); f += 2)
+        {
+          if (fun[f](a) != fun[f+1](a))
+            abort();
+        }
+    }
+
+  exit (0);
+  return 0;
+}
Index: gcc/testsuite/gcc.target/avr/torture/fix-types.h
===================================================================
--- gcc/testsuite/gcc.target/avr/torture/fix-types.h	(revision 0)
+++ gcc/testsuite/gcc.target/avr/torture/fix-types.h	(revision 0)
@@ -0,0 +1,134 @@ 
+typedef __INT8_TYPE__  int_hr_t;
+typedef __UINT8_TYPE__ int_uhr_t;
+
+typedef __INT16_TYPE__  int_hk_t;
+typedef __UINT16_TYPE__ int_uhk_t;
+typedef __INT16_TYPE__  int_r_t;
+typedef __UINT16_TYPE__ int_ur_t;
+
+typedef __INT32_TYPE__  int_k_t;
+typedef __UINT32_TYPE__ int_uk_t;
+typedef __INT32_TYPE__  int_lr_t;
+typedef __UINT32_TYPE__ int_ulr_t;
+
+typedef __INT64_TYPE__  int_lk_t;
+typedef __UINT64_TYPE__ int_ulk_t;
+typedef __INT64_TYPE__  int_llr_t;
+typedef __UINT64_TYPE__ int_ullr_t;
+typedef __INT64_TYPE__  int_llk_t;
+typedef __UINT64_TYPE__ int_ullk_t;
+
+typedef __INT16_TYPE__  xint_hr_t;
+typedef __UINT16_TYPE__ xint_uhr_t;
+
+typedef __INT32_TYPE__  xint_hk_t;
+typedef __UINT32_TYPE__ xint_uhk_t;
+typedef __INT32_TYPE__  xint_r_t;
+typedef __UINT32_TYPE__ xint_ur_t;
+
+typedef __INT64_TYPE__  xint_k_t;
+typedef __UINT64_TYPE__ xint_uk_t;
+typedef __INT64_TYPE__  xint_lr_t;
+typedef __UINT64_TYPE__ xint_ulr_t;
+
+#define INThr_MAX __INT8_MAX__
+#define INThr_MIN (-__INT8_MAX__-1)
+#define INTuhr_MAX __UINT8_MAX__
+
+#define INTr_MAX __INT16_MAX__
+#define INTr_MIN (-__INT16_MAX__-1)
+#define INTur_MAX __UINT16_MAX__
+
+#define INThk_MAX __INT16_MAX__
+#define INThk_MIN (-__INT16_MAX__-1)
+#define INTuhk_MAX __UINT16_MAX__
+
+#define INTlr_MAX __INT32_MAX__
+#define INTlr_MIN (-__INT32_MAX__-1)
+#define INTulr_MAX __UINT32_MAX__
+
+#define INTk_MAX __INT32_MAX__
+#define INTk_MIN (-__INT32_MAX__-1)
+#define INTuk_MAX __UINT32_MAX__
+
+#define INTlk_MAX __INT64_MAX__
+#define INTlk_MIN (-__INT64_MAX__-1)
+#define INTulk_MAX __UINT64_MAX__
+
+#define INTllk_MAX __INT64_MAX__
+#define INTllk_MIN (-__INT64_MAX__-1)
+#define INTullk_MAX __UINT64_MAX__
+
+#define SS_FUN(NAME, OP, T, FX)                 \
+  T __attribute__((noinline,noclone))           \
+  NAME##_##FX (T fa, T fb)                      \
+  {                                             \
+    int_##FX##_t ia;                            \
+    int_##FX##_t ib;                            \
+    xint_##FX##_t ic;                           \
+    __builtin_memcpy (&ia, &fa, sizeof (ia));   \
+    __builtin_memcpy (&ib, &fb, sizeof (ib));   \
+    ic = (xint_##FX##_t) ia OP ib;              \
+    if (ic > INT##FX##_MAX)                     \
+      ic = INT##FX##_MAX;                       \
+    else if (ic < INT##FX##_MIN)                \
+      ic = INT##FX##_MIN;                       \
+    ia = (int_##FX##_t) ic;                     \
+    __builtin_memcpy (&fa, &ia, sizeof (ia));   \
+    return fa;                                  \
+  }
+
+#define US_FUN(NAME, OP, T, FX)                 \
+  T __attribute__((noinline,noclone))           \
+  NAME##_##FX (T fa, T fb)                      \
+  {                                             \
+    int_##FX##_t ia;                            \
+    int_##FX##_t ib;                            \
+    xint_##FX##_t ic;                           \
+    __builtin_memcpy (&ia, &fa, sizeof (ia));   \
+    __builtin_memcpy (&ib, &fb, sizeof (ib));   \
+    ic = (xint_##FX##_t) ia OP ib;              \
+    if (ic > INT##FX##_MAX)                     \
+      ic = INT##FX##_MAX;                       \
+    else if (ic < 0)                            \
+      ic = 0;                                   \
+    ia = (int_##FX##_t) ic;                     \
+    __builtin_memcpy (&fa, &ia, sizeof (ia));   \
+    return fa;                                  \
+  }
+
+#define SS_LFUN(NAME, OP, T, FX, CMP)           \
+  T __attribute__((noinline,noclone))           \
+  NAME##_##FX (T fa, T fb)                      \
+  {                                             \
+    int_##FX##_t ia;                            \
+    int_##FX##_t ib;                            \
+    int_##FX##_t ic;                            \
+    __builtin_memcpy (&ia, &fa, sizeof (ia));   \
+    __builtin_memcpy (&ib, &fb, sizeof (ib));   \
+    ic = (int_##FX##_t) ia OP ib;               \
+    if (ic < ia && ib CMP 0)                    \
+      ic = INT##FX##_MAX;                       \
+    else if (ic > ia && 0 CMP ib)               \
+      ic = INT##FX##_MIN;                       \
+    __builtin_memcpy (&fa, &ic, sizeof (ic));   \
+    return fa;                                  \
+  }
+
+#define US_LFUN(NAME, OP, T, FX, CMP)           \
+  T __attribute__((noinline,noclone))           \
+  NAME##_##FX (T fa, T fb)                      \
+  {                                             \
+    int_##FX##_t ia;                            \
+    int_##FX##_t ib;                            \
+    int_##FX##_t ic;                            \
+    __builtin_memcpy (&ia, &fa, sizeof (ia));   \
+    __builtin_memcpy (&ib, &fb, sizeof (ib));   \
+    ic = (int_##FX##_t) ia OP ib;               \
+    if (ia CMP ic && 1 CMP 0)                   \
+      ic = INT##FX##_MAX;                       \
+    if (ia CMP ic && 0 CMP 1)                   \
+      ic = 0;                                   \
+    __builtin_memcpy (&fa, &ic, sizeof (ic));   \
+    return fa;                                  \
+  }