OSDN Git Service

* target.h (invalid_conversion, invalid_unary_op,
authorjsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 28 Jun 2005 21:55:24 +0000 (21:55 +0000)
committerjsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 28 Jun 2005 21:55:24 +0000 (21:55 +0000)
invalid_binary_op): New hooks.
* target-def.h (TARGET_INVALID_CONVERSION,
TARGET_INVALID_UNARY_OP, TARGET_INVALID_BINARY_OP,
TARGET_INITIALIZER): Likewise.
* hooks.h (hook_constcharptr_tree_tree_null,
hook_constcharptr_int_tree_null,
hook_constcharptr_int_tree_tree_null): New.
* hooks.c (hook_constcharptr_tree_tree_null,
hook_constcharptr_int_tree_null,
hook_constcharptr_int_tree_tree_null): Likewise.
* gcc/doc/tm.texi (TARGET_INVALID_CONVERSION,
TARGET_INVALID_UNARY_OP, TARGET_INVALID_BINARY_OP): Document.
* c-convert.c (convert): Use invalid_conversion hook.
* c-typeck.c (build_unary_op): Use invalid_unary_op hook.
(build_binary_op): Use invalid_binary_op hook.
* config/ia64/ia64-modes.def: Define RFmode.
* config/ia64/ia64-protos.h (spill_xfmode_operand): Remove.
(ia64_expand_movxf_movrf): New.
* config/ia64/ia64.md (movxf): Move code to
ia64_expand_movxf_movrf.
(movrf, movrf_internal): New.
* ia64.c (ia64_invalid_conversion, ia64_invalid_unary_op,
ia64_invalid_binary_op, TARGET_INVALID_CONVERSION,
TARGET_INVALID_UNARY_OP, TARGET_INVALID_BINARY_OP): New.
(spill_xfmode_operand): Rename to spill_xfmode_rfmode_operand.
Add mode parameter.  Make static.
(ia64_expand_movxf_movrf): New, moved from ia64.md.  Handle RFmode
as well as XFmode.
(ia64_function_arg, ia64_function_value, ia64_register_move_cost,
ia64_scalar_mode_supported_p): Handle RFmode as well as XFmode.
(ia64_init_builtins): Set up __fpreg as RFmode.
(ia64_mangle_fundamental_type): Mangle __fpreg as u7__fpreg.

cp:
* cvt.c (ocp_convert): Use invalid_conversion hook.
* typeck.c (build_binary_op): Use invalid_binary_op hook.
(build_unary_op): Use invalid_unary_op hook.

testsuite:
* g++.dg/ext/fpreg1.C, gcc.target/ia64/fpreg-1.c,
gcc.target/ia64/fpreg-2.c: New tests.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@101391 138bc75d-0d04-0410-961f-82ee72b054a4

19 files changed:
gcc/ChangeLog
gcc/c-convert.c
gcc/c-typeck.c
gcc/config/ia64/ia64-modes.def
gcc/config/ia64/ia64-protos.h
gcc/config/ia64/ia64.c
gcc/config/ia64/ia64.md
gcc/cp/ChangeLog
gcc/cp/cvt.c
gcc/cp/typeck.c
gcc/doc/tm.texi
gcc/hooks.c
gcc/hooks.h
gcc/target-def.h
gcc/target.h
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ext/fpreg1.C [new file with mode: 0644]
gcc/testsuite/gcc.target/ia64/fpreg-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/ia64/fpreg-2.c [new file with mode: 0644]

index 225451d..cb67755 100644 (file)
@@ -1,3 +1,39 @@
+2005-06-28  Joseph S. Myers  <joseph@codesourcery.com>
+
+       * target.h (invalid_conversion, invalid_unary_op,
+       invalid_binary_op): New hooks.
+       * target-def.h (TARGET_INVALID_CONVERSION,
+       TARGET_INVALID_UNARY_OP, TARGET_INVALID_BINARY_OP,
+       TARGET_INITIALIZER): Likewise.
+       * hooks.h (hook_constcharptr_tree_tree_null,
+       hook_constcharptr_int_tree_null,
+       hook_constcharptr_int_tree_tree_null): New.
+       * hooks.c (hook_constcharptr_tree_tree_null,
+       hook_constcharptr_int_tree_null,
+       hook_constcharptr_int_tree_tree_null): Likewise.
+       * gcc/doc/tm.texi (TARGET_INVALID_CONVERSION,
+       TARGET_INVALID_UNARY_OP, TARGET_INVALID_BINARY_OP): Document.
+       * c-convert.c (convert): Use invalid_conversion hook.
+       * c-typeck.c (build_unary_op): Use invalid_unary_op hook.
+       (build_binary_op): Use invalid_binary_op hook.
+       * config/ia64/ia64-modes.def: Define RFmode.
+       * config/ia64/ia64-protos.h (spill_xfmode_operand): Remove.
+       (ia64_expand_movxf_movrf): New.
+       * config/ia64/ia64.md (movxf): Move code to
+       ia64_expand_movxf_movrf.
+       (movrf, movrf_internal): New.
+       * ia64.c (ia64_invalid_conversion, ia64_invalid_unary_op,
+       ia64_invalid_binary_op, TARGET_INVALID_CONVERSION,
+       TARGET_INVALID_UNARY_OP, TARGET_INVALID_BINARY_OP): New.
+       (spill_xfmode_operand): Rename to spill_xfmode_rfmode_operand.
+       Add mode parameter.  Make static.
+       (ia64_expand_movxf_movrf): New, moved from ia64.md.  Handle RFmode
+       as well as XFmode.
+       (ia64_function_arg, ia64_function_value, ia64_register_move_cost,
+       ia64_scalar_mode_supported_p): Handle RFmode as well as XFmode.
+       (ia64_init_builtins): Set up __fpreg as RFmode.
+       (ia64_mangle_fundamental_type): Mangle __fpreg as u7__fpreg.
+
 2006-06-28  Adrian Straetling  <straetling@de.ibm.com>
 
        * builtins.c: (expand_builtin_memset): Rewrite to support
index 8b78383..50bb923 100644 (file)
@@ -36,6 +36,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "c-tree.h"
 #include "langhooks.h"
 #include "toplev.h"
+#include "target.h"
 
 /* Change of width--truncation and extension of integers or reals--
    is represented with NOP_EXPR.  Proper functioning of many things
@@ -69,10 +70,21 @@ convert (tree type, tree expr)
 {
   tree e = expr;
   enum tree_code code = TREE_CODE (type);
+  const char *invalid_conv_diag;
 
-  if (type == TREE_TYPE (expr)
-      || TREE_CODE (expr) == ERROR_MARK
-      || code == ERROR_MARK || TREE_CODE (TREE_TYPE (expr)) == ERROR_MARK)
+  if (type == error_mark_node
+      || expr == error_mark_node
+      || TREE_TYPE (expr) == error_mark_node)
+    return error_mark_node;
+
+  if ((invalid_conv_diag
+       = targetm.invalid_conversion (TREE_TYPE (expr), type)))
+    {
+      error (invalid_conv_diag);
+      return error_mark_node;
+    }
+
+  if (type == TREE_TYPE (expr))
     return expr;
 
   if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (expr)))
index da826d4..34f94d1 100644 (file)
@@ -2501,12 +2501,20 @@ build_unary_op (enum tree_code code, tree xarg, int flag)
   enum tree_code typecode = TREE_CODE (TREE_TYPE (arg));
   tree val;
   int noconvert = flag;
+  const char *invalid_op_diag;
 
   if (typecode == ERROR_MARK)
     return error_mark_node;
   if (typecode == ENUMERAL_TYPE || typecode == BOOLEAN_TYPE)
     typecode = INTEGER_TYPE;
 
+  if ((invalid_op_diag
+       = targetm.invalid_unary_op (code, TREE_TYPE (xarg))))
+    {
+      error (invalid_op_diag);
+      return error_mark_node;
+    }
+
   switch (code)
     {
     case CONVERT_EXPR:
@@ -7397,6 +7405,7 @@ build_binary_op (enum tree_code code, tree orig_op0, tree orig_op1,
   tree type0, type1;
   enum tree_code code0, code1;
   tree op0, op1;
+  const char *invalid_op_diag;
 
   /* Expression code to give to the expression when it is built.
      Normally this is CODE, which is what the caller asked for,
@@ -7472,6 +7481,13 @@ build_binary_op (enum tree_code code, tree orig_op0, tree orig_op1,
   if (code0 == ERROR_MARK || code1 == ERROR_MARK)
     return error_mark_node;
 
+  if ((invalid_op_diag
+       = targetm.invalid_binary_op (code, type0, type1)))
+    {
+      error (invalid_op_diag);
+      return error_mark_node;
+    }
+
   objc_ok = objc_compare_types (type0, type1, -3, NULL_TREE);
 
   switch (code)
index 04e4108..c7e9927 100644 (file)
@@ -23,9 +23,12 @@ Boston, MA 02110-1301, USA.  */
 /* IA64 requires both XF and TF modes.
    XFmode is __float80 is IEEE extended; TFmode is __float128
    is IEEE quad.  Both these modes occupy 16 bytes, but XFmode
-   only has 80 significant bits.  */
+   only has 80 significant bits.  RFmode is __fpreg is IA64 internal
+   register format with 82 significant bits but otherwise handled like
+   XFmode.  */
 
 FRACTIONAL_FLOAT_MODE (XF, 80, 16, ieee_extended_intel_128_format);
+FRACTIONAL_FLOAT_MODE (RF, 82, 16, ieee_extended_intel_128_format);
 FLOAT_MODE (TF, 16, ieee_quad_format);
 
 /* The above produces:
@@ -54,6 +57,12 @@ ADJUST_FLOAT_FORMAT (XF, (TARGET_ILP32 && !TARGET_HPUX)
 ADJUST_BYTESIZE  (XF, (TARGET_ILP32 && !TARGET_HPUX) ? 12 : 16);
 ADJUST_ALIGNMENT (XF, (TARGET_ILP32 && !TARGET_HPUX) ?  4 : 16);
 
+ADJUST_FLOAT_FORMAT (RF, (TARGET_ILP32 && !TARGET_HPUX)
+                        ? &ieee_extended_intel_96_format
+                        : &ieee_extended_intel_128_format);
+ADJUST_BYTESIZE  (RF, (TARGET_ILP32 && !TARGET_HPUX) ? 12 : 16);
+ADJUST_ALIGNMENT (RF, (TARGET_ILP32 && !TARGET_HPUX) ?  4 : 16);
+
 ADJUST_ALIGNMENT (TF, (TARGET_ILP32 &&  TARGET_HPUX) ?  8 : 16);
 
 /* 256-bit integer mode is needed for STACK_SAVEAREA_MODE.  */
index 1fe645b..8684649 100644 (file)
@@ -45,7 +45,7 @@ extern int addp4_optimize_ok (rtx, rtx);
 extern void ia64_emit_cond_move (rtx, rtx, rtx);
 extern int ia64_depz_field_mask (rtx, rtx);
 extern void ia64_split_tmode_move (rtx[]);
-extern rtx spill_xfmode_operand (rtx, int);
+extern bool ia64_expand_movxf_movrf (enum machine_mode, rtx[]);
 extern rtx ia64_expand_compare (enum rtx_code, enum machine_mode);
 extern void ia64_expand_vecint_cmov (rtx[]);
 extern bool ia64_expand_vecint_minmax (enum rtx_code, enum machine_mode, rtx[]);
index 5109d4f..7ebe293 100644 (file)
@@ -52,6 +52,7 @@ Boston, MA 02110-1301, USA.  */
 #include "langhooks.h"
 #include "cfglayout.h"
 #include "tree-gimple.h"
+#include "intl.h"
 
 /* This is used for communication between ASM_OUTPUT_LABEL and
    ASM_OUTPUT_LABELREF.  */
@@ -263,6 +264,9 @@ static bool ia64_scalar_mode_supported_p (enum machine_mode mode);
 static bool ia64_vector_mode_supported_p (enum machine_mode mode);
 static bool ia64_cannot_force_const_mem (rtx);
 static const char *ia64_mangle_fundamental_type (tree);
+static const char *ia64_invalid_conversion (tree, tree);
+static const char *ia64_invalid_unary_op (int, tree);
+static const char *ia64_invalid_binary_op (int, tree, tree);
 \f
 /* Table of valid machine attributes.  */
 static const struct attribute_spec ia64_attribute_table[] =
@@ -433,6 +437,13 @@ static const struct attribute_spec ia64_attribute_table[] =
 #undef TARGET_MANGLE_FUNDAMENTAL_TYPE
 #define TARGET_MANGLE_FUNDAMENTAL_TYPE ia64_mangle_fundamental_type
 
+#undef TARGET_INVALID_CONVERSION
+#define TARGET_INVALID_CONVERSION ia64_invalid_conversion
+#undef TARGET_INVALID_UNARY_OP
+#define TARGET_INVALID_UNARY_OP ia64_invalid_unary_op
+#undef TARGET_INVALID_BINARY_OP
+#define TARGET_INVALID_BINARY_OP ia64_invalid_binary_op
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 typedef enum
@@ -1285,8 +1296,8 @@ ia64_split_tmode_move (rtx operands[])
    This solution attempts to prevent this situation from occurring.  When
    we see something like the above, we spill the inner register to memory.  */
 
-rtx
-spill_xfmode_operand (rtx in, int force)
+static rtx
+spill_xfmode_rfmode_operand (rtx in, int force, enum machine_mode mode)
 {
   if (GET_CODE (in) == SUBREG
       && GET_MODE (SUBREG_REG (in)) == TImode
@@ -1294,11 +1305,11 @@ spill_xfmode_operand (rtx in, int force)
     {
       rtx memt = assign_stack_temp (TImode, 16, 0);
       emit_move_insn (memt, SUBREG_REG (in));
-      return adjust_address (memt, XFmode, 0);
+      return adjust_address (memt, mode, 0);
     }
   else if (force && GET_CODE (in) == REG)
     {
-      rtx memx = assign_stack_temp (XFmode, 16, 0);
+      rtx memx = assign_stack_temp (mode, 16, 0);
       emit_move_insn (memx, in);
       return memx;
     }
@@ -1306,6 +1317,132 @@ spill_xfmode_operand (rtx in, int force)
     return in;
 }
 
+/* Expand the movxf or movrf pattern (MODE says which) with the given
+   OPERANDS, returning true if the pattern should then invoke
+   DONE.  */
+
+bool
+ia64_expand_movxf_movrf (enum machine_mode mode, rtx operands[])
+{
+  rtx op0 = operands[0];
+
+  if (GET_CODE (op0) == SUBREG)
+    op0 = SUBREG_REG (op0);
+
+  /* We must support XFmode loads into general registers for stdarg/vararg,
+     unprototyped calls, and a rare case where a long double is passed as
+     an argument after a float HFA fills the FP registers.  We split them into
+     DImode loads for convenience.  We also need to support XFmode stores
+     for the last case.  This case does not happen for stdarg/vararg routines,
+     because we do a block store to memory of unnamed arguments.  */
+
+  if (GET_CODE (op0) == REG && GR_REGNO_P (REGNO (op0)))
+    {
+      rtx out[2];
+
+      /* We're hoping to transform everything that deals with XFmode
+        quantities and GR registers early in the compiler.  */
+      gcc_assert (!no_new_pseudos);
+
+      /* Struct to register can just use TImode instead.  */
+      if ((GET_CODE (operands[1]) == SUBREG
+          && GET_MODE (SUBREG_REG (operands[1])) == TImode)
+         || (GET_CODE (operands[1]) == REG
+             && GR_REGNO_P (REGNO (operands[1]))))
+       {
+         rtx op1 = operands[1];
+
+         if (GET_CODE (op1) == SUBREG)
+           op1 = SUBREG_REG (op1);
+         else
+           op1 = gen_rtx_REG (TImode, REGNO (op1));
+
+         emit_move_insn (gen_rtx_REG (TImode, REGNO (op0)), op1);
+         return true;
+       }
+
+      if (GET_CODE (operands[1]) == CONST_DOUBLE)
+       {
+         emit_move_insn (gen_rtx_REG (DImode, REGNO (op0)),
+                         operand_subword (operands[1], 0, 0, mode));
+         emit_move_insn (gen_rtx_REG (DImode, REGNO (op0) + 1),
+                         operand_subword (operands[1], 1, 0, mode));
+         return true;
+       }
+
+      /* If the quantity is in a register not known to be GR, spill it.  */
+      if (register_operand (operands[1], mode))
+       operands[1] = spill_xfmode_rfmode_operand (operands[1], 1, mode);
+
+      gcc_assert (GET_CODE (operands[1]) == MEM);
+
+      out[WORDS_BIG_ENDIAN] = gen_rtx_REG (DImode, REGNO (op0));
+      out[!WORDS_BIG_ENDIAN] = gen_rtx_REG (DImode, REGNO (op0) + 1);
+
+      emit_move_insn (out[0], adjust_address (operands[1], DImode, 0));
+      emit_move_insn (out[1], adjust_address (operands[1], DImode, 8));
+      return true;
+    }
+
+  if (GET_CODE (operands[1]) == REG && GR_REGNO_P (REGNO (operands[1])))
+    {
+      /* We're hoping to transform everything that deals with XFmode
+        quantities and GR registers early in the compiler.  */
+      gcc_assert (!no_new_pseudos);
+
+      /* Op0 can't be a GR_REG here, as that case is handled above.
+        If op0 is a register, then we spill op1, so that we now have a
+        MEM operand.  This requires creating an XFmode subreg of a TImode reg
+        to force the spill.  */
+      if (register_operand (operands[0], mode))
+       {
+         rtx op1 = gen_rtx_REG (TImode, REGNO (operands[1]));
+         op1 = gen_rtx_SUBREG (mode, op1, 0);
+         operands[1] = spill_xfmode_rfmode_operand (op1, 0, mode);
+       }
+
+      else
+       {
+         rtx in[2];
+
+          gcc_assert (GET_CODE (operands[0]) == MEM);
+         in[WORDS_BIG_ENDIAN] = gen_rtx_REG (DImode, REGNO (operands[1]));
+         in[!WORDS_BIG_ENDIAN] = gen_rtx_REG (DImode, REGNO (operands[1]) + 1);
+
+         emit_move_insn (adjust_address (operands[0], DImode, 0), in[0]);
+         emit_move_insn (adjust_address (operands[0], DImode, 8), in[1]);
+         return true;
+       }
+    }
+
+  if (!reload_in_progress && !reload_completed)
+    {
+      operands[1] = spill_xfmode_rfmode_operand (operands[1], 0, mode);
+
+      if (GET_MODE (op0) == TImode && GET_CODE (op0) == REG)
+       {
+         rtx memt, memx, in = operands[1];
+         if (CONSTANT_P (in))
+           in = validize_mem (force_const_mem (mode, in));
+         if (GET_CODE (in) == MEM)
+           memt = adjust_address (in, TImode, 0);
+         else
+           {
+             memt = assign_stack_temp (TImode, 16, 0);
+             memx = adjust_address (memt, mode, 0);
+             emit_move_insn (memx, in);
+           }
+         emit_move_insn (op0, memt);
+         return true;
+       }
+
+      if (!ia64_move_ok (operands[0], operands[1]))
+       operands[1] = force_reg (mode, operands[1]);
+    }
+
+  return false;
+}
+
 /* Emit comparison instruction if necessary, returning the expression
    that holds the compare result in the proper mode.  */
 
@@ -3839,9 +3976,9 @@ ia64_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
                    gen_rtx_EXPR_LIST (VOIDmode,
                     gen_rtx_REG (DImode, basereg + cum->words + offset),
                                      const0_rtx)));
-      /* Similarly, an anonymous XFmode value must be split into two
-        registers and padded appropriately.  */
-      else if (BYTES_BIG_ENDIAN && mode == XFmode)
+      /* Similarly, an anonymous XFmode or RFmode value must be split
+        into two registers and padded appropriately.  */
+      else if (BYTES_BIG_ENDIAN && (mode == XFmode || mode == RFmode))
        {
          rtx loc[2];
          loc[0] = gen_rtx_EXPR_LIST (VOIDmode,
@@ -4159,7 +4296,7 @@ ia64_function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
         the middle-end will give it XFmode anyway, and XFmode values
         don't normally fit in integer registers.  So we need to smuggle
         the value inside a parallel.  */
-      else if (mode == XFmode || mode == XCmode)
+      else if (mode == XFmode || mode == XCmode || mode == RFmode)
        need_parallel = true;
 
       if (need_parallel)
@@ -4574,7 +4711,7 @@ ia64_register_move_cost (enum machine_mode mode, enum reg_class from,
      so that we get secondary memory reloads.  Between FR_REGS,
      we have to make this at least as expensive as MEMORY_MOVE_COST
      to avoid spectacularly poor register class preferencing.  */
-  if (mode == XFmode)
+  if (mode == XFmode || mode == RFmode)
     {
       if (to != GR_REGS || from != GR_REGS)
         return MEMORY_MOVE_COST (mode, to, 0);
@@ -8058,9 +8195,7 @@ ia64_init_builtins (void)
 
   /* The __fpreg type.  */
   fpreg_type = make_node (REAL_TYPE);
-  /* ??? The back end should know to load/save __fpreg variables using
-     the ldf.fill and stf.spill instructions.  */
-  TYPE_PRECISION (fpreg_type) = 80;
+  TYPE_PRECISION (fpreg_type) = 82;
   layout_type (fpreg_type);
   (*lang_hooks.types.register_builtin_type) (fpreg_type, "__fpreg");
 
@@ -8549,6 +8684,7 @@ ia64_scalar_mode_supported_p (enum machine_mode mode)
     case SFmode:
     case DFmode:
     case XFmode:
+    case RFmode:
       return true;
 
     case TFmode:
@@ -8659,6 +8795,48 @@ ia64_mangle_fundamental_type (tree type)
      double is 80 bits.  */
   if (TYPE_MODE (type) == XFmode)
     return TARGET_HPUX ? "u9__float80" : "e";
+  if (TYPE_MODE (type) == RFmode)
+    return "u7__fpreg";
+  return NULL;
+}
+
+/* Return the diagnostic message string if conversion from FROMTYPE to
+   TOTYPE is not allowed, NULL otherwise.  */
+static const char *
+ia64_invalid_conversion (tree fromtype, tree totype)
+{
+  /* Reject nontrivial conversion to or from __fpreg.  */
+  if (TYPE_MODE (fromtype) == RFmode
+      && TYPE_MODE (totype) != RFmode
+      && TYPE_MODE (totype) != VOIDmode)
+    return N_("invalid conversion from %<__fpreg%>");
+  if (TYPE_MODE (totype) == RFmode
+      && TYPE_MODE (fromtype) != RFmode)
+    return N_("invalid conversion to %<__fpreg%>");
+  return NULL;
+}
+
+/* Return the diagnostic message string if the unary operation OP is
+   not permitted on TYPE, NULL otherwise.  */
+static const char *
+ia64_invalid_unary_op (int op, tree type)
+{
+  /* Reject operations on __fpreg other than unary + or &.  */
+  if (TYPE_MODE (type) == RFmode
+      && op != CONVERT_EXPR
+      && op != ADDR_EXPR)
+    return N_("invalid operation on %<__fpreg%>");
+  return NULL;
+}
+
+/* Return the diagnostic message string if the binary operation OP is
+   not permitted on TYPE1 and TYPE2, NULL otherwise.  */
+static const char *
+ia64_invalid_binary_op (int op ATTRIBUTE_UNUSED, tree type1, tree type2)
+{
+  /* Reject operations on __fpreg.  */
+  if (TYPE_MODE (type1) == RFmode || TYPE_MODE (type2) == RFmode)
+    return N_("invalid operation on %<__fpreg%>");
   return NULL;
 }
 
index 57cd70b..18e6cb3 100644 (file)
        (match_operand:XF 1 "general_operand" ""))]
   ""
 {
-  rtx op0 = operands[0];
-
-  if (GET_CODE (op0) == SUBREG)
-    op0 = SUBREG_REG (op0);
-
-  /* We must support XFmode loads into general registers for stdarg/vararg,
-     unprototyped calls, and a rare case where a long double is passed as
-     an argument after a float HFA fills the FP registers.  We split them into
-     DImode loads for convenience.  We also need to support XFmode stores
-     for the last case.  This case does not happen for stdarg/vararg routines,
-     because we do a block store to memory of unnamed arguments.  */
-
-  if (GET_CODE (op0) == REG && GR_REGNO_P (REGNO (op0)))
-    {
-      rtx out[2];
-
-      /* We're hoping to transform everything that deals with XFmode
-        quantities and GR registers early in the compiler.  */
-      gcc_assert (!no_new_pseudos);
-
-      /* Struct to register can just use TImode instead.  */
-      if ((GET_CODE (operands[1]) == SUBREG
-          && GET_MODE (SUBREG_REG (operands[1])) == TImode)
-         || (GET_CODE (operands[1]) == REG
-             && GR_REGNO_P (REGNO (operands[1]))))
-       {
-         rtx op1 = operands[1];
-
-         if (GET_CODE (op1) == SUBREG)
-           op1 = SUBREG_REG (op1);
-         else
-           op1 = gen_rtx_REG (TImode, REGNO (op1));
-
-         emit_move_insn (gen_rtx_REG (TImode, REGNO (op0)), op1);
-         DONE;
-       }
-
-      if (GET_CODE (operands[1]) == CONST_DOUBLE)
-       {
-         emit_move_insn (gen_rtx_REG (DImode, REGNO (op0)),
-                         operand_subword (operands[1], 0, 0, XFmode));
-         emit_move_insn (gen_rtx_REG (DImode, REGNO (op0) + 1),
-                         operand_subword (operands[1], 1, 0, XFmode));
-         DONE;
-       }
-
-      /* If the quantity is in a register not known to be GR, spill it.  */
-      if (register_operand (operands[1], XFmode))
-       operands[1] = spill_xfmode_operand (operands[1], 1);
-
-      gcc_assert (GET_CODE (operands[1]) == MEM);
-
-      out[WORDS_BIG_ENDIAN] = gen_rtx_REG (DImode, REGNO (op0));
-      out[!WORDS_BIG_ENDIAN] = gen_rtx_REG (DImode, REGNO (op0) + 1);
-
-      emit_move_insn (out[0], adjust_address (operands[1], DImode, 0));
-      emit_move_insn (out[1], adjust_address (operands[1], DImode, 8));
-      DONE;
-    }
-
-  if (GET_CODE (operands[1]) == REG && GR_REGNO_P (REGNO (operands[1])))
-    {
-      /* We're hoping to transform everything that deals with XFmode
-        quantities and GR registers early in the compiler.  */
-      gcc_assert (!no_new_pseudos);
-
-      /* Op0 can't be a GR_REG here, as that case is handled above.
-        If op0 is a register, then we spill op1, so that we now have a
-        MEM operand.  This requires creating an XFmode subreg of a TImode reg
-        to force the spill.  */
-      if (register_operand (operands[0], XFmode))
-       {
-         rtx op1 = gen_rtx_REG (TImode, REGNO (operands[1]));
-         op1 = gen_rtx_SUBREG (XFmode, op1, 0);
-         operands[1] = spill_xfmode_operand (op1, 0);
-       }
-
-      else
-       {
-         rtx in[2];
-
-          gcc_assert (GET_CODE (operands[0]) == MEM);
-         in[WORDS_BIG_ENDIAN] = gen_rtx_REG (DImode, REGNO (operands[1]));
-         in[!WORDS_BIG_ENDIAN] = gen_rtx_REG (DImode, REGNO (operands[1]) + 1);
-
-         emit_move_insn (adjust_address (operands[0], DImode, 0), in[0]);
-         emit_move_insn (adjust_address (operands[0], DImode, 8), in[1]);
-         DONE;
-       }
-    }
-
-  if (! reload_in_progress && ! reload_completed)
-    {
-      operands[1] = spill_xfmode_operand (operands[1], 0);
-
-      if (GET_MODE (op0) == TImode && GET_CODE (op0) == REG)
-       {
-         rtx memt, memx, in = operands[1];
-         if (CONSTANT_P (in))
-           in = validize_mem (force_const_mem (XFmode, in));
-         if (GET_CODE (in) == MEM)
-           memt = adjust_address (in, TImode, 0);
-         else
-           {
-             memt = assign_stack_temp (TImode, 16, 0);
-             memx = adjust_address (memt, XFmode, 0);
-             emit_move_insn (memx, in);
-           }
-         emit_move_insn (op0, memt);
-         DONE;
-       }
-
-      if (! ia64_move_ok (operands[0], operands[1]))
-       operands[1] = force_reg (XFmode, operands[1]);
-    }
+  if (ia64_expand_movxf_movrf (XFmode, operands))
+    DONE;
 })
 
 ;; ??? There's no easy way to mind volatile acquire/release semantics.
    stfe %0 = %F1%P0"
   [(set_attr "itanium_class" "fmisc,fld,stf")])
 
+;; Same as for movxf, but for RFmode.
+(define_expand "movrf"
+  [(set (match_operand:RF 0 "general_operand" "")
+       (match_operand:RF 1 "general_operand" ""))]
+  ""
+{
+  if (ia64_expand_movxf_movrf (RFmode, operands))
+    DONE;
+})
+
+(define_insn "*movrf_internal"
+  [(set (match_operand:RF 0 "destination_operand" "=f,f, m")
+       (match_operand:RF 1 "general_operand"     "fG,m,fG"))]
+  "ia64_move_ok (operands[0], operands[1])"
+  "@
+   mov %0 = %F1
+   ldf.fill %0 = %1%P1
+   stf.spill %0 = %F1%P0"
+  [(set_attr "itanium_class" "fmisc,fld,stf")])
+
 ;; Better code generation via insns that deal with TFmode register pairs
 ;; directly.  Same concerns apply as for TImode.
 (define_expand "movtf"
index 128ea64..a973622 100644 (file)
@@ -1,3 +1,9 @@
+2005-06-28  Joseph S. Myers  <joseph@codesourcery.com>
+
+       * cvt.c (ocp_convert): Use invalid_conversion hook.
+       * typeck.c (build_binary_op): Use invalid_binary_op hook.
+       (build_unary_op): Use invalid_unary_op hook.
+
 2005-06-28  Paul Brook  <paul@codesourcery.com>
 
        * Make-lang.in (cp/except.o): Depend on $(TARGET_H)
index 5b4cc3b..de0792a 100644 (file)
@@ -36,6 +36,7 @@ Boston, MA 02110-1301, USA.  */
 #include "convert.h"
 #include "toplev.h"
 #include "decl.h"
+#include "target.h"
 
 static tree cp_convert_to_pointer (tree, tree, bool);
 static tree convert_to_pointer_force (tree, tree);
@@ -601,6 +602,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags)
 {
   tree e = expr;
   enum tree_code code = TREE_CODE (type);
+  const char *invalid_conv_diag;
 
   if (error_operand_p (e) || type == error_mark_node)
     return error_mark_node;
@@ -608,6 +610,13 @@ ocp_convert (tree type, tree expr, int convtype, int flags)
   complete_type (type);
   complete_type (TREE_TYPE (expr));
 
+  if ((invalid_conv_diag
+       = targetm.invalid_conversion (TREE_TYPE (expr), type)))
+    {
+      error (invalid_conv_diag);
+      return error_mark_node;
+    }
+
   e = integral_constant_value (e);
 
   if (IS_AGGR_TYPE (type) && (convtype & CONV_FORCE_TEMP)
index edf1519..32d3f04 100644 (file)
@@ -2736,6 +2736,7 @@ build_binary_op (enum tree_code code, tree orig_op0, tree orig_op1,
   tree op0, op1;
   enum tree_code code0, code1;
   tree type0, type1;
+  const char *invalid_op_diag;
 
   /* Expression code to give to the expression when it is built.
      Normally this is CODE, which is what the caller asked for,
@@ -2845,6 +2846,13 @@ build_binary_op (enum tree_code code, tree orig_op0, tree orig_op1,
   if (code0 == ERROR_MARK || code1 == ERROR_MARK)
     return error_mark_node;
 
+  if ((invalid_op_diag
+       = targetm.invalid_binary_op (code, type0, type1)))
+    {
+      error (invalid_op_diag);
+      return error_mark_node;
+    }
+
   switch (code)
     {
     case PLUS_EXPR:
@@ -3725,10 +3733,21 @@ build_unary_op (enum tree_code code, tree xarg, int noconvert)
   tree argtype = 0;
   const char *errstring = NULL;
   tree val;
+  const char *invalid_op_diag;
 
   if (arg == error_mark_node)
     return error_mark_node;
 
+  if ((invalid_op_diag
+       = targetm.invalid_unary_op ((code == UNARY_PLUS_EXPR
+                                   ? CONVERT_EXPR
+                                   : code),
+                                  TREE_TYPE (xarg))))
+    {
+      error (invalid_op_diag);
+      return error_mark_node;
+    }
+
   switch (code)
     {
     case UNARY_PLUS_EXPR:
index bbca6b9..dca9ad6 100644 (file)
@@ -9606,6 +9606,26 @@ illegal to pass argument @var{val} to function @var{funcdecl}
 with prototype @var{typelist}.
 @end deftypefn
 
+@deftypefn {Target Hook} {const char *} TARGET_INVALID_CONVERSION (tree @var{fromtype}, tree @var{totype})
+If defined, this macro returns the diagnostic message when it is
+invalid to convert from @var{fromtype} to @var{totype}, or @code{NULL}
+if validity should be determined by the front end.
+@end deftypefn
+
+@deftypefn {Target Hook} {const char *} TARGET_INVALID_UNARY_OP (int @var{op}, tree @var{type})
+If defined, this macro returns the diagnostic message when it is
+invalid to apply operation @var{op} (where unary plus is denoted by
+@code{CONVERT_EXPR}) to an operand of type @var{type}, or @code{NULL}
+if validity should be determined by the front end.
+@end deftypefn
+
+@deftypefn {Target Hook} {const char *} TARGET_INVALID_BINARY_OP (int @var{op}, tree @var{type1}, tree @var{type2})
+If defined, this macro returns the diagnostic message when it is
+invalid to apply operation @var{op} to operands of types @var{type1}
+and @var{type2}, or @code{NULL} if validity should be determined by
+the front end.
+@end deftypefn
+
 @defmac TARGET_USE_JCR_SECTION
 This macro determines whether to use the JCR section to register Java
 classes. By default, TARGET_USE_JCR_SECTION is defined to 1 if both
index 65b347e..78871a4 100644 (file)
@@ -256,3 +256,25 @@ hook_constcharptr_rtx_null (rtx r ATTRIBUTE_UNUSED)
 {
   return NULL;
 }
+
+const char *
+hook_constcharptr_tree_tree_null (tree t0 ATTRIBUTE_UNUSED,
+                                 tree t1 ATTRIBUTE_UNUSED)
+{
+  return NULL;
+}
+
+const char *
+hook_constcharptr_int_tree_null (int i ATTRIBUTE_UNUSED,
+                                tree t0 ATTRIBUTE_UNUSED)
+{
+  return NULL;
+}
+
+const char *
+hook_constcharptr_int_tree_tree_null (int i ATTRIBUTE_UNUSED,
+                                     tree t0 ATTRIBUTE_UNUSED,
+                                     tree t1 ATTRIBUTE_UNUSED)
+{
+  return NULL;
+}
index 318af08..eebda9a 100644 (file)
@@ -66,4 +66,7 @@ extern tree hook_tree_tree_tree_tree_3rd_identity (tree, tree, tree);
 extern const char *hook_constcharptr_tree_null (tree);
 extern tree hook_tree_tree_tree_bool_null (tree, tree, bool);
 extern const char *hook_constcharptr_rtx_null (rtx);
+extern const char *hook_constcharptr_tree_tree_null (tree, tree);
+extern const char *hook_constcharptr_int_tree_null (int, tree);
+extern const char *hook_constcharptr_int_tree_tree_null (int, tree, tree);
 #endif
index 9998a19..447b5c3 100644 (file)
@@ -379,6 +379,10 @@ Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 #define TARGET_INVALID_ARG_FOR_UNPROTOTYPED_FN hook_invalid_arg_for_unprototyped_fn
 #endif
 
+#define TARGET_INVALID_CONVERSION hook_constcharptr_tree_tree_null
+#define TARGET_INVALID_UNARY_OP hook_constcharptr_int_tree_null
+#define TARGET_INVALID_BINARY_OP hook_constcharptr_int_tree_tree_null
+
 #define TARGET_FIXED_CONDITION_CODE_REGS hook_bool_uintp_uintp_false
 
 #define TARGET_CC_MODES_COMPATIBLE default_cc_modes_compatible
@@ -581,6 +585,9 @@ Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   TARGET_STACK_PROTECT_FAIL,                   \
   TARGET_INVALID_WITHIN_DOLOOP,                        \
   TARGET_CALLS,                                        \
+  TARGET_INVALID_CONVERSION,                   \
+  TARGET_INVALID_UNARY_OP,                     \
+  TARGET_INVALID_BINARY_OP,                    \
   TARGET_CXX,                                  \
   TARGET_UNWIND_TABLES_DEFAULT,                        \
   TARGET_HAVE_NAMED_SECTIONS,                  \
index 364e95a..65e50ca 100644 (file)
@@ -598,6 +598,18 @@ struct gcc_target
                                                    tree funcdecl, tree val);
   } calls;
 
+  /* Return the diagnostic message string if conversion from FROMTYPE
+     to TOTYPE is not allowed, NULL otherwise.  */
+  const char *(*invalid_conversion) (tree fromtype, tree totype);
+
+  /* Return the diagnostic message string if the unary operation OP is
+     not permitted on TYPE, NULL otherwise.  */
+  const char *(*invalid_unary_op) (int op, tree type);
+
+  /* Return the diagnostic message string if the binary operation OP
+     is not permitted on TYPE1 and TYPE2, NULL otherwise.  */
+  const char *(*invalid_binary_op) (int op, tree type1, tree type2);
+
   /* Functions specific to the C++ frontend.  */
   struct cxx {
     /* Return the integer type used for guard variables.  */
index 39a5c27..1794a3b 100644 (file)
@@ -1,3 +1,8 @@
+2005-06-28  Joseph S. Myers  <joseph@codesourcery.com>
+
+       * g++.dg/ext/fpreg1.C, gcc.target/ia64/fpreg-1.c,
+       gcc.target/ia64/fpreg-2.c: New tests.
+
 2005-06-28  Francois-Xavier Coudert  <coudert@clipper.ens.fr>
 
        * gfortran.dg/dev_null.f90: Run test only on linux and solaris.
diff --git a/gcc/testsuite/g++.dg/ext/fpreg1.C b/gcc/testsuite/g++.dg/ext/fpreg1.C
new file mode 100644 (file)
index 0000000..72b8113
--- /dev/null
@@ -0,0 +1,82 @@
+// Test permitted and invalid uses of __fpreg, for C++.
+// Origin: Joseph Myers <joseph@codesourcery.com>
+// { dg-do compile { target ia64-*-* } }
+// { dg-options "" }
+
+__float80 f80;
+double d;
+// Default initialized __fpreg is OK.
+__fpreg fpreg, fpreg2;
+// But explicitly zero-initialized is an invalid conversion.
+__fpreg fi = 0; // { dg-error "error: invalid conversion to '__fpreg'" }
+
+__fpreg f0 (__fpreg);
+int f1 (__float80);
+
+// __fpreg in a structure is OK.
+struct s {
+  __float80 b;
+  __fpreg a;
+} x;
+
+void
+f (void)
+{
+  __fpreg *p;
+  // Valid operations.
+  fpreg = fpreg2;
+  fpreg2 = (__fpreg) fpreg;
+  fpreg = f0 (fpreg2);
+  fpreg = +fpreg2;
+  p = &fpreg;
+  (void) fpreg;
+  fpreg = x.a;
+  fpreg2 = (struct s) { 0 }.a;
+  fpreg = (d ? fpreg : fpreg2);
+  d = sizeof (fpreg);
+  (void)(fpreg, fpreg);
+  // Invalid operations.
+  ++fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  --fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg++; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg--; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg = -fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg = ~fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = !fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = *fpreg; // { dg-error "error: invalid type argument" }
+  if (fpreg) // { dg-error "error: invalid conversion from '__fpreg'" }
+    return;
+  d = fpreg; // { dg-error "error: invalid conversion from '__fpreg'" }
+  d = (double) fpreg; // { dg-error "error: invalid conversion from '__fpreg'" }
+  fpreg = (__fpreg) d; // { dg-error "error: invalid conversion to '__fpreg'" }
+  fpreg = fpreg * fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg = fpreg / fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg = fpreg % fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg = fpreg + fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg = fpreg - fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg = fpreg << fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  fpreg = fpreg >> fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg < fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg > fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg <= fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg >= fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg == fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg != fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg & fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg ^ fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg | fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg && fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = fpreg || fpreg; // { dg-error "error: invalid operation on '__fpreg'" }
+  d = (fpreg ? 1 : 2); // { dg-error "error: invalid conversion from '__fpreg'" }
+  fpreg = (d ? fpreg : d); // { dg-error "error: invalid conversion to '__fpreg'" }
+  fpreg *= fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+  fpreg /= fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+  fpreg %= fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+  fpreg += fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+  fpreg -= fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+  fpreg <<= fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+  fpreg >>= fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+  fpreg &= fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+  fpreg ^= fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+  fpreg |= fpreg; // { dg-error "error: invalid operation on '__fpreg'|in evaluation" }
+}
diff --git a/gcc/testsuite/gcc.target/ia64/fpreg-1.c b/gcc/testsuite/gcc.target/ia64/fpreg-1.c
new file mode 100644 (file)
index 0000000..022fb94
--- /dev/null
@@ -0,0 +1,82 @@
+/* Test permitted and invalid uses of __fpreg.  */
+/* Origin: Joseph Myers <joseph@codesourcery.com> */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+__float80 f80;
+double d;
+/* Default initialized __fpreg is OK.  */
+__fpreg fpreg, fpreg2;
+/* But explicitly zero-initialized is an invalid conversion.  */
+__fpreg fi = 0; /* { dg-error "error: invalid conversion to '__fpreg'" } */
+
+__fpreg f0 (__fpreg);
+int f1 (__float80);
+
+/* __fpreg in a structure is OK.  */
+struct s {
+  __float80 b;
+  __fpreg a;
+} x;
+
+void
+f (void)
+{
+  __fpreg *p;
+  /* Valid operations.  */
+  fpreg = fpreg2;
+  fpreg2 = (__fpreg) fpreg;
+  fpreg = f0 (fpreg2);
+  fpreg = +fpreg2;
+  p = &fpreg;
+  (void) fpreg;
+  fpreg = x.a;
+  fpreg2 = (struct s) { 0 }.a;
+  fpreg = (d ? fpreg : fpreg2);
+  d = sizeof (fpreg);
+  (void)(fpreg, fpreg);
+  /* Invalid operations.  */
+  ++fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  --fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg++; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg--; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg = -fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg = ~fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = !fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = *fpreg; /* { dg-error "error: invalid type argument" } */
+  if (fpreg) /* { dg-error "error: invalid operation on '__fpreg'" } */
+    return;
+  d = fpreg; /* { dg-error "error: invalid conversion from '__fpreg'" } */
+  d = (double) fpreg; /* { dg-error "error: invalid conversion from '__fpreg'" } */
+  fpreg = (__fpreg) d; /* { dg-error "error: invalid conversion to '__fpreg'" } */
+  fpreg = fpreg * fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg = fpreg / fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg = fpreg % fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg = fpreg + fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg = fpreg - fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg = fpreg << fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg = fpreg >> fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg < fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg > fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg <= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg >= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg == fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg != fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg & fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg ^ fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg | fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg && fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = fpreg || fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  d = (fpreg ? 1 : 2); /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg = (d ? fpreg : d); /* { dg-error "error: invalid conversion to '__fpreg'" } */
+  fpreg *= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg /= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg %= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg += fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg -= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg <<= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg >>= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg &= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg ^= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+  fpreg |= fpreg; /* { dg-error "error: invalid operation on '__fpreg'" } */
+}
diff --git a/gcc/testsuite/gcc.target/ia64/fpreg-2.c b/gcc/testsuite/gcc.target/ia64/fpreg-2.c
new file mode 100644 (file)
index 0000000..a21bd0f
--- /dev/null
@@ -0,0 +1,21 @@
+/* Test __fpreg ABI.  */
+/* Origin: Joseph Myers <joseph@codesourcery.com> */
+/* { dg-do compile } */
+/* { dg-options "" } */
+/* { dg-final { scan-assembler "ldf.fill" } } */
+/* { dg-final { scan-assembler "stf.spill" } } */
+
+__fpreg x;
+
+void f (void);
+
+void
+g (void)
+{
+  __fpreg b = x;
+  f ();
+  x = b;
+}
+
+char t1[(sizeof (__fpreg) == sizeof (__float80) ? 1 : -1)];
+char t2[(__alignof (__fpreg) == __alignof (__float80) ? 1 : -1)];