mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-08 16:47:25 +08:00
sparc.c, [...]: Many changes related to V9 code generation.
Sat Apr 18 01:23:11 1998 John Carr <jfc@mit.edu> * sparc.c, sparc.h, sparc.md, sol2.h: Many changes related to V9 code generation. Use 64 bit instructions in 32 bit mode when possible. Use V9 return instruction. UltraSPARC optimizations. * sparc.h: Change gen_rtx (CODE to gen_rtx_CODE (. From-SVN: r19278
This commit is contained in:
parent
f4c81730d8
commit
284d86e9f0
@ -1,3 +1,11 @@
|
||||
Sat Apr 18 01:23:11 1998 John Carr <jfc@mit.edu>
|
||||
|
||||
* sparc.c, sparc.h, sparc.md, sol2.h: Many changes related to V9
|
||||
code generation. Use 64 bit instructions in 32 bit mode when
|
||||
possible. Use V9 return instruction. UltraSPARC optimizations.
|
||||
|
||||
* sparc.h: Change gen_rtx (CODE to gen_rtx_CODE (.
|
||||
|
||||
Fri Apr 17 22:38:17 1998 Jeffrey A Law (law@cygnus.com)
|
||||
|
||||
* global.c (global_alloc): Don't pass HARD_CONST (0) to find_reg,
|
||||
|
@ -198,3 +198,9 @@ Boston, MA 02111-1307, USA. */
|
||||
#define TARGET_LIVE_G0 0
|
||||
#undef TARGET_BROKEN_SAVERESTORE
|
||||
#define TARGET_BROKEN_SAVERESTORE 0
|
||||
|
||||
/* Solaris allows 64 bit out and global registers in 32 bit mode.
|
||||
sparc_override_options will disable V8+ if not generating V9 code. */
|
||||
#undef TARGET_DEFAULT
|
||||
#define TARGET_DEFAULT (MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU + MASK_V8PLUS)
|
||||
|
||||
|
@ -22,17 +22,7 @@ the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#else
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#endif
|
||||
#include "system.h"
|
||||
#include "tree.h"
|
||||
#include "rtl.h"
|
||||
#include "regs.h"
|
||||
@ -208,11 +198,9 @@ sparc_override_options ()
|
||||
{ "sparclet", PROCESSOR_SPARCLET, MASK_ISA, MASK_SPARCLET },
|
||||
/* TEMIC sparclet */
|
||||
{ "tsc701", PROCESSOR_TSC701, MASK_ISA, MASK_SPARCLET },
|
||||
/* "v8plus" is what Sun calls Solaris2.5 running on UltraSPARC's. */
|
||||
{ "v8plus", PROCESSOR_V8PLUS, MASK_ISA, MASK_V8PLUS },
|
||||
{ "v9", PROCESSOR_V9, MASK_ISA, MASK_V9 },
|
||||
/* TI ultrasparc */
|
||||
{ "ultrasparc", PROCESSOR_ULTRASPARC, MASK_ISA, MASK_V8PLUS },
|
||||
{ "ultrasparc", PROCESSOR_ULTRASPARC, MASK_ISA, MASK_V9 },
|
||||
{ 0 }
|
||||
};
|
||||
struct cpu_table *cpu;
|
||||
@ -288,6 +276,10 @@ sparc_override_options ()
|
||||
if (TARGET_V9 && TARGET_ARCH32)
|
||||
target_flags |= MASK_DEPRECATED_V8_INSNS;
|
||||
|
||||
/* V8PLUS requires V9 */
|
||||
if (! TARGET_V9)
|
||||
target_flags &= ~MASK_V8PLUS;
|
||||
|
||||
/* Validate -malign-loops= value, or provide default. */
|
||||
if (sparc_align_loops_string)
|
||||
{
|
||||
@ -333,40 +325,6 @@ sparc_override_options ()
|
||||
sparc_init_modes ();
|
||||
}
|
||||
|
||||
/* Float conversions (v9 only).
|
||||
|
||||
The floating point registers cannot hold DImode values because SUBREG's
|
||||
on them get the wrong register. "(subreg:SI (reg:DI M int-reg) 0)" is the
|
||||
same as "(subreg:SI (reg:DI N float-reg) 1)", but gcc doesn't know how to
|
||||
turn the "0" to a "1". Therefore, we must explicitly do the conversions
|
||||
to/from int/fp regs. `sparc64_fpconv_stack_slot' is the address of an
|
||||
8 byte stack slot used during the transfer.
|
||||
??? I could have used [%fp-16] but I didn't want to add yet another
|
||||
dependence on this. */
|
||||
/* ??? Can we use assign_stack_temp here? */
|
||||
|
||||
static rtx fpconv_stack_temp;
|
||||
|
||||
/* Called once for each function. */
|
||||
|
||||
void
|
||||
sparc_init_expanders ()
|
||||
{
|
||||
fpconv_stack_temp = NULL_RTX;
|
||||
}
|
||||
|
||||
/* Assign a stack temp for fp/int DImode conversions. */
|
||||
|
||||
rtx
|
||||
sparc64_fpconv_stack_temp ()
|
||||
{
|
||||
if (fpconv_stack_temp == NULL_RTX)
|
||||
fpconv_stack_temp =
|
||||
assign_stack_local (DImode, GET_MODE_SIZE (DImode), 0);
|
||||
|
||||
return fpconv_stack_temp;
|
||||
}
|
||||
|
||||
/* Miscellaneous utilities. */
|
||||
|
||||
/* Nonzero if CODE, a comparison, is suitable for use in v9 conditional move
|
||||
@ -380,6 +338,14 @@ v9_regcmp_p (code)
|
||||
|| code == LE || code == GT);
|
||||
}
|
||||
|
||||
/* 32 bit registers are zero extended so only zero/non-zero comparisons
|
||||
work. */
|
||||
int
|
||||
v8plus_regcmp_p (code)
|
||||
enum rtx_code code;
|
||||
{
|
||||
return (code == EQ || code == NE);
|
||||
}
|
||||
|
||||
/* Operand constraints. */
|
||||
|
||||
@ -798,6 +764,16 @@ v9_regcmp_op (op, mode)
|
||||
return v9_regcmp_p (code);
|
||||
}
|
||||
|
||||
int
|
||||
v8plus_regcmp_op (op, mode)
|
||||
register rtx op;
|
||||
enum machine_mode mode;
|
||||
{
|
||||
enum rtx_code code = GET_CODE (op);
|
||||
|
||||
return (code == EQ || code == NE);
|
||||
}
|
||||
|
||||
/* Return 1 if this is a SIGN_EXTEND or ZERO_EXTEND operation. */
|
||||
|
||||
int
|
||||
@ -848,8 +824,13 @@ arith_operand (op, mode)
|
||||
rtx op;
|
||||
enum machine_mode mode;
|
||||
{
|
||||
return (register_operand (op, mode)
|
||||
|| (GET_CODE (op) == CONST_INT && SMALL_INT (op)));
|
||||
int val;
|
||||
if (register_operand (op, mode))
|
||||
return 1;
|
||||
if (GET_CODE (op) != CONST_INT)
|
||||
return 0;
|
||||
val = INTVAL (op) & 0xffffffff;
|
||||
return SPARC_SIMM13_P (val);
|
||||
}
|
||||
|
||||
/* Return true if OP is a register, or is a CONST_INT that can fit in a
|
||||
@ -1059,8 +1040,15 @@ gen_compare_reg (code, x, y)
|
||||
else
|
||||
cc_reg = gen_rtx (REG, mode, SPARC_ICC_REG);
|
||||
|
||||
emit_insn (gen_rtx (SET, VOIDmode, cc_reg,
|
||||
gen_rtx (COMPARE, mode, x, y)));
|
||||
if (TARGET_V8PLUS && mode == CCXmode)
|
||||
{
|
||||
emit_insn (gen_cmpdi_v8plus (x, y));
|
||||
}
|
||||
else
|
||||
{
|
||||
emit_insn (gen_rtx (SET, VOIDmode, cc_reg,
|
||||
gen_rtx (COMPARE, mode, x, y)));
|
||||
}
|
||||
|
||||
return cc_reg;
|
||||
}
|
||||
@ -1287,14 +1275,53 @@ eligible_for_epilogue_delay (trial, slot)
|
||||
|| register_operand (XEXP (src, 1), DImode)))
|
||||
return 1;
|
||||
|
||||
/* This matches "*return_subsi". */
|
||||
else if (GET_CODE (src) == MINUS
|
||||
&& register_operand (XEXP (src, 0), SImode)
|
||||
&& small_int (XEXP (src, 1), VOIDmode)
|
||||
&& INTVAL (XEXP (src, 1)) != -4096)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
check_return_regs (x)
|
||||
rtx x;
|
||||
{
|
||||
switch (GET_CODE (x))
|
||||
{
|
||||
case REG:
|
||||
return IN_OR_GLOBAL_P (x);
|
||||
|
||||
case CONST_INT:
|
||||
case CONST_DOUBLE:
|
||||
case CONST:
|
||||
case SYMBOL_REF:
|
||||
case LABEL_REF:
|
||||
return 1;
|
||||
|
||||
case SET:
|
||||
case IOR:
|
||||
case AND:
|
||||
case XOR:
|
||||
case PLUS:
|
||||
case MINUS:
|
||||
if (check_return_regs (XEXP (x, 1)) == 0)
|
||||
return 0;
|
||||
case NOT:
|
||||
case NEG:
|
||||
case MEM:
|
||||
return check_return_regs (XEXP (x, 0));
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Return 1 if TRIAL references only in and global registers. */
|
||||
int
|
||||
eligible_for_return_delay (trial)
|
||||
rtx trial;
|
||||
{
|
||||
if (GET_CODE (PATTERN (trial)) != SET)
|
||||
return 0;
|
||||
|
||||
return check_return_regs (PATTERN (trial));
|
||||
}
|
||||
|
||||
int
|
||||
@ -1346,6 +1373,10 @@ reg_unused_after (reg, insn)
|
||||
/* The table we use to reference PIC data. */
|
||||
static rtx global_offset_table;
|
||||
|
||||
/* The function we use to get at it. */
|
||||
static rtx get_pc_symbol;
|
||||
static char get_pc_symbol_name[256];
|
||||
|
||||
/* Ensure that we are not using patterns that are not OK with PIC. */
|
||||
|
||||
int
|
||||
@ -1499,61 +1530,11 @@ initialize_pic ()
|
||||
static rtx
|
||||
pic_setup_code ()
|
||||
{
|
||||
rtx pic_pc_rtx;
|
||||
rtx l1, l2;
|
||||
rtx seq;
|
||||
|
||||
start_sequence ();
|
||||
|
||||
/* If -O0, show the PIC register remains live before this. */
|
||||
if (obey_regdecls)
|
||||
emit_insn (gen_rtx (USE, VOIDmode, pic_offset_table_rtx));
|
||||
|
||||
l1 = gen_label_rtx ();
|
||||
|
||||
pic_pc_rtx = gen_rtx (CONST, Pmode,
|
||||
gen_rtx (MINUS, Pmode,
|
||||
global_offset_table,
|
||||
gen_rtx (CONST, Pmode,
|
||||
gen_rtx (MINUS, Pmode,
|
||||
gen_rtx (LABEL_REF,
|
||||
VOIDmode, l1),
|
||||
pc_rtx))));
|
||||
|
||||
/* sparc64: the RDPC instruction doesn't pair, and puts 4 bubbles in the
|
||||
pipe to boot. So don't use it here, especially when we're
|
||||
doing a save anyway because of %l7. */
|
||||
|
||||
l2 = gen_label_rtx ();
|
||||
emit_label (l1);
|
||||
|
||||
/* Iff we are doing delay branch optimization, slot the sethi up
|
||||
here so that it will fill the delay slot of the call. */
|
||||
if (flag_delayed_branch)
|
||||
emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx,
|
||||
gen_rtx (HIGH, Pmode, pic_pc_rtx)));
|
||||
|
||||
/* Note that we pun calls and jumps here! */
|
||||
emit_jump_insn (gen_get_pc_via_call (l2, l1));
|
||||
|
||||
emit_label (l2);
|
||||
|
||||
if (!flag_delayed_branch)
|
||||
emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx,
|
||||
gen_rtx (HIGH, Pmode, pic_pc_rtx)));
|
||||
|
||||
emit_insn (gen_rtx (SET, VOIDmode,
|
||||
pic_offset_table_rtx,
|
||||
gen_rtx (LO_SUM, Pmode,
|
||||
pic_offset_table_rtx, pic_pc_rtx)));
|
||||
emit_insn (gen_rtx (SET, VOIDmode,
|
||||
pic_offset_table_rtx,
|
||||
gen_rtx (PLUS, Pmode,
|
||||
pic_offset_table_rtx,
|
||||
gen_rtx (REG, Pmode, 15))));
|
||||
|
||||
/* emit_insn (gen_rtx (ASM_INPUT, VOIDmode, "!#PROLOGUE# 1")); */
|
||||
|
||||
emit_insn (gen_get_pc (pic_offset_table_rtx, global_offset_table,
|
||||
get_pc_symbol));
|
||||
seq = gen_sequence ();
|
||||
end_sequence ();
|
||||
|
||||
@ -1575,9 +1556,21 @@ finalize_pic ()
|
||||
if (! flag_pic)
|
||||
abort ();
|
||||
|
||||
/* If we havn't emitted the special get_pc helper function, do so now. */
|
||||
if (get_pc_symbol_name[0] == 0)
|
||||
{
|
||||
ASM_GENERATE_INTERNAL_LABEL (get_pc_symbol_name, "LGETPC", 0);
|
||||
|
||||
text_section ();
|
||||
ASM_OUTPUT_ALIGN (asm_out_file, 3);
|
||||
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LGETPC", 0);
|
||||
fputs ("\tretl\n\tadd %o7,%l7,%l7\n", asm_out_file);
|
||||
}
|
||||
|
||||
/* Initialize every time through, since we can't easily
|
||||
know this to be permanent. */
|
||||
global_offset_table = gen_rtx (SYMBOL_REF, Pmode, "_GLOBAL_OFFSET_TABLE_");
|
||||
get_pc_symbol = gen_rtx (SYMBOL_REF, Pmode, get_pc_symbol_name);
|
||||
flag_pic = 0;
|
||||
|
||||
emit_insn_after (pic_setup_code (), get_insns ());
|
||||
@ -1618,6 +1611,15 @@ emit_move_sequence (operands, mode)
|
||||
/* Handle most common case first: storing into a register. */
|
||||
if (register_operand (operand0, mode))
|
||||
{
|
||||
/* Integer constant to FP register. */
|
||||
if (GET_CODE (operand0) == REG
|
||||
&& REGNO (operand0) >= 32
|
||||
&& REGNO (operand0) < FIRST_PSEUDO_REGISTER
|
||||
&& CONSTANT_P (operand1))
|
||||
{
|
||||
operand1 = validize_mem (force_const_mem (GET_MODE (operand0), operand1));
|
||||
}
|
||||
|
||||
if (register_operand (operand1, mode)
|
||||
|| (GET_CODE (operand1) == CONST_INT && SMALL_INT (operand1))
|
||||
|| (GET_CODE (operand1) == CONST_DOUBLE
|
||||
@ -1683,6 +1685,7 @@ emit_move_sequence (operands, mode)
|
||||
}
|
||||
else if (GET_CODE (operand1) == CONST_INT
|
||||
? (! SMALL_INT (operand1)
|
||||
&& INTVAL (operand1) != -4096
|
||||
&& ! SPARC_SETHI_P (INTVAL (operand1)))
|
||||
: GET_CODE (operand1) == CONST_DOUBLE
|
||||
? ! arith_double_operand (operand1, DImode)
|
||||
@ -1704,16 +1707,20 @@ emit_move_sequence (operands, mode)
|
||||
rtx temp = ((reload_in_progress || mode == DImode)
|
||||
? operand0 : gen_reg_rtx (mode));
|
||||
|
||||
if (mode == SImode)
|
||||
{
|
||||
if (GET_CODE (operand1) == CONST_INT)
|
||||
operand1 = GEN_INT (INTVAL (operand1) & 0xffffffff);
|
||||
else if (GET_CODE (operand1) == CONST_DOUBLE)
|
||||
operand1 = GEN_INT (CONST_DOUBLE_LOW (operand1) & 0xffffffff);
|
||||
}
|
||||
|
||||
if (TARGET_ARCH64 && mode == DImode)
|
||||
emit_insn (gen_sethi_di_sp64 (temp, operand1));
|
||||
else
|
||||
emit_insn (gen_rtx (SET, VOIDmode, temp,
|
||||
gen_rtx (HIGH, mode, operand1)));
|
||||
|
||||
if (GET_CODE (operand1) == CONST_INT)
|
||||
operand1 = GEN_INT (INTVAL (operand1) & 0xffffffff);
|
||||
else if (GET_CODE (operand1) == CONST_DOUBLE)
|
||||
operand1 = GEN_INT (CONST_DOUBLE_LOW (operand1) & 0xffffffff);
|
||||
operands[1] = gen_rtx (LO_SUM, mode, temp, operand1);
|
||||
}
|
||||
}
|
||||
@ -1763,10 +1770,16 @@ singlemove_string (operands)
|
||||
else
|
||||
return "sethi %%hi(%a1),%0";
|
||||
}
|
||||
else if (GET_CODE (operands[1]) == CONST_INT
|
||||
&& ! CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'I'))
|
||||
else if (GET_CODE (operands[1]) == CONST_INT)
|
||||
{
|
||||
HOST_WIDE_INT i = INTVAL (operands[1]);
|
||||
/* Only consider the low 32 bits of the constant. */
|
||||
int i = INTVAL (operands[1]) & 0xffffffff;
|
||||
|
||||
if (SPARC_SIMM13_P (i))
|
||||
return "mov %1,%0";
|
||||
|
||||
if (i == 4096)
|
||||
return "sub %%g0,-4096,%0";
|
||||
|
||||
/* If all low order 10 bits are clear, then we only need a single
|
||||
sethi insn to load the constant. */
|
||||
@ -2291,9 +2304,9 @@ output_move_quad (operands)
|
||||
operands[2] = adj_offsettable_operand (mem, 8);
|
||||
/* ??? In arch64 case, shouldn't we use ldd/std for fp regs. */
|
||||
if (mem == op1)
|
||||
return TARGET_ARCH64 ? "ldx %1,%0;ldx %2,%R0" : "ldd %1,%0;ldd %2,%S0";
|
||||
return TARGET_ARCH64 ? "ldx %1,%0\n\tldx %2,%R0" : "ldd %1,%0\n\tldd %2,%S0";
|
||||
else
|
||||
return TARGET_ARCH64 ? "stx %1,%0;stx %R1,%2" : "std %1,%0;std %S1,%2";
|
||||
return TARGET_ARCH64 ? "stx %1,%0\n\tstx %R1,%2" : "std %1,%0\n\tstd %S1,%2";
|
||||
}
|
||||
}
|
||||
|
||||
@ -2968,13 +2981,10 @@ enum sparc_mode_class {
|
||||
/* Modes for double-float and smaller quantities. */
|
||||
#define DF_MODES (S_MODES | D_MODES)
|
||||
|
||||
/* ??? Sparc64 fp regs cannot hold DImode values. */
|
||||
#define DF_MODES64 (SF_MODES | (1 << (int) DF_MODE) /* | (1 << (int) D_MODE)*/)
|
||||
#define DF_MODES64 DF_MODES
|
||||
|
||||
/* Modes for double-float only quantities. */
|
||||
/* ??? Sparc64 fp regs cannot hold DImode values.
|
||||
See fix_truncsfdi2. */
|
||||
#define DF_ONLY_MODES ((1 << (int) DF_MODE) /*| (1 << (int) D_MODE)*/)
|
||||
#define DF_ONLY_MODES ((1 << (int) DF_MODE) | (1 << (int) D_MODE))
|
||||
|
||||
/* Modes for double-float and larger quantities. */
|
||||
#define DF_UP_MODES (DF_ONLY_MODES | TF_ONLY_MODES)
|
||||
@ -2985,8 +2995,6 @@ enum sparc_mode_class {
|
||||
/* Modes for quad-float and smaller quantities. */
|
||||
#define TF_MODES (DF_MODES | TF_ONLY_MODES)
|
||||
|
||||
/* ??? Sparc64 fp regs cannot hold DImode values.
|
||||
See fix_truncsfdi2. */
|
||||
#define TF_MODES64 (DF_MODES64 | TF_ONLY_MODES)
|
||||
|
||||
/* Modes for condition codes. */
|
||||
@ -3115,7 +3123,9 @@ sparc_init_modes ()
|
||||
/* Initialize the array used by REGNO_REG_CLASS. */
|
||||
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
||||
{
|
||||
if (i < 32)
|
||||
if (i < 16 && TARGET_V8PLUS)
|
||||
sparc_regno_reg_class[i] = I64_REGS;
|
||||
else if (i < 32)
|
||||
sparc_regno_reg_class[i] = GENERAL_REGS;
|
||||
else if (i < 64)
|
||||
sparc_regno_reg_class[i] = FP_REGS;
|
||||
@ -3584,6 +3594,8 @@ output_function_epilogue (file, size, leaf_function)
|
||||
PATTERN (insn)));
|
||||
final_scan_insn (insn, file, 1, 0, 1);
|
||||
}
|
||||
else if (TARGET_V9 && ! SKIP_CALLERS_UNIMP_P)
|
||||
fputs ("\treturn %i7+8\n\tnop\n", file);
|
||||
else
|
||||
fprintf (file, "\t%s\n\trestore\n", ret);
|
||||
}
|
||||
@ -4566,22 +4578,77 @@ output_v9branch (op, reg, label, reversed, annul, noop)
|
||||
return string;
|
||||
}
|
||||
|
||||
/* Output assembler code to return from a function. */
|
||||
/* Renumber registers in delay slot. Replace registers instead of
|
||||
renumbering because they may be shared.
|
||||
|
||||
/* ??? v9: Update to use the new `return' instruction. Also, add patterns to
|
||||
md file for the `return' instruction. */
|
||||
This does not handle instructions other than move. */
|
||||
|
||||
static void
|
||||
epilogue_renumber (where)
|
||||
rtx *where;
|
||||
{
|
||||
rtx x = *where;
|
||||
enum rtx_code code = GET_CODE (x);
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case MEM:
|
||||
*where = x = copy_rtx (x);
|
||||
epilogue_renumber (&XEXP (x, 0));
|
||||
return;
|
||||
|
||||
case REG:
|
||||
{
|
||||
int regno = REGNO (x);
|
||||
if (regno > 8 && regno < 24)
|
||||
abort ();
|
||||
if (regno >= 24 && regno < 32)
|
||||
*where = gen_rtx_REG (GET_MODE (x), regno - 16);
|
||||
return;
|
||||
}
|
||||
case CONST_INT:
|
||||
case CONST_DOUBLE:
|
||||
case CONST:
|
||||
case SYMBOL_REF:
|
||||
case LABEL_REF:
|
||||
return;
|
||||
|
||||
case IOR:
|
||||
case AND:
|
||||
case XOR:
|
||||
case PLUS:
|
||||
case MINUS:
|
||||
epilogue_renumber (&XEXP (x, 1));
|
||||
case NEG:
|
||||
case NOT:
|
||||
epilogue_renumber (&XEXP (x, 0));
|
||||
return;
|
||||
|
||||
default:
|
||||
debug_rtx (*where);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/* Output assembler code to return from a function. */
|
||||
|
||||
char *
|
||||
output_return (operands)
|
||||
rtx *operands;
|
||||
{
|
||||
rtx delay = final_sequence ? XVECEXP (final_sequence, 0, 1) : 0;
|
||||
|
||||
if (leaf_label)
|
||||
{
|
||||
operands[0] = leaf_label;
|
||||
return "b,a %l0";
|
||||
return "b%* %l0%(";
|
||||
}
|
||||
else if (leaf_function)
|
||||
{
|
||||
/* No delay slot in a leaf function. */
|
||||
if (delay)
|
||||
abort ();
|
||||
|
||||
/* If we didn't allocate a frame pointer for the current function,
|
||||
the stack pointer might have been adjusted. Output code to
|
||||
restore it now. */
|
||||
@ -4621,8 +4688,22 @@ output_return (operands)
|
||||
return "sethi %%hi(%a0),%%g1\n\tretl\n\tadd %%sp,%%g1,%%sp";
|
||||
}
|
||||
}
|
||||
else if (TARGET_V9)
|
||||
{
|
||||
if (delay)
|
||||
{
|
||||
epilogue_renumber (&SET_DEST (PATTERN (delay)));
|
||||
epilogue_renumber (&SET_SRC (PATTERN (delay)));
|
||||
}
|
||||
if (SKIP_CALLERS_UNIMP_P)
|
||||
return "return %%i7+12%#";
|
||||
else
|
||||
return "return %%i7+8%#";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (delay)
|
||||
abort ();
|
||||
if (SKIP_CALLERS_UNIMP_P)
|
||||
return "jmp %%i7+12\n\trestore";
|
||||
else
|
||||
@ -4795,14 +4876,14 @@ print_operand (file, x, code)
|
||||
/* On UltraSPARC, a branch in a delay slot causes a pipeline flush.
|
||||
Always emit a nop in case the next instruction is a branch. */
|
||||
if (dbr_sequence_length () == 0
|
||||
&& (optimize && (int)sparc_cpu < PROCESSOR_V8PLUS))
|
||||
&& (optimize && (int)sparc_cpu < PROCESSOR_V9))
|
||||
fputs (",a", file);
|
||||
return;
|
||||
case '(':
|
||||
/* Output a 'nop' if there's nothing for the delay slot and we are
|
||||
not optimizing. This is always used with '*' above. */
|
||||
if (dbr_sequence_length () == 0
|
||||
&& ! (optimize && (int)sparc_cpu < PROCESSOR_V8PLUS))
|
||||
&& ! (optimize && (int)sparc_cpu < PROCESSOR_V9))
|
||||
fputs ("\n\tnop", file);
|
||||
return;
|
||||
case '_':
|
||||
@ -6066,7 +6147,8 @@ ultrasparc_adjust_cost (insn, link, dep_insn, cost)
|
||||
dep_type = get_attr_type (dep_insn);
|
||||
|
||||
#define SLOW_FP(dep_type) \
|
||||
(dep_type == TYPE_FPSQRT || dep_type == TYPE_FPDIVS || dep_type == TYPE_FPDIVD)
|
||||
(dep_type == TYPE_FPSQRT || dep_type == TYPE_FPDIVS || dep_type == TYPE_FPDIVD)
|
||||
|
||||
switch (REG_NOTE_KIND (link))
|
||||
{
|
||||
case 0:
|
||||
@ -6080,16 +6162,16 @@ ultrasparc_adjust_cost (insn, link, dep_insn, cost)
|
||||
case TYPE_FPSTORE:
|
||||
if (! SLOW_FP (dep_type))
|
||||
return 0;
|
||||
break;
|
||||
return cost;
|
||||
|
||||
case TYPE_STORE:
|
||||
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
|
||||
return cost;
|
||||
|
||||
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
|
||||
/* The dependency between the two instructions is on the data
|
||||
that is being stored. Assume that the address of the store
|
||||
is not also dependent. */
|
||||
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
|
||||
return 0;
|
||||
return cost;
|
||||
|
||||
@ -6109,15 +6191,15 @@ ultrasparc_adjust_cost (insn, link, dep_insn, cost)
|
||||
compensate for a dependency which might not really
|
||||
exist, and 0. */
|
||||
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET
|
||||
|| GET_CODE (SET_DEST (pat)) != MEM
|
||||
|| GET_CODE (SET_SRC (dep_pat)) != MEM
|
||||
|| ! rtx_equal_p (XEXP (SET_DEST (pat), 0),
|
||||
XEXP (SET_SRC (dep_pat), 0)))
|
||||
|| GET_CODE (SET_SRC (pat)) != MEM
|
||||
|| GET_CODE (SET_DEST (dep_pat)) != MEM
|
||||
|| ! rtx_equal_p (XEXP (SET_SRC (pat), 0),
|
||||
XEXP (SET_DEST (dep_pat), 0)))
|
||||
return cost + 2;
|
||||
|
||||
return cost + 8;
|
||||
}
|
||||
break;
|
||||
return cost;
|
||||
|
||||
case TYPE_BRANCH:
|
||||
/* Compare to branch latency is 0. There is no benefit from
|
||||
@ -6128,16 +6210,15 @@ ultrasparc_adjust_cost (insn, link, dep_insn, cost)
|
||||
compare to conditional move. */
|
||||
if (dep_type == TYPE_FPCMP)
|
||||
return cost - 1;
|
||||
break;
|
||||
return cost;
|
||||
|
||||
case TYPE_FPCMOVE:
|
||||
/* FMOVR class instructions can not issue in the same cycle
|
||||
or the cycle after an instruction which writes any
|
||||
integer register. Model this as cost 2 for dependent
|
||||
instructions. */
|
||||
if (GET_CODE (PATTERN (insn)) == SET
|
||||
&& (GET_MODE (SET_DEST (PATTERN (insn))) == SFmode
|
||||
|| GET_MODE (SET_DEST (PATTERN (insn))) == DFmode)
|
||||
if ((dep_type == TYPE_IALU || dep_type == TYPE_UNARY
|
||||
|| dep_type == TYPE_BINARY)
|
||||
&& cost < 2)
|
||||
return 2;
|
||||
/* Otherwise check as for integer conditional moves. */
|
||||
@ -6149,7 +6230,7 @@ ultrasparc_adjust_cost (insn, link, dep_insn, cost)
|
||||
to model. */
|
||||
if (dep_type == TYPE_LOAD || dep_type == TYPE_SLOAD)
|
||||
return cost + 3;
|
||||
break;
|
||||
return cost;
|
||||
|
||||
default:
|
||||
break;
|
||||
@ -6190,9 +6271,8 @@ sparc_issue_rate ()
|
||||
{
|
||||
default:
|
||||
return 1;
|
||||
case PROCESSOR_V8PLUS:
|
||||
case PROCESSOR_V9:
|
||||
/* Assume these generic V9 types are capable of at least dual-issue. */
|
||||
/* Assume V9 processors are capable of at least dual-issue. */
|
||||
return 2;
|
||||
case PROCESSOR_SUPERSPARC:
|
||||
return 3;
|
||||
@ -6200,3 +6280,175 @@ sparc_issue_rate ()
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
set_extends(x, insn)
|
||||
rtx x, insn;
|
||||
{
|
||||
register rtx pat = PATTERN (insn);
|
||||
|
||||
switch (GET_CODE (SET_SRC (pat)))
|
||||
{
|
||||
/* Load and some shift instructions zero extend. */
|
||||
case MEM:
|
||||
case ZERO_EXTEND:
|
||||
/* sethi clears the high bits */
|
||||
case HIGH:
|
||||
/* LO_SUM is used with sethi. sethi cleared the high
|
||||
bits and the values used with lo_sum are positive */
|
||||
case LO_SUM:
|
||||
/* UNSPEC is v8plus_clear_high */
|
||||
case UNSPEC:
|
||||
/* Store flag stores 0 or 1 */
|
||||
case LT: case LTU:
|
||||
case GT: case GTU:
|
||||
case LE: case LEU:
|
||||
case GE: case GEU:
|
||||
case EQ:
|
||||
case NE:
|
||||
return 1;
|
||||
case AND:
|
||||
{
|
||||
rtx op1 = XEXP (SET_SRC (pat), 1);
|
||||
if (GET_CODE (op1) == CONST_INT)
|
||||
return INTVAL (op1) >= 0;
|
||||
if (GET_CODE (XEXP (SET_SRC (pat), 0)) == REG
|
||||
&& sparc_check_64 (XEXP (SET_SRC (pat), 0), insn) == 1)
|
||||
return 1;
|
||||
if (GET_CODE (op1) == REG
|
||||
&& sparc_check_64 ((op1), insn) == 1)
|
||||
return 1;
|
||||
}
|
||||
case ASHIFT:
|
||||
case LSHIFTRT:
|
||||
return GET_MODE (SET_SRC (pat)) == SImode;
|
||||
/* Positive integers leave the high bits zero. */
|
||||
case CONST_DOUBLE:
|
||||
return ! (CONST_DOUBLE_LOW (x) & 0x80000000);
|
||||
case CONST_INT:
|
||||
return ! (INTVAL (x) & 0x80000000);
|
||||
case ASHIFTRT:
|
||||
case SIGN_EXTEND:
|
||||
return - (GET_MODE (SET_SRC (pat)) == SImode);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return 0 if the high 32 bits of X (the low word of X, if DImode) are
|
||||
unknown. Return 1 if the high bits are zero, -1 if the register is
|
||||
sign extended. */
|
||||
int
|
||||
sparc_check_64 (x, insn)
|
||||
rtx x, insn;
|
||||
{
|
||||
/* If a register is set only once it is safe to ignore insns this
|
||||
code does not know how to handle. The loop will either recognize
|
||||
the single set and return the correct value or fail to recognize
|
||||
it and return 0. */
|
||||
int set_once = 0;
|
||||
|
||||
if (GET_CODE (x) == REG
|
||||
&& flag_expensive_optimizations
|
||||
&& REG_N_SETS (REGNO (x)) == 1)
|
||||
set_once = 1;
|
||||
|
||||
if (insn == 0)
|
||||
if (set_once)
|
||||
insn = get_last_insn_anywhere ();
|
||||
else
|
||||
return 0;
|
||||
|
||||
while (insn = PREV_INSN (insn))
|
||||
{
|
||||
switch (GET_CODE (insn))
|
||||
{
|
||||
case JUMP_INSN:
|
||||
case NOTE:
|
||||
break;
|
||||
case CODE_LABEL:
|
||||
case CALL_INSN:
|
||||
default:
|
||||
if (! set_once)
|
||||
return 0;
|
||||
break;
|
||||
case INSN:
|
||||
{
|
||||
rtx pat = PATTERN (insn);
|
||||
if (GET_CODE (pat) != SET)
|
||||
return 0;
|
||||
if (rtx_equal_p (x, SET_DEST (pat)))
|
||||
return set_extends (x, insn);
|
||||
if (reg_overlap_mentioned_p (SET_DEST (pat), x))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
sparc_v8plus_shift (operands, insn, opcode)
|
||||
rtx *operands;
|
||||
rtx insn;
|
||||
char *opcode;
|
||||
{
|
||||
static char asm_code[60];
|
||||
|
||||
if (GET_CODE (operands[3]) == SCRATCH)
|
||||
operands[3] = operands[0];
|
||||
output_asm_insn ("sllx %H1,32,%3", operands);
|
||||
if (sparc_check_64 (operands[1], insn) <= 0)
|
||||
output_asm_insn ("srl %L1,0,%L1", operands);
|
||||
output_asm_insn ("or %L1,%3,%3", operands);
|
||||
|
||||
strcpy(asm_code, opcode);
|
||||
if (which_alternative != 2)
|
||||
return strcat (asm_code, " %0,%2,%L0\n\tsrlx %L0,32,%H0");
|
||||
else
|
||||
return strcat (asm_code, " %3,%2,%3\n\tsrlx %3,32,%H0\n\tmov %3,%L0");
|
||||
}
|
||||
|
||||
|
||||
/* Return 1 if DEST and SRC reference only global and in registers. */
|
||||
|
||||
int
|
||||
sparc_return_peephole_ok (dest, src)
|
||||
rtx dest, src;
|
||||
{
|
||||
if (! TARGET_V9)
|
||||
return 0;
|
||||
if (leaf_function)
|
||||
return 0;
|
||||
if (GET_CODE (src) != CONST_INT
|
||||
&& (GET_CODE (src) != REG || ! IN_OR_GLOBAL_P (src)))
|
||||
return 0;
|
||||
return IN_OR_GLOBAL_P (dest);
|
||||
}
|
||||
|
||||
int
|
||||
delay_operand (op, mode)
|
||||
rtx op;
|
||||
enum machine_mode mode;
|
||||
{
|
||||
switch (GET_CODE (op))
|
||||
{
|
||||
case CONST:
|
||||
case CONST_INT:
|
||||
case SYMBOL_REF:
|
||||
case LABEL_REF:
|
||||
return 1;
|
||||
|
||||
case MEM:
|
||||
return delay_operand (XEXP (op, 0), Pmode);
|
||||
|
||||
case REG:
|
||||
return IN_OR_GLOBAL_P (op);
|
||||
|
||||
case PLUS:
|
||||
return delay_operand (XEXP (op, 0), Pmode) && delay_operand (XEXP (op, 1), Pmode);
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +158,6 @@ Unrecognized value in TARGET_CPU_DEFAULT.
|
||||
%{mcpu=f930:-D__sparclite__} %{mcpu=f934:-D__sparclite__} \
|
||||
%{mcpu=v8:-D__sparc_v8__} \
|
||||
%{mcpu=supersparc:-D__supersparc__ -D__sparc_v8__} \
|
||||
%{mcpu=v8plus:-D__sparc_v9__} \
|
||||
%{mcpu=v9:-D__sparc_v9__} \
|
||||
%{mcpu=ultrasparc:-D__sparc_v9__} \
|
||||
%{!mcpu*:%{!mcypress:%{!msparclite:%{!mf930:%{!mf934:%{!mv8:%{!msupersparc:%(cpp_cpu_default)}}}}}}} \
|
||||
@ -209,9 +208,9 @@ Unrecognized value in TARGET_CPU_DEFAULT.
|
||||
%{mf930:-Asparclite} %{mf934:-Asparclite} \
|
||||
%{mcpu=sparclite:-Asparclite} \
|
||||
%{mcpu=f930:-Asparclite} %{mcpu=f934:-Asparclite} \
|
||||
%{mcpu=v8plus:-Av8plus} \
|
||||
%{mv8plus:-Av8plus} \
|
||||
%{mcpu=v9:-Av9} \
|
||||
%{mcpu=ultrasparc:-Av9a} \
|
||||
%{mcpu=ultrasparc:%{!mv8plus:-Av9a}} \
|
||||
%{!mcpu*:%{!mcypress:%{!msparclite:%{!mf930:%{!mf934:%{!mv8:%{!msupersparc:%(asm_cpu_default)}}}}}}} \
|
||||
"
|
||||
|
||||
@ -453,13 +452,17 @@ extern int target_flags;
|
||||
#define MASK_VIS 0x1000000
|
||||
#define TARGET_VIS (target_flags & MASK_VIS)
|
||||
|
||||
/* Compile for Solaris V8+. 64 bit instructions are available but the
|
||||
high 32 bits of all registers except the globals and current outs may
|
||||
be cleared at any time. */
|
||||
/* Compile for Solaris V8+. 32 bit Solaris preserves the high bits of
|
||||
the current out and global registers. Linux saves the high bits on
|
||||
context switches but not signals. */
|
||||
#define MASK_V8PLUS 0x2000000
|
||||
#define TARGET_V8PLUS (target_flags & MASK_V8PLUS)
|
||||
|
||||
/* See sparc.md */
|
||||
/* TARGET_HARD_MUL: Use hardware multiply instructions but not %y.
|
||||
TARGET_HARD_MUL32: Use hardware multiply instructions with rd %y
|
||||
to get high 32 bits. False in V8+ or V9 because multiply stores
|
||||
a 64 bit result in a register. */
|
||||
|
||||
#define TARGET_HARD_MUL32 \
|
||||
((TARGET_V8 || TARGET_SPARCLITE \
|
||||
|| TARGET_SPARCLET || TARGET_DEPRECATED_V8_INSNS) \
|
||||
@ -495,6 +498,8 @@ extern int target_flags;
|
||||
{"no-app-regs", -MASK_APP_REGS}, \
|
||||
{"hard-quad-float", MASK_HARD_QUAD}, \
|
||||
{"soft-quad-float", -MASK_HARD_QUAD}, \
|
||||
{"v8plus", MASK_V8PLUS}, \
|
||||
{"no-v8plus", -MASK_V8PLUS}, \
|
||||
{"vis", MASK_VIS}, \
|
||||
/* ??? These are deprecated, coerced to -mcpu=. Delete in 2.9. */ \
|
||||
{"cypress", 0}, \
|
||||
@ -502,7 +507,6 @@ extern int target_flags;
|
||||
{"f930", 0}, \
|
||||
{"f934", 0}, \
|
||||
{"v8", 0}, \
|
||||
{"v8plus", 0}, \
|
||||
{"supersparc", 0}, \
|
||||
/* End of deprecated options. */ \
|
||||
/* -mptrNN exists for *experimental* purposes. */ \
|
||||
@ -535,7 +539,6 @@ enum processor_type {
|
||||
PROCESSOR_F934,
|
||||
PROCESSOR_SPARCLET,
|
||||
PROCESSOR_TSC701,
|
||||
PROCESSOR_V8PLUS,
|
||||
PROCESSOR_V9,
|
||||
PROCESSOR_ULTRASPARC
|
||||
};
|
||||
@ -977,6 +980,12 @@ while (0)
|
||||
: (GET_MODE_SIZE (MODE) + 3) / 4) \
|
||||
: ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
|
||||
|
||||
/* A subreg in 64 bit mode will have the wrong offset for a floating point
|
||||
register. The least significant part is at offset 1, compared to 0 for
|
||||
integer registers. */
|
||||
#define ALTER_HARD_SUBREG(TMODE, WORD, FMODE, REGNO) \
|
||||
(TARGET_ARCH64 && (REGNO) >= 32 && (REGNO) < 96 && (TMODE) == SImode ? 1 : ((REGNO) + (WORD)))
|
||||
|
||||
/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
|
||||
See sparc.c for how we initialize this. */
|
||||
extern int *hard_regno_mode_classes;
|
||||
@ -1093,14 +1102,14 @@ extern int sparc_mode_class[];
|
||||
#define STRUCT_VALUE \
|
||||
(TARGET_ARCH64 \
|
||||
? 0 \
|
||||
: gen_rtx (MEM, Pmode, \
|
||||
gen_rtx (PLUS, Pmode, stack_pointer_rtx, \
|
||||
: gen_rtx_MEM (Pmode, \
|
||||
gen_rtx_PLUS (Pmode, stack_pointer_rtx, \
|
||||
GEN_INT (STRUCT_VALUE_OFFSET))))
|
||||
#define STRUCT_VALUE_INCOMING \
|
||||
(TARGET_ARCH64 \
|
||||
? 0 \
|
||||
: gen_rtx (MEM, Pmode, \
|
||||
gen_rtx (PLUS, Pmode, frame_pointer_rtx, \
|
||||
: gen_rtx_MEM (Pmode, \
|
||||
gen_rtx_PLUS (Pmode, frame_pointer_rtx, \
|
||||
GEN_INT (STRUCT_VALUE_OFFSET))))
|
||||
|
||||
/* Define the classes of registers for register constraints in the
|
||||
@ -1157,8 +1166,8 @@ extern int sparc_mode_class[];
|
||||
??? Should %fcc[0123] be handled similarly?
|
||||
*/
|
||||
|
||||
enum reg_class { NO_REGS, FPCC_REGS, GENERAL_REGS, FP_REGS, EXTRA_FP_REGS,
|
||||
GENERAL_OR_FP_REGS, GENERAL_OR_EXTRA_FP_REGS,
|
||||
enum reg_class { NO_REGS, FPCC_REGS, I64_REGS, GENERAL_REGS, FP_REGS,
|
||||
EXTRA_FP_REGS, GENERAL_OR_FP_REGS, GENERAL_OR_EXTRA_FP_REGS,
|
||||
ALL_REGS, LIM_REG_CLASSES };
|
||||
|
||||
#define N_REG_CLASSES (int) LIM_REG_CLASSES
|
||||
@ -1166,15 +1175,16 @@ enum reg_class { NO_REGS, FPCC_REGS, GENERAL_REGS, FP_REGS, EXTRA_FP_REGS,
|
||||
/* Give names of register classes as strings for dump file. */
|
||||
|
||||
#define REG_CLASS_NAMES \
|
||||
{ "NO_REGS", "FPCC_REGS", "GENERAL_REGS", "FP_REGS", "EXTRA_FP_REGS", \
|
||||
"GENERAL_OR_FP_REGS", "GENERAL_OR_EXTRA_FP_REGS", "ALL_REGS" }
|
||||
{ "NO_REGS", "FPCC_REGS", "I64_REGS", "GENERAL_REGS", "FP_REGS", \
|
||||
"EXTRA_FP_REGS", "GENERAL_OR_FP_REGS", "GENERAL_OR_EXTRA_FP_REGS", \
|
||||
"ALL_REGS" }
|
||||
|
||||
/* Define which registers fit in which classes.
|
||||
This is an initializer for a vector of HARD_REG_SET
|
||||
of length N_REG_CLASSES. */
|
||||
|
||||
#define REG_CLASS_CONTENTS \
|
||||
{{0, 0, 0, 0}, {0, 0, 0, 0xf}, \
|
||||
{{0, 0, 0, 0}, {0, 0, 0, 0xf}, {0xffff, 0, 0, 0}, \
|
||||
{-1, 0, 0, 0}, {0, -1, 0, 0}, {0, -1, -1, 0}, \
|
||||
{-1, -1, 0, 0}, {-1, -1, -1, 0}, {-1, -1, -1, 0x1f}}
|
||||
|
||||
@ -1266,15 +1276,18 @@ extern char leaf_reg_remap[];
|
||||
/* Get reg_class from a letter such as appears in the machine description.
|
||||
In the not-v9 case, coerce v9's 'e' class to 'f', so we can use 'e' in the
|
||||
.md file for v8 and v9.
|
||||
Use 'd' and 'b' for single precision VIS operations if TARGET_VIS. */
|
||||
'd' and 'b' are used for single and double precision VIS operations,
|
||||
if TARGET_VIS.
|
||||
'h' is used for V8+ 64 bit global and out registers. */
|
||||
|
||||
#define REG_CLASS_FROM_LETTER(C) \
|
||||
(TARGET_V9 \
|
||||
? ((C) == 'f' ? FP_REGS \
|
||||
: (C) == 'e' ? EXTRA_FP_REGS \
|
||||
: (C) == 'c' ? FPCC_REGS \
|
||||
: ((C) == 'd' && TARGET_VIS) ? FP_REGS \
|
||||
: ((C) == 'b' && TARGET_VIS) ? FP_REGS \
|
||||
: ((C) == 'd' && TARGET_VIS) ? FP_REGS\
|
||||
: ((C) == 'b' && TARGET_VIS) ? EXTRA_FP_REGS\
|
||||
: ((C) == 'h' && TARGET_V8PLUS) ? I64_REGS\
|
||||
: NO_REGS) \
|
||||
: ((C) == 'f' ? FP_REGS \
|
||||
: (C) == 'e' ? FP_REGS \
|
||||
@ -1299,6 +1312,8 @@ extern char leaf_reg_remap[];
|
||||
/* 10 and 11 bit immediates are only used for a few specific insns.
|
||||
SMALL_INT is used throughout the port so we continue to use it. */
|
||||
#define SMALL_INT(X) (SPARC_SIMM13_P (INTVAL (X)))
|
||||
/* 13 bit immediate, considering only the low 32 bits */
|
||||
#define SMALL_INT32(X) (SPARC_SIMM13_P ((int)INTVAL (X) & 0xffffffff))
|
||||
#define SPARC_SETHI_P(X) \
|
||||
(((unsigned HOST_WIDE_INT) (X) & ~(unsigned HOST_WIDE_INT) 0xfffffc00) == 0)
|
||||
|
||||
@ -1366,7 +1381,7 @@ extern char leaf_reg_remap[];
|
||||
#define SECONDARY_MEMORY_NEEDED_RTX(MODE) \
|
||||
(get_frame_size () == 0 \
|
||||
? assign_stack_local (MODE, GET_MODE_SIZE (MODE), 0) \
|
||||
: gen_rtx (MEM, MODE, gen_rtx (PLUS, Pmode, frame_pointer_rtx, \
|
||||
: gen_rtx_MEM (MODE, gen_rtx_PLUS (Pmode, frame_pointer_rtx, \
|
||||
GEN_INT (STARTING_FRAME_OFFSET))))
|
||||
|
||||
/* Get_secondary_mem widens it's argument to BITS_PER_WORD which loses on v9
|
||||
@ -1501,18 +1516,18 @@ extern char leaf_reg_remap[];
|
||||
/* On SPARC the value is found in the first "output" register. */
|
||||
|
||||
#define FUNCTION_VALUE(VALTYPE, FUNC) \
|
||||
gen_rtx (REG, TYPE_MODE (VALTYPE), BASE_RETURN_VALUE_REG (TYPE_MODE (VALTYPE)))
|
||||
gen_rtx_REG (TYPE_MODE (VALTYPE), BASE_RETURN_VALUE_REG (TYPE_MODE (VALTYPE)))
|
||||
|
||||
/* But the called function leaves it in the first "input" register. */
|
||||
|
||||
#define FUNCTION_OUTGOING_VALUE(VALTYPE, FUNC) \
|
||||
gen_rtx (REG, TYPE_MODE (VALTYPE), BASE_OUTGOING_VALUE_REG (TYPE_MODE (VALTYPE)))
|
||||
gen_rtx_REG (TYPE_MODE (VALTYPE), BASE_OUTGOING_VALUE_REG (TYPE_MODE (VALTYPE)))
|
||||
|
||||
/* Define how to find the value returned by a library function
|
||||
assuming the value has mode MODE. */
|
||||
|
||||
#define LIBCALL_VALUE(MODE) \
|
||||
gen_rtx (REG, MODE, BASE_RETURN_VALUE_REG (MODE))
|
||||
gen_rtx_REG (MODE, BASE_RETURN_VALUE_REG (MODE))
|
||||
|
||||
/* 1 if N is a possible register number for a function value
|
||||
as seen by the caller.
|
||||
@ -1615,7 +1630,7 @@ function_arg_pass_by_reference (& (CUM), (MODE), (TYPE), (NAMED))
|
||||
to pad out an argument with extra space. The value should be of type
|
||||
`enum direction': either `upward' to pad above the argument,
|
||||
`downward' to pad below, or `none' to inhibit padding. */
|
||||
extern enum direction function_arg_padding ();
|
||||
|
||||
#define FUNCTION_ARG_PADDING(MODE, TYPE) \
|
||||
function_arg_padding ((MODE), (TYPE))
|
||||
|
||||
@ -1630,17 +1645,6 @@ function_arg_padding ((MODE), (TYPE))
|
||||
|| ((TYPE) && TYPE_ALIGN (TYPE) == 128))) \
|
||||
? 128 : PARM_BOUNDARY)
|
||||
|
||||
/* Initialize data used by insn expanders. This is called from
|
||||
init_emit, once for each function, before code is generated.
|
||||
For v9, clear the temp slot used by float/int DImode conversions.
|
||||
??? There is the 16 bytes at [%fp-16], however we'd like to delete this
|
||||
space at some point.
|
||||
??? Use assign_stack_temp? */
|
||||
|
||||
extern void sparc_init_expanders ();
|
||||
extern struct rtx_def *sparc64_fpconv_stack_temp ();
|
||||
#define INIT_EXPANDERS sparc_init_expanders ()
|
||||
|
||||
/* Define the information needed to generate branch and scc insns. This is
|
||||
stored from the compare operation. Note that we can't use "rtx" here
|
||||
since it hasn't been defined! */
|
||||
@ -1691,8 +1695,8 @@ do { \
|
||||
|
||||
extern int leaf_function;
|
||||
#define FUNCTION_PROLOGUE(FILE, SIZE) \
|
||||
(TARGET_FLAT ? sparc_flat_output_function_prologue (FILE, SIZE) \
|
||||
: output_function_prologue (FILE, SIZE, leaf_function))
|
||||
(TARGET_FLAT ? sparc_flat_output_function_prologue (FILE, (int)SIZE) \
|
||||
: output_function_prologue (FILE, (int)SIZE, leaf_function))
|
||||
|
||||
/* Output assembler code to FILE to increment profiler label # LABELNO
|
||||
for profiling a function entry.
|
||||
@ -2070,8 +2074,8 @@ extern int current_function_outgoing_args_size;
|
||||
extern union tree_node *current_function_decl;
|
||||
|
||||
#define FUNCTION_EPILOGUE(FILE, SIZE) \
|
||||
(TARGET_FLAT ? sparc_flat_output_function_epilogue (FILE, SIZE) \
|
||||
: output_function_epilogue (FILE, SIZE, leaf_function))
|
||||
(TARGET_FLAT ? sparc_flat_output_function_epilogue (FILE, (int)SIZE) \
|
||||
: output_function_epilogue (FILE, (int)SIZE, leaf_function))
|
||||
|
||||
#define DELAY_SLOTS_FOR_EPILOGUE \
|
||||
(TARGET_FLAT ? sparc_flat_epilogue_delay_slots () : 1)
|
||||
@ -2120,11 +2124,11 @@ do { \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
ASM_OUTPUT_INT (FILE, GEN_INT (0x00000000)); \
|
||||
ASM_OUTPUT_INT (FILE, GEN_INT (0x00000000)); \
|
||||
ASM_OUTPUT_INT (FILE, GEN_INT (0x00000000)); \
|
||||
ASM_OUTPUT_INT (FILE, const0_rtx); \
|
||||
ASM_OUTPUT_INT (FILE, const0_rtx); \
|
||||
ASM_OUTPUT_INT (FILE, const0_rtx); \
|
||||
ASM_OUTPUT_INT (FILE, GEN_INT (0x81C04000)); \
|
||||
ASM_OUTPUT_INT (FILE, GEN_INT (0x00000000)); \
|
||||
ASM_OUTPUT_INT (FILE, const0_rtx); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@ -2175,7 +2179,7 @@ extern struct rtx_def *sparc_builtin_saveregs ();
|
||||
that holds the dynamic chain--the previous frame's address.
|
||||
??? -mflat support? */
|
||||
#define DYNAMIC_CHAIN_ADDRESS(frame) \
|
||||
gen_rtx (PLUS, Pmode, frame, GEN_INT (14 * UNITS_PER_WORD))
|
||||
gen_rtx_PLUS (Pmode, frame, GEN_INT (14 * UNITS_PER_WORD))
|
||||
|
||||
/* The return address isn't on the stack, it is in a register, so we can't
|
||||
access it from the current frame pointer. We can access it from the
|
||||
@ -2194,8 +2198,8 @@ extern struct rtx_def *sparc_builtin_saveregs ();
|
||||
returns, and +12 for structure returns. */
|
||||
#define RETURN_ADDR_RTX(count, frame) \
|
||||
((count == -1) \
|
||||
? gen_rtx (REG, Pmode, 31) \
|
||||
: gen_rtx (MEM, Pmode, \
|
||||
? gen_rtx_REG (Pmode, 31) \
|
||||
: gen_rtx_MEM (Pmode, \
|
||||
memory_address (Pmode, plus_constant (frame, 15 * UNITS_PER_WORD))))
|
||||
|
||||
/* Before the prologue, the return address is %o7 + 8. OK, sometimes it's
|
||||
@ -2203,7 +2207,7 @@ extern struct rtx_def *sparc_builtin_saveregs ();
|
||||
Actually, just using %o7 is close enough for unwinding, but %o7+8
|
||||
is something you can return to. */
|
||||
#define INCOMING_RETURN_ADDR_RTX \
|
||||
gen_rtx (PLUS, word_mode, gen_rtx (REG, word_mode, 15), GEN_INT (8))
|
||||
gen_rtx_PLUS (word_mode, gen_rtx_REG (word_mode, 15), GEN_INT (8))
|
||||
|
||||
/* The offset from the incoming value of %sp to the top of the stack frame
|
||||
for the current function. On sparc64, we have to account for the stack
|
||||
@ -2250,6 +2254,9 @@ extern struct rtx_def *sparc_builtin_saveregs ();
|
||||
/* 1 if X is an fp register. */
|
||||
|
||||
#define FP_REG_P(X) (REG_P (X) && REGNO_OK_FOR_FP_P (REGNO (X)))
|
||||
|
||||
/* Is X, a REG, an in or global register? i.e. is regno 0..7 or 24..31 */
|
||||
#define IN_OR_GLOBAL_P(X) (REGNO (X) < 8 || (REGNO (X) >= 24 && REGNO (X) <= 31))
|
||||
|
||||
/* Maximum number of registers that can appear in a valid memory address. */
|
||||
|
||||
@ -2439,30 +2446,30 @@ extern struct rtx_def *legitimize_pic_address ();
|
||||
#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \
|
||||
{ rtx sparc_x = (X); \
|
||||
if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == MULT) \
|
||||
(X) = gen_rtx (PLUS, Pmode, XEXP (X, 1), \
|
||||
(X) = gen_rtx_PLUS (Pmode, XEXP (X, 1), \
|
||||
force_operand (XEXP (X, 0), NULL_RTX)); \
|
||||
if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == MULT) \
|
||||
(X) = gen_rtx (PLUS, Pmode, XEXP (X, 0), \
|
||||
(X) = gen_rtx_PLUS (Pmode, XEXP (X, 0), \
|
||||
force_operand (XEXP (X, 1), NULL_RTX)); \
|
||||
if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == PLUS) \
|
||||
(X) = gen_rtx (PLUS, Pmode, force_operand (XEXP (X, 0), NULL_RTX),\
|
||||
(X) = gen_rtx_PLUS (Pmode, force_operand (XEXP (X, 0), NULL_RTX),\
|
||||
XEXP (X, 1)); \
|
||||
if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == PLUS) \
|
||||
(X) = gen_rtx (PLUS, Pmode, XEXP (X, 0), \
|
||||
(X) = gen_rtx_PLUS (Pmode, XEXP (X, 0), \
|
||||
force_operand (XEXP (X, 1), NULL_RTX)); \
|
||||
if (sparc_x != (X) && memory_address_p (MODE, X)) \
|
||||
goto WIN; \
|
||||
if (flag_pic) (X) = legitimize_pic_address (X, MODE, 0); \
|
||||
else if (GET_CODE (X) == PLUS && CONSTANT_ADDRESS_P (XEXP (X, 1))) \
|
||||
(X) = gen_rtx (PLUS, Pmode, XEXP (X, 0), \
|
||||
(X) = gen_rtx_PLUS (Pmode, XEXP (X, 0), \
|
||||
copy_to_mode_reg (Pmode, XEXP (X, 1))); \
|
||||
else if (GET_CODE (X) == PLUS && CONSTANT_ADDRESS_P (XEXP (X, 0))) \
|
||||
(X) = gen_rtx (PLUS, Pmode, XEXP (X, 1), \
|
||||
(X) = gen_rtx_PLUS (Pmode, XEXP (X, 1), \
|
||||
copy_to_mode_reg (Pmode, XEXP (X, 0))); \
|
||||
else if (GET_CODE (X) == SYMBOL_REF || GET_CODE (X) == CONST \
|
||||
|| GET_CODE (X) == LABEL_REF) \
|
||||
(X) = gen_rtx (LO_SUM, Pmode, \
|
||||
copy_to_mode_reg (Pmode, gen_rtx (HIGH, Pmode, X)), X); \
|
||||
(X) = gen_rtx_LO_SUM (Pmode, \
|
||||
copy_to_mode_reg (Pmode, gen_rtx_HIGH (Pmode, X)), X); \
|
||||
if (memory_address_p (MODE, X)) \
|
||||
goto WIN; }
|
||||
|
||||
@ -2512,7 +2519,7 @@ extern struct rtx_def *legitimize_pic_address ();
|
||||
|
||||
/* This is how to refer to the variable errno. */
|
||||
#define GEN_ERRNO_RTX \
|
||||
gen_rtx (MEM, SImode, gen_rtx (SYMBOL_REF, Pmode, "errno"))
|
||||
gen_rtx_MEM (SImode, gen_rtx_SYMBOL_REF (Pmode, "errno"))
|
||||
#endif /* 0 */
|
||||
|
||||
/* Define if operations between registers always perform the operation
|
||||
@ -2585,7 +2592,7 @@ extern struct rtx_def *legitimize_pic_address ();
|
||||
: ((GET_CODE (X) == PLUS || GET_CODE (X) == MINUS \
|
||||
|| GET_CODE (X) == NEG || GET_CODE (X) == ASHIFT) \
|
||||
? (TARGET_ARCH64 && GET_MODE (X) == DImode ? CCX_NOOVmode : CC_NOOVmode) \
|
||||
: (TARGET_ARCH64 && GET_MODE (X) == DImode ? CCXmode : CCmode)))
|
||||
: ((TARGET_ARCH64 || TARGET_V8PLUS) && GET_MODE (X) == DImode ? CCXmode : CCmode)))
|
||||
|
||||
/* Return non-zero if SELECT_CC_MODE will never return MODE for a
|
||||
floating point inequality comparison. */
|
||||
@ -2645,32 +2652,32 @@ extern struct rtx_def *legitimize_pic_address ();
|
||||
#define INIT_TARGET_OPTABS \
|
||||
do { \
|
||||
add_optab->handlers[(int) TFmode].libfunc \
|
||||
= gen_rtx (SYMBOL_REF, Pmode, ADDTF3_LIBCALL); \
|
||||
= gen_rtx_SYMBOL_REF (Pmode, ADDTF3_LIBCALL); \
|
||||
sub_optab->handlers[(int) TFmode].libfunc \
|
||||
= gen_rtx (SYMBOL_REF, Pmode, SUBTF3_LIBCALL); \
|
||||
= gen_rtx_SYMBOL_REF (Pmode, SUBTF3_LIBCALL); \
|
||||
neg_optab->handlers[(int) TFmode].libfunc \
|
||||
= gen_rtx (SYMBOL_REF, Pmode, NEGTF2_LIBCALL); \
|
||||
= gen_rtx_SYMBOL_REF (Pmode, NEGTF2_LIBCALL); \
|
||||
smul_optab->handlers[(int) TFmode].libfunc \
|
||||
= gen_rtx (SYMBOL_REF, Pmode, MULTF3_LIBCALL); \
|
||||
= gen_rtx_SYMBOL_REF (Pmode, MULTF3_LIBCALL); \
|
||||
flodiv_optab->handlers[(int) TFmode].libfunc \
|
||||
= gen_rtx (SYMBOL_REF, Pmode, DIVTF3_LIBCALL); \
|
||||
eqtf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, EQTF2_LIBCALL); \
|
||||
netf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, NETF2_LIBCALL); \
|
||||
gttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, GTTF2_LIBCALL); \
|
||||
getf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, GETF2_LIBCALL); \
|
||||
lttf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, LTTF2_LIBCALL); \
|
||||
letf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, LETF2_LIBCALL); \
|
||||
trunctfsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, TRUNCTFSF2_LIBCALL); \
|
||||
trunctfdf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, TRUNCTFDF2_LIBCALL); \
|
||||
extendsftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, EXTENDSFTF2_LIBCALL); \
|
||||
extenddftf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, EXTENDDFTF2_LIBCALL); \
|
||||
floatsitf_libfunc = gen_rtx (SYMBOL_REF, Pmode, FLOATSITF2_LIBCALL); \
|
||||
fixtfsi_libfunc = gen_rtx (SYMBOL_REF, Pmode, FIX_TRUNCTFSI2_LIBCALL); \
|
||||
= gen_rtx_SYMBOL_REF (Pmode, DIVTF3_LIBCALL); \
|
||||
eqtf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, EQTF2_LIBCALL); \
|
||||
netf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, NETF2_LIBCALL); \
|
||||
gttf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, GTTF2_LIBCALL); \
|
||||
getf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, GETF2_LIBCALL); \
|
||||
lttf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, LTTF2_LIBCALL); \
|
||||
letf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, LETF2_LIBCALL); \
|
||||
trunctfsf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, TRUNCTFSF2_LIBCALL); \
|
||||
trunctfdf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, TRUNCTFDF2_LIBCALL); \
|
||||
extendsftf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, EXTENDSFTF2_LIBCALL); \
|
||||
extenddftf2_libfunc = gen_rtx_SYMBOL_REF (Pmode, EXTENDDFTF2_LIBCALL); \
|
||||
floatsitf_libfunc = gen_rtx_SYMBOL_REF (Pmode, FLOATSITF2_LIBCALL); \
|
||||
fixtfsi_libfunc = gen_rtx_SYMBOL_REF (Pmode, FIX_TRUNCTFSI2_LIBCALL); \
|
||||
fixunstfsi_libfunc \
|
||||
= gen_rtx (SYMBOL_REF, Pmode, FIXUNS_TRUNCTFSI2_LIBCALL); \
|
||||
= gen_rtx_SYMBOL_REF (Pmode, FIXUNS_TRUNCTFSI2_LIBCALL); \
|
||||
if (TARGET_FPU) \
|
||||
sqrt_optab->handlers[(int) TFmode].libfunc \
|
||||
= gen_rtx (SYMBOL_REF, Pmode, "_Q_sqrt"); \
|
||||
= gen_rtx_SYMBOL_REF (Pmode, "_Q_sqrt"); \
|
||||
INIT_SUBTARGET_OPTABS; \
|
||||
} while (0)
|
||||
|
||||
@ -2709,12 +2716,12 @@ extern struct rtx_def *legitimize_pic_address ();
|
||||
|
||||
/* Compute extra cost of moving data between one register class
|
||||
and another. */
|
||||
#define GENERAL_OR_I64(C) ((C) == GENERAL_REGS || (C) == I64_REGS)
|
||||
#define REGISTER_MOVE_COST(CLASS1, CLASS2) \
|
||||
(((FP_REG_CLASS_P (CLASS1) && (CLASS2) == GENERAL_REGS) \
|
||||
|| ((CLASS1) == GENERAL_REGS && FP_REG_CLASS_P (CLASS2)) \
|
||||
(((FP_REG_CLASS_P (CLASS1) && GENERAL_OR_I64 (CLASS2)) \
|
||||
|| (GENERAL_OR_I64 (CLASS1) && FP_REG_CLASS_P (CLASS2)) \
|
||||
|| (CLASS1) == FPCC_REGS || (CLASS2) == FPCC_REGS) \
|
||||
? (sparc_cpu == PROCESSOR_ULTRASPARC ? 12 : 6) \
|
||||
: 2)
|
||||
? (sparc_cpu == PROCESSOR_ULTRASPARC ? 12 : 6) : 2)
|
||||
|
||||
/* Provide the costs of a rtl expression. This is in the body of a
|
||||
switch on CODE. The purpose for the cost of MULT is to encourage
|
||||
@ -2741,20 +2748,17 @@ extern struct rtx_def *legitimize_pic_address ();
|
||||
|
||||
/* Adjust the cost of dependencies. */
|
||||
#define ADJUST_COST(INSN,LINK,DEP,COST) \
|
||||
do { \
|
||||
if (sparc_cpu == PROCESSOR_SUPERSPARC) \
|
||||
(COST) = supersparc_adjust_cost (INSN, LINK, DEP, COST); \
|
||||
else if (sparc_cpu == PROCESSOR_ULTRASPARC) \
|
||||
(COST) = ultrasparc_adjust_cost (INSN, LINK, DEP, COST); \
|
||||
} while (0)
|
||||
else
|
||||
|
||||
/* Conditional branches with empty delay slots have a length of two. */
|
||||
#define ADJUST_INSN_LENGTH(INSN, LENGTH) \
|
||||
do { \
|
||||
if (GET_CODE (INSN) == CALL_INSN \
|
||||
|| (GET_CODE (INSN) == JUMP_INSN && ! simplejump_p (insn))) \
|
||||
LENGTH += 1; \
|
||||
} while (0)
|
||||
LENGTH += 1; else
|
||||
|
||||
/* Control the assembler format that we output. */
|
||||
|
||||
@ -3252,6 +3256,16 @@ extern int v9_regcmp_p ();
|
||||
extern unsigned long sparc_flat_compute_frame_size ();
|
||||
extern unsigned long sparc_type_code ();
|
||||
|
||||
extern char *sparc_v8plus_shift ();
|
||||
|
||||
#ifdef __STDC__
|
||||
/* Function used for V8+ code generation. Returns 1 if the high
|
||||
32 bits of REG are 0 before INSN. */
|
||||
extern int sparc_check_64 (struct rtx_def *, struct rtx_def *);
|
||||
extern int sparc_return_peephole_ok (struct rtx_def *, struct rtx_def *);
|
||||
extern int compute_frame_size (int, int);
|
||||
#endif
|
||||
|
||||
/* Defined in flags.h, but insn-emit.c does not include flags.h. */
|
||||
|
||||
extern int flag_pic;
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user