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