From patchwork Fri Jul 2 21:25:12 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Botcazou X-Patchwork-Id: 57777 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 D71F41007D5 for ; Sat, 3 Jul 2010 07:27:14 +1000 (EST) Received: (qmail 20692 invoked by alias); 2 Jul 2010 21:27:12 -0000 Received: (qmail 20664 invoked by uid 22791); 2 Jul 2010 21:27:09 -0000 X-SWARE-Spam-Status: No, hits=-2.1 required=5.0 tests=AWL,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; Fri, 02 Jul 2010 21:27:01 +0000 Received: from localhost (localhost [127.0.0.1]) by filtered-smtp.eu.adacore.com (Postfix) with ESMTP id 73FBCCB01DB for ; Fri, 2 Jul 2010 23:26:58 +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 fywKKWopHqSM for ; Fri, 2 Jul 2010 23:26:58 +0200 (CEST) Received: from [192.168.1.2] (bon31-9-83-155-120-49.fbx.proxad.net [83.155.120.49]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mel.act-europe.fr (Postfix) with ESMTP id 32EE6CB01D6 for ; Fri, 2 Jul 2010 23:26:58 +0200 (CEST) From: Eric Botcazou To: gcc-patches@gcc.gnu.org Subject: [SPARC] Implement static static checking Date: Fri, 2 Jul 2010 23:25:12 +0200 User-Agent: KMail/1.9.9 MIME-Version: 1.0 Content-Disposition: inline Message-Id: <201007022325.12610.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 This implements static stack checking in the SPARC 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 SPARC/Solaris and SPARC64/Solaris. Applied on the mainline. 2010-07-02 Eric Botcazou * expr.h (emit_stack_probe): Declare. * explow.c (emit_stack_probe): Make global. (anti_adjust_stack_and_probe): Fix comments. * config/sparc/linux.h (STACK_CHECK_STATIC_BUILTIN): Define to 1. * config/sparc/linux64.h (STACK_CHECK_STATIC_BUILTIN): Likewise. * config/sparc/sol2.h (STACK_CHECK_STATIC_BUILTIN): Likewise. * config/sparc/sparc.c: Include except.h. (sparc_emit_probe_stack_range): New function. (output_probe_stack_range): Likewise. (sparc_expand_prologue): Invoke sparc_emit_probe_stack_range if static built-in stack checking is enabled. * config/sparc/sparc-protos.h (output_probe_stack_range): Declare. * config/sparc/sparc.md (UNSPECV_PROBE_STACK_RANGE): New constant. (probe_stack_range): New insn. Index: expr.h =================================================================== --- expr.h (revision 161685) +++ expr.h (working copy) @@ -645,6 +645,9 @@ extern void update_nonlocal_goto_save_ar says how many bytes. */ extern rtx allocate_dynamic_stack_space (rtx, rtx, int); +/* Emit one stack probe at ADDRESS, an address within the stack. */ +extern void emit_stack_probe (rtx); + /* Probe a range of stack addresses from FIRST to FIRST+SIZE, inclusive. FIRST is a constant and size is a Pmode RTX. These are offsets from the current stack pointer. STACK_GROWS_DOWNWARD says whether to add Index: explow.c =================================================================== --- explow.c (revision 161685) +++ explow.c (working copy) @@ -43,7 +43,6 @@ along with GCC; see the file COPYING3. #include "output.h" static rtx break_out_memory_refs (rtx); -static void emit_stack_probe (rtx); /* Truncate and perhaps sign-extend C as appropriate for MODE. */ @@ -1355,7 +1354,7 @@ set_stack_check_libfunc (const char *lib /* Emit one stack probe at ADDRESS, an address within the stack. */ -static void +void emit_stack_probe (rtx address) { rtx memref = gen_rtx_MEM (word_mode, address); @@ -1567,7 +1566,7 @@ anti_adjust_stack_and_probe (rtx size, b HOST_WIDE_INT isize = INTVAL (size), i; bool first_probe = true; - /* Adjust SP and probe to PROBE_INTERVAL + N * PROBE_INTERVAL for + /* Adjust SP and probe at PROBE_INTERVAL + 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 adjust and probe to PROBE_INTERVAL + SIZE. */ @@ -1623,13 +1622,13 @@ anti_adjust_stack_and_probe (rtx size, b /* Step 3: the loop - while (SP != LAST_ADDR) - { - SP = SP + PROBE_INTERVAL - probe at SP - } + while (SP != LAST_ADDR) + { + SP = SP + PROBE_INTERVAL + probe at SP + } - adjusts SP and probes to PROBE_INTERVAL + N * PROBE_INTERVAL for + adjusts SP and probes at PROBE_INTERVAL + N * PROBE_INTERVAL for values of N from 1 until it is equal to ROUNDED_SIZE. */ emit_label (loop_lab); @@ -1647,7 +1646,7 @@ anti_adjust_stack_and_probe (rtx size, b emit_label (end_lab); - /* Step 4: adjust SP and probe to PROBE_INTERVAL + SIZE if we cannot + /* Step 4: adjust SP and probe at PROBE_INTERVAL + SIZE if we cannot assert at compile-time that SIZE is equal to ROUNDED_SIZE. */ /* TEMP = SIZE - ROUNDED_SIZE. */ Index: config/sparc/linux.h =================================================================== --- config/sparc/linux.h (revision 161685) +++ config/sparc/linux.h (working copy) @@ -161,6 +161,9 @@ do { \ #undef CTORS_SECTION_ASM_OP #undef DTORS_SECTION_ASM_OP +/* Static stack checking is supported by means of probes. */ +#define STACK_CHECK_STATIC_BUILTIN 1 + #define MD_UNWIND_SUPPORT "config/sparc/linux-unwind.h" /* Linux currently uses RMO in uniprocessor mode, which is equivalent to Index: config/sparc/sparc.md =================================================================== --- config/sparc/sparc.md (revision 161685) +++ config/sparc/sparc.md (working copy) @@ -75,6 +75,7 @@ (define_constants (UNSPECV_CAS 8) (UNSPECV_SWAP 9) (UNSPECV_LDSTUB 10) + (UNSPECV_PROBE_STACK_RANGE 11) ]) @@ -6340,6 +6341,15 @@ (define_expand "probe_stack" = adjust_address (operands[0], GET_MODE (operands[0]), SPARC_STACK_BIAS); }) +(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" "multi")]) + ;; Prepare to return any type including a structure value. (define_expand "untyped_return" Index: config/sparc/sparc-protos.h =================================================================== --- config/sparc/sparc-protos.h (revision 161685) +++ config/sparc/sparc-protos.h (working copy) @@ -75,6 +75,7 @@ extern const char *output_return (rtx); extern const char *output_sibcall (rtx, rtx); extern const char *output_v8plus_shift (rtx *, rtx, const char *); extern const char *output_v9branch (rtx, rtx, int, int, int, int, rtx); +extern const char *output_probe_stack_range (rtx, rtx); extern bool emit_scc_insn (rtx []); extern void emit_conditional_branch_insn (rtx []); extern void print_operand (FILE *, rtx, int); Index: config/sparc/sparc.c =================================================================== --- config/sparc/sparc.c (revision 161685) +++ config/sparc/sparc.c (working copy) @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. #include "insn-attr.h" #include "flags.h" #include "function.h" +#include "except.h" #include "expr.h" #include "optabs.h" #include "recog.h" @@ -4005,6 +4006,160 @@ sparc_output_scratch_registers (FILE *fi #endif } +#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. + + Note that we don't use the REG+REG addressing mode for the probes because + of the stack bias in 64-bit mode. And it doesn't really buy us anything + so the advantages of having a single code win here. */ + +static void +sparc_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size) +{ + rtx g1 = gen_rtx_REG (Pmode, 1); + + /* 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 (g1, GEN_INT (first)); + emit_insn (gen_rtx_SET (VOIDmode, g1, + gen_rtx_MINUS (Pmode, stack_pointer_rtx, g1))); + emit_stack_probe (plus_constant (g1, -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; + + emit_move_insn (g1, GEN_INT (first + PROBE_INTERVAL)); + emit_insn (gen_rtx_SET (VOIDmode, g1, + gen_rtx_MINUS (Pmode, stack_pointer_rtx, g1))); + emit_stack_probe (g1); + + /* 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_insn (gen_rtx_SET (VOIDmode, g1, + plus_constant (g1, -PROBE_INTERVAL))); + emit_stack_probe (g1); + } + + emit_stack_probe (plus_constant (g1, (i - PROBE_INTERVAL) - 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 g4 = gen_rtx_REG (Pmode, 4); + + emit_move_insn (g1, GEN_INT (first)); + + + /* Step 1: round SIZE to the previous multiple of the interval. */ + + rounded_size = size & -PROBE_INTERVAL; + emit_move_insn (g4, GEN_INT (rounded_size)); + + + /* Step 2: compute initial and final value of the loop counter. */ + + /* TEST_ADDR = SP + FIRST. */ + emit_insn (gen_rtx_SET (VOIDmode, g1, + gen_rtx_MINUS (Pmode, stack_pointer_rtx, g1))); + + /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE. */ + emit_insn (gen_rtx_SET (VOIDmode, g4, gen_rtx_MINUS (Pmode, g1, g4))); + + + /* 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 (g1, g1, g4)); + else + emit_insn (gen_probe_stack_rangesi (g1, g1, g4)); + + + /* 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 (g4, rounded_size - size)); + } + + /* 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], 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; + output_asm_insn ("cmp\t%0, %1", xops); + if (TARGET_ARCH64) + fputs ("\tbe,pn\t%xcc,", asm_out_file); + else + fputs ("\tbe\t", 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 (" add\t%0, %1, %0", xops); + + /* Probe at TEST_ADDR and branch. */ + if (TARGET_ARCH64) + fputs ("\tba,pt\t%xcc,", asm_out_file); + else + fputs ("\tba\t", asm_out_file); + assemble_name_raw (asm_out_file, loop_lab); + fputc ('\n', asm_out_file); + xops[1] = GEN_INT (SPARC_STACK_BIAS); + output_asm_insn (" st\t%%g0, [%0+%1]", xops); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, end_lab); + + return ""; +} + /* Save/restore call-saved registers from LOW to HIGH at BASE+OFFSET as needed. LOW should be double-word aligned for 32-bit registers. Return the new OFFSET. */ @@ -4193,6 +4348,9 @@ sparc_expand_prologue (void) /* Advertise that the data calculated just above are now valid. */ sparc_prologue_data_valid_p = true; + if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && actual_fsize) + sparc_emit_probe_stack_range (STACK_CHECK_PROTECT, actual_fsize); + if (sparc_leaf_function_p) { frame_base_reg = stack_pointer_rtx; Index: config/sparc/linux64.h =================================================================== --- config/sparc/linux64.h (revision 161685) +++ config/sparc/linux64.h (working copy) @@ -287,6 +287,9 @@ do { \ #undef CTORS_SECTION_ASM_OP #undef DTORS_SECTION_ASM_OP +/* Static stack checking is supported by means of probes. */ +#define STACK_CHECK_STATIC_BUILTIN 1 + #define MD_UNWIND_SUPPORT "config/sparc/linux-unwind.h" /* Linux currently uses RMO in uniprocessor mode, which is equivalent to Index: config/sparc/sol2.h =================================================================== --- config/sparc/sol2.h (revision 161685) +++ config/sparc/sol2.h (working copy) @@ -188,4 +188,7 @@ along with GCC; see the file COPYING3. #define PUSHSECTION_FORMAT "\t.pushsection\t\"%s\"\n" #endif +/* Static stack checking is supported by means of probes. */ +#define STACK_CHECK_STATIC_BUILTIN 1 + #define MD_UNWIND_SUPPORT "config/sparc/sol2-unwind.h"