diff mbox series

Use ptr_mode to save/restore pointers in builtin jmpbuf

Message ID 20180201001627.GA21117@gmail.com
State New
Headers show
Series Use ptr_mode to save/restore pointers in builtin jmpbuf | expand

Commit Message

H.J. Lu Feb. 1, 2018, 12:16 a.m. UTC
We currently read and write beyond the builtin jmpbuf on ILP32 targets
where Pmode == DImode and ptr_mode == SImode.  Since the builtin jmpbuf
is an array of 5 pointers, ptr_mode should be used to save and restore
frame and program pointers.  Since x86 only saves stack pointer in
stack save area, STACK_SAVEAREA_MODE should be ptr_mode, not Pmode.

Note: If ptr_mode != Pmode, builtin setjmp/longjmp tests may fail.  When
it happens, please check if there are correct save_stack_nonlocal and
restore_stack_nonlocal expand patterns.

Tested on i686 and x86-64.  OK for trunk?

H.J.
----
gcc/

	PR middle-end/84150
	* builtins.c (expand_builtin_setjmp_setup): Use ptr_mode to
	save frame and program pointers.
	(expand_builtin_longjmp): Use ptr_mode to restore frame and
	program pointers.
	* config/i386/i386.md (save_stack_nonlocal): New expand pattern.
	(restore_stack_nonlocal): Likewise.
	* config/i386/i386.h (STACK_SAVEAREA_MODE): New.

gcc/testsuite/

	PR middle-end/84150
	* gcc.dg/pr84150.c: New test.
	* gcc.target/i386/pr84150.c: Likewisde.
---
 gcc/builtins.c                          | 36 +++++++++++++++++---------
 gcc/config/i386/i386.h                  |  4 +++
 gcc/config/i386/i386.md                 | 36 ++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/pr84150.c          | 45 +++++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr84150.c | 44 ++++++++++++++++++++++++++++++++
 5 files changed, 153 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr84150.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr84150.c

Comments

Uros Bizjak Feb. 1, 2018, 7:42 a.m. UTC | #1
On Thu, Feb 1, 2018 at 1:16 AM, H.J. Lu <hongjiu.lu@intel.com> wrote:
>
> We currently read and write beyond the builtin jmpbuf on ILP32 targets
> where Pmode == DImode and ptr_mode == SImode.  Since the builtin jmpbuf
> is an array of 5 pointers, ptr_mode should be used to save and restore
> frame and program pointers.  Since x86 only saves stack pointer in
> stack save area, STACK_SAVEAREA_MODE should be ptr_mode, not Pmode.
>
> Note: If ptr_mode != Pmode, builtin setjmp/longjmp tests may fail.  When
> it happens, please check if there are correct save_stack_nonlocal and
> restore_stack_nonlocal expand patterns.
>
> Tested on i686 and x86-64.  OK for trunk?
>
> H.J.
> ----
> gcc/
>
>         PR middle-end/84150
>         * builtins.c (expand_builtin_setjmp_setup): Use ptr_mode to
>         save frame and program pointers.
>         (expand_builtin_longjmp): Use ptr_mode to restore frame and
>         program pointers.
>         * config/i386/i386.md (save_stack_nonlocal): New expand pattern.
>         (restore_stack_nonlocal): Likewise.
>         * config/i386/i386.h (STACK_SAVEAREA_MODE): New.
>
> gcc/testsuite/
>
>         PR middle-end/84150
>         * gcc.dg/pr84150.c: New test.
>         * gcc.target/i386/pr84150.c: Likewisde.
> ---
>  gcc/builtins.c                          | 36 +++++++++++++++++---------
>  gcc/config/i386/i386.h                  |  4 +++
>  gcc/config/i386/i386.md                 | 36 ++++++++++++++++++++++++++
>  gcc/testsuite/gcc.dg/pr84150.c          | 45 +++++++++++++++++++++++++++++++++
>  gcc/testsuite/gcc.target/i386/pr84150.c | 44 ++++++++++++++++++++++++++++++++
>  5 files changed, 153 insertions(+), 12 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/pr84150.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr84150.c
>
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index 683c6ec6e5b..c3d45d5e3fa 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -840,6 +840,7 @@ expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label)
>    machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
>    rtx stack_save;
>    rtx mem;
> +  rtx tmp;
>
>    if (setjmp_alias_set == -1)
>      setjmp_alias_set = new_alias_set ();
> @@ -852,20 +853,25 @@ expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label)
>       the buffer and use the rest of it for the stack save area, which
>       is machine-dependent.  */
>
> -  mem = gen_rtx_MEM (Pmode, buf_addr);
> +  mem = gen_rtx_MEM (ptr_mode, buf_addr);
>    set_mem_alias_set (mem, setjmp_alias_set);
> -  emit_move_insn (mem, targetm.builtin_setjmp_frame_value ());
> +  tmp = targetm.builtin_setjmp_frame_value ();
> +  if (GET_MODE (tmp) != ptr_mode)
> +    tmp = gen_lowpart (ptr_mode, tmp);
> +  emit_move_insn (mem, tmp);
>
> -  mem = gen_rtx_MEM (Pmode, plus_constant (Pmode, buf_addr,
> -                                          GET_MODE_SIZE (Pmode))),
> +  mem = gen_rtx_MEM (ptr_mode, plus_constant (Pmode, buf_addr,
> +                                             GET_MODE_SIZE (ptr_mode))),
>    set_mem_alias_set (mem, setjmp_alias_set);
>
> -  emit_move_insn (validize_mem (mem),
> -                 force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label)));
> +  tmp = force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label));
> +  if (Pmode != ptr_mode)
> +    tmp = gen_lowpart (ptr_mode, tmp);
> +  emit_move_insn (validize_mem (mem), tmp);
>
>    stack_save = gen_rtx_MEM (sa_mode,
>                             plus_constant (Pmode, buf_addr,
> -                                          2 * GET_MODE_SIZE (Pmode)));
> +                                          2 * GET_MODE_SIZE (ptr_mode)));
>    set_mem_alias_set (stack_save, setjmp_alias_set);
>    emit_stack_save (SAVE_NONLOCAL, &stack_save);
>
> @@ -991,12 +997,14 @@ expand_builtin_longjmp (rtx buf_addr, rtx value)
>      emit_insn (targetm.gen_builtin_longjmp (buf_addr));
>    else
>      {
> -      fp = gen_rtx_MEM (Pmode, buf_addr);
> -      lab = gen_rtx_MEM (Pmode, plus_constant (Pmode, buf_addr,
> -                                              GET_MODE_SIZE (Pmode)));
> +      fp = gen_rtx_MEM (ptr_mode, buf_addr);
> +      lab = gen_rtx_MEM (ptr_mode,
> +                        plus_constant (Pmode, buf_addr,
> +                                       GET_MODE_SIZE (ptr_mode)));
>
> -      stack = gen_rtx_MEM (sa_mode, plus_constant (Pmode, buf_addr,
> -                                                  2 * GET_MODE_SIZE (Pmode)));
> +      stack = gen_rtx_MEM (sa_mode,
> +                          plus_constant (Pmode, buf_addr,
> +                                         2 * GET_MODE_SIZE (ptr_mode)));
>        set_mem_alias_set (fp, setjmp_alias_set);
>        set_mem_alias_set (lab, setjmp_alias_set);
>        set_mem_alias_set (stack, setjmp_alias_set);
> @@ -1015,6 +1023,10 @@ expand_builtin_longjmp (rtx buf_addr, rtx value)
>           emit_clobber (gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)));
>           emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx));
>
> +#ifdef POINTERS_EXTEND_UNSIGNED
> +         if (GET_MODE (fp) != Pmode)
> +           fp = convert_to_mode (Pmode, fp, POINTERS_EXTEND_UNSIGNED);
> +#endif
>           emit_move_insn (hard_frame_pointer_rtx, fp);
>           emit_stack_restore (SAVE_NONLOCAL, stack);
>
> diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
> index 59522ccba02..16aace86e48 100644
> --- a/gcc/config/i386/i386.h
> +++ b/gcc/config/i386/i386.h
> @@ -1937,6 +1937,10 @@ do {                                                     \
>     between pointers and any other objects of this machine mode.  */
>  #define Pmode (ix86_pmode == PMODE_DI ? DImode : SImode)
>
> +/* Supply a definition of STACK_SAVEAREA_MODE for emit_stack_save.  We
> +   only need save and restore stack pointer in ptr_mode.  */
> +#define STACK_SAVEAREA_MODE(LEVEL) ptr_mode
> +
>  /* Specify the machine mode that bounds have.  */
>  #define BNDmode (ix86_pmode == PMODE_DI ? BND64mode : BND32mode)
>
> diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
> index c08e4f55cff..c563a26cd60 100644
> --- a/gcc/config/i386/i386.md
> +++ b/gcc/config/i386/i386.md
> @@ -18375,6 +18375,42 @@
>    "* return output_probe_stack_range (operands[0], operands[2]);"
>    [(set_attr "type" "multi")])
>
> +;; Non-local goto support.
> +
> +(define_expand "save_stack_nonlocal"
> +  [(use (match_operand 0 "memory_operand" ""))
> +   (use (match_operand 1 "register_operand" ""))]
> +  ""
> +{
> +  /* Stack is saved in ptr_mode and stack register is in Pmode.  */
> +  if (GET_MODE (operands[0]) != GET_MODE (operands[1]))
> +    {
> +      if (GET_MODE (operands[0]) != ptr_mode
> +         || GET_MODE (operands[1]) != Pmode)
> +       gcc_unreachable ();

gcc_assert instead of if (...) gcc_unreachable.

> +      operands[1] = gen_rtx_SUBREG (ptr_mode, operands[1], 0);

gen_lowpart

> +    }
> +  emit_move_insn (operands[0], operands[1]);
> +  DONE;
> +})
> +
> +(define_expand "restore_stack_nonlocal"
> +  [(use (match_operand 0 "register_operand" ""))
> +   (use (match_operand 1 "memory_operand" ""))]
> +  ""
> +{
> +  /* Stack is saved in ptr_mode and stack register is in Pmode.  */
> +  if (GET_MODE (operands[0]) != GET_MODE (operands[1]))
> +    {
> +      if (GET_MODE (operands[0]) != Pmode
> +         || GET_MODE (operands[1]) != ptr_mode)
> +       gcc_unreachable ();

Also here.

> +      operands[1] = gen_rtx_ZERO_EXTEND (Pmode, operands[1]);
> +    }
> +  emit_move_insn (operands[0], operands[1]);
> +  DONE;
> +})
> +
>  /* Additional processing for builtin_setjmp.  Store the shadow stack pointer
>     as a forth element in jmpbuf.  */
>  (define_expand "builtin_setjmp_setup"
> diff --git a/gcc/testsuite/gcc.dg/pr84150.c b/gcc/testsuite/gcc.dg/pr84150.c
> new file mode 100644
> index 00000000000..1e7a04c1e9c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr84150.c
> @@ -0,0 +1,45 @@
> +/* { dg-do run } */
> +/* { dg-options "-O" } */
> +/* { dg-require-effective-target indirect_jumps } */
> +
> +void *buf[6] = {
> +  (void *) -1,
> +  (void *) -1,
> +  (void *) -1,
> +  (void *) -1,
> +  (void *) -1,
> +  (void *) -1
> +};
> +
> +void raise0(void)
> +{
> +  __builtin_longjmp (buf, 1);
> +}
> +
> +int execute(int cmd)
> +{
> +  int last = 0;
> +
> +  if (__builtin_setjmp (buf) == 0)
> +    while (1)
> +      {
> +        last = 1;
> +        raise0 ();
> +      }
> +
> +  if (last == 0)
> +    return 0;
> +  else
> +    return cmd;
> +}
> +
> +int main(void)
> +{
> +  if (execute (1) == 0)
> +    __builtin_abort ();
> +
> +  if (buf[5] != (void *) -1)
> +    __builtin_abort ();
> +
> +  return 0;
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/pr84150.c b/gcc/testsuite/gcc.target/i386/pr84150.c
> new file mode 100644
> index 00000000000..11d3d361487
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr84150.c
> @@ -0,0 +1,44 @@
> +/* { dg-do run { target x32 } } */
> +/* { dg-options "-O -maddress-mode=long" } */
> +
> +void *buf[6] = {
> +  (void *) -1,
> +  (void *) -1,
> +  (void *) -1,
> +  (void *) -1,
> +  (void *) -1,
> +  (void *) -1
> +};
> +
> +void raise0(void)
> +{
> +  __builtin_longjmp (buf, 1);
> +}
> +
> +int execute(int cmd)
> +{
> +  int last = 0;
> +
> +  if (__builtin_setjmp (buf) == 0)
> +    while (1)
> +      {
> +        last = 1;
> +        raise0 ();
> +      }
> +
> +  if (last == 0)
> +    return 0;
> +  else
> +    return cmd;
> +}
> +
> +int main(void)
> +{
> +  if (execute (1) == 0)
> +    __builtin_abort ();
> +
> +  if (buf[5] != (void *) -1)
> +    __builtin_abort ();
> +
> +  return 0;
> +}
> --
> 2.14.3
>
H.J. Lu Feb. 1, 2018, 1:43 p.m. UTC | #2
On Wed, Jan 31, 2018 at 11:42 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Thu, Feb 1, 2018 at 1:16 AM, H.J. Lu <hongjiu.lu@intel.com> wrote:
>>
>> We currently read and write beyond the builtin jmpbuf on ILP32 targets
>> where Pmode == DImode and ptr_mode == SImode.  Since the builtin jmpbuf
>> is an array of 5 pointers, ptr_mode should be used to save and restore
>> frame and program pointers.  Since x86 only saves stack pointer in
>> stack save area, STACK_SAVEAREA_MODE should be ptr_mode, not Pmode.
>>
>> Note: If ptr_mode != Pmode, builtin setjmp/longjmp tests may fail.  When
>> it happens, please check if there are correct save_stack_nonlocal and
>> restore_stack_nonlocal expand patterns.
>>
>> Tested on i686 and x86-64.  OK for trunk?
>>
>> H.J.
>> ----
>> gcc/
>>
>>         PR middle-end/84150
>>         * builtins.c (expand_builtin_setjmp_setup): Use ptr_mode to
>>         save frame and program pointers.
>>         (expand_builtin_longjmp): Use ptr_mode to restore frame and
>>         program pointers.
>>         * config/i386/i386.md (save_stack_nonlocal): New expand pattern.
>>         (restore_stack_nonlocal): Likewise.
>>         * config/i386/i386.h (STACK_SAVEAREA_MODE): New.

>> diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
>> index c08e4f55cff..c563a26cd60 100644
>> --- a/gcc/config/i386/i386.md
>> +++ b/gcc/config/i386/i386.md
>> @@ -18375,6 +18375,42 @@
>>    "* return output_probe_stack_range (operands[0], operands[2]);"
>>    [(set_attr "type" "multi")])
>>
>> +;; Non-local goto support.
>> +
>> +(define_expand "save_stack_nonlocal"
>> +  [(use (match_operand 0 "memory_operand" ""))
>> +   (use (match_operand 1 "register_operand" ""))]
>> +  ""
>> +{
>> +  /* Stack is saved in ptr_mode and stack register is in Pmode.  */
>> +  if (GET_MODE (operands[0]) != GET_MODE (operands[1]))
>> +    {
>> +      if (GET_MODE (operands[0]) != ptr_mode
>> +         || GET_MODE (operands[1]) != Pmode)
>> +       gcc_unreachable ();
>
> gcc_assert instead of if (...) gcc_unreachable.
>
>> +      operands[1] = gen_rtx_SUBREG (ptr_mode, operands[1], 0);
>
> gen_lowpart
>
>> +    }
>> +  emit_move_insn (operands[0], operands[1]);
>> +  DONE;
>> +})
>> +
>> +(define_expand "restore_stack_nonlocal"
>> +  [(use (match_operand 0 "register_operand" ""))
>> +   (use (match_operand 1 "memory_operand" ""))]
>> +  ""
>> +{
>> +  /* Stack is saved in ptr_mode and stack register is in Pmode.  */
>> +  if (GET_MODE (operands[0]) != GET_MODE (operands[1]))
>> +    {
>> +      if (GET_MODE (operands[0]) != Pmode
>> +         || GET_MODE (operands[1]) != ptr_mode)
>> +       gcc_unreachable ();
>
> Also here.
>

Here is the updated patch.  OK for trunk?

BTW, the -maddress-mode=long test in Igor's patch for

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84066

is expected to fail without this fix.
Eric Botcazou Feb. 2, 2018, 5:53 p.m. UTC | #3
> We currently read and write beyond the builtin jmpbuf on ILP32 targets
> where Pmode == DImode and ptr_mode == SImode.  Since the builtin jmpbuf
> is an array of 5 pointers, ptr_mode should be used to save and restore
> frame and program pointers.  Since x86 only saves stack pointer in
> stack save area, STACK_SAVEAREA_MODE should be ptr_mode, not Pmode.

I think that some targets really need Pmode.  And the buffer should be able to 
accomodate up to 5 words, see init_eh:

      /* Compute a minimally sized jump buffer.  We need room to store at
	 least 3 pointers - stack pointer, frame pointer and return address.
	 Plus for some targets we need room for an extra pointer - in the
	 case of MIPS this is the global pointer.  This makes a total of four
	 pointers, but to be safe we actually allocate room for 5.

	 If pointers are smaller than words then we allocate enough room for
	 5 words, just in case the backend needs this much room.  For more
	 discussion on this issue see:
	 http://gcc.gnu.org/ml/gcc-patches/2014-05/msg00313.html.  */
      if (POINTER_SIZE > BITS_PER_WORD)
	tmp = size_int (5 - 1);
      else
	tmp = size_int ((5 * BITS_PER_WORD / POINTER_SIZE) - 1);
H.J. Lu Feb. 2, 2018, 5:57 p.m. UTC | #4
On Fri, Feb 2, 2018 at 9:53 AM, Eric Botcazou <ebotcazou@adacore.com> wrote:
>> We currently read and write beyond the builtin jmpbuf on ILP32 targets
>> where Pmode == DImode and ptr_mode == SImode.  Since the builtin jmpbuf
>> is an array of 5 pointers, ptr_mode should be used to save and restore
>> frame and program pointers.  Since x86 only saves stack pointer in
>> stack save area, STACK_SAVEAREA_MODE should be ptr_mode, not Pmode.
>
> I think that some targets really need Pmode.  And the buffer should be able to

Some targets need more than Pmode.  ia64/ia64.h has

/* We need 32 bytes, so we can save the sp, ar.rnat, ar.bsp, and ar.pfs of
   the function containing a non-local goto target.  */

#define STACK_SAVEAREA_MODE(LEVEL) \
  ((LEVEL) == SAVE_NONLOCAL ? OImode : Pmode)

> accomodate up to 5 words, see init_eh:
>
>       /* Compute a minimally sized jump buffer.  We need room to store at
>          least 3 pointers - stack pointer, frame pointer and return address.
>          Plus for some targets we need room for an extra pointer - in the
>          case of MIPS this is the global pointer.  This makes a total of four
>          pointers, but to be safe we actually allocate room for 5.
>
>          If pointers are smaller than words then we allocate enough room for
>          5 words, just in case the backend needs this much room.  For more
>          discussion on this issue see:
>          http://gcc.gnu.org/ml/gcc-patches/2014-05/msg00313.html.  */
>       if (POINTER_SIZE > BITS_PER_WORD)
>         tmp = size_int (5 - 1);
>       else
>         tmp = size_int ((5 * BITS_PER_WORD / POINTER_SIZE) - 1);
>

My only changes STACK_SAVEAREA_MODE for x86.  Other targets
are unchanged.
H.J. Lu Feb. 2, 2018, 6:42 p.m. UTC | #5
On Fri, Feb 2, 2018 at 9:57 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Fri, Feb 2, 2018 at 9:53 AM, Eric Botcazou <ebotcazou@adacore.com> wrote:
>>> We currently read and write beyond the builtin jmpbuf on ILP32 targets
>>> where Pmode == DImode and ptr_mode == SImode.  Since the builtin jmpbuf
>>> is an array of 5 pointers, ptr_mode should be used to save and restore
>>> frame and program pointers.  Since x86 only saves stack pointer in
>>> stack save area, STACK_SAVEAREA_MODE should be ptr_mode, not Pmode.
>>
>> I think that some targets really need Pmode.  And the buffer should be able to
>
> Some targets need more than Pmode.  ia64/ia64.h has
>
> /* We need 32 bytes, so we can save the sp, ar.rnat, ar.bsp, and ar.pfs of
>    the function containing a non-local goto target.  */
>
> #define STACK_SAVEAREA_MODE(LEVEL) \
>   ((LEVEL) == SAVE_NONLOCAL ? OImode : Pmode)
>
>> accomodate up to 5 words, see init_eh:

One more thing.  Word can be bigger than pointer on ILP32 targets.
5 pointers aren't sufficient.

>>       /* Compute a minimally sized jump buffer.  We need room to store at
>>          least 3 pointers - stack pointer, frame pointer and return address.
>>          Plus for some targets we need room for an extra pointer - in the
>>          case of MIPS this is the global pointer.  This makes a total of four
>>          pointers, but to be safe we actually allocate room for 5.
>>
>>          If pointers are smaller than words then we allocate enough room for
>>          5 words, just in case the backend needs this much room.  For more
>>          discussion on this issue see:
>>          http://gcc.gnu.org/ml/gcc-patches/2014-05/msg00313.html.  */
>>       if (POINTER_SIZE > BITS_PER_WORD)
>>         tmp = size_int (5 - 1);
>>       else
>>         tmp = size_int ((5 * BITS_PER_WORD / POINTER_SIZE) - 1);
>>
>
> My only changes STACK_SAVEAREA_MODE for x86.  Other targets
> are unchanged.
Eric Botcazou Feb. 2, 2018, 6:54 p.m. UTC | #6
> One more thing.  Word can be bigger than pointer on ILP32 targets.
> 5 pointers aren't sufficient.

Yes, that's why the buffer should be 5 words, as init_eh allocates now.
H.J. Lu Feb. 2, 2018, 6:56 p.m. UTC | #7
On Fri, Feb 2, 2018 at 10:54 AM, Eric Botcazou <ebotcazou@adacore.com> wrote:
>> One more thing.  Word can be bigger than pointer on ILP32 targets.
>> 5 pointers aren't sufficient.
>
> Yes, that's why the buffer should be 5 words, as init_eh allocates now.
>

But, that is not what the builtin setjmp/longjmp tests have.
Eric Botcazou Feb. 2, 2018, 8:55 p.m. UTC | #8
> But, that is not what the builtin setjmp/longjmp tests have.

Yes, but I don't think that we want to risk breaking a working compiler on 
some targets because peculiar tests don't pass on another.  I think that 
init_eh is OK for x32 so SJLJ exceptions work and the issue is only with the 
undocumented builtin setjmp/longjmp.

What happens on Aarch64 -milp32 for example?  Would it be OK to save/restore 
only 32-bit values?  And MIPS n32?

Maybe we can define another builtin, e.g. __builtin_setjmp_size, and let the 
compiler compute the size based on the same formula as init_eh or somesuch:

  char buf[__builtin_setjmp_size ()];


Btw, the "array of pointers" thing is an interpolation, here's an excerpt of 
start_dynamic_handler in GCC 2.8.x:

/* Emit RTL to start a dynamic handler on the EH runtime dynamic
   handler stack.  This should only be used by expand_eh_region_start
   or expand_eh_region_start_tree.  */

static void
start_dynamic_handler ()
{
  rtx dhc, dcc;
  rtx x, arg, buf;
  int size;

#ifndef DONT_USE_BUILTIN_SETJMP
  /* The number of Pmode words for the setjmp buffer, when using the
     builtin setjmp/longjmp, see expand_builtin, case
     BUILT_IN_LONGJMP.  */
  size = 5;
#else

so the tests are incorrect, not the implementation.
H.J. Lu Feb. 2, 2018, 8:59 p.m. UTC | #9
On Fri, Feb 2, 2018 at 12:55 PM, Eric Botcazou <ebotcazou@adacore.com> wrote:
>> But, that is not what the builtin setjmp/longjmp tests have.
>
> Yes, but I don't think that we want to risk breaking a working compiler on
> some targets because peculiar tests don't pass on another.  I think that
> init_eh is OK for x32 so SJLJ exceptions work and the issue is only with the
> undocumented builtin setjmp/longjmp.
>
> What happens on Aarch64 -milp32 for example?  Would it be OK to save/restore
> only 32-bit values?  And MIPS n32?
>
> Maybe we can define another builtin, e.g. __builtin_setjmp_size, and let the
> compiler compute the size based on the same formula as init_eh or somesuch:
>
>   char buf[__builtin_setjmp_size ()];
>

I think it should be a predefined macro.

H.J.
Eric Botcazou Feb. 2, 2018, 9:16 p.m. UTC | #10
> I think it should be a predefined macro.

Macros cannot be used outside of the C family of languages while builtins can, 
but a macro might be sufficient in practice and easier to implement indeed.
Richard Earnshaw (lists) Feb. 6, 2018, 11:38 a.m. UTC | #11
On 02/02/18 20:55, Eric Botcazou wrote:
>> But, that is not what the builtin setjmp/longjmp tests have.
> 
> Yes, but I don't think that we want to risk breaking a working compiler on 
> some targets because peculiar tests don't pass on another.  I think that 
> init_eh is OK for x32 so SJLJ exceptions work and the issue is only with the 
> undocumented builtin setjmp/longjmp.
> 
> What happens on Aarch64 -milp32 for example?  Would it be OK to save/restore 
> only 32-bit values?  And MIPS n32?
> 

No.  At least, not for the frame pointer on AArch64.  If the caller has
used FP as a normal register, then all 64 bits must be preserved.  The
same is true of any other register that might also be used as a
non-frame-related register.

R.
H.J. Lu Feb. 6, 2018, 12:27 p.m. UTC | #12
On Tue, Feb 6, 2018 at 3:38 AM, Richard Earnshaw (lists)
<Richard.Earnshaw@arm.com> wrote:
> On 02/02/18 20:55, Eric Botcazou wrote:
>>> But, that is not what the builtin setjmp/longjmp tests have.
>>
>> Yes, but I don't think that we want to risk breaking a working compiler on
>> some targets because peculiar tests don't pass on another.  I think that
>> init_eh is OK for x32 so SJLJ exceptions work and the issue is only with the
>> undocumented builtin setjmp/longjmp.
>>
>> What happens on Aarch64 -milp32 for example?  Would it be OK to save/restore
>> only 32-bit values?  And MIPS n32?
>>
>
> No.  At least, not for the frame pointer on AArch64.  If the caller has
> used FP as a normal register, then all 64 bits must be preserved.  The
> same is true of any other register that might also be used as a
> non-frame-related register.

If FP is used as a normal register, it won't be saved as frame pointer
in builtin
jmp buffer.
diff mbox series

Patch

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 683c6ec6e5b..c3d45d5e3fa 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -840,6 +840,7 @@  expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label)
   machine_mode sa_mode = STACK_SAVEAREA_MODE (SAVE_NONLOCAL);
   rtx stack_save;
   rtx mem;
+  rtx tmp;
 
   if (setjmp_alias_set == -1)
     setjmp_alias_set = new_alias_set ();
@@ -852,20 +853,25 @@  expand_builtin_setjmp_setup (rtx buf_addr, rtx receiver_label)
      the buffer and use the rest of it for the stack save area, which
      is machine-dependent.  */
 
-  mem = gen_rtx_MEM (Pmode, buf_addr);
+  mem = gen_rtx_MEM (ptr_mode, buf_addr);
   set_mem_alias_set (mem, setjmp_alias_set);
-  emit_move_insn (mem, targetm.builtin_setjmp_frame_value ());
+  tmp = targetm.builtin_setjmp_frame_value ();
+  if (GET_MODE (tmp) != ptr_mode)
+    tmp = gen_lowpart (ptr_mode, tmp);
+  emit_move_insn (mem, tmp);
 
-  mem = gen_rtx_MEM (Pmode, plus_constant (Pmode, buf_addr,
-					   GET_MODE_SIZE (Pmode))),
+  mem = gen_rtx_MEM (ptr_mode, plus_constant (Pmode, buf_addr,
+					      GET_MODE_SIZE (ptr_mode))),
   set_mem_alias_set (mem, setjmp_alias_set);
 
-  emit_move_insn (validize_mem (mem),
-		  force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label)));
+  tmp = force_reg (Pmode, gen_rtx_LABEL_REF (Pmode, receiver_label));
+  if (Pmode != ptr_mode)
+    tmp = gen_lowpart (ptr_mode, tmp);
+  emit_move_insn (validize_mem (mem), tmp);
 
   stack_save = gen_rtx_MEM (sa_mode,
 			    plus_constant (Pmode, buf_addr,
-					   2 * GET_MODE_SIZE (Pmode)));
+					   2 * GET_MODE_SIZE (ptr_mode)));
   set_mem_alias_set (stack_save, setjmp_alias_set);
   emit_stack_save (SAVE_NONLOCAL, &stack_save);
 
@@ -991,12 +997,14 @@  expand_builtin_longjmp (rtx buf_addr, rtx value)
     emit_insn (targetm.gen_builtin_longjmp (buf_addr));
   else
     {
-      fp = gen_rtx_MEM (Pmode, buf_addr);
-      lab = gen_rtx_MEM (Pmode, plus_constant (Pmode, buf_addr,
-					       GET_MODE_SIZE (Pmode)));
+      fp = gen_rtx_MEM (ptr_mode, buf_addr);
+      lab = gen_rtx_MEM (ptr_mode,
+			 plus_constant (Pmode, buf_addr,
+					GET_MODE_SIZE (ptr_mode)));
 
-      stack = gen_rtx_MEM (sa_mode, plus_constant (Pmode, buf_addr,
-						   2 * GET_MODE_SIZE (Pmode)));
+      stack = gen_rtx_MEM (sa_mode,
+			   plus_constant (Pmode, buf_addr,
+					  2 * GET_MODE_SIZE (ptr_mode)));
       set_mem_alias_set (fp, setjmp_alias_set);
       set_mem_alias_set (lab, setjmp_alias_set);
       set_mem_alias_set (stack, setjmp_alias_set);
@@ -1015,6 +1023,10 @@  expand_builtin_longjmp (rtx buf_addr, rtx value)
 	  emit_clobber (gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)));
 	  emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx));
 
+#ifdef POINTERS_EXTEND_UNSIGNED
+	  if (GET_MODE (fp) != Pmode)
+	    fp = convert_to_mode (Pmode, fp, POINTERS_EXTEND_UNSIGNED);
+#endif
 	  emit_move_insn (hard_frame_pointer_rtx, fp);
 	  emit_stack_restore (SAVE_NONLOCAL, stack);
 
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 59522ccba02..16aace86e48 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1937,6 +1937,10 @@  do {							\
    between pointers and any other objects of this machine mode.  */
 #define Pmode (ix86_pmode == PMODE_DI ? DImode : SImode)
 
+/* Supply a definition of STACK_SAVEAREA_MODE for emit_stack_save.  We
+   only need save and restore stack pointer in ptr_mode.  */
+#define STACK_SAVEAREA_MODE(LEVEL) ptr_mode
+
 /* Specify the machine mode that bounds have.  */
 #define BNDmode (ix86_pmode == PMODE_DI ? BND64mode : BND32mode)
 
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index c08e4f55cff..c563a26cd60 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -18375,6 +18375,42 @@ 
   "* return output_probe_stack_range (operands[0], operands[2]);"
   [(set_attr "type" "multi")])
 
+;; Non-local goto support.
+
+(define_expand "save_stack_nonlocal"
+  [(use (match_operand 0 "memory_operand" ""))
+   (use (match_operand 1 "register_operand" ""))]
+  ""
+{
+  /* Stack is saved in ptr_mode and stack register is in Pmode.  */
+  if (GET_MODE (operands[0]) != GET_MODE (operands[1]))
+    {
+      if (GET_MODE (operands[0]) != ptr_mode
+	  || GET_MODE (operands[1]) != Pmode)
+	gcc_unreachable ();
+      operands[1] = gen_rtx_SUBREG (ptr_mode, operands[1], 0);
+    }
+  emit_move_insn (operands[0], operands[1]);
+  DONE;
+})
+
+(define_expand "restore_stack_nonlocal"
+  [(use (match_operand 0 "register_operand" ""))
+   (use (match_operand 1 "memory_operand" ""))]
+  ""
+{
+  /* Stack is saved in ptr_mode and stack register is in Pmode.  */
+  if (GET_MODE (operands[0]) != GET_MODE (operands[1]))
+    {
+      if (GET_MODE (operands[0]) != Pmode
+	  || GET_MODE (operands[1]) != ptr_mode)
+	gcc_unreachable ();
+      operands[1] = gen_rtx_ZERO_EXTEND (Pmode, operands[1]);
+    }
+  emit_move_insn (operands[0], operands[1]);
+  DONE;
+})
+
 /* Additional processing for builtin_setjmp.  Store the shadow stack pointer
    as a forth element in jmpbuf.  */
 (define_expand "builtin_setjmp_setup"
diff --git a/gcc/testsuite/gcc.dg/pr84150.c b/gcc/testsuite/gcc.dg/pr84150.c
new file mode 100644
index 00000000000..1e7a04c1e9c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr84150.c
@@ -0,0 +1,45 @@ 
+/* { dg-do run } */
+/* { dg-options "-O" } */
+/* { dg-require-effective-target indirect_jumps } */
+
+void *buf[6] = {
+  (void *) -1,
+  (void *) -1,
+  (void *) -1,
+  (void *) -1,
+  (void *) -1,
+  (void *) -1
+};
+
+void raise0(void)
+{
+  __builtin_longjmp (buf, 1);
+}
+
+int execute(int cmd)
+{
+  int last = 0;
+
+  if (__builtin_setjmp (buf) == 0)
+    while (1)
+      {
+        last = 1;
+        raise0 ();
+      }
+
+  if (last == 0)
+    return 0;
+  else
+    return cmd;
+}
+
+int main(void)
+{
+  if (execute (1) == 0)
+    __builtin_abort ();
+
+  if (buf[5] != (void *) -1)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr84150.c b/gcc/testsuite/gcc.target/i386/pr84150.c
new file mode 100644
index 00000000000..11d3d361487
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr84150.c
@@ -0,0 +1,44 @@ 
+/* { dg-do run { target x32 } } */
+/* { dg-options "-O -maddress-mode=long" } */
+
+void *buf[6] = {
+  (void *) -1,
+  (void *) -1,
+  (void *) -1,
+  (void *) -1,
+  (void *) -1,
+  (void *) -1
+};
+
+void raise0(void)
+{
+  __builtin_longjmp (buf, 1);
+}
+
+int execute(int cmd)
+{
+  int last = 0;
+
+  if (__builtin_setjmp (buf) == 0)
+    while (1)
+      {
+        last = 1;
+        raise0 ();
+      }
+
+  if (last == 0)
+    return 0;
+  else
+    return cmd;
+}
+
+int main(void)
+{
+  if (execute (1) == 0)
+    __builtin_abort ();
+
+  if (buf[5] != (void *) -1)
+    __builtin_abort ();
+
+  return 0;
+}