From patchwork Fri Jul 29 06:25:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 56412 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id F27CF385380B for ; Fri, 29 Jul 2022 06:26:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org F27CF385380B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1659075964; bh=DqMGryDqW1mpIH+1YrSnZ6WUP5VDmLDPGUl/D/QJa8M=; h=To:Subject:References:Date:In-Reply-To:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=iGzyKHUEtPwinKG1IUBJqQdZte/5Y0Er7pMLaMNa4tbqU9dlTUMqzC3Jpg9Bd/eHT ck9uiGLLCWHEFbW/ECcIpQ8066k0bQfIHH1WkpmofutbSpsEb/KMVcdRNOqn7ZvHbV 5Q4roOjqrkLCVIgldHEgKu7RjAUBJ8pvYXy3r5HU= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from rock.gnat.com (rock.gnat.com [205.232.38.15]) by sourceware.org (Postfix) with ESMTPS id 78ACA3858027 for ; Fri, 29 Jul 2022 06:25:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 78ACA3858027 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id DA7471168D2; Fri, 29 Jul 2022 02:25:29 -0400 (EDT) X-Virus-Scanned: Debian amavisd-new at gnat.com Received: from rock.gnat.com ([127.0.0.1]) by localhost (rock.gnat.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id hlknwaNmfGEv; Fri, 29 Jul 2022 02:25:29 -0400 (EDT) Received: from free.home (tron.gnat.com [IPv6:2620:20:4000:0:46a8:42ff:fe0e:e294]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by rock.gnat.com (Postfix) with ESMTPS id A99281168CD; Fri, 29 Jul 2022 02:25:28 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 26T6PFjW1851944 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 Jul 2022 03:25:15 -0300 To: gcc-patches@gcc.gnu.org Subject: [PATCH v2 02/10] Introduce strub: torture tests for C and C++ Organization: Free thinker, does not speak for AdaCore References: Date: Fri, 29 Jul 2022 03:25:15 -0300 In-Reply-To: (Alexandre Oliva's message of "Fri, 29 Jul 2022 03:16:41 -0300") Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 X-Spam-Status: No, score=-12.3 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_STATUS, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Alexandre Oliva via Gcc-patches From: Alexandre Oliva Reply-To: Alexandre Oliva Cc: Jan Hubicka , Jim Wilson , Graham Markall Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" for gcc/testsuite/ChangeLog * c-c++-common/torture/strub-callable1.c: New. * c-c++-common/torture/strub-callable2.c: New. * c-c++-common/torture/strub-const1.c: New. * c-c++-common/torture/strub-const2.c: New. * c-c++-common/torture/strub-const3.c: New. * c-c++-common/torture/strub-const4.c: New. * c-c++-common/torture/strub-data1.c: New. * c-c++-common/torture/strub-data2.c: New. * c-c++-common/torture/strub-data3.c: New. * c-c++-common/torture/strub-data4.c: New. * c-c++-common/torture/strub-data5.c: New. * c-c++-common/torture/strub-indcall1.c: New. * c-c++-common/torture/strub-indcall2.c: New. * c-c++-common/torture/strub-indcall3.c: New. * c-c++-common/torture/strub-inlinable1.c: New. * c-c++-common/torture/strub-inlinable2.c: New. * c-c++-common/torture/strub-ptrfn1.c: New. * c-c++-common/torture/strub-ptrfn2.c: New. * c-c++-common/torture/strub-ptrfn3.c: New. * c-c++-common/torture/strub-ptrfn4.c: New. * c-c++-common/torture/strub-pure1.c: New. * c-c++-common/torture/strub-pure2.c: New. * c-c++-common/torture/strub-pure3.c: New. * c-c++-common/torture/strub-pure4.c: New. * c-c++-common/torture/strub-run1.c: New. * c-c++-common/torture/strub-run2.c: New. * c-c++-common/torture/strub-run3.c: New. * c-c++-common/torture/strub-run4.c: New. * c-c++-common/torture/strub-run4c.c: New. * c-c++-common/torture/strub-run4d.c: New. * c-c++-common/torture/strub-run4i.c: New. diff --git a/gcc/testsuite/c-c++-common/torture/strub-callable1.c b/gcc/testsuite/c-c++-common/torture/strub-callable1.c new file mode 100644 index 0000000000000..b5e45ab0525ad --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-callable1.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict" } */ + +/* Check that strub and non-strub functions can be called from non-strub + contexts, and that strub and callable functions can be called from strub + contexts. */ + +#define OMIT_IMPERMISSIBLE_CALLS 1 +#include "strub-callable2.c" diff --git a/gcc/testsuite/c-c++-common/torture/strub-callable2.c b/gcc/testsuite/c-c++-common/torture/strub-callable2.c new file mode 100644 index 0000000000000..96aa7fe4b07f7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-callable2.c @@ -0,0 +1,264 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict" } */ + +/* Check that impermissible (cross-strub-context) calls are reported. */ + +extern int __attribute__ ((__strub__ ("callable"))) xcallable (void); +extern int __attribute__ ((__strub__ ("internal"))) xinternal (void); +extern int __attribute__ ((__strub__ ("at-calls"))) xat_calls (void); +extern int __attribute__ ((__strub__ ("disabled"))) xdisabled (void); + +int __attribute__ ((__strub__ ("callable"))) callable (void); +int __attribute__ ((__strub__ ("internal"))) internal (void); +int __attribute__ ((__strub__ ("at-calls"))) at_calls (void); +int __attribute__ ((__strub__ ("disabled"))) disabled (void); + +int __attribute__ ((__strub__)) var; +int var_user (void); + +static inline int __attribute__ ((__always_inline__, __strub__ ("callable"))) +icallable (void); +static inline int __attribute__ ((__always_inline__, __strub__ ("internal"))) +iinternal (void); +static inline int __attribute__ ((__always_inline__, __strub__ ("at-calls"))) +iat_calls (void); +static inline int __attribute__ ((__always_inline__, __strub__ ("disabled"))) +idisabled (void); +static inline int __attribute__ ((__always_inline__)) +ivar_user (void); + +static inline int __attribute__ ((__always_inline__, __strub__ ("callable"))) +i_callable (void) { return 0; } +static inline int __attribute__ ((__always_inline__, __strub__ ("internal"))) +i_internal (void) { return var; } +static inline int __attribute__ ((__always_inline__, __strub__ ("at-calls"))) +i_at_calls (void) { return var; } +static inline int __attribute__ ((__always_inline__, __strub__ ("disabled"))) +i_disabled (void) { return 0; } +static inline int __attribute__ ((__always_inline__)) +i_var_user (void) { return var; } + +#define CALLS_GOOD_FOR_STRUB_CONTEXT(ISEP) \ + do { \ + ret += i ## ISEP ## at_calls (); \ + ret += i ## ISEP ## internal (); \ + ret += i ## ISEP ## var_user (); \ + } while (0) + +#define CALLS_GOOD_FOR_NONSTRUB_CONTEXT(ISEP) \ + do { \ + ret += internal (); \ + ret += disabled (); \ + ret += var_user (); \ + \ + ret += i ## ISEP ## disabled (); \ + \ + ret += xinternal (); \ + ret += xdisabled (); \ + } while (0) + +#define CALLS_GOOD_FOR_EITHER_CONTEXT(ISEP) \ + do { \ + ret += i ## ISEP ## callable (); \ + \ + ret += callable (); \ + ret += at_calls (); \ + \ + ret += xat_calls (); \ + ret += xcallable (); \ + } while (0) + +/* Not a strub context, so it can call anything. + Explicitly declared as callable even from within strub contexts. */ +int __attribute__ ((__strub__ ("callable"))) +callable (void) { + int ret = 0; + + /* CALLS_GOOD_FOR_STRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += iat_calls (); /* { dg-error "in non-.strub. context" } */ + ret += iinternal (); /* { dg-error "in non-.strub. context" } */ + ret += ivar_user (); /* { dg-error "in non-.strub. context" } */ +#endif + CALLS_GOOD_FOR_EITHER_CONTEXT(); + CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); + + return ret; +} + +/* Internal strubbing means the body is a strub context, so it can only call + strub functions, and it's not itself callable from strub functions. */ +int __attribute__ ((__strub__ ("internal"))) +internal (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(); + CALLS_GOOD_FOR_EITHER_CONTEXT(); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += idisabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int __attribute__ ((__strub__ ("at-calls"))) +at_calls (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(); + CALLS_GOOD_FOR_EITHER_CONTEXT(); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += idisabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int __attribute__ ((__strub__ ("disabled"))) +disabled () { + int ret = 0; + + /* CALLS_GOOD_FOR_STRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += iat_calls (); /* { dg-error "in non-.strub. context" } */ + ret += iinternal (); /* { dg-error "in non-.strub. context" } */ + ret += ivar_user (); /* { dg-error "in non-.strub. context" } */ +#endif + CALLS_GOOD_FOR_EITHER_CONTEXT(); + CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); + + return ret; +} + +int +var_user (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(); + CALLS_GOOD_FOR_EITHER_CONTEXT(); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += idisabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int +icallable (void) +{ + int ret = 0; + + /* CALLS_GOOD_FOR_STRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += i_at_calls (); /* { dg-error "in non-.strub. context" } */ + ret += i_internal (); /* { dg-error "in non-.strub. context" } */ + ret += i_var_user (); /* { dg-error "in non-.strub. context" } */ +#endif + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); + + return ret; +} + +int +iinternal (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(_); + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += i_disabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int __attribute__ ((__always_inline__, __strub__ ("at-calls"))) +iat_calls (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(_); + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += i_disabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} + +int +idisabled () { + int ret = 0; + + /* CALLS_GOOD_FOR_STRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += i_at_calls (); /* { dg-error "in non-.strub. context" } */ + ret += i_internal (); /* { dg-error "in non-.strub. context" } */ + ret += i_var_user (); /* { dg-error "in non-.strub. context" } */ +#endif + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); + + return ret; +} + +int +ivar_user (void) { + int ret = var; + + CALLS_GOOD_FOR_STRUB_CONTEXT(_); + CALLS_GOOD_FOR_EITHER_CONTEXT(_); + /* CALLS_GOOD_FOR_NONSTRUB_CONTEXT(_); */ +#if !OMIT_IMPERMISSIBLE_CALLS + ret += internal (); /* { dg-error "in .strub. context" } */ + ret += disabled (); /* { dg-error "in .strub. context" } */ + ret += var_user (); /* { dg-error "in .strub. context" } */ + + ret += i_disabled (); /* { dg-error "in .strub. context" } */ + + ret += xinternal (); /* { dg-error "in .strub. context" } */ + ret += xdisabled (); /* { dg-error "in .strub. context" } */ +#endif + + return ret; +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-const1.c b/gcc/testsuite/c-c++-common/torture/strub-const1.c new file mode 100644 index 0000000000000..2857195706ed6 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-const1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* Check that, along with a strub const function call, we issue an asm statement + to make sure the watermark passed to it is held in memory before the call, + and another to make sure it is not assumed to be unchanged. */ + +int __attribute__ ((__strub__, __const__)) +f() { + return 0; +} + +int +g() { + return f(); +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-const2.c b/gcc/testsuite/c-c++-common/torture/strub-const2.c new file mode 100644 index 0000000000000..98a92bc9eac2b --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-const2.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* Check that, along with a strub implicitly-const function call, we issue an + asm statement to make sure the watermark passed to it is held in memory + before the call, and another to make sure it is not assumed to be + unchanged. */ + +int __attribute__ ((__strub__)) +#if ! __OPTIMIZE__ +__attribute__ ((__const__)) +#endif +f() { + return 0; +} + +int +g() { + return f(); +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-const3.c b/gcc/testsuite/c-c++-common/torture/strub-const3.c new file mode 100644 index 0000000000000..5511a6e1e71d3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-const3.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* Check that, along with a strub const wrapping call, we issue an asm statement + to make sure the watermark passed to it is held in memory before the call, + and another to make sure it is not assumed to be unchanged. */ + +int __attribute__ ((__strub__ ("internal"), __const__)) +f() { + return 0; +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-const4.c b/gcc/testsuite/c-c++-common/torture/strub-const4.c new file mode 100644 index 0000000000000..47ee927964dff --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-const4.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* Check that, along with a strub implicitly-const wrapping call, we issue an + asm statement to make sure the watermark passed to it is held in memory + before the call, and another to make sure it is not assumed to be + unchanged. */ + +int __attribute__ ((__strub__ ("internal"))) +#if ! __OPTIMIZE__ +__attribute__ ((__const__)) +#endif +f() { + return 0; +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data1.c b/gcc/testsuite/c-c++-common/torture/strub-data1.c new file mode 100644 index 0000000000000..7c27a2a1a6dca --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data1.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* The pointed-to data enables strubbing if accessed. */ +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data2.c b/gcc/testsuite/c-c++-common/torture/strub-data2.c new file mode 100644 index 0000000000000..e66d903780afd --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data2.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* The pointer itself is a strub variable, enabling internal strubbing when + its value is used. */ +int __attribute__ ((__strub__)) *ptr; + +int *f() { + return ptr; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data3.c b/gcc/testsuite/c-c++-common/torture/strub-data3.c new file mode 100644 index 0000000000000..5e08e0e58c658 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data3.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* The pointer itself is a strub variable, that would enable internal strubbing + if its value was used. Here, it's only overwritten, so no strub. */ +int __attribute__ ((__strub__)) var; + +void f() { + var = 0; +} + +/* { dg-final { scan-ipa-dump-not "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data4.c b/gcc/testsuite/c-c++-common/torture/strub-data4.c new file mode 100644 index 0000000000000..a818e7a38bb5f --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data4.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* The pointer itself is a strub variable, that would enable internal strubbing + if its value was used. Here, it's only overwritten, so no strub. */ +int __attribute__ ((__strub__)) *ptr; + +void f() { + ptr = 0; +} + +/* { dg-final { scan-ipa-dump-not "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-data5.c b/gcc/testsuite/c-c++-common/torture/strub-data5.c new file mode 100644 index 0000000000000..ddb0b5c0543b0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-data5.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict" } */ + +/* It would be desirable to issue at least warnings for these. */ + +typedef int __attribute__ ((__strub__)) strub_int; +strub_int *ptr; + +int *f () { + return ptr; /* { dg-message "incompatible|invalid conversion" } */ +} + +strub_int *g () { + return f (); /* { dg-message "incompatible|invalid conversion" } */ +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-indcall1.c b/gcc/testsuite/c-c++-common/torture/strub-indcall1.c new file mode 100644 index 0000000000000..c165f312f16de --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-indcall1.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +typedef void __attribute__ ((__strub__)) fntype (); +fntype (*ptr); + +void f() { + ptr (); +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "(&\.strub\.watermark\.\[0-9\]\+)" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-indcall2.c b/gcc/testsuite/c-c++-common/torture/strub-indcall2.c new file mode 100644 index 0000000000000..69fcff8d3763d --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-indcall2.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +typedef void __attribute__ ((__strub__)) fntype (int, int); +fntype (*ptr); + +void f() { + ptr (0, 0); +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "(0, 0, &\.strub\.watermark\.\[0-9\]\+)" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-indcall3.c b/gcc/testsuite/c-c++-common/torture/strub-indcall3.c new file mode 100644 index 0000000000000..ff006224909bd --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-indcall3.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +typedef void __attribute__ ((__strub__)) fntype (int, int, ...); +fntype (*ptr); + +void f() { + ptr (0, 0, 1, 1); +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "(0, 0, &\.strub\.watermark\.\[0-9\]\+, 1, 1)" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-inlinable1.c b/gcc/testsuite/c-c++-common/torture/strub-inlinable1.c new file mode 100644 index 0000000000000..614b02228ba29 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-inlinable1.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=relaxed" } */ + +inline void __attribute__ ((strub ("internal"), always_inline)) +inl_int_ali (void) +{ + /* No internal wrapper, so this body ALWAYS gets inlined, + but it cannot be called from non-strub contexts. */ +} + +void +bat (void) +{ + /* Not allowed, not a strub context. */ + inl_int_ali (); /* { dg-error "context" } */ +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-inlinable2.c b/gcc/testsuite/c-c++-common/torture/strub-inlinable2.c new file mode 100644 index 0000000000000..f9a6b4a16faf8 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-inlinable2.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=all" } */ + +#include "strub-inlinable1.c" + +/* With -fstrub=all, the caller becomes a strub context, so the strub-inlinable + callee is not rejected. */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-ptrfn1.c b/gcc/testsuite/c-c++-common/torture/strub-ptrfn1.c new file mode 100644 index 0000000000000..b4a7f3992bbaa --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-ptrfn1.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict" } */ + +typedef void ft (void); +typedef void ft2 (int, int); +extern ft __attribute__ ((__strub__)) fnac; + +ft * f (void) { + return fnac; /* { dg-message "incompatible|invalid conversion" } */ +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-ptrfn2.c b/gcc/testsuite/c-c++-common/torture/strub-ptrfn2.c new file mode 100644 index 0000000000000..d9d2c0caec42d --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-ptrfn2.c @@ -0,0 +1,55 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=relaxed -Wpedantic" } */ + +/* C++ does not warn about the partial incompatibilities. + + The d_p () calls are actually rejected, even in C++, but they are XFAILed + here because we don't get far enough in the compilation as to observe them, + because the incompatibilities are errors without -fpermissive. + strub-ptrfn3.c uses -fpermissive to check those. + */ + +extern int __attribute__ ((strub ("callable"))) bac (void); +extern int __attribute__ ((strub ("disabled"))) bad (void); +extern int __attribute__ ((strub ("internal"))) bar (void); +extern int __attribute__ ((strub ("at-calls"))) bal (void); + +void __attribute__ ((strub)) +bap (void) +{ + int __attribute__ ((strub ("disabled"))) (*d_p) (void) = bad; + int __attribute__ ((strub ("callable"))) (*c_p) (void) = bac; + int __attribute__ ((strub ("at-calls"))) (*a_p) (void) = bal; + + d_p = bac; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bad; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bar; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bal; /* { dg-message "incompatible|invalid conversion" } */ + a_p = bac; /* { dg-message "incompatible|invalid conversion" } */ + + d_p (); /* { dg-error "indirect non-.strub. call in .strub. context" "" { xfail c++ } } */ + c_p (); + a_p (); +} + +void __attribute__ ((strub)) +baP (void) +{ + typedef int __attribute__ ((strub ("disabled"))) d_fn_t (void); + typedef int __attribute__ ((strub ("callable"))) c_fn_t (void); + typedef int __attribute__ ((strub ("at-calls"))) a_fn_t (void); + + d_fn_t *d_p = bad; + c_fn_t *c_p = bac; + a_fn_t *a_p = bal; + + d_p = bac; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bad; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bar; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bal; /* { dg-message "incompatible|invalid conversion" } */ + a_p = bac; /* { dg-message "incompatible|invalid conversion" } */ + + d_p (); /* { dg-error "indirect non-.strub. call in .strub. context" "" { xfail c++ } } */ + c_p (); + a_p (); +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-ptrfn3.c b/gcc/testsuite/c-c++-common/torture/strub-ptrfn3.c new file mode 100644 index 0000000000000..e1f179e160e5c --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-ptrfn3.c @@ -0,0 +1,50 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=relaxed -Wpedantic -fpermissive" } */ +/* { dg-prune-output "command-line option .-fpermissive." } */ + +/* See strub-ptrfn2.c. */ + +extern int __attribute__ ((strub ("callable"))) bac (void); +extern int __attribute__ ((strub ("disabled"))) bad (void); +extern int __attribute__ ((strub ("internal"))) bar (void); +extern int __attribute__ ((strub ("at-calls"))) bal (void); + +void __attribute__ ((strub)) +bap (void) +{ + int __attribute__ ((strub ("disabled"))) (*d_p) (void) = bad; + int __attribute__ ((strub ("callable"))) (*c_p) (void) = bac; + int __attribute__ ((strub ("at-calls"))) (*a_p) (void) = bal; + + d_p = bac; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bad; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bar; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bal; /* { dg-message "incompatible|invalid conversion" } */ + a_p = bac; /* { dg-message "incompatible|invalid conversion" } */ + + d_p (); /* { dg-error "indirect non-.strub. call in .strub. context" } */ + c_p (); + a_p (); +} + +void __attribute__ ((strub)) +baP (void) +{ + typedef int __attribute__ ((strub ("disabled"))) d_fn_t (void); + typedef int __attribute__ ((strub ("callable"))) c_fn_t (void); + typedef int __attribute__ ((strub ("at-calls"))) a_fn_t (void); + + d_fn_t *d_p = bad; + c_fn_t *c_p = bac; + a_fn_t *a_p = bal; + + d_p = bac; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bad; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bar; /* { dg-warning "not quite compatible" "" { xfail c++ } } */ + c_p = bal; /* { dg-message "incompatible|invalid conversion" } */ + a_p = bac; /* { dg-message "incompatible|invalid conversion" } */ + + d_p (); /* { dg-error "indirect non-.strub. call in .strub. context" } */ + c_p (); + a_p (); +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-ptrfn4.c b/gcc/testsuite/c-c++-common/torture/strub-ptrfn4.c new file mode 100644 index 0000000000000..70b558afad040 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-ptrfn4.c @@ -0,0 +1,43 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=relaxed" } */ + +/* This is strub-ptrfn2.c without -Wpedantic. + + Even C doesn't report the (not-quite-)compatible conversions without it. */ + +extern int __attribute__ ((strub ("callable"))) bac (void); +extern int __attribute__ ((strub ("disabled"))) bad (void); +extern int __attribute__ ((strub ("internal"))) bar (void); +extern int __attribute__ ((strub ("at-calls"))) bal (void); + +void __attribute__ ((strub)) +bap (void) +{ + int __attribute__ ((strub ("disabled"))) (*d_p) (void) = bad; + int __attribute__ ((strub ("callable"))) (*c_p) (void) = bac; + int __attribute__ ((strub ("at-calls"))) (*a_p) (void) = bal; + + d_p = bac; + c_p = bad; + c_p = bar; + c_p = bal; /* { dg-message "incompatible|invalid conversion" } */ + a_p = bac; /* { dg-message "incompatible|invalid conversion" } */ +} + +void __attribute__ ((strub)) +baP (void) +{ + typedef int __attribute__ ((strub ("disabled"))) d_fn_t (void); + typedef int __attribute__ ((strub ("callable"))) c_fn_t (void); + typedef int __attribute__ ((strub ("at-calls"))) a_fn_t (void); + + d_fn_t *d_p = bad; + c_fn_t *c_p = bac; + a_fn_t *a_p = bal; + + d_p = bac; + c_p = bad; + c_p = bar; + c_p = bal; /* { dg-message "incompatible|invalid conversion" } */ + a_p = bac; /* { dg-message "incompatible|invalid conversion" } */ +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-pure1.c b/gcc/testsuite/c-c++-common/torture/strub-pure1.c new file mode 100644 index 0000000000000..a262a086837b2 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-pure1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* Check that, along with a strub pure function call, we issue an asm statement + to make sure the watermark passed to it is not assumed to be unchanged. */ + +int __attribute__ ((__strub__, __pure__)) +f() { + static int i; /* Stop it from being detected as const. */ + return i; +} + +int +g() { + return f(); +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-pure2.c b/gcc/testsuite/c-c++-common/torture/strub-pure2.c new file mode 100644 index 0000000000000..4c4bd50c209a0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-pure2.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* Check that, along with a strub implicitly-pure function call, we issue an asm + statement to make sure the watermark passed to it is not assumed to be + unchanged. */ + +int __attribute__ ((__strub__)) +#if ! __OPTIMIZE__ /* At -O0, implicit pure detection doesn't run. */ +__attribute__ ((__pure__)) +#endif +f() { + static int i; /* Stop it from being detected as const. */ + return i; +} + +int +g() { + return f(); +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-pure3.c b/gcc/testsuite/c-c++-common/torture/strub-pure3.c new file mode 100644 index 0000000000000..ce195c6b1f1b6 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-pure3.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* Check that, along with a strub pure wrapping call, we issue an asm statement + to make sure the watermark passed to it is not assumed to be unchanged. */ + +int __attribute__ ((__strub__ ("internal"), __pure__)) +f() { + static int i; /* Stop it from being detected as const. */ + return i; +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-pure4.c b/gcc/testsuite/c-c++-common/torture/strub-pure4.c new file mode 100644 index 0000000000000..75cd54ccb5b5d --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-pure4.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* Check that, along with a strub implicitly-pure wrapping call, we issue an asm + statement to make sure the watermark passed to it is not assumed to be + unchanged. */ + +int __attribute__ ((__strub__ ("internal"))) +#if ! __OPTIMIZE__ /* At -O0, implicit pure detection doesn't run. */ +__attribute__ ((__pure__)) +#endif +f() { + static int i; /* Stop it from being detected as const. */ + return i; +} + +/* { dg-final { scan-ipa-dump-times "__asm__" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/torture/strub-run1.c b/gcc/testsuite/c-c++-common/torture/strub-run1.c new file mode 100644 index 0000000000000..b24a1c7a345fa --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run1.c @@ -0,0 +1,90 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=strict" } */ + +/* Check that a non-strub function leaves a string behind in the stack, and that + equivalent strub functions don't. Avoid the use of red zones by avoiding + leaf functions. */ + +const char test_string[] = "\x55\xde\xad\xbe\xef\xc0\x1d\xca\xfe\x55\xaa"; + +/* Pad before and after the string on the stack, so that it's not overwritten by + regular stack use. */ +#define PAD 7 + +static inline __attribute__ ((__always_inline__, __strub__ ("callable"))) +char * +leak_string (void) +{ + /* We use this variable to avoid any stack red zone. Stack scrubbing covers + it, but __builtin_stack_address, that we take as a reference, doesn't, so + if e.g. callable() were to store the string in the red zone, we wouldn't + find it because it would be outside the range we searched. */ + typedef void __attribute__ ((__strub__ ("callable"))) callable_t (char *); + callable_t *f = 0; + + char s[2 * PAD + 1][sizeof (test_string)]; + __builtin_strcpy (s[PAD], test_string); + asm ("" : "+m" (s), "+r" (f)); + + if (__builtin_expect (!f, 1)) + return (char *) __builtin_stack_address (); + + f (s[PAD]); + return 0; +} + +static inline __attribute__ ((__always_inline__)) +int +look_for_string (char *e) +{ + char *p = (char *) __builtin_stack_address (); + + if (p == e) + __builtin_abort (); + + if (p > e) + { + char *q = p; + p = e; + e = q; + } + + for (char *re = e - sizeof (test_string); p < re; p++) + for (int i = 0; p[i] == test_string[i]; i++) + if (i == sizeof (test_string) - 1) + return i; + + return 0; +} + +static __attribute__ ((__noinline__, __noclone__)) +char * +callable () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ ("at-calls"))) +char * +at_calls () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ ("internal"))) +char * +internal () +{ + return leak_string (); +} + +int main () +{ + if (!look_for_string (callable ())) + __builtin_abort (); + if (look_for_string (at_calls ())) + __builtin_abort (); + if (look_for_string (internal ())) + __builtin_abort (); + __builtin_exit (0); +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-run2.c b/gcc/testsuite/c-c++-common/torture/strub-run2.c new file mode 100644 index 0000000000000..1df2ffe2fe58c --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run2.c @@ -0,0 +1,79 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=strict" } */ + +/* Check that a non-strub function leaves a string behind in the stack, and that + equivalent strub functions don't. Allow red zones to be used. */ + +const char test_string[] = "\x55\xde\xad\xbe\xef\xc0\x1d\xca\xfe\x55\xaa"; + +/* Pad before and after the string on the stack, so that it's not overwritten by + regular stack use. */ +#define PAD 7 + +static inline __attribute__ ((__always_inline__, __strub__ ("callable"))) +char * +leak_string (void) +{ + int len = sizeof (test_string); + asm ("" : "+rm" (len)); + char s[2 * PAD + 1][len]; + __builtin_strcpy (s[PAD], test_string); + asm ("" : "+m" (s)); + return (char *) __builtin_stack_address (); +} + +static inline __attribute__ ((__always_inline__)) +int +look_for_string (char *e) +{ + char *p = (char *) __builtin_stack_address (); + + if (p == e) + __builtin_abort (); + + if (p > e) + { + char *q = p; + p = e; + e = q; + } + + for (char *re = e - sizeof (test_string); p < re; p++) + for (int i = 0; p[i] == test_string[i]; i++) + if (i == sizeof (test_string) - 1) + return i; + + return 0; +} + +static __attribute__ ((__noinline__, __noclone__)) +char * +callable () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ ("at-calls"))) +char * +at_calls () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ ("internal"))) +char * +internal () +{ + return leak_string (); +} + +int main () +{ + if (!look_for_string (callable ())) + __builtin_abort (); + if (look_for_string (at_calls ())) + __builtin_abort (); + if (look_for_string (internal ())) + __builtin_abort (); + __builtin_exit (0); +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-run3.c b/gcc/testsuite/c-c++-common/torture/strub-run3.c new file mode 100644 index 0000000000000..afbc2cc9ab484 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run3.c @@ -0,0 +1,75 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=strict" } */ +/* { dg-require-effective-target alloca } */ + +/* Check that a non-strub function leaves a string behind in the stack, and that + equivalent strub functions don't. */ + +const char test_string[] = "\x55\xde\xad\xbe\xef\xc0\x1d\xca\xfe\x55\xaa"; + +static inline __attribute__ ((__always_inline__, __strub__ ("callable"))) +char * +leak_string (void) +{ + int len = sizeof (test_string); + char *s = (char *) __builtin_alloca (len); + __builtin_strcpy (s, test_string); + asm ("" : "+m" (s)); + return (char *) __builtin_stack_address (); +} + +static inline __attribute__ ((__always_inline__)) +int +look_for_string (char *e) +{ + char *p = (char *) __builtin_stack_address (); + + if (p == e) + __builtin_abort (); + + if (p > e) + { + char *q = p; + p = e; + e = q; + } + + for (char *re = e - sizeof (test_string); p < re; p++) + for (int i = 0; p[i] == test_string[i]; i++) + if (i == sizeof (test_string) - 1) + return i; + + return 0; +} + +static __attribute__ ((__noinline__, __noclone__)) +char * +callable () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ ("at-calls"))) +char * +at_calls () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ ("internal"))) +char * +internal () +{ + return leak_string (); +} + +int main () +{ + if (!look_for_string (callable ())) + __builtin_abort (); + if (look_for_string (at_calls ())) + __builtin_abort (); + if (look_for_string (internal ())) + __builtin_abort (); + __builtin_exit (0); +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-run4.c b/gcc/testsuite/c-c++-common/torture/strub-run4.c new file mode 100644 index 0000000000000..5300f1d330b87 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run4.c @@ -0,0 +1,101 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=all" } */ +/* { dg-require-effective-target alloca } */ + +/* Check that multi-level, multi-inlined functions still get cleaned up as + expected, without overwriting temporary stack allocations while they should + still be available. */ + +#ifndef ATTR_STRUB_AT_CALLS +# define ATTR_STRUB_AT_CALLS /* Defined in strub-run4d.c. */ +#endif + +const char test_string[] = "\x55\xde\xad\xbe\xef\xc0\x1d\xca\xfe\x55\xaa"; + +static inline __attribute__ ((__always_inline__)) +char * +leak_string (void) +{ + int __attribute__ ((__strub__)) len = 512; + asm ("" : "+r" (len)); + char s[len]; + __builtin_strcpy (s, test_string); + __builtin_strcpy (s + len - sizeof (test_string), test_string); + asm ("" : "+m" (s)); + return (char *) __builtin_stack_address (); +} + +static inline __attribute__ ((__always_inline__)) +int +look_for_string (char *e) +{ + char *p = (char *) __builtin_stack_address (); + + if (p == e) + __builtin_abort (); + + if (p > e) + { + char *q = p; + p = e; + e = q; + } + + for (char *re = e - sizeof (test_string); p < re; p++) + for (int i = 0; p[i] == test_string[i]; i++) + if (i == sizeof (test_string) - 1) + return i; + + return 0; +} + +static inline ATTR_STRUB_AT_CALLS +char * +innermost () +{ + int __attribute__ ((__strub__)) len = 512; + asm ("" : "+r" (len)); + char s[len]; + __builtin_strcpy (s, test_string); + __builtin_strcpy (s + len - sizeof (test_string), test_string); + asm ("" : "+m" (s)); + char *ret = leak_string (); + if (__builtin_strcmp (s, test_string) != 0) + __builtin_abort (); + if (__builtin_strcmp (s + len - sizeof (test_string), test_string) != 0) + __builtin_abort (); + return ret; +} + +static inline ATTR_STRUB_AT_CALLS +char * +intermediate () +{ + int __attribute__ ((__strub__)) len = 512; + asm ("" : "+r" (len)); + char s[len]; + __builtin_strcpy (s, test_string); + __builtin_strcpy (s + len - sizeof (test_string), test_string); + asm ("" : "+m" (s)); + char *ret = innermost (); + if (__builtin_strcmp (s, test_string) != 0) + __builtin_abort (); + if (__builtin_strcmp (s + len - sizeof (test_string), test_string) != 0) + __builtin_abort (); + return ret; +} + +static inline __attribute__ ((__strub__ ("internal"))) +char * +internal () +{ + return intermediate (); +} + +int __attribute__ ((__strub__ ("disabled"))) +main () +{ + if (look_for_string (internal ())) + __builtin_abort (); + __builtin_exit (0); +} diff --git a/gcc/testsuite/c-c++-common/torture/strub-run4c.c b/gcc/testsuite/c-c++-common/torture/strub-run4c.c new file mode 100644 index 0000000000000..57f9baf758ded --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run4c.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=at-calls" } */ +/* { dg-require-effective-target alloca } */ + +#include "strub-run4.c" diff --git a/gcc/testsuite/c-c++-common/torture/strub-run4d.c b/gcc/testsuite/c-c++-common/torture/strub-run4d.c new file mode 100644 index 0000000000000..08de3f1c3b17c --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run4d.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=strict" } */ +/* { dg-require-effective-target alloca } */ + +#define ATTR_STRUB_AT_CALLS __attribute__ ((__strub__ ("at-calls"))) + +#include "strub-run4.c" diff --git a/gcc/testsuite/c-c++-common/torture/strub-run4i.c b/gcc/testsuite/c-c++-common/torture/strub-run4i.c new file mode 100644 index 0000000000000..459f6886c5499 --- /dev/null +++ b/gcc/testsuite/c-c++-common/torture/strub-run4i.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=internal" } */ +/* { dg-require-effective-target alloca } */ + +#include "strub-run4.c"