public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] ARM fixed-point support [6/6]: target-specific parts
@ 2011-05-13 14:54 Julian Brown
  2011-05-13 16:00 ` Joseph S. Myers
  0 siblings, 1 reply; 7+ messages in thread
From: Julian Brown @ 2011-05-13 14:54 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1578 bytes --]

This is the last patch to add fixed-point support to the ARM backend,
the actual ARM-specific backend parts. It's functionally unchanged from
the earlier patch:

http://gcc.gnu.org/ml/gcc-patches/2011-05/msg00837.html

although Joseph's comments have (hopefully) all been addressed. One
must now configure with "--enable-fixed-point" to enable the
fixed-point support.

Tested alongside other patches in the series. OK to apply?

Julian

ChangeLog

    gcc/
    * configure.ac (fixed-point): Add ARM support.
    * config/arm/arm.c (arm_fixed_mode_set): New struct.
    (arm_set_fixed_optab_libfunc): New.
    (arm_set_fixed_conv_libfunc): New.
    (arm_init_libfuncs): Initialise fixed-point helper libfuncs with
    ARM-specific names.
    (arm_scalar_mode_supported_p): Support fixed-point modes.
    (arm_vector_mode_supported_p): Support vector fixed-point modes.
    * config/arm/arm-modes.def (FRACT, UFRACT, ACCUM, UACCUM): Declare
    vector modes.
    * config/arm/predicates.md (sat_shift_operator): New predicate.
    * config/arm/arm-fixed.md: New.
    * config/arm/arm.md: Include arm-fixed.md.
    * config/arm/t-arm (MD_INCLUDES): Add arm-fixed.md.
    * config/arm/fixed-bit-machine.h: New.
 
    libgcc/
    * config.host (arm*-*-linux*, arm*-*-uclinux*, arm*-*-eabi*)
    (arm*-*-symbianelf*): Add arm/t-fixed-point makefile fragment.
    target-specific or generic fixed_bit_machine_header to decorate the
    names of fixed-point helper functions.
    * config/arm/t-fixed-point: New.

    gcc/testsuite/
    * gcc.target/arm/fixed-point-exec.c: New test.


[-- Attachment #2: fixed-point-arm-parts-1.diff --]
[-- Type: text/x-patch, Size: 38343 bytes --]

commit a93c41f6d4a4d4f434be897e5a3e8a16a4d4e9ad
Author: Julian Brown <julian@henry8.codesourcery.com>
Date:   Wed May 11 04:42:46 2011 -0700

    Fixed-point extension support for ARM.

diff --git a/gcc/config/arm/arm-fixed.md b/gcc/config/arm/arm-fixed.md
new file mode 100644
index 0000000..b19745e
--- /dev/null
+++ b/gcc/config/arm/arm-fixed.md
@@ -0,0 +1,402 @@
+;; Copyright 2011 Free Software Foundation, Inc.
+;;
+;; This file is part of GCC.
+;;
+;; GCC is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published
+;; by the Free Software Foundation; either version 3, or (at your
+;; option) any later version.
+;;
+;; GCC is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+;; License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+;;
+;; This file contains ARM instructions that support fixed-point operations.
+
+(define_mode_iterator FIXED [QQ HQ SQ UQQ UHQ USQ HA SA UHA USA])
+
+(define_mode_iterator ADDSUB [V4QQ V2HQ V2HA])
+
+(define_mode_iterator UQADDSUB [V4UQQ V2UHQ UQQ UHQ V2UHA UHA])
+
+(define_mode_iterator QADDSUB [V4QQ V2HQ QQ HQ V2HA HA SQ SA])
+
+(define_mode_iterator QMUL [HQ HA])
+
+(define_mode_attr qaddsub_suf [(V4UQQ "8") (V2UHQ "16") (UQQ "8") (UHQ "16")
+			       (V2UHA "16") (UHA "16")
+			       (V4QQ "8") (V2HQ "16") (QQ "8") (HQ "16")
+			       (V2HA "16") (HA "16") (SQ "") (SA "")])
+
+(define_insn "add<mode>3"
+  [(set (match_operand:FIXED 0 "s_register_operand" "=r")
+	(plus:FIXED (match_operand:FIXED 1 "s_register_operand" "r")
+		    (match_operand:FIXED 2 "s_register_operand" "r")))]
+  "TARGET_32BIT"
+  "add%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "add<mode>3"
+  [(set (match_operand:ADDSUB 0 "s_register_operand" "=r")
+	(plus:ADDSUB (match_operand:ADDSUB 1 "s_register_operand" "r")
+		     (match_operand:ADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "sadd<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "usadd<mode>3"
+  [(set (match_operand:UQADDSUB 0 "s_register_operand" "=r")
+	(us_plus:UQADDSUB (match_operand:UQADDSUB 1 "s_register_operand" "r")
+			  (match_operand:UQADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "uqadd<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "ssadd<mode>3"
+  [(set (match_operand:QADDSUB 0 "s_register_operand" "=r")
+	(ss_plus:QADDSUB (match_operand:QADDSUB 1 "s_register_operand" "r")
+			 (match_operand:QADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "qadd<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "sub<mode>3"
+  [(set (match_operand:FIXED 0 "s_register_operand" "=r")
+	(minus:FIXED (match_operand:FIXED 1 "s_register_operand" "r")
+		     (match_operand:FIXED 2 "s_register_operand" "r")))]
+  "TARGET_32BIT"
+  "sub%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "sub<mode>3"
+  [(set (match_operand:ADDSUB 0 "s_register_operand" "=r")
+	(minus:ADDSUB (match_operand:ADDSUB 1 "s_register_operand" "r")
+		      (match_operand:ADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "ssub<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "ussub<mode>3"
+  [(set (match_operand:UQADDSUB 0 "s_register_operand" "=r")
+	(us_minus:UQADDSUB
+	  (match_operand:UQADDSUB 1 "s_register_operand" "r")
+	  (match_operand:UQADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "uqsub<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "sssub<mode>3"
+  [(set (match_operand:QADDSUB 0 "s_register_operand" "=r")
+	(ss_minus:QADDSUB (match_operand:QADDSUB 1 "s_register_operand" "r")
+			  (match_operand:QADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "qsub<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+;; Fractional multiplies.
+
+; Note: none of these do any rounding.
+
+(define_expand "mulqq3"
+  [(set (match_operand:QQ 0 "s_register_operand" "")
+	(mult:QQ (match_operand:QQ 1 "s_register_operand" "")
+		 (match_operand:QQ 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY && arm_arch_thumb2"
+{
+  rtx tmp1 = gen_reg_rtx (HImode);
+  rtx tmp2 = gen_reg_rtx (HImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_extendqihi2 (tmp1, gen_lowpart (QImode, operands[1])));
+  emit_insn (gen_extendqihi2 (tmp2, gen_lowpart (QImode, operands[2])));
+  emit_insn (gen_mulhisi3 (tmp3, tmp1, tmp2));
+  emit_insn (gen_extv (gen_lowpart (SImode, operands[0]), tmp3, GEN_INT (8),
+		       GEN_INT (7)));
+  DONE;
+})
+
+(define_expand "mulhq3"
+  [(set (match_operand:HQ 0 "s_register_operand" "")
+	(mult:HQ (match_operand:HQ 1 "s_register_operand" "")
+		 (match_operand:HQ 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY && arm_arch_thumb2"
+{
+  rtx tmp = gen_reg_rtx (SImode);
+
+  emit_insn (gen_mulhisi3 (tmp, gen_lowpart (HImode, operands[1]),
+			   gen_lowpart (HImode, operands[2])));
+  /* We're doing a s.15 * s.15 multiplication, getting an s.30 result.  Extract
+     an s.15 value from that.  This won't overflow/saturate for _Fract
+     values.  */
+  emit_insn (gen_extv (gen_lowpart (SImode, operands[0]), tmp,
+		       GEN_INT (16), GEN_INT (15)));
+  DONE;
+})
+
+(define_expand "mulsq3"
+  [(set (match_operand:SQ 0 "s_register_operand" "")
+	(mult:SQ (match_operand:SQ 1 "s_register_operand" "")
+		 (match_operand:SQ 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch3m"
+{
+  rtx tmp1 = gen_reg_rtx (DImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  /* s.31 * s.31 -> s.62 multiplication.  */
+  emit_insn (gen_mulsidi3 (tmp1, gen_lowpart (SImode, operands[1]),
+			   gen_lowpart (SImode, operands[2])));
+  emit_insn (gen_lshrsi3 (tmp2, gen_lowpart (SImode, tmp1), GEN_INT (31)));
+  emit_insn (gen_ashlsi3 (tmp3, simplify_gen_subreg (SImode, tmp1, DImode, 4),
+			  GEN_INT (1)));
+  emit_insn (gen_iorsi3 (gen_lowpart (SImode, operands[0]), tmp2, tmp3));
+
+  DONE;
+})
+
+;; Accumulator multiplies.
+
+(define_expand "mulsa3"
+  [(set (match_operand:SA 0 "s_register_operand" "")
+	(mult:SA (match_operand:SA 1 "s_register_operand" "")
+		 (match_operand:SA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch3m"
+{
+  rtx tmp1 = gen_reg_rtx (DImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_mulsidi3 (tmp1, gen_lowpart (SImode, operands[1]),
+			   gen_lowpart (SImode, operands[2])));
+  emit_insn (gen_lshrsi3 (tmp2, gen_lowpart (SImode, tmp1), GEN_INT (15)));
+  emit_insn (gen_ashlsi3 (tmp3, simplify_gen_subreg (SImode, tmp1, DImode, 4),
+			  GEN_INT (17)));
+  emit_insn (gen_iorsi3 (gen_lowpart (SImode, operands[0]), tmp2, tmp3));
+
+  DONE;
+})
+
+(define_expand "mulusa3"
+  [(set (match_operand:USA 0 "s_register_operand" "")
+	(mult:USA (match_operand:USA 1 "s_register_operand" "")
+		  (match_operand:USA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch3m"
+{
+  rtx tmp1 = gen_reg_rtx (DImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_umulsidi3 (tmp1, gen_lowpart (SImode, operands[1]),
+			    gen_lowpart (SImode, operands[2])));
+  emit_insn (gen_lshrsi3 (tmp2, gen_lowpart (SImode, tmp1), GEN_INT (16)));
+  emit_insn (gen_ashlsi3 (tmp3, simplify_gen_subreg (SImode, tmp1, DImode, 4),
+			  GEN_INT (16)));
+  emit_insn (gen_iorsi3 (gen_lowpart (SImode, operands[0]), tmp2, tmp3));
+  
+  DONE;
+})
+
+;; The code sequence emitted by this insn pattern uses the Q flag, which GCC
+;; doesn't generally know about, so we don't bother expanding to individual
+;; instructions.  It may be better to just use an out-of-line asm libcall for
+;; this.
+
+(define_insn "ssmulsa3"
+  [(set (match_operand:SA 0 "s_register_operand" "=r")
+	(ss_mult:SA (match_operand:SA 1 "s_register_operand" "r")
+		    (match_operand:SA 2 "s_register_operand" "r")))
+   (clobber (match_scratch:DI 3 "=r"))
+   (clobber (match_scratch:SI 4 "=r"))
+   (clobber (reg:CC CC_REGNUM))]
+  "TARGET_32BIT && arm_arch6"
+{
+  /* s16.15 * s16.15 -> s32.30.  */
+  output_asm_insn ("smull\\t%Q3, %R3, %1, %2", operands);
+
+  if (TARGET_ARM)
+    output_asm_insn ("msr\\tAPSR_nzcvq, #0", operands);
+  else
+    {
+      output_asm_insn ("mov\\t%4, #0", operands);
+      output_asm_insn ("msr\\tAPSR_nzcvq, %4", operands);
+    }
+
+  /* We have:
+      31  high word  0     31  low word  0 
+
+    [ S i i .... i i i ] [ i f f f ... f f ]
+                        |
+			v
+	     [ S i ... i f ... f f ]
+
+    Need 16 integral bits, so saturate at 15th bit of high word.  */
+
+  output_asm_insn ("ssat\\t%R3, #15, %R3", operands);
+  output_asm_insn ("mrs\\t%4, APSR", operands);
+  output_asm_insn ("tst\\t%4, #1<<27", operands);
+  if (TARGET_THUMB2)
+    output_asm_insn ("it\\tne", operands);
+  output_asm_insn ("mvnne\\t%Q3, %R3, asr #32", operands);
+  output_asm_insn ("mov\\t%0, %Q3, lsr #15", operands);
+  output_asm_insn ("orr\\t%0, %0, %R3, asl #17", operands);
+  return "";
+}
+  [(set_attr "conds" "clob")
+   (set (attr "length")
+	(if_then_else (eq_attr "is_thumb" "yes")
+		      (const_int 38)
+		      (const_int 32)))])
+
+;; Same goes for this.
+
+(define_insn "usmulusa3"
+  [(set (match_operand:USA 0 "s_register_operand" "=r")
+	(us_mult:USA (match_operand:USA 1 "s_register_operand" "r")
+		     (match_operand:USA 2 "s_register_operand" "r")))
+   (clobber (match_scratch:DI 3 "=r"))
+   (clobber (match_scratch:SI 4 "=r"))
+   (clobber (reg:CC CC_REGNUM))]
+  "TARGET_32BIT && arm_arch6"
+{
+  /* 16.16 * 16.16 -> 32.32.  */
+  output_asm_insn ("umull\\t%Q3, %R3, %1, %2", operands);
+
+  if (TARGET_ARM)
+    output_asm_insn ("msr\\tAPSR_nzcvq, #0", operands);
+  else
+    {
+      output_asm_insn ("mov\\t%4, #0", operands);
+      output_asm_insn ("msr\\tAPSR_nzcvq, %4", operands);
+    }
+
+  /* We have:
+      31  high word  0     31  low word  0 
+
+    [ i i i .... i i i ] [ f f f f ... f f ]
+                        |
+			v
+	     [ i i ... i f ... f f ]
+
+    Need 16 integral bits, so saturate at 16th bit of high word.  */
+
+  output_asm_insn ("usat\\t%R3, #16, %R3", operands);
+  output_asm_insn ("mrs\\t%4, APSR", operands);
+  output_asm_insn ("tst\\t%4, #1<<27", operands);
+  if (TARGET_THUMB2)
+    output_asm_insn ("it\\tne", operands);
+  output_asm_insn ("sbfxne\\t%Q3, %R3, #15, #1", operands);
+  output_asm_insn ("lsr\\t%0, %Q3, #16", operands);
+  output_asm_insn ("orr\\t%0, %0, %R3, asl #16", operands);
+  return "";
+}
+  [(set_attr "conds" "clob")
+   (set (attr "length")
+	(if_then_else (eq_attr "is_thumb" "yes")
+		      (const_int 38)
+		      (const_int 32)))])
+
+(define_expand "mulha3"
+  [(set (match_operand:HA 0 "s_register_operand" "")
+	(mult:HA (match_operand:HA 1 "s_register_operand" "")
+		 (match_operand:HA 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY && arm_arch_thumb2"
+{
+  rtx tmp = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_mulhisi3 (tmp, gen_lowpart (HImode, operands[1]),
+			   gen_lowpart (HImode, operands[2])));
+  emit_insn (gen_extv (gen_lowpart (SImode, operands[0]), tmp, GEN_INT (16),
+		       GEN_INT (7)));
+
+  DONE;
+})
+
+(define_expand "muluha3"
+  [(set (match_operand:UHA 0 "s_register_operand" "")
+	(mult:UHA (match_operand:UHA 1 "s_register_operand" "")
+		  (match_operand:UHA 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY"
+{
+  rtx tmp1 = gen_reg_rtx (SImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  /* 8.8 * 8.8 -> 16.16 multiply.  */
+  emit_insn (gen_zero_extendhisi2 (tmp1, gen_lowpart (HImode, operands[1])));
+  emit_insn (gen_zero_extendhisi2 (tmp2, gen_lowpart (HImode, operands[2])));
+  emit_insn (gen_mulsi3 (tmp3, tmp1, tmp2));
+  emit_insn (gen_extzv (gen_lowpart (SImode, operands[0]), tmp3,
+			GEN_INT (16), GEN_INT (8)));
+
+  DONE;
+})
+
+(define_expand "ssmulha3"
+  [(set (match_operand:HA 0 "s_register_operand" "")
+	(ss_mult:HA (match_operand:HA 1 "s_register_operand" "")
+		    (match_operand:HA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch6"
+{
+  rtx tmp = gen_reg_rtx (SImode);
+  rtx rshift;
+  
+  emit_insn (gen_mulhisi3 (tmp, gen_lowpart (HImode, operands[1]),
+			   gen_lowpart (HImode, operands[2])));
+
+  rshift = gen_rtx_ASHIFTRT (SImode, tmp, GEN_INT (7));
+
+  emit_insn (gen_rtx_SET (VOIDmode, gen_lowpart (HImode, operands[0]),
+			  gen_rtx_SS_TRUNCATE (HImode, rshift)));
+
+  DONE;
+})
+
+(define_expand "usmuluha3"
+  [(set (match_operand:UHA 0 "s_register_operand" "")
+	(us_mult:UHA (match_operand:UHA 1 "s_register_operand" "")
+		     (match_operand:UHA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch6"
+{
+  rtx tmp1 = gen_reg_rtx (SImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  rtx rshift_tmp = gen_reg_rtx (SImode);
+  
+  /* Note: there's no smul[bt][bt] equivalent for unsigned multiplies.  Use a
+     normal 32x32->32-bit multiply instead.  */
+  emit_insn (gen_zero_extendhisi2 (tmp1, gen_lowpart (HImode, operands[1])));
+  emit_insn (gen_zero_extendhisi2 (tmp2, gen_lowpart (HImode, operands[2])));
+  
+  emit_insn (gen_mulsi3 (tmp3, tmp1, tmp2));
+
+  /* The operand to "usat" is signed, so we cannot use the "..., asr #8"
+     form of that instruction since the multiplication result TMP3 may have the
+     top bit set, thus be negative and saturate to zero.  Use a separate
+     logical right-shift instead.  */
+  emit_insn (gen_lshrsi3 (rshift_tmp, tmp3, GEN_INT (8)));
+  emit_insn (gen_arm_usatsihi (gen_lowpart (HImode, operands[0]), rshift_tmp));
+
+  DONE;
+})
+
+(define_insn "arm_ssatsihi_shift"
+  [(set (match_operand:HI 0 "s_register_operand" "=r")
+	(ss_truncate:HI (match_operator:SI 1 "sat_shift_operator"
+			  [(match_operand:SI 2 "s_register_operand" "r")
+			   (match_operand:SI 3 "immediate_operand" "I")])))]
+  "TARGET_32BIT && arm_arch6"
+  "ssat%?\\t%0, #16, %2%S1"
+  [(set_attr "predicable" "yes")
+   (set_attr "type" "alu_shift")])
+
+(define_insn "arm_usatsihi"
+  [(set (match_operand:HI 0 "s_register_operand" "=r")
+	(us_truncate:HI (match_operand:SI 1 "s_register_operand")))]
+  "TARGET_INT_SIMD"
+  "usat%?\\t%0, #16, %1"
+  [(set_attr "predicable" "yes")])
diff --git a/gcc/config/arm/arm-modes.def b/gcc/config/arm/arm-modes.def
index 24e3d90..7f19ebe 100644
--- a/gcc/config/arm/arm-modes.def
+++ b/gcc/config/arm/arm-modes.def
@@ -70,6 +70,12 @@ VECTOR_MODES (INT, 16);       /* V16QI V8HI V4SI V2DI */
 VECTOR_MODES (FLOAT, 8);      /*            V4HF V2SF */
 VECTOR_MODES (FLOAT, 16);     /*       V8HF V4SF V2DF */
 
+/* Fraction and accumulator vector modes.  */
+VECTOR_MODES (FRACT, 4);      /* V4QQ  V2HQ */
+VECTOR_MODES (UFRACT, 4);     /* V4UQQ V2UHQ */
+VECTOR_MODES (ACCUM, 4);      /*       V2HA */
+VECTOR_MODES (UACCUM, 4);     /*       V2UHA */
+
 /* Opaque integer modes for 3, 4, 6 or 8 Neon double registers (2 is
    TImode).  */
 INT_MODE (EI, 24);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 4f9c2aa..6d22ee9 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -1068,6 +1068,49 @@ bit_count (unsigned long value)
   return count;
 }
 
+typedef struct
+{
+  enum machine_mode mode;
+  const char *name;
+} arm_fixed_mode_set;
+
+/* A small helper for setting fixed-point library libfuncs.  */
+
+static void
+arm_set_fixed_optab_libfunc (optab optable, enum machine_mode mode,
+			     const char *funcname, const char *modename,
+			     int num_suffix)
+{
+  char buffer[50];
+  
+  if (num_suffix == 0)
+    sprintf (buffer, "__gnu_%s%s", funcname, modename);
+  else
+    sprintf (buffer, "__gnu_%s%s%d", funcname, modename, num_suffix);
+  
+  set_optab_libfunc (optable, mode, buffer);
+}
+
+static void
+arm_set_fixed_conv_libfunc (convert_optab optable, enum machine_mode to,
+			    enum machine_mode from, const char *funcname,
+			    const char *toname, const char *fromname)
+{
+  char buffer[50];
+  char *maybe_suffix_2 = "";
+  
+  /* Follow the logic for selecting a "2" suffix in fixed-bit.h.  */
+  if (ALL_FIXED_POINT_MODE_P (from) && ALL_FIXED_POINT_MODE_P (to)
+      && UNSIGNED_FIXED_POINT_MODE_P (from) == UNSIGNED_FIXED_POINT_MODE_P (to)
+      && ALL_FRACT_MODE_P (from) == ALL_FRACT_MODE_P (to))
+    maybe_suffix_2 = "2";
+  
+  sprintf (buffer, "__gnu_%s%s%s%s", funcname, fromname, toname,
+	   maybe_suffix_2);
+
+  set_conv_libfunc (optable, to, from, buffer);
+}
+
 /* Set up library functions unique to ARM.  */
 
 static void
@@ -1213,6 +1256,137 @@ arm_init_libfuncs (void)
       break;
     }
 
+  /* Use names prefixed with __gnu_ for fixed-point helper functions.  */
+  {
+    const arm_fixed_mode_set fixed_arith_modes[] =
+      {
+	{ QQmode, "qq" },
+	{ UQQmode, "uqq" },
+	{ HQmode, "hq" },
+	{ UHQmode, "uhq" },
+	{ SQmode, "sq" },
+	{ USQmode, "usq" },
+	{ DQmode, "dq" },
+	{ UDQmode, "udq" },
+	{ TQmode, "tq" },
+	{ UTQmode, "utq" },
+	{ HAmode, "ha" },
+	{ UHAmode, "uha" },
+	{ SAmode, "sa" },
+	{ USAmode, "usa" },
+	{ DAmode, "da" },
+	{ UDAmode, "uda" },
+	{ TAmode, "ta" },
+	{ UTAmode, "uta" }
+      };
+    const arm_fixed_mode_set fixed_conv_modes[] =
+      {
+	{ QQmode, "qq" },
+	{ UQQmode, "uqq" },
+	{ HQmode, "hq" },
+	{ UHQmode, "uhq" },
+	{ SQmode, "sq" },
+	{ USQmode, "usq" },
+	{ DQmode, "dq" },
+	{ UDQmode, "udq" },
+	{ TQmode, "tq" },
+	{ UTQmode, "utq" },
+	{ HAmode, "ha" },
+	{ UHAmode, "uha" },
+	{ SAmode, "sa" },
+	{ USAmode, "usa" },
+	{ DAmode, "da" },
+	{ UDAmode, "uda" },
+	{ TAmode, "ta" },
+	{ UTAmode, "uta" },
+	{ QImode, "qi" },
+	{ HImode, "hi" },
+	{ SImode, "si" },
+	{ DImode, "di" },
+	{ TImode, "ti" },
+	{ SFmode, "sf" },
+	{ DFmode, "df" }
+      };
+    unsigned int i, j;
+
+    for (i = 0; i < ARRAY_SIZE (fixed_arith_modes); i++)
+      {
+	arm_set_fixed_optab_libfunc (add_optab, fixed_arith_modes[i].mode,
+				     "add", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssadd_optab, fixed_arith_modes[i].mode,
+				     "ssadd", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usadd_optab, fixed_arith_modes[i].mode,
+				     "usadd", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (sub_optab, fixed_arith_modes[i].mode,
+				     "sub", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (sssub_optab, fixed_arith_modes[i].mode,
+				     "sssub", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ussub_optab, fixed_arith_modes[i].mode,
+				     "ussub", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (smul_optab, fixed_arith_modes[i].mode,
+				     "mul", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssmul_optab, fixed_arith_modes[i].mode,
+				     "ssmul", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usmul_optab, fixed_arith_modes[i].mode,
+				     "usmul", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (sdiv_optab, fixed_arith_modes[i].mode,
+				     "div", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (udiv_optab, fixed_arith_modes[i].mode,
+				     "udiv", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssdiv_optab, fixed_arith_modes[i].mode,
+				     "ssdiv", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usdiv_optab, fixed_arith_modes[i].mode,
+				     "usdiv", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (neg_optab, fixed_arith_modes[i].mode,
+				     "neg", fixed_arith_modes[i].name, 2);
+	arm_set_fixed_optab_libfunc (ssneg_optab, fixed_arith_modes[i].mode,
+				     "ssneg", fixed_arith_modes[i].name, 2);
+	arm_set_fixed_optab_libfunc (usneg_optab, fixed_arith_modes[i].mode,
+				     "usneg", fixed_arith_modes[i].name, 2);
+	arm_set_fixed_optab_libfunc (ashl_optab, fixed_arith_modes[i].mode,
+				     "ashl", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ashr_optab, fixed_arith_modes[i].mode,
+				     "ashr", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (lshr_optab, fixed_arith_modes[i].mode,
+				     "lshr", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssashl_optab, fixed_arith_modes[i].mode,
+				     "ssashl", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usashl_optab, fixed_arith_modes[i].mode,
+				     "usashl", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (cmp_optab, fixed_arith_modes[i].mode,
+				     "cmp", fixed_arith_modes[i].name, 2);
+      }
+
+    for (i = 0; i < ARRAY_SIZE (fixed_conv_modes); i++)
+      for (j = 0; j < ARRAY_SIZE (fixed_conv_modes); j++)
+	{
+	  if (i == j
+	      || (!ALL_FIXED_POINT_MODE_P (fixed_conv_modes[i].mode)
+		  && !ALL_FIXED_POINT_MODE_P (fixed_conv_modes[j].mode)))
+	    continue;
+
+	  arm_set_fixed_conv_libfunc (fract_optab, fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "fract",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	  arm_set_fixed_conv_libfunc (satfract_optab,
+				      fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "satfract",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	  arm_set_fixed_conv_libfunc (fractuns_optab,
+				      fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "fractuns",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	  arm_set_fixed_conv_libfunc (satfractuns_optab,
+				      fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "satfractuns",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	}
+  }
+
   if (TARGET_AAPCS_BASED)
     synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
 }
@@ -19333,6 +19507,12 @@ arm_scalar_mode_supported_p (enum machine_mode mode)
 {
   if (mode == HFmode)
     return (arm_fp16_format != ARM_FP16_FORMAT_NONE);
+  else if (mode == QQmode || mode == HQmode || mode == SQmode
+	   || mode == UQQmode || mode == UHQmode || mode == USQmode
+	   || mode == HAmode || mode == SAmode || mode == UHAmode
+	   || mode == USAmode || mode == DQmode || mode == UDQmode
+	   || mode == DAmode || mode == UDAmode)
+    return true;
   else
     return default_scalar_mode_supported_p (mode);
 }
@@ -22454,6 +22634,11 @@ arm_vector_mode_supported_p (enum machine_mode mode)
 	  || (mode == V8QImode)))
     return true;
 
+  if (TARGET_INT_SIMD && (mode == V4UQQmode || mode == V4QQmode
+      || mode == V2UHQmode || mode == V2HQmode || mode == V2UHAmode
+      || mode == V2HAmode))
+    return true;
+
   return false;
 }
 
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 40ebf35..8a1dd76 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -10751,3 +10751,5 @@
 (include "neon.md")
 ;; Synchronization Primitives
 (include "sync.md")
+;; Fixed-point patterns
+(include "arm-fixed.md")
diff --git a/gcc/config/arm/fixed-bit-machine.h b/gcc/config/arm/fixed-bit-machine.h
new file mode 100644
index 0000000..9f3bf5b
--- /dev/null
+++ b/gcc/config/arm/fixed-bit-machine.h
@@ -0,0 +1,30 @@
+/* Machine-specific override for fixed-point support routine names.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published
+   by the Free Software Foundation; either version 3, or (at your
+   option) any later version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* To be EABI-compliant (until the EABI supports fixed-point functionality),
+   use the __gnu_ namespace for fixed-point helper functions.  */
+
+#ifdef __ARM_EABI__
+#define DECORATE_FIXED_NAME(X) __gnu_ ## X
+#endif
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index 891a974..f1800dc 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -227,6 +227,13 @@
 	    (match_code "ashift,ashiftrt,lshiftrt,rotatert"))
        (match_test "mode == GET_MODE (op)")))
 
+;; True for shift operators which can be used with saturation instructions.
+(define_special_predicate "sat_shift_operator"
+  (and (match_code "ashift,ashiftrt")
+       (match_test "GET_CODE (XEXP (op, 1)) == CONST_INT
+		    && ((unsigned HOST_WIDE_INT) INTVAL (XEXP (op, 1)) <= 32)")
+       (match_test "mode == GET_MODE (op)")))
+
 ;; True for MULT, to identify which variant of shift_operator is in use.
 (define_special_predicate "mult_operator"
   (match_code "mult"))
diff --git a/gcc/config/arm/t-arm b/gcc/config/arm/t-arm
index 18a548a..24e2631 100644
--- a/gcc/config/arm/t-arm
+++ b/gcc/config/arm/t-arm
@@ -37,7 +37,8 @@ MD_INCLUDES= 	$(srcdir)/config/arm/arm-tune.md \
 		$(srcdir)/config/arm/iwmmxt.md \
 		$(srcdir)/config/arm/vfp.md \
 		$(srcdir)/config/arm/neon.md \
-		$(srcdir)/config/arm/thumb2.md
+		$(srcdir)/config/arm/thumb2.md \
+		$(srcdir)/config/arm/arm-fixed.md
 
 LIB1ASMSRC = arm/lib1funcs.asm
 LIB1ASMFUNCS = _thumb1_case_sqi _thumb1_case_uqi _thumb1_case_shi \
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 5fa5d07..0cf46bb 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -633,6 +633,10 @@ AC_ARG_ENABLE(fixed-point,
 ],
 [
   case $target in
+    arm*)
+      enable_fixed_point=yes
+      ;;
+
     mips*-*-*)
       case $host in
 	mips*-sgi-irix*)
diff --git a/gcc/testsuite/gcc.target/arm/fixed-point-exec.c b/gcc/testsuite/gcc.target/arm/fixed-point-exec.c
new file mode 100644
index 0000000..6bc3b07
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/fixed-point-exec.c
@@ -0,0 +1,301 @@
+/* { dg-do run { target { fixed_point } } } */
+/* { dg-options "-std=gnu99" } */
+
+/* Check basic arithmetic ops for ARM fixed-point/saturating operation support.
+   Not target-independent since we make various assumptions about precision and
+   magnitudes of various types.  */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdfix.h>
+
+#define TEST(TYPE, OP, NAME, SUFFIX)				\
+  TYPE NAME##SUFFIX (TYPE A, TYPE B)				\
+    {								\
+      return A OP B;						\
+    }
+
+#define VARIANTS(TYPE, OP, NAME)				\
+  TEST (short TYPE, OP, NAME, _short);				\
+  TEST (TYPE, OP, NAME, _regular);				\
+  TEST (long TYPE, OP, NAME, _long);				\
+  TEST (_Sat short TYPE, OP, NAME, _sat_short);			\
+  TEST (_Sat TYPE, OP, NAME, _sat_regular);			\
+  TEST (_Sat long TYPE, OP, NAME, _sat_long);			\
+  TEST (unsigned short TYPE, OP, NAME, _uns_short);		\
+  TEST (unsigned TYPE, OP, NAME, _uns_regular);			\
+  TEST (unsigned long TYPE, OP, NAME, _uns_long);		\
+  TEST (unsigned _Sat short TYPE, OP, NAME, _uns_sat_short);	\
+  TEST (unsigned _Sat TYPE, OP, NAME, _uns_sat_regular);	\
+  TEST (unsigned _Sat long TYPE, OP, NAME, _uns_sat_long)
+
+VARIANTS (_Fract, +, plus_fract);
+VARIANTS (_Accum, +, plus_accum);
+VARIANTS (_Fract, -, minus_fract);
+VARIANTS (_Accum, -, minus_accum);
+VARIANTS (_Fract, *, mult_fract);
+VARIANTS (_Accum, *, mult_accum);
+VARIANTS (_Accum, /, div_accum);
+
+/* Inputs for signed add, multiply fractional tests.  */
+short _Fract sf_a = 0.9hr;
+short _Fract sf_b = -0.8hr;
+_Fract f_a = 0.9r;
+_Fract f_b = -0.8r;
+long _Fract lf_a = 0.9lr;
+long _Fract lf_b = -0.8lr;
+
+/* Inputs for signed subtract fractional tests.  */
+short _Fract sf_c = 0.7hr;
+short _Fract sf_d = 0.9hr;
+_Fract f_c = 0.7r;
+_Fract f_d = 0.9r;
+long _Fract lf_c = 0.7lr;
+long _Fract lf_d = 0.9lr;
+
+/* Inputs for unsigned add, subtract, multiply fractional tests.  */
+unsigned short _Fract usf_a = 0.4uhr;
+unsigned short _Fract usf_b = 0.3uhr;
+unsigned _Fract uf_a = 0.4ur;
+unsigned _Fract uf_b = 0.3ur;
+unsigned long _Fract ulf_a = 0.4ulr;
+unsigned long _Fract ulf_b = 0.3ulr;
+
+/* Inputs for saturating signed add tests.  */
+short _Sat _Fract sf_e = 0.8hr;
+short _Sat _Fract sf_f = 0.8hr;
+_Sat _Fract f_e = 0.8r;
+_Sat _Fract f_f = 0.8r;
+long _Sat _Fract lf_e = 0.8r;
+long _Sat _Fract lf_f = 0.8r;
+
+short _Sat _Fract sf_g = -0.8hr;
+short _Sat _Fract sf_h = -0.8hr;
+_Sat _Fract f_g = -0.8r;
+_Sat _Fract f_h = -0.8r;
+long _Sat _Fract lf_g = -0.8r;
+long _Sat _Fract lf_h = -0.8r;
+
+/* Inputs for saturating unsigned subtract tests.  */
+unsigned short _Sat _Fract usf_c = 0.3uhr;
+unsigned short _Sat _Fract usf_d = 0.4uhr;
+unsigned _Sat _Fract uf_c = 0.3ur;
+unsigned _Sat _Fract uf_d = 0.4ur;
+unsigned long _Sat _Fract ulf_c = 0.3ulr;
+unsigned long _Sat _Fract ulf_d = 0.4ulr;
+
+/* Inputs for signed accumulator tests.  */
+
+short _Accum sa_a = 1.25hk;
+short _Accum sa_b = -1.5hk;
+_Accum a_a = 100.25k;
+_Accum a_b = -100.5k;
+long _Accum la_a = 1000.25lk;
+long _Accum la_b = -1000.5lk;
+
+/* Inputs for unsigned accumulator tests.  */
+
+unsigned short _Accum usa_a = 2.5uhk;
+unsigned short _Accum usa_b = 1.75uhk;
+unsigned _Accum ua_a = 255.5uk;
+unsigned _Accum ua_b = 170.25uk;
+unsigned long _Accum ula_a = 1550.5ulk;
+unsigned long _Accum ula_b = 999.5ulk;
+
+/* Inputs for signed saturating accumulator tests.  */
+
+short _Sat _Accum sa_c = 240.0hk;
+short _Sat _Accum sa_d = 250.0hk;
+short _Sat _Accum sa_e = -240.0hk;
+short _Sat _Accum sa_f = -250.0hk;
+short _Sat _Accum sa_g = 0.5hk;
+
+_Sat _Accum a_c = 65000.0k;
+_Sat _Accum a_d = 20000.0k;
+_Sat _Accum a_e = -65000.0k;
+_Sat _Accum a_f = -20000.0k;
+_Sat _Accum a_g = 0.5k;
+
+long _Sat _Accum la_c = 3472883712.0lk;
+long _Sat _Accum la_d = 3456106496.0lk;
+long _Sat _Accum la_e = -3472883712.0lk;
+long _Sat _Accum la_f = -3456106496.0lk;
+long _Sat _Accum la_g = 0.5lk;
+
+/* Inputs for unsigned saturating accumulator tests.  */
+
+unsigned short _Sat _Accum usa_c = 250.0uhk;
+unsigned short _Sat _Accum usa_d = 240.0uhk;
+unsigned short _Sat _Accum usa_e = 0.5uhk;
+
+unsigned _Sat _Accum ua_c = 65000.0uk;
+unsigned _Sat _Accum ua_d = 20000.0uk;
+unsigned _Sat _Accum ua_e = 0.5uk;
+
+unsigned long _Sat _Accum ula_c = 3472883712.0ulk;
+unsigned long _Sat _Accum ula_d = 3456106496.0ulk;
+unsigned long _Sat _Accum ula_e = 0.5ulk;
+
+#define CHECK(FN, EXP) do {						     \
+  if (fabs ((float) (FN) - (EXP)) > 0.05)				     \
+    {									     \
+      fprintf (stderr, "result for " #FN " (as float): %f\n", (double) (FN));\
+      abort ();								     \
+    }									     \
+  } while (0)
+
+#define CHECK_EXACT(FN, EXP) do {					     \
+  if ((FN) != (EXP))							     \
+    {									     \
+      fprintf (stderr, "result for " #FN " (as float): %f, should be %f\n",  \
+	       (double) (FN), (double) (EXP));				     \
+      abort ();								     \
+    }									     \
+  } while (0)
+
+int
+main (int argc, char *argv[])
+{
+  /* Fract/fract operations, non-saturating.  */
+
+  CHECK (plus_fract_short (sf_a, sf_b), 0.1);
+  CHECK (plus_fract_regular (f_a, f_b), 0.1);
+  CHECK (plus_fract_long (lf_a, lf_b), 0.1);
+
+  CHECK (plus_fract_uns_short (usf_a, usf_b), 0.7);
+  CHECK (plus_fract_uns_regular (uf_a, uf_b), 0.7);
+  CHECK (plus_fract_uns_long (ulf_a, ulf_b), 0.7);
+
+  CHECK (minus_fract_short (sf_c, sf_d), -0.2);
+  CHECK (minus_fract_regular (f_c, f_d), -0.2);
+  CHECK (minus_fract_long (lf_c, lf_d), -0.2);
+
+  CHECK (minus_fract_uns_short (usf_a, usf_b), 0.1);
+  CHECK (minus_fract_uns_regular (uf_a, uf_b), 0.1);
+  CHECK (minus_fract_uns_long (ulf_a, ulf_b), 0.1);
+
+  CHECK (mult_fract_short (sf_a, sf_b), -0.72);
+  CHECK (mult_fract_regular (f_a, f_b), -0.72);
+  CHECK (mult_fract_long (lf_a, lf_b), -0.72);
+
+  CHECK (mult_fract_uns_short (usf_a, usf_b), 0.12);
+  CHECK (mult_fract_uns_regular (uf_a, uf_b), 0.12);
+  CHECK (mult_fract_uns_long (ulf_a, ulf_b), 0.12);
+
+  /* Fract/fract operations, saturating.  */
+
+  CHECK (plus_fract_sat_short (sf_e, sf_f), 1.0);
+  CHECK (plus_fract_sat_regular (f_e, f_f), 1.0);
+  CHECK (plus_fract_sat_long (lf_e, lf_f), 1.0);
+
+  CHECK (plus_fract_sat_short (sf_g, sf_h), -1.0);
+  CHECK (plus_fract_sat_regular (f_g, f_h), -1.0);
+  CHECK (plus_fract_sat_long (lf_g, lf_h), -1.0);
+  
+  CHECK (plus_fract_uns_sat_short (sf_e, sf_f), 1.0);
+  CHECK (plus_fract_uns_sat_regular (f_e, f_f), 1.0);
+  CHECK (plus_fract_uns_sat_long (lf_e, lf_f), 1.0);
+
+  CHECK (plus_fract_sat_short (sf_a, sf_b), 0.1);
+  CHECK (plus_fract_sat_regular (f_a, f_b), 0.1);
+  CHECK (plus_fract_sat_long (lf_a, lf_b), 0.1);
+  
+  CHECK (plus_fract_uns_sat_short (usf_a, usf_b), 0.7);
+  CHECK (plus_fract_uns_sat_regular (uf_a, uf_b), 0.7);
+  CHECK (plus_fract_uns_sat_long (ulf_a, ulf_b), 0.7);
+
+  CHECK (minus_fract_uns_sat_short (usf_c, usf_d), 0.0);
+  CHECK (minus_fract_uns_sat_regular (uf_c, uf_d), 0.0);
+  CHECK (minus_fract_uns_sat_short (ulf_c, ulf_d), 0.0);
+  
+  CHECK (minus_fract_sat_short (sf_c, sf_d), -0.2);
+  CHECK (minus_fract_sat_regular (f_c, f_d), -0.2);
+  CHECK (minus_fract_sat_long (lf_c, lf_d), -0.2);
+
+  /* Accum/accum operations, non-saturating.  */
+
+  CHECK (plus_accum_short (sa_a, sa_b), -0.25);
+  CHECK (plus_accum_regular (a_a, a_b), -0.25);
+  CHECK (plus_accum_long (la_a, la_b), -0.25);
+
+  CHECK (minus_accum_short (sa_a, sa_b), 2.75);
+  CHECK (minus_accum_regular (a_a, a_b), 200.75);
+  CHECK (minus_accum_long (la_a, la_b), 2000.75);
+  
+  CHECK (mult_accum_short (sa_a, sa_b), -1.875);
+  CHECK (mult_accum_regular (a_a, a_b), -10075.125);
+  CHECK (mult_accum_long (la_a, la_b), -1000750.125);
+
+  CHECK (div_accum_short (sa_a, sa_b), -1.25/1.5);
+  CHECK (div_accum_regular (a_a, a_b), -100.25/100.5);
+  CHECK (div_accum_long (la_a, la_b), -1000.25/1000.5);
+
+  /* Unsigned accum/accum operations, non-saturating.  */
+  
+  CHECK (plus_accum_uns_short (usa_a, usa_b), 4.25);
+  CHECK (plus_accum_uns_regular (ua_a, ua_b), 425.75);
+  CHECK (plus_accum_uns_long (ula_a, ula_b), 2550.0);
+
+  CHECK (minus_accum_uns_short (usa_a, usa_b), 0.75);
+  CHECK (minus_accum_uns_regular (ua_a, ua_b), 85.25);
+  CHECK (minus_accum_uns_long (ula_a, ula_b), 551.0);
+  
+  CHECK (mult_accum_uns_short (usa_a, usa_b), 4.375);
+  CHECK (mult_accum_uns_regular (ua_a, ua_b), 43498.875);
+  CHECK (mult_accum_uns_long (ula_a, ula_b), 1549724.75);
+
+  CHECK (div_accum_uns_short (usa_a, usa_b), 2.5/1.75);
+  CHECK (div_accum_uns_regular (ua_a, ua_b), 255.5/170.25);
+  CHECK (div_accum_uns_long (ula_a, ula_b), 1550.5/999.5);
+
+  /* Signed accum/accum operations, saturating.  */
+  
+  CHECK_EXACT (plus_accum_sat_short (sa_c, sa_d), SACCUM_MAX);
+  CHECK_EXACT (plus_accum_sat_short (sa_e, sa_f), SACCUM_MIN);
+  CHECK_EXACT (plus_accum_sat_regular (a_c, a_d), ACCUM_MAX);
+  CHECK_EXACT (plus_accum_sat_regular (a_e, a_f), ACCUM_MIN);
+  CHECK_EXACT (plus_accum_sat_long (la_c, la_d), LACCUM_MAX);
+  CHECK_EXACT (plus_accum_sat_long (la_e, la_f), LACCUM_MIN);
+
+  CHECK_EXACT (minus_accum_sat_short (sa_e, sa_d), SACCUM_MIN);
+  CHECK_EXACT (minus_accum_sat_short (sa_c, sa_f), SACCUM_MAX);
+  CHECK_EXACT (minus_accum_sat_regular (a_e, a_d), ACCUM_MIN);
+  CHECK_EXACT (minus_accum_sat_regular (a_c, a_f), ACCUM_MAX);
+  CHECK_EXACT (minus_accum_sat_long (la_e, la_d), LACCUM_MIN);
+  CHECK_EXACT (minus_accum_sat_long (la_c, la_f), LACCUM_MAX);
+
+  CHECK_EXACT (mult_accum_sat_short (sa_c, sa_d), SACCUM_MAX);
+  CHECK_EXACT (mult_accum_sat_short (sa_c, sa_e), SACCUM_MIN);
+  CHECK_EXACT (mult_accum_sat_regular (a_c, a_d), ACCUM_MAX);
+  CHECK_EXACT (mult_accum_sat_regular (a_c, a_e), ACCUM_MIN);
+  CHECK_EXACT (mult_accum_sat_long (la_c, la_d), LACCUM_MAX);
+  CHECK_EXACT (mult_accum_sat_long (la_c, la_e), LACCUM_MIN);
+  
+  CHECK_EXACT (div_accum_sat_short (sa_d, sa_g), SACCUM_MAX);
+  CHECK_EXACT (div_accum_sat_short (sa_e, sa_g), SACCUM_MIN);
+  CHECK_EXACT (div_accum_sat_regular (a_c, a_g), ACCUM_MAX);
+  CHECK_EXACT (div_accum_sat_regular (a_e, a_g), ACCUM_MIN);
+  CHECK_EXACT (div_accum_sat_long (la_d, la_g), LACCUM_MAX);
+  CHECK_EXACT (div_accum_sat_long (la_e, la_g), LACCUM_MIN);
+
+  /* Unsigned accum/accum operations, saturating.  */
+
+  CHECK_EXACT (plus_accum_uns_sat_short (usa_c, usa_d), USACCUM_MAX);
+  CHECK_EXACT (plus_accum_uns_sat_regular (ua_c, ua_d), UACCUM_MAX);
+  CHECK_EXACT (plus_accum_uns_sat_long (ula_c, ula_d), ULACCUM_MAX);
+  
+  CHECK_EXACT (minus_accum_uns_sat_short (usa_d, usa_c), 0uhk);
+  CHECK_EXACT (minus_accum_uns_sat_regular (ua_d, ua_c), 0uk);
+  CHECK_EXACT (minus_accum_uns_sat_long (ula_d, ula_c), 0ulk);
+
+  CHECK_EXACT (mult_accum_uns_sat_short (usa_c, usa_d), USACCUM_MAX);
+  CHECK_EXACT (mult_accum_uns_sat_regular (ua_c, ua_d), UACCUM_MAX);
+  CHECK_EXACT (mult_accum_uns_sat_long (ula_c, ula_d), ULACCUM_MAX);
+
+  CHECK_EXACT (div_accum_uns_sat_short (usa_c, usa_e), USACCUM_MAX);
+  CHECK_EXACT (div_accum_uns_sat_regular (ua_c, ua_e), UACCUM_MAX);
+  CHECK_EXACT (div_accum_uns_sat_long (ula_c, ula_e), ULACCUM_MAX);
+
+  return 0;
+}
diff --git a/libgcc/config.host b/libgcc/config.host
index e3c48bb..5f24004 100644
--- a/libgcc/config.host
+++ b/libgcc/config.host
@@ -207,12 +207,15 @@ arm*-*-freebsd*)
 arm*-*-netbsdelf*)
 	;;
 arm*-*-linux*)			# ARM GNU/Linux with ELF
+	tmake_file="${tmake_file} arm/t-fixed-point"
 	;;
 arm*-*-uclinux*)		# ARM ucLinux
+	tmake_file="${tmake_file} arm/t-fixed-point"
 	;;
 arm*-*-ecos-elf)
 	;;
 arm*-*-eabi* | arm*-*-symbianelf* )
+	tmake_file="${tmake_file} arm/t-fixed-point"
 	;;
 arm*-*-rtems*)
 	;;
diff --git a/libgcc/config/arm/t-fixed-point b/libgcc/config/arm/t-fixed-point
new file mode 100644
index 0000000..66346dd
--- /dev/null
+++ b/libgcc/config/arm/t-fixed-point
@@ -0,0 +1 @@
+fixed_bit_machine_header := arm/fixed-bit-machine.h

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] ARM fixed-point support [6/6]: target-specific parts
  2011-05-13 14:54 [PATCH] ARM fixed-point support [6/6]: target-specific parts Julian Brown
@ 2011-05-13 16:00 ` Joseph S. Myers
  2011-05-13 18:45   ` Julian Brown
  0 siblings, 1 reply; 7+ messages in thread
From: Joseph S. Myers @ 2011-05-13 16:00 UTC (permalink / raw)
  To: Julian Brown; +Cc: gcc-patches

On Fri, 13 May 2011, Julian Brown wrote:

> although Joseph's comments have (hopefully) all been addressed. One
> must now configure with "--enable-fixed-point" to enable the
> fixed-point support.

No, that's not how this configure logic works; it enables fixed point by 
default for the given targets.  The bit that enables it by default for 
MIPS (and that you're changing to cover ARM as well) is the code that is 
called if the user *doesn't* pass --enable-fixed-point or 
--disable-fixed-point.

That is, for ARM and non-IRIX MIPS it's enabled by default; for other 
targets it's disabled by default.  In any case, an explicit configure 
option overrides this default.  Which is the logic we want.  The only 
problem is the calls to AC_MSG_WARN, which produce

configure: WARNING: fixed-point is not supported for this target, ignored

when you didn't pass any fixed-point configure option.  And that's a 
pre-existing problem, that could be fixed simply by removing two 
AC_MSG_WARN calls.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] ARM fixed-point support [6/6]: target-specific parts
  2011-05-13 16:00 ` Joseph S. Myers
@ 2011-05-13 18:45   ` Julian Brown
  2011-05-26 17:39     ` Julian Brown
  0 siblings, 1 reply; 7+ messages in thread
From: Julian Brown @ 2011-05-13 18:45 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: gcc-patches

On Fri, 13 May 2011 14:54:47 +0000 (UTC)
"Joseph S. Myers" <joseph@codesourcery.com> wrote:

> On Fri, 13 May 2011, Julian Brown wrote:
> 
> > although Joseph's comments have (hopefully) all been addressed. One
> > must now configure with "--enable-fixed-point" to enable the
> > fixed-point support.
> 
> No, that's not how this configure logic works; it enables fixed point
> by default for the given targets.  The bit that enables it by default
> for MIPS (and that you're changing to cover ARM as well) is the code
> that is called if the user *doesn't* pass --enable-fixed-point or 
> --disable-fixed-point.

Yes, you're quite right: I had forgotten to run autoconf after changing
configure.ac whilst testing. Sorry for the misinformation!

Cheers,

Julian

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] ARM fixed-point support [6/6]: target-specific parts
  2011-05-13 18:45   ` Julian Brown
@ 2011-05-26 17:39     ` Julian Brown
  2011-06-30 14:01       ` Richard Earnshaw
  0 siblings, 1 reply; 7+ messages in thread
From: Julian Brown @ 2011-05-26 17:39 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph S. Myers, paul, rearnsha, Ramana Radhakrishnan

[-- Attachment #1: Type: text/plain, Size: 5277 bytes --]

On Fri, 13 May 2011 17:31:18 +0100
Julian Brown <julian@codesourcery.com> wrote:

> On Fri, 13 May 2011 14:54:47 +0000 (UTC)
> "Joseph S. Myers" <joseph@codesourcery.com> wrote:
> 
> > On Fri, 13 May 2011, Julian Brown wrote:
> > 
> > > although Joseph's comments have (hopefully) all been addressed.
> > > One must now configure with "--enable-fixed-point" to enable the
> > > fixed-point support.
> > 
> > No, that's not how this configure logic works; it enables fixed
> > point by default for the given targets.  The bit that enables it by
> > default for MIPS (and that you're changing to cover ARM as well) is
> > the code that is called if the user *doesn't* pass
> > --enable-fixed-point or --disable-fixed-point.
> 
> Yes, you're quite right: I had forgotten to run autoconf after
> changing configure.ac whilst testing. Sorry for the misinformation!

This is a new version of the patch, which fixes a couple of issues
discovered on our internal branch since the last version was posted.
This version handles "long long" fixed point types in the same way as
32-bit MIPS: basically the sizes of those types are clamped to 64 bits.

This means the testsuite changes in:

http://gcc.gnu.org/ml/gcc-patches/2011-05/msg00969.html

are no longer required.

Another couple of problems have also been fixed, relating to big-endian
mode. One was the use of simplify_gen_subreg with a hard-wired offset in
a several of the insn expanders, where I should have just used
gen_highpart -- which works in either endianness, and makes the intent
clearer anyway.

The other issue is ABI-related. My previous assertion that fixed-point
types (smaller than word size) should be passed to and returned from
functions "like integers" doesn't really work out very well after all
in big-endian mode, so needs re-thinking. The problem is basically that
such fixed-point types are incorrectly padded in big-endian mode, so
the data ends up in a different part of the passed word than expected.

The way chars and shorts is handled is as follows: when those types are
passed to a function, they are promoted to word-size ints, which is a
nop-op: the values can simply be reinterpreted as ints, and that works
fine in either endianness. We might hope to be able to do the same thing
for smaller-than-word-size fixed-point types, but unfortunately we
can't: e.g. a "short fract" or "fract" can't just be reinterpreted as a
"long fract" (the 32-bit fractional type), because the fixed-point is
in a different place for the wider type (and performing conversions
would itself require libcalls at present). It also doesn't work to
promote small fixed-point types to SImode: as well as being
conceptually messy, we hit ICEs very quickly attempting that.

So instead, I think that small fixed-point types should be handled the
same way as small aggregates or small complex-valued types. This means
effectively no change in little-endian mode, but in big-endian mode:

* for register arguments, fixed-point types are passed in the
  most-significant end of the register.

* for stack arguments, fixed-point types are passed in lower memory
  addresses.

* for return values, fixed-point types are again in the
  most-significant end of the return register (r0).

This is quite easy to set up in the backend code, but unfortunately I
hit a snag in libcall expansion in calls.c: padding of arguments and
return values is not properly supported, as it is for regular function
calls. I've implemented that, and will post it as a separate patch.

OK to apply? Tested alongside the rest of the patch series, in both big
& little-endian mode.

Julian

ChangeLog

    gcc/
    * configure.ac (fixed-point): Add ARM support.
    * configure: Regenerate.
    * config/arm/arm.c (arm_fixed_mode_set): New struct.
    (arm_set_fixed_optab_libfunc): New.
    (arm_set_fixed_conv_libfunc): New.
    (arm_init_libfuncs): Initialise fixed-point helper libfuncs with
    ARM-specific names.
    (aapcs_libcall_value): Return sub-word-size fixed-point libcall
    return values in SImode.
    (arm_return_in_msb): Return fixed-point types in the msb.
    (arm_pad_reg_upwards, arm_pad_arg_upwards): Pad fixed-point types
    upwards.
    (arm_scalar_mode_supported_p): Support fixed-point modes.
    (arm_vector_mode_supported_p): Support vector fixed-point modes.
    * config/arm/arm.h (SHORT_FRACT_TYPE_SIZE, FRACT_TYPE_SIZE)
    (LONG_FRACT_TYPE_SIZE, LONG_LONG_FRACT_TYPE_SIZE)
    (SHORT_ACCUM_TYPE_SIZE, ACCUM_TYPE_SIZE, LONG_ACCUM_TYPE_SIZE)
    (LONG_LONG_ACCUM_TYPE_SIZE, MAX_FIXED_MODE_SIZE): Define.
    * config/arm/arm-modes.def (FRACT, UFRACT, ACCUM, UACCUM): Declare
    vector modes.
    * config/arm/predicates.md (sat_shift_operator): New predicate.
    * config/arm/arm-fixed.md: New.
    * config/arm/arm.md: Include arm-fixed.md.
    * config/arm/t-arm (MD_INCLUDES): Add arm-fixed.md.
    * config/arm/fixed-bit-machine.h: New.
 
    libgcc/
    * config.host (arm*-*-linux*, arm*-*-uclinux*, arm*-*-eabi*)
    (arm*-*-symbianelf*): Add arm/t-fixed-point makefile fragment.
    target-specific or generic fixed_bit_machine_header to decorate the
    names of fixed-point helper functions.
    * config/arm/t-fixed-point: New.

    gcc/testsuite/
    * gcc.target/arm/fixed-point-exec.c: New test.

[-- Attachment #2: arm-fixed-point-support-fsf-5.diff --]
[-- Type: text/x-patch, Size: 39920 bytes --]

commit b4ddde3e7fbc6a2961dd24695df53623f4970f1d
Author: Julian Brown <julian@henry8.codesourcery.com>
Date:   Thu May 26 09:12:05 2011 -0700

    Fixed-point extension support for ARM.

diff --git a/gcc/config/arm/arm-fixed.md b/gcc/config/arm/arm-fixed.md
new file mode 100644
index 0000000..df1c9ef
--- /dev/null
+++ b/gcc/config/arm/arm-fixed.md
@@ -0,0 +1,399 @@
+;; Copyright 2011 Free Software Foundation, Inc.
+;;
+;; This file is part of GCC.
+;;
+;; GCC is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published
+;; by the Free Software Foundation; either version 3, or (at your
+;; option) any later version.
+;;
+;; GCC is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+;; License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+;;
+;; This file contains ARM instructions that support fixed-point operations.
+
+(define_mode_iterator FIXED [QQ HQ SQ UQQ UHQ USQ HA SA UHA USA])
+
+(define_mode_iterator ADDSUB [V4QQ V2HQ V2HA])
+
+(define_mode_iterator UQADDSUB [V4UQQ V2UHQ UQQ UHQ V2UHA UHA])
+
+(define_mode_iterator QADDSUB [V4QQ V2HQ QQ HQ V2HA HA SQ SA])
+
+(define_mode_iterator QMUL [HQ HA])
+
+(define_mode_attr qaddsub_suf [(V4UQQ "8") (V2UHQ "16") (UQQ "8") (UHQ "16")
+			       (V2UHA "16") (UHA "16")
+			       (V4QQ "8") (V2HQ "16") (QQ "8") (HQ "16")
+			       (V2HA "16") (HA "16") (SQ "") (SA "")])
+
+(define_insn "add<mode>3"
+  [(set (match_operand:FIXED 0 "s_register_operand" "=r")
+	(plus:FIXED (match_operand:FIXED 1 "s_register_operand" "r")
+		    (match_operand:FIXED 2 "s_register_operand" "r")))]
+  "TARGET_32BIT"
+  "add%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "add<mode>3"
+  [(set (match_operand:ADDSUB 0 "s_register_operand" "=r")
+	(plus:ADDSUB (match_operand:ADDSUB 1 "s_register_operand" "r")
+		     (match_operand:ADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "sadd<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "usadd<mode>3"
+  [(set (match_operand:UQADDSUB 0 "s_register_operand" "=r")
+	(us_plus:UQADDSUB (match_operand:UQADDSUB 1 "s_register_operand" "r")
+			  (match_operand:UQADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "uqadd<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "ssadd<mode>3"
+  [(set (match_operand:QADDSUB 0 "s_register_operand" "=r")
+	(ss_plus:QADDSUB (match_operand:QADDSUB 1 "s_register_operand" "r")
+			 (match_operand:QADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "qadd<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "sub<mode>3"
+  [(set (match_operand:FIXED 0 "s_register_operand" "=r")
+	(minus:FIXED (match_operand:FIXED 1 "s_register_operand" "r")
+		     (match_operand:FIXED 2 "s_register_operand" "r")))]
+  "TARGET_32BIT"
+  "sub%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "sub<mode>3"
+  [(set (match_operand:ADDSUB 0 "s_register_operand" "=r")
+	(minus:ADDSUB (match_operand:ADDSUB 1 "s_register_operand" "r")
+		      (match_operand:ADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "ssub<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "ussub<mode>3"
+  [(set (match_operand:UQADDSUB 0 "s_register_operand" "=r")
+	(us_minus:UQADDSUB
+	  (match_operand:UQADDSUB 1 "s_register_operand" "r")
+	  (match_operand:UQADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "uqsub<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "sssub<mode>3"
+  [(set (match_operand:QADDSUB 0 "s_register_operand" "=r")
+	(ss_minus:QADDSUB (match_operand:QADDSUB 1 "s_register_operand" "r")
+			  (match_operand:QADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "qsub<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+;; Fractional multiplies.
+
+; Note: none of these do any rounding.
+
+(define_expand "mulqq3"
+  [(set (match_operand:QQ 0 "s_register_operand" "")
+	(mult:QQ (match_operand:QQ 1 "s_register_operand" "")
+		 (match_operand:QQ 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY && arm_arch_thumb2"
+{
+  rtx tmp1 = gen_reg_rtx (HImode);
+  rtx tmp2 = gen_reg_rtx (HImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_extendqihi2 (tmp1, gen_lowpart (QImode, operands[1])));
+  emit_insn (gen_extendqihi2 (tmp2, gen_lowpart (QImode, operands[2])));
+  emit_insn (gen_mulhisi3 (tmp3, tmp1, tmp2));
+  emit_insn (gen_extv (gen_lowpart (SImode, operands[0]), tmp3, GEN_INT (8),
+		       GEN_INT (7)));
+  DONE;
+})
+
+(define_expand "mulhq3"
+  [(set (match_operand:HQ 0 "s_register_operand" "")
+	(mult:HQ (match_operand:HQ 1 "s_register_operand" "")
+		 (match_operand:HQ 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY && arm_arch_thumb2"
+{
+  rtx tmp = gen_reg_rtx (SImode);
+
+  emit_insn (gen_mulhisi3 (tmp, gen_lowpart (HImode, operands[1]),
+			   gen_lowpart (HImode, operands[2])));
+  /* We're doing a s.15 * s.15 multiplication, getting an s.30 result.  Extract
+     an s.15 value from that.  This won't overflow/saturate for _Fract
+     values.  */
+  emit_insn (gen_extv (gen_lowpart (SImode, operands[0]), tmp,
+		       GEN_INT (16), GEN_INT (15)));
+  DONE;
+})
+
+(define_expand "mulsq3"
+  [(set (match_operand:SQ 0 "s_register_operand" "")
+	(mult:SQ (match_operand:SQ 1 "s_register_operand" "")
+		 (match_operand:SQ 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch3m"
+{
+  rtx tmp1 = gen_reg_rtx (DImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  /* s.31 * s.31 -> s.62 multiplication.  */
+  emit_insn (gen_mulsidi3 (tmp1, gen_lowpart (SImode, operands[1]),
+			   gen_lowpart (SImode, operands[2])));
+  emit_insn (gen_lshrsi3 (tmp2, gen_lowpart (SImode, tmp1), GEN_INT (31)));
+  emit_insn (gen_ashlsi3 (tmp3, gen_highpart (SImode, tmp1), GEN_INT (1)));
+  emit_insn (gen_iorsi3 (gen_lowpart (SImode, operands[0]), tmp2, tmp3));
+
+  DONE;
+})
+
+;; Accumulator multiplies.
+
+(define_expand "mulsa3"
+  [(set (match_operand:SA 0 "s_register_operand" "")
+	(mult:SA (match_operand:SA 1 "s_register_operand" "")
+		 (match_operand:SA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch3m"
+{
+  rtx tmp1 = gen_reg_rtx (DImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_mulsidi3 (tmp1, gen_lowpart (SImode, operands[1]),
+			   gen_lowpart (SImode, operands[2])));
+  emit_insn (gen_lshrsi3 (tmp2, gen_lowpart (SImode, tmp1), GEN_INT (15)));
+  emit_insn (gen_ashlsi3 (tmp3, gen_highpart (SImode, tmp1), GEN_INT (17)));
+  emit_insn (gen_iorsi3 (gen_lowpart (SImode, operands[0]), tmp2, tmp3));
+
+  DONE;
+})
+
+(define_expand "mulusa3"
+  [(set (match_operand:USA 0 "s_register_operand" "")
+	(mult:USA (match_operand:USA 1 "s_register_operand" "")
+		  (match_operand:USA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch3m"
+{
+  rtx tmp1 = gen_reg_rtx (DImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_umulsidi3 (tmp1, gen_lowpart (SImode, operands[1]),
+			    gen_lowpart (SImode, operands[2])));
+  emit_insn (gen_lshrsi3 (tmp2, gen_lowpart (SImode, tmp1), GEN_INT (16)));
+  emit_insn (gen_ashlsi3 (tmp3, gen_highpart (SImode, tmp1), GEN_INT (16)));
+  emit_insn (gen_iorsi3 (gen_lowpart (SImode, operands[0]), tmp2, tmp3));
+  
+  DONE;
+})
+
+;; The code sequence emitted by this insn pattern uses the Q flag, which GCC
+;; doesn't generally know about, so we don't bother expanding to individual
+;; instructions.  It may be better to just use an out-of-line asm libcall for
+;; this.
+
+(define_insn "ssmulsa3"
+  [(set (match_operand:SA 0 "s_register_operand" "=r")
+	(ss_mult:SA (match_operand:SA 1 "s_register_operand" "r")
+		    (match_operand:SA 2 "s_register_operand" "r")))
+   (clobber (match_scratch:DI 3 "=r"))
+   (clobber (match_scratch:SI 4 "=r"))
+   (clobber (reg:CC CC_REGNUM))]
+  "TARGET_32BIT && arm_arch6"
+{
+  /* s16.15 * s16.15 -> s32.30.  */
+  output_asm_insn ("smull\\t%Q3, %R3, %1, %2", operands);
+
+  if (TARGET_ARM)
+    output_asm_insn ("msr\\tAPSR_nzcvq, #0", operands);
+  else
+    {
+      output_asm_insn ("mov\\t%4, #0", operands);
+      output_asm_insn ("msr\\tAPSR_nzcvq, %4", operands);
+    }
+
+  /* We have:
+      31  high word  0     31  low word  0 
+
+    [ S i i .... i i i ] [ i f f f ... f f ]
+                        |
+			v
+	     [ S i ... i f ... f f ]
+
+    Need 16 integral bits, so saturate at 15th bit of high word.  */
+
+  output_asm_insn ("ssat\\t%R3, #15, %R3", operands);
+  output_asm_insn ("mrs\\t%4, APSR", operands);
+  output_asm_insn ("tst\\t%4, #1<<27", operands);
+  if (TARGET_THUMB2)
+    output_asm_insn ("it\\tne", operands);
+  output_asm_insn ("mvnne\\t%Q3, %R3, asr #32", operands);
+  output_asm_insn ("mov\\t%0, %Q3, lsr #15", operands);
+  output_asm_insn ("orr\\t%0, %0, %R3, asl #17", operands);
+  return "";
+}
+  [(set_attr "conds" "clob")
+   (set (attr "length")
+	(if_then_else (eq_attr "is_thumb" "yes")
+		      (const_int 38)
+		      (const_int 32)))])
+
+;; Same goes for this.
+
+(define_insn "usmulusa3"
+  [(set (match_operand:USA 0 "s_register_operand" "=r")
+	(us_mult:USA (match_operand:USA 1 "s_register_operand" "r")
+		     (match_operand:USA 2 "s_register_operand" "r")))
+   (clobber (match_scratch:DI 3 "=r"))
+   (clobber (match_scratch:SI 4 "=r"))
+   (clobber (reg:CC CC_REGNUM))]
+  "TARGET_32BIT && arm_arch6"
+{
+  /* 16.16 * 16.16 -> 32.32.  */
+  output_asm_insn ("umull\\t%Q3, %R3, %1, %2", operands);
+
+  if (TARGET_ARM)
+    output_asm_insn ("msr\\tAPSR_nzcvq, #0", operands);
+  else
+    {
+      output_asm_insn ("mov\\t%4, #0", operands);
+      output_asm_insn ("msr\\tAPSR_nzcvq, %4", operands);
+    }
+
+  /* We have:
+      31  high word  0     31  low word  0 
+
+    [ i i i .... i i i ] [ f f f f ... f f ]
+                        |
+			v
+	     [ i i ... i f ... f f ]
+
+    Need 16 integral bits, so saturate at 16th bit of high word.  */
+
+  output_asm_insn ("usat\\t%R3, #16, %R3", operands);
+  output_asm_insn ("mrs\\t%4, APSR", operands);
+  output_asm_insn ("tst\\t%4, #1<<27", operands);
+  if (TARGET_THUMB2)
+    output_asm_insn ("it\\tne", operands);
+  output_asm_insn ("sbfxne\\t%Q3, %R3, #15, #1", operands);
+  output_asm_insn ("lsr\\t%0, %Q3, #16", operands);
+  output_asm_insn ("orr\\t%0, %0, %R3, asl #16", operands);
+  return "";
+}
+  [(set_attr "conds" "clob")
+   (set (attr "length")
+	(if_then_else (eq_attr "is_thumb" "yes")
+		      (const_int 38)
+		      (const_int 32)))])
+
+(define_expand "mulha3"
+  [(set (match_operand:HA 0 "s_register_operand" "")
+	(mult:HA (match_operand:HA 1 "s_register_operand" "")
+		 (match_operand:HA 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY && arm_arch_thumb2"
+{
+  rtx tmp = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_mulhisi3 (tmp, gen_lowpart (HImode, operands[1]),
+			   gen_lowpart (HImode, operands[2])));
+  emit_insn (gen_extv (gen_lowpart (SImode, operands[0]), tmp, GEN_INT (16),
+		       GEN_INT (7)));
+
+  DONE;
+})
+
+(define_expand "muluha3"
+  [(set (match_operand:UHA 0 "s_register_operand" "")
+	(mult:UHA (match_operand:UHA 1 "s_register_operand" "")
+		  (match_operand:UHA 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY"
+{
+  rtx tmp1 = gen_reg_rtx (SImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  /* 8.8 * 8.8 -> 16.16 multiply.  */
+  emit_insn (gen_zero_extendhisi2 (tmp1, gen_lowpart (HImode, operands[1])));
+  emit_insn (gen_zero_extendhisi2 (tmp2, gen_lowpart (HImode, operands[2])));
+  emit_insn (gen_mulsi3 (tmp3, tmp1, tmp2));
+  emit_insn (gen_extzv (gen_lowpart (SImode, operands[0]), tmp3,
+			GEN_INT (16), GEN_INT (8)));
+
+  DONE;
+})
+
+(define_expand "ssmulha3"
+  [(set (match_operand:HA 0 "s_register_operand" "")
+	(ss_mult:HA (match_operand:HA 1 "s_register_operand" "")
+		    (match_operand:HA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && TARGET_DSP_MULTIPLY && arm_arch6"
+{
+  rtx tmp = gen_reg_rtx (SImode);
+  rtx rshift;
+  
+  emit_insn (gen_mulhisi3 (tmp, gen_lowpart (HImode, operands[1]),
+			   gen_lowpart (HImode, operands[2])));
+
+  rshift = gen_rtx_ASHIFTRT (SImode, tmp, GEN_INT (7));
+
+  emit_insn (gen_rtx_SET (VOIDmode, gen_lowpart (HImode, operands[0]),
+			  gen_rtx_SS_TRUNCATE (HImode, rshift)));
+
+  DONE;
+})
+
+(define_expand "usmuluha3"
+  [(set (match_operand:UHA 0 "s_register_operand" "")
+	(us_mult:UHA (match_operand:UHA 1 "s_register_operand" "")
+		     (match_operand:UHA 2 "s_register_operand" "")))]
+  "TARGET_INT_SIMD"
+{
+  rtx tmp1 = gen_reg_rtx (SImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  rtx rshift_tmp = gen_reg_rtx (SImode);
+  
+  /* Note: there's no smul[bt][bt] equivalent for unsigned multiplies.  Use a
+     normal 32x32->32-bit multiply instead.  */
+  emit_insn (gen_zero_extendhisi2 (tmp1, gen_lowpart (HImode, operands[1])));
+  emit_insn (gen_zero_extendhisi2 (tmp2, gen_lowpart (HImode, operands[2])));
+  
+  emit_insn (gen_mulsi3 (tmp3, tmp1, tmp2));
+
+  /* The operand to "usat" is signed, so we cannot use the "..., asr #8"
+     form of that instruction since the multiplication result TMP3 may have the
+     top bit set, thus be negative and saturate to zero.  Use a separate
+     logical right-shift instead.  */
+  emit_insn (gen_lshrsi3 (rshift_tmp, tmp3, GEN_INT (8)));
+  emit_insn (gen_arm_usatsihi (gen_lowpart (HImode, operands[0]), rshift_tmp));
+
+  DONE;
+})
+
+(define_insn "arm_ssatsihi_shift"
+  [(set (match_operand:HI 0 "s_register_operand" "=r")
+	(ss_truncate:HI (match_operator:SI 1 "sat_shift_operator"
+			  [(match_operand:SI 2 "s_register_operand" "r")
+			   (match_operand:SI 3 "immediate_operand" "I")])))]
+  "TARGET_32BIT && arm_arch6"
+  "ssat%?\\t%0, #16, %2%S1"
+  [(set_attr "predicable" "yes")
+   (set_attr "type" "alu_shift")])
+
+(define_insn "arm_usatsihi"
+  [(set (match_operand:HI 0 "s_register_operand" "=r")
+	(us_truncate:HI (match_operand:SI 1 "s_register_operand")))]
+  "TARGET_INT_SIMD"
+  "usat%?\\t%0, #16, %1"
+  [(set_attr "predicable" "yes")])
diff --git a/gcc/config/arm/arm-modes.def b/gcc/config/arm/arm-modes.def
index 24e3d90..7f19ebe 100644
--- a/gcc/config/arm/arm-modes.def
+++ b/gcc/config/arm/arm-modes.def
@@ -70,6 +70,12 @@ VECTOR_MODES (INT, 16);       /* V16QI V8HI V4SI V2DI */
 VECTOR_MODES (FLOAT, 8);      /*            V4HF V2SF */
 VECTOR_MODES (FLOAT, 16);     /*       V8HF V4SF V2DF */
 
+/* Fraction and accumulator vector modes.  */
+VECTOR_MODES (FRACT, 4);      /* V4QQ  V2HQ */
+VECTOR_MODES (UFRACT, 4);     /* V4UQQ V2UHQ */
+VECTOR_MODES (ACCUM, 4);      /*       V2HA */
+VECTOR_MODES (UACCUM, 4);     /*       V2UHA */
+
 /* Opaque integer modes for 3, 4, 6 or 8 Neon double registers (2 is
    TImode).  */
 INT_MODE (EI, 24);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index cd32fe3..5a62802 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -986,6 +986,49 @@ bit_count (unsigned long value)
   return count;
 }
 
+typedef struct
+{
+  enum machine_mode mode;
+  const char *name;
+} arm_fixed_mode_set;
+
+/* A small helper for setting fixed-point library libfuncs.  */
+
+static void
+arm_set_fixed_optab_libfunc (optab optable, enum machine_mode mode,
+			     const char *funcname, const char *modename,
+			     int num_suffix)
+{
+  char buffer[50];
+  
+  if (num_suffix == 0)
+    sprintf (buffer, "__gnu_%s%s", funcname, modename);
+  else
+    sprintf (buffer, "__gnu_%s%s%d", funcname, modename, num_suffix);
+  
+  set_optab_libfunc (optable, mode, buffer);
+}
+
+static void
+arm_set_fixed_conv_libfunc (convert_optab optable, enum machine_mode to,
+			    enum machine_mode from, const char *funcname,
+			    const char *toname, const char *fromname)
+{
+  char buffer[50];
+  char *maybe_suffix_2 = "";
+  
+  /* Follow the logic for selecting a "2" suffix in fixed-bit.h.  */
+  if (ALL_FIXED_POINT_MODE_P (from) && ALL_FIXED_POINT_MODE_P (to)
+      && UNSIGNED_FIXED_POINT_MODE_P (from) == UNSIGNED_FIXED_POINT_MODE_P (to)
+      && ALL_FRACT_MODE_P (from) == ALL_FRACT_MODE_P (to))
+    maybe_suffix_2 = "2";
+  
+  sprintf (buffer, "__gnu_%s%s%s%s", funcname, fromname, toname,
+	   maybe_suffix_2);
+
+  set_conv_libfunc (optable, to, from, buffer);
+}
+
 /* Set up library functions unique to ARM.  */
 
 static void
@@ -1131,6 +1174,137 @@ arm_init_libfuncs (void)
       break;
     }
 
+  /* Use names prefixed with __gnu_ for fixed-point helper functions.  */
+  {
+    const arm_fixed_mode_set fixed_arith_modes[] =
+      {
+	{ QQmode, "qq" },
+	{ UQQmode, "uqq" },
+	{ HQmode, "hq" },
+	{ UHQmode, "uhq" },
+	{ SQmode, "sq" },
+	{ USQmode, "usq" },
+	{ DQmode, "dq" },
+	{ UDQmode, "udq" },
+	{ TQmode, "tq" },
+	{ UTQmode, "utq" },
+	{ HAmode, "ha" },
+	{ UHAmode, "uha" },
+	{ SAmode, "sa" },
+	{ USAmode, "usa" },
+	{ DAmode, "da" },
+	{ UDAmode, "uda" },
+	{ TAmode, "ta" },
+	{ UTAmode, "uta" }
+      };
+    const arm_fixed_mode_set fixed_conv_modes[] =
+      {
+	{ QQmode, "qq" },
+	{ UQQmode, "uqq" },
+	{ HQmode, "hq" },
+	{ UHQmode, "uhq" },
+	{ SQmode, "sq" },
+	{ USQmode, "usq" },
+	{ DQmode, "dq" },
+	{ UDQmode, "udq" },
+	{ TQmode, "tq" },
+	{ UTQmode, "utq" },
+	{ HAmode, "ha" },
+	{ UHAmode, "uha" },
+	{ SAmode, "sa" },
+	{ USAmode, "usa" },
+	{ DAmode, "da" },
+	{ UDAmode, "uda" },
+	{ TAmode, "ta" },
+	{ UTAmode, "uta" },
+	{ QImode, "qi" },
+	{ HImode, "hi" },
+	{ SImode, "si" },
+	{ DImode, "di" },
+	{ TImode, "ti" },
+	{ SFmode, "sf" },
+	{ DFmode, "df" }
+      };
+    unsigned int i, j;
+
+    for (i = 0; i < ARRAY_SIZE (fixed_arith_modes); i++)
+      {
+	arm_set_fixed_optab_libfunc (add_optab, fixed_arith_modes[i].mode,
+				     "add", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssadd_optab, fixed_arith_modes[i].mode,
+				     "ssadd", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usadd_optab, fixed_arith_modes[i].mode,
+				     "usadd", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (sub_optab, fixed_arith_modes[i].mode,
+				     "sub", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (sssub_optab, fixed_arith_modes[i].mode,
+				     "sssub", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ussub_optab, fixed_arith_modes[i].mode,
+				     "ussub", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (smul_optab, fixed_arith_modes[i].mode,
+				     "mul", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssmul_optab, fixed_arith_modes[i].mode,
+				     "ssmul", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usmul_optab, fixed_arith_modes[i].mode,
+				     "usmul", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (sdiv_optab, fixed_arith_modes[i].mode,
+				     "div", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (udiv_optab, fixed_arith_modes[i].mode,
+				     "udiv", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssdiv_optab, fixed_arith_modes[i].mode,
+				     "ssdiv", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usdiv_optab, fixed_arith_modes[i].mode,
+				     "usdiv", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (neg_optab, fixed_arith_modes[i].mode,
+				     "neg", fixed_arith_modes[i].name, 2);
+	arm_set_fixed_optab_libfunc (ssneg_optab, fixed_arith_modes[i].mode,
+				     "ssneg", fixed_arith_modes[i].name, 2);
+	arm_set_fixed_optab_libfunc (usneg_optab, fixed_arith_modes[i].mode,
+				     "usneg", fixed_arith_modes[i].name, 2);
+	arm_set_fixed_optab_libfunc (ashl_optab, fixed_arith_modes[i].mode,
+				     "ashl", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ashr_optab, fixed_arith_modes[i].mode,
+				     "ashr", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (lshr_optab, fixed_arith_modes[i].mode,
+				     "lshr", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssashl_optab, fixed_arith_modes[i].mode,
+				     "ssashl", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usashl_optab, fixed_arith_modes[i].mode,
+				     "usashl", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (cmp_optab, fixed_arith_modes[i].mode,
+				     "cmp", fixed_arith_modes[i].name, 2);
+      }
+
+    for (i = 0; i < ARRAY_SIZE (fixed_conv_modes); i++)
+      for (j = 0; j < ARRAY_SIZE (fixed_conv_modes); j++)
+	{
+	  if (i == j
+	      || (!ALL_FIXED_POINT_MODE_P (fixed_conv_modes[i].mode)
+		  && !ALL_FIXED_POINT_MODE_P (fixed_conv_modes[j].mode)))
+	    continue;
+
+	  arm_set_fixed_conv_libfunc (fract_optab, fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "fract",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	  arm_set_fixed_conv_libfunc (satfract_optab,
+				      fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "satfract",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	  arm_set_fixed_conv_libfunc (fractuns_optab,
+				      fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "fractuns",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	  arm_set_fixed_conv_libfunc (satfractuns_optab,
+				      fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "satfractuns",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	}
+  }
+
   if (TARGET_AAPCS_BASED)
     synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
 }
@@ -4137,6 +4311,10 @@ aapcs_allocate_return_reg (enum machine_mode mode, const_tree type,
 rtx
 aapcs_libcall_value (enum machine_mode mode)
 {
+  if (BYTES_BIG_ENDIAN && ALL_FIXED_POINT_MODE_P (mode)
+      && GET_MODE_SIZE (mode) <= 4)
+    mode = SImode;
+
   return aapcs_allocate_return_reg (mode, NULL_TREE, NULL_TREE);
 }
 
@@ -9037,8 +9215,9 @@ arm_return_in_msb (const_tree valtype)
 {
   return (TARGET_AAPCS_BASED
           && BYTES_BIG_ENDIAN
-          && (AGGREGATE_TYPE_P (valtype)
-              || TREE_CODE (valtype) == COMPLEX_TYPE));
+	  && (AGGREGATE_TYPE_P (valtype)
+	      || TREE_CODE (valtype) == COMPLEX_TYPE
+	      || FIXED_POINT_TYPE_P (valtype)));
 }
 
 /* Returns TRUE if INSN is an "LDR REG, ADDR" instruction.
@@ -11401,7 +11580,8 @@ arm_pad_reg_upward (enum machine_mode mode ATTRIBUTE_UNUSED,
 {
   if (TARGET_AAPCS_BASED
       && BYTES_BIG_ENDIAN
-      && (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE)
+      && (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE
+	  || FIXED_POINT_TYPE_P (type))
       && int_size_in_bytes (type) <= 4)
     return true;
 
@@ -19539,6 +19719,8 @@ arm_scalar_mode_supported_p (enum machine_mode mode)
 {
   if (mode == HFmode)
     return (arm_fp16_format != ARM_FP16_FORMAT_NONE);
+  else if (ALL_FIXED_POINT_MODE_P (mode))
+    return true;
   else
     return default_scalar_mode_supported_p (mode);
 }
@@ -22664,6 +22846,11 @@ arm_vector_mode_supported_p (enum machine_mode mode)
 	  || (mode == V8QImode)))
     return true;
 
+  if (TARGET_INT_SIMD && (mode == V4UQQmode || mode == V4QQmode
+      || mode == V2UHQmode || mode == V2HQmode || mode == V2UHAmode
+      || mode == V2HAmode))
+    return true;
+
   return false;
 }
 
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index 86d842d..7a5dd01 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -592,6 +592,20 @@ extern int arm_arch_hwdiv;
 #define WCHAR_TYPE_SIZE BITS_PER_WORD
 #endif
 
+/* Sized for fixed-point types.  */
+
+#define SHORT_FRACT_TYPE_SIZE 8
+#define FRACT_TYPE_SIZE 16
+#define LONG_FRACT_TYPE_SIZE 32
+#define LONG_LONG_FRACT_TYPE_SIZE 64
+
+#define SHORT_ACCUM_TYPE_SIZE 16
+#define ACCUM_TYPE_SIZE 32
+#define LONG_ACCUM_TYPE_SIZE 64
+#define LONG_LONG_ACCUM_TYPE_SIZE 64
+
+#define MAX_FIXED_MODE_SIZE 64
+
 #ifndef SIZE_TYPE
 #define SIZE_TYPE (TARGET_AAPCS_BASED ? "unsigned int" : "long unsigned int")
 #endif
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 12fd7ca..72f862e 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -10935,3 +10935,5 @@
 (include "neon.md")
 ;; Synchronization Primitives
 (include "sync.md")
+;; Fixed-point patterns
+(include "arm-fixed.md")
diff --git a/gcc/config/arm/fixed-bit-machine.h b/gcc/config/arm/fixed-bit-machine.h
new file mode 100644
index 0000000..9f3bf5b
--- /dev/null
+++ b/gcc/config/arm/fixed-bit-machine.h
@@ -0,0 +1,30 @@
+/* Machine-specific override for fixed-point support routine names.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published
+   by the Free Software Foundation; either version 3, or (at your
+   option) any later version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   Under Section 7 of GPL version 3, you are granted additional
+   permissions described in the GCC Runtime Library Exception, version
+   3.1, as published by the Free Software Foundation.
+
+   You should have received a copy of the GNU General Public License and
+   a copy of the GCC Runtime Library Exception along with this program;
+   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* To be EABI-compliant (until the EABI supports fixed-point functionality),
+   use the __gnu_ namespace for fixed-point helper functions.  */
+
+#ifdef __ARM_EABI__
+#define DECORATE_FIXED_NAME(X) __gnu_ ## X
+#endif
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index 891a974..f1800dc 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -227,6 +227,13 @@
 	    (match_code "ashift,ashiftrt,lshiftrt,rotatert"))
        (match_test "mode == GET_MODE (op)")))
 
+;; True for shift operators which can be used with saturation instructions.
+(define_special_predicate "sat_shift_operator"
+  (and (match_code "ashift,ashiftrt")
+       (match_test "GET_CODE (XEXP (op, 1)) == CONST_INT
+		    && ((unsigned HOST_WIDE_INT) INTVAL (XEXP (op, 1)) <= 32)")
+       (match_test "mode == GET_MODE (op)")))
+
 ;; True for MULT, to identify which variant of shift_operator is in use.
 (define_special_predicate "mult_operator"
   (match_code "mult"))
diff --git a/gcc/config/arm/t-arm b/gcc/config/arm/t-arm
index 826ec0a..b970ec2 100644
--- a/gcc/config/arm/t-arm
+++ b/gcc/config/arm/t-arm
@@ -37,7 +37,8 @@ MD_INCLUDES= 	$(srcdir)/config/arm/arm-tune.md \
 		$(srcdir)/config/arm/iwmmxt.md \
 		$(srcdir)/config/arm/vfp.md \
 		$(srcdir)/config/arm/neon.md \
-		$(srcdir)/config/arm/thumb2.md
+		$(srcdir)/config/arm/thumb2.md \
+		$(srcdir)/config/arm/arm-fixed.md
 
 LIB1ASMSRC = arm/lib1funcs.asm
 LIB1ASMFUNCS = _thumb1_case_sqi _thumb1_case_uqi _thumb1_case_shi \
diff --git a/gcc/configure b/gcc/configure
index 274af3e..aba11bf 100755
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 9ace66d..db783ea 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -638,6 +638,10 @@ AC_ARG_ENABLE(fixed-point,
 [],
 [
   case $target in
+    arm*)
+      enable_fixed_point=yes
+      ;;
+
     mips*-*-*)
       case $host in
 	mips*-sgi-irix*)
diff --git a/gcc/testsuite/gcc.target/arm/fixed-point-exec.c b/gcc/testsuite/gcc.target/arm/fixed-point-exec.c
new file mode 100644
index 0000000..6bc3b07
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/fixed-point-exec.c
@@ -0,0 +1,301 @@
+/* { dg-do run { target { fixed_point } } } */
+/* { dg-options "-std=gnu99" } */
+
+/* Check basic arithmetic ops for ARM fixed-point/saturating operation support.
+   Not target-independent since we make various assumptions about precision and
+   magnitudes of various types.  */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdfix.h>
+
+#define TEST(TYPE, OP, NAME, SUFFIX)				\
+  TYPE NAME##SUFFIX (TYPE A, TYPE B)				\
+    {								\
+      return A OP B;						\
+    }
+
+#define VARIANTS(TYPE, OP, NAME)				\
+  TEST (short TYPE, OP, NAME, _short);				\
+  TEST (TYPE, OP, NAME, _regular);				\
+  TEST (long TYPE, OP, NAME, _long);				\
+  TEST (_Sat short TYPE, OP, NAME, _sat_short);			\
+  TEST (_Sat TYPE, OP, NAME, _sat_regular);			\
+  TEST (_Sat long TYPE, OP, NAME, _sat_long);			\
+  TEST (unsigned short TYPE, OP, NAME, _uns_short);		\
+  TEST (unsigned TYPE, OP, NAME, _uns_regular);			\
+  TEST (unsigned long TYPE, OP, NAME, _uns_long);		\
+  TEST (unsigned _Sat short TYPE, OP, NAME, _uns_sat_short);	\
+  TEST (unsigned _Sat TYPE, OP, NAME, _uns_sat_regular);	\
+  TEST (unsigned _Sat long TYPE, OP, NAME, _uns_sat_long)
+
+VARIANTS (_Fract, +, plus_fract);
+VARIANTS (_Accum, +, plus_accum);
+VARIANTS (_Fract, -, minus_fract);
+VARIANTS (_Accum, -, minus_accum);
+VARIANTS (_Fract, *, mult_fract);
+VARIANTS (_Accum, *, mult_accum);
+VARIANTS (_Accum, /, div_accum);
+
+/* Inputs for signed add, multiply fractional tests.  */
+short _Fract sf_a = 0.9hr;
+short _Fract sf_b = -0.8hr;
+_Fract f_a = 0.9r;
+_Fract f_b = -0.8r;
+long _Fract lf_a = 0.9lr;
+long _Fract lf_b = -0.8lr;
+
+/* Inputs for signed subtract fractional tests.  */
+short _Fract sf_c = 0.7hr;
+short _Fract sf_d = 0.9hr;
+_Fract f_c = 0.7r;
+_Fract f_d = 0.9r;
+long _Fract lf_c = 0.7lr;
+long _Fract lf_d = 0.9lr;
+
+/* Inputs for unsigned add, subtract, multiply fractional tests.  */
+unsigned short _Fract usf_a = 0.4uhr;
+unsigned short _Fract usf_b = 0.3uhr;
+unsigned _Fract uf_a = 0.4ur;
+unsigned _Fract uf_b = 0.3ur;
+unsigned long _Fract ulf_a = 0.4ulr;
+unsigned long _Fract ulf_b = 0.3ulr;
+
+/* Inputs for saturating signed add tests.  */
+short _Sat _Fract sf_e = 0.8hr;
+short _Sat _Fract sf_f = 0.8hr;
+_Sat _Fract f_e = 0.8r;
+_Sat _Fract f_f = 0.8r;
+long _Sat _Fract lf_e = 0.8r;
+long _Sat _Fract lf_f = 0.8r;
+
+short _Sat _Fract sf_g = -0.8hr;
+short _Sat _Fract sf_h = -0.8hr;
+_Sat _Fract f_g = -0.8r;
+_Sat _Fract f_h = -0.8r;
+long _Sat _Fract lf_g = -0.8r;
+long _Sat _Fract lf_h = -0.8r;
+
+/* Inputs for saturating unsigned subtract tests.  */
+unsigned short _Sat _Fract usf_c = 0.3uhr;
+unsigned short _Sat _Fract usf_d = 0.4uhr;
+unsigned _Sat _Fract uf_c = 0.3ur;
+unsigned _Sat _Fract uf_d = 0.4ur;
+unsigned long _Sat _Fract ulf_c = 0.3ulr;
+unsigned long _Sat _Fract ulf_d = 0.4ulr;
+
+/* Inputs for signed accumulator tests.  */
+
+short _Accum sa_a = 1.25hk;
+short _Accum sa_b = -1.5hk;
+_Accum a_a = 100.25k;
+_Accum a_b = -100.5k;
+long _Accum la_a = 1000.25lk;
+long _Accum la_b = -1000.5lk;
+
+/* Inputs for unsigned accumulator tests.  */
+
+unsigned short _Accum usa_a = 2.5uhk;
+unsigned short _Accum usa_b = 1.75uhk;
+unsigned _Accum ua_a = 255.5uk;
+unsigned _Accum ua_b = 170.25uk;
+unsigned long _Accum ula_a = 1550.5ulk;
+unsigned long _Accum ula_b = 999.5ulk;
+
+/* Inputs for signed saturating accumulator tests.  */
+
+short _Sat _Accum sa_c = 240.0hk;
+short _Sat _Accum sa_d = 250.0hk;
+short _Sat _Accum sa_e = -240.0hk;
+short _Sat _Accum sa_f = -250.0hk;
+short _Sat _Accum sa_g = 0.5hk;
+
+_Sat _Accum a_c = 65000.0k;
+_Sat _Accum a_d = 20000.0k;
+_Sat _Accum a_e = -65000.0k;
+_Sat _Accum a_f = -20000.0k;
+_Sat _Accum a_g = 0.5k;
+
+long _Sat _Accum la_c = 3472883712.0lk;
+long _Sat _Accum la_d = 3456106496.0lk;
+long _Sat _Accum la_e = -3472883712.0lk;
+long _Sat _Accum la_f = -3456106496.0lk;
+long _Sat _Accum la_g = 0.5lk;
+
+/* Inputs for unsigned saturating accumulator tests.  */
+
+unsigned short _Sat _Accum usa_c = 250.0uhk;
+unsigned short _Sat _Accum usa_d = 240.0uhk;
+unsigned short _Sat _Accum usa_e = 0.5uhk;
+
+unsigned _Sat _Accum ua_c = 65000.0uk;
+unsigned _Sat _Accum ua_d = 20000.0uk;
+unsigned _Sat _Accum ua_e = 0.5uk;
+
+unsigned long _Sat _Accum ula_c = 3472883712.0ulk;
+unsigned long _Sat _Accum ula_d = 3456106496.0ulk;
+unsigned long _Sat _Accum ula_e = 0.5ulk;
+
+#define CHECK(FN, EXP) do {						     \
+  if (fabs ((float) (FN) - (EXP)) > 0.05)				     \
+    {									     \
+      fprintf (stderr, "result for " #FN " (as float): %f\n", (double) (FN));\
+      abort ();								     \
+    }									     \
+  } while (0)
+
+#define CHECK_EXACT(FN, EXP) do {					     \
+  if ((FN) != (EXP))							     \
+    {									     \
+      fprintf (stderr, "result for " #FN " (as float): %f, should be %f\n",  \
+	       (double) (FN), (double) (EXP));				     \
+      abort ();								     \
+    }									     \
+  } while (0)
+
+int
+main (int argc, char *argv[])
+{
+  /* Fract/fract operations, non-saturating.  */
+
+  CHECK (plus_fract_short (sf_a, sf_b), 0.1);
+  CHECK (plus_fract_regular (f_a, f_b), 0.1);
+  CHECK (plus_fract_long (lf_a, lf_b), 0.1);
+
+  CHECK (plus_fract_uns_short (usf_a, usf_b), 0.7);
+  CHECK (plus_fract_uns_regular (uf_a, uf_b), 0.7);
+  CHECK (plus_fract_uns_long (ulf_a, ulf_b), 0.7);
+
+  CHECK (minus_fract_short (sf_c, sf_d), -0.2);
+  CHECK (minus_fract_regular (f_c, f_d), -0.2);
+  CHECK (minus_fract_long (lf_c, lf_d), -0.2);
+
+  CHECK (minus_fract_uns_short (usf_a, usf_b), 0.1);
+  CHECK (minus_fract_uns_regular (uf_a, uf_b), 0.1);
+  CHECK (minus_fract_uns_long (ulf_a, ulf_b), 0.1);
+
+  CHECK (mult_fract_short (sf_a, sf_b), -0.72);
+  CHECK (mult_fract_regular (f_a, f_b), -0.72);
+  CHECK (mult_fract_long (lf_a, lf_b), -0.72);
+
+  CHECK (mult_fract_uns_short (usf_a, usf_b), 0.12);
+  CHECK (mult_fract_uns_regular (uf_a, uf_b), 0.12);
+  CHECK (mult_fract_uns_long (ulf_a, ulf_b), 0.12);
+
+  /* Fract/fract operations, saturating.  */
+
+  CHECK (plus_fract_sat_short (sf_e, sf_f), 1.0);
+  CHECK (plus_fract_sat_regular (f_e, f_f), 1.0);
+  CHECK (plus_fract_sat_long (lf_e, lf_f), 1.0);
+
+  CHECK (plus_fract_sat_short (sf_g, sf_h), -1.0);
+  CHECK (plus_fract_sat_regular (f_g, f_h), -1.0);
+  CHECK (plus_fract_sat_long (lf_g, lf_h), -1.0);
+  
+  CHECK (plus_fract_uns_sat_short (sf_e, sf_f), 1.0);
+  CHECK (plus_fract_uns_sat_regular (f_e, f_f), 1.0);
+  CHECK (plus_fract_uns_sat_long (lf_e, lf_f), 1.0);
+
+  CHECK (plus_fract_sat_short (sf_a, sf_b), 0.1);
+  CHECK (plus_fract_sat_regular (f_a, f_b), 0.1);
+  CHECK (plus_fract_sat_long (lf_a, lf_b), 0.1);
+  
+  CHECK (plus_fract_uns_sat_short (usf_a, usf_b), 0.7);
+  CHECK (plus_fract_uns_sat_regular (uf_a, uf_b), 0.7);
+  CHECK (plus_fract_uns_sat_long (ulf_a, ulf_b), 0.7);
+
+  CHECK (minus_fract_uns_sat_short (usf_c, usf_d), 0.0);
+  CHECK (minus_fract_uns_sat_regular (uf_c, uf_d), 0.0);
+  CHECK (minus_fract_uns_sat_short (ulf_c, ulf_d), 0.0);
+  
+  CHECK (minus_fract_sat_short (sf_c, sf_d), -0.2);
+  CHECK (minus_fract_sat_regular (f_c, f_d), -0.2);
+  CHECK (minus_fract_sat_long (lf_c, lf_d), -0.2);
+
+  /* Accum/accum operations, non-saturating.  */
+
+  CHECK (plus_accum_short (sa_a, sa_b), -0.25);
+  CHECK (plus_accum_regular (a_a, a_b), -0.25);
+  CHECK (plus_accum_long (la_a, la_b), -0.25);
+
+  CHECK (minus_accum_short (sa_a, sa_b), 2.75);
+  CHECK (minus_accum_regular (a_a, a_b), 200.75);
+  CHECK (minus_accum_long (la_a, la_b), 2000.75);
+  
+  CHECK (mult_accum_short (sa_a, sa_b), -1.875);
+  CHECK (mult_accum_regular (a_a, a_b), -10075.125);
+  CHECK (mult_accum_long (la_a, la_b), -1000750.125);
+
+  CHECK (div_accum_short (sa_a, sa_b), -1.25/1.5);
+  CHECK (div_accum_regular (a_a, a_b), -100.25/100.5);
+  CHECK (div_accum_long (la_a, la_b), -1000.25/1000.5);
+
+  /* Unsigned accum/accum operations, non-saturating.  */
+  
+  CHECK (plus_accum_uns_short (usa_a, usa_b), 4.25);
+  CHECK (plus_accum_uns_regular (ua_a, ua_b), 425.75);
+  CHECK (plus_accum_uns_long (ula_a, ula_b), 2550.0);
+
+  CHECK (minus_accum_uns_short (usa_a, usa_b), 0.75);
+  CHECK (minus_accum_uns_regular (ua_a, ua_b), 85.25);
+  CHECK (minus_accum_uns_long (ula_a, ula_b), 551.0);
+  
+  CHECK (mult_accum_uns_short (usa_a, usa_b), 4.375);
+  CHECK (mult_accum_uns_regular (ua_a, ua_b), 43498.875);
+  CHECK (mult_accum_uns_long (ula_a, ula_b), 1549724.75);
+
+  CHECK (div_accum_uns_short (usa_a, usa_b), 2.5/1.75);
+  CHECK (div_accum_uns_regular (ua_a, ua_b), 255.5/170.25);
+  CHECK (div_accum_uns_long (ula_a, ula_b), 1550.5/999.5);
+
+  /* Signed accum/accum operations, saturating.  */
+  
+  CHECK_EXACT (plus_accum_sat_short (sa_c, sa_d), SACCUM_MAX);
+  CHECK_EXACT (plus_accum_sat_short (sa_e, sa_f), SACCUM_MIN);
+  CHECK_EXACT (plus_accum_sat_regular (a_c, a_d), ACCUM_MAX);
+  CHECK_EXACT (plus_accum_sat_regular (a_e, a_f), ACCUM_MIN);
+  CHECK_EXACT (plus_accum_sat_long (la_c, la_d), LACCUM_MAX);
+  CHECK_EXACT (plus_accum_sat_long (la_e, la_f), LACCUM_MIN);
+
+  CHECK_EXACT (minus_accum_sat_short (sa_e, sa_d), SACCUM_MIN);
+  CHECK_EXACT (minus_accum_sat_short (sa_c, sa_f), SACCUM_MAX);
+  CHECK_EXACT (minus_accum_sat_regular (a_e, a_d), ACCUM_MIN);
+  CHECK_EXACT (minus_accum_sat_regular (a_c, a_f), ACCUM_MAX);
+  CHECK_EXACT (minus_accum_sat_long (la_e, la_d), LACCUM_MIN);
+  CHECK_EXACT (minus_accum_sat_long (la_c, la_f), LACCUM_MAX);
+
+  CHECK_EXACT (mult_accum_sat_short (sa_c, sa_d), SACCUM_MAX);
+  CHECK_EXACT (mult_accum_sat_short (sa_c, sa_e), SACCUM_MIN);
+  CHECK_EXACT (mult_accum_sat_regular (a_c, a_d), ACCUM_MAX);
+  CHECK_EXACT (mult_accum_sat_regular (a_c, a_e), ACCUM_MIN);
+  CHECK_EXACT (mult_accum_sat_long (la_c, la_d), LACCUM_MAX);
+  CHECK_EXACT (mult_accum_sat_long (la_c, la_e), LACCUM_MIN);
+  
+  CHECK_EXACT (div_accum_sat_short (sa_d, sa_g), SACCUM_MAX);
+  CHECK_EXACT (div_accum_sat_short (sa_e, sa_g), SACCUM_MIN);
+  CHECK_EXACT (div_accum_sat_regular (a_c, a_g), ACCUM_MAX);
+  CHECK_EXACT (div_accum_sat_regular (a_e, a_g), ACCUM_MIN);
+  CHECK_EXACT (div_accum_sat_long (la_d, la_g), LACCUM_MAX);
+  CHECK_EXACT (div_accum_sat_long (la_e, la_g), LACCUM_MIN);
+
+  /* Unsigned accum/accum operations, saturating.  */
+
+  CHECK_EXACT (plus_accum_uns_sat_short (usa_c, usa_d), USACCUM_MAX);
+  CHECK_EXACT (plus_accum_uns_sat_regular (ua_c, ua_d), UACCUM_MAX);
+  CHECK_EXACT (plus_accum_uns_sat_long (ula_c, ula_d), ULACCUM_MAX);
+  
+  CHECK_EXACT (minus_accum_uns_sat_short (usa_d, usa_c), 0uhk);
+  CHECK_EXACT (minus_accum_uns_sat_regular (ua_d, ua_c), 0uk);
+  CHECK_EXACT (minus_accum_uns_sat_long (ula_d, ula_c), 0ulk);
+
+  CHECK_EXACT (mult_accum_uns_sat_short (usa_c, usa_d), USACCUM_MAX);
+  CHECK_EXACT (mult_accum_uns_sat_regular (ua_c, ua_d), UACCUM_MAX);
+  CHECK_EXACT (mult_accum_uns_sat_long (ula_c, ula_d), ULACCUM_MAX);
+
+  CHECK_EXACT (div_accum_uns_sat_short (usa_c, usa_e), USACCUM_MAX);
+  CHECK_EXACT (div_accum_uns_sat_regular (ua_c, ua_e), UACCUM_MAX);
+  CHECK_EXACT (div_accum_uns_sat_long (ula_c, ula_e), ULACCUM_MAX);
+
+  return 0;
+}
diff --git a/libgcc/config.host b/libgcc/config.host
index e3c48bb..5f24004 100644
--- a/libgcc/config.host
+++ b/libgcc/config.host
@@ -207,12 +207,15 @@ arm*-*-freebsd*)
 arm*-*-netbsdelf*)
 	;;
 arm*-*-linux*)			# ARM GNU/Linux with ELF
+	tmake_file="${tmake_file} arm/t-fixed-point"
 	;;
 arm*-*-uclinux*)		# ARM ucLinux
+	tmake_file="${tmake_file} arm/t-fixed-point"
 	;;
 arm*-*-ecos-elf)
 	;;
 arm*-*-eabi* | arm*-*-symbianelf* )
+	tmake_file="${tmake_file} arm/t-fixed-point"
 	;;
 arm*-*-rtems*)
 	;;
diff --git a/libgcc/config/arm/t-fixed-point b/libgcc/config/arm/t-fixed-point
new file mode 100644
index 0000000..66346dd
--- /dev/null
+++ b/libgcc/config/arm/t-fixed-point
@@ -0,0 +1 @@
+fixed_bit_machine_header := arm/fixed-bit-machine.h

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] ARM fixed-point support [6/6]: target-specific parts
  2011-05-26 17:39     ` Julian Brown
@ 2011-06-30 14:01       ` Richard Earnshaw
  2011-07-29 16:08         ` Julian Brown
  0 siblings, 1 reply; 7+ messages in thread
From: Richard Earnshaw @ 2011-06-30 14:01 UTC (permalink / raw)
  To: Julian Brown
  Cc: gcc-patches, Joseph S. Myers, paul, rearnsha, Ramana Radhakrishnan

On 26/05/11 17:45, Julian Brown wrote:
> On Fri, 13 May 2011 17:31:18 +0100
> Julian Brown <julian@codesourcery.com> wrote:
> 
>> On Fri, 13 May 2011 14:54:47 +0000 (UTC)
>> "Joseph S. Myers" <joseph@codesourcery.com> wrote:
>>
>>> On Fri, 13 May 2011, Julian Brown wrote:
>>>
>>>> although Joseph's comments have (hopefully) all been addressed.
>>>> One must now configure with "--enable-fixed-point" to enable the
>>>> fixed-point support.
>>>
>>> No, that's not how this configure logic works; it enables fixed
>>> point by default for the given targets.  The bit that enables it by
>>> default for MIPS (and that you're changing to cover ARM as well) is
>>> the code that is called if the user *doesn't* pass
>>> --enable-fixed-point or --disable-fixed-point.
>>
>> Yes, you're quite right: I had forgotten to run autoconf after
>> changing configure.ac whilst testing. Sorry for the misinformation!
> 
> This is a new version of the patch, which fixes a couple of issues
> discovered on our internal branch since the last version was posted.
> This version handles "long long" fixed point types in the same way as
> 32-bit MIPS: basically the sizes of those types are clamped to 64 bits.
> 
> This means the testsuite changes in:
> 
> http://gcc.gnu.org/ml/gcc-patches/2011-05/msg00969.html
> 
> are no longer required.
> 
> Another couple of problems have also been fixed, relating to big-endian
> mode. One was the use of simplify_gen_subreg with a hard-wired offset in
> a several of the insn expanders, where I should have just used
> gen_highpart -- which works in either endianness, and makes the intent
> clearer anyway.
> 
> The other issue is ABI-related. My previous assertion that fixed-point
> types (smaller than word size) should be passed to and returned from
> functions "like integers" doesn't really work out very well after all
> in big-endian mode, so needs re-thinking. The problem is basically that
> such fixed-point types are incorrectly padded in big-endian mode, so
> the data ends up in a different part of the passed word than expected.
> 
> The way chars and shorts is handled is as follows: when those types are
> passed to a function, they are promoted to word-size ints, which is a
> nop-op: the values can simply be reinterpreted as ints, and that works
> fine in either endianness. We might hope to be able to do the same thing
> for smaller-than-word-size fixed-point types, but unfortunately we
> can't: e.g. a "short fract" or "fract" can't just be reinterpreted as a
> "long fract" (the 32-bit fractional type), because the fixed-point is
> in a different place for the wider type (and performing conversions
> would itself require libcalls at present). It also doesn't work to
> promote small fixed-point types to SImode: as well as being
> conceptually messy, we hit ICEs very quickly attempting that.
> 
> So instead, I think that small fixed-point types should be handled the
> same way as small aggregates or small complex-valued types. This means
> effectively no change in little-endian mode, but in big-endian mode:
> 
> * for register arguments, fixed-point types are passed in the
>   most-significant end of the register.
> 
> * for stack arguments, fixed-point types are passed in lower memory
>   addresses.
> 
> * for return values, fixed-point types are again in the
>   most-significant end of the return register (r0).
> 
> This is quite easy to set up in the backend code, but unfortunately I
> hit a snag in libcall expansion in calls.c: padding of arguments and
> return values is not properly supported, as it is for regular function
> calls. I've implemented that, and will post it as a separate patch.
> 
> OK to apply? Tested alongside the rest of the patch series, in both big
> & little-endian mode.
> 
> Julian
> 
> ChangeLog
> 
>     gcc/
>     * configure.ac (fixed-point): Add ARM support.
>     * configure: Regenerate.
>     * config/arm/arm.c (arm_fixed_mode_set): New struct.
>     (arm_set_fixed_optab_libfunc): New.
>     (arm_set_fixed_conv_libfunc): New.
>     (arm_init_libfuncs): Initialise fixed-point helper libfuncs with
>     ARM-specific names.
>     (aapcs_libcall_value): Return sub-word-size fixed-point libcall
>     return values in SImode.
>     (arm_return_in_msb): Return fixed-point types in the msb.
>     (arm_pad_reg_upwards, arm_pad_arg_upwards): Pad fixed-point types
>     upwards.
>     (arm_scalar_mode_supported_p): Support fixed-point modes.
>     (arm_vector_mode_supported_p): Support vector fixed-point modes.
>     * config/arm/arm.h (SHORT_FRACT_TYPE_SIZE, FRACT_TYPE_SIZE)
>     (LONG_FRACT_TYPE_SIZE, LONG_LONG_FRACT_TYPE_SIZE)
>     (SHORT_ACCUM_TYPE_SIZE, ACCUM_TYPE_SIZE, LONG_ACCUM_TYPE_SIZE)
>     (LONG_LONG_ACCUM_TYPE_SIZE, MAX_FIXED_MODE_SIZE): Define.
>     * config/arm/arm-modes.def (FRACT, UFRACT, ACCUM, UACCUM): Declare
>     vector modes.
>     * config/arm/predicates.md (sat_shift_operator): New predicate.
>     * config/arm/arm-fixed.md: New.
>     * config/arm/arm.md: Include arm-fixed.md.
>     * config/arm/t-arm (MD_INCLUDES): Add arm-fixed.md.
>     * config/arm/fixed-bit-machine.h: New.
>  
>     libgcc/
>     * config.host (arm*-*-linux*, arm*-*-uclinux*, arm*-*-eabi*)
>     (arm*-*-symbianelf*): Add arm/t-fixed-point makefile fragment.
>     target-specific or generic fixed_bit_machine_header to decorate the
>     names of fixed-point helper functions.
>     * config/arm/t-fixed-point: New.
> 
>     gcc/testsuite/
>     * gcc.target/arm/fixed-point-exec.c: New test.
> 
> 

Please put the iterator definitions in iterators.md

Otherwise OK.

R.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] ARM fixed-point support [6/6]: target-specific parts
  2011-06-30 14:01       ` Richard Earnshaw
@ 2011-07-29 16:08         ` Julian Brown
  2011-07-29 17:12           ` Richard Earnshaw
  0 siblings, 1 reply; 7+ messages in thread
From: Julian Brown @ 2011-07-29 16:08 UTC (permalink / raw)
  To: Richard Earnshaw
  Cc: gcc-patches, Joseph S. Myers, paul, rearnsha, Ramana Radhakrishnan

[-- Attachment #1: Type: text/plain, Size: 2200 bytes --]

On Thu, 30 Jun 2011 14:42:54 +0100
Richard Earnshaw <rearnsha@arm.com> wrote:

> > OK to apply? Tested alongside the rest of the patch series, in both
> > big & little-endian mode.
> > [snip]
> 
> Please put the iterator definitions in iterators.md

I've done this, and made a few other changes as required by the new
version of [4/6] in this series:

http://gcc.gnu.org/ml/gcc-patches/2011-07/msg02680.html

OK to apply? (I'll assume so if the previous patch gets approved, and
tests look OK).

Julian

ChangeLog

    gcc/
    * configure.ac (fixed-point): Add ARM support.
    * configure: Regenerate.
    * config/arm/arm.c (arm_fixed_mode_set): New struct.
    (arm_set_fixed_optab_libfunc): New.
    (arm_set_fixed_conv_libfunc): New.
    (arm_init_libfuncs): Initialise fixed-point helper libfuncs with
    ARM-specific names.
    (aapcs_libcall_value): Return sub-word-size fixed-point libcall
    return values in SImode.
    (arm_return_in_msb): Return fixed-point types in the msb.
    (arm_pad_reg_upwards, arm_pad_arg_upwards): Pad fixed-point types
    upwards.
    (arm_scalar_mode_supported_p): Support fixed-point modes.
    (arm_vector_mode_supported_p): Support vector fixed-point modes.
    * config/arm/arm.h (SHORT_FRACT_TYPE_SIZE, FRACT_TYPE_SIZE)
    (LONG_FRACT_TYPE_SIZE, LONG_LONG_FRACT_TYPE_SIZE)
    (SHORT_ACCUM_TYPE_SIZE, ACCUM_TYPE_SIZE, LONG_ACCUM_TYPE_SIZE)
    (LONG_LONG_ACCUM_TYPE_SIZE, MAX_FIXED_MODE_SIZE): Define.
    * config/arm/iterators.md (FIXED, ADDSUB, UQADDSUB, QADDSUB, QMUL):
    New mode iterators.
    (qaddsub_suf): New mode attribute.
    * config/arm/arm-modes.def (FRACT, UFRACT, ACCUM, UACCUM): Declare
    vector modes.
    * config/arm/predicates.md (sat_shift_operator): New predicate.
    * config/arm/arm-fixed.md: New.
    * config/arm/arm.md: Include arm-fixed.md.
    * config/arm/t-arm (MD_INCLUDES): Add arm-fixed.md.
 
    libgcc/
    * config.host (arm*-*-linux*, arm*-*-uclinux*, arm*-*-eabi*)
    (arm*-*-symbianelf*): Add t-fixedpoint-gnu-prefix makefile fragment.
    * config/arm/bpabi-lib.h (LIBGCC2_GNU_PREFIX): Define, if
    BUILDING_FIXED_BIT is set.

    gcc/testsuite/
    * gcc.target/arm/fixed-point-exec.c: New test.

[-- Attachment #2: arm-fixed-point-support-fsf-6.diff --]
[-- Type: text/x-patch, Size: 40348 bytes --]

commit 51a5cdba96c5e583456b24bfb71aaad75c86ec8b
Author: Julian Brown <julian@henry8.codesourcery.com>
Date:   Thu May 26 09:12:05 2011 -0700

    Fixed-point extension support for ARM.

diff --git a/gcc/config/arm/arm-fixed.md b/gcc/config/arm/arm-fixed.md
new file mode 100644
index 0000000..bd33ce2
--- /dev/null
+++ b/gcc/config/arm/arm-fixed.md
@@ -0,0 +1,384 @@
+;; Copyright 2011 Free Software Foundation, Inc.
+;;
+;; This file is part of GCC.
+;;
+;; GCC is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published
+;; by the Free Software Foundation; either version 3, or (at your
+;; option) any later version.
+;;
+;; GCC is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+;; License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GCC; see the file COPYING3.  If not see
+;; <http://www.gnu.org/licenses/>.
+;;
+;; This file contains ARM instructions that support fixed-point operations.
+
+(define_insn "add<mode>3"
+  [(set (match_operand:FIXED 0 "s_register_operand" "=r")
+	(plus:FIXED (match_operand:FIXED 1 "s_register_operand" "r")
+		    (match_operand:FIXED 2 "s_register_operand" "r")))]
+  "TARGET_32BIT"
+  "add%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "add<mode>3"
+  [(set (match_operand:ADDSUB 0 "s_register_operand" "=r")
+	(plus:ADDSUB (match_operand:ADDSUB 1 "s_register_operand" "r")
+		     (match_operand:ADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "sadd<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "usadd<mode>3"
+  [(set (match_operand:UQADDSUB 0 "s_register_operand" "=r")
+	(us_plus:UQADDSUB (match_operand:UQADDSUB 1 "s_register_operand" "r")
+			  (match_operand:UQADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "uqadd<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "ssadd<mode>3"
+  [(set (match_operand:QADDSUB 0 "s_register_operand" "=r")
+	(ss_plus:QADDSUB (match_operand:QADDSUB 1 "s_register_operand" "r")
+			 (match_operand:QADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "qadd<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "sub<mode>3"
+  [(set (match_operand:FIXED 0 "s_register_operand" "=r")
+	(minus:FIXED (match_operand:FIXED 1 "s_register_operand" "r")
+		     (match_operand:FIXED 2 "s_register_operand" "r")))]
+  "TARGET_32BIT"
+  "sub%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "sub<mode>3"
+  [(set (match_operand:ADDSUB 0 "s_register_operand" "=r")
+	(minus:ADDSUB (match_operand:ADDSUB 1 "s_register_operand" "r")
+		      (match_operand:ADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "ssub<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "ussub<mode>3"
+  [(set (match_operand:UQADDSUB 0 "s_register_operand" "=r")
+	(us_minus:UQADDSUB
+	  (match_operand:UQADDSUB 1 "s_register_operand" "r")
+	  (match_operand:UQADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "uqsub<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+(define_insn "sssub<mode>3"
+  [(set (match_operand:QADDSUB 0 "s_register_operand" "=r")
+	(ss_minus:QADDSUB (match_operand:QADDSUB 1 "s_register_operand" "r")
+			  (match_operand:QADDSUB 2 "s_register_operand" "r")))]
+  "TARGET_INT_SIMD"
+  "qsub<qaddsub_suf>%?\\t%0, %1, %2"
+  [(set_attr "predicable" "yes")])
+
+;; Fractional multiplies.
+
+; Note: none of these do any rounding.
+
+(define_expand "mulqq3"
+  [(set (match_operand:QQ 0 "s_register_operand" "")
+	(mult:QQ (match_operand:QQ 1 "s_register_operand" "")
+		 (match_operand:QQ 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY && arm_arch_thumb2"
+{
+  rtx tmp1 = gen_reg_rtx (HImode);
+  rtx tmp2 = gen_reg_rtx (HImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_extendqihi2 (tmp1, gen_lowpart (QImode, operands[1])));
+  emit_insn (gen_extendqihi2 (tmp2, gen_lowpart (QImode, operands[2])));
+  emit_insn (gen_mulhisi3 (tmp3, tmp1, tmp2));
+  emit_insn (gen_extv (gen_lowpart (SImode, operands[0]), tmp3, GEN_INT (8),
+		       GEN_INT (7)));
+  DONE;
+})
+
+(define_expand "mulhq3"
+  [(set (match_operand:HQ 0 "s_register_operand" "")
+	(mult:HQ (match_operand:HQ 1 "s_register_operand" "")
+		 (match_operand:HQ 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY && arm_arch_thumb2"
+{
+  rtx tmp = gen_reg_rtx (SImode);
+
+  emit_insn (gen_mulhisi3 (tmp, gen_lowpart (HImode, operands[1]),
+			   gen_lowpart (HImode, operands[2])));
+  /* We're doing a s.15 * s.15 multiplication, getting an s.30 result.  Extract
+     an s.15 value from that.  This won't overflow/saturate for _Fract
+     values.  */
+  emit_insn (gen_extv (gen_lowpart (SImode, operands[0]), tmp,
+		       GEN_INT (16), GEN_INT (15)));
+  DONE;
+})
+
+(define_expand "mulsq3"
+  [(set (match_operand:SQ 0 "s_register_operand" "")
+	(mult:SQ (match_operand:SQ 1 "s_register_operand" "")
+		 (match_operand:SQ 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch3m"
+{
+  rtx tmp1 = gen_reg_rtx (DImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  /* s.31 * s.31 -> s.62 multiplication.  */
+  emit_insn (gen_mulsidi3 (tmp1, gen_lowpart (SImode, operands[1]),
+			   gen_lowpart (SImode, operands[2])));
+  emit_insn (gen_lshrsi3 (tmp2, gen_lowpart (SImode, tmp1), GEN_INT (31)));
+  emit_insn (gen_ashlsi3 (tmp3, gen_highpart (SImode, tmp1), GEN_INT (1)));
+  emit_insn (gen_iorsi3 (gen_lowpart (SImode, operands[0]), tmp2, tmp3));
+
+  DONE;
+})
+
+;; Accumulator multiplies.
+
+(define_expand "mulsa3"
+  [(set (match_operand:SA 0 "s_register_operand" "")
+	(mult:SA (match_operand:SA 1 "s_register_operand" "")
+		 (match_operand:SA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch3m"
+{
+  rtx tmp1 = gen_reg_rtx (DImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_mulsidi3 (tmp1, gen_lowpart (SImode, operands[1]),
+			   gen_lowpart (SImode, operands[2])));
+  emit_insn (gen_lshrsi3 (tmp2, gen_lowpart (SImode, tmp1), GEN_INT (15)));
+  emit_insn (gen_ashlsi3 (tmp3, gen_highpart (SImode, tmp1), GEN_INT (17)));
+  emit_insn (gen_iorsi3 (gen_lowpart (SImode, operands[0]), tmp2, tmp3));
+
+  DONE;
+})
+
+(define_expand "mulusa3"
+  [(set (match_operand:USA 0 "s_register_operand" "")
+	(mult:USA (match_operand:USA 1 "s_register_operand" "")
+		  (match_operand:USA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && arm_arch3m"
+{
+  rtx tmp1 = gen_reg_rtx (DImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_umulsidi3 (tmp1, gen_lowpart (SImode, operands[1]),
+			    gen_lowpart (SImode, operands[2])));
+  emit_insn (gen_lshrsi3 (tmp2, gen_lowpart (SImode, tmp1), GEN_INT (16)));
+  emit_insn (gen_ashlsi3 (tmp3, gen_highpart (SImode, tmp1), GEN_INT (16)));
+  emit_insn (gen_iorsi3 (gen_lowpart (SImode, operands[0]), tmp2, tmp3));
+  
+  DONE;
+})
+
+;; The code sequence emitted by this insn pattern uses the Q flag, which GCC
+;; doesn't generally know about, so we don't bother expanding to individual
+;; instructions.  It may be better to just use an out-of-line asm libcall for
+;; this.
+
+(define_insn "ssmulsa3"
+  [(set (match_operand:SA 0 "s_register_operand" "=r")
+	(ss_mult:SA (match_operand:SA 1 "s_register_operand" "r")
+		    (match_operand:SA 2 "s_register_operand" "r")))
+   (clobber (match_scratch:DI 3 "=r"))
+   (clobber (match_scratch:SI 4 "=r"))
+   (clobber (reg:CC CC_REGNUM))]
+  "TARGET_32BIT && arm_arch6"
+{
+  /* s16.15 * s16.15 -> s32.30.  */
+  output_asm_insn ("smull\\t%Q3, %R3, %1, %2", operands);
+
+  if (TARGET_ARM)
+    output_asm_insn ("msr\\tAPSR_nzcvq, #0", operands);
+  else
+    {
+      output_asm_insn ("mov\\t%4, #0", operands);
+      output_asm_insn ("msr\\tAPSR_nzcvq, %4", operands);
+    }
+
+  /* We have:
+      31  high word  0     31  low word  0 
+
+    [ S i i .... i i i ] [ i f f f ... f f ]
+                        |
+			v
+	     [ S i ... i f ... f f ]
+
+    Need 16 integral bits, so saturate at 15th bit of high word.  */
+
+  output_asm_insn ("ssat\\t%R3, #15, %R3", operands);
+  output_asm_insn ("mrs\\t%4, APSR", operands);
+  output_asm_insn ("tst\\t%4, #1<<27", operands);
+  if (TARGET_THUMB2)
+    output_asm_insn ("it\\tne", operands);
+  output_asm_insn ("mvnne\\t%Q3, %R3, asr #32", operands);
+  output_asm_insn ("mov\\t%0, %Q3, lsr #15", operands);
+  output_asm_insn ("orr\\t%0, %0, %R3, asl #17", operands);
+  return "";
+}
+  [(set_attr "conds" "clob")
+   (set (attr "length")
+	(if_then_else (eq_attr "is_thumb" "yes")
+		      (const_int 38)
+		      (const_int 32)))])
+
+;; Same goes for this.
+
+(define_insn "usmulusa3"
+  [(set (match_operand:USA 0 "s_register_operand" "=r")
+	(us_mult:USA (match_operand:USA 1 "s_register_operand" "r")
+		     (match_operand:USA 2 "s_register_operand" "r")))
+   (clobber (match_scratch:DI 3 "=r"))
+   (clobber (match_scratch:SI 4 "=r"))
+   (clobber (reg:CC CC_REGNUM))]
+  "TARGET_32BIT && arm_arch6"
+{
+  /* 16.16 * 16.16 -> 32.32.  */
+  output_asm_insn ("umull\\t%Q3, %R3, %1, %2", operands);
+
+  if (TARGET_ARM)
+    output_asm_insn ("msr\\tAPSR_nzcvq, #0", operands);
+  else
+    {
+      output_asm_insn ("mov\\t%4, #0", operands);
+      output_asm_insn ("msr\\tAPSR_nzcvq, %4", operands);
+    }
+
+  /* We have:
+      31  high word  0     31  low word  0 
+
+    [ i i i .... i i i ] [ f f f f ... f f ]
+                        |
+			v
+	     [ i i ... i f ... f f ]
+
+    Need 16 integral bits, so saturate at 16th bit of high word.  */
+
+  output_asm_insn ("usat\\t%R3, #16, %R3", operands);
+  output_asm_insn ("mrs\\t%4, APSR", operands);
+  output_asm_insn ("tst\\t%4, #1<<27", operands);
+  if (TARGET_THUMB2)
+    output_asm_insn ("it\\tne", operands);
+  output_asm_insn ("sbfxne\\t%Q3, %R3, #15, #1", operands);
+  output_asm_insn ("lsr\\t%0, %Q3, #16", operands);
+  output_asm_insn ("orr\\t%0, %0, %R3, asl #16", operands);
+  return "";
+}
+  [(set_attr "conds" "clob")
+   (set (attr "length")
+	(if_then_else (eq_attr "is_thumb" "yes")
+		      (const_int 38)
+		      (const_int 32)))])
+
+(define_expand "mulha3"
+  [(set (match_operand:HA 0 "s_register_operand" "")
+	(mult:HA (match_operand:HA 1 "s_register_operand" "")
+		 (match_operand:HA 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY && arm_arch_thumb2"
+{
+  rtx tmp = gen_reg_rtx (SImode);
+  
+  emit_insn (gen_mulhisi3 (tmp, gen_lowpart (HImode, operands[1]),
+			   gen_lowpart (HImode, operands[2])));
+  emit_insn (gen_extv (gen_lowpart (SImode, operands[0]), tmp, GEN_INT (16),
+		       GEN_INT (7)));
+
+  DONE;
+})
+
+(define_expand "muluha3"
+  [(set (match_operand:UHA 0 "s_register_operand" "")
+	(mult:UHA (match_operand:UHA 1 "s_register_operand" "")
+		  (match_operand:UHA 2 "s_register_operand" "")))]
+  "TARGET_DSP_MULTIPLY"
+{
+  rtx tmp1 = gen_reg_rtx (SImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  
+  /* 8.8 * 8.8 -> 16.16 multiply.  */
+  emit_insn (gen_zero_extendhisi2 (tmp1, gen_lowpart (HImode, operands[1])));
+  emit_insn (gen_zero_extendhisi2 (tmp2, gen_lowpart (HImode, operands[2])));
+  emit_insn (gen_mulsi3 (tmp3, tmp1, tmp2));
+  emit_insn (gen_extzv (gen_lowpart (SImode, operands[0]), tmp3,
+			GEN_INT (16), GEN_INT (8)));
+
+  DONE;
+})
+
+(define_expand "ssmulha3"
+  [(set (match_operand:HA 0 "s_register_operand" "")
+	(ss_mult:HA (match_operand:HA 1 "s_register_operand" "")
+		    (match_operand:HA 2 "s_register_operand" "")))]
+  "TARGET_32BIT && TARGET_DSP_MULTIPLY && arm_arch6"
+{
+  rtx tmp = gen_reg_rtx (SImode);
+  rtx rshift;
+  
+  emit_insn (gen_mulhisi3 (tmp, gen_lowpart (HImode, operands[1]),
+			   gen_lowpart (HImode, operands[2])));
+
+  rshift = gen_rtx_ASHIFTRT (SImode, tmp, GEN_INT (7));
+
+  emit_insn (gen_rtx_SET (VOIDmode, gen_lowpart (HImode, operands[0]),
+			  gen_rtx_SS_TRUNCATE (HImode, rshift)));
+
+  DONE;
+})
+
+(define_expand "usmuluha3"
+  [(set (match_operand:UHA 0 "s_register_operand" "")
+	(us_mult:UHA (match_operand:UHA 1 "s_register_operand" "")
+		     (match_operand:UHA 2 "s_register_operand" "")))]
+  "TARGET_INT_SIMD"
+{
+  rtx tmp1 = gen_reg_rtx (SImode);
+  rtx tmp2 = gen_reg_rtx (SImode);
+  rtx tmp3 = gen_reg_rtx (SImode);
+  rtx rshift_tmp = gen_reg_rtx (SImode);
+  
+  /* Note: there's no smul[bt][bt] equivalent for unsigned multiplies.  Use a
+     normal 32x32->32-bit multiply instead.  */
+  emit_insn (gen_zero_extendhisi2 (tmp1, gen_lowpart (HImode, operands[1])));
+  emit_insn (gen_zero_extendhisi2 (tmp2, gen_lowpart (HImode, operands[2])));
+  
+  emit_insn (gen_mulsi3 (tmp3, tmp1, tmp2));
+
+  /* The operand to "usat" is signed, so we cannot use the "..., asr #8"
+     form of that instruction since the multiplication result TMP3 may have the
+     top bit set, thus be negative and saturate to zero.  Use a separate
+     logical right-shift instead.  */
+  emit_insn (gen_lshrsi3 (rshift_tmp, tmp3, GEN_INT (8)));
+  emit_insn (gen_arm_usatsihi (gen_lowpart (HImode, operands[0]), rshift_tmp));
+
+  DONE;
+})
+
+(define_insn "arm_ssatsihi_shift"
+  [(set (match_operand:HI 0 "s_register_operand" "=r")
+	(ss_truncate:HI (match_operator:SI 1 "sat_shift_operator"
+			  [(match_operand:SI 2 "s_register_operand" "r")
+			   (match_operand:SI 3 "immediate_operand" "I")])))]
+  "TARGET_32BIT && arm_arch6"
+  "ssat%?\\t%0, #16, %2%S1"
+  [(set_attr "predicable" "yes")
+   (set_attr "type" "alu_shift")])
+
+(define_insn "arm_usatsihi"
+  [(set (match_operand:HI 0 "s_register_operand" "=r")
+	(us_truncate:HI (match_operand:SI 1 "s_register_operand")))]
+  "TARGET_INT_SIMD"
+  "usat%?\\t%0, #16, %1"
+  [(set_attr "predicable" "yes")])
diff --git a/gcc/config/arm/arm-modes.def b/gcc/config/arm/arm-modes.def
index 24e3d90..7f19ebe 100644
--- a/gcc/config/arm/arm-modes.def
+++ b/gcc/config/arm/arm-modes.def
@@ -70,6 +70,12 @@ VECTOR_MODES (INT, 16);       /* V16QI V8HI V4SI V2DI */
 VECTOR_MODES (FLOAT, 8);      /*            V4HF V2SF */
 VECTOR_MODES (FLOAT, 16);     /*       V8HF V4SF V2DF */
 
+/* Fraction and accumulator vector modes.  */
+VECTOR_MODES (FRACT, 4);      /* V4QQ  V2HQ */
+VECTOR_MODES (UFRACT, 4);     /* V4UQQ V2UHQ */
+VECTOR_MODES (ACCUM, 4);      /*       V2HA */
+VECTOR_MODES (UACCUM, 4);     /*       V2UHA */
+
 /* Opaque integer modes for 3, 4, 6 or 8 Neon double registers (2 is
    TImode).  */
 INT_MODE (EI, 24);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index e0b8c3d..28b34c2 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -1038,6 +1038,49 @@ bit_count (unsigned long value)
   return count;
 }
 
+typedef struct
+{
+  enum machine_mode mode;
+  const char *name;
+} arm_fixed_mode_set;
+
+/* A small helper for setting fixed-point library libfuncs.  */
+
+static void
+arm_set_fixed_optab_libfunc (optab optable, enum machine_mode mode,
+			     const char *funcname, const char *modename,
+			     int num_suffix)
+{
+  char buffer[50];
+  
+  if (num_suffix == 0)
+    sprintf (buffer, "__gnu_%s%s", funcname, modename);
+  else
+    sprintf (buffer, "__gnu_%s%s%d", funcname, modename, num_suffix);
+  
+  set_optab_libfunc (optable, mode, buffer);
+}
+
+static void
+arm_set_fixed_conv_libfunc (convert_optab optable, enum machine_mode to,
+			    enum machine_mode from, const char *funcname,
+			    const char *toname, const char *fromname)
+{
+  char buffer[50];
+  char *maybe_suffix_2 = "";
+  
+  /* Follow the logic for selecting a "2" suffix in fixed-bit.h.  */
+  if (ALL_FIXED_POINT_MODE_P (from) && ALL_FIXED_POINT_MODE_P (to)
+      && UNSIGNED_FIXED_POINT_MODE_P (from) == UNSIGNED_FIXED_POINT_MODE_P (to)
+      && ALL_FRACT_MODE_P (from) == ALL_FRACT_MODE_P (to))
+    maybe_suffix_2 = "2";
+  
+  sprintf (buffer, "__gnu_%s%s%s%s", funcname, fromname, toname,
+	   maybe_suffix_2);
+
+  set_conv_libfunc (optable, to, from, buffer);
+}
+
 /* Set up library functions unique to ARM.  */
 
 static void
@@ -1183,6 +1226,137 @@ arm_init_libfuncs (void)
       break;
     }
 
+  /* Use names prefixed with __gnu_ for fixed-point helper functions.  */
+  {
+    const arm_fixed_mode_set fixed_arith_modes[] =
+      {
+	{ QQmode, "qq" },
+	{ UQQmode, "uqq" },
+	{ HQmode, "hq" },
+	{ UHQmode, "uhq" },
+	{ SQmode, "sq" },
+	{ USQmode, "usq" },
+	{ DQmode, "dq" },
+	{ UDQmode, "udq" },
+	{ TQmode, "tq" },
+	{ UTQmode, "utq" },
+	{ HAmode, "ha" },
+	{ UHAmode, "uha" },
+	{ SAmode, "sa" },
+	{ USAmode, "usa" },
+	{ DAmode, "da" },
+	{ UDAmode, "uda" },
+	{ TAmode, "ta" },
+	{ UTAmode, "uta" }
+      };
+    const arm_fixed_mode_set fixed_conv_modes[] =
+      {
+	{ QQmode, "qq" },
+	{ UQQmode, "uqq" },
+	{ HQmode, "hq" },
+	{ UHQmode, "uhq" },
+	{ SQmode, "sq" },
+	{ USQmode, "usq" },
+	{ DQmode, "dq" },
+	{ UDQmode, "udq" },
+	{ TQmode, "tq" },
+	{ UTQmode, "utq" },
+	{ HAmode, "ha" },
+	{ UHAmode, "uha" },
+	{ SAmode, "sa" },
+	{ USAmode, "usa" },
+	{ DAmode, "da" },
+	{ UDAmode, "uda" },
+	{ TAmode, "ta" },
+	{ UTAmode, "uta" },
+	{ QImode, "qi" },
+	{ HImode, "hi" },
+	{ SImode, "si" },
+	{ DImode, "di" },
+	{ TImode, "ti" },
+	{ SFmode, "sf" },
+	{ DFmode, "df" }
+      };
+    unsigned int i, j;
+
+    for (i = 0; i < ARRAY_SIZE (fixed_arith_modes); i++)
+      {
+	arm_set_fixed_optab_libfunc (add_optab, fixed_arith_modes[i].mode,
+				     "add", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssadd_optab, fixed_arith_modes[i].mode,
+				     "ssadd", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usadd_optab, fixed_arith_modes[i].mode,
+				     "usadd", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (sub_optab, fixed_arith_modes[i].mode,
+				     "sub", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (sssub_optab, fixed_arith_modes[i].mode,
+				     "sssub", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ussub_optab, fixed_arith_modes[i].mode,
+				     "ussub", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (smul_optab, fixed_arith_modes[i].mode,
+				     "mul", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssmul_optab, fixed_arith_modes[i].mode,
+				     "ssmul", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usmul_optab, fixed_arith_modes[i].mode,
+				     "usmul", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (sdiv_optab, fixed_arith_modes[i].mode,
+				     "div", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (udiv_optab, fixed_arith_modes[i].mode,
+				     "udiv", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssdiv_optab, fixed_arith_modes[i].mode,
+				     "ssdiv", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usdiv_optab, fixed_arith_modes[i].mode,
+				     "usdiv", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (neg_optab, fixed_arith_modes[i].mode,
+				     "neg", fixed_arith_modes[i].name, 2);
+	arm_set_fixed_optab_libfunc (ssneg_optab, fixed_arith_modes[i].mode,
+				     "ssneg", fixed_arith_modes[i].name, 2);
+	arm_set_fixed_optab_libfunc (usneg_optab, fixed_arith_modes[i].mode,
+				     "usneg", fixed_arith_modes[i].name, 2);
+	arm_set_fixed_optab_libfunc (ashl_optab, fixed_arith_modes[i].mode,
+				     "ashl", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ashr_optab, fixed_arith_modes[i].mode,
+				     "ashr", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (lshr_optab, fixed_arith_modes[i].mode,
+				     "lshr", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (ssashl_optab, fixed_arith_modes[i].mode,
+				     "ssashl", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (usashl_optab, fixed_arith_modes[i].mode,
+				     "usashl", fixed_arith_modes[i].name, 3);
+	arm_set_fixed_optab_libfunc (cmp_optab, fixed_arith_modes[i].mode,
+				     "cmp", fixed_arith_modes[i].name, 2);
+      }
+
+    for (i = 0; i < ARRAY_SIZE (fixed_conv_modes); i++)
+      for (j = 0; j < ARRAY_SIZE (fixed_conv_modes); j++)
+	{
+	  if (i == j
+	      || (!ALL_FIXED_POINT_MODE_P (fixed_conv_modes[i].mode)
+		  && !ALL_FIXED_POINT_MODE_P (fixed_conv_modes[j].mode)))
+	    continue;
+
+	  arm_set_fixed_conv_libfunc (fract_optab, fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "fract",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	  arm_set_fixed_conv_libfunc (satfract_optab,
+				      fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "satfract",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	  arm_set_fixed_conv_libfunc (fractuns_optab,
+				      fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "fractuns",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	  arm_set_fixed_conv_libfunc (satfractuns_optab,
+				      fixed_conv_modes[i].mode,
+				      fixed_conv_modes[j].mode, "satfractuns",
+				      fixed_conv_modes[i].name,
+				      fixed_conv_modes[j].name);
+	}
+  }
+
   if (TARGET_AAPCS_BASED)
     synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
 }
@@ -4203,6 +4377,10 @@ aapcs_allocate_return_reg (enum machine_mode mode, const_tree type,
 rtx
 aapcs_libcall_value (enum machine_mode mode)
 {
+  if (BYTES_BIG_ENDIAN && ALL_FIXED_POINT_MODE_P (mode)
+      && GET_MODE_SIZE (mode) <= 4)
+    mode = SImode;
+
   return aapcs_allocate_return_reg (mode, NULL_TREE, NULL_TREE);
 }
 
@@ -9252,8 +9430,9 @@ arm_return_in_msb (const_tree valtype)
 {
   return (TARGET_AAPCS_BASED
           && BYTES_BIG_ENDIAN
-          && (AGGREGATE_TYPE_P (valtype)
-              || TREE_CODE (valtype) == COMPLEX_TYPE));
+	  && (AGGREGATE_TYPE_P (valtype)
+	      || TREE_CODE (valtype) == COMPLEX_TYPE
+	      || FIXED_POINT_TYPE_P (valtype)));
 }
 
 /* Returns TRUE if INSN is an "LDR REG, ADDR" instruction.
@@ -11287,7 +11466,8 @@ arm_pad_reg_upward (enum machine_mode mode ATTRIBUTE_UNUSED,
 {
   if (TARGET_AAPCS_BASED
       && BYTES_BIG_ENDIAN
-      && (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE)
+      && (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE
+	  || FIXED_POINT_TYPE_P (type))
       && int_size_in_bytes (type) <= 4)
     return true;
 
@@ -19433,6 +19613,8 @@ arm_scalar_mode_supported_p (enum machine_mode mode)
 {
   if (mode == HFmode)
     return (arm_fp16_format != ARM_FP16_FORMAT_NONE);
+  else if (ALL_FIXED_POINT_MODE_P (mode))
+    return true;
   else
     return default_scalar_mode_supported_p (mode);
 }
@@ -22552,6 +22734,11 @@ arm_vector_mode_supported_p (enum machine_mode mode)
 	  || (mode == V8QImode)))
     return true;
 
+  if (TARGET_INT_SIMD && (mode == V4UQQmode || mode == V4QQmode
+      || mode == V2UHQmode || mode == V2HQmode || mode == V2UHAmode
+      || mode == V2HAmode))
+    return true;
+
   return false;
 }
 
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index 3810f9e..869b9a9 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -607,6 +607,20 @@ extern int arm_arch_thumb_hwdiv;
 #define WCHAR_TYPE_SIZE BITS_PER_WORD
 #endif
 
+/* Sized for fixed-point types.  */
+
+#define SHORT_FRACT_TYPE_SIZE 8
+#define FRACT_TYPE_SIZE 16
+#define LONG_FRACT_TYPE_SIZE 32
+#define LONG_LONG_FRACT_TYPE_SIZE 64
+
+#define SHORT_ACCUM_TYPE_SIZE 16
+#define ACCUM_TYPE_SIZE 32
+#define LONG_ACCUM_TYPE_SIZE 64
+#define LONG_LONG_ACCUM_TYPE_SIZE 64
+
+#define MAX_FIXED_MODE_SIZE 64
+
 #ifndef SIZE_TYPE
 #define SIZE_TYPE (TARGET_AAPCS_BASED ? "unsigned int" : "long unsigned int")
 #endif
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 03ae72d..3d4dcfa 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -10889,3 +10889,5 @@
 (include "neon.md")
 ;; Synchronization Primitives
 (include "sync.md")
+;; Fixed-point patterns
+(include "arm-fixed.md")
diff --git a/gcc/config/arm/iterators.md b/gcc/config/arm/iterators.md
index b11b112..219918c 100644
--- a/gcc/config/arm/iterators.md
+++ b/gcc/config/arm/iterators.md
@@ -140,7 +140,18 @@
 
 ;; Modes with 8-bit, 16-bit and 32-bit elements.
 (define_mode_iterator VU [V16QI V8HI V4SI])
- 
+
+;; Iterators used for fixed-point support.
+(define_mode_iterator FIXED [QQ HQ SQ UQQ UHQ USQ HA SA UHA USA])
+
+(define_mode_iterator ADDSUB [V4QQ V2HQ V2HA])
+
+(define_mode_iterator UQADDSUB [V4UQQ V2UHQ UQQ UHQ V2UHA UHA])
+
+(define_mode_iterator QADDSUB [V4QQ V2HQ QQ HQ V2HA HA SQ SA])
+
+(define_mode_iterator QMUL [HQ HA])
+
 ;;----------------------------------------------------------------------------
 ;; Code iterators
 ;;----------------------------------------------------------------------------
@@ -384,6 +395,12 @@
 				   (QI "nonimmediate_operand")])
 (define_mode_attr qhs_extenddi_cstr [(SI "r") (HI "rm") (QI "rm")])
 
+;; Mode attributes used for fixed-point support.
+(define_mode_attr qaddsub_suf [(V4UQQ "8") (V2UHQ "16") (UQQ "8") (UHQ "16")
+			       (V2UHA "16") (UHA "16")
+			       (V4QQ "8") (V2HQ "16") (QQ "8") (HQ "16")
+			       (V2HA "16") (HA "16") (SQ "") (SA "")])
+
 ;;----------------------------------------------------------------------------
 ;; Code attributes
 ;;----------------------------------------------------------------------------
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index 678a31c..cfe8d33 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -227,6 +227,13 @@
 	    (match_code "ashift,ashiftrt,lshiftrt,rotatert"))
        (match_test "mode == GET_MODE (op)")))
 
+;; True for shift operators which can be used with saturation instructions.
+(define_special_predicate "sat_shift_operator"
+  (and (match_code "ashift,ashiftrt")
+       (match_test "GET_CODE (XEXP (op, 1)) == CONST_INT
+		    && ((unsigned HOST_WIDE_INT) INTVAL (XEXP (op, 1)) <= 32)")
+       (match_test "mode == GET_MODE (op)")))
+
 ;; True for MULT, to identify which variant of shift_operator is in use.
 (define_special_predicate "mult_operator"
   (match_code "mult"))
diff --git a/gcc/config/arm/t-arm b/gcc/config/arm/t-arm
index 826ec0a..b970ec2 100644
--- a/gcc/config/arm/t-arm
+++ b/gcc/config/arm/t-arm
@@ -37,7 +37,8 @@ MD_INCLUDES= 	$(srcdir)/config/arm/arm-tune.md \
 		$(srcdir)/config/arm/iwmmxt.md \
 		$(srcdir)/config/arm/vfp.md \
 		$(srcdir)/config/arm/neon.md \
-		$(srcdir)/config/arm/thumb2.md
+		$(srcdir)/config/arm/thumb2.md \
+		$(srcdir)/config/arm/arm-fixed.md
 
 LIB1ASMSRC = arm/lib1funcs.asm
 LIB1ASMFUNCS = _thumb1_case_sqi _thumb1_case_uqi _thumb1_case_shi \
diff --git a/gcc/configure b/gcc/configure
index 1a4c1b7..63e44c5 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -6965,6 +6965,10 @@ if test "${enable_fixed_point+set}" = set; then :
 else
 
   case $target in
+    arm*)
+      enable_fixed_point=yes
+      ;;
+
     mips*-*-*)
       case $host in
 	mips*-sgi-irix*)
@@ -17801,7 +17805,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 17804 "configure"
+#line 17808 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -17907,7 +17911,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 17910 "configure"
+#line 17914 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 9d7bb66..c71281c 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -638,6 +638,10 @@ AC_ARG_ENABLE(fixed-point,
 [],
 [
   case $target in
+    arm*)
+      enable_fixed_point=yes
+      ;;
+
     mips*-*-*)
       case $host in
 	mips*-sgi-irix*)
diff --git a/gcc/testsuite/gcc.target/arm/fixed-point-exec.c b/gcc/testsuite/gcc.target/arm/fixed-point-exec.c
new file mode 100644
index 0000000..6bc3b07
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/fixed-point-exec.c
@@ -0,0 +1,301 @@
+/* { dg-do run { target { fixed_point } } } */
+/* { dg-options "-std=gnu99" } */
+
+/* Check basic arithmetic ops for ARM fixed-point/saturating operation support.
+   Not target-independent since we make various assumptions about precision and
+   magnitudes of various types.  */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdfix.h>
+
+#define TEST(TYPE, OP, NAME, SUFFIX)				\
+  TYPE NAME##SUFFIX (TYPE A, TYPE B)				\
+    {								\
+      return A OP B;						\
+    }
+
+#define VARIANTS(TYPE, OP, NAME)				\
+  TEST (short TYPE, OP, NAME, _short);				\
+  TEST (TYPE, OP, NAME, _regular);				\
+  TEST (long TYPE, OP, NAME, _long);				\
+  TEST (_Sat short TYPE, OP, NAME, _sat_short);			\
+  TEST (_Sat TYPE, OP, NAME, _sat_regular);			\
+  TEST (_Sat long TYPE, OP, NAME, _sat_long);			\
+  TEST (unsigned short TYPE, OP, NAME, _uns_short);		\
+  TEST (unsigned TYPE, OP, NAME, _uns_regular);			\
+  TEST (unsigned long TYPE, OP, NAME, _uns_long);		\
+  TEST (unsigned _Sat short TYPE, OP, NAME, _uns_sat_short);	\
+  TEST (unsigned _Sat TYPE, OP, NAME, _uns_sat_regular);	\
+  TEST (unsigned _Sat long TYPE, OP, NAME, _uns_sat_long)
+
+VARIANTS (_Fract, +, plus_fract);
+VARIANTS (_Accum, +, plus_accum);
+VARIANTS (_Fract, -, minus_fract);
+VARIANTS (_Accum, -, minus_accum);
+VARIANTS (_Fract, *, mult_fract);
+VARIANTS (_Accum, *, mult_accum);
+VARIANTS (_Accum, /, div_accum);
+
+/* Inputs for signed add, multiply fractional tests.  */
+short _Fract sf_a = 0.9hr;
+short _Fract sf_b = -0.8hr;
+_Fract f_a = 0.9r;
+_Fract f_b = -0.8r;
+long _Fract lf_a = 0.9lr;
+long _Fract lf_b = -0.8lr;
+
+/* Inputs for signed subtract fractional tests.  */
+short _Fract sf_c = 0.7hr;
+short _Fract sf_d = 0.9hr;
+_Fract f_c = 0.7r;
+_Fract f_d = 0.9r;
+long _Fract lf_c = 0.7lr;
+long _Fract lf_d = 0.9lr;
+
+/* Inputs for unsigned add, subtract, multiply fractional tests.  */
+unsigned short _Fract usf_a = 0.4uhr;
+unsigned short _Fract usf_b = 0.3uhr;
+unsigned _Fract uf_a = 0.4ur;
+unsigned _Fract uf_b = 0.3ur;
+unsigned long _Fract ulf_a = 0.4ulr;
+unsigned long _Fract ulf_b = 0.3ulr;
+
+/* Inputs for saturating signed add tests.  */
+short _Sat _Fract sf_e = 0.8hr;
+short _Sat _Fract sf_f = 0.8hr;
+_Sat _Fract f_e = 0.8r;
+_Sat _Fract f_f = 0.8r;
+long _Sat _Fract lf_e = 0.8r;
+long _Sat _Fract lf_f = 0.8r;
+
+short _Sat _Fract sf_g = -0.8hr;
+short _Sat _Fract sf_h = -0.8hr;
+_Sat _Fract f_g = -0.8r;
+_Sat _Fract f_h = -0.8r;
+long _Sat _Fract lf_g = -0.8r;
+long _Sat _Fract lf_h = -0.8r;
+
+/* Inputs for saturating unsigned subtract tests.  */
+unsigned short _Sat _Fract usf_c = 0.3uhr;
+unsigned short _Sat _Fract usf_d = 0.4uhr;
+unsigned _Sat _Fract uf_c = 0.3ur;
+unsigned _Sat _Fract uf_d = 0.4ur;
+unsigned long _Sat _Fract ulf_c = 0.3ulr;
+unsigned long _Sat _Fract ulf_d = 0.4ulr;
+
+/* Inputs for signed accumulator tests.  */
+
+short _Accum sa_a = 1.25hk;
+short _Accum sa_b = -1.5hk;
+_Accum a_a = 100.25k;
+_Accum a_b = -100.5k;
+long _Accum la_a = 1000.25lk;
+long _Accum la_b = -1000.5lk;
+
+/* Inputs for unsigned accumulator tests.  */
+
+unsigned short _Accum usa_a = 2.5uhk;
+unsigned short _Accum usa_b = 1.75uhk;
+unsigned _Accum ua_a = 255.5uk;
+unsigned _Accum ua_b = 170.25uk;
+unsigned long _Accum ula_a = 1550.5ulk;
+unsigned long _Accum ula_b = 999.5ulk;
+
+/* Inputs for signed saturating accumulator tests.  */
+
+short _Sat _Accum sa_c = 240.0hk;
+short _Sat _Accum sa_d = 250.0hk;
+short _Sat _Accum sa_e = -240.0hk;
+short _Sat _Accum sa_f = -250.0hk;
+short _Sat _Accum sa_g = 0.5hk;
+
+_Sat _Accum a_c = 65000.0k;
+_Sat _Accum a_d = 20000.0k;
+_Sat _Accum a_e = -65000.0k;
+_Sat _Accum a_f = -20000.0k;
+_Sat _Accum a_g = 0.5k;
+
+long _Sat _Accum la_c = 3472883712.0lk;
+long _Sat _Accum la_d = 3456106496.0lk;
+long _Sat _Accum la_e = -3472883712.0lk;
+long _Sat _Accum la_f = -3456106496.0lk;
+long _Sat _Accum la_g = 0.5lk;
+
+/* Inputs for unsigned saturating accumulator tests.  */
+
+unsigned short _Sat _Accum usa_c = 250.0uhk;
+unsigned short _Sat _Accum usa_d = 240.0uhk;
+unsigned short _Sat _Accum usa_e = 0.5uhk;
+
+unsigned _Sat _Accum ua_c = 65000.0uk;
+unsigned _Sat _Accum ua_d = 20000.0uk;
+unsigned _Sat _Accum ua_e = 0.5uk;
+
+unsigned long _Sat _Accum ula_c = 3472883712.0ulk;
+unsigned long _Sat _Accum ula_d = 3456106496.0ulk;
+unsigned long _Sat _Accum ula_e = 0.5ulk;
+
+#define CHECK(FN, EXP) do {						     \
+  if (fabs ((float) (FN) - (EXP)) > 0.05)				     \
+    {									     \
+      fprintf (stderr, "result for " #FN " (as float): %f\n", (double) (FN));\
+      abort ();								     \
+    }									     \
+  } while (0)
+
+#define CHECK_EXACT(FN, EXP) do {					     \
+  if ((FN) != (EXP))							     \
+    {									     \
+      fprintf (stderr, "result for " #FN " (as float): %f, should be %f\n",  \
+	       (double) (FN), (double) (EXP));				     \
+      abort ();								     \
+    }									     \
+  } while (0)
+
+int
+main (int argc, char *argv[])
+{
+  /* Fract/fract operations, non-saturating.  */
+
+  CHECK (plus_fract_short (sf_a, sf_b), 0.1);
+  CHECK (plus_fract_regular (f_a, f_b), 0.1);
+  CHECK (plus_fract_long (lf_a, lf_b), 0.1);
+
+  CHECK (plus_fract_uns_short (usf_a, usf_b), 0.7);
+  CHECK (plus_fract_uns_regular (uf_a, uf_b), 0.7);
+  CHECK (plus_fract_uns_long (ulf_a, ulf_b), 0.7);
+
+  CHECK (minus_fract_short (sf_c, sf_d), -0.2);
+  CHECK (minus_fract_regular (f_c, f_d), -0.2);
+  CHECK (minus_fract_long (lf_c, lf_d), -0.2);
+
+  CHECK (minus_fract_uns_short (usf_a, usf_b), 0.1);
+  CHECK (minus_fract_uns_regular (uf_a, uf_b), 0.1);
+  CHECK (minus_fract_uns_long (ulf_a, ulf_b), 0.1);
+
+  CHECK (mult_fract_short (sf_a, sf_b), -0.72);
+  CHECK (mult_fract_regular (f_a, f_b), -0.72);
+  CHECK (mult_fract_long (lf_a, lf_b), -0.72);
+
+  CHECK (mult_fract_uns_short (usf_a, usf_b), 0.12);
+  CHECK (mult_fract_uns_regular (uf_a, uf_b), 0.12);
+  CHECK (mult_fract_uns_long (ulf_a, ulf_b), 0.12);
+
+  /* Fract/fract operations, saturating.  */
+
+  CHECK (plus_fract_sat_short (sf_e, sf_f), 1.0);
+  CHECK (plus_fract_sat_regular (f_e, f_f), 1.0);
+  CHECK (plus_fract_sat_long (lf_e, lf_f), 1.0);
+
+  CHECK (plus_fract_sat_short (sf_g, sf_h), -1.0);
+  CHECK (plus_fract_sat_regular (f_g, f_h), -1.0);
+  CHECK (plus_fract_sat_long (lf_g, lf_h), -1.0);
+  
+  CHECK (plus_fract_uns_sat_short (sf_e, sf_f), 1.0);
+  CHECK (plus_fract_uns_sat_regular (f_e, f_f), 1.0);
+  CHECK (plus_fract_uns_sat_long (lf_e, lf_f), 1.0);
+
+  CHECK (plus_fract_sat_short (sf_a, sf_b), 0.1);
+  CHECK (plus_fract_sat_regular (f_a, f_b), 0.1);
+  CHECK (plus_fract_sat_long (lf_a, lf_b), 0.1);
+  
+  CHECK (plus_fract_uns_sat_short (usf_a, usf_b), 0.7);
+  CHECK (plus_fract_uns_sat_regular (uf_a, uf_b), 0.7);
+  CHECK (plus_fract_uns_sat_long (ulf_a, ulf_b), 0.7);
+
+  CHECK (minus_fract_uns_sat_short (usf_c, usf_d), 0.0);
+  CHECK (minus_fract_uns_sat_regular (uf_c, uf_d), 0.0);
+  CHECK (minus_fract_uns_sat_short (ulf_c, ulf_d), 0.0);
+  
+  CHECK (minus_fract_sat_short (sf_c, sf_d), -0.2);
+  CHECK (minus_fract_sat_regular (f_c, f_d), -0.2);
+  CHECK (minus_fract_sat_long (lf_c, lf_d), -0.2);
+
+  /* Accum/accum operations, non-saturating.  */
+
+  CHECK (plus_accum_short (sa_a, sa_b), -0.25);
+  CHECK (plus_accum_regular (a_a, a_b), -0.25);
+  CHECK (plus_accum_long (la_a, la_b), -0.25);
+
+  CHECK (minus_accum_short (sa_a, sa_b), 2.75);
+  CHECK (minus_accum_regular (a_a, a_b), 200.75);
+  CHECK (minus_accum_long (la_a, la_b), 2000.75);
+  
+  CHECK (mult_accum_short (sa_a, sa_b), -1.875);
+  CHECK (mult_accum_regular (a_a, a_b), -10075.125);
+  CHECK (mult_accum_long (la_a, la_b), -1000750.125);
+
+  CHECK (div_accum_short (sa_a, sa_b), -1.25/1.5);
+  CHECK (div_accum_regular (a_a, a_b), -100.25/100.5);
+  CHECK (div_accum_long (la_a, la_b), -1000.25/1000.5);
+
+  /* Unsigned accum/accum operations, non-saturating.  */
+  
+  CHECK (plus_accum_uns_short (usa_a, usa_b), 4.25);
+  CHECK (plus_accum_uns_regular (ua_a, ua_b), 425.75);
+  CHECK (plus_accum_uns_long (ula_a, ula_b), 2550.0);
+
+  CHECK (minus_accum_uns_short (usa_a, usa_b), 0.75);
+  CHECK (minus_accum_uns_regular (ua_a, ua_b), 85.25);
+  CHECK (minus_accum_uns_long (ula_a, ula_b), 551.0);
+  
+  CHECK (mult_accum_uns_short (usa_a, usa_b), 4.375);
+  CHECK (mult_accum_uns_regular (ua_a, ua_b), 43498.875);
+  CHECK (mult_accum_uns_long (ula_a, ula_b), 1549724.75);
+
+  CHECK (div_accum_uns_short (usa_a, usa_b), 2.5/1.75);
+  CHECK (div_accum_uns_regular (ua_a, ua_b), 255.5/170.25);
+  CHECK (div_accum_uns_long (ula_a, ula_b), 1550.5/999.5);
+
+  /* Signed accum/accum operations, saturating.  */
+  
+  CHECK_EXACT (plus_accum_sat_short (sa_c, sa_d), SACCUM_MAX);
+  CHECK_EXACT (plus_accum_sat_short (sa_e, sa_f), SACCUM_MIN);
+  CHECK_EXACT (plus_accum_sat_regular (a_c, a_d), ACCUM_MAX);
+  CHECK_EXACT (plus_accum_sat_regular (a_e, a_f), ACCUM_MIN);
+  CHECK_EXACT (plus_accum_sat_long (la_c, la_d), LACCUM_MAX);
+  CHECK_EXACT (plus_accum_sat_long (la_e, la_f), LACCUM_MIN);
+
+  CHECK_EXACT (minus_accum_sat_short (sa_e, sa_d), SACCUM_MIN);
+  CHECK_EXACT (minus_accum_sat_short (sa_c, sa_f), SACCUM_MAX);
+  CHECK_EXACT (minus_accum_sat_regular (a_e, a_d), ACCUM_MIN);
+  CHECK_EXACT (minus_accum_sat_regular (a_c, a_f), ACCUM_MAX);
+  CHECK_EXACT (minus_accum_sat_long (la_e, la_d), LACCUM_MIN);
+  CHECK_EXACT (minus_accum_sat_long (la_c, la_f), LACCUM_MAX);
+
+  CHECK_EXACT (mult_accum_sat_short (sa_c, sa_d), SACCUM_MAX);
+  CHECK_EXACT (mult_accum_sat_short (sa_c, sa_e), SACCUM_MIN);
+  CHECK_EXACT (mult_accum_sat_regular (a_c, a_d), ACCUM_MAX);
+  CHECK_EXACT (mult_accum_sat_regular (a_c, a_e), ACCUM_MIN);
+  CHECK_EXACT (mult_accum_sat_long (la_c, la_d), LACCUM_MAX);
+  CHECK_EXACT (mult_accum_sat_long (la_c, la_e), LACCUM_MIN);
+  
+  CHECK_EXACT (div_accum_sat_short (sa_d, sa_g), SACCUM_MAX);
+  CHECK_EXACT (div_accum_sat_short (sa_e, sa_g), SACCUM_MIN);
+  CHECK_EXACT (div_accum_sat_regular (a_c, a_g), ACCUM_MAX);
+  CHECK_EXACT (div_accum_sat_regular (a_e, a_g), ACCUM_MIN);
+  CHECK_EXACT (div_accum_sat_long (la_d, la_g), LACCUM_MAX);
+  CHECK_EXACT (div_accum_sat_long (la_e, la_g), LACCUM_MIN);
+
+  /* Unsigned accum/accum operations, saturating.  */
+
+  CHECK_EXACT (plus_accum_uns_sat_short (usa_c, usa_d), USACCUM_MAX);
+  CHECK_EXACT (plus_accum_uns_sat_regular (ua_c, ua_d), UACCUM_MAX);
+  CHECK_EXACT (plus_accum_uns_sat_long (ula_c, ula_d), ULACCUM_MAX);
+  
+  CHECK_EXACT (minus_accum_uns_sat_short (usa_d, usa_c), 0uhk);
+  CHECK_EXACT (minus_accum_uns_sat_regular (ua_d, ua_c), 0uk);
+  CHECK_EXACT (minus_accum_uns_sat_long (ula_d, ula_c), 0ulk);
+
+  CHECK_EXACT (mult_accum_uns_sat_short (usa_c, usa_d), USACCUM_MAX);
+  CHECK_EXACT (mult_accum_uns_sat_regular (ua_c, ua_d), UACCUM_MAX);
+  CHECK_EXACT (mult_accum_uns_sat_long (ula_c, ula_d), ULACCUM_MAX);
+
+  CHECK_EXACT (div_accum_uns_sat_short (usa_c, usa_e), USACCUM_MAX);
+  CHECK_EXACT (div_accum_uns_sat_regular (ua_c, ua_e), UACCUM_MAX);
+  CHECK_EXACT (div_accum_uns_sat_long (ula_c, ula_e), ULACCUM_MAX);
+
+  return 0;
+}
diff --git a/libgcc/config.host b/libgcc/config.host
index 11760d0..f7c1b3b 100644
--- a/libgcc/config.host
+++ b/libgcc/config.host
@@ -264,12 +264,15 @@ arm*-*-freebsd*)
 arm*-*-netbsdelf*)
 	;;
 arm*-*-linux*)			# ARM GNU/Linux with ELF
+	tmake_file="${tmake_file} t-fixedpoint-gnu-prefix"
 	;;
 arm*-*-uclinux*)		# ARM ucLinux
+	tmake_file="${tmake_file} t-fixedpoint-gnu-prefix"
 	;;
 arm*-*-ecos-elf)
 	;;
 arm*-*-eabi* | arm*-*-symbianelf* )
+	tmake_file="${tmake_file} t-fixedpoint-gnu-prefix"
 	;;
 arm*-*-rtems*)
 	;;
diff --git a/libgcc/config/arm/bpabi-lib.h b/libgcc/config/arm/bpabi-lib.h
index 49a28c3..3a6cb91 100644
--- a/libgcc/config/arm/bpabi-lib.h
+++ b/libgcc/config/arm/bpabi-lib.h
@@ -78,3 +78,10 @@
 #ifdef L_floatundisf
 #define DECLARE_LIBRARY_RENAMES RENAME_LIBRARY (floatundisf, ul2f)
 #endif
+
+/* For ARM bpabi, we only want to use a "__gnu_" prefix for the fixed-point
+   helper functions - not everything in libgcc - in the interests of
+   maintaining backward compatibility.  */
+#ifdef BUILDING_FIXED_BIT
+#define LIBGCC2_GNU_PREFIX
+#endif

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] ARM fixed-point support [6/6]: target-specific parts
  2011-07-29 16:08         ` Julian Brown
@ 2011-07-29 17:12           ` Richard Earnshaw
  0 siblings, 0 replies; 7+ messages in thread
From: Richard Earnshaw @ 2011-07-29 17:12 UTC (permalink / raw)
  To: Julian Brown
  Cc: gcc-patches, Joseph S. Myers, paul, rearnsha, Ramana Radhakrishnan

On 29/07/11 16:47, Julian Brown wrote:
> On Thu, 30 Jun 2011 14:42:54 +0100
> Richard Earnshaw <rearnsha@arm.com> wrote:
> 
>>> OK to apply? Tested alongside the rest of the patch series, in both
>>> big & little-endian mode.
>>> [snip]
>>
>> Please put the iterator definitions in iterators.md
> 
> I've done this, and made a few other changes as required by the new
> version of [4/6] in this series:
> 
> http://gcc.gnu.org/ml/gcc-patches/2011-07/msg02680.html
> 
> OK to apply? (I'll assume so if the previous patch gets approved, and
> tests look OK).
> 
> Julian
> 
> ChangeLog
> 
>     gcc/
>     * configure.ac (fixed-point): Add ARM support.
>     * configure: Regenerate.
>     * config/arm/arm.c (arm_fixed_mode_set): New struct.
>     (arm_set_fixed_optab_libfunc): New.
>     (arm_set_fixed_conv_libfunc): New.
>     (arm_init_libfuncs): Initialise fixed-point helper libfuncs with
>     ARM-specific names.
>     (aapcs_libcall_value): Return sub-word-size fixed-point libcall
>     return values in SImode.
>     (arm_return_in_msb): Return fixed-point types in the msb.
>     (arm_pad_reg_upwards, arm_pad_arg_upwards): Pad fixed-point types
>     upwards.
>     (arm_scalar_mode_supported_p): Support fixed-point modes.
>     (arm_vector_mode_supported_p): Support vector fixed-point modes.
>     * config/arm/arm.h (SHORT_FRACT_TYPE_SIZE, FRACT_TYPE_SIZE)
>     (LONG_FRACT_TYPE_SIZE, LONG_LONG_FRACT_TYPE_SIZE)
>     (SHORT_ACCUM_TYPE_SIZE, ACCUM_TYPE_SIZE, LONG_ACCUM_TYPE_SIZE)
>     (LONG_LONG_ACCUM_TYPE_SIZE, MAX_FIXED_MODE_SIZE): Define.
>     * config/arm/iterators.md (FIXED, ADDSUB, UQADDSUB, QADDSUB, QMUL):
>     New mode iterators.
>     (qaddsub_suf): New mode attribute.
>     * config/arm/arm-modes.def (FRACT, UFRACT, ACCUM, UACCUM): Declare
>     vector modes.
>     * config/arm/predicates.md (sat_shift_operator): New predicate.
>     * config/arm/arm-fixed.md: New.
>     * config/arm/arm.md: Include arm-fixed.md.
>     * config/arm/t-arm (MD_INCLUDES): Add arm-fixed.md.
>  
>     libgcc/
>     * config.host (arm*-*-linux*, arm*-*-uclinux*, arm*-*-eabi*)
>     (arm*-*-symbianelf*): Add t-fixedpoint-gnu-prefix makefile fragment.
>     * config/arm/bpabi-lib.h (LIBGCC2_GNU_PREFIX): Define, if
>     BUILDING_FIXED_BIT is set.
> 
>     gcc/testsuite/
>     * gcc.target/arm/fixed-point-exec.c: New test.
> 
> 

OK

R.

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2011-07-29 16:08 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-13 14:54 [PATCH] ARM fixed-point support [6/6]: target-specific parts Julian Brown
2011-05-13 16:00 ` Joseph S. Myers
2011-05-13 18:45   ` Julian Brown
2011-05-26 17:39     ` Julian Brown
2011-06-30 14:01       ` Richard Earnshaw
2011-07-29 16:08         ` Julian Brown
2011-07-29 17:12           ` Richard Earnshaw

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).