From patchwork Fri Jul 29 06:24:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 56411 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 285C13857BAB for ; Fri, 29 Jul 2022 06:25:12 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 285C13857BAB DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1659075912; bh=mimJ+N++pRGG9xJUUveKeOAqMUsR1JP170dESMTHu2M=; 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=bXcE9TM+FqRaxkHcH2fzT6AT5ROHRlLCdCmy01Tw4EP1SfBQQdbL7FWpsCGDrUBGP 0EtrIetsRv6A1mgE5l/qUXeUO4b9MIwP2vqD8oMXosJqEwhTfq3l73mwnA2NgvR/Jp vYMcJHerGbGsCSdDq3vF+4PbK2tzW2cHXV8Ms/6c= 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 1EAC8385840A for ; Fri, 29 Jul 2022 06:24:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 1EAC8385840A Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 6C4141168D2; Fri, 29 Jul 2022 02:24:40 -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 WDdh6gqrM53m; Fri, 29 Jul 2022 02:24:40 -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 762CE1168CD; Fri, 29 Jul 2022 02:24:39 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 26T6OTQ71851928 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 Jul 2022 03:24:29 -0300 To: gcc-patches@gcc.gnu.org Subject: [PATCH v2 01/10] Introduce strub: documentation, and new command-line options Organization: Free thinker, does not speak for AdaCore References: Date: Fri, 29 Jul 2022 03:24:29 -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" Ada already has some strub documentation in gcc/ada/doc/gnat_rm/security_hardening_features.rst. for gcc/ChangeLog * common.opt (fstrub=*): New options. * doc/extend.texi (strub): New type attribute. (__builtin_stack_address): New function. (Stack Scrubbing): New section. * doc/invoke.texi (-fstrub=*): New options. (-fdump-ipa-*): New passes. diff --git a/gcc/common.opt b/gcc/common.opt index e7a51e882bade..2c635806bbf2c 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2780,6 +2780,35 @@ fstrict-overflow Common Treat signed overflow as undefined. Negated as -fwrapv -fwrapv-pointer. +fstrub=disable +Common RejectNegative Var(flag_strub, 0) +Disable stack scrub entirely, disregarding strub attributes. + +fstrub=strict +Common RejectNegative Var(flag_strub, -4) +Enable stack scrub as per attributes, with strict call checking. + +; If any strub-enabling attribute is seen when the default or strict +; initializer values are in effect, flag_strub is bumped up by 2. The +; scrub mode gate function will then bump these initializer values to +; 0 if no strub-enabling attribute is seen. This minimizes the strub +; overhead. +fstrub=relaxed +Common RejectNegative Var(flag_strub, -3) Init(-3) +Restore default strub mode: as per attributes, with relaxed checking. + +fstrub=all +Common RejectNegative Var(flag_strub, 3) +Enable stack scrubbing for all viable functions. + +fstrub=at-calls +Common RejectNegative Var(flag_strub, 1) +Enable at-calls stack scrubbing for all viable functions. + +fstrub=internal +Common RejectNegative Var(flag_strub, 2) +Enable internal stack scrubbing for all viable functions. + fsync-libcalls Common Var(flag_sync_libcalls) Init(1) Implement __atomic operations via libcalls to legacy __sync functions. diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 7fe7f8817cdd4..90d38c9f08362 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -77,6 +77,7 @@ extensions, accepted by GCC in C90 mode and in C++. * Function Names:: Printable strings which are the name of the current function. * Return Address:: Getting the return or frame address of a function. +* Stack Scrubbing:: Stack scrubbing internal interfaces. * Vector Extensions:: Using vector instructions through built-in functions. * Offsetof:: Special syntax for implementing @code{offsetof}. * __sync Builtins:: Legacy built-in functions for atomic memory access. @@ -8890,6 +8891,263 @@ pid_t wait (wait_status_ptr_t p) @} @end smallexample +@item strub +@cindex @code{strub} type attribute +This attribute defines stack-scrubbing properties of functions and +variables. Being a type attribute, it attaches to types, even when +specified in function and variable declarations. When applied to +function types, it takes an optional string argument. When applied to a +pointer-to-function type, if the optional argument is given, it gets +propagated to the function type. + +@smallexample +/* A strub variable. */ +int __attribute__ ((strub)) var; +/* A strub variable that happens to be a pointer. */ +__attribute__ ((strub)) int *strub_ptr_to_int; +/* A pointer type that may point to a strub variable. */ +typedef int __attribute__ ((strub)) *ptr_to_strub_int_type; + +/* A declaration of a strub function. */ +extern int __attribute__ ((strub)) foo (void); +/* A pointer to that strub function. */ +int __attribute__ ((strub ("at-calls"))) (*ptr_to_strub_fn)(void) = foo; +@end smallexample + +A function associated with @code{at-calls} @code{strub} mode +(@code{strub("at-calls")}, or just @code{strub}) undergoes interface +changes. Its callers are adjusted to match the changes, and to scrub +(overwrite with zeros) the stack space used by the called function after +it returns. The interface change makes the function type incompatible +with an unadorned but otherwise equivalent type, so @emph{every} +declaration and every type that may be used to call the function must be +associated with this strub mode. + +A function associated with @code{internal} @code{strub} mode +(@code{strub("internal")}) retains an unmodified, type-compatible +interface, but it may be turned into a wrapper that calls the wrapped +body using a custom interface. The wrapper then scrubs the stack space +used by the wrapped body. Though the wrapped body has its stack space +scrubbed, the wrapper does not, so arguments and return values may +remain unscrubbed even when such a function is called by another +function that enables @code{strub}. This is why, when compiling with +@option{-fstrub=strict}, a @code{strub} context is not allowed to call +@code{internal} @code{strub} functions. + +@smallexample +/* A declaration of an internal-strub function. */ +extern int __attribute__ ((strub ("internal"))) bar (void); + +int __attribute__ ((strub)) +baz (void) +@{ + /* Ok, foo was declared above as an at-calls strub function. */ + foo (); + /* Not allowed in strict mode, otherwise allowed. */ + bar (); +@} +@end smallexample + +An automatically-allocated variable associated with the @code{strub} +attribute causes the (immediately) enclosing function to have +@code{strub} enabled. + +A statically-allocated variable associated with the @code{strub} +attribute causes functions that @emph{read} it, through its @code{strub} +data type, to have @code{strub} enabled. Reading data by dereferencing +a pointer to a @code{strub} data type has the same effect. Note: The +attribute does not carry over from a composite type to the types of its +components, so the intended effect may not be obtained with non-scalar +types. + +When selecting a @code{strub}-enabled mode for a function that is not +explicitly associated with one, because of @code{strub} variables or +data pointers, the function must satisfy @code{internal} mode viability +requirements (see below), even when @code{at-calls} mode is also viable +and, being more efficient, ends up selected as an optimization. + +@smallexample +/* zapme is implicitly strub-enabled because of strub variables. + Optimization may change its strub mode, but not the requirements. */ +static int +zapme (int i) +@{ + /* A local strub variable enables strub. */ + int __attribute__ ((strub)) lvar; + /* Reading strub data through a pointer-to-strub enables strub. */ + lvar = * (ptr_to_strub_int_type) &i; + /* Writing to a global strub variable does not enable strub. */ + var = lvar; + /* Reading from a global strub variable enables strub. */ + return var; +@} +@end smallexample + +A @code{strub} context is the body (as opposed to the interface) of a +function that has @code{strub} enabled, be it explicitly, by +@code{at-calls} or @code{internal} mode, or implicitly, due to +@code{strub} variables or command-line options. + +A function of a type associated with the @code{disabled} @code{strub} +mode (@code{strub("disabled")} will not have its own stack space +scrubbed. Such functions @emph{cannot} be called from within +@code{strub} contexts. + +In order to enable a function to be called from within @code{strub} +contexts without having its stack space scrubbed, associate it with the +@code{callable} @code{strub} mode (@code{strub("callable")}). + +When a function is not assigned a @code{strub} mode, explicitly or +implicitly, the mode defaults to @code{callable}, except when compiling +with @option{-fstrub=strict}, that causes @code{strub} mode to default +to @code{disabled}. + +@example +extern int __attribute__ ((strub ("callable"))) bac (void); +extern int __attribute__ ((strub ("disabled"))) bad (void); + /* Implicitly disabled with -fstrub=strict, otherwise callable. */ +extern int bah (void); + +int __attribute__ ((strub)) +bal (void) +@{ + /* Not allowed, bad is not strub-callable. */ + bad (); + /* Ok, bac is strub-callable. */ + bac (); + /* Not allowed with -fstrub=strict, otherwise allowed. */ + bah (); +@} +@end example + +Function types marked @code{callable} and @code{disabled} are not +mutually compatible types, but the underlying interfaces are compatible, +so it is safe to convert pointers between them, and to use such pointers +or alternate declarations to call them. Interfaces are also +interchangeable between them and @code{internal} (but not +@code{at-calls}!), but adding @code{internal} to a pointer type will not +cause the pointed-to function to perform stack scrubbing. + +@example +void __attribute__ ((strub)) +bap (void) +@{ + /* Assign a callable function to pointer-to-disabled. + Flagged as not quite compatible with -Wpedantic. */ + int __attribute__ ((strub ("disabled"))) (*d_p) (void) = bac; + /* Not allowed: calls disabled type in a strub context. */ + d_p (); + + /* Assign a disabled function to pointer-to-callable. + Flagged as not quite compatible with -Wpedantic. */ + int __attribute__ ((strub ("callable"))) (*c_p) (void) = bad; + /* Ok, safe. */ + c_p (); + + /* Assign an internal function to pointer-to-callable. + Flagged as not quite compatible with -Wpedantic. */ + c_p = bar; + /* Ok, safe. */ + c_p (); + + /* Assign an at-calls function to pointer-to-callable. + Flaggged as incompatible. */ + c_p = bal; + /* The call through an interface-incompatible type will not use the + modified interface expected by the at-calls function, so it is + likely to misbehave at runtime. */ + c_p (); +@} +@end example + +@code{Strub} contexts are never inlined into non-@code{strub} contexts. +When an @code{internal}-strub function is split up, the wrapper can +often be inlined, but the wrapped body @emph{never} is. A function +marked as @code{always_inline}, even if explicitly assigned +@code{internal} strub mode, will not undergo wrapping, so its body gets +inlined as required. + +@example +inline int __attribute__ ((strub ("at-calls"))) +inl_atc (void) +@{ + /* This body may get inlined into strub contexts. */ +@} + +inline int __attribute__ ((strub ("internal"))) +inl_int (void) +@{ + /* This body NEVER gets inlined, though its wrapper may. */ +@} + +inline int __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 __attribute__ ((strub ("disabled"))) +bat (void) +@{ + /* Not allowed, cannot inline into a non-strub context. */ + inl_int_ali (); +@} +@end example + +@cindex strub eligibility and viability +Some @option{-fstrub=*} command line options enable @code{strub} modes +implicitly where viable. A @code{strub} mode is only viable for a +function if the function is eligible for that mode, and if other +conditions, detailed below, are satisfied. If it's not eligible for a +mode, attempts to explicitly associate it with that mode are rejected +with an error message. If it is eligible, that mode may be assigned +explicitly through this attribute, but implicit assignment through +command-line options may involve additional viability requirements. + +A function is ineligible for @code{at-calls} @code{strub} mode if a +different @code{strub} mode is explicitly requested, if attribute +@code{noipa} is present, or if it calls @code{__builtin_apply_args}. +@code{At-calls} @code{strub} mode, if not requested through the function +type, is only viable for an eligible function if the function is not +visible to other translation units, if it doesn't have its address +taken, and if it is never called with a function type overrider. + +@smallexample +/* bar is eligible for at-calls strub mode, + but not viable for that mode because it is visible to other units. + It is eligible and viable for internal strub mode. */ +void bav () @{@} + +/* setp is eligible for at-calls strub mode, + but not viable for that mode because its address is taken. + It is eligible and viable for internal strub mode. */ +void setp (void) @{ static void (*p)(void); = setp; @} +@end smallexample + +A function is ineligible for @code{internal} @code{strub} mode if a +different @code{strub} mode is explicitly requested, or if attribute +@code{noipa} is present. For an @code{always_inline} function, meeting +these requirements is enough to make it eligible. Any function that has +attribute @code{noclone}, that uses such extensions as non-local labels, +computed gotos, alternate variable argument passing interfaces, +@code{__builtin_next_arg}, or @code{__builtin_return_address}, or that +takes too many (about 64Ki) arguments is ineligible, unless it is +@code{always_inline}. For @code{internal} @code{strub} mode, all +eligible functions are viable. + +@smallexample +/* flop is not eligible, thus not viable, for at-calls strub mode. + Likewise for internal strub mode. */ +__attribute__ ((noipa)) void flop (void) @{@} + +/* flip is eligible and viable for at-calls strub mode. + It would be ineligible for internal strub mode, because of noclone, + if it weren't for always_inline. With always_inline, noclone is not + an obstacle, so it is also eligible and viable for internal strub mode. */ +inline __attribute__ ((noclone, always_inline)) void flip (void) @{@} +@end smallexample + @item unused @cindex @code{unused} type attribute When attached to a type (including a @code{union} or a @code{struct}), @@ -11927,6 +12185,55 @@ option is in effect. Such calls should only be made in debugging situations. @end deftypefn +@deftypefn {Built-in Function} {void *} __builtin_stack_address () +This function returns the value of the stack pointer register. +@end deftypefn + +@node Stack Scrubbing +@section Stack scrubbing internal interfaces + +Stack scrubbing involves cooperation between a @code{strub} context, +i.e., a function whose stack frame is to be zeroed-out, and its callers. +The caller initializes a stack watermark, the @code{strub} context +updates the watermark according to its stack use, and the caller zeroes +it out once it regains control, whether by the callee's returning or by +an exception. + +Each of these steps is performed by a different builtin function call. +Calls to these builtins are introduced automatically, in response to +@code{strub} attributes and command-line options; they are not expected +to be explicitly called by source code. + +The functions that implement the builtins are available in libgcc but, +depending on optimization levels, they are expanded internally, adjusted +to account for inlining, and sometimes combined/deferred (e.g. passing +the caller-supplied watermark on to callees, refraining from erasing +stack areas that the caller will) to enable tail calls and to optimize +for code size. + +@deftypefn {Built-in Function} {void} __builtin___strub_enter (void **@var{wmptr}) +This function initializes a stack @var{watermark} variable with the +current top of the stack. A call to this builtin function is introduced +before entering a @code{strub} context. It remains as a function call +if optimization is not enabled. +@end deftypefn + +@deftypefn {Built-in Function} {void} __builtin___strub_update (void **@var{wmptr}) +This function updates a stack @var{watermark} variable with the current +top of the stack, if it tops the previous watermark. A call to this +builtin function is inserted within @code{strub} contexts, whenever +additional stack space may have been used. It remains as a function +call at optimization levels lower than 2. +@end deftypefn + +@deftypefn {Built-in Function} {void} __builtin___strub_leave (void **@var{wmptr}) +This function overwrites the memory area between the current top of the +stack, and the @var{watermark}ed address. A call to this builtin +function is inserted after leaving a @code{strub} context. It remains +as a function call at optimization levels lower than 3, and it is guarded by +a condition at level 2. +@end deftypefn + @node Vector Extensions @section Using Vector Instructions through Built-in Functions diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index fa23fbeaaaa22..1226efb5e7e3a 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -624,6 +624,8 @@ Objective-C and Objective-C++ Dialects}. -fstack-protector-explicit -fstack-check @gol -fstack-limit-register=@var{reg} -fstack-limit-symbol=@var{sym} @gol -fno-stack-limit -fsplit-stack @gol +-fstrub=disable -fstrub=strict -fstrub=relaxed @gol +-fstrub=all -fstrub=at-calls -fstrub=internal @gol -fvtable-verify=@r{[}std@r{|}preinit@r{|}none@r{]} @gol -fvtv-counts -fvtv-debug @gol -finstrument-functions -finstrument-functions-once @gol @@ -16482,6 +16484,56 @@ without @option{-fsplit-stack} always has a large stack. Support for this is implemented in the gold linker in GNU binutils release 2.21 and later. +@item -fstrub=disable +@opindex -fstrub=disable +Disable stack scrubbing entirely, ignoring any @code{strub} attributes. +See @xref{Common Type Attributes}. + +@item -fstrub=strict +@opindex fstrub=strict +Functions default to @code{strub} mode @code{disabled}, and apply +@option{strict}ly the restriction that only functions associated with +@code{strub}-@code{callable} modes (@code{at-calls}, @code{callable} and +@code{always_inline} @code{internal}) are @code{callable} by functions +with @code{strub}-enabled modes (@code{at-calls} and @code{internal}). + +@item -fstrub=relaxed +@opindex fstrub=relaxed +Restore the default stack scrub (@code{strub}) setting, namely, +@code{strub} is only enabled as required by @code{strub} attributes +associated with function and data types. @code{Relaxed} means that +strub contexts are only prevented from calling functions explicitly +associated with @code{strub} mode @code{disabled}. This option is only +useful to override other @option{-fstrub=*} options that precede it in +the command line. + +@item -fstrub=at-calls +@opindex fstrub=at-calls +Enable @code{at-calls} @code{strub} mode where viable. The primary use +of this option is for testing. It exercises the @code{strub} machinery +in scenarios strictly local to a translation unit. This @code{strub} +mode modifies function interfaces, so any function that is visible to +other translation units, or that has its address taken, will @emph{not} +be affected by this option. Optimization options may also affect +viability. See the @code{strub} attribute documentation for details on +viability and eligibility requirements. + +@item -fstrub=internal +@opindex fstrub=internal +Enable @code{internal} @code{strub} mode where viable. The primary use +of this option is for testing. This option is intended to exercise +thoroughly parts of the @code{strub} machinery that implement the less +efficient, but interface-preserving @code{strub} mode. Functions that +would not be affected by this option are quite uncommon. + +@item -fstrub=all +@opindex fstrub=all +Enable some @code{strub} mode where viable. When both strub modes are +viable, @code{at-calls} is preferred. @option{-fdump-ipa-strubm} adds +function attributes that tell which mode was selected for each function. +The primary use of this option is for testing, to exercise thoroughly +the @code{strub} machinery. + @item -fvtable-verify=@r{[}std@r{|}preinit@r{|}none@r{]} @opindex fvtable-verify This option is only available when compiling C++ code. @@ -18379,6 +18431,14 @@ and inlining decisions. @item inline Dump after function inlining. +@item strubm +Dump after selecting @code{strub} modes, and recording the selections as +function attributes. + +@item strub +Dump @code{strub} transformations: interface changes, function wrapping, +and insertion of builtin calls for stack scrubbing and watermarking. + @end table Additionally, the options @option{-optimized}, @option{-missed}, 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" From patchwork Fri Jul 29 06:25:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 56413 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 D8B9C3853802 for ; Fri, 29 Jul 2022 06:26:28 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D8B9C3853802 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1659075988; bh=/GS0CmSTXuKGMWFtt0yUGwJBpvG5sNq7SzEE9Rxz78U=; 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=vgC/RHWImbZTgVksoKQGr1KKKfPSiG/Z6SHOCIkd6C2ZZXeDQZuuwptYuihk/ar85 qzNTBRsdHYcpujFXBPqn8wIF/yzPnjem22JB3yG7Zku5rM1hoOZFAFnSMhSOh6VQEI tD7ESMs7rUsMakYuypB9jv7Efc9yV6NABEpnyb2U= 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 BC5143854158 for ; Fri, 29 Jul 2022 06:25:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org BC5143854158 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 2A1201168D2; Fri, 29 Jul 2022 02:25:55 -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 kZlb7HFwUCZd; Fri, 29 Jul 2022 02:25:55 -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 54ABB1168CD; Fri, 29 Jul 2022 02:25:54 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 26T6Pg341851954 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 Jul 2022 03:25:42 -0300 To: gcc-patches@gcc.gnu.org Subject: [PATCH v2 03/10] Introduce strub: non-torture tests for C and C++ Organization: Free thinker, does not speak for AdaCore References: Date: Fri, 29 Jul 2022 03:25:42 -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/strub-O0.c: New. * c-c++-common/strub-O1.c: New. * c-c++-common/strub-O2.c: New. * c-c++-common/strub-O2fni.c: New. * c-c++-common/strub-O3.c: New. * c-c++-common/strub-O3fni.c: New. * c-c++-common/strub-Og.c: New. * c-c++-common/strub-Os.c: New. * c-c++-common/strub-all1.c: New. * c-c++-common/strub-all2.c: New. * c-c++-common/strub-apply1.c: New. * c-c++-common/strub-apply2.c: New. * c-c++-common/strub-apply3.c: New. * c-c++-common/strub-apply4.c: New. * c-c++-common/strub-at-calls1.c: New. * c-c++-common/strub-at-calls2.c: New. * c-c++-common/strub-defer-O1.c: New. * c-c++-common/strub-defer-O2.c: New. * c-c++-common/strub-defer-O3.c: New. * c-c++-common/strub-defer-Os.c: New. * c-c++-common/strub-internal1.c: New. * c-c++-common/strub-internal2.c: New. * c-c++-common/strub-parms1.c: New. * c-c++-common/strub-parms2.c: New. * c-c++-common/strub-parms3.c: New. * c-c++-common/strub-relaxed1.c: New. * c-c++-common/strub-relaxed2.c: New. * c-c++-common/strub-short-O0-exc.c: New. * c-c++-common/strub-short-O0.c: New. * c-c++-common/strub-short-O1.c: New. * c-c++-common/strub-short-O2.c: New. * c-c++-common/strub-short-O3.c: New. * c-c++-common/strub-short-Os.c: New. * c-c++-common/strub-strict1.c: New. * c-c++-common/strub-strict2.c: New. * c-c++-common/strub-tail-O1.c: New. * c-c++-common/strub-tail-O2.c: New. diff --git a/gcc/testsuite/c-c++-common/strub-O0.c b/gcc/testsuite/c-c++-common/strub-O0.c new file mode 100644 index 0000000000000..c7a79a6ea0d8a --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O0.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-O0 -fstrub=strict -fdump-rtl-expand" } */ + +/* At -O0, none of the strub builtins are expanded inline. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O1.c b/gcc/testsuite/c-c++-common/strub-O1.c new file mode 100644 index 0000000000000..96285c975d98e --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O1 -fstrub=strict -fdump-rtl-expand" } */ + +/* At -O1, without -fno-inline, we fully expand enter, but neither update nor + leave. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O2.c b/gcc/testsuite/c-c++-common/strub-O2.c new file mode 100644 index 0000000000000..8edc0d8aa1321 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O2.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fstrub=strict -fdump-rtl-expand" } */ + +/* At -O2, without -fno-inline, we fully expand enter and update, and add a test + around the leave call. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump "\[(\]call\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O2fni.c b/gcc/testsuite/c-c++-common/strub-O2fni.c new file mode 100644 index 0000000000000..c6d900cf3c45b --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O2fni.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fstrub=strict -fdump-rtl-expand -fno-inline" } */ + +/* With -fno-inline, none of the strub builtins are inlined. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "\[(\]call\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O3.c b/gcc/testsuite/c-c++-common/strub-O3.c new file mode 100644 index 0000000000000..33ee465e51cb6 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O3.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fstrub=strict -fdump-rtl-expand" } */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "strub_leave" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-O3fni.c b/gcc/testsuite/c-c++-common/strub-O3fni.c new file mode 100644 index 0000000000000..2936f82079e18 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-O3fni.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fstrub=strict -fdump-rtl-expand -fno-inline" } */ + +/* With -fno-inline, none of the strub builtins are inlined. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "\[(\]call\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-Og.c b/gcc/testsuite/c-c++-common/strub-Og.c new file mode 100644 index 0000000000000..479746e57d87e --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-Og.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-Og -fstrub=strict -fdump-rtl-expand" } */ + +/* At -Og, without -fno-inline, we fully expand enter, but neither update nor + leave. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "\[(\]call\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-Os.c b/gcc/testsuite/c-c++-common/strub-Os.c new file mode 100644 index 0000000000000..2241d4ea07f27 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-Os.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -fstrub=strict -fdump-rtl-expand" } */ + +/* At -Os, without -fno-inline, we fully expand enter, and also update. The + expanded update might be larger than a call proper, but argument saving and + restoring required by the call will most often make it larger. The leave + call is left untouched. */ + +int __attribute__ ((__strub__)) var; + +int f() { + return var; +} + +/* { dg-final { scan-rtl-dump-not "strub_enter" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "strub_update" "expand" } } */ +/* { dg-final { scan-rtl-dump "strub_leave" "expand" } } */ +/* { dg-final { scan-rtl-dump-not "\[(\]call\[^\n\]*strub_leave.*\n\[(\]code_label" "expand" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-all1.c b/gcc/testsuite/c-c++-common/strub-all1.c new file mode 100644 index 0000000000000..a322bcc5da606 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-all1.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=all -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* h becomes STRUB_CALLABLE, rather than STRUB_INLINABLE, because of the + strub-enabling -fstrub flag, and gets inlined before pass_ipa_strub. */ +static inline void +__attribute__ ((__always_inline__)) +h() { +} + +/* g becomes STRUB_AT_CALLS, because of the flag. */ +static inline void +g() { + h(); +} + +/* f becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]callable\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]at-calls\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]internal\[)\]" 1 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]at-calls\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapped\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapper\[)\]" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-all2.c b/gcc/testsuite/c-c++-common/strub-all2.c new file mode 100644 index 0000000000000..db60026d0e080 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-all2.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=all -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* g becomes STRUB_INTERNAL, because of the flag. Without inline, force_output + is set for static non-inline functions when not optimizing, and that keeps + only_called_directly_p from returning true, which makes STRUB_AT_CALLS + non-viable. */ +static void +g() { +} + +/* f becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]internal\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapped\[)\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapper\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-apply1.c b/gcc/testsuite/c-c++-common/strub-apply1.c new file mode 100644 index 0000000000000..2f462adc1efe0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-apply1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict" } */ + +void __attribute__ ((__strub__ ("callable"))) +apply_function (void *args) +{ + __builtin_apply (0, args, 0); +} + +void __attribute__ ((__strub__ ("internal"))) +apply_args (int i, int j, double d) +{ + void *args = __builtin_apply_args (); + apply_function (args); +} diff --git a/gcc/testsuite/c-c++-common/strub-apply2.c b/gcc/testsuite/c-c++-common/strub-apply2.c new file mode 100644 index 0000000000000..a5d7551f5da5c --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-apply2.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict" } */ + +extern void __attribute__ ((__strub__)) +apply_function (void *args); + +void __attribute__ ((__strub__)) +apply_args (int i, int j, double d) /* { dg-error "selected" } */ +{ + void *args = __builtin_apply_args (); /* { dg-message "does not support" } */ + apply_function (args); +} diff --git a/gcc/testsuite/c-c++-common/strub-apply3.c b/gcc/testsuite/c-c++-common/strub-apply3.c new file mode 100644 index 0000000000000..64422a0d1e880 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-apply3.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict" } */ + +void __attribute__ ((__strub__)) +apply_function (void *args) +{ + __builtin_apply (0, args, 0); /* { dg-error "in .strub. context" } */ +} diff --git a/gcc/testsuite/c-c++-common/strub-apply4.c b/gcc/testsuite/c-c++-common/strub-apply4.c new file mode 100644 index 0000000000000..15ffaa031b899 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-apply4.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fstrub=strict -fdump-ipa-strubm" } */ + +/* Check that implicit enabling of strub mode selects internal strub when the + function uses __builtin_apply_args, that prevents the optimization to + at-calls mode. */ + +int __attribute__ ((__strub__)) var; + +static inline void +apply_args (int i, int j, double d) +{ + var++; + __builtin_apply_args (); +} + +void f() { + apply_args (1, 2, 3); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]internal\[)\]" 1 "strubm" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-at-calls1.c b/gcc/testsuite/c-c++-common/strub-at-calls1.c new file mode 100644 index 0000000000000..b70843b4215a4 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-at-calls1.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=at-calls -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* h becomes STRUB_CALLABLE, rather than STRUB_INLINABLE, because of the + strub-enabling -fstrub flag, and gets inlined before pass_ipa_strub. */ +static inline void +__attribute__ ((__always_inline__)) +h() { +} + +/* g becomes STRUB_AT_CALLS, because of the flag. */ +static inline void +g() { + h(); +} + +/* f does NOT become STRUB_AT_CALLS because it is visible; it becomes + STRUB_CALLABLE. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]at-calls\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]callable\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]at-calls\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]callable\[)\]" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-at-calls2.c b/gcc/testsuite/c-c++-common/strub-at-calls2.c new file mode 100644 index 0000000000000..97a3988a6b922 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-at-calls2.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=at-calls -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* g does NOT become STRUB_AT_CALLS because it's not viable. Without inline, + force_output is set for static non-inline functions when not optimizing, and + that keeps only_called_directly_p from returning true, which makes + STRUB_AT_CALLS non-viable. It becomes STRUB_CALLABLE instead. */ +static void +g() { +} + +/* f does NOT become STRUB_AT_CALLS because it is visible; it becomes + STRUB_CALLABLE. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]callable\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]callable\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-defer-O1.c b/gcc/testsuite/c-c++-common/strub-defer-O1.c new file mode 100644 index 0000000000000..3d73431b3dcd3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-defer-O1.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=strict -O1" } */ + +/* Check that a strub function called by another strub function does NOT defer + the strubbing to its caller at -O1. */ + +#include "strub-defer-O2.c" diff --git a/gcc/testsuite/c-c++-common/strub-defer-O2.c b/gcc/testsuite/c-c++-common/strub-defer-O2.c new file mode 100644 index 0000000000000..fddf3c745e7e6 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-defer-O2.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=strict -O2" } */ + +/* Check that a strub function called by another strub function does NOT defer + the strubbing to its caller at -O2. */ + +#define EXPECT_DEFERRAL ! +#include "strub-defer-O3.c" diff --git a/gcc/testsuite/c-c++-common/strub-defer-O3.c b/gcc/testsuite/c-c++-common/strub-defer-O3.c new file mode 100644 index 0000000000000..5974e72efed46 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-defer-O3.c @@ -0,0 +1,97 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=strict -O3" } */ + +/* Check that a strub function called by another strub function defers the + strubbing to its caller at -O3. */ + +#ifndef EXPECT_DEFERRAL +# define EXPECT_DEFERRAL +#endif + +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__, __strub__ ("callable"))) +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__ ((__strub__ ("at-calls"), __noinline__, __noclone__)) +char * +at_calls () +{ + return leak_string (); +} + +static __attribute__ ((__strub__ ("at-calls"))) +char * +deferred_at_calls () +{ + char *ret = at_calls (); + if (EXPECT_DEFERRAL !look_for_string (ret)) + __builtin_abort (); + return ret; +} + +static __attribute__ ((__strub__ ("internal"))) +char * +deferred_internal () +{ + char *ret = at_calls (); + if (EXPECT_DEFERRAL !look_for_string (ret)) + __builtin_abort (); + return ret; +} + +int main () +{ + if (look_for_string (deferred_at_calls ())) + __builtin_abort (); + if (look_for_string (deferred_internal ())) + __builtin_abort (); + __builtin_exit (0); +} diff --git a/gcc/testsuite/c-c++-common/strub-defer-Os.c b/gcc/testsuite/c-c++-common/strub-defer-Os.c new file mode 100644 index 0000000000000..fbaf85fe0fafe --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-defer-Os.c @@ -0,0 +1,7 @@ +/* { dg-do run } */ +/* { dg-options "-fstrub=strict -Os" } */ + +/* Check that a strub function called by another strub function defers the + strubbing to its caller at -Os. */ + +#include "strub-defer-O3.c" diff --git a/gcc/testsuite/c-c++-common/strub-internal1.c b/gcc/testsuite/c-c++-common/strub-internal1.c new file mode 100644 index 0000000000000..e9d7b7b9ee0a8 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-internal1.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=internal -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* h becomes STRUB_CALLABLE, rather than STRUB_INLINABLE, because of the + strub-enabling -fstrub flag, and gets inlined before pass_ipa_strub. */ +static inline void +__attribute__ ((__always_inline__)) +h() { +} + +/* g becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +static inline void +g() { + h(); +} + +/* f becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]callable\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]internal\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapped\[)\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapper\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-internal2.c b/gcc/testsuite/c-c++-common/strub-internal2.c new file mode 100644 index 0000000000000..8b8e15a51c71c --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-internal2.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=internal -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* g becomes STRUB_INTERNAL, because of the flag. */ +static void +g() { +} + +/* f becomes STRUB_INTERNAL because of the flag, and gets split into + STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + g(); +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]internal\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapped\[)\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapper\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-parms1.c b/gcc/testsuite/c-c++-common/strub-parms1.c new file mode 100644 index 0000000000000..0a4a7539d3489 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-parms1.c @@ -0,0 +1,48 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +#include + +void __attribute__ ((__strub__ ("internal"))) +small_args (int i, long long l, void *p, void **q, double d, char c) +{ +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*small_args\[^ \]*.strub.\[0-9\]* \[(\]int i, long long int l, void \\* p, void \\* \\* q, double d, char c, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " \[^ \]*small_args\[^ \]*.strub.\[0-9\]* \[(\]\[^&\]*&.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ + + +struct large_arg { + int x[128]; +}; + +void __attribute__ ((__strub__ ("internal"))) +large_byref_arg (struct large_arg la) +{ +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*large_byref_arg\[^ \]*.strub.\[0-9\]* \[(\]struct large_arg & la, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " \[^ \]*large_byref_arg\[^ \]*.strub.\[0-9\]* \[(\]&\[^&\]*&.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ + +void __attribute__ ((__strub__ ("internal"))) +std_arg (int i, ...) +{ + va_list vl; + va_start (vl, i); + va_end (vl); +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*std_arg\[^ \]*.strub.\[0-9\]* \[(\]int i, \[^&,\]* &\[^&,\]*.strub.va_list_ptr, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " \[^ \]*std_arg\[^ \]*.strub.\[0-9\]* \[(\]\[^&\]*&.strub.va_list.\[0-9\]*, &.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_start \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_copy \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_end \\(" 2 "strub" } } */ + +void __attribute__ ((__strub__ ("internal"))) +apply_args (int i, int j, double d) +{ + __builtin_apply_args (); +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*apply_args\[^ \]*.strub.\[0-9\]* \[(\]int i, int j, double d, void \\*\[^&,\]*.strub.apply_args, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " \[^ \]*apply_args\[^ \]*.strub.\[0-9\]* \[(\]\[^&\]*.strub.apply_args.\[0-9\]*_\[0-9\]*, &.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-parms2.c b/gcc/testsuite/c-c++-common/strub-parms2.c new file mode 100644 index 0000000000000..147171d96d5a1 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-parms2.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +#include + +void __attribute__ ((__strub__ ("at-calls"))) +small_args (int i, long long l, void *p, void **q, double d, char c) +{ +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*small_args\[^ \]* \[(\]int i, long long int l, void \\* p, void \\* \\* q, double d, char c, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ + + +struct large_arg { + int x[128]; +}; + +void __attribute__ ((__strub__ ("at-calls"))) +large_byref_arg (struct large_arg la) +{ +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*large_byref_arg\[^ \]* \[(\]struct large_arg la, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ + +void __attribute__ ((__strub__ ("at-calls"))) +std_arg (int i, ...) +{ + va_list vl; + va_start (vl, i); + va_end (vl); +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*std_arg\[^ \]* \[(\]int i, void \\* &\[^&,\]*.strub.watermark_ptr\[, .]*\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_start \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-not "va_copy \\(" "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_end \\(" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-parms3.c b/gcc/testsuite/c-c++-common/strub-parms3.c new file mode 100644 index 0000000000000..4e92682895a43 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-parms3.c @@ -0,0 +1,58 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +/* Check that uses of a strub variable implicitly enables internal strub for + publicly-visible functions, and causes the same transformations to their + signatures as those in strub-parms1.c. */ + +#include + +int __attribute__ ((__strub__)) var; + +void +small_args (int i, long long l, void *p, void **q, double d, char c) +{ + var++; +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*small_args\[^ \]*.strub.\[0-9\]* \[(\]int i, long long int l, void \\* p, void \\* \\* q, double d, char c, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " \[^ \]*small_args\[^ \]*.strub.\[0-9\]* \[(\]\[^&\]*&.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ + + +struct large_arg { + int x[128]; +}; + +void +large_byref_arg (struct large_arg la) +{ + var++; +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*large_byref_arg\[^ \]*.strub.\[0-9\]* \[(\]struct large_arg & la, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " \[^ \]*large_byref_arg\[^ \]*.strub.\[0-9\]* \[(\]&\[^&\]*&.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ + +void +std_arg (int i, ...) +{ + va_list vl; + va_start (vl, i); + var++; + va_end (vl); +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*std_arg\[^ \]*.strub.\[0-9\]* \[(\]int i, \[^&,\]* &\[^&,\]*.strub.va_list_ptr, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " \[^ \]*std_arg\[^ \]*.strub.\[0-9\]* \[(\]\[^&\]*&.strub.va_list.\[0-9\]*, &.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_start \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_copy \\(" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "va_end \\(" 2 "strub" } } */ + +void +apply_args (int i, int j, double d) +{ + var++; + __builtin_apply_args (); +} + +/* { dg-final { scan-ipa-dump "\n(void )?\[^ \]*apply_args\[^ \]*.strub.\[0-9\]* \[(\]int i, int j, double d, void \\*\[^&,\]*.strub.apply_args, void \\* &\[^&,\]*.strub.watermark_ptr\[)\]" "strub" } } */ +/* { dg-final { scan-ipa-dump " \[^ \]*apply_args\[^ \]*.strub.\[0-9\]* \[(\]\[^&\]*.strub.apply_args.\[0-9\]*_\[0-9\]*, &.strub.watermark.\[0-9\]*\[)\]" "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-relaxed1.c b/gcc/testsuite/c-c++-common/strub-relaxed1.c new file mode 100644 index 0000000000000..e2f9d8aebca58 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-relaxed1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=relaxed -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* The difference between relaxed and strict in this case is that we accept the + call from one internal-strub function to another. Without the error, + inlining takes place. */ + +#include "strub-strict1.c" + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]inlinable\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]at-calls-opt\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]internal\[)\]" 1 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]at-calls-opt\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapped\[)\]" 1 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapper\[)\]" 1 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-relaxed2.c b/gcc/testsuite/c-c++-common/strub-relaxed2.c new file mode 100644 index 0000000000000..98474435d2e59 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-relaxed2.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=relaxed -fdump-ipa-strubm -fdump-ipa-strub" } */ + +/* The difference between relaxed and strict in this case is that we accept the + call from one internal-strub function to another. */ + +#include "strub-strict2.c" + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]internal\[)\]" 2 "strubm" } } */ + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapped\[)\]" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]wrapper\[)\]" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-short-O0-exc.c b/gcc/testsuite/c-c++-common/strub-short-O0-exc.c new file mode 100644 index 0000000000000..1de15342595e4 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-short-O0-exc.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-O0 -fstrub=strict -fexceptions -fdump-ipa-strub" } */ + +/* Check that the expected strub calls are issued. */ + +#include "torture/strub-callable1.c" + +/* { dg-final { scan-ipa-dump-times "strub_enter" 45 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_update" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_leave" 89 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-short-O0.c b/gcc/testsuite/c-c++-common/strub-short-O0.c new file mode 100644 index 0000000000000..f9209c819004b --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-short-O0.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-O0 -fstrub=strict -fno-exceptions -fdump-ipa-strub" } */ + +/* Check that the expected strub calls are issued. */ + +#include "torture/strub-callable1.c" + +/* { dg-final { scan-ipa-dump-times "strub_enter" 45 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_update" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_leave" 45 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-short-O1.c b/gcc/testsuite/c-c++-common/strub-short-O1.c new file mode 100644 index 0000000000000..bed1dcfb54a45 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-short-O1.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-O1 -fstrub=strict -fno-exceptions -fdump-ipa-strub" } */ + +/* Check that the expected strub calls are issued. */ + +#include "torture/strub-callable1.c" + +/* { dg-final { scan-ipa-dump-times "strub_enter" 45 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_update" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_leave" 45 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-short-O2.c b/gcc/testsuite/c-c++-common/strub-short-O2.c new file mode 100644 index 0000000000000..6bf0071f52b93 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-short-O2.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fstrub=strict -fno-exceptions -fdump-ipa-strub" } */ + +/* Check that the expected strub calls are issued. */ + +#include "torture/strub-callable1.c" + +/* { dg-final { scan-ipa-dump-times "strub_enter" 45 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_update" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_leave" 45 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-short-O3.c b/gcc/testsuite/c-c++-common/strub-short-O3.c new file mode 100644 index 0000000000000..4732f515bf70c --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-short-O3.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fstrub=strict -fno-exceptions -fdump-ipa-strub" } */ + +/* Check that the expected strub calls are issued. At -O3 and -Os, we omit + enter and leave calls within strub contexts, passing on the enclosing + watermark. */ + +#include "torture/strub-callable1.c" + +/* { dg-final { scan-ipa-dump-times "strub_enter" 15 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_update" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_leave" 15 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-short-Os.c b/gcc/testsuite/c-c++-common/strub-short-Os.c new file mode 100644 index 0000000000000..8d6424c479a3a --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-short-Os.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -fstrub=strict -fno-exceptions -fdump-ipa-strub" } */ + +/* Check that the expected strub calls are issued. At -O3 and -Os, we omit + enter and leave calls within strub contexts, passing on the enclosing + watermark. */ + +#include "torture/strub-callable1.c" + +/* { dg-final { scan-ipa-dump-times "strub_enter" 15 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_update" 4 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_leave" 15 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-strict1.c b/gcc/testsuite/c-c++-common/strub-strict1.c new file mode 100644 index 0000000000000..368522442066e --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-strict1.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strubm" } */ + +static int __attribute__ ((__strub__)) var; + +/* h becomes STRUB_INLINABLE, because of the use of the strub variable, + and the always_inline flag. It would get inlined before pass_ipa_strub, if + it weren't for the error. */ +static inline void +__attribute__ ((__always_inline__)) +h() { + var++; +} + +/* g becomes STRUB_AT_CALLS_OPT, because of the use of the strub variable, and + the viability of at-calls strubbing. Though internally a strub context, its + interface is not strub-enabled, so it's not callable from within strub + contexts. */ +static inline void +g() { + var--; + h(); +} + +/* f becomes STRUB_INTERNAL because of the use of the strub variable, and gets + split into STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + var++; + g(); /* { dg-error "calling non-.strub." } */ +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 3 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]inlinable\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]at-calls-opt\[)\]" 1 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]internal\[)\]" 1 "strubm" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-strict2.c b/gcc/testsuite/c-c++-common/strub-strict2.c new file mode 100644 index 0000000000000..b4f2888321821 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-strict2.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strubm" } */ + +static int __attribute__ ((__strub__)) var; + +/* g becomes STRUB_INTERNAL because of the use of the strub variable, and gets + split into STRUB_WRAPPER and STRUB_WRAPPED. It's not STRUB_AT_CALLS_OPT + because force_output is set for static non-inline functions when not + optimizing, and that keeps only_called_directly_p from returning true, which + makes STRUB_AT_CALLS[_OPT] non-viable. */ +static void +g() { + var--; +} + +/* f becomes STRUB_INTERNAL because of the use of the strub variable, and gets + split into STRUB_WRAPPER and STRUB_WRAPPED. */ +void +f() { + var++; + g(); /* { dg-error "calling non-.strub." } */ +} + +/* { dg-final { scan-ipa-dump-times "strub \[(\]" 2 "strubm" } } */ +/* { dg-final { scan-ipa-dump-times "strub \[(\]internal\[)\]" 2 "strubm" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-tail-O1.c b/gcc/testsuite/c-c++-common/strub-tail-O1.c new file mode 100644 index 0000000000000..e48e0610e079b --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-tail-O1.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O1 -fstrub=strict -fno-exceptions -fdump-ipa-strub" } */ + +#include "strub-tail-O2.c" + +/* { dg-final { scan-ipa-dump-times "strub_enter" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_update" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_leave" 2 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-tail-O2.c b/gcc/testsuite/c-c++-common/strub-tail-O2.c new file mode 100644 index 0000000000000..87cda7ab21b16 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-tail-O2.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fstrub=strict -fno-exceptions -fdump-ipa-strub" } */ + +/* Check that the expected strub calls are issued. + Tail calls are short-circuited at -O2+. */ + +int __attribute__ ((__strub__)) +g (int i, int j) { + return g (i, j); +} + +/* { dg-final { scan-ipa-dump-times "strub_enter" 0 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_update" 2 "strub" } } */ +/* { dg-final { scan-ipa-dump-times "strub_leave" 0 "strub" } } */ diff --git a/gcc/testsuite/c-c++-common/strub-var1.c b/gcc/testsuite/c-c++-common/strub-var1.c new file mode 100644 index 0000000000000..eb6250fd39c90 --- /dev/null +++ b/gcc/testsuite/c-c++-common/strub-var1.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ + +int __attribute__ ((strub)) x; +float __attribute__ ((strub)) f; +double __attribute__ ((strub)) d; + +/* The attribute applies to the type of the declaration, i.e., to the pointer + variable p, not to the pointed-to integer. */ +int __attribute__ ((strub)) * +p = &x; /* { dg-message "incompatible|invalid conversion" } */ + +typedef int __attribute__ ((strub)) strub_int; +strub_int *q = &x; /* Now this is compatible. */ + +int __attribute__ ((strub)) +a[2]; /* { dg-warning "does not apply to elements" } */ + +int __attribute__ ((vector_size (4 * sizeof (int)))) + __attribute__ ((strub)) +v; /* { dg-warning "does not apply to elements" } */ + +struct s { + int i, j; +} __attribute__ ((strub)) w; /* { dg-warning "does not apply to fields" } */ From patchwork Fri Jul 29 06:26:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 56414 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 970643857BAB for ; Fri, 29 Jul 2022 06:26:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 970643857BAB DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1659076013; bh=DxSUzm9pniuBT3G5NARu/GYZwV0Gdhop86ueEGMcKR0=; 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=eNjZ0TMl7hUdp1YWcmudMnWRKVZWPD3ZBa7bZduyh/3AgutRsGp044/jtmAwS4gtk KSd5IK9Z50Ob5RIB4YiG3uiLf1XrdQP+cBrKgZxCllvv6RLPt6MQxUBs93+g5vVtlU miNLyBstiDpkxIfJht7qz48mToeVyE81DrOe1yK8= 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 7A91F3852764 for ; Fri, 29 Jul 2022 06:26:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 7A91F3852764 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id EAC581168D2; Fri, 29 Jul 2022 02:26:20 -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 VyfG802psjF0; Fri, 29 Jul 2022 02:26:20 -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 561BA1168CD; Fri, 29 Jul 2022 02:26:20 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 26T6QAP81851979 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 Jul 2022 03:26:10 -0300 To: gcc-patches@gcc.gnu.org Subject: [PATCH v2 04/10] Introduce strub: tests for C++ and Ada Organization: Free thinker, does not speak for AdaCore References: Date: Fri, 29 Jul 2022 03:26:10 -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 * g++.dg/strub-run1.C: New. * g++.dg/torture/strub-init1.C: New. * g++.dg/torture/strub-init2.C: New. * g++.dg/torture/strub-init3.C: New. * gnat.dg/strub_attr.adb, gnat.dg/strub_attr.ads: New. * gnat.dg/strub_ind.adb, gnat.dg/strub_ind.ads: New. diff --git a/gcc/testsuite/g++.dg/strub-run1.C b/gcc/testsuite/g++.dg/strub-run1.C new file mode 100644 index 0000000000000..0d367fb83d09d --- /dev/null +++ b/gcc/testsuite/g++.dg/strub-run1.C @@ -0,0 +1,19 @@ +// { dg-do run } +// { dg-options "-fstrub=internal" } + +// Check that we don't get extra copies. + +struct T { + T &self; + void check () const { if (&self != this) __builtin_abort (); } + T() : self (*this) { check (); } + T(const T& ck) : self (*this) { ck.check (); check (); } + ~T() { check (); } +}; + +T foo (T q) { q.check (); return T(); } +T bar (T p) { p.check (); return foo (p); } + +int main () { + bar (T()).check (); +} diff --git a/gcc/testsuite/g++.dg/torture/strub-init1.C b/gcc/testsuite/g++.dg/torture/strub-init1.C new file mode 100644 index 0000000000000..c226ab10ff651 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/strub-init1.C @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +extern int __attribute__((__strub__)) initializer (); + +int f() { + static int x = initializer (); + return x; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/g++.dg/torture/strub-init2.C b/gcc/testsuite/g++.dg/torture/strub-init2.C new file mode 100644 index 0000000000000..a7911f1fa7212 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/strub-init2.C @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +extern int __attribute__((__strub__)) initializer (); + +static int x = initializer (); + +int f() { + return x; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/g++.dg/torture/strub-init3.C b/gcc/testsuite/g++.dg/torture/strub-init3.C new file mode 100644 index 0000000000000..6ebebcd01e8ea --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/strub-init3.C @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fstrub=strict -fdump-ipa-strub" } */ + +extern int __attribute__((__strub__)) initializer (); + +int f() { + int x = initializer (); + return x; +} + +/* { dg-final { scan-ipa-dump "strub_enter" "strub" } } */ +/* { dg-final { scan-ipa-dump "strub_leave" "strub" } } */ +/* { dg-final { scan-ipa-dump-not "strub_update" "strub" } } */ diff --git a/gcc/testsuite/gnat.dg/strub_access.adb b/gcc/testsuite/gnat.dg/strub_access.adb new file mode 100644 index 0000000000000..29e6996ecf61c --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_access.adb @@ -0,0 +1,21 @@ +-- { dg-do compile } +-- { dg-options "-fstrub=relaxed -fdump-ipa-strubm" } + +-- The main subprogram doesn't read from the automatic variable, but +-- being an automatic variable, its presence should be enough for the +-- procedure to get strub enabled. + +procedure Strub_Access is + type Strub_Int is new Integer; + pragma Machine_Attribute (Strub_Int, "strub"); + + X : aliased Strub_Int := 0; + + function F (P : access Strub_Int) return Strub_Int is (P.all); + +begin + X := F (X'Access); +end Strub_Access; + +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]internal\[)\]\[)\]" 1 "strubm" } } +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]at-calls-opt\[)\]\[)\]" 1 "strubm" } } diff --git a/gcc/testsuite/gnat.dg/strub_access1.adb b/gcc/testsuite/gnat.dg/strub_access1.adb new file mode 100644 index 0000000000000..dae4706016436 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_access1.adb @@ -0,0 +1,16 @@ +-- { dg-do compile } +-- { dg-options "-fstrub=relaxed" } + +-- Check that we reject 'Access of a strub variable whose type does +-- not carry a strub modifier. + +procedure Strub_Access1 is + X : aliased Integer := 0; + pragma Machine_Attribute (X, "strub"); + + function F (P : access Integer) return Integer is (P.all); + +begin + X := F (X'Unchecked_access); -- OK. + X := F (X'Access); -- { dg-error "target access type drops .strub. mode" } +end Strub_Access1; diff --git a/gcc/testsuite/gnat.dg/strub_attr.adb b/gcc/testsuite/gnat.dg/strub_attr.adb new file mode 100644 index 0000000000000..10445d7cf8451 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_attr.adb @@ -0,0 +1,37 @@ +-- { dg-do compile } +-- { dg-options "-fstrub=strict -fdump-ipa-strubm -fdump-ipa-strub" } + +package body Strub_Attr is + E : exception; + + procedure P (X : Integer) is + begin + raise E; + end; + + function F (X : Integer) return Integer is + begin + return X * X; + end; + + function G return Integer is (F (X)); + -- function G return Integer is (FP (X)); + -- Calling G would likely raise an exception, because although FP + -- carries the strub at-calls attribute needed to call F, the + -- attribute is dropped from the type used for the call proper. +end Strub_Attr; + +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]internal\[)\]\[)\]" 2 "strubm" } } +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]at-calls\[)\]\[)\]" 0 "strubm" } } +-- { dg-final { scan-ipa-dump-times "\[(\]strub\[)\]" 1 "strubm" } } + +-- { dg-final { scan-ipa-dump-times "strub.watermark_ptr" 6 "strub" } } +-- We have 1 at-calls subprogram (F) and 2 wrapped (P and G). +-- For each of them, there's one match for the wrapped signature, +-- and one for the update call. + +-- { dg-final { scan-ipa-dump-times "strub.watermark" 27 "strub" } } +-- The 6 matches above, plus: +-- 5*2: wm var decl, enter, call, leave and clobber for each wrapper; +-- 2*1: an extra leave and clobber for the exception paths in the wrappers. +-- 7*1: for the F call in G, including EH path. diff --git a/gcc/testsuite/gnat.dg/strub_attr.ads b/gcc/testsuite/gnat.dg/strub_attr.ads new file mode 100644 index 0000000000000..a94c23bf41833 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_attr.ads @@ -0,0 +1,12 @@ +package Strub_Attr is + procedure P (X : Integer); + pragma Machine_Attribute (P, "strub", "internal"); + + function F (X : Integer) return Integer; + pragma Machine_Attribute (F, "strub"); + + X : Integer := 0; + pragma Machine_Attribute (X, "strub"); + + function G return Integer; +end Strub_Attr; diff --git a/gcc/testsuite/gnat.dg/strub_disp.adb b/gcc/testsuite/gnat.dg/strub_disp.adb new file mode 100644 index 0000000000000..3dbcc4a357cba --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_disp.adb @@ -0,0 +1,64 @@ +-- { dg-do compile } + +procedure Strub_Disp is + package Foo is + type A is tagged null record; + + procedure P (I : Integer; X : A); + pragma Machine_Attribute (P, "strub", "at-calls"); + + function F (X : access A) return Integer; + + type B is new A with null record; + + overriding + procedure P (I : Integer; X : B); -- { dg-error "requires the same .strub. mode" } + + overriding + function F (X : access B) return Integer; + pragma Machine_Attribute (F, "strub", "at-calls"); -- { dg-error "requires the same .strub. mode" } + + end Foo; + + package body Foo is + procedure P (I : Integer; X : A) is + begin + null; + end; + + function F (X : access A) return Integer is (0); + + overriding + procedure P (I : Integer; X : B) is + begin + P (I, A (X)); + end; + + overriding + function F (X : access B) return Integer is (1); + end Foo; + + use Foo; + + procedure Q (X : A'Class) is + begin + P (-1, X); + end; + + XA : aliased A; + XB : aliased B; + I : Integer := 0; + XC : access A'Class; +begin + Q (XA); + Q (XB); + + I := I + F (XA'Access); + I := I + F (XB'Access); + + XC := XA'Access; + I := I + F (XC); + + XC := XB'Access; + I := I + F (XC); +end Strub_Disp; diff --git a/gcc/testsuite/gnat.dg/strub_disp1.adb b/gcc/testsuite/gnat.dg/strub_disp1.adb new file mode 100644 index 0000000000000..09756a74b7d81 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_disp1.adb @@ -0,0 +1,79 @@ +-- { dg-do compile } +-- { dg-options "-fdump-ipa-strub" } + +-- Check that at-calls dispatching calls are transformed. + +procedure Strub_Disp1 is + package Foo is + type A is tagged null record; + + procedure P (I : Integer; X : A); + pragma Machine_Attribute (P, "strub", "at-calls"); + + function F (X : access A) return Integer; + pragma Machine_Attribute (F, "strub", "at-calls"); + + type B is new A with null record; + + overriding + procedure P (I : Integer; X : B); + pragma Machine_Attribute (P, "strub", "at-calls"); + + overriding + function F (X : access B) return Integer; + pragma Machine_Attribute (F, "strub", "at-calls"); + + end Foo; + + package body Foo is + procedure P (I : Integer; X : A) is + begin + null; + end; + + function F (X : access A) return Integer is (0); + + overriding + procedure P (I : Integer; X : B) is + begin + P (I, A (X)); -- strub-at-calls non-dispatching call + end; + + overriding + function F (X : access B) return Integer is (1); + end Foo; + + use Foo; + + procedure Q (X : A'Class) is + begin + P (-1, X); -- strub-at-calls dispatching call. + end; + + XA : aliased A; + XB : aliased B; + I : Integer := 0; + XC : access A'Class; +begin + Q (XA); + Q (XB); + + I := I + F (XA'Access); -- strub-at-calls non-dispatching call + I := I + F (XB'Access); -- strub-at-calls non-dispatching call + + XC := XA'Access; + I := I + F (XC); -- strub-at-calls dispatching call. + + XC := XB'Access; + I := I + F (XC); -- strub-at-calls dispatching call. +end Strub_Disp1; + +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]at-calls\[)\]\[)\]" 4 "strub" } } + +-- Count the strub-at-calls non-dispatching calls +-- (+ 2 each, for the matching prototypes) +-- { dg-final { scan-ipa-dump-times "foo\.p \[(\]\[^\n\]*watermark" 3 "strub" } } +-- { dg-final { scan-ipa-dump-times "foo\.f \[(\]\[^\n\]*watermark" 4 "strub" } } + +-- Count the strub-at-calls dispatching calls. +-- { dg-final { scan-ipa-dump-times "_\[0-9\]* \[(\]\[^\n\]*watermark" 3 "strub" } } diff --git a/gcc/testsuite/gnat.dg/strub_ind.adb b/gcc/testsuite/gnat.dg/strub_ind.adb new file mode 100644 index 0000000000000..da56acaa957d2 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_ind.adb @@ -0,0 +1,33 @@ +-- { dg-do compile } +-- { dg-options "-fstrub=strict" } + +-- This is essentially the same test as strub_attr.adb, +-- but applying attributes to access types as well. +-- That doesn't quite work yet, so we get an error we shouldn't get. + +package body Strub_Ind is + E : exception; + + function G return Integer; + + procedure P (X : Integer) is + begin + raise E; + end; + + function F (X : Integer) return Integer is + begin + return X * X; + end; + + function G return Integer is (FP (X)); + + type GT is access function return Integer; + + type GT_SAC is access function return Integer; + pragma Machine_Attribute (GT_SAC, "strub", "at-calls"); + + GP : GT_SAC := GT_SAC (GT'(G'Access)); -- { dg-error "incompatible" } + -- pragma Machine_Attribute (GP, "strub", "at-calls"); + +end Strub_Ind; diff --git a/gcc/testsuite/gnat.dg/strub_ind.ads b/gcc/testsuite/gnat.dg/strub_ind.ads new file mode 100644 index 0000000000000..99a65fc24b1ec --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_ind.ads @@ -0,0 +1,17 @@ +package Strub_Ind is + procedure P (X : Integer); + pragma Machine_Attribute (P, "strub", "internal"); + + function F (X : Integer) return Integer; + pragma Machine_Attribute (F, "strub"); + + X : Integer := 0; + pragma Machine_Attribute (X, "strub"); + + type FT is access function (X : Integer) return Integer; + pragma Machine_Attribute (FT, "strub", "at-calls"); + + FP : FT := F'Access; + -- pragma Machine_Attribute (FP, "strub", "at-calls"); -- not needed + +end Strub_Ind; diff --git a/gcc/testsuite/gnat.dg/strub_ind1.adb b/gcc/testsuite/gnat.dg/strub_ind1.adb new file mode 100644 index 0000000000000..825e395e6819c --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_ind1.adb @@ -0,0 +1,41 @@ +-- { dg-do compile } +-- { dg-options "-fstrub=strict -fdump-ipa-strubm" } + +-- This is essentially the same test as strub_attr.adb, +-- but with an explicit conversion. + +package body Strub_Ind1 is + E : exception; + + type Strub_Int is New Integer; + pragma Machine_Attribute (Strub_Int, "strub"); + + function G return Integer; + pragma Machine_Attribute (G, "strub", "disabled"); + + procedure P (X : Integer) is + begin + raise E; + end; + + function G return Integer is (FP (X)); + + type GT is access function return Integer; + pragma Machine_Attribute (GT, "strub", "disabled"); + + type GT_SC is access function return Integer; + pragma Machine_Attribute (GT_SC, "strub", "callable"); + + GP : GT_SC := GT_SC (GT'(G'Access)); + -- pragma Machine_Attribute (GP, "strub", "callable"); -- not needed. + + function F (X : Integer) return Integer is + begin + return X * GP.all; + end; + +end Strub_Ind1; + +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]disabled\[)\]\[)\]" 1 "strubm" } } +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]internal\[)\]\[)\]" 1 "strubm" } } +-- { dg-final { scan-ipa-dump-times "\[(\]strub\[)\]" 1 "strubm" } } diff --git a/gcc/testsuite/gnat.dg/strub_ind1.ads b/gcc/testsuite/gnat.dg/strub_ind1.ads new file mode 100644 index 0000000000000..d3f1273b3a6b9 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_ind1.ads @@ -0,0 +1,17 @@ +package Strub_Ind1 is + procedure P (X : Integer); + pragma Machine_Attribute (P, "strub", "internal"); + + function F (X : Integer) return Integer; + pragma Machine_Attribute (F, "strub"); + + X : aliased Integer := 0; + pragma Machine_Attribute (X, "strub"); + + type FT is access function (X : Integer) return Integer; + pragma Machine_Attribute (FT, "strub", "at-calls"); + + FP : FT := F'Access; + pragma Machine_Attribute (FP, "strub", "at-calls"); + +end Strub_Ind1; diff --git a/gcc/testsuite/gnat.dg/strub_ind2.adb b/gcc/testsuite/gnat.dg/strub_ind2.adb new file mode 100644 index 0000000000000..e918b39263117 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_ind2.adb @@ -0,0 +1,34 @@ +-- { dg-do compile } +-- { dg-options "-fstrub=strict" } + +-- This is essentially the same test as strub_attr.adb, +-- but with an explicit conversion. + +package body Strub_Ind2 is + E : exception; + + function G return Integer; + pragma Machine_Attribute (G, "strub", "callable"); + + procedure P (X : Integer) is + begin + raise E; + end; + + function G return Integer is (FP (X)); + + type GT is access function return Integer; + pragma Machine_Attribute (GT, "strub", "callable"); + + type GT_SD is access function return Integer; + pragma Machine_Attribute (GT_SD, "strub", "disabled"); + + GP : GT_SD := GT_SD (GT'(G'Access)); + -- pragma Machine_Attribute (GP, "strub", "disabled"); -- not needed. + + function F (X : Integer) return Integer is + begin + return X * GP.all; -- { dg-error "using non-.strub. type" } + end; + +end Strub_Ind2; diff --git a/gcc/testsuite/gnat.dg/strub_ind2.ads b/gcc/testsuite/gnat.dg/strub_ind2.ads new file mode 100644 index 0000000000000..e13865ec49c38 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_ind2.ads @@ -0,0 +1,17 @@ +package Strub_Ind2 is + procedure P (X : Integer); + pragma Machine_Attribute (P, "strub", "internal"); + + function F (X : Integer) return Integer; + pragma Machine_Attribute (F, "strub"); + + X : Integer := 0; + pragma Machine_Attribute (X, "strub"); + + type FT is access function (X : Integer) return Integer; + pragma Machine_Attribute (FT, "strub", "at-calls"); + + FP : FT := F'Access; + pragma Machine_Attribute (FP, "strub", "at-calls"); + +end Strub_Ind2; diff --git a/gcc/testsuite/gnat.dg/strub_intf.adb b/gcc/testsuite/gnat.dg/strub_intf.adb new file mode 100644 index 0000000000000..728b85572b719 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_intf.adb @@ -0,0 +1,93 @@ +-- { dg-do compile } + +-- Check that strub mode mismatches between overrider and overridden +-- subprograms are reported. + +procedure Strub_Intf is + package Foo is + type TP is interface; + procedure P (I : Integer; X : TP) is abstract; + pragma Machine_Attribute (P, "strub", "at-calls"); -- { dg-error "requires the same .strub. mode" } + + type TF is interface; + function F (X : access TF) return Integer is abstract; + + type TX is interface; + procedure P (I : Integer; X : TX) is abstract; + + type TI is interface and TP and TF and TX; + -- When we freeze TI, we detect the mismatch between the + -- inherited P and another parent's P. Because TP appears + -- before TX, we inherit P from TP, and report the mismatch at + -- the pragma inherited from TP against TX's P. In contrast, + -- when we freeze TII below, since TX appears before TP, we + -- report the error at the line in which the inherited + -- subprogram is synthesized, namely the line below, against + -- the line of the pragma. + + type TII is interface and TX and TP and TF; -- { dg-error "requires the same .strub. mode" } + + function F (X : access TI) return Integer is abstract; + pragma Machine_Attribute (F, "strub", "at-calls"); -- { dg-error "requires the same .strub. mode" } + + type A is new TI with null record; + + procedure P (I : Integer; X : A); + pragma Machine_Attribute (P, "strub", "at-calls"); -- { dg-error "requires the same .strub. mode" } + + function F (X : access A) return Integer; -- { dg-error "requires the same .strub. mode" } + + type B is new TI with null record; + + overriding + procedure P (I : Integer; X : B); -- { dg-error "requires the same .strub. mode" } + + overriding + function F (X : access B) return Integer; + pragma Machine_Attribute (F, "strub", "at-calls"); -- { dg-error "requires the same .strub. mode" } + + end Foo; + + package body Foo is + procedure P (I : Integer; X : A) is + begin + null; + end; + + function F (X : access A) return Integer is (0); + + overriding + procedure P (I : Integer; X : B) is + begin + P (I, A (X)); + end; + + overriding + function F (X : access B) return Integer is (1); + + end Foo; + + use Foo; + + procedure Q (X : TX'Class) is + begin + P (-1, X); + end; + + XA : aliased A; + XB : aliased B; + I : Integer := 0; + XC : access TI'Class; +begin + Q (XA); + Q (XB); + + I := I + F (XA'Access); + I := I + F (XB'Access); + + XC := XA'Access; + I := I + F (XC); + + XC := XB'Access; + I := I + F (XC); +end Strub_Intf; diff --git a/gcc/testsuite/gnat.dg/strub_intf1.adb b/gcc/testsuite/gnat.dg/strub_intf1.adb new file mode 100644 index 0000000000000..aa68fcd2c0b0e --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_intf1.adb @@ -0,0 +1,86 @@ +-- { dg-do compile } +-- { dg-options "-fdump-ipa-strub" } + +-- Check that at-calls dispatching calls to interfaces are transformed. + +procedure Strub_Intf1 is + package Foo is + type TX is Interface; + procedure P (I : Integer; X : TX) is abstract; + pragma Machine_Attribute (P, "strub", "at-calls"); + function F (X : access TX) return Integer is abstract; + pragma Machine_Attribute (F, "strub", "at-calls"); + + type A is new TX with null record; + + procedure P (I : Integer; X : A); + pragma Machine_Attribute (P, "strub", "at-calls"); + + function F (X : access A) return Integer; + pragma Machine_Attribute (F, "strub", "at-calls"); + + type B is new TX with null record; + + overriding + procedure P (I : Integer; X : B); + pragma Machine_Attribute (P, "strub", "at-calls"); + + overriding + function F (X : access B) return Integer; + pragma Machine_Attribute (F, "strub", "at-calls"); + + end Foo; + + package body Foo is + procedure P (I : Integer; X : A) is + begin + null; + end; + + function F (X : access A) return Integer is (0); + + overriding + procedure P (I : Integer; X : B) is + begin + P (I, A (X)); + end; + + overriding + function F (X : access B) return Integer is (1); + + end Foo; + + use Foo; + + procedure Q (X : TX'Class) is + begin + P (-1, X); + end; + + XA : aliased A; + XB : aliased B; + I : Integer := 0; + XC : access TX'Class; +begin + Q (XA); + Q (XB); + + I := I + F (XA'Access); + I := I + F (XB'Access); + + XC := XA'Access; + I := I + F (XC); + + XC := XB'Access; + I := I + F (XC); +end Strub_Intf1; + +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]at-calls\[)\]\[)\]" 4 "strub" } } + +-- Count the strub-at-calls non-dispatching calls +-- (+ 2 each, for the matching prototypes) +-- { dg-final { scan-ipa-dump-times "foo\.p \[(\]\[^\n\]*watermark" 3 "strub" } } +-- { dg-final { scan-ipa-dump-times "foo\.f \[(\]\[^\n\]*watermark" 4 "strub" } } + +-- Count the strub-at-calls dispatching calls. +-- { dg-final { scan-ipa-dump-times "_\[0-9\]* \[(\]\[^\n\]*watermark" 3 "strub" } } diff --git a/gcc/testsuite/gnat.dg/strub_intf2.adb b/gcc/testsuite/gnat.dg/strub_intf2.adb new file mode 100644 index 0000000000000..e8880dbc43730 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_intf2.adb @@ -0,0 +1,55 @@ +-- { dg-do compile } + +-- Check that strub mode mismatches between overrider and overridden +-- subprograms are reported even when the overriders for an +-- interface's subprograms are inherited from a type that is not a +-- descendent of the interface. + +procedure Strub_Intf2 is + package Foo is + type A is tagged null record; + + procedure P (I : Integer; X : A); + pragma Machine_Attribute (P, "strub", "at-calls"); -- { dg-error "requires the same .strub. mode" } + + function F (X : access A) return Integer; + + type TX is Interface; + + procedure P (I : Integer; X : TX) is abstract; + + function F (X : access TX) return Integer is abstract; + pragma Machine_Attribute (F, "strub", "at-calls"); + + type B is new A and TX with null record; -- { dg-error "requires the same .strub. mode" } + + end Foo; + + package body Foo is + procedure P (I : Integer; X : A) is + begin + null; + end; + + function F (X : access A) return Integer is (0); + + end Foo; + + use Foo; + + procedure Q (X : TX'Class) is + begin + P (-1, X); + end; + + XB : aliased B; + I : Integer := 0; + XC : access TX'Class; +begin + Q (XB); + + I := I + F (XB'Access); + + XC := XB'Access; + I := I + F (XC); +end Strub_Intf2; diff --git a/gcc/testsuite/gnat.dg/strub_renm.adb b/gcc/testsuite/gnat.dg/strub_renm.adb new file mode 100644 index 0000000000000..217367e712d82 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_renm.adb @@ -0,0 +1,21 @@ +-- { dg-do compile } + +procedure Strub_Renm is + procedure P (X : Integer); + pragma Machine_Attribute (P, "strub", "at-calls"); + + function F return Integer; + pragma Machine_Attribute (F, "strub", "internal"); + + procedure Q (X : Integer) renames P; -- { dg-error "requires the same .strub. mode" } + + function G return Integer renames F; + pragma Machine_Attribute (G, "strub", "callable"); -- { dg-error "requires the same .strub. mode" } + + procedure P (X : Integer) is null; + function F return Integer is (0); + +begin + P (F); + Q (G); +end Strub_Renm; diff --git a/gcc/testsuite/gnat.dg/strub_renm1.adb b/gcc/testsuite/gnat.dg/strub_renm1.adb new file mode 100644 index 0000000000000..a11adbfb5a9d6 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_renm1.adb @@ -0,0 +1,32 @@ +-- { dg-do compile } +-- { dg-options "-fstrub=relaxed -fdump-ipa-strub" } + +procedure Strub_Renm1 is + V : Integer := 0; + pragma Machine_Attribute (V, "strub"); + + procedure P (X : Integer); + pragma Machine_Attribute (P, "strub", "at-calls"); + + function F return Integer; + + procedure Q (X : Integer) renames P; + pragma Machine_Attribute (Q, "strub", "at-calls"); + + function G return Integer renames F; + pragma Machine_Attribute (G, "strub", "internal"); + + procedure P (X : Integer) is null; + function F return Integer is (0); + +begin + P (F); + Q (G); +end Strub_Renm1; + +-- This is for P; Q is an alias. +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]at-calls\[)\]\[)\]" 1 "strub" } } + +-- This is *not* for G, but for Strub_Renm1. +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]wrapped\[)\]\[)\]" 1 "strub" } } +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]wrapper\[)\]\[)\]" 1 "strub" } } diff --git a/gcc/testsuite/gnat.dg/strub_renm2.adb b/gcc/testsuite/gnat.dg/strub_renm2.adb new file mode 100644 index 0000000000000..c488c20826fdb --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_renm2.adb @@ -0,0 +1,32 @@ +-- { dg-do compile } +-- { dg-options "-fstrub=strict -fdump-ipa-strub" } + +procedure Strub_Renm2 is + V : Integer := 0; + pragma Machine_Attribute (V, "strub"); + + procedure P (X : Integer); + pragma Machine_Attribute (P, "strub", "at-calls"); + + function F return Integer; + + procedure Q (X : Integer) renames P; + pragma Machine_Attribute (Q, "strub", "at-calls"); + + type T is access function return Integer; + + type TC is access function return Integer; + pragma Machine_Attribute (TC, "strub", "callable"); + + FCptr : constant TC := TC (T'(F'Access)); + + function G return Integer renames FCptr.all; + pragma Machine_Attribute (G, "strub", "callable"); + + procedure P (X : Integer) is null; + function F return Integer is (0); + +begin + P (F); -- { dg-error "calling non-.strub." } + Q (G); -- ok, G is callable. +end Strub_Renm2; diff --git a/gcc/testsuite/gnat.dg/strub_var.adb b/gcc/testsuite/gnat.dg/strub_var.adb new file mode 100644 index 0000000000000..3d158de28031f --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_var.adb @@ -0,0 +1,16 @@ +-- { dg-do compile } +-- { dg-options "-fstrub=strict -fdump-ipa-strubm" } + +-- We don't read from the automatic variable, but being an automatic +-- variable, its presence should be enough for the procedure to get +-- strub enabled. + +with Strub_Attr; +procedure Strub_Var is + X : Integer := 0; + pragma Machine_Attribute (X, "strub"); +begin + X := Strub_Attr.F (0); +end Strub_Var; + +-- { dg-final { scan-ipa-dump-times "\[(\]strub \[(\]internal\[)\]\[)\]" 1 "strubm" } } diff --git a/gcc/testsuite/gnat.dg/strub_var1.adb b/gcc/testsuite/gnat.dg/strub_var1.adb new file mode 100644 index 0000000000000..6a504e09198b6 --- /dev/null +++ b/gcc/testsuite/gnat.dg/strub_var1.adb @@ -0,0 +1,20 @@ +-- { dg-do compile } + +with Strub_Attr; +procedure Strub_Var1 is + type TA -- { dg-warning "does not apply to elements" } + is array (1..2) of Integer; + pragma Machine_Attribute (TA, "strub"); + + A : TA := (0, 0); -- { dg-warning "does not apply to elements" } + + type TR is record -- { dg-warning "does not apply to fields" } + M, N : Integer; + end record; + pragma Machine_Attribute (TR, "strub"); + + R : TR := (0, 0); + +begin + A(2) := Strub_Attr.F (A(1)); +end Strub_Var1; From patchwork Fri Jul 29 06:26:38 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 56415 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 0BA153851425 for ; Fri, 29 Jul 2022 06:27:20 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0BA153851425 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1659076040; bh=BmagmV0ZOADVnFEEywd4M4gLcfO8IHSGG7xYzo6NaYo=; 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=j5UTPUfWIyf5Z18MXClQpuGnRcUMtRkmGqX+NUY7wNuC9rNE+r6J5IyxUYCabdTMm jpjAVUGfA2r3ZcHDlcmELW0+36VTa5Fef/HcRlxoRyvIPk6lVAfRds00KphFY0nOhp SYpI1cK6x3ZRYiT7oPvPr84Olz9XjIraRGuNfCLc= 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 AF26638515CE for ; Fri, 29 Jul 2022 06:26:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org AF26638515CE Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 263771168D2; Fri, 29 Jul 2022 02:26:48 -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 d578qdvC5YYz; Fri, 29 Jul 2022 02:26:48 -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 B22C81168CD; Fri, 29 Jul 2022 02:26:47 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 26T6Qc971851989 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 Jul 2022 03:26:38 -0300 To: gcc-patches@gcc.gnu.org Subject: [PATCH v2 05/10] Introduce strub: builtins and runtime Organization: Free thinker, does not speak for AdaCore References: Date: Fri, 29 Jul 2022 03:26:38 -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, KAM_SHORT, 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/ChangeLog * builtins.def (BUILT_IN_STACK_ADDRESS): New. (BUILT_IN___STRUB_ENTER): New. (BUILT_IN___STRUB_UPDATE): New. (BUILT_IN___STRUB_LEAVE): New. * builtins.cc: Include ipa-strub.h. (STACK_STOPS, STACK_UNSIGNED): Define. (expand_builtin_stack_address): New. (expand_builtin_strub_enter): New. (expand_builtin_strub_update): New. (expand_builtin_strub_leave): New. (expand_bulitin): Call them. for libgcc/ChangeLog * Makefile.in (LIB2ADD): Add strub.c. * libgcc2.h (__strub_enter, __strub_update, __strub_leave): Declare. * strub.c: New. diff --git a/gcc/builtins.cc b/gcc/builtins.cc index b08b4365da36b..656186c308997 100644 --- a/gcc/builtins.cc +++ b/gcc/builtins.cc @@ -71,6 +71,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-fold.h" #include "intl.h" #include "file-prefix-map.h" /* remap_macro_filename() */ +#include "ipa-strub.h" /* strub_watermark_parm() */ #include "gomp-constants.h" #include "omp-general.h" #include "tree-dfa.h" @@ -151,6 +152,7 @@ static rtx expand_builtin_strnlen (tree, rtx, machine_mode); static rtx expand_builtin_alloca (tree); static rtx expand_builtin_unop (machine_mode, tree, rtx, rtx, optab); static rtx expand_builtin_frame_address (tree, tree); +static rtx expand_builtin_stack_address (); static tree stabilize_va_list_loc (location_t, tree, int); static rtx expand_builtin_expect (tree, rtx); static rtx expand_builtin_expect_with_probability (tree, rtx); @@ -4968,6 +4970,256 @@ expand_builtin_frame_address (tree fndecl, tree exp) } } +#ifndef STACK_GROWS_DOWNWARD +# define STACK_TOPS GT +#else +# define STACK_TOPS LT +#endif + +#ifdef POINTERS_EXTEND_UNSIGNED +# define STACK_UNSIGNED POINTERS_EXTEND_UNSIGNED +#else +# define STACK_UNSIGNED true +#endif + +/* Expand a call to builtin function __builtin_stack_address. */ + +static rtx +expand_builtin_stack_address () +{ + return convert_to_mode (ptr_mode, copy_to_reg (stack_pointer_rtx), + STACK_UNSIGNED); +} + +/* Expand a call to builtin function __builtin_strub_enter. */ + +static rtx +expand_builtin_strub_enter (tree exp) +{ + if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE)) + return NULL_RTX; + + if (optimize < 1 || flag_no_inline) + return NULL_RTX; + + rtx stktop = NULL_RTX; + +#if 1 || defined RED_ZONE_SIZE + if (tree wmptr = (optimize + ? strub_watermark_parm (current_function_decl) + : NULL_TREE)) + { + tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); + tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr, + build_int_cst (TREE_TYPE (wmptr), 0)); + rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); + stktop = force_reg (ptr_mode, wmark); + } +#endif + + if (!stktop) + stktop = expand_builtin_stack_address (); + + tree wmptr = CALL_EXPR_ARG (exp, 0); + tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); + tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr, + build_int_cst (TREE_TYPE (wmptr), 0)); + rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); + + emit_move_insn (wmark, stktop); + + return const0_rtx; +} + +/* Expand a call to builtin function __builtin_strub_update. */ + +static rtx +expand_builtin_strub_update (tree exp) +{ + if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE)) + return NULL_RTX; + + if (optimize < 2 || flag_no_inline) + return NULL_RTX; + + rtx stktop = expand_builtin_stack_address (); + +#ifdef RED_ZONE_SIZE + /* Here's how the strub enter, update and leave functions deal with red zones. + + If it weren't for red zones, update, called from within a strub context, + would bump the watermark to the top of the stack. Enter and leave, running + in the caller, would use the caller's top of stack address both to + initialize the watermark passed to the callee, and to start strubbing the + stack afterwards. + + Ideally, we'd update the watermark so as to cover the used amount of red + zone, and strub starting at the caller's other end of the (presumably + unused) red zone. Normally, only leaf functions use the red zone, but at + this point we can't tell whether a function is a leaf, nor can we tell how + much of the red zone it uses. Furthermore, some strub contexts may have + been inlined so that update and leave are called from the same stack frame, + and the strub builtins may all have been inlined, turning a strub function + into a leaf. + + So cleaning the range from the caller's stack pointer (one end of the red + zone) to the (potentially inlined) callee's (other end of the) red zone + could scribble over the caller's own red zone. + + We avoid this possibility by arranging for callers that are strub contexts + to use their own watermark as the strub starting point. So, if A calls B, + and B calls C, B will tell A to strub up to the end of B's red zone, and + will strub itself only the part of C's stack frame and red zone that + doesn't overlap with B's. With that, we don't need to know who's leaf and + who isn't: inlined calls will shrink their strub window to zero, each + remaining call will strub some portion of the stack, and eventually the + strub context will return to a caller that isn't a strub context itself, + that will therefore use its own stack pointer as the strub starting point. + It's not a leaf, because strub contexts can't be inlined into non-strub + contexts, so it doesn't use the red zone, and it will therefore correctly + strub up the callee's stack frame up to the end of the callee's red zone. + Neat! */ + if (true /* (flags_from_decl_or_type (current_function_decl) & ECF_LEAF) */) + { + poly_int64 red_zone_size = RED_ZONE_SIZE; +#if STACK_GROWS_DOWNWARD + red_zone_size = -red_zone_size; +#endif + stktop = plus_constant (ptr_mode, stktop, red_zone_size); + stktop = force_reg (ptr_mode, stktop); + } +#endif + + tree wmptr = CALL_EXPR_ARG (exp, 0); + tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); + tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr, + build_int_cst (TREE_TYPE (wmptr), 0)); + rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); + + rtx wmarkr = force_reg (ptr_mode, wmark); + + rtx_code_label *lab = gen_label_rtx (); + do_compare_rtx_and_jump (stktop, wmarkr, STACK_TOPS, STACK_UNSIGNED, + ptr_mode, NULL_RTX, lab, NULL, + profile_probability::very_likely ()); + emit_move_insn (wmark, stktop); + +#if 1 || defined RED_ZONE_SIZE + /* If this is an inlined strub function, also bump the watermark for the + enclosing function. This avoids a problem with the following scenario: A + calls B and B calls C, and both B and C get inlined into A. B allocates + temporary stack space before calling C. If we don't update A's watermark, + we may use an outdated baseline for the post-C strub_leave, erasing B's + temporary stack allocation. We only need this if we're fully expanding + strub_leave inline. */ + tree xwmptr = (optimize > 2 + ? strub_watermark_parm (current_function_decl) + : wmptr); + if (wmptr != xwmptr) + { + wmptr = xwmptr; + wmtype = TREE_TYPE (TREE_TYPE (wmptr)); + wmtree = fold_build2 (MEM_REF, wmtype, wmptr, + build_int_cst (TREE_TYPE (wmptr), 0)); + wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); + wmarkr = force_reg (ptr_mode, wmark); + + do_compare_rtx_and_jump (stktop, wmarkr, STACK_TOPS, STACK_UNSIGNED, + ptr_mode, NULL_RTX, lab, NULL, + profile_probability::very_likely ()); + emit_move_insn (wmark, stktop); + } +#endif + + emit_label (lab); + + return const0_rtx; +} + + +/* Expand a call to builtin function __builtin_strub_leave. */ + +static rtx +expand_builtin_strub_leave (tree exp) +{ + if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE)) + return NULL_RTX; + + if (optimize < 2 || optimize_size || flag_no_inline) + return NULL_RTX; + + rtx stktop = NULL_RTX; + +#if 1 || defined RED_ZONE_SIZE + if (tree wmptr = (optimize + ? strub_watermark_parm (current_function_decl) + : NULL_TREE)) + { + tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); + tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr, + build_int_cst (TREE_TYPE (wmptr), 0)); + rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); + stktop = force_reg (ptr_mode, wmark); + } +#endif + + if (!stktop) + stktop = expand_builtin_stack_address (); + + tree wmptr = CALL_EXPR_ARG (exp, 0); + tree wmtype = TREE_TYPE (TREE_TYPE (wmptr)); + tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr, + build_int_cst (TREE_TYPE (wmptr), 0)); + rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY); + + rtx wmarkr = force_reg (ptr_mode, wmark); + +#ifndef STACK_GROWS_DOWNWARD + rtx base = stktop; + rtx end = wmarkr; +#else + rtx base = wmarkr; + rtx end = stktop; +#endif + + /* We're going to modify it, so make sure it's not e.g. the stack pointer. */ + base = copy_to_reg (base); + + rtx_code_label *done = gen_label_rtx (); + do_compare_rtx_and_jump (base, end, LT, STACK_UNSIGNED, + ptr_mode, NULL_RTX, done, NULL, + profile_probability::very_likely ()); + + if (optimize < 3) + expand_call (exp, NULL_RTX, true); + else + { + /* Ok, now we've determined we want to copy the block, so convert the + addresses to Pmode, as needed to dereference them to access ptr_mode + memory locations, so that we don't have to convert anything within the + loop. */ + base = memory_address (ptr_mode, base); + end = memory_address (ptr_mode, end); + + rtx zero = force_operand (const0_rtx, NULL_RTX); + int ulen = GET_MODE_SIZE (ptr_mode); + rtx incr = plus_constant (Pmode, base, ulen); + rtx dstm = gen_rtx_MEM (ptr_mode, base); + + rtx_code_label *loop = gen_label_rtx (); + emit_label (loop); + emit_move_insn (dstm, zero); + emit_move_insn (base, force_operand (incr, NULL_RTX)); + do_compare_rtx_and_jump (base, end, LT, STACK_UNSIGNED, + Pmode, NULL_RTX, NULL, loop, + profile_probability::very_likely ()); + } + + emit_label (done); + + return const0_rtx; +} + /* Expand EXP, a call to the alloca builtin. Return NULL_RTX if we failed and the caller should emit a normal call. */ @@ -7263,6 +7515,27 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, case BUILT_IN_RETURN_ADDRESS: return expand_builtin_frame_address (fndecl, exp); + case BUILT_IN_STACK_ADDRESS: + return expand_builtin_stack_address (); + + case BUILT_IN___STRUB_ENTER: + target = expand_builtin_strub_enter (exp); + if (target) + return target; + break; + + case BUILT_IN___STRUB_UPDATE: + target = expand_builtin_strub_update (exp); + if (target) + return target; + break; + + case BUILT_IN___STRUB_LEAVE: + target = expand_builtin_strub_leave (exp); + if (target) + return target; + break; + /* Returns the address of the area where the structure is returned. 0 otherwise. */ case BUILT_IN_AGGREGATE_INCOMING_ADDRESS: diff --git a/gcc/builtins.def b/gcc/builtins.def index 005976f34e913..98763df73da8c 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -874,6 +874,10 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_FFSL, "ffsl", BT_FN_INT_LONG, ATTR_CONST_NOTHRO DEF_EXT_LIB_BUILTIN (BUILT_IN_FFSLL, "ffsll", BT_FN_INT_LONGLONG, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_FORK, "fork", BT_FN_PID, ATTR_NOTHROW_LIST) DEF_GCC_BUILTIN (BUILT_IN_FRAME_ADDRESS, "frame_address", BT_FN_PTR_UINT, ATTR_NULL) +DEF_GCC_BUILTIN (BUILT_IN_STACK_ADDRESS, "stack_address", BT_FN_PTR, ATTR_NULL) +DEF_BUILTIN_STUB (BUILT_IN___STRUB_ENTER, "__builtin___strub_enter") +DEF_BUILTIN_STUB (BUILT_IN___STRUB_UPDATE, "__builtin___strub_update") +DEF_BUILTIN_STUB (BUILT_IN___STRUB_LEAVE, "__builtin___strub_leave") /* [trans-mem]: Adjust BUILT_IN_TM_FREE if BUILT_IN_FREE is changed. */ DEF_LIB_BUILTIN (BUILT_IN_FREE, "free", BT_FN_VOID_PTR, ATTR_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_FROB_RETURN_ADDR, "frob_return_addr", BT_FN_PTR_PTR, ATTR_NULL) diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in index 1fe708a93f731..114076ad0a3f3 100644 --- a/libgcc/Makefile.in +++ b/libgcc/Makefile.in @@ -430,6 +430,9 @@ endif LIB2ADD += enable-execute-stack.c +# Stack scrubbing infrastructure. +LIB2ADD += $(srcdir)/strub.c + # While emutls.c has nothing to do with EH, it is in LIB2ADDEH* # instead of LIB2ADD because that's the way to be sure on some targets # (e.g. *-*-darwin*) only one copy of it is linked. diff --git a/libgcc/libgcc2.h b/libgcc/libgcc2.h index fc24ac34502bc..c45973f18d23b 100644 --- a/libgcc/libgcc2.h +++ b/libgcc/libgcc2.h @@ -532,6 +532,10 @@ extern int __parityDI2 (UDWtype); extern void __enable_execute_stack (void *); +extern void __strub_enter (void **); +extern void __strub_update (void**); +extern void __strub_leave (void **); + #ifndef HIDE_EXPORTS #pragma GCC visibility pop #endif diff --git a/libgcc/strub.c b/libgcc/strub.c new file mode 100644 index 0000000000000..90d3e82067b2f --- /dev/null +++ b/libgcc/strub.c @@ -0,0 +1,112 @@ +/* Stack scrubbing infrastructure + Copyright (C) 2021-2022 Free Software Foundation, Inc. + Contributed by Alexandre Oliva + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "coretypes.h" +#include "tm.h" +#include "libgcc_tm.h" +#include "libgcc2.h" + +#ifndef STACK_GROWS_DOWNWARD +# define TOPS > +#else +# define TOPS < +#endif + +#define ATTRIBUTE_STRUB_CALLABLE __attribute__ ((__strub__ ("callable"))) + +/* Enter a stack scrubbing context, initializing the watermark to the caller's + stack address. */ +void ATTRIBUTE_STRUB_CALLABLE +__strub_enter (void **watermark) +{ + *watermark = __builtin_frame_address (0); +} + +/* Update the watermark within a stack scrubbing context with the current stack + pointer. */ +void ATTRIBUTE_STRUB_CALLABLE +__strub_update (void **watermark) +{ + void *sp = __builtin_frame_address (0); + + if (sp TOPS *watermark) + *watermark = sp; +} + +#ifndef TARGET_STRUB_USE_DYNAMIC_ARRAY +# define TARGET_STRUB_DONT_USE_DYNAMIC_ARRAY 1 +#endif + +#ifndef TARGET_STRUB_DONT_USE_DYNAMIC_ARRAY +# ifdef TARGET_STRUB_MAY_USE_MEMSET +# define TARGET_STRUB_DONT_USE_DYNAMIC_ARRAY 1 +# else +# define TARGET_STRUB_MAY_USE_MEMSET 1 +# endif +#endif + +/* Leave a stack scrubbing context, restoring and updating SAVED, and + clearing the stack between top and watermark. */ +void ATTRIBUTE_STRUB_CALLABLE +#if ! TARGET_STRUB_MAY_USE_MEMSET +__attribute__ ((__optimize__ ("-fno-tree-loop-distribute-patterns"))) +#endif +__strub_leave (void **mark) +{ + void *sp = __builtin_stack_address (); + + void **base, **end; +#ifndef STACK_GROWS_DOWNWARD + base = sp; + end = *mark; +#else + base = *mark; + end = sp; +#endif + + ptrdiff_t len = end - base; + if (len <= 0) + return; + +#if ! TARGET_STRUB_DONT_USE_DYNAMIC_ARRAY + /* Allocate a dynamically-sized array covering the desired range, so that we + can safely call memset on it. */ + void *ptr[len]; + base = &ptr[0]; + end = &ptr[len]; +#else + void **ptr = end; +#endif /* TARGET_STRUB_DONT_USE_DYNAMIC_ARRAY */ + + /* ldist turns this into a memset. Without the dynamic array above, that call + is likely unsafe: possibly tail-called, and likely scribbling over its own + stack frame. */ + while (base < end) + *base++ = 0; + + asm ("" : : "m" (ptr)); +} From patchwork Fri Jul 29 06:27:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 56416 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 0FC3E3853826 for ; Fri, 29 Jul 2022 06:28:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0FC3E3853826 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1659076091; bh=DuwRtvhgNEaopDwKUnnOyjxWNVhHDf06EcR25sHmEKo=; 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=nGADzYukklp951eNjrQ0isxOtUYcVeUN8mZN86jImfEDYTatkLmrLlp/AiDXIhg/i kq3P7DMoy75s+57kH2w0XYd3tYWLfIK/SxHwA72db6lwHokSkETgmL6qMtMpTP9fnG 9CEAOejrLm6+HhLC1wSvHzaYU7WJt3bqdxb+9znE= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from rock.gnat.com (rock.gnat.com [IPv6:2620:20:4000:0:a9e:1ff:fe9b:1d1]) by sourceware.org (Postfix) with ESMTPS id 5C6EA3851C3A for ; Fri, 29 Jul 2022 06:27:39 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 5C6EA3851C3A Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 0408E1168D2; Fri, 29 Jul 2022 02:27:38 -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 pUSQSLdYpqX6; Fri, 29 Jul 2022 02:27:37 -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 B58551168CD; Fri, 29 Jul 2022 02:27:37 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 26T6RTFE1852044 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 Jul 2022 03:27:29 -0300 To: gcc-patches@gcc.gnu.org Subject: [PATCH v2 06/10] Introduce strub: attributes Organization: Free thinker, does not speak for AdaCore References: Date: Fri, 29 Jul 2022 03:27:29 -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" Ada already has support for the strub attributes stubbed-out, and the front-end code already has support for them and their effects in the type system. for gcc/ChangeLog * attribs.cc: Include ipa-strub.h. (decl_attributes): Support applying attributes to function type, rather than pointer type, at handler's request. (comp_type_attributes): Combine strub_comptypes and target comp_type results. for gcc/c-family/ChangeLog * c-attribs.cc: Include ipa-strub.h. (handle_strub_attribute): New. (c_common_attribute_table): Add strub. for gcc/ada/ChangeLog * gcc-interface/utils.cc: Include ipa-strub.h. (handle_strub_attribute): New. (gnat_internal_attribute_table): Add strub. diff --git a/gcc/attribs.cc b/gcc/attribs.cc index fb89616ff296b..d559cfc1b9f4e 100644 --- a/gcc/attribs.cc +++ b/gcc/attribs.cc @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-core.h" #include "attribs.h" #include "fold-const.h" +#include "ipa-strub.h" #include "stor-layout.h" #include "langhooks.h" #include "plugin.h" @@ -774,12 +775,11 @@ decl_attributes (tree *node, tree attributes, int flags, flags &= ~(int) ATTR_FLAG_TYPE_IN_PLACE; } - if (spec->function_type_required && TREE_CODE (*anode) != FUNCTION_TYPE - && TREE_CODE (*anode) != METHOD_TYPE) + if (spec->function_type_required + && !FUNC_OR_METHOD_TYPE_P (*anode)) { if (TREE_CODE (*anode) == POINTER_TYPE - && (TREE_CODE (TREE_TYPE (*anode)) == FUNCTION_TYPE - || TREE_CODE (TREE_TYPE (*anode)) == METHOD_TYPE)) + && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (*anode))) { /* OK, this is a bit convoluted. We can't just make a copy of the pointer type and modify its TREE_TYPE, because if @@ -887,7 +887,24 @@ decl_attributes (tree *node, tree attributes, int flags, TYPE_NAME (tt) = *node; } - *anode = cur_and_last_decl[0]; + if (*anode != cur_and_last_decl[0]) + { + /* Even if !spec->function_type_required, allow the attribute + handler to request the attribute to be applied to the function + type, rather than to the function pointer type, by setting + cur_and_last_decl[0] to the function type. */ + if (!fn_ptr_tmp + && POINTER_TYPE_P (*anode) + && TREE_TYPE (*anode) == cur_and_last_decl[0] + && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (*anode))) + { + fn_ptr_tmp = TREE_TYPE (*anode); + fn_ptr_quals = TYPE_QUALS (*anode); + anode = &fn_ptr_tmp; + } + *anode = cur_and_last_decl[0]; + } + if (ret == error_mark_node) { warning (OPT_Wattributes, "%qE attribute ignored", name); @@ -1491,9 +1508,20 @@ comp_type_attributes (const_tree type1, const_tree type2) if ((lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (type1)) != NULL) ^ (lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (type2)) != NULL)) return 0; + int strub_ret = strub_comptypes (CONST_CAST_TREE (type1), + CONST_CAST_TREE (type2)); + if (strub_ret == 0) + return strub_ret; /* As some type combinations - like default calling-convention - might be compatible, we have to call the target hook to get the final result. */ - return targetm.comp_type_attributes (type1, type2); + int target_ret = targetm.comp_type_attributes (type1, type2); + if (target_ret == 0) + return target_ret; + if (strub_ret == 2 || target_ret == 2) + return 2; + if (strub_ret == 1 && target_ret == 1) + return 1; + gcc_unreachable (); } /* PREDICATE acts as a function of type: diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index e4f1d3542f378..08c7d71f827a2 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -41,6 +41,7 @@ along with GCC; see the file COPYING3. If not see #include "common/common-target.h" #include "langhooks.h" #include "tree-inline.h" +#include "ipa-strub.h" #include "toplev.h" #include "tree-iterator.h" #include "opts.h" @@ -69,6 +70,7 @@ static tree handle_asan_odr_indicator_attribute (tree *, tree, tree, int, static tree handle_stack_protect_attribute (tree *, tree, tree, int, bool *); static tree handle_no_stack_protector_function_attribute (tree *, tree, tree, int, bool *); +static tree handle_strub_attribute (tree *, tree, tree, int, bool *); static tree handle_noinline_attribute (tree *, tree, tree, int, bool *); static tree handle_noclone_attribute (tree *, tree, tree, int, bool *); static tree handle_nocf_check_attribute (tree *, tree, tree, int, bool *); @@ -314,6 +316,8 @@ const struct attribute_spec c_common_attribute_table[] = { "no_stack_protector", 0, 0, true, false, false, false, handle_no_stack_protector_function_attribute, attr_stack_protect_exclusions }, + { "strub", 0, 1, false, true, false, true, + handle_strub_attribute, NULL }, { "noinline", 0, 0, true, false, false, false, handle_noinline_attribute, attr_noinline_exclusions }, @@ -1327,6 +1331,84 @@ handle_noipa_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) return NULL_TREE; } +/* Handle a "strub" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_strub_attribute (tree *node, tree name, + tree args, + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + bool enable = true; + + if (args && FUNCTION_POINTER_TYPE_P (*node)) + *node = TREE_TYPE (*node); + + if (args && FUNC_OR_METHOD_TYPE_P (*node)) + { + switch (strub_validate_fn_attr_parm (TREE_VALUE (args))) + { + case 1: + case 2: + enable = true; + break; + + case 0: + warning (OPT_Wattributes, + "%qE attribute ignored because of argument %qE", + name, TREE_VALUE (args)); + *no_add_attrs = true; + enable = false; + break; + + case -1: + case -2: + enable = false; + break; + + default: + gcc_unreachable (); + } + + args = TREE_CHAIN (args); + } + + if (args) + { + warning (OPT_Wattributes, + "ignoring attribute %qE because of excess arguments" + " starting at %qE", + name, TREE_VALUE (args)); + *no_add_attrs = true; + enable = false; + } + + /* Warn about unmet expectations that the strub attribute works like a + qualifier. ??? Could/should we extend it to the element/field types + here? */ + if (TREE_CODE (*node) == ARRAY_TYPE + || VECTOR_TYPE_P (*node) + || TREE_CODE (*node) == COMPLEX_TYPE) + warning (OPT_Wattributes, + "attribute %qE does not apply to elements" + " of non-scalar type %qT", + name, *node); + else if (RECORD_OR_UNION_TYPE_P (*node)) + warning (OPT_Wattributes, + "attribute %qE does not apply to fields" + " of aggregate type %qT", + name, *node); + + /* If we see a strub-enabling attribute, and we're at the default setting, + implicitly or explicitly, note that the attribute was seen, so that we can + reduce the compile-time overhead to nearly zero when the strub feature is + not used. */ + if (enable && flag_strub < -2) + flag_strub += 2; + + return NULL_TREE; +} + /* Handle a "noinline" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/ada/gcc-interface/utils.cc b/gcc/ada/gcc-interface/utils.cc index a57143021a79e..36b1345530d71 100644 --- a/gcc/ada/gcc-interface/utils.cc +++ b/gcc/ada/gcc-interface/utils.cc @@ -39,6 +39,7 @@ #include "varasm.h" #include "toplev.h" #include "opts.h" +#include "ipa-strub.h" #include "output.h" #include "debug.h" #include "convert.h" @@ -6601,9 +6602,77 @@ handle_no_stack_protector_attribute (tree *node, tree name, tree, int, struct attribute_spec.handler. */ static tree -handle_strub_attribute (tree *, tree, tree, int, bool *no_add_attrs) +handle_strub_attribute (tree *node, tree name, + tree args, + int ARG_UNUSED (flags), bool *no_add_attrs) { - *no_add_attrs = true; + bool enable = true; + + if (args && FUNCTION_POINTER_TYPE_P (*node)) + *node = TREE_TYPE (*node); + + if (args && FUNC_OR_METHOD_TYPE_P (*node)) + { + switch (strub_validate_fn_attr_parm (TREE_VALUE (args))) + { + case 1: + case 2: + enable = true; + break; + + case 0: + warning (OPT_Wattributes, + "%qE attribute ignored because of argument %qE", + name, TREE_VALUE (args)); + *no_add_attrs = true; + enable = false; + break; + + case -1: + case -2: + enable = false; + break; + + default: + gcc_unreachable (); + } + + args = TREE_CHAIN (args); + } + + if (args) + { + warning (OPT_Wattributes, + "ignoring attribute %qE because of excess arguments" + " starting at %qE", + name, TREE_VALUE (args)); + *no_add_attrs = true; + enable = false; + } + + /* Warn about unmet expectations that the strub attribute works like a + qualifier. ??? Could/should we extend it to the element/field types + here? */ + if (TREE_CODE (*node) == ARRAY_TYPE + || VECTOR_TYPE_P (*node) + || TREE_CODE (*node) == COMPLEX_TYPE) + warning (OPT_Wattributes, + "attribute %qE does not apply to elements" + " of non-scalar type %qT", + name, *node); + else if (RECORD_OR_UNION_TYPE_P (*node)) + warning (OPT_Wattributes, + "attribute %qE does not apply to fields" + " of aggregate type %qT", + name, *node); + + /* If we see a strub-enabling attribute, and we're at the default setting, + implicitly or explicitly, note that the attribute was seen, so that we can + reduce the compile-time overhead to nearly zero when the strub feature is + not used. */ + if (enable && flag_strub < -2) + flag_strub += 2; + return NULL_TREE; } From patchwork Fri Jul 29 06:28:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 56417 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 78474385840A for ; Fri, 29 Jul 2022 06:29:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 78474385840A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1659076146; bh=j982+YDUyAHtFEjz28PShxI6nafjwlKNgF033kPU0q4=; 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=e1EpAFqCVjsynobjco7nylXR079iD8/QNc/urqbnb7WhZlZMOZ+mRLcAmBY9D+zwk 0HtXsHr13e9AZ5hmRr9isY2yyhplW8vvwJJ9V7lIwkh4s56XKx/eciMddVsZESkdCT pfyseeWG9eZ4vK2pCdQHVLaDZzW4V8HtXOQItzE4= 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 E2CC93851C19 for ; Fri, 29 Jul 2022 06:28:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org E2CC93851C19 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 5F4441168D2; Fri, 29 Jul 2022 02:28:35 -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 7gZe6jAsxN9q; Fri, 29 Jul 2022 02:28:35 -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 1D2951168CD; Fri, 29 Jul 2022 02:28:34 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 26T6SQ5R1852115 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 Jul 2022 03:28:26 -0300 To: gcc-patches@gcc.gnu.org Subject: [PATCH v2 07/10] Introduce strub: infrastructure interfaces and adjustments Organization: Free thinker, does not speak for AdaCore References: Date: Fri, 29 Jul 2022 03:28:26 -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, KAM_SHORT, 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" Introduce the new strub passes, adjust other passes and front-end declaration for strub. for gcc/ChangeLog * Makefile.in (OBJS): Add ipa-strub.o. * ipa-inline.cc: Include ipa-strub.h. (can_inline_edge_p): Test strub_inlinable_to_p. * ipa-split.cc: Include ipa-strub.h. (execute_split_functions): Test strub_splittable_p. * ipa-strub.h: New. * passes.def: Add strub_mode and strub passes. * tree-cfg.cc (gimple_verify_flow_info): Note on debug stmts. * tree-pass.h (make_pass_ipa_strub_mode): Declare. (make_pass_ipa_strub): Declare. (make_pass_ipa_function_and_variable_visibility): Fix formatting. * tree-ssa-ccp.cc (optimize_stack_restore): Keep restores before strub leave. * multiple_target.cc (pass_target_clone::gate): Test seen_error. for gcc/ada/ChangeLog * gcc-interface/trans.cc: Include ipa-strub.h. (gigi): Make internal decls for targets of compiler-generated calls strub-callable too. (build_raise_check): Likewise. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 203f0a15187d2..4100531d73ae7 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1480,6 +1480,7 @@ OBJS = \ ipa-reference.o \ ipa-ref.o \ ipa-utils.o \ + ipa-strub.o \ ipa.o \ ira.o \ ira-build.o \ diff --git a/gcc/ada/gcc-interface/trans.cc b/gcc/ada/gcc-interface/trans.cc index c1dd567b2e4e2..e3553a7a87bfc 100644 --- a/gcc/ada/gcc-interface/trans.cc +++ b/gcc/ada/gcc-interface/trans.cc @@ -69,6 +69,21 @@ #include "ada-tree.h" #include "gigi.h" +/* The following #include is for strub_make_callable. + + This function marks a function as safe to call from strub contexts. We mark + Ada subprograms that may be called implicitly by the compiler, and that won't + leave on the stack caller data passed to them. This stops implicit calls + introduced in subprograms that have their stack scrubbed from being flagged + as unsafe, even in -fstrub=strict mode. + + These subprograms are also marked with the strub(callable) attribute in Ada + sources, but their declarations aren't necessarily imported by GNAT, or made + visible to gigi, in units that end up relying on them. So when gigi + introduces their declarations on its own, it must also add the attribute, by + calling strub_make_callable. */ +#include "ipa-strub.h" + /* We should avoid allocating more than ALLOCA_THRESHOLD bytes via alloca, for fear of running out of stack space. If we need more, we use xmalloc instead. */ @@ -449,6 +464,7 @@ gigi (Node_Id gnat_root, int64_type, NULL_TREE), NULL_TREE, is_default, true, true, true, false, false, NULL, Empty); + strub_make_callable (mulv64_decl); if (Enable_128bit_Types) { @@ -461,6 +477,7 @@ gigi (Node_Id gnat_root, NULL_TREE), NULL_TREE, is_default, true, true, true, false, false, NULL, Empty); + strub_make_callable (mulv128_decl); } /* Name of the _Parent field in tagged record types. */ @@ -716,6 +733,7 @@ build_raise_check (int check, enum exception_info_kind kind) = create_subprog_decl (get_identifier (Name_Buffer), NULL_TREE, ftype, NULL_TREE, is_default, true, true, true, false, false, NULL, Empty); + strub_make_callable (result); return result; } diff --git a/gcc/ipa-inline.cc b/gcc/ipa-inline.cc index 14969198cde1c..0674fe138574d 100644 --- a/gcc/ipa-inline.cc +++ b/gcc/ipa-inline.cc @@ -119,6 +119,7 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "attribs.h" #include "asan.h" +#include "ipa-strub.h" typedef fibonacci_heap edge_heap_t; typedef fibonacci_node edge_heap_node_t; @@ -397,6 +398,11 @@ can_inline_edge_p (struct cgraph_edge *e, bool report, inlinable = false; } + if (inlinable && !strub_inlinable_to_p (callee, caller)) + { + e->inline_failed = CIF_UNSPECIFIED; + inlinable = false; + } if (!inlinable && report) report_inline_failed_reason (e); return inlinable; diff --git a/gcc/ipa-split.cc b/gcc/ipa-split.cc index 16734617d0381..b3b9963f13669 100644 --- a/gcc/ipa-split.cc +++ b/gcc/ipa-split.cc @@ -104,6 +104,7 @@ along with GCC; see the file COPYING3. If not see #include "ipa-fnsummary.h" #include "cfgloop.h" #include "attribs.h" +#include "ipa-strub.h" /* Per basic block info. */ @@ -1810,6 +1811,12 @@ execute_split_functions (void) "section.\n"); return 0; } + if (!strub_splittable_p (node)) + { + if (dump_file) + fprintf (dump_file, "Not splitting: function is a strub context.\n"); + return 0; + } /* We enforce splitting after loop headers when profile info is not available. */ diff --git a/gcc/ipa-strub.h b/gcc/ipa-strub.h new file mode 100644 index 0000000000000..29869fadfa6c9 --- /dev/null +++ b/gcc/ipa-strub.h @@ -0,0 +1,45 @@ +/* strub (stack scrubbing) infrastructure. + Copyright (C) 2021-2022 Free Software Foundation, Inc. + Contributed by Alexandre Oliva . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* Return TRUE if CALLEE can be inlined into CALLER, as far as stack scrubbing + constraints are concerned. CALLEE doesn't have to be called directly by + CALLER, but the returned value says nothing about intervening functions. */ +extern bool strub_inlinable_to_p (cgraph_node *callee, cgraph_node *caller); + +/* Return FALSE if NODE is a strub context, and TRUE otherwise. */ +extern bool strub_splittable_p (cgraph_node *node); + +/* Locate and return the watermark_ptr parameter for FNDECL. If FNDECL is not a + strub context, return NULL. */ +extern tree strub_watermark_parm (tree fndecl); + +/* Make a function type or declaration callable. */ +extern void strub_make_callable (tree fndecl); + +/* Return zero iff ID is NOT an acceptable parameter for a user-supplied strub + attribute for a function. Otherwise, return >0 if it enables strub, <0 if it + does not. Return +/-1 if the attribute-modified type is compatible with the + type without the attribute, or +/-2 if it is not compatible. */ +extern int strub_validate_fn_attr_parm (tree id); + +/* Like comptypes, return 0 if t1 and t2 are not compatible, 1 if they are + compatible, and 2 if they are nearly compatible. Same strub mode is + compatible, interface-compatible strub modes are nearly compatible. */ +extern int strub_comptypes (tree t1, tree t2); diff --git a/gcc/multiple_target.cc b/gcc/multiple_target.cc index 3e2d26882c8e9..7cbd0a571e1ff 100644 --- a/gcc/multiple_target.cc +++ b/gcc/multiple_target.cc @@ -536,7 +536,7 @@ public: bool pass_target_clone::gate (function *) { - return true; + return !seen_error (); } } // anon namespace diff --git a/gcc/passes.def b/gcc/passes.def index 6bb92efacd451..513c5c29cd281 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -52,6 +52,7 @@ along with GCC; see the file COPYING3. If not see INSERT_PASSES_AFTER (all_small_ipa_passes) NEXT_PASS (pass_ipa_free_lang_data); NEXT_PASS (pass_ipa_function_and_variable_visibility); + NEXT_PASS (pass_ipa_strub_mode); NEXT_PASS (pass_build_ssa_passes); PUSH_INSERT_PASSES_WITHIN (pass_build_ssa_passes) NEXT_PASS (pass_fixup_cfg); @@ -113,6 +114,7 @@ along with GCC; see the file COPYING3. If not see POP_INSERT_PASSES () NEXT_PASS (pass_ipa_remove_symbols); + NEXT_PASS (pass_ipa_strub); NEXT_PASS (pass_ipa_oacc); PUSH_INSERT_PASSES_WITHIN (pass_ipa_oacc) NEXT_PASS (pass_ipa_pta); diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc index 5bcf78198e78b..6144b9ee4fca4 100644 --- a/gcc/tree-cfg.cc +++ b/gcc/tree-cfg.cc @@ -5658,6 +5658,7 @@ gimple_verify_flow_info (void) { gimple *stmt = gsi_stmt (gsi); + /* Do NOT disregard debug stmts after found_ctrl_stmt. */ if (found_ctrl_stmt) { error ("control flow in the middle of basic block %d", diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 4dfe05ed8e0de..ecf1f9ef28b12 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -502,8 +502,9 @@ extern gimple_opt_pass *make_pass_adjust_alignment (gcc::context *ctxt); /* IPA Passes */ extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt); -extern simple_ipa_opt_pass - *make_pass_ipa_function_and_variable_visibility (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_function_and_variable_visibility (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_strub_mode (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_strub (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_tree_profile (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_auto_profile (gcc::context *ctxt); diff --git a/gcc/tree-ssa-ccp.cc b/gcc/tree-ssa-ccp.cc index 85c046078249c..8d04e55ba6523 100644 --- a/gcc/tree-ssa-ccp.cc +++ b/gcc/tree-ssa-ccp.cc @@ -3054,7 +3054,9 @@ optimize_stack_restore (gimple_stmt_iterator i) if (!callee || !fndecl_built_in_p (callee, BUILT_IN_NORMAL) /* All regular builtins are ok, just obviously not alloca. */ - || ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callee))) + || ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callee)) + /* Do not remove stack updates before strub leave. */ + || fndecl_built_in_p (callee, BUILT_IN___STRUB_LEAVE)) return NULL_TREE; if (fndecl_built_in_p (callee, BUILT_IN_STACK_RESTORE)) From patchwork Fri Jul 29 06:28:53 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 56419 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 1A0963851A92 for ; Fri, 29 Jul 2022 06:30:04 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1A0963851A92 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1659076204; bh=+gerHoEob+POI3PWU089gA4d8mjPjGe6FPfpHEC8WPU=; 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=IOx2W/GMstpTJ5YLdfIj1cneU/P1Gom2zq0Qi435BK0tIGw20m7XUHH1De1pBiHFo U2CmXe473vv3GnMd/7+yOAtpv0f66Ulmx6VugE0k5dhsnAMcPgu+lVlcTw0fm74HxY kiBNS+vtq3pEn58FGtxtlFD5MJoNSxW2Wv/s7mgk= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from rock.gnat.com (rock.gnat.com [IPv6:2620:20:4000:0:a9e:1ff:fe9b:1d1]) by sourceware.org (Postfix) with ESMTPS id DD7DA3851C1B for ; Fri, 29 Jul 2022 06:29:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org DD7DA3851C1B Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 88B9D1168CD; Fri, 29 Jul 2022 02:29:02 -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 IOU8SWFdaXgi; Fri, 29 Jul 2022 02:29:02 -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 214CA1168D2; Fri, 29 Jul 2022 02:29:01 -0400 (EDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 26T6SrN71852125 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 29 Jul 2022 03:28:54 -0300 To: gcc-patches@gcc.gnu.org Subject: [PATCH v2 08/10] Introduce strub: strub modes Organization: Free thinker, does not speak for AdaCore References: Date: Fri, 29 Jul 2022 03:28:53 -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, KAM_SHORT, 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" This initial fragment of ipa-strub.cc covers strub modes and their internal representation. for gcc/ChangeLog * ipa-strub.cc: New. diff --git a/gcc/ipa-strub.cc b/gcc/ipa-strub.cc new file mode 100644 index 0000000000000..d61b7e2e36e43 --- /dev/null +++ b/gcc/ipa-strub.cc @@ -0,0 +1,3489 @@ +/* strub (stack scrubbing) support. + Copyright (C) 2021-2022 Free Software Foundation, Inc. + Contributed by Alexandre Oliva . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "gimplify.h" +#include "tree-pass.h" +#include "ssa.h" +#include "gimple-iterator.h" +#include "gimplify-me.h" +#include "tree-into-ssa.h" +#include "tree-ssa.h" +#include "tree-cfg.h" +#include "cfghooks.h" +#include "cfgloop.h" +#include "cfgcleanup.h" +#include "tree-eh.h" +#include "except.h" +#include "builtins.h" +#include "attribs.h" +#include "tree-inline.h" +#include "cgraph.h" +#include "alloc-pool.h" +#include "symbol-summary.h" +#include "ipa-prop.h" +#include "ipa-fnsummary.h" +#include "gimple-fold.h" +#include "fold-const.h" +#include "gimple-walk.h" +#include "tree-dfa.h" +#include "langhooks.h" +#include "calls.h" +#include "vec.h" +#include "stor-layout.h" +#include "varasm.h" +#include "alias.h" +#include "diagnostic.h" +#include "intl.h" +#include "ipa-strub.h" + +#if BUILDING_GCC_MAJOR >= 11 +# include "symtab-thunks.h" +# include "attr-fnspec.h" +# define HAVE_ATTR_FNSPEC 1 +# define FOR_GCC_11P 1 +#else +# define HAVE_ATTR_FNSPEC 0 +# define FOR_GCC_11P 0 +#endif + +/* Const and pure functions that gain a watermark parameter for strub purposes + are still regarded as such, which may cause the inline expansions of the + __strub builtins to malfunction. Ideally, attribute "fn spec" would enable + us to inform the backend about requirements and side effects of the call, but + call_fusage building in calls.c:expand_call does not even look at + attr_fnspec, so we resort to asm loads and updates to attain an equivalent + effect. Once expand_call gains the ability to issue extra memory uses and + clobbers based on pure/const function's fnspec, we can define this to 1. */ +#define ATTR_FNSPEC_DECONST_WATERMARK 0 + +enum strub_mode { + /* This mode denotes a regular function, that does not require stack + scrubbing (strubbing). It may call any other functions, but if + it calls AT_CALLS (or WRAPPED) ones, strubbing logic is + automatically introduced around those calls (the latter, by + inlining INTERNAL wrappers). */ + STRUB_DISABLED = 0, + + /* This denotes a function whose signature is (to be) modified to + take an extra parameter, for stack use annotation, and its + callers must initialize and pass that argument, and perform the + strubbing. Functions that are explicitly marked with attribute + strub must have the mark visible wherever the function is, + including aliases, and overriders and overriding methods. + Functions that are implicitly marked for strubbing, for accessing + variables explicitly marked as such, will only select this + strubbing method if they are internal to a translation unit. It + can only be inlined into other strubbing functions, i.e., + STRUB_AT_CALLS or STRUB_WRAPPED. */ + STRUB_AT_CALLS = 1, + + /* This denotes a function that is to perform strubbing internally, + without any changes to its interface (the function is turned into + a strubbing wrapper, and its original body is moved to a separate + STRUB_WRAPPED function, with a modified interface). Functions + may be explicitly marked with attribute strub(2), and the + attribute must be visible at the point of definition. Functions + that are explicitly marked for strubbing, for accessing variables + explicitly marked as such, may select this strubbing mode if + their interface cannot change, e.g. because its interface is + visible to other translation units, directly, by indirection + (having its address taken), inheritance, etc. Functions that use + this method must not have the noclone attribute, nor the noipa + one. Functions marked as always_inline may select this mode, but + they are NOT wrapped, they remain unchanged, and are only inlined + into strubbed contexts. Once non-always_inline functions are + wrapped, the wrapper becomes STRUB_WRAPPER, and the wrapped becomes + STRUB_WRAPPED. */ + STRUB_INTERNAL = 2, + + /* This denotes a function whose stack is not strubbed, but that is + nevertheless explicitly or implicitly marked as callable from strubbing + functions. Normally, only STRUB_AT_CALLS (and STRUB_INTERNAL -> + STRUB_WRAPPED) functions can be called from strubbing contexts (bodies of + STRUB_AT_CALLS, STRUB_INTERNAL and STRUB_WRAPPED functions), but attribute + strub(3) enables other functions to be (indirectly) called from these + contexts. Some builtins and internal functions may be implicitly marked as + STRUB_CALLABLE. */ + STRUB_CALLABLE = 3, + + /* This denotes the function that took over the body of a + STRUB_INTERNAL function. At first, it's only called by its + wrapper, but the wrapper may be inlined. The wrapped function, + in turn, can only be inlined into other functions whose stack + frames are strubbed, i.e., that are STRUB_WRAPPED or + STRUB_AT_CALLS. */ + STRUB_WRAPPED = -1, + + /* This denotes the wrapper function that replaced the STRUB_INTERNAL + function. This mode overrides the STRUB_INTERNAL mode at the time the + internal to-be-wrapped function becomes a wrapper, so that inlining logic + can tell one from the other. */ + STRUB_WRAPPER = -2, + + /* This denotes an always_inline function that requires strubbing. It can + only be called from, and inlined into, other strubbing contexts. */ + STRUB_INLINABLE = -3, + + /* This denotes a function that accesses strub variables, so it would call for + internal strubbing (whether or not it's eligible for that), but since + at-calls strubbing is viable, that's selected as an optimization. This + mode addresses the inconvenience that such functions may have different + modes selected depending on optimization flags, and get a different + callable status depending on that choice: if we assigned them + STRUB_AT_CALLS mode, they would be callable when optimizing, whereas + STRUB_INTERNAL would not be callable. */ + STRUB_AT_CALLS_OPT = -4, + +}; + +/* Look up a strub attribute in TYPE, and return it. */ + +static tree +get_strub_attr_from_type (tree type) +{ + return lookup_attribute ("strub", TYPE_ATTRIBUTES (type)); +} + +/* Look up a strub attribute in DECL or in its type, and return it. */ + +static tree +get_strub_attr_from_decl (tree decl) +{ + tree ret = lookup_attribute ("strub", DECL_ATTRIBUTES (decl)); + if (ret) + return ret; + return get_strub_attr_from_type (TREE_TYPE (decl)); +} + +/* Define a function to cache identifier ID, to be used as a strub attribute + parameter for a strub mode named after NAME. */ +#define DEF_STRUB_IDS(NAME, ID) \ +static inline tree get_strub_mode_id_ ## NAME () { \ + static tree identifier = NULL_TREE; \ + if (!identifier) \ + identifier = get_identifier (ID); \ + return identifier; \ +} +/* Same as DEF_STRUB_IDS, but use the string expansion of NAME as ID. */ +#define DEF_STRUB_ID(NAME) \ +DEF_STRUB_IDS (NAME, #NAME) + +/* Define functions for each of the strub mode identifiers. + Expose dashes rather than underscores. */ +DEF_STRUB_ID (disabled) +DEF_STRUB_IDS (at_calls, "at-calls") +DEF_STRUB_ID (internal) +DEF_STRUB_ID (callable) +DEF_STRUB_ID (wrapped) +DEF_STRUB_ID (wrapper) +DEF_STRUB_ID (inlinable) +DEF_STRUB_IDS (at_calls_opt, "at-calls-opt") + +/* Release the temporary macro names. */ +#undef DEF_STRUB_IDS +#undef DEF_STRUB_ID + +/* Return the identifier corresponding to strub MODE. */ + +static tree +get_strub_mode_attr_parm (enum strub_mode mode) +{ + switch (mode) + { + case STRUB_DISABLED: + return get_strub_mode_id_disabled (); + + case STRUB_AT_CALLS: + return get_strub_mode_id_at_calls (); + + case STRUB_INTERNAL: + return get_strub_mode_id_internal (); + + case STRUB_CALLABLE: + return get_strub_mode_id_callable (); + + case STRUB_WRAPPED: + return get_strub_mode_id_wrapped (); + + case STRUB_WRAPPER: + return get_strub_mode_id_wrapper (); + + case STRUB_INLINABLE: + return get_strub_mode_id_inlinable (); + + case STRUB_AT_CALLS_OPT: + return get_strub_mode_id_at_calls_opt (); + + default: + gcc_unreachable (); + } +} + +/* Return the parmeters (TREE_VALUE) for a strub attribute of MODE. + We know we use a single parameter, so we bypass the creation of a + tree list. */ + +static tree +get_strub_mode_attr_value (enum strub_mode mode) +{ + return get_strub_mode_attr_parm (mode); +} + +/* Determine whether ID is a well-formed strub mode-specifying attribute + parameter for a function (type). Only user-visible modes are accepted, and + ID must be non-NULL. + + For unacceptable parms, return 0, otherwise a nonzero value as below. + + If the parm enables strub, return positive, otherwise negative. + + If the affected type must be a distinct, incompatible type,return an integer + of absolute value 2, otherwise 1. */ + +int +strub_validate_fn_attr_parm (tree id) +{ + int ret; + const char *s = NULL; + size_t len = 0; + + /* do NOT test for NULL. This is only to be called with non-NULL arguments. + We assume that the strub parameter applies to a function, because only + functions accept an explicit argument. If we accepted NULL, and we + happened to be called to verify the argument for a variable, our return + values would be wrong. */ + if (TREE_CODE (id) == STRING_CST) + { + s = TREE_STRING_POINTER (id); + len = TREE_STRING_LENGTH (id) - 1; + } + else if (TREE_CODE (id) == IDENTIFIER_NODE) + { + s = IDENTIFIER_POINTER (id); + len = IDENTIFIER_LENGTH (id); + } + else + return 0; + + enum strub_mode mode; + + if (len != 8) + return 0; + + switch (s[0]) + { + case 'd': + mode = STRUB_DISABLED; + ret = -1; + break; + + case 'a': + mode = STRUB_AT_CALLS; + ret = 2; + break; + + case 'i': + mode = STRUB_INTERNAL; + ret = 1; + break; + + case 'c': + mode = STRUB_CALLABLE; + ret = -2; + break; + + default: + /* Other parms are for internal use only. */ + return 0; + } + + tree mode_id = get_strub_mode_attr_parm (mode); + + if (TREE_CODE (id) == IDENTIFIER_NODE + ? id != mode_id + : strncmp (s, IDENTIFIER_POINTER (mode_id), len) != 0) + return 0; + + return ret; +} + +/* Return the strub mode from STRUB_ATTR. VAR_P should be TRUE if the attribute + is taken from a variable, rather than from a function, or a type thereof. */ + +static enum strub_mode +get_strub_mode_from_attr (tree strub_attr, bool var_p = false) +{ + enum strub_mode mode = STRUB_DISABLED; + + if (strub_attr) + { + if (!TREE_VALUE (strub_attr)) + mode = !var_p ? STRUB_AT_CALLS : STRUB_INTERNAL; + else + { + gcc_checking_assert (!var_p); + tree id = TREE_VALUE (strub_attr); + if (TREE_CODE (id) == TREE_LIST) + id = TREE_VALUE (id); + const char *s = (TREE_CODE (id) == STRING_CST + ? TREE_STRING_POINTER (id) + : IDENTIFIER_POINTER (id)); + size_t len = (TREE_CODE (id) == STRING_CST + ? TREE_STRING_LENGTH (id) - 1 + : IDENTIFIER_LENGTH (id)); + + switch (len) + { + case 7: + switch (s[6]) + { + case 'r': + mode = STRUB_WRAPPER; + break; + + case 'd': + mode = STRUB_WRAPPED; + break; + + default: + gcc_unreachable (); + } + break; + + case 8: + switch (s[0]) + { + case 'd': + mode = STRUB_DISABLED; + break; + + case 'a': + mode = STRUB_AT_CALLS; + break; + + case 'i': + mode = STRUB_INTERNAL; + break; + + case 'c': + mode = STRUB_CALLABLE; + break; + + default: + gcc_unreachable (); + } + break; + + case 9: + mode = STRUB_INLINABLE; + break; + + case 12: + mode = STRUB_AT_CALLS_OPT; + break; + + default: + gcc_unreachable (); + } + + gcc_checking_assert (TREE_CODE (id) == IDENTIFIER_NODE + ? id == get_strub_mode_attr_parm (mode) + : strncmp (IDENTIFIER_POINTER + (get_strub_mode_attr_parm (mode)), + s, len) == 0); + } + } + + return mode; +} + +/* Look up, decode and return the strub mode associated with FNDECL. */ + +static enum strub_mode +get_strub_mode_from_fndecl (tree fndecl) +{ + return get_strub_mode_from_attr (get_strub_attr_from_decl (fndecl)); +} + +/* Look up, decode and return the strub mode associated with NODE. */ + +static enum strub_mode +get_strub_mode (cgraph_node *node) +{ + return get_strub_mode_from_fndecl (node->decl); +} + +/* Look up, decode and return the strub mode associated with TYPE. */ + +static enum strub_mode +get_strub_mode_from_type (tree type) +{ + bool var_p = !FUNC_OR_METHOD_TYPE_P (type); + tree attr = get_strub_attr_from_type (type); + + if (attr) + return get_strub_mode_from_attr (attr, var_p); + + if (flag_strub >= -1 && !var_p) + return STRUB_CALLABLE; + + return STRUB_DISABLED; +} + + -- Alexandre Oliva, happy hacker https://FSFLA.org/blogs/lxo/ Free Software Activist GNU Toolchain Engineer Disinformation flourishes because many people care deeply about injustice but very few check the facts. Ask me about