]> gcc.gnu.org Git - gcc.git/blobdiff - gcc/config/rs6000/rs6000.c
(ASM_OUTPUT_SECTION_NAME): New define.
[gcc.git] / gcc / config / rs6000 / rs6000.c
index 0c0eae6c16fd3029dce1c6fc16c9962091772ee7..401faa62b3cecc6caa79e29ecace6c261f47b4b9 100644 (file)
@@ -533,8 +533,6 @@ easy_fp_constant (op, mode)
      register rtx op;
      register enum machine_mode mode;
 {
-  rtx low, high;
-
   if (GET_CODE (op) != CONST_DOUBLE
       || GET_MODE (op) != mode
       || GET_MODE_CLASS (mode) != MODE_FLOAT)
@@ -544,14 +542,27 @@ easy_fp_constant (op, mode)
   if (TARGET_SOFT_FLOAT)
     return 1;
 
-  high = operand_subword (op, 0, 0, mode);
-  low = operand_subword (op, 1, 0, mode);
+  if (mode == DFmode)
+    {
+      long k[2];
+      REAL_VALUE_TYPE rv;
 
-  if (high == 0 || ! input_operand (high, word_mode))
-    return 0;
+      REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
+      REAL_VALUE_TO_TARGET_DOUBLE (rv, k);
 
-  return (mode == SFmode
-         || (low != 0 && input_operand (low, word_mode)));
+      return (((unsigned) (k[0] + 0x8000) < 0x10000 || (k[0] & 0xffff) == 0)
+             && ((unsigned) (k[1] + 0x8000) < 0x10000 || (k[1] & 0xffff) == 0));
+    }
+  else
+    {
+      long l;
+      REAL_VALUE_TYPE rv;
+
+      REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
+      REAL_VALUE_TO_TARGET_SINGLE (rv, l);
+
+      return ((unsigned) (l + 0x8000) < 0x10000 || (l & 0xffff) == 0);
+    }
 }
 
 /* Return 1 if the operand is in volatile memory.  Note that during the
@@ -843,9 +854,7 @@ input_operand (op, mode)
       && small_data_operand (op, Pmode))
     return 1;
 
-  /* Otherwise, we will be doing this SET with an add, so anything valid
-     for an add will be valid.  */
-  return add_operand (op, mode);
+  return 0;
 }
 
 /* Return 1 for an operand in small memory on V.4/eabi */
@@ -930,6 +939,10 @@ init_cumulative_args (cum, fntype, libname, incoming)
       && lookup_attribute ("dllimport", TYPE_ATTRIBUTES (fntype)))
     cum->call_cookie = CALL_NT_DLLIMPORT;
 
+  /* Also check for longcall's */
+  else if (fntype && lookup_attribute ("longcall", TYPE_ATTRIBUTES (fntype)))
+    cum->call_cookie = CALL_LONG;
+
   if (TARGET_DEBUG_ARG)
     {
       fprintf (stderr, "\ninit_cumulative_args:");
@@ -943,9 +956,12 @@ init_cumulative_args (cum, fntype, libname, incoming)
       if (abi == ABI_V4 && incoming)
        fprintf (stderr, " varargs = %d, ", cum->varargs_offset);
 
-      if (cum->call_cookie == CALL_NT_DLLIMPORT)
+      if (cum->call_cookie & CALL_NT_DLLIMPORT)
        fprintf (stderr, " dllimport,");
 
+      if (cum->call_cookie & CALL_LONG)
+       fprintf (stderr, " longcall,");
+
       fprintf (stderr, " proto = %d, nargs = %d\n",
               cum->prototype, cum->nargs_prototype);
     }
@@ -1080,12 +1096,10 @@ function_arg (cum, mode, type, named)
          && cum->nargs_prototype < 0
          && type && (cum->prototype || TARGET_NO_PROTOTYPE))
        {
-         if (cum->call_cookie != CALL_NORMAL)
-           abort ();
-
-         return GEN_INT ((cum->fregno == FP_ARG_MIN_REG)
-                         ? CALL_V4_SET_FP_ARGS
-                         : CALL_V4_CLEAR_FP_ARGS);
+         return GEN_INT (cum->call_cookie
+                         | ((cum->fregno == FP_ARG_MIN_REG)
+                            ? CALL_V4_SET_FP_ARGS
+                            : CALL_V4_CLEAR_FP_ARGS));
        }
 
       return GEN_INT (cum->call_cookie);
@@ -2045,6 +2059,15 @@ print_operand (file, x, code)
        print_operand (file, x, 0);
       return;
 
+    case 'H':
+      /* If constant, output low-order six bits.  Otherwise,
+        write normally. */
+      if (INT_P (x))
+       fprintf (file, "%d", INT_LOWPART (x) & 63);
+      else
+       print_operand (file, x, 0);
+      return;
+
     case 'I':
       /* Print `i' if this is a constant, else nothing.  */
       if (INT_P (x))
@@ -3603,34 +3626,66 @@ output_toc (file, x, labelno)
   /* Handle FP constants specially.  Note that if we have a minimal
      TOC, things we put here aren't actually in the TOC, so we can allow
      FP constants.  */
-  if (GET_CODE (x) == CONST_DOUBLE
-      && GET_MODE (x) == DFmode
+  if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode
       && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC))
     {
-      REAL_VALUE_TYPE r;
-      long l[2];
+      REAL_VALUE_TYPE rv;
+      long k[2];
 
-      REAL_VALUE_FROM_CONST_DOUBLE (r, x);
-      REAL_VALUE_TO_TARGET_DOUBLE (r, l);
+      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+      REAL_VALUE_TO_TARGET_DOUBLE (rv, k);
       if (TARGET_MINIMAL_TOC)
-       fprintf (file, "\t.long %ld\n\t.long %ld\n", l[0], l[1]);
+       fprintf (file, "\t.long %ld\n\t.long %ld\n", k[0], k[1]);
       else
        fprintf (file, "\t.tc FD_%lx_%lx[TC],%ld,%ld\n",
-                l[0], l[1], l[0], l[1]);
+                k[0], k[1], k[0], k[1]);
       return;
     }
   else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode
           && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC))
     {
-      rtx val = operand_subword (x, 0, 0, SFmode);
+      REAL_VALUE_TYPE rv;
+      long l;
 
-      if (val == 0 || GET_CODE (val) != CONST_INT)
-       abort ();
+      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+      REAL_VALUE_TO_TARGET_SINGLE (rv, l);
 
       if (TARGET_MINIMAL_TOC)
-       fprintf (file, "\t.long %d\n", INTVAL (val));
+       fprintf (file, "\t.long %d\n", l);
       else
-       fprintf (file, "\t.tc FS_%x[TC],%d\n", INTVAL (val), INTVAL (val));
+       fprintf (file, "\t.tc FS_%x[TC],%d\n", l, l);
+      return;
+    }
+  else if (GET_MODE (x) == DImode
+          && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
+          && ! (TARGET_NO_FP_IN_TOC && ! TARGET_MINIMAL_TOC))
+    {
+      HOST_WIDE_INT low;
+      HOST_WIDE_INT high;
+
+      if (GET_CODE (x) == CONST_DOUBLE)
+       {
+         low = CONST_DOUBLE_LOW (x);
+         high = CONST_DOUBLE_HIGH (x);
+       }
+      else
+#if HOST_BITS_PER_WIDE_INT == 32
+       {
+         low = INTVAL (x);
+         high = (low < 0) ? ~0 : 0;
+       }
+#else
+       {
+          low = INTVAL (x) & 0xffffffff;
+          high = (HOST_WIDE_INT) INTVAL (x) >> 32;
+       }
+#endif
+
+      if (TARGET_MINIMAL_TOC)
+       fprintf (file, "\t.long %ld\n\t.long %ld\n", high, low);
+      else
+       fprintf (file, "\t.tc ID_%lx_%lx[TC],%ld,%ld\n",
+                high, low, high, low);
       return;
     }
 
@@ -3985,6 +4040,7 @@ rs6000_trampoline_template (file)
 {
   char *sc = reg_names[STATIC_CHAIN_REGNUM];
   char *r0 = reg_names[0];
+  char *r2 = reg_names[2];
 
   switch (DEFAULT_ABI)
     {
@@ -4037,23 +4093,26 @@ rs6000_trampoline_template (file)
 
   /* NT function pointers point to a two word area (real address, TOC)
      which unfortunately does not include a static chain field.  So we
-     need to have a 2 word area followed by the code to load up the
-     static chain.  */
+     use the function field to point to ..LTRAMP1 and the toc field
+     to point to the whole table.  */
     case ABI_NT:
-      if (STATIC_CHAIN_REGNUM == 0 || !TARGET_NEW_MNEMONICS || TARGET_64BIT)
+      if (STATIC_CHAIN_REGNUM == 0
+         || STATIC_CHAIN_REGNUM == 2
+         || TARGET_64BIT
+         || !TARGET_NEW_MNEMONICS)
        abort ();
 
-      fprintf (file, "\t.ualong 0,0\n");               /* offset  0 */
-      fprintf (file, "\tmflr %s\n", r0);               /* offset  8 */
-      fprintf (file, "\tbl .LTRAMP1\n");               /* offset 12 */
-      fprintf (file, "\t.ualong 0,0\n");               /* offset 16 */
-      fprintf (file, ".LTRAMP1:\n");
-      fprintf (file, "\tmflr %s\n", sc);               /* offset 28 */
-      fprintf (file, "\tmtlr %s\n", r0);               /* offset 32 */
-      fprintf (file, "\tlwz %s,0(%s)\n", r0, sc);      /* offset 36 */
-      fprintf (file, "\tlwz %s,4(%s)\n", sc, sc);      /* offset 40 */
-      fprintf (file, "\tmtctr %s\n", r0);              /* offset 44 */
-      fprintf (file, "\tbctr\n");                      /* offset 48 */
+      fprintf (file, "\t.ualong 0\n");                 /* offset  0 */
+      fprintf (file, "\t.ualong 0\n");                 /* offset  4 */
+      fprintf (file, "\t.ualong 0\n");                 /* offset  8 */
+      fprintf (file, "\t.ualong 0\n");                 /* offset 12 */
+      fprintf (file, "\t.ualong 0\n");                 /* offset 16 */
+      fprintf (file, "..LTRAMP1..0:\n");               /* offset 20 */
+      fprintf (file, "\tlwz %s,8(%s)\n", r0, r2);      /* offset 24 */
+      fprintf (file, "\tlwz %s,12(%s)\n", sc, r2);     /* offset 28 */
+      fprintf (file, "\tmtctr %s\n", r0);              /* offset 32 */
+      fprintf (file, "\tlwz %s,16(%s)\n", r2, r2);     /* offset 36 */
+      fprintf (file, "\tbctr\n");                      /* offset 40 */
       break;
     }
 
@@ -4082,7 +4141,7 @@ rs6000_trampoline_size ()
       break;
 
     case ABI_NT:
-      ret = 52;
+      ret = 20;
       break;
     }
 
@@ -4099,64 +4158,64 @@ rs6000_initialize_trampoline (addr, fnaddr, cxt)
      rtx fnaddr;
      rtx cxt;
 {
-  rtx reg, reg2, reg3;
   enum machine_mode pmode = Pmode;
+  int regsize = (TARGET_32BIT) ? 4 : 8;
+  rtx ctx_reg = force_reg (pmode, cxt);
 
   switch (DEFAULT_ABI)
     {
     default:
       abort ();
 
+/* Macros to shorten the code expansions below.  */
+#define MEM_DEREF(addr) gen_rtx (MEM, pmode, memory_address (pmode, addr))
+#define MEM_PLUS(addr,offset) gen_rtx (MEM, pmode, memory_address (pmode, plus_constant (addr, offset)))
+
     /* Under AIX, just build the 3 word function descriptor */
     case ABI_AIX:
-      emit_move_insn (gen_rtx (MEM, pmode,
-                              memory_address (pmode, (addr))),
-                     gen_rtx (MEM, pmode,
-                              memory_address (pmode, (fnaddr))));
-      emit_move_insn (gen_rtx (MEM, pmode,
-                              memory_address (pmode,
-                                              plus_constant ((addr), 4))),
-                     gen_rtx (MEM, pmode,
-                              memory_address (pmode,
-                                              plus_constant ((fnaddr), 4))));
-      emit_move_insn (gen_rtx (MEM, pmode,
-                              memory_address (pmode,
-                                              plus_constant ((addr), 8))),
-                     force_reg (pmode, (cxt)));
+      {
+       rtx fn_reg = gen_reg_rtx (pmode);
+       rtx toc_reg = gen_reg_rtx (pmode);
+       emit_move_insn (fn_reg, MEM_DEREF (fnaddr));
+       emit_move_insn (toc_reg, MEM_PLUS (fnaddr, 4));
+       emit_move_insn (MEM_DEREF (addr), fn_reg);
+       emit_move_insn (MEM_PLUS (addr, regsize), toc_reg);
+       emit_move_insn (MEM_PLUS (addr, 2*regsize), ctx_reg);
+      }
       break;
 
     /* Under V.4/eabi, update the two words after the bl to have the real
        function address and the static chain.  */
     case ABI_V4:
     case ABI_AIX_NODESC:
-      reg = gen_reg_rtx (pmode);
-
-      emit_move_insn (reg, fnaddr);
-      emit_move_insn (gen_rtx (MEM, pmode, plus_constant (addr, 8)), reg);
-      emit_move_insn (gen_rtx (MEM, pmode,
-                              plus_constant (addr, (TARGET_64BIT ? 16 : 12))),
-                     cxt);
-
-      rs6000_sync_trampoline (addr);
+      {
+       rtx reg = gen_reg_rtx (pmode);
+       emit_move_insn (reg, fnaddr);
+       emit_move_insn (MEM_PLUS (addr, 8), reg);
+       emit_move_insn (MEM_PLUS (addr, 8 + regsize), ctx_reg);
+       rs6000_sync_trampoline (addr);
+      }
       break;
 
-    /* Under NT, update the first 2 words to look like a normal descriptor, and
-       then fill in the fields with the function address and static chain after
-       the bl instruction.  */
+    /* Under NT, update the first word to point to the ..LTRAMP1..0 header,
+       the second word will point to the whole trampoline, third-fifth words
+       will then have the real address, static chain, and toc value.  */
     case ABI_NT:
-      reg  = gen_reg_rtx (pmode);
-      reg2 = gen_reg_rtx (pmode);
-      reg3 = gen_reg_rtx (pmode);
-
-      emit_move_insn (gen_rtx (MEM, pmode, plus_constant (addr, 4)),
-                     gen_rtx (REG, pmode, 2));
-      emit_move_insn (reg, fnaddr);
-      emit_move_insn (reg2, gen_rtx (MEM, pmode, reg));
-      emit_move_insn (reg3, plus_constant (addr, 8));
-      emit_move_insn (gen_rtx (MEM, pmode, plus_constant (addr, 16)), reg);
-      emit_move_insn (gen_rtx (MEM, pmode, addr), reg3);
-      emit_move_insn (gen_rtx (MEM, pmode, plus_constant (addr, 20)), cxt);
-      rs6000_sync_trampoline (addr);
+      {
+       rtx tramp_reg = gen_reg_rtx (pmode);
+       rtx fn_reg = gen_reg_rtx (pmode);
+       rtx toc_reg = gen_reg_rtx (pmode);
+
+       emit_move_insn (tramp_reg, gen_rtx (SYMBOL_REF, pmode, "..LTRAMP1..0"));
+       addr = force_reg (pmode, addr);
+       emit_move_insn (fn_reg, MEM_DEREF (fnaddr));
+       emit_move_insn (toc_reg, MEM_PLUS (fnaddr, regsize));
+       emit_move_insn (MEM_DEREF (addr), tramp_reg);
+       emit_move_insn (MEM_PLUS (addr, regsize), addr);
+       emit_move_insn (MEM_PLUS (addr, 2*regsize), fn_reg);
+       emit_move_insn (MEM_PLUS (addr, 3*regsize), ctx_reg);
+       emit_move_insn (MEM_PLUS (addr, 4*regsize), gen_rtx (REG, pmode, 2));
+      }
       break;
     }
 
@@ -4194,6 +4253,11 @@ rs6000_valid_type_attribute_p (type, attributes, identifier, args)
       && TREE_CODE (type) != TYPE_DECL)
     return 0;
 
+  /* Longcall attribute says that the function is not within 2**26 bytes
+     of the current function, and to do an indirect call.  */
+  if (is_attribute_p ("longcall", identifier))
+    return (args == NULL_TREE);
+
   if (DEFAULT_ABI == ABI_NT)
     {
       /* Stdcall attribute says callee is responsible for popping arguments
@@ -4270,6 +4334,34 @@ rs6000_dll_import_ref (call_ref)
   return reg2;
 }
 
+/* Return a reference suitable for calling a function with the longcall attribute.  */
+struct rtx_def *
+rs6000_longcall_ref (call_ref)
+     rtx call_ref;
+{
+  char *call_name;
+  int len;
+  char *p;
+  rtx reg1, reg2;
+  tree node;
+
+  if (GET_CODE (call_ref) != SYMBOL_REF)
+    return call_ref;
+
+  /* System V adds '.' to the internal name, so skip them.  */
+  call_name = XSTR (call_ref, 0);
+  if (*call_name == '.')
+    {
+      while (*call_name == '.')
+       call_name++;
+
+      node = get_identifier (call_name);
+      call_ref = gen_rtx (SYMBOL_REF, VOIDmode, IDENTIFIER_POINTER (node));
+    }
+
+  return force_reg (Pmode, call_ref);
+}
+
 \f
 /* A C statement or statements to switch to the appropriate section
    for output of RTX in mode MODE.  You can assume that RTX is some
This page took 0.04862 seconds and 5 git commands to generate.