Message ID | 20180201001627.GA21117@gmail.com |
---|---|
State | New |
Headers | show |
Series | Use ptr_mode to save/restore pointers in builtin jmpbuf | expand |
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 >
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.
> 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);
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.
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.
> 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.
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.
> 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.
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.
> 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.
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.
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 --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; +}