Patchwork [SPARC] Fix PR target/59316

login
register
mail settings
Submitter Eric Botcazou
Date Dec. 5, 2013, 4:53 p.m.
Message ID <5391528.xJ694iqFr8@polaris>
Download mbox | patch
Permalink /patch/297206/
State New
Headers show

Comments

Eric Botcazou - Dec. 5, 2013, 4:53 p.m.
This is the failure of gcc.dg/atomic/c11-atomic-exec-5.c on the SPARC, because 
of the missing TARGET_ATOMIC_ASSIGN_EXPAND_FENV hook.  The implementation is 
heavily inspired from that of glibc; it seems to work fine on Solaris too, 
except that the FE_* macros are different from the Linux ones, so an internal 
macro is added to parameterize the hook.  According to a quick browsing of the 
sources, all the BSDs use the same values as Linux for the macros.

The patch also revamps the support code for builtins to make it work with LTO.
I'll very likely backport this part to the 4.8 branch at some point.

Tested on SPARC/Solaris.  Joseph, do you have any comments?


2013-12-05  Eric Botcazou  <ebotcazou@adacore.com>

	PR target/59316
	* config/sparc/sparc.h (SPARC_LOW_FE_EXCEPT_VALUES): Define.
	* config/sparc/sol2.h (SPARC_LOW_FE_EXCEPT_VALUES): Redefine.
	* config/sparc/sparc.c (TARGET_INIT_BUILTINS): Move around.
	(TARGET_BUILTIN_DECL): Define.
	(TARGET_ATOMIC_ASSIGN_EXPAND_FENV): Likewise.
	(sparc32_initialize_trampoline): Adjust call to gen_flush.
	(enum sparc_builtins): New enumeral type.
	(sparc_builtins): New static array.
	(sparc_builtins_icode): Likewise.
	(def_builtin): Accept a separate icode and save the result.
	(def_builtin_const): Likewise.
	(sparc_fpu_init_builtins): New function.
	(sparc_vis_init_builtins): Pass the builtin code.
	(sparc_init_builtins): Call it if TARGET_FPU.
	(sparc_builtin_decl): New function.
	(sparc_expand_builtin): Deal with SPARC_BUILTIN_{LD,ST}FSR.
	(sparc_handle_vis_mul8x16): Use the builtin code.
	(sparc_fold_builtin): Likewise.  Deal with SPARC_BUILTIN_{LD,ST}FSR
	and SPARC_BUILTIN_PDISTN.
	(compound_expr): New helper function.
	(sparc_atomic_assign_expand_fenv): New function.
	* config/sparc/sparc.md (unspecv): Reorder values, add UNSPECV_LDFSR
	and UNSPECV_STFSR.
	(flush, flushdi): Merge into single pattern.
	(ldfsr): New instruction.
	(stfsr): Likewise.


2013-12-05  Eric Botcazou  <ebotcazou@adacore.com>

	* gcc.target/sparc/pdistn.c: New test.
	* gcc.target/sparc/pdistn-2.c: Likewise.
Joseph S. Myers - Dec. 5, 2013, 5:22 p.m.
On Thu, 5 Dec 2013, Eric Botcazou wrote:

> +  /* We generate the equivalent of feclearexcept (FE_ALL_EXCEPT):
> +
> +       unsigned int tmp2_var;
> +       __builtin_store_fsr (&tmp2_var);
> +
> +       tmp2_var &= ~accrued_exception_mask;
> +
> +       __builtin_load_fsr (&tmp2_var);  */

Note that you could probably just reload the tmp1_var value rather than 
needing to store the fsr value again to clear exceptions from it, unless 
there's an architecture-specific reason the tmp1_var value might no longer 
be the right value to load to clear exceptions.  (This is what x86 does 
for SSE state, loading the same value for exceptions-cleared SSE state 
every iteration of the loop.)
Eric Botcazou - Dec. 5, 2013, 8:57 p.m.
> Note that you could probably just reload the tmp1_var value rather than
> needing to store the fsr value again to clear exceptions from it, unless
> there's an architecture-specific reason the tmp1_var value might no longer
> be the right value to load to clear exceptions.  (This is what x86 does
> for SSE state, loading the same value for exceptions-cleared SSE state
> every iteration of the loop.)

That depends upon the context I'd think: the FSR contains the FP condition 
code so reloading tmp1_var will overwrite it with the old value (and SPARC is 
not a cc0 target).  Would that be safe?

In the meantime, it occurred to me that I very likely don't need to mask the 
value of tmp3_var before calling __atomic_feraiseexcept on it.  glibc does it 
but this looks unnecessary even there.
Joseph S. Myers - Dec. 5, 2013, 10:35 p.m.
On Thu, 5 Dec 2013, Eric Botcazou wrote:

> > Note that you could probably just reload the tmp1_var value rather than
> > needing to store the fsr value again to clear exceptions from it, unless
> > there's an architecture-specific reason the tmp1_var value might no longer
> > be the right value to load to clear exceptions.  (This is what x86 does
> > for SSE state, loading the same value for exceptions-cleared SSE state
> > every iteration of the loop.)
> 
> That depends upon the context I'd think: the FSR contains the FP condition 
> code so reloading tmp1_var will overwrite it with the old value (and SPARC is 
> not a cc0 target).  Would that be safe?

Generically, each iteration of the compare-and-exchange loop should be as 
if the previous iterations hadn't happened, so restoring all the previous 
FP state is right in that sense.

With regard to the implementation, the insn patterns for the SPARC 
builtins should specify that the condition code is clobbered when FSR is 
set, which should avoid any problems in that regard.  (The x86 fstenv and 
fldenv builtins are specified to clobber all the x87 stack registers to 
ensure that the TOS value saved at fnstenv is valid after fldenv.  fldenv 
is also specified to clobber the condition codes.)

> In the meantime, it occurred to me that I very likely don't need to mask the 
> value of tmp3_var before calling __atomic_feraiseexcept on it.  glibc does it 
> but this looks unnecessary even there.

Indeed, __atomic_feraiseexcept is defined to ignore all bits in its 
argument that aren't exception bits, so no masking is needed.
Eric Botcazou - Dec. 5, 2013, 10:52 p.m.
> Generically, each iteration of the compare-and-exchange loop should be as
> if the previous iterations hadn't happened, so restoring all the previous
> FP state is right in that sense.

I see.

> With regard to the implementation, the insn patterns for the SPARC
> builtins should specify that the condition code is clobbered when FSR is
> set, which should avoid any problems in that regard.

They are unspec_volatile though so they already clobber all the hard regs.

> Indeed, __atomic_feraiseexcept is defined to ignore all bits in its
> argument that aren't exception bits, so no masking is needed.

OK, thanks for confirming.

Patch

Index: config/sparc/sparc.md
===================================================================
--- config/sparc/sparc.md	(revision 205689)
+++ config/sparc/sparc.md	(working copy)
@@ -96,13 +96,19 @@  (define_c_enum "unspec" [
 
 (define_c_enum "unspecv" [
   UNSPECV_BLOCKAGE
+  UNSPECV_PROBE_STACK_RANGE
+
   UNSPECV_FLUSHW
-  UNSPECV_FLUSH
   UNSPECV_SAVEW
-  UNSPECV_CAS
-  UNSPECV_SWAP
+
+  UNSPECV_FLUSH
+
   UNSPECV_LDSTUB
-  UNSPECV_PROBE_STACK_RANGE
+  UNSPECV_SWAP
+  UNSPECV_CAS
+
+  UNSPECV_LDFSR
+  UNSPECV_STFSR
 ])
 
 (define_constants
@@ -6783,22 +6789,26 @@  (define_insn "flush_register_windows"
 
 ;; Special pattern for the FLUSH instruction.
 
-; We do SImode and DImode versions of this to quiet down genrecog's complaints
-; of the define_insn otherwise missing a mode.  We make "flush", aka
-; gen_flush, the default one since sparc_initialize_trampoline uses
-; it on SImode mem values.
-
-(define_insn "flush"
-  [(unspec_volatile [(match_operand:SI 0 "memory_operand" "m")] UNSPECV_FLUSH)]
+(define_insn "flush<P:mode>"
+  [(unspec_volatile [(match_operand:P 0 "memory_operand" "m")] UNSPECV_FLUSH)]
   ""
   { return TARGET_V9 ? "flush\t%f0" : "iflush\t%f0"; }
   [(set_attr "type" "iflush")])
 
-(define_insn "flushdi"
-  [(unspec_volatile [(match_operand:DI 0 "memory_operand" "m")] UNSPECV_FLUSH)]
-  ""
-  { return TARGET_V9 ? "flush\t%f0" : "iflush\t%f0"; }
-  [(set_attr "type" "iflush")])
+;; Special insns to load and store the 32-bit FP Status Register.
+
+(define_insn "ldfsr"
+  [(unspec_volatile [(match_operand:SI 0 "memory_operand" "m")] UNSPECV_LDFSR)]
+  "TARGET_FPU"
+  "ld\t%0, %%fsr"
+  [(set_attr "type" "load")])
+
+(define_insn "stfsr"
+  [(set (match_operand:SI 0 "memory_operand" "=m")
+        (unspec_volatile:SI [(const_int 0)] UNSPECV_STFSR))]
+  "TARGET_FPU"
+  "st\t%%fsr, %0"
+  [(set_attr "type" "store")])
 
 
 ;; Find first set instructions.
Index: config/sparc/sparc.c
===================================================================
--- config/sparc/sparc.c	(revision 205689)
+++ config/sparc/sparc.c	(working copy)
@@ -570,11 +570,11 @@  static void emit_hard_tfmode_operation (
 static bool sparc_function_ok_for_sibcall (tree, tree);
 static void sparc_init_libfuncs (void);
 static void sparc_init_builtins (void);
+static void sparc_fpu_init_builtins (void);
 static void sparc_vis_init_builtins (void);
+static tree sparc_builtin_decl (unsigned, bool);
 static rtx sparc_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
 static tree sparc_fold_builtin (tree, int, tree *, bool);
-static int sparc_vis_mul8x16 (int, int);
-static void sparc_handle_vis_mul8x16 (tree *, int, tree, tree, tree);
 static void sparc_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
 				   HOST_WIDE_INT, tree);
 static bool sparc_can_output_mi_thunk (const_tree, HOST_WIDE_INT,
@@ -638,6 +638,7 @@  static reg_class_t sparc_secondary_reloa
 					   enum machine_mode,
 					   secondary_reload_info *);
 static enum machine_mode sparc_cstore_mode (enum insn_code icode);
+static void sparc_atomic_assign_expand_fenv (tree *, tree *, tree *);
 
 #ifdef SUBTARGET_ATTRIBUTE_TABLE
 /* Table of valid machine attributes.  */
@@ -693,8 +694,6 @@  char sparc_hard_reg_printed[8];
 
 #undef TARGET_INIT_LIBFUNCS
 #define TARGET_INIT_LIBFUNCS sparc_init_libfuncs
-#undef TARGET_INIT_BUILTINS
-#define TARGET_INIT_BUILTINS sparc_init_builtins
 
 #undef TARGET_LEGITIMIZE_ADDRESS
 #define TARGET_LEGITIMIZE_ADDRESS sparc_legitimize_address
@@ -703,6 +702,10 @@  char sparc_hard_reg_printed[8];
 #undef TARGET_MODE_DEPENDENT_ADDRESS_P
 #define TARGET_MODE_DEPENDENT_ADDRESS_P sparc_mode_dependent_address_p
 
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS sparc_init_builtins
+#undef TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL sparc_builtin_decl
 #undef TARGET_EXPAND_BUILTIN
 #define TARGET_EXPAND_BUILTIN sparc_expand_builtin
 #undef TARGET_FOLD_BUILTIN
@@ -843,6 +846,9 @@  char sparc_hard_reg_printed[8];
 #undef TARGET_CSTORE_MODE
 #define TARGET_CSTORE_MODE sparc_cstore_mode
 
+#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV
+#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV sparc_atomic_assign_expand_fenv
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 /* Return the memory reference contained in X if any, zero otherwise.  */
@@ -9237,14 +9243,14 @@  sparc32_initialize_trampoline (rtx m_tra
 
   /* On UltraSPARC a flush flushes an entire cache line.  The trampoline is
      aligned on a 16 byte boundary so one flush clears it all.  */
-  emit_insn (gen_flush (validize_mem (adjust_address (m_tramp, SImode, 0))));
+  emit_insn (gen_flushsi (validize_mem (adjust_address (m_tramp, SImode, 0))));
   if (sparc_cpu != PROCESSOR_ULTRASPARC
       && sparc_cpu != PROCESSOR_ULTRASPARC3
       && sparc_cpu != PROCESSOR_NIAGARA
       && sparc_cpu != PROCESSOR_NIAGARA2
       && sparc_cpu != PROCESSOR_NIAGARA3
       && sparc_cpu != PROCESSOR_NIAGARA4)
-    emit_insn (gen_flush (validize_mem (adjust_address (m_tramp, SImode, 8))));
+    emit_insn (gen_flushsi (validize_mem (adjust_address (m_tramp, SImode, 8))));
 
   /* Call __enable_execute_stack after writing onto the stack to make sure
      the stack address is accessible.  */
@@ -9969,15 +9975,145 @@  sparc_init_libfuncs (void)
     }
 }
 
-static tree def_builtin(const char *name, int code, tree type)
+/* SPARC builtins.  */
+enum sparc_builtins
+{
+  /* FPU builtins.  */
+  SPARC_BUILTIN_LDFSR,
+  SPARC_BUILTIN_STFSR,
+
+  /* VIS 1.0 builtins.  */
+  SPARC_BUILTIN_FPACK16,
+  SPARC_BUILTIN_FPACK32,
+  SPARC_BUILTIN_FPACKFIX,
+  SPARC_BUILTIN_FEXPAND,
+  SPARC_BUILTIN_FPMERGE,
+  SPARC_BUILTIN_FMUL8X16,
+  SPARC_BUILTIN_FMUL8X16AU,
+  SPARC_BUILTIN_FMUL8X16AL,
+  SPARC_BUILTIN_FMUL8SUX16,
+  SPARC_BUILTIN_FMUL8ULX16,
+  SPARC_BUILTIN_FMULD8SUX16,
+  SPARC_BUILTIN_FMULD8ULX16,
+  SPARC_BUILTIN_FALIGNDATAV4HI,
+  SPARC_BUILTIN_FALIGNDATAV8QI,
+  SPARC_BUILTIN_FALIGNDATAV2SI,
+  SPARC_BUILTIN_FALIGNDATADI,
+  SPARC_BUILTIN_WRGSR,
+  SPARC_BUILTIN_RDGSR,
+  SPARC_BUILTIN_ALIGNADDR,
+  SPARC_BUILTIN_ALIGNADDRL,
+  SPARC_BUILTIN_PDIST,
+  SPARC_BUILTIN_EDGE8,
+  SPARC_BUILTIN_EDGE8L,
+  SPARC_BUILTIN_EDGE16,
+  SPARC_BUILTIN_EDGE16L,
+  SPARC_BUILTIN_EDGE32,
+  SPARC_BUILTIN_EDGE32L,
+  SPARC_BUILTIN_FCMPLE16,
+  SPARC_BUILTIN_FCMPLE32,
+  SPARC_BUILTIN_FCMPNE16,
+  SPARC_BUILTIN_FCMPNE32,
+  SPARC_BUILTIN_FCMPGT16,
+  SPARC_BUILTIN_FCMPGT32,
+  SPARC_BUILTIN_FCMPEQ16,
+  SPARC_BUILTIN_FCMPEQ32,
+  SPARC_BUILTIN_FPADD16,
+  SPARC_BUILTIN_FPADD16S,
+  SPARC_BUILTIN_FPADD32,
+  SPARC_BUILTIN_FPADD32S,
+  SPARC_BUILTIN_FPSUB16,
+  SPARC_BUILTIN_FPSUB16S,
+  SPARC_BUILTIN_FPSUB32,
+  SPARC_BUILTIN_FPSUB32S,
+  SPARC_BUILTIN_ARRAY8,
+  SPARC_BUILTIN_ARRAY16,
+  SPARC_BUILTIN_ARRAY32,
+
+  /* VIS 2.0 builtins.  */
+  SPARC_BUILTIN_EDGE8N,
+  SPARC_BUILTIN_EDGE8LN,
+  SPARC_BUILTIN_EDGE16N,
+  SPARC_BUILTIN_EDGE16LN,
+  SPARC_BUILTIN_EDGE32N,
+  SPARC_BUILTIN_EDGE32LN,
+  SPARC_BUILTIN_BMASK,
+  SPARC_BUILTIN_BSHUFFLEV4HI,
+  SPARC_BUILTIN_BSHUFFLEV8QI,
+  SPARC_BUILTIN_BSHUFFLEV2SI,
+  SPARC_BUILTIN_BSHUFFLEDI,
+
+  /* VIS 3.0 builtins.  */
+  SPARC_BUILTIN_CMASK8,
+  SPARC_BUILTIN_CMASK16,
+  SPARC_BUILTIN_CMASK32,
+  SPARC_BUILTIN_FCHKSM16,
+  SPARC_BUILTIN_FSLL16,
+  SPARC_BUILTIN_FSLAS16,
+  SPARC_BUILTIN_FSRL16,
+  SPARC_BUILTIN_FSRA16,
+  SPARC_BUILTIN_FSLL32,
+  SPARC_BUILTIN_FSLAS32,
+  SPARC_BUILTIN_FSRL32,
+  SPARC_BUILTIN_FSRA32,
+  SPARC_BUILTIN_PDISTN,
+  SPARC_BUILTIN_FMEAN16,
+  SPARC_BUILTIN_FPADD64,
+  SPARC_BUILTIN_FPSUB64,
+  SPARC_BUILTIN_FPADDS16,
+  SPARC_BUILTIN_FPADDS16S,
+  SPARC_BUILTIN_FPSUBS16,
+  SPARC_BUILTIN_FPSUBS16S,
+  SPARC_BUILTIN_FPADDS32,
+  SPARC_BUILTIN_FPADDS32S,
+  SPARC_BUILTIN_FPSUBS32,
+  SPARC_BUILTIN_FPSUBS32S,
+  SPARC_BUILTIN_FUCMPLE8,
+  SPARC_BUILTIN_FUCMPNE8,
+  SPARC_BUILTIN_FUCMPGT8,
+  SPARC_BUILTIN_FUCMPEQ8,
+  SPARC_BUILTIN_FHADDS,
+  SPARC_BUILTIN_FHADDD,
+  SPARC_BUILTIN_FHSUBS,
+  SPARC_BUILTIN_FHSUBD,
+  SPARC_BUILTIN_FNHADDS,
+  SPARC_BUILTIN_FNHADDD,
+  SPARC_BUILTIN_UMULXHI,
+  SPARC_BUILTIN_XMULX,
+  SPARC_BUILTIN_XMULXHI,
+
+  SPARC_BUILTIN_MAX
+};
+
+static GTY (()) tree sparc_builtins[(int) SPARC_BUILTIN_MAX];
+static enum insn_code sparc_builtins_icode[(int) SPARC_BUILTIN_MAX];
+
+/* Add a SPARC builtin function with NAME, ICODE, CODE and TYPE.  Return the
+   function decl or NULL_TREE if the builtin was not added.  */
+
+static tree
+def_builtin (const char *name, enum insn_code icode, enum sparc_builtins code,
+	     tree type)
 {
-  return add_builtin_function(name, type, code, BUILT_IN_MD, NULL,
-			      NULL_TREE);
+  tree t
+    = add_builtin_function (name, type, code, BUILT_IN_MD, NULL, NULL_TREE);
+
+  if (t)
+    {
+      sparc_builtins[code] = t;
+      sparc_builtins_icode[code] = icode;
+    }
+
+  return t;
 }
 
-static tree def_builtin_const(const char *name, int code, tree type)
+/* Likewise, but also marks the function as "const".  */
+
+static tree
+def_builtin_const (const char *name, enum insn_code icode,
+		   enum sparc_builtins code, tree type)
 {
-  tree t = def_builtin(name, code, type);
+  tree t = def_builtin (name, icode, code, type);
 
   if (t)
     TREE_READONLY (t) = 1;
@@ -9991,11 +10127,28 @@  static tree def_builtin_const(const char
 static void
 sparc_init_builtins (void)
 {
+  if (TARGET_FPU)
+    sparc_fpu_init_builtins ();
+
   if (TARGET_VIS)
     sparc_vis_init_builtins ();
 }
 
-/* Create builtin functions for VIS 1.0 instructions.  */
+/* Create builtin functions for FPU instructions.  */
+
+static void
+sparc_fpu_init_builtins (void)
+{
+  tree ftype
+    = build_function_type_list (void_type_node,
+				build_pointer_type (unsigned_type_node), 0);
+  def_builtin ("__builtin_load_fsr", CODE_FOR_ldfsr,
+	       SPARC_BUILTIN_LDFSR, ftype);
+  def_builtin ("__builtin_store_fsr", CODE_FOR_stfsr,
+	       SPARC_BUILTIN_STFSR, ftype);
+}
+
+/* Create builtin functions for VIS instructions.  */
 
 static void
 sparc_vis_init_builtins (void)
@@ -10069,223 +10222,225 @@  sparc_vis_init_builtins (void)
 
   /* Packing and expanding vectors.  */
   def_builtin ("__builtin_vis_fpack16", CODE_FOR_fpack16_vis,
-	       v4qi_ftype_v4hi);
+	       SPARC_BUILTIN_FPACK16, v4qi_ftype_v4hi);
   def_builtin ("__builtin_vis_fpack32", CODE_FOR_fpack32_vis,
-	       v8qi_ftype_v2si_v8qi);
+	       SPARC_BUILTIN_FPACK32, v8qi_ftype_v2si_v8qi);
   def_builtin ("__builtin_vis_fpackfix", CODE_FOR_fpackfix_vis,
-	       v2hi_ftype_v2si);
+	       SPARC_BUILTIN_FPACKFIX, v2hi_ftype_v2si);
   def_builtin_const ("__builtin_vis_fexpand", CODE_FOR_fexpand_vis,
-		     v4hi_ftype_v4qi);
+		     SPARC_BUILTIN_FEXPAND, v4hi_ftype_v4qi);
   def_builtin_const ("__builtin_vis_fpmerge", CODE_FOR_fpmerge_vis,
-		     v8qi_ftype_v4qi_v4qi);
+		     SPARC_BUILTIN_FPMERGE, v8qi_ftype_v4qi_v4qi);
 
   /* Multiplications.  */
   def_builtin_const ("__builtin_vis_fmul8x16", CODE_FOR_fmul8x16_vis,
-		     v4hi_ftype_v4qi_v4hi);
+		     SPARC_BUILTIN_FMUL8X16, v4hi_ftype_v4qi_v4hi);
   def_builtin_const ("__builtin_vis_fmul8x16au", CODE_FOR_fmul8x16au_vis,
-		     v4hi_ftype_v4qi_v2hi);
+		     SPARC_BUILTIN_FMUL8X16AU, v4hi_ftype_v4qi_v2hi);
   def_builtin_const ("__builtin_vis_fmul8x16al", CODE_FOR_fmul8x16al_vis,
-		     v4hi_ftype_v4qi_v2hi);
+		     SPARC_BUILTIN_FMUL8X16AL, v4hi_ftype_v4qi_v2hi);
   def_builtin_const ("__builtin_vis_fmul8sux16", CODE_FOR_fmul8sux16_vis,
-		     v4hi_ftype_v8qi_v4hi);
+		     SPARC_BUILTIN_FMUL8SUX16, v4hi_ftype_v8qi_v4hi);
   def_builtin_const ("__builtin_vis_fmul8ulx16", CODE_FOR_fmul8ulx16_vis,
-		     v4hi_ftype_v8qi_v4hi);
+		     SPARC_BUILTIN_FMUL8ULX16, v4hi_ftype_v8qi_v4hi);
   def_builtin_const ("__builtin_vis_fmuld8sux16", CODE_FOR_fmuld8sux16_vis,
-		     v2si_ftype_v4qi_v2hi);
+		     SPARC_BUILTIN_FMULD8SUX16, v2si_ftype_v4qi_v2hi);
   def_builtin_const ("__builtin_vis_fmuld8ulx16", CODE_FOR_fmuld8ulx16_vis,
-		     v2si_ftype_v4qi_v2hi);
+		     SPARC_BUILTIN_FMULD8ULX16, v2si_ftype_v4qi_v2hi);
 
   /* Data aligning.  */
   def_builtin ("__builtin_vis_faligndatav4hi", CODE_FOR_faligndatav4hi_vis,
-	       v4hi_ftype_v4hi_v4hi);
+	       SPARC_BUILTIN_FALIGNDATAV4HI, v4hi_ftype_v4hi_v4hi);
   def_builtin ("__builtin_vis_faligndatav8qi", CODE_FOR_faligndatav8qi_vis,
-	       v8qi_ftype_v8qi_v8qi);
+	       SPARC_BUILTIN_FALIGNDATAV8QI, v8qi_ftype_v8qi_v8qi);
   def_builtin ("__builtin_vis_faligndatav2si", CODE_FOR_faligndatav2si_vis,
-	       v2si_ftype_v2si_v2si);
+	       SPARC_BUILTIN_FALIGNDATAV2SI, v2si_ftype_v2si_v2si);
   def_builtin ("__builtin_vis_faligndatadi", CODE_FOR_faligndatav1di_vis,
-	       di_ftype_di_di);
+	       SPARC_BUILTIN_FALIGNDATADI, di_ftype_di_di);
 
   def_builtin ("__builtin_vis_write_gsr", CODE_FOR_wrgsr_vis,
-	       void_ftype_di);
+	       SPARC_BUILTIN_WRGSR, void_ftype_di);
   def_builtin ("__builtin_vis_read_gsr", CODE_FOR_rdgsr_vis,
-	       di_ftype_void);
+	       SPARC_BUILTIN_RDGSR, di_ftype_void);
 
   if (TARGET_ARCH64)
     {
       def_builtin ("__builtin_vis_alignaddr", CODE_FOR_alignaddrdi_vis,
-		   ptr_ftype_ptr_di);
+		   SPARC_BUILTIN_ALIGNADDR, ptr_ftype_ptr_di);
       def_builtin ("__builtin_vis_alignaddrl", CODE_FOR_alignaddrldi_vis,
-		   ptr_ftype_ptr_di);
+		   SPARC_BUILTIN_ALIGNADDRL, ptr_ftype_ptr_di);
     }
   else
     {
       def_builtin ("__builtin_vis_alignaddr", CODE_FOR_alignaddrsi_vis,
-		   ptr_ftype_ptr_si);
+		   SPARC_BUILTIN_ALIGNADDR, ptr_ftype_ptr_si);
       def_builtin ("__builtin_vis_alignaddrl", CODE_FOR_alignaddrlsi_vis,
-		   ptr_ftype_ptr_si);
+		   SPARC_BUILTIN_ALIGNADDRL, ptr_ftype_ptr_si);
     }
 
   /* Pixel distance.  */
   def_builtin_const ("__builtin_vis_pdist", CODE_FOR_pdist_vis,
-		     di_ftype_v8qi_v8qi_di);
+		     SPARC_BUILTIN_PDIST, di_ftype_v8qi_v8qi_di);
 
   /* Edge handling.  */
   if (TARGET_ARCH64)
     {
       def_builtin_const ("__builtin_vis_edge8", CODE_FOR_edge8di_vis,
-			 di_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE8, di_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge8l", CODE_FOR_edge8ldi_vis,
-			 di_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE8L, di_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge16", CODE_FOR_edge16di_vis,
-			 di_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE16, di_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge16l", CODE_FOR_edge16ldi_vis,
-			 di_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE16L, di_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge32", CODE_FOR_edge32di_vis,
-			 di_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE32, di_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge32l", CODE_FOR_edge32ldi_vis,
-			 di_ftype_ptr_ptr);
-      if (TARGET_VIS2)
-	{
-	  def_builtin_const ("__builtin_vis_edge8n", CODE_FOR_edge8ndi_vis,
-			     di_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge8ln", CODE_FOR_edge8lndi_vis,
-			     di_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge16n", CODE_FOR_edge16ndi_vis,
-			     di_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge16ln", CODE_FOR_edge16lndi_vis,
-			     di_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge32n", CODE_FOR_edge32ndi_vis,
-			     di_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge32ln", CODE_FOR_edge32lndi_vis,
-			     di_ftype_ptr_ptr);
-	}
+			 SPARC_BUILTIN_EDGE32L, di_ftype_ptr_ptr);
     }
   else
     {
       def_builtin_const ("__builtin_vis_edge8", CODE_FOR_edge8si_vis,
-			 si_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE8, si_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge8l", CODE_FOR_edge8lsi_vis,
-			 si_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE8L, si_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge16", CODE_FOR_edge16si_vis,
-			 si_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE16, si_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge16l", CODE_FOR_edge16lsi_vis,
-			 si_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE16L, si_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge32", CODE_FOR_edge32si_vis,
-			 si_ftype_ptr_ptr);
+			 SPARC_BUILTIN_EDGE32, si_ftype_ptr_ptr);
       def_builtin_const ("__builtin_vis_edge32l", CODE_FOR_edge32lsi_vis,
-			 si_ftype_ptr_ptr);
-      if (TARGET_VIS2)
-	{
-	  def_builtin_const ("__builtin_vis_edge8n", CODE_FOR_edge8nsi_vis,
-			     si_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge8ln", CODE_FOR_edge8lnsi_vis,
-			     si_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge16n", CODE_FOR_edge16nsi_vis,
-			     si_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge16ln", CODE_FOR_edge16lnsi_vis,
-			     si_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge32n", CODE_FOR_edge32nsi_vis,
-			     si_ftype_ptr_ptr);
-	  def_builtin_const ("__builtin_vis_edge32ln", CODE_FOR_edge32lnsi_vis,
-			     si_ftype_ptr_ptr);
-	}
+			 SPARC_BUILTIN_EDGE32L, si_ftype_ptr_ptr);
     }
 
   /* Pixel compare.  */
   if (TARGET_ARCH64)
     {
       def_builtin_const ("__builtin_vis_fcmple16", CODE_FOR_fcmple16di_vis,
-			 di_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FCMPLE16, di_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fcmple32", CODE_FOR_fcmple32di_vis,
-			 di_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FCMPLE32, di_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fcmpne16", CODE_FOR_fcmpne16di_vis,
-			 di_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FCMPNE16, di_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fcmpne32", CODE_FOR_fcmpne32di_vis,
-			 di_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FCMPNE32, di_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fcmpgt16", CODE_FOR_fcmpgt16di_vis,
-			 di_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FCMPGT16, di_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fcmpgt32", CODE_FOR_fcmpgt32di_vis,
-			 di_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FCMPGT32, di_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fcmpeq16", CODE_FOR_fcmpeq16di_vis,
-			 di_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FCMPEQ16, di_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fcmpeq32", CODE_FOR_fcmpeq32di_vis,
-			 di_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FCMPEQ32, di_ftype_v2si_v2si);
     }
   else
     {
       def_builtin_const ("__builtin_vis_fcmple16", CODE_FOR_fcmple16si_vis,
-			 si_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FCMPLE16, si_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fcmple32", CODE_FOR_fcmple32si_vis,
-			 si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FCMPLE32, si_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fcmpne16", CODE_FOR_fcmpne16si_vis,
-			 si_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FCMPNE16, si_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fcmpne32", CODE_FOR_fcmpne32si_vis,
-			 si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FCMPNE32, si_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fcmpgt16", CODE_FOR_fcmpgt16si_vis,
-			 si_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FCMPGT16, si_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fcmpgt32", CODE_FOR_fcmpgt32si_vis,
-			 si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FCMPGT32, si_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fcmpeq16", CODE_FOR_fcmpeq16si_vis,
-			 si_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FCMPEQ16, si_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fcmpeq32", CODE_FOR_fcmpeq32si_vis,
-			 si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FCMPEQ32, si_ftype_v2si_v2si);
     }
 
   /* Addition and subtraction.  */
   def_builtin_const ("__builtin_vis_fpadd16", CODE_FOR_addv4hi3,
-		     v4hi_ftype_v4hi_v4hi);
+		     SPARC_BUILTIN_FPADD16, v4hi_ftype_v4hi_v4hi);
   def_builtin_const ("__builtin_vis_fpadd16s", CODE_FOR_addv2hi3,
-		     v2hi_ftype_v2hi_v2hi);
+		     SPARC_BUILTIN_FPADD16S, v2hi_ftype_v2hi_v2hi);
   def_builtin_const ("__builtin_vis_fpadd32", CODE_FOR_addv2si3,
-		     v2si_ftype_v2si_v2si);
+		     SPARC_BUILTIN_FPADD32, v2si_ftype_v2si_v2si);
   def_builtin_const ("__builtin_vis_fpadd32s", CODE_FOR_addv1si3,
-		     v1si_ftype_v1si_v1si);
+		     SPARC_BUILTIN_FPADD32S, v1si_ftype_v1si_v1si);
   def_builtin_const ("__builtin_vis_fpsub16", CODE_FOR_subv4hi3,
-		     v4hi_ftype_v4hi_v4hi);
+		     SPARC_BUILTIN_FPSUB16, v4hi_ftype_v4hi_v4hi);
   def_builtin_const ("__builtin_vis_fpsub16s", CODE_FOR_subv2hi3,
-		     v2hi_ftype_v2hi_v2hi);
+		     SPARC_BUILTIN_FPSUB16S, v2hi_ftype_v2hi_v2hi);
   def_builtin_const ("__builtin_vis_fpsub32", CODE_FOR_subv2si3,
-		     v2si_ftype_v2si_v2si);
+		     SPARC_BUILTIN_FPSUB32, v2si_ftype_v2si_v2si);
   def_builtin_const ("__builtin_vis_fpsub32s", CODE_FOR_subv1si3,
-		     v1si_ftype_v1si_v1si);
+		     SPARC_BUILTIN_FPSUB32S, v1si_ftype_v1si_v1si);
 
   /* Three-dimensional array addressing.  */
   if (TARGET_ARCH64)
     {
       def_builtin_const ("__builtin_vis_array8", CODE_FOR_array8di_vis,
-			 di_ftype_di_di);
+			 SPARC_BUILTIN_ARRAY8, di_ftype_di_di);
       def_builtin_const ("__builtin_vis_array16", CODE_FOR_array16di_vis,
-			 di_ftype_di_di);
+			 SPARC_BUILTIN_ARRAY16, di_ftype_di_di);
       def_builtin_const ("__builtin_vis_array32", CODE_FOR_array32di_vis,
-			 di_ftype_di_di);
+			 SPARC_BUILTIN_ARRAY32, di_ftype_di_di);
     }
   else
     {
       def_builtin_const ("__builtin_vis_array8", CODE_FOR_array8si_vis,
-			 si_ftype_si_si);
+			 SPARC_BUILTIN_ARRAY8, si_ftype_si_si);
       def_builtin_const ("__builtin_vis_array16", CODE_FOR_array16si_vis,
-			 si_ftype_si_si);
+			 SPARC_BUILTIN_ARRAY16, si_ftype_si_si);
       def_builtin_const ("__builtin_vis_array32", CODE_FOR_array32si_vis,
-			 si_ftype_si_si);
-  }
+			 SPARC_BUILTIN_ARRAY32, si_ftype_si_si);
+    }
 
   if (TARGET_VIS2)
     {
-      /* Byte mask and shuffle */
+      /* Edge handling.  */
+      if (TARGET_ARCH64)
+	{
+	  def_builtin_const ("__builtin_vis_edge8n", CODE_FOR_edge8ndi_vis,
+			     SPARC_BUILTIN_EDGE8N, di_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge8ln", CODE_FOR_edge8lndi_vis,
+			     SPARC_BUILTIN_EDGE8LN, di_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge16n", CODE_FOR_edge16ndi_vis,
+			     SPARC_BUILTIN_EDGE16N, di_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge16ln", CODE_FOR_edge16lndi_vis,
+			     SPARC_BUILTIN_EDGE16LN, di_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge32n", CODE_FOR_edge32ndi_vis,
+			     SPARC_BUILTIN_EDGE32N, di_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge32ln", CODE_FOR_edge32lndi_vis,
+			     SPARC_BUILTIN_EDGE32LN, di_ftype_ptr_ptr);
+	}
+      else
+	{
+	  def_builtin_const ("__builtin_vis_edge8n", CODE_FOR_edge8nsi_vis,
+			     SPARC_BUILTIN_EDGE8N, si_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge8ln", CODE_FOR_edge8lnsi_vis,
+			     SPARC_BUILTIN_EDGE8LN, si_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge16n", CODE_FOR_edge16nsi_vis,
+			     SPARC_BUILTIN_EDGE16N, si_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge16ln", CODE_FOR_edge16lnsi_vis,
+			     SPARC_BUILTIN_EDGE16LN, si_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge32n", CODE_FOR_edge32nsi_vis,
+			     SPARC_BUILTIN_EDGE32N, si_ftype_ptr_ptr);
+	  def_builtin_const ("__builtin_vis_edge32ln", CODE_FOR_edge32lnsi_vis,
+			     SPARC_BUILTIN_EDGE32LN, si_ftype_ptr_ptr);
+	}
+
+      /* Byte mask and shuffle.  */
       if (TARGET_ARCH64)
 	def_builtin ("__builtin_vis_bmask", CODE_FOR_bmaskdi_vis,
-		     di_ftype_di_di);
+		     SPARC_BUILTIN_BMASK, di_ftype_di_di);
       else
 	def_builtin ("__builtin_vis_bmask", CODE_FOR_bmasksi_vis,
-		     si_ftype_si_si);
+		     SPARC_BUILTIN_BMASK, si_ftype_si_si);
       def_builtin ("__builtin_vis_bshufflev4hi", CODE_FOR_bshufflev4hi_vis,
-		   v4hi_ftype_v4hi_v4hi);
+		   SPARC_BUILTIN_BSHUFFLEV4HI, v4hi_ftype_v4hi_v4hi);
       def_builtin ("__builtin_vis_bshufflev8qi", CODE_FOR_bshufflev8qi_vis,
-		   v8qi_ftype_v8qi_v8qi);
+		   SPARC_BUILTIN_BSHUFFLEV8QI, v8qi_ftype_v8qi_v8qi);
       def_builtin ("__builtin_vis_bshufflev2si", CODE_FOR_bshufflev2si_vis,
-		   v2si_ftype_v2si_v2si);
+		   SPARC_BUILTIN_BSHUFFLEV2SI, v2si_ftype_v2si_v2si);
       def_builtin ("__builtin_vis_bshuffledi", CODE_FOR_bshufflev1di_vis,
-		   di_ftype_di_di);
+		   SPARC_BUILTIN_BSHUFFLEDI, di_ftype_di_di);
     }
 
   if (TARGET_VIS3)
@@ -10293,120 +10448,130 @@  sparc_vis_init_builtins (void)
       if (TARGET_ARCH64)
 	{
 	  def_builtin ("__builtin_vis_cmask8", CODE_FOR_cmask8di_vis,
-		       void_ftype_di);
+		       SPARC_BUILTIN_CMASK8, void_ftype_di);
 	  def_builtin ("__builtin_vis_cmask16", CODE_FOR_cmask16di_vis,
-		       void_ftype_di);
+		       SPARC_BUILTIN_CMASK16, void_ftype_di);
 	  def_builtin ("__builtin_vis_cmask32", CODE_FOR_cmask32di_vis,
-		       void_ftype_di);
+		       SPARC_BUILTIN_CMASK32, void_ftype_di);
 	}
       else
 	{
 	  def_builtin ("__builtin_vis_cmask8", CODE_FOR_cmask8si_vis,
-		       void_ftype_si);
+		       SPARC_BUILTIN_CMASK8, void_ftype_si);
 	  def_builtin ("__builtin_vis_cmask16", CODE_FOR_cmask16si_vis,
-		       void_ftype_si);
+		       SPARC_BUILTIN_CMASK16, void_ftype_si);
 	  def_builtin ("__builtin_vis_cmask32", CODE_FOR_cmask32si_vis,
-		       void_ftype_si);
+		       SPARC_BUILTIN_CMASK32, void_ftype_si);
 	}
 
       def_builtin_const ("__builtin_vis_fchksm16", CODE_FOR_fchksm16_vis,
-			 v4hi_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FCHKSM16, v4hi_ftype_v4hi_v4hi);
 
       def_builtin_const ("__builtin_vis_fsll16", CODE_FOR_vashlv4hi3,
-			 v4hi_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FSLL16, v4hi_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fslas16", CODE_FOR_vssashlv4hi3,
-			 v4hi_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FSLAS16, v4hi_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fsrl16", CODE_FOR_vlshrv4hi3,
-			 v4hi_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FSRL16, v4hi_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fsra16", CODE_FOR_vashrv4hi3,
-			 v4hi_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FSRA16, v4hi_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fsll32", CODE_FOR_vashlv2si3,
-			 v2si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FSLL32, v2si_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fslas32", CODE_FOR_vssashlv2si3,
-			 v2si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FSLAS32, v2si_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fsrl32", CODE_FOR_vlshrv2si3,
-			 v2si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FSRL32, v2si_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fsra32", CODE_FOR_vashrv2si3,
-			 v2si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FSRA32, v2si_ftype_v2si_v2si);
 
       if (TARGET_ARCH64)
 	def_builtin_const ("__builtin_vis_pdistn", CODE_FOR_pdistndi_vis,
-			   di_ftype_v8qi_v8qi);
+			   SPARC_BUILTIN_PDISTN, di_ftype_v8qi_v8qi);
       else
 	def_builtin_const ("__builtin_vis_pdistn", CODE_FOR_pdistnsi_vis,
-			   si_ftype_v8qi_v8qi);
+			   SPARC_BUILTIN_PDISTN, si_ftype_v8qi_v8qi);
 
       def_builtin_const ("__builtin_vis_fmean16", CODE_FOR_fmean16_vis,
-			 v4hi_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FMEAN16, v4hi_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fpadd64", CODE_FOR_fpadd64_vis,
-			 di_ftype_di_di);
+			 SPARC_BUILTIN_FPADD64, di_ftype_di_di);
       def_builtin_const ("__builtin_vis_fpsub64", CODE_FOR_fpsub64_vis,
-			 di_ftype_di_di);
+			 SPARC_BUILTIN_FPSUB64, di_ftype_di_di);
 
       def_builtin_const ("__builtin_vis_fpadds16", CODE_FOR_ssaddv4hi3,
-			 v4hi_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FPADDS16, v4hi_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fpadds16s", CODE_FOR_ssaddv2hi3,
-			 v2hi_ftype_v2hi_v2hi);
+			 SPARC_BUILTIN_FPADDS16S, v2hi_ftype_v2hi_v2hi);
       def_builtin_const ("__builtin_vis_fpsubs16", CODE_FOR_sssubv4hi3,
-			 v4hi_ftype_v4hi_v4hi);
+			 SPARC_BUILTIN_FPSUBS16, v4hi_ftype_v4hi_v4hi);
       def_builtin_const ("__builtin_vis_fpsubs16s", CODE_FOR_sssubv2hi3,
-			 v2hi_ftype_v2hi_v2hi);
+			 SPARC_BUILTIN_FPSUBS16S, v2hi_ftype_v2hi_v2hi);
       def_builtin_const ("__builtin_vis_fpadds32", CODE_FOR_ssaddv2si3,
-			 v2si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FPADDS32, v2si_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fpadds32s", CODE_FOR_ssaddv1si3,
-			 v1si_ftype_v1si_v1si);
+			 SPARC_BUILTIN_FPADDS32S, v1si_ftype_v1si_v1si);
       def_builtin_const ("__builtin_vis_fpsubs32", CODE_FOR_sssubv2si3,
-			 v2si_ftype_v2si_v2si);
+			 SPARC_BUILTIN_FPSUBS32, v2si_ftype_v2si_v2si);
       def_builtin_const ("__builtin_vis_fpsubs32s", CODE_FOR_sssubv1si3,
-			 v1si_ftype_v1si_v1si);
+			 SPARC_BUILTIN_FPSUBS32S, v1si_ftype_v1si_v1si);
 
       if (TARGET_ARCH64)
 	{
 	  def_builtin_const ("__builtin_vis_fucmple8", CODE_FOR_fucmple8di_vis,
-			     di_ftype_v8qi_v8qi);
+			     SPARC_BUILTIN_FUCMPLE8, di_ftype_v8qi_v8qi);
 	  def_builtin_const ("__builtin_vis_fucmpne8", CODE_FOR_fucmpne8di_vis,
-			     di_ftype_v8qi_v8qi);
+			     SPARC_BUILTIN_FUCMPNE8, di_ftype_v8qi_v8qi);
 	  def_builtin_const ("__builtin_vis_fucmpgt8", CODE_FOR_fucmpgt8di_vis,
-			     di_ftype_v8qi_v8qi);
+			     SPARC_BUILTIN_FUCMPGT8, di_ftype_v8qi_v8qi);
 	  def_builtin_const ("__builtin_vis_fucmpeq8", CODE_FOR_fucmpeq8di_vis,
-			     di_ftype_v8qi_v8qi);
+			     SPARC_BUILTIN_FUCMPEQ8, di_ftype_v8qi_v8qi);
 	}
       else
 	{
 	  def_builtin_const ("__builtin_vis_fucmple8", CODE_FOR_fucmple8si_vis,
-			     si_ftype_v8qi_v8qi);
+			     SPARC_BUILTIN_FUCMPLE8, si_ftype_v8qi_v8qi);
 	  def_builtin_const ("__builtin_vis_fucmpne8", CODE_FOR_fucmpne8si_vis,
-			     si_ftype_v8qi_v8qi);
+			     SPARC_BUILTIN_FUCMPNE8, si_ftype_v8qi_v8qi);
 	  def_builtin_const ("__builtin_vis_fucmpgt8", CODE_FOR_fucmpgt8si_vis,
-			     si_ftype_v8qi_v8qi);
+			     SPARC_BUILTIN_FUCMPGT8, si_ftype_v8qi_v8qi);
 	  def_builtin_const ("__builtin_vis_fucmpeq8", CODE_FOR_fucmpeq8si_vis,
-			     si_ftype_v8qi_v8qi);
+			     SPARC_BUILTIN_FUCMPEQ8, si_ftype_v8qi_v8qi);
 	}
 
       def_builtin_const ("__builtin_vis_fhadds", CODE_FOR_fhaddsf_vis,
-			 sf_ftype_sf_sf);
+			 SPARC_BUILTIN_FHADDS, sf_ftype_sf_sf);
       def_builtin_const ("__builtin_vis_fhaddd", CODE_FOR_fhadddf_vis,
-			 df_ftype_df_df);
+			 SPARC_BUILTIN_FHADDD, df_ftype_df_df);
       def_builtin_const ("__builtin_vis_fhsubs", CODE_FOR_fhsubsf_vis,
-			 sf_ftype_sf_sf);
+			 SPARC_BUILTIN_FHSUBS, sf_ftype_sf_sf);
       def_builtin_const ("__builtin_vis_fhsubd", CODE_FOR_fhsubdf_vis,
-			 df_ftype_df_df);
+			 SPARC_BUILTIN_FHSUBD, df_ftype_df_df);
       def_builtin_const ("__builtin_vis_fnhadds", CODE_FOR_fnhaddsf_vis,
-			 sf_ftype_sf_sf);
+			 SPARC_BUILTIN_FNHADDS, sf_ftype_sf_sf);
       def_builtin_const ("__builtin_vis_fnhaddd", CODE_FOR_fnhadddf_vis,
-			 df_ftype_df_df);
+			 SPARC_BUILTIN_FNHADDD, df_ftype_df_df);
 
       def_builtin_const ("__builtin_vis_umulxhi", CODE_FOR_umulxhi_vis,
-			 di_ftype_di_di);
+			 SPARC_BUILTIN_UMULXHI, di_ftype_di_di);
       def_builtin_const ("__builtin_vis_xmulx", CODE_FOR_xmulx_vis,
-			 di_ftype_di_di);
+			 SPARC_BUILTIN_XMULX, di_ftype_di_di);
       def_builtin_const ("__builtin_vis_xmulxhi", CODE_FOR_xmulxhi_vis,
-			 di_ftype_di_di);
+			 SPARC_BUILTIN_XMULXHI, di_ftype_di_di);
     }
 }
 
-/* Handle TARGET_EXPAND_BUILTIN target hook.
-   Expand builtin functions for sparc intrinsics.  */
+/* Implement TARGET_BUILTIN_DECL hook.  */
+
+static tree
+sparc_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+  if (code >= SPARC_BUILTIN_MAX)
+    return error_mark_node;
+
+  return sparc_builtins[code];
+}
+
+/* Implemented TARGET_EXPAND_BUILTIN hook.  */
 
 static rtx
 sparc_expand_builtin (tree exp, rtx target,
@@ -10414,15 +10579,14 @@  sparc_expand_builtin (tree exp, rtx targ
 		      enum machine_mode tmode ATTRIBUTE_UNUSED,
 		      int ignore ATTRIBUTE_UNUSED)
 {
-  tree arg;
-  call_expr_arg_iterator iter;
   tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
-  unsigned int icode = DECL_FUNCTION_CODE (fndecl);
-  rtx pat, op[4];
+  enum sparc_builtins code = (enum sparc_builtins) DECL_FUNCTION_CODE (fndecl);
+  enum insn_code icode = sparc_builtins_icode[code];
+  bool nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
+  call_expr_arg_iterator iter;
   int arg_count = 0;
-  bool nonvoid;
-
-  nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
+  rtx pat, op[4];
+  tree arg;
 
   if (nonvoid)
     {
@@ -10434,6 +10598,7 @@  sparc_expand_builtin (tree exp, rtx targ
       else
 	op[0] = target;
     }
+
   FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
     {
       const struct insn_operand_data *insn_op;
@@ -10447,11 +10612,22 @@  sparc_expand_builtin (tree exp, rtx targ
       insn_op = &insn_data[icode].operand[idx];
       op[arg_count] = expand_normal (arg);
 
-      if (insn_op->mode == V1DImode
-	  && GET_MODE (op[arg_count]) == DImode)
+      if (code == SPARC_BUILTIN_LDFSR || code == SPARC_BUILTIN_STFSR)
+	{
+	  if (!address_operand (op[arg_count], SImode))
+	    {
+	      op[arg_count] = convert_memory_address (Pmode, op[arg_count]);
+	      op[arg_count] = copy_addr_to_reg (op[arg_count]);
+	    }
+	  op[arg_count] = gen_rtx_MEM (SImode, op[arg_count]);
+	}
+
+      else if (insn_op->mode == V1DImode
+	       && GET_MODE (op[arg_count]) == DImode)
 	op[arg_count] = gen_lowpart (V1DImode, op[arg_count]);
+
       else if (insn_op->mode == V1SImode
-	  && GET_MODE (op[arg_count]) == SImode)
+	       && GET_MODE (op[arg_count]) == SImode)
 	op[arg_count] = gen_lowpart (V1SImode, op[arg_count]);
 
       if (! (*insn_data[icode].operand[idx].predicate) (op[arg_count],
@@ -10485,12 +10661,11 @@  sparc_expand_builtin (tree exp, rtx targ
 
   emit_insn (pat);
 
-  if (nonvoid)
-    return op[0];
-  else
-    return const0_rtx;
+  return (nonvoid ? op[0] : const0_rtx);
 }
 
+/* Return the upper 16 bits of the 8x16 multiplication.  */
+
 static int
 sparc_vis_mul8x16 (int e8, int e16)
 {
@@ -10501,15 +10676,15 @@  sparc_vis_mul8x16 (int e8, int e16)
    the result into the array N_ELTS, whose elements are of INNER_TYPE.  */
 
 static void
-sparc_handle_vis_mul8x16 (tree *n_elts, int fncode, tree inner_type,
-			  tree cst0, tree cst1)
+sparc_handle_vis_mul8x16 (tree *n_elts, enum sparc_builtins fncode,
+			  tree inner_type, tree cst0, tree cst1)
 {
   unsigned i, num = VECTOR_CST_NELTS (cst0);
   int scale;
 
   switch (fncode)
     {
-    case CODE_FOR_fmul8x16_vis:
+    case SPARC_BUILTIN_FMUL8X16:
       for (i = 0; i < num; ++i)
 	{
 	  int val
@@ -10519,7 +10694,7 @@  sparc_handle_vis_mul8x16 (tree *n_elts,
 	}
       break;
 
-    case CODE_FOR_fmul8x16au_vis:
+    case SPARC_BUILTIN_FMUL8X16AU:
       scale = TREE_INT_CST_LOW (VECTOR_CST_ELT (cst1, 0));
 
       for (i = 0; i < num; ++i)
@@ -10531,7 +10706,7 @@  sparc_handle_vis_mul8x16 (tree *n_elts,
 	}
       break;
 
-    case CODE_FOR_fmul8x16al_vis:
+    case SPARC_BUILTIN_FMUL8X16AL:
       scale = TREE_INT_CST_LOW (VECTOR_CST_ELT (cst1, 1));
 
       for (i = 0; i < num; ++i)
@@ -10548,7 +10723,8 @@  sparc_handle_vis_mul8x16 (tree *n_elts,
     }
 }
 
-/* Handle TARGET_FOLD_BUILTIN target hook.
+/* Implement TARGET_FOLD_BUILTIN hook.
+
    Fold builtin functions for SPARC intrinsics.  If IGNORE is true the
    result of the function call is ignored.  NULL_TREE is returned if the
    function could not be folded.  */
@@ -10557,34 +10733,30 @@  static tree
 sparc_fold_builtin (tree fndecl, int n_args ATTRIBUTE_UNUSED,
 		    tree *args, bool ignore)
 {
-  tree arg0, arg1, arg2;
+  enum sparc_builtins code = (enum sparc_builtins) DECL_FUNCTION_CODE (fndecl);
   tree rtype = TREE_TYPE (TREE_TYPE (fndecl));
-  enum insn_code icode = (enum insn_code) DECL_FUNCTION_CODE (fndecl);
+  tree arg0, arg1, arg2;
 
   if (ignore)
-    {
-      /* Note that a switch statement instead of the sequence of tests would
-	 be incorrect as many of the CODE_FOR values could be CODE_FOR_nothing
-	 and that would yield multiple alternatives with identical values.  */
-      if (icode == CODE_FOR_alignaddrsi_vis
-	  || icode == CODE_FOR_alignaddrdi_vis
-	  || icode == CODE_FOR_wrgsr_vis
-	  || icode == CODE_FOR_bmasksi_vis
-	  || icode == CODE_FOR_bmaskdi_vis
-	  || icode == CODE_FOR_cmask8si_vis
-	  || icode == CODE_FOR_cmask8di_vis
-	  || icode == CODE_FOR_cmask16si_vis
-	  || icode == CODE_FOR_cmask16di_vis
-	  || icode == CODE_FOR_cmask32si_vis
-	  || icode == CODE_FOR_cmask32di_vis)
-	;
-      else
+    switch (code)
+      {
+      case SPARC_BUILTIN_LDFSR:
+      case SPARC_BUILTIN_STFSR:
+      case SPARC_BUILTIN_ALIGNADDR:
+      case SPARC_BUILTIN_WRGSR:
+      case SPARC_BUILTIN_BMASK:
+      case SPARC_BUILTIN_CMASK8:
+      case SPARC_BUILTIN_CMASK16:
+      case SPARC_BUILTIN_CMASK32:
+	break;
+
+      default:
 	return build_zero_cst (rtype);
-    }
+      }
 
-  switch (icode)
+  switch (code)
     {
-    case CODE_FOR_fexpand_vis:
+    case SPARC_BUILTIN_FEXPAND:
       arg0 = args[0];
       STRIP_NOPS (arg0);
 
@@ -10603,9 +10775,9 @@  sparc_fold_builtin (tree fndecl, int n_a
 	}
       break;
 
-    case CODE_FOR_fmul8x16_vis:
-    case CODE_FOR_fmul8x16au_vis:
-    case CODE_FOR_fmul8x16al_vis:
+    case SPARC_BUILTIN_FMUL8X16:
+    case SPARC_BUILTIN_FMUL8X16AU:
+    case SPARC_BUILTIN_FMUL8X16AL:
       arg0 = args[0];
       arg1 = args[1];
       STRIP_NOPS (arg0);
@@ -10615,12 +10787,12 @@  sparc_fold_builtin (tree fndecl, int n_a
 	{
 	  tree inner_type = TREE_TYPE (rtype);
 	  tree *n_elts = XALLOCAVEC (tree, VECTOR_CST_NELTS (arg0));
-	  sparc_handle_vis_mul8x16 (n_elts, icode, inner_type, arg0, arg1);
+	  sparc_handle_vis_mul8x16 (n_elts, code, inner_type, arg0, arg1);
 	  return build_vector (rtype, n_elts);
 	}
       break;
 
-    case CODE_FOR_fpmerge_vis:
+    case SPARC_BUILTIN_FPMERGE:
       arg0 = args[0];
       arg1 = args[1];
       STRIP_NOPS (arg0);
@@ -10640,13 +10812,19 @@  sparc_fold_builtin (tree fndecl, int n_a
 	}
       break;
 
-    case CODE_FOR_pdist_vis:
+    case SPARC_BUILTIN_PDIST:
+    case SPARC_BUILTIN_PDISTN:
       arg0 = args[0];
       arg1 = args[1];
-      arg2 = args[2];
       STRIP_NOPS (arg0);
       STRIP_NOPS (arg1);
-      STRIP_NOPS (arg2);
+      if (code == SPARC_BUILTIN_PDIST)
+	{
+	  arg2 = args[2];
+	  STRIP_NOPS (arg2);
+	}
+      else
+	arg2 = integer_zero_node;
 
       if (TREE_CODE (arg0) == VECTOR_CST
 	  && TREE_CODE (arg1) == VECTOR_CST
@@ -12325,9 +12503,134 @@  sparc_modes_tieable_p (enum machine_mode
   return true;
 }
 
-static enum machine_mode sparc_cstore_mode (enum insn_code icode ATTRIBUTE_UNUSED)
+/* Implement TARGET_CSTORE_MODE.  */
+
+static enum machine_mode
+sparc_cstore_mode (enum insn_code icode ATTRIBUTE_UNUSED)
 {
   return (TARGET_ARCH64 ? DImode : SImode);
 }
 
+/* Return the compound expression made of T1 and T2.  */
+
+static inline tree
+compound_expr (tree t1, tree t2)
+{
+  return build2 (COMPOUND_EXPR, void_type_node, t1, t2);
+}
+
+/* Implement TARGET_ATOMIC_ASSIGN_EXPAND_FENV hook.  */
+
+static void
+sparc_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
+{
+  if (!TARGET_FPU)
+    return;
+
+  const unsigned HOST_WIDE_INT accrued_exception_mask = 0x1f << 5;
+  const unsigned HOST_WIDE_INT trap_enable_mask = 0x1f << 23;
+
+  /* We generate the equivalent of feholdexcept (&fenv_var):
+
+       unsigned int fenv_var;
+       __builtin_store_fsr (&fenv_var);
+
+       unsigned int tmp1_var;
+       tmp1_var = fenv_var & ~(accrued_exception_mask | trap_enable_mask);
+
+       __builtin_load_fsr (&tmp1_var);  */
+
+  tree fenv_var = create_tmp_var (unsigned_type_node, NULL);
+  mark_addressable (fenv_var);
+  tree fenv_addr = build_fold_addr_expr (fenv_var);
+  tree stfsr = sparc_builtins[SPARC_BUILTIN_STFSR];
+  tree hold_stfsr = build_call_expr (stfsr, 1, fenv_addr);
+
+  tree tmp1_var = create_tmp_var (unsigned_type_node, NULL);
+  mark_addressable (tmp1_var);
+  tree masked_fenv_var
+    = build2 (BIT_AND_EXPR, unsigned_type_node, fenv_var,
+	      build_int_cst (unsigned_type_node,
+			     ~(accrued_exception_mask | trap_enable_mask)));
+  tree hold_mask
+    = build2 (MODIFY_EXPR, void_type_node, tmp1_var, masked_fenv_var);
+
+  tree tmp1_addr = build_fold_addr_expr (tmp1_var);
+  tree ldfsr = sparc_builtins[SPARC_BUILTIN_LDFSR];
+  tree hold_ldfsr = build_call_expr (ldfsr, 1, tmp1_addr);
+
+  *hold = compound_expr (compound_expr (hold_stfsr, hold_mask), hold_ldfsr);
+
+  /* We generate the equivalent of feclearexcept (FE_ALL_EXCEPT):
+
+       unsigned int tmp2_var;
+       __builtin_store_fsr (&tmp2_var);
+
+       tmp2_var &= ~accrued_exception_mask;
+
+       __builtin_load_fsr (&tmp2_var);  */
+
+  tree tmp2_var = create_tmp_var (unsigned_type_node, NULL);
+  mark_addressable (tmp2_var);
+  tree tmp2_addr = build_fold_addr_expr (tmp2_var);
+  tree clear_stfsr = build_call_expr (stfsr, 1, tmp2_addr);
+
+  tree masked_tmp2_var
+    = build2 (BIT_AND_EXPR, unsigned_type_node, tmp2_var,
+	      build_int_cst (unsigned_type_node, ~accrued_exception_mask));
+  tree clear_mask
+    = build2 (MODIFY_EXPR, void_type_node, tmp2_var, masked_tmp2_var);
+
+  tree clear_ldfsr = build_call_expr (ldfsr, 1, tmp2_addr);
+
+  *clear
+    = compound_expr (compound_expr (clear_stfsr, clear_mask), clear_ldfsr);
+
+  /* We generate the equivalent of feupdateenv (&fenv_var):
+
+       unsigned int tmp3_var;
+       __builtin_store_fsr (&tmp3_var);
+
+       __builtin_load_fsr (&fenv_var);
+
+       tmp3_var &= accrued_exception_mask;
+       if (SPARC_LOW_FE_EXCEPT_VALUES)
+         tmp3_var >>= 5;
+
+       __atomic_feraiseexcept ((int) tmp3_var);  */
+
+  tree tmp3_var = create_tmp_var (unsigned_type_node, NULL);
+  mark_addressable (tmp3_var);
+  tree tmp3_addr = build_fold_addr_expr (tmp3_var);
+  tree update_stfsr = build_call_expr (stfsr, 1, tmp3_addr);
+
+  tree update_ldfsr = build_call_expr (ldfsr, 1, fenv_addr);
+
+  tree masked_tmp3_var
+    = build2 (BIT_AND_EXPR, unsigned_type_node, tmp3_var,
+	      build_int_cst (unsigned_type_node, accrued_exception_mask));
+  tree update_mask
+    = build2 (MODIFY_EXPR, void_type_node, tmp3_var, masked_tmp3_var);
+  if (SPARC_LOW_FE_EXCEPT_VALUES)
+    {
+      tree shifted_tmp3_var
+	= build2 (RSHIFT_EXPR, unsigned_type_node, tmp3_var,
+		  build_int_cst (unsigned_type_node, 5));
+      tree update_shift
+	= build2 (MODIFY_EXPR, void_type_node, tmp3_var, shifted_tmp3_var);
+      update_mask = compound_expr (update_mask, update_shift);
+    }
+
+  tree atomic_feraiseexcept
+    = builtin_decl_implicit (BUILT_IN_ATOMIC_FERAISEEXCEPT);
+  tree update_call
+    = build_call_expr (atomic_feraiseexcept, 1,
+		       fold_convert (integer_type_node, tmp3_var));
+
+  *update
+    = compound_expr (compound_expr (compound_expr (update_stfsr, update_ldfsr),
+				    update_mask),
+		     update_call);
+}
+
 #include "gt-sparc.h"
Index: config/sparc/sparc.h
===================================================================
--- config/sparc/sparc.h	(revision 205689)
+++ config/sparc/sparc.h	(working copy)
@@ -1777,3 +1777,6 @@  extern int sparc_indent_opcode;
 #ifndef SUBTARGET_DEFAULT_MEMORY_MODEL
 #define SUBTARGET_DEFAULT_MEMORY_MODEL	SMM_DEFAULT
 #endif
+
+/* Define this to 1 if the FE_EXCEPT values defined in fenv.h start at 1.  */
+#define SPARC_LOW_FE_EXCEPT_VALUES 0
Index: config/sparc/sol2.h
===================================================================
--- config/sparc/sol2.h	(revision 205689)
+++ config/sparc/sol2.h	(working copy)
@@ -406,3 +406,6 @@  extern const char *host_detect_local_cpu
 
 #undef SUN_INTEGER_MULTIPLY_64
 #define SUN_INTEGER_MULTIPLY_64 1
+
+#undef SPARC_LOW_FE_EXCEPT_VALUES
+#define SPARC_LOW_FE_EXCEPT_VALUES 1