From patchwork Mon Aug 5 10:28:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Schulze Frielinghaus X-Patchwork-Id: 1969016 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.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=FptFXDGB; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Wct4G63FGz1yZb for ; Mon, 5 Aug 2024 20:30:22 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 13D543858431 for ; Mon, 5 Aug 2024 10:30:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 13D543858431 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1722853819; bh=fh8o+Amo8bp2dOJhor6VqMA/Gf3zZhODdfLO+8JDnXI=; h=From:To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From; b=FptFXDGBrgqOPGnf2t+D+QrM8g72hghGJ04vtv+yHRXpuL/tsyh3QzwmOEZ3iDes7 u8d9abu1RZard/42A4NHXSuIDRJV/CLkdR+/qBZeAsxUok/WWPWv9VDbeuVJy5jGOc AZOxiWINgTO8kyvyNuXql/R41mEgf5DOBKFZaVDc= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mx0b-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) by sourceware.org (Postfix) with ESMTPS id 9B1B3385840F; Mon, 5 Aug 2024 10:29:33 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9B1B3385840F Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gcc.gnu.org Authentication-Results: sourceware.org; spf=fail smtp.mailfrom=gcc.gnu.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 9B1B3385840F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=148.163.158.5 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722853780; cv=none; b=NuyzFpLVGQaISoaghoFYOThxmGBI1RNFXcHeFaTg9P8PYNP8WwKPlFo7VlZuvT0RVaRAEm3VYlofK3ZuvN2P9Vhs5yxvn5RF1S1Pb936Cp7vxjXZnsmXtlOKUDz9fCzOwUxG4MpbCENwO1vtFLyyHz7gWI0IjAxBPM/GN5IJbkQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722853780; c=relaxed/simple; bh=Azho7D35gWYR8j2a6+ov+Oxq/ne5rbBMNjmKgVJczbw=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=M1GDL9L5IOBnMA0uGE4juhxjS7Na+UVxxjLsCMmUsCX0Wlx6yia2qSKMSFGJ12cymZn8kG+gWr1lZXlCORRhn8Ilmf0pnrh7leEefd7OxYOX5gV4LBznMicPCaLkeALqs8ytswM53SReVDgNiSa4TuQlGj5zukDn9R4WXr1KXWU= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from pps.filterd (m0353724.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4756SmgB031975; Mon, 5 Aug 2024 10:29:33 GMT Received: from pps.reinject (localhost [127.0.0.1]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 40tsge8ewg-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 05 Aug 2024 10:29:32 +0000 (GMT) Received: from m0353724.ppops.net (m0353724.ppops.net [127.0.0.1]) by pps.reinject (8.18.0.8/8.18.0.8) with ESMTP id 475ATWAQ018151; Mon, 5 Aug 2024 10:29:32 GMT Received: from ppma22.wdc07v.mail.ibm.com (5c.69.3da9.ip4.static.sl-reverse.com [169.61.105.92]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 40tsge8ewf-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 05 Aug 2024 10:29:32 +0000 (GMT) Received: from pps.filterd (ppma22.wdc07v.mail.ibm.com [127.0.0.1]) by ppma22.wdc07v.mail.ibm.com (8.17.1.19/8.17.1.19) with ESMTP id 4756e1wO024386; Mon, 5 Aug 2024 10:29:31 GMT Received: from smtprelay03.fra02v.mail.ibm.com ([9.218.2.224]) by ppma22.wdc07v.mail.ibm.com (PPS) with ESMTPS id 40sy90e1w5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 05 Aug 2024 10:29:31 +0000 Received: from smtpav06.fra02v.mail.ibm.com (smtpav06.fra02v.mail.ibm.com [10.20.54.105]) by smtprelay03.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 475ATRTC51053050 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 5 Aug 2024 10:29:29 GMT Received: from smtpav06.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 22AB52004B; Mon, 5 Aug 2024 10:29:27 +0000 (GMT) Received: from smtpav06.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id E41AC20040; Mon, 5 Aug 2024 10:29:26 +0000 (GMT) Received: from a8345010.lnxne.boe (unknown [9.152.108.100]) by smtpav06.fra02v.mail.ibm.com (Postfix) with ESMTPS; Mon, 5 Aug 2024 10:29:26 +0000 (GMT) From: Stefan Schulze Frielinghaus To: gcc-patches@gcc.gnu.org Cc: Paul Koning , Georg-Johann Lay , Stefan Schulze Frielinghaus Subject: [PATCH v2] Hard register constraints Date: Mon, 5 Aug 2024 12:28:58 +0200 Message-ID: <20240805102857.2829431-2-stefansf@gcc.gnu.org> X-Mailer: git-send-email 2.45.2 X-TM-AS-GCONF: 00 X-Proofpoint-GUID: 0M3XFkKx8bYaCoL_GgBedKlIQD4caKzd X-Proofpoint-ORIG-GUID: 5I_CVVBP_IDoMoWOg827Rok1CNkWgP78 X-Proofpoint-UnRewURL: 0 URL was un-rewritten MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.28.16 definitions=2024-08-04_14,2024-08-02_01,2024-05-17_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 spamscore=0 adultscore=0 clxscore=1034 mlxscore=0 bulkscore=0 phishscore=0 suspectscore=0 impostorscore=0 lowpriorityscore=0 malwarescore=0 mlxlogscore=999 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2407110000 definitions=main-2408050070 X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_DMARC_STATUS, KAM_SHORT, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NEUTRAL, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org This is a follow-up of https://gcc.gnu.org/pipermail/gcc-patches/2024-June/654013.html What has changed? - Rebased and fixed an issue in constrain_operands which manifested after late-combine. - Introduced new test cases for Arm, Intel, POWER, RISCV, S/390 for 32- and 64-bit where appropriate (including register pairs etc.). Test gcc.dg/asm-hard-reg-7.c is a bit controversial since I'm testing for an anti feature here, i.e., I'm testing for register asm in conjunction with calls. I'm fine with removing it in the end but I wanted to keep it in for demonstration purposes at least during discussion of this patch. - Split test pr87600-2.c into pr87600-2.c and pr87600-3.c since test0 errors out early, now. Otherwise, the remaining errors would not be reported. Beside that the error message has slightly changed. - Modified genoutput.cc in order to allow hard register constraints in machine descriptions. For example, on s390 the instruction mvcrl makes use of the implicit register r0 which we currently deal with as follows: (define_insn "*mvcrl" [(set (match_operand:BLK 0 "memory_operand" "=Q") (unspec:BLK [(match_operand:BLK 1 "memory_operand" "Q") (reg:SI GPR0_REGNUM)] UNSPEC_MVCRL))] "TARGET_Z15" "mvcrl\t%0,%1" [(set_attr "op_type" "SSE")]) (define_expand "mvcrl" [(set (reg:SI GPR0_REGNUM) (match_operand:SI 2 "general_operand")) (set (match_operand:BLK 0 "memory_operand" "=Q") (unspec:BLK [(match_operand:BLK 1 "memory_operand" "Q") (reg:SI GPR0_REGNUM)] UNSPEC_MVCRL))] "TARGET_Z15" "") In the expander we ensure that GPR0 is setup correctly. With this patch we could simply write (define_insn "mvcrl" [(set (match_operand:BLK 0 "memory_operand" "=Q") (unspec:BLK [(match_operand:BLK 1 "memory_operand" "Q") (match_operand:SI 2 "general_operand" "{r0}")] UNSPEC_MVCRL))] "TARGET_Z15" "mvcrl\t%0,%1" [(set_attr "op_type" "SSE")]) What I dislike is that I didn't find a way to verify hard register names during genoutput, i.e., ensuring that the name is valid after all. This is due to the fact how reg_names is defined which cannot be accessed by genoutput. The same holds true for REGISTER_NAMES et al. which may reference some target specific variable (see e.g. POWER). Thus, in case of an invalid register name in a machine description file we do not end-up with a genoutput-time error but instead fail at run-time in process_alt_operands(): case '{': { int regno = parse_constraint_regname (p); gcc_assert (regno >= 0); cl = REGNO_REG_CLASS (regno); CLEAR_HARD_REG_SET (hregset); SET_HARD_REG_BIT (hregset, regno); cl_filter = &hregset; goto reg; } This is rather unfortunate but I couldn't find a way how to validate register names during genoutput. If no one else has an idea I will replace gcc_assert with a more expressive error message. What's next? I was thinking about replacing register asm with the new hard register constraint. This would solve problems like demonstrated by gcc.dg/asm-hard-reg-7.c. For example, we could replace the constraint register int x asm ("r5") = 42; asm ("foo %0" :: "r" (x)); with register int x asm ("r5") = 42; asm ("foo %0" :: "{r5}" (x)); and ignore any further effect of the register asm. However, I haven't really thought this through and there are certainly cases which are currently allowed which cannot trivially be converted as e.g. here: register int x asm ("r5") = 42; asm ("foo %0" :: "rd" (x)); Multiple alternatives are kind of strange in combination with register asm. For example, on s390 the two constraints "r" and "d" restrict both to GPRs. That is not a show stopper but certainly something which needs some consideration. If you can think of some wild combinations/edge cases I would be happy to here about. Anyhow, this is something for a further patch. Last but not least, if there is enough consent to accept this feature, I will start writing up some documentation. Bootstrapped and regtested on Arm, Intel, POWER, RISCV, S/390. I have only verified the 32-bit tests via cross compilers and didn't execute them in contrast to 64-bit targets. --- gcc/cfgexpand.cc | 42 ----- gcc/genoutput.cc | 12 ++ gcc/genpreds.cc | 4 +- gcc/gimplify.cc | 134 ++++++++++++++- gcc/lra-constraints.cc | 13 ++ gcc/recog.cc | 11 +- gcc/stmt.cc | 155 +++++++++++++++++- gcc/stmt.h | 12 +- gcc/testsuite/gcc.dg/asm-hard-reg-1.c | 85 ++++++++++ gcc/testsuite/gcc.dg/asm-hard-reg-2.c | 33 ++++ gcc/testsuite/gcc.dg/asm-hard-reg-3.c | 25 +++ gcc/testsuite/gcc.dg/asm-hard-reg-4.c | 50 ++++++ gcc/testsuite/gcc.dg/asm-hard-reg-5.c | 36 ++++ gcc/testsuite/gcc.dg/asm-hard-reg-6.c | 60 +++++++ gcc/testsuite/gcc.dg/asm-hard-reg-7.c | 70 ++++++++ gcc/testsuite/gcc.dg/asm-hard-reg-error-1.c | 67 ++++++++ gcc/testsuite/gcc.dg/asm-hard-reg-error-2.c | 19 +++ gcc/testsuite/gcc.dg/asm-hard-reg-error-3.c | 20 +++ gcc/testsuite/gcc.dg/pr87600-2.c | 30 +--- gcc/testsuite/gcc.dg/pr87600-3.c | 35 ++++ .../gcc.target/s390/asm-hard-reg-1.c | 103 ++++++++++++ .../gcc.target/s390/asm-hard-reg-2.c | 43 +++++ .../gcc.target/s390/asm-hard-reg-3.c | 42 +++++ gcc/testsuite/lib/scanasm.exp | 4 + 24 files changed, 1020 insertions(+), 85 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-1.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-2.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-3.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-4.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-5.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-6.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-7.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-error-1.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-error-2.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-error-3.c create mode 100644 gcc/testsuite/gcc.dg/pr87600-3.c create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc index dad3ae1b7c6..8bdc530626c 100644 --- a/gcc/cfgexpand.cc +++ b/gcc/cfgexpand.cc @@ -2966,44 +2966,6 @@ expand_asm_loc (tree string, int vol, location_t locus) emit_insn (body); } -/* Return the number of times character C occurs in string S. */ -static int -n_occurrences (int c, const char *s) -{ - int n = 0; - while (*s) - n += (*s++ == c); - return n; -} - -/* A subroutine of expand_asm_operands. Check that all operands have - the same number of alternatives. Return true if so. */ - -static bool -check_operand_nalternatives (const vec &constraints) -{ - unsigned len = constraints.length(); - if (len > 0) - { - int nalternatives = n_occurrences (',', constraints[0]); - - if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES) - { - error ("too many alternatives in %"); - return false; - } - - for (unsigned i = 1; i < len; ++i) - if (n_occurrences (',', constraints[i]) != nalternatives) - { - error ("operand constraints for % differ " - "in number of alternatives"); - return false; - } - } - return true; -} - /* Check for overlap between registers marked in CLOBBERED_REGS and anything inappropriate in T. Emit error and return the register variable definition for error, NULL_TREE for ok. */ @@ -3169,10 +3131,6 @@ expand_asm_stmt (gasm *stmt) = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t))); } - /* ??? Diagnose during gimplification? */ - if (! check_operand_nalternatives (constraints)) - return; - /* Count the number of meaningful clobbered registers, ignoring what we would ignore later. */ auto_vec clobber_rvec; diff --git a/gcc/genoutput.cc b/gcc/genoutput.cc index efd81766bb5..c1efb043579 100644 --- a/gcc/genoutput.cc +++ b/gcc/genoutput.cc @@ -1219,6 +1219,18 @@ mdep_constraint_len (const char *s, file_location loc, int opno) if (!strncmp (s, p->name, p->namelen)) return p->namelen; + if (*s == '{') + { + const char *end = s + 1; + while (*end != '}' && *end != '"' && *end != '\0') + ++end; + /* Similarly as in parse_constraint_regname(), consider any hard register + name longer than a few characters as an error. */ + ptrdiff_t len = end - s; + if (*end == '}' && len > 1 && len < 31) + return len + 1; + } + error_at (loc, "error: undefined machine-specific constraint " "at this point: \"%s\"", s); message_at (loc, "note: in operand %d", opno); diff --git a/gcc/genpreds.cc b/gcc/genpreds.cc index 55d149e8a40..0777cb7a4db 100644 --- a/gcc/genpreds.cc +++ b/gcc/genpreds.cc @@ -1148,7 +1148,7 @@ write_insn_constraint_len (void) unsigned int i; puts ("static inline size_t\n" - "insn_constraint_len (char fc, const char *str ATTRIBUTE_UNUSED)\n" + "insn_constraint_len (char fc, const char *str)\n" "{\n" " switch (fc)\n" " {"); @@ -1181,6 +1181,8 @@ write_insn_constraint_len (void) puts (" default: break;\n" " }\n" + " if (str[0] == '{')\n" + " return ((const char *) rawmemchr (str + 1, '}') - str) + 1;\n" " return 1;\n" "}\n"); } diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index ab323d764e8..abb705d49ac 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -70,6 +70,9 @@ along with GCC; see the file COPYING3. If not see #include "omp-offload.h" #include "context.h" #include "tree-nested.h" +#include "insn-config.h" +#include "recog.h" +#include "output.h" /* Identifier for a basic condition, mapping it to other basic conditions of its Boolean expression. Basic conditions given the same uid (in the same @@ -6993,6 +6996,42 @@ gimplify_addr_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) return ret; } +/* Return the number of times character C occurs in string S. */ + +static int +num_occurrences (int c, const char *s) +{ + int n = 0; + while (*s) + n += (*s++ == c); + return n; +} + +/* A subroutine of gimplify_asm_expr. Check that all operands have + the same number of alternatives. Return -1 if this is violated. Otherwise + return the number of alternatives. */ + +static int +num_alternatives (const_tree link) +{ + if (link == nullptr) + return 0; + + const char *constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); + int num = num_occurrences (',', constraint); + + if (num + 1 > MAX_RECOG_ALTERNATIVES) + return -1; + + for (link = TREE_CHAIN (link); link; link = TREE_CHAIN (link)) + { + constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); + if (num_occurrences (',', constraint) != num) + return -1; + } + return num + 1; +} + /* Gimplify the operands of an ASM_EXPR. Input operands should be a gimple value; output operands should be a gimple lvalue. */ @@ -7023,6 +7062,36 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) clobbers = NULL; labels = NULL; + int num_alternatives_out = num_alternatives (ASM_OUTPUTS (expr)); + int num_alternatives_in = num_alternatives (ASM_INPUTS (expr)); + if (num_alternatives_out == -1 || num_alternatives_in == -1 + || (num_alternatives_out > 0 && num_alternatives_in > 0 + && num_alternatives_out != num_alternatives_in)) + { + error ("operand constraints for % differ " + "in number of alternatives"); + return GS_ERROR; + } + int num_alternatives = MAX (num_alternatives_out, num_alternatives_in); + + /* Regarding hard register constraints ensure that each hard register is used + at most once over all inputs/outputs and each alternative. Keep track in + hardregs[0] which hard register is used via an asm register over all + inputs/outputs. hardregs[i] for i >= 2 describes which hard registers are + used for alternative i-2 over all inputs/outputs. hardregs[1] is a + reduction of all alternatives, i.e., hardregs[1] |= hardregs[i] for i >= 2 + and describes whether a hard register is used in any alternative. This is + just a shortcut instead of recomputing the union over all alternatives; + possibly multiple times. */ + auto_vec hardregs (num_alternatives + 2); + std::pair *, machine_mode> hardreg_props = {&hardregs, VOIDmode}; + for (int i = 0; i < num_alternatives + 2; ++i) + { + HARD_REG_SET hregset; + CLEAR_HARD_REG_SET (hregset); + hardregs.quick_push (hregset); + } + ret = GS_ALL_DONE; link_next = NULL_TREE; for (i = 0, link = ASM_OUTPUTS (expr); link; ++i, link = link_next) @@ -7039,8 +7108,13 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) if (constraint_len == 0) continue; - ok = parse_output_constraint (&constraint, i, 0, 0, - &allows_mem, &allows_reg, &is_inout); + tree outtype = TREE_TYPE (TREE_VALUE (link)); + auto hardreg_props_p + = outtype != error_mark_node + ? (hardreg_props.second = TYPE_MODE (outtype), &hardreg_props) + : nullptr; + ok = parse_output_constraint (&constraint, i, 0, 0, &allows_mem, + &allows_reg, &is_inout, hardreg_props_p); if (!ok) { ret = GS_ERROR; @@ -7049,7 +7123,6 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) /* If we can't make copies, we can only accept memory. Similarly for VLAs. */ - tree outtype = TREE_TYPE (TREE_VALUE (link)); if (outtype != error_mark_node && (TREE_ADDRESSABLE (outtype) || !COMPLETE_TYPE_P (outtype) @@ -7111,6 +7184,24 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) TREE_VALUE (link) = tem; tret = GS_OK; } + if (VAR_P (op) && DECL_HARD_REGISTER (op)) + { + tree id = DECL_ASSEMBLER_NAME (op); + const char *asmspec = IDENTIFIER_POINTER (id) + 1; + int hardreg = decode_reg_name (asmspec); + if (hardreg >= 0) + { + if (TEST_HARD_REG_BIT (hardregs[0], hardreg) + || TEST_HARD_REG_BIT (hardregs[1], hardreg)) + { + error ("multiple outputs to hard register: %s", + reg_names[hardreg]); + return GS_ERROR; + } + else + SET_HARD_REG_BIT (hardregs[0], hardreg); + } + } } vec_safe_push (outputs, link); @@ -7210,16 +7301,29 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) } } + for (unsigned int i = 0; i < hardregs.length (); ++i) + CLEAR_HARD_REG_SET (hardregs[i]); + link_next = NULL_TREE; for (link = ASM_INPUTS (expr); link; ++i, link = link_next) { link_next = TREE_CHAIN (link); constraint = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); - parse_input_constraint (&constraint, 0, 0, noutputs, 0, - oconstraints, &allows_mem, &allows_reg); + tree intype = TREE_TYPE (TREE_VALUE (link)); + auto hardreg_props_p + = intype != error_mark_node + ? (hardreg_props.second = TYPE_MODE (intype), &hardreg_props) + : nullptr; + bool ok = parse_input_constraint (&constraint, 0, 0, noutputs, 0, + oconstraints, &allows_mem, &allows_reg, + hardreg_props_p); + if (!ok) + { + ret = GS_ERROR; + is_inout = false; + } /* If we can't make copies, we can only accept memory. */ - tree intype = TREE_TYPE (TREE_VALUE (link)); if (intype != error_mark_node && (TREE_ADDRESSABLE (intype) || !COMPLETE_TYPE_P (intype) @@ -7290,6 +7394,24 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) is_gimple_asm_val, fb_rvalue); if (tret == GS_ERROR) ret = tret; + tree inputv = TREE_VALUE (link); + if (VAR_P (inputv) && DECL_HARD_REGISTER (inputv)) + { + tree id = DECL_ASSEMBLER_NAME (inputv); + const char *asmspec = IDENTIFIER_POINTER (id) + 1; + int hardreg = decode_reg_name (asmspec); + if (hardreg >= 0) + { + if (TEST_HARD_REG_BIT (hardregs[1], hardreg)) + { + error ("multiple inputs to hard register: %s", + reg_names[hardreg]); + return GS_ERROR; + } + else + SET_HARD_REG_BIT (hardregs[0], hardreg); + } + } } TREE_CHAIN (link) = NULL_TREE; diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc index 92b343fa99a..632c75ef01c 100644 --- a/gcc/lra-constraints.cc +++ b/gcc/lra-constraints.cc @@ -114,6 +114,7 @@ #include "target.h" #include "rtl.h" #include "tree.h" +#include "stmt.h" #include "predict.h" #include "df.h" #include "memmodel.h" @@ -2165,6 +2166,7 @@ process_alt_operands (int only_alternative) bool costly_p; enum reg_class cl; const HARD_REG_SET *cl_filter; + HARD_REG_SET hregset; /* Calculate some data common for all alternatives to speed up the function. */ @@ -2536,6 +2538,17 @@ process_alt_operands (int only_alternative) cl_filter = nullptr; goto reg; + case '{': + { + int regno = decode_hreg_constraint (p); + gcc_assert (regno >= 0); + cl = REGNO_REG_CLASS (regno); + CLEAR_HARD_REG_SET (hregset); + SET_HARD_REG_BIT (hregset, regno); + cl_filter = &hregset; + goto reg; + } + default: cn = lookup_constraint (p); switch (get_constraint_type (cn)) diff --git a/gcc/recog.cc b/gcc/recog.cc index 54b317126c2..b604029d5f1 100644 --- a/gcc/recog.cc +++ b/gcc/recog.cc @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see #include "target.h" #include "rtl.h" #include "tree.h" +#include "stmt.h" #include "cfghooks.h" #include "df.h" #include "memmodel.h" @@ -2333,7 +2334,8 @@ asm_operand_ok (rtx op, const char *constraint, const char **constraints) { case CT_REGISTER: if (!result - && reg_class_for_constraint (cn) != NO_REGS + && (reg_class_for_constraint (cn) != NO_REGS + || constraint[0] == '{') && GET_MODE (op) != BLKmode && register_operand (op, VOIDmode)) result = 1; @@ -3267,6 +3269,13 @@ constrain_operands (int strict, alternative_mask alternatives) win = true; break; + case '{': + if ((REG_P (op) && HARD_REGISTER_P (op) + && (int) REGNO (op) == decode_hreg_constraint (p)) + || !reload_completed) + win = true; + break; + default: { enum constraint_num cn = lookup_constraint (p); diff --git a/gcc/stmt.cc b/gcc/stmt.cc index ae1527f0a19..7b073f8ce85 100644 --- a/gcc/stmt.cc +++ b/gcc/stmt.cc @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see #include "emit-rtl.h" #include "pretty-print.h" #include "diagnostic-core.h" +#include "output.h" #include "fold-const.h" #include "varasm.h" @@ -174,6 +175,77 @@ expand_label (tree label) maybe_set_first_label_num (label_r); } +/* Parse a hard register constraint and return its number or -1 in case of an + error. BEGIN should point to a string of the form "{regname}". For the + sake of simplicity assume that a register name is not longer than 31 + characters, if not error out. */ + +int +decode_hreg_constraint (const char *begin) +{ + if (*begin != '{') + return -1; + ++begin; + const char *end = begin; + while (*end != '}' && *end != '\0') + ++end; + if (*end != '}' || end == begin) + return -1; + ptrdiff_t len = end - begin; + if (len >= 31) + return -1; + char regname[32]; + memcpy (regname, begin, len); + regname[len] = '\0'; + int regno = decode_reg_name (regname); + return regno; +} + +static bool +eliminable_regno_p (int regnum) +{ + static const struct + { + const int from; + const int to; + } eliminables[] = ELIMINABLE_REGS; + for (size_t i = 0; i < ARRAY_SIZE (eliminables); i++) + if (regnum == eliminables[i].from) + return true; + return false; +} + +/* Perform a similar check as done in make_decl_rtl(). */ + +static bool +hardreg_ok_p (int reg_number, machine_mode mode, int operand_num) +{ + if (mode == BLKmode) + error ("data type isn%'t suitable for register %s of operand %i", + reg_names[reg_number], operand_num); + else if (!in_hard_reg_set_p (accessible_reg_set, mode, reg_number)) + error ("register %s for operand %i cannot be accessed" + " by the current target", reg_names[reg_number], operand_num); + else if (!in_hard_reg_set_p (operand_reg_set, mode, reg_number)) + error ("register %s for operand %i is not general enough" + " to be used as a register variable", reg_names[reg_number], operand_num); + else if (!targetm.hard_regno_mode_ok (reg_number, mode)) + error ("register %s for operand %i isn%'t suitable for data type", + reg_names[reg_number], operand_num); + else if (reg_number != HARD_FRAME_POINTER_REGNUM + && (reg_number == FRAME_POINTER_REGNUM +#ifdef RETURN_ADDRESS_POINTER_REGNUM + || reg_number == RETURN_ADDRESS_POINTER_REGNUM +#endif + || reg_number == ARG_POINTER_REGNUM) + && eliminable_regno_p (reg_number)) + error ("register for operand %i is an internal GCC " + "implementation detail", operand_num); + else + return true; + return false; +} + /* Parse the output constraint pointed to by *CONSTRAINT_P. It is the OPERAND_NUMth output operand, indexed from zero. There are NINPUTS inputs and NOUTPUTS outputs to this extended-asm. Upon return, @@ -190,7 +262,9 @@ expand_label (tree label) bool parse_output_constraint (const char **constraint_p, int operand_num, int ninputs, int noutputs, bool *allows_mem, - bool *allows_reg, bool *is_inout) + bool *allows_reg, bool *is_inout, + const std::pair *, machine_mode> + *hardreg_props) { const char *constraint = *constraint_p; const char *p; @@ -244,6 +318,8 @@ parse_output_constraint (const char **constraint_p, int operand_num, constraint = *constraint_p; } + unsigned int alternative = 2; + /* Loop through the constraint string. */ for (p = constraint + 1; *p; ) { @@ -268,7 +344,11 @@ parse_output_constraint (const char **constraint_p, int operand_num, case 'E': case 'F': case 'G': case 'H': case 's': case 'i': case 'n': case 'I': case 'J': case 'K': case 'L': case 'M': - case 'N': case 'O': case 'P': case ',': + case 'N': case 'O': case 'P': + break; + + case ',': + ++alternative; break; case '0': case '1': case '2': case '3': case '4': @@ -289,6 +369,36 @@ parse_output_constraint (const char **constraint_p, int operand_num, *allows_mem = true; break; + case '{': + { + int regno = decode_hreg_constraint (p); + if (regno < 0) + { + error ("invalid output constraint: %s", p); + return false; + } + if (hardreg_props) + { + vec *hardregs = hardreg_props->first; + if (TEST_HARD_REG_BIT ((*hardregs)[0], regno) + || TEST_HARD_REG_BIT ((*hardregs)[alternative], regno)) + { + error ("multiple outputs to hard register: %s", + reg_names[regno]); + return false; + } + else + { + SET_HARD_REG_BIT ((*hardregs)[1], regno); + SET_HARD_REG_BIT ((*hardregs)[alternative], regno); + } + if (!hardreg_ok_p (regno, hardreg_props->second, operand_num)) + return false; + } + *allows_reg = true; + break; + } + default: if (!ISALPHA (*p)) break; @@ -317,7 +427,9 @@ bool parse_input_constraint (const char **constraint_p, int input_num, int ninputs, int noutputs, int ninout, const char * const * constraints, - bool *allows_mem, bool *allows_reg) + bool *allows_mem, bool *allows_reg, + const std::pair *, machine_mode> + *hardreg_props) { const char *constraint = *constraint_p; const char *orig_constraint = constraint; @@ -332,6 +444,8 @@ parse_input_constraint (const char **constraint_p, int input_num, /* Make sure constraint has neither `=', `+', nor '&'. */ + unsigned int alternative = 2; + for (j = 0; j < c_len; j += CONSTRAINT_LEN (constraint[j], constraint+j)) switch (constraint[j]) { @@ -358,7 +472,11 @@ parse_input_constraint (const char **constraint_p, int input_num, case 'E': case 'F': case 'G': case 'H': case 's': case 'i': case 'n': case 'I': case 'J': case 'K': case 'L': case 'M': - case 'N': case 'O': case 'P': case ',': + case 'N': case 'O': case 'P': + break; + + case ',': + ++alternative; break; /* Whether or not a numeric constraint allows a register is @@ -408,6 +526,35 @@ parse_input_constraint (const char **constraint_p, int input_num, *allows_mem = true; break; + case '{': + { + int regno = decode_hreg_constraint (constraint + j); + if (regno < 0) + { + error ("invalid input constraint: %s", constraint + j); + return false; + } + if (hardreg_props) + { + vec *hardregs = hardreg_props->first; + if (TEST_HARD_REG_BIT ((*hardregs)[0], regno) + || TEST_HARD_REG_BIT ((*hardregs)[alternative], regno)) + { + error ("multiple inputs to hard register: %s", + reg_names[regno]); + } + else + { + SET_HARD_REG_BIT ((*hardregs)[1], regno); + SET_HARD_REG_BIT ((*hardregs)[alternative], regno); + } + if (!hardreg_ok_p (regno, hardreg_props->second, input_num)) + return false; + } + *allows_reg = true; + break; + } + default: if (! ISALPHA (constraint[j])) { diff --git a/gcc/stmt.h b/gcc/stmt.h index a2caae7121b..a380ecd8cbf 100644 --- a/gcc/stmt.h +++ b/gcc/stmt.h @@ -20,11 +20,19 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_STMT_H #define GCC_STMT_H +#include "target.h" +#include "hard-reg-set.h" + extern void expand_label (tree); extern bool parse_output_constraint (const char **, int, int, int, - bool *, bool *, bool *); + bool *, bool *, bool *, + const std::pair *, + machine_mode> * = nullptr); extern bool parse_input_constraint (const char **, int, int, int, int, - const char * const *, bool *, bool *); + const char * const *, bool *, bool *, + const std::pair *, + machine_mode> * = nullptr); +extern int decode_hreg_constraint (const char *); extern tree resolve_asm_operand_names (tree, tree, tree, tree); #ifdef HARD_CONST /* Silly ifdef to avoid having all includers depend on hard-reg-set.h. */ diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-1.c b/gcc/testsuite/gcc.dg/asm-hard-reg-1.c new file mode 100644 index 00000000000..6a5a9ada45f --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-1.c @@ -0,0 +1,85 @@ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* riscv*-*-* s390*-*-* x86_64-*-* } } */ + +#if defined (__aarch64__) +# define GPR "{x4}" +/* { dg-final { scan-assembler-times "foo\tx4" 8 { target { aarch64*-*-* } } } } */ +#elif defined (__arm__) +# define GPR "{r4}" +/* { dg-final { scan-assembler-times "foo\tr4" 8 { target { arm*-*-* } } } } */ +#elif defined (__i386__) +# define GPR "{ecx}" +/* { dg-final { scan-assembler-times "foo\t%cl" 2 { target { i?86-*-* } } } } */ +/* { dg-final { scan-assembler-times "foo\t%cx" 2 { target { i?86-*-* } } } } */ +/* { dg-final { scan-assembler-times "foo\t%ecx" 4 { target { i?86-*-* } } } } */ +#elif defined (__powerpc__) || defined (__POWERPC__) +# define GPR "{r5}" +/* { dg-final { scan-assembler-times "foo\t5" 8 { target { powerpc*-*-* } } } } */ +#elif defined (__riscv) +# define GPR "{t5}" +/* { dg-final { scan-assembler-times "foo\tt5" 8 { target { riscv*-*-* } } } } */ +#elif defined (__s390__) +# define GPR "{r4}" +/* { dg-final { scan-assembler-times "foo\t%r4" 8 { target { s390*-*-* } } } } */ +#elif defined (__x86_64__) +# define GPR "{rcx}" +/* { dg-final { scan-assembler-times "foo\t%cl" 2 { target { x86_64-*-* } } } } */ +/* { dg-final { scan-assembler-times "foo\t%cx" 2 { target { x86_64-*-* } } } } */ +/* { dg-final { scan-assembler-times "foo\t%ecx" 2 { target { x86_64-*-* } } } } */ +/* { dg-final { scan-assembler-times "foo\t%rcx" 2 { target { x86_64-*-* } } } } */ +#endif + +char +test_char (char x) +{ + __asm__ ("foo\t%0" : "+"GPR (x)); + return x; +} + +char +test_char_from_mem (char *x) +{ + __asm__ ("foo\t%0" : "+"GPR (*x)); + return *x; +} + +short +test_short (short x) +{ + __asm__ ("foo\t%0" : "+"GPR (x)); + return x; +} + +short +test_short_from_mem (short *x) +{ + __asm__ ("foo\t%0" : "+"GPR (*x)); + return *x; +} + +int +test_int (int x) +{ + __asm__ ("foo\t%0" : "+"GPR (x)); + return x; +} + +int +test_int_from_mem (int *x) +{ + __asm__ ("foo\t%0" : "+"GPR (*x)); + return *x; +} + +long +test_long (long x) +{ + __asm__ ("foo\t%0" : "+"GPR (x)); + return x; +} + +long +test_long_from_mem (long *x) +{ + __asm__ ("foo\t%0" : "+"GPR (*x)); + return *x; +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-2.c b/gcc/testsuite/gcc.dg/asm-hard-reg-2.c new file mode 100644 index 00000000000..7dabf9657cb --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-2.c @@ -0,0 +1,33 @@ +/* { dg-do compile { target aarch64*-*-* powerpc64*-*-* riscv64-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-std=c99" } we need long long */ + +#if defined (__aarch64__) +# define GPR "{x4}" +/* { dg-final { scan-assembler-times "foo\tx4" 2 { target { aarch64*-*-* } } } } */ +#elif defined (__powerpc__) || defined (__POWERPC__) +# define GPR "{r5}" +/* { dg-final { scan-assembler-times "foo\t5" 2 { target { powerpc64*-*-* } } } } */ +#elif defined (__riscv) +# define GPR "{t5}" +/* { dg-final { scan-assembler-times "foo\tt5" 2 { target { riscv64-*-* } } } } */ +#elif defined (__s390__) +# define GPR "{r4}" +/* { dg-final { scan-assembler-times "foo\t%r4" 2 { target { s390*-*-* } } } } */ +#elif defined (__x86_64__) +# define GPR "{rcx}" +/* { dg-final { scan-assembler-times "foo\t%rcx" 2 { target { x86_64-*-* } } } } */ +#endif + +long long +test_longlong (long long x) +{ + __asm__ ("foo\t%0" : "+"GPR (x)); + return x; +} + +long long +test_longlong_from_mem (long long *x) +{ + __asm__ ("foo\t%0" : "+"GPR (*x)); + return *x; +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-3.c b/gcc/testsuite/gcc.dg/asm-hard-reg-3.c new file mode 100644 index 00000000000..fa4472ae8a8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-3.c @@ -0,0 +1,25 @@ +/* { dg-do compile { target { { aarch64*-*-* powerpc64*-*-* riscv64-*-* s390*-*-* x86_64-*-* } && int128 } } } */ +/* { dg-options "-O2" } get rid of -ansi since we use __int128 */ + +#if defined (__aarch64__) +# define REG "{x4}" +/* { dg-final { scan-assembler-times "foo\tx4" 1 { target { aarch64*-*-* } } } } */ +#elif defined (__powerpc__) || defined (__POWERPC__) +# define REG "{r5}" +/* { dg-final { scan-assembler-times "foo\t5" 1 { target { powerpc*-*-* } } } } */ +#elif defined (__riscv) +# define REG "{t5}" +/* { dg-final { scan-assembler-times "foo\tt5" 1 { target { riscv*-*-* } } } } */ +#elif defined (__s390__) +# define REG "{r4}" +/* { dg-final { scan-assembler-times "foo\t%r4" 1 { target { s390*-*-* } } } } */ +#elif defined (__x86_64__) +# define REG "{xmm0}" +/* { dg-final { scan-assembler-times "foo\t%xmm0" 1 { target { x86_64-*-* } } } } */ +#endif + +void +test (void) +{ + __asm__ ("foo\t%0" :: REG ((__int128) 42)); +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-4.c b/gcc/testsuite/gcc.dg/asm-hard-reg-4.c new file mode 100644 index 00000000000..0816df8f719 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-4.c @@ -0,0 +1,50 @@ +/* { dg-do compile { target aarch64*-*-* arm*-*-* powerpc*-*-* riscv*-*-* s390*-*-* x86_64-*-* } } */ + +#if defined (__aarch64__) +# define FPR "{d5}" +/* { dg-final { scan-assembler-times "foo\tv5" 4 { target { aarch64*-*-* } } } } */ +#elif defined (__arm__) +# define FPR "{d5}" +/* { dg-additional-options "-march=armv7-a+fp -mfloat-abi=hard" { target arm*-*-* } } */ +/* { dg-final { scan-assembler-times "foo\ts10" 4 { target { arm*-*-* } } } } */ +#elif defined (__powerpc__) || defined (__POWERPC__) +# define FPR "{5}" +/* { dg-final { scan-assembler-times "foo\t5" 4 { target { powerpc*-*-* } } } } */ +#elif defined (__riscv) +# define FPR "{f5}" +/* { dg-final { scan-assembler-times "foo\tf5" 4 { target { rsicv*-*-* } } } } */ +#elif defined (__s390__) +# define FPR "{f5}" +/* { dg-final { scan-assembler-times "foo\t%f5" 4 { target { s390*-*-* } } } } */ +#elif defined (__x86_64__) +# define FPR "{xmm5}" +/* { dg-final { scan-assembler-times "foo\t%xmm5" 4 { target { x86_64-*-* } } } } */ +#endif + +float +test_float (float x) +{ + __asm__ ("foo\t%0" : "+"FPR (x)); + return x; +} + +float +test_float_from_mem (float *x) +{ + __asm__ ("foo\t%0" : "+"FPR (*x)); + return *x; +} + +double +test_double (double x) +{ + __asm__ ("foo\t%0" : "+"FPR (x)); + return x; +} + +double +test_double_from_mem (double *x) +{ + __asm__ ("foo\t%0" : "+"FPR (*x)); + return *x; +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-5.c b/gcc/testsuite/gcc.dg/asm-hard-reg-5.c new file mode 100644 index 00000000000..a9e25ce1746 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-5.c @@ -0,0 +1,36 @@ +/* { dg-do compile { target aarch64*-*-* powerpc64*-*-* riscv64-*-* s390*-*-* x86_64-*-* } } */ + +typedef int V __attribute__ ((vector_size (4 * sizeof (int)))); + +#if defined (__aarch64__) +# define VR "{v20}" +/* { dg-final { scan-assembler-times "foo\tv20" 2 { target { aarch64*-*-* } } } } */ +#elif defined (__powerpc__) || defined (__POWERPC__) +# define VR "{v5}" +/* { dg-final { scan-assembler-times "foo\t5" 2 { target { powerpc64*-*-* } } } } */ +#elif defined (__riscv) +# define VR "{v5}" +/* { dg-additional-options "-march=rv64imv" { target riscv64-*-* } } */ +/* { dg-final { scan-assembler-times "foo\tv5" 2 { target { riscv*-*-* } } } } */ +#elif defined (__s390__) +# define VR "{v5}" +/* { dg-require-effective-target s390_mvx { target s390*-*-* } } */ +/* { dg-final { scan-assembler-times "foo\t%v5" 2 { target s390*-*-* } } } */ +#elif defined (__x86_64__) +# define VR "{xmm9}" +/* { dg-final { scan-assembler-times "foo\t%xmm9" 2 { target { x86_64-*-* } } } } */ +#endif + +V +test (V x) +{ + __asm__ ("foo\t%0" : "+"VR (x)); + return x; +} + +V +test_from_mem (V *x) +{ + __asm__ ("foo\t%0" : "+"VR (*x)); + return *x; +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-6.c b/gcc/testsuite/gcc.dg/asm-hard-reg-6.c new file mode 100644 index 00000000000..d9b7fae8097 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-6.c @@ -0,0 +1,60 @@ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* riscv*-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-O2" } */ + +/* Test multiple alternatives. */ + +#if defined (__aarch64__) +# define GPR1 "{x1}" +# define GPR2 "{x2}" +# define GPR3 "{x3}" +/* { dg-final { scan-assembler-times "foo\tx1,x3" 1 { target { aarch64*-*-* } } } } */ +/* { dg-final { scan-assembler-times "bar\tx2,\\\[x1\\\]" 1 { target { aarch64*-*-* } } } } */ +#elif defined (__arm__) +# define GPR1 "{r1}" +# define GPR2 "{r2}" +# define GPR3 "{r3}" +/* { dg-final { scan-assembler-times "foo\tr1,r3" 1 { target { arm*-*-* } } } } */ +/* { dg-final { scan-assembler-times "bar\tr2,\\\[r1\\\]" 1 { target { arm*-*-* } } } } */ +#elif defined (__i386__) +# define GPR1 "{eax}" +# define GPR2 "{ebx}" +# define GPR3 "{ecx}" +/* { dg-final { scan-assembler-times "foo\t4\\(%esp\\),%ecx" 1 { target { i?86-*-* } } } } */ +/* { dg-final { scan-assembler-times "bar\t%ebx,\\(%eax\\)" 1 { target { i?86-*-* } } } } */ +#elif defined (__powerpc__) || defined (__POWERPC__) +# define GPR1 "{r4}" +# define GPR2 "{r5}" +# define GPR3 "{r6}" +/* { dg-final { scan-assembler-times "foo\t4,6" 1 { target { powerpc*-*-* } } } } */ +/* { dg-final { scan-assembler-times "bar\t5,0\\(4\\)" 1 { target { powerpc*-*-* } } } } */ +#elif defined (__riscv) +# define GPR1 "{t1}" +# define GPR2 "{t2}" +# define GPR3 "{t3}" +/* { dg-final { scan-assembler-times "foo\tt1,t3" 1 { target { riscv*-*-* } } } } */ +/* { dg-final { scan-assembler-times "bar\tt2,0\\(a1\\)" 1 { target { riscv*-*-* } } } } */ +#elif defined (__s390__) +# define GPR1 "{r0}" +# define GPR2 "{r1}" +# define GPR3 "{r2}" +/* { dg-final { scan-assembler-times "foo\t%r0,%r2" 1 { target { s390*-*-* } } } } */ +/* { dg-final { scan-assembler-times "bar\t%r1,0\\(%r3\\)" 1 { target { s390*-*-* } } } } */ +#elif defined (__x86_64__) +# define GPR1 "{eax}" +# define GPR2 "{ebx}" +# define GPR3 "{rcx}" +/* { dg-final { scan-assembler-times "foo\t%eax,%rcx" 1 { target { x86_64-*-* } } } } */ +/* { dg-final { scan-assembler-times "bar\t%ebx,\\(%rsi\\)" 1 { target { x86_64-*-* } } } } */ +#endif + +void +test_reg_reg (int x, long long *y) +{ + __asm__ ("foo\t%0,%1" :: GPR1"m,"GPR2 (x), GPR3",m" (y)); +} + +void +test_reg_mem (int x, long long *y) +{ + __asm__ ("bar\t%0,%1" :: GPR1"m,"GPR2 (x), GPR3",m" (*y)); +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-7.c b/gcc/testsuite/gcc.dg/asm-hard-reg-7.c new file mode 100644 index 00000000000..39c4497ecaf --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-7.c @@ -0,0 +1,70 @@ +/* { dg-do run { target aarch64*-*-* powerpc64*-*-* riscv64-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-O2" } */ + +/* Pass parameter x in the first general argument register to the assembler + instruction. + + In function bar we fail to do so because after the function call to foo, + variable argreg1 does not contain the value of x but rather 42 which got + passed to foo. Thus, the function always returns 42. In contrast in + function baz, variable x is saved over the function call and materializes in + the asm statement and therefore is returned. */ + +#if defined (__aarch64__) +# define REG register int argreg1 __asm__ ("x0") = x; +# define MOVE1 __asm__ ("mov\t%0,%1" : "=r" (out) : "r" (argreg1)); +# define MOVE2 __asm__ ("mov\t%0,%1" : "=r" (out) : "{x0}" (x)); +#elif defined (__powerpc__) || defined (__POWERPC__) +# define REG register int argreg1 __asm__ ("r3") = x; +# define MOVE1 __asm__ ("mr\t%0,%1" : "=r" (out) : "r" (argreg1)); +# define MOVE2 __asm__ ("mr\t%0,%1" : "=r" (out) : "{r3}" (x)); +#elif defined (__riscv) +# define REG register int argreg1 __asm__ ("a0") = x; +# define MOVE1 __asm__ ("mv\t%0,%1" : "=r" (out) : "r" (argreg1)); +# define MOVE2 __asm__ ("mv\t%0,%1" : "=r" (out) : "{a0}" (x)); +#elif defined (__s390__) +# define REG register int argreg1 __asm__ ("r2") = x; +# define MOVE1 __asm__ ("lr\t%0,%1" : "=r" (out) : "r" (argreg1)); +# define MOVE2 __asm__ ("lr\t%0,%1" : "=r" (out) : "{r2}" (x)); +#elif defined (__x86_64__) +# define REG register int argreg1 __asm__ ("edi") = x; +# define MOVE1 __asm__ ("mov\t%1,%0" : "=r" (out) : "r" (argreg1)); +# define MOVE2 __asm__ ("mov\t%1,%0" : "=r" (out) : "{edi}" (x)); +#endif + +__attribute__ ((noipa)) +int foo (int unused) { } + +int +bar (int x) +{ + int out; + REG + foo (42); + MOVE1 + return out; +} + +int +baz (int x) +{ + int out; + foo (42); + MOVE2 + return out; +} + +int +main (void) +{ + if (bar (0) != 42 + || bar (1) != 42 + || bar (2) != 42 + || bar (32) != 42 + || baz (0) != 0 + || baz (1) != 1 + || baz (2) != 2 + || baz (32) != 32) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-error-1.c b/gcc/testsuite/gcc.dg/asm-hard-reg-error-1.c new file mode 100644 index 00000000000..6060c0946da --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-error-1.c @@ -0,0 +1,67 @@ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* riscv*-*-* s390*-*-* x86_64-*-* } } */ + +#if defined (__aarch64__) +# define GPR1_RAW "x0" +# define GPR2 "{x1}" +# define GPR3 "{x2}" +# define INVALID_GPR_A "{x31}" +#elif defined (__arm__) +# define GPR1_RAW "r0" +# define GPR2 "{r1}" +# define GPR3 "{r2}" +# define INVALID_GPR_A "{r16}" +#elif defined (__i386__) +# define GPR1_RAW "%eax" +# define GPR2 "{%ebx}" +# define GPR3 "{%edx}" +# define INVALID_GPR_A "{%eex}" +#elif defined (__powerpc__) || defined (__POWERPC__) +# define GPR1_RAW "r4" +# define GPR2 "{r5}" +# define GPR3 "{r6}" +# define INVALID_GPR_A "{r33}" +#elif defined (__riscv) +# define GPR1_RAW "t4" +# define GPR2 "{t5}" +# define GPR3 "{t6}" +# define INVALID_GPR_A "{t7}" +#elif defined (__s390__) +# define GPR1_RAW "r4" +# define GPR2 "{r5}" +# define GPR3 "{r6}" +# define INVALID_GPR_A "{r17}" +#elif defined (__x86_64__) +# define GPR1_RAW "rax" +# define GPR2 "{rbx}" +# define GPR3 "{rcx}" +# define INVALID_GPR_A "{rex}" +#endif + +#define GPR1 "{"GPR1_RAW"}" +#define INVALID_GPR_B "{"GPR1_RAW + +struct { int a[128]; } s = {0}; + +void +test (void) +{ + int x, y; + register int gpr1 __asm__ (GPR1_RAW) = 0; + + __asm__ ("" :: "{}" (42)); /* { dg-error "invalid input constraint: \{\}" } */ + __asm__ ("" :: INVALID_GPR_A (42)); /* { dg-error "invalid input constraint" } */ + __asm__ ("" :: INVALID_GPR_B (42)); /* { dg-error "invalid input constraint" } */ + + __asm__ ("" :: GPR1 (s)); /* { dg-error "data type isn't suitable for register .* of operand 0" } */ + + __asm__ ("" :: "r" (gpr1), GPR1 (42)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" :: GPR1 (42), "r" (gpr1)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" :: GPR1 (42), GPR1 (42)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" :: GPR1","GPR2 (42), GPR2","GPR3 (42)); + __asm__ ("" :: GPR1","GPR2 (42), GPR3","GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" :: GPR1","GPR2 (42), GPR1","GPR3 (42)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" :: GPR1 GPR2 (42), GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" : "+"GPR1 (x), "="GPR1 (y)); /* { dg-error "multiple outputs to hard register" } */ + __asm__ ("" : "="GPR1 (y) : GPR1 (42), "0" (42)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" : "+"GPR1 (x) : GPR1 (42)); /* { dg-error "multiple inputs to hard register" } */ +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-error-2.c b/gcc/testsuite/gcc.dg/asm-hard-reg-error-2.c new file mode 100644 index 00000000000..efa843e0800 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-error-2.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target { { aarch64*-*-* s390x-*-* } && int128 } } } */ +/* { dg-options "-O2" } get rid of -ansi since we use __int128 */ + +/* Test register pairs. */ + +#if defined (__aarch64__) +# define GPR "{x4}" +# define INVALID_GPR "{x5}" +#elif defined (__s390__) +# define GPR "{r4}" +# define INVALID_GPR "{r5}" +#endif + +void +test (void) +{ + __asm__ ("" :: GPR ((__int128) 42)); + __asm__ ("" :: INVALID_GPR ((__int128) 42)); /* { dg-error "register .* for operand 0 isn't suitable for data type" } */ +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-error-3.c b/gcc/testsuite/gcc.dg/asm-hard-reg-error-3.c new file mode 100644 index 00000000000..ef8af5a6d52 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-error-3.c @@ -0,0 +1,20 @@ +/* { dg-do compile { target arm*-*-* s390-*-* } } */ +/* { dg-options "-std=c99" } we need long long */ +/* { dg-additional-options "-march=armv8-a" { target arm*-*-* } } */ + +/* Test register pairs. */ + +#if defined (__arm__) +# define GPR "{r4}" +# define INVALID_GPR "{r5}" +#elif defined (__s390__) +# define GPR "{r4}" +# define INVALID_GPR "{r5}" +#endif + +void +test (void) +{ + __asm__ ("" :: GPR (42ll)); + __asm__ ("" :: INVALID_GPR (42ll)); /* { dg-error "register .* for operand 0 isn't suitable for data type" } */ +} diff --git a/gcc/testsuite/gcc.dg/pr87600-2.c b/gcc/testsuite/gcc.dg/pr87600-2.c index e8a9f194b73..860d3f965ef 100644 --- a/gcc/testsuite/gcc.dg/pr87600-2.c +++ b/gcc/testsuite/gcc.dg/pr87600-2.c @@ -11,34 +11,6 @@ test0 (void) { register long var1 asm (REG1); register long var2 asm (REG1); - asm ("blah %0 %1" : "=r" (var1), "=r" (var2)); /* { dg-error "invalid hard register usage between output operands" } */ + asm ("blah %0 %1" : "=r" (var1), "=r" (var2)); /* { dg-error "multiple outputs to hard register" } */ return var1; } - -long -test1 (void) -{ - register long var1 asm (REG1); - register long var2 asm (REG2); - asm ("blah %0 %1" : "=r" (var1) : "0" (var2)); /* { dg-error "invalid hard register usage between output operand and matching constraint operand" } */ - return var1; -} - -long -test2 (void) -{ - register long var1 asm (REG1); - register long var2 asm (REG1); - asm ("blah %0 %1" : "=&r" (var1) : "r" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ - return var1; -} - -long -test3 (void) -{ - register long var1 asm (REG1); - register long var2 asm (REG1); - long var3; - asm ("blah %0 %1" : "=&r" (var1), "=r" (var3) : "1" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ - return var1 + var3; -} diff --git a/gcc/testsuite/gcc.dg/pr87600-3.c b/gcc/testsuite/gcc.dg/pr87600-3.c new file mode 100644 index 00000000000..2673d004130 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr87600-3.c @@ -0,0 +1,35 @@ +/* PR rtl-optimization/87600 */ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-O2" } */ + +#include "pr87600.h" + +/* The following are all invalid uses of local register variables. */ + +long +test1 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG2); + asm ("blah %0 %1" : "=r" (var1) : "0" (var2)); /* { dg-error "invalid hard register usage between output operand and matching constraint operand" } */ + return var1; +} + +long +test2 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG1); + asm ("blah %0 %1" : "=&r" (var1) : "r" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ + return var1; +} + +long +test3 (void) +{ + register long var1 asm (REG1); + register long var2 asm (REG1); + long var3; + asm ("blah %0 %1" : "=&r" (var1), "=r" (var3) : "1" (var2)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ + return var1 + var3; +} diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c b/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c new file mode 100644 index 00000000000..671c0ede6ef --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-1.c @@ -0,0 +1,103 @@ +/* { dg-do compile { target { lp64 } } } */ +/* { dg-options "-O2 -march=z13 -mzarch" } */ +/* { dg-final { check-function-bodies "**" "" "" } } */ + +/* +** test_in_1: +** foo %r2 +** br %r14 +*/ + +int +test_in_1 (int x) +{ + asm ("foo %0" :: "{r2}" (x)); + return x; +} + +/* +** test_in_2: +** lgr (%r[0-9]+),%r2 +** lr %r2,%r3 +** foo %r2 +** lgr %r2,\1 +** br %r14 +*/ + +int +test_in_2 (int x, int y) +{ + asm ("foo %0" :: "{r2}" (y)); + return x; +} + +/* +** test_in_3: +** stmg %r12,%r15,96\(%r15\) +** lay %r15,-160\(%r15\) +** lgr (%r[0-9]+),%r2 +** ahi %r2,1 +** lgfr %r2,%r2 +** brasl %r14,foo@PLT +** lr %r3,%r2 +** lr %r2,\1 +** foo %r3,%r2 +** lgr %r2,\1 +** lmg %r12,%r15,256\(%r15\) +** br %r14 +*/ + +extern int foo (int); + +int +test_in_3 (int x) +{ + asm ("foo %0,%1\n" :: "{r3}" (foo (x + 1)), "{r2}" (x)); + return x; +} + +/* +** test_out_1: +** foo %r3 +** lgfr %r2,%r3 +** br %r14 +*/ + +int +test_out_1 (void) +{ + int x; + asm ("foo %0" : "={r3}" (x)); + return x; +} + +/* +** test_out_2: +** lgr (%r[0-9]+),%r2 +** foo %r2 +** ark (%r[0-9]+),\1,%r2 +** lgfr %r2,\2 +** br %r14 +*/ + +int +test_out_2 (int x) +{ + int y; + asm ("foo %0" : "={r2}" (y)); + return x + y; +} + +/* +** test_inout_1: +** foo %r2 +** lgfr %r2,%r2 +** br %r14 +*/ + +int +test_inout_1 (int x) +{ + asm ("foo %0" : "+{r2}" (x)); + return x; +} diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c b/gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c new file mode 100644 index 00000000000..a892fe8f0aa --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-2.c @@ -0,0 +1,43 @@ +/* { dg-do compile { target { lp64 } } } */ +/* { dg-options "-O2 -march=z13 -mzarch" } */ +/* { dg-final { check-function-bodies "**" "" "" } } */ +/* { dg-final { scan-assembler {\.LC0:\n\t\.long\t1078523331\n} } } */ + + +/* +** test_float_into_gpr: +** lrl %r4,.LC0 +** foo %r4 +** br %r14 +*/ + +void +test_float_into_gpr (void) +{ + // This is the counterpart to + // register float x asm ("r4") = 3.14f; + // asm ("foo %0" :: "r" (x)); + // where the bit-pattern of 3.14f is loaded into GPR. + asm ("foo %0" :: "{r4}" (3.14f)); +} + +/* +** test_float: +** ( +** ldr %f4,%f0 +** ldr %f5,%f2 +** | +** ldr %f5,%f2 +** ldr %f4,%f0 +** ) +** aebr %f5,%f4 +** ldr %f0,%f5 +** br %r14 +*/ + +float +test_float (float x, float y) +{ + asm ("aebr %0,%1" : "+{f5}" (y) : "{f4}" (x)); + return y; +} diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c b/gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c new file mode 100644 index 00000000000..5df37b5b717 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-3.c @@ -0,0 +1,42 @@ +/* { dg-do compile { target lp64 } } */ +/* { dg-options "-O2 -march=z13 -mzarch" } */ +/* { dg-final { check-function-bodies "**" "" "" } } */ +/* { dg-final { scan-assembler {\.LC0:\n\t\.long\t1074339512\n\t\.long\t1374389535\n} } } */ + +/* +** test_double_into_gpr: +** lgrl %r4,.LC0 +** foo %r4 +** br %r14 +*/ + +void +test_double_into_gpr (void) +{ + // This is the counterpart to + // register double x asm ("r4") = 3.14; + // asm ("foo %0" :: "r" (x)); + // where the bit-pattern of 3.14 is loaded into GPR. + asm ("foo %0" :: "{r4}" (3.14)); +} + +/* +** test_double: +** ( +** ldr %f4,%f0 +** ldr %f5,%f2 +** | +** ldr %f5,%f2 +** ldr %f4,%f0 +** ) +** adbr %f5,%f4 +** ldr %f0,%f5 +** br %r14 +*/ + +double +test_double (double x, double y) +{ + asm ("adbr %0,%1" : "+{f5}" (y) : "{f4}" (x)); + return y; +} diff --git a/gcc/testsuite/lib/scanasm.exp b/gcc/testsuite/lib/scanasm.exp index 42c719c512c..3c28ccec955 100644 --- a/gcc/testsuite/lib/scanasm.exp +++ b/gcc/testsuite/lib/scanasm.exp @@ -896,6 +896,10 @@ proc configure_check-function-bodies { config } { set up_config(fluff) {^\s*(?://)} } elseif { [istarget *-*-darwin*] } { set up_config(fluff) {^\s*(?:\.|//|@)|^L[0-9ABCESV]} + } elseif { [istarget s390*-*-*] } { + # Additionally to the defaults skip lines beginning with a # resulting + # from inline asm. + set up_config(fluff) {^\s*(?:\.|//|@|$|#)} } else { # Skip lines beginning with labels ('.L[...]:') or other directives # ('.align', '.cfi_startproc', '.quad [...]', '.text', etc.), '//' or