public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Committed: CRIS v32 support 2/2
@ 2007-12-15 21:45 Hans-Peter Nilsson
  0 siblings, 0 replies; only message in thread
From: Hans-Peter Nilsson @ 2007-12-15 21:45 UTC (permalink / raw)
  To: gcc-patches

I've committed this; updates to the CRIS port to support CRIS v32.

See previous post.  A fix for failures with the -mcc-init option
tagged along when exporting this from Axis repository, sorry.

gcc:
	Add CRIS v32 support.  Fix -mcc-init.
	* config.gcc: Make crisv32-* have cpu_type cris.  Handle
	crisv32-*-elf and crisv32-*-none like cris-*-elf and cris-*-none
	but without multilibs and with target_cpu_default=32.
	* crisv32-*-linux*: Handle as cris-*-linux*.  Set
	target_cpu_default to 32 and 10 accordingly.
	* config/cris/cris.c (ASSERT_PLT_UNSPEC): Remove unused macro.
	(cris_movem_load_rest_p, cris_store_multiple_op_p): Remove FIXME.
	Change regno_dir and regno only if !TARGET_V32.
	(cris_conditional_register_usage): If TARGET_V32, set
	reg_alloc_order as per REG_ALLOC_ORDER_V32 and make
	CRIS_ACR_REGNUM non-fixed.
	(cris_print_base): Add gcc_assert for post_inc on CRIS_ACR_REGNUM.
	(cris_print_operand) <case 'Z', case 'u'>: New cases.
	<case REG of case 'H'>: Allow for CRIS_SRP_REGNUM.
	(cris_reload_address_legitimized): Always return false for
	TARGET_V32.
	(cris_register_move_cost): New function, guts from
	REGISTER_MOVE_COST adjusted for CRIS v32.
	(cris_normal_notice_update_cc): New function split out from...
	(cris_notice_update_cc): Set cc_status.flags CC_REVERSED for
	TARGET_CCINIT.  Call cris_normal_notice_update_cc for CC_REV,
	CC_NOOV32 and CC_NORMAL, but set cc_status.flags CC_NO_OVERFLOW
	for CC_NOOV32 and TARGET_V32.
	(cris_simple_epilogue): Always return false for TARGET_V32 if
	cris_return_address_on_stack yields true.
	(cris_cc0_user_requires_cmp): New function.
	(cris_valid_pic_const): Add argument ANY_OPERAND.  All callers
	changed.  Handle CRIS_UNSPEC_PLT_PCREL and CRIS_UNSPEC_PCREL.
	(cris_asm_output_case_end): New function, guts from
	ASM_OUTPUT_CASE_END adjusted for CRIS v32.
	(cris_override_options): Adjust for CRIS v32.  Mask out
	TARGET_SIDE_EFFECT_PREFIXES and TARGET_MUL_BUG if v32.
	(cris_asm_output_mi_thunk, cris_expand_epilogue)
	(cris_gen_movem_load, cris_emit_movem_store)
	(cris_expand_pic_call_address, cris_asm_output_symbol_ref)
	(cris_asm_output_label_ref, cris_output_addr_const_extra): Adjust
	for CRIS v32.
	(cris_split_movdx): Copy re-used MEM.
	* config/cris/t-elfmulti: Add multilib v32 for -march=v32.
	* config/cris/predicates.md
	("cris_general_operand_or_pic_source"): New predicate.
	("cris_general_operand_or_plt_symbol"): Replace by...
	("cris_nonmemory_operand_or_callable_symbol"): New predicate.
	* config/cris/linux.h: Sanity-check TARGET_CPU_DEFAULT for
	presence and contents.
	(CRIS_SUBTARGET_DEFAULT_ARCH): New macro, MASK_AVOID_GOTPLT for
	v32, 0 otherwise.
	(CRIS_CPP_SUBTARGET_SPEC, CRIS_CC1_SUBTARGET_SPEC,
	CRIS_ASM_SUBTARGET_SPEC): Adjust for different
	TARGET_CPU_DEFAULT.
	(CRIS_SUBTARGET_DEFAULT): Add CRIS_SUBTARGET_DEFAULT_ARCH.
	* config/cris/cris.h: Sanity-check TARGET_CPU_DEFAULT for contents.
	(CRIS_DEFAULT_TUNE, CRIS_ARCH_CPP_DEFAULT)
	(CRIS_DEFAULT_ASM_ARCH_OPTION): New macros.
	(CRIS_CC1_SUBTARGET_SPEC): Change default tuning to use
	CRIS_DEFAULT_TUNE. 
	(CRIS_CPP_SUBTARGET_SPEC): Ditto.  Add CRIS_ARCH_CPP_DEFAULT.
	(ASM_SPEC): Add sanity-check erroring out when both -march= and
	-mcpu= are specified.  Pass on either as --march=v32.
	(CRIS_ASM_SUBTARGET_SPEC): When neither -march= or -mcpu= are
	specified, pass on CRIS_DEFAULT_ASM_ARCH_OPTION.
	(CRIS_CPU_V32): New macro.
	[!TARGET_CPU_DEFAULT]: Default-define as CRIS_CPU_BASE.
	[!TARGET_DEFAULT, TARGET_CPU_DEFAULT == 32]: Move default
	TARGET_DEFAULT definition after new TARGET_CPU_DEFAULT definition.
	Define v32-adjusted TARGET_DEFAULT.
	(CRIS_DEFAULT_CPU_VERSION): Change to TARGET_CPU_DEFAULT from
	CRIS_CPU_BASE.
	(TARGET_V32): New macro.
	(REG_ALLOC_ORDER_V32): New macro.
	(HARD_REGNO_MODE_OK): Do not allow larger-than-register-size modes
	into CRIS_ACR_REGNUM.
	(enum reg_class): New classes ACR_REGS, SPEC_ACR_REGS,
	GENNONACR_REGS and SPEC_GENNONACR_REGS.
	(REG_CLASS_NAMES, REG_CLASS_CONTENTS): Adjust for new classes.
	(REGNO_REG_CLASS): Give ACR_REGS for CRIS_ACR_REGNUM.
	(MODE_CODE_BASE_REG_CLASS): Define, give for OCODE POST_INC
	GENNONACR_REGS, BASE_REG_CLASS otherwise.
	(REG_CLASS_FROM_LETTER): 'a' is for ACR_REGS.
	(REGNO_MODE_CODE_OK_FOR_BASE_P): Define, refusing OCODE POST_INC
	for CRIS_ACR_REGNUM.
	(PREFERRED_RELOAD_CLASS): Keep ACR_REGS as preferred.
	(HARD_REGNO_RENAME_OK): Refuse CRIS_ACR_REGNUM as TO.
	(EXTRA_CONSTRAINT): New constraint 'U'.
	(TRAMPOLINE_TEMPLATE, TRAMPOLINE_SIZE, INITIALIZE_TRAMPOLINE)
	(ASM_OUTPUT_ADDR_DIFF_ELT): Adjust for CRIS v32.
	(BASE_OR_AUTOINCR_P): Refuse POST_INC for CRIS_ACR_REGNUM.
	(SIMPLE_ADDRESS_P): Remove.
	(GO_IF_LEGITIMATE_ADDRESS): Use BASE_OR_AUTOINCR_P, not redundant
	SIMPLE_ADDRESS_P.  Make one chained if-else, finishing as
	non-match after BASE_OR_AUTOINCR_P for TARGET_V32.
	(REGISTER_MOVE_COST): Just call the new function
	cris_register_move_cost.
	(enum cris_pic_symbol_type): Rename cris_gotrel_symbol to
	cris_rel_symbol.  All users changed.
	(REGISTER_NAMES): Replace "pc" with "acr".
	(ADDITIONAL_REGISTER_NAMES): Add "pc" for 15.
	(ASM_OUTPUT_REG_PUSH): Change to v32-compatible sequence.
	(ASM_OUTPUT_REG_POP): Change to v32-compatible syntax.
	(ASM_OUTPUT_CASE_END): Just call the new function
	cris_asm_output_case_end.
	* gcc/config/cris/cris.md: Group related constants together, with
	comments local.
	(CRIS_UNSPEC_PLT_GOTREL, CRIS_UNSPEC_PLT_PCREL, CRIS_UNSPEC_PCREL)
	(CRIS_UNSPEC_CASESI): New constants.
	(CRIS_UNSPEC_PLT): Remove constant.
	(CRIS_ACR_REGNUM): New constant.
	("slottable"): New attr alternatives "has_return_slot" and
	"has_call_slot".
	("cc"): New attr alternatives "noov32" and "rev".
	((eq_attr "slottable" "has_call_slot"))
	((eq_attr "slottable" "has_return_slot")): New define_delays.
	("movdi", "movsi"): Adjust operands for CRIS v32.
	("tstdi", "cmpdi", "adddi3", "subdi3", "uminsi3")
	("indirect_jump"): Ditto.  Make define_expand.
	("*tstdi_non_v32", "*tstdi_v32", "*tst<mode>_cmp")
	("*tst<mode>_non_cmp", "*cmpdi_non_v32", "*cmpdi_v32")
	("*movdi_v32", "*adddi3_non_v32", "*adddi3_v32")
	("*addsi3_non_v32", "*addsi3_v32", "*addhi3_non_v32")
	("*addhi3_v32", "*addqi3_non_v32", "*addqi3_v32")
	("*subdi3_non_v32", "*subdi3_v32", "*subsi3_non_v32")
	("*subsi3_v32", "*sub<mode>3_nonv32", "*sub<mode>3_v32")
	("*andqi3_non_v32", "*andqi3_v32", "*iorsi3_non_v32")
	("*iorsi3_v32", "*iorhi3_non_v32", "*iorhi3_v32")
	("*iorqi3_non_v32", "*iorqi3_v32", "*uminsi3_non_v32")
	("*uminsi3_v32", "*indirect_jump_non_v32", "*indirect_jump_v32")
	("*expanded_call_v32", "*expanded_call_value_v32"): New patterns,
	for the corresponding standard name.
	("tst<mode>"): Limit to BW and make define_expand.
	("tstsi"): Make separate insn, adjusting for CRIS v32.
	("*cmp_swapext<mode>"): Adjust for v32.  Specify "rev" for attr "cc".
	("cmpsi", "cmp<mode>"): Remove special cases for zero.  Specify
	attr "cc".
	("*btst"): Don't match for TARGET_CCINIT.  Replace test of
	register with compatible "cmpq 0".  Specify attr "cc".
	("*movdi_insn_non_v32"): New pattern, replacing "*movdi_insn" and
	define_split.
	(define_split for DI move): Match CRIS v32 only.
	("*movsi_got_load", "*movsi_internal", "*addi"): Adjust for CRIS
	v32.
	("load_multiple", "store_multiple", "*addsbw_v32", "*addubw_v32")
	("*adds<mode>_v32", "*addu<mode>_v32", "*bound<mode>_v32")
	("*casesi_jump_v32", "*expanded_andsi_v32", "*expanded_andhi_v32")
	("*extop<mode>si_v32", "*extopqihi_v32", "*andhi_lowpart_v32")
	("*andqi_lowpart_v32", "cris_casesi_v32"): New patterns. 
	("add<mode>3"): Make addsi3, addhi3 and addqi3 define_expand.
	("sub<mode>3"): Ditto subsi3, subhi3 and subqi3.
	("ior<mode>3"): Ditto iorsi3, iorhi3 and iorqi3.
	("*extopqihi_non_v32"): Replace "*extopqihi".
	("*extop<mode>si_non_v32"): Replace "*extop<mode>si".
	("*addxqihi_swap_non_v32"): Rename from "*extopqihi_swap", make
	non-v32 only.
	("*extop<mode>si_swap_non_v32"): Ditto "*extop<mode>si_swap".
	("*expanded_andsi_non_v32"): Ditto "*expanded_andsi".
	("*expanded_andhi_non_v32"): Ditto "*expanded_andhi".
	("*andhi_lowpart_non_v32"): Ditto "*andhi_lowpart".
	("*andqi_lowpart_non_v32"): Ditto "*andqi_lowpart".
	("*expanded_call_non_v32"): Ditto "*expanded_call".  Change from
	"cris_general_operand_or_plt_symbol" to "general_operand".
	("*expanded_call_value_non_v32") Ditto "*expanded_call_value".
	("*casesi_adds_w", "mstep_shift", "mstep_mul")
	("*expanded_call_side", "*expanded_call_value_side")
	(op-extend-split, op-extend-split-rx=rz, op-extend-split-swapped)
	(op-extend-split-swapped-rx=rz, op-extend, op-split-rx=rz)
	(op-split-swapped, op-split-swapped-rx=rz): Make non-v32 only.
	("dstep_mul", "xorsi3", "one_cmplsi2", "<shlr>si3")
	("*expanded_<shlr><mode>", "*<shlr><mode>_lowpart", "ashl<mode>3")
	("*ashl<mode>_lowpart", "abssi2", "clzsi2", "bswapsi2", "cris_swap_bits"): Specify "noov32" for
	attr "cc".
	("<su>mulsi3_highpart"): Ditto.  Correct operand 0 to
	register_operand.
	("andqi3"): Make define_expand.
	("*return_expanded"): For attr "slottable", change from "has_slot"
	to "has_return_slot".
	("cris_casesi_non_v32"): New pattern, old contents of "casesi".
	("casesi"): Divert into "cris_casesi_v32" and
	"cris_casesi_non_v32".
	(moversideqi, movemsideqi, mover2side): Require
	TARGET_SIDE_EFFECT_PREFIXES.
	(gotplt-to-plt, gotplt-to-plt-side): Change from CRIS_UNSPEC_PLT
	to CRIS_UNSPEC_PLT_GOTREL.
	* config/cris/cris-protos.h (cris_register_move_cost)
	(cris_cc0_user_requires_cmp, cris_asm_output_case_end): Declare.

Index: gcc/config.gcc
===================================================================
--- gcc/config.gcc	(revision 130951)
+++ gcc/config.gcc	(working copy)
@@ -265,6 +265,9 @@ arm*-*-*)
 bfin*-*)
 	cpu_type=bfin
 	;;
+crisv32-*)
+	cpu_type=cris
+	;;
 ep9312*-*-*)
 	cpu_type=arm
 	;;
@@ -844,17 +847,32 @@ cris-*-aout)
 	tmake_file="cris/t-cris cris/t-aout"
 	extra_options="${extra_options} cris/aout.opt"
 	;;
+crisv32-*-elf | crisv32-*-none)
+	tm_file="dbxelf.h elfos.h ${tm_file}"
+	tmake_file="cris/t-cris"
+	target_cpu_default=32
+	gas=yes
+	extra_options="${extra_options} cris/elf.opt"
+	;;
 cris-*-elf | cris-*-none)
 	tm_file="dbxelf.h elfos.h ${tm_file}"
 	tmake_file="cris/t-cris cris/t-elfmulti"
 	gas=yes
 	extra_options="${extra_options} cris/elf.opt"
 	;;
-cris-*-linux*)
+crisv32-*-linux* | cris-*-linux*)
 	tm_file="dbxelf.h elfos.h svr4.h ${tm_file} linux.h cris/linux.h"
 	# We need to avoid using t-linux, so override default tmake_file
 	tmake_file="cris/t-cris t-slibgcc-elf-ver cris/t-linux"
 	extra_options="${extra_options} cris/linux.opt"
+	case $target in
+	  cris-*-*)
+		target_cpu_default=10
+		;;
+	  crisv32-*-*)
+		target_cpu_default=32
+		;;
+	esac
 	;;
 crx-*-elf)
 	tm_file="elfos.h ${tm_file}"
Index: gcc/config/cris/cris.c
===================================================================
--- gcc/config/cris/cris.c	(revision 130951)
+++ gcc/config/cris/cris.c	(working copy)
@@ -52,11 +52,6 @@ along with GCC; see the file COPYING3.  
 #define ADDITIVE_SIZE_MODIFIER(size) \
  ((size) <= 63 ? "q" : (size) <= 255 ? "u.b" : (size) <= 65535 ? "u.w" : ".d")
 
-#define ASSERT_PLT_UNSPEC(x)						\
-  CRIS_ASSERT (XINT (x, 1) == CRIS_UNSPEC_PLT				\
-	       && ((GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF)		\
-		   || GET_CODE (XVECEXP (x, 0, 0)) == LABEL_REF))
-
 #define LOSE_AND_RETURN(msgid, x)			\
   do						\
     {						\
@@ -229,9 +224,11 @@ cris_movem_load_rest_p (rtx op, int offs
   else
     i = offs + 1;
 
-  /* FIXME: These two only for pre-v32.  */
-  regno_dir = -1;
-  regno = reg_count - 1;
+  if (!TARGET_V32)
+    {
+      regno_dir = -1;
+      regno = reg_count - 1;
+    }
 
   elt = XVECEXP (op, 0, offs);
   src_addr = XEXP (SET_SRC (elt), 0);
@@ -331,9 +328,11 @@ cris_store_multiple_op_p (rtx op)
   else
     i = 1;
 
-  /* FIXME: These two only for pre-v32.  */
-  regno_dir = -1;
-  regno = reg_count - 1;
+  if (!TARGET_V32)
+    {
+      regno_dir = -1;
+      regno = reg_count - 1;
+    }
 
   if (GET_CODE (elt) != SET
       || !REG_P (SET_SRC (elt))
@@ -390,6 +389,20 @@ cris_conditional_register_usage (void)
     fixed_regs[PIC_OFFSET_TABLE_REGNUM]
       = call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
 
+  /* Allow use of ACR (PC in pre-V32) and tweak order.  */
+  if (TARGET_V32)
+    {
+      static const int reg_alloc_order_v32[] = REG_ALLOC_ORDER_V32;
+      unsigned int i;
+
+      fixed_regs[CRIS_ACR_REGNUM] = 0;
+
+      for (i = 0;
+          i < sizeof (reg_alloc_order_v32)/sizeof (reg_alloc_order_v32[0]);
+          i++)
+       reg_alloc_order[i] = reg_alloc_order_v32[i];
+    }
+
   if (TARGET_HAS_MUL_INSNS)
     fixed_regs[CRIS_MOF_REGNUM] = 0;
 
@@ -551,7 +564,10 @@ cris_print_base (rtx base, FILE *file)
   if (REG_P (base))
     fprintf (file, "$%s", reg_names[REGNO (base)]);
   else if (GET_CODE (base) == POST_INC)
-    fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]);
+    {
+      gcc_assert (REGNO (XEXP (base, 0)) != CRIS_ACR_REGNUM);
+      fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]);
+    }
   else
     cris_operand_lossage ("unexpected base-type in cris_print_base",
 			  base);
@@ -781,6 +797,16 @@ cris_print_operand (FILE *file, rtx x, i
       putc (INTVAL (x) >= -128 && INTVAL (x) <= 255 ? 'b' : 'w', file);
       return;
 
+    case 'Z':
+      /* If this is a GOT-symbol, print the size-letter corresponding to
+	 -fpic/-fPIC.  For everything else, print "d".  */
+      putc ((flag_pic == 1
+	     && GET_CODE (x) == CONST
+	     && GET_CODE (XEXP (x, 0)) == UNSPEC
+	     && XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREAD)
+	    ? 'w' : 'd', file);
+      return;
+
     case '#':
       /* Output a 'nop' if there's nothing for the delay slot.
 	 This method stolen from the sparc files.  */
@@ -835,8 +861,12 @@ cris_print_operand (FILE *file, rtx x, i
 
 	case REG:
 	  /* Print reg + 1.  Check that there's not an attempt to print
-	     high-parts of registers like stack-pointer or higher.  */
-	  if (REGNO (operand) > STACK_POINTER_REGNUM - 2)
+	     high-parts of registers like stack-pointer or higher, except
+	     for SRP (where the "high part" is MOF).  */
+	  if (REGNO (operand) > STACK_POINTER_REGNUM - 2
+	      && (REGNO (operand) != CRIS_SRP_REGNUM
+		  || CRIS_SRP_REGNUM + 1 != CRIS_MOF_REGNUM
+		  || fixed_regs[CRIS_MOF_REGNUM] != 0))
 	    LOSE_AND_RETURN ("bad register", operand);
 	  fprintf (file, "$%s", reg_names[REGNO (operand) + 1]);
 	  return;
@@ -964,6 +994,17 @@ cris_print_operand (FILE *file, rtx x, i
       fprintf (file, "%s", mults[INTVAL (operand)]);
       return;
 
+    case 'u':
+      /* Print "u.w" if a GOT symbol and flag_pic == 1, else ".d".  */
+      if (flag_pic == 1
+	  && GET_CODE (operand) == CONST
+	  && GET_CODE (XEXP (operand, 0)) == UNSPEC
+	  && XINT (XEXP (operand, 0), 1) == CRIS_UNSPEC_GOTREAD)
+	fprintf (file, "u.w");
+      else
+	fprintf (file, ".d");
+      return;
+
     case 0:
       /* No code, print as usual.  */
       break;
@@ -1227,6 +1268,9 @@ cris_reload_address_legitimized (rtx x,
   if (GET_CODE (x) != PLUS)
     return false;
 
+  if (TARGET_V32)
+    return false;
+
   op0 = XEXP (x, 0);
   op0p = &XEXP (x, 0);
   op1 = XEXP (x, 1);
@@ -1284,6 +1328,291 @@ cris_reload_address_legitimized (rtx x,
   return false;
 }
 
+/* Worker function for REGISTER_MOVE_COST.  */
+
+int
+cris_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+			 enum reg_class from, enum reg_class to) 
+{
+  if (!TARGET_V32)
+    {
+      /* Pretend that classes that we don't support are ALL_REGS, so
+	 we give them the highest cost.  */
+      if (from != SPECIAL_REGS && from != MOF_REGS
+	  && from != GENERAL_REGS && from != GENNONACR_REGS)
+	from = ALL_REGS;
+
+      if (to != SPECIAL_REGS && to != MOF_REGS
+	  && to != GENERAL_REGS && to != GENNONACR_REGS)
+	to = ALL_REGS;
+    }
+
+  /* Can't move to and from a SPECIAL_REGS register, so we have to say
+     their move cost within that class is higher.  How about 7?  That's 3
+     for a move to a GENERAL_REGS register, 3 for the move from the
+     GENERAL_REGS register, and 1 for the increased register pressure.
+     Also, it's higher than the memory move cost, which is in order.  
+     We also do this for ALL_REGS, since we don't want that class to be
+     preferred (even to memory) at all where GENERAL_REGS doesn't fit.
+     Whenever it's about to be used, it's for SPECIAL_REGS.  If we don't
+     present a higher cost for ALL_REGS than memory, a SPECIAL_REGS may be
+     used when a GENERAL_REGS should be used, even if there are call-saved
+     GENERAL_REGS left to allocate.  This is because the fall-back when
+     the most preferred register class isn't available, isn't the next
+     (or next good) wider register class, but the *most widest* register
+     class.  */
+
+  if ((reg_classes_intersect_p (from, SPECIAL_REGS)
+       && reg_classes_intersect_p (to, SPECIAL_REGS))
+      || from == ALL_REGS || to == ALL_REGS)
+    return 7;
+
+  if (reg_classes_intersect_p (from, SPECIAL_REGS)
+      || reg_classes_intersect_p (to, SPECIAL_REGS))
+    return 3;
+
+  return 2;
+}
+
+/* Worker for cris_notice_update_cc; handles the "normal" cases.
+   FIXME: this code is historical; its functionality should be
+   refactored to look at insn attributes and moved to
+   cris_notice_update_cc.  Except, we better lose cc0 entirely.  */
+
+static void
+cris_normal_notice_update_cc (rtx exp, rtx insn)
+{
+  /* "Normal" means, for:
+     (set (cc0) (...)):
+     CC is (...).
+
+     (set (reg) (...)):
+     CC is (reg) and (...) - unless (...) is 0 or reg is a special
+        register or (v32 and (...) is -32..-1), then CC does not change.
+     CC_NO_OVERFLOW unless (...) is reg or mem.
+
+     (set (mem) (...)):
+     CC does not change.
+
+     (set (pc) (...)):
+     CC does not change.
+
+     (parallel
+      (set (reg1) (mem (bdap/biap)))
+      (set (reg2) (bdap/biap))):
+     CC is (reg1) and (mem (reg2))
+
+     (parallel
+      (set (mem (bdap/biap)) (reg1)) [or 0]
+      (set (reg2) (bdap/biap))):
+     CC does not change.
+
+     (where reg and mem includes strict_low_parts variants thereof)
+
+     For all others, assume CC is clobbered.
+     Note that we do not have to care about setting CC_NO_OVERFLOW,
+     since the overflow flag is set to 0 (i.e. right) for
+     instructions where it does not have any sane sense, but where
+     other flags have meanings.  (This includes shifts; the carry is
+     not set by them).
+
+     Note that there are other parallel constructs we could match,
+     but we don't do that yet.  */
+
+  if (GET_CODE (exp) == SET)
+    {
+      /* FIXME: Check when this happens.  It looks like we should
+	 actually do a CC_STATUS_INIT here to be safe.  */
+      if (SET_DEST (exp) == pc_rtx)
+	return;
+
+      /* Record CC0 changes, so we do not have to output multiple
+	 test insns.  */
+      if (SET_DEST (exp) == cc0_rtx)
+	{
+	  CC_STATUS_INIT;
+	  cc_status.value1 = SET_SRC (exp);
+
+	  /* Handle flags for the special btstq on one bit.  */
+	  if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT
+	      && XEXP (SET_SRC (exp), 1) == const1_rtx)
+	    {
+	      if (CONST_INT_P (XEXP (SET_SRC (exp), 0)))
+		/* Using cmpq.  */
+		cc_status.flags = CC_INVERTED;
+	      else
+		/* A one-bit btstq.  */
+		cc_status.flags = CC_Z_IN_NOT_N;
+	    }
+
+	  if (GET_CODE (SET_SRC (exp)) == COMPARE)
+	    {
+	      if (!REG_P (XEXP (SET_SRC (exp), 0))
+		  && XEXP (SET_SRC (exp), 1) != const0_rtx)
+		/* For some reason gcc will not canonicalize compare
+		   operations, reversing the sign by itself if
+		   operands are in wrong order.  */
+		/* (But NOT inverted; eq is still eq.) */
+		cc_status.flags = CC_REVERSED;
+
+	      /* This seems to be overlooked by gcc.  FIXME: Check again.
+		 FIXME:  Is it really safe?  */
+	      cc_status.value2
+		= gen_rtx_MINUS (GET_MODE (SET_SRC (exp)),
+				 XEXP (SET_SRC (exp), 0),
+				 XEXP (SET_SRC (exp), 1));
+	    }
+	  return;
+	}
+      else if (REG_P (SET_DEST (exp))
+	       || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
+		   && REG_P (XEXP (SET_DEST (exp), 0))))
+	{
+	  /* A register is set; normally CC is set to show that no
+	     test insn is needed.  Catch the exceptions.  */
+
+	  /* If not to cc0, then no "set"s in non-natural mode give
+	     ok cc0...  */
+	  if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD
+	      || GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT)
+	    {
+	      /* ... except add:s and sub:s in DImode.  */
+	      if (GET_MODE (SET_DEST (exp)) == DImode
+		  && (GET_CODE (SET_SRC (exp)) == PLUS
+		      || GET_CODE (SET_SRC (exp)) == MINUS))
+		{
+		  CC_STATUS_INIT;
+		  cc_status.value1 = SET_DEST (exp);
+		  cc_status.value2 = SET_SRC (exp);
+
+		  if (cris_reg_overlap_mentioned_p (cc_status.value1,
+						    cc_status.value2))
+		    cc_status.value2 = 0;
+
+		  /* Add and sub may set V, which gets us
+		     unoptimizable results in "gt" and "le" condition
+		     codes.  */
+		  cc_status.flags |= CC_NO_OVERFLOW;
+
+		  return;
+		}
+	    }
+	  else if (SET_SRC (exp) == const0_rtx
+		   || (REG_P (SET_SRC (exp))
+		       && (REGNO (SET_SRC (exp))
+			   > CRIS_LAST_GENERAL_REGISTER))
+		   || (TARGET_V32
+		       && GET_CODE (SET_SRC (exp)) == CONST_INT
+		       && CONST_OK_FOR_LETTER_P (INTVAL (SET_SRC (exp)),
+						 'I')))
+	    {
+	      /* There's no CC0 change for this case.  Just check
+		 for overlap.  */
+	      if (cc_status.value1
+		  && modified_in_p (cc_status.value1, insn))
+		cc_status.value1 = 0;
+
+	      if (cc_status.value2
+		  && modified_in_p (cc_status.value2, insn))
+		cc_status.value2 = 0;
+
+	      return;
+	    }
+	  else
+	    {
+	      CC_STATUS_INIT;
+	      cc_status.value1 = SET_DEST (exp);
+	      cc_status.value2 = SET_SRC (exp);
+
+	      if (cris_reg_overlap_mentioned_p (cc_status.value1,
+						cc_status.value2))
+		cc_status.value2 = 0;
+
+	      /* Some operations may set V, which gets us
+		 unoptimizable results in "gt" and "le" condition
+		 codes.  */
+	      if (GET_CODE (SET_SRC (exp)) == PLUS
+		  || GET_CODE (SET_SRC (exp)) == MINUS
+		  || GET_CODE (SET_SRC (exp)) == NEG)
+		cc_status.flags |= CC_NO_OVERFLOW;
+
+	      /* For V32, nothing with a register destination sets
+		 C and V usefully.  */
+	      if (TARGET_V32)
+		cc_status.flags |= CC_NO_OVERFLOW;
+
+	      return;
+	    }
+	}
+      else if (MEM_P (SET_DEST (exp))
+	       || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
+		   && MEM_P (XEXP (SET_DEST (exp), 0))))
+	{
+	  /* When SET to MEM, then CC is not changed (except for
+	     overlap).  */
+	  if (cc_status.value1
+	      && modified_in_p (cc_status.value1, insn))
+	    cc_status.value1 = 0;
+
+	  if (cc_status.value2
+	      && modified_in_p (cc_status.value2, insn))
+	    cc_status.value2 = 0;
+
+	  return;
+	}
+    }
+  else if (GET_CODE (exp) == PARALLEL)
+    {
+      if (GET_CODE (XVECEXP (exp, 0, 0)) == SET
+	  && GET_CODE (XVECEXP (exp, 0, 1)) == SET
+	  && REG_P (XEXP (XVECEXP (exp, 0, 1), 0)))
+	{
+	  if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0))
+	      && MEM_P (XEXP (XVECEXP (exp, 0, 0), 1)))
+	    {
+	      CC_STATUS_INIT;
+
+	      /* For "move.S [rx=ry+o],rz", say CC reflects
+		 value1=rz and value2=[rx] */
+	      cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0);
+	      cc_status.value2
+		= replace_equiv_address (XEXP (XVECEXP (exp, 0, 0), 1),
+					 XEXP (XVECEXP (exp, 0, 1), 0));
+
+	      /* Huh?  A side-effect cannot change the destination
+		 register.  */
+	      if (cris_reg_overlap_mentioned_p (cc_status.value1,
+						cc_status.value2))
+		internal_error ("internal error: sideeffect-insn affecting main effect");
+
+	      /* For V32, moves to registers don't set C and V.  */
+	      if (TARGET_V32)
+		cc_status.flags |= CC_NO_OVERFLOW;
+	      return;
+	    }
+	  else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1))
+		    || XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx)
+		   && MEM_P (XEXP (XVECEXP (exp, 0, 0), 0)))
+	    {
+	      /* For "move.S rz,[rx=ry+o]" and "clear.S [rx=ry+o]",
+		 say flags are not changed, except for overlap.  */
+	      if (cc_status.value1
+		  && modified_in_p (cc_status.value1, insn))
+		cc_status.value1 = 0;
+
+	      if (cc_status.value2
+		  && modified_in_p (cc_status.value2, insn))
+		cc_status.value2 = 0;
+
+	      return;
+	    }
+	}
+    }
+
+  /* If we got here, the case wasn't covered by the code above.  */
+  CC_STATUS_INIT;
+}
+
 /*  This function looks into the pattern to see how this insn affects
     condition codes.
 
@@ -1298,19 +1627,25 @@ cris_reload_address_legitimized (rtx x,
 void
 cris_notice_update_cc (rtx exp, rtx insn)
 {
-  /* Check if user specified "-mcc-init" as a bug-workaround.  FIXME:
-     TARGET_CCINIT does not work; we must set CC_REVERSED as below.
-     Several testcases will otherwise fail, for example
+  enum attr_cc attrval = get_attr_cc (insn);
+
+  /* Check if user specified "-mcc-init" as a bug-workaround.  Remember
+     to still set CC_REVERSED as below, since that's required by some
+     compare insn alternatives.  (FIXME: GCC should do this virtual
+     operand swap by itself.)  A test-case that may otherwise fail is
      gcc.c-torture/execute/20000217-1.c -O0 and -O1.  */
   if (TARGET_CCINIT)
     {
       CC_STATUS_INIT;
+
+      if (attrval == CC_REV)
+	cc_status.flags = CC_REVERSED;
       return;
     }
 
   /* Slowly, we're converting to using attributes to control the setting
      of condition-code status.  */
-  switch (get_attr_cc (insn))
+  switch (attrval)
     {
     case CC_NONE:
       /* Even if it is "none", a setting may clobber a previous
@@ -1329,220 +1664,20 @@ cris_notice_update_cc (rtx exp, rtx insn
 
     case CC_CLOBBER:
       CC_STATUS_INIT;
-      break;
+      return;
 
+    case CC_REV:
+    case CC_NOOV32:
     case CC_NORMAL:
-      /* Which means, for:
-	 (set (cc0) (...)):
-	 CC is (...).
-
-	 (set (reg) (...)):
-	 CC is (reg) and (...) - unless (...) is 0, then CC does not change.
-	 CC_NO_OVERFLOW unless (...) is reg or mem.
-
-	 (set (mem) (...)):
-	 CC does not change.
-
-	 (set (pc) (...)):
-	 CC does not change.
-
-	 (parallel
-	  (set (reg1) (mem (bdap/biap)))
-	  (set (reg2) (bdap/biap))):
-	 CC is (reg1) and (mem (reg2))
-
-	 (parallel
-	  (set (mem (bdap/biap)) (reg1)) [or 0]
-	  (set (reg2) (bdap/biap))):
-	 CC does not change.
-
-	 (where reg and mem includes strict_low_parts variants thereof)
-
-	 For all others, assume CC is clobbered.
-	 Note that we do not have to care about setting CC_NO_OVERFLOW,
-	 since the overflow flag is set to 0 (i.e. right) for
-	 instructions where it does not have any sane sense, but where
-	 other flags have meanings.  (This includes shifts; the carry is
-	 not set by them).
-
-	 Note that there are other parallel constructs we could match,
-	 but we don't do that yet.  */
-
-      if (GET_CODE (exp) == SET)
-	{
-	  /* FIXME: Check when this happens.  It looks like we should
-	     actually do a CC_STATUS_INIT here to be safe.  */
-	  if (SET_DEST (exp) == pc_rtx)
-	    return;
-
-	  /* Record CC0 changes, so we do not have to output multiple
-	     test insns.  */
-	  if (SET_DEST (exp) == cc0_rtx)
-	    {
-	      cc_status.value1 = SET_SRC (exp);
-	      cc_status.value2 = 0;
-
-	      /* Handle flags for the special btstq on one bit.  */
-	      if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT
-		  && XEXP (SET_SRC (exp), 1) == const1_rtx)
-		{
-		  if (CONST_INT_P (XEXP (SET_SRC (exp), 0)))
-		    /* Using cmpq.  */
-		    cc_status.flags = CC_INVERTED;
-		  else
-		    /* A one-bit btstq.  */
-		    cc_status.flags = CC_Z_IN_NOT_N;
-		}
-	      else
-		cc_status.flags = 0;
-
-	      if (GET_CODE (SET_SRC (exp)) == COMPARE)
-		{
-		  if (!REG_P (XEXP (SET_SRC (exp), 0))
-		      && XEXP (SET_SRC (exp), 1) != const0_rtx)
-		    /* For some reason gcc will not canonicalize compare
-		       operations, reversing the sign by itself if
-		       operands are in wrong order.  */
-		    /* (But NOT inverted; eq is still eq.) */
-		    cc_status.flags = CC_REVERSED;
-
-		  /* This seems to be overlooked by gcc.  FIXME: Check again.
-		     FIXME:  Is it really safe?  */
-		  cc_status.value2
-		    = gen_rtx_MINUS (GET_MODE (SET_SRC (exp)),
-				     XEXP (SET_SRC (exp), 0),
-				     XEXP (SET_SRC (exp), 1));
-		}
-	      return;
-	    }
-	  else if (REG_P (SET_DEST (exp))
-		   || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
-		       && REG_P (XEXP (SET_DEST (exp), 0))))
-	    {
-	      /* A register is set; normally CC is set to show that no
-		 test insn is needed.  Catch the exceptions.  */
-
-	      /* If not to cc0, then no "set"s in non-natural mode give
-		 ok cc0...  */
-	      if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD
-		  || GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT)
-		{
-		  /* ... except add:s and sub:s in DImode.  */
-		  if (GET_MODE (SET_DEST (exp)) == DImode
-		      && (GET_CODE (SET_SRC (exp)) == PLUS
-			  || GET_CODE (SET_SRC (exp)) == MINUS))
-		    {
-		      cc_status.flags = 0;
-		      cc_status.value1 = SET_DEST (exp);
-		      cc_status.value2 = SET_SRC (exp);
-
-		      if (cris_reg_overlap_mentioned_p (cc_status.value1,
-							cc_status.value2))
-			cc_status.value2 = 0;
-
-		      /* Add and sub may set V, which gets us
-			 unoptimizable results in "gt" and "le" condition
-			 codes.  */
-		      cc_status.flags |= CC_NO_OVERFLOW;
-
-		      return;
-		    }
-		}
-	      else if (SET_SRC (exp) == const0_rtx)
-		{
-		  /* There's no CC0 change when clearing a register or
-		     memory.  Just check for overlap.  */
-		  if (cc_status.value1
-		      && modified_in_p (cc_status.value1, insn))
-		    cc_status.value1 = 0;
-
-		  if (cc_status.value2
-		      && modified_in_p (cc_status.value2, insn))
-		    cc_status.value2 = 0;
-
-		  return;
-		}
-	      else
-		{
-		  cc_status.flags = 0;
-		  cc_status.value1 = SET_DEST (exp);
-		  cc_status.value2 = SET_SRC (exp);
-
-		  if (cris_reg_overlap_mentioned_p (cc_status.value1,
-						    cc_status.value2))
-		    cc_status.value2 = 0;
-
-		  /* Some operations may set V, which gets us
-		     unoptimizable results in "gt" and "le" condition
-		     codes.  */
-		  if (GET_CODE (SET_SRC (exp)) == PLUS
-		      || GET_CODE (SET_SRC (exp)) == MINUS
-		      || GET_CODE (SET_SRC (exp)) == NEG)
-		    cc_status.flags |= CC_NO_OVERFLOW;
+      cris_normal_notice_update_cc (exp, insn);
 
-		  return;
-		}
-	    }
-	  else if (MEM_P (SET_DEST (exp))
-		   || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART
-		       && MEM_P (XEXP (SET_DEST (exp), 0))))
-	    {
-	      /* When SET to MEM, then CC is not changed (except for
-		 overlap).  */
-	      if (cc_status.value1
-		  && modified_in_p (cc_status.value1, insn))
-		cc_status.value1 = 0;
-
-	      if (cc_status.value2
-		  && modified_in_p (cc_status.value2, insn))
-		cc_status.value2 = 0;
-
-	      return;
-	    }
-	}
-      else if (GET_CODE (exp) == PARALLEL)
-	{
-	  if (GET_CODE (XVECEXP (exp, 0, 0)) == SET
-	      && GET_CODE (XVECEXP (exp, 0, 1)) == SET
-	      && REG_P (XEXP (XVECEXP (exp, 0, 1), 0)))
-	    {
-	      if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0))
-		  && MEM_P (XEXP (XVECEXP (exp, 0, 0), 1)))
-		{
-		  /* For "move.S [rx=ry+o],rz", say CC reflects
-		     value1=rz and value2=[rx] */
-		  cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0);
-		  cc_status.value2
-		    = replace_equiv_address (XEXP (XVECEXP (exp, 0, 0), 1),
-					     XEXP (XVECEXP (exp, 0, 1), 0));
-		  cc_status.flags = 0;
-
-		  /* Huh?  A side-effect cannot change the destination
-		     register.  */
-		  if (cris_reg_overlap_mentioned_p (cc_status.value1,
-						    cc_status.value2))
-		    internal_error ("internal error: sideeffect-insn affecting main effect");
-		  return;
-		}
-	      else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1))
-			|| XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx)
-		       && MEM_P (XEXP (XVECEXP (exp, 0, 0), 0)))
-		{
-		  /* For "move.S rz,[rx=ry+o]" and "clear.S [rx=ry+o]",
-		     say flags are not changed, except for overlap.  */
-		  if (cc_status.value1
-		      && modified_in_p (cc_status.value1, insn))
-		    cc_status.value1 = 0;
-
-		  if (cc_status.value2
-		      && modified_in_p (cc_status.value2, insn))
-		    cc_status.value2 = 0;
-
-		  return;
-		}
-	    }
-	}
-      break;
+      /* The "test" insn doesn't clear (carry and) overflow on V32.  We
+        can change bge => bpl and blt => bmi by passing on to the cc0
+        user that V should not be considered; bgt and ble are taken
+        care of by other methods (see {tst,cmp}{si,hi,qi}).  */
+      if (attrval == CC_NOOV32 && TARGET_V32)
+	cc_status.flags |= CC_NO_OVERFLOW;
+      return;
 
     default:
       internal_error ("unknown cc_attr value");
@@ -1575,6 +1710,10 @@ cris_simple_epilogue (void)
       || !TARGET_PROLOGUE_EPILOGUE)
     return false;
 
+  /* Can't return from stacked return address with v32.  */
+  if (TARGET_V32 && cris_return_address_on_stack ())
+    return false;
+
   if (current_function_uses_pic_offset_table)
     {
       push_topmost_sequence ();
@@ -1901,6 +2040,49 @@ cris_side_effect_mode_ok (enum rtx_code 
   internal_error ("internal error: cris_side_effect_mode_ok with bad operands");
 }
 
+/* Whether next_cc0_user of insn is LE or GT or requires a real compare
+   insn for other reasons.  */
+
+bool
+cris_cc0_user_requires_cmp (rtx insn)
+{
+  rtx cc0_user = NULL;
+  rtx body;
+  rtx set;
+
+  gcc_assert (insn != NULL);
+
+  if (!TARGET_V32)
+    return false;
+
+  cc0_user = next_cc0_user (insn);
+  if (cc0_user == NULL)
+    return false;
+
+  body = PATTERN (cc0_user);
+  set = single_set (cc0_user);
+
+  /* Users can be sCC and bCC.  */
+  if (JUMP_P (cc0_user)
+      && GET_CODE (body) == SET
+      && SET_DEST (body) == pc_rtx
+      && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE
+      && XEXP (XEXP (SET_SRC (body), 0), 0) == cc0_rtx)
+    {
+      return
+	GET_CODE (XEXP (SET_SRC (body), 0)) == GT
+	|| GET_CODE (XEXP (SET_SRC (body), 0)) == LE;
+    }
+  else if (set)
+    {
+      return
+	GET_CODE (SET_SRC (body)) == GT
+	|| GET_CODE (SET_SRC (body)) == LE;
+    }
+
+  gcc_unreachable ();
+}
+
 /* The function reg_overlap_mentioned_p in CVS (still as of 2001-05-16)
    does not handle the case where the IN operand is strict_low_part; it
    does handle it for X.  Test-case in Axis-20010516.  This function takes
@@ -1931,10 +2113,12 @@ cris_target_asm_named_section (const cha
     default_elf_asm_named_section (name, flags, decl);
 }
 
-/* Return TRUE iff X is a CONST valid for e.g. indexing.  */
+/* Return TRUE iff X is a CONST valid for e.g. indexing.
+   ANY_OPERAND is 0 if X is in a CALL_P insn or movsi, 1
+   elsewhere.  */
 
 bool
-cris_valid_pic_const (rtx x)
+cris_valid_pic_const (rtx x, bool any_operand)
 {
   gcc_assert (flag_pic);
 
@@ -1955,14 +2139,20 @@ cris_valid_pic_const (rtx x)
   /* Handle (const (plus (unspec .. UNSPEC_GOTREL) (const_int ...))).  */
   if (GET_CODE (x) == PLUS
       && GET_CODE (XEXP (x, 0)) == UNSPEC
-      && XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREL
+      && (XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_GOTREL
+	  || XINT (XEXP (x, 0), 1) == CRIS_UNSPEC_PCREL)
       && CONST_INT_P (XEXP (x, 1)))
     x = XEXP (x, 0);
 
   if (GET_CODE (x) == UNSPEC)
     switch (XINT (x, 1))
       {
-      case CRIS_UNSPEC_PLT:
+	/* A PCREL operand is only valid for call and movsi.  */
+      case CRIS_UNSPEC_PLT_PCREL:
+      case CRIS_UNSPEC_PCREL:
+	return !any_operand;
+
+      case CRIS_UNSPEC_PLT_GOTREL:
       case CRIS_UNSPEC_PLTGOTREAD:
       case CRIS_UNSPEC_GOTREAD:
       case CRIS_UNSPEC_GOTREL:
@@ -1984,10 +2174,10 @@ cris_pic_symbol_type_of (rtx x)
     {
     case SYMBOL_REF:
       return SYMBOL_REF_LOCAL_P (x)
-	? cris_gotrel_symbol : cris_got_symbol;
+	? cris_rel_symbol : cris_got_symbol;
 
     case LABEL_REF:
-      return cris_gotrel_symbol;
+      return cris_rel_symbol;
 
     case CONST:
       return cris_pic_symbol_type_of (XEXP (x, 0));
@@ -2028,7 +2218,45 @@ int
 cris_legitimate_pic_operand (rtx x)
 {
   /* Symbols are not valid PIC operands as-is; just constants.  */
-  return cris_valid_pic_const (x);
+  return cris_valid_pic_const (x, true);
+}
+
+/* The ASM_OUTPUT_CASE_END worker.  */
+
+void
+cris_asm_output_case_end (FILE *stream, int num, rtx table)
+{
+  if (TARGET_V32)
+    {
+      rtx whole_jump_insn = PATTERN (PREV_INSN (PREV_INSN (table)));
+
+      /* This can be a SEQUENCE, meaning the delay-slot of the jump is
+	 filled.  */
+      rtx parallel_jump
+	= (GET_CODE (whole_jump_insn) == SEQUENCE
+	   ? PATTERN (XVECEXP (whole_jump_insn, 0, 0)) : whole_jump_insn);
+
+      asm_fprintf (stream,
+		   "\t.word %LL%d-.%s\n",
+		   CODE_LABEL_NUMBER (XEXP (XEXP (XEXP (XVECEXP
+							(parallel_jump, 0, 0),
+							1), 2), 0)),
+		   (TARGET_PDEBUG ? "; default" : ""));
+      return;
+    }
+
+  asm_fprintf (stream,
+	       "\t.word %LL%d-%LL%d%s\n",
+	       CODE_LABEL_NUMBER (XEXP
+				  (XEXP
+				   (XEXP
+				    (XVECEXP
+				     (PATTERN
+				      (PREV_INSN
+				       (PREV_INSN (table))), 0, 0), 1),
+				    2), 0)),
+	       num,
+	       (TARGET_PDEBUG ? "; default" : ""));
 }
 
 /* TARGET_HANDLE_OPTION worker.  We just store the values into local
@@ -2128,7 +2356,7 @@ cris_override_options (void)
 	  || strcmp ("etrax100lx", cris_cpu_str) == 0)
 	cris_cpu_version = 10;
 
-      if (cris_cpu_version < 0 || cris_cpu_version > 10)
+      if (cris_cpu_version < 0 || cris_cpu_version > 32)
 	error ("unknown CRIS version specification in -march= or -mcpu= : %s",
 	       cris_cpu_str);
 
@@ -2164,7 +2392,7 @@ cris_override_options (void)
 	  || strcmp ("etrax100lx", cris_tune_str) == 0)
 	cris_tune = 10;
 
-      if (cris_tune < 0 || cris_tune > 10)
+      if (cris_tune < 0 || cris_tune > 32)
 	error ("unknown CRIS cpu version specification in -mtune= : %s",
 	       cris_tune_str);
 
@@ -2176,6 +2404,9 @@ cris_override_options (void)
 	      | MASK_DATA_ALIGN | MASK_ALIGN_BY_32);
     }
 
+  if (cris_cpu_version >= CRIS_CPU_V32)
+    target_flags &= ~(MASK_SIDE_EFFECT_PREFIXES|MASK_MUL_BUG);
+
   if (flag_pic)
     {
       /* Use error rather than warning, so invalid use is easily
@@ -2229,15 +2460,28 @@ cris_asm_output_mi_thunk (FILE *stream,
       const char *name = XSTR (XEXP (DECL_RTL (funcdecl), 0), 0);
 
       name = (* targetm.strip_name_encoding) (name);
-      fprintf (stream, "add.d ");
-      assemble_name (stream, name);
-      fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX);
+
+      if (TARGET_V32)
+	{
+	  fprintf (stream, "\tba ");
+	  assemble_name (stream, name);
+	  fprintf (stream, "%s\n", CRIS_PLT_PCOFFSET_SUFFIX);
+	}
+      else
+	{
+	  fprintf (stream, "add.d ");
+	  assemble_name (stream, name);
+	  fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX);
+	}
     }
   else
     {
       fprintf (stream, "jump ");
       assemble_name (stream, XSTR (XEXP (DECL_RTL (funcdecl), 0), 0));
       fprintf (stream, "\n");
+
+      if (TARGET_V32)
+	fprintf (stream, "\tnop\n");
     }
 }
 
@@ -2374,7 +2618,7 @@ cris_split_movdx (rtx *operands)
 		  = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
 				     REG_NOTES (insn));
 
-	      mem = change_address (src, SImode, addr);
+	      mem = copy_rtx (mem);
 	      insn
 		= gen_rtx_SET (VOIDmode,
 			       operand_subword (dest, 1, TRUE, mode), mem);
@@ -2438,7 +2682,7 @@ cris_split_movdx (rtx *operands)
 	      = alloc_EXPR_LIST (REG_INC, XEXP (XEXP (mem, 0), 0),
 				 REG_NOTES (insn));
 
-	  mem = change_address (dest, SImode, addr);
+	  mem = copy_rtx (mem);
 	  insn
 	    = gen_rtx_SET (VOIDmode,
 			   mem,
@@ -2924,7 +3168,7 @@ cris_expand_epilogue (void)
      the return address on the stack.  */
   if (return_address_on_stack && pretend == 0)
     {
-      if (current_function_calls_eh_return)
+      if (TARGET_V32 || current_function_calls_eh_return)
 	{
 	  rtx mem;
 	  rtx insn;
@@ -2940,10 +3184,11 @@ cris_expand_epilogue (void)
 	  REG_NOTES (insn)
 	    = alloc_EXPR_LIST (REG_INC, stack_pointer_rtx, REG_NOTES (insn));
 
-	  emit_insn (gen_addsi3 (stack_pointer_rtx,
-				 stack_pointer_rtx,
-				 gen_rtx_raw_REG (SImode,
-						  CRIS_STACKADJ_REG)));
+	  if (current_function_calls_eh_return)
+	    emit_insn (gen_addsi3 (stack_pointer_rtx,
+				   stack_pointer_rtx,
+				   gen_rtx_raw_REG (SImode,
+						    CRIS_STACKADJ_REG)));
 	  cris_expand_return (false);
 	}
       else
@@ -3002,6 +3247,12 @@ cris_gen_movem_load (rtx src, rtx nregs_
   unsigned int regno = nregs - 1;
   int regno_inc = -1;
 
+  if (TARGET_V32)
+    {
+      regno = 0;
+      regno_inc = 1;
+    }
+
   if (GET_CODE (srcreg) == POST_INC)
     srcreg = XEXP (srcreg, 0);
 
@@ -3055,6 +3306,12 @@ cris_emit_movem_store (rtx dest, rtx nre
   unsigned int regno = nregs - 1;
   int regno_inc = -1;
 
+  if (TARGET_V32)
+    {
+      regno = 0;
+      regno_inc = 1;
+    }
+
   if (GET_CODE (destreg) == POST_INC)
     increment += nregs * 4;
 
@@ -3191,32 +3448,61 @@ cris_expand_pic_call_address (rtx *opp)
       /* For local symbols (non-PLT), just get the plain symbol
 	 reference into a register.  For symbols that can be PLT, make
 	 them PLT.  */
-      if (t == cris_gotrel_symbol)
-	op = force_reg (Pmode, op);
+      if (t == cris_rel_symbol)
+	{
+	  /* For v32, we're fine as-is; just PICify the symbol.  Forcing
+	     into a register caused performance regression for 3.2.1,
+	     observable in __floatdidf and elsewhere in libgcc.  */
+	  if (TARGET_V32)
+	    {
+	      rtx sym = GET_CODE (op) != CONST ? op : get_related_value (op);
+	      HOST_WIDE_INT offs = get_integer_term (op);
+
+	      /* We can't get calls to sym+N, N integer, can we?  */
+	      gcc_assert (offs == 0);
+
+	      op = gen_rtx_CONST (Pmode,
+				  gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym),
+						  CRIS_UNSPEC_PCREL));
+	    }
+	  else
+	    op = force_reg (Pmode, op);
+	}
       else if (t == cris_got_symbol)
 	{
 	  if (TARGET_AVOID_GOTPLT)
 	    {
 	      /* Change a "jsr sym" into (allocate register rM, rO)
-		 "move.d (const (unspec [sym] CRIS_UNSPEC_PLT)),rM"
-		 "add.d rPIC,rM,rO", "jsr rO".  */
+		 "move.d (const (unspec [sym rPIC] CRIS_UNSPEC_PLT_GOTREL)),rM"
+		 "add.d rPIC,rM,rO", "jsr rO" for pre-v32 and
+		 "jsr (const (unspec [sym rPIC] CRIS_UNSPEC_PLT_PCREL))"
+		 for v32.  */
 	      rtx tem, rm, ro;
 	      gcc_assert (can_create_pseudo_p ());
 	      current_function_uses_pic_offset_table = 1;
-	      tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op), CRIS_UNSPEC_PLT);
-	      rm = gen_reg_rtx (Pmode);
-	      emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
-	      ro = gen_reg_rtx (Pmode);
-	      if (expand_binop (Pmode, add_optab, rm,
-				pic_offset_table_rtx,
-				ro, 0, OPTAB_LIB_WIDEN) != ro)
-		internal_error ("expand_binop failed in movsi got");
-	      op = ro;
+	      tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op),
+				    TARGET_V32
+				    ? CRIS_UNSPEC_PLT_PCREL
+				    : CRIS_UNSPEC_PLT_GOTREL);
+	      tem = gen_rtx_CONST (Pmode, tem);
+	      if (TARGET_V32)
+		op = tem;
+	      else
+		{
+		  rm = gen_reg_rtx (Pmode);
+		  emit_move_insn (rm, tem);
+		  ro = gen_reg_rtx (Pmode);
+		  if (expand_binop (Pmode, add_optab, rm,
+				    pic_offset_table_rtx,
+				    ro, 0, OPTAB_LIB_WIDEN) != ro)
+		    internal_error ("expand_binop failed in movsi got");
+		  op = ro;
+		}
 	    }
 	  else
 	    {
 	      /* Change a "jsr sym" into (allocate register rM, rO)
-		 "move.d (const (unspec [sym] CRIS_UNSPEC_PLTGOT)),rM"
+		 "move.d (const (unspec [sym] CRIS_UNSPEC_PLTGOTREAD)),rM"
 		 "add.d rPIC,rM,rO" "jsr [rO]" with the memory access
 		 marked as not trapping and not aliasing.  No "move.d
 		 [rO],rP" as that would invite to re-use of a value
@@ -3300,7 +3586,7 @@ cris_asm_output_symbol_ref (FILE *file, 
      assemble_name (file, str);
 
      /* Sanity check.  */
-     if (! current_function_uses_pic_offset_table)
+     if (!TARGET_V32 && !current_function_uses_pic_offset_table)
        output_operand_lossage ("PIC register isn't set up");
     }
   else
@@ -3317,7 +3603,7 @@ cris_asm_output_label_ref (FILE *file, c
       assemble_name (file, buf);
 
       /* Sanity check.  */
-      if (! current_function_uses_pic_offset_table)
+      if (!TARGET_V32 && !current_function_uses_pic_offset_table)
 	internal_error ("emitting PIC operand, but PIC register isn't set up");
     }
   else
@@ -3341,11 +3627,25 @@ cris_output_addr_const_extra (FILE *file
       output_addr_const (file, x);
       switch (XINT (xconst, 1))
 	{
-	case CRIS_UNSPEC_PLT:
+	case CRIS_UNSPEC_PCREL:
+	  /* We only get this with -fpic/PIC to tell it apart from an
+	     invalid symbol.  We can't tell here, but it should only
+	     be the operand of a call or movsi.  */
+	  gcc_assert (TARGET_V32 && flag_pic);
+	  break;
+
+	case CRIS_UNSPEC_PLT_PCREL:
+	  gcc_assert (TARGET_V32);
+	  fprintf (file, ":PLT");
+	  break;
+
+	case CRIS_UNSPEC_PLT_GOTREL:
+	  gcc_assert (!TARGET_V32);
 	  fprintf (file, ":PLTG");
 	  break;
 
 	case CRIS_UNSPEC_GOTREL:
+	  gcc_assert (!TARGET_V32);
 	  fprintf (file, ":GOTOFF");
 	  break;
 
Index: gcc/config/cris/t-elfmulti
===================================================================
--- gcc/config/cris/t-elfmulti	(revision 130951)
+++ gcc/config/cris/t-elfmulti	(working copy)
@@ -1,6 +1,6 @@
 LIB2FUNCS_STATIC_EXTRA = $(srcdir)/config/cris/mulsi3.asm
-MULTILIB_OPTIONS = march=v10
-MULTILIB_DIRNAMES = v10
+MULTILIB_OPTIONS = march=v10/march=v32
+MULTILIB_DIRNAMES = v10 v32
 MULTILIB_MATCHES = \
 		march?v10=mcpu?etrax100lx \
 		march?v10=mcpu?ng \
@@ -8,7 +8,8 @@ MULTILIB_MATCHES = \
 		march?v10=march?ng \
 		march?v10=march?v11 \
 		march?v10=mcpu?v11 \
-		march?v10=mcpu?v10
+		march?v10=mcpu?v10 \
+		march?v32=mcpu?v32
 MULTILIB_EXTRA_OPTS = mbest-lib-options
 INSTALL_LIBGCC = install-multilib
 LIBGCC = stmp-multilib
Index: gcc/config/cris/predicates.md
===================================================================
--- gcc/config/cris/predicates.md	(revision 130951)
+++ gcc/config/cris/predicates.md	(working copy)
@@ -63,7 +63,7 @@ (define_predicate "cris_store_multiple_o
 (define_predicate "cris_bdap_const_operand"
   (and (match_code "label_ref, symbol_ref, const_int, const_double, const")
        (ior (not (match_test "flag_pic"))
-	    (match_test "cris_valid_pic_const (op)"))))
+	    (match_test "cris_valid_pic_const (op, true)"))))
 
 (define_predicate "cris_simple_address_operand"
   (ior (match_operand:SI 0 "register_operand")
@@ -140,15 +140,26 @@ (define_special_predicate "cris_general_
        	    ; The following test is actually just an assertion.
 	    (match_test "cris_pic_symbol_type_of (op) != cris_no_symbol"))))
 
+;; A predicate for the anon movsi expansion, one that fits a PCREL
+;; operand as well as general_operand.
+
+(define_special_predicate "cris_general_operand_or_pic_source"
+  (ior (match_operand 0 "general_operand")
+       (and (match_test "flag_pic")
+	    (match_test "cris_valid_pic_const (op, false)"))))
+
 ;; Since a PLT symbol is not a general_operand, we have to have a
 ;; predicate that matches it when we need it.  We use this in the expanded
 ;; "call" and "call_value" anonymous patterns.
 
-(define_predicate "cris_general_operand_or_plt_symbol"
-  (ior (match_operand 0 "general_operand")
+(define_predicate "cris_nonmemory_operand_or_callable_symbol"
+  (ior (match_operand 0 "nonmemory_operand")
        (and (match_code "const")
-	    (and (match_test "GET_CODE (XEXP (op, 0)) == UNSPEC")
-		 (not (match_test "TARGET_AVOID_GOTPLT"))))))
+	    (and
+	     (match_test "GET_CODE (XEXP (op, 0)) == UNSPEC")
+	     (ior
+	      (match_test "XINT (XEXP (op, 0), 1) == CRIS_UNSPEC_PLT_PCREL")
+	      (match_test "XINT (XEXP (op, 0), 1) == CRIS_UNSPEC_PCREL"))))))
 
 ;; This matches a (MEM (general_operand)) or
 ;; (MEM (cris_general_operand_or_symbol)).  The second one isn't a valid
Index: gcc/config/cris/linux.h
===================================================================
--- gcc/config/cris/linux.h	(revision 130951)
+++ gcc/config/cris/linux.h	(working copy)
@@ -35,6 +35,13 @@ along with GCC; see the file COPYING3.  
 /* This file defines the macros for cris-axis-linux-gnu that are not
    covered by cris.h, elfos.h and (config/)linux.h.  */
 
+/* Make sure we have a valid TARGET_CPU_DEFAULT, so we can assume it
+   and take shortcuts below.  */
+#ifndef TARGET_CPU_DEFAULT
+#error "TARGET_CPU_DEFAULT not defined"
+#elif (TARGET_CPU_DEFAULT+0) != 10 && (TARGET_CPU_DEFAULT+0) != 32
+#error "TARGET_CPU_DEFAULT must be 10 or 32, or this file be updated"
+#endif
 
 /* Node: Instruction Output */
 
@@ -45,19 +52,41 @@ along with GCC; see the file COPYING3.  
 /* These macros are CRIS-specific, but used in target driver macros.  */
 
 #undef CRIS_CPP_SUBTARGET_SPEC
-#define CRIS_CPP_SUBTARGET_SPEC \
+#if TARGET_CPU_DEFAULT == 32
+# define CRIS_CPP_SUBTARGET_SPEC \
+  "%{pthread:-D_REENTRANT}\
+   %{!march=*:%{!cpu=*:-D__arch_v32 -D__CRIS_arch_version=32}}"
+#else
+# define CRIS_CPP_SUBTARGET_SPEC \
   "%{pthread:-D_REENTRANT}\
    %{!march=*:%{!cpu=*:-D__arch_v10 -D__CRIS_arch_version=10}}"
+#endif
 
 #undef CRIS_CC1_SUBTARGET_SPEC
-#define CRIS_CC1_SUBTARGET_SPEC \
+#if TARGET_CPU_DEFAULT == 32
+# define CRIS_CC1_SUBTARGET_SPEC \
+ "%{!march=*:%{!cpu=*:-march=v32}}"
+#define CRIS_SUBTARGET_DEFAULT_ARCH MASK_AVOID_GOTPLT
+#else
+# define CRIS_CC1_SUBTARGET_SPEC \
  "%{!march=*:%{!cpu=*:-march=v10}}"
+#define CRIS_SUBTARGET_DEFAULT_ARCH 0
+#endif
 
 #undef CRIS_ASM_SUBTARGET_SPEC
-#define CRIS_ASM_SUBTARGET_SPEC \
- "--em=criself\
+#if TARGET_CPU_DEFAULT == 32
+# define CRIS_ASM_SUBTARGET_SPEC \
+ "--em=criself \
+  %{!march=*:%{!cpu=*:--march=v32}} \
+  %{!fleading-underscore:--no-underscore}\
+  %{fPIC|fpic|fPIE|fpie: --pic}"
+#else
+# define CRIS_ASM_SUBTARGET_SPEC \
+ "--em=criself \
+  %{!march=*:%{!cpu=*:--march=v10}} \
   %{!fleading-underscore:--no-underscore}\
   %{fPIC|fpic|fPIE|fpie: --pic}"
+#endif
 
 /* Previously controlled by target_flags.  */
 #undef TARGET_LINUX
@@ -67,7 +96,8 @@ along with GCC; see the file COPYING3.  
 #define CRIS_SUBTARGET_DEFAULT			\
   (MASK_SVINTO					\
    + MASK_ETRAX4_ADD				\
-   + MASK_ALIGN_BY_32)
+   + MASK_ALIGN_BY_32				\
+   + CRIS_SUBTARGET_DEFAULT_ARCH)
 
 #undef CRIS_DEFAULT_CPU_VERSION
 #define CRIS_DEFAULT_CPU_VERSION CRIS_CPU_NG
Index: gcc/config/cris/cris.h
===================================================================
--- gcc/config/cris/cris.h	(revision 130951)
+++ gcc/config/cris/cris.h	(working copy)
@@ -131,10 +131,42 @@ extern int cris_cpu_version;
   %(cpp_subtarget)"
 
 /* For the cris-*-elf subtarget.  */
+
+#define CRIS_DEFAULT_TUNE "10"
+#define CRIS_ARCH_CPP_DEFAULT
+#define CRIS_DEFAULT_ASM_ARCH_OPTION ""
+
+#ifdef TARGET_CPU_DEFAULT
+#if TARGET_CPU_DEFAULT != 32 && TARGET_CPU_DEFAULT != 10
+ #error "Due to '()'; e.g. '#define TARGET_CPU_DEFAULT (10)', stringize TARGET_CPU_DEFAULT isn't useful: update manually."
+#endif
+
+#if TARGET_CPU_DEFAULT == 32
+#undef CRIS_DEFAULT_TUNE
+#define CRIS_DEFAULT_TUNE "32"
+/* To enable use of "generic" cris-axis-elf binutils, always pass the
+   architecture option to GAS.  (We don't do this for non-v32.)  */
+#undef CRIS_DEFAULT_ASM_ARCH_OPTION
+#define CRIS_DEFAULT_ASM_ARCH_OPTION "--march=v32"
+#endif
+
+#undef CRIS_ARCH_CPP_DEFAULT
+#define CRIS_ARCH_CPP_DEFAULT \
+ "%{!march=*:\
+   %{!metrax*:\
+    %{!mcpu=*:\
+     %{!mtune=*:-D__tune_v" CRIS_DEFAULT_TUNE "}\
+     -D__arch_v"CRIS_DEFAULT_TUNE\
+   " -D__CRIS_arch_version=" CRIS_DEFAULT_TUNE "}}}"
+#endif
+
 #define CRIS_CPP_SUBTARGET_SPEC \
  "%{mbest-lib-options:\
    %{!moverride-best-lib-options:\
-    %{!march=*:%{!metrax*:%{!mcpu=*:-D__tune_v10 -D__CRIS_arch_tune=10}}}}}"
+   %{!march=*:%{!metrax*:%{!mcpu=*:\
+      -D__tune_v" CRIS_DEFAULT_TUNE \
+    " -D__CRIS_arch_tune=" CRIS_DEFAULT_TUNE "}}}}}"\
+ CRIS_ARCH_CPP_DEFAULT
 
 /* Remove those Sun-make "target" switches.  */
 /* Override previous definitions (linux.h).  */
@@ -150,7 +182,8 @@ extern int cris_cpu_version;
  "-melf\
   %{mbest-lib-options:\
    %{!moverride-best-lib-options:\
-    %{!march=*:%{!mcpu=*:-mtune=v10 -D__CRIS_arch_tune=10}}\
+   %{!march=*:%{!mcpu=*:-mtune=v" CRIS_DEFAULT_TUNE\
+       " -D__CRIS_arch_tune=" CRIS_DEFAULT_TUNE "}}\
     %{!finhibit-size-directive:\
       %{!fno-function-sections: -ffunction-sections}\
       %{!fno-data-sections: -fdata-sections}}}}"
@@ -174,10 +207,13 @@ extern int cris_cpu_version;
 #define ASM_SPEC \
  MAYBE_AS_NO_MUL_BUG_ABORT \
  "%{v:-v}\
-  %(asm_subtarget)"
+ %(asm_subtarget)\
+ %{march=*:%{cpu=*:%eDo not specify both -march=... and -mcpu=...}}\
+ %{march=v32:--march=v32} %{mcpu=v32:--march=v32}"
 
 /* For the cris-*-elf subtarget.  */
-#define CRIS_ASM_SUBTARGET_SPEC "--em=criself"
+#define CRIS_ASM_SUBTARGET_SPEC \
+ "--em=criself %{!march=*:%{!cpu=*:" CRIS_DEFAULT_ASM_ARCH_OPTION "}}"
 
 /* FIXME: We should propagate the -melf option to make the criself
    "emulation" unless a linker script is provided (-T*), but I don't know
@@ -258,14 +294,6 @@ extern int target_flags;
    for -melinux.  */
 #define TARGET_LINUX 0
 
-/* Default target_flags if no switches specified.  */
-#ifndef TARGET_DEFAULT
-# define TARGET_DEFAULT \
- (MASK_SIDE_EFFECT_PREFIXES + MASK_STACK_ALIGN \
-  + MASK_CONST_ALIGN + MASK_DATA_ALIGN \
-  + MASK_PROLOGUE_EPILOGUE + MASK_MUL_BUG)
-#endif
-
 /* For the cris-*-elf subtarget.  */
 #define CRIS_SUBTARGET_DEFAULT 0
 
@@ -273,13 +301,34 @@ extern int target_flags;
 #define CRIS_CPU_ETRAX4 3	/* Just lz added.  */
 #define CRIS_CPU_SVINTO 8	/* Added swap, jsrc & Co., 32-bit accesses.  */
 #define CRIS_CPU_NG 10		/* Added mul[su].  */
+#define CRIS_CPU_V32 32		/* Major changes.  */
+
+#ifndef TARGET_CPU_DEFAULT
+#define TARGET_CPU_DEFAULT CRIS_CPU_BASE
+#endif
+
+/* Default target_flags if no switches specified.  */
+#ifndef TARGET_DEFAULT
+# if TARGET_CPU_DEFAULT == 32
+#  define TARGET_DEFAULT \
+ (MASK_STACK_ALIGN \
+  + MASK_CONST_ALIGN + MASK_DATA_ALIGN \
+  + MASK_PROLOGUE_EPILOGUE)
+# else  /* 10 */
+# define TARGET_DEFAULT \
+ (MASK_SIDE_EFFECT_PREFIXES + MASK_STACK_ALIGN \
+  + MASK_CONST_ALIGN + MASK_DATA_ALIGN \
+  + MASK_PROLOGUE_EPILOGUE + MASK_MUL_BUG)
+# endif
+#endif
 
 /* Local, providing a default for cris_cpu_version.  */
-#define CRIS_DEFAULT_CPU_VERSION CRIS_CPU_BASE
+#define CRIS_DEFAULT_CPU_VERSION TARGET_CPU_DEFAULT
 
 #define TARGET_HAS_MUL_INSNS (cris_cpu_version >= CRIS_CPU_NG)
 #define TARGET_HAS_LZ (cris_cpu_version >= CRIS_CPU_ETRAX4)
 #define TARGET_HAS_SWAP (cris_cpu_version >= CRIS_CPU_SVINTO)
+#define TARGET_V32 (cris_cpu_version >= CRIS_CPU_V32)
 
 #define CRIS_SUBTARGET_HANDLE_OPTION(x, y, z)
 
@@ -466,6 +515,13 @@ extern int target_flags;
 #define REG_ALLOC_ORDER \
  {9, 13, 12, 11, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 17, 16, 18, 19}
 
+/* Use MOF and ACR.  Prefer ACR before any other register.  Prefer MOF
+   then SRP after saved registers.  The *after* is because they're only
+   useful for storage, not for things being computed, which is
+   apparently more common.  */
+#define REG_ALLOC_ORDER_V32 \
+ {15, 9, 13, 12, 11, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 17, 16, 14, 18, 19}
+
 
 /* Node: Values in Registers */
 
@@ -482,7 +538,7 @@ extern int target_flags;
  (((MODE) == CCmode				\
    || (REGNO) != CRIS_CC0_REGNUM)		\
   && (GET_MODE_SIZE (MODE) <= UNITS_PER_WORD	\
-      || (REGNO) != CRIS_MOF_REGNUM))
+      || ((REGNO) != CRIS_MOF_REGNUM && (REGNO) != CRIS_ACR_REGNUM)))
 
 /* Because CCmode isn't covered by the "narrower mode" statement in
    tm.texi, we can still say all modes are tieable despite not having an
@@ -499,18 +555,30 @@ extern int target_flags;
 
 /* Node: Register Classes */
 
-enum reg_class 
+/* FIXME: A separate class for the return register would make sense.
+
+   We need a separate register class to handle register allocation for
+   ACR, since it can't be used for post-increment.
+
+   It's not obvious, but having subunions of all movable-between
+   register classes does really help register allocation.  */
+enum reg_class
   {
     NO_REGS,
-    MOF_REGS, CC0_REGS, SPECIAL_REGS, GENERAL_REGS, ALL_REGS,
+    ACR_REGS, MOF_REGS, CC0_REGS, SPECIAL_REGS,
+    SPEC_ACR_REGS, GENNONACR_REGS,
+    SPEC_GENNONACR_REGS, GENERAL_REGS,
+    ALL_REGS,
     LIM_REG_CLASSES
   };
 
 #define N_REG_CLASSES (int) LIM_REG_CLASSES
 
-#define REG_CLASS_NAMES							\
-  {"NO_REGS",								\
-   "MOF_REGS", "CC0_REGS", "SPECIAL_REGS", "GENERAL_REGS", "ALL_REGS"}
+#define REG_CLASS_NAMES						\
+  {"NO_REGS",							\
+   "ACR_REGS", "MOF_REGS", "CC0_REGS", "SPECIAL_REGS",		\
+   "SPEC_ACR_REGS", "GENNONACR_REGS", "SPEC_GENNONACR_REGS",	\
+   "GENERAL_REGS", "ALL_REGS"}
 
 #define CRIS_SPECIAL_REGS_CONTENTS					\
  ((1 << CRIS_SRP_REGNUM) | (1 << CRIS_MOF_REGNUM) | (1 << CRIS_CC0_REGNUM))
@@ -519,26 +587,39 @@ enum reg_class 
 #define REG_CLASS_CONTENTS			\
   {						\
    {0},						\
+   {1 << CRIS_ACR_REGNUM},			\
    {1 << CRIS_MOF_REGNUM},			\
    {1 << CRIS_CC0_REGNUM},			\
    {CRIS_SPECIAL_REGS_CONTENTS},		\
+   {CRIS_SPECIAL_REGS_CONTENTS			\
+    | (1 << CRIS_ACR_REGNUM)},			\
+   {(0xffff | (1 << CRIS_AP_REGNUM))		\
+    & ~(1 << CRIS_ACR_REGNUM)},			\
+   {(0xffff | (1 << CRIS_AP_REGNUM)		\
+    | CRIS_SPECIAL_REGS_CONTENTS)		\
+    & ~(1 << CRIS_ACR_REGNUM)},			\
    {0xffff | (1 << CRIS_AP_REGNUM)},		\
    {0xffff | (1 << CRIS_AP_REGNUM)		\
     | CRIS_SPECIAL_REGS_CONTENTS}		\
   }
 
 #define REGNO_REG_CLASS(REGNO)			\
-  ((REGNO) == CRIS_MOF_REGNUM ? MOF_REGS :	\
+  ((REGNO) == CRIS_ACR_REGNUM ? ACR_REGS :	\
+   (REGNO) == CRIS_MOF_REGNUM ? MOF_REGS :	\
    (REGNO) == CRIS_CC0_REGNUM ? CC0_REGS :	\
    (REGNO) == CRIS_SRP_REGNUM ? SPECIAL_REGS :	\
    GENERAL_REGS)
 
 #define BASE_REG_CLASS GENERAL_REGS
 
+#define MODE_CODE_BASE_REG_CLASS(MODE, OCODE, ICODE)	\
+  ((OCODE) != POST_INC ? BASE_REG_CLASS : GENNONACR_REGS)
+
 #define INDEX_REG_CLASS GENERAL_REGS
 
 #define REG_CLASS_FROM_LETTER(C)		\
   (						\
+   (C) == 'a' ? ACR_REGS :			\
    (C) == 'h' ? MOF_REGS :			\
    (C) == 'x' ? SPECIAL_REGS :			\
    (C) == 'c' ? CC0_REGS :			\
@@ -553,6 +634,14 @@ enum reg_class 
   || (unsigned) reg_renumber[REGNO] <= CRIS_LAST_GENERAL_REGISTER	\
   || (unsigned) reg_renumber[REGNO] == ARG_POINTER_REGNUM)
 
+/* REGNO_OK_FOR_BASE_P seems to be obsolete wrt. this one, but not yet
+   documented as such.  */
+#define REGNO_MODE_CODE_OK_FOR_BASE_P(REGNO, MODE, OCODE, ICODE)	\
+ (REGNO_OK_FOR_BASE_P (REGNO)						\
+  && ((OCODE) != POST_INC						\
+      || !((REGNO) == CRIS_ACR_REGNUM					\
+	   || (unsigned) reg_renumber[REGNO] == CRIS_ACR_REGNUM)))
+
 /* See REGNO_OK_FOR_BASE_P.  */
 #define REGNO_OK_FOR_INDEX_P(REGNO) REGNO_OK_FOR_BASE_P(REGNO)
 
@@ -565,7 +654,8 @@ enum reg_class 
    they need to be reloaded.  FIXME: Investigate whether that constitutes
    a bug.  */
 #define PREFERRED_RELOAD_CLASS(X, CLASS)	\
- ((CLASS) != MOF_REGS				\
+ ((CLASS) != ACR_REGS				\
+  && (CLASS) != MOF_REGS			\
   && (CLASS) != CC0_REGS			\
   && (CLASS) != SPECIAL_REGS			\
   ? GENERAL_REGS : (CLASS))
@@ -578,6 +668,11 @@ enum reg_class 
    || !MEM_P (X)					\
    ? NO_REGS : GENERAL_REGS)
 
+/* FIXME: Fix regrename.c; it should check validity of replacements,
+   not just with a silly pass-specific macro.  We may miss some
+   opportunities, but we must stop regrename from creating acr++.  */
+#define HARD_REGNO_RENAME_OK(FROM, TO) ((TO) != CRIS_ACR_REGNUM)
+
 /* For CRIS, this is always the size of MODE in words,
    since all registers are the same size.  To use omitted modes in
    patterns with reload constraints, you must say the widest size
@@ -632,6 +727,8 @@ enum reg_class 
   (C) == 'S' ? EXTRA_CONSTRAINT_S (X) :		\
   /* A three-address addressing-mode?  */	\
   (C) == 'T' ? EXTRA_CONSTRAINT_T (X) :		\
+  /* A PLT symbol?  */				\
+  (C) == 'U' ? EXTRA_CONSTRAINT_U (X) :		\
   0)
 
 #define EXTRA_MEMORY_CONSTRAINT(X, STR) ((X) == 'Q')
@@ -683,7 +780,12 @@ enum reg_class 
 
 /* PIC-constructs for symbols.  */
 #define EXTRA_CONSTRAINT_S(X)						\
- (flag_pic && GET_CODE (X) == CONST && cris_valid_pic_const (X))
+ (flag_pic && GET_CODE (X) == CONST && cris_valid_pic_const (X, false))
+
+#define EXTRA_CONSTRAINT_U(X)						\
+ (flag_pic								\
+  && CONSTANT_P (X)							\
+  && cris_nonmemory_operand_or_callable_symbol (X, VOIDmode))
 
 
 /* Node: Frame Layout */
@@ -877,22 +979,65 @@ struct cum_args {int regs;};
    somewhat lack of elegance.
     (Do not be tempted to "straighten up" whitespace in the asms; the
    assembler #NO_APP state mandates strict spacing).  */
-#define TRAMPOLINE_TEMPLATE(FILE)		\
-  do						\
-    {						\
-      fprintf (FILE, "\tmove.d $%s,[$pc+20]\n",	\
-	       reg_names[STATIC_CHAIN_REGNUM]);	\
-      fprintf (FILE, "\tmove $srp,[$pc+22]\n");	\
-      fprintf (FILE, "\tmove.d 0,$%s\n",	\
-	       reg_names[STATIC_CHAIN_REGNUM]);	\
-      fprintf (FILE, "\tjsr 0\n");		\
-      fprintf (FILE, "\tmove.d 0,$%s\n",	\
-	       reg_names[STATIC_CHAIN_REGNUM]);	\
-      fprintf (FILE, "\tjump 0\n");		\
-    }						\
+#define TRAMPOLINE_TEMPLATE(FILE)				       \
+  do								       \
+    {								       \
+      if (TARGET_V32)						       \
+       {							       \
+	 /* This normally-unused nop insn acts as an instruction to    \
+	    the simulator to flush its instruction cache.  None of     \
+	    the other instructions in the trampoline template suits    \
+	    as a trigger for V32.  The pc-relative addressing mode     \
+	    works nicely as a trigger for V10.			       \
+	    FIXME: Have specific V32 template (possibly avoiding the   \
+	    use of a special instruction).  */			       \
+	 fprintf (FILE, "\tclearf x\n");			       \
+	 /* We have to use a register as an intermediate, choosing     \
+	    semi-randomly R1 (which has to not be the		       \
+	    STATIC_CHAIN_REGNUM), so we can use it for address	       \
+	    indirection and jsr target.	 */			       \
+	 fprintf (FILE, "\tmove $r1,$mof\n");			       \
+	 /* +4 */						       \
+	 fprintf (FILE, "\tmove.d 0,$r1\n");			       \
+	 fprintf (FILE, "\tmove.d $%s,[$r1]\n",			       \
+		  reg_names[STATIC_CHAIN_REGNUM]);		       \
+	 fprintf (FILE, "\taddq 6,$r1\n");			       \
+	 fprintf (FILE, "\tmove $mof,[$r1]\n");			       \
+	 fprintf (FILE, "\taddq 6,$r1\n");			       \
+	 fprintf (FILE, "\tmove $srp,[$r1]\n");			       \
+	 /* +20 */						       \
+	 fprintf (FILE, "\tmove.d 0,$%s\n",			       \
+		  reg_names[STATIC_CHAIN_REGNUM]);		       \
+	 /* +26 */						       \
+	 fprintf (FILE, "\tmove.d 0,$r1\n");			       \
+	 fprintf (FILE, "\tjsr $r1\n");				       \
+	 fprintf (FILE, "\tsetf\n");				       \
+	 /* +36 */						       \
+	 fprintf (FILE, "\tmove.d 0,$%s\n",			       \
+		  reg_names[STATIC_CHAIN_REGNUM]);		       \
+	 /* +42 */						       \
+	 fprintf (FILE, "\tmove.d 0,$r1\n");			       \
+	 /* +48 */						       \
+	 fprintf (FILE, "\tmove.d 0,$r9\n");			       \
+	 fprintf (FILE, "\tjump $r9\n");			       \
+	 fprintf (FILE, "\tsetf\n");				       \
+       }							       \
+      else							       \
+       {							       \
+	 fprintf (FILE, "\tmove.d $%s,[$pc+20]\n",		       \
+		  reg_names[STATIC_CHAIN_REGNUM]);		       \
+	 fprintf (FILE, "\tmove $srp,[$pc+22]\n");		       \
+	 fprintf (FILE, "\tmove.d 0,$%s\n",			       \
+		  reg_names[STATIC_CHAIN_REGNUM]);		       \
+	 fprintf (FILE, "\tjsr 0\n");				       \
+	 fprintf (FILE, "\tmove.d 0,$%s\n",			       \
+		  reg_names[STATIC_CHAIN_REGNUM]);		       \
+	 fprintf (FILE, "\tjump 0\n");				       \
+       }							       \
+    }								       \
   while (0)
 
-#define TRAMPOLINE_SIZE 32
+#define TRAMPOLINE_SIZE (TARGET_V32 ? 58 : 32)
 
 /* CRIS wants instructions on word-boundary.
    Note that due to a bug (reported) in 2.7.2 and earlier, this is
@@ -900,16 +1045,29 @@ struct cum_args {int regs;};
    this is not fatal, only a slight waste of stack space).  */
 #define TRAMPOLINE_ALIGNMENT 16
 
-#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT)		\
-  do								\
-    {								\
-      emit_move_insn (gen_rtx_MEM (SImode,			\
-			       plus_constant (TRAMP, 10)),	\
-		      CXT);					\
-      emit_move_insn (gen_rtx_MEM (SImode,			\
-			       plus_constant (TRAMP, 16)),	\
-		      FNADDR);					\
-    }								\
+#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT)			\
+  do									\
+    if (TARGET_V32)							\
+      {									\
+	emit_move_insn (gen_rtx_MEM (SImode,				\
+				     plus_constant (TRAMP, 6)),		\
+		 	plus_constant (TRAMP, 38));			\
+	emit_move_insn (gen_rtx_MEM (SImode,				\
+				     plus_constant (TRAMP, 22)),	\
+			CXT);						\
+	emit_move_insn (gen_rtx_MEM (SImode,				\
+				     plus_constant (TRAMP, 28)),	\
+		 	FNADDR);					\
+      }									\
+    else								\
+      {									\
+	emit_move_insn (gen_rtx_MEM (SImode,				\
+				     plus_constant (TRAMP, 10)),	\
+			CXT);						\
+	emit_move_insn (gen_rtx_MEM (SImode,				\
+				     plus_constant (TRAMP, 16)),	\
+		 	FNADDR);					\
+      }									\
   while (0)
 
 /* Note that there is no need to do anything with the cache for sake of
@@ -929,6 +1087,8 @@ struct cum_args {int regs;};
 
 #define CONSTANT_ADDRESS_P(X) CONSTANT_P (X)
 
+/* Must be a compile-time constant, so we go with the highest value
+   among all CRIS variants.  */
 #define MAX_REGS_PER_ADDRESS 2
 
 /* There are helper macros defined here which are used only in
@@ -945,15 +1105,18 @@ struct cum_args {int regs;};
 /* No symbol can be used as an index (or more correct, as a base) together
    with a register with PIC; the PIC register must be there.  */
 #define CONSTANT_INDEX_P(X) \
- (CONSTANT_P (X) && (!flag_pic || cris_valid_pic_const (X)))
+ (CONSTANT_P (X) && (!flag_pic || cris_valid_pic_const (X, true)))
 
 /* True if X is a valid base register.  */
 #define BASE_P(X) \
  (REG_P (X) && REG_OK_FOR_BASE_P (X))
 
 /* True if X is a valid base register with or without autoincrement.  */
-#define BASE_OR_AUTOINCR_P(X) \
- (BASE_P (X) || (GET_CODE (X) == POST_INC && BASE_P (XEXP (X, 0))))
+#define BASE_OR_AUTOINCR_P(X)				\
+ (BASE_P (X)						\
+  || (GET_CODE (X) == POST_INC				\
+      && BASE_P (XEXP (X, 0))				\
+      && REGNO (XEXP (X, 0)) != CRIS_ACR_REGNUM))
 
 /* True if X is a valid (register) index for BDAP, i.e. [Rs].S or [Rs+].S.  */
 #define BDAP_INDEX_P(X)					\
@@ -975,12 +1138,6 @@ struct cum_args {int regs;};
       && (INTVAL (XEXP (X, 1)) == 2		\
 	  || INTVAL (XEXP (X, 1)) == 4)))
 
-/* True if X is an address that doesn't need a prefix i.e. [Rs] or [Rs+].  */
-#define SIMPLE_ADDRESS_P(X)	\
- (BASE_P (X)			\
-  || (GET_CODE (X) == POST_INC	\
-      && BASE_P (XEXP (X, 0))))
-
 /* A PIC operand looks like a normal symbol here.  At output we dress it
    in "[rPIC+symbol:GOT]" (global symbol) or "rPIC+symbol:GOTOFF" (local
    symbol) so we exclude all addressing modes where we can't replace a
@@ -990,12 +1147,15 @@ struct cum_args {int regs;};
 #define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR)			\
  {								\
    rtx x1, x2;							\
-   if (SIMPLE_ADDRESS_P (X))					\
+   if (BASE_OR_AUTOINCR_P (X))					\
      goto ADDR;							\
-   if (CONSTANT_INDEX_P (X))					\
+   else if (TARGET_V32)						\
+     /* Nothing else is valid then.  */				\
+     ;								\
+   else if (CONSTANT_INDEX_P (X))				\
      goto ADDR;							\
    /* Indexed?  */						\
-   if (GET_CODE (X) == PLUS)					\
+   else if (GET_CODE (X) == PLUS)				\
      {								\
        x1 = XEXP (X, 0);					\
        x2 = XEXP (X, 1);					\
@@ -1081,32 +1241,8 @@ struct cum_args {int regs;};
 
 /* Node: Costs */
 
-/* Can't move to and from a SPECIAL_REGS register, so we have to say
-   their move cost within that class is higher.  How about 7?  That's 3
-   for a move to a GENERAL_REGS register, 3 for the move from the
-   GENERAL_REGS register, and 1 for the increased register pressure.
-   Also, it's higher than the memory move cost, which is in order.  
-   We also do this for ALL_REGS, since we don't want that class to be
-   preferred (even to memory) at all where GENERAL_REGS doesn't fit.
-   Whenever it's about to be used, it's for SPECIAL_REGS.  If we don't
-   present a higher cost for ALL_REGS than memory, a SPECIAL_REGS may be
-   used when a GENERAL_REGS should be used, even if there are call-saved
-   GENERAL_REGS left to allocate.  This is because the fall-back when
-   the most preferred register class isn't available, isn't the next
-   (or next good) wider register class, but the *most widest* register
-   class.
-   Give the cost 3 between a special register and a general register,
-   because we want constraints verified.  */
-
-#define REGISTER_MOVE_COST(MODE, FROM, TO)		\
- ((((FROM) == SPECIAL_REGS || (FROM) == MOF_REGS)	\
-   && ((TO) == SPECIAL_REGS || (TO) == MOF_REGS))	\
-  || (FROM) == ALL_REGS					\
-  || (TO) == ALL_REGS					\
-  ? 7 :							\
-  ((FROM) == SPECIAL_REGS || (FROM) == MOF_REGS		\
-   || (TO) == SPECIAL_REGS || (TO) == MOF_REGS)		\
-  ? 3 : 2)
+#define REGISTER_MOVE_COST(MODE, FROM, TO)	\
+  cris_register_move_cost (MODE, FROM, TO)
 
 /* This isn't strictly correct for v0..3 in buswidth-8bit mode, but
    should suffice.  */
@@ -1146,7 +1282,7 @@ enum cris_pic_symbol_type
   {
     cris_no_symbol = 0,
     cris_got_symbol = 1,
-    cris_gotrel_symbol = 2,
+    cris_rel_symbol = 2,
     cris_got_symbol_needing_fixup = 3,
     cris_invalid_pic_symbol = 4
   };
@@ -1264,10 +1400,10 @@ enum cris_pic_symbol_type
 
 #define REGISTER_NAMES					\
  {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8",	\
-  "r9", "r10", "r11", "r12", "r13", "sp", "pc", "srp", "mof", "faked_ap", "dccr"}
+  "r9", "r10", "r11", "r12", "r13", "sp", "acr", "srp", "mof", "faked_ap", "dccr"}
 
 #define ADDITIONAL_REGISTER_NAMES \
- {{"r14", 14}, {"r15", 15}}
+ {{"r14", 14}, {"r15", 15}, {"pc", 15}}
 
 #define PRINT_OPERAND(FILE, X, CODE)		\
  cris_print_operand (FILE, X, CODE)
@@ -1297,17 +1433,27 @@ enum cris_pic_symbol_type
 #undef USER_LABEL_PREFIX
 #define USER_LABEL_PREFIX "_"
 
-#define ASM_OUTPUT_REG_PUSH(FILE, REGNO) \
-  fprintf (FILE, "\tpush $%s\n", reg_names[REGNO])
+#define ASM_OUTPUT_REG_PUSH(FILE, REGNO)				\
+  fprintf (FILE,							\
+	   TARGET_V32							\
+	   ? "\tsubq 4,$sp\n\tmove $%s,[$sp]\n" : "\tpush $%s\n",	\
+	   reg_names[REGNO])
 
 #define ASM_OUTPUT_REG_POP(FILE, REGNO) \
-  fprintf (FILE, "\tpop $%s\n", reg_names[REGNO])
+  fprintf (FILE, "\tmove [$sp+],$%s\n", reg_names[REGNO])
 
 
 /* Node: Dispatch Tables */
 
-#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL)	\
-  asm_fprintf (FILE, "\t.word %LL%d-%LL%d\n", VALUE, REL)
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL)		\
+  do									\
+    {									\
+      if (TARGET_V32)							\
+       asm_fprintf (FILE, "\t.word %LL%d-.\n", VALUE);			\
+      else								\
+       asm_fprintf (FILE, "\t.word %LL%d-%LL%d\n", VALUE, REL);		\
+    }									\
+  while (0)
 
 #define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE)  \
   asm_fprintf (FILE, "\t.dword %LL%d\n", VALUE)
@@ -1322,20 +1468,7 @@ enum cris_pic_symbol_type
    the expanded casesi core-insn.
    FIXME: Check this construct when changing to new version of gcc.  */
 #define ASM_OUTPUT_CASE_END(STREAM, NUM, TABLE)				\
-  do									\
-    {									\
-      asm_fprintf (STREAM, "\t.word %LL%d-%LL%d%s\n",			\
-		   CODE_LABEL_NUMBER					\
-		    (XEXP (XEXP (XEXP					\
-				  (XVECEXP				\
-				    (PATTERN				\
-				     (prev_nonnote_insn			\
-				      (PREV_INSN (TABLE))),		\
-				     0, 0), 1), 2), 0)),		\
-		   NUM,							\
-		   (TARGET_PDEBUG ? "; default" : ""));			\
-    }									\
-  while (0)
+  cris_asm_output_case_end (STREAM, NUM, TABLE)
 
 
 /* Node: Exception Region Output */
Index: gcc/config/cris/cris.md
===================================================================
--- gcc/config/cris/cris.md	(revision 130951)
+++ gcc/config/cris/cris.md	(working copy)
@@ -54,23 +54,48 @@
 ;; [rX=gotless_symbol].
 ;; The movsi for a gotless symbol could be split (post reload).
 \f
-;; UNSPEC Usage:
-;; 0 PLT reference from call expansion: operand 0 is the address,
-;;   the mode is VOIDmode.  Always wrapped in CONST.
-;; 1 Stack frame deallocation barrier.
-;; 2 The address of the global offset table as a source operand.
-;; 3 The address of a global-offset-table-relative symbol + offset.
-;; 4 The offset within GOT of a symbol.
-;; 5 The offset within GOT of a symbol that has a PLT.
-
-(define_constants ; FIXME: reorder sanely.
-  [(CRIS_UNSPEC_PLT 0)
-   (CRIS_UNSPEC_FRAME_DEALLOC 1)
+
+(define_constants
+  [
+   ;; PLT reference from call expansion: operand 0 is the address,
+   ;; the mode is VOIDmode.  Always wrapped in CONST.
+   ;; The value is relative to the GOT.
+   (CRIS_UNSPEC_PLT_GOTREL 0)
+
+   ;; PLT reference from call expansion: operand 0 is the address,
+   ;; the mode is VOIDmode.  Always wrapped in CONST.
+   ;; The value is relative to the PC.  It's arch-dependent whether
+   ;; the offset counts from the start or the end of the current item.
+   (CRIS_UNSPEC_PLT_PCREL 1)
+
+   ;; The address of the global offset table as a source operand.
    (CRIS_UNSPEC_GOT 2)
+
+   ;; The offset from the global offset table to the operand.
    (CRIS_UNSPEC_GOTREL 3)
-   (CRIS_UNSPEC_GOTREAD 4)
-   (CRIS_UNSPEC_PLTGOTREAD 5)
-   (CRIS_UNSPEC_SWAP_BITS 6)])
+
+   ;; The PC-relative offset to the operand.  It's arch-dependent whether
+   ;; the offset counts from the start or the end of the current item.
+   (CRIS_UNSPEC_PCREL 4)
+
+   ;; The index into the global offset table of a symbol, while
+   ;; also generating a GOT entry for the symbol.
+   (CRIS_UNSPEC_GOTREAD 5)
+
+   ;; Similar to CRIS_UNSPEC_GOTREAD, but also generating a PLT entry.
+   (CRIS_UNSPEC_PLTGOTREAD 6)
+
+   ;; Condition for v32 casesi jump, since it needs to have if_then_else
+   ;; form with register as one branch and default label as other.
+   ;; Operand 0 is const_int 0.
+   (CRIS_UNSPEC_CASESI 7)
+
+   ;; Stack frame deallocation barrier.
+   (CRIS_UNSPEC_FRAME_DEALLOC 8)
+
+   ;; Swap all 32 bits of the operand; 31 <=> 0, 30 <=> 1...
+   (CRIS_UNSPEC_SWAP_BITS 9)
+  ])
 
 ;; Register numbers.
 (define_constants
@@ -78,6 +103,7 @@ (define_constants
    (CRIS_STATIC_CHAIN_REGNUM 7)
    (CRIS_FP_REGNUM 8)
    (CRIS_SP_REGNUM 14)
+   (CRIS_ACR_REGNUM 15)
    (CRIS_SRP_REGNUM 16)
    (CRIS_MOF_REGNUM 17)
    (CRIS_AP_REGNUM 18)
@@ -100,14 +126,21 @@ (define_constants
 ;; In short, any "slottable" instruction must be 16 bit and not refer
 ;; to pc, or alter it.
 ;;
-;; The possible values are "yes", "no" and "has_slot".  Yes/no means if
-;; the insn is slottable or not.  Has_slot means that the insn is a
-;; return insn or branch insn (which are not considered slottable since
-;; that is generally true).  Having the seemingly illogical value
-;; "has_slot" means we do not have to add another attribute just to say
-;; that an insn has a delay-slot, since it also infers that it is not
-;; slottable.  Better names for the attribute were found to be longer and
-;; not add readability to the machine description.
+;; The possible values are "yes", "no", "has_slot", "has_return_slot"
+;; and "has_call_slot".
+;; Yes/no tells whether the insn is slottable or not.  Has_call_slot means
+;; that the insn is a call insn, which for CRIS v32 has a delay-slot.
+;; Of special concern is that no RTX_FRAME_RELATED insn must go in that
+;; call delay slot, as it's located in the address *after* the call insn,
+;; and the unwind machinery doesn't know about delay slots.
+;; Has_slot means that the insn is a branch insn (which are
+;; not considered slottable since that is generally true).  Having the
+;; seemingly illogical value "has_slot" means we do not have to add
+;; another attribute just to say that an insn has a delay-slot, since it
+;; also infers that it is not slottable.  Better names for the attribute
+;; were found to be longer and not add readability to the machine
+;; description.
+;; Has_return_slot is similar, for the return insn.
 ;;
 ;; The default that is defined here for this attribute is "no", not
 ;; slottable, not having a delay-slot, so there's no need to worry about
@@ -125,19 +158,20 @@ (define_constants
 ;; constraint pattern for the slottable pattern.  An alternative using
 ;; only "r" constraints is most often slottable.
 
-(define_attr "slottable" "no,yes,has_slot" (const_string "no"))
+(define_attr "slottable" "no,yes,has_slot,has_return_slot,has_call_slot"
+  (const_string "no"))
 
 ;; We also need attributes to sanely determine the condition code
 ;; state.  See cris_notice_update_cc for how this is used.
 
-(define_attr "cc" "none,clobber,normal" (const_string "normal"))
+(define_attr "cc" "none,clobber,normal,noov32,rev" (const_string "normal"))
 
 ;; At the moment, this attribute is just used to help bb-reorder do its
 ;; work; the default 0 doesn't help it.  Many insns have other lengths,
 ;; though none are shorter.
 (define_attr "length" "" (const_int 2))
 
-;; A branch or return has one delay-slot.  The instruction in the
+;; A branch has one delay-slot.  The instruction in the
 ;; delay-slot is always executed, independent of whether the branch is
 ;; taken or not.  Note that besides setting "slottable" to "has_slot",
 ;; there also has to be a "%#" at the end of a "delayed" instruction
@@ -147,6 +181,33 @@ (define_attr "length" "" (const_int 2))
 
 (define_delay (eq_attr "slottable" "has_slot")
   [(eq_attr "slottable" "yes") (nil) (nil)])
+
+;; We can't put prologue insns in call-insn delay-slots when
+;; DWARF2 unwind info is emitted, because the unwinder matches the
+;; address after the insn.  It must see the return address of a call at
+;; a position at least *one byte after* the insn, or it'll think that
+;; the insn hasn't been executed.  If the insn is in a delay-slot of a
+;; call, it's just *exactly* after the insn.
+
+(define_delay (eq_attr "slottable" "has_call_slot")
+  [(and (eq_attr "slottable" "yes")
+	(ior (eq (symbol_ref "RTX_FRAME_RELATED_P (insn)")
+		 (const_int 0))
+	     (eq (symbol_ref "flag_exceptions")
+		 (const_int 0))))
+   (nil) (nil)])
+
+;; The insn in the return insn slot must not be the
+;; return-address-register restore.  FIXME: Use has_slot and express
+;; as a parallel with a use of the return-address-register (currently
+;; only SRP).  However, this requires an amount of fixing tests for
+;; naked RETURN in middle-end.
+(define_delay (eq_attr "slottable" "has_return_slot")
+  [(and (eq_attr "slottable" "yes")
+	(eq (symbol_ref "dead_or_set_regno_p (insn, CRIS_SRP_REGNUM)")
+	    (const_int 0)))
+   (nil) (nil)])
+
 \f
 ;; Iterator definitions.
 
@@ -192,26 +253,70 @@ (define_code_attr roCC [(lt "pl") (ge "m
 ;; Allow register and offsettable mem operands only; post-increment is
 ;; not worth the trouble.
 
-(define_insn "tstdi"
+(define_expand "tstdi"
+  [(set (cc0) (match_operand:DI 0 "nonimmediate_operand"))]
+  ""
+{
+  if (TARGET_V32 && MEM_P (operands[0]))
+    operands[0] = force_reg (DImode, operands[0]);
+})
+
+(define_insn "*tstdi_non_v32"
   [(set (cc0)
 	(match_operand:DI 0 "nonimmediate_operand" "r,o"))]
-  ""
+  "!TARGET_V32"
   "test.d %M0\;ax\;test.d %H0")
 
+(define_insn "*tstdi_v32"
+  [(set (cc0)
+	(match_operand:DI 0 "register_operand" "r"))]
+  "TARGET_V32"
+  "cmpq 0,%M0\;ax\;cmpq 0,%H0")
+
 ;; No test insns with side-effect on the mem addressing.
 ;;
 ;; See note on cmp-insns with side-effects (or lack of them)
 
 ;; Normal named test patterns from SI on.
-;; FIXME: Seems they should change to be in order smallest..largest.
 
-(define_insn "tst<mode>"
+(define_insn "tstsi"
   [(set (cc0)
-	(match_operand:BWD 0 "nonimmediate_operand" "r,Q>,m"))]
+	(match_operand:SI 0 "nonimmediate_operand" "r,Q>,m"))]
   ""
-  "test<m> %0"
+{
+  if (which_alternative == 0 && TARGET_V32)
+    return "cmpq 0,%0";
+  return "test.d %0";
+}
   [(set_attr "slottable" "yes,yes,no")])
 
+(define_expand "tst<mode>"
+  [(set (cc0)
+	(match_operand:BW 0 "nonimmediate_operand"))]
+  ""
+  "")
+
+(define_insn "*tst<mode>_cmp"
+  [(set (cc0)
+	(match_operand:BW 0 "nonimmediate_operand" "r,Q>,m"))]
+  "cris_cc0_user_requires_cmp (insn)"
+  "@
+   cmp<m> 0,%0
+   test<m> %0
+   test<m> %0"
+  [(set_attr "slottable" "no,yes,no")])
+
+(define_insn "*tst<mode>_non_cmp"
+  [(set (cc0)
+	(match_operand:BW 0 "nonimmediate_operand" "r,Q>,m"))]
+  "!cris_cc0_user_requires_cmp (insn)"
+  "@
+   move<m> %0,%0
+   test<m> %0
+   test<m> %0"
+  [(set_attr "slottable" "yes,yes,no")
+   (set_attr "cc" "noov32,*,*")])
+
 ;; It seems that the position of the sign-bit and the fact that 0.0 is
 ;; all 0-bits would make "tstsf" a straight-forward implementation;
 ;; either "test.d" it for positive/negative or "btstq 30,r" it for
@@ -227,11 +332,23 @@ (define_insn "tst<mode>"
 ;; DImode for anything else but a structure/block-mode.  Just do the
 ;; obvious stuff for the straight-forward constraint letters.
 
-(define_insn "cmpdi"
+(define_expand "cmpdi"
+  [(set (cc0)
+	(compare (match_operand:DI 0 "nonimmediate_operand" "")
+		 (match_operand:DI 1 "general_operand" "")))]
+  ""
+{
+  if (TARGET_V32 && !REG_P (operands[0]))
+    operands[0] = force_reg (DImode, operands[0]);
+  if (TARGET_V32 && MEM_P (operands[1]))
+    operands[1] = force_reg (DImode, operands[1]);
+})
+
+(define_insn "*cmpdi_non_v32"
   [(set (cc0)
 	(compare (match_operand:DI 0 "nonimmediate_operand" "r,r,r,r,r,r,o")
 		 (match_operand:DI 1 "general_operand" "K,I,P,n,r,o,r")))]
-  ""
+  "!TARGET_V32"
   "@
    cmpq %1,%M0\;ax\;cmpq 0,%H0
    cmpq %1,%M0\;ax\;cmpq -1,%H0
@@ -241,6 +358,18 @@ (define_insn "cmpdi"
    cmp.d %M1,%M0\;ax\;cmp.d %H1,%H0
    cmp.d %M0,%M1\;ax\;cmp.d %H0,%H1")
 
+(define_insn "*cmpdi_v32"
+  [(set (cc0)
+	(compare (match_operand:DI 0 "register_operand"  "r,r,r,r,r")
+		 (match_operand:DI 1 "nonmemory_operand" "K,I,P,n,r")))]
+  "TARGET_V32"
+  "@
+   cmpq %1,%M0\;ax\;cmpq 0,%H0
+   cmpq %1,%M0\;ax\;cmpq -1,%H0
+   cmp%e1.%z1 %1,%M0\;ax\;cmpq %H1,%H0
+   cmp.d %M1,%M0\;ax\;cmp.d %H1,%H0
+   cmp.d %M1,%M0\;ax\;cmp.d %H1,%H0")
+
 ;; Note that compare insns with side effect addressing mode (e.g.):
 ;;
 ;; cmp.S [rx=ry+i],rz;
@@ -281,47 +410,43 @@ (define_insn "*cmp_swapext<mode>"
 			    [(match_operand:BW 0 "memory_operand" "Q>,m")])
 	 (match_operand:SI 1 "register_operand" "r,r")))]
   ""
-  "cmp%e2<m> %0,%1" ; The function cris_notice_update_cc knows about
-		     ; swapped operands to compares.
-  [(set_attr "slottable" "yes,no")])
+  "cmp%e2<m> %0,%1"
+  [(set_attr "slottable" "yes,no")
+   (set_attr "cc" "rev")])
 \f
-;; The "normal" compare patterns, from SI on.
+;; The "normal" compare patterns, from SI on.  Special-cases with zero
+;; should not happen.
 
 (define_insn "cmpsi"
   [(set (cc0)
 	(compare
-	 (match_operand:SI 0 "nonimmediate_operand" "r,r,r, r,Q>,Q>,r,r,m,m")
-	 (match_operand:SI 1 "general_operand"	    "I,r,Q>,M,M, r, P,g,M,r")))]
+	 (match_operand:SI 0 "nonimmediate_operand" "r,r,r, Q>,r,r,m")
+	 (match_operand:SI 1 "general_operand"	    "I,r,Q>,r, P,g,r")))]
   ""
   "@
    cmpq %1,%0
    cmp.d %1,%0
    cmp.d %1,%0
-   test.d %0
-   test.d %0
    cmp.d %0,%1
    cmp%e1.%z1 %1,%0
    cmp.d %1,%0
-   test.d %0
    cmp.d %0,%1"
-  [(set_attr "slottable" "yes,yes,yes,yes,yes,yes,no,no,no,no")])
+  [(set_attr "slottable" "yes,yes,yes,yes,no,no,no")
+   (set_attr "cc" "normal,normal,normal,rev,normal,normal,rev")])
 
 (define_insn "cmp<mode>"
   [(set (cc0)
-	(compare
-	 (match_operand:BW 0 "nonimmediate_operand" "r,r, r,Q>,Q>,r,m,m")
-	 (match_operand:BW 1 "general_operand"	    "r,Q>,M,M, r, g,M,r")))]
+	(compare (match_operand:BW 0 "nonimmediate_operand" "r,r, Q>,r,m")
+		 (match_operand:BW 1 "general_operand"	    "r,Q>,r, g,r")))]
   ""
   "@
    cmp<m> %1,%0
    cmp<m> %1,%0
-   test<m> %0
-   test<m> %0
    cmp<m> %0,%1
    cmp<m> %1,%0
-   test<m> %0
    cmp<m> %0,%1"
-  [(set_attr "slottable" "yes,yes,yes,yes,yes,no,no,no")])
+  [(set_attr "slottable" "yes,yes,yes,no,no")
+   (set_attr "cc" "normal,normal,rev,normal,rev")])
 \f
 ;; Pattern matching the BTST insn.
 ;; It is useful for "if (i & val)" constructs, where val is an exact
@@ -337,15 +462,17 @@ (define_insn "*btst"
 	 (match_operand:SI 1 "const_int_operand" "K,n,K,n,K,n,n")
 	 (match_operand:SI 2 "nonmemory_operand" "M,M,K,n,r,r,r")))]
   ;; Either it is a single bit, or consecutive ones starting at 0.
+  ;; The btst ones depend on stuff in NOTICE_UPDATE_CC.
   "CONST_INT_P (operands[1])
    && (operands[1] == const1_rtx || operands[2] == const0_rtx)
    && (REG_S_P (operands[0])
        || (operands[1] == const1_rtx
 	   && REG_S_P (operands[2])
 	   && CONST_INT_P (operands[0])
-	   && exact_log2 (INTVAL (operands[0])) >= 0))"
+	   && exact_log2 (INTVAL (operands[0])) >= 0))
+   && !TARGET_CCINIT"
 
-;; The last "&&" condition above should be caught by some kind of
+;; The next-to-last "&&" condition above should be caught by some kind of
 ;; canonicalization in gcc, but we can easily help with it here.
 ;;  It results from expressions of the type
 ;; "power_of_2_value & (1 << y)".
@@ -357,13 +484,14 @@ (define_insn "*btst"
 
   "@
    btstq (%1-1),%0
-   test.d %0
+   cmpq 0,%0
    btstq %2,%0
    clearf nz
    btst %2,%0
    clearf nz
    cmpq %p0,%2"
- [(set_attr "slottable" "yes")])
+ [(set_attr "slottable" "yes")
+  (set_attr "cc" "noov32")])
 \f
 ;; Move insns.
 
@@ -409,7 +537,9 @@ (define_expand "movdi"
 	(match_operand:DI 1 "general_operand" ""))]
   ""
 {
-  if (MEM_P (operands[0]) && operands[1] != const0_rtx)
+  if (MEM_P (operands[0])
+      && operands[1] != const0_rtx
+      && (!TARGET_V32 || (!REG_P (operands[1]) && can_create_pseudo_p ())))
     operands[1] = copy_to_mode_reg (DImode, operands[1]);
 
   /* Some other ports (as of 2001-09-10 for example mcore and romp) also
@@ -441,18 +571,64 @@ (define_expand "movdi"
     }
 })
 
-(define_insn "*movdi_insn"
+(define_insn_and_split "*movdi_insn_non_v32"
   [(set (match_operand:DI 0 "nonimmediate_operand" "=r,rx,m")
-	(match_operand:DI 1 "general_operand" "rx,g,rxM"))]
-  "register_operand (operands[0], DImode)
-   || register_operand (operands[1], DImode)
-   || operands[1] == const0_rtx"
-  "#")
+	(match_operand:DI 1 "general_operand"	   "rx,g,rxM"))]
+  "(register_operand (operands[0], DImode)
+    || register_operand (operands[1], DImode)
+    || operands[1] == const0_rtx)
+   && !TARGET_V32"
+  "#"
+  "&& reload_completed"
+  [(match_dup 2)]
+  "operands[2] = cris_split_movdx (operands);")
+
+;; Overlapping (but non-identical) source memory address and destination
+;; register would be a compiler bug, so we don't have to specify that.
+(define_insn "*movdi_v32"
+  [(set
+    (match_operand:DI 0 "nonimmediate_operand" "=r,rx,&r,>, m,r,x,m")
+    (match_operand:DI 1 "general_operand"     "rxi,r>,m, rx,r,m,m,x"))]
+  "TARGET_V32"
+{
+  switch (which_alternative)
+    {
+      /* FIXME: 1) Use autoincrement where possible.  2) Have peephole2,
+	 particularly for cases where the address register is dead.  */
+    case 5:
+      if (REGNO (operands[0]) == REGNO (XEXP (operands[1], 0)))
+	return "addq 4,%L1\;move.d %1,%H0\;subq 4,%L1\;move.d %1,%M0";
+      gcc_assert (REGNO (operands[0]) + 1 == REGNO (XEXP (operands[1], 0)));
+      return "move.d [%L1+],%M0\;move.d [%L1],%H0";
+    case 2:
+      /* We could do away with the addq if we knew the address-register
+	 isn't ACR.  If we knew the address-register is dead, we could do
+	 away with the subq too.  */
+      return "move.d [%L1],%M0\;addq 4,%L1\;move.d [%L1],%H0\;subq 4,%L1";
+    case 4:
+      return "move.d %M1,[%L0]\;addq 4,%L0\;move.d %H1,[%L0]\;subq 4,%L0";
+    case 6:
+      return "move [%L1],%M0\;addq 4,%L1\;move [%L1],%H0\;subq 4,%L1";
+    case 7:
+      return "move %M1,[%L0]\;addq 4,%L0\;move %H1,[%L0]\;subq 4,%L0";
+
+    default:
+      return "#";
+    }
+}
+  ;; The non-split cases clobber cc0 because of their adds and subs.
+  ;; Beware that NOTICE_UPDATE_CC is called before the forced split happens.
+  [(set_attr "cc" "*,*,clobber,*,clobber,clobber,*,*")])
 
+;; Much like "*movdi_insn_non_v32".  Overlapping registers and constants
+;; is handled so much better in cris_split_movdx.
 (define_split
   [(set (match_operand:DI 0 "nonimmediate_operand" "")
 	(match_operand:DI 1 "general_operand" ""))]
-  "reload_completed"
+  "TARGET_V32
+   && reload_completed
+   && (!MEM_P (operands[0]) || !REG_P (XEXP (operands[0], 0)))
+   && (!MEM_P (operands[1]) || !REG_P (XEXP (operands[1], 0)))"
   [(match_dup 2)]
   "operands[2] = cris_split_movdx (operands);")
 \f
@@ -462,6 +638,7 @@ (define_split
 ;;
 ;; move.S1 [rx=ry+rz.S],rw avoiding when rx is ry, or rw is rx
 ;; FIXME: These could have anonymous mode for operand 0.
+;; FIXME: Special registers' alternatives too.
 
 (define_insn "*mov_side<mode>_biap"
   [(set (match_operand:BW 0 "register_operand" "=r,r")
@@ -800,7 +977,7 @@ (define_expand "movsi"
      offset?  */
     if (flag_pic
 	&& CONSTANT_ADDRESS_P (operands[1])
-	&& !cris_valid_pic_const (operands[1]))
+	&& !cris_valid_pic_const (operands[1], false))
       {
 	enum cris_pic_symbol_type t = cris_pic_symbol_type_of (operands[1]);
 
@@ -819,26 +996,46 @@ (define_expand "movsi"
 	       destination register for the symbol.  It might not be
 	       worth it.  Measure.  */
 	    current_function_uses_pic_offset_table = 1;
-	    if (t == cris_gotrel_symbol)
+	    if (t == cris_rel_symbol)
 	      {
 		/* Change a "move.d sym(+offs),rN" into (allocate register rM)
+		   for pre-v32:
 		   "move.d (const (plus (unspec [sym]
-		    CRIS_UNSPEC_GOTREL) offs)),rM" "add.d rPIC,rM,rN"  */
+		    CRIS_UNSPEC_GOTREL) offs)),rM" "add.d rPIC,rM,rN"
+		   and for v32:
+		   "move.d (const (plus (unspec [sym]
+		    CRIS_UNSPEC_PCREL) offs)),rN".  */
 		rtx tem, rm, rn = operands[0];
 		rtx sym = GET_CODE (operands[1]) != CONST
 		  ? operands[1] : get_related_value (operands[1]);
 		HOST_WIDE_INT offs = get_integer_term (operands[1]);
 
 		gcc_assert (can_create_pseudo_p ());
-		tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym),
-				      CRIS_UNSPEC_GOTREL);
-		if (offs != 0)
-		  tem = plus_constant (tem, offs);
-		rm = gen_reg_rtx (Pmode);
-		emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
-	        if (expand_binop (Pmode, add_optab, rm, pic_offset_table_rtx,
-				  rn, 0, OPTAB_LIB_WIDEN) != rn)
-		  internal_error ("expand_binop failed in movsi gotrel");
+
+		if (TARGET_V32)
+		  {
+		    tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym),
+					  CRIS_UNSPEC_PCREL);
+		    if (offs != 0)
+		      tem = plus_constant (tem, offs);
+		    rm = rn;
+		    emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
+		  }
+		else
+		  {
+		    /* We still uses GOT-relative addressing for
+		       pre-v32.	 */
+		    current_function_uses_pic_offset_table = 1;
+		    tem = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, sym),
+					  CRIS_UNSPEC_GOTREL);
+		    if (offs != 0)
+		      tem = plus_constant (tem, offs);
+		    rm = gen_reg_rtx (Pmode);
+		    emit_move_insn (rm, gen_rtx_CONST (Pmode, tem));
+		    if (expand_binop (Pmode, add_optab, rm, pic_offset_table_rtx,
+				      rn, 0, OPTAB_LIB_WIDEN) != rn)
+		      internal_error ("expand_binop failed in movsi gotrel");
+		  }
 		DONE;
 	      }
 	    else if (t == cris_got_symbol)
@@ -863,6 +1060,12 @@ (define_expand "movsi"
 		   aliases other same symbols is unimportant.  */
 		set_mem_alias_set (mem, new_alias_set ());
 		MEM_NOTRAP_P (mem) = 1;
+
+		/* We can set the GOT memory read of a non-called symbol
+		   to readonly, but not that of a call symbol, as those
+		   are subject to lazy evaluation and usually have the value
+		   changed from the first call to the second (but
+		   constant thereafter).  */
 		MEM_READONLY_P (mem) = 1;
 		emit_move_insn (rn, mem);
 		DONE;
@@ -897,15 +1100,21 @@ (define_expand "movsi"
 (define_insn "*movsi_got_load"
   [(set (reg:SI CRIS_GOT_REGNUM) (unspec:SI [(const_int 0)] CRIS_UNSPEC_GOT))]
   "flag_pic"
-  "move.d $pc,%:\;sub.d .:GOTOFF,%:"
+{
+  return TARGET_V32
+    ? "lapc _GLOBAL_OFFSET_TABLE_,%:"
+    : "move.d $pc,%:\;sub.d .:GOTOFF,%:";
+}
   [(set_attr "cc" "clobber")])
 
 (define_insn "*movsi_internal"
   [(set
-    (match_operand:SI 0 "nonimmediate_operand" "=r,r, r,Q>,r,Q>,g,r,r, r,g,rQ>,x,  m,x")
+    (match_operand:SI 0 "nonimmediate_operand"
+		      "=r,r, r,Q>,r,Q>,g,r,r, r,g,rQ>,x,  m,x")
+    (match_operand:SI 1 "cris_general_operand_or_pic_source"
+		       "r,Q>,M,M, I,r, M,n,!S,g,r,x,  rQ>,x,gi"))]
     ;; Note that we prefer not to use the S alternative (if for some reason
-    ;; it competes with others), but g matches S.
-    (match_operand:SI 1 "general_operand"	"r,Q>,M,M, I,r, M,n,!S,g,r,x,  rQ>,x,gi"))]
+    ;; it competes with others) above, but g matches S.
   ""
 {
   /* Better to have c-switch here; it is worth it to optimize the size of
@@ -913,10 +1122,31 @@ (define_insn "*movsi_internal"
      letters.  FIXME: Check again.  It seems this could shrink a bit.  */
   switch (which_alternative)
     {
+    case 9:
+      if (TARGET_V32)
+       {
+	 if (!flag_pic
+	     && (GET_CODE (operands[1]) == SYMBOL_REF
+		 || GET_CODE (operands[1]) == LABEL_REF
+		 || GET_CODE (operands[1]) == CONST))
+	   {
+	     /* FIXME: Express this through (set_attr cc none) instead,
+		since we can't express the ``none'' at this point.  FIXME:
+		Use lapc for everything except const_int and when next cc0
+		user would want the flag setting.  */
+	     CC_STATUS_INIT;
+	     return "lapc %1,%0";
+	   }
+	 if (flag_pic == 1
+	     && GET_CODE (operands[1]) == CONST
+	     && GET_CODE (XEXP (operands[1], 0)) == UNSPEC
+	     && XINT (XEXP (operands[1], 0), 1) == CRIS_UNSPEC_GOTREAD)
+	   return "movu.w %1,%0";
+       }
+       /* FALLTHROUGH */
     case 0:
     case 1:
     case 5:
-    case 9:
     case 10:
       return "move.d %1,%0";
 
@@ -958,7 +1188,8 @@ (define_insn "*movsi_internal"
 	tem = XEXP (tem, 0);
 	if (GET_CODE (tem) == PLUS
 	    && GET_CODE (XEXP (tem, 0)) == UNSPEC
-	    && XINT (XEXP (tem, 0), 1) == CRIS_UNSPEC_GOTREL
+	    && (XINT (XEXP (tem, 0), 1) == CRIS_UNSPEC_GOTREL
+		|| XINT (XEXP (tem, 0), 1) == CRIS_UNSPEC_PCREL)
 	    && CONST_INT_P (XEXP (tem, 1)))
 	  tem = XEXP (tem, 0);
 	gcc_assert (GET_CODE (tem) == UNSPEC);
@@ -970,10 +1201,18 @@ (define_insn "*movsi_internal"
 	       indexed addressing mode.  */
 	    if (flag_pic == 1)
 	      return "movs.w %1,%0";
+	    return "move.d %1,%0";
+
 	  case CRIS_UNSPEC_GOTREL:
-	  case CRIS_UNSPEC_PLT:
+	  case CRIS_UNSPEC_PLT_GOTREL:
+	    gcc_assert (!TARGET_V32);
 	    return "move.d %1,%0";
 
+	  case CRIS_UNSPEC_PCREL:
+	  case CRIS_UNSPEC_PLT_PCREL:
+	    gcc_assert (TARGET_V32);
+	    return "lapc %1,%0";
+
 	  default:
 	    gcc_unreachable ();
 	  }
@@ -1218,9 +1457,69 @@ (define_insn "movsf"
    move %1,%0
    move %1,%0"
   [(set_attr "slottable" "yes,yes,yes,yes,yes,no,no,no,yes,yes,yes,no,yes,no")])
+\f
+;; Movem patterns.  Primarily for use in function prologue and epilogue.
+;; The V32 variants have an ordering matching the expectations of the
+;; standard names "load_multiple" and "store_multiple"; pre-v32 movem
+;; store R0 in the highest memory location.
+
+(define_expand "load_multiple"
+  [(match_operand:SI 0 "register_operand" "")
+   (match_operand:SI 1 "memory_operand" "")
+   (match_operand:SI 2 "const_int_operand" "")]
+  "TARGET_V32"
+{
+  rtx indreg;
+
+  /* Apparently the predicate isn't checked, so we need to do so
+     manually.  Once happened for libstdc++-v3 locale_facets.tcc.  */
+  if (!MEM_P (operands[1]))
+    FAIL;
+
+  indreg = XEXP (operands[1], 0);
+
+  if (GET_CODE (indreg) == POST_INC)
+    indreg = XEXP (indreg, 0);
+  if (!REG_P (indreg)
+      || GET_CODE (operands[2]) != CONST_INT
+      || !REG_P (operands[0])
+      || REGNO (operands[0]) != 0
+      || INTVAL (operands[2]) > CRIS_SP_REGNUM
+      || (int) REGNO (indreg) < INTVAL (operands[2]))
+    FAIL;
+  gcc_unreachable ();
+  emit_insn (cris_gen_movem_load (operands[1], operands[2], 0));
+  DONE;
+})
+
+(define_expand "store_multiple"
+  [(match_operand:SI 0 "memory_operand" "")
+   (match_operand:SI 1 "register_operand" "")
+   (match_operand:SI 2 "const_int_operand" "")]
+  "TARGET_V32"
+{
+  rtx indreg;
+
+  /* See load_multiple.  */
+  if (!MEM_P (operands[0]))
+    FAIL;
+
+  indreg = XEXP (operands[0], 0);
+
+  if (GET_CODE (indreg) == POST_INC)
+    indreg = XEXP (indreg, 0);
+  if (!REG_P (indreg)
+      || GET_CODE (operands[2]) != CONST_INT
+      || !REG_P (operands[1])
+      || REGNO (operands[1]) != 0
+      || INTVAL (operands[2]) > CRIS_SP_REGNUM
+      || (int) REGNO (indreg) < INTVAL (operands[2]))
+    FAIL;
+  gcc_unreachable ();
+  cris_emit_movem_store (operands[0], operands[2], 0, false);
+  DONE;
+})
 
-;; Note that the memory layout of the registers is the reverse of that
-;; of the standard patterns "load_multiple" and "store_multiple".
 (define_insn "*cris_load_multiple"
   [(match_parallel 0 "cris_load_multiple_op"
 		   [(set (match_operand:SI 1 "register_operand" "=r,r")
@@ -1421,11 +1720,21 @@ (define_insn "*op_swap_side<mode>"
 ;; Note that for the 'P' constraint, the high part can be -1 or 0.  We
 ;; output the insn through the 'A' output modifier as "adds.w" and "addq",
 ;; respectively.
-(define_insn "adddi3"
+(define_expand "adddi3"
+  [(set (match_operand:DI 0 "register_operand")
+	(plus:DI (match_operand:DI 1 "register_operand")
+		 (match_operand:DI 2 "general_operand")))]
+  ""
+{
+  if (MEM_P (operands[2]) && TARGET_V32)
+    operands[2] = force_reg (DImode, operands[2]);
+})
+
+(define_insn "*adddi3_non_v32"
   [(set (match_operand:DI 0 "register_operand" "=r,r,r,&r,&r")
 	(plus:DI (match_operand:DI 1 "register_operand" "%0,0,0,0,r")
 		 (match_operand:DI 2 "general_operand" "J,N,P,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    addq %2,%M0\;ax\;addq 0,%H0
    subq %n2,%M0\;ax\;subq 0,%H0
@@ -1433,7 +1742,29 @@ (define_insn "adddi3"
    add.d %M2,%M0\;ax\;add.d %H2,%H0
    add.d %M2,%M1,%M0\;ax\;add.d %H2,%H1,%H0")
 
-(define_insn "addsi3"
+; It seems no use allowing a memory operand for this one, because we'd
+; need a scratch register for incrementing the address.
+(define_insn "*adddi3_v32"
+  [(set (match_operand:DI 0 "register_operand" "=r,r,r,r,r")
+       (plus:DI (match_operand:DI 1 "register_operand" "%0,0,0,0,0")
+                (match_operand:DI 2 "nonmemory_operand" "J,N,P,r,n")))]
+  "TARGET_V32"
+  "@
+   addq %2,%M0\;addc 0,%H0
+   subq %n2,%M0\;ax\;subq 0,%H0
+   add%e2.%z2 %2,%M0\;addc %H2,%H0
+   add.d %M2,%M0\;addc %H2,%H0
+   add.d %M2,%M0\;addc %H2,%H0")
+
+(define_expand "add<mode>3"
+  [(set (match_operand:BWD 0 "register_operand")
+	(plus:BWD
+	 (match_operand:BWD 1 "register_operand")
+	 (match_operand:BWD 2 "general_operand")))]
+  ""
+  "")
+
+(define_insn "*addsi3_non_v32"
   [(set (match_operand:SI 0 "register_operand"  "=r,r, r,r,r,r, r,r,  r")
 	(plus:SI
 	 (match_operand:SI 1 "register_operand" "%0,0, 0,0,0,0, 0,r,  r")
@@ -1444,7 +1775,7 @@ (define_insn "addsi3"
 ;; register as in destination.  This will happen after insn splitting.
 ;; gcc <= 2.7.2.  FIXME: Check for gcc-2.9x
 
- ""
+ "!TARGET_V32"
 {
   switch (which_alternative)
     {
@@ -1480,6 +1811,8 @@ (define_insn "addsi3"
 	tem = XEXP (tem, 0);
 	if (GET_CODE (tem) == PLUS
 	    && GET_CODE (XEXP (tem, 0)) == UNSPEC
+	    /* We don't allow CRIS_UNSPEC_PCREL here; we can't have a
+	       pc-relative operand in an add insn.  */
 	    && XINT (XEXP (tem, 0), 1) == CRIS_UNSPEC_GOTREL
 	    && CONST_INT_P (XEXP (tem, 1)))
 	  tem = XEXP (tem, 0);
@@ -1492,8 +1825,9 @@ (define_insn "addsi3"
 	       indexed addressing mode.  */
 	    if (flag_pic == 1)
 	      return "adds.w %2,%0";
-	    /* Fall through.  */
-	  case CRIS_UNSPEC_PLT:
+	    return "add.d %2,%0";
+
+	  case CRIS_UNSPEC_PLT_GOTREL:
 	  case CRIS_UNSPEC_GOTREL:
 	    return "add.d %2,%0";
 	  default:
@@ -1501,7 +1835,7 @@ (define_insn "addsi3"
 	  }
       }
     case 6:
-      return "add.d %2,%0";
+      return "add%u2 %2,%0";
     case 7:
       return "add.d %2,%1,%0";
     case 8:
@@ -1511,12 +1845,38 @@ (define_insn "addsi3"
     }
 }
  [(set_attr "slottable" "yes,yes,yes,yes,no,no,no,no,yes")])
+
+; FIXME: Check what's best: having the three-operand ACR alternative
+; before or after the corresponding-operand2 alternative.  Check for
+; *all* insns.  FIXME: constant constraint letter for -128..127.
+(define_insn "*addsi3_v32"
+  [(set (match_operand:SI 0 "register_operand"  "=r,!a,r,!a, r,r,!a,r,!a,r,r,r,!a")
+	(plus:SI
+	 (match_operand:SI 1 "register_operand" "%0,r, 0, r, 0,0,r, 0,r, 0,0,0,r")
+	 (match_operand:SI 2 "general_operand"  "r, r, Q>,Q>,J,N,NJ,L,L, P,n,g,g")))]
+  "TARGET_V32"
+  "@
+   add.d %2,%0
+   addi %2.b,%1,%0
+   add.d %2,%0
+   addo.d %2,%1,%0
+   addq %2,%0
+   subq %n2,%0
+   addoq %2,%1,%0
+   adds.w %2,%0
+   addo %2,%1,%0
+   addu.w %2,%0
+   add.d %2,%0
+   add%u2 %2,%0
+   addo.%Z2 %2,%1,%0"
+  [(set_attr "slottable" "yes,yes,yes,yes,yes,yes,yes,no,no,no,no,no,no")
+   (set_attr "cc" "*,none,*,none,*,*,none,*,*,*,*,*,none")])
 \f
-(define_insn "addhi3"
+(define_insn "*addhi3_non_v32"
   [(set (match_operand:HI 0 "register_operand"		"=r,r, r,r,r,r")
 	(plus:HI (match_operand:HI 1 "register_operand" "%0,0, 0,0,0,r")
 		 (match_operand:HI 2 "general_operand"   "r,Q>,J,N,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    add.w %2,%0
    add.w %2,%0
@@ -1527,11 +1887,30 @@ (define_insn "addhi3"
   [(set_attr "slottable" "yes,yes,yes,yes,no,no")
    (set_attr "cc" "normal,normal,clobber,clobber,normal,normal")])
 
-(define_insn "addqi3"
+(define_insn "*addhi3_v32"
+  [(set (match_operand:HI 0 "register_operand" "=r, !a,r,!a, r,r,!a,r,!a")
+	(plus:HI
+	 (match_operand:HI 1 "register_operand" "%0,r, 0, r, 0,0,r, 0,r")
+	 (match_operand:HI 2 "general_operand"  "r, r, Q>,Q>,J,N,NJ,g,g")))]
+  "TARGET_V32"
+  "@
+   add.w %2,%0
+   addi %2.b,%1,%0
+   add.w %2,%0
+   addo.w %2,%1,%0
+   addq %2,%0
+   subq %n2,%0
+   addoq %2,%1,%0
+   add.w %2,%0
+   addo.w %2,%1,%0"
+  [(set_attr "slottable" "yes,yes,yes,yes,yes,yes,yes,no,no")
+   (set_attr "cc" "*,none,*,none,clobber,clobber,none,*,none")])
+
+(define_insn "*addqi3_non_v32"
   [(set (match_operand:QI 0 "register_operand"		"=r,r, r,r,r,r,r")
 	(plus:QI (match_operand:QI 1 "register_operand" "%0,0, 0,0,0,0,r")
 		 (match_operand:QI 2 "general_operand"	 "r,Q>,J,N,O,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    add.b %2,%0
    add.b %2,%0
@@ -1542,6 +1921,26 @@ (define_insn "addqi3"
    add.b %2,%1,%0"
   [(set_attr "slottable" "yes,yes,yes,yes,yes,no,no")
    (set_attr "cc" "normal,normal,clobber,clobber,clobber,normal,normal")])
+
+(define_insn "*addqi3_v32"
+  [(set (match_operand:QI 0 "register_operand"  "=r,!a,r,!a, r,r,!a,r,r,!a")
+	(plus:QI
+	 (match_operand:QI 1 "register_operand" "%0,r, 0, r, 0,0,r, 0,0,r")
+	 (match_operand:QI 2 "general_operand"   "r,r, Q>,Q>,J,N,NJ,O,g,g")))]
+  "TARGET_V32"
+  "@
+   add.b %2,%0
+   addi %2.b,%1,%0
+   add.b %2,%0
+   addo.b %2,%1,%0
+   addq %2,%0
+   subq %n2,%0
+   addoq %2,%1,%0
+   subQ -%b2,%0
+   add.b %2,%0
+   addo.b %2,%1,%0"
+  [(set_attr "slottable" "yes,yes,yes,yes,yes,yes,yes,yes,no,no")
+   (set_attr "cc" "*,none,*,none,clobber,clobber,none,clobber,*,none")])
 \f
 ;; Subtract.
 ;;
@@ -1551,11 +1950,21 @@ (define_insn "addqi3"
 ;; Note that for the 'P' constraint, the high part can be -1 or 0.  We
 ;; output the insn through the 'D' output modifier as "subs.w" and "subq",
 ;; respectively.
-(define_insn "subdi3"
+(define_expand "subdi3"
+  [(set (match_operand:DI 0 "register_operand")
+	(minus:DI (match_operand:DI 1 "register_operand")
+		  (match_operand:DI 2 "general_operand")))]
+  ""
+{
+  if (TARGET_V32 && MEM_P (operands[2]))
+    operands[2] = force_reg (DImode, operands[2]);
+})
+
+(define_insn "*subdi3_non_v32"
   [(set (match_operand:DI 0 "register_operand" "=r,r,r,&r,&r")
 	(minus:DI (match_operand:DI 1 "register_operand" "0,0,0,0,r")
 		  (match_operand:DI 2 "general_operand" "J,N,P,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    subq %2,%M0\;ax\;subq 0,%H0
    addq %n2,%M0\;ax\;addq 0,%H0
@@ -1563,12 +1972,31 @@ (define_insn "subdi3"
    sub.d %M2,%M0\;ax\;sub.d %H2,%H0
    sub.d %M2,%M1,%M0\;ax\;sub.d %H2,%H1,%H0")
 
-(define_insn "subsi3"
+(define_insn "*subdi3_v32"
+  [(set (match_operand:DI 0 "register_operand" "=r,r,r,&r")
+	(minus:DI (match_operand:DI 1 "register_operand" "0,0,0,0")
+		  (match_operand:DI 2 "nonmemory_operand" "J,N,P,r")))]
+  "TARGET_V32"
+  "@
+   subq %2,%M0\;ax\;subq 0,%H0
+   addq %n2,%M0\;ax\;addq 0,%H0
+   sub%e2.%z2 %2,%M0\;ax\;%D2 %H2,%H0
+   sub.d %M2,%M0\;ax\;sub.d %H2,%H0")
+
+(define_expand "sub<mode>3"
+  [(set (match_operand:BWD 0 "register_operand")
+	(minus:BWD
+	 (match_operand:BWD 1 "register_operand")
+	 (match_operand:BWD 2 "general_operand")))]
+  ""
+  "")
+
+(define_insn "*subsi3_non_v32"
   [(set (match_operand:SI 0 "register_operand" "=r,r, r,r,r,r,r,r")
 	(minus:SI
 	 (match_operand:SI 1 "register_operand" "0,0, 0,0,0,0,0,r")
 	 (match_operand:SI 2 "general_operand"	"r,Q>,J,N,P,n,g,!To")))]
-  ""
+  "!TARGET_V32"
 
 ;; This does not do the optimal: "addu.w 65535,r0" when %2 is negative.
 ;; But then again, %2 should not be negative.
@@ -1583,12 +2011,28 @@ (define_insn "subsi3"
    sub.d %2,%0
    sub.d %2,%1,%0"
   [(set_attr "slottable" "yes,yes,yes,yes,no,no,no,no")])
+
+(define_insn "*subsi3_v32"
+  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r")
+       (minus:SI
+        (match_operand:SI 1 "register_operand" "0,0,0,0,0,0,0")
+        (match_operand:SI 2 "general_operand" "r,Q>,J,N,P,n,g")))]
+  "TARGET_V32"
+  "@
+   sub.d %2,%0
+   sub.d %2,%0
+   subq %2,%0
+   addq %n2,%0
+   sub%e2.%z2 %2,%0
+   sub.d %2,%0
+   sub.d %2,%0"
+  [(set_attr "slottable" "yes,yes,yes,yes,no,no,no")])
 \f
-(define_insn "sub<mode>3"
+(define_insn "*sub<mode>3_nonv32"
   [(set (match_operand:BW 0 "register_operand"		"=r,r, r,r,r,r")
 	(minus:BW (match_operand:BW 1 "register_operand" "0,0, 0,0,0,r")
 		  (match_operand:BW 2 "general_operand"  "r,Q>,J,N,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    sub<m> %2,%0
    sub<m> %2,%0
@@ -1598,6 +2042,20 @@ (define_insn "sub<mode>3"
    sub<m> %2,%1,%0"
   [(set_attr "slottable" "yes,yes,yes,yes,no,no")
    (set_attr "cc" "normal,normal,clobber,clobber,normal,normal")])
+
+(define_insn "*sub<mode>3_v32"
+  [(set (match_operand:BW 0 "register_operand" "=r,r,r,r,r")
+	(minus:BW (match_operand:BW 1 "register_operand" "0,0,0,0,0")
+		  (match_operand:BW 2 "general_operand" "r,Q>,J,N,g")))]
+  "TARGET_V32"
+  "@
+   sub<m> %2,%0
+   sub<m> %2,%0
+   subq %2,%0
+   addq %n2,%0
+   sub<m> %2,%0"
+  [(set_attr "slottable" "yes,yes,yes,yes,no")
+   (set_attr "cc" "normal,normal,clobber,clobber,normal")])
 \f
 ;; CRIS has some add/sub-with-sign/zero-extend instructions.
 ;;  Although these perform sign/zero-extension to SImode, they are
@@ -1720,7 +2178,7 @@ (define_insn "*extop<mode>si_side"
 \f
 
 ;; As with op.S we may have to add special pattern to match commuted
-;; operands to adds/addu  and bound
+;; operands to adds/addu and bound
 ;;
 ;; adds/addu/bound [rx=ry+rz.S]
 
@@ -1831,7 +2289,7 @@ (define_insn "*extop<mode>si_swap_side"
 ;; QImode to HImode
 ;; FIXME: GCC should widen.
 
-(define_insn "*extopqihi"
+(define_insn "*extopqihi_non_v32"
   [(set (match_operand:HI 0 "register_operand" "=r,r,r,r")
 	(match_operator:HI
 	 3 "cris_additive_operand_extend_operator"
@@ -1839,7 +2297,7 @@ (define_insn "*extopqihi"
 	  (match_operator:HI
 	   4 "cris_extend_operator"
 	   [(match_operand:QI 2 "nonimmediate_operand" "r,Q>,m,!To")])]))]
-  "GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
+  "!TARGET_V32 && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
    && (operands[1] != frame_pointer_rtx || GET_CODE (operands[3]) != PLUS)"
   "@
    %x3%E4.%m4 %2,%0
@@ -1849,9 +2307,22 @@ (define_insn "*extopqihi"
   [(set_attr "slottable" "yes,yes,no,no")
    (set_attr "cc" "clobber")])
 
+(define_insn "*extopqihi_v32"
+  [(set (match_operand:HI 0 "register_operand" "=r,r")
+	(match_operator:HI
+	 3 "cris_additive_operand_extend_operator"
+	 [(match_operand:HI 1 "register_operand" "0,0")
+	  (match_operator:HI
+	   4 "cris_extend_operator"
+	   [(match_operand:QI 2 "nonimmediate_operand" "r,m")])]))]
+  "TARGET_V32"
+  "%x3%e4.%m4 %2,%0"
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "clobber")])
+
 ;; QImode to SImode
 
-(define_insn "*extop<mode>si"
+(define_insn "*extop<mode>si_non_v32"
   [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
 	(match_operator:SI
 	 3 "cris_operand_extend_operator"
@@ -1859,7 +2330,8 @@ (define_insn "*extop<mode>si"
 	  (match_operator:SI
 	   4 "cris_extend_operator"
 	   [(match_operand:BW 2 "nonimmediate_operand" "r,Q>,m,!To")])]))]
-  "(GET_CODE (operands[3]) != UMIN || GET_CODE (operands[4]) == ZERO_EXTEND)
+  "!TARGET_V32
+   && (GET_CODE (operands[3]) != UMIN || GET_CODE (operands[4]) == ZERO_EXTEND)
    && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
    && (operands[1] != frame_pointer_rtx || GET_CODE (operands[3]) != PLUS)"
   "@
@@ -1868,21 +2340,32 @@ (define_insn "*extop<mode>si"
    %x3%E4<m> %2,%0
    %x3%E4<m> %2,%1,%0"
   [(set_attr "slottable" "yes,yes,no,no")])
-\f
 
+(define_insn "*extop<mode>si_v32"
+  [(set (match_operand:SI 0 "register_operand" "=r,r")
+	(match_operator:SI
+	 3 "cris_additive_operand_extend_operator"
+	 [(match_operand:SI 1 "register_operand" "0,0")
+	  (match_operator:SI
+	   4 "cris_extend_operator"
+	   [(match_operand:BW 2 "nonimmediate_operand" "r,m")])]))]
+  "TARGET_V32"
+  "%x3%e4.%m4 %2,%0"
+  [(set_attr "slottable" "yes")])
+\f
 ;; As with the side-effect patterns, may have to have swapped operands for add.
-;; FIXME: *should* be redundant to gcc.
+;; For commutative operands, these are the canonical forms.
 
 ;; QImode to HImode
 
-(define_insn "*extopqihi_swap"
+(define_insn "*addxqihi_swap_non_v32"
   [(set (match_operand:HI 0 "register_operand" "=r,r,r,r")
 	(plus:HI
 	 (match_operator:HI
 	  3 "cris_extend_operator"
 	  [(match_operand:QI 2 "nonimmediate_operand" "r,Q>,m,!To")])
 	 (match_operand:HI 1 "register_operand" "0,0,0,r")))]
-  "operands[1] != frame_pointer_rtx"
+  "!TARGET_V32 && operands[1] != frame_pointer_rtx"
   "@
    add%e3.b %2,%0
    add%e3.b %2,%0
@@ -1891,7 +2374,35 @@ (define_insn "*extopqihi_swap"
   [(set_attr "slottable" "yes,yes,no,no")
    (set_attr "cc" "clobber")])
 
-(define_insn "*extop<mode>si_swap"
+;; A case for v32, to catch the "addo" insn in addition to "adds".  We
+;; only care to match the canonical form; there should be no other.
+
+(define_insn "*addsbw_v32"
+  [(set (match_operand:HI 0 "register_operand" "=r,r,!a")
+	(plus:HI
+	 (sign_extend:HI
+	  (match_operand:QI 2 "nonimmediate_operand" "r,m,m"))
+	 (match_operand:HI 1 "register_operand" "0,0,r")))]
+  "TARGET_V32"
+  "@
+   adds.b %2,%0
+   adds.b %2,%0
+   addo.b %2,%1,%0"
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "clobber,clobber,none")])
+
+(define_insn "*addubw_v32"
+  [(set (match_operand:HI 0 "register_operand" "=r,r")
+	(plus:HI
+	 (zero_extend:HI
+	  (match_operand:QI 2 "nonimmediate_operand" "r,m"))
+	 (match_operand:HI 1 "register_operand" "0,0")))]
+  "TARGET_V32"
+  "addu.b %2,%0"
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "clobber")])
+
+(define_insn "*extop<mode>si_swap_non_v32"
   [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
 	(match_operator:SI
 	 4 "cris_plus_or_bound_operator"
@@ -1899,7 +2410,8 @@ (define_insn "*extop<mode>si_swap"
 	   3 "cris_extend_operator"
 	   [(match_operand:BW 2 "nonimmediate_operand" "r,Q>,m,!To")])
 	  (match_operand:SI 1 "register_operand" "0,0,0,r")]))]
-  "(GET_CODE (operands[4]) != UMIN || GET_CODE (operands[3]) == ZERO_EXTEND)
+  "!TARGET_V32
+   && (GET_CODE (operands[4]) != UMIN || GET_CODE (operands[3]) == ZERO_EXTEND)
    && operands[1] != frame_pointer_rtx"
   "@
    %x4%E3<m> %2,%0
@@ -1907,6 +2419,40 @@ (define_insn "*extop<mode>si_swap"
    %x4%E3<m> %2,%0
    %x4%E3<m> %2,%1,%0"
   [(set_attr "slottable" "yes,yes,no,no")])
+
+(define_insn "*adds<mode>_v32"
+  [(set (match_operand:SI 0 "register_operand" "=r,r,!a")
+	(plus:SI
+	 (sign_extend:SI
+	  (match_operand:BW 2 "nonimmediate_operand" "r,m,m"))
+	 (match_operand:SI 1 "register_operand" "0,0,r")))]
+  "TARGET_V32"
+  "@
+   adds<m> %2,%0
+   adds<m> %2,%0
+   addo<m> %2,%1,%0"
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "*,*,none")])
+
+(define_insn "*addu<mode>_v32"
+  [(set (match_operand:SI 0 "register_operand" "=r,r")
+       (plus:SI
+        (zero_extend:SI
+          (match_operand:BW 2 "nonimmediate_operand" "r,m"))
+        (match_operand:SI 1 "register_operand" "0,0")))]
+  "TARGET_V32 && operands[1] != frame_pointer_rtx"
+  "addu<m> %2,%0"
+  [(set_attr "slottable" "yes")])
+
+(define_insn "*bound<mode>_v32"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (umin:SI
+        (zero_extend:SI
+         (match_operand:BW 2 "register_operand" "r"))
+        (match_operand:SI 1 "register_operand" "0")))]
+  "TARGET_V32 && operands[1] != frame_pointer_rtx"
+  "bound<m> %2,%0"
+  [(set_attr "slottable" "yes")])
 \f
 ;; This is the special case when we use what corresponds to the
 ;; instruction above in "casesi".  Do *not* change it to use the generic
@@ -1940,11 +2486,29 @@ (define_insn "*casesi_adds_w"
 		  (pc))
 	 (label_ref (match_operand 2 "" ""))))
    (use (label_ref (match_operand 3 "" "")))]
-
-  "operands[0] != frame_pointer_rtx"
-
+  "!TARGET_V32 && operands[0] != frame_pointer_rtx"
   "adds.w [$pc+%0.w],$pc"
   [(set_attr "cc" "clobber")])
+
+;; For V32, we just have a jump, but we need to mark the table as used,
+;; and the jump insn must have the if_then_else form expected by core
+;; GCC.  Since we don't want to prolong the lifetime of the original
+;; index value, we compare against "unspec 0".  It's a pity we have to
+;; jump through to get the default label in place and to keep the jump
+;; table around.  FIXME: Look into it some time.
+
+(define_insn "*casesi_jump_v32"
+  [(set (pc)
+	(if_then_else
+	 (ltu (unspec [(const_int 0)] CRIS_UNSPEC_CASESI)
+	      (match_operand:SI 0 "const_int_operand" "n"))
+	 (match_operand:SI 1 "register_operand" "r")
+	 (label_ref (match_operand 2 "" ""))))
+   (use (label_ref (match_operand 3 "" "")))]
+  "TARGET_V32"
+  "jump %1%#"
+  [(set_attr "cc" "clobber")
+   (set_attr "slottable" "has_slot")])
 \f
 ;; Multiply instructions.
 
@@ -1984,18 +2548,24 @@ (define_insn "addi_mul"
 
 ;; The addi insn as it is normally used.
 
+;; Make the the ACR alternative taste bad enough to not choose it as a
+;; preference to avoid spilling problems (unwind-dw2-fde.c at build).
+;; FIXME: Revisit for new register allocator.
+
 (define_insn "*addi"
-  [(set (match_operand:SI 0 "register_operand" "=r")
+  [(set (match_operand:SI 0 "register_operand" "=r,!a")
 	(plus:SI
-	 (mult:SI (match_operand:SI 2 "register_operand" "r")
-		  (match_operand:SI 3 "const_int_operand" "n"))
-	 (match_operand:SI 1 "register_operand" "0")))]
+	 (mult:SI (match_operand:SI 2 "register_operand" "r,r")
+		  (match_operand:SI 3 "const_int_operand" "n,n"))
+	 (match_operand:SI 1 "register_operand" "0,r")))]
   "operands[0] != frame_pointer_rtx
    && operands[1] != frame_pointer_rtx
    && CONST_INT_P (operands[3])
    && (INTVAL (operands[3]) == 1
        || INTVAL (operands[3]) == 2 || INTVAL (operands[3]) == 4)"
-  "addi %2%T3,%0"
+  "@
+   addi %2%T3,%0
+   addi %2%T3,%1,%0"
   [(set_attr "slottable" "yes")
    (set_attr "cc" "none")])
 
@@ -2012,7 +2582,7 @@ (define_insn "mstep_shift"
 		  (match_operand:SI 2 "register_operand" "r"))
 	 (ashift:SI (match_operand:SI 3 "register_operand" "0")
 		    (const_int 1))))]
-  ""
+  "!TARGET_V32"
   "mstep %2,%0"
   [(set_attr "slottable" "yes")])
 
@@ -2030,7 +2600,8 @@ (define_insn "mstep_mul"
 		  (match_operand:SI 2 "register_operand" "r"))
 	 (mult:SI (match_operand:SI 3 "register_operand" "0")
 		  (const_int 2))))]
-  "operands[0] != frame_pointer_rtx
+  "!TARGET_V32
+   && operands[0] != frame_pointer_rtx
    && operands[1] != frame_pointer_rtx
    && operands[2] != frame_pointer_rtx
    && operands[3] != frame_pointer_rtx"
@@ -2107,7 +2678,7 @@ (define_insn "<u>mulsidi3"
 ;; a / 1000;}" and unsigned.  FIXME: Comment above was for 3.2, revisit.
 
 (define_insn "<su>mulsi3_highpart"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=h,h,?r,?r")
+  [(set (match_operand:SI 0 "register_operand" "=h,h,?r,?r")
 	(truncate:SI
 	 (lshiftrt:DI
 	  (mult:DI
@@ -2139,7 +2710,8 @@ (define_insn "dstep_shift"
 			(const_int 1))))]
   ""
   "dstep %2,%0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 
 ;; Here's a variant with mult instead of ashift.
 ;;
@@ -2161,7 +2733,8 @@ (define_insn "dstep_mul"
    && operands[2] != frame_pointer_rtx
    && operands[3] != frame_pointer_rtx"
   "dstep %2,%0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 \f
 ;; Logical operators.
 
@@ -2256,11 +2829,11 @@ (define_insn "*andsi_clear"
 ;; pressure (worse code).  That will hopefully change with an
 ;; improved reload pass.
 
-(define_insn "*expanded_andsi"
+(define_insn "*expanded_andsi_non_v32"
   [(set (match_operand:SI 0 "register_operand"	       "=r,r,r, r,r")
 	(and:SI (match_operand:SI 1 "register_operand" "%0,0,0, 0,r")
 		(match_operand:SI 2 "general_operand"   "I,r,Q>,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    andq %2,%0
    and.d %2,%0
@@ -2268,6 +2841,19 @@ (define_insn "*expanded_andsi"
    and.d %2,%0
    and.d %2,%1,%0"
   [(set_attr "slottable" "yes,yes,yes,no,no")])
+
+(define_insn "*expanded_andsi_v32"
+  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
+	(and:SI (match_operand:SI 1 "register_operand" "%0,0,0,0")
+		(match_operand:SI 2 "general_operand" "I,r,Q>,g")))]
+  "TARGET_V32"
+  "@
+   andq %2,%0
+   and.d %2,%0
+   and.d %2,%0
+   and.d %2,%0"
+  [(set_attr "slottable" "yes,yes,yes,no")
+   (set_attr "cc" "noov32")])
 \f
 ;; For both QI and HI we may use the quick patterns.  This results in
 ;; useless condition codes, but that is used rarely enough for it to
@@ -2331,7 +2917,7 @@ (define_insn "*andhi_clear"
 
 ;; Catch-all andhi3 pattern.
 
-(define_insn "*expanded_andhi"
+(define_insn "*expanded_andhi_non_v32"
   [(set (match_operand:HI 0 "register_operand"	       "=r,r,r, r,r,r,r")
 	(and:HI (match_operand:HI 1 "register_operand" "%0,0,0, 0,0,0,r")
 		(match_operand:HI 2 "general_operand"   "I,r,Q>,L,O,g,!To")))]
@@ -2341,7 +2927,7 @@ (define_insn "*expanded_andhi"
 ;; pressure (worse code).  That will hopefully change with an
 ;; improved reload pass.
 
-  ""
+  "!TARGET_V32"
   "@
    andq %2,%0
    and.w %2,%0
@@ -2353,14 +2939,29 @@ (define_insn "*expanded_andhi"
   [(set_attr "slottable" "yes,yes,yes,no,yes,no,no")
    (set_attr "cc" "clobber,normal,normal,normal,clobber,normal,normal")])
 
+(define_insn "*expanded_andhi_v32"
+  [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r")
+       (and:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0,0")
+               (match_operand:HI 2 "general_operand" "I,r,Q>,L,O,g")))]
+  "TARGET_V32"
+  "@
+   andq %2,%0
+   and.w %2,%0
+   and.w %2,%0
+   and.w %2,%0
+   anDq %b2,%0
+   and.w %2,%0"
+  [(set_attr "slottable" "yes,yes,yes,no,yes,no")
+   (set_attr "cc" "clobber,noov32,noov32,noov32,clobber,noov32")])
+
 ;; A strict_low_part pattern.
 
-(define_insn "*andhi_lowpart"
+(define_insn "*andhi_lowpart_non_v32"
   [(set (strict_low_part
 	 (match_operand:HI 0 "register_operand"	       "=r,r, r,r,r,r"))
 	(and:HI (match_operand:HI 1 "register_operand" "%0,0, 0,0,0,r")
 		(match_operand:HI 2 "general_operand"   "r,Q>,L,O,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    and.w %2,%0
    and.w %2,%0
@@ -2370,12 +2971,34 @@ (define_insn "*andhi_lowpart"
    and.w %2,%1,%0"
   [(set_attr "slottable" "yes,yes,no,yes,no,no")
    (set_attr "cc" "normal,normal,normal,clobber,normal,normal")])
-\f
-(define_insn "andqi3"
+
+(define_insn "*andhi_lowpart_v32"
+  [(set (strict_low_part
+	 (match_operand:HI 0 "register_operand" "=r,r,r,r,r"))
+	(and:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0")
+		(match_operand:HI 2 "general_operand" "r,Q>,L,O,g")))]
+  "TARGET_V32"
+  "@
+   and.w %2,%0
+   and.w %2,%0
+   and.w %2,%0
+   anDq %b2,%0
+   and.w %2,%0"
+  [(set_attr "slottable" "yes,yes,no,yes,no")
+   (set_attr "cc" "noov32,noov32,noov32,clobber,noov32")])
+\f
+(define_expand "andqi3"
+  [(set (match_operand:QI 0 "register_operand")
+	(and:QI (match_operand:QI 1 "register_operand")
+               (match_operand:QI 2 "general_operand")))]
+  ""
+  "")
+
+(define_insn "*andqi3_non_v32"
   [(set (match_operand:QI 0 "register_operand"	       "=r,r,r, r,r,r")
 	(and:QI (match_operand:QI 1 "register_operand" "%0,0,0, 0,0,r")
 		(match_operand:QI 2 "general_operand"   "I,r,Q>,O,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    andq %2,%0
    and.b %2,%0
@@ -2386,12 +3009,26 @@ (define_insn "andqi3"
   [(set_attr "slottable" "yes,yes,yes,yes,no,no")
    (set_attr "cc" "clobber,normal,normal,clobber,normal,normal")])
 
-(define_insn "*andqi_lowpart"
+(define_insn "*andqi3_v32"
+  [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r")
+	(and:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0")
+		(match_operand:QI 2 "general_operand" "I,r,Q>,O,g")))]
+  "TARGET_V32"
+  "@
+   andq %2,%0
+   and.b %2,%0
+   and.b %2,%0
+   andQ %b2,%0
+   and.b %2,%0"
+  [(set_attr "slottable" "yes,yes,yes,yes,no")
+   (set_attr "cc" "clobber,noov32,noov32,clobber,noov32")])
+
+(define_insn "*andqi_lowpart_non_v32"
   [(set (strict_low_part
 	 (match_operand:QI 0 "register_operand"	       "=r,r, r,r,r"))
 	(and:QI (match_operand:QI 1 "register_operand" "%0,0, 0,0,r")
 		(match_operand:QI 2 "general_operand"   "r,Q>,O,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    and.b %2,%0
    and.b %2,%0
@@ -2400,6 +3037,20 @@ (define_insn "*andqi_lowpart"
    and.b %2,%1,%0"
   [(set_attr "slottable" "yes,yes,yes,no,no")
    (set_attr "cc" "normal,normal,clobber,normal,normal")])
+
+(define_insn "*andqi_lowpart_v32"
+  [(set (strict_low_part
+	 (match_operand:QI 0 "register_operand" "=r,r,r,r"))
+	(and:QI (match_operand:QI 1 "register_operand" "%0,0,0,0")
+		(match_operand:QI 2 "general_operand" "r,Q>,O,g")))]
+  "TARGET_V32"
+  "@
+   and.b %2,%0
+   and.b %2,%0
+   andQ %b2,%0
+   and.b %2,%0"
+  [(set_attr "slottable" "yes,yes,yes,no")
+   (set_attr "cc" "noov32,noov32,clobber,noov32")])
 \f
 ;; Bitwise or.
 
@@ -2408,11 +3059,18 @@ (define_insn "*andqi_lowpart"
 ;; It seems there's no need to jump through hoops to get good code such as
 ;; with andsi3.
 
-(define_insn "iorsi3"
+(define_expand "ior<mode>3"
+  [(set (match_operand:BWD 0 "register_operand")
+	(ior:BWD (match_operand:BWD 1 "register_operand")
+		 (match_operand:BWD 2 "general_operand")))]
+  ""
+  "")
+
+(define_insn "*iorsi3_non_v32"
   [(set (match_operand:SI 0 "register_operand"	       "=r,r,r, r,r,r")
 	(ior:SI (match_operand:SI 1 "register_operand" "%0,0,0, 0,0,r")
 		(match_operand:SI 2 "general_operand"  "I, r,Q>,n,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    orq %2,%0
    or.d %2,%0
@@ -2423,11 +3081,25 @@ (define_insn "iorsi3"
   [(set_attr "slottable" "yes,yes,yes,no,no,no")
    (set_attr "cc" "normal,normal,normal,clobber,normal,normal")])
 
-(define_insn "iorhi3"
+(define_insn "*iorsi3_v32"
+  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r")
+	(ior:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0")
+		(match_operand:SI 2 "general_operand" "I,r,Q>,n,g")))]
+  "TARGET_V32"
+  "@
+   orq %2,%0
+   or.d %2,%0
+   or.d %2,%0
+   oR.%s2 %2,%0
+   or.d %2,%0"
+  [(set_attr "slottable" "yes,yes,yes,no,no")
+   (set_attr "cc" "noov32,noov32,noov32,clobber,noov32")])
+
+(define_insn "*iorhi3_non_v32"
   [(set (match_operand:HI 0 "register_operand"	       "=r,r,r, r,r,r,r")
 	(ior:HI (match_operand:HI 1 "register_operand" "%0,0,0, 0,0,0,r")
 		(match_operand:HI 2 "general_operand"   "I,r,Q>,L,O,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    orq %2,%0
    or.w %2,%0
@@ -2439,11 +3111,26 @@ (define_insn "iorhi3"
   [(set_attr "slottable" "yes,yes,yes,no,yes,no,no")
    (set_attr "cc" "clobber,normal,normal,normal,clobber,normal,normal")])
 
-(define_insn "iorqi3"
+(define_insn "*iorhi3_v32"
+  [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r")
+	(ior:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0,0")
+		(match_operand:HI 2 "general_operand" "I,r,Q>,L,O,g")))]
+  "TARGET_V32"
+  "@
+   orq %2,%0
+   or.w %2,%0
+   or.w %2,%0
+   or.w %2,%0
+   oRq %b2,%0
+   or.w %2,%0"
+  [(set_attr "slottable" "yes,yes,yes,no,yes,no")
+   (set_attr "cc" "clobber,noov32,noov32,noov32,clobber,noov32")])
+
+(define_insn "*iorqi3_non_v32"
   [(set (match_operand:QI 0 "register_operand"	       "=r,r,r, r,r,r")
 	(ior:QI (match_operand:QI 1 "register_operand" "%0,0,0, 0,0,r")
 		(match_operand:QI 2 "general_operand"   "I,r,Q>,O,g,!To")))]
-  ""
+  "!TARGET_V32"
   "@
    orq %2,%0
    or.b %2,%0
@@ -2453,6 +3140,20 @@ (define_insn "iorqi3"
    or.b %2,%1,%0"
   [(set_attr "slottable" "yes,yes,yes,yes,no,no")
    (set_attr "cc" "clobber,normal,normal,clobber,normal,normal")])
+
+(define_insn "*iorqi3_v32"
+  [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r")
+	(ior:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0")
+		(match_operand:QI 2 "general_operand" "I,r,Q>,O,g")))]
+  "TARGET_V32"
+  "@
+   orq %2,%0
+   or.b %2,%0
+   or.b %2,%0
+   orQ %b2,%0
+   or.b %2,%0"
+  [(set_attr "slottable" "yes,yes,yes,yes,no")
+   (set_attr "cc" "clobber,noov32,noov32,clobber,noov32")])
 \f
 ;; Exclusive-or
 
@@ -2465,7 +3166,8 @@ (define_insn "xorsi3"
 		(match_operand:SI 2 "register_operand" "r")))]
   ""
   "xor %2,%0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 
 (define_insn "xor<mode>3"
   [(set (match_operand:BW 0 "register_operand" "=r")
@@ -2522,7 +3224,8 @@ (define_insn "one_cmplsi2"
 	(not:SI (match_operand:SI 1 "register_operand" "0")))]
   ""
   "not %0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 
 (define_insn "one_cmpl<mode>2"
   [(set (match_operand:BW 0 "register_operand" "=r")
@@ -2545,7 +3248,8 @@ (define_insn "<shlr>si3"
 
   return "<slr>q %2,%0";
 }
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 
 ;; Since gcc gets lost, and forgets to zero-extend the source (or mask
 ;; the destination) when it changes shifts of lower modes into SImode,
@@ -2595,7 +3299,8 @@ (define_insn "*expanded_<shlr><mode>"
 		    (match_operand:BW 2 "register_operand" "r")))]
   ""
   "<slr><m> %2,%0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 
 (define_insn "*<shlr><mode>_lowpart"
   [(set (strict_low_part (match_operand:BW 0 "register_operand" "+r"))
@@ -2603,7 +3308,8 @@ (define_insn "*<shlr><mode>_lowpart"
 		    (match_operand:BW 1 "register_operand" "r")))]
   ""
   "<slr><m> %1,%0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 \f
 ;; Arithmetic/logical shift left.
 
@@ -2624,7 +3330,7 @@ (define_insn "ashl<mode>3"
        ? "lslq %2,%0" : "lsl<m> %2,%0");
 }
   [(set_attr "slottable" "yes")
-   (set_attr "cc" "normal,clobber")])
+   (set_attr "cc" "noov32,clobber")])
 
 ;; A strict_low_part matcher.
 
@@ -2634,7 +3340,8 @@ (define_insn "*ashl<mode>_lowpart"
 		   (match_operand:HI 1 "register_operand" "r")))]
   ""
   "lsl<m> %1,%0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 \f
 ;; Various strange insns that gcc likes.
 
@@ -2652,7 +3359,8 @@ (define_insn "abssi2"
 	(abs:SI (match_operand:SI 1 "register_operand" "r")))]
   ""
   "abs %1,%0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 
 ;; FIXME: GCC should be able to do these expansions itself.
 
@@ -2670,14 +3378,16 @@ (define_insn "clzsi2"
 	(clz:SI (match_operand:SI 1 "register_operand" "r")))]
   "TARGET_HAS_LZ"
   "lz %1,%0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 
 (define_insn "bswapsi2"
   [(set (match_operand:SI 0 "register_operand" "=r")
         (bswap:SI (match_operand:SI 1 "register_operand" "0")))]
   "TARGET_HAS_SWAP"
   "swapwb %0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 
 ;; This instruction swaps all bits in a register.
 ;; That means that the most significant bit is put in the place
@@ -2689,7 +3399,8 @@ (define_insn "cris_swap_bits"
 		   CRIS_UNSPEC_SWAP_BITS))]
   "TARGET_HAS_SWAP"
   "swapwbr %0"
-  [(set_attr "slottable" "yes")])
+  [(set_attr "slottable" "yes")
+   (set_attr "cc" "noov32")])
 
 ;; Implement ctz using two instructions, one for bit swap and one for clz.
 ;; Defines a scratch register to avoid clobbering input.
@@ -2708,11 +3419,21 @@ (define_expand "ctzsi2"
 ;; operation supported by gcc.  Used in casesi, but used now and then in
 ;; normal code too.
 
-(define_insn "uminsi3"
+(define_expand "uminsi3"
+  [(set (match_operand:SI 0 "register_operand" "")
+	(umin:SI  (match_operand:SI 1 "register_operand" "")
+		  (match_operand:SI 2 "general_operand" "")))]
+  ""
+{
+  if (MEM_P (operands[2]) && TARGET_V32)
+    operands[2] = force_reg (SImode, operands[2]);
+})
+
+(define_insn "*uminsi3_non_v32"
   [(set (match_operand:SI 0 "register_operand"		 "=r,r, r,r")
 	(umin:SI  (match_operand:SI 1 "register_operand" "%0,0, 0,r")
 		  (match_operand:SI 2 "general_operand"   "r,Q>,g,!To")))]
-  ""
+  "!TARGET_V32"
 {
   if (CONST_INT_P (operands[2]))
     {
@@ -2733,6 +3454,30 @@ (define_insn "uminsi3"
   return "bound.d %2,%0";
 }
  [(set_attr "slottable" "yes,yes,no,no")])
+
+(define_insn "*uminsi3_v32"
+  [(set (match_operand:SI 0 "register_operand" "=r,r")
+	(umin:SI  (match_operand:SI 1 "register_operand" "%0,0")
+		  (match_operand:SI 2 "nonmemory_operand" "r,i")))]
+  "TARGET_V32"
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    {
+      /* Constant operands are zero-extended, so only 32-bit operands
+	 may be negative.  */
+      if (INTVAL (operands[2]) >= 0)
+	{
+	  if (INTVAL (operands[2]) < 256)
+	    return "bound.b %2,%0";
+
+	  if (INTVAL (operands[2]) < 65536)
+	    return "bound.w %2,%0";
+	}
+    }
+
+  return "bound.d %2,%0";
+}
+ [(set_attr "slottable" "yes,no")])
 \f
 ;; Jump and branch insns.
 
@@ -2748,11 +3493,25 @@ (define_insn "jump"
 ;; jmp_uses_reg_or_mem used by computed_jump_p.  Perhaps it is a kludge to
 ;; change from general_operand to nonimmediate_operand (at least the docs
 ;; should be changed), but then again the pattern is called indirect_jump.
-(define_insn "indirect_jump"
-  [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))]
+(define_expand "indirect_jump"
+  [(set (pc) (match_operand:SI 0 "nonimmediate_operand"))]
   ""
+{
+  if (TARGET_V32 && MEM_P (operands[0]))
+    operands[0] = force_reg (SImode, operands[0]);
+})
+
+(define_insn "*indirect_jump_non_v32"
+  [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))]
+  "!TARGET_V32"
   "jump %0")
 
+(define_insn "*indirect_jump_v32"
+  [(set (pc) (match_operand:SI 0 "register_operand" "r"))]
+  "TARGET_V32"
+  "jump %0%#"
+  [(set_attr "slottable" "has_slot")])
+
 ;; Return insn.  Used whenever the epilogue is very simple; if it is only
 ;; a single ret or jump [sp+].  No allocated stack space or saved
 ;; registers are allowed.
@@ -2777,7 +3536,7 @@ (define_insn "*return_expanded"
 	      "(cris_return_address_on_stack_for_return ())")
  	     (const_int 0))
  	 (const_string "no")
-	 (const_string "has_slot")))])
+	 (const_string "has_return_slot")))])
 
 (define_expand "prologue"
   [(const_int 0)]
@@ -2935,14 +3694,27 @@ (define_expand "call"
 ;; Accept *anything* as operand 1.  Accept operands for operand 0 in
 ;; order of preference.
 
-(define_insn "*expanded_call"
-  [(call (mem:QI (match_operand:SI
-		  0 "cris_general_operand_or_plt_symbol" "r,Q>,g"))
+(define_insn "*expanded_call_non_v32"
+  [(call (mem:QI (match_operand:SI 0 "general_operand" "r,Q>,g"))
 	 (match_operand 1 "" ""))
    (clobber (reg:SI CRIS_SRP_REGNUM))]
-  ""
+  "!TARGET_V32"
   "jsr %0")
 
+(define_insn "*expanded_call_v32"
+  [(call
+    (mem:QI
+     (match_operand:SI 0 "cris_nonmemory_operand_or_callable_symbol" "n,r,U,i"))
+    (match_operand 1 "" ""))
+   (clobber (reg:SI CRIS_SRP_REGNUM))]
+  "TARGET_V32"
+  "@
+   jsr %0%#
+   jsr %0%#
+   bsr %0%#
+   bsr %0%#"
+  [(set_attr "slottable" "has_call_slot")])
+
 ;; Parallel when calculating and reusing address of indirect pointer
 ;; with simple offset.  (Makes most sense with PIC.)  It looks a bit
 ;; wrong not to have the clobber last, but that's the way combine
@@ -2958,7 +3730,7 @@ (define_insn "*expanded_call_side"
    (set (match_operand:SI 3 "register_operand" "=*0,r,r")
 	(plus:SI (match_dup 0)
 		 (match_dup 1)))]
-  "! TARGET_AVOID_GOTPLT"
+  "!TARGET_AVOID_GOTPLT && !TARGET_V32"
   "jsr [%3=%0%S1]")
 
 (define_expand "call_value"
@@ -2979,13 +3751,12 @@ (define_expand "call_value"
 ;;  We also accept a PLT symbol.  We output it as [rPIC+sym:GOTPLT] rather
 ;; than requiring getting rPIC + sym:PLT into a register.
 
-(define_insn "*expanded_call_value"
+(define_insn "*expanded_call_value_non_v32"
   [(set (match_operand 0 "nonimmediate_operand" "=g,g,g")
-	(call (mem:QI (match_operand:SI
-		       1 "cris_general_operand_or_plt_symbol" "r,Q>,g"))
+	(call (mem:QI (match_operand:SI 1 "general_operand" "r,Q>,g"))
 	      (match_operand 2 "" "")))
    (clobber (reg:SI CRIS_SRP_REGNUM))]
-  ""
+  "!TARGET_V32"
   "Jsr %1"
   [(set_attr "cc" "clobber")])
 
@@ -3002,10 +3773,27 @@ (define_insn "*expanded_call_value_side"
    (set (match_operand:SI 4 "register_operand" "=*1,r,r")
 	(plus:SI (match_dup 1)
 		 (match_dup 2)))]
-  "! TARGET_AVOID_GOTPLT"
+  "!TARGET_AVOID_GOTPLT && !TARGET_V32"
   "Jsr [%4=%1%S2]"
   [(set_attr "cc" "clobber")])
 
+(define_insn "*expanded_call_value_v32"
+  [(set
+    (match_operand 0 "nonimmediate_operand" "=g,g,g,g")
+    (call
+     (mem:QI
+      (match_operand:SI 1 "cris_nonmemory_operand_or_callable_symbol" "n,r,U,i"))
+     (match_operand 2 "" "")))
+   (clobber (reg:SI 16))]
+  "TARGET_V32"
+  "@
+   Jsr %1%#
+   Jsr %1%#
+   Bsr %1%#
+   Bsr %1%#"
+  [(set_attr "cc" "clobber")
+   (set_attr "slottable" "has_call_slot")])
+
 ;; Used in debugging.  No use for the direct pattern; unfilled
 ;; delayed-branches are taken care of by other means.
 
@@ -3036,7 +3824,7 @@ (define_insn "cris_frame_deallocated_bar
 ;; this expansion, you must change the macro ASM_OUTPUT_CASE_END
 ;; accordingly, to add the default case at the end of the jump-table.
 
-(define_expand "casesi"
+(define_expand "cris_casesi_non_v32"
   [(set (match_dup 5) (match_operand:SI 0 "general_operand" ""))
    (set (match_dup 6)
 	(minus:SI (match_dup 5)
@@ -3062,6 +3850,62 @@ (define_expand "casesi"
   operands[6] = gen_reg_rtx (SImode);
   operands[7] = gen_reg_rtx (SImode);
 })
+
+;; FIXME: Check effect of not JUMP_TABLES_IN_TEXT_SECTION.
+(define_expand "cris_casesi_v32"
+  [(set (match_dup 5) (match_operand:SI 0 "general_operand"))
+   (set (match_dup 6)
+       (minus:SI (match_dup 5)
+		 (match_operand:SI 1 "const_int_operand")))
+   (set (match_dup 7)
+       (umin:SI (match_dup 6)
+		(match_operand:SI 2 "const_int_operand")))
+   (set (match_dup 8) (match_dup 11))
+   (set (match_dup 9)
+       (plus:SI (mult:SI (match_dup 7) (const_int 2))
+		(match_dup 8)))
+   (set (match_dup 10)
+       (plus:SI (sign_extend:SI (mem:HI (match_dup 9)))
+		(match_dup 9)))
+   (parallel
+    [(set (pc)
+	 (if_then_else
+	  (ltu (unspec [(const_int 0)] CRIS_UNSPEC_CASESI) (match_dup 2))
+	  (match_dup 10)
+	  (label_ref (match_operand 4 "" ""))))
+     (use (label_ref (match_dup 3)))])]
+  "TARGET_V32"
+{
+  int i;
+  rtx xlabel = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
+  for (i = 5; i <= 10; i++)
+    operands[i] = gen_reg_rtx (SImode);
+  operands[2] = plus_constant (operands[2], 1);
+
+  /* Don't forget to decorate labels too, for PIC.  */
+  operands[11] = flag_pic
+    ? gen_rtx_CONST (Pmode,
+		    gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xlabel),
+				    CRIS_UNSPEC_PCREL))
+    : xlabel;
+})
+
+(define_expand "casesi"
+  [(match_operand:SI 0 "general_operand")
+   (match_operand:SI 1 "const_int_operand")
+   (match_operand:SI 2 "const_int_operand")
+   (match_operand 3 "" "")
+   (match_operand 4 "" "")]
+  ""
+{
+  if (TARGET_V32)
+    emit_insn (gen_cris_casesi_v32 (operands[0], operands[1], operands[2],
+				    operands[3], operands[4]));
+  else
+    emit_insn (gen_cris_casesi_non_v32 (operands[0], operands[1], operands[2],
+					operands[3], operands[4]));
+  DONE;
+})
 \f
 ;; Split-patterns.  Some of them have modes unspecified.  This
 ;; should always be ok; if for no other reason sparc.md has it as
@@ -3081,7 +3925,9 @@ (define_expand "casesi"
 ;;  move ry,rz
 ;;  op [rx],rz
 ;; Lose if rz=ry or rx=rz.
-;; Call this op-extend-split
+;; Call this op-extend-split.
+;; Do not match for V32; the addo and addi shouldn't be split
+;; up.
 
 (define_split
   [(set (match_operand 0 "register_operand" "")
@@ -3091,7 +3937,8 @@ (define_split
 	  (match_operator
 	   3 "cris_extend_operator"
 	   [(match_operand 2 "memory_operand" "")])]))]
-  "REG_P (operands[0])
+  "!TARGET_V32
+   && REG_P (operands[0])
    && REG_P (operands[1])
    && REGNO (operands[1]) != REGNO (operands[0])
    && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
@@ -3120,7 +3967,8 @@ (define_split
 	  (match_operator
 	   3 "cris_extend_operator"
 	   [(match_operand 2 "memory_operand" "")])]))]
-  "REG_P (operands[0])
+  "!TARGET_V32
+   && REG_P (operands[0])
    && REG_P (operands[1])
    && REGNO (operands[1]) != REGNO (operands[0])
    && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
@@ -3147,7 +3995,8 @@ (define_split
 	   3 "cris_extend_operator"
 	   [(match_operand 2 "memory_operand" "")])
 	  (match_operand 1 "register_operand" "")]))]
-  "REG_P (operands[0])
+  "!TARGET_V32
+   && REG_P (operands[0])
    && REG_P (operands[1])
    && REGNO (operands[1]) != REGNO (operands[0])
    && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
@@ -3172,7 +4021,8 @@ (define_split
 	   3 "cris_extend_operator"
 	   [(match_operand 2 "memory_operand" "")])
 	  (match_operand 1 "register_operand" "")]))]
-  "REG_P (operands[0])
+  "!TARGET_V32
+   && REG_P (operands[0])
    && REG_P (operands[1])
    && REGNO (operands[1]) != REGNO (operands[0])
    && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
@@ -3200,7 +4050,8 @@ (define_split
 	 3 "cris_orthogonal_operator"
 	 [(match_operand 1 "register_operand" "")
 	  (match_operand 2 "memory_operand" "")]))]
-  "REG_P (operands[0])
+  "!TARGET_V32
+   && REG_P (operands[0])
    && REG_P (operands[1])
    && REGNO (operands[1]) != REGNO (operands[0])
    && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
@@ -3223,7 +4074,8 @@ (define_split
 	 3 "cris_commutative_orth_op"
 	 [(match_operand 2 "memory_operand" "")
 	  (match_operand 1 "register_operand" "")]))]
-  "REG_P (operands[0])
+  "!TARGET_V32
+   && REG_P (operands[0])
    && REG_P (operands[1])
    && REGNO (operands[1]) != REGNO (operands[0])
    && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
@@ -3246,7 +4098,8 @@ (define_split
 	 3 "cris_commutative_orth_op"
 	 [(match_operand 1 "register_operand" "")
 	  (match_operand 2 "memory_operand" "")]))]
-  "REG_P (operands[0]) && REG_P (operands[1])
+  "!TARGET_V32
+   && REG_P (operands[0]) && REG_P (operands[1])
    && REGNO (operands[1]) != REGNO (operands[0])
    && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
    && REG_P (XEXP (operands[2], 0))
@@ -3268,7 +4121,8 @@ (define_split
 	 3 "cris_orthogonal_operator"
 	 [(match_operand 2 "memory_operand" "")
 	  (match_operand 1 "register_operand" "")]))]
-  "REG_P (operands[0]) && REG_P (operands[1])
+  "!TARGET_V32
+   && REG_P (operands[0]) && REG_P (operands[1])
    && REGNO (operands[1]) != REGNO (operands[0])
    && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
    && REG_P (XEXP (operands[2], 0))
@@ -3818,7 +4672,8 @@ (define_peephole2 ; moversideqi (peephol
    && (BASE_P (operands[1]) || BASE_P (operands[2]))
    && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')
    && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N')
-   && (INTVAL (operands[2]) >= -128 && INTVAL (operands[2]) < 128)"
+   && (INTVAL (operands[2]) >= -128 && INTVAL (operands[2]) < 128)
+   && TARGET_SIDE_EFFECT_PREFIXES"
   [(parallel
     [(set (match_dup 3) (match_dup 5))
      (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))])]
@@ -3853,7 +4708,8 @@ (define_peephole2 ; movemsideqi (peephol
    && (BASE_P (operands[1]) || BASE_P (operands[2]))
    && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')
    && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N')
-   && (INTVAL (operands[2]) >= -128 && INTVAL (operands[2]) < 128)"
+   && (INTVAL (operands[2]) >= -128 && INTVAL (operands[2]) < 128)
+   && TARGET_SIDE_EFFECT_PREFIXES"
   [(parallel
     [(set (match_dup 5) (match_dup 4))
      (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))])]
@@ -3891,7 +4747,8 @@ (define_peephole2 ; mover2side (peephole
    && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J')
    && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N')
    && INTVAL (operands[2]) >= -128
-   && INTVAL (operands[2]) <= 127"
+   && INTVAL (operands[2]) <= 127
+   && TARGET_SIDE_EFFECT_PREFIXES"
   [(parallel
     [(set (match_dup 3) (match_op_dup 4 [(match_dup 3) (match_dup 6)]))
      (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))])]
@@ -4121,9 +4978,9 @@ (define_peephole2 ; gotplt-to-plt
 	(unspec:SI [(match_operand:SI 2 "cris_general_operand_or_symbol" "")]
 		   CRIS_UNSPEC_PLTGOTREAD)))]))]
   "flag_pic
-   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1))
+   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1), true)
    && REGNO_REG_CLASS (REGNO (operands[0])) == REGNO_REG_CLASS (0)"
-  [(set (match_dup 0) (const:SI (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLT)))
+  [(set (match_dup 0) (const:SI (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLT_GOTREL)))
    (set (match_dup 0) (plus:SI (match_dup 0) (reg:SI CRIS_GOT_REGNUM)))]
   "")
 
@@ -4150,7 +5007,7 @@ (define_peephole2 ; gotplt-to-plt-side-c
 		    (match_operand 4 "" ""))
 	      (clobber (reg:SI CRIS_SRP_REGNUM))])]
   "flag_pic
-   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1))
+   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1), true)
    && peep2_reg_dead_p (2, operands[0])"
   [(parallel [(call (mem:QI (match_dup 1))
 		    (match_dup 4))
@@ -4183,7 +5040,7 @@ (define_peephole2 ; gotplt-to-plt-side-c
 			 (match_operand 4 "" "")))
 	      (clobber (reg:SI CRIS_SRP_REGNUM))])]
   "flag_pic
-   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1))
+   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1), true)
    && peep2_reg_dead_p (2, operands[0])"
   [(parallel [(set (match_dup 5)
 		   (call (mem:QI (match_dup 1))
@@ -4213,12 +5070,13 @@ (define_peephole2 ; gotplt-to-plt-side
 		   (const:SI
 		    (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLTGOTREAD))))])]
   "flag_pic
-   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1))
+   && cris_valid_pic_const (XEXP (XEXP (operands[1], 0), 1), true)
    && REGNO_REG_CLASS (REGNO (operands[0])) == REGNO_REG_CLASS (0)"
   [(set (match_dup 3)
 	(const:SI (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLTGOTREAD)))
    (set (match_dup 3) (plus:SI (match_dup 3) (reg:SI CRIS_GOT_REGNUM)))
-   (set (match_dup 0) (const:SI (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLT)))
+   (set (match_dup 0)
+	(const:SI (unspec:SI [(match_dup 2)] CRIS_UNSPEC_PLT_GOTREL)))
    (set (match_dup 0) (plus:SI (match_dup 0) (reg:SI CRIS_GOT_REGNUM)))]
   "")
 \f
Index: gcc/config/cris/cris-protos.h
===================================================================
--- gcc/config/cris/cris-protos.h	(revision 130951)
+++ gcc/config/cris/cris-protos.h	(working copy)
@@ -31,20 +31,24 @@ extern bool cris_simple_epilogue (void);
 extern const char *cris_op_str (rtx);
 extern void cris_notice_update_cc (rtx, rtx);
 extern bool cris_reload_address_legitimized (rtx, enum machine_mode, int, int, int);
+extern int cris_register_move_cost (enum machine_mode, enum reg_class,
+				    enum reg_class);
 extern void cris_print_operand (FILE *, rtx, int);
 extern void cris_print_operand_address (FILE *, rtx);
 extern int cris_side_effect_mode_ok (enum rtx_code, rtx *, int, int,
                                      int, int, int);
+extern bool cris_cc0_user_requires_cmp (rtx);
 extern rtx cris_return_addr_rtx (int, rtx);
 extern rtx cris_split_movdx (rtx *);
 extern int cris_legitimate_pic_operand (rtx);
 extern enum cris_pic_symbol_type cris_pic_symbol_type_of (rtx);
-extern bool cris_valid_pic_const (rtx);
+extern bool cris_valid_pic_const (rtx, bool);
 extern bool cris_store_multiple_op_p (rtx);
 extern bool cris_movem_load_rest_p (rtx, int);
 extern void cris_asm_output_symbol_ref (FILE *, rtx);
 extern bool cris_output_addr_const_extra (FILE *, rtx);
 extern int cris_cfun_uses_pic_table (void);
+extern void cris_asm_output_case_end (FILE *, int, rtx);
 extern rtx cris_gen_movem_load (rtx, rtx, int);
 extern rtx cris_emit_movem_store (rtx, rtx, int, bool);
 extern void cris_expand_pic_call_address (rtx *);

brgds, H-P

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2007-12-15 21:42 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-12-15 21:45 Committed: CRIS v32 support 2/2 Hans-Peter Nilsson

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