aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/sparc/sparc.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/sparc/sparc.c')
-rw-r--r--gcc/config/sparc/sparc.c347
1 files changed, 295 insertions, 52 deletions
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index 9f608c352eb..4c627091302 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -1,8 +1,8 @@
/* Subroutines for insn-output.c for Sun SPARC.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com)
- 64 bit SPARC V9 support by Michael Tiemann, Jim Wilson, and Doug Evans,
+ 64-bit SPARC-V9 support by Michael Tiemann, Jim Wilson, and Doug Evans,
at Cygnus Support.
This file is part of GNU CC.
@@ -178,6 +178,8 @@ static void emit_hard_tfmode_operation PARAMS ((enum rtx_code, rtx *));
static void sparc_encode_section_info PARAMS ((tree, int));
static void sparc_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
HOST_WIDE_INT, tree));
+static bool sparc_can_output_mi_thunk PARAMS ((tree, HOST_WIDE_INT,
+ HOST_WIDE_INT, tree));
/* Option handling. */
@@ -244,7 +246,7 @@ enum processor_type sparc_cpu;
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK sparc_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
-#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK sparc_can_output_mi_thunk
struct gcc_target targetm = TARGET_INITIALIZER;
@@ -1357,6 +1359,34 @@ input_operand (op, mode)
return 0;
}
+/* Return 1 if OP is valid for the lhs of a compare insn. */
+
+int
+compare_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (GET_CODE (op) == ZERO_EXTRACT)
+ return (register_operand (XEXP (op, 0), mode)
+ && small_int_or_double (XEXP (op, 1), mode)
+ && small_int_or_double (XEXP (op, 2), mode)
+ /* This matches cmp_zero_extract. */
+ && ((mode == SImode
+ && ((GET_CODE (XEXP (op, 2)) == CONST_INT
+ && INTVAL (XEXP (op, 2)) > 19)
+ || (GET_CODE (XEXP (op, 2)) == CONST_DOUBLE
+ && CONST_DOUBLE_LOW (XEXP (op, 2)) > 19)))
+ /* This matches cmp_zero_extract_sp64. */
+ || (mode == DImode
+ && TARGET_ARCH64
+ && ((GET_CODE (XEXP (op, 2)) == CONST_INT
+ && INTVAL (XEXP (op, 2)) > 51)
+ || (GET_CODE (XEXP (op, 2)) == CONST_DOUBLE
+ && CONST_DOUBLE_LOW (XEXP (op, 2)) > 51)))));
+ else
+ return register_operand (op, mode);
+}
+
/* We know it can't be done in one insn when we get here,
the movsi expander guarentees this. */
@@ -3393,7 +3423,7 @@ mem_min_alignment (mem, desired)
/* Vectors to keep interesting information about registers where it can easily
- be got. We use to use the actual mode value as the bit number, but there
+ be got. We used to use the actual mode value as the bit number, but there
are more than 32 modes now. Instead we use two tables: one indexed by
hard register number, and one indexed by mode. */
@@ -4037,11 +4067,19 @@ sparc_nonflat_function_epilogue (file, size, leaf_function)
of a function were call foo; dslot; this can make the return
PC of foo (ie. address of call instruction plus 8) point to
the first instruction in the next function. */
- rtx insn;
-
- fputs("\tnop\n", file);
+ rtx insn, last_real_insn;
insn = get_last_insn ();
+
+ last_real_insn = prev_real_insn (insn);
+ if (last_real_insn
+ && GET_CODE (last_real_insn) == INSN
+ && GET_CODE (PATTERN (last_real_insn)) == SEQUENCE)
+ last_real_insn = XVECEXP (PATTERN (last_real_insn), 0, 0);
+
+ if (last_real_insn && GET_CODE (last_real_insn) == CALL_INSN)
+ fputs("\tnop\n", file);
+
if (GET_CODE (insn) == NOTE)
insn = prev_nonnote_insn (insn);
if (insn && GET_CODE (insn) == BARRIER)
@@ -4522,10 +4560,13 @@ function_arg_slotno (cum, mode, type, named, incoming_p, pregno, ppadding)
struct function_arg_record_value_parms
{
- rtx ret;
- int slotno, named, regbase;
- unsigned int nregs;
- int intoffset;
+ rtx ret; /* return expression being built. */
+ int slotno; /* slot number of the argument. */
+ int named; /* whether the argument is named. */
+ int regbase; /* regno of the base register. */
+ int stack; /* 1 if part of the argument is on the stack. */
+ int intoffset; /* offset of the pending integer field. */
+ unsigned int nregs; /* number of words passed in registers. */
};
static void function_arg_record_value_3
@@ -4600,8 +4641,13 @@ function_arg_record_value_1 (type, startbitpos, parms)
this_slotno = parms->slotno + parms->intoffset
/ BITS_PER_WORD;
- intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
- intslots = MAX (intslots, 0);
+ if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno)
+ {
+ intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno);
+ /* We need to pass this field on the stack. */
+ parms->stack = 1;
+ }
+
parms->nregs += intslots;
parms->intoffset = -1;
}
@@ -4666,7 +4712,7 @@ function_arg_record_value_3 (bitpos, parms)
{
regno = parms->regbase + this_slotno;
reg = gen_rtx_REG (mode, regno);
- XVECEXP (parms->ret, 0, parms->nregs)
+ XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (intoffset));
this_slotno += 1;
@@ -4739,7 +4785,7 @@ function_arg_record_value_2 (type, startbitpos, parms)
default: break;
}
reg = gen_rtx_REG (mode, regno);
- XVECEXP (parms->ret, 0, parms->nregs)
+ XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg,
GEN_INT (bitpos / BITS_PER_UNIT));
parms->nregs += 1;
@@ -4747,7 +4793,7 @@ function_arg_record_value_2 (type, startbitpos, parms)
{
regno += GET_MODE_SIZE (mode) / 4;
reg = gen_rtx_REG (mode, regno);
- XVECEXP (parms->ret, 0, parms->nregs)
+ XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg,
GEN_INT ((bitpos + GET_MODE_BITSIZE (mode))
/ BITS_PER_UNIT));
@@ -4764,8 +4810,19 @@ function_arg_record_value_2 (type, startbitpos, parms)
}
/* Used by function_arg and function_value to implement the complex
- SPARC64 structure calling conventions. */
+ conventions of the 64-bit ABI for passing and returning structures.
+ Return an expression valid as a return value for the two macros
+ FUNCTION_ARG and FUNCTION_VALUE.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ MODE is the argument's machine mode.
+ SLOTNO is the index number of the argument's slot in the parameter array.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis).
+ REGBASE is the regno of the base register for the parameter array. */
+
static rtx
function_arg_record_value (type, mode, slotno, named, regbase)
tree type;
@@ -4780,6 +4837,7 @@ function_arg_record_value (type, mode, slotno, named, regbase)
parms.slotno = slotno;
parms.named = named;
parms.regbase = regbase;
+ parms.stack = 0;
/* Compute how many registers we need. */
parms.nregs = 0;
@@ -4796,8 +4854,12 @@ function_arg_record_value (type, mode, slotno, named, regbase)
intslots = (endbit - startbit) / BITS_PER_WORD;
this_slotno = slotno + parms.intoffset / BITS_PER_WORD;
- intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
- intslots = MAX (intslots, 0);
+ if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno)
+ {
+ intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno);
+ /* We need to pass this field on the stack. */
+ parms.stack = 1;
+ }
parms.nregs += intslots;
}
@@ -4827,7 +4889,17 @@ function_arg_record_value (type, mode, slotno, named, regbase)
if (nregs == 0)
abort ();
- parms.ret = gen_rtx_PARALLEL (mode, rtvec_alloc (nregs));
+ parms.ret = gen_rtx_PARALLEL (mode, rtvec_alloc (parms.stack + nregs));
+
+ /* If at least one field must be passed on the stack, generate
+ (parallel [(expr_list (nil) ...) ...]) so that all fields will
+ also be passed on the stack. We can't do much better because the
+ semantics of FUNCTION_ARG_PARTIAL_NREGS doesn't handle the case
+ of structures for which the fields passed exclusively in registers
+ are not at the beginning of the structure. */
+ if (parms.stack)
+ XVECEXP (parms.ret, 0, 0)
+ = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
/* Fill in the entries. */
parms.nregs = 0;
@@ -5372,18 +5444,80 @@ sparc_va_arg (valist, type)
return addr_rtx;
}
+/* Return the string to output an unconditional branch to LABEL, which is
+ the operand number of the label.
+
+ DEST is the destination insn (i.e. the label), INSN is the source. */
+
+const char *
+output_ubranch (dest, label, insn)
+ rtx dest;
+ int label;
+ rtx insn;
+{
+ static char string[64];
+ bool noop = false;
+ char *p;
+
+ /* TurboSPARC is reported to have problems with
+ with
+ foo: b,a foo
+ i.e. an empty loop with the annul bit set. The workaround is to use
+ foo: b foo; nop
+ instead. */
+
+ if (! TARGET_V9 && flag_delayed_branch
+ && (INSN_ADDRESSES (INSN_UID (dest))
+ == INSN_ADDRESSES (INSN_UID (insn))))
+ {
+ strcpy (string, "b\t");
+ noop = true;
+ }
+ else
+ {
+ bool v9_form = false;
+
+ if (TARGET_V9 && INSN_ADDRESSES_SET_P ())
+ {
+ int delta = (INSN_ADDRESSES (INSN_UID (dest))
+ - INSN_ADDRESSES (INSN_UID (insn)));
+ /* Leave some instructions for "slop". */
+ if (delta >= -260000 && delta < 260000)
+ v9_form = true;
+ }
+
+ if (v9_form)
+ strcpy (string, "ba%*,pt\t%%xcc, ");
+ else
+ strcpy (string, "b%*\t");
+ }
+
+ p = strchr (string, '\0');
+ *p++ = '%';
+ *p++ = 'l';
+ *p++ = '0' + label;
+ *p++ = '%';
+ if (noop)
+ *p++ = '#';
+ else
+ *p++ = '(';
+ *p = '\0';
+
+ return string;
+}
+
/* Return the string to output a conditional branch to LABEL, which is
the operand number of the label. OP is the conditional expression.
XEXP (OP, 0) is assumed to be a condition code register (integer or
floating point) and its mode specifies what kind of comparison we made.
+ DEST is the destination insn (i.e. the label), INSN is the source.
+
REVERSED is nonzero if we should reverse the sense of the comparison.
ANNUL is nonzero if we should generate an annulling branch.
- NOOP is nonzero if we have to follow this branch by a noop.
-
- INSN, if set, is the insn. */
+ NOOP is nonzero if we have to follow this branch by a noop. */
char *
output_cbranch (op, dest, label, reversed, annul, noop, insn)
@@ -5421,7 +5555,7 @@ output_cbranch (op, dest, label, reversed, annul, noop, insn)
nop
ba .LC29 */
- far = get_attr_length (insn) >= 3;
+ far = TARGET_V9 && (get_attr_length (insn) >= 3);
if (reversed ^ far)
{
/* Reversal of FP compares takes care -- an ordered compare
@@ -5551,9 +5685,7 @@ output_cbranch (op, dest, label, reversed, annul, noop, insn)
spaces -= 2;
}
- if (! TARGET_V9)
- labelno = "";
- else
+ if (TARGET_V9)
{
rtx note;
int v8 = 0;
@@ -5603,6 +5735,9 @@ output_cbranch (op, dest, label, reversed, annul, noop, insn)
spaces -= 3;
}
}
+ else
+ labelno = "";
+
if (spaces > 0)
*p++ = '\t';
else
@@ -5810,6 +5945,8 @@ sparc_emit_floatunsdi (operands)
operand number of the reg. OP is the conditional expression. The mode
of REG says what kind of comparison we made.
+ DEST is the destination insn (i.e. the label), INSN is the source.
+
REVERSED is nonzero if we should reverse the sense of the comparison.
ANNUL is nonzero if we should generate an annulling branch.
@@ -6464,6 +6601,24 @@ print_operand (file, x, code)
output_address (XEXP (x, 0));
return;
+ case 's':
+ {
+ /* Print a sign-extended 32-bit value. */
+ HOST_WIDE_INT i;
+ if (GET_CODE(x) == CONST_INT)
+ i = INTVAL (x);
+ else if (GET_CODE(x) == CONST_DOUBLE)
+ i = CONST_DOUBLE_LOW (x);
+ else
+ {
+ output_operand_lossage ("invalid %%s operand");
+ return;
+ }
+ i = trunc_int_for_mode (i, SImode);
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, i);
+ return;
+ }
+
case 0:
/* Do nothing special. */
break;
@@ -6707,7 +6862,7 @@ void
sparc_initialize_trampoline (tramp, fnaddr, cxt)
rtx tramp, fnaddr, cxt;
{
- /* SPARC 32 bit trampoline:
+ /* SPARC 32-bit trampoline:
sethi %hi(fn), %g1
sethi %hi(static), %g2
@@ -6717,10 +6872,6 @@ sparc_initialize_trampoline (tramp, fnaddr, cxt)
SETHI i,r = 00rr rrr1 00ii iiii iiii iiii iiii iiii
JMPL r+i,d = 10dd ddd1 1100 0rrr rr1i iiii iiii iiii
*/
-#ifdef TRANSFER_FROM_TRAMPOLINE
- emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"),
- LCT_NORMAL, VOIDmode, 1, tramp, Pmode);
-#endif
emit_move_insn
(gen_rtx_MEM (SImode, plus_constant (tramp, 0)),
@@ -6759,9 +6910,17 @@ sparc_initialize_trampoline (tramp, fnaddr, cxt)
&& sparc_cpu != PROCESSOR_ULTRASPARC3)
emit_insn (gen_flush (validize_mem (gen_rtx_MEM (SImode,
plus_constant (tramp, 8)))));
+
+ /* Call __enable_execute_stack after writing onto the stack to make sure
+ the stack address is accessible. */
+#ifdef TRANSFER_FROM_TRAMPOLINE
+ emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"),
+ LCT_NORMAL, VOIDmode, 1, tramp, Pmode);
+#endif
+
}
-/* The 64 bit version is simpler because it makes more sense to load the
+/* The 64-bit version is simpler because it makes more sense to load the
values as "immediate" data out of the trampoline. It's also easier since
we can read the PC without clobbering a register. */
@@ -6769,12 +6928,8 @@ void
sparc64_initialize_trampoline (tramp, fnaddr, cxt)
rtx tramp, fnaddr, cxt;
{
-#ifdef TRANSFER_FROM_TRAMPOLINE
- emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"),
- LCT_NORMAL, VOIDmode, 1, tramp, Pmode);
-#endif
+ /* SPARC 64-bit trampoline:
- /*
rd %pc, %g1
ldx [%g1+24], %g5
jmp %g5
@@ -6797,6 +6952,13 @@ sparc64_initialize_trampoline (tramp, fnaddr, cxt)
if (sparc_cpu != PROCESSOR_ULTRASPARC
&& sparc_cpu != PROCESSOR_ULTRASPARC3)
emit_insn (gen_flushdi (validize_mem (gen_rtx_MEM (DImode, plus_constant (tramp, 8)))));
+
+ /* Call __enable_execute_stack after writing onto the stack to make sure
+ the stack address is accessible. */
+#ifdef TRANSFER_FROM_TRAMPOLINE
+ emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__enable_execute_stack"),
+ LCT_NORMAL, VOIDmode, 1, tramp, Pmode);
+#endif
}
/* Subroutines to support a flat (single) register window calling
@@ -7953,6 +8115,8 @@ sparc_check_64 (x, insn)
return 0;
}
+/* Returns assembly code to perform a DImode shift using
+ a 64-bit global or out register on SPARC-V8+. */
char *
sparc_v8plus_shift (operands, insn, opcode)
rtx *operands;
@@ -7961,8 +8125,15 @@ sparc_v8plus_shift (operands, insn, opcode)
{
static char asm_code[60];
- if (GET_CODE (operands[3]) == SCRATCH)
+ /* The scratch register is only required when the destination
+ register is not a 64-bit global or out register. */
+ if (which_alternative != 2)
operands[3] = operands[0];
+
+ /* We can only shift by constants <= 63. */
+ if (GET_CODE (operands[2]) == CONST_INT)
+ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
if (GET_CODE (operands[1]) == CONST_INT)
{
output_asm_insn ("mov\t%1, %3", operands);
@@ -7976,6 +8147,7 @@ sparc_v8plus_shift (operands, insn, opcode)
}
strcpy(asm_code, opcode);
+
if (which_alternative != 2)
return strcat (asm_code, "\t%0, %2, %L0\n\tsrlx\t%L0, 32, %H0");
else
@@ -8452,18 +8624,21 @@ sparc_encode_section_info (decl, first)
SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
}
-/* Output code to add DELTA to the first argument, and then jump to FUNCTION.
- Used for C++ multiple inheritance. */
+/* Output the assembler code for a thunk function. THUNK_DECL is the
+ declaration for the thunk function itself, FUNCTION is the decl for
+ the target function. DELTA is an immediate constant offset to be
+ added to THIS. If VCALL_OFFSET is nonzero, the word at address
+ (*THIS + VCALL_OFFSET) should be additionally added to THIS. */
static void
sparc_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
FILE *file;
tree thunk_fndecl ATTRIBUTE_UNUSED;
HOST_WIDE_INT delta;
- HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED;
+ HOST_WIDE_INT vcall_offset;
tree function;
{
- rtx this, insn, funexp, delta_rtx, tmp;
+ rtx this, insn, funexp;
reload_completed = 1;
no_new_pseudos = 1;
@@ -8480,19 +8655,73 @@ sparc_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
/* Add DELTA. When possible use a plain add, otherwise load it into
a register first. */
- delta_rtx = GEN_INT (delta);
- if (!SPARC_SIMM13_P (delta))
+ if (delta)
+ {
+ rtx delta_rtx = GEN_INT (delta);
+
+ if (! SPARC_SIMM13_P (delta))
+ {
+ rtx scratch = gen_rtx_REG (Pmode, 1);
+ emit_move_insn (scratch, delta_rtx);
+ delta_rtx = scratch;
+ }
+
+ /* THIS += DELTA. */
+ emit_insn (gen_add2_insn (this, delta_rtx));
+ }
+
+ /* Add the word at address (*THIS + VCALL_OFFSET). */
+ if (vcall_offset)
{
+ rtx vcall_offset_rtx = GEN_INT (vcall_offset);
rtx scratch = gen_rtx_REG (Pmode, 1);
- if (TARGET_ARCH64)
- sparc_emit_set_const64 (scratch, delta_rtx);
+
+ if (vcall_offset >= 0)
+ abort ();
+
+ /* SCRATCH = *THIS. */
+ emit_move_insn (scratch, gen_rtx_MEM (Pmode, this));
+
+ /* Prepare for adding VCALL_OFFSET. The difficulty is that we
+ may not have any available scratch register at this point. */
+ if (SPARC_SIMM13_P (vcall_offset))
+ ;
+ /* This is the case if ARCH64 (unless -ffixed-g5 is passed). */
+ else if (! fixed_regs[5]
+ /* The below sequence is made up of at least 2 insns,
+ while the default method may need only one. */
+ && vcall_offset < -8192)
+ {
+ rtx scratch2 = gen_rtx_REG (Pmode, 5);
+ emit_move_insn (scratch2, vcall_offset_rtx);
+ vcall_offset_rtx = scratch2;
+ }
else
- sparc_emit_set_const32 (scratch, delta_rtx);
- delta_rtx = scratch;
- }
+ {
+ rtx increment = GEN_INT (-4096);
+
+ /* VCALL_OFFSET is a negative number whose typical range can be
+ estimated as -32768..0 in 32-bit mode. In almost all cases
+ it is therefore cheaper to emit multiple add insns than
+ spilling and loading the constant into a register (at least
+ 6 insns). */
+ while (! SPARC_SIMM13_P (vcall_offset))
+ {
+ emit_insn (gen_add2_insn (scratch, increment));
+ vcall_offset += 4096;
+ }
+ vcall_offset_rtx = GEN_INT (vcall_offset); /* cannot be 0 */
+ }
+
+ /* SCRATCH = *(*THIS + VCALL_OFFSET). */
+ emit_move_insn (scratch, gen_rtx_MEM (Pmode,
+ gen_rtx_PLUS (Pmode,
+ scratch,
+ vcall_offset_rtx)));
- tmp = gen_rtx_PLUS (Pmode, this, delta_rtx);
- emit_insn (gen_rtx_SET (VOIDmode, this, tmp));
+ /* THIS += *(*THIS + VCALL_OFFSET). */
+ emit_insn (gen_add2_insn (this, scratch));
+ }
/* Generate a tail call to the target function. */
if (! TREE_USED (function))
@@ -8520,4 +8749,18 @@ sparc_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
no_new_pseudos = 0;
}
+/* Return true if sparc_output_mi_thunk would be able to output the
+ assembler code for the thunk function specified by the arguments
+ it is passed, and false otherwise. */
+static bool
+sparc_can_output_mi_thunk (thunk_fndecl, delta, vcall_offset, function)
+ tree thunk_fndecl ATTRIBUTE_UNUSED;
+ HOST_WIDE_INT delta ATTRIBUTE_UNUSED;
+ HOST_WIDE_INT vcall_offset;
+ tree function ATTRIBUTE_UNUSED;
+{
+ /* Bound the loop used in the default method above. */
+ return (vcall_offset >= -32768 || ! fixed_regs[5]);
+}
+
#include "gt-sparc.h"