-/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004
+/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005
Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
#include "config.h"
#include "system.h"
/* The number of nop instructions in frv_nops[]. */
static unsigned int frv_num_nops;
+/* Information about one __builtin_read or __builtin_write access, or
+ the combination of several such accesses. The most general value
+ is all-zeros (an unknown access to an unknown address). */
+struct frv_io {
+ /* The type of access. FRV_IO_UNKNOWN means the access can be either
+ a read or a write. */
+ enum { FRV_IO_UNKNOWN, FRV_IO_READ, FRV_IO_WRITE } type;
+
+ /* The constant address being accessed, or zero if not known. */
+ HOST_WIDE_INT const_address;
+
+ /* The run-time address, as used in operand 0 of the membar pattern. */
+ rtx var_address;
+};
+
/* Return true if instruction INSN should be packed with the following
instruction. */
#define PACKING_FLAG_P(INSN) (GET_MODE (INSN) == TImode)
REG < REGNO (X) + HARD_REGNO_NREGS (REGNO (X), GET_MODE (X)); \
REG++)
-/* Information about a relocation unspec. SYMBOL is the relocation symbol
- (a SYMBOL_REF or LABEL_REF), RELOC is the type of relocation and OFFSET
- is the constant addend. */
-struct frv_unspec {
- rtx symbol;
- int reloc;
- HOST_WIDE_INT offset;
+/* This structure contains machine specific function data. */
+struct machine_function GTY(())
+{
+ /* True if we have created an rtx that relies on the stack frame. */
+ int frame_needed;
+
+ /* True if this function contains at least one __builtin_{read,write}*. */
+ bool has_membar_p;
};
/* Temporary register allocation support structure. */
/* Cached value of frv_stack_info. */
static frv_stack_t *frv_stack_cache = (frv_stack_t *)0;
-/* -mbranch-cost= support */
-const char *frv_branch_cost_string;
-int frv_branch_cost_int = DEFAULT_BRANCH_COST;
-
/* -mcpu= support */
-const char *frv_cpu_string; /* -mcpu= option */
frv_cpu_t frv_cpu_type = CPU_TYPE; /* value of -mcpu= */
-/* -mcond-exec-insns= support */
-const char *frv_condexec_insns_str; /* -mcond-exec-insns= option */
-int frv_condexec_insns = DEFAULT_CONDEXEC_INSNS; /* value of -mcond-exec-insns*/
-
-/* -mcond-exec-temps= support */
-const char *frv_condexec_temps_str; /* -mcond-exec-temps= option */
-int frv_condexec_temps = DEFAULT_CONDEXEC_TEMPS; /* value of -mcond-exec-temps*/
-
-/* -msched-lookahead=n */
-const char *frv_sched_lookahead_str; /* -msched-lookahead=n */
-int frv_sched_lookahead = 4; /* -msched-lookahead=n */
-
/* Forward references */
+
+static bool frv_handle_option (size_t, const char *, int);
static int frv_default_flags_for_cpu (void);
static int frv_string_begins_with (tree, const char *);
static FRV_INLINE bool frv_small_data_reloc_p (rtx, int);
-static FRV_INLINE bool frv_const_unspec_p (rtx, struct frv_unspec *);
static void frv_print_operand_memory_reference_reg
(FILE *, rtx);
static void frv_print_operand_memory_reference (FILE *, rtx, int);
static int frv_print_operand_jump_hint (rtx);
+static const char *comparison_string (enum rtx_code, rtx);
static FRV_INLINE int frv_regno_ok_for_base_p (int, int);
static rtx single_set_pattern (rtx);
static int frv_function_contains_far_jump (void);
static void frv_frame_access_standard_regs (enum frv_stack_op,
frv_stack_t *);
static struct machine_function *frv_init_machine_status (void);
-static int frv_legitimate_memory_operand (rtx, enum machine_mode, int);
static rtx frv_int_to_acc (enum insn_code, int, rtx);
static enum machine_mode frv_matching_accg_mode (enum machine_mode);
static rtx frv_read_argument (tree *);
static int frv_check_constant_argument (enum insn_code, int, rtx);
static rtx frv_legitimize_target (enum insn_code, rtx);
static rtx frv_legitimize_argument (enum insn_code, int, rtx);
+static rtx frv_legitimize_tls_address (rtx, enum tls_model);
static rtx frv_expand_set_builtin (enum insn_code, tree, rtx);
static rtx frv_expand_unop_builtin (enum insn_code, tree, rtx);
static rtx frv_expand_binop_builtin (enum insn_code, tree, rtx);
static bool frv_function_ok_for_sibcall (tree, tree);
static rtx frv_struct_value_rtx (tree, int);
static bool frv_must_pass_in_stack (enum machine_mode mode, tree type);
+static int frv_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
+ tree, bool);
+static void frv_output_dwarf_dtprel (FILE *, int, rtx)
+ ATTRIBUTE_UNUSED;
+\f
+/* Allow us to easily change the default for -malloc-cc. */
+#ifndef DEFAULT_NO_ALLOC_CC
+#define MASK_DEFAULT_ALLOC_CC MASK_ALLOC_CC
+#else
+#define MASK_DEFAULT_ALLOC_CC 0
+#endif
\f
/* Initialize the GCC target structure. */
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE frv_function_epilogue
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER frv_assemble_integer
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS \
+ (MASK_DEFAULT_ALLOC_CC \
+ | MASK_COND_MOVE \
+ | MASK_SCC \
+ | MASK_COND_EXEC \
+ | MASK_VLIW_BRANCH \
+ | MASK_MULTI_CE \
+ | MASK_NESTED_CE)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION frv_handle_option
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS frv_init_builtins
#undef TARGET_EXPAND_BUILTIN
#undef TARGET_CANNOT_FORCE_CONST_MEM
#define TARGET_CANNOT_FORCE_CONST_MEM frv_cannot_force_const_mem
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS HAVE_AS_TLS
+
#undef TARGET_STRUCT_VALUE_RTX
#define TARGET_STRUCT_VALUE_RTX frv_struct_value_rtx
#undef TARGET_MUST_PASS_IN_STACK
#define TARGET_MUST_PASS_IN_STACK frv_must_pass_in_stack
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE hook_pass_by_reference_must_pass_in_stack
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES frv_arg_partial_bytes
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS frv_expand_builtin_saveregs
#undef TARGET_MACHINE_DEPENDENT_REORG
#define TARGET_MACHINE_DEPENDENT_REORG frv_reorg
+#if HAVE_AS_TLS
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL frv_output_dwarf_dtprel
+#endif
+
struct gcc_target targetm = TARGET_INITIALIZER;
+
+#define FRV_SYMBOL_REF_TLS_P(RTX) \
+ (GET_CODE (RTX) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (RTX) != 0)
+
\f
/* Any function call that satisfies the machine-independent
requirements is eligible on FR-V. */
/* Return true if X is a valid relocation unspec. If it is, fill in UNSPEC
appropriately. */
-static FRV_INLINE bool
+bool
frv_const_unspec_p (rtx x, struct frv_unspec *unspec)
{
if (GET_CODE (x) == CONST)
return TARGET_FDPIC;
}
\f
+/* Implement TARGET_HANDLE_OPTION. */
+
+static bool
+frv_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+{
+ switch (code)
+ {
+ case OPT_mcpu_:
+ if (strcmp (arg, "simple") == 0)
+ frv_cpu_type = FRV_CPU_SIMPLE;
+ else if (strcmp (arg, "tomcat") == 0)
+ frv_cpu_type = FRV_CPU_TOMCAT;
+ else if (strcmp (arg, "fr550") == 0)
+ frv_cpu_type = FRV_CPU_FR550;
+ else if (strcmp (arg, "fr500") == 0)
+ frv_cpu_type = FRV_CPU_FR500;
+ else if (strcmp (arg, "fr450") == 0)
+ frv_cpu_type = FRV_CPU_FR450;
+ else if (strcmp (arg, "fr405") == 0)
+ frv_cpu_type = FRV_CPU_FR405;
+ else if (strcmp (arg, "fr400") == 0)
+ frv_cpu_type = FRV_CPU_FR400;
+ else if (strcmp (arg, "fr300") == 0)
+ frv_cpu_type = FRV_CPU_FR300;
+ else if (strcmp (arg, "frv") == 0)
+ frv_cpu_type = FRV_CPU_GENERIC;
+ else
+ return false;
+ return true;
+
+ default:
+ return true;
+ }
+}
+
static int
frv_default_flags_for_cpu (void)
{
case FRV_CPU_FR300:
case FRV_CPU_SIMPLE:
return MASK_DEFAULT_SIMPLE;
+
+ default:
+ gcc_unreachable ();
}
- abort ();
}
/* Sometimes certain combinations of command options do not make
int regno;
unsigned int i;
- /* Set the cpu type. */
- if (frv_cpu_string)
- {
- if (strcmp (frv_cpu_string, "simple") == 0)
- frv_cpu_type = FRV_CPU_SIMPLE;
-
- else if (strcmp (frv_cpu_string, "tomcat") == 0)
- frv_cpu_type = FRV_CPU_TOMCAT;
-
- else if (strncmp (frv_cpu_string, "fr", sizeof ("fr")-1) != 0)
- error ("Unknown cpu: -mcpu=%s", frv_cpu_string);
-
- else
- {
- const char *p = frv_cpu_string + sizeof ("fr") - 1;
- if (strcmp (p, "550") == 0)
- frv_cpu_type = FRV_CPU_FR550;
-
- else if (strcmp (p, "500") == 0)
- frv_cpu_type = FRV_CPU_FR500;
-
- else if (strcmp (p, "450") == 0)
- frv_cpu_type = FRV_CPU_FR450;
-
- else if (strcmp (p, "405") == 0)
- frv_cpu_type = FRV_CPU_FR405;
-
- else if (strcmp (p, "400") == 0)
- frv_cpu_type = FRV_CPU_FR400;
-
- else if (strcmp (p, "300") == 0)
- frv_cpu_type = FRV_CPU_FR300;
-
- else if (strcmp (p, "v") == 0)
- frv_cpu_type = FRV_CPU_GENERIC;
-
- else
- error ("Unknown cpu: -mcpu=%s", frv_cpu_string);
- }
- }
-
target_flags |= (frv_default_flags_for_cpu () & ~target_flags_explicit);
/* -mlibrary-pic sets -fPIC and -G0 and also suppresses warnings from the
}
}
- /* Change the branch cost value. */
- if (frv_branch_cost_string)
- frv_branch_cost_int = atoi (frv_branch_cost_string);
-
- /* Change the # of insns to be converted to conditional execution. */
- if (frv_condexec_insns_str)
- frv_condexec_insns = atoi (frv_condexec_insns_str);
-
- /* Change # of temporary registers used to hold integer constants. */
- if (frv_condexec_temps_str)
- frv_condexec_temps = atoi (frv_condexec_temps_str);
-
- /* Change scheduling look ahead. */
- if (frv_sched_lookahead_str)
- frv_sched_lookahead = atoi (frv_sched_lookahead_str);
-
/* A C expression whose value is a register class containing hard
register REGNO. In general there is more than one such class;
choose a class which is "minimal", meaning that no smaller class
if (GPR_P (regno))
{
int gpr_reg = regno - GPR_FIRST;
- if ((gpr_reg & 3) == 0)
+
+ if (gpr_reg == GR8_REG)
+ class = GR8_REGS;
+
+ else if (gpr_reg == GR9_REG)
+ class = GR9_REGS;
+
+ else if (gpr_reg == GR14_REG)
+ class = FDPIC_FPTR_REGS;
+
+ else if (gpr_reg == FDPIC_REGNO)
+ class = FDPIC_REGS;
+
+ else if ((gpr_reg & 3) == 0)
class = QUAD_REGS;
else if ((gpr_reg & 1) == 0)
if ((target_flags_explicit & MASK_LINKED_FP) == 0)
target_flags |= MASK_LINKED_FP;
+ if ((target_flags_explicit & MASK_OPTIMIZE_MEMBAR) == 0)
+ target_flags |= MASK_OPTIMIZE_MEMBAR;
+
for (i = 0; i < ARRAY_SIZE (frv_unit_names); i++)
frv_unit_codes[i] = get_cpu_unit_code (frv_unit_names[i]);
You should not use this macro to change options that are not
machine-specific. These should uniformly selected by the same optimization
- level on all supported machines. Use this macro to enable machbine-specific
+ level on all supported machines. Use this macro to enable machine-specific
optimizations.
*Do not examine `write_symbols' in this macro!* The debugging options are
rtx insn;
/* Just to check that the above comment is true. */
- if (regs_ever_live[GPR_FIRST + 3])
- abort ();
+ gcc_assert (!regs_ever_live[GPR_FIRST + 3]);
/* Generate the instruction that saves the link register. */
fprintf (file, "\tmovsg lr,gr3\n");
regno = 0;
if (regno == orig_regno)
{
- if (no_abort)
- return NULL_RTX;
- else
- abort ();
+ gcc_assert (no_abort);
+ return NULL_RTX;
}
}
memset (&frv_ifcvt.tmp_reg, 0, sizeof (frv_ifcvt.tmp_reg));
/* Release the bitmap of created insns. */
- BITMAP_XFREE (frv_ifcvt.scratch_insns_bitmap);
+ BITMAP_FREE (frv_ifcvt.scratch_insns_bitmap);
}
\f
- info->pretend_size);
else
- abort ();
+ gcc_unreachable ();
if (TARGET_DEBUG_STACK)
fprintf (stderr, "Eliminate %s to %s by adding %d\n",
debug_rtx (nextarg);
}
- t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
- make_tree (ptr_type_node, nextarg));
+ t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist,
+ make_tree (ptr_type_node, nextarg));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
if (! constp)
return FALSE;
- /* If this is not a fixed size alignment, abort. */
- if (GET_CODE (align_rtx) != CONST_INT)
- abort ();
+ /* This should be a fixed size alignment. */
+ gcc_assert (GET_CODE (align_rtx) == CONST_INT);
align = INTVAL (align_rtx);
operands[0] is the destination
operands[1] is the length
- operands[2] is the alignment */
+ operands[3] is the alignment */
int
frv_expand_block_clear (rtx operands[])
{
rtx orig_dest = operands[0];
rtx bytes_rtx = operands[1];
- rtx align_rtx = operands[2];
+ rtx align_rtx = operands[3];
int constp = (GET_CODE (bytes_rtx) == CONST_INT);
int align;
int bytes;
if (! constp)
return FALSE;
- /* If this is not a fixed size alignment, abort. */
- if (GET_CODE (align_rtx) != CONST_INT)
- abort ();
+ /* This should be a fixed size alignment. */
+ gcc_assert (GET_CODE (align_rtx) == CONST_INT);
align = INTVAL (align_rtx);
break;
}
- fatal_insn ("Bad insn to frv_print_operand_address:", x);
+ fatal_insn ("bad insn to frv_print_operand_address:", x);
}
\f
if (GPR_P (regno))
fputs (reg_names[regno], stream);
else
- fatal_insn ("Bad register to frv_print_operand_memory_reference_reg:", x);
+ fatal_insn ("bad register to frv_print_operand_memory_reference_reg:", x);
}
/* Print a memory reference suitable for the ld/st instructions. */
break;
default:
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
break;
}
if (!x1)
x1 = const0_rtx;
else if (GET_CODE (x1) != CONST_INT)
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
}
fputs ("@(", stream);
else if (GET_CODE (x0) == REG || GET_CODE (x0) == SUBREG)
frv_print_operand_memory_reference_reg (stream, x0);
else
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
fputs (",", stream);
if (!x1)
case CONST:
if (!frv_const_unspec_p (x1, &unspec))
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x1);
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x1);
frv_output_const_unspec (stream, &unspec);
break;
default:
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
}
}
HOST_WIDE_INT prob = -1;
enum { UNKNOWN, BACKWARD, FORWARD } jump_type = UNKNOWN;
- if (GET_CODE (insn) != JUMP_INSN)
- abort ();
+ gcc_assert (GET_CODE (insn) == JUMP_INSN);
/* Assume any non-conditional jump is likely. */
if (! any_condjump_p (insn))
}
\f
+/* Return the comparison operator to use for CODE given that the ICC
+ register is OP0. */
+
+static const char *
+comparison_string (enum rtx_code code, rtx op0)
+{
+ bool is_nz_p = GET_MODE (op0) == CC_NZmode;
+ switch (code)
+ {
+ default: output_operand_lossage ("bad condition code");
+ case EQ: return "eq";
+ case NE: return "ne";
+ case LT: return is_nz_p ? "n" : "lt";
+ case LE: return "le";
+ case GT: return "gt";
+ case GE: return is_nz_p ? "p" : "ge";
+ case LTU: return is_nz_p ? "no" : "c";
+ case LEU: return is_nz_p ? "eq" : "ls";
+ case GTU: return is_nz_p ? "ne" : "hi";
+ case GEU: return is_nz_p ? "ra" : "nc";
+ }
+}
+
/* Print an operand to an assembler instruction.
`%' followed by a letter and a digit says to output an operand in an
value = CONST_DOUBLE_LOW (x);
else
- fatal_insn ("Bad insn in frv_print_operand, bad const_double", x);
+ fatal_insn ("bad insn in frv_print_operand, bad const_double", x);
}
else
case 'C':
/* Print appropriate test for integer branch false operation. */
- switch (GET_CODE (x))
- {
- default:
- fatal_insn ("Bad insn to frv_print_operand, 'C' modifier:", x);
-
- case EQ: fputs ("ne", file); break;
- case NE: fputs ("eq", file); break;
- case LT: fputs ("ge", file); break;
- case LE: fputs ("gt", file); break;
- case GT: fputs ("le", file); break;
- case GE: fputs ("lt", file); break;
- case LTU: fputs ("nc", file); break;
- case LEU: fputs ("hi", file); break;
- case GTU: fputs ("ls", file); break;
- case GEU: fputs ("c", file); break;
- }
+ fputs (comparison_string (reverse_condition (GET_CODE (x)),
+ XEXP (x, 0)), file);
break;
- /* case 'c': print a constant without the constant prefix. If
- CONSTANT_ADDRESS_P(x) is not true, PRINT_OPERAND is called. */
-
case 'c':
/* Print appropriate test for integer branch true operation. */
- switch (GET_CODE (x))
- {
- default:
- fatal_insn ("Bad insn to frv_print_operand, 'c' modifier:", x);
-
- case EQ: fputs ("eq", file); break;
- case NE: fputs ("ne", file); break;
- case LT: fputs ("lt", file); break;
- case LE: fputs ("le", file); break;
- case GT: fputs ("gt", file); break;
- case GE: fputs ("ge", file); break;
- case LTU: fputs ("c", file); break;
- case LEU: fputs ("ls", file); break;
- case GTU: fputs ("hi", file); break;
- case GEU: fputs ("nc", file); break;
- }
+ fputs (comparison_string (GET_CODE (x), XEXP (x, 0)), file);
break;
case 'e':
fputs ("0", file);
else
- fatal_insn ("Bad insn to frv_print_operand, 'e' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'e' modifier:", x);
break;
case 'F':
switch (GET_CODE (x))
{
default:
- fatal_insn ("Bad insn to frv_print_operand, 'F' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'F' modifier:", x);
case EQ: fputs ("ne", file); break;
case NE: fputs ("eq", file); break;
switch (GET_CODE (x))
{
default:
- fatal_insn ("Bad insn to frv_print_operand, 'f' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'f' modifier:", x);
case EQ: fputs ("eq", file); break;
case NE: fputs ("ne", file); break;
case 'g':
/* Print appropriate GOT function. */
if (GET_CODE (x) != CONST_INT)
- fatal_insn ("Bad insn to frv_print_operand, 'g' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'g' modifier:", x);
fputs (unspec_got_name (INTVAL (x)), file);
break;
if (GET_CODE (x) == REG)
fputs (reg_names[ REGNO (x)+1 ], file);
else
- fatal_insn ("Bad insn to frv_print_operand, 'L' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'L' modifier:", x);
break;
/* case 'l': print a LABEL_REF. */
switch (GET_CODE (x))
{
default:
- fatal_insn ("Bad insn to frv_print_operand, 'M/N' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'M/N' modifier:", x);
case MEM:
frv_print_operand_memory_reference (file, XEXP (x, 0), offset);
switch (GET_CODE (x))
{
default:
- fatal_insn ("Bad insn to frv_print_operand, 'O' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'O' modifier:", x);
case PLUS: fputs ("add", file); break;
case MINUS: fputs ("sub", file); break;
case 'P':
/* Print PIC label using operand as the number. */
if (GET_CODE (x) != CONST_INT)
- fatal_insn ("Bad insn to frv_print_operand, P modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, P modifier:", x);
fprintf (file, ".LCF%ld", (long)INTVAL (x));
break;
fputs (reg_names [REGNO (x)], file);
else
- fatal_insn ("Bad insn in frv_print_operand, z case", x);
+ fatal_insn ("bad insn in frv_print_operand, z case", x);
break;
case 'x':
frv_print_operand_address (file, x);
else
- fatal_insn ("Bad insn in frv_print_operand, 0 case", x);
+ fatal_insn ("bad insn in frv_print_operand, 0 case", x);
break;
used by the caller for this argument; likewise `FUNCTION_INCOMING_ARG', for
the called function. */
-int
-frv_function_arg_partial_nregs (CUMULATIVE_ARGS *cum,
- enum machine_mode mode,
- tree type ATTRIBUTE_UNUSED,
- int named ATTRIBUTE_UNUSED)
+static int
+frv_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type ATTRIBUTE_UNUSED, bool named ATTRIBUTE_UNUSED)
{
enum machine_mode xmode = (mode == BLKmode) ? SImode : mode;
int bytes = GET_MODE_SIZE (xmode);
ret = ((arg_num <= LAST_ARG_REGNUM && arg_num + words > LAST_ARG_REGNUM+1)
? LAST_ARG_REGNUM - arg_num + 1
: 0);
+ ret *= UNITS_PER_WORD;
if (TARGET_DEBUG_ARG && ret)
- fprintf (stderr, "function_arg_partial_nregs: %d\n", ret);
+ fprintf (stderr, "frv_arg_partial_bytes: %d\n", ret);
return ret;
-
}
\f
HOST_WIDE_INT value;
unsigned regno0;
+ if (FRV_SYMBOL_REF_TLS_P (x))
+ return 0;
+
switch (GET_CODE (x))
{
default:
return ret;
}
+/* Given an ADDR, generate code to inline the PLT. */
+static rtx
+gen_inlined_tls_plt (rtx addr)
+{
+ rtx retval, dest;
+ rtx picreg = get_hard_reg_initial_val (Pmode, FDPIC_REG);
+
+
+ dest = gen_reg_rtx (DImode);
+
+ if (flag_pic == 1)
+ {
+ /*
+ -fpic version:
+
+ lddi.p @(gr15, #gottlsdesc12(ADDR)), gr8
+ calll #gettlsoff(ADDR)@(gr8, gr0)
+ */
+ emit_insn (gen_tls_lddi (dest, addr, picreg));
+ }
+ else
+ {
+ /*
+ -fPIC version:
+
+ sethi.p #gottlsdeschi(ADDR), gr8
+ setlo #gottlsdesclo(ADDR), gr8
+ ldd #tlsdesc(ADDR)@(gr15, gr8), gr8
+ calll #gettlsoff(ADDR)@(gr8, gr0)
+ */
+ rtx reguse = gen_reg_rtx (Pmode);
+ emit_insn (gen_tlsoff_hilo (reguse, addr, GEN_INT (R_FRV_GOTTLSDESCHI)));
+ emit_insn (gen_tls_tlsdesc_ldd (dest, picreg, reguse, addr));
+ }
+
+ retval = gen_reg_rtx (Pmode);
+ emit_insn (gen_tls_indirect_call (retval, addr, dest, picreg));
+ return retval;
+}
+
+/* Emit a TLSMOFF or TLSMOFF12 offset, depending on -mTLS. Returns
+ the destination address. */
+static rtx
+gen_tlsmoff (rtx addr, rtx reg)
+{
+ rtx dest = gen_reg_rtx (Pmode);
+
+ if (TARGET_BIG_TLS)
+ {
+ /* sethi.p #tlsmoffhi(x), grA
+ setlo #tlsmofflo(x), grA
+ */
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_tlsoff_hilo (dest, addr,
+ GEN_INT (R_FRV_TLSMOFFHI)));
+ dest = gen_rtx_PLUS (Pmode, dest, reg);
+ }
+ else
+ {
+ /* addi grB, #tlsmoff12(x), grC
+ -or-
+ ld/st @(grB, #tlsmoff12(x)), grC
+ */
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_symGOTOFF2reg_i (dest, addr, reg,
+ GEN_INT (R_FRV_TLSMOFF12)));
+ }
+ return dest;
+}
+
+/* Generate code for a TLS address. */
+static rtx
+frv_legitimize_tls_address (rtx addr, enum tls_model model)
+{
+ rtx dest, tp = gen_rtx_REG (Pmode, 29);
+ rtx picreg = get_hard_reg_initial_val (Pmode, 15);
+
+ switch (model)
+ {
+ case TLS_MODEL_INITIAL_EXEC:
+ if (flag_pic == 1)
+ {
+ /* -fpic version.
+ ldi @(gr15, #gottlsoff12(x)), gr5
+ */
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_tls_load_gottlsoff12 (dest, addr, picreg));
+ dest = gen_rtx_PLUS (Pmode, tp, dest);
+ }
+ else
+ {
+ /* -fPIC or anything else.
+
+ sethi.p #gottlsoffhi(x), gr14
+ setlo #gottlsofflo(x), gr14
+ ld #tlsoff(x)@(gr15, gr14), gr9
+ */
+ rtx tmp = gen_reg_rtx (Pmode);
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_tlsoff_hilo (tmp, addr,
+ GEN_INT (R_FRV_GOTTLSOFF_HI)));
+
+ emit_insn (gen_tls_tlsoff_ld (dest, picreg, tmp, addr));
+ dest = gen_rtx_PLUS (Pmode, tp, dest);
+ }
+ break;
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ {
+ rtx reg, retval;
+
+ if (TARGET_INLINE_PLT)
+ retval = gen_inlined_tls_plt (GEN_INT (0));
+ else
+ {
+ /* call #gettlsoff(0) */
+ retval = gen_reg_rtx (Pmode);
+ emit_insn (gen_call_gettlsoff (retval, GEN_INT (0), picreg));
+ }
+
+ reg = gen_reg_rtx (Pmode);
+ emit_insn (gen_rtx_SET (VOIDmode, reg,
+ gen_rtx_PLUS (Pmode,
+ retval, tp)));
+
+ dest = gen_tlsmoff (addr, reg);
+
+ /*
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_tlsoff_hilo (dest, addr,
+ GEN_INT (R_FRV_TLSMOFFHI)));
+ dest = gen_rtx_PLUS (Pmode, dest, reg);
+ */
+ break;
+ }
+ case TLS_MODEL_LOCAL_EXEC:
+ dest = gen_tlsmoff (addr, gen_rtx_REG (Pmode, 29));
+ break;
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ {
+ rtx retval;
+
+ if (TARGET_INLINE_PLT)
+ retval = gen_inlined_tls_plt (addr);
+ else
+ {
+ /* call #gettlsoff(x) */
+ retval = gen_reg_rtx (Pmode);
+ emit_insn (gen_call_gettlsoff (retval, addr, picreg));
+ }
+ dest = gen_rtx_PLUS (Pmode, retval, tp);
+ break;
+ }
+ default:
+ gcc_unreachable ();
+ }
+
+ return dest;
+}
+
rtx
-frv_legitimize_address (rtx x ATTRIBUTE_UNUSED,
+frv_legitimize_address (rtx x,
rtx oldx ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED)
{
+ if (GET_CODE (x) == SYMBOL_REF)
+ {
+ enum tls_model model = SYMBOL_REF_TLS_MODEL (x);
+ if (model != 0)
+ return frv_legitimize_tls_address (x, model);
+ }
+
return NULL_RTX;
}
\f
case R_FRV_GPREL12: return "gprel12";
case R_FRV_GPRELHI: return "gprelhi";
case R_FRV_GPRELLO: return "gprello";
- default: abort ();
+ case R_FRV_GOTTLSOFF_HI: return "gottlsoffhi";
+ case R_FRV_GOTTLSOFF_LO: return "gottlsofflo";
+ case R_FRV_TLSMOFFHI: return "tlsmoffhi";
+ case R_FRV_TLSMOFFLO: return "tlsmofflo";
+ case R_FRV_TLSMOFF12: return "tlsmoff12";
+ case R_FRV_TLSDESCHI: return "tlsdeschi";
+ case R_FRV_TLSDESCLO: return "tlsdesclo";
+ case R_FRV_GOTTLSDESCHI: return "gottlsdeschi";
+ case R_FRV_GOTTLSDESCLO: return "gottlsdesclo";
+ default: gcc_unreachable ();
}
}
/* Return 1 if operand is a valid FRV address. CONDEXEC_P is true if
the operand is used by a predicated instruction. */
-static int
+int
frv_legitimate_memory_operand (rtx op, enum machine_mode mode, int condexec_p)
{
return ((GET_MODE (op) == mode || mode == VOIDmode)
c = gen_call_fdpicdi (picreg, const0_rtx, lr);
emit_call_insn (c);
}
-
-/* An address operand that may use a pair of registers, an addressing
- mode that we reject in general. */
-
-int
-ldd_address_operand (rtx x, enum machine_mode mode)
-{
- if (GET_MODE (x) != mode && GET_MODE (x) != VOIDmode)
- return FALSE;
-
- return frv_legitimate_address_p (DImode, x, reload_completed, FALSE, TRUE);
-}
-
-int
-fdpic_fptr_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
- if (GET_CODE (op) != REG)
- return FALSE;
- if (REGNO (op) != FDPIC_FPTR_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
- return FALSE;
- return TRUE;
-}
\f
-/* Return 1 is OP is a memory operand, or will be turned into one by
- reload. */
-
-int
-frv_load_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (reload_in_progress)
- {
- rtx tmp = op;
- if (GET_CODE (tmp) == SUBREG)
- tmp = SUBREG_REG (tmp);
- if (GET_CODE (tmp) == REG
- && REGNO (tmp) >= FIRST_PSEUDO_REGISTER)
- op = reg_equiv_memory_loc[REGNO (tmp)];
- }
-
- return op && memory_operand (op, mode);
-}
-
-
-/* Return 1 if operand is a GPR register or a FPR register. */
-
-int
-gpr_or_fpr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- return FALSE;
-}
-
-/* Return 1 if operand is a GPR register or 12 bit signed immediate. */
+/* Look for a SYMBOL_REF of a function in an rtx. We always want to
+ process these separately from any offsets, such that we add any
+ offsets to the function descriptor (the actual pointer), not to the
+ function address. */
-int
-gpr_or_int12_operand (rtx op, enum machine_mode mode)
+static bool
+frv_function_symbol_referenced_p (rtx x)
{
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -2048, 2047);
+ const char *format;
+ int length;
+ int j;
- if (got12_operand (op, mode))
- return true;
+ if (GET_CODE (x) == SYMBOL_REF)
+ return SYMBOL_REF_FUNCTION_P (x);
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ length = GET_RTX_LENGTH (GET_CODE (x));
+ format = GET_RTX_FORMAT (GET_CODE (x));
- if (GET_CODE (op) == SUBREG)
+ for (j = 0; j < length; ++j)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return 1 if operand is a GPR register, or a FPR register, or a 12 bit
- signed immediate. */
-
-int
-gpr_fpr_or_int12_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -2048, 2047);
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ switch (format[j])
+ {
+ case 'e':
+ if (frv_function_symbol_referenced_p (XEXP (x, j)))
+ return TRUE;
+ break;
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ case 'V':
+ case 'E':
+ if (XVEC (x, j) != 0)
+ {
+ int k;
+ for (k = 0; k < XVECLEN (x, j); ++k)
+ if (frv_function_symbol_referenced_p (XVECEXP (x, j, k)))
+ return TRUE;
+ }
+ break;
- op = SUBREG_REG (op);
+ default:
+ /* Nothing to do. */
+ break;
+ }
}
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- return FALSE;
-}
-
-/* Return 1 if operand is a register or 6 bit signed immediate. */
-
-int
-fpr_or_int6_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -32, 31);
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return FPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return 1 if operand is a register or 10 bit signed immediate. */
-
-int
-gpr_or_int10_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -512, 511);
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return 1 if operand is a register or an integer immediate. */
-
-int
-gpr_or_int_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == CONST_INT)
- return TRUE;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return 1 if operand is a 12 bit signed immediate. */
-
-int
-int12_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
-
- return IN_RANGE_P (INTVAL (op), -2048, 2047);
-}
-
-/* Return 1 if operand is a 6 bit signed immediate. */
-
-int
-int6_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
-
- return IN_RANGE_P (INTVAL (op), -32, 31);
-}
-
-/* Return 1 if operand is a 5 bit signed immediate. */
-
-int
-int5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), -16, 15);
-}
-
-/* Return 1 if operand is a 5 bit unsigned immediate. */
-
-int
-uint5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 31);
-}
-
-/* Return 1 if operand is a 4 bit unsigned immediate. */
-
-int
-uint4_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 15);
-}
-
-/* Return 1 if operand is a 1 bit unsigned immediate (0 or 1). */
-
-int
-uint1_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 1);
-}
-
-/* Return 1 if operand is an integer constant that takes 2 instructions
- to load up and can be split into sethi/setlo instructions.. */
-
-int
-int_2word_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- HOST_WIDE_INT value;
- REAL_VALUE_TYPE rv;
- long l;
-
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case LABEL_REF:
- if (TARGET_FDPIC)
- return FALSE;
-
- return (flag_pic == 0);
-
- case CONST:
- if (flag_pic || TARGET_FDPIC)
- return FALSE;
-
- op = XEXP (op, 0);
- if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
- op = XEXP (op, 0);
- return GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF;
-
- case SYMBOL_REF:
- if (TARGET_FDPIC)
- return FALSE;
-
- /* small data references are already 1 word */
- return (flag_pic == 0) && (! SYMBOL_REF_SMALL_P (op));
-
- case CONST_INT:
- return ! IN_RANGE_P (INTVAL (op), -32768, 32767);
-
- case CONST_DOUBLE:
- if (GET_MODE (op) == SFmode)
- {
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_SINGLE (rv, l);
- value = l;
- return ! IN_RANGE_P (value, -32768, 32767);
- }
- else if (GET_MODE (op) == VOIDmode)
- {
- value = CONST_DOUBLE_LOW (op);
- return ! IN_RANGE_P (value, -32768, 32767);
- }
- break;
- }
-
- return FALSE;
-}
-
-/* Return 1 if operand is a 16 bit unsigned immediate. */
-
-int
-uint16_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
-
- return IN_RANGE_P (INTVAL (op), 0, 0xffff);
-}
-
-/* Return 1 if operand is an integer constant with the bottom 16 bits
- clear. */
-
-int
-upper_int16_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
-
- return ((INTVAL (op) & 0xffff) == 0);
-}
-
-/* Return true if operand is a GPR register. */
-
-int
-integer_register_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return true if operand is a GPR register. Do not allow SUBREG's
- here, in order to prevent a combine bug. */
-
-int
-gpr_no_subreg_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return true if operand is a FPR register. */
-
-int
-fpr_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return FPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return true if operand is an even GPR or FPR register. */
-
-int
-even_reg_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- if (GPR_P (regno))
- return (((regno - GPR_FIRST) & 1) == 0);
-
- if (FPR_P (regno))
- return (((regno - FPR_FIRST) & 1) == 0);
-
- return FALSE;
-}
-
-/* Return true if operand is an odd GPR register. */
-
-int
-odd_reg_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- /* Assume that reload will give us an even register. */
- if (regno >= FIRST_PSEUDO_REGISTER)
- return FALSE;
-
- if (GPR_P (regno))
- return (((regno - GPR_FIRST) & 1) != 0);
-
- if (FPR_P (regno))
- return (((regno - FPR_FIRST) & 1) != 0);
-
- return FALSE;
-}
-
-/* Return true if operand is an even GPR register. */
-
-int
-even_gpr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- if (! GPR_P (regno))
- return FALSE;
-
- return (((regno - GPR_FIRST) & 1) == 0);
-}
-
-/* Return true if operand is an odd GPR register. */
-
-int
-odd_gpr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- /* Assume that reload will give us an even register. */
- if (regno >= FIRST_PSEUDO_REGISTER)
- return FALSE;
-
- if (! GPR_P (regno))
- return FALSE;
-
- return (((regno - GPR_FIRST) & 1) != 0);
-}
-
-/* Return true if operand is a quad aligned FPR register. */
-
-int
-quad_fpr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- if (! FPR_P (regno))
- return FALSE;
-
- return (((regno - FPR_FIRST) & 3) == 0);
-}
-
-/* Return true if operand is an even FPR register. */
-
-int
-even_fpr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- if (! FPR_P (regno))
- return FALSE;
-
- return (((regno - FPR_FIRST) & 1) == 0);
-}
-
-/* Return true if operand is an odd FPR register. */
-
-int
-odd_fpr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- /* Assume that reload will give us an even register. */
- if (regno >= FIRST_PSEUDO_REGISTER)
- return FALSE;
-
- if (! FPR_P (regno))
- return FALSE;
-
- return (((regno - FPR_FIRST) & 1) != 0);
-}
-
-/* Return true if operand is a 2 word memory address that can be loaded in one
- instruction to load or store. We assume the stack and frame pointers are
- suitably aligned, and variables in the small data area. FIXME -- at some we
- should recognize other globals and statics. We can't assume that any old
- pointer is aligned, given that arguments could be passed on an odd word on
- the stack and the address taken and passed through to another function. */
-
-int
-dbl_memory_one_insn_operand (rtx op, enum machine_mode mode)
-{
- rtx addr;
- rtx addr_reg;
-
- if (! TARGET_DWORD)
- return FALSE;
-
- if (GET_CODE (op) != MEM)
- return FALSE;
-
- if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD)
- return FALSE;
-
- addr = XEXP (op, 0);
- if (GET_CODE (addr) == REG)
- addr_reg = addr;
-
- else if (GET_CODE (addr) == PLUS)
- {
- rtx addr0 = XEXP (addr, 0);
- rtx addr1 = XEXP (addr, 1);
-
- if (GET_CODE (addr0) != REG)
- return FALSE;
-
- if (got12_operand (addr1, VOIDmode))
- return TRUE;
-
- if (GET_CODE (addr1) != CONST_INT)
- return FALSE;
-
- if ((INTVAL (addr1) & 7) != 0)
- return FALSE;
-
- addr_reg = addr0;
- }
-
- else
- return FALSE;
-
- if (addr_reg == frame_pointer_rtx || addr_reg == stack_pointer_rtx)
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operand is a 2 word memory address that needs to
- use two instructions to load or store. */
-
-int
-dbl_memory_two_insn_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) != MEM)
- return FALSE;
-
- if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD)
- return FALSE;
-
- if (! TARGET_DWORD)
- return TRUE;
-
- return ! dbl_memory_one_insn_operand (op, mode);
-}
-
-/* Return true if operand is something that can be an output for a move
- operation. */
-
-int
-move_destination_operand (rtx op, enum machine_mode mode)
-{
- rtx subreg;
- enum rtx_code code;
-
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, FALSE, FALSE);
-
- return (code == REG);
-
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return TRUE;
-
- case MEM:
- return frv_legitimate_memory_operand (op, mode, FALSE);
- }
-
- return FALSE;
-}
-
-/* Look for a SYMBOL_REF of a function in an rtx. We always want to
- process these separately from any offsets, such that we add any
- offsets to the function descriptor (the actual pointer), not to the
- function address. */
-
-static bool
-frv_function_symbol_referenced_p (rtx x)
-{
- const char *format;
- int length;
- int j;
-
- if (GET_CODE (x) == SYMBOL_REF)
- return SYMBOL_REF_FUNCTION_P (x);
-
- length = GET_RTX_LENGTH (GET_CODE (x));
- format = GET_RTX_FORMAT (GET_CODE (x));
-
- for (j = 0; j < length; ++j)
- {
- switch (format[j])
- {
- case 'e':
- if (frv_function_symbol_referenced_p (XEXP (x, j)))
- return TRUE;
- break;
-
- case 'V':
- case 'E':
- if (XVEC (x, j) != 0)
- {
- int k;
- for (k = 0; k < XVECLEN (x, j); ++k)
- if (frv_function_symbol_referenced_p (XVECEXP (x, j, k)))
- return TRUE;
- }
- break;
-
- default:
- /* Nothing to do. */
- break;
- }
- }
-
- return FALSE;
-}
-
-/* Return true if operand is something that can be an input for a move
- operation. */
-
-int
-move_source_operand (rtx op, enum machine_mode mode)
-{
- rtx subreg;
- enum rtx_code code;
-
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case CONST_INT:
- case CONST_DOUBLE:
- return immediate_operand (op, mode);
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, FALSE, FALSE);
-
- return (code == REG);
-
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return TRUE;
-
- case MEM:
- return frv_legitimate_memory_operand (op, mode, FALSE);
- }
-
- return FALSE;
-}
-
-/* Return true if operand is something that can be an output for a conditional
- move operation. */
-
-int
-condexec_dest_operand (rtx op, enum machine_mode mode)
-{
- rtx subreg;
- enum rtx_code code;
-
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, TRUE, FALSE);
-
- return (code == REG);
-
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return TRUE;
-
- case MEM:
- return frv_legitimate_memory_operand (op, mode, TRUE);
- }
-
- return FALSE;
-}
-
-/* Return true if operand is something that can be an input for a conditional
- move operation. */
-
-int
-condexec_source_operand (rtx op, enum machine_mode mode)
-{
- rtx subreg;
- enum rtx_code code;
-
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case CONST_INT:
- case CONST_DOUBLE:
- return ZERO_P (op);
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, TRUE, FALSE);
-
- return (code == REG);
-
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return TRUE;
-
- case MEM:
- return frv_legitimate_memory_operand (op, mode, TRUE);
- }
-
- return FALSE;
-}
-
-/* Return true if operand is a register of any flavor or a 0 of the
- appropriate type. */
-
-int
-reg_or_0_operand (rtx op, enum machine_mode mode)
-{
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case REG:
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return register_operand (op, mode);
-
- case CONST_INT:
- case CONST_DOUBLE:
- return ZERO_P (op);
- }
-
- return FALSE;
-}
-
-/* Return true if operand is the link register. */
-
-int
-lr_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) != REG)
- return FALSE;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (REGNO (op) != LR_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
- return FALSE;
-
- return TRUE;
-}
-
-/* Return true if operand is the uClinux PIC register. */
-
-int
-fdpic_operand (rtx op, enum machine_mode mode)
-{
- if (!TARGET_FDPIC)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (REGNO (op) != FDPIC_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
- return FALSE;
-
- return TRUE;
-}
-
-int
-got12_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- struct frv_unspec unspec;
-
- if (frv_const_unspec_p (op, &unspec))
- switch (unspec.reloc)
- {
- case R_FRV_GOT12:
- case R_FRV_GOTOFF12:
- case R_FRV_FUNCDESC_GOT12:
- case R_FRV_FUNCDESC_GOTOFF12:
- case R_FRV_GPREL12:
- return true;
- }
- return false;
-}
-
-/* Return true if OP is a valid const-unspec expression. */
-
-int
-const_unspec_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- struct frv_unspec unspec;
-
- return frv_const_unspec_p (op, &unspec);
-}
-/* Return true if operand is a gpr register or a valid memory operation. */
-
-int
-gpr_or_memory_operand (rtx op, enum machine_mode mode)
-{
- return (integer_register_operand (op, mode)
- || frv_legitimate_memory_operand (op, mode, FALSE));
-}
-
-/* Return true if operand is a fpr register or a valid memory operation. */
-
-int
-fpr_or_memory_operand (rtx op, enum machine_mode mode)
-{
- return (fpr_operand (op, mode)
- || frv_legitimate_memory_operand (op, mode, FALSE));
-}
-
-/* Return true if operand is an icc register. */
-
-int
-icc_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return ICC_OR_PSEUDO_P (regno);
-}
-
-/* Return true if operand is an fcc register. */
-
-int
-fcc_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return FCC_OR_PSEUDO_P (regno);
-}
-
-/* Return true if operand is either an fcc or icc register. */
-
-int
-cc_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (CC_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operand is an integer CCR register. */
-
-int
-icr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return ICR_OR_PSEUDO_P (regno);
-}
-
-/* Return true if operand is an fcc register. */
-
-int
-fcr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return FCR_OR_PSEUDO_P (regno);
-}
-
-/* Return true if operand is either an fcc or icc register. */
-
-int
-cr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operand is a memory reference suitable for a call. */
-
-int
-call_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode && GET_CODE (op) != CONST_INT)
- return FALSE;
-
- if (GET_CODE (op) == SYMBOL_REF)
- return !TARGET_LONG_CALLS || SYMBOL_REF_LOCAL_P (op);
-
- /* Note this doesn't allow reg+reg or reg+imm12 addressing (which should
- never occur anyway), but prevents reload from not handling the case
- properly of a call through a pointer on a function that calls
- vfork/setjmp, etc. due to the need to flush all of the registers to stack. */
- return gpr_or_int12_operand (op, mode);
-}
-
-/* Return true if operand is a memory reference suitable for a sibcall. */
-
-int
-sibcall_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode && GET_CODE (op) != CONST_INT)
- return FALSE;
-
- /* Note this doesn't allow reg+reg or reg+imm12 addressing (which should
- never occur anyway), but prevents reload from not handling the case
- properly of a call through a pointer on a function that calls
- vfork/setjmp, etc. due to the need to flush all of the registers to stack. */
- return gpr_or_int12_operand (op, mode);
-}
-
-/* Return true if operator is a kind of relational operator. */
-
-int
-relational_operator (rtx op, enum machine_mode mode)
-{
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case EQ:
- case NE:
- case LE:
- case LT:
- case GE:
- case GT:
- case LEU:
- case LTU:
- case GEU:
- case GTU:
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- switch (GET_MODE (op0))
- {
- default:
- break;
-
- case CCmode:
- case CC_UNSmode:
- return ICC_OR_PSEUDO_P (regno);
-
- case CC_FPmode:
- return FCC_OR_PSEUDO_P (regno);
-
- case CC_CCRmode:
- return CR_OR_PSEUDO_P (regno);
- }
-
- return FALSE;
-}
-
-/* Return true if operator is a signed integer relational operator. */
-
-int
-signed_relational_operator (rtx op, enum machine_mode mode)
-{
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case EQ:
- case NE:
- case LE:
- case LT:
- case GE:
- case GT:
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- if (GET_MODE (op0) == CCmode && ICC_OR_PSEUDO_P (regno))
- return TRUE;
-
- if (GET_MODE (op0) == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operator is a signed integer relational operator. */
-
-int
-unsigned_relational_operator (rtx op, enum machine_mode mode)
-{
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case LEU:
- case LTU:
- case GEU:
- case GTU:
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- if (GET_MODE (op0) == CC_UNSmode && ICC_OR_PSEUDO_P (regno))
- return TRUE;
-
- if (GET_MODE (op0) == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operator is a floating point relational operator. */
-
-int
-float_relational_operator (rtx op, enum machine_mode mode)
-{
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case EQ: case NE:
- case LE: case LT:
- case GE: case GT:
-#if 0
- case UEQ: case UNE:
- case ULE: case ULT:
- case UGE: case UGT:
- case ORDERED:
- case UNORDERED:
-#endif
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- if (GET_MODE (op0) == CC_FPmode && FCC_OR_PSEUDO_P (regno))
- return TRUE;
-
- if (GET_MODE (op0) == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operator is EQ/NE of a conditional execution register. */
-
-int
-ccr_eqne_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case EQ:
- case NE:
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- if (op_mode == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operator is a minimum or maximum operator (both signed and
- unsigned). */
-
-int
-minmax_operator (rtx op, enum machine_mode mode)
-{
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case SMIN:
- case SMAX:
- case UMIN:
- case UMAX:
- break;
- }
-
- if (! integer_register_operand (XEXP (op, 0), mode))
- return FALSE;
-
- if (! gpr_or_int10_operand (XEXP (op, 1), mode))
- return FALSE;
-
- return TRUE;
-}
-
-/* Return true if operator is an integer binary operator that can executed
- conditionally and takes 1 cycle. */
-
-int
-condexec_si_binary_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case PLUS:
- case MINUS:
- case AND:
- case IOR:
- case XOR:
- case ASHIFT:
- case ASHIFTRT:
- case LSHIFTRT:
- return TRUE;
- }
-}
-
-/* Return true if operator is an integer binary operator that can be
- executed conditionally by a media instruction. */
-
-int
-condexec_si_media_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case AND:
- case IOR:
- case XOR:
- return TRUE;
- }
-}
-
-/* Return true if operator is an integer division operator that can executed
- conditionally. */
-
-int
-condexec_si_divide_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case DIV:
- case UDIV:
- return TRUE;
- }
-}
-
-/* Return true if operator is an integer unary operator that can executed
- conditionally. */
-
-int
-condexec_si_unary_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case NEG:
- case NOT:
- return TRUE;
- }
-}
-
-/* Return true if operator is a conversion-type expression that can be
- evaluated conditionally by floating-point instructions. */
-
-int
-condexec_sf_conv_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case NEG:
- case ABS:
- return TRUE;
- }
-}
-
-/* Return true if operator is an addition or subtraction expression.
- Such expressions can be evaluated conditionally by floating-point
- instructions. */
-
-int
-condexec_sf_add_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case PLUS:
- case MINUS:
- return TRUE;
- }
-}
-
-/* Return true if the memory operand is one that can be conditionally
- executed. */
-
-int
-condexec_memory_operand (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
- rtx addr;
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (op_mode)
- {
- default:
- return FALSE;
-
- case QImode:
- case HImode:
- case SImode:
- case SFmode:
- break;
- }
-
- if (GET_CODE (op) != MEM)
- return FALSE;
-
- addr = XEXP (op, 0);
- return frv_legitimate_address_p (mode, addr, reload_completed, TRUE, FALSE);
-}
-
-/* Return true if operator is an integer binary operator that can be combined
- with a setcc operation. Do not allow the arithmetic operations that could
- potentially overflow since the FR-V sets the condition code based on the
- "true" value of the result, not the result after truncating to a 32-bit
- register. */
-
-int
-intop_compare_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case AND:
- case IOR:
- case XOR:
- case ASHIFTRT:
- case LSHIFTRT:
- break;
- }
-
- if (! integer_register_operand (XEXP (op, 0), SImode))
- return FALSE;
-
- if (! gpr_or_int10_operand (XEXP (op, 1), SImode))
- return FALSE;
-
- return TRUE;
-}
-
-/* Return true if operator is an integer binary operator that can be combined
- with a setcc operation inside of a conditional execution. */
-
-int
-condexec_intop_cmp_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case AND:
- case IOR:
- case XOR:
- case ASHIFTRT:
- case LSHIFTRT:
- break;
- }
-
- if (! integer_register_operand (XEXP (op, 0), SImode))
- return FALSE;
-
- if (! integer_register_operand (XEXP (op, 1), SImode))
- return FALSE;
-
- return TRUE;
-}
-
-/* Return 1 if operand is a valid ACC register number. */
-
-int
-acc_operand (rtx op, enum machine_mode mode)
-{
- return ((mode == VOIDmode || mode == GET_MODE (op))
- && REG_P (op) && ACC_P (REGNO (op))
- && ((REGNO (op) - ACC_FIRST) & ~ACC_MASK) == 0);
+
+ return FALSE;
}
-/* Return 1 if operand is a valid even ACC register number. */
+/* Return true if the memory operand is one that can be conditionally
+ executed. */
int
-even_acc_operand (rtx op, enum machine_mode mode)
+condexec_memory_operand (rtx op, enum machine_mode mode)
{
- return acc_operand (op, mode) && ((REGNO (op) - ACC_FIRST) & 1) == 0;
-}
+ enum machine_mode op_mode = GET_MODE (op);
+ rtx addr;
-/* Return 1 if operand is zero or four. */
+ if (mode != VOIDmode && op_mode != mode)
+ return FALSE;
-int
-quad_acc_operand (rtx op, enum machine_mode mode)
-{
- return acc_operand (op, mode) && ((REGNO (op) - ACC_FIRST) & 3) == 0;
-}
+ switch (op_mode)
+ {
+ default:
+ return FALSE;
-/* Return 1 if operand is a valid ACCG register number. */
+ case QImode:
+ case HImode:
+ case SImode:
+ case SFmode:
+ break;
+ }
-int
-accg_operand (rtx op, enum machine_mode mode)
-{
- return ((mode == VOIDmode || mode == GET_MODE (op))
- && REG_P (op) && ACCG_P (REGNO (op))
- && ((REGNO (op) - ACCG_FIRST) & ~ACC_MASK) == 0);
-}
+ if (GET_CODE (op) != MEM)
+ return FALSE;
+ addr = XEXP (op, 0);
+ return frv_legitimate_address_p (mode, addr, reload_completed, TRUE, FALSE);
+}
\f
/* Return true if the bare return instruction can be used outside of the
epilog code. For frv, we only do it if there was no stack allocation. */
void
frv_emit_move (enum machine_mode mode, rtx dest, rtx src)
{
+ if (GET_CODE (src) == SYMBOL_REF)
+ {
+ enum tls_model model = SYMBOL_REF_TLS_MODEL (src);
+ if (model != 0)
+ src = frv_legitimize_tls_address (src, model);
+ }
+
switch (mode)
{
case SImode:
break;
default:
- abort ();
+ gcc_unreachable ();
}
emit_insn (gen_rtx_SET (VOIDmode, dest, src));
handle_sym:
if (TARGET_FDPIC)
{
+ enum tls_model model = SYMBOL_REF_TLS_MODEL (sym);
+
+ if (model != 0)
+ {
+ src = frv_legitimize_tls_address (src, model);
+ emit_move_insn (dest, src);
+ return TRUE;
+ }
+
if (SYMBOL_REF_FUNCTION_P (sym))
{
if (frv_local_funcdesc_p (sym))
/* Since OUR_FDPIC_REG is a pseudo register, we can't safely introduce
new uses of it once reload has begun. */
- if (reload_in_progress || reload_completed)
- abort ();
+ gcc_assert (!reload_in_progress && !reload_completed);
switch (unspec)
{
}
}
- fatal_insn ("Bad output_move_single operand", insn);
+ fatal_insn ("bad output_move_single operand", insn);
return "";
}
}
}
- fatal_insn ("Bad output_move_double operand", insn);
+ fatal_insn ("bad output_move_double operand", insn);
return "";
}
}
}
- fatal_insn ("Bad output_condmove_single operand", insn);
+ fatal_insn ("bad output_condmove_single operand", insn);
return "";
}
}
else
- abort ();
+ gcc_unreachable ();
}
else
{
switch (GET_CODE (minmax))
{
default:
- abort ();
+ gcc_unreachable ();
case SMIN: test_code = LT; break;
case SMAX: test_code = GT; break;
then do a conditional move of the other value. */
if (GET_CODE (src2) == CONST_INT && INTVAL (src2) != 0)
{
- if (rtx_equal_p (dest, src1))
- abort ();
+ gcc_assert (!rtx_equal_p (dest, src1));
emit_move_insn (dest, src2);
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
/* Make sure we are only dealing with hard registers. Also honor the
-mno-cond-exec switch, and -mno-nested-cond-exec switches if
applicable. */
- if (!reload_completed || TARGET_NO_COND_EXEC
- || (TARGET_NO_NESTED_CE && ce_info->pass > 1))
+ if (!reload_completed || !TARGET_COND_EXEC
+ || (!TARGET_NESTED_CE && ce_info->pass > 1))
goto fail;
/* Figure out which registers we can allocate for our own purposes. Only
for (j = CC_FIRST; j <= CC_LAST; j++)
if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
{
- if (REGNO_REG_SET_P (then_bb->global_live_at_start, j))
+ if (REGNO_REG_SET_P (then_bb->il.rtl->global_live_at_start, j))
continue;
- if (else_bb && REGNO_REG_SET_P (else_bb->global_live_at_start, j))
+ if (else_bb
+ && REGNO_REG_SET_P (else_bb->il.rtl->global_live_at_start, j))
continue;
- if (join_bb && REGNO_REG_SET_P (join_bb->global_live_at_start, j))
+ if (join_bb
+ && REGNO_REG_SET_P (join_bb->il.rtl->global_live_at_start, j))
continue;
SET_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j);
if (join_bb)
{
- int regno;
+ unsigned int regno;
/* Remove anything live at the beginning of the join block from being
available for allocation. */
- EXECUTE_IF_SET_IN_REG_SET (join_bb->global_live_at_start, 0, regno, rsi)
+ EXECUTE_IF_SET_IN_REG_SET (join_bb->il.rtl->global_live_at_start, 0, regno, rsi)
{
if (regno < FIRST_PSEUDO_REGISTER)
CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
{
rtx last_insn = BB_END (bb[j]);
rtx insn = BB_HEAD (bb[j]);
- int regno;
+ unsigned int regno;
if (dump_file)
fprintf (dump_file, "Scanning %s block %d, start %d, end %d\n",
/* Anything live at the beginning of the block is obviously unavailable
for allocation. */
- EXECUTE_IF_SET_IN_REG_SET (bb[j]->global_live_at_start, 0, regno, rsi)
+ EXECUTE_IF_SET_IN_REG_SET (bb[j]->il.rtl->global_live_at_start, 0, regno, rsi)
{
if (regno < FIRST_PSEUDO_REGISTER)
CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
/* Allocate the appropriate temporary condition code register. Try to
allocate the ICR/FCR register that corresponds to the ICC/FCC register so
that conditional cmp's can be done. */
- if (mode == CCmode || mode == CC_UNSmode)
+ if (mode == CCmode || mode == CC_UNSmode || mode == CC_NZmode)
{
cr_class = ICR_REGS;
cc_class = ICC_REGS;
debug_rtx (*p_false);
}
- if (TARGET_NO_MULTI_CE)
+ if (!TARGET_MULTI_CE)
goto fail;
if (GET_CODE (cr) != REG)
goto fail;
- if (mode == CCmode || mode == CC_UNSmode)
+ if (mode == CCmode || mode == CC_UNSmode || mode == CC_NZmode)
{
cr_class = ICR_REGS;
p_new_cr = &frv_ifcvt.extra_int_cr;
rtx op1;
rtx test;
- if (GET_CODE (pattern) != COND_EXEC)
- abort ();
+ gcc_assert (GET_CODE (pattern) == COND_EXEC);
test = COND_EXEC_TEST (pattern);
if (GET_CODE (test) == AND)
severely. */
&& ce_info->join_bb
&& ! (REGNO_REG_SET_P
- (ce_info->join_bb->global_live_at_start,
+ (ce_info->join_bb->il.rtl->global_live_at_start,
REGNO (SET_DEST (set))))
/* Similarly, we must not unconditionally set a reg
used as scratch in the THEN branch if the same reg
&& (! ce_info->else_bb
|| BLOCK_FOR_INSN (insn) == ce_info->else_bb
|| ! (REGNO_REG_SET_P
- (ce_info->else_bb->global_live_at_start,
+ (ce_info->else_bb->il.rtl->global_live_at_start,
REGNO (SET_DEST (set))))))
pattern = set;
/* Loop inserting the check insns. The last check insn is the first test,
and is the appropriate place to insert constants. */
- if (! p)
- abort ();
+ gcc_assert (p);
do
{
{
rtx insn = emit_insn_before (frv_ifcvt.scratch_regs[i], existing_insn);
if (! frv_ifcvt.scratch_insns_bitmap)
- frv_ifcvt.scratch_insns_bitmap = BITMAP_XMALLOC ();
+ frv_ifcvt.scratch_insns_bitmap = BITMAP_ALLOC (NULL);
bitmap_set_bit (frv_ifcvt.scratch_insns_bitmap, INSN_UID (insn));
frv_ifcvt.scratch_regs[i] = NULL_RTX;
}
default:
break;
+ case GR8_REGS:
+ case GR9_REGS:
+ case GR89_REGS:
+ case FDPIC_FPTR_REGS:
+ case FDPIC_REGS:
case ICC_REGS:
case FCC_REGS:
case CC_REGS:
prev = cur;
}
- if (!cur)
- abort ();
+ gcc_assert (cur);
/* If this isn't a :0 field and if the previous element is a bitfield
also, see if the type is different, if so, we will need to align the
{
case CCmode:
case CC_UNSmode:
+ case CC_NZmode:
return ICC_P (regno) || GPR_P (regno);
case CC_CCRmode:
/* Otherwise store the constant away and do a load. */
return FALSE;
}
+
+/* Implement SELECT_CC_MODE. Choose CC_FP for floating-point comparisons,
+ CC_NZ for comparisons against zero in which a single Z or N flag test
+ is enough, CC_UNS for other unsigned comparisons, and CC for other
+ signed comparisons. */
+
+enum machine_mode
+frv_select_cc_mode (enum rtx_code code, rtx x, rtx y)
+{
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ return CC_FPmode;
+
+ switch (code)
+ {
+ case EQ:
+ case NE:
+ case LT:
+ case GE:
+ return y == const0_rtx ? CC_NZmode : CCmode;
+
+ case GTU:
+ case GEU:
+ case LTU:
+ case LEU:
+ return y == const0_rtx ? CC_NZmode : CC_UNSmode;
+
+ default:
+ return CCmode;
+ }
+}
\f
/* A C expression for the cost of moving data from a register in class FROM to
one in class TO. The classes are expressed using the enumeration values
if (cpu_unit_reservation_p (state, frv_unit_codes[unit]))
break;
- if (unit == ARRAY_SIZE (frv_unit_codes))
- abort ();
+ gcc_assert (unit != ARRAY_SIZE (frv_unit_codes));
frv_type_to_unit[type] = unit;
}
static int
frv_cond_flags (rtx cond)
{
- if ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
- && GET_CODE (XEXP (cond, 0)) == REG
- && CR_P (REGNO (XEXP (cond, 0)))
- && XEXP (cond, 1) == const0_rtx)
- return ((REGNO (XEXP (cond, 0)) - CR_FIRST)
- | (GET_CODE (cond) == NE
- ? REGSTATE_IF_TRUE
- : REGSTATE_IF_FALSE));
- abort ();
+ gcc_assert ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
+ && GET_CODE (XEXP (cond, 0)) == REG
+ && CR_P (REGNO (XEXP (cond, 0)))
+ && XEXP (cond, 1) == const0_rtx);
+ return ((REGNO (XEXP (cond, 0)) - CR_FIRST)
+ | (GET_CODE (cond) == NE
+ ? REGSTATE_IF_TRUE
+ : REGSTATE_IF_FALSE));
}
/* Early exit if we don't want to pack insns. */
if (!optimize
|| !flag_schedule_insns_after_reload
- || TARGET_NO_VLIW_BRANCH
+ || !TARGET_VLIW_BRANCH
|| frv_packet.issue_rate == 1)
return false;
size_t dfa_size;
packet_group = &frv_packet.groups[group];
+
+ /* Assume no nop is needed. */
+ packet_group->nop = 0;
+
if (packet_group->num_insns == 0)
return;
return;
}
}
- abort ();
+ gcc_unreachable ();
}
\f
/* Sort the current packet into assembly-language order. Set packing
if (cursor[group] < packet_group->num_insns)
{
/* frv_reorg should have added nops for us. */
- if (packet_group->sorted[cursor[group]] == packet_group->nop)
- abort ();
+ gcc_assert (packet_group->sorted[cursor[group]]
+ != packet_group->nop);
insns[to++] = packet_group->sorted[cursor[group]++];
}
}
- if (to != frv_packet.num_insns)
- abort ();
+ gcc_assert (to == frv_packet.num_insns);
/* Clear the last instruction's packing flag, thus marking the end of
a packet. Reorder the other instructions relative to it. */
frv_insert_nop_in_packet (packet_group->nop);
}
+/* Return true if accesses IO1 and IO2 refer to the same doubleword. */
+
+static bool
+frv_same_doubleword_p (const struct frv_io *io1, const struct frv_io *io2)
+{
+ if (io1->const_address != 0 && io2->const_address != 0)
+ return io1->const_address == io2->const_address;
+
+ if (io1->var_address != 0 && io2->var_address != 0)
+ return rtx_equal_p (io1->var_address, io2->var_address);
+
+ return false;
+}
+
+/* Return true if operations IO1 and IO2 are guaranteed to complete
+ in order. */
+
+static bool
+frv_io_fixed_order_p (const struct frv_io *io1, const struct frv_io *io2)
+{
+ /* The order of writes is always preserved. */
+ if (io1->type == FRV_IO_WRITE && io2->type == FRV_IO_WRITE)
+ return true;
+
+ /* The order of reads isn't preserved. */
+ if (io1->type != FRV_IO_WRITE && io2->type != FRV_IO_WRITE)
+ return false;
+
+ /* One operation is a write and the other is (or could be) a read.
+ The order is only guaranteed if the accesses are to the same
+ doubleword. */
+ return frv_same_doubleword_p (io1, io2);
+}
+
+/* Generalize I/O operation X so that it covers both X and Y. */
+
+static void
+frv_io_union (struct frv_io *x, const struct frv_io *y)
+{
+ if (x->type != y->type)
+ x->type = FRV_IO_UNKNOWN;
+ if (!frv_same_doubleword_p (x, y))
+ {
+ x->const_address = 0;
+ x->var_address = 0;
+ }
+}
+
+/* Fill IO with information about the load or store associated with
+ membar instruction INSN. */
+
+static void
+frv_extract_membar (struct frv_io *io, rtx insn)
+{
+ extract_insn (insn);
+ io->type = INTVAL (recog_data.operand[2]);
+ io->const_address = INTVAL (recog_data.operand[1]);
+ io->var_address = XEXP (recog_data.operand[0], 0);
+}
+
+/* A note_stores callback for which DATA points to an rtx. Nullify *DATA
+ if X is a register and *DATA depends on X. */
+
+static void
+frv_io_check_address (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ rtx *other = data;
+
+ if (REG_P (x) && *other != 0 && reg_overlap_mentioned_p (x, *other))
+ *other = 0;
+}
+
+/* A note_stores callback for which DATA points to a HARD_REG_SET.
+ Remove every modified register from the set. */
+
+static void
+frv_io_handle_set (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ HARD_REG_SET *set = data;
+ unsigned int regno;
+
+ if (REG_P (x))
+ FOR_EACH_REGNO (regno, x)
+ CLEAR_HARD_REG_BIT (*set, regno);
+}
+
+/* A for_each_rtx callback for which DATA points to a HARD_REG_SET.
+ Add every register in *X to the set. */
+
+static int
+frv_io_handle_use_1 (rtx *x, void *data)
+{
+ HARD_REG_SET *set = data;
+ unsigned int regno;
+
+ if (REG_P (*x))
+ FOR_EACH_REGNO (regno, *x)
+ SET_HARD_REG_BIT (*set, regno);
+
+ return 0;
+}
+
+/* A note_stores callback that applies frv_io_handle_use_1 to an
+ entire rhs value. */
+
+static void
+frv_io_handle_use (rtx *x, void *data)
+{
+ for_each_rtx (x, frv_io_handle_use_1, data);
+}
+
+/* Go through block BB looking for membars to remove. There are two
+ cases where intra-block analysis is enough:
+
+ - a membar is redundant if it occurs between two consecutive I/O
+ operations and if those operations are guaranteed to complete
+ in order.
+
+ - a membar for a __builtin_read is redundant if the result is
+ used before the next I/O operation is issued.
+
+ If the last membar in the block could not be removed, and there
+ are guaranteed to be no I/O operations between that membar and
+ the end of the block, store the membar in *LAST_MEMBAR, otherwise
+ store null.
+
+ Describe the block's first I/O operation in *NEXT_IO. Describe
+ an unknown operation if the block doesn't do any I/O. */
+
+static void
+frv_optimize_membar_local (basic_block bb, struct frv_io *next_io,
+ rtx *last_membar)
+{
+ HARD_REG_SET used_regs;
+ rtx next_membar, set, insn;
+ bool next_is_end_p;
+
+ /* NEXT_IO is the next I/O operation to be performed after the current
+ instruction. It starts off as being an unknown operation. */
+ memset (next_io, 0, sizeof (*next_io));
+
+ /* NEXT_IS_END_P is true if NEXT_IO describes the end of the block. */
+ next_is_end_p = true;
+
+ /* If the current instruction is a __builtin_read or __builtin_write,
+ NEXT_MEMBAR is the membar instruction associated with it. NEXT_MEMBAR
+ is null if the membar has already been deleted.
+
+ Note that the initialization here should only be needed to
+ suppress warnings. */
+ next_membar = 0;
+
+ /* USED_REGS is the set of registers that are used before the
+ next I/O instruction. */
+ CLEAR_HARD_REG_SET (used_regs);
+
+ for (insn = BB_END (bb); insn != BB_HEAD (bb); insn = PREV_INSN (insn))
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ /* We can't predict what a call will do to volatile memory. */
+ memset (next_io, 0, sizeof (struct frv_io));
+ next_is_end_p = false;
+ CLEAR_HARD_REG_SET (used_regs);
+ }
+ else if (INSN_P (insn))
+ switch (recog_memoized (insn))
+ {
+ case CODE_FOR_optional_membar_qi:
+ case CODE_FOR_optional_membar_hi:
+ case CODE_FOR_optional_membar_si:
+ case CODE_FOR_optional_membar_di:
+ next_membar = insn;
+ if (next_is_end_p)
+ {
+ /* Local information isn't enough to decide whether this
+ membar is needed. Stash it away for later. */
+ *last_membar = insn;
+ frv_extract_membar (next_io, insn);
+ next_is_end_p = false;
+ }
+ else
+ {
+ /* Check whether the I/O operation before INSN could be
+ reordered with one described by NEXT_IO. If it can't,
+ INSN will not be needed. */
+ struct frv_io prev_io;
+
+ frv_extract_membar (&prev_io, insn);
+ if (frv_io_fixed_order_p (&prev_io, next_io))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ ";; [Local] Removing membar %d since order"
+ " of accesses is guaranteed\n",
+ INSN_UID (next_membar));
+
+ insn = NEXT_INSN (insn);
+ delete_insn (next_membar);
+ next_membar = 0;
+ }
+ *next_io = prev_io;
+ }
+ break;
+
+ default:
+ /* Invalidate NEXT_IO's address if it depends on something that
+ is clobbered by INSN. */
+ if (next_io->var_address)
+ note_stores (PATTERN (insn), frv_io_check_address,
+ &next_io->var_address);
+
+ /* If the next membar is associated with a __builtin_read,
+ see if INSN reads from that address. If it does, and if
+ the destination register is used before the next I/O access,
+ there is no need for the membar. */
+ set = PATTERN (insn);
+ if (next_io->type == FRV_IO_READ
+ && next_io->var_address != 0
+ && next_membar != 0
+ && GET_CODE (set) == SET
+ && GET_CODE (SET_DEST (set)) == REG
+ && TEST_HARD_REG_BIT (used_regs, REGNO (SET_DEST (set))))
+ {
+ rtx src;
+
+ src = SET_SRC (set);
+ if (GET_CODE (src) == ZERO_EXTEND)
+ src = XEXP (src, 0);
+
+ if (GET_CODE (src) == MEM
+ && rtx_equal_p (XEXP (src, 0), next_io->var_address))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ ";; [Local] Removing membar %d since the target"
+ " of %d is used before the I/O operation\n",
+ INSN_UID (next_membar), INSN_UID (insn));
+
+ if (next_membar == *last_membar)
+ *last_membar = 0;
+
+ delete_insn (next_membar);
+ next_membar = 0;
+ }
+ }
+
+ /* If INSN has volatile references, forget about any registers
+ that are used after it. Otherwise forget about uses that
+ are (or might be) defined by INSN. */
+ if (volatile_refs_p (PATTERN (insn)))
+ CLEAR_HARD_REG_SET (used_regs);
+ else
+ note_stores (PATTERN (insn), frv_io_handle_set, &used_regs);
+
+ note_uses (&PATTERN (insn), frv_io_handle_use, &used_regs);
+ break;
+ }
+}
+
+/* See if MEMBAR, the last membar instruction in BB, can be removed.
+ FIRST_IO[X] describes the first operation performed by basic block X. */
+
+static void
+frv_optimize_membar_global (basic_block bb, struct frv_io *first_io,
+ rtx membar)
+{
+ struct frv_io this_io, next_io;
+ edge succ;
+ edge_iterator ei;
+
+ /* We need to keep the membar if there is an edge to the exit block. */
+ FOR_EACH_EDGE (succ, ei, bb->succs)
+ /* for (succ = bb->succ; succ != 0; succ = succ->succ_next) */
+ if (succ->dest == EXIT_BLOCK_PTR)
+ return;
+
+ /* Work out the union of all successor blocks. */
+ ei = ei_start (bb->succs);
+ ei_cond (ei, &succ);
+ /* next_io = first_io[bb->succ->dest->index]; */
+ next_io = first_io[succ->dest->index];
+ ei = ei_start (bb->succs);
+ if (ei_cond (ei, &succ))
+ {
+ for (ei_next (&ei); ei_cond (ei, &succ); ei_next (&ei))
+ /*for (succ = bb->succ->succ_next; succ != 0; succ = succ->succ_next)*/
+ frv_io_union (&next_io, &first_io[succ->dest->index]);
+ }
+ else
+ gcc_unreachable ();
+
+ frv_extract_membar (&this_io, membar);
+ if (frv_io_fixed_order_p (&this_io, &next_io))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ ";; [Global] Removing membar %d since order of accesses"
+ " is guaranteed\n", INSN_UID (membar));
+
+ delete_insn (membar);
+ }
+}
+
+/* Remove redundant membars from the current function. */
+
+static void
+frv_optimize_membar (void)
+{
+ basic_block bb;
+ struct frv_io *first_io;
+ rtx *last_membar;
+
+ compute_bb_for_insn ();
+ first_io = xcalloc (last_basic_block, sizeof (struct frv_io));
+ last_membar = xcalloc (last_basic_block, sizeof (rtx));
+
+ FOR_EACH_BB (bb)
+ frv_optimize_membar_local (bb, &first_io[bb->index],
+ &last_membar[bb->index]);
+
+ FOR_EACH_BB (bb)
+ if (last_membar[bb->index] != 0)
+ frv_optimize_membar_global (bb, first_io, last_membar[bb->index]);
+
+ free (first_io);
+ free (last_membar);
+}
+\f
/* Used by frv_reorg to keep track of the current packet's address. */
static unsigned int frv_packet_address;
static void
frv_reorg (void)
{
+ if (optimize > 0 && TARGET_OPTIMIZE_MEMBAR && cfun->machine->has_membar_p)
+ frv_optimize_membar ();
+
frv_num_nops = 0;
frv_register_nop (gen_nop ());
if (TARGET_MEDIA)
{ CODE_FOR_mqsubhss, "__MQSUBHSS", FRV_BUILTIN_MQSUBHSS, 0, 0 },
{ CODE_FOR_mqsubhus, "__MQSUBHUS", FRV_BUILTIN_MQSUBHUS, 0, 0 },
{ CODE_FOR_mpackh, "__MPACKH", FRV_BUILTIN_MPACKH, 0, 0 },
- { CODE_FOR_mdpackh, "__MDPACKH", FRV_BUILTIN_MDPACKH, 0, 0 },
{ CODE_FOR_mcop1, "__Mcop1", FRV_BUILTIN_MCOP1, 0, 0 },
{ CODE_FOR_mcop2, "__Mcop2", FRV_BUILTIN_MCOP2, 0, 0 },
{ CODE_FOR_mwcut, "__MWCUT", FRV_BUILTIN_MWCUT, 0, 0 },
{ CODE_FOR_mdasaccs, "__MDASACCS", FRV_BUILTIN_MDASACCS, 0, 0 }
};
+/* Intrinsics that load a value and then issue a MEMBAR. The load is
+ a normal move and the ICODE is for the membar. */
+
+static struct builtin_description bdesc_loads[] =
+{
+ { CODE_FOR_optional_membar_qi, "__builtin_read8",
+ FRV_BUILTIN_READ8, 0, 0 },
+ { CODE_FOR_optional_membar_hi, "__builtin_read16",
+ FRV_BUILTIN_READ16, 0, 0 },
+ { CODE_FOR_optional_membar_si, "__builtin_read32",
+ FRV_BUILTIN_READ32, 0, 0 },
+ { CODE_FOR_optional_membar_di, "__builtin_read64",
+ FRV_BUILTIN_READ64, 0, 0 }
+};
+
+/* Likewise stores. */
+
+static struct builtin_description bdesc_stores[] =
+{
+ { CODE_FOR_optional_membar_qi, "__builtin_write8",
+ FRV_BUILTIN_WRITE8, 0, 0 },
+ { CODE_FOR_optional_membar_hi, "__builtin_write16",
+ FRV_BUILTIN_WRITE16, 0, 0 },
+ { CODE_FOR_optional_membar_si, "__builtin_write32",
+ FRV_BUILTIN_WRITE32, 0, 0 },
+ { CODE_FOR_optional_membar_di, "__builtin_write64",
+ FRV_BUILTIN_WRITE64, 0, 0 },
+};
+
/* Initialize media builtins. */
static void
tree sword2 = long_long_integer_type_node;
tree uword2 = long_long_unsigned_type_node;
tree uword4 = build_pointer_type (uword1);
+ tree vptr = build_pointer_type (build_type_variant (void_type_node, 0, 1));
+ tree ubyte = unsigned_char_type_node;
tree iacc = integer_type_node;
#define UNARY(RET, T1) \
tree_cons (NULL_TREE, T2, \
tree_cons (NULL_TREE, T3, endlink))))
+#define QUAD(RET, T1, T2, T3, T4) \
+ build_function_type (RET, tree_cons (NULL_TREE, T1, \
+ tree_cons (NULL_TREE, T2, \
+ tree_cons (NULL_TREE, T3, \
+ tree_cons (NULL_TREE, T4, endlink)))))
+
tree void_ftype_void = build_function_type (voidt, endlink);
tree void_ftype_acc = UNARY (voidt, accumulator);
tree uw2_ftype_uw2_uw2 = BINARY (uword2, uword2, uword2);
tree uw2_ftype_uw2_int = BINARY (uword2, uword2, integer);
tree uw2_ftype_acc_int = BINARY (uword2, accumulator, integer);
+ tree uw2_ftype_uh_uh_uh_uh = QUAD (uword2, uhalf, uhalf, uhalf, uhalf);
tree sw2_ftype_sw2_sw2 = BINARY (sword2, sword2, sword2);
tree sw2_ftype_sw2_int = BINARY (sword2, sword2, integer);
tree sw2_ftype_iacc = UNARY (sword2, iacc);
tree sw1_ftype_iacc = UNARY (sword1, iacc);
tree void_ftype_ptr = UNARY (voidt, const_ptr_type_node);
+ tree uw1_ftype_vptr = UNARY (uword1, vptr);
+ tree uw2_ftype_vptr = UNARY (uword2, vptr);
+ tree void_ftype_vptr_ub = BINARY (voidt, vptr, ubyte);
+ tree void_ftype_vptr_uh = BINARY (voidt, vptr, uhalf);
+ tree void_ftype_vptr_uw1 = BINARY (voidt, vptr, uword1);
+ tree void_ftype_vptr_uw2 = BINARY (voidt, vptr, uword2);
def_builtin ("__MAND", uw1_ftype_uw1_uw1, FRV_BUILTIN_MAND);
def_builtin ("__MOR", uw1_ftype_uw1_uw1, FRV_BUILTIN_MOR);
def_builtin ("__MEXPDHD", uw2_ftype_uw1_int, FRV_BUILTIN_MEXPDHD);
def_builtin ("__MPACKH", uw1_ftype_uh_uh, FRV_BUILTIN_MPACKH);
def_builtin ("__MUNPACKH", uw2_ftype_uw1, FRV_BUILTIN_MUNPACKH);
- def_builtin ("__MDPACKH", uw2_ftype_uw2_uw2, FRV_BUILTIN_MDPACKH);
+ def_builtin ("__MDPACKH", uw2_ftype_uh_uh_uh_uh, FRV_BUILTIN_MDPACKH);
def_builtin ("__MDUNPACKH", void_ftype_uw4_uw2, FRV_BUILTIN_MDUNPACKH);
def_builtin ("__MBTOH", uw2_ftype_uw1, FRV_BUILTIN_MBTOH);
def_builtin ("__MHTOB", uw1_ftype_uw2, FRV_BUILTIN_MHTOB);
def_builtin ("__IACCsetl", void_ftype_iacc_sw1, FRV_BUILTIN_IACCsetl);
def_builtin ("__data_prefetch0", void_ftype_ptr, FRV_BUILTIN_PREFETCH0);
def_builtin ("__data_prefetch", void_ftype_ptr, FRV_BUILTIN_PREFETCH);
+ def_builtin ("__builtin_read8", uw1_ftype_vptr, FRV_BUILTIN_READ8);
+ def_builtin ("__builtin_read16", uw1_ftype_vptr, FRV_BUILTIN_READ16);
+ def_builtin ("__builtin_read32", uw1_ftype_vptr, FRV_BUILTIN_READ32);
+ def_builtin ("__builtin_read64", uw2_ftype_vptr, FRV_BUILTIN_READ64);
+
+ def_builtin ("__builtin_write8", void_ftype_vptr_ub, FRV_BUILTIN_WRITE8);
+ def_builtin ("__builtin_write16", void_ftype_vptr_uh, FRV_BUILTIN_WRITE16);
+ def_builtin ("__builtin_write32", void_ftype_vptr_uw1, FRV_BUILTIN_WRITE32);
+ def_builtin ("__builtin_write64", void_ftype_vptr_uw2, FRV_BUILTIN_WRITE64);
#undef UNARY
#undef BINARY
#undef TRINARY
+#undef QUAD
}
/* Set the names for various arithmetic operations according to the
rtx reg;
int i;
- /* ACCs and ACCGs are implicity global registers if media intrinsics
+ /* ACCs and ACCGs are implicit global registers if media intrinsics
are being used. We set up this lazily to avoid creating lots of
unnecessary call_insn rtl in non-media code. */
for (i = 0; i <= ACC_MASK; i++)
return QImode;
default:
- abort ();
+ gcc_unreachable ();
}
}
+/* Given that a __builtin_read or __builtin_write function is accessing
+ address ADDRESS, return the value that should be used as operand 1
+ of the membar. */
+
+static rtx
+frv_io_address_cookie (rtx address)
+{
+ return (GET_CODE (address) == CONST_INT
+ ? GEN_INT (INTVAL (address) / 8 * 8)
+ : const0_rtx);
+}
+
/* Return the accumulator guard that should be paired with accumulator
register ACC. The mode of the returned register is in the same
class as ACC, but is four times smaller. */
op = const0_rtx;
}
- /* IACCs are implicity global registers. We set up this lazily to
+ /* IACCs are implicit global registers. We set up this lazily to
avoid creating lots of unnecessary call_insn rtl when IACCs aren't
being used. */
regno = INTVAL (op) + IACC_FIRST;
return copy_to_mode_reg (mode, arg);
}
+/* Return a volatile memory reference of mode MODE whose address is ARG. */
+
+static rtx
+frv_volatile_memref (enum machine_mode mode, rtx arg)
+{
+ rtx mem;
+
+ mem = gen_rtx_MEM (mode, memory_address (mode, arg));
+ MEM_VOLATILE_P (mem) = 1;
+ return mem;
+}
+
/* Expand builtins that take a single, constant argument. At the moment,
only MHDSETS falls into this category. */
return NULL_RTX;
}
+/* Expand a __builtin_read* function. ICODE is the instruction code for the
+ membar and TARGET_MODE is the mode that the loaded value should have. */
+
+static rtx
+frv_expand_load_builtin (enum insn_code icode, enum machine_mode target_mode,
+ tree arglist, rtx target)
+{
+ rtx op0 = frv_read_argument (&arglist);
+ rtx cookie = frv_io_address_cookie (op0);
+
+ if (target == 0 || !REG_P (target))
+ target = gen_reg_rtx (target_mode);
+ op0 = frv_volatile_memref (insn_data[icode].operand[0].mode, op0);
+ convert_move (target, op0, 1);
+ emit_insn (GEN_FCN (icode) (copy_rtx (op0), cookie, GEN_INT (FRV_IO_READ)));
+ cfun->machine->has_membar_p = 1;
+ return target;
+}
+
+/* Likewise __builtin_write* functions. */
+
+static rtx
+frv_expand_store_builtin (enum insn_code icode, tree arglist)
+{
+ rtx op0 = frv_read_argument (&arglist);
+ rtx op1 = frv_read_argument (&arglist);
+ rtx cookie = frv_io_address_cookie (op0);
+
+ op0 = frv_volatile_memref (insn_data[icode].operand[0].mode, op0);
+ convert_move (op0, force_reg (insn_data[icode].operand[0].mode, op1), 1);
+ emit_insn (GEN_FCN (icode) (copy_rtx (op0), cookie, GEN_INT (FRV_IO_WRITE)));
+ cfun->machine->has_membar_p = 1;
+ return NULL_RTX;
+}
+
+/* Expand the MDPACKH builtin. It takes four unsigned short arguments and
+ each argument forms one word of the two double-word input registers.
+ ARGLIST is a TREE_LIST of the arguments and TARGET, if nonnull,
+ suggests a good place to put the return value. */
+
+static rtx
+frv_expand_mdpackh_builtin (tree arglist, rtx target)
+{
+ enum insn_code icode = CODE_FOR_mdpackh;
+ rtx pat, op0, op1;
+ rtx arg1 = frv_read_argument (&arglist);
+ rtx arg2 = frv_read_argument (&arglist);
+ rtx arg3 = frv_read_argument (&arglist);
+ rtx arg4 = frv_read_argument (&arglist);
+
+ target = frv_legitimize_target (icode, target);
+ op0 = gen_reg_rtx (DImode);
+ op1 = gen_reg_rtx (DImode);
+
+ /* The high half of each word is not explicitly initialized, so indicate
+ that the input operands are not live before this point. */
+ emit_insn (gen_rtx_CLOBBER (DImode, op0));
+ emit_insn (gen_rtx_CLOBBER (DImode, op1));
+
+ /* Move each argument into the low half of its associated input word. */
+ emit_move_insn (simplify_gen_subreg (HImode, op0, DImode, 2), arg1);
+ emit_move_insn (simplify_gen_subreg (HImode, op0, DImode, 6), arg2);
+ emit_move_insn (simplify_gen_subreg (HImode, op1, DImode, 2), arg3);
+ emit_move_insn (simplify_gen_subreg (HImode, op1, DImode, 6), arg4);
+
+ pat = GEN_FCN (icode) (target, op0, op1);
+ if (! pat)
+ return NULL_RTX;
+
+ emit_insn (pat);
+ return target;
+}
+
/* Expand the MCLRACC builtin. This builtin takes a single accumulator
number as argument. */
case FRV_BUILTIN_MWTACCG:
return frv_expand_mwtacc_builtin (CODE_FOR_mwtaccg, arglist);
+ case FRV_BUILTIN_MDPACKH:
+ return frv_expand_mdpackh_builtin (arglist, target);
+
case FRV_BUILTIN_IACCreadll:
{
rtx src = frv_read_iacc_argument (DImode, &arglist);
if (d->code == fcode)
return frv_expand_prefetches (d->icode, arglist);
+ for (i = 0, d = bdesc_loads; i < ARRAY_SIZE (bdesc_loads); i++, d++)
+ if (d->code == fcode)
+ return frv_expand_load_builtin (d->icode, TYPE_MODE (TREE_TYPE (exp)),
+ arglist, target);
+
+ for (i = 0, d = bdesc_stores; i < ARRAY_SIZE (bdesc_stores); i++, d++)
+ if (d->code == fcode)
+ return frv_expand_store_builtin (d->icode, arglist);
+
return 0;
}
section_name = DECL_SECTION_NAME (decl);
if (section_name)
{
- if (TREE_CODE (section_name) != STRING_CST)
- abort ();
+ gcc_assert (TREE_CODE (section_name) == STRING_CST);
if (frv_string_begins_with (section_name, ".sdata"))
return true;
if (frv_string_begins_with (section_name, ".sbss"))
assemble_align (POINTER_SIZE);
if (TARGET_FDPIC)
{
- if (!frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1))
- abort ();
+ int ok = frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ gcc_assert (ok);
return;
}
assemble_integer_with_op ("\t.picptr\t", symbol);
assemble_align (POINTER_SIZE);
if (TARGET_FDPIC)
{
- if (!frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1))
- abort ();
+ int ok = frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ gcc_assert (ok);
return;
}
assemble_integer_with_op ("\t.picptr\t", symbol);
return gen_rtx_REG (Pmode, FRV_STRUCT_VALUE_REGNUM);
}
+#define TLS_BIAS (2048 - 16)
+
+/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
+ We need to emit DTP-relative relocations. */
+
+static void
+frv_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+ gcc_assert (size == 4);
+ fputs ("\t.picptr\ttlsmoff(", file);
+ /* We want the unbiased TLS offset, so add the bias to the
+ expression, such that the implicit biasing cancels out. */
+ output_addr_const (file, plus_constant (x, TLS_BIAS));
+ fputs (")", file);
+}
+
#include "gt-frv.h"