Commit 4fb94ef9 by Eric Botcazou Committed by Eric Botcazou

re PR middle-end/65958 (-fstack-check breaks alloca on architectures using generic stack checking)

	PR middle-end/65958
	* config/arm/linux-elf.h (STACK_CHECK_STATIC_BUILTIN): Define.
	* config/arm/arm-protos.h (output_probe_stack_range): Declare.
	* config/arm/arm.c: Include common/common-target.h.
	(use_return_insn): Return 0 if the static chain register was saved
	above a non-APCS frame.
	(arm_compute_static_chain_stack_bytes): Adjust for stack checking.
	(struct scratch_reg): New.
	(get_scratch_register_on_entry): New function.
	(release_scratch_register_on_entry): Likewise.
	(arm_emit_probe_stack_range): Likewise.
	(output_probe_stack_range): Likewise.
	(arm_expand_prologue): Factor out code dealing with the IP register
	for nested function and adjust it for stack checking.
	Invoke arm_emit_probe_stack_range if static builtin stack checking
	is enabled.
	(thumb1_expand_prologue): Sorry out if static builtin stack checking
	is enabled.
	(arm_expand_epilogue): Add the saved static chain register, if any, to
	the amount of pre-pushed registers to pop.
	(arm_frame_pointer_required): Return true if static stack checking is
	enabled and we want to catch the exception with the EABI unwinder.
	* config/arm/unspecs.md (UNSPEC_PROBE_STACK): New constant.
	(UNSPEC_PROBE_STACK_RANGE): Likewise.
	* config/arm/arm.md (probe_stack): New insn.
	(probe_stack_range): Likewise.

From-SVN: r227860
parent 2e96ac06
2015-09-17 Eric Botcazou <ebotcazou@adacore.com>
PR middle-end/65958
* config/arm/linux-elf.h (STACK_CHECK_STATIC_BUILTIN): Define.
* config/arm/arm-protos.h (output_probe_stack_range): Declare.
* config/arm/arm.c: Include common/common-target.h.
(use_return_insn): Return 0 if the static chain register was saved
above a non-APCS frame.
(arm_compute_static_chain_stack_bytes): Adjust for stack checking.
(struct scratch_reg): New.
(get_scratch_register_on_entry): New function.
(release_scratch_register_on_entry): Likewise.
(arm_emit_probe_stack_range): Likewise.
(output_probe_stack_range): Likewise.
(arm_expand_prologue): Factor out code dealing with the IP register
for nested function and adjust it for stack checking.
Invoke arm_emit_probe_stack_range if static builtin stack checking
is enabled.
(thumb1_expand_prologue): Sorry out if static builtin stack checking
is enabled.
(arm_expand_epilogue): Add the saved static chain register, if any, to
the amount of pre-pushed registers to pop.
(arm_frame_pointer_required): Return true if static stack checking is
enabled and we want to catch the exception with the EABI unwinder.
* config/arm/unspecs.md (UNSPEC_PROBE_STACK): New constant.
(UNSPEC_PROBE_STACK_RANGE): Likewise.
* config/arm/arm.md (probe_stack): New insn.
(probe_stack_range): Likewise.
2015-09-17 Richard Biener <rguenther@suse.de> 2015-09-17 Richard Biener <rguenther@suse.de>
* genmatch.c (parser::parse_expr): Improve error message * genmatch.c (parser::parse_expr): Improve error message
...@@ -146,6 +146,7 @@ extern const char *output_add_immediate (rtx *); ...@@ -146,6 +146,7 @@ extern const char *output_add_immediate (rtx *);
extern const char *arithmetic_instr (rtx, int); extern const char *arithmetic_instr (rtx, int);
extern void output_ascii_pseudo_op (FILE *, const unsigned char *, int); extern void output_ascii_pseudo_op (FILE *, const unsigned char *, int);
extern const char *output_return_instruction (rtx, bool, bool, bool); extern const char *output_return_instruction (rtx, bool, bool, bool);
extern const char *output_probe_stack_range (rtx, rtx);
extern void arm_poke_function_name (FILE *, const char *); extern void arm_poke_function_name (FILE *, const char *);
extern void arm_final_prescan_insn (rtx_insn *); extern void arm_final_prescan_insn (rtx_insn *);
extern int arm_debugger_arg_offset (int, rtx); extern int arm_debugger_arg_offset (int, rtx);
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
#include "tm_p.h" #include "tm_p.h"
#include "target.h" #include "target.h"
#include "sched-int.h" #include "sched-int.h"
#include "common/common-target.h"
#include "debug.h" #include "debug.h"
#include "langhooks.h" #include "langhooks.h"
#include "intl.h" #include "intl.h"
...@@ -3669,7 +3670,11 @@ use_return_insn (int iscond, rtx sibling) ...@@ -3669,7 +3670,11 @@ use_return_insn (int iscond, rtx sibling)
/* Or if there is a stack adjustment. However, if the stack pointer /* Or if there is a stack adjustment. However, if the stack pointer
is saved on the stack, we can use a pre-incrementing stack load. */ is saved on the stack, we can use a pre-incrementing stack load. */
|| !(stack_adjust == 0 || (TARGET_APCS_FRAME && frame_pointer_needed || !(stack_adjust == 0 || (TARGET_APCS_FRAME && frame_pointer_needed
&& stack_adjust == 4))) && stack_adjust == 4))
/* Or if the static chain register was saved above the frame, under the
assumption that the stack pointer isn't saved on the stack. */
|| (!(TARGET_APCS_FRAME && frame_pointer_needed)
&& arm_compute_static_chain_stack_bytes() != 0))
return 0; return 0;
saved_int_regs = offsets->saved_regs_mask; saved_int_regs = offsets->saved_regs_mask;
...@@ -19174,8 +19179,10 @@ static int ...@@ -19174,8 +19179,10 @@ static int
arm_compute_static_chain_stack_bytes (void) arm_compute_static_chain_stack_bytes (void)
{ {
/* See the defining assertion in arm_expand_prologue. */ /* See the defining assertion in arm_expand_prologue. */
if (TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM if (IS_NESTED (arm_current_func_type ())
&& IS_NESTED (arm_current_func_type ()) && ((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
|| (flag_stack_check == STATIC_BUILTIN_STACK_CHECK
&& !df_regs_ever_live_p (LR_REGNUM)))
&& arm_r3_live_at_start_p () && arm_r3_live_at_start_p ()
&& crtl->args.pretend_args_size == 0) && crtl->args.pretend_args_size == 0)
return 4; return 4;
...@@ -19270,7 +19277,6 @@ arm_compute_save_reg_mask (void) ...@@ -19270,7 +19277,6 @@ arm_compute_save_reg_mask (void)
return save_reg_mask; return save_reg_mask;
} }
/* Compute a bit mask of which registers need to be /* Compute a bit mask of which registers need to be
saved on the stack for the current function. */ saved on the stack for the current function. */
static unsigned long static unsigned long
...@@ -21098,6 +21104,240 @@ thumb_set_frame_pointer (arm_stack_offsets *offsets) ...@@ -21098,6 +21104,240 @@ thumb_set_frame_pointer (arm_stack_offsets *offsets)
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
} }
struct scratch_reg {
rtx reg;
bool saved;
};
/* Return a short-lived scratch register for use as a 2nd scratch register on
function entry after the registers are saved in the prologue. This register
must be released by means of release_scratch_register_on_entry. IP is not
considered since it is always used as the 1st scratch register if available.
REGNO1 is the index number of the 1st scratch register and LIVE_REGS is the
mask of live registers. */
static void
get_scratch_register_on_entry (struct scratch_reg *sr, unsigned int regno1,
unsigned long live_regs)
{
int regno = -1;
sr->saved = false;
if (regno1 != LR_REGNUM && (live_regs & (1 << LR_REGNUM)) != 0)
regno = LR_REGNUM;
else
{
unsigned int i;
for (i = 4; i < 11; i++)
if (regno1 != i && (live_regs & (1 << i)) != 0)
{
regno = i;
break;
}
if (regno < 0)
{
/* If IP is used as the 1st scratch register for a nested function,
then either r3 wasn't available or is used to preserve IP. */
if (regno1 == IP_REGNUM && IS_NESTED (arm_current_func_type ()))
regno1 = 3;
regno = (regno1 == 3 ? 2 : 3);
sr->saved
= REGNO_REG_SET_P (df_get_live_out (ENTRY_BLOCK_PTR_FOR_FN (cfun)),
regno);
}
}
sr->reg = gen_rtx_REG (SImode, regno);
if (sr->saved)
{
rtx addr = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
rtx insn = emit_set_insn (gen_frame_mem (SImode, addr), sr->reg);
rtx x = gen_rtx_SET (stack_pointer_rtx,
plus_constant (Pmode, stack_pointer_rtx, -4));
RTX_FRAME_RELATED_P (insn) = 1;
add_reg_note (insn, REG_FRAME_RELATED_EXPR, x);
}
}
/* Release a scratch register obtained from the preceding function. */
static void
release_scratch_register_on_entry (struct scratch_reg *sr)
{
if (sr->saved)
{
rtx addr = gen_rtx_POST_INC (Pmode, stack_pointer_rtx);
rtx insn = emit_set_insn (sr->reg, gen_frame_mem (SImode, addr));
rtx x = gen_rtx_SET (stack_pointer_rtx,
plus_constant (Pmode, stack_pointer_rtx, 4));
RTX_FRAME_RELATED_P (insn) = 1;
add_reg_note (insn, REG_FRAME_RELATED_EXPR, x);
}
}
#define PROBE_INTERVAL (1 << STACK_CHECK_PROBE_INTERVAL_EXP)
#if PROBE_INTERVAL > 4096
#error Cannot use indexed addressing mode for stack probing
#endif
/* Emit code to probe a range of stack addresses from FIRST to FIRST+SIZE,
inclusive. These are offsets from the current stack pointer. REGNO1
is the index number of the 1st scratch register and LIVE_REGS is the
mask of live registers. */
static void
arm_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size,
unsigned int regno1, unsigned long live_regs)
{
rtx reg1 = gen_rtx_REG (Pmode, regno1);
/* See if we have a constant small number of probes to generate. If so,
that's the easy case. */
if (size <= PROBE_INTERVAL)
{
emit_move_insn (reg1, GEN_INT (first + PROBE_INTERVAL));
emit_set_insn (reg1, gen_rtx_MINUS (Pmode, stack_pointer_rtx, reg1));
emit_stack_probe (plus_constant (Pmode, reg1, PROBE_INTERVAL - size));
}
/* The run-time loop is made up of 10 insns in the generic case while the
compile-time loop is made up of 4+2*(n-2) insns for n # of intervals. */
else if (size <= 5 * PROBE_INTERVAL)
{
HOST_WIDE_INT i, rem;
emit_move_insn (reg1, GEN_INT (first + PROBE_INTERVAL));
emit_set_insn (reg1, gen_rtx_MINUS (Pmode, stack_pointer_rtx, reg1));
emit_stack_probe (reg1);
/* Probe at FIRST + N * PROBE_INTERVAL for values of N from 2 until
it exceeds SIZE. If only two probes are needed, this will not
generate any code. Then probe at FIRST + SIZE. */
for (i = 2 * PROBE_INTERVAL; i < size; i += PROBE_INTERVAL)
{
emit_set_insn (reg1, plus_constant (Pmode, reg1, -PROBE_INTERVAL));
emit_stack_probe (reg1);
}
rem = size - (i - PROBE_INTERVAL);
if (rem > 4095 || (TARGET_THUMB2 && rem > 255))
{
emit_set_insn (reg1, plus_constant (Pmode, reg1, -PROBE_INTERVAL));
emit_stack_probe (plus_constant (Pmode, reg1, PROBE_INTERVAL - rem));
}
else
emit_stack_probe (plus_constant (Pmode, reg1, -rem));
}
/* Otherwise, do the same as above, but in a loop. Note that we must be
extra careful with variables wrapping around because we might be at
the very top (or the very bottom) of the address space and we have
to be able to handle this case properly; in particular, we use an
equality test for the loop condition. */
else
{
HOST_WIDE_INT rounded_size;
struct scratch_reg sr;
get_scratch_register_on_entry (&sr, regno1, live_regs);
emit_move_insn (reg1, GEN_INT (first));
/* Step 1: round SIZE to the previous multiple of the interval. */
rounded_size = size & -PROBE_INTERVAL;
emit_move_insn (sr.reg, GEN_INT (rounded_size));
/* Step 2: compute initial and final value of the loop counter. */
/* TEST_ADDR = SP + FIRST. */
emit_set_insn (reg1, gen_rtx_MINUS (Pmode, stack_pointer_rtx, reg1));
/* LAST_ADDR = SP + FIRST + ROUNDED_SIZE. */
emit_set_insn (sr.reg, gen_rtx_MINUS (Pmode, reg1, sr.reg));
/* Step 3: the loop
while (TEST_ADDR != LAST_ADDR)
{
TEST_ADDR = TEST_ADDR + PROBE_INTERVAL
probe at TEST_ADDR
}
probes at FIRST + N * PROBE_INTERVAL for values of N from 1
until it is equal to ROUNDED_SIZE. */
emit_insn (gen_probe_stack_range (reg1, reg1, sr.reg));
/* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time
that SIZE is equal to ROUNDED_SIZE. */
if (size != rounded_size)
{
HOST_WIDE_INT rem = size - rounded_size;
if (rem > 4095 || (TARGET_THUMB2 && rem > 255))
{
emit_set_insn (sr.reg,
plus_constant (Pmode, sr.reg, -PROBE_INTERVAL));
emit_stack_probe (plus_constant (Pmode, sr.reg,
PROBE_INTERVAL - rem));
}
else
emit_stack_probe (plus_constant (Pmode, sr.reg, -rem));
}
release_scratch_register_on_entry (&sr);
}
/* Make sure nothing is scheduled before we are done. */
emit_insn (gen_blockage ());
}
/* Probe a range of stack addresses from REG1 to REG2 inclusive. These are
absolute addresses. */
const char *
output_probe_stack_range (rtx reg1, rtx reg2)
{
static int labelno = 0;
char loop_lab[32];
rtx xops[2];
ASM_GENERATE_INTERNAL_LABEL (loop_lab, "LPSRL", labelno++);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, loop_lab);
/* Test if TEST_ADDR == LAST_ADDR. */
xops[0] = reg1;
xops[1] = reg2;
output_asm_insn ("cmp\t%0, %1", xops);
if (TARGET_THUMB2)
fputs ("\tittt\tne\n", asm_out_file);
/* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL. */
xops[1] = GEN_INT (PROBE_INTERVAL);
output_asm_insn ("subne\t%0, %0, %1", xops);
/* Probe at TEST_ADDR and branch. */
output_asm_insn ("strne\tr0, [%0, #0]", xops);
fputs ("\tbne\t", asm_out_file);
assemble_name_raw (asm_out_file, loop_lab);
fputc ('\n', asm_out_file);
return "";
}
/* Generate the prologue instructions for entry into an ARM or Thumb-2 /* Generate the prologue instructions for entry into an ARM or Thumb-2
function. */ function. */
void void
...@@ -21112,7 +21352,9 @@ arm_expand_prologue (void) ...@@ -21112,7 +21352,9 @@ arm_expand_prologue (void)
int saved_pretend_args = 0; int saved_pretend_args = 0;
int saved_regs = 0; int saved_regs = 0;
unsigned HOST_WIDE_INT args_to_push; unsigned HOST_WIDE_INT args_to_push;
HOST_WIDE_INT size;
arm_stack_offsets *offsets; arm_stack_offsets *offsets;
bool clobber_ip;
func_type = arm_current_func_type (); func_type = arm_current_func_type ();
...@@ -21163,109 +21405,108 @@ arm_expand_prologue (void) ...@@ -21163,109 +21405,108 @@ arm_expand_prologue (void)
emit_insn (gen_movsi (stack_pointer_rtx, r1)); emit_insn (gen_movsi (stack_pointer_rtx, r1));
} }
/* For APCS frames, if IP register is clobbered /* The static chain register is the same as the IP register. If it is
when creating frame, save that register in a special clobbered when creating the frame, we need to save and restore it. */
way. */ clobber_ip = IS_NESTED (func_type)
if (TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM) && ((TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
|| (flag_stack_check == STATIC_BUILTIN_STACK_CHECK
&& !df_regs_ever_live_p (LR_REGNUM)
&& arm_r3_live_at_start_p ()));
/* Find somewhere to store IP whilst the frame is being created.
We try the following places in order:
1. The last argument register r3 if it is available.
2. A slot on the stack above the frame if there are no
arguments to push onto the stack.
3. Register r3 again, after pushing the argument registers
onto the stack, if this is a varargs function.
4. The last slot on the stack created for the arguments to
push, if this isn't a varargs function.
Note - we only need to tell the dwarf2 backend about the SP
adjustment in the second variant; the static chain register
doesn't need to be unwound, as it doesn't contain a value
inherited from the caller. */
if (clobber_ip)
{ {
if (IS_INTERRUPT (func_type)) if (!arm_r3_live_at_start_p ())
insn = emit_set_insn (gen_rtx_REG (SImode, 3), ip_rtx);
else if (args_to_push == 0)
{ {
/* Interrupt functions must not corrupt any registers. rtx addr, dwarf;
Creating a frame pointer however, corrupts the IP
register, so we must push it first. */
emit_multi_reg_push (1 << IP_REGNUM, 1 << IP_REGNUM);
/* Do not set RTX_FRAME_RELATED_P on this insn. gcc_assert(arm_compute_static_chain_stack_bytes() == 4);
The dwarf stack unwinding code only wants to see one saved_regs += 4;
stack decrement per function, and this is not it. If
this instruction is labeled as being part of the frame
creation sequence then dwarf2out_frame_debug_expr will
die when it encounters the assignment of IP to FP
later on, since the use of SP here establishes SP as
the CFA register and not IP.
Anyway this instruction is not really part of the stack addr = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
frame creation although it is part of the prologue. */ insn = emit_set_insn (gen_frame_mem (SImode, addr), ip_rtx);
fp_offset = 4;
/* Just tell the dwarf backend that we adjusted SP. */
dwarf = gen_rtx_SET (stack_pointer_rtx,
plus_constant (Pmode, stack_pointer_rtx,
-fp_offset));
RTX_FRAME_RELATED_P (insn) = 1;
add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
} }
else if (IS_NESTED (func_type)) else
{ {
/* The static chain register is the same as the IP register /* Store the args on the stack. */
used as a scratch register during stack frame creation. if (cfun->machine->uses_anonymous_args)
To get around this need to find somewhere to store IP {
whilst the frame is being created. We try the following insn = emit_multi_reg_push ((0xf0 >> (args_to_push / 4)) & 0xf,
places in order: (0xf0 >> (args_to_push / 4)) & 0xf);
emit_set_insn (gen_rtx_REG (SImode, 3), ip_rtx);
1. The last argument register r3 if it is available. saved_pretend_args = 1;
2. A slot on the stack above the frame if there are no }
arguments to push onto the stack. else
3. Register r3 again, after pushing the argument registers
onto the stack, if this is a varargs function.
4. The last slot on the stack created for the arguments to
push, if this isn't a varargs function.
Note - we only need to tell the dwarf2 backend about the SP
adjustment in the second variant; the static chain register
doesn't need to be unwound, as it doesn't contain a value
inherited from the caller. */
if (!arm_r3_live_at_start_p ())
insn = emit_set_insn (gen_rtx_REG (SImode, 3), ip_rtx);
else if (args_to_push == 0)
{ {
rtx addr, dwarf; rtx addr, dwarf;
gcc_assert(arm_compute_static_chain_stack_bytes() == 4); if (args_to_push == 4)
saved_regs += 4; addr = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
else
addr = gen_rtx_PRE_MODIFY (Pmode, stack_pointer_rtx,
plus_constant (Pmode,
stack_pointer_rtx,
-args_to_push));
addr = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx);
insn = emit_set_insn (gen_frame_mem (SImode, addr), ip_rtx); insn = emit_set_insn (gen_frame_mem (SImode, addr), ip_rtx);
fp_offset = 4;
/* Just tell the dwarf backend that we adjusted SP. */ /* Just tell the dwarf backend that we adjusted SP. */
dwarf = gen_rtx_SET (stack_pointer_rtx, dwarf = gen_rtx_SET (stack_pointer_rtx,
plus_constant (Pmode, stack_pointer_rtx, plus_constant (Pmode, stack_pointer_rtx,
-fp_offset)); -args_to_push));
RTX_FRAME_RELATED_P (insn) = 1;
add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf); add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
} }
else
{
/* Store the args on the stack. */
if (cfun->machine->uses_anonymous_args)
{
insn
= emit_multi_reg_push ((0xf0 >> (args_to_push / 4)) & 0xf,
(0xf0 >> (args_to_push / 4)) & 0xf);
emit_set_insn (gen_rtx_REG (SImode, 3), ip_rtx);
saved_pretend_args = 1;
}
else
{
rtx addr, dwarf;
if (args_to_push == 4) RTX_FRAME_RELATED_P (insn) = 1;
addr = gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx); fp_offset = args_to_push;
else args_to_push = 0;
addr }
= gen_rtx_PRE_MODIFY (Pmode, stack_pointer_rtx, }
plus_constant (Pmode,
stack_pointer_rtx,
-args_to_push));
insn = emit_set_insn (gen_frame_mem (SImode, addr), ip_rtx); if (TARGET_APCS_FRAME && frame_pointer_needed && TARGET_ARM)
{
if (IS_INTERRUPT (func_type))
{
/* Interrupt functions must not corrupt any registers.
Creating a frame pointer however, corrupts the IP
register, so we must push it first. */
emit_multi_reg_push (1 << IP_REGNUM, 1 << IP_REGNUM);
/* Just tell the dwarf backend that we adjusted SP. */ /* Do not set RTX_FRAME_RELATED_P on this insn.
dwarf The dwarf stack unwinding code only wants to see one
= gen_rtx_SET (stack_pointer_rtx, stack decrement per function, and this is not it. If
plus_constant (Pmode, stack_pointer_rtx, this instruction is labeled as being part of the frame
-args_to_push)); creation sequence then dwarf2out_frame_debug_expr will
add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf); die when it encounters the assignment of IP to FP
} later on, since the use of SP here establishes SP as
the CFA register and not IP.
RTX_FRAME_RELATED_P (insn) = 1; Anyway this instruction is not really part of the stack
fp_offset = args_to_push; frame creation although it is part of the prologue. */
args_to_push = 0;
}
} }
insn = emit_set_insn (ip_rtx, insn = emit_set_insn (ip_rtx,
...@@ -21364,34 +21605,60 @@ arm_expand_prologue (void) ...@@ -21364,34 +21605,60 @@ arm_expand_prologue (void)
insn = GEN_INT (-(4 + args_to_push + fp_offset)); insn = GEN_INT (-(4 + args_to_push + fp_offset));
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn)); insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, ip_rtx, insn));
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
if (IS_NESTED (func_type))
{
/* Recover the static chain register. */
if (!arm_r3_live_at_start_p () || saved_pretend_args)
insn = gen_rtx_REG (SImode, 3);
else
{
insn = plus_constant (Pmode, hard_frame_pointer_rtx, 4);
insn = gen_frame_mem (SImode, insn);
}
emit_set_insn (ip_rtx, insn);
/* Add a USE to stop propagate_one_insn() from barfing. */
emit_insn (gen_force_register_use (ip_rtx));
}
} }
else else
{ {
insn = GEN_INT (saved_regs - 4); insn = GEN_INT (saved_regs - (4 + fp_offset));
insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx, insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
stack_pointer_rtx, insn)); stack_pointer_rtx, insn));
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
} }
} }
size = offsets->outgoing_args - offsets->saved_args;
if (flag_stack_usage_info) if (flag_stack_usage_info)
current_function_static_stack_size current_function_static_stack_size = size;
= offsets->outgoing_args - offsets->saved_args;
/* If this isn't an interrupt service routine and we have a frame, then do
stack checking. We use IP as the first scratch register, except for the
non-APCS nested functions if LR or r3 are available (see clobber_ip). */
if (!IS_INTERRUPT (func_type)
&& flag_stack_check == STATIC_BUILTIN_STACK_CHECK)
{
unsigned int regno;
if (!IS_NESTED (func_type) || clobber_ip)
regno = IP_REGNUM;
else if (df_regs_ever_live_p (LR_REGNUM))
regno = LR_REGNUM;
else
regno = 3;
if (crtl->is_leaf && !cfun->calls_alloca)
{
if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
arm_emit_probe_stack_range (STACK_CHECK_PROTECT,
size - STACK_CHECK_PROTECT,
regno, live_regs_mask);
}
else if (size > 0)
arm_emit_probe_stack_range (STACK_CHECK_PROTECT, size,
regno, live_regs_mask);
}
/* Recover the static chain register. */
if (clobber_ip)
{
if (!arm_r3_live_at_start_p () || saved_pretend_args)
insn = gen_rtx_REG (SImode, 3);
else
{
insn = plus_constant (Pmode, hard_frame_pointer_rtx, 4);
insn = gen_frame_mem (SImode, insn);
}
emit_set_insn (ip_rtx, insn);
emit_insn (gen_force_register_use (ip_rtx));
}
if (offsets->outgoing_args != offsets->saved_args + saved_regs) if (offsets->outgoing_args != offsets->saved_args + saved_regs)
{ {
...@@ -24370,6 +24637,7 @@ thumb1_expand_prologue (void) ...@@ -24370,6 +24637,7 @@ thumb1_expand_prologue (void)
rtx_insn *insn; rtx_insn *insn;
HOST_WIDE_INT amount; HOST_WIDE_INT amount;
HOST_WIDE_INT size;
arm_stack_offsets *offsets; arm_stack_offsets *offsets;
unsigned long func_type; unsigned long func_type;
int regno; int regno;
...@@ -24604,9 +24872,13 @@ thumb1_expand_prologue (void) ...@@ -24604,9 +24872,13 @@ thumb1_expand_prologue (void)
emit_move_insn (gen_rtx_REG (Pmode, ARM_HARD_FRAME_POINTER_REGNUM), emit_move_insn (gen_rtx_REG (Pmode, ARM_HARD_FRAME_POINTER_REGNUM),
stack_pointer_rtx); stack_pointer_rtx);
size = offsets->outgoing_args - offsets->saved_args;
if (flag_stack_usage_info) if (flag_stack_usage_info)
current_function_static_stack_size current_function_static_stack_size = size;
= offsets->outgoing_args - offsets->saved_args;
/* If we have a frame, then do stack checking. FIXME: not implemented. */
if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && size)
sorry ("-fstack-check=specific for THUMB1");
amount = offsets->outgoing_args - offsets->saved_regs; amount = offsets->outgoing_args - offsets->saved_regs;
amount -= 4 * thumb1_extra_regs_pushed (offsets, true); amount -= 4 * thumb1_extra_regs_pushed (offsets, true);
...@@ -25177,14 +25449,16 @@ arm_expand_epilogue (bool really_return) ...@@ -25177,14 +25449,16 @@ arm_expand_epilogue (bool really_return)
return; return;
} }
if (crtl->args.pretend_args_size) amount
= crtl->args.pretend_args_size + arm_compute_static_chain_stack_bytes();
if (amount)
{ {
int i, j; int i, j;
rtx dwarf = NULL_RTX; rtx dwarf = NULL_RTX;
rtx_insn *tmp = rtx_insn *tmp =
emit_insn (gen_addsi3 (stack_pointer_rtx, emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx, stack_pointer_rtx,
GEN_INT (crtl->args.pretend_args_size))); GEN_INT (amount)));
RTX_FRAME_RELATED_P (tmp) = 1; RTX_FRAME_RELATED_P (tmp) = 1;
...@@ -25203,7 +25477,7 @@ arm_expand_epilogue (bool really_return) ...@@ -25203,7 +25477,7 @@ arm_expand_epilogue (bool really_return)
} }
REG_NOTES (tmp) = dwarf; REG_NOTES (tmp) = dwarf;
} }
arm_add_cfa_adjust_cfa_note (tmp, crtl->args.pretend_args_size, arm_add_cfa_adjust_cfa_note (tmp, amount,
stack_pointer_rtx, stack_pointer_rtx); stack_pointer_rtx, stack_pointer_rtx);
} }
...@@ -27212,9 +27486,45 @@ arm_order_regs_for_local_alloc (void) ...@@ -27212,9 +27486,45 @@ arm_order_regs_for_local_alloc (void)
bool bool
arm_frame_pointer_required (void) arm_frame_pointer_required (void)
{ {
return (cfun->has_nonlocal_label if (SUBTARGET_FRAME_POINTER_REQUIRED)
|| SUBTARGET_FRAME_POINTER_REQUIRED return true;
|| (TARGET_ARM && TARGET_APCS_FRAME && ! leaf_function_p ()));
/* If the function receives nonlocal gotos, it needs to save the frame
pointer in the nonlocal_goto_save_area object. */
if (cfun->has_nonlocal_label)
return true;
/* The frame pointer is required for non-leaf APCS frames. */
if (TARGET_ARM && TARGET_APCS_FRAME && !leaf_function_p ())
return true;
/* If we are probing the stack in the prologue, we will have a faulting
instruction prior to the stack adjustment and this requires a frame
pointer if we want to catch the exception using the EABI unwinder. */
if (!IS_INTERRUPT (arm_current_func_type ())
&& flag_stack_check == STATIC_BUILTIN_STACK_CHECK
&& arm_except_unwind_info (&global_options) == UI_TARGET
&& cfun->can_throw_non_call_exceptions)
{
HOST_WIDE_INT size = get_frame_size ();
/* That's irrelevant if there is no stack adjustment. */
if (size <= 0)
return false;
/* That's relevant only if there is a stack probe. */
if (crtl->is_leaf && !cfun->calls_alloca)
{
/* We don't have the final size of the frame so adjust. */
size += 32 * UNITS_PER_WORD;
if (size > PROBE_INTERVAL && size > STACK_CHECK_PROTECT)
return true;
}
else
return true;
}
return false;
} }
/* Only thumb1 can't support conditional execution, so return true if /* Only thumb1 can't support conditional execution, so return true if
......
...@@ -8259,6 +8259,30 @@ ...@@ -8259,6 +8259,30 @@
(set_attr "type" "block")] (set_attr "type" "block")]
) )
(define_insn "probe_stack"
[(set (match_operand 0 "memory_operand" "=m")
(unspec [(const_int 0)] UNSPEC_PROBE_STACK))]
"TARGET_32BIT"
{
return "str%?\\tr0, %0";
}
[(set_attr "type" "store1")
(set_attr "predicable" "yes")]
)
(define_insn "probe_stack_range"
[(set (match_operand:SI 0 "register_operand" "=r")
(unspec_volatile:SI [(match_operand:SI 1 "register_operand" "0")
(match_operand:SI 2 "register_operand" "r")]
UNSPEC_PROBE_STACK_RANGE))]
"TARGET_32BIT"
{
return output_probe_stack_range (operands[0], operands[2]);
}
[(set_attr "type" "multiple")
(set_attr "conds" "clob")]
)
(define_expand "casesi" (define_expand "casesi"
[(match_operand:SI 0 "s_register_operand" "") ; index to jump on [(match_operand:SI 0 "s_register_operand" "") ; index to jump on
(match_operand:SI 1 "const_int_operand" "") ; lower bound (match_operand:SI 1 "const_int_operand" "") ; lower bound
......
...@@ -124,3 +124,6 @@ ...@@ -124,3 +124,6 @@
to COPY relocated symbol in the executable. See PR65780. */ to COPY relocated symbol in the executable. See PR65780. */
#undef TARGET_BINDS_LOCAL_P #undef TARGET_BINDS_LOCAL_P
#define TARGET_BINDS_LOCAL_P default_binds_local_p_2 #define TARGET_BINDS_LOCAL_P default_binds_local_p_2
/* Define this to be nonzero if static stack checking is supported. */
#define STACK_CHECK_STATIC_BUILTIN 1
...@@ -83,6 +83,8 @@ ...@@ -83,6 +83,8 @@
; FPSCR rounding mode and signal inexactness. ; FPSCR rounding mode and signal inexactness.
UNSPEC_VRINTA ; Represent a float to integral float rounding UNSPEC_VRINTA ; Represent a float to integral float rounding
; towards nearest, ties away from zero. ; towards nearest, ties away from zero.
UNSPEC_PROBE_STACK ; Probe stack memory reference
UNSPEC_PROBE_STACK_RANGE ; Probe stack range
]) ])
(define_c_enum "unspec" [ (define_c_enum "unspec" [
......
2015-09-17 Eric Botcazou <ebotcazou@adacore.com>
* gcc.target/arm/stack-checking.c: New test.
2015-09-17 Paul Thomas <pault@gcc.gnu.org> 2015-09-17 Paul Thomas <pault@gcc.gnu.org>
PR fortran/52846 PR fortran/52846
......
/* { dg-do run { target { *-*-linux* } } } */
/* { dg-options "-fstack-check" } */
int main(void)
{
char *p;
if (1)
{
char i[48];
p = __builtin_alloca(8);
p[0] = 1;
}
if (1)
{
char i[48], j[64];
j[48] = 0;
}
return !p[0];
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment