OSDN Git Service

(emit_unop_insn): Don't do -fforce-mem for SIGN_EXTEND.
[pf3gnuchains/gcc-fork.git] / gcc / optabs.c
index db68da8..cae4c4e 100644 (file)
@@ -1,5 +1,5 @@
 /* Expand the basic unary and binary arithmetic operations, for GNU compiler.
-   Copyright (C) 1987, 1988, 1992, 1993, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 92, 93, 94, 1995 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,7 +15,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include "config.h"
@@ -211,6 +212,15 @@ rtxfun bcc_gen_fctn[NUM_RTX_CODE];
 
 enum insn_code setcc_gen_code[NUM_RTX_CODE];
 
+#ifdef HAVE_conditional_move
+/* Indexed by the machine mode, gives the insn code to make a conditional
+   move insn.  This is not indexed by the rtx-code like bcc_gen_fctn and
+   setcc_gen_code to cut down on the number of named patterns.  Consider a day
+   when a lot more rtx codes are conditional (eg: for the ARM).  */
+
+enum insn_code movcc_gen_code[NUM_MACHINE_MODES];
+#endif
+
 static int add_equal_note      PROTO((rtx, rtx, enum rtx_code, rtx, rtx));
 static rtx widen_operand       PROTO((rtx, enum machine_mode,
                                       enum machine_mode, int, int));
@@ -380,8 +390,8 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
     op0 = force_reg (mode, op0);
 
   if (CONSTANT_P (op1) && preserve_subexpressions_p ()
-      && rtx_cost (op1, binoptab->code) > 2)
-    op1 = force_reg (shift_op ? word_mode : mode, op1);
+      && ! shift_op && rtx_cost (op1, binoptab->code) > 2)
+    op1 = force_reg (mode, op1);
 
   /* Record where to delete back to if we backtrack.  */
   last = get_last_insn ();
@@ -445,20 +455,24 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
         the result, convert the operands.  */
 
       if (GET_MODE (op0) != VOIDmode
-         && GET_MODE (op0) != mode0)
+         && GET_MODE (op0) != mode0
+         && mode0 != VOIDmode)
        xop0 = convert_to_mode (mode0, xop0, unsignedp);
 
       if (GET_MODE (xop1) != VOIDmode
-         && GET_MODE (xop1) != mode1)
+         && GET_MODE (xop1) != mode1
+         && mode1 != VOIDmode)
        xop1 = convert_to_mode (mode1, xop1, unsignedp);
 
       /* Now, if insn's predicates don't allow our operands, put them into
         pseudo regs.  */
 
-      if (! (*insn_operand_predicate[icode][1]) (xop0, mode0))
+      if (! (*insn_operand_predicate[icode][1]) (xop0, mode0)
+         && mode0 != VOIDmode)
        xop0 = copy_to_mode_reg (mode0, xop0);
 
-      if (! (*insn_operand_predicate[icode][2]) (xop1, mode1))
+      if (! (*insn_operand_predicate[icode][2]) (xop1, mode1)
+         && mode1 != VOIDmode)
        xop1 = copy_to_mode_reg (mode1, xop1);
 
       if (! (*insn_operand_predicate[icode][0]) (temp, mode))
@@ -1034,7 +1048,8 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
 
       /* If the target is the same as one of the inputs, don't use it.  This
         prevents problems with the REG_EQUAL note.  */
-      if (target == op0 || target == op1)
+      if (target == op0 || target == op1
+         || (target != 0 && GET_CODE (target) != REG))
        target = 0;
 
       /* Multiply the two lower words to get a double-word product.
@@ -1499,7 +1514,8 @@ expand_binop (mode, binoptab, op0, op1, target, unsignedp, methods)
          op1x = convert_to_mode (word_mode, op1, 1);
        }
 
-      if (GET_MODE (op0) != mode)
+      if (GET_MODE (op0) != VOIDmode
+         && GET_MODE (op0) != mode)
        op0 = convert_to_mode (mode, op0, unsignedp);
 
       /* Pass 1 for NO_QUEUE so we don't lose any increments
@@ -2041,6 +2057,17 @@ expand_unop (mode, unoptab, op0, target, unsignedp)
        }
     }
 
+  /* If there is no negate operation, try doing a subtract from zero.
+     The US Software GOFAST library needs this.  */
+  if (unoptab == neg_optab)
+    {    
+      rtx temp;
+      temp = expand_binop (mode, sub_optab, CONST0_RTX (mode), op0,
+                          target, unsignedp, OPTAB_LIB_WIDEN);
+      if (temp)
+       return temp;
+    }
+      
   return 0;
 }
 \f
@@ -2347,7 +2374,9 @@ emit_unop_insn (icode, target, op0, code)
 
   op0 = protect_from_queue (op0, 0);
 
-  if (flag_force_mem)
+  /* Sign extension from memory is often done specially on RISC
+     machines, so forcing into a register here can pessimize code.  */
+  if (flag_force_mem && code != SIGN_EXTEND)
     op0 = force_not_mem (op0);
 
   /* Now, if insn does not accept our operands, put them into pseudos.  */
@@ -2384,9 +2413,7 @@ emit_unop_insn (icode, target, op0, code)
 
    INSNS is a block of code generated to perform the operation, not including
    the CLOBBER and final copy.  All insns that compute intermediate values
-   are first emitted, followed by the block as described above.  Only
-   INSNs are allowed in the block; no library calls or jumps may be
-   present.
+   are first emitted, followed by the block as described above.  
 
    TARGET, OP0, and OP1 are the output and inputs of the operations,
    respectively.  OP1 may be zero for a unary operation.
@@ -2395,7 +2422,8 @@ emit_unop_insn (icode, target, op0, code)
    on the last insn.
 
    If TARGET is not a register, INSNS is simply emitted with no special
-   processing.
+   processing.  Likewise if anything in INSNS is not an INSN or if
+   there is a libcall block inside INSNS.
 
    The final insn emitted is returned.  */
 
@@ -2410,6 +2438,11 @@ emit_no_conflict_block (insns, target, op0, op1, equiv)
 
   if (GET_CODE (target) != REG || reload_in_progress)
     return emit_insns (insns);
+  else
+    for (insn = insns; insn; insn = NEXT_INSN (insn))
+      if (GET_CODE (insn) != INSN
+         || find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+       return emit_insns (insns);
 
   /* First emit all insns that do not store into words of the output and remove
      these from the list.  */
@@ -2420,9 +2453,6 @@ emit_no_conflict_block (insns, target, op0, op1, equiv)
 
       next = NEXT_INSN (insn);
 
-      if (GET_CODE (insn) != INSN)
-       abort ();
-
       if (GET_CODE (PATTERN (insn)) == SET)
        set = PATTERN (insn);
       else if (GET_CODE (PATTERN (insn)) == PARALLEL)
@@ -2717,12 +2747,17 @@ emit_cmp_insn (x, y, comparison, size, mode, unsignedp, align)
          emit_library_call (memcmp_libfunc, 0,
                             TYPE_MODE (integer_type_node), 3,
                             XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
-                            size, Pmode);
+                            convert_to_mode (TYPE_MODE (sizetype), size,
+                                             TREE_UNSIGNED (sizetype)),
+                            TYPE_MODE (sizetype));
 #else
          emit_library_call (bcmp_libfunc, 0,
                             TYPE_MODE (integer_type_node), 3,
                             XEXP (x, 0), Pmode, XEXP (y, 0), Pmode,
-                            size, Pmode);
+                            convert_to_mode (TYPE_MODE (integer_type_node),
+                                             size,
+                                             TREE_UNSIGNED (integer_type_node)),
+                            TYPE_MODE (integer_type_node));
 #endif
          emit_cmp_insn (hard_libcall_value (TYPE_MODE (integer_type_node)),
                         const0_rtx, comparison, NULL_RTX,
@@ -3034,6 +3069,148 @@ emit_indirect_jump (loc)
   emit_barrier ();
 }
 \f
+#ifdef HAVE_conditional_move
+
+/* Emit a conditional move instruction if the machine supports one for that
+   condition and machine mode.
+
+   OP0 and OP1 are the operands that should be compared using CODE.  CMODE is
+   the mode to use should they be constants.  If it is VOIDmode, they cannot
+   both be constants.
+
+   OP2 should be stored in TARGET if the comparison is true, otherwise OP3
+   should be stored there.  MODE is the mode to use should they be constants.
+   If it is VOIDmode, they cannot both be constants.
+
+   The result is either TARGET (perhaps modified) or NULL_RTX if the operation
+   is not supported.  */
+
+rtx
+emit_conditional_move (target, code, op0, op1, cmode, op2, op3, mode,
+                      unsignedp)
+     rtx target;
+     enum rtx_code code;
+     rtx op0, op1;
+     enum machine_mode cmode;
+     rtx op2, op3;
+     enum machine_mode mode;
+     int unsignedp;
+{
+  rtx tem, subtarget, comparison, insn;
+  enum insn_code icode;
+
+  /* If one operand is constant, make it the second one.  Only do this
+     if the other operand is not constant as well.  */
+
+  if ((CONSTANT_P (op0) && ! CONSTANT_P (op1))
+      || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT))
+    {
+      tem = op0;
+      op0 = op1;
+      op1 = tem;
+      code = swap_condition (code);
+    }
+
+  if (cmode == VOIDmode)
+    cmode = GET_MODE (op0);
+
+  if ((CONSTANT_P (op2) && ! CONSTANT_P (op3))
+      || (GET_CODE (op2) == CONST_INT && GET_CODE (op3) != CONST_INT))
+    {
+      tem = op2;
+      op2 = op3;
+      op3 = tem;
+      /* ??? This may not be appropriate (consider IEEE).  Perhaps we should
+        call can_reverse_comparison_p here and bail out if necessary.
+        It's not clear whether we need to do this canonicalization though.  */
+      code = reverse_condition (code);
+    }
+
+  if (mode == VOIDmode)
+    mode = GET_MODE (op2);
+
+  icode = movcc_gen_code[mode];
+
+  if (icode == CODE_FOR_nothing)
+    return 0;
+
+  if (flag_force_mem)
+    {
+      op2 = force_not_mem (op2);
+      op3 = force_not_mem (op3);
+    }
+
+  if (target)
+    target = protect_from_queue (target, 1);
+  else
+    target = gen_reg_rtx (mode);
+
+  subtarget = target;
+
+  emit_queue ();
+
+  op2 = protect_from_queue (op2, 0);
+  op3 = protect_from_queue (op3, 0);
+
+  /* If the insn doesn't accept these operands, put them in pseudos.  */
+
+  if (! (*insn_operand_predicate[icode][0])
+      (subtarget, insn_operand_mode[icode][0]))
+    subtarget = gen_reg_rtx (insn_operand_mode[icode][0]);
+
+  if (! (*insn_operand_predicate[icode][2])
+      (op2, insn_operand_mode[icode][2]))
+    op2 = copy_to_mode_reg (insn_operand_mode[icode][2], op2);
+
+  if (! (*insn_operand_predicate[icode][3])
+      (op3, insn_operand_mode[icode][3]))
+    op3 = copy_to_mode_reg (insn_operand_mode[icode][3], op3);
+
+  /* Everything should now be in the suitable form, so emit the compare insn
+     and then the conditional move.  */
+
+  comparison 
+    = compare_from_rtx (op0, op1, code, unsignedp, cmode, NULL_RTX, 0);
+
+  /* ??? Watch for const0_rtx (nop) and const_true_rtx (unconditional)?  */
+  if (GET_CODE (comparison) != code)
+    /* This shouldn't happen.  */
+    abort ();
+  
+  insn = GEN_FCN (icode) (subtarget, comparison, op2, op3);
+
+  /* If that failed, then give up.  */
+  if (insn == 0)
+    return 0;
+
+  emit_insn (insn);
+
+  if (subtarget != target)
+    convert_move (target, subtarget, 0);
+
+  return target;
+}
+
+/* Return non-zero if a conditional move of mode MODE is supported.
+
+   This function is for combine so it can tell whether an insn that looks
+   like a conditional move is actually supported by the hardware.  If we
+   guess wrong we lose a bit on optimization, but that's it.  */
+/* ??? sparc64 supports conditionally moving integers values based on fp
+   comparisons, and vice versa.  How do we handle them?  */
+
+int
+can_conditionally_move_p (mode)
+     enum machine_mode mode;
+{
+  if (movcc_gen_code[mode] != CODE_FOR_nothing)
+    return 1;
+
+  return 0;
+}
+
+#endif /* HAVE_conditional_move */
+\f
 /* These three functions generate an insn body and return it
    rather than emitting the insn.
 
@@ -3341,6 +3518,7 @@ expand_float (to, from, unsignedp)
              /* The sign bit is not set.  Convert as signed.  */
              expand_float (target, from, 0);
              emit_jump_insn (gen_jump (label));
+             emit_barrier ();
 
              /* The sign bit is set.
                 Convert to a usable (positive signed) value by shifting right
@@ -3754,7 +3932,7 @@ init_libfuncs (optable, first_mode, last_mode, opname, suffix)
     register int first_mode;
     register int last_mode;
     register char *opname;
-    register char suffix;
+    register int suffix;
 {
   register int mode;
   register unsigned opname_len = strlen (opname);
@@ -3792,7 +3970,7 @@ static void
 init_integral_libfuncs (optable, opname, suffix)
     register optab optable;
     register char *opname;
-    register char suffix;
+    register int suffix;
 {
   init_libfuncs (optable, SImode, TImode, opname, suffix);
 }
@@ -3806,7 +3984,7 @@ static void
 init_floating_libfuncs (optable, opname, suffix)
     register optab optable;
     register char *opname;
-    register char suffix;
+    register int suffix;
 {
   init_libfuncs (optable, SFmode, TFmode, opname, suffix);
 }
@@ -3820,7 +3998,7 @@ static void
 init_complex_libfuncs (optable, opname, suffix)
     register optab optable;
     register char *opname;
-    register char suffix;
+    register int suffix;
 {
   init_libfuncs (optable, SCmode, TCmode, opname, suffix);
 }
@@ -3859,6 +4037,11 @@ init_optabs ()
   for (i = 0; i < NUM_RTX_CODE; i++)
     setcc_gen_code[i] = CODE_FOR_nothing;
 
+#ifdef HAVE_conditional_move
+  for (i = 0; i < NUM_MACHINE_MODES; i++)
+    movcc_gen_code[i] = CODE_FOR_nothing;
+#endif
+
   add_optab = init_optab (PLUS);
   sub_optab = init_optab (MINUS);
   smul_optab = init_optab (MULT);
@@ -3903,6 +4086,7 @@ init_optabs ()
   for (i = 0; i < NUM_MACHINE_MODES; i++)
     {
       movstr_optab[i] = CODE_FOR_nothing;
+      clrstr_optab[i] = CODE_FOR_nothing;
 
 #ifdef HAVE_SECONDARY_RELOADS
       reload_in_optab[i] = reload_out_optab[i] = CODE_FOR_nothing;
@@ -3945,8 +4129,6 @@ init_optabs ()
   init_integral_libfuncs (ashl_optab, "ashl", '3');
   init_integral_libfuncs (ashr_optab, "ashr", '3');
   init_integral_libfuncs (lshr_optab, "lshr", '3');
-  init_integral_libfuncs (rotl_optab, "rotl", '3');
-  init_integral_libfuncs (rotr_optab, "rotr", '3');
   init_integral_libfuncs (smin_optab, "min", '3');
   init_floating_libfuncs (smin_optab, "min", '3');
   init_integral_libfuncs (smax_optab, "max", '3');
@@ -3971,10 +4153,6 @@ init_optabs ()
   smul_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, MULDI3_LIBCALL);
 #endif
-#ifdef MULTI3_LIBCALL
-  smul_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, MULTI3_LIBCALL);
-#endif
 
 #ifdef DIVSI3_LIBCALL
   sdiv_optab->handlers[(int) SImode].libfunc
@@ -3984,10 +4162,6 @@ init_optabs ()
   sdiv_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, DIVDI3_LIBCALL);
 #endif
-#ifdef DIVTI3_LIBCALL
-  sdiv_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, DIVTI3_LIBCALL);
-#endif
 
 #ifdef UDIVSI3_LIBCALL
   udiv_optab->handlers[(int) SImode].libfunc
@@ -3997,11 +4171,6 @@ init_optabs ()
   udiv_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, UDIVDI3_LIBCALL);
 #endif
-#ifdef UDIVTI3_LIBCALL
-  udiv_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, UDIVTI3_LIBCALL);
-#endif
-
 
 #ifdef MODSI3_LIBCALL
   smod_optab->handlers[(int) SImode].libfunc
@@ -4011,11 +4180,6 @@ init_optabs ()
   smod_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, MODDI3_LIBCALL);
 #endif
-#ifdef MODTI3_LIBCALL
-  smod_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, MODTI3_LIBCALL);
-#endif
-
 
 #ifdef UMODSI3_LIBCALL
   umod_optab->handlers[(int) SImode].libfunc
@@ -4025,32 +4189,6 @@ init_optabs ()
   umod_optab->handlers[(int) DImode].libfunc
     = gen_rtx (SYMBOL_REF, Pmode, UMODDI3_LIBCALL);
 #endif
-#ifdef UMODTI3_LIBCALL
-  umod_optab->handlers[(int) TImode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, UMODTI3_LIBCALL);
-#endif
-
-/* Define library calls for quad FP instructions */
-#ifdef ADDTF3_LIBCALL
-  add_optab->handlers[(int) TFmode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, ADDTF3_LIBCALL);
-#endif
-#ifdef SUBTF3_LIBCALL
-  sub_optab->handlers[(int) TFmode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, SUBTF3_LIBCALL);
-#endif
-#ifdef MULTF3_LIBCALL
-  smul_optab->handlers[(int) TFmode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, MULTF3_LIBCALL);
-#endif
-#ifdef DIVTF3_LIBCALL
-  flodiv_optab->handlers[(int) TFmode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, DIVTF3_LIBCALL);
-#endif
-#ifdef SQRTTF2_LIBCALL
-  sqrt_optab->handlers[(int) TFmode].libfunc
-    = gen_rtx (SYMBOL_REF, Pmode, SQRTTF2_LIBCALL);
-#endif
 
   /* Use cabs for DC complex abs, since systems generally have cabs.
      Don't define any libcall for SCmode, so that cabs will be used.  */
@@ -4118,26 +4256,6 @@ init_optabs ()
   lttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__lttf2");
   letf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__letf2");
 
-/* Define library calls for quad FP instructions */
-#ifdef EQTF2_LIBCALL
-  eqtf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, EQTF2_LIBCALL);
-#endif
-#ifdef NETF2_LIBCALL
-  netf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, NETF2_LIBCALL);
-#endif
-#ifdef GTTF2_LIBCALL
-  gttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, GTTF2_LIBCALL);
-#endif
-#ifdef GETF2_LIBCALL
-  getf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, GETF2_LIBCALL);
-#endif
-#ifdef LTTF2_LIBCALL
-  lttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, LTTF2_LIBCALL);
-#endif
-#ifdef LETF2_LIBCALL
-  letf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, LETF2_LIBCALL);
-#endif
-
   floatsisf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatsisf");
   floatdisf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floatdisf");
   floattisf_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__floattisf");
@@ -4186,29 +4304,6 @@ init_optabs ()
   fixunstfdi_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfdi");
   fixunstfti_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__fixunstfti");
 
-/* Define library calls for quad FP instructions */
-#ifdef TRUNCTFSF2_LIBCALL
-  trunctfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, TRUNCTFSF2_LIBCALL);
-#endif
-#ifdef TRUNCTFDF2_LIBCALL
-  trunctfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, TRUNCTFDF2_LIBCALL);
-#endif
-#ifdef EXTENDSFTF2_LIBCALL
-  extendsftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, EXTENDSFTF2_LIBCALL);
-#endif
-#ifdef EXTENDDFTF2_LIBCALL
-  extenddftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, EXTENDDFTF2_LIBCALL);
-#endif
-#ifdef FLOATSITF2_LIBCALL
-  floatsitf_libfunc = gen_rtx (SYMBOL_REF, Pmode, FLOATSITF2_LIBCALL);
-#endif
-#ifdef FIX_TRUNCTFSI2_LIBCALL
-  fixtfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, FIX_TRUNCTFSI2_LIBCALL);
-#endif
-#ifdef FIXUNS_TRUNCTFSI2_LIBCALL
-  fixunstfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, FIXUNS_TRUNCTFSI2_LIBCALL);
-#endif
-
 #ifdef INIT_TARGET_OPTABS
   /* Allow the target to add more libcalls or rename some, etc.  */
   INIT_TARGET_OPTABS;