From patchwork Wed Aug 18 23:28:24 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Botcazou X-Patchwork-Id: 62096 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id DFCDCB70D2 for ; Thu, 19 Aug 2010 09:28:34 +1000 (EST) Received: (qmail 24335 invoked by alias); 18 Aug 2010 23:28:33 -0000 Received: (qmail 24326 invoked by uid 22791); 18 Aug 2010 23:28:32 -0000 X-SWARE-Spam-Status: No, hits=-1.9 required=5.0 tests=BAYES_00 X-Spam-Check-By: sourceware.org Received: from mel.act-europe.fr (HELO mel.act-europe.fr) (212.99.106.210) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 18 Aug 2010 23:28:27 +0000 Received: from localhost (localhost [127.0.0.1]) by filtered-smtp.eu.adacore.com (Postfix) with ESMTP id 8E1BFCB0238 for ; Thu, 19 Aug 2010 01:28:24 +0200 (CEST) Received: from mel.act-europe.fr ([127.0.0.1]) by localhost (smtp.eu.adacore.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2dslRd-fo-by for ; Thu, 19 Aug 2010 01:28:24 +0200 (CEST) Received: from new-host.home (ADijon-552-1-28-130.w92-138.abo.wanadoo.fr [92.138.163.130]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mel.act-europe.fr (Postfix) with ESMTP id 5CF13CB01BA for ; Thu, 19 Aug 2010 01:28:24 +0200 (CEST) From: Eric Botcazou To: gcc-patches@gcc.gnu.org Subject: [PowerPC] Implement static stack checking Date: Thu, 19 Aug 2010 01:28:24 +0200 User-Agent: KMail/1.9.9 MIME-Version: 1.0 Message-Id: <201008190128.24139.ebotcazou@adacore.com> Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Hi, this implements static stack checking in the PowerPC back-end, i.e. checking of the static part of the stack frame in the prologue. This is modelled on the Alpha back-end, but of course only activated by means of -fstack-check. Tested on AIX, Linux 32-bit and 64-bit. OK for the mainline? 2010-08-18 Eric Botcazou * config/rs6000/aix.h (STACK_CHECK_STATIC_BUILTIN): Define to 1. * config/rs6000/linux.h (STACK_CHECK_STATIC_BUILTIN): Likewise. * config/rs6000/linux64.h (STACK_CHECK_STATIC_BUILTIN): Likewise. (STACK_CHECK_PROTECT): Define. * config/rs6000/rs6000.c (rs6000_emit_probe_stack_range): New function. (output_probe_stack_range): Likewise. (rs6000_emit_prologue): Invoke rs6000_emit_probe_stack_range if static builtin stack checking is enabled. * config/rs6000/rs6000-protos.h (output_probe_stack_range): Declare. * config/rs6000/rs6000.md (UNSPECV_PROBE_STACK_RANGE): New constant. (probe_stack_range): New insn. Index: config/rs6000/linux.h =================================================================== --- config/rs6000/linux.h (revision 163220) +++ config/rs6000/linux.h (working copy) @@ -130,3 +130,6 @@ #ifdef TARGET_DEFAULT_LONG_DOUBLE_128 #define RS6000_DEFAULT_LONG_DOUBLE_SIZE 128 #endif + +/* Static stack checking is supported by means of probes. */ +#define STACK_CHECK_STATIC_BUILTIN 1 Index: config/rs6000/rs6000-protos.h =================================================================== --- config/rs6000/rs6000-protos.h (revision 163220) +++ config/rs6000/rs6000-protos.h (working copy) @@ -95,6 +95,7 @@ extern void rs6000_emit_sCOND (enum mach extern void rs6000_emit_cbranch (enum machine_mode, rtx[]); extern char * output_cbranch (rtx, const char *, int, rtx); extern char * output_e500_flip_gt_bit (rtx, rtx); +extern const char * output_probe_stack_range (rtx, rtx); extern rtx rs6000_emit_set_const (rtx, enum machine_mode, rtx, int); extern int rs6000_emit_cmove (rtx, rtx, rtx, rtx); extern int rs6000_emit_vector_cond_expr (rtx, rtx, rtx, rtx, rtx, rtx); Index: config/rs6000/aix.h =================================================================== --- config/rs6000/aix.h (revision 163220) +++ config/rs6000/aix.h (working copy) @@ -260,3 +260,6 @@ /* WINT_TYPE */ #define WINT_TYPE "int" + +/* Static stack checking is supported by means of probes. */ +#define STACK_CHECK_STATIC_BUILTIN 1 Index: config/rs6000/linux64.h =================================================================== --- config/rs6000/linux64.h (revision 163220) +++ config/rs6000/linux64.h (working copy) @@ -573,3 +573,9 @@ extern enum rs6000_cmodel cmodel; #ifdef TARGET_DEFAULT_LONG_DOUBLE_128 #define RS6000_DEFAULT_LONG_DOUBLE_SIZE 128 #endif + +/* Static stack checking is supported by means of probes. */ +#define STACK_CHECK_STATIC_BUILTIN 1 + +/* The default value isn't sufficient in 64-bit mode. */ +#define STACK_CHECK_PROTECT (TARGET_64BIT ? 16 * 1024 : 12 * 1024) Index: config/rs6000/rs6000.c =================================================================== --- config/rs6000/rs6000.c (revision 163220) +++ config/rs6000/rs6000.c (working copy) @@ -18788,6 +18788,137 @@ rs6000_emit_allocate_stack (HOST_WIDE_IN GEN_INT (-size)))); } +#define PROBE_INTERVAL (1 << STACK_CHECK_PROBE_INTERVAL_EXP) + +#if PROBE_INTERVAL > 32768 +#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. */ + +static void +rs6000_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size) +{ + /* 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 (stack_pointer_rtx, -(first + i))); + + emit_stack_probe (plus_constant (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 r12 = gen_rtx_REG (Pmode, 12); + rtx r0 = gen_rtx_REG (Pmode, 0); + + /* 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, r12, + plus_constant (stack_pointer_rtx, -first))); + + /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE. */ + if (rounded_size > 32768) + { + emit_move_insn (r0, GEN_INT (-rounded_size)); + emit_insn (gen_rtx_SET (VOIDmode, r0, + gen_rtx_PLUS (Pmode, r12, r0))); + } + else + emit_insn (gen_rtx_SET (VOIDmode, r0, + plus_constant (r12, -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) + emit_insn (gen_probe_stack_rangedi (r12, r12, r0)); + else + emit_insn (gen_probe_stack_rangesi (r12, r12, r0)); + + + /* 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 (r12, rounded_size - size)); + } +} + +/* 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], end_lab[32]; + 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; + if (TARGET_64BIT) + output_asm_insn ("{cmp|cmpd} 0,%0,%1", xops); + else + output_asm_insn ("{cmp|cmpw} 0,%0,%1", xops); + + fputs ("\tbeq 0,", asm_out_file); + assemble_name_raw (asm_out_file, end_lab); + fputc ('\n', asm_out_file); + + /* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL. */ + xops[1] = GEN_INT (-PROBE_INTERVAL); + output_asm_insn ("{cal %0,%1(%0)|addi %0,%0,%1}", xops); + + /* Probe at TEST_ADDR and branch. */ + output_asm_insn ("{st|stw} 0,0(%0)", xops); + fprintf (asm_out_file, "\tb "); + assemble_name_raw (asm_out_file, loop_lab); + fputc ('\n', asm_out_file); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, end_lab); + + return ""; +} + /* Add to 'insn' a note which is PATTERN (INSN) but with REG replaced with (plus:P (reg 1) VAL), and with REG2 replaced with RREG if REG2 is not NULL. It would be nice if dwarf2out_frame_debug_expr could @@ -19400,6 +19531,9 @@ rs6000_emit_prologue (void) && call_used_regs[STATIC_CHAIN_REGNUM]); HOST_WIDE_INT sp_offset = 0; + if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && info->total_size) + rs6000_emit_probe_stack_range (STACK_CHECK_PROTECT, info->total_size); + if (TARGET_FIX_AND_CONTINUE) { /* gdb on darwin arranges to forward a function from the old Index: config/rs6000/rs6000.md =================================================================== --- config/rs6000/rs6000.md (revision 163220) +++ config/rs6000/rs6000.md (working copy) @@ -115,6 +115,7 @@ (define_constants [(UNSPECV_BLOCK 0) (UNSPECV_LL 1) ; load-locked (UNSPECV_SC 2) ; store-conditional + (UNSPECV_PROBE_STACK_RANGE 3) ; probe range of stack addresses (UNSPECV_EH_RR 9) ; eh_reg_restore ]) @@ -12663,6 +12664,15 @@ (define_insn "probe_stack" "{st%U0%X0|stw%U0%X0} 0,%0" [(set_attr "type" "store") (set_attr "length" "4")]) + +(define_insn "probe_stack_range" + [(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")] + UNSPECV_PROBE_STACK_RANGE))] + "" + "* return output_probe_stack_range (operands[0], operands[2]);" + [(set_attr "type" "three")]) ;; Compare insns are next. Note that the RS/6000 has two types of compares, ;; signed & unsigned, and one type of branch.