[ARM/FDPIC,04/21,ARM] FDPIC: Add support for FDPIC for arm architecture

Message ID 20180525080354.13295-5-christophe.lyon@st.com
State New
Headers show
Series
  • FDPIC ARM for ARM
Related show

Commit Message

Christophe Lyon May 25, 2018, 8:03 a.m.
The FDPIC register is hard-coded to r9, as defined in the ABI.

We have to disable tailcall optimizations if we don't know if the
target function is in the same module. If not, we have to set r9 to
the value associated with the target module.

When generating a symbol address, we have to take into account whether
it is a pointer to data or to a function, because different
relocations are needed.

2018-XX-XX  Christophe Lyon  <christophe.lyon@st.com>
	Mickaël Guêné <mickael.guene@st.com>

	* config/arm/arm-c.c (__FDPIC__): Define new pre-processor macro
	in FDPIC mode.
	* config/arm/arm-protos.h (arm_load_function_descriptor): Declare
	new function.
	* config/arm/arm.c (arm_option_override): Define pic register to
	r9.
	(arm_function_ok_for_sibcall) Disable sibcall optimization if we
	have no decl or go through PLT.
	(arm_load_pic_register): Handle TARGET_FDPIC.
	(arm_is_segment_info_known): New function.
	(arm_pic_static_addr): Add support for FDPIC.
	(arm_load_function_descriptor): New function.
	(arm_assemble_integer): Add support for FDPIC.
	* config/arm/arm.h (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED): Define.
	* config/arm/arm.md (call): Add support for FDPIC.
	(call_value): Likewise.
	(*restore_pic_register_after_call): New pattern.
	(untyped_call): Disable if FDPIC.
	(untyped_return): Likewise.
	* config/arm/unspecs.md (UNSPEC_PIC_RESTORE): New.

Change-Id: Icee8484772f97ac6f3a9574df4aa4f25a8196786

-- 
2.6.3

Comments

Kyrill Tkachov June 8, 2018, 10:31 a.m. | #1
Hi Christophe,

On 25/05/18 09:03, Christophe Lyon wrote:
> The FDPIC register is hard-coded to r9, as defined in the ABI.

>

> We have to disable tailcall optimizations if we don't know if the

> target function is in the same module. If not, we have to set r9 to

> the value associated with the target module.

>

> When generating a symbol address, we have to take into account whether

> it is a pointer to data or to a function, because different

> relocations are needed.

>

> 2018-XX-XX  Christophe Lyon  <christophe.lyon@st.com>

>         Mickaël Guêné <mickael.guene@st.com>

>

>         * config/arm/arm-c.c (__FDPIC__): Define new pre-processor macro

>         in FDPIC mode.

>         * config/arm/arm-protos.h (arm_load_function_descriptor): Declare

>         new function.

>         * config/arm/arm.c (arm_option_override): Define pic register to

>         r9.

>         (arm_function_ok_for_sibcall) Disable sibcall optimization if we

>         have no decl or go through PLT.

>         (arm_load_pic_register): Handle TARGET_FDPIC.

>         (arm_is_segment_info_known): New function.

>         (arm_pic_static_addr): Add support for FDPIC.

>         (arm_load_function_descriptor): New function.

>         (arm_assemble_integer): Add support for FDPIC.

>         * config/arm/arm.h (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED): Define.

>         * config/arm/arm.md (call): Add support for FDPIC.

>         (call_value): Likewise.

>         (*restore_pic_register_after_call): New pattern.

>         (untyped_call): Disable if FDPIC.

>         (untyped_return): Likewise.

>         * config/arm/unspecs.md (UNSPEC_PIC_RESTORE): New.

>

> Change-Id: Icee8484772f97ac6f3a9574df4aa4f25a8196786

>

> diff --git a/gcc/config/arm/arm-c.c b/gcc/config/arm/arm-c.c

> index 4471f79..90733cc 100644

> --- a/gcc/config/arm/arm-c.c

> +++ b/gcc/config/arm/arm-c.c

> @@ -202,6 +202,8 @@ arm_cpu_builtins (struct cpp_reader* pfile)

>        builtin_define ("__ARM_EABI__");

>      }

>

> +  def_or_undef_macro (pfile, "__FDPIC__", TARGET_FDPIC);

> +

>    def_or_undef_macro (pfile, "__ARM_ARCH_EXT_IDIV__", TARGET_IDIV);

>    def_or_undef_macro (pfile, "__ARM_FEATURE_IDIV", TARGET_IDIV);

>

> diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h

> index 8537262..edebeb7 100644

> --- a/gcc/config/arm/arm-protos.h

> +++ b/gcc/config/arm/arm-protos.h

> @@ -134,6 +134,7 @@ extern int arm_max_const_double_inline_cost (void);

>  extern int arm_const_double_inline_cost (rtx);

>  extern bool arm_const_double_by_parts (rtx);

>  extern bool arm_const_double_by_immediates (rtx);

> +extern rtx arm_load_function_descriptor (rtx funcdesc);

>  extern void arm_emit_call_insn (rtx, rtx, bool);

>  bool detect_cmse_nonsecure_call (tree);

>  extern const char *output_call (rtx *);

> diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c

> index 4a5da7e..56670e3 100644

> --- a/gcc/config/arm/arm.c

> +++ b/gcc/config/arm/arm.c

> @@ -3475,6 +3475,12 @@ arm_option_override (void)

>    if (flag_pic && TARGET_VXWORKS_RTP)

>      arm_pic_register = 9;

>

> +  /* If in FDPIC mode then force arm_pic_register to be r9.  */

> +  if (TARGET_FDPIC)

> +    {

> +      arm_pic_register = 9;

> +    }

> +


Leave out the braces.
Also, I believe you'll want to add option checking for TARGET_FDPIC.
Your cover letter says that this series supports Armv7. So you should add
checks in arm_override to error out on unsupported configurations appropriately
(pre-Armv7? TARGET_THUMB1? float-abi configurations?)

Thanks,
Kyrill

>    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

>
Christophe Lyon June 11, 2018, 6:48 a.m. | #2
On 8 June 2018 at 12:31, Kyrill  Tkachov <kyrylo.tkachov@foss.arm.com> wrote:
> Hi Christophe,

>

>

> On 25/05/18 09:03, Christophe Lyon wrote:

>>

>> The FDPIC register is hard-coded to r9, as defined in the ABI.

>>

>> We have to disable tailcall optimizations if we don't know if the

>> target function is in the same module. If not, we have to set r9 to

>> the value associated with the target module.

>>

>> When generating a symbol address, we have to take into account whether

>> it is a pointer to data or to a function, because different

>> relocations are needed.

>>

>> 2018-XX-XX  Christophe Lyon  <christophe.lyon@st.com>

>>         Mickaël Guêné <mickael.guene@st.com>

>>

>>         * config/arm/arm-c.c (__FDPIC__): Define new pre-processor macro

>>         in FDPIC mode.

>>         * config/arm/arm-protos.h (arm_load_function_descriptor): Declare

>>         new function.

>>         * config/arm/arm.c (arm_option_override): Define pic register to

>>         r9.

>>         (arm_function_ok_for_sibcall) Disable sibcall optimization if we

>>         have no decl or go through PLT.

>>         (arm_load_pic_register): Handle TARGET_FDPIC.

>>         (arm_is_segment_info_known): New function.

>>         (arm_pic_static_addr): Add support for FDPIC.

>>         (arm_load_function_descriptor): New function.

>>         (arm_assemble_integer): Add support for FDPIC.

>>         * config/arm/arm.h (PIC_OFFSET_TABLE_REG_CALL_CLOBBERED): Define.

>>         * config/arm/arm.md (call): Add support for FDPIC.

>>         (call_value): Likewise.

>>         (*restore_pic_register_after_call): New pattern.

>>         (untyped_call): Disable if FDPIC.

>>         (untyped_return): Likewise.

>>         * config/arm/unspecs.md (UNSPEC_PIC_RESTORE): New.

>>

>> Change-Id: Icee8484772f97ac6f3a9574df4aa4f25a8196786

>>

>> diff --git a/gcc/config/arm/arm-c.c b/gcc/config/arm/arm-c.c

>> index 4471f79..90733cc 100644

>> --- a/gcc/config/arm/arm-c.c

>> +++ b/gcc/config/arm/arm-c.c

>> @@ -202,6 +202,8 @@ arm_cpu_builtins (struct cpp_reader* pfile)

>>        builtin_define ("__ARM_EABI__");

>>      }

>>

>> +  def_or_undef_macro (pfile, "__FDPIC__", TARGET_FDPIC);

>> +

>>    def_or_undef_macro (pfile, "__ARM_ARCH_EXT_IDIV__", TARGET_IDIV);

>>    def_or_undef_macro (pfile, "__ARM_FEATURE_IDIV", TARGET_IDIV);

>>

>> diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h

>> index 8537262..edebeb7 100644

>> --- a/gcc/config/arm/arm-protos.h

>> +++ b/gcc/config/arm/arm-protos.h

>> @@ -134,6 +134,7 @@ extern int arm_max_const_double_inline_cost (void);

>>  extern int arm_const_double_inline_cost (rtx);

>>  extern bool arm_const_double_by_parts (rtx);

>>  extern bool arm_const_double_by_immediates (rtx);

>> +extern rtx arm_load_function_descriptor (rtx funcdesc);

>>  extern void arm_emit_call_insn (rtx, rtx, bool);

>>  bool detect_cmse_nonsecure_call (tree);

>>  extern const char *output_call (rtx *);

>> diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c

>> index 4a5da7e..56670e3 100644

>> --- a/gcc/config/arm/arm.c

>> +++ b/gcc/config/arm/arm.c

>> @@ -3475,6 +3475,12 @@ arm_option_override (void)

>>    if (flag_pic && TARGET_VXWORKS_RTP)

>>      arm_pic_register = 9;

>>

>> +  /* If in FDPIC mode then force arm_pic_register to be r9.  */

>> +  if (TARGET_FDPIC)

>> +    {

>> +      arm_pic_register = 9;

>> +    }

>> +

>

>

> Leave out the braces.

> Also, I believe you'll want to add option checking for TARGET_FDPIC.

> Your cover letter says that this series supports Armv7. So you should add

> checks in arm_override to error out on unsupported configurations

> appropriately

> (pre-Armv7? TARGET_THUMB1? float-abi configurations?)

>


Indeed, it will be cleaner.

Thanks

> Thanks,

> Kyrill

>

>

>>    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

>>

>

Patch

diff --git a/gcc/config/arm/arm-c.c b/gcc/config/arm/arm-c.c
index 4471f79..90733cc 100644
--- a/gcc/config/arm/arm-c.c
+++ b/gcc/config/arm/arm-c.c
@@ -202,6 +202,8 @@  arm_cpu_builtins (struct cpp_reader* pfile)
       builtin_define ("__ARM_EABI__");
     }
 
+  def_or_undef_macro (pfile, "__FDPIC__", TARGET_FDPIC);
+
   def_or_undef_macro (pfile, "__ARM_ARCH_EXT_IDIV__", TARGET_IDIV);
   def_or_undef_macro (pfile, "__ARM_FEATURE_IDIV", TARGET_IDIV);
 
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 8537262..edebeb7 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -134,6 +134,7 @@  extern int arm_max_const_double_inline_cost (void);
 extern int arm_const_double_inline_cost (rtx);
 extern bool arm_const_double_by_parts (rtx);
 extern bool arm_const_double_by_immediates (rtx);
+extern rtx arm_load_function_descriptor (rtx funcdesc);
 extern void arm_emit_call_insn (rtx, rtx, bool);
 bool detect_cmse_nonsecure_call (tree);
 extern const char *output_call (rtx *);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 4a5da7e..56670e3 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -3475,6 +3475,12 @@  arm_option_override (void)
   if (flag_pic && TARGET_VXWORKS_RTP)
     arm_pic_register = 9;
 
+  /* If in FDPIC mode then force arm_pic_register to be r9.  */
+  if (TARGET_FDPIC)
+    {
+      arm_pic_register = 9;
+    }
+
   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" [