aboutsummaryrefslogtreecommitdiff
path: root/gcc/config
diff options
context:
space:
mode:
authorJulian Brown <julian@codesourcery.com>2011-08-01 12:41:30 +0000
committerJulian Brown <jules@gcc.gnu.org>2011-08-01 12:41:30 +0000
commit655b30bfde9172bd40189a787b85b16e461ef1ef (patch)
tree42de6b523b06d8fd9304883974cdc19b9dcf95b2 /gcc/config
parent3d6c3bd710cae34a0a4e7e69061f42fe19c442cb (diff)
downloadgcc-655b30bfde9172bd40189a787b85b16e461ef1ef.zip
gcc-655b30bfde9172bd40189a787b85b16e461ef1ef.tar.gz
gcc-655b30bfde9172bd40189a787b85b16e461ef1ef.tar.bz2
configure.ac (fixed-point): Add ARM support.
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_FIXEDBIT_GNU_PREFIX): Define. gcc/testsuite/ * gcc.target/arm/fixed-point-exec.c: New test. From-SVN: r177025
Diffstat (limited to 'gcc/config')
-rw-r--r--gcc/config/arm/arm-fixed.md384
-rw-r--r--gcc/config/arm/arm-modes.def6
-rw-r--r--gcc/config/arm/arm.c193
-rw-r--r--gcc/config/arm/arm.h14
-rw-r--r--gcc/config/arm/arm.md2
-rw-r--r--gcc/config/arm/iterators.md19
-rw-r--r--gcc/config/arm/predicates.md7
-rw-r--r--gcc/config/arm/t-arm3
8 files changed, 623 insertions, 5 deletions
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 \