From patchwork Fri Oct 25 09:57:17 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: 99570 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id ED8863858D21 for ; Fri, 25 Oct 2024 09:59:16 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org ED8863858D21 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1729850357; bh=KBpufMNMQffPsEODpaHEvMkeedPPIaJ6s+Jo4e5IqFA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=h7ecfbE4emcavlF+i1Up7Ownxf3mTbzPpENIYKOaNEaJt8F6rRjGQFffKlzpKQNfr juBmT1TmcbSfmIgto7J5d5oLFe49/nFidcJ2z6ZZ1A6PnfIhGVVNLxQ7bpS/sTrXZt oMInpN658GdxzbhpOOELueuKr0ariJo3usgjT1Pw= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) by sourceware.org (Postfix) with ESMTPS id 313DF3858D35; Fri, 25 Oct 2024 09:57:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 313DF3858D35 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 313DF3858D35 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=148.163.156.1 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729850277; cv=none; b=Q3/XoMJ/UnkaAt8/H9WR0iz5UOVsveea/Kf//LQJt9fdFJJljRPm78xlKsHPAjL4W9gaFND5XXjckzpgivdFRW4TqL5JvsdE+MrqYqy9cHwefwdssr+VNKGOVCz572uN02ubxU6YnYBgPSEAjrHSLU8sDq80Xb7N8W/s2OELK40= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729850277; c=relaxed/simple; bh=PdMmA5RABsVoxNq6p9Ac14JAr5PxhKn2qSdEYhes3R0=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=HpFzNKrmLPnZTzG+8wRWd5IyH5i79TWOmWHdNbOQ3wfsZ+MEfivjTD7lrLALruAlWA5BeBZSUNdhEd/lDi4eriO8KgtJ/ATP6LQyXXLslRvKAJ9Sy1u3xuzXxBjCkUcV2497WRsOJeJQJSMwery5+dVeYfDWvT/2E69y8vN/6hE= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from pps.filterd (m0356517.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 49P37cKl015366; Fri, 25 Oct 2024 09:57:47 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 42emae5jtt-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Oct 2024 09:57:46 +0000 (GMT) Received: from pps.filterd (ppma22.wdc07v.mail.ibm.com [127.0.0.1]) by ppma22.wdc07v.mail.ibm.com (8.18.1.2/8.18.1.2) with ESMTP id 49P6OZAY014603; Fri, 25 Oct 2024 09:57:45 GMT Received: from smtprelay02.fra02v.mail.ibm.com ([9.218.2.226]) by ppma22.wdc07v.mail.ibm.com (PPS) with ESMTPS id 42emk8528f-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Oct 2024 09:57:45 +0000 Received: from smtpav01.fra02v.mail.ibm.com (smtpav01.fra02v.mail.ibm.com [10.20.54.100]) by smtprelay02.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 49P9vhAc53215726 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 25 Oct 2024 09:57:43 GMT Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 0FCA4200CD; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 4FAB6200CB; Fri, 25 Oct 2024 09:57:25 +0000 (GMT) Received: from a8345010.lnxne.boe (unknown [9.152.108.100]) by smtpav01.fra02v.mail.ibm.com (Postfix) with ESMTPS; Fri, 25 Oct 2024 09:57:25 +0000 (GMT) From: Stefan Schulze Frielinghaus To: gcc-patches@gcc.gnu.org Cc: Stefan Schulze Frielinghaus Subject: [PATCH v3 1/4] Hard register constraints Date: Fri, 25 Oct 2024 11:57:17 +0200 Message-ID: <20241025095720.2990683-2-stefansf@gcc.gnu.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20241025095720.2990683-1-stefansf@gcc.gnu.org> References: <20241025095720.2990683-1-stefansf@gcc.gnu.org> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Proofpoint-ORIG-GUID: 0BAU5bgsTqxtpX_hyUX83M43w58Fed8V X-Proofpoint-GUID: 0BAU5bgsTqxtpX_hyUX83M43w58Fed8V X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1051,Hydra:6.0.680,FMLib:17.12.62.30 definitions=2024-10-15_01,2024-10-11_01,2024-09-30_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 suspectscore=0 phishscore=0 mlxlogscore=999 adultscore=0 spamscore=0 malwarescore=0 clxscore=1034 lowpriorityscore=0 bulkscore=0 mlxscore=0 impostorscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2409260000 definitions=main-2410250075 X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_DMARC_STATUS, KAM_SHORT, RCVD_IN_DNSWL_LOW, 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~patchwork=sourceware.org@gcc.gnu.org Implement hard register constraints of the form {regname} where regname must be a valid register name for the target. Such constraints may be used in asm statements as a replacement for register asm and in machine descriptions. Due to optimizations it is not unexpected if two or more inputs require the same value, then those also share a common pseudo. However, this in turn may lead to unsatisfiable asm where multiple inputs with different hard register constraints share the same pseudo. Therefore, we have to introduce copies of such a pseudo and use these for conflicting inputs. This is done prior RA during asmcons in match_asm_constraints_2(). While IRA tries to reduce live ranges, it also replaces some register-register moves. That in turn might undo those copies of a pseudo which we just introduced during asmcons. Thus, check in decrease_live_ranges_number() via valid_replacement_for_asm_input_p() whether it is valid to perform a replacement. The reminder of the patch mostly deals with parsing and decoding hard register constraints. The actual work is done by LRA in process_alt_operands() where a register filter, according to the constraint, is installed. For the sake of "reviewability" and in order to show the beauty of LRA, error handling (which gets pretty involved) is spread out into a subsequent patch. Limitation ---------- Currently, a fixed register cannot be used as hard register constraint. For example, loading the stack pointer on x86_64 via void * foo (void) { void *y; __asm__ ("" : "={rsp}" (y)); return y; } leads to an error. This is unfortunate since register asm does not have this limitation. The culprit seems to be that during reload ira_class_hard_regs_num[rclass] does not even include fixed registers which is why lra_assign() ultimately fails. Does anyone have an idea how to lift this limitation? Maybe there is even a shortcut in order to force a pseudo into a hard reg? Asm Adjust Hook --------------- The following targets implement TARGET_MD_ASM_ADJUST: - aarch64 - arm - avr - cris - i386 - mn10300 - nds32 - pdp11 - rs6000 - s390 - vax Most of them only add the CC register to the list of clobbered register. However, cris, i386, and s390 need some minor adjustment. --- gcc/config/cris/cris.cc | 6 +- gcc/config/i386/i386.cc | 6 + gcc/config/s390/s390.cc | 6 +- gcc/doc/extend.texi | 178 ++++++++++++++++++ gcc/doc/md.texi | 6 + gcc/function.cc | 116 ++++++++++++ gcc/genoutput.cc | 14 ++ gcc/genpreds.cc | 4 +- gcc/ira.cc | 79 +++++++- gcc/lra-constraints.cc | 13 ++ gcc/recog.cc | 11 +- gcc/stmt.cc | 39 ++++ gcc/stmt.h | 1 + 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 | 41 ++++ gcc/testsuite/gcc.dg/asm-hard-reg-8.c | 49 +++++ .../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.target/s390/asm-hard-reg-4.c | 6 + .../gcc.target/s390/asm-hard-reg-5.c | 6 + .../gcc.target/s390/asm-hard-reg-longdouble.h | 18 ++ 27 files changed, 1069 insertions(+), 7 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-8.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 create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-4.c create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-5.c create mode 100644 gcc/testsuite/gcc.target/s390/asm-hard-reg-longdouble.h diff --git a/gcc/config/cris/cris.cc b/gcc/config/cris/cris.cc index 617fc0a0cb3..4acf962d38b 100644 --- a/gcc/config/cris/cris.cc +++ b/gcc/config/cris/cris.cc @@ -3712,9 +3712,11 @@ cris_md_asm_adjust (vec &outputs, vec &inputs, /* Determine if the source using MOF. If it is, automatically clobbering MOF would cause it to have impossible constraints. */ - /* Look for a use of the MOF constraint letter: h. */ + /* Look for a use of the MOF constraint letter h or a hard register + constraint. */ for (unsigned i = 0, n = constraints.length(); i < n; ++i) - if (strchr (constraints[i], 'h') != NULL) + if (strchr (constraints[i], 'h') != NULL + || strstr (constraints[i], "{mof}") != NULL) return NULL; /* Look for an output or an input that touches MOF. */ diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc index 2f736a3b346..21a6a244d03 100644 --- a/gcc/config/i386/i386.cc +++ b/gcc/config/i386/i386.cc @@ -23865,6 +23865,12 @@ static void map_egpr_constraints (vec &constraints) buf.safe_push (cur[j + 1]); j++; break; + case '{': + do + { + buf.safe_push (cur[j]); + } while (cur[j++] != '}'); + break; default: buf.safe_push (cur[j]); break; diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc index 25d43ae3e13..34982c87869 100644 --- a/gcc/config/s390/s390.cc +++ b/gcc/config/s390/s390.cc @@ -17659,9 +17659,11 @@ f_constraint_p (const char *constraint) for (size_t i = 0, c_len = strlen (constraint); i < c_len; i += CONSTRAINT_LEN (constraint[i], constraint + i)) { - if (constraint[i] == 'f') + if (constraint[i] == 'f' + || (constraint[i] == '{' && constraint[i + 1] == 'f')) seen_f_p = true; - if (constraint[i] == 'v') + if (constraint[i] == 'v' + || (constraint[i] == '{' && constraint[i + 1] == 'v')) seen_v_p = true; } diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c95df845634..0650d788009 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -12623,6 +12623,7 @@ the two, as explained in the sections below. @menu * Global Register Variables:: Variables declared at global scope. * Local Register Variables:: Variables declared within a function. +* Hard Register Constraints:: Operands forced into specific machine registers. @end menu @node Global Register Variables @@ -12828,6 +12829,183 @@ with slightly different characteristics (@pxref{MIPS Coprocessors,, Defining coprocessor specifics for MIPS targets, gccint, GNU Compiler Collection (GCC) Internals}). +@node Hard Register Constraints +@subsubsection Hard Register Constraints + +Similar to register @code{asm} but still distinct, hard register constraints +are another way to force operands of inline @code{asm} into specific machine +registers. In contrast to register @code{asm} where a variable is bound to a +machine register, a hard register constraint binds an @code{asm} operand to a +machine register. Assume in the following that @code{r4} is a general-purpose +register, @code{f5} a floating-point register, and @code{v6} a vector register +for some target. + +@smallexample +int x; +int y __attribute__ ((vector_size (16))); +@dots{} +asm ("some instructions" + : "=@{r4@}" (x) + : "@{f5@}" (42.0), "@{v6@}" (y)); +@end smallexample + +For the inline @code{asm}, variable @code{x} is bound to register @code{r4}, +and @code{y} is loaded to @code{v6}. Furthermore, constant @code{42.0} is +loaded into floating-point register @code{f5}. + +A key difference between register @code{asm} and hard register constraints is +that the latter are specified at the point where they are supposed to +materialize, namely at inline @code{asm}, which may lead to more readable code. + +@subsubheading Usage + +Each input operand is loaded into the register specified by its corresponding +hard register constraint. Furthermore, each hard register must be used at most +once among an alternative for inputs. This renders hard register constraints +more strict compared to register @code{asm} where multiple inputs may share a +register as for example in + +@smallexample +int x; +register int y __asm__ ("0") = @dots{}; +asm ("" : "=r" (x) : "r" (y), "r" (y)); +@end smallexample + +or even + +@smallexample +register int x asm ("0") = 42; +register int y asm ("0") = 24; +asm ("" : "=r" (x) : "r" (x), "r" (y)); +@end smallexample + +The analogue for hard register constraints is invalid in order to prevent +subtle bugs. + +Likewise, two outputs must not share a register among an alternative. That +means, the following example is invalid + +@smallexample +int x, y; +asm ("" : "=@{r4@}" (x), "=@{r4@}" (y)); // invalid +@end smallexample + +which also aligns with register @code{asm}. Despite that, each output must +refer to a distinct object if a hard register constraint is involved. For +example, in the following, object @code{x} is assigned two registers. + +@smallexample +int x; +asm ("" : "=r" (x), "=r" (x)); +@end smallexample + +This is not allowed for hard register constraints in order to prevent subtle +bugs. Even if only one output operand has a hard register constraint, the code +is rejected since the allocation for the object is still ambiguous. + +@smallexample +int x; +asm ("" : "=r" (x), "=@{1@}" (x)); // invalid +@end smallexample + +The type of an operand must be supported by the corresponding machine register. + +A hard register constraint may refer to any general, floating-point, or vector +register except a fixed register as e.g.@: the stack pointer register. The set +of allowed registers is target dependent analogue to register @code{asm}. +Furthermore, the referenced register must be a valid register name of the +target. Note, on some targets, a single register may be referred to by +different names where each name specifies the length of the register. For +example, on x86_64 the register names @code{rcx}, @code{ecx}, and @code{cx} all +refer to the same register but in different sizes. If any of those names is +used for a hard register constraint, the actual size of a register is +determined by its corresponding operand. For example + +@smallexample +long x; +asm ("mov\t$42, %0" : "=@{ecx@}" (x)); +@end smallexample + +Although the hard register constraint refers to register @code{ecx}, the actual +register will be @code{rcx} since on x86_64 a @code{long} is 8 byte in total. +This aligns with register @code{asm} where you could have + +@smallexample +register long x asm ("ecx"); +@end smallexample + +@subsubheading Interaction with Register @code{asm} + +A mixture of both constructs as for example + +@smallexample +register int x asm ("r4") = 42; +int y; +asm ("" : "=@{r5@}" (y) : "r" (x)); +@end smallexample + +is valid. + +If an operand is a register @code{asm} and the corresponding constraint a hard +register, then both must refer to the same register. That means + +@smallexample +register int x asm ("r4"); +asm ("" : "=@{r4@}" (x)); +@end smallexample + +is valid and + +@smallexample +register int x asm ("r4"); +asm ("" : "=@{r5@}" (x)); +@end smallexample + +is invalid. + +Note, register @code{asm} may not only be clobbered by function calls but also +by inline @code{asm} in conjunction with hard register constraints. For +example, in the following + +@smallexample +register int x asm ("r5") = 42; +int y; +asm ("" : "=@{r5@}" (y)); +asm ("" : "+r" (x)); +@end smallexample + +variable @code{x} materializes before the very first inline @code{asm} which +writes to register @code{r5} and therefore clobbers @code{x} which in turn is +read by the subsequent inline @code{asm}. + +@subsubheading Limitations + +At the moment fixed registers are not supported for hard register constraints. +Thus, idioms like + +@smallexample +register void *x asm ("rsp"); +asm ("" : "=r" (x)); +@end smallexample + +are not supported for hard register constraints. This might be lifted. + +@subsubheading Discussion: Unions and Multiple Alternatives + +With the current implementation unions and multiple alternatives are supported. +For example + +@smallexample +asm ("" : "=@{r1@}@{r2@}@{r3@},m@{r4@}" (x) : "@{r4@},r" (y), "@{r5@},@{r4@}" (z)); +@end smallexample + +Note, both inputs make use of @code{r4} but in different alternatives which is +fine. Since LRA does all the hard work, this works so far TM. However, I'm +not entirely sure whether allowing alternatives or even unions is a thing we +really want to support. On the other hand if we automatically want to convert +from register @code{asm} to hard register constraints, then unions/alternatives +must be supported. + @node Size of an asm @subsection Size of an @code{asm} diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index a9259112251..b10bd459f59 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -1366,6 +1366,12 @@ as for @samp{<} apply. A register operand is allowed provided that it is in a general register. +@cindex hard registers in constraint +@item @samp{@{r@}} +An operand is bound to hard register @samp{r} which may be any general, +floating-point, or vector register except a fixed register like a stack pointer +register. The set of fixed registers is target dependent. + @cindex constants in constraints @cindex @samp{i} in constraint @item @samp{i} diff --git a/gcc/function.cc b/gcc/function.cc index a6f6de34942..bf5992f2b06 100644 --- a/gcc/function.cc +++ b/gcc/function.cc @@ -6974,6 +6974,115 @@ match_asm_constraints_1 (rtx_insn *insn, rtx *p_sets, int noutputs) df_insn_rescan (insn); } +/* It is expected and desired that optimizations coalesce multiple pseudos into + one whenever possible. However, in case of hard register constraints we may + have to undo this and introduce copies since otherwise we could constraint a + single pseudo to different hard registers. For example, during register + allocation the following insn would be unsatisfiable since pseudo 60 is + constrained to hard register r5 and r6 at the same time. + + (insn 7 5 0 2 (asm_operands/v ("foo") ("") 0 [ + (reg:DI 60) repeated x2 + ] + [ + (asm_input:DI ("{r5}") t.c:4) + (asm_input:DI ("{r6}") t.c:4) + ] + [] t.c:4) "t.c":4:3 -1 + (expr_list:REG_DEAD (reg:DI 60) + (nil))) + + Therefore, introduce a copy of pseudo 60 and transform it into + + (insn 10 5 7 2 (set (reg:DI 62) + (reg:DI 60)) "t.c":4:3 1503 {*movdi_64} + (nil)) + (insn 7 10 11 2 (asm_operands/v ("foo") ("") 0 [ + (reg:DI 60) + (reg:DI 62) + ] + [ + (asm_input:DI ("{r5}") t.c:4) + (asm_input:DI ("{r6}") t.c:4) + ] + [] t.c:4) "t.c":4:3 -1 + (expr_list:REG_DEAD (reg:DI 62) + (expr_list:REG_DEAD (reg:DI 60) + (nil)))) + + Now, LRA can assign pseudo 60 to r5, and pseudo 62 to r6. + + TODO: The current implementation is conservative and we could do a bit + better in case of alternatives. For example + + (insn 7 5 0 2 (asm_operands/v ("foo") ("") 0 [ + (reg:DI 60) repeated x2 + ] + [ + (asm_input:DI ("r,{r5}") t.c:4) + (asm_input:DI ("{r6},r") t.c:4) + ] + [] t.c:4) "t.c":4:3 -1 + (expr_list:REG_DEAD (reg:DI 60) + (nil))) + + For this insn we wouldn't need to come up with a copy of pseudo 60 since in + each alternative pseudo 60 is constrained exactly one time. */ + +static void +match_asm_constraints_2 (rtx_insn *insn, rtx pat) +{ + rtx op; + if (GET_CODE (pat) == SET && GET_CODE (SET_SRC (pat)) == ASM_OPERANDS) + op = SET_SRC (pat); + else if (GET_CODE (pat) == ASM_OPERANDS) + op = pat; + else + return; + int ninputs = ASM_OPERANDS_INPUT_LENGTH (op); + rtvec inputs = ASM_OPERANDS_INPUT_VEC (op); + bool changed = false; + auto_bitmap constrained_regs; + + for (int i = 0; i < ninputs; ++i) + { + rtx input = RTVEC_ELT (inputs, i); + const char *constraint = ASM_OPERANDS_INPUT_CONSTRAINT (op, i); + if ((!REG_P (input) && !SUBREG_P (input)) + || (REG_P (input) && HARD_REGISTER_P (input)) + || strchr (constraint, '{') == nullptr) + continue; + int regno; + if (SUBREG_P (input)) + { + if (REG_P (SUBREG_REG (input))) + regno = REGNO (SUBREG_REG (input)); + else + continue; + } + else + regno = REGNO (input); + /* Keep the first usage of a constrained pseudo as is and only + introduce copies for subsequent usages. */ + if (! bitmap_bit_p (constrained_regs, regno)) + { + bitmap_set_bit (constrained_regs, regno); + continue; + } + rtx tmp = gen_reg_rtx (GET_MODE (input)); + start_sequence (); + emit_move_insn (tmp, input); + rtx_insn *insns = get_insns (); + end_sequence (); + emit_insn_before (insns, insn); + RTVEC_ELT (inputs, i) = tmp; + changed = true; + } + + if (changed) + df_insn_rescan (insn); +} + /* Add the decl D to the local_decls list of FUN. */ void @@ -7030,6 +7139,13 @@ pass_match_asm_constraints::execute (function *fun) continue; pat = PATTERN (insn); + + if (GET_CODE (pat) == PARALLEL) + for (int i = XVECLEN (pat, 0) - 1; i >= 0; --i) + match_asm_constraints_2 (insn, XVECEXP (pat, 0, i)); + else + match_asm_constraints_2 (insn, pat); + if (GET_CODE (pat) == PARALLEL) p_sets = &XVECEXP (pat, 0, 0), noutputs = XVECLEN (pat, 0); else if (GET_CODE (pat) == SET) diff --git a/gcc/genoutput.cc b/gcc/genoutput.cc index 16fd811b5dd..2ffb2fb28d2 100644 --- a/gcc/genoutput.cc +++ b/gcc/genoutput.cc @@ -1284,6 +1284,20 @@ 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 decode_hreg_constraint(), 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/ira.cc b/gcc/ira.cc index 5231f63398e..eac9be667a2 100644 --- a/gcc/ira.cc +++ b/gcc/ira.cc @@ -2128,6 +2128,82 @@ ira_get_dup_out_num (int op_num, alternative_mask alts, +/* Return true if a replacement of SRC by DEST does not lead to unsatisfiable + asm. Thus, a replacement is valid if and only if SRC and DEST are not + constrained in asm inputs of a single asm statement. See + match_asm_constraints_2() for more details. TODO: As in + match_asm_constraints_2() consider alternatives more precisely. */ + +static bool +valid_replacement_for_asm_input_p_1 (const_rtx asmops, const_rtx src, const_rtx dest) +{ + int ninputs = ASM_OPERANDS_INPUT_LENGTH (asmops); + rtvec inputs = ASM_OPERANDS_INPUT_VEC (asmops); + for (int i = 0; i < ninputs; ++i) + { + rtx input_src = RTVEC_ELT (inputs, i); + const char *constraint_src + = ASM_OPERANDS_INPUT_CONSTRAINT (asmops, i); + if (rtx_equal_p (input_src, src) + && strchr (constraint_src, '{') != nullptr) + for (int j = 0; j < ninputs; ++j) + { + rtx input_dest = RTVEC_ELT (inputs, j); + const char *constraint_dest + = ASM_OPERANDS_INPUT_CONSTRAINT (asmops, j); + if (rtx_equal_p (input_dest, dest) + && strchr (constraint_dest, '{') != nullptr) + return false; + } + } + return true; +} + +static bool +valid_replacement_for_asm_input_p (const_rtx src, const_rtx dest) +{ + /* Bail out early if there is no asm statement. */ + if (!crtl->has_asm_statement) + return true; + for (df_ref use = DF_REG_USE_CHAIN (REGNO (src)); + use; + use = DF_REF_NEXT_REG (use)) + { + struct df_insn_info *use_info = DF_REF_INSN_INFO (use); + /* Only check real uses, not artificial ones. */ + if (use_info) + { + rtx_insn *insn = DF_REF_INSN (use); + rtx pat = PATTERN (insn); + if (asm_noperands (pat) <= 0) + continue; + if (GET_CODE (pat) == SET) + { + if (!valid_replacement_for_asm_input_p_1 (SET_SRC (pat), src, dest)) + return false; + } + else if (GET_CODE (pat) == PARALLEL) + for (int i = 0, len = XVECLEN (pat, 0); i < len; ++i) + { + rtx asmops = XVECEXP (pat, 0, i); + if (GET_CODE (asmops) == SET) + asmops = SET_SRC (asmops); + if (GET_CODE (asmops) == ASM_OPERANDS + && !valid_replacement_for_asm_input_p_1 (asmops, src, dest)) + return false; + } + else if (GET_CODE (pat) == ASM_OPERANDS) + { + if (!valid_replacement_for_asm_input_p_1 (pat, src, dest)) + return false; + } + else + gcc_unreachable (); + } + } + return true; +} + /* Search forward to see if the source register of a copy insn dies before either it or the destination register is modified, but don't scan past the end of the basic block. If so, we can replace the @@ -2177,7 +2253,8 @@ decrease_live_ranges_number (void) auto-inc memory reference, so we must disallow this optimization on them. */ || sregno == STACK_POINTER_REGNUM - || dregno == STACK_POINTER_REGNUM) + || dregno == STACK_POINTER_REGNUM + || !valid_replacement_for_asm_input_p (src, dest)) continue; dest_death = NULL_RTX; diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc index fdcc07764a2..5d95072b8f8 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 hard_reg_constraint; /* 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_hard_reg_constraint (p); + gcc_assert (regno >= 0); + cl = REGNO_REG_CLASS (regno); + CLEAR_HARD_REG_SET (hard_reg_constraint); + SET_HARD_REG_BIT (hard_reg_constraint, regno); + cl_filter = &hard_reg_constraint; + goto reg; + } + default: cn = lookup_constraint (p); switch (get_constraint_type (cn)) diff --git a/gcc/recog.cc b/gcc/recog.cc index 615aaabc551..c95ac0bdfa0 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" @@ -2367,7 +2368,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; @@ -3301,6 +3303,13 @@ constrain_operands (int strict, alternative_mask alternatives) win = true; break; + case '{': + if ((REG_P (op) && HARD_REGISTER_P (op) + && (int) REGNO (op) == decode_hard_reg_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..915969ee116 100644 --- a/gcc/stmt.cc +++ b/gcc/stmt.cc @@ -52,6 +52,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-cfg.h" #include "dumpfile.h" #include "builtins.h" +#include "output.h" /* Functions and data structures for expanding case statements. */ @@ -174,6 +175,32 @@ 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_hard_reg_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; +} + /* 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, @@ -289,6 +316,12 @@ parse_output_constraint (const char **constraint_p, int operand_num, *allows_mem = true; break; + case '{': + { + *allows_reg = true; + break; + } + default: if (!ISALPHA (*p)) break; @@ -408,6 +441,12 @@ parse_input_constraint (const char **constraint_p, int input_num, *allows_mem = true; break; + case '{': + { + *allows_reg = true; + break; + } + default: if (! ISALPHA (constraint[j])) { diff --git a/gcc/stmt.h b/gcc/stmt.h index a2caae7121b..7d79d682645 100644 --- a/gcc/stmt.h +++ b/gcc/stmt.h @@ -25,6 +25,7 @@ extern bool parse_output_constraint (const char **, int, int, int, bool *, bool *, bool *); extern bool parse_input_constraint (const char **, int, int, int, int, const char * const *, bool *, bool *); +extern int decode_hard_reg_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..761a6b77d34 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-7.c @@ -0,0 +1,41 @@ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* riscv*-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-O2" } */ + +/* Test multiple alternatives. */ + +#if defined (__aarch64__) +# define GPR "{x1}" +/* { dg-final { scan-assembler-times "foo\tx1,x1" 2 { target { aarch64*-*-* } } } } */ +#elif defined (__arm__) +# define GPR "{r1}" +/* { dg-final { scan-assembler-times "foo\tr1,r1" 2 { target { arm*-*-* } } } } */ +#elif defined (__i386__) +# define GPR "{eax}" +/* { dg-final { scan-assembler-times "foo\t%eax,%eax" 2 { target { i?86-*-* } } } } */ +#elif defined (__powerpc__) || defined (__POWERPC__) +# define GPR "{r4}" +/* { dg-final { scan-assembler-times "foo\t4,4" 2 { target { powerpc*-*-* } } } } */ +#elif defined (__riscv) +# define GPR "{t1}" +/* { dg-final { scan-assembler-times "foo\tt1,t1" 2 { target { riscv*-*-* } } } } */ +#elif defined (__s390__) +# define GPR "{r0}" +/* { dg-final { scan-assembler-times "foo\t%r0,%r0" 2 { target { s390*-*-* } } } } */ +#elif defined (__x86_64__) +# define GPR "{eax}" +/* { dg-final { scan-assembler-times "foo\t%eax,%eax" 2 { target { x86_64-*-* } } } } */ +#endif + +int +test_1 (int x) +{ + __asm__ ("foo\t%0,%0" : "+"GPR (x)); + return x; +} + +int +test_2 (int x, int y) +{ + __asm__ ("foo\t%0,%1" : "="GPR (x) : GPR (y)); + return x; +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-8.c b/gcc/testsuite/gcc.dg/asm-hard-reg-8.c new file mode 100644 index 00000000000..cda5e3e4c3f --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-8.c @@ -0,0 +1,49 @@ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* riscv*-*-* s390*-*-* x86_64-*-* } } */ + +/* Due to hard register constraints, X must be copied. */ + +#if defined (__aarch64__) +# define GPR1 "{x1}" +# define GPR2 "{x2}" +#elif defined (__arm__) +# define GPR1 "{r1}" +# define GPR2 "{r2}" +#elif defined (__i386__) +# define GPR1 "{eax}" +# define GPR2 "{ebx}" +#elif defined (__powerpc__) || defined (__POWERPC__) +# define GPR1 "{r4}" +# define GPR2 "{r5}" +#elif defined (__riscv) +# define GPR1 "{t1}" +# define GPR2 "{t2}" +#elif defined (__s390__) +# define GPR1 "{r0}" +# define GPR2 "{r1}" +#elif defined (__x86_64__) +# define GPR1 "{eax}" +# define GPR2 "{ebx}" +#endif + +#define TEST(T) \ +int \ +test_##T (T x) \ +{ \ + int out; \ + __asm__ ("foo" : "=r" (out) : GPR1 (x), GPR2 (x)); \ + return out; \ +} + +TEST(char) +TEST(short) +TEST(int) +TEST(long) + +int +test_subreg (long x) +{ + int out; + short subreg_x = x; + __asm__ ("foo" : "=r" (out) : GPR1 (x), GPR2 (subreg_x)); + return out; +} 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/gcc.target/s390/asm-hard-reg-4.c b/gcc/testsuite/gcc.target/s390/asm-hard-reg-4.c new file mode 100644 index 00000000000..29927ce1821 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-4.c @@ -0,0 +1,6 @@ +/* { dg-do run { target lp64 } } */ +/* { dg-options "-O2 -march=z13 -mzarch" } */ + +/* Test TARGET_MD_ASM_ADJUST for z13 and long double. */ + +#include "asm-hard-reg-longdouble.h" diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-5.c b/gcc/testsuite/gcc.target/s390/asm-hard-reg-5.c new file mode 100644 index 00000000000..eaf34d94379 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-5.c @@ -0,0 +1,6 @@ +/* { dg-do run { target lp64 } } */ +/* { dg-options "-O2 -march=z14 -mzarch" } */ + +/* Test TARGET_MD_ASM_ADJUST for z14 and long double. */ + +#include "asm-hard-reg-longdouble.h" diff --git a/gcc/testsuite/gcc.target/s390/asm-hard-reg-longdouble.h b/gcc/testsuite/gcc.target/s390/asm-hard-reg-longdouble.h new file mode 100644 index 00000000000..9f4adad2acc --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/asm-hard-reg-longdouble.h @@ -0,0 +1,18 @@ +__attribute__ ((noipa)) +long double +test_longdouble (long double x) +{ + long double y; + asm ("sqxbr\t%0,%1" : "={f4}" (y) : "{f5}" (x)); + return y; +} + +int +main (void) +{ + long double x = test_longdouble (42.L); + long double y = 6.48074069840786023096596743608799656681773277430814773408787249757445105002862106857719481922686100006103515625L; + if (x != y) + __builtin_abort (); + return 0; +} From patchwork Fri Oct 25 09:57:18 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: 99569 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9B6D93858D3C for ; Fri, 25 Oct 2024 09:58:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9B6D93858D3C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1729850328; bh=KkZyTz9KbCDL3G8gYOYAwFNvfTU7Z+kH+4UDnDaG46c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=giDYExoQVlWta8fgfcAJaESgNFSUQwuC+2hyyMYRO39m0gNtj7LmY6/9mY8/Lp38O 9EDW+vXCOCCwDBo/FGdgF+/+clrF0+PmH7//6Ft32Oq77BvB0sEGSMXrVUUjrdJjGz ex/2ie3R/9ICD0yDc3ko+XIN4pCZm+z5nHrakKbc= 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 E6A123858D34; Fri, 25 Oct 2024 09:57:46 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E6A123858D34 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 E6A123858D34 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=1729850271; cv=none; b=TRqno6jnGOrdBd0KJdrqxcKljdELSLKdHKK6NZ5onyiEaRu8Uwe+M+ZxN1HdjJ/+pvrJ9aIMXMU8lqh6KNZ4lP7tnSbkAU8pfIggLEkUmNcX2jKQzdjHe+PM0Bxe4Js5GpJGH8K/fxQxyPeptqkXL+ttbCxvlJEAYZp4l2rZkE8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729850271; c=relaxed/simple; bh=xyNcT0r8ecRERlQZ5bAZmVAyE/XhGOVDmA6pbdAKNVY=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=oVsq9zkZW7ADBAxYb3iV+HEaYJ7I0S6EnCUeOmHYD8d1BRrdsckSVBWPn2atZU134Lxaq/T5D8MXBe7nBQA8Atatvyp5BUXOkLVPokIi0PK8HvKTUztU1ASEAX+MZkefCpvnHuNf+EDdFespuuMGXF1CBHN41F3dnZ3yWcdDufA= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from pps.filterd (m0360072.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 49P3HNd5010414; Fri, 25 Oct 2024 09:57:46 GMT Received: from ppma23.wdc07v.mail.ibm.com (5d.69.3da9.ip4.static.sl-reverse.com [169.61.105.93]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 42fbw47gdk-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Oct 2024 09:57:46 +0000 (GMT) Received: from pps.filterd (ppma23.wdc07v.mail.ibm.com [127.0.0.1]) by ppma23.wdc07v.mail.ibm.com (8.18.1.2/8.18.1.2) with ESMTP id 49P6ThE2006835; Fri, 25 Oct 2024 09:57:45 GMT Received: from smtprelay02.fra02v.mail.ibm.com ([9.218.2.226]) by ppma23.wdc07v.mail.ibm.com (PPS) with ESMTPS id 42emjd51ak-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Oct 2024 09:57:45 +0000 Received: from smtpav01.fra02v.mail.ibm.com (smtpav01.fra02v.mail.ibm.com [10.20.54.100]) by smtprelay02.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 49P9vh8556164806 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 25 Oct 2024 09:57:43 GMT Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 3D1B2200D3; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 1577F200D2; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) Received: from a8345010.lnxne.boe (unknown [9.152.108.100]) by smtpav01.fra02v.mail.ibm.com (Postfix) with ESMTPS; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) From: Stefan Schulze Frielinghaus To: gcc-patches@gcc.gnu.org Cc: Stefan Schulze Frielinghaus Subject: [PATCH v3 2/4] Error handling for hard register constraints Date: Fri, 25 Oct 2024 11:57:18 +0200 Message-ID: <20241025095720.2990683-3-stefansf@gcc.gnu.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20241025095720.2990683-1-stefansf@gcc.gnu.org> References: <20241025095720.2990683-1-stefansf@gcc.gnu.org> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Proofpoint-ORIG-GUID: Ie9Sgg9RyL-BkeznXzLTMcXp3zdp8xDh X-Proofpoint-GUID: Ie9Sgg9RyL-BkeznXzLTMcXp3zdp8xDh X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1051,Hydra:6.0.680,FMLib:17.12.62.30 definitions=2024-10-15_01,2024-10-11_01,2024-09-30_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 mlxscore=0 bulkscore=0 suspectscore=0 spamscore=0 phishscore=0 adultscore=0 priorityscore=1501 clxscore=1034 lowpriorityscore=0 malwarescore=0 mlxlogscore=999 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2409260000 definitions=main-2410250075 X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_DMARC_STATUS, RCVD_IN_DNSWL_LOW, 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~patchwork=sourceware.org@gcc.gnu.org This implements error handling for hard register constraints including potential conflicts with register asm operands. In contrast to register asm operands, hard register constraints allow more than just one register per operand. Even more than just one register per alternative. For example, a valid constraint for an operand is "{r0}{r1}m,{r2}". However, this also means that we have to make sure that each register is used at most once in each alternative over all outputs and likewise over all inputs. For asm statements this is done by this patch during gimplification. For hard register constraints used in machine description, error handling is still a todo and I haven't investigated this so far and consider this rather a low priority. There are 9/10 call sides for parse_{input,output}_constraint() which I didn't dare to touch in the first run. If this patch is about to be accepted I could change those call sides and explicitly pass a null pointer instead of overloading those functions as it is done right now. I consider this an implementation nit and didn't want to clutter the patch for reviewing. --- gcc/cfgexpand.cc | 42 ---- gcc/gimplify.cc | 149 ++++++++++-- gcc/gimplify_reg_info.h | 169 ++++++++++++++ gcc/stmt.cc | 239 +++++++++++++++++++- gcc/stmt.h | 8 +- gcc/testsuite/gcc.dg/asm-hard-reg-error-1.c | 83 +++++++ gcc/testsuite/gcc.dg/asm-hard-reg-error-2.c | 26 +++ gcc/testsuite/gcc.dg/asm-hard-reg-error-3.c | 27 +++ gcc/testsuite/gcc.dg/asm-hard-reg-error-4.c | 24 ++ gcc/testsuite/gcc.dg/asm-hard-reg-error-5.c | 13 ++ gcc/testsuite/gcc.dg/pr87600-2.c | 30 +-- gcc/testsuite/gcc.dg/pr87600-3.c | 35 +++ gcc/testsuite/lib/scanasm.exp | 4 + 13 files changed, 756 insertions(+), 93 deletions(-) create mode 100644 gcc/gimplify_reg_info.h 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/asm-hard-reg-error-4.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-error-5.c create mode 100644 gcc/testsuite/gcc.dg/pr87600-3.c diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc index f32cf1b20c9..6a8a7be3063 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/gimplify.cc b/gcc/gimplify.cc index ceb53e5d5bb..e39ba2d3766 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -70,6 +70,10 @@ 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" +#include "gimplify_reg_info.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 @@ -7021,6 +7025,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. */ @@ -7051,6 +7091,47 @@ 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); + + int ninputs = list_length (ASM_INPUTS (expr)); + /* Normalization turns in/out operands into explicit output and input + operands. Thus, count those implicit inputs, too. */ + for (tree link = ASM_OUTPUTS (expr); link; link = TREE_CHAIN (link)) + { + const char *constraint + = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); + if (strchr (constraint, '+') != nullptr) + ++ninputs; + } + + gimplify_reg_info reg_info (num_alternatives, noutputs, ninputs); + + link_next = NULL_TREE; + for (link = ASM_CLOBBERS (expr); link; link = link_next) + { + /* The clobber entry could also be an error marker. */ + if (TREE_CODE (TREE_VALUE (link)) == STRING_CST) + { + const char *regname= TREE_STRING_POINTER (TREE_VALUE (link)); + int regno = decode_reg_name (regname); + if (regno >= 0) + reg_info.set_clobbered (regno); + } + link_next = TREE_CHAIN (link); + TREE_CHAIN (link) = NULL_TREE; + vec_safe_push (clobbers, link); + } + ret = GS_ALL_DONE; link_next = NULL_TREE; for (i = 0, link = ASM_OUTPUTS (expr); link; ++i, link = link_next) @@ -7067,8 +7148,9 @@ 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); + reg_info.operand = TREE_VALUE (link); + ok = parse_output_constraint (&constraint, i, 0, 0, &allows_mem, + &allows_reg, &is_inout, ®_info); if (!ok) { ret = GS_ERROR; @@ -7238,13 +7320,60 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) } } + /* After all output operands have been gimplified, verify that each output + operand is used at most once in case of hard register constraints. Thus, + error out in cases like + asm ("" : "={0}" (x), "={1}" (x)); + or even for + asm ("" : "=r" (x), "={1}" (x)); + + FIXME: Ideally we would also error out for cases like + int x; + asm ("" : "=r" (x), "=r" (x)); + However, since code like that was previously accepted, erroring out now might + break existing code. On the other hand, we already error out for register + asm like + register int x asm ("0"); + asm ("" : "=r" (x), "=r" (x)); + Thus, maybe it wouldn't be too bad to also error out in the former + non-register-asm case. + */ + for (unsigned i = 0; i < vec_safe_length (outputs); ++i) + { + tree link = (*outputs)[i]; + tree op1 = TREE_VALUE (link); + const char *constraint + = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); + if (strchr (constraint, '{') != nullptr) + for (unsigned j = 0; j < vec_safe_length (outputs); ++j) + { + if (i == j) + continue; + tree link2 = (*outputs)[j]; + tree op2 = TREE_VALUE (link2); + if (op1 == op2) + { + error ("multiple outputs to lvalue %qE", op2); + return GS_ERROR; + } + } + } + link_next = NULL_TREE; - for (link = ASM_INPUTS (expr); link; ++i, link = link_next) + int input_num = 0; + for (link = ASM_INPUTS (expr); link; ++input_num, ++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); + reg_info.operand = TREE_VALUE (link); + bool ok = parse_input_constraint (&constraint, input_num, 0, noutputs, 0, + oconstraints, &allows_mem, &allows_reg, + ®_info); + 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)); @@ -7325,15 +7454,7 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) } link_next = NULL_TREE; - for (link = ASM_CLOBBERS (expr); link; ++i, link = link_next) - { - link_next = TREE_CHAIN (link); - TREE_CHAIN (link) = NULL_TREE; - vec_safe_push (clobbers, link); - } - - link_next = NULL_TREE; - for (link = ASM_LABELS (expr); link; ++i, link = link_next) + for (link = ASM_LABELS (expr); link; link = link_next) { link_next = TREE_CHAIN (link); TREE_CHAIN (link) = NULL_TREE; diff --git a/gcc/gimplify_reg_info.h b/gcc/gimplify_reg_info.h new file mode 100644 index 00000000000..e567c277825 --- /dev/null +++ b/gcc/gimplify_reg_info.h @@ -0,0 +1,169 @@ +/* gimplify_reg_info is used during gimplification while walking over + operands and their corresponding constraints of asm statements in order to + detect errors. + + m_alt_output is a mapping describing which registers are potentially used in + which alternative over all outputs. Similarly for m_alt_input but over all + inputs. + + m_early_clobbered_alt is a mapping describing which register is early + clobbered in which alternative over all outputs. + + m_early_clobbered_output is the counter part to the prior one, i.e., it + is a mapping describing which register is early clobbered in which operand + over all alternatives. + + m_reg_asm_input describes the register in case an input is a register asm + and -1 in case it is not. */ + +#include "regs.h" + +class gimplify_reg_info +{ + HARD_REG_SET *m_buf; + HARD_REG_SET *m_alt_output; + HARD_REG_SET *m_alt_input; + HARD_REG_SET *m_early_clobbered_alt; + HARD_REG_SET *m_early_clobbered_output; + int *m_reg_asm_input; + const unsigned m_num_alternatives; + const unsigned m_num_outputs; + const unsigned m_num_inputs = 0; + /* Member m_clobbered describes all the registers marked as clobbered in an + asm statement, i.e., this is the clobbers list of an extended asm + + asm asm-qualifiers ( AssemblerTemplate + : OutputOperands + [ : InputOperands + [ : Clobbers ] ]) + + and is not to be confused with the early clobbers sets. */ + HARD_REG_SET m_clobbered; + +public: + tree operand; + + gimplify_reg_info (unsigned num_alternatives, + unsigned num_outputs, + unsigned num_inputs) + : m_num_alternatives{num_alternatives} + , m_num_outputs{num_outputs} + , m_num_inputs{num_inputs} + { + CLEAR_HARD_REG_SET (m_clobbered); + + /* If there are no alternatives, then there are no outputs/inputs and there + is nothing to do on our end. Thus, we are dealing most likely with a + basic asm. */ + if (num_alternatives == 0) + return; + + unsigned buf_size = num_alternatives * 3 + num_outputs; + m_buf = new HARD_REG_SET[buf_size]; + for (unsigned i = 0; i < buf_size; ++i) + CLEAR_HARD_REG_SET (m_buf[i]); + m_alt_output = &m_buf[0]; + m_alt_input = &m_buf[num_alternatives]; + m_early_clobbered_alt = &m_buf[num_alternatives * 2]; + if (num_outputs > 0) + m_early_clobbered_output = &m_buf[num_alternatives * 3]; + else + m_early_clobbered_output = nullptr; + if (num_inputs > 0) + { + m_reg_asm_input = new int[num_inputs]; + for (unsigned i = 0; i < num_inputs; ++i) + m_reg_asm_input[i] = -1; + } + else + m_reg_asm_input = nullptr; + } + + ~gimplify_reg_info () + { + if (m_num_alternatives > 0) + delete[] m_buf; + if (m_num_inputs > 0) + delete[] m_reg_asm_input; + } + + void set_output (unsigned alt, int regno) + { + machine_mode mode = TYPE_MODE (TREE_TYPE (operand)); + add_to_hard_reg_set (&m_alt_output[alt], mode, regno); + } + + void set_input (unsigned alt, int regno) + { + machine_mode mode = TYPE_MODE (TREE_TYPE (operand)); + add_to_hard_reg_set (&m_alt_input[alt], mode, regno); + } + + bool test_alt_output (unsigned alt, int regno) const + { + gcc_checking_assert (alt < m_num_alternatives); + return TEST_HARD_REG_BIT (m_alt_output[alt], regno); + } + + bool test_alt_input (unsigned alt, int regno) const + { + gcc_checking_assert (alt < m_num_alternatives); + return TEST_HARD_REG_BIT (m_alt_input[alt], regno); + } + + void set_reg_asm_input (unsigned input, int regno) + { + gcc_checking_assert (input < m_num_inputs); + m_reg_asm_input[input] = regno; + } + + bool used_by_different_reg_asm_input (unsigned input, int regno) + { + gcc_checking_assert (input < m_num_inputs); + for (unsigned i = 0; i < m_num_inputs; ++i) + { + if (i == input) + continue; + if (m_reg_asm_input[i] == regno) + return true; + } + return false; + } + + void set_early_clobbered (unsigned alt, unsigned output, int regno) const + { + gcc_checking_assert (alt < m_num_alternatives); + gcc_checking_assert (output < m_num_outputs); + machine_mode mode = TYPE_MODE (TREE_TYPE (operand)); + add_to_hard_reg_set (&m_early_clobbered_alt[alt], mode, regno); + add_to_hard_reg_set (&m_early_clobbered_output[output], mode, regno); + } + + bool test_early_clobbered_alt (unsigned alt, int regno) const + { + gcc_checking_assert (alt < m_num_alternatives); + return TEST_HARD_REG_BIT (m_early_clobbered_alt[alt], regno); + } + + bool is_early_clobbered_in_any_output_unequal (unsigned operand, + int regno) const + { + gcc_checking_assert (operand < m_num_outputs); + for (unsigned op = 0; op < m_num_outputs; ++op) + if (op != operand + && TEST_HARD_REG_BIT (m_early_clobbered_output[op], regno)) + return true; + return false; + } + + void set_clobbered (int regno) + { + SET_HARD_REG_BIT (m_clobbered, regno); + } + + bool is_clobbered (int regno) const + { + machine_mode mode = TYPE_MODE (TREE_TYPE (operand)); + return overlaps_hard_reg_set_p (m_clobbered, mode, regno); + } +}; diff --git a/gcc/stmt.cc b/gcc/stmt.cc index 915969ee116..b1faf7a5189 100644 --- a/gcc/stmt.cc +++ b/gcc/stmt.cc @@ -39,6 +39,8 @@ 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 "gimplify_reg_info.h" #include "fold-const.h" #include "varasm.h" @@ -201,6 +203,51 @@ decode_hard_reg_constraint (const char *begin) 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, @@ -217,7 +264,8 @@ decode_hard_reg_constraint (const char *begin) 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, + gimplify_reg_info *reg_info) { const char *constraint = *constraint_p; const char *p; @@ -271,6 +319,9 @@ parse_output_constraint (const char **constraint_p, int operand_num, constraint = *constraint_p; } + unsigned int alt = 0; + bool early_clobbered = false; + /* Loop through the constraint string. */ for (p = constraint + 1; *p; ) { @@ -290,12 +341,21 @@ parse_output_constraint (const char **constraint_p, int operand_num, } break; - case '?': case '!': case '*': case '&': case '#': + case '&': + early_clobbered = true; + break; + + case '?': case '!': case '*': case '#': case '$': case '^': 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 ',': + ++alt; + early_clobbered = false; break; case '0': case '1': case '2': case '3': case '4': @@ -318,6 +378,54 @@ parse_output_constraint (const char **constraint_p, int operand_num, case '{': { + if (!targetm.lra_p ()) + { + error ("hard register constraints are only supported while using LRA"); + return false; + } + if (reg_info) + { + int regno = decode_hard_reg_constraint (p); + if (regno < 0) + { + error ("invalid output constraint: %s", p); + return false; + } + if (reg_info->test_alt_output (alt, regno)) + { + error ("multiple outputs to hard register: %s", + reg_names[regno]); + return false; + } + else + { + reg_info->set_output (alt, regno); + if (early_clobbered) + reg_info->set_early_clobbered (alt, operand_num, regno); + } + if (reg_info->is_clobbered (regno)) + { + error ("hard register constraint for output %i conflicts " + "with % clobber list", operand_num); + return false; + } + if (VAR_P (reg_info->operand) + && DECL_HARD_REGISTER (reg_info->operand)) + { + tree id = DECL_ASSEMBLER_NAME (reg_info->operand); + const char *asmspec = IDENTIFIER_POINTER (id) + 1; + int regno_op = decode_reg_name (asmspec); + if (regno != regno_op) + { + error ("constraint and register % for output " + "operand %i are unsatisfiable", operand_num); + return false; + } + } + machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand)); + if (!hardreg_ok_p (regno, mode, operand_num)) + return false; + } *allows_reg = true; break; } @@ -333,6 +441,34 @@ parse_output_constraint (const char **constraint_p, int operand_num, *allows_mem = true; else insn_extra_constraint_allows_reg_mem (cn, allows_reg, allows_mem); + if (reg_info && *allows_reg + && VAR_P (reg_info->operand) + && DECL_HARD_REGISTER (reg_info->operand)) + { + tree id = DECL_ASSEMBLER_NAME (reg_info->operand); + const char *asmspec = IDENTIFIER_POINTER (id) + 1; + int regno = decode_reg_name (asmspec); + if (regno < 0) + { + error ("invalid register name for %q+D", reg_info->operand); + return false; + } + if (reg_info->test_alt_output (alt, regno)) + { + error ("multiple outputs to hard register: %s", + reg_names[regno]); + return false; + } + else + { + reg_info->set_output (alt, regno); + if (early_clobbered) + reg_info->set_early_clobbered (alt, operand_num, regno); + } + machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand)); + if (!hardreg_ok_p (regno, mode, operand_num)) + return false; + } break; } @@ -350,7 +486,8 @@ 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, + gimplify_reg_info *reg_info) { const char *constraint = *constraint_p; const char *orig_constraint = constraint; @@ -365,6 +502,9 @@ parse_input_constraint (const char **constraint_p, int input_num, /* Make sure constraint has neither `=', `+', nor '&'. */ + unsigned int alt = 0; + unsigned long match = 0; + for (j = 0; j < c_len; j += CONSTRAINT_LEN (constraint[j], constraint+j)) switch (constraint[j]) { @@ -391,7 +531,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 ',': + ++alt; break; /* Whether or not a numeric constraint allows a register is @@ -403,7 +547,6 @@ parse_input_constraint (const char **constraint_p, int input_num, case '5': case '6': case '7': case '8': case '9': { char *end; - unsigned long match; saw_match = true; @@ -443,6 +586,58 @@ parse_input_constraint (const char **constraint_p, int input_num, case '{': { + if (!targetm.lra_p ()) + { + error ("hard register constraints are only supported while using LRA"); + return false; + } + if (reg_info) + { + int regno = decode_hard_reg_constraint (constraint + j); + if (regno < 0) + { + error ("invalid input constraint: %s", constraint + j); + return false; + } + if (reg_info->test_alt_input (alt, regno) + || reg_info->used_by_different_reg_asm_input (input_num, regno)) + { + error ("multiple inputs to hard register: %s", + reg_names[regno]); + return false; + } + else + reg_info->set_input (alt, regno); + if (reg_info->is_clobbered (regno)) + { + error ("hard register constraint for input %i conflicts " + "with % clobber list", input_num); + return false; + } + if (constraint == orig_constraint + && reg_info->test_early_clobbered_alt (alt, regno)) + { + error ("invalid hard register usage between earlyclobber " + "operand and input operand"); + return false; + } + if (VAR_P (reg_info->operand) + && DECL_HARD_REGISTER (reg_info->operand)) + { + tree id = DECL_ASSEMBLER_NAME (reg_info->operand); + const char *asmspec = IDENTIFIER_POINTER (id) + 1; + int regno_op = decode_reg_name (asmspec); + if (regno != regno_op) + { + error ("constraint and register % for input " + "operand %i are unsatisfiable", input_num); + return false; + } + } + machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand)); + if (!hardreg_ok_p (regno, mode, input_num)) + return false; + } *allows_reg = true; break; } @@ -463,6 +658,38 @@ parse_input_constraint (const char **constraint_p, int input_num, *allows_mem = true; else insn_extra_constraint_allows_reg_mem (cn, allows_reg, allows_mem); + if (reg_info && *allows_reg + && VAR_P (reg_info->operand) + && DECL_HARD_REGISTER (reg_info->operand)) + { + tree id = DECL_ASSEMBLER_NAME (reg_info->operand); + const char *asmspec = IDENTIFIER_POINTER (id) + 1; + int regno = decode_reg_name (asmspec); + if (regno < 0) + { + error ("invalid register name for %q+D", reg_info->operand); + return false; + } + if (reg_info->test_alt_input (alt, regno)) + { + error ("multiple inputs to hard register: %s", + reg_names[regno]); + return false; + } + reg_info->set_reg_asm_input (input_num, regno); + if ((constraint == orig_constraint + && reg_info->test_early_clobbered_alt (alt, regno)) + || (constraint != orig_constraint + && reg_info->is_early_clobbered_in_any_output_unequal + (match, regno))) + { + error ("invalid hard register usage between earlyclobber operand and input operand"); + return false; + } + machine_mode mode = TYPE_MODE (TREE_TYPE (reg_info->operand)); + if (!hardreg_ok_p (regno, mode, input_num)) + return false; + } break; } diff --git a/gcc/stmt.h b/gcc/stmt.h index 7d79d682645..21116411d71 100644 --- a/gcc/stmt.h +++ b/gcc/stmt.h @@ -20,11 +20,15 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_STMT_H #define GCC_STMT_H +class gimplify_reg_info; + extern void expand_label (tree); extern bool parse_output_constraint (const char **, int, int, int, - bool *, bool *, bool *); + bool *, bool *, bool *, + gimplify_reg_info * = nullptr); extern bool parse_input_constraint (const char **, int, int, int, int, - const char * const *, bool *, bool *); + const char * const *, bool *, bool *, + gimplify_reg_info * = nullptr); extern int decode_hard_reg_constraint (const char *); extern tree resolve_asm_operand_names (tree, tree, tree, tree); #ifdef HARD_CONST 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..0d7c2f210d8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-error-1.c @@ -0,0 +1,83 @@ +/* { 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" } */ + + __asm__ ("" : "="GPR1 (gpr1)); + __asm__ ("" : "="GPR2 (gpr1)); /* { dg-error "constraint and register 'asm' for output operand 0 are unsatisfiable" } */ + __asm__ ("" :: GPR2 (gpr1)); /* { dg-error "constraint and register 'asm' for input operand 0 are unsatisfiable" } */ + __asm__ ("" : "="GPR1 (x) : "0" (gpr1)); + __asm__ ("" : "="GPR1 GPR2 (x) : "0" (gpr1)); /* { dg-error "constraint and register 'asm' for input operand 0 are unsatisfiable" } */ + + __asm__ ("" : "=&"GPR1 (x) : "0" (gpr1)); + __asm__ ("" : "=&"GPR1 (x) : "0" (42)); + __asm__ ("" : "=&"GPR2","GPR1 (x) : "r,"GPR1 (42)); + __asm__ ("" : "="GPR2",&"GPR1 (x) : "r,"GPR1 (42)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ + __asm__ ("" : "=&"GPR1 (x) : GPR1 (42)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ + __asm__ ("" : "=&"GPR2","GPR1 (x) : "r,r" (gpr1)); + __asm__ ("" : "="GPR2",&"GPR1 (x) : "r,r" (gpr1)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ + __asm__ ("" : "=&r" (gpr1) : GPR1 (42)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ + __asm__ ("" : "=&"GPR1 (x), "=r" (y) : "1" (gpr1)); /* { dg-error "invalid hard register usage between earlyclobber operand and input operand" } */ +} 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..d0d5cfe14ea --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-error-2.c @@ -0,0 +1,26 @@ +/* { 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 GPR1 "{x4}" +# define GPR2_RAW "x5" +#elif defined (__s390__) +# define GPR1 "{r4}" +# define GPR2_RAW "r5" +#endif + +#define GPR2 "{"GPR2_RAW"}" + +void +test (void) +{ + __asm__ ("" :: GPR1 ((__int128) 42)); + __asm__ ("" :: GPR2 ((__int128) 42)); /* { dg-error "register .* for operand 0 isn't suitable for data type" } */ + __asm__ ("" :: GPR1 ((__int128) 42), GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */ + + __int128 x; + __asm__ ("" : "="GPR1 (x) :: GPR2_RAW); /* { dg-error "hard register constraint for output 0 conflicts with 'asm' clobber list" } */ + __asm__ ("" : "=r" (x) : GPR1 (x) : GPR2_RAW); /* { dg-error "hard register constraint for input 0 conflicts with 'asm' clobber list" } */ +} 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..17b2317db9b --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-error-3.c @@ -0,0 +1,27 @@ +/* { dg-do compile { target arm-*-* s390-*-* } } */ +/* { dg-options "-std=c99" } we need long long */ +/* { dg-additional-options "-march=armv7-a" { target arm-*-* } } */ + +/* Test register pairs. */ + +#if defined (__arm__) +# define GPR1 "{r4}" +# define GPR2_RAW "r5" +#elif defined (__s390__) +# define GPR1 "{r4}" +# define GPR2_RAW "r5" +#endif + +#define GPR2 "{"GPR2_RAW"}" + +void +test (void) +{ + __asm__ ("" :: GPR1 (42ll)); + __asm__ ("" :: GPR2 (42ll)); /* { dg-error "register .* for operand 0 isn't suitable for data type" } */ + __asm__ ("" :: GPR1 (42ll), GPR2 (42)); /* { dg-error "multiple inputs to hard register" } */ + + long long x; + __asm__ ("" : "="GPR1 (x) :: GPR2_RAW); /* { dg-error "hard register constraint for output 0 conflicts with 'asm' clobber list" } */ + __asm__ ("" : "=r" (x) : GPR1 (x) : GPR2_RAW); /* { dg-error "hard register constraint for input 0 conflicts with 'asm' clobber list" } */ +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-error-4.c b/gcc/testsuite/gcc.dg/asm-hard-reg-error-4.c new file mode 100644 index 00000000000..51fde23e63c --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-error-4.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ + +/* Verify output operands. */ + +int +test (void) +{ + int x; + register int y __asm__ ("0"); + + /* Preserve status quo and don't error out. */ + __asm__ ("" : "=r" (x), "=r" (x)); + + /* Although it is somewhat inconsistent, we error out in case of register asm for status quo. */ + __asm__ ("" : "=r" (y), "=r" (y)); /* { dg-error "multiple outputs to hard register" } */ + + /* Be more strict for hard register constraints and error out. */ + __asm__ ("" : "={0}" (x), "={1}" (x)); /* { dg-error "multiple outputs to lvalue 'x'" } */ + + /* Still error out in case of a mixture. */ + __asm__ ("" : "=r" (x), "={1}" (x)); /* { dg-error "multiple outputs to lvalue 'x'" } */ + + return x + y; +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-error-5.c b/gcc/testsuite/gcc.dg/asm-hard-reg-error-5.c new file mode 100644 index 00000000000..85398f04cc8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-error-5.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ + +/* Test clobbers. + See asm-hard-reg-error-{2,3}.c for tests involving register pairs. */ + +int +test (void) +{ + int x, y; + __asm__ ("" : "={0}" (x), "={1}" (y) : : "1"); /* { dg-error "hard register constraint for output 1 conflicts with 'asm' clobber list" } */ + __asm__ ("" : "={0}" (x) : "{0}" (y), "{1}" (y) : "1"); /* { dg-error "hard register constraint for input 1 conflicts with 'asm' clobber list" } */ + return x + y; +} 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/lib/scanasm.exp b/gcc/testsuite/lib/scanasm.exp index 737eefc655e..1cc3cdcc904 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 From patchwork Fri Oct 25 09:57:19 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: 99572 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D6C753858C2B for ; Fri, 25 Oct 2024 10:01:05 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D6C753858C2B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1729850465; bh=FxvCkey0BiZD9eS15zjpeHqlR0HPtEOwSHqB64/J+Yk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=yQw2oWqicpUURs2sabU09WJalPemt3mMSWcWdVpRtDw0k8AWIxL02MjeT2bU9VD// dA8/KEUdTZb2i2xUu11EeEswiG/+v9E4FKgDiOwUwkZBwAdeR0exFGiMDBt9GE4oOT RcyFSPyNrrjcGyk6Jn059Bf8PJ7Odt7HXg+yH+q8= 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 4CCCB385841E; Fri, 25 Oct 2024 09:57:53 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4CCCB385841E 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 4CCCB385841E 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=1729850278; cv=none; b=S00rO6dBlDAj+6M2bmoQlGr7ciPmkRf0gAjz5OAnuxaTp+mQZhZthotW7oE4ic+8Tu1i5U9xA5ubVQxS3X2rowuajkFDxVLIlCGesL4xZfkqhxDwYFiblebS3Nwh27sq3cJ2ifuNrHREveqwKnXSK9NRXvqSzl9BVDFqowZkKzg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729850278; c=relaxed/simple; bh=vD59koWdPUtzO8lvWEtcR6hItWeg7UjpiAVsV7sLAws=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=Wo9WvQWkQ3FfOwtN4ssq6UaxFddl6LDrYKwQjfQwYKAOilAiV8zPM57M4FTdG96upRTN4+xnagDrc9sBBmW2REpV+Js0LqM/rW6trBa9uBGOlbZ/4OjSHGh3ieAjOkgY2/2Q2uL5j6JgpvBwIylC2lROc9Myix3WAH8gywZU5qg= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from pps.filterd (m0360072.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 49P2vJf5012559; Fri, 25 Oct 2024 09:57:53 GMT Received: from ppma11.dal12v.mail.ibm.com (db.9e.1632.ip4.static.sl-reverse.com [50.22.158.219]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 42fbw47gdx-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Oct 2024 09:57:52 +0000 (GMT) Received: from pps.filterd (ppma11.dal12v.mail.ibm.com [127.0.0.1]) by ppma11.dal12v.mail.ibm.com (8.18.1.2/8.18.1.2) with ESMTP id 49P6tS6Y008757; Fri, 25 Oct 2024 09:57:52 GMT Received: from smtprelay02.fra02v.mail.ibm.com ([9.218.2.226]) by ppma11.dal12v.mail.ibm.com (PPS) with ESMTPS id 42emkaw2uw-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Oct 2024 09:57:51 +0000 Received: from smtpav01.fra02v.mail.ibm.com (smtpav01.fra02v.mail.ibm.com [10.20.54.100]) by smtprelay02.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 49P9voUe31129876 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 25 Oct 2024 09:57:50 GMT Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 57F10200D6; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 41F5A200D4; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) Received: from a8345010.lnxne.boe (unknown [9.152.108.100]) by smtpav01.fra02v.mail.ibm.com (Postfix) with ESMTPS; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) From: Stefan Schulze Frielinghaus To: gcc-patches@gcc.gnu.org Cc: Stefan Schulze Frielinghaus Subject: [PATCH v3 3/4] genoutput: Verify hard register constraints Date: Fri, 25 Oct 2024 11:57:19 +0200 Message-ID: <20241025095720.2990683-4-stefansf@gcc.gnu.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20241025095720.2990683-1-stefansf@gcc.gnu.org> References: <20241025095720.2990683-1-stefansf@gcc.gnu.org> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Proofpoint-ORIG-GUID: V2QcSeQ-RDYrGqYcqmFULskPuhTLrJ4- X-Proofpoint-GUID: V2QcSeQ-RDYrGqYcqmFULskPuhTLrJ4- X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1051,Hydra:6.0.680,FMLib:17.12.62.30 definitions=2024-10-15_01,2024-10-11_01,2024-09-30_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 impostorscore=0 mlxscore=0 bulkscore=0 suspectscore=0 spamscore=0 phishscore=0 adultscore=0 priorityscore=1501 clxscore=1034 lowpriorityscore=0 malwarescore=0 mlxlogscore=777 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2409260000 definitions=main-2410250075 X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_DMARC_STATUS, RCVD_IN_DNSWL_LOW, 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~patchwork=sourceware.org@gcc.gnu.org Since genoutput has no information about hard register names we cannot statically verify those names in constraints of the machine description. Therefore, we have to do it at runtime. Although verification shouldn't be too expensive, restrict it to checking builds. This should be sufficient since hard register constraints in machine descriptions probably change rarely, and each commit should be tested with checking anyway, or at the very least before a release is taken. --- gcc/genoutput.cc | 46 ++++++++++++++++++++++++++++++++++++++++++++++ gcc/output.h | 2 ++ gcc/toplev.cc | 4 ++++ 3 files changed, 52 insertions(+) diff --git a/gcc/genoutput.cc b/gcc/genoutput.cc index 2ffb2fb28d2..4f4fde83608 100644 --- a/gcc/genoutput.cc +++ b/gcc/genoutput.cc @@ -200,6 +200,8 @@ static const char indep_constraints[] = ",=+%*?!^$#&g"; static class constraint_data * constraints_by_letter_table[1 << CHAR_BIT]; +static hash_set used_reg_names; + static int mdep_constraint_len (const char *, file_location, int); static void note_constraint (md_rtx_info *); @@ -1156,6 +1158,45 @@ main (int argc, const char **argv) output_insn_data (); output_get_insn_name (); + /* Since genoutput has no information about hard register names we cannot + statically verify hard register names in constraints of the machine + description. Therefore, we have to do it at runtime. Although + verification shouldn't be too expensive, restrict it to checking builds. + */ + printf ("\n\n#if CHECKING_P\n"); + if (used_reg_names.is_empty ()) + printf ("void verify_reg_names_in_constraints () { }\n"); + else + { + size_t max_len = 0; + for (auto it = used_reg_names.begin (); it != used_reg_names.end (); ++it) + { + size_t len = strlen (*it); + if (len > max_len) + max_len = len; + } + printf ("void\nverify_reg_names_in_constraints ()\n{\n"); + printf (" static const char hregnames[%zu][%zu] = {\n", + used_reg_names.elements (), max_len + 1); + auto it = used_reg_names.begin (); + while (it != used_reg_names.end ()) + { + printf (" \"%s\"", *it); + ++it; + if (it != used_reg_names.end ()) + printf (","); + printf ("\n"); + } + printf (" };\n"); + printf (" for (size_t i = 0; i < %zu; ++i)\n", + used_reg_names.elements ()); + printf (" if (decode_reg_name (hregnames[i]) < 0)\n"); + printf (" internal_error (\"invalid register %%qs used in " + "constraint of machine description\", hregnames[i]);\n"); + printf ("}\n"); + } + printf ("#endif\n"); + fflush (stdout); return (ferror (stdout) != 0 || have_error ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE); @@ -1294,6 +1335,11 @@ mdep_constraint_len (const char *s, file_location loc, int opno) ptrdiff_t len = end - s; if (*end == '}' && len > 1 && len < 31) { + char *regname = new char[len]; + memcpy (regname, s + 1, len - 1); + regname[len - 1] = '\0'; + if (used_reg_names.add (regname)) + delete[] regname; return len + 1; } } diff --git a/gcc/output.h b/gcc/output.h index 46b0033b221..5f0f8a6098c 100644 --- a/gcc/output.h +++ b/gcc/output.h @@ -636,4 +636,6 @@ extern int default_address_cost (rtx, machine_mode, addr_space_t, bool); /* Stack usage. */ extern void output_stack_usage (void); +extern void verify_reg_names_in_constraints (); + #endif /* ! GCC_OUTPUT_H */ diff --git a/gcc/toplev.cc b/gcc/toplev.cc index 5df59b79c80..67095dcd144 100644 --- a/gcc/toplev.cc +++ b/gcc/toplev.cc @@ -1817,6 +1817,10 @@ backend_init_target (void) static void backend_init (void) { +#if CHECKING_P + verify_reg_names_in_constraints (); +#endif + init_emit_once (); init_rtlanal (); From patchwork Fri Oct 25 09:57:20 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: 99571 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A4D123858405 for ; Fri, 25 Oct 2024 10:00:27 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A4D123858405 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1729850427; bh=5k8g5yZY81PsyNoiaowBL64w+G8ntpOxUdRzn24RFRY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=Aeuqkw8TDDeH8FRhLwOsO4RF4g5PyI1FwywOqLXoiMbf+r74CN70ddec6qD212mTL +BEFNEqMXnDliWYxXeanIDTz13UmJr3g47GNK5mW+DPG2QkQbaBLSjaM14hclwwXbL wRmdIVa1l9ryQMc+YRx141XvNbj4htDTTrHLk6ck= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) by sourceware.org (Postfix) with ESMTPS id 355353858C3A; Fri, 25 Oct 2024 09:57:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 355353858C3A 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 355353858C3A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=148.163.156.1 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729850277; cv=none; b=YwkkEcRNfjwIMHLb+dXs8DSEHK0iO0aSyf3EnAIcGdjTOHZoPVB3cU9fTgDqGPoBsffGpCP6s1Bzbtpkm4gp+9pNHfi6WTgJiUZXMGS4C8wT59JmtkpyJPnL2kVZOmNSEAxSKzrrQXSnxCUG5KLf/jkukx/x9uhCPBDfU25X2Ac= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729850277; c=relaxed/simple; bh=k7sR0eC5U4EwLnweE2SseceO4kBy8pOPg7PD4WDkHrM=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=FDdJehEquZP3NR1Szln25GEOgZ2hLrdln/X05HnA984gEe9WQRKsyZ2umx7tA5AI8zayJ+9orxwQNImC4GhGuKaRSByOxJcriyx83sb6+V6SbjOOCLTo3sv8bQU0LdUilG5XnOybwiK1imFoTx7zRfi/8HOFZ34BOUEkqQp6yt0= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from pps.filterd (m0356517.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 49P2c7TL015299; Fri, 25 Oct 2024 09:57:46 GMT Received: from ppma13.dal12v.mail.ibm.com (dd.9e.1632.ip4.static.sl-reverse.com [50.22.158.221]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 42emae5jtr-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Oct 2024 09:57:45 +0000 (GMT) Received: from pps.filterd (ppma13.dal12v.mail.ibm.com [127.0.0.1]) by ppma13.dal12v.mail.ibm.com (8.18.1.2/8.18.1.2) with ESMTP id 49P6H00R012456; Fri, 25 Oct 2024 09:57:45 GMT Received: from smtprelay02.fra02v.mail.ibm.com ([9.218.2.226]) by ppma13.dal12v.mail.ibm.com (PPS) with ESMTPS id 42emhfn3jr-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Oct 2024 09:57:45 +0000 Received: from smtpav01.fra02v.mail.ibm.com (smtpav01.fra02v.mail.ibm.com [10.20.54.100]) by smtprelay02.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 49P9vhpS53215730 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 25 Oct 2024 09:57:43 GMT Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 7160B200CF; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 5D4FC200D2; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) Received: from a8345010.lnxne.boe (unknown [9.152.108.100]) by smtpav01.fra02v.mail.ibm.com (Postfix) with ESMTPS; Fri, 25 Oct 2024 09:57:42 +0000 (GMT) From: Stefan Schulze Frielinghaus To: gcc-patches@gcc.gnu.org Cc: Stefan Schulze Frielinghaus Subject: [PATCH v3 4/4] Rewrite register asm into hard register constraints Date: Fri, 25 Oct 2024 11:57:20 +0200 Message-ID: <20241025095720.2990683-5-stefansf@gcc.gnu.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20241025095720.2990683-1-stefansf@gcc.gnu.org> References: <20241025095720.2990683-1-stefansf@gcc.gnu.org> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Proofpoint-ORIG-GUID: GZNBEalgwMSfxvDXkim2k18GXxyvhA1J X-Proofpoint-GUID: GZNBEalgwMSfxvDXkim2k18GXxyvhA1J X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1051,Hydra:6.0.680,FMLib:17.12.62.30 definitions=2024-10-15_01,2024-10-11_01,2024-09-30_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 suspectscore=0 phishscore=0 mlxlogscore=801 adultscore=0 spamscore=0 malwarescore=0 clxscore=1034 lowpriorityscore=0 bulkscore=0 mlxscore=0 impostorscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2409260000 definitions=main-2410250075 X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_DMARC_STATUS, RCVD_IN_DNSWL_LOW, 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~patchwork=sourceware.org@gcc.gnu.org Currently a register asm already materializes during expand. This means, a hard register is allocated for the very first access of a register asm as e.g. in an assignment. As a consequence this might lead to suboptimal register allocation if the assignment and the using asm statement are spread far apart. Even more problematic are function calls in between register asm assignments and its using asm statement since hard registers may be clobbered by a call. The former may be solved by pulling register asm assignments and asm statements close by. However, the latter is not easily solved since sometimes function calls are implicit. For example int foo (int *x) { register int y asm ("0") = 42; register int z asm ("1") = *x; asm ("bar\t%0,%1" : "+r" (z) : "r" (y)); return z; } If compiled with address sanitizer, then a function call is introduced for the memory load which in turn may interfer with the initialization of register asm y. Likewise, for some targets and configurations even an operation like an addition may lead to an implicit library call. In contrast hard register constraints materialize during register allocation and therefore do not suffer from this, i.e., asm operands are kept in pseudos until RA. This patch adds the feature of rewriting local register asm into code which exploits hard register constraints. For example register int global asm ("r3"); int foo (int x0) { register int x asm ("r4") = x0; register int y asm ("r5"); asm ("bar\t%0,%1,%2" : "=r" (x) : "0" (x), "r" (global)); x += 42; asm ("baz\t%0,%1" : "=r" (y) : "r" (x)); return y; } is rewritten during gimplification into register int global asm ("r3"); int foo (int x0) { int x = x0; int y; asm ("bar\t%0,%1,%2" : "={r4}" (x) : "0" (x), "r" (global)); x += 42; asm ("baz\t%0,%1" : "={r5}" (y) : "{r4}" (x)); return y; } The resulting code solely relies on hard register constraints modulo global register asm. Since I consider this as an experimental feature it is hidden behind new flag -fdemote-register-asm (I'm open for other naming suggestions). --- gcc/common.opt | 4 + gcc/gimplify.cc | 87 +++++++++++++++++++ .../gcc.dg/asm-hard-reg-demotion-1.c | 19 ++++ .../gcc.dg/asm-hard-reg-demotion-2.c | 19 ++++ .../gcc.dg/asm-hard-reg-demotion-error-1.c | 29 +++++++ gcc/testsuite/gcc.dg/asm-hard-reg-demotion.h | 52 +++++++++++ 6 files changed, 210 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-demotion-1.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-demotion-2.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-demotion-error-1.c create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-demotion.h diff --git a/gcc/common.opt b/gcc/common.opt index d270e524ff4..8fcbb54018a 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -3426,6 +3426,10 @@ fverbose-asm Common Var(flag_verbose_asm) Add extra commentary to assembler output. +fdemote-register-asm +Common Var(flag_demote_register_asm) Init(0) +Demote local register asm and use hard register constraints instead. + fvisibility= Common Joined RejectNegative Enum(symbol_visibility) Var(default_visibility) Init(VISIBILITY_DEFAULT) -fvisibility=[default|internal|hidden|protected] Set the default symbol visibility. diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc index e39ba2d3766..c0e2e63bda2 100644 --- a/gcc/gimplify.cc +++ b/gcc/gimplify.cc @@ -7061,6 +7061,72 @@ num_alternatives (const_tree link) return num + 1; } +/* Keep track of all register asm which have been replaced by hard register + constraints. After all asm statements of a function have been processed, + demote those to ordinary variables. */ +static hash_set demote_register_asm; + +static void +gimplify_demote_register_asm (tree link) +{ + tree op = TREE_VALUE (link); + if (!VAR_P (op) || !DECL_HARD_REGISTER (op) || is_global_var (op)) + return; + tree id = DECL_ASSEMBLER_NAME (op); + const char *regname = IDENTIFIER_POINTER (id); + ++regname; + int regno = decode_reg_name (regname); + if (regno < 0) + /* This indicates an error and we error out later on. */ + return; + const char *constraint + = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link))); + auto_vec constraint_new; + for (const char *p = constraint; *p; ) + { + bool pushed = false; + switch (*p) + { + case '+': case '=': case '%': case '?': case '!': case '*': case '&': + case '#': case '$': case '^': case '{': 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 '0': + case '1': case '2': case '3': case '4': case '5': case '6': case '7': + case '8': case '9': case '[': case '<': case '>': case 'g': case 'X': + break; + + default: + if (!ISALPHA (*p)) + break; + enum constraint_num cn = lookup_constraint (p); + enum reg_class rclass = reg_class_for_constraint (cn); + if (rclass != NO_REGS || insn_extra_address_constraint (cn)) + { + constraint_new.safe_push ('{'); + size_t len = strlen (regname); + for (size_t i = 0; i < len; ++i) + constraint_new.safe_push (regname[i]); + constraint_new.safe_push ('}'); + pushed = true; + } + break; + } + + for (size_t len = CONSTRAINT_LEN (*p, p); len; len--, p++) + { + if (!pushed) + constraint_new.safe_push (*p); + if (*p == '\0') + break; + } + } + constraint_new.safe_push ('\0'); + unsigned int len = constraint_new.length (); + tree str = build_string (len, constraint_new.address ()); + TREE_VALUE (TREE_PURPOSE (link)) = str; + demote_register_asm.add (op); +} + /* Gimplify the operands of an ASM_EXPR. Input operands should be a gimple value; output operands should be a gimple lvalue. */ @@ -7464,6 +7530,20 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) /* Do not add ASMs with errors to the gimple IL stream. */ if (ret != GS_ERROR) { + if (flag_demote_register_asm) + { + for (unsigned i = 0; i < vec_safe_length (outputs); ++i) + { + tree link = (*outputs)[i]; + gimplify_demote_register_asm (link); + } + for (unsigned i = 0; i < vec_safe_length (inputs); ++i) + { + tree link = (*inputs)[i]; + gimplify_demote_register_asm (link); + } + } + stmt = gimple_build_asm_vec (TREE_STRING_POINTER (ASM_STRING (expr)), inputs, outputs, clobbers, labels); @@ -19595,6 +19675,13 @@ gimplify_body (tree fndecl, bool do_parms) } } + for (auto op : demote_register_asm) + { + DECL_REGISTER (op) = 0; + DECL_HARD_REGISTER (op) = 0; + } + demote_register_asm.empty (); + if ((flag_openacc || flag_openmp || flag_openmp_simd) && gimplify_omp_ctxp) { diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-demotion-1.c b/gcc/testsuite/gcc.dg/asm-hard-reg-demotion-1.c new file mode 100644 index 00000000000..541a66a8d05 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-demotion-1.c @@ -0,0 +1,19 @@ +/* { dg-do run { target aarch64*-*-* powerpc64*-*-* riscv64-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-O2 -fdemote-register-asm" } */ + +#include "asm-hard-reg-demotion.h" + +int +main (void) +{ + if (bar (0) != 0 + || bar (1) != 1 + || bar (2) != 2 + || bar (32) != 32 + || 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-demotion-2.c b/gcc/testsuite/gcc.dg/asm-hard-reg-demotion-2.c new file mode 100644 index 00000000000..3d216d440af --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-demotion-2.c @@ -0,0 +1,19 @@ +/* { dg-do run { target aarch64*-*-* powerpc64*-*-* riscv64-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-options "-O2 -fno-demote-register-asm" } */ + +#include "asm-hard-reg-demotion.h" + +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-demotion-error-1.c b/gcc/testsuite/gcc.dg/asm-hard-reg-demotion-error-1.c new file mode 100644 index 00000000000..097a6ff901d --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-demotion-error-1.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-options "-fdemote-register-asm" } */ + +/* Verify input operands. */ + +int +test (int x) +{ + register int y __asm__ ("0") = x; + + /* Preserve status quo for register asm and don't error out. This must hold + true even if we replace register asm with hard register constraints. */ + __asm__ ("" : "+r" (y) : "r" (y)); + __asm__ ("" : "=r" (y) : "0" (y), "r" (y)); + __asm__ ("" : "=r" (y) : "r" (y), "r" (y)); + + /* Be more strict for hard register constraints and error out. */ + __asm__ ("" : "+{0}" (x) : "{0}" (x)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" : "={0}" (x) : "0" (x), "{0}" (x)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" : "=r" (x) : "{0}" (x), "{0}" (x)); /* { dg-error "multiple inputs to hard register" } */ + + /* Still error out in case of a mixture of both constructs. */ + __asm__ ("" : "+{0}" (x) : "r" (y)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" : "={0}" (x) : "0" (x), "r" (y)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" : "+r" (y) : "{0}" (x)); /* { dg-error "multiple inputs to hard register" } */ + __asm__ ("" : "=r" (y) : "0" (y), "{0}" (x)); /* { dg-error "multiple inputs to hard register" } */ + + return x + y; +} diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-demotion.h b/gcc/testsuite/gcc.dg/asm-hard-reg-demotion.h new file mode 100644 index 00000000000..6d72f622ce9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-demotion.h @@ -0,0 +1,52 @@ +/* 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; +}