From patchwork Wed Oct 28 23:54:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Qing Zhao X-Patchwork-Id: 1389624 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gcc.gnu.org Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.a=rsa-sha256 header.s=default header.b=lIEOO9VX; dkim-atps=neutral Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CM56c1f0rz9sSP for ; Thu, 29 Oct 2020 10:55:20 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5211B3844025; Wed, 28 Oct 2020 23:55:18 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5211B3844025 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1603929318; bh=KrFC2ErL5SZtzfUrbIGX7V4gVjqNS1hZiP5Mq9hEDO8=; h=Subject:Date:To:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=lIEOO9VXVCIto8LDy5gkYqUupXgfXsK/PPI5qV9zXyxyVVDq7+LB+ER1Lg0NhKBdh KiB2ibtzRR1OO3TTv5HSs/Emklg2CoNO+MKzY+h4Rz9XjeV8mfhr5O5ZE2maQGYivu K45kkX/ZcfrIOoDWICxRgM3/61QWUwtDOnll5Qt4= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from aserp2130.oracle.com (aserp2130.oracle.com [141.146.126.79]) by sourceware.org (Postfix) with ESMTPS id E96403858001 for ; Wed, 28 Oct 2020 23:55:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org E96403858001 Received: from pps.filterd (aserp2130.oracle.com [127.0.0.1]) by aserp2130.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 09SNnXXf138157; Wed, 28 Oct 2020 23:55:05 GMT Received: from userp3030.oracle.com (userp3030.oracle.com [156.151.31.80]) by aserp2130.oracle.com with ESMTP id 34c9sb2caf-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 28 Oct 2020 23:55:04 +0000 Received: from pps.filterd (userp3030.oracle.com [127.0.0.1]) by userp3030.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 09SNpOpS072308; Wed, 28 Oct 2020 23:55:03 GMT Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by userp3030.oracle.com with ESMTP id 34cx6xudwx-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Wed, 28 Oct 2020 23:55:03 +0000 Received: from abhmp0004.oracle.com (abhmp0004.oracle.com [141.146.116.10]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id 09SNswED004923; Wed, 28 Oct 2020 23:54:58 GMT Received: from dhcp-10-154-170-165.vpn.oracle.com (/10.154.170.165) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Wed, 28 Oct 2020 16:54:57 -0700 Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.17\)) Subject: [PATCH][middle-end][i386][version 5]Add -fzero-call-used-regs=[skip|used-gpr-arg|used-arg|all-gpr-arg|all-arg|used-gpr|all-gpr|used|all] Message-Id: Date: Wed, 28 Oct 2020 18:54:56 -0500 To: richard Sandiford , Uros Bizjak X-Mailer: Apple Mail (2.3445.104.17) X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9788 signatures=668682 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 adultscore=0 phishscore=0 spamscore=0 bulkscore=0 malwarescore=0 mlxlogscore=999 mlxscore=0 suspectscore=96 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2010280148 X-Proofpoint-Virus-Version: vendor=nai engine=6000 definitions=9788 signatures=668682 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 mlxscore=0 impostorscore=0 mlxlogscore=999 malwarescore=0 lowpriorityscore=0 bulkscore=0 priorityscore=1501 spamscore=0 phishscore=0 clxscore=1015 suspectscore=96 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2010280148 X-Spam-Status: No, score=-9.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_MSPIKE_H2, SPF_HELO_PASS, SPF_PASS, TXREP, UNPARSEABLE_RELAY autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) 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: Qing Zhao via Gcc-patches From: Qing Zhao Reply-To: Qing Zhao Cc: gcc-patches Kees Cook via , kees Cook , segher Boessenkool , rodriguez Bahena Victor Errors-To: gcc-patches-bounces@gcc.gnu.org Sender: "Gcc-patches" Hi, This is the 5th version of the implementation of patch -fzero-call-used-regs. The major change compared to the previous version (4th version) are: 1. Documentation change per Richard’s suggestion; 2. Use namespace for zero_regs_code; 3. Add more general testing cases per Richard’s suggestion; 4. I386 part, ST/MM register sets clearing per Uros’s suggestion. 5. Add more i386 testing cases for ST/MM clearing per Uros’s suggestion. 6. Some minor style fixes. I have tested this new GCC on both x86 and arm64, no regression. Please let me know whether it’s ready for stage 1 gcc11? Thanks. Qing ******The documentation (gcc.info): 'zero_call_used_regs ("CHOICE")' The 'zero_call_used_regs' attribute causes the compiler to zero a subset of all call-used registers at function return according to CHOICE. This is used to increase the program security by either mitigating Return-Oriented Programming (ROP) or preventing information leak through registers. A "call-used" register is a register whose contents can be changed by a function call; therefore, a caller cannot assume that the register has the same contents on return from the function as it had before calling the function. Such registers are also called "call-clobbered", "caller-saved", or "volatile". In order to satisfy users with different security needs and control the run-time overhead at the same time, GCC provides a flexible way to choose the subset of the call-used registers to be zeroed. The three basic values of CHOICE are: * 'skip' doesn't zero any call-used registers. * 'used' only zeros call-used registers that are used in the function. A "used" register is one whose content has been set or referenced in the function. * 'all' zeros all call-used registers. In addition to these three basic choices, it is possible to modify 'used' or 'all' as follows: * Adding '-gpr' restricts the zeroing to general-purpose registers. * Adding '-arg' restricts the zeroing to registers that can sometimes be used to pass function arguments. This includes all argument registers defined by the platform's calling conversion, regardless of whether the function uses those registers for function arguments or not. The modifiers can be used individually or together. If they are used together, they must appear in the order above. The full list of CHOICEs is therefore: * 'skip' doesn't zero any call-used register. * 'used' only zeros call-used registers that are used in the function. * 'all' zeros all call-used registers. * 'used-arg' only zeros used call-used registers that pass arguments. * 'used-gpr' only zeros used call-used general purpose registers. * 'used-gpr-arg' only zeros used call-used general purpose registers that pass arguments. * 'all-gpr-arg' zeros all call-used general purpose registers that pass arguments. * 'all-arg' zeros all call-used registers that pass arguments. * 'all-gpr' zeros all call-used general purpose registers. Among this list, 'used-gpr-arg', 'used-arg', 'all-gpr-arg', and 'all-arg' are mainly used for ROP mitigation. The default for the attribute is controlled by '-fzero-call-used-regs’. '-fzero-call-used-regs=CHOICE' Zero call-used registers at function return to increase the program security by either mitigating Return-Oriented Programming (ROP) or preventing information leak through registers. The possible values of CHOICE are the same as for the 'zero_call_used_regs' attribute (*note Function Attributes::). The default is 'skip'. You can control this behavior for a specific function by using the function attribute 'zero_call_used_regs' (*note Function Attributes::). ******The changelog: gcc/ChangeLog: 2020-10-28 Qing Zhao H.J.Lu * common.opt: Add new option -fzero-call-used-regs * config/i386/i386.c (zero_call_used_regno_p): New function. (zero_call_used_regno_mode): Likewise. (zero_all_vector_registers): Likewise. (zero_all_st_registers): Likewise. (zero_all_mm_registers): Likewise. (ix86_zero_call_used_regs): Likewise. (TARGET_ZERO_CALL_USED_REGS): Define. * df-scan.c (df_epilogue_uses_p): New function. (df_get_exit_block_use_set): Replace EPILOGUE_USES with df_epilogue_uses_p. * df.h (df_epilogue_uses_p): Declare. * doc/extend.texi: Document the new zero_call_used_regs attribute. * doc/invoke.texi: Document the new -fzero-call-used-regs option. * doc/tm.texi: Regenerate. * doc/tm.texi.in (TARGET_ZERO_CALL_USED_REGS): New hook. * emit-rtl.h (struct rtl_data): New fields zero_call_used_regs and must_be_zero_on_return. * flag-types.h (namespace zero_regs_code): New namespace. * function.c (gen_call_used_regs_seq): New function. (class pass_zero_call_used_regs): New class. (pass_zero_call_used_regs::execute): New function. (make_pass_zero_call_used_regs): New function. * optabs.c (expand_asm_reg_clobber_mem_blockage): New function. * optabs.h (expand_asm_reg_clobber_mem_blockage): Declare. * opts.c (zero_call_used_regs_opts): New structure array initialization. (parse_zero_call_used_regs_options): New function. (common_handle_option): Handle fzero-call-used-regs. * opts.h (zero_call_used_regs_opts): New structure array. * passes.def: Add new pass pass_zero_call_used_regs. * recog.c (valid_insn_p): New function. * recog.h (valid_insn_p): Declare. * resource.c (init_resource_info): Replace EPILOGUE_USES with df_epilogue_uses_p. * target.def (zero_call_used_regs): New hook. * targhooks.c (default_zero_call_used_regs): New function. * targhooks.h (default_zero_call_used_regs): Declare. * tree-pass.h (make_pass_zero_call_used_regs): Declare. gcc/c-family/ChangeLog: 2020-10-28 Qing Zhao H.J.Lu * c-attribs.c (c_common_attribute_table): Add new attribute zero_call_used_regs. (handle_zero_call_used_regs_attribute): New function. gcc/testsuite/ChangeLog: 2020-10-28 Qing Zhao H.J.Lu * c-c++-common/zero-scratch-regs-1.c: New test. * c-c++-common/zero-scratch-regs-10.c: New test. * c-c++-common/zero-scratch-regs-11.c: New test. * c-c++-common/zero-scratch-regs-2.c: New test. * c-c++-common/zero-scratch-regs-3.c: New test. * c-c++-common/zero-scratch-regs-4.c: New test. * c-c++-common/zero-scratch-regs-5.c: New test. * c-c++-common/zero-scratch-regs-6.c: New test. * c-c++-common/zero-scratch-regs-7.c: New test. * c-c++-common/zero-scratch-regs-8.c: New test. * c-c++-common/zero-scratch-regs-9.c: New test. * c-c++-common/zero-scratch-regs-attr-usages.c: New test. * gcc.target/i386/zero-scratch-regs-1.c: New test. * gcc.target/i386/zero-scratch-regs-10.c: New test. * gcc.target/i386/zero-scratch-regs-11.c: New test. * gcc.target/i386/zero-scratch-regs-12.c: New test. * gcc.target/i386/zero-scratch-regs-13.c: New test. * gcc.target/i386/zero-scratch-regs-14.c: New test. * gcc.target/i386/zero-scratch-regs-15.c: New test. * gcc.target/i386/zero-scratch-regs-16.c: New test. * gcc.target/i386/zero-scratch-regs-17.c: New test. * gcc.target/i386/zero-scratch-regs-18.c: New test. * gcc.target/i386/zero-scratch-regs-19.c: New test. * gcc.target/i386/zero-scratch-regs-2.c: New test. * gcc.target/i386/zero-scratch-regs-20.c: New test. * gcc.target/i386/zero-scratch-regs-21.c: New test. * gcc.target/i386/zero-scratch-regs-22.c: New test. * gcc.target/i386/zero-scratch-regs-23.c: New test. * gcc.target/i386/zero-scratch-regs-24.c: New test. * gcc.target/i386/zero-scratch-regs-25.c: New test. * gcc.target/i386/zero-scratch-regs-26.c: New test. * gcc.target/i386/zero-scratch-regs-27.c: New test. * gcc.target/i386/zero-scratch-regs-28.c: New test. * gcc.target/i386/zero-scratch-regs-29.c: New test. * gcc.target/i386/zero-scratch-regs-30.c: New test. * gcc.target/i386/zero-scratch-regs-31.c: New test. * gcc.target/i386/zero-scratch-regs-3.c: New test. * gcc.target/i386/zero-scratch-regs-4.c: New test. * gcc.target/i386/zero-scratch-regs-5.c: New test. * gcc.target/i386/zero-scratch-regs-6.c: New test. * gcc.target/i386/zero-scratch-regs-7.c: New test. * gcc.target/i386/zero-scratch-regs-8.c: New test. * gcc.target/i386/zero-scratch-regs-9.c: New test. *******The patch: From 257dd69611a08eff9f3ee129c305236fedc5c4f9 Mon Sep 17 00:00:00 2001 From: qing zhao Date: Thu, 1 Oct 2020 00:27:57 +0000 Subject: [PATCH] The 5th version of -fzero-call-used-regs We will provide a new feature into GCC: Add -fzero-call-used-regs=[skip|used-gpr-arg|used-arg|all-gpr-arg |all-arg|used-gpr|all-gpr|used|all] command-line option and zero_call_used_regs("skip|used-gpr-arg|used-arg|all-gpr-arg |all-arg|used-gpr|all-gpr|used|all") function attribues: 1. -fzero-call-used-regs=skip and zero_call_used_regs("skip") 2. -fzero-call-used-regs=used-gpr-arg and zero_call_used_regs("used-gpr-arg") 3. -fzero-call-used-regs=used-arg and zero_call_used_regs("used-arg") 4. -fzero-call-used-regs=all-gpr-arg and zero_call_used_regs("all-gpr-arg") 5. -fzero-call-used-regs=all-arg and zero_call_used_regs("all-arg") 6. -fzero-call-used-regs=used-gpr and zero_call_used_regs("used-gpr") 7. -fzero-call-used-regs=all-gpr and zero_call_used_regs("all-gpr") 8. -fzero-call-used-regs=used and zero_call_used_regs("used") 9. -fzero-call-used-regs=all and zero_call_used_regs("all") --- gcc/c-family/c-attribs.c | 49 ++++ gcc/common.opt | 8 + gcc/config/i386/i386.c | 294 +++++++++++++++++++++ gcc/df-scan.c | 12 +- gcc/df.h | 1 + gcc/doc/extend.texi | 90 +++++++ gcc/doc/invoke.texi | 15 +- gcc/doc/tm.texi | 12 + gcc/doc/tm.texi.in | 2 + gcc/emit-rtl.h | 6 + gcc/flag-types.h | 22 ++ gcc/function.c | 192 +++++++++++++- gcc/optabs.c | 42 +++ gcc/optabs.h | 2 + gcc/opts.c | 45 ++++ gcc/opts.h | 6 + gcc/passes.def | 1 + gcc/recog.c | 18 +- gcc/recog.h | 1 + gcc/resource.c | 2 +- gcc/target.def | 15 ++ gcc/targhooks.c | 32 +++ gcc/targhooks.h | 1 + gcc/testsuite/c-c++-common/zero-scratch-regs-1.c | 15 ++ gcc/testsuite/c-c++-common/zero-scratch-regs-10.c | 92 +++++++ gcc/testsuite/c-c++-common/zero-scratch-regs-11.c | 92 +++++++ gcc/testsuite/c-c++-common/zero-scratch-regs-2.c | 15 ++ gcc/testsuite/c-c++-common/zero-scratch-regs-3.c | 15 ++ gcc/testsuite/c-c++-common/zero-scratch-regs-4.c | 15 ++ gcc/testsuite/c-c++-common/zero-scratch-regs-5.c | 15 ++ gcc/testsuite/c-c++-common/zero-scratch-regs-6.c | 15 ++ gcc/testsuite/c-c++-common/zero-scratch-regs-7.c | 15 ++ gcc/testsuite/c-c++-common/zero-scratch-regs-8.c | 15 ++ gcc/testsuite/c-c++-common/zero-scratch-regs-9.c | 15 ++ .../c-c++-common/zero-scratch-regs-attr-usages.c | 10 + .../gcc.target/i386/zero-scratch-regs-1.c | 12 + .../gcc.target/i386/zero-scratch-regs-10.c | 21 ++ .../gcc.target/i386/zero-scratch-regs-11.c | 39 +++ .../gcc.target/i386/zero-scratch-regs-12.c | 39 +++ .../gcc.target/i386/zero-scratch-regs-13.c | 21 ++ .../gcc.target/i386/zero-scratch-regs-14.c | 19 ++ .../gcc.target/i386/zero-scratch-regs-15.c | 14 + .../gcc.target/i386/zero-scratch-regs-16.c | 14 + .../gcc.target/i386/zero-scratch-regs-17.c | 13 + .../gcc.target/i386/zero-scratch-regs-18.c | 13 + .../gcc.target/i386/zero-scratch-regs-19.c | 12 + .../gcc.target/i386/zero-scratch-regs-2.c | 19 ++ .../gcc.target/i386/zero-scratch-regs-20.c | 23 ++ .../gcc.target/i386/zero-scratch-regs-21.c | 14 + .../gcc.target/i386/zero-scratch-regs-22.c | 21 ++ .../gcc.target/i386/zero-scratch-regs-23.c | 29 ++ .../gcc.target/i386/zero-scratch-regs-24.c | 10 + .../gcc.target/i386/zero-scratch-regs-25.c | 10 + .../gcc.target/i386/zero-scratch-regs-26.c | 23 ++ .../gcc.target/i386/zero-scratch-regs-27.c | 15 ++ .../gcc.target/i386/zero-scratch-regs-28.c | 16 ++ .../gcc.target/i386/zero-scratch-regs-29.c | 10 + .../gcc.target/i386/zero-scratch-regs-3.c | 12 + .../gcc.target/i386/zero-scratch-regs-30.c | 12 + .../gcc.target/i386/zero-scratch-regs-31.c | 12 + .../gcc.target/i386/zero-scratch-regs-4.c | 14 + .../gcc.target/i386/zero-scratch-regs-5.c | 20 ++ .../gcc.target/i386/zero-scratch-regs-6.c | 14 + .../gcc.target/i386/zero-scratch-regs-7.c | 13 + .../gcc.target/i386/zero-scratch-regs-8.c | 19 ++ .../gcc.target/i386/zero-scratch-regs-9.c | 15 ++ gcc/tree-pass.h | 1 + 67 files changed, 1729 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-1.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-10.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-11.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-2.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-3.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-4.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-5.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-6.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-7.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-8.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-9.c create mode 100644 gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c create mode 100644 gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index c779d13..1b05e8c 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -138,6 +138,8 @@ static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *); static tree handle_optimize_attribute (tree *, tree, tree, int, bool *); static tree ignore_attribute (tree *, tree, tree, int, bool *); static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *); +static tree handle_zero_call_used_regs_attribute (tree *, tree, tree, int, + bool *); static tree handle_argspec_attribute (tree *, tree, tree, int, bool *); static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *); static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *); @@ -437,6 +439,8 @@ const struct attribute_spec c_common_attribute_table[] = ignore_attribute, NULL }, { "no_split_stack", 0, 0, true, false, false, false, handle_no_split_stack_attribute, NULL }, + { "zero_call_used_regs", 1, 1, true, false, false, false, + handle_zero_call_used_regs_attribute, NULL }, /* For internal use only (marking of function arguments). The name contains a space to prevent its usage in source code. */ { "arg spec", 1, -1, true, false, false, false, @@ -4959,6 +4963,51 @@ handle_no_split_stack_attribute (tree *node, tree name, return NULL_TREE; } +/* Handle a "zero_call_used_regs" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_zero_call_used_regs_attribute (tree *node, tree name, tree args, + int ARG_UNUSED (flags), + bool *no_add_attrs) +{ + tree decl = *node; + tree id = TREE_VALUE (args); + + if (TREE_CODE (decl) != FUNCTION_DECL) + { + error_at (DECL_SOURCE_LOCATION (decl), + "%qE attribute applies only to functions", name); + *no_add_attrs = true; + } + + if (TREE_CODE (id) != STRING_CST) + { + error_at (DECL_SOURCE_LOCATION (decl), + "attribute %qE arguments not a string", name); + *no_add_attrs = true; + } + + bool found = false; + for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i) + if (strcmp (TREE_STRING_POINTER (id), + zero_call_used_regs_opts[i].name) == 0) + { + found = true; + break; + } + + if (!found) + { + error_at (DECL_SOURCE_LOCATION (decl), + "unrecognized zero_call_used_regs attribute: %qs", + TREE_STRING_POINTER (id)); + *no_add_attrs = true; + } + + return NULL_TREE; +} + /* Handle a "returns_nonnull" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/common.opt b/gcc/common.opt index 292c2de..4a13f32 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -228,6 +228,10 @@ unsigned int flag_sanitize_coverage Variable bool dump_base_name_prefixed = false +; What subset of registers should be zeroed +Variable +unsigned int flag_zero_call_used_regs + ### Driver @@ -3111,6 +3115,10 @@ fzero-initialized-in-bss Common Report Var(flag_zero_initialized_in_bss) Init(1) Put zero initialized data in the bss section. +fzero-call-used-regs= +Common Report RejectNegative Joined +Clear call-used registers upon function return. + g Common Driver RejectNegative JoinedOrMissing Generate debug information in default format. diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index f684954..4360595 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -3551,6 +3551,297 @@ ix86_function_value_regno_p (const unsigned int regno) return false; } +/* Check whether the register REGNO should be zeroed on X86. + When ALL_SSE_ZEROED is true, all SSE registers have been zeroed + together, no need to zero it again. + When NEED_ZERO_MMX is true, MMX registers should be cleared. */ + +static bool +zero_call_used_regno_p (const unsigned int regno, + bool all_sse_zeroed, + bool need_zero_mmx) +{ + return GENERAL_REGNO_P (regno) + || (!all_sse_zeroed && SSE_REGNO_P (regno)) + || MASK_REGNO_P (regno) + || (need_zero_mmx && MMX_REGNO_P (regno)); +} + +/* Return the machine_mode that is used to zero register REGNO. */ + +static machine_mode +zero_call_used_regno_mode (const unsigned int regno) +{ + /* NB: We only need to zero the lower 32 bits for integer registers + and the lower 128 bits for vector registers since destination are + zero-extended to the full register width. */ + if (GENERAL_REGNO_P (regno)) + return SImode; + else if (SSE_REGNO_P (regno)) + return V4SFmode; + else if (MASK_REGNO_P (regno)) + return HImode; + else if (MMX_REGNO_P (regno)) + return V4HImode; + else + gcc_unreachable (); +} + +/* Generate a rtx to zero all vector registers together if possible, + otherwise, return NULL. */ + +static rtx +zero_all_vector_registers (HARD_REG_SET need_zeroed_hardregs) +{ + if (!TARGET_AVX) + return NULL; + + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((IN_RANGE (regno, FIRST_SSE_REG, LAST_SSE_REG) + || (TARGET_64BIT + && (REX_SSE_REGNO_P (regno) + || (TARGET_AVX512F && EXT_REX_SSE_REGNO_P (regno))))) + && !TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + return NULL; + + return gen_avx_vzeroall (); +} + +/* Generate insns to zero all st registers together. + Return true when zeroing instructions are generated. + Assume the number of st registers that are zeroed is num_of_st, + we will emit the following sequence to zero them together: + fldz; \ + fldz; \ + ... + fldz; \ + fstp %%st(0); \ + fstp %%st(0); \ + ... + fstp %%st(0); + i.e., num_of_st fldz followed by num_of_st fstp to clear the stack + mark stack slots empty. + + How to compute the num_of_st: + There is no direct mapping from stack registers to hard register + numbers. If one stack register needs to be cleared, we don't know + where in the stack the value remains. So, if any stack register + needs to be cleared, the whole stack should be cleared. However, + x87 stack registers that hold the return value should be excluded. + x87 returns in the top (two for complex values) register, so + num_of_st should be 7/6 when x87 returns, otherwise it will be 8. */ + + +static bool +zero_all_st_registers (HARD_REG_SET need_zeroed_hardregs) +{ + unsigned int num_of_st = 0; + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((STACK_REGNO_P (regno) || MMX_REGNO_P (regno)) + && TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + { + num_of_st++; + break; + } + + if (num_of_st == 0) + return false; + + bool return_with_x87 = false; + return_with_x87 = (crtl->return_rtx + && (STACK_REG_P (crtl->return_rtx))); + + bool complex_return = false; + complex_return = (crtl->return_rtx + && COMPLEX_MODE_P (GET_MODE (crtl->return_rtx))); + + if (return_with_x87) + if (complex_return) + num_of_st = 6; + else + num_of_st = 7; + else + num_of_st = 8; + + rtx st_reg = gen_rtx_REG (XFmode, FIRST_STACK_REG); + for (unsigned int i = 0; i < num_of_st; i++) + emit_insn (gen_rtx_SET (st_reg, CONST0_RTX (XFmode))); + + for (unsigned int i = 0; i < num_of_st; i++) + { + rtx insn; + insn = emit_insn (gen_rtx_SET (st_reg, st_reg)); + add_reg_note (insn, REG_DEAD, st_reg); + } + return true; +} + + +/* When the routine exit in MMX mode, if any ST register needs + to be zeroed, we should clear all MMX registers except the + RET_MMX_REGNO that holds the return value. */ +static bool +zero_all_mm_registers (HARD_REG_SET need_zeroed_hardregs, + unsigned int ret_mmx_regno) +{ + bool need_zero_all_mm = false; + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (STACK_REGNO_P (regno) + && TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + { + need_zero_all_mm = true; + break; + } + + if (!need_zero_all_mm) + return false; + + rtx zero_mmx = NULL_RTX; + machine_mode mode = V4HImode; + for (unsigned int regno = FIRST_MMX_REG; regno <= LAST_MMX_REG; regno++) + if (regno != ret_mmx_regno) + { + rtx reg = gen_rtx_REG (mode, regno); + if (zero_mmx == NULL_RTX) + { + zero_mmx = reg; + emit_insn (gen_rtx_SET (reg, CONST0_RTX (mode))); + } + else + emit_move_insn (reg, zero_mmx); + } + return true; +} + +/* TARGET_ZERO_CALL_USED_REGS. */ +/* Generate a sequence of instructions that zero registers specified by + NEED_ZEROED_HARDREGS. Return the ZEROED_HARDREGS that are actually + zeroed. */ +static HARD_REG_SET +ix86_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs) +{ + HARD_REG_SET zeroed_hardregs; + bool all_sse_zeroed = false; + bool all_st_zeroed = false; + bool all_mm_zeroed = false; + + CLEAR_HARD_REG_SET (zeroed_hardregs); + + /* first, let's see whether we can zero all vector registers together. */ + rtx zero_all_vec_insn = zero_all_vector_registers (need_zeroed_hardregs); + if (zero_all_vec_insn) + { + emit_insn (zero_all_vec_insn); + all_sse_zeroed = true; + } + + /* mm/st registers are shared registers set, we should follow the following + rules to clear them: + MMX exit mode x87 exit mode + -------------|----------------------|--------------- + uses x87 reg | clear all MMX | clear all x87 + uses MMX reg | clear individual MMX | clear all x87 + x87 + MMX | clear all MMX | clear all x87 + + first, we should decide which mode (MMX mode or x87 mode) the function + exit with. */ + + bool exit_with_mmx_mode = (crtl->return_rtx + && (MMX_REG_P (crtl->return_rtx))); + + if (!exit_with_mmx_mode) + /* x87 exit mode, we should zero all st registers together. */ + { + all_st_zeroed = zero_all_st_registers (need_zeroed_hardregs); + if (all_st_zeroed) + SET_HARD_REG_BIT (zeroed_hardregs, FIRST_STACK_REG); + } + else + /* MMX exit mode, check whether we can zero all mm registers. */ + { + unsigned int exit_mmx_regno = REGNO (crtl->return_rtx); + all_mm_zeroed = zero_all_mm_registers (need_zeroed_hardregs, + exit_mmx_regno); + if (all_mm_zeroed) + for (unsigned int regno = FIRST_MMX_REG; regno <= LAST_MMX_REG; regno++) + if (regno != exit_mmx_regno) + SET_HARD_REG_BIT (zeroed_hardregs, regno); + } + + /* Now, generate instructions to zero all the other registers. */ + + rtx zero_gpr = NULL_RTX; + rtx zero_vector = NULL_RTX; + rtx zero_mask = NULL_RTX; + rtx zero_mmx = NULL_RTX; + + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (!TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + continue; + if (!zero_call_used_regno_p (regno, all_sse_zeroed, + exit_with_mmx_mode && !all_mm_zeroed)) + continue; + + SET_HARD_REG_BIT (zeroed_hardregs, regno); + + rtx reg, tmp, zero_rtx; + machine_mode mode = zero_call_used_regno_mode (regno); + + reg = gen_rtx_REG (mode, regno); + zero_rtx = CONST0_RTX (mode); + + if (mode == SImode) + if (zero_gpr == NULL_RTX) + { + zero_gpr = reg; + tmp = gen_rtx_SET (reg, zero_rtx); + if (!TARGET_USE_MOV0 || optimize_insn_for_size_p ()) + { + rtx clob = gen_rtx_CLOBBER (VOIDmode, + gen_rtx_REG (CCmode, + FLAGS_REG)); + tmp = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, + tmp, + clob)); + } + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_gpr); + else if (mode == V4SFmode) + if (zero_vector == NULL_RTX) + { + zero_vector = reg; + tmp = gen_rtx_SET (reg, zero_rtx); + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_vector); + else if (mode == HImode) + if (zero_mask == NULL_RTX) + { + zero_mask = reg; + tmp = gen_rtx_SET (reg, zero_rtx); + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_mask); + else if (mode == V4HImode) + if (zero_mmx == NULL_RTX) + { + zero_mmx = reg; + tmp = gen_rtx_SET (reg, zero_rtx); + emit_insn (tmp); + } + else + emit_move_insn (reg, zero_mmx); + else + gcc_unreachable (); + } + return zeroed_hardregs; +} + /* Define how to find the value returned by a function. VALTYPE is the data type of the value (as a tree). If the precise function being called is known, FUNC is its FUNCTION_DECL; @@ -23229,6 +23520,9 @@ ix86_run_selftests (void) #undef TARGET_FUNCTION_VALUE_REGNO_P #define TARGET_FUNCTION_VALUE_REGNO_P ix86_function_value_regno_p +#undef TARGET_ZERO_CALL_USED_REGS +#define TARGET_ZERO_CALL_USED_REGS ix86_zero_call_used_regs + #undef TARGET_PROMOTE_FUNCTION_MODE #define TARGET_PROMOTE_FUNCTION_MODE ix86_promote_function_mode diff --git a/gcc/df-scan.c b/gcc/df-scan.c index 93b060f..9e75c13 100644 --- a/gcc/df-scan.c +++ b/gcc/df-scan.c @@ -3614,6 +3614,14 @@ df_update_entry_block_defs (void) } +/* Return true if REGNO is used by the epilogue. */ +bool +df_epilogue_uses_p (unsigned int regno) +{ + return (EPILOGUE_USES (regno) + || TEST_HARD_REG_BIT (crtl->must_be_zero_on_return, regno)); +} + /* Set the bit for regs that are considered being used at the exit. */ static void @@ -3661,7 +3669,7 @@ df_get_exit_block_use_set (bitmap exit_block_uses) epilogue as being live at the end of the function since they may be referenced by our caller. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i] || EPILOGUE_USES (i)) + if (global_regs[i] || df_epilogue_uses_p (i)) bitmap_set_bit (exit_block_uses, i); if (targetm.have_epilogue () && epilogue_completed) @@ -3802,7 +3810,6 @@ df_hard_reg_init (void) initialized = true; } - /* Recompute the parts of scanning that are based on regs_ever_live because something changed in that array. */ @@ -3862,7 +3869,6 @@ df_regs_ever_live_p (unsigned int regno) return regs_ever_live[regno]; } - /* Set regs_ever_live[REGNO] to VALUE. If this cause regs_ever_live to change, schedule that change for the next update. */ diff --git a/gcc/df.h b/gcc/df.h index 8b6ca8c..0f098d7 100644 --- a/gcc/df.h +++ b/gcc/df.h @@ -1085,6 +1085,7 @@ extern void df_update_entry_exit_and_calls (void); extern bool df_hard_reg_used_p (unsigned int); extern unsigned int df_hard_reg_used_count (unsigned int); extern bool df_regs_ever_live_p (unsigned int); +extern bool df_epilogue_uses_p (unsigned int); extern void df_set_regs_ever_live (unsigned int, bool); extern void df_compute_regs_ever_live (bool); extern void df_scan_verify (void); diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c9f7299..b011c17 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3992,6 +3992,96 @@ performing a link with relocatable output (i.e.@: @code{ld -r}) on them. A declaration to which @code{weakref} is attached and that is associated with a named @code{target} must be @code{static}. +@item zero_call_used_regs ("@var{choice}") +@cindex @code{zero_call_used_regs} function attribute + +The @code{zero_call_used_regs} attribute causes the compiler to zero +a subset of all call-used registers at function return according to +@var{choice}. +This is used to increase the program security by either mitigating +Return-Oriented Programming (ROP) or preventing information leak +through registers. + +A ``call-used'' register is a register whose contents can be changed by +a function call; therefore, a caller cannot assume that the register has +the same contents on return from the function as it had before calling +the function. Such registers are also called ``call-clobbered'', +``caller-saved'', or ``volatile''. + +In order to satisfy users with different security needs and control the +run-time overhead at the same time, GCC provides a flexible way to choose +the subset of the call-used registers to be zeroed. + +The three basic values of @var{choice} are: + +@itemize @bullet +@item +@samp{skip} doesn't zero any call-used registers. + +@item +@samp{used} only zeros call-used registers that are used in the function. +A ``used'' register is one whose content has been set or referenced in +the function. + +@item +@samp{all} zeros all call-used registers. +@end itemize + +In addition to these three basic choices, it is possible to modify +@samp{used} or @samp{all} as follows: + +@itemize @bullet +@item +Adding @samp{-gpr} restricts the zeroing to general-purpose registers. + +@item +Adding @samp{-arg} restricts the zeroing to registers that can sometimes +be used to pass function arguments. This includes all argument registers +defined by the platform's calling conversion, regardless of whether the +function uses those registers for function arguments or not. +@end itemize + +The modifiers can be used individually or together. If they are used +together, they must appear in the order above. + +The full list of @var{choice}s is therefore: + +@itemize @bullet +@item +@samp{skip} doesn't zero any call-used register. + +@item +@samp{used} only zeros call-used registers that are used in the function. + +@item +@samp{all} zeros all call-used registers. + +@item +@samp{used-arg} only zeros used call-used registers that pass arguments. + +@item +@samp{used-gpr} only zeros used call-used general purpose registers. + +@item +@samp{used-gpr-arg} only zeros used call-used general purpose registers that +pass arguments. + +@item +@samp{all-gpr-arg} zeros all call-used general purpose registers that pass +arguments. + +@item +@samp{all-arg} zeros all call-used registers that pass arguments. + +@item +@samp{all-gpr} zeros all call-used general purpose registers. +@end itemize + +Among this list, @samp{used-gpr-arg}, @samp{used-arg}, @samp{all-gpr-arg}, +and @samp{all-arg} are mainly used for ROP mitigation. + +The default for the attribute is controlled by @option{-fzero-call-used-regs}. + @end table @c This is the end of the target-independent attribute table diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index c049932..c9e3128 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -550,7 +550,7 @@ Objective-C and Objective-C++ Dialects}. -funit-at-a-time -funroll-all-loops -funroll-loops @gol -funsafe-math-optimizations -funswitch-loops @gol -fipa-ra -fvariable-expansion-in-unroller -fvect-cost-model -fvpt @gol --fweb -fwhole-program -fwpa -fuse-linker-plugin @gol +-fweb -fwhole-program -fwpa -fuse-linker-plugin -fzero-call-used-regs @gol --param @var{name}=@var{value} -O -O0 -O1 -O2 -O3 -Os -Ofast -Og} @@ -12550,6 +12550,19 @@ int foo (void) Not all targets support this option. +@item -fzero-call-used-regs=@var{choice} +@opindex fzero-call-used-regs +Zero call-used registers at function return to increase the program +security by either mitigating Return-Oriented Programming (ROP) or +preventing information leak through registers. + +The possible values of @var{choice} are the same as for the +@code{zero_call_used_regs} attribute (@pxref{Function Attributes}). +The default is @samp{skip}. + +You can control this behavior for a specific function by using the function +attribute @code{zero_call_used_regs} (@pxref{Function Attributes}). + @item --param @var{name}=@var{value} @opindex param In some places, GCC uses various constants to control the amount of diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 97437e8..18f8ad9 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -12053,6 +12053,18 @@ argument list due to stack realignment. Return @code{NULL} if no DRAP is needed. @end deftypefn +@deftypefn {Target Hook} HARD_REG_SET TARGET_ZERO_CALL_USED_REGS (HARD_REG_SET @var{selected_regs}) +This target hook emits instructions to zero the subset of @var{selected_regs} +that could conceivably contain values that are useful to an attacker. +Return the set of registers that were actually cleared. + +The default implementation uses normal move instructions to zero +all the registers in @var{selected_regs}. Define this hook if the +target has more efficient ways of zeroing certain registers, +or if you believe that certain registers would never contain +values that are useful to an attacker. +@end deftypefn + @deftypefn {Target Hook} bool TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS (void) When optimization is disabled, this hook indicates whether or not arguments should be allocated to stack slots. Normally, GCC allocates diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index 412e22c..a67dbea 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -8111,6 +8111,8 @@ and the associated definitions of those functions. @hook TARGET_GET_DRAP_RTX +@hook TARGET_ZERO_CALL_USED_REGS + @hook TARGET_ALLOCATE_STACK_SLOTS_FOR_ARGS @hook TARGET_CONST_ANCHOR diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h index 92ad0dd6..d7bdb66 100644 --- a/gcc/emit-rtl.h +++ b/gcc/emit-rtl.h @@ -173,6 +173,9 @@ struct GTY(()) rtl_data { local stack. */ unsigned int stack_alignment_estimated; + /* How to zero call-used regsiters for this routine. */ + unsigned int zero_call_used_regs; + /* How many NOP insns to place at each function entry by default. */ unsigned short patch_area_size; @@ -310,6 +313,9 @@ struct GTY(()) rtl_data { sets them. */ HARD_REG_SET asm_clobbers; + /* All hard registers that need to be zeroed at the return of the routine. */ + HARD_REG_SET must_be_zero_on_return; + /* The highest address seen during shorten_branches. */ int max_insn_address; }; diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 852ea76..8807635 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -20,6 +20,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_FLAG_TYPES_H #define GCC_FLAG_TYPES_H +#if !defined(IN_LIBGCC2) && !defined(IN_TARGET_LIBS) && !defined(IN_RTS) + enum debug_info_type { NO_DEBUG, /* Write no debug info. */ @@ -285,6 +287,24 @@ enum sanitize_code { | SANITIZE_BOUNDS_STRICT }; +/* Different settings for zeroing subset of registers. */ +namespace zero_regs_code { + const unsigned int UNSET = 0; + const unsigned int SKIP = 1UL << 0; + const unsigned int ONLY_USED = 1UL << 1; + const unsigned int ONLY_GPR = 1UL << 2; + const unsigned int ONLY_ARG = 1UL << 3; + const unsigned int ENABLED = 1UL << 4; + const unsigned int USED_GPR_ARG = ENABLED | ONLY_USED | ONLY_GPR | ONLY_ARG; + const unsigned int USED_GPR = ENABLED | ONLY_USED | ONLY_GPR; + const unsigned int USED_ARG = ENABLED | ONLY_USED | ONLY_ARG; + const unsigned int USED = ENABLED | ONLY_USED; + const unsigned int ALL_GPR_ARG = ENABLED | ONLY_GPR | ONLY_ARG; + const unsigned int ALL_GPR = ENABLED | ONLY_GPR; + const unsigned int ALL_ARG = ENABLED | ONLY_ARG; + const unsigned int ALL = ENABLED; +} + /* Settings of flag_incremental_link. */ enum incremental_link { INCREMENTAL_LINK_NONE, @@ -382,4 +402,6 @@ enum parloops_schedule_type PARLOOPS_SCHEDULE_RUNTIME }; +#endif + #endif /* ! GCC_FLAG_TYPES_H */ diff --git a/gcc/function.c b/gcc/function.c index c612959..77f0b5d 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -46,10 +46,12 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "expmed.h" #include "optabs.h" +#include "opts.h" #include "regs.h" #include "emit-rtl.h" #include "recog.h" #include "rtl-error.h" +#include "hard-reg-set.h" #include "alias.h" #include "fold-const.h" #include "stor-layout.h" @@ -5815,6 +5817,101 @@ make_prologue_seq (void) return seq; } +/* Emit a sequence of insns to zero the call-used registers before RET. */ +using namespace zero_regs_code; + +static void +gen_call_used_regs_seq (rtx_insn *ret) +{ + bool gpr_only = true; + bool used_only = true; + bool arg_only = true; + + /* No need to zero call-used-regs in main (). */ + if (MAIN_NAME_P (DECL_NAME (current_function_decl))) + return; + + /* No need to zero call-used-regs if __builtin_eh_return is called + since it isn't a normal function return. */ + if (crtl->calls_eh_return) + return; + + /* If gpr_only is true, only zero call-used registers that are + general-purpose registers; if used_only is true, only zero + call-used registers that are used in the current function; + if arg_only is true, only zero call-used registers that pass + parameters defined by the flatform's calling conversion. */ + + gpr_only = crtl->zero_call_used_regs & ONLY_GPR; + used_only = crtl->zero_call_used_regs & ONLY_USED; + arg_only = crtl->zero_call_used_regs & ONLY_ARG; + + /* For each of the hard registers, we should zero it if: + 1. it is a call-used register; + and 2. it is not a fixed register; + and 3. it is not live at the return of the routine; + and 4. it is general registor if gpr_only is true; + and 5. it is used in the routine if used_only is true; + and 6. it is a register that passes parameter if arg_only is true. */ + + /* First, prepare the data flow information. */ + basic_block bb = BLOCK_FOR_INSN (ret); + auto_bitmap live_out; + bitmap_copy (live_out, df_get_live_out (bb)); + df_simulate_initialize_backwards (bb, live_out); + df_simulate_one_insn_backwards (bb, ret, live_out); + + HARD_REG_SET selected_hardregs; + CLEAR_HARD_REG_SET (selected_hardregs); + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if (!crtl->abi->clobbers_full_reg_p (regno)) + continue; + if (fixed_regs[regno]) + continue; + if (REGNO_REG_SET_P (live_out, regno)) + continue; + if (gpr_only + && !TEST_HARD_REG_BIT (reg_class_contents[GENERAL_REGS], regno)) + continue; + if (used_only && !df_regs_ever_live_p (regno)) + continue; + if (arg_only && !FUNCTION_ARG_REGNO_P (regno)) + continue; + + /* Now this is a register that we might want to zero. */ + SET_HARD_REG_BIT (selected_hardregs, regno); + } + + if (hard_reg_set_empty_p (selected_hardregs)) + return; + + /* Now that we have a hard register set that needs to be zeroed, pass it to + target to generate zeroing sequence. */ + HARD_REG_SET zeroed_hardregs; + start_sequence (); + zeroed_hardregs = targetm.calls.zero_call_used_regs (selected_hardregs); + rtx_insn *seq = get_insns (); + end_sequence (); + if (seq) + { + /* Emit the memory blockage and register clobber asm volatile before + the whole sequence. */ + start_sequence (); + expand_asm_reg_clobber_mem_blockage (zeroed_hardregs); + rtx_insn *seq_barrier = get_insns (); + end_sequence (); + + emit_insn_before (seq_barrier, ret); + emit_insn_before (seq, ret); + + /* Update the data flow information. */ + crtl->must_be_zero_on_return |= zeroed_hardregs; + df_set_bb_dirty (EXIT_BLOCK_PTR_FOR_FN (cfun)); + } +} + + /* Return a sequence to be used as the epilogue for the current function, or NULL. */ @@ -6486,7 +6583,100 @@ make_pass_thread_prologue_and_epilogue (gcc::context *ctxt) { return new pass_thread_prologue_and_epilogue (ctxt); } - + +namespace { + +const pass_data pass_data_zero_call_used_regs = +{ + RTL_PASS, /* type */ + "zero_call_used_regs", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_zero_call_used_regs: public rtl_opt_pass +{ +public: + pass_zero_call_used_regs (gcc::context *ctxt) + : rtl_opt_pass (pass_data_zero_call_used_regs, ctxt) + {} + + /* opt_pass methods: */ + virtual unsigned int execute (function *); + +}; // class pass_zero_call_used_regs + +unsigned int +pass_zero_call_used_regs::execute (function *fun) +{ + unsigned int zero_regs_type = UNSET; + unsigned int attr_zero_regs_type = UNSET; + + tree attr_zero_regs + = lookup_attribute ("zero_call_used_regs", + DECL_ATTRIBUTES (fun->decl)); + + /* Get the type of zero_call_used_regs from function attribute. */ + if (attr_zero_regs) + { + /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE + is the attribute argument's value. */ + attr_zero_regs = TREE_VALUE (attr_zero_regs); + gcc_assert (TREE_CODE (attr_zero_regs) == TREE_LIST); + attr_zero_regs = TREE_VALUE (attr_zero_regs); + gcc_assert (TREE_CODE (attr_zero_regs) == STRING_CST); + + for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i) + if (strcmp (TREE_STRING_POINTER (attr_zero_regs), + zero_call_used_regs_opts[i].name) == 0) + { + attr_zero_regs_type = zero_call_used_regs_opts[i].flag; + break; + } + } + + zero_regs_type = attr_zero_regs_type; + + if (!zero_regs_type) + zero_regs_type = flag_zero_call_used_regs; + + crtl->zero_call_used_regs = zero_regs_type; + + /* No need to zero call-used-regs when no user request is present. */ + if (!(zero_regs_type & ENABLED)) + return 0; + + edge_iterator ei; + edge e; + + /* This pass needs data flow information. */ + df_analyze (); + + /* Iterate over the function's return instructions and insert any + register zeroing required by the -fzero-call-used-regs command-line + option or the "zero_call_used_regs" function attribute. */ + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + { + rtx_insn *insn = BB_END (e->src); + if (JUMP_P (insn) && ANY_RETURN_P (JUMP_LABEL (insn))) + gen_call_used_regs_seq (insn); + } + + return 0; +} + +} // anon namespace + +rtl_opt_pass * +make_pass_zero_call_used_regs (gcc::context *ctxt) +{ + return new pass_zero_call_used_regs (ctxt); +} /* If CONSTRAINT is a matching constraint, then return its number. Otherwise, return -1. */ diff --git a/gcc/optabs.c b/gcc/optabs.c index 8ad7f4b..1820b91 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -6484,6 +6484,48 @@ expand_memory_blockage (void) expand_asm_memory_blockage (); } +/* Generate asm volatile("" : : : "memory") as a memory blockage, at the + same time clobbering the register set specified by REGS. */ + +void +expand_asm_reg_clobber_mem_blockage (HARD_REG_SET regs) +{ + rtx asm_op, clob_mem; + + unsigned int num_of_regs = 0; + for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (TEST_HARD_REG_BIT (regs, i)) + num_of_regs++; + + asm_op = gen_rtx_ASM_OPERANDS (VOIDmode, "", "", 0, + rtvec_alloc (0), rtvec_alloc (0), + rtvec_alloc (0), UNKNOWN_LOCATION); + MEM_VOLATILE_P (asm_op) = 1; + + rtvec v = rtvec_alloc (num_of_regs + 2); + + clob_mem = gen_rtx_SCRATCH (VOIDmode); + clob_mem = gen_rtx_MEM (BLKmode, clob_mem); + clob_mem = gen_rtx_CLOBBER (VOIDmode, clob_mem); + + RTVEC_ELT (v, 0) = asm_op; + RTVEC_ELT (v, 1) = clob_mem; + + if (num_of_regs > 0) + { + unsigned int j = 2; + for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; i++) + if (TEST_HARD_REG_BIT (regs, i)) + { + RTVEC_ELT (v, j) = gen_rtx_CLOBBER (VOIDmode, regno_reg_rtx[i]); + j++; + } + gcc_assert (j == (num_of_regs + 2)); + } + + emit_insn (gen_rtx_PARALLEL (VOIDmode, v)); +} + /* This routine will either emit the mem_thread_fence pattern or issue a sync_synchronize to generate a fence for memory model MEMMODEL. */ diff --git a/gcc/optabs.h b/gcc/optabs.h index 0b14700..bfa10c8 100644 --- a/gcc/optabs.h +++ b/gcc/optabs.h @@ -345,6 +345,8 @@ rtx expand_atomic_store (rtx, rtx, enum memmodel, bool); rtx expand_atomic_fetch_op (rtx, rtx, rtx, enum rtx_code, enum memmodel, bool); +extern void expand_asm_reg_clobber_mem_blockage (HARD_REG_SET); + extern bool insn_operand_matches (enum insn_code icode, unsigned int opno, rtx operand); extern bool valid_multiword_target_p (rtx); diff --git a/gcc/opts.c b/gcc/opts.c index 3bda59a..7cd097a 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1776,6 +1776,25 @@ const struct sanitizer_opts_s coverage_sanitizer_opts[] = { NULL, 0U, 0UL, false } }; +using namespace zero_regs_code; +/* -fzero-call-used-regs= suboptions. */ +const struct zero_call_used_regs_opts_s zero_call_used_regs_opts[] = +{ +#define ZERO_CALL_USED_REGS_OPT(name, flags) \ + { #name, flags } + ZERO_CALL_USED_REGS_OPT (skip, SKIP), + ZERO_CALL_USED_REGS_OPT (used-gpr-arg, USED_GPR_ARG), + ZERO_CALL_USED_REGS_OPT (used-gpr, USED_GPR), + ZERO_CALL_USED_REGS_OPT (used-arg, USED_ARG), + ZERO_CALL_USED_REGS_OPT (used, USED), + ZERO_CALL_USED_REGS_OPT (all-gpr-arg, ALL_GPR_ARG), + ZERO_CALL_USED_REGS_OPT (all-gpr, ALL_GPR), + ZERO_CALL_USED_REGS_OPT (all-arg, ALL_ARG), + ZERO_CALL_USED_REGS_OPT (all, ALL), +#undef ZERO_CALL_USED_REGS_OPT + {NULL, 0U} +}; + /* A struct for describing a run of chars within a string. */ class string_fragment @@ -1970,6 +1989,27 @@ parse_no_sanitize_attribute (char *value) return flags; } +/* Parse -fzero-call-used-regs suboptions from ARG, return the FLAGS. */ + +unsigned int +parse_zero_call_used_regs_options (const char *arg) +{ + unsigned int flags = 0; + + /* Check to see if the string matches a sub-option name. */ + for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i) + if (strcmp (arg, zero_call_used_regs_opts[i].name) == 0) + { + flags = zero_call_used_regs_opts[i].flag; + break; + } + + if (!flags) + error ("unrecognized argument to %<-fzero-call-used-regs=%>: %qs", arg); + + return flags; +} + /* Parse -falign-NAME format for a FLAG value. Return individual parsed integer values into RESULT_VALUES array. If REPORT_ERROR is set, print error message at LOC location. */ @@ -2601,6 +2641,11 @@ common_handle_option (struct gcc_options *opts, /* Automatically sets -ftree-loop-vectorize and -ftree-slp-vectorize. Nothing more to do here. */ break; + case OPT_fzero_call_used_regs_: + opts->x_flag_zero_call_used_regs + = parse_zero_call_used_regs_options (arg); + break; + case OPT_fshow_column: dc->show_column = value; break; diff --git a/gcc/opts.h b/gcc/opts.h index 8f594b4..7d1e126 100644 --- a/gcc/opts.h +++ b/gcc/opts.h @@ -444,6 +444,12 @@ extern const struct sanitizer_opts_s bool can_recover; } sanitizer_opts[]; +extern const struct zero_call_used_regs_opts_s +{ + const char *const name; + unsigned int flag; +} zero_call_used_regs_opts[]; + extern vec help_option_arguments; extern void add_misspelling_candidates (auto_vec *candidates, diff --git a/gcc/passes.def b/gcc/passes.def index f865bdc..77d4676 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -492,6 +492,7 @@ along with GCC; see the file COPYING3. If not see POP_INSERT_PASSES () NEXT_PASS (pass_late_compilation); PUSH_INSERT_PASSES_WITHIN (pass_late_compilation) + NEXT_PASS (pass_zero_call_used_regs); NEXT_PASS (pass_compute_alignments); NEXT_PASS (pass_variable_tracking); NEXT_PASS (pass_free_cfg); diff --git a/gcc/recog.c b/gcc/recog.c index ce83b7f..b458746 100644 --- a/gcc/recog.c +++ b/gcc/recog.c @@ -922,7 +922,23 @@ validate_simplify_insn (rtx_insn *insn) } return ((num_changes_pending () > 0) && (apply_change_group () > 0)); } - + +/* Check whether INSN matches a specific alternative of an .md pattern. */ + +bool +valid_insn_p (rtx_insn *insn) +{ + recog_memoized (insn); + if (INSN_CODE (insn) < 0) + return false; + extract_insn (insn); + /* We don't know whether the insn will be in code that is optimized + for size or speed, so consider all enabled alternatives. */ + if (!constrain_operands (1, get_enabled_alternatives (insn))) + return false; + return true; +} + /* Return 1 if OP is a valid general operand for machine mode MODE. This is either a register reference, a memory reference, or a constant. In the case of a memory reference, the address diff --git a/gcc/recog.h b/gcc/recog.h index ae3675f..d87456c 100644 --- a/gcc/recog.h +++ b/gcc/recog.h @@ -113,6 +113,7 @@ extern void validate_replace_src_group (rtx, rtx, rtx_insn *); extern bool validate_simplify_insn (rtx_insn *insn); extern int num_changes_pending (void); extern bool reg_fits_class_p (const_rtx, reg_class_t, int, machine_mode); +extern bool valid_insn_p (rtx_insn *); extern int offsettable_memref_p (rtx); extern int offsettable_nonstrict_memref_p (rtx); diff --git a/gcc/resource.c b/gcc/resource.c index 0a9d594..90cf091 100644 --- a/gcc/resource.c +++ b/gcc/resource.c @@ -1186,7 +1186,7 @@ init_resource_info (rtx_insn *epilogue_insn) &end_of_function_needs, true); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i] || EPILOGUE_USES (i)) + if (global_regs[i] || df_epilogue_uses_p (i)) SET_HARD_REG_BIT (end_of_function_needs.regs, i); /* The registers required to be live at the end of the function are diff --git a/gcc/target.def b/gcc/target.def index ed2da15..b916635 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -5080,6 +5080,21 @@ argument list due to stack realignment. Return @code{NULL} if no DRAP\n\ is needed.", rtx, (void), NULL) +/* Generate instruction sequence to zero call used registers. */ +DEFHOOK +(zero_call_used_regs, + "This target hook emits instructions to zero the subset of @var{selected_regs}\n\ +that could conceivably contain values that are useful to an attacker.\n\ +Return the set of registers that were actually cleared.\n\ +\n\ +The default implementation uses normal move instructions to zero\n\ +all the registers in @var{selected_regs}. Define this hook if the\n\ +target has more efficient ways of zeroing certain registers,\n\ +or if you believe that certain registers would never contain\n\ +values that are useful to an attacker.", + HARD_REG_SET, (HARD_REG_SET selected_regs), +default_zero_call_used_regs) + /* Return true if all function parameters should be spilled to the stack. */ DEFHOOK diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 5d94fce..88eef00 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -56,6 +56,9 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa-alias.h" #include "gimple-expr.h" #include "memmodel.h" +#include "backend.h" +#include "emit-rtl.h" +#include "df.h" #include "tm_p.h" #include "stringpool.h" #include "tree-vrp.h" @@ -987,6 +990,35 @@ default_function_value_regno_p (const unsigned int regno ATTRIBUTE_UNUSED) #endif } +/* The default hook for TARGET_ZERO_CALL_USED_REGS. */ + +HARD_REG_SET +default_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs) +{ + gcc_assert (!hard_reg_set_empty_p (need_zeroed_hardregs)); + + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) + { + rtx_insn *last_insn = get_last_insn (); + machine_mode mode = GET_MODE (regno_reg_rtx[regno]); + rtx zero = CONST0_RTX (mode); + rtx_insn *insn = emit_move_insn (regno_reg_rtx[regno], zero); + if (!valid_insn_p (insn)) + { + static bool issued_error; + if (!issued_error) + { + issued_error = true; + sorry ("%qs not supported on this target", + "fzero-call-used_regs"); + } + delete_insns_since (last_insn); + } + } + return need_zeroed_hardregs; +} + rtx default_internal_arg_pointer (void) { diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 44ab926..e0a925f 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -160,6 +160,7 @@ extern unsigned int default_function_arg_round_boundary (machine_mode, const_tree); extern bool hook_bool_const_rtx_commutative_p (const_rtx, int); extern rtx default_function_value (const_tree, const_tree, bool); +extern HARD_REG_SET default_zero_call_used_regs (HARD_REG_SET); extern rtx default_libcall_value (machine_mode, const_rtx); extern bool default_function_value_regno_p (const unsigned int); extern rtx default_internal_arg_pointer (void); diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c new file mode 100644 index 0000000..2463353 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-1.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-10.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-10.c new file mode 100644 index 0000000..bdaf8e7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-10.c @@ -0,0 +1,92 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#include +int result = 0; + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("skip"))) +foo1 (int x) +{ + return (x + 1); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used-gpr-arg"))) +foo2 (int x) +{ + return (x + 2); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used-gpr"))) +foo3 (int x) +{ + return (x + 3); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used-arg"))) +foo4 (int x) +{ + return (x + 4); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used"))) +foo5 (int x) +{ + return (x + 5); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all-gpr-arg"))) +foo6 (int x) +{ + return (x + 6); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all-gpr"))) +foo7 (int x) +{ + return (x + 7); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all-arg"))) +foo8 (int x) +{ + return (x + 8); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all"))) +foo9 (int x) +{ + return (x + 9); +} + +int main() +{ + result = foo1 (1); + result += foo2 (1); + result += foo3 (1); + result += foo4 (1); + result += foo5 (1); + result += foo6 (1); + result += foo7 (1); + result += foo8 (1); + result += foo9 (1); + assert (result == 54); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c new file mode 100644 index 0000000..6756f57 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-11.c @@ -0,0 +1,92 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +#include +int result = 0; + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("skip"))) +foo1 (int x) +{ + return (x + 1); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used-gpr-arg"))) +foo2 (int x) +{ + return (x + 2); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used-gpr"))) +foo3 (int x) +{ + return (x + 3); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used-arg"))) +foo4 (int x) +{ + return (x + 4); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("used"))) +foo5 (int x) +{ + return (x + 5); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all-gpr-arg"))) +foo6 (int x) +{ + return (x + 6); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all-gpr"))) +foo7 (int x) +{ + return (x + 7); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all-arg"))) +foo8 (int x) +{ + return (x + 8); +} + +int +__attribute__((noipa)) +__attribute__ ((zero_call_used_regs("all"))) +foo9 (int x) +{ + return (x + 9); +} + +int main() +{ + result = foo1 (1); + result += foo2 (1); + result += foo3 (1); + result += foo4 (1); + result += foo5 (1); + result += foo6 (1); + result += foo7 (1); + result += foo8 (1); + result += foo9 (1); + assert (result == 54); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c new file mode 100644 index 0000000..73c3794 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-2.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr-arg" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c new file mode 100644 index 0000000..d3db757 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-3.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c new file mode 100644 index 0000000..029aa79 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-4.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-arg" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c new file mode 100644 index 0000000..509ce20 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-5.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=used" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c new file mode 100644 index 0000000..56d8063 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-6.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr-arg" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c new file mode 100644 index 0000000..40378ed --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-7.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c new file mode 100644 index 0000000..7bdef95 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-8.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-arg" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c new file mode 100644 index 0000000..032da1a --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-9.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +volatile int result = 0; +int +__attribute__((noipa)) +foo (int x) +{ + return x; +} +int main() +{ + result = foo (2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c new file mode 100644 index 0000000..c60e946 --- /dev/null +++ b/gcc/testsuite/c-c++-common/zero-scratch-regs-attr-usages.c @@ -0,0 +1,10 @@ +/* { dg-do compile} */ +/* { dg-options "-O2" } */ + +int result __attribute__ ((zero_call_used_regs("all"))); /* { dg-error "attribute applies only to functions" } */ +int +__attribute__ ((zero_call_used_regs("gpr-arg-all"))) +foo1 (int x) /* { dg-error "unrecognized zero_call_used_regs attribute" } */ +{ + return (x + 1); +} diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c new file mode 100644 index 0000000..9f61dc4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-1.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c new file mode 100644 index 0000000..09048e5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-10.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +extern int foo (int) __attribute__ ((zero_call_used_regs("all-gpr"))); + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c new file mode 100644 index 0000000..4862688 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-11.c @@ -0,0 +1,39 @@ +/* { dg-do run { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */ + +struct S { int i; }; +__attribute__((const, noinline, noclone)) +struct S foo (int x) +{ + struct S s; + s.i = x; + return s; +} + +int a[2048], b[2048], c[2048], d[2048]; +struct S e[2048]; + +__attribute__((noinline, noclone)) void +bar (void) +{ + int i; + for (i = 0; i < 1024; i++) + { + e[i] = foo (i); + a[i+2] = a[i] + a[i+1]; + b[10] = b[10] + i; + c[i] = c[2047 - i]; + d[i] = d[i + 1]; + } +} + +int +main () +{ + int i; + bar (); + for (i = 0; i < 1024; i++) + if (e[i].i != i) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c new file mode 100644 index 0000000..500251b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-12.c @@ -0,0 +1,39 @@ +/* { dg-do run { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +struct S { int i; }; +__attribute__((const, noinline, noclone)) +struct S foo (int x) +{ + struct S s; + s.i = x; + return s; +} + +int a[2048], b[2048], c[2048], d[2048]; +struct S e[2048]; + +__attribute__((noinline, noclone)) void +bar (void) +{ + int i; + for (i = 0; i < 1024; i++) + { + e[i] = foo (i); + a[i+2] = a[i] + a[i+1]; + b[10] = b[10] + i; + c[i] = c[2047 - i]; + d[i] = d[i + 1]; + } +} + +int +main () +{ + int i; + bar (); + for (i = 0; i < 1024; i++) + if (e[i].i != i) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c new file mode 100644 index 0000000..8b058e3 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-13.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { target { ia32 } } } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 15 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c new file mode 100644 index 0000000..d4eaaf7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-14.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-times "vzeroall" 1 } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c new file mode 100644 index 0000000..dd3bb90 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-15.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +extern void foo (void) __attribute__ ((zero_call_used_regs("used"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c new file mode 100644 index 0000000..e2274f6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-16.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +extern void foo (void) __attribute__ ((zero_call_used_regs("skip"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c new file mode 100644 index 0000000..7f5d153 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-17.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c new file mode 100644 index 0000000..fe13d2b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-18.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used -march=corei7" } */ + +float +foo (float z, float y, float x) +{ + return x + y; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c new file mode 100644 index 0000000..205a532 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-19.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used -march=corei7" } */ + +float +foo (float z, float y, float x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm2, %xmm2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c new file mode 100644 index 0000000..e046684 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-2.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c new file mode 100644 index 0000000..4be8ff6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-20.c @@ -0,0 +1,23 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7" } */ + +float +foo (float z, float y, float x) +{ + return x + y; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" { target { ia32 } } } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm0, %xmm\[0-9\]+" 7 { target { ia32 } } } } */ +/* { dg-final { scan-assembler-times "movaps\[ \t\]*%xmm1, %xmm\[0-9\]+" 14 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c new file mode 100644 index 0000000..0eb34e0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-21.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip -march=corei7" } */ + +__attribute__ ((zero_call_used_regs("used"))) +float +foo (float z, float y, float x) +{ + return x + y; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm1, %xmm1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm1, %xmm2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c new file mode 100644 index 0000000..0258c70 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-22.c @@ -0,0 +1,21 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler "vzeroall" } } */ +/* { dg-final { scan-assembler-times "fldz" 8 } } */ +/* { dg-final { scan-assembler-times "fstp" 8 } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c new file mode 100644 index 0000000..0625eb5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-23.c @@ -0,0 +1,29 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all -march=corei7 -mavx512f" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler "vzeroall" } } */ +/* { dg-final { scan-assembler-times "fldz" 8 } } */ +/* { dg-final { scan-assembler-times "fstp" 8 } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kxorw\[ \t\]*%k0, %k0, %k0" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k1" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k2" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k3" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k4" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k5" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k6" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "kmovw\[ \t\]*%k0, %k7" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c new file mode 100644 index 0000000..208633e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-24.c @@ -0,0 +1,10 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr-arg" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c new file mode 100644 index 0000000..21e82c6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-25.c @@ -0,0 +1,10 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-arg" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c new file mode 100644 index 0000000..293d2fe --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-26.c @@ -0,0 +1,23 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-arg" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" } } */ +/* { dg-final { scan-assembler "pxor\[ \t\]*%xmm0, %xmm0" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm1" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm2" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm3" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm4" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm5" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm6" } } */ +/* { dg-final { scan-assembler "movaps\[ \t\]*%xmm0, %xmm7" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c new file mode 100644 index 0000000..c34e6af --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-27.c @@ -0,0 +1,15 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr-arg" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c new file mode 100644 index 0000000..48b1f01 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-28.c @@ -0,0 +1,16 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mmmx -fzero-call-used-regs=all" } */ +/* { dg-require-effective-target ia32 } */ + +__v2si ret_mmx (void) +{ + return (__v2si) { 123, 345 }; +} + +/* { dg-final { scan-assembler "pxor\[ \t\]*%mm1, %mm1" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm2" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm3" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm4" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm5" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm6" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm7" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c new file mode 100644 index 0000000..8b5e1cd --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-29.c @@ -0,0 +1,10 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +long double ret_x87 (void) +{ + return 1.1L; +} + +/* { dg-final { scan-assembler-times "fldz" 7 } } */ +/* { dg-final { scan-assembler-times "fstp" 7 } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c new file mode 100644 index 0000000..de71223 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-3.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c new file mode 100644 index 0000000..2a6f38f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-30.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all" } */ + +_Complex long double ret_x87_cplx (void) +{ + return 1.1L + 1.2iL; +} + +/* { dg-final { scan-assembler-times "fldz" 8 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "fstp" 8 { target ia32 } } } */ +/* { dg-final { scan-assembler-times "fldz" 6 { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-times "fstp" 6 { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c new file mode 100644 index 0000000..df71286 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-31.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -mmmx -fzero-call-used-regs=all-arg" } */ +/* { dg-require-effective-target ia32 } */ + +__v2si ret_mmx (void) +{ + return (__v2si) { 123, 345 }; +} + +/* { dg-final { scan-assembler "pxor\[ \t\]*%mm1, %mm1" } } */ +/* { dg-final { scan-assembler "movq\[ \t\]*%mm1, %mm2" } } */ +/* { dg-final { scan-assembler-not "movq\[ \t\]*%mm1, %mm\[34567\]" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c new file mode 100644 index 0000000..ccfa441 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-4.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +extern void foo (void) __attribute__ ((zero_call_used_regs("used-gpr"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c new file mode 100644 index 0000000..6b46ca3 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-5.c @@ -0,0 +1,20 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +__attribute__ ((zero_call_used_regs("all-gpr"))) +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%eax, %eax" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%eax, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c new file mode 100644 index 0000000..0680f38 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-6.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +extern void foo (void) __attribute__ ((zero_call_used_regs("skip"))); + +void +foo (void) +{ +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*%" } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c new file mode 100644 index 0000000..534defa --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-7.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=used-gpr" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c new file mode 100644 index 0000000..477bb19 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-8.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=all-gpr" } */ + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edx, %edx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %ecx" } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %esi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %edi" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r8d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r9d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r10d" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*%edx, %r11d" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c new file mode 100644 index 0000000..a305a60 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/zero-scratch-regs-9.c @@ -0,0 +1,15 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fzero-call-used-regs=skip" } */ + +extern int foo (int) __attribute__ ((zero_call_used_regs("used-gpr"))); + +int +foo (int x) +{ + return x; +} + +/* { dg-final { scan-assembler-not "vzeroall" } } */ +/* { dg-final { scan-assembler-not "%xmm" } } */ +/* { dg-final { scan-assembler-not "xorl\[ \t\]*%" { target ia32 } } } */ +/* { dg-final { scan-assembler "xorl\[ \t\]*%edi, %edi" { target { ! ia32 } } } } */ diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 62e5b69..8afe8ee 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -592,6 +592,7 @@ extern rtl_opt_pass *make_pass_gcse2 (gcc::context *ctxt); extern rtl_opt_pass *make_pass_split_after_reload (gcc::context *ctxt); extern rtl_opt_pass *make_pass_thread_prologue_and_epilogue (gcc::context *ctxt); +extern rtl_opt_pass *make_pass_zero_call_used_regs (gcc::context *ctxt); extern rtl_opt_pass *make_pass_stack_adjustments (gcc::context *ctxt); extern rtl_opt_pass *make_pass_sched_fusion (gcc::context *ctxt); extern rtl_opt_pass *make_pass_peephole2 (gcc::context *ctxt);