if (arm_pic_register_string != NULL)
{
int pic_register = decode_reg_name (arm_pic_register_string);
@@ -7256,6 +7262,21 @@ arm_function_ok_for_sibcall (tree decl, tree exp)
if (cfun->machine->sibcall_blocked)
return false;
+ if (TARGET_FDPIC)
+ {
+ /* In FDPIC, never tailcall something for which we have no decl:
+ the target function could be in a different module, requiring
+ a different r9 value. */
+ if (decl == NULL)
+ return false;
+
+ /* Don't tailcall if we go through the PLT since r9 is then
+ corrupted and we don't restore it for static function
+ call. */
+ if (!targetm.binds_local_p (decl))
+ return false;
+ }
+
/* Never tailcall something if we are generating code for Thumb-1. */
if (TARGET_THUMB1)
return false;
@@ -7634,7 +7655,9 @@ arm_load_pic_register (unsigned long saved_regs
ATTRIBUTE_UNUSED)
{
rtx l1, labelno, pic_tmp, pic_rtx, pic_reg;
- if (crtl->uses_pic_offset_table == 0 || TARGET_SINGLE_PIC_BASE)
+ if (crtl->uses_pic_offset_table == 0
+ || TARGET_SINGLE_PIC_BASE
+ || TARGET_FDPIC)
return;
gcc_assert (flag_pic);
@@ -7702,28 +7725,167 @@ arm_load_pic_register (unsigned long saved_regs
ATTRIBUTE_UNUSED)
emit_use (pic_reg);
}
+/* Try to know if the object will go in text or data segment. */
+static bool
+arm_is_segment_info_known (rtx orig, bool *is_readonly)
+{
+ bool res = false;
+
+ *is_readonly = false;
+
+ if (GET_CODE (orig) == LABEL_REF)
+ {
+ res = true;
+ *is_readonly = true;
+ }
+ else if (GET_CODE (orig) == SYMBOL_REF)
+ {
+ if (CONSTANT_POOL_ADDRESS_P (orig))
+ {
+ res = true;
+ *is_readonly = true;
+ }
+ else if (SYMBOL_REF_LOCAL_P (orig)
+ && !SYMBOL_REF_EXTERNAL_P (orig)
+ && SYMBOL_REF_DECL (orig)
+ && (!DECL_P (SYMBOL_REF_DECL (orig))
+ || !DECL_COMMON (SYMBOL_REF_DECL (orig))))
+ {
+ tree decl = SYMBOL_REF_DECL (orig);
+ tree init = (TREE_CODE (decl) == VAR_DECL)
+ ? DECL_INITIAL (decl) : (TREE_CODE (decl) == CONSTRUCTOR)
+ ? decl : 0;
+ int reloc = 0;
+ bool named_section, readonly;
+
+ if (init && init != error_mark_node)
+ reloc = compute_reloc_for_constant (init);
+
+ named_section = TREE_CODE (decl) == VAR_DECL
+ && lookup_attribute ("section", DECL_ATTRIBUTES (decl));
+ readonly = decl_readonly_section (decl, reloc);
+
+ if (named_section)
+ {
+ /* We don't know where the link script will put this section. */
+ res = false;
+ }
+ else if (!readonly)
+ {
+ res = true;
+ *is_readonly = false;
+ }
+ else
+ {
+ res = true;
+ *is_readonly = true;
+ }
+ }
+ else
+ {
+ /* We don't know. */
+ res = false;
+ }
+ }
+ else
+ gcc_unreachable ();
+
+ return res;
+}
+
/* Generate code to load the address of a static var when flag_pic is set. */
static rtx_insn *
arm_pic_static_addr (rtx orig, rtx reg)
{
rtx l1, labelno, offset_rtx;
+ rtx_insn *insn;
gcc_assert (flag_pic);
- /* We use an UNSPEC rather than a LABEL_REF because this label
- never appears in the code stream. */
- labelno = GEN_INT (pic_labelno++);
- l1 = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_PIC_LABEL);
- l1 = gen_rtx_CONST (VOIDmode, l1);
+ if (TARGET_FDPIC
+ && GET_CODE (orig) == SYMBOL_REF
+ && !SYMBOL_REF_FUNCTION_P (orig))
+ {
+ bool is_readonly;
+
+ if (!arm_is_segment_info_known (orig, &is_readonly))
+ {
+ /* Use GOT relocation. */
+ rtx pat;
+ rtx mem;
+ rtx pic_reg = gen_rtx_REG (Pmode, 9);
- /* On the ARM the PC register contains 'dot + 8' at the time of the
- addition, on the Thumb it is 'dot + 4'. */
- offset_rtx = plus_constant (Pmode, l1, TARGET_ARM ? 8 : 4);
- offset_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, orig, offset_rtx),
- UNSPEC_SYMBOL_OFFSET);
- offset_rtx = gen_rtx_CONST (Pmode, offset_rtx);
+ pat = gen_calculate_pic_address (reg, pic_reg, orig);
- return emit_insn (gen_pic_load_addr_unified (reg, offset_rtx, labelno));
+ /* Make the MEM as close to a constant as possible. */
+ mem = SET_SRC (pat);
+ gcc_assert (MEM_P (mem) && !MEM_VOLATILE_P (mem));
+ MEM_READONLY_P (mem) = 1;
+ MEM_NOTRAP_P (mem) = 1;
+
+ insn = emit_insn (pat);
+ }
+ else if (is_readonly)
+ {
+ /* We can use PC-relative access. */
+ /* We use an UNSPEC rather than a LABEL_REF because this label
+ never appears in the code stream. */
+ labelno = GEN_INT (pic_labelno++);
+ l1 = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_PIC_LABEL);
+ l1 = gen_rtx_CONST (VOIDmode, l1);
+
+ /* On the ARM the PC register contains 'dot + 8' at the time of the
+ addition, on the Thumb it is 'dot + 4'. */
+ offset_rtx = plus_constant (Pmode, l1, TARGET_ARM ? 8 : 4);
+ offset_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, orig, offset_rtx),
+ UNSPEC_SYMBOL_OFFSET);
+ offset_rtx = gen_rtx_CONST (Pmode, offset_rtx);
+
+ insn = emit_insn (gen_pic_load_addr_unified (reg, offset_rtx,
+ labelno));
+ }
+ else
+ {
+ /* We use the GOTOFF relocation. */
+ rtx pic_reg = gen_rtx_REG (Pmode, 9);
+
+ rtx l1 = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, orig), UNSPEC_PIC_SYM);
+ emit_insn (gen_movsi (reg, l1));
+ insn = emit_insn (gen_addsi3 (reg, reg, pic_reg));
+ }
+ }
+ else
+ {
+ if (TARGET_FDPIC
+ && GET_CODE (orig) == SYMBOL_REF
+ && SYMBOL_REF_FUNCTION_P (orig))
+ {
+ rtx pic_reg = gen_rtx_REG (Pmode, 9);
+
+ rtx l1 = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, orig), UNSPEC_PIC_SYM);
+ emit_insn (gen_movsi (reg, l1));
+ insn = emit_insn (gen_addsi3 (reg, reg, pic_reg));
+ }
+ else
+ {
+ /* We use an UNSPEC rather than a LABEL_REF because this label
+ never appears in the code stream. */
+ labelno = GEN_INT (pic_labelno++);
+ l1 = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_PIC_LABEL);
+ l1 = gen_rtx_CONST (VOIDmode, l1);
+
+ /* On the ARM the PC register contains 'dot + 8' at the time of the
+ addition, on the Thumb it is 'dot + 4'. */
+ offset_rtx = plus_constant (Pmode, l1, TARGET_ARM ? 8 : 4);
+ offset_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, orig, offset_rtx),
+ UNSPEC_SYMBOL_OFFSET);
+ offset_rtx = gen_rtx_CONST (Pmode, offset_rtx);
+
+ insn = emit_insn (gen_pic_load_addr_unified (reg, offset_rtx,
+ labelno));
+ }
+ }
+ return insn;
}
/* Return nonzero if X is valid as an ARM state addressing register. */
@@ -15938,9 +16100,35 @@ get_jump_table_size (rtx_jump_table_data *insn)
return 0;
}
+/* Emit insns to load the function address from FUNCDESC (an FDPIC
+ function descriptor) into r8 and the GOT address into r9,
+ returning an rtx for r8. */
+
+rtx
+arm_load_function_descriptor (rtx funcdesc)
+{
+ rtx fnaddrReg = gen_reg_rtx (Pmode);
+ rtx pic_reg = gen_rtx_REG (Pmode, 9);
+ rtx fnaddr = gen_rtx_MEM (Pmode, funcdesc);
+ rtx gotaddr = gen_rtx_MEM (Pmode, plus_constant (Pmode, funcdesc, 4));
+ rtx par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (3));
+
+ emit_move_insn (fnaddrReg, fnaddr);
+ /* The ABI requires the entry point address to be loaded first, so
+ prevent the load from being moved after that of the GOT
+ address. */
+ XVECEXP (par, 0, 0) = gen_rtx_UNSPEC (VOIDmode,
+ gen_rtvec (2, pic_reg, gotaddr),
+ UNSPEC_PIC_RESTORE);
+ XVECEXP (par, 0, 1) = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 9));
+ XVECEXP (par, 0, 2) = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 9));
+ emit_insn (par);
+
+ return fnaddrReg;
+}
+
/* Return the maximum amount of padding that will be inserted before
label LABEL. */
-
static HOST_WIDE_INT
get_label_padding (rtx label)
{
@@ -22885,9 +23073,37 @@ arm_assemble_integer (rtx x, unsigned int size, int
aligned_p)
&& (!SYMBOL_REF_LOCAL_P (x)
|| (SYMBOL_REF_DECL (x)
? DECL_WEAK (SYMBOL_REF_DECL (x)) : 0))))
- fputs ("(GOT)", asm_out_file);
+ {
+ if (TARGET_FDPIC && SYMBOL_REF_FUNCTION_P (x))
+ fputs ("(GOTFUNCDESC)", asm_out_file);
+ else
+ fputs ("(GOT)", asm_out_file);
+ }
else
- fputs ("(GOTOFF)", asm_out_file);
+ {
+ if (TARGET_FDPIC && SYMBOL_REF_FUNCTION_P (x))
+ fputs ("(GOTOFFFUNCDESC)", asm_out_file);
+ else
+ {
+ bool is_readonly;
+
+ if (arm_is_segment_info_known (x, &is_readonly))
+ fputs ("(GOTOFF)", asm_out_file);
+ else
+ fputs ("(GOT)", asm_out_file);
+ }
+ }
+ }
+
+ /* For FDPIC we also have to mark symbol for .data section. */
+ if (TARGET_FDPIC
+ && NEED_GOT_RELOC
+ && flag_pic
+ && !making_const_table
+ && GET_CODE (x) == SYMBOL_REF)
+ {
+ if (SYMBOL_REF_FUNCTION_P (x))
+ fputs ("(FUNCDESC)", asm_out_file);
}
fputc ('\n', asm_out_file);
return true;
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index 34894c0..e8ef439 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -1927,6 +1927,10 @@ extern unsigned arm_pic_register;
data addresses in memory. */
#define PIC_OFFSET_TABLE_REGNUM arm_pic_register
+/* For FDPIC, the FDPIC register is call-clobbered (otherwise PLT
+ entries would need to handle saving and restoring it). */
+#define PIC_OFFSET_TABLE_REG_CALL_CLOBBERED TARGET_FDPIC
+
/* We can't directly access anything that contains a symbol,
nor can we indirect via the constant pool. One exception is
UNSPEC_TLS, which is always PIC. */
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 361a026..78c236c 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -8031,6 +8031,22 @@
rtx callee, pat;
tree addr = MEM_EXPR (operands[0]);
+ /* Force r9 before call. */
+ if (TARGET_FDPIC)
+ {
+ /* No need to update r9 if calling a static function. */
+ callee = XEXP (operands[0], 0);
+ if (GET_CODE (callee) != SYMBOL_REF
+ || !SYMBOL_REF_LOCAL_P (callee)
+ || arm_is_long_call_p (SYMBOL_REF_DECL (callee)))
+ {
+ emit_insn (gen_blockage ());
+ rtx pic_reg = gen_rtx_REG (Pmode, 9);
+ emit_move_insn (pic_reg, get_hard_reg_initial_val (Pmode, 9));
+ emit_insn (gen_rtx_USE (VOIDmode, pic_reg));
+ }
+ }
+
/* In an untyped call, we can get NULL for operand 2. */
if (operands[2] == NULL_RTX)
operands[2] = const0_rtx;
@@ -8044,6 +8060,13 @@
: !REG_P (callee))
XEXP (operands[0], 0) = force_reg (Pmode, callee);
+ if (TARGET_FDPIC && GET_CODE (XEXP (operands[0], 0)) != SYMBOL_REF)
+ {
+ /* Indirect call. */
+ XEXP (operands[0], 0)
+ = arm_load_function_descriptor (XEXP (operands[0], 0));
+ }
+
if (detect_cmse_nonsecure_call (addr))
{
pat = gen_nonsecure_call_internal (operands[0], operands[1],
@@ -8055,10 +8078,38 @@
pat = gen_call_internal (operands[0], operands[1], operands[2]);
arm_emit_call_insn (pat, XEXP (operands[0], 0), false);
}
+
+ /* Restore r9 after call. */
+ if (TARGET_FDPIC)
+ {
+ /* No need to update r9 if calling a static function. */
+ if (GET_CODE (callee) != SYMBOL_REF
+ || !SYMBOL_REF_LOCAL_P (callee)
+ || arm_is_long_call_p (SYMBOL_REF_DECL (callee)))
+ {
+ rtx pic_reg = gen_rtx_REG (Pmode, 9);
+ emit_move_insn (pic_reg, get_hard_reg_initial_val (Pmode, 9));
+ emit_insn (gen_rtx_USE (VOIDmode, pic_reg));
+ emit_insn (gen_blockage ());
+ }
+ }
DONE;
}"
)
+(define_insn "*restore_pic_register_after_call"
+ [(parallel [(unspec [(match_operand:SI 0 "s_register_operand" "=r,r")
+ (match_operand:SI 1 "general_operand" "r,m")]
+ UNSPEC_PIC_RESTORE)
+ (use (match_dup 0))
+ (clobber (match_dup 0))])
+ ]
+ ""
+ "@
+ mov\t%0, %1
+ ldr\t%0, %1"
+)
+
(define_expand "call_internal"
[(parallel [(call (match_operand 0 "memory_operand" "")
(match_operand 1 "general_operand" ""))
@@ -8119,6 +8170,28 @@
rtx pat, callee;
tree addr = MEM_EXPR (operands[1]);
+ if (TARGET_FDPIC)
+ {
+ /* No need to update r9 if calling a static function. */
+ callee = XEXP (operands[1], 0);
+ if (GET_CODE (callee) != SYMBOL_REF
+ || !SYMBOL_REF_LOCAL_P (callee)
+ || arm_is_long_call_p (SYMBOL_REF_DECL (callee)))
+ {
+ rtx par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (3));
+
+ XVECEXP (par, 0, 0) = gen_rtx_UNSPEC (VOIDmode,
+ gen_rtvec (2, gen_rtx_REG (Pmode, 9),
+ get_hard_reg_initial_val (Pmode, 9)),
+ UNSPEC_PIC_RESTORE);
+ XVECEXP (par, 0, 1)
+ = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 9));
+ XVECEXP (par, 0, 2)
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 9));
+ emit_insn (par);
+ }
+ }
+
/* In an untyped call, we can get NULL for operand 2. */
if (operands[3] == 0)
operands[3] = const0_rtx;
@@ -8132,6 +8205,14 @@
: !REG_P (callee))
XEXP (operands[1], 0) = force_reg (Pmode, callee);
+ if (TARGET_FDPIC
+ && GET_CODE (XEXP (operands[1], 0)) != SYMBOL_REF)
+ {
+ /* Indirect call. */
+ XEXP (operands[1], 0)
+ = arm_load_function_descriptor (XEXP (operands[1], 0));
+ }
+
if (detect_cmse_nonsecure_call (addr))
{
pat = gen_nonsecure_call_value_internal (operands[0], operands[1],
@@ -8144,6 +8225,28 @@
operands[2], operands[3]);
arm_emit_call_insn (pat, XEXP (operands[1], 0), false);
}
+ /* Force r9 after call. */
+ if (TARGET_FDPIC)
+ {
+ /* No need to update r9 if calling a static function. */
+ if (GET_CODE (callee) != SYMBOL_REF
+ || !SYMBOL_REF_LOCAL_P (callee)
+ || arm_is_long_call_p (SYMBOL_REF_DECL (callee)))
+ {
+ rtx par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (3));
+
+ XVECEXP (par, 0, 0) = gen_rtx_UNSPEC (VOIDmode,
+ gen_rtvec (2, gen_rtx_REG (Pmode, 9),
+ get_hard_reg_initial_val (Pmode, 9)),
+ UNSPEC_PIC_RESTORE);
+ XVECEXP (par, 0, 1)
+ = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 9));
+ XVECEXP (par, 0, 2)
+ = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, 9));
+ emit_insn (par);
+ }
+ }
+
DONE;
}"
)
@@ -8486,7 +8589,7 @@
(const_int 0))
(match_operand 1 "" "")
(match_operand 2 "" "")])]
- "TARGET_EITHER"
+ "TARGET_EITHER && !TARGET_FDPIC"
"
{
int i;
@@ -8553,7 +8656,7 @@
(define_expand "untyped_return"
[(match_operand:BLK 0 "memory_operand" "")
(match_operand 1 "" "")]
- "TARGET_EITHER"
+ "TARGET_EITHER && !TARGET_FDPIC"
"
{
int i;
diff --git a/gcc/config/arm/unspecs.md b/gcc/config/arm/unspecs.md
index b05f85e..5506e2d 100644
--- a/gcc/config/arm/unspecs.md
+++ b/gcc/config/arm/unspecs.md
@@ -86,6 +86,7 @@
UNSPEC_PROBE_STACK ; Probe stack memory reference
UNSPEC_NONSECURE_MEM ; Represent non-secure memory in ARMv8-M with
; security extension
+ UNSPEC_PIC_RESTORE ; Use to restore fdpic register
])
(define_c_enum "unspec" [
--
2.6.3