This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [MIPS] Implement static stack checking
- From: Richard Sandiford <rdsandiford at googlemail dot com>
- To: Eric Botcazou <ebotcazou at adacore dot com>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Mon, 22 Oct 2012 21:30:31 +0100
- Subject: Re: [MIPS] Implement static stack checking
- References: <17103666.Uvnd1BXjPp@polaris>
Eric Botcazou <ebotcazou@adacore.com> writes:
> This implements static stack checking for MIPS, i.e. checking of the static
> part of the frame in the prologue when -fstack-check is specified. This is
> very similar to the PowerPC and SPARC implementations and makes it possible to
> pass the full ACATS testsuite with -fstack-check.
>
> Tested on mips64el-linux-gnu (n32/32/64), OK for the mainline?
The Ada bits I'll leave to you. :-) The config/mips stuff looks good,
but a couple of nits:
> +(define_insn "probe_stack_range<P:mode>"
> + [(set (match_operand:P 0 "register_operand" "=r")
> + (unspec_volatile:P [(match_operand:P 1 "register_operand" "0")
> + (match_operand:P 2 "register_operand" "r")]
> + UNSPEC_PROBE_STACK_RANGE))]
> + ""
> + "* return mips_output_probe_stack_range (operands[0], operands[2]);"
> + [(set_attr "type" "unknown")
> + (set_attr "can_delay" "no")
> + (set_attr "mode" "<MODE>")])
Please use "d" rather than "r" in these constraints. Please use:
{ return mips_output_probe_stack_range (operands[0], operands[2]); }
for the output line.
> +/* Emit code to probe a range of stack addresses from FIRST to FIRST+SIZE,
> + inclusive. These are offsets from the current stack pointer. */
> +
> +static void
> +mips_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size)
> +{
This function doesn't work with MIPS16 mode. Maybe just:
if (TARGET_MIPS16)
sorry ("MIPS16 stack probes");
(We can't test TARGET_MIPS16 in something like STACK_CHECK_STATIC_BUILTIN
because MIPS16ness is a per-function property.)
> + /* See if we have a constant small number of probes to generate. If so,
> + that's the easy case. */
> + if (first + size <= 32768)
> + {
> + HOST_WIDE_INT i;
> +
> + /* Probe at FIRST + N * PROBE_INTERVAL for values of N from 1 until
> + it exceeds SIZE. If only one probe is needed, this will not
> + generate any code. Then probe at FIRST + SIZE. */
> + for (i = PROBE_INTERVAL; i < size; i += PROBE_INTERVAL)
> + emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
> + -(first + i)));
> +
> + emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
> + -(first + size)));
> + }
> +
> + /* 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;
> + rtx r3 = gen_rtx_REG (Pmode, GP_REG_FIRST + 3);
> + rtx r12 = gen_rtx_REG (Pmode, GP_REG_FIRST + 12);
Please use MIPS_PROLOGUE_TEMP for r3 (and probably rename r3).
I suppose GP_REG_FIRST + 12 should be MIPS_PROLOGUE_TEMP2, probably as:
#define MIPS_PROLOGUE_TEMP2_REGNUM \
(TARGET_MIPS16 ? gcc_unreachable () \
cfun->machine->interrupt_handler_p ? K1_REG_NUM : GP_REG_FIRST + 12)
#define MIPS_PROLOGUE_TEMP2(MODE) \
gen_rtx_REG (MODE, MIPS_PROLOGUE_TEMP2_REGNUM)
and update the block comment above the MIPS_PROLOGUE_TEMP_REGNUM definition.
> + /* Sanity check for the addressing mode we're going to use. */
> + gcc_assert (first <= 32768);
> +
> +
> + /* Step 1: round SIZE to the previous multiple of the interval. */
> +
> + rounded_size = size & -PROBE_INTERVAL;
> +
> +
> + /* Step 2: compute initial and final value of the loop counter. */
> +
> + /* TEST_ADDR = SP + FIRST. */
> + emit_insn (gen_rtx_SET (VOIDmode, r3,
> + plus_constant (Pmode, stack_pointer_rtx,
> + -first)));
> +
> + /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE. */
> + if (rounded_size > 32768)
> + {
> + emit_move_insn (r12, GEN_INT (rounded_size));
> + emit_insn (gen_rtx_SET (VOIDmode, r12,
> + gen_rtx_MINUS (Pmode, r3, r12)));
> + }
> + else
> + emit_insn (gen_rtx_SET (VOIDmode, r12,
> + plus_constant (Pmode, r3, -rounded_size)));
> +
> +
> + /* 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. */
> +
> + if (TARGET_64BIT && TARGET_LONG64)
> + emit_insn (gen_probe_stack_rangedi (r3, r3, r12));
> + else
> + emit_insn (gen_probe_stack_rangesi (r3, r3, r12));
> +
> +
> + /* Step 4: probe at FIRST + SIZE if we cannot assert at compile-time
> + that SIZE is equal to ROUNDED_SIZE. */
> +
> + if (size != rounded_size)
> + emit_stack_probe (plus_constant (Pmode, r12, rounded_size - size));
I Might Be Wrong, but it looks like this won't probe at FIRST + SIZE
in the case where SIZE == ROUNDED_SIZE, because the loop exits on that
value without probing it. Should the last line be unconditional,
or does the loop need to be a do-while instead? (I suppose the latter,
so that there isn't a hole bigger than PROBE_INTERVAL in the
SIZE != ROUNDED_SIZE case?)
> +/* Probe a range of stack addresses from REG1 to REG2 inclusive. These are
> + absolute addresses. */
And as above, it seems to be exclusive of REG2 as things stand.
> +
> +const char *
> +mips_output_probe_stack_range (rtx reg1, rtx reg2)
> +{
> + static int labelno = 0;
> + char loop_lab[32], end_lab[32], tmp[64];
> + rtx xops[2];
> +
> + ASM_GENERATE_INTERNAL_LABEL (loop_lab, "LPSRL", labelno);
> + ASM_GENERATE_INTERNAL_LABEL (end_lab, "LPSRE", labelno++);
> +
> + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, loop_lab);
> +
> + /* Jump to END_LAB if TEST_ADDR == LAST_ADDR. */
> + xops[0] = reg1;
> + xops[1] = reg2;
> + strcpy (tmp, "beq\t%0,%1,");
> + output_asm_insn (strcat (tmp, &end_lab[1]), xops);
> +
> + /* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL. */
> + xops[1] = GEN_INT (-PROBE_INTERVAL);
> + if (TARGET_64BIT && TARGET_LONG64)
> + output_asm_insn (" daddiu\t%0,%0,%1", xops);
> + else
> + output_asm_insn (" addiu\t%0,%0,%1", xops);
This only works in noreorder mode. If there's an asm in the function,
or something else that forces reorder mode (e.g. a -mfix-* option),
the addition won't be put in the delay slot.
"%(%<beq\t%0,%1," and "daddiu\t%0,%0,%1%>%)" should work. (Note that
our MIPS asm output doesn't have a space before the delay slot; there's
a blank line after it instead. That's all handled by output_asm_insn though.)
> + /* Probe at TEST_ADDR and branch. */
> + fprintf (asm_out_file, "\tb\t");
> + assemble_name_raw (asm_out_file, loop_lab);
> + fputc ('\n', asm_out_file);
> + if (TARGET_64BIT)
> + output_asm_insn (" sd\t$0,0(%0)", xops);
> + else
> + output_asm_insn (" sw\t$0,0(%0)", xops);
Same here, so I think the branch should use output_asm_insn too.
OK with those changes, thanks.
Richard