nios2_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
{
fprintf (file, "\tmov\tr8, ra\n");
- if (flag_pic)
+ if (flag_pic == 1)
{
fprintf (file, "\tnextpc\tr2\n");
fprintf (file, "\t1: movhi\tr3, %%hiadj(_gp_got - 1b)\n");
fprintf (file, "\tldw\tr2, %%call(_mcount)(r2)\n");
fprintf (file, "\tcallr\tr2\n");
}
+ else if (flag_pic == 2)
+ {
+ fprintf (file, "\tnextpc\tr2\n");
+ fprintf (file, "\t1: movhi\tr3, %%hiadj(_gp_got - 1b)\n");
+ fprintf (file, "\taddi\tr3, r3, %%lo(_gp_got - 1b)\n");
+ fprintf (file, "\tadd\tr2, r2, r3\n");
+ fprintf (file, "\tmovhi\tr3, %%call_hiadj(_mcount)\n");
+ fprintf (file, "\taddi\tr3, %%call_lo(_mcount)\n");
+ fprintf (file, "\tadd\tr3, r2, r3\n");
+ fprintf (file, "\tldw\tr2, 0(r3)\n");
+ fprintf (file, "\tcallr\tr2\n");
+ }
else
fprintf (file, "\tcall\t_mcount\n");
fprintf (file, "\tmov\tra, r8\n");
}
else
warning (0, "ignoring unrecognized switch %<-mcustom-fpu-cfg%> "
- "value %<%s%>", cfg);
+ "value %<%s%>", cfgname);
/* Guard against errors in the standard configurations. */
nios2_custom_check_insns ();
return ret;
}
+/* Return true for large offsets requiring hiadj/lo relocation pairs. */
+static bool
+nios2_large_offset_p (int unspec)
+{
+ gcc_assert (nios2_unspec_reloc_name (unspec) != NULL);
+
+ if (flag_pic == 2
+ /* FIXME: TLS GOT offset relocations will eventually also get this
+ treatment, after binutils support for those are also completed. */
+ && (unspec == UNSPEC_PIC_SYM || unspec == UNSPEC_PIC_CALL_SYM))
+ return true;
+
+ /* 'gotoff' offsets are always hiadj/lo. */
+ if (unspec == UNSPEC_PIC_GOTOFF_SYM)
+ return true;
+
+ return false;
+}
+
+/* Return true for conforming unspec relocations. Also used in
+ constraints.md and predicates.md. */
+bool
+nios2_unspec_reloc_p (rtx op)
+{
+ return (GET_CODE (op) == CONST
+ && GET_CODE (XEXP (op, 0)) == UNSPEC
+ && ! nios2_large_offset_p (XINT (XEXP (op, 0), 1)));
+}
+
+/* Helper to generate unspec constant. */
+static rtx
+nios2_unspec_offset (rtx loc, int unspec)
+{
+ return gen_rtx_CONST (Pmode, gen_rtx_UNSPEC (Pmode, gen_rtvec (1, loc),
+ unspec));
+}
+
+/* Generate GOT pointer based address with large offset. */
static rtx
-nios2_unspec_address (rtx loc, rtx base_reg, int unspec)
+nios2_large_got_address (rtx sym, rtx offset)
{
- rtx unspec_offset =
- gen_rtx_CONST (Pmode, gen_rtx_UNSPEC (Pmode, gen_rtvec (1, loc),
- unspec));
- return gen_rtx_PLUS (Pmode, base_reg, unspec_offset);
+ rtx addr = gen_reg_rtx (Pmode);
+ emit_insn (gen_add3_insn (addr, pic_offset_table_rtx,
+ force_reg (Pmode, offset)));
+ return addr;
}
+/* Generate a GOT pointer based address. */
static rtx
nios2_got_address (rtx loc, int unspec)
{
+ rtx offset = nios2_unspec_offset (loc, unspec);
crtl->uses_pic_offset_table = 1;
- return nios2_unspec_address (loc, pic_offset_table_rtx, unspec);
+
+ if (nios2_large_offset_p (unspec))
+ return nios2_large_got_address (loc, offset);
+
+ return gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset);
}
/* Generate the code to access LOC, a thread local SYMBOL_REF. The
case TLS_MODEL_LOCAL_DYNAMIC:
tmp = gen_reg_rtx (Pmode);
emit_move_insn (tmp, nios2_got_address (loc, UNSPEC_ADD_TLS_LDM));
- return nios2_unspec_address (loc, nios2_call_tls_get_addr (tmp),
- UNSPEC_ADD_TLS_LDO);
+ return gen_rtx_PLUS (Pmode, nios2_call_tls_get_addr (tmp),
+ nios2_unspec_offset (loc, UNSPEC_ADD_TLS_LDO));
case TLS_MODEL_INITIAL_EXEC:
tmp = gen_reg_rtx (Pmode);
case TLS_MODEL_LOCAL_EXEC:
tp = gen_rtx_REG (Pmode, TP_REGNO);
- return nios2_unspec_address (loc, tp, UNSPEC_ADD_TLS_LE);
-
+ return gen_rtx_PLUS (Pmode, tp,
+ nios2_unspec_offset (loc, UNSPEC_ADD_TLS_LE));
default:
gcc_unreachable ();
}
return flags;
}
+/* Return true if SYMBOL_REF X binds locally. */
+
+static bool
+nios2_symbol_binds_local_p (const_rtx x)
+{
+ return (SYMBOL_REF_DECL (x)
+ ? targetm.binds_local_p (SYMBOL_REF_DECL (x))
+ : SYMBOL_REF_LOCAL_P (x));
+}
/* Position independent code related. */
static rtx
nios2_load_pic_address (rtx sym, int unspec)
{
- rtx gotaddr = nios2_got_address (sym, unspec);
- return gen_const_mem (Pmode, gotaddr);
+ if (flag_pic == 2
+ && GET_CODE (sym) == SYMBOL_REF
+ && nios2_symbol_binds_local_p (sym))
+ /* Under -fPIC, generate a GOTOFF address for local symbols. */
+ return nios2_got_address (sym, UNSPEC_PIC_GOTOFF_SYM);
+
+ return gen_const_mem (Pmode, nios2_got_address (sym, unspec));
}
/* Nonzero if the constant value X is a legitimate general operand
bool
nios2_legitimate_pic_operand_p (rtx x)
{
+ if (GET_CODE (x) == CONST
+ && GET_CODE (XEXP (x, 0)) == UNSPEC
+ && nios2_large_offset_p (XINT (XEXP (x, 0), 1)))
+ return true;
+
return ! (GET_CODE (x) == SYMBOL_REF
|| GET_CODE (x) == LABEL_REF || GET_CODE (x) == CONST);
}
rtx unspec, offset, reg = XEXP (x, 0);
split_const (XEXP (x, 1), &unspec, &offset);
if (GET_CODE (unspec) == UNSPEC
- && nios2_unspec_reloc_name (XINT (unspec, 1)) != NULL
+ && !nios2_large_offset_p (XINT (unspec, 1))
&& offset != const0_rtx)
{
unspec = copy_rtx (unspec);
}
if (GET_CODE (from) == SYMBOL_REF || GET_CODE (from) == LABEL_REF
- || GET_CODE (from) == CONST)
+ || (GET_CODE (from) == CONST
+ && GET_CODE (XEXP (from, 0)) != UNSPEC))
from = nios2_legitimize_constant_address (from);
operands[0] = to;
output_addr_const (file, op);
return;
}
- else if (letter == 'H')
- {
- fprintf (file, "%%hiadj(");
- output_addr_const (file, op);
- fprintf (file, ")");
- return;
- }
- else if (letter == 'L')
- {
- fprintf (file, "%%lo(");
+ else if (letter == 'H' || letter == 'L')
+ {
+ fprintf (file, "%%");
+ if (GET_CODE (op) == CONST
+ && GET_CODE (XEXP (op, 0)) == UNSPEC)
+ {
+ rtx unspec = XEXP (op, 0);
+ int unspec_reloc = XINT (unspec, 1);
+ gcc_assert (nios2_large_offset_p (unspec_reloc));
+ fprintf (file, "%s_", nios2_unspec_reloc_name (unspec_reloc));
+ op = XVECEXP (unspec, 0, 0);
+ }
+ fprintf (file, letter == 'H' ? "hiadj(" : "lo(");
output_addr_const (file, op);
fprintf (file, ")");
return;
- }
+ }
break;
case SUBREG:
return "got";
case UNSPEC_PIC_CALL_SYM:
return "call";
+ case UNSPEC_PIC_GOTOFF_SYM:
+ return "gotoff";
case UNSPEC_LOAD_TLS_IE:
return "tls_ie";
case UNSPEC_ADD_TLS_LE:
}
}
-/* Return true for conforming unspec relocations. Also used in
- constraints.md and predicates.md. */
-bool
-nios2_unspec_reloc_p (rtx op)
-{
- return (GET_CODE (op) == CONST
- && GET_CODE (XEXP (op, 0)) == UNSPEC
- && nios2_unspec_reloc_name (XINT (XEXP (op, 0), 1)) != NULL);
-}
-
/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA. */
static bool
nios2_output_addr_const_extra (FILE *file, rtx op)