gcc/ChangeLog:
* config/rx/rx.cc (rx_legitimize_address): Add PIC support.
(legitimize_pic_address): New.
(rx_is_legitimate_address): Add PIC case.
(+nonpic_symbol_mentioned_p): New.
(rx_print_operand_address): Add PIC symbol attribute.
(rx_assemble_integer): Add PIC case output.
(rx_print_operand): Add PC based address output.
(rx_get_stack_layout): Save PIC registers.
(rx_expand_prologue): Genarate PIC prologue.
(rx_elf_asm_cdtor): Add FDPIC special output.
(rx_option_override): fdpic support.
(rx_function_ok_for_sibcall): Use relative call in fdpic mode.
(rx_is_legitimate_constant): Add PIC support.
(rx_legitimate_pic_operand_p): New.
(rx_load_function_descriptor): New.
(rx_get_fdpic_reg_initial_val) New.
(rx_mov_pic_operands): New.
(rx_asm_output_addr_const_extra) New.
(rx_const_not_ok_for_debug_p): New.
(rx_pic_vector_address): New.
(rx_cannot_force_const_mem_p): New.
(TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA): New.
(TARGET_CONST_NOT_OK_FOR_DEBUG_P): New.
(TARGET_CANNOT_FORCE_CONST_MEM): New.
Signed-off-by: Yoshinori Sato <[email protected]>
---
gcc/config/rx/rx.cc | 390 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 384 insertions(+), 6 deletions(-)
diff --git a/gcc/config/rx/rx.cc b/gcc/config/rx/rx.cc
index c5638817d08..865637e434c 100644
--- a/gcc/config/rx/rx.cc
+++ b/gcc/config/rx/rx.cc
@@ -148,6 +148,9 @@ rx_legitimize_address (rtx x,
rtx oldx ATTRIBUTE_UNUSED,
machine_mode mode ATTRIBUTE_UNUSED)
{
+ if (flag_pic)
+ return legitimize_pic_address (oldx, mode, NULL_RTX);
+
if (rx_pid_data_operand (x) == PID_UNENCODED)
{
rtx rv = gen_pid_addr (gen_rtx_REG (SImode, rx_pid_base_regnum ()), x);
@@ -163,6 +166,59 @@ rx_legitimize_address (rtx x,
return x;
}
+/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
+ @GOTOFF in `reg'. */
+rtx
+legitimize_pic_address (rtx orig, machine_mode mode ATTRIBUTE_UNUSED, rtx reg)
+{
+ if (flag_plt && (GET_CODE (orig) == LABEL_REF ||
+ (GET_CODE (orig) == SYMBOL_REF &&
+ !SYMBOL_REF_LOCAL_P (orig))))
+ {
+ if (reg == NULL_RTX)
+ reg = gen_reg_rtx (Pmode);
+
+ if (TARGET_FDPIC)
+ {
+ if (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (orig))
+ emit_insn (gen_symGOTOFFFUNCDESC2reg (reg, orig));
+ else
+ emit_insn (gen_symGOT2reg (reg, orig));
+ }
+ crtl->uses_pic_offset_table = 1;
+ return reg;
+ }
+ else if (GET_CODE (orig) == SYMBOL_REF)
+ {
+ if (reg == NULL_RTX)
+ reg = gen_reg_rtx (Pmode);
+
+ if (TARGET_FDPIC &&
+ SYMBOL_REF_FUNCTION_P (orig) && SYMBOL_REF_EXTERNAL_P(orig))
+ emit_insn (gen_symGOTFUNCDESC2reg (reg, orig));
+ else
+ emit_insn (gen_symGOT2reg (reg, orig));
+ crtl->uses_pic_offset_table = 1;
+ return reg;
+ }
+ else if ((MEM_P(orig) && GET_CODE (XEXP (orig, 1)) == LABEL_REF) &&
+ (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_DECL (orig)
+ && (TREE_READONLY (SYMBOL_REF_DECL (orig)))))
+ {
+ rtx label = XEXP (orig, 1);
+ if (reg == NULL_RTX)
+ reg = gen_reg_rtx(Pmode);
+ emit_insn(gen_load_pc_location(reg, GEN_INT (CTRLREG_PC)));
+ if (GET_CODE (orig) == PLUS)
+ {
+ label = gen_pcoffset_label(label);
+ XEXP (orig, 1) = XEXP (orig, 0);
+ XEXP (orig, 0) = gen_rtx_PLUS(Pmode, reg, label);
+ }
+ }
+ return orig;
+}
+
/* Return true if OP is a reference to an object in a small data area. */
static bool
@@ -262,6 +318,8 @@ rx_is_legitimate_address (machine_mode mode, rtx x,
&& CONST_INT_P (factor)
&& GET_MODE_SIZE (mode) == INTVAL (factor);
}
+ case CONST:
+ return GET_CODE (XEXP (index, 0)) == UNSPEC;
default:
return false;
@@ -272,6 +330,45 @@ rx_is_legitimate_address (machine_mode mode, rtx x,
return rx_small_data_operand (x);
}
+/* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol
+ isn't protected by a PIC unspec. */
+bool
+nonpic_symbol_mentioned_p (rtx x)
+{
+ if (GET_CODE (x) == PC)
+ return true;
+
+ /* We don't want to look into the possible MEM location of a
+ CONST_DOUBLE, since we're not going to use it, in general. */
+ if (GET_CODE (x) == CONST_DOUBLE)
+ return false;
+
+ if (GET_CODE (x) == UNSPEC
+ && (XINT (x, 1) == UNSPEC_PIC
+ || XINT (x, 1) == UNSPEC_GOT
+ || XINT (x, 1) == UNSPEC_GOTOFF
+ || XINT (x, 1) == UNSPEC_PLT
+ || XINT (x, 1) == UNSPEC_GOTFUNCDESC
+ || XINT (x, 1) == UNSPEC_GOTOFFFUNCDESC
+ || XINT (x, 1) == UNSPEC_PCOFFSET))
+ return false;
+
+ const char* fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (int i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ for (int j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
+ return true;
+ }
+ else if (fmt[i] == 'e' && nonpic_symbol_mentioned_p (XEXP (x, i)))
+ return true;
+ }
+
+ return false;
+}
+
/* Returns TRUE for simple memory addresses, ie ones
that do not involve register indirect addressing
or pre/post increment/decrement. */
@@ -461,7 +558,47 @@ rx_print_operand_address (FILE * file, machine_mode
/*mode*/, rtx addr)
break;
case UNSPEC:
- addr = XVECEXP (addr, 0, 0);
+ {
+ int post = XINT (addr, 1);
+ const char *attr = NULL;
+ bool immideate = false;
+#define ATTR_STR(_name) \
+ case UNSPEC_##_name: \
+ addr = XVECEXP (addr, 0, 0); \
+ attr = #_name; \
+ break
+#define ATTR_STR_IMM(_name) \
+ case UNSPEC_##_name: \
+ addr = XVECEXP (addr, 0, 0); \
+ attr = #_name; \
+ immideate = true; \
+ break
+
+ switch (post)
+ {
+ ATTR_STR(GOT);
+ ATTR_STR(GOTFUNCDESC);
+ ATTR_STR(PLT);
+ ATTR_STR_IMM(GOTOFF);
+ ATTR_STR_IMM(GOTOFFFUNCDESC);
+ case UNSPEC_PCOFFSET:
+ output_addr_const (file, addr);
+ return;
+ }
+ if (attr)
+ {
+ if (SYMBOL_REF_P(addr))
+ {
+ if (immideate)
+ fprintf (file, "#");
+ output_addr_const (file, addr);
+ fprintf (file, "@%s", attr);
+ }
+ else
+ rx_print_operand (file, addr, 0);
+ return;
+ }
+ }
/* Fall through. */
case LABEL_REF:
case SYMBOL_REF:
@@ -490,6 +627,15 @@ rx_assemble_integer (rtx x, unsigned int size, int
is_aligned)
{
const char * op = integer_asm_op (size, is_aligned);
+ if (TARGET_FDPIC && size == UNITS_PER_WORD
+ && GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (x))
+ {
+ fputs (op, asm_out_file);
+ output_addr_const (asm_out_file, x);
+ fputs ("@FUNCDESC\n", asm_out_file);
+ return true;
+ }
+
if (! CONST_INT_P (x))
return default_assemble_integer (x, size, is_aligned);
@@ -917,6 +1063,11 @@ rx_print_operand (FILE * file, rtx op, int letter)
fprintf (file, ")");
return;
}
+ case UNSPEC_PCOFFSET:
+ if (print_hash)
+ fprintf (file, "#");
+ output_addr_const(file, op);
+ return;
}
/* Fall through */
@@ -1496,7 +1647,8 @@ rx_get_stack_layout (unsigned int * lowest,
to call-used by rx_conditional_register_usage. If so then
they can be used in the fast interrupt handler without
saving them on the stack. */
- || (is_fast_interrupt_func (NULL_TREE)
+ || is_fast_interrupt_func (NULL_TREE)
+ || ((flag_pic && reg == PIC_REG)
&& ! IN_RANGE (reg, 10, 13))))
{
if (low == 0)
@@ -1832,6 +1984,13 @@ rx_expand_prologue (void)
GEN_INT ((HOST_WIDE_INT) stack_size),
true);
}
+ if (crtl->uses_pic_offset_table && !TARGET_FDPIC)
+ {
+ rtx picreg = gen_rtx_REG (Pmode, PIC_REG);
+ rtx gotsym = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
+ emit_insn (gen_mvfc (picreg, GEN_INT (CTRLREG_PC)));
+ emit_move_insn (picreg, gen_rtx_PLUS (Pmode, picreg, gotsym));
+ }
}
static void
@@ -2681,7 +2840,14 @@ rx_elf_asm_cdtor (rtx symbol, int priority, bool is_ctor)
switch_to_section (s);
assemble_align (POINTER_SIZE);
- assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
+ if (!TARGET_FDPIC)
+ assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
+ else
+ {
+ fputs ("\t.long\t", asm_out_file);
+ output_addr_const (asm_out_file, symbol);
+ fputs ("\n", asm_out_file);
+ }
}
static void
@@ -2832,6 +2998,13 @@ rx_option_override (void)
if (flag_strict_volatile_bitfields < 0 && abi_version_at_least(2))
flag_strict_volatile_bitfields = 1;
+ /* FDPIC code is a special form of PIC, and the vast majority of code
+ generation constraints that apply to PIC also apply to FDPIC, so we
+ set flag_pic to avoid the need to check TARGET_FDPIC everywhere
+ flag_pic is checked. */
+ if (TARGET_FDPIC && !flag_pic)
+ flag_pic = 2;
+
rx_override_options_after_change ();
/* These values are bytes, not log. */
@@ -2879,7 +3052,7 @@ rx_warn_func_return (tree decl)
static bool
rx_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
- if (TARGET_JSR)
+ if (TARGET_JSR || TARGET_FDPIC)
return false;
/* Do not allow indirect tailcalls. The
@@ -2936,10 +3109,17 @@ rx_is_legitimate_constant (machine_mode mode
ATTRIBUTE_UNUSED, rtx x)
{
case LABEL_REF:
case SYMBOL_REF:
- return true;
+ return !flag_pic;
case UNSPEC:
- return XINT (x, 1) == UNSPEC_CONST || XINT (x, 1) == UNSPEC_PID_ADDR;
+ return XINT (x, 1) == UNSPEC_CONST ||
+ XINT (x, 1) == UNSPEC_PID_ADDR ||
+ XINT (x, 1) == UNSPEC_GOT ||
+ XINT (x, 1) == UNSPEC_GOTOFF ||
+ XINT (x, 1) == UNSPEC_GOTFUNCDESC ||
+ XINT (x, 1) == UNSPEC_GOTOFFFUNCDESC ||
+ XINT (x, 1) == UNSPEC_PLT ||
+ XINT (x, 1) == UNSPEC_PCOFFSET;
default:
/* FIXME: Can this ever happen ? */
@@ -3644,7 +3824,196 @@ rx_c_mode_for_floating_type (enum tree_index ti)
return TARGET_64BIT_DOUBLES ? DFmode : SFmode;
return default_mode_for_floating_type (ti);
}
+
+int rx_legitimate_pic_operand_p(rtx x)
+{
+ return (! nonpic_symbol_mentioned_p (x)
+ && GET_CODE (x) != SYMBOL_REF);
+}
+
+
+
+/* Emit insns to load the function address from FUNCDESC (an FDPIC
+ function descriptor) and the GOT address */
+
+rtx
+rx_load_function_descriptor (rtx sym)
+{
+ rtx gotsym = gen_sym2GOTOFFFUNCDESC (sym);
+ rtx picreg = gen_rtx_REG (Pmode, PIC_REG);
+ rtx func = gen_reg_rtx (GET_MODE (sym));
+ rtx got;
+ rtx tmp = gen_reg_rtx(Pmode);
+
+ emit_move_insn (picreg, rx_get_fdpic_reg_initial_val ());
+ PUT_MODE (gotsym, Pmode);
+ got = gen_rtx_MEM(Pmode, plus_constant(Pmode, picreg, 4));
+ emit_move_insn(tmp, gen_rtx_MEM(Pmode, gotsym));
+ emit_move_insn(picreg, gen_rtx_PLUS(Pmode, picreg, tmp));
+ emit_move_insn(func, gen_rtx_MEM(Pmode, picreg));
+ emit_insn(gen_set_got(picreg, got));
+
+ return func;
+}
+
+/* Return an rtx holding the initial value of the FDPIC register (the
+ FDPIC pointer passed in from the caller). */
+
+rtx
+rx_get_fdpic_reg_initial_val (void)
+{
+ return get_hard_reg_initial_val (Pmode, PIC_REG);
+}
+
+rtx
+rx_mov_pic_operands (rtx x, bool offset)
+{
+ rtx gotsym = NULL;
+ rtx funcsym = NULL;
+ rtx picreg = gen_rtx_REG (Pmode, PIC_REG);
+ rtx t;
+
+ if (!can_create_pseudo_p ())
+ {
+ if (!offset &&
+ MEM_P(x) && GET_CODE(XEXP(x, 0)) == PLUS &&
+ GET_CODE(XEXP(XEXP(x, 0), 1)) == CONST &&
+ GET_CODE(XEXP(XEXP(XEXP(x, 0), 1), 0)) == UNSPEC)
+ switch(XINT(XEXP(XEXP(XEXP(x, 0), 1), 0), 1))
+ {
+ case UNSPEC_GOTOFFFUNCDESC:
+ XINT(XEXP(XEXP(XEXP(x, 0), 1), 0), 1) = UNSPEC_GOTFUNCDESC;
+ break;
+ case UNSPEC_GOTOFF:
+ XINT(XEXP(XEXP(XEXP(x, 0), 1), 0), 1) = UNSPEC_GOT;
+ break;
+ }
+ return x;
+ }
+ if (GET_CODE(x) == CONST &&
+ (GET_CODE(XEXP(x, 0)) == PLUS || GET_CODE(XEXP(x, 0)) == MINUS) &&
+ GET_CODE(XEXP(XEXP(x, 0), 0)) == SYMBOL_REF)
+ {
+ if (TREE_READONLY (SYMBOL_REF_DECL (XEXP(XEXP(x, 0), 0))))
+ {
+ // PC relative with offset
+ rtx label = gen_pcoffset_label(XEXP(XEXP(x, 0), 0));
+ t = gen_reg_rtx (GET_MODE(x));
+ emit_insn(gen_load_pc_location(t, GEN_INT(CTRLREG_PC)));
+ emit_insn(gen_addsi3(t, t, label));
+ if (GET_CODE(XEXP(x, 0)) == PLUS)
+ emit_insn(gen_addsi3(t, t, XEXP(XEXP(x, 0), 1)));
+ else
+ emit_insn(gen_subsi3(t, t, XEXP(XEXP(x, 0), 1)));
+ return t;
+ }
+ else
+ {
+ // GOT with offset
+ gotsym = gen_sym2GOT (XEXP(XEXP(x, 0), 0));
+ t = gen_reg_rtx (GET_MODE(x));
+ emit_move_insn(t, gen_rtx_MEM(GET_MODE(x),
+ gen_rtx_PLUS(Pmode, picreg, gotsym)));
+ crtl->uses_pic_offset_table = true;
+ if (GET_CODE(XEXP(x, 0)) == PLUS)
+ emit_insn(gen_addsi3(t, t, XEXP(XEXP(x, 0), 1)));
+ else
+ emit_insn(gen_subsi3(t, t, XEXP(XEXP(x, 0), 1)));
+ return t;
+ }
+ }
+ if (GET_CODE(x) == SYMBOL_REF)
+ {
+ if (!RTX_FLAG (x, frame_related))
+ {
+ if (TARGET_FDPIC && SYMBOL_REF_FUNCTION_P(x))
+ funcsym = !offset ?
+ gen_sym2GOTOFFFUNCDESC (x) : gen_sym2GOTFUNCDESC (x);
+ else
+ gotsym = offset ? gen_sym2GOTOFF (x) : gen_sym2GOT(x);
+ }
+ else if (TREE_READONLY (SYMBOL_REF_DECL (x)))
+ {
+ // PC relative address
+ rtx label = gen_pcoffset_label(x);
+ t = gen_reg_rtx (GET_MODE(x));
+ emit_insn(gen_load_pc_location(t, GEN_INT(CTRLREG_PC)));
+ emit_insn(gen_addsi3(t, t, label));
+ return t;
+ }
+ else
+ gotsym = gen_sym2GOT (x);
+ }
+ if (gotsym)
+ {
+ if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_DECL (x)
+ && (TREE_READONLY (SYMBOL_REF_DECL (x))))
+ {
+ rtx reg = gen_reg_rtx(Pmode);
+ rtx label = gen_pcoffset_label(x);
+ emit_insn(gen_load_pc_location(reg, GEN_INT (CTRLREG_PC)));
+ return gen_rtx_PLUS(Pmode, reg, label);
+ }
+ t = gen_rtx_MEM(GET_MODE(x), gen_rtx_PLUS(Pmode, picreg, gotsym));
+ crtl->uses_pic_offset_table = true;
+ return t;
+ }
+ else if (funcsym)
+ {
+ t = gen_reg_rtx (GET_MODE(x));
+ emit_insn(gen_addsi3(t, picreg, funcsym));
+ return t;
+ }
+ else
+ return x;
+}
+
+/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA. */
+static bool
+rx_asm_output_addr_const_extra (FILE *file, rtx x)
+{
+ if (GET_CODE (x) == UNSPEC)
+ {
+ switch (XINT (x, 1))
+ {
+ case UNSPEC_PCOFFSET:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fprintf (file, " - 1b");
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+static bool
+rx_const_not_ok_for_debug_p (rtx p)
+{
+ return (GET_CODE (p) == SYMBOL_REF);
+}
+
+rtx rx_pic_vector_address(rtx index)
+{
+ rtx base = gen_reg_rtx(SImode);
+ rtx label = gen_pcoffset_label(XEXP(index, 1));
+
+ emit_insn(gen_load_pc_location(base, GEN_INT(1)));
+ XEXP(index, 1) = gen_rtx_PLUS(SImode, base, label);
+ return index;
+}
+
+bool
+rx_cannot_force_const_mem_p (machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x ATTRIBUTE_UNUSED)
+{
+ return flag_pic;
+}
+
+
#undef TARGET_NARROW_VOLATILE_BITFIELD
#define TARGET_NARROW_VOLATILE_BITFIELD
rx_narrow_volatile_bitfield
@@ -3809,6 +4178,15 @@ rx_c_mode_for_floating_type (enum tree_index ti)
#undef TARGET_DOCUMENTATION_NAME
#define TARGET_DOCUMENTATION_NAME "RX"
+#undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA
+#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA rx_asm_output_addr_const_extra
+
+#undef TARGET_CONST_NOT_OK_FOR_DEBUG_P
+#define TARGET_CONST_NOT_OK_FOR_DEBUG_P rx_const_not_ok_for_debug_p
+
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM rx_cannot_force_const_mem_p
+
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-rx.h"
--
2.47.3