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++)
+/* 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. */
typedef struct frv_tmp_reg_struct
{
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
#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) \
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
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]);
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;
}
}
- 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))
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
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;
static rtx
gen_inlined_tls_plt (rtx addr)
{
- rtx mem, retval, dest;
+ rtx retval, dest;
rtx picreg = get_hard_reg_initial_val (Pmode, FDPIC_REG);
break;
}
default:
- abort ();
+ gcc_unreachable ();
}
return dest;
case R_FRV_TLSDESCLO: return "tlsdesclo";
case R_FRV_GOTTLSDESCHI: return "gottlsdeschi";
case R_FRV_GOTTLSDESCLO: return "gottlsdesclo";
- default: abort ();
+ default: gcc_unreachable ();
}
}
break;
default:
- abort ();
+ gcc_unreachable ();
}
emit_insn (gen_rtx_SET (VOIDmode, dest, src));
/* 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,
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);
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
{
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
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));
}
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_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 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 ("__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
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,
op0 = gen_reg_rtx (DImode);
op1 = gen_reg_rtx (DImode);
- /* The high half of each word is not explicitly initialised, so indicate
+ /* 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));
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);
#define TLS_BIAS (2048 - 16)
-/* This is called from dwarf2out.c via ASM_OUTPUT_DWARF_DTPREL.
+/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
We need to emit DTP-relative relocations. */
-void
+static void
frv_output_dwarf_dtprel (FILE *file, int size, rtx x)
{
- if (size != 4)
- abort ();
+ 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. */