> Am 25.06.2026 um 19:23 schrieb Richard Biener <[email protected]>:
>
>
>
>> Am 25.06.2026 um 18:45 schrieb Stefan Schulze Frielinghaus
>> <[email protected]>:
>>
>> On Thu, Jun 25, 2026 at 03:26:36PM +0200, Richard Biener wrote:
>>>> On Sun, May 24, 2026 at 7:40 PM Stefan Schulze Frielinghaus
>>>> <[email protected]> wrote:
>>>>
>>>> From: Stefan Schulze Frielinghaus <[email protected]>
>>>>
>>>> This is a followup to
>>>> https://gcc.gnu.org/pipermail/gcc-patches/2026-May/717097.html
>>>> with the following changes:
>>>>
>>>> I have renamed -fdemote-register-asm into -fstrict-extended-asm. This
>>>> should
>>>> give users a better intuition if things break. I'm using
>>>> -fstrict-extended-asm here instead of -fstrict-register-asm since the
>>>> analysis happens at extended asms, or in other words you could still have
>>>> unusual usages of register asm across multiple extended asms which are not
>>>> diagnosed, i.e., the analysis is limited to a single extended asm
>>>> statement.
>>>>
>>>> Speaking of diagnostics, I have added -Wstrict-extended-asm which is
>>>> enabled by
>>>> -Wall for C/C++ in order to diagnose cases for which we error out with
>>>> -fstrict-register-asm (see new tests asm-hard-reg-strict-*.c).
>>>>
>>>> I have generalized the odd ball cases if an lvalue is used for multiple
>>>> output
>>>> operands. Previously, we diagnosed
>>>>
>>>> register int x __asm__ ("5");
>>>> __asm__ __volatile__ ("" : "=r" (x), "=r" (x));
>>>>
>>>> with an error: "invalid hard register usage between output operands"
>>>> This was done only for register asm objects during expand. Now, I
>>>> diagnose if
>>>> any two output operands refer to the same lvalue. This is done
>>>> independently
>>>> of constraints and types of output operands. In order to align all those
>>>> different combinations I went for diagnosing those with: "multiple outputs
>>>> to
>>>> lvalue 'x'". Therefore, for the register asm example from above the
>>>> diagnostics change. I guess having "non-stable" error messages for the
>>>> sake of
>>>> streamlining is fine ... just wanted to make it clear here.
>>>>
>>>> While working on the diagnostics I somehow thought that it would be nice
>>>> here
>>>> and there to refer to a concrete operand. For example, with this patch I
>>>> diagnose
>>>>
>>>> register int x __asm__ ("5") = 42;
>>>> asm ("" : "+r" (x) : "r" (x));
>>>>
>>>> with
>>>>
>>>> t.c:11:3: warning: multiple inputs to hard register: %r5
>>>> [-Wstrict-extended-asm]
>>>> 11 | __asm__ __volatile__ ("" : "+r" (x) : "r" (x));
>>>> | ^~~~~~~
>>>>
>>>> Instead of just pointing to the asm statement it would be nice to have some
>>>> ascii art in order to point to the concrete operands as for example
>>>>
>>>> t.c:11:3: warning: multiple inputs to hard register: %r5
>>>> [-Wstrict-extended-asm]
>>>> 11 | __asm__ __volatile__ ("" : "+r" (x) : "r" (x));
>>>> | ^~~~~~~~~~^
>>>>
>>>> Of course, this example behaved good in the sense that it is easy to draw a
>>>> line between operands. It would be more complicated if operands were
>>>> aligned
>>>> in different rows and what not. So maybe this is just wishful thinking.
>>>> Anyhow, I'm not familiar with the diagnostics system at all, however, if
>>>> someone has a pointer for me, I'm happy to take a look-see.
>>>>
>>>> Cheers,
>>>> Stefan
>>>>
>>>> -- >8 --
>>>>
>>>> Currently local register asm assignments materialize during expand into
>>>> assignments utilizing hard registers. Since hard registers or more
>>>> precisely objects residing in hard registers are not tracked
>>>> individually, those are subject to be clobbered. Well known and
>>>> documented are function calls which may clobber hard registers used for
>>>> register asm objects. For example, compiling on aarch64
>>>>
>>>> register int x asm ("x0") = 0x123;
>>>> register int y asm ("x1") = *ptr;
>>>>
>>>> using address sanitizers results in
>>>>
>>>> x0:SI=0x123
>>>> x0:DI=r104:DI
>>>> call [`__asan_load4'] argc:0
>>>> x1:SI=[r104:DI]
>>>>
>>>> The implicit function call added by the address sanitizer clobbers
>>>> argument register x0 which was previously set for the register asm
>>>> object.
>>>>
>>>> With the advent of hard register constraints, this can be overcome.
>>>> Instead of expanding a register asm assignment directly into a hard
>>>> register assignment, keep the register asm object in a pseudo for as
>>>> long as possible and use a hard register constraint in Extended Asm
>>>> statements which ensures that the object is finally allocated the
>>>> respective hard register. Since local register asm is supposed to have
>>>> an effect only for Extended Asm statements, this coincides with hard
>>>> register constraints which materialize for the respective insn.
>>>>
>>>> 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)
>>>> {
>>>> register int tmp asm ("r5");
>>>> int x = x0;
>>>> int y = tmp;
>>>>
>>>> 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;
>>>> }
>>>>
>>>> Note, since hard register constraints are more strict in order to
>>>> prevent subtle bugs, this in turn means that certain programs are not
>>>> valid after register asm demotion. For example,
>>>>
>>>> register int x asm ("r5") = 42;
>>>> asm ("" : "+r" (x) : "r" (x));
>>>>
>>>> is rewritten into
>>>>
>>>> int x = 42;
>>>> asm ("" : "={r5}" (x) : "0" (x), "{r5}" (x));
>>>>
>>>> Two inputs refer to the very same register which is invalid when using
>>>> hard register constraints. Therefore, currently the transformation is
>>>> hidden behind new flag -fstrict-extended-asm which is disabled by
>>>> default. This patch also adds the new warning -Wstrict-extended-asm
>>>> which is enabled by -Wall in order to diagnose cases for which
>>>> -fstrict-extended-asm errors out.
>>>>
>>>> In order to automatically rewrite register asm into hard register
>>>> constraints, it is crucial that the register referred to by the register
>>>> asm operand is entailed in the register class of the corresponding
>>>> constraint of the operand. If this is not the case, then error out in
>>>> case of -fstrict-extended-asm, or in case of -fno-strict-extended-asm
>>>> -Wstrict-extended-asm emit a warning.
>>>>
>>>> Note, uninitialized register asm objects may be used as inputs. Thus,
>>>> if naively translated into hard register constraints, this would
>>>> introduce reads from uninitialized objects which init-regs pass would
>>>> fix up which in turn would mean that once hard register constraints
>>>> materialize, respective registers would be zeroed (see comment in
>>>> gimplify.cc for more details). This is solved by initializing every
>>>> uninitialized register asm object by a fresh register asm object
>>>> ensuring that it contains the respective register value. Subsequent
>>>> passes remove dead stores in case those objects are eventually
>>>> initialized at later points or are used exclusively as output operands.
>>>> Therefore, in most cases, those temporary register asm objects won't
>>>> materialize. This is not pretty at all but required in order to compile
>>>> real world applications as e.g. glibc for target powerpc64le.
>>>
>>> I wonder if we can fix init-regs to not consider an asm use as use
>>> and skip initialization? (init-regs should go away anyway)
>>> Is there user-level documentation about this "feature"?
>>
>> Even if I disable init-regs temporarily and prevent asmcons to introduce
>> any manual reload, LRA will do and assign a different register to the
>> uninitialized pseudo. For example, prior LRA we have
>>
>> (insn 6 7 12 2 (set (reg:SI 62 [ x ])
>> (asm_operands:SI ("# %0") ("={0}") 0 [
>> (reg/v:SI 60 [ x ])
>> ]
>> [
>> (asm_input:SI ("0") t.c:5)
>> ]
>> [] t.c:5)) "t.c":5:3 -1
>> (expr_list:REG_DEAD (reg/v:SI 60 [ x ])
>> (nil)))
>>
>> where pseudo 60 has no other use. During LRA, an input reload is
>> introduced and pseudo 60 is assigned a different register than 0 which
>> renders this even worse than just zeroing:
>>
>> (insn 17 7 6 2 (set (reg/v:SI 0 %r0 [orig:60 x ] [60])
>> (reg/v:SI 2 %r2 [orig:60 x ] [60])) "t.c":5:3 1949 {*movsi_zarch}
>> (nil))
>> (insn 6 17 18 2 (set (reg/v:SI 0 %r0 [orig:60 x ] [60])
>> (asm_operands:SI ("# %0") ("={0}") 0 [
>> (reg/v:SI 0 %r0 [orig:60 x ] [60])
>> ]
>> [
>> (asm_input:SI ("0") t.c:5)
>> ]
>> [] t.c:5)) "t.c":5:3 -1
>> (nil))
>>
>> Now, r2 is copied into r0 instead of just zeroing it.
>>
>>>
>>> I'd like to see us enable -Wstrict-extended-asm by default
>>
>> With this patch version -Wstrict-extended-asm is enabled if -Wall is
>> given. Do you mean by enabling by default also in case -Wall is not
>> given?
>>
>>> and
>>> document that -fstrict-extended-asm while default off for now
>>> will be enabled by default in a future release.
>>>
>>> The patch looks good to me apart from the uninit reg case which indeed
>>> is ugly. We can eventually live with this, but it would keep pre-RA
>>> local hardregs which is ... unwanted (and fragile as well).
>>
>> Absolutely agree. I was a bit shivering when implementing it ;-) It is
>> a pity that we didn't warn about this previously. Though, what I
>> haven't mentioned so far is that if -Wall is used we of course warn
>> about this. Assuming that this hack is removed for:
>>
>> int
>> test ()
>> {
>> register int x __asm__ ("0");
>> __asm__ ("# %0" : "+r" (x));
>> return x;
>> }
>>
>> we get:
>>
>> t.c: In function 'test':
>> t.c:5:3: warning: 'x' is used uninitialized [-Wuninitialized]
>> 5 | __asm__ ("# %0" : "+r" (x));
>> | ^~~~~~~
>> t.c:4:16: note: 'x' was declared here
>> 4 | register int x __asm__ ("0");
>> | ^
>>
>> My initial intentions were to keep the current behaviour in order to
>> have a broader acceptance ... maybe I went a little bit to far here
>> since preserving the behaviour of uninitialized reads doesn't resonate
>> well on IL. If there is consensus to keep the current behaviour maybe
>> another solution would be to tackle this during LRA ... something I
>> would need to think about.
>
> Yeah, it’s a bit odd that we see initialization from another register there.
> But I also question whether there was any well-defined behavior before this
> proposed change. Certainly the position of the uninit read would have been
> quite random.
Meaning, I’d say we can document it as undefined with -fstrict-extended-asm and
get the zero initialized behavior. If we can get an uninit diagnostic by
default would be nice (I can try to fix that)
Richard
>
> Richard
>
>>
>> Cheers,
>> Stefan
>>
>>>
>>> Richard.
>>>
>>>>
>>>> gcc/ChangeLog:
>>>>
>>>> * common.opt (Wstrict-extended-asm): New warning.
>>>> (fstrict-extended-asm) New flag.
>>>> * doc/invoke.texi: Document new warning and flag.
>>>> * gimplify.cc (gimplify_decl_expr): Deal with uninitialized
>>>> register asm objects.
>>>> (gimplify_demote_register_asm): Helper for gimplify_asm_expr in
>>>> order to prepare demotion of a register asm object into an orderinary
>>>> one by rewriting constaints.
>>>> (gimplify_asm_expr): Add diagnostics logic for new warning.
>>>> (gimplify_body): Cleanup helper hash set.
>>>> * stmt.cc (rclass_entails_registers): Helper while parsing
>>>> constraints.
>>>> (parse_output_constraint): Diagnose if register asm and
>>>> constraint do not coincide.
>>>> (parse_input_constraint): Ditto.
>>>>
>>>> gcc/testsuite/ChangeLog:
>>>>
>>>> * gcc.dg/asm-hard-reg-strict-1.c: New test.
>>>> * gcc.dg/asm-hard-reg-strict-2.c: New test.
>>>> * gcc.dg/asm-hard-reg-strict-3.c: New test.
>>>> * gcc.dg/asm-hard-reg-strict-4.c: New test.
>>>> * gcc.dg/asm-hard-reg-strict-5.c: New test.
>>>> ---
>>>> gcc/common.opt | 8 +
>>>> gcc/doc/invoke.texi | 61 ++++-
>>>> gcc/gimplify.cc | 247 ++++++++++++++++---
>>>> gcc/stmt.cc | 42 ++++
>>>> gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c | 51 ++++
>>>> gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c | 54 ++++
>>>> gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c | 22 ++
>>>> gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c | 25 ++
>>>> gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c | 65 +++++
>>>> 9 files changed, 532 insertions(+), 43 deletions(-)
>>>> create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c
>>>> create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c
>>>> create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c
>>>> create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c
>>>> create mode 100644 gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c
>>>>
>>>> diff --git a/gcc/common.opt b/gcc/common.opt
>>>> index 3ad1444cc88..933f791aeb4 100644
>>>> --- a/gcc/common.opt
>>>> +++ b/gcc/common.opt
>>>> @@ -936,6 +936,10 @@ Wzero-init-padding-bits=
>>>> Common Joined RejectNegative Enum(zero_init_padding_bits_kind)
>>>> Var(warn_zero_init_padding_bits) Init(ZERO_INIT_PADDING_BITS_STANDARD)
>>>> Warning
>>>> -Wzero-init-padding-bits=[standard|unions|all] Warn about initializers
>>>> that might not zero padding bits.
>>>>
>>>> +Wstrict-extended-asm
>>>> +Common Var(warn_strict_extended_asm) Warning LangEnabledBy(C C++,Wall)
>>>> +Warn about constructs for which -fstrict-extended-asm fails.
>>>> +
>>>> Xassembler
>>>> Driver Separate
>>>>
>>>> @@ -3588,6 +3592,10 @@ fverbose-asm
>>>> Common Var(flag_verbose_asm)
>>>> Add extra commentary to assembler output.
>>>>
>>>> +fstrict-extended-asm
>>>> +Common Var(flag_strict_extended_asm) Init(0)
>>>> +Apply strict rules for extended asm comprising hard register constraint
>>>> rules.
>>>> +
>>>> 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/doc/invoke.texi b/gcc/doc/invoke.texi
>>>> index 6d1da9f610e..c394c8e864f 100644
>>>> --- a/gcc/doc/invoke.texi
>>>> +++ b/gcc/doc/invoke.texi
>>>> @@ -210,7 +210,7 @@ in the following sections.
>>>> -fpermitted-flt-eval-methods=@var{standard}
>>>> -fplan9-extensions -fsigned-bitfields -funsigned-bitfields
>>>> -fsigned-char -funsigned-char -fstrict-flex-arrays[=@var{n}]
>>>> --fsso-struct=@var{endianness} --ansi}
>>>> +-fstrict-extended-asm -fsso-struct=@var{endianness} --ansi}
>>>>
>>>> @item C++ Language Options
>>>> @xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
>>>> @@ -448,7 +448,7 @@ Objective-C and Objective-C++ Dialects}.
>>>> -Wstrict-aliasing=@var{n} -Wstrict-overflow -Wstrict-overflow=@var{n}
>>>> -Wstring-compare
>>>> -Wno-stringop-overflow -Wno-stringop-overread
>>>> --Wno-stringop-truncation -Wstrict-flex-arrays
>>>> +-Wno-stringop-truncation -Wstrict-flex-arrays -Wstrict-extended-asm
>>>> -Wsuggest-attribute=@var{attribute-name}
>>>> -Wswitch -Wno-switch-bool -Wswitch-default -Wswitch-enum
>>>> -Wno-switch-outside-range -Wno-switch-unreachable -Wsync-nand
>>>> @@ -2976,6 +2976,15 @@ The @option{-fstrict_flex_arrays} option interacts
>>>> with the
>>>> @option{-Wstrict-flex-arrays} option. @xref{Warning Options}, for more
>>>> information.
>>>>
>>>> +@opindex fstrict-extended-asm
>>>> +@opindex fno-strict-extended-asm
>>>> +@item -fstrict-extended-asm
>>>> +Apply strict rules for extended @code{asm} (@pxref{Extended Asm}). Among
>>>> other
>>>> +things, this means that local register @code{asm} operands must follow the
>>>> +rules for hard register constraints. For example, don't allow multiple
>>>> input
>>>> +operands to refer to the same register. See also
>>>> +@option{-Wstrict-extended-asm}.
>>>> +
>>>> @opindex fsso-struct
>>>> @item -fsso-struct=@var{endianness}
>>>> Set the default scalar storage order of structures and unions to the
>>>> @@ -8990,6 +8999,54 @@ This option is more effective when
>>>> @option{-ftree-vrp} is active (the
>>>> default for @option{-O2} and above) but some warnings may be diagnosed
>>>> even without optimization.
>>>>
>>>> +@opindex Wstrict-extended-asm
>>>> +@opindex Wno-strict-extended-asm
>>>> +@item -Wstrict-extended-asm
>>>> +Warn about extended @code{asm} (@pxref{Extended Asm}) usages which could
>>>> lead
>>>> +to subtle bugs and for which @option{-fstrict-extended-asm} errors out.
>>>> For
>>>> +example, warn if an lvalue is used by multiple output operands:
>>>> +
>>>> +@smallexample
>>>> +int x;
>>>> +asm ("..." : "=r" (x), "=r" (x));
>>>> +@end smallexample
>>>> +
>>>> +Both output operands get a different register assigned but outside of the
>>>> +extended @code{asm} only one is bound to @code{x}.
>>>> +
>>>> +Furthermore, warn about register @code{asm} usages, if multiple operands
>>>> refer
>>>> +to the same register (including overlaps in case of register pairs).
>>>> +
>>>> +@smallexample
>>>> +register int x asm ("r5") = 42;
>>>> +register int y asm ("r5") = 24;
>>>> +asm ("..." : "=r" (x) : "r" (x), "r" (y));
>>>> +@end smallexample
>>>> +
>>>> +Here @code{x} and @code{y} refer to the same register @code{r5}. Note,
>>>> this also
>>>> +includes more subtle cases as for example:
>>>> +
>>>> +@smallexample
>>>> +register int x asm ("r5") = 42;
>>>> +asm ("..." : "+r" (x) : "r" (x));
>>>> +@end smallexample
>>>> +
>>>> +After multiplying out the in-out operand we are faced with two inputs
>>>> which
>>>> +refer to the same register. One time indirectly via @code{"0" (x)} and
>>>> the
>>>> +other one directly via @code{"r" (x)}.
>>>> +
>>>> +Also diagnose if a register @code{asm} operand does not coincide with its
>>>> +corresponding constraint. Assume in the following that @code{f5} is a
>>>> +floating-point register which is not entailed in the register class
>>>> associated
>>>> +with constraint @code{r}.
>>>> +
>>>> +@smallexample
>>>> +register float x asm ("f5");
>>>> +asm ("..." : "=r" (x));
>>>> +@end smallexample
>>>> +
>>>> +This indicates a subtle bug which is diagnosed.
>>>> +
>>>> @opindex Wsuggest-attribute=
>>>> @opindex Wno-suggest-attribute=
>>>> @item -Wsuggest-attribute=@var{attribute-name}
>>>> diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
>>>> index e4db4b1d9bd..951fd69a98b 100644
>>>> --- a/gcc/gimplify.cc
>>>> +++ b/gcc/gimplify.cc
>>>> @@ -2246,6 +2246,40 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
>>>> && clear_padding_type_may_have_padding_p (TREE_TYPE (decl)))
>>>> gimple_add_padding_init_for_auto_var (decl, is_vla, seq_p);
>>>> }
>>>> + else if (flag_strict_extended_asm && !init && DECL_HARD_REGISTER
>>>> (decl))
>>>> + {
>>>> + /* Register asm objects may be used uninitialized as inputs.
>>>> + Therefore, a naive translation into hard register constraints
>>>> + would render the demoted objects to be default initialized by
>>>> + init-regs and as a consequence, once hard register constraints
>>>> + materialize for an Extended Asm, hard registers would be
>>>> zeroed
>>>> + which didn't happen before. Therefore, to overcome this,
>>>> + initialize each demoted object with the current contents of
>>>> the
>>>> + hard register it previously referred to. For example,
>>>> translate
>>>> +
>>>> + register int x asm ("r5");
>>>> + int y;
>>>> + asm ("..." : "=r" (y) : "r" (x));
>>>> +
>>>> + to
>>>> +
>>>> + register int tmp asm ("r5");
>>>> + int x = tmp;
>>>> + int y;
>>>> + asm ("..." : "=r" (y) : "{r5}" (x));
>>>> +
>>>> + Do this unconditionally for all uninitialized register asm
>>>> objects
>>>> + and let subsequent passes remove dead stores in case those
>>>> objects
>>>> + are initialized at later points or are used exclusively as
>>>> output
>>>> + operands. */
>>>> + tree tmp = create_tmp_var (TREE_TYPE (decl), "regasm");
>>>> + SET_DECL_ASSEMBLER_NAME (tmp, DECL_ASSEMBLER_NAME (decl));
>>>> + DECL_REGISTER (tmp) = 1;
>>>> + DECL_HARD_REGISTER (tmp) = 1;
>>>> + DECL_INITIAL (decl) = tmp;
>>>> + *stmt_p = stmt;
>>>> + return gimplify_decl_expr (stmt_p, seq_p);
>>>> + }
>>>> }
>>>>
>>>> return GS_ALL_DONE;
>>>> @@ -7976,6 +8010,71 @@ 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 objects. */
>>>> +static hash_set<tree> demote_register_asm;
>>>> +
>>>> +/* If -fstrict-extended-asm is specified, rewrite constraints of Extended
>>>> Asm
>>>> + operands which refer to local register asm objects into hard register
>>>> + constraints. Also mark those objects to be demoted from register asm
>>>> + objects to ordinary objects which is done basically after
>>>> gimplification of
>>>> + the function body. */
>>>> +
>>>> +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;
>>>> + /* Currently, fixed registers cannot be used for hard register
>>>> constraints
>>>> + which is why we skip those for the moment. */
>>>> + if (fixed_regs[regno])
>>>> + return;
>>>> + const char *constraint
>>>> + = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
>>>> + auto_vec<char, 64> constraint_new;
>>>> + for (const char *p = constraint; *p; )
>>>> + {
>>>> + bool changed_p = false;
>>>> + enum constraint_num cn = lookup_constraint (p);
>>>> + enum reg_class rclass = reg_class_for_constraint (cn);
>>>> + if (rclass != NO_REGS)
>>>> + {
>>>> + /* At this point we have a constraint which entails all the
>>>> + registers required by the register asm operand. Therefore,
>>>> + rewrite the constraint into a corresponding hard register
>>>> + constraint. */
>>>> + 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 ('}');
>>>> + changed_p = true;
>>>> + }
>>>> +
>>>> + for (size_t len = CONSTRAINT_LEN (*p, p); len; len--, p++)
>>>> + {
>>>> + if (!changed_p)
>>>> + 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. */
>>>>
>>>> @@ -8063,6 +8162,43 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p,
>>>> gimple_seq *post_p)
>>>> is_inout = false;
>>>> }
>>>>
>>>> + /* In case of -fstrict-extended-asm or for hard register
>>>> constraints,
>>>> + error out if an lvalue is used for more than one output operand.
>>>> + Otherwise emit at most a warning. For example, error out for
>>>> +
>>>> + asm ("" : "={0}" (x), "={1}" (x));
>>>> + asm ("" : "=r" (x), "={1}" (x));
>>>> +
>>>> + and emit a warning for
>>>> +
>>>> + int x;
>>>> + asm ("" : "=r" (x), "=r" (x));
>>>> +
>>>> + if -fno-strict-extended-asm -Wstrict-register-asm is given. */
>>>> +
>>>> + for (tree i = link_next; i; i = TREE_CHAIN (i))
>>>> + {
>>>> + tree op1 = TREE_VALUE (link);
>>>> + tree op2 = TREE_VALUE (i);
>>>> + if (op1 != op2)
>>>> + continue;
>>>> + const char *constraint2
>>>> + = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (i)));
>>>> + if (flag_strict_extended_asm
>>>> + || strchr (constraint, '{') != nullptr
>>>> + || strchr (constraint2, '{') != nullptr
>>>> + /* We already diagnose global/local register asm cases during
>>>> + expand as errors. Therefore, do here, too. */
>>>> + || (VAR_P (op2) && DECL_HARD_REGISTER (op2)))
>>>> + {
>>>> + error ("multiple outputs to lvalue %qE", op2);
>>>> + return GS_ERROR;
>>>> + }
>>>> + else
>>>> + warning (OPT_Wstrict_extended_asm,
>>>> + "multiple outputs to lvalue %qE", op2);
>>>> + }
>>>> +
>>>> /* If we can't make copies, we can only accept memory.
>>>> Similarly for VLAs. */
>>>> tree outtype = TREE_TYPE (TREE_VALUE (link));
>>>> @@ -8226,45 +8362,6 @@ 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;
>>>> int input_num = 0;
>>>> for (link = ASM_INPUTS (expr); link; ++input_num, ++i, link = link_next)
>>>> @@ -8283,8 +8380,10 @@ gimplify_asm_expr (tree *expr_p, gimple_seq *pre_p,
>>>> gimple_seq *post_p)
>>>> is_inout = false;
>>>> }
>>>>
>>>> + tree inputv = TREE_VALUE (link);
>>>> + tree intype = TREE_TYPE (inputv);
>>>> +
>>>> /* If we can't make copies, we can only accept memory. */
>>>> - tree intype = TREE_TYPE (TREE_VALUE (link));
>>>> if (TREE_ADDRESSABLE (intype)
>>>> || !COMPLETE_TYPE_P (intype)
>>>> || !tree_fits_poly_uint64_p (TYPE_SIZE_UNIT (intype)))
>>>> @@ -8299,10 +8398,55 @@ gimplify_asm_expr (tree *expr_p, gimple_seq
>>>> *pre_p, gimple_seq *post_p)
>>>> }
>>>> }
>>>>
>>>> + if (VAR_P (inputv)
>>>> + && DECL_HARD_REGISTER (inputv)
>>>> + && !is_global_var (inputv))
>>>> + {
>>>> + tree id1 = DECL_ASSEMBLER_NAME (inputv);
>>>> + const char *regname1 = IDENTIFIER_POINTER (id1);
>>>> + ++regname1;
>>>> + int regno1 = decode_reg_name (regname1);
>>>> + if (regno1 >= 0)
>>>> + {
>>>> + HARD_REG_SET hreg_set1;
>>>> + CLEAR_HARD_REG_SET (hreg_set1);
>>>> + add_to_hard_reg_set (&hreg_set1, TYPE_MODE (intype), regno1);
>>>> + for (tree i = link_next; i; i = TREE_CHAIN (i))
>>>> + {
>>>> + tree inputv2 = TREE_VALUE (i);
>>>> + if (VAR_P (inputv2)
>>>> + && DECL_HARD_REGISTER (inputv2)
>>>> + && !is_global_var (inputv2))
>>>> + {
>>>> + tree id2 = DECL_ASSEMBLER_NAME (inputv2);
>>>> + const char *regname2 = IDENTIFIER_POINTER (id2);
>>>> + ++regname2;
>>>> + int regno2 = decode_reg_name (regname2);
>>>> + if (regno2 < 0)
>>>> + continue;
>>>> + tree intype2 = TREE_TYPE (inputv2);
>>>> + int nregs2
>>>> + = hard_regno_nregs (regno2, TYPE_MODE (intype2));
>>>> + for (int j = regno2; j < regno2 + nregs2; ++j)
>>>> + {
>>>> + if (!TEST_HARD_REG_BIT (hreg_set1, j))
>>>> + continue;
>>>> + if (flag_strict_extended_asm)
>>>> + error ("multiple inputs to hard register: %s",
>>>> + reg_names[j]);
>>>> + else
>>>> + warning (OPT_Wstrict_extended_asm,
>>>> + "multiple inputs to hard register:
>>>> %s",
>>>> + reg_names[j]);
>>>> + }
>>>> + }
>>>> + }
>>>> + }
>>>> + }
>>>> +
>>>> /* If the operand is a memory input, it should be an lvalue. */
>>>> if (!allows_reg && allows_mem)
>>>> {
>>>> - tree inputv = TREE_VALUE (link);
>>>> STRIP_NOPS (inputv);
>>>> if (TREE_CODE (inputv) == PREDECREMENT_EXPR
>>>> || TREE_CODE (inputv) == PREINCREMENT_EXPR
>>>> @@ -8372,6 +8516,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_strict_extended_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);
>>>>
>>>> @@ -21874,6 +22032,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/stmt.cc b/gcc/stmt.cc
>>>> index 382a2d75c07..cb98d6197fa 100644
>>>> --- a/gcc/stmt.cc
>>>> +++ b/gcc/stmt.cc
>>>> @@ -249,6 +249,15 @@ hardreg_ok_p (int reg_number, machine_mode mode, int
>>>> operand_num)
>>>> return false;
>>>> }
>>>>
>>>> +static inline bool
>>>> +rclass_entails_registers (enum reg_class rclass, int regno, int nregs)
>>>> +{
>>>> + for (int i = regno; i < regno + nregs ; ++i)
>>>> + if (!TEST_HARD_REG_BIT (reg_class_contents[rclass], i))
>>>> + return false;
>>>> + return true;
>>>> +}
>>>> +
>>>> /* 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,
>>>> @@ -499,6 +508,23 @@ parse_output_constraint (const char **constraint_p,
>>>> int operand_num,
>>>> reg_names[overlap_regno]);
>>>> return false;
>>>> }
>>>> + enum reg_class rclass = reg_class_for_constraint (cn);
>>>> + machine_mode mode = TYPE_MODE (TREE_TYPE
>>>> (reg_info->operand));
>>>> + int nregs = hard_regno_nregs (regno, mode);
>>>> + if (rclass != NO_REGS
>>>> + && !rclass_entails_registers (rclass, regno, nregs))
>>>> + {
>>>> + if (flag_strict_extended_asm)
>>>> + {
>>>> + error
>>>> + ("constraint and register %<asm%> do not
>>>> coincide");
>>>> + return false;
>>>> + }
>>>> + else
>>>> + warning
>>>> + (OPT_Wstrict_extended_asm,
>>>> + "constraint and register %<asm%> do not
>>>> coincide");
>>>> + }
>>>> reg_info->set_reg_asm_output (regno);
>>>> if (early_clobbered)
>>>> reg_info->set_early_clobbered (alt, operand_num, regno);
>>>> @@ -756,6 +782,22 @@ repeat:
>>>> reg_names[overlap_regno]);
>>>> return false;
>>>> }
>>>> + enum reg_class rclass = reg_class_for_constraint (cn);
>>>> + machine_mode mode = TYPE_MODE (TREE_TYPE
>>>> (reg_info->operand));
>>>> + int nregs = hard_regno_nregs (regno, mode);
>>>> + if (rclass != NO_REGS
>>>> + && !rclass_entails_registers (rclass, regno, nregs))
>>>> + {
>>>> + if (flag_strict_extended_asm)
>>>> + {
>>>> + error ("constraint and register %<asm%> do not
>>>> coincide");
>>>> + return false;
>>>> + }
>>>> + else
>>>> + warning
>>>> + (OPT_Wstrict_extended_asm,
>>>> + "constraint and register %<asm%> do not coincide");
>>>> + }
>>>> reg_info->set_reg_asm_input (regno);
>>>> if ((constraint == orig_constraint
>>>> && reg_info->test_early_clobbered_alt (alt, regno))
>>>> diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c
>>>> b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c
>>>> new file mode 100644
>>>> index 00000000000..f8c7cad48a5
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-1.c
>>>> @@ -0,0 +1,51 @@
>>>> +/* { dg-do compile { target aarch64*-*-* s390*-*-* x86_64-*-* } } */
>>>> +/* { dg-additional-options "-fstrict-extended-asm" } */
>>>> +/* { dg-additional-options "-msse2" { target x86_64-*-* } } */
>>>> +
>>>> +#if __aarch64__
>>>> +# define GPR "r5"
>>>> +# define FPR "d5"
>>>> +# define CSTR_GPR "r"
>>>> +# define CSTR_FPR "w"
>>>> +#elif __s390__
>>>> +# define GPR "r5"
>>>> +# define FPR "f5"
>>>> +# define CSTR_GPR "r"
>>>> +# define CSTR_FPR "f"
>>>> +#elif __x86_64__
>>>> +# define GPR "cx"
>>>> +# define FPR "xmm5"
>>>> +# define CSTR_GPR "r"
>>>> +# define CSTR_FPR "x"
>>>> +#else
>>>> +# error unsupported target
>>>> +#endif
>>>> +
>>>> +int
>>>> +test ()
>>>> +{
>>>> + register int x __asm__ (GPR) = 42;
>>>> + register float y __asm__ (FPR) = 42;
>>>> +
>>>> + __asm__ ("" : "="CSTR_FPR (x)); /* { dg-error "constraint and register
>>>> 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_FPR","CSTR_FPR (x)); /* { dg-error "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_GPR","CSTR_FPR (x)); /* { dg-error "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_FPR","CSTR_GPR (x)); /* { dg-error "constraint
>>>> and register 'asm' do not coincide" } */
>>>> +
>>>> + __asm__ __volatile ("" :: CSTR_FPR (x)); /* { dg-error "constraint and
>>>> register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_FPR","CSTR_FPR (x)); /* { dg-error
>>>> "constraint and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_GPR","CSTR_FPR (x)); /* { dg-error
>>>> "constraint and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_FPR","CSTR_GPR (x)); /* { dg-error
>>>> "constraint and register 'asm' do not coincide" } */
>>>> +
>>>> + __asm__ ("" : "="CSTR_GPR (y)); /* { dg-error "constraint and register
>>>> 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_GPR","CSTR_GPR (y)); /* { dg-error "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_FPR","CSTR_GPR (y)); /* { dg-error "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_GPR","CSTR_FPR (y)); /* { dg-error "constraint
>>>> and register 'asm' do not coincide" } */
>>>> +
>>>> + __asm__ __volatile ("" :: CSTR_GPR (y)); /* { dg-error "constraint and
>>>> register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_GPR","CSTR_GPR (y)); /* { dg-error
>>>> "constraint and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_FPR","CSTR_GPR (y)); /* { dg-error
>>>> "constraint and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_GPR","CSTR_FPR (y)); /* { dg-error
>>>> "constraint and register 'asm' do not coincide" } */
>>>> +
>>>> + return x + y;
>>>> +}
>>>> diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c
>>>> b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c
>>>> new file mode 100644
>>>> index 00000000000..cfa783d4e94
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-2.c
>>>> @@ -0,0 +1,54 @@
>>>> +/* { dg-do compile { target aarch64*-*-* s390*-*-* x86_64-*-* } } */
>>>> +/* { dg-additional-options "-fno-strict-extended-asm
>>>> -Wstrict-extended-asm" } */
>>>> +/* { dg-additional-options "-msse2" { target x86_64-*-* } } */
>>>> +
>>>> +/* This is a copy of asm-hard-reg-strict-1.c for -fno-strict-register-asm
>>>> where
>>>> + we expect warnings instead of errors. */
>>>> +
>>>> +#if __aarch64__
>>>> +# define GPR "r5"
>>>> +# define FPR "d5"
>>>> +# define CSTR_GPR "r"
>>>> +# define CSTR_FPR "w"
>>>> +#elif __s390__
>>>> +# define GPR "r5"
>>>> +# define FPR "f5"
>>>> +# define CSTR_GPR "r"
>>>> +# define CSTR_FPR "f"
>>>> +#elif __x86_64__
>>>> +# define GPR "cx"
>>>> +# define FPR "xmm5"
>>>> +# define CSTR_GPR "r"
>>>> +# define CSTR_FPR "x"
>>>> +#else
>>>> +# error unsupported target
>>>> +#endif
>>>> +
>>>> +int
>>>> +test ()
>>>> +{
>>>> + register int x __asm__ (GPR) = 42;
>>>> + register float y __asm__ (FPR) = 42;
>>>> +
>>>> + __asm__ ("" : "="CSTR_FPR (x)); /* { dg-warning "constraint and
>>>> register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_FPR","CSTR_FPR (x)); /* { dg-warning "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_GPR","CSTR_FPR (x)); /* { dg-warning "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_FPR","CSTR_GPR (x)); /* { dg-warning "constraint
>>>> and register 'asm' do not coincide" } */
>>>> +
>>>> + __asm__ __volatile ("" :: CSTR_FPR (x)); /* { dg-warning "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_FPR","CSTR_FPR (x)); /* { dg-warning
>>>> "constraint and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_GPR","CSTR_FPR (x)); /* { dg-warning
>>>> "constraint and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_FPR","CSTR_GPR (x)); /* { dg-warning
>>>> "constraint and register 'asm' do not coincide" } */
>>>> +
>>>> + __asm__ ("" : "="CSTR_GPR (y)); /* { dg-warning "constraint and
>>>> register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_GPR","CSTR_GPR (y)); /* { dg-warning "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_FPR","CSTR_GPR (y)); /* { dg-warning "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ ("" : "="CSTR_GPR","CSTR_FPR (y)); /* { dg-warning "constraint
>>>> and register 'asm' do not coincide" } */
>>>> +
>>>> + __asm__ __volatile ("" :: CSTR_GPR (y)); /* { dg-warning "constraint
>>>> and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_GPR","CSTR_GPR (y)); /* { dg-warning
>>>> "constraint and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_FPR","CSTR_GPR (y)); /* { dg-warning
>>>> "constraint and register 'asm' do not coincide" } */
>>>> + __asm__ __volatile ("" :: CSTR_GPR","CSTR_FPR (y)); /* { dg-warning
>>>> "constraint and register 'asm' do not coincide" } */
>>>> +
>>>> + return x + y;
>>>> +}
>>>> diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c
>>>> b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c
>>>> new file mode 100644
>>>> index 00000000000..f3732a028a5
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-3.c
>>>> @@ -0,0 +1,22 @@
>>>> +/* { dg-do compile { target aarch64*-*-* s390*-*-* x86_64-*-* } } */
>>>> +/* { dg-additional-options "-fstrict-extended-asm" } */
>>>> +
>>>> +void
>>>> +test ()
>>>> +{
>>>> + register int x __asm__ ("5") = 42;
>>>> + register int y __asm__ ("5") = 24;
>>>> + int z;
>>>> +
>>>> + __asm__ __volatile__ ("" : "+r" (x) : "r" (x)); /* { dg-error
>>>> "multiple inputs to hard register" } */
>>>> + __asm__ __volatile__ ("" : "=r" (x) : "0" (x), "r" (x)); /* { dg-error
>>>> "multiple inputs to hard register" } */
>>>> + __asm__ __volatile__ ("" : : "r" (x), "r" (x)); /* { dg-error
>>>> "multiple inputs to hard register" } */
>>>> + __asm__ __volatile__ ("" : : "r" (x), "r" (y)); /* { dg-error
>>>> "multiple inputs to hard register" } */
>>>> +
>>>> + __asm__ __volatile__ ("" : "=r" (x), "=r" (x)); /* { dg-error "multiple
>>>> outputs to lvalue 'x'" } */
>>>> +
>>>> + __asm__ __volatile__ ("" : "=r" (z), "=r" (z)); /* { dg-error "multiple
>>>> outputs to lvalue 'z'" } */
>>>> + __asm__ __volatile__ ("" : "=m" (z), "=r" (z)); /* { dg-error "multiple
>>>> outputs to lvalue 'z'" } */
>>>> + __asm__ __volatile__ ("" : "=r" (z), "=m" (z)); /* { dg-error "multiple
>>>> outputs to lvalue 'z'" } */
>>>> + __asm__ __volatile__ ("" : "=m" (z), "=m" (z)); /* { dg-error "multiple
>>>> outputs to lvalue 'z'" } */
>>>> +}
>>>> diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c
>>>> b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c
>>>> new file mode 100644
>>>> index 00000000000..98eb0386b3b
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-4.c
>>>> @@ -0,0 +1,25 @@
>>>> +/* { dg-do compile { target aarch64*-*-* s390*-*-* x86_64-*-* } } */
>>>> +/* { dg-additional-options "-fno-strict-extended-asm
>>>> -Wstrict-extended-asm" } */
>>>> +
>>>> +/* This is a copy of asm-hard-reg-strict-3.c for -fno-strict-register-asm
>>>> where
>>>> + we expect warnings instead of errors. */
>>>> +
>>>> +void
>>>> +test ()
>>>> +{
>>>> + register int x __asm__ ("5") = 42;
>>>> + register int y __asm__ ("5") = 24;
>>>> + int z;
>>>> +
>>>> + __asm__ __volatile__ ("" : "+r" (x) : "r" (x)); /* {
>>>> dg-warning "multiple inputs to hard register" } */
>>>> + __asm__ __volatile__ ("" : "=r" (x) : "0" (x), "r" (x)); /* {
>>>> dg-warning "multiple inputs to hard register" } */
>>>> + __asm__ __volatile__ ("" : : "r" (x), "r" (x)); /* {
>>>> dg-warning "multiple inputs to hard register" } */
>>>> + __asm__ __volatile__ ("" : : "r" (x), "r" (y)); /* {
>>>> dg-warning "multiple inputs to hard register" } */
>>>> +
>>>> + __asm__ __volatile__ ("" : "=r" (x), "=r" (x)); /* { dg-error "multiple
>>>> outputs to lvalue 'x'" } */
>>>> +
>>>> + __asm__ __volatile__ ("" : "=r" (z), "=r" (z)); /* { dg-warning
>>>> "multiple outputs to lvalue 'z'" } */
>>>> + __asm__ __volatile__ ("" : "=m" (z), "=r" (z)); /* { dg-warning
>>>> "multiple outputs to lvalue 'z'" } */
>>>> + __asm__ __volatile__ ("" : "=r" (z), "=m" (z)); /* { dg-warning
>>>> "multiple outputs to lvalue 'z'" } */
>>>> + __asm__ __volatile__ ("" : "=m" (z), "=m" (z)); /* { dg-warning
>>>> "multiple outputs to lvalue 'z'" } */
>>>> +}
>>>> diff --git a/gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c
>>>> b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c
>>>> new file mode 100644
>>>> index 00000000000..699bed70a43
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/asm-hard-reg-strict-5.c
>>>> @@ -0,0 +1,65 @@
>>>> +/* { dg-do compile { target aarch64*-*-* s390*-*-* x86_64-*-* } } */
>>>> +/* { dg-additional-options "-fstrict-extended-asm -fdump-tree-gimple" } */
>>>> +/* { dg-additional-options "-msse2" { target x86_64-*-* } } */
>>>> +
>>>> +/* Test rewriting constraints into hard register constraints and demote
>>>> + register asm objects into ordinary objects. */
>>>> +
>>>> +#if __aarch64__
>>>> +# define GPR "r5"
>>>> +# define FPR "d5"
>>>> +# define CSTR_GPR "r"
>>>> +# define CSTR_FPR "w"
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{r5\}\"
>>>> x0\\);" 1 "gimple" { target aarch64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{r5\}\"
>>>> x0\\);" 1 "gimple" { target aarch64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{r5\},g\"
>>>> x0\\);" 1 "gimple" { target aarch64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{d5\}\"
>>>> x1\\);" 1 "gimple" { target aarch64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{d5\}\"
>>>> x1\\);" 1 "gimple" { target aarch64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{d5\},g\"
>>>> x1\\);" 1 "gimple" { target aarch64-*-* } } } */
>>>> +#elif __s390__
>>>> +# define GPR "r5"
>>>> +# define FPR "f5"
>>>> +# define CSTR_GPR "r"
>>>> +# define CSTR_FPR "f"
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{r5\}\"
>>>> x0\\);" 1 "gimple" { target s390*-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{r5\}\"
>>>> x0\\);" 1 "gimple" { target s390*-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{r5\},g\"
>>>> x0\\);" 1 "gimple" { target s390*-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{f5\}\"
>>>> x1\\);" 1 "gimple" { target s390*-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{f5\}\"
>>>> x1\\);" 1 "gimple" { target s390*-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{f5\},g\"
>>>> x1\\);" 1 "gimple" { target s390*-*-* } } } */
>>>> +#elif __x86_64__
>>>> +# define GPR "cx"
>>>> +# define FPR "xmm5"
>>>> +# define CSTR_GPR "r"
>>>> +# define CSTR_FPR "x"
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{cx\}\"
>>>> x0\\);" 1 "gimple" { target x86_64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{cx\}\"
>>>> x0\\);" 1 "gimple" { target x86_64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{cx\},g\"
>>>> x0\\);" 1 "gimple" { target x86_64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=\{xmm5\}\"
>>>> x1\\);" 1 "gimple" { target x86_64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=g,!\{xmm5\}\"
>>>> x1\\);" 1 "gimple" { target x86_64-*-* } } } */
>>>> +/* { dg-final { scan-tree-dump-times "__asm__\\(\"\" : \"=!\{xmm5\},g\"
>>>> x1\\);" 1 "gimple" { target x86_64-*-* } } } */
>>>> +#else
>>>> +# error unsupported target
>>>> +#endif
>>>> +
>>>> +int
>>>> +test_gpr (void)
>>>> +{
>>>> +/* { dg-final { scan-tree-dump-times "int x0;" 1 "gimple" } } */
>>>> + register int x0 __asm__ (GPR);
>>>> + __asm__ ("" : "="CSTR_GPR (x0));
>>>> + __asm__ ("" : "=g,!"CSTR_GPR (x0));
>>>> + __asm__ ("" : "=!"CSTR_GPR",g" (x0));
>>>> + return x0;
>>>> +}
>>>> +
>>>> +float
>>>> +test_fpr (void)
>>>> +{
>>>> +/* { dg-final { scan-tree-dump-times "float x1;" 1 "gimple" } } */
>>>> + register float x1 __asm__ (FPR);
>>>> + __asm__ ("" : "="CSTR_FPR (x1));
>>>> + __asm__ ("" : "=g,!"CSTR_FPR (x1));
>>>> + __asm__ ("" : "=!"CSTR_FPR",g" (x1));
>>>> + return x1;
>>>> +}
>>>> --
>>>> 2.54.0
>>>>