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)
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
&& 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 */
&& 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:");
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);
}
&& 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);
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))
/* 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;
}
{
char *sc = reg_names[STATIC_CHAIN_REGNUM];
char *r0 = reg_names[0];
+ char *r2 = reg_names[2];
switch (DEFAULT_ABI)
{
/* 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;
}
break;
case ABI_NT:
- ret = 52;
+ ret = 20;
break;
}
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;
}
&& 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
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