aboutsummaryrefslogtreecommitdiffstats
path: root/toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99457.patch
diff options
context:
space:
mode:
authorKoen Kooi <koen@dominion.thruhere.net>2012-03-23 08:22:26 +0100
committerKoen Kooi <koen@dominion.thruhere.net>2012-03-24 07:35:22 +0100
commitff0f815593c33f1a82ba4d1cbe41e6b987da1f47 (patch)
tree22b43fa2e84f25cc948df79f9e9de07e8ec57418 /toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99457.patch
parent6b22bd198a87b5f113971d8fcd0e7211cd143c7d (diff)
downloadmeta-openembedded-contrib-ff0f815593c33f1a82ba4d1cbe41e6b987da1f47.tar.gz
toolchain-layer: move binutils and gcc from meta-oe into here
Acked-by: Martin Jansa <Martin.Jansa@gmail.com> Acked-by: Eric BĂ©nard <eric@eukrea.com> Acked-by: Khem Raj <raj.khem@gmail.com> Signed-off-by: Koen Kooi <koen@dominion.thruhere.net>
Diffstat (limited to 'toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99457.patch')
-rw-r--r--toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99457.patch4236
1 files changed, 4236 insertions, 0 deletions
diff --git a/toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99457.patch b/toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99457.patch
new file mode 100644
index 00000000000..47b897d5e77
--- /dev/null
+++ b/toolchain-layer/recipes-devtools/gcc/gcc-4.5/linaro/gcc-4.5-linaro-r99457.patch
@@ -0,0 +1,4236 @@
+2010-12-03 Yao Qi <yao@codesourcery.com>
+
+ * config/arm/arm-ldmstm.ml: Rewrite ldm/stm RTL patterns to fix
+ regressions.
+ * config/arm/ldmstm.md: Regenreate.
+
+2010-12-03 Yao Qi <yao@codesourcery.com>
+
+ Backport from FSF mainline:
+
+ 2010-08-02 Bernd Schmidt <bernds@codesourcery.com>
+
+ PR target/40457
+ * config/arm/arm.h (arm_regs_in_sequence): Declare.
+ * config/arm/arm-protos.h (emit_ldm_seq, emit_stm_seq,
+ load_multiple_sequence, store_multiple_sequence): Delete
+ declarations.
+ (arm_gen_load_multiple, arm_gen_store_multiple): Adjust
+ declarations.
+ * config/arm/ldmstm.md: New file.
+ * config/arm/arm.c (arm_regs_in_sequence): New array.
+ (load_multiple_sequence): Now static. New args SAVED_ORDER,
+ CHECK_REGS. All callers changed.
+ If SAVED_ORDER is nonnull, copy the computed order into it.
+ If CHECK_REGS is false, don't sort REGS. Handle Thumb mode.
+ (store_multiple_sequence): Now static. New args NOPS_TOTAL,
+ SAVED_ORDER, REG_RTXS and CHECK_REGS. All callers changed.
+ If SAVED_ORDER is nonnull, copy the computed order into it.
+ If CHECK_REGS is false, don't sort REGS. Set up REG_RTXS just
+ like REGS. Handle Thumb mode.
+ (arm_gen_load_multiple_1): New function, broken out of
+ arm_gen_load_multiple.
+ (arm_gen_store_multiple_1): New function, broken out of
+ arm_gen_store_multiple.
+ (arm_gen_multiple_op): New function, with code from
+ arm_gen_load_multiple and arm_gen_store_multiple moved here.
+ (arm_gen_load_multiple, arm_gen_store_multiple): Now just
+ wrappers around arm_gen_multiple_op. Remove argument UP, all callers
+ changed.
+ (gen_ldm_seq, gen_stm_seq, gen_const_stm_seq): New functions.
+ * config/arm/predicates.md (commutative_binary_operator): New.
+ (load_multiple_operation, store_multiple_operation): Handle more
+ variants of these patterns with different starting offsets. Handle
+ Thumb-1.
+ * config/arm/arm.md: Include "ldmstm.md".
+ (ldmsi_postinc4, ldmsi_postinc4_thumb1, ldmsi_postinc3, ldmsi_postinc2,
+ ldmsi4, ldmsi3, ldmsi2, stmsi_postinc4, stmsi_postinc4_thumb1,
+ stmsi_postinc3, stmsi_postinc2, stmsi4, stmsi3, stmsi2 and related
+ peepholes): Delete.
+ * config/arm/ldmstm.md: New file.
+ * config/arm/arm-ldmstm.ml: New file.
+
+ * config/arm/arm.c (arm_rtx_costs_1): Remove second clause from the
+ if statement which adds extra costs to frame-related expressions.
+
+ 2010-05-06 Bernd Schmidt <bernds@codesourcery.com>
+
+ * config/arm/arm.h (MAX_LDM_STM_OPS): New macro.
+ * config/arm/arm.c (multiple_operation_profitable_p,
+ compute_offset_order): New static functions.
+ (load_multiple_sequence, store_multiple_sequence): Use them.
+ Replace constant 4 with MAX_LDM_STM_OPS. Compute order[0] from
+ memory offsets, not register numbers.
+ (emit_ldm_seq, emit_stm_seq): Replace constant 4 with MAX_LDM_STM_OPS.
+
+ 2010-04-16 Bernd Schmidt <bernds@codesourcery.com>
+
+ * recog.h (struct recog_data): New field is_operator.
+ (struct insn_operand_data): New field is_operator.
+ * recog.c (extract_insn): Set recog_data.is_operator.
+ * genoutput.c (output_operand_data): Emit code to set the
+ is_operator field.
+ * reload.c (find_reloads): Use it rather than testing for an
+ empty constraint string.
+
+=== added file 'gcc/config/arm/arm-ldmstm.ml'
+--- old/gcc/config/arm/arm-ldmstm.ml 1970-01-01 00:00:00 +0000
++++ new/gcc/config/arm/arm-ldmstm.ml 2010-11-16 13:08:47 +0000
+@@ -0,0 +1,333 @@
++(* Auto-generate ARM ldm/stm patterns
++ Copyright (C) 2010 Free Software Foundation, Inc.
++ Contributed by CodeSourcery.
++
++ 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 is an O'Caml program. The O'Caml compiler is available from:
++
++ http://caml.inria.fr/
++
++ Or from your favourite OS's friendly packaging system. Tested with version
++ 3.09.2, though other versions will probably work too.
++
++ Run with:
++ ocaml arm-ldmstm.ml >/path/to/gcc/config/arm/ldmstm.ml
++*)
++
++type amode = IA | IB | DA | DB
++
++type optype = IN | OUT | INOUT
++
++let rec string_of_addrmode addrmode =
++ match addrmode with
++ IA -> "ia" | IB -> "ib" | DA -> "da" | DB -> "db"
++
++let rec initial_offset addrmode nregs =
++ match addrmode with
++ IA -> 0
++ | IB -> 4
++ | DA -> -4 * nregs + 4
++ | DB -> -4 * nregs
++
++let rec final_offset addrmode nregs =
++ match addrmode with
++ IA -> nregs * 4
++ | IB -> nregs * 4
++ | DA -> -4 * nregs
++ | DB -> -4 * nregs
++
++let constr thumb =
++ if thumb then "l" else "rk"
++
++let inout_constr op_type =
++ match op_type with
++ OUT -> "="
++ | INOUT -> "+&"
++ | IN -> ""
++
++let destreg nregs first op_type thumb =
++ if not first then
++ Printf.sprintf "(match_dup %d)" (nregs)
++ else
++ Printf.sprintf ("(match_operand:SI %d \"s_register_operand\" \"%s%s\")")
++ (nregs) (inout_constr op_type) (constr thumb)
++
++let write_ldm_set thumb nregs offset opnr first =
++ let indent = " " in
++ Printf.printf "%s" (if first then " [" else indent);
++ Printf.printf "(set (match_operand:SI %d \"arm_hard_register_operand\" \"\")\n" opnr;
++ Printf.printf "%s (mem:SI " indent;
++ begin if offset != 0 then Printf.printf "(plus:SI " end;
++ Printf.printf "%s" (destreg nregs first IN thumb);
++ begin if offset != 0 then Printf.printf "\n%s (const_int %d))" indent offset end;
++ Printf.printf "))"
++
++let write_stm_set thumb nregs offset opnr first =
++ let indent = " " in
++ Printf.printf "%s" (if first then " [" else indent);
++ Printf.printf "(set (mem:SI ";
++ begin if offset != 0 then Printf.printf "(plus:SI " end;
++ Printf.printf "%s" (destreg nregs first IN thumb);
++ begin if offset != 0 then Printf.printf " (const_int %d))" offset end;
++ Printf.printf ")\n%s (match_operand:SI %d \"arm_hard_register_operand\" \"\"))" indent opnr
++
++let write_ldm_peep_set extra_indent nregs opnr first =
++ let indent = " " ^ extra_indent in
++ Printf.printf "%s" (if first then extra_indent ^ " [" else indent);
++ Printf.printf "(set (match_operand:SI %d \"s_register_operand\" \"\")\n" opnr;
++ Printf.printf "%s (match_operand:SI %d \"memory_operand\" \"\"))" indent (nregs + opnr)
++
++let write_stm_peep_set extra_indent nregs opnr first =
++ let indent = " " ^ extra_indent in
++ Printf.printf "%s" (if first then extra_indent ^ " [" else indent);
++ Printf.printf "(set (match_operand:SI %d \"memory_operand\" \"\")\n" (nregs + opnr);
++ Printf.printf "%s (match_operand:SI %d \"s_register_operand\" \"\"))" indent opnr
++
++let write_any_load optype nregs opnr first =
++ let indent = " " in
++ Printf.printf "%s" (if first then " [" else indent);
++ Printf.printf "(set (match_operand:SI %d \"s_register_operand\" \"\")\n" opnr;
++ Printf.printf "%s (match_operand:SI %d \"%s\" \"\"))" indent (nregs * 2 + opnr) optype
++
++let write_const_store nregs opnr first =
++ let indent = " " in
++ Printf.printf "%s(set (match_operand:SI %d \"memory_operand\" \"\")\n" indent (nregs + opnr);
++ Printf.printf "%s (match_dup %d))" indent opnr
++
++let write_const_stm_peep_set nregs opnr first =
++ write_any_load "const_int_operand" nregs opnr first;
++ Printf.printf "\n";
++ write_const_store nregs opnr false
++
++
++let rec write_pat_sets func opnr offset first n_left =
++ func offset opnr first;
++ begin
++ if n_left > 1 then begin
++ Printf.printf "\n";
++ write_pat_sets func (opnr + 1) (offset + 4) false (n_left - 1);
++ end else
++ Printf.printf "]"
++ end
++
++let rec write_peep_sets func opnr first n_left =
++ func opnr first;
++ begin
++ if n_left > 1 then begin
++ Printf.printf "\n";
++ write_peep_sets func (opnr + 1) false (n_left - 1);
++ end
++ end
++
++let can_thumb addrmode update is_store =
++ match addrmode, update, is_store with
++ (* Thumb1 mode only supports IA with update. However, for LDMIA,
++ if the address register also appears in the list of loaded
++ registers, the loaded value is stored, hence the RTL pattern
++ to describe such an insn does not have an update. We check
++ in the match_parallel predicate that the condition described
++ above is met. *)
++ IA, _, false -> true
++ | IA, true, true -> true
++ | _ -> false
++
++let target addrmode thumb =
++ match addrmode, thumb with
++ IA, true -> "TARGET_THUMB1"
++ | IA, false -> "TARGET_32BIT"
++ | DB, false -> "TARGET_32BIT"
++ | _, false -> "TARGET_ARM"
++
++let write_pattern_1 name ls addrmode nregs write_set_fn update thumb =
++ let astr = string_of_addrmode addrmode in
++ Printf.printf "(define_insn \"*%s%s%d_%s%s\"\n"
++ (if thumb then "thumb_" else "") name nregs astr
++ (if update then "_update" else "");
++ Printf.printf " [(match_parallel 0 \"%s_multiple_operation\"\n" ls;
++ begin
++ if update then begin
++ Printf.printf " [(set %s\n (plus:SI "
++ (destreg 1 true OUT thumb); (*destreg 2 true IN thumb*)
++ Printf.printf "(match_operand:SI 2 \"s_register_operand\" \"1\")";
++ Printf.printf " (const_int %d)))\n"
++ (final_offset addrmode nregs)
++ end
++ end;
++ write_pat_sets
++ (write_set_fn thumb (if update then 2 else 1)) (if update then 3 else 2)
++ (initial_offset addrmode nregs)
++ (not update) nregs;
++ Printf.printf ")]\n \"%s && XVECLEN (operands[0], 0) == %d\"\n"
++ (target addrmode thumb)
++ (if update then nregs + 1 else nregs);
++ Printf.printf " \"%s%%(%s%%)\\t%%%d%s, {"
++ name astr (1) (if update then "!" else "");
++ for n = 1 to nregs; do
++ Printf.printf "%%%d%s" (n+(if update then 2 else 1)) (if n < nregs then ", " else "")
++ done;
++ Printf.printf "}\"\n";
++ Printf.printf " [(set_attr \"type\" \"%s%d\")" ls nregs;
++ begin if not thumb then
++ Printf.printf "\n (set_attr \"predicable\" \"yes\")";
++ end;
++ Printf.printf "])\n\n"
++
++let write_ldm_pattern addrmode nregs update =
++ write_pattern_1 "ldm" "load" addrmode nregs write_ldm_set update false;
++ begin if can_thumb addrmode update false then
++ write_pattern_1 "ldm" "load" addrmode nregs write_ldm_set update true;
++ end
++
++let write_stm_pattern addrmode nregs update =
++ write_pattern_1 "stm" "store" addrmode nregs write_stm_set update false;
++ begin if can_thumb addrmode update true then
++ write_pattern_1 "stm" "store" addrmode nregs write_stm_set update true;
++ end
++
++let write_ldm_commutative_peephole thumb =
++ let nregs = 2 in
++ Printf.printf "(define_peephole2\n";
++ write_peep_sets (write_ldm_peep_set "" nregs) 0 true nregs;
++ let indent = " " in
++ if thumb then begin
++ Printf.printf "\n%s(set (match_operand:SI %d \"s_register_operand\" \"\")\n" indent (nregs * 2);
++ Printf.printf "%s (match_operator:SI %d \"commutative_binary_operator\"\n" indent (nregs * 2 + 1);
++ Printf.printf "%s [(match_operand:SI %d \"s_register_operand\" \"\")\n" indent (nregs * 2 + 2);
++ Printf.printf "%s (match_operand:SI %d \"s_register_operand\" \"\")]))]\n" indent (nregs * 2 + 3)
++ end else begin
++ Printf.printf "\n%s(parallel\n" indent;
++ Printf.printf "%s [(set (match_operand:SI %d \"s_register_operand\" \"\")\n" indent (nregs * 2);
++ Printf.printf "%s (match_operator:SI %d \"commutative_binary_operator\"\n" indent (nregs * 2 + 1);
++ Printf.printf "%s [(match_operand:SI %d \"s_register_operand\" \"\")\n" indent (nregs * 2 + 2);
++ Printf.printf "%s (match_operand:SI %d \"s_register_operand\" \"\")]))\n" indent (nregs * 2 + 3);
++ Printf.printf "%s (clobber (reg:CC CC_REGNUM))])]\n" indent
++ end;
++ Printf.printf " \"(((operands[%d] == operands[0] && operands[%d] == operands[1])\n" (nregs * 2 + 2) (nregs * 2 + 3);
++ Printf.printf " || (operands[%d] == operands[0] && operands[%d] == operands[1]))\n" (nregs * 2 + 3) (nregs * 2 + 2);
++ Printf.printf " && peep2_reg_dead_p (%d, operands[0]) && peep2_reg_dead_p (%d, operands[1]))\"\n" (nregs + 1) (nregs + 1);
++ begin
++ if thumb then
++ Printf.printf " [(set (match_dup %d) (match_op_dup %d [(match_dup %d) (match_dup %d)]))]\n"
++ (nregs * 2) (nregs * 2 + 1) (nregs * 2 + 2) (nregs * 2 + 3)
++ else begin
++ Printf.printf " [(parallel\n";
++ Printf.printf " [(set (match_dup %d) (match_op_dup %d [(match_dup %d) (match_dup %d)]))\n"
++ (nregs * 2) (nregs * 2 + 1) (nregs * 2 + 2) (nregs * 2 + 3);
++ Printf.printf " (clobber (reg:CC CC_REGNUM))])]\n"
++ end
++ end;
++ Printf.printf "{\n if (!gen_ldm_seq (operands, %d, true))\n FAIL;\n" nregs;
++ Printf.printf "})\n\n"
++
++let write_ldm_peephole nregs =
++ Printf.printf "(define_peephole2\n";
++ write_peep_sets (write_ldm_peep_set "" nregs) 0 true nregs;
++ Printf.printf "]\n \"\"\n [(const_int 0)]\n{\n";
++ Printf.printf " if (gen_ldm_seq (operands, %d, false))\n DONE;\n else\n FAIL;\n})\n\n" nregs
++
++let write_ldm_peephole_b nregs =
++ if nregs > 2 then begin
++ Printf.printf "(define_peephole2\n";
++ write_ldm_peep_set "" nregs 0 true;
++ Printf.printf "\n (parallel\n";
++ write_peep_sets (write_ldm_peep_set " " nregs) 1 true (nregs - 1);
++ Printf.printf "])]\n \"\"\n [(const_int 0)]\n{\n";
++ Printf.printf " if (gen_ldm_seq (operands, %d, false))\n DONE;\n else\n FAIL;\n})\n\n" nregs
++ end
++
++let write_stm_peephole nregs =
++ Printf.printf "(define_peephole2\n";
++ write_peep_sets (write_stm_peep_set "" nregs) 0 true nregs;
++ Printf.printf "]\n \"\"\n [(const_int 0)]\n{\n";
++ Printf.printf " if (gen_stm_seq (operands, %d))\n DONE;\n else\n FAIL;\n})\n\n" nregs
++
++let write_stm_peephole_b nregs =
++ if nregs > 2 then begin
++ Printf.printf "(define_peephole2\n";
++ write_stm_peep_set "" nregs 0 true;
++ Printf.printf "\n (parallel\n";
++ write_peep_sets (write_stm_peep_set "" nregs) 1 true (nregs - 1);
++ Printf.printf "]\n \"\"\n [(const_int 0)]\n{\n";
++ Printf.printf " if (gen_stm_seq (operands, %d))\n DONE;\n else\n FAIL;\n})\n\n" nregs
++ end
++
++let write_const_stm_peephole_a nregs =
++ Printf.printf "(define_peephole2\n";
++ write_peep_sets (write_const_stm_peep_set nregs) 0 true nregs;
++ Printf.printf "]\n \"\"\n [(const_int 0)]\n{\n";
++ Printf.printf " if (gen_const_stm_seq (operands, %d))\n DONE;\n else\n FAIL;\n})\n\n" nregs
++
++let write_const_stm_peephole_b nregs =
++ Printf.printf "(define_peephole2\n";
++ write_peep_sets (write_any_load "const_int_operand" nregs) 0 true nregs;
++ Printf.printf "\n";
++ write_peep_sets (write_const_store nregs) 0 false nregs;
++ Printf.printf "]\n \"\"\n [(const_int 0)]\n{\n";
++ Printf.printf " if (gen_const_stm_seq (operands, %d))\n DONE;\n else\n FAIL;\n})\n\n" nregs
++
++let patterns () =
++ let addrmodes = [ IA; IB; DA; DB ] in
++ let sizes = [ 4; 3; 2] in
++ List.iter
++ (fun n ->
++ List.iter
++ (fun addrmode ->
++ write_ldm_pattern addrmode n false;
++ write_ldm_pattern addrmode n true;
++ write_stm_pattern addrmode n false;
++ write_stm_pattern addrmode n true)
++ addrmodes;
++ write_ldm_peephole n;
++ write_ldm_peephole_b n;
++ write_const_stm_peephole_a n;
++ write_const_stm_peephole_b n;
++ write_stm_peephole n;)
++ sizes;
++ write_ldm_commutative_peephole false;
++ write_ldm_commutative_peephole true
++
++let print_lines = List.iter (fun s -> Format.printf "%s@\n" s)
++
++(* Do it. *)
++
++let _ =
++ print_lines [
++"/* ARM ldm/stm instruction patterns. This file was automatically generated";
++" using arm-ldmstm.ml. Please do not edit manually.";
++"";
++" Copyright (C) 2010 Free Software Foundation, Inc.";
++" Contributed by CodeSourcery.";
++"";
++" 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 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/>. */";
++""];
++ patterns ();
+
+=== modified file 'gcc/config/arm/arm-protos.h'
+--- old/gcc/config/arm/arm-protos.h 2011-01-05 12:12:18 +0000
++++ new/gcc/config/arm/arm-protos.h 2011-01-05 18:20:37 +0000
+@@ -100,14 +100,11 @@
+ extern int label_mentioned_p (rtx);
+ extern RTX_CODE minmax_code (rtx);
+ extern int adjacent_mem_locations (rtx, rtx);
+-extern int load_multiple_sequence (rtx *, int, int *, int *, HOST_WIDE_INT *);
+-extern const char *emit_ldm_seq (rtx *, int);
+-extern int store_multiple_sequence (rtx *, int, int *, int *, HOST_WIDE_INT *);
+-extern const char * emit_stm_seq (rtx *, int);
+-extern rtx arm_gen_load_multiple (int, int, rtx, int, int,
+- rtx, HOST_WIDE_INT *);
+-extern rtx arm_gen_store_multiple (int, int, rtx, int, int,
+- rtx, HOST_WIDE_INT *);
++extern bool gen_ldm_seq (rtx *, int, bool);
++extern bool gen_stm_seq (rtx *, int);
++extern bool gen_const_stm_seq (rtx *, int);
++extern rtx arm_gen_load_multiple (int *, int, rtx, int, rtx, HOST_WIDE_INT *);
++extern rtx arm_gen_store_multiple (int *, int, rtx, int, rtx, HOST_WIDE_INT *);
+ extern int arm_gen_movmemqi (rtx *);
+ extern enum machine_mode arm_select_cc_mode (RTX_CODE, rtx, rtx);
+ extern enum machine_mode arm_select_dominance_cc_mode (rtx, rtx,
+
+=== modified file 'gcc/config/arm/arm.c'
+--- old/gcc/config/arm/arm.c 2011-01-05 12:12:18 +0000
++++ new/gcc/config/arm/arm.c 2011-01-05 18:20:37 +0000
+@@ -753,6 +753,12 @@
+ "hi", "ls", "ge", "lt", "gt", "le", "al", "nv"
+ };
+
++/* The register numbers in sequence, for passing to arm_gen_load_multiple. */
++int arm_regs_in_sequence[] =
++{
++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
++};
++
+ #define ARM_LSL_NAME (TARGET_UNIFIED_ASM ? "lsl" : "asl")
+ #define streq(string1, string2) (strcmp (string1, string2) == 0)
+
+@@ -9680,142 +9686,16 @@
+ return 0;
+ }
+
+-int
+-load_multiple_sequence (rtx *operands, int nops, int *regs, int *base,
+- HOST_WIDE_INT *load_offset)
+-{
+- int unsorted_regs[4];
+- HOST_WIDE_INT unsorted_offsets[4];
+- int order[4];
+- int base_reg = -1;
+- int i;
+-
+- if (low_irq_latency)
+- return 0;
+-
+- /* Can only handle 2, 3, or 4 insns at present,
+- though could be easily extended if required. */
+- gcc_assert (nops >= 2 && nops <= 4);
+-
+- memset (order, 0, 4 * sizeof (int));
+-
+- /* Loop over the operands and check that the memory references are
+- suitable (i.e. immediate offsets from the same base register). At
+- the same time, extract the target register, and the memory
+- offsets. */
+- for (i = 0; i < nops; i++)
+- {
+- rtx reg;
+- rtx offset;
+-
+- /* Convert a subreg of a mem into the mem itself. */
+- if (GET_CODE (operands[nops + i]) == SUBREG)
+- operands[nops + i] = alter_subreg (operands + (nops + i));
+-
+- gcc_assert (GET_CODE (operands[nops + i]) == MEM);
+-
+- /* Don't reorder volatile memory references; it doesn't seem worth
+- looking for the case where the order is ok anyway. */
+- if (MEM_VOLATILE_P (operands[nops + i]))
+- return 0;
+-
+- offset = const0_rtx;
+-
+- if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG
+- || (GET_CODE (reg) == SUBREG
+- && GET_CODE (reg = SUBREG_REG (reg)) == REG))
+- || (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS
+- && ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0))
+- == REG)
+- || (GET_CODE (reg) == SUBREG
+- && GET_CODE (reg = SUBREG_REG (reg)) == REG))
+- && (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1))
+- == CONST_INT)))
+- {
+- if (i == 0)
+- {
+- base_reg = REGNO (reg);
+- unsorted_regs[0] = (GET_CODE (operands[i]) == REG
+- ? REGNO (operands[i])
+- : REGNO (SUBREG_REG (operands[i])));
+- order[0] = 0;
+- }
+- else
+- {
+- if (base_reg != (int) REGNO (reg))
+- /* Not addressed from the same base register. */
+- return 0;
+-
+- unsorted_regs[i] = (GET_CODE (operands[i]) == REG
+- ? REGNO (operands[i])
+- : REGNO (SUBREG_REG (operands[i])));
+- if (unsorted_regs[i] < unsorted_regs[order[0]])
+- order[0] = i;
+- }
+-
+- /* If it isn't an integer register, or if it overwrites the
+- base register but isn't the last insn in the list, then
+- we can't do this. */
+- if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14
+- || (i != nops - 1 && unsorted_regs[i] == base_reg))
+- return 0;
+-
+- unsorted_offsets[i] = INTVAL (offset);
+- }
+- else
+- /* Not a suitable memory address. */
+- return 0;
+- }
+-
+- /* All the useful information has now been extracted from the
+- operands into unsorted_regs and unsorted_offsets; additionally,
+- order[0] has been set to the lowest numbered register in the
+- list. Sort the registers into order, and check that the memory
+- offsets are ascending and adjacent. */
+-
+- for (i = 1; i < nops; i++)
+- {
+- int j;
+-
+- order[i] = order[i - 1];
+- for (j = 0; j < nops; j++)
+- if (unsorted_regs[j] > unsorted_regs[order[i - 1]]
+- && (order[i] == order[i - 1]
+- || unsorted_regs[j] < unsorted_regs[order[i]]))
+- order[i] = j;
+-
+- /* Have we found a suitable register? if not, one must be used more
+- than once. */
+- if (order[i] == order[i - 1])
+- return 0;
+-
+- /* Is the memory address adjacent and ascending? */
+- if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4)
+- return 0;
+- }
+-
+- if (base)
+- {
+- *base = base_reg;
+-
+- for (i = 0; i < nops; i++)
+- regs[i] = unsorted_regs[order[i]];
+-
+- *load_offset = unsorted_offsets[order[0]];
+- }
+-
+- if (unsorted_offsets[order[0]] == 0)
+- return 1; /* ldmia */
+-
+- if (TARGET_ARM && unsorted_offsets[order[0]] == 4)
+- return 2; /* ldmib */
+-
+- if (TARGET_ARM && unsorted_offsets[order[nops - 1]] == 0)
+- return 3; /* ldmda */
+-
+- if (unsorted_offsets[order[nops - 1]] == -4)
+- return 4; /* ldmdb */
+-
++
++/* Return true iff it would be profitable to turn a sequence of NOPS loads
++ or stores (depending on IS_STORE) into a load-multiple or store-multiple
++ instruction. ADD_OFFSET is nonzero if the base address register needs
++ to be modified with an add instruction before we can use it. */
++
++static bool
++multiple_operation_profitable_p (bool is_store ATTRIBUTE_UNUSED,
++ int nops, HOST_WIDE_INT add_offset)
++ {
+ /* For ARM8,9 & StrongARM, 2 ldr instructions are faster than an ldm
+ if the offset isn't small enough. The reason 2 ldrs are faster
+ is because these ARMs are able to do more than one cache access
+@@ -9845,91 +9725,239 @@
+ We cheat here and test 'arm_ld_sched' which we currently know to
+ only be true for the ARM8, ARM9 and StrongARM. If this ever
+ changes, then the test below needs to be reworked. */
+- if (nops == 2 && arm_ld_sched)
++ if (nops == 2 && arm_ld_sched && add_offset != 0)
++ return false;
++
++ return true;
++}
++
++/* Subroutine of load_multiple_sequence and store_multiple_sequence.
++ Given an array of UNSORTED_OFFSETS, of which there are NOPS, compute
++ an array ORDER which describes the sequence to use when accessing the
++ offsets that produces an ascending order. In this sequence, each
++ offset must be larger by exactly 4 than the previous one. ORDER[0]
++ must have been filled in with the lowest offset by the caller.
++ If UNSORTED_REGS is nonnull, it is an array of register numbers that
++ we use to verify that ORDER produces an ascending order of registers.
++ Return true if it was possible to construct such an order, false if
++ not. */
++
++static bool
++compute_offset_order (int nops, HOST_WIDE_INT *unsorted_offsets, int *order,
++ int *unsorted_regs)
++{
++ int i;
++ for (i = 1; i < nops; i++)
++ {
++ int j;
++
++ order[i] = order[i - 1];
++ for (j = 0; j < nops; j++)
++ if (unsorted_offsets[j] == unsorted_offsets[order[i - 1]] + 4)
++ {
++ /* We must find exactly one offset that is higher than the
++ previous one by 4. */
++ if (order[i] != order[i - 1])
++ return false;
++ order[i] = j;
++ }
++ if (order[i] == order[i - 1])
++ return false;
++ /* The register numbers must be ascending. */
++ if (unsorted_regs != NULL
++ && unsorted_regs[order[i]] <= unsorted_regs[order[i - 1]])
++ return false;
++ }
++ return true;
++}
++
++/* Used to determine in a peephole whether a sequence of load
++ instructions can be changed into a load-multiple instruction.
++ NOPS is the number of separate load instructions we are examining. The
++ first NOPS entries in OPERANDS are the destination registers, the
++ next NOPS entries are memory operands. If this function is
++ successful, *BASE is set to the common base register of the memory
++ accesses; *LOAD_OFFSET is set to the first memory location's offset
++ from that base register.
++ REGS is an array filled in with the destination register numbers.
++ SAVED_ORDER (if nonnull), is an array filled in with an order that maps
++ insn numbers to to an ascending order of stores. If CHECK_REGS is true,
++ the sequence of registers in REGS matches the loads from ascending memory
++ locations, and the function verifies that the register numbers are
++ themselves ascending. If CHECK_REGS is false, the register numbers
++ are stored in the order they are found in the operands. */
++static int
++load_multiple_sequence (rtx *operands, int nops, int *regs, int *saved_order,
++ int *base, HOST_WIDE_INT *load_offset, bool check_regs)
++{
++ int unsorted_regs[MAX_LDM_STM_OPS];
++ HOST_WIDE_INT unsorted_offsets[MAX_LDM_STM_OPS];
++ int order[MAX_LDM_STM_OPS];
++ rtx base_reg_rtx = NULL;
++ int base_reg = -1;
++ int i, ldm_case;
++
++ if (low_irq_latency)
+ return 0;
+
+- /* Can't do it without setting up the offset, only do this if it takes
+- no more than one insn. */
+- return (const_ok_for_arm (unsorted_offsets[order[0]])
+- || const_ok_for_arm (-unsorted_offsets[order[0]])) ? 5 : 0;
+-}
+-
+-const char *
+-emit_ldm_seq (rtx *operands, int nops)
+-{
+- int regs[4];
+- int base_reg;
+- HOST_WIDE_INT offset;
+- char buf[100];
+- int i;
+-
+- switch (load_multiple_sequence (operands, nops, regs, &base_reg, &offset))
++ /* Can only handle up to MAX_LDM_STM_OPS insns at present, though could be
++ easily extended if required. */
++ gcc_assert (nops >= 2 && nops <= MAX_LDM_STM_OPS);
++
++ memset (order, 0, MAX_LDM_STM_OPS * sizeof (int));
++
++ /* Loop over the operands and check that the memory references are
++ suitable (i.e. immediate offsets from the same base register). At
++ the same time, extract the target register, and the memory
++ offsets. */
++ for (i = 0; i < nops; i++)
+ {
+- case 1:
+- strcpy (buf, "ldm%(ia%)\t");
+- break;
+-
+- case 2:
+- strcpy (buf, "ldm%(ib%)\t");
+- break;
+-
+- case 3:
+- strcpy (buf, "ldm%(da%)\t");
+- break;
+-
+- case 4:
+- strcpy (buf, "ldm%(db%)\t");
+- break;
+-
+- case 5:
+- if (offset >= 0)
+- sprintf (buf, "add%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX,
+- reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg],
+- (long) offset);
++ rtx reg;
++ rtx offset;
++
++ /* Convert a subreg of a mem into the mem itself. */
++ if (GET_CODE (operands[nops + i]) == SUBREG)
++ operands[nops + i] = alter_subreg (operands + (nops + i));
++
++ gcc_assert (GET_CODE (operands[nops + i]) == MEM);
++
++ /* Don't reorder volatile memory references; it doesn't seem worth
++ looking for the case where the order is ok anyway. */
++ if (MEM_VOLATILE_P (operands[nops + i]))
++ return 0;
++
++ offset = const0_rtx;
++
++ if ((GET_CODE (reg = XEXP (operands[nops + i], 0)) == REG
++ || (GET_CODE (reg) == SUBREG
++ && GET_CODE (reg = SUBREG_REG (reg)) == REG))
++ || (GET_CODE (XEXP (operands[nops + i], 0)) == PLUS
++ && ((GET_CODE (reg = XEXP (XEXP (operands[nops + i], 0), 0))
++ == REG)
++ || (GET_CODE (reg) == SUBREG
++ && GET_CODE (reg = SUBREG_REG (reg)) == REG))
++ && (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1))
++ == CONST_INT)))
++ {
++ if (i == 0)
++ {
++ base_reg = REGNO (reg);
++ base_reg_rtx = reg;
++ if (TARGET_THUMB1 && base_reg > LAST_LO_REGNUM)
++ return 0;
++ }
++ else if (base_reg != (int) REGNO (reg))
++ /* Not addressed from the same base register. */
++ return 0;
++
++ unsorted_regs[i] = (GET_CODE (operands[i]) == REG
++ ? REGNO (operands[i])
++ : REGNO (SUBREG_REG (operands[i])));
++
++ /* If it isn't an integer register, or if it overwrites the
++ base register but isn't the last insn in the list, then
++ we can't do this. */
++ if (unsorted_regs[i] < 0
++ || (TARGET_THUMB1 && unsorted_regs[i] > LAST_LO_REGNUM)
++ || unsorted_regs[i] > 14
++ || (i != nops - 1 && unsorted_regs[i] == base_reg))
++ return 0;
++
++ unsorted_offsets[i] = INTVAL (offset);
++ if (i == 0 || unsorted_offsets[i] < unsorted_offsets[order[0]])
++ order[0] = i;
++ }
+ else
+- sprintf (buf, "sub%%?\t%s%s, %s%s, #%ld", REGISTER_PREFIX,
+- reg_names[regs[0]], REGISTER_PREFIX, reg_names[base_reg],
+- (long) -offset);
+- output_asm_insn (buf, operands);
+- base_reg = regs[0];
+- strcpy (buf, "ldm%(ia%)\t");
+- break;
+-
+- default:
+- gcc_unreachable ();
+- }
+-
+- sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX,
+- reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]);
+-
+- for (i = 1; i < nops; i++)
+- sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX,
+- reg_names[regs[i]]);
+-
+- strcat (buf, "}\t%@ phole ldm");
+-
+- output_asm_insn (buf, operands);
+- return "";
++ /* Not a suitable memory address. */
++ return 0;
++ }
++
++ /* All the useful information has now been extracted from the
++ operands into unsorted_regs and unsorted_offsets; additionally,
++ order[0] has been set to the lowest offset in the list. Sort
++ the offsets into order, verifying that they are adjacent, and
++ check that the register numbers are ascending. */
++ if (!compute_offset_order (nops, unsorted_offsets, order,
++ check_regs ? unsorted_regs : NULL))
++ return 0;
++
++ if (saved_order)
++ memcpy (saved_order, order, sizeof order);
++
++ if (base)
++ {
++ *base = base_reg;
++
++ for (i = 0; i < nops; i++)
++ regs[i] = unsorted_regs[check_regs ? order[i] : i];
++
++ *load_offset = unsorted_offsets[order[0]];
++ }
++
++ if (TARGET_THUMB1
++ && !peep2_reg_dead_p (nops, base_reg_rtx))
++ return 0;
++
++ if (unsorted_offsets[order[0]] == 0)
++ ldm_case = 1; /* ldmia */
++ else if (TARGET_ARM && unsorted_offsets[order[0]] == 4)
++ ldm_case = 2; /* ldmib */
++ else if (TARGET_ARM && unsorted_offsets[order[nops - 1]] == 0)
++ ldm_case = 3; /* ldmda */
++ else if (TARGET_32BIT && unsorted_offsets[order[nops - 1]] == -4)
++ ldm_case = 4; /* ldmdb */
++ else if (const_ok_for_arm (unsorted_offsets[order[0]])
++ || const_ok_for_arm (-unsorted_offsets[order[0]]))
++ ldm_case = 5;
++ else
++ return 0;
++
++ if (!multiple_operation_profitable_p (false, nops,
++ ldm_case == 5
++ ? unsorted_offsets[order[0]] : 0))
++ return 0;
++
++ return ldm_case;
+ }
+
+-int
+-store_multiple_sequence (rtx *operands, int nops, int *regs, int *base,
+- HOST_WIDE_INT * load_offset)
++/* Used to determine in a peephole whether a sequence of store instructions can
++ be changed into a store-multiple instruction.
++ NOPS is the number of separate store instructions we are examining.
++ NOPS_TOTAL is the total number of instructions recognized by the peephole
++ pattern.
++ The first NOPS entries in OPERANDS are the source registers, the next
++ NOPS entries are memory operands. If this function is successful, *BASE is
++ set to the common base register of the memory accesses; *LOAD_OFFSET is set
++ to the first memory location's offset from that base register. REGS is an
++ array filled in with the source register numbers, REG_RTXS (if nonnull) is
++ likewise filled with the corresponding rtx's.
++ SAVED_ORDER (if nonnull), is an array filled in with an order that maps insn
++ numbers to to an ascending order of stores.
++ If CHECK_REGS is true, the sequence of registers in *REGS matches the stores
++ from ascending memory locations, and the function verifies that the register
++ numbers are themselves ascending. If CHECK_REGS is false, the register
++ numbers are stored in the order they are found in the operands. */
++static int
++store_multiple_sequence (rtx *operands, int nops, int nops_total,
++ int *regs, rtx *reg_rtxs, int *saved_order, int *base,
++ HOST_WIDE_INT *load_offset, bool check_regs)
+ {
+- int unsorted_regs[4];
+- HOST_WIDE_INT unsorted_offsets[4];
+- int order[4];
++ int unsorted_regs[MAX_LDM_STM_OPS];
++ rtx unsorted_reg_rtxs[MAX_LDM_STM_OPS];
++ HOST_WIDE_INT unsorted_offsets[MAX_LDM_STM_OPS];
++ int order[MAX_LDM_STM_OPS];
+ int base_reg = -1;
+- int i;
++ rtx base_reg_rtx = NULL;
++ int i, stm_case;
+
+ if (low_irq_latency)
+ return 0;
+
+- /* Can only handle 2, 3, or 4 insns at present, though could be easily
+- extended if required. */
+- gcc_assert (nops >= 2 && nops <= 4);
++ /* Can only handle up to MAX_LDM_STM_OPS insns at present, though could be
++ easily extended if required. */
++ gcc_assert (nops >= 2 && nops <= MAX_LDM_STM_OPS);
+
+- memset (order, 0, 4 * sizeof (int));
++ memset (order, 0, MAX_LDM_STM_OPS * sizeof (int));
+
+ /* Loop over the operands and check that the memory references are
+ suitable (i.e. immediate offsets from the same base register). At
+@@ -9964,32 +9992,32 @@
+ && (GET_CODE (offset = XEXP (XEXP (operands[nops + i], 0), 1))
+ == CONST_INT)))
+ {
++ unsorted_reg_rtxs[i] = (GET_CODE (operands[i]) == REG
++ ? operands[i] : SUBREG_REG (operands[i]));
++ unsorted_regs[i] = REGNO (unsorted_reg_rtxs[i]);
++
+ if (i == 0)
+ {
+ base_reg = REGNO (reg);
+- unsorted_regs[0] = (GET_CODE (operands[i]) == REG
+- ? REGNO (operands[i])
+- : REGNO (SUBREG_REG (operands[i])));
+- order[0] = 0;
+- }
+- else
+- {
+- if (base_reg != (int) REGNO (reg))
+- /* Not addressed from the same base register. */
++ base_reg_rtx = reg;
++ if (TARGET_THUMB1 && base_reg > LAST_LO_REGNUM)
+ return 0;
+-
+- unsorted_regs[i] = (GET_CODE (operands[i]) == REG
+- ? REGNO (operands[i])
+- : REGNO (SUBREG_REG (operands[i])));
+- if (unsorted_regs[i] < unsorted_regs[order[0]])
+- order[0] = i;
+ }
++ else if (base_reg != (int) REGNO (reg))
++ /* Not addressed from the same base register. */
++ return 0;
+
+ /* If it isn't an integer register, then we can't do this. */
+- if (unsorted_regs[i] < 0 || unsorted_regs[i] > 14)
++ if (unsorted_regs[i] < 0
++ || (TARGET_THUMB1 && unsorted_regs[i] > LAST_LO_REGNUM)
++ || (TARGET_THUMB2 && unsorted_regs[i] == base_reg)
++ || (TARGET_THUMB2 && unsorted_regs[i] == SP_REGNUM)
++ || unsorted_regs[i] > 14)
+ return 0;
+
+ unsorted_offsets[i] = INTVAL (offset);
++ if (i == 0 || unsorted_offsets[i] < unsorted_offsets[order[0]])
++ order[0] = i;
+ }
+ else
+ /* Not a suitable memory address. */
+@@ -9998,111 +10026,65 @@
+
+ /* All the useful information has now been extracted from the
+ operands into unsorted_regs and unsorted_offsets; additionally,
+- order[0] has been set to the lowest numbered register in the
+- list. Sort the registers into order, and check that the memory
+- offsets are ascending and adjacent. */
+-
+- for (i = 1; i < nops; i++)
+- {
+- int j;
+-
+- order[i] = order[i - 1];
+- for (j = 0; j < nops; j++)
+- if (unsorted_regs[j] > unsorted_regs[order[i - 1]]
+- && (order[i] == order[i - 1]
+- || unsorted_regs[j] < unsorted_regs[order[i]]))
+- order[i] = j;
+-
+- /* Have we found a suitable register? if not, one must be used more
+- than once. */
+- if (order[i] == order[i - 1])
+- return 0;
+-
+- /* Is the memory address adjacent and ascending? */
+- if (unsorted_offsets[order[i]] != unsorted_offsets[order[i - 1]] + 4)
+- return 0;
+- }
++ order[0] has been set to the lowest offset in the list. Sort
++ the offsets into order, verifying that they are adjacent, and
++ check that the register numbers are ascending. */
++ if (!compute_offset_order (nops, unsorted_offsets, order,
++ check_regs ? unsorted_regs : NULL))
++ return 0;
++
++ if (saved_order)
++ memcpy (saved_order, order, sizeof order);
+
+ if (base)
+ {
+ *base = base_reg;
+
+ for (i = 0; i < nops; i++)
+- regs[i] = unsorted_regs[order[i]];
++ {
++ regs[i] = unsorted_regs[check_regs ? order[i] : i];
++ if (reg_rtxs)
++ reg_rtxs[i] = unsorted_reg_rtxs[check_regs ? order[i] : i];
++ }
+
+ *load_offset = unsorted_offsets[order[0]];
+ }
+
++ if (TARGET_THUMB1
++ && !peep2_reg_dead_p (nops_total, base_reg_rtx))
++ return 0;
++
+ if (unsorted_offsets[order[0]] == 0)
+- return 1; /* stmia */
+-
+- if (unsorted_offsets[order[0]] == 4)
+- return 2; /* stmib */
+-
+- if (unsorted_offsets[order[nops - 1]] == 0)
+- return 3; /* stmda */
+-
+- if (unsorted_offsets[order[nops - 1]] == -4)
+- return 4; /* stmdb */
+-
+- return 0;
+-}
+-
+-const char *
+-emit_stm_seq (rtx *operands, int nops)
+-{
+- int regs[4];
+- int base_reg;
+- HOST_WIDE_INT offset;
+- char buf[100];
+- int i;
+-
+- switch (store_multiple_sequence (operands, nops, regs, &base_reg, &offset))
+- {
+- case 1:
+- strcpy (buf, "stm%(ia%)\t");
+- break;
+-
+- case 2:
+- strcpy (buf, "stm%(ib%)\t");
+- break;
+-
+- case 3:
+- strcpy (buf, "stm%(da%)\t");
+- break;
+-
+- case 4:
+- strcpy (buf, "stm%(db%)\t");
+- break;
+-
+- default:
+- gcc_unreachable ();
+- }
+-
+- sprintf (buf + strlen (buf), "%s%s, {%s%s", REGISTER_PREFIX,
+- reg_names[base_reg], REGISTER_PREFIX, reg_names[regs[0]]);
+-
+- for (i = 1; i < nops; i++)
+- sprintf (buf + strlen (buf), ", %s%s", REGISTER_PREFIX,
+- reg_names[regs[i]]);
+-
+- strcat (buf, "}\t%@ phole stm");
+-
+- output_asm_insn (buf, operands);
+- return "";
++ stm_case = 1; /* stmia */
++ else if (TARGET_ARM && unsorted_offsets[order[0]] == 4)
++ stm_case = 2; /* stmib */
++ else if (TARGET_ARM && unsorted_offsets[order[nops - 1]] == 0)
++ stm_case = 3; /* stmda */
++ else if (TARGET_32BIT && unsorted_offsets[order[nops - 1]] == -4)
++ stm_case = 4; /* stmdb */
++ else
++ return 0;
++
++ if (!multiple_operation_profitable_p (false, nops, 0))
++ return 0;
++
++ return stm_case;
+ }
+
+ /* Routines for use in generating RTL. */
+
+-rtx
+-arm_gen_load_multiple (int base_regno, int count, rtx from, int up,
+- int write_back, rtx basemem, HOST_WIDE_INT *offsetp)
++/* Generate a load-multiple instruction. COUNT is the number of loads in
++ the instruction; REGS and MEMS are arrays containing the operands.
++ BASEREG is the base register to be used in addressing the memory operands.
++ WBACK_OFFSET is nonzero if the instruction should update the base
++ register. */
++
++static rtx
++arm_gen_load_multiple_1 (int count, int *regs, rtx *mems, rtx basereg,
++ HOST_WIDE_INT wback_offset)
+ {
+- HOST_WIDE_INT offset = *offsetp;
+ int i = 0, j;
+ rtx result;
+- int sign = up ? 1 : -1;
+- rtx mem, addr;
+
+ /* XScale has load-store double instructions, but they have stricter
+ alignment requirements than load-store multiple, so we cannot
+@@ -10139,18 +10121,10 @@
+ start_sequence ();
+
+ for (i = 0; i < count; i++)
+- {
+- addr = plus_constant (from, i * 4 * sign);
+- mem = adjust_automodify_address (basemem, SImode, addr, offset);
+- emit_move_insn (gen_rtx_REG (SImode, base_regno + i), mem);
+- offset += 4 * sign;
+- }
++ emit_move_insn (gen_rtx_REG (SImode, regs[i]), mems[i]);
+
+- if (write_back)
+- {
+- emit_move_insn (from, plus_constant (from, count * 4 * sign));
+- *offsetp = offset;
+- }
++ if (wback_offset != 0)
++ emit_move_insn (basereg, plus_constant (basereg, wback_offset));
+
+ seq = get_insns ();
+ end_sequence ();
+@@ -10159,41 +10133,40 @@
+ }
+
+ result = gen_rtx_PARALLEL (VOIDmode,
+- rtvec_alloc (count + (write_back ? 1 : 0)));
+- if (write_back)
++ rtvec_alloc (count + (wback_offset != 0 ? 1 : 0)));
++ if (wback_offset != 0)
+ {
+ XVECEXP (result, 0, 0)
+- = gen_rtx_SET (VOIDmode, from, plus_constant (from, count * 4 * sign));
++ = gen_rtx_SET (VOIDmode, basereg,
++ plus_constant (basereg, wback_offset));
+ i = 1;
+ count++;
+ }
+
+ for (j = 0; i < count; i++, j++)
+- {
+- addr = plus_constant (from, j * 4 * sign);
+- mem = adjust_automodify_address_nv (basemem, SImode, addr, offset);
+- XVECEXP (result, 0, i)
+- = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, base_regno + j), mem);
+- offset += 4 * sign;
+- }
+-
+- if (write_back)
+- *offsetp = offset;
++ XVECEXP (result, 0, i)
++ = gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, regs[j]), mems[j]);
+
+ return result;
+ }
+
+-rtx
+-arm_gen_store_multiple (int base_regno, int count, rtx to, int up,
+- int write_back, rtx basemem, HOST_WIDE_INT *offsetp)
++/* Generate a store-multiple instruction. COUNT is the number of stores in
++ the instruction; REGS and MEMS are arrays containing the operands.
++ BASEREG is the base register to be used in addressing the memory operands.
++ WBACK_OFFSET is nonzero if the instruction should update the base
++ register. */
++
++static rtx
++arm_gen_store_multiple_1 (int count, int *regs, rtx *mems, rtx basereg,
++ HOST_WIDE_INT wback_offset)
+ {
+- HOST_WIDE_INT offset = *offsetp;
+ int i = 0, j;
+ rtx result;
+- int sign = up ? 1 : -1;
+- rtx mem, addr;
+-
+- /* See arm_gen_load_multiple for discussion of
++
++ if (GET_CODE (basereg) == PLUS)
++ basereg = XEXP (basereg, 0);
++
++ /* See arm_gen_load_multiple_1 for discussion of
+ the pros/cons of ldm/stm usage for XScale. */
+ if (low_irq_latency || (arm_tune_xscale && count <= 2 && ! optimize_size))
+ {
+@@ -10202,18 +10175,10 @@
+ start_sequence ();
+
+ for (i = 0; i < count; i++)
+- {
+- addr = plus_constant (to, i * 4 * sign);
+- mem = adjust_automodify_address (basemem, SImode, addr, offset);
+- emit_move_insn (mem, gen_rtx_REG (SImode, base_regno + i));
+- offset += 4 * sign;
+- }
++ emit_move_insn (mems[i], gen_rtx_REG (SImode, regs[i]));
+
+- if (write_back)
+- {
+- emit_move_insn (to, plus_constant (to, count * 4 * sign));
+- *offsetp = offset;
+- }
++ if (wback_offset != 0)
++ emit_move_insn (basereg, plus_constant (basereg, wback_offset));
+
+ seq = get_insns ();
+ end_sequence ();
+@@ -10222,29 +10187,319 @@
+ }
+
+ result = gen_rtx_PARALLEL (VOIDmode,
+- rtvec_alloc (count + (write_back ? 1 : 0)));
+- if (write_back)
++ rtvec_alloc (count + (wback_offset != 0 ? 1 : 0)));
++ if (wback_offset != 0)
+ {
+ XVECEXP (result, 0, 0)
+- = gen_rtx_SET (VOIDmode, to,
+- plus_constant (to, count * 4 * sign));
++ = gen_rtx_SET (VOIDmode, basereg,
++ plus_constant (basereg, wback_offset));
+ i = 1;
+ count++;
+ }
+
+ for (j = 0; i < count; i++, j++)
++ XVECEXP (result, 0, i)
++ = gen_rtx_SET (VOIDmode, mems[j], gen_rtx_REG (SImode, regs[j]));
++
++ return result;
++}
++
++/* Generate either a load-multiple or a store-multiple instruction. This
++ function can be used in situations where we can start with a single MEM
++ rtx and adjust its address upwards.
++ COUNT is the number of operations in the instruction, not counting a
++ possible update of the base register. REGS is an array containing the
++ register operands.
++ BASEREG is the base register to be used in addressing the memory operands,
++ which are constructed from BASEMEM.
++ WRITE_BACK specifies whether the generated instruction should include an
++ update of the base register.
++ OFFSETP is used to pass an offset to and from this function; this offset
++ is not used when constructing the address (instead BASEMEM should have an
++ appropriate offset in its address), it is used only for setting
++ MEM_OFFSET. It is updated only if WRITE_BACK is true.*/
++
++static rtx
++arm_gen_multiple_op (bool is_load, int *regs, int count, rtx basereg,
++ bool write_back, rtx basemem, HOST_WIDE_INT *offsetp)
++{
++ rtx mems[MAX_LDM_STM_OPS];
++ HOST_WIDE_INT offset = *offsetp;
++ int i;
++
++ gcc_assert (count <= MAX_LDM_STM_OPS);
++
++ if (GET_CODE (basereg) == PLUS)
++ basereg = XEXP (basereg, 0);
++
++ for (i = 0; i < count; i++)
+ {
+- addr = plus_constant (to, j * 4 * sign);
+- mem = adjust_automodify_address_nv (basemem, SImode, addr, offset);
+- XVECEXP (result, 0, i)
+- = gen_rtx_SET (VOIDmode, mem, gen_rtx_REG (SImode, base_regno + j));
+- offset += 4 * sign;
++ rtx addr = plus_constant (basereg, i * 4);
++ mems[i] = adjust_automodify_address_nv (basemem, SImode, addr, offset);
++ offset += 4;
+ }
+
+ if (write_back)
+ *offsetp = offset;
+
+- return result;
++ if (is_load)
++ return arm_gen_load_multiple_1 (count, regs, mems, basereg,
++ write_back ? 4 * count : 0);
++ else
++ return arm_gen_store_multiple_1 (count, regs, mems, basereg,
++ write_back ? 4 * count : 0);
++}
++
++rtx
++arm_gen_load_multiple (int *regs, int count, rtx basereg, int write_back,
++ rtx basemem, HOST_WIDE_INT *offsetp)
++{
++ return arm_gen_multiple_op (TRUE, regs, count, basereg, write_back, basemem,
++ offsetp);
++}
++
++rtx
++arm_gen_store_multiple (int *regs, int count, rtx basereg, int write_back,
++ rtx basemem, HOST_WIDE_INT *offsetp)
++{
++ return arm_gen_multiple_op (FALSE, regs, count, basereg, write_back, basemem,
++ offsetp);
++}
++
++/* Called from a peephole2 expander to turn a sequence of loads into an
++ LDM instruction. OPERANDS are the operands found by the peephole matcher;
++ NOPS indicates how many separate loads we are trying to combine. SORT_REGS
++ is true if we can reorder the registers because they are used commutatively
++ subsequently.
++ Returns true iff we could generate a new instruction. */
++
++bool
++gen_ldm_seq (rtx *operands, int nops, bool sort_regs)
++{
++ int regs[MAX_LDM_STM_OPS], mem_order[MAX_LDM_STM_OPS];
++ rtx mems[MAX_LDM_STM_OPS];
++ int i, j, base_reg;
++ rtx base_reg_rtx;
++ HOST_WIDE_INT offset;
++ int write_back = FALSE;
++ int ldm_case;
++ rtx addr;
++
++ ldm_case = load_multiple_sequence (operands, nops, regs, mem_order,
++ &base_reg, &offset, !sort_regs);
++
++ if (ldm_case == 0)
++ return false;
++
++ if (sort_regs)
++ for (i = 0; i < nops - 1; i++)
++ for (j = i + 1; j < nops; j++)
++ if (regs[i] > regs[j])
++ {
++ int t = regs[i];
++ regs[i] = regs[j];
++ regs[j] = t;
++ }
++ base_reg_rtx = gen_rtx_REG (Pmode, base_reg);
++
++ if (TARGET_THUMB1)
++ {
++ gcc_assert (peep2_reg_dead_p (nops, base_reg_rtx));
++ gcc_assert (ldm_case == 1 || ldm_case == 5);
++ write_back = TRUE;
++ }
++
++ if (ldm_case == 5)
++ {
++ rtx newbase = TARGET_THUMB1 ? base_reg_rtx : gen_rtx_REG (SImode, regs[0]);
++ emit_insn (gen_addsi3 (newbase, base_reg_rtx, GEN_INT (offset)));
++ offset = 0;
++ if (!TARGET_THUMB1)
++ {
++ base_reg = regs[0];
++ base_reg_rtx = newbase;
++ }
++ }
++
++ for (i = 0; i < nops; i++)
++ {
++ addr = plus_constant (base_reg_rtx, offset + i * 4);
++ mems[i] = adjust_automodify_address_nv (operands[nops + mem_order[i]],
++ SImode, addr, 0);
++ }
++ emit_insn (arm_gen_load_multiple_1 (nops, regs, mems, base_reg_rtx,
++ write_back ? offset + i * 4 : 0));
++ return true;
++}
++
++/* Called from a peephole2 expander to turn a sequence of stores into an
++ STM instruction. OPERANDS are the operands found by the peephole matcher;
++ NOPS indicates how many separate stores we are trying to combine.
++ Returns true iff we could generate a new instruction. */
++
++bool
++gen_stm_seq (rtx *operands, int nops)
++{
++ int i;
++ int regs[MAX_LDM_STM_OPS], mem_order[MAX_LDM_STM_OPS];
++ rtx mems[MAX_LDM_STM_OPS];
++ int base_reg;
++ rtx base_reg_rtx;
++ HOST_WIDE_INT offset;
++ int write_back = FALSE;
++ int stm_case;
++ rtx addr;
++ bool base_reg_dies;
++
++ stm_case = store_multiple_sequence (operands, nops, nops, regs, NULL,
++ mem_order, &base_reg, &offset, true);
++
++ if (stm_case == 0)
++ return false;
++
++ base_reg_rtx = gen_rtx_REG (Pmode, base_reg);
++
++ base_reg_dies = peep2_reg_dead_p (nops, base_reg_rtx);
++ if (TARGET_THUMB1)
++ {
++ gcc_assert (base_reg_dies);
++ write_back = TRUE;
++ }
++
++ if (stm_case == 5)
++ {
++ gcc_assert (base_reg_dies);
++ emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, GEN_INT (offset)));
++ offset = 0;
++ }
++
++ addr = plus_constant (base_reg_rtx, offset);
++
++ for (i = 0; i < nops; i++)
++ {
++ addr = plus_constant (base_reg_rtx, offset + i * 4);
++ mems[i] = adjust_automodify_address_nv (operands[nops + mem_order[i]],
++ SImode, addr, 0);
++ }
++ emit_insn (arm_gen_store_multiple_1 (nops, regs, mems, base_reg_rtx,
++ write_back ? offset + i * 4 : 0));
++ return true;
++}
++
++/* Called from a peephole2 expander to turn a sequence of stores that are
++ preceded by constant loads into an STM instruction. OPERANDS are the
++ operands found by the peephole matcher; NOPS indicates how many
++ separate stores we are trying to combine; there are 2 * NOPS
++ instructions in the peephole.
++ Returns true iff we could generate a new instruction. */
++
++bool
++gen_const_stm_seq (rtx *operands, int nops)
++{
++ int regs[MAX_LDM_STM_OPS], sorted_regs[MAX_LDM_STM_OPS];
++ int reg_order[MAX_LDM_STM_OPS], mem_order[MAX_LDM_STM_OPS];
++ rtx reg_rtxs[MAX_LDM_STM_OPS], orig_reg_rtxs[MAX_LDM_STM_OPS];
++ rtx mems[MAX_LDM_STM_OPS];
++ int base_reg;
++ rtx base_reg_rtx;
++ HOST_WIDE_INT offset;
++ int write_back = FALSE;
++ int stm_case;
++ rtx addr;
++ bool base_reg_dies;
++ int i, j;
++ HARD_REG_SET allocated;
++
++ stm_case = store_multiple_sequence (operands, nops, 2 * nops, regs, reg_rtxs,
++ mem_order, &base_reg, &offset, false);
++
++ if (stm_case == 0)
++ return false;
++
++ memcpy (orig_reg_rtxs, reg_rtxs, sizeof orig_reg_rtxs);
++
++ /* If the same register is used more than once, try to find a free
++ register. */
++ CLEAR_HARD_REG_SET (allocated);
++ for (i = 0; i < nops; i++)
++ {
++ for (j = i + 1; j < nops; j++)
++ if (regs[i] == regs[j])
++ {
++ rtx t = peep2_find_free_register (0, nops * 2,
++ TARGET_THUMB1 ? "l" : "r",
++ SImode, &allocated);
++ if (t == NULL_RTX)
++ return false;
++ reg_rtxs[i] = t;
++ regs[i] = REGNO (t);
++ }
++ }
++
++ /* Compute an ordering that maps the register numbers to an ascending
++ sequence. */
++ reg_order[0] = 0;
++ for (i = 0; i < nops; i++)
++ if (regs[i] < regs[reg_order[0]])
++ reg_order[0] = i;
++
++ for (i = 1; i < nops; i++)
++ {
++ int this_order = reg_order[i - 1];
++ for (j = 0; j < nops; j++)
++ if (regs[j] > regs[reg_order[i - 1]]
++ && (this_order == reg_order[i - 1]
++ || regs[j] < regs[this_order]))
++ this_order = j;
++ reg_order[i] = this_order;
++ }
++
++ /* Ensure that registers that must be live after the instruction end
++ up with the correct value. */
++ for (i = 0; i < nops; i++)
++ {
++ int this_order = reg_order[i];
++ if ((this_order != mem_order[i]
++ || orig_reg_rtxs[this_order] != reg_rtxs[this_order])
++ && !peep2_reg_dead_p (nops * 2, orig_reg_rtxs[this_order]))
++ return false;
++ }
++
++ /* Load the constants. */
++ for (i = 0; i < nops; i++)
++ {
++ rtx op = operands[2 * nops + mem_order[i]];
++ sorted_regs[i] = regs[reg_order[i]];
++ emit_move_insn (reg_rtxs[reg_order[i]], op);
++ }
++
++ base_reg_rtx = gen_rtx_REG (Pmode, base_reg);
++
++ base_reg_dies = peep2_reg_dead_p (nops * 2, base_reg_rtx);
++ if (TARGET_THUMB1)
++ {
++ gcc_assert (base_reg_dies);
++ write_back = TRUE;
++ }
++
++ if (stm_case == 5)
++ {
++ gcc_assert (base_reg_dies);
++ emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, GEN_INT (offset)));
++ offset = 0;
++ }
++
++ addr = plus_constant (base_reg_rtx, offset);
++
++ for (i = 0; i < nops; i++)
++ {
++ addr = plus_constant (base_reg_rtx, offset + i * 4);
++ mems[i] = adjust_automodify_address_nv (operands[nops + mem_order[i]],
++ SImode, addr, 0);
++ }
++ emit_insn (arm_gen_store_multiple_1 (nops, sorted_regs, mems, base_reg_rtx,
++ write_back ? offset + i * 4 : 0));
++ return true;
+ }
+
+ int
+@@ -10280,20 +10535,21 @@
+ for (i = 0; in_words_to_go >= 2; i+=4)
+ {
+ if (in_words_to_go > 4)
+- emit_insn (arm_gen_load_multiple (0, 4, src, TRUE, TRUE,
+- srcbase, &srcoffset));
++ emit_insn (arm_gen_load_multiple (arm_regs_in_sequence, 4, src,
++ TRUE, srcbase, &srcoffset));
+ else
+- emit_insn (arm_gen_load_multiple (0, in_words_to_go, src, TRUE,
+- FALSE, srcbase, &srcoffset));
++ emit_insn (arm_gen_load_multiple (arm_regs_in_sequence, in_words_to_go,
++ src, FALSE, srcbase,
++ &srcoffset));
+
+ if (out_words_to_go)
+ {
+ if (out_words_to_go > 4)
+- emit_insn (arm_gen_store_multiple (0, 4, dst, TRUE, TRUE,
+- dstbase, &dstoffset));
++ emit_insn (arm_gen_store_multiple (arm_regs_in_sequence, 4, dst,
++ TRUE, dstbase, &dstoffset));
+ else if (out_words_to_go != 1)
+- emit_insn (arm_gen_store_multiple (0, out_words_to_go,
+- dst, TRUE,
++ emit_insn (arm_gen_store_multiple (arm_regs_in_sequence,
++ out_words_to_go, dst,
+ (last_bytes == 0
+ ? FALSE : TRUE),
+ dstbase, &dstoffset));
+
+=== modified file 'gcc/config/arm/arm.h'
+--- old/gcc/config/arm/arm.h 2011-01-05 12:12:18 +0000
++++ new/gcc/config/arm/arm.h 2011-01-05 18:20:37 +0000
+@@ -1143,6 +1143,9 @@
+ ((MODE) == TImode || (MODE) == EImode || (MODE) == OImode \
+ || (MODE) == CImode || (MODE) == XImode)
+
++/* The register numbers in sequence, for passing to arm_gen_load_multiple. */
++extern int arm_regs_in_sequence[];
++
+ /* The order in which register should be allocated. It is good to use ip
+ since no saving is required (though calls clobber it) and it never contains
+ function parameters. It is quite good to use lr since other calls may
+@@ -2823,4 +2826,8 @@
+ #define NEED_INDICATE_EXEC_STACK 0
+ #endif
+
++/* The maximum number of parallel loads or stores we support in an ldm/stm
++ instruction. */
++#define MAX_LDM_STM_OPS 4
++
+ #endif /* ! GCC_ARM_H */
+
+=== modified file 'gcc/config/arm/arm.md'
+--- old/gcc/config/arm/arm.md 2011-01-05 12:12:18 +0000
++++ new/gcc/config/arm/arm.md 2011-01-05 18:20:37 +0000
+@@ -6282,7 +6282,7 @@
+
+ ;; load- and store-multiple insns
+ ;; The arm can load/store any set of registers, provided that they are in
+-;; ascending order; but that is beyond GCC so stick with what it knows.
++;; ascending order, but these expanders assume a contiguous set.
+
+ (define_expand "load_multiple"
+ [(match_par_dup 3 [(set (match_operand:SI 0 "" "")
+@@ -6303,126 +6303,12 @@
+ FAIL;
+
+ operands[3]
+- = arm_gen_load_multiple (REGNO (operands[0]), INTVAL (operands[2]),
++ = arm_gen_load_multiple (arm_regs_in_sequence + REGNO (operands[0]),
++ INTVAL (operands[2]),
+ force_reg (SImode, XEXP (operands[1], 0)),
+- TRUE, FALSE, operands[1], &offset);
++ FALSE, operands[1], &offset);
+ })
+
+-;; Load multiple with write-back
+-
+-(define_insn "*ldmsi_postinc4"
+- [(match_parallel 0 "load_multiple_operation"
+- [(set (match_operand:SI 1 "s_register_operand" "=r")
+- (plus:SI (match_operand:SI 2 "s_register_operand" "1")
+- (const_int 16)))
+- (set (match_operand:SI 3 "arm_hard_register_operand" "")
+- (mem:SI (match_dup 2)))
+- (set (match_operand:SI 4 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 2) (const_int 4))))
+- (set (match_operand:SI 5 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 2) (const_int 8))))
+- (set (match_operand:SI 6 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 2) (const_int 12))))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 5"
+- "ldm%(ia%)\\t%1!, {%3, %4, %5, %6}"
+- [(set_attr "type" "load4")
+- (set_attr "predicable" "yes")]
+-)
+-
+-(define_insn "*ldmsi_postinc4_thumb1"
+- [(match_parallel 0 "load_multiple_operation"
+- [(set (match_operand:SI 1 "s_register_operand" "=l")
+- (plus:SI (match_operand:SI 2 "s_register_operand" "1")
+- (const_int 16)))
+- (set (match_operand:SI 3 "arm_hard_register_operand" "")
+- (mem:SI (match_dup 2)))
+- (set (match_operand:SI 4 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 2) (const_int 4))))
+- (set (match_operand:SI 5 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 2) (const_int 8))))
+- (set (match_operand:SI 6 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 2) (const_int 12))))])]
+- "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 5"
+- "ldmia\\t%1!, {%3, %4, %5, %6}"
+- [(set_attr "type" "load4")]
+-)
+-
+-(define_insn "*ldmsi_postinc3"
+- [(match_parallel 0 "load_multiple_operation"
+- [(set (match_operand:SI 1 "s_register_operand" "=r")
+- (plus:SI (match_operand:SI 2 "s_register_operand" "1")
+- (const_int 12)))
+- (set (match_operand:SI 3 "arm_hard_register_operand" "")
+- (mem:SI (match_dup 2)))
+- (set (match_operand:SI 4 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 2) (const_int 4))))
+- (set (match_operand:SI 5 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 2) (const_int 8))))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
+- "ldm%(ia%)\\t%1!, {%3, %4, %5}"
+- [(set_attr "type" "load3")
+- (set_attr "predicable" "yes")]
+-)
+-
+-(define_insn "*ldmsi_postinc2"
+- [(match_parallel 0 "load_multiple_operation"
+- [(set (match_operand:SI 1 "s_register_operand" "=r")
+- (plus:SI (match_operand:SI 2 "s_register_operand" "1")
+- (const_int 8)))
+- (set (match_operand:SI 3 "arm_hard_register_operand" "")
+- (mem:SI (match_dup 2)))
+- (set (match_operand:SI 4 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 2) (const_int 4))))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
+- "ldm%(ia%)\\t%1!, {%3, %4}"
+- [(set_attr "type" "load2")
+- (set_attr "predicable" "yes")]
+-)
+-
+-;; Ordinary load multiple
+-
+-(define_insn "*ldmsi4"
+- [(match_parallel 0 "load_multiple_operation"
+- [(set (match_operand:SI 2 "arm_hard_register_operand" "")
+- (mem:SI (match_operand:SI 1 "s_register_operand" "r")))
+- (set (match_operand:SI 3 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 1) (const_int 4))))
+- (set (match_operand:SI 4 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 1) (const_int 8))))
+- (set (match_operand:SI 5 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 1) (const_int 12))))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
+- "ldm%(ia%)\\t%1, {%2, %3, %4, %5}"
+- [(set_attr "type" "load4")
+- (set_attr "predicable" "yes")]
+-)
+-
+-(define_insn "*ldmsi3"
+- [(match_parallel 0 "load_multiple_operation"
+- [(set (match_operand:SI 2 "arm_hard_register_operand" "")
+- (mem:SI (match_operand:SI 1 "s_register_operand" "r")))
+- (set (match_operand:SI 3 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 1) (const_int 4))))
+- (set (match_operand:SI 4 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 1) (const_int 8))))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
+- "ldm%(ia%)\\t%1, {%2, %3, %4}"
+- [(set_attr "type" "load3")
+- (set_attr "predicable" "yes")]
+-)
+-
+-(define_insn "*ldmsi2"
+- [(match_parallel 0 "load_multiple_operation"
+- [(set (match_operand:SI 2 "arm_hard_register_operand" "")
+- (mem:SI (match_operand:SI 1 "s_register_operand" "r")))
+- (set (match_operand:SI 3 "arm_hard_register_operand" "")
+- (mem:SI (plus:SI (match_dup 1) (const_int 4))))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 2"
+- "ldm%(ia%)\\t%1, {%2, %3}"
+- [(set_attr "type" "load2")
+- (set_attr "predicable" "yes")]
+-)
+-
+ (define_expand "store_multiple"
+ [(match_par_dup 3 [(set (match_operand:SI 0 "" "")
+ (match_operand:SI 1 "" ""))
+@@ -6442,125 +6328,12 @@
+ FAIL;
+
+ operands[3]
+- = arm_gen_store_multiple (REGNO (operands[1]), INTVAL (operands[2]),
++ = arm_gen_store_multiple (arm_regs_in_sequence + REGNO (operands[1]),
++ INTVAL (operands[2]),
+ force_reg (SImode, XEXP (operands[0], 0)),
+- TRUE, FALSE, operands[0], &offset);
++ FALSE, operands[0], &offset);
+ })
+
+-;; Store multiple with write-back
+-
+-(define_insn "*stmsi_postinc4"
+- [(match_parallel 0 "store_multiple_operation"
+- [(set (match_operand:SI 1 "s_register_operand" "=r")
+- (plus:SI (match_operand:SI 2 "s_register_operand" "1")
+- (const_int 16)))
+- (set (mem:SI (match_dup 2))
+- (match_operand:SI 3 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
+- (match_operand:SI 4 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
+- (match_operand:SI 5 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 2) (const_int 12)))
+- (match_operand:SI 6 "arm_hard_register_operand" ""))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 5"
+- "stm%(ia%)\\t%1!, {%3, %4, %5, %6}"
+- [(set_attr "predicable" "yes")
+- (set_attr "type" "store4")]
+-)
+-
+-(define_insn "*stmsi_postinc4_thumb1"
+- [(match_parallel 0 "store_multiple_operation"
+- [(set (match_operand:SI 1 "s_register_operand" "=l")
+- (plus:SI (match_operand:SI 2 "s_register_operand" "1")
+- (const_int 16)))
+- (set (mem:SI (match_dup 2))
+- (match_operand:SI 3 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
+- (match_operand:SI 4 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
+- (match_operand:SI 5 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 2) (const_int 12)))
+- (match_operand:SI 6 "arm_hard_register_operand" ""))])]
+- "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 5"
+- "stmia\\t%1!, {%3, %4, %5, %6}"
+- [(set_attr "type" "store4")]
+-)
+-
+-(define_insn "*stmsi_postinc3"
+- [(match_parallel 0 "store_multiple_operation"
+- [(set (match_operand:SI 1 "s_register_operand" "=r")
+- (plus:SI (match_operand:SI 2 "s_register_operand" "1")
+- (const_int 12)))
+- (set (mem:SI (match_dup 2))
+- (match_operand:SI 3 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
+- (match_operand:SI 4 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
+- (match_operand:SI 5 "arm_hard_register_operand" ""))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
+- "stm%(ia%)\\t%1!, {%3, %4, %5}"
+- [(set_attr "predicable" "yes")
+- (set_attr "type" "store3")]
+-)
+-
+-(define_insn "*stmsi_postinc2"
+- [(match_parallel 0 "store_multiple_operation"
+- [(set (match_operand:SI 1 "s_register_operand" "=r")
+- (plus:SI (match_operand:SI 2 "s_register_operand" "1")
+- (const_int 8)))
+- (set (mem:SI (match_dup 2))
+- (match_operand:SI 3 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
+- (match_operand:SI 4 "arm_hard_register_operand" ""))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
+- "stm%(ia%)\\t%1!, {%3, %4}"
+- [(set_attr "predicable" "yes")
+- (set_attr "type" "store2")]
+-)
+-
+-;; Ordinary store multiple
+-
+-(define_insn "*stmsi4"
+- [(match_parallel 0 "store_multiple_operation"
+- [(set (mem:SI (match_operand:SI 1 "s_register_operand" "r"))
+- (match_operand:SI 2 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 1) (const_int 4)))
+- (match_operand:SI 3 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 1) (const_int 8)))
+- (match_operand:SI 4 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 1) (const_int 12)))
+- (match_operand:SI 5 "arm_hard_register_operand" ""))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
+- "stm%(ia%)\\t%1, {%2, %3, %4, %5}"
+- [(set_attr "predicable" "yes")
+- (set_attr "type" "store4")]
+-)
+-
+-(define_insn "*stmsi3"
+- [(match_parallel 0 "store_multiple_operation"
+- [(set (mem:SI (match_operand:SI 1 "s_register_operand" "r"))
+- (match_operand:SI 2 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 1) (const_int 4)))
+- (match_operand:SI 3 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 1) (const_int 8)))
+- (match_operand:SI 4 "arm_hard_register_operand" ""))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
+- "stm%(ia%)\\t%1, {%2, %3, %4}"
+- [(set_attr "predicable" "yes")
+- (set_attr "type" "store3")]
+-)
+-
+-(define_insn "*stmsi2"
+- [(match_parallel 0 "store_multiple_operation"
+- [(set (mem:SI (match_operand:SI 1 "s_register_operand" "r"))
+- (match_operand:SI 2 "arm_hard_register_operand" ""))
+- (set (mem:SI (plus:SI (match_dup 1) (const_int 4)))
+- (match_operand:SI 3 "arm_hard_register_operand" ""))])]
+- "TARGET_32BIT && XVECLEN (operands[0], 0) == 2"
+- "stm%(ia%)\\t%1, {%2, %3}"
+- [(set_attr "predicable" "yes")
+- (set_attr "type" "store2")]
+-)
+
+ ;; Move a block of memory if it is word aligned and MORE than 2 words long.
+ ;; We could let this apply for blocks of less than this, but it clobbers so
+@@ -9031,8 +8804,8 @@
+ if (REGNO (reg) == R0_REGNUM)
+ {
+ /* On thumb we have to use a write-back instruction. */
+- emit_insn (arm_gen_store_multiple (R0_REGNUM, 4, addr, TRUE,
+- TARGET_THUMB ? TRUE : FALSE, mem, &offset));
++ emit_insn (arm_gen_store_multiple (arm_regs_in_sequence, 4, addr,
++ TARGET_THUMB ? TRUE : FALSE, mem, &offset));
+ size = TARGET_ARM ? 16 : 0;
+ }
+ else
+@@ -9078,8 +8851,8 @@
+ if (REGNO (reg) == R0_REGNUM)
+ {
+ /* On thumb we have to use a write-back instruction. */
+- emit_insn (arm_gen_load_multiple (R0_REGNUM, 4, addr, TRUE,
+- TARGET_THUMB ? TRUE : FALSE, mem, &offset));
++ emit_insn (arm_gen_load_multiple (arm_regs_in_sequence, 4, addr,
++ TARGET_THUMB ? TRUE : FALSE, mem, &offset));
+ size = TARGET_ARM ? 16 : 0;
+ }
+ else
+@@ -10672,87 +10445,6 @@
+ ""
+ )
+
+-; Peepholes to spot possible load- and store-multiples, if the ordering is
+-; reversed, check that the memory references aren't volatile.
+-
+-(define_peephole
+- [(set (match_operand:SI 0 "s_register_operand" "=rk")
+- (match_operand:SI 4 "memory_operand" "m"))
+- (set (match_operand:SI 1 "s_register_operand" "=rk")
+- (match_operand:SI 5 "memory_operand" "m"))
+- (set (match_operand:SI 2 "s_register_operand" "=rk")
+- (match_operand:SI 6 "memory_operand" "m"))
+- (set (match_operand:SI 3 "s_register_operand" "=rk")
+- (match_operand:SI 7 "memory_operand" "m"))]
+- "TARGET_ARM && load_multiple_sequence (operands, 4, NULL, NULL, NULL)"
+- "*
+- return emit_ldm_seq (operands, 4);
+- "
+-)
+-
+-(define_peephole
+- [(set (match_operand:SI 0 "s_register_operand" "=rk")
+- (match_operand:SI 3 "memory_operand" "m"))
+- (set (match_operand:SI 1 "s_register_operand" "=rk")
+- (match_operand:SI 4 "memory_operand" "m"))
+- (set (match_operand:SI 2 "s_register_operand" "=rk")
+- (match_operand:SI 5 "memory_operand" "m"))]
+- "TARGET_ARM && load_multiple_sequence (operands, 3, NULL, NULL, NULL)"
+- "*
+- return emit_ldm_seq (operands, 3);
+- "
+-)
+-
+-(define_peephole
+- [(set (match_operand:SI 0 "s_register_operand" "=rk")
+- (match_operand:SI 2 "memory_operand" "m"))
+- (set (match_operand:SI 1 "s_register_operand" "=rk")
+- (match_operand:SI 3 "memory_operand" "m"))]
+- "TARGET_ARM && load_multiple_sequence (operands, 2, NULL, NULL, NULL)"
+- "*
+- return emit_ldm_seq (operands, 2);
+- "
+-)
+-
+-(define_peephole
+- [(set (match_operand:SI 4 "memory_operand" "=m")
+- (match_operand:SI 0 "s_register_operand" "rk"))
+- (set (match_operand:SI 5 "memory_operand" "=m")
+- (match_operand:SI 1 "s_register_operand" "rk"))
+- (set (match_operand:SI 6 "memory_operand" "=m")
+- (match_operand:SI 2 "s_register_operand" "rk"))
+- (set (match_operand:SI 7 "memory_operand" "=m")
+- (match_operand:SI 3 "s_register_operand" "rk"))]
+- "TARGET_ARM && store_multiple_sequence (operands, 4, NULL, NULL, NULL)"
+- "*
+- return emit_stm_seq (operands, 4);
+- "
+-)
+-
+-(define_peephole
+- [(set (match_operand:SI 3 "memory_operand" "=m")
+- (match_operand:SI 0 "s_register_operand" "rk"))
+- (set (match_operand:SI 4 "memory_operand" "=m")
+- (match_operand:SI 1 "s_register_operand" "rk"))
+- (set (match_operand:SI 5 "memory_operand" "=m")
+- (match_operand:SI 2 "s_register_operand" "rk"))]
+- "TARGET_ARM && store_multiple_sequence (operands, 3, NULL, NULL, NULL)"
+- "*
+- return emit_stm_seq (operands, 3);
+- "
+-)
+-
+-(define_peephole
+- [(set (match_operand:SI 2 "memory_operand" "=m")
+- (match_operand:SI 0 "s_register_operand" "rk"))
+- (set (match_operand:SI 3 "memory_operand" "=m")
+- (match_operand:SI 1 "s_register_operand" "rk"))]
+- "TARGET_ARM && store_multiple_sequence (operands, 2, NULL, NULL, NULL)"
+- "*
+- return emit_stm_seq (operands, 2);
+- "
+-)
+-
+ (define_split
+ [(set (match_operand:SI 0 "s_register_operand" "")
+ (and:SI (ge:SI (match_operand:SI 1 "s_register_operand" "")
+@@ -11559,6 +11251,8 @@
+ "
+ )
+
++;; Load the load/store multiple patterns
++(include "ldmstm.md")
+ ;; Load the FPA co-processor patterns
+ (include "fpa.md")
+ ;; Load the Maverick co-processor patterns
+
+=== added file 'gcc/config/arm/ldmstm.md'
+--- old/gcc/config/arm/ldmstm.md 1970-01-01 00:00:00 +0000
++++ new/gcc/config/arm/ldmstm.md 2010-11-16 13:08:47 +0000
+@@ -0,0 +1,1191 @@
++/* ARM ldm/stm instruction patterns. This file was automatically generated
++ using arm-ldmstm.ml. Please do not edit manually.
++
++ Copyright (C) 2010 Free Software Foundation, Inc.
++ Contributed by CodeSourcery.
++
++ 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 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/>. */
++
++(define_insn "*ldm4_ia"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (match_operand:SI 1 "s_register_operand" "rk")))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 4))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 8))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 12))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
++ "ldm%(ia%)\t%1, {%2, %3, %4, %5}"
++ [(set_attr "type" "load4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*thumb_ldm4_ia"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (match_operand:SI 1 "s_register_operand" "l")))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 4))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 8))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 12))))])]
++ "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 4"
++ "ldm%(ia%)\t%1, {%2, %3, %4, %5}"
++ [(set_attr "type" "load4")])
++
++(define_insn "*ldm4_ia_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 16)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 2)))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 4))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 8))))
++ (set (match_operand:SI 6 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 12))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 5"
++ "ldm%(ia%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "load4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*thumb_ldm4_ia_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=l")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 16)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 2)))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 4))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 8))))
++ (set (match_operand:SI 6 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 12))))])]
++ "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 5"
++ "ldm%(ia%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "load4")])
++
++(define_insn "*stm4_ia"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (match_operand:SI 1 "s_register_operand" "rk"))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 4)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 8)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 12)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
++ "stm%(ia%)\t%1, {%2, %3, %4, %5}"
++ [(set_attr "type" "store4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm4_ia_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 16)))
++ (set (mem:SI (match_dup 2))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 12)))
++ (match_operand:SI 6 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 5"
++ "stm%(ia%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "store4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*thumb_stm4_ia_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=l")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 16)))
++ (set (mem:SI (match_dup 2))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 12)))
++ (match_operand:SI 6 "arm_hard_register_operand" ""))])]
++ "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 5"
++ "stm%(ia%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "store4")])
++
++(define_insn "*ldm4_ib"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
++ (const_int 4))))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 8))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 12))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 16))))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 4"
++ "ldm%(ib%)\t%1, {%2, %3, %4, %5}"
++ [(set_attr "type" "load4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm4_ib_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 16)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 4))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 8))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 12))))
++ (set (match_operand:SI 6 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 16))))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 5"
++ "ldm%(ib%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "load4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm4_ib"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") (const_int 4)))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 8)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 12)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 16)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 4"
++ "stm%(ib%)\t%1, {%2, %3, %4, %5}"
++ [(set_attr "type" "store4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm4_ib_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 16)))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 12)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 16)))
++ (match_operand:SI 6 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 5"
++ "stm%(ib%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "store4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm4_da"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
++ (const_int -12))))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int -8))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int -4))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 1)))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 4"
++ "ldm%(da%)\t%1, {%2, %3, %4, %5}"
++ [(set_attr "type" "load4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm4_da_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -16)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -12))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -8))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -4))))
++ (set (match_operand:SI 6 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 2)))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 5"
++ "ldm%(da%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "load4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm4_da"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") (const_int -12)))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int -8)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int -4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (match_dup 1))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 4"
++ "stm%(da%)\t%1, {%2, %3, %4, %5}"
++ [(set_attr "type" "store4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm4_da_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -16)))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -12)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -8)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -4)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))
++ (set (mem:SI (match_dup 2))
++ (match_operand:SI 6 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 5"
++ "stm%(da%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "store4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm4_db"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
++ (const_int -16))))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int -12))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int -8))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int -4))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
++ "ldm%(db%)\t%1, {%2, %3, %4, %5}"
++ [(set_attr "type" "load4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm4_db_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -16)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -16))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -12))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -8))))
++ (set (match_operand:SI 6 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -4))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 5"
++ "ldm%(db%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "load4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm4_db"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") (const_int -16)))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int -12)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int -8)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int -4)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
++ "stm%(db%)\t%1, {%2, %3, %4, %5}"
++ [(set_attr "type" "store4")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm4_db_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -16)))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -16)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -12)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -8)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -4)))
++ (match_operand:SI 6 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 5"
++ "stm%(db%)\t%1!, {%3, %4, %5, %6}"
++ [(set_attr "type" "store4")
++ (set_attr "predicable" "yes")])
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 4 "memory_operand" ""))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 5 "memory_operand" ""))
++ (set (match_operand:SI 2 "s_register_operand" "")
++ (match_operand:SI 6 "memory_operand" ""))
++ (set (match_operand:SI 3 "s_register_operand" "")
++ (match_operand:SI 7 "memory_operand" ""))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_ldm_seq (operands, 4, false))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 4 "memory_operand" ""))
++ (parallel
++ [(set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 5 "memory_operand" ""))
++ (set (match_operand:SI 2 "s_register_operand" "")
++ (match_operand:SI 6 "memory_operand" ""))
++ (set (match_operand:SI 3 "s_register_operand" "")
++ (match_operand:SI 7 "memory_operand" ""))])]
++ ""
++ [(const_int 0)]
++{
++ if (gen_ldm_seq (operands, 4, false))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 8 "const_int_operand" ""))
++ (set (match_operand:SI 4 "memory_operand" "")
++ (match_dup 0))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 9 "const_int_operand" ""))
++ (set (match_operand:SI 5 "memory_operand" "")
++ (match_dup 1))
++ (set (match_operand:SI 2 "s_register_operand" "")
++ (match_operand:SI 10 "const_int_operand" ""))
++ (set (match_operand:SI 6 "memory_operand" "")
++ (match_dup 2))
++ (set (match_operand:SI 3 "s_register_operand" "")
++ (match_operand:SI 11 "const_int_operand" ""))
++ (set (match_operand:SI 7 "memory_operand" "")
++ (match_dup 3))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_const_stm_seq (operands, 4))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 8 "const_int_operand" ""))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 9 "const_int_operand" ""))
++ (set (match_operand:SI 2 "s_register_operand" "")
++ (match_operand:SI 10 "const_int_operand" ""))
++ (set (match_operand:SI 3 "s_register_operand" "")
++ (match_operand:SI 11 "const_int_operand" ""))
++ (set (match_operand:SI 4 "memory_operand" "")
++ (match_dup 0))
++ (set (match_operand:SI 5 "memory_operand" "")
++ (match_dup 1))
++ (set (match_operand:SI 6 "memory_operand" "")
++ (match_dup 2))
++ (set (match_operand:SI 7 "memory_operand" "")
++ (match_dup 3))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_const_stm_seq (operands, 4))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 4 "memory_operand" "")
++ (match_operand:SI 0 "s_register_operand" ""))
++ (set (match_operand:SI 5 "memory_operand" "")
++ (match_operand:SI 1 "s_register_operand" ""))
++ (set (match_operand:SI 6 "memory_operand" "")
++ (match_operand:SI 2 "s_register_operand" ""))
++ (set (match_operand:SI 7 "memory_operand" "")
++ (match_operand:SI 3 "s_register_operand" ""))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_stm_seq (operands, 4))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_insn "*ldm3_ia"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (match_operand:SI 1 "s_register_operand" "rk")))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 4))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 8))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
++ "ldm%(ia%)\t%1, {%2, %3, %4}"
++ [(set_attr "type" "load3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*thumb_ldm3_ia"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (match_operand:SI 1 "s_register_operand" "l")))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 4))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 8))))])]
++ "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 3"
++ "ldm%(ia%)\t%1, {%2, %3, %4}"
++ [(set_attr "type" "load3")])
++
++(define_insn "*ldm3_ia_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 12)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 2)))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 4))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 8))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
++ "ldm%(ia%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "load3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*thumb_ldm3_ia_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=l")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 12)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 2)))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 4))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 8))))])]
++ "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 4"
++ "ldm%(ia%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "load3")])
++
++(define_insn "*stm3_ia"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (match_operand:SI 1 "s_register_operand" "rk"))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 4)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 8)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
++ "stm%(ia%)\t%1, {%2, %3, %4}"
++ [(set_attr "type" "store3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm3_ia_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 12)))
++ (set (mem:SI (match_dup 2))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
++ "stm%(ia%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "store3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*thumb_stm3_ia_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=l")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 12)))
++ (set (mem:SI (match_dup 2))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))])]
++ "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 4"
++ "stm%(ia%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "store3")])
++
++(define_insn "*ldm3_ib"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
++ (const_int 4))))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 8))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 12))))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 3"
++ "ldm%(ib%)\t%1, {%2, %3, %4}"
++ [(set_attr "type" "load3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm3_ib_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 12)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 4))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 8))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 12))))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 4"
++ "ldm%(ib%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "load3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm3_ib"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") (const_int 4)))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 8)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 12)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 3"
++ "stm%(ib%)\t%1, {%2, %3, %4}"
++ [(set_attr "type" "store3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm3_ib_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 12)))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 12)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 4"
++ "stm%(ib%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "store3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm3_da"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
++ (const_int -8))))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int -4))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 1)))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 3"
++ "ldm%(da%)\t%1, {%2, %3, %4}"
++ [(set_attr "type" "load3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm3_da_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -12)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -8))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -4))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 2)))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 4"
++ "ldm%(da%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "load3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm3_da"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") (const_int -8)))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int -4)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (match_dup 1))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 3"
++ "stm%(da%)\t%1, {%2, %3, %4}"
++ [(set_attr "type" "store3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm3_da_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -12)))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -8)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (match_dup 2))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 4"
++ "stm%(da%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "store3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm3_db"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
++ (const_int -12))))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int -8))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int -4))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
++ "ldm%(db%)\t%1, {%2, %3, %4}"
++ [(set_attr "type" "load3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm3_db_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -12)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -12))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -8))))
++ (set (match_operand:SI 5 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -4))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
++ "ldm%(db%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "load3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm3_db"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") (const_int -12)))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int -8)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int -4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
++ "stm%(db%)\t%1, {%2, %3, %4}"
++ [(set_attr "type" "store3")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm3_db_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -12)))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -12)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -8)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -4)))
++ (match_operand:SI 5 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 4"
++ "stm%(db%)\t%1!, {%3, %4, %5}"
++ [(set_attr "type" "store3")
++ (set_attr "predicable" "yes")])
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 3 "memory_operand" ""))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 4 "memory_operand" ""))
++ (set (match_operand:SI 2 "s_register_operand" "")
++ (match_operand:SI 5 "memory_operand" ""))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_ldm_seq (operands, 3, false))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 3 "memory_operand" ""))
++ (parallel
++ [(set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 4 "memory_operand" ""))
++ (set (match_operand:SI 2 "s_register_operand" "")
++ (match_operand:SI 5 "memory_operand" ""))])]
++ ""
++ [(const_int 0)]
++{
++ if (gen_ldm_seq (operands, 3, false))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 6 "const_int_operand" ""))
++ (set (match_operand:SI 3 "memory_operand" "")
++ (match_dup 0))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 7 "const_int_operand" ""))
++ (set (match_operand:SI 4 "memory_operand" "")
++ (match_dup 1))
++ (set (match_operand:SI 2 "s_register_operand" "")
++ (match_operand:SI 8 "const_int_operand" ""))
++ (set (match_operand:SI 5 "memory_operand" "")
++ (match_dup 2))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_const_stm_seq (operands, 3))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 6 "const_int_operand" ""))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 7 "const_int_operand" ""))
++ (set (match_operand:SI 2 "s_register_operand" "")
++ (match_operand:SI 8 "const_int_operand" ""))
++ (set (match_operand:SI 3 "memory_operand" "")
++ (match_dup 0))
++ (set (match_operand:SI 4 "memory_operand" "")
++ (match_dup 1))
++ (set (match_operand:SI 5 "memory_operand" "")
++ (match_dup 2))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_const_stm_seq (operands, 3))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 3 "memory_operand" "")
++ (match_operand:SI 0 "s_register_operand" ""))
++ (set (match_operand:SI 4 "memory_operand" "")
++ (match_operand:SI 1 "s_register_operand" ""))
++ (set (match_operand:SI 5 "memory_operand" "")
++ (match_operand:SI 2 "s_register_operand" ""))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_stm_seq (operands, 3))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_insn "*ldm2_ia"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (match_operand:SI 1 "s_register_operand" "rk")))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 4))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 2"
++ "ldm%(ia%)\t%1, {%2, %3}"
++ [(set_attr "type" "load2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*thumb_ldm2_ia"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (match_operand:SI 1 "s_register_operand" "l")))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 4))))])]
++ "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 2"
++ "ldm%(ia%)\t%1, {%2, %3}"
++ [(set_attr "type" "load2")])
++
++(define_insn "*ldm2_ia_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 8)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 2)))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 4))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
++ "ldm%(ia%)\t%1!, {%3, %4}"
++ [(set_attr "type" "load2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*thumb_ldm2_ia_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=l")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 8)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 2)))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 4))))])]
++ "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 3"
++ "ldm%(ia%)\t%1!, {%3, %4}"
++ [(set_attr "type" "load2")])
++
++(define_insn "*stm2_ia"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (match_operand:SI 1 "s_register_operand" "rk"))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 4)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 2"
++ "stm%(ia%)\t%1, {%2, %3}"
++ [(set_attr "type" "store2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm2_ia_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 8)))
++ (set (mem:SI (match_dup 2))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
++ "stm%(ia%)\t%1!, {%3, %4}"
++ [(set_attr "type" "store2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*thumb_stm2_ia_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=l")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 8)))
++ (set (mem:SI (match_dup 2))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))])]
++ "TARGET_THUMB1 && XVECLEN (operands[0], 0) == 3"
++ "stm%(ia%)\t%1!, {%3, %4}"
++ [(set_attr "type" "store2")])
++
++(define_insn "*ldm2_ib"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
++ (const_int 4))))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int 8))))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 2"
++ "ldm%(ib%)\t%1, {%2, %3}"
++ [(set_attr "type" "load2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm2_ib_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 8)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 4))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int 8))))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 3"
++ "ldm%(ib%)\t%1!, {%3, %4}"
++ [(set_attr "type" "load2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm2_ib"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") (const_int 4)))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int 8)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 2"
++ "stm%(ib%)\t%1, {%2, %3}"
++ [(set_attr "type" "store2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm2_ib_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int 8)))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 4)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int 8)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 3"
++ "stm%(ib%)\t%1!, {%3, %4}"
++ [(set_attr "type" "store2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm2_da"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
++ (const_int -4))))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 1)))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 2"
++ "ldm%(da%)\t%1, {%2, %3}"
++ [(set_attr "type" "load2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm2_da_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -8)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -4))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (match_dup 2)))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 3"
++ "ldm%(da%)\t%1!, {%3, %4}"
++ [(set_attr "type" "load2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm2_da"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") (const_int -4)))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (match_dup 1))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 2"
++ "stm%(da%)\t%1, {%2, %3}"
++ [(set_attr "type" "store2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm2_da_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -8)))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -4)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (match_dup 2))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))])]
++ "TARGET_ARM && XVECLEN (operands[0], 0) == 3"
++ "stm%(da%)\t%1!, {%3, %4}"
++ [(set_attr "type" "store2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm2_db"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 2 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk")
++ (const_int -8))))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 1)
++ (const_int -4))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 2"
++ "ldm%(db%)\t%1, {%2, %3}"
++ [(set_attr "type" "load2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*ldm2_db_update"
++ [(match_parallel 0 "load_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -8)))
++ (set (match_operand:SI 3 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -8))))
++ (set (match_operand:SI 4 "arm_hard_register_operand" "")
++ (mem:SI (plus:SI (match_dup 2)
++ (const_int -4))))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
++ "ldm%(db%)\t%1!, {%3, %4}"
++ [(set_attr "type" "load2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm2_db"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (mem:SI (plus:SI (match_operand:SI 1 "s_register_operand" "rk") (const_int -8)))
++ (match_operand:SI 2 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 1) (const_int -4)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 2"
++ "stm%(db%)\t%1, {%2, %3}"
++ [(set_attr "type" "store2")
++ (set_attr "predicable" "yes")])
++
++(define_insn "*stm2_db_update"
++ [(match_parallel 0 "store_multiple_operation"
++ [(set (match_operand:SI 1 "s_register_operand" "=rk")
++ (plus:SI (match_operand:SI 2 "s_register_operand" "1") (const_int -8)))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -8)))
++ (match_operand:SI 3 "arm_hard_register_operand" ""))
++ (set (mem:SI (plus:SI (match_dup 2) (const_int -4)))
++ (match_operand:SI 4 "arm_hard_register_operand" ""))])]
++ "TARGET_32BIT && XVECLEN (operands[0], 0) == 3"
++ "stm%(db%)\t%1!, {%3, %4}"
++ [(set_attr "type" "store2")
++ (set_attr "predicable" "yes")])
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 2 "memory_operand" ""))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 3 "memory_operand" ""))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_ldm_seq (operands, 2, false))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 4 "const_int_operand" ""))
++ (set (match_operand:SI 2 "memory_operand" "")
++ (match_dup 0))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 5 "const_int_operand" ""))
++ (set (match_operand:SI 3 "memory_operand" "")
++ (match_dup 1))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_const_stm_seq (operands, 2))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 4 "const_int_operand" ""))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 5 "const_int_operand" ""))
++ (set (match_operand:SI 2 "memory_operand" "")
++ (match_dup 0))
++ (set (match_operand:SI 3 "memory_operand" "")
++ (match_dup 1))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_const_stm_seq (operands, 2))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 2 "memory_operand" "")
++ (match_operand:SI 0 "s_register_operand" ""))
++ (set (match_operand:SI 3 "memory_operand" "")
++ (match_operand:SI 1 "s_register_operand" ""))]
++ ""
++ [(const_int 0)]
++{
++ if (gen_stm_seq (operands, 2))
++ DONE;
++ else
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 2 "memory_operand" ""))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 3 "memory_operand" ""))
++ (parallel
++ [(set (match_operand:SI 4 "s_register_operand" "")
++ (match_operator:SI 5 "commutative_binary_operator"
++ [(match_operand:SI 6 "s_register_operand" "")
++ (match_operand:SI 7 "s_register_operand" "")]))
++ (clobber (reg:CC CC_REGNUM))])]
++ "(((operands[6] == operands[0] && operands[7] == operands[1])
++ || (operands[7] == operands[0] && operands[6] == operands[1]))
++ && peep2_reg_dead_p (3, operands[0]) && peep2_reg_dead_p (3, operands[1]))"
++ [(parallel
++ [(set (match_dup 4) (match_op_dup 5 [(match_dup 6) (match_dup 7)]))
++ (clobber (reg:CC CC_REGNUM))])]
++{
++ if (!gen_ldm_seq (operands, 2, true))
++ FAIL;
++})
++
++(define_peephole2
++ [(set (match_operand:SI 0 "s_register_operand" "")
++ (match_operand:SI 2 "memory_operand" ""))
++ (set (match_operand:SI 1 "s_register_operand" "")
++ (match_operand:SI 3 "memory_operand" ""))
++ (set (match_operand:SI 4 "s_register_operand" "")
++ (match_operator:SI 5 "commutative_binary_operator"
++ [(match_operand:SI 6 "s_register_operand" "")
++ (match_operand:SI 7 "s_register_operand" "")]))]
++ "(((operands[6] == operands[0] && operands[7] == operands[1])
++ || (operands[7] == operands[0] && operands[6] == operands[1]))
++ && peep2_reg_dead_p (3, operands[0]) && peep2_reg_dead_p (3, operands[1]))"
++ [(set (match_dup 4) (match_op_dup 5 [(match_dup 6) (match_dup 7)]))]
++{
++ if (!gen_ldm_seq (operands, 2, true))
++ FAIL;
++})
++
+
+=== modified file 'gcc/config/arm/predicates.md'
+--- old/gcc/config/arm/predicates.md 2010-11-04 10:45:05 +0000
++++ new/gcc/config/arm/predicates.md 2010-11-16 12:32:34 +0000
+@@ -211,6 +211,11 @@
+ (and (match_code "ior,xor,and")
+ (match_test "mode == GET_MODE (op)")))
+
++;; True for commutative operators
++(define_special_predicate "commutative_binary_operator"
++ (and (match_code "ior,xor,and,plus")
++ (match_test "mode == GET_MODE (op)")))
++
+ ;; True for shift operators.
+ (define_special_predicate "shift_operator"
+ (and (ior (ior (and (match_code "mult")
+@@ -334,16 +339,20 @@
+ (match_code "parallel")
+ {
+ HOST_WIDE_INT count = XVECLEN (op, 0);
+- int dest_regno;
++ unsigned dest_regno;
+ rtx src_addr;
+ HOST_WIDE_INT i = 1, base = 0;
++ HOST_WIDE_INT offset = 0;
+ rtx elt;
++ bool addr_reg_loaded = false;
++ bool update = false;
+
+ if (low_irq_latency)
+ return false;
+
+ if (count <= 1
+- || GET_CODE (XVECEXP (op, 0, 0)) != SET)
++ || GET_CODE (XVECEXP (op, 0, 0)) != SET
++ || !REG_P (SET_DEST (XVECEXP (op, 0, 0))))
+ return false;
+
+ /* Check to see if this might be a write-back. */
+@@ -351,6 +360,7 @@
+ {
+ i++;
+ base = 1;
++ update = true;
+
+ /* Now check it more carefully. */
+ if (GET_CODE (SET_DEST (elt)) != REG
+@@ -369,6 +379,15 @@
+
+ dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, i - 1)));
+ src_addr = XEXP (SET_SRC (XVECEXP (op, 0, i - 1)), 0);
++ if (GET_CODE (src_addr) == PLUS)
++ {
++ if (GET_CODE (XEXP (src_addr, 1)) != CONST_INT)
++ return false;
++ offset = INTVAL (XEXP (src_addr, 1));
++ src_addr = XEXP (src_addr, 0);
++ }
++ if (!REG_P (src_addr))
++ return false;
+
+ for (; i < count; i++)
+ {
+@@ -377,16 +396,28 @@
+ if (GET_CODE (elt) != SET
+ || GET_CODE (SET_DEST (elt)) != REG
+ || GET_MODE (SET_DEST (elt)) != SImode
+- || REGNO (SET_DEST (elt)) != (unsigned int)(dest_regno + i - base)
++ || REGNO (SET_DEST (elt)) <= dest_regno
+ || GET_CODE (SET_SRC (elt)) != MEM
+ || GET_MODE (SET_SRC (elt)) != SImode
+- || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
+- || !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
+- || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
+- || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != (i - base) * 4)
++ || ((GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
++ || !rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
++ || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
++ || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) != offset + (i - base) * 4)
++ && (!REG_P (XEXP (SET_SRC (elt), 0))
++ || offset + (i - base) * 4 != 0)))
+ return false;
++ dest_regno = REGNO (SET_DEST (elt));
++ if (dest_regno == REGNO (src_addr))
++ addr_reg_loaded = true;
+ }
+-
++ /* For Thumb, we only have updating instructions. If the pattern does
++ not describe an update, it must be because the address register is
++ in the list of loaded registers - on the hardware, this has the effect
++ of overriding the update. */
++ if (update && addr_reg_loaded)
++ return false;
++ if (TARGET_THUMB1)
++ return update || addr_reg_loaded;
+ return true;
+ })
+
+@@ -394,9 +425,9 @@
+ (match_code "parallel")
+ {
+ HOST_WIDE_INT count = XVECLEN (op, 0);
+- int src_regno;
++ unsigned src_regno;
+ rtx dest_addr;
+- HOST_WIDE_INT i = 1, base = 0;
++ HOST_WIDE_INT i = 1, base = 0, offset = 0;
+ rtx elt;
+
+ if (low_irq_latency)
+@@ -430,6 +461,16 @@
+ src_regno = REGNO (SET_SRC (XVECEXP (op, 0, i - 1)));
+ dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, i - 1)), 0);
+
++ if (GET_CODE (dest_addr) == PLUS)
++ {
++ if (GET_CODE (XEXP (dest_addr, 1)) != CONST_INT)
++ return false;
++ offset = INTVAL (XEXP (dest_addr, 1));
++ dest_addr = XEXP (dest_addr, 0);
++ }
++ if (!REG_P (dest_addr))
++ return false;
++
+ for (; i < count; i++)
+ {
+ elt = XVECEXP (op, 0, i);
+@@ -437,14 +478,17 @@
+ if (GET_CODE (elt) != SET
+ || GET_CODE (SET_SRC (elt)) != REG
+ || GET_MODE (SET_SRC (elt)) != SImode
+- || REGNO (SET_SRC (elt)) != (unsigned int)(src_regno + i - base)
++ || REGNO (SET_SRC (elt)) <= src_regno
+ || GET_CODE (SET_DEST (elt)) != MEM
+ || GET_MODE (SET_DEST (elt)) != SImode
+- || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
+- || !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
+- || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
+- || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != (i - base) * 4)
++ || ((GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
++ || !rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
++ || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
++ || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) != offset + (i - base) * 4)
++ && (!REG_P (XEXP (SET_DEST (elt), 0))
++ || offset + (i - base) * 4 != 0)))
+ return false;
++ src_regno = REGNO (SET_SRC (elt));
+ }
+
+ return true;
+
+=== modified file 'gcc/config/i386/i386.md'
+--- old/gcc/config/i386/i386.md 2011-01-05 12:12:18 +0000
++++ new/gcc/config/i386/i386.md 2011-01-05 18:20:37 +0000
+@@ -20023,15 +20023,14 @@
+ ;; leal (%edx,%eax,4), %eax
+
+ (define_peephole2
+- [(parallel [(set (match_operand 0 "register_operand" "")
++ [(match_scratch:SI 5 "r")
++ (parallel [(set (match_operand 0 "register_operand" "")
+ (ashift (match_operand 1 "register_operand" "")
+ (match_operand 2 "const_int_operand" "")))
+ (clobber (reg:CC FLAGS_REG))])
+- (set (match_operand 3 "register_operand")
+- (match_operand 4 "x86_64_general_operand" ""))
+- (parallel [(set (match_operand 5 "register_operand" "")
+- (plus (match_operand 6 "register_operand" "")
+- (match_operand 7 "register_operand" "")))
++ (parallel [(set (match_operand 3 "register_operand" "")
++ (plus (match_dup 0)
++ (match_operand 4 "x86_64_general_operand" "")))
+ (clobber (reg:CC FLAGS_REG))])]
+ "INTVAL (operands[2]) >= 0 && INTVAL (operands[2]) <= 3
+ /* Validate MODE for lea. */
+@@ -20041,30 +20040,21 @@
+ || GET_MODE (operands[0]) == SImode
+ || (TARGET_64BIT && GET_MODE (operands[0]) == DImode))
+ /* We reorder load and the shift. */
+- && !rtx_equal_p (operands[1], operands[3])
+- && !reg_overlap_mentioned_p (operands[0], operands[4])
+- /* Last PLUS must consist of operand 0 and 3. */
+- && !rtx_equal_p (operands[0], operands[3])
+- && (rtx_equal_p (operands[3], operands[6])
+- || rtx_equal_p (operands[3], operands[7]))
+- && (rtx_equal_p (operands[0], operands[6])
+- || rtx_equal_p (operands[0], operands[7]))
+- /* The intermediate operand 0 must die or be same as output. */
+- && (rtx_equal_p (operands[0], operands[5])
+- || peep2_reg_dead_p (3, operands[0]))"
+- [(set (match_dup 3) (match_dup 4))
++ && !reg_overlap_mentioned_p (operands[0], operands[4])"
++ [(set (match_dup 5) (match_dup 4))
+ (set (match_dup 0) (match_dup 1))]
+ {
+- enum machine_mode mode = GET_MODE (operands[5]) == DImode ? DImode : SImode;
++ enum machine_mode mode = GET_MODE (operands[1]) == DImode ? DImode : SImode;
+ int scale = 1 << INTVAL (operands[2]);
+ rtx index = gen_lowpart (Pmode, operands[1]);
+- rtx base = gen_lowpart (Pmode, operands[3]);
+- rtx dest = gen_lowpart (mode, operands[5]);
++ rtx base = gen_lowpart (Pmode, operands[5]);
++ rtx dest = gen_lowpart (mode, operands[3]);
+
+ operands[1] = gen_rtx_PLUS (Pmode, base,
+ gen_rtx_MULT (Pmode, index, GEN_INT (scale)));
+ if (mode != Pmode)
+ operands[1] = gen_rtx_SUBREG (mode, operands[1], 0);
++ operands[5] = base;
+ operands[0] = dest;
+ })
+
+
+=== modified file 'gcc/df-problems.c'
+--- old/gcc/df-problems.c 2010-11-16 22:17:17 +0000
++++ new/gcc/df-problems.c 2010-12-02 13:42:47 +0000
+@@ -3748,9 +3748,22 @@
+ for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++)
+ {
+ df_ref def = *def_rec;
+- /* If the def is to only part of the reg, it does
+- not kill the other defs that reach here. */
+- if (!(DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL)))
++ bitmap_set_bit (defs, DF_REF_REGNO (def));
++ }
++}
++
++/* Find the set of real DEFs, which are not clobbers, for INSN. */
++
++void
++df_simulate_find_noclobber_defs (rtx insn, bitmap defs)
++{
++ df_ref *def_rec;
++ unsigned int uid = INSN_UID (insn);
++
++ for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++)
++ {
++ df_ref def = *def_rec;
++ if (!(DF_REF_FLAGS (def) & (DF_REF_MUST_CLOBBER | DF_REF_MAY_CLOBBER)))
+ bitmap_set_bit (defs, DF_REF_REGNO (def));
+ }
+ }
+@@ -3921,7 +3934,7 @@
+ {
+ df_ref def = *def_rec;
+ if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
+- bitmap_clear_bit (live, DF_REF_REGNO (def));
++ bitmap_set_bit (live, DF_REF_REGNO (def));
+ }
+ }
+
+@@ -3942,7 +3955,7 @@
+ while here the scan is performed forwards! So, first assume that the
+ def is live, and if this is not true REG_UNUSED notes will rectify the
+ situation. */
+- df_simulate_find_defs (insn, live);
++ df_simulate_find_noclobber_defs (insn, live);
+
+ /* Clear all of the registers that go dead. */
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+
+=== modified file 'gcc/df.h'
+--- old/gcc/df.h 2010-01-29 12:14:47 +0000
++++ new/gcc/df.h 2010-12-02 13:42:47 +0000
+@@ -978,6 +978,7 @@
+ extern void df_md_add_problem (void);
+ extern void df_md_simulate_artificial_defs_at_top (basic_block, bitmap);
+ extern void df_md_simulate_one_insn (basic_block, rtx, bitmap);
++extern void df_simulate_find_noclobber_defs (rtx, bitmap);
+ extern void df_simulate_find_defs (rtx, bitmap);
+ extern void df_simulate_defs (rtx, bitmap);
+ extern void df_simulate_uses (rtx, bitmap);
+
+=== modified file 'gcc/fwprop.c'
+--- old/gcc/fwprop.c 2010-04-02 18:54:46 +0000
++++ new/gcc/fwprop.c 2010-11-16 12:32:34 +0000
+@@ -228,7 +228,10 @@
+
+ process_uses (df_get_artificial_uses (bb_index), DF_REF_AT_TOP);
+ process_defs (df_get_artificial_defs (bb_index), DF_REF_AT_TOP);
+- df_simulate_initialize_forwards (bb, local_lr);
++
++ /* We don't call df_simulate_initialize_forwards, as it may overestimate
++ the live registers if there are unused artificial defs. We prefer
++ liveness to be underestimated. */
+
+ FOR_BB_INSNS (bb, insn)
+ if (INSN_P (insn))
+
+=== modified file 'gcc/genoutput.c'
+--- old/gcc/genoutput.c 2009-04-08 14:00:34 +0000
++++ new/gcc/genoutput.c 2010-11-16 12:32:34 +0000
+@@ -266,6 +266,8 @@
+
+ printf (" %d,\n", d->strict_low);
+
++ printf (" %d,\n", d->constraint == NULL ? 1 : 0);
++
+ printf (" %d\n", d->eliminable);
+
+ printf(" },\n");
+
+=== modified file 'gcc/genrecog.c'
+--- old/gcc/genrecog.c 2009-06-22 09:29:13 +0000
++++ new/gcc/genrecog.c 2010-11-16 12:32:34 +0000
+@@ -1782,20 +1782,11 @@
+ int odepth = strlen (oldpos);
+ int ndepth = strlen (newpos);
+ int depth;
+- int old_has_insn, new_has_insn;
+
+ /* Pop up as many levels as necessary. */
+ for (depth = odepth; strncmp (oldpos, newpos, depth) != 0; --depth)
+ continue;
+
+- /* Hunt for the last [A-Z] in both strings. */
+- for (old_has_insn = odepth - 1; old_has_insn >= 0; --old_has_insn)
+- if (ISUPPER (oldpos[old_has_insn]))
+- break;
+- for (new_has_insn = ndepth - 1; new_has_insn >= 0; --new_has_insn)
+- if (ISUPPER (newpos[new_has_insn]))
+- break;
+-
+ /* Go down to desired level. */
+ while (depth < ndepth)
+ {
+
+=== modified file 'gcc/ifcvt.c'
+--- old/gcc/ifcvt.c 2011-01-05 12:12:18 +0000
++++ new/gcc/ifcvt.c 2011-01-05 18:20:37 +0000
+@@ -4011,6 +4011,7 @@
+ basic_block new_dest = dest_edge->dest;
+ rtx head, end, jump, earliest = NULL_RTX, old_dest;
+ bitmap merge_set = NULL;
++ bitmap merge_set_noclobber = NULL;
+ /* Number of pending changes. */
+ int n_validated_changes = 0;
+ rtx new_dest_label;
+@@ -4169,6 +4170,7 @@
+ end of the block. */
+
+ merge_set = BITMAP_ALLOC (&reg_obstack);
++ merge_set_noclobber = BITMAP_ALLOC (&reg_obstack);
+
+ /* If we allocated new pseudos (e.g. in the conditional move
+ expander called from noce_emit_cmove), we must resize the
+@@ -4187,6 +4189,7 @@
+ df_ref def = *def_rec;
+ bitmap_set_bit (merge_set, DF_REF_REGNO (def));
+ }
++ df_simulate_find_noclobber_defs (insn, merge_set_noclobber);
+ }
+ }
+
+@@ -4197,7 +4200,7 @@
+ unsigned i;
+ bitmap_iterator bi;
+
+- EXECUTE_IF_SET_IN_BITMAP (merge_set, 0, i, bi)
++ EXECUTE_IF_SET_IN_BITMAP (merge_set_noclobber, 0, i, bi)
+ {
+ if (i < FIRST_PSEUDO_REGISTER
+ && ! fixed_regs[i]
+@@ -4233,7 +4236,7 @@
+ TEST_SET & DF_LIVE_IN (merge_bb)
+ are empty. */
+
+- if (bitmap_intersect_p (merge_set, test_set)
++ if (bitmap_intersect_p (merge_set_noclobber, test_set)
+ || bitmap_intersect_p (merge_set, test_live)
+ || bitmap_intersect_p (test_set, df_get_live_in (merge_bb)))
+ intersect = true;
+@@ -4320,6 +4323,7 @@
+ remove_reg_equal_equiv_notes_for_regno (i);
+
+ BITMAP_FREE (merge_set);
++ BITMAP_FREE (merge_set_noclobber);
+ }
+
+ reorder_insns (head, end, PREV_INSN (earliest));
+@@ -4340,7 +4344,10 @@
+ cancel_changes (0);
+ fail:
+ if (merge_set)
+- BITMAP_FREE (merge_set);
++ {
++ BITMAP_FREE (merge_set);
++ BITMAP_FREE (merge_set_noclobber);
++ }
+ return FALSE;
+ }
+
+
+=== modified file 'gcc/recog.c'
+--- old/gcc/recog.c 2010-08-05 15:28:47 +0000
++++ new/gcc/recog.c 2010-11-16 12:32:34 +0000
+@@ -2082,6 +2082,7 @@
+ recog_data.operand_loc,
+ recog_data.constraints,
+ recog_data.operand_mode, NULL);
++ memset (recog_data.is_operator, 0, sizeof recog_data.is_operator);
+ if (noperands > 0)
+ {
+ const char *p = recog_data.constraints[0];
+@@ -2111,6 +2112,7 @@
+ for (i = 0; i < noperands; i++)
+ {
+ recog_data.constraints[i] = insn_data[icode].operand[i].constraint;
++ recog_data.is_operator[i] = insn_data[icode].operand[i].is_operator;
+ recog_data.operand_mode[i] = insn_data[icode].operand[i].mode;
+ /* VOIDmode match_operands gets mode from their real operand. */
+ if (recog_data.operand_mode[i] == VOIDmode)
+@@ -2909,6 +2911,10 @@
+
+ static struct peep2_insn_data peep2_insn_data[MAX_INSNS_PER_PEEP2 + 1];
+ static int peep2_current;
++
++static bool peep2_do_rebuild_jump_labels;
++static bool peep2_do_cleanup_cfg;
++
+ /* The number of instructions available to match a peep2. */
+ int peep2_current_count;
+
+@@ -2917,6 +2923,16 @@
+ DF_LIVE_OUT for the block. */
+ #define PEEP2_EOB pc_rtx
+
++/* Wrap N to fit into the peep2_insn_data buffer. */
++
++static int
++peep2_buf_position (int n)
++{
++ if (n >= MAX_INSNS_PER_PEEP2 + 1)
++ n -= MAX_INSNS_PER_PEEP2 + 1;
++ return n;
++}
++
+ /* Return the Nth non-note insn after `current', or return NULL_RTX if it
+ does not exist. Used by the recognizer to find the next insn to match
+ in a multi-insn pattern. */
+@@ -2926,9 +2942,7 @@
+ {
+ gcc_assert (n <= peep2_current_count);
+
+- n += peep2_current;
+- if (n >= MAX_INSNS_PER_PEEP2 + 1)
+- n -= MAX_INSNS_PER_PEEP2 + 1;
++ n = peep2_buf_position (peep2_current + n);
+
+ return peep2_insn_data[n].insn;
+ }
+@@ -2941,9 +2955,7 @@
+ {
+ gcc_assert (ofs < MAX_INSNS_PER_PEEP2 + 1);
+
+- ofs += peep2_current;
+- if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
+- ofs -= MAX_INSNS_PER_PEEP2 + 1;
++ ofs = peep2_buf_position (peep2_current + ofs);
+
+ gcc_assert (peep2_insn_data[ofs].insn != NULL_RTX);
+
+@@ -2959,9 +2971,7 @@
+
+ gcc_assert (ofs < MAX_INSNS_PER_PEEP2 + 1);
+
+- ofs += peep2_current;
+- if (ofs >= MAX_INSNS_PER_PEEP2 + 1)
+- ofs -= MAX_INSNS_PER_PEEP2 + 1;
++ ofs = peep2_buf_position (peep2_current + ofs);
+
+ gcc_assert (peep2_insn_data[ofs].insn != NULL_RTX);
+
+@@ -2996,12 +3006,8 @@
+ gcc_assert (from < MAX_INSNS_PER_PEEP2 + 1);
+ gcc_assert (to < MAX_INSNS_PER_PEEP2 + 1);
+
+- from += peep2_current;
+- if (from >= MAX_INSNS_PER_PEEP2 + 1)
+- from -= MAX_INSNS_PER_PEEP2 + 1;
+- to += peep2_current;
+- if (to >= MAX_INSNS_PER_PEEP2 + 1)
+- to -= MAX_INSNS_PER_PEEP2 + 1;
++ from = peep2_buf_position (peep2_current + from);
++ to = peep2_buf_position (peep2_current + to);
+
+ gcc_assert (peep2_insn_data[from].insn != NULL_RTX);
+ REG_SET_TO_HARD_REG_SET (live, peep2_insn_data[from].live_before);
+@@ -3010,8 +3016,7 @@
+ {
+ HARD_REG_SET this_live;
+
+- if (++from >= MAX_INSNS_PER_PEEP2 + 1)
+- from = 0;
++ from = peep2_buf_position (from + 1);
+ gcc_assert (peep2_insn_data[from].insn != NULL_RTX);
+ REG_SET_TO_HARD_REG_SET (this_live, peep2_insn_data[from].live_before);
+ IOR_HARD_REG_SET (live, this_live);
+@@ -3104,19 +3109,234 @@
+ COPY_REG_SET (peep2_insn_data[MAX_INSNS_PER_PEEP2].live_before, live);
+ }
+
++/* While scanning basic block BB, we found a match of length MATCH_LEN,
++ starting at INSN. Perform the replacement, removing the old insns and
++ replacing them with ATTEMPT. Returns the last insn emitted. */
++
++static rtx
++peep2_attempt (basic_block bb, rtx insn, int match_len, rtx attempt)
++{
++ int i;
++ rtx last, note, before_try, x;
++ bool was_call = false;
++
++ /* If we are splitting a CALL_INSN, look for the CALL_INSN
++ in SEQ and copy our CALL_INSN_FUNCTION_USAGE and other
++ cfg-related call notes. */
++ for (i = 0; i <= match_len; ++i)
++ {
++ int j;
++ rtx old_insn, new_insn, note;
++
++ j = peep2_buf_position (peep2_current + i);
++ old_insn = peep2_insn_data[j].insn;
++ if (!CALL_P (old_insn))
++ continue;
++ was_call = true;
++
++ new_insn = attempt;
++ while (new_insn != NULL_RTX)
++ {
++ if (CALL_P (new_insn))
++ break;
++ new_insn = NEXT_INSN (new_insn);
++ }
++
++ gcc_assert (new_insn != NULL_RTX);
++
++ CALL_INSN_FUNCTION_USAGE (new_insn)
++ = CALL_INSN_FUNCTION_USAGE (old_insn);
++
++ for (note = REG_NOTES (old_insn);
++ note;
++ note = XEXP (note, 1))
++ switch (REG_NOTE_KIND (note))
++ {
++ case REG_NORETURN:
++ case REG_SETJMP:
++ add_reg_note (new_insn, REG_NOTE_KIND (note),
++ XEXP (note, 0));
++ break;
++ default:
++ /* Discard all other reg notes. */
++ break;
++ }
++
++ /* Croak if there is another call in the sequence. */
++ while (++i <= match_len)
++ {
++ j = peep2_buf_position (peep2_current + i);
++ old_insn = peep2_insn_data[j].insn;
++ gcc_assert (!CALL_P (old_insn));
++ }
++ break;
++ }
++
++ i = peep2_buf_position (peep2_current + match_len);
++
++ note = find_reg_note (peep2_insn_data[i].insn, REG_EH_REGION, NULL_RTX);
++
++ /* Replace the old sequence with the new. */
++ last = emit_insn_after_setloc (attempt,
++ peep2_insn_data[i].insn,
++ INSN_LOCATOR (peep2_insn_data[i].insn));
++ before_try = PREV_INSN (insn);
++ delete_insn_chain (insn, peep2_insn_data[i].insn, false);
++
++ /* Re-insert the EH_REGION notes. */
++ if (note || (was_call && nonlocal_goto_handler_labels))
++ {
++ edge eh_edge;
++ edge_iterator ei;
++
++ FOR_EACH_EDGE (eh_edge, ei, bb->succs)
++ if (eh_edge->flags & (EDGE_EH | EDGE_ABNORMAL_CALL))
++ break;
++
++ if (note)
++ copy_reg_eh_region_note_backward (note, last, before_try);
++
++ if (eh_edge)
++ for (x = last; x != before_try; x = PREV_INSN (x))
++ if (x != BB_END (bb)
++ && (can_throw_internal (x)
++ || can_nonlocal_goto (x)))
++ {
++ edge nfte, nehe;
++ int flags;
++
++ nfte = split_block (bb, x);
++ flags = (eh_edge->flags
++ & (EDGE_EH | EDGE_ABNORMAL));
++ if (CALL_P (x))
++ flags |= EDGE_ABNORMAL_CALL;
++ nehe = make_edge (nfte->src, eh_edge->dest,
++ flags);
++
++ nehe->probability = eh_edge->probability;
++ nfte->probability
++ = REG_BR_PROB_BASE - nehe->probability;
++
++ peep2_do_cleanup_cfg |= purge_dead_edges (nfte->dest);
++ bb = nfte->src;
++ eh_edge = nehe;
++ }
++
++ /* Converting possibly trapping insn to non-trapping is
++ possible. Zap dummy outgoing edges. */
++ peep2_do_cleanup_cfg |= purge_dead_edges (bb);
++ }
++
++ /* If we generated a jump instruction, it won't have
++ JUMP_LABEL set. Recompute after we're done. */
++ for (x = last; x != before_try; x = PREV_INSN (x))
++ if (JUMP_P (x))
++ {
++ peep2_do_rebuild_jump_labels = true;
++ break;
++ }
++
++ return last;
++}
++
++/* After performing a replacement in basic block BB, fix up the life
++ information in our buffer. LAST is the last of the insns that we
++ emitted as a replacement. PREV is the insn before the start of
++ the replacement. MATCH_LEN is the number of instructions that were
++ matched, and which now need to be replaced in the buffer. */
++
++static void
++peep2_update_life (basic_block bb, int match_len, rtx last, rtx prev)
++{
++ int i = peep2_buf_position (peep2_current + match_len + 1);
++ rtx x;
++ regset_head live;
++
++ INIT_REG_SET (&live);
++ COPY_REG_SET (&live, peep2_insn_data[i].live_before);
++
++ gcc_assert (peep2_current_count >= match_len + 1);
++ peep2_current_count -= match_len + 1;
++
++ x = last;
++ do
++ {
++ if (INSN_P (x))
++ {
++ df_insn_rescan (x);
++ if (peep2_current_count < MAX_INSNS_PER_PEEP2)
++ {
++ peep2_current_count++;
++ if (--i < 0)
++ i = MAX_INSNS_PER_PEEP2;
++ peep2_insn_data[i].insn = x;
++ df_simulate_one_insn_backwards (bb, x, &live);
++ COPY_REG_SET (peep2_insn_data[i].live_before, &live);
++ }
++ }
++ x = PREV_INSN (x);
++ }
++ while (x != prev);
++ CLEAR_REG_SET (&live);
++
++ peep2_current = i;
++}
++
++/* Add INSN, which is in BB, at the end of the peep2 insn buffer if possible.
++ Return true if we added it, false otherwise. The caller will try to match
++ peepholes against the buffer if we return false; otherwise it will try to
++ add more instructions to the buffer. */
++
++static bool
++peep2_fill_buffer (basic_block bb, rtx insn, regset live)
++{
++ int pos;
++
++ /* Once we have filled the maximum number of insns the buffer can hold,
++ allow the caller to match the insns against peepholes. We wait until
++ the buffer is full in case the target has similar peepholes of different
++ length; we always want to match the longest if possible. */
++ if (peep2_current_count == MAX_INSNS_PER_PEEP2)
++ return false;
++
++ /* If an insn has RTX_FRAME_RELATED_P set, peephole substitution would lose
++ the REG_FRAME_RELATED_EXPR that is attached. */
++ if (RTX_FRAME_RELATED_P (insn))
++ {
++ /* Let the buffer drain first. */
++ if (peep2_current_count > 0)
++ return false;
++ /* Step over the insn then return true without adding the insn
++ to the buffer; this will cause us to process the next
++ insn. */
++ df_simulate_one_insn_forwards (bb, insn, live);
++ return true;
++ }
++
++ pos = peep2_buf_position (peep2_current + peep2_current_count);
++ peep2_insn_data[pos].insn = insn;
++ COPY_REG_SET (peep2_insn_data[pos].live_before, live);
++ peep2_current_count++;
++
++ df_simulate_one_insn_forwards (bb, insn, live);
++ return true;
++}
++
+ /* Perform the peephole2 optimization pass. */
+
+ static void
+ peephole2_optimize (void)
+ {
+- rtx insn, prev;
++ rtx insn;
+ bitmap live;
+ int i;
+ basic_block bb;
+- bool do_cleanup_cfg = false;
+- bool do_rebuild_jump_labels = false;
++
++ peep2_do_cleanup_cfg = false;
++ peep2_do_rebuild_jump_labels = false;
+
+ df_set_flags (DF_LR_RUN_DCE);
++ df_note_add_problem ();
+ df_analyze ();
+
+ /* Initialize the regsets we're going to use. */
+@@ -3126,214 +3346,59 @@
+
+ FOR_EACH_BB_REVERSE (bb)
+ {
++ bool past_end = false;
++ int pos;
++
+ rtl_profile_for_bb (bb);
+
+ /* Start up propagation. */
+- bitmap_copy (live, DF_LR_OUT (bb));
+- df_simulate_initialize_backwards (bb, live);
++ bitmap_copy (live, DF_LR_IN (bb));
++ df_simulate_initialize_forwards (bb, live);
+ peep2_reinit_state (live);
+
+- for (insn = BB_END (bb); ; insn = prev)
++ insn = BB_HEAD (bb);
++ for (;;)
+ {
+- prev = PREV_INSN (insn);
+- if (NONDEBUG_INSN_P (insn))
++ rtx attempt, head;
++ int match_len;
++
++ if (!past_end && !NONDEBUG_INSN_P (insn))
+ {
+- rtx attempt, before_try, x;
+- int match_len;
+- rtx note;
+- bool was_call = false;
+-
+- /* Record this insn. */
+- if (--peep2_current < 0)
+- peep2_current = MAX_INSNS_PER_PEEP2;
+- if (peep2_current_count < MAX_INSNS_PER_PEEP2
+- && peep2_insn_data[peep2_current].insn == NULL_RTX)
+- peep2_current_count++;
+- peep2_insn_data[peep2_current].insn = insn;
+- df_simulate_one_insn_backwards (bb, insn, live);
+- COPY_REG_SET (peep2_insn_data[peep2_current].live_before, live);
+-
+- if (RTX_FRAME_RELATED_P (insn))
+- {
+- /* If an insn has RTX_FRAME_RELATED_P set, peephole
+- substitution would lose the
+- REG_FRAME_RELATED_EXPR that is attached. */
+- peep2_reinit_state (live);
+- attempt = NULL;
+- }
+- else
+- /* Match the peephole. */
+- attempt = peephole2_insns (PATTERN (insn), insn, &match_len);
+-
+- if (attempt != NULL)
+- {
+- /* If we are splitting a CALL_INSN, look for the CALL_INSN
+- in SEQ and copy our CALL_INSN_FUNCTION_USAGE and other
+- cfg-related call notes. */
+- for (i = 0; i <= match_len; ++i)
+- {
+- int j;
+- rtx old_insn, new_insn, note;
+-
+- j = i + peep2_current;
+- if (j >= MAX_INSNS_PER_PEEP2 + 1)
+- j -= MAX_INSNS_PER_PEEP2 + 1;
+- old_insn = peep2_insn_data[j].insn;
+- if (!CALL_P (old_insn))
+- continue;
+- was_call = true;
+-
+- new_insn = attempt;
+- while (new_insn != NULL_RTX)
+- {
+- if (CALL_P (new_insn))
+- break;
+- new_insn = NEXT_INSN (new_insn);
+- }
+-
+- gcc_assert (new_insn != NULL_RTX);
+-
+- CALL_INSN_FUNCTION_USAGE (new_insn)
+- = CALL_INSN_FUNCTION_USAGE (old_insn);
+-
+- for (note = REG_NOTES (old_insn);
+- note;
+- note = XEXP (note, 1))
+- switch (REG_NOTE_KIND (note))
+- {
+- case REG_NORETURN:
+- case REG_SETJMP:
+- add_reg_note (new_insn, REG_NOTE_KIND (note),
+- XEXP (note, 0));
+- break;
+- default:
+- /* Discard all other reg notes. */
+- break;
+- }
+-
+- /* Croak if there is another call in the sequence. */
+- while (++i <= match_len)
+- {
+- j = i + peep2_current;
+- if (j >= MAX_INSNS_PER_PEEP2 + 1)
+- j -= MAX_INSNS_PER_PEEP2 + 1;
+- old_insn = peep2_insn_data[j].insn;
+- gcc_assert (!CALL_P (old_insn));
+- }
+- break;
+- }
+-
+- i = match_len + peep2_current;
+- if (i >= MAX_INSNS_PER_PEEP2 + 1)
+- i -= MAX_INSNS_PER_PEEP2 + 1;
+-
+- note = find_reg_note (peep2_insn_data[i].insn,
+- REG_EH_REGION, NULL_RTX);
+-
+- /* Replace the old sequence with the new. */
+- attempt = emit_insn_after_setloc (attempt,
+- peep2_insn_data[i].insn,
+- INSN_LOCATOR (peep2_insn_data[i].insn));
+- before_try = PREV_INSN (insn);
+- delete_insn_chain (insn, peep2_insn_data[i].insn, false);
+-
+- /* Re-insert the EH_REGION notes. */
+- if (note || (was_call && nonlocal_goto_handler_labels))
+- {
+- edge eh_edge;
+- edge_iterator ei;
+-
+- FOR_EACH_EDGE (eh_edge, ei, bb->succs)
+- if (eh_edge->flags & (EDGE_EH | EDGE_ABNORMAL_CALL))
+- break;
+-
+- if (note)
+- copy_reg_eh_region_note_backward (note, attempt,
+- before_try);
+-
+- if (eh_edge)
+- for (x = attempt ; x != before_try ; x = PREV_INSN (x))
+- if (x != BB_END (bb)
+- && (can_throw_internal (x)
+- || can_nonlocal_goto (x)))
+- {
+- edge nfte, nehe;
+- int flags;
+-
+- nfte = split_block (bb, x);
+- flags = (eh_edge->flags
+- & (EDGE_EH | EDGE_ABNORMAL));
+- if (CALL_P (x))
+- flags |= EDGE_ABNORMAL_CALL;
+- nehe = make_edge (nfte->src, eh_edge->dest,
+- flags);
+-
+- nehe->probability = eh_edge->probability;
+- nfte->probability
+- = REG_BR_PROB_BASE - nehe->probability;
+-
+- do_cleanup_cfg |= purge_dead_edges (nfte->dest);
+- bb = nfte->src;
+- eh_edge = nehe;
+- }
+-
+- /* Converting possibly trapping insn to non-trapping is
+- possible. Zap dummy outgoing edges. */
+- do_cleanup_cfg |= purge_dead_edges (bb);
+- }
+-
+- if (targetm.have_conditional_execution ())
+- {
+- for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i)
+- peep2_insn_data[i].insn = NULL_RTX;
+- peep2_insn_data[peep2_current].insn = PEEP2_EOB;
+- peep2_current_count = 0;
+- }
+- else
+- {
+- /* Back up lifetime information past the end of the
+- newly created sequence. */
+- if (++i >= MAX_INSNS_PER_PEEP2 + 1)
+- i = 0;
+- bitmap_copy (live, peep2_insn_data[i].live_before);
+-
+- /* Update life information for the new sequence. */
+- x = attempt;
+- do
+- {
+- if (INSN_P (x))
+- {
+- if (--i < 0)
+- i = MAX_INSNS_PER_PEEP2;
+- if (peep2_current_count < MAX_INSNS_PER_PEEP2
+- && peep2_insn_data[i].insn == NULL_RTX)
+- peep2_current_count++;
+- peep2_insn_data[i].insn = x;
+- df_insn_rescan (x);
+- df_simulate_one_insn_backwards (bb, x, live);
+- bitmap_copy (peep2_insn_data[i].live_before,
+- live);
+- }
+- x = PREV_INSN (x);
+- }
+- while (x != prev);
+-
+- peep2_current = i;
+- }
+-
+- /* If we generated a jump instruction, it won't have
+- JUMP_LABEL set. Recompute after we're done. */
+- for (x = attempt; x != before_try; x = PREV_INSN (x))
+- if (JUMP_P (x))
+- {
+- do_rebuild_jump_labels = true;
+- break;
+- }
+- }
++ next_insn:
++ insn = NEXT_INSN (insn);
++ if (insn == NEXT_INSN (BB_END (bb)))
++ past_end = true;
++ continue;
+ }
++ if (!past_end && peep2_fill_buffer (bb, insn, live))
++ goto next_insn;
+
+- if (insn == BB_HEAD (bb))
++ /* If we did not fill an empty buffer, it signals the end of the
++ block. */
++ if (peep2_current_count == 0)
+ break;
++
++ /* The buffer filled to the current maximum, so try to match. */
++
++ pos = peep2_buf_position (peep2_current + peep2_current_count);
++ peep2_insn_data[pos].insn = PEEP2_EOB;
++ COPY_REG_SET (peep2_insn_data[pos].live_before, live);
++
++ /* Match the peephole. */
++ head = peep2_insn_data[peep2_current].insn;
++ attempt = peephole2_insns (PATTERN (head), head, &match_len);
++ if (attempt != NULL)
++ {
++ rtx last;
++ last = peep2_attempt (bb, head, match_len, attempt);
++ peep2_update_life (bb, match_len, last, PREV_INSN (attempt));
++ }
++ else
++ {
++ /* If no match, advance the buffer by one insn. */
++ peep2_current = peep2_buf_position (peep2_current + 1);
++ peep2_current_count--;
++ }
+ }
+ }
+
+@@ -3341,7 +3406,7 @@
+ for (i = 0; i < MAX_INSNS_PER_PEEP2 + 1; ++i)
+ BITMAP_FREE (peep2_insn_data[i].live_before);
+ BITMAP_FREE (live);
+- if (do_rebuild_jump_labels)
++ if (peep2_do_rebuild_jump_labels)
+ rebuild_jump_labels (get_insns ());
+ }
+ #endif /* HAVE_peephole2 */
+
+=== modified file 'gcc/recog.h'
+--- old/gcc/recog.h 2009-10-26 21:55:59 +0000
++++ new/gcc/recog.h 2010-11-16 12:32:34 +0000
+@@ -194,6 +194,9 @@
+ /* Gives the constraint string for operand N. */
+ const char *constraints[MAX_RECOG_OPERANDS];
+
++ /* Nonzero if operand N is a match_operator or a match_parallel. */
++ char is_operator[MAX_RECOG_OPERANDS];
++
+ /* Gives the mode of operand N. */
+ enum machine_mode operand_mode[MAX_RECOG_OPERANDS];
+
+@@ -260,6 +263,8 @@
+
+ const char strict_low;
+
++ const char is_operator;
++
+ const char eliminable;
+ };
+
+
+=== modified file 'gcc/reload.c'
+--- old/gcc/reload.c 2009-12-21 16:32:44 +0000
++++ new/gcc/reload.c 2010-11-16 12:32:34 +0000
+@@ -3631,7 +3631,7 @@
+ || modified[j] != RELOAD_WRITE)
+ && j != i
+ /* Ignore things like match_operator operands. */
+- && *recog_data.constraints[j] != 0
++ && !recog_data.is_operator[j]
+ /* Don't count an input operand that is constrained to match
+ the early clobber operand. */
+ && ! (this_alternative_matches[j] == i
+