This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [MIPS] Implement static stack checking


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]