mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-19 12:21:15 +08:00
Support for official Sparc V9 ABI:
* sparc.c (sparc_override_options): Force stack bias off for !arch64. Care for flag_pcc_struct_return default. (output_move_quad): Rewrite to move by halves on v9 and in the proper direction. (move_quad_direction): New function. (output_fp_move_quad): Use it to determine the direction of copy. (function_arg_slotno): Return -1 for FP reg overflow as well. (function_arg_record_value*): New functions. (function_arg): Use them. Streamline unprototyped parameter passing. (function_arg_pass_by_reference): Pass TCmode by reference. (function_value): New function. * sparc.h (PTRDIFF_TYPE, SIZE_TYPE): For -pedantic's sake, don't use long long in 64-bit mode. (RETURN_IN_MEMORY): v9 returns structs < 32-bytes in regs. (DEFAULT_PCC_STRUCT_RETURN): Make the default detectable. (BASE_RETURN_VALUE_REG): Consider complex float types for arch64. (BASE_OUTGOING_VALUE_REG, BASE_PASSING_ARG_REG): Likewise. (BASE_INCOMING_ARG_REG): Likewise. (FUNCTION_VALUE): Call function_value. (FUNCTION_OUTGOING_VALUE, LIBCALL_VALUE): Likewise. * sparc.md (movdi_sp32_v9): Disable for arch64. (movsf, movdf, movtf): Sort all ulternatives using fp regs first. (call_value_address_sp64): Remove register class constraints. (call_value_symbolic_sp64): Likewise. (nonlocal_goto): Pass label reg directly to goto_handlers. Constrain v9 case to 32-bit constants. (goto_handler_and_restore_v9): Provide a version for arch64. * sparc/linux64.h (SIZE_TYPE, PTRDIFF_TYPE): Remove private definition. * sparc/sp64-aout.h (TARGET_DEFAULT): Turn on stack bias. (CPP_PREDEFINES): New. * sparc/sp64-elf.h: Likewise. (PREFERRED_DEBUGGING_TYPE): Dwarf2. (ASM_OUTPUT_DWARF2_ADDR_CONST): New. * sparc/sysv4.h (SIZE_TYPE, PTRDIFF_TYPE): Undo svr4.h's changes. From-SVN: r19526
This commit is contained in:
parent
959d87966a
commit
82d6b402bb
@ -1,3 +1,41 @@
|
||||
Sun May 3 13:51:34 PDT 1998 Richard Henerson <rth@cygnus.com>
|
||||
|
||||
Support for official Sparc V9 ABI:
|
||||
* sparc.c (sparc_override_options): Force stack bias off for !arch64.
|
||||
Care for flag_pcc_struct_return default.
|
||||
(output_move_quad): Rewrite to move by halves on v9 and in the
|
||||
proper direction.
|
||||
(move_quad_direction): New function.
|
||||
(output_fp_move_quad): Use it to determine the direction of copy.
|
||||
(function_arg_slotno): Return -1 for FP reg overflow as well.
|
||||
(function_arg_record_value*): New functions.
|
||||
(function_arg): Use them. Streamline unprototyped parameter passing.
|
||||
(function_arg_pass_by_reference): Pass TCmode by reference.
|
||||
(function_value): New function.
|
||||
* sparc.h (PTRDIFF_TYPE, SIZE_TYPE): For -pedantic's sake, don't use
|
||||
long long in 64-bit mode.
|
||||
(RETURN_IN_MEMORY): v9 returns structs < 32-bytes in regs.
|
||||
(DEFAULT_PCC_STRUCT_RETURN): Make the default detectable.
|
||||
(BASE_RETURN_VALUE_REG): Consider complex float types for arch64.
|
||||
(BASE_OUTGOING_VALUE_REG, BASE_PASSING_ARG_REG): Likewise.
|
||||
(BASE_INCOMING_ARG_REG): Likewise.
|
||||
(FUNCTION_VALUE): Call function_value.
|
||||
(FUNCTION_OUTGOING_VALUE, LIBCALL_VALUE): Likewise.
|
||||
* sparc.md (movdi_sp32_v9): Disable for arch64.
|
||||
(movsf, movdf, movtf): Sort all ulternatives using fp regs first.
|
||||
(call_value_address_sp64): Remove register class constraints.
|
||||
(call_value_symbolic_sp64): Likewise.
|
||||
(nonlocal_goto): Pass label reg directly to goto_handlers. Constrain
|
||||
v9 case to 32-bit constants.
|
||||
(goto_handler_and_restore_v9): Provide a version for arch64.
|
||||
* sparc/linux64.h (SIZE_TYPE, PTRDIFF_TYPE): Remove private definition.
|
||||
* sparc/sp64-aout.h (TARGET_DEFAULT): Turn on stack bias.
|
||||
(CPP_PREDEFINES): New.
|
||||
* sparc/sp64-elf.h: Likewise.
|
||||
(PREFERRED_DEBUGGING_TYPE): Dwarf2.
|
||||
(ASM_OUTPUT_DWARF2_ADDR_CONST): New.
|
||||
* sparc/sysv4.h (SIZE_TYPE, PTRDIFF_TYPE): Undo svr4.h's changes.
|
||||
|
||||
Sat May 2 17:47:17 PDT 1998 Jeff Law (law@cygnus.com)
|
||||
|
||||
* version.c: Bump for snapshot.
|
||||
|
@ -84,12 +84,6 @@ Boston, MA 02111-1307, USA. */
|
||||
#undef SPARC_DEFAULT_CMODEL
|
||||
#define SPARC_DEFAULT_CMODEL CM_MEDANY
|
||||
|
||||
#undef SIZE_TYPE
|
||||
#define SIZE_TYPE "long long unsigned int"
|
||||
|
||||
#undef PTRDIFF_TYPE
|
||||
#define PTRDIFF_TYPE "long long int"
|
||||
|
||||
#undef WCHAR_TYPE
|
||||
#define WCHAR_TYPE "long int"
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Definitions of target machine for GNU compiler, for SPARC64, a.out.
|
||||
Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
|
||||
Copyright (C) 1994, 1996, 1997, 1998 Free Software Foundation, Inc.
|
||||
Contributed by Doug Evans, dje@cygnus.com.
|
||||
|
||||
This file is part of GNU CC.
|
||||
@ -28,8 +28,11 @@ Boston, MA 02111-1307, USA. */
|
||||
#undef TARGET_DEFAULT
|
||||
#define TARGET_DEFAULT \
|
||||
(MASK_V9 + MASK_PTR64 + MASK_64BIT + MASK_HARD_QUAD \
|
||||
+ MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU)
|
||||
+ MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU + MASK_STACK_BIAS)
|
||||
|
||||
/* The only code model supported is Medium/Low. */
|
||||
#undef SPARC_DEFAULT_CMODEL
|
||||
#define SPARC_DEFAULT_CMODEL CM_MEDLOW
|
||||
|
||||
#undef CPP_PREDEFINES
|
||||
#define CPP_PREDEFINES "-Dsparc -Acpu(sparc) -Amachine(sparc)"
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Definitions of target machine for GNU compiler, for SPARC64, ELF.
|
||||
Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
|
||||
Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
|
||||
Contributed by Doug Evans, dje@cygnus.com.
|
||||
|
||||
This file is part of GNU CC.
|
||||
@ -29,19 +29,19 @@ Boston, MA 02111-1307, USA. */
|
||||
#undef TARGET_VERSION
|
||||
#define TARGET_VERSION fprintf (stderr, " (sparc64-elf)")
|
||||
|
||||
/* A 64 bit v9 compiler without stack-bias,
|
||||
in a Medium/Anywhere code model environment.
|
||||
There is no stack bias as this configuration is intended for
|
||||
embedded systems. */
|
||||
/* A 64 bit v9 compiler in a Medium/Anywhere code model environment. */
|
||||
|
||||
#undef TARGET_DEFAULT
|
||||
#define TARGET_DEFAULT \
|
||||
(MASK_V9 + MASK_PTR64 + MASK_64BIT + MASK_HARD_QUAD \
|
||||
+ MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU)
|
||||
+ MASK_APP_REGS + MASK_EPILOGUE + MASK_FPU + MASK_STACK_BIAS)
|
||||
|
||||
#undef SPARC_DEFAULT_CMODEL
|
||||
#define SPARC_DEFAULT_CMODEL CM_EMBMEDANY
|
||||
|
||||
#undef CPP_PREDEFINES
|
||||
#define CPP_PREDEFINES "-Dsparc -D__ELF__ -Acpu(sparc) -Amachine(sparc)"
|
||||
|
||||
/* __svr4__ is used by the C library (FIXME) */
|
||||
#undef CPP_SUBTARGET_SPEC
|
||||
#define CPP_SUBTARGET_SPEC "-D__svr4__"
|
||||
@ -99,11 +99,6 @@ crtbegin.o%s \
|
||||
#undef LONG_DOUBLE_TYPE_SIZE
|
||||
#define LONG_DOUBLE_TYPE_SIZE 128
|
||||
|
||||
#undef PTRDIFF_TYPE
|
||||
#define PTRDIFF_TYPE "long long int"
|
||||
#undef SIZE_TYPE
|
||||
#define SIZE_TYPE "long long unsigned int"
|
||||
|
||||
/* The medium/anywhere code model practically requires us to put jump tables
|
||||
in the text section as gcc is unable to distinguish LABEL_REF's of jump
|
||||
tables from other label refs (when we need to). */
|
||||
@ -120,7 +115,7 @@ crtbegin.o%s \
|
||||
#define DBX_DEBUGGING_INFO
|
||||
|
||||
#undef PREFERRED_DEBUGGING_TYPE
|
||||
#define PREFERRED_DEBUGGING_TYPE DWARF_DEBUG
|
||||
#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
|
||||
|
||||
/* Stabs doesn't use this, and it confuses a simulator. */
|
||||
/* ??? Need to see what DWARF needs, if anything. */
|
||||
@ -150,6 +145,9 @@ do { \
|
||||
fputc ('\n', (FILE)); \
|
||||
} while (0)
|
||||
|
||||
#define ASM_OUTPUT_DWARF2_ADDR_CONST(FILE, ADDR) \
|
||||
fprintf ((FILE), "\t%s\t%s", UNALIGNED_LONGLONG_ASM_OP, (ADDR))
|
||||
|
||||
/* ??? Not sure if this should be 4 or 8 bytes. 4 works for now. */
|
||||
#define ASM_OUTPUT_DWARF_REF(FILE, LABEL) \
|
||||
do { \
|
||||
|
@ -280,6 +280,10 @@ sparc_override_options ()
|
||||
if (! TARGET_V9)
|
||||
target_flags &= ~MASK_V8PLUS;
|
||||
|
||||
/* Don't use stack biasing in 32 bit mode. */
|
||||
if (TARGET_ARCH32)
|
||||
target_flags &= ~MASK_STACK_BIAS;
|
||||
|
||||
/* Validate -malign-loops= value, or provide default. */
|
||||
if (sparc_align_loops_string)
|
||||
{
|
||||
@ -321,6 +325,10 @@ sparc_override_options ()
|
||||
else
|
||||
sparc_align_funcs = DEFAULT_SPARC_ALIGN_FUNCS;
|
||||
|
||||
/* Validate PCC_STRUCT_RETURN. */
|
||||
if (flag_pcc_struct_return == DEFAULT_PCC_STRUCT_RETURN)
|
||||
flag_pcc_struct_return = (TARGET_ARCH64 ? 0 : 1);
|
||||
|
||||
/* Do various machine dependent initializations. */
|
||||
sparc_init_modes ();
|
||||
}
|
||||
@ -2150,9 +2158,18 @@ output_move_quad (operands)
|
||||
register enum optype optype0;
|
||||
register enum optype optype1;
|
||||
rtx wordpart[4][2];
|
||||
rtx load_late[4];
|
||||
int load_late_half[2];
|
||||
rtx addreg0 = 0;
|
||||
rtx addreg1 = 0;
|
||||
|
||||
load_late_half[0] = 0; load_late_half[1] = 0;
|
||||
load_late[0] = 0; load_late[1] = 0; load_late[2] = 0;
|
||||
load_late[3] = 0;
|
||||
|
||||
wordpart[0][0] = NULL; wordpart[1][0] = NULL; wordpart[2][0] = NULL;
|
||||
wordpart[3][0] = NULL;
|
||||
|
||||
/* First classify both operands. */
|
||||
|
||||
if (REG_P (op0))
|
||||
@ -2183,6 +2200,72 @@ output_move_quad (operands)
|
||||
|| (optype0 == MEM && optype1 == MEM))
|
||||
abort ();
|
||||
|
||||
if (optype0 == REGOP)
|
||||
{
|
||||
wordpart[0][0] = gen_rtx (REG, word_mode, REGNO (op0) + 0);
|
||||
if (TARGET_ARCH64 && FP_REG_P (op0)
|
||||
&& REGNO (op0) < SPARC_FIRST_V9_FP_REG)
|
||||
wordpart[1][0] = gen_rtx (REG, word_mode, REGNO (op0) + 2);
|
||||
else
|
||||
wordpart[1][0] = gen_rtx (REG, word_mode, REGNO (op0) + 1);
|
||||
|
||||
if (TARGET_ARCH32)
|
||||
{
|
||||
wordpart[2][0] = gen_rtx (REG, word_mode, REGNO (op0) + 2);
|
||||
wordpart[3][0] = gen_rtx (REG, word_mode, REGNO (op0) + 3);
|
||||
}
|
||||
|
||||
/* Loading into a register which overlaps a register used in the
|
||||
address. */
|
||||
if (optype1 != REGOP && reg_overlap_mentioned_p (op0, op1))
|
||||
{
|
||||
int i;
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < 4 && wordpart[i][0] != NULL; i++)
|
||||
{
|
||||
if (reg_mentioned_p (wordpart[i][0], op1))
|
||||
{
|
||||
load_late[i] = wordpart[i][0];
|
||||
load_late_half[TARGET_ARCH64 ? i : i/2] = 1;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 2)
|
||||
{
|
||||
/* Not sure what to do here. Multiple adds? Can't happen. */
|
||||
abort ();
|
||||
}
|
||||
else if (count == 2)
|
||||
{
|
||||
/* We have a two-address source operand, and both registers
|
||||
overlap with the dest quad. Add them together and
|
||||
store the result into the last register of the quad being
|
||||
loaded, then generate an appropriate MEM insn. */
|
||||
rtx temp[3];
|
||||
int place = 0;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (load_late[i])
|
||||
{
|
||||
temp[place++] = load_late[i];
|
||||
load_late[i] = 0;
|
||||
}
|
||||
}
|
||||
temp[2] = wordpart[3][0];
|
||||
output_asm_insn ("add %0, %1, %2", temp);
|
||||
load_late_half[0] = 0;
|
||||
load_late_half[1] = 1;
|
||||
op1 = gen_rtx (MEM, TFmode, wordpart[3][0]);
|
||||
operands[1] = op1;
|
||||
optype1 = OFFSOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If an operand is an unoffsettable memory ref, find a register
|
||||
we can increment temporarily to make it refer to the later words. */
|
||||
|
||||
@ -2195,17 +2278,7 @@ output_move_quad (operands)
|
||||
/* Ok, we can do one word at a time.
|
||||
Set up in wordpart the operands to use for each word of the arguments. */
|
||||
|
||||
if (optype0 == REGOP)
|
||||
{
|
||||
wordpart[0][0] = gen_rtx (REG, word_mode, REGNO (op0) + 0);
|
||||
wordpart[1][0] = gen_rtx (REG, word_mode, REGNO (op0) + 1);
|
||||
if (TARGET_ARCH32)
|
||||
{
|
||||
wordpart[2][0] = gen_rtx (REG, word_mode, REGNO (op0) + 2);
|
||||
wordpart[3][0] = gen_rtx (REG, word_mode, REGNO (op0) + 3);
|
||||
}
|
||||
}
|
||||
else if (optype0 == OFFSOP)
|
||||
if (optype0 == OFFSOP)
|
||||
{
|
||||
wordpart[0][0] = adj_offsettable_operand (op0, 0);
|
||||
if (TARGET_ARCH32)
|
||||
@ -2217,7 +2290,7 @@ output_move_quad (operands)
|
||||
else
|
||||
wordpart[1][0] = adj_offsettable_operand (op0, 8);
|
||||
}
|
||||
else
|
||||
else if (optype0 != REGOP)
|
||||
{
|
||||
wordpart[0][0] = op0;
|
||||
wordpart[1][0] = op0;
|
||||
@ -2228,7 +2301,12 @@ output_move_quad (operands)
|
||||
if (optype1 == REGOP)
|
||||
{
|
||||
wordpart[0][1] = gen_rtx (REG, word_mode, REGNO (op1) + 0);
|
||||
wordpart[1][1] = gen_rtx (REG, word_mode, REGNO (op1) + 1);
|
||||
if (TARGET_ARCH64 && FP_REG_P (op1)
|
||||
&& REGNO (op1) < SPARC_FIRST_V9_FP_REG)
|
||||
wordpart[1][1] = gen_rtx (REG, word_mode, REGNO (op1) + 2);
|
||||
else
|
||||
wordpart[1][1] = gen_rtx (REG, word_mode, REGNO (op1) + 1);
|
||||
|
||||
if (TARGET_ARCH32)
|
||||
{
|
||||
wordpart[2][1] = gen_rtx (REG, word_mode, REGNO (op1) + 2);
|
||||
@ -2282,6 +2360,7 @@ output_move_quad (operands)
|
||||
|| (optype0 == OFFSOP && optype1 == REGOP && (REGNO (op1) & 1) == 0))
|
||||
{
|
||||
rtx mem, reg;
|
||||
int use_ldx;
|
||||
|
||||
if (optype0 == REGOP)
|
||||
mem = op1, reg = op0;
|
||||
@ -2294,19 +2373,33 @@ output_move_quad (operands)
|
||||
the register number. */
|
||||
|| (TARGET_V9 && REGNO (reg) >= SPARC_FIRST_V9_FP_REG))
|
||||
{
|
||||
static char * const mov_by_64[2][2][2] = {
|
||||
{ { "std %S1,%2;std %1,%0", "stx %R1,%2;stx %1,%0" },
|
||||
{ "ldd %2,%S0;ldd %1,%0", "ldx %2,%R0;ldx %1,%0" } },
|
||||
{ { "std %1,%0;std %S1,%2", "stx %1,%0;stx %R1,%2" },
|
||||
{ "ldd %1,%0;ldd %2,%S0", "ldx %1,%0;ldx %2,%R0" } }
|
||||
};
|
||||
|
||||
if (TARGET_V9 && FP_REG_P (reg) && TARGET_HARD_QUAD)
|
||||
{
|
||||
if ((REGNO (reg) & 3) != 0)
|
||||
abort ();
|
||||
/* ??? Can `mem' have an inappropriate alignment here? */
|
||||
return (mem == op1 ? "ldq %1,%0" : "stq %1,%0");
|
||||
/* Only abort if the register # requires that we use ldq. */
|
||||
if ((REGNO (reg) & 3) == 0)
|
||||
{
|
||||
/* ??? Can `mem' have an inappropriate alignment here? */
|
||||
return (mem == op1 ? "ldq %1,%0" : "stq %1,%0");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (REGNO (reg) >= SPARC_FIRST_V9_FP_REG)
|
||||
abort();
|
||||
}
|
||||
}
|
||||
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\n\tldx %2,%R0" : "ldd %1,%0\n\tldd %2,%S0";
|
||||
else
|
||||
return TARGET_ARCH64 ? "stx %1,%0\n\tstx %R1,%2" : "std %1,%0\n\tstd %S1,%2";
|
||||
|
||||
/* Do the loads in the right order; can't overwrite our address
|
||||
register. */
|
||||
use_ldx = TARGET_ARCH64 && !FP_REG_P (reg);
|
||||
return mov_by_64[!load_late_half[0]][mem != op1][use_ldx];
|
||||
}
|
||||
}
|
||||
|
||||
@ -2343,77 +2436,124 @@ output_move_quad (operands)
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading into a register which overlaps a register used in the address. */
|
||||
if (optype0 == REGOP && optype1 != REGOP
|
||||
&& reg_overlap_mentioned_p (op0, op1))
|
||||
{
|
||||
/* ??? Not implemented yet. This is a bit complicated, because we
|
||||
must load which ever part overlaps the address last. If the address
|
||||
is a double-reg address, then there are two parts which need to
|
||||
be done last, which is impossible. We would need a scratch register
|
||||
in that case. */
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* Normal case: move the words in lowest to highest address order. */
|
||||
/* Normal case: move the words in lowest to highest address order.
|
||||
There may have an overlapping register; in that case, skip and go
|
||||
back. */
|
||||
|
||||
if (TARGET_ARCH32)
|
||||
{
|
||||
output_asm_insn (singlemove_string (wordpart[0]), wordpart[0]);
|
||||
int i;
|
||||
int offset = 0xc;
|
||||
rtx temp[2];
|
||||
|
||||
/* Make any unoffsettable addresses point at the second word. */
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,0x4,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,0x4,%0", &addreg1);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (! load_late[i])
|
||||
output_asm_insn (singlemove_string (wordpart[i]), wordpart[i]);
|
||||
|
||||
/* Do the second word. */
|
||||
output_asm_insn (singlemove_string (wordpart[1]), wordpart[1]);
|
||||
if (i != 3)
|
||||
{
|
||||
/* Make any unoffsettable addresses point at the next word. */
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,0x4,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,0x4,%0", &addreg1);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (load_late[i])
|
||||
{
|
||||
int fix = offset - i * 4;
|
||||
|
||||
/* Make any unoffsettable addresses point at the third word. */
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,0x4,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,0x4,%0", &addreg1);
|
||||
|
||||
/* Do the third word. */
|
||||
output_asm_insn (singlemove_string (wordpart[2]), wordpart[2]);
|
||||
|
||||
/* Make any unoffsettable addresses point at the fourth word. */
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,0x4,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,0x4,%0", &addreg1);
|
||||
|
||||
/* Do the fourth word. */
|
||||
output_asm_insn (singlemove_string (wordpart[3]), wordpart[3]);
|
||||
|
||||
/* Undo the adds we just did. */
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,-0xc,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,-0xc,%0", &addreg1);
|
||||
/* Back up to the appropriate place. */
|
||||
temp[1] = gen_rtx (CONST_INT, VOIDmode, -fix);
|
||||
if (addreg0)
|
||||
{
|
||||
temp[0] = addreg0;
|
||||
output_asm_insn ("add %0,%1,%0", temp);
|
||||
}
|
||||
if (addreg1)
|
||||
{
|
||||
temp[0] = addreg1;
|
||||
output_asm_insn ("add %0,%1,%0", temp);
|
||||
}
|
||||
output_asm_insn (singlemove_string (wordpart[i]),
|
||||
wordpart[i]);
|
||||
/* Don't modify the register that's the destination of the
|
||||
move. */
|
||||
temp[0] = gen_rtx (CONST_INT, VOIDmode, -(offset - fix));
|
||||
if (addreg0 && REGNO (addreg0) != REGNO (wordpart[i][0]))
|
||||
{
|
||||
temp[1] = addreg0;
|
||||
output_asm_insn("add %0,%1,%0", temp);
|
||||
}
|
||||
if (addreg1 && REGNO (addreg1) != REGNO (wordpart[i][0]))
|
||||
{
|
||||
temp[1] = addreg1;
|
||||
output_asm_insn("add %0,%1,%0",temp);
|
||||
}
|
||||
offset = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (offset)
|
||||
{
|
||||
temp[1] = gen_rtx (CONST_INT, VOIDmode, -offset);
|
||||
/* Undo the adds we just did. */
|
||||
if (addreg0)
|
||||
{
|
||||
temp[0] = addreg0;
|
||||
output_asm_insn ("add %0,%1,%0", temp);
|
||||
}
|
||||
if (addreg1)
|
||||
{
|
||||
temp[0] = addreg1;
|
||||
output_asm_insn ("add %0,%1,%0", temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* TARGET_ARCH64 */
|
||||
{
|
||||
output_asm_insn (doublemove_string (wordpart[0]), wordpart[0]);
|
||||
if (load_late_half[0])
|
||||
{
|
||||
/* Load the second half first. */
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,0x8,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,0x8,%0", &addreg1);
|
||||
|
||||
/* Make any unoffsettable addresses point at the second word. */
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,0x8,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,0x8,%0", &addreg1);
|
||||
output_asm_insn (doublemove_string (wordpart[1]), wordpart[1]);
|
||||
|
||||
/* Do the second word. */
|
||||
output_asm_insn (doublemove_string (wordpart[1]), wordpart[1]);
|
||||
/* Undo the adds we just did. */
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,-0x8,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,-0x8,%0", &addreg1);
|
||||
|
||||
/* Undo the adds we just did. */
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,-0x8,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,-0x8,%0", &addreg1);
|
||||
output_asm_insn (doublemove_string (wordpart[0]), wordpart[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
output_asm_insn (doublemove_string (wordpart[0]), wordpart[0]);
|
||||
|
||||
if (addreg0)
|
||||
output_asm_insn ("add %0,0x8,%0", &addreg0);
|
||||
if (addreg1)
|
||||
output_asm_insn ("add %0,0x8,%0", &addreg1);
|
||||
|
||||
/* Do the second word. */
|
||||
output_asm_insn (doublemove_string (wordpart[1]), wordpart[1]);
|
||||
|
||||
/* Undo the adds we just did. But don't modify the dest of
|
||||
the move. */
|
||||
if (addreg0 && REGNO (addreg0) != REGNO (wordpart[1][0]))
|
||||
output_asm_insn ("add %0,-0x8,%0", &addreg0);
|
||||
if (addreg1 && REGNO (addreg1) != REGNO (wordpart[1][0]))
|
||||
output_asm_insn ("add %0,-0x8,%0", &addreg1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -2448,6 +2588,24 @@ output_fp_move_double (operands)
|
||||
else abort ();
|
||||
}
|
||||
|
||||
/* When doing a quad-register move, determine the drection in which
|
||||
the move needs to be performed. SRC and DST are the source and
|
||||
destination registers.
|
||||
|
||||
A value of -1 indicates that the move needs to be done from the
|
||||
highest register to the lowest. */
|
||||
|
||||
static int
|
||||
move_quad_direction (src, dst)
|
||||
rtx src, dst;
|
||||
{
|
||||
if ((REGNO (dst) > REGNO (src))
|
||||
&& (REGNO (dst) < (REGNO (src) + 4)))
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Output assembler code to perform a quadword move insn with operands
|
||||
OPERANDS, one of which must be a floating point register. */
|
||||
|
||||
@ -2465,9 +2623,21 @@ output_fp_move_quad (operands)
|
||||
if (TARGET_V9 && TARGET_HARD_QUAD)
|
||||
return "fmovq %1,%0";
|
||||
else if (TARGET_V9)
|
||||
return "fmovd %1,%0\n\tfmovd %S1,%S0";
|
||||
{
|
||||
int dir = move_quad_direction (op1, op0);
|
||||
if (dir > 0)
|
||||
return "fmovd %1,%0\n\tfmovd %S1,%S0";
|
||||
else
|
||||
return "fmovd %S1,%S0\n\tfmovd %1,%0";
|
||||
}
|
||||
else
|
||||
return "fmovs %1,%0\n\tfmovs %R1,%R0\n\tfmovs %S1,%S0\n\tfmovs %T1,%T0";
|
||||
{
|
||||
int dir = move_quad_direction (op0, op1);
|
||||
if (dir > 0)
|
||||
return "fmovs %1,%0\n\tfmovs %R1,%R0\n\tfmovs %S1,%S0\n\tfmovs %T1,%T0";
|
||||
else
|
||||
return "fmovs %T1,%T0\n\tfmovs %S1,%S0\n\tfmovs %R1,%R0\n\tfmovs %1,%0";
|
||||
}
|
||||
}
|
||||
else if (GET_CODE (op1) == REG)
|
||||
abort ();
|
||||
@ -3777,7 +3947,7 @@ function_arg_slotno (cum, mode, type, named, incoming_p, pregno, ppadding)
|
||||
if (TARGET_FPU && named)
|
||||
{
|
||||
if (slotno >= SPARC_FP_ARG_MAX)
|
||||
return 0;
|
||||
return -1;
|
||||
regno = SPARC_FP_ARG_FIRST + slotno * 2;
|
||||
if (mode == SFmode)
|
||||
regno++;
|
||||
@ -3855,6 +4025,271 @@ function_arg_slotno (cum, mode, type, named, incoming_p, pregno, ppadding)
|
||||
return slotno;
|
||||
}
|
||||
|
||||
/* Handle recursive register counting for structure field layout. */
|
||||
|
||||
struct function_arg_record_value_parms
|
||||
{
|
||||
rtx ret;
|
||||
int slotno, named, regbase;
|
||||
int nregs, intoffset;
|
||||
};
|
||||
|
||||
static void
|
||||
function_arg_record_value_1 (type, startbitpos, parms)
|
||||
tree type;
|
||||
int startbitpos;
|
||||
struct function_arg_record_value_parms *parms;
|
||||
{
|
||||
tree field;
|
||||
|
||||
/* The ABI obviously doesn't specify how packed structures are
|
||||
passed. These are defined to be passed in int regs if possible,
|
||||
otherwise memory. */
|
||||
int packed_p = 0;
|
||||
|
||||
/* We need to compute how many registers are needed so we can
|
||||
allocate the PARALLEL but before we can do that we need to know
|
||||
whether there are any packed fields. If there are, int regs are
|
||||
used regardless of whether there are fp values present. */
|
||||
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
||||
{
|
||||
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
|
||||
{
|
||||
packed_p = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute how many registers we need. */
|
||||
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
||||
{
|
||||
if (TREE_CODE (field) == FIELD_DECL)
|
||||
{
|
||||
int bitpos = startbitpos;
|
||||
if (DECL_FIELD_BITPOS (field))
|
||||
bitpos += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
|
||||
/* ??? FIXME: else assume zero offset. */
|
||||
|
||||
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
|
||||
{
|
||||
function_arg_record_value_1 (TREE_TYPE (field), bitpos, parms);
|
||||
}
|
||||
else if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
|
||||
&& TARGET_FPU
|
||||
&& ! packed_p
|
||||
&& parms->named)
|
||||
{
|
||||
if (parms->intoffset != -1)
|
||||
{
|
||||
int intslots, this_slotno;
|
||||
|
||||
intslots = (bitpos - parms->intoffset + BITS_PER_WORD - 1)
|
||||
/ BITS_PER_WORD;
|
||||
this_slotno = parms->slotno + parms->intoffset
|
||||
/ BITS_PER_WORD;
|
||||
|
||||
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
|
||||
intslots = MAX (intslots, 0);
|
||||
parms->nregs += intslots;
|
||||
parms->intoffset = -1;
|
||||
}
|
||||
|
||||
/* There's no need to check this_slotno < SPARC_FP_ARG MAX.
|
||||
If it wasn't true we wouldn't be here. */
|
||||
parms->nregs += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parms->intoffset == -1)
|
||||
parms->intoffset = bitpos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle recursive structure field register assignment. */
|
||||
|
||||
static void
|
||||
function_arg_record_value_3 (bitpos, parms)
|
||||
int bitpos;
|
||||
struct function_arg_record_value_parms *parms;
|
||||
{
|
||||
enum machine_mode mode;
|
||||
int regno, this_slotno, intslots, intoffset;
|
||||
rtx reg;
|
||||
|
||||
if (parms->intoffset == -1)
|
||||
return;
|
||||
intoffset = parms->intoffset;
|
||||
parms->intoffset = -1;
|
||||
|
||||
intslots = (bitpos - intoffset + BITS_PER_WORD - 1) / BITS_PER_WORD;
|
||||
this_slotno = parms->slotno + intoffset / BITS_PER_WORD;
|
||||
|
||||
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
|
||||
if (intslots <= 0)
|
||||
return;
|
||||
|
||||
/* If this is the trailing part of a word, only load that much into
|
||||
the register. Otherwise load the whole register. Note that in
|
||||
the latter case we may pick up unwanted bits. It's not a problem
|
||||
at the moment but may wish to revisit. */
|
||||
|
||||
if (intoffset % BITS_PER_WORD != 0)
|
||||
{
|
||||
mode = mode_for_size (BITS_PER_WORD - intoffset%BITS_PER_WORD,
|
||||
MODE_INT, 0);
|
||||
}
|
||||
else
|
||||
mode = word_mode;
|
||||
|
||||
intoffset /= BITS_PER_UNIT;
|
||||
do
|
||||
{
|
||||
regno = parms->regbase + this_slotno;
|
||||
reg = gen_rtx (REG, mode, regno);
|
||||
XVECEXP (parms->ret, 0, parms->nregs)
|
||||
= gen_rtx (EXPR_LIST, VOIDmode, reg, GEN_INT (intoffset));
|
||||
|
||||
this_slotno += 1;
|
||||
intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1;
|
||||
parms->nregs += 1;
|
||||
intslots -= 1;
|
||||
}
|
||||
while (intslots > 0);
|
||||
}
|
||||
|
||||
static void
|
||||
function_arg_record_value_2 (type, startbitpos, parms)
|
||||
tree type;
|
||||
int startbitpos;
|
||||
struct function_arg_record_value_parms *parms;
|
||||
{
|
||||
tree field;
|
||||
int packed_p = 0;
|
||||
|
||||
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
||||
{
|
||||
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
|
||||
{
|
||||
packed_p = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
||||
{
|
||||
if (TREE_CODE (field) == FIELD_DECL)
|
||||
{
|
||||
int bitpos = startbitpos;
|
||||
if (DECL_FIELD_BITPOS (field))
|
||||
bitpos += TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
|
||||
/* ??? FIXME: else assume zero offset. */
|
||||
|
||||
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
|
||||
{
|
||||
function_arg_record_value_2 (TREE_TYPE (field), bitpos, parms);
|
||||
}
|
||||
else if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
|
||||
&& TARGET_FPU
|
||||
&& ! packed_p
|
||||
&& parms->named)
|
||||
{
|
||||
int this_slotno = parms->slotno + bitpos / BITS_PER_WORD;
|
||||
rtx reg;
|
||||
|
||||
function_arg_record_value_3 (bitpos, parms);
|
||||
|
||||
reg = gen_rtx (REG, DECL_MODE (field),
|
||||
(SPARC_FP_ARG_FIRST + this_slotno * 2
|
||||
+ (DECL_MODE (field) == SFmode
|
||||
&& (bitpos & 32) != 0)));
|
||||
XVECEXP (parms->ret, 0, parms->nregs)
|
||||
= gen_rtx (EXPR_LIST, VOIDmode, reg,
|
||||
GEN_INT (bitpos / BITS_PER_UNIT));
|
||||
parms->nregs += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parms->intoffset == -1)
|
||||
parms->intoffset = bitpos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static rtx
|
||||
function_arg_record_value (type, slotno, named, regbase)
|
||||
tree type;
|
||||
int slotno, named, regbase;
|
||||
{
|
||||
HOST_WIDE_INT typesize = int_size_in_bytes (type);
|
||||
struct function_arg_record_value_parms parms;
|
||||
int nregs;
|
||||
|
||||
parms.ret = NULL_RTX;
|
||||
parms.slotno = slotno;
|
||||
parms.named = named;
|
||||
parms.regbase = regbase;
|
||||
|
||||
/* Compute how many registers we need. */
|
||||
parms.nregs = 0;
|
||||
parms.intoffset = 0;
|
||||
function_arg_record_value_1 (type, 0, &parms);
|
||||
|
||||
if (parms.intoffset != -1)
|
||||
{
|
||||
int intslots, this_slotno;
|
||||
|
||||
intslots = (typesize*BITS_PER_UNIT - parms.intoffset + BITS_PER_WORD - 1)
|
||||
/ BITS_PER_WORD;
|
||||
this_slotno = slotno + parms.intoffset / BITS_PER_WORD;
|
||||
|
||||
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
|
||||
intslots = MAX (intslots, 0);
|
||||
|
||||
parms.nregs += intslots;
|
||||
}
|
||||
nregs = parms.nregs;
|
||||
|
||||
/* Allocate the vector and handle some annoying special cases. */
|
||||
if (nregs == 0)
|
||||
{
|
||||
/* ??? Empty structure has no value? Duh? */
|
||||
if (typesize <= 0)
|
||||
{
|
||||
/* Though there's nothing really to store, return a word register
|
||||
anyway so the rest of gcc doesn't go nuts. Returning a PARALLEL
|
||||
leads to breakage due to the fact that there are zero bytes to
|
||||
load. */
|
||||
return gen_rtx (REG, DImode, regbase);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* ??? C++ has structures with no fields, and yet a size. Give up
|
||||
for now and pass everything back in integer registers. */
|
||||
nregs = (typesize + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
|
||||
}
|
||||
if (nregs + slotno > SPARC_INT_ARG_MAX)
|
||||
nregs = SPARC_INT_ARG_MAX - slotno;
|
||||
}
|
||||
if (nregs == 0)
|
||||
abort();
|
||||
|
||||
parms.ret = gen_rtx (PARALLEL, VOIDmode, rtvec_alloc (nregs));
|
||||
|
||||
/* Fill in the entries. */
|
||||
parms.nregs = 0;
|
||||
parms.intoffset = 0;
|
||||
function_arg_record_value_2 (type, 0, &parms);
|
||||
function_arg_record_value_3 (typesize * BITS_PER_UNIT, &parms);
|
||||
|
||||
if (parms.nregs != nregs)
|
||||
abort ();
|
||||
|
||||
return parms.ret;
|
||||
}
|
||||
|
||||
/* Handle the FUNCTION_ARG macro.
|
||||
Determine where to put an argument to a function.
|
||||
Value is zero to push the argument on the stack,
|
||||
@ -3909,6 +4344,7 @@ function_arg (cum, mode, type, named, incoming_p)
|
||||
{
|
||||
/* "* 2" because fp reg numbers are recorded in 4 byte
|
||||
quantities. */
|
||||
#if 0
|
||||
/* ??? This will cause the value to be passed in the fp reg and
|
||||
in the stack. When a prototype exists we want to pass the
|
||||
value in the reg but reserve space on the stack. That's an
|
||||
@ -3921,172 +4357,58 @@ function_arg (cum, mode, type, named, incoming_p)
|
||||
gen_rtx (EXPR_LIST, VOIDmode,
|
||||
reg, const0_rtx)));
|
||||
else
|
||||
#else
|
||||
/* ??? It seems that passing back a register even when past
|
||||
the area declared by REG_PARM_STACK_SPACE will allocate
|
||||
space appropriately, and will not copy the data onto the
|
||||
stack, exactly as we desire.
|
||||
|
||||
This is due to locate_and_pad_parm being called in
|
||||
expand_call whenever reg_parm_stack_space > 0, which
|
||||
while benefical to our example here, would seem to be
|
||||
in error from what had been intended. Ho hum... -- r~ */
|
||||
#endif
|
||||
return reg;
|
||||
}
|
||||
else
|
||||
{
|
||||
rtx v0, v1;
|
||||
|
||||
if ((regno - SPARC_FP_ARG_FIRST) < SPARC_INT_ARG_MAX * 2)
|
||||
{
|
||||
int regbase = (incoming_p
|
||||
? SPARC_INCOMING_INT_ARG_FIRST
|
||||
: SPARC_OUTGOING_INT_ARG_FIRST);
|
||||
int intreg = regbase + (regno - SPARC_FP_ARG_FIRST) / 2;
|
||||
return gen_rtx (PARALLEL, mode,
|
||||
gen_rtvec (2,
|
||||
gen_rtx (EXPR_LIST, VOIDmode,
|
||||
reg, const0_rtx),
|
||||
gen_rtx (EXPR_LIST, VOIDmode,
|
||||
gen_rtx (REG, mode, intreg),
|
||||
const0_rtx)));
|
||||
int intreg;
|
||||
|
||||
/* On incoming, we don't need to know that the value
|
||||
is passed in %f0 and %i0, and it confuses other parts
|
||||
causing needless spillage even on the simplest cases. */
|
||||
if (incoming_p)
|
||||
return reg;
|
||||
|
||||
intreg = (SPARC_OUTGOING_INT_ARG_FIRST
|
||||
+ (regno - SPARC_FP_ARG_FIRST) / 2);
|
||||
|
||||
v0 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
|
||||
v1 = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode, intreg),
|
||||
const0_rtx);
|
||||
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
|
||||
}
|
||||
else
|
||||
return gen_rtx (PARALLEL, mode,
|
||||
gen_rtvec (2,
|
||||
gen_rtx (EXPR_LIST, VOIDmode,
|
||||
NULL_RTX, const0_rtx),
|
||||
gen_rtx (EXPR_LIST, VOIDmode,
|
||||
reg, const0_rtx)));
|
||||
{
|
||||
v0 = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
|
||||
v1 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
|
||||
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type && TREE_CODE (type) == RECORD_TYPE)
|
||||
{
|
||||
/* Structures up to 16 bytes in size are passed in arg slots on the
|
||||
stack and are promoted to registers where possible. */
|
||||
tree field;
|
||||
rtx ret;
|
||||
int i;
|
||||
int nregs;
|
||||
/* Starting bit position of a sequence of integer fields, counted from
|
||||
msb of left most byte, -1 if last field wasn't an int. */
|
||||
/* ??? This isn't entirely necessary, some simplification
|
||||
may be possible. */
|
||||
int start_int_bitpos;
|
||||
/* Current bitpos in struct, counted from msb of left most byte. */
|
||||
int bitpos, this_slotno;
|
||||
/* The ABI obviously doesn't specify how packed
|
||||
structures are passed. These are defined to be passed
|
||||
in int regs if possible, otherwise memory. */
|
||||
int packed_p = 0;
|
||||
|
||||
if (int_size_in_bytes (type) > 16)
|
||||
abort (); /* shouldn't get here */
|
||||
|
||||
/* We need to compute how many registers are needed so we can allocate
|
||||
the PARALLEL but before we can do that we need to know whether there
|
||||
are any packed fields. If there are, int regs are used regardless of
|
||||
whether there are fp values present. */
|
||||
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
||||
{
|
||||
if (TREE_CODE (field) == FIELD_DECL
|
||||
&& DECL_PACKED (field))
|
||||
{
|
||||
packed_p = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute how many registers we need. */
|
||||
nregs = 0;
|
||||
start_int_bitpos = -1;
|
||||
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
||||
{
|
||||
bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
|
||||
this_slotno = slotno + bitpos / BITS_PER_WORD;
|
||||
if (TREE_CODE (field) == FIELD_DECL)
|
||||
{
|
||||
if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
|
||||
&& TARGET_FPU
|
||||
&& ! packed_p
|
||||
&& named)
|
||||
{
|
||||
/* There's no need to check this_slotno < SPARC_FP_ARG MAX.
|
||||
If it wasn't true we wouldn't be here. */
|
||||
nregs++;
|
||||
start_int_bitpos = -1;
|
||||
}
|
||||
else if (this_slotno < SPARC_INT_ARG_MAX)
|
||||
{
|
||||
if (start_int_bitpos == -1)
|
||||
{
|
||||
nregs++;
|
||||
start_int_bitpos = bitpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bitpos % BITS_PER_WORD == 0)
|
||||
nregs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nregs == 0)
|
||||
abort ();
|
||||
|
||||
ret = gen_rtx (PARALLEL, BLKmode, rtvec_alloc (nregs + 1));
|
||||
|
||||
/* ??? This causes the entire struct to be passed in memory.
|
||||
This isn't necessary, but is left for later. */
|
||||
XVECEXP (ret, 0, 0) = gen_rtx (EXPR_LIST, VOIDmode, NULL_RTX,
|
||||
const0_rtx);
|
||||
|
||||
/* Fill in the entries. */
|
||||
start_int_bitpos = -1;
|
||||
for (i = 1, field = TYPE_FIELDS (type);
|
||||
field;
|
||||
field = TREE_CHAIN (field))
|
||||
{
|
||||
bitpos = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field));
|
||||
this_slotno = slotno + bitpos / BITS_PER_WORD;
|
||||
if (TREE_CODE (field) == FIELD_DECL)
|
||||
{
|
||||
if (TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
|
||||
&& TARGET_FPU
|
||||
&& ! packed_p
|
||||
&& named)
|
||||
{
|
||||
reg = gen_rtx (REG, DECL_MODE (field),
|
||||
(SPARC_FP_ARG_FIRST + this_slotno * 2
|
||||
+ (DECL_MODE (field) == SFmode
|
||||
&& (bitpos & 32) != 0)));
|
||||
XVECEXP (ret, 0, i) = gen_rtx (EXPR_LIST, VOIDmode, reg,
|
||||
GEN_INT (bitpos / BITS_PER_UNIT));
|
||||
i++;
|
||||
start_int_bitpos = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this_slotno < SPARC_INT_ARG_MAX
|
||||
&& (start_int_bitpos == -1
|
||||
|| bitpos % BITS_PER_WORD == 0))
|
||||
{
|
||||
enum machine_mode mode;
|
||||
|
||||
/* If this is the trailing part of a word, only load
|
||||
that much into the register. Otherwise load the
|
||||
whole register. Note that in the latter case we may
|
||||
pick up unwanted bits. It's not a problem at the
|
||||
moment but may wish to revisit. */
|
||||
if (bitpos % BITS_PER_WORD != 0)
|
||||
mode = mode_for_size (BITS_PER_WORD - bitpos % BITS_PER_WORD,
|
||||
MODE_INT, 0);
|
||||
else
|
||||
mode = word_mode;
|
||||
|
||||
regno = regbase + this_slotno;
|
||||
reg = gen_rtx (REG, mode, regno);
|
||||
XVECEXP (ret, 0, i) = gen_rtx (EXPR_LIST, VOIDmode, reg,
|
||||
GEN_INT (bitpos / BITS_PER_UNIT));
|
||||
i++;
|
||||
if (start_int_bitpos == -1)
|
||||
start_int_bitpos = bitpos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i != nregs + 1)
|
||||
abort ();
|
||||
|
||||
return ret;
|
||||
return function_arg_record_value (type, slotno, named, regbase);
|
||||
}
|
||||
else if (type && TREE_CODE (type) == UNION_TYPE)
|
||||
{
|
||||
@ -4187,7 +4509,7 @@ function_arg_partial_nregs (cum, mode, type, named)
|
||||
/* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro.
|
||||
!v9: The SPARC ABI stipulates passing struct arguments (of any size) and
|
||||
quad-precision floats by invisible reference.
|
||||
v9: aggregates greater than 16 bytes are passed by reference.
|
||||
v9: Aggregates greater than 16 bytes are passed by reference.
|
||||
For Pascal, also pass arrays by reference. */
|
||||
|
||||
int
|
||||
@ -4205,6 +4527,8 @@ function_arg_pass_by_reference (cum, mode, type, named)
|
||||
else
|
||||
{
|
||||
return ((type && TREE_CODE (type) == ARRAY_TYPE)
|
||||
/* Consider complex values as aggregates, so care for TCmode. */
|
||||
|| GET_MODE_SIZE (mode) > 16
|
||||
|| (type && AGGREGATE_TYPE_P (type)
|
||||
&& int_size_in_bytes (type) > 16));
|
||||
}
|
||||
@ -4288,7 +4612,53 @@ function_arg_padding (mode, type)
|
||||
: GET_MODE_BITSIZE (mode) < PARM_BOUNDARY)
|
||||
? downward : upward));
|
||||
}
|
||||
|
||||
|
||||
/* Handle FUNCTION_VALUE, FUNCTION_OUTGOING_VALUE, and LIBCALL_VALUE macros.
|
||||
For v9, function return values are subject to the same rules as arguments,
|
||||
except that up to 32-bytes may be returned in registers. */
|
||||
|
||||
rtx
|
||||
function_value (type, mode, incoming_p)
|
||||
tree type;
|
||||
enum machine_mode mode;
|
||||
int incoming_p;
|
||||
{
|
||||
int regno;
|
||||
int regbase = (incoming_p
|
||||
? SPARC_OUTGOING_INT_ARG_FIRST
|
||||
: SPARC_INCOMING_INT_ARG_FIRST);
|
||||
|
||||
if (TARGET_ARCH64 && type)
|
||||
{
|
||||
if (TREE_CODE (type) == RECORD_TYPE)
|
||||
{
|
||||
/* Structures up to 32 bytes in size are passed in registers,
|
||||
promoted to fp registers where possible. */
|
||||
|
||||
if (int_size_in_bytes (type) > 32)
|
||||
abort (); /* shouldn't get here */
|
||||
|
||||
return function_arg_record_value (type, 0, 1, regbase);
|
||||
}
|
||||
else if (TREE_CODE (type) == UNION_TYPE)
|
||||
{
|
||||
int bytes = int_size_in_bytes (type);
|
||||
|
||||
if (bytes > 32)
|
||||
abort ();
|
||||
|
||||
mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (incoming_p)
|
||||
regno = BASE_RETURN_VALUE_REG (mode);
|
||||
else
|
||||
regno = BASE_OUTGOING_VALUE_REG (mode);
|
||||
|
||||
return gen_rtx (REG, mode, regno);
|
||||
}
|
||||
|
||||
/* Do what is necessary for `va_start'. The argument is ignored.
|
||||
|
||||
We look at the current function to determine if stdarg or varargs
|
||||
|
@ -276,8 +276,8 @@ Unrecognized value in TARGET_CPU_DEFAULT.
|
||||
#define NO_BUILTIN_PTRDIFF_TYPE
|
||||
#define NO_BUILTIN_SIZE_TYPE
|
||||
#endif
|
||||
#define PTRDIFF_TYPE (TARGET_ARCH64 ? "long long int" : "int")
|
||||
#define SIZE_TYPE (TARGET_ARCH64 ? "long long unsigned int" : "unsigned int")
|
||||
#define PTRDIFF_TYPE (TARGET_ARCH64 ? "long int" : "int")
|
||||
#define SIZE_TYPE (TARGET_ARCH64 ? "long unsigned int" : "unsigned int")
|
||||
|
||||
/* ??? This should be 32 bits for v9 but what can we do? */
|
||||
#define WCHAR_TYPE "short unsigned int"
|
||||
@ -1078,18 +1078,24 @@ extern int sparc_mode_class[];
|
||||
#define INITIALIZE_PIC initialize_pic ()
|
||||
#define FINALIZE_PIC finalize_pic ()
|
||||
|
||||
/* Pick a default value we can notice from override_options:
|
||||
!v9: Default is on.
|
||||
v9: Default is off. */
|
||||
|
||||
#define DEFAULT_PCC_STRUCT_RETURN -1
|
||||
|
||||
/* Sparc ABI says that quad-precision floats and all structures are returned
|
||||
in memory.
|
||||
For v9: unions <= 32 bytes in size are returned in int regs,
|
||||
structures up to 32 bytes are returned in int and fp regs.
|
||||
FIXME: wip */
|
||||
structures up to 32 bytes are returned in int and fp regs. */
|
||||
|
||||
#define RETURN_IN_MEMORY(TYPE) \
|
||||
(TARGET_ARCH32 \
|
||||
? (TYPE_MODE (TYPE) == BLKmode \
|
||||
|| TYPE_MODE (TYPE) == TFmode \
|
||||
|| TYPE_MODE (TYPE) == TCmode) \
|
||||
: TYPE_MODE (TYPE) == BLKmode)
|
||||
: (TYPE_MODE (TYPE) == BLKmode \
|
||||
&& int_size_in_bytes (TYPE) > 32))
|
||||
|
||||
/* Functions which return large structures get the address
|
||||
to place the wanted value at offset 64 from the frame.
|
||||
@ -1449,9 +1455,14 @@ extern char leaf_reg_remap[];
|
||||
: (STRUCT_VALUE_OFFSET + UNITS_PER_WORD))
|
||||
|
||||
/* When a parameter is passed in a register, stack space is still
|
||||
allocated for it. */
|
||||
/* This only takes into account the int regs.
|
||||
fp regs are handled elsewhere. */
|
||||
allocated for it.
|
||||
!v9: All 6 possible integer registers have backing store allocated.
|
||||
v9: Only space for the arguments passed is allocated. */
|
||||
/* ??? Ideally, we'd use zero here (as the minimum), but zero has special
|
||||
meaning to the backend. Further, we need to be able to detect if a
|
||||
varargs/unprototyped function is called, as they may want to spill more
|
||||
registers than we've provided space. Ugly, ugly. So for now we retain
|
||||
all 6 slots even for v9. */
|
||||
#define REG_PARM_STACK_SPACE(DECL) (6 * UNITS_PER_WORD)
|
||||
|
||||
/* Keep the stack pointer constant throughout the function.
|
||||
@ -1472,24 +1483,28 @@ extern char leaf_reg_remap[];
|
||||
/* Some subroutine macros specific to this machine.
|
||||
When !TARGET_FPU, put float return values in the general registers,
|
||||
since we don't have any fp registers. */
|
||||
#define BASE_RETURN_VALUE_REG(MODE) \
|
||||
(TARGET_ARCH64 \
|
||||
? (TARGET_FPU && GET_MODE_CLASS (MODE) == MODE_FLOAT ? 32 : 8) \
|
||||
#define BASE_RETURN_VALUE_REG(MODE) \
|
||||
(TARGET_ARCH64 \
|
||||
? (TARGET_FPU && FLOAT_MODE_P (MODE) ? 32 : 8) \
|
||||
: (((MODE) == SFmode || (MODE) == DFmode) && TARGET_FPU ? 32 : 8))
|
||||
#define BASE_OUTGOING_VALUE_REG(MODE) \
|
||||
(TARGET_ARCH64 \
|
||||
? (TARGET_FPU && GET_MODE_CLASS (MODE) == MODE_FLOAT ? 32 \
|
||||
: TARGET_FLAT ? 8 : 24) \
|
||||
|
||||
#define BASE_OUTGOING_VALUE_REG(MODE) \
|
||||
(TARGET_ARCH64 \
|
||||
? (TARGET_FPU && FLOAT_MODE_P (MODE) ? 32 \
|
||||
: TARGET_FLAT ? 8 : 24) \
|
||||
: (((MODE) == SFmode || (MODE) == DFmode) && TARGET_FPU ? 32 \
|
||||
: (TARGET_FLAT ? 8 : 24)))
|
||||
#define BASE_PASSING_ARG_REG(MODE) \
|
||||
(TARGET_ARCH64 \
|
||||
? (TARGET_FPU && GET_MODE_CLASS (MODE) == MODE_FLOAT ? 32 : 8) \
|
||||
|
||||
#define BASE_PASSING_ARG_REG(MODE) \
|
||||
(TARGET_ARCH64 \
|
||||
? (TARGET_FPU && FLOAT_MODE_P (MODE) ? 32 : 8) \
|
||||
: 8)
|
||||
#define BASE_INCOMING_ARG_REG(MODE) \
|
||||
(TARGET_ARCH64 \
|
||||
? (TARGET_FPU && GET_MODE_CLASS (MODE) == MODE_FLOAT ? 32 \
|
||||
: TARGET_FLAT ? 8 : 24) \
|
||||
|
||||
/* ??? FIXME -- seems wrong for v9 structure passing... */
|
||||
#define BASE_INCOMING_ARG_REG(MODE) \
|
||||
(TARGET_ARCH64 \
|
||||
? (TARGET_FPU && FLOAT_MODE_P (MODE) ? 32 \
|
||||
: TARGET_FLAT ? 8 : 24) \
|
||||
: (TARGET_FLAT ? 8 : 24))
|
||||
|
||||
/* Define this macro if the target machine has "register windows". This
|
||||
@ -1515,19 +1530,20 @@ 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)))
|
||||
extern struct rtx_def *function_value ();
|
||||
#define FUNCTION_VALUE(VALTYPE, FUNC) \
|
||||
function_value ((VALTYPE), TYPE_MODE (VALTYPE), 1)
|
||||
|
||||
/* 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)))
|
||||
#define FUNCTION_OUTGOING_VALUE(VALTYPE, FUNC) \
|
||||
function_value ((VALTYPE), TYPE_MODE (VALTYPE), 0)
|
||||
|
||||
/* 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))
|
||||
#define LIBCALL_VALUE(MODE) \
|
||||
function_value (NULL_TREE, (MODE), 1)
|
||||
|
||||
/* 1 if N is a possible register number for a function value
|
||||
as seen by the caller.
|
||||
|
@ -2202,7 +2202,7 @@
|
||||
(define_insn "*movdi_sp32_v9"
|
||||
[(set (match_operand:DI 0 "reg_or_nonsymb_mem_operand" "=r,T,Q,r,r,?e,?e,?Q,?b")
|
||||
(match_operand:DI 1 "general_operand" "r,J,r,Q,i,e,Q,e,J"))]
|
||||
"TARGET_V9
|
||||
"TARGET_V9 && ! TARGET_ARCH64
|
||||
&& (register_operand (operands[0], DImode)
|
||||
|| register_operand (operands[1], DImode)
|
||||
|| operands[1] == const0_rtx)
|
||||
@ -2297,7 +2297,7 @@
|
||||
case 3:
|
||||
return \"stx %r1,%0\";
|
||||
case 4:
|
||||
return \"mov %1,%0\";
|
||||
return \"fmovd %1,%0\";
|
||||
case 5:
|
||||
return \"ldd %1,%0\";
|
||||
case 6:
|
||||
@ -2367,8 +2367,8 @@
|
||||
;; to be reloaded by putting the constant into memory.
|
||||
;; It must come before the more general movsf pattern.
|
||||
(define_insn "*movsf_const_insn"
|
||||
[(set (match_operand:SF 0 "general_operand" "=?r,f,m,d")
|
||||
(match_operand:SF 1 "" "?F,m,G,G"))]
|
||||
[(set (match_operand:SF 0 "general_operand" "=f,d,m,?r")
|
||||
(match_operand:SF 1 "" "m,G,G,?F"))]
|
||||
"TARGET_FPU
|
||||
&& GET_CODE (operands[1]) == CONST_DOUBLE
|
||||
&& (GET_CODE (operands[0]) == REG
|
||||
@ -2378,19 +2378,19 @@
|
||||
switch (which_alternative)
|
||||
{
|
||||
case 0:
|
||||
return singlemove_string (operands);
|
||||
case 1:
|
||||
return \"ld %1,%0\";
|
||||
case 1:
|
||||
return \"fzeros %0\";
|
||||
case 2:
|
||||
return \"st %%g0,%0\";
|
||||
case 3:
|
||||
return \"fzeros %0\";
|
||||
return singlemove_string (operands);
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}"
|
||||
[(set_attr "type" "load,fpload,store,fpmove")
|
||||
(set_attr "length" "2,1,1,1")])
|
||||
[(set_attr "type" "fpload,fpmove,store,load")
|
||||
(set_attr "length" "1,1,1,2")])
|
||||
|
||||
(define_expand "movsf"
|
||||
[(set (match_operand:SF 0 "general_operand" "")
|
||||
@ -2403,19 +2403,19 @@
|
||||
}")
|
||||
|
||||
(define_insn "*movsf_insn"
|
||||
[(set (match_operand:SF 0 "reg_or_nonsymb_mem_operand" "=f,r,f,r,Q,Q")
|
||||
(match_operand:SF 1 "reg_or_nonsymb_mem_operand" "f,r,Q,Q,f,r"))]
|
||||
[(set (match_operand:SF 0 "reg_or_nonsymb_mem_operand" "=f,f,Q,r,r,Q")
|
||||
(match_operand:SF 1 "reg_or_nonsymb_mem_operand" "f,Q,f,r,Q,r"))]
|
||||
"TARGET_FPU
|
||||
&& (register_operand (operands[0], SFmode)
|
||||
|| register_operand (operands[1], SFmode))"
|
||||
"@
|
||||
fmovs %1,%0
|
||||
mov %1,%0
|
||||
ld %1,%0
|
||||
ld %1,%0
|
||||
st %1,%0
|
||||
mov %1,%0
|
||||
ld %1,%0
|
||||
st %1,%0"
|
||||
[(set_attr "type" "fpmove,move,fpload,load,fpstore,store")])
|
||||
[(set_attr "type" "fpmove,fpload,fpstore,move,load,store")])
|
||||
|
||||
;; Exactly the same as above, except that all `f' cases are deleted.
|
||||
;; This is necessary to prevent reload from ever trying to use a `f' reg
|
||||
@ -2492,8 +2492,8 @@
|
||||
}")
|
||||
|
||||
(define_insn "*movdf_insn"
|
||||
[(set (match_operand:DF 0 "reg_or_nonsymb_mem_operand" "=T,U,e,r,Q,Q,e,r")
|
||||
(match_operand:DF 1 "reg_or_nonsymb_mem_operand" "U,T,e,r,e,r,Q,Q"))]
|
||||
[(set (match_operand:DF 0 "reg_or_nonsymb_mem_operand" "=e,Q,e,T,U,r,Q,r")
|
||||
(match_operand:DF 1 "reg_or_nonsymb_mem_operand" "e,e,Q,U,T,r,r,Q"))]
|
||||
"TARGET_FPU
|
||||
&& (register_operand (operands[0], DFmode)
|
||||
|| register_operand (operands[1], DFmode))"
|
||||
@ -2512,7 +2512,7 @@
|
||||
|
||||
(define_insn "*movdf_no_e_insn"
|
||||
[(set (match_operand:DF 0 "reg_or_nonsymb_mem_operand" "=T,U,r,Q,&r")
|
||||
(match_operand:DF 1 "reg_or_nonsymb_mem_operand" "U,T,r,r,Q"))]
|
||||
(match_operand:DF 1 "reg_or_nonsymb_mem_operand" "U,T,r,r,Q"))]
|
||||
"! TARGET_FPU
|
||||
&& (register_operand (operands[0], DFmode)
|
||||
|| register_operand (operands[1], DFmode))"
|
||||
@ -2619,8 +2619,8 @@
|
||||
}")
|
||||
|
||||
(define_insn "*movtf_insn"
|
||||
[(set (match_operand:TF 0 "reg_or_nonsymb_mem_operand" "=e,r,Q,Q,e,&r")
|
||||
(match_operand:TF 1 "reg_or_nonsymb_mem_operand" "e,r,e,r,Q,Q"))]
|
||||
[(set (match_operand:TF 0 "reg_or_nonsymb_mem_operand" "=e,Q,e,r,Q,r")
|
||||
(match_operand:TF 1 "reg_or_nonsymb_mem_operand" "e,e,Q,r,r,Q"))]
|
||||
"TARGET_FPU
|
||||
&& (register_operand (operands[0], TFmode)
|
||||
|| register_operand (operands[1], TFmode))"
|
||||
@ -5517,7 +5517,8 @@ if (! TARGET_ARCH64)
|
||||
""
|
||||
"*
|
||||
{
|
||||
/* Some implementations are reported to have problems with
|
||||
/* Some implementations (e.g. TurboSparc) are reported to have problems
|
||||
with
|
||||
foo: b,a foo
|
||||
i.e. an empty loop with the annul bit set. The workaround is to use
|
||||
foo: b foo; nop
|
||||
@ -5824,7 +5825,7 @@ if (! TARGET_ARCH64)
|
||||
[(set_attr "type" "call")])
|
||||
|
||||
(define_insn "*call_value_address_sp64"
|
||||
[(set (match_operand 0 "" "=rf")
|
||||
[(set (match_operand 0 "" "")
|
||||
(call (mem:SI (match_operand:DI 1 "address_operand" "p"))
|
||||
(match_operand 2 "" "")))
|
||||
(clobber (reg:DI 15))]
|
||||
@ -5834,7 +5835,7 @@ if (! TARGET_ARCH64)
|
||||
[(set_attr "type" "call")])
|
||||
|
||||
(define_insn "*call_value_symbolic_sp64"
|
||||
[(set (match_operand 0 "" "=rf")
|
||||
[(set (match_operand 0 "" "")
|
||||
(call (mem:SI (match_operand:DI 1 "symbolic_operand" "s"))
|
||||
(match_operand 2 "" "")))
|
||||
(clobber (reg:DI 15))]
|
||||
@ -5982,6 +5983,7 @@ if (! TARGET_ARCH64)
|
||||
rtx fp = operands[1];
|
||||
rtx stack = operands[2];
|
||||
rtx lab = operands[3];
|
||||
rtx labreg;
|
||||
|
||||
/* Trap instruction to flush all the register windows. */
|
||||
emit_insn (gen_flush_register_windows ());
|
||||
@ -5995,7 +5997,8 @@ if (! TARGET_ARCH64)
|
||||
|
||||
/* Find the containing function's current nonlocal goto handler,
|
||||
which will do any cleanups and then jump to the label. */
|
||||
emit_move_insn (gen_rtx (REG, Pmode, 8), lab);
|
||||
labreg = gen_rtx (REG, Pmode, 8);
|
||||
emit_move_insn (labreg, lab);
|
||||
|
||||
/* Restore %fp from stack pointer value for containing function.
|
||||
The restore insn that follows will move this to %sp,
|
||||
@ -6007,16 +6010,18 @@ if (! TARGET_ARCH64)
|
||||
/*emit_insn (gen_rtx (USE, VOIDmode, frame_pointer_rtx));*/
|
||||
emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
|
||||
/* Return, restoring reg window and jumping to goto handler. */
|
||||
if (TARGET_V9 && GET_CODE (chain) == CONST_INT)
|
||||
if (TARGET_V9 && GET_CODE (chain) == CONST_INT
|
||||
&& ! (INTVAL (chain) & ~(HOST_WIDE_INT)0xffffffff))
|
||||
{
|
||||
emit_insn (gen_goto_handler_and_restore_v9 (static_chain_rtx, chain));
|
||||
emit_insn (gen_goto_handler_and_restore_v9 (labreg, static_chain_rtx,
|
||||
chain));
|
||||
emit_barrier ();
|
||||
DONE;
|
||||
}
|
||||
/* Put in the static chain register the nonlocal label address. */
|
||||
emit_move_insn (static_chain_rtx, chain);
|
||||
emit_insn (gen_rtx (USE, VOIDmode, static_chain_rtx));
|
||||
emit_insn (gen_goto_handler_and_restore ());
|
||||
emit_insn (gen_goto_handler_and_restore (labreg));
|
||||
emit_barrier ();
|
||||
DONE;
|
||||
}")
|
||||
@ -6029,20 +6034,31 @@ if (! TARGET_ARCH64)
|
||||
[(set_attr "type" "misc")])
|
||||
|
||||
(define_insn "goto_handler_and_restore"
|
||||
[(unspec_volatile [(reg:SI 8)] 2)]
|
||||
"! TARGET_V9"
|
||||
"jmp %%o0+0\;restore"
|
||||
[(unspec_volatile [(match_operand:SI 0 "register_operand" "=r")] 2)]
|
||||
""
|
||||
"jmp %0+0\;restore"
|
||||
[(set_attr "type" "misc")
|
||||
(set_attr "length" "2")])
|
||||
|
||||
(define_insn "goto_handler_and_restore_v9"
|
||||
[(unspec_volatile [(reg:SI 8)
|
||||
(match_operand:SI 0 "register_operand" "=r,r")
|
||||
(match_operand:SI 1 "const_int_operand" "I,n")] 3)]
|
||||
"TARGET_V9"
|
||||
[(unspec_volatile [(match_operand:SI 0 "register_operand" "=r,r")
|
||||
(match_operand:SI 1 "register_operand" "=r,r")
|
||||
(match_operand:SI 2 "const_int_operand" "I,n")] 3)]
|
||||
"TARGET_V9 && ! TARGET_ARCH64"
|
||||
"@
|
||||
return %%o0+0\;mov %1,%Y0
|
||||
sethi %%hi(%1),%0\;return %%o0+0\;or %Y0,%%lo(%1),%Y0"
|
||||
return %0+0\;mov %2,%Y1
|
||||
sethi %%hi(%2),%1\;return %0+0\;or %Y1,%%lo(%2),%Y1"
|
||||
[(set_attr "type" "misc")
|
||||
(set_attr "length" "2,3")])
|
||||
|
||||
(define_insn "*goto_handler_and_restore_v9_sp64"
|
||||
[(unspec_volatile [(match_operand:DI 0 "register_operand" "=r,r")
|
||||
(match_operand:DI 1 "register_operand" "=r,r")
|
||||
(match_operand:SI 2 "const_int_operand" "I,n")] 3)]
|
||||
"TARGET_V9 && TARGET_ARCH64"
|
||||
"@
|
||||
return %0+0\;mov %2,%Y1
|
||||
sethi %%hi(%2),%1\;return %0+0\;or %Y1,%%lo(%2),%Y1"
|
||||
[(set_attr "type" "misc")
|
||||
(set_attr "length" "2,3")])
|
||||
|
||||
|
@ -33,6 +33,16 @@ Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "svr4.h"
|
||||
|
||||
/* ??? Put back the SIZE_TYPE/PTRDIFF_TYPE definitions set by sparc.h.
|
||||
Why, exactly, is svr4.h messing with this? Seems like the chip
|
||||
would know best. */
|
||||
|
||||
#undef SIZE_TYPE
|
||||
#define SIZE_TYPE (TARGET_ARCH64 ? "long unsigned int" : "unsigned int")
|
||||
|
||||
#undef PTRDIFF_TYPE
|
||||
#define PTRDIFF_TYPE (TARGET_ARCH64 ? "long int" : "int")
|
||||
|
||||
/* Undefined some symbols which are defined in "svr4.h" but which are
|
||||
appropriate only for typical svr4 systems, but not for the specific
|
||||
case of svr4 running on a Sparc. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user